Wallet.fromJson constructor

Wallet.fromJson(
  1. String encoded,
  2. String password
)

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);
}