From f75f3ca966f33b87f1b34bbd8a7bcf2bc8cffe03 Mon Sep 17 00:00:00 2001 From: Paul Miller Date: Thu, 22 Aug 2024 14:05:49 +0000 Subject: [PATCH] Fix benchmarks and build --- .github/workflows/nodejs.yml | 1 + README.md | 57 +++++------ benchmark/ml-dsa.js | 186 +++++++---------------------------- benchmark/ml-kem.js | 184 ++++++++++------------------------ benchmark/slh-dsa.js | 56 ++++------- build/input.js | 15 ++- build/package-lock.json | 7 +- 7 files changed, 146 insertions(+), 360 deletions(-) diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 81bf353..0adc854 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -11,6 +11,7 @@ jobs: node: - 18 - 20 + - 22 steps: - uses: actions/checkout@1e31de5234b9f8995739874a8ce0492dc87873e2 # v4 - name: Use Node.js ${{ matrix.node }} diff --git a/README.md b/README.md index c585602..b4eb28f 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Auditable & minimal JS implementation of public-key post-quantum cryptography. - 🦾 ML-KEM & CRYSTALS-Kyber: lattice-based kem from FIPS-203 - 🔋 ML-DSA & CRYSTALS-Dilithium: lattice-based signatures from FIPS-204 - 🐈 SLH-DSA & SPHINCS+: hash-based signatures from FIPS-205 -- 🪶 113KB (20KB gzipped) for everything including bundled hashes, 71KB (14KB gzipped) for ML-KEM build +- 🪶 77KB (15KB gzipped) for everything including bundled hashes For discussions, questions and support, visit [GitHub Discussions](https://github.com/paulmillr/noble-post-quantum/discussions) @@ -45,12 +45,17 @@ A standalone file [noble-post-quantum.js](https://github.com/paulmillr/noble-post-quantum/releases) is also available. ```js -// import * from '@noble/post-quantum'; // Error: use sub-imports, to ensure small app size +// import * from '@noble/post-quantum'; // Error: use sub-imports instead import { ml_kem512, ml_kem768, ml_kem1024 } from '@noble/post-quantum/ml-kem'; import { ml_dsa44, ml_dsa65, ml_dsa87 } from '@noble/post-quantum/ml-dsa'; import { - slh_dsa_shake_128f, slh_dsa_shake_128s, slh_dsa_sha2_128f, slh_dsa_sha2_128s, -} from '@noble/ciphers/slh-dsa'; + slh_dsa_shake_128f, slh_dsa_shake_128s, + slh_dsa_shake_192f, slh_dsa_shake_192s, + slh_dsa_shake_256f, slh_dsa_shake_256s, + slh_dsa_sha2_128f, slh_dsa_sha2_128s, + slh_dsa_sha2_192f, slh_dsa_sha2_192s, + slh_dsa_sha2_256f, slh_dsa_sha2_256s, +} from '@noble/post-quantum/slh-dsa'; // import { ml_kem768 } from 'npm:@noble/post-quantum@0.1.0/ml-kem'; // Deno ``` @@ -74,14 +79,14 @@ import { | ML-DSA | Normal | 1.3 - 2.5KB | 2.5 - 4.5KB | 1990s | 2020s | Yes | | SLH-DSA | Slow | 32 - 128B | 17 - 50KB | 1970s | 2020s | Yes | -Speed (higher is better): +JS speed (higher is better): | OPs/sec | Keygen | Signing | Verification | Shared secret | | ------------ | ------ | ------- | ------------ | ------------- | | ECC ed25519 | 10270 | 5110 | 1050 | 1470 | | ML-KEM-512 | 3050 | | | 2090 | | ML-DSA44 | 580 | 170 | 550 | | -| SLH-DSA-128f | 200 | 8 | 140 | | +| SLH-DSA-SHA2-128f | 200 | 8 | 140 | | We suggest to use ECC + ML-KEM for key agreement, SLH-DSA for pq signatures. @@ -96,18 +101,19 @@ suffer less from quantum computers. For AES, simply update from AES-128 to AES-2 ```ts import { ml_kem512, ml_kem768, ml_kem1024 } from '@noble/post-quantum/ml-kem'; -const aliceKeys = ml_kem768.keygen(); // [Alice] generates key pair (secret and public key) -const alicePub = aliceKeys.publicKey; // [Alice] sends public key to Bob (somehow) +// [Alice] generates secret & public keys, then sends publicKey to Bob +const aliceKeys = ml_kem768.keygen(); +const alicePub = aliceKeys.publicKey; // [Bob] generates shared secret for Alice publicKey -// bobShared never leaves [Bob] system and unknown to other parties +// bobShared never leaves [Bob] system and is unknown to other parties const { cipherText, sharedSecret: bobShared } = ml_kem768.encapsulate(alicePub); // Alice gets and decrypts cipherText from Bob -const aliceShared = ml_kem768.decapsulate(cipherText, aliceKeys.secretKey); // [Alice] decrypts sharedSecret from Bob +const aliceShared = ml_kem768.decapsulate(cipherText, aliceKeys.secretKey); -// Now, both Alice and Both have same sharedSecret key without exchanging in plainText -// aliceShared == bobShared +// Now, both Alice and Both have same sharedSecret key +// without exchanging in plainText: aliceShared == bobShared // Warning: Can be MITM-ed const carolKeys = kyber1024.keygen(); @@ -117,24 +123,18 @@ notDeepStrictEqual(aliceShared, carolShared); // Different key! Lattice-based key encapsulation mechanism, defined in [FIPS-203](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.203.pdf). -See [official site](https://www.pq-crystals.org/kyber/resources.shtml) and [repo](https://github.com/pq-crystals/kyber). - -Key encapsulation is similar to DH / ECDH (think X25519), with important differences: - -- We can't verify if it was "Bob" who've sent the shared secret. - In ECDH, it's always verified -- It is probabalistic and relies on quality of randomness (CSPRNG). - ECDH doesn't (to this extent). -- Decapsulation never throws an error, even when shared secret was - encrypted by a different public key. It will just return a different - shared secret - +See [website](https://www.pq-crystals.org/kyber/resources.shtml) and [repo](https://github.com/pq-crystals/kyber). There are some concerns with regards to security: see [djb blog](https://blog.cr.yp.to/20231003-countcorrectly.html) and [mailing list](https://groups.google.com/a/list.nist.gov/g/pqc-forum/c/W2VOzy0wz_E). - Old, incompatible version (Kyber) is not provided. Open an issue if you need it. +> [!WARNING] +> Unlike ECDH, KEM doesn't verify whether it was "Bob" who've sent the ciphertext. +> Instead of throwing an error when the ciphertext is encrypted by a different pubkey, +> `decapsulate` will simply return a different shared secret. +> ML-KEM is also probabilistic and relies on quality of CSPRNG. + ### ML-DSA / Dilithium signatures ```ts @@ -147,7 +147,7 @@ const isValid = ml_dsa65.verify(aliceKeys.publicKey, msg, sig); ``` Lattice-based digital signature algorithm, defined in [FIPS-204](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.204.pdf). See -[official site](https://www.pq-crystals.org/dilithium/index.shtml) and +[website](https://www.pq-crystals.org/dilithium/index.shtml) and [repo](https://github.com/pq-crystals/dilithium). The internals are similar to ML-KEM, but keys and params are different. @@ -161,7 +161,7 @@ import { slh_dsa_sha2_128f, slh_dsa_sha2_128s, slh_dsa_sha2_192f, slh_dsa_sha2_192s, slh_dsa_sha2_256f, slh_dsa_sha2_256s, -} from '@noble/ciphers/slh-dsa'; +} from '@noble/post-quantum/slh-dsa'; const aliceKeys = sph.keygen(); const msg = new Uint8Array(1); @@ -169,7 +169,8 @@ const sig = sph.sign(aliceKeys.secretKey, msg); const isValid = sph.verify(aliceKeys.publicKey, msg, sig); ``` -Hash-based digital signature algorithm, defined in [FIPS-205](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.205.pdf). See [official site](https://sphincs.org) and [repo](https://github.com/sphincs/sphincsplus). +Hash-based digital signature algorithm, defined in [FIPS-205](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.205.pdf). +See [website](https://sphincs.org) and [repo](https://github.com/sphincs/sphincsplus). We implement spec v3.1 with FIPS adjustments. Some wasm libraries use older specs. ## Security diff --git a/benchmark/ml-dsa.js b/benchmark/ml-dsa.js index fe8e66c..8e1eb65 100644 --- a/benchmark/ml-dsa.js +++ b/benchmark/ml-dsa.js @@ -1,28 +1,10 @@ import { deepStrictEqual } from 'node:assert'; import { compare, utils } from 'micro-bmark'; import { - dilithium_v30, - dilithium_v31, ml_dsa44, ml_dsa65, ml_dsa87, - dilithium_v31_aes, } from '../ml-dsa.js'; - -import * as asanrom from '@asanrom/dilithium'; -import * as theqrl from '@theqrl/dilithium5'; - -// wasm -import { default as dashline2 } from '@dashlane/pqc-sign-dilithium2-node'; -import { default as dashline3 } from '@dashlane/pqc-sign-dilithium3-node'; -import { default as dashline5 } from '@dashlane/pqc-sign-dilithium5-node'; -let dl2, dl3, dl5; -async function initDashline() { - dl2 = await dashline2(); - dl3 = await dashline3(); - dl5 = await dashline5(); -} - const seed = new Uint8Array(32).fill(1); const msg = new Uint8Array(32).fill(2); @@ -38,150 +20,56 @@ const getNoble = (lib) => ({ verify: (opts) => deepStrictEqual(lib.verify(opts.publicKey, msg, opts.signature), true), }); -const getAsanrom = (n) => { - const level = asanrom.DilithiumLevel.get(n); - return { - keygen: () => asanrom.DilithiumKeyPair.generate(level), - sign: (opts) => { - deepStrictEqual( - asanrom.DilithiumPrivateKey.fromBytes(opts.secretKey, level).sign(msg).getBytes(), - opts.signature - ); - }, - verify: (opts) => { - deepStrictEqual( - asanrom.DilithiumPublicKey.fromBytes(opts.publicKey, level).verifySignature( - msg, - asanrom.DilithiumSignature.fromBytes(opts.signature, level) - ), - true - ); - }, - }; -}; - -const getDashline = (fn) => { - return { - keygen: async () => await fn().keypair(), - sign: async (opts) => - deepStrictEqual((await fn().sign(msg, opts.secretKey)).signature, opts.signature), - verify: async (opts) => - deepStrictEqual(await fn().verify(opts.signature, msg, opts.publicKey), true), - }; -}; - -const DILITHIUM = { - dilithium_v30_2: { - opts: getOpts(dilithium_v30.dilithium2), - asanrom: getAsanrom(2), - noble: getNoble(dilithium_v30.dilithium2), - }, - dilithium_v30_3: { - opts: getOpts(dilithium_v30.dilithium3), - asanrom: getAsanrom(3), - noble: getNoble(dilithium_v30.dilithium3), - }, - dilithium_v30_5: { - opts: getOpts(dilithium_v30.dilithium5), - asanrom: getAsanrom(5), - noble: getNoble(dilithium_v30.dilithium5), - }, - dilithium_v31_2: { - opts: getOpts(dilithium_v31.dilithium2), - dashline: getDashline(() => dl2), - noble: getNoble(dilithium_v31.dilithium2), - }, - dilithium_v31_3: { - opts: getOpts(dilithium_v31.dilithium3), - dashline: getDashline(() => dl3), - noble: getNoble(dilithium_v31.dilithium3), - }, - dilithium_v31_5: { - opts: getOpts(dilithium_v31.dilithium5), - dashline: getDashline(() => dl5), - theqrl: { - keygen: () => { - const pk = new Uint8Array(theqrl.CryptoPublicKeyBytes); - const sk = new Uint8Array(theqrl.CryptoSecretKeyBytes); - theqrl.cryptoSignKeypair(Buffer.from(seed), pk, sk); - }, - sign: (opts) => - deepStrictEqual( - theqrl - .cryptoSign(Buffer.from(msg), Buffer.from(opts.secretKey), false) - .subarray(0, -msg.length), - opts.signature - ), - verify: (opts) => - deepStrictEqual( - theqrl.cryptoSignVerify( - Buffer.from(opts.signature), - Buffer.from(msg), - Buffer.from(opts.publicKey) - ), - true - ), - }, - noble: getNoble(dilithium_v31.dilithium5), - }, - dilithium_v31_aes_2: { - opts: getOpts(dilithium_v31_aes.dilithium2), - noble: getNoble(dilithium_v31_aes.dilithium2), - }, - dilithium_v31_aes_3: { - opts: getOpts(dilithium_v31_aes.dilithium3), - noble: getNoble(dilithium_v31_aes.dilithium3), - }, - dilithium_v31_aes_5: { - opts: getOpts(dilithium_v31_aes.dilithium5), - noble: getNoble(dilithium_v31_aes.dilithium5), - }, - 'ML-DSA44': { +const MLDSA = { + 'v44': { opts: getOpts(ml_dsa44), noble: getNoble(ml_dsa44), }, - 'ML-DSA65': { + 'v65': { opts: getOpts(ml_dsa65), noble: getNoble(ml_dsa65), }, - 'ML-DSA87': { + 'v87': { opts: getOpts(ml_dsa87), noble: getNoble(ml_dsa87), }, }; const FNS = ['keygen', 'sign', 'verify']; -const SAMPLES = 100; export async function main() { - const onlyNoble = process.argv[2] === 'noble'; - if (onlyNoble) { - for (const fn of FNS) { - await compare( - `==== ${fn} ====`, - SAMPLES, - Object.fromEntries( - Object.entries(DILITHIUM).map(([k, v]) => [k, v.noble[fn].bind(null, v.opts)]) - ) - ); - } - return; - } - await initDashline(); - - for (const [algoName, libraries] of Object.entries(DILITHIUM)) { - for (const fn of FNS) { - const opts = libraries.opts; - await compare( - `==== ${algoName}/${fn} ====`, - SAMPLES, - Object.fromEntries( - Object.entries(libraries) - .filter(([k, v]) => k !== 'opts') - .map(([k, v]) => [k, v[fn].bind(null, opts)]) - ) - ); - } - } + await compare('keygen', 100, { + 'ML-DSA44': () => { + MLDSA.v44.noble.keygen(); + }, + 'ML-DSA65': () => { + MLDSA.v65.noble.keygen(); + }, + 'ML-DSA87': () => { + MLDSA.v87.noble.keygen(); + }, + }); + await compare('sign', 100, { + 'ML-DSA44': () => { + MLDSA.v44.noble.sign(MLDSA.v44.opts); + }, + 'ML-DSA65': () => { + MLDSA.v65.noble.sign(MLDSA.v65.opts); + }, + 'ML-DSA87': () => { + MLDSA.v87.noble.sign(MLDSA.v87.opts); + }, + }); + await compare('verify', 100, { + 'ML-DSA44': () => { + MLDSA.v44.noble.verify(MLDSA.v44.opts); + }, + 'ML-DSA65': () => { + MLDSA.v65.noble.verify(MLDSA.v65.opts); + }, + 'ML-DSA87': () => { + MLDSA.v87.noble.verify(MLDSA.v87.opts); + }, + }); utils.logMem(); } diff --git a/benchmark/ml-kem.js b/benchmark/ml-kem.js index f7a5089..5a02109 100644 --- a/benchmark/ml-kem.js +++ b/benchmark/ml-kem.js @@ -1,35 +1,27 @@ import { deepStrictEqual } from 'node:assert'; import { compare, utils } from 'micro-bmark'; import { - kyber512, - kyber768, - kyber1024, - kyber512_90s, - kyber768_90s, - kyber1024_90s, ml_kem512, ml_kem768, ml_kem1024, } from '../ml-kem.js'; -import * as ck from 'crystals-kyber'; -import * as ckjs from 'crystals-kyber-js'; -const ckjs512 = new ckjs.Kyber512(); -const ckjs768 = new ckjs.Kyber768(); -const ckjs1024 = new ckjs.Kyber1024(); +// import * as ck from 'crystals-kyber'; +// import * as ckjs from 'crystals-kyber-js'; +// const ckjs1024 = new ckjs.Kyber1024(); // broken package // import * as pqck from 'pqc-kyber/pqc_ml-kem.js'; // wasm -import { default as pqcrypto_js } from 'kyber-crystals'; //wasm +// import { default as pqcrypto_js } from 'kyber-crystals'; //wasm // wasm also -import { default as dashline512 } from '@dashlane/pqc-kem-kyber512-node'; -import { default as dashline768 } from '@dashlane/pqc-kem-kyber768-node'; -import { default as dashline1024 } from '@dashlane/pqc-kem-kyber1024-node'; -let dl512, dl768, dl1024; -async function initDashline() { - dl512 = await dashline512(); - dl768 = await dashline768(); - dl1024 = await dashline1024(); -} +// import { default as dashline512 } from '@dashlane/pqc-kem-kyber512-node'; +// import { default as dashline768 } from '@dashlane/pqc-kem-kyber768-node'; +// import { default as dashline1024 } from '@dashlane/pqc-kem-kyber1024-node'; +// let dl512, dl768, dl1024; +// async function initDashline() { +// dl512 = await dashline512(); +// dl768 = await dashline768(); +// dl1024 = await dashline1024(); +// } const getOpts = (lib) => { const { publicKey, secretKey } = lib.keygen(); @@ -44,113 +36,7 @@ const getNoble = (lib) => ({ deepStrictEqual(lib.decapsulate(opts.cipherText, opts.secretKey), opts.sharedSecret), }); -const KYBER = { - kyber512: { - opts: getOpts(kyber512), - dashline: { - keygen: () => dl512.keypair(), - encrypt: (opts) => dl512.encapsulate(opts.publicKey), - decrypt: async (opts) => - deepStrictEqual( - (await dl512.decapsulate(opts.cipherText, opts.secretKey)).sharedSecret, - opts.sharedSecret - ), - }, - ckjs: { - keygen: async () => await ckjs512.generateKeyPair(), - encrypt: async (opts) => await ckjs512.encap(opts.publicKey), - decrypt: async (opts) => - deepStrictEqual(await ckjs512.decap(opts.cipherText, opts.secretKey), opts.sharedSecret), - }, - ck: { - keygen: () => ck.KeyGen512(), - encrypt: (opts) => ck.Encrypt512(opts.publicKey), - decrypt: (opts) => - deepStrictEqual( - Uint8Array.from(ck.Decrypt512(opts.cipherText, opts.secretKey)), - opts.sharedSecret - ), - }, - noble: getNoble(kyber512), - }, - kyber768: { - opts: getOpts(kyber768), - dashline: { - keygen: () => dl768.keypair(), - encrypt: (opts) => dl768.encapsulate(opts.publicKey), - decrypt: async (opts) => - deepStrictEqual( - (await dl768.decapsulate(opts.cipherText, opts.secretKey)).sharedSecret, - opts.sharedSecret - ), - }, - ckjs: { - keygen: async () => await ckjs768.generateKeyPair(), - encrypt: async (opts) => await ckjs768.encap(opts.publicKey), - decrypt: async (opts) => - deepStrictEqual(await ckjs768.decap(opts.cipherText, opts.secretKey), opts.sharedSecret), - }, - ck: { - keygen: () => ck.KeyGen768(), - encrypt: (opts) => ck.Encrypt768(opts.publicKey), - decrypt: (opts) => - deepStrictEqual( - Uint8Array.from(ck.Decrypt768(opts.cipherText, opts.secretKey)), - opts.sharedSecret - ), - }, - noble: getNoble(kyber768), - }, - kyber1024: { - opts: getOpts(kyber1024), - // only 1024 - pqcrypto_js: { - keygen: () => pqcrypto_js.keyPair(), - encrypt: (opts) => pqcrypto_js.encrypt(opts.publicKey), - decrypt: async (opts) => - deepStrictEqual( - await pqcrypto_js.decrypt(opts.cipherText, opts.secretKey), - opts.sharedSecret - ), - }, - dashline: { - keygen: () => dl1024.keypair(), - encrypt: (opts) => dl1024.encapsulate(opts.publicKey), - decrypt: async (opts) => - deepStrictEqual( - (await dl1024.decapsulate(opts.cipherText, opts.secretKey)).sharedSecret, - opts.sharedSecret - ), - }, - ckjs: { - keygen: async () => await ckjs1024.generateKeyPair(), - encrypt: async (opts) => await ckjs1024.encap(opts.publicKey), - decrypt: async (opts) => - deepStrictEqual(await ckjs1024.decap(opts.cipherText, opts.secretKey), opts.sharedSecret), - }, - ck: { - keygen: () => ck.KeyGen1024(), - encrypt: (opts) => ck.Encrypt1024(opts.publicKey), - decrypt: (opts) => - deepStrictEqual( - Uint8Array.from(ck.Decrypt1024(opts.cipherText, opts.secretKey)), - opts.sharedSecret - ), - }, - noble: getNoble(kyber1024), - }, - kyber512_90s: { - opts: getOpts(kyber512_90s), - noble: getNoble(kyber512_90s), - }, - kyber768_90s: { - opts: getOpts(kyber768_90s), - noble: getNoble(kyber768_90s), - }, - kyber1024_90s: { - opts: getOpts(kyber1024_90s), - noble: getNoble(kyber1024_90s), - }, +const MLKEM = { 'ML-KEM-512': { opts: getOpts(ml_kem512), noble: getNoble(ml_kem512), @@ -161,12 +47,46 @@ const KYBER = { }, 'ML-KEM-1024': { opts: getOpts(ml_kem1024), + // // only 1024 + // pqcrypto_js: { + // keygen: () => pqcrypto_js.keyPair(), + // encrypt: (opts) => pqcrypto_js.encrypt(opts.publicKey), + // decrypt: async (opts) => + // deepStrictEqual( + // await pqcrypto_js.decrypt(opts.cipherText, opts.secretKey), + // opts.sharedSecret + // ), + // }, + // dashline: { + // keygen: () => dl1024.keypair(), + // encrypt: (opts) => dl1024.encapsulate(opts.publicKey), + // decrypt: async (opts) => + // deepStrictEqual( + // (await dl1024.decapsulate(opts.cipherText, opts.secretKey)).sharedSecret, + // opts.sharedSecret + // ), + // }, + // ckjs: { + // keygen: async () => await ckjs1024.generateKeyPair(), + // encrypt: async (opts) => await ckjs1024.encap(opts.publicKey), + // decrypt: async (opts) => + // deepStrictEqual(await ckjs1024.decap(opts.cipherText, opts.secretKey), opts.sharedSecret), + // }, + // ck: { + // keygen: () => ck.KeyGen1024(), + // encrypt: (opts) => ck.Encrypt1024(opts.publicKey), + // decrypt: (opts) => + // deepStrictEqual( + // Uint8Array.from(ck.Decrypt1024(opts.cipherText, opts.secretKey)), + // opts.sharedSecret + // ), + // }, noble: getNoble(ml_kem1024), }, }; const FNS = ['keygen', 'encrypt', 'decrypt']; -const SAMPLES = 10_000; +const SAMPLES = 5_000; export async function main() { const onlyNoble = process.argv[2] === 'noble'; if (onlyNoble) { @@ -175,14 +95,14 @@ export async function main() { `==== ${fn} ====`, SAMPLES, Object.fromEntries( - Object.entries(KYBER).map(([k, v]) => [k, v.noble[fn].bind(null, v.opts)]) + Object.entries(MLKEM).map(([k, v]) => [k, v.noble[fn].bind(null, v.opts)]) ) ); } return; } - await initDashline(); - for (const [algoName, libraries] of Object.entries(KYBER)) { + // await initDashline(); + for (const [algoName, libraries] of Object.entries(MLKEM)) { for (const fn of FNS) { const opts = libraries.opts; await compare( diff --git a/benchmark/slh-dsa.js b/benchmark/slh-dsa.js index ef13129..03e3f77 100644 --- a/benchmark/slh-dsa.js +++ b/benchmark/slh-dsa.js @@ -1,7 +1,6 @@ import { deepStrictEqual } from 'node:assert'; import { compare, utils } from 'micro-bmark'; -import * as sphincs_sha2 from '../slh-dsa.js'; -import * as sphincs_shake from '../slh-dsa.js'; +import * as dsa from '../slh-dsa.js'; // wasm // import { default as wasmSphincs } from 'sphincs'; @@ -26,43 +25,22 @@ const getNoble = (lib) => ({ const testNoble = (lib) => ({ opts: getOpts(lib), noble: getNoble(lib) }); -const SPHINCS = { - // Fast - sphincs_sha2_128f_simple: testNoble(sphincs_sha2.sphincs_sha2_128f_simple), - sphincs_sha2_192f_simple: testNoble(sphincs_sha2.sphincs_sha2_192f_simple), - sphincs_sha2_256f_simple: testNoble(sphincs_sha2.sphincs_sha2_256f_simple), - // sphincs_sha2_128f_robust: testNoble(sphincs_sha2.sphincs_sha2_128f_robust), +const SLHDSA = { + slh_dsa_shake_128f: testNoble(dsa.slh_dsa_shake_128f), + slh_dsa_shake_192f: testNoble(dsa.slh_dsa_shake_192f), + slh_dsa_shake_256f: testNoble(dsa.slh_dsa_shake_256f), - // s version is too slow for now - // sphincs_sha2_128s_simple: testNoble(sphincs.sphincs_sha2_128s_simple), - // sphincs_sha2_128s_robust: testNoble(sphincs.sphincs_sha2_128s_robust), + slh_dsa_sha2_128f: testNoble(dsa.slh_dsa_sha2_128f), + slh_dsa_sha2_192f: testNoble(dsa.slh_dsa_sha2_192f), + slh_dsa_sha2_256f: testNoble(dsa.slh_dsa_sha2_256f), - sphincs_shake_128f_simple: testNoble(sphincs_shake.sphincs_shake_128f_simple), - sphincs_shake_192f_simple: testNoble(sphincs_shake.sphincs_shake_192f_simple), - sphincs_shake_256f_simple: testNoble(sphincs_shake.sphincs_shake_256f_simple), - - //sphincs_shake_128f_robust: testNoble(sphincs_shake.sphincs_shake_128f_robust), - - // Worst case: - //sphincs_sha2_256s_robust: testNoble(sphincs.sphincs_sha2_256s_robust), - //sphincs_shake_256s_robust: testNoble(sphincs.sphincs_shake_256s_robust), - - // sphincs_shake_256s_robust: { - // opts: getOpts(sphincs.sphincs_shake_256s_robust), - // // The default parameter set is SPHINCS+-SHAKE-256s-robust (roughly 256-bit strength). - // wasm: { - // keygen: async () => await wasmSphincs.keyPair(), - // // Cannot provide random & verify - // // Also, seems like different version of sphincs. Awesome. - // sign: async (opts) => await wasmSphincs.signDetached(msg, opts.secretKey), - // verify: async (opts) => - // deepStrictEqual( - // await wasmSphincs.verifyDetached(opts.signature, msg, opts.publicKey), - // true - // ), - // }, - // noble: getNoble(sphincs.sphincs_shake_256s_robust), - // }, + // Too slow + // slh_dsa_shake_128s: testNoble(dsa.slh_dsa_shake_128s), + // slh_dsa_shake_192s: testNoble(dsa.slh_dsa_shake_192s), + // slh_dsa_shake_256s: testNoble(dsa.slh_dsa_shake_256s), + // slh_dsa_sha2_128s: testNoble(dsa.slh_dsa_sha2_128s), + // slh_dsa_sha2_192s: testNoble(dsa.slh_dsa_sha2_192s), + // slh_dsa_sha2_256s: testNoble(dsa.slh_dsa_sha2_256s), }; const FNS = ['keygen', 'sign', 'verify']; @@ -75,13 +53,13 @@ export async function main() { `==== ${fn} ====`, SAMPLES, Object.fromEntries( - Object.entries(SPHINCS).map(([k, v]) => [k, v.noble[fn].bind(null, v.opts)]) + Object.entries(SLHDSA).map(([k, v]) => [k, v.noble[fn].bind(null, v.opts)]) ) ); } return; } - for (const [algoName, libraries] of Object.entries(SPHINCS)) { + for (const [algoName, libraries] of Object.entries(SLHDSA)) { for (const fn of FNS) { const opts = libraries.opts; await compare( diff --git a/build/input.js b/build/input.js index 86512ea..100c044 100644 --- a/build/input.js +++ b/build/input.js @@ -1,11 +1,10 @@ export { ml_kem512, ml_kem768, ml_kem1024 } from '@noble/post-quantum/ml-kem'; -export { kyber512, kyber768, kyber1024 } from '@noble/post-quantum/ml-kem'; export { ml_dsa44, ml_dsa65, ml_dsa87 } from '@noble/post-quantum/ml-dsa'; export { - slh_dsa_sha2_128f, - slh_dsa_sha2_128s, - slh_dsa_sha2_192f, - slh_dsa_sha2_192s, - slh_dsa_sha2_256f, - slh_dsa_sha2_256s, -} from '@noble/post-quantum/slh-dsa'; + slh_dsa_shake_128f, slh_dsa_shake_128s, + slh_dsa_shake_192f, slh_dsa_shake_192s, + slh_dsa_shake_256f, slh_dsa_shake_256s, + slh_dsa_sha2_128f, slh_dsa_sha2_128s, + slh_dsa_sha2_192f, slh_dsa_sha2_192s, + slh_dsa_sha2_256f, slh_dsa_sha2_256s, +} from '@noble/post-quantum/slh-dsa'; \ No newline at end of file diff --git a/build/package-lock.json b/build/package-lock.json index ea10ee9..491b14c 100644 --- a/build/package-lock.json +++ b/build/package-lock.json @@ -18,16 +18,15 @@ "dev": true, "license": "MIT", "dependencies": { - "@noble/ciphers": "0.5.2", "@noble/hashes": "1.4.0" }, "devDependencies": { - "@paulmillr/jsbt": "0.1.0", + "@paulmillr/jsbt": "0.2.1", "@scure/base": "1.1.5", "micro-bmark": "0.3.1", "micro-should": "0.4.0", - "prettier": "3.1.1", - "typescript": "5.3.2" + "prettier": "3.3.2", + "typescript": "5.5.2" }, "funding": { "url": "https://paulmillr.com/funding/"