Compare commits
73 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e2a7594eae | ||
|
|
823149ecd9 | ||
|
|
e57aec63d8 | ||
|
|
837aca98c9 | ||
|
|
dbb16b0e5e | ||
|
|
e14af67254 | ||
|
|
4780850748 | ||
|
|
3374a70f47 | ||
|
|
131f88b504 | ||
|
|
4333e9a686 | ||
|
|
a60d15ff05 | ||
|
|
ceffbc69da | ||
|
|
c75129e629 | ||
|
|
f39fb80c52 | ||
|
|
fcd422d246 | ||
|
|
ed9bf89038 | ||
|
|
7262b4219f | ||
|
|
02b0b25147 | ||
|
|
79100c2d47 | ||
|
|
4ef2cad685 | ||
|
|
69b3ab5a57 | ||
|
|
9465e60d30 | ||
|
|
0fb78b7097 | ||
|
|
be0b2a32a5 | ||
|
|
3d77422731 | ||
|
|
c46914f1bc | ||
|
|
f250f355e8 | ||
|
|
c095d74673 | ||
|
|
ac52fea952 | ||
|
|
f2ee24bee4 | ||
|
|
cffea91061 | ||
|
|
5fc38fc0e7 | ||
|
|
849dc38f3c | ||
|
|
0422e6ef38 | ||
|
|
21d2438a33 | ||
|
|
cea4696599 | ||
|
|
f14b8d2be5 | ||
|
|
2ed27da8eb | ||
|
|
17e5be5f1b | ||
|
|
a49f0d266e | ||
|
|
bfbcf733e6 | ||
|
|
7fda6de619 | ||
|
|
2b908ad602 | ||
|
|
ceb3f67faa | ||
|
|
a2c87f9c2f | ||
|
|
e1fd346279 | ||
|
|
11e78aadbf | ||
|
|
055147f1be | ||
|
|
6f99f6042e | ||
|
|
1e47bf2372 | ||
|
|
40530eae0c | ||
|
|
b9482bb17d | ||
|
|
74475dca68 | ||
|
|
f4cf21b9c8 | ||
|
|
5312d92b2c | ||
|
|
d1770c0ac7 | ||
|
|
2d37edf7d1 | ||
|
|
36998fede8 | ||
|
|
83960d445d | ||
|
|
23cc2aa5d1 | ||
|
|
e45d7c2d25 | ||
|
|
bfe929aac3 | ||
|
|
069452dbe7 | ||
|
|
2e81f31d2e | ||
|
|
9f7df0f13b | ||
|
|
5600629bca | ||
|
|
2bd5e9ac16 | ||
|
|
6890c26091 | ||
|
|
a15e3a93a9 | ||
|
|
910c508da9 | ||
|
|
12da04a2bb | ||
|
|
cc2c84f040 | ||
|
|
5d42549acc |
238
README.md
238
README.md
@@ -6,11 +6,12 @@ Minimal, auditable JS implementation of elliptic curve cryptography.
|
||||
- ECDSA, EdDSA, Schnorr, BLS signature schemes, ECDH key agreement
|
||||
- [hash to curve](https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/)
|
||||
for encoding or hashing an arbitrary string to a point on an elliptic curve
|
||||
- Auditable, [fast](#speed)
|
||||
- 🔻 Tree-shaking-friendly: there is no entry point, which ensures small size of your app
|
||||
- [Poseidon](https://www.poseidon-hash.info) ZK-friendly hash
|
||||
- 🏎 [Ultra-fast](#speed), hand-optimized for caveats of JS engines
|
||||
- 🔍 Unique tests ensure correctness. Wycheproof vectors included
|
||||
- 🔻 Tree-shaking-friendly: there is no entry point, which ensures small size of your app
|
||||
|
||||
There are two parts of the package:
|
||||
Package consists of two parts:
|
||||
|
||||
1. `abstract/` directory specifies zero-dependency EC algorithms
|
||||
2. root directory utilizes one dependency `@noble/hashes` and provides ready-to-use:
|
||||
@@ -24,7 +25,7 @@ Curves incorporate work from previous noble packages
|
||||
[ed25519](https://github.com/paulmillr/noble-ed25519),
|
||||
[bls12-381](https://github.com/paulmillr/noble-bls12-381)),
|
||||
which had security audits and were developed from 2019 to 2022.
|
||||
The goal is to replace them with lean UMD builds based on single-codebase noble-curves.
|
||||
Check out [Upgrading](#upgrading) section if you've used them before.
|
||||
|
||||
### This library belongs to _noble_ crypto
|
||||
|
||||
@@ -84,11 +85,13 @@ To define a custom curve, check out API below.
|
||||
## API
|
||||
|
||||
- [Overview](#overview)
|
||||
- [abstract/edwards: Twisted Edwards curve](#abstract/edwards-twisted-edwards-curve)
|
||||
- [abstract/montgomery: Montgomery curve](#abstract/montgomery-montgomery-curve)
|
||||
- [abstract/weierstrass: Short Weierstrass curve](#abstract/weierstrass-short-weierstrass-curve)
|
||||
- [abstract/modular](#abstract/modular)
|
||||
- [abstract/utils](#abstract/utils)
|
||||
- [abstract/edwards: Twisted Edwards curve](#abstractedwards-twisted-edwards-curve)
|
||||
- [abstract/montgomery: Montgomery curve](#abstractmontgomery-montgomery-curve)
|
||||
- [abstract/weierstrass: Short Weierstrass curve](#abstractweierstrass-short-weierstrass-curve)
|
||||
- [abstract/hash-to-curve: Hashing strings to curve points](#abstracthash-to-curve-hashing-strings-to-curve-points)
|
||||
- [abstract/poseidon: Poseidon hash](#abstractposeidon-poseidon-hash)
|
||||
- [abstract/modular](#abstractmodular)
|
||||
- [abstract/utils](#abstractutils)
|
||||
|
||||
### Overview
|
||||
|
||||
@@ -200,8 +203,6 @@ export type CurveFn = {
|
||||
ExtendedPoint: ExtendedPointConstructor;
|
||||
Signature: SignatureConstructor;
|
||||
utils: {
|
||||
mod: (a: bigint, b?: bigint) => bigint;
|
||||
invert: (number: bigint, modulo?: bigint) => bigint;
|
||||
randomPrivateKey: () => Uint8Array;
|
||||
getExtendedPublicKey: (key: PrivKey) => {
|
||||
head: Uint8Array;
|
||||
@@ -303,20 +304,18 @@ const shared = secp256k1.getSharedSecret(key, someonesPubkey);
|
||||
export type CurveFn = {
|
||||
CURVE: ReturnType<typeof validateOpts>;
|
||||
getPublicKey: (privateKey: PrivKey, isCompressed?: boolean) => Uint8Array;
|
||||
getSharedSecret: (privateA: PrivKey, publicB: PubKey, isCompressed?: boolean) => Uint8Array;
|
||||
getSharedSecret: (privateA: PrivKey, publicB: Hex, isCompressed?: boolean) => Uint8Array;
|
||||
sign: (msgHash: Hex, privKey: PrivKey, opts?: SignOpts) => SignatureType;
|
||||
verify: (
|
||||
signature: Hex | SignatureType,
|
||||
msgHash: Hex,
|
||||
publicKey: PubKey,
|
||||
publicKey: Hex,
|
||||
opts?: { lowS?: boolean }
|
||||
) => boolean;
|
||||
Point: PointConstructor;
|
||||
ProjectivePoint: ProjectivePointConstructor;
|
||||
Signature: SignatureConstructor;
|
||||
utils: {
|
||||
mod: (a: bigint) => bigint;
|
||||
invert: (number: bigint) => bigint;
|
||||
isValidPrivateKey(privateKey: PrivKey): boolean;
|
||||
hashToPrivateKey: (hash: Hex) => Uint8Array;
|
||||
randomPrivateKey: () => Uint8Array;
|
||||
@@ -324,20 +323,101 @@ export type CurveFn = {
|
||||
};
|
||||
```
|
||||
|
||||
### abstract/hash-to-curve: Hashing strings to curve points
|
||||
|
||||
The module allows to hash arbitrary strings to elliptic curve points.
|
||||
|
||||
- `expand_message_xmd` [(spec)](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.4.1) produces a uniformly random byte string using a cryptographic hash function H that outputs b bits..
|
||||
|
||||
```ts
|
||||
function expand_message_xmd(
|
||||
msg: Uint8Array,
|
||||
DST: Uint8Array,
|
||||
lenInBytes: number,
|
||||
H: CHash
|
||||
): Uint8Array;
|
||||
function expand_message_xof(
|
||||
msg: Uint8Array,
|
||||
DST: Uint8Array,
|
||||
lenInBytes: number,
|
||||
k: number,
|
||||
H: CHash
|
||||
): Uint8Array;
|
||||
```
|
||||
|
||||
- `hash_to_field(msg, count, options)` [(spec)](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3)
|
||||
hashes arbitrary-length byte strings to a list of one or more elements of a finite field F.
|
||||
_ `msg` a byte string containing the message to hash
|
||||
_ `count` the number of elements of F to output
|
||||
_ `options` `{DST: string, p: bigint, m: number, k: number, expand: 'xmd' | 'xof', hash: H}`
|
||||
_ Returns `[u_0, ..., u_(count - 1)]`, a list of field elements.
|
||||
|
||||
```ts
|
||||
function hash_to_field(msg: Uint8Array, count: number, options: htfOpts): bigint[][];
|
||||
type htfOpts = {
|
||||
// DST: a domain separation tag
|
||||
// defined in section 2.2.5
|
||||
DST: string;
|
||||
// p: the characteristic of F
|
||||
// where F is a finite field of characteristic p and order q = p^m
|
||||
p: bigint;
|
||||
// m: the extension degree of F, m >= 1
|
||||
// where F is a finite field of characteristic p and order q = p^m
|
||||
m: number;
|
||||
// k: the target security level for the suite in bits
|
||||
// defined in section 5.1
|
||||
k: number;
|
||||
// option to use a message that has already been processed by
|
||||
// expand_message_xmd
|
||||
expand?: 'xmd' | 'xof';
|
||||
// Hash functions for: expand_message_xmd is appropriate for use with a
|
||||
// wide range of hash functions, including SHA-2, SHA-3, BLAKE2, and others.
|
||||
// BBS+ uses blake2: https://github.com/hyperledger/aries-framework-go/issues/2247
|
||||
// TODO: verify that hash is shake if expand==='xof' via types
|
||||
hash: CHash;
|
||||
};
|
||||
```
|
||||
|
||||
### abstract/poseidon: Poseidon hash
|
||||
|
||||
Implements [Poseidon](https://www.poseidon-hash.info) ZK-friendly hash.
|
||||
|
||||
There are many poseidon instances with different constants. We don't provide them,
|
||||
but we provide ability to specify them manually. For actual usage, check out
|
||||
stark curve source code.
|
||||
|
||||
```ts
|
||||
import { poseidon } from '@noble/curves/abstract/poseidon';
|
||||
|
||||
type PoseidonOpts = {
|
||||
Fp: Field<bigint>;
|
||||
t: number;
|
||||
roundsFull: number;
|
||||
roundsPartial: number;
|
||||
sboxPower?: number;
|
||||
reversePartialPowIdx?: boolean; // Hack for stark
|
||||
mds: bigint[][];
|
||||
roundConstants: bigint[][];
|
||||
};
|
||||
const instance = poseidon(opts: PoseidonOpts);
|
||||
```
|
||||
|
||||
### abstract/modular
|
||||
|
||||
Modular arithmetics utilities.
|
||||
|
||||
```typescript
|
||||
import { mod, invert, div, invertBatch, sqrt, Fp } from '@noble/curves/abstract/modular';
|
||||
import { Fp, mod, invert, div, invertBatch, sqrt } from '@noble/curves/abstract/modular';
|
||||
const fp = Fp(2n ** 255n - 19n); // Finite field over 2^255-19
|
||||
fp.mul(591n, 932n);
|
||||
fp.pow(481n, 11024858120n);
|
||||
|
||||
// Generic non-FP utils are also available
|
||||
mod(21n, 10n); // 21 mod 10 == 1n; fixed version of 21 % 10
|
||||
invert(17n, 10n); // invert(17) mod 10; modular multiplicative inverse
|
||||
div(5n, 17n, 10n); // 5/17 mod 10 == 5 * invert(17) mod 10; division
|
||||
invertBatch([1n, 2n, 4n], 21n); // => [1n, 11n, 16n] in one inversion
|
||||
sqrt(21n, 73n); // √21 mod 73; square root
|
||||
const fp = Fp(2n ** 255n - 19n); // Finite field over 2^255-19
|
||||
fp.mul(591n, 932n);
|
||||
fp.pow(481n, 11024858120n);
|
||||
```
|
||||
|
||||
### abstract/utils
|
||||
@@ -372,45 +452,95 @@ We consider infrastructure attacks like rogue NPM modules very important; that's
|
||||
Benchmark results on Apple M2 with node v18.10:
|
||||
|
||||
```
|
||||
getPublicKey
|
||||
secp256k1 x 5,241 ops/sec @ 190μs/op
|
||||
P256 x 7,993 ops/sec @ 125μs/op
|
||||
P384 x 3,819 ops/sec @ 261μs/op
|
||||
P521 x 2,074 ops/sec @ 481μs/op
|
||||
ed25519 x 8,390 ops/sec @ 119μs/op
|
||||
ed448 x 3,224 ops/sec @ 310μs/op
|
||||
sign
|
||||
secp256k1 x 3,934 ops/sec @ 254μs/op
|
||||
P256 x 5,327 ops/sec @ 187μs/op
|
||||
P384 x 2,728 ops/sec @ 366μs/op
|
||||
P521 x 1,594 ops/sec @ 626μs/op
|
||||
ed25519 x 4,233 ops/sec @ 236μs/op
|
||||
ed448 x 1,561 ops/sec @ 640μs/op
|
||||
verify
|
||||
secp256k1 x 731 ops/sec @ 1ms/op
|
||||
P256 x 806 ops/sec @ 1ms/op
|
||||
P384 x 353 ops/sec @ 2ms/op
|
||||
P521 x 171 ops/sec @ 5ms/op
|
||||
ed25519 x 860 ops/sec @ 1ms/op
|
||||
ed448 x 313 ops/sec @ 3ms/op
|
||||
getSharedSecret
|
||||
secp256k1 x 445 ops/sec @ 2ms/op
|
||||
recoverPublicKey
|
||||
secp256k1 x 732 ops/sec @ 1ms/op
|
||||
==== bls12-381 ====
|
||||
getPublicKey x 817 ops/sec @ 1ms/op
|
||||
sign x 50 ops/sec @ 19ms/op
|
||||
verify x 34 ops/sec @ 28ms/op
|
||||
pairing x 89 ops/sec @ 11ms/op
|
||||
==== stark ====
|
||||
secp256k1
|
||||
init x 57 ops/sec @ 17ms/op
|
||||
getPublicKey x 4,946 ops/sec @ 202μs/op
|
||||
sign x 3,914 ops/sec @ 255μs/op
|
||||
verify x 682 ops/sec @ 1ms/op
|
||||
getSharedSecret x 427 ops/sec @ 2ms/op
|
||||
recoverPublicKey x 683 ops/sec @ 1ms/op
|
||||
schnorr.sign x 539 ops/sec @ 1ms/op
|
||||
schnorr.verify x 716 ops/sec @ 1ms/op
|
||||
|
||||
P256
|
||||
init x 30 ops/sec @ 32ms/op
|
||||
getPublicKey x 5,008 ops/sec @ 199μs/op
|
||||
sign x 3,970 ops/sec @ 251μs/op
|
||||
verify x 515 ops/sec @ 1ms/op
|
||||
|
||||
P384
|
||||
init x 14 ops/sec @ 66ms/op
|
||||
getPublicKey x 2,434 ops/sec @ 410μs/op
|
||||
sign x 1,942 ops/sec @ 514μs/op
|
||||
verify x 206 ops/sec @ 4ms/op
|
||||
|
||||
P521
|
||||
init x 7 ops/sec @ 126ms/op
|
||||
getPublicKey x 1,282 ops/sec @ 779μs/op
|
||||
sign x 1,077 ops/sec @ 928μs/op
|
||||
verify x 110 ops/sec @ 9ms/op
|
||||
|
||||
ed25519
|
||||
init x 37 ops/sec @ 26ms/op
|
||||
getPublicKey x 8,147 ops/sec @ 122μs/op
|
||||
sign x 3,979 ops/sec @ 251μs/op
|
||||
verify x 848 ops/sec @ 1ms/op
|
||||
|
||||
ed448
|
||||
init x 17 ops/sec @ 58ms/op
|
||||
getPublicKey x 3,083 ops/sec @ 324μs/op
|
||||
sign x 1,473 ops/sec @ 678μs/op
|
||||
verify x 323 ops/sec @ 3ms/op
|
||||
|
||||
bls12-381
|
||||
init x 30 ops/sec @ 33ms/op
|
||||
getPublicKey x 788 ops/sec @ 1ms/op
|
||||
sign x 45 ops/sec @ 21ms/op
|
||||
verify x 32 ops/sec @ 30ms/op
|
||||
pairing x 88 ops/sec @ 11ms/op
|
||||
|
||||
stark
|
||||
init x 31 ops/sec @ 31ms/op
|
||||
pedersen
|
||||
old x 85 ops/sec @ 11ms/op
|
||||
noble x 1,216 ops/sec @ 822μs/op
|
||||
├─old x 84 ops/sec @ 11ms/op
|
||||
└─noble x 802 ops/sec @ 1ms/op
|
||||
poseidon x 7,466 ops/sec @ 133μs/op
|
||||
verify
|
||||
old x 302 ops/sec @ 3ms/op
|
||||
noble x 698 ops/sec @ 1ms/op
|
||||
├─old x 300 ops/sec @ 3ms/op
|
||||
└─noble x 474 ops/sec @ 2ms/op
|
||||
```
|
||||
|
||||
## Upgrading
|
||||
|
||||
If you're coming from single-curve noble packages, the following changes need to be kept in mind:
|
||||
|
||||
- 2d affine (x, y) points have been removed to reduce complexity and improve speed
|
||||
- Removed `number` support as a type for private keys. `bigint` is still supported
|
||||
- `mod`, `invert` are no longer present in `utils`. Use `@noble/curves/abstract/modular.js` now.
|
||||
|
||||
Upgrading from @noble/secp256k1 1.7:
|
||||
|
||||
- Compressed (33-byte) public keys are now returned by default, instead of uncompressed
|
||||
- Methods are now synchronous. Setting `secp.utils.hmacSha256` is no longer required
|
||||
- `sign()`
|
||||
- `der`, `recovered` options were removed
|
||||
- `canonical` was renamed to `lowS`
|
||||
- Return type is now `{ r: bigint, s: bigint, recovery: number }` instance of `Signature`
|
||||
- `verify()`
|
||||
- `strict` was renamed to `lowS`
|
||||
- `recoverPublicKey()`: moved to sig instance `Signature#recoverPublicKey(msgHash)`
|
||||
- `Point` was removed: use `ProjectivePoint` in xyz coordinates
|
||||
- `utils`: Many methods were removed, others were moved to `schnorr` namespace
|
||||
|
||||
Upgrading from @noble/ed25519 1.7:
|
||||
|
||||
- Methods are now synchronous. Setting `secp.utils.hmacSha256` is no longer required
|
||||
- ed25519ph, ed25519ctx
|
||||
- `Point` was removed: use `ExtendedPoint` in xyzt coordinates
|
||||
- `Signature` was removed
|
||||
- `getSharedSecret` was removed: use separate x25519 sub-module
|
||||
- `bigint` is no longer allowed in `getPublicKey`, `sign`, `verify`. Reason: ed25519 is LE, can lead to bugs
|
||||
|
||||
## Contributing & testing
|
||||
|
||||
1. Clone the repository
|
||||
|
||||
7
benchmark/_shared.js
Normal file
7
benchmark/_shared.js
Normal file
@@ -0,0 +1,7 @@
|
||||
export function generateData(curve) {
|
||||
const priv = curve.utils.randomPrivateKey();
|
||||
const pub = curve.getPublicKey(priv);
|
||||
const msg = curve.utils.randomPrivateKey();
|
||||
const sig = curve.sign(msg, priv);
|
||||
return { priv, pub, msg, sig };
|
||||
}
|
||||
52
benchmark/bls.js
Normal file
52
benchmark/bls.js
Normal file
@@ -0,0 +1,52 @@
|
||||
import { readFileSync } from 'fs';
|
||||
import { mark, run } from 'micro-bmark';
|
||||
import { bls12_381 as bls } from '../lib/bls12-381.js';
|
||||
|
||||
const G2_VECTORS = readFileSync('../test/bls12-381/bls12-381-g2-test-vectors.txt', 'utf-8')
|
||||
.trim()
|
||||
.split('\n')
|
||||
.map((l) => l.split(':'));
|
||||
|
||||
run(async () => {
|
||||
console.log(`\x1b[36mbls12-381\x1b[0m`);
|
||||
let p1, p2, sig;
|
||||
await mark('init', 1, () => {
|
||||
p1 =
|
||||
bls.G1.ProjectivePoint.BASE.multiply(
|
||||
0x28b90deaf189015d3a325908c5e0e4bf00f84f7e639b056ff82d7e70b6eede4cn
|
||||
);
|
||||
p2 =
|
||||
bls.G2.ProjectivePoint.BASE.multiply(
|
||||
0x28b90deaf189015d3a325908c5e0e4bf00f84f7e639b056ff82d7e70b6eede4dn
|
||||
);
|
||||
bls.pairing(p1, p2);
|
||||
});
|
||||
const priv = '28b90deaf189015d3a325908c5e0e4bf00f84f7e639b056ff82d7e70b6eede4c';
|
||||
sig = bls.sign('09', priv);
|
||||
const pubs = G2_VECTORS.map((v) => bls.getPublicKey(v[0]));
|
||||
const sigs = G2_VECTORS.map((v) => v[2]);
|
||||
const pub = bls.getPublicKey(priv);
|
||||
const pub512 = pubs.slice(0, 512); // .map(bls.PointG1.fromHex)
|
||||
const pub32 = pub512.slice(0, 32);
|
||||
const pub128 = pub512.slice(0, 128);
|
||||
const pub2048 = pub512.concat(pub512, pub512, pub512);
|
||||
const sig512 = sigs.slice(0, 512); // .map(bls.PointG2.fromSignature);
|
||||
const sig32 = sig512.slice(0, 32);
|
||||
const sig128 = sig512.slice(0, 128);
|
||||
const sig2048 = sig512.concat(sig512, sig512, sig512);
|
||||
await mark('getPublicKey 1-bit', 1000, () => bls.getPublicKey('2'.padStart(64, '0')));
|
||||
await mark('getPublicKey', 1000, () => bls.getPublicKey(priv));
|
||||
await mark('sign', 50, () => bls.sign('09', priv));
|
||||
await mark('verify', 50, () => bls.verify(sig, '09', pub));
|
||||
await mark('pairing', 100, () => bls.pairing(p1, p2));
|
||||
await mark('aggregatePublicKeys/8', 100, () => bls.aggregatePublicKeys(pubs.slice(0, 8)));
|
||||
await mark('aggregatePublicKeys/32', 50, () => bls.aggregatePublicKeys(pub32));
|
||||
await mark('aggregatePublicKeys/128', 20, () => bls.aggregatePublicKeys(pub128));
|
||||
await mark('aggregatePublicKeys/512', 10, () => bls.aggregatePublicKeys(pub512));
|
||||
await mark('aggregatePublicKeys/2048', 5, () => bls.aggregatePublicKeys(pub2048));
|
||||
await mark('aggregateSignatures/8', 100, () => bls.aggregateSignatures(sigs.slice(0, 8)));
|
||||
await mark('aggregateSignatures/32', 50, () => bls.aggregateSignatures(sig32));
|
||||
await mark('aggregateSignatures/128', 20, () => bls.aggregateSignatures(sig128));
|
||||
await mark('aggregateSignatures/512', 10, () => bls.aggregateSignatures(sig512));
|
||||
await mark('aggregateSignatures/2048', 5, () => bls.aggregateSignatures(sig2048));
|
||||
});
|
||||
23
benchmark/curves.js
Normal file
23
benchmark/curves.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import { run, mark, utils } from 'micro-bmark';
|
||||
import { generateData } from './_shared.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';
|
||||
|
||||
run(async () => {
|
||||
const RAM = false
|
||||
for (let kv of Object.entries({ P256, P384, P521, ed25519, ed448 })) {
|
||||
const [name, curve] = kv;
|
||||
console.log();
|
||||
console.log(`\x1b[36m${name}\x1b[0m`);
|
||||
if (RAM) utils.logMem();
|
||||
await mark('init', 1, () => curve.utils.precompute(8));
|
||||
const d = generateData(curve);
|
||||
await mark('getPublicKey', 5000, () => curve.getPublicKey(d.priv));
|
||||
await mark('sign', 5000, () => curve.sign(d.msg, d.priv));
|
||||
await mark('verify', 500, () => curve.verify(d.sig, d.msg, d.pub));
|
||||
if (RAM) utils.logMem();
|
||||
}
|
||||
});
|
||||
@@ -1,398 +0,0 @@
|
||||
import * as bench from 'micro-bmark';
|
||||
const { run, mark } = bench; // or bench.mark
|
||||
import { readFileSync } from 'fs';
|
||||
|
||||
// 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';
|
||||
import { bls12_381 as bls } from '../lib/bls12-381.js';
|
||||
|
||||
// Others
|
||||
import { hmac } from '@noble/hashes/hmac';
|
||||
import { sha256 } from '@noble/hashes/sha256';
|
||||
import { sha512 } from '@noble/hashes/sha512';
|
||||
|
||||
import * as old_secp from '@noble/secp256k1';
|
||||
import * as old_bls from '@noble/bls12-381';
|
||||
import { concatBytes, hexToBytes } from '@noble/hashes/utils';
|
||||
|
||||
import * as starkwareCrypto from '@starkware-industries/starkware-crypto-utils';
|
||||
import * as stark from '../lib/stark.js';
|
||||
|
||||
old_secp.utils.sha256Sync = (...msgs) =>
|
||||
sha256
|
||||
.create()
|
||||
.update(concatBytes(...msgs))
|
||||
.digest();
|
||||
old_secp.utils.hmacSha256Sync = (key, ...msgs) =>
|
||||
hmac
|
||||
.create(sha256, key)
|
||||
.update(concatBytes(...msgs))
|
||||
.digest();
|
||||
import * as noble_ed25519 from '@noble/ed25519';
|
||||
noble_ed25519.utils.sha512Sync = (...m) => sha512(concatBytes(...m));
|
||||
|
||||
// BLS
|
||||
const G2_VECTORS = readFileSync('../test/bls12-381/bls12-381-g2-test-vectors.txt', 'utf-8')
|
||||
.trim()
|
||||
.split('\n')
|
||||
.map((l) => l.split(':'));
|
||||
let p1, p2, oldp1, oldp2;
|
||||
// /BLS
|
||||
|
||||
for (let item of [secp256k1, ed25519, ed448, P256, P384, P521, old_secp, noble_ed25519]) {
|
||||
item.utils.precompute(8);
|
||||
}
|
||||
|
||||
const ONLY_NOBLE = process.argv[2] === 'noble';
|
||||
|
||||
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: () => {
|
||||
return generateData(secp256k1);
|
||||
},
|
||||
getPublicKey1: {
|
||||
samples: 10000,
|
||||
secp256k1_old: () => old_secp.getPublicKey(3n),
|
||||
secp256k1: () => secp256k1.getPublicKey(3n),
|
||||
},
|
||||
getPublicKey255: {
|
||||
samples: 10000,
|
||||
secp256k1_old: () => old_secp.getPublicKey(2n ** 255n - 1n),
|
||||
secp256k1: () => secp256k1.getPublicKey(2n ** 255n - 1n),
|
||||
},
|
||||
sign: {
|
||||
samples: 5000,
|
||||
secp256k1_old: ({ msg, priv }) => old_secp.signSync(msg, priv),
|
||||
secp256k1: ({ msg, priv }) => secp256k1.sign(msg, priv),
|
||||
},
|
||||
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),
|
||||
},
|
||||
getSharedSecret: {
|
||||
samples: 1000,
|
||||
secp256k1_old: ({ pub, priv }) => old_secp.getSharedSecret(priv, pub),
|
||||
secp256k1: ({ pub, priv }) => secp256k1.getSharedSecret(priv, pub),
|
||||
},
|
||||
recoverPublicKey: {
|
||||
samples: 1000,
|
||||
secp256k1_old: ({ sig, msg }) =>
|
||||
old_secp.recoverPublicKey(msg, new old_secp.Signature(sig.r, sig.s), sig.recovery),
|
||||
secp256k1: ({ sig, msg }) => sig.recoverPublicKey(msg),
|
||||
},
|
||||
},
|
||||
ed25519: {
|
||||
data: () => {
|
||||
function to32Bytes(numOrStr) {
|
||||
const hex = typeof numOrStr === 'string' ? numOrStr : numOrStr.toString(16);
|
||||
return hexToBytes(hex.padStart(64, '0'));
|
||||
}
|
||||
const priv = to32Bytes(0x9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60n);
|
||||
const pub = noble_ed25519.sync.getPublicKey(priv);
|
||||
const msg = to32Bytes('deadbeefdeadbeefdeadbeefdeadbeefdeadbeef');
|
||||
const sig = noble_ed25519.sync.sign(msg, priv);
|
||||
return { pub, priv, msg, sig };
|
||||
},
|
||||
getPublicKey: {
|
||||
samples: 10000,
|
||||
old: () => noble_ed25519.sync.getPublicKey(noble_ed25519.utils.randomPrivateKey()),
|
||||
noble: () => ed25519.getPublicKey(ed25519.utils.randomPrivateKey()),
|
||||
},
|
||||
sign: {
|
||||
samples: 5000,
|
||||
old: ({ msg, priv }) => noble_ed25519.sync.sign(msg, priv),
|
||||
noble: ({ msg, priv }) => ed25519.sign(msg, priv),
|
||||
},
|
||||
verify: {
|
||||
samples: 1000,
|
||||
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);
|
||||
|
||||
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 { priv, sig, msg, pub, publicKeyStark, msgHash, keyPair };
|
||||
},
|
||||
pedersen: {
|
||||
samples: 500,
|
||||
old: () => {
|
||||
return starkwareCrypto.default.pedersen([
|
||||
'3d937c035c878245caf64531a5756109c53068da139362728feb561405371cb',
|
||||
'208a0a10250e382e1e4bbe2880906c2791bf6275695e02fbbc6aeff9cd8b31a',
|
||||
]);
|
||||
},
|
||||
noble: () => {
|
||||
return stark.pedersen(
|
||||
'3d937c035c878245caf64531a5756109c53068da139362728feb561405371cb',
|
||||
'208a0a10250e382e1e4bbe2880906c2791bf6275695e02fbbc6aeff9cd8b31a'
|
||||
);
|
||||
},
|
||||
},
|
||||
verify: {
|
||||
samples: 500,
|
||||
old: ({ publicKeyStark, msgHash, keyPair }) => {
|
||||
return starkwareCrypto.default.verify(
|
||||
publicKeyStark,
|
||||
msgHash,
|
||||
starkwareCrypto.default.sign(keyPair, msgHash)
|
||||
);
|
||||
},
|
||||
noble: ({ priv, msg, pub }) => {
|
||||
return stark.verify(stark.sign(msg, priv), msg, pub);
|
||||
},
|
||||
},
|
||||
},
|
||||
'bls12-381': {
|
||||
data: async () => {
|
||||
const priv = '28b90deaf189015d3a325908c5e0e4bf00f84f7e639b056ff82d7e70b6eede4c';
|
||||
const pubs = G2_VECTORS.map((v) => bls.getPublicKey(v[0]));
|
||||
const sigs = G2_VECTORS.map((v) => v[2]);
|
||||
const pub = bls.getPublicKey(priv);
|
||||
const pub512 = pubs.slice(0, 512); // .map(bls.PointG1.fromHex)
|
||||
const pub32 = pub512.slice(0, 32);
|
||||
const pub128 = pub512.slice(0, 128);
|
||||
const pub2048 = pub512.concat(pub512, pub512, pub512);
|
||||
const sig512 = sigs.slice(0, 512); // .map(bls.PointG2.fromSignature);
|
||||
const sig32 = sig512.slice(0, 32);
|
||||
const sig128 = sig512.slice(0, 128);
|
||||
const sig2048 = sig512.concat(sig512, sig512, sig512);
|
||||
return {
|
||||
priv,
|
||||
pubs,
|
||||
sigs,
|
||||
pub,
|
||||
pub512,
|
||||
pub32,
|
||||
pub128,
|
||||
pub2048,
|
||||
sig32,
|
||||
sig128,
|
||||
sig512,
|
||||
sig2048,
|
||||
};
|
||||
},
|
||||
init: {
|
||||
samples: 1,
|
||||
old: () => {
|
||||
oldp1 =
|
||||
old_bls.PointG1.BASE.multiply(
|
||||
0x28b90deaf189015d3a325908c5e0e4bf00f84f7e639b056ff82d7e70b6eede4cn
|
||||
);
|
||||
oldp2 =
|
||||
old_bls.PointG2.BASE.multiply(
|
||||
0x28b90deaf189015d3a325908c5e0e4bf00f84f7e639b056ff82d7e70b6eede4dn
|
||||
);
|
||||
old_bls.pairing(oldp1, oldp2);
|
||||
},
|
||||
noble: () => {
|
||||
p1 =
|
||||
bls.G1.Point.BASE.multiply(
|
||||
0x28b90deaf189015d3a325908c5e0e4bf00f84f7e639b056ff82d7e70b6eede4cn
|
||||
);
|
||||
p2 =
|
||||
bls.G2.Point.BASE.multiply(
|
||||
0x28b90deaf189015d3a325908c5e0e4bf00f84f7e639b056ff82d7e70b6eede4dn
|
||||
);
|
||||
bls.pairing(p1, p2);
|
||||
},
|
||||
},
|
||||
'getPublicKey (1-bit)': {
|
||||
samples: 1000,
|
||||
old: () => old_bls.getPublicKey('2'.padStart(64, '0')),
|
||||
noble: () => bls.getPublicKey('2'.padStart(64, '0')),
|
||||
},
|
||||
getPublicKey: {
|
||||
samples: 1000,
|
||||
old: ({ priv }) => old_bls.getPublicKey(priv),
|
||||
noble: ({ priv }) => bls.getPublicKey(priv),
|
||||
},
|
||||
sign: {
|
||||
samples: 50,
|
||||
old: ({ priv }) => old_bls.sign('09', priv),
|
||||
noble: ({ priv }) => bls.sign('09', priv),
|
||||
},
|
||||
verify: {
|
||||
samples: 50,
|
||||
old: ({ pub }) =>
|
||||
old_bls.verify(
|
||||
'8647aa9680cd0cdf065b94e818ff2bb948cc97838bcee987b9bc1b76d0a0a6e0d85db4e9d75aaedfc79d4ea2733a21ae0579014de7636dd2943d45b87c82b1c66a289006b0b9767921bb8edd3f6c5c5dec0d54cd65f61513113c50cc977849e5',
|
||||
'09',
|
||||
pub
|
||||
),
|
||||
noble: ({ pub }) =>
|
||||
bls.verify(
|
||||
'8647aa9680cd0cdf065b94e818ff2bb948cc97838bcee987b9bc1b76d0a0a6e0d85db4e9d75aaedfc79d4ea2733a21ae0579014de7636dd2943d45b87c82b1c66a289006b0b9767921bb8edd3f6c5c5dec0d54cd65f61513113c50cc977849e5',
|
||||
'09',
|
||||
pub
|
||||
),
|
||||
},
|
||||
pairing: {
|
||||
samples: 100,
|
||||
old: () => old_bls.pairing(oldp1, oldp2),
|
||||
noble: () => bls.pairing(p1, p2),
|
||||
},
|
||||
'hashToCurve/G1': {
|
||||
samples: 500,
|
||||
old: () => old_bls.PointG1.hashToCurve('abcd'),
|
||||
noble: () => bls.G1.Point.hashToCurve('abcd'),
|
||||
},
|
||||
'hashToCurve/G2': {
|
||||
samples: 200,
|
||||
old: () => old_bls.PointG2.hashToCurve('abcd'),
|
||||
noble: () => bls.G2.Point.hashToCurve('abcd'),
|
||||
},
|
||||
// SLOW PART
|
||||
// Requires points which we cannot init before (data fn same for all)
|
||||
// await mark('sign/nc', 30, () => bls.sign(msgp, priv));
|
||||
// await mark('verify/nc', 30, () => bls.verify(sigp, msgp, pubp));
|
||||
'aggregatePublicKeys/8': {
|
||||
samples: 100,
|
||||
old: ({ pubs }) => old_bls.aggregatePublicKeys(pubs.slice(0, 8)),
|
||||
noble: ({ pubs }) => bls.aggregatePublicKeys(pubs.slice(0, 8)),
|
||||
},
|
||||
'aggregatePublicKeys/32': {
|
||||
samples: 50,
|
||||
old: ({ pub32 }) => old_bls.aggregatePublicKeys(pub32.map(old_bls.PointG1.fromHex)),
|
||||
noble: ({ pub32 }) => bls.aggregatePublicKeys(pub32.map(bls.G1.Point.fromHex)),
|
||||
},
|
||||
'aggregatePublicKeys/128': {
|
||||
samples: 20,
|
||||
old: ({ pub128 }) => old_bls.aggregatePublicKeys(pub128.map(old_bls.PointG1.fromHex)),
|
||||
noble: ({ pub128 }) => bls.aggregatePublicKeys(pub128.map(bls.G1.Point.fromHex)),
|
||||
},
|
||||
'aggregatePublicKeys/512': {
|
||||
samples: 10,
|
||||
old: ({ pub512 }) => old_bls.aggregatePublicKeys(pub512.map(old_bls.PointG1.fromHex)),
|
||||
noble: ({ pub512 }) => bls.aggregatePublicKeys(pub512.map(bls.G1.Point.fromHex)),
|
||||
},
|
||||
'aggregatePublicKeys/2048': {
|
||||
samples: 5,
|
||||
old: ({ pub2048 }) => old_bls.aggregatePublicKeys(pub2048.map(old_bls.PointG1.fromHex)),
|
||||
noble: ({ pub2048 }) => bls.aggregatePublicKeys(pub2048.map(bls.G1.Point.fromHex)),
|
||||
},
|
||||
'aggregateSignatures/8': {
|
||||
samples: 50,
|
||||
old: ({ sigs }) => old_bls.aggregateSignatures(sigs.slice(0, 8)),
|
||||
noble: ({ sigs }) => bls.aggregateSignatures(sigs.slice(0, 8)),
|
||||
},
|
||||
'aggregateSignatures/32': {
|
||||
samples: 10,
|
||||
old: ({ sig32 }) => old_bls.aggregateSignatures(sig32.map(old_bls.PointG2.fromSignature)),
|
||||
noble: ({ sig32 }) => bls.aggregateSignatures(sig32.map(bls.Signature.decode)),
|
||||
},
|
||||
'aggregateSignatures/128': {
|
||||
samples: 5,
|
||||
old: ({ sig128 }) => old_bls.aggregateSignatures(sig128.map(old_bls.PointG2.fromSignature)),
|
||||
noble: ({ sig128 }) => bls.aggregateSignatures(sig128.map(bls.Signature.decode)),
|
||||
},
|
||||
'aggregateSignatures/512': {
|
||||
samples: 3,
|
||||
old: ({ sig512 }) => old_bls.aggregateSignatures(sig512.map(old_bls.PointG2.fromSignature)),
|
||||
noble: ({ sig512 }) => bls.aggregateSignatures(sig512.map(bls.Signature.decode)),
|
||||
},
|
||||
'aggregateSignatures/2048': {
|
||||
samples: 2,
|
||||
old: ({ sig2048 }) => old_bls.aggregateSignatures(sig2048.map(old_bls.PointG2.fromSignature)),
|
||||
noble: ({ sig2048 }) => bls.aggregateSignatures(sig2048.map(bls.Signature.decode)),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const main = () =>
|
||||
run(async () => {
|
||||
for (const [name, curve] of Object.entries(CURVES)) {
|
||||
console.log(`==== ${name} ====`);
|
||||
const data = await curve.data();
|
||||
for (const [fnName, libs] of Object.entries(curve)) {
|
||||
if (fnName === 'data') continue;
|
||||
const samples = libs.samples;
|
||||
console.log(` - ${fnName} (samples: ${samples})`);
|
||||
for (const [lib, fn] of Object.entries(libs)) {
|
||||
if (lib === 'samples') continue;
|
||||
if (ONLY_NOBLE && lib !== 'noble') continue;
|
||||
await mark(` ${lib}`, samples, () => fn(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Log current RAM
|
||||
bench.logMem();
|
||||
});
|
||||
|
||||
// ESM is broken.
|
||||
import url from 'url';
|
||||
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
|
||||
main();
|
||||
}
|
||||
@@ -1,24 +1,22 @@
|
||||
{
|
||||
"name": "benchmark",
|
||||
"private": true,
|
||||
"version": "0.1.0",
|
||||
"description": "benchmarks",
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"bench": "node index.js"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"micro-bmark": "0.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@noble/bls12-381": "^1.4.0",
|
||||
"@noble/ed25519": "^1.7.1",
|
||||
"@noble/hashes": "^1.1.5",
|
||||
"@noble/secp256k1": "^1.7.0",
|
||||
"@starkware-industries/starkware-crypto-utils": "^0.0.2"
|
||||
}
|
||||
"name": "benchmark",
|
||||
"private": true,
|
||||
"version": "0.1.0",
|
||||
"description": "benchmarks",
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"bench": "node index.js"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"micro-bmark": "0.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@noble/hashes": "^1.1.5",
|
||||
"@starkware-industries/starkware-crypto-utils": "^0.0.2",
|
||||
"elliptic": "^6.5.4"
|
||||
}
|
||||
}
|
||||
|
||||
22
benchmark/secp256k1.js
Normal file
22
benchmark/secp256k1.js
Normal file
@@ -0,0 +1,22 @@
|
||||
import { run, mark, utils } from 'micro-bmark';
|
||||
import { secp256k1, schnorr } from '../lib/secp256k1.js';
|
||||
import { generateData } from './_shared.js';
|
||||
|
||||
run(async () => {
|
||||
const RAM = false;
|
||||
if (RAM) utils.logMem();
|
||||
console.log(`\x1b[36msecp256k1\x1b[0m`);
|
||||
await mark('init', 1, () => secp256k1.utils.precompute(8));
|
||||
const d = generateData(secp256k1);
|
||||
await mark('getPublicKey', 10000, () => secp256k1.getPublicKey(d.priv));
|
||||
await mark('sign', 10000, () => secp256k1.sign(d.msg, d.priv));
|
||||
await mark('verify', 1000, () => secp256k1.verify(d.sig, d.msg, d.pub));
|
||||
const pub2 = secp256k1.getPublicKey(secp256k1.utils.randomPrivateKey());
|
||||
await mark('getSharedSecret', 1000, () => secp256k1.getSharedSecret(d.priv, pub2));
|
||||
await mark('recoverPublicKey', 1000, () => d.sig.recoverPublicKey(d.msg));
|
||||
const s = schnorr.sign(d.msg, d.priv);
|
||||
const spub = schnorr.getPublicKey(d.priv);
|
||||
await mark('schnorr.sign', 1000, () => schnorr.sign(d.msg, d.priv));
|
||||
await mark('schnorr.verify', 1000, () => schnorr.verify(s, d.msg, spub));
|
||||
if (RAM) utils.logMem();
|
||||
});
|
||||
56
benchmark/stark.js
Normal file
56
benchmark/stark.js
Normal file
@@ -0,0 +1,56 @@
|
||||
import { run, mark, compare, utils } from 'micro-bmark';
|
||||
import * as starkwareCrypto from '@starkware-industries/starkware-crypto-utils';
|
||||
import * as stark from '../lib/stark.js';
|
||||
|
||||
run(async () => {
|
||||
const RAM = false;
|
||||
if (RAM) utils.logMem();
|
||||
console.log(`\x1b[36mstark\x1b[0m`);
|
||||
await mark('init', 1, () => stark.utils.precompute(8));
|
||||
const d = (() => {
|
||||
const priv = '2dccce1da22003777062ee0870e9881b460a8b7eca276870f57c601f182136c';
|
||||
const msg = 'c465dd6b1bbffdb05442eb17f5ca38ad1aa78a6f56bf4415bdee219114a47';
|
||||
const pub = stark.getPublicKey(priv);
|
||||
const sig = stark.sign(msg, priv);
|
||||
|
||||
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 { priv, sig, msg, pub, publicKeyStark, msgHash, keyPair };
|
||||
})();
|
||||
await compare('pedersen', 500, {
|
||||
old: () => {
|
||||
return starkwareCrypto.default.pedersen([
|
||||
'3d937c035c878245caf64531a5756109c53068da139362728feb561405371cb',
|
||||
'208a0a10250e382e1e4bbe2880906c2791bf6275695e02fbbc6aeff9cd8b31a',
|
||||
]);
|
||||
},
|
||||
noble: () => {
|
||||
return stark.pedersen(
|
||||
'3d937c035c878245caf64531a5756109c53068da139362728feb561405371cb',
|
||||
'208a0a10250e382e1e4bbe2880906c2791bf6275695e02fbbc6aeff9cd8b31a'
|
||||
);
|
||||
},
|
||||
});
|
||||
await mark('poseidon', 10000, () => stark.poseidonHash(
|
||||
0x3d937c035c878245caf64531a5756109c53068da139362728feb561405371cbn,
|
||||
0x208a0a10250e382e1e4bbe2880906c2791bf6275695e02fbbc6aeff9cd8b31an
|
||||
));
|
||||
await compare('verify', 500, {
|
||||
old: () => {
|
||||
return starkwareCrypto.default.verify(
|
||||
d.publicKeyStark,
|
||||
d.msgHash,
|
||||
starkwareCrypto.default.sign(d.keyPair, d.msgHash)
|
||||
);
|
||||
},
|
||||
noble: () => {
|
||||
return stark.verify(stark.sign(d.msg, d.priv), d.msg, d.pub);
|
||||
},
|
||||
});
|
||||
if (RAM) utils.logMem();
|
||||
});
|
||||
23
package.json
23
package.json
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@noble/curves",
|
||||
"version": "0.5.0",
|
||||
"version": "0.6.2",
|
||||
"description": "Minimal, auditable JS implementation of elliptic curve cryptography",
|
||||
"files": [
|
||||
"lib"
|
||||
],
|
||||
"scripts": {
|
||||
"bench": "node benchmark/index.js",
|
||||
"bench": "cd benchmark; node secp256k1.js; node curves.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'",
|
||||
@@ -30,9 +30,9 @@
|
||||
"@scure/bip39": "~1.1.0",
|
||||
"@types/node": "18.11.3",
|
||||
"fast-check": "3.0.0",
|
||||
"micro-bmark": "0.2.0",
|
||||
"micro-should": "0.2.0",
|
||||
"prettier": "2.6.2",
|
||||
"micro-bmark": "0.3.0",
|
||||
"micro-should": "0.4.0",
|
||||
"prettier": "2.8.3",
|
||||
"rollup": "2.75.5",
|
||||
"typescript": "4.7.3"
|
||||
},
|
||||
@@ -73,16 +73,21 @@
|
||||
"import": "./lib/esm/abstract/hash-to-curve.js",
|
||||
"default": "./lib/abstract/hash-to-curve.js"
|
||||
},
|
||||
"./abstract/group": {
|
||||
"types": "./lib/abstract/group.d.ts",
|
||||
"import": "./lib/esm/abstract/group.js",
|
||||
"default": "./lib/abstract/group.js"
|
||||
"./abstract/curve": {
|
||||
"types": "./lib/abstract/curve.d.ts",
|
||||
"import": "./lib/esm/abstract/curve.js",
|
||||
"default": "./lib/abstract/curve.js"
|
||||
},
|
||||
"./abstract/utils": {
|
||||
"types": "./lib/abstract/utils.d.ts",
|
||||
"import": "./lib/esm/abstract/utils.js",
|
||||
"default": "./lib/abstract/utils.js"
|
||||
},
|
||||
"./abstract/poseidon": {
|
||||
"types": "./lib/abstract/poseidon.d.ts",
|
||||
"import": "./lib/esm/abstract/poseidon.js",
|
||||
"default": "./lib/abstract/poseidon.js"
|
||||
},
|
||||
"./_shortw_utils": {
|
||||
"types": "./lib/_shortw_utils.d.ts",
|
||||
"import": "./lib/esm/_shortw_utils.js",
|
||||
|
||||
@@ -1,93 +1,110 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
// Barreto-Lynn-Scott Curves. A family of pairing friendly curves, with embedding degree = 12 or 24
|
||||
// NOTE: only 12 supported for now
|
||||
// Constructed from pair of weierstrass curves, based pairing logic
|
||||
import * as mod from './modular.js';
|
||||
import { ensureBytes, numberToBytesBE, bytesToNumberBE, bitLen, bitGet } from './utils.js';
|
||||
import * as utils from './utils.js';
|
||||
// Types
|
||||
import { hexToBytes, bytesToHex, Hex, PrivKey } from './utils.js';
|
||||
import { htfOpts, stringToBytes, hash_to_field, expand_message_xmd } from './hash-to-curve.js';
|
||||
import { CurvePointsType, PointType, CurvePointsRes, weierstrassPoints } from './weierstrass.js';
|
||||
/**
|
||||
* BLS (Barreto-Lynn-Scott) family of pairing-friendly curves.
|
||||
* Implements BLS (Boneh-Lynn-Shacham) signatures.
|
||||
* Consists of two curves: G1 and G2:
|
||||
* - G1 is a subgroup of (x, y) E(Fq) over y² = x³ + 4.
|
||||
* - G2 is a subgroup of ((x₁, x₂+i), (y₁, y₂+i)) E(Fq²) over y² = x³ + 4(1 + i) where i is √-1
|
||||
* - Gt, created by bilinear (ate) pairing e(G1, G2), consists of p-th roots of unity in
|
||||
* Fq^k where k is embedding degree. Only degree 12 is currently supported, 24 is not.
|
||||
* Pairing is used to aggregate and verify signatures.
|
||||
* We are using Fp for private keys (shorter) and Fp₂ for signatures (longer).
|
||||
* Some projects may prefer to swap this relation, it is not supported for now.
|
||||
*/
|
||||
import { AffinePoint } from './curve.js';
|
||||
import { Field, hashToPrivateScalar } from './modular.js';
|
||||
import { Hex, PrivKey, CHash, bitLen, bitGet, hexToBytes, bytesToHex } from './utils.js';
|
||||
import * as htf from './hash-to-curve.js';
|
||||
import {
|
||||
CurvePointsType,
|
||||
ProjPointType as ProjPointType,
|
||||
CurvePointsRes,
|
||||
weierstrassPoints,
|
||||
} from './weierstrass.js';
|
||||
|
||||
type Fp = bigint; // Can be different field?
|
||||
|
||||
export type SignatureCoder<Fp2> = {
|
||||
decode(hex: Hex): PointType<Fp2>;
|
||||
encode(point: PointType<Fp2>): Uint8Array;
|
||||
decode(hex: Hex): ProjPointType<Fp2>;
|
||||
encode(point: ProjPointType<Fp2>): Uint8Array;
|
||||
};
|
||||
|
||||
export type CurveType<Fp, Fp2, Fp6, Fp12> = {
|
||||
r: bigint;
|
||||
G1: Omit<CurvePointsType<Fp>, 'n'>;
|
||||
G1: Omit<CurvePointsType<Fp>, 'n'> & {
|
||||
mapToCurve: htf.MapToCurve<Fp>;
|
||||
htfDefaults: htf.Opts;
|
||||
};
|
||||
G2: Omit<CurvePointsType<Fp2>, 'n'> & {
|
||||
Signature: SignatureCoder<Fp2>;
|
||||
mapToCurve: htf.MapToCurve<Fp2>;
|
||||
htfDefaults: htf.Opts;
|
||||
};
|
||||
x: bigint;
|
||||
Fp: mod.Field<Fp>;
|
||||
Fr: mod.Field<bigint>;
|
||||
Fp2: mod.Field<Fp2> & {
|
||||
Fp: Field<Fp>;
|
||||
Fr: Field<bigint>;
|
||||
Fp2: Field<Fp2> & {
|
||||
reim: (num: Fp2) => { re: bigint; im: bigint };
|
||||
multiplyByB: (num: Fp2) => Fp2;
|
||||
frobeniusMap(num: Fp2, power: number): Fp2;
|
||||
};
|
||||
Fp6: mod.Field<Fp6>;
|
||||
Fp12: mod.Field<Fp12> & {
|
||||
Fp6: Field<Fp6>;
|
||||
Fp12: Field<Fp12> & {
|
||||
frobeniusMap(num: Fp12, power: number): Fp12;
|
||||
multiplyBy014(num: Fp12, o0: Fp2, o1: Fp2, o4: Fp2): Fp12;
|
||||
conjugate(num: Fp12): Fp12;
|
||||
finalExponentiate(num: Fp12): Fp12;
|
||||
};
|
||||
htfDefaults: htfOpts;
|
||||
hash: utils.CHash; // Because we need outputLen for DRBG
|
||||
htfDefaults: htf.Opts;
|
||||
hash: CHash; // Because we need outputLen for DRBG
|
||||
randomBytes: (bytesLength?: number) => Uint8Array;
|
||||
};
|
||||
|
||||
export type CurveFn<Fp, Fp2, Fp6, Fp12> = {
|
||||
CURVE: CurveType<Fp, Fp2, Fp6, Fp12>;
|
||||
Fr: mod.Field<bigint>;
|
||||
Fp: mod.Field<Fp>;
|
||||
Fp2: mod.Field<Fp2>;
|
||||
Fp6: mod.Field<Fp6>;
|
||||
Fp12: mod.Field<Fp12>;
|
||||
Fr: Field<bigint>;
|
||||
Fp: Field<Fp>;
|
||||
Fp2: Field<Fp2>;
|
||||
Fp6: Field<Fp6>;
|
||||
Fp12: Field<Fp12>;
|
||||
G1: CurvePointsRes<Fp>;
|
||||
G2: CurvePointsRes<Fp2>;
|
||||
Signature: SignatureCoder<Fp2>;
|
||||
millerLoop: (ell: [Fp2, Fp2, Fp2][], g1: [Fp, Fp]) => Fp12;
|
||||
calcPairingPrecomputes: (x: Fp2, y: Fp2) => [Fp2, Fp2, Fp2][];
|
||||
pairing: (P: PointType<Fp>, Q: PointType<Fp2>, withFinalExponent?: boolean) => Fp12;
|
||||
calcPairingPrecomputes: (p: AffinePoint<Fp2>) => [Fp2, Fp2, Fp2][];
|
||||
// prettier-ignore
|
||||
hashToCurve: {
|
||||
G1: ReturnType<(typeof htf.hashToCurve<Fp>)>,
|
||||
G2: ReturnType<(typeof htf.hashToCurve<Fp2>)>,
|
||||
},
|
||||
pairing: (P: ProjPointType<Fp>, Q: ProjPointType<Fp2>, withFinalExponent?: boolean) => Fp12;
|
||||
getPublicKey: (privateKey: PrivKey) => Uint8Array;
|
||||
sign: {
|
||||
(message: Hex, privateKey: PrivKey): Uint8Array;
|
||||
(message: PointType<Fp2>, privateKey: PrivKey): PointType<Fp2>;
|
||||
(message: ProjPointType<Fp2>, privateKey: PrivKey): ProjPointType<Fp2>;
|
||||
};
|
||||
verify: (
|
||||
signature: Hex | PointType<Fp2>,
|
||||
message: Hex | PointType<Fp2>,
|
||||
publicKey: Hex | PointType<Fp>
|
||||
signature: Hex | ProjPointType<Fp2>,
|
||||
message: Hex | ProjPointType<Fp2>,
|
||||
publicKey: Hex | ProjPointType<Fp>
|
||||
) => boolean;
|
||||
aggregatePublicKeys: {
|
||||
(publicKeys: Hex[]): Uint8Array;
|
||||
(publicKeys: PointType<Fp>[]): PointType<Fp>;
|
||||
(publicKeys: ProjPointType<Fp>[]): ProjPointType<Fp>;
|
||||
};
|
||||
aggregateSignatures: {
|
||||
(signatures: Hex[]): Uint8Array;
|
||||
(signatures: PointType<Fp2>[]): PointType<Fp2>;
|
||||
(signatures: ProjPointType<Fp2>[]): ProjPointType<Fp2>;
|
||||
};
|
||||
verifyBatch: (
|
||||
signature: Hex | PointType<Fp2>,
|
||||
messages: (Hex | PointType<Fp2>)[],
|
||||
publicKeys: (Hex | PointType<Fp>)[]
|
||||
signature: Hex | ProjPointType<Fp2>,
|
||||
messages: (Hex | ProjPointType<Fp2>)[],
|
||||
publicKeys: (Hex | ProjPointType<Fp>)[]
|
||||
) => boolean;
|
||||
utils: {
|
||||
bytesToHex: typeof utils.bytesToHex;
|
||||
hexToBytes: typeof utils.hexToBytes;
|
||||
stringToBytes: typeof stringToBytes;
|
||||
hashToField: typeof hash_to_field;
|
||||
expandMessageXMD: typeof expand_message_xmd;
|
||||
mod: typeof mod.mod;
|
||||
getDSTLabel: () => string;
|
||||
setDSTLabel(newLabel: string): void;
|
||||
stringToBytes: typeof htf.stringToBytes;
|
||||
hashToField: typeof htf.hash_to_field;
|
||||
expandMessageXMD: typeof htf.expand_message_xmd;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -95,16 +112,14 @@ export function bls<Fp2, Fp6, Fp12>(
|
||||
CURVE: CurveType<Fp, Fp2, Fp6, Fp12>
|
||||
): CurveFn<Fp, Fp2, Fp6, Fp12> {
|
||||
// Fields looks pretty specific for curve, so for now we need to pass them with options
|
||||
const Fp = CURVE.Fp;
|
||||
const Fr = CURVE.Fr;
|
||||
const Fp2 = CURVE.Fp2;
|
||||
const Fp6 = CURVE.Fp6;
|
||||
const Fp12 = CURVE.Fp12;
|
||||
const { Fp, Fr, Fp2, Fp6, Fp12 } = CURVE;
|
||||
const BLS_X_LEN = bitLen(CURVE.x);
|
||||
const groupLen = 32; // TODO: calculate; hardcoded for now
|
||||
|
||||
// Pre-compute coefficients for sparse multiplication
|
||||
// Point addition and point double calculations is reused for coefficients
|
||||
function calcPairingPrecomputes(x: Fp2, y: Fp2) {
|
||||
function calcPairingPrecomputes(p: AffinePoint<Fp2>) {
|
||||
const { x, y } = p;
|
||||
// prettier-ignore
|
||||
const Qx = x, Qy = y, Qz = Fp2.ONE;
|
||||
// prettier-ignore
|
||||
@@ -112,18 +127,18 @@ export function bls<Fp2, Fp6, Fp12>(
|
||||
let ell_coeff: [Fp2, Fp2, Fp2][] = [];
|
||||
for (let i = BLS_X_LEN - 2; i >= 0; i--) {
|
||||
// Double
|
||||
let t0 = Fp2.square(Ry); // Ry²
|
||||
let t1 = Fp2.square(Rz); // Rz²
|
||||
let t0 = Fp2.sqr(Ry); // Ry²
|
||||
let t1 = Fp2.sqr(Rz); // Rz²
|
||||
let t2 = Fp2.multiplyByB(Fp2.mul(t1, 3n)); // 3 * T1 * B
|
||||
let t3 = Fp2.mul(t2, 3n); // 3 * T2
|
||||
let t4 = Fp2.sub(Fp2.sub(Fp2.square(Fp2.add(Ry, Rz)), t1), t0); // (Ry + Rz)² - T1 - T0
|
||||
let t4 = Fp2.sub(Fp2.sub(Fp2.sqr(Fp2.add(Ry, Rz)), t1), t0); // (Ry + Rz)² - T1 - T0
|
||||
ell_coeff.push([
|
||||
Fp2.sub(t2, t0), // T2 - T0
|
||||
Fp2.mul(Fp2.square(Rx), 3n), // 3 * Rx²
|
||||
Fp2.negate(t4), // -T4
|
||||
Fp2.mul(Fp2.sqr(Rx), 3n), // 3 * Rx²
|
||||
Fp2.neg(t4), // -T4
|
||||
]);
|
||||
Rx = Fp2.div(Fp2.mul(Fp2.mul(Fp2.sub(t0, t3), Rx), Ry), 2n); // ((T0 - T3) * Rx * Ry) / 2
|
||||
Ry = Fp2.sub(Fp2.square(Fp2.div(Fp2.add(t0, t3), 2n)), Fp2.mul(Fp2.square(t2), 3n)); // ((T0 + T3) / 2)² - 3 * T2²
|
||||
Ry = Fp2.sub(Fp2.sqr(Fp2.div(Fp2.add(t0, t3), 2n)), Fp2.mul(Fp2.sqr(t2), 3n)); // ((T0 + T3) / 2)² - 3 * T2²
|
||||
Rz = Fp2.mul(t0, t4); // T0 * T4
|
||||
if (bitGet(CURVE.x, i)) {
|
||||
// Addition
|
||||
@@ -131,13 +146,13 @@ export function bls<Fp2, Fp6, Fp12>(
|
||||
let t1 = Fp2.sub(Rx, Fp2.mul(Qx, Rz)); // Rx - Qx * Rz
|
||||
ell_coeff.push([
|
||||
Fp2.sub(Fp2.mul(t0, Qx), Fp2.mul(t1, Qy)), // T0 * Qx - T1 * Qy
|
||||
Fp2.negate(t0), // -T0
|
||||
Fp2.neg(t0), // -T0
|
||||
t1, // T1
|
||||
]);
|
||||
let t2 = Fp2.square(t1); // T1²
|
||||
let t2 = Fp2.sqr(t1); // T1²
|
||||
let t3 = Fp2.mul(t2, t1); // T2 * T1
|
||||
let t4 = Fp2.mul(t2, Rx); // T2 * Rx
|
||||
let t5 = Fp2.add(Fp2.sub(t3, Fp2.mul(t4, 2n)), Fp2.mul(Fp2.square(t0), Rz)); // T3 - 2 * T4 + T0² * Rz
|
||||
let t5 = Fp2.add(Fp2.sub(t3, Fp2.mul(t4, 2n)), Fp2.mul(Fp2.sqr(t0), Rz)); // T3 - 2 * T4 + T0² * Rz
|
||||
Rx = Fp2.mul(t1, t5); // T1 * T5
|
||||
Ry = Fp2.sub(Fp2.mul(Fp2.sub(t4, t5), t0), Fp2.mul(t3, Ry)); // (T4 - T5) * T0 - T3 * Ry
|
||||
Rz = Fp2.mul(Rz, t3); // Rz * T3
|
||||
@@ -147,102 +162,49 @@ export function bls<Fp2, Fp6, Fp12>(
|
||||
}
|
||||
|
||||
function millerLoop(ell: [Fp2, Fp2, Fp2][], g1: [Fp, Fp]): Fp12 {
|
||||
const { x } = CURVE;
|
||||
const Px = g1[0];
|
||||
const Py = g1[1];
|
||||
let f12 = Fp12.ONE;
|
||||
for (let j = 0, i = BLS_X_LEN - 2; i >= 0; i--, j++) {
|
||||
const E = ell[j];
|
||||
f12 = Fp12.multiplyBy014(f12, E[0], Fp2.mul(E[1], Px), Fp2.mul(E[2], Py));
|
||||
if (bitGet(CURVE.x, i)) {
|
||||
if (bitGet(x, i)) {
|
||||
j += 1;
|
||||
const F = ell[j];
|
||||
f12 = Fp12.multiplyBy014(f12, F[0], Fp2.mul(F[1], Px), Fp2.mul(F[2], Py));
|
||||
}
|
||||
if (i !== 0) f12 = Fp12.square(f12);
|
||||
if (i !== 0) f12 = Fp12.sqr(f12);
|
||||
}
|
||||
return Fp12.conjugate(f12);
|
||||
}
|
||||
|
||||
// bls12-381 is a construction of two curves:
|
||||
// 1. Fp: (x, y)
|
||||
// 2. Fp₂: ((x₁, x₂+i), (y₁, y₂+i)) - (complex numbers)
|
||||
//
|
||||
// Bilinear Pairing (ate pairing) is used to combine both elements into a paired one:
|
||||
// Fp₁₂ = e(Fp, Fp2)
|
||||
// where Fp₁₂ = 12-degree polynomial
|
||||
// Pairing is used to verify signatures.
|
||||
//
|
||||
// We are using Fp for private keys (shorter) and Fp2 for signatures (longer).
|
||||
// Some projects may prefer to swap this relation, it is not supported for now.
|
||||
|
||||
const htfDefaults = { ...CURVE.htfDefaults };
|
||||
|
||||
function isWithinCurveOrder(num: bigint): boolean {
|
||||
return 0 < num && num < CURVE.r;
|
||||
}
|
||||
|
||||
const utils = {
|
||||
hexToBytes: hexToBytes,
|
||||
bytesToHex: bytesToHex,
|
||||
mod: mod.mod,
|
||||
stringToBytes,
|
||||
stringToBytes: htf.stringToBytes,
|
||||
// TODO: do we need to export it here?
|
||||
hashToField: (msg: Uint8Array, count: number, options: Partial<typeof htfDefaults> = {}) =>
|
||||
hash_to_field(msg, count, { ...CURVE.htfDefaults, ...options }),
|
||||
hashToField: (
|
||||
msg: Uint8Array,
|
||||
count: number,
|
||||
options: Partial<typeof CURVE.htfDefaults> = {}
|
||||
) => htf.hash_to_field(msg, count, { ...CURVE.htfDefaults, ...options }),
|
||||
expandMessageXMD: (msg: Uint8Array, DST: Uint8Array, lenInBytes: number, H = CURVE.hash) =>
|
||||
expand_message_xmd(msg, DST, lenInBytes, H),
|
||||
|
||||
/**
|
||||
* Can take 40 or more bytes of uniform input e.g. from CSPRNG or KDF
|
||||
* and convert them into private key, with the modulo bias being negligible.
|
||||
* As per FIPS 186 B.1.1.
|
||||
* https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
|
||||
* @param hash hash output from sha512, or a similar function
|
||||
* @returns valid private key
|
||||
*/
|
||||
hashToPrivateKey: (hash: Hex): Uint8Array => {
|
||||
hash = ensureBytes(hash);
|
||||
if (hash.length < 40 || hash.length > 1024)
|
||||
throw new Error('Expected 40-1024 bytes of private key as per FIPS 186');
|
||||
// hashToPrivateScalar(hash, CURVE.r)
|
||||
// NOTE: doesn't add +/-1
|
||||
const num = mod.mod(bytesToNumberBE(hash), CURVE.r);
|
||||
// This should never happen
|
||||
if (num === 0n || num === 1n) throw new Error('Invalid private key');
|
||||
return numberToBytesBE(num, 32);
|
||||
},
|
||||
|
||||
randomBytes: (bytesLength: number = 32): Uint8Array => CURVE.randomBytes(bytesLength),
|
||||
// NIST SP 800-56A rev 3, section 5.6.1.2.2
|
||||
// https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
|
||||
randomPrivateKey: (): Uint8Array => utils.hashToPrivateKey(utils.randomBytes(40)),
|
||||
getDSTLabel: () => htfDefaults.DST,
|
||||
setDSTLabel(newLabel: string) {
|
||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3.1
|
||||
if (typeof newLabel !== 'string' || newLabel.length > 2048 || newLabel.length === 0) {
|
||||
throw new TypeError('Invalid DST');
|
||||
}
|
||||
htfDefaults.DST = newLabel;
|
||||
},
|
||||
htf.expand_message_xmd(msg, DST, lenInBytes, H),
|
||||
hashToPrivateKey: (hash: Hex): Uint8Array => Fr.toBytes(hashToPrivateScalar(hash, CURVE.r)),
|
||||
randomBytes: (bytesLength: number = groupLen): Uint8Array => CURVE.randomBytes(bytesLength),
|
||||
randomPrivateKey: (): Uint8Array => utils.hashToPrivateKey(utils.randomBytes(groupLen + 8)),
|
||||
};
|
||||
|
||||
function normalizePrivKey(key: PrivKey): bigint {
|
||||
let int: bigint;
|
||||
if (key instanceof Uint8Array && key.length === 32) int = bytesToNumberBE(key);
|
||||
else if (typeof key === 'string' && key.length === 64) int = BigInt(`0x${key}`);
|
||||
else if (typeof key === 'number' && key > 0 && Number.isSafeInteger(key)) int = BigInt(key);
|
||||
else if (typeof key === 'bigint' && key > 0n) int = key;
|
||||
else throw new TypeError('Expected valid private key');
|
||||
int = mod.mod(int, CURVE.r);
|
||||
if (!isWithinCurveOrder(int)) throw new Error('Private key must be 0 < key < CURVE.r');
|
||||
return int;
|
||||
}
|
||||
|
||||
// Point on G1 curve: (x, y)
|
||||
const G1 = weierstrassPoints({
|
||||
n: Fr.ORDER,
|
||||
...CURVE.G1,
|
||||
});
|
||||
const G1HashToCurve = htf.hashToCurve(G1.ProjectivePoint, CURVE.G1.mapToCurve, {
|
||||
...CURVE.htfDefaults,
|
||||
...CURVE.G1.htfDefaults,
|
||||
});
|
||||
|
||||
// Sparse multiplication against precomputed coefficients
|
||||
// TODO: replace with weakmap?
|
||||
@@ -250,83 +212,93 @@ export function bls<Fp2, Fp6, Fp12>(
|
||||
function pairingPrecomputes(point: G2): [Fp2, Fp2, Fp2][] {
|
||||
const p = point as G2 & withPairingPrecomputes;
|
||||
if (p._PPRECOMPUTES) return p._PPRECOMPUTES;
|
||||
p._PPRECOMPUTES = calcPairingPrecomputes(p.x, p.y);
|
||||
p._PPRECOMPUTES = calcPairingPrecomputes(point.toAffine());
|
||||
return p._PPRECOMPUTES;
|
||||
}
|
||||
|
||||
function clearPairingPrecomputes(point: G2) {
|
||||
const p = point as G2 & withPairingPrecomputes;
|
||||
p._PPRECOMPUTES = undefined;
|
||||
}
|
||||
clearPairingPrecomputes;
|
||||
|
||||
function millerLoopG1(Q: G1, P: G2): Fp12 {
|
||||
return millerLoop(pairingPrecomputes(P), [Q.x, Q.y]);
|
||||
}
|
||||
// TODO: export
|
||||
// function clearPairingPrecomputes(point: G2) {
|
||||
// const p = point as G2 & withPairingPrecomputes;
|
||||
// p._PPRECOMPUTES = undefined;
|
||||
// }
|
||||
|
||||
// Point on G2 curve (complex numbers): (x₁, x₂+i), (y₁, y₂+i)
|
||||
const G2 = weierstrassPoints({
|
||||
n: Fr.ORDER,
|
||||
...CURVE.G2,
|
||||
});
|
||||
const C = G2.ProjectivePoint as htf.H2CPointConstructor<Fp2>; // TODO: fix
|
||||
const G2HashToCurve = htf.hashToCurve(C, CURVE.G2.mapToCurve, {
|
||||
...CURVE.htfDefaults,
|
||||
...CURVE.G2.htfDefaults,
|
||||
});
|
||||
|
||||
const { Signature } = CURVE.G2;
|
||||
|
||||
// Calculates bilinear pairing
|
||||
function pairing(P: G1, Q: G2, withFinalExponent: boolean = true): Fp12 {
|
||||
if (P.equals(G1.Point.ZERO) || Q.equals(G2.Point.ZERO))
|
||||
throw new Error('No pairings at point of Infinity');
|
||||
P.assertValidity();
|
||||
function pairing(Q: G1, P: G2, withFinalExponent: boolean = true): Fp12 {
|
||||
if (Q.equals(G1.ProjectivePoint.ZERO) || P.equals(G2.ProjectivePoint.ZERO))
|
||||
throw new Error('pairing is not available for ZERO point');
|
||||
Q.assertValidity();
|
||||
P.assertValidity();
|
||||
// Performance: 9ms for millerLoop and ~14ms for exp.
|
||||
const looped = millerLoopG1(P, Q);
|
||||
const Qa = Q.toAffine();
|
||||
const looped = millerLoop(pairingPrecomputes(P), [Qa.x, Qa.y]);
|
||||
return withFinalExponent ? Fp12.finalExponentiate(looped) : looped;
|
||||
}
|
||||
type G1 = typeof G1.Point.BASE;
|
||||
type G2 = typeof G2.Point.BASE;
|
||||
type G1 = typeof G1.ProjectivePoint.BASE;
|
||||
type G2 = typeof G2.ProjectivePoint.BASE;
|
||||
|
||||
type G1Hex = Hex | G1;
|
||||
type G2Hex = Hex | G2;
|
||||
function normP1(point: G1Hex): G1 {
|
||||
return point instanceof G1.Point ? (point as G1) : G1.Point.fromHex(point);
|
||||
return point instanceof G1.ProjectivePoint ? (point as G1) : G1.ProjectivePoint.fromHex(point);
|
||||
}
|
||||
function normP2(point: G2Hex): G2 {
|
||||
return point instanceof G2.Point ? point : Signature.decode(point);
|
||||
return point instanceof G2.ProjectivePoint ? point : Signature.decode(point);
|
||||
}
|
||||
function normP2Hash(point: G2Hex): G2 {
|
||||
return point instanceof G2.Point ? point : G2.Point.hashToCurve(point);
|
||||
function normP2Hash(point: G2Hex, htfOpts?: htf.htfBasicOpts): G2 {
|
||||
return point instanceof G2.ProjectivePoint
|
||||
? point
|
||||
: (G2HashToCurve.hashToCurve(point, htfOpts) as G2);
|
||||
}
|
||||
|
||||
// Multiplies generator by private key.
|
||||
// P = pk x G
|
||||
function getPublicKey(privateKey: PrivKey): Uint8Array {
|
||||
return G1.Point.fromPrivateKey(privateKey).toRawBytes(true);
|
||||
return G1.ProjectivePoint.fromPrivateKey(privateKey).toRawBytes(true);
|
||||
}
|
||||
|
||||
// Executes `hashToCurve` on the message and then multiplies the result by private key.
|
||||
// S = pk x H(m)
|
||||
function sign(message: Hex, privateKey: PrivKey): Uint8Array;
|
||||
function sign(message: G2, privateKey: PrivKey): G2;
|
||||
function sign(message: G2Hex, privateKey: PrivKey): Uint8Array | G2 {
|
||||
const msgPoint = normP2Hash(message);
|
||||
function sign(message: Hex, privateKey: PrivKey, htfOpts?: htf.htfBasicOpts): Uint8Array;
|
||||
function sign(message: G2, privateKey: PrivKey, htfOpts?: htf.htfBasicOpts): G2;
|
||||
function sign(message: G2Hex, privateKey: PrivKey, htfOpts?: htf.htfBasicOpts): Uint8Array | G2 {
|
||||
const msgPoint = normP2Hash(message, htfOpts);
|
||||
msgPoint.assertValidity();
|
||||
const sigPoint = msgPoint.multiply(normalizePrivKey(privateKey));
|
||||
if (message instanceof G2.Point) return sigPoint;
|
||||
const sigPoint = msgPoint.multiply(G1.normalizePrivateKey(privateKey));
|
||||
if (message instanceof G2.ProjectivePoint) return sigPoint;
|
||||
return Signature.encode(sigPoint);
|
||||
}
|
||||
|
||||
// Checks if pairing of public key & hash is equal to pairing of generator & signature.
|
||||
// e(P, H(m)) == e(G, S)
|
||||
function verify(signature: G2Hex, message: G2Hex, publicKey: G1Hex): boolean {
|
||||
function verify(
|
||||
signature: G2Hex,
|
||||
message: G2Hex,
|
||||
publicKey: G1Hex,
|
||||
htfOpts?: htf.htfBasicOpts
|
||||
): boolean {
|
||||
const P = normP1(publicKey);
|
||||
const Hm = normP2Hash(message);
|
||||
const G = G1.Point.BASE;
|
||||
const Hm = normP2Hash(message, htfOpts);
|
||||
const G = G1.ProjectivePoint.BASE;
|
||||
const S = normP2(signature);
|
||||
// Instead of doing 2 exponentiations, we use property of billinear maps
|
||||
// and do one exp after multiplying 2 points.
|
||||
const ePHm = pairing(P.negate(), Hm, false);
|
||||
const eGS = pairing(G, S, false);
|
||||
const exp = Fp12.finalExponentiate(Fp12.mul(eGS, ePHm));
|
||||
return Fp12.equals(exp, Fp12.ONE);
|
||||
return Fp12.eql(exp, Fp12.ONE);
|
||||
}
|
||||
|
||||
// Adds a bunch of public key points together.
|
||||
@@ -335,11 +307,9 @@ export function bls<Fp2, Fp6, Fp12>(
|
||||
function aggregatePublicKeys(publicKeys: G1[]): G1;
|
||||
function aggregatePublicKeys(publicKeys: G1Hex[]): Uint8Array | G1 {
|
||||
if (!publicKeys.length) throw new Error('Expected non-empty array');
|
||||
const agg = publicKeys
|
||||
.map(normP1)
|
||||
.reduce((sum, p) => sum.add(G1.ProjectivePoint.fromAffine(p)), G1.ProjectivePoint.ZERO);
|
||||
const aggAffine = agg.toAffine();
|
||||
if (publicKeys[0] instanceof G1.Point) {
|
||||
const agg = publicKeys.map(normP1).reduce((sum, p) => sum.add(p), G1.ProjectivePoint.ZERO);
|
||||
const aggAffine = agg; //.toAffine();
|
||||
if (publicKeys[0] instanceof G1.ProjectivePoint) {
|
||||
aggAffine.assertValidity();
|
||||
return aggAffine;
|
||||
}
|
||||
@@ -352,11 +322,9 @@ export function bls<Fp2, Fp6, Fp12>(
|
||||
function aggregateSignatures(signatures: G2[]): G2;
|
||||
function aggregateSignatures(signatures: G2Hex[]): Uint8Array | G2 {
|
||||
if (!signatures.length) throw new Error('Expected non-empty array');
|
||||
const agg = signatures
|
||||
.map(normP2)
|
||||
.reduce((sum, s) => sum.add(G2.ProjectivePoint.fromAffine(s)), G2.ProjectivePoint.ZERO);
|
||||
const aggAffine = agg.toAffine();
|
||||
if (signatures[0] instanceof G2.Point) {
|
||||
const agg = signatures.map(normP2).reduce((sum, s) => sum.add(s), G2.ProjectivePoint.ZERO);
|
||||
const aggAffine = agg; //.toAffine();
|
||||
if (signatures[0] instanceof G2.ProjectivePoint) {
|
||||
aggAffine.assertValidity();
|
||||
return aggAffine;
|
||||
}
|
||||
@@ -365,12 +333,20 @@ export function bls<Fp2, Fp6, Fp12>(
|
||||
|
||||
// https://ethresear.ch/t/fast-verification-of-multiple-bls-signatures/5407
|
||||
// e(G, S) = e(G, SUM(n)(Si)) = MUL(n)(e(G, Si))
|
||||
function verifyBatch(signature: G2Hex, messages: G2Hex[], publicKeys: G1Hex[]): boolean {
|
||||
function verifyBatch(
|
||||
signature: G2Hex,
|
||||
messages: G2Hex[],
|
||||
publicKeys: G1Hex[],
|
||||
htfOpts?: htf.htfBasicOpts
|
||||
): boolean {
|
||||
// @ts-ignore
|
||||
// console.log('verifyBatch', bytesToHex(signature as any), messages, publicKeys.map(bytesToHex));
|
||||
|
||||
if (!messages.length) throw new Error('Expected non-empty messages array');
|
||||
if (publicKeys.length !== messages.length)
|
||||
throw new Error('Pubkey count should equal msg count');
|
||||
const sig = normP2(signature);
|
||||
const nMessages = messages.map(normP2Hash);
|
||||
const nMessages = messages.map((i) => normP2Hash(i, htfOpts));
|
||||
const nPublicKeys = publicKeys.map(normP1);
|
||||
try {
|
||||
const paired = [];
|
||||
@@ -378,23 +354,23 @@ export function bls<Fp2, Fp6, Fp12>(
|
||||
const groupPublicKey = nMessages.reduce(
|
||||
(groupPublicKey, subMessage, i) =>
|
||||
subMessage === message ? groupPublicKey.add(nPublicKeys[i]) : groupPublicKey,
|
||||
G1.Point.ZERO
|
||||
G1.ProjectivePoint.ZERO
|
||||
);
|
||||
// const msg = message instanceof PointG2 ? message : await PointG2.hashToCurve(message);
|
||||
// Possible to batch pairing for same msg with different groupPublicKey here
|
||||
paired.push(pairing(groupPublicKey, message, false));
|
||||
}
|
||||
paired.push(pairing(G1.Point.BASE.negate(), sig, false));
|
||||
paired.push(pairing(G1.ProjectivePoint.BASE.negate(), sig, false));
|
||||
const product = paired.reduce((a, b) => Fp12.mul(a, b), Fp12.ONE);
|
||||
const exp = Fp12.finalExponentiate(product);
|
||||
return Fp12.equals(exp, Fp12.ONE);
|
||||
return Fp12.eql(exp, Fp12.ONE);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Pre-compute points. Refer to README.
|
||||
G1.Point.BASE._setWindowSize(4);
|
||||
G1.ProjectivePoint.BASE._setWindowSize(4);
|
||||
|
||||
return {
|
||||
CURVE,
|
||||
Fr,
|
||||
@@ -407,6 +383,7 @@ export function bls<Fp2, Fp6, Fp12>(
|
||||
Signature,
|
||||
millerLoop,
|
||||
calcPairingPrecomputes,
|
||||
hashToCurve: { G1: G1HashToCurve, G2: G2HashToCurve },
|
||||
pairing,
|
||||
getPublicKey,
|
||||
sign,
|
||||
|
||||
@@ -1,22 +1,32 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
// Abelian group utilities
|
||||
import { Field, validateField, nLength } from './modular.js';
|
||||
import { validateObject } from './utils.js';
|
||||
const _0n = BigInt(0);
|
||||
const _1n = BigInt(1);
|
||||
|
||||
export type AffinePoint<T> = {
|
||||
x: T;
|
||||
y: T;
|
||||
} & { z?: never; t?: never };
|
||||
|
||||
export interface Group<T extends Group<T>> {
|
||||
double(): T;
|
||||
negate(): T;
|
||||
add(other: T): T;
|
||||
subtract(other: T): T;
|
||||
equals(other: T): boolean;
|
||||
multiply(scalar: number | bigint): T;
|
||||
multiply(scalar: bigint): T;
|
||||
}
|
||||
|
||||
export type GroupConstructor<T> = {
|
||||
BASE: T;
|
||||
ZERO: T;
|
||||
};
|
||||
// Not big, but pretty complex and it is easy to break stuff. To avoid too much copy paste
|
||||
export type Mapper<T> = (i: T[]) => T[];
|
||||
|
||||
// Elliptic curve multiplication of Point by scalar. Complicated and fragile. Uses wNAF method.
|
||||
// Windowed method is 10% faster, but takes 2x longer to generate & consumes 2x memory.
|
||||
export function wNAF<T extends Group<T>>(c: GroupConstructor<T>, bits: number) {
|
||||
const constTimeNegate = (condition: boolean, item: T): T => {
|
||||
const neg = item.negate();
|
||||
@@ -125,5 +135,52 @@ export function wNAF<T extends Group<T>>(c: GroupConstructor<T>, bits: number) {
|
||||
// which makes it less const-time: around 1 bigint multiply.
|
||||
return { p, f };
|
||||
},
|
||||
|
||||
wNAFCached(P: T, precomputesMap: Map<T, T[]>, n: bigint, transform: Mapper<T>): { p: T; f: T } {
|
||||
// @ts-ignore
|
||||
const W: number = P._WINDOW_SIZE || 1;
|
||||
// Calculate precomputes on a first run, reuse them after
|
||||
let comp = precomputesMap.get(P);
|
||||
if (!comp) {
|
||||
comp = this.precomputeWindow(P, W) as T[];
|
||||
if (W !== 1) {
|
||||
precomputesMap.set(P, transform(comp));
|
||||
}
|
||||
}
|
||||
return this.wNAF(W, comp, n);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Generic BasicCurve interface: works even for polynomial fields (BLS): P, n, h would be ok.
|
||||
// Though generator can be different (Fp2 / Fp6 for BLS).
|
||||
export type BasicCurve<T> = {
|
||||
Fp: Field<T>; // Field over which we'll do calculations (Fp)
|
||||
n: bigint; // Curve order, total count of valid points in the field
|
||||
nBitLength?: number; // bit length of curve order
|
||||
nByteLength?: number; // byte length of curve order
|
||||
h: bigint; // cofactor. we can assign default=1, but users will just ignore it w/o validation
|
||||
hEff?: bigint; // Number to multiply to clear cofactor
|
||||
Gx: T; // base point X coordinate
|
||||
Gy: T; // base point Y coordinate
|
||||
allowInfinityPoint?: boolean; // bls12-381 requires it. ZERO point is valid, but invalid pubkey
|
||||
};
|
||||
|
||||
export function validateBasic<FP, T>(curve: BasicCurve<FP> & T) {
|
||||
validateField(curve.Fp);
|
||||
validateObject(
|
||||
curve,
|
||||
{
|
||||
n: 'bigint',
|
||||
h: 'bigint',
|
||||
Gx: 'field',
|
||||
Gy: 'field',
|
||||
},
|
||||
{
|
||||
nBitLength: 'isSafeInteger',
|
||||
nByteLength: 'isSafeInteger',
|
||||
}
|
||||
);
|
||||
// Set defaults
|
||||
return Object.freeze({ ...nLength(curve.n, curve.nBitLength), ...curve } as const);
|
||||
}
|
||||
@@ -1,29 +1,9 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
// Twisted Edwards curve. The formula is: ax² + y² = 1 + dx²y²
|
||||
|
||||
// Differences from @noble/ed25519 1.7:
|
||||
// 1. Different field element lengths in ed448:
|
||||
// EDDSA (RFC8032) is 456 bits / 57 bytes, ECDH (RFC7748) is 448 bits / 56 bytes
|
||||
// 2. Different addition formula (doubling is same)
|
||||
// 3. uvRatio differs between curves (half-expected, not only pow fn changes)
|
||||
// 4. Point decompression code is different too (unexpected), now using generalized formula
|
||||
// 5. Domain function was no-op for ed25519, but adds some data even with empty context for ed448
|
||||
|
||||
import * as mod from './modular.js';
|
||||
import {
|
||||
bytesToHex,
|
||||
concatBytes,
|
||||
ensureBytes,
|
||||
numberToBytesLE,
|
||||
bytesToNumberLE,
|
||||
hashToPrivateScalar,
|
||||
BasicCurve,
|
||||
validateOpts as utilOpts,
|
||||
Hex,
|
||||
PrivKey,
|
||||
} from './utils.js'; // TODO: import * as u from './utils.js'?
|
||||
import { Group, GroupConstructor, wNAF } from './group.js';
|
||||
import { hash_to_field, htfOpts, validateHTFOpts } from './hash-to-curve.js';
|
||||
import { mod } from './modular.js';
|
||||
import * as ut from './utils.js';
|
||||
import { ensureBytes, FHash, Hex } from './utils.js';
|
||||
import { Group, GroupConstructor, wNAF, BasicCurve, validateBasic, AffinePoint } from './curve.js';
|
||||
|
||||
// Be friendly to bad ECMAScript parsers by not using bigint literals like 123n
|
||||
const _0n = BigInt(0);
|
||||
@@ -31,204 +11,193 @@ const _1n = BigInt(1);
|
||||
const _2n = BigInt(2);
|
||||
const _8n = BigInt(8);
|
||||
|
||||
export type CHash = {
|
||||
(message: Uint8Array | string): Uint8Array;
|
||||
blockLen: number;
|
||||
outputLen: number;
|
||||
create(): any;
|
||||
};
|
||||
|
||||
// Edwards curves must declare params a & d.
|
||||
export type CurveType = BasicCurve<bigint> & {
|
||||
// Params: a, d
|
||||
a: bigint;
|
||||
d: bigint;
|
||||
// Hashes
|
||||
hash: CHash; // Because we need outputLen for DRBG
|
||||
randomBytes: (bytesLength?: number) => Uint8Array;
|
||||
adjustScalarBytes?: (bytes: Uint8Array) => Uint8Array;
|
||||
domain?: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array;
|
||||
uvRatio?: (u: bigint, v: bigint) => { isValid: boolean; value: bigint };
|
||||
preHash?: CHash;
|
||||
clearCofactor?: (c: ExtendedPointConstructor, point: ExtendedPointType) => ExtendedPointType;
|
||||
// Hash to field opts
|
||||
htfDefaults?: htfOpts;
|
||||
mapToCurve?: (scalar: bigint[]) => { x: bigint; y: bigint };
|
||||
a: bigint; // curve param a
|
||||
d: bigint; // curve param d
|
||||
hash: FHash; // Hashing
|
||||
randomBytes: (bytesLength?: number) => Uint8Array; // CSPRNG
|
||||
adjustScalarBytes?: (bytes: Uint8Array) => Uint8Array; // clears bits to get valid field elemtn
|
||||
domain?: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array; // Used for hashing
|
||||
uvRatio?: (u: bigint, v: bigint) => { isValid: boolean; value: bigint }; // Ratio √(u/v)
|
||||
preHash?: FHash; // RFC 8032 pre-hashing of messages to sign() / verify()
|
||||
mapToCurve?: (scalar: bigint[]) => AffinePoint<bigint>; // for hash-to-curve standard
|
||||
};
|
||||
|
||||
// Should be separate from overrides, since overrides can use information about curve (for example nBits)
|
||||
function validateOpts(curve: CurveType) {
|
||||
const opts = utilOpts(curve);
|
||||
if (typeof opts.hash !== 'function' || !Number.isSafeInteger(opts.hash.outputLen))
|
||||
throw new Error('Invalid hash function');
|
||||
for (const i of ['a', 'd'] as const) {
|
||||
if (typeof opts[i] !== 'bigint')
|
||||
throw new Error(`Invalid curve param ${i}=${opts[i]} (${typeof opts[i]})`);
|
||||
}
|
||||
for (const fn of ['randomBytes'] as const) {
|
||||
if (typeof opts[fn] !== 'function') throw new Error(`Invalid ${fn} function`);
|
||||
}
|
||||
for (const fn of [
|
||||
'adjustScalarBytes',
|
||||
'domain',
|
||||
'uvRatio',
|
||||
'mapToCurve',
|
||||
'clearCofactor',
|
||||
] as const) {
|
||||
if (opts[fn] === undefined) continue; // Optional
|
||||
if (typeof opts[fn] !== 'function') throw new Error(`Invalid ${fn} function`);
|
||||
}
|
||||
if (opts.htfDefaults !== undefined) validateHTFOpts(opts.htfDefaults);
|
||||
const opts = validateBasic(curve);
|
||||
ut.validateObject(
|
||||
curve,
|
||||
{
|
||||
hash: 'function',
|
||||
a: 'bigint',
|
||||
d: 'bigint',
|
||||
randomBytes: 'function',
|
||||
},
|
||||
{
|
||||
adjustScalarBytes: 'function',
|
||||
domain: 'function',
|
||||
uvRatio: 'function',
|
||||
mapToCurve: 'function',
|
||||
}
|
||||
);
|
||||
// Set defaults
|
||||
return Object.freeze({ ...opts } as const);
|
||||
}
|
||||
|
||||
// Instance
|
||||
export interface SignatureType {
|
||||
readonly r: PointType;
|
||||
readonly s: bigint;
|
||||
assertValidity(): SignatureType;
|
||||
toRawBytes(): Uint8Array;
|
||||
toHex(): string;
|
||||
}
|
||||
// Static methods
|
||||
export type SignatureConstructor = {
|
||||
new (r: PointType, s: bigint): SignatureType;
|
||||
fromHex(hex: Hex): SignatureType;
|
||||
};
|
||||
|
||||
// Instance
|
||||
export interface ExtendedPointType extends Group<ExtendedPointType> {
|
||||
readonly x: bigint;
|
||||
readonly y: bigint;
|
||||
readonly z: bigint;
|
||||
readonly t: bigint;
|
||||
multiply(scalar: number | bigint, affinePoint?: PointType): ExtendedPointType;
|
||||
multiplyUnsafe(scalar: number | bigint): ExtendedPointType;
|
||||
// Instance of Extended Point with coordinates in X, Y, Z, T
|
||||
export interface ExtPointType extends Group<ExtPointType> {
|
||||
readonly ex: bigint;
|
||||
readonly ey: bigint;
|
||||
readonly ez: bigint;
|
||||
readonly et: bigint;
|
||||
assertValidity(): void;
|
||||
multiply(scalar: bigint): ExtPointType;
|
||||
multiplyUnsafe(scalar: bigint): ExtPointType;
|
||||
isSmallOrder(): boolean;
|
||||
isTorsionFree(): boolean;
|
||||
toAffine(invZ?: bigint): PointType;
|
||||
clearCofactor(): ExtendedPointType;
|
||||
clearCofactor(): ExtPointType;
|
||||
toAffine(iz?: bigint): AffinePoint<bigint>;
|
||||
}
|
||||
// Static methods
|
||||
export interface ExtendedPointConstructor extends GroupConstructor<ExtendedPointType> {
|
||||
new (x: bigint, y: bigint, z: bigint, t: bigint): ExtendedPointType;
|
||||
fromAffine(p: PointType): ExtendedPointType;
|
||||
toAffineBatch(points: ExtendedPointType[]): PointType[];
|
||||
normalizeZ(points: ExtendedPointType[]): ExtendedPointType[];
|
||||
// Static methods of Extended Point with coordinates in X, Y, Z, T
|
||||
export interface ExtPointConstructor extends GroupConstructor<ExtPointType> {
|
||||
new (x: bigint, y: bigint, z: bigint, t: bigint): ExtPointType;
|
||||
fromAffine(p: AffinePoint<bigint>): ExtPointType;
|
||||
fromHex(hex: Hex): ExtPointType;
|
||||
fromPrivateKey(privateKey: Hex): ExtPointType;
|
||||
}
|
||||
|
||||
// Instance
|
||||
export interface PointType extends Group<PointType> {
|
||||
readonly x: bigint;
|
||||
readonly y: bigint;
|
||||
_setWindowSize(windowSize: number): void;
|
||||
toRawBytes(isCompressed?: boolean): Uint8Array;
|
||||
toHex(isCompressed?: boolean): string;
|
||||
isTorsionFree(): boolean;
|
||||
clearCofactor(): PointType;
|
||||
}
|
||||
// Static methods
|
||||
export interface PointConstructor extends GroupConstructor<PointType> {
|
||||
new (x: bigint, y: bigint): PointType;
|
||||
fromHex(hex: Hex): PointType;
|
||||
fromPrivateKey(privateKey: PrivKey): PointType;
|
||||
hashToCurve(msg: Hex, options?: Partial<htfOpts>): PointType;
|
||||
encodeToCurve(msg: Hex, options?: Partial<htfOpts>): PointType;
|
||||
}
|
||||
|
||||
export type PubKey = Hex | PointType;
|
||||
export type SigType = Hex | SignatureType;
|
||||
|
||||
export type CurveFn = {
|
||||
CURVE: ReturnType<typeof validateOpts>;
|
||||
getPublicKey: (privateKey: PrivKey, isCompressed?: boolean) => Uint8Array;
|
||||
getPublicKey: (privateKey: Hex) => Uint8Array;
|
||||
sign: (message: Hex, privateKey: Hex) => Uint8Array;
|
||||
verify: (sig: SigType, message: Hex, publicKey: PubKey) => boolean;
|
||||
Point: PointConstructor;
|
||||
ExtendedPoint: ExtendedPointConstructor;
|
||||
Signature: SignatureConstructor;
|
||||
verify: (sig: Hex, message: Hex, publicKey: Hex) => boolean;
|
||||
ExtendedPoint: ExtPointConstructor;
|
||||
utils: {
|
||||
mod: (a: bigint) => bigint;
|
||||
invert: (number: bigint) => bigint;
|
||||
randomPrivateKey: () => Uint8Array;
|
||||
getExtendedPublicKey: (key: PrivKey) => {
|
||||
getExtendedPublicKey: (key: Hex) => {
|
||||
head: Uint8Array;
|
||||
prefix: Uint8Array;
|
||||
scalar: bigint;
|
||||
point: PointType;
|
||||
point: ExtPointType;
|
||||
pointBytes: Uint8Array;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
// NOTE: it is not generic twisted curve for now, but ed25519/ed448 generic implementation
|
||||
// It is not generic twisted curve for now, but ed25519/ed448 generic implementation
|
||||
export function twistedEdwards(curveDef: CurveType): CurveFn {
|
||||
const CURVE = validateOpts(curveDef) as ReturnType<typeof validateOpts>;
|
||||
const Fp = CURVE.Fp as mod.Field<bigint>;
|
||||
const CURVE_ORDER = CURVE.n;
|
||||
const fieldLen = Fp.BYTES; // 32 (length of one field element)
|
||||
if (fieldLen > 2048) throw new Error('Field lengths over 2048 are not supported');
|
||||
const groupLen = CURVE.nByteLength;
|
||||
// (2n ** 256n).toString(16);
|
||||
const maxGroupElement = _2n ** BigInt(groupLen * 8); // previous POW_2_256
|
||||
|
||||
// Function overrides
|
||||
const { randomBytes } = CURVE;
|
||||
const modP = Fp.create;
|
||||
const { Fp, n: CURVE_ORDER, preHash, hash: cHash, randomBytes, nByteLength, h: cofactor } = CURVE;
|
||||
const MASK = _2n ** BigInt(nByteLength * 8);
|
||||
const modP = Fp.create; // Function overrides
|
||||
|
||||
// sqrt(u/v)
|
||||
function _uvRatio(u: bigint, v: bigint) {
|
||||
try {
|
||||
const value = Fp.sqrt(u * Fp.invert(v));
|
||||
return { isValid: true, value };
|
||||
} catch (e) {
|
||||
return { isValid: false, value: _0n };
|
||||
}
|
||||
}
|
||||
const uvRatio = CURVE.uvRatio || _uvRatio;
|
||||
|
||||
const _adjustScalarBytes = (bytes: Uint8Array) => bytes; // NOOP
|
||||
const adjustScalarBytes = CURVE.adjustScalarBytes || _adjustScalarBytes;
|
||||
function _domain(data: Uint8Array, ctx: Uint8Array, phflag: boolean) {
|
||||
if (ctx.length || phflag) throw new Error('Contexts/pre-hash are not supported');
|
||||
return data;
|
||||
}
|
||||
const domain = CURVE.domain || _domain; // NOOP
|
||||
|
||||
/**
|
||||
* Extended Point works in extended coordinates: (x, y, z, t) ∋ (x=x/z, y=y/z, t=xy).
|
||||
* Default Point works in affine coordinates: (x, y)
|
||||
* https://en.wikipedia.org/wiki/Twisted_Edwards_curve#Extended_coordinates
|
||||
*/
|
||||
class ExtendedPoint implements ExtendedPointType {
|
||||
constructor(readonly x: bigint, readonly y: bigint, readonly z: bigint, readonly t: bigint) {}
|
||||
|
||||
static BASE = new ExtendedPoint(CURVE.Gx, CURVE.Gy, _1n, modP(CURVE.Gx * CURVE.Gy));
|
||||
static ZERO = new ExtendedPoint(_0n, _1n, _1n, _0n);
|
||||
static fromAffine(p: Point): ExtendedPoint {
|
||||
if (!(p instanceof Point)) {
|
||||
throw new TypeError('ExtendedPoint#fromAffine: expected Point');
|
||||
const uvRatio =
|
||||
CURVE.uvRatio ||
|
||||
((u: bigint, v: bigint) => {
|
||||
try {
|
||||
return { isValid: true, value: Fp.sqrt(u * Fp.inv(v)) };
|
||||
} catch (e) {
|
||||
return { isValid: false, value: _0n };
|
||||
}
|
||||
if (p.equals(Point.ZERO)) return ExtendedPoint.ZERO;
|
||||
return new ExtendedPoint(p.x, p.y, _1n, modP(p.x * p.y));
|
||||
}
|
||||
// Takes a bunch of Jacobian Points but executes only one
|
||||
// invert on all of them. invert is very slow operation,
|
||||
// so this improves performance massively.
|
||||
static toAffineBatch(points: ExtendedPoint[]): Point[] {
|
||||
const toInv = Fp.invertBatch(points.map((p) => p.z));
|
||||
return points.map((p, i) => p.toAffine(toInv[i]));
|
||||
});
|
||||
const adjustScalarBytes = CURVE.adjustScalarBytes || ((bytes: Uint8Array) => bytes); // NOOP
|
||||
const domain =
|
||||
CURVE.domain ||
|
||||
((data: Uint8Array, ctx: Uint8Array, phflag: boolean) => {
|
||||
if (ctx.length || phflag) throw new Error('Contexts/pre-hash are not supported');
|
||||
return data;
|
||||
}); // NOOP
|
||||
const inBig = (n: bigint) => typeof n === 'bigint' && 0n < n; // n in [1..]
|
||||
const inRange = (n: bigint, max: bigint) => inBig(n) && inBig(max) && n < max; // n in [1..max-1]
|
||||
const in0MaskRange = (n: bigint) => n === _0n || inRange(n, MASK); // n in [0..MASK-1]
|
||||
function assertInRange(n: bigint, max: bigint) {
|
||||
// n in [1..max-1]
|
||||
if (inRange(n, max)) return n;
|
||||
throw new Error(`Expected valid scalar < ${max}, got ${typeof n} ${n}`);
|
||||
}
|
||||
function assertGE0(n: bigint) {
|
||||
// n in [0..CURVE_ORDER-1]
|
||||
return n === _0n ? n : assertInRange(n, CURVE_ORDER); // GE = prime subgroup, not full group
|
||||
}
|
||||
const pointPrecomputes = new Map<Point, Point[]>();
|
||||
function isPoint(other: unknown) {
|
||||
if (!(other instanceof Point)) throw new Error('ExtendedPoint expected');
|
||||
}
|
||||
// Extended Point works in extended coordinates: (x, y, z, t) ∋ (x=x/z, y=y/z, t=xy).
|
||||
// https://en.wikipedia.org/wiki/Twisted_Edwards_curve#Extended_coordinates
|
||||
class Point implements ExtPointType {
|
||||
static readonly BASE = new Point(CURVE.Gx, CURVE.Gy, _1n, modP(CURVE.Gx * CURVE.Gy));
|
||||
static readonly ZERO = new Point(_0n, _1n, _1n, _0n); // 0, 1, 1, 0
|
||||
|
||||
constructor(
|
||||
readonly ex: bigint,
|
||||
readonly ey: bigint,
|
||||
readonly ez: bigint,
|
||||
readonly et: bigint
|
||||
) {
|
||||
if (!in0MaskRange(ex)) throw new Error('x required');
|
||||
if (!in0MaskRange(ey)) throw new Error('y required');
|
||||
if (!in0MaskRange(ez)) throw new Error('z required');
|
||||
if (!in0MaskRange(et)) throw new Error('t required');
|
||||
}
|
||||
|
||||
static normalizeZ(points: ExtendedPoint[]): ExtendedPoint[] {
|
||||
return this.toAffineBatch(points).map(this.fromAffine);
|
||||
get x(): bigint {
|
||||
return this.toAffine().x;
|
||||
}
|
||||
get y(): bigint {
|
||||
return this.toAffine().y;
|
||||
}
|
||||
|
||||
static fromAffine(p: AffinePoint<bigint>): Point {
|
||||
if (p instanceof Point) throw new Error('extended point not allowed');
|
||||
const { x, y } = p || {};
|
||||
if (!in0MaskRange(x) || !in0MaskRange(y)) throw new Error('invalid affine point');
|
||||
return new Point(x, y, _1n, modP(x * y));
|
||||
}
|
||||
static normalizeZ(points: Point[]): Point[] {
|
||||
const toInv = Fp.invertBatch(points.map((p) => p.ez));
|
||||
return points.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine);
|
||||
}
|
||||
|
||||
// We calculate precomputes for elliptic curve point multiplication
|
||||
// using windowed method. This specifies window size and
|
||||
// stores precomputed values. Usually only base point would be precomputed.
|
||||
_WINDOW_SIZE?: number;
|
||||
|
||||
// "Private method", don't use it directly
|
||||
_setWindowSize(windowSize: number) {
|
||||
this._WINDOW_SIZE = windowSize;
|
||||
pointPrecomputes.delete(this);
|
||||
}
|
||||
// Not required for fromHex(), which always creates valid points.
|
||||
// Could be useful for fromAffine().
|
||||
assertValidity(): void {
|
||||
const { a, d } = CURVE;
|
||||
if (this.is0()) throw new Error('bad point: ZERO'); // TODO: optimize, with vars below?
|
||||
// Equation in affine coordinates: ax² + y² = 1 + dx²y²
|
||||
// Equation in projective coordinates (X/Z, Y/Z, Z): (aX² + Y²)Z² = Z⁴ + dX²Y²
|
||||
const { ex: X, ey: Y, ez: Z, et: T } = this;
|
||||
const X2 = modP(X * X); // X²
|
||||
const Y2 = modP(Y * Y); // Y²
|
||||
const Z2 = modP(Z * Z); // Z²
|
||||
const Z4 = modP(Z2 * Z2); // Z⁴
|
||||
const aX2 = modP(X2 * a); // aX²
|
||||
const left = modP(Z2 * modP(aX2 + Y2)); // (aX² + Y²)Z²
|
||||
const right = modP(Z4 + modP(d * modP(X2 * Y2))); // Z⁴ + dX²Y²
|
||||
if (left !== right) throw new Error('bad point: equation left != right (1)');
|
||||
// In Extended coordinates we also have T, which is x*y=T/Z: check X*Y == Z*T
|
||||
const XY = modP(X * Y);
|
||||
const ZT = modP(Z * T);
|
||||
if (XY !== ZT) throw new Error('bad point: equation left != right (2)');
|
||||
}
|
||||
|
||||
// Compare one point to another.
|
||||
equals(other: ExtendedPoint): boolean {
|
||||
assertExtPoint(other);
|
||||
const { x: X1, y: Y1, z: Z1 } = this;
|
||||
const { x: X2, y: Y2, z: Z2 } = other;
|
||||
equals(other: Point): boolean {
|
||||
isPoint(other);
|
||||
const { ex: X1, ey: Y1, ez: Z1 } = this;
|
||||
const { ex: X2, ey: Y2, ez: Z2 } = other;
|
||||
const X1Z2 = modP(X1 * Z2);
|
||||
const X2Z1 = modP(X2 * Z1);
|
||||
const Y1Z2 = modP(Y1 * Z2);
|
||||
@@ -236,17 +205,21 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
||||
return X1Z2 === X2Z1 && Y1Z2 === Y2Z1;
|
||||
}
|
||||
|
||||
// Inverses point to one corresponding to (x, -y) in Affine coordinates.
|
||||
negate(): ExtendedPoint {
|
||||
return new ExtendedPoint(modP(-this.x), this.y, this.z, modP(-this.t));
|
||||
protected is0(): boolean {
|
||||
return this.equals(Point.ZERO);
|
||||
}
|
||||
|
||||
negate(): Point {
|
||||
// Flips point sign to a negative one (-x, y in affine coords)
|
||||
return new Point(modP(-this.ex), this.ey, this.ez, modP(-this.et));
|
||||
}
|
||||
|
||||
// Fast algo for doubling Extended Point.
|
||||
// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd
|
||||
// Cost: 4M + 4S + 1*a + 6add + 1*2.
|
||||
double(): ExtendedPoint {
|
||||
double(): Point {
|
||||
const { a } = CURVE;
|
||||
const { x: X1, y: Y1, z: Z1 } = this;
|
||||
const { ex: X1, ey: Y1, ez: Z1 } = this;
|
||||
const A = modP(X1 * X1); // A = X12
|
||||
const B = modP(Y1 * Y1); // B = Y12
|
||||
const C = modP(_2n * modP(Z1 * Z1)); // C = 2*Z12
|
||||
@@ -260,17 +233,17 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
||||
const Y3 = modP(G * H); // Y3 = G*H
|
||||
const T3 = modP(E * H); // T3 = E*H
|
||||
const Z3 = modP(F * G); // Z3 = F*G
|
||||
return new ExtendedPoint(X3, Y3, Z3, T3);
|
||||
return new Point(X3, Y3, Z3, T3);
|
||||
}
|
||||
|
||||
// Fast algo for adding 2 Extended Points.
|
||||
// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#addition-add-2008-hwcd
|
||||
// Cost: 9M + 1*a + 1*d + 7add.
|
||||
add(other: ExtendedPoint) {
|
||||
assertExtPoint(other);
|
||||
add(other: Point) {
|
||||
isPoint(other);
|
||||
const { a, d } = CURVE;
|
||||
const { x: X1, y: Y1, z: Z1, t: T1 } = this;
|
||||
const { x: X2, y: Y2, z: Z2, t: T2 } = other;
|
||||
const { ex: X1, ey: Y1, ez: Z1, et: T1 } = this;
|
||||
const { ex: X2, ey: Y2, ez: Z2, et: T2 } = other;
|
||||
// Faster algo for adding 2 Extended Points when curve's a=-1.
|
||||
// http://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-4
|
||||
// Cost: 8M + 8add + 2*2.
|
||||
@@ -289,7 +262,7 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
||||
const Y3 = modP(G * H);
|
||||
const T3 = modP(E * H);
|
||||
const Z3 = modP(F * G);
|
||||
return new ExtendedPoint(X3, Y3, Z3, T3);
|
||||
return new Point(X3, Y3, Z3, T3);
|
||||
}
|
||||
const A = modP(X1 * X2); // A = X1*X2
|
||||
const B = modP(Y1 * Y2); // B = Y1*Y2
|
||||
@@ -304,401 +277,190 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
||||
const T3 = modP(E * H); // T3 = E*H
|
||||
const Z3 = modP(F * G); // Z3 = F*G
|
||||
|
||||
return new ExtendedPoint(X3, Y3, Z3, T3);
|
||||
return new Point(X3, Y3, Z3, T3);
|
||||
}
|
||||
|
||||
subtract(other: ExtendedPoint): ExtendedPoint {
|
||||
subtract(other: Point): Point {
|
||||
return this.add(other.negate());
|
||||
}
|
||||
|
||||
private wNAF(n: bigint, affinePoint?: Point): ExtendedPoint {
|
||||
if (!affinePoint && this.equals(ExtendedPoint.BASE)) affinePoint = Point.BASE;
|
||||
const W = (affinePoint && affinePoint._WINDOW_SIZE) || 1;
|
||||
let precomputes = affinePoint && pointPrecomputes.get(affinePoint);
|
||||
if (!precomputes) {
|
||||
precomputes = wnaf.precomputeWindow(this, W) as ExtendedPoint[];
|
||||
if (affinePoint && W !== 1) {
|
||||
precomputes = ExtendedPoint.normalizeZ(precomputes);
|
||||
pointPrecomputes.set(affinePoint, precomputes);
|
||||
}
|
||||
}
|
||||
const { p, f } = wnaf.wNAF(W, precomputes, n);
|
||||
return ExtendedPoint.normalizeZ([p, f])[0];
|
||||
private wNAF(n: bigint): { p: Point; f: Point } {
|
||||
return wnaf.wNAFCached(this, pointPrecomputes, n, Point.normalizeZ);
|
||||
}
|
||||
|
||||
// Constant time multiplication.
|
||||
// Uses wNAF method. Windowed method may be 10% faster,
|
||||
// but takes 2x longer to generate and consumes 2x memory.
|
||||
multiply(scalar: number | bigint, affinePoint?: Point): ExtendedPoint {
|
||||
return this.wNAF(normalizeScalar(scalar, CURVE_ORDER), affinePoint);
|
||||
// Constant-time multiplication.
|
||||
multiply(scalar: bigint): Point {
|
||||
const { p, f } = this.wNAF(assertInRange(scalar, CURVE_ORDER));
|
||||
return Point.normalizeZ([p, f])[0];
|
||||
}
|
||||
|
||||
// Non-constant-time multiplication. Uses double-and-add algorithm.
|
||||
// It's faster, but should only be used when you don't care about
|
||||
// an exposed private key e.g. sig verification.
|
||||
// Allows scalar bigger than curve order, but less than 2^256
|
||||
multiplyUnsafe(scalar: number | bigint): ExtendedPoint {
|
||||
let n = normalizeScalar(scalar, CURVE_ORDER, false);
|
||||
const G = ExtendedPoint.BASE;
|
||||
const P0 = ExtendedPoint.ZERO;
|
||||
if (n === _0n) return P0;
|
||||
if (this.equals(P0) || n === _1n) return this;
|
||||
if (this.equals(G)) return this.wNAF(n);
|
||||
multiplyUnsafe(scalar: bigint): Point {
|
||||
let n = assertGE0(scalar);
|
||||
if (n === _0n) return I;
|
||||
if (this.equals(I) || n === _1n) return this;
|
||||
if (this.equals(G)) return this.wNAF(n).p;
|
||||
return wnaf.unsafeLadder(this, n);
|
||||
}
|
||||
|
||||
// Checks if point is of small order.
|
||||
// If you add something to small order point, you will have "dirty"
|
||||
// point with torsion component.
|
||||
// Multiplies point by cofactor and checks if the result is 0.
|
||||
isSmallOrder(): boolean {
|
||||
return this.multiplyUnsafe(CURVE.h).equals(ExtendedPoint.ZERO);
|
||||
return this.multiplyUnsafe(cofactor).is0();
|
||||
}
|
||||
|
||||
// Multiplies point by a very big scalar n and checks if the result is 0.
|
||||
// Multiplies point by curve order and checks if the result is 0.
|
||||
// Returns `false` is the point is dirty.
|
||||
isTorsionFree(): boolean {
|
||||
return this.multiplyUnsafe(CURVE_ORDER).equals(ExtendedPoint.ZERO);
|
||||
return wnaf.unsafeLadder(this, CURVE_ORDER).is0();
|
||||
}
|
||||
|
||||
// Converts Extended point to default (x, y) coordinates.
|
||||
// Can accept precomputed Z^-1 - for example, from invertBatch.
|
||||
toAffine(invZ?: bigint): Point {
|
||||
const { x, y, z } = this;
|
||||
const is0 = this.equals(ExtendedPoint.ZERO);
|
||||
if (invZ == null) invZ = is0 ? _8n : (Fp.invert(z) as bigint); // 8 was chosen arbitrarily
|
||||
const ax = modP(x * invZ);
|
||||
const ay = modP(y * invZ);
|
||||
const zz = modP(z * invZ);
|
||||
if (is0) return Point.ZERO;
|
||||
toAffine(iz?: bigint): AffinePoint<bigint> {
|
||||
const { ex: x, ey: y, ez: z } = this;
|
||||
const is0 = this.is0();
|
||||
if (iz == null) iz = is0 ? _8n : (Fp.inv(z) as bigint); // 8 was chosen arbitrarily
|
||||
const ax = modP(x * iz);
|
||||
const ay = modP(y * iz);
|
||||
const zz = modP(z * iz);
|
||||
if (is0) return { x: _0n, y: _1n };
|
||||
if (zz !== _1n) throw new Error('invZ was invalid');
|
||||
return new Point(ax, ay);
|
||||
return { x: ax, y: ay };
|
||||
}
|
||||
clearCofactor(): ExtendedPoint {
|
||||
if (CURVE.h === _1n) return this; // Fast-path
|
||||
// clear_cofactor(P) := h_eff * P
|
||||
// hEff = h for ed25519/ed448. Maybe worth moving to params?
|
||||
if (CURVE.clearCofactor) return CURVE.clearCofactor(ExtendedPoint, this) as ExtendedPoint;
|
||||
return this.multiplyUnsafe(CURVE.h);
|
||||
}
|
||||
}
|
||||
const wnaf = wNAF(ExtendedPoint, groupLen * 8);
|
||||
|
||||
function assertExtPoint(other: unknown) {
|
||||
if (!(other instanceof ExtendedPoint)) throw new TypeError('ExtendedPoint expected');
|
||||
}
|
||||
// Stores precomputed values for points.
|
||||
const pointPrecomputes = new WeakMap<Point, ExtendedPoint[]>();
|
||||
|
||||
/**
|
||||
* Default Point works in affine coordinates: (x, y)
|
||||
*/
|
||||
class Point implements PointType {
|
||||
// Base point aka generator
|
||||
// public_key = Point.BASE * private_key
|
||||
static BASE: Point = new Point(CURVE.Gx, CURVE.Gy);
|
||||
// Identity point aka point at infinity
|
||||
// point = point + zero_point
|
||||
static ZERO: Point = new Point(_0n, _1n);
|
||||
// We calculate precomputes for elliptic curve point multiplication
|
||||
// using windowed method. This specifies window size and
|
||||
// stores precomputed values. Usually only base point would be precomputed.
|
||||
_WINDOW_SIZE?: number;
|
||||
|
||||
constructor(readonly x: bigint, readonly y: bigint) {}
|
||||
|
||||
// "Private method", don't use it directly.
|
||||
_setWindowSize(windowSize: number) {
|
||||
this._WINDOW_SIZE = windowSize;
|
||||
pointPrecomputes.delete(this);
|
||||
clearCofactor(): Point {
|
||||
const { h: cofactor } = CURVE;
|
||||
if (cofactor === _1n) return this;
|
||||
return this.multiplyUnsafe(cofactor);
|
||||
}
|
||||
|
||||
// Converts hash string or Uint8Array to Point.
|
||||
// Uses algo from RFC8032 5.1.3.
|
||||
static fromHex(hex: Hex, strict = true) {
|
||||
static fromHex(hex: Hex, strict = true): Point {
|
||||
const { d, a } = CURVE;
|
||||
hex = ensureBytes(hex, fieldLen);
|
||||
// 1. First, interpret the string as an integer in little-endian
|
||||
// representation. Bit 255 of this number is the least significant
|
||||
// bit of the x-coordinate and denote this value x_0. The
|
||||
// y-coordinate is recovered simply by clearing this bit. If the
|
||||
// resulting value is >= p, decoding fails.
|
||||
const normed = hex.slice();
|
||||
const lastByte = hex[fieldLen - 1];
|
||||
normed[fieldLen - 1] = lastByte & ~0x80;
|
||||
const y = bytesToNumberLE(normed);
|
||||
|
||||
if (strict && y >= Fp.ORDER) throw new Error('Expected 0 < hex < P');
|
||||
if (!strict && y >= maxGroupElement) throw new Error('Expected 0 < hex < 2**256');
|
||||
|
||||
// 2. To recover the x-coordinate, the curve equation implies
|
||||
// Ed25519: x² = (y² - 1) / (d y² + 1) (mod p).
|
||||
// Ed448: x² = (y² - 1) / (d y² - 1) (mod p).
|
||||
// For generic case:
|
||||
// a*x²+y²=1+d*x²*y²
|
||||
// -> y²-1 = d*x²*y²-a*x²
|
||||
// -> y²-1 = x² (d*y²-a)
|
||||
// -> x² = (y²-1) / (d*y²-a)
|
||||
|
||||
// The denominator is always non-zero mod p. Let u = y² - 1 and v = d y² + 1.
|
||||
const y2 = modP(y * y);
|
||||
const u = modP(y2 - _1n);
|
||||
const v = modP(d * y2 - a);
|
||||
let { isValid, value: x } = uvRatio(u, v);
|
||||
if (!isValid) throw new Error('Point.fromHex: invalid y coordinate');
|
||||
// 4. Finally, use the x_0 bit to select the right square root. If
|
||||
// x = 0, and x_0 = 1, decoding fails. Otherwise, if x_0 != x mod
|
||||
// 2, set x <-- p - x. Return the decoded point (x,y).
|
||||
const isXOdd = (x & _1n) === _1n;
|
||||
const isLastByteOdd = (lastByte & 0x80) !== 0;
|
||||
if (isLastByteOdd !== isXOdd) x = modP(-x);
|
||||
return new Point(x, y);
|
||||
}
|
||||
|
||||
static fromPrivateKey(privateKey: PrivKey) {
|
||||
return getExtendedPublicKey(privateKey).point;
|
||||
}
|
||||
|
||||
// There can always be only two x values (x, -x) for any y
|
||||
// When compressing point, it's enough to only store its y coordinate
|
||||
// and use the last byte to encode sign of x.
|
||||
toRawBytes(): Uint8Array {
|
||||
const bytes = numberToBytesLE(this.y, fieldLen);
|
||||
bytes[fieldLen - 1] |= this.x & _1n ? 0x80 : 0;
|
||||
return bytes;
|
||||
}
|
||||
|
||||
// Same as toRawBytes, but returns string.
|
||||
toHex(): string {
|
||||
return bytesToHex(this.toRawBytes());
|
||||
}
|
||||
|
||||
isTorsionFree(): boolean {
|
||||
return ExtendedPoint.fromAffine(this).isTorsionFree();
|
||||
}
|
||||
|
||||
equals(other: Point): boolean {
|
||||
if (!(other instanceof Point)) throw new TypeError('Point#equals: expected Point');
|
||||
return this.x === other.x && this.y === other.y;
|
||||
}
|
||||
|
||||
negate(): Point {
|
||||
return new Point(modP(-this.x), this.y);
|
||||
}
|
||||
|
||||
double(): Point {
|
||||
return ExtendedPoint.fromAffine(this).double().toAffine();
|
||||
}
|
||||
|
||||
add(other: Point) {
|
||||
return ExtendedPoint.fromAffine(this).add(ExtendedPoint.fromAffine(other)).toAffine();
|
||||
}
|
||||
|
||||
subtract(other: Point) {
|
||||
return this.add(other.negate());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constant time multiplication.
|
||||
* @param scalar Big-Endian number
|
||||
* @returns new point
|
||||
*/
|
||||
multiply(scalar: number | bigint): Point {
|
||||
return ExtendedPoint.fromAffine(this).multiply(scalar, this).toAffine();
|
||||
}
|
||||
|
||||
clearCofactor() {
|
||||
return ExtendedPoint.fromAffine(this).clearCofactor().toAffine();
|
||||
}
|
||||
// Encodes byte string to elliptic curve
|
||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3
|
||||
static hashToCurve(msg: Hex, options?: Partial<htfOpts>) {
|
||||
if (!CURVE.mapToCurve) throw new Error('No mapToCurve defined for curve');
|
||||
msg = ensureBytes(msg);
|
||||
const u = hash_to_field(msg, 2, { ...CURVE.htfDefaults, ...options } as htfOpts);
|
||||
const { x: x0, y: y0 } = CURVE.mapToCurve(u[0]);
|
||||
const { x: x1, y: y1 } = CURVE.mapToCurve(u[1]);
|
||||
const p = new Point(x0, y0).add(new Point(x1, y1)).clearCofactor();
|
||||
return p;
|
||||
}
|
||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-3
|
||||
static encodeToCurve(msg: Hex, options?: Partial<htfOpts>) {
|
||||
if (!CURVE.mapToCurve) throw new Error('No mapToCurve defined for curve');
|
||||
msg = ensureBytes(msg);
|
||||
const u = hash_to_field(msg, 1, { ...CURVE.htfDefaults, ...options } as htfOpts);
|
||||
const { x, y } = CURVE.mapToCurve(u[0]);
|
||||
return new Point(x, y).clearCofactor();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* EDDSA signature.
|
||||
*/
|
||||
class Signature implements SignatureType {
|
||||
constructor(readonly r: Point, readonly s: bigint) {
|
||||
this.assertValidity();
|
||||
}
|
||||
|
||||
static fromHex(hex: Hex) {
|
||||
const bytes = ensureBytes(hex, 2 * fieldLen);
|
||||
const r = Point.fromHex(bytes.slice(0, fieldLen), false);
|
||||
const s = bytesToNumberLE(bytes.slice(fieldLen, 2 * fieldLen));
|
||||
return new Signature(r, s);
|
||||
}
|
||||
|
||||
assertValidity() {
|
||||
const { r, s } = this;
|
||||
if (!(r instanceof Point)) throw new Error('Expected Point instance');
|
||||
// 0 <= s < l
|
||||
normalizeScalar(s, CURVE_ORDER, false);
|
||||
return this;
|
||||
}
|
||||
|
||||
toRawBytes() {
|
||||
return concatBytes(this.r.toRawBytes(), numberToBytesLE(this.s, fieldLen));
|
||||
}
|
||||
|
||||
toHex() {
|
||||
return bytesToHex(this.toRawBytes());
|
||||
}
|
||||
}
|
||||
|
||||
// Little-endian SHA512 with modulo n
|
||||
function modlLE(hash: Uint8Array): bigint {
|
||||
return mod.mod(bytesToNumberLE(hash), CURVE_ORDER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for num to be in range:
|
||||
* For strict == true: `0 < num < max`.
|
||||
* For strict == false: `0 <= num < max`.
|
||||
* Converts non-float safe numbers to bigints.
|
||||
*/
|
||||
function normalizeScalar(num: number | bigint, max: bigint, strict = true): bigint {
|
||||
if (!max) throw new TypeError('Specify max value');
|
||||
if (typeof num === 'number' && Number.isSafeInteger(num)) num = BigInt(num);
|
||||
if (typeof num === 'bigint' && num < max) {
|
||||
if (strict) {
|
||||
if (_0n < num) return num;
|
||||
const len = Fp.BYTES;
|
||||
hex = ensureBytes(hex, len); // copy hex to a new array
|
||||
const normed = hex.slice(); // copy again, we'll manipulate it
|
||||
const lastByte = hex[len - 1]; // select last byte
|
||||
normed[len - 1] = lastByte & ~0x80; // clear last bit
|
||||
const y = ut.bytesToNumberLE(normed);
|
||||
if (y === _0n) {
|
||||
// y=0 is allowed
|
||||
} else {
|
||||
if (_0n <= num) return num;
|
||||
// RFC8032 prohibits >= p, but ZIP215 doesn't
|
||||
if (strict) assertInRange(y, Fp.ORDER); // strict=true [1..P-1] (2^255-19-1 for ed25519)
|
||||
else assertInRange(y, MASK); // strict=false [1..MASK-1] (2^256-1 for ed25519)
|
||||
}
|
||||
|
||||
// Ed25519: x² = (y²-1)/(dy²+1) mod p. Ed448: x² = (y²-1)/(dy²-1) mod p. Generic case:
|
||||
// ax²+y²=1+dx²y² => y²-1=dx²y²-ax² => y²-1=x²(dy²-a) => x²=(y²-1)/(dy²-a)
|
||||
const y2 = modP(y * y); // denominator is always non-0 mod p.
|
||||
const u = modP(y2 - _1n); // u = y² - 1
|
||||
const v = modP(d * y2 - a); // v = d y² + 1.
|
||||
let { isValid, value: x } = uvRatio(u, v); // √(u/v)
|
||||
if (!isValid) throw new Error('Point.fromHex: invalid y coordinate');
|
||||
const isXOdd = (x & _1n) === _1n; // There are 2 square roots. Use x_0 bit to select proper
|
||||
const isLastByteOdd = (lastByte & 0x80) !== 0; // if x=0 and x_0 = 1, fail
|
||||
if (isLastByteOdd !== isXOdd) x = modP(-x); // if x_0 != x mod 2, set x = p-x
|
||||
return Point.fromAffine({ x, y });
|
||||
}
|
||||
static fromPrivateKey(privKey: Hex) {
|
||||
return getExtendedPublicKey(privKey).point;
|
||||
}
|
||||
toRawBytes(): Uint8Array {
|
||||
const { x, y } = this.toAffine();
|
||||
const bytes = ut.numberToBytesLE(y, Fp.BYTES); // each y has 2 x values (x, -y)
|
||||
bytes[bytes.length - 1] |= x & _1n ? 0x80 : 0; // when compressing, it's enough to store y
|
||||
return bytes; // and use the last byte to encode sign of x
|
||||
}
|
||||
toHex(): string {
|
||||
return ut.bytesToHex(this.toRawBytes()); // Same as toRawBytes, but returns string.
|
||||
}
|
||||
throw new TypeError('Expected valid scalar: 0 < scalar < max');
|
||||
}
|
||||
const { BASE: G, ZERO: I } = Point;
|
||||
const wnaf = wNAF(Point, nByteLength * 8);
|
||||
|
||||
function checkPrivateKey(key: PrivKey) {
|
||||
// Normalize bigint / number / string to Uint8Array
|
||||
key =
|
||||
typeof key === 'bigint' || typeof key === 'number'
|
||||
? numberToBytesLE(normalizeScalar(key, maxGroupElement), groupLen)
|
||||
: ensureBytes(key);
|
||||
if (key.length !== groupLen) throw new Error(`Expected ${groupLen} bytes, got ${key.length}`);
|
||||
return key;
|
||||
function modN(a: bigint) {
|
||||
return mod(a, CURVE_ORDER);
|
||||
}
|
||||
|
||||
// Takes 64 bytes
|
||||
function getKeyFromHash(hashed: Uint8Array) {
|
||||
// First 32 bytes of 64b uniformingly random input are taken,
|
||||
// clears 3 bits of it to produce a random field element.
|
||||
const head = adjustScalarBytes(hashed.slice(0, groupLen));
|
||||
// Second 32 bytes is called key prefix (5.1.6)
|
||||
const prefix = hashed.slice(groupLen, 2 * groupLen);
|
||||
// The actual private scalar
|
||||
const scalar = modlLE(head);
|
||||
// Point on Edwards curve aka public key
|
||||
const point = Point.BASE.multiply(scalar);
|
||||
const pointBytes = point.toRawBytes();
|
||||
return { head, prefix, scalar, point, pointBytes };
|
||||
// Little-endian SHA512 with modulo n
|
||||
function modN_LE(hash: Uint8Array): bigint {
|
||||
return modN(ut.bytesToNumberLE(hash));
|
||||
}
|
||||
function isHex(item: Hex, err: string) {
|
||||
if (typeof item !== 'string' && !(item instanceof Uint8Array))
|
||||
throw new Error(`${err} must be hex string or Uint8Array`);
|
||||
}
|
||||
|
||||
/** Convenience method that creates public key and other stuff. RFC8032 5.1.5 */
|
||||
function getExtendedPublicKey(key: PrivKey) {
|
||||
return getKeyFromHash(CURVE.hash(checkPrivateKey(key)));
|
||||
function getExtendedPublicKey(key: Hex) {
|
||||
isHex(key, 'private key');
|
||||
const len = nByteLength;
|
||||
// Hash private key with curve's hash function to produce uniformingly random input
|
||||
// Check byte lengths: ensure(64, h(ensure(32, key)))
|
||||
const hashed = ensureBytes(cHash(ensureBytes(key, len)), 2 * len);
|
||||
const head = adjustScalarBytes(hashed.slice(0, len)); // clear first half bits, produce FE
|
||||
const prefix = hashed.slice(len, 2 * len); // second half is called key prefix (5.1.6)
|
||||
const scalar = modN_LE(head); // The actual private scalar
|
||||
const point = G.multiply(scalar); // Point on Edwards curve aka public key
|
||||
const pointBytes = point.toRawBytes(); // Uint8Array representation
|
||||
return { head, prefix, scalar, point, pointBytes };
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates ed25519 public key. RFC8032 5.1.5
|
||||
* 1. private key is hashed with sha512, then first 32 bytes are taken from the hash
|
||||
* 2. 3 least significant bits of the first byte are cleared
|
||||
*/
|
||||
function getPublicKey(privateKey: PrivKey): Uint8Array {
|
||||
return getExtendedPublicKey(privateKey).pointBytes;
|
||||
// Calculates EdDSA pub key. RFC8032 5.1.5. Privkey is hashed. Use first half with 3 bits cleared
|
||||
function getPublicKey(privKey: Hex): Uint8Array {
|
||||
return getExtendedPublicKey(privKey).pointBytes;
|
||||
}
|
||||
|
||||
const EMPTY = new Uint8Array();
|
||||
function hashDomainToScalar(message: Uint8Array, context: Hex = EMPTY) {
|
||||
context = ensureBytes(context);
|
||||
return modlLE(CURVE.hash(domain(message, context, !!CURVE.preHash)));
|
||||
// int('LE', SHA512(dom2(F, C) || msgs)) mod N
|
||||
function hashDomainToScalar(context: Hex = new Uint8Array(), ...msgs: Uint8Array[]) {
|
||||
const msg = ut.concatBytes(...msgs);
|
||||
return modN_LE(cHash(domain(msg, ensureBytes(context), !!preHash)));
|
||||
}
|
||||
|
||||
/** Signs message with privateKey. RFC8032 5.1.6 */
|
||||
function sign(message: Hex, privateKey: Hex, context?: Hex): Uint8Array {
|
||||
message = ensureBytes(message);
|
||||
if (CURVE.preHash) message = CURVE.preHash(message);
|
||||
const { prefix, scalar, pointBytes } = getExtendedPublicKey(privateKey);
|
||||
const r = hashDomainToScalar(concatBytes(prefix, message), context);
|
||||
const R = Point.BASE.multiply(r); // R = rG
|
||||
const k = hashDomainToScalar(concatBytes(R.toRawBytes(), pointBytes, message), context); // k = hash(R+P+msg)
|
||||
const s = mod.mod(r + k * scalar, CURVE_ORDER); // s = r + kp
|
||||
return new Signature(R, s).toRawBytes();
|
||||
function sign(msg: Hex, privKey: Hex, context?: Hex): Uint8Array {
|
||||
isHex(msg, 'message');
|
||||
msg = ensureBytes(msg);
|
||||
if (preHash) msg = preHash(msg); // for ed25519ph etc.
|
||||
const { prefix, scalar, pointBytes } = getExtendedPublicKey(privKey);
|
||||
const r = hashDomainToScalar(context, prefix, msg); // r = dom2(F, C) || prefix || PH(M)
|
||||
const R = G.multiply(r).toRawBytes(); // R = rG
|
||||
const k = hashDomainToScalar(context, R, pointBytes, msg); // R || A || PH(M)
|
||||
const s = modN(r + k * scalar); // S = (r + k * s) mod L
|
||||
assertGE0(s); // 0 <= s < l
|
||||
const res = ut.concatBytes(R, ut.numberToBytesLE(s, Fp.BYTES));
|
||||
return ensureBytes(res, nByteLength * 2); // 64-byte signature
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies EdDSA signature against message and public key.
|
||||
* An extended group equation is checked.
|
||||
* RFC8032 5.1.7
|
||||
* Compliant with ZIP215:
|
||||
* 0 <= sig.R/publicKey < 2**256 (can be >= curve.P)
|
||||
* 0 <= sig.s < l
|
||||
* Not compliant with RFC8032: it's not possible to comply to both ZIP & RFC at the same time.
|
||||
*/
|
||||
function verify(sig: SigType, message: Hex, publicKey: PubKey, context?: Hex): boolean {
|
||||
message = ensureBytes(message);
|
||||
if (CURVE.preHash) message = CURVE.preHash(message);
|
||||
// When hex is passed, we check public key fully.
|
||||
// When Point instance is passed, we assume it has already been checked, for performance.
|
||||
// If user passes Point/Sig instance, we assume it has been already verified.
|
||||
// We don't check its equations for performance. We do check for valid bounds for s though
|
||||
// We always check for: a) s bounds. b) hex validity
|
||||
if (publicKey instanceof Point) {
|
||||
// ignore
|
||||
} else if (publicKey instanceof Uint8Array || typeof publicKey === 'string') {
|
||||
publicKey = Point.fromHex(publicKey, false);
|
||||
} else {
|
||||
throw new Error(`Invalid publicKey: ${publicKey}`);
|
||||
}
|
||||
|
||||
if (sig instanceof Signature) sig.assertValidity();
|
||||
else if (sig instanceof Uint8Array || typeof sig === 'string') sig = Signature.fromHex(sig);
|
||||
else throw new Error(`Wrong signature: ${sig}`);
|
||||
|
||||
const { r, s } = sig;
|
||||
const SB = ExtendedPoint.BASE.multiplyUnsafe(s);
|
||||
const k = hashDomainToScalar(
|
||||
concatBytes(r.toRawBytes(), publicKey.toRawBytes(), message),
|
||||
context
|
||||
);
|
||||
const kA = ExtendedPoint.fromAffine(publicKey).multiplyUnsafe(k);
|
||||
const RkA = ExtendedPoint.fromAffine(r).add(kA);
|
||||
function verify(sig: Hex, msg: Hex, publicKey: Hex, context?: Hex): boolean {
|
||||
isHex(sig, 'sig');
|
||||
isHex(msg, 'message');
|
||||
const len = Fp.BYTES; // Verifies EdDSA signature against message and public key. RFC8032 5.1.7.
|
||||
sig = ensureBytes(sig, 2 * len); // An extended group equation is checked.
|
||||
msg = ensureBytes(msg); // ZIP215 compliant, which means not fully RFC8032 compliant.
|
||||
if (preHash) msg = preHash(msg); // for ed25519ph, etc
|
||||
const A = Point.fromHex(publicKey, false); // Check for s bounds, hex validity
|
||||
const R = Point.fromHex(sig.slice(0, len), false); // 0 <= R < 2^256: ZIP215 R can be >= P
|
||||
const s = ut.bytesToNumberLE(sig.slice(len, 2 * len)); // 0 <= s < l
|
||||
const SB = G.multiplyUnsafe(s);
|
||||
const k = hashDomainToScalar(context, R.toRawBytes(), A.toRawBytes(), msg);
|
||||
const RkA = R.add(A.multiplyUnsafe(k));
|
||||
// [8][S]B = [8]R + [8][k]A'
|
||||
return RkA.subtract(SB).multiplyUnsafe(CURVE.h).equals(ExtendedPoint.ZERO);
|
||||
return RkA.subtract(SB).clearCofactor().equals(Point.ZERO);
|
||||
}
|
||||
|
||||
// Enable precomputes. Slows down first publicKey computation by 20ms.
|
||||
Point.BASE._setWindowSize(8);
|
||||
G._setWindowSize(8); // Enable precomputes. Slows down first publicKey computation by 20ms.
|
||||
|
||||
const utils = {
|
||||
getExtendedPublicKey,
|
||||
mod: modP,
|
||||
invert: Fp.invert,
|
||||
|
||||
/**
|
||||
* Not needed for ed25519 private keys. Needed if you use scalars directly (rare).
|
||||
*/
|
||||
hashToPrivateScalar: (hash: Hex): bigint => hashToPrivateScalar(hash, CURVE_ORDER, true),
|
||||
|
||||
/**
|
||||
* ed25519 private keys are uniform 32-bit strings. We do not need to check for
|
||||
* modulo bias like we do in secp256k1 randomPrivateKey()
|
||||
*/
|
||||
randomPrivateKey: (): Uint8Array => randomBytes(fieldLen),
|
||||
// ed25519 private keys are uniform 32b. No need to check for modulo bias, like in secp256k1.
|
||||
randomPrivateKey: (): Uint8Array => randomBytes(Fp.BYTES),
|
||||
|
||||
/**
|
||||
* We're doing scalar multiplication (used in getPublicKey etc) with precomputed BASE_POINT
|
||||
@@ -706,11 +468,10 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
||||
* but allows to speed-up subsequent getPublicKey() calls up to 20x.
|
||||
* @param windowSize 2, 4, 8, 16
|
||||
*/
|
||||
precompute(windowSize = 8, point = Point.BASE): Point {
|
||||
const cached = point.equals(Point.BASE) ? point : new Point(point.x, point.y);
|
||||
cached._setWindowSize(windowSize);
|
||||
cached.multiply(_2n);
|
||||
return cached;
|
||||
precompute(windowSize = 8, point = Point.BASE): typeof Point.BASE {
|
||||
point._setWindowSize(windowSize);
|
||||
point.multiply(BigInt(3));
|
||||
return point;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -719,9 +480,7 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
||||
getPublicKey,
|
||||
sign,
|
||||
verify,
|
||||
ExtendedPoint,
|
||||
Point,
|
||||
Signature,
|
||||
ExtendedPoint: Point,
|
||||
utils,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
import { CHash, concatBytes } from './utils.js';
|
||||
import * as mod from './modular.js';
|
||||
import type { Group, GroupConstructor, AffinePoint } from './curve.js';
|
||||
import { mod, Field } from './modular.js';
|
||||
import { CHash, Hex, concatBytes, ensureBytes } from './utils.js';
|
||||
|
||||
export type htfOpts = {
|
||||
export type Opts = {
|
||||
// DST: a domain separation tag
|
||||
// defined in section 2.2.5
|
||||
DST: string;
|
||||
encodeDST: string;
|
||||
// p: the characteristic of F
|
||||
// where F is a finite field of characteristic p and order q = p^m
|
||||
p: bigint;
|
||||
@@ -17,29 +19,35 @@ export type htfOpts = {
|
||||
k: number;
|
||||
// option to use a message that has already been processed by
|
||||
// expand_message_xmd
|
||||
expand: boolean;
|
||||
expand?: 'xmd' | 'xof';
|
||||
// Hash functions for: expand_message_xmd is appropriate for use with a
|
||||
// wide range of hash functions, including SHA-2, SHA-3, BLAKE2, and others.
|
||||
// BBS+ uses blake2: https://github.com/hyperledger/aries-framework-go/issues/2247
|
||||
// TODO: verify that hash is shake if expand==='xof' via types
|
||||
hash: CHash;
|
||||
};
|
||||
|
||||
export function validateHTFOpts(opts: htfOpts) {
|
||||
export function validateOpts(opts: Opts) {
|
||||
if (typeof opts.DST !== 'string') throw new Error('Invalid htf/DST');
|
||||
if (typeof opts.p !== 'bigint') throw new Error('Invalid htf/p');
|
||||
if (typeof opts.m !== 'number') throw new Error('Invalid htf/m');
|
||||
if (typeof opts.k !== 'number') throw new Error('Invalid htf/k');
|
||||
if (typeof opts.expand !== 'boolean') throw new Error('Invalid htf/expand');
|
||||
if (opts.expand !== 'xmd' && opts.expand !== 'xof' && opts.expand !== undefined)
|
||||
throw new Error('Invalid htf/expand');
|
||||
if (typeof opts.hash !== 'function' || !Number.isSafeInteger(opts.hash.outputLen))
|
||||
throw new Error('Invalid htf/hash function');
|
||||
}
|
||||
|
||||
// UTF8 to ui8a
|
||||
// TODO: looks broken, ASCII only, why not TextEncoder/TextDecoder? it is in hashes anyway
|
||||
export function stringToBytes(str: string) {
|
||||
const bytes = new Uint8Array(str.length);
|
||||
for (let i = 0; i < str.length; i++) bytes[i] = str.charCodeAt(i);
|
||||
return bytes;
|
||||
// Global symbols in both browsers and Node.js since v11
|
||||
// See https://github.com/microsoft/TypeScript/issues/31535
|
||||
declare const TextEncoder: any;
|
||||
declare const TextDecoder: any;
|
||||
|
||||
export function stringToBytes(str: string): Uint8Array {
|
||||
if (typeof str !== 'string') {
|
||||
throw new Error(`utf8ToBytes expected string, got ${typeof str}`);
|
||||
}
|
||||
return new TextEncoder().encode(str);
|
||||
}
|
||||
|
||||
// Octet Stream to Integer (bytesToNumberBE)
|
||||
@@ -101,14 +109,41 @@ export function expand_message_xmd(
|
||||
return pseudo_random_bytes.slice(0, lenInBytes);
|
||||
}
|
||||
|
||||
// hashes arbitrary-length byte strings to a list of one or more elements of a finite field F
|
||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3
|
||||
// Inputs:
|
||||
// msg - a byte string containing the message to hash.
|
||||
// count - the number of elements of F to output.
|
||||
// Outputs:
|
||||
// [u_0, ..., u_(count - 1)], a list of field elements.
|
||||
export function hash_to_field(msg: Uint8Array, count: number, options: htfOpts): bigint[][] {
|
||||
export function expand_message_xof(
|
||||
msg: Uint8Array,
|
||||
DST: Uint8Array,
|
||||
lenInBytes: number,
|
||||
k: number,
|
||||
H: CHash
|
||||
): Uint8Array {
|
||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-5.3.3
|
||||
// DST = H('H2C-OVERSIZE-DST-' || a_very_long_DST, Math.ceil((lenInBytes * k) / 8));
|
||||
if (DST.length > 255) {
|
||||
const dkLen = Math.ceil((2 * k) / 8);
|
||||
DST = H.create({ dkLen }).update(stringToBytes('H2C-OVERSIZE-DST-')).update(DST).digest();
|
||||
}
|
||||
if (lenInBytes > 65535 || DST.length > 255)
|
||||
throw new Error('expand_message_xof: invalid lenInBytes');
|
||||
return (
|
||||
H.create({ dkLen: lenInBytes })
|
||||
.update(msg)
|
||||
.update(i2osp(lenInBytes, 2))
|
||||
// 2. DST_prime = DST || I2OSP(len(DST), 1)
|
||||
.update(DST)
|
||||
.update(i2osp(DST.length, 1))
|
||||
.digest()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hashes arbitrary-length byte strings to a list of one or more elements of a finite field F
|
||||
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3
|
||||
* @param msg a byte string containing the message to hash
|
||||
* @param count the number of elements of F to output
|
||||
* @param options `{DST: string, p: bigint, m: number, k: number, expand: 'xmd' | 'xof', hash: H}`
|
||||
* @returns [u_0, ..., u_(count - 1)], a list of field elements.
|
||||
*/
|
||||
export function hash_to_field(msg: Uint8Array, count: number, options: Opts): bigint[][] {
|
||||
// if options is provided but incomplete, fill any missing fields with the
|
||||
// value in hftDefaults (ie hash to G2).
|
||||
const log2p = options.p.toString(2).length;
|
||||
@@ -116,8 +151,10 @@ export function hash_to_field(msg: Uint8Array, count: number, options: htfOpts):
|
||||
const len_in_bytes = count * options.m * L;
|
||||
const DST = stringToBytes(options.DST);
|
||||
let pseudo_random_bytes = msg;
|
||||
if (options.expand) {
|
||||
if (options.expand === 'xmd') {
|
||||
pseudo_random_bytes = expand_message_xmd(msg, DST, len_in_bytes, options.hash);
|
||||
} else if (options.expand === 'xof') {
|
||||
pseudo_random_bytes = expand_message_xof(msg, DST, len_in_bytes, options.k, options.hash);
|
||||
}
|
||||
const u = new Array(count);
|
||||
for (let i = 0; i < count; i++) {
|
||||
@@ -125,14 +162,14 @@ export function hash_to_field(msg: Uint8Array, count: number, options: htfOpts):
|
||||
for (let j = 0; j < options.m; j++) {
|
||||
const elm_offset = L * (j + i * options.m);
|
||||
const tv = pseudo_random_bytes.subarray(elm_offset, elm_offset + L);
|
||||
e[j] = mod.mod(os2ip(tv), options.p);
|
||||
e[j] = mod(os2ip(tv), options.p);
|
||||
}
|
||||
u[i] = e;
|
||||
}
|
||||
return u;
|
||||
}
|
||||
|
||||
export function isogenyMap<T, F extends mod.Field<T>>(field: F, map: [T[], T[], T[], T[]]) {
|
||||
export function isogenyMap<T, F extends Field<T>>(field: F, map: [T[], T[], T[], T[]]) {
|
||||
// Make same order as in spec
|
||||
const COEFF = map.map((i) => Array.from(i).reverse());
|
||||
return (x: T, y: T) => {
|
||||
@@ -144,3 +181,56 @@ export function isogenyMap<T, F extends mod.Field<T>>(field: F, map: [T[], T[],
|
||||
return { x, y };
|
||||
};
|
||||
}
|
||||
|
||||
export interface H2CPoint<T> extends Group<H2CPoint<T>> {
|
||||
add(rhs: H2CPoint<T>): H2CPoint<T>;
|
||||
toAffine(iz?: bigint): AffinePoint<T>;
|
||||
clearCofactor(): H2CPoint<T>;
|
||||
assertValidity(): void;
|
||||
}
|
||||
|
||||
export interface H2CPointConstructor<T> extends GroupConstructor<H2CPoint<T>> {
|
||||
fromAffine(ap: AffinePoint<T>): H2CPoint<T>;
|
||||
}
|
||||
|
||||
export type MapToCurve<T> = (scalar: bigint[]) => AffinePoint<T>;
|
||||
|
||||
// Separated from initialization opts, so users won't accidentally change per-curve parameters (changing DST is ok!)
|
||||
export type htfBasicOpts = {
|
||||
DST: string;
|
||||
};
|
||||
|
||||
export function hashToCurve<T>(
|
||||
Point: H2CPointConstructor<T>,
|
||||
mapToCurve: MapToCurve<T>,
|
||||
def: Opts
|
||||
) {
|
||||
validateOpts(def);
|
||||
if (typeof mapToCurve !== 'function')
|
||||
throw new Error('hashToCurve: mapToCurve() has not been defined');
|
||||
|
||||
return {
|
||||
// Encodes byte string to elliptic curve
|
||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3
|
||||
hashToCurve(msg: Hex, options?: htfBasicOpts) {
|
||||
if (!mapToCurve) throw new Error('CURVE.mapToCurve() has not been defined');
|
||||
msg = ensureBytes(msg);
|
||||
const u = hash_to_field(msg, 2, { ...def, DST: def.DST, ...options } as Opts);
|
||||
const P = Point.fromAffine(mapToCurve(u[0]))
|
||||
.add(Point.fromAffine(mapToCurve(u[1])))
|
||||
.clearCofactor();
|
||||
P.assertValidity();
|
||||
return P;
|
||||
},
|
||||
|
||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-3
|
||||
encodeToCurve(msg: Hex, options?: htfBasicOpts) {
|
||||
if (!mapToCurve) throw new Error('CURVE.mapToCurve() has not been defined');
|
||||
msg = ensureBytes(msg);
|
||||
const u = hash_to_field(msg, 1, { ...def, DST: def.encodeDST, ...options } as Opts);
|
||||
const P = Point.fromAffine(mapToCurve(u[0])).clearCofactor();
|
||||
P.assertValidity();
|
||||
return P;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
import * as utils from './utils.js';
|
||||
// Utilities for modular arithmetics and finite fields
|
||||
import {
|
||||
bitMask,
|
||||
numberToBytesBE,
|
||||
numberToBytesLE,
|
||||
bytesToNumberBE,
|
||||
bytesToNumberLE,
|
||||
ensureBytes,
|
||||
validateObject,
|
||||
} from './utils.js';
|
||||
// prettier-ignore
|
||||
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3);
|
||||
// prettier-ignore
|
||||
const _4n = BigInt(4), _5n = BigInt(5), _7n = BigInt(7), _8n = BigInt(8);
|
||||
const _4n = BigInt(4), _5n = BigInt(5), _8n = BigInt(8);
|
||||
// prettier-ignore
|
||||
const _9n = BigInt(9), _16n = BigInt(16);
|
||||
|
||||
@@ -33,7 +41,6 @@ export function pow(num: bigint, power: bigint, modulo: bigint): bigint {
|
||||
}
|
||||
|
||||
// Does x ^ (2 ^ power) mod p. pow2(30, 4) == 30 ^ (2 ^ 4)
|
||||
// TODO: Fp version?
|
||||
export function pow2(x: bigint, power: bigint, modulo: bigint): bigint {
|
||||
let res = x;
|
||||
while (power-- > _0n) {
|
||||
@@ -54,6 +61,7 @@ export function invert(number: bigint, modulo: bigint): bigint {
|
||||
// prettier-ignore
|
||||
let x = _0n, y = _1n, u = _1n, v = _0n;
|
||||
while (a !== _0n) {
|
||||
// JIT applies optimization if those two lines follow each other
|
||||
const q = b / a;
|
||||
const r = b % a;
|
||||
const m = x - u * q;
|
||||
@@ -66,26 +74,68 @@ export function invert(number: bigint, modulo: bigint): bigint {
|
||||
return mod(x, modulo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates Legendre symbol (a | p), which denotes the value of a^((p-1)/2) (mod p).
|
||||
* * (a | p) ≡ 1 if a is a square (mod p)
|
||||
* * (a | p) ≡ -1 if a is not a square (mod p)
|
||||
* * (a | p) ≡ 0 if a ≡ 0 (mod p)
|
||||
*/
|
||||
export function legendre(num: bigint, fieldPrime: bigint): bigint {
|
||||
return pow(num, (fieldPrime - _1n) / _2n, fieldPrime);
|
||||
// Tonelli-Shanks algorithm
|
||||
// Paper 1: https://eprint.iacr.org/2012/685.pdf (page 12)
|
||||
// Paper 2: Square Roots from 1; 24, 51, 10 to Dan Shanks
|
||||
export function tonelliShanks(P: bigint) {
|
||||
// Legendre constant: used to calculate Legendre symbol (a | p),
|
||||
// which denotes the value of a^((p-1)/2) (mod p).
|
||||
// (a | p) ≡ 1 if a is a square (mod p)
|
||||
// (a | p) ≡ -1 if a is not a square (mod p)
|
||||
// (a | p) ≡ 0 if a ≡ 0 (mod p)
|
||||
const legendreC = (P - _1n) / _2n;
|
||||
|
||||
let Q: bigint, S: number, Z: bigint;
|
||||
// Step 1: By factoring out powers of 2 from p - 1,
|
||||
// find q and s such that p - 1 = q*(2^s) with q odd
|
||||
for (Q = P - _1n, S = 0; Q % _2n === _0n; Q /= _2n, S++);
|
||||
|
||||
// Step 2: Select a non-square z such that (z | p) ≡ -1 and set c ≡ zq
|
||||
for (Z = _2n; Z < P && pow(Z, legendreC, P) !== P - _1n; Z++);
|
||||
|
||||
// Fast-path
|
||||
if (S === 1) {
|
||||
const p1div4 = (P + _1n) / _4n;
|
||||
return function tonelliFast<T>(Fp: Field<T>, n: T) {
|
||||
const root = Fp.pow(n, p1div4);
|
||||
if (!Fp.eql(Fp.sqr(root), n)) throw new Error('Cannot find square root');
|
||||
return root;
|
||||
};
|
||||
}
|
||||
|
||||
// Slow-path
|
||||
const Q1div2 = (Q + _1n) / _2n;
|
||||
return function tonelliSlow<T>(Fp: Field<T>, n: T): T {
|
||||
// Step 0: Check that n is indeed a square: (n | p) should not be ≡ -1
|
||||
if (Fp.pow(n, legendreC) === Fp.neg(Fp.ONE)) throw new Error('Cannot find square root');
|
||||
let r = S;
|
||||
// TODO: will fail at Fp2/etc
|
||||
let g = Fp.pow(Fp.mul(Fp.ONE, Z), Q); // will update both x and b
|
||||
let x = Fp.pow(n, Q1div2); // first guess at the square root
|
||||
let b = Fp.pow(n, Q); // first guess at the fudge factor
|
||||
|
||||
while (!Fp.eql(b, Fp.ONE)) {
|
||||
if (Fp.eql(b, Fp.ZERO)) return Fp.ZERO; // https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm (4. If t = 0, return r = 0)
|
||||
// Find m such b^(2^m)==1
|
||||
let m = 1;
|
||||
for (let t2 = Fp.sqr(b); m < r; m++) {
|
||||
if (Fp.eql(t2, Fp.ONE)) break;
|
||||
t2 = Fp.sqr(t2); // t2 *= t2
|
||||
}
|
||||
// NOTE: r-m-1 can be bigger than 32, need to convert to bigint before shift, otherwise there will be overflow
|
||||
const ge = Fp.pow(g, _1n << BigInt(r - m - 1)); // ge = 2^(r-m-1)
|
||||
g = Fp.sqr(ge); // g = ge * ge
|
||||
x = Fp.mul(x, ge); // x *= ge
|
||||
b = Fp.mul(b, g); // b *= g
|
||||
r = m;
|
||||
}
|
||||
return x;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates square root of a number in a finite field.
|
||||
* √a mod P
|
||||
*/
|
||||
// TODO: rewrite as generic Fp function && remove bls versions
|
||||
export function sqrt(number: bigint, modulo: bigint): bigint {
|
||||
// prettier-ignore
|
||||
const n = number;
|
||||
const P = modulo;
|
||||
const p1div4 = (P + _1n) / _4n;
|
||||
export function FpSqrt(P: bigint) {
|
||||
// NOTE: different algorithms can give different roots, it is up to user to decide which one they want.
|
||||
// For example there is FpSqrtOdd/FpSqrtEven to choice root based on oddness (used for hash-to-curve).
|
||||
|
||||
// P ≡ 3 (mod 4)
|
||||
// √n = n^((P+1)/4)
|
||||
@@ -94,48 +144,54 @@ export function sqrt(number: bigint, modulo: bigint): bigint {
|
||||
// const ORDER =
|
||||
// 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaabn;
|
||||
// const NUM = 72057594037927816n;
|
||||
// TODO: fix sqrtMod in secp256k1
|
||||
const root = pow(n, p1div4, P);
|
||||
if (mod(root * root, modulo) !== number) throw new Error('Cannot find square root');
|
||||
return root;
|
||||
const p1div4 = (P + _1n) / _4n;
|
||||
return function sqrt3mod4<T>(Fp: Field<T>, n: T) {
|
||||
const root = Fp.pow(n, p1div4);
|
||||
// Throw if root**2 != n
|
||||
if (!Fp.eql(Fp.sqr(root), n)) throw new Error('Cannot find square root');
|
||||
return root;
|
||||
};
|
||||
}
|
||||
|
||||
// P ≡ 5 (mod 8)
|
||||
// Atkin algorithm for q ≡ 5 (mod 8), https://eprint.iacr.org/2012/685.pdf (page 10)
|
||||
if (P % _8n === _5n) {
|
||||
const n2 = mod(n * _2n, P);
|
||||
const v = pow(n2, (P - _5n) / _8n, P);
|
||||
const nv = mod(n * v, P);
|
||||
const i = mod(_2n * nv * v, P);
|
||||
const r = mod(nv * (i - _1n), P);
|
||||
return r;
|
||||
const c1 = (P - _5n) / _8n;
|
||||
return function sqrt5mod8<T>(Fp: Field<T>, n: T) {
|
||||
const n2 = Fp.mul(n, _2n);
|
||||
const v = Fp.pow(n2, c1);
|
||||
const nv = Fp.mul(n, v);
|
||||
const i = Fp.mul(Fp.mul(nv, _2n), v);
|
||||
const root = Fp.mul(nv, Fp.sub(i, Fp.ONE));
|
||||
if (!Fp.eql(Fp.sqr(root), n)) throw new Error('Cannot find square root');
|
||||
return root;
|
||||
};
|
||||
}
|
||||
|
||||
// P ≡ 9 (mod 16)
|
||||
if (P % _16n === _9n) {
|
||||
// NOTE: tonelli is too slow for bls-Fp2 calculations even on start
|
||||
// Means we cannot use sqrt for constants at all!
|
||||
//
|
||||
// const c1 = Fp.sqrt(Fp.negate(Fp.ONE)); // 1. c1 = sqrt(-1) in F, i.e., (c1^2) == -1 in F
|
||||
// const c2 = Fp.sqrt(c1); // 2. c2 = sqrt(c1) in F, i.e., (c2^2) == c1 in F
|
||||
// const c3 = Fp.sqrt(Fp.negate(c1)); // 3. c3 = sqrt(-c1) in F, i.e., (c3^2) == -c1 in F
|
||||
// const c4 = (P + _7n) / _16n; // 4. c4 = (q + 7) / 16 # Integer arithmetic
|
||||
// sqrt = (x) => {
|
||||
// let tv1 = Fp.pow(x, c4); // 1. tv1 = x^c4
|
||||
// let tv2 = Fp.mul(c1, tv1); // 2. tv2 = c1 * tv1
|
||||
// const tv3 = Fp.mul(c2, tv1); // 3. tv3 = c2 * tv1
|
||||
// let tv4 = Fp.mul(c3, tv1); // 4. tv4 = c3 * tv1
|
||||
// const e1 = Fp.equals(Fp.square(tv2), x); // 5. e1 = (tv2^2) == x
|
||||
// const e2 = Fp.equals(Fp.square(tv3), x); // 6. e2 = (tv3^2) == x
|
||||
// tv1 = Fp.cmov(tv1, tv2, e1); // 7. tv1 = CMOV(tv1, tv2, e1) # Select tv2 if (tv2^2) == x
|
||||
// tv2 = Fp.cmov(tv4, tv3, e2); // 8. tv2 = CMOV(tv4, tv3, e2) # Select tv3 if (tv3^2) == x
|
||||
// const e3 = Fp.equals(Fp.square(tv2), x); // 9. e3 = (tv2^2) == x
|
||||
// return Fp.cmov(tv1, tv2, e3); // 10. z = CMOV(tv1, tv2, e3) # Select the sqrt from tv1 and tv2
|
||||
// }
|
||||
}
|
||||
|
||||
// Other cases: Tonelli-Shanks algorithm
|
||||
if (legendre(n, P) !== _1n) throw new Error('Cannot find square root');
|
||||
let q: bigint, s: number, z: bigint;
|
||||
for (q = P - _1n, s = 0; q % _2n === _0n; q /= _2n, s++);
|
||||
if (s === 1) return pow(n, p1div4, P);
|
||||
for (z = _2n; z < P && legendre(z, P) !== P - _1n; z++);
|
||||
|
||||
let c = pow(z, q, P);
|
||||
let r = pow(n, (q + _1n) / _2n, P);
|
||||
let t = pow(n, q, P);
|
||||
|
||||
let t2 = _0n;
|
||||
while (mod(t - _1n, P) !== _0n) {
|
||||
t2 = mod(t * t, P);
|
||||
let i;
|
||||
for (i = 1; i < s; i++) {
|
||||
if (mod(t2 - _1n, P) === _0n) break;
|
||||
t2 = mod(t2 * t2, P);
|
||||
}
|
||||
let b = pow(c, BigInt(1 << (s - i - 1)), P);
|
||||
r = mod(r * b, P);
|
||||
c = mod(b * b, P);
|
||||
t = mod(t * c, P);
|
||||
s = i;
|
||||
}
|
||||
return r;
|
||||
return tonelliShanks(P);
|
||||
}
|
||||
|
||||
// Little-endian check for first LE bit (last BE bit);
|
||||
@@ -156,13 +212,13 @@ export interface Field<T> {
|
||||
// 1-arg
|
||||
create: (num: T) => T;
|
||||
isValid: (num: T) => boolean;
|
||||
isZero: (num: T) => boolean;
|
||||
negate(num: T): T;
|
||||
invert(num: T): T;
|
||||
is0: (num: T) => boolean;
|
||||
neg(num: T): T;
|
||||
inv(num: T): T;
|
||||
sqrt(num: T): T;
|
||||
square(num: T): T;
|
||||
sqr(num: T): T;
|
||||
// 2-args
|
||||
equals(lhs: T, rhs: T): boolean;
|
||||
eql(lhs: T, rhs: T): boolean;
|
||||
add(lhs: T, rhs: T): T;
|
||||
sub(lhs: T, rhs: T): T;
|
||||
mul(lhs: T, rhs: T | bigint): T;
|
||||
@@ -172,12 +228,13 @@ export interface Field<T> {
|
||||
addN(lhs: T, rhs: T): T;
|
||||
subN(lhs: T, rhs: T): T;
|
||||
mulN(lhs: T, rhs: T | bigint): T;
|
||||
squareN(num: T): T;
|
||||
sqrN(num: T): T;
|
||||
|
||||
// Optional
|
||||
// Should be same as sgn0 function in https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/
|
||||
// NOTE: sgn0 is 'negative in LE', which is same as odd. And negative in LE is kinda strange definition anyway.
|
||||
isOdd?(num: T): boolean; // Odd instead of even since we have it for Fp2
|
||||
legendre?(num: T): T;
|
||||
// legendre?(num: T): T;
|
||||
pow(lhs: T, power: bigint): T;
|
||||
invertBatch: (lst: T[]) => T[];
|
||||
toBytes(num: T): Uint8Array;
|
||||
@@ -187,23 +244,22 @@ export interface Field<T> {
|
||||
}
|
||||
// prettier-ignore
|
||||
const FIELD_FIELDS = [
|
||||
'create', 'isValid', 'isZero', 'negate', 'invert', 'sqrt', 'square',
|
||||
'equals', 'add', 'sub', 'mul', 'pow', 'div',
|
||||
'addN', 'subN', 'mulN', 'squareN'
|
||||
'create', 'isValid', 'is0', 'neg', 'inv', 'sqrt', 'sqr',
|
||||
'eql', 'add', 'sub', 'mul', 'pow', 'div',
|
||||
'addN', 'subN', 'mulN', 'sqrN'
|
||||
] as const;
|
||||
export function validateField<T>(field: Field<T>) {
|
||||
for (const i of ['ORDER', 'MASK'] as const) {
|
||||
if (typeof field[i] !== 'bigint')
|
||||
throw new Error(`Invalid field param ${i}=${field[i]} (${typeof field[i]})`);
|
||||
}
|
||||
for (const i of ['BYTES', 'BITS'] as const) {
|
||||
if (typeof field[i] !== 'number')
|
||||
throw new Error(`Invalid field param ${i}=${field[i]} (${typeof field[i]})`);
|
||||
}
|
||||
for (const i of FIELD_FIELDS) {
|
||||
if (typeof field[i] !== 'function')
|
||||
throw new Error(`Invalid field param ${i}=${field[i]} (${typeof field[i]})`);
|
||||
}
|
||||
const initial = {
|
||||
ORDER: 'bigint',
|
||||
MASK: 'bigint',
|
||||
BYTES: 'isSafeInteger',
|
||||
BITS: 'isSafeInteger',
|
||||
} as Record<string, string>;
|
||||
const opts = FIELD_FIELDS.reduce((map, val: string) => {
|
||||
map[val] = 'function';
|
||||
return map;
|
||||
}, initial);
|
||||
return validateObject(field, opts);
|
||||
}
|
||||
|
||||
// Generic field functions
|
||||
@@ -217,7 +273,7 @@ export function FpPow<T>(f: Field<T>, num: T, power: bigint): T {
|
||||
let d = num;
|
||||
while (power > _0n) {
|
||||
if (power & _1n) p = f.mul(p, d);
|
||||
d = f.square(d);
|
||||
d = f.sqr(d);
|
||||
power >>= 1n;
|
||||
}
|
||||
return p;
|
||||
@@ -227,15 +283,15 @@ export function FpInvertBatch<T>(f: Field<T>, nums: T[]): T[] {
|
||||
const tmp = new Array(nums.length);
|
||||
// Walk from first to last, multiply them by each other MOD p
|
||||
const lastMultiplied = nums.reduce((acc, num, i) => {
|
||||
if (f.isZero(num)) return acc;
|
||||
if (f.is0(num)) return acc;
|
||||
tmp[i] = acc;
|
||||
return f.mul(acc, num);
|
||||
}, f.ONE);
|
||||
// Invert last element
|
||||
const inverted = f.invert(lastMultiplied);
|
||||
const inverted = f.inv(lastMultiplied);
|
||||
// Walk from last to first, multiply them by inverted each other MOD p
|
||||
nums.reduceRight((acc, num, i) => {
|
||||
if (f.isZero(num)) return acc;
|
||||
if (f.is0(num)) return acc;
|
||||
tmp[i] = f.mul(acc, tmp[i]);
|
||||
return f.mul(acc, num);
|
||||
}, inverted);
|
||||
@@ -243,42 +299,60 @@ export function FpInvertBatch<T>(f: Field<T>, nums: T[]): T[] {
|
||||
}
|
||||
|
||||
export function FpDiv<T>(f: Field<T>, lhs: T, rhs: T | bigint): T {
|
||||
return f.mul(lhs, typeof rhs === 'bigint' ? invert(rhs, f.ORDER) : f.invert(rhs));
|
||||
return f.mul(lhs, typeof rhs === 'bigint' ? invert(rhs, f.ORDER) : f.inv(rhs));
|
||||
}
|
||||
|
||||
// This function returns True whenever the value x is a square in the field F.
|
||||
export function FpIsSquare<T>(f: Field<T>) {
|
||||
const legendreConst = (f.ORDER - _1n) / _2n; // Integer arithmetic
|
||||
return (x: T): boolean => {
|
||||
const p = f.pow(x, legendreConst);
|
||||
return f.eql(p, f.ZERO) || f.eql(p, f.ONE);
|
||||
};
|
||||
}
|
||||
|
||||
// CURVE.n lengths
|
||||
export function nLength(n: bigint, nBitLength?: number) {
|
||||
// Bit size, byte size of CURVE.n
|
||||
const _nBitLength = nBitLength !== undefined ? nBitLength : n.toString(2).length;
|
||||
const nByteLength = Math.ceil(_nBitLength / 8);
|
||||
return { nBitLength: _nBitLength, nByteLength };
|
||||
}
|
||||
|
||||
// NOTE: very fragile, always bench. Major performance points:
|
||||
// - NonNormalized ops
|
||||
// - Object.freeze
|
||||
// - same shape of object (don't add/remove keys)
|
||||
type FpField = Field<bigint> & Required<Pick<Field<bigint>, 'isOdd'>>;
|
||||
export function Fp(
|
||||
ORDER: bigint,
|
||||
bitLen?: number,
|
||||
isLE = false,
|
||||
redef: Partial<Field<bigint>> = {}
|
||||
): Readonly<Field<bigint>> {
|
||||
): Readonly<FpField> {
|
||||
if (ORDER <= _0n) throw new Error(`Expected Fp ORDER > 0, got ${ORDER}`);
|
||||
const { nBitLength: BITS, nByteLength: BYTES } = utils.nLength(ORDER, bitLen);
|
||||
const { nBitLength: BITS, nByteLength: BYTES } = nLength(ORDER, bitLen);
|
||||
if (BYTES > 2048) throw new Error('Field lengths over 2048 bytes are not supported');
|
||||
const sqrtP = (num: bigint) => sqrt(num, ORDER);
|
||||
const f: Field<bigint> = Object.freeze({
|
||||
const sqrtP = FpSqrt(ORDER);
|
||||
const f: Readonly<FpField> = Object.freeze({
|
||||
ORDER,
|
||||
BITS,
|
||||
BYTES,
|
||||
MASK: utils.bitMask(BITS),
|
||||
MASK: bitMask(BITS),
|
||||
ZERO: _0n,
|
||||
ONE: _1n,
|
||||
create: (num) => mod(num, ORDER),
|
||||
isValid: (num) => {
|
||||
if (typeof num !== 'bigint')
|
||||
throw new Error(`Invalid field element: expected bigint, got ${typeof num}`);
|
||||
return _0n <= num && num < ORDER;
|
||||
return _0n <= num && num < ORDER; // 0 is valid element, but it's not invertible
|
||||
},
|
||||
isZero: (num) => num === _0n,
|
||||
is0: (num) => num === _0n,
|
||||
isOdd: (num) => (num & _1n) === _1n,
|
||||
negate: (num) => mod(-num, ORDER),
|
||||
equals: (lhs, rhs) => lhs === rhs,
|
||||
neg: (num) => mod(-num, ORDER),
|
||||
eql: (lhs, rhs) => lhs === rhs,
|
||||
|
||||
square: (num) => mod(num * num, ORDER),
|
||||
sqr: (num) => mod(num * num, ORDER),
|
||||
add: (lhs, rhs) => mod(lhs + rhs, ORDER),
|
||||
sub: (lhs, rhs) => mod(lhs - rhs, ORDER),
|
||||
mul: (lhs, rhs) => mod(lhs * rhs, ORDER),
|
||||
@@ -286,106 +360,58 @@ export function Fp(
|
||||
div: (lhs, rhs) => mod(lhs * invert(rhs, ORDER), ORDER),
|
||||
|
||||
// Same as above, but doesn't normalize
|
||||
squareN: (num) => num * num,
|
||||
sqrN: (num) => num * num,
|
||||
addN: (lhs, rhs) => lhs + rhs,
|
||||
subN: (lhs, rhs) => lhs - rhs,
|
||||
mulN: (lhs, rhs) => lhs * rhs,
|
||||
|
||||
invert: (num) => invert(num, ORDER),
|
||||
sqrt: redef.sqrt || sqrtP,
|
||||
inv: (num) => invert(num, ORDER),
|
||||
sqrt: redef.sqrt || ((n) => sqrtP(f, n)),
|
||||
invertBatch: (lst) => FpInvertBatch(f, lst),
|
||||
// TODO: do we really need constant cmov?
|
||||
// We don't have const-time bigints anyway, so probably will be not very useful
|
||||
cmov: (a, b, c) => (c ? b : a),
|
||||
toBytes: (num) =>
|
||||
isLE ? utils.numberToBytesLE(num, BYTES) : utils.numberToBytesBE(num, BYTES),
|
||||
|
||||
toBytes: (num) => (isLE ? numberToBytesLE(num, BYTES) : numberToBytesBE(num, BYTES)),
|
||||
fromBytes: (bytes) => {
|
||||
if (bytes.length !== BYTES)
|
||||
throw new Error(`Fp.fromBytes: expected ${BYTES}, got ${bytes.length}`);
|
||||
return isLE ? utils.bytesToNumberLE(bytes) : utils.bytesToNumberBE(bytes);
|
||||
return isLE ? bytesToNumberLE(bytes) : bytesToNumberBE(bytes);
|
||||
},
|
||||
} as Field<bigint>);
|
||||
} as FpField);
|
||||
return Object.freeze(f);
|
||||
}
|
||||
|
||||
// TODO: re-use in bls/generic sqrt for field/etc?
|
||||
// Something like sqrtUnsafe which always returns value, but sqrt throws exception if non-square
|
||||
// From draft-irtf-cfrg-hash-to-curve-16
|
||||
export function FpSqrt<T>(Fp: Field<T>) {
|
||||
// NOTE: it requires another sqrt for constant precomputes, but no need for roots of unity,
|
||||
// probably we can simply bls code using it
|
||||
const q = Fp.ORDER;
|
||||
const squareConst = (q - _1n) / _2n;
|
||||
// is_square(x) := { True, if x^((q - 1) / 2) is 0 or 1 in F;
|
||||
// { False, otherwise.
|
||||
let isSquare: (x: T) => boolean = (x) => {
|
||||
const p = Fp.pow(x, squareConst);
|
||||
return Fp.equals(p, Fp.ZERO) || Fp.equals(p, Fp.ONE);
|
||||
};
|
||||
// Constant-time Tonelli-Shanks algorithm
|
||||
let l = _0n;
|
||||
for (let o = q - _1n; o % _2n === _0n; o /= _2n) l += _1n;
|
||||
const c1 = l; // 1. c1, the largest integer such that 2^c1 divides q - 1.
|
||||
const c2 = (q - _1n) / _2n ** c1; // 2. c2 = (q - 1) / (2^c1) # Integer arithmetic
|
||||
const c3 = (c2 - _1n) / _2n; // 3. c3 = (c2 - 1) / 2 # Integer arithmetic
|
||||
// 4. c4, a non-square value in F
|
||||
// 5. c5 = c4^c2 in F
|
||||
let c4 = Fp.ONE;
|
||||
while (isSquare(c4)) c4 = Fp.add(c4, Fp.ONE);
|
||||
const c5 = Fp.pow(c4, c2);
|
||||
|
||||
let sqrt: (x: T) => T = (x) => {
|
||||
let z = Fp.pow(x, c3); // 1. z = x^c3
|
||||
let t = Fp.square(z); // 2. t = z * z
|
||||
t = Fp.mul(t, x); // 3. t = t * x
|
||||
z = Fp.mul(z, x); // 4. z = z * x
|
||||
let b = t; // 5. b = t
|
||||
let c = c5; // 6. c = c5
|
||||
// 7. for i in (c1, c1 - 1, ..., 2):
|
||||
for (let i = c1; i > 1; i--) {
|
||||
// 8. for j in (1, 2, ..., i - 2):
|
||||
// 9. b = b * b
|
||||
for (let j = _1n; j < i - _1n; i++) b = Fp.square(b);
|
||||
const e = Fp.equals(b, Fp.ONE); // 10. e = b == 1
|
||||
const zt = Fp.mul(z, c); // 11. zt = z * c
|
||||
z = Fp.cmov(zt, z, e); // 12. z = CMOV(zt, z, e)
|
||||
c = Fp.square(c); // 13. c = c * c
|
||||
let tt = Fp.mul(t, c); // 14. tt = t * c
|
||||
t = Fp.cmov(tt, t, e); // 15. t = CMOV(tt, t, e)
|
||||
b = t; // 16. b = t
|
||||
}
|
||||
return z; // 17. return z
|
||||
};
|
||||
if (q % _4n === _3n) {
|
||||
const c1 = (q + _1n) / _4n; // 1. c1 = (q + 1) / 4 # Integer arithmetic
|
||||
sqrt = (x) => Fp.pow(x, c1);
|
||||
} else if (q % _8n === _5n) {
|
||||
const c1 = Fp.sqrt(Fp.negate(Fp.ONE)); // 1. c1 = sqrt(-1) in F, i.e., (c1^2) == -1 in F
|
||||
const c2 = (q + _3n) / _8n; // 2. c2 = (q + 3) / 8 # Integer arithmetic
|
||||
sqrt = (x) => {
|
||||
let tv1 = Fp.pow(x, c2); // 1. tv1 = x^c2
|
||||
let tv2 = Fp.mul(tv1, c1); // 2. tv2 = tv1 * c1
|
||||
let e = Fp.equals(Fp.square(tv1), x); // 3. e = (tv1^2) == x
|
||||
return Fp.cmov(tv2, tv1, e); // 4. z = CMOV(tv2, tv1, e)
|
||||
};
|
||||
} else if (Fp.ORDER % _16n === _9n) {
|
||||
const c1 = Fp.sqrt(Fp.negate(Fp.ONE)); // 1. c1 = sqrt(-1) in F, i.e., (c1^2) == -1 in F
|
||||
const c2 = Fp.sqrt(c1); // 2. c2 = sqrt(c1) in F, i.e., (c2^2) == c1 in F
|
||||
const c3 = Fp.sqrt(Fp.negate(c1)); // 3. c3 = sqrt(-c1) in F, i.e., (c3^2) == -c1 in F
|
||||
const c4 = (Fp.ORDER + _7n) / _16n; // 4. c4 = (q + 7) / 16 # Integer arithmetic
|
||||
sqrt = (x) => {
|
||||
let tv1 = Fp.pow(x, c4); // 1. tv1 = x^c4
|
||||
let tv2 = Fp.mul(c1, tv1); // 2. tv2 = c1 * tv1
|
||||
const tv3 = Fp.mul(c2, tv1); // 3. tv3 = c2 * tv1
|
||||
let tv4 = Fp.mul(c3, tv1); // 4. tv4 = c3 * tv1
|
||||
const e1 = Fp.equals(Fp.square(tv2), x); // 5. e1 = (tv2^2) == x
|
||||
const e2 = Fp.equals(Fp.square(tv3), x); // 6. e2 = (tv3^2) == x
|
||||
tv1 = Fp.cmov(tv1, tv2, e1); // 7. tv1 = CMOV(tv1, tv2, e1) # Select tv2 if (tv2^2) == x
|
||||
tv2 = Fp.cmov(tv4, tv3, e2); // 8. tv2 = CMOV(tv4, tv3, e2) # Select tv3 if (tv3^2) == x
|
||||
const e3 = Fp.equals(Fp.square(tv2), x); // 9. e3 = (tv2^2) == x
|
||||
return Fp.cmov(tv1, tv2, e3); // 10. z = CMOV(tv1, tv2, e3) # Select the sqrt from tv1 and tv2
|
||||
};
|
||||
}
|
||||
return { sqrt, isSquare };
|
||||
export function FpSqrtOdd<T>(Fp: Field<T>, elm: T) {
|
||||
if (!Fp.isOdd) throw new Error(`Field doesn't have isOdd`);
|
||||
const root = Fp.sqrt(elm);
|
||||
return Fp.isOdd(root) ? root : Fp.neg(root);
|
||||
}
|
||||
|
||||
export function FpSqrtEven<T>(Fp: Field<T>, elm: T) {
|
||||
if (!Fp.isOdd) throw new Error(`Field doesn't have isOdd`);
|
||||
const root = Fp.sqrt(elm);
|
||||
return Fp.isOdd(root) ? Fp.neg(root) : root;
|
||||
}
|
||||
|
||||
/**
|
||||
* FIPS 186 B.4.1-compliant "constant-time" private key generation utility.
|
||||
* Can take (n+8) or more bytes of uniform input e.g. from CSPRNG or KDF
|
||||
* and convert them into private scalar, with the modulo bias being neglible.
|
||||
* Needs at least 40 bytes of input for 32-byte private key.
|
||||
* https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
|
||||
* @param hash hash output from SHA3 or a similar function
|
||||
* @returns valid private scalar
|
||||
*/
|
||||
export function hashToPrivateScalar(
|
||||
hash: string | Uint8Array,
|
||||
groupOrder: bigint,
|
||||
isLE = false
|
||||
): bigint {
|
||||
hash = ensureBytes(hash);
|
||||
const hashLen = hash.length;
|
||||
const minLen = nLength(groupOrder).nByteLength + 8;
|
||||
if (minLen < 24 || hashLen < minLen || hashLen > 1024)
|
||||
throw new Error(`hashToPrivateScalar: expected ${minLen}-1024 bytes of input, got ${hashLen}`);
|
||||
const num = isLE ? bytesToNumberLE(hash) : bytesToNumberBE(hash);
|
||||
return mod(num, groupOrder - _1n) + _1n;
|
||||
}
|
||||
|
||||
@@ -1,19 +1,13 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
import * as mod from './modular.js';
|
||||
import {
|
||||
ensureBytes,
|
||||
numberToBytesLE,
|
||||
bytesToNumberLE,
|
||||
// nLength,
|
||||
} from './utils.js';
|
||||
import { mod, pow } from './modular.js';
|
||||
import { bytesToNumberLE, ensureBytes, numberToBytesLE, validateObject } from './utils.js';
|
||||
|
||||
const _0n = BigInt(0);
|
||||
const _1n = BigInt(1);
|
||||
type Hex = string | Uint8Array;
|
||||
|
||||
export type CurveType = {
|
||||
// Field over which we'll do calculations. Verify with:
|
||||
P: bigint;
|
||||
P: bigint; // finite field prime
|
||||
nByteLength: number;
|
||||
adjustScalarBytes?: (bytes: Uint8Array) => Uint8Array;
|
||||
domain?: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array;
|
||||
@@ -32,26 +26,21 @@ export type CurveFn = {
|
||||
};
|
||||
|
||||
function validateOpts(curve: CurveType) {
|
||||
for (const i of ['a24'] as const) {
|
||||
if (typeof curve[i] !== 'bigint')
|
||||
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
|
||||
}
|
||||
for (const i of ['montgomeryBits', 'nByteLength'] as const) {
|
||||
if (curve[i] === undefined) continue; // Optional
|
||||
if (!Number.isSafeInteger(curve[i]))
|
||||
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
|
||||
}
|
||||
for (const fn of ['adjustScalarBytes', 'domain', 'powPminus2'] as const) {
|
||||
if (curve[fn] === undefined) continue; // Optional
|
||||
if (typeof curve[fn] !== 'function') throw new Error(`Invalid ${fn} function`);
|
||||
}
|
||||
for (const i of ['Gu'] as const) {
|
||||
if (curve[i] === undefined) continue; // Optional
|
||||
if (typeof curve[i] !== 'string')
|
||||
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
|
||||
}
|
||||
validateObject(
|
||||
curve,
|
||||
{
|
||||
a24: 'bigint',
|
||||
},
|
||||
{
|
||||
montgomeryBits: 'isSafeInteger',
|
||||
nByteLength: 'isSafeInteger',
|
||||
adjustScalarBytes: 'function',
|
||||
domain: 'function',
|
||||
powPminus2: 'function',
|
||||
Gu: 'string',
|
||||
}
|
||||
);
|
||||
// Set defaults
|
||||
// ...nLength(curve.n, curve.nBitLength),
|
||||
return Object.freeze({ ...curve } as const);
|
||||
}
|
||||
|
||||
@@ -60,34 +49,14 @@ function validateOpts(curve: CurveType) {
|
||||
export function montgomery(curveDef: CurveType): CurveFn {
|
||||
const CURVE = validateOpts(curveDef);
|
||||
const { P } = CURVE;
|
||||
const modP = (a: bigint) => mod.mod(a, P);
|
||||
const modP = (a: bigint) => mod(a, P);
|
||||
const montgomeryBits = CURVE.montgomeryBits;
|
||||
const montgomeryBytes = Math.ceil(montgomeryBits / 8);
|
||||
const fieldLen = CURVE.nByteLength;
|
||||
const adjustScalarBytes = CURVE.adjustScalarBytes || ((bytes: Uint8Array) => bytes);
|
||||
const powPminus2 = CURVE.powPminus2 || ((x: bigint) => mod.pow(x, P - BigInt(2), P));
|
||||
const powPminus2 = CURVE.powPminus2 || ((x: bigint) => pow(x, P - BigInt(2), P));
|
||||
|
||||
/**
|
||||
* Checks for num to be in range:
|
||||
* For strict == true: `0 < num < max`.
|
||||
* For strict == false: `0 <= num < max`.
|
||||
* Converts non-float safe numbers to bigints.
|
||||
*/
|
||||
function normalizeScalar(num: number | bigint, max: bigint, strict = true): bigint {
|
||||
if (!max) throw new TypeError('Specify max value');
|
||||
if (typeof num === 'number' && Number.isSafeInteger(num)) num = BigInt(num);
|
||||
if (typeof num === 'bigint' && num < max) {
|
||||
if (strict) {
|
||||
if (_0n < num) return num;
|
||||
} else {
|
||||
if (_0n <= num) return num;
|
||||
}
|
||||
}
|
||||
throw new TypeError('Expected valid scalar: 0 < scalar < max');
|
||||
}
|
||||
|
||||
// cswap from RFC7748
|
||||
// NOTE: cswap is not from RFC7748!
|
||||
// cswap from RFC7748. But it is not from RFC7748!
|
||||
/*
|
||||
cswap(swap, x_2, x_3):
|
||||
dummy = mask(swap) AND (x_2 XOR x_3)
|
||||
@@ -104,6 +73,11 @@ export function montgomery(curveDef: CurveType): CurveFn {
|
||||
return [x_2, x_3];
|
||||
}
|
||||
|
||||
function assertFieldElement(n: bigint): bigint {
|
||||
if (typeof n === 'bigint' && _0n <= n && n < P) return n;
|
||||
throw new Error('Expected valid scalar 0 < scalar < CURVE.P');
|
||||
}
|
||||
|
||||
// x25519 from 4
|
||||
/**
|
||||
*
|
||||
@@ -112,11 +86,10 @@ export function montgomery(curveDef: CurveType): CurveFn {
|
||||
* @returns new Point on Montgomery curve
|
||||
*/
|
||||
function montgomeryLadder(pointU: bigint, scalar: bigint): bigint {
|
||||
const { P } = CURVE;
|
||||
const u = normalizeScalar(pointU, P);
|
||||
const u = assertFieldElement(pointU);
|
||||
// Section 5: Implementations MUST accept non-canonical values and process them as
|
||||
// if they had been reduced modulo the field prime.
|
||||
const k = normalizeScalar(scalar, P);
|
||||
const k = assertFieldElement(scalar);
|
||||
// The constant a24 is (486662 - 2) / 4 = 121665 for curve25519/X25519
|
||||
const a24 = CURVE.a24;
|
||||
const x_1 = u;
|
||||
@@ -172,28 +145,21 @@ export function montgomery(curveDef: CurveType): CurveFn {
|
||||
}
|
||||
|
||||
function decodeUCoordinate(uEnc: Hex): bigint {
|
||||
const u = ensureBytes(uEnc, montgomeryBytes);
|
||||
// Section 5: When receiving such an array, implementations of X25519
|
||||
// MUST mask the most significant bit in the final byte.
|
||||
// This is very ugly way, but it works because fieldLen-1 is outside of bounds for X448, so this becomes NOOP
|
||||
// fieldLen - scalaryBytes = 1 for X448 and = 0 for X25519
|
||||
u[fieldLen - 1] &= 127; // 0b0111_1111
|
||||
const u = ensureBytes(uEnc, montgomeryBytes);
|
||||
// u[fieldLen-1] crashes QuickJS (TypeError: out-of-bound numeric index)
|
||||
if (fieldLen === montgomeryBytes) u[fieldLen - 1] &= 127; // 0b0111_1111
|
||||
return bytesToNumberLE(u);
|
||||
}
|
||||
|
||||
function decodeScalar(n: Hex): bigint {
|
||||
const bytes = ensureBytes(n);
|
||||
if (bytes.length !== montgomeryBytes && bytes.length !== fieldLen)
|
||||
throw new Error(`Expected ${montgomeryBytes} or ${fieldLen} bytes, got ${bytes.length}`);
|
||||
return bytesToNumberLE(adjustScalarBytes(bytes));
|
||||
}
|
||||
/**
|
||||
* Computes shared secret between private key "scalar" and public key's "u" (x) coordinate.
|
||||
* We can get 'y' coordinate from 'u',
|
||||
* but Point.fromHex also wants 'x' coordinate oddity flag,
|
||||
* and we cannot get 'x' without knowing 'v'.
|
||||
* Need to add generic conversion between twisted edwards and complimentary curve for JubJub.
|
||||
*/
|
||||
function scalarMult(scalar: Hex, u: Hex): Uint8Array {
|
||||
const pointU = decodeUCoordinate(u);
|
||||
const _scalar = decodeScalar(scalar);
|
||||
@@ -203,12 +169,7 @@ export function montgomery(curveDef: CurveType): CurveFn {
|
||||
if (pu === _0n) throw new Error('Invalid private or public key received');
|
||||
return encodeUCoordinate(pu);
|
||||
}
|
||||
/**
|
||||
* Computes public key from private.
|
||||
* Executes scalar multiplication of curve's base point by scalar.
|
||||
* @param scalar private key
|
||||
* @returns new public key
|
||||
*/
|
||||
// Computes public key from private. By doing scalar multiplication of base point.
|
||||
function scalarMultBase(scalar: Hex): Uint8Array {
|
||||
return scalarMult(scalar, CURVE.Gu);
|
||||
}
|
||||
|
||||
119
src/abstract/poseidon.ts
Normal file
119
src/abstract/poseidon.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
// Poseidon Hash: https://eprint.iacr.org/2019/458.pdf, https://www.poseidon-hash.info
|
||||
import { Field, FpPow, validateField } from './modular.js';
|
||||
// We don't provide any constants, since different implementations use different constants.
|
||||
// For reference constants see './test/poseidon.test.js'.
|
||||
export type PoseidonOpts = {
|
||||
Fp: Field<bigint>;
|
||||
t: number;
|
||||
roundsFull: number;
|
||||
roundsPartial: number;
|
||||
sboxPower?: number;
|
||||
reversePartialPowIdx?: boolean; // Hack for stark
|
||||
mds: bigint[][];
|
||||
roundConstants: bigint[][];
|
||||
};
|
||||
|
||||
export function validateOpts(opts: PoseidonOpts) {
|
||||
const { Fp } = opts;
|
||||
validateField(Fp);
|
||||
for (const i of ['t', 'roundsFull', 'roundsPartial'] as const) {
|
||||
if (typeof opts[i] !== 'number' || !Number.isSafeInteger(opts[i]))
|
||||
throw new Error(`Poseidon: invalid param ${i}=${opts[i]} (${typeof opts[i]})`);
|
||||
}
|
||||
if (opts.reversePartialPowIdx !== undefined && typeof opts.reversePartialPowIdx !== 'boolean')
|
||||
throw new Error(`Poseidon: invalid param reversePartialPowIdx=${opts.reversePartialPowIdx}`);
|
||||
// Default is 5, but by some reasons stark uses 3
|
||||
let sboxPower = opts.sboxPower;
|
||||
if (sboxPower === undefined) sboxPower = 5;
|
||||
if (typeof sboxPower !== 'number' || !Number.isSafeInteger(sboxPower))
|
||||
throw new Error(`Poseidon wrong sboxPower=${sboxPower}`);
|
||||
|
||||
const _sboxPower = BigInt(sboxPower);
|
||||
let sboxFn = (n: bigint) => FpPow(Fp, n, _sboxPower);
|
||||
// Unwrapped sbox power for common cases (195->142μs)
|
||||
if (sboxPower === 3) sboxFn = (n: bigint) => Fp.mul(Fp.sqrN(n), n);
|
||||
else if (sboxPower === 5) sboxFn = (n: bigint) => Fp.mul(Fp.sqrN(Fp.sqrN(n)), n);
|
||||
|
||||
if (opts.roundsFull % 2 !== 0)
|
||||
throw new Error(`Poseidon roundsFull is not even: ${opts.roundsFull}`);
|
||||
const rounds = opts.roundsFull + opts.roundsPartial;
|
||||
|
||||
if (!Array.isArray(opts.roundConstants) || opts.roundConstants.length !== rounds)
|
||||
throw new Error('Poseidon: wrong round constants');
|
||||
const roundConstants = opts.roundConstants.map((rc) => {
|
||||
if (!Array.isArray(rc) || rc.length !== opts.t)
|
||||
throw new Error(`Poseidon wrong round constants: ${rc}`);
|
||||
return rc.map((i) => {
|
||||
if (typeof i !== 'bigint' || !Fp.isValid(i))
|
||||
throw new Error(`Poseidon wrong round constant=${i}`);
|
||||
return Fp.create(i);
|
||||
});
|
||||
});
|
||||
// MDS is TxT matrix
|
||||
if (!Array.isArray(opts.mds) || opts.mds.length !== opts.t)
|
||||
throw new Error('Poseidon: wrong MDS matrix');
|
||||
const mds = opts.mds.map((mdsRow) => {
|
||||
if (!Array.isArray(mdsRow) || mdsRow.length !== opts.t)
|
||||
throw new Error(`Poseidon MDS matrix row: ${mdsRow}`);
|
||||
return mdsRow.map((i) => {
|
||||
if (typeof i !== 'bigint') throw new Error(`Poseidon MDS matrix value=${i}`);
|
||||
return Fp.create(i);
|
||||
});
|
||||
});
|
||||
return Object.freeze({ ...opts, rounds, sboxFn, roundConstants, mds });
|
||||
}
|
||||
|
||||
export function splitConstants(rc: bigint[], t: number) {
|
||||
if (typeof t !== 'number') throw new Error('poseidonSplitConstants: wrong t');
|
||||
if (!Array.isArray(rc) || rc.length % t) throw new Error('poseidonSplitConstants: wrong rc');
|
||||
const res = [];
|
||||
let tmp = [];
|
||||
for (let i = 0; i < rc.length; i++) {
|
||||
tmp.push(rc[i]);
|
||||
if (tmp.length === t) {
|
||||
res.push(tmp);
|
||||
tmp = [];
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
export function poseidon(opts: PoseidonOpts) {
|
||||
const { t, Fp, rounds, sboxFn, reversePartialPowIdx } = validateOpts(opts);
|
||||
const halfRoundsFull = Math.floor(opts.roundsFull / 2);
|
||||
const partialIdx = reversePartialPowIdx ? t - 1 : 0;
|
||||
const poseidonRound = (values: bigint[], isFull: boolean, idx: number) => {
|
||||
values = values.map((i, j) => Fp.add(i, opts.roundConstants[idx][j]));
|
||||
|
||||
if (isFull) values = values.map((i) => sboxFn(i));
|
||||
else values[partialIdx] = sboxFn(values[partialIdx]);
|
||||
// Matrix multiplication
|
||||
values = opts.mds.map((i) =>
|
||||
i.reduce((acc, i, j) => Fp.add(acc, Fp.mulN(i, values[j])), Fp.ZERO)
|
||||
);
|
||||
return values;
|
||||
};
|
||||
const poseidonHash = function poseidonHash(values: bigint[]) {
|
||||
if (!Array.isArray(values) || values.length !== t)
|
||||
throw new Error(`Poseidon: wrong values (expected array of bigints with length ${t})`);
|
||||
values = values.map((i) => {
|
||||
if (typeof i !== 'bigint') throw new Error(`Poseidon: wrong value=${i} (${typeof i})`);
|
||||
return Fp.create(i);
|
||||
});
|
||||
let round = 0;
|
||||
// Apply r_f/2 full rounds.
|
||||
for (let i = 0; i < halfRoundsFull; i++) values = poseidonRound(values, true, round++);
|
||||
// Apply r_p partial rounds.
|
||||
for (let i = 0; i < opts.roundsPartial; i++) values = poseidonRound(values, false, round++);
|
||||
// Apply r_f/2 full rounds.
|
||||
for (let i = 0; i < halfRoundsFull; i++) values = poseidonRound(values, true, round++);
|
||||
|
||||
if (round !== rounds)
|
||||
throw new Error(`Poseidon: wrong number of rounds: last round=${round}, total=${rounds}`);
|
||||
return values;
|
||||
};
|
||||
// For verification in tests
|
||||
poseidonHash.roundConstants = opts.roundConstants;
|
||||
return poseidonHash;
|
||||
}
|
||||
@@ -1,70 +1,28 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
import * as mod from './modular.js';
|
||||
const _0n = BigInt(0);
|
||||
const _1n = BigInt(1);
|
||||
const _2n = BigInt(2);
|
||||
const u8a = (a: any): a is Uint8Array => a instanceof Uint8Array;
|
||||
|
||||
// We accept hex strings besides Uint8Array for simplicity
|
||||
export type Hex = Uint8Array | string;
|
||||
// Very few implementations accept numbers, we do it to ease learning curve
|
||||
export type PrivKey = Hex | bigint | number;
|
||||
export type PrivKey = Hex | bigint;
|
||||
export type CHash = {
|
||||
(message: Uint8Array | string): Uint8Array;
|
||||
blockLen: number;
|
||||
outputLen: number;
|
||||
create(): any;
|
||||
create(opts?: { dkLen?: number }): any; // For shake
|
||||
};
|
||||
|
||||
// NOTE: these are generic, even if curve is on some polynominal field (bls), it will still have P/n/h
|
||||
// But generator can be different (Fp2/Fp6 for bls?)
|
||||
export type BasicCurve<T> = {
|
||||
// Field over which we'll do calculations (Fp)
|
||||
Fp: mod.Field<T>;
|
||||
// Curve order, total count of valid points in the field
|
||||
n: bigint;
|
||||
// Bit/byte length of curve order
|
||||
nBitLength?: number;
|
||||
nByteLength?: number;
|
||||
// Cofactor
|
||||
// NOTE: we can assign default value of 1, but then users will just ignore it, without validating with spec
|
||||
// Has not use for now, but nice to have in API
|
||||
h: bigint;
|
||||
hEff?: bigint; // Number to multiply to clear cofactor
|
||||
// Base point (x, y) aka generator point
|
||||
Gx: T;
|
||||
Gy: T;
|
||||
// Wrap private key by curve order (% CURVE.n instead of throwing error)
|
||||
wrapPrivateKey?: boolean;
|
||||
// Point at infinity is perfectly valid point, but not valid public key.
|
||||
// Disabled by default because of compatibility reasons with @noble/secp256k1
|
||||
allowInfinityPoint?: boolean;
|
||||
};
|
||||
|
||||
export function validateOpts<FP, T>(curve: BasicCurve<FP> & T) {
|
||||
mod.validateField(curve.Fp);
|
||||
for (const i of ['n', 'h'] as const) {
|
||||
if (typeof curve[i] !== 'bigint')
|
||||
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
|
||||
}
|
||||
if (!curve.Fp.isValid(curve.Gx)) throw new Error('Invalid generator X coordinate Fp element');
|
||||
if (!curve.Fp.isValid(curve.Gy)) throw new Error('Invalid generator Y coordinate Fp element');
|
||||
|
||||
for (const i of ['nBitLength', 'nByteLength'] as const) {
|
||||
if (curve[i] === undefined) continue; // Optional
|
||||
if (!Number.isSafeInteger(curve[i]))
|
||||
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
|
||||
}
|
||||
// Set defaults
|
||||
return Object.freeze({ ...nLength(curve.n, curve.nBitLength), ...curve } as const);
|
||||
}
|
||||
export type FHash = (message: Uint8Array | string) => Uint8Array;
|
||||
|
||||
const hexes = Array.from({ length: 256 }, (v, i) => i.toString(16).padStart(2, '0'));
|
||||
export function bytesToHex(uint8a: Uint8Array): string {
|
||||
if (!(uint8a instanceof Uint8Array)) throw new Error('Expected Uint8Array');
|
||||
export function bytesToHex(bytes: Uint8Array): string {
|
||||
if (!u8a(bytes)) throw new Error('Uint8Array expected');
|
||||
// pre-caching improves the speed 6x
|
||||
let hex = '';
|
||||
for (let i = 0; i < uint8a.length; i++) {
|
||||
hex += hexes[uint8a[i]];
|
||||
for (let i = 0; i < bytes.length; i++) {
|
||||
hex += hexes[bytes[i]];
|
||||
}
|
||||
return hex;
|
||||
}
|
||||
@@ -75,25 +33,21 @@ export function numberToHexUnpadded(num: number | bigint): string {
|
||||
}
|
||||
|
||||
export function hexToNumber(hex: string): bigint {
|
||||
if (typeof hex !== 'string') {
|
||||
throw new TypeError('hexToNumber: expected string, got ' + typeof hex);
|
||||
}
|
||||
if (typeof hex !== 'string') throw new Error('string expected, got ' + typeof hex);
|
||||
// Big Endian
|
||||
return BigInt(`0x${hex}`);
|
||||
return BigInt(hex === '' ? '0' : `0x${hex}`);
|
||||
}
|
||||
|
||||
// Caching slows it down 2-3x
|
||||
export function hexToBytes(hex: string): Uint8Array {
|
||||
if (typeof hex !== 'string') {
|
||||
throw new TypeError('hexToBytes: expected string, got ' + typeof hex);
|
||||
}
|
||||
if (hex.length % 2) throw new Error('hexToBytes: received invalid unpadded hex ' + hex.length);
|
||||
if (typeof hex !== 'string') throw new Error('string expected, got ' + typeof hex);
|
||||
if (hex.length % 2) throw new Error('hex string is invalid: unpadded ' + hex.length);
|
||||
const array = new Uint8Array(hex.length / 2);
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
const j = i * 2;
|
||||
const hexByte = hex.slice(j, j + 2);
|
||||
const byte = Number.parseInt(hexByte, 16);
|
||||
if (Number.isNaN(byte) || byte < 0) throw new Error('Invalid byte sequence');
|
||||
if (Number.isNaN(byte) || byte < 0) throw new Error('invalid byte sequence');
|
||||
array[i] = byte;
|
||||
}
|
||||
return array;
|
||||
@@ -103,62 +57,36 @@ export function hexToBytes(hex: string): Uint8Array {
|
||||
export function bytesToNumberBE(bytes: Uint8Array): bigint {
|
||||
return hexToNumber(bytesToHex(bytes));
|
||||
}
|
||||
export function bytesToNumberLE(uint8a: Uint8Array): bigint {
|
||||
if (!(uint8a instanceof Uint8Array)) throw new Error('Expected Uint8Array');
|
||||
return BigInt('0x' + bytesToHex(Uint8Array.from(uint8a).reverse()));
|
||||
export function bytesToNumberLE(bytes: Uint8Array): bigint {
|
||||
if (!u8a(bytes)) throw new Error('Uint8Array expected');
|
||||
return hexToNumber(bytesToHex(Uint8Array.from(bytes).reverse()));
|
||||
}
|
||||
|
||||
export const numberToBytesBE = (n: bigint, len: number) =>
|
||||
hexToBytes(n.toString(16).padStart(len * 2, '0'));
|
||||
export const numberToBytesLE = (n: bigint, len: number) => numberToBytesBE(n, len).reverse();
|
||||
// Returns variable number bytes (minimal bigint encoding?)
|
||||
export const numberToVarBytesBE = (n: bigint) => hexToBytes(numberToHexUnpadded(n));
|
||||
|
||||
export function ensureBytes(hex: Hex, expectedLength?: number): Uint8Array {
|
||||
// Uint8Array.from() instead of hash.slice() because node.js Buffer
|
||||
// is instance of Uint8Array, and its slice() creates **mutable** copy
|
||||
const bytes = hex instanceof Uint8Array ? Uint8Array.from(hex) : hexToBytes(hex);
|
||||
const bytes = u8a(hex) ? Uint8Array.from(hex) : hexToBytes(hex);
|
||||
if (typeof expectedLength === 'number' && bytes.length !== expectedLength)
|
||||
throw new Error(`Expected ${expectedLength} bytes`);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
// Copies several Uint8Arrays into one.
|
||||
export function concatBytes(...arrays: Uint8Array[]): Uint8Array {
|
||||
if (!arrays.every((b) => b instanceof Uint8Array)) throw new Error('Uint8Array list expected');
|
||||
if (arrays.length === 1) return arrays[0];
|
||||
const length = arrays.reduce((a, arr) => a + arr.length, 0);
|
||||
const result = new Uint8Array(length);
|
||||
for (let i = 0, pad = 0; i < arrays.length; i++) {
|
||||
const arr = arrays[i];
|
||||
result.set(arr, pad);
|
||||
pad += arr.length;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// CURVE.n lengths
|
||||
export function nLength(n: bigint, nBitLength?: number) {
|
||||
// Bit size, byte size of CURVE.n
|
||||
const _nBitLength = nBitLength !== undefined ? nBitLength : n.toString(2).length;
|
||||
const nByteLength = Math.ceil(_nBitLength / 8);
|
||||
return { nBitLength: _nBitLength, nByteLength };
|
||||
}
|
||||
|
||||
/**
|
||||
* Can take (n+8) or more bytes of uniform input e.g. from CSPRNG or KDF
|
||||
* and convert them into private scalar, with the modulo bias being neglible.
|
||||
* As per FIPS 186 B.4.1.
|
||||
* https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
|
||||
* @param hash hash output from sha512, or a similar function
|
||||
* @returns valid private scalar
|
||||
*/
|
||||
export function hashToPrivateScalar(hash: Hex, CURVE_ORDER: bigint, isLE = false): bigint {
|
||||
hash = ensureBytes(hash);
|
||||
const orderLen = nLength(CURVE_ORDER).nByteLength;
|
||||
const minLen = orderLen + 8;
|
||||
if (orderLen < 16 || hash.length < minLen || hash.length > 1024)
|
||||
throw new Error('Expected valid bytes of private key as per FIPS 186');
|
||||
const num = isLE ? bytesToNumberLE(hash) : bytesToNumberBE(hash);
|
||||
return mod.mod(num, CURVE_ORDER - _1n) + _1n;
|
||||
export function concatBytes(...arrs: Uint8Array[]): Uint8Array {
|
||||
const r = new Uint8Array(arrs.reduce((sum, a) => sum + a.length, 0));
|
||||
let pad = 0; // walk through each item, ensure they have proper type
|
||||
arrs.forEach((a) => {
|
||||
if (!u8a(a)) throw new Error('Uint8Array expected');
|
||||
r.set(a, pad);
|
||||
pad += a.length;
|
||||
});
|
||||
return r;
|
||||
}
|
||||
|
||||
export function equalBytes(b1: Uint8Array, b2: Uint8Array) {
|
||||
@@ -185,3 +113,48 @@ export const bitSet = (n: bigint, pos: number, value: boolean) =>
|
||||
// Return mask for N bits (Same as BigInt(`0b${Array(i).fill('1').join('')}`))
|
||||
// Not using ** operator with bigints for old engines.
|
||||
export const bitMask = (n: number) => (_2n << BigInt(n - 1)) - _1n;
|
||||
|
||||
const validatorFns = {
|
||||
bigint: (val: any) => typeof val === 'bigint',
|
||||
function: (val: any) => typeof val === 'function',
|
||||
boolean: (val: any) => typeof val === 'boolean',
|
||||
string: (val: any) => typeof val === 'string',
|
||||
isSafeInteger: (val: any) => Number.isSafeInteger(val),
|
||||
array: (val: any) => Array.isArray(val),
|
||||
field: (val: any, object: any) => (object as any).Fp.isValid(val),
|
||||
hash: (val: any) => typeof val === 'function' && Number.isSafeInteger(val.outputLen),
|
||||
} as const;
|
||||
type Validator = keyof typeof validatorFns;
|
||||
type ValMap<T extends Record<string, any>> = { [K in keyof T]?: Validator };
|
||||
// type Record<K extends string | number | symbol, T> = { [P in K]: T; }
|
||||
|
||||
export function validateObject<T extends Record<string, any>>(
|
||||
object: T,
|
||||
validators: ValMap<T>,
|
||||
optValidators: ValMap<T> = {}
|
||||
) {
|
||||
const checkField = (fieldName: keyof T, type: Validator, isOptional: boolean) => {
|
||||
const checkVal = validatorFns[type];
|
||||
if (typeof checkVal !== 'function')
|
||||
throw new Error(`Invalid validator "${type}", expected function`);
|
||||
|
||||
const val = object[fieldName as keyof typeof object];
|
||||
if (isOptional && val === undefined) return;
|
||||
if (!checkVal(val, object)) {
|
||||
throw new Error(
|
||||
`Invalid param ${String(fieldName)}=${val} (${typeof val}), expected ${type}`
|
||||
);
|
||||
}
|
||||
};
|
||||
for (const [fieldName, type] of Object.entries(validators)) checkField(fieldName, type!, false);
|
||||
for (const [fieldName, type] of Object.entries(optValidators)) checkField(fieldName, type!, true);
|
||||
return object;
|
||||
}
|
||||
// validate type tests
|
||||
// const o: { a: number; b: number; c: number } = { a: 1, b: 5, c: 6 };
|
||||
// const z0 = validateObject(o, { a: 'isSafeInteger' }, { c: 'bigint' }); // Ok!
|
||||
// // Should fail type-check
|
||||
// const z1 = validateObject(o, { a: 'tmp' }, { c: 'zz' });
|
||||
// const z2 = validateObject(o, { a: 'isSafeInteger' }, { c: 'zz' });
|
||||
// const z3 = validateObject(o, { test: 'boolean', z: 'bug' });
|
||||
// const z4 = validateObject(o, { a: 'boolean', z: 'bug' });
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
193
src/bls12-381.ts
193
src/bls12-381.ts
@@ -1,10 +1,22 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
|
||||
// The pairing-friendly Barreto-Lynn-Scott elliptic curve construction allows to:
|
||||
// - Construct zk-SNARKs at the 128-bit security
|
||||
// - Use threshold signatures, which allows a user to sign lots of messages with one signature and verify them swiftly in a batch, using Boneh-Lynn-Shacham signature scheme.
|
||||
// Differences from @noble/bls12-381 1.4:
|
||||
// - PointG1 -> G1.Point
|
||||
// - PointG2 -> G2.Point
|
||||
// - PointG2.fromSignature -> Signature.decode
|
||||
// - PointG2.toSignature -> Signature.encode
|
||||
// - Fixed Fp2 ORDER
|
||||
// - Points now have only two coordinates
|
||||
|
||||
import { sha256 } from '@noble/hashes/sha256';
|
||||
import { randomBytes } from '@noble/hashes/utils';
|
||||
import { bls, CurveFn } from './abstract/bls.js';
|
||||
import * as mod from './abstract/modular.js';
|
||||
import {
|
||||
concatBytes,
|
||||
concatBytes as concatB,
|
||||
ensureBytes,
|
||||
numberToBytesBE,
|
||||
bytesToNumberBE,
|
||||
@@ -16,21 +28,13 @@ import {
|
||||
} from './abstract/utils.js';
|
||||
// Types
|
||||
import {
|
||||
PointType,
|
||||
ProjectivePointType,
|
||||
ProjectiveConstructor,
|
||||
ProjPointType,
|
||||
ProjConstructor,
|
||||
mapToCurveSimpleSWU,
|
||||
AffinePoint,
|
||||
} from './abstract/weierstrass.js';
|
||||
import { isogenyMap } from './abstract/hash-to-curve.js';
|
||||
|
||||
// Differences from bls12-381:
|
||||
// - PointG1 -> G1.Point
|
||||
// - PointG2 -> G2.Point
|
||||
// - PointG2.fromSignature -> Signature.decode
|
||||
// - PointG2.toSignature -> Signature.encode
|
||||
// - Fixed Fp2 ORDER
|
||||
// Points now have only two coordinates
|
||||
|
||||
// CURVE FIELDS
|
||||
// Finite field over p.
|
||||
const Fp =
|
||||
@@ -95,25 +99,24 @@ const Fp2: mod.Field<Fp2> & Fp2Utils = {
|
||||
ONE: { c0: Fp.ONE, c1: Fp.ZERO },
|
||||
create: (num) => num,
|
||||
isValid: ({ c0, c1 }) => typeof c0 === 'bigint' && typeof c1 === 'bigint',
|
||||
isZero: ({ c0, c1 }) => Fp.isZero(c0) && Fp.isZero(c1),
|
||||
equals: ({ c0, c1 }: Fp2, { c0: r0, c1: r1 }: Fp2) => Fp.equals(c0, r0) && Fp.equals(c1, r1),
|
||||
negate: ({ c0, c1 }) => ({ c0: Fp.negate(c0), c1: Fp.negate(c1) }),
|
||||
is0: ({ c0, c1 }) => Fp.is0(c0) && Fp.is0(c1),
|
||||
eql: ({ c0, c1 }: Fp2, { c0: r0, c1: r1 }: Fp2) => Fp.eql(c0, r0) && Fp.eql(c1, r1),
|
||||
neg: ({ c0, c1 }) => ({ c0: Fp.neg(c0), c1: Fp.neg(c1) }),
|
||||
pow: (num, power) => mod.FpPow(Fp2, num, power),
|
||||
invertBatch: (nums) => mod.FpInvertBatch(Fp2, nums),
|
||||
// Normalized
|
||||
add: Fp2Add,
|
||||
sub: Fp2Subtract,
|
||||
mul: Fp2Multiply,
|
||||
square: Fp2Square,
|
||||
sqr: Fp2Square,
|
||||
// NonNormalized stuff
|
||||
addN: Fp2Add,
|
||||
subN: Fp2Subtract,
|
||||
mulN: Fp2Multiply,
|
||||
squareN: Fp2Square,
|
||||
sqrN: Fp2Square,
|
||||
// Why inversion for bigint inside Fp instead of Fp2? it is even used in that context?
|
||||
div: (lhs, rhs) =>
|
||||
Fp2.mul(lhs, typeof rhs === 'bigint' ? Fp.invert(Fp.create(rhs)) : Fp2.invert(rhs)),
|
||||
invert: ({ c0: a, c1: b }) => {
|
||||
div: (lhs, rhs) => Fp2.mul(lhs, typeof rhs === 'bigint' ? Fp.inv(Fp.create(rhs)) : Fp2.inv(rhs)),
|
||||
inv: ({ c0: a, c1: b }) => {
|
||||
// We wish to find the multiplicative inverse of a nonzero
|
||||
// element a + bu in Fp2. We leverage an identity
|
||||
//
|
||||
@@ -127,10 +130,11 @@ const Fp2: mod.Field<Fp2> & Fp2Utils = {
|
||||
// This gives that (a - bu)/(a² + b²) is the inverse
|
||||
// of (a + bu). Importantly, this can be computing using
|
||||
// only a single inversion in Fp.
|
||||
const factor = Fp.invert(Fp.create(a * a + b * b));
|
||||
const factor = Fp.inv(Fp.create(a * a + b * b));
|
||||
return { c0: Fp.mul(factor, Fp.create(a)), c1: Fp.mul(factor, Fp.create(-b)) };
|
||||
},
|
||||
sqrt: (num) => {
|
||||
if (Fp2.eql(num, Fp2.ZERO)) return Fp2.ZERO; // Algo doesn't handles this case
|
||||
// TODO: Optimize this line. It's extremely slow.
|
||||
// Speeding this up would boost aggregateSignatures.
|
||||
// https://eprint.iacr.org/2012/685.pdf applicable?
|
||||
@@ -138,15 +142,15 @@ const Fp2: mod.Field<Fp2> & Fp2Utils = {
|
||||
// https://github.com/supranational/blst/blob/aae0c7d70b799ac269ff5edf29d8191dbd357876/src/exp2.c#L1
|
||||
// Inspired by https://github.com/dalek-cryptography/curve25519-dalek/blob/17698df9d4c834204f83a3574143abacb4fc81a5/src/field.rs#L99
|
||||
const candidateSqrt = Fp2.pow(num, (Fp2.ORDER + 8n) / 16n);
|
||||
const check = Fp2.div(Fp2.square(candidateSqrt), num); // candidateSqrt.square().div(this);
|
||||
const check = Fp2.div(Fp2.sqr(candidateSqrt), num); // candidateSqrt.square().div(this);
|
||||
const R = FP2_ROOTS_OF_UNITY;
|
||||
const divisor = [R[0], R[2], R[4], R[6]].find((r) => Fp2.equals(r, check));
|
||||
const divisor = [R[0], R[2], R[4], R[6]].find((r) => Fp2.eql(r, check));
|
||||
if (!divisor) throw new Error('No root');
|
||||
const index = R.indexOf(divisor);
|
||||
const root = R[index / 2];
|
||||
if (!root) throw new Error('Invalid root');
|
||||
const x1 = Fp2.div(candidateSqrt, root);
|
||||
const x2 = Fp2.negate(x1);
|
||||
const x2 = Fp2.neg(x1);
|
||||
const { re: re1, im: im1 } = Fp2.reim(x1);
|
||||
const { re: re2, im: im2 } = Fp2.reim(x2);
|
||||
if (im1 > im2 || (im1 === im2 && re1 > re2)) return x1;
|
||||
@@ -165,7 +169,7 @@ const Fp2: mod.Field<Fp2> & Fp2Utils = {
|
||||
if (b.length !== Fp2.BYTES) throw new Error(`fromBytes wrong length=${b.length}`);
|
||||
return { c0: Fp.fromBytes(b.subarray(0, Fp.BYTES)), c1: Fp.fromBytes(b.subarray(Fp.BYTES)) };
|
||||
},
|
||||
toBytes: ({ c0, c1 }) => concatBytes(Fp.toBytes(c0), Fp.toBytes(c1)),
|
||||
toBytes: ({ c0, c1 }) => concatB(Fp.toBytes(c0), Fp.toBytes(c1)),
|
||||
cmov: ({ c0, c1 }, { c0: r0, c1: r1 }, c) => ({
|
||||
c0: Fp.cmov(c0, r0, c),
|
||||
c1: Fp.cmov(c1, r1, c),
|
||||
@@ -275,18 +279,15 @@ const Fp6Multiply = ({ c0, c1, c2 }: Fp6, rhs: Fp6 | bigint) => {
|
||||
};
|
||||
};
|
||||
const Fp6Square = ({ c0, c1, c2 }: Fp6) => {
|
||||
let t0 = Fp2.square(c0); // c0²
|
||||
let t0 = Fp2.sqr(c0); // c0²
|
||||
let t1 = Fp2.mul(Fp2.mul(c0, c1), 2n); // 2 * c0 * c1
|
||||
let t3 = Fp2.mul(Fp2.mul(c1, c2), 2n); // 2 * c1 * c2
|
||||
let t4 = Fp2.square(c2); // c2²
|
||||
let t4 = Fp2.sqr(c2); // c2²
|
||||
return {
|
||||
c0: Fp2.add(Fp2.mulByNonresidue(t3), t0), // T3 * (u + 1) + T0
|
||||
c1: Fp2.add(Fp2.mulByNonresidue(t4), t1), // T4 * (u + 1) + T1
|
||||
// T1 + (c0 - c1 + c2)² + T3 - T0 - T4
|
||||
c2: Fp2.sub(
|
||||
Fp2.sub(Fp2.add(Fp2.add(t1, Fp2.square(Fp2.add(Fp2.sub(c0, c1), c2))), t3), t0),
|
||||
t4
|
||||
),
|
||||
c2: Fp2.sub(Fp2.sub(Fp2.add(Fp2.add(t1, Fp2.sqr(Fp2.add(Fp2.sub(c0, c1), c2))), t3), t0), t4),
|
||||
};
|
||||
};
|
||||
type Fp6Utils = {
|
||||
@@ -307,35 +308,34 @@ const Fp6: mod.Field<Fp6> & Fp6Utils = {
|
||||
ONE: { c0: Fp2.ONE, c1: Fp2.ZERO, c2: Fp2.ZERO },
|
||||
create: (num) => num,
|
||||
isValid: ({ c0, c1, c2 }) => Fp2.isValid(c0) && Fp2.isValid(c1) && Fp2.isValid(c2),
|
||||
isZero: ({ c0, c1, c2 }) => Fp2.isZero(c0) && Fp2.isZero(c1) && Fp2.isZero(c2),
|
||||
negate: ({ c0, c1, c2 }) => ({ c0: Fp2.negate(c0), c1: Fp2.negate(c1), c2: Fp2.negate(c2) }),
|
||||
equals: ({ c0, c1, c2 }, { c0: r0, c1: r1, c2: r2 }) =>
|
||||
Fp2.equals(c0, r0) && Fp2.equals(c1, r1) && Fp2.equals(c2, r2),
|
||||
is0: ({ c0, c1, c2 }) => Fp2.is0(c0) && Fp2.is0(c1) && Fp2.is0(c2),
|
||||
neg: ({ c0, c1, c2 }) => ({ c0: Fp2.neg(c0), c1: Fp2.neg(c1), c2: Fp2.neg(c2) }),
|
||||
eql: ({ c0, c1, c2 }, { c0: r0, c1: r1, c2: r2 }) =>
|
||||
Fp2.eql(c0, r0) && Fp2.eql(c1, r1) && Fp2.eql(c2, r2),
|
||||
sqrt: () => {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
// Do we need division by bigint at all? Should be done via order:
|
||||
div: (lhs, rhs) =>
|
||||
Fp6.mul(lhs, typeof rhs === 'bigint' ? Fp.invert(Fp.create(rhs)) : Fp6.invert(rhs)),
|
||||
div: (lhs, rhs) => Fp6.mul(lhs, typeof rhs === 'bigint' ? Fp.inv(Fp.create(rhs)) : Fp6.inv(rhs)),
|
||||
pow: (num, power) => mod.FpPow(Fp6, num, power),
|
||||
invertBatch: (nums) => mod.FpInvertBatch(Fp6, nums),
|
||||
// Normalized
|
||||
add: Fp6Add,
|
||||
sub: Fp6Subtract,
|
||||
mul: Fp6Multiply,
|
||||
square: Fp6Square,
|
||||
sqr: Fp6Square,
|
||||
// NonNormalized stuff
|
||||
addN: Fp6Add,
|
||||
subN: Fp6Subtract,
|
||||
mulN: Fp6Multiply,
|
||||
squareN: Fp6Square,
|
||||
sqrN: Fp6Square,
|
||||
|
||||
invert: ({ c0, c1, c2 }) => {
|
||||
let t0 = Fp2.sub(Fp2.square(c0), Fp2.mulByNonresidue(Fp2.mul(c2, c1))); // c0² - c2 * c1 * (u + 1)
|
||||
let t1 = Fp2.sub(Fp2.mulByNonresidue(Fp2.square(c2)), Fp2.mul(c0, c1)); // c2² * (u + 1) - c0 * c1
|
||||
let t2 = Fp2.sub(Fp2.square(c1), Fp2.mul(c0, c2)); // c1² - c0 * c2
|
||||
inv: ({ c0, c1, c2 }) => {
|
||||
let t0 = Fp2.sub(Fp2.sqr(c0), Fp2.mulByNonresidue(Fp2.mul(c2, c1))); // c0² - c2 * c1 * (u + 1)
|
||||
let t1 = Fp2.sub(Fp2.mulByNonresidue(Fp2.sqr(c2)), Fp2.mul(c0, c1)); // c2² * (u + 1) - c0 * c1
|
||||
let t2 = Fp2.sub(Fp2.sqr(c1), Fp2.mul(c0, c2)); // c1² - c0 * c2
|
||||
// 1/(((c2 * T1 + c1 * T2) * v) + c0 * T0)
|
||||
let t4 = Fp2.invert(
|
||||
let t4 = Fp2.inv(
|
||||
Fp2.add(Fp2.mulByNonresidue(Fp2.add(Fp2.mul(c2, t1), Fp2.mul(c1, t2))), Fp2.mul(c0, t0))
|
||||
);
|
||||
return { c0: Fp2.mul(t4, t0), c1: Fp2.mul(t4, t1), c2: Fp2.mul(t4, t2) };
|
||||
@@ -350,7 +350,7 @@ const Fp6: mod.Field<Fp6> & Fp6Utils = {
|
||||
};
|
||||
},
|
||||
toBytes: ({ c0, c1, c2 }): Uint8Array =>
|
||||
concatBytes(Fp2.toBytes(c0), Fp2.toBytes(c1), Fp2.toBytes(c2)),
|
||||
concatB(Fp2.toBytes(c0), Fp2.toBytes(c1), Fp2.toBytes(c2)),
|
||||
cmov: ({ c0, c1, c2 }: Fp6, { c0: r0, c1: r1, c2: r2 }: Fp6, c) => ({
|
||||
c0: Fp2.cmov(c0, r0, c),
|
||||
c1: Fp2.cmov(c1, r1, c),
|
||||
@@ -493,11 +493,11 @@ const Fp12Square = ({ c0, c1 }: Fp12) => {
|
||||
}; // AB + AB
|
||||
};
|
||||
function Fp4Square(a: Fp2, b: Fp2): { first: Fp2; second: Fp2 } {
|
||||
const a2 = Fp2.square(a);
|
||||
const b2 = Fp2.square(b);
|
||||
const a2 = Fp2.sqr(a);
|
||||
const b2 = Fp2.sqr(b);
|
||||
return {
|
||||
first: Fp2.add(Fp2.mulByNonresidue(b2), a2), // b² * Nonresidue + a²
|
||||
second: Fp2.sub(Fp2.sub(Fp2.square(Fp2.add(a, b)), a2), b2), // (a + b)² - a² - b²
|
||||
second: Fp2.sub(Fp2.sub(Fp2.sqr(Fp2.add(a, b)), a2), b2), // (a + b)² - a² - b²
|
||||
};
|
||||
}
|
||||
type Fp12Utils = {
|
||||
@@ -520,30 +520,30 @@ const Fp12: mod.Field<Fp12> & Fp12Utils = {
|
||||
ONE: { c0: Fp6.ONE, c1: Fp6.ZERO },
|
||||
create: (num) => num,
|
||||
isValid: ({ c0, c1 }) => Fp6.isValid(c0) && Fp6.isValid(c1),
|
||||
isZero: ({ c0, c1 }) => Fp6.isZero(c0) && Fp6.isZero(c1),
|
||||
negate: ({ c0, c1 }) => ({ c0: Fp6.negate(c0), c1: Fp6.negate(c1) }),
|
||||
equals: ({ c0, c1 }, { c0: r0, c1: r1 }) => Fp6.equals(c0, r0) && Fp6.equals(c1, r1),
|
||||
is0: ({ c0, c1 }) => Fp6.is0(c0) && Fp6.is0(c1),
|
||||
neg: ({ c0, c1 }) => ({ c0: Fp6.neg(c0), c1: Fp6.neg(c1) }),
|
||||
eql: ({ c0, c1 }, { c0: r0, c1: r1 }) => Fp6.eql(c0, r0) && Fp6.eql(c1, r1),
|
||||
sqrt: () => {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
invert: ({ c0, c1 }) => {
|
||||
let t = Fp6.invert(Fp6.sub(Fp6.square(c0), Fp6.mulByNonresidue(Fp6.square(c1)))); // 1 / (c0² - c1² * v)
|
||||
return { c0: Fp6.mul(c0, t), c1: Fp6.negate(Fp6.mul(c1, t)) }; // ((C0 * T) * T) + (-C1 * T) * w
|
||||
inv: ({ c0, c1 }) => {
|
||||
let t = Fp6.inv(Fp6.sub(Fp6.sqr(c0), Fp6.mulByNonresidue(Fp6.sqr(c1)))); // 1 / (c0² - c1² * v)
|
||||
return { c0: Fp6.mul(c0, t), c1: Fp6.neg(Fp6.mul(c1, t)) }; // ((C0 * T) * T) + (-C1 * T) * w
|
||||
},
|
||||
div: (lhs, rhs) =>
|
||||
Fp12.mul(lhs, typeof rhs === 'bigint' ? Fp.invert(Fp.create(rhs)) : Fp12.invert(rhs)),
|
||||
Fp12.mul(lhs, typeof rhs === 'bigint' ? Fp.inv(Fp.create(rhs)) : Fp12.inv(rhs)),
|
||||
pow: (num, power) => mod.FpPow(Fp12, num, power),
|
||||
invertBatch: (nums) => mod.FpInvertBatch(Fp12, nums),
|
||||
// Normalized
|
||||
add: Fp12Add,
|
||||
sub: Fp12Subtract,
|
||||
mul: Fp12Multiply,
|
||||
square: Fp12Square,
|
||||
sqr: Fp12Square,
|
||||
// NonNormalized stuff
|
||||
addN: Fp12Add,
|
||||
subN: Fp12Subtract,
|
||||
mulN: Fp12Multiply,
|
||||
squareN: Fp12Square,
|
||||
sqrN: Fp12Square,
|
||||
|
||||
// Bytes utils
|
||||
fromBytes: (b: Uint8Array): Fp12 => {
|
||||
@@ -553,7 +553,7 @@ const Fp12: mod.Field<Fp12> & Fp12Utils = {
|
||||
c1: Fp6.fromBytes(b.subarray(Fp6.BYTES)),
|
||||
};
|
||||
},
|
||||
toBytes: ({ c0, c1 }): Uint8Array => concatBytes(Fp6.toBytes(c0), Fp6.toBytes(c1)),
|
||||
toBytes: ({ c0, c1 }): Uint8Array => concatB(Fp6.toBytes(c0), Fp6.toBytes(c1)),
|
||||
cmov: ({ c0, c1 }, { c0: r0, c1: r1 }, c) => ({
|
||||
c0: Fp6.cmov(c0, r0, c),
|
||||
c1: Fp6.cmov(c1, r1, c),
|
||||
@@ -597,7 +597,7 @@ const Fp12: mod.Field<Fp12> & Fp12Utils = {
|
||||
c0: Fp6.multiplyByFp2(c0, rhs),
|
||||
c1: Fp6.multiplyByFp2(c1, rhs),
|
||||
}),
|
||||
conjugate: ({ c0, c1 }): Fp12 => ({ c0, c1: Fp6.negate(c1) }),
|
||||
conjugate: ({ c0, c1 }): Fp12 => ({ c0, c1: Fp6.neg(c1) }),
|
||||
|
||||
// A cyclotomic group is a subgroup of Fp^n defined by
|
||||
// GΦₙ(p) = {α ∈ Fpⁿ : α^Φₙ(p) = 1}
|
||||
@@ -881,7 +881,7 @@ function psi(x: Fp2, y: Fp2): [Fp2, Fp2] {
|
||||
return [x2, y2];
|
||||
}
|
||||
// Ψ endomorphism
|
||||
function G2psi(c: ProjectiveConstructor<Fp2>, P: ProjectivePointType<Fp2>) {
|
||||
function G2psi(c: ProjConstructor<Fp2>, P: ProjPointType<Fp2>) {
|
||||
const affine = P.toAffine();
|
||||
const p = psi(affine.x, affine.y);
|
||||
return new c(p[0], p[1], Fp2.ONE);
|
||||
@@ -892,9 +892,9 @@ const PSI2_C1 =
|
||||
0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaacn;
|
||||
|
||||
function psi2(x: Fp2, y: Fp2): [Fp2, Fp2] {
|
||||
return [Fp2.mul(x, PSI2_C1), Fp2.negate(y)];
|
||||
return [Fp2.mul(x, PSI2_C1), Fp2.neg(y)];
|
||||
}
|
||||
function G2psi2(c: ProjectiveConstructor<Fp2>, P: ProjectivePointType<Fp2>) {
|
||||
function G2psi2(c: ProjConstructor<Fp2>, P: ProjPointType<Fp2>) {
|
||||
const affine = P.toAffine();
|
||||
const p = psi2(affine.x, affine.y);
|
||||
return new c(p[0], p[1], Fp2.ONE);
|
||||
@@ -915,6 +915,7 @@ const htfDefaults = {
|
||||
// defined in section 2.2.5
|
||||
// Use utils.getDSTLabel(), utils.setDSTLabel(value)
|
||||
DST: 'BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_',
|
||||
encodeDST: 'BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_',
|
||||
// p: the characteristic of F
|
||||
// where F is a finite field of characteristic p and order q = p^m
|
||||
p: Fp.ORDER,
|
||||
@@ -926,12 +927,12 @@ const htfDefaults = {
|
||||
k: 128,
|
||||
// option to use a message that has already been processed by
|
||||
// expand_message_xmd
|
||||
expand: true,
|
||||
expand: 'xmd',
|
||||
// Hash functions for: expand_message_xmd is appropriate for use with a
|
||||
// wide range of hash functions, including SHA-2, SHA-3, BLAKE2, and others.
|
||||
// BBS+ uses blake2: https://github.com/hyperledger/aries-framework-go/issues/2247
|
||||
hash: sha256,
|
||||
};
|
||||
} as const;
|
||||
|
||||
// Encoding utils
|
||||
// Point on G1 curve: (x, y)
|
||||
@@ -984,7 +985,7 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
||||
// φ endomorphism
|
||||
const cubicRootOfUnityModP =
|
||||
0x5f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffen;
|
||||
const phi = new c(Fp.mul(point.x, cubicRootOfUnityModP), point.y, point.z);
|
||||
const phi = new c(Fp.mul(point.px, cubicRootOfUnityModP), point.py, point.pz);
|
||||
|
||||
// todo: unroll
|
||||
const xP = point.multiplyUnsafe(bls12_381.CURVE.x).negate(); // [x]P
|
||||
@@ -1013,7 +1014,7 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
||||
const { x, y } = G1_SWU(Fp.create(scalars[0]));
|
||||
return isogenyMapG1(x, y);
|
||||
},
|
||||
fromBytes: (bytes: Uint8Array): { x: Fp; y: Fp } => {
|
||||
fromBytes: (bytes: Uint8Array): AffinePoint<Fp> => {
|
||||
if (bytes.length === 48) {
|
||||
const P = Fp.ORDER;
|
||||
const compressedValue = bytesToNumberBE(bytes);
|
||||
@@ -1025,11 +1026,11 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
||||
let y = Fp.sqrt(right);
|
||||
if (!y) throw new Error('Invalid compressed G1 point');
|
||||
const aflag = bitGet(compressedValue, C_BIT_POS);
|
||||
if ((y * 2n) / P !== aflag) y = Fp.negate(y);
|
||||
if ((y * 2n) / P !== aflag) y = Fp.neg(y);
|
||||
return { x: Fp.create(x), y: Fp.create(y) };
|
||||
} else if (bytes.length === 96) {
|
||||
// Check if the infinity flag is set
|
||||
if ((bytes[0] & (1 << 6)) !== 0) return bls12_381.G1.Point.ZERO;
|
||||
if ((bytes[0] & (1 << 6)) !== 0) return bls12_381.G1.ProjectivePoint.ZERO.toAffine();
|
||||
const x = bytesToNumberBE(bytes.slice(0, Fp.BYTES));
|
||||
const y = bytesToNumberBE(bytes.slice(Fp.BYTES));
|
||||
return { x: Fp.create(x), y: Fp.create(y) };
|
||||
@@ -1039,7 +1040,7 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
||||
},
|
||||
toBytes: (c, point, isCompressed) => {
|
||||
const isZero = point.equals(c.ZERO);
|
||||
const { x, y } = point;
|
||||
const { x, y } = point.toAffine();
|
||||
if (isCompressed) {
|
||||
if (isZero) return COMPRESSED_ZERO.slice();
|
||||
const P = Fp.ORDER;
|
||||
@@ -1050,10 +1051,10 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
||||
} else {
|
||||
if (isZero) {
|
||||
// 2x PUBLIC_KEY_LENGTH
|
||||
const x = concatBytes(new Uint8Array([0x40]), new Uint8Array(2 * Fp.BYTES - 1));
|
||||
const x = concatB(new Uint8Array([0x40]), new Uint8Array(2 * Fp.BYTES - 1));
|
||||
return x;
|
||||
} else {
|
||||
return concatBytes(numberToBytesBE(x, Fp.BYTES), numberToBytesBE(y, Fp.BYTES));
|
||||
return concatB(numberToBytesBE(x, Fp.BYTES), numberToBytesBE(y, Fp.BYTES));
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1115,7 +1116,7 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
||||
const Q = t3.subtract(P); // Ψ²(2P) - Ψ(P) + [x²]P - [x]Ψ(P) + [x]P - 1P
|
||||
return Q; // [x²-x-1]P + [x-1]Ψ(P) + Ψ²(2P)
|
||||
},
|
||||
fromBytes: (bytes: Uint8Array): { x: Fp2; y: Fp2 } => {
|
||||
fromBytes: (bytes: Uint8Array): AffinePoint<Fp2> => {
|
||||
const m_byte = bytes[0] & 0xe0;
|
||||
if (m_byte === 0x20 || m_byte === 0x60 || m_byte === 0xe0) {
|
||||
throw new Error('Invalid encoding flag: ' + m_byte);
|
||||
@@ -1123,6 +1124,8 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
||||
const bitC = m_byte & 0x80; // compression bit
|
||||
const bitI = m_byte & 0x40; // point at infinity bit
|
||||
const bitS = m_byte & 0x20; // sign bit
|
||||
const L = Fp.BYTES;
|
||||
const slc = (b: Uint8Array, from: number, to?: number) => bytesToNumberBE(b.slice(from, to));
|
||||
if (bytes.length === 96 && bitC) {
|
||||
const { b } = bls12_381.CURVE.G2;
|
||||
const P = Fp.ORDER;
|
||||
@@ -1135,23 +1138,23 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
||||
}
|
||||
return { x: Fp2.ZERO, y: Fp2.ZERO };
|
||||
}
|
||||
const x_1 = bytesToNumberBE(bytes.slice(0, Fp.BYTES));
|
||||
const x_0 = bytesToNumberBE(bytes.slice(Fp.BYTES));
|
||||
const x_1 = slc(bytes, 0, L);
|
||||
const x_0 = slc(bytes, L, 2 * L);
|
||||
const x = Fp2.create({ c0: Fp.create(x_0), c1: Fp.create(x_1) });
|
||||
const right = Fp2.add(Fp2.pow(x, 3n), b); // y² = x³ + 4 * (u+1) = x³ + b
|
||||
let y = Fp2.sqrt(right);
|
||||
const Y_bit = y.c1 === 0n ? (y.c0 * 2n) / P : (y.c1 * 2n) / P ? 1n : 0n;
|
||||
y = bitS > 0 && Y_bit > 0 ? y : Fp2.negate(y);
|
||||
y = bitS > 0 && Y_bit > 0 ? y : Fp2.neg(y);
|
||||
return { x, y };
|
||||
} else if (bytes.length === 192 && !bitC) {
|
||||
// Check if the infinity flag is set
|
||||
if ((bytes[0] & (1 << 6)) !== 0) {
|
||||
return { x: Fp2.ZERO, y: Fp2.ZERO };
|
||||
}
|
||||
const x1 = bytesToNumberBE(bytes.slice(0, Fp.BYTES));
|
||||
const x0 = bytesToNumberBE(bytes.slice(Fp.BYTES, 2 * Fp.BYTES));
|
||||
const y1 = bytesToNumberBE(bytes.slice(2 * Fp.BYTES, 3 * Fp.BYTES));
|
||||
const y0 = bytesToNumberBE(bytes.slice(3 * Fp.BYTES));
|
||||
const x1 = slc(bytes, 0, L);
|
||||
const x0 = slc(bytes, L, 2 * L);
|
||||
const y1 = slc(bytes, 2 * L, 3 * L);
|
||||
const y0 = slc(bytes, 3 * L, 4 * L);
|
||||
return { x: Fp2.fromBigTuple([x0, x1]), y: Fp2.fromBigTuple([y0, y1]) };
|
||||
} else {
|
||||
throw new Error('Invalid point G2, expected 96/192 bytes');
|
||||
@@ -1159,20 +1162,20 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
||||
},
|
||||
toBytes: (c, point, isCompressed) => {
|
||||
const isZero = point.equals(c.ZERO);
|
||||
const { x, y } = point;
|
||||
const { x, y } = point.toAffine();
|
||||
if (isCompressed) {
|
||||
const P = Fp.ORDER;
|
||||
if (isZero) return concatBytes(COMPRESSED_ZERO, numberToBytesBE(0n, Fp.BYTES));
|
||||
if (isZero) return concatB(COMPRESSED_ZERO, numberToBytesBE(0n, Fp.BYTES));
|
||||
const flag = Boolean(y.c1 === 0n ? (y.c0 * 2n) / P : (y.c1 * 2n) / P);
|
||||
// set compressed & sign bits (looks like different offsets than for G1/Fp?)
|
||||
let x_1 = bitSet(x.c1, C_BIT_POS, flag);
|
||||
x_1 = bitSet(x_1, S_BIT_POS, true);
|
||||
return concatBytes(numberToBytesBE(x_1, Fp.BYTES), numberToBytesBE(x.c0, Fp.BYTES));
|
||||
return concatB(numberToBytesBE(x_1, Fp.BYTES), numberToBytesBE(x.c0, Fp.BYTES));
|
||||
} else {
|
||||
if (isZero) return concatBytes(new Uint8Array([0x40]), new Uint8Array(4 * Fp.BYTES - 1)); // bytes[0] |= 1 << 6;
|
||||
if (isZero) return concatB(new Uint8Array([0x40]), new Uint8Array(4 * Fp.BYTES - 1)); // bytes[0] |= 1 << 6;
|
||||
const { re: x0, im: x1 } = Fp2.reim(x);
|
||||
const { re: y0, im: y1 } = Fp2.reim(y);
|
||||
return concatBytes(
|
||||
return concatB(
|
||||
numberToBytesBE(x1, Fp.BYTES),
|
||||
numberToBytesBE(x0, Fp.BYTES),
|
||||
numberToBytesBE(y1, Fp.BYTES),
|
||||
@@ -1182,7 +1185,7 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
||||
},
|
||||
Signature: {
|
||||
// TODO: Optimize, it's very slow because of sqrt.
|
||||
decode(hex: Hex): PointType<Fp2> {
|
||||
decode(hex: Hex): ProjPointType<Fp2> {
|
||||
hex = ensureBytes(hex);
|
||||
const P = Fp.ORDER;
|
||||
const half = hex.length / 2;
|
||||
@@ -1192,7 +1195,7 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
||||
const z2 = bytesToNumberBE(hex.slice(half));
|
||||
// Indicates the infinity point
|
||||
const bflag1 = bitGet(z1, I_BIT_POS);
|
||||
if (bflag1 === 1n) return bls12_381.G2.Point.ZERO;
|
||||
if (bflag1 === 1n) return bls12_381.G2.ProjectivePoint.ZERO;
|
||||
|
||||
const x1 = Fp.create(z1 & Fp.MASK);
|
||||
const x2 = Fp.create(z2);
|
||||
@@ -1208,23 +1211,25 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
||||
const aflag1 = bitGet(z1, 381);
|
||||
const isGreater = y1 > 0n && (y1 * 2n) / P !== aflag1;
|
||||
const isZero = y1 === 0n && (y0 * 2n) / P !== aflag1;
|
||||
if (isGreater || isZero) y = Fp2.negate(y);
|
||||
const point = new bls12_381.G2.Point(x, y);
|
||||
if (isGreater || isZero) y = Fp2.neg(y);
|
||||
const point = bls12_381.G2.ProjectivePoint.fromAffine({ x, y });
|
||||
// console.log('Signature.decode', point);
|
||||
point.assertValidity();
|
||||
return point;
|
||||
},
|
||||
encode(point: PointType<Fp2>) {
|
||||
encode(point: ProjPointType<Fp2>) {
|
||||
// NOTE: by some reasons it was missed in bls12-381, looks like bug
|
||||
point.assertValidity();
|
||||
if (point.equals(bls12_381.G2.Point.ZERO))
|
||||
return concatBytes(COMPRESSED_ZERO, numberToBytesBE(0n, Fp.BYTES));
|
||||
const { re: x0, im: x1 } = Fp2.reim(point.x);
|
||||
const { re: y0, im: y1 } = Fp2.reim(point.y);
|
||||
if (point.equals(bls12_381.G2.ProjectivePoint.ZERO))
|
||||
return concatB(COMPRESSED_ZERO, numberToBytesBE(0n, Fp.BYTES));
|
||||
const a = point.toAffine();
|
||||
const { re: x0, im: x1 } = Fp2.reim(a.x);
|
||||
const { re: y0, im: y1 } = Fp2.reim(a.y);
|
||||
const tmp = y1 > 0n ? y1 * 2n : y0 * 2n;
|
||||
const aflag1 = Boolean((tmp / Fp.ORDER) & 1n);
|
||||
const z1 = bitSet(bitSet(x1, 381, aflag1), S_BIT_POS, true);
|
||||
const z2 = x0;
|
||||
return concatBytes(numberToBytesBE(z1, Fp.BYTES), numberToBytesBE(z2, Fp.BYTES));
|
||||
return concatB(numberToBytesBE(z1, Fp.BYTES), numberToBytesBE(z2, Fp.BYTES));
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
import { weierstrass } from './abstract/weierstrass.js';
|
||||
import { sha256 } from '@noble/hashes/sha256';
|
||||
import { weierstrass } from './abstract/weierstrass.js';
|
||||
import { getHash } from './_shortw_utils.js';
|
||||
import { Fp } from './abstract/modular.js';
|
||||
/**
|
||||
|
||||
135
src/ed25519.ts
135
src/ed25519.ts
@@ -1,9 +1,9 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
import { sha512 } from '@noble/hashes/sha512';
|
||||
import { concatBytes, randomBytes, utf8ToBytes } from '@noble/hashes/utils';
|
||||
import { twistedEdwards, ExtendedPointType } from './abstract/edwards.js';
|
||||
import { twistedEdwards, ExtPointType } from './abstract/edwards.js';
|
||||
import { montgomery } from './abstract/montgomery.js';
|
||||
import { mod, pow2, isNegativeLE, Fp as Field } from './abstract/modular.js';
|
||||
import { mod, pow2, isNegativeLE, Fp as Field, FpSqrtEven } from './abstract/modular.js';
|
||||
import {
|
||||
ensureBytes,
|
||||
equalBytes,
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
numberToBytesLE,
|
||||
Hex,
|
||||
} from './abstract/utils.js';
|
||||
import * as htf from './abstract/hash-to-curve.js';
|
||||
|
||||
/**
|
||||
* ed25519 Twisted Edwards curve with following addons:
|
||||
@@ -91,7 +92,7 @@ export const ED25519_TORSION_SUBGROUP = [
|
||||
'c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa',
|
||||
];
|
||||
|
||||
const Fp = Field(ED25519_P);
|
||||
const Fp = Field(ED25519_P, undefined, true);
|
||||
|
||||
const ED25519_DEF = {
|
||||
// Param: a
|
||||
@@ -116,19 +117,6 @@ const ED25519_DEF = {
|
||||
// Ratio of u to v. Allows us to combine inversion and square root. Uses algo from RFC8032 5.1.3.
|
||||
// Constant-time, u/√v
|
||||
uvRatio,
|
||||
htfDefaults: {
|
||||
DST: 'edwards25519_XMD:SHA-512_ELL2_RO_',
|
||||
p: Fp.ORDER,
|
||||
m: 1,
|
||||
k: 128,
|
||||
expand: true,
|
||||
hash: sha512,
|
||||
},
|
||||
mapToCurve: (scalars: bigint[]): { x: bigint; y: bigint } => {
|
||||
throw new Error('Not supported yet');
|
||||
// const { x, y } = calcElligatorRistrettoMap(scalars[0]).toAffine();
|
||||
// return { x, y };
|
||||
},
|
||||
} as const;
|
||||
|
||||
export const ed25519 = twistedEdwards(ED25519_DEF);
|
||||
@@ -163,8 +151,95 @@ export const x25519 = montgomery({
|
||||
adjustScalarBytes,
|
||||
});
|
||||
|
||||
// Hash To Curve Elligator2 Map (NOTE: different from ristretto255 elligator)
|
||||
// NOTE: very important part is usage of FpSqrtEven for ELL2_C1_EDWARDS, since
|
||||
// SageMath returns different root first and everything falls apart
|
||||
|
||||
const ELL2_C1 = (Fp.ORDER + BigInt(3)) / BigInt(8); // 1. c1 = (q + 3) / 8 # Integer arithmetic
|
||||
|
||||
const ELL2_C2 = Fp.pow(_2n, ELL2_C1); // 2. c2 = 2^c1
|
||||
const ELL2_C3 = Fp.sqrt(Fp.neg(Fp.ONE)); // 3. c3 = sqrt(-1)
|
||||
const ELL2_C4 = (Fp.ORDER - BigInt(5)) / BigInt(8); // 4. c4 = (q - 5) / 8 # Integer arithmetic
|
||||
const ELL2_J = BigInt(486662);
|
||||
|
||||
// prettier-ignore
|
||||
function map_to_curve_elligator2_curve25519(u: bigint) {
|
||||
let tv1 = Fp.sqr(u); // 1. tv1 = u^2
|
||||
tv1 = Fp.mul(tv1, _2n); // 2. tv1 = 2 * tv1
|
||||
let xd = Fp.add(tv1, Fp.ONE); // 3. xd = tv1 + 1 # Nonzero: -1 is square (mod p), tv1 is not
|
||||
let x1n = Fp.neg(ELL2_J); // 4. x1n = -J # x1 = x1n / xd = -J / (1 + 2 * u^2)
|
||||
let tv2 = Fp.sqr(xd); // 5. tv2 = xd^2
|
||||
let gxd = Fp.mul(tv2, xd); // 6. gxd = tv2 * xd # gxd = xd^3
|
||||
let gx1 = Fp.mul(tv1, ELL2_J); // 7. gx1 = J * tv1 # x1n + J * xd
|
||||
gx1 = Fp.mul(gx1, x1n); // 8. gx1 = gx1 * x1n # x1n^2 + J * x1n * xd
|
||||
gx1 = Fp.add(gx1, tv2); // 9. gx1 = gx1 + tv2 # x1n^2 + J * x1n * xd + xd^2
|
||||
gx1 = Fp.mul(gx1, x1n); // 10. gx1 = gx1 * x1n # x1n^3 + J * x1n^2 * xd + x1n * xd^2
|
||||
let tv3 = Fp.sqr(gxd); // 11. tv3 = gxd^2
|
||||
tv2 = Fp.sqr(tv3); // 12. tv2 = tv3^2 # gxd^4
|
||||
tv3 = Fp.mul(tv3, gxd); // 13. tv3 = tv3 * gxd # gxd^3
|
||||
tv3 = Fp.mul(tv3, gx1); // 14. tv3 = tv3 * gx1 # gx1 * gxd^3
|
||||
tv2 = Fp.mul(tv2, tv3); // 15. tv2 = tv2 * tv3 # gx1 * gxd^7
|
||||
let y11 = Fp.pow(tv2, ELL2_C4); // 16. y11 = tv2^c4 # (gx1 * gxd^7)^((p - 5) / 8)
|
||||
y11 = Fp.mul(y11, tv3); // 17. y11 = y11 * tv3 # gx1*gxd^3*(gx1*gxd^7)^((p-5)/8)
|
||||
let y12 = Fp.mul(y11, ELL2_C3); // 18. y12 = y11 * c3
|
||||
tv2 = Fp.sqr(y11); // 19. tv2 = y11^2
|
||||
tv2 = Fp.mul(tv2, gxd); // 20. tv2 = tv2 * gxd
|
||||
let e1 = Fp.eql(tv2, gx1); // 21. e1 = tv2 == gx1
|
||||
let y1 = Fp.cmov(y12, y11, e1); // 22. y1 = CMOV(y12, y11, e1) # If g(x1) is square, this is its sqrt
|
||||
let x2n = Fp.mul(x1n, tv1); // 23. x2n = x1n * tv1 # x2 = x2n / xd = 2 * u^2 * x1n / xd
|
||||
let y21 = Fp.mul(y11, u); // 24. y21 = y11 * u
|
||||
y21 = Fp.mul(y21, ELL2_C2); // 25. y21 = y21 * c2
|
||||
let y22 = Fp.mul(y21, ELL2_C3); // 26. y22 = y21 * c3
|
||||
let gx2 = Fp.mul(gx1, tv1); // 27. gx2 = gx1 * tv1 # g(x2) = gx2 / gxd = 2 * u^2 * g(x1)
|
||||
tv2 = Fp.sqr(y21); // 28. tv2 = y21^2
|
||||
tv2 = Fp.mul(tv2, gxd); // 29. tv2 = tv2 * gxd
|
||||
let e2 = Fp.eql(tv2, gx2); // 30. e2 = tv2 == gx2
|
||||
let y2 = Fp.cmov(y22, y21, e2); // 31. y2 = CMOV(y22, y21, e2) # If g(x2) is square, this is its sqrt
|
||||
tv2 = Fp.sqr(y1); // 32. tv2 = y1^2
|
||||
tv2 = Fp.mul(tv2, gxd); // 33. tv2 = tv2 * gxd
|
||||
let e3 = Fp.eql(tv2, gx1); // 34. e3 = tv2 == gx1
|
||||
let xn = Fp.cmov(x2n, x1n, e3); // 35. xn = CMOV(x2n, x1n, e3) # If e3, x = x1, else x = x2
|
||||
let y = Fp.cmov(y2, y1, e3); // 36. y = CMOV(y2, y1, e3) # If e3, y = y1, else y = y2
|
||||
let e4 = Fp.isOdd(y); // 37. e4 = sgn0(y) == 1 # Fix sign of y
|
||||
y = Fp.cmov(y, Fp.neg(y), e3 !== e4); // 38. y = CMOV(y, -y, e3 XOR e4)
|
||||
return { xMn: xn, xMd: xd, yMn: y, yMd: 1n }; // 39. return (xn, xd, y, 1)
|
||||
}
|
||||
|
||||
const ELL2_C1_EDWARDS = FpSqrtEven(Fp, Fp.neg(BigInt(486664))); // sgn0(c1) MUST equal 0
|
||||
function map_to_curve_elligator2_edwards25519(u: bigint) {
|
||||
const { xMn, xMd, yMn, yMd } = map_to_curve_elligator2_curve25519(u); // 1. (xMn, xMd, yMn, yMd) = map_to_curve_elligator2_curve25519(u)
|
||||
let xn = Fp.mul(xMn, yMd); // 2. xn = xMn * yMd
|
||||
xn = Fp.mul(xn, ELL2_C1_EDWARDS); // 3. xn = xn * c1
|
||||
let xd = Fp.mul(xMd, yMn); // 4. xd = xMd * yMn # xn / xd = c1 * xM / yM
|
||||
let yn = Fp.sub(xMn, xMd); // 5. yn = xMn - xMd
|
||||
let yd = Fp.add(xMn, xMd); // 6. yd = xMn + xMd # (n / d - 1) / (n / d + 1) = (n - d) / (n + d)
|
||||
let tv1 = Fp.mul(xd, yd); // 7. tv1 = xd * yd
|
||||
let e = Fp.eql(tv1, Fp.ZERO); // 8. e = tv1 == 0
|
||||
xn = Fp.cmov(xn, Fp.ZERO, e); // 9. xn = CMOV(xn, 0, e)
|
||||
xd = Fp.cmov(xd, Fp.ONE, e); // 10. xd = CMOV(xd, 1, e)
|
||||
yn = Fp.cmov(yn, Fp.ONE, e); // 11. yn = CMOV(yn, 1, e)
|
||||
yd = Fp.cmov(yd, Fp.ONE, e); // 12. yd = CMOV(yd, 1, e)
|
||||
|
||||
const inv = Fp.invertBatch([xd, yd]); // batch division
|
||||
return { x: Fp.mul(xn, inv[0]), y: Fp.mul(yn, inv[1]) }; // 13. return (xn, xd, yn, yd)
|
||||
}
|
||||
const { hashToCurve, encodeToCurve } = htf.hashToCurve(
|
||||
ed25519.ExtendedPoint,
|
||||
(scalars: bigint[]) => map_to_curve_elligator2_edwards25519(scalars[0]),
|
||||
{
|
||||
DST: 'edwards25519_XMD:SHA-512_ELL2_RO_',
|
||||
encodeDST: 'edwards25519_XMD:SHA-512_ELL2_NU_',
|
||||
p: Fp.ORDER,
|
||||
m: 1,
|
||||
k: 128,
|
||||
expand: 'xmd',
|
||||
hash: sha512,
|
||||
}
|
||||
);
|
||||
export { hashToCurve, encodeToCurve };
|
||||
|
||||
function assertRstPoint(other: unknown) {
|
||||
if (!(other instanceof RistrettoPoint)) throw new TypeError('RistrettoPoint expected');
|
||||
if (!(other instanceof RistrettoPoint)) throw new Error('RistrettoPoint expected');
|
||||
}
|
||||
// √(-1) aka √(a) aka 2^((p-1)/4)
|
||||
const SQRT_M1 = BigInt(
|
||||
@@ -191,16 +266,16 @@ const invertSqrt = (number: bigint) => uvRatio(_1n, number);
|
||||
|
||||
const MAX_255B = BigInt('0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
|
||||
const bytes255ToNumberLE = (bytes: Uint8Array) =>
|
||||
ed25519.utils.mod(bytesToNumberLE(bytes) & MAX_255B);
|
||||
ed25519.CURVE.Fp.create(bytesToNumberLE(bytes) & MAX_255B);
|
||||
|
||||
type ExtendedPoint = ExtendedPointType;
|
||||
type ExtendedPoint = ExtPointType;
|
||||
|
||||
// Computes Elligator map for Ristretto
|
||||
// https://ristretto.group/formulas/elligator.html
|
||||
function calcElligatorRistrettoMap(r0: bigint): ExtendedPoint {
|
||||
const { d } = ed25519.CURVE;
|
||||
const P = ed25519.CURVE.Fp.ORDER;
|
||||
const { mod } = ed25519.utils;
|
||||
const mod = ed25519.CURVE.Fp.create;
|
||||
const r = mod(SQRT_M1 * r0 * r0); // 1
|
||||
const Ns = mod((r + _1n) * ONE_MINUS_D_SQ); // 2
|
||||
let c = BigInt(-1); // 3
|
||||
@@ -258,7 +333,7 @@ export class RistrettoPoint {
|
||||
hex = ensureBytes(hex, 32);
|
||||
const { a, d } = ed25519.CURVE;
|
||||
const P = ed25519.CURVE.Fp.ORDER;
|
||||
const { mod } = ed25519.utils;
|
||||
const mod = ed25519.CURVE.Fp.create;
|
||||
const emsg = 'RistrettoPoint.fromHex: the hex is not valid encoding of RistrettoPoint';
|
||||
const s = bytes255ToNumberLE(hex);
|
||||
// 1. Check that s_bytes is the canonical encoding of a field element, or else abort.
|
||||
@@ -286,9 +361,9 @@ export class RistrettoPoint {
|
||||
* https://ristretto.group/formulas/encoding.html
|
||||
*/
|
||||
toRawBytes(): Uint8Array {
|
||||
let { x, y, z, t } = this.ep;
|
||||
let { ex: x, ey: y, ez: z, et: t } = this.ep;
|
||||
const P = ed25519.CURVE.Fp.ORDER;
|
||||
const { mod } = ed25519.utils;
|
||||
const mod = ed25519.CURVE.Fp.create;
|
||||
const u1 = mod(mod(z + y) * mod(z - y)); // 1
|
||||
const u2 = mod(x * y); // 2
|
||||
// Square root always exists
|
||||
@@ -324,12 +399,12 @@ export class RistrettoPoint {
|
||||
// Compare one point to another.
|
||||
equals(other: RistrettoPoint): boolean {
|
||||
assertRstPoint(other);
|
||||
const a = this.ep;
|
||||
const b = other.ep;
|
||||
const { mod } = ed25519.utils;
|
||||
const { ex: X1, ey: Y1 } = this.ep;
|
||||
const { ex: X2, ey: Y2 } = this.ep;
|
||||
const mod = ed25519.CURVE.Fp.create;
|
||||
// (x1 * y2 == y1 * x2) | (y1 * y2 == x1 * x2)
|
||||
const one = mod(a.x * b.y) === mod(a.y * b.x);
|
||||
const two = mod(a.y * b.y) === mod(a.x * b.x);
|
||||
const one = mod(X1 * Y2) === mod(Y1 * X2);
|
||||
const two = mod(Y1 * Y2) === mod(X1 * X2);
|
||||
return one || two;
|
||||
}
|
||||
|
||||
@@ -343,11 +418,11 @@ export class RistrettoPoint {
|
||||
return new RistrettoPoint(this.ep.subtract(other.ep));
|
||||
}
|
||||
|
||||
multiply(scalar: number | bigint): RistrettoPoint {
|
||||
multiply(scalar: bigint): RistrettoPoint {
|
||||
return new RistrettoPoint(this.ep.multiply(scalar));
|
||||
}
|
||||
|
||||
multiplyUnsafe(scalar: number | bigint): RistrettoPoint {
|
||||
multiplyUnsafe(scalar: bigint): RistrettoPoint {
|
||||
return new RistrettoPoint(this.ep.multiplyUnsafe(scalar));
|
||||
}
|
||||
}
|
||||
|
||||
100
src/ed448.ts
100
src/ed448.ts
@@ -2,8 +2,9 @@
|
||||
import { shake256 } from '@noble/hashes/sha3';
|
||||
import { concatBytes, randomBytes, utf8ToBytes, wrapConstructor } from '@noble/hashes/utils';
|
||||
import { twistedEdwards } from './abstract/edwards.js';
|
||||
import { mod, pow2, Fp } from './abstract/modular.js';
|
||||
import { mod, pow2, Fp as Field } from './abstract/modular.js';
|
||||
import { montgomery } from './abstract/montgomery.js';
|
||||
import * as htf from './abstract/hash-to-curve.js';
|
||||
|
||||
/**
|
||||
* Edwards448 (not Ed448-Goldilocks) curve with following addons:
|
||||
@@ -52,6 +53,8 @@ function adjustScalarBytes(bytes: Uint8Array): Uint8Array {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
const Fp = Field(ed448P, 456, true);
|
||||
|
||||
const ED448_DEF = {
|
||||
// Param: a
|
||||
a: BigInt(1),
|
||||
@@ -60,8 +63,9 @@ const ED448_DEF = {
|
||||
'726838724295606890549323807888004534353641360687318060281490199180612328166730772686396383698676545930088884461843637361053498018326358'
|
||||
),
|
||||
// Finite field 𝔽p over which we'll do calculations; 2n ** 448n - 2n ** 224n - 1n
|
||||
Fp: Fp(ed448P, 456),
|
||||
// Subgroup order: how many points ed448 has; 2n**446n - 13818066809895115352007386748515426880336692474882178609894547503885n
|
||||
Fp,
|
||||
// Subgroup order: how many points curve has;
|
||||
// 2n**446n - 13818066809895115352007386748515426880336692474882178609894547503885n
|
||||
n: BigInt(
|
||||
'181709681073901722637330951972001133588410340171829515070372549795146003961539585716195755291692375963310293709091662304773755859649779'
|
||||
),
|
||||
@@ -145,3 +149,93 @@ export const x448 = montgomery({
|
||||
// return numberToBytesLE(u, 56);
|
||||
// },
|
||||
});
|
||||
|
||||
// Hash To Curve Elligator2 Map
|
||||
const ELL2_C1 = (Fp.ORDER - BigInt(3)) / BigInt(4); // 1. c1 = (q - 3) / 4 # Integer arithmetic
|
||||
const ELL2_J = BigInt(156326);
|
||||
function map_to_curve_elligator2_curve448(u: bigint) {
|
||||
let tv1 = Fp.sqr(u); // 1. tv1 = u^2
|
||||
let e1 = Fp.eql(tv1, Fp.ONE); // 2. e1 = tv1 == 1
|
||||
tv1 = Fp.cmov(tv1, Fp.ZERO, e1); // 3. tv1 = CMOV(tv1, 0, e1) # If Z * u^2 == -1, set tv1 = 0
|
||||
let xd = Fp.sub(Fp.ONE, tv1); // 4. xd = 1 - tv1
|
||||
let x1n = Fp.neg(ELL2_J); // 5. x1n = -J
|
||||
let tv2 = Fp.sqr(xd); // 6. tv2 = xd^2
|
||||
let gxd = Fp.mul(tv2, xd); // 7. gxd = tv2 * xd # gxd = xd^3
|
||||
let gx1 = Fp.mul(tv1, Fp.neg(ELL2_J)); // 8. gx1 = -J * tv1 # x1n + J * xd
|
||||
gx1 = Fp.mul(gx1, x1n); // 9. gx1 = gx1 * x1n # x1n^2 + J * x1n * xd
|
||||
gx1 = Fp.add(gx1, tv2); // 10. gx1 = gx1 + tv2 # x1n^2 + J * x1n * xd + xd^2
|
||||
gx1 = Fp.mul(gx1, x1n); // 11. gx1 = gx1 * x1n # x1n^3 + J * x1n^2 * xd + x1n * xd^2
|
||||
let tv3 = Fp.sqr(gxd); // 12. tv3 = gxd^2
|
||||
tv2 = Fp.mul(gx1, gxd); // 13. tv2 = gx1 * gxd # gx1 * gxd
|
||||
tv3 = Fp.mul(tv3, tv2); // 14. tv3 = tv3 * tv2 # gx1 * gxd^3
|
||||
let y1 = Fp.pow(tv3, ELL2_C1); // 15. y1 = tv3^c1 # (gx1 * gxd^3)^((p - 3) / 4)
|
||||
y1 = Fp.mul(y1, tv2); // 16. y1 = y1 * tv2 # gx1 * gxd * (gx1 * gxd^3)^((p - 3) / 4)
|
||||
let x2n = Fp.mul(x1n, Fp.neg(tv1)); // 17. x2n = -tv1 * x1n # x2 = x2n / xd = -1 * u^2 * x1n / xd
|
||||
let y2 = Fp.mul(y1, u); // 18. y2 = y1 * u
|
||||
y2 = Fp.cmov(y2, Fp.ZERO, e1); // 19. y2 = CMOV(y2, 0, e1)
|
||||
tv2 = Fp.sqr(y1); // 20. tv2 = y1^2
|
||||
tv2 = Fp.mul(tv2, gxd); // 21. tv2 = tv2 * gxd
|
||||
let e2 = Fp.eql(tv2, gx1); // 22. e2 = tv2 == gx1
|
||||
let xn = Fp.cmov(x2n, x1n, e2); // 23. xn = CMOV(x2n, x1n, e2) # If e2, x = x1, else x = x2
|
||||
let y = Fp.cmov(y2, y1, e2); // 24. y = CMOV(y2, y1, e2) # If e2, y = y1, else y = y2
|
||||
let e3 = Fp.isOdd(y); // 25. e3 = sgn0(y) == 1 # Fix sign of y
|
||||
y = Fp.cmov(y, Fp.neg(y), e2 !== e3); // 26. y = CMOV(y, -y, e2 XOR e3)
|
||||
return { xn, xd, yn: y, yd: Fp.ONE }; // 27. return (xn, xd, y, 1)
|
||||
}
|
||||
function map_to_curve_elligator2_edwards448(u: bigint) {
|
||||
let { xn, xd, yn, yd } = map_to_curve_elligator2_curve448(u); // 1. (xn, xd, yn, yd) = map_to_curve_elligator2_curve448(u)
|
||||
let xn2 = Fp.sqr(xn); // 2. xn2 = xn^2
|
||||
let xd2 = Fp.sqr(xd); // 3. xd2 = xd^2
|
||||
let xd4 = Fp.sqr(xd2); // 4. xd4 = xd2^2
|
||||
let yn2 = Fp.sqr(yn); // 5. yn2 = yn^2
|
||||
let yd2 = Fp.sqr(yd); // 6. yd2 = yd^2
|
||||
let xEn = Fp.sub(xn2, xd2); // 7. xEn = xn2 - xd2
|
||||
let tv2 = Fp.sub(xEn, xd2); // 8. tv2 = xEn - xd2
|
||||
xEn = Fp.mul(xEn, xd2); // 9. xEn = xEn * xd2
|
||||
xEn = Fp.mul(xEn, yd); // 10. xEn = xEn * yd
|
||||
xEn = Fp.mul(xEn, yn); // 11. xEn = xEn * yn
|
||||
xEn = Fp.mul(xEn, 4n); // 12. xEn = xEn * 4
|
||||
tv2 = Fp.mul(tv2, xn2); // 13. tv2 = tv2 * xn2
|
||||
tv2 = Fp.mul(tv2, yd2); // 14. tv2 = tv2 * yd2
|
||||
let tv3 = Fp.mul(yn2, 4n); // 15. tv3 = 4 * yn2
|
||||
let tv1 = Fp.add(tv3, yd2); // 16. tv1 = tv3 + yd2
|
||||
tv1 = Fp.mul(tv1, xd4); // 17. tv1 = tv1 * xd4
|
||||
let xEd = Fp.add(tv1, tv2); // 18. xEd = tv1 + tv2
|
||||
tv2 = Fp.mul(tv2, xn); // 19. tv2 = tv2 * xn
|
||||
let tv4 = Fp.mul(xn, xd4); // 20. tv4 = xn * xd4
|
||||
let yEn = Fp.sub(tv3, yd2); // 21. yEn = tv3 - yd2
|
||||
yEn = Fp.mul(yEn, tv4); // 22. yEn = yEn * tv4
|
||||
yEn = Fp.sub(yEn, tv2); // 23. yEn = yEn - tv2
|
||||
tv1 = Fp.add(xn2, xd2); // 24. tv1 = xn2 + xd2
|
||||
tv1 = Fp.mul(tv1, xd2); // 25. tv1 = tv1 * xd2
|
||||
tv1 = Fp.mul(tv1, xd); // 26. tv1 = tv1 * xd
|
||||
tv1 = Fp.mul(tv1, yn2); // 27. tv1 = tv1 * yn2
|
||||
tv1 = Fp.mul(tv1, BigInt(-2)); // 28. tv1 = -2 * tv1
|
||||
let yEd = Fp.add(tv2, tv1); // 29. yEd = tv2 + tv1
|
||||
tv4 = Fp.mul(tv4, yd2); // 30. tv4 = tv4 * yd2
|
||||
yEd = Fp.add(yEd, tv4); // 31. yEd = yEd + tv4
|
||||
tv1 = Fp.mul(xEd, yEd); // 32. tv1 = xEd * yEd
|
||||
let e = Fp.eql(tv1, Fp.ZERO); // 33. e = tv1 == 0
|
||||
xEn = Fp.cmov(xEn, Fp.ZERO, e); // 34. xEn = CMOV(xEn, 0, e)
|
||||
xEd = Fp.cmov(xEd, Fp.ONE, e); // 35. xEd = CMOV(xEd, 1, e)
|
||||
yEn = Fp.cmov(yEn, Fp.ONE, e); // 36. yEn = CMOV(yEn, 1, e)
|
||||
yEd = Fp.cmov(yEd, Fp.ONE, e); // 37. yEd = CMOV(yEd, 1, e)
|
||||
|
||||
const inv = Fp.invertBatch([xEd, yEd]); // batch division
|
||||
return { x: Fp.mul(xEn, inv[0]), y: Fp.mul(yEn, inv[1]) }; // 38. return (xEn, xEd, yEn, yEd)
|
||||
}
|
||||
|
||||
const { hashToCurve, encodeToCurve } = htf.hashToCurve(
|
||||
ed448.ExtendedPoint,
|
||||
(scalars: bigint[]) => map_to_curve_elligator2_edwards448(scalars[0]),
|
||||
{
|
||||
DST: 'edwards448_XOF:SHAKE256_ELL2_RO_',
|
||||
encodeDST: 'edwards448_XOF:SHAKE256_ELL2_NU_',
|
||||
p: Fp.ORDER,
|
||||
m: 1,
|
||||
k: 224,
|
||||
expand: 'xof',
|
||||
hash: shake256,
|
||||
}
|
||||
);
|
||||
export { hashToCurve, encodeToCurve };
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
import { sha256 } from '@noble/hashes/sha256';
|
||||
import { sha512 } from '@noble/hashes/sha512';
|
||||
import { concatBytes, randomBytes, utf8ToBytes } from '@noble/hashes/utils';
|
||||
import { twistedEdwards } from './abstract/edwards.js';
|
||||
import { blake2s } from '@noble/hashes/blake2s';
|
||||
@@ -8,6 +8,7 @@ import { Fp } from './abstract/modular.js';
|
||||
/**
|
||||
* jubjub Twisted Edwards curve.
|
||||
* https://neuromancer.sk/std/other/JubJub
|
||||
* jubjub does not use EdDSA, so `hash`/sha512 params are passed because interface expects them.
|
||||
*/
|
||||
|
||||
export const jubjub = twistedEdwards({
|
||||
@@ -15,16 +16,16 @@ export const jubjub = twistedEdwards({
|
||||
a: BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000'),
|
||||
d: BigInt('0x2a9318e74bfa2b48f5fd9207e6bd7fd4292d7f6d37579d2601065fd6d6343eb1'),
|
||||
// Finite field 𝔽p over which we'll do calculations
|
||||
// Same value as bls12-381 Fr (not Fp)
|
||||
Fp: Fp(BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001')),
|
||||
// Subgroup order: how many points ed25519 has
|
||||
// 2n ** 252n + 27742317777372353535851937790883648493n;
|
||||
// Subgroup order: how many points curve has
|
||||
n: BigInt('0xe7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7'),
|
||||
// Cofactor
|
||||
h: BigInt(8),
|
||||
// Base point (x, y) aka generator point
|
||||
Gx: BigInt('0x11dafe5d23e1218086a365b99fbf3d3be72f6afd7d1f72623e6b071492d1122b'),
|
||||
Gy: BigInt('0x1d523cf1ddab1a1793132e78c866c0c33e26ba5cc220fed7cc3f870e59d292aa'),
|
||||
hash: sha256,
|
||||
hash: sha512,
|
||||
randomBytes,
|
||||
} as const);
|
||||
|
||||
@@ -38,7 +39,7 @@ export function groupHash(tag: Uint8Array, personalization: Uint8Array) {
|
||||
h.update(GH_FIRST_BLOCK);
|
||||
h.update(tag);
|
||||
// NOTE: returns ExtendedPoint, in case it will be multiplied later
|
||||
let p = jubjub.ExtendedPoint.fromAffine(jubjub.Point.fromHex(h.digest()));
|
||||
let p = jubjub.ExtendedPoint.fromHex(h.digest());
|
||||
// NOTE: cannot replace with isSmallOrder, returns Point*8
|
||||
p = p.multiply(jubjub.CURVE.h);
|
||||
if (p.equals(jubjub.ExtendedPoint.ZERO)) throw new Error('Point has small order');
|
||||
|
||||
@@ -10,7 +10,7 @@ export const P224 = createCurve(
|
||||
// Params: a, b
|
||||
a: BigInt('0xfffffffffffffffffffffffffffffffefffffffffffffffffffffffe'),
|
||||
b: BigInt('0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4'),
|
||||
// Field over which we'll do calculations; 2n**224n - 2n**96n + 1n
|
||||
// Field over which we'll do calculations;
|
||||
Fp: Fp(BigInt('0xffffffffffffffffffffffffffffffff000000000000000000000001')),
|
||||
// Curve order, total count of valid points in the field
|
||||
n: BigInt('0xffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d'),
|
||||
|
||||
25
src/p256.ts
25
src/p256.ts
@@ -3,6 +3,7 @@ import { createCurve } from './_shortw_utils.js';
|
||||
import { sha256 } from '@noble/hashes/sha256';
|
||||
import { Fp as Field } from './abstract/modular.js';
|
||||
import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
|
||||
import * as htf from './abstract/hash-to-curve.js';
|
||||
|
||||
// NIST secp256r1 aka P256
|
||||
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/nist/P-256
|
||||
@@ -31,16 +32,22 @@ export const P256 = createCurve(
|
||||
Gy: BigInt('0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5'),
|
||||
h: BigInt(1),
|
||||
lowS: false,
|
||||
mapToCurve: (scalars: bigint[]) => mapSWU(scalars[0]),
|
||||
htfDefaults: {
|
||||
DST: 'P256_XMD:SHA-256_SSWU_RO_',
|
||||
p: Fp.ORDER,
|
||||
m: 1,
|
||||
k: 128,
|
||||
expand: true,
|
||||
hash: sha256,
|
||||
},
|
||||
} as const,
|
||||
sha256
|
||||
);
|
||||
export const secp256r1 = P256;
|
||||
|
||||
const { hashToCurve, encodeToCurve } = htf.hashToCurve(
|
||||
secp256r1.ProjectivePoint,
|
||||
(scalars: bigint[]) => mapSWU(scalars[0]),
|
||||
{
|
||||
DST: 'P256_XMD:SHA-256_SSWU_RO_',
|
||||
encodeDST: 'P256_XMD:SHA-256_SSWU_NU_',
|
||||
p: Fp.ORDER,
|
||||
m: 1,
|
||||
k: 128,
|
||||
expand: 'xmd',
|
||||
hash: sha256,
|
||||
}
|
||||
);
|
||||
export { hashToCurve, encodeToCurve };
|
||||
|
||||
25
src/p384.ts
25
src/p384.ts
@@ -3,6 +3,7 @@ import { createCurve } from './_shortw_utils.js';
|
||||
import { sha384 } from '@noble/hashes/sha512';
|
||||
import { Fp as Field } from './abstract/modular.js';
|
||||
import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
|
||||
import * as htf from './abstract/hash-to-curve.js';
|
||||
|
||||
// NIST secp384r1 aka P384
|
||||
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/nist/P-384
|
||||
@@ -35,16 +36,22 @@ export const P384 = createCurve({
|
||||
Gy: BigInt('0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f'),
|
||||
h: BigInt(1),
|
||||
lowS: false,
|
||||
mapToCurve: (scalars: bigint[]) => mapSWU(scalars[0]),
|
||||
htfDefaults: {
|
||||
DST: 'P384_XMD:SHA-384_SSWU_RO_',
|
||||
p: Fp.ORDER,
|
||||
m: 1,
|
||||
k: 192,
|
||||
expand: true,
|
||||
hash: sha384,
|
||||
},
|
||||
} as const,
|
||||
sha384
|
||||
);
|
||||
export const secp384r1 = P384;
|
||||
|
||||
const { hashToCurve, encodeToCurve } = htf.hashToCurve(
|
||||
secp384r1.ProjectivePoint,
|
||||
(scalars: bigint[]) => mapSWU(scalars[0]),
|
||||
{
|
||||
DST: 'P384_XMD:SHA-384_SSWU_RO_',
|
||||
encodeDST: 'P384_XMD:SHA-384_SSWU_NU_',
|
||||
p: Fp.ORDER,
|
||||
m: 1,
|
||||
k: 192,
|
||||
expand: 'xmd',
|
||||
hash: sha384,
|
||||
}
|
||||
);
|
||||
export { hashToCurve, encodeToCurve };
|
||||
|
||||
32
src/p521.ts
32
src/p521.ts
@@ -1,9 +1,9 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
import { createCurve } from './_shortw_utils.js';
|
||||
import { sha512 } from '@noble/hashes/sha512';
|
||||
import { bytesToHex, PrivKey } from './abstract/utils.js';
|
||||
import { Fp as Field } from './abstract/modular.js';
|
||||
import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
|
||||
import * as htf from './abstract/hash-to-curve.js';
|
||||
|
||||
// NIST secp521r1 aka P521
|
||||
// Note that it's 521, which differs from 512 of its hash function.
|
||||
@@ -37,25 +37,21 @@ export const P521 = createCurve({
|
||||
Gy: BigInt('0x011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650'),
|
||||
h: BigInt(1),
|
||||
lowS: false,
|
||||
// P521 keys could be 130, 131, 132 bytes - which doesn't play nicely.
|
||||
// We ensure all keys are 132 bytes.
|
||||
// Does not replace validation; invalid keys would still be rejected.
|
||||
normalizePrivateKey(key: PrivKey) {
|
||||
if (typeof key === 'bigint') return key;
|
||||
if (key instanceof Uint8Array) key = bytesToHex(key);
|
||||
if (typeof key !== 'string' || !([130, 131, 132].includes(key.length))) {
|
||||
throw new Error('Invalid key');
|
||||
}
|
||||
return key.padStart(66 * 2, '0');
|
||||
},
|
||||
mapToCurve: (scalars: bigint[]) => mapSWU(scalars[0]),
|
||||
htfDefaults: {
|
||||
allowedPrivateKeyLengths: [130, 131, 132] // P521 keys are variable-length. Normalize to 132b
|
||||
} as const, sha512);
|
||||
export const secp521r1 = P521;
|
||||
|
||||
const { hashToCurve, encodeToCurve } = htf.hashToCurve(
|
||||
secp521r1.ProjectivePoint,
|
||||
(scalars: bigint[]) => mapSWU(scalars[0]),
|
||||
{
|
||||
DST: 'P521_XMD:SHA-512_SSWU_RO_',
|
||||
encodeDST: 'P521_XMD:SHA-512_SSWU_NU_',
|
||||
p: Fp.ORDER,
|
||||
m: 1,
|
||||
k: 256,
|
||||
expand: true,
|
||||
expand: 'xmd',
|
||||
hash: sha512,
|
||||
},
|
||||
} as const, sha512);
|
||||
export const secp521r1 = P521;
|
||||
}
|
||||
);
|
||||
export { hashToCurve, encodeToCurve };
|
||||
|
||||
417
src/secp256k1.ts
417
src/secp256k1.ts
@@ -2,23 +2,21 @@
|
||||
import { sha256 } from '@noble/hashes/sha256';
|
||||
import { Fp as Field, mod, pow2 } from './abstract/modular.js';
|
||||
import { createCurve } from './_shortw_utils.js';
|
||||
import { PointType, mapToCurveSimpleSWU } from './abstract/weierstrass.js';
|
||||
import { ProjPointType as PointType, mapToCurveSimpleSWU } from './abstract/weierstrass.js';
|
||||
import {
|
||||
ensureBytes,
|
||||
concatBytes,
|
||||
Hex,
|
||||
hexToBytes,
|
||||
bytesToNumberBE,
|
||||
bytesToNumberBE as bytesToInt,
|
||||
PrivKey,
|
||||
numberToBytesBE,
|
||||
} from './abstract/utils.js';
|
||||
import { randomBytes } from '@noble/hashes/utils';
|
||||
import { isogenyMap } from './abstract/hash-to-curve.js';
|
||||
import * as htf from './abstract/hash-to-curve.js';
|
||||
|
||||
/**
|
||||
* 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%.
|
||||
* secp256k1 belongs to Koblitz curves: it has efficiently computable endomorphism.
|
||||
* Endomorphism uses 2x less RAM, speeds up precomputation by 2x and ECDH / key recovery by 20%.
|
||||
* Should always be used for Projective'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
|
||||
@@ -31,10 +29,7 @@ const _2n = BigInt(2);
|
||||
const divNearest = (a: bigint, b: bigint) => (a + b / _2n) / b;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* √n = n^((p+1)/4) for fields p = 3 mod 4. We unwrap the loop and multiply bit-by-bit.
|
||||
* (P+1n/4n).toString(2) would produce bits [223x 1, 0, 22x 1, 4x 0, 11, 00]
|
||||
*/
|
||||
function sqrtMod(y: bigint): bigint {
|
||||
@@ -56,13 +51,178 @@ function sqrtMod(y: bigint): bigint {
|
||||
const b223 = (pow2(b220, _3n, P) * b3) % P;
|
||||
const t1 = (pow2(b223, _23n, P) * b22) % P;
|
||||
const t2 = (pow2(t1, _6n, P) * b2) % P;
|
||||
return pow2(t2, _2n, P);
|
||||
const root = pow2(t2, _2n, P);
|
||||
if (!Fp.eql(Fp.sqr(root), y)) throw new Error('Cannot find square root');
|
||||
return root;
|
||||
}
|
||||
|
||||
const Fp = Field(secp256k1P, undefined, undefined, { sqrt: sqrtMod });
|
||||
type Fp = bigint;
|
||||
|
||||
const isoMap = isogenyMap(
|
||||
export const secp256k1 = createCurve(
|
||||
{
|
||||
// Params: a, b
|
||||
// Seem to be rigid https://bitcointalk.org/index.php?topic=289795.msg3183975#msg3183975
|
||||
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
|
||||
Fp,
|
||||
// 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),
|
||||
// Alllow only low-S signatures by default in sign() and verify()
|
||||
lowS: true,
|
||||
endo: {
|
||||
// Params taken from https://gist.github.com/paulmillr/eb670806793e84df628a7c434a873066
|
||||
beta: BigInt('0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee'),
|
||||
splitScalar: (k: bigint) => {
|
||||
const n = secp256k1N;
|
||||
const a1 = BigInt('0x3086d221a7d46bcde86c90e49284eb15');
|
||||
const b1 = -_1n * BigInt('0xe4437ed6010e88286f547fa90abfe4c3');
|
||||
const a2 = BigInt('0x114ca50f7a8e2f3f657c1108d9d44cfd8');
|
||||
const b2 = a1;
|
||||
const POW_2_128 = BigInt('0x100000000000000000000000000000000'); // (2n**128n).toString(16)
|
||||
|
||||
const c1 = divNearest(b2 * k, n);
|
||||
const c2 = divNearest(-b1 * k, n);
|
||||
let k1 = mod(k - c1 * a1 - c2 * a2, n);
|
||||
let k2 = mod(-c1 * b1 - c2 * b2, n);
|
||||
const k1neg = k1 > POW_2_128;
|
||||
const k2neg = k2 > POW_2_128;
|
||||
if (k1neg) k1 = n - k1;
|
||||
if (k2neg) k2 = n - k2;
|
||||
if (k1 > POW_2_128 || k2 > POW_2_128) {
|
||||
throw new Error('splitScalar: Endomorphism failed, k=' + k);
|
||||
}
|
||||
return { k1neg, k1, k2neg, k2 };
|
||||
},
|
||||
},
|
||||
},
|
||||
sha256
|
||||
);
|
||||
|
||||
// Schnorr signatures are superior to ECDSA from above.
|
||||
// Below is Schnorr-specific code as per BIP0340.
|
||||
// https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki
|
||||
const _0n = BigInt(0);
|
||||
const fe = (x: bigint) => typeof x === 'bigint' && _0n < x && x < secp256k1P;
|
||||
const ge = (x: bigint) => typeof x === 'bigint' && _0n < x && x < secp256k1N;
|
||||
|
||||
const TAGS = {
|
||||
challenge: 'BIP0340/challenge',
|
||||
aux: 'BIP0340/aux',
|
||||
nonce: 'BIP0340/nonce',
|
||||
} as const;
|
||||
|
||||
/** An object mapping tags to their tagged hash prefix of [SHA256(tag) | SHA256(tag)] */
|
||||
const TAGGED_HASH_PREFIXES: { [tag: string]: Uint8Array } = {};
|
||||
function taggedHash(tag: string, ...messages: Uint8Array[]): Uint8Array {
|
||||
let tagP = TAGGED_HASH_PREFIXES[tag];
|
||||
if (tagP === undefined) {
|
||||
const tagH = sha256(Uint8Array.from(tag, (c) => c.charCodeAt(0)));
|
||||
tagP = concatBytes(tagH, tagH);
|
||||
TAGGED_HASH_PREFIXES[tag] = tagP;
|
||||
}
|
||||
return sha256(concatBytes(tagP, ...messages));
|
||||
}
|
||||
|
||||
const pointToBytes = (point: PointType<bigint>) => point.toRawBytes(true).slice(1);
|
||||
const numTo32b = (n: bigint) => numberToBytesBE(n, 32);
|
||||
const modN = (x: bigint) => mod(x, secp256k1N);
|
||||
const Point = secp256k1.ProjectivePoint;
|
||||
const GmulAdd = (Q: PointType<bigint>, a: bigint, b: bigint) =>
|
||||
Point.BASE.multiplyAndAddUnsafe(Q, a, b);
|
||||
const hex32ToInt = (key: Hex) => bytesToInt(ensureBytes(key, 32));
|
||||
function schnorrGetExtPubKey(priv: PrivKey) {
|
||||
let d = typeof priv === 'bigint' ? priv : hex32ToInt(priv);
|
||||
const point = Point.fromPrivateKey(d); // P = d'⋅G; 0 < d' < n check is done inside
|
||||
const scalar = point.hasEvenY() ? d : modN(-d); // d = d' if has_even_y(P), otherwise d = n-d'
|
||||
return { point, scalar, bytes: pointToBytes(point) };
|
||||
}
|
||||
function lift_x(x: bigint): PointType<bigint> {
|
||||
if (!fe(x)) throw new Error('bad x: need 0 < x < p'); // Fail if x ≥ p.
|
||||
const c = mod(x * x * x + BigInt(7), secp256k1P); // Let c = x³ + 7 mod p.
|
||||
let y = sqrtMod(c); // Let y = c^(p+1)/4 mod p.
|
||||
if (y % 2n !== 0n) y = mod(-y, secp256k1P); // Return the unique point P such that x(P) = x and
|
||||
const p = new Point(x, y, _1n); // y(P) = y if y mod 2 = 0 or y(P) = p-y otherwise.
|
||||
p.assertValidity();
|
||||
return p;
|
||||
}
|
||||
function challenge(...args: Uint8Array[]): bigint {
|
||||
return modN(bytesToInt(taggedHash(TAGS.challenge, ...args)));
|
||||
}
|
||||
|
||||
// Schnorr's pubkey is just `x` of Point (BIP340)
|
||||
function schnorrGetPublicKey(privateKey: Hex): Uint8Array {
|
||||
return schnorrGetExtPubKey(privateKey).bytes; // d'=int(sk). Fail if d'=0 or d'≥n. Ret bytes(d'⋅G)
|
||||
}
|
||||
|
||||
// Creates Schnorr signature as per BIP340. Verifies itself before returning anything.
|
||||
// auxRand is optional and is not the sole source of k generation: bad CSPRNG won't be dangerous
|
||||
function schnorrSign(
|
||||
message: Hex,
|
||||
privateKey: PrivKey,
|
||||
auxRand: Hex = randomBytes(32)
|
||||
): Uint8Array {
|
||||
if (message == null) throw new Error(`sign: Expected valid message, not "${message}"`);
|
||||
const m = ensureBytes(message); // checks for isWithinCurveOrder
|
||||
const { bytes: px, scalar: d } = schnorrGetExtPubKey(privateKey);
|
||||
const a = ensureBytes(auxRand, 32); // Auxiliary random data a: a 32-byte array
|
||||
const t = numTo32b(d ^ bytesToInt(taggedHash(TAGS.aux, a))); // Let t be the byte-wise xor of bytes(d) and hash/aux(a)
|
||||
const rand = taggedHash(TAGS.nonce, t, px, m); // Let rand = hash/nonce(t || bytes(P) || m)
|
||||
const k_ = modN(bytesToInt(rand)); // Let k' = int(rand) mod n
|
||||
if (k_ === _0n) throw new Error('sign failed: k is zero'); // Fail if k' = 0.
|
||||
const { point: R, bytes: rx, scalar: k } = schnorrGetExtPubKey(k_); // Let R = k'⋅G.
|
||||
const e = challenge(rx, px, m); // Let e = int(hash/challenge(bytes(R) || bytes(P) || m)) mod n.
|
||||
const sig = new Uint8Array(64); // Let sig = bytes(R) || bytes((k + ed) mod n).
|
||||
sig.set(numTo32b(R.px), 0);
|
||||
sig.set(numTo32b(modN(k + e * d)), 32);
|
||||
// If Verify(bytes(P), m, sig) (see below) returns failure, abort
|
||||
if (!schnorrVerify(sig, m, px)) throw new Error('sign: Invalid signature produced');
|
||||
return sig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies Schnorr signature synchronously.
|
||||
*/
|
||||
function schnorrVerify(signature: Hex, message: Hex, publicKey: Hex): boolean {
|
||||
try {
|
||||
const P = lift_x(hex32ToInt(publicKey)); // P = lift_x(int(pk)); fail if that fails
|
||||
const sig = ensureBytes(signature, 64);
|
||||
const r = bytesToInt(sig.subarray(0, 32)); // Let r = int(sig[0:32]); fail if r ≥ p.
|
||||
if (!fe(r)) return false;
|
||||
const s = bytesToInt(sig.subarray(32, 64)); // Let s = int(sig[32:64]); fail if s ≥ n.
|
||||
if (!ge(s)) return false;
|
||||
const m = ensureBytes(message);
|
||||
const e = challenge(numTo32b(r), pointToBytes(P), m); // int(challenge(bytes(r)||bytes(P)||m)) mod n
|
||||
const R = GmulAdd(P, s, modN(-e)); // R = s⋅G - e⋅P
|
||||
if (!R || !R.hasEvenY() || R.toAffine().x !== r) return false; // -eP == (n-e)P
|
||||
return true; // Fail if is_infinite(R) / not has_even_y(R) / x(R) ≠ r.
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export const schnorr = {
|
||||
getPublicKey: schnorrGetPublicKey,
|
||||
sign: schnorrSign,
|
||||
verify: schnorrVerify,
|
||||
utils: {
|
||||
getExtendedPublicKey: schnorrGetExtPubKey,
|
||||
lift_x,
|
||||
pointToBytes,
|
||||
numberToBytesBE,
|
||||
bytesToNumberBE: bytesToInt,
|
||||
taggedHash,
|
||||
mod,
|
||||
},
|
||||
};
|
||||
|
||||
const isoMap = htf.isogenyMap(
|
||||
Fp,
|
||||
[
|
||||
// xNum
|
||||
@@ -94,224 +254,25 @@ const isoMap = isogenyMap(
|
||||
],
|
||||
].map((i) => i.map((j) => BigInt(j))) as [Fp[], Fp[], Fp[], Fp[]]
|
||||
);
|
||||
|
||||
const mapSWU = mapToCurveSimpleSWU(Fp, {
|
||||
A: BigInt('0x3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533'),
|
||||
B: BigInt('1771'),
|
||||
Z: Fp.create(BigInt('-11')),
|
||||
});
|
||||
|
||||
export const secp256k1 = createCurve(
|
||||
{
|
||||
// Params: a, b
|
||||
// Seem to be rigid https://bitcointalk.org/index.php?topic=289795.msg3183975#msg3183975
|
||||
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
|
||||
Fp,
|
||||
// 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),
|
||||
// Alllow only low-S signatures by default in sign() and verify()
|
||||
lowS: true,
|
||||
endo: {
|
||||
// Params taken from https://gist.github.com/paulmillr/eb670806793e84df628a7c434a873066
|
||||
beta: BigInt('0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee'),
|
||||
splitScalar: (k: bigint) => {
|
||||
const n = secp256k1N;
|
||||
const a1 = BigInt('0x3086d221a7d46bcde86c90e49284eb15');
|
||||
const b1 = -_1n * BigInt('0xe4437ed6010e88286f547fa90abfe4c3');
|
||||
const a2 = BigInt('0x114ca50f7a8e2f3f657c1108d9d44cfd8');
|
||||
const b2 = a1;
|
||||
const POW_2_128 = BigInt('0x100000000000000000000000000000000');
|
||||
|
||||
const c1 = divNearest(b2 * k, n);
|
||||
const c2 = divNearest(-b1 * k, n);
|
||||
let k1 = mod(k - c1 * a1 - c2 * a2, n);
|
||||
let k2 = mod(-c1 * b1 - c2 * b2, n);
|
||||
const k1neg = k1 > POW_2_128;
|
||||
const k2neg = k2 > POW_2_128;
|
||||
if (k1neg) k1 = n - k1;
|
||||
if (k2neg) k2 = n - k2;
|
||||
if (k1 > POW_2_128 || k2 > POW_2_128) {
|
||||
throw new Error('splitScalar: Endomorphism failed, k=' + k);
|
||||
}
|
||||
return { k1neg, k1, k2neg, k2 };
|
||||
},
|
||||
},
|
||||
mapToCurve: (scalars: bigint[]) => {
|
||||
const { x, y } = mapSWU(Fp.create(scalars[0]));
|
||||
return isoMap(x, y);
|
||||
},
|
||||
htfDefaults: {
|
||||
DST: 'secp256k1_XMD:SHA-256_SSWU_RO_',
|
||||
p: Fp.ORDER,
|
||||
m: 1,
|
||||
k: 128,
|
||||
expand: true,
|
||||
hash: sha256,
|
||||
},
|
||||
const { hashToCurve, encodeToCurve } = htf.hashToCurve(
|
||||
secp256k1.ProjectivePoint,
|
||||
(scalars: bigint[]) => {
|
||||
const { x, y } = mapSWU(Fp.create(scalars[0]));
|
||||
return isoMap(x, y);
|
||||
},
|
||||
sha256
|
||||
{
|
||||
DST: 'secp256k1_XMD:SHA-256_SSWU_RO_',
|
||||
encodeDST: 'secp256k1_XMD:SHA-256_SSWU_NU_',
|
||||
p: Fp.ORDER,
|
||||
m: 1,
|
||||
k: 128,
|
||||
expand: 'xmd',
|
||||
hash: sha256,
|
||||
}
|
||||
);
|
||||
|
||||
// Schnorr
|
||||
const _0n = BigInt(0);
|
||||
const numTo32b = secp256k1.utils._bigintToBytes;
|
||||
const numTo32bStr = secp256k1.utils._bigintToString;
|
||||
const normalizePrivateKey = secp256k1.utils._normalizePrivateKey;
|
||||
|
||||
// TODO: export?
|
||||
function normalizePublicKey(publicKey: Hex | PointType<bigint>): PointType<bigint> {
|
||||
if (publicKey instanceof secp256k1.Point) {
|
||||
publicKey.assertValidity();
|
||||
return publicKey;
|
||||
} else {
|
||||
const bytes = ensureBytes(publicKey);
|
||||
// Schnorr is 32 bytes
|
||||
if (bytes.length === 32) {
|
||||
const x = bytesToNumberBE(bytes);
|
||||
if (!isValidFieldElement(x)) throw new Error('Point is not on curve');
|
||||
const y2 = secp256k1.utils._weierstrassEquation(x); // y² = x³ + ax + b
|
||||
let y = sqrtMod(y2); // y = y² ^ (p+1)/4
|
||||
const isYOdd = (y & _1n) === _1n;
|
||||
// Schnorr
|
||||
if (isYOdd) y = secp256k1.CURVE.Fp.negate(y);
|
||||
const point = new secp256k1.Point(x, y);
|
||||
point.assertValidity();
|
||||
return point;
|
||||
}
|
||||
// Do we need that in schnorr at all?
|
||||
return secp256k1.Point.fromHex(publicKey);
|
||||
}
|
||||
}
|
||||
|
||||
const isWithinCurveOrder = secp256k1.utils._isWithinCurveOrder;
|
||||
const isValidFieldElement = secp256k1.utils._isValidFieldElement;
|
||||
|
||||
const TAGS = {
|
||||
challenge: 'BIP0340/challenge',
|
||||
aux: 'BIP0340/aux',
|
||||
nonce: 'BIP0340/nonce',
|
||||
} as const;
|
||||
|
||||
/** An object mapping tags to their tagged hash prefix of [SHA256(tag) | SHA256(tag)] */
|
||||
const TAGGED_HASH_PREFIXES: { [tag: string]: Uint8Array } = {};
|
||||
export function taggedHash(tag: string, ...messages: Uint8Array[]): Uint8Array {
|
||||
let tagP = TAGGED_HASH_PREFIXES[tag];
|
||||
if (tagP === undefined) {
|
||||
const tagH = sha256(Uint8Array.from(tag, (c) => c.charCodeAt(0)));
|
||||
tagP = concatBytes(tagH, tagH);
|
||||
TAGGED_HASH_PREFIXES[tag] = tagP;
|
||||
}
|
||||
return sha256(concatBytes(tagP, ...messages));
|
||||
}
|
||||
|
||||
const toRawX = (point: PointType<bigint>) => point.toRawBytes(true).slice(1);
|
||||
|
||||
// Schnorr signatures are superior to ECDSA from above.
|
||||
// Below is Schnorr-specific code as per BIP0340.
|
||||
function schnorrChallengeFinalize(ch: Uint8Array): bigint {
|
||||
return mod(bytesToNumberBE(ch), secp256k1.CURVE.n);
|
||||
}
|
||||
// Do we need this at all for Schnorr?
|
||||
class SchnorrSignature {
|
||||
constructor(readonly r: bigint, readonly s: bigint) {
|
||||
this.assertValidity();
|
||||
}
|
||||
static fromHex(hex: Hex) {
|
||||
const bytes = ensureBytes(hex);
|
||||
if (bytes.length !== 64)
|
||||
throw new TypeError(`SchnorrSignature.fromHex: expected 64 bytes, not ${bytes.length}`);
|
||||
const r = bytesToNumberBE(bytes.subarray(0, 32));
|
||||
const s = bytesToNumberBE(bytes.subarray(32, 64));
|
||||
return new SchnorrSignature(r, s);
|
||||
}
|
||||
assertValidity() {
|
||||
const { r, s } = this;
|
||||
if (!isValidFieldElement(r) || !isWithinCurveOrder(s)) throw new Error('Invalid signature');
|
||||
}
|
||||
toHex(): string {
|
||||
return numTo32bStr(this.r) + numTo32bStr(this.s);
|
||||
}
|
||||
toRawBytes(): Uint8Array {
|
||||
return hexToBytes(this.toHex());
|
||||
}
|
||||
}
|
||||
|
||||
function schnorrGetScalar(priv: bigint) {
|
||||
const point = secp256k1.Point.fromPrivateKey(priv);
|
||||
const scalar = point.hasEvenY() ? priv : secp256k1.CURVE.n - priv;
|
||||
return { point, scalar, x: toRawX(point) };
|
||||
}
|
||||
/**
|
||||
* Synchronously creates Schnorr signature. Improved security: verifies itself before
|
||||
* producing an output.
|
||||
* @param msg message (not message hash)
|
||||
* @param privateKey private key
|
||||
* @param auxRand random bytes that would be added to k. Bad RNG won't break it.
|
||||
*/
|
||||
function schnorrSign(
|
||||
message: Hex,
|
||||
privateKey: PrivKey,
|
||||
auxRand: Hex = randomBytes(32)
|
||||
): Uint8Array {
|
||||
if (message == null) throw new TypeError(`sign: Expected valid message, not "${message}"`);
|
||||
const m = ensureBytes(message);
|
||||
// checks for isWithinCurveOrder
|
||||
const { x: px, scalar: d } = schnorrGetScalar(normalizePrivateKey(privateKey));
|
||||
const rand = ensureBytes(auxRand);
|
||||
if (rand.length !== 32) throw new TypeError('sign: Expected 32 bytes of aux randomness');
|
||||
const tag = taggedHash;
|
||||
const t0h = tag(TAGS.aux, rand);
|
||||
const t = numTo32b(d ^ bytesToNumberBE(t0h));
|
||||
const k0h = tag(TAGS.nonce, t, px, m);
|
||||
const k0 = mod(bytesToNumberBE(k0h), secp256k1.CURVE.n);
|
||||
if (k0 === _0n) throw new Error('sign: Creation of signature failed. k is zero');
|
||||
const { point: R, x: rx, scalar: k } = schnorrGetScalar(k0);
|
||||
const e = schnorrChallengeFinalize(tag(TAGS.challenge, rx, px, m));
|
||||
const sig = new SchnorrSignature(R.x, mod(k + e * d, secp256k1.CURVE.n)).toRawBytes();
|
||||
if (!schnorrVerify(sig, m, px)) throw new Error('sign: Invalid signature produced');
|
||||
return sig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies Schnorr signature synchronously.
|
||||
*/
|
||||
function schnorrVerify(signature: Hex, message: Hex, publicKey: Hex): boolean {
|
||||
try {
|
||||
const raw = signature instanceof SchnorrSignature;
|
||||
const sig: SchnorrSignature = raw ? signature : SchnorrSignature.fromHex(signature);
|
||||
if (raw) sig.assertValidity(); // just in case
|
||||
|
||||
const { r, s } = sig;
|
||||
const m = ensureBytes(message);
|
||||
const P = normalizePublicKey(publicKey);
|
||||
const e = schnorrChallengeFinalize(taggedHash(TAGS.challenge, numTo32b(r), toRawX(P), m));
|
||||
// Finalize
|
||||
// R = s⋅G - e⋅P
|
||||
// -eP == (n-e)P
|
||||
const R = secp256k1.Point.BASE.multiplyAndAddUnsafe(
|
||||
P,
|
||||
normalizePrivateKey(s),
|
||||
mod(-e, secp256k1.CURVE.n)
|
||||
);
|
||||
if (!R || !R.hasEvenY() || R.x !== r) return false;
|
||||
return true;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export const schnorr = {
|
||||
Signature: SchnorrSignature,
|
||||
// Schnorr's pubkey is just `x` of Point (BIP340)
|
||||
getPublicKey: (privateKey: PrivKey): Uint8Array =>
|
||||
toRawX(secp256k1.Point.fromPrivateKey(privateKey)),
|
||||
sign: schnorrSign,
|
||||
verify: schnorrVerify,
|
||||
};
|
||||
export { hashToCurve, encodeToCurve };
|
||||
|
||||
186
src/stark.ts
186
src/stark.ts
@@ -1,12 +1,14 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
import { keccak_256 } from '@noble/hashes/sha3';
|
||||
import { sha256 } from '@noble/hashes/sha256';
|
||||
import { weierstrass, ProjectivePointType } from './abstract/weierstrass.js';
|
||||
import { weierstrass, ProjPointType } from './abstract/weierstrass.js';
|
||||
import * as cutils from './abstract/utils.js';
|
||||
import { Fp } from './abstract/modular.js';
|
||||
import { Fp, mod, Field, validateField } from './abstract/modular.js';
|
||||
import { getHash } from './_shortw_utils.js';
|
||||
import * as poseidon from './abstract/poseidon.js';
|
||||
import { utf8ToBytes } from '@noble/hashes/utils';
|
||||
|
||||
type ProjectivePoint = ProjectivePointType<bigint>;
|
||||
type ProjectivePoint = ProjPointType<bigint>;
|
||||
// Stark-friendly elliptic curve
|
||||
// https://docs.starkware.co/starkex/stark-curve.html
|
||||
|
||||
@@ -14,6 +16,15 @@ const CURVE_N = BigInt(
|
||||
'3618502788666131213697322783095070105526743751716087489154079457884512865583'
|
||||
);
|
||||
const nBitLength = 252;
|
||||
// Copy-pasted from weierstrass.ts
|
||||
function bits2int(bytes: Uint8Array): bigint {
|
||||
const delta = bytes.length * 8 - nBitLength;
|
||||
const num = cutils.bytesToNumberBE(bytes);
|
||||
return delta > 0 ? num >> BigInt(delta) : num;
|
||||
}
|
||||
function bits2int_modN(bytes: Uint8Array): bigint {
|
||||
return mod(bits2int(bytes), CURVE_N);
|
||||
}
|
||||
export const starkCurve = weierstrass({
|
||||
// Params: a, b
|
||||
a: BigInt(1),
|
||||
@@ -31,31 +42,27 @@ export const starkCurve = weierstrass({
|
||||
// Default options
|
||||
lowS: false,
|
||||
...getHash(sha256),
|
||||
truncateHash: (hash: Uint8Array, truncateOnly = false): bigint => {
|
||||
// TODO: cleanup, ugly code
|
||||
// Fix truncation
|
||||
if (!truncateOnly) {
|
||||
let hashS = bytesToNumber0x(hash).toString(16);
|
||||
if (hashS.length === 63) {
|
||||
hashS += '0';
|
||||
hash = hexToBytes0x(hashS);
|
||||
}
|
||||
// Custom truncation routines for stark curve
|
||||
bits2int: (bytes: Uint8Array): bigint => {
|
||||
while (bytes[0] === 0) bytes = bytes.subarray(1);
|
||||
return bits2int(bytes);
|
||||
},
|
||||
bits2int_modN: (bytes: Uint8Array): bigint => {
|
||||
let hashS = cutils.bytesToNumberBE(bytes).toString(16);
|
||||
if (hashS.length === 63) {
|
||||
hashS += '0';
|
||||
bytes = hexToBytes0x(hashS);
|
||||
}
|
||||
// Truncate zero bytes on left (compat with elliptic)
|
||||
while (hash[0] === 0) hash = hash.subarray(1);
|
||||
const byteLength = hash.length;
|
||||
const delta = byteLength * 8 - nBitLength; // size of curve.n (252 bits)
|
||||
let h = hash.length ? bytesToNumber0x(hash) : 0n;
|
||||
if (delta > 0) h = h >> BigInt(delta);
|
||||
if (!truncateOnly && h >= CURVE_N) h -= CURVE_N;
|
||||
return h;
|
||||
while (bytes[0] === 0) bytes = bytes.subarray(1);
|
||||
return bits2int_modN(bytes);
|
||||
},
|
||||
});
|
||||
|
||||
// Custom Starknet type conversion functions that can handle 0x and unpadded hex
|
||||
function hexToBytes0x(hex: string): Uint8Array {
|
||||
if (typeof hex !== 'string') {
|
||||
throw new TypeError('hexToBytes: expected string, got ' + typeof hex);
|
||||
throw new Error('hexToBytes: expected string, got ' + typeof hex);
|
||||
}
|
||||
hex = strip0x(hex);
|
||||
if (hex.length & 1) hex = '0' + hex; // padding
|
||||
@@ -72,7 +79,7 @@ function hexToBytes0x(hex: string): Uint8Array {
|
||||
}
|
||||
function hexToNumber0x(hex: string): bigint {
|
||||
if (typeof hex !== 'string') {
|
||||
throw new TypeError('hexToNumber: expected string, got ' + typeof hex);
|
||||
throw new Error('hexToNumber: expected string, got ' + typeof hex);
|
||||
}
|
||||
// Big Endian
|
||||
// TODO: strip vs no strip?
|
||||
@@ -88,16 +95,16 @@ function ensureBytes0x(hex: Hex): Uint8Array {
|
||||
}
|
||||
|
||||
function normalizePrivateKey(privKey: Hex) {
|
||||
return cutils.bytesToHex(ensureBytes0x(privKey)).padStart(32 * 2, '0');
|
||||
return cutils.bytesToHex(ensureBytes0x(privKey)).padStart(64, '0');
|
||||
}
|
||||
function getPublicKey0x(privKey: Hex, isCompressed?: boolean) {
|
||||
function getPublicKey0x(privKey: Hex, isCompressed = false) {
|
||||
return starkCurve.getPublicKey(normalizePrivateKey(privKey), isCompressed);
|
||||
}
|
||||
function getSharedSecret0x(privKeyA: Hex, pubKeyB: Hex) {
|
||||
return starkCurve.getSharedSecret(normalizePrivateKey(privKeyA), pubKeyB);
|
||||
}
|
||||
|
||||
function sign0x(msgHash: Hex, privKey: Hex, opts: any) {
|
||||
function sign0x(msgHash: Hex, privKey: Hex, opts?: any) {
|
||||
if (typeof privKey === 'string') privKey = strip0x(privKey).padStart(64, '0');
|
||||
return starkCurve.sign(ensureBytes0x(msgHash), normalizePrivateKey(privKey), opts);
|
||||
}
|
||||
@@ -106,11 +113,10 @@ function verify0x(signature: Hex, msgHash: Hex, pubKey: Hex) {
|
||||
return starkCurve.verify(sig, ensureBytes0x(msgHash), ensureBytes0x(pubKey));
|
||||
}
|
||||
|
||||
const { CURVE, Point, ProjectivePoint, Signature } = starkCurve;
|
||||
const { CURVE, ProjectivePoint, Signature } = starkCurve;
|
||||
export const utils = starkCurve.utils;
|
||||
export {
|
||||
CURVE,
|
||||
Point,
|
||||
Signature,
|
||||
ProjectivePoint,
|
||||
getPublicKey0x as getPublicKey,
|
||||
@@ -132,17 +138,18 @@ type Hex = Uint8Array | string;
|
||||
function hashKeyWithIndex(key: Uint8Array, index: number) {
|
||||
let indexHex = cutils.numberToHexUnpadded(index);
|
||||
if (indexHex.length & 1) indexHex = '0' + indexHex;
|
||||
return bytesToNumber0x(sha256(cutils.concatBytes(key, hexToBytes0x(indexHex))));
|
||||
return sha256Num(cutils.concatBytes(key, hexToBytes0x(indexHex)));
|
||||
}
|
||||
|
||||
export function grindKey(seed: Hex) {
|
||||
const _seed = ensureBytes0x(seed);
|
||||
const sha256mask = 2n ** 256n;
|
||||
const limit = sha256mask - starkCurve.utils.mod(sha256mask, starkCurve.CURVE.n);
|
||||
|
||||
const limit = sha256mask - mod(sha256mask, CURVE_N);
|
||||
for (let i = 0; ; i++) {
|
||||
const key = hashKeyWithIndex(_seed, i);
|
||||
// key should be in [0, limit)
|
||||
if (key < limit) return starkCurve.utils.mod(key, starkCurve.CURVE.n).toString(16);
|
||||
if (key < limit) return mod(key, CURVE_N).toString(16);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,37 +171,42 @@ export function getAccountPath(
|
||||
ethereumAddress: string,
|
||||
index: number
|
||||
) {
|
||||
const layerNum = int31(bytesToNumber0x(sha256(layer)));
|
||||
const applicationNum = int31(bytesToNumber0x(sha256(application)));
|
||||
const layerNum = int31(sha256Num(layer));
|
||||
const applicationNum = int31(sha256Num(application));
|
||||
const eth = hexToNumber0x(ethereumAddress);
|
||||
return `m/2645'/${layerNum}'/${applicationNum}'/${int31(eth)}'/${int31(eth >> 31n)}'/${index}`;
|
||||
}
|
||||
|
||||
// https://docs.starkware.co/starkex/pedersen-hash-function.html
|
||||
const PEDERSEN_POINTS_AFFINE = [
|
||||
new Point(
|
||||
new ProjectivePoint(
|
||||
2089986280348253421170679821480865132823066470938446095505822317253594081284n,
|
||||
1713931329540660377023406109199410414810705867260802078187082345529207694986n
|
||||
1713931329540660377023406109199410414810705867260802078187082345529207694986n,
|
||||
1n
|
||||
),
|
||||
new Point(
|
||||
new ProjectivePoint(
|
||||
996781205833008774514500082376783249102396023663454813447423147977397232763n,
|
||||
1668503676786377725805489344771023921079126552019160156920634619255970485781n
|
||||
1668503676786377725805489344771023921079126552019160156920634619255970485781n,
|
||||
1n
|
||||
),
|
||||
new Point(
|
||||
new ProjectivePoint(
|
||||
2251563274489750535117886426533222435294046428347329203627021249169616184184n,
|
||||
1798716007562728905295480679789526322175868328062420237419143593021674992973n
|
||||
1798716007562728905295480679789526322175868328062420237419143593021674992973n,
|
||||
1n
|
||||
),
|
||||
new Point(
|
||||
new ProjectivePoint(
|
||||
2138414695194151160943305727036575959195309218611738193261179310511854807447n,
|
||||
113410276730064486255102093846540133784865286929052426931474106396135072156n
|
||||
113410276730064486255102093846540133784865286929052426931474106396135072156n,
|
||||
1n
|
||||
),
|
||||
new Point(
|
||||
new ProjectivePoint(
|
||||
2379962749567351885752724891227938183011949129833673362440656643086021394946n,
|
||||
776496453633298175483985398648758586525933812536653089401905292063708816422n
|
||||
776496453633298175483985398648758586525933812536653089401905292063708816422n,
|
||||
1n
|
||||
),
|
||||
];
|
||||
// for (const p of PEDERSEN_POINTS) p._setWindowSize(8);
|
||||
const PEDERSEN_POINTS = PEDERSEN_POINTS_AFFINE.map(ProjectivePoint.fromAffine);
|
||||
const PEDERSEN_POINTS = PEDERSEN_POINTS_AFFINE;
|
||||
|
||||
function pedersenPrecompute(p1: ProjectivePoint, p2: ProjectivePoint): ProjectivePoint[] {
|
||||
const out: ProjectivePoint[] = [];
|
||||
@@ -203,6 +215,8 @@ function pedersenPrecompute(p1: ProjectivePoint, p2: ProjectivePoint): Projectiv
|
||||
out.push(p);
|
||||
p = p.double();
|
||||
}
|
||||
// NOTE: we cannot use wNAF here, because last 4 bits will require full 248 bits multiplication
|
||||
// We can add support for this to wNAF, but it will complicate wNAF.
|
||||
p = p2;
|
||||
for (let i = 0; i < 4; i++) {
|
||||
out.push(p);
|
||||
@@ -231,7 +245,7 @@ function pedersenSingle(point: ProjectivePoint, value: PedersenArg, constants: P
|
||||
let x = pedersenArg(value);
|
||||
for (let j = 0; j < 252; j++) {
|
||||
const pt = constants[j];
|
||||
if (pt.x === point.x) throw new Error('Same point');
|
||||
if (pt.px === point.px) throw new Error('Same point');
|
||||
if ((x & 1n) !== 0n) point = point.add(pt);
|
||||
x >>= 1n;
|
||||
}
|
||||
@@ -243,7 +257,7 @@ export function pedersen(x: PedersenArg, y: PedersenArg) {
|
||||
let point: ProjectivePoint = PEDERSEN_POINTS[0];
|
||||
point = pedersenSingle(point, x, PEDERSEN_POINTS1);
|
||||
point = pedersenSingle(point, y, PEDERSEN_POINTS2);
|
||||
return bytesToHexEth(point.toAffine().toRawBytes(true).slice(1));
|
||||
return bytesToHexEth(point.toRawBytes(true).slice(1));
|
||||
}
|
||||
|
||||
export function hashChain(data: PedersenArg[], fn = pedersen) {
|
||||
@@ -258,5 +272,85 @@ export function hashChain(data: PedersenArg[], fn = pedersen) {
|
||||
export const computeHashOnElements = (data: PedersenArg[], fn = pedersen) =>
|
||||
[0, ...data, data.length].reduce((x, y) => fn(x, y));
|
||||
|
||||
const MASK_250 = 2n ** 250n - 1n;
|
||||
export const keccak = (data: Uint8Array) => bytesToNumber0x(keccak_256(data)) & MASK_250;
|
||||
const MASK_250 = cutils.bitMask(250);
|
||||
export const keccak = (data: Uint8Array): bigint => bytesToNumber0x(keccak_256(data)) & MASK_250;
|
||||
const sha256Num = (data: Uint8Array | string): bigint => cutils.bytesToNumberBE(sha256(data));
|
||||
|
||||
// Poseidon hash
|
||||
export const Fp253 = Fp(
|
||||
BigInt('14474011154664525231415395255581126252639794253786371766033694892385558855681')
|
||||
); // 2^253 + 2^199 + 1
|
||||
export const Fp251 = Fp(
|
||||
BigInt('3618502788666131213697322783095070105623107215331596699973092056135872020481')
|
||||
); // 2^251 + 17 * 2^192 + 1
|
||||
|
||||
function poseidonRoundConstant(Fp: Field<bigint>, name: string, idx: number) {
|
||||
const val = Fp.fromBytes(sha256(utf8ToBytes(`${name}${idx}`)));
|
||||
return Fp.create(val);
|
||||
}
|
||||
|
||||
// NOTE: doesn't check eiginvalues and possible can create unsafe matrix. But any filtration here will break compatibility with starknet
|
||||
// Please use only if you really know what you doing.
|
||||
// https://eprint.iacr.org/2019/458.pdf Section 2.3 (Avoiding Insecure Matrices)
|
||||
export function _poseidonMDS(Fp: Field<bigint>, name: string, m: number, attempt = 0) {
|
||||
const x_values: bigint[] = [];
|
||||
const y_values: bigint[] = [];
|
||||
for (let i = 0; i < m; i++) {
|
||||
x_values.push(poseidonRoundConstant(Fp, `${name}x`, attempt * m + i));
|
||||
y_values.push(poseidonRoundConstant(Fp, `${name}y`, attempt * m + i));
|
||||
}
|
||||
if (new Set([...x_values, ...y_values]).size !== 2 * m)
|
||||
throw new Error('X and Y values are not distinct');
|
||||
return x_values.map((x) => y_values.map((y) => Fp.inv(Fp.sub(x, y))));
|
||||
}
|
||||
|
||||
const MDS_SMALL = [
|
||||
[3, 1, 1],
|
||||
[1, -1, 1],
|
||||
[1, 1, -2],
|
||||
].map((i) => i.map(BigInt));
|
||||
|
||||
export type PoseidonOpts = {
|
||||
Fp: Field<bigint>;
|
||||
rate: number;
|
||||
capacity: number;
|
||||
roundsFull: number;
|
||||
roundsPartial: number;
|
||||
};
|
||||
|
||||
export function poseidonBasic(opts: PoseidonOpts, mds: bigint[][]) {
|
||||
validateField(opts.Fp);
|
||||
if (!Number.isSafeInteger(opts.rate) || !Number.isSafeInteger(opts.capacity))
|
||||
throw new Error(`Wrong poseidon opts: ${opts}`);
|
||||
const m = opts.rate + opts.capacity;
|
||||
const rounds = opts.roundsFull + opts.roundsPartial;
|
||||
const roundConstants = [];
|
||||
for (let i = 0; i < rounds; i++) {
|
||||
const row = [];
|
||||
for (let j = 0; j < m; j++) row.push(poseidonRoundConstant(opts.Fp, 'Hades', m * i + j));
|
||||
roundConstants.push(row);
|
||||
}
|
||||
return poseidon.poseidon({
|
||||
...opts,
|
||||
t: m,
|
||||
sboxPower: 3,
|
||||
reversePartialPowIdx: true, // Why?!
|
||||
mds,
|
||||
roundConstants,
|
||||
});
|
||||
}
|
||||
|
||||
export function poseidonCreate(opts: PoseidonOpts, mdsAttempt = 0) {
|
||||
const m = opts.rate + opts.capacity;
|
||||
if (!Number.isSafeInteger(mdsAttempt)) throw new Error(`Wrong mdsAttempt=${mdsAttempt}`);
|
||||
return poseidonBasic(opts, _poseidonMDS(opts.Fp, 'HadesMDS', m, mdsAttempt));
|
||||
}
|
||||
|
||||
export const poseidonSmall = poseidonBasic(
|
||||
{ Fp: Fp251, rate: 2, capacity: 1, roundsFull: 8, roundsPartial: 83 },
|
||||
MDS_SMALL
|
||||
);
|
||||
|
||||
export function poseidonHash(x: bigint, y: bigint, fn = poseidonSmall) {
|
||||
return fn([x, y, 2n])[0];
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { deepStrictEqual, throws } from 'assert';
|
||||
import { should } from 'micro-should';
|
||||
import { should, describe } from 'micro-should';
|
||||
import * as fc from 'fast-check';
|
||||
import * as mod from '../lib/esm/abstract/modular.js';
|
||||
import { bytesToHex as toHex } from '../lib/esm/abstract/utils.js';
|
||||
// Generic tests for all curves in package
|
||||
import { secp192r1 } from '../lib/esm/p192.js';
|
||||
import { secp224r1 } from '../lib/esm/p224.js';
|
||||
@@ -9,13 +10,292 @@ import { secp256r1 } from '../lib/esm/p256.js';
|
||||
import { secp384r1 } from '../lib/esm/p384.js';
|
||||
import { secp521r1 } from '../lib/esm/p521.js';
|
||||
import { secp256k1 } from '../lib/esm/secp256k1.js';
|
||||
import { ed25519, ed25519ctx, ed25519ph } from '../lib/esm/ed25519.js';
|
||||
import { ed25519, ed25519ctx, ed25519ph, x25519 } from '../lib/esm/ed25519.js';
|
||||
import { ed448, ed448ph } from '../lib/esm/ed448.js';
|
||||
import { starkCurve } from '../lib/esm/stark.js';
|
||||
import { pallas, vesta } from '../lib/esm/pasta.js';
|
||||
import { bn254 } from '../lib/esm/bn.js';
|
||||
import { jubjub } from '../lib/esm/jubjub.js';
|
||||
import { bls12_381 } from '../lib/esm/bls12-381.js';
|
||||
|
||||
// Fields tests
|
||||
const FIELDS = {
|
||||
secp192r1: { Fp: [secp192r1.CURVE.Fp] },
|
||||
secp224r1: { Fp: [secp224r1.CURVE.Fp] },
|
||||
secp256r1: { Fp: [secp256r1.CURVE.Fp] },
|
||||
secp521r1: { Fp: [secp521r1.CURVE.Fp] },
|
||||
secp256k1: { Fp: [secp256k1.CURVE.Fp] },
|
||||
stark: { Fp: [starkCurve.CURVE.Fp] },
|
||||
jubjub: { Fp: [jubjub.CURVE.Fp] },
|
||||
ed25519: { Fp: [ed25519.CURVE.Fp] },
|
||||
ed448: { Fp: [ed448.CURVE.Fp] },
|
||||
bn254: { Fp: [bn254.CURVE.Fp] },
|
||||
pallas: { Fp: [pallas.CURVE.Fp] },
|
||||
vesta: { Fp: [vesta.CURVE.Fp] },
|
||||
bls12: {
|
||||
Fp: [bls12_381.CURVE.Fp],
|
||||
Fp2: [
|
||||
bls12_381.CURVE.Fp2,
|
||||
fc.array(fc.bigInt(1n, bls12_381.CURVE.Fp.ORDER - 1n), {
|
||||
minLength: 2,
|
||||
maxLength: 2,
|
||||
}),
|
||||
(Fp2, num) => Fp2.fromBigTuple([num[0], num[1]]),
|
||||
],
|
||||
// Fp6: [bls12_381.CURVE.Fp6],
|
||||
Fp12: [
|
||||
bls12_381.CURVE.Fp12,
|
||||
fc.array(fc.bigInt(1n, bls12_381.CURVE.Fp.ORDER - 1n), {
|
||||
minLength: 12,
|
||||
maxLength: 12,
|
||||
}),
|
||||
(Fp12, num) => Fp12.fromBigTwelve(num),
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
for (const c in FIELDS) {
|
||||
const curve = FIELDS[c];
|
||||
for (const f in curve) {
|
||||
const Fp = curve[f][0];
|
||||
const name = `${c}/${f}:`;
|
||||
const FC_BIGINT = curve[f][1] ? curve[f][1] : fc.bigInt(1n, Fp.ORDER - 1n);
|
||||
|
||||
const create = curve[f][2] ? curve[f][2].bind(null, Fp) : (num) => Fp.create(num);
|
||||
describe(name, () => {
|
||||
should('equality', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (num) => {
|
||||
const a = create(num);
|
||||
const b = create(num);
|
||||
deepStrictEqual(Fp.eql(a, b), true);
|
||||
deepStrictEqual(Fp.eql(b, a), true);
|
||||
})
|
||||
);
|
||||
});
|
||||
should('non-equality', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => {
|
||||
const a = create(num1);
|
||||
const b = create(num2);
|
||||
deepStrictEqual(Fp.eql(a, b), num1 === num2);
|
||||
deepStrictEqual(Fp.eql(b, a), num1 === num2);
|
||||
})
|
||||
);
|
||||
});
|
||||
should('add/subtract/commutativity', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => {
|
||||
const a = create(num1);
|
||||
const b = create(num2);
|
||||
deepStrictEqual(Fp.add(a, b), Fp.add(b, a));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('add/subtract/associativity', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, FC_BIGINT, (num1, num2, num3) => {
|
||||
const a = create(num1);
|
||||
const b = create(num2);
|
||||
const c = create(num3);
|
||||
deepStrictEqual(Fp.add(a, Fp.add(b, c)), Fp.add(Fp.add(a, b), c));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('add/subtract/x+0=x', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (num) => {
|
||||
const a = create(num);
|
||||
deepStrictEqual(Fp.add(a, Fp.ZERO), a);
|
||||
})
|
||||
);
|
||||
});
|
||||
should('add/subtract/x-0=x', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (num) => {
|
||||
const a = create(num);
|
||||
deepStrictEqual(Fp.sub(a, Fp.ZERO), a);
|
||||
deepStrictEqual(Fp.sub(a, a), Fp.ZERO);
|
||||
})
|
||||
);
|
||||
});
|
||||
should('add/subtract/negate equality', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (num1) => {
|
||||
const a = create(num1);
|
||||
const b = create(num1);
|
||||
deepStrictEqual(Fp.sub(Fp.ZERO, a), Fp.neg(a));
|
||||
deepStrictEqual(Fp.sub(a, b), Fp.add(a, Fp.neg(b)));
|
||||
deepStrictEqual(Fp.sub(a, b), Fp.add(a, Fp.mul(b, Fp.create(-1n))));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('add/subtract/negate', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (num) => {
|
||||
const a = create(num);
|
||||
deepStrictEqual(Fp.neg(a), Fp.sub(Fp.ZERO, a));
|
||||
deepStrictEqual(Fp.neg(a), Fp.mul(a, Fp.create(-1n)));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('negate(0)', () => {
|
||||
deepStrictEqual(Fp.neg(Fp.ZERO), Fp.ZERO);
|
||||
});
|
||||
|
||||
should('multiply/commutativity', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => {
|
||||
const a = create(num1);
|
||||
const b = create(num2);
|
||||
deepStrictEqual(Fp.mul(a, b), Fp.mul(b, a));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('multiply/associativity', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, FC_BIGINT, (num1, num2, num3) => {
|
||||
const a = create(num1);
|
||||
const b = create(num2);
|
||||
const c = create(num3);
|
||||
deepStrictEqual(Fp.mul(a, Fp.mul(b, c)), Fp.mul(Fp.mul(a, b), c));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('multiply/distributivity', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, FC_BIGINT, (num1, num2, num3) => {
|
||||
const a = create(num1);
|
||||
const b = create(num2);
|
||||
const c = create(num3);
|
||||
deepStrictEqual(Fp.mul(a, Fp.add(b, c)), Fp.add(Fp.mul(b, a), Fp.mul(c, a)));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('multiply/add equality', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (num) => {
|
||||
const a = create(num);
|
||||
deepStrictEqual(Fp.mul(a, 0n), Fp.ZERO);
|
||||
deepStrictEqual(Fp.mul(a, Fp.ZERO), Fp.ZERO);
|
||||
deepStrictEqual(Fp.mul(a, 1n), a);
|
||||
deepStrictEqual(Fp.mul(a, Fp.ONE), a);
|
||||
deepStrictEqual(Fp.mul(a, 2n), Fp.add(a, a));
|
||||
deepStrictEqual(Fp.mul(a, 3n), Fp.add(Fp.add(a, a), a));
|
||||
deepStrictEqual(Fp.mul(a, 4n), Fp.add(Fp.add(Fp.add(a, a), a), a));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('multiply/square equality', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (num) => {
|
||||
const a = create(num);
|
||||
deepStrictEqual(Fp.sqr(a), Fp.mul(a, a));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('multiply/pow equality', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (num) => {
|
||||
const a = create(num);
|
||||
deepStrictEqual(Fp.pow(a, 0n), Fp.ONE);
|
||||
deepStrictEqual(Fp.pow(a, 1n), a);
|
||||
deepStrictEqual(Fp.pow(a, 2n), Fp.mul(a, a));
|
||||
deepStrictEqual(Fp.pow(a, 3n), Fp.mul(Fp.mul(a, a), a));
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
should('square(0)', () => {
|
||||
deepStrictEqual(Fp.sqr(Fp.ZERO), Fp.ZERO);
|
||||
deepStrictEqual(Fp.mul(Fp.ZERO, Fp.ZERO), Fp.ZERO);
|
||||
});
|
||||
|
||||
should('square(1)', () => {
|
||||
deepStrictEqual(Fp.sqr(Fp.ONE), Fp.ONE);
|
||||
deepStrictEqual(Fp.mul(Fp.ONE, Fp.ONE), Fp.ONE);
|
||||
});
|
||||
|
||||
should('square(-1)', () => {
|
||||
const minus1 = Fp.neg(Fp.ONE);
|
||||
deepStrictEqual(Fp.sqr(minus1), Fp.ONE);
|
||||
deepStrictEqual(Fp.mul(minus1, minus1), Fp.ONE);
|
||||
});
|
||||
|
||||
const isSquare = mod.FpIsSquare(Fp);
|
||||
// Not implemented
|
||||
if (Fp !== bls12_381.CURVE.Fp12) {
|
||||
should('multiply/sqrt', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (num) => {
|
||||
const a = create(num);
|
||||
let root;
|
||||
try {
|
||||
root = Fp.sqrt(a);
|
||||
} catch (e) {
|
||||
deepStrictEqual(isSquare(a), false);
|
||||
return;
|
||||
}
|
||||
deepStrictEqual(isSquare(a), true);
|
||||
deepStrictEqual(Fp.eql(Fp.sqr(root), a), true, 'sqrt(a)^2 == a');
|
||||
deepStrictEqual(Fp.eql(Fp.sqr(Fp.neg(root)), a), true, '(-sqrt(a))^2 == a');
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
should('sqrt(0)', () => {
|
||||
deepStrictEqual(Fp.sqrt(Fp.ZERO), Fp.ZERO);
|
||||
const sqrt1 = Fp.sqrt(Fp.ONE);
|
||||
deepStrictEqual(
|
||||
Fp.eql(sqrt1, Fp.ONE) || Fp.eql(sqrt1, Fp.neg(Fp.ONE)),
|
||||
true,
|
||||
'sqrt(1) = 1 or -1'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
should('div/division by one equality', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (num) => {
|
||||
const a = create(num);
|
||||
if (Fp.eql(a, Fp.ZERO)) return; // No division by zero
|
||||
deepStrictEqual(Fp.div(a, Fp.ONE), a);
|
||||
deepStrictEqual(Fp.div(a, a), Fp.ONE);
|
||||
})
|
||||
);
|
||||
});
|
||||
should('zero division equality', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (num) => {
|
||||
const a = create(num);
|
||||
deepStrictEqual(Fp.div(Fp.ZERO, a), Fp.ZERO);
|
||||
})
|
||||
);
|
||||
});
|
||||
should('div/division distributivity', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, FC_BIGINT, (num1, num2, num3) => {
|
||||
const a = create(num1);
|
||||
const b = create(num2);
|
||||
const c = create(num3);
|
||||
deepStrictEqual(Fp.div(Fp.add(a, b), c), Fp.add(Fp.div(a, c), Fp.div(b, c)));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('div/division and multiplication equality', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => {
|
||||
const a = create(num1);
|
||||
const b = create(num2);
|
||||
deepStrictEqual(Fp.div(a, b), Fp.mul(a, Fp.inv(b)));
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Group tests
|
||||
// prettier-ignore
|
||||
const CURVES = {
|
||||
secp192r1, secp224r1, secp256r1, secp384r1, secp521r1,
|
||||
@@ -29,6 +309,7 @@ const CURVES = {
|
||||
};
|
||||
|
||||
const NUM_RUNS = 5;
|
||||
|
||||
const getXY = (p) => ({ x: p.x, y: p.y });
|
||||
|
||||
function equal(a, b, comment) {
|
||||
@@ -61,254 +342,336 @@ for (const name in CURVES) {
|
||||
if (!p) continue;
|
||||
|
||||
const G = [p.ZERO, p.BASE];
|
||||
for (let i = 2; i < 10; i++) G.push(G[1].multiply(i));
|
||||
// Here we check basic group laws, to verify that points works as group
|
||||
should(`${name}/${pointName}/Basic group laws (zero)`, () => {
|
||||
equal(G[0].double(), G[0], '(0*G).double() = 0');
|
||||
equal(G[0].add(G[0]), G[0], '0*G + 0*G = 0');
|
||||
equal(G[0].subtract(G[0]), G[0], '0*G - 0*G = 0');
|
||||
equal(G[0].negate(), G[0], '-0 = 0');
|
||||
for (let i = 0; i < G.length; i++) {
|
||||
const p = G[i];
|
||||
equal(p, p.add(G[0]), `${i}*G + 0 = ${i}*G`);
|
||||
equal(G[0].multiply(i + 1), G[0], `${i + 1}*0 = 0`);
|
||||
}
|
||||
});
|
||||
should(`${name}/${pointName}/Basic group laws (one)`, () => {
|
||||
equal(G[1].double(), G[2], '(1*G).double() = 2*G');
|
||||
equal(G[1].subtract(G[1]), G[0], '1*G - 1*G = 0');
|
||||
equal(G[1].add(G[1]), G[2], '1*G + 1*G = 2*G');
|
||||
});
|
||||
should(`${name}/${pointName}/Basic group laws (sanity tests)`, () => {
|
||||
equal(G[2].double(), G[4], `(2*G).double() = 4*G`);
|
||||
equal(G[2].add(G[2]), G[4], `2*G + 2*G = 4*G`);
|
||||
equal(G[7].add(G[3].negate()), G[4], `7*G - 3*G = 4*G`);
|
||||
});
|
||||
should(`${name}/${pointName}/Basic group laws (addition commutativity)`, () => {
|
||||
equal(G[4].add(G[3]), G[3].add(G[4]), `4*G + 3*G = 3*G + 4*G`);
|
||||
equal(G[4].add(G[3]), G[3].add(G[2]).add(G[2]), `4*G + 3*G = 3*G + 2*G + 2*G`);
|
||||
});
|
||||
should(`${name}/${pointName}/Basic group laws (double)`, () => {
|
||||
equal(G[3].double(), G[6], '(3*G).double() = 6*G');
|
||||
});
|
||||
should(`${name}/${pointName}/Basic group laws (multiply)`, () => {
|
||||
equal(G[2].multiply(3), G[6], '(2*G).multiply(3) = 6*G');
|
||||
});
|
||||
should(`${name}/${pointName}/Basic group laws (same point addition)`, () => {
|
||||
equal(G[3].add(G[3]), G[6], `3*G + 3*G = 6*G`);
|
||||
});
|
||||
should(`${name}/${pointName}/Basic group laws (same point (negative) addition)`, () => {
|
||||
equal(G[3].add(G[3].negate()), G[0], '3*G + (- 3*G) = 0*G');
|
||||
equal(G[3].subtract(G[3]), G[0], '3*G - 3*G = 0*G');
|
||||
});
|
||||
should(`${name}/${pointName}/Basic group laws (curve order)`, () => {
|
||||
equal(G[1].multiply(CURVE_ORDER - 1n).add(G[1]), G[0], '(N-1)*G + G = 0');
|
||||
equal(G[1].multiply(CURVE_ORDER - 1n).add(G[2]), G[1], '(N-1)*G + 2*G = 1*G');
|
||||
equal(G[1].multiply(CURVE_ORDER - 2n).add(G[2]), G[0], '(N-2)*G + 2*G = 0');
|
||||
const half = CURVE_ORDER / 2n;
|
||||
const carry = CURVE_ORDER % 2n === 1n ? G[1] : G[0];
|
||||
equal(G[1].multiply(half).double().add(carry), G[0], '((N/2) * G).double() = 0');
|
||||
});
|
||||
should(`${name}/${pointName}/Basic group laws (inversion)`, () => {
|
||||
const a = 1234n;
|
||||
const b = 5678n;
|
||||
const c = a * b;
|
||||
equal(G[1].multiply(a).multiply(b), G[1].multiply(c), 'a*b*G = c*G');
|
||||
const inv = mod.invert(b, CURVE_ORDER);
|
||||
equal(G[1].multiply(c).multiply(inv), G[1].multiply(a), 'c*G * (1/b)*G = a*G');
|
||||
});
|
||||
should(`${name}/${pointName}/Basic group laws (multiply, rand)`, () =>
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, (a, b) => {
|
||||
const c = mod.mod(a + b, CURVE_ORDER);
|
||||
if (c === CURVE_ORDER || c < 1n) return;
|
||||
const pA = G[1].multiply(a);
|
||||
const pB = G[1].multiply(b);
|
||||
const pC = G[1].multiply(c);
|
||||
equal(pA.add(pB), pB.add(pA), `pA + pB = pB + pA`);
|
||||
equal(pA.add(pB), pC, `pA + pB = pC`);
|
||||
}),
|
||||
{ numRuns: NUM_RUNS }
|
||||
)
|
||||
);
|
||||
should(`${name}/${pointName}/Basic group laws (multiply2, rand)`, () =>
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, (a, b) => {
|
||||
const c = mod.mod(a * b, CURVE_ORDER);
|
||||
const pA = G[1].multiply(a);
|
||||
const pB = G[1].multiply(b);
|
||||
equal(pA.multiply(b), pB.multiply(a), `b*pA = a*pB`);
|
||||
equal(pA.multiply(b), G[1].multiply(c), `b*pA = c*G`);
|
||||
}),
|
||||
{ numRuns: NUM_RUNS }
|
||||
)
|
||||
);
|
||||
|
||||
for (const op of ['add', 'subtract']) {
|
||||
should(`${name}/${pointName}/${op} type check`, () => {
|
||||
throws(() => G[1][op](0), '0');
|
||||
throws(() => G[1][op](0n), '0n');
|
||||
G[1][op](G[2]);
|
||||
throws(() => G[1][op](CURVE_ORDER), 'CURVE_ORDER');
|
||||
throws(() => G[1][op](123.456), '123.456');
|
||||
throws(() => G[1][op](true), 'true');
|
||||
throws(() => G[1][op]('1'), "'1'");
|
||||
throws(() => G[1][op]({ x: 1n, y: 1n, z: 1n, t: 1n }), '{ x: 1n, y: 1n, z: 1n, t: 1n }');
|
||||
throws(() => G[1][op](new Uint8Array([])), 'ui8a([])');
|
||||
throws(() => G[1][op](new Uint8Array([0])), 'ui8a([0])');
|
||||
throws(() => G[1][op](new Uint8Array([1])), 'ui8a([1])');
|
||||
throws(() => G[1][op](new Uint8Array(4096).fill(1)), 'ui8a(4096*[1])');
|
||||
if (G[1].toAffine) throws(() => G[1][op](C.Point.BASE), `Point ${op} ${pointName}`);
|
||||
throws(() => G[1][op](o.BASE), `${op}/other curve point`);
|
||||
});
|
||||
}
|
||||
|
||||
should(`${name}/${pointName}/equals type check`, () => {
|
||||
throws(() => G[1].equals(0), '0');
|
||||
throws(() => G[1].equals(0n), '0n');
|
||||
deepStrictEqual(G[1].equals(G[2]), false, '1*G != 2*G');
|
||||
deepStrictEqual(G[1].equals(G[1]), true, '1*G == 1*G');
|
||||
deepStrictEqual(G[2].equals(G[2]), true, '2*G == 2*G');
|
||||
throws(() => G[1].equals(CURVE_ORDER), 'CURVE_ORDER');
|
||||
throws(() => G[1].equals(123.456), '123.456');
|
||||
throws(() => G[1].equals(true), 'true');
|
||||
throws(() => G[1].equals('1'), "'1'");
|
||||
throws(() => G[1].equals({ x: 1n, y: 1n, z: 1n, t: 1n }), '{ x: 1n, y: 1n, z: 1n, t: 1n }');
|
||||
throws(() => G[1].equals(new Uint8Array([])), 'ui8a([])');
|
||||
throws(() => G[1].equals(new Uint8Array([0])), 'ui8a([0])');
|
||||
throws(() => G[1].equals(new Uint8Array([1])), 'ui8a([1])');
|
||||
throws(() => G[1].equals(new Uint8Array(4096).fill(1)), 'ui8a(4096*[1])');
|
||||
if (G[1].toAffine) throws(() => G[1].equals(C.Point.BASE), `Point.equals(${pointName})`);
|
||||
throws(() => G[1].equals(o.BASE), 'other curve point');
|
||||
});
|
||||
|
||||
for (const op of ['multiply', 'multiplyUnsafe']) {
|
||||
if (!p.BASE[op]) continue;
|
||||
should(`${name}/${pointName}/${op} type check`, () => {
|
||||
if (op !== 'multiplyUnsafe') {
|
||||
throws(() => G[1][op](0), '0');
|
||||
throws(() => G[1][op](0n), '0n');
|
||||
}
|
||||
G[1][op](1n);
|
||||
G[1][op](CURVE_ORDER - 1n);
|
||||
throws(() => G[1][op](G[2]), 'G[2]');
|
||||
throws(() => G[1][op](CURVE_ORDER), 'CURVE_ORDER');
|
||||
throws(() => G[1][op](CURVE_ORDER + 1n), 'CURVE_ORDER+1');
|
||||
throws(() => G[1][op](123.456), '123.456');
|
||||
throws(() => G[1][op](true), 'true');
|
||||
throws(() => G[1][op]('1'), '1');
|
||||
throws(() => G[1][op](new Uint8Array([])), 'ui8a([])');
|
||||
throws(() => G[1][op](new Uint8Array([0])), 'ui8a([0])');
|
||||
throws(() => G[1][op](new Uint8Array([1])), 'ui8a([1])');
|
||||
throws(() => G[1][op](new Uint8Array(4096).fill(1)), 'ui8a(4096*[1])');
|
||||
throws(() => G[1][op](o.BASE), 'other curve point');
|
||||
});
|
||||
}
|
||||
// Complex point (Extended/Jacobian/Projective?)
|
||||
if (p.BASE.toAffine) {
|
||||
should(`${name}/${pointName}/toAffine()`, () => {
|
||||
equal(p.ZERO.toAffine(), C.Point.ZERO, `0 = 0`);
|
||||
equal(p.BASE.toAffine(), C.Point.BASE, `1 = 1`);
|
||||
});
|
||||
}
|
||||
if (p.fromAffine) {
|
||||
should(`${name}/${pointName}/fromAffine()`, () => {
|
||||
equal(p.ZERO, p.fromAffine(C.Point.ZERO), `0 = 0`);
|
||||
equal(p.BASE, p.fromAffine(C.Point.BASE), `1 = 1`);
|
||||
});
|
||||
}
|
||||
// toHex/fromHex (if available)
|
||||
if (p.fromHex && p.BASE.toHex) {
|
||||
should(`${name}/${pointName}/fromHex(toHex()) roundtrip`, () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (x) => {
|
||||
const hex = p.BASE.multiply(x).toHex();
|
||||
deepStrictEqual(p.fromHex(hex).toHex(), hex);
|
||||
})
|
||||
for (let i = 2n; i < 10n; i++) G.push(G[1].multiply(i));
|
||||
const title = `${name}/${pointName}`;
|
||||
describe(title, () => {
|
||||
describe('basic group laws', () => {
|
||||
// Here we check basic group laws, to verify that points works as group
|
||||
should('zero', () => {
|
||||
equal(G[0].double(), G[0], '(0*G).double() = 0');
|
||||
equal(G[0].add(G[0]), G[0], '0*G + 0*G = 0');
|
||||
equal(G[0].subtract(G[0]), G[0], '0*G - 0*G = 0');
|
||||
equal(G[0].negate(), G[0], '-0 = 0');
|
||||
for (let i = 0; i < G.length; i++) {
|
||||
const p = G[i];
|
||||
equal(p, p.add(G[0]), `${i}*G + 0 = ${i}*G`);
|
||||
equal(G[0].multiply(BigInt(i + 1)), G[0], `${i + 1}*0 = 0`);
|
||||
}
|
||||
});
|
||||
should('one', () => {
|
||||
equal(G[1].double(), G[2], '(1*G).double() = 2*G');
|
||||
equal(G[1].subtract(G[1]), G[0], '1*G - 1*G = 0');
|
||||
equal(G[1].add(G[1]), G[2], '1*G + 1*G = 2*G');
|
||||
});
|
||||
should('sanity tests', () => {
|
||||
equal(G[2].double(), G[4], '(2*G).double() = 4*G');
|
||||
equal(G[2].add(G[2]), G[4], '2*G + 2*G = 4*G');
|
||||
equal(G[7].add(G[3].negate()), G[4], '7*G - 3*G = 4*G');
|
||||
});
|
||||
should('add commutativity', () => {
|
||||
equal(G[4].add(G[3]), G[3].add(G[4]), '4*G + 3*G = 3*G + 4*G');
|
||||
equal(G[4].add(G[3]), G[3].add(G[2]).add(G[2]), '4*G + 3*G = 3*G + 2*G + 2*G');
|
||||
});
|
||||
should('double', () => {
|
||||
equal(G[3].double(), G[6], '(3*G).double() = 6*G');
|
||||
});
|
||||
should('multiply', () => {
|
||||
equal(G[2].multiply(3n), G[6], '(2*G).multiply(3) = 6*G');
|
||||
});
|
||||
should('add same-point', () => {
|
||||
equal(G[3].add(G[3]), G[6], '3*G + 3*G = 6*G');
|
||||
});
|
||||
should('add same-point negative', () => {
|
||||
equal(G[3].add(G[3].negate()), G[0], '3*G + (- 3*G) = 0*G');
|
||||
equal(G[3].subtract(G[3]), G[0], '3*G - 3*G = 0*G');
|
||||
});
|
||||
should('mul by curve order', () => {
|
||||
equal(G[1].multiply(CURVE_ORDER - 1n).add(G[1]), G[0], '(N-1)*G + G = 0');
|
||||
equal(G[1].multiply(CURVE_ORDER - 1n).add(G[2]), G[1], '(N-1)*G + 2*G = 1*G');
|
||||
equal(G[1].multiply(CURVE_ORDER - 2n).add(G[2]), G[0], '(N-2)*G + 2*G = 0');
|
||||
const half = CURVE_ORDER / 2n;
|
||||
const carry = CURVE_ORDER % 2n === 1n ? G[1] : G[0];
|
||||
equal(G[1].multiply(half).double().add(carry), G[0], '((N/2) * G).double() = 0');
|
||||
});
|
||||
should('inversion', () => {
|
||||
const a = 1234n;
|
||||
const b = 5678n;
|
||||
const c = a * b;
|
||||
equal(G[1].multiply(a).multiply(b), G[1].multiply(c), 'a*b*G = c*G');
|
||||
const inv = mod.invert(b, CURVE_ORDER);
|
||||
equal(G[1].multiply(c).multiply(inv), G[1].multiply(a), 'c*G * (1/b)*G = a*G');
|
||||
});
|
||||
should('multiply, rand', () =>
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, (a, b) => {
|
||||
const c = mod.mod(a + b, CURVE_ORDER);
|
||||
if (c === CURVE_ORDER || c < 1n) return;
|
||||
const pA = G[1].multiply(a);
|
||||
const pB = G[1].multiply(b);
|
||||
const pC = G[1].multiply(c);
|
||||
equal(pA.add(pB), pB.add(pA), 'pA + pB = pB + pA');
|
||||
equal(pA.add(pB), pC, 'pA + pB = pC');
|
||||
}),
|
||||
{ numRuns: NUM_RUNS }
|
||||
)
|
||||
);
|
||||
should('multiply2, rand', () =>
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, (a, b) => {
|
||||
const c = mod.mod(a * b, CURVE_ORDER);
|
||||
const pA = G[1].multiply(a);
|
||||
const pB = G[1].multiply(b);
|
||||
equal(pA.multiply(b), pB.multiply(a), 'b*pA = a*pB');
|
||||
equal(pA.multiply(b), G[1].multiply(c), 'b*pA = c*G');
|
||||
}),
|
||||
{ numRuns: NUM_RUNS }
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
// Generic complex things (getPublicKey/sign/verify/getSharedSecret)
|
||||
should(`${name}/getPublicKey type check`, () => {
|
||||
throws(() => C.getPublicKey(0), '0');
|
||||
throws(() => C.getPublicKey(0n), '0n');
|
||||
throws(() => C.getPublicKey(false), 'false');
|
||||
throws(() => C.getPublicKey(123.456), '123.456');
|
||||
throws(() => C.getPublicKey(true), 'true');
|
||||
throws(() => C.getPublicKey(''), "''");
|
||||
// NOTE: passes because of disabled hex padding checks for starknet, maybe enable?
|
||||
//throws(() => C.getPublicKey('1'), "'1'");
|
||||
throws(() => C.getPublicKey('key'), "'key'");
|
||||
throws(() => C.getPublicKey(new Uint8Array([])));
|
||||
throws(() => C.getPublicKey(new Uint8Array([0])));
|
||||
throws(() => C.getPublicKey(new Uint8Array([1])));
|
||||
throws(() => C.getPublicKey(new Uint8Array(4096).fill(1)));
|
||||
});
|
||||
should(`${name}.verify()/should verify random signatures`, () =>
|
||||
fc.assert(
|
||||
fc.property(fc.hexaString({ minLength: 64, maxLength: 64 }), (msg) => {
|
||||
const priv = C.utils.randomPrivateKey();
|
||||
const pub = C.getPublicKey(priv);
|
||||
const sig = C.sign(msg, priv);
|
||||
deepStrictEqual(C.verify(sig, msg, pub), true);
|
||||
}),
|
||||
{ numRuns: NUM_RUNS }
|
||||
)
|
||||
);
|
||||
should(`${name}.sign()/edge cases`, () => {
|
||||
throws(() => C.sign());
|
||||
throws(() => C.sign(''));
|
||||
});
|
||||
|
||||
should(`${name}.verify()/should not verify signature with wrong hash`, () => {
|
||||
const MSG = '01'.repeat(32);
|
||||
const PRIV_KEY = 0x2n;
|
||||
const WRONG_MSG = '11'.repeat(32);
|
||||
const signature = C.sign(MSG, PRIV_KEY);
|
||||
const publicKey = C.getPublicKey(PRIV_KEY);
|
||||
deepStrictEqual(C.verify(signature, WRONG_MSG, publicKey), false);
|
||||
});
|
||||
// NOTE: fails for ed, because of empty message. Since we convert it to scalar,
|
||||
// need to check what other implementations do. Empty message != new Uint8Array([0]), but what scalar should be in that case?
|
||||
// should(`${name}/should not verify signature with wrong message`, () => {
|
||||
// fc.assert(
|
||||
// fc.property(
|
||||
// fc.array(fc.integer({ min: 0x00, max: 0xff })),
|
||||
// fc.array(fc.integer({ min: 0x00, max: 0xff })),
|
||||
// (bytes, wrongBytes) => {
|
||||
// const privKey = C.utils.randomPrivateKey();
|
||||
// const message = new Uint8Array(bytes);
|
||||
// const wrongMessage = new Uint8Array(wrongBytes);
|
||||
// const publicKey = C.getPublicKey(privKey);
|
||||
// const signature = C.sign(message, privKey);
|
||||
// deepStrictEqual(
|
||||
// C.verify(signature, wrongMessage, publicKey),
|
||||
// bytes.toString() === wrongBytes.toString()
|
||||
// );
|
||||
// }
|
||||
// ),
|
||||
// { numRuns: NUM_RUNS }
|
||||
// );
|
||||
// });
|
||||
for (const op of ['add', 'subtract']) {
|
||||
describe(op, () => {
|
||||
should('type check', () => {
|
||||
throws(() => G[1][op](0), '0');
|
||||
throws(() => G[1][op](0n), '0n');
|
||||
G[1][op](G[2]);
|
||||
throws(() => G[1][op](CURVE_ORDER), 'CURVE_ORDER');
|
||||
throws(() => G[1][op](-123n), '-123n');
|
||||
throws(() => G[1][op](123), '123');
|
||||
throws(() => G[1][op](123.456), '123.456');
|
||||
throws(() => G[1][op](true), 'true');
|
||||
throws(() => G[1][op](false), 'false');
|
||||
throws(() => G[1][op](null), 'null');
|
||||
throws(() => G[1][op](undefined), 'undefined');
|
||||
throws(() => G[1][op]('1'), "'1'");
|
||||
throws(() => G[1][op]({ x: 1n, y: 1n }), '{ x: 1n, y: 1n }');
|
||||
throws(() => G[1][op]({ x: 1n, y: 1n, z: 1n }), '{ x: 1n, y: 1n, z: 1n }');
|
||||
throws(
|
||||
() => G[1][op]({ x: 1n, y: 1n, z: 1n, t: 1n }),
|
||||
'{ x: 1n, y: 1n, z: 1n, t: 1n }'
|
||||
);
|
||||
throws(() => G[1][op](new Uint8Array([])), 'ui8a([])');
|
||||
throws(() => G[1][op](new Uint8Array([0])), 'ui8a([0])');
|
||||
throws(() => G[1][op](new Uint8Array([1])), 'ui8a([1])');
|
||||
throws(() => G[1][op](new Uint8Array(4096).fill(1)), 'ui8a(4096*[1])');
|
||||
// if (G[1].toAffine) throws(() => G[1][op](C.Point.BASE), `Point ${op} ${pointName}`);
|
||||
throws(() => G[1][op](o.BASE), `${op}/other curve point`);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (C.getSharedSecret) {
|
||||
should(`${name}/getSharedSecret() should be commutative`, () => {
|
||||
for (let i = 0; i < NUM_RUNS; i++) {
|
||||
const asec = C.utils.randomPrivateKey();
|
||||
const apub = C.getPublicKey(asec);
|
||||
const bsec = C.utils.randomPrivateKey();
|
||||
const bpub = C.getPublicKey(bsec);
|
||||
try {
|
||||
deepStrictEqual(C.getSharedSecret(asec, bpub), C.getSharedSecret(bsec, apub));
|
||||
} catch (error) {
|
||||
console.error('not commutative', { asec, apub, bsec, bpub });
|
||||
throw error;
|
||||
}
|
||||
should('equals type check', () => {
|
||||
throws(() => G[1].equals(0), '0');
|
||||
throws(() => G[1].equals(0n), '0n');
|
||||
deepStrictEqual(G[1].equals(G[2]), false, '1*G != 2*G');
|
||||
deepStrictEqual(G[1].equals(G[1]), true, '1*G == 1*G');
|
||||
deepStrictEqual(G[2].equals(G[2]), true, '2*G == 2*G');
|
||||
throws(() => G[1].equals(CURVE_ORDER), 'CURVE_ORDER');
|
||||
throws(() => G[1].equals(123.456), '123.456');
|
||||
throws(() => G[1].equals(true), 'true');
|
||||
throws(() => G[1].equals('1'), "'1'");
|
||||
throws(() => G[1].equals({ x: 1n, y: 1n, z: 1n, t: 1n }), '{ x: 1n, y: 1n, z: 1n, t: 1n }');
|
||||
throws(() => G[1].equals(new Uint8Array([])), 'ui8a([])');
|
||||
throws(() => G[1].equals(new Uint8Array([0])), 'ui8a([0])');
|
||||
throws(() => G[1].equals(new Uint8Array([1])), 'ui8a([1])');
|
||||
throws(() => G[1].equals(new Uint8Array(4096).fill(1)), 'ui8a(4096*[1])');
|
||||
// if (G[1].toAffine) throws(() => G[1].equals(C.Point.BASE), 'Point.equals(${pointName})');
|
||||
throws(() => G[1].equals(o.BASE), 'other curve point');
|
||||
});
|
||||
|
||||
for (const op of ['multiply', 'multiplyUnsafe']) {
|
||||
if (!p.BASE[op]) continue;
|
||||
describe(op, () => {
|
||||
should('type check', () => {
|
||||
if (op !== 'multiplyUnsafe') {
|
||||
throws(() => G[1][op](0), '0');
|
||||
throws(() => G[1][op](0n), '0n');
|
||||
}
|
||||
G[1][op](1n);
|
||||
G[1][op](CURVE_ORDER - 1n);
|
||||
throws(() => G[1][op](G[2]), 'G[2]');
|
||||
throws(() => G[1][op](CURVE_ORDER), 'CURVE_ORDER');
|
||||
throws(() => G[1][op](CURVE_ORDER + 1n), 'CURVE_ORDER+1');
|
||||
throws(() => G[1][op](123.456), '123.456');
|
||||
throws(() => G[1][op](true), 'true');
|
||||
throws(() => G[1][op]('1'), '1');
|
||||
throws(() => G[1][op](new Uint8Array([])), 'ui8a([])');
|
||||
throws(() => G[1][op](new Uint8Array([0])), 'ui8a([0])');
|
||||
throws(() => G[1][op](new Uint8Array([1])), 'ui8a([1])');
|
||||
throws(() => G[1][op](new Uint8Array(4096).fill(1)), 'ui8a(4096*[1])');
|
||||
throws(() => G[1][op](o.BASE), 'other curve point');
|
||||
});
|
||||
});
|
||||
}
|
||||
// Complex point (Extended/Jacobian/Projective?)
|
||||
// if (p.BASE.toAffine && C.Point) {
|
||||
// should('toAffine()', () => {
|
||||
// equal(p.ZERO.toAffine(), C.Point.ZERO, '0 = 0');
|
||||
// equal(p.BASE.toAffine(), C.Point.BASE, '1 = 1');
|
||||
// });
|
||||
// }
|
||||
// if (p.fromAffine && C.Point) {
|
||||
// should('fromAffine()', () => {
|
||||
// equal(p.ZERO, p.fromAffine(C.Point.ZERO), '0 = 0');
|
||||
// equal(p.BASE, p.fromAffine(C.Point.BASE), '1 = 1');
|
||||
// });
|
||||
// }
|
||||
// toHex/fromHex (if available)
|
||||
if (p.fromHex && p.BASE.toHex) {
|
||||
should('fromHex(toHex()) roundtrip', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (x) => {
|
||||
const hex = p.BASE.multiply(x).toHex();
|
||||
deepStrictEqual(p.fromHex(hex).toHex(), hex);
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
describe(name, () => {
|
||||
// Generic complex things (getPublicKey/sign/verify/getSharedSecret)
|
||||
should('.getPublicKey() type check', () => {
|
||||
throws(() => C.getPublicKey(0), '0');
|
||||
throws(() => C.getPublicKey(0n), '0n');
|
||||
throws(() => C.getPublicKey(-123n), '-123n');
|
||||
throws(() => C.getPublicKey(123), '123');
|
||||
throws(() => C.getPublicKey(123.456), '123.456');
|
||||
throws(() => C.getPublicKey(true), 'true');
|
||||
throws(() => C.getPublicKey(false), 'false');
|
||||
throws(() => C.getPublicKey(null), 'null');
|
||||
throws(() => C.getPublicKey(undefined), 'undefined');
|
||||
throws(() => C.getPublicKey(''), "''");
|
||||
// NOTE: passes because of disabled hex padding checks for starknet, maybe enable?
|
||||
// throws(() => C.getPublicKey('1'), "'1'");
|
||||
throws(() => C.getPublicKey('key'), "'key'");
|
||||
throws(() => C.getPublicKey({}));
|
||||
throws(() => C.getPublicKey(new Uint8Array([])));
|
||||
throws(() => C.getPublicKey(new Uint8Array([0])));
|
||||
throws(() => C.getPublicKey(new Uint8Array([1])));
|
||||
throws(() => C.getPublicKey(new Uint8Array(4096).fill(1)));
|
||||
throws(() => C.getPublicKey(Array(32).fill(1)));
|
||||
});
|
||||
should('.verify() should verify random signatures', () =>
|
||||
fc.assert(
|
||||
fc.property(fc.hexaString({ minLength: 64, maxLength: 64 }), (msg) => {
|
||||
const priv = C.utils.randomPrivateKey();
|
||||
const pub = C.getPublicKey(priv);
|
||||
const sig = C.sign(msg, priv);
|
||||
deepStrictEqual(
|
||||
C.verify(sig, msg, pub),
|
||||
true,
|
||||
'priv=${toHex(priv)},pub=${toHex(pub)},msg=${msg}'
|
||||
);
|
||||
}),
|
||||
{ numRuns: NUM_RUNS }
|
||||
)
|
||||
);
|
||||
should('.verify() should verify empty signatures', () => {
|
||||
const msg = new Uint8Array([]);
|
||||
const priv = C.utils.randomPrivateKey();
|
||||
const pub = C.getPublicKey(priv);
|
||||
const sig = C.sign(msg, priv);
|
||||
deepStrictEqual(
|
||||
C.verify(sig, msg, pub),
|
||||
true,
|
||||
'priv=${toHex(priv)},pub=${toHex(pub)},msg=${msg}'
|
||||
);
|
||||
});
|
||||
should('.sign() edge cases', () => {
|
||||
throws(() => C.sign());
|
||||
throws(() => C.sign(''));
|
||||
throws(() => C.sign('', ''));
|
||||
throws(() => C.sign(new Uint8Array(), new Uint8Array()));
|
||||
});
|
||||
|
||||
describe('verify()', () => {
|
||||
const msg = '01'.repeat(32);
|
||||
should('true for proper signatures', () => {
|
||||
const priv = C.utils.randomPrivateKey();
|
||||
const sig = C.sign(msg, priv);
|
||||
const pub = C.getPublicKey(priv);
|
||||
deepStrictEqual(C.verify(sig, msg, pub), true);
|
||||
});
|
||||
should('false for wrong messages', () => {
|
||||
const priv = C.utils.randomPrivateKey();
|
||||
const sig = C.sign(msg, priv);
|
||||
const pub = C.getPublicKey(priv);
|
||||
deepStrictEqual(C.verify(sig, '11'.repeat(32), pub), false);
|
||||
});
|
||||
should('false for wrong keys', () => {
|
||||
const priv = C.utils.randomPrivateKey();
|
||||
const sig = C.sign(msg, priv);
|
||||
deepStrictEqual(C.verify(sig, msg, C.getPublicKey(C.utils.randomPrivateKey())), false);
|
||||
});
|
||||
});
|
||||
// NOTE: fails for ed, because of empty message. Since we convert it to scalar,
|
||||
// need to check what other implementations do. Empty message != new Uint8Array([0]), but what scalar should be in that case?
|
||||
// should('should not verify signature with wrong message', () => {
|
||||
// fc.assert(
|
||||
// fc.property(
|
||||
// fc.array(fc.integer({ min: 0x00, max: 0xff })),
|
||||
// fc.array(fc.integer({ min: 0x00, max: 0xff })),
|
||||
// (bytes, wrongBytes) => {
|
||||
// const privKey = C.utils.randomPrivateKey();
|
||||
// const message = new Uint8Array(bytes);
|
||||
// const wrongMessage = new Uint8Array(wrongBytes);
|
||||
// const publicKey = C.getPublicKey(privKey);
|
||||
// const signature = C.sign(message, privKey);
|
||||
// deepStrictEqual(
|
||||
// C.verify(signature, wrongMessage, publicKey),
|
||||
// bytes.toString() === wrongBytes.toString()
|
||||
// );
|
||||
// }
|
||||
// ),
|
||||
// { numRuns: NUM_RUNS }
|
||||
// );
|
||||
// });
|
||||
|
||||
if (C.getSharedSecret) {
|
||||
should('getSharedSecret() should be commutative', () => {
|
||||
for (let i = 0; i < NUM_RUNS; i++) {
|
||||
const asec = C.utils.randomPrivateKey();
|
||||
const apub = C.getPublicKey(asec);
|
||||
const bsec = C.utils.randomPrivateKey();
|
||||
const bpub = C.getPublicKey(bsec);
|
||||
try {
|
||||
deepStrictEqual(C.getSharedSecret(asec, bpub), C.getSharedSecret(bsec, apub));
|
||||
} catch (error) {
|
||||
console.error('not commutative', { asec, apub, bsec, bpub });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
should('secp224k1 sqrt bug', () => {
|
||||
const { Fp } = secp224r1.CURVE;
|
||||
const sqrtMinus1 = Fp.sqrt(-1n);
|
||||
// Verified against sage
|
||||
deepStrictEqual(
|
||||
sqrtMinus1,
|
||||
23621584063597419797792593680131996961517196803742576047493035507225n
|
||||
);
|
||||
deepStrictEqual(
|
||||
Fp.neg(sqrtMinus1),
|
||||
3338362603553219996874421406887633712040719456283732096017030791656n
|
||||
);
|
||||
deepStrictEqual(Fp.sqr(sqrtMinus1), Fp.create(-1n));
|
||||
});
|
||||
|
||||
should('bigInt private keys', () => {
|
||||
// Doesn't support bigints anymore
|
||||
throws(() => ed25519.sign('', 123n));
|
||||
throws(() => ed25519.getPublicKey(123n));
|
||||
throws(() => x25519.getPublicKey(123n));
|
||||
// Weierstrass still supports
|
||||
secp256k1.getPublicKey(123n);
|
||||
secp256k1.sign('', 123n);
|
||||
});
|
||||
|
||||
// ESM is broken.
|
||||
import url from 'url';
|
||||
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
1269
test/ed25519.test.js
1269
test/ed25519.test.js
File diff suppressed because it is too large
Load Diff
1255
test/ed448.test.js
1255
test/ed448.test.js
File diff suppressed because it is too large
Load Diff
@@ -1,19 +1,30 @@
|
||||
import { deepStrictEqual } from 'assert';
|
||||
import { should } from 'micro-should';
|
||||
import { describe, should } from 'micro-should';
|
||||
import { bytesToHex } from '@noble/hashes/utils';
|
||||
// Generic tests for all curves in package
|
||||
import { sha256 } from '@noble/hashes/sha256';
|
||||
import { sha512 } from '@noble/hashes/sha512';
|
||||
import { secp256r1 } from '../lib/esm/p256.js';
|
||||
import { secp384r1 } from '../lib/esm/p384.js';
|
||||
import { secp521r1 } from '../lib/esm/p521.js';
|
||||
import { secp256k1 } from '../lib/esm/secp256k1.js';
|
||||
import { shake128, shake256 } from '@noble/hashes/sha3';
|
||||
import * as secp256r1 from '../lib/esm/p256.js';
|
||||
import * as secp384r1 from '../lib/esm/p384.js';
|
||||
import * as secp521r1 from '../lib/esm/p521.js';
|
||||
import * as ed25519 from '../lib/esm/ed25519.js';
|
||||
import * as ed448 from '../lib/esm/ed448.js';
|
||||
import * as secp256k1 from '../lib/esm/secp256k1.js';
|
||||
import { bls12_381 } from '../lib/esm/bls12-381.js';
|
||||
import { stringToBytes, expand_message_xmd } from '../lib/esm/abstract/hash-to-curve.js';
|
||||
|
||||
import {
|
||||
stringToBytes,
|
||||
expand_message_xmd,
|
||||
expand_message_xof,
|
||||
} from '../lib/esm/abstract/hash-to-curve.js';
|
||||
// XMD
|
||||
import { default as xmd_sha256_38 } from './hash-to-curve/expand_message_xmd_SHA256_38.json' assert { type: 'json' };
|
||||
import { default as xmd_sha256_256 } from './hash-to-curve/expand_message_xmd_SHA256_256.json' assert { type: 'json' };
|
||||
import { default as xmd_sha512_38 } from './hash-to-curve/expand_message_xmd_SHA512_38.json' assert { type: 'json' };
|
||||
// XOF
|
||||
import { default as xof_shake128_36 } from './hash-to-curve/expand_message_xof_SHAKE128_36.json' assert { type: 'json' };
|
||||
import { default as xof_shake128_256 } from './hash-to-curve/expand_message_xof_SHAKE128_256.json' assert { type: 'json' };
|
||||
import { default as xof_shake256_36 } from './hash-to-curve/expand_message_xof_SHAKE256_36.json' assert { type: 'json' };
|
||||
// P256
|
||||
import { default as p256_ro } from './hash-to-curve/P256_XMD:SHA-256_SSWU_RO_.json' assert { type: 'json' };
|
||||
import { default as p256_nu } from './hash-to-curve/P256_XMD:SHA-256_SSWU_NU_.json' assert { type: 'json' };
|
||||
@@ -40,23 +51,51 @@ import { default as ed448_ro } from './hash-to-curve/edwards448_XOF:SHAKE256_ELL
|
||||
import { default as ed448_nu } from './hash-to-curve/edwards448_XOF:SHAKE256_ELL2_NU_.json' assert { type: 'json' };
|
||||
|
||||
function testExpandXMD(hash, vectors) {
|
||||
for (let i = 0; i < vectors.tests.length; i++) {
|
||||
const t = vectors.tests[i];
|
||||
should(`expand_message_xmd/${vectors.hash}/${vectors.DST.length}/${i}`, () => {
|
||||
const p = expand_message_xmd(
|
||||
stringToBytes(t.msg),
|
||||
stringToBytes(vectors.DST),
|
||||
t.len_in_bytes,
|
||||
hash
|
||||
);
|
||||
deepStrictEqual(bytesToHex(p), t.uniform_bytes);
|
||||
});
|
||||
}
|
||||
describe(`${vectors.hash}/${vectors.DST.length}`, () => {
|
||||
for (let i = 0; i < vectors.tests.length; i++) {
|
||||
const t = vectors.tests[i];
|
||||
should(`${vectors.hash}/${vectors.DST.length}/${i}`, () => {
|
||||
const p = expand_message_xmd(
|
||||
stringToBytes(t.msg),
|
||||
stringToBytes(vectors.DST),
|
||||
t.len_in_bytes,
|
||||
hash
|
||||
);
|
||||
deepStrictEqual(bytesToHex(p), t.uniform_bytes);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
testExpandXMD(sha256, xmd_sha256_38);
|
||||
testExpandXMD(sha256, xmd_sha256_256);
|
||||
testExpandXMD(sha512, xmd_sha512_38);
|
||||
describe('expand_message_xmd', () => {
|
||||
testExpandXMD(sha256, xmd_sha256_38);
|
||||
testExpandXMD(sha256, xmd_sha256_256);
|
||||
testExpandXMD(sha512, xmd_sha512_38);
|
||||
});
|
||||
|
||||
function testExpandXOF(hash, vectors) {
|
||||
describe(`${vectors.hash}/${vectors.DST.length}`, () => {
|
||||
for (let i = 0; i < vectors.tests.length; i++) {
|
||||
const t = vectors.tests[i];
|
||||
should(`${i}`, () => {
|
||||
const p = expand_message_xof(
|
||||
stringToBytes(t.msg),
|
||||
stringToBytes(vectors.DST),
|
||||
+t.len_in_bytes,
|
||||
vectors.k,
|
||||
hash
|
||||
);
|
||||
deepStrictEqual(bytesToHex(p), t.uniform_bytes);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
describe('expand_message_xof', () => {
|
||||
testExpandXOF(shake128, xof_shake128_36);
|
||||
testExpandXOF(shake128, xof_shake128_256);
|
||||
testExpandXOF(shake256, xof_shake256_36);
|
||||
});
|
||||
|
||||
function stringToFp(s) {
|
||||
// bls-G2 support
|
||||
@@ -68,37 +107,44 @@ function stringToFp(s) {
|
||||
}
|
||||
|
||||
function testCurve(curve, ro, nu) {
|
||||
for (let i = 0; i < ro.vectors.length; i++) {
|
||||
const t = ro.vectors[i];
|
||||
should(`${ro.curve}/${ro.ciphersuite}(${i})`, () => {
|
||||
const p = curve.Point.hashToCurve(stringToBytes(t.msg), {
|
||||
DST: ro.dst,
|
||||
describe(`${ro.curve}/${ro.ciphersuite}`, () => {
|
||||
for (let i = 0; i < ro.vectors.length; i++) {
|
||||
const t = ro.vectors[i];
|
||||
should(`(${i})`, () => {
|
||||
const p = curve
|
||||
.hashToCurve(stringToBytes(t.msg), {
|
||||
DST: ro.dst,
|
||||
})
|
||||
.toAffine();
|
||||
deepStrictEqual(p.x, stringToFp(t.P.x), 'Px');
|
||||
deepStrictEqual(p.y, stringToFp(t.P.y), 'Py');
|
||||
});
|
||||
deepStrictEqual(p.x, stringToFp(t.P.x), 'Px');
|
||||
deepStrictEqual(p.y, stringToFp(t.P.y), 'Py');
|
||||
});
|
||||
}
|
||||
for (let i = 0; i < nu.vectors.length; i++) {
|
||||
const t = nu.vectors[i];
|
||||
should(`${nu.curve}/${nu.ciphersuite}(${i})`, () => {
|
||||
const p = curve.Point.encodeToCurve(stringToBytes(t.msg), {
|
||||
DST: nu.dst,
|
||||
}
|
||||
});
|
||||
describe(`${nu.curve}/${nu.ciphersuite}`, () => {
|
||||
for (let i = 0; i < nu.vectors.length; i++) {
|
||||
const t = nu.vectors[i];
|
||||
should(`(${i})`, () => {
|
||||
const p = curve
|
||||
.encodeToCurve(stringToBytes(t.msg), {
|
||||
DST: nu.dst,
|
||||
})
|
||||
.toAffine();
|
||||
deepStrictEqual(p.x, stringToFp(t.P.x), 'Px');
|
||||
deepStrictEqual(p.y, stringToFp(t.P.y), 'Py');
|
||||
});
|
||||
deepStrictEqual(p.x, stringToFp(t.P.x), 'Px');
|
||||
deepStrictEqual(p.y, stringToFp(t.P.y), 'Py');
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
testCurve(secp256r1, p256_ro, p256_nu);
|
||||
testCurve(secp384r1, p384_ro, p384_nu);
|
||||
testCurve(secp521r1, p521_ro, p521_nu);
|
||||
// TODO: remove same tests from bls12
|
||||
testCurve(bls12_381.G1, g1_ro, g1_nu);
|
||||
testCurve(bls12_381.G2, g2_ro, g2_nu);
|
||||
testCurve(bls12_381.hashToCurve.G1, g1_ro, g1_nu);
|
||||
testCurve(bls12_381.hashToCurve.G2, g2_ro, g2_nu);
|
||||
testCurve(secp256k1, secp256k1_ro, secp256k1_nu);
|
||||
//testCurve(ed25519, ed25519_ro, ed25519_nu);
|
||||
//testCurve(ed448, ed448_ro, ed448_nu);
|
||||
testCurve(ed25519, ed25519_ro, ed25519_nu);
|
||||
testCurve(ed448, ed448_ro, ed448_nu);
|
||||
|
||||
// ESM is broken.
|
||||
import url from 'url';
|
||||
|
||||
@@ -6,7 +6,7 @@ import './nist.test.js';
|
||||
import './ed448.test.js';
|
||||
import './ed25519.test.js';
|
||||
import './secp256k1.test.js';
|
||||
import './stark/stark.test.js';
|
||||
import './stark/index.test.js';
|
||||
import './jubjub.test.js';
|
||||
import './bls12-381.test.js';
|
||||
import './hash-to-curve.test.js';
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { jubjub, findGroupHash } from '../lib/esm/jubjub.js';
|
||||
import { should } from 'micro-should';
|
||||
import { describe, should } from 'micro-should';
|
||||
import { deepStrictEqual, throws } from 'assert';
|
||||
import { hexToBytes, bytesToHex } from '@noble/hashes/utils';
|
||||
const Point = jubjub.ExtendedPoint;
|
||||
|
||||
const G_SPEND = new jubjub.ExtendedPoint(
|
||||
const G_SPEND = new Point(
|
||||
0x055f1f24f0f0512287e51c3c5a0a6903fc0baf8711de9eafd7c0e66f69d8d2dbn,
|
||||
0x566178b2505fdd52132a5007d80a04652842e78ffb376897588f406278214ed7n,
|
||||
0x0141fafa1f11088a3b2007c14d652375888f3b37838ba6bdffae096741ceddfen,
|
||||
0x12eada93c0b7d595f5f04f5ebfb4b7d033ef2884136475cab5e41ce17db5be9cn
|
||||
);
|
||||
const G_PROOF = new jubjub.ExtendedPoint(
|
||||
const G_PROOF = new Point(
|
||||
0x0174d54ce9fad258a2f8a86a1deabf15c7a2b51106b0fbcd9d29020f78936f71n,
|
||||
0x16871d6d877dcd222e4ec3bccb3f37cb1865a2d37dd3a5dcbc032a69b62b4445n,
|
||||
0x57a3cd31e496d82bd4aa78bd5ecd751cfb76d54a5d3f4560866379f9fc11c9b3n,
|
||||
@@ -18,53 +18,61 @@ const G_PROOF = new jubjub.ExtendedPoint(
|
||||
|
||||
const getXY = (p) => ({ x: p.x, y: p.y });
|
||||
|
||||
should('toHex/fromHex', () => {
|
||||
// More than field
|
||||
throws(() =>
|
||||
jubjub.Point.fromHex(
|
||||
describe('jubjub', () => {
|
||||
should('toHex/fromHex', () => {
|
||||
// More than field
|
||||
throws(() =>
|
||||
Point.fromHex(
|
||||
new Uint8Array([
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
])
|
||||
)
|
||||
);
|
||||
// Multiplicative generator (sqrt == null), not on curve.
|
||||
throws(() =>
|
||||
Point.fromHex(
|
||||
new Uint8Array([
|
||||
7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0,
|
||||
])
|
||||
)
|
||||
);
|
||||
const tmp = Point.fromHex(
|
||||
new Uint8Array([
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
])
|
||||
)
|
||||
);
|
||||
// Multiplicative generator (sqrt == null), not on curve.
|
||||
throws(() =>
|
||||
jubjub.Point.fromHex(
|
||||
new Uint8Array([
|
||||
7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0,
|
||||
])
|
||||
)
|
||||
);
|
||||
const tmp = jubjub.Point.fromHex(
|
||||
new Uint8Array([
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0,
|
||||
])
|
||||
);
|
||||
deepStrictEqual(tmp.x, 0x8d51ccce760304d0ec030002760300000001000000000000n);
|
||||
deepStrictEqual(tmp.y, 0n);
|
||||
);
|
||||
deepStrictEqual(tmp.x, 0x8d51ccce760304d0ec030002760300000001000000000000n);
|
||||
deepStrictEqual(tmp.y, 0n);
|
||||
|
||||
const S = G_SPEND.toAffine().toRawBytes();
|
||||
const S2 = G_SPEND.double().toAffine().toRawBytes();
|
||||
const P = G_PROOF.toAffine().toRawBytes();
|
||||
const P2 = G_PROOF.double().toAffine().toRawBytes();
|
||||
const S_exp = jubjub.Point.fromHex(S);
|
||||
const S2_exp = jubjub.Point.fromHex(S2);
|
||||
const P_exp = jubjub.Point.fromHex(P);
|
||||
const P2_exp = jubjub.Point.fromHex(P2);
|
||||
deepStrictEqual(getXY(G_SPEND.toAffine()), getXY(S_exp));
|
||||
deepStrictEqual(getXY(G_SPEND.double().toAffine()), getXY(S2_exp));
|
||||
deepStrictEqual(getXY(G_PROOF.toAffine()), getXY(P_exp));
|
||||
deepStrictEqual(getXY(G_PROOF.double().toAffine()), getXY(P2_exp));
|
||||
});
|
||||
const S = G_SPEND.toRawBytes();
|
||||
const S2 = G_SPEND.double().toRawBytes();
|
||||
const P = G_PROOF.toRawBytes();
|
||||
const P2 = G_PROOF.double().toRawBytes();
|
||||
const S_exp = Point.fromHex(S);
|
||||
const S2_exp = Point.fromHex(S2);
|
||||
const P_exp = Point.fromHex(P);
|
||||
const P2_exp = Point.fromHex(P2);
|
||||
deepStrictEqual(getXY(G_SPEND.toAffine()), getXY(S_exp));
|
||||
deepStrictEqual(getXY(G_SPEND.double().toAffine()), getXY(S2_exp));
|
||||
deepStrictEqual(getXY(G_PROOF.toAffine()), getXY(P_exp));
|
||||
deepStrictEqual(getXY(G_PROOF.double().toAffine()), getXY(P2_exp));
|
||||
});
|
||||
|
||||
should('Find generators', () => {
|
||||
const spend = findGroupHash(new Uint8Array(), new Uint8Array([90, 99, 97, 115, 104, 95, 71, 95]));
|
||||
const proof = findGroupHash(new Uint8Array(), new Uint8Array([90, 99, 97, 115, 104, 95, 72, 95]));
|
||||
deepStrictEqual(getXY(spend.toAffine()), getXY(G_SPEND.toAffine()));
|
||||
deepStrictEqual(getXY(proof.toAffine()), getXY(G_PROOF.toAffine()));
|
||||
should('Find generators', () => {
|
||||
const spend = findGroupHash(
|
||||
new Uint8Array(),
|
||||
new Uint8Array([90, 99, 97, 115, 104, 95, 71, 95])
|
||||
);
|
||||
const proof = findGroupHash(
|
||||
new Uint8Array(),
|
||||
new Uint8Array([90, 99, 97, 115, 104, 95, 72, 95])
|
||||
);
|
||||
deepStrictEqual(getXY(spend.toAffine()), getXY(G_SPEND.toAffine()));
|
||||
deepStrictEqual(getXY(proof.toAffine()), getXY(G_PROOF.toAffine()));
|
||||
});
|
||||
});
|
||||
|
||||
// ESM is broken.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { deepStrictEqual, throws } from 'assert';
|
||||
import { should } from 'micro-should';
|
||||
import { describe, should } from 'micro-should';
|
||||
import { secp192r1, P192 } from '../lib/esm/p192.js';
|
||||
import { secp224r1, P224 } from '../lib/esm/p224.js';
|
||||
import { secp256r1, P256 } from '../lib/esm/p256.js';
|
||||
@@ -11,164 +11,11 @@ import { default as ecdsa } from './wycheproof/ecdsa_test.json' assert { type: '
|
||||
import { default as ecdh } from './wycheproof/ecdh_test.json' assert { type: 'json' };
|
||||
import { default as rfc6979 } from './fixtures/rfc6979.json' assert { type: 'json' };
|
||||
|
||||
const hex = bytesToHex;
|
||||
|
||||
// prettier-ignore
|
||||
const NIST = {
|
||||
secp192r1, P192,
|
||||
secp224r1, P224,
|
||||
secp256r1, P256,
|
||||
secp384r1, P384,
|
||||
secp521r1, P521,
|
||||
secp256k1,
|
||||
};
|
||||
|
||||
should('Curve Fields', () => {
|
||||
const vectors = {
|
||||
secp192r1: 0xfffffffffffffffffffffffffffffffeffffffffffffffffn,
|
||||
secp224r1: 0xffffffffffffffffffffffffffffffff000000000000000000000001n,
|
||||
secp256r1: 0xffffffff00000001000000000000000000000000ffffffffffffffffffffffffn,
|
||||
secp256k1: 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2fn,
|
||||
secp384r1:
|
||||
0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffffn,
|
||||
secp521r1:
|
||||
0x01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffn,
|
||||
};
|
||||
for (const n in vectors) deepStrictEqual(NIST[n].CURVE.Fp.ORDER, vectors[n]);
|
||||
});
|
||||
|
||||
should('wychenproof ECDSA vectors', () => {
|
||||
for (const group of ecdsa.testGroups) {
|
||||
// Tested in secp256k1.test.js
|
||||
if (group.key.curve === 'secp256k1') continue;
|
||||
let CURVE = NIST[group.key.curve];
|
||||
if (!CURVE) continue;
|
||||
if (group.key.curve === 'secp224r1' && group.sha !== 'SHA-224') {
|
||||
if (group.sha === 'SHA-256') CURVE = CURVE.create(sha256);
|
||||
}
|
||||
const pubKey = CURVE.Point.fromHex(group.key.uncompressed);
|
||||
deepStrictEqual(pubKey.x, BigInt(`0x${group.key.wx}`));
|
||||
deepStrictEqual(pubKey.y, BigInt(`0x${group.key.wy}`));
|
||||
for (const test of group.tests) {
|
||||
if (['Hash weaker than DL-group'].includes(test.comment)) {
|
||||
continue;
|
||||
}
|
||||
const m = CURVE.CURVE.hash(hexToBytes(test.msg));
|
||||
if (test.result === 'valid' || test.result === 'acceptable') {
|
||||
try {
|
||||
CURVE.Signature.fromDER(test.sig);
|
||||
} catch (e) {
|
||||
// Some test has invalid signature which we don't accept
|
||||
if (e.message.includes('Invalid signature: incorrect length')) continue;
|
||||
throw e;
|
||||
}
|
||||
const verified = CURVE.verify(test.sig, m, pubKey);
|
||||
deepStrictEqual(verified, true, 'valid');
|
||||
} else if (test.result === 'invalid') {
|
||||
let failed = false;
|
||||
try {
|
||||
failed = !CURVE.verify(test.sig, m, pubKey);
|
||||
} catch (error) {
|
||||
failed = true;
|
||||
}
|
||||
deepStrictEqual(failed, true, 'invalid');
|
||||
} else throw new Error('unknown test result');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
should('wychenproof ECDH vectors', () => {
|
||||
for (const group of ecdh.testGroups) {
|
||||
// // Tested in secp256k1.test.js
|
||||
// if (group.key.curve === 'secp256k1') continue;
|
||||
// We don't have SHA-224
|
||||
const CURVE = NIST[group.curve];
|
||||
if (!CURVE) continue;
|
||||
for (const test of group.tests) {
|
||||
if (test.result === 'valid' || test.result === 'acceptable') {
|
||||
try {
|
||||
const pub = CURVE.Point.fromHex(test.public);
|
||||
} catch (e) {
|
||||
if (e.message.includes('Point.fromHex: received invalid point.')) continue;
|
||||
throw e;
|
||||
}
|
||||
const shared = CURVE.getSharedSecret(test.private, test.public);
|
||||
deepStrictEqual(shared, test.shared, 'valid');
|
||||
} else if (test.result === 'invalid') {
|
||||
let failed = false;
|
||||
try {
|
||||
CURVE.getSharedSecret(test.private, test.public);
|
||||
} catch (error) {
|
||||
failed = true;
|
||||
}
|
||||
deepStrictEqual(failed, true, 'invalid');
|
||||
} else throw new Error('unknown test result');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
import { default as ecdh_secp224r1_test } from './wycheproof/ecdh_secp224r1_test.json' assert { type: 'json' };
|
||||
import { default as ecdh_secp256r1_test } from './wycheproof/ecdh_secp256r1_test.json' assert { type: 'json' };
|
||||
import { default as ecdh_secp256k1_test } from './wycheproof/ecdh_secp256k1_test.json' assert { type: 'json' };
|
||||
import { default as ecdh_secp384r1_test } from './wycheproof/ecdh_secp384r1_test.json' assert { type: 'json' };
|
||||
import { default as ecdh_secp521r1_test } from './wycheproof/ecdh_secp521r1_test.json' assert { type: 'json' };
|
||||
|
||||
// More per curve tests
|
||||
const WYCHEPROOF_ECDH = {
|
||||
P224: {
|
||||
curve: P224,
|
||||
tests: [ecdh_secp224r1_test],
|
||||
},
|
||||
P256: {
|
||||
curve: P256,
|
||||
tests: [ecdh_secp256r1_test],
|
||||
},
|
||||
secp256k1: {
|
||||
curve: secp256k1,
|
||||
tests: [ecdh_secp256k1_test],
|
||||
},
|
||||
P384: {
|
||||
curve: P384,
|
||||
tests: [ecdh_secp384r1_test],
|
||||
},
|
||||
P521: {
|
||||
curve: P521,
|
||||
tests: [ecdh_secp521r1_test],
|
||||
},
|
||||
};
|
||||
|
||||
for (const name in WYCHEPROOF_ECDH) {
|
||||
const { curve, tests } = WYCHEPROOF_ECDH[name];
|
||||
for (let i = 0; i < tests.length; i++) {
|
||||
const test = tests[i];
|
||||
for (let j = 0; j < test.testGroups.length; j++) {
|
||||
const group = test.testGroups[j];
|
||||
should(`Wycheproof/ECDH ${name} (${i}/${j})`, () => {
|
||||
for (const test of group.tests) {
|
||||
if (test.result === 'valid' || test.result === 'acceptable') {
|
||||
try {
|
||||
const pub = curve.Point.fromHex(test.public);
|
||||
} catch (e) {
|
||||
if (e.message.includes('Point.fromHex: received invalid point.')) continue;
|
||||
throw e;
|
||||
}
|
||||
const shared = curve.getSharedSecret(test.private, test.public);
|
||||
deepStrictEqual(hex(shared), test.shared, 'valid');
|
||||
} else if (test.result === 'invalid') {
|
||||
let failed = false;
|
||||
try {
|
||||
curve.getSharedSecret(test.private, test.public);
|
||||
} catch (error) {
|
||||
failed = true;
|
||||
}
|
||||
deepStrictEqual(failed, true, 'invalid');
|
||||
} else throw new Error('unknown test result');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tests with custom hashes
|
||||
import { default as secp224r1_sha224_test } from './wycheproof/ecdsa_secp224r1_sha224_test.json' assert { type: 'json' };
|
||||
import { default as secp224r1_sha256_test } from './wycheproof/ecdsa_secp224r1_sha256_test.json' assert { type: 'json' };
|
||||
@@ -199,6 +46,123 @@ import { sha3_224, sha3_256, sha3_384, sha3_512 } from '@noble/hashes/sha3';
|
||||
import { sha512, sha384 } from '@noble/hashes/sha512';
|
||||
import { sha224, sha256 } from '@noble/hashes/sha256';
|
||||
|
||||
const hex = bytesToHex;
|
||||
|
||||
// prettier-ignore
|
||||
const NIST = {
|
||||
secp192r1, P192,
|
||||
secp224r1, P224,
|
||||
secp256r1, P256,
|
||||
secp384r1, P384,
|
||||
secp521r1, P521,
|
||||
secp256k1,
|
||||
};
|
||||
|
||||
describe('NIST curves', () => {});
|
||||
should('fields', () => {
|
||||
const vectors = {
|
||||
secp192r1: 0xfffffffffffffffffffffffffffffffeffffffffffffffffn,
|
||||
secp224r1: 0xffffffffffffffffffffffffffffffff000000000000000000000001n,
|
||||
secp256r1: 0xffffffff00000001000000000000000000000000ffffffffffffffffffffffffn,
|
||||
secp256k1: 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2fn,
|
||||
secp384r1:
|
||||
0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffffn,
|
||||
secp521r1:
|
||||
0x01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffn,
|
||||
};
|
||||
for (const n in vectors) deepStrictEqual(NIST[n].CURVE.Fp.ORDER, vectors[n]);
|
||||
});
|
||||
|
||||
describe('wycheproof ECDH', () => {
|
||||
for (const group of ecdh.testGroups) {
|
||||
// // Tested in secp256k1.test.js
|
||||
// if (group.key.curve === 'secp256k1') continue;
|
||||
// We don't have SHA-224
|
||||
const CURVE = NIST[group.curve];
|
||||
if (!CURVE) continue;
|
||||
should(group.curve, () => {
|
||||
for (const test of group.tests) {
|
||||
if (test.result === 'valid' || test.result === 'acceptable') {
|
||||
try {
|
||||
const pub = CURVE.ProjectivePoint.fromHex(test.public);
|
||||
} catch (e) {
|
||||
// Our strict validation filter doesn't let weird-length DER vectors
|
||||
if (e.message.startsWith('Point of length')) continue;
|
||||
throw e;
|
||||
}
|
||||
const shared = CURVE.getSharedSecret(test.private, test.public);
|
||||
deepStrictEqual(shared, test.shared, 'valid');
|
||||
} else if (test.result === 'invalid') {
|
||||
let failed = false;
|
||||
try {
|
||||
CURVE.getSharedSecret(test.private, test.public);
|
||||
} catch (error) {
|
||||
failed = true;
|
||||
}
|
||||
deepStrictEqual(failed, true, 'invalid');
|
||||
} else throw new Error('unknown test result');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// More per curve tests
|
||||
const WYCHEPROOF_ECDH = {
|
||||
P224: {
|
||||
curve: P224,
|
||||
tests: [ecdh_secp224r1_test],
|
||||
},
|
||||
P256: {
|
||||
curve: P256,
|
||||
tests: [ecdh_secp256r1_test],
|
||||
},
|
||||
secp256k1: {
|
||||
curve: secp256k1,
|
||||
tests: [ecdh_secp256k1_test],
|
||||
},
|
||||
P384: {
|
||||
curve: P384,
|
||||
tests: [ecdh_secp384r1_test],
|
||||
},
|
||||
P521: {
|
||||
curve: P521,
|
||||
tests: [ecdh_secp521r1_test],
|
||||
},
|
||||
};
|
||||
|
||||
for (const name in WYCHEPROOF_ECDH) {
|
||||
const { curve, tests } = WYCHEPROOF_ECDH[name];
|
||||
for (let i = 0; i < tests.length; i++) {
|
||||
const test = tests[i];
|
||||
for (let j = 0; j < test.testGroups.length; j++) {
|
||||
const group = test.testGroups[j];
|
||||
should(`additional ${name} (${i}/${j})`, () => {
|
||||
for (const test of group.tests) {
|
||||
if (test.result === 'valid' || test.result === 'acceptable') {
|
||||
try {
|
||||
const pub = curve.ProjectivePoint.fromHex(test.public);
|
||||
} catch (e) {
|
||||
// Our strict validation filter doesn't let weird-length DER vectors
|
||||
if (e.message.includes('Point of length')) continue;
|
||||
throw e;
|
||||
}
|
||||
const shared = curve.getSharedSecret(test.private, test.public);
|
||||
deepStrictEqual(hex(shared), test.shared, 'valid');
|
||||
} else if (test.result === 'invalid') {
|
||||
let failed = false;
|
||||
try {
|
||||
curve.getSharedSecret(test.private, test.public);
|
||||
} catch (error) {
|
||||
failed = true;
|
||||
}
|
||||
deepStrictEqual(failed, true, 'invalid');
|
||||
} else throw new Error('unknown test result');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const WYCHEPROOF_ECDSA = {
|
||||
P224: {
|
||||
curve: P224,
|
||||
@@ -232,7 +196,6 @@ const WYCHEPROOF_ECDSA = {
|
||||
secp256k1: {
|
||||
curve: secp256k1,
|
||||
hashes: {
|
||||
// TODO: debug why fails, can be bug
|
||||
sha256: {
|
||||
hash: sha256,
|
||||
tests: [secp256k1_sha256_test],
|
||||
@@ -309,31 +272,33 @@ const WYCHEPROOF_ECDSA = {
|
||||
};
|
||||
|
||||
function runWycheproof(name, CURVE, group, index) {
|
||||
const pubKey = CURVE.Point.fromHex(group.key.uncompressed);
|
||||
const pubKey = CURVE.ProjectivePoint.fromHex(group.key.uncompressed);
|
||||
deepStrictEqual(pubKey.x, BigInt(`0x${group.key.wx}`));
|
||||
deepStrictEqual(pubKey.y, BigInt(`0x${group.key.wy}`));
|
||||
const pubR = pubKey.toRawBytes();
|
||||
for (const test of group.tests) {
|
||||
const m = CURVE.CURVE.hash(hexToBytes(test.msg));
|
||||
const { sig } = test;
|
||||
|
||||
if (test.result === 'valid' || test.result === 'acceptable') {
|
||||
try {
|
||||
CURVE.Signature.fromDER(test.sig);
|
||||
CURVE.Signature.fromDER(sig);
|
||||
} catch (e) {
|
||||
// Some tests has invalid signature which we don't accept
|
||||
if (e.message.includes('Invalid signature: incorrect length')) continue;
|
||||
throw e;
|
||||
}
|
||||
const verified = CURVE.verify(test.sig, m, pubKey);
|
||||
const verified = CURVE.verify(sig, m, pubR);
|
||||
if (name === 'secp256k1') {
|
||||
// lowS: true for secp256k1
|
||||
deepStrictEqual(verified, !CURVE.Signature.fromDER(test.sig).hasHighS(), `${index}: valid`);
|
||||
deepStrictEqual(verified, !CURVE.Signature.fromDER(sig).hasHighS(), `${index}: valid`);
|
||||
} else {
|
||||
deepStrictEqual(verified, true, `${index}: valid`);
|
||||
}
|
||||
} else if (test.result === 'invalid') {
|
||||
let failed = false;
|
||||
try {
|
||||
failed = !CURVE.verify(test.sig, m, pubKey);
|
||||
failed = !CURVE.verify(sig, m, pubR);
|
||||
} catch (error) {
|
||||
failed = true;
|
||||
}
|
||||
@@ -342,40 +307,85 @@ function runWycheproof(name, CURVE, group, index) {
|
||||
}
|
||||
}
|
||||
|
||||
for (const name in WYCHEPROOF_ECDSA) {
|
||||
const { curve, hashes } = WYCHEPROOF_ECDSA[name];
|
||||
for (const hName in hashes) {
|
||||
const { hash, tests } = hashes[hName];
|
||||
const CURVE = curve.create(hash);
|
||||
should(`Wycheproof/WYCHEPROOF_ECDSA ${name}/${hName}`, () => {
|
||||
for (let i = 0; i < tests.length; i++) {
|
||||
const groups = tests[i].testGroups;
|
||||
for (let j = 0; j < groups.length; j++) {
|
||||
const group = groups[j];
|
||||
runWycheproof(name, CURVE, group, `${i}/${j}`);
|
||||
describe('wycheproof ECDSA', () => {
|
||||
should('generic', () => {
|
||||
for (const group of ecdsa.testGroups) {
|
||||
// Tested in secp256k1.test.js
|
||||
if (group.key.curve === 'secp256k1') continue;
|
||||
let CURVE = NIST[group.key.curve];
|
||||
if (!CURVE) continue;
|
||||
if (group.key.curve === 'secp224r1' && group.sha !== 'SHA-224') {
|
||||
if (group.sha === 'SHA-256') CURVE = CURVE.create(sha256);
|
||||
}
|
||||
const pubKey = CURVE.ProjectivePoint.fromHex(group.key.uncompressed);
|
||||
deepStrictEqual(pubKey.x, BigInt(`0x${group.key.wx}`));
|
||||
deepStrictEqual(pubKey.y, BigInt(`0x${group.key.wy}`));
|
||||
for (const test of group.tests) {
|
||||
if (['Hash weaker than DL-group'].includes(test.comment)) {
|
||||
continue;
|
||||
}
|
||||
const m = CURVE.CURVE.hash(hexToBytes(test.msg));
|
||||
if (test.result === 'valid' || test.result === 'acceptable') {
|
||||
try {
|
||||
CURVE.Signature.fromDER(test.sig);
|
||||
} catch (e) {
|
||||
// Some test has invalid signature which we don't accept
|
||||
if (e.message.includes('Invalid signature: incorrect length')) continue;
|
||||
throw e;
|
||||
}
|
||||
const verified = CURVE.verify(test.sig, m, pubKey.toHex());
|
||||
deepStrictEqual(verified, true, 'valid');
|
||||
} else if (test.result === 'invalid') {
|
||||
let failed = false;
|
||||
try {
|
||||
failed = !CURVE.verify(test.sig, m, pubKey.toHex());
|
||||
} catch (error) {
|
||||
failed = true;
|
||||
}
|
||||
deepStrictEqual(failed, true, 'invalid');
|
||||
} else throw new Error('unknown test result');
|
||||
}
|
||||
}
|
||||
});
|
||||
for (const name in WYCHEPROOF_ECDSA) {
|
||||
const { curve, hashes } = WYCHEPROOF_ECDSA[name];
|
||||
describe(name, () => {
|
||||
for (const hName in hashes) {
|
||||
const { hash, tests } = hashes[hName];
|
||||
const CURVE = curve.create(hash);
|
||||
should(`${name}/${hName}`, () => {
|
||||
for (let i = 0; i < tests.length; i++) {
|
||||
const groups = tests[i].testGroups;
|
||||
for (let j = 0; j < groups.length; j++) {
|
||||
const group = groups[j];
|
||||
runWycheproof(name, CURVE, group, `${i}/${j}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const hexToBigint = (hex) => BigInt(`0x${hex}`);
|
||||
should('RFC6979', () => {
|
||||
describe('RFC6979', () => {
|
||||
for (const v of rfc6979) {
|
||||
const curve = NIST[v.curve];
|
||||
deepStrictEqual(curve.CURVE.n, hexToBigint(v.q));
|
||||
const pubKey = curve.getPublicKey(v.private);
|
||||
const pubPoint = curve.Point.fromHex(pubKey);
|
||||
deepStrictEqual(pubPoint.x, hexToBigint(v.Ux));
|
||||
deepStrictEqual(pubPoint.y, hexToBigint(v.Uy));
|
||||
for (const c of v.cases) {
|
||||
const h = curve.CURVE.hash(c.message);
|
||||
const sigObj = curve.sign(h, v.private);
|
||||
deepStrictEqual(sigObj.r, hexToBigint(c.r), 'R');
|
||||
deepStrictEqual(sigObj.s, hexToBigint(c.s), 'S');
|
||||
deepStrictEqual(curve.verify(sigObj.toDERRawBytes(), h, pubKey), true, 'verify(1)');
|
||||
deepStrictEqual(curve.verify(sigObj, h, pubKey), true, 'verify(2)');
|
||||
}
|
||||
should(v.curve, () => {
|
||||
const curve = NIST[v.curve];
|
||||
deepStrictEqual(curve.CURVE.n, hexToBigint(v.q));
|
||||
const pubKey = curve.getPublicKey(v.private);
|
||||
const pubPoint = curve.ProjectivePoint.fromHex(pubKey);
|
||||
deepStrictEqual(pubPoint.x, hexToBigint(v.Ux));
|
||||
deepStrictEqual(pubPoint.y, hexToBigint(v.Uy));
|
||||
for (const c of v.cases) {
|
||||
const h = curve.CURVE.hash(c.message);
|
||||
const sigObj = curve.sign(h, v.private);
|
||||
deepStrictEqual(sigObj.r, hexToBigint(c.r), 'R');
|
||||
deepStrictEqual(sigObj.s, hexToBigint(c.s), 'S');
|
||||
deepStrictEqual(curve.verify(sigObj.toDERRawBytes(), h, pubKey), true, 'verify(1)');
|
||||
deepStrictEqual(curve.verify(sigObj, h, pubKey), true, 'verify(2)');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
375
test/poseidon.test.js
Normal file
375
test/poseidon.test.js
Normal file
@@ -0,0 +1,375 @@
|
||||
import { deepStrictEqual, throws } from 'assert';
|
||||
import { should, describe } from 'micro-should';
|
||||
import * as poseidon from '../lib/esm/abstract/poseidon.js';
|
||||
import * as stark from '../lib/esm/stark.js';
|
||||
import * as mod from '../lib/esm/abstract/modular.js';
|
||||
import { default as pvectors } from './vectors/poseidon.json' assert { type: 'json' };
|
||||
const { st1, st2, st3, st4 } = pvectors;
|
||||
|
||||
describe('Stark', () => {
|
||||
should('poseidonMdsMatrixUnsafe', () => {
|
||||
const matrix = [
|
||||
[
|
||||
2778560475384578201077246683568670693743746494974613838537993780462451025202n,
|
||||
1175299404131241652930097281601393692628174430208909163156444576599667748918n,
|
||||
459930634481240293374476654621049426021644833445120509139335338093973616187n,
|
||||
],
|
||||
[
|
||||
2699370377471722242958186781613316939129713429759631049128040020458992590651n,
|
||||
1488831960940040807419416081499284128899207850625157044437836107358246188803n,
|
||||
3405112981980800875534081635548548562399171531483475155039499736396630179833n,
|
||||
],
|
||||
|
||||
[
|
||||
1860070716810022053527433635909648527418980081585070357136946388030401399342n,
|
||||
2606527819893847364468965441606872534271438365089422719512470850627617054272n,
|
||||
2715867691630559973784374069384091521307896505826088878858115800121387149186n,
|
||||
],
|
||||
];
|
||||
deepStrictEqual(stark._poseidonMDS(stark.Fp251, 'HadesMDS', 3, 0), matrix);
|
||||
});
|
||||
|
||||
should('HadesPermutation', () => {
|
||||
deepStrictEqual(
|
||||
stark.poseidonSmall([
|
||||
4379311784651118086770398084575492314150568148003994287303975907890254409956n,
|
||||
5329163686893598957822497554130545759427567507701132391649270915797304266381n,
|
||||
1081797873147645298856697595691862435558345225505029083672323747888463248125n,
|
||||
]),
|
||||
[
|
||||
1342232677189718451682683203787286758407058155581807117466384919996430343159n,
|
||||
380853961496438693334706417244065195303131974442781224856980145160981376662n,
|
||||
1919212703304954644851339421413808305076993030243665926017858381407659820613n,
|
||||
]
|
||||
);
|
||||
});
|
||||
should('HadesPermutation (custom)', () => {
|
||||
const h = stark.poseidonCreate({
|
||||
Fp: stark.Fp251,
|
||||
rate: 2,
|
||||
capacity: 1,
|
||||
roundsFull: 8,
|
||||
roundsPartial: 83,
|
||||
});
|
||||
deepStrictEqual(
|
||||
h([
|
||||
4379311784651118086770398084575492314150568148003994287303975907890254409956n,
|
||||
5329163686893598957822497554130545759427567507701132391649270915797304266381n,
|
||||
1081797873147645298856697595691862435558345225505029083672323747888463248125n,
|
||||
]),
|
||||
[
|
||||
2864461397224564530993577865807718592436235694918699912757414692654057505365n,
|
||||
1576206983934669422583425346343473837630736957734769961428118554039862202613n,
|
||||
1607006208879950753054674913136990521997740361932184292107790666308092455675n,
|
||||
]
|
||||
);
|
||||
});
|
||||
should('HadesPermutation (custom, Fp253)', () => {
|
||||
const h = stark.poseidonCreate({
|
||||
Fp: stark.Fp253,
|
||||
rate: 2,
|
||||
capacity: 1,
|
||||
roundsFull: 8,
|
||||
roundsPartial: 83,
|
||||
});
|
||||
deepStrictEqual(
|
||||
h([
|
||||
4379311784651118086770398084575492314150568148003994287303975907890254409956n,
|
||||
5329163686893598957822497554130545759427567507701132391649270915797304266381n,
|
||||
1081797873147645298856697595691862435558345225505029083672323747888463248125n,
|
||||
]),
|
||||
[
|
||||
11142411210283675631592374649001218595612035205233832049083369488791454026844n,
|
||||
98304838055259883374145304326851527594402230455144399354815642835291000581n,
|
||||
8643534790068701259242695637167859384191499281344739826454631748110172472997n,
|
||||
]
|
||||
);
|
||||
});
|
||||
should('PoseidonHash', () => {
|
||||
deepStrictEqual(
|
||||
stark.poseidonHash(
|
||||
4379311784651118086770398084575492314150568148003994287303975907890254409956n,
|
||||
5329163686893598957822497554130545759427567507701132391649270915797304266381n
|
||||
),
|
||||
2457757238178986673695038558497063891521456354791980183317105434323761563347n
|
||||
);
|
||||
});
|
||||
should('PoseidonHash (custom)', () => {
|
||||
const h = stark.poseidonCreate({
|
||||
Fp: stark.Fp251,
|
||||
rate: 2,
|
||||
capacity: 1,
|
||||
roundsFull: 8,
|
||||
roundsPartial: 83,
|
||||
});
|
||||
deepStrictEqual(
|
||||
stark.poseidonHash(
|
||||
4379311784651118086770398084575492314150568148003994287303975907890254409956n,
|
||||
5329163686893598957822497554130545759427567507701132391649270915797304266381n,
|
||||
h
|
||||
),
|
||||
654164301216498483748450956182386165976155551413834652546305861430119544536n
|
||||
);
|
||||
});
|
||||
should('PoseidonHash (custom, Fp253)', () => {
|
||||
const h = stark.poseidonCreate({
|
||||
Fp: stark.Fp253,
|
||||
rate: 2,
|
||||
capacity: 1,
|
||||
roundsFull: 8,
|
||||
roundsPartial: 83,
|
||||
});
|
||||
deepStrictEqual(
|
||||
stark.poseidonHash(
|
||||
4379311784651118086770398084575492314150568148003994287303975907890254409956n,
|
||||
5329163686893598957822497554130545759427567507701132391649270915797304266381n,
|
||||
h
|
||||
),
|
||||
9557424461253897982213839283192966960594440725760392861778010931094267239786n
|
||||
);
|
||||
});
|
||||
});
|
||||
// Official vectors: https://extgit.iaik.tugraz.at/krypto/hadeshash/-/blob/master/code/test_vectors.txt
|
||||
|
||||
should('poseidonperm_x5_255_3', () => {
|
||||
const Fp = mod.Fp(BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001'));
|
||||
|
||||
const mds = [
|
||||
[
|
||||
0x3d955d6c02fe4d7cb500e12f2b55eff668a7b4386bd27413766713c93f2acfcdn,
|
||||
0x3798866f4e6058035dcf8addb2cf1771fac234bcc8fc05d6676e77e797f224bfn,
|
||||
0x2c51456a7bf2467eac813649f3f25ea896eac27c5da020dae54a6e640278fda2n,
|
||||
],
|
||||
[
|
||||
0x20088ca07bbcd7490a0218ebc0ecb31d0ea34840e2dc2d33a1a5adfecff83b43n,
|
||||
0x1d04ba0915e7807c968ea4b1cb2d610c7f9a16b4033f02ebacbb948c86a988c3n,
|
||||
0x5387ccd5729d7acbd09d96714d1d18bbd0eeaefb2ddee3d2ef573c9c7f953307n,
|
||||
],
|
||||
[
|
||||
0x1e208f585a72558534281562cad89659b428ec61433293a8d7f0f0e38a6726acn,
|
||||
0x0455ebf862f0b60f69698e97d36e8aafd4d107cae2b61be1858b23a3363642e0n,
|
||||
0x569e2c206119e89455852059f707370e2c1fc9721f6c50991cedbbf782daef54n,
|
||||
],
|
||||
];
|
||||
|
||||
const t = 3;
|
||||
|
||||
const roundConstants = poseidon.splitConstants(st1.map(BigInt), t);
|
||||
|
||||
const poseidon_x5_255_3 = poseidon.poseidon({
|
||||
Fp,
|
||||
t,
|
||||
roundsFull: 8,
|
||||
roundsPartial: 57,
|
||||
mds,
|
||||
roundConstants,
|
||||
});
|
||||
deepStrictEqual(
|
||||
poseidon_x5_255_3([
|
||||
0x0000000000000000000000000000000000000000000000000000000000000000n,
|
||||
0x0000000000000000000000000000000000000000000000000000000000000001n,
|
||||
0x0000000000000000000000000000000000000000000000000000000000000002n,
|
||||
]),
|
||||
[
|
||||
0x28ce19420fc246a05553ad1e8c98f5c9d67166be2c18e9e4cb4b4e317dd2a78an,
|
||||
0x51f3e312c95343a896cfd8945ea82ba956c1118ce9b9859b6ea56637b4b1ddc4n,
|
||||
0x3b2b69139b235626a0bfb56c9527ae66a7bf486ad8c11c14d1da0c69bbe0f79an,
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
should('poseidonperm_x5_255_5', () => {
|
||||
const Fp = mod.Fp(0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001n);
|
||||
const t = 5;
|
||||
|
||||
const mds = [
|
||||
[
|
||||
0x354423b163d1078b0dd645be56316e34a9b98e52dcf9f469be44b108be46c107n,
|
||||
0x44778737e8bc1154aca1cd92054a1e5b83808403705f7d54da88bbd1920e1053n,
|
||||
0x5872eefb5ab6b2946556524168a2aebb69afd513a2fff91e50167b1f6e4055e0n,
|
||||
0x43dff85b25129835819bc8c95819f1a34136f6114e900cd3656e1b9e0e13f86an,
|
||||
0x07803d2ffe72940596803f244ac090a9cf2d3616546520bc360c7eed0b81cbf8n,
|
||||
],
|
||||
[
|
||||
0x45d6bc4b818e2b9a53e0e2c0a08f70c34167fd8128e05ac800651ddfee0932d1n,
|
||||
0x08317abbb9e5046b22dfb79e64c8184855107c1d95dddd2b63ca10dddea9ff1an,
|
||||
0x1bb80eba77c5dcffafb55ccba4ae39ac8f94a054f2a0ee3006b362f709d5e470n,
|
||||
0x038e75bdcf8be7fd3a1e844c4de7333531bbd5a8d2c3779627df88e7480e7c5cn,
|
||||
0x2dd797a699e620ea6b31b91ba3fad4a82f40cffb3e8a30c0b7a546ff69a9002bn,
|
||||
],
|
||||
[
|
||||
0x4b906f9ee339b196e958e3541b555b4b53e540a113b2f1cabba627be16eb5608n,
|
||||
0x605f0c707b82ef287f46431f9241fe4acf0b7ddb151803cbcf1e7bbd27c3e974n,
|
||||
0x100c514bf38f6ff10df1c83bb428397789cfff7bb0b1280f52343861e8c8737en,
|
||||
0x2d40ce8af8a252f5611701c3d6b1e517161d0549ef27f443570c81fcdfe3706bn,
|
||||
0x3e6418bdf0313f59afc5f40b4450e56881110ea9a0532e8092efb06a12a8b0f1n,
|
||||
],
|
||||
[
|
||||
0x71788bf7f6c0cebae5627c5629d012d5fba52428d1f25cdaa0a7434e70e014d0n,
|
||||
0x55cc73296f7e7d26d10b9339721d7983ca06145675255025ab00b34342557db7n,
|
||||
0x0f043b29be2def73a6c6ec92168ea4b47bc9f434a5e6b5d48677670a7ca4d285n,
|
||||
0x62ccc9cdfed859a610f103d74ea04dec0f6874a9b36f3b4e9b47fd73368d45b4n,
|
||||
0x55fb349dd6200b34eaba53a67e74f47d08e473da139dc47e44df50a26423d2d1n,
|
||||
],
|
||||
[
|
||||
0x45bfbe5ed2f4a01c13b15f20bba00ff577b1154a81b3f318a6aff86369a66735n,
|
||||
0x6a008906685587af05dce9ad2c65ea1d42b1ec32609597bd00c01f58443329efn,
|
||||
0x004feebd0dbdb9b71176a1d43c9eb495e16419382cdf7864e4bce7b37440cd58n,
|
||||
0x09f080180ce23a5aef3a07e60b28ffeb2cf1771aefbc565c2a3059b39ed82f43n,
|
||||
0x2f7126ddc54648ab6d02493dbe9907f29f4ef3967ad8cd609f0d9467e1694607n,
|
||||
],
|
||||
];
|
||||
|
||||
const roundConstants = poseidon.splitConstants(st2.map(BigInt), t);
|
||||
|
||||
const poseidon_x5_255_5 = poseidon.poseidon({
|
||||
Fp,
|
||||
t,
|
||||
roundsFull: 8,
|
||||
roundsPartial: 60,
|
||||
mds,
|
||||
roundConstants,
|
||||
});
|
||||
|
||||
deepStrictEqual(
|
||||
poseidon_x5_255_5([
|
||||
0x0000000000000000000000000000000000000000000000000000000000000000n,
|
||||
0x0000000000000000000000000000000000000000000000000000000000000001n,
|
||||
0x0000000000000000000000000000000000000000000000000000000000000002n,
|
||||
0x0000000000000000000000000000000000000000000000000000000000000003n,
|
||||
0x0000000000000000000000000000000000000000000000000000000000000004n,
|
||||
]),
|
||||
[
|
||||
0x2a918b9c9f9bd7bb509331c81e297b5707f6fc7393dcee1b13901a0b22202e18n,
|
||||
0x65ebf8671739eeb11fb217f2d5c5bf4a0c3f210e3f3cd3b08b5db75675d797f7n,
|
||||
0x2cc176fc26bc70737a696a9dfd1b636ce360ee76926d182390cdb7459cf585cen,
|
||||
0x4dc4e29d283afd2a491fe6aef122b9a968e74eff05341f3cc23fda1781dcb566n,
|
||||
0x03ff622da276830b9451b88b85e6184fd6ae15c8ab3ee25a5667be8592cce3b1n,
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
should('poseidonperm_x5_254_3', () => {
|
||||
const Fp = mod.Fp(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001n);
|
||||
const t = 3;
|
||||
|
||||
const mds = [
|
||||
[
|
||||
0x109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118bn,
|
||||
0x16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e0n,
|
||||
0x2b90bba00fca0589f617e7dcbfe82e0df706ab640ceb247b791a93b74e36736dn,
|
||||
],
|
||||
[
|
||||
0x2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771n,
|
||||
0x2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe23n,
|
||||
0x101071f0032379b697315876690f053d148d4e109f5fb065c8aacc55a0f89bfan,
|
||||
],
|
||||
[
|
||||
0x143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7n,
|
||||
0x176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee2911n,
|
||||
0x19a3fc0a56702bf417ba7fee3802593fa644470307043f7773279cd71d25d5e0n,
|
||||
],
|
||||
];
|
||||
|
||||
const roundConstants = poseidon.splitConstants(st3.map(BigInt), t);
|
||||
|
||||
const poseidon_x5_254_3 = poseidon.poseidon({
|
||||
Fp,
|
||||
t,
|
||||
roundsFull: 8,
|
||||
roundsPartial: 57,
|
||||
mds,
|
||||
roundConstants,
|
||||
});
|
||||
|
||||
deepStrictEqual(
|
||||
poseidon_x5_254_3([
|
||||
0x0000000000000000000000000000000000000000000000000000000000000000n,
|
||||
0x0000000000000000000000000000000000000000000000000000000000000001n,
|
||||
0x0000000000000000000000000000000000000000000000000000000000000002n,
|
||||
]),
|
||||
[
|
||||
0x115cc0f5e7d690413df64c6b9662e9cf2a3617f2743245519e19607a4417189an,
|
||||
0x0fca49b798923ab0239de1c9e7a4a9a2210312b6a2f616d18b5a87f9b628ae29n,
|
||||
0x0e7ae82e40091e63cbd4f16a6d16310b3729d4b6e138fcf54110e2867045a30cn,
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
should('poseidonperm_x5_254_5', () => {
|
||||
const Fp = mod.Fp(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001n);
|
||||
const t = 5;
|
||||
|
||||
const mds = [
|
||||
[
|
||||
0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1n,
|
||||
0x25fb50b65acf4fb047cbd3b1c17d97c7fe26ea9ca238d6e348550486e91c7765n,
|
||||
0x293d617d7da72102355f39ebf62f91b06deb5325f367a4556ea1e31ed5767833n,
|
||||
0x104d0295ab00c85e960111ac25da474366599e575a9b7edf6145f14ba6d3c1c4n,
|
||||
0x0aaa35e2c84baf117dea3e336cd96a39792b3813954fe9bf3ed5b90f2f69c977n,
|
||||
],
|
||||
[
|
||||
0x2a70b9f1d4bbccdbc03e17c1d1dcdb02052903dc6609ea6969f661b2eb74c839n,
|
||||
0x281154651c921e746315a9934f1b8a1bba9f92ad8ef4b979115b8e2e991ccd7an,
|
||||
0x28c2be2f8264f95f0b53c732134efa338ccd8fdb9ee2b45fb86a894f7db36c37n,
|
||||
0x21888041e6febd546d427c890b1883bb9b626d8cb4dc18dcc4ec8fa75e530a13n,
|
||||
0x14ddb5fada0171db80195b9592d8cf2be810930e3ea4574a350d65e2cbff4941n,
|
||||
],
|
||||
[
|
||||
0x2f69a7198e1fbcc7dea43265306a37ed55b91bff652ad69aa4fa8478970d401dn,
|
||||
0x001c1edd62645b73ad931ab80e37bbb267ba312b34140e716d6a3747594d3052n,
|
||||
0x15b98ce93e47bc64ce2f2c96c69663c439c40c603049466fa7f9a4b228bfc32bn,
|
||||
0x12c7e2adfa524e5958f65be2fbac809fcba8458b28e44d9265051de33163cf9cn,
|
||||
0x2efc2b90d688134849018222e7b8922eaf67ce79816ef468531ec2de53bbd167n,
|
||||
],
|
||||
[
|
||||
0x0c3f050a6bf5af151981e55e3e1a29a13c3ffa4550bd2514f1afd6c5f721f830n,
|
||||
0x0dec54e6dbf75205fa75ba7992bd34f08b2efe2ecd424a73eda7784320a1a36en,
|
||||
0x1c482a25a729f5df20225815034b196098364a11f4d988fb7cc75cf32d8136fan,
|
||||
0x2625ce48a7b39a4252732624e4ab94360812ac2fc9a14a5fb8b607ae9fd8514an,
|
||||
0x07f017a7ebd56dd086f7cd4fd710c509ed7ef8e300b9a8bb9fb9f28af710251fn,
|
||||
],
|
||||
[
|
||||
0x2a20e3a4a0e57d92f97c9d6186c6c3ea7c5e55c20146259be2f78c2ccc2e3595n,
|
||||
0x1049f8210566b51faafb1e9a5d63c0ee701673aed820d9c4403b01feb727a549n,
|
||||
0x02ecac687ef5b4b568002bd9d1b96b4bef357a69e3e86b5561b9299b82d69c8en,
|
||||
0x2d3a1aea2e6d44466808f88c9ba903d3bdcb6b58ba40441ed4ebcf11bbe1e37bn,
|
||||
0x14074bb14c982c81c9ad171e4f35fe49b39c4a7a72dbb6d9c98d803bfed65e64n,
|
||||
],
|
||||
];
|
||||
|
||||
const roundConstants = poseidon.splitConstants(st4.map(BigInt), t);
|
||||
|
||||
const poseidon_x5_254_5 = poseidon.poseidon({
|
||||
Fp,
|
||||
t,
|
||||
roundsFull: 8,
|
||||
roundsPartial: 60,
|
||||
mds,
|
||||
roundConstants,
|
||||
});
|
||||
|
||||
deepStrictEqual(
|
||||
poseidon_x5_254_5([
|
||||
0x0000000000000000000000000000000000000000000000000000000000000000n,
|
||||
0x0000000000000000000000000000000000000000000000000000000000000001n,
|
||||
0x0000000000000000000000000000000000000000000000000000000000000002n,
|
||||
0x0000000000000000000000000000000000000000000000000000000000000003n,
|
||||
0x0000000000000000000000000000000000000000000000000000000000000004n,
|
||||
]),
|
||||
[
|
||||
0x299c867db6c1fdd79dcefa40e4510b9837e60ebb1ce0663dbaa525df65250465n,
|
||||
0x1148aaef609aa338b27dafd89bb98862d8bb2b429aceac47d86206154ffe053dn,
|
||||
0x24febb87fed7462e23f6665ff9a0111f4044c38ee1672c1ac6b0637d34f24907n,
|
||||
0x0eb08f6d809668a981c186beaf6110060707059576406b248e5d9cf6e78b3d3en,
|
||||
0x07748bc6877c9b82c8b98666ee9d0626ec7f5be4205f79ee8528ef1c4a376fc7n,
|
||||
]
|
||||
);
|
||||
});
|
||||
// Startadperm is unsupported, since it is non prime field
|
||||
|
||||
// ESM is broken.
|
||||
import url from 'url';
|
||||
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
|
||||
should.run();
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,198 +1,199 @@
|
||||
import { deepStrictEqual, throws } from 'assert';
|
||||
import { should } from 'micro-should';
|
||||
import { describe, should } from 'micro-should';
|
||||
import * as starknet from '../../lib/esm/stark.js';
|
||||
import { default as issue2 } from './fixtures/issue2.json' assert { type: 'json' };
|
||||
|
||||
should('Basic elliptic sanity check', () => {
|
||||
const g1 = starknet.Point.BASE;
|
||||
deepStrictEqual(
|
||||
g1.x.toString(16),
|
||||
'1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca'
|
||||
);
|
||||
deepStrictEqual(
|
||||
g1.y.toString(16),
|
||||
'5668060aa49730b7be4801df46ec62de53ecd11abe43a32873000c36e8dc1f'
|
||||
);
|
||||
const g2 = g1.double();
|
||||
deepStrictEqual(
|
||||
g2.x.toString(16),
|
||||
'759ca09377679ecd535a81e83039658bf40959283187c654c5416f439403cf5'
|
||||
);
|
||||
deepStrictEqual(
|
||||
g2.y.toString(16),
|
||||
'6f524a3400e7708d5c01a28598ad272e7455aa88778b19f93b562d7a9646c41'
|
||||
);
|
||||
const g3 = g2.add(g1);
|
||||
deepStrictEqual(
|
||||
g3.x.toString(16),
|
||||
'411494b501a98abd8262b0da1351e17899a0c4ef23dd2f96fec5ba847310b20'
|
||||
);
|
||||
deepStrictEqual(
|
||||
g3.y.toString(16),
|
||||
'7e1b3ebac08924d2c26f409549191fcf94f3bf6f301ed3553e22dfb802f0686'
|
||||
);
|
||||
const g32 = g1.multiply(3);
|
||||
deepStrictEqual(
|
||||
g32.x.toString(16),
|
||||
'411494b501a98abd8262b0da1351e17899a0c4ef23dd2f96fec5ba847310b20'
|
||||
);
|
||||
deepStrictEqual(
|
||||
g32.y.toString(16),
|
||||
'7e1b3ebac08924d2c26f409549191fcf94f3bf6f301ed3553e22dfb802f0686'
|
||||
);
|
||||
const minus1 = g1.multiply(starknet.CURVE.n - 1n);
|
||||
deepStrictEqual(
|
||||
minus1.x.toString(16),
|
||||
'1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca'
|
||||
);
|
||||
deepStrictEqual(
|
||||
minus1.y.toString(16),
|
||||
'7a997f9f55b68e04841b7fe20b9139d21ac132ee541bc5cd78cfff3c91723e2'
|
||||
);
|
||||
});
|
||||
|
||||
should('Pedersen', () => {
|
||||
deepStrictEqual(
|
||||
starknet.pedersen(2, 3),
|
||||
'0x5774fa77b3d843ae9167abd61cf80365a9b2b02218fc2f628494b5bdc9b33b8'
|
||||
);
|
||||
deepStrictEqual(
|
||||
starknet.pedersen(1, 2),
|
||||
'0x5bb9440e27889a364bcb678b1f679ecd1347acdedcbf36e83494f857cc58026'
|
||||
);
|
||||
deepStrictEqual(
|
||||
starknet.pedersen(3, 4),
|
||||
'0x262697b88544f733e5c6907c3e1763131e9f14c51ee7951258abbfb29415fbf'
|
||||
);
|
||||
});
|
||||
|
||||
should('Hash chain', () => {
|
||||
deepStrictEqual(
|
||||
starknet.hashChain([1, 2, 3]),
|
||||
'0x5d9d62d4040b977c3f8d2389d494e4e89a96a8b45c44b1368f1cc6ec5418915'
|
||||
);
|
||||
});
|
||||
|
||||
should('Pedersen hash edgecases', () => {
|
||||
// >>> pedersen_hash(0,0)
|
||||
const zero = '0x49ee3eba8c1600700ee1b87eb599f16716b0b1022947733551fde4050ca6804';
|
||||
deepStrictEqual(starknet.pedersen(0, 0), zero);
|
||||
deepStrictEqual(starknet.pedersen(0n, 0n), zero);
|
||||
deepStrictEqual(starknet.pedersen('0', '0'), zero);
|
||||
deepStrictEqual(starknet.pedersen('0x0', '0x0'), zero);
|
||||
// >>> pedersen_hash(3618502788666131213697322783095070105623107215331596699973092056135872020475,3618502788666131213697322783095070105623107215331596699973092056135872020475)
|
||||
// 3226051580231087455100099637526672350308978851161639703631919449959447036451
|
||||
const big = 3618502788666131213697322783095070105623107215331596699973092056135872020475n;
|
||||
const bigExp = '0x721e167a36655994e88efa865e2ed8a0488d36db4d988fec043cda755728223';
|
||||
deepStrictEqual(starknet.pedersen(big, big), bigExp);
|
||||
// >= FIELD
|
||||
const big2 = 36185027886661312136973227830950701056231072153315966999730920561358720204751n;
|
||||
throws(() => starknet.pedersen(big2, big2), 'big2');
|
||||
|
||||
// FIELD -1
|
||||
const big3 = 3618502788666131213697322783095070105623107215331596699973092056135872020480n;
|
||||
const big3exp = '0x7258fccaf3371fad51b117471d9d888a1786c5694c3e6099160477b593a576e';
|
||||
deepStrictEqual(starknet.pedersen(big3, big3), big3exp, 'big3');
|
||||
// FIELD
|
||||
const big4 = 3618502788666131213697322783095070105623107215331596699973092056135872020481n;
|
||||
throws(() => starknet.pedersen(big4, big4), 'big4');
|
||||
throws(() => starknet.pedersen(-1, -1), 'neg');
|
||||
throws(() => starknet.pedersen(false, false), 'false');
|
||||
throws(() => starknet.pedersen(true, true), 'true');
|
||||
throws(() => starknet.pedersen(10.1, 10.1), 'float');
|
||||
});
|
||||
|
||||
should('hashChain edgecases', () => {
|
||||
deepStrictEqual(starknet.hashChain([32312321312321312312312321n]), '0x1aba6672c014b4838cc201');
|
||||
deepStrictEqual(
|
||||
starknet.hashChain([1n, 2n]),
|
||||
'0x5bb9440e27889a364bcb678b1f679ecd1347acdedcbf36e83494f857cc58026'
|
||||
);
|
||||
deepStrictEqual(
|
||||
starknet.hashChain([1, 2]),
|
||||
'0x5bb9440e27889a364bcb678b1f679ecd1347acdedcbf36e83494f857cc58026'
|
||||
);
|
||||
throws(() => starknet.hashChain([]));
|
||||
throws(() => starknet.hashChain('123'));
|
||||
deepStrictEqual(
|
||||
starknet.hashChain([1, 2]),
|
||||
'0x5bb9440e27889a364bcb678b1f679ecd1347acdedcbf36e83494f857cc58026'
|
||||
);
|
||||
});
|
||||
|
||||
should('Pedersen hash, issue #2', () => {
|
||||
// Verified with starnet.js
|
||||
deepStrictEqual(
|
||||
starknet.computeHashOnElements(issue2),
|
||||
'0x22064462ea33a6ce5272a295e0f551c5da3834f80d8444e7a4df68190b1bc42'
|
||||
);
|
||||
deepStrictEqual(
|
||||
starknet.computeHashOnElements([]),
|
||||
'0x49ee3eba8c1600700ee1b87eb599f16716b0b1022947733551fde4050ca6804'
|
||||
);
|
||||
deepStrictEqual(
|
||||
starknet.computeHashOnElements([1]),
|
||||
'0x78d74f61aeaa8286418fd34b3a12a610445eba11d00ecc82ecac2542d55f7a4'
|
||||
);
|
||||
});
|
||||
|
||||
import * as bip32 from '@scure/bip32';
|
||||
import * as bip39 from '@scure/bip39';
|
||||
|
||||
should('Seed derivation (example)', () => {
|
||||
const layer = 'starkex';
|
||||
const application = 'starkdeployement';
|
||||
const mnemonic =
|
||||
'range mountain blast problem vibrant void vivid doctor cluster enough melody ' +
|
||||
'salt layer language laptop boat major space monkey unit glimpse pause change vibrant';
|
||||
const ethAddress = '0xa4864d977b944315389d1765ffa7e66F74ee8cd7';
|
||||
const hdKey = bip32.HDKey.fromMasterSeed(bip39.mnemonicToSeedSync(mnemonic)).derive(
|
||||
starknet.getAccountPath(layer, application, ethAddress, 0)
|
||||
);
|
||||
deepStrictEqual(
|
||||
starknet.grindKey(hdKey.privateKey),
|
||||
'6cf0a8bf113352eb863157a45c5e5567abb34f8d32cddafd2c22aa803f4892c'
|
||||
);
|
||||
});
|
||||
describe('starknet basic', () => {
|
||||
should('Basic elliptic sanity check', () => {
|
||||
const g1 = starknet.ProjectivePoint.BASE;
|
||||
deepStrictEqual(
|
||||
g1.toAffine().x.toString(16),
|
||||
'1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca'
|
||||
);
|
||||
deepStrictEqual(
|
||||
g1.toAffine().y.toString(16),
|
||||
'5668060aa49730b7be4801df46ec62de53ecd11abe43a32873000c36e8dc1f'
|
||||
);
|
||||
const g2 = g1.double();
|
||||
deepStrictEqual(
|
||||
g2.toAffine().x.toString(16),
|
||||
'759ca09377679ecd535a81e83039658bf40959283187c654c5416f439403cf5'
|
||||
);
|
||||
deepStrictEqual(
|
||||
g2.toAffine().y.toString(16),
|
||||
'6f524a3400e7708d5c01a28598ad272e7455aa88778b19f93b562d7a9646c41'
|
||||
);
|
||||
const g3 = g2.add(g1);
|
||||
deepStrictEqual(
|
||||
g3.toAffine().x.toString(16),
|
||||
'411494b501a98abd8262b0da1351e17899a0c4ef23dd2f96fec5ba847310b20'
|
||||
);
|
||||
deepStrictEqual(
|
||||
g3.toAffine().y.toString(16),
|
||||
'7e1b3ebac08924d2c26f409549191fcf94f3bf6f301ed3553e22dfb802f0686'
|
||||
);
|
||||
const g32 = g1.multiply(3n);
|
||||
deepStrictEqual(
|
||||
g32.toAffine().x.toString(16),
|
||||
'411494b501a98abd8262b0da1351e17899a0c4ef23dd2f96fec5ba847310b20'
|
||||
);
|
||||
deepStrictEqual(
|
||||
g32.toAffine().y.toString(16),
|
||||
'7e1b3ebac08924d2c26f409549191fcf94f3bf6f301ed3553e22dfb802f0686'
|
||||
);
|
||||
const minus1 = g1.multiply(starknet.CURVE.n - 1n);
|
||||
deepStrictEqual(
|
||||
minus1.toAffine().x.toString(16),
|
||||
'1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca'
|
||||
);
|
||||
deepStrictEqual(
|
||||
minus1.toAffine().y.toString(16),
|
||||
'7a997f9f55b68e04841b7fe20b9139d21ac132ee541bc5cd78cfff3c91723e2'
|
||||
);
|
||||
});
|
||||
|
||||
should('Compressed keys', () => {
|
||||
const G = starknet.Point.BASE;
|
||||
const half = starknet.CURVE.n / 2n;
|
||||
const last = starknet.CURVE.n;
|
||||
const vectors = [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
half - 5n,
|
||||
half - 4n,
|
||||
half - 3n,
|
||||
half - 2n,
|
||||
half - 1n,
|
||||
half,
|
||||
half + 1n,
|
||||
half + 2n,
|
||||
half + 3n,
|
||||
half + 4n,
|
||||
half + 5n,
|
||||
last - 5n,
|
||||
last - 4n,
|
||||
last - 3n,
|
||||
last - 2n,
|
||||
last - 1n,
|
||||
].map((i) => G.multiply(i));
|
||||
const fixPoint = (pt) => ({ ...pt, _WINDOW_SIZE: undefined });
|
||||
for (const v of vectors) {
|
||||
const uncompressed = v.toHex();
|
||||
const compressed = v.toHex(true);
|
||||
const exp = fixPoint(v);
|
||||
deepStrictEqual(fixPoint(starknet.Point.fromHex(uncompressed)), exp);
|
||||
deepStrictEqual(fixPoint(starknet.Point.fromHex(compressed)), exp);
|
||||
deepStrictEqual(starknet.Point.fromHex(compressed).toHex(), uncompressed);
|
||||
}
|
||||
});
|
||||
should('Pedersen', () => {
|
||||
deepStrictEqual(
|
||||
starknet.pedersen(2, 3),
|
||||
'0x5774fa77b3d843ae9167abd61cf80365a9b2b02218fc2f628494b5bdc9b33b8'
|
||||
);
|
||||
deepStrictEqual(
|
||||
starknet.pedersen(1, 2),
|
||||
'0x5bb9440e27889a364bcb678b1f679ecd1347acdedcbf36e83494f857cc58026'
|
||||
);
|
||||
deepStrictEqual(
|
||||
starknet.pedersen(3, 4),
|
||||
'0x262697b88544f733e5c6907c3e1763131e9f14c51ee7951258abbfb29415fbf'
|
||||
);
|
||||
});
|
||||
|
||||
should('Hash chain', () => {
|
||||
deepStrictEqual(
|
||||
starknet.hashChain([1, 2, 3]),
|
||||
'0x5d9d62d4040b977c3f8d2389d494e4e89a96a8b45c44b1368f1cc6ec5418915'
|
||||
);
|
||||
});
|
||||
|
||||
should('Pedersen hash edgecases', () => {
|
||||
// >>> pedersen_hash(0,0)
|
||||
const zero = '0x49ee3eba8c1600700ee1b87eb599f16716b0b1022947733551fde4050ca6804';
|
||||
deepStrictEqual(starknet.pedersen(0, 0), zero);
|
||||
deepStrictEqual(starknet.pedersen(0n, 0n), zero);
|
||||
deepStrictEqual(starknet.pedersen('0', '0'), zero);
|
||||
deepStrictEqual(starknet.pedersen('0x0', '0x0'), zero);
|
||||
// >>> pedersen_hash(3618502788666131213697322783095070105623107215331596699973092056135872020475,3618502788666131213697322783095070105623107215331596699973092056135872020475)
|
||||
// 3226051580231087455100099637526672350308978851161639703631919449959447036451
|
||||
const big = 3618502788666131213697322783095070105623107215331596699973092056135872020475n;
|
||||
const bigExp = '0x721e167a36655994e88efa865e2ed8a0488d36db4d988fec043cda755728223';
|
||||
deepStrictEqual(starknet.pedersen(big, big), bigExp);
|
||||
// >= FIELD
|
||||
const big2 = 36185027886661312136973227830950701056231072153315966999730920561358720204751n;
|
||||
throws(() => starknet.pedersen(big2, big2), 'big2');
|
||||
|
||||
// FIELD -1
|
||||
const big3 = 3618502788666131213697322783095070105623107215331596699973092056135872020480n;
|
||||
const big3exp = '0x7258fccaf3371fad51b117471d9d888a1786c5694c3e6099160477b593a576e';
|
||||
deepStrictEqual(starknet.pedersen(big3, big3), big3exp, 'big3');
|
||||
// FIELD
|
||||
const big4 = 3618502788666131213697322783095070105623107215331596699973092056135872020481n;
|
||||
throws(() => starknet.pedersen(big4, big4), 'big4');
|
||||
throws(() => starknet.pedersen(-1, -1), 'neg');
|
||||
throws(() => starknet.pedersen(false, false), 'false');
|
||||
throws(() => starknet.pedersen(true, true), 'true');
|
||||
throws(() => starknet.pedersen(10.1, 10.1), 'float');
|
||||
});
|
||||
|
||||
should('hashChain edgecases', () => {
|
||||
deepStrictEqual(starknet.hashChain([32312321312321312312312321n]), '0x1aba6672c014b4838cc201');
|
||||
deepStrictEqual(
|
||||
starknet.hashChain([1n, 2n]),
|
||||
'0x5bb9440e27889a364bcb678b1f679ecd1347acdedcbf36e83494f857cc58026'
|
||||
);
|
||||
deepStrictEqual(
|
||||
starknet.hashChain([1, 2]),
|
||||
'0x5bb9440e27889a364bcb678b1f679ecd1347acdedcbf36e83494f857cc58026'
|
||||
);
|
||||
throws(() => starknet.hashChain([]));
|
||||
throws(() => starknet.hashChain('123'));
|
||||
deepStrictEqual(
|
||||
starknet.hashChain([1, 2]),
|
||||
'0x5bb9440e27889a364bcb678b1f679ecd1347acdedcbf36e83494f857cc58026'
|
||||
);
|
||||
});
|
||||
|
||||
should('Pedersen hash, issue #2', () => {
|
||||
// Verified with starnet.js
|
||||
deepStrictEqual(
|
||||
starknet.computeHashOnElements(issue2),
|
||||
'0x22064462ea33a6ce5272a295e0f551c5da3834f80d8444e7a4df68190b1bc42'
|
||||
);
|
||||
deepStrictEqual(
|
||||
starknet.computeHashOnElements([]),
|
||||
'0x49ee3eba8c1600700ee1b87eb599f16716b0b1022947733551fde4050ca6804'
|
||||
);
|
||||
deepStrictEqual(
|
||||
starknet.computeHashOnElements([1]),
|
||||
'0x78d74f61aeaa8286418fd34b3a12a610445eba11d00ecc82ecac2542d55f7a4'
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
should('Seed derivation (example)', () => {
|
||||
const layer = 'starkex';
|
||||
const application = 'starkdeployement';
|
||||
const mnemonic =
|
||||
'range mountain blast problem vibrant void vivid doctor cluster enough melody ' +
|
||||
'salt layer language laptop boat major space monkey unit glimpse pause change vibrant';
|
||||
const ethAddress = '0xa4864d977b944315389d1765ffa7e66F74ee8cd7';
|
||||
const hdKey = bip32.HDKey.fromMasterSeed(bip39.mnemonicToSeedSync(mnemonic)).derive(
|
||||
starknet.getAccountPath(layer, application, ethAddress, 0)
|
||||
);
|
||||
deepStrictEqual(
|
||||
starknet.grindKey(hdKey.privateKey),
|
||||
'6cf0a8bf113352eb863157a45c5e5567abb34f8d32cddafd2c22aa803f4892c'
|
||||
);
|
||||
});
|
||||
|
||||
should('Compressed keys', () => {
|
||||
const G = starknet.ProjectivePoint.BASE;
|
||||
const half = starknet.CURVE.n / 2n;
|
||||
const last = starknet.CURVE.n;
|
||||
const vectors = [
|
||||
1n,
|
||||
2n,
|
||||
3n,
|
||||
4n,
|
||||
5n,
|
||||
half - 5n,
|
||||
half - 4n,
|
||||
half - 3n,
|
||||
half - 2n,
|
||||
half - 1n,
|
||||
half,
|
||||
half + 1n,
|
||||
half + 2n,
|
||||
half + 3n,
|
||||
half + 4n,
|
||||
half + 5n,
|
||||
last - 5n,
|
||||
last - 4n,
|
||||
last - 3n,
|
||||
last - 2n,
|
||||
last - 1n,
|
||||
].map((i) => G.multiply(i));
|
||||
const fixPoint = (pt) => pt.toAffine();
|
||||
for (const v of vectors) {
|
||||
const uncompressed = v.toHex();
|
||||
const compressed = v.toHex(true);
|
||||
const exp = fixPoint(v);
|
||||
deepStrictEqual(fixPoint(starknet.ProjectivePoint.fromHex(uncompressed)), exp);
|
||||
deepStrictEqual(fixPoint(starknet.ProjectivePoint.fromHex(compressed)), exp);
|
||||
deepStrictEqual(starknet.ProjectivePoint.fromHex(compressed).toHex(), uncompressed);
|
||||
}
|
||||
});
|
||||
});
|
||||
// ESM is broken.
|
||||
import url from 'url';
|
||||
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
|
||||
import './basic.test.js';
|
||||
import './stark.test.js';
|
||||
import './property.test.js';
|
||||
|
||||
import './poseidon.test.js';
|
||||
|
||||
114
test/stark/poseidon.test.js
Normal file
114
test/stark/poseidon.test.js
Normal file
@@ -0,0 +1,114 @@
|
||||
import { deepStrictEqual, throws } from 'assert';
|
||||
import { describe, should } from 'micro-should';
|
||||
import * as starknet from '../../lib/esm/stark.js';
|
||||
import * as fs from 'fs';
|
||||
|
||||
function parseTest(path) {
|
||||
let data = fs.readFileSync(path, 'ascii');
|
||||
// Remove whitespaces
|
||||
data = data.replace(/[ |\t]/g, '');
|
||||
const pattern =
|
||||
'Rate=(\\d+)\n' +
|
||||
'Capacity=(\\d+)\n' +
|
||||
'FullRounds=(\\d+)\n' +
|
||||
'PartialRounds=(\\d+)\n' +
|
||||
'MDS=\\[(.+)\\]\n' +
|
||||
'RoundKeys=\\(?\n?\\[\n?(.+)\n?\\]\n?\\)?';
|
||||
const r = data.match(new RegExp(pattern, 'ms'));
|
||||
|
||||
function parseArray(s) {
|
||||
// Remove new lines
|
||||
s = s.replace(/\n/gms, '');
|
||||
return s.match(/(\[.+?\])/g).map((i) =>
|
||||
i
|
||||
.replace(/^\[(.+)\]$/, '$1')
|
||||
.split(',')
|
||||
.filter((i) => !!i)
|
||||
);
|
||||
}
|
||||
const res = {
|
||||
rate: +r[1],
|
||||
capacity: +r[2],
|
||||
roundsFull: +r[3],
|
||||
roundsPartial: +r[4],
|
||||
MDS: parseArray(r[5]).map((i) => i.map((j) => BigInt(j))),
|
||||
roundConstants: parseArray(r[6]).map((i) => i.map((j) => BigInt(j))),
|
||||
};
|
||||
return res;
|
||||
}
|
||||
|
||||
function mapPoseidon(parsed) {
|
||||
return starknet.poseidonBasic(
|
||||
{
|
||||
Fp: starknet.Fp251,
|
||||
rate: parsed.rate,
|
||||
capacity: parsed.capacity,
|
||||
roundsFull: parsed.roundsFull,
|
||||
roundsPartial: parsed.roundsPartial,
|
||||
},
|
||||
parsed.MDS
|
||||
);
|
||||
}
|
||||
|
||||
const parsed = {
|
||||
poseidon3: parseTest('./test/stark/poseidon/poseidon3.txt'),
|
||||
poseidon4: parseTest('./test/stark/poseidon/poseidon4.txt'),
|
||||
poseidon5: parseTest('./test/stark/poseidon/poseidon5.txt'),
|
||||
poseidon9: parseTest('./test/stark/poseidon/poseidon9.txt'),
|
||||
};
|
||||
|
||||
function poseidonTest(name, parsed) {
|
||||
should(`${name}`, () => {
|
||||
const fn = mapPoseidon(parsed);
|
||||
deepStrictEqual(fn.roundConstants, parsed.roundConstants);
|
||||
});
|
||||
}
|
||||
|
||||
describe('poseidon txt vectors', () => {
|
||||
poseidonTest('poseidon3', parsed.poseidon3);
|
||||
poseidonTest('poseidon4', parsed.poseidon4);
|
||||
poseidonTest('poseidon5', parsed.poseidon5);
|
||||
poseidonTest('poseidon9', parsed.poseidon9);
|
||||
});
|
||||
|
||||
should('Poseidon examples', () => {
|
||||
const p3 = mapPoseidon(parsed.poseidon3);
|
||||
deepStrictEqual(p3([0n, 0n, 0n]), [
|
||||
3446325744004048536138401612021367625846492093718951375866996507163446763827n,
|
||||
1590252087433376791875644726012779423683501236913937337746052470473806035332n,
|
||||
867921192302518434283879514999422690776342565400001269945778456016268852423n,
|
||||
]);
|
||||
const p4 = mapPoseidon(parsed.poseidon4);
|
||||
deepStrictEqual(p4([0n, 0n, 0n, 0n]), [
|
||||
535071095200566880914603862188010633478042591441142518549720701573192347548n,
|
||||
3567335813488551850156302853280844225974867890860330236555401145692518003968n,
|
||||
229995103310401763929738317978722680640995513996113588430855556460153357543n,
|
||||
3513983790849716360905369754287999509206472929684378838050290392634812839312n,
|
||||
]);
|
||||
const p5 = mapPoseidon(parsed.poseidon5);
|
||||
deepStrictEqual(p5([0n, 0n, 0n, 0n, 0n]), [
|
||||
2337689130971531876049206831496963607805116499042700598724344149414565980684n,
|
||||
3230969295497815870174763682436655274044379544854667759151474216427142025631n,
|
||||
3297330512217530111610698859408044542971696143761201570393504997742535648562n,
|
||||
2585480844700786541432072704002477919020588246983274666988914431019064343941n,
|
||||
3595308260654382824623573767385493361624474708214823462901432822513585995028n,
|
||||
]);
|
||||
const p9 = mapPoseidon(parsed.poseidon9);
|
||||
deepStrictEqual(p9([0n, 0n, 0n, 0n, 0n, 0n, 0n, 0n, 0n]), [
|
||||
1534116856660032929112709488204491699743182428465681149262739677337223235050n,
|
||||
1710856073207389764546990138116985223517553616229641666885337928044617114700n,
|
||||
3165864635055638516987240200217592641540231237468651257819894959934472989427n,
|
||||
1003007637710164252047715558598366312649052908276423203724288341354608811559n,
|
||||
68117303579957054409211824649914588822081700129416361923518488718489651489n,
|
||||
1123395637839379807713801282868237406546107732595903195840754789810160564711n,
|
||||
478590974834311070537087181212389392308746075734019180430422247431982932503n,
|
||||
835322726024358888065061514739954009068852229059154336727219387089732433787n,
|
||||
3129703030204995742174502162918848446737407262178341733578946634564864233056n,
|
||||
]);
|
||||
});
|
||||
|
||||
// ESM is broken.
|
||||
import url from 'url';
|
||||
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
|
||||
should.run();
|
||||
}
|
||||
201
test/stark/poseidon/LICENSE
Normal file
201
test/stark/poseidon/LICENSE
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
35
test/stark/poseidon/README.md
Normal file
35
test/stark/poseidon/README.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# StarkWare's Poseidon Hash
|
||||
|
||||
[Poseidon](https://www.poseidon-hash.info/) is a family of hash functions designed for being very efficient as algebraic circuits.
|
||||
As such, they may be very useful in ZK proving systems such as STARKs and others.
|
||||
|
||||
This repository provides the official parameters of StarkWare's Poseidon hash implementations.
|
||||
|
||||
All the instances are over the prime field:
|
||||
p = 2^251 + 17 * 2^192 + 1 = 3618502788666131213697322783095070105623107215331596699973092056135872020481
|
||||
|
||||
A few examples hash results with the different parameters:
|
||||
|
||||
```
|
||||
Poseidon3([0,0,0]) = [3446325744004048536138401612021367625846492093718951375866996507163446763827,
|
||||
1590252087433376791875644726012779423683501236913937337746052470473806035332,
|
||||
867921192302518434283879514999422690776342565400001269945778456016268852423]
|
||||
Poseidon4([0,0,0,0]) = [535071095200566880914603862188010633478042591441142518549720701573192347548,
|
||||
3567335813488551850156302853280844225974867890860330236555401145692518003968,
|
||||
229995103310401763929738317978722680640995513996113588430855556460153357543,
|
||||
3513983790849716360905369754287999509206472929684378838050290392634812839312]
|
||||
Poseidon5([0,0,0,0,0]) = [2337689130971531876049206831496963607805116499042700598724344149414565980684,
|
||||
3230969295497815870174763682436655274044379544854667759151474216427142025631,
|
||||
3297330512217530111610698859408044542971696143761201570393504997742535648562,
|
||||
2585480844700786541432072704002477919020588246983274666988914431019064343941,
|
||||
3595308260654382824623573767385493361624474708214823462901432822513585995028]
|
||||
Poseidon9([0,0,0,0,0,0,0,0,0]) = [1534116856660032929112709488204491699743182428465681149262739677337223235050,
|
||||
1710856073207389764546990138116985223517553616229641666885337928044617114700,
|
||||
3165864635055638516987240200217592641540231237468651257819894959934472989427,
|
||||
1003007637710164252047715558598366312649052908276423203724288341354608811559,
|
||||
68117303579957054409211824649914588822081700129416361923518488718489651489,
|
||||
1123395637839379807713801282868237406546107732595903195840754789810160564711,
|
||||
478590974834311070537087181212389392308746075734019180430422247431982932503,
|
||||
835322726024358888065061514739954009068852229059154336727219387089732433787,
|
||||
3129703030204995742174502162918848446737407262178341733578946634564864233056]
|
||||
```
|
||||
462
test/stark/poseidon/poseidon3.txt
Normal file
462
test/stark/poseidon/poseidon3.txt
Normal file
@@ -0,0 +1,462 @@
|
||||
Rate = 2
|
||||
Capacity = 1
|
||||
FullRounds = 8
|
||||
PartialRounds = 83
|
||||
MDS = [[3, 1, 1], [1, -1, 1], [1, 1, -2]]
|
||||
RoundKeys = [
|
||||
[
|
||||
2950795762459345168613727575620414179244544320470208355568817838579231751791,
|
||||
1587446564224215276866294500450702039420286416111469274423465069420553242820,
|
||||
1645965921169490687904413452218868659025437693527479459426157555728339600137,
|
||||
],
|
||||
[
|
||||
2782373324549879794752287702905278018819686065818504085638398966973694145741,
|
||||
3409172630025222641379726933524480516420204828329395644967085131392375707302,
|
||||
2379053116496905638239090788901387719228422033660130943198035907032739387135,
|
||||
],
|
||||
[
|
||||
2570819397480941104144008784293466051718826502582588529995520356691856497111,
|
||||
3546220846133880637977653625763703334841539452343273304410918449202580719746,
|
||||
2720682389492889709700489490056111332164748138023159726590726667539759963454,
|
||||
],
|
||||
[
|
||||
1899653471897224903834726250400246354200311275092866725547887381599836519005,
|
||||
2369443697923857319844855392163763375394720104106200469525915896159690979559,
|
||||
2354174693689535854311272135513626412848402744119855553970180659094265527996,
|
||||
],
|
||||
[
|
||||
2404084503073127963385083467393598147276436640877011103379112521338973185443,
|
||||
950320777137731763811524327595514151340412860090489448295239456547370725376,
|
||||
2121140748740143694053732746913428481442990369183417228688865837805149503386,
|
||||
],
|
||||
[
|
||||
2372065044800422557577242066480215868569521938346032514014152523102053709709,
|
||||
2618497439310693947058545060953893433487994458443568169824149550389484489896,
|
||||
3518297267402065742048564133910509847197496119850246255805075095266319996916,
|
||||
],
|
||||
[
|
||||
340529752683340505065238931581518232901634742162506851191464448040657139775,
|
||||
1954876811294863748406056845662382214841467408616109501720437541211031966538,
|
||||
813813157354633930267029888722341725864333883175521358739311868164460385261,
|
||||
],
|
||||
[
|
||||
71901595776070443337150458310956362034911936706490730914901986556638720031,
|
||||
2789761472166115462625363403490399263810962093264318361008954888847594113421,
|
||||
2628791615374802560074754031104384456692791616314774034906110098358135152410,
|
||||
],
|
||||
[
|
||||
3617032588734559635167557152518265808024917503198278888820567553943986939719,
|
||||
2624012360209966117322788103333497793082705816015202046036057821340914061980,
|
||||
149101987103211771991327927827692640556911620408176100290586418839323044234,
|
||||
],
|
||||
[
|
||||
1039927963829140138166373450440320262590862908847727961488297105916489431045,
|
||||
2213946951050724449162431068646025833746639391992751674082854766704900195669,
|
||||
2792724903541814965769131737117981991997031078369482697195201969174353468597,
|
||||
],
|
||||
[
|
||||
3212031629728871219804596347439383805499808476303618848198208101593976279441,
|
||||
3343514080098703935339621028041191631325798327656683100151836206557453199613,
|
||||
614054702436541219556958850933730254992710988573177298270089989048553060199,
|
||||
],
|
||||
[
|
||||
148148081026449726283933484730968827750202042869875329032965774667206931170,
|
||||
1158283532103191908366672518396366136968613180867652172211392033571980848414,
|
||||
1032400527342371389481069504520755916075559110755235773196747439146396688513,
|
||||
],
|
||||
[
|
||||
806900704622005851310078578853499250941978435851598088619290797134710613736,
|
||||
462498083559902778091095573017508352472262817904991134671058825705968404510,
|
||||
1003580119810278869589347418043095667699674425582646347949349245557449452503,
|
||||
],
|
||||
[
|
||||
619074932220101074089137133998298830285661916867732916607601635248249357793,
|
||||
2635090520059500019661864086615522409798872905401305311748231832709078452746,
|
||||
978252636251682252755279071140187792306115352460774007308726210405257135181,
|
||||
],
|
||||
[
|
||||
1766912167973123409669091967764158892111310474906691336473559256218048677083,
|
||||
1663265127259512472182980890707014969235283233442916350121860684522654120381,
|
||||
3532407621206959585000336211742670185380751515636605428496206887841428074250,
|
||||
],
|
||||
[
|
||||
2507023127157093845256722098502856938353143387711652912931112668310034975446,
|
||||
3321152907858462102434883844787153373036767230808678981306827073335525034593,
|
||||
3039253036806065280643845548147711477270022154459620569428286684179698125661,
|
||||
],
|
||||
[
|
||||
103480338868480851881924519768416587261556021758163719199282794248762465380,
|
||||
2394049781357087698434751577708655768465803975478348134669006211289636928495,
|
||||
2660531560345476340796109810821127229446538730404600368347902087220064379579,
|
||||
],
|
||||
[
|
||||
3603166934034556203649050570865466556260359798872408576857928196141785055563,
|
||||
1553799760191949768532188139643704561532896296986025007089826672890485412324,
|
||||
2744284717053657689091306578463476341218866418732695211367062598446038965164,
|
||||
],
|
||||
[
|
||||
320745764922149897598257794663594419839885234101078803811049904310835548856,
|
||||
979382242100682161589753881721708883681034024104145498709287731138044566302,
|
||||
1860426855810549882740147175136418997351054138609396651615467358416651354991,
|
||||
],
|
||||
[
|
||||
336173081054369235994909356892506146234495707857220254489443629387613956145,
|
||||
1632470326779699229772327605759783482411227247311431865655466227711078175883,
|
||||
921958250077481394074960433988881176409497663777043304881055317463712938502,
|
||||
],
|
||||
[
|
||||
3034358982193370602048539901033542101022185309652879937418114324899281842797,
|
||||
25626282149517463867572353922222474817434101087272320606729439087234878607,
|
||||
3002662261401575565838149305485737102400501329139562227180277188790091853682,
|
||||
],
|
||||
[
|
||||
2939684373453383817196521641512509179310654199629514917426341354023324109367,
|
||||
1076484609897998179434851570277297233169621096172424141759873688902355505136,
|
||||
2575095284833160494841112025725243274091830284746697961080467506739203605049,
|
||||
],
|
||||
[
|
||||
3565075264617591783581665711620369529657840830498005563542124551465195621851,
|
||||
2197016502533303822395077038351174326125210255869204501838837289716363437993,
|
||||
331415322883530754594261416546036195982886300052707474899691116664327869405,
|
||||
],
|
||||
[
|
||||
1935011233711290003793244296594669823169522055520303479680359990463281661839,
|
||||
3495901467168087413996941216661589517270845976538454329511167073314577412322,
|
||||
954195417117133246453562983448451025087661597543338750600301835944144520375,
|
||||
],
|
||||
[
|
||||
1271840477709992894995746871435810599280944810893784031132923384456797925777,
|
||||
2565310762274337662754531859505158700827688964841878141121196528015826671847,
|
||||
3365022288251637014588279139038152521653896670895105540140002607272936852513,
|
||||
],
|
||||
[
|
||||
1660592021628965529963974299647026602622092163312666588591285654477111176051,
|
||||
970104372286014048279296575474974982288801187216974504035759997141059513421,
|
||||
2617024574317953753849168721871770134225690844968986289121504184985993971227,
|
||||
],
|
||||
[
|
||||
999899815343607746071464113462778273556695659506865124478430189024755832262,
|
||||
2228536129413411161615629030408828764980855956560026807518714080003644769896,
|
||||
2701953891198001564547196795777701119629537795442025393867364730330476403227,
|
||||
],
|
||||
[
|
||||
837078355588159388741598313782044128527494922918203556465116291436461597853,
|
||||
2121749601840466143704862369657561429793951309962582099604848281796392359214,
|
||||
771812260179247428733132708063116523892339056677915387749121983038690154755,
|
||||
],
|
||||
[
|
||||
3317336423132806446086732225036532603224267214833263122557471741829060578219,
|
||||
481570067997721834712647566896657604857788523050900222145547508314620762046,
|
||||
242195042559343964206291740270858862066153636168162642380846129622127460192,
|
||||
],
|
||||
[
|
||||
2855462178889999218204481481614105202770810647859867354506557827319138379686,
|
||||
3525521107148375040131784770413887305850308357895464453970651672160034885202,
|
||||
1320839531502392535964065058804908871811967681250362364246430459003920305799,
|
||||
],
|
||||
[
|
||||
2514191518588387125173345107242226637171897291221681115249521904869763202419,
|
||||
2798335750958827619666318316247381695117827718387653874070218127140615157902,
|
||||
2808467767967035643407948058486565877867906577474361783201337540214875566395,
|
||||
],
|
||||
[
|
||||
3551834385992706206273955480294669176699286104229279436819137165202231595747,
|
||||
1219439673853113792340300173186247996249367102884530407862469123523013083971,
|
||||
761519904537984520554247997444508040636526566551719396202550009393012691157,
|
||||
],
|
||||
[
|
||||
3355402549169351700500518865338783382387571349497391475317206324155237401353,
|
||||
199541098009731541347317515995192175813554789571447733944970283654592727138,
|
||||
192100490643078165121235261796864975568292640203635147901612231594408079071,
|
||||
],
|
||||
[
|
||||
1187019357602953326192019968809486933768550466167033084944727938441427050581,
|
||||
189525349641911362389041124808934468936759383310282010671081989585219065700,
|
||||
2831653363992091308880573627558515686245403755586311978724025292003353336665,
|
||||
],
|
||||
[
|
||||
2052859812632218952608271535089179639890275494426396974475479657192657094698,
|
||||
1670756178709659908159049531058853320846231785448204274277900022176591811072,
|
||||
3538757242013734574731807289786598937548399719866320954894004830207085723125,
|
||||
],
|
||||
[
|
||||
710549042741321081781917034337800036872214466705318638023070812391485261299,
|
||||
2345013122330545298606028187653996682275206910242635100920038943391319595180,
|
||||
3528369671971445493932880023233332035122954362711876290904323783426765912206,
|
||||
],
|
||||
[
|
||||
1167120829038120978297497195837406760848728897181138760506162680655977700764,
|
||||
3073243357129146594530765548901087443775563058893907738967898816092270628884,
|
||||
378514724418106317738164464176041649567501099164061863402473942795977719726,
|
||||
],
|
||||
[
|
||||
333391138410406330127594722511180398159664250722328578952158227406762627796,
|
||||
1727570175639917398410201375510924114487348765559913502662122372848626931905,
|
||||
968312190621809249603425066974405725769739606059422769908547372904403793174,
|
||||
],
|
||||
[
|
||||
360659316299446405855194688051178331671817370423873014757323462844775818348,
|
||||
1386580151907705298970465943238806620109618995410132218037375811184684929291,
|
||||
3604888328937389309031638299660239238400230206645344173700074923133890528967,
|
||||
],
|
||||
[
|
||||
2496185632263372962152518155651824899299616724241852816983268163379540137546,
|
||||
486538168871046887467737983064272608432052269868418721234810979756540672990,
|
||||
1558415498960552213241704009433360128041672577274390114589014204605400783336,
|
||||
],
|
||||
[
|
||||
3512058327686147326577190314835092911156317204978509183234511559551181053926,
|
||||
2235429387083113882635494090887463486491842634403047716936833563914243946191,
|
||||
1290896777143878193192832813769470418518651727840187056683408155503813799882,
|
||||
],
|
||||
[
|
||||
1143310336918357319571079551779316654556781203013096026972411429993634080835,
|
||||
3235435208525081966062419599803346573407862428113723170955762956243193422118,
|
||||
1293239921425673430660897025143433077974838969258268884994339615096356996604,
|
||||
],
|
||||
[
|
||||
236252269127612784685426260840574970698541177557674806964960352572864382971,
|
||||
1733907592497266237374827232200506798207318263912423249709509725341212026275,
|
||||
302004309771755665128395814807589350526779835595021835389022325987048089868,
|
||||
],
|
||||
[
|
||||
3018926838139221755384801385583867283206879023218491758435446265703006270945,
|
||||
39701437664873825906031098349904330565195980985885489447836580931425171297,
|
||||
908381723021746969965674308809436059628307487140174335882627549095646509778,
|
||||
],
|
||||
[
|
||||
219062858908229855064136253265968615354041842047384625689776811853821594358,
|
||||
1283129863776453589317845316917890202859466483456216900835390291449830275503,
|
||||
418512623547417594896140369190919231877873410935689672661226540908900544012,
|
||||
],
|
||||
[
|
||||
1792181590047131972851015200157890246436013346535432437041535789841136268632,
|
||||
370546432987510607338044736824316856592558876687225326692366316978098770516,
|
||||
3323437805230586112013581113386626899534419826098235300155664022709435756946,
|
||||
],
|
||||
[
|
||||
910076621742039763058481476739499965761942516177975130656340375573185415877,
|
||||
1762188042455633427137702520675816545396284185254002959309669405982213803405,
|
||||
2186362253913140345102191078329764107619534641234549431429008219905315900520,
|
||||
],
|
||||
[
|
||||
2230647725927681765419218738218528849146504088716182944327179019215826045083,
|
||||
1069243907556644434301190076451112491469636357133398376850435321160857761825,
|
||||
2695241469149243992683268025359863087303400907336026926662328156934068747593,
|
||||
],
|
||||
[
|
||||
1361519681544413849831669554199151294308350560528931040264950307931824877035,
|
||||
1339116632207878730171031743761550901312154740800549632983325427035029084904,
|
||||
790593524918851401449292693473498591068920069246127392274811084156907468875,
|
||||
],
|
||||
[
|
||||
2723400368331924254840192318398326090089058735091724263333980290765736363637,
|
||||
3457180265095920471443772463283225391927927225993685928066766687141729456030,
|
||||
1483675376954327086153452545475557749815683871577400883707749788555424847954,
|
||||
],
|
||||
[
|
||||
2926303836265506736227240325795090239680154099205721426928300056982414025239,
|
||||
543969119775473768170832347411484329362572550684421616624136244239799475526,
|
||||
237401230683847084256617415614300816373730178313253487575312839074042461932,
|
||||
],
|
||||
[
|
||||
844568412840391587862072008674263874021460074878949862892685736454654414423,
|
||||
151922054871708336050647150237534498235916969120198637893731715254687336644,
|
||||
1299332034710622815055321547569101119597030148120309411086203580212105652312,
|
||||
],
|
||||
[
|
||||
487046922649899823989594814663418784068895385009696501386459462815688122993,
|
||||
1104883249092599185744249485896585912845784382683240114120846423960548576851,
|
||||
1458388705536282069567179348797334876446380557083422364875248475157495514484,
|
||||
],
|
||||
[
|
||||
850248109622750774031817200193861444623975329881731864752464222442574976566,
|
||||
2885843173858536690032695698009109793537724845140477446409245651176355435722,
|
||||
3027068551635372249579348422266406787688980506275086097330568993357835463816,
|
||||
],
|
||||
[
|
||||
3231892723647447539926175383213338123506134054432701323145045438168976970994,
|
||||
1719080830641935421242626784132692936776388194122314954558418655725251172826,
|
||||
1172253756541066126131022537343350498482225068791630219494878195815226839450,
|
||||
],
|
||||
[
|
||||
1619232269633026603732619978083169293258272967781186544174521481891163985093,
|
||||
3495680684841853175973173610562400042003100419811771341346135531754869014567,
|
||||
1576161515913099892951745452471618612307857113799539794680346855318958552758,
|
||||
],
|
||||
[
|
||||
2618326122974253423403350731396350223238201817594761152626832144510903048529,
|
||||
2696245132758436974032479782852265185094623165224532063951287925001108567649,
|
||||
930116505665110070247395429730201844026054810856263733273443066419816003444,
|
||||
],
|
||||
[
|
||||
2786389174502246248523918824488629229455088716707062764363111940462137404076,
|
||||
1555260846425735320214671887347115247546042526197895180675436886484523605116,
|
||||
2306241912153325247392671742757902161446877415586158295423293240351799505917,
|
||||
],
|
||||
[
|
||||
411529621724849932999694270803131456243889635467661223241617477462914950626,
|
||||
1542495485262286701469125140275904136434075186064076910329015697714211835205,
|
||||
1853045663799041100600825096887578544265580718909350942241802897995488264551,
|
||||
],
|
||||
[
|
||||
2963055259497271220202739837493041799968576111953080503132045092194513937286,
|
||||
2303806870349915764285872605046527036748108533406243381676768310692344456050,
|
||||
2622104986201990620910286730213140904984256464479840856728424375142929278875,
|
||||
],
|
||||
[
|
||||
2369987021925266811581727383184031736927816625797282287927222602539037105864,
|
||||
285070227712021899602056480426671736057274017903028992288878116056674401781,
|
||||
3034087076179360957800568733595959058628497428787907887933697691951454610691,
|
||||
],
|
||||
[
|
||||
469095854351700119980323115747590868855368701825706298740201488006320881056,
|
||||
360001976264385426746283365024817520563236378289230404095383746911725100012,
|
||||
3438709327109021347267562000879503009590697221730578667498351600602230296178,
|
||||
],
|
||||
[
|
||||
63573904800572228121671659287593650438456772568903228287754075619928214969,
|
||||
3470881855042989871434874691030920672110111605547839662680968354703074556970,
|
||||
724559311507950497340993415408274803001166693839947519425501269424891465492,
|
||||
],
|
||||
[
|
||||
880409284677518997550768549487344416321062350742831373397603704465823658986,
|
||||
6876255662475867703077362872097208259197756317287339941435193538565586230,
|
||||
2701916445133770775447884812906226786217969545216086200932273680400909154638,
|
||||
],
|
||||
[
|
||||
425152119158711585559310064242720816611629181537672850898056934507216982586,
|
||||
1475552998258917706756737045704649573088377604240716286977690565239187213744,
|
||||
2413772448122400684309006716414417978370152271397082147158000439863002593561,
|
||||
],
|
||||
[
|
||||
392160855822256520519339260245328807036619920858503984710539815951012864164,
|
||||
1075036996503791536261050742318169965707018400307026402939804424927087093987,
|
||||
2176439430328703902070742432016450246365760303014562857296722712989275658921,
|
||||
],
|
||||
[
|
||||
1413865976587623331051814207977382826721471106513581745229680113383908569693,
|
||||
4879283427490523253696177116563427032332223531862961281430108575019551814,
|
||||
3392583297537374046875199552977614390492290683707960975137418536812266544902,
|
||||
],
|
||||
[
|
||||
3600854486849487646325182927019642276644093512133907046667282144129939150983,
|
||||
2779924664161372134024229593301361846129279572186444474616319283535189797834,
|
||||
2722699960903170449291146429799738181514821447014433304730310678334403972040,
|
||||
],
|
||||
[
|
||||
819109815049226540285781191874507704729062681836086010078910930707209464699,
|
||||
3046121243742768013822760785918001632929744274211027071381357122228091333823,
|
||||
1339019590803056172509793134119156250729668216522001157582155155947567682278,
|
||||
],
|
||||
[
|
||||
1933279639657506214789316403763326578443023901555983256955812717638093967201,
|
||||
2138221547112520744699126051903811860205771600821672121643894708182292213541,
|
||||
2694713515543641924097704224170357995809887124438248292930846280951601597065,
|
||||
],
|
||||
[
|
||||
2471734202930133750093618989223585244499567111661178960753938272334153710615,
|
||||
504903761112092757611047718215309856203214372330635774577409639907729993533,
|
||||
1943979703748281357156510253941035712048221353507135074336243405478613241290,
|
||||
],
|
||||
[
|
||||
684525210957572142559049112233609445802004614280157992196913315652663518936,
|
||||
1705585400798782397786453706717059483604368413512485532079242223503960814508,
|
||||
192429517716023021556170942988476050278432319516032402725586427701913624665,
|
||||
],
|
||||
[
|
||||
1586493702243128040549584165333371192888583026298039652930372758731750166765,
|
||||
686072673323546915014972146032384917012218151266600268450347114036285993377,
|
||||
3464340397998075738891129996710075228740496767934137465519455338004332839215,
|
||||
],
|
||||
[
|
||||
2805249176617071054530589390406083958753103601524808155663551392362371834663,
|
||||
667746464250968521164727418691487653339733392025160477655836902744186489526,
|
||||
1131527712905109997177270289411406385352032457456054589588342450404257139778,
|
||||
],
|
||||
[
|
||||
1908969485750011212309284349900149072003218505891252313183123635318886241171,
|
||||
1025257076985551890132050019084873267454083056307650830147063480409707787695,
|
||||
2153175291918371429502545470578981828372846236838301412119329786849737957977,
|
||||
],
|
||||
[
|
||||
3410257749736714576487217882785226905621212230027780855361670645857085424384,
|
||||
3442969106887588154491488961893254739289120695377621434680934888062399029952,
|
||||
3029953900235731770255937704976720759948880815387104275525268727341390470237,
|
||||
],
|
||||
[
|
||||
85453456084781138713939104192561924536933417707871501802199311333127894466,
|
||||
2730629666577257820220329078741301754580009106438115341296453318350676425129,
|
||||
178242450661072967256438102630920745430303027840919213764087927763335940415,
|
||||
],
|
||||
[
|
||||
2844589222514708695700541363167856718216388819406388706818431442998498677557,
|
||||
3547876269219141094308889387292091231377253967587961309624916269569559952944,
|
||||
2525005406762984211707203144785482908331876505006839217175334833739957826850,
|
||||
],
|
||||
[
|
||||
3096397013555211396701910432830904669391580557191845136003938801598654871345,
|
||||
574424067119200181933992948252007230348512600107123873197603373898923821490,
|
||||
1714030696055067278349157346067719307863507310709155690164546226450579547098,
|
||||
],
|
||||
[
|
||||
2339895272202694698739231405357972261413383527237194045718815176814132612501,
|
||||
3562501318971895161271663840954705079797767042115717360959659475564651685069,
|
||||
69069358687197963617161747606993436483967992689488259107924379545671193749,
|
||||
],
|
||||
[
|
||||
2614502738369008850475068874731531583863538486212691941619835266611116051561,
|
||||
655247349763023251625727726218660142895322325659927266813592114640858573566,
|
||||
2305235672527595714255517865498269719545193172975330668070873705108690670678,
|
||||
],
|
||||
[
|
||||
926416070297755413261159098243058134401665060349723804040714357642180531931,
|
||||
866523735635840246543516964237513287099659681479228450791071595433217821460,
|
||||
2284334068466681424919271582037156124891004191915573957556691163266198707693,
|
||||
],
|
||||
[
|
||||
1812588309302477291425732810913354633465435706480768615104211305579383928792,
|
||||
2836899808619013605432050476764608707770404125005720004551836441247917488507,
|
||||
2989087789022865112405242078196235025698647423649950459911546051695688370523,
|
||||
],
|
||||
[
|
||||
68056284404189102136488263779598243992465747932368669388126367131855404486,
|
||||
505425339250887519581119854377342241317528319745596963584548343662758204398,
|
||||
2118963546856545068961709089296976921067035227488975882615462246481055679215,
|
||||
],
|
||||
[
|
||||
2253872596319969096156004495313034590996995209785432485705134570745135149681,
|
||||
1625090409149943603241183848936692198923183279116014478406452426158572703264,
|
||||
179139838844452470348634657368199622305888473747024389514258107503778442495,
|
||||
],
|
||||
[
|
||||
1567067018147735642071130442904093290030432522257811793540290101391210410341,
|
||||
2737301854006865242314806979738760349397411136469975337509958305470398783585,
|
||||
3002738216460904473515791428798860225499078134627026021350799206894618186256,
|
||||
],
|
||||
[
|
||||
374029488099466837453096950537275565120689146401077127482884887409712315162,
|
||||
973403256517481077805460710540468856199855789930951602150773500862180885363,
|
||||
2691967457038172130555117632010860984519926022632800605713473799739632878867,
|
||||
],
|
||||
[
|
||||
3515906794910381201365530594248181418811879320679684239326734893975752012109,
|
||||
148057579455448384062325089530558091463206199724854022070244924642222283388,
|
||||
1541588700238272710315890873051237741033408846596322948443180470429851502842,
|
||||
],
|
||||
[
|
||||
147013865879011936545137344076637170977925826031496203944786839068852795297,
|
||||
2630278389304735265620281704608245039972003761509102213752997636382302839857,
|
||||
1359048670759642844930007747955701205155822111403150159614453244477853867621,
|
||||
],
|
||||
[
|
||||
2438984569205812336319229336885480537793786558293523767186829418969842616677,
|
||||
2137792255841525507649318539501906353254503076308308692873313199435029594138,
|
||||
2262318076430740712267739371170174514379142884859595360065535117601097652755,
|
||||
],
|
||||
[
|
||||
2792703718581084537295613508201818489836796608902614779596544185252826291584,
|
||||
2294173715793292812015960640392421991604150133581218254866878921346561546149,
|
||||
2770011224727997178743274791849308200493823127651418989170761007078565678171,
|
||||
],
|
||||
]
|
||||
559
test/stark/poseidon/poseidon4.txt
Normal file
559
test/stark/poseidon/poseidon4.txt
Normal file
@@ -0,0 +1,559 @@
|
||||
Rate = 3
|
||||
Capacity = 1
|
||||
FullRounds = 8
|
||||
PartialRounds = 84
|
||||
MDS = [[2, 1, 1, 1], [1, 1, 1, 1], [1, 1, 0, 1], [1, 1, 1, -1]]
|
||||
RoundKeys = [
|
||||
[
|
||||
2950795762459345168613727575620414179244544320470208355568817838579231751791,
|
||||
1587446564224215276866294500450702039420286416111469274423465069420553242820,
|
||||
1645965921169490687904413452218868659025437693527479459426157555728339600137,
|
||||
2782373324549879794752287702905278018819686065818504085638398966973694145741,
|
||||
],
|
||||
[
|
||||
3409172630025222641379726933524480516420204828329395644967085131392375707302,
|
||||
2379053116496905638239090788901387719228422033660130943198035907032739387135,
|
||||
2570819397480941104144008784293466051718826502582588529995520356691856497111,
|
||||
3546220846133880637977653625763703334841539452343273304410918449202580719746,
|
||||
],
|
||||
[
|
||||
2720682389492889709700489490056111332164748138023159726590726667539759963454,
|
||||
1899653471897224903834726250400246354200311275092866725547887381599836519005,
|
||||
2369443697923857319844855392163763375394720104106200469525915896159690979559,
|
||||
2354174693689535854311272135513626412848402744119855553970180659094265527996,
|
||||
],
|
||||
[
|
||||
2404084503073127963385083467393598147276436640877011103379112521338973185443,
|
||||
950320777137731763811524327595514151340412860090489448295239456547370725376,
|
||||
2121140748740143694053732746913428481442990369183417228688865837805149503386,
|
||||
2372065044800422557577242066480215868569521938346032514014152523102053709709,
|
||||
],
|
||||
[
|
||||
2618497439310693947058545060953893433487994458443568169824149550389484489896,
|
||||
3518297267402065742048564133910509847197496119850246255805075095266319996916,
|
||||
340529752683340505065238931581518232901634742162506851191464448040657139775,
|
||||
1954876811294863748406056845662382214841467408616109501720437541211031966538,
|
||||
],
|
||||
[
|
||||
813813157354633930267029888722341725864333883175521358739311868164460385261,
|
||||
71901595776070443337150458310956362034911936706490730914901986556638720031,
|
||||
2789761472166115462625363403490399263810962093264318361008954888847594113421,
|
||||
2628791615374802560074754031104384456692791616314774034906110098358135152410,
|
||||
],
|
||||
[
|
||||
3617032588734559635167557152518265808024917503198278888820567553943986939719,
|
||||
2624012360209966117322788103333497793082705816015202046036057821340914061980,
|
||||
149101987103211771991327927827692640556911620408176100290586418839323044234,
|
||||
1039927963829140138166373450440320262590862908847727961488297105916489431045,
|
||||
],
|
||||
[
|
||||
2213946951050724449162431068646025833746639391992751674082854766704900195669,
|
||||
2792724903541814965769131737117981991997031078369482697195201969174353468597,
|
||||
3212031629728871219804596347439383805499808476303618848198208101593976279441,
|
||||
3343514080098703935339621028041191631325798327656683100151836206557453199613,
|
||||
],
|
||||
[
|
||||
614054702436541219556958850933730254992710988573177298270089989048553060199,
|
||||
148148081026449726283933484730968827750202042869875329032965774667206931170,
|
||||
1158283532103191908366672518396366136968613180867652172211392033571980848414,
|
||||
1032400527342371389481069504520755916075559110755235773196747439146396688513,
|
||||
],
|
||||
[
|
||||
806900704622005851310078578853499250941978435851598088619290797134710613736,
|
||||
462498083559902778091095573017508352472262817904991134671058825705968404510,
|
||||
1003580119810278869589347418043095667699674425582646347949349245557449452503,
|
||||
619074932220101074089137133998298830285661916867732916607601635248249357793,
|
||||
],
|
||||
[
|
||||
2635090520059500019661864086615522409798872905401305311748231832709078452746,
|
||||
978252636251682252755279071140187792306115352460774007308726210405257135181,
|
||||
1766912167973123409669091967764158892111310474906691336473559256218048677083,
|
||||
1663265127259512472182980890707014969235283233442916350121860684522654120381,
|
||||
],
|
||||
[
|
||||
3532407621206959585000336211742670185380751515636605428496206887841428074250,
|
||||
2507023127157093845256722098502856938353143387711652912931112668310034975446,
|
||||
3321152907858462102434883844787153373036767230808678981306827073335525034593,
|
||||
3039253036806065280643845548147711477270022154459620569428286684179698125661,
|
||||
],
|
||||
[
|
||||
103480338868480851881924519768416587261556021758163719199282794248762465380,
|
||||
2394049781357087698434751577708655768465803975478348134669006211289636928495,
|
||||
2660531560345476340796109810821127229446538730404600368347902087220064379579,
|
||||
3603166934034556203649050570865466556260359798872408576857928196141785055563,
|
||||
],
|
||||
[
|
||||
1553799760191949768532188139643704561532896296986025007089826672890485412324,
|
||||
2744284717053657689091306578463476341218866418732695211367062598446038965164,
|
||||
320745764922149897598257794663594419839885234101078803811049904310835548856,
|
||||
979382242100682161589753881721708883681034024104145498709287731138044566302,
|
||||
],
|
||||
[
|
||||
1860426855810549882740147175136418997351054138609396651615467358416651354991,
|
||||
336173081054369235994909356892506146234495707857220254489443629387613956145,
|
||||
1632470326779699229772327605759783482411227247311431865655466227711078175883,
|
||||
921958250077481394074960433988881176409497663777043304881055317463712938502,
|
||||
],
|
||||
[
|
||||
3034358982193370602048539901033542101022185309652879937418114324899281842797,
|
||||
25626282149517463867572353922222474817434101087272320606729439087234878607,
|
||||
3002662261401575565838149305485737102400501329139562227180277188790091853682,
|
||||
2939684373453383817196521641512509179310654199629514917426341354023324109367,
|
||||
],
|
||||
[
|
||||
1076484609897998179434851570277297233169621096172424141759873688902355505136,
|
||||
2575095284833160494841112025725243274091830284746697961080467506739203605049,
|
||||
3565075264617591783581665711620369529657840830498005563542124551465195621851,
|
||||
2197016502533303822395077038351174326125210255869204501838837289716363437993,
|
||||
],
|
||||
[
|
||||
331415322883530754594261416546036195982886300052707474899691116664327869405,
|
||||
1935011233711290003793244296594669823169522055520303479680359990463281661839,
|
||||
3495901467168087413996941216661589517270845976538454329511167073314577412322,
|
||||
954195417117133246453562983448451025087661597543338750600301835944144520375,
|
||||
],
|
||||
[
|
||||
1271840477709992894995746871435810599280944810893784031132923384456797925777,
|
||||
2565310762274337662754531859505158700827688964841878141121196528015826671847,
|
||||
3365022288251637014588279139038152521653896670895105540140002607272936852513,
|
||||
1660592021628965529963974299647026602622092163312666588591285654477111176051,
|
||||
],
|
||||
[
|
||||
970104372286014048279296575474974982288801187216974504035759997141059513421,
|
||||
2617024574317953753849168721871770134225690844968986289121504184985993971227,
|
||||
999899815343607746071464113462778273556695659506865124478430189024755832262,
|
||||
2228536129413411161615629030408828764980855956560026807518714080003644769896,
|
||||
],
|
||||
[
|
||||
2701953891198001564547196795777701119629537795442025393867364730330476403227,
|
||||
837078355588159388741598313782044128527494922918203556465116291436461597853,
|
||||
2121749601840466143704862369657561429793951309962582099604848281796392359214,
|
||||
771812260179247428733132708063116523892339056677915387749121983038690154755,
|
||||
],
|
||||
[
|
||||
3317336423132806446086732225036532603224267214833263122557471741829060578219,
|
||||
481570067997721834712647566896657604857788523050900222145547508314620762046,
|
||||
242195042559343964206291740270858862066153636168162642380846129622127460192,
|
||||
2855462178889999218204481481614105202770810647859867354506557827319138379686,
|
||||
],
|
||||
[
|
||||
3525521107148375040131784770413887305850308357895464453970651672160034885202,
|
||||
1320839531502392535964065058804908871811967681250362364246430459003920305799,
|
||||
2514191518588387125173345107242226637171897291221681115249521904869763202419,
|
||||
2798335750958827619666318316247381695117827718387653874070218127140615157902,
|
||||
],
|
||||
[
|
||||
2808467767967035643407948058486565877867906577474361783201337540214875566395,
|
||||
3551834385992706206273955480294669176699286104229279436819137165202231595747,
|
||||
1219439673853113792340300173186247996249367102884530407862469123523013083971,
|
||||
761519904537984520554247997444508040636526566551719396202550009393012691157,
|
||||
],
|
||||
[
|
||||
3355402549169351700500518865338783382387571349497391475317206324155237401353,
|
||||
199541098009731541347317515995192175813554789571447733944970283654592727138,
|
||||
192100490643078165121235261796864975568292640203635147901612231594408079071,
|
||||
1187019357602953326192019968809486933768550466167033084944727938441427050581,
|
||||
],
|
||||
[
|
||||
189525349641911362389041124808934468936759383310282010671081989585219065700,
|
||||
2831653363992091308880573627558515686245403755586311978724025292003353336665,
|
||||
2052859812632218952608271535089179639890275494426396974475479657192657094698,
|
||||
1670756178709659908159049531058853320846231785448204274277900022176591811072,
|
||||
],
|
||||
[
|
||||
3538757242013734574731807289786598937548399719866320954894004830207085723125,
|
||||
710549042741321081781917034337800036872214466705318638023070812391485261299,
|
||||
2345013122330545298606028187653996682275206910242635100920038943391319595180,
|
||||
3528369671971445493932880023233332035122954362711876290904323783426765912206,
|
||||
],
|
||||
[
|
||||
1167120829038120978297497195837406760848728897181138760506162680655977700764,
|
||||
3073243357129146594530765548901087443775563058893907738967898816092270628884,
|
||||
378514724418106317738164464176041649567501099164061863402473942795977719726,
|
||||
333391138410406330127594722511180398159664250722328578952158227406762627796,
|
||||
],
|
||||
[
|
||||
1727570175639917398410201375510924114487348765559913502662122372848626931905,
|
||||
968312190621809249603425066974405725769739606059422769908547372904403793174,
|
||||
360659316299446405855194688051178331671817370423873014757323462844775818348,
|
||||
1386580151907705298970465943238806620109618995410132218037375811184684929291,
|
||||
],
|
||||
[
|
||||
3604888328937389309031638299660239238400230206645344173700074923133890528967,
|
||||
2496185632263372962152518155651824899299616724241852816983268163379540137546,
|
||||
486538168871046887467737983064272608432052269868418721234810979756540672990,
|
||||
1558415498960552213241704009433360128041672577274390114589014204605400783336,
|
||||
],
|
||||
[
|
||||
3512058327686147326577190314835092911156317204978509183234511559551181053926,
|
||||
2235429387083113882635494090887463486491842634403047716936833563914243946191,
|
||||
1290896777143878193192832813769470418518651727840187056683408155503813799882,
|
||||
1143310336918357319571079551779316654556781203013096026972411429993634080835,
|
||||
],
|
||||
[
|
||||
3235435208525081966062419599803346573407862428113723170955762956243193422118,
|
||||
1293239921425673430660897025143433077974838969258268884994339615096356996604,
|
||||
236252269127612784685426260840574970698541177557674806964960352572864382971,
|
||||
1733907592497266237374827232200506798207318263912423249709509725341212026275,
|
||||
],
|
||||
[
|
||||
302004309771755665128395814807589350526779835595021835389022325987048089868,
|
||||
3018926838139221755384801385583867283206879023218491758435446265703006270945,
|
||||
39701437664873825906031098349904330565195980985885489447836580931425171297,
|
||||
908381723021746969965674308809436059628307487140174335882627549095646509778,
|
||||
],
|
||||
[
|
||||
219062858908229855064136253265968615354041842047384625689776811853821594358,
|
||||
1283129863776453589317845316917890202859466483456216900835390291449830275503,
|
||||
418512623547417594896140369190919231877873410935689672661226540908900544012,
|
||||
1792181590047131972851015200157890246436013346535432437041535789841136268632,
|
||||
],
|
||||
[
|
||||
370546432987510607338044736824316856592558876687225326692366316978098770516,
|
||||
3323437805230586112013581113386626899534419826098235300155664022709435756946,
|
||||
910076621742039763058481476739499965761942516177975130656340375573185415877,
|
||||
1762188042455633427137702520675816545396284185254002959309669405982213803405,
|
||||
],
|
||||
[
|
||||
2186362253913140345102191078329764107619534641234549431429008219905315900520,
|
||||
2230647725927681765419218738218528849146504088716182944327179019215826045083,
|
||||
1069243907556644434301190076451112491469636357133398376850435321160857761825,
|
||||
2695241469149243992683268025359863087303400907336026926662328156934068747593,
|
||||
],
|
||||
[
|
||||
1361519681544413849831669554199151294308350560528931040264950307931824877035,
|
||||
1339116632207878730171031743761550901312154740800549632983325427035029084904,
|
||||
790593524918851401449292693473498591068920069246127392274811084156907468875,
|
||||
2723400368331924254840192318398326090089058735091724263333980290765736363637,
|
||||
],
|
||||
[
|
||||
3457180265095920471443772463283225391927927225993685928066766687141729456030,
|
||||
1483675376954327086153452545475557749815683871577400883707749788555424847954,
|
||||
2926303836265506736227240325795090239680154099205721426928300056982414025239,
|
||||
543969119775473768170832347411484329362572550684421616624136244239799475526,
|
||||
],
|
||||
[
|
||||
237401230683847084256617415614300816373730178313253487575312839074042461932,
|
||||
844568412840391587862072008674263874021460074878949862892685736454654414423,
|
||||
151922054871708336050647150237534498235916969120198637893731715254687336644,
|
||||
1299332034710622815055321547569101119597030148120309411086203580212105652312,
|
||||
],
|
||||
[
|
||||
487046922649899823989594814663418784068895385009696501386459462815688122993,
|
||||
1104883249092599185744249485896585912845784382683240114120846423960548576851,
|
||||
1458388705536282069567179348797334876446380557083422364875248475157495514484,
|
||||
850248109622750774031817200193861444623975329881731864752464222442574976566,
|
||||
],
|
||||
[
|
||||
2885843173858536690032695698009109793537724845140477446409245651176355435722,
|
||||
3027068551635372249579348422266406787688980506275086097330568993357835463816,
|
||||
3231892723647447539926175383213338123506134054432701323145045438168976970994,
|
||||
1719080830641935421242626784132692936776388194122314954558418655725251172826,
|
||||
],
|
||||
[
|
||||
1172253756541066126131022537343350498482225068791630219494878195815226839450,
|
||||
1619232269633026603732619978083169293258272967781186544174521481891163985093,
|
||||
3495680684841853175973173610562400042003100419811771341346135531754869014567,
|
||||
1576161515913099892951745452471618612307857113799539794680346855318958552758,
|
||||
],
|
||||
[
|
||||
2618326122974253423403350731396350223238201817594761152626832144510903048529,
|
||||
2696245132758436974032479782852265185094623165224532063951287925001108567649,
|
||||
930116505665110070247395429730201844026054810856263733273443066419816003444,
|
||||
2786389174502246248523918824488629229455088716707062764363111940462137404076,
|
||||
],
|
||||
[
|
||||
1555260846425735320214671887347115247546042526197895180675436886484523605116,
|
||||
2306241912153325247392671742757902161446877415586158295423293240351799505917,
|
||||
411529621724849932999694270803131456243889635467661223241617477462914950626,
|
||||
1542495485262286701469125140275904136434075186064076910329015697714211835205,
|
||||
],
|
||||
[
|
||||
1853045663799041100600825096887578544265580718909350942241802897995488264551,
|
||||
2963055259497271220202739837493041799968576111953080503132045092194513937286,
|
||||
2303806870349915764285872605046527036748108533406243381676768310692344456050,
|
||||
2622104986201990620910286730213140904984256464479840856728424375142929278875,
|
||||
],
|
||||
[
|
||||
2369987021925266811581727383184031736927816625797282287927222602539037105864,
|
||||
285070227712021899602056480426671736057274017903028992288878116056674401781,
|
||||
3034087076179360957800568733595959058628497428787907887933697691951454610691,
|
||||
469095854351700119980323115747590868855368701825706298740201488006320881056,
|
||||
],
|
||||
[
|
||||
360001976264385426746283365024817520563236378289230404095383746911725100012,
|
||||
3438709327109021347267562000879503009590697221730578667498351600602230296178,
|
||||
63573904800572228121671659287593650438456772568903228287754075619928214969,
|
||||
3470881855042989871434874691030920672110111605547839662680968354703074556970,
|
||||
],
|
||||
[
|
||||
724559311507950497340993415408274803001166693839947519425501269424891465492,
|
||||
880409284677518997550768549487344416321062350742831373397603704465823658986,
|
||||
6876255662475867703077362872097208259197756317287339941435193538565586230,
|
||||
2701916445133770775447884812906226786217969545216086200932273680400909154638,
|
||||
],
|
||||
[
|
||||
425152119158711585559310064242720816611629181537672850898056934507216982586,
|
||||
1475552998258917706756737045704649573088377604240716286977690565239187213744,
|
||||
2413772448122400684309006716414417978370152271397082147158000439863002593561,
|
||||
392160855822256520519339260245328807036619920858503984710539815951012864164,
|
||||
],
|
||||
[
|
||||
1075036996503791536261050742318169965707018400307026402939804424927087093987,
|
||||
2176439430328703902070742432016450246365760303014562857296722712989275658921,
|
||||
1413865976587623331051814207977382826721471106513581745229680113383908569693,
|
||||
4879283427490523253696177116563427032332223531862961281430108575019551814,
|
||||
],
|
||||
[
|
||||
3392583297537374046875199552977614390492290683707960975137418536812266544902,
|
||||
3600854486849487646325182927019642276644093512133907046667282144129939150983,
|
||||
2779924664161372134024229593301361846129279572186444474616319283535189797834,
|
||||
2722699960903170449291146429799738181514821447014433304730310678334403972040,
|
||||
],
|
||||
[
|
||||
819109815049226540285781191874507704729062681836086010078910930707209464699,
|
||||
3046121243742768013822760785918001632929744274211027071381357122228091333823,
|
||||
1339019590803056172509793134119156250729668216522001157582155155947567682278,
|
||||
1933279639657506214789316403763326578443023901555983256955812717638093967201,
|
||||
],
|
||||
[
|
||||
2138221547112520744699126051903811860205771600821672121643894708182292213541,
|
||||
2694713515543641924097704224170357995809887124438248292930846280951601597065,
|
||||
2471734202930133750093618989223585244499567111661178960753938272334153710615,
|
||||
504903761112092757611047718215309856203214372330635774577409639907729993533,
|
||||
],
|
||||
[
|
||||
1943979703748281357156510253941035712048221353507135074336243405478613241290,
|
||||
684525210957572142559049112233609445802004614280157992196913315652663518936,
|
||||
1705585400798782397786453706717059483604368413512485532079242223503960814508,
|
||||
192429517716023021556170942988476050278432319516032402725586427701913624665,
|
||||
],
|
||||
[
|
||||
1586493702243128040549584165333371192888583026298039652930372758731750166765,
|
||||
686072673323546915014972146032384917012218151266600268450347114036285993377,
|
||||
3464340397998075738891129996710075228740496767934137465519455338004332839215,
|
||||
2805249176617071054530589390406083958753103601524808155663551392362371834663,
|
||||
],
|
||||
[
|
||||
667746464250968521164727418691487653339733392025160477655836902744186489526,
|
||||
1131527712905109997177270289411406385352032457456054589588342450404257139778,
|
||||
1908969485750011212309284349900149072003218505891252313183123635318886241171,
|
||||
1025257076985551890132050019084873267454083056307650830147063480409707787695,
|
||||
],
|
||||
[
|
||||
2153175291918371429502545470578981828372846236838301412119329786849737957977,
|
||||
3410257749736714576487217882785226905621212230027780855361670645857085424384,
|
||||
3442969106887588154491488961893254739289120695377621434680934888062399029952,
|
||||
3029953900235731770255937704976720759948880815387104275525268727341390470237,
|
||||
],
|
||||
[
|
||||
85453456084781138713939104192561924536933417707871501802199311333127894466,
|
||||
2730629666577257820220329078741301754580009106438115341296453318350676425129,
|
||||
178242450661072967256438102630920745430303027840919213764087927763335940415,
|
||||
2844589222514708695700541363167856718216388819406388706818431442998498677557,
|
||||
],
|
||||
[
|
||||
3547876269219141094308889387292091231377253967587961309624916269569559952944,
|
||||
2525005406762984211707203144785482908331876505006839217175334833739957826850,
|
||||
3096397013555211396701910432830904669391580557191845136003938801598654871345,
|
||||
574424067119200181933992948252007230348512600107123873197603373898923821490,
|
||||
],
|
||||
[
|
||||
1714030696055067278349157346067719307863507310709155690164546226450579547098,
|
||||
2339895272202694698739231405357972261413383527237194045718815176814132612501,
|
||||
3562501318971895161271663840954705079797767042115717360959659475564651685069,
|
||||
69069358687197963617161747606993436483967992689488259107924379545671193749,
|
||||
],
|
||||
[
|
||||
2614502738369008850475068874731531583863538486212691941619835266611116051561,
|
||||
655247349763023251625727726218660142895322325659927266813592114640858573566,
|
||||
2305235672527595714255517865498269719545193172975330668070873705108690670678,
|
||||
926416070297755413261159098243058134401665060349723804040714357642180531931,
|
||||
],
|
||||
[
|
||||
866523735635840246543516964237513287099659681479228450791071595433217821460,
|
||||
2284334068466681424919271582037156124891004191915573957556691163266198707693,
|
||||
1812588309302477291425732810913354633465435706480768615104211305579383928792,
|
||||
2836899808619013605432050476764608707770404125005720004551836441247917488507,
|
||||
],
|
||||
[
|
||||
2989087789022865112405242078196235025698647423649950459911546051695688370523,
|
||||
68056284404189102136488263779598243992465747932368669388126367131855404486,
|
||||
505425339250887519581119854377342241317528319745596963584548343662758204398,
|
||||
2118963546856545068961709089296976921067035227488975882615462246481055679215,
|
||||
],
|
||||
[
|
||||
2253872596319969096156004495313034590996995209785432485705134570745135149681,
|
||||
1625090409149943603241183848936692198923183279116014478406452426158572703264,
|
||||
179139838844452470348634657368199622305888473747024389514258107503778442495,
|
||||
1567067018147735642071130442904093290030432522257811793540290101391210410341,
|
||||
],
|
||||
[
|
||||
2737301854006865242314806979738760349397411136469975337509958305470398783585,
|
||||
3002738216460904473515791428798860225499078134627026021350799206894618186256,
|
||||
374029488099466837453096950537275565120689146401077127482884887409712315162,
|
||||
973403256517481077805460710540468856199855789930951602150773500862180885363,
|
||||
],
|
||||
[
|
||||
2691967457038172130555117632010860984519926022632800605713473799739632878867,
|
||||
3515906794910381201365530594248181418811879320679684239326734893975752012109,
|
||||
148057579455448384062325089530558091463206199724854022070244924642222283388,
|
||||
1541588700238272710315890873051237741033408846596322948443180470429851502842,
|
||||
],
|
||||
[
|
||||
147013865879011936545137344076637170977925826031496203944786839068852795297,
|
||||
2630278389304735265620281704608245039972003761509102213752997636382302839857,
|
||||
1359048670759642844930007747955701205155822111403150159614453244477853867621,
|
||||
2438984569205812336319229336885480537793786558293523767186829418969842616677,
|
||||
],
|
||||
[
|
||||
2137792255841525507649318539501906353254503076308308692873313199435029594138,
|
||||
2262318076430740712267739371170174514379142884859595360065535117601097652755,
|
||||
2792703718581084537295613508201818489836796608902614779596544185252826291584,
|
||||
2294173715793292812015960640392421991604150133581218254866878921346561546149,
|
||||
],
|
||||
[
|
||||
2770011224727997178743274791849308200493823127651418989170761007078565678171,
|
||||
3321642244537785916275181932172303118112488081726311374164578600576901819844,
|
||||
3522708517589950573320671158134804505970724681591943826922697952040487655044,
|
||||
3417974441436557992524691506735790206623600049454586729879955931972546347402,
|
||||
],
|
||||
[
|
||||
175039333145381316571259690443853067809784261609912638686739799135919647022,
|
||||
1930713062131033273316869231148248962041053029742760224583505092759642967464,
|
||||
2971452932574554603554350185069538580257636405419430340233233400633251319042,
|
||||
2774781903758215341037075610938953949868289195845367046186588750871862784919,
|
||||
],
|
||||
[
|
||||
666516874137869653699423537799457099346460194092311952417454613224504932738,
|
||||
1900462225013533249140457703727169176351786259991305560412832202759625668041,
|
||||
2665631186082687279121709429531834469477679375137509769347092380798929714377,
|
||||
837840745988147279235494664091280091563355097569199320366973125128366540061,
|
||||
],
|
||||
[
|
||||
3391544118305848781823721719916289805455110924839794510205940718821197620955,
|
||||
2888553035909938253628892138500390690221493345071933642853222968481016605919,
|
||||
3386241569867597612447901482685846444743718781330869478721963580925825915450,
|
||||
1205126220630896984850042596877918177217334376800874965105642874206963597698,
|
||||
],
|
||||
[
|
||||
3590072615491710252422997155203204584659171612188004116415640739580250394190,
|
||||
692469013329617220154003334549812915100479873898898958632988703738125356983,
|
||||
1623178235190707102808841905143937367808788834203621005714003335195182126335,
|
||||
1972826180775011489122426045504602288576507493792470102803637471568052321297,
|
||||
],
|
||||
[
|
||||
3415141329098504418158191749675997877417539760075593313736376750580696083073,
|
||||
587811537889727046473915684463981273175495137461951211739526104349163747811,
|
||||
2523982964351069134084525951849317400231659428055762640605248929856135518199,
|
||||
2686176526711834950207666047281383173339057216783586039351834948812568447629,
|
||||
],
|
||||
[
|
||||
983144446441739425577690449774542566745526459152966545642451764143532586964,
|
||||
171558252019175695567663688494555626159399786667979998273792882504784080805,
|
||||
332337623010057542760158225837623039780806442976079546879646069338600179518,
|
||||
1264669683963885571544813806669118319675288608634733888843804451222546848295,
|
||||
],
|
||||
[
|
||||
2426165115815723668018318268486497504249785449504758403912155206515511627681,
|
||||
11387399609384288947733630450855186629703576293221897150193655994854764608,
|
||||
2541728569046079092074077754414781968906176513081761690404588216304985421091,
|
||||
47685947554980329431290582269851186106577733250761848107645535357326439312,
|
||||
],
|
||||
[
|
||||
472176388418187405374813530639596064799362505024895746833751173199773896628,
|
||||
2764298116617383397920343358525617195195060562266243809245480210157942112738,
|
||||
486863835068754002670800862273365477867695879270721744227071001883208334054,
|
||||
2973492686137102577527656941792991264994301121122130295965761350095846874635,
|
||||
],
|
||||
[
|
||||
178385615141132702906181473263873416748818415607305319148067639744074654009,
|
||||
533624640096756667052211553746016402543259206286603356120804827761339634127,
|
||||
819406716720171922688026098737835227857400444543748198788964759773510472096,
|
||||
531851793767260921861217458033110066464894334064526987603936107947006031387,
|
||||
],
|
||||
[
|
||||
3269709072483585277009748181134917746036523619604017812342933951952104134829,
|
||||
838191718603413598040249006803464503100808192944407407147899973659013630611,
|
||||
1574561296941310904780257598780779812250055948216417844567262310524022037406,
|
||||
551394354289003977607664358739006072556227894953233419144430578080352094737,
|
||||
],
|
||||
[
|
||||
445076790942318675726839050057337819004979443030540904213920669247413907302,
|
||||
1963946696292687224902912968478695543164747600779913024040878700455222386521,
|
||||
484284614181963381509745298932402076252103342403432879800905151752488144767,
|
||||
2240507606126946994415203252302826782042951346966859379502140796364876543253,
|
||||
],
|
||||
[
|
||||
3237135638753992982179886898758938279897590886053928839613434762582576319619,
|
||||
2334333034701915027889533058426879447140084891006486138782876488162658230991,
|
||||
14411091399844539897439754491034751977136685514851444574462584316609631592,
|
||||
1264480371592407258420308876448697804787923638319277097663041109464608464284,
|
||||
],
|
||||
[
|
||||
671929312763821646360589403212798993954209530574443543917757335777610372144,
|
||||
2513909805455654095962542944994577107405216428214873444765576238504714067396,
|
||||
870121102846043786263357605823753628974859886859187558617096145653709171231,
|
||||
399132620893316356411986266679786708905730068946836982293484206366500277754,
|
||||
],
|
||||
[
|
||||
2855046250836680633532995284655778407402587437073106249445470889390454667586,
|
||||
2063679741125384345396981490971605710211281905716315529671473143278849561151,
|
||||
1433753212258929925682201698758056443128516570551146995210728194816988328337,
|
||||
3334984763425011856632257855270507440816274246647423607159847074739331865077,
|
||||
],
|
||||
[
|
||||
337911293622078184850923533628334646725451591671907148383867096651211846605,
|
||||
559587005295238702015018022040357402231957131094636365177008701077975941644,
|
||||
885963059604819264377490633589388189646118257469490919900554134369512794660,
|
||||
1957748763518471091057032383332840331641373304981058387824598000170709016333,
|
||||
],
|
||||
[
|
||||
3175295982155056798972302481564899381103533409383494814704562889625572018450,
|
||||
498987160612401618114584726510347771865331516606886613019084323862447372555,
|
||||
947374835104260364630171441676101001841507588423166778786886198914150312958,
|
||||
906933977754491302438795274167251538820934378773708095543613756654712689280,
|
||||
],
|
||||
[
|
||||
2170116291766863179909957030577284618726490893598499117272497866180009722894,
|
||||
1801335399574515889082584621772588704763181408217893911806726119813067220453,
|
||||
1942500232535842474530840356353427989892065499159260166135596750084681859966,
|
||||
62936080219825306823124060587235998278756755377419521154040408253893795176,
|
||||
],
|
||||
[
|
||||
3091993939935137795359769774909373279950941171574748645375255810204590357753,
|
||||
1283528386884634267663661033944552098742115012555712906773586466375284501324,
|
||||
1581820717639229420476069802992937438655873471854930764425841549067913106065,
|
||||
2301986095388751633126546121528329200085681648876910655269533407603441046514,
|
||||
],
|
||||
[
|
||||
2850003828037698751961753862613545302539465803982364898225617297398939302949,
|
||||
48024691078494936445046366770271288984930221238071705874025261821606393528,
|
||||
1482336297033144958942154923925185950152551534403871620222916667536030875354,
|
||||
3081177564717719643771186007689458633949181485535169123213511264603782950049,
|
||||
],
|
||||
[
|
||||
3315701127039521853279746297714590495201061397709680410650043502532250578075,
|
||||
3514407611000441301995070394422463400067690470546731164089622325748803106020,
|
||||
368970178199930154322724953487299516224498421233447528815195701420122548537,
|
||||
584353160413525267849669053228533951552602295860601556035386665117717227391,
|
||||
],
|
||||
[
|
||||
752038702160385294706011538400822066722189014251268673051846350397729870418,
|
||||
3594041683498798688197194521326299097635429790757880308152971477196489335154,
|
||||
1367902435756906062215608264424138718742854099315395230911274560900857414183,
|
||||
1828549068951502746189364466794037234789986878381694857475972053743463890779,
|
||||
],
|
||||
[
|
||||
488172495141237210878388657234137733008417573114482400652274985829148564248,
|
||||
962906242461930394022372340919543491337923491322497419797555620396501785566,
|
||||
2275418085010046236619290386129138234541669589549771944697082317642065048898,
|
||||
1966395064658902622886154686288219600816893261614483533899715888994623208964,
|
||||
],
|
||||
[
|
||||
3496095878293416917311185659829821476802828534554531050412634978086916288609,
|
||||
3368478822390537245916137403277928093536087427951052230723275731232142463388,
|
||||
3397410259276620127103231993277518800970669191016277541098821699302368873803,
|
||||
2662600899665871010006649609856695263727220473364611552472965243032255906029,
|
||||
],
|
||||
]
|
||||
651
test/stark/poseidon/poseidon5.txt
Normal file
651
test/stark/poseidon/poseidon5.txt
Normal file
@@ -0,0 +1,651 @@
|
||||
Rate = 4
|
||||
Capacity = 1
|
||||
FullRounds = 8
|
||||
PartialRounds = 84
|
||||
MDS = [[3, 1, 1, 1, 1], [1, 2, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, -1, 1], [1, 1, 1, 1, -2]]
|
||||
RoundKeys = [
|
||||
[
|
||||
2950795762459345168613727575620414179244544320470208355568817838579231751791,
|
||||
1587446564224215276866294500450702039420286416111469274423465069420553242820,
|
||||
1645965921169490687904413452218868659025437693527479459426157555728339600137,
|
||||
2782373324549879794752287702905278018819686065818504085638398966973694145741,
|
||||
3409172630025222641379726933524480516420204828329395644967085131392375707302,
|
||||
],
|
||||
[
|
||||
2379053116496905638239090788901387719228422033660130943198035907032739387135,
|
||||
2570819397480941104144008784293466051718826502582588529995520356691856497111,
|
||||
3546220846133880637977653625763703334841539452343273304410918449202580719746,
|
||||
2720682389492889709700489490056111332164748138023159726590726667539759963454,
|
||||
1899653471897224903834726250400246354200311275092866725547887381599836519005,
|
||||
],
|
||||
[
|
||||
2369443697923857319844855392163763375394720104106200469525915896159690979559,
|
||||
2354174693689535854311272135513626412848402744119855553970180659094265527996,
|
||||
2404084503073127963385083467393598147276436640877011103379112521338973185443,
|
||||
950320777137731763811524327595514151340412860090489448295239456547370725376,
|
||||
2121140748740143694053732746913428481442990369183417228688865837805149503386,
|
||||
],
|
||||
[
|
||||
2372065044800422557577242066480215868569521938346032514014152523102053709709,
|
||||
2618497439310693947058545060953893433487994458443568169824149550389484489896,
|
||||
3518297267402065742048564133910509847197496119850246255805075095266319996916,
|
||||
340529752683340505065238931581518232901634742162506851191464448040657139775,
|
||||
1954876811294863748406056845662382214841467408616109501720437541211031966538,
|
||||
],
|
||||
[
|
||||
813813157354633930267029888722341725864333883175521358739311868164460385261,
|
||||
71901595776070443337150458310956362034911936706490730914901986556638720031,
|
||||
2789761472166115462625363403490399263810962093264318361008954888847594113421,
|
||||
2628791615374802560074754031104384456692791616314774034906110098358135152410,
|
||||
3617032588734559635167557152518265808024917503198278888820567553943986939719,
|
||||
],
|
||||
[
|
||||
2624012360209966117322788103333497793082705816015202046036057821340914061980,
|
||||
149101987103211771991327927827692640556911620408176100290586418839323044234,
|
||||
1039927963829140138166373450440320262590862908847727961488297105916489431045,
|
||||
2213946951050724449162431068646025833746639391992751674082854766704900195669,
|
||||
2792724903541814965769131737117981991997031078369482697195201969174353468597,
|
||||
],
|
||||
[
|
||||
3212031629728871219804596347439383805499808476303618848198208101593976279441,
|
||||
3343514080098703935339621028041191631325798327656683100151836206557453199613,
|
||||
614054702436541219556958850933730254992710988573177298270089989048553060199,
|
||||
148148081026449726283933484730968827750202042869875329032965774667206931170,
|
||||
1158283532103191908366672518396366136968613180867652172211392033571980848414,
|
||||
],
|
||||
[
|
||||
1032400527342371389481069504520755916075559110755235773196747439146396688513,
|
||||
806900704622005851310078578853499250941978435851598088619290797134710613736,
|
||||
462498083559902778091095573017508352472262817904991134671058825705968404510,
|
||||
1003580119810278869589347418043095667699674425582646347949349245557449452503,
|
||||
619074932220101074089137133998298830285661916867732916607601635248249357793,
|
||||
],
|
||||
[
|
||||
2635090520059500019661864086615522409798872905401305311748231832709078452746,
|
||||
978252636251682252755279071140187792306115352460774007308726210405257135181,
|
||||
1766912167973123409669091967764158892111310474906691336473559256218048677083,
|
||||
1663265127259512472182980890707014969235283233442916350121860684522654120381,
|
||||
3532407621206959585000336211742670185380751515636605428496206887841428074250,
|
||||
],
|
||||
[
|
||||
2507023127157093845256722098502856938353143387711652912931112668310034975446,
|
||||
3321152907858462102434883844787153373036767230808678981306827073335525034593,
|
||||
3039253036806065280643845548147711477270022154459620569428286684179698125661,
|
||||
103480338868480851881924519768416587261556021758163719199282794248762465380,
|
||||
2394049781357087698434751577708655768465803975478348134669006211289636928495,
|
||||
],
|
||||
[
|
||||
2660531560345476340796109810821127229446538730404600368347902087220064379579,
|
||||
3603166934034556203649050570865466556260359798872408576857928196141785055563,
|
||||
1553799760191949768532188139643704561532896296986025007089826672890485412324,
|
||||
2744284717053657689091306578463476341218866418732695211367062598446038965164,
|
||||
320745764922149897598257794663594419839885234101078803811049904310835548856,
|
||||
],
|
||||
[
|
||||
979382242100682161589753881721708883681034024104145498709287731138044566302,
|
||||
1860426855810549882740147175136418997351054138609396651615467358416651354991,
|
||||
336173081054369235994909356892506146234495707857220254489443629387613956145,
|
||||
1632470326779699229772327605759783482411227247311431865655466227711078175883,
|
||||
921958250077481394074960433988881176409497663777043304881055317463712938502,
|
||||
],
|
||||
[
|
||||
3034358982193370602048539901033542101022185309652879937418114324899281842797,
|
||||
25626282149517463867572353922222474817434101087272320606729439087234878607,
|
||||
3002662261401575565838149305485737102400501329139562227180277188790091853682,
|
||||
2939684373453383817196521641512509179310654199629514917426341354023324109367,
|
||||
1076484609897998179434851570277297233169621096172424141759873688902355505136,
|
||||
],
|
||||
[
|
||||
2575095284833160494841112025725243274091830284746697961080467506739203605049,
|
||||
3565075264617591783581665711620369529657840830498005563542124551465195621851,
|
||||
2197016502533303822395077038351174326125210255869204501838837289716363437993,
|
||||
331415322883530754594261416546036195982886300052707474899691116664327869405,
|
||||
1935011233711290003793244296594669823169522055520303479680359990463281661839,
|
||||
],
|
||||
[
|
||||
3495901467168087413996941216661589517270845976538454329511167073314577412322,
|
||||
954195417117133246453562983448451025087661597543338750600301835944144520375,
|
||||
1271840477709992894995746871435810599280944810893784031132923384456797925777,
|
||||
2565310762274337662754531859505158700827688964841878141121196528015826671847,
|
||||
3365022288251637014588279139038152521653896670895105540140002607272936852513,
|
||||
],
|
||||
[
|
||||
1660592021628965529963974299647026602622092163312666588591285654477111176051,
|
||||
970104372286014048279296575474974982288801187216974504035759997141059513421,
|
||||
2617024574317953753849168721871770134225690844968986289121504184985993971227,
|
||||
999899815343607746071464113462778273556695659506865124478430189024755832262,
|
||||
2228536129413411161615629030408828764980855956560026807518714080003644769896,
|
||||
],
|
||||
[
|
||||
2701953891198001564547196795777701119629537795442025393867364730330476403227,
|
||||
837078355588159388741598313782044128527494922918203556465116291436461597853,
|
||||
2121749601840466143704862369657561429793951309962582099604848281796392359214,
|
||||
771812260179247428733132708063116523892339056677915387749121983038690154755,
|
||||
3317336423132806446086732225036532603224267214833263122557471741829060578219,
|
||||
],
|
||||
[
|
||||
481570067997721834712647566896657604857788523050900222145547508314620762046,
|
||||
242195042559343964206291740270858862066153636168162642380846129622127460192,
|
||||
2855462178889999218204481481614105202770810647859867354506557827319138379686,
|
||||
3525521107148375040131784770413887305850308357895464453970651672160034885202,
|
||||
1320839531502392535964065058804908871811967681250362364246430459003920305799,
|
||||
],
|
||||
[
|
||||
2514191518588387125173345107242226637171897291221681115249521904869763202419,
|
||||
2798335750958827619666318316247381695117827718387653874070218127140615157902,
|
||||
2808467767967035643407948058486565877867906577474361783201337540214875566395,
|
||||
3551834385992706206273955480294669176699286104229279436819137165202231595747,
|
||||
1219439673853113792340300173186247996249367102884530407862469123523013083971,
|
||||
],
|
||||
[
|
||||
761519904537984520554247997444508040636526566551719396202550009393012691157,
|
||||
3355402549169351700500518865338783382387571349497391475317206324155237401353,
|
||||
199541098009731541347317515995192175813554789571447733944970283654592727138,
|
||||
192100490643078165121235261796864975568292640203635147901612231594408079071,
|
||||
1187019357602953326192019968809486933768550466167033084944727938441427050581,
|
||||
],
|
||||
[
|
||||
189525349641911362389041124808934468936759383310282010671081989585219065700,
|
||||
2831653363992091308880573627558515686245403755586311978724025292003353336665,
|
||||
2052859812632218952608271535089179639890275494426396974475479657192657094698,
|
||||
1670756178709659908159049531058853320846231785448204274277900022176591811072,
|
||||
3538757242013734574731807289786598937548399719866320954894004830207085723125,
|
||||
],
|
||||
[
|
||||
710549042741321081781917034337800036872214466705318638023070812391485261299,
|
||||
2345013122330545298606028187653996682275206910242635100920038943391319595180,
|
||||
3528369671971445493932880023233332035122954362711876290904323783426765912206,
|
||||
1167120829038120978297497195837406760848728897181138760506162680655977700764,
|
||||
3073243357129146594530765548901087443775563058893907738967898816092270628884,
|
||||
],
|
||||
[
|
||||
378514724418106317738164464176041649567501099164061863402473942795977719726,
|
||||
333391138410406330127594722511180398159664250722328578952158227406762627796,
|
||||
1727570175639917398410201375510924114487348765559913502662122372848626931905,
|
||||
968312190621809249603425066974405725769739606059422769908547372904403793174,
|
||||
360659316299446405855194688051178331671817370423873014757323462844775818348,
|
||||
],
|
||||
[
|
||||
1386580151907705298970465943238806620109618995410132218037375811184684929291,
|
||||
3604888328937389309031638299660239238400230206645344173700074923133890528967,
|
||||
2496185632263372962152518155651824899299616724241852816983268163379540137546,
|
||||
486538168871046887467737983064272608432052269868418721234810979756540672990,
|
||||
1558415498960552213241704009433360128041672577274390114589014204605400783336,
|
||||
],
|
||||
[
|
||||
3512058327686147326577190314835092911156317204978509183234511559551181053926,
|
||||
2235429387083113882635494090887463486491842634403047716936833563914243946191,
|
||||
1290896777143878193192832813769470418518651727840187056683408155503813799882,
|
||||
1143310336918357319571079551779316654556781203013096026972411429993634080835,
|
||||
3235435208525081966062419599803346573407862428113723170955762956243193422118,
|
||||
],
|
||||
[
|
||||
1293239921425673430660897025143433077974838969258268884994339615096356996604,
|
||||
236252269127612784685426260840574970698541177557674806964960352572864382971,
|
||||
1733907592497266237374827232200506798207318263912423249709509725341212026275,
|
||||
302004309771755665128395814807589350526779835595021835389022325987048089868,
|
||||
3018926838139221755384801385583867283206879023218491758435446265703006270945,
|
||||
],
|
||||
[
|
||||
39701437664873825906031098349904330565195980985885489447836580931425171297,
|
||||
908381723021746969965674308809436059628307487140174335882627549095646509778,
|
||||
219062858908229855064136253265968615354041842047384625689776811853821594358,
|
||||
1283129863776453589317845316917890202859466483456216900835390291449830275503,
|
||||
418512623547417594896140369190919231877873410935689672661226540908900544012,
|
||||
],
|
||||
[
|
||||
1792181590047131972851015200157890246436013346535432437041535789841136268632,
|
||||
370546432987510607338044736824316856592558876687225326692366316978098770516,
|
||||
3323437805230586112013581113386626899534419826098235300155664022709435756946,
|
||||
910076621742039763058481476739499965761942516177975130656340375573185415877,
|
||||
1762188042455633427137702520675816545396284185254002959309669405982213803405,
|
||||
],
|
||||
[
|
||||
2186362253913140345102191078329764107619534641234549431429008219905315900520,
|
||||
2230647725927681765419218738218528849146504088716182944327179019215826045083,
|
||||
1069243907556644434301190076451112491469636357133398376850435321160857761825,
|
||||
2695241469149243992683268025359863087303400907336026926662328156934068747593,
|
||||
1361519681544413849831669554199151294308350560528931040264950307931824877035,
|
||||
],
|
||||
[
|
||||
1339116632207878730171031743761550901312154740800549632983325427035029084904,
|
||||
790593524918851401449292693473498591068920069246127392274811084156907468875,
|
||||
2723400368331924254840192318398326090089058735091724263333980290765736363637,
|
||||
3457180265095920471443772463283225391927927225993685928066766687141729456030,
|
||||
1483675376954327086153452545475557749815683871577400883707749788555424847954,
|
||||
],
|
||||
[
|
||||
2926303836265506736227240325795090239680154099205721426928300056982414025239,
|
||||
543969119775473768170832347411484329362572550684421616624136244239799475526,
|
||||
237401230683847084256617415614300816373730178313253487575312839074042461932,
|
||||
844568412840391587862072008674263874021460074878949862892685736454654414423,
|
||||
151922054871708336050647150237534498235916969120198637893731715254687336644,
|
||||
],
|
||||
[
|
||||
1299332034710622815055321547569101119597030148120309411086203580212105652312,
|
||||
487046922649899823989594814663418784068895385009696501386459462815688122993,
|
||||
1104883249092599185744249485896585912845784382683240114120846423960548576851,
|
||||
1458388705536282069567179348797334876446380557083422364875248475157495514484,
|
||||
850248109622750774031817200193861444623975329881731864752464222442574976566,
|
||||
],
|
||||
[
|
||||
2885843173858536690032695698009109793537724845140477446409245651176355435722,
|
||||
3027068551635372249579348422266406787688980506275086097330568993357835463816,
|
||||
3231892723647447539926175383213338123506134054432701323145045438168976970994,
|
||||
1719080830641935421242626784132692936776388194122314954558418655725251172826,
|
||||
1172253756541066126131022537343350498482225068791630219494878195815226839450,
|
||||
],
|
||||
[
|
||||
1619232269633026603732619978083169293258272967781186544174521481891163985093,
|
||||
3495680684841853175973173610562400042003100419811771341346135531754869014567,
|
||||
1576161515913099892951745452471618612307857113799539794680346855318958552758,
|
||||
2618326122974253423403350731396350223238201817594761152626832144510903048529,
|
||||
2696245132758436974032479782852265185094623165224532063951287925001108567649,
|
||||
],
|
||||
[
|
||||
930116505665110070247395429730201844026054810856263733273443066419816003444,
|
||||
2786389174502246248523918824488629229455088716707062764363111940462137404076,
|
||||
1555260846425735320214671887347115247546042526197895180675436886484523605116,
|
||||
2306241912153325247392671742757902161446877415586158295423293240351799505917,
|
||||
411529621724849932999694270803131456243889635467661223241617477462914950626,
|
||||
],
|
||||
[
|
||||
1542495485262286701469125140275904136434075186064076910329015697714211835205,
|
||||
1853045663799041100600825096887578544265580718909350942241802897995488264551,
|
||||
2963055259497271220202739837493041799968576111953080503132045092194513937286,
|
||||
2303806870349915764285872605046527036748108533406243381676768310692344456050,
|
||||
2622104986201990620910286730213140904984256464479840856728424375142929278875,
|
||||
],
|
||||
[
|
||||
2369987021925266811581727383184031736927816625797282287927222602539037105864,
|
||||
285070227712021899602056480426671736057274017903028992288878116056674401781,
|
||||
3034087076179360957800568733595959058628497428787907887933697691951454610691,
|
||||
469095854351700119980323115747590868855368701825706298740201488006320881056,
|
||||
360001976264385426746283365024817520563236378289230404095383746911725100012,
|
||||
],
|
||||
[
|
||||
3438709327109021347267562000879503009590697221730578667498351600602230296178,
|
||||
63573904800572228121671659287593650438456772568903228287754075619928214969,
|
||||
3470881855042989871434874691030920672110111605547839662680968354703074556970,
|
||||
724559311507950497340993415408274803001166693839947519425501269424891465492,
|
||||
880409284677518997550768549487344416321062350742831373397603704465823658986,
|
||||
],
|
||||
[
|
||||
6876255662475867703077362872097208259197756317287339941435193538565586230,
|
||||
2701916445133770775447884812906226786217969545216086200932273680400909154638,
|
||||
425152119158711585559310064242720816611629181537672850898056934507216982586,
|
||||
1475552998258917706756737045704649573088377604240716286977690565239187213744,
|
||||
2413772448122400684309006716414417978370152271397082147158000439863002593561,
|
||||
],
|
||||
[
|
||||
392160855822256520519339260245328807036619920858503984710539815951012864164,
|
||||
1075036996503791536261050742318169965707018400307026402939804424927087093987,
|
||||
2176439430328703902070742432016450246365760303014562857296722712989275658921,
|
||||
1413865976587623331051814207977382826721471106513581745229680113383908569693,
|
||||
4879283427490523253696177116563427032332223531862961281430108575019551814,
|
||||
],
|
||||
[
|
||||
3392583297537374046875199552977614390492290683707960975137418536812266544902,
|
||||
3600854486849487646325182927019642276644093512133907046667282144129939150983,
|
||||
2779924664161372134024229593301361846129279572186444474616319283535189797834,
|
||||
2722699960903170449291146429799738181514821447014433304730310678334403972040,
|
||||
819109815049226540285781191874507704729062681836086010078910930707209464699,
|
||||
],
|
||||
[
|
||||
3046121243742768013822760785918001632929744274211027071381357122228091333823,
|
||||
1339019590803056172509793134119156250729668216522001157582155155947567682278,
|
||||
1933279639657506214789316403763326578443023901555983256955812717638093967201,
|
||||
2138221547112520744699126051903811860205771600821672121643894708182292213541,
|
||||
2694713515543641924097704224170357995809887124438248292930846280951601597065,
|
||||
],
|
||||
[
|
||||
2471734202930133750093618989223585244499567111661178960753938272334153710615,
|
||||
504903761112092757611047718215309856203214372330635774577409639907729993533,
|
||||
1943979703748281357156510253941035712048221353507135074336243405478613241290,
|
||||
684525210957572142559049112233609445802004614280157992196913315652663518936,
|
||||
1705585400798782397786453706717059483604368413512485532079242223503960814508,
|
||||
],
|
||||
[
|
||||
192429517716023021556170942988476050278432319516032402725586427701913624665,
|
||||
1586493702243128040549584165333371192888583026298039652930372758731750166765,
|
||||
686072673323546915014972146032384917012218151266600268450347114036285993377,
|
||||
3464340397998075738891129996710075228740496767934137465519455338004332839215,
|
||||
2805249176617071054530589390406083958753103601524808155663551392362371834663,
|
||||
],
|
||||
[
|
||||
667746464250968521164727418691487653339733392025160477655836902744186489526,
|
||||
1131527712905109997177270289411406385352032457456054589588342450404257139778,
|
||||
1908969485750011212309284349900149072003218505891252313183123635318886241171,
|
||||
1025257076985551890132050019084873267454083056307650830147063480409707787695,
|
||||
2153175291918371429502545470578981828372846236838301412119329786849737957977,
|
||||
],
|
||||
[
|
||||
3410257749736714576487217882785226905621212230027780855361670645857085424384,
|
||||
3442969106887588154491488961893254739289120695377621434680934888062399029952,
|
||||
3029953900235731770255937704976720759948880815387104275525268727341390470237,
|
||||
85453456084781138713939104192561924536933417707871501802199311333127894466,
|
||||
2730629666577257820220329078741301754580009106438115341296453318350676425129,
|
||||
],
|
||||
[
|
||||
178242450661072967256438102630920745430303027840919213764087927763335940415,
|
||||
2844589222514708695700541363167856718216388819406388706818431442998498677557,
|
||||
3547876269219141094308889387292091231377253967587961309624916269569559952944,
|
||||
2525005406762984211707203144785482908331876505006839217175334833739957826850,
|
||||
3096397013555211396701910432830904669391580557191845136003938801598654871345,
|
||||
],
|
||||
[
|
||||
574424067119200181933992948252007230348512600107123873197603373898923821490,
|
||||
1714030696055067278349157346067719307863507310709155690164546226450579547098,
|
||||
2339895272202694698739231405357972261413383527237194045718815176814132612501,
|
||||
3562501318971895161271663840954705079797767042115717360959659475564651685069,
|
||||
69069358687197963617161747606993436483967992689488259107924379545671193749,
|
||||
],
|
||||
[
|
||||
2614502738369008850475068874731531583863538486212691941619835266611116051561,
|
||||
655247349763023251625727726218660142895322325659927266813592114640858573566,
|
||||
2305235672527595714255517865498269719545193172975330668070873705108690670678,
|
||||
926416070297755413261159098243058134401665060349723804040714357642180531931,
|
||||
866523735635840246543516964237513287099659681479228450791071595433217821460,
|
||||
],
|
||||
[
|
||||
2284334068466681424919271582037156124891004191915573957556691163266198707693,
|
||||
1812588309302477291425732810913354633465435706480768615104211305579383928792,
|
||||
2836899808619013605432050476764608707770404125005720004551836441247917488507,
|
||||
2989087789022865112405242078196235025698647423649950459911546051695688370523,
|
||||
68056284404189102136488263779598243992465747932368669388126367131855404486,
|
||||
],
|
||||
[
|
||||
505425339250887519581119854377342241317528319745596963584548343662758204398,
|
||||
2118963546856545068961709089296976921067035227488975882615462246481055679215,
|
||||
2253872596319969096156004495313034590996995209785432485705134570745135149681,
|
||||
1625090409149943603241183848936692198923183279116014478406452426158572703264,
|
||||
179139838844452470348634657368199622305888473747024389514258107503778442495,
|
||||
],
|
||||
[
|
||||
1567067018147735642071130442904093290030432522257811793540290101391210410341,
|
||||
2737301854006865242314806979738760349397411136469975337509958305470398783585,
|
||||
3002738216460904473515791428798860225499078134627026021350799206894618186256,
|
||||
374029488099466837453096950537275565120689146401077127482884887409712315162,
|
||||
973403256517481077805460710540468856199855789930951602150773500862180885363,
|
||||
],
|
||||
[
|
||||
2691967457038172130555117632010860984519926022632800605713473799739632878867,
|
||||
3515906794910381201365530594248181418811879320679684239326734893975752012109,
|
||||
148057579455448384062325089530558091463206199724854022070244924642222283388,
|
||||
1541588700238272710315890873051237741033408846596322948443180470429851502842,
|
||||
147013865879011936545137344076637170977925826031496203944786839068852795297,
|
||||
],
|
||||
[
|
||||
2630278389304735265620281704608245039972003761509102213752997636382302839857,
|
||||
1359048670759642844930007747955701205155822111403150159614453244477853867621,
|
||||
2438984569205812336319229336885480537793786558293523767186829418969842616677,
|
||||
2137792255841525507649318539501906353254503076308308692873313199435029594138,
|
||||
2262318076430740712267739371170174514379142884859595360065535117601097652755,
|
||||
],
|
||||
[
|
||||
2792703718581084537295613508201818489836796608902614779596544185252826291584,
|
||||
2294173715793292812015960640392421991604150133581218254866878921346561546149,
|
||||
2770011224727997178743274791849308200493823127651418989170761007078565678171,
|
||||
3321642244537785916275181932172303118112488081726311374164578600576901819844,
|
||||
3522708517589950573320671158134804505970724681591943826922697952040487655044,
|
||||
],
|
||||
[
|
||||
3417974441436557992524691506735790206623600049454586729879955931972546347402,
|
||||
175039333145381316571259690443853067809784261609912638686739799135919647022,
|
||||
1930713062131033273316869231148248962041053029742760224583505092759642967464,
|
||||
2971452932574554603554350185069538580257636405419430340233233400633251319042,
|
||||
2774781903758215341037075610938953949868289195845367046186588750871862784919,
|
||||
],
|
||||
[
|
||||
666516874137869653699423537799457099346460194092311952417454613224504932738,
|
||||
1900462225013533249140457703727169176351786259991305560412832202759625668041,
|
||||
2665631186082687279121709429531834469477679375137509769347092380798929714377,
|
||||
837840745988147279235494664091280091563355097569199320366973125128366540061,
|
||||
3391544118305848781823721719916289805455110924839794510205940718821197620955,
|
||||
],
|
||||
[
|
||||
2888553035909938253628892138500390690221493345071933642853222968481016605919,
|
||||
3386241569867597612447901482685846444743718781330869478721963580925825915450,
|
||||
1205126220630896984850042596877918177217334376800874965105642874206963597698,
|
||||
3590072615491710252422997155203204584659171612188004116415640739580250394190,
|
||||
692469013329617220154003334549812915100479873898898958632988703738125356983,
|
||||
],
|
||||
[
|
||||
1623178235190707102808841905143937367808788834203621005714003335195182126335,
|
||||
1972826180775011489122426045504602288576507493792470102803637471568052321297,
|
||||
3415141329098504418158191749675997877417539760075593313736376750580696083073,
|
||||
587811537889727046473915684463981273175495137461951211739526104349163747811,
|
||||
2523982964351069134084525951849317400231659428055762640605248929856135518199,
|
||||
],
|
||||
[
|
||||
2686176526711834950207666047281383173339057216783586039351834948812568447629,
|
||||
983144446441739425577690449774542566745526459152966545642451764143532586964,
|
||||
171558252019175695567663688494555626159399786667979998273792882504784080805,
|
||||
332337623010057542760158225837623039780806442976079546879646069338600179518,
|
||||
1264669683963885571544813806669118319675288608634733888843804451222546848295,
|
||||
],
|
||||
[
|
||||
2426165115815723668018318268486497504249785449504758403912155206515511627681,
|
||||
11387399609384288947733630450855186629703576293221897150193655994854764608,
|
||||
2541728569046079092074077754414781968906176513081761690404588216304985421091,
|
||||
47685947554980329431290582269851186106577733250761848107645535357326439312,
|
||||
472176388418187405374813530639596064799362505024895746833751173199773896628,
|
||||
],
|
||||
[
|
||||
2764298116617383397920343358525617195195060562266243809245480210157942112738,
|
||||
486863835068754002670800862273365477867695879270721744227071001883208334054,
|
||||
2973492686137102577527656941792991264994301121122130295965761350095846874635,
|
||||
178385615141132702906181473263873416748818415607305319148067639744074654009,
|
||||
533624640096756667052211553746016402543259206286603356120804827761339634127,
|
||||
],
|
||||
[
|
||||
819406716720171922688026098737835227857400444543748198788964759773510472096,
|
||||
531851793767260921861217458033110066464894334064526987603936107947006031387,
|
||||
3269709072483585277009748181134917746036523619604017812342933951952104134829,
|
||||
838191718603413598040249006803464503100808192944407407147899973659013630611,
|
||||
1574561296941310904780257598780779812250055948216417844567262310524022037406,
|
||||
],
|
||||
[
|
||||
551394354289003977607664358739006072556227894953233419144430578080352094737,
|
||||
445076790942318675726839050057337819004979443030540904213920669247413907302,
|
||||
1963946696292687224902912968478695543164747600779913024040878700455222386521,
|
||||
484284614181963381509745298932402076252103342403432879800905151752488144767,
|
||||
2240507606126946994415203252302826782042951346966859379502140796364876543253,
|
||||
],
|
||||
[
|
||||
3237135638753992982179886898758938279897590886053928839613434762582576319619,
|
||||
2334333034701915027889533058426879447140084891006486138782876488162658230991,
|
||||
14411091399844539897439754491034751977136685514851444574462584316609631592,
|
||||
1264480371592407258420308876448697804787923638319277097663041109464608464284,
|
||||
671929312763821646360589403212798993954209530574443543917757335777610372144,
|
||||
],
|
||||
[
|
||||
2513909805455654095962542944994577107405216428214873444765576238504714067396,
|
||||
870121102846043786263357605823753628974859886859187558617096145653709171231,
|
||||
399132620893316356411986266679786708905730068946836982293484206366500277754,
|
||||
2855046250836680633532995284655778407402587437073106249445470889390454667586,
|
||||
2063679741125384345396981490971605710211281905716315529671473143278849561151,
|
||||
],
|
||||
[
|
||||
1433753212258929925682201698758056443128516570551146995210728194816988328337,
|
||||
3334984763425011856632257855270507440816274246647423607159847074739331865077,
|
||||
337911293622078184850923533628334646725451591671907148383867096651211846605,
|
||||
559587005295238702015018022040357402231957131094636365177008701077975941644,
|
||||
885963059604819264377490633589388189646118257469490919900554134369512794660,
|
||||
],
|
||||
[
|
||||
1957748763518471091057032383332840331641373304981058387824598000170709016333,
|
||||
3175295982155056798972302481564899381103533409383494814704562889625572018450,
|
||||
498987160612401618114584726510347771865331516606886613019084323862447372555,
|
||||
947374835104260364630171441676101001841507588423166778786886198914150312958,
|
||||
906933977754491302438795274167251538820934378773708095543613756654712689280,
|
||||
],
|
||||
[
|
||||
2170116291766863179909957030577284618726490893598499117272497866180009722894,
|
||||
1801335399574515889082584621772588704763181408217893911806726119813067220453,
|
||||
1942500232535842474530840356353427989892065499159260166135596750084681859966,
|
||||
62936080219825306823124060587235998278756755377419521154040408253893795176,
|
||||
3091993939935137795359769774909373279950941171574748645375255810204590357753,
|
||||
],
|
||||
[
|
||||
1283528386884634267663661033944552098742115012555712906773586466375284501324,
|
||||
1581820717639229420476069802992937438655873471854930764425841549067913106065,
|
||||
2301986095388751633126546121528329200085681648876910655269533407603441046514,
|
||||
2850003828037698751961753862613545302539465803982364898225617297398939302949,
|
||||
48024691078494936445046366770271288984930221238071705874025261821606393528,
|
||||
],
|
||||
[
|
||||
1482336297033144958942154923925185950152551534403871620222916667536030875354,
|
||||
3081177564717719643771186007689458633949181485535169123213511264603782950049,
|
||||
3315701127039521853279746297714590495201061397709680410650043502532250578075,
|
||||
3514407611000441301995070394422463400067690470546731164089622325748803106020,
|
||||
368970178199930154322724953487299516224498421233447528815195701420122548537,
|
||||
],
|
||||
[
|
||||
584353160413525267849669053228533951552602295860601556035386665117717227391,
|
||||
752038702160385294706011538400822066722189014251268673051846350397729870418,
|
||||
3594041683498798688197194521326299097635429790757880308152971477196489335154,
|
||||
1367902435756906062215608264424138718742854099315395230911274560900857414183,
|
||||
1828549068951502746189364466794037234789986878381694857475972053743463890779,
|
||||
],
|
||||
[
|
||||
488172495141237210878388657234137733008417573114482400652274985829148564248,
|
||||
962906242461930394022372340919543491337923491322497419797555620396501785566,
|
||||
2275418085010046236619290386129138234541669589549771944697082317642065048898,
|
||||
1966395064658902622886154686288219600816893261614483533899715888994623208964,
|
||||
3496095878293416917311185659829821476802828534554531050412634978086916288609,
|
||||
],
|
||||
[
|
||||
3368478822390537245916137403277928093536087427951052230723275731232142463388,
|
||||
3397410259276620127103231993277518800970669191016277541098821699302368873803,
|
||||
2662600899665871010006649609856695263727220473364611552472965243032255906029,
|
||||
2854831720595596992200155718152374313555878203864206470581502555480894633975,
|
||||
2417859092561967752135741161218626374900182454089059862468108240576782064037,
|
||||
],
|
||||
[
|
||||
1064506915903089299531724594973601253341866933071158266140674053459433520889,
|
||||
243845138053687262800349059300355289745206315347524675450796070948867090098,
|
||||
1952653154963756062322124110012629666160000286707762177032475477295929736283,
|
||||
2760979128531476595658428672038276216079708408852493051222686009638650156041,
|
||||
3341178930260137001230946104398194306290005446746057811731360203227371301716,
|
||||
],
|
||||
[
|
||||
1033242545866274439991875444609632860132556714736615395036273942261573810479,
|
||||
3567973410830779135148598005871071456943945697865168835204985462698751038238,
|
||||
23014034649293369426970379738102323014738017168969687350330825050016457105,
|
||||
1146720508452451012445869043641390200263192255569203352823376998708972325392,
|
||||
2553707028642376593497768606567528232999203496079990242456254686325586089356,
|
||||
],
|
||||
[
|
||||
269729857648436699208023125596593246149228245518586029792966091405383426269,
|
||||
276912682886955358118649215147238115764108757952690361549816619060658800027,
|
||||
2367180947887796341722261610916728725977893583923967218630363334645641817362,
|
||||
2398694802751362950028137620758033447242325333923222365760836442417755445092,
|
||||
984868389243025029364428136317275892280780834039611841422502834917752411391,
|
||||
],
|
||||
[
|
||||
861353329558771468244040268521983016756775808329676883407171471251365927595,
|
||||
2498672969617384807617108262141800974986393948110233099680635130601163654234,
|
||||
1336236634145657673540555267430353130305889434115514586892320600753700983325,
|
||||
980337801407886250576371882962628290239239581416378379141354256717803603922,
|
||||
2308558359523317875952657835109605515063994805873180719205156915762120497245,
|
||||
],
|
||||
[
|
||||
2116737905426837141304542819940293184404010538896700217242374222514653607487,
|
||||
2143995283326808680518644927890182524580312777400009071739277407358043120199,
|
||||
3038758768133404431511594054950351369492648883179154555267474054094234927849,
|
||||
981824005865625678985009911415023115269386212492064371040001594972137748141,
|
||||
2427990511715778580869565219059895697855813782250850855111162965998948386792,
|
||||
],
|
||||
[
|
||||
1987498156785173719076522405088076990979859292718600184358583152317049836167,
|
||||
1633834915134208237423144264187482951766302060112099587851513525797020813799,
|
||||
2895454976388515752029424688351979030650325184941524820409482023485820781526,
|
||||
941019661238578826272324221721825852217063629464317974190162904813488515671,
|
||||
2529926057929249454763690180607677568685011502604470585585763159431333258299,
|
||||
],
|
||||
[
|
||||
2604831509257756199338105380847564711923112853239827243306562341166492672823,
|
||||
2300475954087415591738767759767032267163723345312082546282694920273655145455,
|
||||
1954000528502201000509342111010021527425422549437946241062907964768089317082,
|
||||
1179936151696782249912570883839105595634344582873818018332922940963046083567,
|
||||
3077707030301573630126144767923697288658782137457660869231140049571827937228,
|
||||
],
|
||||
[
|
||||
1062324397142900251844488719868780667589966366756786302007970554437994421840,
|
||||
353718609497993885193404630053532608155520921625518104461520254335222009911,
|
||||
770557645309607171206012551080400276506165720184677119001983749356594531977,
|
||||
3043628430985247363392058521341757139056029350680498644930013342982472853636,
|
||||
1694968537785457252742656255724723357998402478572600479401200420305593921487,
|
||||
],
|
||||
[
|
||||
539865665379093791531434211889371819368504193082947002067781562776138072582,
|
||||
3473466148775696692731190426971123680342615414200262605154732883324298196699,
|
||||
482783534456196983135936103604928650836406142744767857356485953118411089098,
|
||||
2389101033971236780034779577432189630800997581132154923233144722790749715251,
|
||||
845264223568475649981141803833883014312596504303895519674002924871878791033,
|
||||
],
|
||||
[
|
||||
3027004059915270231142566724881373969831662022738947178800901294120992473905,
|
||||
2169574859350740480088697859610203373582027214052754592019828328614087431593,
|
||||
3515527080764222354309565181793838292349410992793070639041305826153436624160,
|
||||
1817926918350512904327755405973355211358017834277255662858654992240629698587,
|
||||
1999148133619270973098477176176178514394558202995832714883251820350860287223,
|
||||
],
|
||||
[
|
||||
1203131300029280096510929599113528018338088236684405405384757591977164161039,
|
||||
336815403657101171302040383579077521911288747438919304948637997306314852594,
|
||||
986661060847815533035934253464295060766339947679669645818832311132001095573,
|
||||
2291116974939980228917916563988261327966840303336559854772343651559589512651,
|
||||
3421243089992476528970346847858594146122972226790673723411896208702859892637,
|
||||
],
|
||||
[
|
||||
1015505198663386486420800821559060487156096175034250154764824837183581949724,
|
||||
1165880582987807286271819576391581724550686829511475839624601920297855380101,
|
||||
904232961143172831178860280790910264843503022179578981166030973682571903458,
|
||||
261322216292849827900157598748641385787016033372999683866859675894253115357,
|
||||
3060676319159217735181388708455879854358158161989877552543698103915296690395,
|
||||
],
|
||||
[
|
||||
1175560144527845912984609340783959238735643215413930887771084560168082442967,
|
||||
2813871258576082360085006002528268796351819524936446195552260262614692343332,
|
||||
1841341101531851399935829271555098629075809587212843292354556374386667658235,
|
||||
3076135575511709688509914361447080149794919016880133063891720256749999834767,
|
||||
753111801049754117414662684453226478940731922961768343984187479992842213733,
|
||||
],
|
||||
[
|
||||
1405657437118503342762241742745888533114216548278983907019917904938403345580,
|
||||
3111186124713876864436867307979940633543281080828379725576742174555539054855,
|
||||
3404463650394703220454952017098727360005393139199301323890695570346564876407,
|
||||
2024087816190101179456573591359233695334184711688920998987373624570170649371,
|
||||
2770035625774572095496575568588054654502991645588385802705097377675051032967,
|
||||
],
|
||||
[
|
||||
437058215235292632621847481185406671372191763951486300610124033096831557414,
|
||||
1345792773780982398809956395232061067669190682958320579442454533085407626029,
|
||||
925357273912625669941681596445839316566672314870287993638671283923476231904,
|
||||
3288133122086768300615066039539687885053110015077924175836976549020438910830,
|
||||
666190075990703867784232802074474372379358766701681865975596503982238839889,
|
||||
],
|
||||
[
|
||||
2664898601165892062970298960258838238925231697327906221693001926762280012052,
|
||||
2075648691532387787722427044464731934171216054855867223374228487601569118337,
|
||||
3173725544188532489243684991828985285646224157242834030308807120745121062293,
|
||||
1517474443612606408422643323550409253700128234157734252330869178582583531320,
|
||||
1593950878945144789965609248470060076911813704207225832606804796819386297511,
|
||||
],
|
||||
[
|
||||
141195541167651298813588829225208004611326987855926870823948793274702167509,
|
||||
2990187949585642302497822222637786229364740008175968941859105979392907839776,
|
||||
2893807105405820282316438050347503569385510241526138409321358916388308586443,
|
||||
1379719211597875648759619903854862028510320482486109668868067715175935658353,
|
||||
2702780364788282233075255946852944970202849869091427738791947810055591218061,
|
||||
],
|
||||
[
|
||||
1825815734419326277729273926504439575157952821379179501821641713286627304656,
|
||||
1481344458867016048625916723816339719872443766684158199301690902395849166360,
|
||||
2014084774259125722186109781197998076881266739680534358898592778318128968629,
|
||||
2612744185006548312909661512508122065214170543806989291921289897662387203493,
|
||||
2486291022451231582267428921150634472835925206862678364689227838329114330247,
|
||||
],
|
||||
]
|
||||
1031
test/stark/poseidon/poseidon9.txt
Normal file
1031
test/stark/poseidon/poseidon9.txt
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,49 +1,51 @@
|
||||
import { deepStrictEqual, throws } from 'assert';
|
||||
import { should } from 'micro-should';
|
||||
import { describe, should } from 'micro-should';
|
||||
import * as starknet from '../../lib/esm/stark.js';
|
||||
import * as fc from 'fast-check';
|
||||
|
||||
const FC_BIGINT = fc.bigInt(1n + 1n, starknet.CURVE.n - 1n);
|
||||
|
||||
should('Point#toHex() roundtrip', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (x) => {
|
||||
const point1 = starknet.Point.fromPrivateKey(x);
|
||||
const hex = point1.toHex(true);
|
||||
deepStrictEqual(starknet.Point.fromHex(hex).toHex(true), hex);
|
||||
})
|
||||
describe('starknet property', () => {
|
||||
should('Point#toHex() roundtrip', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (x) => {
|
||||
const point1 = starknet.ProjectivePoint.fromPrivateKey(x);
|
||||
const hex = point1.toHex(true);
|
||||
deepStrictEqual(starknet.ProjectivePoint.fromHex(hex).toHex(true), hex);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
should('Signature.fromCompactHex() roundtrip', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, (r, s) => {
|
||||
const sig = new starknet.Signature(r, s);
|
||||
deepStrictEqual(starknet.Signature.fromCompact(sig.toCompactHex()), sig);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
should('Signature.fromDERHex() roundtrip', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, (r, s) => {
|
||||
const sig = new starknet.Signature(r, s);
|
||||
deepStrictEqual(starknet.Signature.fromDER(sig.toDERHex()), sig);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
should('verify()/should verify random signatures', () =>
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, fc.hexaString({ minLength: 64, maxLength: 64 }), (privNum, msg) => {
|
||||
const privKey = privNum.toString(16).padStart(64, '0');
|
||||
const pub = starknet.getPublicKey(privKey);
|
||||
const sig = starknet.sign(msg, privKey);
|
||||
deepStrictEqual(starknet.verify(sig, msg, pub), true);
|
||||
})
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
should('Signature.fromCompactHex() roundtrip', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, (r, s) => {
|
||||
const sig = new starknet.Signature(r, s);
|
||||
deepStrictEqual(starknet.Signature.fromCompact(sig.toCompactHex()), sig);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
should('Signature.fromDERHex() roundtrip', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, (r, s) => {
|
||||
const sig = new starknet.Signature(r, s);
|
||||
deepStrictEqual(starknet.Signature.fromDER(sig.toDERHex()), sig);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
should('verify()/should verify random signatures', () =>
|
||||
fc.assert(
|
||||
fc.asyncProperty(FC_BIGINT, fc.hexaString({ minLength: 64, maxLength: 64 }), (privNum, msg) => {
|
||||
const privKey = privNum.toString(16).padStart(64, '0');
|
||||
const pub = starknet.getPublicKey(privKey);
|
||||
const sig = starknet.sign(msg, privKey);
|
||||
deepStrictEqual(starknet.verify(sig, msg, pub), true);
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
// ESM is broken.
|
||||
import url from 'url';
|
||||
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { deepStrictEqual, throws } from 'assert';
|
||||
import { should } from 'micro-should';
|
||||
import { describe, should } from 'micro-should';
|
||||
import { hex, utf8 } from '@scure/base';
|
||||
import * as bip32 from '@scure/bip32';
|
||||
import * as bip39 from '@scure/bip39';
|
||||
@@ -7,277 +7,279 @@ import * as starknet from '../../lib/esm/stark.js';
|
||||
import { default as sigVec } from './fixtures/rfc6979_signature_test_vector.json' assert { type: 'json' };
|
||||
import { default as precomputedKeys } from './fixtures/keys_precomputed.json' assert { type: 'json' };
|
||||
|
||||
should('Starknet keccak', () => {
|
||||
const value = starknet.keccak(utf8.decode('hello'));
|
||||
deepStrictEqual(value, 0x8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8n);
|
||||
deepStrictEqual(value < 2n ** 250n, true);
|
||||
});
|
||||
describe('starknet', () => {
|
||||
should('custom keccak', () => {
|
||||
const value = starknet.keccak(utf8.decode('hello'));
|
||||
deepStrictEqual(value, 0x8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8n);
|
||||
deepStrictEqual(value < 2n ** 250n, true);
|
||||
});
|
||||
|
||||
should('RFC6979', () => {
|
||||
for (const msg of sigVec.messages) {
|
||||
const { r, s } = starknet.sign(msg.hash, sigVec.private_key);
|
||||
// const { r, s } = starknet.Signature.fromDER(sig);
|
||||
deepStrictEqual(r.toString(10), msg.r);
|
||||
deepStrictEqual(s.toString(10), msg.s);
|
||||
}
|
||||
});
|
||||
should('RFC6979', () => {
|
||||
for (const msg of sigVec.messages) {
|
||||
const { r, s } = starknet.sign(msg.hash, sigVec.private_key);
|
||||
// const { r, s } = starknet.Signature.fromDER(sig);
|
||||
deepStrictEqual(r.toString(10), msg.r);
|
||||
deepStrictEqual(s.toString(10), msg.s);
|
||||
}
|
||||
});
|
||||
|
||||
should('Signatures', () => {
|
||||
const vectors = [
|
||||
{
|
||||
// Message hash of length 61.
|
||||
msg: 'c465dd6b1bbffdb05442eb17f5ca38ad1aa78a6f56bf4415bdee219114a47',
|
||||
r: '5f496f6f210b5810b2711c74c15c05244dad43d18ecbbdbe6ed55584bc3b0a2',
|
||||
s: '4e8657b153787f741a67c0666bad6426c3741b478c8eaa3155196fc571416f3',
|
||||
},
|
||||
{
|
||||
// Message hash of length 61, with leading zeros.
|
||||
msg: '00c465dd6b1bbffdb05442eb17f5ca38ad1aa78a6f56bf4415bdee219114a47',
|
||||
r: '5f496f6f210b5810b2711c74c15c05244dad43d18ecbbdbe6ed55584bc3b0a2',
|
||||
s: '4e8657b153787f741a67c0666bad6426c3741b478c8eaa3155196fc571416f3',
|
||||
},
|
||||
{
|
||||
// Message hash of length 62.
|
||||
msg: 'c465dd6b1bbffdb05442eb17f5ca38ad1aa78a6f56bf4415bdee219114a47a',
|
||||
r: '233b88c4578f0807b4a7480c8076eca5cfefa29980dd8e2af3c46a253490e9c',
|
||||
s: '28b055e825bc507349edfb944740a35c6f22d377443c34742c04e0d82278cf1',
|
||||
},
|
||||
{
|
||||
// Message hash of length 63.
|
||||
msg: '7465dd6b1bbffdb05442eb17f5ca38ad1aa78a6f56bf4415bdee219114a47a1',
|
||||
r: 'b6bee8010f96a723f6de06b5fa06e820418712439c93850dd4e9bde43ddf',
|
||||
s: '1a3d2bc954ed77e22986f507d68d18115fa543d1901f5b4620db98e2f6efd80',
|
||||
},
|
||||
];
|
||||
const privateKey = '2dccce1da22003777062ee0870e9881b460a8b7eca276870f57c601f182136c';
|
||||
const publicKey = starknet.getPublicKey(privateKey);
|
||||
for (const v of vectors) {
|
||||
const sig = starknet.sign(v.msg, privateKey);
|
||||
should('Signatures', () => {
|
||||
const vectors = [
|
||||
{
|
||||
// Message hash of length 61.
|
||||
msg: 'c465dd6b1bbffdb05442eb17f5ca38ad1aa78a6f56bf4415bdee219114a47',
|
||||
r: '5f496f6f210b5810b2711c74c15c05244dad43d18ecbbdbe6ed55584bc3b0a2',
|
||||
s: '4e8657b153787f741a67c0666bad6426c3741b478c8eaa3155196fc571416f3',
|
||||
},
|
||||
{
|
||||
// Message hash of length 61, with leading zeros.
|
||||
msg: '00c465dd6b1bbffdb05442eb17f5ca38ad1aa78a6f56bf4415bdee219114a47',
|
||||
r: '5f496f6f210b5810b2711c74c15c05244dad43d18ecbbdbe6ed55584bc3b0a2',
|
||||
s: '4e8657b153787f741a67c0666bad6426c3741b478c8eaa3155196fc571416f3',
|
||||
},
|
||||
{
|
||||
// Message hash of length 62.
|
||||
msg: 'c465dd6b1bbffdb05442eb17f5ca38ad1aa78a6f56bf4415bdee219114a47a',
|
||||
r: '233b88c4578f0807b4a7480c8076eca5cfefa29980dd8e2af3c46a253490e9c',
|
||||
s: '28b055e825bc507349edfb944740a35c6f22d377443c34742c04e0d82278cf1',
|
||||
},
|
||||
{
|
||||
// Message hash of length 63.
|
||||
msg: '7465dd6b1bbffdb05442eb17f5ca38ad1aa78a6f56bf4415bdee219114a47a1',
|
||||
r: 'b6bee8010f96a723f6de06b5fa06e820418712439c93850dd4e9bde43ddf',
|
||||
s: '1a3d2bc954ed77e22986f507d68d18115fa543d1901f5b4620db98e2f6efd80',
|
||||
},
|
||||
];
|
||||
const privateKey = '2dccce1da22003777062ee0870e9881b460a8b7eca276870f57c601f182136c';
|
||||
const publicKey = starknet.getPublicKey(privateKey);
|
||||
for (const v of vectors) {
|
||||
const sig = starknet.sign(v.msg, privateKey);
|
||||
const { r, s } = sig;
|
||||
// const { r, s } = starknet.Signature.fromDER(sig);
|
||||
deepStrictEqual(r.toString(16), v.r, 'r equality');
|
||||
deepStrictEqual(s.toString(16), v.s, 's equality');
|
||||
deepStrictEqual(starknet.verify(sig, v.msg, publicKey), true, 'verify');
|
||||
}
|
||||
});
|
||||
|
||||
should('Invalid signatures', () => {
|
||||
/*
|
||||
|
||||
it('should not verify invalid signature inputs lengths', () => {
|
||||
const ecOrder = starkwareCrypto.ec.n;
|
||||
const {maxEcdsaVal} = starkwareCrypto;
|
||||
const maxMsgHash = maxEcdsaVal.sub(oneBn);
|
||||
const maxR = maxEcdsaVal.sub(oneBn);
|
||||
const maxS = ecOrder.sub(oneBn).sub(oneBn);
|
||||
const maxStarkKey = maxEcdsaVal.sub(oneBn);
|
||||
|
||||
// Test invalid message length.
|
||||
expect(() =>
|
||||
starkwareCrypto.verify(maxStarkKey, maxMsgHash.add(oneBn).toString(16), {
|
||||
r: maxR,
|
||||
s: maxS
|
||||
})
|
||||
).to.throw('Message not signable, invalid msgHash length.');
|
||||
// Test invalid r length.
|
||||
expect(() =>
|
||||
starkwareCrypto.verify(maxStarkKey, maxMsgHash.toString(16), {
|
||||
r: maxR.add(oneBn),
|
||||
s: maxS
|
||||
})
|
||||
).to.throw('Message not signable, invalid r length.');
|
||||
// Test invalid w length.
|
||||
expect(() =>
|
||||
starkwareCrypto.verify(maxStarkKey, maxMsgHash.toString(16), {
|
||||
r: maxR,
|
||||
s: maxS.add(oneBn)
|
||||
})
|
||||
).to.throw('Message not signable, invalid w length.');
|
||||
// Test invalid s length.
|
||||
expect(() =>
|
||||
starkwareCrypto.verify(maxStarkKey, maxMsgHash.toString(16), {
|
||||
r: maxR,
|
||||
s: maxS.add(oneBn).add(oneBn)
|
||||
})
|
||||
).to.throw('Message not signable, invalid s length.');
|
||||
});
|
||||
|
||||
it('should not verify invalid signatures', () => {
|
||||
const privKey = generateRandomStarkPrivateKey();
|
||||
const keyPair = starkwareCrypto.ec.keyFromPrivate(privKey, 'hex');
|
||||
const keyPairPub = starkwareCrypto.ec.keyFromPublic(
|
||||
keyPair.getPublic(),
|
||||
'BN'
|
||||
);
|
||||
const msgHash = new BN(randomHexString(61));
|
||||
const msgSignature = starkwareCrypto.sign(keyPair, msgHash);
|
||||
|
||||
// Test invalid public key.
|
||||
const invalidKeyPairPub = starkwareCrypto.ec.keyFromPublic(
|
||||
{x: keyPairPub.pub.getX().add(oneBn), y: keyPairPub.pub.getY()},
|
||||
'BN'
|
||||
);
|
||||
expect(
|
||||
starkwareCrypto.verify(
|
||||
invalidKeyPairPub,
|
||||
msgHash.toString(16),
|
||||
msgSignature
|
||||
)
|
||||
).to.be.false;
|
||||
// Test invalid message.
|
||||
expect(
|
||||
starkwareCrypto.verify(
|
||||
keyPair,
|
||||
msgHash.add(oneBn).toString(16),
|
||||
msgSignature
|
||||
)
|
||||
).to.be.false;
|
||||
expect(
|
||||
starkwareCrypto.verify(
|
||||
keyPairPub,
|
||||
msgHash.add(oneBn).toString(16),
|
||||
msgSignature
|
||||
)
|
||||
).to.be.false;
|
||||
// Test invalid r.
|
||||
msgSignature.r.iadd(oneBn);
|
||||
expect(starkwareCrypto.verify(keyPair, msgHash.toString(16), msgSignature))
|
||||
.to.be.false;
|
||||
expect(
|
||||
starkwareCrypto.verify(keyPairPub, msgHash.toString(16), msgSignature)
|
||||
).to.be.false;
|
||||
// Test invalid s.
|
||||
msgSignature.r.isub(oneBn);
|
||||
msgSignature.s.iadd(oneBn);
|
||||
expect(starkwareCrypto.verify(keyPair, msgHash.toString(16), msgSignature))
|
||||
.to.be.false;
|
||||
expect(
|
||||
starkwareCrypto.verify(keyPairPub, msgHash.toString(16), msgSignature)
|
||||
).to.be.false;
|
||||
});
|
||||
});
|
||||
*/
|
||||
});
|
||||
|
||||
should('Pedersen', () => {
|
||||
deepStrictEqual(
|
||||
starknet.pedersen(
|
||||
'0x3d937c035c878245caf64531a5756109c53068da139362728feb561405371cb',
|
||||
'0x208a0a10250e382e1e4bbe2880906c2791bf6275695e02fbbc6aeff9cd8b31a'
|
||||
),
|
||||
'0x30e480bed5fe53fa909cc0f8c4d99b8f9f2c016be4c41e13a4848797979c662'
|
||||
);
|
||||
deepStrictEqual(
|
||||
starknet.pedersen(
|
||||
'0x58f580910a6ca59b28927c08fe6c43e2e303ca384badc365795fc645d479d45',
|
||||
'0x78734f65a067be9bdb39de18434d71e79f7b6466a4b66bbd979ab9e7515fe0b'
|
||||
),
|
||||
'0x68cc0b76cddd1dd4ed2301ada9b7c872b23875d5ff837b3a87993e0d9996b87'
|
||||
);
|
||||
});
|
||||
|
||||
should('Hash chain', () => {
|
||||
deepStrictEqual(starknet.hashChain([1, 2, 3]), starknet.pedersen(1, starknet.pedersen(2, 3)));
|
||||
});
|
||||
|
||||
should('Key grinding', () => {
|
||||
deepStrictEqual(
|
||||
starknet.grindKey('86F3E7293141F20A8BAFF320E8EE4ACCB9D4A4BF2B4D295E8CEE784DB46E0519'),
|
||||
'5c8c8683596c732541a59e03007b2d30dbbbb873556fe65b5fb63c16688f941'
|
||||
);
|
||||
// Loops more than once (verified manually)
|
||||
deepStrictEqual(
|
||||
starknet.grindKey('94F3E7293141F20A8BAFF320E8EE4ACCB9D4A4BF2B4D295E8CEE784DB46E0595'),
|
||||
'33880b9aba464c1c01c9f8f5b4fc1134698f9b0a8d18505cab6cdd34d93dc02'
|
||||
);
|
||||
});
|
||||
|
||||
should('Private to stark key', () => {
|
||||
deepStrictEqual(
|
||||
starknet.getStarkKey('0x178047D3869489C055D7EA54C014FFB834A069C9595186ABE04EA4D1223A03F'),
|
||||
'0x1895a6a77ae14e7987b9cb51329a5adfb17bd8e7c638f92d6892d76e51cebcf'
|
||||
);
|
||||
for (const [privKey, expectedPubKey] of Object.entries(precomputedKeys)) {
|
||||
deepStrictEqual(starknet.getStarkKey(privKey), expectedPubKey);
|
||||
}
|
||||
});
|
||||
|
||||
should('Private stark key from eth signature', () => {
|
||||
const ethSignature =
|
||||
'0x21fbf0696d5e0aa2ef41a2b4ffb623bcaf070461d61cf7251c74161f82fec3a43' +
|
||||
'70854bc0a34b3ab487c1bc021cd318c734c51ae29374f2beb0e6f2dd49b4bf41c';
|
||||
deepStrictEqual(
|
||||
starknet.ethSigToPrivate(ethSignature),
|
||||
'766f11e90cd7c7b43085b56da35c781f8c067ac0d578eabdceebc4886435bda'
|
||||
);
|
||||
});
|
||||
|
||||
should('Key derivation', () => {
|
||||
const layer = 'starkex';
|
||||
const application = 'starkdeployement';
|
||||
const mnemonic =
|
||||
'range mountain blast problem vibrant void vivid doctor cluster enough melody ' +
|
||||
'salt layer language laptop boat major space monkey unit glimpse pause change vibrant';
|
||||
const ethAddress = '0xa4864d977b944315389d1765ffa7e66F74ee8cd7';
|
||||
const VECTORS = [
|
||||
{
|
||||
index: 0,
|
||||
path: "m/2645'/579218131'/891216374'/1961790679'/2135936222'/0",
|
||||
privateKey: '6cf0a8bf113352eb863157a45c5e5567abb34f8d32cddafd2c22aa803f4892c',
|
||||
},
|
||||
{
|
||||
index: 7,
|
||||
path: "m/2645'/579218131'/891216374'/1961790679'/2135936222'/7",
|
||||
privateKey: '341751bdc42841da35ab74d13a1372c1f0250617e8a2ef96034d9f46e6847af',
|
||||
},
|
||||
{
|
||||
index: 598,
|
||||
path: "m/2645'/579218131'/891216374'/1961790679'/2135936222'/598",
|
||||
privateKey: '41a4d591a868353d28b7947eb132aa4d00c4a022743689ffd20a3628d6ca28c',
|
||||
},
|
||||
];
|
||||
const hd = bip32.HDKey.fromMasterSeed(bip39.mnemonicToSeedSync(mnemonic));
|
||||
for (const { index, path, privateKey } of VECTORS) {
|
||||
const realPath = starknet.getAccountPath(layer, application, ethAddress, index);
|
||||
deepStrictEqual(realPath, path);
|
||||
deepStrictEqual(starknet.grindKey(hd.derive(realPath).privateKey), privateKey);
|
||||
}
|
||||
});
|
||||
|
||||
// Verified against starknet.js
|
||||
should('Starknet.js cross-tests', () => {
|
||||
const privateKey = '0x019800ea6a9a73f94aee6a3d2edf018fc770443e90c7ba121e8303ec6b349279';
|
||||
// NOTE: there is no compressed keys here, getPubKey returns stark-key (which is schnorr-like X coordinate)
|
||||
// But it is not used in signing/verifying
|
||||
deepStrictEqual(
|
||||
starknet.getStarkKey(privateKey),
|
||||
'0x33f45f07e1bd1a51b45fc24ec8c8c9908db9e42191be9e169bfcac0c0d99745'
|
||||
);
|
||||
const msgHash = '0x6d1706bd3d1ba7c517be2a2a335996f63d4738e2f182144d078a1dd9997062e';
|
||||
const sig = starknet.sign(msgHash, privateKey);
|
||||
const { r, s } = sig;
|
||||
// const { r, s } = starknet.Signature.fromDER(sig);
|
||||
deepStrictEqual(r.toString(16), v.r, 'r equality');
|
||||
deepStrictEqual(s.toString(16), v.s, 's equality');
|
||||
deepStrictEqual(starknet.verify(sig, v.msg, publicKey), true, 'verify');
|
||||
}
|
||||
});
|
||||
|
||||
should('Invalid signatures', () => {
|
||||
/*
|
||||
|
||||
it('should not verify invalid signature inputs lengths', () => {
|
||||
const ecOrder = starkwareCrypto.ec.n;
|
||||
const {maxEcdsaVal} = starkwareCrypto;
|
||||
const maxMsgHash = maxEcdsaVal.sub(oneBn);
|
||||
const maxR = maxEcdsaVal.sub(oneBn);
|
||||
const maxS = ecOrder.sub(oneBn).sub(oneBn);
|
||||
const maxStarkKey = maxEcdsaVal.sub(oneBn);
|
||||
|
||||
// Test invalid message length.
|
||||
expect(() =>
|
||||
starkwareCrypto.verify(maxStarkKey, maxMsgHash.add(oneBn).toString(16), {
|
||||
r: maxR,
|
||||
s: maxS
|
||||
})
|
||||
).to.throw('Message not signable, invalid msgHash length.');
|
||||
// Test invalid r length.
|
||||
expect(() =>
|
||||
starkwareCrypto.verify(maxStarkKey, maxMsgHash.toString(16), {
|
||||
r: maxR.add(oneBn),
|
||||
s: maxS
|
||||
})
|
||||
).to.throw('Message not signable, invalid r length.');
|
||||
// Test invalid w length.
|
||||
expect(() =>
|
||||
starkwareCrypto.verify(maxStarkKey, maxMsgHash.toString(16), {
|
||||
r: maxR,
|
||||
s: maxS.add(oneBn)
|
||||
})
|
||||
).to.throw('Message not signable, invalid w length.');
|
||||
// Test invalid s length.
|
||||
expect(() =>
|
||||
starkwareCrypto.verify(maxStarkKey, maxMsgHash.toString(16), {
|
||||
r: maxR,
|
||||
s: maxS.add(oneBn).add(oneBn)
|
||||
})
|
||||
).to.throw('Message not signable, invalid s length.');
|
||||
});
|
||||
|
||||
it('should not verify invalid signatures', () => {
|
||||
const privKey = generateRandomStarkPrivateKey();
|
||||
const keyPair = starkwareCrypto.ec.keyFromPrivate(privKey, 'hex');
|
||||
const keyPairPub = starkwareCrypto.ec.keyFromPublic(
|
||||
keyPair.getPublic(),
|
||||
'BN'
|
||||
deepStrictEqual(
|
||||
r.toString(),
|
||||
'1427981024487605678086498726488552139932400435436186597196374630267616399345'
|
||||
);
|
||||
const msgHash = new BN(randomHexString(61));
|
||||
const msgSignature = starkwareCrypto.sign(keyPair, msgHash);
|
||||
|
||||
// Test invalid public key.
|
||||
const invalidKeyPairPub = starkwareCrypto.ec.keyFromPublic(
|
||||
{x: keyPairPub.pub.getX().add(oneBn), y: keyPairPub.pub.getY()},
|
||||
'BN'
|
||||
deepStrictEqual(
|
||||
s.toString(),
|
||||
'1853664302719670721837677288395394946745467311923401353018029119631574115563'
|
||||
);
|
||||
expect(
|
||||
starkwareCrypto.verify(
|
||||
invalidKeyPairPub,
|
||||
msgHash.toString(16),
|
||||
msgSignature
|
||||
)
|
||||
).to.be.false;
|
||||
// Test invalid message.
|
||||
expect(
|
||||
starkwareCrypto.verify(
|
||||
keyPair,
|
||||
msgHash.add(oneBn).toString(16),
|
||||
msgSignature
|
||||
)
|
||||
).to.be.false;
|
||||
expect(
|
||||
starkwareCrypto.verify(
|
||||
keyPairPub,
|
||||
msgHash.add(oneBn).toString(16),
|
||||
msgSignature
|
||||
)
|
||||
).to.be.false;
|
||||
// Test invalid r.
|
||||
msgSignature.r.iadd(oneBn);
|
||||
expect(starkwareCrypto.verify(keyPair, msgHash.toString(16), msgSignature))
|
||||
.to.be.false;
|
||||
expect(
|
||||
starkwareCrypto.verify(keyPairPub, msgHash.toString(16), msgSignature)
|
||||
).to.be.false;
|
||||
// Test invalid s.
|
||||
msgSignature.r.isub(oneBn);
|
||||
msgSignature.s.iadd(oneBn);
|
||||
expect(starkwareCrypto.verify(keyPair, msgHash.toString(16), msgSignature))
|
||||
.to.be.false;
|
||||
expect(
|
||||
starkwareCrypto.verify(keyPairPub, msgHash.toString(16), msgSignature)
|
||||
).to.be.false;
|
||||
const hashMsg2 = starknet.pedersen(
|
||||
'0x33f45f07e1bd1a51b45fc24ec8c8c9908db9e42191be9e169bfcac0c0d99745',
|
||||
'1'
|
||||
);
|
||||
deepStrictEqual(hashMsg2, '0x2b0d4d43acce8ff68416f667f92ec7eab2b96f1d2224abd4d9d4d1e7fa4bb00');
|
||||
const pubKey =
|
||||
'04033f45f07e1bd1a51b45fc24ec8c8c9908db9e42191be9e169bfcac0c0d997450319d0f53f6ca077c4fa5207819144a2a4165daef6ee47a7c1d06c0dcaa3e456';
|
||||
const sig2 = new starknet.Signature(
|
||||
558858382392827003930138586379728730695763862039474863361948210004201119180n,
|
||||
2440689354481625417078677634625227600823892606910345662891037256374285369343n
|
||||
);
|
||||
deepStrictEqual(starknet.verify(sig2.toDERHex(), hashMsg2, pubKey), true);
|
||||
});
|
||||
});
|
||||
*/
|
||||
});
|
||||
|
||||
should('Pedersen', () => {
|
||||
deepStrictEqual(
|
||||
starknet.pedersen(
|
||||
'0x3d937c035c878245caf64531a5756109c53068da139362728feb561405371cb',
|
||||
'0x208a0a10250e382e1e4bbe2880906c2791bf6275695e02fbbc6aeff9cd8b31a'
|
||||
),
|
||||
'0x30e480bed5fe53fa909cc0f8c4d99b8f9f2c016be4c41e13a4848797979c662'
|
||||
);
|
||||
deepStrictEqual(
|
||||
starknet.pedersen(
|
||||
'0x58f580910a6ca59b28927c08fe6c43e2e303ca384badc365795fc645d479d45',
|
||||
'0x78734f65a067be9bdb39de18434d71e79f7b6466a4b66bbd979ab9e7515fe0b'
|
||||
),
|
||||
'0x68cc0b76cddd1dd4ed2301ada9b7c872b23875d5ff837b3a87993e0d9996b87'
|
||||
);
|
||||
});
|
||||
|
||||
should('Hash chain', () => {
|
||||
deepStrictEqual(starknet.hashChain([1, 2, 3]), starknet.pedersen(1, starknet.pedersen(2, 3)));
|
||||
});
|
||||
|
||||
should('Key grinding', () => {
|
||||
deepStrictEqual(
|
||||
starknet.grindKey('86F3E7293141F20A8BAFF320E8EE4ACCB9D4A4BF2B4D295E8CEE784DB46E0519'),
|
||||
'5c8c8683596c732541a59e03007b2d30dbbbb873556fe65b5fb63c16688f941'
|
||||
);
|
||||
// Loops more than once (verified manually)
|
||||
deepStrictEqual(
|
||||
starknet.grindKey('94F3E7293141F20A8BAFF320E8EE4ACCB9D4A4BF2B4D295E8CEE784DB46E0595'),
|
||||
'33880b9aba464c1c01c9f8f5b4fc1134698f9b0a8d18505cab6cdd34d93dc02'
|
||||
);
|
||||
});
|
||||
|
||||
should('Private to stark key', () => {
|
||||
deepStrictEqual(
|
||||
starknet.getStarkKey('0x178047D3869489C055D7EA54C014FFB834A069C9595186ABE04EA4D1223A03F'),
|
||||
'0x1895a6a77ae14e7987b9cb51329a5adfb17bd8e7c638f92d6892d76e51cebcf'
|
||||
);
|
||||
for (const [privKey, expectedPubKey] of Object.entries(precomputedKeys)) {
|
||||
deepStrictEqual(starknet.getStarkKey(privKey), expectedPubKey);
|
||||
}
|
||||
});
|
||||
|
||||
should('Private stark key from eth signature', () => {
|
||||
const ethSignature =
|
||||
'0x21fbf0696d5e0aa2ef41a2b4ffb623bcaf070461d61cf7251c74161f82fec3a43' +
|
||||
'70854bc0a34b3ab487c1bc021cd318c734c51ae29374f2beb0e6f2dd49b4bf41c';
|
||||
deepStrictEqual(
|
||||
starknet.ethSigToPrivate(ethSignature),
|
||||
'766f11e90cd7c7b43085b56da35c781f8c067ac0d578eabdceebc4886435bda'
|
||||
);
|
||||
});
|
||||
|
||||
should('Key derivation', () => {
|
||||
const layer = 'starkex';
|
||||
const application = 'starkdeployement';
|
||||
const mnemonic =
|
||||
'range mountain blast problem vibrant void vivid doctor cluster enough melody ' +
|
||||
'salt layer language laptop boat major space monkey unit glimpse pause change vibrant';
|
||||
const ethAddress = '0xa4864d977b944315389d1765ffa7e66F74ee8cd7';
|
||||
const VECTORS = [
|
||||
{
|
||||
index: 0,
|
||||
path: "m/2645'/579218131'/891216374'/1961790679'/2135936222'/0",
|
||||
privateKey: '6cf0a8bf113352eb863157a45c5e5567abb34f8d32cddafd2c22aa803f4892c',
|
||||
},
|
||||
{
|
||||
index: 7,
|
||||
path: "m/2645'/579218131'/891216374'/1961790679'/2135936222'/7",
|
||||
privateKey: '341751bdc42841da35ab74d13a1372c1f0250617e8a2ef96034d9f46e6847af',
|
||||
},
|
||||
{
|
||||
index: 598,
|
||||
path: "m/2645'/579218131'/891216374'/1961790679'/2135936222'/598",
|
||||
privateKey: '41a4d591a868353d28b7947eb132aa4d00c4a022743689ffd20a3628d6ca28c',
|
||||
},
|
||||
];
|
||||
const hd = bip32.HDKey.fromMasterSeed(bip39.mnemonicToSeedSync(mnemonic));
|
||||
for (const { index, path, privateKey } of VECTORS) {
|
||||
const realPath = starknet.getAccountPath(layer, application, ethAddress, index);
|
||||
deepStrictEqual(realPath, path);
|
||||
deepStrictEqual(starknet.grindKey(hd.derive(realPath).privateKey), privateKey);
|
||||
}
|
||||
});
|
||||
|
||||
// Verified against starknet.js
|
||||
should('Starknet.js cross-tests', () => {
|
||||
const privateKey = '0x019800ea6a9a73f94aee6a3d2edf018fc770443e90c7ba121e8303ec6b349279';
|
||||
// NOTE: there is no compressed keys here, getPubKey returns stark-key (which is schnorr-like X coordinate)
|
||||
// But it is not used in signing/verifying
|
||||
deepStrictEqual(
|
||||
starknet.getStarkKey(privateKey),
|
||||
'0x33f45f07e1bd1a51b45fc24ec8c8c9908db9e42191be9e169bfcac0c0d99745'
|
||||
);
|
||||
const msgHash = '0x6d1706bd3d1ba7c517be2a2a335996f63d4738e2f182144d078a1dd9997062e';
|
||||
const sig = starknet.sign(msgHash, privateKey);
|
||||
const { r, s } = (sig);
|
||||
|
||||
deepStrictEqual(
|
||||
r.toString(),
|
||||
'1427981024487605678086498726488552139932400435436186597196374630267616399345'
|
||||
);
|
||||
deepStrictEqual(
|
||||
s.toString(),
|
||||
'1853664302719670721837677288395394946745467311923401353018029119631574115563'
|
||||
);
|
||||
const hashMsg2 = starknet.pedersen(
|
||||
'0x33f45f07e1bd1a51b45fc24ec8c8c9908db9e42191be9e169bfcac0c0d99745',
|
||||
'1'
|
||||
);
|
||||
deepStrictEqual(hashMsg2, '0x2b0d4d43acce8ff68416f667f92ec7eab2b96f1d2224abd4d9d4d1e7fa4bb00');
|
||||
const pubKey =
|
||||
'04033f45f07e1bd1a51b45fc24ec8c8c9908db9e42191be9e169bfcac0c0d997450319d0f53f6ca077c4fa5207819144a2a4165daef6ee47a7c1d06c0dcaa3e456';
|
||||
const sig2 = new starknet.Signature(
|
||||
558858382392827003930138586379728730695763862039474863361948210004201119180n,
|
||||
2440689354481625417078677634625227600823892606910345662891037256374285369343n
|
||||
);
|
||||
deepStrictEqual(starknet.verify(sig2.toDERHex(), hashMsg2, pubKey), true);
|
||||
});
|
||||
|
||||
// ESM is broken.
|
||||
import url from 'url';
|
||||
|
||||
1080
test/vectors/poseidon.json
Normal file
1080
test/vectors/poseidon.json
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user