pqOpen function
Future<Uint8List>
pqOpen(
- AtKemAlgorithm kem,
- Uint8List recipientSecretKey,
- Uint8List envelope, {
- required Uint8List info,
- Uint8List? aad,
Open an envelope produced by pqSeal using recipientSecretKey.
kem must be the same KEM type used by the sender. info/aad must
match what the sender supplied — info is required for the reason given on
pqSeal, and passing the wrong one is indistinguishable from a tampered
envelope.
Throws PqOpenException on any failure (see PqOpenFailure).
Implementation
Future<Uint8List> pqOpen(
AtKemAlgorithm kem,
Uint8List recipientSecretKey,
Uint8List envelope, {
required Uint8List info,
Uint8List? aad,
}) async {
if (envelope.length < 3) {
throw PqOpenException(
PqOpenFailure.malformedEnvelope, 'envelope shorter than header');
}
final int ver = envelope[0];
final _SealVersion? row = _versions[ver];
if (row == null) {
throw PqOpenException(PqOpenFailure.versionMismatch,
'unsupported envelope version 0x${ver.toRadixString(16)}');
}
final int ctLen = (envelope[1] << 8) | envelope[2];
if (envelope.length < 3 + ctLen + row.aead.tagLength) {
throw PqOpenException(PqOpenFailure.malformedEnvelope,
'declared ciphertext length overruns envelope');
}
final Uint8List kemCt = Uint8List.sublistView(envelope, 3, 3 + ctLen);
// aeadBody = aeadCiphertext || tag; the AEAD splits the tag off itself.
final Uint8List aeadBody = Uint8List.sublistView(envelope, 3 + ctLen);
// Decapsulation rejects a wrong-length secret key or KEM ciphertext with an
// ArgumentError. That is still a malformed envelope from the caller's side,
// and letting it escape would break the documented contract that every
// failure arrives as a PqOpenException — leaving a caller who catches the
// documented type with an uncaught error on a bad input.
final Uint8List ss;
try {
ss = await kem.decapsulate(recipientSecretKey, kemCt);
} on ArgumentError catch (e) {
throw PqOpenException(PqOpenFailure.malformedEnvelope,
'decapsulation rejected the input: $e');
}
final _DerivedKey dk = _deriveKeyAndNonce(ss, ver, info);
try {
return await row.aead.decrypt(aeadBody,
key: dk.key, nonce: dk.nonce, aad: aad ?? const <int>[]);
} on AtDecryptionException {
throw PqOpenException(
PqOpenFailure.authFailure, 'AEAD authentication failed');
}
}