Skip to content

Commit

Permalink
Add PQC KEM key validation
Browse files Browse the repository at this point in the history
  • Loading branch information
larabr committed Jul 8, 2024
1 parent fb3886f commit ea3114e
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 3 deletions.
7 changes: 5 additions & 2 deletions src/crypto/crypto.js
Original file line number Diff line number Diff line change
Expand Up @@ -575,8 +575,11 @@ export async function validateParams(algo, publicParams, privateParams) {
return keySize === keyMaterial.length &&
util.equalsUint8Array(digest, await hash.sha256(hashSeed));
}
case enums.publicKey.pqc_mlkem_x25519:
throw new Error('TODO');
case enums.publicKey.pqc_mlkem_x25519: {
const { eccSecretKey, mlkemSecretKey } = privateParams;
const { eccPublicKey, mlkemPublicKey } = publicParams;
return publicKey.postQuantum.kem.validateParams(algo, eccPublicKey, eccSecretKey, mlkemPublicKey, mlkemSecretKey);
}
default:
throw new Error('Unknown public key algorithm.');
}
Expand Down
9 changes: 9 additions & 0 deletions src/crypto/public_key/post_quantum/kem/ecc_kem.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,12 @@ export async function decaps(algo, eccCipherText, eccSecretKey, eccPublicKey) {
throw new Error('Unsupported KEM algorithm');
}
}

export async function validateParams(algo, eccPublicKey, eccSecretKey) {
switch (algo) {
case enums.publicKey.pqc_mlkem_x25519:
return ecdhX.validateParams(enums.publicKey.x25519, eccPublicKey, eccSecretKey);
default:
throw new Error('Unsupported KEM algorithm');
}
}
2 changes: 1 addition & 1 deletion src/crypto/public_key/post_quantum/kem/index.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { generate, encrypt, decrypt } from './kem';
export { generate, encrypt, decrypt, validateParams } from './kem';
7 changes: 7 additions & 0 deletions src/crypto/public_key/post_quantum/kem/kem.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,10 @@ async function multiKeyCombine(eccKeyShare, eccCipherText, mlkemKeyShare, mlkemC

return mb;
}

export async function validateParams(algo, eccPublicKey, eccSecretKey, mlkemPublicKey, mlkemSecretKey) {
const eccValidationPromise = eccKem.validateParams(algo, eccPublicKey, eccSecretKey);
const mlkemValidationPromise = mlKem.validateParams(algo, mlkemPublicKey, mlkemSecretKey);
const valid = await eccValidationPromise && await mlkemValidationPromise;
return valid;
}
14 changes: 14 additions & 0 deletions src/crypto/public_key/post_quantum/kem/ml_kem.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import enums from '../../../../enums';
import util from '../../../../util';

export async function generate(algo) {
switch (algo) {
Expand Down Expand Up @@ -41,3 +42,16 @@ export async function decaps(algo, mlkemCipherText, mlkemSecretKey) {
throw new Error('Unsupported KEM algorithm');
}
}

export async function validateParams(algo, mlkemPublicKey, mlkemSecretKey) {
switch (algo) {
case enums.publicKey.pqc_mlkem_x25519: {
// TODO confirm this is the best option performance- & security-wise (is key re-generation faster?)
const { mlkemCipherText: validationCipherText, mlkemKeyShare: validationKeyShare } = await encaps(algo, mlkemPublicKey);
const resultingKeyShare = await decaps(algo, validationCipherText, mlkemSecretKey);
return util.equalsUint8Array(resultingKeyShare, validationKeyShare);
}
default:
throw new Error('Unsupported KEM algorithm');
}
}
32 changes: 32 additions & 0 deletions test/crypto/validate.js
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,38 @@ export default () => {
});
});

describe('PQC parameter validation', function() {
let pqcEncryptionSubkey;
before(async () => {
const key = await generatePrivateKeyObject({ type: 'symmetric', subkeys: [{ type: 'pqc' }] });
pqcEncryptionSubkey = key.subkeys[0];
});

async function cloneSubeyPacket(subkey) {
const subkeyPacket = new openpgp.SecretSubkeyPacket();
await subkeyPacket.read(subkey.keyPacket.write());
return subkeyPacket;
}

it('generated params are valid', async function() {
await expect(pqcEncryptionSubkey.keyPacket.validate()).to.not.be.rejected;
});

it('detect invalid ML-KEM public key part', async function() {
const keyPacket = await cloneSubeyPacket(pqcEncryptionSubkey);
const { mlkemPublicKey } = keyPacket.publicParams;
mlkemPublicKey[0]++;
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
});

it('detect invalid ECC-KEM key part', async function() {
const keyPacket = await cloneSubeyPacket(pqcEncryptionSubkey);
const { eccPublicKey } = keyPacket.publicParams;
eccPublicKey[0]++;
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
});
});

describe('DSA parameter validation', function() {
let dsaKey;
before(async () => {
Expand Down

0 comments on commit ea3114e

Please sign in to comment.