decrypt static method

Uint8List decrypt(
  1. Map<String, dynamic> json,
  2. String password
)

Decrypts a Keystore V3 JSON object.

Implementation

static Uint8List decrypt(Map<String, dynamic> json, String password) {
  if (json['version'] != 3) {
    throw ArgumentError('Only Keystore V3 is supported');
  }

  final crypto = (json['crypto'] ?? json['Crypto']) as Map<String, dynamic>?;
  if (crypto == null) {
    throw ArgumentError('Invalid keystore: missing crypto section');
  }

  final ciphertext = _fromHex(crypto['ciphertext'] as String);
  final cipherparams = crypto['cipherparams'] as Map<String, dynamic>;
  final iv = _fromHex(cipherparams['iv'] as String);
  final mac = _fromHex(crypto['mac'] as String);
  final kdf = crypto['kdf'] as String;
  final kdfParams = crypto['kdfparams'] as Map<String, dynamic>;

  final passwordBytes = Uint8List.fromList(utf8.encode(password));
  Uint8List derivedKey;

  if (kdf == 'scrypt') {
    final n = kdfParams['n'] as int;
    final r = kdfParams['r'] as int;
    final p = kdfParams['p'] as int;
    final salt = _fromHex(kdfParams['salt'] as String);
    derivedKey = Scrypt.derive(passwordBytes, salt, n, r, p, 32);
  } else if (kdf == 'pbkdf2') {
    final iterations = kdfParams['c'] as int;
    final salt = _fromHex(kdfParams['salt'] as String);
    derivedKey = Pbkdf2.deriveSha256(
        password: passwordBytes,
        salt: salt,
        iterations: iterations,
        keyLength: 32);
  } else {
    throw ArgumentError('Unsupported KDF: $kdf');
  }

  // Verify MAC
  final macData = Uint8List.fromList(derivedKey.sublist(16, 32) + ciphertext);
  final calculatedMac = Keccak256.hash(macData);
  if (!_uint8ListEquals(calculatedMac, mac)) {
    throw StateError('Invalid password or corrupted keystore (MAC mismatch)');
  }

  final encryptionKey = derivedKey.sublist(0, 16);
  final aes = AES(encryptionKey);
  return aes.ctr(ciphertext, iv);
}