SecretWallet.decode constructor

SecretWallet.decode(
  1. String encoded,
  2. String password
)

using a separate thread for encode or decode secret wallet.

Implementation

factory SecretWallet.decode(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 = _toJsonEcoded(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));

  final aes = _initCipher(false, aesKey, iv);
  final privateKey = aes.process(Uint8List.fromList(encryptedPrivateKey));
  final id = UUID.toBuffer(data['id'] as String);
  return SecretWallet._(
      utf8.decode(privateKey), derivator, encodedPassword, iv, id);
}