Wallet.fromJson constructor
Reads and unlocks the wallet denoted in the json string given with the
specified password
. encoded
must be the String contents of a valid
v3 Ethereum wallet file.
Implementation
factory Wallet.fromJson(String encoded, String password) {
/*
In order to read the wallet and obtain the secret key stored in it, we
need to do the following:
1: Key Derivation: Based on the key derivator specified (either pbdkdf2 or
scryt), we need to use the password to obtain the aes key used to
decrypt the private key.
2: Using the obtained aes key and the iv parameter, decrypt the private
key stored in the wallet.
*/
final data = json.decode(encoded);
// Ensure version is 3, only version that we support at the moment
final version = data['version'];
if (version != 3) {
throw ArgumentError.value(
version,
'version',
'Library only supports '
'version 3 of wallet files at the moment. However, the following value'
' has been given:',
);
}
final crypto = data['crypto'] ?? data['Crypto'];
final kdf = crypto['kdf'] as String;
_KeyDerivator derivator;
switch (kdf) {
case 'pbkdf2':
final derParams = crypto['kdfparams'] as Map<String, dynamic>;
if (derParams['prf'] != 'hmac-sha256') {
throw ArgumentError(
'Invalid prf supplied with the pdf: was ${derParams["prf"]}, expected hmac-sha256',
);
}
derivator = _PBDKDF2KeyDerivator(
derParams['c'] as int,
Uint8List.fromList(hexToBytes(derParams['salt'] as String)),
derParams['dklen'] as int,
);
break;
case 'scrypt':
final derParams = crypto['kdfparams'] as Map<String, dynamic>;
derivator = _ScryptKeyDerivator(
derParams['dklen'] as int,
derParams['n'] as int,
derParams['r'] as int,
derParams['p'] as int,
Uint8List.fromList(hexToBytes(derParams['salt'] as String)),
);
break;
default:
throw ArgumentError(
'Wallet file uses $kdf as key derivation function, which is not supported.',
);
}
// Now that we have the derivator, let's obtain the aes key:
final encodedPassword = Uint8List.fromList(utf8.encode(password));
final derivedKey = derivator.deriveKey(encodedPassword);
final aesKey = Uint8List.fromList(derivedKey.sublist(0, 16));
final encryptedPrivateKey = hexToBytes(crypto['ciphertext'] as String);
//Validate the derived key with the mac provided
final derivedMac = _generateMac(derivedKey, encryptedPrivateKey);
if (derivedMac != crypto['mac']) {
throw ArgumentError(
'Could not unlock wallet file. You either supplied the wrong password or the file is corrupted',
);
}
// We only support this mode at the moment
if (crypto['cipher'] != 'aes-128-ctr') {
throw ArgumentError(
'Wallet file uses ${crypto["cipher"]} as cipher, but only aes-128-ctr is supported.',
);
}
final iv =
Uint8List.fromList(hexToBytes(crypto['cipherparams']['iv'] as String));
// Decrypt the private key
final aes = _initCipher(false, aesKey, iv);
final privateKey = aes.process(Uint8List.fromList(encryptedPrivateKey));
final credentials = EthPrivateKey(privateKey);
final id = parseUuid(data['id'] as String);
return Wallet._(credentials, derivator, encodedPassword, iv, id);
}