Compare commits
126 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b8b2e91f74 | ||
|
|
9ee694ae23 | ||
|
|
6bc4b35cf4 | ||
|
|
0163b63532 | ||
|
|
7e825520f1 | ||
|
|
d739297b2c | ||
|
|
285aa6375d | ||
|
|
8c77331ef2 | ||
|
|
669641e0a3 | ||
|
|
68dd57ed31 | ||
|
|
a9fdd6df9f | ||
|
|
d485d8b0e6 | ||
|
|
0fdd763dc7 | ||
|
|
586e2ad5fb | ||
|
|
ed81707bdc | ||
|
|
6d56b2d78e | ||
|
|
8397241a8f | ||
|
|
001d0cc24a | ||
|
|
ce9d165657 | ||
|
|
2902b0299a | ||
|
|
e1cb8549e8 | ||
|
|
26ebb5dcce | ||
|
|
8b2863aeac | ||
|
|
b1f50d9364 | ||
|
|
b81d74d3cb | ||
|
|
d5fe537159 | ||
|
|
cde1d5c488 | ||
|
|
3486bbf6b8 | ||
|
|
0d7a8296c5 | ||
|
|
0f1e7a5a43 | ||
|
|
3da48cf899 | ||
|
|
4ec46dd65d | ||
|
|
7073f63c6b | ||
|
|
80966cbd03 | ||
|
|
98ea15dca4 | ||
|
|
e1910e85ea | ||
|
|
4d311d7294 | ||
|
|
c36d90cae6 | ||
|
|
af5aa8424f | ||
|
|
67b99652fc | ||
|
|
c8d292976b | ||
|
|
daffaa2339 | ||
|
|
a462fc5779 | ||
|
|
fe3491c5aa | ||
|
|
c0877ba69a | ||
|
|
8e449cc78c | ||
|
|
1b6071cabd | ||
|
|
debb9d9709 | ||
|
|
d2c6459756 | ||
|
|
47533b6336 | ||
|
|
00b73b68d3 | ||
|
|
cef4b52d12 | ||
|
|
47ce547dcf | ||
|
|
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 |
14
.gitignore
vendored
14
.gitignore
vendored
@@ -1,7 +1,13 @@
|
|||||||
build/
|
build/
|
||||||
node_modules/
|
node_modules/
|
||||||
coverage/
|
coverage/
|
||||||
/lib/**/*.js
|
/*.js
|
||||||
/lib/**/*.ts
|
/*.ts
|
||||||
/lib/**/*.d.ts.map
|
/*.js.map
|
||||||
/curve-definitions/lib
|
/*.d.ts.map
|
||||||
|
/esm/*.js
|
||||||
|
/esm/*.ts
|
||||||
|
/esm/*.js.map
|
||||||
|
/esm/*.d.ts.map
|
||||||
|
/esm/abstract
|
||||||
|
/abstract/
|
||||||
|
|||||||
842
README.md
842
README.md
@@ -1,72 +1,77 @@
|
|||||||
# noble-curves
|
# noble-curves
|
||||||
|
|
||||||
Minimal, auditable JS implementation of elliptic curve cryptography.
|
Audited & minimal JS implementation of elliptic curve cryptography.
|
||||||
|
|
||||||
|
- **noble** family, zero dependencies
|
||||||
- Short Weierstrass, Edwards, Montgomery curves
|
- Short Weierstrass, Edwards, Montgomery curves
|
||||||
- ECDSA, EdDSA, Schnorr, BLS signature schemes, ECDH key agreement
|
- ECDSA, EdDSA, Schnorr, BLS signature schemes, ECDH key agreement
|
||||||
- [hash to curve](https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/)
|
- #️⃣ [hash to curve](#abstracthash-to-curve-hashing-strings-to-curve-points)
|
||||||
for encoding or hashing an arbitrary string to a point on an elliptic curve
|
for encoding or hashing an arbitrary string to an elliptic curve point
|
||||||
- Auditable, [fast](#speed)
|
- 🧜♂️ [Poseidon](https://www.poseidon-hash.info) ZK-friendly hash
|
||||||
|
- 🏎 [Ultra-fast](#speed), hand-optimized for caveats of JS engines
|
||||||
|
- 🔍 Unique tests ensure correctness with Wycheproof vectors and [cryptofuzz](https://github.com/guidovranken/cryptofuzz) differential fuzzing
|
||||||
- 🔻 Tree-shaking-friendly: there is no entry point, which ensures small size of your app
|
- 🔻 Tree-shaking-friendly: there is no entry point, which ensures small size of your app
|
||||||
- 🔍 Unique tests ensure correctness. Wycheproof vectors included
|
|
||||||
|
|
||||||
There are two parts of the package:
|
Package consists of two parts:
|
||||||
|
|
||||||
1. `abstract/` directory specifies zero-dependency EC algorithms
|
1. [Abstract](#abstract-api), zero-dependency EC algorithms
|
||||||
2. root directory utilizes one dependency `@noble/hashes` and provides ready-to-use:
|
2. [Implementations](#implementations), utilizing one dependency `@noble/hashes`, providing ready-to-use:
|
||||||
- NIST curves secp192r1/P192, secp224r1/P224, secp256r1/P256, secp384r1/P384, secp521r1/P521
|
- NIST curves secp256r1/P256, secp384r1/P384, secp521r1/P521
|
||||||
- SECG curve secp256k1
|
- SECG curve secp256k1
|
||||||
|
- ed25519/curve25519/x25519/ristretto255, edwards448/curve448/x448 [RFC7748](https://www.rfc-editor.org/rfc/rfc7748) / [RFC8032](https://www.rfc-editor.org/rfc/rfc8032) / [ZIP215](https://zips.z.cash/zip-0215) stuff
|
||||||
- pairing-friendly curves bls12-381, bn254
|
- pairing-friendly curves bls12-381, bn254
|
||||||
- ed25519/curve25519/x25519/ristretto, edwards448/curve448/x448 RFC7748 / RFC8032 / ZIP215 stuff
|
|
||||||
|
|
||||||
Curves incorporate work from previous noble packages
|
Check out [Upgrading](#upgrading) if you've previously used single-feature noble packages
|
||||||
([secp256k1](https://github.com/paulmillr/noble-secp256k1),
|
([secp256k1](https://github.com/paulmillr/noble-secp256k1), [ed25519](https://github.com/paulmillr/noble-ed25519)).
|
||||||
[ed25519](https://github.com/paulmillr/noble-ed25519),
|
See [Resources](#resources) for articles and real-world software that uses curves.
|
||||||
[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.
|
|
||||||
|
|
||||||
### This library belongs to _noble_ crypto
|
### This library belongs to _noble_ crypto
|
||||||
|
|
||||||
> **noble-crypto** — high-security, easily auditable set of contained cryptographic libraries and tools.
|
> **noble-crypto** — high-security, easily auditable set of contained cryptographic libraries and tools.
|
||||||
|
|
||||||
- Minimal dependencies, small files
|
- No dependencies, protection against supply chain attacks
|
||||||
- Easily auditable TypeScript/JS code
|
- Easily auditable TypeScript/JS code
|
||||||
- Supported in all major browsers and stable node.js versions
|
- Supported in all major browsers and stable node.js versions
|
||||||
- All releases are signed with PGP keys
|
- All releases are signed with PGP keys
|
||||||
- Check out [homepage](https://paulmillr.com/noble/) & all libraries:
|
- Check out [homepage](https://paulmillr.com/noble/) & all libraries:
|
||||||
[curves](https://github.com/paulmillr/noble-curves) ([secp256k1](https://github.com/paulmillr/noble-secp256k1),
|
[curves](https://github.com/paulmillr/noble-curves)
|
||||||
[ed25519](https://github.com/paulmillr/noble-ed25519),
|
([secp256k1](https://github.com/paulmillr/noble-secp256k1),
|
||||||
[bls12-381](https://github.com/paulmillr/noble-bls12-381)),
|
[ed25519](https://github.com/paulmillr/noble-ed25519)),
|
||||||
[hashes](https://github.com/paulmillr/noble-hashes)
|
[hashes](https://github.com/paulmillr/noble-hashes)
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Use NPM in node.js / browser, or include single file from
|
Use NPM for browser / node.js:
|
||||||
[GitHub's releases page](https://github.com/paulmillr/noble-curves/releases):
|
|
||||||
|
|
||||||
> npm install @noble/curves
|
> npm install @noble/curves
|
||||||
|
|
||||||
The library does not have an entry point. It allows you to select specific primitives and drop everything else. If you only want to use secp256k1, just use the library with rollup or other bundlers. This is done to make your bundles tiny.
|
For [Deno](https://deno.land), use it with [npm specifier](https://deno.land/manual@v1.28.0/node/npm_specifiers). In browser, you could also include the single file from
|
||||||
|
[GitHub's releases page](https://github.com/paulmillr/noble-curves/releases).
|
||||||
|
|
||||||
|
The library is tree-shaking-friendly and does not expose root entry point as `import * from '@noble/curves'`.
|
||||||
|
Instead, you need to import specific primitives. This is done to ensure small size of your apps.
|
||||||
|
|
||||||
|
### Implementations
|
||||||
|
|
||||||
|
Each curve can be used in the following way:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
// Common.js and ECMAScript Modules (ESM)
|
import { secp256k1 } from '@noble/curves/secp256k1'; // ECMAScript Modules (ESM) and Common.js
|
||||||
import { secp256k1 } from '@noble/curves/secp256k1';
|
// import { secp256k1 } from 'npm:@noble/curves@1.2.0/secp256k1'; // Deno
|
||||||
|
const priv = secp256k1.utils.randomPrivateKey();
|
||||||
const key = secp256k1.utils.randomPrivateKey();
|
const pub = secp256k1.getPublicKey(priv);
|
||||||
const pub = secp256k1.getPublicKey(key);
|
|
||||||
const msg = new Uint8Array(32).fill(1);
|
const msg = new Uint8Array(32).fill(1);
|
||||||
const sig = secp256k1.sign(msg, key);
|
const sig = secp256k1.sign(msg, priv);
|
||||||
secp256k1.verify(sig, msg, pub) === true;
|
secp256k1.verify(sig, msg, pub) === true;
|
||||||
sig.recoverPublicKey(msg) === pub;
|
|
||||||
const someonesPub = secp256k1.getPublicKey(secp256k1.utils.randomPrivateKey());
|
const privHex = '46c930bc7bb4db7f55da20798697421b98c4175a52c630294d75a84b9c126236';
|
||||||
const shared = secp256k1.getSharedSecret(key, someonesPub);
|
const pub2 = secp256k1.getPublicKey(privHex); // keys & other inputs can be Uint8Array-s or hex strings
|
||||||
```
|
```
|
||||||
|
|
||||||
All curves:
|
All curves:
|
||||||
|
|
||||||
```ts
|
```typescript
|
||||||
import { secp256k1 } from '@noble/curves/secp256k1';
|
import { secp256k1, schnorr } from '@noble/curves/secp256k1';
|
||||||
import { ed25519, ed25519ph, ed25519ctx, x25519, RistrettoPoint } from '@noble/curves/ed25519';
|
import { ed25519, ed25519ph, ed25519ctx, x25519, RistrettoPoint } from '@noble/curves/ed25519';
|
||||||
import { ed448, ed448ph, ed448ctx, x448 } from '@noble/curves/ed448';
|
import { ed448, ed448ph, ed448ctx, x448 } from '@noble/curves/ed448';
|
||||||
import { p256 } from '@noble/curves/p256';
|
import { p256 } from '@noble/curves/p256';
|
||||||
@@ -79,94 +84,327 @@ import { bn254 } from '@noble/curves/bn';
|
|||||||
import { jubjub } from '@noble/curves/jubjub';
|
import { jubjub } from '@noble/curves/jubjub';
|
||||||
```
|
```
|
||||||
|
|
||||||
To define a custom curve, check out API below.
|
Weierstrass curves feature recovering public keys from signatures and ECDH key agreement:
|
||||||
|
|
||||||
## 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)
|
|
||||||
|
|
||||||
### Overview
|
|
||||||
|
|
||||||
There are following zero-dependency abstract algorithms:
|
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { bls } from '@noble/curves/abstract/bls';
|
// extraEntropy https://moderncrypto.org/mail-archive/curves/2017/000925.html
|
||||||
import { twistedEdwards } from '@noble/curves/abstract/edwards';
|
const sigImprovedSecurity = secp256k1.sign(msg, priv, { extraEntropy: true });
|
||||||
import { montgomery } from '@noble/curves/abstract/montgomery';
|
sig.recoverPublicKey(msg) === pub; // public key recovery
|
||||||
import { weierstrass } from '@noble/curves/abstract/weierstrass';
|
const someonesPub = secp256k1.getPublicKey(secp256k1.utils.randomPrivateKey());
|
||||||
import * as mod from '@noble/curves/abstract/modular';
|
const shared = secp256k1.getSharedSecret(priv, someonesPub); // ECDH (elliptic curve diffie-hellman)
|
||||||
import * as utils from '@noble/curves/abstract/utils';
|
|
||||||
```
|
```
|
||||||
|
|
||||||
They allow to define a new curve in a few lines of code:
|
secp256k1 has schnorr signature implementation which follows
|
||||||
|
[BIP340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki):
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { Fp } from '@noble/curves/abstract/modular';
|
import { schnorr } from '@noble/curves/secp256k1';
|
||||||
import { weierstrass } from '@noble/curves/abstract/weierstrass';
|
const priv = schnorr.utils.randomPrivateKey();
|
||||||
import { hmac } from '@noble/hashes/hmac';
|
const pub = schnorr.getPublicKey(priv);
|
||||||
import { sha256 } from '@noble/hashes/sha256';
|
const msg = new TextEncoder().encode('hello');
|
||||||
import { concatBytes, randomBytes } from '@noble/hashes/utils';
|
const sig = schnorr.sign(msg, priv);
|
||||||
|
const isValid = schnorr.verify(sig, msg, pub);
|
||||||
|
console.log(isValid);
|
||||||
|
```
|
||||||
|
|
||||||
const secp256k1 = weierstrass({
|
ed25519 module has ed25519ctx / ed25519ph variants,
|
||||||
|
x25519 ECDH and [ristretto255](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-ristretto255-decaf448).
|
||||||
|
It follows [ZIP215](https://zips.z.cash/zip-0215) and [can be used in consensus-critical applications](https://hdevalence.ca/blog/2020-10-04-its-25519am):
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { ed25519 } from '@noble/curves/ed25519';
|
||||||
|
|
||||||
|
// Variants from RFC8032: with context, prehashed
|
||||||
|
import { ed25519ctx, ed25519ph } from '@noble/curves/ed25519';
|
||||||
|
|
||||||
|
// ECDH using curve25519 aka x25519
|
||||||
|
import { x25519 } from '@noble/curves/ed25519';
|
||||||
|
const priv = 'a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4';
|
||||||
|
const pub = 'e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c';
|
||||||
|
x25519.getSharedSecret(priv, pub) === x25519.scalarMult(priv, pub); // aliases
|
||||||
|
x25519.getPublicKey(priv) === x25519.scalarMultBase(priv);
|
||||||
|
|
||||||
|
// hash-to-curve
|
||||||
|
import { hashToCurve, encodeToCurve } from '@noble/curves/ed25519';
|
||||||
|
|
||||||
|
import { RistrettoPoint } from '@noble/curves/ed25519';
|
||||||
|
const rp = RistrettoPoint.fromHex(
|
||||||
|
'6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919'
|
||||||
|
);
|
||||||
|
RistrettoPoint.hashToCurve('Ristretto is traditionally a short shot of espresso coffee');
|
||||||
|
// also has add(), equals(), multiply(), toRawBytes() methods
|
||||||
|
```
|
||||||
|
|
||||||
|
ed448 module is basically the same:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { ed448, ed448ph, ed448ctx, x448 } from '@noble/curves/ed448';
|
||||||
|
import { hashToCurve, encodeToCurve } from '@noble/curves/ed448';
|
||||||
|
```
|
||||||
|
|
||||||
|
BLS12-381 pairing-friendly Barreto-Lynn-Scott elliptic curve construction allows to
|
||||||
|
construct [zk-SNARKs](https://z.cash/technology/zksnarks/) at the 128-bit security
|
||||||
|
and use aggregated, batch-verifiable
|
||||||
|
[threshold signatures](https://medium.com/snigirev.stepan/bls-signatures-better-than-schnorr-5a7fe30ea716),
|
||||||
|
using Boneh-Lynn-Shacham signature scheme.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { bls12_381 as bls } from '@noble/curves/bls12-381';
|
||||||
|
const privateKey = '67d53f170b908cabb9eb326c3c337762d59289a8fec79f7bc9254b584b73265c';
|
||||||
|
const message = '64726e3da8';
|
||||||
|
const publicKey = bls.getPublicKey(privateKey);
|
||||||
|
const signature = bls.sign(message, privateKey);
|
||||||
|
const isValid = bls.verify(signature, message, publicKey);
|
||||||
|
console.log({ publicKey, signature, isValid });
|
||||||
|
|
||||||
|
// Sign 1 msg with 3 keys
|
||||||
|
const privateKeys = [
|
||||||
|
'18f020b98eb798752a50ed0563b079c125b0db5dd0b1060d1c1b47d4a193e1e4',
|
||||||
|
'ed69a8c50cf8c9836be3b67c7eeff416612d45ba39a5c099d48fa668bf558c9c',
|
||||||
|
'16ae669f3be7a2121e17d0c68c05a8f3d6bef21ec0f2315f1d7aec12484e4cf5',
|
||||||
|
];
|
||||||
|
const messages = ['d2', '0d98', '05caf3'];
|
||||||
|
const publicKeys = privateKeys.map(bls.getPublicKey);
|
||||||
|
const signatures2 = privateKeys.map((p) => bls.sign(message, p));
|
||||||
|
const aggPubKey2 = bls.aggregatePublicKeys(publicKeys);
|
||||||
|
const aggSignature2 = bls.aggregateSignatures(signatures2);
|
||||||
|
const isValid2 = bls.verify(aggSignature2, message, aggPubKey2);
|
||||||
|
console.log({ signatures2, aggSignature2, isValid2 });
|
||||||
|
|
||||||
|
// Sign 3 msgs with 3 keys
|
||||||
|
const signatures3 = privateKeys.map((p, i) => bls.sign(messages[i], p));
|
||||||
|
const aggSignature3 = bls.aggregateSignatures(signatures3);
|
||||||
|
const isValid3 = bls.verifyBatch(aggSignature3, messages, publicKeys);
|
||||||
|
console.log({ publicKeys, signatures3, aggSignature3, isValid3 });
|
||||||
|
// bls.pairing(PointG1, PointG2) // pairings
|
||||||
|
|
||||||
|
// hash-to-curve examples can be seen below
|
||||||
|
```
|
||||||
|
|
||||||
|
## Abstract API
|
||||||
|
|
||||||
|
Abstract API allows to define custom curves. All arithmetics is done with JS bigints over finite fields,
|
||||||
|
which is defined from `modular` sub-module. For scalar multiplication, we use [precomputed tables with w-ary non-adjacent form (wNAF)](https://paulmillr.com/posts/noble-secp256k1-fast-ecc/).
|
||||||
|
Precomputes are enabled for weierstrass and edwards BASE points of a curve. You could precompute any
|
||||||
|
other point (e.g. for ECDH) using `utils.precompute()` method: check out examples.
|
||||||
|
|
||||||
|
There are following zero-dependency algorithms:
|
||||||
|
|
||||||
|
- [abstract/weierstrass: Short Weierstrass curve](#abstractweierstrass-short-weierstrass-curve)
|
||||||
|
- [abstract/edwards: Twisted Edwards curve](#abstractedwards-twisted-edwards-curve)
|
||||||
|
- [abstract/montgomery: Montgomery curve](#abstractmontgomery-montgomery-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: Modular arithmetics utilities](#abstractmodular-modular-arithmetics-utilities)
|
||||||
|
- [abstract/utils: General utilities](#abstractutils-general-utilities)
|
||||||
|
|
||||||
|
### abstract/weierstrass: Short Weierstrass curve
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { weierstrass } from '@noble/curves/abstract/weierstrass';
|
||||||
|
```
|
||||||
|
|
||||||
|
Short Weierstrass curve's formula is `y² = x³ + ax + b`. `weierstrass` expects arguments `a`, `b`, field `Fp`, curve order `n`, cofactor `h`
|
||||||
|
and coordinates `Gx`, `Gy` of generator point.
|
||||||
|
|
||||||
|
**`k` generation** is done deterministically, following [RFC6979](https://www.rfc-editor.org/rfc/rfc6979).
|
||||||
|
For this you will need `hmac` & `hash`, which in our implementations is provided by noble-hashes.
|
||||||
|
If you're using different hashing library, make sure to wrap it in the following interface:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
type CHash = {
|
||||||
|
(message: Uint8Array): Uint8Array;
|
||||||
|
blockLen: number;
|
||||||
|
outputLen: number;
|
||||||
|
create(): any;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
**Weierstrass points:**
|
||||||
|
|
||||||
|
1. Exported as `ProjectivePoint`
|
||||||
|
2. Represented in projective (homogeneous) coordinates: (x, y, z) ∋ (x=x/z, y=y/z)
|
||||||
|
3. Use complete exception-free formulas for addition and doubling
|
||||||
|
4. Can be decoded/encoded from/to Uint8Array / hex strings using `ProjectivePoint.fromHex` and `ProjectivePoint#toRawBytes()`
|
||||||
|
5. Have `assertValidity()` which checks for being on-curve
|
||||||
|
6. Have `toAffine()` and `x` / `y` getters which convert to 2d xy affine coordinates
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// T is usually bigint, but can be something else like complex numbers in BLS curves
|
||||||
|
interface ProjPointType<T> extends Group<ProjPointType<T>> {
|
||||||
|
readonly px: T;
|
||||||
|
readonly py: T;
|
||||||
|
readonly pz: T;
|
||||||
|
multiply(scalar: bigint): ProjPointType<T>;
|
||||||
|
multiplyUnsafe(scalar: bigint): ProjPointType<T>;
|
||||||
|
multiplyAndAddUnsafe(Q: ProjPointType<T>, a: bigint, b: bigint): ProjPointType<T> | undefined;
|
||||||
|
toAffine(iz?: T): AffinePoint<T>;
|
||||||
|
isTorsionFree(): boolean;
|
||||||
|
clearCofactor(): ProjPointType<T>;
|
||||||
|
assertValidity(): void;
|
||||||
|
hasEvenY(): boolean;
|
||||||
|
toRawBytes(isCompressed?: boolean): Uint8Array;
|
||||||
|
toHex(isCompressed?: boolean): string;
|
||||||
|
}
|
||||||
|
// Static methods for 3d XYZ points
|
||||||
|
interface ProjConstructor<T> extends GroupConstructor<ProjPointType<T>> {
|
||||||
|
new (x: T, y: T, z: T): ProjPointType<T>;
|
||||||
|
fromAffine(p: AffinePoint<T>): ProjPointType<T>;
|
||||||
|
fromHex(hex: Hex): ProjPointType<T>;
|
||||||
|
fromPrivateKey(privateKey: PrivKey): ProjPointType<T>;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**ECDSA signatures** are represented by `Signature` instances and can be described by the interface:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
interface SignatureType {
|
||||||
|
readonly r: bigint;
|
||||||
|
readonly s: bigint;
|
||||||
|
readonly recovery?: number;
|
||||||
|
assertValidity(): void;
|
||||||
|
addRecoveryBit(recovery: number): SignatureType;
|
||||||
|
hasHighS(): boolean;
|
||||||
|
normalizeS(): SignatureType;
|
||||||
|
recoverPublicKey(msgHash: Hex): ProjPointType<bigint>;
|
||||||
|
toCompactRawBytes(): Uint8Array;
|
||||||
|
toCompactHex(): string;
|
||||||
|
// DER-encoded
|
||||||
|
toDERRawBytes(): Uint8Array;
|
||||||
|
toDERHex(): string;
|
||||||
|
}
|
||||||
|
type SignatureConstructor = {
|
||||||
|
new (r: bigint, s: bigint): SignatureType;
|
||||||
|
fromCompact(hex: Hex): SignatureType;
|
||||||
|
fromDER(hex: Hex): SignatureType;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Example implementing [secq256k1](https://personaelabs.org/posts/spartan-ecdsa) (NOT secp256k1)
|
||||||
|
[cycle](https://zcash.github.io/halo2/background/curves.html#cycles-of-curves) of secp256k1 with Fp/N flipped.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { weierstrass } from '@noble/curves/abstract/weierstrass';
|
||||||
|
import { Field } from '@noble/curves/abstract/modular'; // finite field, mod arithmetics done over it
|
||||||
|
import { sha256 } from '@noble/hashes/sha256'; // 3rd-party sha256() of type utils.CHash, with blockLen/outputLen
|
||||||
|
import { hmac } from '@noble/hashes/hmac'; // 3rd-party hmac() that will accept sha256()
|
||||||
|
import { concatBytes, randomBytes } from '@noble/hashes/utils'; // 3rd-party utilities
|
||||||
|
const secq256k1 = weierstrass({
|
||||||
|
// secq256k1: cycle of secp256k1 with Fp/N flipped.
|
||||||
a: 0n,
|
a: 0n,
|
||||||
b: 7n,
|
b: 7n,
|
||||||
Fp: Fp(2n ** 256n - 2n ** 32n - 2n ** 9n - 2n ** 8n - 2n ** 7n - 2n ** 6n - 2n ** 4n - 1n),
|
Fp: Field(2n ** 256n - 432420386565659656852420866394968145599n),
|
||||||
n: 2n ** 256n - 432420386565659656852420866394968145599n,
|
n: 2n ** 256n - 2n ** 32n - 2n ** 9n - 2n ** 8n - 2n ** 7n - 2n ** 6n - 2n ** 4n - 1n,
|
||||||
Gx: 55066263022277343669578718895168534326250603453777594175500187360389116729240n,
|
Gx: 55066263022277343669578718895168534326250603453777594175500187360389116729240n,
|
||||||
Gy: 32670510020758816978083085130507043184471273380659243275938904335757337482424n,
|
Gy: 32670510020758816978083085130507043184471273380659243275938904335757337482424n,
|
||||||
hash: sha256,
|
hash: sha256,
|
||||||
hmac: (key: Uint8Array, ...msgs: Uint8Array[]) => hmac(sha256, key, concatBytes(...msgs)),
|
hmac: (key: Uint8Array, ...msgs: Uint8Array[]) => hmac(sha256, key, concatBytes(...msgs)),
|
||||||
randomBytes,
|
randomBytes,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// All curves expose same generic interface.
|
||||||
|
const priv = secq256k1.utils.randomPrivateKey();
|
||||||
|
secq256k1.getPublicKey(priv); // Convert private key to public.
|
||||||
|
const sig = secq256k1.sign(msg, priv); // Sign msg with private key.
|
||||||
|
secq256k1.verify(sig, msg, priv); // Verify if sig is correct.
|
||||||
|
|
||||||
|
const Point = secq256k1.ProjectivePoint;
|
||||||
|
const point = Point.BASE; // Elliptic curve Point class and BASE point static var.
|
||||||
|
point.add(point).equals(point.double()); // add(), equals(), double() methods
|
||||||
|
point.subtract(point).equals(Point.ZERO); // subtract() method, ZERO static var
|
||||||
|
point.negate(); // Flips point over x/y coordinate.
|
||||||
|
point.multiply(31415n); // Multiplication of Point by scalar.
|
||||||
|
|
||||||
|
point.assertValidity(); // Checks for being on-curve
|
||||||
|
point.toAffine(); // Converts to 2d affine xy coordinates
|
||||||
|
|
||||||
|
secq256k1.CURVE.n;
|
||||||
|
secq256k1.CURVE.Fp.mod();
|
||||||
|
secq256k1.CURVE.hash();
|
||||||
|
|
||||||
|
// precomputes
|
||||||
|
const fast = secq256k1.utils.precompute(8, Point.fromHex(someonesPubKey));
|
||||||
|
fast.multiply(privKey); // much faster ECDH now
|
||||||
```
|
```
|
||||||
|
|
||||||
- To initialize new curve, you must specify its variables, order (number of points on curve), field prime (over which the modular division would be done)
|
`weierstrass()` returns `CurveFn`:
|
||||||
- All curves expose same generic interface:
|
|
||||||
- `getPublicKey()`, `sign()`, `verify()` functions
|
```ts
|
||||||
- `Point` conforming to `Group` interface with add/multiply/double/negate/add/equals methods
|
type SignOpts = { lowS?: boolean; prehash?: boolean; extraEntropy: boolean | Uint8Array };
|
||||||
- `CURVE` object with curve variables like `Gx`, `Gy`, `Fp` (field), `n` (order)
|
type CurveFn = {
|
||||||
- `utils` object with `randomPrivateKey()`, `mod()`, `invert()` methods (`mod CURVE.P`)
|
CURVE: ReturnType<typeof validateOpts>;
|
||||||
- All arithmetics is done with JS bigints over finite fields, which is defined from `modular` sub-module
|
getPublicKey: (privateKey: PrivKey, isCompressed?: boolean) => Uint8Array;
|
||||||
- Many features require hashing, which is not provided. `@noble/hashes` can be used for this purpose.
|
getSharedSecret: (privateA: PrivKey, publicB: Hex, isCompressed?: boolean) => Uint8Array;
|
||||||
Any other library must conform to the CHash interface:
|
sign: (msgHash: Hex, privKey: PrivKey, opts?: SignOpts) => SignatureType;
|
||||||
```ts
|
verify: (
|
||||||
export type CHash = {
|
signature: Hex | SignatureType,
|
||||||
(message: Uint8Array): Uint8Array;
|
msgHash: Hex,
|
||||||
blockLen: number;
|
publicKey: Hex,
|
||||||
outputLen: number;
|
opts?: { lowS?: boolean; prehash?: boolean }
|
||||||
create(): any;
|
) => boolean;
|
||||||
|
ProjectivePoint: ProjectivePointConstructor;
|
||||||
|
Signature: SignatureConstructor;
|
||||||
|
utils: {
|
||||||
|
normPrivateKeyToScalar: (key: PrivKey) => bigint;
|
||||||
|
isValidPrivateKey(key: PrivKey): boolean;
|
||||||
|
randomPrivateKey: () => Uint8Array;
|
||||||
|
precompute: (windowSize?: number, point?: ProjPointType<bigint>) => ProjPointType<bigint>;
|
||||||
};
|
};
|
||||||
```
|
};
|
||||||
- w-ary non-adjacent form (wNAF) method with constant-time adjustments is used for point multiplication.
|
```
|
||||||
It is possible to enable precomputes for edwards & weierstrass curves.
|
|
||||||
Precomputes are calculated once (takes ~20-40ms), after that most `G` base point multiplications:
|
|
||||||
for example, `getPublicKey()`, `sign()` and similar methods - would be much faster.
|
|
||||||
Use `curve.utils.precompute()` to adjust precomputation window size
|
|
||||||
- You could use optional special params to tune performance:
|
|
||||||
- `Fp({sqrt})` square root calculation, used for point decompression
|
|
||||||
- `endo` endomorphism options for Koblitz curves
|
|
||||||
|
|
||||||
### abstract/edwards: Twisted Edwards curve
|
### abstract/edwards: Twisted Edwards curve
|
||||||
|
|
||||||
Twisted Edwards curve's formula is: ax² + y² = 1 + dx²y².
|
Twisted Edwards curve's formula is `ax² + y² = 1 + dx²y²`. You must specify `a`, `d`, field `Fp`, order `n`, cofactor `h`
|
||||||
|
and coordinates `Gx`, `Gy` of generator point.
|
||||||
|
|
||||||
- You must specify curve params `a`, `d`, field `Fp`, order `n`, cofactor `h` and coordinates `Gx`, `Gy` of generator point
|
For EdDSA signatures, `hash` param required. `adjustScalarBytes` which instructs how to change private scalars could be specified.
|
||||||
- For EdDSA signatures, params `hash` is also required. `adjustScalarBytes` which instructs how to change private scalars could be specified
|
|
||||||
|
|
||||||
```typescript
|
**Edwards points:**
|
||||||
|
|
||||||
|
1. Exported as `ExtendedPoint`
|
||||||
|
2. Represented in extended coordinates: (x, y, z, t) ∋ (x=x/z, y=y/z)
|
||||||
|
3. Use complete exception-free formulas for addition and doubling
|
||||||
|
4. Can be decoded/encoded from/to Uint8Array / hex strings using `ExtendedPoint.fromHex` and `ExtendedPoint#toRawBytes()`
|
||||||
|
5. Have `assertValidity()` which checks for being on-curve
|
||||||
|
6. Have `toAffine()` and `x` / `y` getters which convert to 2d xy affine coordinates
|
||||||
|
7. Have `isTorsionFree()`, `clearCofactor()` and `isSmallOrder()` utilities to handle torsions
|
||||||
|
|
||||||
|
```ts
|
||||||
|
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;
|
||||||
|
clearCofactor(): ExtPointType;
|
||||||
|
toAffine(iz?: bigint): AffinePoint<bigint>;
|
||||||
|
}
|
||||||
|
// Static methods of Extended Point with coordinates in X, Y, Z, T
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Example implementing edwards25519:
|
||||||
|
|
||||||
|
```ts
|
||||||
import { twistedEdwards } from '@noble/curves/abstract/edwards';
|
import { twistedEdwards } from '@noble/curves/abstract/edwards';
|
||||||
import { div } from '@noble/curves/abstract/modular';
|
import { Field, div } from '@noble/curves/abstract/modular';
|
||||||
import { sha512 } from '@noble/hashes/sha512';
|
import { sha512 } from '@noble/hashes/sha512';
|
||||||
|
|
||||||
|
const Fp = Field(2n ** 255n - 19n);
|
||||||
const ed25519 = twistedEdwards({
|
const ed25519 = twistedEdwards({
|
||||||
a: -1n,
|
a: -1n,
|
||||||
d: div(-121665n, 121666n, 2n ** 255n - 19n), // -121665n/121666n
|
d: Fp.div(-121665n, 121666n), // -121665n/121666n mod p
|
||||||
P: 2n ** 255n - 19n,
|
Fp,
|
||||||
n: 2n ** 252n + 27742317777372353535851937790883648493n,
|
n: 2n ** 252n + 27742317777372353535851937790883648493n,
|
||||||
h: 8n,
|
h: 8n,
|
||||||
Gx: 15112221349535400772501151409588531511454012693041857206046113283949847762202n,
|
Gx: 15112221349535400772501151409588531511454012693041857206046113283949847762202n,
|
||||||
@@ -174,34 +412,25 @@ const ed25519 = twistedEdwards({
|
|||||||
hash: sha512,
|
hash: sha512,
|
||||||
randomBytes,
|
randomBytes,
|
||||||
adjustScalarBytes(bytes) {
|
adjustScalarBytes(bytes) {
|
||||||
// optional in general, mandatory in ed25519
|
// optional; but mandatory in ed25519
|
||||||
bytes[0] &= 248;
|
bytes[0] &= 248;
|
||||||
bytes[31] &= 127;
|
bytes[31] &= 127;
|
||||||
bytes[31] |= 64;
|
bytes[31] |= 64;
|
||||||
return bytes;
|
return bytes;
|
||||||
},
|
},
|
||||||
} as const);
|
} as const);
|
||||||
const key = ed25519.utils.randomPrivateKey();
|
|
||||||
const pub = ed25519.getPublicKey(key);
|
|
||||||
const msg = new TextEncoder().encode('hello world'); // strings not accepted, must be Uint8Array
|
|
||||||
const sig = ed25519.sign(msg, key);
|
|
||||||
ed25519.verify(sig, msg, pub) === true;
|
|
||||||
```
|
```
|
||||||
|
|
||||||
`twistedEdwards()` returns `CurveFn` of following type:
|
`twistedEdwards()` returns `CurveFn` of following type:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
export type CurveFn = {
|
type CurveFn = {
|
||||||
CURVE: ReturnType<typeof validateOpts>;
|
CURVE: ReturnType<typeof validateOpts>;
|
||||||
getPublicKey: (privateKey: PrivKey, isCompressed?: boolean) => Uint8Array;
|
getPublicKey: (privateKey: Hex) => Uint8Array;
|
||||||
sign: (message: Hex, privateKey: Hex) => Uint8Array;
|
sign: (message: Hex, privateKey: Hex, context?: Hex) => Uint8Array;
|
||||||
verify: (sig: SigType, message: Hex, publicKey: PubKey) => boolean;
|
verify: (sig: SigType, message: Hex, publicKey: Hex, context?: Hex) => boolean;
|
||||||
Point: PointConstructor;
|
ExtendedPoint: ExtPointConstructor;
|
||||||
ExtendedPoint: ExtendedPointConstructor;
|
|
||||||
Signature: SignatureConstructor;
|
|
||||||
utils: {
|
utils: {
|
||||||
mod: (a: bigint, b?: bigint) => bigint;
|
|
||||||
invert: (number: bigint, modulo?: bigint) => bigint;
|
|
||||||
randomPrivateKey: () => Uint8Array;
|
randomPrivateKey: () => Uint8Array;
|
||||||
getExtendedPublicKey: (key: PrivKey) => {
|
getExtendedPublicKey: (key: PrivKey) => {
|
||||||
head: Uint8Array;
|
head: Uint8Array;
|
||||||
@@ -216,26 +445,20 @@ export type CurveFn = {
|
|||||||
|
|
||||||
### abstract/montgomery: Montgomery curve
|
### abstract/montgomery: Montgomery curve
|
||||||
|
|
||||||
For now the module only contains methods for x-only ECDH on Curve25519 / Curve448 from RFC7748.
|
The module contains methods for x-only ECDH on Curve25519 / Curve448 from RFC7748. Proper Elliptic Curve Points are not implemented yet.
|
||||||
|
|
||||||
Proper Elliptic Curve Points are not implemented yet.
|
You must specify curve params `Fp`, `a`, `Gu` coordinate of u, `montgomeryBits` and `nByteLength`.
|
||||||
|
|
||||||
You must specify curve field, `a24` special variable, `montgomeryBits`, `nByteLength`, and coordinate `u` of generator point.
|
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { montgomery } from '@noble/curves/abstract/montgomery';
|
import { montgomery } from '@noble/curves/abstract/montgomery';
|
||||||
|
|
||||||
const x25519 = montgomery({
|
const x25519 = montgomery({
|
||||||
P: 2n ** 255n - 19n,
|
Fp: Field(2n ** 255n - 19n),
|
||||||
a24: 121665n, // TODO: change to a
|
a: 486662n,
|
||||||
|
Gu: 9n,
|
||||||
montgomeryBits: 255,
|
montgomeryBits: 255,
|
||||||
nByteLength: 32,
|
nByteLength: 32,
|
||||||
Gu: '0900000000000000000000000000000000000000000000000000000000000000',
|
// Optional param
|
||||||
|
|
||||||
// Optional params
|
|
||||||
powPminus2: (x: bigint): bigint => {
|
|
||||||
return mod.pow(x, P - 2, P);
|
|
||||||
},
|
|
||||||
adjustScalarBytes(bytes) {
|
adjustScalarBytes(bytes) {
|
||||||
bytes[0] &= 248;
|
bytes[0] &= 248;
|
||||||
bytes[31] &= 127;
|
bytes[31] &= 127;
|
||||||
@@ -245,104 +468,133 @@ const x25519 = montgomery({
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
### abstract/weierstrass: Short Weierstrass curve
|
### abstract/hash-to-curve: Hashing strings to curve points
|
||||||
|
|
||||||
Short Weierstrass curve's formula is: y² = x³ + ax + b. Uses deterministic ECDSA from RFC6979. You can also specify `extraEntropy` in `sign()`.
|
The module allows to hash arbitrary strings to elliptic curve points. Implements [hash-to-curve v16](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16).
|
||||||
|
|
||||||
- You must specify curve params: `a`, `b`, field `Fp`, order `n`, cofactor `h` and coordinates `Gx`, `Gy` of generator point
|
Every curve has exported `hashToCurve` and `encodeToCurve` methods:
|
||||||
- For ECDSA, you must specify `hash`, `hmac`. It is also possible to recover keys from signatures
|
|
||||||
- For ECDH, use `getSharedSecret(privKeyA, pubKeyB)`
|
|
||||||
- Optional params are `lowS` (default value) and `endo` (endomorphism)
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { Fp } from '@noble/curves/abstract/modular';
|
|
||||||
import { weierstrass } from '@noble/curves/abstract/weierstrass'; // Short Weierstrass curve
|
|
||||||
import { sha256 } from '@noble/hashes/sha256';
|
|
||||||
import { hmac } from '@noble/hashes/hmac';
|
|
||||||
import { concatBytes, randomBytes } from '@noble/hashes/utils';
|
|
||||||
|
|
||||||
const secp256k1 = weierstrass({
|
|
||||||
a: 0n,
|
|
||||||
b: 7n,
|
|
||||||
Fp: Fp(2n ** 256n - 2n ** 32n - 2n ** 9n - 2n ** 8n - 2n ** 7n - 2n ** 6n - 2n ** 4n - 1n),
|
|
||||||
n: 2n ** 256n - 432420386565659656852420866394968145599n,
|
|
||||||
Gx: 55066263022277343669578718895168534326250603453777594175500187360389116729240n,
|
|
||||||
Gy: 32670510020758816978083085130507043184471273380659243275938904335757337482424n,
|
|
||||||
hash: sha256,
|
|
||||||
hmac: (k: Uint8Array, ...msgs: Uint8Array[]) => hmac(sha256, key, concatBytes(...msgs)),
|
|
||||||
randomBytes,
|
|
||||||
|
|
||||||
// Optional params
|
|
||||||
h: 1n, // Cofactor
|
|
||||||
lowS: true, // Allow only low-S signatures by default in sign() and verify()
|
|
||||||
endo: {
|
|
||||||
// Endomorphism options for Koblitz curve
|
|
||||||
// Beta param
|
|
||||||
beta: 0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501een,
|
|
||||||
// Split scalar k into k1, k2
|
|
||||||
splitScalar: (k: bigint) => {
|
|
||||||
// return { k1neg: true, k1: 512n, k2neg: false, k2: 448n };
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Usage
|
|
||||||
const key = secp256k1.utils.randomPrivateKey();
|
|
||||||
const pub = secp256k1.getPublicKey(key);
|
|
||||||
const msg = randomBytes(32);
|
|
||||||
const sig = secp256k1.sign(msg, key);
|
|
||||||
secp256k1.verify(sig, msg, pub); // true
|
|
||||||
sig.recoverPublicKey(msg); // == pub
|
|
||||||
const someonesPubkey = secp256k1.getPublicKey(secp256k1.utils.randomPrivateKey());
|
|
||||||
const shared = secp256k1.getSharedSecret(key, someonesPubkey);
|
|
||||||
```
|
|
||||||
|
|
||||||
`weierstrass()` returns `CurveFn`:
|
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
export type CurveFn = {
|
import { hashToCurve, encodeToCurve } from '@noble/curves/secp256k1';
|
||||||
CURVE: ReturnType<typeof validateOpts>;
|
import { randomBytes } from '@noble/hashes/utils';
|
||||||
getPublicKey: (privateKey: PrivKey, isCompressed?: boolean) => Uint8Array;
|
hashToCurve('0102abcd');
|
||||||
getSharedSecret: (privateA: PrivKey, publicB: PubKey, isCompressed?: boolean) => Uint8Array;
|
console.log(hashToCurve(randomBytes()));
|
||||||
sign: (msgHash: Hex, privKey: PrivKey, opts?: SignOpts) => SignatureType;
|
console.log(encodeToCurve(randomBytes()));
|
||||||
verify: (
|
|
||||||
signature: Hex | SignatureType,
|
import { bls12_381 } from '@noble/curves/bls12-381';
|
||||||
msgHash: Hex,
|
bls12_381.G1.hashToCurve(randomBytes(), { DST: 'another' });
|
||||||
publicKey: PubKey,
|
bls12_381.G2.hashToCurve(randomBytes(), { DST: 'custom' });
|
||||||
opts?: { lowS?: boolean }
|
```
|
||||||
) => boolean;
|
|
||||||
Point: PointConstructor;
|
If you need low-level methods from spec:
|
||||||
ProjectivePoint: ProjectivePointConstructor;
|
|
||||||
Signature: SignatureConstructor;
|
`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.
|
||||||
utils: {
|
|
||||||
mod: (a: bigint) => bigint;
|
Hash must conform to `CHash` interface (see [weierstrass section](#abstractweierstrass-short-weierstrass-curve)).
|
||||||
invert: (number: bigint) => bigint;
|
|
||||||
isValidPrivateKey(privateKey: PrivKey): boolean;
|
```ts
|
||||||
hashToPrivateKey: (hash: Hex) => Uint8Array;
|
function expand_message_xmd(
|
||||||
randomPrivateKey: () => Uint8Array;
|
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}`.
|
||||||
|
- `p` is field prime, m=field extension (1 for prime fields)
|
||||||
|
- `k` is security target in bits (e.g. 128).
|
||||||
|
- `expand` should be `xmd` for SHA2, SHA3, BLAKE; `xof` for SHAKE, BLAKE-XOF
|
||||||
|
- `hash` conforming to `utils.CHash` interface, with `outputLen` / `blockLen` props
|
||||||
|
- Returns `[u_0, ..., u_(count - 1)]`, a list of field elements.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
function hash_to_field(msg: Uint8Array, count: number, options: Opts): bigint[][];
|
||||||
|
```
|
||||||
|
|
||||||
|
### abstract/poseidon: Poseidon hash
|
||||||
|
|
||||||
|
Implements [Poseidon](https://www.poseidon-hash.info) ZK-friendly hash.
|
||||||
|
|
||||||
|
There are many poseidon variants with different constants.
|
||||||
|
We don't provide them: you should construct them manually.
|
||||||
|
The only variant provided resides in `stark` module: inspect it for proper usage.
|
||||||
|
|
||||||
|
```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
|
### abstract/bls
|
||||||
|
|
||||||
Modular arithmetics utilities.
|
The module abstracts BLS (Barreto-Lynn-Scott) primitives. In theory you should be able to write BLS12-377, BLS24,
|
||||||
|
and others with it.
|
||||||
|
|
||||||
```typescript
|
### abstract/modular: Modular arithmetics utilities
|
||||||
import { mod, invert, div, invertBatch, sqrt, Fp } from '@noble/curves/abstract/modular';
|
|
||||||
mod(21n, 10n); // 21 mod 10 == 1n; fixed version of 21 % 10
|
```ts
|
||||||
invert(17n, 10n); // invert(17) mod 10; modular multiplicative inverse
|
import * as mod from '@noble/curves/abstract/modular';
|
||||||
div(5n, 17n, 10n); // 5/17 mod 10 == 5 * invert(17) mod 10; division
|
const fp = mod.Field(2n ** 255n - 19n); // Finite field over 2^255-19
|
||||||
invertBatch([1n, 2n, 4n], 21n); // => [1n, 11n, 16n] in one inversion
|
fp.mul(591n, 932n); // multiplication
|
||||||
sqrt(21n, 73n); // √21 mod 73; square root
|
fp.pow(481n, 11024858120n); // exponentiation
|
||||||
const fp = Fp(2n ** 255n - 19n); // Finite field over 2^255-19
|
fp.div(5n, 17n); // division: 5/17 mod 2^255-19 == 5 * invert(17)
|
||||||
fp.mul(591n, 932n);
|
fp.sqrt(21n); // square root
|
||||||
fp.pow(481n, 11024858120n);
|
|
||||||
|
// Generic non-FP utils are also available
|
||||||
|
mod.mod(21n, 10n); // 21 mod 10 == 1n; fixed version of 21 % 10
|
||||||
|
mod.invert(17n, 10n); // invert(17) mod 10; modular multiplicative inverse
|
||||||
|
mod.invertBatch([1n, 2n, 4n], 21n); // => [1n, 11n, 16n] in one inversion
|
||||||
```
|
```
|
||||||
|
|
||||||
### abstract/utils
|
#### Creating private keys from hashes
|
||||||
|
|
||||||
```typescript
|
Suppose you have `sha256(something)` (e.g. from HMAC) and you want to make a private key from it.
|
||||||
|
Even though p256 or secp256k1 may have 32-byte private keys,
|
||||||
|
and sha256 output is also 32-byte, you can't just use it and reduce it modulo `CURVE.n`.
|
||||||
|
|
||||||
|
Doing so will make the result key [biased](https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/).
|
||||||
|
|
||||||
|
To avoid the bias, we implement FIPS 186 B.4.1, which allows to take arbitrary
|
||||||
|
byte array and produce valid scalars / private keys with bias being neglible.
|
||||||
|
|
||||||
|
Use [hash-to-curve](#abstracthash-to-curve-hashing-strings-to-curve-points) if you need
|
||||||
|
hashing to **public keys**; the function in the module instead operates on **private keys**.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { p256 } from '@noble/curves/p256';
|
||||||
|
import { sha256 } from '@noble/hashes/sha256';
|
||||||
|
import { hkdf } from '@noble/hashes/hkdf';
|
||||||
|
const someKey = new Uint8Array(32).fill(2); // Needs to actually be random, not .fill(2)
|
||||||
|
const derived = hkdf(sha256, someKey, undefined, 'application', 40); // 40 bytes
|
||||||
|
const validPrivateKey = mod.hashToPrivateScalar(derived, p256.CURVE.n);
|
||||||
|
```
|
||||||
|
|
||||||
|
### abstract/utils: General utilities
|
||||||
|
|
||||||
|
```ts
|
||||||
import * as utils from '@noble/curves/abstract/utils';
|
import * as utils from '@noble/curves/abstract/utils';
|
||||||
|
|
||||||
utils.bytesToHex(Uint8Array.from([0xde, 0xad, 0xbe, 0xef]));
|
utils.bytesToHex(Uint8Array.from([0xde, 0xad, 0xbe, 0xef]));
|
||||||
@@ -350,67 +602,157 @@ utils.hexToBytes('deadbeef');
|
|||||||
utils.hexToNumber();
|
utils.hexToNumber();
|
||||||
utils.bytesToNumberBE(Uint8Array.from([0xde, 0xad, 0xbe, 0xef]));
|
utils.bytesToNumberBE(Uint8Array.from([0xde, 0xad, 0xbe, 0xef]));
|
||||||
utils.bytesToNumberLE(Uint8Array.from([0xde, 0xad, 0xbe, 0xef]));
|
utils.bytesToNumberLE(Uint8Array.from([0xde, 0xad, 0xbe, 0xef]));
|
||||||
utils.numberToBytesBE(123n);
|
utils.numberToBytesBE(123n, 32);
|
||||||
utils.numberToBytesLE(123n);
|
utils.numberToBytesLE(123n, 64);
|
||||||
utils.numberToHexUnpadded(123n);
|
utils.numberToHexUnpadded(123n);
|
||||||
utils.concatBytes(Uint8Array.from([0xde, 0xad]), Uint8Array.from([0xbe, 0xef]));
|
utils.concatBytes(Uint8Array.from([0xde, 0xad]), Uint8Array.from([0xbe, 0xef]));
|
||||||
utils.nLength(255n);
|
utils.nLength(255n);
|
||||||
utils.hashToPrivateScalar(sha512_of_something, secp256r1.n);
|
|
||||||
utils.equalBytes(Uint8Array.from([0xde]), Uint8Array.from([0xde]));
|
utils.equalBytes(Uint8Array.from([0xde]), Uint8Array.from([0xde]));
|
||||||
```
|
```
|
||||||
|
|
||||||
## Security
|
## Security
|
||||||
|
|
||||||
The library had no prior security audit.
|
The library had no prior security audit. The library has been fuzzed by [Guido Vranken's cryptofuzz](https://github.com/guidovranken/cryptofuzz): you can run the fuzzer by yourself to check it.
|
||||||
|
|
||||||
[Timing attack](https://en.wikipedia.org/wiki/Timing_attack) considerations: _JIT-compiler_ and _Garbage Collector_ make "constant time" extremely hard to achieve in a scripting language. Which means _any other JS library can't have constant-timeness_. Even statically typed Rust, a language without GC, [makes it harder to achieve constant-time](https://www.chosenplaintext.ca/open-source/rust-timing-shield/security) for some cases. If your goal is absolute security, don't use any JS lib — including bindings to native ones. Use low-level libraries & languages. Nonetheless we're targetting algorithmic constant time.
|
[Timing attack](https://en.wikipedia.org/wiki/Timing_attack) considerations: we are using non-CT bigints. However, _JIT-compiler_ and _Garbage Collector_ make "constant time" extremely hard to achieve in a scripting language. Which means _any other JS library can't have constant-timeness_. Even statically typed Rust, a language without GC, [makes it harder to achieve constant-time](https://www.chosenplaintext.ca/open-source/rust-timing-shield/security) for some cases. If your goal is absolute security, don't use any JS lib — including bindings to native ones. Use low-level libraries & languages. Nonetheless we're targetting algorithmic constant time.
|
||||||
|
|
||||||
We consider infrastructure attacks like rogue NPM modules very important; that's why it's crucial to minimize the amount of 3rd-party dependencies & native bindings. If your app uses 500 dependencies, any dep could get hacked and you'll be downloading malware with every `npm install`. Our goal is to minimize this attack vector.
|
We consider infrastructure attacks like rogue NPM modules very important; that's why it's crucial to minimize the amount of 3rd-party dependencies & native bindings. If your app uses 500 dependencies, any dep could get hacked and you'll be downloading malware with every `npm install`. Our goal is to minimize this attack vector. As for devDependencies used by the library:
|
||||||
|
|
||||||
|
- `@scure` base, bip32, bip39 (used in tests), micro-bmark (benchmark), micro-should (testing) are developed by us
|
||||||
|
and follow the same practices such as: minimal library size, auditability, signed releases
|
||||||
|
- prettier (linter), fast-check (property-based testing),
|
||||||
|
typescript versions are locked and rarely updated. Every update is checked with `npm-diff`.
|
||||||
|
The packages are big, which makes it hard to audit their source code thoroughly and fully.
|
||||||
|
- They are only used if you clone the git repo and want to add some feature to it. End-users won't use them.
|
||||||
|
|
||||||
## Speed
|
## Speed
|
||||||
|
|
||||||
Benchmark results on Apple M2 with node v18.10:
|
Benchmark results on Apple M2 with node v19:
|
||||||
|
|
||||||
```
|
```
|
||||||
getPublicKey
|
secp256k1
|
||||||
secp256k1 x 5,241 ops/sec @ 190μs/op
|
init x 58 ops/sec @ 17ms/op
|
||||||
P256 x 7,993 ops/sec @ 125μs/op
|
getPublicKey x 5,640 ops/sec @ 177μs/op
|
||||||
P384 x 3,819 ops/sec @ 261μs/op
|
sign x 3,909 ops/sec @ 255μs/op
|
||||||
P521 x 2,074 ops/sec @ 481μs/op
|
verify x 780 ops/sec @ 1ms/op
|
||||||
ed25519 x 8,390 ops/sec @ 119μs/op
|
getSharedSecret x 465 ops/sec @ 2ms/op
|
||||||
ed448 x 3,224 ops/sec @ 310μs/op
|
recoverPublicKey x 740 ops/sec @ 1ms/op
|
||||||
sign
|
schnorr.sign x 597 ops/sec @ 1ms/op
|
||||||
secp256k1 x 3,934 ops/sec @ 254μs/op
|
schnorr.verify x 775 ops/sec @ 1ms/op
|
||||||
P256 x 5,327 ops/sec @ 187μs/op
|
|
||||||
P384 x 2,728 ops/sec @ 366μs/op
|
P256
|
||||||
P521 x 1,594 ops/sec @ 626μs/op
|
init x 31 ops/sec @ 31ms/op
|
||||||
ed25519 x 4,233 ops/sec @ 236μs/op
|
getPublicKey x 5,607 ops/sec @ 178μs/op
|
||||||
ed448 x 1,561 ops/sec @ 640μs/op
|
sign x 3,930 ops/sec @ 254μs/op
|
||||||
verify
|
verify x 540 ops/sec @ 1ms/op
|
||||||
secp256k1 x 731 ops/sec @ 1ms/op
|
|
||||||
P256 x 806 ops/sec @ 1ms/op
|
P384
|
||||||
P384 x 353 ops/sec @ 2ms/op
|
init x 15 ops/sec @ 63ms/op
|
||||||
P521 x 171 ops/sec @ 5ms/op
|
getPublicKey x 2,622 ops/sec @ 381μs/op
|
||||||
ed25519 x 860 ops/sec @ 1ms/op
|
sign x 1,913 ops/sec @ 522μs/op
|
||||||
ed448 x 313 ops/sec @ 3ms/op
|
verify x 222 ops/sec @ 4ms/op
|
||||||
getSharedSecret
|
|
||||||
secp256k1 x 445 ops/sec @ 2ms/op
|
P521
|
||||||
recoverPublicKey
|
init x 8 ops/sec @ 119ms/op
|
||||||
secp256k1 x 732 ops/sec @ 1ms/op
|
getPublicKey x 1,371 ops/sec @ 729μs/op
|
||||||
==== bls12-381 ====
|
sign x 1,090 ops/sec @ 917μs/op
|
||||||
getPublicKey x 817 ops/sec @ 1ms/op
|
verify x 118 ops/sec @ 8ms/op
|
||||||
sign x 50 ops/sec @ 19ms/op
|
|
||||||
|
ed25519
|
||||||
|
init x 47 ops/sec @ 20ms/op
|
||||||
|
getPublicKey x 9,414 ops/sec @ 106μs/op
|
||||||
|
sign x 4,516 ops/sec @ 221μs/op
|
||||||
|
verify x 912 ops/sec @ 1ms/op
|
||||||
|
|
||||||
|
ed448
|
||||||
|
init x 17 ops/sec @ 56ms/op
|
||||||
|
getPublicKey x 3,363 ops/sec @ 297μs/op
|
||||||
|
sign x 1,615 ops/sec @ 619μs/op
|
||||||
|
verify x 319 ops/sec @ 3ms/op
|
||||||
|
|
||||||
|
stark
|
||||||
|
init x 35 ops/sec @ 28ms/op
|
||||||
|
pedersen x 884 ops/sec @ 1ms/op
|
||||||
|
poseidon x 8,598 ops/sec @ 116μs/op
|
||||||
|
verify x 528 ops/sec @ 1ms/op
|
||||||
|
|
||||||
|
ecdh
|
||||||
|
├─x25519 x 1,337 ops/sec @ 747μs/op
|
||||||
|
├─secp256k1 x 461 ops/sec @ 2ms/op
|
||||||
|
├─P256 x 441 ops/sec @ 2ms/op
|
||||||
|
├─P384 x 179 ops/sec @ 5ms/op
|
||||||
|
├─P521 x 93 ops/sec @ 10ms/op
|
||||||
|
└─x448 x 496 ops/sec @ 2ms/op
|
||||||
|
|
||||||
|
bls12-381
|
||||||
|
init x 32 ops/sec @ 30ms/op
|
||||||
|
getPublicKey 1-bit x 858 ops/sec @ 1ms/op
|
||||||
|
getPublicKey x 858 ops/sec @ 1ms/op
|
||||||
|
sign x 49 ops/sec @ 20ms/op
|
||||||
verify x 34 ops/sec @ 28ms/op
|
verify x 34 ops/sec @ 28ms/op
|
||||||
pairing x 89 ops/sec @ 11ms/op
|
pairing x 94 ops/sec @ 10ms/op
|
||||||
==== stark ====
|
aggregatePublicKeys/8 x 116 ops/sec @ 8ms/op
|
||||||
pedersen
|
aggregatePublicKeys/32 x 31 ops/sec @ 31ms/op
|
||||||
old x 85 ops/sec @ 11ms/op
|
aggregatePublicKeys/128 x 7 ops/sec @ 125ms/op
|
||||||
noble x 1,216 ops/sec @ 822μs/op
|
aggregateSignatures/8 x 45 ops/sec @ 22ms/op
|
||||||
verify
|
aggregateSignatures/32 x 11 ops/sec @ 84ms/op
|
||||||
old x 302 ops/sec @ 3ms/op
|
aggregateSignatures/128 x 3 ops/sec @ 332ms/opp
|
||||||
noble x 698 ops/sec @ 1ms/op
|
|
||||||
|
hash-to-curve
|
||||||
|
hash_to_field x 850,340 ops/sec @ 1μs/op
|
||||||
|
hashToCurve
|
||||||
|
├─secp256k1 x 1,850 ops/sec @ 540μs/op
|
||||||
|
├─P256 x 3,352 ops/sec @ 298μs/op
|
||||||
|
├─P384 x 1,367 ops/sec @ 731μs/op
|
||||||
|
├─P521 x 691 ops/sec @ 1ms/op
|
||||||
|
├─ed25519 x 2,492 ops/sec @ 401μs/op
|
||||||
|
└─ed448 x 1,045 ops/sec @ 956μs/op
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
Article about some of library's features: [Learning fast elliptic-curve cryptography](https://paulmillr.com/posts/noble-secp256k1-fast-ecc/). Elliptic curve calculator: [paulmillr.com/ecc](https://paulmillr.com/ecc)
|
||||||
|
|
||||||
|
- secp256k1
|
||||||
|
- [btc-signer](https://github.com/paulmillr/micro-btc-signer), [eth-signer](https://github.com/paulmillr/micro-eth-signer)
|
||||||
|
- ed25519
|
||||||
|
- [sol-signer](https://github.com/paulmillr/micro-sol-signer)
|
||||||
|
- BLS12-381
|
||||||
|
- Check out `bls12-381.ts` for articles about the curve
|
||||||
|
- Threshold sigs demo [genthresh.com](https://genthresh.com)
|
||||||
|
- BBS signatures [github.com/Wind4Greg/BBS-Draft-Checks](https://github.com/Wind4Greg/BBS-Draft-Checks) following [draft-irtf-cfrg-bbs-signatures-latest](https://identity.foundation/bbs-signature/draft-irtf-cfrg-bbs-signatures.html)
|
||||||
|
|
||||||
|
## Upgrading
|
||||||
|
|
||||||
|
If you're coming from single-feature 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`
|
||||||
|
|
||||||
|
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
|
## Contributing & testing
|
||||||
|
|
||||||
1. Clone the repository
|
1. Clone the repository
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
|
|
||||||
| Version | Supported |
|
| Version | Supported |
|
||||||
| ------- | ------------------ |
|
| ------- | ------------------ |
|
||||||
| >=0.5.0 | :white_check_mark: |
|
| >=1.0.0 | :white_check_mark: |
|
||||||
| <0.5.0 | :x: |
|
| <1.0.0 | :x: |
|
||||||
|
|
||||||
## Reporting a Vulnerability
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
|||||||
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 '../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 '../p256.js';
|
||||||
|
import { P384 } from '../p384.js';
|
||||||
|
import { P521 } from '../p521.js';
|
||||||
|
import { ed25519 } from '../ed25519.js';
|
||||||
|
import { ed448 } from '../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();
|
||||||
|
}
|
||||||
|
});
|
||||||
19
benchmark/ecdh.js
Normal file
19
benchmark/ecdh.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { run, mark, compare, utils } from 'micro-bmark';
|
||||||
|
import { generateData } from './_shared.js';
|
||||||
|
import { secp256k1 } from '../secp256k1.js';
|
||||||
|
import { P256 } from '../p256.js';
|
||||||
|
import { P384 } from '../p384.js';
|
||||||
|
import { P521 } from '../p521.js';
|
||||||
|
import { x25519 } from '../ed25519.js';
|
||||||
|
import { x448 } from '../ed448.js';
|
||||||
|
|
||||||
|
run(async () => {
|
||||||
|
const curves = { x25519, secp256k1, P256, P384, P521, x448 };
|
||||||
|
const fns = {};
|
||||||
|
for (let [k, c] of Object.entries(curves)) {
|
||||||
|
const pubB = c.getPublicKey(c.utils.randomPrivateKey());
|
||||||
|
const privA = c.utils.randomPrivateKey();
|
||||||
|
fns[k] = () => c.getSharedSecret(privA, pubB);
|
||||||
|
}
|
||||||
|
await compare('ecdh', 1000, fns);
|
||||||
|
});
|
||||||
29
benchmark/hash-to-curve.js
Normal file
29
benchmark/hash-to-curve.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { run, mark, utils } from 'micro-bmark';
|
||||||
|
import { hash_to_field } from '../abstract/hash-to-curve.js';
|
||||||
|
import { hashToPrivateScalar } from '../abstract/modular.js';
|
||||||
|
import { randomBytes } from '@noble/hashes/utils';
|
||||||
|
import { sha256 } from '@noble/hashes/sha256';
|
||||||
|
// import { generateData } from './_shared.js';
|
||||||
|
import { hashToCurve as secp256k1 } from '../secp256k1.js';
|
||||||
|
import { hashToCurve as P256 } from '../p256.js';
|
||||||
|
import { hashToCurve as P384 } from '../p384.js';
|
||||||
|
import { hashToCurve as P521 } from '../p521.js';
|
||||||
|
import { hashToCurve as ed25519 } from '../ed25519.js';
|
||||||
|
import { hashToCurve as ed448 } from '../ed448.js';
|
||||||
|
import { utf8ToBytes } from '../abstract/utils.js';
|
||||||
|
|
||||||
|
const N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141n;
|
||||||
|
run(async () => {
|
||||||
|
const rand = randomBytes(40);
|
||||||
|
await mark('hashToPrivateScalar', 1000000, () => hashToPrivateScalar(rand, N));
|
||||||
|
// - p, the characteristic of F
|
||||||
|
// - m, the extension degree of F, m >= 1
|
||||||
|
// - L = ceil((ceil(log2(p)) + k) / 8), where k is the security of suite (e.g. 128)
|
||||||
|
await mark('hash_to_field', 1000000, () =>
|
||||||
|
hash_to_field(rand, 1, { DST: 'secp256k1', hash: sha256, p: N, m: 1, k: 128 })
|
||||||
|
);
|
||||||
|
const msg = utf8ToBytes('message');
|
||||||
|
for (let [title, fn] of Object.entries({ secp256k1, P256, P384, P521, ed25519, ed448 })) {
|
||||||
|
await mark(`hashToCurve ${title}`, 1000, () => fn(msg));
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -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();
|
|
||||||
}
|
|
||||||
@@ -12,13 +12,11 @@
|
|||||||
"author": "",
|
"author": "",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"micro-bmark": "0.2.0"
|
"micro-bmark": "0.3.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@noble/bls12-381": "^1.4.0",
|
|
||||||
"@noble/ed25519": "^1.7.1",
|
|
||||||
"@noble/hashes": "^1.1.5",
|
"@noble/hashes": "^1.1.5",
|
||||||
"@noble/secp256k1": "^1.7.0",
|
"@starkware-industries/starkware-crypto-utils": "^0.0.2",
|
||||||
"@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 '../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 '../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();
|
||||||
|
});
|
||||||
178
package-lock.json
generated
Normal file
178
package-lock.json
generated
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
{
|
||||||
|
"name": "@noble/curves",
|
||||||
|
"version": "0.7.2",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "@noble/curves",
|
||||||
|
"version": "0.7.2",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@noble/hashes": "1.2.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@scure/bip32": "~1.1.5",
|
||||||
|
"@scure/bip39": "~1.1.1",
|
||||||
|
"@types/node": "18.11.3",
|
||||||
|
"fast-check": "3.0.0",
|
||||||
|
"micro-bmark": "0.3.1",
|
||||||
|
"micro-should": "0.4.0",
|
||||||
|
"prettier": "2.8.3",
|
||||||
|
"typescript": "4.7.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@noble/hashes": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@noble/secp256k1": {
|
||||||
|
"version": "1.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.1.tgz",
|
||||||
|
"integrity": "sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@scure/base": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@scure/bip32": {
|
||||||
|
"version": "1.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.1.5.tgz",
|
||||||
|
"integrity": "sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"@noble/hashes": "~1.2.0",
|
||||||
|
"@noble/secp256k1": "~1.7.0",
|
||||||
|
"@scure/base": "~1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@scure/bip39": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"@noble/hashes": "~1.2.0",
|
||||||
|
"@scure/base": "~1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/node": {
|
||||||
|
"version": "18.11.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.3.tgz",
|
||||||
|
"integrity": "sha512-fNjDQzzOsZeKZu5NATgXUPsaFaTxeRgFXoosrHivTl8RGeV733OLawXsGfEk9a8/tySyZUyiZ6E8LcjPFZ2y1A==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/fast-check": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-uujtrFJEQQqnIMO52ARwzPcuV4omiL1OJBUBLE9WnNFeu0A97sREXDOmCIHY+Z6KLVcemUf09rWr0q0Xy/Y/Ew==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"pure-rand": "^5.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/fast-check"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/micro-bmark": {
|
||||||
|
"version": "0.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/micro-bmark/-/micro-bmark-0.3.1.tgz",
|
||||||
|
"integrity": "sha512-bNaKObD4yPAAPrpEqp5jO6LJ2sEFgLoFSmRjEY809mJ62+2AehI/K3+RlVpN3Oo92RHpgC2RQhj6b1Tb4dmo+w==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/micro-should": {
|
||||||
|
"version": "0.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/micro-should/-/micro-should-0.4.0.tgz",
|
||||||
|
"integrity": "sha512-Vclj8yrngSYc9Y3dL2C+AdUlTkyx/syWc4R7LYfk4h7+icfF0DoUBGjjUIaEDzZA19RzoI+Hg8rW9IRoNGP0tQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/prettier": {
|
||||||
|
"version": "2.8.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.3.tgz",
|
||||||
|
"integrity": "sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"prettier": "bin-prettier.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.13.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pure-rand": {
|
||||||
|
"version": "5.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-5.0.5.tgz",
|
||||||
|
"integrity": "sha512-BwQpbqxSCBJVpamI6ydzcKqyFmnd5msMWUGvzXLm1aXvusbbgkbOto/EUPM00hjveJEaJtdbhUjKSzWRhQVkaw==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://github.com/sponsors/dubzzz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/fast-check"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/typescript": {
|
||||||
|
"version": "4.7.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.3.tgz",
|
||||||
|
"integrity": "sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"tsc": "bin/tsc",
|
||||||
|
"tsserver": "bin/tsserver"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.2.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
176
package.json
176
package.json
@@ -1,12 +1,18 @@
|
|||||||
{
|
{
|
||||||
"name": "@noble/curves",
|
"name": "@noble/curves",
|
||||||
"version": "0.5.0",
|
"version": "0.7.3",
|
||||||
"description": "Minimal, auditable JS implementation of elliptic curve cryptography",
|
"description": "Minimal, auditable JS implementation of elliptic curve cryptography",
|
||||||
"files": [
|
"files": [
|
||||||
"lib"
|
"abstract",
|
||||||
|
"esm",
|
||||||
|
"src",
|
||||||
|
"*.js",
|
||||||
|
"*.js.map",
|
||||||
|
"*.d.ts",
|
||||||
|
"*.d.ts.map"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"bench": "node benchmark/index.js",
|
"bench": "cd benchmark; node secp256k1.js; node curves.js; node ecdh.js; node stark.js; node bls.js",
|
||||||
"build": "tsc && tsc -p tsconfig.esm.json",
|
"build": "tsc && tsc -p tsconfig.esm.json",
|
||||||
"build:release": "rollup -c rollup.config.js",
|
"build:release": "rollup -c rollup.config.js",
|
||||||
"lint": "prettier --check 'src/**/*.{js,ts}' 'test/*.js'",
|
"lint": "prettier --check 'src/**/*.{js,ts}' 'test/*.js'",
|
||||||
@@ -21,142 +27,134 @@
|
|||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@noble/hashes": "1.1.5"
|
"@noble/hashes": "1.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-node-resolve": "13.3.0",
|
"@scure/bip32": "~1.1.5",
|
||||||
"@scure/base": "~1.1.1",
|
"@scure/bip39": "~1.1.1",
|
||||||
"@scure/bip32": "~1.1.1",
|
|
||||||
"@scure/bip39": "~1.1.0",
|
|
||||||
"@types/node": "18.11.3",
|
"@types/node": "18.11.3",
|
||||||
"fast-check": "3.0.0",
|
"fast-check": "3.0.0",
|
||||||
"micro-bmark": "0.2.0",
|
"micro-bmark": "0.3.1",
|
||||||
"micro-should": "0.2.0",
|
"micro-should": "0.4.0",
|
||||||
"prettier": "2.6.2",
|
"prettier": "2.8.3",
|
||||||
"rollup": "2.75.5",
|
|
||||||
"typescript": "4.7.3"
|
"typescript": "4.7.3"
|
||||||
},
|
},
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": {
|
".": {
|
||||||
"types": "./lib/index.d.ts",
|
"types": "./index.d.ts",
|
||||||
"import": "./lib/esm/index.js",
|
"import": "./esm/index.js",
|
||||||
"default": "./lib/index.js"
|
"default": "./index.js"
|
||||||
},
|
},
|
||||||
"./abstract/edwards": {
|
"./abstract/edwards": {
|
||||||
"types": "./lib/abstract/edwards.d.ts",
|
"types": "./abstract/edwards.d.ts",
|
||||||
"import": "./lib/esm/abstract/edwards.js",
|
"import": "./esm/abstract/edwards.js",
|
||||||
"default": "./lib/abstract/edwards.js"
|
"default": "./abstract/edwards.js"
|
||||||
},
|
},
|
||||||
"./abstract/modular": {
|
"./abstract/modular": {
|
||||||
"types": "./lib/abstract/modular.d.ts",
|
"types": "./abstract/modular.d.ts",
|
||||||
"import": "./lib/esm/abstract/modular.js",
|
"import": "./esm/abstract/modular.js",
|
||||||
"default": "./lib/abstract/modular.js"
|
"default": "./abstract/modular.js"
|
||||||
},
|
},
|
||||||
"./abstract/montgomery": {
|
"./abstract/montgomery": {
|
||||||
"types": "./lib/abstract/montgomery.d.ts",
|
"types": "./abstract/montgomery.d.ts",
|
||||||
"import": "./lib/esm/abstract/montgomery.js",
|
"import": "./esm/abstract/montgomery.js",
|
||||||
"default": "./lib/abstract/montgomery.js"
|
"default": "./abstract/montgomery.js"
|
||||||
},
|
},
|
||||||
"./abstract/weierstrass": {
|
"./abstract/weierstrass": {
|
||||||
"types": "./lib/abstract/weierstrass.d.ts",
|
"types": "./abstract/weierstrass.d.ts",
|
||||||
"import": "./lib/esm/abstract/weierstrass.js",
|
"import": "./esm/abstract/weierstrass.js",
|
||||||
"default": "./lib/abstract/weierstrass.js"
|
"default": "./abstract/weierstrass.js"
|
||||||
},
|
},
|
||||||
"./abstract/bls": {
|
"./abstract/bls": {
|
||||||
"types": "./lib/abstract/bls.d.ts",
|
"types": "./abstract/bls.d.ts",
|
||||||
"import": "./lib/esm/abstract/bls.js",
|
"import": "./esm/abstract/bls.js",
|
||||||
"default": "./lib/abstract/bls.js"
|
"default": "./abstract/bls.js"
|
||||||
},
|
},
|
||||||
"./abstract/hash-to-curve": {
|
"./abstract/hash-to-curve": {
|
||||||
"types": "./lib/abstract/hash-to-curve.d.ts",
|
"types": "./abstract/hash-to-curve.d.ts",
|
||||||
"import": "./lib/esm/abstract/hash-to-curve.js",
|
"import": "./esm/abstract/hash-to-curve.js",
|
||||||
"default": "./lib/abstract/hash-to-curve.js"
|
"default": "./abstract/hash-to-curve.js"
|
||||||
},
|
},
|
||||||
"./abstract/group": {
|
"./abstract/curve": {
|
||||||
"types": "./lib/abstract/group.d.ts",
|
"types": "./abstract/curve.d.ts",
|
||||||
"import": "./lib/esm/abstract/group.js",
|
"import": "./esm/abstract/curve.js",
|
||||||
"default": "./lib/abstract/group.js"
|
"default": "./abstract/curve.js"
|
||||||
},
|
},
|
||||||
"./abstract/utils": {
|
"./abstract/utils": {
|
||||||
"types": "./lib/abstract/utils.d.ts",
|
"types": "./abstract/utils.d.ts",
|
||||||
"import": "./lib/esm/abstract/utils.js",
|
"import": "./esm/abstract/utils.js",
|
||||||
"default": "./lib/abstract/utils.js"
|
"default": "./abstract/utils.js"
|
||||||
|
},
|
||||||
|
"./abstract/poseidon": {
|
||||||
|
"types": "./abstract/poseidon.d.ts",
|
||||||
|
"import": "./esm/abstract/poseidon.js",
|
||||||
|
"default": "./abstract/poseidon.js"
|
||||||
},
|
},
|
||||||
"./_shortw_utils": {
|
"./_shortw_utils": {
|
||||||
"types": "./lib/_shortw_utils.d.ts",
|
"types": "./_shortw_utils.d.ts",
|
||||||
"import": "./lib/esm/_shortw_utils.js",
|
"import": "./esm/_shortw_utils.js",
|
||||||
"default": "./lib/_shortw_utils.js"
|
"default": "./_shortw_utils.js"
|
||||||
},
|
},
|
||||||
"./bls12-381": {
|
"./bls12-381": {
|
||||||
"types": "./lib/bls12-381.d.ts",
|
"types": "./bls12-381.d.ts",
|
||||||
"import": "./lib/esm/bls12-381.js",
|
"import": "./esm/bls12-381.js",
|
||||||
"default": "./lib/bls12-381.js"
|
"default": "./bls12-381.js"
|
||||||
},
|
},
|
||||||
"./bn": {
|
"./bn": {
|
||||||
"types": "./lib/bn.d.ts",
|
"types": "./bn.d.ts",
|
||||||
"import": "./lib/esm/bn.js",
|
"import": "./esm/bn.js",
|
||||||
"default": "./lib/bn.js"
|
"default": "./bn.js"
|
||||||
},
|
},
|
||||||
"./ed25519": {
|
"./ed25519": {
|
||||||
"types": "./lib/ed25519.d.ts",
|
"types": "./ed25519.d.ts",
|
||||||
"import": "./lib/esm/ed25519.js",
|
"import": "./esm/ed25519.js",
|
||||||
"default": "./lib/ed25519.js"
|
"default": "./ed25519.js"
|
||||||
},
|
},
|
||||||
"./ed448": {
|
"./ed448": {
|
||||||
"types": "./lib/ed448.d.ts",
|
"types": "./ed448.d.ts",
|
||||||
"import": "./lib/esm/ed448.js",
|
"import": "./esm/ed448.js",
|
||||||
"default": "./lib/ed448.js"
|
"default": "./ed448.js"
|
||||||
},
|
},
|
||||||
"./index": {
|
"./index": {
|
||||||
"types": "./lib/index.d.ts",
|
"types": "./index.d.ts",
|
||||||
"import": "./lib/esm/index.js",
|
"import": "./esm/index.js",
|
||||||
"default": "./lib/index.js"
|
"default": "./index.js"
|
||||||
},
|
},
|
||||||
"./jubjub": {
|
"./jubjub": {
|
||||||
"types": "./lib/jubjub.d.ts",
|
"types": "./jubjub.d.ts",
|
||||||
"import": "./lib/esm/jubjub.js",
|
"import": "./esm/jubjub.js",
|
||||||
"default": "./lib/jubjub.js"
|
"default": "./jubjub.js"
|
||||||
},
|
|
||||||
"./p192": {
|
|
||||||
"types": "./lib/p192.d.ts",
|
|
||||||
"import": "./lib/esm/p192.js",
|
|
||||||
"default": "./lib/p192.js"
|
|
||||||
},
|
|
||||||
"./p224": {
|
|
||||||
"types": "./lib/p224.d.ts",
|
|
||||||
"import": "./lib/esm/p224.js",
|
|
||||||
"default": "./lib/p224.js"
|
|
||||||
},
|
},
|
||||||
"./p256": {
|
"./p256": {
|
||||||
"types": "./lib/p256.d.ts",
|
"types": "./p256.d.ts",
|
||||||
"import": "./lib/esm/p256.js",
|
"import": "./esm/p256.js",
|
||||||
"default": "./lib/p256.js"
|
"default": "./p256.js"
|
||||||
},
|
},
|
||||||
"./p384": {
|
"./p384": {
|
||||||
"types": "./lib/p384.d.ts",
|
"types": "./p384.d.ts",
|
||||||
"import": "./lib/esm/p384.js",
|
"import": "./esm/p384.js",
|
||||||
"default": "./lib/p384.js"
|
"default": "./p384.js"
|
||||||
},
|
},
|
||||||
"./p521": {
|
"./p521": {
|
||||||
"types": "./lib/p521.d.ts",
|
"types": "./p521.d.ts",
|
||||||
"import": "./lib/esm/p521.js",
|
"import": "./esm/p521.js",
|
||||||
"default": "./lib/p521.js"
|
"default": "./p521.js"
|
||||||
},
|
},
|
||||||
"./pasta": {
|
"./pasta": {
|
||||||
"types": "./lib/pasta.d.ts",
|
"types": "./pasta.d.ts",
|
||||||
"import": "./lib/esm/pasta.js",
|
"import": "./esm/pasta.js",
|
||||||
"default": "./lib/pasta.js"
|
"default": "./pasta.js"
|
||||||
},
|
},
|
||||||
"./secp256k1": {
|
"./secp256k1": {
|
||||||
"types": "./lib/secp256k1.d.ts",
|
"types": "./secp256k1.d.ts",
|
||||||
"import": "./lib/esm/secp256k1.js",
|
"import": "./esm/secp256k1.js",
|
||||||
"default": "./lib/secp256k1.js"
|
"default": "./secp256k1.js"
|
||||||
},
|
},
|
||||||
"./stark": {
|
"./stark": {
|
||||||
"types": "./lib/stark.d.ts",
|
"types": "./stark.d.ts",
|
||||||
"import": "./lib/esm/stark.js",
|
"import": "./esm/stark.js",
|
||||||
"default": "./lib/stark.js"
|
"default": "./stark.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { concatBytes, randomBytes } from '@noble/hashes/utils';
|
|||||||
import { weierstrass, CurveType } from './abstract/weierstrass.js';
|
import { weierstrass, CurveType } from './abstract/weierstrass.js';
|
||||||
import { CHash } from './abstract/utils.js';
|
import { CHash } from './abstract/utils.js';
|
||||||
|
|
||||||
|
// connects noble-curves to noble-hashes
|
||||||
export function getHash(hash: CHash) {
|
export function getHash(hash: CHash) {
|
||||||
return {
|
return {
|
||||||
hash,
|
hash,
|
||||||
|
|||||||
@@ -1,110 +1,118 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
/*! 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
|
* BLS (Barreto-Lynn-Scott) family of pairing-friendly curves.
|
||||||
// Constructed from pair of weierstrass curves, based pairing logic
|
* Implements BLS (Boneh-Lynn-Shacham) signatures.
|
||||||
import * as mod from './modular.js';
|
* Consists of two curves: G1 and G2:
|
||||||
import { ensureBytes, numberToBytesBE, bytesToNumberBE, bitLen, bitGet } from './utils.js';
|
* - G1 is a subgroup of (x, y) E(Fq) over y² = x³ + 4.
|
||||||
import * as utils from './utils.js';
|
* - G2 is a subgroup of ((x₁, x₂+i), (y₁, y₂+i)) E(Fq²) over y² = x³ + 4(1 + i) where i is √-1
|
||||||
// Types
|
* - Gt, created by bilinear (ate) pairing e(G1, G2), consists of p-th roots of unity in
|
||||||
import { hexToBytes, bytesToHex, Hex, PrivKey } from './utils.js';
|
* Fq^k where k is embedding degree. Only degree 12 is currently supported, 24 is not.
|
||||||
import { htfOpts, stringToBytes, hash_to_field, expand_message_xmd } from './hash-to-curve.js';
|
* Pairing is used to aggregate and verify signatures.
|
||||||
import { CurvePointsType, PointType, CurvePointsRes, weierstrassPoints } from './weierstrass.js';
|
* 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, ensureBytes } 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?
|
type Fp = bigint; // Can be different field?
|
||||||
|
|
||||||
export type SignatureCoder<Fp2> = {
|
export type SignatureCoder<Fp2> = {
|
||||||
decode(hex: Hex): PointType<Fp2>;
|
decode(hex: Hex): ProjPointType<Fp2>;
|
||||||
encode(point: PointType<Fp2>): Uint8Array;
|
encode(point: ProjPointType<Fp2>): Uint8Array;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CurveType<Fp, Fp2, Fp6, Fp12> = {
|
export type CurveType<Fp, Fp2, Fp6, Fp12> = {
|
||||||
r: bigint;
|
r: bigint;
|
||||||
G1: Omit<CurvePointsType<Fp>, 'n'>;
|
G1: Omit<CurvePointsType<Fp>, 'n'> & {
|
||||||
|
mapToCurve: htf.MapToCurve<Fp>;
|
||||||
|
htfDefaults: htf.Opts;
|
||||||
|
};
|
||||||
G2: Omit<CurvePointsType<Fp2>, 'n'> & {
|
G2: Omit<CurvePointsType<Fp2>, 'n'> & {
|
||||||
Signature: SignatureCoder<Fp2>;
|
Signature: SignatureCoder<Fp2>;
|
||||||
|
mapToCurve: htf.MapToCurve<Fp2>;
|
||||||
|
htfDefaults: htf.Opts;
|
||||||
};
|
};
|
||||||
x: bigint;
|
x: bigint;
|
||||||
Fp: mod.Field<Fp>;
|
Fp: Field<Fp>;
|
||||||
Fr: mod.Field<bigint>;
|
Fr: Field<bigint>;
|
||||||
Fp2: mod.Field<Fp2> & {
|
Fp2: Field<Fp2> & {
|
||||||
reim: (num: Fp2) => { re: bigint; im: bigint };
|
reim: (num: Fp2) => { re: bigint; im: bigint };
|
||||||
multiplyByB: (num: Fp2) => Fp2;
|
multiplyByB: (num: Fp2) => Fp2;
|
||||||
frobeniusMap(num: Fp2, power: number): Fp2;
|
frobeniusMap(num: Fp2, power: number): Fp2;
|
||||||
};
|
};
|
||||||
Fp6: mod.Field<Fp6>;
|
Fp6: Field<Fp6>;
|
||||||
Fp12: mod.Field<Fp12> & {
|
Fp12: Field<Fp12> & {
|
||||||
frobeniusMap(num: Fp12, power: number): Fp12;
|
frobeniusMap(num: Fp12, power: number): Fp12;
|
||||||
multiplyBy014(num: Fp12, o0: Fp2, o1: Fp2, o4: Fp2): Fp12;
|
multiplyBy014(num: Fp12, o0: Fp2, o1: Fp2, o4: Fp2): Fp12;
|
||||||
conjugate(num: Fp12): Fp12;
|
conjugate(num: Fp12): Fp12;
|
||||||
finalExponentiate(num: Fp12): Fp12;
|
finalExponentiate(num: Fp12): Fp12;
|
||||||
};
|
};
|
||||||
htfDefaults: htfOpts;
|
htfDefaults: htf.Opts;
|
||||||
hash: utils.CHash; // Because we need outputLen for DRBG
|
hash: CHash; // Because we need outputLen for DRBG
|
||||||
randomBytes: (bytesLength?: number) => Uint8Array;
|
randomBytes: (bytesLength?: number) => Uint8Array;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CurveFn<Fp, Fp2, Fp6, Fp12> = {
|
export type CurveFn<Fp, Fp2, Fp6, Fp12> = {
|
||||||
CURVE: CurveType<Fp, Fp2, Fp6, Fp12>;
|
CURVE: CurveType<Fp, Fp2, Fp6, Fp12>;
|
||||||
Fr: mod.Field<bigint>;
|
Fr: Field<bigint>;
|
||||||
Fp: mod.Field<Fp>;
|
Fp: Field<Fp>;
|
||||||
Fp2: mod.Field<Fp2>;
|
Fp2: Field<Fp2>;
|
||||||
Fp6: mod.Field<Fp6>;
|
Fp6: Field<Fp6>;
|
||||||
Fp12: mod.Field<Fp12>;
|
Fp12: Field<Fp12>;
|
||||||
G1: CurvePointsRes<Fp>;
|
G1: CurvePointsRes<Fp> & ReturnType<typeof htf.createHasher<Fp>>;
|
||||||
G2: CurvePointsRes<Fp2>;
|
G2: CurvePointsRes<Fp2> & ReturnType<typeof htf.createHasher<Fp2>>;
|
||||||
Signature: SignatureCoder<Fp2>;
|
Signature: SignatureCoder<Fp2>;
|
||||||
millerLoop: (ell: [Fp2, Fp2, Fp2][], g1: [Fp, Fp]) => Fp12;
|
millerLoop: (ell: [Fp2, Fp2, Fp2][], g1: [Fp, Fp]) => Fp12;
|
||||||
calcPairingPrecomputes: (x: Fp2, y: Fp2) => [Fp2, Fp2, Fp2][];
|
calcPairingPrecomputes: (p: AffinePoint<Fp2>) => [Fp2, Fp2, Fp2][];
|
||||||
pairing: (P: PointType<Fp>, Q: PointType<Fp2>, withFinalExponent?: boolean) => Fp12;
|
pairing: (P: ProjPointType<Fp>, Q: ProjPointType<Fp2>, withFinalExponent?: boolean) => Fp12;
|
||||||
getPublicKey: (privateKey: PrivKey) => Uint8Array;
|
getPublicKey: (privateKey: PrivKey) => Uint8Array;
|
||||||
sign: {
|
sign: {
|
||||||
(message: Hex, privateKey: PrivKey): Uint8Array;
|
(message: Hex, privateKey: PrivKey): Uint8Array;
|
||||||
(message: PointType<Fp2>, privateKey: PrivKey): PointType<Fp2>;
|
(message: ProjPointType<Fp2>, privateKey: PrivKey): ProjPointType<Fp2>;
|
||||||
};
|
};
|
||||||
verify: (
|
verify: (
|
||||||
signature: Hex | PointType<Fp2>,
|
signature: Hex | ProjPointType<Fp2>,
|
||||||
message: Hex | PointType<Fp2>,
|
message: Hex | ProjPointType<Fp2>,
|
||||||
publicKey: Hex | PointType<Fp>
|
publicKey: Hex | ProjPointType<Fp>
|
||||||
) => boolean;
|
) => boolean;
|
||||||
aggregatePublicKeys: {
|
aggregatePublicKeys: {
|
||||||
(publicKeys: Hex[]): Uint8Array;
|
(publicKeys: Hex[]): Uint8Array;
|
||||||
(publicKeys: PointType<Fp>[]): PointType<Fp>;
|
(publicKeys: ProjPointType<Fp>[]): ProjPointType<Fp>;
|
||||||
};
|
};
|
||||||
aggregateSignatures: {
|
aggregateSignatures: {
|
||||||
(signatures: Hex[]): Uint8Array;
|
(signatures: Hex[]): Uint8Array;
|
||||||
(signatures: PointType<Fp2>[]): PointType<Fp2>;
|
(signatures: ProjPointType<Fp2>[]): ProjPointType<Fp2>;
|
||||||
};
|
};
|
||||||
verifyBatch: (
|
verifyBatch: (
|
||||||
signature: Hex | PointType<Fp2>,
|
signature: Hex | ProjPointType<Fp2>,
|
||||||
messages: (Hex | PointType<Fp2>)[],
|
messages: (Hex | ProjPointType<Fp2>)[],
|
||||||
publicKeys: (Hex | PointType<Fp>)[]
|
publicKeys: (Hex | ProjPointType<Fp>)[]
|
||||||
) => boolean;
|
) => boolean;
|
||||||
utils: {
|
utils: {
|
||||||
bytesToHex: typeof utils.bytesToHex;
|
randomPrivateKey: () => Uint8Array;
|
||||||
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;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export function bls<Fp2, Fp6, Fp12>(
|
export function bls<Fp2, Fp6, Fp12>(
|
||||||
CURVE: CurveType<Fp, Fp2, Fp6, Fp12>
|
CURVE: CurveType<Fp, Fp2, Fp6, Fp12>
|
||||||
): CurveFn<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
|
// Fields looks pretty specific for curve, so for now we need to pass them with opts
|
||||||
const Fp = CURVE.Fp;
|
const { Fp, Fr, Fp2, Fp6, Fp12 } = CURVE;
|
||||||
const Fr = CURVE.Fr;
|
|
||||||
const Fp2 = CURVE.Fp2;
|
|
||||||
const Fp6 = CURVE.Fp6;
|
|
||||||
const Fp12 = CURVE.Fp12;
|
|
||||||
const BLS_X_LEN = bitLen(CURVE.x);
|
const BLS_X_LEN = bitLen(CURVE.x);
|
||||||
|
const groupLen = 32; // TODO: calculate; hardcoded for now
|
||||||
|
|
||||||
// Pre-compute coefficients for sparse multiplication
|
// Pre-compute coefficients for sparse multiplication
|
||||||
// Point addition and point double calculations is reused for coefficients
|
// 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
|
// prettier-ignore
|
||||||
const Qx = x, Qy = y, Qz = Fp2.ONE;
|
const Qx = x, Qy = y, Qz = Fp2.ONE;
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
@@ -112,18 +120,18 @@ export function bls<Fp2, Fp6, Fp12>(
|
|||||||
let ell_coeff: [Fp2, Fp2, Fp2][] = [];
|
let ell_coeff: [Fp2, Fp2, Fp2][] = [];
|
||||||
for (let i = BLS_X_LEN - 2; i >= 0; i--) {
|
for (let i = BLS_X_LEN - 2; i >= 0; i--) {
|
||||||
// Double
|
// Double
|
||||||
let t0 = Fp2.square(Ry); // Ry²
|
let t0 = Fp2.sqr(Ry); // Ry²
|
||||||
let t1 = Fp2.square(Rz); // Rz²
|
let t1 = Fp2.sqr(Rz); // Rz²
|
||||||
let t2 = Fp2.multiplyByB(Fp2.mul(t1, 3n)); // 3 * T1 * B
|
let t2 = Fp2.multiplyByB(Fp2.mul(t1, 3n)); // 3 * T1 * B
|
||||||
let t3 = Fp2.mul(t2, 3n); // 3 * T2
|
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([
|
ell_coeff.push([
|
||||||
Fp2.sub(t2, t0), // T2 - T0
|
Fp2.sub(t2, t0), // T2 - T0
|
||||||
Fp2.mul(Fp2.square(Rx), 3n), // 3 * Rx²
|
Fp2.mul(Fp2.sqr(Rx), 3n), // 3 * Rx²
|
||||||
Fp2.negate(t4), // -T4
|
Fp2.neg(t4), // -T4
|
||||||
]);
|
]);
|
||||||
Rx = Fp2.div(Fp2.mul(Fp2.mul(Fp2.sub(t0, t3), Rx), Ry), 2n); // ((T0 - T3) * Rx * Ry) / 2
|
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
|
Rz = Fp2.mul(t0, t4); // T0 * T4
|
||||||
if (bitGet(CURVE.x, i)) {
|
if (bitGet(CURVE.x, i)) {
|
||||||
// Addition
|
// Addition
|
||||||
@@ -131,13 +139,13 @@ export function bls<Fp2, Fp6, Fp12>(
|
|||||||
let t1 = Fp2.sub(Rx, Fp2.mul(Qx, Rz)); // Rx - Qx * Rz
|
let t1 = Fp2.sub(Rx, Fp2.mul(Qx, Rz)); // Rx - Qx * Rz
|
||||||
ell_coeff.push([
|
ell_coeff.push([
|
||||||
Fp2.sub(Fp2.mul(t0, Qx), Fp2.mul(t1, Qy)), // T0 * Qx - T1 * Qy
|
Fp2.sub(Fp2.mul(t0, Qx), Fp2.mul(t1, Qy)), // T0 * Qx - T1 * Qy
|
||||||
Fp2.negate(t0), // -T0
|
Fp2.neg(t0), // -T0
|
||||||
t1, // T1
|
t1, // T1
|
||||||
]);
|
]);
|
||||||
let t2 = Fp2.square(t1); // T1²
|
let t2 = Fp2.sqr(t1); // T1²
|
||||||
let t3 = Fp2.mul(t2, t1); // T2 * T1
|
let t3 = Fp2.mul(t2, t1); // T2 * T1
|
||||||
let t4 = Fp2.mul(t2, Rx); // T2 * Rx
|
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
|
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
|
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
|
Rz = Fp2.mul(Rz, t3); // Rz * T3
|
||||||
@@ -147,102 +155,38 @@ export function bls<Fp2, Fp6, Fp12>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function millerLoop(ell: [Fp2, Fp2, Fp2][], g1: [Fp, Fp]): Fp12 {
|
function millerLoop(ell: [Fp2, Fp2, Fp2][], g1: [Fp, Fp]): Fp12 {
|
||||||
|
const { x } = CURVE;
|
||||||
const Px = g1[0];
|
const Px = g1[0];
|
||||||
const Py = g1[1];
|
const Py = g1[1];
|
||||||
let f12 = Fp12.ONE;
|
let f12 = Fp12.ONE;
|
||||||
for (let j = 0, i = BLS_X_LEN - 2; i >= 0; i--, j++) {
|
for (let j = 0, i = BLS_X_LEN - 2; i >= 0; i--, j++) {
|
||||||
const E = ell[j];
|
const E = ell[j];
|
||||||
f12 = Fp12.multiplyBy014(f12, E[0], Fp2.mul(E[1], Px), Fp2.mul(E[2], Py));
|
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;
|
j += 1;
|
||||||
const F = ell[j];
|
const F = ell[j];
|
||||||
f12 = Fp12.multiplyBy014(f12, F[0], Fp2.mul(F[1], Px), Fp2.mul(F[2], Py));
|
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);
|
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 = {
|
const utils = {
|
||||||
hexToBytes: hexToBytes,
|
randomPrivateKey: (): Uint8Array => {
|
||||||
bytesToHex: bytesToHex,
|
return Fr.toBytes(hashToPrivateScalar(CURVE.randomBytes(groupLen + 8), CURVE.r));
|
||||||
mod: mod.mod,
|
|
||||||
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 }),
|
|
||||||
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;
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
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)
|
// Point on G1 curve: (x, y)
|
||||||
const G1 = weierstrassPoints({
|
const G1_ = weierstrassPoints({ n: Fr.ORDER, ...CURVE.G1 });
|
||||||
n: Fr.ORDER,
|
const G1 = Object.assign(
|
||||||
...CURVE.G1,
|
G1_,
|
||||||
});
|
htf.createHasher(G1_.ProjectivePoint, CURVE.G1.mapToCurve, {
|
||||||
|
...CURVE.htfDefaults,
|
||||||
|
...CURVE.G1.htfDefaults,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
// Sparse multiplication against precomputed coefficients
|
// Sparse multiplication against precomputed coefficients
|
||||||
// TODO: replace with weakmap?
|
// TODO: replace with weakmap?
|
||||||
@@ -250,83 +194,92 @@ export function bls<Fp2, Fp6, Fp12>(
|
|||||||
function pairingPrecomputes(point: G2): [Fp2, Fp2, Fp2][] {
|
function pairingPrecomputes(point: G2): [Fp2, Fp2, Fp2][] {
|
||||||
const p = point as G2 & withPairingPrecomputes;
|
const p = point as G2 & withPairingPrecomputes;
|
||||||
if (p._PPRECOMPUTES) return p._PPRECOMPUTES;
|
if (p._PPRECOMPUTES) return p._PPRECOMPUTES;
|
||||||
p._PPRECOMPUTES = calcPairingPrecomputes(p.x, p.y);
|
p._PPRECOMPUTES = calcPairingPrecomputes(point.toAffine());
|
||||||
return p._PPRECOMPUTES;
|
return p._PPRECOMPUTES;
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearPairingPrecomputes(point: G2) {
|
// TODO: export
|
||||||
const p = point as G2 & withPairingPrecomputes;
|
// function clearPairingPrecomputes(point: G2) {
|
||||||
p._PPRECOMPUTES = undefined;
|
// 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]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Point on G2 curve (complex numbers): (x₁, x₂+i), (y₁, y₂+i)
|
// Point on G2 curve (complex numbers): (x₁, x₂+i), (y₁, y₂+i)
|
||||||
const G2 = weierstrassPoints({
|
const G2_ = weierstrassPoints({ n: Fr.ORDER, ...CURVE.G2 });
|
||||||
n: Fr.ORDER,
|
const G2 = Object.assign(
|
||||||
...CURVE.G2,
|
G2_,
|
||||||
});
|
htf.createHasher(G2_.ProjectivePoint as htf.H2CPointConstructor<Fp2>, CURVE.G2.mapToCurve, {
|
||||||
|
...CURVE.htfDefaults,
|
||||||
|
...CURVE.G2.htfDefaults,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
const { Signature } = CURVE.G2;
|
const { Signature } = CURVE.G2;
|
||||||
|
|
||||||
// Calculates bilinear pairing
|
// Calculates bilinear pairing
|
||||||
function pairing(P: G1, Q: G2, withFinalExponent: boolean = true): Fp12 {
|
function pairing(Q: G1, P: G2, withFinalExponent: boolean = true): Fp12 {
|
||||||
if (P.equals(G1.Point.ZERO) || Q.equals(G2.Point.ZERO))
|
if (Q.equals(G1.ProjectivePoint.ZERO) || P.equals(G2.ProjectivePoint.ZERO))
|
||||||
throw new Error('No pairings at point of Infinity');
|
throw new Error('pairing is not available for ZERO point');
|
||||||
P.assertValidity();
|
|
||||||
Q.assertValidity();
|
Q.assertValidity();
|
||||||
|
P.assertValidity();
|
||||||
// Performance: 9ms for millerLoop and ~14ms for exp.
|
// 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;
|
return withFinalExponent ? Fp12.finalExponentiate(looped) : looped;
|
||||||
}
|
}
|
||||||
type G1 = typeof G1.Point.BASE;
|
type G1 = typeof G1.ProjectivePoint.BASE;
|
||||||
type G2 = typeof G2.Point.BASE;
|
type G2 = typeof G2.ProjectivePoint.BASE;
|
||||||
|
|
||||||
type G1Hex = Hex | G1;
|
type G1Hex = Hex | G1;
|
||||||
type G2Hex = Hex | G2;
|
type G2Hex = Hex | G2;
|
||||||
function normP1(point: G1Hex): G1 {
|
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 {
|
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 {
|
function normP2Hash(point: G2Hex, htfOpts?: htf.htfBasicOpts): G2 {
|
||||||
return point instanceof G2.Point ? point : G2.Point.hashToCurve(point);
|
return point instanceof G2.ProjectivePoint
|
||||||
|
? point
|
||||||
|
: (G2.hashToCurve(ensureBytes('point', point), htfOpts) as G2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Multiplies generator by private key.
|
// Multiplies generator by private key.
|
||||||
// P = pk x G
|
// P = pk x G
|
||||||
function getPublicKey(privateKey: PrivKey): Uint8Array {
|
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.
|
// Executes `hashToCurve` on the message and then multiplies the result by private key.
|
||||||
// S = pk x H(m)
|
// S = pk x H(m)
|
||||||
function sign(message: Hex, privateKey: PrivKey): Uint8Array;
|
function sign(message: Hex, privateKey: PrivKey, htfOpts?: htf.htfBasicOpts): Uint8Array;
|
||||||
function sign(message: G2, privateKey: PrivKey): G2;
|
function sign(message: G2, privateKey: PrivKey, htfOpts?: htf.htfBasicOpts): G2;
|
||||||
function sign(message: G2Hex, privateKey: PrivKey): Uint8Array | G2 {
|
function sign(message: G2Hex, privateKey: PrivKey, htfOpts?: htf.htfBasicOpts): Uint8Array | G2 {
|
||||||
const msgPoint = normP2Hash(message);
|
const msgPoint = normP2Hash(message, htfOpts);
|
||||||
msgPoint.assertValidity();
|
msgPoint.assertValidity();
|
||||||
const sigPoint = msgPoint.multiply(normalizePrivKey(privateKey));
|
const sigPoint = msgPoint.multiply(G1.normPrivateKeyToScalar(privateKey));
|
||||||
if (message instanceof G2.Point) return sigPoint;
|
if (message instanceof G2.ProjectivePoint) return sigPoint;
|
||||||
return Signature.encode(sigPoint);
|
return Signature.encode(sigPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks if pairing of public key & hash is equal to pairing of generator & signature.
|
// Checks if pairing of public key & hash is equal to pairing of generator & signature.
|
||||||
// e(P, H(m)) == e(G, S)
|
// 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 P = normP1(publicKey);
|
||||||
const Hm = normP2Hash(message);
|
const Hm = normP2Hash(message, htfOpts);
|
||||||
const G = G1.Point.BASE;
|
const G = G1.ProjectivePoint.BASE;
|
||||||
const S = normP2(signature);
|
const S = normP2(signature);
|
||||||
// Instead of doing 2 exponentiations, we use property of billinear maps
|
// Instead of doing 2 exponentiations, we use property of billinear maps
|
||||||
// and do one exp after multiplying 2 points.
|
// and do one exp after multiplying 2 points.
|
||||||
const ePHm = pairing(P.negate(), Hm, false);
|
const ePHm = pairing(P.negate(), Hm, false);
|
||||||
const eGS = pairing(G, S, false);
|
const eGS = pairing(G, S, false);
|
||||||
const exp = Fp12.finalExponentiate(Fp12.mul(eGS, ePHm));
|
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.
|
// Adds a bunch of public key points together.
|
||||||
@@ -335,11 +288,9 @@ export function bls<Fp2, Fp6, Fp12>(
|
|||||||
function aggregatePublicKeys(publicKeys: G1[]): G1;
|
function aggregatePublicKeys(publicKeys: G1[]): G1;
|
||||||
function aggregatePublicKeys(publicKeys: G1Hex[]): Uint8Array | G1 {
|
function aggregatePublicKeys(publicKeys: G1Hex[]): Uint8Array | G1 {
|
||||||
if (!publicKeys.length) throw new Error('Expected non-empty array');
|
if (!publicKeys.length) throw new Error('Expected non-empty array');
|
||||||
const agg = publicKeys
|
const agg = publicKeys.map(normP1).reduce((sum, p) => sum.add(p), G1.ProjectivePoint.ZERO);
|
||||||
.map(normP1)
|
const aggAffine = agg; //.toAffine();
|
||||||
.reduce((sum, p) => sum.add(G1.ProjectivePoint.fromAffine(p)), G1.ProjectivePoint.ZERO);
|
if (publicKeys[0] instanceof G1.ProjectivePoint) {
|
||||||
const aggAffine = agg.toAffine();
|
|
||||||
if (publicKeys[0] instanceof G1.Point) {
|
|
||||||
aggAffine.assertValidity();
|
aggAffine.assertValidity();
|
||||||
return aggAffine;
|
return aggAffine;
|
||||||
}
|
}
|
||||||
@@ -352,11 +303,9 @@ export function bls<Fp2, Fp6, Fp12>(
|
|||||||
function aggregateSignatures(signatures: G2[]): G2;
|
function aggregateSignatures(signatures: G2[]): G2;
|
||||||
function aggregateSignatures(signatures: G2Hex[]): Uint8Array | G2 {
|
function aggregateSignatures(signatures: G2Hex[]): Uint8Array | G2 {
|
||||||
if (!signatures.length) throw new Error('Expected non-empty array');
|
if (!signatures.length) throw new Error('Expected non-empty array');
|
||||||
const agg = signatures
|
const agg = signatures.map(normP2).reduce((sum, s) => sum.add(s), G2.ProjectivePoint.ZERO);
|
||||||
.map(normP2)
|
const aggAffine = agg; //.toAffine();
|
||||||
.reduce((sum, s) => sum.add(G2.ProjectivePoint.fromAffine(s)), G2.ProjectivePoint.ZERO);
|
if (signatures[0] instanceof G2.ProjectivePoint) {
|
||||||
const aggAffine = agg.toAffine();
|
|
||||||
if (signatures[0] instanceof G2.Point) {
|
|
||||||
aggAffine.assertValidity();
|
aggAffine.assertValidity();
|
||||||
return aggAffine;
|
return aggAffine;
|
||||||
}
|
}
|
||||||
@@ -365,12 +314,20 @@ export function bls<Fp2, Fp6, Fp12>(
|
|||||||
|
|
||||||
// https://ethresear.ch/t/fast-verification-of-multiple-bls-signatures/5407
|
// https://ethresear.ch/t/fast-verification-of-multiple-bls-signatures/5407
|
||||||
// e(G, S) = e(G, SUM(n)(Si)) = MUL(n)(e(G, Si))
|
// 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 (!messages.length) throw new Error('Expected non-empty messages array');
|
||||||
if (publicKeys.length !== messages.length)
|
if (publicKeys.length !== messages.length)
|
||||||
throw new Error('Pubkey count should equal msg count');
|
throw new Error('Pubkey count should equal msg count');
|
||||||
const sig = normP2(signature);
|
const sig = normP2(signature);
|
||||||
const nMessages = messages.map(normP2Hash);
|
const nMessages = messages.map((i) => normP2Hash(i, htfOpts));
|
||||||
const nPublicKeys = publicKeys.map(normP1);
|
const nPublicKeys = publicKeys.map(normP1);
|
||||||
try {
|
try {
|
||||||
const paired = [];
|
const paired = [];
|
||||||
@@ -378,23 +335,23 @@ export function bls<Fp2, Fp6, Fp12>(
|
|||||||
const groupPublicKey = nMessages.reduce(
|
const groupPublicKey = nMessages.reduce(
|
||||||
(groupPublicKey, subMessage, i) =>
|
(groupPublicKey, subMessage, i) =>
|
||||||
subMessage === message ? groupPublicKey.add(nPublicKeys[i]) : groupPublicKey,
|
subMessage === message ? groupPublicKey.add(nPublicKeys[i]) : groupPublicKey,
|
||||||
G1.Point.ZERO
|
G1.ProjectivePoint.ZERO
|
||||||
);
|
);
|
||||||
// const msg = message instanceof PointG2 ? message : await PointG2.hashToCurve(message);
|
// const msg = message instanceof PointG2 ? message : await PointG2.hashToCurve(message);
|
||||||
// Possible to batch pairing for same msg with different groupPublicKey here
|
// Possible to batch pairing for same msg with different groupPublicKey here
|
||||||
paired.push(pairing(groupPublicKey, message, false));
|
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 product = paired.reduce((a, b) => Fp12.mul(a, b), Fp12.ONE);
|
||||||
const exp = Fp12.finalExponentiate(product);
|
const exp = Fp12.finalExponentiate(product);
|
||||||
return Fp12.equals(exp, Fp12.ONE);
|
return Fp12.eql(exp, Fp12.ONE);
|
||||||
} catch {
|
} catch {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pre-compute points. Refer to README.
|
G1.ProjectivePoint.BASE._setWindowSize(4);
|
||||||
G1.Point.BASE._setWindowSize(4);
|
|
||||||
return {
|
return {
|
||||||
CURVE,
|
CURVE,
|
||||||
Fr,
|
Fr,
|
||||||
|
|||||||
@@ -1,22 +1,41 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
// Abelian group utilities
|
// Abelian group utilities
|
||||||
|
import { Field, validateField, nLength } from './modular.js';
|
||||||
|
import { validateObject } from './utils.js';
|
||||||
const _0n = BigInt(0);
|
const _0n = BigInt(0);
|
||||||
const _1n = BigInt(1);
|
const _1n = BigInt(1);
|
||||||
|
|
||||||
|
export type AffinePoint<T> = {
|
||||||
|
x: T;
|
||||||
|
y: T;
|
||||||
|
} & { z?: never; t?: never };
|
||||||
|
|
||||||
export interface Group<T extends Group<T>> {
|
export interface Group<T extends Group<T>> {
|
||||||
double(): T;
|
double(): T;
|
||||||
negate(): T;
|
negate(): T;
|
||||||
add(other: T): T;
|
add(other: T): T;
|
||||||
subtract(other: T): T;
|
subtract(other: T): T;
|
||||||
equals(other: T): boolean;
|
equals(other: T): boolean;
|
||||||
multiply(scalar: number | bigint): T;
|
multiply(scalar: bigint): T;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type GroupConstructor<T> = {
|
export type GroupConstructor<T> = {
|
||||||
BASE: T;
|
BASE: T;
|
||||||
ZERO: 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. Fragile.
|
||||||
|
// Scalars should always be less than curve order: this should be checked inside of a curve itself.
|
||||||
|
// Creates precomputation tables for fast multiplication:
|
||||||
|
// - private scalar is split by fixed size windows of W bits
|
||||||
|
// - every window point is collected from window's table & added to accumulator
|
||||||
|
// - since windows are different, same point inside tables won't be accessed more than once per calc
|
||||||
|
// - each multiplication is 'Math.ceil(CURVE_ORDER / 𝑊) + 1' point additions (fixed for any scalar)
|
||||||
|
// - +1 window is neccessary for wNAF
|
||||||
|
// - wNAF reduces table size: 2x less memory + 2x faster generation, but 10% slower multiplication
|
||||||
|
// TODO: Research returning 2d JS array of windows, instead of a single window. This would allow
|
||||||
|
// windows to be in different memory locations
|
||||||
export function wNAF<T extends Group<T>>(c: GroupConstructor<T>, bits: number) {
|
export function wNAF<T extends Group<T>>(c: GroupConstructor<T>, bits: number) {
|
||||||
const constTimeNegate = (condition: boolean, item: T): T => {
|
const constTimeNegate = (condition: boolean, item: T): T => {
|
||||||
const neg = item.negate();
|
const neg = item.negate();
|
||||||
@@ -44,8 +63,12 @@ export function wNAF<T extends Group<T>>(c: GroupConstructor<T>, bits: number) {
|
|||||||
/**
|
/**
|
||||||
* Creates a wNAF precomputation window. Used for caching.
|
* Creates a wNAF precomputation window. Used for caching.
|
||||||
* Default window size is set by `utils.precompute()` and is equal to 8.
|
* Default window size is set by `utils.precompute()` and is equal to 8.
|
||||||
* Which means we are caching 65536 points: 256 points for every bit from 0 to 256.
|
* Number of precomputed points depends on the curve size:
|
||||||
* @returns 65K precomputed points, depending on W
|
* 2^(𝑊−1) * (Math.ceil(𝑛 / 𝑊) + 1), where:
|
||||||
|
* - 𝑊 is the window size
|
||||||
|
* - 𝑛 is the bitlength of the curve order.
|
||||||
|
* For a 256-bit curve and window size 8, the number of precomputed points is 128 * 33 = 4224.
|
||||||
|
* @returns precomputed point tables flattened to a single array
|
||||||
*/
|
*/
|
||||||
precomputeWindow(elm: T, W: number): Group<T>[] {
|
precomputeWindow(elm: T, W: number): Group<T>[] {
|
||||||
const { windows, windowSize } = opts(W);
|
const { windows, windowSize } = opts(W);
|
||||||
@@ -66,14 +89,14 @@ export function wNAF<T extends Group<T>>(c: GroupConstructor<T>, bits: number) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements w-ary non-adjacent form for calculating ec multiplication.
|
* Implements ec multiplication using precomputed tables and w-ary non-adjacent form.
|
||||||
* @param W window size
|
* @param W window size
|
||||||
* @param affinePoint optional 2d point to save cached precompute windows on it.
|
* @param precomputes precomputed tables
|
||||||
* @param n bits
|
* @param n scalar (we don't check here, but should be less than curve order)
|
||||||
* @returns real and fake (for const-time) points
|
* @returns real and fake (for const-time) points
|
||||||
*/
|
*/
|
||||||
wNAF(W: number, precomputes: T[], n: bigint): { p: T; f: T } {
|
wNAF(W: number, precomputes: T[], n: bigint): { p: T; f: T } {
|
||||||
// TODO: maybe check that scalar is less than group order? wNAF will fail otherwise
|
// TODO: maybe check that scalar is less than group order? wNAF behavious is undefined otherwise
|
||||||
// But need to carefully remove other checks before wNAF. ORDER == bits here
|
// But need to carefully remove other checks before wNAF. ORDER == bits here
|
||||||
const { windows, windowSize } = opts(W);
|
const { windows, windowSize } = opts(W);
|
||||||
|
|
||||||
@@ -125,5 +148,52 @@ export function wNAF<T extends Group<T>>(c: GroupConstructor<T>, bits: number) {
|
|||||||
// which makes it less const-time: around 1 bigint multiply.
|
// which makes it less const-time: around 1 bigint multiply.
|
||||||
return { p, f };
|
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) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
// Twisted Edwards curve. The formula is: ax² + y² = 1 + dx²y²
|
// Twisted Edwards curve. The formula is: ax² + y² = 1 + dx²y²
|
||||||
|
import { mod } from './modular.js';
|
||||||
// Differences from @noble/ed25519 1.7:
|
import * as ut from './utils.js';
|
||||||
// 1. Different field element lengths in ed448:
|
import { ensureBytes, FHash, Hex } from './utils.js';
|
||||||
// EDDSA (RFC8032) is 456 bits / 57 bytes, ECDH (RFC7748) is 448 bits / 56 bytes
|
import { Group, GroupConstructor, wNAF, BasicCurve, validateBasic, AffinePoint } from './curve.js';
|
||||||
// 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';
|
|
||||||
|
|
||||||
// Be friendly to bad ECMAScript parsers by not using bigint literals like 123n
|
// Be friendly to bad ECMAScript parsers by not using bigint literals like 123n
|
||||||
const _0n = BigInt(0);
|
const _0n = BigInt(0);
|
||||||
@@ -31,204 +11,193 @@ const _1n = BigInt(1);
|
|||||||
const _2n = BigInt(2);
|
const _2n = BigInt(2);
|
||||||
const _8n = BigInt(8);
|
const _8n = BigInt(8);
|
||||||
|
|
||||||
export type CHash = {
|
// Edwards curves must declare params a & d.
|
||||||
(message: Uint8Array | string): Uint8Array;
|
|
||||||
blockLen: number;
|
|
||||||
outputLen: number;
|
|
||||||
create(): any;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type CurveType = BasicCurve<bigint> & {
|
export type CurveType = BasicCurve<bigint> & {
|
||||||
// Params: a, d
|
a: bigint; // curve param a
|
||||||
a: bigint;
|
d: bigint; // curve param d
|
||||||
d: bigint;
|
hash: FHash; // Hashing
|
||||||
// Hashes
|
randomBytes: (bytesLength?: number) => Uint8Array; // CSPRNG
|
||||||
hash: CHash; // Because we need outputLen for DRBG
|
adjustScalarBytes?: (bytes: Uint8Array) => Uint8Array; // clears bits to get valid field elemtn
|
||||||
randomBytes: (bytesLength?: number) => Uint8Array;
|
domain?: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array; // Used for hashing
|
||||||
adjustScalarBytes?: (bytes: Uint8Array) => Uint8Array;
|
uvRatio?: (u: bigint, v: bigint) => { isValid: boolean; value: bigint }; // Ratio √(u/v)
|
||||||
domain?: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array;
|
preHash?: FHash; // RFC 8032 pre-hashing of messages to sign() / verify()
|
||||||
uvRatio?: (u: bigint, v: bigint) => { isValid: boolean; value: bigint };
|
mapToCurve?: (scalar: bigint[]) => AffinePoint<bigint>; // for hash-to-curve standard
|
||||||
preHash?: CHash;
|
|
||||||
clearCofactor?: (c: ExtendedPointConstructor, point: ExtendedPointType) => ExtendedPointType;
|
|
||||||
// Hash to field opts
|
|
||||||
htfDefaults?: htfOpts;
|
|
||||||
mapToCurve?: (scalar: bigint[]) => { x: bigint; y: bigint };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Should be separate from overrides, since overrides can use information about curve (for example nBits)
|
|
||||||
function validateOpts(curve: CurveType) {
|
function validateOpts(curve: CurveType) {
|
||||||
const opts = utilOpts(curve);
|
const opts = validateBasic(curve);
|
||||||
if (typeof opts.hash !== 'function' || !Number.isSafeInteger(opts.hash.outputLen))
|
ut.validateObject(
|
||||||
throw new Error('Invalid hash function');
|
curve,
|
||||||
for (const i of ['a', 'd'] as const) {
|
{
|
||||||
if (typeof opts[i] !== 'bigint')
|
hash: 'function',
|
||||||
throw new Error(`Invalid curve param ${i}=${opts[i]} (${typeof opts[i]})`);
|
a: 'bigint',
|
||||||
|
d: 'bigint',
|
||||||
|
randomBytes: 'function',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
adjustScalarBytes: 'function',
|
||||||
|
domain: 'function',
|
||||||
|
uvRatio: 'function',
|
||||||
|
mapToCurve: 'function',
|
||||||
}
|
}
|
||||||
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);
|
|
||||||
// Set defaults
|
// Set defaults
|
||||||
return Object.freeze({ ...opts } as const);
|
return Object.freeze({ ...opts } as const);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Instance
|
// Instance of Extended Point with coordinates in X, Y, Z, T
|
||||||
export interface SignatureType {
|
export interface ExtPointType extends Group<ExtPointType> {
|
||||||
readonly r: PointType;
|
readonly ex: bigint;
|
||||||
readonly s: bigint;
|
readonly ey: bigint;
|
||||||
assertValidity(): SignatureType;
|
readonly ez: bigint;
|
||||||
toRawBytes(): Uint8Array;
|
readonly et: bigint;
|
||||||
toHex(): string;
|
assertValidity(): void;
|
||||||
}
|
multiply(scalar: bigint): ExtPointType;
|
||||||
// Static methods
|
multiplyUnsafe(scalar: bigint): ExtPointType;
|
||||||
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;
|
|
||||||
isSmallOrder(): boolean;
|
isSmallOrder(): boolean;
|
||||||
isTorsionFree(): boolean;
|
isTorsionFree(): boolean;
|
||||||
toAffine(invZ?: bigint): PointType;
|
clearCofactor(): ExtPointType;
|
||||||
clearCofactor(): ExtendedPointType;
|
toAffine(iz?: bigint): AffinePoint<bigint>;
|
||||||
}
|
}
|
||||||
// Static methods
|
// Static methods of Extended Point with coordinates in X, Y, Z, T
|
||||||
export interface ExtendedPointConstructor extends GroupConstructor<ExtendedPointType> {
|
export interface ExtPointConstructor extends GroupConstructor<ExtPointType> {
|
||||||
new (x: bigint, y: bigint, z: bigint, t: bigint): ExtendedPointType;
|
new (x: bigint, y: bigint, z: bigint, t: bigint): ExtPointType;
|
||||||
fromAffine(p: PointType): ExtendedPointType;
|
fromAffine(p: AffinePoint<bigint>): ExtPointType;
|
||||||
toAffineBatch(points: ExtendedPointType[]): PointType[];
|
fromHex(hex: Hex): ExtPointType;
|
||||||
normalizeZ(points: ExtendedPointType[]): ExtendedPointType[];
|
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 = {
|
export type CurveFn = {
|
||||||
CURVE: ReturnType<typeof validateOpts>;
|
CURVE: ReturnType<typeof validateOpts>;
|
||||||
getPublicKey: (privateKey: PrivKey, isCompressed?: boolean) => Uint8Array;
|
getPublicKey: (privateKey: Hex) => Uint8Array;
|
||||||
sign: (message: Hex, privateKey: Hex) => Uint8Array;
|
sign: (message: Hex, privateKey: Hex) => Uint8Array;
|
||||||
verify: (sig: SigType, message: Hex, publicKey: PubKey) => boolean;
|
verify: (sig: Hex, message: Hex, publicKey: Hex) => boolean;
|
||||||
Point: PointConstructor;
|
ExtendedPoint: ExtPointConstructor;
|
||||||
ExtendedPoint: ExtendedPointConstructor;
|
|
||||||
Signature: SignatureConstructor;
|
|
||||||
utils: {
|
utils: {
|
||||||
mod: (a: bigint) => bigint;
|
|
||||||
invert: (number: bigint) => bigint;
|
|
||||||
randomPrivateKey: () => Uint8Array;
|
randomPrivateKey: () => Uint8Array;
|
||||||
getExtendedPublicKey: (key: PrivKey) => {
|
getExtendedPublicKey: (key: Hex) => {
|
||||||
head: Uint8Array;
|
head: Uint8Array;
|
||||||
prefix: Uint8Array;
|
prefix: Uint8Array;
|
||||||
scalar: bigint;
|
scalar: bigint;
|
||||||
point: PointType;
|
point: ExtPointType;
|
||||||
pointBytes: Uint8Array;
|
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 {
|
export function twistedEdwards(curveDef: CurveType): CurveFn {
|
||||||
const CURVE = validateOpts(curveDef) as ReturnType<typeof validateOpts>;
|
const CURVE = validateOpts(curveDef) as ReturnType<typeof validateOpts>;
|
||||||
const Fp = CURVE.Fp as mod.Field<bigint>;
|
const { Fp, n: CURVE_ORDER, preHash, hash: cHash, randomBytes, nByteLength, h: cofactor } = CURVE;
|
||||||
const CURVE_ORDER = CURVE.n;
|
const MASK = _2n ** BigInt(nByteLength * 8);
|
||||||
const fieldLen = Fp.BYTES; // 32 (length of one field element)
|
const modP = Fp.create; // Function overrides
|
||||||
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;
|
|
||||||
|
|
||||||
// sqrt(u/v)
|
// sqrt(u/v)
|
||||||
function _uvRatio(u: bigint, v: bigint) {
|
const uvRatio =
|
||||||
|
CURVE.uvRatio ||
|
||||||
|
((u: bigint, v: bigint) => {
|
||||||
try {
|
try {
|
||||||
const value = Fp.sqrt(u * Fp.invert(v));
|
return { isValid: true, value: Fp.sqrt(u * Fp.inv(v)) };
|
||||||
return { isValid: true, value };
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return { isValid: false, value: _0n };
|
return { isValid: false, value: _0n };
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
const uvRatio = CURVE.uvRatio || _uvRatio;
|
const adjustScalarBytes = CURVE.adjustScalarBytes || ((bytes: Uint8Array) => bytes); // NOOP
|
||||||
|
const domain =
|
||||||
const _adjustScalarBytes = (bytes: Uint8Array) => bytes; // NOOP
|
CURVE.domain ||
|
||||||
const adjustScalarBytes = CURVE.adjustScalarBytes || _adjustScalarBytes;
|
((data: Uint8Array, ctx: Uint8Array, phflag: boolean) => {
|
||||||
function _domain(data: Uint8Array, ctx: Uint8Array, phflag: boolean) {
|
|
||||||
if (ctx.length || phflag) throw new Error('Contexts/pre-hash are not supported');
|
if (ctx.length || phflag) throw new Error('Contexts/pre-hash are not supported');
|
||||||
return data;
|
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}`);
|
||||||
}
|
}
|
||||||
const domain = CURVE.domain || _domain; // NOOP
|
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(
|
||||||
* Extended Point works in extended coordinates: (x, y, z, t) ∋ (x=x/z, y=y/z, t=xy).
|
readonly ex: bigint,
|
||||||
* Default Point works in affine coordinates: (x, y)
|
readonly ey: bigint,
|
||||||
* https://en.wikipedia.org/wiki/Twisted_Edwards_curve#Extended_coordinates
|
readonly ez: bigint,
|
||||||
*/
|
readonly et: bigint
|
||||||
class ExtendedPoint implements ExtendedPointType {
|
) {
|
||||||
constructor(readonly x: bigint, readonly y: bigint, readonly z: bigint, readonly t: bigint) {}
|
if (!in0MaskRange(ex)) throw new Error('x required');
|
||||||
|
if (!in0MaskRange(ey)) throw new Error('y required');
|
||||||
static BASE = new ExtendedPoint(CURVE.Gx, CURVE.Gy, _1n, modP(CURVE.Gx * CURVE.Gy));
|
if (!in0MaskRange(ez)) throw new Error('z required');
|
||||||
static ZERO = new ExtendedPoint(_0n, _1n, _1n, _0n);
|
if (!in0MaskRange(et)) throw new Error('t required');
|
||||||
static fromAffine(p: Point): ExtendedPoint {
|
|
||||||
if (!(p instanceof Point)) {
|
|
||||||
throw new TypeError('ExtendedPoint#fromAffine: expected Point');
|
|
||||||
}
|
|
||||||
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]));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static normalizeZ(points: ExtendedPoint[]): ExtendedPoint[] {
|
get x(): bigint {
|
||||||
return this.toAffineBatch(points).map(this.fromAffine);
|
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.
|
// Compare one point to another.
|
||||||
equals(other: ExtendedPoint): boolean {
|
equals(other: Point): boolean {
|
||||||
assertExtPoint(other);
|
isPoint(other);
|
||||||
const { x: X1, y: Y1, z: Z1 } = this;
|
const { ex: X1, ey: Y1, ez: Z1 } = this;
|
||||||
const { x: X2, y: Y2, z: Z2 } = other;
|
const { ex: X2, ey: Y2, ez: Z2 } = other;
|
||||||
const X1Z2 = modP(X1 * Z2);
|
const X1Z2 = modP(X1 * Z2);
|
||||||
const X2Z1 = modP(X2 * Z1);
|
const X2Z1 = modP(X2 * Z1);
|
||||||
const Y1Z2 = modP(Y1 * Z2);
|
const Y1Z2 = modP(Y1 * Z2);
|
||||||
@@ -236,17 +205,21 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|||||||
return X1Z2 === X2Z1 && Y1Z2 === Y2Z1;
|
return X1Z2 === X2Z1 && Y1Z2 === Y2Z1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inverses point to one corresponding to (x, -y) in Affine coordinates.
|
protected is0(): boolean {
|
||||||
negate(): ExtendedPoint {
|
return this.equals(Point.ZERO);
|
||||||
return new ExtendedPoint(modP(-this.x), this.y, this.z, modP(-this.t));
|
}
|
||||||
|
|
||||||
|
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.
|
// Fast algo for doubling Extended Point.
|
||||||
// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd
|
// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd
|
||||||
// Cost: 4M + 4S + 1*a + 6add + 1*2.
|
// Cost: 4M + 4S + 1*a + 6add + 1*2.
|
||||||
double(): ExtendedPoint {
|
double(): Point {
|
||||||
const { a } = CURVE;
|
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 A = modP(X1 * X1); // A = X12
|
||||||
const B = modP(Y1 * Y1); // B = Y12
|
const B = modP(Y1 * Y1); // B = Y12
|
||||||
const C = modP(_2n * modP(Z1 * Z1)); // C = 2*Z12
|
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 Y3 = modP(G * H); // Y3 = G*H
|
||||||
const T3 = modP(E * H); // T3 = E*H
|
const T3 = modP(E * H); // T3 = E*H
|
||||||
const Z3 = modP(F * G); // Z3 = F*G
|
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.
|
// Fast algo for adding 2 Extended Points.
|
||||||
// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#addition-add-2008-hwcd
|
// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#addition-add-2008-hwcd
|
||||||
// Cost: 9M + 1*a + 1*d + 7add.
|
// Cost: 9M + 1*a + 1*d + 7add.
|
||||||
add(other: ExtendedPoint) {
|
add(other: Point) {
|
||||||
assertExtPoint(other);
|
isPoint(other);
|
||||||
const { a, d } = CURVE;
|
const { a, d } = CURVE;
|
||||||
const { x: X1, y: Y1, z: Z1, t: T1 } = this;
|
const { ex: X1, ey: Y1, ez: Z1, et: T1 } = this;
|
||||||
const { x: X2, y: Y2, z: Z2, t: T2 } = other;
|
const { ex: X2, ey: Y2, ez: Z2, et: T2 } = other;
|
||||||
// Faster algo for adding 2 Extended Points when curve's a=-1.
|
// 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
|
// http://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-4
|
||||||
// Cost: 8M + 8add + 2*2.
|
// Cost: 8M + 8add + 2*2.
|
||||||
@@ -289,7 +262,7 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|||||||
const Y3 = modP(G * H);
|
const Y3 = modP(G * H);
|
||||||
const T3 = modP(E * H);
|
const T3 = modP(E * H);
|
||||||
const Z3 = modP(F * G);
|
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 A = modP(X1 * X2); // A = X1*X2
|
||||||
const B = modP(Y1 * Y2); // B = Y1*Y2
|
const B = modP(Y1 * Y2); // B = Y1*Y2
|
||||||
@@ -304,401 +277,183 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|||||||
const T3 = modP(E * H); // T3 = E*H
|
const T3 = modP(E * H); // T3 = E*H
|
||||||
const Z3 = modP(F * G); // Z3 = F*G
|
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());
|
return this.add(other.negate());
|
||||||
}
|
}
|
||||||
|
|
||||||
private wNAF(n: bigint, affinePoint?: Point): ExtendedPoint {
|
private wNAF(n: bigint): { p: Point; f: Point } {
|
||||||
if (!affinePoint && this.equals(ExtendedPoint.BASE)) affinePoint = Point.BASE;
|
return wnaf.wNAFCached(this, pointPrecomputes, n, Point.normalizeZ);
|
||||||
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];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Constant time multiplication.
|
// Constant-time multiplication.
|
||||||
// Uses wNAF method. Windowed method may be 10% faster,
|
multiply(scalar: bigint): Point {
|
||||||
// but takes 2x longer to generate and consumes 2x memory.
|
const { p, f } = this.wNAF(assertInRange(scalar, CURVE_ORDER));
|
||||||
multiply(scalar: number | bigint, affinePoint?: Point): ExtendedPoint {
|
return Point.normalizeZ([p, f])[0];
|
||||||
return this.wNAF(normalizeScalar(scalar, CURVE_ORDER), affinePoint);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Non-constant-time multiplication. Uses double-and-add algorithm.
|
// Non-constant-time multiplication. Uses double-and-add algorithm.
|
||||||
// It's faster, but should only be used when you don't care about
|
// It's faster, but should only be used when you don't care about
|
||||||
// an exposed private key e.g. sig verification.
|
// an exposed private key e.g. sig verification.
|
||||||
// Allows scalar bigger than curve order, but less than 2^256
|
multiplyUnsafe(scalar: bigint): Point {
|
||||||
multiplyUnsafe(scalar: number | bigint): ExtendedPoint {
|
let n = assertGE0(scalar);
|
||||||
let n = normalizeScalar(scalar, CURVE_ORDER, false);
|
if (n === _0n) return I;
|
||||||
const G = ExtendedPoint.BASE;
|
if (this.equals(I) || n === _1n) return this;
|
||||||
const P0 = ExtendedPoint.ZERO;
|
if (this.equals(G)) return this.wNAF(n).p;
|
||||||
if (n === _0n) return P0;
|
|
||||||
if (this.equals(P0) || n === _1n) return this;
|
|
||||||
if (this.equals(G)) return this.wNAF(n);
|
|
||||||
return wnaf.unsafeLadder(this, n);
|
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.
|
// Multiplies point by cofactor and checks if the result is 0.
|
||||||
isSmallOrder(): boolean {
|
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 {
|
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.
|
// Converts Extended point to default (x, y) coordinates.
|
||||||
// Can accept precomputed Z^-1 - for example, from invertBatch.
|
// Can accept precomputed Z^-1 - for example, from invertBatch.
|
||||||
toAffine(invZ?: bigint): Point {
|
toAffine(iz?: bigint): AffinePoint<bigint> {
|
||||||
const { x, y, z } = this;
|
const { ex: x, ey: y, ez: z } = this;
|
||||||
const is0 = this.equals(ExtendedPoint.ZERO);
|
const is0 = this.is0();
|
||||||
if (invZ == null) invZ = is0 ? _8n : (Fp.invert(z) as bigint); // 8 was chosen arbitrarily
|
if (iz == null) iz = is0 ? _8n : (Fp.inv(z) as bigint); // 8 was chosen arbitrarily
|
||||||
const ax = modP(x * invZ);
|
const ax = modP(x * iz);
|
||||||
const ay = modP(y * invZ);
|
const ay = modP(y * iz);
|
||||||
const zz = modP(z * invZ);
|
const zz = modP(z * iz);
|
||||||
if (is0) return Point.ZERO;
|
if (is0) return { x: _0n, y: _1n };
|
||||||
if (zz !== _1n) throw new Error('invZ was invalid');
|
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) {
|
clearCofactor(): Point {
|
||||||
if (!(other instanceof ExtendedPoint)) throw new TypeError('ExtendedPoint expected');
|
const { h: cofactor } = CURVE;
|
||||||
}
|
if (cofactor === _1n) return this;
|
||||||
// Stores precomputed values for points.
|
return this.multiplyUnsafe(cofactor);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Converts hash string or Uint8Array to Point.
|
// Converts hash string or Uint8Array to Point.
|
||||||
// Uses algo from RFC8032 5.1.3.
|
// Uses algo from RFC8032 5.1.3.
|
||||||
static fromHex(hex: Hex, strict = true) {
|
static fromHex(hex: Hex, strict = true): Point {
|
||||||
const { d, a } = CURVE;
|
const { d, a } = CURVE;
|
||||||
hex = ensureBytes(hex, fieldLen);
|
const len = Fp.BYTES;
|
||||||
// 1. First, interpret the string as an integer in little-endian
|
hex = ensureBytes('pointHex', hex, len); // copy hex to a new array
|
||||||
// representation. Bit 255 of this number is the least significant
|
const normed = hex.slice(); // copy again, we'll manipulate it
|
||||||
// bit of the x-coordinate and denote this value x_0. The
|
const lastByte = hex[len - 1]; // select last byte
|
||||||
// y-coordinate is recovered simply by clearing this bit. If the
|
normed[len - 1] = lastByte & ~0x80; // clear last bit
|
||||||
// resulting value is >= p, decoding fails.
|
const y = ut.bytesToNumberLE(normed);
|
||||||
const normed = hex.slice();
|
if (y === _0n) {
|
||||||
const lastByte = hex[fieldLen - 1];
|
// y=0 is allowed
|
||||||
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;
|
|
||||||
} else {
|
} 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)
|
||||||
throw new TypeError('Expected valid scalar: 0 < scalar < max');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkPrivateKey(key: PrivKey) {
|
// Ed25519: x² = (y²-1)/(dy²+1) mod p. Ed448: x² = (y²-1)/(dy²-1) mod p. Generic case:
|
||||||
// Normalize bigint / number / string to Uint8Array
|
// ax²+y²=1+dx²y² => y²-1=dx²y²-ax² => y²-1=x²(dy²-a) => x²=(y²-1)/(dy²-a)
|
||||||
key =
|
const y2 = modP(y * y); // denominator is always non-0 mod p.
|
||||||
typeof key === 'bigint' || typeof key === 'number'
|
const u = modP(y2 - _1n); // u = y² - 1
|
||||||
? numberToBytesLE(normalizeScalar(key, maxGroupElement), groupLen)
|
const v = modP(d * y2 - a); // v = d y² + 1.
|
||||||
: ensureBytes(key);
|
let { isValid, value: x } = uvRatio(u, v); // √(u/v)
|
||||||
if (key.length !== groupLen) throw new Error(`Expected ${groupLen} bytes, got ${key.length}`);
|
if (!isValid) throw new Error('Point.fromHex: invalid y coordinate');
|
||||||
return key;
|
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.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const { BASE: G, ZERO: I } = Point;
|
||||||
|
const wnaf = wNAF(Point, nByteLength * 8);
|
||||||
|
|
||||||
// Takes 64 bytes
|
function modN(a: bigint) {
|
||||||
function getKeyFromHash(hashed: Uint8Array) {
|
return mod(a, CURVE_ORDER);
|
||||||
// First 32 bytes of 64b uniformingly random input are taken,
|
}
|
||||||
// clears 3 bits of it to produce a random field element.
|
// Little-endian SHA512 with modulo n
|
||||||
const head = adjustScalarBytes(hashed.slice(0, groupLen));
|
function modN_LE(hash: Uint8Array): bigint {
|
||||||
// Second 32 bytes is called key prefix (5.1.6)
|
return modN(ut.bytesToNumberLE(hash));
|
||||||
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 };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Convenience method that creates public key and other stuff. RFC8032 5.1.5 */
|
/** Convenience method that creates public key and other stuff. RFC8032 5.1.5 */
|
||||||
function getExtendedPublicKey(key: PrivKey) {
|
function getExtendedPublicKey(key: Hex) {
|
||||||
return getKeyFromHash(CURVE.hash(checkPrivateKey(key)));
|
const len = nByteLength;
|
||||||
|
key = ensureBytes('private key', key, len);
|
||||||
|
// 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('hashed private key', cHash(key), 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 EdDSA pub key. RFC8032 5.1.5. Privkey is hashed. Use first half with 3 bits cleared
|
||||||
* Calculates ed25519 public key. RFC8032 5.1.5
|
function getPublicKey(privKey: Hex): Uint8Array {
|
||||||
* 1. private key is hashed with sha512, then first 32 bytes are taken from the hash
|
return getExtendedPublicKey(privKey).pointBytes;
|
||||||
* 2. 3 least significant bits of the first byte are cleared
|
|
||||||
*/
|
|
||||||
function getPublicKey(privateKey: PrivKey): Uint8Array {
|
|
||||||
return getExtendedPublicKey(privateKey).pointBytes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const EMPTY = new Uint8Array();
|
// int('LE', SHA512(dom2(F, C) || msgs)) mod N
|
||||||
function hashDomainToScalar(message: Uint8Array, context: Hex = EMPTY) {
|
function hashDomainToScalar(context: Hex = new Uint8Array(), ...msgs: Uint8Array[]) {
|
||||||
context = ensureBytes(context);
|
const msg = ut.concatBytes(...msgs);
|
||||||
return modlLE(CURVE.hash(domain(message, context, !!CURVE.preHash)));
|
return modN_LE(cHash(domain(msg, ensureBytes('context', context), !!preHash)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Signs message with privateKey. RFC8032 5.1.6 */
|
/** Signs message with privateKey. RFC8032 5.1.6 */
|
||||||
function sign(message: Hex, privateKey: Hex, context?: Hex): Uint8Array {
|
function sign(msg: Hex, privKey: Hex, context?: Hex): Uint8Array {
|
||||||
message = ensureBytes(message);
|
msg = ensureBytes('message', msg);
|
||||||
if (CURVE.preHash) message = CURVE.preHash(message);
|
if (preHash) msg = preHash(msg); // for ed25519ph etc.
|
||||||
const { prefix, scalar, pointBytes } = getExtendedPublicKey(privateKey);
|
const { prefix, scalar, pointBytes } = getExtendedPublicKey(privKey);
|
||||||
const r = hashDomainToScalar(concatBytes(prefix, message), context);
|
const r = hashDomainToScalar(context, prefix, msg); // r = dom2(F, C) || prefix || PH(M)
|
||||||
const R = Point.BASE.multiply(r); // R = rG
|
const R = G.multiply(r).toRawBytes(); // R = rG
|
||||||
const k = hashDomainToScalar(concatBytes(R.toRawBytes(), pointBytes, message), context); // k = hash(R+P+msg)
|
const k = hashDomainToScalar(context, R, pointBytes, msg); // R || A || PH(M)
|
||||||
const s = mod.mod(r + k * scalar, CURVE_ORDER); // s = r + kp
|
const s = modN(r + k * scalar); // S = (r + k * s) mod L
|
||||||
return new Signature(R, s).toRawBytes();
|
assertGE0(s); // 0 <= s < l
|
||||||
|
const res = ut.concatBytes(R, ut.numberToBytesLE(s, Fp.BYTES));
|
||||||
|
return ensureBytes('result', res, nByteLength * 2); // 64-byte signature
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
function verify(sig: Hex, msg: Hex, publicKey: Hex, context?: Hex): boolean {
|
||||||
* Verifies EdDSA signature against message and public key.
|
const len = Fp.BYTES; // Verifies EdDSA signature against message and public key. RFC8032 5.1.7.
|
||||||
* An extended group equation is checked.
|
sig = ensureBytes('signature', sig, 2 * len); // An extended group equation is checked.
|
||||||
* RFC8032 5.1.7
|
msg = ensureBytes('message', msg); // ZIP215 compliant, which means not fully RFC8032 compliant.
|
||||||
* Compliant with ZIP215:
|
if (preHash) msg = preHash(msg); // for ed25519ph, etc
|
||||||
* 0 <= sig.R/publicKey < 2**256 (can be >= curve.P)
|
const A = Point.fromHex(publicKey, false); // Check for s bounds, hex validity
|
||||||
* 0 <= sig.s < l
|
const R = Point.fromHex(sig.slice(0, len), false); // 0 <= R < 2^256: ZIP215 R can be >= P
|
||||||
* Not compliant with RFC8032: it's not possible to comply to both ZIP & RFC at the same time.
|
const s = ut.bytesToNumberLE(sig.slice(len, 2 * len)); // 0 <= s < l
|
||||||
*/
|
const SB = G.multiplyUnsafe(s);
|
||||||
function verify(sig: SigType, message: Hex, publicKey: PubKey, context?: Hex): boolean {
|
const k = hashDomainToScalar(context, R.toRawBytes(), A.toRawBytes(), msg);
|
||||||
message = ensureBytes(message);
|
const RkA = R.add(A.multiplyUnsafe(k));
|
||||||
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);
|
|
||||||
// [8][S]B = [8]R + [8][k]A'
|
// [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.
|
G._setWindowSize(8); // Enable precomputes. Slows down first publicKey computation by 20ms.
|
||||||
Point.BASE._setWindowSize(8);
|
|
||||||
|
|
||||||
const utils = {
|
const utils = {
|
||||||
getExtendedPublicKey,
|
getExtendedPublicKey,
|
||||||
mod: modP,
|
// ed25519 private keys are uniform 32b. No need to check for modulo bias, like in secp256k1.
|
||||||
invert: Fp.invert,
|
randomPrivateKey: (): Uint8Array => randomBytes(Fp.BYTES),
|
||||||
|
|
||||||
/**
|
|
||||||
* 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),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We're doing scalar multiplication (used in getPublicKey etc) with precomputed BASE_POINT
|
* We're doing scalar multiplication (used in getPublicKey etc) with precomputed BASE_POINT
|
||||||
@@ -706,11 +461,10 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|||||||
* but allows to speed-up subsequent getPublicKey() calls up to 20x.
|
* but allows to speed-up subsequent getPublicKey() calls up to 20x.
|
||||||
* @param windowSize 2, 4, 8, 16
|
* @param windowSize 2, 4, 8, 16
|
||||||
*/
|
*/
|
||||||
precompute(windowSize = 8, point = Point.BASE): Point {
|
precompute(windowSize = 8, point = Point.BASE): typeof Point.BASE {
|
||||||
const cached = point.equals(Point.BASE) ? point : new Point(point.x, point.y);
|
point._setWindowSize(windowSize);
|
||||||
cached._setWindowSize(windowSize);
|
point.multiply(BigInt(3));
|
||||||
cached.multiply(_2n);
|
return point;
|
||||||
return cached;
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -719,9 +473,7 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|||||||
getPublicKey,
|
getPublicKey,
|
||||||
sign,
|
sign,
|
||||||
verify,
|
verify,
|
||||||
ExtendedPoint,
|
ExtendedPoint: Point,
|
||||||
Point,
|
|
||||||
Signature,
|
|
||||||
utils,
|
utils,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,58 +1,35 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
import { CHash, concatBytes } from './utils.js';
|
import type { Group, GroupConstructor, AffinePoint } from './curve.js';
|
||||||
import * as mod from './modular.js';
|
import { mod, Field } from './modular.js';
|
||||||
|
import { bytesToNumberBE, CHash, concatBytes, utf8ToBytes, validateObject } from './utils.js';
|
||||||
|
|
||||||
export type htfOpts = {
|
/**
|
||||||
// DST: a domain separation tag
|
* * `DST` is a domain separation tag, defined in section 2.2.5
|
||||||
// defined in section 2.2.5
|
* * `p` characteristic of F, where F is a finite field of characteristic p and order q = p^m
|
||||||
DST: string;
|
* * `m` is extension degree (1 for prime fields)
|
||||||
// p: the characteristic of F
|
* * `k` is the target security target in bits (e.g. 128), from section 5.1
|
||||||
// where F is a finite field of characteristic p and order q = p^m
|
* * `expand` is `xmd` (SHA2, SHA3, BLAKE) or `xof` (SHAKE, BLAKE-XOF)
|
||||||
|
* * `hash` conforming to `utils.CHash` interface, with `outputLen` / `blockLen` props
|
||||||
|
*/
|
||||||
|
export type Opts = {
|
||||||
|
DST: string | Uint8Array;
|
||||||
p: bigint;
|
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;
|
m: number;
|
||||||
// k: the target security level for the suite in bits
|
|
||||||
// defined in section 5.1
|
|
||||||
k: number;
|
k: number;
|
||||||
// option to use a message that has already been processed by
|
expand?: 'xmd' | 'xof';
|
||||||
// expand_message_xmd
|
|
||||||
expand: boolean;
|
|
||||||
// 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: CHash;
|
hash: CHash;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function validateHTFOpts(opts: htfOpts) {
|
function validateDST(dst: string | Uint8Array): Uint8Array {
|
||||||
if (typeof opts.DST !== 'string') throw new Error('Invalid htf/DST');
|
if (dst instanceof Uint8Array) return dst;
|
||||||
if (typeof opts.p !== 'bigint') throw new Error('Invalid htf/p');
|
if (typeof dst === 'string') return utf8ToBytes(dst);
|
||||||
if (typeof opts.m !== 'number') throw new Error('Invalid htf/m');
|
throw new Error('DST must be Uint8Array or string');
|
||||||
if (typeof opts.k !== 'number') throw new Error('Invalid htf/k');
|
|
||||||
if (typeof opts.expand !== 'boolean') 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
|
// Octet Stream to Integer. "spec" implementation of os2ip is 2.5x slower vs bytesToNumberBE.
|
||||||
// TODO: looks broken, ASCII only, why not TextEncoder/TextDecoder? it is in hashes anyway
|
const os2ip = bytesToNumberBE;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Octet Stream to Integer (bytesToNumberBE)
|
// Integer to Octet Stream (numberToBytesBE)
|
||||||
function os2ip(bytes: Uint8Array): bigint {
|
|
||||||
let result = 0n;
|
|
||||||
for (let i = 0; i < bytes.length; i++) {
|
|
||||||
result <<= 8n;
|
|
||||||
result += BigInt(bytes[i]);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Integer to Octet Stream
|
|
||||||
function i2osp(value: number, length: number): Uint8Array {
|
function i2osp(value: number, length: number): Uint8Array {
|
||||||
if (value < 0 || value >= 1 << (8 * length)) {
|
if (value < 0 || value >= 1 << (8 * length)) {
|
||||||
throw new Error(`bad I2OSP call: value=${value} length=${length}`);
|
throw new Error(`bad I2OSP call: value=${value} length=${length}`);
|
||||||
@@ -73,6 +50,13 @@ function strxor(a: Uint8Array, b: Uint8Array): Uint8Array {
|
|||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isBytes(item: unknown): void {
|
||||||
|
if (!(item instanceof Uint8Array)) throw new Error('Uint8Array expected');
|
||||||
|
}
|
||||||
|
function isNum(item: unknown): void {
|
||||||
|
if (!Number.isSafeInteger(item)) throw new Error('number expected');
|
||||||
|
}
|
||||||
|
|
||||||
// Produces a uniformly random byte string using a cryptographic hash function H that outputs b bits
|
// Produces a uniformly random byte string using a cryptographic hash function H that outputs b bits
|
||||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.4.1
|
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.4.1
|
||||||
export function expand_message_xmd(
|
export function expand_message_xmd(
|
||||||
@@ -81,15 +65,17 @@ export function expand_message_xmd(
|
|||||||
lenInBytes: number,
|
lenInBytes: number,
|
||||||
H: CHash
|
H: CHash
|
||||||
): Uint8Array {
|
): Uint8Array {
|
||||||
|
isBytes(msg);
|
||||||
|
isBytes(DST);
|
||||||
|
isNum(lenInBytes);
|
||||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-5.3.3
|
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-5.3.3
|
||||||
if (DST.length > 255) DST = H(concatBytes(stringToBytes('H2C-OVERSIZE-DST-'), DST));
|
if (DST.length > 255) DST = H(concatBytes(utf8ToBytes('H2C-OVERSIZE-DST-'), DST));
|
||||||
const b_in_bytes = H.outputLen;
|
const { outputLen: b_in_bytes, blockLen: r_in_bytes } = H;
|
||||||
const r_in_bytes = H.blockLen;
|
|
||||||
const ell = Math.ceil(lenInBytes / b_in_bytes);
|
const ell = Math.ceil(lenInBytes / b_in_bytes);
|
||||||
if (ell > 255) throw new Error('Invalid xmd length');
|
if (ell > 255) throw new Error('Invalid xmd length');
|
||||||
const DST_prime = concatBytes(DST, i2osp(DST.length, 1));
|
const DST_prime = concatBytes(DST, i2osp(DST.length, 1));
|
||||||
const Z_pad = i2osp(0, r_in_bytes);
|
const Z_pad = i2osp(0, r_in_bytes);
|
||||||
const l_i_b_str = i2osp(lenInBytes, 2);
|
const l_i_b_str = i2osp(lenInBytes, 2); // len_in_bytes_str
|
||||||
const b = new Array<Uint8Array>(ell);
|
const b = new Array<Uint8Array>(ell);
|
||||||
const b_0 = H(concatBytes(Z_pad, msg, l_i_b_str, i2osp(0, 1), DST_prime));
|
const b_0 = H(concatBytes(Z_pad, msg, l_i_b_str, i2osp(0, 1), DST_prime));
|
||||||
b[0] = H(concatBytes(b_0, i2osp(1, 1), DST_prime));
|
b[0] = H(concatBytes(b_0, i2osp(1, 1), DST_prime));
|
||||||
@@ -101,38 +87,75 @@ export function expand_message_xmd(
|
|||||||
return pseudo_random_bytes.slice(0, lenInBytes);
|
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
|
export function expand_message_xof(
|
||||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3
|
msg: Uint8Array,
|
||||||
// Inputs:
|
DST: Uint8Array,
|
||||||
// msg - a byte string containing the message to hash.
|
lenInBytes: number,
|
||||||
// count - the number of elements of F to output.
|
k: number,
|
||||||
// Outputs:
|
H: CHash
|
||||||
// [u_0, ..., u_(count - 1)], a list of field elements.
|
): Uint8Array {
|
||||||
export function hash_to_field(msg: Uint8Array, count: number, options: htfOpts): bigint[][] {
|
isBytes(msg);
|
||||||
// if options is provided but incomplete, fill any missing fields with the
|
isBytes(DST);
|
||||||
// value in hftDefaults (ie hash to G2).
|
isNum(lenInBytes);
|
||||||
const log2p = options.p.toString(2).length;
|
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-5.3.3
|
||||||
const L = Math.ceil((log2p + options.k) / 8); // section 5.1 of ietf draft link above
|
// DST = H('H2C-OVERSIZE-DST-' || a_very_long_DST, Math.ceil((lenInBytes * k) / 8));
|
||||||
const len_in_bytes = count * options.m * L;
|
if (DST.length > 255) {
|
||||||
const DST = stringToBytes(options.DST);
|
const dkLen = Math.ceil((2 * k) / 8);
|
||||||
let pseudo_random_bytes = msg;
|
DST = H.create({ dkLen }).update(utf8ToBytes('H2C-OVERSIZE-DST-')).update(DST).digest();
|
||||||
if (options.expand) {
|
}
|
||||||
pseudo_random_bytes = expand_message_xmd(msg, DST, len_in_bytes, options.hash);
|
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}`, see above
|
||||||
|
* @returns [u_0, ..., u_(count - 1)], a list of field elements.
|
||||||
|
*/
|
||||||
|
export function hash_to_field(msg: Uint8Array, count: number, options: Opts): bigint[][] {
|
||||||
|
const { p, k, m, hash, expand, DST: _DST } = options;
|
||||||
|
isBytes(msg);
|
||||||
|
isNum(count);
|
||||||
|
const DST = validateDST(_DST);
|
||||||
|
const log2p = p.toString(2).length;
|
||||||
|
const L = Math.ceil((log2p + k) / 8); // section 5.1 of ietf draft link above
|
||||||
|
const len_in_bytes = count * m * L;
|
||||||
|
let prb; // pseudo_random_bytes
|
||||||
|
if (expand === 'xmd') {
|
||||||
|
prb = expand_message_xmd(msg, DST, len_in_bytes, hash);
|
||||||
|
} else if (expand === 'xof') {
|
||||||
|
prb = expand_message_xof(msg, DST, len_in_bytes, k, hash);
|
||||||
|
} else if (expand === undefined) {
|
||||||
|
prb = msg;
|
||||||
|
} else {
|
||||||
|
throw new Error('expand must be "xmd", "xof" or undefined');
|
||||||
}
|
}
|
||||||
const u = new Array(count);
|
const u = new Array(count);
|
||||||
for (let i = 0; i < count; i++) {
|
for (let i = 0; i < count; i++) {
|
||||||
const e = new Array(options.m);
|
const e = new Array(m);
|
||||||
for (let j = 0; j < options.m; j++) {
|
for (let j = 0; j < m; j++) {
|
||||||
const elm_offset = L * (j + i * options.m);
|
const elm_offset = L * (j + i * m);
|
||||||
const tv = pseudo_random_bytes.subarray(elm_offset, elm_offset + L);
|
const tv = prb.subarray(elm_offset, elm_offset + L);
|
||||||
e[j] = mod.mod(os2ip(tv), options.p);
|
e[j] = mod(os2ip(tv), p);
|
||||||
}
|
}
|
||||||
u[i] = e;
|
u[i] = e;
|
||||||
}
|
}
|
||||||
return u;
|
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
|
// Make same order as in spec
|
||||||
const COEFF = map.map((i) => Array.from(i).reverse());
|
const COEFF = map.map((i) => Array.from(i).reverse());
|
||||||
return (x: T, y: T) => {
|
return (x: T, y: T) => {
|
||||||
@@ -144,3 +167,55 @@ export function isogenyMap<T, F extends mod.Field<T>>(field: F, map: [T[], T[],
|
|||||||
return { x, y };
|
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 createHasher<T>(
|
||||||
|
Point: H2CPointConstructor<T>,
|
||||||
|
mapToCurve: MapToCurve<T>,
|
||||||
|
def: Opts & { encodeDST?: string }
|
||||||
|
) {
|
||||||
|
validateObject(def, {
|
||||||
|
DST: 'string',
|
||||||
|
p: 'bigint',
|
||||||
|
m: 'isSafeInteger',
|
||||||
|
k: 'isSafeInteger',
|
||||||
|
hash: 'hash',
|
||||||
|
});
|
||||||
|
if (typeof mapToCurve !== 'function') throw new Error('mapToCurve() must be 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: Uint8Array, options?: htfBasicOpts) {
|
||||||
|
const u = hash_to_field(msg, 2, { ...def, DST: def.DST, ...options } as Opts);
|
||||||
|
const u0 = Point.fromAffine(mapToCurve(u[0]));
|
||||||
|
const u1 = Point.fromAffine(mapToCurve(u[1]));
|
||||||
|
const P = u0.add(u1).clearCofactor();
|
||||||
|
P.assertValidity();
|
||||||
|
return P;
|
||||||
|
},
|
||||||
|
|
||||||
|
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-3
|
||||||
|
encodeToCurve(msg: Uint8Array, options?: htfBasicOpts) {
|
||||||
|
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) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
import * as utils from './utils.js';
|
|
||||||
// Utilities for modular arithmetics and finite fields
|
// Utilities for modular arithmetics and finite fields
|
||||||
|
import {
|
||||||
|
bitMask,
|
||||||
|
numberToBytesBE,
|
||||||
|
numberToBytesLE,
|
||||||
|
bytesToNumberBE,
|
||||||
|
bytesToNumberLE,
|
||||||
|
ensureBytes,
|
||||||
|
validateObject,
|
||||||
|
} from './utils.js';
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3);
|
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3);
|
||||||
// prettier-ignore
|
// 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
|
// prettier-ignore
|
||||||
const _9n = BigInt(9), _16n = BigInt(16);
|
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)
|
// 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 {
|
export function pow2(x: bigint, power: bigint, modulo: bigint): bigint {
|
||||||
let res = x;
|
let res = x;
|
||||||
while (power-- > _0n) {
|
while (power-- > _0n) {
|
||||||
@@ -54,6 +61,7 @@ export function invert(number: bigint, modulo: bigint): bigint {
|
|||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
let x = _0n, y = _1n, u = _1n, v = _0n;
|
let x = _0n, y = _1n, u = _1n, v = _0n;
|
||||||
while (a !== _0n) {
|
while (a !== _0n) {
|
||||||
|
// JIT applies optimization if those two lines follow each other
|
||||||
const q = b / a;
|
const q = b / a;
|
||||||
const r = b % a;
|
const r = b % a;
|
||||||
const m = x - u * q;
|
const m = x - u * q;
|
||||||
@@ -66,26 +74,68 @@ export function invert(number: bigint, modulo: bigint): bigint {
|
|||||||
return mod(x, modulo);
|
return mod(x, modulo);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Tonelli-Shanks algorithm
|
||||||
* Calculates Legendre symbol (a | p), which denotes the value of a^((p-1)/2) (mod p).
|
// Paper 1: https://eprint.iacr.org/2012/685.pdf (page 12)
|
||||||
* * (a | p) ≡ 1 if a is a square (mod p)
|
// Paper 2: Square Roots from 1; 24, 51, 10 to Dan Shanks
|
||||||
* * (a | p) ≡ -1 if a is not a square (mod p)
|
export function tonelliShanks(P: bigint) {
|
||||||
* * (a | p) ≡ 0 if a ≡ 0 (mod p)
|
// Legendre constant: used to calculate Legendre symbol (a | p),
|
||||||
*/
|
// which denotes the value of a^((p-1)/2) (mod p).
|
||||||
export function legendre(num: bigint, fieldPrime: bigint): bigint {
|
// (a | p) ≡ 1 if a is a square (mod p)
|
||||||
return pow(num, (fieldPrime - _1n) / _2n, fieldPrime);
|
// (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;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export function FpSqrt(P: bigint) {
|
||||||
* Calculates square root of a number in a finite field.
|
// NOTE: different algorithms can give different roots, it is up to user to decide which one they want.
|
||||||
* √a mod P
|
// For example there is FpSqrtOdd/FpSqrtEven to choice root based on oddness (used for hash-to-curve).
|
||||||
*/
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
// P ≡ 3 (mod 4)
|
// P ≡ 3 (mod 4)
|
||||||
// √n = n^((P+1)/4)
|
// √n = n^((P+1)/4)
|
||||||
@@ -94,48 +144,54 @@ export function sqrt(number: bigint, modulo: bigint): bigint {
|
|||||||
// const ORDER =
|
// const ORDER =
|
||||||
// 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaabn;
|
// 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaabn;
|
||||||
// const NUM = 72057594037927816n;
|
// const NUM = 72057594037927816n;
|
||||||
// TODO: fix sqrtMod in secp256k1
|
const p1div4 = (P + _1n) / _4n;
|
||||||
const root = pow(n, p1div4, P);
|
return function sqrt3mod4<T>(Fp: Field<T>, n: T) {
|
||||||
if (mod(root * root, modulo) !== number) throw new Error('Cannot find square root');
|
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;
|
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) {
|
if (P % _8n === _5n) {
|
||||||
const n2 = mod(n * _2n, P);
|
const c1 = (P - _5n) / _8n;
|
||||||
const v = pow(n2, (P - _5n) / _8n, P);
|
return function sqrt5mod8<T>(Fp: Field<T>, n: T) {
|
||||||
const nv = mod(n * v, P);
|
const n2 = Fp.mul(n, _2n);
|
||||||
const i = mod(_2n * nv * v, P);
|
const v = Fp.pow(n2, c1);
|
||||||
const r = mod(nv * (i - _1n), P);
|
const nv = Fp.mul(n, v);
|
||||||
return r;
|
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
|
// Other cases: Tonelli-Shanks algorithm
|
||||||
if (legendre(n, P) !== _1n) throw new Error('Cannot find square root');
|
return tonelliShanks(P);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Little-endian check for first LE bit (last BE bit);
|
// Little-endian check for first LE bit (last BE bit);
|
||||||
@@ -156,13 +212,13 @@ export interface Field<T> {
|
|||||||
// 1-arg
|
// 1-arg
|
||||||
create: (num: T) => T;
|
create: (num: T) => T;
|
||||||
isValid: (num: T) => boolean;
|
isValid: (num: T) => boolean;
|
||||||
isZero: (num: T) => boolean;
|
is0: (num: T) => boolean;
|
||||||
negate(num: T): T;
|
neg(num: T): T;
|
||||||
invert(num: T): T;
|
inv(num: T): T;
|
||||||
sqrt(num: T): T;
|
sqrt(num: T): T;
|
||||||
square(num: T): T;
|
sqr(num: T): T;
|
||||||
// 2-args
|
// 2-args
|
||||||
equals(lhs: T, rhs: T): boolean;
|
eql(lhs: T, rhs: T): boolean;
|
||||||
add(lhs: T, rhs: T): T;
|
add(lhs: T, rhs: T): T;
|
||||||
sub(lhs: T, rhs: T): T;
|
sub(lhs: T, rhs: T): T;
|
||||||
mul(lhs: T, rhs: T | bigint): T;
|
mul(lhs: T, rhs: T | bigint): T;
|
||||||
@@ -172,12 +228,13 @@ export interface Field<T> {
|
|||||||
addN(lhs: T, rhs: T): T;
|
addN(lhs: T, rhs: T): T;
|
||||||
subN(lhs: T, rhs: T): T;
|
subN(lhs: T, rhs: T): T;
|
||||||
mulN(lhs: T, rhs: T | bigint): T;
|
mulN(lhs: T, rhs: T | bigint): T;
|
||||||
squareN(num: T): T;
|
sqrN(num: T): T;
|
||||||
|
|
||||||
// Optional
|
// Optional
|
||||||
// Should be same as sgn0 function in https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/
|
// 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
|
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;
|
pow(lhs: T, power: bigint): T;
|
||||||
invertBatch: (lst: T[]) => T[];
|
invertBatch: (lst: T[]) => T[];
|
||||||
toBytes(num: T): Uint8Array;
|
toBytes(num: T): Uint8Array;
|
||||||
@@ -187,23 +244,22 @@ export interface Field<T> {
|
|||||||
}
|
}
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
const FIELD_FIELDS = [
|
const FIELD_FIELDS = [
|
||||||
'create', 'isValid', 'isZero', 'negate', 'invert', 'sqrt', 'square',
|
'create', 'isValid', 'is0', 'neg', 'inv', 'sqrt', 'sqr',
|
||||||
'equals', 'add', 'sub', 'mul', 'pow', 'div',
|
'eql', 'add', 'sub', 'mul', 'pow', 'div',
|
||||||
'addN', 'subN', 'mulN', 'squareN'
|
'addN', 'subN', 'mulN', 'sqrN'
|
||||||
] as const;
|
] as const;
|
||||||
export function validateField<T>(field: Field<T>) {
|
export function validateField<T>(field: Field<T>) {
|
||||||
for (const i of ['ORDER', 'MASK'] as const) {
|
const initial = {
|
||||||
if (typeof field[i] !== 'bigint')
|
ORDER: 'bigint',
|
||||||
throw new Error(`Invalid field param ${i}=${field[i]} (${typeof field[i]})`);
|
MASK: 'bigint',
|
||||||
}
|
BYTES: 'isSafeInteger',
|
||||||
for (const i of ['BYTES', 'BITS'] as const) {
|
BITS: 'isSafeInteger',
|
||||||
if (typeof field[i] !== 'number')
|
} as Record<string, string>;
|
||||||
throw new Error(`Invalid field param ${i}=${field[i]} (${typeof field[i]})`);
|
const opts = FIELD_FIELDS.reduce((map, val: string) => {
|
||||||
}
|
map[val] = 'function';
|
||||||
for (const i of FIELD_FIELDS) {
|
return map;
|
||||||
if (typeof field[i] !== 'function')
|
}, initial);
|
||||||
throw new Error(`Invalid field param ${i}=${field[i]} (${typeof field[i]})`);
|
return validateObject(field, opts);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generic field functions
|
// Generic field functions
|
||||||
@@ -217,7 +273,7 @@ export function FpPow<T>(f: Field<T>, num: T, power: bigint): T {
|
|||||||
let d = num;
|
let d = num;
|
||||||
while (power > _0n) {
|
while (power > _0n) {
|
||||||
if (power & _1n) p = f.mul(p, d);
|
if (power & _1n) p = f.mul(p, d);
|
||||||
d = f.square(d);
|
d = f.sqr(d);
|
||||||
power >>= 1n;
|
power >>= 1n;
|
||||||
}
|
}
|
||||||
return p;
|
return p;
|
||||||
@@ -227,15 +283,15 @@ export function FpInvertBatch<T>(f: Field<T>, nums: T[]): T[] {
|
|||||||
const tmp = new Array(nums.length);
|
const tmp = new Array(nums.length);
|
||||||
// Walk from first to last, multiply them by each other MOD p
|
// Walk from first to last, multiply them by each other MOD p
|
||||||
const lastMultiplied = nums.reduce((acc, num, i) => {
|
const lastMultiplied = nums.reduce((acc, num, i) => {
|
||||||
if (f.isZero(num)) return acc;
|
if (f.is0(num)) return acc;
|
||||||
tmp[i] = acc;
|
tmp[i] = acc;
|
||||||
return f.mul(acc, num);
|
return f.mul(acc, num);
|
||||||
}, f.ONE);
|
}, f.ONE);
|
||||||
// Invert last element
|
// 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
|
// Walk from last to first, multiply them by inverted each other MOD p
|
||||||
nums.reduceRight((acc, num, i) => {
|
nums.reduceRight((acc, num, i) => {
|
||||||
if (f.isZero(num)) return acc;
|
if (f.is0(num)) return acc;
|
||||||
tmp[i] = f.mul(acc, tmp[i]);
|
tmp[i] = f.mul(acc, tmp[i]);
|
||||||
return f.mul(acc, num);
|
return f.mul(acc, num);
|
||||||
}, inverted);
|
}, 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 {
|
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:
|
// NOTE: very fragile, always bench. Major performance points:
|
||||||
// - NonNormalized ops
|
// - NonNormalized ops
|
||||||
// - Object.freeze
|
// - Object.freeze
|
||||||
// - same shape of object (don't add/remove keys)
|
// - same shape of object (don't add/remove keys)
|
||||||
|
type FpField = Field<bigint> & Required<Pick<Field<bigint>, 'isOdd'>>;
|
||||||
export function Fp(
|
export function Fp(
|
||||||
ORDER: bigint,
|
ORDER: bigint,
|
||||||
bitLen?: number,
|
bitLen?: number,
|
||||||
isLE = false,
|
isLE = false,
|
||||||
redef: Partial<Field<bigint>> = {}
|
redef: Partial<Field<bigint>> = {}
|
||||||
): Readonly<Field<bigint>> {
|
): Readonly<FpField> {
|
||||||
if (ORDER <= _0n) throw new Error(`Expected Fp ORDER > 0, got ${ORDER}`);
|
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');
|
if (BYTES > 2048) throw new Error('Field lengths over 2048 bytes are not supported');
|
||||||
const sqrtP = (num: bigint) => sqrt(num, ORDER);
|
const sqrtP = FpSqrt(ORDER);
|
||||||
const f: Field<bigint> = Object.freeze({
|
const f: Readonly<FpField> = Object.freeze({
|
||||||
ORDER,
|
ORDER,
|
||||||
BITS,
|
BITS,
|
||||||
BYTES,
|
BYTES,
|
||||||
MASK: utils.bitMask(BITS),
|
MASK: bitMask(BITS),
|
||||||
ZERO: _0n,
|
ZERO: _0n,
|
||||||
ONE: _1n,
|
ONE: _1n,
|
||||||
create: (num) => mod(num, ORDER),
|
create: (num) => mod(num, ORDER),
|
||||||
isValid: (num) => {
|
isValid: (num) => {
|
||||||
if (typeof num !== 'bigint')
|
if (typeof num !== 'bigint')
|
||||||
throw new Error(`Invalid field element: expected bigint, got ${typeof num}`);
|
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,
|
isOdd: (num) => (num & _1n) === _1n,
|
||||||
negate: (num) => mod(-num, ORDER),
|
neg: (num) => mod(-num, ORDER),
|
||||||
equals: (lhs, rhs) => lhs === rhs,
|
eql: (lhs, rhs) => lhs === rhs,
|
||||||
|
|
||||||
square: (num) => mod(num * num, ORDER),
|
sqr: (num) => mod(num * num, ORDER),
|
||||||
add: (lhs, rhs) => mod(lhs + rhs, ORDER),
|
add: (lhs, rhs) => mod(lhs + rhs, ORDER),
|
||||||
sub: (lhs, rhs) => mod(lhs - rhs, ORDER),
|
sub: (lhs, rhs) => mod(lhs - rhs, ORDER),
|
||||||
mul: (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),
|
div: (lhs, rhs) => mod(lhs * invert(rhs, ORDER), ORDER),
|
||||||
|
|
||||||
// Same as above, but doesn't normalize
|
// Same as above, but doesn't normalize
|
||||||
squareN: (num) => num * num,
|
sqrN: (num) => num * num,
|
||||||
addN: (lhs, rhs) => lhs + rhs,
|
addN: (lhs, rhs) => lhs + rhs,
|
||||||
subN: (lhs, rhs) => lhs - rhs,
|
subN: (lhs, rhs) => lhs - rhs,
|
||||||
mulN: (lhs, rhs) => lhs * rhs,
|
mulN: (lhs, rhs) => lhs * rhs,
|
||||||
|
|
||||||
invert: (num) => invert(num, ORDER),
|
inv: (num) => invert(num, ORDER),
|
||||||
sqrt: redef.sqrt || sqrtP,
|
sqrt: redef.sqrt || ((n) => sqrtP(f, n)),
|
||||||
invertBatch: (lst) => FpInvertBatch(f, lst),
|
invertBatch: (lst) => FpInvertBatch(f, lst),
|
||||||
// TODO: do we really need constant cmov?
|
// TODO: do we really need constant cmov?
|
||||||
// We don't have const-time bigints anyway, so probably will be not very useful
|
// We don't have const-time bigints anyway, so probably will be not very useful
|
||||||
cmov: (a, b, c) => (c ? b : a),
|
cmov: (a, b, c) => (c ? b : a),
|
||||||
toBytes: (num) =>
|
toBytes: (num) => (isLE ? numberToBytesLE(num, BYTES) : numberToBytesBE(num, BYTES)),
|
||||||
isLE ? utils.numberToBytesLE(num, BYTES) : utils.numberToBytesBE(num, BYTES),
|
|
||||||
|
|
||||||
fromBytes: (bytes) => {
|
fromBytes: (bytes) => {
|
||||||
if (bytes.length !== BYTES)
|
if (bytes.length !== BYTES)
|
||||||
throw new Error(`Fp.fromBytes: expected ${BYTES}, got ${bytes.length}`);
|
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);
|
return Object.freeze(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: re-use in bls/generic sqrt for field/etc?
|
export function FpSqrtOdd<T>(Fp: Field<T>, elm: T) {
|
||||||
// Something like sqrtUnsafe which always returns value, but sqrt throws exception if non-square
|
if (!Fp.isOdd) throw new Error(`Field doesn't have isOdd`);
|
||||||
// From draft-irtf-cfrg-hash-to-curve-16
|
const root = Fp.sqrt(elm);
|
||||||
export function FpSqrt<T>(Fp: Field<T>) {
|
return Fp.isOdd(root) ? root : Fp.neg(root);
|
||||||
// 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;
|
export function FpSqrtEven<T>(Fp: Field<T>, elm: T) {
|
||||||
const squareConst = (q - _1n) / _2n;
|
if (!Fp.isOdd) throw new Error(`Field doesn't have isOdd`);
|
||||||
// is_square(x) := { True, if x^((q - 1) / 2) is 0 or 1 in F;
|
const root = Fp.sqrt(elm);
|
||||||
// { False, otherwise.
|
return Fp.isOdd(root) ? Fp.neg(root) : root;
|
||||||
let isSquare: (x: T) => boolean = (x) => {
|
}
|
||||||
const p = Fp.pow(x, squareConst);
|
|
||||||
return Fp.equals(p, Fp.ZERO) || Fp.equals(p, Fp.ONE);
|
/**
|
||||||
};
|
* FIPS 186 B.4.1-compliant "constant-time" private key generation utility.
|
||||||
// Constant-time Tonelli-Shanks algorithm
|
* Can take (n+8) or more bytes of uniform input e.g. from CSPRNG or KDF
|
||||||
let l = _0n;
|
* and convert them into private scalar, with the modulo bias being neglible.
|
||||||
for (let o = q - _1n; o % _2n === _0n; o /= _2n) l += _1n;
|
* Needs at least 40 bytes of input for 32-byte private key.
|
||||||
const c1 = l; // 1. c1, the largest integer such that 2^c1 divides q - 1.
|
* https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
|
||||||
const c2 = (q - _1n) / _2n ** c1; // 2. c2 = (q - 1) / (2^c1) # Integer arithmetic
|
* @param hash hash output from SHA3 or a similar function
|
||||||
const c3 = (c2 - _1n) / _2n; // 3. c3 = (c2 - 1) / 2 # Integer arithmetic
|
* @returns valid private scalar
|
||||||
// 4. c4, a non-square value in F
|
*/
|
||||||
// 5. c5 = c4^c2 in F
|
export function hashToPrivateScalar(
|
||||||
let c4 = Fp.ONE;
|
hash: string | Uint8Array,
|
||||||
while (isSquare(c4)) c4 = Fp.add(c4, Fp.ONE);
|
groupOrder: bigint,
|
||||||
const c5 = Fp.pow(c4, c2);
|
isLE = false
|
||||||
|
): bigint {
|
||||||
let sqrt: (x: T) => T = (x) => {
|
hash = ensureBytes('privateHash', hash);
|
||||||
let z = Fp.pow(x, c3); // 1. z = x^c3
|
const hashLen = hash.length;
|
||||||
let t = Fp.square(z); // 2. t = z * z
|
const minLen = nLength(groupOrder).nByteLength + 8;
|
||||||
t = Fp.mul(t, x); // 3. t = t * x
|
if (minLen < 24 || hashLen < minLen || hashLen > 1024)
|
||||||
z = Fp.mul(z, x); // 4. z = z * x
|
throw new Error(`hashToPrivateScalar: expected ${minLen}-1024 bytes of input, got ${hashLen}`);
|
||||||
let b = t; // 5. b = t
|
const num = isLE ? bytesToNumberLE(hash) : bytesToNumberBE(hash);
|
||||||
let c = c5; // 6. c = c5
|
return mod(num, groupOrder - _1n) + _1n;
|
||||||
// 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 };
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,57 +1,48 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
import * as mod from './modular.js';
|
import { mod, pow } from './modular.js';
|
||||||
import {
|
import { bytesToNumberLE, ensureBytes, numberToBytesLE, validateObject } from './utils.js';
|
||||||
ensureBytes,
|
|
||||||
numberToBytesLE,
|
|
||||||
bytesToNumberLE,
|
|
||||||
// nLength,
|
|
||||||
} from './utils.js';
|
|
||||||
|
|
||||||
const _0n = BigInt(0);
|
const _0n = BigInt(0);
|
||||||
const _1n = BigInt(1);
|
const _1n = BigInt(1);
|
||||||
type Hex = string | Uint8Array;
|
type Hex = string | Uint8Array;
|
||||||
|
|
||||||
export type CurveType = {
|
export type CurveType = {
|
||||||
// Field over which we'll do calculations. Verify with:
|
P: bigint; // finite field prime
|
||||||
P: bigint;
|
|
||||||
nByteLength: number;
|
nByteLength: number;
|
||||||
adjustScalarBytes?: (bytes: Uint8Array) => Uint8Array;
|
adjustScalarBytes?: (bytes: Uint8Array) => Uint8Array;
|
||||||
domain?: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array;
|
domain?: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array;
|
||||||
a24: bigint; // Related to d, but cannot be derived from it
|
a: bigint;
|
||||||
montgomeryBits: number;
|
montgomeryBits: number;
|
||||||
powPminus2?: (x: bigint) => bigint;
|
powPminus2?: (x: bigint) => bigint;
|
||||||
xyToU?: (x: bigint, y: bigint) => bigint;
|
xyToU?: (x: bigint, y: bigint) => bigint;
|
||||||
Gu: string;
|
Gu: bigint;
|
||||||
|
randomBytes?: (bytesLength?: number) => Uint8Array;
|
||||||
};
|
};
|
||||||
export type CurveFn = {
|
export type CurveFn = {
|
||||||
scalarMult: (scalar: Hex, u: Hex) => Uint8Array;
|
scalarMult: (scalar: Hex, u: Hex) => Uint8Array;
|
||||||
scalarMultBase: (scalar: Hex) => Uint8Array;
|
scalarMultBase: (scalar: Hex) => Uint8Array;
|
||||||
getSharedSecret: (privateKeyA: Hex, publicKeyB: Hex) => Uint8Array;
|
getSharedSecret: (privateKeyA: Hex, publicKeyB: Hex) => Uint8Array;
|
||||||
getPublicKey: (privateKey: Hex) => Uint8Array;
|
getPublicKey: (privateKey: Hex) => Uint8Array;
|
||||||
Gu: string;
|
utils: { randomPrivateKey: () => Uint8Array };
|
||||||
|
GuBytes: Uint8Array;
|
||||||
};
|
};
|
||||||
|
|
||||||
function validateOpts(curve: CurveType) {
|
function validateOpts(curve: CurveType) {
|
||||||
for (const i of ['a24'] as const) {
|
validateObject(
|
||||||
if (typeof curve[i] !== 'bigint')
|
curve,
|
||||||
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
|
{
|
||||||
}
|
a: 'bigint',
|
||||||
for (const i of ['montgomeryBits', 'nByteLength'] as const) {
|
},
|
||||||
if (curve[i] === undefined) continue; // Optional
|
{
|
||||||
if (!Number.isSafeInteger(curve[i]))
|
montgomeryBits: 'isSafeInteger',
|
||||||
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
|
nByteLength: 'isSafeInteger',
|
||||||
}
|
adjustScalarBytes: 'function',
|
||||||
for (const fn of ['adjustScalarBytes', 'domain', 'powPminus2'] as const) {
|
domain: 'function',
|
||||||
if (curve[fn] === undefined) continue; // Optional
|
powPminus2: 'function',
|
||||||
if (typeof curve[fn] !== 'function') throw new Error(`Invalid ${fn} function`);
|
Gu: 'bigint',
|
||||||
}
|
|
||||||
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]})`);
|
|
||||||
}
|
}
|
||||||
|
);
|
||||||
// Set defaults
|
// Set defaults
|
||||||
// ...nLength(curve.n, curve.nBitLength),
|
|
||||||
return Object.freeze({ ...curve } as const);
|
return Object.freeze({ ...curve } as const);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,34 +51,14 @@ function validateOpts(curve: CurveType) {
|
|||||||
export function montgomery(curveDef: CurveType): CurveFn {
|
export function montgomery(curveDef: CurveType): CurveFn {
|
||||||
const CURVE = validateOpts(curveDef);
|
const CURVE = validateOpts(curveDef);
|
||||||
const { P } = CURVE;
|
const { P } = CURVE;
|
||||||
const modP = (a: bigint) => mod.mod(a, P);
|
const modP = (n: bigint) => mod(n, P);
|
||||||
const montgomeryBits = CURVE.montgomeryBits;
|
const montgomeryBits = CURVE.montgomeryBits;
|
||||||
const montgomeryBytes = Math.ceil(montgomeryBits / 8);
|
const montgomeryBytes = Math.ceil(montgomeryBits / 8);
|
||||||
const fieldLen = CURVE.nByteLength;
|
const fieldLen = CURVE.nByteLength;
|
||||||
const adjustScalarBytes = CURVE.adjustScalarBytes || ((bytes: Uint8Array) => bytes);
|
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));
|
||||||
|
|
||||||
/**
|
// cswap from RFC7748. But it is not from RFC7748!
|
||||||
* 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(swap, x_2, x_3):
|
cswap(swap, x_2, x_3):
|
||||||
dummy = mask(swap) AND (x_2 XOR x_3)
|
dummy = mask(swap) AND (x_2 XOR x_3)
|
||||||
@@ -104,7 +75,15 @@ export function montgomery(curveDef: CurveType): CurveFn {
|
|||||||
return [x_2, x_3];
|
return [x_2, x_3];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Accepts 0 as well
|
||||||
|
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
|
// x25519 from 4
|
||||||
|
// The constant a24 is (486662 - 2) / 4 = 121665 for curve25519/X25519
|
||||||
|
const a24 = (CURVE.a - BigInt(2)) / BigInt(4);
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param pointU u coordinate (x) on Montgomery Curve 25519
|
* @param pointU u coordinate (x) on Montgomery Curve 25519
|
||||||
@@ -112,13 +91,10 @@ export function montgomery(curveDef: CurveType): CurveFn {
|
|||||||
* @returns new Point on Montgomery curve
|
* @returns new Point on Montgomery curve
|
||||||
*/
|
*/
|
||||||
function montgomeryLadder(pointU: bigint, scalar: bigint): bigint {
|
function montgomeryLadder(pointU: bigint, scalar: bigint): bigint {
|
||||||
const { P } = CURVE;
|
const u = assertFieldElement(pointU);
|
||||||
const u = normalizeScalar(pointU, P);
|
|
||||||
// Section 5: Implementations MUST accept non-canonical values and process them as
|
// Section 5: Implementations MUST accept non-canonical values and process them as
|
||||||
// if they had been reduced modulo the field prime.
|
// 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;
|
const x_1 = u;
|
||||||
let x_2 = _1n;
|
let x_2 = _1n;
|
||||||
let z_2 = _0n;
|
let z_2 = _0n;
|
||||||
@@ -172,28 +148,21 @@ export function montgomery(curveDef: CurveType): CurveFn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function decodeUCoordinate(uEnc: Hex): bigint {
|
function decodeUCoordinate(uEnc: Hex): bigint {
|
||||||
const u = ensureBytes(uEnc, montgomeryBytes);
|
|
||||||
// Section 5: When receiving such an array, implementations of X25519
|
// Section 5: When receiving such an array, implementations of X25519
|
||||||
// MUST mask the most significant bit in the final byte.
|
// 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
|
// 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
|
// fieldLen - scalaryBytes = 1 for X448 and = 0 for X25519
|
||||||
u[fieldLen - 1] &= 127; // 0b0111_1111
|
const u = ensureBytes('u coordinate', 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);
|
return bytesToNumberLE(u);
|
||||||
}
|
}
|
||||||
|
|
||||||
function decodeScalar(n: Hex): bigint {
|
function decodeScalar(n: Hex): bigint {
|
||||||
const bytes = ensureBytes(n);
|
const bytes = ensureBytes('scalar', n);
|
||||||
if (bytes.length !== montgomeryBytes && bytes.length !== fieldLen)
|
if (bytes.length !== montgomeryBytes && bytes.length !== fieldLen)
|
||||||
throw new Error(`Expected ${montgomeryBytes} or ${fieldLen} bytes, got ${bytes.length}`);
|
throw new Error(`Expected ${montgomeryBytes} or ${fieldLen} bytes, got ${bytes.length}`);
|
||||||
return bytesToNumberLE(adjustScalarBytes(bytes));
|
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 {
|
function scalarMult(scalar: Hex, u: Hex): Uint8Array {
|
||||||
const pointU = decodeUCoordinate(u);
|
const pointU = decodeUCoordinate(u);
|
||||||
const _scalar = decodeScalar(scalar);
|
const _scalar = decodeScalar(scalar);
|
||||||
@@ -203,14 +172,10 @@ export function montgomery(curveDef: CurveType): CurveFn {
|
|||||||
if (pu === _0n) throw new Error('Invalid private or public key received');
|
if (pu === _0n) throw new Error('Invalid private or public key received');
|
||||||
return encodeUCoordinate(pu);
|
return encodeUCoordinate(pu);
|
||||||
}
|
}
|
||||||
/**
|
// Computes public key from private. By doing scalar multiplication of base point.
|
||||||
* Computes public key from private.
|
const GuBytes = encodeUCoordinate(CURVE.Gu);
|
||||||
* Executes scalar multiplication of curve's base point by scalar.
|
|
||||||
* @param scalar private key
|
|
||||||
* @returns new public key
|
|
||||||
*/
|
|
||||||
function scalarMultBase(scalar: Hex): Uint8Array {
|
function scalarMultBase(scalar: Hex): Uint8Array {
|
||||||
return scalarMult(scalar, CURVE.Gu);
|
return scalarMult(scalar, GuBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -218,6 +183,7 @@ export function montgomery(curveDef: CurveType): CurveFn {
|
|||||||
scalarMultBase,
|
scalarMultBase,
|
||||||
getSharedSecret: (privateKey: Hex, publicKey: Hex) => scalarMult(privateKey, publicKey),
|
getSharedSecret: (privateKey: Hex, publicKey: Hex) => scalarMult(privateKey, publicKey),
|
||||||
getPublicKey: (privateKey: Hex): Uint8Array => scalarMultBase(privateKey),
|
getPublicKey: (privateKey: Hex): Uint8Array => scalarMultBase(privateKey),
|
||||||
Gu: CURVE.Gu,
|
utils: { randomPrivateKey: () => CURVE.randomBytes!(CURVE.nByteLength) },
|
||||||
|
GuBytes: GuBytes,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
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) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
import * as mod from './modular.js';
|
|
||||||
const _0n = BigInt(0);
|
const _0n = BigInt(0);
|
||||||
const _1n = BigInt(1);
|
const _1n = BigInt(1);
|
||||||
const _2n = BigInt(2);
|
const _2n = BigInt(2);
|
||||||
|
const u8a = (a: any): a is Uint8Array => a instanceof Uint8Array;
|
||||||
|
|
||||||
// We accept hex strings besides Uint8Array for simplicity
|
// We accept hex strings besides Uint8Array for simplicity
|
||||||
export type Hex = Uint8Array | string;
|
export type Hex = Uint8Array | string;
|
||||||
// Very few implementations accept numbers, we do it to ease learning curve
|
// 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 = {
|
export type CHash = {
|
||||||
(message: Uint8Array | string): Uint8Array;
|
(message: Uint8Array | string): Uint8Array;
|
||||||
blockLen: number;
|
blockLen: number;
|
||||||
outputLen: number;
|
outputLen: number;
|
||||||
create(): any;
|
create(opts?: { dkLen?: number }): any; // For shake
|
||||||
};
|
};
|
||||||
|
export type FHash = (message: Uint8Array | string) => Uint8Array;
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
const hexes = Array.from({ length: 256 }, (v, i) => i.toString(16).padStart(2, '0'));
|
const hexes = Array.from({ length: 256 }, (v, i) => i.toString(16).padStart(2, '0'));
|
||||||
export function bytesToHex(uint8a: Uint8Array): string {
|
export function bytesToHex(bytes: Uint8Array): string {
|
||||||
if (!(uint8a instanceof Uint8Array)) throw new Error('Expected Uint8Array');
|
if (!u8a(bytes)) throw new Error('Uint8Array expected');
|
||||||
// pre-caching improves the speed 6x
|
// pre-caching improves the speed 6x
|
||||||
let hex = '';
|
let hex = '';
|
||||||
for (let i = 0; i < uint8a.length; i++) {
|
for (let i = 0; i < bytes.length; i++) {
|
||||||
hex += hexes[uint8a[i]];
|
hex += hexes[bytes[i]];
|
||||||
}
|
}
|
||||||
return hex;
|
return hex;
|
||||||
}
|
}
|
||||||
@@ -75,25 +33,21 @@ export function numberToHexUnpadded(num: number | bigint): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function hexToNumber(hex: string): bigint {
|
export function hexToNumber(hex: string): bigint {
|
||||||
if (typeof hex !== 'string') {
|
if (typeof hex !== 'string') throw new Error('hex string expected, got ' + typeof hex);
|
||||||
throw new TypeError('hexToNumber: expected string, got ' + typeof hex);
|
|
||||||
}
|
|
||||||
// Big Endian
|
// Big Endian
|
||||||
return BigInt(`0x${hex}`);
|
return BigInt(hex === '' ? '0' : `0x${hex}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Caching slows it down 2-3x
|
// Caching slows it down 2-3x
|
||||||
export function hexToBytes(hex: string): Uint8Array {
|
export function hexToBytes(hex: string): Uint8Array {
|
||||||
if (typeof hex !== 'string') {
|
if (typeof hex !== 'string') throw new Error('hex string expected, got ' + typeof hex);
|
||||||
throw new TypeError('hexToBytes: expected string, got ' + typeof hex);
|
if (hex.length % 2) throw new Error('hex string is invalid: unpadded ' + hex.length);
|
||||||
}
|
|
||||||
if (hex.length % 2) throw new Error('hexToBytes: received invalid unpadded hex ' + hex.length);
|
|
||||||
const array = new Uint8Array(hex.length / 2);
|
const array = new Uint8Array(hex.length / 2);
|
||||||
for (let i = 0; i < array.length; i++) {
|
for (let i = 0; i < array.length; i++) {
|
||||||
const j = i * 2;
|
const j = i * 2;
|
||||||
const hexByte = hex.slice(j, j + 2);
|
const hexByte = hex.slice(j, j + 2);
|
||||||
const byte = Number.parseInt(hexByte, 16);
|
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;
|
array[i] = byte;
|
||||||
}
|
}
|
||||||
return array;
|
return array;
|
||||||
@@ -103,62 +57,48 @@ export function hexToBytes(hex: string): Uint8Array {
|
|||||||
export function bytesToNumberBE(bytes: Uint8Array): bigint {
|
export function bytesToNumberBE(bytes: Uint8Array): bigint {
|
||||||
return hexToNumber(bytesToHex(bytes));
|
return hexToNumber(bytesToHex(bytes));
|
||||||
}
|
}
|
||||||
export function bytesToNumberLE(uint8a: Uint8Array): bigint {
|
export function bytesToNumberLE(bytes: Uint8Array): bigint {
|
||||||
if (!(uint8a instanceof Uint8Array)) throw new Error('Expected Uint8Array');
|
if (!u8a(bytes)) throw new Error('Uint8Array expected');
|
||||||
return BigInt('0x' + bytesToHex(Uint8Array.from(uint8a).reverse()));
|
return hexToNumber(bytesToHex(Uint8Array.from(bytes).reverse()));
|
||||||
}
|
}
|
||||||
|
|
||||||
export const numberToBytesBE = (n: bigint, len: number) =>
|
export const numberToBytesBE = (n: bigint, len: number) =>
|
||||||
hexToBytes(n.toString(16).padStart(len * 2, '0'));
|
hexToBytes(n.toString(16).padStart(len * 2, '0'));
|
||||||
export const numberToBytesLE = (n: bigint, len: number) => numberToBytesBE(n, len).reverse();
|
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 {
|
export function ensureBytes(title: string, hex: Hex, expectedLength?: number): Uint8Array {
|
||||||
|
let res: Uint8Array;
|
||||||
|
if (typeof hex === 'string') {
|
||||||
|
try {
|
||||||
|
res = hexToBytes(hex);
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(`${title} must be valid hex string, got "${hex}". Cause: ${e}`);
|
||||||
|
}
|
||||||
|
} else if (u8a(hex)) {
|
||||||
// Uint8Array.from() instead of hash.slice() because node.js Buffer
|
// Uint8Array.from() instead of hash.slice() because node.js Buffer
|
||||||
// is instance of Uint8Array, and its slice() creates **mutable** copy
|
// is instance of Uint8Array, and its slice() creates **mutable** copy
|
||||||
const bytes = hex instanceof Uint8Array ? Uint8Array.from(hex) : hexToBytes(hex);
|
res = Uint8Array.from(hex);
|
||||||
if (typeof expectedLength === 'number' && bytes.length !== expectedLength)
|
} else {
|
||||||
throw new Error(`Expected ${expectedLength} bytes`);
|
throw new Error(`${title} must be hex string or Uint8Array`);
|
||||||
return bytes;
|
}
|
||||||
|
const len = res.length;
|
||||||
|
if (typeof expectedLength === 'number' && len !== expectedLength)
|
||||||
|
throw new Error(`${title} expected ${expectedLength} bytes, got ${len}`);
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copies several Uint8Arrays into one.
|
// Copies several Uint8Arrays into one.
|
||||||
export function concatBytes(...arrays: Uint8Array[]): Uint8Array {
|
export function concatBytes(...arrs: Uint8Array[]): Uint8Array {
|
||||||
if (!arrays.every((b) => b instanceof Uint8Array)) throw new Error('Uint8Array list expected');
|
const r = new Uint8Array(arrs.reduce((sum, a) => sum + a.length, 0));
|
||||||
if (arrays.length === 1) return arrays[0];
|
let pad = 0; // walk through each item, ensure they have proper type
|
||||||
const length = arrays.reduce((a, arr) => a + arr.length, 0);
|
arrs.forEach((a) => {
|
||||||
const result = new Uint8Array(length);
|
if (!u8a(a)) throw new Error('Uint8Array expected');
|
||||||
for (let i = 0, pad = 0; i < arrays.length; i++) {
|
r.set(a, pad);
|
||||||
const arr = arrays[i];
|
pad += a.length;
|
||||||
result.set(arr, pad);
|
});
|
||||||
pad += arr.length;
|
return r;
|
||||||
}
|
|
||||||
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 equalBytes(b1: Uint8Array, b2: Uint8Array) {
|
export function equalBytes(b1: Uint8Array, b2: Uint8Array) {
|
||||||
@@ -168,6 +108,16 @@ export function equalBytes(b1: Uint8Array, b2: Uint8Array) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Global symbols in both browsers and Node.js since v11
|
||||||
|
// See https://github.com/microsoft/TypeScript/issues/31535
|
||||||
|
declare const TextEncoder: any;
|
||||||
|
export function utf8ToBytes(str: string): Uint8Array {
|
||||||
|
if (typeof str !== 'string') {
|
||||||
|
throw new Error(`utf8ToBytes expected string, got ${typeof str}`);
|
||||||
|
}
|
||||||
|
return new TextEncoder().encode(str);
|
||||||
|
}
|
||||||
|
|
||||||
// Bit operations
|
// Bit operations
|
||||||
|
|
||||||
// Amount of bits inside bigint (Same as n.toString(2).length)
|
// Amount of bits inside bigint (Same as n.toString(2).length)
|
||||||
@@ -185,3 +135,112 @@ export const bitSet = (n: bigint, pos: number, value: boolean) =>
|
|||||||
// Return mask for N bits (Same as BigInt(`0b${Array(i).fill('1').join('')}`))
|
// Return mask for N bits (Same as BigInt(`0b${Array(i).fill('1').join('')}`))
|
||||||
// Not using ** operator with bigints for old engines.
|
// Not using ** operator with bigints for old engines.
|
||||||
export const bitMask = (n: number) => (_2n << BigInt(n - 1)) - _1n;
|
export const bitMask = (n: number) => (_2n << BigInt(n - 1)) - _1n;
|
||||||
|
|
||||||
|
// DRBG
|
||||||
|
|
||||||
|
const u8n = (data?: any) => new Uint8Array(data); // creates Uint8Array
|
||||||
|
const u8fr = (arr: any) => Uint8Array.from(arr); // another shortcut
|
||||||
|
type Pred<T> = (v: Uint8Array) => T | undefined;
|
||||||
|
/**
|
||||||
|
* Minimal HMAC-DRBG from NIST 800-90 for RFC6979 sigs.
|
||||||
|
* @returns function that will call DRBG until 2nd arg returns something meaningful
|
||||||
|
* @example
|
||||||
|
* const drbg = createHmacDRBG<Key>(32, 32, hmac);
|
||||||
|
* drbg(seed, bytesToKey); // bytesToKey must return Key or undefined
|
||||||
|
*/
|
||||||
|
export function createHmacDrbg<T>(
|
||||||
|
hashLen: number,
|
||||||
|
qByteLen: number,
|
||||||
|
hmacFn: (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array
|
||||||
|
): (seed: Uint8Array, predicate: Pred<T>) => T {
|
||||||
|
if (typeof hashLen !== 'number' || hashLen < 2) throw new Error('hashLen must be a number');
|
||||||
|
if (typeof qByteLen !== 'number' || qByteLen < 2) throw new Error('qByteLen must be a number');
|
||||||
|
if (typeof hmacFn !== 'function') throw new Error('hmacFn must be a function');
|
||||||
|
// Step B, Step C: set hashLen to 8*ceil(hlen/8)
|
||||||
|
let v = u8n(hashLen); // Minimal non-full-spec HMAC-DRBG from NIST 800-90 for RFC6979 sigs.
|
||||||
|
let k = u8n(hashLen); // Steps B and C of RFC6979 3.2: set hashLen, in our case always same
|
||||||
|
let i = 0; // Iterations counter, will throw when over 1000
|
||||||
|
const reset = () => {
|
||||||
|
v.fill(1);
|
||||||
|
k.fill(0);
|
||||||
|
i = 0;
|
||||||
|
};
|
||||||
|
const h = (...b: Uint8Array[]) => hmacFn(k, v, ...b); // hmac(k)(v, ...values)
|
||||||
|
const reseed = (seed = u8n()) => {
|
||||||
|
// HMAC-DRBG reseed() function. Steps D-G
|
||||||
|
k = h(u8fr([0x00]), seed); // k = hmac(k || v || 0x00 || seed)
|
||||||
|
v = h(); // v = hmac(k || v)
|
||||||
|
if (seed.length === 0) return;
|
||||||
|
k = h(u8fr([0x01]), seed); // k = hmac(k || v || 0x01 || seed)
|
||||||
|
v = h(); // v = hmac(k || v)
|
||||||
|
};
|
||||||
|
const gen = () => {
|
||||||
|
// HMAC-DRBG generate() function
|
||||||
|
if (i++ >= 1000) throw new Error('drbg: tried 1000 values');
|
||||||
|
let len = 0;
|
||||||
|
const out: Uint8Array[] = [];
|
||||||
|
while (len < qByteLen) {
|
||||||
|
v = h();
|
||||||
|
const sl = v.slice();
|
||||||
|
out.push(sl);
|
||||||
|
len += v.length;
|
||||||
|
}
|
||||||
|
return concatBytes(...out);
|
||||||
|
};
|
||||||
|
const genUntil = (seed: Uint8Array, pred: Pred<T>): T => {
|
||||||
|
reset();
|
||||||
|
reseed(seed); // Steps D-G
|
||||||
|
let res: T | undefined = undefined; // Step H: grind until k is in [1..n-1]
|
||||||
|
while (!(res = pred(gen()))) reseed();
|
||||||
|
reset();
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
return genUntil;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validating curves and fields
|
||||||
|
|
||||||
|
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
230
src/bls12-381.ts
230
src/bls12-381.ts
@@ -1,10 +1,56 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
|
|
||||||
|
// bls12-381 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.
|
||||||
|
//
|
||||||
|
// The library uses G1 for public keys and G2 for signatures. Support for G1 signatures is planned.
|
||||||
|
// Compatible with Algorand, Chia, Dfinity, Ethereum, FIL, Zcash. Matches specs
|
||||||
|
// [pairing-curves-10](https://tools.ietf.org/html/draft-irtf-cfrg-pairing-friendly-curves-10),
|
||||||
|
// [bls-sigs-04](https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04),
|
||||||
|
// [hash-to-curve-12](https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-12).
|
||||||
|
//
|
||||||
|
// ### Summary
|
||||||
|
// 1. BLS Relies on Bilinear Pairing (expensive)
|
||||||
|
// 2. Private Keys: 32 bytes
|
||||||
|
// 3. Public Keys: 48 bytes: 381 bit affine x coordinate, encoded into 48 big-endian bytes.
|
||||||
|
// 4. Signatures: 96 bytes: two 381 bit integers (affine x coordinate), encoded into two 48 big-endian byte arrays.
|
||||||
|
// - The signature is a point on the G2 subgroup, which is defined over a finite field
|
||||||
|
// with elements twice as big as the G1 curve (G2 is over Fp2 rather than Fp. Fp2 is analogous to the complex numbers).
|
||||||
|
// 5. The 12 stands for the Embedding degree.
|
||||||
|
//
|
||||||
|
// ### Formulas
|
||||||
|
// - `P = pk x G` - public keys
|
||||||
|
// - `S = pk x H(m)` - signing
|
||||||
|
// - `e(P, H(m)) == e(G, S)` - verification using pairings
|
||||||
|
// - `e(G, S) = e(G, SUM(n)(Si)) = MUL(n)(e(G, Si))` - signature aggregation
|
||||||
|
// Filecoin uses little endian byte arrays for private keys -
|
||||||
|
// so ensure to reverse byte order if you'll use it with FIL.
|
||||||
|
//
|
||||||
|
// ### Resources
|
||||||
|
// - [BLS12-381 for the rest of us](https://hackmd.io/@benjaminion/bls12-381)
|
||||||
|
// - [Key concepts of pairings](https://medium.com/@alonmuroch_65570/bls-signatures-part-2-key-concepts-of-pairings-27a8a9533d0c)
|
||||||
|
// - Pairing over bls12-381:
|
||||||
|
// [part 1](https://research.nccgroup.com/2020/07/06/pairing-over-bls12-381-part-1-fields/),
|
||||||
|
// [part 2](https://research.nccgroup.com/2020/07/13/pairing-over-bls12-381-part-2-curves/),
|
||||||
|
// [part 3](https://research.nccgroup.com/2020/08/13/pairing-over-bls12-381-part-3-pairing/)
|
||||||
|
// - [Estimating the bit security of pairing-friendly curves](https://research.nccgroup.com/2022/02/03/estimating-the-bit-security-of-pairing-friendly-curves/)
|
||||||
|
//
|
||||||
|
// ### 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 { sha256 } from '@noble/hashes/sha256';
|
||||||
import { randomBytes } from '@noble/hashes/utils';
|
import { randomBytes } from '@noble/hashes/utils';
|
||||||
import { bls, CurveFn } from './abstract/bls.js';
|
import { bls, CurveFn } from './abstract/bls.js';
|
||||||
import * as mod from './abstract/modular.js';
|
import * as mod from './abstract/modular.js';
|
||||||
import {
|
import {
|
||||||
concatBytes,
|
concatBytes as concatB,
|
||||||
ensureBytes,
|
ensureBytes,
|
||||||
numberToBytesBE,
|
numberToBytesBE,
|
||||||
bytesToNumberBE,
|
bytesToNumberBE,
|
||||||
@@ -16,21 +62,13 @@ import {
|
|||||||
} from './abstract/utils.js';
|
} from './abstract/utils.js';
|
||||||
// Types
|
// Types
|
||||||
import {
|
import {
|
||||||
PointType,
|
ProjPointType,
|
||||||
ProjectivePointType,
|
ProjConstructor,
|
||||||
ProjectiveConstructor,
|
|
||||||
mapToCurveSimpleSWU,
|
mapToCurveSimpleSWU,
|
||||||
|
AffinePoint,
|
||||||
} from './abstract/weierstrass.js';
|
} from './abstract/weierstrass.js';
|
||||||
import { isogenyMap } from './abstract/hash-to-curve.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
|
// CURVE FIELDS
|
||||||
// Finite field over p.
|
// Finite field over p.
|
||||||
const Fp =
|
const Fp =
|
||||||
@@ -95,25 +133,24 @@ const Fp2: mod.Field<Fp2> & Fp2Utils = {
|
|||||||
ONE: { c0: Fp.ONE, c1: Fp.ZERO },
|
ONE: { c0: Fp.ONE, c1: Fp.ZERO },
|
||||||
create: (num) => num,
|
create: (num) => num,
|
||||||
isValid: ({ c0, c1 }) => typeof c0 === 'bigint' && typeof c1 === 'bigint',
|
isValid: ({ c0, c1 }) => typeof c0 === 'bigint' && typeof c1 === 'bigint',
|
||||||
isZero: ({ c0, c1 }) => Fp.isZero(c0) && Fp.isZero(c1),
|
is0: ({ c0, c1 }) => Fp.is0(c0) && Fp.is0(c1),
|
||||||
equals: ({ c0, c1 }: Fp2, { c0: r0, c1: r1 }: Fp2) => Fp.equals(c0, r0) && Fp.equals(c1, r1),
|
eql: ({ c0, c1 }: Fp2, { c0: r0, c1: r1 }: Fp2) => Fp.eql(c0, r0) && Fp.eql(c1, r1),
|
||||||
negate: ({ c0, c1 }) => ({ c0: Fp.negate(c0), c1: Fp.negate(c1) }),
|
neg: ({ c0, c1 }) => ({ c0: Fp.neg(c0), c1: Fp.neg(c1) }),
|
||||||
pow: (num, power) => mod.FpPow(Fp2, num, power),
|
pow: (num, power) => mod.FpPow(Fp2, num, power),
|
||||||
invertBatch: (nums) => mod.FpInvertBatch(Fp2, nums),
|
invertBatch: (nums) => mod.FpInvertBatch(Fp2, nums),
|
||||||
// Normalized
|
// Normalized
|
||||||
add: Fp2Add,
|
add: Fp2Add,
|
||||||
sub: Fp2Subtract,
|
sub: Fp2Subtract,
|
||||||
mul: Fp2Multiply,
|
mul: Fp2Multiply,
|
||||||
square: Fp2Square,
|
sqr: Fp2Square,
|
||||||
// NonNormalized stuff
|
// NonNormalized stuff
|
||||||
addN: Fp2Add,
|
addN: Fp2Add,
|
||||||
subN: Fp2Subtract,
|
subN: Fp2Subtract,
|
||||||
mulN: Fp2Multiply,
|
mulN: Fp2Multiply,
|
||||||
squareN: Fp2Square,
|
sqrN: Fp2Square,
|
||||||
// Why inversion for bigint inside Fp instead of Fp2? it is even used in that context?
|
// Why inversion for bigint inside Fp instead of Fp2? it is even used in that context?
|
||||||
div: (lhs, rhs) =>
|
div: (lhs, rhs) => Fp2.mul(lhs, typeof rhs === 'bigint' ? Fp.inv(Fp.create(rhs)) : Fp2.inv(rhs)),
|
||||||
Fp2.mul(lhs, typeof rhs === 'bigint' ? Fp.invert(Fp.create(rhs)) : Fp2.invert(rhs)),
|
inv: ({ c0: a, c1: b }) => {
|
||||||
invert: ({ c0: a, c1: b }) => {
|
|
||||||
// We wish to find the multiplicative inverse of a nonzero
|
// We wish to find the multiplicative inverse of a nonzero
|
||||||
// element a + bu in Fp2. We leverage an identity
|
// element a + bu in Fp2. We leverage an identity
|
||||||
//
|
//
|
||||||
@@ -127,10 +164,11 @@ const Fp2: mod.Field<Fp2> & Fp2Utils = {
|
|||||||
// This gives that (a - bu)/(a² + b²) is the inverse
|
// This gives that (a - bu)/(a² + b²) is the inverse
|
||||||
// of (a + bu). Importantly, this can be computing using
|
// of (a + bu). Importantly, this can be computing using
|
||||||
// only a single inversion in Fp.
|
// 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)) };
|
return { c0: Fp.mul(factor, Fp.create(a)), c1: Fp.mul(factor, Fp.create(-b)) };
|
||||||
},
|
},
|
||||||
sqrt: (num) => {
|
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.
|
// TODO: Optimize this line. It's extremely slow.
|
||||||
// Speeding this up would boost aggregateSignatures.
|
// Speeding this up would boost aggregateSignatures.
|
||||||
// https://eprint.iacr.org/2012/685.pdf applicable?
|
// https://eprint.iacr.org/2012/685.pdf applicable?
|
||||||
@@ -138,15 +176,15 @@ const Fp2: mod.Field<Fp2> & Fp2Utils = {
|
|||||||
// https://github.com/supranational/blst/blob/aae0c7d70b799ac269ff5edf29d8191dbd357876/src/exp2.c#L1
|
// 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
|
// 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 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 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');
|
if (!divisor) throw new Error('No root');
|
||||||
const index = R.indexOf(divisor);
|
const index = R.indexOf(divisor);
|
||||||
const root = R[index / 2];
|
const root = R[index / 2];
|
||||||
if (!root) throw new Error('Invalid root');
|
if (!root) throw new Error('Invalid root');
|
||||||
const x1 = Fp2.div(candidateSqrt, 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: re1, im: im1 } = Fp2.reim(x1);
|
||||||
const { re: re2, im: im2 } = Fp2.reim(x2);
|
const { re: re2, im: im2 } = Fp2.reim(x2);
|
||||||
if (im1 > im2 || (im1 === im2 && re1 > re2)) return x1;
|
if (im1 > im2 || (im1 === im2 && re1 > re2)) return x1;
|
||||||
@@ -165,7 +203,7 @@ const Fp2: mod.Field<Fp2> & Fp2Utils = {
|
|||||||
if (b.length !== Fp2.BYTES) throw new Error(`fromBytes wrong length=${b.length}`);
|
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)) };
|
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) => ({
|
cmov: ({ c0, c1 }, { c0: r0, c1: r1 }, c) => ({
|
||||||
c0: Fp.cmov(c0, r0, c),
|
c0: Fp.cmov(c0, r0, c),
|
||||||
c1: Fp.cmov(c1, r1, c),
|
c1: Fp.cmov(c1, r1, c),
|
||||||
@@ -275,18 +313,15 @@ const Fp6Multiply = ({ c0, c1, c2 }: Fp6, rhs: Fp6 | bigint) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
const Fp6Square = ({ c0, c1, c2 }: Fp6) => {
|
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 t1 = Fp2.mul(Fp2.mul(c0, c1), 2n); // 2 * c0 * c1
|
||||||
let t3 = Fp2.mul(Fp2.mul(c1, c2), 2n); // 2 * c1 * c2
|
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 {
|
return {
|
||||||
c0: Fp2.add(Fp2.mulByNonresidue(t3), t0), // T3 * (u + 1) + T0
|
c0: Fp2.add(Fp2.mulByNonresidue(t3), t0), // T3 * (u + 1) + T0
|
||||||
c1: Fp2.add(Fp2.mulByNonresidue(t4), t1), // T4 * (u + 1) + T1
|
c1: Fp2.add(Fp2.mulByNonresidue(t4), t1), // T4 * (u + 1) + T1
|
||||||
// T1 + (c0 - c1 + c2)² + T3 - T0 - T4
|
// T1 + (c0 - c1 + c2)² + T3 - T0 - T4
|
||||||
c2: Fp2.sub(
|
c2: Fp2.sub(Fp2.sub(Fp2.add(Fp2.add(t1, Fp2.sqr(Fp2.add(Fp2.sub(c0, c1), c2))), t3), t0), t4),
|
||||||
Fp2.sub(Fp2.add(Fp2.add(t1, Fp2.square(Fp2.add(Fp2.sub(c0, c1), c2))), t3), t0),
|
|
||||||
t4
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
type Fp6Utils = {
|
type Fp6Utils = {
|
||||||
@@ -307,35 +342,34 @@ const Fp6: mod.Field<Fp6> & Fp6Utils = {
|
|||||||
ONE: { c0: Fp2.ONE, c1: Fp2.ZERO, c2: Fp2.ZERO },
|
ONE: { c0: Fp2.ONE, c1: Fp2.ZERO, c2: Fp2.ZERO },
|
||||||
create: (num) => num,
|
create: (num) => num,
|
||||||
isValid: ({ c0, c1, c2 }) => Fp2.isValid(c0) && Fp2.isValid(c1) && Fp2.isValid(c2),
|
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),
|
is0: ({ c0, c1, c2 }) => Fp2.is0(c0) && Fp2.is0(c1) && Fp2.is0(c2),
|
||||||
negate: ({ c0, c1, c2 }) => ({ c0: Fp2.negate(c0), c1: Fp2.negate(c1), c2: Fp2.negate(c2) }),
|
neg: ({ c0, c1, c2 }) => ({ c0: Fp2.neg(c0), c1: Fp2.neg(c1), c2: Fp2.neg(c2) }),
|
||||||
equals: ({ c0, c1, c2 }, { c0: r0, c1: r1, c2: r2 }) =>
|
eql: ({ c0, c1, c2 }, { c0: r0, c1: r1, c2: r2 }) =>
|
||||||
Fp2.equals(c0, r0) && Fp2.equals(c1, r1) && Fp2.equals(c2, r2),
|
Fp2.eql(c0, r0) && Fp2.eql(c1, r1) && Fp2.eql(c2, r2),
|
||||||
sqrt: () => {
|
sqrt: () => {
|
||||||
throw new Error('Not implemented');
|
throw new Error('Not implemented');
|
||||||
},
|
},
|
||||||
// Do we need division by bigint at all? Should be done via order:
|
// Do we need division by bigint at all? Should be done via order:
|
||||||
div: (lhs, rhs) =>
|
div: (lhs, rhs) => Fp6.mul(lhs, typeof rhs === 'bigint' ? Fp.inv(Fp.create(rhs)) : Fp6.inv(rhs)),
|
||||||
Fp6.mul(lhs, typeof rhs === 'bigint' ? Fp.invert(Fp.create(rhs)) : Fp6.invert(rhs)),
|
|
||||||
pow: (num, power) => mod.FpPow(Fp6, num, power),
|
pow: (num, power) => mod.FpPow(Fp6, num, power),
|
||||||
invertBatch: (nums) => mod.FpInvertBatch(Fp6, nums),
|
invertBatch: (nums) => mod.FpInvertBatch(Fp6, nums),
|
||||||
// Normalized
|
// Normalized
|
||||||
add: Fp6Add,
|
add: Fp6Add,
|
||||||
sub: Fp6Subtract,
|
sub: Fp6Subtract,
|
||||||
mul: Fp6Multiply,
|
mul: Fp6Multiply,
|
||||||
square: Fp6Square,
|
sqr: Fp6Square,
|
||||||
// NonNormalized stuff
|
// NonNormalized stuff
|
||||||
addN: Fp6Add,
|
addN: Fp6Add,
|
||||||
subN: Fp6Subtract,
|
subN: Fp6Subtract,
|
||||||
mulN: Fp6Multiply,
|
mulN: Fp6Multiply,
|
||||||
squareN: Fp6Square,
|
sqrN: Fp6Square,
|
||||||
|
|
||||||
invert: ({ c0, c1, c2 }) => {
|
inv: ({ c0, c1, c2 }) => {
|
||||||
let t0 = Fp2.sub(Fp2.square(c0), Fp2.mulByNonresidue(Fp2.mul(c2, c1))); // c0² - c2 * c1 * (u + 1)
|
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.square(c2)), Fp2.mul(c0, c1)); // c2² * (u + 1) - c0 * c1
|
let t1 = Fp2.sub(Fp2.mulByNonresidue(Fp2.sqr(c2)), Fp2.mul(c0, c1)); // c2² * (u + 1) - c0 * c1
|
||||||
let t2 = Fp2.sub(Fp2.square(c1), Fp2.mul(c0, c2)); // c1² - c0 * c2
|
let t2 = Fp2.sub(Fp2.sqr(c1), Fp2.mul(c0, c2)); // c1² - c0 * c2
|
||||||
// 1/(((c2 * T1 + c1 * T2) * v) + c0 * T0)
|
// 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))
|
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) };
|
return { c0: Fp2.mul(t4, t0), c1: Fp2.mul(t4, t1), c2: Fp2.mul(t4, t2) };
|
||||||
@@ -350,7 +384,7 @@ const Fp6: mod.Field<Fp6> & Fp6Utils = {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
toBytes: ({ c0, c1, c2 }): Uint8Array =>
|
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) => ({
|
cmov: ({ c0, c1, c2 }: Fp6, { c0: r0, c1: r1, c2: r2 }: Fp6, c) => ({
|
||||||
c0: Fp2.cmov(c0, r0, c),
|
c0: Fp2.cmov(c0, r0, c),
|
||||||
c1: Fp2.cmov(c1, r1, c),
|
c1: Fp2.cmov(c1, r1, c),
|
||||||
@@ -493,11 +527,11 @@ const Fp12Square = ({ c0, c1 }: Fp12) => {
|
|||||||
}; // AB + AB
|
}; // AB + AB
|
||||||
};
|
};
|
||||||
function Fp4Square(a: Fp2, b: Fp2): { first: Fp2; second: Fp2 } {
|
function Fp4Square(a: Fp2, b: Fp2): { first: Fp2; second: Fp2 } {
|
||||||
const a2 = Fp2.square(a);
|
const a2 = Fp2.sqr(a);
|
||||||
const b2 = Fp2.square(b);
|
const b2 = Fp2.sqr(b);
|
||||||
return {
|
return {
|
||||||
first: Fp2.add(Fp2.mulByNonresidue(b2), a2), // b² * Nonresidue + a²
|
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 = {
|
type Fp12Utils = {
|
||||||
@@ -520,30 +554,30 @@ const Fp12: mod.Field<Fp12> & Fp12Utils = {
|
|||||||
ONE: { c0: Fp6.ONE, c1: Fp6.ZERO },
|
ONE: { c0: Fp6.ONE, c1: Fp6.ZERO },
|
||||||
create: (num) => num,
|
create: (num) => num,
|
||||||
isValid: ({ c0, c1 }) => Fp6.isValid(c0) && Fp6.isValid(c1),
|
isValid: ({ c0, c1 }) => Fp6.isValid(c0) && Fp6.isValid(c1),
|
||||||
isZero: ({ c0, c1 }) => Fp6.isZero(c0) && Fp6.isZero(c1),
|
is0: ({ c0, c1 }) => Fp6.is0(c0) && Fp6.is0(c1),
|
||||||
negate: ({ c0, c1 }) => ({ c0: Fp6.negate(c0), c1: Fp6.negate(c1) }),
|
neg: ({ c0, c1 }) => ({ c0: Fp6.neg(c0), c1: Fp6.neg(c1) }),
|
||||||
equals: ({ c0, c1 }, { c0: r0, c1: r1 }) => Fp6.equals(c0, r0) && Fp6.equals(c1, r1),
|
eql: ({ c0, c1 }, { c0: r0, c1: r1 }) => Fp6.eql(c0, r0) && Fp6.eql(c1, r1),
|
||||||
sqrt: () => {
|
sqrt: () => {
|
||||||
throw new Error('Not implemented');
|
throw new Error('Not implemented');
|
||||||
},
|
},
|
||||||
invert: ({ c0, c1 }) => {
|
inv: ({ c0, c1 }) => {
|
||||||
let t = Fp6.invert(Fp6.sub(Fp6.square(c0), Fp6.mulByNonresidue(Fp6.square(c1)))); // 1 / (c0² - c1² * v)
|
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.negate(Fp6.mul(c1, t)) }; // ((C0 * T) * T) + (-C1 * T) * w
|
return { c0: Fp6.mul(c0, t), c1: Fp6.neg(Fp6.mul(c1, t)) }; // ((C0 * T) * T) + (-C1 * T) * w
|
||||||
},
|
},
|
||||||
div: (lhs, rhs) =>
|
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),
|
pow: (num, power) => mod.FpPow(Fp12, num, power),
|
||||||
invertBatch: (nums) => mod.FpInvertBatch(Fp12, nums),
|
invertBatch: (nums) => mod.FpInvertBatch(Fp12, nums),
|
||||||
// Normalized
|
// Normalized
|
||||||
add: Fp12Add,
|
add: Fp12Add,
|
||||||
sub: Fp12Subtract,
|
sub: Fp12Subtract,
|
||||||
mul: Fp12Multiply,
|
mul: Fp12Multiply,
|
||||||
square: Fp12Square,
|
sqr: Fp12Square,
|
||||||
// NonNormalized stuff
|
// NonNormalized stuff
|
||||||
addN: Fp12Add,
|
addN: Fp12Add,
|
||||||
subN: Fp12Subtract,
|
subN: Fp12Subtract,
|
||||||
mulN: Fp12Multiply,
|
mulN: Fp12Multiply,
|
||||||
squareN: Fp12Square,
|
sqrN: Fp12Square,
|
||||||
|
|
||||||
// Bytes utils
|
// Bytes utils
|
||||||
fromBytes: (b: Uint8Array): Fp12 => {
|
fromBytes: (b: Uint8Array): Fp12 => {
|
||||||
@@ -553,7 +587,7 @@ const Fp12: mod.Field<Fp12> & Fp12Utils = {
|
|||||||
c1: Fp6.fromBytes(b.subarray(Fp6.BYTES)),
|
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) => ({
|
cmov: ({ c0, c1 }, { c0: r0, c1: r1 }, c) => ({
|
||||||
c0: Fp6.cmov(c0, r0, c),
|
c0: Fp6.cmov(c0, r0, c),
|
||||||
c1: Fp6.cmov(c1, r1, c),
|
c1: Fp6.cmov(c1, r1, c),
|
||||||
@@ -597,7 +631,7 @@ const Fp12: mod.Field<Fp12> & Fp12Utils = {
|
|||||||
c0: Fp6.multiplyByFp2(c0, rhs),
|
c0: Fp6.multiplyByFp2(c0, rhs),
|
||||||
c1: Fp6.multiplyByFp2(c1, 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
|
// A cyclotomic group is a subgroup of Fp^n defined by
|
||||||
// GΦₙ(p) = {α ∈ Fpⁿ : α^Φₙ(p) = 1}
|
// GΦₙ(p) = {α ∈ Fpⁿ : α^Φₙ(p) = 1}
|
||||||
@@ -881,7 +915,7 @@ function psi(x: Fp2, y: Fp2): [Fp2, Fp2] {
|
|||||||
return [x2, y2];
|
return [x2, y2];
|
||||||
}
|
}
|
||||||
// Ψ endomorphism
|
// Ψ endomorphism
|
||||||
function G2psi(c: ProjectiveConstructor<Fp2>, P: ProjectivePointType<Fp2>) {
|
function G2psi(c: ProjConstructor<Fp2>, P: ProjPointType<Fp2>) {
|
||||||
const affine = P.toAffine();
|
const affine = P.toAffine();
|
||||||
const p = psi(affine.x, affine.y);
|
const p = psi(affine.x, affine.y);
|
||||||
return new c(p[0], p[1], Fp2.ONE);
|
return new c(p[0], p[1], Fp2.ONE);
|
||||||
@@ -892,9 +926,9 @@ const PSI2_C1 =
|
|||||||
0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaacn;
|
0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaacn;
|
||||||
|
|
||||||
function psi2(x: Fp2, y: Fp2): [Fp2, Fp2] {
|
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 affine = P.toAffine();
|
||||||
const p = psi2(affine.x, affine.y);
|
const p = psi2(affine.x, affine.y);
|
||||||
return new c(p[0], p[1], Fp2.ONE);
|
return new c(p[0], p[1], Fp2.ONE);
|
||||||
@@ -910,11 +944,12 @@ function G2psi2(c: ProjectiveConstructor<Fp2>, P: ProjectivePointType<Fp2>) {
|
|||||||
// p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab
|
// p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab
|
||||||
// m = 2 (or 1 for G1 see section 8.8.1)
|
// m = 2 (or 1 for G1 see section 8.8.1)
|
||||||
// k = 128
|
// k = 128
|
||||||
const htfDefaults = {
|
const htfDefaults = Object.freeze({
|
||||||
// DST: a domain separation tag
|
// DST: a domain separation tag
|
||||||
// defined in section 2.2.5
|
// defined in section 2.2.5
|
||||||
// Use utils.getDSTLabel(), utils.setDSTLabel(value)
|
// Use utils.getDSTLabel(), utils.setDSTLabel(value)
|
||||||
DST: 'BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_',
|
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
|
// p: the characteristic of F
|
||||||
// where F is a finite field of characteristic p and order q = p^m
|
// where F is a finite field of characteristic p and order q = p^m
|
||||||
p: Fp.ORDER,
|
p: Fp.ORDER,
|
||||||
@@ -926,12 +961,12 @@ const htfDefaults = {
|
|||||||
k: 128,
|
k: 128,
|
||||||
// option to use a message that has already been processed by
|
// option to use a message that has already been processed by
|
||||||
// expand_message_xmd
|
// expand_message_xmd
|
||||||
expand: true,
|
expand: 'xmd',
|
||||||
// Hash functions for: expand_message_xmd is appropriate for use with a
|
// 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.
|
// 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
|
// BBS+ uses blake2: https://github.com/hyperledger/aries-framework-go/issues/2247
|
||||||
hash: sha256,
|
hash: sha256,
|
||||||
};
|
} as const);
|
||||||
|
|
||||||
// Encoding utils
|
// Encoding utils
|
||||||
// Point on G1 curve: (x, y)
|
// Point on G1 curve: (x, y)
|
||||||
@@ -984,7 +1019,7 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|||||||
// φ endomorphism
|
// φ endomorphism
|
||||||
const cubicRootOfUnityModP =
|
const cubicRootOfUnityModP =
|
||||||
0x5f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffen;
|
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
|
// todo: unroll
|
||||||
const xP = point.multiplyUnsafe(bls12_381.CURVE.x).negate(); // [x]P
|
const xP = point.multiplyUnsafe(bls12_381.CURVE.x).negate(); // [x]P
|
||||||
@@ -1013,7 +1048,7 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|||||||
const { x, y } = G1_SWU(Fp.create(scalars[0]));
|
const { x, y } = G1_SWU(Fp.create(scalars[0]));
|
||||||
return isogenyMapG1(x, y);
|
return isogenyMapG1(x, y);
|
||||||
},
|
},
|
||||||
fromBytes: (bytes: Uint8Array): { x: Fp; y: Fp } => {
|
fromBytes: (bytes: Uint8Array): AffinePoint<Fp> => {
|
||||||
if (bytes.length === 48) {
|
if (bytes.length === 48) {
|
||||||
const P = Fp.ORDER;
|
const P = Fp.ORDER;
|
||||||
const compressedValue = bytesToNumberBE(bytes);
|
const compressedValue = bytesToNumberBE(bytes);
|
||||||
@@ -1025,11 +1060,11 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|||||||
let y = Fp.sqrt(right);
|
let y = Fp.sqrt(right);
|
||||||
if (!y) throw new Error('Invalid compressed G1 point');
|
if (!y) throw new Error('Invalid compressed G1 point');
|
||||||
const aflag = bitGet(compressedValue, C_BIT_POS);
|
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) };
|
return { x: Fp.create(x), y: Fp.create(y) };
|
||||||
} else if (bytes.length === 96) {
|
} else if (bytes.length === 96) {
|
||||||
// Check if the infinity flag is set
|
// 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 x = bytesToNumberBE(bytes.slice(0, Fp.BYTES));
|
||||||
const y = bytesToNumberBE(bytes.slice(Fp.BYTES));
|
const y = bytesToNumberBE(bytes.slice(Fp.BYTES));
|
||||||
return { x: Fp.create(x), y: Fp.create(y) };
|
return { x: Fp.create(x), y: Fp.create(y) };
|
||||||
@@ -1039,7 +1074,7 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|||||||
},
|
},
|
||||||
toBytes: (c, point, isCompressed) => {
|
toBytes: (c, point, isCompressed) => {
|
||||||
const isZero = point.equals(c.ZERO);
|
const isZero = point.equals(c.ZERO);
|
||||||
const { x, y } = point;
|
const { x, y } = point.toAffine();
|
||||||
if (isCompressed) {
|
if (isCompressed) {
|
||||||
if (isZero) return COMPRESSED_ZERO.slice();
|
if (isZero) return COMPRESSED_ZERO.slice();
|
||||||
const P = Fp.ORDER;
|
const P = Fp.ORDER;
|
||||||
@@ -1050,10 +1085,10 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|||||||
} else {
|
} else {
|
||||||
if (isZero) {
|
if (isZero) {
|
||||||
// 2x PUBLIC_KEY_LENGTH
|
// 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;
|
return x;
|
||||||
} else {
|
} else {
|
||||||
return concatBytes(numberToBytesBE(x, Fp.BYTES), numberToBytesBE(y, Fp.BYTES));
|
return concatB(numberToBytesBE(x, Fp.BYTES), numberToBytesBE(y, Fp.BYTES));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -1115,7 +1150,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
|
const Q = t3.subtract(P); // Ψ²(2P) - Ψ(P) + [x²]P - [x]Ψ(P) + [x]P - 1P
|
||||||
return Q; // [x²-x-1]P + [x-1]Ψ(P) + Ψ²(2P)
|
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;
|
const m_byte = bytes[0] & 0xe0;
|
||||||
if (m_byte === 0x20 || m_byte === 0x60 || m_byte === 0xe0) {
|
if (m_byte === 0x20 || m_byte === 0x60 || m_byte === 0xe0) {
|
||||||
throw new Error('Invalid encoding flag: ' + m_byte);
|
throw new Error('Invalid encoding flag: ' + m_byte);
|
||||||
@@ -1123,6 +1158,8 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|||||||
const bitC = m_byte & 0x80; // compression bit
|
const bitC = m_byte & 0x80; // compression bit
|
||||||
const bitI = m_byte & 0x40; // point at infinity bit
|
const bitI = m_byte & 0x40; // point at infinity bit
|
||||||
const bitS = m_byte & 0x20; // sign 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) {
|
if (bytes.length === 96 && bitC) {
|
||||||
const { b } = bls12_381.CURVE.G2;
|
const { b } = bls12_381.CURVE.G2;
|
||||||
const P = Fp.ORDER;
|
const P = Fp.ORDER;
|
||||||
@@ -1135,23 +1172,23 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|||||||
}
|
}
|
||||||
return { x: Fp2.ZERO, y: Fp2.ZERO };
|
return { x: Fp2.ZERO, y: Fp2.ZERO };
|
||||||
}
|
}
|
||||||
const x_1 = bytesToNumberBE(bytes.slice(0, Fp.BYTES));
|
const x_1 = slc(bytes, 0, L);
|
||||||
const x_0 = bytesToNumberBE(bytes.slice(Fp.BYTES));
|
const x_0 = slc(bytes, L, 2 * L);
|
||||||
const x = Fp2.create({ c0: Fp.create(x_0), c1: Fp.create(x_1) });
|
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
|
const right = Fp2.add(Fp2.pow(x, 3n), b); // y² = x³ + 4 * (u+1) = x³ + b
|
||||||
let y = Fp2.sqrt(right);
|
let y = Fp2.sqrt(right);
|
||||||
const Y_bit = y.c1 === 0n ? (y.c0 * 2n) / P : (y.c1 * 2n) / P ? 1n : 0n;
|
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 };
|
return { x, y };
|
||||||
} else if (bytes.length === 192 && !bitC) {
|
} else if (bytes.length === 192 && !bitC) {
|
||||||
// Check if the infinity flag is set
|
// Check if the infinity flag is set
|
||||||
if ((bytes[0] & (1 << 6)) !== 0) {
|
if ((bytes[0] & (1 << 6)) !== 0) {
|
||||||
return { x: Fp2.ZERO, y: Fp2.ZERO };
|
return { x: Fp2.ZERO, y: Fp2.ZERO };
|
||||||
}
|
}
|
||||||
const x1 = bytesToNumberBE(bytes.slice(0, Fp.BYTES));
|
const x1 = slc(bytes, 0, L);
|
||||||
const x0 = bytesToNumberBE(bytes.slice(Fp.BYTES, 2 * Fp.BYTES));
|
const x0 = slc(bytes, L, 2 * L);
|
||||||
const y1 = bytesToNumberBE(bytes.slice(2 * Fp.BYTES, 3 * Fp.BYTES));
|
const y1 = slc(bytes, 2 * L, 3 * L);
|
||||||
const y0 = bytesToNumberBE(bytes.slice(3 * Fp.BYTES));
|
const y0 = slc(bytes, 3 * L, 4 * L);
|
||||||
return { x: Fp2.fromBigTuple([x0, x1]), y: Fp2.fromBigTuple([y0, y1]) };
|
return { x: Fp2.fromBigTuple([x0, x1]), y: Fp2.fromBigTuple([y0, y1]) };
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid point G2, expected 96/192 bytes');
|
throw new Error('Invalid point G2, expected 96/192 bytes');
|
||||||
@@ -1159,20 +1196,20 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|||||||
},
|
},
|
||||||
toBytes: (c, point, isCompressed) => {
|
toBytes: (c, point, isCompressed) => {
|
||||||
const isZero = point.equals(c.ZERO);
|
const isZero = point.equals(c.ZERO);
|
||||||
const { x, y } = point;
|
const { x, y } = point.toAffine();
|
||||||
if (isCompressed) {
|
if (isCompressed) {
|
||||||
const P = Fp.ORDER;
|
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);
|
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?)
|
// set compressed & sign bits (looks like different offsets than for G1/Fp?)
|
||||||
let x_1 = bitSet(x.c1, C_BIT_POS, flag);
|
let x_1 = bitSet(x.c1, C_BIT_POS, flag);
|
||||||
x_1 = bitSet(x_1, S_BIT_POS, true);
|
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 {
|
} 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: x0, im: x1 } = Fp2.reim(x);
|
||||||
const { re: y0, im: y1 } = Fp2.reim(y);
|
const { re: y0, im: y1 } = Fp2.reim(y);
|
||||||
return concatBytes(
|
return concatB(
|
||||||
numberToBytesBE(x1, Fp.BYTES),
|
numberToBytesBE(x1, Fp.BYTES),
|
||||||
numberToBytesBE(x0, Fp.BYTES),
|
numberToBytesBE(x0, Fp.BYTES),
|
||||||
numberToBytesBE(y1, Fp.BYTES),
|
numberToBytesBE(y1, Fp.BYTES),
|
||||||
@@ -1182,8 +1219,8 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|||||||
},
|
},
|
||||||
Signature: {
|
Signature: {
|
||||||
// TODO: Optimize, it's very slow because of sqrt.
|
// TODO: Optimize, it's very slow because of sqrt.
|
||||||
decode(hex: Hex): PointType<Fp2> {
|
decode(hex: Hex): ProjPointType<Fp2> {
|
||||||
hex = ensureBytes(hex);
|
hex = ensureBytes('signatureHex', hex);
|
||||||
const P = Fp.ORDER;
|
const P = Fp.ORDER;
|
||||||
const half = hex.length / 2;
|
const half = hex.length / 2;
|
||||||
if (half !== 48 && half !== 96)
|
if (half !== 48 && half !== 96)
|
||||||
@@ -1192,7 +1229,7 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|||||||
const z2 = bytesToNumberBE(hex.slice(half));
|
const z2 = bytesToNumberBE(hex.slice(half));
|
||||||
// Indicates the infinity point
|
// Indicates the infinity point
|
||||||
const bflag1 = bitGet(z1, I_BIT_POS);
|
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 x1 = Fp.create(z1 & Fp.MASK);
|
||||||
const x2 = Fp.create(z2);
|
const x2 = Fp.create(z2);
|
||||||
@@ -1208,23 +1245,24 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|||||||
const aflag1 = bitGet(z1, 381);
|
const aflag1 = bitGet(z1, 381);
|
||||||
const isGreater = y1 > 0n && (y1 * 2n) / P !== aflag1;
|
const isGreater = y1 > 0n && (y1 * 2n) / P !== aflag1;
|
||||||
const isZero = y1 === 0n && (y0 * 2n) / P !== aflag1;
|
const isZero = y1 === 0n && (y0 * 2n) / P !== aflag1;
|
||||||
if (isGreater || isZero) y = Fp2.negate(y);
|
if (isGreater || isZero) y = Fp2.neg(y);
|
||||||
const point = new bls12_381.G2.Point(x, y);
|
const point = bls12_381.G2.ProjectivePoint.fromAffine({ x, y });
|
||||||
point.assertValidity();
|
point.assertValidity();
|
||||||
return point;
|
return point;
|
||||||
},
|
},
|
||||||
encode(point: PointType<Fp2>) {
|
encode(point: ProjPointType<Fp2>) {
|
||||||
// NOTE: by some reasons it was missed in bls12-381, looks like bug
|
// NOTE: by some reasons it was missed in bls12-381, looks like bug
|
||||||
point.assertValidity();
|
point.assertValidity();
|
||||||
if (point.equals(bls12_381.G2.Point.ZERO))
|
if (point.equals(bls12_381.G2.ProjectivePoint.ZERO))
|
||||||
return concatBytes(COMPRESSED_ZERO, numberToBytesBE(0n, Fp.BYTES));
|
return concatB(COMPRESSED_ZERO, numberToBytesBE(0n, Fp.BYTES));
|
||||||
const { re: x0, im: x1 } = Fp2.reim(point.x);
|
const a = point.toAffine();
|
||||||
const { re: y0, im: y1 } = Fp2.reim(point.y);
|
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 tmp = y1 > 0n ? y1 * 2n : y0 * 2n;
|
||||||
const aflag1 = Boolean((tmp / Fp.ORDER) & 1n);
|
const aflag1 = Boolean((tmp / Fp.ORDER) & 1n);
|
||||||
const z1 = bitSet(bitSet(x1, 381, aflag1), S_BIT_POS, true);
|
const z1 = bitSet(bitSet(x1, 381, aflag1), S_BIT_POS, true);
|
||||||
const z2 = x0;
|
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) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
import { weierstrass } from './abstract/weierstrass.js';
|
|
||||||
import { sha256 } from '@noble/hashes/sha256';
|
import { sha256 } from '@noble/hashes/sha256';
|
||||||
|
import { weierstrass } from './abstract/weierstrass.js';
|
||||||
import { getHash } from './_shortw_utils.js';
|
import { getHash } from './_shortw_utils.js';
|
||||||
import { Fp } from './abstract/modular.js';
|
import { Fp } from './abstract/modular.js';
|
||||||
/**
|
/**
|
||||||
|
|||||||
146
src/ed25519.ts
146
src/ed25519.ts
@@ -1,17 +1,18 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
import { sha512 } from '@noble/hashes/sha512';
|
import { sha512 } from '@noble/hashes/sha512';
|
||||||
import { concatBytes, randomBytes, utf8ToBytes } from '@noble/hashes/utils';
|
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 { 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 {
|
import {
|
||||||
ensureBytes,
|
|
||||||
equalBytes,
|
equalBytes,
|
||||||
bytesToHex,
|
bytesToHex,
|
||||||
bytesToNumberLE,
|
bytesToNumberLE,
|
||||||
numberToBytesLE,
|
numberToBytesLE,
|
||||||
Hex,
|
Hex,
|
||||||
|
ensureBytes,
|
||||||
} from './abstract/utils.js';
|
} from './abstract/utils.js';
|
||||||
|
import * as htf from './abstract/hash-to-curve.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ed25519 Twisted Edwards curve with following addons:
|
* ed25519 Twisted Edwards curve with following addons:
|
||||||
@@ -91,7 +92,7 @@ export const ED25519_TORSION_SUBGROUP = [
|
|||||||
'c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa',
|
'c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa',
|
||||||
];
|
];
|
||||||
|
|
||||||
const Fp = Field(ED25519_P);
|
const Fp = Field(ED25519_P, undefined, true);
|
||||||
|
|
||||||
const ED25519_DEF = {
|
const ED25519_DEF = {
|
||||||
// Param: a
|
// 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.
|
// Ratio of u to v. Allows us to combine inversion and square root. Uses algo from RFC8032 5.1.3.
|
||||||
// Constant-time, u/√v
|
// Constant-time, u/√v
|
||||||
uvRatio,
|
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;
|
} as const;
|
||||||
|
|
||||||
export const ed25519 = twistedEdwards(ED25519_DEF);
|
export const ed25519 = twistedEdwards(ED25519_DEF);
|
||||||
@@ -150,10 +138,10 @@ export const ed25519ph = twistedEdwards({
|
|||||||
|
|
||||||
export const x25519 = montgomery({
|
export const x25519 = montgomery({
|
||||||
P: ED25519_P,
|
P: ED25519_P,
|
||||||
a24: BigInt('121665'),
|
a: BigInt(486662),
|
||||||
montgomeryBits: 255, // n is 253 bits
|
montgomeryBits: 255, // n is 253 bits
|
||||||
nByteLength: 32,
|
nByteLength: 32,
|
||||||
Gu: '0900000000000000000000000000000000000000000000000000000000000000',
|
Gu: BigInt(9),
|
||||||
powPminus2: (x: bigint): bigint => {
|
powPminus2: (x: bigint): bigint => {
|
||||||
const P = ED25519_P;
|
const P = ED25519_P;
|
||||||
// x^(p-2) aka x^(2^255-21)
|
// x^(p-2) aka x^(2^255-21)
|
||||||
@@ -161,10 +149,98 @@ export const x25519 = montgomery({
|
|||||||
return mod(pow2(pow_p_5_8, BigInt(3), P) * b2, P);
|
return mod(pow2(pow_p_5_8, BigInt(3), P) * b2, P);
|
||||||
},
|
},
|
||||||
adjustScalarBytes,
|
adjustScalarBytes,
|
||||||
|
randomBytes,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 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.createHasher(
|
||||||
|
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) {
|
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)
|
// √(-1) aka √(a) aka 2^((p-1)/4)
|
||||||
const SQRT_M1 = BigInt(
|
const SQRT_M1 = BigInt(
|
||||||
@@ -191,16 +267,16 @@ const invertSqrt = (number: bigint) => uvRatio(_1n, number);
|
|||||||
|
|
||||||
const MAX_255B = BigInt('0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
|
const MAX_255B = BigInt('0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
|
||||||
const bytes255ToNumberLE = (bytes: Uint8Array) =>
|
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
|
// Computes Elligator map for Ristretto
|
||||||
// https://ristretto.group/formulas/elligator.html
|
// https://ristretto.group/formulas/elligator.html
|
||||||
function calcElligatorRistrettoMap(r0: bigint): ExtendedPoint {
|
function calcElligatorRistrettoMap(r0: bigint): ExtendedPoint {
|
||||||
const { d } = ed25519.CURVE;
|
const { d } = ed25519.CURVE;
|
||||||
const P = ed25519.CURVE.Fp.ORDER;
|
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 r = mod(SQRT_M1 * r0 * r0); // 1
|
||||||
const Ns = mod((r + _1n) * ONE_MINUS_D_SQ); // 2
|
const Ns = mod((r + _1n) * ONE_MINUS_D_SQ); // 2
|
||||||
let c = BigInt(-1); // 3
|
let c = BigInt(-1); // 3
|
||||||
@@ -241,7 +317,7 @@ export class RistrettoPoint {
|
|||||||
* @param hex 64-bit output of a hash function
|
* @param hex 64-bit output of a hash function
|
||||||
*/
|
*/
|
||||||
static hashToCurve(hex: Hex): RistrettoPoint {
|
static hashToCurve(hex: Hex): RistrettoPoint {
|
||||||
hex = ensureBytes(hex, 64);
|
hex = ensureBytes('ristrettoHash', hex, 64);
|
||||||
const r1 = bytes255ToNumberLE(hex.slice(0, 32));
|
const r1 = bytes255ToNumberLE(hex.slice(0, 32));
|
||||||
const R1 = calcElligatorRistrettoMap(r1);
|
const R1 = calcElligatorRistrettoMap(r1);
|
||||||
const r2 = bytes255ToNumberLE(hex.slice(32, 64));
|
const r2 = bytes255ToNumberLE(hex.slice(32, 64));
|
||||||
@@ -255,10 +331,10 @@ export class RistrettoPoint {
|
|||||||
* @param hex Ristretto-encoded 32 bytes. Not every 32-byte string is valid ristretto encoding
|
* @param hex Ristretto-encoded 32 bytes. Not every 32-byte string is valid ristretto encoding
|
||||||
*/
|
*/
|
||||||
static fromHex(hex: Hex): RistrettoPoint {
|
static fromHex(hex: Hex): RistrettoPoint {
|
||||||
hex = ensureBytes(hex, 32);
|
hex = ensureBytes('ristrettoHex', hex, 32);
|
||||||
const { a, d } = ed25519.CURVE;
|
const { a, d } = ed25519.CURVE;
|
||||||
const P = ed25519.CURVE.Fp.ORDER;
|
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 emsg = 'RistrettoPoint.fromHex: the hex is not valid encoding of RistrettoPoint';
|
||||||
const s = bytes255ToNumberLE(hex);
|
const s = bytes255ToNumberLE(hex);
|
||||||
// 1. Check that s_bytes is the canonical encoding of a field element, or else abort.
|
// 1. Check that s_bytes is the canonical encoding of a field element, or else abort.
|
||||||
@@ -286,9 +362,9 @@ export class RistrettoPoint {
|
|||||||
* https://ristretto.group/formulas/encoding.html
|
* https://ristretto.group/formulas/encoding.html
|
||||||
*/
|
*/
|
||||||
toRawBytes(): Uint8Array {
|
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 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 u1 = mod(mod(z + y) * mod(z - y)); // 1
|
||||||
const u2 = mod(x * y); // 2
|
const u2 = mod(x * y); // 2
|
||||||
// Square root always exists
|
// Square root always exists
|
||||||
@@ -324,12 +400,12 @@ export class RistrettoPoint {
|
|||||||
// Compare one point to another.
|
// Compare one point to another.
|
||||||
equals(other: RistrettoPoint): boolean {
|
equals(other: RistrettoPoint): boolean {
|
||||||
assertRstPoint(other);
|
assertRstPoint(other);
|
||||||
const a = this.ep;
|
const { ex: X1, ey: Y1 } = this.ep;
|
||||||
const b = other.ep;
|
const { ex: X2, ey: Y2 } = this.ep;
|
||||||
const { mod } = ed25519.utils;
|
const mod = ed25519.CURVE.Fp.create;
|
||||||
// (x1 * y2 == y1 * x2) | (y1 * y2 == x1 * x2)
|
// (x1 * y2 == y1 * x2) | (y1 * y2 == x1 * x2)
|
||||||
const one = mod(a.x * b.y) === mod(a.y * b.x);
|
const one = mod(X1 * Y2) === mod(Y1 * X2);
|
||||||
const two = mod(a.y * b.y) === mod(a.x * b.x);
|
const two = mod(Y1 * Y2) === mod(X1 * X2);
|
||||||
return one || two;
|
return one || two;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -343,11 +419,11 @@ export class RistrettoPoint {
|
|||||||
return new RistrettoPoint(this.ep.subtract(other.ep));
|
return new RistrettoPoint(this.ep.subtract(other.ep));
|
||||||
}
|
}
|
||||||
|
|
||||||
multiply(scalar: number | bigint): RistrettoPoint {
|
multiply(scalar: bigint): RistrettoPoint {
|
||||||
return new RistrettoPoint(this.ep.multiply(scalar));
|
return new RistrettoPoint(this.ep.multiply(scalar));
|
||||||
}
|
}
|
||||||
|
|
||||||
multiplyUnsafe(scalar: number | bigint): RistrettoPoint {
|
multiplyUnsafe(scalar: bigint): RistrettoPoint {
|
||||||
return new RistrettoPoint(this.ep.multiplyUnsafe(scalar));
|
return new RistrettoPoint(this.ep.multiplyUnsafe(scalar));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
105
src/ed448.ts
105
src/ed448.ts
@@ -2,8 +2,9 @@
|
|||||||
import { shake256 } from '@noble/hashes/sha3';
|
import { shake256 } from '@noble/hashes/sha3';
|
||||||
import { concatBytes, randomBytes, utf8ToBytes, wrapConstructor } from '@noble/hashes/utils';
|
import { concatBytes, randomBytes, utf8ToBytes, wrapConstructor } from '@noble/hashes/utils';
|
||||||
import { twistedEdwards } from './abstract/edwards.js';
|
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 { montgomery } from './abstract/montgomery.js';
|
||||||
|
import * as htf from './abstract/hash-to-curve.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Edwards448 (not Ed448-Goldilocks) curve with following addons:
|
* Edwards448 (not Ed448-Goldilocks) curve with following addons:
|
||||||
@@ -52,6 +53,8 @@ function adjustScalarBytes(bytes: Uint8Array): Uint8Array {
|
|||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Fp = Field(ed448P, 456, true);
|
||||||
|
|
||||||
const ED448_DEF = {
|
const ED448_DEF = {
|
||||||
// Param: a
|
// Param: a
|
||||||
a: BigInt(1),
|
a: BigInt(1),
|
||||||
@@ -60,8 +63,9 @@ const ED448_DEF = {
|
|||||||
'726838724295606890549323807888004534353641360687318060281490199180612328166730772686396383698676545930088884461843637361053498018326358'
|
'726838724295606890549323807888004534353641360687318060281490199180612328166730772686396383698676545930088884461843637361053498018326358'
|
||||||
),
|
),
|
||||||
// Finite field 𝔽p over which we'll do calculations; 2n ** 448n - 2n ** 224n - 1n
|
// Finite field 𝔽p over which we'll do calculations; 2n ** 448n - 2n ** 224n - 1n
|
||||||
Fp: Fp(ed448P, 456),
|
Fp,
|
||||||
// Subgroup order: how many points ed448 has; 2n**446n - 13818066809895115352007386748515426880336692474882178609894547503885n
|
// Subgroup order: how many points curve has;
|
||||||
|
// 2n**446n - 13818066809895115352007386748515426880336692474882178609894547503885n
|
||||||
n: BigInt(
|
n: BigInt(
|
||||||
'181709681073901722637330951972001133588410340171829515070372549795146003961539585716195755291692375963310293709091662304773755859649779'
|
'181709681073901722637330951972001133588410340171829515070372549795146003961539585716195755291692375963310293709091662304773755859649779'
|
||||||
),
|
),
|
||||||
@@ -118,11 +122,11 @@ export const ed448 = twistedEdwards(ED448_DEF);
|
|||||||
export const ed448ph = twistedEdwards({ ...ED448_DEF, preHash: shake256_64 });
|
export const ed448ph = twistedEdwards({ ...ED448_DEF, preHash: shake256_64 });
|
||||||
|
|
||||||
export const x448 = montgomery({
|
export const x448 = montgomery({
|
||||||
a24: BigInt(39081),
|
a: BigInt(156326),
|
||||||
montgomeryBits: 448,
|
montgomeryBits: 448,
|
||||||
nByteLength: 57,
|
nByteLength: 57,
|
||||||
P: ed448P,
|
P: ed448P,
|
||||||
Gu: '0500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
|
Gu: BigInt(5),
|
||||||
powPminus2: (x: bigint): bigint => {
|
powPminus2: (x: bigint): bigint => {
|
||||||
const P = ed448P;
|
const P = ed448P;
|
||||||
const Pminus3div4 = ed448_pow_Pminus3div4(x);
|
const Pminus3div4 = ed448_pow_Pminus3div4(x);
|
||||||
@@ -130,6 +134,7 @@ export const x448 = montgomery({
|
|||||||
return mod(Pminus3 * x, P); // Pminus3 * x = Pminus2
|
return mod(Pminus3 * x, P); // Pminus3 * x = Pminus2
|
||||||
},
|
},
|
||||||
adjustScalarBytes,
|
adjustScalarBytes,
|
||||||
|
randomBytes,
|
||||||
// The 4-isogeny maps between the Montgomery curve and this Edwards
|
// The 4-isogeny maps between the Montgomery curve and this Edwards
|
||||||
// curve are:
|
// curve are:
|
||||||
// (u, v) = (y^2/x^2, (2 - x^2 - y^2)*y/x^3)
|
// (u, v) = (y^2/x^2, (2 - x^2 - y^2)*y/x^3)
|
||||||
@@ -145,3 +150,93 @@ export const x448 = montgomery({
|
|||||||
// return numberToBytesLE(u, 56);
|
// 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.createHasher(
|
||||||
|
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) */
|
/*! 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 { concatBytes, randomBytes, utf8ToBytes } from '@noble/hashes/utils';
|
||||||
import { twistedEdwards } from './abstract/edwards.js';
|
import { twistedEdwards } from './abstract/edwards.js';
|
||||||
import { blake2s } from '@noble/hashes/blake2s';
|
import { blake2s } from '@noble/hashes/blake2s';
|
||||||
@@ -8,6 +8,7 @@ import { Fp } from './abstract/modular.js';
|
|||||||
/**
|
/**
|
||||||
* jubjub Twisted Edwards curve.
|
* jubjub Twisted Edwards curve.
|
||||||
* https://neuromancer.sk/std/other/JubJub
|
* 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({
|
export const jubjub = twistedEdwards({
|
||||||
@@ -15,16 +16,16 @@ export const jubjub = twistedEdwards({
|
|||||||
a: BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000'),
|
a: BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000'),
|
||||||
d: BigInt('0x2a9318e74bfa2b48f5fd9207e6bd7fd4292d7f6d37579d2601065fd6d6343eb1'),
|
d: BigInt('0x2a9318e74bfa2b48f5fd9207e6bd7fd4292d7f6d37579d2601065fd6d6343eb1'),
|
||||||
// Finite field 𝔽p over which we'll do calculations
|
// Finite field 𝔽p over which we'll do calculations
|
||||||
|
// Same value as bls12-381 Fr (not Fp)
|
||||||
Fp: Fp(BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001')),
|
Fp: Fp(BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001')),
|
||||||
// Subgroup order: how many points ed25519 has
|
// Subgroup order: how many points curve has
|
||||||
// 2n ** 252n + 27742317777372353535851937790883648493n;
|
|
||||||
n: BigInt('0xe7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7'),
|
n: BigInt('0xe7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7'),
|
||||||
// Cofactor
|
// Cofactor
|
||||||
h: BigInt(8),
|
h: BigInt(8),
|
||||||
// Base point (x, y) aka generator point
|
// Base point (x, y) aka generator point
|
||||||
Gx: BigInt('0x11dafe5d23e1218086a365b99fbf3d3be72f6afd7d1f72623e6b071492d1122b'),
|
Gx: BigInt('0x11dafe5d23e1218086a365b99fbf3d3be72f6afd7d1f72623e6b071492d1122b'),
|
||||||
Gy: BigInt('0x1d523cf1ddab1a1793132e78c866c0c33e26ba5cc220fed7cc3f870e59d292aa'),
|
Gy: BigInt('0x1d523cf1ddab1a1793132e78c866c0c33e26ba5cc220fed7cc3f870e59d292aa'),
|
||||||
hash: sha256,
|
hash: sha512,
|
||||||
randomBytes,
|
randomBytes,
|
||||||
} as const);
|
} as const);
|
||||||
|
|
||||||
@@ -38,7 +39,7 @@ export function groupHash(tag: Uint8Array, personalization: Uint8Array) {
|
|||||||
h.update(GH_FIRST_BLOCK);
|
h.update(GH_FIRST_BLOCK);
|
||||||
h.update(tag);
|
h.update(tag);
|
||||||
// NOTE: returns ExtendedPoint, in case it will be multiplied later
|
// 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
|
// NOTE: cannot replace with isSmallOrder, returns Point*8
|
||||||
p = p.multiply(jubjub.CURVE.h);
|
p = p.multiply(jubjub.CURVE.h);
|
||||||
if (p.equals(jubjub.ExtendedPoint.ZERO)) throw new Error('Point has small order');
|
if (p.equals(jubjub.ExtendedPoint.ZERO)) throw new Error('Point has small order');
|
||||||
|
|||||||
25
src/p192.ts
25
src/p192.ts
@@ -1,25 +0,0 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
||||||
import { createCurve } from './_shortw_utils.js';
|
|
||||||
import { sha256 } from '@noble/hashes/sha256';
|
|
||||||
import { Fp } from './abstract/modular.js';
|
|
||||||
|
|
||||||
// NIST secp192r1 aka P192
|
|
||||||
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/secg/secp192r1
|
|
||||||
export const P192 = createCurve(
|
|
||||||
{
|
|
||||||
// Params: a, b
|
|
||||||
a: BigInt('0xfffffffffffffffffffffffffffffffefffffffffffffffc'),
|
|
||||||
b: BigInt('0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1'),
|
|
||||||
// Field over which we'll do calculations; 2n ** 192n - 2n ** 64n - 1n
|
|
||||||
Fp: Fp(BigInt('0xfffffffffffffffffffffffffffffffeffffffffffffffff')),
|
|
||||||
// Curve order, total count of valid points in the field.
|
|
||||||
n: BigInt('0xffffffffffffffffffffffff99def836146bc9b1b4d22831'),
|
|
||||||
// Base point (x, y) aka generator point
|
|
||||||
Gx: BigInt('0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012'),
|
|
||||||
Gy: BigInt('0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811'),
|
|
||||||
h: BigInt(1),
|
|
||||||
lowS: false,
|
|
||||||
} as const,
|
|
||||||
sha256
|
|
||||||
);
|
|
||||||
export const secp192r1 = P192;
|
|
||||||
25
src/p224.ts
25
src/p224.ts
@@ -1,25 +0,0 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
||||||
import { createCurve } from './_shortw_utils.js';
|
|
||||||
import { sha224 } from '@noble/hashes/sha256';
|
|
||||||
import { Fp } from './abstract/modular.js';
|
|
||||||
|
|
||||||
// NIST secp224r1 aka P224
|
|
||||||
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/nist/P-224
|
|
||||||
export const P224 = createCurve(
|
|
||||||
{
|
|
||||||
// Params: a, b
|
|
||||||
a: BigInt('0xfffffffffffffffffffffffffffffffefffffffffffffffffffffffe'),
|
|
||||||
b: BigInt('0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4'),
|
|
||||||
// Field over which we'll do calculations; 2n**224n - 2n**96n + 1n
|
|
||||||
Fp: Fp(BigInt('0xffffffffffffffffffffffffffffffff000000000000000000000001')),
|
|
||||||
// Curve order, total count of valid points in the field
|
|
||||||
n: BigInt('0xffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d'),
|
|
||||||
// Base point (x, y) aka generator point
|
|
||||||
Gx: BigInt('0xb70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21'),
|
|
||||||
Gy: BigInt('0xbd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34'),
|
|
||||||
h: BigInt(1),
|
|
||||||
lowS: false,
|
|
||||||
} as const,
|
|
||||||
sha224
|
|
||||||
);
|
|
||||||
export const secp224r1 = P224;
|
|
||||||
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 { sha256 } from '@noble/hashes/sha256';
|
||||||
import { Fp as Field } from './abstract/modular.js';
|
import { Fp as Field } from './abstract/modular.js';
|
||||||
import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
|
import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
|
||||||
|
import * as htf from './abstract/hash-to-curve.js';
|
||||||
|
|
||||||
// NIST secp256r1 aka P256
|
// NIST secp256r1 aka P256
|
||||||
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/nist/P-256
|
// 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'),
|
Gy: BigInt('0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5'),
|
||||||
h: BigInt(1),
|
h: BigInt(1),
|
||||||
lowS: false,
|
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,
|
} as const,
|
||||||
sha256
|
sha256
|
||||||
);
|
);
|
||||||
export const secp256r1 = P256;
|
export const secp256r1 = P256;
|
||||||
|
|
||||||
|
const { hashToCurve, encodeToCurve } = htf.createHasher(
|
||||||
|
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 { sha384 } from '@noble/hashes/sha512';
|
||||||
import { Fp as Field } from './abstract/modular.js';
|
import { Fp as Field } from './abstract/modular.js';
|
||||||
import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
|
import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
|
||||||
|
import * as htf from './abstract/hash-to-curve.js';
|
||||||
|
|
||||||
// NIST secp384r1 aka P384
|
// NIST secp384r1 aka P384
|
||||||
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/nist/P-384
|
// 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'),
|
Gy: BigInt('0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f'),
|
||||||
h: BigInt(1),
|
h: BigInt(1),
|
||||||
lowS: false,
|
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,
|
} as const,
|
||||||
sha384
|
sha384
|
||||||
);
|
);
|
||||||
export const secp384r1 = P384;
|
export const secp384r1 = P384;
|
||||||
|
|
||||||
|
const { hashToCurve, encodeToCurve } = htf.createHasher(
|
||||||
|
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) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
import { createCurve } from './_shortw_utils.js';
|
import { createCurve } from './_shortw_utils.js';
|
||||||
import { sha512 } from '@noble/hashes/sha512';
|
import { sha512 } from '@noble/hashes/sha512';
|
||||||
import { bytesToHex, PrivKey } from './abstract/utils.js';
|
|
||||||
import { Fp as Field } from './abstract/modular.js';
|
import { Fp as Field } from './abstract/modular.js';
|
||||||
import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
|
import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
|
||||||
|
import * as htf from './abstract/hash-to-curve.js';
|
||||||
|
|
||||||
// NIST secp521r1 aka P521
|
// NIST secp521r1 aka P521
|
||||||
// Note that it's 521, which differs from 512 of its hash function.
|
// Note that it's 521, which differs from 512 of its hash function.
|
||||||
@@ -37,25 +37,21 @@ export const P521 = createCurve({
|
|||||||
Gy: BigInt('0x011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650'),
|
Gy: BigInt('0x011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650'),
|
||||||
h: BigInt(1),
|
h: BigInt(1),
|
||||||
lowS: false,
|
lowS: false,
|
||||||
// P521 keys could be 130, 131, 132 bytes - which doesn't play nicely.
|
allowedPrivateKeyLengths: [130, 131, 132] // P521 keys are variable-length. Normalize to 132b
|
||||||
// We ensure all keys are 132 bytes.
|
} as const, sha512);
|
||||||
// Does not replace validation; invalid keys would still be rejected.
|
export const secp521r1 = P521;
|
||||||
normalizePrivateKey(key: PrivKey) {
|
|
||||||
if (typeof key === 'bigint') return key;
|
const { hashToCurve, encodeToCurve } = htf.createHasher(
|
||||||
if (key instanceof Uint8Array) key = bytesToHex(key);
|
secp521r1.ProjectivePoint,
|
||||||
if (typeof key !== 'string' || !([130, 131, 132].includes(key.length))) {
|
(scalars: bigint[]) => mapSWU(scalars[0]),
|
||||||
throw new Error('Invalid key');
|
{
|
||||||
}
|
|
||||||
return key.padStart(66 * 2, '0');
|
|
||||||
},
|
|
||||||
mapToCurve: (scalars: bigint[]) => mapSWU(scalars[0]),
|
|
||||||
htfDefaults: {
|
|
||||||
DST: 'P521_XMD:SHA-512_SSWU_RO_',
|
DST: 'P521_XMD:SHA-512_SSWU_RO_',
|
||||||
|
encodeDST: 'P521_XMD:SHA-512_SSWU_NU_',
|
||||||
p: Fp.ORDER,
|
p: Fp.ORDER,
|
||||||
m: 1,
|
m: 1,
|
||||||
k: 256,
|
k: 256,
|
||||||
expand: true,
|
expand: 'xmd',
|
||||||
hash: sha512,
|
hash: sha512,
|
||||||
},
|
}
|
||||||
} as const, sha512);
|
);
|
||||||
export const secp521r1 = P521;
|
export { hashToCurve, encodeToCurve };
|
||||||
|
|||||||
423
src/secp256k1.ts
423
src/secp256k1.ts
@@ -1,28 +1,12 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
import { sha256 } from '@noble/hashes/sha256';
|
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 {
|
|
||||||
ensureBytes,
|
|
||||||
concatBytes,
|
|
||||||
Hex,
|
|
||||||
hexToBytes,
|
|
||||||
bytesToNumberBE,
|
|
||||||
PrivKey,
|
|
||||||
} from './abstract/utils.js';
|
|
||||||
import { randomBytes } from '@noble/hashes/utils';
|
import { randomBytes } from '@noble/hashes/utils';
|
||||||
import { isogenyMap } from './abstract/hash-to-curve.js';
|
import { Fp as Field, mod, pow2 } from './abstract/modular.js';
|
||||||
|
import { ProjPointType as PointType, mapToCurveSimpleSWU } from './abstract/weierstrass.js';
|
||||||
/**
|
import type { Hex, PrivKey } from './abstract/utils.js';
|
||||||
* secp256k1 belongs to Koblitz curves: it has
|
import { bytesToNumberBE, concatBytes, ensureBytes, numberToBytesBE } from './abstract/utils.js';
|
||||||
* efficiently computable Frobenius endomorphism.
|
import * as htf from './abstract/hash-to-curve.js';
|
||||||
* Endomorphism improves efficiency:
|
import { createCurve } from './_shortw_utils.js';
|
||||||
* Uses 2x less RAM, speeds up precomputation by 2x and ECDH / sign 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
|
|
||||||
*/
|
|
||||||
|
|
||||||
const secp256k1P = BigInt('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f');
|
const secp256k1P = BigInt('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f');
|
||||||
const secp256k1N = BigInt('0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141');
|
const secp256k1N = BigInt('0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141');
|
||||||
@@ -31,10 +15,7 @@ const _2n = BigInt(2);
|
|||||||
const divNearest = (a: bigint, b: bigint) => (a + b / _2n) / b;
|
const divNearest = (a: bigint, b: bigint) => (a + b / _2n) / b;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows to compute square root √y 2x faster.
|
* √n = n^((p+1)/4) for fields p = 3 mod 4. We unwrap the loop and multiply bit-by-bit.
|
||||||
* To calculate √y, we need to exponentiate it to a very big number:
|
|
||||||
* `y² = x³ + ax + b; y = y² ^ (p+1)/4`
|
|
||||||
* We are unwrapping the loop and multiplying it bit-by-bit.
|
|
||||||
* (P+1n/4n).toString(2) would produce bits [223x 1, 0, 22x 1, 4x 0, 11, 00]
|
* (P+1n/4n).toString(2) would produce bits [223x 1, 0, 22x 1, 4x 0, 11, 00]
|
||||||
*/
|
*/
|
||||||
function sqrtMod(y: bigint): bigint {
|
function sqrtMod(y: bigint): bigint {
|
||||||
@@ -56,13 +37,185 @@ function sqrtMod(y: bigint): bigint {
|
|||||||
const b223 = (pow2(b220, _3n, P) * b3) % P;
|
const b223 = (pow2(b220, _3n, P) * b3) % P;
|
||||||
const t1 = (pow2(b223, _23n, P) * b22) % P;
|
const t1 = (pow2(b223, _23n, P) * b22) % P;
|
||||||
const t2 = (pow2(t1, _6n, P) * b2) % 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 });
|
const Fp = Field(secp256k1P, undefined, undefined, { sqrt: sqrtMod });
|
||||||
type Fp = bigint;
|
type Fp = bigint;
|
||||||
|
|
||||||
const isoMap = isogenyMap(
|
export const secp256k1 = createCurve(
|
||||||
|
{
|
||||||
|
a: BigInt(0), // equation params: a, b
|
||||||
|
b: BigInt(7), // Seem to be rigid: bitcointalk.org/index.php?topic=289795.msg3183975#msg3183975
|
||||||
|
Fp, // Field's prime: 2n**256n - 2n**32n - 2n**9n - 2n**8n - 2n**7n - 2n**6n - 2n**4n - 1n
|
||||||
|
n: secp256k1N, // Curve order, total count of valid points in the field
|
||||||
|
// Base point (x, y) aka generator point
|
||||||
|
Gx: BigInt('55066263022277343669578718895168534326250603453777594175500187360389116729240'),
|
||||||
|
Gy: BigInt('32670510020758816978083085130507043184471273380659243275938904335757337482424'),
|
||||||
|
h: BigInt(1), // Cofactor
|
||||||
|
lowS: true, // Allow only low-S signatures by default in sign() and verify()
|
||||||
|
/**
|
||||||
|
* 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%.
|
||||||
|
* For precomputed wNAF it trades off 1/2 init time & 1/3 ram for 20% perf hit.
|
||||||
|
* Explanation: https://gist.github.com/paulmillr/eb670806793e84df628a7c434a873066
|
||||||
|
*/
|
||||||
|
endo: {
|
||||||
|
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 BIP0340 code.
|
||||||
|
// 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;
|
||||||
|
/** 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ECDSA compact points are 33-byte. Schnorr is 32: we strip first byte 0x02 or 0x03
|
||||||
|
const pointToBytes = (point: PointType<bigint>) => point.toRawBytes(true).slice(1);
|
||||||
|
const numTo32b = (n: bigint) => numberToBytesBE(n, 32);
|
||||||
|
const modP = (x: bigint) => mod(x, secp256k1P);
|
||||||
|
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);
|
||||||
|
// Calculate point, scalar and bytes
|
||||||
|
function schnorrGetExtPubKey(priv: PrivKey) {
|
||||||
|
const d = secp256k1.utils.normPrivateKeyToScalar(priv); // same method executed in fromPrivateKey
|
||||||
|
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) };
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* lift_x from BIP340. Convert 32-byte x coordinate to elliptic curve point.
|
||||||
|
* @returns valid point checked for being on-curve
|
||||||
|
*/
|
||||||
|
function lift_x(x: bigint): PointType<bigint> {
|
||||||
|
if (!fe(x)) throw new Error('bad x: need 0 < x < p'); // Fail if x ≥ p.
|
||||||
|
const xx = modP(x * x);
|
||||||
|
const c = modP(xx * x + BigInt(7)); // Let c = x³ + 7 mod p.
|
||||||
|
let y = sqrtMod(c); // Let y = c^(p+1)/4 mod p.
|
||||||
|
if (y % 2n !== 0n) y = modP(-y); // 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;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Create tagged hash, convert it to bigint, reduce modulo-n.
|
||||||
|
*/
|
||||||
|
function challenge(...args: Uint8Array[]): bigint {
|
||||||
|
return modN(bytesToNumberBE(taggedHash('BIP0340/challenge', ...args)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schnorr public key is just `x` coordinate of Point as per 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 {
|
||||||
|
const m = ensureBytes('message', message);
|
||||||
|
const { bytes: px, scalar: d } = schnorrGetExtPubKey(privateKey); // checks for isWithinCurveOrder
|
||||||
|
const a = ensureBytes('auxRand', auxRand, 32); // Auxiliary random data a: a 32-byte array
|
||||||
|
const t = numTo32b(d ^ bytesToNumberBE(taggedHash('BIP0340/aux', a))); // Let t be the byte-wise xor of bytes(d) and hash/aux(a)
|
||||||
|
const rand = taggedHash('BIP0340/nonce', t, px, m); // Let rand = hash/nonce(t || bytes(P) || m)
|
||||||
|
const k_ = modN(bytesToNumberBE(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.
|
||||||
|
* Will swallow errors & return false except for initial type validation of arguments.
|
||||||
|
*/
|
||||||
|
function schnorrVerify(signature: Hex, message: Hex, publicKey: Hex): boolean {
|
||||||
|
const sig = ensureBytes('signature', signature, 64);
|
||||||
|
const m = ensureBytes('message', message);
|
||||||
|
const pub = ensureBytes('publicKey', publicKey, 32);
|
||||||
|
try {
|
||||||
|
const P = lift_x(bytesToNumberBE(pub)); // P = lift_x(int(pk)); fail if that fails
|
||||||
|
const r = bytesToNumberBE(sig.subarray(0, 32)); // Let r = int(sig[0:32]); fail if r ≥ p.
|
||||||
|
if (!fe(r)) return false;
|
||||||
|
const s = bytesToNumberBE(sig.subarray(32, 64)); // Let s = int(sig[32:64]); fail if s ≥ n.
|
||||||
|
if (!ge(s)) return false;
|
||||||
|
const e = challenge(numTo32b(r), pointToBytes(P), m); // int(challenge(bytes(r)||bytes(P)||m))%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: {
|
||||||
|
randomPrivateKey: secp256k1.utils.randomPrivateKey,
|
||||||
|
getExtendedPublicKey: schnorrGetExtPubKey,
|
||||||
|
lift_x,
|
||||||
|
pointToBytes,
|
||||||
|
numberToBytesBE,
|
||||||
|
bytesToNumberBE,
|
||||||
|
taggedHash,
|
||||||
|
mod,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const isoMap = htf.isogenyMap(
|
||||||
Fp,
|
Fp,
|
||||||
[
|
[
|
||||||
// xNum
|
// xNum
|
||||||
@@ -94,224 +247,24 @@ const isoMap = isogenyMap(
|
|||||||
],
|
],
|
||||||
].map((i) => i.map((j) => BigInt(j))) as [Fp[], Fp[], Fp[], Fp[]]
|
].map((i) => i.map((j) => BigInt(j))) as [Fp[], Fp[], Fp[], Fp[]]
|
||||||
);
|
);
|
||||||
|
|
||||||
const mapSWU = mapToCurveSimpleSWU(Fp, {
|
const mapSWU = mapToCurveSimpleSWU(Fp, {
|
||||||
A: BigInt('0x3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533'),
|
A: BigInt('0x3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533'),
|
||||||
B: BigInt('1771'),
|
B: BigInt('1771'),
|
||||||
Z: Fp.create(BigInt('-11')),
|
Z: Fp.create(BigInt('-11')),
|
||||||
});
|
});
|
||||||
|
export const { hashToCurve, encodeToCurve } = htf.createHasher(
|
||||||
export const secp256k1 = createCurve(
|
secp256k1.ProjectivePoint,
|
||||||
{
|
(scalars: bigint[]) => {
|
||||||
// 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]));
|
const { x, y } = mapSWU(Fp.create(scalars[0]));
|
||||||
return isoMap(x, y);
|
return isoMap(x, y);
|
||||||
},
|
},
|
||||||
htfDefaults: {
|
{
|
||||||
DST: 'secp256k1_XMD:SHA-256_SSWU_RO_',
|
DST: 'secp256k1_XMD:SHA-256_SSWU_RO_',
|
||||||
|
encodeDST: 'secp256k1_XMD:SHA-256_SSWU_NU_',
|
||||||
p: Fp.ORDER,
|
p: Fp.ORDER,
|
||||||
m: 1,
|
m: 1,
|
||||||
k: 128,
|
k: 128,
|
||||||
expand: true,
|
expand: 'xmd',
|
||||||
hash: sha256,
|
hash: sha256,
|
||||||
},
|
}
|
||||||
},
|
|
||||||
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,
|
|
||||||
};
|
|
||||||
|
|||||||
344
src/stark.ts
344
src/stark.ts
@@ -1,157 +1,126 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
import { keccak_256 } from '@noble/hashes/sha3';
|
import { keccak_256 } from '@noble/hashes/sha3';
|
||||||
import { sha256 } from '@noble/hashes/sha256';
|
import { sha256 } from '@noble/hashes/sha256';
|
||||||
import { weierstrass, ProjectivePointType } from './abstract/weierstrass.js';
|
import { utf8ToBytes } from '@noble/hashes/utils';
|
||||||
import * as cutils from './abstract/utils.js';
|
import { Fp, mod, Field, validateField } from './abstract/modular.js';
|
||||||
import { Fp } from './abstract/modular.js';
|
import { poseidon } from './abstract/poseidon.js';
|
||||||
|
import { weierstrass, ProjPointType, SignatureType } from './abstract/weierstrass.js';
|
||||||
|
import {
|
||||||
|
Hex,
|
||||||
|
bitMask,
|
||||||
|
bytesToHex,
|
||||||
|
bytesToNumberBE,
|
||||||
|
concatBytes,
|
||||||
|
ensureBytes as ensureBytesOrig,
|
||||||
|
hexToBytes,
|
||||||
|
hexToNumber,
|
||||||
|
numberToVarBytesBE,
|
||||||
|
} from './abstract/utils.js';
|
||||||
import { getHash } from './_shortw_utils.js';
|
import { getHash } from './_shortw_utils.js';
|
||||||
|
|
||||||
type ProjectivePoint = ProjectivePointType<bigint>;
|
|
||||||
// Stark-friendly elliptic curve
|
// Stark-friendly elliptic curve
|
||||||
// https://docs.starkware.co/starkex/stark-curve.html
|
// https://docs.starkware.co/starkex/stark-curve.html
|
||||||
|
|
||||||
const CURVE_N = BigInt(
|
type ProjectivePoint = ProjPointType<bigint>;
|
||||||
|
const CURVE_ORDER = BigInt(
|
||||||
'3618502788666131213697322783095070105526743751716087489154079457884512865583'
|
'3618502788666131213697322783095070105526743751716087489154079457884512865583'
|
||||||
);
|
);
|
||||||
const nBitLength = 252;
|
const nBitLength = 252;
|
||||||
export const starkCurve = weierstrass({
|
function bits2int(bytes: Uint8Array): bigint {
|
||||||
// Params: a, b
|
while (bytes[0] === 0) bytes = bytes.subarray(1); // strip leading 0s
|
||||||
a: BigInt(1),
|
// Copy-pasted from weierstrass.ts
|
||||||
|
const delta = bytes.length * 8 - nBitLength;
|
||||||
|
const num = bytesToNumberBE(bytes);
|
||||||
|
return delta > 0 ? num >> BigInt(delta) : num;
|
||||||
|
}
|
||||||
|
function hex0xToBytes(hex: string): Uint8Array {
|
||||||
|
if (typeof hex === 'string') {
|
||||||
|
hex = strip0x(hex); // allow 0x prefix
|
||||||
|
if (hex.length & 1) hex = '0' + hex; // allow unpadded hex
|
||||||
|
}
|
||||||
|
return hexToBytes(hex);
|
||||||
|
}
|
||||||
|
const curve = weierstrass({
|
||||||
|
a: BigInt(1), // Params: a, b
|
||||||
b: BigInt('3141592653589793238462643383279502884197169399375105820974944592307816406665'),
|
b: BigInt('3141592653589793238462643383279502884197169399375105820974944592307816406665'),
|
||||||
// Field over which we'll do calculations; 2n**251n + 17n * 2n**192n + 1n
|
// Field over which we'll do calculations; 2n**251n + 17n * 2n**192n + 1n
|
||||||
// There is no efficient sqrt for field (P%4==1)
|
// There is no efficient sqrt for field (P%4==1)
|
||||||
Fp: Fp(BigInt('0x800000000000011000000000000000000000000000000000000000000000001')),
|
Fp: Fp(BigInt('0x800000000000011000000000000000000000000000000000000000000000001')),
|
||||||
// Curve order, total count of valid points in the field.
|
n: CURVE_ORDER, // Curve order, total count of valid points in the field.
|
||||||
n: CURVE_N,
|
nBitLength, // len(bin(N).replace('0b',''))
|
||||||
nBitLength: nBitLength, // len(bin(N).replace('0b',''))
|
|
||||||
// Base point (x, y) aka generator point
|
// Base point (x, y) aka generator point
|
||||||
Gx: BigInt('874739451078007766457464989774322083649278607533249481151382481072868806602'),
|
Gx: BigInt('874739451078007766457464989774322083649278607533249481151382481072868806602'),
|
||||||
Gy: BigInt('152666792071518830868575557812948353041420400780739481342941381225525861407'),
|
Gy: BigInt('152666792071518830868575557812948353041420400780739481342941381225525861407'),
|
||||||
h: BigInt(1),
|
h: BigInt(1), // cofactor
|
||||||
// Default options
|
lowS: false, // Allow high-s signatures
|
||||||
lowS: false,
|
|
||||||
...getHash(sha256),
|
...getHash(sha256),
|
||||||
truncateHash: (hash: Uint8Array, truncateOnly = false): bigint => {
|
// Custom truncation routines for stark curve
|
||||||
// TODO: cleanup, ugly code
|
bits2int,
|
||||||
// Fix truncation
|
bits2int_modN: (bytes: Uint8Array): bigint => {
|
||||||
if (!truncateOnly) {
|
// 2102820b232636d200cb21f1d330f20d096cae09d1bf3edb1cc333ddee11318 =>
|
||||||
let hashS = bytesToNumber0x(hash).toString(16);
|
// 2102820b232636d200cb21f1d330f20d096cae09d1bf3edb1cc333ddee113180
|
||||||
if (hashS.length === 63) {
|
const hex = bytesToNumberBE(bytes).toString(16); // toHex unpadded
|
||||||
hashS += '0';
|
if (hex.length === 63) bytes = hex0xToBytes(hex + '0'); // append trailing 0
|
||||||
hash = hexToBytes0x(hashS);
|
return mod(bits2int(bytes), CURVE_ORDER);
|
||||||
}
|
|
||||||
}
|
|
||||||
// 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;
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
export const _starkCurve = curve;
|
||||||
|
|
||||||
// Custom Starknet type conversion functions that can handle 0x and unpadded hex
|
function ensureBytes(hex: Hex): Uint8Array {
|
||||||
function hexToBytes0x(hex: string): Uint8Array {
|
return ensureBytesOrig('', typeof hex === 'string' ? hex0xToBytes(hex) : hex);
|
||||||
if (typeof hex !== 'string') {
|
|
||||||
throw new TypeError('hexToBytes: expected string, got ' + typeof hex);
|
|
||||||
}
|
|
||||||
hex = strip0x(hex);
|
|
||||||
if (hex.length & 1) hex = '0' + hex; // padding
|
|
||||||
if (hex.length % 2) throw new Error('hexToBytes: received invalid unpadded hex ' + 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');
|
|
||||||
array[i] = byte;
|
|
||||||
}
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
function hexToNumber0x(hex: string): bigint {
|
|
||||||
if (typeof hex !== 'string') {
|
|
||||||
throw new TypeError('hexToNumber: expected string, got ' + typeof hex);
|
|
||||||
}
|
|
||||||
// Big Endian
|
|
||||||
// TODO: strip vs no strip?
|
|
||||||
return BigInt(`0x${strip0x(hex)}`);
|
|
||||||
}
|
|
||||||
function bytesToNumber0x(bytes: Uint8Array): bigint {
|
|
||||||
return hexToNumber0x(cutils.bytesToHex(bytes));
|
|
||||||
}
|
|
||||||
function ensureBytes0x(hex: Hex): Uint8Array {
|
|
||||||
// Uint8Array.from() instead of hash.slice() because node.js Buffer
|
|
||||||
// is instance of Uint8Array, and its slice() creates **mutable** copy
|
|
||||||
return hex instanceof Uint8Array ? Uint8Array.from(hex) : hexToBytes0x(hex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizePrivateKey(privKey: Hex) {
|
function normPrivKey(privKey: Hex): string {
|
||||||
return cutils.bytesToHex(ensureBytes0x(privKey)).padStart(32 * 2, '0');
|
return bytesToHex(ensureBytes(privKey)).padStart(64, '0');
|
||||||
}
|
}
|
||||||
function getPublicKey0x(privKey: Hex, isCompressed?: boolean) {
|
export function getPublicKey(privKey: Hex, isCompressed = false): Uint8Array {
|
||||||
return starkCurve.getPublicKey(normalizePrivateKey(privKey), isCompressed);
|
return curve.getPublicKey(normPrivKey(privKey), isCompressed);
|
||||||
}
|
}
|
||||||
function getSharedSecret0x(privKeyA: Hex, pubKeyB: Hex) {
|
export function getSharedSecret(privKeyA: Hex, pubKeyB: Hex): Uint8Array {
|
||||||
return starkCurve.getSharedSecret(normalizePrivateKey(privKeyA), pubKeyB);
|
return curve.getSharedSecret(normPrivKey(privKeyA), pubKeyB);
|
||||||
|
}
|
||||||
|
export function sign(msgHash: Hex, privKey: Hex, opts?: any): SignatureType {
|
||||||
|
return curve.sign(ensureBytes(msgHash), normPrivKey(privKey), opts);
|
||||||
|
}
|
||||||
|
export function verify(signature: SignatureType | Hex, msgHash: Hex, pubKey: Hex) {
|
||||||
|
const sig = signature instanceof Signature ? signature : ensureBytes(signature);
|
||||||
|
return curve.verify(sig, ensureBytes(msgHash), ensureBytes(pubKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
function sign0x(msgHash: Hex, privKey: Hex, opts: any) {
|
const { CURVE, ProjectivePoint, Signature, utils } = curve;
|
||||||
if (typeof privKey === 'string') privKey = strip0x(privKey).padStart(64, '0');
|
export { CURVE, ProjectivePoint, Signature, utils };
|
||||||
return starkCurve.sign(ensureBytes0x(msgHash), normalizePrivateKey(privKey), opts);
|
|
||||||
|
function extractX(bytes: Uint8Array): string {
|
||||||
|
const hex = bytesToHex(bytes.subarray(1));
|
||||||
|
const stripped = hex.replace(/^0+/gm, ''); // strip leading 0s
|
||||||
|
return `0x${stripped}`;
|
||||||
}
|
}
|
||||||
function verify0x(signature: Hex, msgHash: Hex, pubKey: Hex) {
|
function strip0x(hex: string) {
|
||||||
const sig = signature instanceof Signature ? signature : ensureBytes0x(signature);
|
return hex.replace(/^0x/i, '');
|
||||||
return starkCurve.verify(sig, ensureBytes0x(msgHash), ensureBytes0x(pubKey));
|
}
|
||||||
}
|
function numberTo0x16(num: bigint) {
|
||||||
|
// can't use utils.numberToHexUnpadded: adds leading 0 for even byte length
|
||||||
const { CURVE, Point, ProjectivePoint, Signature } = starkCurve;
|
return `0x${num.toString(16)}`;
|
||||||
export const utils = starkCurve.utils;
|
|
||||||
export {
|
|
||||||
CURVE,
|
|
||||||
Point,
|
|
||||||
Signature,
|
|
||||||
ProjectivePoint,
|
|
||||||
getPublicKey0x as getPublicKey,
|
|
||||||
getSharedSecret0x as getSharedSecret,
|
|
||||||
sign0x as sign,
|
|
||||||
verify0x as verify,
|
|
||||||
};
|
|
||||||
|
|
||||||
const stripLeadingZeros = (s: string) => s.replace(/^0+/gm, '');
|
|
||||||
export const bytesToHexEth = (uint8a: Uint8Array): string =>
|
|
||||||
`0x${stripLeadingZeros(cutils.bytesToHex(uint8a))}`;
|
|
||||||
export const strip0x = (hex: string) => hex.replace(/^0x/i, '');
|
|
||||||
export const numberToHexEth = (num: bigint | number) => `0x${num.toString(16)}`;
|
|
||||||
|
|
||||||
// We accept hex strings besides Uint8Array for simplicity
|
|
||||||
type Hex = Uint8Array | string;
|
|
||||||
|
|
||||||
// 1. seed generation
|
|
||||||
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))));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// seed generation
|
||||||
export function grindKey(seed: Hex) {
|
export function grindKey(seed: Hex) {
|
||||||
const _seed = ensureBytes0x(seed);
|
const _seed = ensureBytes(seed);
|
||||||
const sha256mask = 2n ** 256n;
|
const sha256mask = 2n ** 256n;
|
||||||
const limit = sha256mask - starkCurve.utils.mod(sha256mask, starkCurve.CURVE.n);
|
const limit = sha256mask - mod(sha256mask, CURVE_ORDER);
|
||||||
for (let i = 0; ; i++) {
|
for (let i = 0; ; i++) {
|
||||||
const key = hashKeyWithIndex(_seed, i);
|
const key = sha256Num(concatBytes(_seed, numberToVarBytesBE(BigInt(i))));
|
||||||
// key should be in [0, limit)
|
if (key < limit) return mod(key, CURVE_ORDER).toString(16); // key should be in [0, limit)
|
||||||
if (key < limit) return starkCurve.utils.mod(key, starkCurve.CURVE.n).toString(16);
|
if (i === 100000) throw new Error('grindKey is broken: tried 100k vals'); // prevent dos
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getStarkKey(privateKey: Hex) {
|
export function getStarkKey(privateKey: Hex): string {
|
||||||
return bytesToHexEth(getPublicKey0x(privateKey, true).slice(1));
|
return extractX(getPublicKey(privateKey, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ethSigToPrivate(signature: string) {
|
export function ethSigToPrivate(signature: string): string {
|
||||||
signature = strip0x(signature.replace(/^0x/, ''));
|
signature = strip0x(signature);
|
||||||
if (signature.length !== 130) throw new Error('Wrong ethereum signature');
|
if (signature.length !== 130) throw new Error('Wrong ethereum signature');
|
||||||
return grindKey(signature.substring(0, 64));
|
return grindKey(signature.substring(0, 64));
|
||||||
}
|
}
|
||||||
@@ -163,38 +132,41 @@ export function getAccountPath(
|
|||||||
application: string,
|
application: string,
|
||||||
ethereumAddress: string,
|
ethereumAddress: string,
|
||||||
index: number
|
index: number
|
||||||
) {
|
): string {
|
||||||
const layerNum = int31(bytesToNumber0x(sha256(layer)));
|
const layerNum = int31(sha256Num(layer));
|
||||||
const applicationNum = int31(bytesToNumber0x(sha256(application)));
|
const applicationNum = int31(sha256Num(application));
|
||||||
const eth = hexToNumber0x(ethereumAddress);
|
const eth = hexToNumber(strip0x(ethereumAddress));
|
||||||
return `m/2645'/${layerNum}'/${applicationNum}'/${int31(eth)}'/${int31(eth >> 31n)}'/${index}`;
|
return `m/2645'/${layerNum}'/${applicationNum}'/${int31(eth)}'/${int31(eth >> 31n)}'/${index}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://docs.starkware.co/starkex/pedersen-hash-function.html
|
// https://docs.starkware.co/starkex/pedersen-hash-function.html
|
||||||
const PEDERSEN_POINTS_AFFINE = [
|
const PEDERSEN_POINTS = [
|
||||||
new Point(
|
new ProjectivePoint(
|
||||||
2089986280348253421170679821480865132823066470938446095505822317253594081284n,
|
2089986280348253421170679821480865132823066470938446095505822317253594081284n,
|
||||||
1713931329540660377023406109199410414810705867260802078187082345529207694986n
|
1713931329540660377023406109199410414810705867260802078187082345529207694986n,
|
||||||
|
1n
|
||||||
),
|
),
|
||||||
new Point(
|
new ProjectivePoint(
|
||||||
996781205833008774514500082376783249102396023663454813447423147977397232763n,
|
996781205833008774514500082376783249102396023663454813447423147977397232763n,
|
||||||
1668503676786377725805489344771023921079126552019160156920634619255970485781n
|
1668503676786377725805489344771023921079126552019160156920634619255970485781n,
|
||||||
|
1n
|
||||||
),
|
),
|
||||||
new Point(
|
new ProjectivePoint(
|
||||||
2251563274489750535117886426533222435294046428347329203627021249169616184184n,
|
2251563274489750535117886426533222435294046428347329203627021249169616184184n,
|
||||||
1798716007562728905295480679789526322175868328062420237419143593021674992973n
|
1798716007562728905295480679789526322175868328062420237419143593021674992973n,
|
||||||
|
1n
|
||||||
),
|
),
|
||||||
new Point(
|
new ProjectivePoint(
|
||||||
2138414695194151160943305727036575959195309218611738193261179310511854807447n,
|
2138414695194151160943305727036575959195309218611738193261179310511854807447n,
|
||||||
113410276730064486255102093846540133784865286929052426931474106396135072156n
|
113410276730064486255102093846540133784865286929052426931474106396135072156n,
|
||||||
|
1n
|
||||||
),
|
),
|
||||||
new Point(
|
new ProjectivePoint(
|
||||||
2379962749567351885752724891227938183011949129833673362440656643086021394946n,
|
2379962749567351885752724891227938183011949129833673362440656643086021394946n,
|
||||||
776496453633298175483985398648758586525933812536653089401905292063708816422n
|
776496453633298175483985398648758586525933812536653089401905292063708816422n,
|
||||||
|
1n
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
// for (const p of PEDERSEN_POINTS) p._setWindowSize(8);
|
|
||||||
const PEDERSEN_POINTS = PEDERSEN_POINTS_AFFINE.map(ProjectivePoint.fromAffine);
|
|
||||||
|
|
||||||
function pedersenPrecompute(p1: ProjectivePoint, p2: ProjectivePoint): ProjectivePoint[] {
|
function pedersenPrecompute(p1: ProjectivePoint, p2: ProjectivePoint): ProjectivePoint[] {
|
||||||
const out: ProjectivePoint[] = [];
|
const out: ProjectivePoint[] = [];
|
||||||
@@ -203,6 +175,8 @@ function pedersenPrecompute(p1: ProjectivePoint, p2: ProjectivePoint): Projectiv
|
|||||||
out.push(p);
|
out.push(p);
|
||||||
p = p.double();
|
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;
|
p = p2;
|
||||||
for (let i = 0; i < 4; i++) {
|
for (let i = 0; i < 4; i++) {
|
||||||
out.push(p);
|
out.push(p);
|
||||||
@@ -216,14 +190,16 @@ const PEDERSEN_POINTS2 = pedersenPrecompute(PEDERSEN_POINTS[3], PEDERSEN_POINTS[
|
|||||||
type PedersenArg = Hex | bigint | number;
|
type PedersenArg = Hex | bigint | number;
|
||||||
function pedersenArg(arg: PedersenArg): bigint {
|
function pedersenArg(arg: PedersenArg): bigint {
|
||||||
let value: bigint;
|
let value: bigint;
|
||||||
if (typeof arg === 'bigint') value = arg;
|
if (typeof arg === 'bigint') {
|
||||||
else if (typeof arg === 'number') {
|
value = arg;
|
||||||
|
} else if (typeof arg === 'number') {
|
||||||
if (!Number.isSafeInteger(arg)) throw new Error(`Invalid pedersenArg: ${arg}`);
|
if (!Number.isSafeInteger(arg)) throw new Error(`Invalid pedersenArg: ${arg}`);
|
||||||
value = BigInt(arg);
|
value = BigInt(arg);
|
||||||
} else value = bytesToNumber0x(ensureBytes0x(arg));
|
} else {
|
||||||
// [0..Fp)
|
value = bytesToNumberBE(ensureBytes(arg));
|
||||||
if (!(0n <= value && value < starkCurve.CURVE.Fp.ORDER))
|
}
|
||||||
throw new Error(`PedersenArg should be 0 <= value < CURVE.P: ${value}`);
|
if (!(0n <= value && value < curve.CURVE.Fp.ORDER))
|
||||||
|
throw new Error(`PedersenArg should be 0 <= value < CURVE.P: ${value}`); // [0..Fp)
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,7 +207,7 @@ function pedersenSingle(point: ProjectivePoint, value: PedersenArg, constants: P
|
|||||||
let x = pedersenArg(value);
|
let x = pedersenArg(value);
|
||||||
for (let j = 0; j < 252; j++) {
|
for (let j = 0; j < 252; j++) {
|
||||||
const pt = constants[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);
|
if ((x & 1n) !== 0n) point = point.add(pt);
|
||||||
x >>= 1n;
|
x >>= 1n;
|
||||||
}
|
}
|
||||||
@@ -239,17 +215,17 @@ function pedersenSingle(point: ProjectivePoint, value: PedersenArg, constants: P
|
|||||||
}
|
}
|
||||||
|
|
||||||
// shift_point + x_low * P_0 + x_high * P1 + y_low * P2 + y_high * P3
|
// shift_point + x_low * P_0 + x_high * P1 + y_low * P2 + y_high * P3
|
||||||
export function pedersen(x: PedersenArg, y: PedersenArg) {
|
export function pedersen(x: PedersenArg, y: PedersenArg): string {
|
||||||
let point: ProjectivePoint = PEDERSEN_POINTS[0];
|
let point: ProjectivePoint = PEDERSEN_POINTS[0];
|
||||||
point = pedersenSingle(point, x, PEDERSEN_POINTS1);
|
point = pedersenSingle(point, x, PEDERSEN_POINTS1);
|
||||||
point = pedersenSingle(point, y, PEDERSEN_POINTS2);
|
point = pedersenSingle(point, y, PEDERSEN_POINTS2);
|
||||||
return bytesToHexEth(point.toAffine().toRawBytes(true).slice(1));
|
return extractX(point.toRawBytes(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function hashChain(data: PedersenArg[], fn = pedersen) {
|
export function hashChain(data: PedersenArg[], fn = pedersen) {
|
||||||
if (!Array.isArray(data) || data.length < 1)
|
if (!Array.isArray(data) || data.length < 1)
|
||||||
throw new Error('data should be array of at least 1 element');
|
throw new Error('data should be array of at least 1 element');
|
||||||
if (data.length === 1) return numberToHexEth(pedersenArg(data[0]));
|
if (data.length === 1) return numberTo0x16(pedersenArg(data[0]));
|
||||||
return Array.from(data)
|
return Array.from(data)
|
||||||
.reverse()
|
.reverse()
|
||||||
.reduce((acc, i) => fn(i, acc));
|
.reduce((acc, i) => fn(i, acc));
|
||||||
@@ -258,5 +234,85 @@ export function hashChain(data: PedersenArg[], fn = pedersen) {
|
|||||||
export const computeHashOnElements = (data: PedersenArg[], fn = pedersen) =>
|
export const computeHashOnElements = (data: PedersenArg[], fn = pedersen) =>
|
||||||
[0, ...data, data.length].reduce((x, y) => fn(x, y));
|
[0, ...data, data.length].reduce((x, y) => fn(x, y));
|
||||||
|
|
||||||
const MASK_250 = 2n ** 250n - 1n;
|
const MASK_250 = bitMask(250);
|
||||||
export const keccak = (data: Uint8Array) => bytesToNumber0x(keccak_256(data)) & MASK_250;
|
export const keccak = (data: Uint8Array): bigint => bytesToNumberBE(keccak_256(data)) & MASK_250;
|
||||||
|
const sha256Num = (data: Uint8Array | string): bigint => 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({
|
||||||
|
...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];
|
||||||
|
}
|
||||||
|
|||||||
44
test/_more-curves.helpers.js
Normal file
44
test/_more-curves.helpers.js
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
|
import { createCurve } from '../_shortw_utils.js';
|
||||||
|
import { sha224, sha256 } from '@noble/hashes/sha256';
|
||||||
|
import { Fp } from '../abstract/modular.js';
|
||||||
|
|
||||||
|
// NIST secp192r1 aka P192
|
||||||
|
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/secg/secp192r1
|
||||||
|
export const P192 = createCurve(
|
||||||
|
{
|
||||||
|
// Params: a, b
|
||||||
|
a: BigInt('0xfffffffffffffffffffffffffffffffefffffffffffffffc'),
|
||||||
|
b: BigInt('0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1'),
|
||||||
|
// Field over which we'll do calculations; 2n ** 192n - 2n ** 64n - 1n
|
||||||
|
Fp: Fp(BigInt('0xfffffffffffffffffffffffffffffffeffffffffffffffff')),
|
||||||
|
// Curve order, total count of valid points in the field.
|
||||||
|
n: BigInt('0xffffffffffffffffffffffff99def836146bc9b1b4d22831'),
|
||||||
|
// Base point (x, y) aka generator point
|
||||||
|
Gx: BigInt('0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012'),
|
||||||
|
Gy: BigInt('0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811'),
|
||||||
|
h: BigInt(1),
|
||||||
|
lowS: false,
|
||||||
|
},
|
||||||
|
sha256
|
||||||
|
);
|
||||||
|
export const secp192r1 = P192;
|
||||||
|
|
||||||
|
export const P224 = createCurve(
|
||||||
|
{
|
||||||
|
// Params: a, b
|
||||||
|
a: BigInt('0xfffffffffffffffffffffffffffffffefffffffffffffffffffffffe'),
|
||||||
|
b: BigInt('0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4'),
|
||||||
|
// Field over which we'll do calculations;
|
||||||
|
Fp: Fp(BigInt('0xffffffffffffffffffffffffffffffff000000000000000000000001')),
|
||||||
|
// Curve order, total count of valid points in the field
|
||||||
|
n: BigInt('0xffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d'),
|
||||||
|
// Base point (x, y) aka generator point
|
||||||
|
Gx: BigInt('0xb70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21'),
|
||||||
|
Gy: BigInt('0xbd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34'),
|
||||||
|
h: BigInt(1),
|
||||||
|
lowS: false,
|
||||||
|
},
|
||||||
|
sha224
|
||||||
|
);
|
||||||
|
export const secp224r1 = P224;
|
||||||
@@ -1,21 +1,313 @@
|
|||||||
import { deepStrictEqual, throws } from 'assert';
|
import { deepStrictEqual, throws } from 'assert';
|
||||||
import { should } from 'micro-should';
|
import { should, describe } from 'micro-should';
|
||||||
import * as fc from 'fast-check';
|
import * as fc from 'fast-check';
|
||||||
import * as mod from '../lib/esm/abstract/modular.js';
|
import * as mod from '../esm/abstract/modular.js';
|
||||||
|
import { bytesToHex as toHex } from '../esm/abstract/utils.js';
|
||||||
// Generic tests for all curves in package
|
// Generic tests for all curves in package
|
||||||
import { secp192r1 } from '../lib/esm/p192.js';
|
import { secp192r1, secp224r1 } from './_more-curves.helpers.js';
|
||||||
import { secp224r1 } from '../lib/esm/p224.js';
|
import { secp256r1 } from '../esm/p256.js';
|
||||||
import { secp256r1 } from '../lib/esm/p256.js';
|
import { secp384r1 } from '../esm/p384.js';
|
||||||
import { secp384r1 } from '../lib/esm/p384.js';
|
import { secp521r1 } from '../esm/p521.js';
|
||||||
import { secp521r1 } from '../lib/esm/p521.js';
|
import { secp256k1 } from '../esm/secp256k1.js';
|
||||||
import { secp256k1 } from '../lib/esm/secp256k1.js';
|
import { ed25519, ed25519ctx, ed25519ph, x25519 } from '../esm/ed25519.js';
|
||||||
import { ed25519, ed25519ctx, ed25519ph } from '../lib/esm/ed25519.js';
|
import { ed448, ed448ph } from '../esm/ed448.js';
|
||||||
import { ed448, ed448ph } from '../lib/esm/ed448.js';
|
import { _starkCurve as starkCurve } from '../esm/stark.js';
|
||||||
import { starkCurve } from '../lib/esm/stark.js';
|
import { pallas, vesta } from '../esm/pasta.js';
|
||||||
import { pallas, vesta } from '../lib/esm/pasta.js';
|
import { bn254 } from '../esm/bn.js';
|
||||||
import { bn254 } from '../lib/esm/bn.js';
|
import { jubjub } from '../esm/jubjub.js';
|
||||||
import { jubjub } from '../lib/esm/jubjub.js';
|
import { bls12_381 } from '../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');
|
||||||
|
// Returns odd/even element
|
||||||
|
deepStrictEqual(Fp.isOdd(mod.FpSqrtOdd(Fp, a)), true);
|
||||||
|
deepStrictEqual(Fp.isOdd(mod.FpSqrtEven(Fp, a)), false);
|
||||||
|
deepStrictEqual(Fp.eql(Fp.sqr(mod.FpSqrtOdd(Fp, a)), a), true);
|
||||||
|
deepStrictEqual(Fp.eql(Fp.sqr(mod.FpSqrtEven(Fp, a)), a), true);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
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);
|
||||||
|
// FpDiv tests
|
||||||
|
deepStrictEqual(mod.FpDiv(Fp, a, Fp.ONE), a);
|
||||||
|
deepStrictEqual(mod.FpDiv(Fp, 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);
|
||||||
|
deepStrictEqual(mod.FpDiv(Fp, 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)));
|
||||||
|
deepStrictEqual(
|
||||||
|
mod.FpDiv(Fp, Fp.add(a, b), c),
|
||||||
|
Fp.add(mod.FpDiv(Fp, a, c), mod.FpDiv(Fp, 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
|
// prettier-ignore
|
||||||
const CURVES = {
|
const CURVES = {
|
||||||
secp192r1, secp224r1, secp256r1, secp384r1, secp521r1,
|
secp192r1, secp224r1, secp256r1, secp384r1, secp521r1,
|
||||||
@@ -29,6 +321,7 @@ const CURVES = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const NUM_RUNS = 5;
|
const NUM_RUNS = 5;
|
||||||
|
|
||||||
const getXY = (p) => ({ x: p.x, y: p.y });
|
const getXY = (p) => ({ x: p.x, y: p.y });
|
||||||
|
|
||||||
function equal(a, b, comment) {
|
function equal(a, b, comment) {
|
||||||
@@ -61,9 +354,12 @@ for (const name in CURVES) {
|
|||||||
if (!p) continue;
|
if (!p) continue;
|
||||||
|
|
||||||
const G = [p.ZERO, p.BASE];
|
const G = [p.ZERO, p.BASE];
|
||||||
for (let i = 2; i < 10; i++) G.push(G[1].multiply(i));
|
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
|
// Here we check basic group laws, to verify that points works as group
|
||||||
should(`${name}/${pointName}/Basic group laws (zero)`, () => {
|
should('zero', () => {
|
||||||
equal(G[0].double(), G[0], '(0*G).double() = 0');
|
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].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].subtract(G[0]), G[0], '0*G - 0*G = 0');
|
||||||
@@ -71,37 +367,37 @@ for (const name in CURVES) {
|
|||||||
for (let i = 0; i < G.length; i++) {
|
for (let i = 0; i < G.length; i++) {
|
||||||
const p = G[i];
|
const p = G[i];
|
||||||
equal(p, p.add(G[0]), `${i}*G + 0 = ${i}*G`);
|
equal(p, p.add(G[0]), `${i}*G + 0 = ${i}*G`);
|
||||||
equal(G[0].multiply(i + 1), G[0], `${i + 1}*0 = 0`);
|
equal(G[0].multiply(BigInt(i + 1)), G[0], `${i + 1}*0 = 0`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
should(`${name}/${pointName}/Basic group laws (one)`, () => {
|
should('one', () => {
|
||||||
equal(G[1].double(), G[2], '(1*G).double() = 2*G');
|
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].subtract(G[1]), G[0], '1*G - 1*G = 0');
|
||||||
equal(G[1].add(G[1]), G[2], '1*G + 1*G = 2*G');
|
equal(G[1].add(G[1]), G[2], '1*G + 1*G = 2*G');
|
||||||
});
|
});
|
||||||
should(`${name}/${pointName}/Basic group laws (sanity tests)`, () => {
|
should('sanity tests', () => {
|
||||||
equal(G[2].double(), G[4], `(2*G).double() = 4*G`);
|
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[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`);
|
equal(G[7].add(G[3].negate()), G[4], '7*G - 3*G = 4*G');
|
||||||
});
|
});
|
||||||
should(`${name}/${pointName}/Basic group laws (addition commutativity)`, () => {
|
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[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`);
|
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)`, () => {
|
should('double', () => {
|
||||||
equal(G[3].double(), G[6], '(3*G).double() = 6*G');
|
equal(G[3].double(), G[6], '(3*G).double() = 6*G');
|
||||||
});
|
});
|
||||||
should(`${name}/${pointName}/Basic group laws (multiply)`, () => {
|
should('multiply', () => {
|
||||||
equal(G[2].multiply(3), G[6], '(2*G).multiply(3) = 6*G');
|
equal(G[2].multiply(3n), G[6], '(2*G).multiply(3) = 6*G');
|
||||||
});
|
});
|
||||||
should(`${name}/${pointName}/Basic group laws (same point addition)`, () => {
|
should('add same-point', () => {
|
||||||
equal(G[3].add(G[3]), G[6], `3*G + 3*G = 6*G`);
|
equal(G[3].add(G[3]), G[6], '3*G + 3*G = 6*G');
|
||||||
});
|
});
|
||||||
should(`${name}/${pointName}/Basic group laws (same point (negative) addition)`, () => {
|
should('add same-point negative', () => {
|
||||||
equal(G[3].add(G[3].negate()), G[0], '3*G + (- 3*G) = 0*G');
|
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');
|
equal(G[3].subtract(G[3]), G[0], '3*G - 3*G = 0*G');
|
||||||
});
|
});
|
||||||
should(`${name}/${pointName}/Basic group laws (curve order)`, () => {
|
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[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 - 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');
|
equal(G[1].multiply(CURVE_ORDER - 2n).add(G[2]), G[0], '(N-2)*G + 2*G = 0');
|
||||||
@@ -109,7 +405,7 @@ for (const name in CURVES) {
|
|||||||
const carry = CURVE_ORDER % 2n === 1n ? G[1] : G[0];
|
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');
|
equal(G[1].multiply(half).double().add(carry), G[0], '((N/2) * G).double() = 0');
|
||||||
});
|
});
|
||||||
should(`${name}/${pointName}/Basic group laws (inversion)`, () => {
|
should('inversion', () => {
|
||||||
const a = 1234n;
|
const a = 1234n;
|
||||||
const b = 5678n;
|
const b = 5678n;
|
||||||
const c = a * b;
|
const c = a * b;
|
||||||
@@ -117,7 +413,7 @@ for (const name in CURVES) {
|
|||||||
const inv = mod.invert(b, CURVE_ORDER);
|
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');
|
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)`, () =>
|
should('multiply, rand', () =>
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(FC_BIGINT, FC_BIGINT, (a, b) => {
|
fc.property(FC_BIGINT, FC_BIGINT, (a, b) => {
|
||||||
const c = mod.mod(a + b, CURVE_ORDER);
|
const c = mod.mod(a + b, CURVE_ORDER);
|
||||||
@@ -125,45 +421,58 @@ for (const name in CURVES) {
|
|||||||
const pA = G[1].multiply(a);
|
const pA = G[1].multiply(a);
|
||||||
const pB = G[1].multiply(b);
|
const pB = G[1].multiply(b);
|
||||||
const pC = G[1].multiply(c);
|
const pC = G[1].multiply(c);
|
||||||
equal(pA.add(pB), pB.add(pA), `pA + pB = pB + pA`);
|
equal(pA.add(pB), pB.add(pA), 'pA + pB = pB + pA');
|
||||||
equal(pA.add(pB), pC, `pA + pB = pC`);
|
equal(pA.add(pB), pC, 'pA + pB = pC');
|
||||||
}),
|
}),
|
||||||
{ numRuns: NUM_RUNS }
|
{ numRuns: NUM_RUNS }
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
should(`${name}/${pointName}/Basic group laws (multiply2, rand)`, () =>
|
should('multiply2, rand', () =>
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(FC_BIGINT, FC_BIGINT, (a, b) => {
|
fc.property(FC_BIGINT, FC_BIGINT, (a, b) => {
|
||||||
const c = mod.mod(a * b, CURVE_ORDER);
|
const c = mod.mod(a * b, CURVE_ORDER);
|
||||||
const pA = G[1].multiply(a);
|
const pA = G[1].multiply(a);
|
||||||
const pB = G[1].multiply(b);
|
const pB = G[1].multiply(b);
|
||||||
equal(pA.multiply(b), pB.multiply(a), `b*pA = a*pB`);
|
equal(pA.multiply(b), pB.multiply(a), 'b*pA = a*pB');
|
||||||
equal(pA.multiply(b), G[1].multiply(c), `b*pA = c*G`);
|
equal(pA.multiply(b), G[1].multiply(c), 'b*pA = c*G');
|
||||||
}),
|
}),
|
||||||
{ numRuns: NUM_RUNS }
|
{ numRuns: NUM_RUNS }
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
});
|
||||||
|
|
||||||
for (const op of ['add', 'subtract']) {
|
for (const op of ['add', 'subtract']) {
|
||||||
should(`${name}/${pointName}/${op} type check`, () => {
|
describe(op, () => {
|
||||||
|
should('type check', () => {
|
||||||
throws(() => G[1][op](0), '0');
|
throws(() => G[1][op](0), '0');
|
||||||
throws(() => G[1][op](0n), '0n');
|
throws(() => G[1][op](0n), '0n');
|
||||||
G[1][op](G[2]);
|
G[1][op](G[2]);
|
||||||
throws(() => G[1][op](CURVE_ORDER), 'CURVE_ORDER');
|
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](123.456), '123.456');
|
||||||
throws(() => G[1][op](true), 'true');
|
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]('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]({ 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([])), 'ui8a([])');
|
||||||
throws(() => G[1][op](new Uint8Array([0])), 'ui8a([0])');
|
throws(() => G[1][op](new Uint8Array([0])), 'ui8a([0])');
|
||||||
throws(() => G[1][op](new Uint8Array([1])), 'ui8a([1])');
|
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](new Uint8Array(4096).fill(1)), 'ui8a(4096*[1])');
|
||||||
if (G[1].toAffine) throws(() => G[1][op](C.Point.BASE), `Point ${op} ${pointName}`);
|
// if (G[1].toAffine) throws(() => G[1][op](C.Point.BASE), `Point ${op} ${pointName}`);
|
||||||
throws(() => G[1][op](o.BASE), `${op}/other curve point`);
|
throws(() => G[1][op](o.BASE), `${op}/other curve point`);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
should(`${name}/${pointName}/equals type check`, () => {
|
should('equals type check', () => {
|
||||||
throws(() => G[1].equals(0), '0');
|
throws(() => G[1].equals(0), '0');
|
||||||
throws(() => G[1].equals(0n), '0n');
|
throws(() => G[1].equals(0n), '0n');
|
||||||
deepStrictEqual(G[1].equals(G[2]), false, '1*G != 2*G');
|
deepStrictEqual(G[1].equals(G[2]), false, '1*G != 2*G');
|
||||||
@@ -178,13 +487,14 @@ for (const name in CURVES) {
|
|||||||
throws(() => G[1].equals(new Uint8Array([0])), 'ui8a([0])');
|
throws(() => G[1].equals(new Uint8Array([0])), 'ui8a([0])');
|
||||||
throws(() => G[1].equals(new Uint8Array([1])), 'ui8a([1])');
|
throws(() => G[1].equals(new Uint8Array([1])), 'ui8a([1])');
|
||||||
throws(() => G[1].equals(new Uint8Array(4096).fill(1)), 'ui8a(4096*[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})`);
|
// if (G[1].toAffine) throws(() => G[1].equals(C.Point.BASE), 'Point.equals(${pointName})');
|
||||||
throws(() => G[1].equals(o.BASE), 'other curve point');
|
throws(() => G[1].equals(o.BASE), 'other curve point');
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const op of ['multiply', 'multiplyUnsafe']) {
|
for (const op of ['multiply', 'multiplyUnsafe']) {
|
||||||
if (!p.BASE[op]) continue;
|
if (!p.BASE[op]) continue;
|
||||||
should(`${name}/${pointName}/${op} type check`, () => {
|
describe(op, () => {
|
||||||
|
should('type check', () => {
|
||||||
if (op !== 'multiplyUnsafe') {
|
if (op !== 'multiplyUnsafe') {
|
||||||
throws(() => G[1][op](0), '0');
|
throws(() => G[1][op](0), '0');
|
||||||
throws(() => G[1][op](0n), '0n');
|
throws(() => G[1][op](0n), '0n');
|
||||||
@@ -203,75 +513,173 @@ for (const name in CURVES) {
|
|||||||
throws(() => G[1][op](new Uint8Array(4096).fill(1)), 'ui8a(4096*[1])');
|
throws(() => G[1][op](new Uint8Array(4096).fill(1)), 'ui8a(4096*[1])');
|
||||||
throws(() => G[1][op](o.BASE), 'other curve point');
|
throws(() => G[1][op](o.BASE), 'other curve point');
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
// Complex point (Extended/Jacobian/Projective?)
|
// Complex point (Extended/Jacobian/Projective?)
|
||||||
if (p.BASE.toAffine) {
|
// if (p.BASE.toAffine && C.Point) {
|
||||||
should(`${name}/${pointName}/toAffine()`, () => {
|
// should('toAffine()', () => {
|
||||||
equal(p.ZERO.toAffine(), C.Point.ZERO, `0 = 0`);
|
// equal(p.ZERO.toAffine(), C.Point.ZERO, '0 = 0');
|
||||||
equal(p.BASE.toAffine(), C.Point.BASE, `1 = 1`);
|
// equal(p.BASE.toAffine(), C.Point.BASE, '1 = 1');
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
if (p.fromAffine) {
|
// if (p.fromAffine && C.Point) {
|
||||||
should(`${name}/${pointName}/fromAffine()`, () => {
|
// should('fromAffine()', () => {
|
||||||
equal(p.ZERO, p.fromAffine(C.Point.ZERO), `0 = 0`);
|
// equal(p.ZERO, p.fromAffine(C.Point.ZERO), '0 = 0');
|
||||||
equal(p.BASE, p.fromAffine(C.Point.BASE), `1 = 1`);
|
// equal(p.BASE, p.fromAffine(C.Point.BASE), '1 = 1');
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
// toHex/fromHex (if available)
|
// toHex/fromHex (if available)
|
||||||
if (p.fromHex && p.BASE.toHex) {
|
if (p.fromHex && p.BASE.toHex) {
|
||||||
should(`${name}/${pointName}/fromHex(toHex()) roundtrip`, () => {
|
should('fromHex(toHex()) roundtrip', () => {
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(FC_BIGINT, (x) => {
|
fc.property(FC_BIGINT, (x) => {
|
||||||
const hex = p.BASE.multiply(x).toHex();
|
const point = p.BASE.multiply(x);
|
||||||
|
const hex = point.toHex();
|
||||||
|
const bytes = point.toRawBytes();
|
||||||
deepStrictEqual(p.fromHex(hex).toHex(), hex);
|
deepStrictEqual(p.fromHex(hex).toHex(), hex);
|
||||||
|
deepStrictEqual(p.fromHex(bytes).toHex(), hex);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
should('fromHex(toHex(compressed=true)) roundtrip', () => {
|
||||||
|
fc.assert(
|
||||||
|
fc.property(FC_BIGINT, (x) => {
|
||||||
|
const point = p.BASE.multiply(x);
|
||||||
|
const hex = point.toHex(true);
|
||||||
|
const bytes = point.toRawBytes(true);
|
||||||
|
deepStrictEqual(p.fromHex(hex).toHex(true), hex);
|
||||||
|
deepStrictEqual(p.fromHex(bytes).toHex(true), hex);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
describe(name, () => {
|
||||||
// Generic complex things (getPublicKey/sign/verify/getSharedSecret)
|
// Generic complex things (getPublicKey/sign/verify/getSharedSecret)
|
||||||
should(`${name}/getPublicKey type check`, () => {
|
should('.getPublicKey() type check', () => {
|
||||||
throws(() => C.getPublicKey(0), '0');
|
throws(() => C.getPublicKey(0), '0');
|
||||||
throws(() => C.getPublicKey(0n), '0n');
|
throws(() => C.getPublicKey(0n), '0n');
|
||||||
throws(() => C.getPublicKey(false), 'false');
|
throws(() => C.getPublicKey(-123n), '-123n');
|
||||||
|
throws(() => C.getPublicKey(123), '123');
|
||||||
throws(() => C.getPublicKey(123.456), '123.456');
|
throws(() => C.getPublicKey(123.456), '123.456');
|
||||||
throws(() => C.getPublicKey(true), 'true');
|
throws(() => C.getPublicKey(true), 'true');
|
||||||
|
throws(() => C.getPublicKey(false), 'false');
|
||||||
|
throws(() => C.getPublicKey(null), 'null');
|
||||||
|
throws(() => C.getPublicKey(undefined), 'undefined');
|
||||||
throws(() => C.getPublicKey(''), "''");
|
throws(() => C.getPublicKey(''), "''");
|
||||||
// NOTE: passes because of disabled hex padding checks for starknet, maybe enable?
|
// NOTE: passes because of disabled hex padding checks for starknet, maybe enable?
|
||||||
//throws(() => C.getPublicKey('1'), "'1'");
|
// throws(() => C.getPublicKey('1'), "'1'");
|
||||||
throws(() => C.getPublicKey('key'), "'key'");
|
throws(() => C.getPublicKey('key'), "'key'");
|
||||||
|
throws(() => C.getPublicKey({}));
|
||||||
throws(() => C.getPublicKey(new Uint8Array([])));
|
throws(() => C.getPublicKey(new Uint8Array([])));
|
||||||
throws(() => C.getPublicKey(new Uint8Array([0])));
|
throws(() => C.getPublicKey(new Uint8Array([0])));
|
||||||
throws(() => C.getPublicKey(new Uint8Array([1])));
|
throws(() => C.getPublicKey(new Uint8Array([1])));
|
||||||
throws(() => C.getPublicKey(new Uint8Array(4096).fill(1)));
|
throws(() => C.getPublicKey(new Uint8Array(4096).fill(1)));
|
||||||
|
throws(() => C.getPublicKey(Array(32).fill(1)));
|
||||||
});
|
});
|
||||||
should(`${name}.verify()/should verify random signatures`, () =>
|
should('.verify() should verify random signatures', () =>
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(fc.hexaString({ minLength: 64, maxLength: 64 }), (msg) => {
|
fc.property(fc.hexaString({ minLength: 64, maxLength: 64 }), (msg) => {
|
||||||
const priv = C.utils.randomPrivateKey();
|
const priv = C.utils.randomPrivateKey();
|
||||||
const pub = C.getPublicKey(priv);
|
const pub = C.getPublicKey(priv);
|
||||||
const sig = C.sign(msg, priv);
|
const sig = C.sign(msg, priv);
|
||||||
deepStrictEqual(C.verify(sig, msg, pub), true);
|
deepStrictEqual(
|
||||||
|
C.verify(sig, msg, pub),
|
||||||
|
true,
|
||||||
|
`priv=${toHex(priv)},pub=${toHex(pub)},msg=${msg}`
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
{ numRuns: NUM_RUNS }
|
{ numRuns: NUM_RUNS }
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
should(`${name}.sign()/edge cases`, () => {
|
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(''));
|
||||||
|
throws(() => C.sign('', ''));
|
||||||
|
throws(() => C.sign(new Uint8Array(), new Uint8Array()));
|
||||||
});
|
});
|
||||||
|
|
||||||
should(`${name}.verify()/should not verify signature with wrong hash`, () => {
|
describe('verify()', () => {
|
||||||
const MSG = '01'.repeat(32);
|
const msg = '01'.repeat(32);
|
||||||
const PRIV_KEY = 0x2n;
|
should('true for proper signatures', () => {
|
||||||
const WRONG_MSG = '11'.repeat(32);
|
const priv = C.utils.randomPrivateKey();
|
||||||
const signature = C.sign(MSG, PRIV_KEY);
|
const sig = C.sign(msg, priv);
|
||||||
const publicKey = C.getPublicKey(PRIV_KEY);
|
const pub = C.getPublicKey(priv);
|
||||||
deepStrictEqual(C.verify(signature, WRONG_MSG, publicKey), false);
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if (C.Signature) {
|
||||||
|
should('Signature serialization roundtrip', () =>
|
||||||
|
fc.assert(
|
||||||
|
fc.property(fc.hexaString({ minLength: 64, maxLength: 64 }), (msg) => {
|
||||||
|
const priv = C.utils.randomPrivateKey();
|
||||||
|
const sig = C.sign(msg, priv);
|
||||||
|
const sigRS = (sig) => ({ s: sig.s, r: sig.r });
|
||||||
|
// Compact
|
||||||
|
deepStrictEqual(sigRS(C.Signature.fromCompact(sig.toCompactHex())), sigRS(sig));
|
||||||
|
deepStrictEqual(sigRS(C.Signature.fromCompact(sig.toCompactRawBytes())), sigRS(sig));
|
||||||
|
// DER
|
||||||
|
deepStrictEqual(sigRS(C.Signature.fromDER(sig.toDERHex())), sigRS(sig));
|
||||||
|
deepStrictEqual(sigRS(C.Signature.fromDER(sig.toDERRawBytes())), sigRS(sig));
|
||||||
|
}),
|
||||||
|
{ numRuns: NUM_RUNS }
|
||||||
|
)
|
||||||
|
);
|
||||||
|
should('Signature.addRecoveryBit/Signature.recoveryPublicKey', () =>
|
||||||
|
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(sig.recoverPublicKey(msg).toRawBytes(), pub);
|
||||||
|
const sig2 = C.Signature.fromCompact(sig.toCompactHex());
|
||||||
|
throws(() => sig2.recoverPublicKey(msg));
|
||||||
|
const sig3 = sig2.addRecoveryBit(sig.recovery);
|
||||||
|
deepStrictEqual(sig3.recoverPublicKey(msg).toRawBytes(), pub);
|
||||||
|
}),
|
||||||
|
{ numRuns: NUM_RUNS }
|
||||||
|
)
|
||||||
|
);
|
||||||
|
should('Signature.normalizeS', () =>
|
||||||
|
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);
|
||||||
|
const sig2 = sig.normalizeS();
|
||||||
|
deepStrictEqual(sig2.hasHighS(), false);
|
||||||
|
}),
|
||||||
|
{ numRuns: NUM_RUNS }
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// NOTE: fails for ed, because of empty message. Since we convert it to scalar,
|
// 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?
|
// 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`, () => {
|
// should('should not verify signature with wrong message', () => {
|
||||||
// fc.assert(
|
// fc.assert(
|
||||||
// fc.property(
|
// fc.property(
|
||||||
// fc.array(fc.integer({ min: 0x00, max: 0xff })),
|
// fc.array(fc.integer({ min: 0x00, max: 0xff })),
|
||||||
@@ -293,7 +701,7 @@ for (const name in CURVES) {
|
|||||||
// });
|
// });
|
||||||
|
|
||||||
if (C.getSharedSecret) {
|
if (C.getSharedSecret) {
|
||||||
should(`${name}/getSharedSecret() should be commutative`, () => {
|
should('getSharedSecret() should be commutative', () => {
|
||||||
for (let i = 0; i < NUM_RUNS; i++) {
|
for (let i = 0; i < NUM_RUNS; i++) {
|
||||||
const asec = C.utils.randomPrivateKey();
|
const asec = C.utils.randomPrivateKey();
|
||||||
const apub = C.getPublicKey(asec);
|
const apub = C.getPublicKey(asec);
|
||||||
@@ -308,7 +716,34 @@ for (const name in CURVES) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.
|
// ESM is broken.
|
||||||
import url from 'url';
|
import url from 'url';
|
||||||
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
|
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
290
test/ed25519-addons.test.js
Normal file
290
test/ed25519-addons.test.js
Normal file
@@ -0,0 +1,290 @@
|
|||||||
|
import { sha512 } from '@noble/hashes/sha512';
|
||||||
|
import { hexToBytes, bytesToHex, randomBytes } from '@noble/hashes/utils';
|
||||||
|
import { deepStrictEqual, strictEqual, throws } from 'assert';
|
||||||
|
import { describe, should } from 'micro-should';
|
||||||
|
import { numberToBytesLE } from '../esm/abstract/utils.js';
|
||||||
|
import { default as x25519vectors } from './wycheproof/x25519_test.json' assert { type: 'json' };
|
||||||
|
import { ed25519ctx, ed25519ph, RistrettoPoint, x25519 } from '../esm/ed25519.js';
|
||||||
|
|
||||||
|
// const ed = ed25519;
|
||||||
|
const hex = bytesToHex;
|
||||||
|
// const Point = ed.ExtendedPoint;
|
||||||
|
|
||||||
|
const VECTORS_RFC8032_CTX = [
|
||||||
|
{
|
||||||
|
secretKey: '0305334e381af78f141cb666f6199f57bc3495335a256a95bd2a55bf546663f6',
|
||||||
|
publicKey: 'dfc9425e4f968f7f0c29f0259cf5f9aed6851c2bb4ad8bfb860cfee0ab248292',
|
||||||
|
message: 'f726936d19c800494e3fdaff20b276a8',
|
||||||
|
context: '666f6f',
|
||||||
|
signature:
|
||||||
|
'55a4cc2f70a54e04288c5f4cd1e45a7b' +
|
||||||
|
'b520b36292911876cada7323198dd87a' +
|
||||||
|
'8b36950b95130022907a7fb7c4e9b2d5' +
|
||||||
|
'f6cca685a587b4b21f4b888e4e7edb0d',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
secretKey: '0305334e381af78f141cb666f6199f57bc3495335a256a95bd2a55bf546663f6',
|
||||||
|
publicKey: 'dfc9425e4f968f7f0c29f0259cf5f9aed6851c2bb4ad8bfb860cfee0ab248292',
|
||||||
|
message: 'f726936d19c800494e3fdaff20b276a8',
|
||||||
|
context: '626172',
|
||||||
|
signature:
|
||||||
|
'fc60d5872fc46b3aa69f8b5b4351d580' +
|
||||||
|
'8f92bcc044606db097abab6dbcb1aee3' +
|
||||||
|
'216c48e8b3b66431b5b186d1d28f8ee1' +
|
||||||
|
'5a5ca2df6668346291c2043d4eb3e90d',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
secretKey: '0305334e381af78f141cb666f6199f57bc3495335a256a95bd2a55bf546663f6',
|
||||||
|
publicKey: 'dfc9425e4f968f7f0c29f0259cf5f9aed6851c2bb4ad8bfb860cfee0ab248292',
|
||||||
|
message: '508e9e6882b979fea900f62adceaca35',
|
||||||
|
context: '666f6f',
|
||||||
|
signature:
|
||||||
|
'8b70c1cc8310e1de20ac53ce28ae6e72' +
|
||||||
|
'07f33c3295e03bb5c0732a1d20dc6490' +
|
||||||
|
'8922a8b052cf99b7c4fe107a5abb5b2c' +
|
||||||
|
'4085ae75890d02df26269d8945f84b0b',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
secretKey: 'ab9c2853ce297ddab85c993b3ae14bcad39b2c682beabc27d6d4eb20711d6560',
|
||||||
|
publicKey: '0f1d1274943b91415889152e893d80e93275a1fc0b65fd71b4b0dda10ad7d772',
|
||||||
|
message: 'f726936d19c800494e3fdaff20b276a8',
|
||||||
|
context: '666f6f',
|
||||||
|
signature:
|
||||||
|
'21655b5f1aa965996b3f97b3c849eafb' +
|
||||||
|
'a922a0a62992f73b3d1b73106a84ad85' +
|
||||||
|
'e9b86a7b6005ea868337ff2d20a7f5fb' +
|
||||||
|
'd4cd10b0be49a68da2b2e0dc0ad8960f',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
describe('RFC8032ctx', () => {
|
||||||
|
for (let i = 0; i < VECTORS_RFC8032_CTX.length; i++) {
|
||||||
|
const v = VECTORS_RFC8032_CTX[i];
|
||||||
|
should(`${i}`, () => {
|
||||||
|
deepStrictEqual(hex(ed25519ctx.getPublicKey(v.secretKey)), v.publicKey);
|
||||||
|
deepStrictEqual(hex(ed25519ctx.sign(v.message, v.secretKey, v.context)), v.signature);
|
||||||
|
deepStrictEqual(ed25519ctx.verify(v.signature, v.message, v.publicKey, v.context), true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const VECTORS_RFC8032_PH = [
|
||||||
|
{
|
||||||
|
secretKey: '833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42',
|
||||||
|
publicKey: 'ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf',
|
||||||
|
message: '616263',
|
||||||
|
signature:
|
||||||
|
'98a70222f0b8121aa9d30f813d683f80' +
|
||||||
|
'9e462b469c7ff87639499bb94e6dae41' +
|
||||||
|
'31f85042463c2a355a2003d062adf5aa' +
|
||||||
|
'a10b8c61e636062aaad11c2a26083406',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
describe('RFC8032ph', () => {
|
||||||
|
for (let i = 0; i < VECTORS_RFC8032_PH.length; i++) {
|
||||||
|
const v = VECTORS_RFC8032_PH[i];
|
||||||
|
should(`${i}`, () => {
|
||||||
|
deepStrictEqual(hex(ed25519ph.getPublicKey(v.secretKey)), v.publicKey);
|
||||||
|
deepStrictEqual(hex(ed25519ph.sign(v.message, v.secretKey)), v.signature);
|
||||||
|
deepStrictEqual(ed25519ph.verify(v.signature, v.message, v.publicKey), true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// x25519
|
||||||
|
should('X25519 base point', () => {
|
||||||
|
const { y } = ed25519ph.ExtendedPoint.BASE;
|
||||||
|
const { Fp } = ed25519ph.CURVE;
|
||||||
|
const u = Fp.create((y + 1n) * Fp.inv(1n - y));
|
||||||
|
deepStrictEqual(numberToBytesLE(u, 32), x25519.GuBytes);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('RFC7748', () => {
|
||||||
|
const rfc7748Mul = [
|
||||||
|
{
|
||||||
|
scalar: 'a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4',
|
||||||
|
u: 'e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c',
|
||||||
|
outputU: 'c3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scalar: '4b66e9d4d1b4673c5ad22691957d6af5c11b6421e0ea01d42ca4169e7918ba0d',
|
||||||
|
u: 'e5210f12786811d3f4b7959d0538ae2c31dbe7106fc03c3efc4cd549c715a493',
|
||||||
|
outputU: '95cbde9476e8907d7aade45cb4b873f88b595a68799fa152e6f8f7647aac7957',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
for (let i = 0; i < rfc7748Mul.length; i++) {
|
||||||
|
const v = rfc7748Mul[i];
|
||||||
|
should(`scalarMult (${i})`, () => {
|
||||||
|
deepStrictEqual(hex(x25519.scalarMult(v.scalar, v.u)), v.outputU);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const rfc7748Iter = [
|
||||||
|
{ scalar: '422c8e7a6227d7bca1350b3e2bb7279f7897b87bb6854b783c60e80311ae3079', iters: 1 },
|
||||||
|
{ scalar: '684cf59ba83309552800ef566f2f4d3c1c3887c49360e3875f2eb94d99532c51', iters: 1000 },
|
||||||
|
// { scalar: '7c3911e0ab2586fd864497297e575e6f3bc601c0883c30df5f4dd2d24f665424', iters: 1000000 },
|
||||||
|
];
|
||||||
|
for (let i = 0; i < rfc7748Iter.length; i++) {
|
||||||
|
const { scalar, iters } = rfc7748Iter[i];
|
||||||
|
should(`scalarMult iteration (${i})`, () => {
|
||||||
|
let k = x25519.GuBytes;
|
||||||
|
for (let i = 0, u = k; i < iters; i++) [k, u] = [x25519.scalarMult(k, u), k];
|
||||||
|
deepStrictEqual(hex(k), scalar);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
should('getSharedKey', () => {
|
||||||
|
const alicePrivate = '77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a';
|
||||||
|
const alicePublic = '8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a';
|
||||||
|
const bobPrivate = '5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb';
|
||||||
|
const bobPublic = 'de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f';
|
||||||
|
const shared = '4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742';
|
||||||
|
deepStrictEqual(alicePublic, hex(x25519.getPublicKey(alicePrivate)));
|
||||||
|
deepStrictEqual(bobPublic, hex(x25519.getPublicKey(bobPrivate)));
|
||||||
|
deepStrictEqual(hex(x25519.scalarMult(alicePrivate, bobPublic)), shared);
|
||||||
|
deepStrictEqual(hex(x25519.scalarMult(bobPrivate, alicePublic)), shared);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('Wycheproof', () => {
|
||||||
|
const group = x25519vectors.testGroups[0];
|
||||||
|
should(`X25519`, () => {
|
||||||
|
for (let i = 0; i < group.tests.length; i++) {
|
||||||
|
const v = group.tests[i];
|
||||||
|
const comment = `(${i}, ${v.result}) ${v.comment}`;
|
||||||
|
if (v.result === 'valid' || v.result === 'acceptable') {
|
||||||
|
try {
|
||||||
|
const shared = hex(x25519.scalarMult(v.private, v.public));
|
||||||
|
deepStrictEqual(shared, v.shared, comment);
|
||||||
|
} catch (e) {
|
||||||
|
// We are more strict
|
||||||
|
if (e.message.includes('Expected valid scalar')) return;
|
||||||
|
if (e.message.includes('Invalid private or public key received')) return;
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
} else if (v.result === 'invalid') {
|
||||||
|
let failed = false;
|
||||||
|
try {
|
||||||
|
x25519.scalarMult(v.private, v.public);
|
||||||
|
} catch (error) {
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
|
deepStrictEqual(failed, true, comment);
|
||||||
|
} else throw new Error('unknown test result');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function utf8ToBytes(str) {
|
||||||
|
if (typeof str !== 'string') {
|
||||||
|
throw new Error(`utf8ToBytes expected string, got ${typeof str}`);
|
||||||
|
}
|
||||||
|
return new TextEncoder().encode(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('ristretto255', () => {
|
||||||
|
should('follow the byte encodings of small multiples', () => {
|
||||||
|
const encodingsOfSmallMultiples = [
|
||||||
|
// This is the identity point
|
||||||
|
'0000000000000000000000000000000000000000000000000000000000000000',
|
||||||
|
// This is the basepoint
|
||||||
|
'e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76',
|
||||||
|
// These are small multiples of the basepoint
|
||||||
|
'6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919',
|
||||||
|
'94741f5d5d52755ece4f23f044ee27d5d1ea1e2bd196b462166b16152a9d0259',
|
||||||
|
'da80862773358b466ffadfe0b3293ab3d9fd53c5ea6c955358f568322daf6a57',
|
||||||
|
'e882b131016b52c1d3337080187cf768423efccbb517bb495ab812c4160ff44e',
|
||||||
|
'f64746d3c92b13050ed8d80236a7f0007c3b3f962f5ba793d19a601ebb1df403',
|
||||||
|
'44f53520926ec81fbd5a387845beb7df85a96a24ece18738bdcfa6a7822a176d',
|
||||||
|
'903293d8f2287ebe10e2374dc1a53e0bc887e592699f02d077d5263cdd55601c',
|
||||||
|
'02622ace8f7303a31cafc63f8fc48fdc16e1c8c8d234b2f0d6685282a9076031',
|
||||||
|
'20706fd788b2720a1ed2a5dad4952b01f413bcf0e7564de8cdc816689e2db95f',
|
||||||
|
'bce83f8ba5dd2fa572864c24ba1810f9522bc6004afe95877ac73241cafdab42',
|
||||||
|
'e4549ee16b9aa03099ca208c67adafcafa4c3f3e4e5303de6026e3ca8ff84460',
|
||||||
|
'aa52e000df2e16f55fb1032fc33bc42742dad6bd5a8fc0be0167436c5948501f',
|
||||||
|
'46376b80f409b29dc2b5f6f0c52591990896e5716f41477cd30085ab7f10301e',
|
||||||
|
'e0c418f7c8d9c4cdd7395b93ea124f3ad99021bb681dfc3302a9d99a2e53e64e',
|
||||||
|
];
|
||||||
|
let B = RistrettoPoint.BASE;
|
||||||
|
let P = RistrettoPoint.ZERO;
|
||||||
|
for (const encoded of encodingsOfSmallMultiples) {
|
||||||
|
deepStrictEqual(P.toHex(), encoded);
|
||||||
|
deepStrictEqual(RistrettoPoint.fromHex(encoded).toHex(), encoded);
|
||||||
|
P = P.add(B);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
should('not convert bad bytes encoding', () => {
|
||||||
|
const badEncodings = [
|
||||||
|
// These are all bad because they're non-canonical field encodings.
|
||||||
|
'00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
|
||||||
|
'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f',
|
||||||
|
'f3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f',
|
||||||
|
'edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f',
|
||||||
|
// These are all bad because they're negative field elements.
|
||||||
|
'0100000000000000000000000000000000000000000000000000000000000000',
|
||||||
|
'01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f',
|
||||||
|
'ed57ffd8c914fb201471d1c3d245ce3c746fcbe63a3679d51b6a516ebebe0e20',
|
||||||
|
'c34c4e1826e5d403b78e246e88aa051c36ccf0aafebffe137d148a2bf9104562',
|
||||||
|
'c940e5a4404157cfb1628b108db051a8d439e1a421394ec4ebccb9ec92a8ac78',
|
||||||
|
'47cfc5497c53dc8e61c91d17fd626ffb1c49e2bca94eed052281b510b1117a24',
|
||||||
|
'f1c6165d33367351b0da8f6e4511010c68174a03b6581212c71c0e1d026c3c72',
|
||||||
|
'87260f7a2f12495118360f02c26a470f450dadf34a413d21042b43b9d93e1309',
|
||||||
|
// These are all bad because they give a nonsquare x².
|
||||||
|
'26948d35ca62e643e26a83177332e6b6afeb9d08e4268b650f1f5bbd8d81d371',
|
||||||
|
'4eac077a713c57b4f4397629a4145982c661f48044dd3f96427d40b147d9742f',
|
||||||
|
'de6a7b00deadc788eb6b6c8d20c0ae96c2f2019078fa604fee5b87d6e989ad7b',
|
||||||
|
'bcab477be20861e01e4a0e295284146a510150d9817763caf1a6f4b422d67042',
|
||||||
|
'2a292df7e32cababbd9de088d1d1abec9fc0440f637ed2fba145094dc14bea08',
|
||||||
|
'f4a9e534fc0d216c44b218fa0c42d99635a0127ee2e53c712f70609649fdff22',
|
||||||
|
'8268436f8c4126196cf64b3c7ddbda90746a378625f9813dd9b8457077256731',
|
||||||
|
'2810e5cbc2cc4d4eece54f61c6f69758e289aa7ab440b3cbeaa21995c2f4232b',
|
||||||
|
// These are all bad because they give a negative xy value.
|
||||||
|
'3eb858e78f5a7254d8c9731174a94f76755fd3941c0ac93735c07ba14579630e',
|
||||||
|
'a45fdc55c76448c049a1ab33f17023edfb2be3581e9c7aade8a6125215e04220',
|
||||||
|
'd483fe813c6ba647ebbfd3ec41adca1c6130c2beeee9d9bf065c8d151c5f396e',
|
||||||
|
'8a2e1d30050198c65a54483123960ccc38aef6848e1ec8f5f780e8523769ba32',
|
||||||
|
'32888462f8b486c68ad7dd9610be5192bbeaf3b443951ac1a8118419d9fa097b',
|
||||||
|
'227142501b9d4355ccba290404bde41575b037693cef1f438c47f8fbf35d1165',
|
||||||
|
'5c37cc491da847cfeb9281d407efc41e15144c876e0170b499a96a22ed31e01e',
|
||||||
|
'445425117cb8c90edcbc7c1cc0e74f747f2c1efa5630a967c64f287792a48a4b',
|
||||||
|
// This is s = -1, which causes y = 0.
|
||||||
|
'ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f',
|
||||||
|
];
|
||||||
|
for (const badBytes of badEncodings) {
|
||||||
|
const b = hexToBytes(badBytes);
|
||||||
|
throws(() => RistrettoPoint.fromHex(b), badBytes);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
should('create right points from uniform hash', () => {
|
||||||
|
const labels = [
|
||||||
|
'Ristretto is traditionally a short shot of espresso coffee',
|
||||||
|
'made with the normal amount of ground coffee but extracted with',
|
||||||
|
'about half the amount of water in the same amount of time',
|
||||||
|
'by using a finer grind.',
|
||||||
|
'This produces a concentrated shot of coffee per volume.',
|
||||||
|
'Just pulling a normal shot short will produce a weaker shot',
|
||||||
|
'and is not a Ristretto as some believe.',
|
||||||
|
];
|
||||||
|
const encodedHashToPoints = [
|
||||||
|
'3066f82a1a747d45120d1740f14358531a8f04bbffe6a819f86dfe50f44a0a46',
|
||||||
|
'f26e5b6f7d362d2d2a94c5d0e7602cb4773c95a2e5c31a64f133189fa76ed61b',
|
||||||
|
'006ccd2a9e6867e6a2c5cea83d3302cc9de128dd2a9a57dd8ee7b9d7ffe02826',
|
||||||
|
'f8f0c87cf237953c5890aec3998169005dae3eca1fbb04548c635953c817f92a',
|
||||||
|
'ae81e7dedf20a497e10c304a765c1767a42d6e06029758d2d7e8ef7cc4c41179',
|
||||||
|
'e2705652ff9f5e44d3e841bf1c251cf7dddb77d140870d1ab2ed64f1a9ce8628',
|
||||||
|
'80bd07262511cdde4863f8a7434cef696750681cb9510eea557088f76d9e5065',
|
||||||
|
];
|
||||||
|
|
||||||
|
for (let i = 0; i < labels.length; i++) {
|
||||||
|
const hash = sha512(utf8ToBytes(labels[i]));
|
||||||
|
const point = RistrettoPoint.hashToCurve(hash);
|
||||||
|
deepStrictEqual(point.toHex(), encodedHashToPoints[i]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ESM is broken.
|
||||||
|
import url from 'url';
|
||||||
|
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
|
||||||
|
should.run();
|
||||||
|
}
|
||||||
1
test/ed25519.helpers.js
Normal file
1
test/ed25519.helpers.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { ed25519, ED25519_TORSION_SUBGROUP } from '../esm/ed25519.js';
|
||||||
@@ -1,38 +1,37 @@
|
|||||||
import { deepStrictEqual, throws } from 'assert';
|
import { deepStrictEqual, strictEqual, throws } from 'assert';
|
||||||
import { should } from 'micro-should';
|
|
||||||
import * as fc from 'fast-check';
|
|
||||||
import { ed25519, ed25519ctx, ed25519ph, x25519, RistrettoPoint } from '../lib/esm/ed25519.js';
|
|
||||||
import { readFileSync } from 'fs';
|
import { readFileSync } from 'fs';
|
||||||
import { default as zip215 } from './ed25519/zip215.json' assert { type: 'json' };
|
|
||||||
import { hexToBytes, bytesToHex, randomBytes } from '@noble/hashes/utils';
|
import { hexToBytes, bytesToHex, randomBytes } from '@noble/hashes/utils';
|
||||||
import { numberToBytesLE } from '../lib/esm/abstract/utils.js';
|
import * as fc from 'fast-check';
|
||||||
import { sha512 } from '@noble/hashes/sha512';
|
import { describe, should } from 'micro-should';
|
||||||
|
import { ed25519, ED25519_TORSION_SUBGROUP } from './ed25519.helpers.js';
|
||||||
import { default as ed25519vectors } from './wycheproof/eddsa_test.json' assert { type: 'json' };
|
import { default as ed25519vectors } from './wycheproof/eddsa_test.json' assert { type: 'json' };
|
||||||
import { default as x25519vectors } from './wycheproof/x25519_test.json' assert { type: 'json' };
|
import { default as zip215 } from './ed25519/zip215.json' assert { type: 'json' };
|
||||||
|
|
||||||
const ed = ed25519;
|
describe('ed25519', () => {
|
||||||
const hex = bytesToHex;
|
const ed = ed25519;
|
||||||
|
const hex = bytesToHex;
|
||||||
|
const Point = ed.ExtendedPoint;
|
||||||
|
|
||||||
function to32Bytes(numOrStr) {
|
function to32Bytes(numOrStr) {
|
||||||
let hex = typeof numOrStr === 'string' ? numOrStr : numOrStr.toString(16);
|
let hex = typeof numOrStr === 'string' ? numOrStr : numOrStr.toString(16);
|
||||||
return hexToBytes(hex.padStart(64, '0'));
|
return hexToBytes(hex.padStart(64, '0'));
|
||||||
}
|
}
|
||||||
|
|
||||||
function utf8ToBytes(str) {
|
function utf8ToBytes(str) {
|
||||||
if (typeof str !== 'string') {
|
if (typeof str !== 'string') {
|
||||||
throw new TypeError(`utf8ToBytes expected string, got ${typeof str}`);
|
throw new Error(`utf8ToBytes expected string, got ${typeof str}`);
|
||||||
}
|
}
|
||||||
return new TextEncoder().encode(str);
|
return new TextEncoder().encode(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
ed.utils.precompute(8);
|
ed.utils.precompute(8);
|
||||||
|
|
||||||
should('ed25519/should not accept >32byte private keys', () => {
|
should('not accept >32byte private keys', () => {
|
||||||
const invalidPriv =
|
const invalidPriv =
|
||||||
100000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800073278156000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000n;
|
100000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800073278156000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000n;
|
||||||
throws(() => ed.getPublicKey(invalidPriv));
|
throws(() => ed.getPublicKey(invalidPriv));
|
||||||
});
|
});
|
||||||
should('ed25519/should verify recent signature', () => {
|
should('verify recent signature', () => {
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(
|
fc.property(
|
||||||
fc.hexaString({ minLength: 2, maxLength: 32 }),
|
fc.hexaString({ minLength: 2, maxLength: 32 }),
|
||||||
@@ -47,8 +46,8 @@ should('ed25519/should verify recent signature', () => {
|
|||||||
),
|
),
|
||||||
{ numRuns: 5 }
|
{ numRuns: 5 }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
should('ed25519/should not verify signature with wrong message', () => {
|
should('not verify signature with wrong message', () => {
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(
|
fc.property(
|
||||||
fc.array(fc.integer({ min: 0x00, max: 0xff })),
|
fc.array(fc.integer({ min: 0x00, max: 0xff })),
|
||||||
@@ -68,101 +67,84 @@ should('ed25519/should not verify signature with wrong message', () => {
|
|||||||
),
|
),
|
||||||
{ numRuns: 5 }
|
{ numRuns: 5 }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
const privKey = to32Bytes('a665a45920422f9d417e4867ef');
|
const privKey = to32Bytes('a665a45920422f9d417e4867ef');
|
||||||
const msg = hexToBytes('874f9960c5d2b7a9b5fad383e1ba44719ebb743a');
|
const wrongPriv = to32Bytes('a675a45920422f9d417e4867ef');
|
||||||
const wrongMsg = hexToBytes('589d8c7f1da0a24bc07b7381ad48b1cfc211af1c');
|
const msg = hexToBytes('874f9960c5d2b7a9b5fad383e1ba44719ebb743a');
|
||||||
should('ed25519/basic methods/should sign and verify', () => {
|
const wrongMsg = hexToBytes('589d8c7f1da0a24bc07b7381ad48b1cfc211af1c');
|
||||||
|
describe('basic methods', () => {
|
||||||
|
should('sign and verify', () => {
|
||||||
const publicKey = ed.getPublicKey(privKey);
|
const publicKey = ed.getPublicKey(privKey);
|
||||||
const signature = ed.sign(msg, privKey);
|
const signature = ed.sign(msg, privKey);
|
||||||
deepStrictEqual(ed.verify(signature, msg, publicKey), true);
|
deepStrictEqual(ed.verify(signature, msg, publicKey), true);
|
||||||
});
|
});
|
||||||
should('ed25519/basic methods/should not verify signature with wrong public key', () => {
|
});
|
||||||
const publicKey = ed.getPublicKey(12);
|
describe('sync methods', () => {
|
||||||
const signature = ed.sign(msg, privKey);
|
should('sign and verify', () => {
|
||||||
deepStrictEqual(ed.verify(signature, msg, publicKey), false);
|
|
||||||
});
|
|
||||||
should('ed25519/basic methods/should not verify signature with wrong hash', () => {
|
|
||||||
const publicKey = ed.getPublicKey(privKey);
|
|
||||||
const signature = ed.sign(msg, privKey);
|
|
||||||
deepStrictEqual(ed.verify(signature, wrongMsg, publicKey), false);
|
|
||||||
});
|
|
||||||
|
|
||||||
should('ed25519/sync methods/should sign and verify', () => {
|
|
||||||
const publicKey = ed.getPublicKey(privKey);
|
const publicKey = ed.getPublicKey(privKey);
|
||||||
const signature = ed.sign(msg, privKey);
|
const signature = ed.sign(msg, privKey);
|
||||||
deepStrictEqual(ed.verify(signature, msg, publicKey), true);
|
deepStrictEqual(ed.verify(signature, msg, publicKey), true);
|
||||||
});
|
});
|
||||||
should('ed25519/sync methods/should not verify signature with wrong public key', () => {
|
should('not verify signature with wrong public key', () => {
|
||||||
const publicKey = ed.getPublicKey(12);
|
const publicKey = ed.getPublicKey(wrongPriv);
|
||||||
const signature = ed.sign(msg, privKey);
|
const signature = ed.sign(msg, privKey);
|
||||||
deepStrictEqual(ed.verify(signature, msg, publicKey), false);
|
deepStrictEqual(ed.verify(signature, msg, publicKey), false);
|
||||||
});
|
});
|
||||||
should('ed25519/sync methods/should not verify signature with wrong hash', () => {
|
should('not verify signature with wrong hash', () => {
|
||||||
const publicKey = ed.getPublicKey(privKey);
|
const publicKey = ed.getPublicKey(privKey);
|
||||||
const signature = ed.sign(msg, privKey);
|
const signature = ed.sign(msg, privKey);
|
||||||
deepStrictEqual(ed.verify(signature, wrongMsg, publicKey), false);
|
deepStrictEqual(ed.verify(signature, wrongMsg, publicKey), false);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
// https://xmr.llcoins.net/addresstests.html
|
describe('BASE_POINT.multiply()', () => {
|
||||||
should(
|
// https://xmr.llcoins.net/addresstests.html
|
||||||
'ed25519/BASE_POINT.multiply()/should create right publicKey without SHA-512 hashing TEST 1',
|
should('create right publicKey without SHA-512 hashing TEST 1', () => {
|
||||||
() => {
|
|
||||||
const publicKey =
|
const publicKey =
|
||||||
ed.Point.BASE.multiply(0x90af56259a4b6bfbc4337980d5d75fbe3c074630368ff3804d33028e5dbfa77n);
|
Point.BASE.multiply(0x90af56259a4b6bfbc4337980d5d75fbe3c074630368ff3804d33028e5dbfa77n);
|
||||||
deepStrictEqual(
|
deepStrictEqual(
|
||||||
publicKey.toHex(),
|
publicKey.toHex(),
|
||||||
'0f3b913371411b27e646b537e888f685bf929ea7aab93c950ed84433f064480d'
|
'0f3b913371411b27e646b537e888f685bf929ea7aab93c950ed84433f064480d'
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
);
|
should('create right publicKey without SHA-512 hashing TEST 2', () => {
|
||||||
should(
|
|
||||||
'ed25519/BASE_POINT.multiply()/should create right publicKey without SHA-512 hashing TEST 2',
|
|
||||||
() => {
|
|
||||||
const publicKey =
|
const publicKey =
|
||||||
ed.Point.BASE.multiply(0x364e8711a60780382a5d57b061c126f039940f28a9e91fe039d4d3094d8b88n);
|
Point.BASE.multiply(0x364e8711a60780382a5d57b061c126f039940f28a9e91fe039d4d3094d8b88n);
|
||||||
deepStrictEqual(
|
deepStrictEqual(
|
||||||
publicKey.toHex(),
|
publicKey.toHex(),
|
||||||
'ad545340b58610f0cd62f17d55af1ab11ecde9c084d5476865ddb4dbda015349'
|
'ad545340b58610f0cd62f17d55af1ab11ecde9c084d5476865ddb4dbda015349'
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
);
|
should('create right publicKey without SHA-512 hashing TEST 3', () => {
|
||||||
should(
|
|
||||||
'ed25519/BASE_POINT.multiply()/should create right publicKey without SHA-512 hashing TEST 3',
|
|
||||||
() => {
|
|
||||||
const publicKey =
|
const publicKey =
|
||||||
ed.Point.BASE.multiply(0xb9bf90ff3abec042752cac3a07a62f0c16cfb9d32a3fc2305d676ec2d86e941n);
|
Point.BASE.multiply(0xb9bf90ff3abec042752cac3a07a62f0c16cfb9d32a3fc2305d676ec2d86e941n);
|
||||||
deepStrictEqual(
|
deepStrictEqual(
|
||||||
publicKey.toHex(),
|
publicKey.toHex(),
|
||||||
'e097c4415fe85724d522b2e449e8fd78dd40d20097bdc9ae36fe8ec6fe12cb8c'
|
'e097c4415fe85724d522b2e449e8fd78dd40d20097bdc9ae36fe8ec6fe12cb8c'
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
);
|
should('create right publicKey without SHA-512 hashing TEST 4', () => {
|
||||||
should(
|
|
||||||
'ed25519/BASE_POINT.multiply()/should create right publicKey without SHA-512 hashing TEST 4',
|
|
||||||
() => {
|
|
||||||
const publicKey =
|
const publicKey =
|
||||||
ed.Point.BASE.multiply(0x69d896f02d79524c9878e080308180e2859d07f9f54454e0800e8db0847a46en);
|
Point.BASE.multiply(0x69d896f02d79524c9878e080308180e2859d07f9f54454e0800e8db0847a46en);
|
||||||
deepStrictEqual(
|
deepStrictEqual(
|
||||||
publicKey.toHex(),
|
publicKey.toHex(),
|
||||||
'f12cb7c43b59971395926f278ce7c2eaded9444fbce62ca717564cb508a0db1d'
|
'f12cb7c43b59971395926f278ce7c2eaded9444fbce62ca717564cb508a0db1d'
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
);
|
should('throw Point#multiply on TEST 5', () => {
|
||||||
should('ed25519/BASE_POINT.multiply()/should throw Point#multiply on TEST 5', () => {
|
|
||||||
for (const num of [0n, 0, -1n, -1, 1.1]) {
|
for (const num of [0n, 0, -1n, -1, 1.1]) {
|
||||||
throws(() => ed.Point.BASE.multiply(num));
|
throws(() => Point.BASE.multiply(num));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// https://ed25519.cr.yp.to/python/sign.py
|
// https://ed25519.cr.yp.to/python/sign.py
|
||||||
// https://ed25519.cr.yp.to/python/sign.input
|
// https://ed25519.cr.yp.to/python/sign.input
|
||||||
const data = readFileSync('./test/ed25519/vectors.txt', 'utf-8');
|
const data = readFileSync('./test/ed25519/vectors.txt', 'utf-8');
|
||||||
const vectors = data
|
const vectors = data
|
||||||
.trim()
|
.trim()
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.map((line) => line.split(':'));
|
.map((line) => line.split(':'));
|
||||||
should('ed25519 official vectors/should match 1024 official vectors', () => {
|
should('ed25519 official vectors/should match 1024 official vectors', () => {
|
||||||
for (let i = 0; i < vectors.length; i++) {
|
for (let i = 0; i < vectors.length; i++) {
|
||||||
const vector = vectors[i];
|
const vector = vectors[i];
|
||||||
// Extract.
|
// Extract.
|
||||||
@@ -174,17 +156,17 @@ should('ed25519 official vectors/should match 1024 official vectors', () => {
|
|||||||
// Calculate
|
// Calculate
|
||||||
const pub = ed.getPublicKey(to32Bytes(priv));
|
const pub = ed.getPublicKey(to32Bytes(priv));
|
||||||
deepStrictEqual(hex(pub), expectedPub);
|
deepStrictEqual(hex(pub), expectedPub);
|
||||||
deepStrictEqual(pub, ed.Point.fromHex(pub).toRawBytes());
|
deepStrictEqual(pub, Point.fromHex(pub).toRawBytes());
|
||||||
|
|
||||||
const signature = hex(ed.sign(msg, priv));
|
const signature = hex(ed.sign(msg, priv));
|
||||||
// console.log('vector', i);
|
// console.log('vector', i);
|
||||||
// expect(pub).toBe(expectedPub);
|
// expect(pub).toBe(expectedPub);
|
||||||
deepStrictEqual(signature, expectedSignature);
|
deepStrictEqual(signature, expectedSignature);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// https://tools.ietf.org/html/rfc8032#section-7
|
// https://tools.ietf.org/html/rfc8032#section-7
|
||||||
should('rfc8032 vectors/should create right signature for 0x9d and empty string', () => {
|
should('rfc8032 vectors/should create right signature for 0x9d and empty string', () => {
|
||||||
const privateKey = '9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60';
|
const privateKey = '9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60';
|
||||||
const publicKey = ed.getPublicKey(privateKey);
|
const publicKey = ed.getPublicKey(privateKey);
|
||||||
const message = '';
|
const message = '';
|
||||||
@@ -197,8 +179,8 @@ should('rfc8032 vectors/should create right signature for 0x9d and empty string'
|
|||||||
hex(signature),
|
hex(signature),
|
||||||
'e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b'
|
'e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
should('rfc8032 vectors/should create right signature for 0x4c and 72', () => {
|
should('rfc8032 vectors/should create right signature for 0x4c and 72', () => {
|
||||||
const privateKey = '4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb';
|
const privateKey = '4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb';
|
||||||
const publicKey = ed.getPublicKey(privateKey);
|
const publicKey = ed.getPublicKey(privateKey);
|
||||||
const message = '72';
|
const message = '72';
|
||||||
@@ -211,8 +193,8 @@ should('rfc8032 vectors/should create right signature for 0x4c and 72', () => {
|
|||||||
hex(signature),
|
hex(signature),
|
||||||
'92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00'
|
'92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
should('rfc8032 vectors/should create right signature for 0x00 and 5a', () => {
|
should('rfc8032 vectors/should create right signature for 0x00 and 5a', () => {
|
||||||
const privateKey = '002fdd1f7641793ab064bb7aa848f762e7ec6e332ffc26eeacda141ae33b1783';
|
const privateKey = '002fdd1f7641793ab064bb7aa848f762e7ec6e332ffc26eeacda141ae33b1783';
|
||||||
const publicKey = ed.getPublicKey(privateKey);
|
const publicKey = ed.getPublicKey(privateKey);
|
||||||
const message =
|
const message =
|
||||||
@@ -226,8 +208,8 @@ should('rfc8032 vectors/should create right signature for 0x00 and 5a', () => {
|
|||||||
hex(signature),
|
hex(signature),
|
||||||
'0df3aa0d0999ad3dc580378f52d152700d5b3b057f56a66f92112e441e1cb9123c66f18712c87efe22d2573777296241216904d7cdd7d5ea433928bd2872fa0c'
|
'0df3aa0d0999ad3dc580378f52d152700d5b3b057f56a66f92112e441e1cb9123c66f18712c87efe22d2573777296241216904d7cdd7d5ea433928bd2872fa0c'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
should('rfc8032 vectors/should create right signature for 0xf5 and long msg', () => {
|
should('rfc8032 vectors/should create right signature for 0xf5 and long msg', () => {
|
||||||
const privateKey = 'f5e5767cf153319517630f226876b86c8160cc583bc013744c6bf255f5cc0ee5';
|
const privateKey = 'f5e5767cf153319517630f226876b86c8160cc583bc013744c6bf255f5cc0ee5';
|
||||||
const publicKey = ed.getPublicKey(privateKey);
|
const publicKey = ed.getPublicKey(privateKey);
|
||||||
const message =
|
const message =
|
||||||
@@ -241,165 +223,67 @@ should('rfc8032 vectors/should create right signature for 0xf5 and long msg', ()
|
|||||||
hex(signature),
|
hex(signature),
|
||||||
'0aab4c900501b3e24d7cdf4663326a3a87df5e4843b2cbdb67cbf6e460fec350aa5371b1508f9f4528ecea23c436d94b5e8fcd4f681e30a6ac00a9704a188a03'
|
'0aab4c900501b3e24d7cdf4663326a3a87df5e4843b2cbdb67cbf6e460fec350aa5371b1508f9f4528ecea23c436d94b5e8fcd4f681e30a6ac00a9704a188a03'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// const PRIVATE_KEY = 0xa665a45920422f9d417e4867efn;
|
// const PRIVATE_KEY = 0xa665a45920422f9d417e4867efn;
|
||||||
// const MESSAGE = ripemd160(new Uint8Array([97, 98, 99, 100, 101, 102, 103]));
|
// const MESSAGE = ripemd160(new Uint8Array([97, 98, 99, 100, 101, 102, 103]));
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
// const MESSAGE = new Uint8Array([
|
// const MESSAGE = new Uint8Array([
|
||||||
// 135, 79, 153, 96, 197, 210, 183, 169, 181, 250, 211, 131, 225, 186, 68, 113, 158, 187, 116, 58,
|
// 135, 79, 153, 96, 197, 210, 183, 169, 181, 250, 211, 131, 225, 186, 68, 113, 158, 187, 116, 58,
|
||||||
// ]);
|
// ]);
|
||||||
// const WRONG_MESSAGE = ripemd160(new Uint8Array([98, 99, 100, 101, 102, 103]));
|
// const WRONG_MESSAGE = ripemd160(new Uint8Array([98, 99, 100, 101, 102, 103]));
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
// const WRONG_MESSAGE = new Uint8Array([
|
// const WRONG_MESSAGE = new Uint8Array([
|
||||||
// 88, 157, 140, 127, 29, 160, 162, 75, 192, 123, 115, 129, 173, 72, 177, 207, 194, 17, 175, 28,
|
// 88, 157, 140, 127, 29, 160, 162, 75, 192, 123, 115, 129, 173, 72, 177, 207, 194, 17, 175, 28,
|
||||||
// ]);
|
// ]);
|
||||||
// // it("should verify just signed message", async () => {
|
// // it("should verify just signed message", async () => {
|
||||||
// // await fc.assert(fc.asyncProperty(
|
// // await fc.assert(fc.asyncProperty(
|
||||||
// // fc.hexa(),
|
// // fc.hexa(),
|
||||||
// // fc.bigInt(2n, ristretto25519.PRIME_ORDER),
|
// // fc.bigInt(2n, ristretto25519.PRIME_ORDER),
|
||||||
// // async (message, privateKey) => {
|
// // async (message, privateKey) => {
|
||||||
// // const publicKey = await ristretto25519.getPublicKey(privateKey);
|
// // const publicKey = await ristretto25519.getPublicKey(privateKey);
|
||||||
// // const signature = await ristretto25519.sign(message, privateKey);
|
// // const signature = await ristretto25519.sign(message, privateKey);
|
||||||
// // expect(publicKey.length).toBe(32);
|
// // expect(publicKey.length).toBe(32);
|
||||||
// // expect(signature.length).toBe(64);
|
// // expect(signature.length).toBe(64);
|
||||||
// // expect(await ristretto25519.verify(signature, message, publicKey)).toBe(true);
|
// // expect(await ristretto25519.verify(signature, message, publicKey)).toBe(true);
|
||||||
// // }),
|
// // }),
|
||||||
// // { numRuns: 1 }
|
// // { numRuns: 1 }
|
||||||
// // );
|
// // );
|
||||||
// // });
|
// // });
|
||||||
// // it("should not verify sign with wrong message", async () => {
|
// // it("should not verify sign with wrong message", async () => {
|
||||||
// // await fc.assert(fc.asyncProperty(
|
// // await fc.assert(fc.asyncProperty(
|
||||||
// // fc.array(fc.integer(0x00, 0xff)),
|
// // fc.array(fc.integer(0x00, 0xff)),
|
||||||
// // fc.array(fc.integer(0x00, 0xff)),
|
// // fc.array(fc.integer(0x00, 0xff)),
|
||||||
// // fc.bigInt(2n, ristretto25519.PRIME_ORDER),
|
// // fc.bigInt(2n, ristretto25519.PRIME_ORDER),
|
||||||
// // async (bytes, wrongBytes, privateKey) => {
|
// // async (bytes, wrongBytes, privateKey) => {
|
||||||
// // const message = new Uint8Array(bytes);
|
// // const message = new Uint8Array(bytes);
|
||||||
// // const wrongMessage = new Uint8Array(wrongBytes);
|
// // const wrongMessage = new Uint8Array(wrongBytes);
|
||||||
// // const publicKey = await ristretto25519.getPublicKey(privateKey);
|
// // const publicKey = await ristretto25519.getPublicKey(privateKey);
|
||||||
// // const signature = await ristretto25519.sign(message, privateKey);
|
// // const signature = await ristretto25519.sign(message, privateKey);
|
||||||
// // expect(await ristretto25519.verify(signature, wrongMessage, publicKey)).toBe(
|
// // expect(await ristretto25519.verify(signature, wrongMessage, publicKey)).toBe(
|
||||||
// // bytes.toString() === wrongBytes.toString()
|
// // bytes.toString() === wrongBytes.toString()
|
||||||
// // );
|
// // );
|
||||||
// // }),
|
// // }),
|
||||||
// // { numRuns: 1 }
|
// // { numRuns: 1 }
|
||||||
// // );
|
// // );
|
||||||
// // });
|
// // });
|
||||||
// // it("should sign and verify", async () => {
|
// // it("should sign and verify", async () => {
|
||||||
// // const publicKey = await ristretto25519.getPublicKey(PRIVATE_KEY);
|
// // const publicKey = await ristretto25519.getPublicKey(PRIVATE_KEY);
|
||||||
// // const signature = await ristretto25519.sign(MESSAGE, PRIVATE_KEY);
|
// // const signature = await ristretto25519.sign(MESSAGE, PRIVATE_KEY);
|
||||||
// // expect(await ristretto25519.verify(signature, MESSAGE, publicKey)).toBe(true);
|
// // expect(await ristretto25519.verify(signature, MESSAGE, publicKey)).toBe(true);
|
||||||
// // });
|
// // });
|
||||||
// // it("should not verify signature with wrong public key", async () => {
|
// // it("should not verify signature with wrong public key", async () => {
|
||||||
// // const publicKey = await ristretto25519.getPublicKey(12);
|
// // const publicKey = await ristretto25519.getPublicKey(12);
|
||||||
// // const signature = await ristretto25519.sign(MESSAGE, PRIVATE_KEY);
|
// // const signature = await ristretto25519.sign(MESSAGE, PRIVATE_KEY);
|
||||||
// // expect(await ristretto25519.verify(signature, MESSAGE, publicKey)).toBe(false);
|
// // expect(await ristretto25519.verify(signature, MESSAGE, publicKey)).toBe(false);
|
||||||
// // });
|
// // });
|
||||||
// // it("should not verify signature with wrong hash", async () => {
|
// // it("should not verify signature with wrong hash", async () => {
|
||||||
// // const publicKey = await ristretto25519.getPublicKey(PRIVATE_KEY);
|
// // const publicKey = await ristretto25519.getPublicKey(PRIVATE_KEY);
|
||||||
// // const signature = await ristretto25519.sign(MESSAGE, PRIVATE_KEY);
|
// // const signature = await ristretto25519.sign(MESSAGE, PRIVATE_KEY);
|
||||||
// // expect(await ristretto25519.verify(signature, WRONG_MESSAGE, publicKey)).toBe(false);
|
// // expect(await ristretto25519.verify(signature, WRONG_MESSAGE, publicKey)).toBe(false);
|
||||||
// // });
|
// // });
|
||||||
should('ristretto255/should follow the byte encodings of small multiples', () => {
|
|
||||||
const encodingsOfSmallMultiples = [
|
|
||||||
// This is the identity point
|
|
||||||
'0000000000000000000000000000000000000000000000000000000000000000',
|
|
||||||
// This is the basepoint
|
|
||||||
'e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76',
|
|
||||||
// These are small multiples of the basepoint
|
|
||||||
'6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919',
|
|
||||||
'94741f5d5d52755ece4f23f044ee27d5d1ea1e2bd196b462166b16152a9d0259',
|
|
||||||
'da80862773358b466ffadfe0b3293ab3d9fd53c5ea6c955358f568322daf6a57',
|
|
||||||
'e882b131016b52c1d3337080187cf768423efccbb517bb495ab812c4160ff44e',
|
|
||||||
'f64746d3c92b13050ed8d80236a7f0007c3b3f962f5ba793d19a601ebb1df403',
|
|
||||||
'44f53520926ec81fbd5a387845beb7df85a96a24ece18738bdcfa6a7822a176d',
|
|
||||||
'903293d8f2287ebe10e2374dc1a53e0bc887e592699f02d077d5263cdd55601c',
|
|
||||||
'02622ace8f7303a31cafc63f8fc48fdc16e1c8c8d234b2f0d6685282a9076031',
|
|
||||||
'20706fd788b2720a1ed2a5dad4952b01f413bcf0e7564de8cdc816689e2db95f',
|
|
||||||
'bce83f8ba5dd2fa572864c24ba1810f9522bc6004afe95877ac73241cafdab42',
|
|
||||||
'e4549ee16b9aa03099ca208c67adafcafa4c3f3e4e5303de6026e3ca8ff84460',
|
|
||||||
'aa52e000df2e16f55fb1032fc33bc42742dad6bd5a8fc0be0167436c5948501f',
|
|
||||||
'46376b80f409b29dc2b5f6f0c52591990896e5716f41477cd30085ab7f10301e',
|
|
||||||
'e0c418f7c8d9c4cdd7395b93ea124f3ad99021bb681dfc3302a9d99a2e53e64e',
|
|
||||||
];
|
|
||||||
let B = RistrettoPoint.BASE;
|
|
||||||
let P = RistrettoPoint.ZERO;
|
|
||||||
for (const encoded of encodingsOfSmallMultiples) {
|
|
||||||
deepStrictEqual(P.toHex(), encoded);
|
|
||||||
deepStrictEqual(RistrettoPoint.fromHex(encoded).toHex(), encoded);
|
|
||||||
P = P.add(B);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
should('ristretto255/should not convert bad bytes encoding', () => {
|
|
||||||
const badEncodings = [
|
|
||||||
// These are all bad because they're non-canonical field encodings.
|
|
||||||
'00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
|
|
||||||
'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f',
|
|
||||||
'f3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f',
|
|
||||||
'edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f',
|
|
||||||
// These are all bad because they're negative field elements.
|
|
||||||
'0100000000000000000000000000000000000000000000000000000000000000',
|
|
||||||
'01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f',
|
|
||||||
'ed57ffd8c914fb201471d1c3d245ce3c746fcbe63a3679d51b6a516ebebe0e20',
|
|
||||||
'c34c4e1826e5d403b78e246e88aa051c36ccf0aafebffe137d148a2bf9104562',
|
|
||||||
'c940e5a4404157cfb1628b108db051a8d439e1a421394ec4ebccb9ec92a8ac78',
|
|
||||||
'47cfc5497c53dc8e61c91d17fd626ffb1c49e2bca94eed052281b510b1117a24',
|
|
||||||
'f1c6165d33367351b0da8f6e4511010c68174a03b6581212c71c0e1d026c3c72',
|
|
||||||
'87260f7a2f12495118360f02c26a470f450dadf34a413d21042b43b9d93e1309',
|
|
||||||
// These are all bad because they give a nonsquare x².
|
|
||||||
'26948d35ca62e643e26a83177332e6b6afeb9d08e4268b650f1f5bbd8d81d371',
|
|
||||||
'4eac077a713c57b4f4397629a4145982c661f48044dd3f96427d40b147d9742f',
|
|
||||||
'de6a7b00deadc788eb6b6c8d20c0ae96c2f2019078fa604fee5b87d6e989ad7b',
|
|
||||||
'bcab477be20861e01e4a0e295284146a510150d9817763caf1a6f4b422d67042',
|
|
||||||
'2a292df7e32cababbd9de088d1d1abec9fc0440f637ed2fba145094dc14bea08',
|
|
||||||
'f4a9e534fc0d216c44b218fa0c42d99635a0127ee2e53c712f70609649fdff22',
|
|
||||||
'8268436f8c4126196cf64b3c7ddbda90746a378625f9813dd9b8457077256731',
|
|
||||||
'2810e5cbc2cc4d4eece54f61c6f69758e289aa7ab440b3cbeaa21995c2f4232b',
|
|
||||||
// These are all bad because they give a negative xy value.
|
|
||||||
'3eb858e78f5a7254d8c9731174a94f76755fd3941c0ac93735c07ba14579630e',
|
|
||||||
'a45fdc55c76448c049a1ab33f17023edfb2be3581e9c7aade8a6125215e04220',
|
|
||||||
'd483fe813c6ba647ebbfd3ec41adca1c6130c2beeee9d9bf065c8d151c5f396e',
|
|
||||||
'8a2e1d30050198c65a54483123960ccc38aef6848e1ec8f5f780e8523769ba32',
|
|
||||||
'32888462f8b486c68ad7dd9610be5192bbeaf3b443951ac1a8118419d9fa097b',
|
|
||||||
'227142501b9d4355ccba290404bde41575b037693cef1f438c47f8fbf35d1165',
|
|
||||||
'5c37cc491da847cfeb9281d407efc41e15144c876e0170b499a96a22ed31e01e',
|
|
||||||
'445425117cb8c90edcbc7c1cc0e74f747f2c1efa5630a967c64f287792a48a4b',
|
|
||||||
// This is s = -1, which causes y = 0.
|
|
||||||
'ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f',
|
|
||||||
];
|
|
||||||
for (const badBytes of badEncodings) {
|
|
||||||
const b = hexToBytes(badBytes);
|
|
||||||
throws(() => RistrettoPoint.fromHex(b), badBytes);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
should('ristretto255/should create right points from uniform hash', async () => {
|
|
||||||
const labels = [
|
|
||||||
'Ristretto is traditionally a short shot of espresso coffee',
|
|
||||||
'made with the normal amount of ground coffee but extracted with',
|
|
||||||
'about half the amount of water in the same amount of time',
|
|
||||||
'by using a finer grind.',
|
|
||||||
'This produces a concentrated shot of coffee per volume.',
|
|
||||||
'Just pulling a normal shot short will produce a weaker shot',
|
|
||||||
'and is not a Ristretto as some believe.',
|
|
||||||
];
|
|
||||||
const encodedHashToPoints = [
|
|
||||||
'3066f82a1a747d45120d1740f14358531a8f04bbffe6a819f86dfe50f44a0a46',
|
|
||||||
'f26e5b6f7d362d2d2a94c5d0e7602cb4773c95a2e5c31a64f133189fa76ed61b',
|
|
||||||
'006ccd2a9e6867e6a2c5cea83d3302cc9de128dd2a9a57dd8ee7b9d7ffe02826',
|
|
||||||
'f8f0c87cf237953c5890aec3998169005dae3eca1fbb04548c635953c817f92a',
|
|
||||||
'ae81e7dedf20a497e10c304a765c1767a42d6e06029758d2d7e8ef7cc4c41179',
|
|
||||||
'e2705652ff9f5e44d3e841bf1c251cf7dddb77d140870d1ab2ed64f1a9ce8628',
|
|
||||||
'80bd07262511cdde4863f8a7434cef696750681cb9510eea557088f76d9e5065',
|
|
||||||
];
|
|
||||||
|
|
||||||
for (let i = 0; i < labels.length; i++) {
|
should('input immutability: sign/verify are immutable', () => {
|
||||||
const hash = sha512(utf8ToBytes(labels[i]));
|
|
||||||
const point = RistrettoPoint.hashToCurve(hash);
|
|
||||||
deepStrictEqual(point.toHex(), encodedHashToPoints[i]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
should('input immutability: sign/verify are immutable', () => {
|
|
||||||
const privateKey = ed.utils.randomPrivateKey();
|
const privateKey = ed.utils.randomPrivateKey();
|
||||||
const publicKey = ed.getPublicKey(privateKey);
|
const publicKey = ed.getPublicKey(privateKey);
|
||||||
|
|
||||||
@@ -417,11 +301,11 @@ should('input immutability: sign/verify are immutable', () => {
|
|||||||
if (!ed.verify(signatureCopy, payload, publicKey))
|
if (!ed.verify(signatureCopy, payload, publicKey))
|
||||||
throw new Error('Copied signature verification failed');
|
throw new Error('Copied signature verification failed');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// https://zips.z.cash/zip-0215
|
// https://zips.z.cash/zip-0215
|
||||||
// Vectors from https://gist.github.com/hdevalence/93ed42d17ecab8e42138b213812c8cc7
|
// Vectors from https://gist.github.com/hdevalence/93ed42d17ecab8e42138b213812c8cc7
|
||||||
should('ZIP-215 compliance tests/should pass all of them', () => {
|
should('ZIP-215 compliance tests/should pass all of them', () => {
|
||||||
const str = utf8ToBytes('Zcash');
|
const str = utf8ToBytes('Zcash');
|
||||||
for (let v of zip215) {
|
for (let v of zip215) {
|
||||||
let noble = false;
|
let noble = false;
|
||||||
@@ -430,112 +314,39 @@ should('ZIP-215 compliance tests/should pass all of them', () => {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
noble = false;
|
noble = false;
|
||||||
}
|
}
|
||||||
deepStrictEqual(noble, v.valid_zip215);
|
deepStrictEqual(noble, v.valid_zip215, JSON.stringify(v));
|
||||||
}
|
|
||||||
});
|
|
||||||
should('ZIP-215 compliance tests/disallows sig.s >= CURVE.n', () => {
|
|
||||||
const sig = new ed.Signature(ed.Point.BASE, 1n);
|
|
||||||
sig.s = ed.CURVE.n + 1n;
|
|
||||||
throws(() => ed.verify(sig, 'deadbeef', ed.Point.BASE));
|
|
||||||
});
|
|
||||||
|
|
||||||
const rfc7748Mul = [
|
|
||||||
{
|
|
||||||
scalar: 'a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4',
|
|
||||||
u: 'e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c',
|
|
||||||
outputU: 'c3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
scalar: '4b66e9d4d1b4673c5ad22691957d6af5c11b6421e0ea01d42ca4169e7918ba0d',
|
|
||||||
u: 'e5210f12786811d3f4b7959d0538ae2c31dbe7106fc03c3efc4cd549c715a493',
|
|
||||||
outputU: '95cbde9476e8907d7aade45cb4b873f88b595a68799fa152e6f8f7647aac7957',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
for (let i = 0; i < rfc7748Mul.length; i++) {
|
|
||||||
const v = rfc7748Mul[i];
|
|
||||||
should(`RFC7748: scalarMult (${i})`, () => {
|
|
||||||
deepStrictEqual(hex(x25519.scalarMult(v.scalar, v.u)), v.outputU);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const rfc7748Iter = [
|
|
||||||
{ scalar: '422c8e7a6227d7bca1350b3e2bb7279f7897b87bb6854b783c60e80311ae3079', iters: 1 },
|
|
||||||
{ scalar: '684cf59ba83309552800ef566f2f4d3c1c3887c49360e3875f2eb94d99532c51', iters: 1000 },
|
|
||||||
// { scalar: '7c3911e0ab2586fd864497297e575e6f3bc601c0883c30df5f4dd2d24f665424', iters: 1000000 },
|
|
||||||
];
|
|
||||||
for (let i = 0; i < rfc7748Iter.length; i++) {
|
|
||||||
const { scalar, iters } = rfc7748Iter[i];
|
|
||||||
should(`RFC7748: scalarMult iteration (${i})`, () => {
|
|
||||||
let k = x25519.Gu;
|
|
||||||
for (let i = 0, u = k; i < iters; i++) [k, u] = [x25519.scalarMult(k, u), k];
|
|
||||||
deepStrictEqual(hex(k), scalar);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
should('RFC7748 getSharedKey', () => {
|
|
||||||
const alicePrivate = '77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a';
|
|
||||||
const alicePublic = '8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a';
|
|
||||||
const bobPrivate = '5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb';
|
|
||||||
const bobPublic = 'de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f';
|
|
||||||
const shared = '4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742';
|
|
||||||
deepStrictEqual(alicePublic, hex(x25519.getPublicKey(alicePrivate)));
|
|
||||||
deepStrictEqual(bobPublic, hex(x25519.getPublicKey(bobPrivate)));
|
|
||||||
deepStrictEqual(hex(x25519.scalarMult(alicePrivate, bobPublic)), shared);
|
|
||||||
deepStrictEqual(hex(x25519.scalarMult(bobPrivate, alicePublic)), shared);
|
|
||||||
});
|
|
||||||
|
|
||||||
// should('X25519/getSharedSecret() should be commutative', () => {
|
|
||||||
// for (let i = 0; i < 512; i++) {
|
|
||||||
// const asec = ed.utils.randomPrivateKey();
|
|
||||||
// const apub = ed.getPublicKey(asec);
|
|
||||||
// const bsec = ed.utils.randomPrivateKey();
|
|
||||||
// const bpub = ed.getPublicKey(bsec);
|
|
||||||
// try {
|
|
||||||
// deepStrictEqual(ed.getSharedSecret(asec, bpub), ed.getSharedSecret(bsec, apub));
|
|
||||||
// } catch (error) {
|
|
||||||
// console.error('not commutative', { asec, apub, bsec, bpub });
|
|
||||||
// throw error;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
// should('X25519: should convert base point to montgomery using fromPoint', () => {
|
|
||||||
// deepStrictEqual(
|
|
||||||
// hex(ed.montgomeryCurve.UfromPoint(ed.Point.BASE)),
|
|
||||||
// ed.montgomeryCurve.BASE_POINT_U
|
|
||||||
// );
|
|
||||||
// });
|
|
||||||
|
|
||||||
{
|
|
||||||
const group = x25519vectors.testGroups[0];
|
|
||||||
should(`Wycheproof/X25519`, () => {
|
|
||||||
for (let i = 0; i < group.tests.length; i++) {
|
|
||||||
const v = group.tests[i];
|
|
||||||
const comment = `(${i}, ${v.result}) ${v.comment}`;
|
|
||||||
if (v.result === 'valid' || v.result === 'acceptable') {
|
|
||||||
try {
|
|
||||||
const shared = hex(x25519.scalarMult(v.private, v.public));
|
|
||||||
deepStrictEqual(shared, v.shared, comment);
|
|
||||||
} catch (e) {
|
|
||||||
// We are more strict
|
|
||||||
if (e.message.includes('Expected valid scalar')) return;
|
|
||||||
if (e.message.includes('Invalid private or public key received')) return;
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
} else if (v.result === 'invalid') {
|
|
||||||
let failed = false;
|
|
||||||
try {
|
|
||||||
x25519.scalarMult(v.private, v.public);
|
|
||||||
} catch (error) {
|
|
||||||
failed = true;
|
|
||||||
}
|
|
||||||
deepStrictEqual(failed, true, comment);
|
|
||||||
} else throw new Error('unknown test result');
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
should('ZIP-215 compliance tests/disallows sig.s >= CURVE.n', () => {
|
||||||
|
// sig.R = BASE, sig.s = N+1
|
||||||
|
const sig =
|
||||||
|
'5866666666666666666666666666666666666666666666666666666666666666eed3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010';
|
||||||
|
throws(() => ed.verify(sig, 'deadbeef', Point.BASE));
|
||||||
|
});
|
||||||
|
|
||||||
should(`Wycheproof/ED25519`, () => {
|
// should('X25519/getSharedSecret() should be commutative', () => {
|
||||||
|
// for (let i = 0; i < 512; i++) {
|
||||||
|
// const asec = ed.utils.randomPrivateKey();
|
||||||
|
// const apub = ed.getPublicKey(asec);
|
||||||
|
// const bsec = ed.utils.randomPrivateKey();
|
||||||
|
// const bpub = ed.getPublicKey(bsec);
|
||||||
|
// try {
|
||||||
|
// deepStrictEqual(ed.getSharedSecret(asec, bpub), ed.getSharedSecret(bsec, apub));
|
||||||
|
// } catch (error) {
|
||||||
|
// console.error('not commutative', { asec, apub, bsec, bpub });
|
||||||
|
// throw error;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
// should('X25519: should convert base point to montgomery using fromPoint', () => {
|
||||||
|
// deepStrictEqual(
|
||||||
|
// hex(ed.montgomeryCurve.UfromPoint(Point.BASE)),
|
||||||
|
// ed.montgomeryCurve.BASE_POINT_U
|
||||||
|
// );
|
||||||
|
// });
|
||||||
|
|
||||||
|
should(`Wycheproof/ED25519`, () => {
|
||||||
for (let g = 0; g < ed25519vectors.testGroups.length; g++) {
|
for (let g = 0; g < ed25519vectors.testGroups.length; g++) {
|
||||||
const group = ed25519vectors.testGroups[g];
|
const group = ed25519vectors.testGroups[g];
|
||||||
const key = group.key;
|
const key = group.key;
|
||||||
@@ -557,97 +368,34 @@ should(`Wycheproof/ED25519`, () => {
|
|||||||
} else throw new Error('unknown test result');
|
} else throw new Error('unknown test result');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
should('Property test issue #1', () => {
|
should('Property test issue #1', () => {
|
||||||
const message = new Uint8Array([12, 12, 12]);
|
const message = new Uint8Array([12, 12, 12]);
|
||||||
const signature = ed.sign(message, to32Bytes(1n));
|
const signature = ed.sign(message, to32Bytes(1n));
|
||||||
const publicKey = ed.getPublicKey(to32Bytes(1n)); // <- was 1n
|
const publicKey = ed.getPublicKey(to32Bytes(1n)); // <- was 1n
|
||||||
deepStrictEqual(ed.verify(signature, message, publicKey), true);
|
deepStrictEqual(ed.verify(signature, message, publicKey), true);
|
||||||
|
});
|
||||||
|
|
||||||
|
should('isTorsionFree()', () => {
|
||||||
|
const orig = ed.utils.getExtendedPublicKey(ed.utils.randomPrivateKey()).point;
|
||||||
|
for (const hex of ED25519_TORSION_SUBGROUP.slice(1)) {
|
||||||
|
const dirty = orig.add(Point.fromHex(hex));
|
||||||
|
const cleared = dirty.clearCofactor();
|
||||||
|
strictEqual(orig.isTorsionFree(), true, `orig must be torsionFree: ${hex}`);
|
||||||
|
strictEqual(dirty.isTorsionFree(), false, `dirty must not be torsionFree: ${hex}`);
|
||||||
|
strictEqual(cleared.isTorsionFree(), true, `cleared must be torsionFree: ${hex}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const VECTORS_RFC8032_CTX = [
|
should('ed25519 bug', () => {
|
||||||
{
|
const t = 81718630521762619991978402609047527194981150691135404693881672112315521837062n;
|
||||||
secretKey: '0305334e381af78f141cb666f6199f57bc3495335a256a95bd2a55bf546663f6',
|
const point = ed25519.ExtendedPoint.fromAffine({ x: t, y: t });
|
||||||
publicKey: 'dfc9425e4f968f7f0c29f0259cf5f9aed6851c2bb4ad8bfb860cfee0ab248292',
|
throws(() => point.assertValidity());
|
||||||
message: 'f726936d19c800494e3fdaff20b276a8',
|
// Otherwise (without assertValidity):
|
||||||
context: '666f6f',
|
// const point2 = point.double();
|
||||||
signature:
|
// point2.toAffine(); // crash!
|
||||||
'55a4cc2f70a54e04288c5f4cd1e45a7b' +
|
|
||||||
'b520b36292911876cada7323198dd87a' +
|
|
||||||
'8b36950b95130022907a7fb7c4e9b2d5' +
|
|
||||||
'f6cca685a587b4b21f4b888e4e7edb0d',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
secretKey: '0305334e381af78f141cb666f6199f57bc3495335a256a95bd2a55bf546663f6',
|
|
||||||
publicKey: 'dfc9425e4f968f7f0c29f0259cf5f9aed6851c2bb4ad8bfb860cfee0ab248292',
|
|
||||||
message: 'f726936d19c800494e3fdaff20b276a8',
|
|
||||||
context: '626172',
|
|
||||||
signature:
|
|
||||||
'fc60d5872fc46b3aa69f8b5b4351d580' +
|
|
||||||
'8f92bcc044606db097abab6dbcb1aee3' +
|
|
||||||
'216c48e8b3b66431b5b186d1d28f8ee1' +
|
|
||||||
'5a5ca2df6668346291c2043d4eb3e90d',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
secretKey: '0305334e381af78f141cb666f6199f57bc3495335a256a95bd2a55bf546663f6',
|
|
||||||
publicKey: 'dfc9425e4f968f7f0c29f0259cf5f9aed6851c2bb4ad8bfb860cfee0ab248292',
|
|
||||||
message: '508e9e6882b979fea900f62adceaca35',
|
|
||||||
context: '666f6f',
|
|
||||||
signature:
|
|
||||||
'8b70c1cc8310e1de20ac53ce28ae6e72' +
|
|
||||||
'07f33c3295e03bb5c0732a1d20dc6490' +
|
|
||||||
'8922a8b052cf99b7c4fe107a5abb5b2c' +
|
|
||||||
'4085ae75890d02df26269d8945f84b0b',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
secretKey: 'ab9c2853ce297ddab85c993b3ae14bcad39b2c682beabc27d6d4eb20711d6560',
|
|
||||||
publicKey: '0f1d1274943b91415889152e893d80e93275a1fc0b65fd71b4b0dda10ad7d772',
|
|
||||||
message: 'f726936d19c800494e3fdaff20b276a8',
|
|
||||||
context: '666f6f',
|
|
||||||
signature:
|
|
||||||
'21655b5f1aa965996b3f97b3c849eafb' +
|
|
||||||
'a922a0a62992f73b3d1b73106a84ad85' +
|
|
||||||
'e9b86a7b6005ea868337ff2d20a7f5fb' +
|
|
||||||
'd4cd10b0be49a68da2b2e0dc0ad8960f',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
for (let i = 0; i < VECTORS_RFC8032_CTX.length; i++) {
|
|
||||||
const v = VECTORS_RFC8032_CTX[i];
|
|
||||||
should(`RFC8032ctx/${i}`, () => {
|
|
||||||
deepStrictEqual(hex(ed25519ctx.getPublicKey(v.secretKey)), v.publicKey);
|
|
||||||
deepStrictEqual(hex(ed25519ctx.sign(v.message, v.secretKey, v.context)), v.signature);
|
|
||||||
deepStrictEqual(ed25519ctx.verify(v.signature, v.message, v.publicKey, v.context), true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const VECTORS_RFC8032_PH = [
|
|
||||||
{
|
|
||||||
secretKey: '833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42',
|
|
||||||
publicKey: 'ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf',
|
|
||||||
message: '616263',
|
|
||||||
signature:
|
|
||||||
'98a70222f0b8121aa9d30f813d683f80' +
|
|
||||||
'9e462b469c7ff87639499bb94e6dae41' +
|
|
||||||
'31f85042463c2a355a2003d062adf5aa' +
|
|
||||||
'a10b8c61e636062aaad11c2a26083406',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
for (let i = 0; i < VECTORS_RFC8032_PH.length; i++) {
|
|
||||||
const v = VECTORS_RFC8032_PH[i];
|
|
||||||
should(`RFC8032ph/${i}`, () => {
|
|
||||||
deepStrictEqual(hex(ed25519ph.getPublicKey(v.secretKey)), v.publicKey);
|
|
||||||
deepStrictEqual(hex(ed25519ph.sign(v.message, v.secretKey)), v.signature);
|
|
||||||
deepStrictEqual(ed25519ph.verify(v.signature, v.message, v.publicKey), true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
should('X25519 base point', () => {
|
|
||||||
const { y } = ed25519.Point.BASE;
|
|
||||||
const u = ed25519.utils.mod((y + 1n) * ed25519.utils.invert(1n - y, ed25519.CURVE.P));
|
|
||||||
deepStrictEqual(hex(numberToBytesLE(u, 32)), x25519.Gu);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// ESM is broken.
|
// ESM is broken.
|
||||||
|
|||||||
@@ -1,18 +1,20 @@
|
|||||||
import { deepStrictEqual, throws } from 'assert';
|
import { deepStrictEqual, throws } from 'assert';
|
||||||
import { should } from 'micro-should';
|
import { describe, should } from 'micro-should';
|
||||||
import * as fc from 'fast-check';
|
import * as fc from 'fast-check';
|
||||||
import { ed448, ed448ph, x448 } from '../lib/esm/ed448.js';
|
import { ed448, ed448ph, x448 } from '../esm/ed448.js';
|
||||||
import { hexToBytes, bytesToHex, randomBytes } from '@noble/hashes/utils';
|
import { hexToBytes, bytesToHex, randomBytes } from '@noble/hashes/utils';
|
||||||
import { numberToBytesLE } from '../lib/esm/abstract/utils.js';
|
import { numberToBytesLE } from '../esm/abstract/utils.js';
|
||||||
import { default as ed448vectors } from './wycheproof/ed448_test.json' assert { type: 'json' };
|
import { default as ed448vectors } from './wycheproof/ed448_test.json' assert { type: 'json' };
|
||||||
import { default as x448vectors } from './wycheproof/x448_test.json' assert { type: 'json' };
|
import { default as x448vectors } from './wycheproof/x448_test.json' assert { type: 'json' };
|
||||||
|
|
||||||
const ed = ed448;
|
describe('ed448', () => {
|
||||||
const hex = bytesToHex;
|
const ed = ed448;
|
||||||
ed.utils.precompute(4);
|
const hex = bytesToHex;
|
||||||
|
ed.utils.precompute(4);
|
||||||
|
const Point = ed.ExtendedPoint;
|
||||||
|
|
||||||
should(`Basic`, () => {
|
should(`Basic`, () => {
|
||||||
const G1 = ed.Point.BASE;
|
const G1 = Point.BASE.toAffine();
|
||||||
deepStrictEqual(
|
deepStrictEqual(
|
||||||
G1.x,
|
G1.x,
|
||||||
224580040295924300187604334099896036246789641632564134246125461686950415467406032909029192869357953282578032075146446173674602635247710n
|
224580040295924300187604334099896036246789641632564134246125461686950415467406032909029192869357953282578032075146446173674602635247710n
|
||||||
@@ -21,7 +23,7 @@ should(`Basic`, () => {
|
|||||||
G1.y,
|
G1.y,
|
||||||
298819210078481492676017930443930673437544040154080242095928241372331506189835876003536878655418784733982303233503462500531545062832660n
|
298819210078481492676017930443930673437544040154080242095928241372331506189835876003536878655418784733982303233503462500531545062832660n
|
||||||
);
|
);
|
||||||
const G2 = ed.Point.BASE.multiply(2n);
|
const G2 = Point.BASE.multiply(2n).toAffine();
|
||||||
deepStrictEqual(
|
deepStrictEqual(
|
||||||
G2.x,
|
G2.x,
|
||||||
484559149530404593699549205258669689569094240458212040187660132787056912146709081364401144455726350866276831544947397859048262938744149n
|
484559149530404593699549205258669689569094240458212040187660132787056912146709081364401144455726350866276831544947397859048262938744149n
|
||||||
@@ -30,7 +32,7 @@ should(`Basic`, () => {
|
|||||||
G2.y,
|
G2.y,
|
||||||
494088759867433727674302672526735089350544552303727723746126484473087719117037293890093462157703888342865036477787453078312060500281069n
|
494088759867433727674302672526735089350544552303727723746126484473087719117037293890093462157703888342865036477787453078312060500281069n
|
||||||
);
|
);
|
||||||
const G3 = ed.Point.BASE.multiply(3n);
|
const G3 = Point.BASE.multiply(3n).toAffine();
|
||||||
deepStrictEqual(
|
deepStrictEqual(
|
||||||
G3.x,
|
G3.x,
|
||||||
23839778817283171003887799738662344287085130522697782688245073320169861206004018274567429238677677920280078599146891901463786155880335n
|
23839778817283171003887799738662344287085130522697782688245073320169861206004018274567429238677677920280078599146891901463786155880335n
|
||||||
@@ -39,18 +41,18 @@ should(`Basic`, () => {
|
|||||||
G3.y,
|
G3.y,
|
||||||
636046652612779686502873775776967954190574036985351036782021535703553242737829645273154208057988851307101009474686328623630835377952508n
|
636046652612779686502873775776967954190574036985351036782021535703553242737829645273154208057988851307101009474686328623630835377952508n
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
should('Basic/decompress', () => {
|
should('Basic/decompress', () => {
|
||||||
const G1 = ed.Point.BASE;
|
const G1 = Point.BASE;
|
||||||
const G2 = ed.Point.BASE.multiply(2n);
|
const G2 = Point.BASE.multiply(2n);
|
||||||
const G3 = ed.Point.BASE.multiply(3n);
|
const G3 = Point.BASE.multiply(3n);
|
||||||
const points = [G1, G2, G3];
|
const points = [G1, G2, G3];
|
||||||
const getXY = (p) => ({ x: p.x, y: p.y });
|
const getXY = (p) => p.toAffine();
|
||||||
for (const p of points) deepStrictEqual(getXY(ed.Point.fromHex(p.toHex())), getXY(p));
|
for (const p of points) deepStrictEqual(getXY(Point.fromHex(p.toHex())), getXY(p));
|
||||||
});
|
});
|
||||||
|
|
||||||
const VECTORS_RFC8032 = [
|
const VECTORS_RFC8032 = [
|
||||||
{
|
{
|
||||||
secretKey:
|
secretKey:
|
||||||
'6c82a562cb808d10d632be89c8513ebf' +
|
'6c82a562cb808d10d632be89c8513ebf' +
|
||||||
@@ -312,29 +314,31 @@ const VECTORS_RFC8032 = [
|
|||||||
'3603ce30d8bb761785dc30dbc320869e' +
|
'3603ce30d8bb761785dc30dbc320869e' +
|
||||||
'1a00',
|
'1a00',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
for (let i = 0; i < VECTORS_RFC8032.length; i++) {
|
describe('RFC8032', () => {
|
||||||
|
for (let i = 0; i < VECTORS_RFC8032.length; i++) {
|
||||||
const v = VECTORS_RFC8032[i];
|
const v = VECTORS_RFC8032[i];
|
||||||
should(`RFC8032/${i}`, () => {
|
should(`${i}`, () => {
|
||||||
deepStrictEqual(hex(ed.getPublicKey(v.secretKey)), v.publicKey);
|
deepStrictEqual(hex(ed.getPublicKey(v.secretKey)), v.publicKey);
|
||||||
deepStrictEqual(hex(ed.sign(v.message, v.secretKey)), v.signature);
|
deepStrictEqual(hex(ed.sign(v.message, v.secretKey)), v.signature);
|
||||||
deepStrictEqual(ed.verify(v.signature, v.message, v.publicKey), true);
|
deepStrictEqual(ed.verify(v.signature, v.message, v.publicKey), true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
should('ed448/should not accept >57byte private keys', async () => {
|
should('not accept >57byte private keys', () => {
|
||||||
const invalidPriv =
|
const invalidPriv =
|
||||||
100000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800073278156000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000n;
|
100000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800073278156000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000n;
|
||||||
throws(() => ed.getPublicKey(invalidPriv));
|
throws(() => ed.getPublicKey(invalidPriv));
|
||||||
});
|
});
|
||||||
|
|
||||||
function to57Bytes(numOrStr) {
|
function to57Bytes(numOrStr) {
|
||||||
let hex = typeof numOrStr === 'string' ? numOrStr : numOrStr.toString(16);
|
let hex = typeof numOrStr === 'string' ? numOrStr : numOrStr.toString(16);
|
||||||
return hexToBytes(hex.padStart(114, '0'));
|
return hexToBytes(hex.padStart(114, '0'));
|
||||||
}
|
}
|
||||||
|
|
||||||
should('ed448/should verify recent signature', () => {
|
should('verify recent signature', () => {
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(
|
fc.property(
|
||||||
fc.hexaString({ minLength: 2, maxLength: 57 }),
|
fc.hexaString({ minLength: 2, maxLength: 57 }),
|
||||||
@@ -349,8 +353,8 @@ should('ed448/should verify recent signature', () => {
|
|||||||
),
|
),
|
||||||
{ numRuns: 5 }
|
{ numRuns: 5 }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
should('ed448/should not verify signature with wrong message', () => {
|
should('not verify signature with wrong message', () => {
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(
|
fc.property(
|
||||||
fc.array(fc.integer({ min: 0x00, max: 0xff })),
|
fc.array(fc.integer({ min: 0x00, max: 0xff })),
|
||||||
@@ -370,49 +374,52 @@ should('ed448/should not verify signature with wrong message', () => {
|
|||||||
),
|
),
|
||||||
{ numRuns: 5 }
|
{ numRuns: 5 }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
const privKey = to57Bytes('a665a45920422f9d417e4867ef');
|
const privKey = to57Bytes('a665a45920422f9d417e4867ef');
|
||||||
const msg = hexToBytes('874f9960c5d2b7a9b5fad383e1ba44719ebb743a');
|
const msg = hexToBytes('874f9960c5d2b7a9b5fad383e1ba44719ebb743a');
|
||||||
const wrongMsg = hexToBytes('589d8c7f1da0a24bc07b7381ad48b1cfc211af1c');
|
const wrongMsg = hexToBytes('589d8c7f1da0a24bc07b7381ad48b1cfc211af1c');
|
||||||
should('ed25519/basic methods/should sign and verify', () => {
|
describe('basic methods', () => {
|
||||||
|
should('sign and verify', () => {
|
||||||
const publicKey = ed.getPublicKey(privKey);
|
const publicKey = ed.getPublicKey(privKey);
|
||||||
const signature = ed.sign(msg, privKey);
|
const signature = ed.sign(msg, privKey);
|
||||||
deepStrictEqual(ed.verify(signature, msg, publicKey), true);
|
deepStrictEqual(ed.verify(signature, msg, publicKey), true);
|
||||||
});
|
});
|
||||||
should('ed25519/basic methods/should not verify signature with wrong public key', () => {
|
should('not verify signature with wrong public key', () => {
|
||||||
const publicKey = ed.getPublicKey(12);
|
const publicKey = ed.getPublicKey(ed.utils.randomPrivateKey());
|
||||||
const signature = ed.sign(msg, privKey);
|
const signature = ed.sign(msg, privKey);
|
||||||
deepStrictEqual(ed.verify(signature, msg, publicKey), false);
|
deepStrictEqual(ed.verify(signature, msg, publicKey), false);
|
||||||
});
|
});
|
||||||
should('ed25519/basic methods/should not verify signature with wrong hash', () => {
|
should('not verify signature with wrong hash', () => {
|
||||||
const publicKey = ed.getPublicKey(privKey);
|
const publicKey = ed.getPublicKey(privKey);
|
||||||
const signature = ed.sign(msg, privKey);
|
const signature = ed.sign(msg, privKey);
|
||||||
deepStrictEqual(ed.verify(signature, wrongMsg, publicKey), false);
|
deepStrictEqual(ed.verify(signature, wrongMsg, publicKey), false);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
should('ed25519/sync methods/should sign and verify', () => {
|
describe('sync methods', () => {
|
||||||
|
should('sign and verify', () => {
|
||||||
const publicKey = ed.getPublicKey(privKey);
|
const publicKey = ed.getPublicKey(privKey);
|
||||||
const signature = ed.sign(msg, privKey);
|
const signature = ed.sign(msg, privKey);
|
||||||
deepStrictEqual(ed.verify(signature, msg, publicKey), true);
|
deepStrictEqual(ed.verify(signature, msg, publicKey), true);
|
||||||
});
|
});
|
||||||
should('ed25519/sync methods/should not verify signature with wrong public key', async () => {
|
should('not verify signature with wrong public key', () => {
|
||||||
const publicKey = ed.getPublicKey(12);
|
const publicKey = ed.getPublicKey(ed.utils.randomPrivateKey());
|
||||||
const signature = ed.sign(msg, privKey);
|
const signature = ed.sign(msg, privKey);
|
||||||
deepStrictEqual(ed.verify(signature, msg, publicKey), false);
|
deepStrictEqual(ed.verify(signature, msg, publicKey), false);
|
||||||
});
|
});
|
||||||
should('ed25519/sync methods/should not verify signature with wrong hash', async () => {
|
should('not verify signature with wrong hash', () => {
|
||||||
const publicKey = ed.getPublicKey(privKey);
|
const publicKey = ed.getPublicKey(privKey);
|
||||||
const signature = ed.sign(msg, privKey);
|
const signature = ed.sign(msg, privKey);
|
||||||
deepStrictEqual(ed.verify(signature, wrongMsg, publicKey), false);
|
deepStrictEqual(ed.verify(signature, wrongMsg, publicKey), false);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
should('ed25519/BASE_POINT.multiply()/should throw Point#multiply on TEST 5', () => {
|
should('BASE_POINT.multiply() throws in Point#multiply on TEST 5', () => {
|
||||||
for (const num of [0n, 0, -1n, -1, 1.1]) {
|
for (const num of [0n, 0, -1n, -1, 1.1]) {
|
||||||
throws(() => ed.Point.BASE.multiply(num));
|
throws(() => ed.ExtendedPoint.BASE.multiply(num));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
should('input immutability: sign/verify are immutable', () => {
|
should('input immutability: sign/verify are immutable', () => {
|
||||||
const privateKey = ed.utils.randomPrivateKey();
|
const privateKey = ed.utils.randomPrivateKey();
|
||||||
const publicKey = ed.getPublicKey(privateKey);
|
const publicKey = ed.getPublicKey(privateKey);
|
||||||
|
|
||||||
@@ -430,16 +437,16 @@ should('input immutability: sign/verify are immutable', () => {
|
|||||||
if (!ed.verify(signatureCopy, payload, publicKey))
|
if (!ed.verify(signatureCopy, payload, publicKey))
|
||||||
throw new Error('Copied signature verification failed');
|
throw new Error('Copied signature verification failed');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
{
|
describe('wycheproof', () => {
|
||||||
for (let g = 0; g < ed448vectors.testGroups.length; g++) {
|
for (let g = 0; g < ed448vectors.testGroups.length; g++) {
|
||||||
const group = ed448vectors.testGroups[g];
|
const group = ed448vectors.testGroups[g];
|
||||||
const key = group.key;
|
const key = group.key;
|
||||||
should(`Wycheproof/ED448(${g}, public)`, () => {
|
should(`ED448(${g}, public)`, () => {
|
||||||
deepStrictEqual(hex(ed.getPublicKey(key.sk)), key.pk);
|
deepStrictEqual(hex(ed.getPublicKey(key.sk)), key.pk);
|
||||||
});
|
});
|
||||||
should(`Wycheproof/ED448`, () => {
|
should(`ED448`, () => {
|
||||||
for (let i = 0; i < group.tests.length; i++) {
|
for (let i = 0; i < group.tests.length; i++) {
|
||||||
const v = group.tests[i];
|
const v = group.tests[i];
|
||||||
const index = `${g}/${i} ${v.comment}`;
|
const index = `${g}/${i} ${v.comment}`;
|
||||||
@@ -458,10 +465,10 @@ should('input immutability: sign/verify are immutable', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
// ECDH
|
// ECDH
|
||||||
const rfc7748Mul = [
|
const rfc7748Mul = [
|
||||||
{
|
{
|
||||||
scalar:
|
scalar:
|
||||||
'3d262fddf9ec8e88495266fea19a34d28882acef045104d0d1aae121700a779c984c24f8cdd78fbff44943eba368f54b29259a4f1c600ad3',
|
'3d262fddf9ec8e88495266fea19a34d28882acef045104d0d1aae121700a779c984c24f8cdd78fbff44943eba368f54b29259a4f1c600ad3',
|
||||||
@@ -476,15 +483,17 @@ const rfc7748Mul = [
|
|||||||
outputU:
|
outputU:
|
||||||
'884a02576239ff7a2f2f63b2db6a9ff37047ac13568e1e30fe63c4a7ad1b3ee3a5700df34321d62077e63633c575c1c954514e99da7c179d',
|
'884a02576239ff7a2f2f63b2db6a9ff37047ac13568e1e30fe63c4a7ad1b3ee3a5700df34321d62077e63633c575c1c954514e99da7c179d',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
for (let i = 0; i < rfc7748Mul.length; i++) {
|
describe('RFC7748', () => {
|
||||||
|
for (let i = 0; i < rfc7748Mul.length; i++) {
|
||||||
const v = rfc7748Mul[i];
|
const v = rfc7748Mul[i];
|
||||||
should(`RFC7748: scalarMult (${i})`, () => {
|
should(`scalarMult (${i})`, () => {
|
||||||
deepStrictEqual(hex(x448.scalarMult(v.scalar, v.u)), v.outputU);
|
deepStrictEqual(hex(x448.scalarMult(v.scalar, v.u)), v.outputU);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const rfc7748Iter = [
|
const rfc7748Iter = [
|
||||||
{
|
{
|
||||||
scalar:
|
scalar:
|
||||||
'3f482c8a9f19b01e6c46ee9711d9dc14fd4bf67af30765c2ae2b846a4d23a8cd0db897086239492caf350b51f833868b9bc2b3bca9cf4113',
|
'3f482c8a9f19b01e6c46ee9711d9dc14fd4bf67af30765c2ae2b846a4d23a8cd0db897086239492caf350b51f833868b9bc2b3bca9cf4113',
|
||||||
@@ -496,17 +505,17 @@ const rfc7748Iter = [
|
|||||||
iters: 1000,
|
iters: 1000,
|
||||||
},
|
},
|
||||||
// { scalar: '077f453681caca3693198420bbe515cae0002472519b3e67661a7e89cab94695c8f4bcd66e61b9b9c946da8d524de3d69bd9d9d66b997e37', iters: 1000000 },
|
// { scalar: '077f453681caca3693198420bbe515cae0002472519b3e67661a7e89cab94695c8f4bcd66e61b9b9c946da8d524de3d69bd9d9d66b997e37', iters: 1000000 },
|
||||||
];
|
];
|
||||||
for (let i = 0; i < rfc7748Iter.length; i++) {
|
for (let i = 0; i < rfc7748Iter.length; i++) {
|
||||||
const { scalar, iters } = rfc7748Iter[i];
|
const { scalar, iters } = rfc7748Iter[i];
|
||||||
should(`RFC7748: scalarMult iteration (${i})`, () => {
|
should(`RFC7748: scalarMult iteration (${i})`, () => {
|
||||||
let k = x448.Gu;
|
let k = x448.GuBytes;
|
||||||
for (let i = 0, u = k; i < iters; i++) [k, u] = [x448.scalarMult(k, u), k];
|
for (let i = 0, u = k; i < iters; i++) [k, u] = [x448.scalarMult(k, u), k];
|
||||||
deepStrictEqual(hex(k), scalar);
|
deepStrictEqual(hex(k), scalar);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
should('RFC7748 getSharedKey', () => {
|
should('RFC7748 getSharedKey', () => {
|
||||||
const alicePrivate =
|
const alicePrivate =
|
||||||
'9a8f4925d1519f5775cf46b04b5800d4ee9ee8bae8bc5565d498c28dd9c9baf574a9419744897391006382a6f127ab1d9ac2d8c0a598726b';
|
'9a8f4925d1519f5775cf46b04b5800d4ee9ee8bae8bc5565d498c28dd9c9baf574a9419744897391006382a6f127ab1d9ac2d8c0a598726b';
|
||||||
const alicePublic =
|
const alicePublic =
|
||||||
@@ -521,11 +530,11 @@ should('RFC7748 getSharedKey', () => {
|
|||||||
deepStrictEqual(bobPublic, hex(x448.getPublicKey(bobPrivate)));
|
deepStrictEqual(bobPublic, hex(x448.getPublicKey(bobPrivate)));
|
||||||
deepStrictEqual(hex(x448.scalarMult(alicePrivate, bobPublic)), shared);
|
deepStrictEqual(hex(x448.scalarMult(alicePrivate, bobPublic)), shared);
|
||||||
deepStrictEqual(hex(x448.scalarMult(bobPrivate, alicePublic)), shared);
|
deepStrictEqual(hex(x448.scalarMult(bobPrivate, alicePublic)), shared);
|
||||||
});
|
});
|
||||||
|
|
||||||
{
|
describe('wycheproof', () => {
|
||||||
const group = x448vectors.testGroups[0];
|
const group = x448vectors.testGroups[0];
|
||||||
should(`Wycheproof/X448`, () => {
|
should(`X448`, () => {
|
||||||
for (let i = 0; i < group.tests.length; i++) {
|
for (let i = 0; i < group.tests.length; i++) {
|
||||||
const v = group.tests[i];
|
const v = group.tests[i];
|
||||||
const index = `(${i}, ${v.result}) ${v.comment}`;
|
const index = `(${i}, ${v.result}) ${v.comment}`;
|
||||||
@@ -551,31 +560,31 @@ should('RFC7748 getSharedKey', () => {
|
|||||||
} else throw new Error('unknown test result');
|
} else throw new Error('unknown test result');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
|
|
||||||
// should('X448: should convert base point to montgomery using fromPoint', () => {
|
// should('X448: should convert base point to montgomery using fromPoint', () => {
|
||||||
// deepStrictEqual(
|
// deepStrictEqual(
|
||||||
// hex(ed.montgomeryCurve.UfromPoint(ed.Point.BASE)),
|
// hex(ed.montgomeryCurve.UfromPoint(Point.BASE)),
|
||||||
// ed.montgomeryCurve.BASE_POINT_U
|
// ed.montgomeryCurve.BASE_POINT_U
|
||||||
// );
|
// );
|
||||||
// });
|
// });
|
||||||
|
|
||||||
// should('X448/getSharedSecret() should be commutative', async () => {
|
// should('X448/getSharedSecret() should be commutative', async () => {
|
||||||
// for (let i = 0; i < 512; i++) {
|
// for (let i = 0; i < 512; i++) {
|
||||||
// const asec = ed.utils.randomPrivateKey();
|
// const asec = ed.utils.randomPrivateKey();
|
||||||
// const apub = ed.getPublicKey(asec);
|
// const apub = ed.getPublicKey(asec);
|
||||||
// const bsec = ed.utils.randomPrivateKey();
|
// const bsec = ed.utils.randomPrivateKey();
|
||||||
// const bpub = ed.getPublicKey(bsec);
|
// const bpub = ed.getPublicKey(bsec);
|
||||||
// try {
|
// try {
|
||||||
// deepStrictEqual(ed.getSharedSecret(asec, bpub), ed.getSharedSecret(bsec, apub));
|
// deepStrictEqual(ed.getSharedSecret(asec, bpub), ed.getSharedSecret(bsec, apub));
|
||||||
// } catch (error) {
|
// } catch (error) {
|
||||||
// console.error('not commutative', { asec, apub, bsec, bpub });
|
// console.error('not commutative', { asec, apub, bsec, bpub });
|
||||||
// throw error;
|
// throw error;
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// });
|
// });
|
||||||
|
|
||||||
const VECTORS_RFC8032_CTX = [
|
const VECTORS_RFC8032_CTX = [
|
||||||
{
|
{
|
||||||
secretKey:
|
secretKey:
|
||||||
'c4eab05d357007c632f3dbb48489924d552b08fe0c353a0d4a1f00acda2c463afbea67c5e8d2877c5e3bc397a659949ef8021e954e0a12274e',
|
'c4eab05d357007c632f3dbb48489924d552b08fe0c353a0d4a1f00acda2c463afbea67c5e8d2877c5e3bc397a659949ef8021e954e0a12274e',
|
||||||
@@ -593,18 +602,18 @@ const VECTORS_RFC8032_CTX = [
|
|||||||
'5428407e85dcbc98a49155c13764e66c' +
|
'5428407e85dcbc98a49155c13764e66c' +
|
||||||
'3c00',
|
'3c00',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
for (let i = 0; i < VECTORS_RFC8032_CTX.length; i++) {
|
for (let i = 0; i < VECTORS_RFC8032_CTX.length; i++) {
|
||||||
const v = VECTORS_RFC8032_CTX[i];
|
const v = VECTORS_RFC8032_CTX[i];
|
||||||
should(`RFC8032ctx/${i}`, () => {
|
should(`RFC8032ctx/${i}`, () => {
|
||||||
deepStrictEqual(hex(ed.getPublicKey(v.secretKey)), v.publicKey);
|
deepStrictEqual(hex(ed.getPublicKey(v.secretKey)), v.publicKey);
|
||||||
deepStrictEqual(hex(ed.sign(v.message, v.secretKey, v.context)), v.signature);
|
deepStrictEqual(hex(ed.sign(v.message, v.secretKey, v.context)), v.signature);
|
||||||
deepStrictEqual(ed.verify(v.signature, v.message, v.publicKey, v.context), true);
|
deepStrictEqual(ed.verify(v.signature, v.message, v.publicKey, v.context), true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const VECTORS_RFC8032_PH = [
|
const VECTORS_RFC8032_PH = [
|
||||||
{
|
{
|
||||||
secretKey:
|
secretKey:
|
||||||
'833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42ef7822e0d5104127dc05d6dbefde69e3ab2cec7c867c6e2c49',
|
'833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42ef7822e0d5104127dc05d6dbefde69e3ab2cec7c867c6e2c49',
|
||||||
@@ -638,23 +647,25 @@ const VECTORS_RFC8032_PH = [
|
|||||||
'4f8d0704a608c54a6b62d97beb511d13' +
|
'4f8d0704a608c54a6b62d97beb511d13' +
|
||||||
'2100',
|
'2100',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
for (let i = 0; i < VECTORS_RFC8032_PH.length; i++) {
|
for (let i = 0; i < VECTORS_RFC8032_PH.length; i++) {
|
||||||
const v = VECTORS_RFC8032_PH[i];
|
const v = VECTORS_RFC8032_PH[i];
|
||||||
should(`RFC8032ph/${i}`, () => {
|
should(`RFC8032ph/${i}`, () => {
|
||||||
deepStrictEqual(hex(ed448ph.getPublicKey(v.secretKey)), v.publicKey);
|
deepStrictEqual(hex(ed448ph.getPublicKey(v.secretKey)), v.publicKey);
|
||||||
deepStrictEqual(hex(ed448ph.sign(v.message, v.secretKey, v.context)), v.signature);
|
deepStrictEqual(hex(ed448ph.sign(v.message, v.secretKey, v.context)), v.signature);
|
||||||
deepStrictEqual(ed448ph.verify(v.signature, v.message, v.publicKey, v.context), true);
|
deepStrictEqual(ed448ph.verify(v.signature, v.message, v.publicKey, v.context), true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
should('X448 base point', () => {
|
should('X448 base point', () => {
|
||||||
const { x, y } = ed448.Point.BASE;
|
const { x, y } = Point.BASE;
|
||||||
const { P } = ed448.CURVE;
|
const { Fp } = ed448.CURVE;
|
||||||
const invX = ed448.utils.invert(x * x, P); // x²
|
// const invX = Fp.invert(x * x); // x²
|
||||||
const u = ed448.utils.mod(y * y * invX, P); // (y²/x²)
|
const u = Fp.div(Fp.create(y * y), Fp.create(x * x)); // (y²/x²)
|
||||||
deepStrictEqual(hex(numberToBytesLE(u, 56)), x448.Gu);
|
// const u = Fp.create(y * y * invX);
|
||||||
|
deepStrictEqual(numberToBytesLE(u, 56), x448.GuBytes);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// ESM is broken.
|
// ESM is broken.
|
||||||
|
|||||||
@@ -1,19 +1,27 @@
|
|||||||
import { deepStrictEqual } from 'assert';
|
import { deepStrictEqual } from 'assert';
|
||||||
import { should } from 'micro-should';
|
import { describe, should } from 'micro-should';
|
||||||
import { bytesToHex } from '@noble/hashes/utils';
|
import { bytesToHex } from '@noble/hashes/utils';
|
||||||
// Generic tests for all curves in package
|
// Generic tests for all curves in package
|
||||||
import { sha256 } from '@noble/hashes/sha256';
|
import { sha256 } from '@noble/hashes/sha256';
|
||||||
import { sha512 } from '@noble/hashes/sha512';
|
import { sha512 } from '@noble/hashes/sha512';
|
||||||
import { secp256r1 } from '../lib/esm/p256.js';
|
import { shake128, shake256 } from '@noble/hashes/sha3';
|
||||||
import { secp384r1 } from '../lib/esm/p384.js';
|
import * as secp256r1 from '../esm/p256.js';
|
||||||
import { secp521r1 } from '../lib/esm/p521.js';
|
import * as secp384r1 from '../esm/p384.js';
|
||||||
import { secp256k1 } from '../lib/esm/secp256k1.js';
|
import * as secp521r1 from '../esm/p521.js';
|
||||||
import { bls12_381 } from '../lib/esm/bls12-381.js';
|
import * as ed25519 from '../esm/ed25519.js';
|
||||||
import { stringToBytes, expand_message_xmd } from '../lib/esm/abstract/hash-to-curve.js';
|
import * as ed448 from '../esm/ed448.js';
|
||||||
|
import * as secp256k1 from '../esm/secp256k1.js';
|
||||||
|
import { bls12_381 } from '../esm/bls12-381.js';
|
||||||
|
import { expand_message_xmd, expand_message_xof } from '../esm/abstract/hash-to-curve.js';
|
||||||
|
import { utf8ToBytes } from '../esm/abstract/utils.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_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_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' };
|
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
|
// P256
|
||||||
import { default as p256_ro } from './hash-to-curve/P256_XMD:SHA-256_SSWU_RO_.json' assert { type: 'json' };
|
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' };
|
import { default as p256_nu } from './hash-to-curve/P256_XMD:SHA-256_SSWU_NU_.json' assert { type: 'json' };
|
||||||
@@ -40,23 +48,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' };
|
import { default as ed448_nu } from './hash-to-curve/edwards448_XOF:SHAKE256_ELL2_NU_.json' assert { type: 'json' };
|
||||||
|
|
||||||
function testExpandXMD(hash, vectors) {
|
function testExpandXMD(hash, vectors) {
|
||||||
|
describe(`${vectors.hash}/${vectors.DST.length}`, () => {
|
||||||
for (let i = 0; i < vectors.tests.length; i++) {
|
for (let i = 0; i < vectors.tests.length; i++) {
|
||||||
const t = vectors.tests[i];
|
const t = vectors.tests[i];
|
||||||
should(`expand_message_xmd/${vectors.hash}/${vectors.DST.length}/${i}`, () => {
|
should(`${vectors.hash}/${vectors.DST.length}/${i}`, () => {
|
||||||
const p = expand_message_xmd(
|
const p = expand_message_xmd(
|
||||||
stringToBytes(t.msg),
|
utf8ToBytes(t.msg),
|
||||||
stringToBytes(vectors.DST),
|
utf8ToBytes(vectors.DST),
|
||||||
t.len_in_bytes,
|
Number.parseInt(t.len_in_bytes),
|
||||||
hash
|
hash
|
||||||
);
|
);
|
||||||
deepStrictEqual(bytesToHex(p), t.uniform_bytes);
|
deepStrictEqual(bytesToHex(p), t.uniform_bytes);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
testExpandXMD(sha256, xmd_sha256_38);
|
describe('expand_message_xmd', () => {
|
||||||
testExpandXMD(sha256, xmd_sha256_256);
|
testExpandXMD(sha256, xmd_sha256_38);
|
||||||
testExpandXMD(sha512, xmd_sha512_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(
|
||||||
|
utf8ToBytes(t.msg),
|
||||||
|
utf8ToBytes(vectors.DST),
|
||||||
|
Number.parseInt(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) {
|
function stringToFp(s) {
|
||||||
// bls-G2 support
|
// bls-G2 support
|
||||||
@@ -68,37 +104,44 @@ function stringToFp(s) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function testCurve(curve, ro, nu) {
|
function testCurve(curve, ro, nu) {
|
||||||
|
describe(`${ro.curve}/${ro.ciphersuite}`, () => {
|
||||||
for (let i = 0; i < ro.vectors.length; i++) {
|
for (let i = 0; i < ro.vectors.length; i++) {
|
||||||
const t = ro.vectors[i];
|
const t = ro.vectors[i];
|
||||||
should(`${ro.curve}/${ro.ciphersuite}(${i})`, () => {
|
should(`(${i})`, () => {
|
||||||
const p = curve.Point.hashToCurve(stringToBytes(t.msg), {
|
const p = curve
|
||||||
|
.hashToCurve(utf8ToBytes(t.msg), {
|
||||||
DST: ro.dst,
|
DST: ro.dst,
|
||||||
});
|
})
|
||||||
|
.toAffine();
|
||||||
deepStrictEqual(p.x, stringToFp(t.P.x), 'Px');
|
deepStrictEqual(p.x, stringToFp(t.P.x), 'Px');
|
||||||
deepStrictEqual(p.y, stringToFp(t.P.y), 'Py');
|
deepStrictEqual(p.y, stringToFp(t.P.y), 'Py');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
describe(`${nu.curve}/${nu.ciphersuite}`, () => {
|
||||||
for (let i = 0; i < nu.vectors.length; i++) {
|
for (let i = 0; i < nu.vectors.length; i++) {
|
||||||
const t = nu.vectors[i];
|
const t = nu.vectors[i];
|
||||||
should(`${nu.curve}/${nu.ciphersuite}(${i})`, () => {
|
should(`(${i})`, () => {
|
||||||
const p = curve.Point.encodeToCurve(stringToBytes(t.msg), {
|
const p = curve
|
||||||
|
.encodeToCurve(utf8ToBytes(t.msg), {
|
||||||
DST: nu.dst,
|
DST: nu.dst,
|
||||||
});
|
})
|
||||||
|
.toAffine();
|
||||||
deepStrictEqual(p.x, stringToFp(t.P.x), 'Px');
|
deepStrictEqual(p.x, stringToFp(t.P.x), 'Px');
|
||||||
deepStrictEqual(p.y, stringToFp(t.P.y), 'Py');
|
deepStrictEqual(p.y, stringToFp(t.P.y), 'Py');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
testCurve(secp256r1, p256_ro, p256_nu);
|
testCurve(secp256r1, p256_ro, p256_nu);
|
||||||
testCurve(secp384r1, p384_ro, p384_nu);
|
testCurve(secp384r1, p384_ro, p384_nu);
|
||||||
testCurve(secp521r1, p521_ro, p521_nu);
|
testCurve(secp521r1, p521_ro, p521_nu);
|
||||||
// TODO: remove same tests from bls12
|
|
||||||
testCurve(bls12_381.G1, g1_ro, g1_nu);
|
testCurve(bls12_381.G1, g1_ro, g1_nu);
|
||||||
testCurve(bls12_381.G2, g2_ro, g2_nu);
|
testCurve(bls12_381.G2, g2_ro, g2_nu);
|
||||||
testCurve(secp256k1, secp256k1_ro, secp256k1_nu);
|
testCurve(secp256k1, secp256k1_ro, secp256k1_nu);
|
||||||
//testCurve(ed25519, ed25519_ro, ed25519_nu);
|
testCurve(ed25519, ed25519_ro, ed25519_nu);
|
||||||
//testCurve(ed448, ed448_ro, ed448_nu);
|
testCurve(ed448, ed448_ro, ed448_nu);
|
||||||
|
|
||||||
// ESM is broken.
|
// ESM is broken.
|
||||||
import url from 'url';
|
import url from 'url';
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ import './nist.test.js';
|
|||||||
import './ed448.test.js';
|
import './ed448.test.js';
|
||||||
import './ed25519.test.js';
|
import './ed25519.test.js';
|
||||||
import './secp256k1.test.js';
|
import './secp256k1.test.js';
|
||||||
import './stark/stark.test.js';
|
import './secp256k1-schnorr.test.js';
|
||||||
|
import './stark/index.test.js';
|
||||||
import './jubjub.test.js';
|
import './jubjub.test.js';
|
||||||
import './bls12-381.test.js';
|
import './bls12-381.test.js';
|
||||||
import './hash-to-curve.test.js';
|
import './hash-to-curve.test.js';
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import { jubjub, findGroupHash } from '../lib/esm/jubjub.js';
|
import { jubjub, findGroupHash } from '../esm/jubjub.js';
|
||||||
import { should } from 'micro-should';
|
import { describe, should } from 'micro-should';
|
||||||
import { deepStrictEqual, throws } from 'assert';
|
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,
|
0x055f1f24f0f0512287e51c3c5a0a6903fc0baf8711de9eafd7c0e66f69d8d2dbn,
|
||||||
0x566178b2505fdd52132a5007d80a04652842e78ffb376897588f406278214ed7n,
|
0x566178b2505fdd52132a5007d80a04652842e78ffb376897588f406278214ed7n,
|
||||||
0x0141fafa1f11088a3b2007c14d652375888f3b37838ba6bdffae096741ceddfen,
|
0x0141fafa1f11088a3b2007c14d652375888f3b37838ba6bdffae096741ceddfen,
|
||||||
0x12eada93c0b7d595f5f04f5ebfb4b7d033ef2884136475cab5e41ce17db5be9cn
|
0x12eada93c0b7d595f5f04f5ebfb4b7d033ef2884136475cab5e41ce17db5be9cn
|
||||||
);
|
);
|
||||||
const G_PROOF = new jubjub.ExtendedPoint(
|
const G_PROOF = new Point(
|
||||||
0x0174d54ce9fad258a2f8a86a1deabf15c7a2b51106b0fbcd9d29020f78936f71n,
|
0x0174d54ce9fad258a2f8a86a1deabf15c7a2b51106b0fbcd9d29020f78936f71n,
|
||||||
0x16871d6d877dcd222e4ec3bccb3f37cb1865a2d37dd3a5dcbc032a69b62b4445n,
|
0x16871d6d877dcd222e4ec3bccb3f37cb1865a2d37dd3a5dcbc032a69b62b4445n,
|
||||||
0x57a3cd31e496d82bd4aa78bd5ecd751cfb76d54a5d3f4560866379f9fc11c9b3n,
|
0x57a3cd31e496d82bd4aa78bd5ecd751cfb76d54a5d3f4560866379f9fc11c9b3n,
|
||||||
@@ -18,10 +18,11 @@ const G_PROOF = new jubjub.ExtendedPoint(
|
|||||||
|
|
||||||
const getXY = (p) => ({ x: p.x, y: p.y });
|
const getXY = (p) => ({ x: p.x, y: p.y });
|
||||||
|
|
||||||
should('toHex/fromHex', () => {
|
describe('jubjub', () => {
|
||||||
|
should('toHex/fromHex', () => {
|
||||||
// More than field
|
// More than field
|
||||||
throws(() =>
|
throws(() =>
|
||||||
jubjub.Point.fromHex(
|
Point.fromHex(
|
||||||
new Uint8Array([
|
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, 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,
|
||||||
@@ -30,14 +31,14 @@ should('toHex/fromHex', () => {
|
|||||||
);
|
);
|
||||||
// Multiplicative generator (sqrt == null), not on curve.
|
// Multiplicative generator (sqrt == null), not on curve.
|
||||||
throws(() =>
|
throws(() =>
|
||||||
jubjub.Point.fromHex(
|
Point.fromHex(
|
||||||
new Uint8Array([
|
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,
|
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,
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
const tmp = jubjub.Point.fromHex(
|
const tmp = Point.fromHex(
|
||||||
new Uint8Array([
|
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, 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,
|
||||||
@@ -46,25 +47,32 @@ should('toHex/fromHex', () => {
|
|||||||
deepStrictEqual(tmp.x, 0x8d51ccce760304d0ec030002760300000001000000000000n);
|
deepStrictEqual(tmp.x, 0x8d51ccce760304d0ec030002760300000001000000000000n);
|
||||||
deepStrictEqual(tmp.y, 0n);
|
deepStrictEqual(tmp.y, 0n);
|
||||||
|
|
||||||
const S = G_SPEND.toAffine().toRawBytes();
|
const S = G_SPEND.toRawBytes();
|
||||||
const S2 = G_SPEND.double().toAffine().toRawBytes();
|
const S2 = G_SPEND.double().toRawBytes();
|
||||||
const P = G_PROOF.toAffine().toRawBytes();
|
const P = G_PROOF.toRawBytes();
|
||||||
const P2 = G_PROOF.double().toAffine().toRawBytes();
|
const P2 = G_PROOF.double().toRawBytes();
|
||||||
const S_exp = jubjub.Point.fromHex(S);
|
const S_exp = Point.fromHex(S);
|
||||||
const S2_exp = jubjub.Point.fromHex(S2);
|
const S2_exp = Point.fromHex(S2);
|
||||||
const P_exp = jubjub.Point.fromHex(P);
|
const P_exp = Point.fromHex(P);
|
||||||
const P2_exp = jubjub.Point.fromHex(P2);
|
const P2_exp = Point.fromHex(P2);
|
||||||
deepStrictEqual(getXY(G_SPEND.toAffine()), getXY(S_exp));
|
deepStrictEqual(getXY(G_SPEND.toAffine()), getXY(S_exp));
|
||||||
deepStrictEqual(getXY(G_SPEND.double().toAffine()), getXY(S2_exp));
|
deepStrictEqual(getXY(G_SPEND.double().toAffine()), getXY(S2_exp));
|
||||||
deepStrictEqual(getXY(G_PROOF.toAffine()), getXY(P_exp));
|
deepStrictEqual(getXY(G_PROOF.toAffine()), getXY(P_exp));
|
||||||
deepStrictEqual(getXY(G_PROOF.double().toAffine()), getXY(P2_exp));
|
deepStrictEqual(getXY(G_PROOF.double().toAffine()), getXY(P2_exp));
|
||||||
});
|
});
|
||||||
|
|
||||||
should('Find generators', () => {
|
should('Find generators', () => {
|
||||||
const spend = findGroupHash(new Uint8Array(), new Uint8Array([90, 99, 97, 115, 104, 95, 71, 95]));
|
const spend = findGroupHash(
|
||||||
const proof = findGroupHash(new Uint8Array(), new Uint8Array([90, 99, 97, 115, 104, 95, 72, 95]));
|
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(spend.toAffine()), getXY(G_SPEND.toAffine()));
|
||||||
deepStrictEqual(getXY(proof.toAffine()), getXY(G_PROOF.toAffine()));
|
deepStrictEqual(getXY(proof.toAffine()), getXY(G_PROOF.toAffine()));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// ESM is broken.
|
// ESM is broken.
|
||||||
|
|||||||
@@ -1,174 +1,20 @@
|
|||||||
import { deepStrictEqual, throws } from 'assert';
|
import { deepStrictEqual } from 'assert';
|
||||||
import { should } from 'micro-should';
|
import { describe, should } from 'micro-should';
|
||||||
import { secp192r1, P192 } from '../lib/esm/p192.js';
|
import { secp192r1, secp224r1, P192, P224 } from './_more-curves.helpers.js';
|
||||||
import { secp224r1, P224 } from '../lib/esm/p224.js';
|
import { secp256r1, P256 } from '../esm/p256.js';
|
||||||
import { secp256r1, P256 } from '../lib/esm/p256.js';
|
import { secp384r1, P384 } from '../esm/p384.js';
|
||||||
import { secp384r1, P384 } from '../lib/esm/p384.js';
|
import { secp521r1, P521 } from '../esm/p521.js';
|
||||||
import { secp521r1, P521 } from '../lib/esm/p521.js';
|
import { secp256k1 } from '../esm/secp256k1.js';
|
||||||
import { secp256k1 } from '../lib/esm/secp256k1.js';
|
import { hexToBytes, bytesToHex } from '../esm/abstract/utils.js';
|
||||||
import { hexToBytes, bytesToHex } from '../lib/esm/abstract/utils.js';
|
|
||||||
import { default as ecdsa } from './wycheproof/ecdsa_test.json' assert { type: 'json' };
|
import { default as ecdsa } from './wycheproof/ecdsa_test.json' assert { type: 'json' };
|
||||||
import { default as ecdh } from './wycheproof/ecdh_test.json' assert { type: 'json' };
|
import { default as ecdh } from './wycheproof/ecdh_test.json' assert { type: 'json' };
|
||||||
import { default as rfc6979 } from './fixtures/rfc6979.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_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_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_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_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' };
|
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
|
// Tests with custom hashes
|
||||||
import { default as secp224r1_sha224_test } from './wycheproof/ecdsa_secp224r1_sha224_test.json' assert { type: 'json' };
|
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' };
|
import { default as secp224r1_sha256_test } from './wycheproof/ecdsa_secp224r1_sha256_test.json' assert { type: 'json' };
|
||||||
@@ -199,6 +45,123 @@ import { sha3_224, sha3_256, sha3_384, sha3_512 } from '@noble/hashes/sha3';
|
|||||||
import { sha512, sha384 } from '@noble/hashes/sha512';
|
import { sha512, sha384 } from '@noble/hashes/sha512';
|
||||||
import { sha224, sha256 } from '@noble/hashes/sha256';
|
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 = {
|
const WYCHEPROOF_ECDSA = {
|
||||||
P224: {
|
P224: {
|
||||||
curve: P224,
|
curve: P224,
|
||||||
@@ -232,7 +195,6 @@ const WYCHEPROOF_ECDSA = {
|
|||||||
secp256k1: {
|
secp256k1: {
|
||||||
curve: secp256k1,
|
curve: secp256k1,
|
||||||
hashes: {
|
hashes: {
|
||||||
// TODO: debug why fails, can be bug
|
|
||||||
sha256: {
|
sha256: {
|
||||||
hash: sha256,
|
hash: sha256,
|
||||||
tests: [secp256k1_sha256_test],
|
tests: [secp256k1_sha256_test],
|
||||||
@@ -309,31 +271,33 @@ const WYCHEPROOF_ECDSA = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function runWycheproof(name, CURVE, group, index) {
|
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.x, BigInt(`0x${group.key.wx}`));
|
||||||
deepStrictEqual(pubKey.y, BigInt(`0x${group.key.wy}`));
|
deepStrictEqual(pubKey.y, BigInt(`0x${group.key.wy}`));
|
||||||
|
const pubR = pubKey.toRawBytes();
|
||||||
for (const test of group.tests) {
|
for (const test of group.tests) {
|
||||||
const m = CURVE.CURVE.hash(hexToBytes(test.msg));
|
const m = CURVE.CURVE.hash(hexToBytes(test.msg));
|
||||||
|
const { sig } = test;
|
||||||
|
|
||||||
if (test.result === 'valid' || test.result === 'acceptable') {
|
if (test.result === 'valid' || test.result === 'acceptable') {
|
||||||
try {
|
try {
|
||||||
CURVE.Signature.fromDER(test.sig);
|
CURVE.Signature.fromDER(sig);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Some tests has invalid signature which we don't accept
|
// Some tests has invalid signature which we don't accept
|
||||||
if (e.message.includes('Invalid signature: incorrect length')) continue;
|
if (e.message.includes('Invalid signature: incorrect length')) continue;
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
const verified = CURVE.verify(test.sig, m, pubKey);
|
const verified = CURVE.verify(sig, m, pubR);
|
||||||
if (name === 'secp256k1') {
|
if (name === 'secp256k1') {
|
||||||
// lowS: true for 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 {
|
} else {
|
||||||
deepStrictEqual(verified, true, `${index}: valid`);
|
deepStrictEqual(verified, true, `${index}: valid`);
|
||||||
}
|
}
|
||||||
} else if (test.result === 'invalid') {
|
} else if (test.result === 'invalid') {
|
||||||
let failed = false;
|
let failed = false;
|
||||||
try {
|
try {
|
||||||
failed = !CURVE.verify(test.sig, m, pubKey);
|
failed = !CURVE.verify(sig, m, pubR);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
failed = true;
|
failed = true;
|
||||||
}
|
}
|
||||||
@@ -342,12 +306,53 @@ function runWycheproof(name, CURVE, group, index) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const name in WYCHEPROOF_ECDSA) {
|
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];
|
const { curve, hashes } = WYCHEPROOF_ECDSA[name];
|
||||||
|
describe(name, () => {
|
||||||
for (const hName in hashes) {
|
for (const hName in hashes) {
|
||||||
const { hash, tests } = hashes[hName];
|
const { hash, tests } = hashes[hName];
|
||||||
const CURVE = curve.create(hash);
|
const CURVE = curve.create(hash);
|
||||||
should(`Wycheproof/WYCHEPROOF_ECDSA ${name}/${hName}`, () => {
|
should(`${name}/${hName}`, () => {
|
||||||
for (let i = 0; i < tests.length; i++) {
|
for (let i = 0; i < tests.length; i++) {
|
||||||
const groups = tests[i].testGroups;
|
const groups = tests[i].testGroups;
|
||||||
for (let j = 0; j < groups.length; j++) {
|
for (let j = 0; j < groups.length; j++) {
|
||||||
@@ -357,15 +362,18 @@ for (const name in WYCHEPROOF_ECDSA) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const hexToBigint = (hex) => BigInt(`0x${hex}`);
|
const hexToBigint = (hex) => BigInt(`0x${hex}`);
|
||||||
should('RFC6979', () => {
|
describe('RFC6979', () => {
|
||||||
for (const v of rfc6979) {
|
for (const v of rfc6979) {
|
||||||
|
should(v.curve, () => {
|
||||||
const curve = NIST[v.curve];
|
const curve = NIST[v.curve];
|
||||||
deepStrictEqual(curve.CURVE.n, hexToBigint(v.q));
|
deepStrictEqual(curve.CURVE.n, hexToBigint(v.q));
|
||||||
const pubKey = curve.getPublicKey(v.private);
|
const pubKey = curve.getPublicKey(v.private);
|
||||||
const pubPoint = curve.Point.fromHex(pubKey);
|
const pubPoint = curve.ProjectivePoint.fromHex(pubKey);
|
||||||
deepStrictEqual(pubPoint.x, hexToBigint(v.Ux));
|
deepStrictEqual(pubPoint.x, hexToBigint(v.Ux));
|
||||||
deepStrictEqual(pubPoint.y, hexToBigint(v.Uy));
|
deepStrictEqual(pubPoint.y, hexToBigint(v.Uy));
|
||||||
for (const c of v.cases) {
|
for (const c of v.cases) {
|
||||||
@@ -376,6 +384,7 @@ should('RFC6979', () => {
|
|||||||
deepStrictEqual(curve.verify(sigObj.toDERRawBytes(), h, pubKey), true, 'verify(1)');
|
deepStrictEqual(curve.verify(sigObj.toDERRawBytes(), h, pubKey), true, 'verify(1)');
|
||||||
deepStrictEqual(curve.verify(sigObj, h, pubKey), true, 'verify(2)');
|
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 '../esm/abstract/poseidon.js';
|
||||||
|
import * as stark from '../esm/stark.js';
|
||||||
|
import * as mod from '../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();
|
||||||
|
}
|
||||||
34
test/secp256k1-schnorr.test.js
Normal file
34
test/secp256k1-schnorr.test.js
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import { deepStrictEqual, throws } from 'assert';
|
||||||
|
import { readFileSync } from 'fs';
|
||||||
|
import { should, describe } from 'micro-should';
|
||||||
|
import { bytesToHex as hex } from '@noble/hashes/utils';
|
||||||
|
import { schnorr } from '../esm/secp256k1.js';
|
||||||
|
const schCsv = readFileSync('./test/vectors/schnorr.csv', 'utf-8');
|
||||||
|
|
||||||
|
describe('schnorr.sign()', () => {
|
||||||
|
// index,secret key,public key,aux_rand,message,signature,verification result,comment
|
||||||
|
const vectors = schCsv
|
||||||
|
.split('\n')
|
||||||
|
.map((line) => line.split(','))
|
||||||
|
.slice(1, -1);
|
||||||
|
for (let vec of vectors) {
|
||||||
|
const [index, sec, pub, rnd, msg, expSig, passes, comment] = vec;
|
||||||
|
should(`${comment || 'vector ' + index}`, () => {
|
||||||
|
if (sec) {
|
||||||
|
deepStrictEqual(hex(schnorr.getPublicKey(sec)), pub.toLowerCase());
|
||||||
|
const sig = schnorr.sign(msg, sec, rnd);
|
||||||
|
deepStrictEqual(hex(sig), expSig.toLowerCase());
|
||||||
|
deepStrictEqual(schnorr.verify(sig, msg, pub), true);
|
||||||
|
} else {
|
||||||
|
const passed = schnorr.verify(expSig, msg, pub);
|
||||||
|
deepStrictEqual(passed, passes === 'TRUE');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// ESM is broken.
|
||||||
|
import url from 'url';
|
||||||
|
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
|
||||||
|
should.run();
|
||||||
|
}
|
||||||
14
test/secp256k1.helpers.js
Normal file
14
test/secp256k1.helpers.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
// @ts-ignore
|
||||||
|
export { secp256k1 as secp } from '../esm/secp256k1.js';
|
||||||
|
import { secp256k1 as _secp } from '../esm/secp256k1.js';
|
||||||
|
export { bytesToNumberBE, numberToBytesBE } from '../esm/abstract/utils.js';
|
||||||
|
export { mod } from '../esm/abstract/modular.js';
|
||||||
|
export const sigFromDER = (der) => {
|
||||||
|
return _secp.Signature.fromDER(der);
|
||||||
|
};
|
||||||
|
export const sigToDER = (sig) => sig.toDERHex();
|
||||||
|
export const selectHash = (secp) => secp.CURVE.hash;
|
||||||
|
export const normVerifySig = (s) => _secp.Signature.fromDER(s);
|
||||||
|
// export const bytesToNumberBE = secp256k1.utils.bytesToNumberBE;
|
||||||
|
// export const numberToBytesBE = secp256k1.utils.numberToBytesBE;
|
||||||
|
// export const mod = mod_;
|
||||||
@@ -1,22 +1,23 @@
|
|||||||
|
import { hexToBytes, bytesToHex as hex } from '@noble/hashes/utils';
|
||||||
|
import { deepStrictEqual, throws } from 'assert';
|
||||||
import * as fc from 'fast-check';
|
import * as fc from 'fast-check';
|
||||||
import { secp256k1, schnorr } from '../lib/esm/secp256k1.js';
|
|
||||||
import { readFileSync } from 'fs';
|
import { readFileSync } from 'fs';
|
||||||
|
import { should, describe } from 'micro-should';
|
||||||
|
// prettier-ignore
|
||||||
|
import {
|
||||||
|
secp, sigFromDER, sigToDER, selectHash, normVerifySig, mod, bytesToNumberBE, numberToBytesBE
|
||||||
|
} from './secp256k1.helpers.js';
|
||||||
|
|
||||||
import { default as ecdsa } from './vectors/ecdsa.json' assert { type: 'json' };
|
import { default as ecdsa } from './vectors/ecdsa.json' assert { type: 'json' };
|
||||||
import { default as ecdh } from './vectors/ecdh.json' assert { type: 'json' };
|
import { default as ecdh } from './vectors/ecdh.json' assert { type: 'json' };
|
||||||
import { default as privates } from './vectors/privates.json' assert { type: 'json' };
|
import { default as privates } from './vectors/privates.json' assert { type: 'json' };
|
||||||
import { default as points } from './vectors/points.json' assert { type: 'json' };
|
import { default as points } from './vectors/points.json' assert { type: 'json' };
|
||||||
import { default as wp } from './vectors/wychenproof.json' assert { type: 'json' };
|
import { default as wp } from './vectors/wychenproof.json' assert { type: 'json' };
|
||||||
import { should } from 'micro-should';
|
|
||||||
import { deepStrictEqual, throws } from 'assert';
|
|
||||||
import { hexToBytes, bytesToHex } from '@noble/hashes/utils';
|
|
||||||
|
|
||||||
const hex = bytesToHex;
|
const Point = secp.ProjectivePoint;
|
||||||
const secp = secp256k1;
|
|
||||||
const privatesTxt = readFileSync('./test/vectors/privates-2.txt', 'utf-8');
|
const privatesTxt = readFileSync('./test/vectors/privates-2.txt', 'utf-8');
|
||||||
const schCsv = readFileSync('./test/vectors/schnorr.csv', 'utf-8');
|
|
||||||
|
|
||||||
const FC_BIGINT = fc.bigInt(1n + 1n, secp.CURVE.n - 1n);
|
const FC_BIGINT = fc.bigInt(1n + 1n, secp.CURVE.n - 1n);
|
||||||
const P = secp.CURVE.Fp.ORDER;
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
const INVALID_ITEMS = ['deadbeef', Math.pow(2, 53), [1], 'xyzxyzxyxyzxyzxyxyzxyzxyxyzxyzxyxyzxyzxyxyzxyzxyxyzxyzxyxyzxyzxy', secp.CURVE.n + 2n];
|
const INVALID_ITEMS = ['deadbeef', Math.pow(2, 53), [1], 'xyzxyzxyxyzxyzxyxyzxyzxyxyzxyzxyxyzxyzxyxyzxyzxyxyzxyzxyxyzxyzxy', secp.CURVE.n + 2n];
|
||||||
|
|
||||||
@@ -24,108 +25,110 @@ const toBEHex = (n) => n.toString(16).padStart(64, '0');
|
|||||||
|
|
||||||
function hexToNumber(hex) {
|
function hexToNumber(hex) {
|
||||||
if (typeof hex !== 'string') {
|
if (typeof hex !== 'string') {
|
||||||
throw new TypeError('hexToNumber: expected string, got ' + typeof hex);
|
throw new Error('hexToNumber: expected string, got ' + typeof hex);
|
||||||
}
|
}
|
||||||
// Big Endian
|
// Big Endian
|
||||||
return BigInt(`0x${hex}`);
|
return BigInt(`0x${hex}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
should('secp256k1.getPublicKey()', () => {
|
describe('secp256k1', () => {
|
||||||
|
should('getPublicKey()', () => {
|
||||||
const data = privatesTxt
|
const data = privatesTxt
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.filter((line) => line)
|
.filter((line) => line)
|
||||||
.map((line) => line.split(':'));
|
.map((line) => line.split(':'));
|
||||||
for (let [priv, x, y] of data) {
|
for (let [priv, x, y] of data) {
|
||||||
const point = secp.Point.fromPrivateKey(BigInt(priv));
|
const point = Point.fromPrivateKey(BigInt(priv));
|
||||||
deepStrictEqual(toBEHex(point.x), x);
|
deepStrictEqual(toBEHex(point.x), x);
|
||||||
deepStrictEqual(toBEHex(point.y), y);
|
deepStrictEqual(toBEHex(point.y), y);
|
||||||
|
|
||||||
const point2 = secp.Point.fromHex(secp.getPublicKey(toBEHex(BigInt(priv))));
|
const point2 = Point.fromHex(secp.getPublicKey(toBEHex(BigInt(priv))));
|
||||||
deepStrictEqual(toBEHex(point2.x), x);
|
deepStrictEqual(toBEHex(point2.x), x);
|
||||||
deepStrictEqual(toBEHex(point2.y), y);
|
deepStrictEqual(toBEHex(point2.y), y);
|
||||||
|
|
||||||
const point3 = secp.Point.fromHex(secp.getPublicKey(hexToBytes(toBEHex(BigInt(priv)))));
|
const point3 = Point.fromHex(secp.getPublicKey(hexToBytes(toBEHex(BigInt(priv)))));
|
||||||
deepStrictEqual(toBEHex(point3.x), x);
|
deepStrictEqual(toBEHex(point3.x), x);
|
||||||
deepStrictEqual(toBEHex(point3.y), y);
|
deepStrictEqual(toBEHex(point3.y), y);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
should('secp256k1.getPublicKey() rejects invalid keys', () => {
|
should('getPublicKey() rejects invalid keys', () => {
|
||||||
// for (const item of INVALID_ITEMS) {
|
for (const item of INVALID_ITEMS) {
|
||||||
// throws(() => secp.getPublicKey(item));
|
throws(() => secp.getPublicKey(item));
|
||||||
// }
|
}
|
||||||
});
|
});
|
||||||
should('secp256k1.precompute', () => {
|
should('precompute', () => {
|
||||||
secp.utils.precompute(4);
|
secp.utils.precompute(4);
|
||||||
const data = privatesTxt
|
const data = privatesTxt
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.filter((line) => line)
|
.filter((line) => line)
|
||||||
.map((line) => line.split(':'));
|
.map((line) => line.split(':'));
|
||||||
for (let [priv, x, y] of data) {
|
for (let [priv, x, y] of data) {
|
||||||
const point = secp.Point.fromPrivateKey(BigInt(priv));
|
const point = Point.fromPrivateKey(BigInt(priv));
|
||||||
deepStrictEqual(toBEHex(point.x), x);
|
deepStrictEqual(toBEHex(point.x), x);
|
||||||
deepStrictEqual(toBEHex(point.y), y);
|
deepStrictEqual(toBEHex(point.y), y);
|
||||||
|
|
||||||
const point2 = secp.Point.fromHex(secp.getPublicKey(toBEHex(BigInt(priv))));
|
const point2 = Point.fromHex(secp.getPublicKey(toBEHex(BigInt(priv))));
|
||||||
deepStrictEqual(toBEHex(point2.x), x);
|
deepStrictEqual(toBEHex(point2.x), x);
|
||||||
deepStrictEqual(toBEHex(point2.y), y);
|
deepStrictEqual(toBEHex(point2.y), y);
|
||||||
|
|
||||||
const point3 = secp.Point.fromHex(secp.getPublicKey(hexToBytes(toBEHex(BigInt(priv)))));
|
const point3 = Point.fromHex(secp.getPublicKey(hexToBytes(toBEHex(BigInt(priv)))));
|
||||||
deepStrictEqual(toBEHex(point3.x), x);
|
deepStrictEqual(toBEHex(point3.x), x);
|
||||||
deepStrictEqual(toBEHex(point3.y), y);
|
deepStrictEqual(toBEHex(point3.y), y);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
should('secp256k1.Point.isValidPoint()', () => {
|
describe('Point', () => {
|
||||||
|
should('fromHex() assertValidity', () => {
|
||||||
for (const vector of points.valid.isPoint) {
|
for (const vector of points.valid.isPoint) {
|
||||||
const { P, expected } = vector;
|
const { P, expected } = vector;
|
||||||
if (expected) {
|
if (expected) {
|
||||||
secp.Point.fromHex(P);
|
Point.fromHex(P);
|
||||||
} else {
|
} else {
|
||||||
throws(() => secp.Point.fromHex(P));
|
throws(() => Point.fromHex(P));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
should('secp256k1.Point.fromPrivateKey()', () => {
|
should('.fromPrivateKey()', () => {
|
||||||
for (const vector of points.valid.pointFromScalar) {
|
for (const vector of points.valid.pointFromScalar) {
|
||||||
const { d, expected } = vector;
|
const { d, expected } = vector;
|
||||||
let p = secp.Point.fromPrivateKey(d);
|
let p = Point.fromPrivateKey(d);
|
||||||
deepStrictEqual(p.toHex(true), expected);
|
deepStrictEqual(p.toHex(true), expected);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
should('secp256k1.Point#toHex(compressed)', () => {
|
should('#toHex(compressed)', () => {
|
||||||
for (const vector of points.valid.pointCompress) {
|
for (const vector of points.valid.pointCompress) {
|
||||||
const { P, compress, expected } = vector;
|
const { P, compress, expected } = vector;
|
||||||
let p = secp.Point.fromHex(P);
|
let p = Point.fromHex(P);
|
||||||
deepStrictEqual(p.toHex(compress), expected);
|
deepStrictEqual(p.toHex(compress), expected);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
should('secp256k1.Point#toHex() roundtrip (failed case)', () => {
|
should('#toHex() roundtrip (failed case)', () => {
|
||||||
const point1 =
|
const point1 =
|
||||||
secp.Point.fromPrivateKey(
|
Point.fromPrivateKey(
|
||||||
88572218780422190464634044548753414301110513745532121983949500266768436236425n
|
88572218780422190464634044548753414301110513745532121983949500266768436236425n
|
||||||
);
|
);
|
||||||
// const hex = point1.toHex(true);
|
// const hex = point1.toHex(true);
|
||||||
// deepStrictEqual(secp.Point.fromHex(hex).toHex(true), hex);
|
// deepStrictEqual(Point.fromHex(hex).toHex(true), hex);
|
||||||
});
|
});
|
||||||
|
|
||||||
should('secp256k1.Point#toHex() roundtrip', () => {
|
should('#toHex() roundtrip', () => {
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(FC_BIGINT, (x) => {
|
fc.property(FC_BIGINT, (x) => {
|
||||||
const point1 = secp.Point.fromPrivateKey(x);
|
const point1 = Point.fromPrivateKey(x);
|
||||||
const hex = point1.toHex(true);
|
const hex = point1.toHex(true);
|
||||||
deepStrictEqual(secp.Point.fromHex(hex).toHex(true), hex);
|
deepStrictEqual(Point.fromHex(hex).toHex(true), hex);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
should('secp256k1.Point#add(other)', () => {
|
should('#add(other)', () => {
|
||||||
for (const vector of points.valid.pointAdd) {
|
for (const vector of points.valid.pointAdd) {
|
||||||
const { P, Q, expected } = vector;
|
const { P, Q, expected } = vector;
|
||||||
let p = secp.Point.fromHex(P);
|
let p = Point.fromHex(P);
|
||||||
let q = secp.Point.fromHex(Q);
|
let q = Point.fromHex(Q);
|
||||||
if (expected) {
|
if (expected) {
|
||||||
deepStrictEqual(p.add(q).toHex(true), expected);
|
deepStrictEqual(p.add(q).toHex(true), expected);
|
||||||
} else {
|
} else {
|
||||||
@@ -134,14 +137,14 @@ should('secp256k1.Point#add(other)', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
should('secp256k1.Point#multiply(privateKey)', () => {
|
should('#multiply(privateKey)', () => {
|
||||||
for (const vector of points.valid.pointMultiply) {
|
for (const vector of points.valid.pointMultiply) {
|
||||||
const { P, d, expected } = vector;
|
const { P, d, expected } = vector;
|
||||||
const p = secp.Point.fromHex(P);
|
const p = Point.fromHex(P);
|
||||||
if (expected) {
|
if (expected) {
|
||||||
deepStrictEqual(p.multiply(hexToNumber(d)).toHex(true), expected);
|
deepStrictEqual(p.multiply(hexToNumber(d)).toHex(true), expected, P);
|
||||||
} else {
|
} else {
|
||||||
throws(() => {
|
throws(() => {
|
||||||
p.multiply(hexToNumber(d)).toHex(true);
|
p.multiply(hexToNumber(d)).toHex(true);
|
||||||
@@ -153,47 +156,50 @@ should('secp256k1.Point#multiply(privateKey)', () => {
|
|||||||
const { P, d } = vector;
|
const { P, d } = vector;
|
||||||
if (hexToNumber(d) < secp.CURVE.n) {
|
if (hexToNumber(d) < secp.CURVE.n) {
|
||||||
throws(() => {
|
throws(() => {
|
||||||
const p = secp.Point.fromHex(P);
|
const p = Point.fromHex(P);
|
||||||
p.multiply(hexToNumber(d)).toHex(true);
|
p.multiply(hexToNumber(d)).toHex(true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const num of [0n, 0, -1n, -1, 1.1]) {
|
for (const num of [0n, 0, -1n, -1, 1.1]) {
|
||||||
throws(() => secp.Point.BASE.multiply(num));
|
throws(() => Point.BASE.multiply(num));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// multiply() should equal multiplyUnsafe()
|
// multiply() should equal multiplyUnsafe()
|
||||||
// should('ProjectivePoint#multiplyUnsafe', () => {
|
// should('ProjectivePoint#multiplyUnsafe', () => {
|
||||||
// const p0 = new secp.ProjectivePoint(
|
// const p0 = new secp.ProjectivePoint(
|
||||||
// 55066263022277343669578718895168534326250603453777594175500187360389116729240n,
|
// 55066263022277343669578718895168534326250603453777594175500187360389116729240n,
|
||||||
// 32670510020758816978083085130507043184471273380659243275938904335757337482424n,
|
// 32670510020758816978083085130507043184471273380659243275938904335757337482424n,
|
||||||
// 1n
|
// 1n
|
||||||
// );
|
// );
|
||||||
// const z = 106011723082030650010038151861333186846790370053628296836951575624442507889495n;
|
// const z = 106011723082030650010038151861333186846790370053628296836951575624442507889495n;
|
||||||
// console.log(p0.multiply(z));
|
// console.log(p0.multiply(z));
|
||||||
// console.log(secp.ProjectivePoint.normalizeZ([p0.multiplyUnsafe(z)])[0])
|
// console.log(secp.ProjectivePoint.normalizeZ([p0.multiplyUnsafe(z)])[0])
|
||||||
// });
|
// });
|
||||||
|
describe('Signature', () => {
|
||||||
should('secp256k1.Signature.fromCompactHex() roundtrip', () => {
|
should('.fromCompactHex() roundtrip', () => {
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(FC_BIGINT, FC_BIGINT, (r, s) => {
|
fc.property(FC_BIGINT, FC_BIGINT, (r, s) => {
|
||||||
const sig = new secp.Signature(r, s);
|
const sig = new secp.Signature(r, s);
|
||||||
deepStrictEqual(secp.Signature.fromCompact(sig.toCompactHex()), sig);
|
deepStrictEqual(secp.Signature.fromCompact(sig.toCompactHex()), sig);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
should('secp256k1.Signature.fromDERHex() roundtrip', () => {
|
should('.fromDERHex() roundtrip', () => {
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(FC_BIGINT, FC_BIGINT, (r, s) => {
|
fc.property(FC_BIGINT, FC_BIGINT, (r, s) => {
|
||||||
const sig = new secp.Signature(r, s);
|
const sig = new secp.Signature(r, s);
|
||||||
deepStrictEqual(secp.Signature.fromDER(sig.toDERHex()), sig);
|
deepStrictEqual(sigFromDER(sigToDER(sig)), sig);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
should('secp256k1.sign()/should create deterministic signatures with RFC 6979', () => {
|
describe('sign()', () => {
|
||||||
|
should('create deterministic signatures with RFC 6979', () => {
|
||||||
for (const vector of ecdsa.valid) {
|
for (const vector of ecdsa.valid) {
|
||||||
let usig = secp.sign(vector.m, vector.d);
|
let usig = secp.sign(vector.m, vector.d);
|
||||||
let sig = usig.toCompactHex();
|
let sig = usig.toCompactHex();
|
||||||
@@ -201,20 +207,20 @@ should('secp256k1.sign()/should create deterministic signatures with RFC 6979',
|
|||||||
deepStrictEqual(sig.slice(0, 64), vsig.slice(0, 64));
|
deepStrictEqual(sig.slice(0, 64), vsig.slice(0, 64));
|
||||||
deepStrictEqual(sig.slice(64, 128), vsig.slice(64, 128));
|
deepStrictEqual(sig.slice(64, 128), vsig.slice(64, 128));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
should('secp256k1.sign()/should not create invalid deterministic signatures with RFC 6979', () => {
|
should('not create invalid deterministic signatures with RFC 6979', () => {
|
||||||
for (const vector of ecdsa.invalid.sign) {
|
for (const vector of ecdsa.invalid.sign) {
|
||||||
throws(() => secp.sign(vector.m, vector.d));
|
throws(() => secp.sign(vector.m, vector.d));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
should('secp256k1.sign()/edge cases', () => {
|
should('edge cases', () => {
|
||||||
throws(() => secp.sign());
|
throws(() => secp.sign());
|
||||||
throws(() => secp.sign(''));
|
throws(() => secp.sign(''));
|
||||||
});
|
});
|
||||||
|
|
||||||
should('secp256k1.sign()/should create correct DER encoding against libsecp256k1', () => {
|
should('create correct DER encoding against libsecp256k1', () => {
|
||||||
const CASES = [
|
const CASES = [
|
||||||
[
|
[
|
||||||
'd1a9dc8ed4e46a6a3e5e594615ca351d7d7ef44df1e4c94c1802f3592183794b',
|
'd1a9dc8ed4e46a6a3e5e594615ca351d7d7ef44df1e4c94c1802f3592183794b',
|
||||||
@@ -229,15 +235,17 @@ should('secp256k1.sign()/should create correct DER encoding against libsecp256k1
|
|||||||
'3045022100d18990bba7832bb283e3ecf8700b67beb39acc73f4200ed1c331247c46edccc602202e5c8bbfe47ae159512c583b30a3fa86575cddc62527a03de7756517ae4c6c73',
|
'3045022100d18990bba7832bb283e3ecf8700b67beb39acc73f4200ed1c331247c46edccc602202e5c8bbfe47ae159512c583b30a3fa86575cddc62527a03de7756517ae4c6c73',
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
const privKey = hexToBytes('0101010101010101010101010101010101010101010101010101010101010101');
|
const privKey = hexToBytes(
|
||||||
|
'0101010101010101010101010101010101010101010101010101010101010101'
|
||||||
|
);
|
||||||
for (const [msg, exp] of CASES) {
|
for (const [msg, exp] of CASES) {
|
||||||
const res = secp.sign(msg, privKey, { extraEntropy: undefined });
|
const res = secp.sign(msg, privKey, { extraEntropy: undefined });
|
||||||
deepStrictEqual(res.toDERHex(), exp);
|
deepStrictEqual(sigToDER(res), exp);
|
||||||
const rs = secp.Signature.fromDER(res.toDERHex()).toCompactHex();
|
const rs = sigFromDER(sigToDER(res)).toCompactHex();
|
||||||
deepStrictEqual(secp.Signature.fromCompact(rs).toDERHex(), exp);
|
deepStrictEqual(sigToDER(secp.Signature.fromCompact(rs)), exp);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
should('secp256k1.sign()/sign ecdsa extraData', () => {
|
should('handle {extraData} option', () => {
|
||||||
const ent1 = '0000000000000000000000000000000000000000000000000000000000000000';
|
const ent1 = '0000000000000000000000000000000000000000000000000000000000000000';
|
||||||
const ent2 = '0000000000000000000000000000000000000000000000000000000000000001';
|
const ent2 = '0000000000000000000000000000000000000000000000000000000000000001';
|
||||||
const ent3 = '6e723d3fd94ed5d2b6bdd4f123364b0f3ca52af829988a63f8afe91d29db1c33';
|
const ent3 = '6e723d3fd94ed5d2b6bdd4f123364b0f3ca52af829988a63f8afe91d29db1c33';
|
||||||
@@ -256,35 +264,37 @@ should('secp256k1.sign()/sign ecdsa extraData', () => {
|
|||||||
deepStrictEqual(sign(ent4), e.extraEntropyN);
|
deepStrictEqual(sign(ent4), e.extraEntropyN);
|
||||||
deepStrictEqual(sign(ent5), e.extraEntropyMax);
|
deepStrictEqual(sign(ent5), e.extraEntropyMax);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
should('secp256k1.verify()/should verify signature', () => {
|
describe('verify()', () => {
|
||||||
|
should('verify signature', () => {
|
||||||
const MSG = '01'.repeat(32);
|
const MSG = '01'.repeat(32);
|
||||||
const PRIV_KEY = 0x2n;
|
const PRIV_KEY = 0x2n;
|
||||||
const signature = secp.sign(MSG, PRIV_KEY);
|
const signature = secp.sign(MSG, PRIV_KEY);
|
||||||
const publicKey = secp.getPublicKey(PRIV_KEY);
|
const publicKey = secp.getPublicKey(PRIV_KEY);
|
||||||
deepStrictEqual(publicKey.length, 65);
|
deepStrictEqual(publicKey.length, 33);
|
||||||
deepStrictEqual(secp.verify(signature, MSG, publicKey), true);
|
deepStrictEqual(secp.verify(signature, MSG, publicKey), true);
|
||||||
});
|
});
|
||||||
should('secp256k1.verify()/should not verify signature with wrong public key', () => {
|
should(' not verify signature with wrong public key', () => {
|
||||||
const MSG = '01'.repeat(32);
|
const MSG = '01'.repeat(32);
|
||||||
const PRIV_KEY = 0x2n;
|
const PRIV_KEY = '01'.repeat(32);
|
||||||
const WRONG_PRIV_KEY = 0x22n;
|
const WRONG_PRIV_KEY = '02'.repeat(32);
|
||||||
const signature = secp.sign(MSG, PRIV_KEY);
|
const signature = secp.sign(MSG, PRIV_KEY);
|
||||||
const publicKey = secp.Point.fromPrivateKey(WRONG_PRIV_KEY).toHex();
|
const publicKey = Point.fromPrivateKey(WRONG_PRIV_KEY).toHex();
|
||||||
deepStrictEqual(publicKey.length, 130);
|
deepStrictEqual(publicKey.length, 66);
|
||||||
deepStrictEqual(secp.verify(signature, MSG, publicKey), false);
|
deepStrictEqual(secp.verify(signature, MSG, publicKey), false);
|
||||||
});
|
});
|
||||||
should('secp256k1.verify()/should not verify signature with wrong hash', () => {
|
should('not verify signature with wrong hash', () => {
|
||||||
const MSG = '01'.repeat(32);
|
const MSG = '01'.repeat(32);
|
||||||
const PRIV_KEY = 0x2n;
|
const PRIV_KEY = 0x2n;
|
||||||
const WRONG_MSG = '11'.repeat(32);
|
const WRONG_MSG = '11'.repeat(32);
|
||||||
const signature = secp.sign(MSG, PRIV_KEY);
|
const signature = secp.sign(MSG, PRIV_KEY);
|
||||||
const publicKey = secp.getPublicKey(PRIV_KEY);
|
const publicKey = secp.getPublicKey(PRIV_KEY);
|
||||||
deepStrictEqual(publicKey.length, 65);
|
deepStrictEqual(publicKey.length, 33);
|
||||||
deepStrictEqual(secp.verify(signature, WRONG_MSG, publicKey), false);
|
deepStrictEqual(secp.verify(signature, WRONG_MSG, publicKey), false);
|
||||||
});
|
});
|
||||||
should('secp256k1.verify()/should verify random signatures', () =>
|
should('verify random signatures', () =>
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(FC_BIGINT, fc.hexaString({ minLength: 64, maxLength: 64 }), (privKey, msg) => {
|
fc.property(FC_BIGINT, fc.hexaString({ minLength: 64, maxLength: 64 }), (privKey, msg) => {
|
||||||
const pub = secp.getPublicKey(privKey);
|
const pub = secp.getPublicKey(privKey);
|
||||||
@@ -292,18 +302,19 @@ should('secp256k1.verify()/should verify random signatures', () =>
|
|||||||
deepStrictEqual(secp.verify(sig, msg, pub), true);
|
deepStrictEqual(secp.verify(sig, msg, pub), true);
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
should('secp256k1.verify()/should not verify signature with invalid r/s', () => {
|
should('not verify signature with invalid r/s', () => {
|
||||||
const msg = new Uint8Array([
|
const msg = new Uint8Array([
|
||||||
0xbb, 0x5a, 0x52, 0xf4, 0x2f, 0x9c, 0x92, 0x61, 0xed, 0x43, 0x61, 0xf5, 0x94, 0x22, 0xa1, 0xe3,
|
0xbb, 0x5a, 0x52, 0xf4, 0x2f, 0x9c, 0x92, 0x61, 0xed, 0x43, 0x61, 0xf5, 0x94, 0x22, 0xa1,
|
||||||
0x00, 0x36, 0xe7, 0xc3, 0x2b, 0x27, 0x0c, 0x88, 0x07, 0xa4, 0x19, 0xfe, 0xca, 0x60, 0x50, 0x23,
|
0xe3, 0x00, 0x36, 0xe7, 0xc3, 0x2b, 0x27, 0x0c, 0x88, 0x07, 0xa4, 0x19, 0xfe, 0xca, 0x60,
|
||||||
|
0x50, 0x23,
|
||||||
]);
|
]);
|
||||||
const x = 100260381870027870612475458630405506840396644859280795015145920502443964769584n;
|
const x = 100260381870027870612475458630405506840396644859280795015145920502443964769584n;
|
||||||
const y = 41096923727651821103518389640356553930186852801619204169823347832429067794568n;
|
const y = 41096923727651821103518389640356553930186852801619204169823347832429067794568n;
|
||||||
const r = 1n;
|
const r = 1n;
|
||||||
const s = 115792089237316195423570985008687907852837564279074904382605163141518162728904n;
|
const s = 115792089237316195423570985008687907852837564279074904382605163141518162728904n;
|
||||||
|
|
||||||
const pub = new secp.Point(x, y);
|
const pub = new Point(x, y, 1n).toRawBytes();
|
||||||
const signature = new secp.Signature(2n, 2n);
|
const signature = new secp.Signature(2n, 2n);
|
||||||
signature.r = r;
|
signature.r = r;
|
||||||
signature.s = s;
|
signature.s = s;
|
||||||
@@ -311,98 +322,78 @@ should('secp256k1.verify()/should not verify signature with invalid r/s', () =>
|
|||||||
const verified = secp.verify(signature, msg, pub);
|
const verified = secp.verify(signature, msg, pub);
|
||||||
// Verifies, but it shouldn't, because signature S > curve order
|
// Verifies, but it shouldn't, because signature S > curve order
|
||||||
deepStrictEqual(verified, false);
|
deepStrictEqual(verified, false);
|
||||||
});
|
});
|
||||||
should('secp256k1.verify()/should not verify msg = curve order', () => {
|
should('not verify msg = curve order', () => {
|
||||||
const msg = 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141';
|
const msg = 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141';
|
||||||
const x = 55066263022277343669578718895168534326250603453777594175500187360389116729240n;
|
const x = 55066263022277343669578718895168534326250603453777594175500187360389116729240n;
|
||||||
const y = 32670510020758816978083085130507043184471273380659243275938904335757337482424n;
|
const y = 32670510020758816978083085130507043184471273380659243275938904335757337482424n;
|
||||||
const r = 104546003225722045112039007203142344920046999340768276760147352389092131869133n;
|
const r = 104546003225722045112039007203142344920046999340768276760147352389092131869133n;
|
||||||
const s = 96900796730960181123786672629079577025401317267213807243199432755332205217369n;
|
const s = 96900796730960181123786672629079577025401317267213807243199432755332205217369n;
|
||||||
const pub = new secp.Point(x, y);
|
const pub = new Point(x, y, 1n).toRawBytes();
|
||||||
const sig = new secp.Signature(r, s);
|
const sig = new secp.Signature(r, s);
|
||||||
deepStrictEqual(secp.verify(sig, msg, pub), false);
|
deepStrictEqual(secp.verify(sig, msg, pub), false);
|
||||||
});
|
});
|
||||||
should('secp256k1.verify()/should verify non-strict msg bb5a...', () => {
|
should('verify non-strict msg bb5a...', () => {
|
||||||
const msg = 'bb5a52f42f9c9261ed4361f59422a1e30036e7c32b270c8807a419feca605023';
|
const msg = 'bb5a52f42f9c9261ed4361f59422a1e30036e7c32b270c8807a419feca605023';
|
||||||
const x = 3252872872578928810725465493269682203671229454553002637820453004368632726370n;
|
const x = 3252872872578928810725465493269682203671229454553002637820453004368632726370n;
|
||||||
const y = 17482644437196207387910659778872952193236850502325156318830589868678978890912n;
|
const y = 17482644437196207387910659778872952193236850502325156318830589868678978890912n;
|
||||||
const r = 432420386565659656852420866390673177323n;
|
const r = 432420386565659656852420866390673177323n;
|
||||||
const s = 115792089237316195423570985008687907852837564279074904382605163141518161494334n;
|
const s = 115792089237316195423570985008687907852837564279074904382605163141518161494334n;
|
||||||
const pub = new secp.Point(x, y);
|
const pub = new Point(x, y, 1n).toRawBytes();
|
||||||
const sig = new secp.Signature(r, s);
|
const sig = new secp.Signature(r, s);
|
||||||
deepStrictEqual(secp.verify(sig, msg, pub, { strict: false }), true);
|
deepStrictEqual(secp.verify(sig, msg, pub, { lowS: false }), true);
|
||||||
});
|
});
|
||||||
should(
|
should('not verify invalid deterministic signatures with RFC 6979', () => {
|
||||||
'secp256k1.verify()/should not verify invalid deterministic signatures with RFC 6979',
|
|
||||||
() => {
|
|
||||||
for (const vector of ecdsa.invalid.verify) {
|
for (const vector of ecdsa.invalid.verify) {
|
||||||
const res = secp.verify(vector.signature, vector.m, vector.Q);
|
const res = secp.verify(vector.signature, vector.m, vector.Q);
|
||||||
deepStrictEqual(res, false);
|
deepStrictEqual(res, false);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// index,secret key,public key,aux_rand,message,signature,verification result,comment
|
|
||||||
const vectors = schCsv
|
|
||||||
.split('\n')
|
|
||||||
.map((line) => line.split(','))
|
|
||||||
.slice(1, -1);
|
|
||||||
for (let vec of vectors) {
|
|
||||||
const [index, sec, pub, rnd, msg, expSig, passes, comment] = vec;
|
|
||||||
should(`sign with Schnorr scheme vector ${index}`, () => {
|
|
||||||
if (sec) {
|
|
||||||
deepStrictEqual(hex(schnorr.getPublicKey(sec)), pub.toLowerCase());
|
|
||||||
const sig = schnorr.sign(msg, sec, rnd);
|
|
||||||
deepStrictEqual(hex(sig), expSig.toLowerCase());
|
|
||||||
deepStrictEqual(schnorr.verify(sig, msg, pub), true);
|
|
||||||
} else {
|
|
||||||
const passed = schnorr.verify(expSig, msg, pub);
|
|
||||||
deepStrictEqual(passed, passes === 'TRUE');
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
|
describe('recoverPublicKey()', () => {
|
||||||
should('secp256k1.recoverPublicKey()/should recover public key from recovery bit', () => {
|
should('recover public key from recovery bit', () => {
|
||||||
const message = '00000000000000000000000000000000000000000000000000000000deadbeef';
|
const message = '00000000000000000000000000000000000000000000000000000000deadbeef';
|
||||||
const privateKey = 123456789n;
|
const privateKey = 123456789n;
|
||||||
const publicKey = secp.Point.fromHex(secp.getPublicKey(privateKey)).toHex(false);
|
const publicKey = Point.fromHex(secp.getPublicKey(privateKey)).toHex(false);
|
||||||
const sig = secp.sign(message, privateKey);
|
const sig = secp.sign(message, privateKey);
|
||||||
const recoveredPubkey = sig.recoverPublicKey(message);
|
const recoveredPubkey = sig.recoverPublicKey(message);
|
||||||
// const recoveredPubkey = secp.recoverPublicKey(message, signature, recovery);
|
// const recoveredPubkey = secp.recoverPublicKey(message, signature, recovery);
|
||||||
deepStrictEqual(recoveredPubkey !== null, true);
|
deepStrictEqual(recoveredPubkey !== null, true);
|
||||||
deepStrictEqual(recoveredPubkey.toHex(), publicKey);
|
deepStrictEqual(recoveredPubkey.toHex(false), publicKey);
|
||||||
deepStrictEqual(secp.verify(sig, message, publicKey), true);
|
deepStrictEqual(secp.verify(sig, message, publicKey), true);
|
||||||
});
|
});
|
||||||
should('secp256k1.recoverPublicKey()/should not recover zero points', () => {
|
should('not recover zero points', () => {
|
||||||
const msgHash = '6b8d2c81b11b2d699528dde488dbdf2f94293d0d33c32e347f255fa4a6c1f0a9';
|
const msgHash = '6b8d2c81b11b2d699528dde488dbdf2f94293d0d33c32e347f255fa4a6c1f0a9';
|
||||||
const sig =
|
const sig =
|
||||||
'79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817986b8d2c81b11b2d699528dde488dbdf2f94293d0d33c32e347f255fa4a6c1f0a9';
|
'79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817986b8d2c81b11b2d699528dde488dbdf2f94293d0d33c32e347f255fa4a6c1f0a9';
|
||||||
const recovery = 0;
|
const recovery = 0;
|
||||||
throws(() => secp.recoverPublicKey(msgHash, sig, recovery));
|
throws(() => secp.recoverPublicKey(msgHash, sig, recovery));
|
||||||
});
|
});
|
||||||
should('secp256k1.recoverPublicKey()/should handle all-zeros msghash', () => {
|
should('handle all-zeros msghash', () => {
|
||||||
const privKey = secp.utils.randomPrivateKey();
|
const privKey = secp.utils.randomPrivateKey();
|
||||||
const pub = secp.getPublicKey(privKey);
|
const pub = secp.getPublicKey(privKey);
|
||||||
const zeros = '0000000000000000000000000000000000000000000000000000000000000000';
|
const zeros = '0000000000000000000000000000000000000000000000000000000000000000';
|
||||||
const sig = secp.sign(zeros, privKey, { recovered: true });
|
const sig = secp.sign(zeros, privKey);
|
||||||
const recoveredKey = sig.recoverPublicKey(zeros);
|
const recoveredKey = sig.recoverPublicKey(zeros);
|
||||||
deepStrictEqual(recoveredKey.toRawBytes(), pub);
|
deepStrictEqual(recoveredKey.toRawBytes(), pub);
|
||||||
});
|
});
|
||||||
should('secp256k1.recoverPublicKey()/should handle RFC 6979 vectors', () => {
|
should('handle RFC 6979 vectors', () => {
|
||||||
for (const vector of ecdsa.valid) {
|
for (const vector of ecdsa.valid) {
|
||||||
let usig = secp.sign(vector.m, vector.d);
|
let usig = secp.sign(vector.m, vector.d);
|
||||||
let sig = usig.toDERHex();
|
let sig = sigToDER(usig);
|
||||||
const vpub = secp.getPublicKey(vector.d);
|
const vpub = secp.getPublicKey(vector.d);
|
||||||
const recovered = usig.recoverPublicKey(vector.m);
|
const recovered = usig.recoverPublicKey(vector.m);
|
||||||
deepStrictEqual(recovered.toHex(), hex(vpub));
|
deepStrictEqual(recovered.toHex(), hex(vpub));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// TODO: Real implementation.
|
describe('getSharedSecret()', () => {
|
||||||
function derToPub(der) {
|
// TODO: Real implementation.
|
||||||
|
function derToPub(der) {
|
||||||
return der.slice(46);
|
return der.slice(46);
|
||||||
}
|
}
|
||||||
should('secp256k1.getSharedSecret()/should produce correct results', () => {
|
should('produce correct results', () => {
|
||||||
// TODO: Once der is there, run all tests.
|
// TODO: Once der is there, run all tests.
|
||||||
for (const vector of ecdh.testGroups[0].tests.slice(0, 230)) {
|
for (const vector of ecdh.testGroups[0].tests.slice(0, 230)) {
|
||||||
if (vector.result === 'invalid' || vector.private.length !== 64) {
|
if (vector.result === 'invalid' || vector.private.length !== 64) {
|
||||||
@@ -414,8 +405,8 @@ should('secp256k1.getSharedSecret()/should produce correct results', () => {
|
|||||||
deepStrictEqual(hex(res.slice(1)), `${vector.shared}`);
|
deepStrictEqual(hex(res.slice(1)), `${vector.shared}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
should('secp256k1.getSharedSecret()/priv/pub order matters', () => {
|
should('priv/pub order matters', () => {
|
||||||
for (const vector of ecdh.testGroups[0].tests.slice(0, 100)) {
|
for (const vector of ecdh.testGroups[0].tests.slice(0, 100)) {
|
||||||
if (vector.result === 'valid') {
|
if (vector.result === 'valid') {
|
||||||
let priv = vector.private;
|
let priv = vector.private;
|
||||||
@@ -423,92 +414,100 @@ should('secp256k1.getSharedSecret()/priv/pub order matters', () => {
|
|||||||
throws(() => secp.getSharedSecret(derToPub(vector.public), priv, true));
|
throws(() => secp.getSharedSecret(derToPub(vector.public), priv, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
should('secp256k1.getSharedSecret()/rejects invalid keys', () => {
|
should('reject invalid keys', () => {
|
||||||
throws(() => secp.getSharedSecret('01', '02'));
|
throws(() => secp.getSharedSecret('01', '02'));
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
should('secp256k1.utils.isValidPrivateKey()', () => {
|
should('utils.isValidPrivateKey()', () => {
|
||||||
for (const vector of privates.valid.isPrivate) {
|
for (const vector of privates.valid.isPrivate) {
|
||||||
const { d, expected } = vector;
|
const { d, expected } = vector;
|
||||||
deepStrictEqual(secp.utils.isValidPrivateKey(d), expected);
|
deepStrictEqual(secp.utils.isValidPrivateKey(d), expected);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const normal = secp.utils._normalizePrivateKey;
|
should('have proper curve equation in assertValidity()', () => {
|
||||||
const tweakUtils = {
|
throws(() => {
|
||||||
|
const { Fp } = secp.CURVE;
|
||||||
|
let point = new Point(Fp.create(-2n), Fp.create(-1n), Fp.create(1n));
|
||||||
|
point.assertValidity();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('tweak utilities (legacy)', () => {
|
||||||
|
const normal = secp.utils.normPrivateKeyToScalar;
|
||||||
|
const tweakUtils = {
|
||||||
privateAdd: (privateKey, tweak) => {
|
privateAdd: (privateKey, tweak) => {
|
||||||
const p = normal(privateKey);
|
return numberToBytesBE(mod(normal(privateKey) + normal(tweak), secp.CURVE.n), 32);
|
||||||
const t = normal(tweak);
|
|
||||||
return secp.utils._bigintToBytes(secp.utils.mod(p + t, secp.CURVE.n));
|
|
||||||
},
|
},
|
||||||
|
|
||||||
privateNegate: (privateKey) => {
|
privateNegate: (privateKey) => {
|
||||||
const p = normal(privateKey);
|
return numberToBytesBE(mod(-normal(privateKey), secp.CURVE.n), 32);
|
||||||
return secp.utils._bigintToBytes(secp.CURVE.n - p);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
pointAddScalar: (p, tweak, isCompressed) => {
|
pointAddScalar: (p, tweak, isCompressed) => {
|
||||||
const P = secp.Point.fromHex(p);
|
const tweaked = Point.fromHex(p).add(Point.fromPrivateKey(tweak));
|
||||||
const t = normal(tweak);
|
if (tweaked.equals(Point.ZERO)) throw new Error('Tweaked point at infinity');
|
||||||
const Q = secp.Point.BASE.multiplyAndAddUnsafe(P, t, 1n);
|
return tweaked.toRawBytes(isCompressed);
|
||||||
if (!Q) throw new Error('Tweaked point at infinity');
|
|
||||||
return Q.toRawBytes(isCompressed);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
pointMultiply: (p, tweak, isCompressed) => {
|
pointMultiply: (p, tweak, isCompressed) => {
|
||||||
const P = secp.Point.fromHex(p);
|
if (typeof tweak === 'string') tweak = hexToBytes(tweak);
|
||||||
const h = typeof tweak === 'string' ? tweak : bytesToHex(tweak);
|
const t = bytesToNumberBE(tweak);
|
||||||
const t = BigInt(`0x${h}`);
|
return Point.fromHex(p).multiply(t).toRawBytes(isCompressed);
|
||||||
return P.multiply(t).toRawBytes(isCompressed);
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
should('secp256k1.privateAdd()', () => {
|
should('privateAdd()', () => {
|
||||||
for (const vector of privates.valid.add) {
|
for (const vector of privates.valid.add) {
|
||||||
const { a, b, expected } = vector;
|
const { a, b, expected } = vector;
|
||||||
deepStrictEqual(bytesToHex(tweakUtils.privateAdd(a, b)), expected);
|
deepStrictEqual(hex(tweakUtils.privateAdd(a, b)), expected);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
should('secp256k1.privateNegate()', () => {
|
should('privateNegate()', () => {
|
||||||
for (const vector of privates.valid.negate) {
|
for (const vector of privates.valid.negate) {
|
||||||
const { a, expected } = vector;
|
const { a, expected } = vector;
|
||||||
deepStrictEqual(bytesToHex(tweakUtils.privateNegate(a)), expected);
|
deepStrictEqual(hex(tweakUtils.privateNegate(a)), expected);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
should('secp256k1.pointAddScalar()', () => {
|
should('pointAddScalar()', () => {
|
||||||
for (const vector of points.valid.pointAddScalar) {
|
for (const vector of points.valid.pointAddScalar) {
|
||||||
const { description, P, d, expected } = vector;
|
const { description, P, d, expected } = vector;
|
||||||
const compressed = !!expected && expected.length === 66; // compressed === 33 bytes
|
const compressed = !!expected && expected.length === 66; // compressed === 33 bytes
|
||||||
deepStrictEqual(bytesToHex(tweakUtils.pointAddScalar(P, d, compressed)), expected);
|
deepStrictEqual(hex(tweakUtils.pointAddScalar(P, d, compressed)), expected);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
should('secp256k1.pointAddScalar() invalid', () => {
|
should('pointAddScalar() invalid', () => {
|
||||||
for (const vector of points.invalid.pointAddScalar) {
|
for (const vector of points.invalid.pointAddScalar) {
|
||||||
const { P, d, exception } = vector;
|
const { P, d, exception } = vector;
|
||||||
throws(() => tweakUtils.pointAddScalar(P, d));
|
throws(() => tweakUtils.pointAddScalar(P, d));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
should('secp256k1.pointMultiply()', () => {
|
should('pointMultiply()', () => {
|
||||||
for (const vector of points.valid.pointMultiply) {
|
for (const vector of points.valid.pointMultiply) {
|
||||||
const { P, d, expected } = vector;
|
const { P, d, expected } = vector;
|
||||||
deepStrictEqual(bytesToHex(tweakUtils.pointMultiply(P, d, true)), expected);
|
deepStrictEqual(hex(tweakUtils.pointMultiply(P, d, true)), expected);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
should('secp256k1.pointMultiply() invalid', () => {
|
should('pointMultiply() invalid', () => {
|
||||||
for (const vector of points.invalid.pointMultiply) {
|
for (const vector of points.invalid.pointMultiply) {
|
||||||
const { P, d, exception } = vector;
|
const { P, d, exception } = vector;
|
||||||
throws(() => tweakUtils.pointMultiply(P, d));
|
throws(() => tweakUtils.pointMultiply(P, d));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
should('secp256k1.wychenproof vectors', () => {
|
should('wycheproof vectors', () => {
|
||||||
for (let group of wp.testGroups) {
|
for (let group of wp.testGroups) {
|
||||||
const pubKey = secp.Point.fromHex(group.key.uncompressed);
|
// const pubKey = Point.fromHex().toRawBytes();
|
||||||
|
const pubKey = group.key.uncompressed;
|
||||||
for (let test of group.tests) {
|
for (let test of group.tests) {
|
||||||
const m = secp.CURVE.hash(hexToBytes(test.msg));
|
const h = selectHash(secp);
|
||||||
|
|
||||||
|
const m = h(hexToBytes(test.msg));
|
||||||
if (test.result === 'valid' || test.result === 'acceptable') {
|
if (test.result === 'valid' || test.result === 'acceptable') {
|
||||||
const verified = secp.verify(test.sig, m, pubKey);
|
const verified = secp.verify(normVerifySig(test.sig), m, pubKey);
|
||||||
if (secp.Signature.fromDER(test.sig).hasHighS()) {
|
if (sigFromDER(test.sig).hasHighS()) {
|
||||||
deepStrictEqual(verified, false);
|
deepStrictEqual(verified, false);
|
||||||
} else {
|
} else {
|
||||||
deepStrictEqual(verified, true);
|
deepStrictEqual(verified, true);
|
||||||
@@ -527,6 +526,7 @@ should('secp256k1.wychenproof vectors', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// ESM is broken.
|
// ESM is broken.
|
||||||
|
|||||||
@@ -1,57 +1,60 @@
|
|||||||
import { deepStrictEqual, throws } from 'assert';
|
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 starknet from '../../esm/stark.js';
|
||||||
import { default as issue2 } from './fixtures/issue2.json' assert { type: 'json' };
|
import { default as issue2 } from './fixtures/issue2.json' assert { type: 'json' };
|
||||||
|
import * as bip32 from '@scure/bip32';
|
||||||
|
import * as bip39 from '@scure/bip39';
|
||||||
|
|
||||||
should('Basic elliptic sanity check', () => {
|
describe('starknet basic', () => {
|
||||||
const g1 = starknet.Point.BASE;
|
should('Basic elliptic sanity check', () => {
|
||||||
|
const g1 = starknet.ProjectivePoint.BASE;
|
||||||
deepStrictEqual(
|
deepStrictEqual(
|
||||||
g1.x.toString(16),
|
g1.toAffine().x.toString(16),
|
||||||
'1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca'
|
'1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca'
|
||||||
);
|
);
|
||||||
deepStrictEqual(
|
deepStrictEqual(
|
||||||
g1.y.toString(16),
|
g1.toAffine().y.toString(16),
|
||||||
'5668060aa49730b7be4801df46ec62de53ecd11abe43a32873000c36e8dc1f'
|
'5668060aa49730b7be4801df46ec62de53ecd11abe43a32873000c36e8dc1f'
|
||||||
);
|
);
|
||||||
const g2 = g1.double();
|
const g2 = g1.double();
|
||||||
deepStrictEqual(
|
deepStrictEqual(
|
||||||
g2.x.toString(16),
|
g2.toAffine().x.toString(16),
|
||||||
'759ca09377679ecd535a81e83039658bf40959283187c654c5416f439403cf5'
|
'759ca09377679ecd535a81e83039658bf40959283187c654c5416f439403cf5'
|
||||||
);
|
);
|
||||||
deepStrictEqual(
|
deepStrictEqual(
|
||||||
g2.y.toString(16),
|
g2.toAffine().y.toString(16),
|
||||||
'6f524a3400e7708d5c01a28598ad272e7455aa88778b19f93b562d7a9646c41'
|
'6f524a3400e7708d5c01a28598ad272e7455aa88778b19f93b562d7a9646c41'
|
||||||
);
|
);
|
||||||
const g3 = g2.add(g1);
|
const g3 = g2.add(g1);
|
||||||
deepStrictEqual(
|
deepStrictEqual(
|
||||||
g3.x.toString(16),
|
g3.toAffine().x.toString(16),
|
||||||
'411494b501a98abd8262b0da1351e17899a0c4ef23dd2f96fec5ba847310b20'
|
'411494b501a98abd8262b0da1351e17899a0c4ef23dd2f96fec5ba847310b20'
|
||||||
);
|
);
|
||||||
deepStrictEqual(
|
deepStrictEqual(
|
||||||
g3.y.toString(16),
|
g3.toAffine().y.toString(16),
|
||||||
'7e1b3ebac08924d2c26f409549191fcf94f3bf6f301ed3553e22dfb802f0686'
|
'7e1b3ebac08924d2c26f409549191fcf94f3bf6f301ed3553e22dfb802f0686'
|
||||||
);
|
);
|
||||||
const g32 = g1.multiply(3);
|
const g32 = g1.multiply(3n);
|
||||||
deepStrictEqual(
|
deepStrictEqual(
|
||||||
g32.x.toString(16),
|
g32.toAffine().x.toString(16),
|
||||||
'411494b501a98abd8262b0da1351e17899a0c4ef23dd2f96fec5ba847310b20'
|
'411494b501a98abd8262b0da1351e17899a0c4ef23dd2f96fec5ba847310b20'
|
||||||
);
|
);
|
||||||
deepStrictEqual(
|
deepStrictEqual(
|
||||||
g32.y.toString(16),
|
g32.toAffine().y.toString(16),
|
||||||
'7e1b3ebac08924d2c26f409549191fcf94f3bf6f301ed3553e22dfb802f0686'
|
'7e1b3ebac08924d2c26f409549191fcf94f3bf6f301ed3553e22dfb802f0686'
|
||||||
);
|
);
|
||||||
const minus1 = g1.multiply(starknet.CURVE.n - 1n);
|
const minus1 = g1.multiply(starknet.CURVE.n - 1n);
|
||||||
deepStrictEqual(
|
deepStrictEqual(
|
||||||
minus1.x.toString(16),
|
minus1.toAffine().x.toString(16),
|
||||||
'1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca'
|
'1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca'
|
||||||
);
|
);
|
||||||
deepStrictEqual(
|
deepStrictEqual(
|
||||||
minus1.y.toString(16),
|
minus1.toAffine().y.toString(16),
|
||||||
'7a997f9f55b68e04841b7fe20b9139d21ac132ee541bc5cd78cfff3c91723e2'
|
'7a997f9f55b68e04841b7fe20b9139d21ac132ee541bc5cd78cfff3c91723e2'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
should('Pedersen', () => {
|
should('Pedersen', () => {
|
||||||
deepStrictEqual(
|
deepStrictEqual(
|
||||||
starknet.pedersen(2, 3),
|
starknet.pedersen(2, 3),
|
||||||
'0x5774fa77b3d843ae9167abd61cf80365a9b2b02218fc2f628494b5bdc9b33b8'
|
'0x5774fa77b3d843ae9167abd61cf80365a9b2b02218fc2f628494b5bdc9b33b8'
|
||||||
@@ -64,16 +67,16 @@ should('Pedersen', () => {
|
|||||||
starknet.pedersen(3, 4),
|
starknet.pedersen(3, 4),
|
||||||
'0x262697b88544f733e5c6907c3e1763131e9f14c51ee7951258abbfb29415fbf'
|
'0x262697b88544f733e5c6907c3e1763131e9f14c51ee7951258abbfb29415fbf'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
should('Hash chain', () => {
|
should('Hash chain', () => {
|
||||||
deepStrictEqual(
|
deepStrictEqual(
|
||||||
starknet.hashChain([1, 2, 3]),
|
starknet.hashChain([1, 2, 3]),
|
||||||
'0x5d9d62d4040b977c3f8d2389d494e4e89a96a8b45c44b1368f1cc6ec5418915'
|
'0x5d9d62d4040b977c3f8d2389d494e4e89a96a8b45c44b1368f1cc6ec5418915'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
should('Pedersen hash edgecases', () => {
|
should('Pedersen hash edgecases', () => {
|
||||||
// >>> pedersen_hash(0,0)
|
// >>> pedersen_hash(0,0)
|
||||||
const zero = '0x49ee3eba8c1600700ee1b87eb599f16716b0b1022947733551fde4050ca6804';
|
const zero = '0x49ee3eba8c1600700ee1b87eb599f16716b0b1022947733551fde4050ca6804';
|
||||||
deepStrictEqual(starknet.pedersen(0, 0), zero);
|
deepStrictEqual(starknet.pedersen(0, 0), zero);
|
||||||
@@ -100,9 +103,9 @@ should('Pedersen hash edgecases', () => {
|
|||||||
throws(() => starknet.pedersen(false, false), 'false');
|
throws(() => starknet.pedersen(false, false), 'false');
|
||||||
throws(() => starknet.pedersen(true, true), 'true');
|
throws(() => starknet.pedersen(true, true), 'true');
|
||||||
throws(() => starknet.pedersen(10.1, 10.1), 'float');
|
throws(() => starknet.pedersen(10.1, 10.1), 'float');
|
||||||
});
|
});
|
||||||
|
|
||||||
should('hashChain edgecases', () => {
|
should('hashChain edgecases', () => {
|
||||||
deepStrictEqual(starknet.hashChain([32312321312321312312312321n]), '0x1aba6672c014b4838cc201');
|
deepStrictEqual(starknet.hashChain([32312321312321312312312321n]), '0x1aba6672c014b4838cc201');
|
||||||
deepStrictEqual(
|
deepStrictEqual(
|
||||||
starknet.hashChain([1n, 2n]),
|
starknet.hashChain([1n, 2n]),
|
||||||
@@ -118,9 +121,9 @@ should('hashChain edgecases', () => {
|
|||||||
starknet.hashChain([1, 2]),
|
starknet.hashChain([1, 2]),
|
||||||
'0x5bb9440e27889a364bcb678b1f679ecd1347acdedcbf36e83494f857cc58026'
|
'0x5bb9440e27889a364bcb678b1f679ecd1347acdedcbf36e83494f857cc58026'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
should('Pedersen hash, issue #2', () => {
|
should('Pedersen hash, issue #2', () => {
|
||||||
// Verified with starnet.js
|
// Verified with starnet.js
|
||||||
deepStrictEqual(
|
deepStrictEqual(
|
||||||
starknet.computeHashOnElements(issue2),
|
starknet.computeHashOnElements(issue2),
|
||||||
@@ -134,12 +137,10 @@ should('Pedersen hash, issue #2', () => {
|
|||||||
starknet.computeHashOnElements([1]),
|
starknet.computeHashOnElements([1]),
|
||||||
'0x78d74f61aeaa8286418fd34b3a12a610445eba11d00ecc82ecac2542d55f7a4'
|
'0x78d74f61aeaa8286418fd34b3a12a610445eba11d00ecc82ecac2542d55f7a4'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
import * as bip32 from '@scure/bip32';
|
|
||||||
import * as bip39 from '@scure/bip39';
|
|
||||||
|
|
||||||
should('Seed derivation (example)', () => {
|
should('Seed derivation (example)', () => {
|
||||||
const layer = 'starkex';
|
const layer = 'starkex';
|
||||||
const application = 'starkdeployement';
|
const application = 'starkdeployement';
|
||||||
const mnemonic =
|
const mnemonic =
|
||||||
@@ -153,18 +154,18 @@ should('Seed derivation (example)', () => {
|
|||||||
starknet.grindKey(hdKey.privateKey),
|
starknet.grindKey(hdKey.privateKey),
|
||||||
'6cf0a8bf113352eb863157a45c5e5567abb34f8d32cddafd2c22aa803f4892c'
|
'6cf0a8bf113352eb863157a45c5e5567abb34f8d32cddafd2c22aa803f4892c'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
should('Compressed keys', () => {
|
should('Compressed keys', () => {
|
||||||
const G = starknet.Point.BASE;
|
const G = starknet.ProjectivePoint.BASE;
|
||||||
const half = starknet.CURVE.n / 2n;
|
const half = starknet.CURVE.n / 2n;
|
||||||
const last = starknet.CURVE.n;
|
const last = starknet.CURVE.n;
|
||||||
const vectors = [
|
const vectors = [
|
||||||
1,
|
1n,
|
||||||
2,
|
2n,
|
||||||
3,
|
3n,
|
||||||
4,
|
4n,
|
||||||
5,
|
5n,
|
||||||
half - 5n,
|
half - 5n,
|
||||||
half - 4n,
|
half - 4n,
|
||||||
half - 3n,
|
half - 3n,
|
||||||
@@ -182,17 +183,17 @@ should('Compressed keys', () => {
|
|||||||
last - 2n,
|
last - 2n,
|
||||||
last - 1n,
|
last - 1n,
|
||||||
].map((i) => G.multiply(i));
|
].map((i) => G.multiply(i));
|
||||||
const fixPoint = (pt) => ({ ...pt, _WINDOW_SIZE: undefined });
|
const fixPoint = (pt) => pt.toAffine();
|
||||||
for (const v of vectors) {
|
for (const v of vectors) {
|
||||||
const uncompressed = v.toHex();
|
const uncompressed = v.toHex();
|
||||||
const compressed = v.toHex(true);
|
const compressed = v.toHex(true);
|
||||||
const exp = fixPoint(v);
|
const exp = fixPoint(v);
|
||||||
deepStrictEqual(fixPoint(starknet.Point.fromHex(uncompressed)), exp);
|
deepStrictEqual(fixPoint(starknet.ProjectivePoint.fromHex(uncompressed)), exp);
|
||||||
deepStrictEqual(fixPoint(starknet.Point.fromHex(compressed)), exp);
|
deepStrictEqual(fixPoint(starknet.ProjectivePoint.fromHex(compressed)), exp);
|
||||||
deepStrictEqual(starknet.Point.fromHex(compressed).toHex(), uncompressed);
|
deepStrictEqual(starknet.ProjectivePoint.fromHex(compressed).toHex(), uncompressed);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// ESM is broken.
|
// ESM is broken.
|
||||||
import url from 'url';
|
import url from 'url';
|
||||||
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
|
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as microStark from '../../../lib/esm/stark.js';
|
import * as microStark from '../../../esm/stark.js';
|
||||||
import * as starkwareCrypto from '@starkware-industries/starkware-crypto-utils';
|
import * as starkwareCrypto from '@starkware-industries/starkware-crypto-utils';
|
||||||
import * as bench from 'micro-bmark';
|
import * as bench from 'micro-bmark';
|
||||||
const { run, mark } = bench; // or bench.mark
|
const { run, mark } = bench; // or bench.mark
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
|
import { describe, should } from 'micro-should';
|
||||||
import './basic.test.js';
|
import './basic.test.js';
|
||||||
import './stark.test.js';
|
import './stark.test.js';
|
||||||
import './property.test.js';
|
import './property.test.js';
|
||||||
|
import './poseidon.test.js';
|
||||||
|
|
||||||
|
// ESM is broken.
|
||||||
|
import url from 'url';
|
||||||
|
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
|
||||||
|
should.run();
|
||||||
|
}
|
||||||
|
|||||||
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 '../../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,48 +1,50 @@
|
|||||||
import { deepStrictEqual, throws } from 'assert';
|
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 starknet from '../../esm/stark.js';
|
||||||
import * as fc from 'fast-check';
|
import * as fc from 'fast-check';
|
||||||
|
|
||||||
const FC_BIGINT = fc.bigInt(1n + 1n, starknet.CURVE.n - 1n);
|
const FC_BIGINT = fc.bigInt(1n + 1n, starknet.CURVE.n - 1n);
|
||||||
|
|
||||||
should('Point#toHex() roundtrip', () => {
|
describe('starknet property', () => {
|
||||||
|
should('Point#toHex() roundtrip', () => {
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(FC_BIGINT, (x) => {
|
fc.property(FC_BIGINT, (x) => {
|
||||||
const point1 = starknet.Point.fromPrivateKey(x);
|
const point1 = starknet.ProjectivePoint.fromPrivateKey(x);
|
||||||
const hex = point1.toHex(true);
|
const hex = point1.toHex(true);
|
||||||
deepStrictEqual(starknet.Point.fromHex(hex).toHex(true), hex);
|
deepStrictEqual(starknet.ProjectivePoint.fromHex(hex).toHex(true), hex);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
should('Signature.fromCompactHex() roundtrip', () => {
|
should('Signature.fromCompactHex() roundtrip', () => {
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(FC_BIGINT, FC_BIGINT, (r, s) => {
|
fc.property(FC_BIGINT, FC_BIGINT, (r, s) => {
|
||||||
const sig = new starknet.Signature(r, s);
|
const sig = new starknet.Signature(r, s);
|
||||||
deepStrictEqual(starknet.Signature.fromCompact(sig.toCompactHex()), sig);
|
deepStrictEqual(starknet.Signature.fromCompact(sig.toCompactHex()), sig);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
should('Signature.fromDERHex() roundtrip', () => {
|
should('Signature.fromDERHex() roundtrip', () => {
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(FC_BIGINT, FC_BIGINT, (r, s) => {
|
fc.property(FC_BIGINT, FC_BIGINT, (r, s) => {
|
||||||
const sig = new starknet.Signature(r, s);
|
const sig = new starknet.Signature(r, s);
|
||||||
deepStrictEqual(starknet.Signature.fromDER(sig.toDERHex()), sig);
|
deepStrictEqual(starknet.Signature.fromDER(sig.toDERHex()), sig);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
should('verify()/should verify random signatures', () =>
|
should('verify()/should verify random signatures', () =>
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.asyncProperty(FC_BIGINT, fc.hexaString({ minLength: 64, maxLength: 64 }), (privNum, msg) => {
|
fc.property(FC_BIGINT, fc.hexaString({ minLength: 64, maxLength: 64 }), (privNum, msg) => {
|
||||||
const privKey = privNum.toString(16).padStart(64, '0');
|
const privKey = privNum.toString(16).padStart(64, '0');
|
||||||
const pub = starknet.getPublicKey(privKey);
|
const pub = starknet.getPublicKey(privKey);
|
||||||
const sig = starknet.sign(msg, privKey);
|
const sig = starknet.sign(msg, privKey);
|
||||||
deepStrictEqual(starknet.verify(sig, msg, pub), true);
|
deepStrictEqual(starknet.verify(sig, msg, pub), true);
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
});
|
||||||
|
|
||||||
// ESM is broken.
|
// ESM is broken.
|
||||||
import url from 'url';
|
import url from 'url';
|
||||||
|
|||||||
@@ -1,28 +1,29 @@
|
|||||||
import { deepStrictEqual, throws } from 'assert';
|
import { deepStrictEqual, throws } from 'assert';
|
||||||
import { should } from 'micro-should';
|
import { describe, should } from 'micro-should';
|
||||||
import { hex, utf8 } from '@scure/base';
|
import { utf8ToBytes } from '@noble/hashes/utils';
|
||||||
import * as bip32 from '@scure/bip32';
|
import * as bip32 from '@scure/bip32';
|
||||||
import * as bip39 from '@scure/bip39';
|
import * as bip39 from '@scure/bip39';
|
||||||
import * as starknet from '../../lib/esm/stark.js';
|
import * as starknet from '../../esm/stark.js';
|
||||||
import { default as sigVec } from './fixtures/rfc6979_signature_test_vector.json' assert { type: 'json' };
|
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' };
|
import { default as precomputedKeys } from './fixtures/keys_precomputed.json' assert { type: 'json' };
|
||||||
|
|
||||||
should('Starknet keccak', () => {
|
describe('starknet', () => {
|
||||||
const value = starknet.keccak(utf8.decode('hello'));
|
should('custom keccak', () => {
|
||||||
|
const value = starknet.keccak(utf8ToBytes('hello'));
|
||||||
deepStrictEqual(value, 0x8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8n);
|
deepStrictEqual(value, 0x8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8n);
|
||||||
deepStrictEqual(value < 2n ** 250n, true);
|
deepStrictEqual(value < 2n ** 250n, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
should('RFC6979', () => {
|
should('RFC6979', () => {
|
||||||
for (const msg of sigVec.messages) {
|
for (const msg of sigVec.messages) {
|
||||||
const { r, s } = starknet.sign(msg.hash, sigVec.private_key);
|
const { r, s } = starknet.sign(msg.hash, sigVec.private_key);
|
||||||
// const { r, s } = starknet.Signature.fromDER(sig);
|
// const { r, s } = starknet.Signature.fromDER(sig);
|
||||||
deepStrictEqual(r.toString(10), msg.r);
|
deepStrictEqual(r.toString(10), msg.r);
|
||||||
deepStrictEqual(s.toString(10), msg.s);
|
deepStrictEqual(s.toString(10), msg.s);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
should('Signatures', () => {
|
should('Signatures', () => {
|
||||||
const vectors = [
|
const vectors = [
|
||||||
{
|
{
|
||||||
// Message hash of length 61.
|
// Message hash of length 61.
|
||||||
@@ -59,9 +60,9 @@ should('Signatures', () => {
|
|||||||
deepStrictEqual(s.toString(16), v.s, 's equality');
|
deepStrictEqual(s.toString(16), v.s, 's equality');
|
||||||
deepStrictEqual(starknet.verify(sig, v.msg, publicKey), true, 'verify');
|
deepStrictEqual(starknet.verify(sig, v.msg, publicKey), true, 'verify');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
should('Invalid signatures', () => {
|
should('Invalid signatures', () => {
|
||||||
/*
|
/*
|
||||||
|
|
||||||
it('should not verify invalid signature inputs lengths', () => {
|
it('should not verify invalid signature inputs lengths', () => {
|
||||||
@@ -155,11 +156,11 @@ should('Invalid signatures', () => {
|
|||||||
starkwareCrypto.verify(keyPairPub, msgHash.toString(16), msgSignature)
|
starkwareCrypto.verify(keyPairPub, msgHash.toString(16), msgSignature)
|
||||||
).to.be.false;
|
).to.be.false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
*/
|
*/
|
||||||
});
|
});
|
||||||
|
|
||||||
should('Pedersen', () => {
|
should('Pedersen', () => {
|
||||||
deepStrictEqual(
|
deepStrictEqual(
|
||||||
starknet.pedersen(
|
starknet.pedersen(
|
||||||
'0x3d937c035c878245caf64531a5756109c53068da139362728feb561405371cb',
|
'0x3d937c035c878245caf64531a5756109c53068da139362728feb561405371cb',
|
||||||
@@ -174,13 +175,13 @@ should('Pedersen', () => {
|
|||||||
),
|
),
|
||||||
'0x68cc0b76cddd1dd4ed2301ada9b7c872b23875d5ff837b3a87993e0d9996b87'
|
'0x68cc0b76cddd1dd4ed2301ada9b7c872b23875d5ff837b3a87993e0d9996b87'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
should('Hash chain', () => {
|
should('Hash chain', () => {
|
||||||
deepStrictEqual(starknet.hashChain([1, 2, 3]), starknet.pedersen(1, starknet.pedersen(2, 3)));
|
deepStrictEqual(starknet.hashChain([1, 2, 3]), starknet.pedersen(1, starknet.pedersen(2, 3)));
|
||||||
});
|
});
|
||||||
|
|
||||||
should('Key grinding', () => {
|
should('Key grinding', () => {
|
||||||
deepStrictEqual(
|
deepStrictEqual(
|
||||||
starknet.grindKey('86F3E7293141F20A8BAFF320E8EE4ACCB9D4A4BF2B4D295E8CEE784DB46E0519'),
|
starknet.grindKey('86F3E7293141F20A8BAFF320E8EE4ACCB9D4A4BF2B4D295E8CEE784DB46E0519'),
|
||||||
'5c8c8683596c732541a59e03007b2d30dbbbb873556fe65b5fb63c16688f941'
|
'5c8c8683596c732541a59e03007b2d30dbbbb873556fe65b5fb63c16688f941'
|
||||||
@@ -190,9 +191,9 @@ should('Key grinding', () => {
|
|||||||
starknet.grindKey('94F3E7293141F20A8BAFF320E8EE4ACCB9D4A4BF2B4D295E8CEE784DB46E0595'),
|
starknet.grindKey('94F3E7293141F20A8BAFF320E8EE4ACCB9D4A4BF2B4D295E8CEE784DB46E0595'),
|
||||||
'33880b9aba464c1c01c9f8f5b4fc1134698f9b0a8d18505cab6cdd34d93dc02'
|
'33880b9aba464c1c01c9f8f5b4fc1134698f9b0a8d18505cab6cdd34d93dc02'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
should('Private to stark key', () => {
|
should('Private to stark key', () => {
|
||||||
deepStrictEqual(
|
deepStrictEqual(
|
||||||
starknet.getStarkKey('0x178047D3869489C055D7EA54C014FFB834A069C9595186ABE04EA4D1223A03F'),
|
starknet.getStarkKey('0x178047D3869489C055D7EA54C014FFB834A069C9595186ABE04EA4D1223A03F'),
|
||||||
'0x1895a6a77ae14e7987b9cb51329a5adfb17bd8e7c638f92d6892d76e51cebcf'
|
'0x1895a6a77ae14e7987b9cb51329a5adfb17bd8e7c638f92d6892d76e51cebcf'
|
||||||
@@ -200,9 +201,9 @@ should('Private to stark key', () => {
|
|||||||
for (const [privKey, expectedPubKey] of Object.entries(precomputedKeys)) {
|
for (const [privKey, expectedPubKey] of Object.entries(precomputedKeys)) {
|
||||||
deepStrictEqual(starknet.getStarkKey(privKey), expectedPubKey);
|
deepStrictEqual(starknet.getStarkKey(privKey), expectedPubKey);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
should('Private stark key from eth signature', () => {
|
should('Private stark key from eth signature', () => {
|
||||||
const ethSignature =
|
const ethSignature =
|
||||||
'0x21fbf0696d5e0aa2ef41a2b4ffb623bcaf070461d61cf7251c74161f82fec3a43' +
|
'0x21fbf0696d5e0aa2ef41a2b4ffb623bcaf070461d61cf7251c74161f82fec3a43' +
|
||||||
'70854bc0a34b3ab487c1bc021cd318c734c51ae29374f2beb0e6f2dd49b4bf41c';
|
'70854bc0a34b3ab487c1bc021cd318c734c51ae29374f2beb0e6f2dd49b4bf41c';
|
||||||
@@ -210,9 +211,9 @@ should('Private stark key from eth signature', () => {
|
|||||||
starknet.ethSigToPrivate(ethSignature),
|
starknet.ethSigToPrivate(ethSignature),
|
||||||
'766f11e90cd7c7b43085b56da35c781f8c067ac0d578eabdceebc4886435bda'
|
'766f11e90cd7c7b43085b56da35c781f8c067ac0d578eabdceebc4886435bda'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
should('Key derivation', () => {
|
should('Key derivation', () => {
|
||||||
const layer = 'starkex';
|
const layer = 'starkex';
|
||||||
const application = 'starkdeployement';
|
const application = 'starkdeployement';
|
||||||
const mnemonic =
|
const mnemonic =
|
||||||
@@ -242,10 +243,10 @@ should('Key derivation', () => {
|
|||||||
deepStrictEqual(realPath, path);
|
deepStrictEqual(realPath, path);
|
||||||
deepStrictEqual(starknet.grindKey(hd.derive(realPath).privateKey), privateKey);
|
deepStrictEqual(starknet.grindKey(hd.derive(realPath).privateKey), privateKey);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Verified against starknet.js
|
// Verified against starknet.js
|
||||||
should('Starknet.js cross-tests', () => {
|
should('Starknet.js cross-tests', () => {
|
||||||
const privateKey = '0x019800ea6a9a73f94aee6a3d2edf018fc770443e90c7ba121e8303ec6b349279';
|
const privateKey = '0x019800ea6a9a73f94aee6a3d2edf018fc770443e90c7ba121e8303ec6b349279';
|
||||||
// NOTE: there is no compressed keys here, getPubKey returns stark-key (which is schnorr-like X coordinate)
|
// 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
|
// But it is not used in signing/verifying
|
||||||
@@ -255,7 +256,7 @@ should('Starknet.js cross-tests', () => {
|
|||||||
);
|
);
|
||||||
const msgHash = '0x6d1706bd3d1ba7c517be2a2a335996f63d4738e2f182144d078a1dd9997062e';
|
const msgHash = '0x6d1706bd3d1ba7c517be2a2a335996f63d4738e2f182144d078a1dd9997062e';
|
||||||
const sig = starknet.sign(msgHash, privateKey);
|
const sig = starknet.sign(msgHash, privateKey);
|
||||||
const { r, s } = (sig);
|
const { r, s } = sig;
|
||||||
|
|
||||||
deepStrictEqual(
|
deepStrictEqual(
|
||||||
r.toString(),
|
r.toString(),
|
||||||
@@ -277,6 +278,7 @@ should('Starknet.js cross-tests', () => {
|
|||||||
2440689354481625417078677634625227600823892606910345662891037256374285369343n
|
2440689354481625417078677634625227600823892606910345662891037256374285369343n
|
||||||
);
|
);
|
||||||
deepStrictEqual(starknet.verify(sig2.toDERHex(), hashMsg2, pubKey), true);
|
deepStrictEqual(starknet.verify(sig2.toDERHex(), hashMsg2, pubKey), true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// ESM is broken.
|
// ESM is broken.
|
||||||
|
|||||||
1080
test/vectors/poseidon.json
Normal file
1080
test/vectors/poseidon.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,12 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"outDir": "lib/esm",
|
"outDir": "esm",
|
||||||
"target": "es2020",
|
"target": "es2020",
|
||||||
"module": "es6",
|
"module": "es6",
|
||||||
"moduleResolution": "node16",
|
"moduleResolution": "node16",
|
||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
|
"sourceMap": true,
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@noble/hashes/crypto": [ "src/crypto" ]
|
"@noble/hashes/crypto": [ "src/crypto" ]
|
||||||
|
|||||||
@@ -2,9 +2,11 @@
|
|||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"outDir": "lib",
|
"declarationMap": true,
|
||||||
|
"outDir": ".",
|
||||||
"target": "es2020",
|
"target": "es2020",
|
||||||
"lib": ["es2020"], // Set explicitly to remove DOM
|
"lib": ["es2020"], // Set explicitly to remove DOM
|
||||||
|
"sourceMap": true,
|
||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
|
|||||||
Reference in New Issue
Block a user