pqOpen function

Future<Uint8List> pqOpen(
  1. AtKemAlgorithm kem,
  2. Uint8List recipientSecretKey,
  3. Uint8List envelope, {
  4. required Uint8List info,
  5. 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');
  }
}