Compare commits
134 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
49a659b248 | ||
|
|
9d0a2e25dc | ||
|
|
7c461af2b2 | ||
|
|
4a8f447c8d | ||
|
|
4b2d31ce7f | ||
|
|
16115f27a6 | ||
|
|
0e0d0f530d | ||
|
|
fa5105aef2 | ||
|
|
11f1626ecc | ||
|
|
53ff287bf7 | ||
|
|
214c9aa553 | ||
|
|
ec2c3e1248 | ||
|
|
e64a9d654c | ||
|
|
088edd0fbb | ||
|
|
3e90930e9d | ||
|
|
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 |
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/
|
||||||
|
|||||||
@@ -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,416 +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),
|
|
||||||
},
|
|
||||||
hashToCurve: {
|
|
||||||
samples: 500,
|
|
||||||
noble: () => secp256k1.Point.hashToCurve('abcd'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
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),
|
|
||||||
},
|
|
||||||
hashToCurve: {
|
|
||||||
samples: 500,
|
|
||||||
noble: () => ed25519.Point.hashToCurve('abcd'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
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),
|
|
||||||
},
|
|
||||||
hashToCurve: {
|
|
||||||
samples: 500,
|
|
||||||
noble: () => ed448.Point.hashToCurve('abcd'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
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),
|
|
||||||
},
|
|
||||||
hashToCurve: {
|
|
||||||
samples: 500,
|
|
||||||
P256: () => P256.Point.hashToCurve('abcd'),
|
|
||||||
P384: () => P384.Point.hashToCurve('abcd'),
|
|
||||||
P521: () => P521.Point.hashToCurve('abcd'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
13
benchmark/modular.js
Normal file
13
benchmark/modular.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { run, mark } from 'micro-bmark';
|
||||||
|
import { secp256k1 } from '../secp256k1.js';
|
||||||
|
import { Fp } from '../abstract/modular.js';
|
||||||
|
|
||||||
|
run(async () => {
|
||||||
|
console.log(`\x1b[36mmodular, secp256k1 field\x1b[0m`);
|
||||||
|
const { Fp: secpFp } = secp256k1.CURVE;
|
||||||
|
await mark('invert a', 300000, () => secpFp.inv(2n ** 232n - 5910n));
|
||||||
|
await mark('invert b', 300000, () => secpFp.inv(2n ** 231n - 5910n));
|
||||||
|
await mark('sqrt p = 3 mod 4', 15000, () => secpFp.sqrt(2n ** 231n - 5910n));
|
||||||
|
const FpStark = Fp(BigInt('0x800000000000011000000000000000000000000000000000000000000000001'));
|
||||||
|
await mark('sqrt tonneli-shanks', 500, () => FpStark.sqrt(2n ** 231n - 5909n))
|
||||||
|
});
|
||||||
@@ -12,15 +12,11 @@
|
|||||||
"author": "",
|
"author": "",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"micro-bmark": "0.2.1"
|
"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",
|
||||||
"calculate-correlation": "^1.2.3",
|
|
||||||
"elliptic": "^6.5.4"
|
"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.8.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "@noble/curves",
|
||||||
|
"version": "0.8.0",
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
156
package.json
156
package.json
@@ -1,13 +1,19 @@
|
|||||||
{
|
{
|
||||||
"name": "@noble/curves",
|
"name": "@noble/curves",
|
||||||
"version": "0.5.1",
|
"version": "0.8.0",
|
||||||
"description": "Minimal, auditable JS implementation of elliptic curve cryptography",
|
"description": "Minimal, auditable JS implementation of elliptic curve cryptography",
|
||||||
"files": [
|
"files": [
|
||||||
"lib"
|
"abstract",
|
||||||
|
"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",
|
||||||
|
"build:clean": "rm *.{js,d.ts,js.map} esm/*.{js,js.map} 2> /dev/null",
|
||||||
"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'",
|
||||||
"format": "prettier --write 'src/**/*.{js,ts}' 'test/*.js'",
|
"format": "prettier --write 'src/**/*.{js,ts}' 'test/*.js'",
|
||||||
@@ -21,142 +27,112 @@
|
|||||||
},
|
},
|
||||||
"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",
|
||||||
|
"type": "module",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": {
|
".": {
|
||||||
"types": "./lib/index.d.ts",
|
"types": "./index.d.ts",
|
||||||
"import": "./lib/esm/index.js",
|
"default": "./index.js"
|
||||||
"default": "./lib/index.js"
|
|
||||||
},
|
},
|
||||||
"./abstract/edwards": {
|
"./abstract/edwards": {
|
||||||
"types": "./lib/abstract/edwards.d.ts",
|
"types": "./abstract/edwards.d.ts",
|
||||||
"import": "./lib/esm/abstract/edwards.js",
|
"default": "./abstract/edwards.js"
|
||||||
"default": "./lib/abstract/edwards.js"
|
|
||||||
},
|
},
|
||||||
"./abstract/modular": {
|
"./abstract/modular": {
|
||||||
"types": "./lib/abstract/modular.d.ts",
|
"types": "./abstract/modular.d.ts",
|
||||||
"import": "./lib/esm/abstract/modular.js",
|
"default": "./abstract/modular.js"
|
||||||
"default": "./lib/abstract/modular.js"
|
|
||||||
},
|
},
|
||||||
"./abstract/montgomery": {
|
"./abstract/montgomery": {
|
||||||
"types": "./lib/abstract/montgomery.d.ts",
|
"types": "./abstract/montgomery.d.ts",
|
||||||
"import": "./lib/esm/abstract/montgomery.js",
|
"default": "./abstract/montgomery.js"
|
||||||
"default": "./lib/abstract/montgomery.js"
|
|
||||||
},
|
},
|
||||||
"./abstract/weierstrass": {
|
"./abstract/weierstrass": {
|
||||||
"types": "./lib/abstract/weierstrass.d.ts",
|
"types": "./abstract/weierstrass.d.ts",
|
||||||
"import": "./lib/esm/abstract/weierstrass.js",
|
"default": "./abstract/weierstrass.js"
|
||||||
"default": "./lib/abstract/weierstrass.js"
|
|
||||||
},
|
},
|
||||||
"./abstract/bls": {
|
"./abstract/bls": {
|
||||||
"types": "./lib/abstract/bls.d.ts",
|
"types": "./abstract/bls.d.ts",
|
||||||
"import": "./lib/esm/abstract/bls.js",
|
"default": "./abstract/bls.js"
|
||||||
"default": "./lib/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",
|
"default": "./abstract/hash-to-curve.js"
|
||||||
"default": "./lib/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",
|
"default": "./abstract/curve.js"
|
||||||
"default": "./lib/abstract/group.js"
|
|
||||||
},
|
},
|
||||||
"./abstract/utils": {
|
"./abstract/utils": {
|
||||||
"types": "./lib/abstract/utils.d.ts",
|
"types": "./abstract/utils.d.ts",
|
||||||
"import": "./lib/esm/abstract/utils.js",
|
"default": "./abstract/utils.js"
|
||||||
"default": "./lib/abstract/utils.js"
|
},
|
||||||
|
"./abstract/poseidon": {
|
||||||
|
"types": "./abstract/poseidon.d.ts",
|
||||||
|
"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",
|
"default": "./_shortw_utils.js"
|
||||||
"default": "./lib/_shortw_utils.js"
|
|
||||||
},
|
},
|
||||||
"./bls12-381": {
|
"./bls12-381": {
|
||||||
"types": "./lib/bls12-381.d.ts",
|
"types": "./bls12-381.d.ts",
|
||||||
"import": "./lib/esm/bls12-381.js",
|
"default": "./bls12-381.js"
|
||||||
"default": "./lib/bls12-381.js"
|
|
||||||
},
|
},
|
||||||
"./bn": {
|
"./bn": {
|
||||||
"types": "./lib/bn.d.ts",
|
"types": "./bn.d.ts",
|
||||||
"import": "./lib/esm/bn.js",
|
"default": "./bn.js"
|
||||||
"default": "./lib/bn.js"
|
|
||||||
},
|
},
|
||||||
"./ed25519": {
|
"./ed25519": {
|
||||||
"types": "./lib/ed25519.d.ts",
|
"types": "./ed25519.d.ts",
|
||||||
"import": "./lib/esm/ed25519.js",
|
"default": "./ed25519.js"
|
||||||
"default": "./lib/ed25519.js"
|
|
||||||
},
|
},
|
||||||
"./ed448": {
|
"./ed448": {
|
||||||
"types": "./lib/ed448.d.ts",
|
"types": "./ed448.d.ts",
|
||||||
"import": "./lib/esm/ed448.js",
|
"default": "./ed448.js"
|
||||||
"default": "./lib/ed448.js"
|
|
||||||
},
|
},
|
||||||
"./index": {
|
"./index": {
|
||||||
"types": "./lib/index.d.ts",
|
"types": "./index.d.ts",
|
||||||
"import": "./lib/esm/index.js",
|
"default": "./index.js"
|
||||||
"default": "./lib/index.js"
|
|
||||||
},
|
},
|
||||||
"./jubjub": {
|
"./jubjub": {
|
||||||
"types": "./lib/jubjub.d.ts",
|
"types": "./jubjub.d.ts",
|
||||||
"import": "./lib/esm/jubjub.js",
|
"default": "./jubjub.js"
|
||||||
"default": "./lib/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",
|
"default": "./p256.js"
|
||||||
"default": "./lib/p256.js"
|
|
||||||
},
|
},
|
||||||
"./p384": {
|
"./p384": {
|
||||||
"types": "./lib/p384.d.ts",
|
"types": "./p384.d.ts",
|
||||||
"import": "./lib/esm/p384.js",
|
"default": "./p384.js"
|
||||||
"default": "./lib/p384.js"
|
|
||||||
},
|
},
|
||||||
"./p521": {
|
"./p521": {
|
||||||
"types": "./lib/p521.d.ts",
|
"types": "./p521.d.ts",
|
||||||
"import": "./lib/esm/p521.js",
|
"default": "./p521.js"
|
||||||
"default": "./lib/p521.js"
|
|
||||||
},
|
},
|
||||||
"./pasta": {
|
"./pasta": {
|
||||||
"types": "./lib/pasta.d.ts",
|
"types": "./pasta.d.ts",
|
||||||
"import": "./lib/esm/pasta.js",
|
"default": "./pasta.js"
|
||||||
"default": "./lib/pasta.js"
|
|
||||||
},
|
},
|
||||||
"./secp256k1": {
|
"./secp256k1": {
|
||||||
"types": "./lib/secp256k1.d.ts",
|
"types": "./secp256k1.d.ts",
|
||||||
"import": "./lib/esm/secp256k1.js",
|
"default": "./secp256k1.js"
|
||||||
"default": "./lib/secp256k1.js"
|
|
||||||
},
|
},
|
||||||
"./stark": {
|
"./stark": {
|
||||||
"types": "./lib/stark.d.ts",
|
"types": "./stark.d.ts",
|
||||||
"import": "./lib/esm/stark.js",
|
"default": "./stark.js"
|
||||||
"default": "./lib/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,60 +1,36 @@
|
|||||||
/*! 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
|
||||||
|
*/
|
||||||
|
type UnicodeOrBytes = string | Uint8Array;
|
||||||
|
export type Opts = {
|
||||||
|
DST: UnicodeOrBytes;
|
||||||
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_message_xmd
|
|
||||||
expand?: 'xmd' | 'xof';
|
expand?: 'xmd' | 'xof';
|
||||||
// Hash functions for: expand_message_xmd is appropriate for use with a
|
|
||||||
// wide range of hash functions, including SHA-2, SHA-3, BLAKE2, and others.
|
|
||||||
// BBS+ uses blake2: https://github.com/hyperledger/aries-framework-go/issues/2247
|
|
||||||
// TODO: verify that hash is shake if expand==='xof' via types
|
|
||||||
hash: CHash;
|
hash: CHash;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function validateHTFOpts(opts: htfOpts) {
|
function validateDST(dst: UnicodeOrBytes): 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 (opts.expand !== 'xmd' && opts.expand !== 'xof' && opts.expand !== undefined)
|
|
||||||
throw new Error('Invalid htf/expand');
|
|
||||||
if (typeof opts.hash !== 'function' || !Number.isSafeInteger(opts.hash.outputLen))
|
|
||||||
throw new Error('Invalid htf/hash function');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UTF8 to ui8a
|
// 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}`);
|
||||||
@@ -75,6 +51,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(
|
||||||
@@ -83,15 +66,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));
|
||||||
@@ -110,11 +95,14 @@ export function expand_message_xof(
|
|||||||
k: number,
|
k: 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
|
||||||
// DST = H('H2C-OVERSIZE-DST-' || a_very_long_DST, Math.ceil((lenInBytes * k) / 8));
|
// DST = H('H2C-OVERSIZE-DST-' || a_very_long_DST, Math.ceil((lenInBytes * k) / 8));
|
||||||
if (DST.length > 255) {
|
if (DST.length > 255) {
|
||||||
const dkLen = Math.ceil((2 * k) / 8);
|
const dkLen = Math.ceil((2 * k) / 8);
|
||||||
DST = H.create({ dkLen }).update(stringToBytes('H2C-OVERSIZE-DST-')).update(DST).digest();
|
DST = H.create({ dkLen }).update(utf8ToBytes('H2C-OVERSIZE-DST-')).update(DST).digest();
|
||||||
}
|
}
|
||||||
if (lenInBytes > 65535 || DST.length > 255)
|
if (lenInBytes > 65535 || DST.length > 255)
|
||||||
throw new Error('expand_message_xof: invalid lenInBytes');
|
throw new Error('expand_message_xof: invalid lenInBytes');
|
||||||
@@ -134,36 +122,48 @@ export function expand_message_xof(
|
|||||||
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3
|
* 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 msg a byte string containing the message to hash
|
||||||
* @param count the number of elements of F to output
|
* @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}`
|
* @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.
|
* @returns [u_0, ..., u_(count - 1)], a list of field elements.
|
||||||
*/
|
*/
|
||||||
export function hash_to_field(msg: Uint8Array, count: number, options: htfOpts): bigint[][] {
|
export function hash_to_field(msg: Uint8Array, count: number, options: Opts): bigint[][] {
|
||||||
// if options is provided but incomplete, fill any missing fields with the
|
validateObject(options, {
|
||||||
// value in hftDefaults (ie hash to G2).
|
DST: 'string',
|
||||||
const log2p = options.p.toString(2).length;
|
p: 'bigint',
|
||||||
const L = Math.ceil((log2p + options.k) / 8); // section 5.1 of ietf draft link above
|
m: 'isSafeInteger',
|
||||||
const len_in_bytes = count * options.m * L;
|
k: 'isSafeInteger',
|
||||||
const DST = stringToBytes(options.DST);
|
hash: 'hash',
|
||||||
let pseudo_random_bytes = msg;
|
});
|
||||||
if (options.expand === 'xmd') {
|
const { p, k, m, hash, expand, DST: _DST } = options;
|
||||||
pseudo_random_bytes = expand_message_xmd(msg, DST, len_in_bytes, options.hash);
|
isBytes(msg);
|
||||||
} else if (options.expand === 'xof') {
|
isNum(count);
|
||||||
pseudo_random_bytes = expand_message_xof(msg, DST, len_in_bytes, options.k, options.hash);
|
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) => {
|
||||||
@@ -175,3 +175,48 @@ 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: UnicodeOrBytes };
|
||||||
|
|
||||||
|
export function createHasher<T>(
|
||||||
|
Point: H2CPointConstructor<T>,
|
||||||
|
mapToCurve: MapToCurve<T>,
|
||||||
|
def: Opts & { encodeDST?: UnicodeOrBytes }
|
||||||
|
) {
|
||||||
|
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-16#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,7 +1,14 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
// TODO: remove circular imports
|
|
||||||
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
|
||||||
@@ -34,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) {
|
||||||
@@ -50,11 +56,13 @@ export function invert(number: bigint, modulo: bigint): bigint {
|
|||||||
throw new Error(`invert: expected positive integers, got n=${number} mod=${modulo}`);
|
throw new Error(`invert: expected positive integers, got n=${number} mod=${modulo}`);
|
||||||
}
|
}
|
||||||
// Eucledian GCD https://brilliant.org/wiki/extended-euclidean-algorithm/
|
// Eucledian GCD https://brilliant.org/wiki/extended-euclidean-algorithm/
|
||||||
|
// Fermat's little theorem "CT-like" version inv(n) = n^(m-2) mod m is 30x slower.
|
||||||
let a = mod(number, modulo);
|
let a = mod(number, modulo);
|
||||||
let b = modulo;
|
let b = modulo;
|
||||||
// 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;
|
||||||
@@ -68,7 +76,8 @@ export function invert(number: bigint, modulo: bigint): bigint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Tonelli-Shanks algorithm
|
// Tonelli-Shanks algorithm
|
||||||
// https://eprint.iacr.org/2012/685.pdf (page 12)
|
// Paper 1: https://eprint.iacr.org/2012/685.pdf (page 12)
|
||||||
|
// Paper 2: Square Roots from 1; 24, 51, 10 to Dan Shanks
|
||||||
export function tonelliShanks(P: bigint) {
|
export function tonelliShanks(P: bigint) {
|
||||||
// Legendre constant: used to calculate Legendre symbol (a | p),
|
// Legendre constant: used to calculate Legendre symbol (a | p),
|
||||||
// which denotes the value of a^((p-1)/2) (mod p).
|
// which denotes the value of a^((p-1)/2) (mod p).
|
||||||
@@ -79,7 +88,7 @@ export function tonelliShanks(P: bigint) {
|
|||||||
|
|
||||||
let Q: bigint, S: number, Z: bigint;
|
let Q: bigint, S: number, Z: bigint;
|
||||||
// Step 1: By factoring out powers of 2 from p - 1,
|
// Step 1: By factoring out powers of 2 from p - 1,
|
||||||
// find q and s such that p - 1 = q2s with q odd
|
// 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++);
|
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
|
// Step 2: Select a non-square z such that (z | p) ≡ -1 and set c ≡ zq
|
||||||
@@ -90,7 +99,7 @@ export function tonelliShanks(P: bigint) {
|
|||||||
const p1div4 = (P + _1n) / _4n;
|
const p1div4 = (P + _1n) / _4n;
|
||||||
return function tonelliFast<T>(Fp: Field<T>, n: T) {
|
return function tonelliFast<T>(Fp: Field<T>, n: T) {
|
||||||
const root = Fp.pow(n, p1div4);
|
const root = Fp.pow(n, p1div4);
|
||||||
if (!Fp.equals(Fp.square(root), n)) throw new Error('Cannot find square root');
|
if (!Fp.eql(Fp.sqr(root), n)) throw new Error('Cannot find square root');
|
||||||
return root;
|
return root;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -98,31 +107,30 @@ export function tonelliShanks(P: bigint) {
|
|||||||
// Slow-path
|
// Slow-path
|
||||||
const Q1div2 = (Q + _1n) / _2n;
|
const Q1div2 = (Q + _1n) / _2n;
|
||||||
return function tonelliSlow<T>(Fp: Field<T>, n: T): T {
|
return function tonelliSlow<T>(Fp: Field<T>, n: T): T {
|
||||||
// Step 0: Check that n is indeed a square: (n | p) must be ≡ 1
|
// Step 0: Check that n is indeed a square: (n | p) should not be ≡ -1
|
||||||
if (Fp.pow(n, legendreC) !== Fp.ONE) throw new Error('Cannot find square root');
|
if (Fp.pow(n, legendreC) === Fp.neg(Fp.ONE)) throw new Error('Cannot find square root');
|
||||||
let s = S;
|
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
|
||||||
|
|
||||||
let c = pow(Z, Q, P);
|
while (!Fp.eql(b, Fp.ONE)) {
|
||||||
let r = Fp.pow(n, Q1div2);
|
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)
|
||||||
let t = Fp.pow(n, Q);
|
// Find m such b^(2^m)==1
|
||||||
|
let m = 1;
|
||||||
let t2 = Fp.ZERO;
|
for (let t2 = Fp.sqr(b); m < r; m++) {
|
||||||
while (!Fp.equals(Fp.sub(t, Fp.ONE), Fp.ZERO)) {
|
if (Fp.eql(t2, Fp.ONE)) break;
|
||||||
t2 = Fp.square(t);
|
t2 = Fp.sqr(t2); // t2 *= t2
|
||||||
let i;
|
|
||||||
for (i = 1; i < s; i++) {
|
|
||||||
// stop if t2-1 == 0
|
|
||||||
if (Fp.equals(Fp.sub(t2, Fp.ONE), Fp.ZERO)) break;
|
|
||||||
// t2 *= t2
|
|
||||||
t2 = Fp.square(t2);
|
|
||||||
}
|
}
|
||||||
let b = pow(c, BigInt(1 << (s - i - 1)), P);
|
// NOTE: r-m-1 can be bigger than 32, need to convert to bigint before shift, otherwise there will be overflow
|
||||||
r = Fp.mul(r, b);
|
const ge = Fp.pow(g, _1n << BigInt(r - m - 1)); // ge = 2^(r-m-1)
|
||||||
c = mod(b * b, P);
|
g = Fp.sqr(ge); // g = ge * ge
|
||||||
t = Fp.mul(t, c);
|
x = Fp.mul(x, ge); // x *= ge
|
||||||
s = i;
|
b = Fp.mul(b, g); // b *= g
|
||||||
|
r = m;
|
||||||
}
|
}
|
||||||
return r;
|
return x;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,7 +149,7 @@ export function FpSqrt(P: bigint) {
|
|||||||
return function sqrt3mod4<T>(Fp: Field<T>, n: T) {
|
return function sqrt3mod4<T>(Fp: Field<T>, n: T) {
|
||||||
const root = Fp.pow(n, p1div4);
|
const root = Fp.pow(n, p1div4);
|
||||||
// Throw if root**2 != n
|
// Throw if root**2 != n
|
||||||
if (!Fp.equals(Fp.square(root), n)) throw new Error('Cannot find square root');
|
if (!Fp.eql(Fp.sqr(root), n)) throw new Error('Cannot find square root');
|
||||||
return root;
|
return root;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -155,7 +163,7 @@ export function FpSqrt(P: bigint) {
|
|||||||
const nv = Fp.mul(n, v);
|
const nv = Fp.mul(n, v);
|
||||||
const i = Fp.mul(Fp.mul(nv, _2n), v);
|
const i = Fp.mul(Fp.mul(nv, _2n), v);
|
||||||
const root = Fp.mul(nv, Fp.sub(i, Fp.ONE));
|
const root = Fp.mul(nv, Fp.sub(i, Fp.ONE));
|
||||||
if (!Fp.equals(Fp.square(root), n)) throw new Error('Cannot find square root');
|
if (!Fp.eql(Fp.sqr(root), n)) throw new Error('Cannot find square root');
|
||||||
return root;
|
return root;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -205,13 +213,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;
|
||||||
@@ -221,13 +229,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.
|
// 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;
|
||||||
@@ -237,23 +245,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
|
||||||
@@ -267,7 +274,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;
|
||||||
@@ -277,15 +284,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);
|
||||||
@@ -293,7 +300,7 @@ 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.
|
// This function returns True whenever the value x is a square in the field F.
|
||||||
@@ -301,10 +308,18 @@ export function FpIsSquare<T>(f: Field<T>) {
|
|||||||
const legendreConst = (f.ORDER - _1n) / _2n; // Integer arithmetic
|
const legendreConst = (f.ORDER - _1n) / _2n; // Integer arithmetic
|
||||||
return (x: T): boolean => {
|
return (x: T): boolean => {
|
||||||
const p = f.pow(x, legendreConst);
|
const p = f.pow(x, legendreConst);
|
||||||
return f.equals(p, f.ZERO) || f.equals(p, f.ONE);
|
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
|
||||||
@@ -317,28 +332,28 @@ export function Fp(
|
|||||||
redef: Partial<Field<bigint>> = {}
|
redef: Partial<Field<bigint>> = {}
|
||||||
): Readonly<FpField> {
|
): 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 = FpSqrt(ORDER);
|
const sqrtP = FpSqrt(ORDER);
|
||||||
const f: Readonly<FpField> = 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),
|
||||||
@@ -346,24 +361,22 @@ 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 || ((n) => sqrtP(f, n)),
|
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 FpField);
|
} as FpField);
|
||||||
return Object.freeze(f);
|
return Object.freeze(f);
|
||||||
@@ -372,11 +385,34 @@ export function Fp(
|
|||||||
export function FpSqrtOdd<T>(Fp: Field<T>, elm: T) {
|
export function FpSqrtOdd<T>(Fp: Field<T>, elm: T) {
|
||||||
if (!Fp.isOdd) throw new Error(`Field doesn't have isOdd`);
|
if (!Fp.isOdd) throw new Error(`Field doesn't have isOdd`);
|
||||||
const root = Fp.sqrt(elm);
|
const root = Fp.sqrt(elm);
|
||||||
return Fp.isOdd(root) ? root : Fp.negate(root);
|
return Fp.isOdd(root) ? root : Fp.neg(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FpSqrtEven<T>(Fp: Field<T>, elm: T) {
|
export function FpSqrtEven<T>(Fp: Field<T>, elm: T) {
|
||||||
if (!Fp.isOdd) throw new Error(`Field doesn't have isOdd`);
|
if (!Fp.isOdd) throw new Error(`Field doesn't have isOdd`);
|
||||||
const root = Fp.sqrt(elm);
|
const root = Fp.sqrt(elm);
|
||||||
return Fp.isOdd(root) ? Fp.negate(root) : root;
|
return Fp.isOdd(root) ? Fp.neg(root) : root;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FIPS 186 B.4.1-compliant "constant-time" private key generation utility.
|
||||||
|
* Can take (n+8) or more bytes of uniform input e.g. from CSPRNG or KDF
|
||||||
|
* and convert them into private scalar, with the modulo bias being neglible.
|
||||||
|
* Needs at least 40 bytes of input for 32-byte private key.
|
||||||
|
* https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
|
||||||
|
* @param hash hash output from SHA3 or a similar function
|
||||||
|
* @returns valid private scalar
|
||||||
|
*/
|
||||||
|
export function hashToPrivateScalar(
|
||||||
|
hash: string | Uint8Array,
|
||||||
|
groupOrder: bigint,
|
||||||
|
isLE = false
|
||||||
|
): bigint {
|
||||||
|
hash = ensureBytes('privateHash', hash);
|
||||||
|
const hashLen = hash.length;
|
||||||
|
const minLen = nLength(groupOrder).nByteLength + 8;
|
||||||
|
if (minLen < 24 || hashLen < minLen || hashLen > 1024)
|
||||||
|
throw new Error(`hashToPrivateScalar: expected ${minLen}-1024 bytes of input, got ${hashLen}`);
|
||||||
|
const num = isLE ? bytesToNumberLE(hash) : bytesToNumberBE(hash);
|
||||||
|
return mod(num, groupOrder - _1n) + _1n;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,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(opts?: { dkLen?: number }): any; // For shake
|
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
228
src/bls12-381.ts
228
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,
|
||||||
@@ -931,7 +966,7 @@ const htfDefaults = {
|
|||||||
// 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;
|
} 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';
|
||||||
/**
|
/**
|
||||||
|
|||||||
227
src/ed25519.ts
227
src/ed25519.ts
@@ -1,17 +1,19 @@
|
|||||||
/*! 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, FpSqrtEven } 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';
|
||||||
|
import { AffinePoint } from './abstract/curve.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ed25519 Twisted Edwards curve with following addons:
|
* ed25519 Twisted Edwards curve with following addons:
|
||||||
@@ -93,79 +95,6 @@ export const ED25519_TORSION_SUBGROUP = [
|
|||||||
|
|
||||||
const Fp = Field(ED25519_P, undefined, true);
|
const Fp = Field(ED25519_P, undefined, true);
|
||||||
|
|
||||||
// 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.negate(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.square(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.negate(ELL2_J); // 4. x1n = -J # x1 = x1n / xd = -J / (1 + 2 * u^2)
|
|
||||||
let tv2 = Fp.square(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.square(gxd); // 11. tv3 = gxd^2
|
|
||||||
tv2 = Fp.square(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.square(y11); // 19. tv2 = y11^2
|
|
||||||
tv2 = Fp.mul(tv2, gxd); // 20. tv2 = tv2 * gxd
|
|
||||||
let e1 = Fp.equals(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.square(y21); // 28. tv2 = y21^2
|
|
||||||
tv2 = Fp.mul(tv2, gxd); // 29. tv2 = tv2 * gxd
|
|
||||||
let e2 = Fp.equals(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.square(y1); // 32. tv2 = y1^2
|
|
||||||
tv2 = Fp.mul(tv2, gxd); // 33. tv2 = tv2 * gxd
|
|
||||||
let e3 = Fp.equals(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.negate(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.negate(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.equals(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 ED25519_DEF = {
|
const ED25519_DEF = {
|
||||||
// Param: a
|
// Param: a
|
||||||
a: BigInt(-1),
|
a: BigInt(-1),
|
||||||
@@ -189,15 +118,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: 'xmd',
|
|
||||||
hash: sha512,
|
|
||||||
},
|
|
||||||
mapToCurve: (scalars: bigint[]) => map_to_curve_elligator2_edwards25519(scalars[0]),
|
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const ed25519 = twistedEdwards(ED25519_DEF);
|
export const ed25519 = twistedEdwards(ED25519_DEF);
|
||||||
@@ -219,10 +139,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)
|
||||||
@@ -230,10 +150,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(
|
||||||
@@ -260,16 +268,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
|
||||||
@@ -302,6 +310,11 @@ export class RistrettoPoint {
|
|||||||
// Private property to discourage combining ExtendedPoint + RistrettoPoint
|
// Private property to discourage combining ExtendedPoint + RistrettoPoint
|
||||||
// Always use Ristretto encoding/decoding instead.
|
// Always use Ristretto encoding/decoding instead.
|
||||||
constructor(private readonly ep: ExtendedPoint) {}
|
constructor(private readonly ep: ExtendedPoint) {}
|
||||||
|
|
||||||
|
static fromAffine(ap: AffinePoint<bigint>) {
|
||||||
|
return new RistrettoPoint(ed25519.ExtendedPoint.fromAffine(ap));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Takes uniform output of 64-bit hash function like sha512 and converts it to `RistrettoPoint`.
|
* Takes uniform output of 64-bit hash function like sha512 and converts it to `RistrettoPoint`.
|
||||||
* The hash-to-group operation applies Elligator twice and adds the results.
|
* The hash-to-group operation applies Elligator twice and adds the results.
|
||||||
@@ -310,7 +323,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));
|
||||||
@@ -324,10 +337,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.
|
||||||
@@ -355,9 +368,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
|
||||||
@@ -393,12 +406,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 } = other.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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -412,11 +425,21 @@ 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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/14/
|
||||||
|
// Appendix B. Hashing to ristretto255
|
||||||
|
export const hash_to_ristretto255 = (msg: Uint8Array, options: htf.htfBasicOpts) => {
|
||||||
|
const d = options.DST;
|
||||||
|
const DST = typeof d === 'string' ? utf8ToBytes(d) : d;
|
||||||
|
const uniform_bytes = htf.expand_message_xmd(msg, DST, 64, sha512);
|
||||||
|
const P = RistrettoPoint.hashToCurve(uniform_bytes);
|
||||||
|
return P;
|
||||||
|
};
|
||||||
|
|||||||
183
src/ed448.ts
183
src/ed448.ts
@@ -4,6 +4,7 @@ import { concatBytes, randomBytes, utf8ToBytes, wrapConstructor } from '@noble/h
|
|||||||
import { twistedEdwards } from './abstract/edwards.js';
|
import { twistedEdwards } from './abstract/edwards.js';
|
||||||
import { mod, pow2, Fp as Field } 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:
|
||||||
@@ -54,81 +55,6 @@ function adjustScalarBytes(bytes: Uint8Array): Uint8Array {
|
|||||||
|
|
||||||
const Fp = Field(ed448P, 456, true);
|
const Fp = Field(ed448P, 456, true);
|
||||||
|
|
||||||
// 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.square(u); // 1. tv1 = u^2
|
|
||||||
let e1 = Fp.equals(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.negate(ELL2_J); // 5. x1n = -J
|
|
||||||
let tv2 = Fp.square(xd); // 6. tv2 = xd^2
|
|
||||||
let gxd = Fp.mul(tv2, xd); // 7. gxd = tv2 * xd # gxd = xd^3
|
|
||||||
let gx1 = Fp.mul(tv1, Fp.negate(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.square(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.negate(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.square(y1); // 20. tv2 = y1^2
|
|
||||||
tv2 = Fp.mul(tv2, gxd); // 21. tv2 = tv2 * gxd
|
|
||||||
let e2 = Fp.equals(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.negate(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.square(xn); // 2. xn2 = xn^2
|
|
||||||
let xd2 = Fp.square(xd); // 3. xd2 = xd^2
|
|
||||||
let xd4 = Fp.square(xd2); // 4. xd4 = xd2^2
|
|
||||||
let yn2 = Fp.square(yn); // 5. yn2 = yn^2
|
|
||||||
let yd2 = Fp.square(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.equals(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 ED448_DEF = {
|
const ED448_DEF = {
|
||||||
// Param: a
|
// Param: a
|
||||||
a: BigInt(1),
|
a: BigInt(1),
|
||||||
@@ -138,7 +64,8 @@ const ED448_DEF = {
|
|||||||
),
|
),
|
||||||
// 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,
|
||||||
// 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'
|
||||||
),
|
),
|
||||||
@@ -188,15 +115,6 @@ const ED448_DEF = {
|
|||||||
// square root exists, and the decoding fails.
|
// square root exists, and the decoding fails.
|
||||||
return { isValid: mod(x2 * v, P) === u, value: x };
|
return { isValid: mod(x2 * v, P) === u, value: x };
|
||||||
},
|
},
|
||||||
htfDefaults: {
|
|
||||||
DST: 'edwards448_XOF:SHAKE256_ELL2_RO_',
|
|
||||||
p: Fp.ORDER,
|
|
||||||
m: 1,
|
|
||||||
k: 224,
|
|
||||||
expand: 'xof',
|
|
||||||
hash: shake256,
|
|
||||||
},
|
|
||||||
mapToCurve: (scalars: bigint[]) => map_to_curve_elligator2_edwards448(scalars[0]),
|
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const ed448 = twistedEdwards(ED448_DEF);
|
export const ed448 = twistedEdwards(ED448_DEF);
|
||||||
@@ -204,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);
|
||||||
@@ -216,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)
|
||||||
@@ -231,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;
|
|
||||||
19
src/p256.ts
19
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]),
|
} as const,
|
||||||
htfDefaults: {
|
sha256
|
||||||
|
);
|
||||||
|
export const secp256r1 = P256;
|
||||||
|
|
||||||
|
const { hashToCurve, encodeToCurve } = htf.createHasher(
|
||||||
|
secp256r1.ProjectivePoint,
|
||||||
|
(scalars: bigint[]) => mapSWU(scalars[0]),
|
||||||
|
{
|
||||||
DST: 'P256_XMD:SHA-256_SSWU_RO_',
|
DST: 'P256_XMD:SHA-256_SSWU_RO_',
|
||||||
|
encodeDST: 'P256_XMD:SHA-256_SSWU_NU_',
|
||||||
p: Fp.ORDER,
|
p: Fp.ORDER,
|
||||||
m: 1,
|
m: 1,
|
||||||
k: 128,
|
k: 128,
|
||||||
expand: 'xmd',
|
expand: 'xmd',
|
||||||
hash: sha256,
|
hash: sha256,
|
||||||
},
|
}
|
||||||
} as const,
|
|
||||||
sha256
|
|
||||||
);
|
);
|
||||||
export const secp256r1 = P256;
|
export { hashToCurve, encodeToCurve };
|
||||||
|
|||||||
19
src/p384.ts
19
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]),
|
} as const,
|
||||||
htfDefaults: {
|
sha384
|
||||||
|
);
|
||||||
|
export const secp384r1 = P384;
|
||||||
|
|
||||||
|
const { hashToCurve, encodeToCurve } = htf.createHasher(
|
||||||
|
secp384r1.ProjectivePoint,
|
||||||
|
(scalars: bigint[]) => mapSWU(scalars[0]),
|
||||||
|
{
|
||||||
DST: 'P384_XMD:SHA-384_SSWU_RO_',
|
DST: 'P384_XMD:SHA-384_SSWU_RO_',
|
||||||
|
encodeDST: 'P384_XMD:SHA-384_SSWU_NU_',
|
||||||
p: Fp.ORDER,
|
p: Fp.ORDER,
|
||||||
m: 1,
|
m: 1,
|
||||||
k: 192,
|
k: 192,
|
||||||
expand: 'xmd',
|
expand: 'xmd',
|
||||||
hash: sha384,
|
hash: sha384,
|
||||||
},
|
}
|
||||||
} as const,
|
|
||||||
sha384
|
|
||||||
);
|
);
|
||||||
export const secp384r1 = P384;
|
export { hashToCurve, encodeToCurve };
|
||||||
|
|||||||
30
src/p521.ts
30
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: 'xmd',
|
expand: 'xmd',
|
||||||
hash: sha512,
|
hash: sha512,
|
||||||
},
|
}
|
||||||
} as const, sha512);
|
);
|
||||||
export const secp521r1 = P521;
|
export { hashToCurve, encodeToCurve };
|
||||||
|
|||||||
419
src/secp256k1.ts
419
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 {
|
||||||
@@ -57,14 +38,184 @@ function sqrtMod(y: bigint): bigint {
|
|||||||
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;
|
||||||
const root = pow2(t2, _2n, P);
|
const root = pow2(t2, _2n, P);
|
||||||
if (!Fp.equals(Fp.square(root), y)) throw new Error('Cannot find square root');
|
if (!Fp.eql(Fp.sqr(root), y)) throw new Error('Cannot find square root');
|
||||||
return 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) {
|
||||||
|
let d_ = secp256k1.utils.normPrivateKeyToScalar(priv); // same method executed in fromPrivateKey
|
||||||
|
let p = Point.fromPrivateKey(d_); // P = d'⋅G; 0 < d' < n check is done inside
|
||||||
|
const scalar = p.hasEvenY() ? d_ : modN(-d_);
|
||||||
|
return { scalar: scalar, bytes: pointToBytes(p) };
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 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 { 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(rx, 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,
|
||||||
|
lift_x,
|
||||||
|
pointToBytes,
|
||||||
|
numberToBytesBE,
|
||||||
|
bytesToNumberBE,
|
||||||
|
taggedHash,
|
||||||
|
mod,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const isoMap = htf.isogenyMap(
|
||||||
Fp,
|
Fp,
|
||||||
[
|
[
|
||||||
// xNum
|
// xNum
|
||||||
@@ -96,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: 'xmd',
|
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,
|
|
||||||
};
|
|
||||||
|
|||||||
365
src/stark.ts
365
src/stark.ts
@@ -1,157 +1,117 @@
|
|||||||
/*! 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 * as u from './abstract/utils.js';
|
||||||
|
import type { Hex } 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 = u.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 u.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 = u.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 u.ensureBytes('', 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 u.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 = u.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(u.concatBytes(_seed, u.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 +123,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 = u.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[] = [];
|
||||||
@@ -218,14 +181,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 = u.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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,7 +198,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.equals(point)) 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;
|
||||||
}
|
}
|
||||||
@@ -241,17 +206,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));
|
||||||
@@ -260,5 +225,117 @@ 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 = u.bitMask(250);
|
||||||
export const keccak = (data: Uint8Array) => bytesToNumber0x(keccak_256(data)) & MASK_250;
|
export const keccak = (data: Uint8Array): bigint => u.bytesToNumberBE(keccak_256(data)) & MASK_250;
|
||||||
|
const sha256Num = (data: Uint8Array | string): bigint => u.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 type PoseidonFn = ReturnType<typeof poseidon> & {
|
||||||
|
m: number;
|
||||||
|
rate: number;
|
||||||
|
capacity: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function poseidonBasic(opts: PoseidonOpts, mds: bigint[][]): PoseidonFn {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
const res: Partial<PoseidonFn> = poseidon({
|
||||||
|
...opts,
|
||||||
|
t: m,
|
||||||
|
sboxPower: 3,
|
||||||
|
reversePartialPowIdx: true, // Why?!
|
||||||
|
mds,
|
||||||
|
roundConstants,
|
||||||
|
});
|
||||||
|
res.m = m;
|
||||||
|
res.rate = opts.rate;
|
||||||
|
res.capacity = opts.capacity;
|
||||||
|
return res as PoseidonFn;
|
||||||
|
}
|
||||||
|
|
||||||
|
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): bigint {
|
||||||
|
return fn([x, y, 2n])[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function poseidonHashFunc(x: Uint8Array, y: Uint8Array, fn = poseidonSmall): Uint8Array {
|
||||||
|
return u.numberToVarBytesBE(poseidonHash(u.bytesToNumberBE(x), u.bytesToNumberBE(y), fn));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function poseidonHashSingle(x: bigint, fn = poseidonSmall): bigint {
|
||||||
|
return fn([x, 0n, 1n])[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function poseidonHashMany(values: bigint[], fn = poseidonSmall): bigint {
|
||||||
|
const { m, rate } = fn;
|
||||||
|
if (!Array.isArray(values)) throw new Error('bigint array expected in values');
|
||||||
|
const padded = Array.from(values); // copy
|
||||||
|
padded.push(1n);
|
||||||
|
while (padded.length % rate !== 0) padded.push(0n);
|
||||||
|
let state: bigint[] = new Array(m).fill(0n);
|
||||||
|
for (let i = 0; i < padded.length; i += rate) {
|
||||||
|
for (let j = 0; j < rate; j++) state[j] += padded[i + j];
|
||||||
|
state = fn(state);
|
||||||
|
}
|
||||||
|
return state[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,21 @@
|
|||||||
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 '../abstract/modular.js';
|
||||||
|
import { bytesToHex as toHex } from '../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 '../p256.js';
|
||||||
import { secp256r1 } from '../lib/esm/p256.js';
|
import { secp384r1 } from '../p384.js';
|
||||||
import { secp384r1 } from '../lib/esm/p384.js';
|
import { secp521r1 } from '../p521.js';
|
||||||
import { secp521r1 } from '../lib/esm/p521.js';
|
import { secp256k1 } from '../secp256k1.js';
|
||||||
import { secp256k1 } from '../lib/esm/secp256k1.js';
|
import { ed25519, ed25519ctx, ed25519ph, x25519 } from '../ed25519.js';
|
||||||
import { ed25519, ed25519ctx, ed25519ph } from '../lib/esm/ed25519.js';
|
import { ed448, ed448ph } from '../ed448.js';
|
||||||
import { ed448, ed448ph } from '../lib/esm/ed448.js';
|
import { _starkCurve as starkCurve } from '../stark.js';
|
||||||
import { starkCurve } from '../lib/esm/stark.js';
|
import { pallas, vesta } from '../pasta.js';
|
||||||
import { pallas, vesta } from '../lib/esm/pasta.js';
|
import { bn254 } from '../bn.js';
|
||||||
import { bn254 } from '../lib/esm/bn.js';
|
import { jubjub } from '../jubjub.js';
|
||||||
import { jubjub } from '../lib/esm/jubjub.js';
|
import { bls12_381 } from '../bls12-381.js';
|
||||||
import { bls12_381 } from '../lib/esm/bls12-381.js';
|
|
||||||
|
|
||||||
// Fields tests
|
// Fields tests
|
||||||
const FIELDS = {
|
const FIELDS = {
|
||||||
@@ -61,27 +61,28 @@ for (const c in FIELDS) {
|
|||||||
const FC_BIGINT = curve[f][1] ? curve[f][1] : fc.bigInt(1n, Fp.ORDER - 1n);
|
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);
|
const create = curve[f][2] ? curve[f][2].bind(null, Fp) : (num) => Fp.create(num);
|
||||||
should(`${name} equality`, () => {
|
describe(name, () => {
|
||||||
|
should('equality', () => {
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(FC_BIGINT, (num) => {
|
fc.property(FC_BIGINT, (num) => {
|
||||||
const a = create(num);
|
const a = create(num);
|
||||||
const b = create(num);
|
const b = create(num);
|
||||||
deepStrictEqual(Fp.equals(a, b), true);
|
deepStrictEqual(Fp.eql(a, b), true);
|
||||||
deepStrictEqual(Fp.equals(b, a), true);
|
deepStrictEqual(Fp.eql(b, a), true);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
should(`${name} non-equality`, () => {
|
should('non-equality', () => {
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => {
|
fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => {
|
||||||
const a = create(num1);
|
const a = create(num1);
|
||||||
const b = create(num2);
|
const b = create(num2);
|
||||||
deepStrictEqual(Fp.equals(a, b), num1 === num2);
|
deepStrictEqual(Fp.eql(a, b), num1 === num2);
|
||||||
deepStrictEqual(Fp.equals(b, a), num1 === num2);
|
deepStrictEqual(Fp.eql(b, a), num1 === num2);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
should(`${name} add/subtract/commutativity`, () => {
|
should('add/subtract/commutativity', () => {
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => {
|
fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => {
|
||||||
const a = create(num1);
|
const a = create(num1);
|
||||||
@@ -90,7 +91,7 @@ for (const c in FIELDS) {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
should(`${name} add/subtract/associativity`, () => {
|
should('add/subtract/associativity', () => {
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(FC_BIGINT, FC_BIGINT, FC_BIGINT, (num1, num2, num3) => {
|
fc.property(FC_BIGINT, FC_BIGINT, FC_BIGINT, (num1, num2, num3) => {
|
||||||
const a = create(num1);
|
const a = create(num1);
|
||||||
@@ -100,7 +101,7 @@ for (const c in FIELDS) {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
should(`${name} add/subtract/x+0=x`, () => {
|
should('add/subtract/x+0=x', () => {
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(FC_BIGINT, (num) => {
|
fc.property(FC_BIGINT, (num) => {
|
||||||
const a = create(num);
|
const a = create(num);
|
||||||
@@ -108,7 +109,7 @@ for (const c in FIELDS) {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
should(`${name} add/subtract/x-0=x`, () => {
|
should('add/subtract/x-0=x', () => {
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(FC_BIGINT, (num) => {
|
fc.property(FC_BIGINT, (num) => {
|
||||||
const a = create(num);
|
const a = create(num);
|
||||||
@@ -117,28 +118,31 @@ for (const c in FIELDS) {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
should(`${name} add/subtract/negate equality`, () => {
|
should('add/subtract/negate equality', () => {
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(FC_BIGINT, (num1) => {
|
fc.property(FC_BIGINT, (num1) => {
|
||||||
const a = create(num1);
|
const a = create(num1);
|
||||||
const b = create(num1);
|
const b = create(num1);
|
||||||
deepStrictEqual(Fp.sub(Fp.ZERO, a), Fp.negate(a));
|
deepStrictEqual(Fp.sub(Fp.ZERO, a), Fp.neg(a));
|
||||||
deepStrictEqual(Fp.sub(a, b), Fp.add(a, Fp.negate(b)));
|
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))));
|
deepStrictEqual(Fp.sub(a, b), Fp.add(a, Fp.mul(b, Fp.create(-1n))));
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
should(`${name} add/subtract/negate`, () => {
|
should('add/subtract/negate', () => {
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(FC_BIGINT, (num) => {
|
fc.property(FC_BIGINT, (num) => {
|
||||||
const a = create(num);
|
const a = create(num);
|
||||||
deepStrictEqual(Fp.negate(a), Fp.sub(Fp.ZERO, a));
|
deepStrictEqual(Fp.neg(a), Fp.sub(Fp.ZERO, a));
|
||||||
deepStrictEqual(Fp.negate(a), Fp.mul(a, Fp.create(-1n)));
|
deepStrictEqual(Fp.neg(a), Fp.mul(a, Fp.create(-1n)));
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
should('negate(0)', () => {
|
||||||
|
deepStrictEqual(Fp.neg(Fp.ZERO), Fp.ZERO);
|
||||||
|
});
|
||||||
|
|
||||||
should(`${name} multiply/commutativity`, () => {
|
should('multiply/commutativity', () => {
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => {
|
fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => {
|
||||||
const a = create(num1);
|
const a = create(num1);
|
||||||
@@ -147,7 +151,7 @@ for (const c in FIELDS) {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
should(`${name} multiply/associativity`, () => {
|
should('multiply/associativity', () => {
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(FC_BIGINT, FC_BIGINT, FC_BIGINT, (num1, num2, num3) => {
|
fc.property(FC_BIGINT, FC_BIGINT, FC_BIGINT, (num1, num2, num3) => {
|
||||||
const a = create(num1);
|
const a = create(num1);
|
||||||
@@ -157,7 +161,7 @@ for (const c in FIELDS) {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
should(`${name} multiply/distributivity`, () => {
|
should('multiply/distributivity', () => {
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(FC_BIGINT, FC_BIGINT, FC_BIGINT, (num1, num2, num3) => {
|
fc.property(FC_BIGINT, FC_BIGINT, FC_BIGINT, (num1, num2, num3) => {
|
||||||
const a = create(num1);
|
const a = create(num1);
|
||||||
@@ -167,7 +171,7 @@ for (const c in FIELDS) {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
should(`${name} multiply/add equality`, () => {
|
should('multiply/add equality', () => {
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(FC_BIGINT, (num) => {
|
fc.property(FC_BIGINT, (num) => {
|
||||||
const a = create(num);
|
const a = create(num);
|
||||||
@@ -181,15 +185,15 @@ for (const c in FIELDS) {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
should(`${name} multiply/square equality`, () => {
|
should('multiply/square equality', () => {
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(FC_BIGINT, (num) => {
|
fc.property(FC_BIGINT, (num) => {
|
||||||
const a = create(num);
|
const a = create(num);
|
||||||
deepStrictEqual(Fp.square(a), Fp.mul(a, a));
|
deepStrictEqual(Fp.sqr(a), Fp.mul(a, a));
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
should(`${name} multiply/pow equality`, () => {
|
should('multiply/pow equality', () => {
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(FC_BIGINT, (num) => {
|
fc.property(FC_BIGINT, (num) => {
|
||||||
const a = create(num);
|
const a = create(num);
|
||||||
@@ -200,9 +204,27 @@ for (const c in FIELDS) {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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);
|
const isSquare = mod.FpIsSquare(Fp);
|
||||||
should(`${name} multiply/sqrt`, () => {
|
// Not implemented
|
||||||
if (Fp === bls12_381.CURVE.Fp12) return; // Not implemented
|
if (Fp !== bls12_381.CURVE.Fp12) {
|
||||||
|
should('multiply/sqrt', () => {
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(FC_BIGINT, (num) => {
|
fc.property(FC_BIGINT, (num) => {
|
||||||
const a = create(num);
|
const a = create(num);
|
||||||
@@ -214,49 +236,74 @@ for (const c in FIELDS) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
deepStrictEqual(isSquare(a), true);
|
deepStrictEqual(isSquare(a), true);
|
||||||
deepStrictEqual(Fp.equals(Fp.square(root), a), true, 'sqrt(a)^2 == a');
|
deepStrictEqual(Fp.eql(Fp.sqr(root), a), true, 'sqrt(a)^2 == a');
|
||||||
deepStrictEqual(Fp.equals(Fp.square(Fp.negate(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(`${name} div/division by one equality`, () => {
|
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.assert(
|
||||||
fc.property(FC_BIGINT, (num) => {
|
fc.property(FC_BIGINT, (num) => {
|
||||||
const a = create(num);
|
const a = create(num);
|
||||||
if (Fp.equals(a, Fp.ZERO)) return; // No division by zero
|
if (Fp.eql(a, Fp.ZERO)) return; // No division by zero
|
||||||
deepStrictEqual(Fp.div(a, Fp.ONE), a);
|
deepStrictEqual(Fp.div(a, Fp.ONE), a);
|
||||||
deepStrictEqual(Fp.div(a, a), Fp.ONE);
|
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(`${name} zero division equality`, () => {
|
should('zero division equality', () => {
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(FC_BIGINT, (num) => {
|
fc.property(FC_BIGINT, (num) => {
|
||||||
const a = create(num);
|
const a = create(num);
|
||||||
deepStrictEqual(Fp.div(Fp.ZERO, a), Fp.ZERO);
|
deepStrictEqual(Fp.div(Fp.ZERO, a), Fp.ZERO);
|
||||||
|
deepStrictEqual(mod.FpDiv(Fp, Fp.ZERO, a), Fp.ZERO);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
should(`${name} div/division distributivity`, () => {
|
should('div/division distributivity', () => {
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(FC_BIGINT, FC_BIGINT, FC_BIGINT, (num1, num2, num3) => {
|
fc.property(FC_BIGINT, FC_BIGINT, FC_BIGINT, (num1, num2, num3) => {
|
||||||
const a = create(num1);
|
const a = create(num1);
|
||||||
const b = create(num2);
|
const b = create(num2);
|
||||||
const c = create(num3);
|
const c = create(num3);
|
||||||
deepStrictEqual(Fp.div(Fp.add(a, b), c), Fp.add(Fp.div(a, c), Fp.div(b, c)));
|
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(`${name} div/division and multiplication equality`, () => {
|
should('div/division and multiplication equality', () => {
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => {
|
fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => {
|
||||||
const a = create(num1);
|
const a = create(num1);
|
||||||
const b = create(num2);
|
const b = create(num2);
|
||||||
deepStrictEqual(Fp.div(a, b), Fp.mul(a, Fp.invert(b)));
|
deepStrictEqual(Fp.div(a, b), Fp.mul(a, Fp.inv(b)));
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,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');
|
||||||
@@ -317,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');
|
||||||
@@ -355,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;
|
||||||
@@ -363,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);
|
||||||
@@ -371,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');
|
||||||
@@ -424,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');
|
||||||
@@ -449,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 })),
|
||||||
@@ -539,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);
|
||||||
@@ -554,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
303
test/ed25519-addons.test.js
Normal file
303
test/ed25519-addons.test.js
Normal file
@@ -0,0 +1,303 @@
|
|||||||
|
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 { bytesToNumberLE, numberToBytesLE } from '../abstract/utils.js';
|
||||||
|
import { default as x25519vectors } from './wycheproof/x25519_test.json' assert { type: 'json' };
|
||||||
|
import { ed25519ctx, ed25519ph, RistrettoPoint, x25519 } from '../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]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
should('have proper equality testing', () => {
|
||||||
|
const MAX_255B = BigInt('0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
|
||||||
|
const bytes255ToNumberLE = (bytes) =>
|
||||||
|
ed25519ctx.CURVE.Fp.create(bytesToNumberLE(bytes) & MAX_255B);
|
||||||
|
|
||||||
|
const priv = new Uint8Array([
|
||||||
|
198, 101, 65, 165, 93, 120, 37, 238, 16, 133, 10, 35, 253, 243, 161, 246, 229, 135, 12, 137,
|
||||||
|
202, 114, 222, 139, 146, 123, 4, 125, 152, 173, 1, 7,
|
||||||
|
]);
|
||||||
|
const pub = RistrettoPoint.BASE.multiply(bytes255ToNumberLE(priv));
|
||||||
|
deepStrictEqual(pub.equals(RistrettoPoint.ZERO), false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ESM is broken.
|
||||||
|
import url from 'url';
|
||||||
|
import { assert } from 'console';
|
||||||
|
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 '../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 '../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 '../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,22 +1,19 @@
|
|||||||
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 { shake128, shake256 } from '@noble/hashes/sha3';
|
import { shake128, shake256 } from '@noble/hashes/sha3';
|
||||||
import { secp256r1 } from '../lib/esm/p256.js';
|
import * as secp256r1 from '../p256.js';
|
||||||
import { secp384r1 } from '../lib/esm/p384.js';
|
import * as secp384r1 from '../p384.js';
|
||||||
import { secp521r1 } from '../lib/esm/p521.js';
|
import * as secp521r1 from '../p521.js';
|
||||||
import { ed25519 } from '../lib/esm/ed25519.js';
|
import * as ed25519 from '../ed25519.js';
|
||||||
import { ed448 } from '../lib/esm/ed448.js';
|
import * as ed448 from '../ed448.js';
|
||||||
import { secp256k1 } from '../lib/esm/secp256k1.js';
|
import * as secp256k1 from '../secp256k1.js';
|
||||||
import { bls12_381 } from '../lib/esm/bls12-381.js';
|
import { bls12_381 } from '../bls12-381.js';
|
||||||
import {
|
import { expand_message_xmd, expand_message_xof } from '../abstract/hash-to-curve.js';
|
||||||
stringToBytes,
|
import { utf8ToBytes } from '../abstract/utils.js';
|
||||||
expand_message_xmd,
|
|
||||||
expand_message_xof,
|
|
||||||
} from '../lib/esm/abstract/hash-to-curve.js';
|
|
||||||
// XMD
|
// 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' };
|
||||||
@@ -51,43 +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) {
|
function testExpandXOF(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_xof/${vectors.hash}/${vectors.DST.length}/${i}`, () => {
|
should(`${i}`, () => {
|
||||||
const p = expand_message_xof(
|
const p = expand_message_xof(
|
||||||
stringToBytes(t.msg),
|
utf8ToBytes(t.msg),
|
||||||
stringToBytes(vectors.DST),
|
utf8ToBytes(vectors.DST),
|
||||||
+t.len_in_bytes,
|
Number.parseInt(t.len_in_bytes),
|
||||||
vectors.k,
|
vectors.k,
|
||||||
hash
|
hash
|
||||||
);
|
);
|
||||||
deepStrictEqual(bytesToHex(p), t.uniform_bytes);
|
deepStrictEqual(bytesToHex(p), t.uniform_bytes);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
testExpandXOF(shake128, xof_shake128_36);
|
describe('expand_message_xof', () => {
|
||||||
testExpandXOF(shake128, xof_shake128_256);
|
testExpandXOF(shake128, xof_shake128_36);
|
||||||
testExpandXOF(shake256, xof_shake256_36);
|
testExpandXOF(shake128, xof_shake128_256);
|
||||||
|
testExpandXOF(shake256, xof_shake256_36);
|
||||||
|
});
|
||||||
|
|
||||||
function stringToFp(s) {
|
function stringToFp(s) {
|
||||||
// bls-G2 support
|
// bls-G2 support
|
||||||
@@ -99,32 +104,39 @@ 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);
|
||||||
|
|||||||
@@ -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 '../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 '../p256.js';
|
||||||
import { secp256r1, P256 } from '../lib/esm/p256.js';
|
import { secp384r1, P384 } from '../p384.js';
|
||||||
import { secp384r1, P384 } from '../lib/esm/p384.js';
|
import { secp521r1, P521 } from '../p521.js';
|
||||||
import { secp521r1, P521 } from '../lib/esm/p521.js';
|
import { secp256k1 } from '../secp256k1.js';
|
||||||
import { secp256k1 } from '../lib/esm/secp256k1.js';
|
import { hexToBytes, bytesToHex } from '../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 '../abstract/poseidon.js';
|
||||||
|
import * as stark from '../stark.js';
|
||||||
|
import * as mod from '../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 '../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 '../secp256k1.js';
|
||||||
|
import { secp256k1 as _secp } from '../secp256k1.js';
|
||||||
|
export { bytesToNumberBE, numberToBytesBE } from '../abstract/utils.js';
|
||||||
|
export { mod } from '../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 '../../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 '../../../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();
|
||||||
|
}
|
||||||
|
|||||||
205
test/stark/poseidon.test.js
Normal file
205
test/stark/poseidon.test.js
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
import { deepStrictEqual, throws } from 'assert';
|
||||||
|
import { describe, should } from 'micro-should';
|
||||||
|
import * as starknet from '../../stark.js';
|
||||||
|
import { bytesToHex as hex } from '@noble/hashes/utils';
|
||||||
|
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,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
should('Poseidon 2', () => {
|
||||||
|
// Cross-test with cairo-lang 0.11
|
||||||
|
deepStrictEqual(
|
||||||
|
starknet.poseidonHash(1n, 1n),
|
||||||
|
315729444126170353286530004158376771769107830460625027134495740547491428733n
|
||||||
|
);
|
||||||
|
deepStrictEqual(
|
||||||
|
starknet.poseidonHash(123n, 123n),
|
||||||
|
3149184350054566761517315875549307360045573205732410509163060794402900549639n
|
||||||
|
);
|
||||||
|
deepStrictEqual(
|
||||||
|
starknet.poseidonHash(1231231231231231231231231312312n, 1231231231231231231231231312312n),
|
||||||
|
2544250291965936388474000136445328679708604225006461780180655815882994563864n
|
||||||
|
);
|
||||||
|
// poseidonHashSingle
|
||||||
|
deepStrictEqual(
|
||||||
|
starknet.poseidonHashSingle(1n),
|
||||||
|
3085182978037364507644541379307921604860861694664657935759708330416374536741n
|
||||||
|
);
|
||||||
|
deepStrictEqual(
|
||||||
|
starknet.poseidonHashSingle(123n),
|
||||||
|
2751345659320901472675327541550911744303539407817894466726181731796247467344n
|
||||||
|
);
|
||||||
|
deepStrictEqual(
|
||||||
|
starknet.poseidonHashSingle(1231231231231231231231231312312n),
|
||||||
|
3083085683696942145160394401206391098729120397175152900096470498748103599322n
|
||||||
|
);
|
||||||
|
// poseidonHashMany
|
||||||
|
throws(() => starknet.poseidonHash(new Uint8Array([1, 2, 3])));
|
||||||
|
deepStrictEqual(
|
||||||
|
starknet.poseidonHashMany([1n]),
|
||||||
|
154809849725474173771833689306955346864791482278938452209165301614543497938n
|
||||||
|
);
|
||||||
|
deepStrictEqual(
|
||||||
|
starknet.poseidonHashMany([1n, 2n]),
|
||||||
|
1557996165160500454210437319447297236715335099509187222888255133199463084263n
|
||||||
|
);
|
||||||
|
deepStrictEqual(
|
||||||
|
starknet.poseidonHashMany([1n, 2n, 3n, 4n, 5n, 6n, 7n, 8n, 9n, 1n, 2n, 3n, 4n, 5n, 6n, 7n, 8n]),
|
||||||
|
976552833909388839716191681593200982850734838655927116322079791360264131378n
|
||||||
|
);
|
||||||
|
deepStrictEqual(
|
||||||
|
starknet.poseidonHashMany([1n, 2n, 3n, 4n, 5n, 6n, 7n, 8n, 9n, 1n, 2n, 3n, 4n, 5n, 6n, 7n]),
|
||||||
|
1426681430756292883765769449684978541173830451959857824597431064948702170774n
|
||||||
|
);
|
||||||
|
deepStrictEqual(
|
||||||
|
starknet.poseidonHashMany([1n, 2n, 3n, 4n, 5n, 6n, 7n, 8n, 9n, 1n, 2n, 3n, 4n, 5n, 6n]),
|
||||||
|
3578895185591466904832617962452140411216018208734547126302182794057260630783n
|
||||||
|
);
|
||||||
|
deepStrictEqual(
|
||||||
|
starknet.poseidonHashMany([1n, 2n, 3n, 4n, 5n, 6n, 7n, 8n, 9n, 1n, 2n, 3n, 4n, 5n]),
|
||||||
|
2047942584693618630610564708884241243670450597197937863619828684896211911953n
|
||||||
|
);
|
||||||
|
deepStrictEqual(
|
||||||
|
starknet.poseidonHashMany([1n, 2n, 3n, 4n, 5n, 6n, 7n, 8n, 9n, 1n, 2n, 3n, 4n]),
|
||||||
|
717812721730784692894550948559585317289413466140233907962980309405694367376n
|
||||||
|
);
|
||||||
|
deepStrictEqual(
|
||||||
|
starknet.poseidonHashMany([1n, 2n, 3n, 4n, 5n, 6n, 7n, 8n, 9n, 1n, 2n, 3n]),
|
||||||
|
2926122208425648133778911655767364584769133265503722614793281770361723147648n
|
||||||
|
);
|
||||||
|
deepStrictEqual(
|
||||||
|
starknet.poseidonHashMany([
|
||||||
|
154809849725474173771833689306955346864791482278938452209165301614543497938n,
|
||||||
|
1557996165160500454210437319447297236715335099509187222888255133199463084263n,
|
||||||
|
976552833909388839716191681593200982850734838655927116322079791360264131378n,
|
||||||
|
1426681430756292883765769449684978541173830451959857824597431064948702170774n,
|
||||||
|
3578895185591466904832617962452140411216018208734547126302182794057260630783n,
|
||||||
|
]),
|
||||||
|
1019392520709073131437410341528874594624843119359955302374885123884546721410n
|
||||||
|
);
|
||||||
|
// poseidon_hash_func
|
||||||
|
deepStrictEqual(
|
||||||
|
hex(starknet.poseidonHashFunc(new Uint8Array([1, 2]), new Uint8Array([3, 4]))),
|
||||||
|
'01f87cbb9c58139605384d0f0df49b446600af020aa9dac92301d45c96d78c0a'
|
||||||
|
);
|
||||||
|
deepStrictEqual(
|
||||||
|
hex(starknet.poseidonHashFunc(new Uint8Array(32).fill(255), new Uint8Array(32).fill(255))),
|
||||||
|
'05fd546b5ee3bcbbcbb733ed90bfc33033169d6765ac37bba71794a11cbb51a6'
|
||||||
|
);
|
||||||
|
deepStrictEqual(
|
||||||
|
hex(starknet.poseidonHashFunc(new Uint8Array(64).fill(255), new Uint8Array(64).fill(255))),
|
||||||
|
'07dba6b4d94b3e32697afe0825d6dac2dccafd439f7806a9575693c93735596b'
|
||||||
|
);
|
||||||
|
deepStrictEqual(
|
||||||
|
hex(starknet.poseidonHashFunc(new Uint8Array(256).fill(255), new Uint8Array(256).fill(255))),
|
||||||
|
'02f048581901865201dad701a5653d946b961748ec770fc11139aa7c06a9432a'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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 '../../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 '../../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,19 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"strict": true,
|
|
||||||
"outDir": "lib/esm",
|
|
||||||
"target": "es2020",
|
|
||||||
"module": "es6",
|
|
||||||
"moduleResolution": "node16",
|
|
||||||
"noUnusedLocals": true,
|
|
||||||
"baseUrl": ".",
|
|
||||||
"paths": {
|
|
||||||
"@noble/hashes/crypto": [ "src/crypto" ]
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"include": ["src"],
|
|
||||||
"exclude": [
|
|
||||||
"node_modules",
|
|
||||||
"lib",
|
|
||||||
],
|
|
||||||
}
|
|
||||||
@@ -2,11 +2,13 @@
|
|||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"outDir": "lib",
|
"declarationMap": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"outDir": ".",
|
||||||
"target": "es2020",
|
"target": "es2020",
|
||||||
"lib": ["es2020"], // Set explicitly to remove DOM
|
"lib": ["es2020"], // Set explicitly to remove DOM
|
||||||
"module": "commonjs",
|
"module": "es6",
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node16",
|
||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user