Refactor, benchmarks

This commit is contained in:
Paul Miller 2022-12-14 17:40:59 +00:00
parent 9e5ad8dc85
commit bbe46843fb
No known key found for this signature in database
GPG Key ID: 697079DA6878B89B
13 changed files with 258 additions and 137 deletions

@ -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,