decrypt method
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,
));
}