|
4 | 4 |
|
5 | 5 | //! A Polkadot-JS account loader. |
6 | 6 |
|
| 7 | +use crate::sr25519; |
7 | 8 | use base64::Engine; |
8 | 9 | use crypto_secretbox::{ |
9 | 10 | Key, Nonce, XSalsa20Poly1305, |
10 | 11 | aead::{Aead, KeyInit}, |
11 | 12 | }; |
12 | 13 | use serde::Deserialize; |
13 | | - |
| 14 | +use subxt_utils_accountid32::AccountId32; |
14 | 15 | use thiserror::Error as DeriveError; |
15 | 16 |
|
16 | | -use crate::sr25519; |
| 17 | +/// Official Polkadot-JS allowed scrypt parameters |
| 18 | +/// https://github.com/polkadot-js/common/blob/fe0886be239526e6c559e98d1099815d4b4f4a7f/packages/util-crypto/src/scrypt/defaults.ts#L6 |
| 19 | +const ALLOWED_PARAMS: &[(u32, u32, u32)] = &[ |
| 20 | + (1 << 13, 10, 8), |
| 21 | + (1 << 14, 5, 8), |
| 22 | + (1 << 15, 3, 8), |
| 23 | + (1 << 15, 1, 8), // Standard |
| 24 | + (1 << 16, 2, 8), |
| 25 | + (1 << 17, 1, 8), // High Security |
| 26 | +]; |
17 | 27 |
|
18 | 28 | /// Given a JSON keypair as exported from Polkadot-JS, this returns an [`sr25519::Keypair`] |
19 | 29 | pub fn decrypt_json(json: &str, password: &str) -> Result<sr25519::Keypair, Error> { |
@@ -75,9 +85,6 @@ struct KeyringPairJson { |
75 | 85 | address: AccountId32, |
76 | 86 | } |
77 | 87 |
|
78 | | -// Re-export this type which is used above. |
79 | | -pub use subxt_utils_accountid32::AccountId32; |
80 | | - |
81 | 88 | // This can be removed once split_array is stabilized. |
82 | 89 | fn slice_to_u32(slice: &[u8]) -> u32 { |
83 | 90 | u32::from_le_bytes(slice.try_into().expect("Slice should be 4 bytes.")) |
@@ -118,13 +125,21 @@ impl KeyringPairJson { |
118 | 125 | // protection against carefully-crafted params that can eat up CPU since these are user |
119 | 126 | // inputs. So we need to get very clever here, but atm we only allow the defaults |
120 | 127 | // and if no match, bail out. |
121 | | - if n != 32768 || p != 1 || r != 8 { |
| 128 | + // |
| 129 | + // Check if the combination exists in the allowed list |
| 130 | + if !ALLOWED_PARAMS |
| 131 | + .iter() |
| 132 | + .any(|&(a_n, a_p, a_r)| n == a_n && p == a_p && r == a_r) |
| 133 | + { |
122 | 134 | return Err(Error::UnsupportedScryptParameters { n, p, r }); |
123 | 135 | } |
124 | 136 |
|
| 137 | + // Calculate the `log_n` (e.g., 32768 -> 15) |
| 138 | + let log_n = (n as f64).log2() as u8; |
| 139 | + |
125 | 140 | // Hash password. |
126 | | - let scrypt_params = |
127 | | - scrypt::Params::new(15, 8, 1, 32).expect("Provided parameters should be valid."); |
| 141 | + let scrypt_params = scrypt::Params::new(log_n, r, p, 32) |
| 142 | + .map_err(|_| Error::UnsupportedScryptParameters { n, p, r })?; |
128 | 143 | let mut key = Key::default(); |
129 | 144 | scrypt::scrypt(password.as_bytes(), salt, &scrypt_params, &mut key) |
130 | 145 | .expect("Key should be 32 bytes."); |
@@ -171,7 +186,28 @@ mod test { |
171 | 186 | use super::*; |
172 | 187 |
|
173 | 188 | #[test] |
174 | | - fn test_get_keypair_sr25519() { |
| 189 | + fn test_get_bob_keypair_sr25519() { |
| 190 | + let json = r#" |
| 191 | + { |
| 192 | + "encoded": "J2FFcPHAY11Pmq/38eqbwfUv9OPitYJs+oYgahBvlagAAAIAAQAAAAgAAAB5o0DwXCWDblsH+9pc++RaBO4fpHBHzUirHFHFE9yS3sDzgAIQjhgvPqJ3ODrMR2gy7vk0VZg1fyirIvmsrfjGbWnOI8YU0joX0tYytroyWaykFKtZJMmE0pNKcJ5dJmDxscbK53Ac+7ld2UdH07yKPXxmPuYNNw3vKx8cg9CdQgifKfzQxHnC+EUpOoHPLwGlHsFEYtIlQtngqd9n", |
| 193 | + "encoding": { |
| 194 | + "content": ["pkcs8", "sr25519"], |
| 195 | + "type": ["scrypt", "xsalsa20-poly1305"], |
| 196 | + "version": "3" |
| 197 | + }, |
| 198 | + "address": "5CfWTDh7XxJ2yrayqQ2aJnnZAH5v5XaF1oJFfH5QCpbfP9v8", |
| 199 | + "meta": { |
| 200 | + "genesisHash": "", |
| 201 | + "name": "Bob (Dev)", |
| 202 | + "whenCreated": 1768916488918 |
| 203 | + } |
| 204 | + } |
| 205 | + "#; |
| 206 | + decrypt_json(json, "whoisbob").unwrap(); |
| 207 | + } |
| 208 | + |
| 209 | + #[test] |
| 210 | + fn test_get_alice_keypair_sr25519() { |
175 | 211 | let json = r#" |
176 | 212 | { |
177 | 213 | "encoded": "DumgApKCTqoCty1OZW/8WS+sgo6RdpHhCwAkA2IoDBMAgAAAAQAAAAgAAAB6IG/q24EeVf0JqWqcBd5m2tKq5BlyY84IQ8oamLn9DZe9Ouhgunr7i36J1XxUnTI801axqL/ym1gil0U8440Qvj0lFVKwGuxq38zuifgoj0B3Yru0CI6QKEvQPU5xxj4MpyxdSxP+2PnTzYao0HDH0fulaGvlAYXfqtU89xrx2/z9z7IjSwS3oDFPXRQ9kAdDebtyCVreZ9Otw9v3", |
|
0 commit comments