Skip to content

Commit

Permalink
WIP: add ML-DSA + Ed25519 (Node only)
Browse files Browse the repository at this point in the history
Dilithium lib uses Buffer and doesn't work with browser
  • Loading branch information
larabr committed Sep 13, 2024
1 parent 75568f8 commit 10f0e9d
Show file tree
Hide file tree
Showing 13 changed files with 1,431 additions and 3 deletions.
94 changes: 94 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"@noble/ed25519": "^1.7.3",
"@noble/hashes": "^1.5.0",
"@noble/post-quantum": "^0.2.0",
"@asanrom/dilithium": "^1.1.0",
"@openpgp/jsdoc": "^3.6.11",
"@openpgp/seek-bzip": "^1.0.5-git",
"@openpgp/tweetnacl": "^1.0.4-1",
Expand Down
2 changes: 1 addition & 1 deletion rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const wasmOptions = {

const getChunkFileName = (chunkInfo, extension) => {
// index files result in chunks named simply 'index', so we rename them to include the package name
if (chunkInfo.name === 'index') {
if (chunkInfo.name === 'index' && chunkInfo.facadeModuleId) {
const packageName = chunkInfo.facadeModuleId.split('/').at(-2); // assume index file is under the root folder
return `${packageName}.${extension}`;
}
Expand Down
20 changes: 19 additions & 1 deletion src/crypto/crypto.js
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,11 @@ export function parsePublicKeyParams(algo, bytes) {
const mlkemPublicKey = util.readExactSubarray(bytes, read, read + 1184); read += mlkemPublicKey.length;
return { read, publicParams: { eccPublicKey, mlkemPublicKey } };
}
case enums.publicKey.pqc_mldsa_ed25519: {
const eccPublicKey = util.readExactSubarray(bytes, read, read + getCurvePayloadSize(enums.publicKey.ed25519)); read += eccPublicKey.length;
const mldsaPublicKey = util.readExactSubarray(bytes, read, read + 1952); read += mldsaPublicKey.length;
return { read, publicParams: { eccPublicKey, mldsaPublicKey } };
}
default:
throw new UnsupportedError('Unknown public key encryption algorithm.');
}
Expand Down Expand Up @@ -323,6 +328,11 @@ export function parsePrivateKeyParams(algo, bytes, publicParams) {
const mlkemSecretKey = util.readExactSubarray(bytes, read, read + 2400); read += mlkemSecretKey.length;
return { read, privateParams: { eccSecretKey, mlkemSecretKey } };
}
case enums.publicKey.pqc_mldsa_x25519: {
const eccSecretKey = util.readExactSubarray(bytes, read, read + getCurvePayloadSize(enums.publicKey.ed25519)); read += eccSecretKey.length;
const mldsaSecretKey = util.readExactSubarray(bytes, read, read + 4032); read += mldsaSecretKey.length;
return { read, privateParams: { eccSecretKey, mldsaSecretKey } };
}
default:
throw new UnsupportedError('Unknown public key encryption algorithm.');
}
Expand Down Expand Up @@ -412,7 +422,8 @@ export function serializeParams(algo, params) {
enums.publicKey.x448,
enums.publicKey.aead,
enums.publicKey.hmac,
enums.publicKey.pqc_mlkem_x25519
enums.publicKey.pqc_mlkem_x25519,
enums.publicKey.pqc_mldsa_ed25519
]);
const orderedParams = Object.keys(params).map(name => {
const param = params[name];
Expand Down Expand Up @@ -484,6 +495,11 @@ export async function generateParams(algo, bits, oid, symmetric) {
privateParams: { eccSecretKey, mlkemSecretKey },
publicParams: { eccPublicKey, mlkemPublicKey }
}));
case enums.publicKey.pqc_mldsa_ed25519:
return publicKey.postQuantum.signature.generate(algo).then(({ eccSecretKey, eccPublicKey, mldsaSecretKey, mldsaPublicKey }) => ({
privateParams: { eccSecretKey, mldsaSecretKey },
publicParams: { eccPublicKey, mldsaPublicKey }
}));
case enums.publicKey.dsa:
case enums.publicKey.elgamal:
throw new Error('Unsupported algorithm for key generation.');
Expand Down Expand Up @@ -580,6 +596,8 @@ export async function validateParams(algo, publicParams, privateParams) {
const { eccPublicKey, mlkemPublicKey } = publicParams;
return publicKey.postQuantum.kem.validateParams(algo, eccPublicKey, eccSecretKey, mlkemPublicKey, mlkemSecretKey);
}
case enums.publicKey.pqc_mldsa_ed25519:
throw new Error('TODO');
default:
throw new Error('Unknown public key algorithm.');
}
Expand Down
4 changes: 3 additions & 1 deletion src/crypto/public_key/post_quantum/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import * as kem from './kem/index';
import * as signature from './signature';

export {
kem
kem,
signature
};
40 changes: 40 additions & 0 deletions src/crypto/public_key/post_quantum/signature/ecc_dsa.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// TODOOOOO is this file needed? vs inlining calls in signature.js?


import * as eddsa from '../../elliptic/eddsa';
import enums from '../../../../enums';

export async function generate(algo) {
switch (algo) {
case enums.publicKey.pqc_mldsa_ed25519: {
const { A, seed } = await eddsa.generate(enums.publicKey.ed25519);
return {
eccPublicKey: A,
eccSecretKey: seed
};
}
default:
throw new Error('Unsupported signature algorithm');
}
}

export async function sign(signatureAlgo, hashAlgo, eccSecretKey, eccPublicKey, dataDigest) {
switch (signatureAlgo) {
case enums.publicKey.pqc_mldsa_ed25519: {
const { RS: eccSignature } = await eddsa.sign(enums.publicKey.ed25519, hashAlgo, null, eccPublicKey, eccSecretKey, dataDigest);

return { eccSignature };
}
default:
throw new Error('Unsupported signature algorithm');
}
}

export async function verify(signatureAlgo, hashAlgo, eccPublicKey, dataDigest, eccSignature) {
switch (signatureAlgo) {
case enums.publicKey.pqc_mldsa_ed25519:
return eddsa.verify(enums.publicKey.ed25519, hashAlgo, { RS: eccSignature }, null, eccPublicKey, dataDigest);
default:
throw new Error('Unsupported signature algorithm');
}
}
1 change: 1 addition & 0 deletions src/crypto/public_key/post_quantum/signature/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { generate, sign, verify } from './signature';
47 changes: 47 additions & 0 deletions src/crypto/public_key/post_quantum/signature/ml_dsa.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import enums from '../../../../enums';

export async function generate(algo) {
switch (algo) {
case enums.publicKey.pqc_mldsa_ed25519: {
const { DilithiumKeyPair, DilithiumLevel } = await import('@asanrom/dilithium');

const level = DilithiumLevel.get(3);
const keyPair = DilithiumKeyPair.generate(level);

const mldsaSecretKey = keyPair.getPrivateKey().getBytes();
const mldsaPublicKey = keyPair.getPublicKey().getBytes();

return { mldsaSecretKey, mldsaPublicKey };
}
default:
throw new Error('Unsupported signature algorithm');
}
}

export async function sign(algo, mldsaSecretKey, dataDigest) {
switch (algo) {
case enums.publicKey.pqc_mldsa_ed25519: {
const { DilithiumPrivateKey, DilithiumLevel } = await import('@asanrom/dilithium');
const level = DilithiumLevel.get(3);
const secretKey = DilithiumPrivateKey.fromBytes(mldsaSecretKey, level);
const mldsaSignature = secretKey.sign(dataDigest).getBytes();
return { mldsaSignature };
}
default:
throw new Error('Unsupported signature algorithm');
}
}

export async function verify(algo, mldsaPublicKey, dataDigest, mldsaSignature) {
switch (algo) {
case enums.publicKey.pqc_mldsa_ed25519: {
const { DilithiumPublicKey, DilithiumSignature, DilithiumLevel } = await import('@asanrom/dilithium');
const level = DilithiumLevel.get(3);
const publicKey = DilithiumPublicKey.fromBytes(mldsaPublicKey, level);
const signature = DilithiumSignature.fromBytes(mldsaSignature, level);
return publicKey.verifySignature(dataDigest, signature);
}
default:
throw new Error('Unsupported signature algorithm');
}
}
41 changes: 41 additions & 0 deletions src/crypto/public_key/post_quantum/signature/signature.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import enums from '../../../../enums';
import * as mldsa from './ml_dsa';
import * as eccdsa from './ecc_dsa';

export async function generate(algo) {
switch (algo) {
case enums.publicKey.pqc_mldsa_ed25519: {
const { eccSecretKey, eccPublicKey } = await eccdsa.generate(algo);
const { mldsaSecretKey, mldsaPublicKey } = await mldsa.generate(algo);
return { eccSecretKey, eccPublicKey, mldsaSecretKey, mldsaPublicKey };
}
default:
throw new Error('Unsupported signature algorithm');
}
}

export async function sign(signatureAlgo, hashAlgo, eccSecretKey, eccPublicKey, mldsaSecretKey, dataDigest) {
switch (signatureAlgo) {
case enums.publicKey.pqc_mldsa_ed25519: {
const { eccSignature } = await eccdsa.sign(signatureAlgo, hashAlgo, eccSecretKey, eccPublicKey, dataDigest);
const { mldsaSignature } = await mldsa.sign(signatureAlgo, mldsaSecretKey, dataDigest);

return { eccSignature, mldsaSignature };
}
default:
throw new Error('Unsupported signature algorithm');
}
}

export async function verify(signatureAlgo, hashAlgo, eccPublicKey, mldsaPublicKey, dataDigest, { eccSignature, mldsaSignature }) {
switch (signatureAlgo) {
case enums.publicKey.pqc_mldsa_ed25519: {
const eccVerifiedPromise = eccdsa.verify(signatureAlgo, hashAlgo, eccPublicKey, dataDigest, eccSignature);
const mldsaVerifiedPromise = mldsa.verify(signatureAlgo, mldsaPublicKey, dataDigest, mldsaSignature);
const verified = await eccVerifiedPromise && await mldsaVerifiedPromise;
return verified;
}
default:
throw new Error('Unsupported signature algorithm');
}
}
15 changes: 15 additions & 0 deletions src/crypto/signature.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ export function parseSignatureParams(algo, signature) {
const mac = new ShortByteString(); read += mac.read(signature.subarray(read));
return { read, signatureParams: { mac } };
}
case enums.publicKey.pqc_mldsa_ed25519: {
const eccSignatureSize = 2 * publicKey.elliptic.eddsa.getPayloadSize(algo);
const eccSignature = util.readExactSubarray(signature, read, read + eccSignatureSize); read += eccSignature.length;
const mldsaSignature = util.readExactSubarray(signature, read, read + 3309); read += mldsaSignature.length;
return { read, signatureParams: { eccSignature, mldsaSignature } };
}
default:
throw new UnsupportedError('Unknown signature algorithm.');
}
Expand Down Expand Up @@ -129,6 +135,10 @@ export async function verify(algo, hashAlgo, signature, publicParams, privatePar
const { keyMaterial } = privateParams;
return publicKey.hmac.verify(algo.getValue(), keyMaterial, signature.mac.data, hashed);
}
case enums.publicKey.pqc_mldsa_ed25519: {
const { eccPublicKey, mldsaPublicKey } = publicParams;
return publicKey.postQuantum.signature.verify(algo, hashAlgo, eccPublicKey, mldsaPublicKey, hashed, signature);
}
default:
throw new Error('Unknown signature algorithm.');
}
Expand Down Expand Up @@ -190,6 +200,11 @@ export async function sign(algo, hashAlgo, publicKeyParams, privateKeyParams, da
const mac = await publicKey.hmac.sign(algo.getValue(), keyMaterial, hashed);
return { mac: new ShortByteString(mac) };
}
case enums.publicKey.pqc_mldsa_ed25519: {
const { eccPublicKey } = publicKeyParams;
const { eccSecretKey, mldsaSecretKey } = privateKeyParams;
return publicKey.postQuantum.signature.sign(algo, hashAlgo, eccSecretKey, eccPublicKey, mldsaSecretKey, hashed);
}
default:
throw new Error('Unknown signature algorithm.');
}
Expand Down
Loading

0 comments on commit 10f0e9d

Please sign in to comment.