From 0fdd763dc7ed1abc447348650a90a0c2fee2e08a Mon Sep 17 00:00:00 2001 From: Paul Miller Date: Thu, 16 Feb 2023 11:32:18 +0000 Subject: [PATCH] montgomery: add randomPrivateKey. Add ecdh benchmark. --- README.md | 19 ++++++++++++------- benchmark/ecdh.js | 19 +++++++++++++++++++ package.json | 2 +- src/abstract/montgomery.ts | 3 +++ src/ed25519.ts | 1 + src/ed448.ts | 1 + 6 files changed, 37 insertions(+), 8 deletions(-) create mode 100644 benchmark/ecdh.js diff --git a/README.md b/README.md index df38368..57c1451 100644 --- a/README.md +++ b/README.md @@ -66,10 +66,6 @@ secp256k1.verify(sig, msg, pub) === true; const privHex = '46c930bc7bb4db7f55da20798697421b98c4175a52c630294d75a84b9c126236'; const pub2 = secp256k1.getPublicKey(privHex); // keys & other inputs can be Uint8Array-s or hex strings - -// Follows hash-to-curve specification to encode arbitrary hashes to EC points -import { hashToCurve, encodeToCurve } from '@noble/curves/secp256k1'; -hashToCurve('0102abcd'); ``` All curves: @@ -180,10 +176,9 @@ const signatures3 = privateKeys.map((p, i) => bls.sign(messages[i], p)); const aggSignature3 = bls.aggregateSignatures(signatures3); const isValid3 = bls.verifyBatch(aggSignature3, messages, publicKeys); console.log({ publicKeys, signatures3, aggSignature3, isValid3 }); +// bls.pairing(PointG1, PointG2) // pairings -// Pairings -// bls.pairing(PointG1, PointG2) -// Also, check out hash-to-curve examples below. +// hash-to-curve examples can be seen below ``` ## Abstract API @@ -482,9 +477,11 @@ Every curve has exported `hashToCurve` and `encodeToCurve` methods: ```ts import { hashToCurve, encodeToCurve } from '@noble/curves/secp256k1'; import { randomBytes } from '@noble/hashes/utils'; +hashToCurve('0102abcd'); console.log(hashToCurve(randomBytes())); console.log(encodeToCurve(randomBytes())); + import { bls12_381 } from '@noble/curves/bls12-381'; bls12_381.G1.hashToCurve(randomBytes(), { DST: 'another' }); bls12_381.G2.hashToCurve(randomBytes(), { DST: 'custom' }); @@ -674,6 +671,14 @@ pedersen x 884 ops/sec @ 1ms/op poseidon x 8,598 ops/sec @ 116μs/op verify x 528 ops/sec @ 1ms/op +ecdh +├─x25519 x 1,337 ops/sec @ 747μs/op +├─secp256k1 x 461 ops/sec @ 2ms/op +├─P256 x 441 ops/sec @ 2ms/op +├─P384 x 179 ops/sec @ 5ms/op +├─P521 x 93 ops/sec @ 10ms/op +└─x448 x 496 ops/sec @ 2ms/op + bls12-381 init x 32 ops/sec @ 30ms/op getPublicKey 1-bit x 858 ops/sec @ 1ms/op diff --git a/benchmark/ecdh.js b/benchmark/ecdh.js new file mode 100644 index 0000000..c238826 --- /dev/null +++ b/benchmark/ecdh.js @@ -0,0 +1,19 @@ +import { run, mark, compare, utils } from 'micro-bmark'; +import { generateData } from './_shared.js'; +import { secp256k1 } from '../secp256k1.js'; +import { P256 } from '../p256.js'; +import { P384 } from '../p384.js'; +import { P521 } from '../p521.js'; +import { x25519 } from '../ed25519.js'; +import { x448 } from '../ed448.js'; + +run(async () => { + const curves = { x25519, secp256k1, P256, P384, P521, x448 }; + const fns = {}; + for (let [k, c] of Object.entries(curves)) { + const pubB = c.getPublicKey(c.utils.randomPrivateKey()); + const privA = c.utils.randomPrivateKey(); + fns[k] = () => c.getSharedSecret(privA, pubB); + } + await compare('ecdh', 1000, fns); +}); diff --git a/package.json b/package.json index 6522b7a..2b26ae4 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "*.d.ts.map" ], "scripts": { - "bench": "cd benchmark; node secp256k1.js; node curves.js; node stark.js; node bls.js", + "bench": "cd benchmark; node secp256k1.js; node curves.js; node ecdh.js; node stark.js; node bls.js", "build": "tsc && tsc -p tsconfig.esm.json", "build:release": "rollup -c rollup.config.js", "lint": "prettier --check 'src/**/*.{js,ts}' 'test/*.js'", diff --git a/src/abstract/montgomery.ts b/src/abstract/montgomery.ts index 2b01898..d02a0d5 100644 --- a/src/abstract/montgomery.ts +++ b/src/abstract/montgomery.ts @@ -16,12 +16,14 @@ export type CurveType = { powPminus2?: (x: bigint) => bigint; xyToU?: (x: bigint, y: bigint) => bigint; Gu: bigint; + randomBytes?: (bytesLength?: number) => Uint8Array; }; export type CurveFn = { scalarMult: (scalar: Hex, u: Hex) => Uint8Array; scalarMultBase: (scalar: Hex) => Uint8Array; getSharedSecret: (privateKeyA: Hex, publicKeyB: Hex) => Uint8Array; getPublicKey: (privateKey: Hex) => Uint8Array; + utils: { randomPrivateKey: () => Uint8Array; }; GuBytes: Uint8Array; }; @@ -181,6 +183,7 @@ export function montgomery(curveDef: CurveType): CurveFn { scalarMultBase, getSharedSecret: (privateKey: Hex, publicKey: Hex) => scalarMult(privateKey, publicKey), getPublicKey: (privateKey: Hex): Uint8Array => scalarMultBase(privateKey), + utils: { randomPrivateKey: () => CURVE.randomBytes!(CURVE.nByteLength) }, GuBytes: GuBytes, }; } diff --git a/src/ed25519.ts b/src/ed25519.ts index 584e241..8d457b3 100644 --- a/src/ed25519.ts +++ b/src/ed25519.ts @@ -149,6 +149,7 @@ export const x25519 = montgomery({ return mod(pow2(pow_p_5_8, BigInt(3), P) * b2, P); }, adjustScalarBytes, + randomBytes, }); // Hash To Curve Elligator2 Map (NOTE: different from ristretto255 elligator) diff --git a/src/ed448.ts b/src/ed448.ts index 97c73d4..eb1cb17 100644 --- a/src/ed448.ts +++ b/src/ed448.ts @@ -134,6 +134,7 @@ export const x448 = montgomery({ return mod(Pminus3 * x, P); // Pminus3 * x = Pminus2 }, adjustScalarBytes, + randomBytes, // The 4-isogeny maps between the Montgomery curve and this Edwards // curve are: // (u, v) = (y^2/x^2, (2 - x^2 - y^2)*y/x^3)