deriveHybridSecretKey method
Future<SecretKeyData>
deriveHybridSecretKey({
- required SecretKey postQuantumSecret,
- required Uint8List info,
- Uint8List? salt,
- PqHybridProfile profile = PqHybridProfile.balanced,
- int length = PqForgeCombiner.defaultLength,
Derives a hybrid session key from this classical secret and a post-quantum secret.
final session = await classicalSecret.deriveHybridSecretKey(
postQuantumSecret: mlKemSecret,
info: utf8.encode('myapp/session/v1') as Uint8List,
profile: PqHybridProfile.heavy,
);
final cipher = AesGcm.with256bits();
final box = await cipher.encrypt(message, secretKey: session);
See PqForgeCombiner.combine for the meaning of info, salt, and
length. info is mandatory for domain separation.
Memory hygiene: the secret bytes extracted from both keys are copied into
private buffers and zeroized in a finally once derivation completes. The
two input crypto.SecretKeys are owned by the caller and are left intact.
Implementation
Future<crypto.SecretKeyData> deriveHybridSecretKey({
required crypto.SecretKey postQuantumSecret,
required Uint8List info,
Uint8List? salt,
PqHybridProfile profile = PqHybridProfile.balanced,
int length = PqForgeCombiner.defaultLength,
}) async {
// `extractBytes()` may hand back a key's internal storage, so copy into
// owned buffers before any in-place wiping.
final classicalSharedSecret = Uint8List.fromList(await extractBytes());
final postQuantumSharedSecret = Uint8List.fromList(
await postQuantumSecret.extractBytes(),
);
try {
final sessionKey = PqForgeCombiner(profile: profile).combine(
classicalSharedSecret: classicalSharedSecret,
postQuantumSharedSecret: postQuantumSharedSecret,
info: info,
salt: salt,
length: length,
);
return crypto.SecretKeyData(sessionKey);
} finally {
PqForgeCombiner.wipe(classicalSharedSecret);
PqForgeCombiner.wipe(postQuantumSharedSecret);
}
}