forked from tornado-packages/noble-curves
Refactor, benchmarks
This commit is contained in:
parent
9e5ad8dc85
commit
bbe46843fb
@ -2,6 +2,9 @@ import * as bench from 'micro-bmark';
|
||||
const { run, mark } = bench; // or bench.mark
|
||||
// Curves
|
||||
import { secp256k1 } from '../lib/secp256k1.js';
|
||||
import { P256 } from '../lib/p256.js';
|
||||
import { P384 } from '../lib/p384.js';
|
||||
import { P521 } from '../lib/p521.js';
|
||||
import { ed25519 } from '../lib/ed25519.js';
|
||||
import { ed448 } from '../lib/ed448.js';
|
||||
|
||||
@ -10,15 +13,18 @@ import { hmac } from '@noble/hashes/hmac';
|
||||
import { sha256 } from '@noble/hashes/sha256';
|
||||
import { sha512 } from '@noble/hashes/sha512';
|
||||
|
||||
import * as noble_secp256k1 from '@noble/secp256k1';
|
||||
import * as old_secp from '@noble/secp256k1';
|
||||
import { concatBytes, hexToBytes } from '@noble/hashes/utils';
|
||||
|
||||
noble_secp256k1.utils.sha256Sync = (...msgs) =>
|
||||
import * as starkwareCrypto from '@starkware-industries/starkware-crypto-utils';
|
||||
import * as stark from '../lib/stark';
|
||||
|
||||
old_secp.utils.sha256Sync = (...msgs) =>
|
||||
sha256
|
||||
.create()
|
||||
.update(concatBytes(...msgs))
|
||||
.digest();
|
||||
noble_secp256k1.utils.hmacSha256Sync = (key, ...msgs) =>
|
||||
old_secp.utils.hmacSha256Sync = (key, ...msgs) =>
|
||||
hmac
|
||||
.create(sha256, key)
|
||||
.update(concatBytes(...msgs))
|
||||
@ -28,44 +34,57 @@ import * as noble_ed25519 from '@noble/ed25519';
|
||||
secp256k1.utils.precompute(8); // Not enabled by default?
|
||||
ed25519.utils.precompute(8);
|
||||
ed448.utils.precompute(8);
|
||||
P256.utils.precompute(8);
|
||||
P384.utils.precompute(8);
|
||||
P521.utils.precompute(8);
|
||||
|
||||
noble_ed25519.utils.sha512Sync = (...m) => sha512(concatBytes(...m));
|
||||
noble_secp256k1.utils.precompute(8);
|
||||
old_secp.utils.precompute(8);
|
||||
noble_ed25519.utils.precompute(8);
|
||||
|
||||
const wrapBuf = (arrayBuffer) => new Uint8Array(arrayBuffer);
|
||||
|
||||
const ONLY_NOBLE = process.argv[2] === 'noble';
|
||||
// TODO: add more?
|
||||
|
||||
function generateData(namespace) {
|
||||
const priv = namespace.utils.randomPrivateKey();
|
||||
const pub = namespace.getPublicKey(priv);
|
||||
const msg = namespace.utils.randomPrivateKey();
|
||||
const sig = namespace.sign(msg, priv);
|
||||
return { priv, pub, msg, sig };
|
||||
}
|
||||
|
||||
export const CURVES = {
|
||||
secp256k1: {
|
||||
data: () => {
|
||||
const priv = 'f6fc7fd5acaf8603709160d203253d5cd17daa307483877ad811ec8411df56d2';
|
||||
const pub = noble_secp256k1.getPublicKey(priv, false);
|
||||
const msg = 'deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef';
|
||||
const sig = noble_secp256k1.signSync(msg, priv);
|
||||
return { priv, pub, msg, sig };
|
||||
return generateData(secp256k1);
|
||||
},
|
||||
getPublicKey1: {
|
||||
samples: 10000,
|
||||
noble: () => secp256k1.getPublicKey(3n),
|
||||
old: () => noble_secp256k1.getPublicKey(3n),
|
||||
secp256k1_old: () => old_secp.getPublicKey(3n),
|
||||
secp256k1: () => secp256k1.getPublicKey(3n),
|
||||
},
|
||||
getPublicKey255: {
|
||||
samples: 10000,
|
||||
noble: () => secp256k1.getPublicKey(2n**255n-1n),
|
||||
old: () => noble_secp256k1.getPublicKey(2n**255n-1n),
|
||||
secp256k1_old: () => old_secp.getPublicKey(2n**255n-1n),
|
||||
secp256k1: () => secp256k1.getPublicKey(2n**255n-1n),
|
||||
},
|
||||
sign: {
|
||||
samples: 5000,
|
||||
old: ({ msg, priv }) => noble_secp256k1.signSync(msg, priv),
|
||||
noble: ({ msg, priv }) => secp256k1.sign(msg, priv),
|
||||
secp256k1_old: ({ msg, priv }) => old_secp.signSync(msg, priv),
|
||||
secp256k1: ({ msg, priv }) => secp256k1.sign(msg, priv),
|
||||
},
|
||||
getSharedSecret: {
|
||||
samples: 1000,
|
||||
old: ({ pub, priv }) => noble_secp256k1.getSharedSecret(priv, pub),
|
||||
noble: ({ pub, priv }) => secp256k1.getSharedSecret(priv, pub),
|
||||
secp256k1_old: ({ pub, priv }) => old_secp.getSharedSecret(priv, pub),
|
||||
secp256k1: ({ pub, priv }) => secp256k1.getSharedSecret(priv, pub),
|
||||
},
|
||||
verify: {
|
||||
samples: 1000,
|
||||
secp256k1_old: ({ sig, msg, pub }) => {
|
||||
return old_secp.verify((new old_secp.Signature(sig.r, sig.s)), msg, pub);
|
||||
},
|
||||
secp256k1: ({ sig, msg, pub }) => secp256k1.verify(sig, msg, pub)
|
||||
}
|
||||
},
|
||||
ed25519: {
|
||||
data: () => {
|
||||
@ -83,20 +102,105 @@ export const CURVES = {
|
||||
samples: 10000,
|
||||
old: () => noble_ed25519.sync.getPublicKey(noble_ed25519.utils.randomPrivateKey()),
|
||||
noble: () => ed25519.getPublicKey(ed25519.utils.randomPrivateKey()),
|
||||
ed448: () => ed448.getPublicKey(ed448.utils.randomPrivateKey()),
|
||||
},
|
||||
sign: {
|
||||
samples: 5000,
|
||||
old: ({ msg, priv }) => noble_ed25519.sync.sign(msg, priv),
|
||||
noble: ({ msg, priv }) => ed25519.sign(msg, priv),
|
||||
ed448: () => ed448.sign(ed448.utils.randomPrivateKey(), ed448.utils.randomPrivateKey()),
|
||||
},
|
||||
verify: {
|
||||
samples: 1000,
|
||||
old: ({ msg, pub, sig }) => noble_ed25519.sync.verify(sig, msg, pub),
|
||||
noble: ({ msg, pub, sig }) => ed25519.verify(sig, msg, pub),
|
||||
old: ({ sig, msg, pub }) => noble_ed25519.sync.verify(sig, msg, pub),
|
||||
noble: ({ sig, msg, pub }) => ed25519.verify(sig, msg, pub),
|
||||
},
|
||||
},
|
||||
ed448: {
|
||||
data: () => {
|
||||
const priv = ed448.utils.randomPrivateKey();
|
||||
const pub = ed448.getPublicKey(priv);
|
||||
const msg = ed448.utils.randomPrivateKey();
|
||||
const sig = ed448.sign(msg, priv);
|
||||
return { priv, pub, msg, sig };
|
||||
},
|
||||
getPublicKey: {
|
||||
samples: 5000,
|
||||
noble: () => ed448.getPublicKey(ed448.utils.randomPrivateKey()),
|
||||
},
|
||||
sign: {
|
||||
samples: 2500,
|
||||
noble: ({ msg, priv }) => ed448.sign(msg, priv),
|
||||
},
|
||||
verify: {
|
||||
samples: 500,
|
||||
noble: ({ sig, msg, pub }) => ed448.verify(sig, msg, pub)
|
||||
}
|
||||
},
|
||||
nist: {
|
||||
data: () => {
|
||||
return { p256: generateData(P256), p384: generateData(P384), p521: generateData(P521) }
|
||||
},
|
||||
getPublicKey: {
|
||||
samples: 2500,
|
||||
P256: () => P256.getPublicKey(P256.utils.randomPrivateKey()),
|
||||
P384: () => P384.getPublicKey(P384.utils.randomPrivateKey()),
|
||||
P521: () => P521.getPublicKey(P521.utils.randomPrivateKey()),
|
||||
},
|
||||
sign: {
|
||||
samples: 1000,
|
||||
P256: ({ p256: {msg, priv} }) => P256.sign(msg, priv),
|
||||
P384: ({ p384: {msg, priv} }) => P384.sign(msg, priv),
|
||||
P521: ({ p521: {msg, priv} }) => P521.sign(msg, priv),
|
||||
},
|
||||
verify: {
|
||||
samples: 250,
|
||||
P256: ({ p256: {sig, msg, pub} }) => P256.verify(sig, msg, pub),
|
||||
P384: ({ p384: {sig, msg, pub} }) => P384.verify(sig, msg, pub),
|
||||
P521: ({ p521: {sig, msg, pub} }) => P521.verify(sig, msg, pub),
|
||||
}
|
||||
},
|
||||
stark: {
|
||||
data: () => {
|
||||
const priv = '2dccce1da22003777062ee0870e9881b460a8b7eca276870f57c601f182136c';
|
||||
const msg = 'c465dd6b1bbffdb05442eb17f5ca38ad1aa78a6f56bf4415bdee219114a47';
|
||||
const pub = stark.getPublicKey(priv);
|
||||
const sig = stark.sign(msg, priv);
|
||||
return { priv, sig, msg, pub }
|
||||
},
|
||||
pedersen: {
|
||||
samples: 500,
|
||||
old: () => {
|
||||
return starkwareCrypto.default.pedersen([
|
||||
'3d937c035c878245caf64531a5756109c53068da139362728feb561405371cb',
|
||||
'208a0a10250e382e1e4bbe2880906c2791bf6275695e02fbbc6aeff9cd8b31a',
|
||||
])
|
||||
},
|
||||
noble: () => {
|
||||
return stark.pedersen(
|
||||
'3d937c035c878245caf64531a5756109c53068da139362728feb561405371cb',
|
||||
'208a0a10250e382e1e4bbe2880906c2791bf6275695e02fbbc6aeff9cd8b31a'
|
||||
)
|
||||
}
|
||||
},
|
||||
verify: {
|
||||
samples: 500,
|
||||
old: () => {
|
||||
const privateKey = '2dccce1da22003777062ee0870e9881b460a8b7eca276870f57c601f182136c';
|
||||
const msgHash = 'c465dd6b1bbffdb05442eb17f5ca38ad1aa78a6f56bf4415bdee219114a47';
|
||||
const keyPair = starkwareCrypto.default.ec.keyFromPrivate(privateKey, 'hex');
|
||||
const publicKeyStark = starkwareCrypto.default.ec.keyFromPublic(
|
||||
keyPair.getPublic(true, 'hex'), 'hex'
|
||||
);
|
||||
return starkwareCrypto.default.verify(
|
||||
publicKeyStark,
|
||||
msgHash,
|
||||
starkwareCrypto.default.sign(keyPair, msgHash)
|
||||
);
|
||||
},
|
||||
noble: ({ sig, msg, pub }) => {
|
||||
return stark.verify(sig, msg, pub)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const main = () =>
|
||||
|
@ -16,6 +16,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@noble/ed25519": "^1.7.1",
|
||||
"@noble/secp256k1": "^1.7.0"
|
||||
"@noble/secp256k1": "^1.7.0",
|
||||
"@starkware-industries/starkware-crypto-utils": "^0.0.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,16 +3,19 @@ import { weierstrass } from '@noble/curves/weierstrass';
|
||||
import { sha256 } from '@noble/hashes/sha256';
|
||||
import { getHash } from './_shortw_utils.js';
|
||||
|
||||
// Was known as alt_bn128 when it had 128-bit security. Now that it's much lower, the naming
|
||||
// has been changed to its prime bit count.
|
||||
// https://neuromancer.sk/std/bn/bn254
|
||||
/**
|
||||
* bn254 pairing-friendly curve.
|
||||
* Previously known as alt_bn_128, when it had 128-bit security.
|
||||
* Recent research shown it's weaker, the naming has been adjusted to its prime bit count.
|
||||
* https://github.com/zcash/zcash/issues/2502
|
||||
*/
|
||||
export const bn254 = weierstrass({
|
||||
a: 0n,
|
||||
b: 3n,
|
||||
P: 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47n,
|
||||
n: 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001n,
|
||||
Gx: 1n,
|
||||
Gy: 2n,
|
||||
a: BigInt(0),
|
||||
b: BigInt(3),
|
||||
P: BigInt('0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47'),
|
||||
n: BigInt('0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001'),
|
||||
Gx: BigInt(1),
|
||||
Gy: BigInt(2),
|
||||
h: BigInt(1),
|
||||
...getHash(sha256),
|
||||
});
|
||||
|
@ -13,7 +13,14 @@ import {
|
||||
Hex,
|
||||
} from '@noble/curves/utils';
|
||||
|
||||
const ed25519P = BigInt(
|
||||
/**
|
||||
* ed25519 Twisted Edwards curve with following addons:
|
||||
* - X25519 ECDH
|
||||
* - Ristretto cofactor elimination
|
||||
* - Elligator hash-to-group / point indistinguishability
|
||||
*/
|
||||
|
||||
const ED25519_P = BigInt(
|
||||
'57896044618658097711785492504343953926634992332820282019728792003956564819949'
|
||||
);
|
||||
// √(-1) aka √(a) aka 2^((p-1)/4)
|
||||
@ -21,15 +28,12 @@ const ED25519_SQRT_M1 = BigInt(
|
||||
'19681161376707505956807079304988542015446066515923890162744021073123829784752'
|
||||
);
|
||||
|
||||
// prettier-ignore
|
||||
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _5n = BigInt(5);
|
||||
// prettier-ignore
|
||||
const _10n = BigInt(10), _20n = BigInt(20), _40n = BigInt(40), _80n = BigInt(80);
|
||||
function ed25519_pow_2_252_3(x: bigint) {
|
||||
const P = ed25519P;
|
||||
const _1n = BigInt(1);
|
||||
const _2n = BigInt(2);
|
||||
const _5n = BigInt(5);
|
||||
const _10n = BigInt(10);
|
||||
const _20n = BigInt(20);
|
||||
const _40n = BigInt(40);
|
||||
const _80n = BigInt(80);
|
||||
const P = ED25519_P;
|
||||
const x2 = (x * x) % P;
|
||||
const b2 = (x2 * x) % P; // x^3, 11
|
||||
const b4 = (pow2(b2, _2n, P) * b2) % P; // x^15, 1111
|
||||
@ -57,7 +61,7 @@ function adjustScalarBytes(bytes: Uint8Array): Uint8Array {
|
||||
}
|
||||
// sqrt(u/v)
|
||||
function uvRatio(u: bigint, v: bigint): { isValid: boolean; value: bigint } {
|
||||
const P = ed25519P;
|
||||
const P = ED25519_P;
|
||||
const v3 = mod(v * v * v, P); // v³
|
||||
const v7 = mod(v3 * v3 * v, P); // v⁷
|
||||
// (p+3)/8 and (p-5)/8
|
||||
@ -94,7 +98,7 @@ const ED25519_DEF = {
|
||||
// Negative number is P - number, and division is invert(number, P)
|
||||
d: BigInt('37095705934669439343138083508754565189542113879843219016388785533085940283555'),
|
||||
// Finite field 𝔽p over which we'll do calculations; 2n ** 255n - 19n
|
||||
P: ed25519P,
|
||||
P: ED25519_P,
|
||||
// Subgroup order: how many points ed25519 has
|
||||
// 2n ** 252n + 27742317777372353535851937790883648493n;
|
||||
n: BigInt('7237005577332262213973186563042994240857116359379907606001950938285454250989'),
|
||||
@ -130,13 +134,13 @@ export const ed25519ph = twistedEdwards({
|
||||
});
|
||||
|
||||
export const x25519 = montgomery({
|
||||
P: ed25519P,
|
||||
P: ED25519_P,
|
||||
a24: BigInt('121665'),
|
||||
montgomeryBits: 255, // n is 253 bits
|
||||
nByteLength: 32,
|
||||
Gu: '0900000000000000000000000000000000000000000000000000000000000000',
|
||||
powPminus2: (x: bigint): bigint => {
|
||||
const P = ed25519P;
|
||||
const P = ED25519_P;
|
||||
// x^(p-2) aka x^(2^255-21)
|
||||
const { pow_p_5_8, b2 } = ed25519_pow_2_252_3(x);
|
||||
return mod(pow2(pow_p_5_8, BigInt(3), P) * b2, P);
|
||||
@ -144,13 +148,6 @@ export const x25519 = montgomery({
|
||||
adjustScalarBytes,
|
||||
});
|
||||
|
||||
/**
|
||||
* Each ed25519/ExtendedPoint has 8 different equivalent points. This can be
|
||||
* a source of bugs for protocols like ring signatures. Ristretto was created to solve this.
|
||||
* Ristretto point operates in X:Y:Z:T extended coordinates like ExtendedPoint,
|
||||
* but it should work in its own namespace: do not combine those two.
|
||||
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-ristretto255-decaf448
|
||||
*/
|
||||
function assertRstPoint(other: unknown) {
|
||||
if (!(other instanceof RistrettoPoint)) throw new TypeError('RistrettoPoint expected');
|
||||
}
|
||||
@ -175,8 +172,6 @@ const D_MINUS_ONE_SQ = BigInt(
|
||||
'40440834346308536858101042469323190826248399146238708352240133220865137265952'
|
||||
);
|
||||
// Calculates 1/√(number)
|
||||
const _0n = BigInt(0);
|
||||
const _1n = BigInt(1);
|
||||
const invertSqrt = (number: bigint) => uvRatio(_1n, number);
|
||||
|
||||
const MAX_255B = BigInt('0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
|
||||
@ -184,6 +179,14 @@ const bytes255ToNumberLE = (bytes: Uint8Array) =>
|
||||
ed25519.utils.mod(bytesToNumberLE(bytes) & MAX_255B);
|
||||
|
||||
type ExtendedPoint = ExtendedPointType;
|
||||
|
||||
/**
|
||||
* Each ed25519/ExtendedPoint has 8 different equivalent points. This can be
|
||||
* a source of bugs for protocols like ring signatures. Ristretto was created to solve this.
|
||||
* Ristretto point operates in X:Y:Z:T extended coordinates like ExtendedPoint,
|
||||
* but it should work in its own namespace: do not combine those two.
|
||||
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-ristretto255-decaf448
|
||||
*/
|
||||
export class RistrettoPoint {
|
||||
static BASE = new RistrettoPoint(ed25519.ExtendedPoint.BASE);
|
||||
static ZERO = new RistrettoPoint(ed25519.ExtendedPoint.ZERO);
|
||||
|
@ -1,12 +1,15 @@
|
||||
/*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
import { shake256 } from '@noble/hashes/sha3';
|
||||
import { concatBytes, randomBytes, utf8ToBytes, wrapConstructor } from '@noble/hashes/utils';
|
||||
import { PointType, twistedEdwards } from '@noble/curves/edwards';
|
||||
import { mod, pow2, invert } from '@noble/curves/modular';
|
||||
import { numberToBytesLE } from '@noble/curves/utils';
|
||||
import { twistedEdwards } from '@noble/curves/edwards';
|
||||
import { mod, pow2 } from '@noble/curves/modular';
|
||||
import { montgomery } from '../../lib/montgomery.js';
|
||||
|
||||
const _0n = BigInt(0);
|
||||
/**
|
||||
* Edwards448 (not Ed448-Goldilocks) curve with following addons:
|
||||
* * X448 ECDH
|
||||
* Conforms to RFC 8032 https://www.rfc-editor.org/rfc/rfc8032.html#section-5.2
|
||||
*/
|
||||
|
||||
const shake256_114 = wrapConstructor(() => shake256.create({ dkLen: 114 }));
|
||||
const shake256_64 = wrapConstructor(() => shake256.create({ dkLen: 64 }));
|
||||
@ -17,21 +20,24 @@ const ed448P = BigInt(
|
||||
// powPminus3div4 calculates z = x^k mod p, where k = (p-3)/4.
|
||||
function ed448_pow_Pminus3div4(x: bigint): bigint {
|
||||
const P = ed448P;
|
||||
// prettier-ignore
|
||||
let [_1n, _2n, _3n, _11n, _22n, _44n, _88n, _223n] = [1, 2, 3, 11, 22, 44, 88, 223]
|
||||
.map(n => BigInt(n));
|
||||
// x ** ((P - 3n)/4n) % P
|
||||
// [223 of 1, 0, 222 of 1], almost same as secp!
|
||||
const b2 = (x * x * x) % P;
|
||||
const b3 = (b2 * b2 * x) % P;
|
||||
const b6 = (pow2(b3, 3n, P) * b3) % P;
|
||||
const b9 = (pow2(b6, 3n, P) * b3) % P;
|
||||
const b11 = (pow2(b9, 2n, P) * b2) % P;
|
||||
const b22 = (pow2(b11, 11n, P) * b11) % P;
|
||||
const b44 = (pow2(b22, 22n, P) * b22) % P;
|
||||
const b88 = (pow2(b44, 44n, P) * b44) % P;
|
||||
const b176 = (pow2(b88, 88n, P) * b88) % P;
|
||||
const b220 = (pow2(b176, 44n, P) * b44) % P;
|
||||
const b222 = (pow2(b220, 2n, P) * b2) % P;
|
||||
const b223 = (pow2(b222, 1n, P) * x) % P;
|
||||
return (pow2(b223, 223n, P) * b222) % P;
|
||||
const b6 = (pow2(b3, _3n, P) * b3) % P;
|
||||
const b9 = (pow2(b6, _3n, P) * b3) % P;
|
||||
const b11 = (pow2(b9, _2n, P) * b2) % P;
|
||||
const b22 = (pow2(b11, _11n, P) * b11) % P;
|
||||
const b44 = (pow2(b22, _22n, P) * b22) % P;
|
||||
const b88 = (pow2(b44, _44n, P) * b44) % P;
|
||||
const b176 = (pow2(b88, _88n, P) * b88) % P;
|
||||
const b220 = (pow2(b176, _44n, P) * b44) % P;
|
||||
const b222 = (pow2(b220, _2n, P) * b2) % P;
|
||||
const b223 = (pow2(b222, _1n, P) * x) % P;
|
||||
return (pow2(b223, _223n, P) * b222) % P;
|
||||
}
|
||||
|
||||
function adjustScalarBytes(bytes: Uint8Array): Uint8Array {
|
||||
@ -44,13 +50,11 @@ function adjustScalarBytes(bytes: Uint8Array): Uint8Array {
|
||||
bytes[56] = 0; // Byte outside of group (456 buts vs 448 bits)
|
||||
return bytes;
|
||||
}
|
||||
// Edwards448 from RFC 8032 (https://www.rfc-editor.org/rfc/rfc8032.html#section-5.2).
|
||||
// NOTE: Ed448-Goldilocks is different curve
|
||||
|
||||
const ED448_DEF = {
|
||||
// Param: a
|
||||
a: BigInt(1),
|
||||
// Equal to -39081 over finite field.
|
||||
// Negative number is P - number
|
||||
// -39081. Negative number is P - number
|
||||
d: BigInt(
|
||||
'726838724295606890549323807888004534353641360687318060281490199180612328166730772686396383698676545930088884461843637361053498018326358'
|
||||
),
|
||||
@ -84,8 +88,8 @@ const ED448_DEF = {
|
||||
data
|
||||
);
|
||||
},
|
||||
// Ratio of u to v. Allows us to combine inversion and square root. Uses algo from RFC8032 5.1.3.
|
||||
// Constant-time, u/√v
|
||||
// Constant-time ratio of u to v. Allows to combine inversion and square root u/√v.
|
||||
// Uses algo from RFC8032 5.1.3.
|
||||
uvRatio: (u: bigint, v: bigint): { isValid: boolean; value: bigint } => {
|
||||
const P = ed448P;
|
||||
// https://datatracker.ietf.org/doc/html/rfc8032#section-5.2.3
|
||||
@ -113,7 +117,7 @@ export const ed448 = twistedEdwards(ED448_DEF);
|
||||
export const ed448ph = twistedEdwards({ ...ED448_DEF, preHash: shake256_64 });
|
||||
|
||||
export const x448 = montgomery({
|
||||
a24: BigInt('39081'),
|
||||
a24: BigInt(39081),
|
||||
montgomeryBits: 448,
|
||||
nByteLength: 57,
|
||||
P: ed448P,
|
||||
|
@ -4,7 +4,11 @@ import { concatBytes, randomBytes, utf8ToBytes } from '@noble/hashes/utils';
|
||||
import { twistedEdwards } from '@noble/curves/edwards';
|
||||
import { blake2s } from '@noble/hashes/blake2s';
|
||||
|
||||
// https://neuromancer.sk/std/other/JubJub
|
||||
/**
|
||||
* jubjub Twisted Edwards curve.
|
||||
* https://neuromancer.sk/std/other/JubJub
|
||||
*/
|
||||
|
||||
export const jubjub = twistedEdwards({
|
||||
// Params: a, d
|
||||
a: BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000'),
|
||||
|
@ -2,16 +2,16 @@
|
||||
import { createCurve } from './_shortw_utils.js';
|
||||
import { sha256 } from '@noble/hashes/sha256';
|
||||
|
||||
// https://www.secg.org/sec2-v2.pdf
|
||||
// https://neuromancer.sk/std/secg/secp192r1
|
||||
// NIST secp192r1 aka P192
|
||||
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/secg/secp192r1
|
||||
export const P192 = createCurve(
|
||||
{
|
||||
// Params: a, b
|
||||
a: BigInt('0xfffffffffffffffffffffffffffffffefffffffffffffffc'),
|
||||
b: BigInt('0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1'),
|
||||
// Field over which we'll do calculations. Verify with: 2n ** 192n - 2n ** 64n - 1n
|
||||
// Field over which we'll do calculations; 2n ** 192n - 2n ** 64n - 1n
|
||||
P: BigInt('0xfffffffffffffffffffffffffffffffeffffffffffffffff'),
|
||||
// Curve order, total count of valid points in the field. Verify with:
|
||||
// Curve order, total count of valid points in the field.
|
||||
n: BigInt('0xffffffffffffffffffffffff99def836146bc9b1b4d22831'),
|
||||
// Base point (x, y) aka generator point
|
||||
Gx: BigInt('0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012'),
|
||||
|
@ -2,16 +2,16 @@
|
||||
import { createCurve } from './_shortw_utils.js';
|
||||
import { sha256 } from '@noble/hashes/sha256';
|
||||
|
||||
// https://www.secg.org/sec2-v2.pdf
|
||||
// https://neuromancer.sk/std/nist/P-224
|
||||
// NIST secp224r1 aka P224
|
||||
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/nist/P-224
|
||||
export const P224 = createCurve(
|
||||
{
|
||||
// Params: a, b
|
||||
a: BigInt('0xfffffffffffffffffffffffffffffffefffffffffffffffffffffffe'),
|
||||
b: BigInt('0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4'),
|
||||
// Field over which we'll do calculations. Verify with:
|
||||
P: 2n ** 224n - 2n ** 96n + 1n,
|
||||
// Curve order, total count of valid points in the field. Verify with:
|
||||
// Field over which we'll do calculations; 2n**224n - 2n**96n + 1n
|
||||
P: BigInt('0xffffffffffffffffffffffffffffffff000000000000000000000001'),
|
||||
// Curve order, total count of valid points in the field
|
||||
n: BigInt('0xffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d'),
|
||||
// Base point (x, y) aka generator point
|
||||
Gx: BigInt('0xb70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21'),
|
||||
|
@ -2,17 +2,16 @@
|
||||
import { createCurve } from './_shortw_utils.js';
|
||||
import { sha256 } from '@noble/hashes/sha256';
|
||||
|
||||
// https://www.secg.org/sec2-v2.pdf
|
||||
// https://neuromancer.sk/std/nist/P-256
|
||||
// NIST secp256r1 aka P256
|
||||
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/nist/P-256
|
||||
export const P256 = createCurve(
|
||||
{
|
||||
// Params: a, b
|
||||
a: BigInt('0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc'),
|
||||
b: BigInt('0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b'),
|
||||
// Field over which we'll do calculations. Verify with:
|
||||
// 2n ** 224n * (2n ** 32n - 1n) + 2n ** 192n + 2n ** 96n - 1n,
|
||||
// Field over which we'll do calculations; 2n**224n * (2n**32n-1n) + 2n**192n + 2n**96n-1n
|
||||
P: BigInt('0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff'),
|
||||
// Curve order, total count of valid points in the field. Verify with:
|
||||
// Curve order, total count of valid points in the field
|
||||
n: BigInt('0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551'),
|
||||
// Base point (x, y) aka generator point
|
||||
Gx: BigInt('0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296'),
|
||||
|
@ -2,17 +2,16 @@
|
||||
import { createCurve } from './_shortw_utils.js';
|
||||
import { sha384 } from '@noble/hashes/sha512';
|
||||
|
||||
// https://www.secg.org/sec2-v2.pdf
|
||||
// https://neuromancer.sk/std/nist/P-384
|
||||
// NIST secp384r1 aka P384
|
||||
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/nist/P-384
|
||||
// prettier-ignore
|
||||
export const P384 = createCurve({
|
||||
// Params: a, b
|
||||
a: BigInt('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000fffffffc'),
|
||||
b: BigInt('0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef'),
|
||||
// Field over which we'll do calculations. Verify with:
|
||||
// 2n ** 384n - 2n ** 128n - 2n ** 96n + 2n ** 32n - 1n
|
||||
// Field over which we'll do calculations. 2n**384n - 2n**128n - 2n**96n + 2n**32n - 1n
|
||||
P: BigInt('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff'),
|
||||
// Curve order, total count of valid points in the field. Verify with:
|
||||
// Curve order, total count of valid points in the field.
|
||||
n: BigInt('0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973'),
|
||||
// Base point (x, y) aka generator point
|
||||
Gx: BigInt('0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7'),
|
||||
|
@ -2,17 +2,17 @@
|
||||
import { createCurve } from './_shortw_utils.js';
|
||||
import { sha512 } from '@noble/hashes/sha512';
|
||||
|
||||
// https://www.secg.org/sec2-v2.pdf
|
||||
// https://neuromancer.sk/std/nist/P-521
|
||||
// NIST secp521r1 aka P521
|
||||
// Note that it's 521, which differs from 512 of its hash function.
|
||||
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/nist/P-521
|
||||
// prettier-ignore
|
||||
export const P521 = createCurve({
|
||||
// Params: a, b
|
||||
a: BigInt('0x01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc'),
|
||||
b: BigInt('0x0051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00'),
|
||||
// Field over which we'll do calculations. Verify with:
|
||||
// 2n ** 521n - 1n,
|
||||
// Field over which we'll do calculations; 2n**521n - 1n
|
||||
P: BigInt('0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'),
|
||||
// Curve order, total count of valid points in the field. Verify with:
|
||||
// Curve order, total count of valid points in the field
|
||||
n: BigInt('0x01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409'),
|
||||
// Base point (x, y) aka generator point
|
||||
Gx: BigInt('0x00c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66'),
|
||||
|
@ -14,31 +14,35 @@ import {
|
||||
import { randomBytes } from '@noble/hashes/utils';
|
||||
|
||||
/**
|
||||
* secp256k1 definition with efficient square root and endomorphism.
|
||||
* Endomorphism works only for Koblitz curves with a == 0.
|
||||
* It improves efficiency:
|
||||
* secp256k1 belongs to Koblitz curves: it has
|
||||
* efficiently computable Frobenius endomorphism.
|
||||
* Endomorphism improves efficiency:
|
||||
* Uses 2x less RAM, speeds up precomputation by 2x and ECDH / sign key recovery by 20%.
|
||||
* Should always be used for Jacobian's double-and-add multiplication.
|
||||
* For affines cached multiplication, it trades off 1/2 init time & 1/3 ram for 20% perf hit.
|
||||
* https://gist.github.com/paulmillr/eb670806793e84df628a7c434a873066
|
||||
*/
|
||||
|
||||
const secp256k1P = BigInt('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f');
|
||||
const secp256k1N = BigInt('0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141');
|
||||
const _1n = BigInt(1);
|
||||
const _2n = BigInt(2);
|
||||
const divNearest = (a: bigint, b: bigint) => (a + b / _2n) / b;
|
||||
|
||||
function sqrtMod(x: bigint): bigint {
|
||||
/**
|
||||
* Allows to compute square root √y 2x faster.
|
||||
* To calculate √y, we need to exponentiate it to a very big number:
|
||||
* `y² = x³ + ax + b; y = y² ^ (p+1)/4`
|
||||
* We are unwrapping the loop and multiplying it bit-by-bit.
|
||||
* (P+1n/4n).toString(2) would produce bits [223x 1, 0, 22x 1, 4x 0, 11, 00]
|
||||
*/
|
||||
// prettier-ignore
|
||||
function sqrtMod(y: bigint): bigint {
|
||||
const P = secp256k1P;
|
||||
const _3n = BigInt(3);
|
||||
const _6n = BigInt(6);
|
||||
const _11n = BigInt(11);
|
||||
const _22n = BigInt(22);
|
||||
const _23n = BigInt(23);
|
||||
const _44n = BigInt(44);
|
||||
const _88n = BigInt(88);
|
||||
const b2 = (x * x * x) % P; // x^3, 11
|
||||
const b3 = (b2 * b2 * x) % P; // x^7
|
||||
const _3n = BigInt(3), _6n = BigInt(6), _11n = BigInt(11); const _22n = BigInt(22);
|
||||
const _23n = BigInt(23), _44n = BigInt(44), _88n = BigInt(88);
|
||||
const b2 = (y * y * y) % P; // x^3, 11
|
||||
const b3 = (b2 * b2 * y) % P; // x^7
|
||||
const b6 = (pow2(b3, _3n, P) * b3) % P;
|
||||
const b9 = (pow2(b6, _3n, P) * b3) % P;
|
||||
const b11 = (pow2(b9, _2n, P) * b2) % P;
|
||||
@ -55,26 +59,22 @@ function sqrtMod(x: bigint): bigint {
|
||||
|
||||
export const secp256k1 = createCurve(
|
||||
{
|
||||
a: 0n,
|
||||
b: 7n,
|
||||
// Field over which we'll do calculations. Verify with:
|
||||
// 2n ** 256n - 2n ** 32n - 2n ** 9n - 2n ** 8n - 2n ** 7n - 2n ** 6n - 2n ** 4n - 1n
|
||||
a: BigInt(0),
|
||||
b: BigInt(7),
|
||||
// Field over which we'll do calculations;
|
||||
// 2n**256n - 2n**32n - 2n**9n - 2n**8n - 2n**7n - 2n**6n - 2n**4n - 1n
|
||||
P: secp256k1P,
|
||||
// Curve order, total count of valid points in the field. Verify with:
|
||||
// Curve order, total count of valid points in the field
|
||||
n: secp256k1N,
|
||||
// Base point (x, y) aka generator point
|
||||
Gx: BigInt('55066263022277343669578718895168534326250603453777594175500187360389116729240'),
|
||||
Gy: BigInt('32670510020758816978083085130507043184471273380659243275938904335757337482424'),
|
||||
h: BigInt(1),
|
||||
// noble-secp256k1 compat
|
||||
// Alllow only low-S signatures by default in sign() and verify()
|
||||
lowS: true,
|
||||
// Used to calculate y - the square root of y².
|
||||
// Exponentiates it to very big number (P+1)/4.
|
||||
// We are unwrapping the loop because it's 2x faster.
|
||||
// (P+1n/4n).toString(2) would produce bits [223x 1, 0, 22x 1, 4x 0, 11, 00]
|
||||
// We are multiplying it bit-by-bit
|
||||
sqrtMod,
|
||||
endo: {
|
||||
// Params taken from https://gist.github.com/paulmillr/eb670806793e84df628a7c434a873066
|
||||
beta: BigInt('0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee'),
|
||||
splitScalar: (k: bigint) => {
|
||||
const n = secp256k1N;
|
||||
|
@ -6,6 +6,9 @@ import { concatBytes, randomBytes } from '@noble/hashes/utils';
|
||||
import { weierstrass, CHash, JacobianPointType } from '@noble/curves/weierstrass';
|
||||
import * as cutils from '@noble/curves/utils';
|
||||
|
||||
// Stark-friendly elliptic curve
|
||||
// https://docs.starkware.co/starkex/stark-curve.html
|
||||
|
||||
function getHash(hash: CHash) {
|
||||
return {
|
||||
hash,
|
||||
@ -14,22 +17,23 @@ function getHash(hash: CHash) {
|
||||
};
|
||||
}
|
||||
|
||||
const CURVE_N = 3618502788666131213697322783095070105526743751716087489154079457884512865583n;
|
||||
const CURVE_N = BigInt(
|
||||
'3618502788666131213697322783095070105526743751716087489154079457884512865583'
|
||||
);
|
||||
const nBitLength = 252;
|
||||
// https://docs.starkware.co/starkex/stark-curve.html
|
||||
export const starkCurve = weierstrass({
|
||||
// Params: a, b
|
||||
a: 1n,
|
||||
b: 3141592653589793238462643383279502884197169399375105820974944592307816406665n,
|
||||
// Field over which we'll do calculations. Verify with:
|
||||
// NOTE: there is no efficient sqrt for field (P%4==1)
|
||||
P: 2n ** 251n + 17n * 2n ** 192n + 1n,
|
||||
// Curve order, total count of valid points in the field. Verify with:
|
||||
a: BigInt(1),
|
||||
b: BigInt('3141592653589793238462643383279502884197169399375105820974944592307816406665'),
|
||||
// Field over which we'll do calculations; 2n**251n + 17n * 2n**192n + 1n
|
||||
// There is no efficient sqrt for field (P%4==1)
|
||||
P: BigInt('0x800000000000011000000000000000000000000000000000000000000000001'),
|
||||
// Curve order, total count of valid points in the field.
|
||||
n: CURVE_N,
|
||||
nBitLength: nBitLength, // len(bin(N).replace('0b',''))
|
||||
// Base point (x, y) aka generator point
|
||||
Gx: 874739451078007766457464989774322083649278607533249481151382481072868806602n,
|
||||
Gy: 152666792071518830868575557812948353041420400780739481342941381225525861407n,
|
||||
Gx: BigInt('874739451078007766457464989774322083649278607533249481151382481072868806602'),
|
||||
Gy: BigInt('152666792071518830868575557812948353041420400780739481342941381225525861407'),
|
||||
h: BigInt(1),
|
||||
// Default options
|
||||
lowS: false,
|
||||
|
Loading…
Reference in New Issue
Block a user