decrypt method

PolyseedMnemonic decrypt(
  1. String passphrase
)

Decrypt this seed with passphrase and return an unencrypted copy.

The crypt operation is symmetric: applying it twice with the same passphrase returns the original seed. A wrong passphrase produces no error - it silently yields a different (invalid) seed, matching every other PBKDF2-based scheme.

Throws StateError if isEncrypted is false.

Implementation

PolyseedMnemonic decrypt(String passphrase) {
  if (!isEncrypted) {
    throw StateError('Seed is not encrypted.');
  }

  // Mask salt layout (16 bytes, zero-initialised):
  //  [0..12]  "POLYSEED mask" (UTF-8, 13 bytes)
  //  [13]      0x00 (pad)
  //  [14..15]  0xff 0xff
  final salt = Uint8List(16);
  const label = 'POLYSEED mask';
  salt.setRange(0, label.length, utf8.encode(label));
  salt[14] = 0xff;
  salt[15] = 0xff;

  final mask = _pbkdf2HmacSha256(
    password: Uint8List.fromList(utf8.encode(unorm.nfkd(passphrase))),
    salt: salt,
    iterations: 10000,
    keyLength: 32,
  );

  // XOR the 19 secret bytes with the mask, then clear the 2 unused high
  // bits of byte 18 (150 secret bits span 19 bytes, leaving 2 bits spare).
  const clearMask = 0x3f; // 0xff >> (19*8 - 150)
  final newSecret = Uint8List.fromList(_data.secret);
  for (var i = 0; i < GFPoly.secretSize; i++) {
    newSecret[i] ^= mask[i];
  }
  newSecret[GFPoly.secretSize - 1] &= clearMask;

  return PolyseedMnemonic._(PolyseedData(
    birthday: _data.birthday,
    features: _data.features ^ PolyseedFeatures.encryptedBitMask,
    secret: newSecret,
    checksum: _data.checksum,
  ));
}