Compare commits
104 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b8b2e91f74 | ||
|
|
9ee694ae23 | ||
|
|
6bc4b35cf4 | ||
|
|
0163b63532 | ||
|
|
7e825520f1 | ||
|
|
d739297b2c | ||
|
|
285aa6375d | ||
|
|
8c77331ef2 | ||
|
|
669641e0a3 | ||
|
|
68dd57ed31 | ||
|
|
a9fdd6df9f | ||
|
|
d485d8b0e6 | ||
|
|
0fdd763dc7 | ||
|
|
586e2ad5fb | ||
|
|
ed81707bdc | ||
|
|
6d56b2d78e | ||
|
|
8397241a8f | ||
|
|
001d0cc24a | ||
|
|
ce9d165657 | ||
|
|
2902b0299a | ||
|
|
e1cb8549e8 | ||
|
|
26ebb5dcce | ||
|
|
8b2863aeac | ||
|
|
b1f50d9364 | ||
|
|
b81d74d3cb | ||
|
|
d5fe537159 | ||
|
|
cde1d5c488 | ||
|
|
3486bbf6b8 | ||
|
|
0d7a8296c5 | ||
|
|
0f1e7a5a43 | ||
|
|
3da48cf899 | ||
|
|
4ec46dd65d | ||
|
|
7073f63c6b | ||
|
|
80966cbd03 | ||
|
|
98ea15dca4 | ||
|
|
e1910e85ea | ||
|
|
4d311d7294 | ||
|
|
c36d90cae6 | ||
|
|
af5aa8424f | ||
|
|
67b99652fc | ||
|
|
c8d292976b | ||
|
|
daffaa2339 | ||
|
|
a462fc5779 | ||
|
|
fe3491c5aa | ||
|
|
c0877ba69a | ||
|
|
8e449cc78c | ||
|
|
1b6071cabd | ||
|
|
debb9d9709 | ||
|
|
d2c6459756 | ||
|
|
47533b6336 | ||
|
|
00b73b68d3 | ||
|
|
cef4b52d12 | ||
|
|
47ce547dcf | ||
|
|
e2a7594eae | ||
|
|
823149ecd9 | ||
|
|
e57aec63d8 | ||
|
|
837aca98c9 | ||
|
|
dbb16b0e5e | ||
|
|
e14af67254 | ||
|
|
4780850748 | ||
|
|
3374a70f47 | ||
|
|
131f88b504 | ||
|
|
4333e9a686 | ||
|
|
a60d15ff05 | ||
|
|
ceffbc69da | ||
|
|
c75129e629 | ||
|
|
f39fb80c52 | ||
|
|
fcd422d246 | ||
|
|
ed9bf89038 | ||
|
|
7262b4219f | ||
|
|
02b0b25147 | ||
|
|
79100c2d47 | ||
|
|
4ef2cad685 | ||
|
|
69b3ab5a57 | ||
|
|
9465e60d30 | ||
|
|
0fb78b7097 | ||
|
|
be0b2a32a5 | ||
|
|
3d77422731 | ||
|
|
c46914f1bc | ||
|
|
f250f355e8 | ||
|
|
c095d74673 | ||
|
|
ac52fea952 | ||
|
|
f2ee24bee4 | ||
|
|
cffea91061 | ||
|
|
5fc38fc0e7 | ||
|
|
849dc38f3c | ||
|
|
0422e6ef38 | ||
|
|
21d2438a33 | ||
|
|
cea4696599 | ||
|
|
f14b8d2be5 | ||
|
|
2ed27da8eb | ||
|
|
17e5be5f1b | ||
|
|
a49f0d266e | ||
|
|
bfbcf733e6 | ||
|
|
7fda6de619 | ||
|
|
2b908ad602 | ||
|
|
ceb3f67faa | ||
|
|
a2c87f9c2f | ||
|
|
e1fd346279 | ||
|
|
11e78aadbf | ||
|
|
055147f1be | ||
|
|
6f99f6042e | ||
|
|
1e47bf2372 | ||
|
|
40530eae0c |
14
.gitignore
vendored
14
.gitignore
vendored
@@ -1,7 +1,13 @@
|
||||
build/
|
||||
node_modules/
|
||||
coverage/
|
||||
/lib/**/*.js
|
||||
/lib/**/*.ts
|
||||
/lib/**/*.d.ts.map
|
||||
/curve-definitions/lib
|
||||
/*.js
|
||||
/*.ts
|
||||
/*.js.map
|
||||
/*.d.ts.map
|
||||
/esm/*.js
|
||||
/esm/*.ts
|
||||
/esm/*.js.map
|
||||
/esm/*.d.ts.map
|
||||
/esm/abstract
|
||||
/abstract/
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| >=0.5.0 | :white_check_mark: |
|
||||
| <0.5.0 | :x: |
|
||||
| >=1.0.0 | :white_check_mark: |
|
||||
| <1.0.0 | :x: |
|
||||
|
||||
## 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();
|
||||
}
|
||||
@@ -12,15 +12,11 @@
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"micro-bmark": "0.2.1"
|
||||
"micro-bmark": "0.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@noble/bls12-381": "^1.4.0",
|
||||
"@noble/ed25519": "^1.7.1",
|
||||
"@noble/hashes": "^1.1.5",
|
||||
"@noble/secp256k1": "^1.7.0",
|
||||
"@starkware-industries/starkware-crypto-utils": "^0.0.2",
|
||||
"calculate-correlation": "^1.2.3",
|
||||
"elliptic": "^6.5.4"
|
||||
}
|
||||
}
|
||||
|
||||
22
benchmark/secp256k1.js
Normal file
22
benchmark/secp256k1.js
Normal file
@@ -0,0 +1,22 @@
|
||||
import { run, mark, utils } from 'micro-bmark';
|
||||
import { secp256k1, schnorr } from '../secp256k1.js';
|
||||
import { generateData } from './_shared.js';
|
||||
|
||||
run(async () => {
|
||||
const RAM = false;
|
||||
if (RAM) utils.logMem();
|
||||
console.log(`\x1b[36msecp256k1\x1b[0m`);
|
||||
await mark('init', 1, () => secp256k1.utils.precompute(8));
|
||||
const d = generateData(secp256k1);
|
||||
await mark('getPublicKey', 10000, () => secp256k1.getPublicKey(d.priv));
|
||||
await mark('sign', 10000, () => secp256k1.sign(d.msg, d.priv));
|
||||
await mark('verify', 1000, () => secp256k1.verify(d.sig, d.msg, d.pub));
|
||||
const pub2 = secp256k1.getPublicKey(secp256k1.utils.randomPrivateKey());
|
||||
await mark('getSharedSecret', 1000, () => secp256k1.getSharedSecret(d.priv, pub2));
|
||||
await mark('recoverPublicKey', 1000, () => d.sig.recoverPublicKey(d.msg));
|
||||
const s = schnorr.sign(d.msg, d.priv);
|
||||
const spub = schnorr.getPublicKey(d.priv);
|
||||
await mark('schnorr.sign', 1000, () => schnorr.sign(d.msg, d.priv));
|
||||
await mark('schnorr.verify', 1000, () => schnorr.verify(s, d.msg, spub));
|
||||
if (RAM) utils.logMem();
|
||||
});
|
||||
56
benchmark/stark.js
Normal file
56
benchmark/stark.js
Normal file
@@ -0,0 +1,56 @@
|
||||
import { run, mark, compare, utils } from 'micro-bmark';
|
||||
import * as starkwareCrypto from '@starkware-industries/starkware-crypto-utils';
|
||||
import * as stark from '../stark.js';
|
||||
|
||||
run(async () => {
|
||||
const RAM = false;
|
||||
if (RAM) utils.logMem();
|
||||
console.log(`\x1b[36mstark\x1b[0m`);
|
||||
await mark('init', 1, () => stark.utils.precompute(8));
|
||||
const d = (() => {
|
||||
const priv = '2dccce1da22003777062ee0870e9881b460a8b7eca276870f57c601f182136c';
|
||||
const msg = 'c465dd6b1bbffdb05442eb17f5ca38ad1aa78a6f56bf4415bdee219114a47';
|
||||
const pub = stark.getPublicKey(priv);
|
||||
const sig = stark.sign(msg, priv);
|
||||
|
||||
const privateKey = '2dccce1da22003777062ee0870e9881b460a8b7eca276870f57c601f182136c';
|
||||
const msgHash = 'c465dd6b1bbffdb05442eb17f5ca38ad1aa78a6f56bf4415bdee219114a47';
|
||||
const keyPair = starkwareCrypto.default.ec.keyFromPrivate(privateKey, 'hex');
|
||||
const publicKeyStark = starkwareCrypto.default.ec.keyFromPublic(
|
||||
keyPair.getPublic(true, 'hex'),
|
||||
'hex'
|
||||
);
|
||||
return { priv, sig, msg, pub, publicKeyStark, msgHash, keyPair };
|
||||
})();
|
||||
await compare('pedersen', 500, {
|
||||
old: () => {
|
||||
return starkwareCrypto.default.pedersen([
|
||||
'3d937c035c878245caf64531a5756109c53068da139362728feb561405371cb',
|
||||
'208a0a10250e382e1e4bbe2880906c2791bf6275695e02fbbc6aeff9cd8b31a',
|
||||
]);
|
||||
},
|
||||
noble: () => {
|
||||
return stark.pedersen(
|
||||
'3d937c035c878245caf64531a5756109c53068da139362728feb561405371cb',
|
||||
'208a0a10250e382e1e4bbe2880906c2791bf6275695e02fbbc6aeff9cd8b31a'
|
||||
);
|
||||
},
|
||||
});
|
||||
await mark('poseidon', 10000, () => stark.poseidonHash(
|
||||
0x3d937c035c878245caf64531a5756109c53068da139362728feb561405371cbn,
|
||||
0x208a0a10250e382e1e4bbe2880906c2791bf6275695e02fbbc6aeff9cd8b31an
|
||||
));
|
||||
await compare('verify', 500, {
|
||||
old: () => {
|
||||
return starkwareCrypto.default.verify(
|
||||
d.publicKeyStark,
|
||||
d.msgHash,
|
||||
starkwareCrypto.default.sign(d.keyPair, d.msgHash)
|
||||
);
|
||||
},
|
||||
noble: () => {
|
||||
return stark.verify(stark.sign(d.msg, d.priv), d.msg, d.pub);
|
||||
},
|
||||
});
|
||||
if (RAM) utils.logMem();
|
||||
});
|
||||
178
package-lock.json
generated
Normal file
178
package-lock.json
generated
Normal file
@@ -0,0 +1,178 @@
|
||||
{
|
||||
"name": "@noble/curves",
|
||||
"version": "0.7.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@noble/curves",
|
||||
"version": "0.7.2",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@noble/hashes": "1.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@scure/bip32": "~1.1.5",
|
||||
"@scure/bip39": "~1.1.1",
|
||||
"@types/node": "18.11.3",
|
||||
"fast-check": "3.0.0",
|
||||
"micro-bmark": "0.3.1",
|
||||
"micro-should": "0.4.0",
|
||||
"prettier": "2.8.3",
|
||||
"typescript": "4.7.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@noble/hashes": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.2.0.tgz",
|
||||
"integrity": "sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/@noble/secp256k1": {
|
||||
"version": "1.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.1.tgz",
|
||||
"integrity": "sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/@scure/base": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz",
|
||||
"integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/@scure/bip32": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.1.5.tgz",
|
||||
"integrity": "sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"@noble/hashes": "~1.2.0",
|
||||
"@noble/secp256k1": "~1.7.0",
|
||||
"@scure/base": "~1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@scure/bip39": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.1.tgz",
|
||||
"integrity": "sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"@noble/hashes": "~1.2.0",
|
||||
"@scure/base": "~1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "18.11.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.3.tgz",
|
||||
"integrity": "sha512-fNjDQzzOsZeKZu5NATgXUPsaFaTxeRgFXoosrHivTl8RGeV733OLawXsGfEk9a8/tySyZUyiZ6E8LcjPFZ2y1A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/fast-check": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.0.0.tgz",
|
||||
"integrity": "sha512-uujtrFJEQQqnIMO52ARwzPcuV4omiL1OJBUBLE9WnNFeu0A97sREXDOmCIHY+Z6KLVcemUf09rWr0q0Xy/Y/Ew==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"pure-rand": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/fast-check"
|
||||
}
|
||||
},
|
||||
"node_modules/micro-bmark": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/micro-bmark/-/micro-bmark-0.3.1.tgz",
|
||||
"integrity": "sha512-bNaKObD4yPAAPrpEqp5jO6LJ2sEFgLoFSmRjEY809mJ62+2AehI/K3+RlVpN3Oo92RHpgC2RQhj6b1Tb4dmo+w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/micro-should": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/micro-should/-/micro-should-0.4.0.tgz",
|
||||
"integrity": "sha512-Vclj8yrngSYc9Y3dL2C+AdUlTkyx/syWc4R7LYfk4h7+icfF0DoUBGjjUIaEDzZA19RzoI+Hg8rW9IRoNGP0tQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "2.8.3",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.3.tgz",
|
||||
"integrity": "sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"prettier": "bin-prettier.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/pure-rand": {
|
||||
"version": "5.0.5",
|
||||
"resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-5.0.5.tgz",
|
||||
"integrity": "sha512-BwQpbqxSCBJVpamI6ydzcKqyFmnd5msMWUGvzXLm1aXvusbbgkbOto/EUPM00hjveJEaJtdbhUjKSzWRhQVkaw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/dubzzz"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/fast-check"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "4.7.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.3.tgz",
|
||||
"integrity": "sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.2.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
176
package.json
176
package.json
@@ -1,12 +1,18 @@
|
||||
{
|
||||
"name": "@noble/curves",
|
||||
"version": "0.5.2",
|
||||
"version": "0.7.3",
|
||||
"description": "Minimal, auditable JS implementation of elliptic curve cryptography",
|
||||
"files": [
|
||||
"lib"
|
||||
"abstract",
|
||||
"esm",
|
||||
"src",
|
||||
"*.js",
|
||||
"*.js.map",
|
||||
"*.d.ts",
|
||||
"*.d.ts.map"
|
||||
],
|
||||
"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:release": "rollup -c rollup.config.js",
|
||||
"lint": "prettier --check 'src/**/*.{js,ts}' 'test/*.js'",
|
||||
@@ -21,142 +27,134 @@
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@noble/hashes": "1.1.5"
|
||||
"@noble/hashes": "1.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-node-resolve": "13.3.0",
|
||||
"@scure/base": "~1.1.1",
|
||||
"@scure/bip32": "~1.1.1",
|
||||
"@scure/bip39": "~1.1.0",
|
||||
"@scure/bip32": "~1.1.5",
|
||||
"@scure/bip39": "~1.1.1",
|
||||
"@types/node": "18.11.3",
|
||||
"fast-check": "3.0.0",
|
||||
"micro-bmark": "0.2.0",
|
||||
"micro-should": "0.3.0",
|
||||
"prettier": "2.6.2",
|
||||
"rollup": "2.75.5",
|
||||
"micro-bmark": "0.3.1",
|
||||
"micro-should": "0.4.0",
|
||||
"prettier": "2.8.3",
|
||||
"typescript": "4.7.3"
|
||||
},
|
||||
"main": "index.js",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./lib/index.d.ts",
|
||||
"import": "./lib/esm/index.js",
|
||||
"default": "./lib/index.js"
|
||||
"types": "./index.d.ts",
|
||||
"import": "./esm/index.js",
|
||||
"default": "./index.js"
|
||||
},
|
||||
"./abstract/edwards": {
|
||||
"types": "./lib/abstract/edwards.d.ts",
|
||||
"import": "./lib/esm/abstract/edwards.js",
|
||||
"default": "./lib/abstract/edwards.js"
|
||||
"types": "./abstract/edwards.d.ts",
|
||||
"import": "./esm/abstract/edwards.js",
|
||||
"default": "./abstract/edwards.js"
|
||||
},
|
||||
"./abstract/modular": {
|
||||
"types": "./lib/abstract/modular.d.ts",
|
||||
"import": "./lib/esm/abstract/modular.js",
|
||||
"default": "./lib/abstract/modular.js"
|
||||
"types": "./abstract/modular.d.ts",
|
||||
"import": "./esm/abstract/modular.js",
|
||||
"default": "./abstract/modular.js"
|
||||
},
|
||||
"./abstract/montgomery": {
|
||||
"types": "./lib/abstract/montgomery.d.ts",
|
||||
"import": "./lib/esm/abstract/montgomery.js",
|
||||
"default": "./lib/abstract/montgomery.js"
|
||||
"types": "./abstract/montgomery.d.ts",
|
||||
"import": "./esm/abstract/montgomery.js",
|
||||
"default": "./abstract/montgomery.js"
|
||||
},
|
||||
"./abstract/weierstrass": {
|
||||
"types": "./lib/abstract/weierstrass.d.ts",
|
||||
"import": "./lib/esm/abstract/weierstrass.js",
|
||||
"default": "./lib/abstract/weierstrass.js"
|
||||
"types": "./abstract/weierstrass.d.ts",
|
||||
"import": "./esm/abstract/weierstrass.js",
|
||||
"default": "./abstract/weierstrass.js"
|
||||
},
|
||||
"./abstract/bls": {
|
||||
"types": "./lib/abstract/bls.d.ts",
|
||||
"import": "./lib/esm/abstract/bls.js",
|
||||
"default": "./lib/abstract/bls.js"
|
||||
"types": "./abstract/bls.d.ts",
|
||||
"import": "./esm/abstract/bls.js",
|
||||
"default": "./abstract/bls.js"
|
||||
},
|
||||
"./abstract/hash-to-curve": {
|
||||
"types": "./lib/abstract/hash-to-curve.d.ts",
|
||||
"import": "./lib/esm/abstract/hash-to-curve.js",
|
||||
"default": "./lib/abstract/hash-to-curve.js"
|
||||
"types": "./abstract/hash-to-curve.d.ts",
|
||||
"import": "./esm/abstract/hash-to-curve.js",
|
||||
"default": "./abstract/hash-to-curve.js"
|
||||
},
|
||||
"./abstract/group": {
|
||||
"types": "./lib/abstract/group.d.ts",
|
||||
"import": "./lib/esm/abstract/group.js",
|
||||
"default": "./lib/abstract/group.js"
|
||||
"./abstract/curve": {
|
||||
"types": "./abstract/curve.d.ts",
|
||||
"import": "./esm/abstract/curve.js",
|
||||
"default": "./abstract/curve.js"
|
||||
},
|
||||
"./abstract/utils": {
|
||||
"types": "./lib/abstract/utils.d.ts",
|
||||
"import": "./lib/esm/abstract/utils.js",
|
||||
"default": "./lib/abstract/utils.js"
|
||||
"types": "./abstract/utils.d.ts",
|
||||
"import": "./esm/abstract/utils.js",
|
||||
"default": "./abstract/utils.js"
|
||||
},
|
||||
"./abstract/poseidon": {
|
||||
"types": "./abstract/poseidon.d.ts",
|
||||
"import": "./esm/abstract/poseidon.js",
|
||||
"default": "./abstract/poseidon.js"
|
||||
},
|
||||
"./_shortw_utils": {
|
||||
"types": "./lib/_shortw_utils.d.ts",
|
||||
"import": "./lib/esm/_shortw_utils.js",
|
||||
"default": "./lib/_shortw_utils.js"
|
||||
"types": "./_shortw_utils.d.ts",
|
||||
"import": "./esm/_shortw_utils.js",
|
||||
"default": "./_shortw_utils.js"
|
||||
},
|
||||
"./bls12-381": {
|
||||
"types": "./lib/bls12-381.d.ts",
|
||||
"import": "./lib/esm/bls12-381.js",
|
||||
"default": "./lib/bls12-381.js"
|
||||
"types": "./bls12-381.d.ts",
|
||||
"import": "./esm/bls12-381.js",
|
||||
"default": "./bls12-381.js"
|
||||
},
|
||||
"./bn": {
|
||||
"types": "./lib/bn.d.ts",
|
||||
"import": "./lib/esm/bn.js",
|
||||
"default": "./lib/bn.js"
|
||||
"types": "./bn.d.ts",
|
||||
"import": "./esm/bn.js",
|
||||
"default": "./bn.js"
|
||||
},
|
||||
"./ed25519": {
|
||||
"types": "./lib/ed25519.d.ts",
|
||||
"import": "./lib/esm/ed25519.js",
|
||||
"default": "./lib/ed25519.js"
|
||||
"types": "./ed25519.d.ts",
|
||||
"import": "./esm/ed25519.js",
|
||||
"default": "./ed25519.js"
|
||||
},
|
||||
"./ed448": {
|
||||
"types": "./lib/ed448.d.ts",
|
||||
"import": "./lib/esm/ed448.js",
|
||||
"default": "./lib/ed448.js"
|
||||
"types": "./ed448.d.ts",
|
||||
"import": "./esm/ed448.js",
|
||||
"default": "./ed448.js"
|
||||
},
|
||||
"./index": {
|
||||
"types": "./lib/index.d.ts",
|
||||
"import": "./lib/esm/index.js",
|
||||
"default": "./lib/index.js"
|
||||
"types": "./index.d.ts",
|
||||
"import": "./esm/index.js",
|
||||
"default": "./index.js"
|
||||
},
|
||||
"./jubjub": {
|
||||
"types": "./lib/jubjub.d.ts",
|
||||
"import": "./lib/esm/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"
|
||||
"types": "./jubjub.d.ts",
|
||||
"import": "./esm/jubjub.js",
|
||||
"default": "./jubjub.js"
|
||||
},
|
||||
"./p256": {
|
||||
"types": "./lib/p256.d.ts",
|
||||
"import": "./lib/esm/p256.js",
|
||||
"default": "./lib/p256.js"
|
||||
"types": "./p256.d.ts",
|
||||
"import": "./esm/p256.js",
|
||||
"default": "./p256.js"
|
||||
},
|
||||
"./p384": {
|
||||
"types": "./lib/p384.d.ts",
|
||||
"import": "./lib/esm/p384.js",
|
||||
"default": "./lib/p384.js"
|
||||
"types": "./p384.d.ts",
|
||||
"import": "./esm/p384.js",
|
||||
"default": "./p384.js"
|
||||
},
|
||||
"./p521": {
|
||||
"types": "./lib/p521.d.ts",
|
||||
"import": "./lib/esm/p521.js",
|
||||
"default": "./lib/p521.js"
|
||||
"types": "./p521.d.ts",
|
||||
"import": "./esm/p521.js",
|
||||
"default": "./p521.js"
|
||||
},
|
||||
"./pasta": {
|
||||
"types": "./lib/pasta.d.ts",
|
||||
"import": "./lib/esm/pasta.js",
|
||||
"default": "./lib/pasta.js"
|
||||
"types": "./pasta.d.ts",
|
||||
"import": "./esm/pasta.js",
|
||||
"default": "./pasta.js"
|
||||
},
|
||||
"./secp256k1": {
|
||||
"types": "./lib/secp256k1.d.ts",
|
||||
"import": "./lib/esm/secp256k1.js",
|
||||
"default": "./lib/secp256k1.js"
|
||||
"types": "./secp256k1.d.ts",
|
||||
"import": "./esm/secp256k1.js",
|
||||
"default": "./secp256k1.js"
|
||||
},
|
||||
"./stark": {
|
||||
"types": "./lib/stark.d.ts",
|
||||
"import": "./lib/esm/stark.js",
|
||||
"default": "./lib/stark.js"
|
||||
"types": "./stark.d.ts",
|
||||
"import": "./esm/stark.js",
|
||||
"default": "./stark.js"
|
||||
}
|
||||
},
|
||||
"keywords": [
|
||||
|
||||
@@ -4,6 +4,7 @@ import { concatBytes, randomBytes } from '@noble/hashes/utils';
|
||||
import { weierstrass, CurveType } from './abstract/weierstrass.js';
|
||||
import { CHash } from './abstract/utils.js';
|
||||
|
||||
// connects noble-curves to noble-hashes
|
||||
export function getHash(hash: CHash) {
|
||||
return {
|
||||
hash,
|
||||
|
||||
@@ -11,107 +11,108 @@
|
||||
* 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 * as mod from './modular.js';
|
||||
import * as ut from './utils.js';
|
||||
// Types require separate import
|
||||
import { Hex, PrivKey } from './utils.js';
|
||||
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 {
|
||||
htfOpts,
|
||||
stringToBytes,
|
||||
hash_to_field as hashToField,
|
||||
expand_message_xmd as expandMessageXMD,
|
||||
} from './hash-to-curve.js';
|
||||
import { CurvePointsType, PointType, CurvePointsRes, weierstrassPoints } from './weierstrass.js';
|
||||
CurvePointsType,
|
||||
ProjPointType as ProjPointType,
|
||||
CurvePointsRes,
|
||||
weierstrassPoints,
|
||||
} from './weierstrass.js';
|
||||
|
||||
type Fp = bigint; // Can be different field?
|
||||
|
||||
export type SignatureCoder<Fp2> = {
|
||||
decode(hex: Hex): PointType<Fp2>;
|
||||
encode(point: PointType<Fp2>): Uint8Array;
|
||||
decode(hex: Hex): ProjPointType<Fp2>;
|
||||
encode(point: ProjPointType<Fp2>): Uint8Array;
|
||||
};
|
||||
|
||||
export type CurveType<Fp, Fp2, Fp6, Fp12> = {
|
||||
r: bigint;
|
||||
G1: Omit<CurvePointsType<Fp>, 'n'>;
|
||||
G1: Omit<CurvePointsType<Fp>, 'n'> & {
|
||||
mapToCurve: htf.MapToCurve<Fp>;
|
||||
htfDefaults: htf.Opts;
|
||||
};
|
||||
G2: Omit<CurvePointsType<Fp2>, 'n'> & {
|
||||
Signature: SignatureCoder<Fp2>;
|
||||
mapToCurve: htf.MapToCurve<Fp2>;
|
||||
htfDefaults: htf.Opts;
|
||||
};
|
||||
x: bigint;
|
||||
Fp: mod.Field<Fp>;
|
||||
Fr: mod.Field<bigint>;
|
||||
Fp2: mod.Field<Fp2> & {
|
||||
Fp: Field<Fp>;
|
||||
Fr: Field<bigint>;
|
||||
Fp2: Field<Fp2> & {
|
||||
reim: (num: Fp2) => { re: bigint; im: bigint };
|
||||
multiplyByB: (num: Fp2) => Fp2;
|
||||
frobeniusMap(num: Fp2, power: number): Fp2;
|
||||
};
|
||||
Fp6: mod.Field<Fp6>;
|
||||
Fp12: mod.Field<Fp12> & {
|
||||
Fp6: Field<Fp6>;
|
||||
Fp12: Field<Fp12> & {
|
||||
frobeniusMap(num: Fp12, power: number): Fp12;
|
||||
multiplyBy014(num: Fp12, o0: Fp2, o1: Fp2, o4: Fp2): Fp12;
|
||||
conjugate(num: Fp12): Fp12;
|
||||
finalExponentiate(num: Fp12): Fp12;
|
||||
};
|
||||
htfDefaults: htfOpts;
|
||||
hash: ut.CHash; // Because we need outputLen for DRBG
|
||||
htfDefaults: htf.Opts;
|
||||
hash: CHash; // Because we need outputLen for DRBG
|
||||
randomBytes: (bytesLength?: number) => Uint8Array;
|
||||
};
|
||||
|
||||
export type CurveFn<Fp, Fp2, Fp6, Fp12> = {
|
||||
CURVE: CurveType<Fp, Fp2, Fp6, Fp12>;
|
||||
Fr: mod.Field<bigint>;
|
||||
Fp: mod.Field<Fp>;
|
||||
Fp2: mod.Field<Fp2>;
|
||||
Fp6: mod.Field<Fp6>;
|
||||
Fp12: mod.Field<Fp12>;
|
||||
G1: CurvePointsRes<Fp>;
|
||||
G2: CurvePointsRes<Fp2>;
|
||||
Fr: Field<bigint>;
|
||||
Fp: Field<Fp>;
|
||||
Fp2: Field<Fp2>;
|
||||
Fp6: Field<Fp6>;
|
||||
Fp12: Field<Fp12>;
|
||||
G1: CurvePointsRes<Fp> & ReturnType<typeof htf.createHasher<Fp>>;
|
||||
G2: CurvePointsRes<Fp2> & ReturnType<typeof htf.createHasher<Fp2>>;
|
||||
Signature: SignatureCoder<Fp2>;
|
||||
millerLoop: (ell: [Fp2, Fp2, Fp2][], g1: [Fp, Fp]) => Fp12;
|
||||
calcPairingPrecomputes: (x: Fp2, y: Fp2) => [Fp2, Fp2, Fp2][];
|
||||
pairing: (P: PointType<Fp>, Q: PointType<Fp2>, withFinalExponent?: boolean) => Fp12;
|
||||
calcPairingPrecomputes: (p: AffinePoint<Fp2>) => [Fp2, Fp2, Fp2][];
|
||||
pairing: (P: ProjPointType<Fp>, Q: ProjPointType<Fp2>, withFinalExponent?: boolean) => Fp12;
|
||||
getPublicKey: (privateKey: PrivKey) => Uint8Array;
|
||||
sign: {
|
||||
(message: Hex, privateKey: PrivKey): Uint8Array;
|
||||
(message: PointType<Fp2>, privateKey: PrivKey): PointType<Fp2>;
|
||||
(message: ProjPointType<Fp2>, privateKey: PrivKey): ProjPointType<Fp2>;
|
||||
};
|
||||
verify: (
|
||||
signature: Hex | PointType<Fp2>,
|
||||
message: Hex | PointType<Fp2>,
|
||||
publicKey: Hex | PointType<Fp>
|
||||
signature: Hex | ProjPointType<Fp2>,
|
||||
message: Hex | ProjPointType<Fp2>,
|
||||
publicKey: Hex | ProjPointType<Fp>
|
||||
) => boolean;
|
||||
aggregatePublicKeys: {
|
||||
(publicKeys: Hex[]): Uint8Array;
|
||||
(publicKeys: PointType<Fp>[]): PointType<Fp>;
|
||||
(publicKeys: ProjPointType<Fp>[]): ProjPointType<Fp>;
|
||||
};
|
||||
aggregateSignatures: {
|
||||
(signatures: Hex[]): Uint8Array;
|
||||
(signatures: PointType<Fp2>[]): PointType<Fp2>;
|
||||
(signatures: ProjPointType<Fp2>[]): ProjPointType<Fp2>;
|
||||
};
|
||||
verifyBatch: (
|
||||
signature: Hex | PointType<Fp2>,
|
||||
messages: (Hex | PointType<Fp2>)[],
|
||||
publicKeys: (Hex | PointType<Fp>)[]
|
||||
signature: Hex | ProjPointType<Fp2>,
|
||||
messages: (Hex | ProjPointType<Fp2>)[],
|
||||
publicKeys: (Hex | ProjPointType<Fp>)[]
|
||||
) => boolean;
|
||||
utils: {
|
||||
stringToBytes: typeof stringToBytes;
|
||||
hashToField: typeof hashToField;
|
||||
expandMessageXMD: typeof expandMessageXMD;
|
||||
getDSTLabel: () => string;
|
||||
setDSTLabel(newLabel: string): void;
|
||||
randomPrivateKey: () => Uint8Array;
|
||||
};
|
||||
};
|
||||
|
||||
export function bls<Fp2, Fp6, Fp12>(
|
||||
CURVE: CurveType<Fp, Fp2, Fp6, Fp12>
|
||||
): CurveFn<Fp, Fp2, Fp6, Fp12> {
|
||||
// Fields looks pretty specific for curve, so for now we need to pass them with options
|
||||
// Fields looks pretty specific for curve, so for now we need to pass them with opts
|
||||
const { Fp, Fr, Fp2, Fp6, Fp12 } = CURVE;
|
||||
const BLS_X_LEN = ut.bitLen(CURVE.x);
|
||||
const BLS_X_LEN = bitLen(CURVE.x);
|
||||
const groupLen = 32; // TODO: calculate; hardcoded for now
|
||||
|
||||
// Pre-compute coefficients for sparse multiplication
|
||||
// Point addition and point double calculations is reused for coefficients
|
||||
function calcPairingPrecomputes(x: Fp2, y: Fp2) {
|
||||
function calcPairingPrecomputes(p: AffinePoint<Fp2>) {
|
||||
const { x, y } = p;
|
||||
// prettier-ignore
|
||||
const Qx = x, Qy = y, Qz = Fp2.ONE;
|
||||
// prettier-ignore
|
||||
@@ -119,32 +120,32 @@ export function bls<Fp2, Fp6, Fp12>(
|
||||
let ell_coeff: [Fp2, Fp2, Fp2][] = [];
|
||||
for (let i = BLS_X_LEN - 2; i >= 0; i--) {
|
||||
// Double
|
||||
let t0 = Fp2.square(Ry); // Ry²
|
||||
let t1 = Fp2.square(Rz); // Rz²
|
||||
let t0 = Fp2.sqr(Ry); // Ry²
|
||||
let t1 = Fp2.sqr(Rz); // Rz²
|
||||
let t2 = Fp2.multiplyByB(Fp2.mul(t1, 3n)); // 3 * T1 * B
|
||||
let t3 = Fp2.mul(t2, 3n); // 3 * T2
|
||||
let t4 = Fp2.sub(Fp2.sub(Fp2.square(Fp2.add(Ry, Rz)), t1), t0); // (Ry + Rz)² - T1 - T0
|
||||
let t4 = Fp2.sub(Fp2.sub(Fp2.sqr(Fp2.add(Ry, Rz)), t1), t0); // (Ry + Rz)² - T1 - T0
|
||||
ell_coeff.push([
|
||||
Fp2.sub(t2, t0), // T2 - T0
|
||||
Fp2.mul(Fp2.square(Rx), 3n), // 3 * Rx²
|
||||
Fp2.negate(t4), // -T4
|
||||
Fp2.mul(Fp2.sqr(Rx), 3n), // 3 * Rx²
|
||||
Fp2.neg(t4), // -T4
|
||||
]);
|
||||
Rx = Fp2.div(Fp2.mul(Fp2.mul(Fp2.sub(t0, t3), Rx), Ry), 2n); // ((T0 - T3) * Rx * Ry) / 2
|
||||
Ry = Fp2.sub(Fp2.square(Fp2.div(Fp2.add(t0, t3), 2n)), Fp2.mul(Fp2.square(t2), 3n)); // ((T0 + T3) / 2)² - 3 * T2²
|
||||
Ry = Fp2.sub(Fp2.sqr(Fp2.div(Fp2.add(t0, t3), 2n)), Fp2.mul(Fp2.sqr(t2), 3n)); // ((T0 + T3) / 2)² - 3 * T2²
|
||||
Rz = Fp2.mul(t0, t4); // T0 * T4
|
||||
if (ut.bitGet(CURVE.x, i)) {
|
||||
if (bitGet(CURVE.x, i)) {
|
||||
// Addition
|
||||
let t0 = Fp2.sub(Ry, Fp2.mul(Qy, Rz)); // Ry - Qy * Rz
|
||||
let t1 = Fp2.sub(Rx, Fp2.mul(Qx, Rz)); // Rx - Qx * Rz
|
||||
ell_coeff.push([
|
||||
Fp2.sub(Fp2.mul(t0, Qx), Fp2.mul(t1, Qy)), // T0 * Qx - T1 * Qy
|
||||
Fp2.negate(t0), // -T0
|
||||
Fp2.neg(t0), // -T0
|
||||
t1, // T1
|
||||
]);
|
||||
let t2 = Fp2.square(t1); // T1²
|
||||
let t2 = Fp2.sqr(t1); // T1²
|
||||
let t3 = Fp2.mul(t2, t1); // T2 * T1
|
||||
let t4 = Fp2.mul(t2, Rx); // T2 * Rx
|
||||
let t5 = Fp2.add(Fp2.sub(t3, Fp2.mul(t4, 2n)), Fp2.mul(Fp2.square(t0), Rz)); // T3 - 2 * T4 + T0² * Rz
|
||||
let t5 = Fp2.add(Fp2.sub(t3, Fp2.mul(t4, 2n)), Fp2.mul(Fp2.sqr(t0), Rz)); // T3 - 2 * T4 + T0² * Rz
|
||||
Rx = Fp2.mul(t1, t5); // T1 * T5
|
||||
Ry = Fp2.sub(Fp2.mul(Fp2.sub(t4, t5), t0), Fp2.mul(t3, Ry)); // (T4 - T5) * T0 - T3 * Ry
|
||||
Rz = Fp2.mul(Rz, t3); // Rz * T3
|
||||
@@ -161,46 +162,31 @@ export function bls<Fp2, Fp6, Fp12>(
|
||||
for (let j = 0, i = BLS_X_LEN - 2; i >= 0; i--, j++) {
|
||||
const E = ell[j];
|
||||
f12 = Fp12.multiplyBy014(f12, E[0], Fp2.mul(E[1], Px), Fp2.mul(E[2], Py));
|
||||
if (ut.bitGet(x, i)) {
|
||||
if (bitGet(x, i)) {
|
||||
j += 1;
|
||||
const F = ell[j];
|
||||
f12 = Fp12.multiplyBy014(f12, F[0], Fp2.mul(F[1], Px), Fp2.mul(F[2], Py));
|
||||
}
|
||||
if (i !== 0) f12 = Fp12.square(f12);
|
||||
if (i !== 0) f12 = Fp12.sqr(f12);
|
||||
}
|
||||
return Fp12.conjugate(f12);
|
||||
}
|
||||
|
||||
const utils = {
|
||||
hexToBytes: ut.hexToBytes,
|
||||
bytesToHex: ut.bytesToHex,
|
||||
stringToBytes: stringToBytes,
|
||||
// TODO: do we need to export it here?
|
||||
hashToField: (
|
||||
msg: Uint8Array,
|
||||
count: number,
|
||||
options: Partial<typeof CURVE.htfDefaults> = {}
|
||||
) => hashToField(msg, count, { ...CURVE.htfDefaults, ...options }),
|
||||
expandMessageXMD: (msg: Uint8Array, DST: Uint8Array, lenInBytes: number, H = CURVE.hash) =>
|
||||
expandMessageXMD(msg, DST, lenInBytes, H),
|
||||
hashToPrivateKey: (hash: Hex): Uint8Array => Fr.toBytes(ut.hashToPrivateScalar(hash, CURVE.r)),
|
||||
randomBytes: (bytesLength: number = groupLen): Uint8Array => CURVE.randomBytes(bytesLength),
|
||||
randomPrivateKey: (): Uint8Array => utils.hashToPrivateKey(utils.randomBytes(groupLen + 8)),
|
||||
getDSTLabel: () => CURVE.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');
|
||||
}
|
||||
CURVE.htfDefaults.DST = newLabel;
|
||||
randomPrivateKey: (): Uint8Array => {
|
||||
return Fr.toBytes(hashToPrivateScalar(CURVE.randomBytes(groupLen + 8), CURVE.r));
|
||||
},
|
||||
};
|
||||
|
||||
// Point on G1 curve: (x, y)
|
||||
const G1 = weierstrassPoints({
|
||||
n: Fr.ORDER,
|
||||
...CURVE.G1,
|
||||
});
|
||||
const G1_ = weierstrassPoints({ n: Fr.ORDER, ...CURVE.G1 });
|
||||
const G1 = Object.assign(
|
||||
G1_,
|
||||
htf.createHasher(G1_.ProjectivePoint, CURVE.G1.mapToCurve, {
|
||||
...CURVE.htfDefaults,
|
||||
...CURVE.G1.htfDefaults,
|
||||
})
|
||||
);
|
||||
|
||||
// Sparse multiplication against precomputed coefficients
|
||||
// TODO: replace with weakmap?
|
||||
@@ -208,83 +194,92 @@ export function bls<Fp2, Fp6, Fp12>(
|
||||
function pairingPrecomputes(point: G2): [Fp2, Fp2, Fp2][] {
|
||||
const p = point as G2 & withPairingPrecomputes;
|
||||
if (p._PPRECOMPUTES) return p._PPRECOMPUTES;
|
||||
p._PPRECOMPUTES = calcPairingPrecomputes(p.x, p.y);
|
||||
p._PPRECOMPUTES = calcPairingPrecomputes(point.toAffine());
|
||||
return p._PPRECOMPUTES;
|
||||
}
|
||||
|
||||
function clearPairingPrecomputes(point: G2) {
|
||||
const p = point as G2 & withPairingPrecomputes;
|
||||
p._PPRECOMPUTES = undefined;
|
||||
}
|
||||
clearPairingPrecomputes;
|
||||
|
||||
function millerLoopG1(Q: G1, P: G2): Fp12 {
|
||||
return millerLoop(pairingPrecomputes(P), [Q.x, Q.y]);
|
||||
}
|
||||
// TODO: export
|
||||
// function clearPairingPrecomputes(point: G2) {
|
||||
// const p = point as G2 & withPairingPrecomputes;
|
||||
// p._PPRECOMPUTES = undefined;
|
||||
// }
|
||||
|
||||
// Point on G2 curve (complex numbers): (x₁, x₂+i), (y₁, y₂+i)
|
||||
const G2 = weierstrassPoints({
|
||||
n: Fr.ORDER,
|
||||
...CURVE.G2,
|
||||
});
|
||||
const G2_ = weierstrassPoints({ n: Fr.ORDER, ...CURVE.G2 });
|
||||
const G2 = Object.assign(
|
||||
G2_,
|
||||
htf.createHasher(G2_.ProjectivePoint as htf.H2CPointConstructor<Fp2>, CURVE.G2.mapToCurve, {
|
||||
...CURVE.htfDefaults,
|
||||
...CURVE.G2.htfDefaults,
|
||||
})
|
||||
);
|
||||
|
||||
const { Signature } = CURVE.G2;
|
||||
|
||||
// Calculates bilinear pairing
|
||||
function pairing(P: G1, Q: G2, withFinalExponent: boolean = true): Fp12 {
|
||||
if (P.equals(G1.Point.ZERO) || Q.equals(G2.Point.ZERO))
|
||||
throw new Error('No pairings at point of Infinity');
|
||||
P.assertValidity();
|
||||
function pairing(Q: G1, P: G2, withFinalExponent: boolean = true): Fp12 {
|
||||
if (Q.equals(G1.ProjectivePoint.ZERO) || P.equals(G2.ProjectivePoint.ZERO))
|
||||
throw new Error('pairing is not available for ZERO point');
|
||||
Q.assertValidity();
|
||||
P.assertValidity();
|
||||
// Performance: 9ms for millerLoop and ~14ms for exp.
|
||||
const looped = millerLoopG1(P, Q);
|
||||
const Qa = Q.toAffine();
|
||||
const looped = millerLoop(pairingPrecomputes(P), [Qa.x, Qa.y]);
|
||||
return withFinalExponent ? Fp12.finalExponentiate(looped) : looped;
|
||||
}
|
||||
type G1 = typeof G1.Point.BASE;
|
||||
type G2 = typeof G2.Point.BASE;
|
||||
type G1 = typeof G1.ProjectivePoint.BASE;
|
||||
type G2 = typeof G2.ProjectivePoint.BASE;
|
||||
|
||||
type G1Hex = Hex | G1;
|
||||
type G2Hex = Hex | G2;
|
||||
function normP1(point: G1Hex): G1 {
|
||||
return point instanceof G1.Point ? (point as G1) : G1.Point.fromHex(point);
|
||||
return point instanceof G1.ProjectivePoint ? (point as G1) : G1.ProjectivePoint.fromHex(point);
|
||||
}
|
||||
function normP2(point: G2Hex): G2 {
|
||||
return point instanceof G2.Point ? point : Signature.decode(point);
|
||||
return point instanceof G2.ProjectivePoint ? point : Signature.decode(point);
|
||||
}
|
||||
function normP2Hash(point: G2Hex): G2 {
|
||||
return point instanceof G2.Point ? point : G2.Point.hashToCurve(point);
|
||||
function normP2Hash(point: G2Hex, htfOpts?: htf.htfBasicOpts): G2 {
|
||||
return point instanceof G2.ProjectivePoint
|
||||
? point
|
||||
: (G2.hashToCurve(ensureBytes('point', point), htfOpts) as G2);
|
||||
}
|
||||
|
||||
// Multiplies generator by private key.
|
||||
// P = pk x G
|
||||
function getPublicKey(privateKey: PrivKey): Uint8Array {
|
||||
return G1.Point.fromPrivateKey(privateKey).toRawBytes(true);
|
||||
return G1.ProjectivePoint.fromPrivateKey(privateKey).toRawBytes(true);
|
||||
}
|
||||
|
||||
// Executes `hashToCurve` on the message and then multiplies the result by private key.
|
||||
// S = pk x H(m)
|
||||
function sign(message: Hex, privateKey: PrivKey): Uint8Array;
|
||||
function sign(message: G2, privateKey: PrivKey): G2;
|
||||
function sign(message: G2Hex, privateKey: PrivKey): Uint8Array | G2 {
|
||||
const msgPoint = normP2Hash(message);
|
||||
function sign(message: Hex, privateKey: PrivKey, htfOpts?: htf.htfBasicOpts): Uint8Array;
|
||||
function sign(message: G2, privateKey: PrivKey, htfOpts?: htf.htfBasicOpts): G2;
|
||||
function sign(message: G2Hex, privateKey: PrivKey, htfOpts?: htf.htfBasicOpts): Uint8Array | G2 {
|
||||
const msgPoint = normP2Hash(message, htfOpts);
|
||||
msgPoint.assertValidity();
|
||||
const sigPoint = msgPoint.multiply(G1.normalizePrivateKey(privateKey));
|
||||
if (message instanceof G2.Point) return sigPoint;
|
||||
const sigPoint = msgPoint.multiply(G1.normPrivateKeyToScalar(privateKey));
|
||||
if (message instanceof G2.ProjectivePoint) return sigPoint;
|
||||
return Signature.encode(sigPoint);
|
||||
}
|
||||
|
||||
// Checks if pairing of public key & hash is equal to pairing of generator & signature.
|
||||
// e(P, H(m)) == e(G, S)
|
||||
function verify(signature: G2Hex, message: G2Hex, publicKey: G1Hex): boolean {
|
||||
function verify(
|
||||
signature: G2Hex,
|
||||
message: G2Hex,
|
||||
publicKey: G1Hex,
|
||||
htfOpts?: htf.htfBasicOpts
|
||||
): boolean {
|
||||
const P = normP1(publicKey);
|
||||
const Hm = normP2Hash(message);
|
||||
const G = G1.Point.BASE;
|
||||
const Hm = normP2Hash(message, htfOpts);
|
||||
const G = G1.ProjectivePoint.BASE;
|
||||
const S = normP2(signature);
|
||||
// Instead of doing 2 exponentiations, we use property of billinear maps
|
||||
// and do one exp after multiplying 2 points.
|
||||
const ePHm = pairing(P.negate(), Hm, false);
|
||||
const eGS = pairing(G, S, false);
|
||||
const exp = Fp12.finalExponentiate(Fp12.mul(eGS, ePHm));
|
||||
return Fp12.equals(exp, Fp12.ONE);
|
||||
return Fp12.eql(exp, Fp12.ONE);
|
||||
}
|
||||
|
||||
// Adds a bunch of public key points together.
|
||||
@@ -293,11 +288,9 @@ export function bls<Fp2, Fp6, Fp12>(
|
||||
function aggregatePublicKeys(publicKeys: G1[]): G1;
|
||||
function aggregatePublicKeys(publicKeys: G1Hex[]): Uint8Array | G1 {
|
||||
if (!publicKeys.length) throw new Error('Expected non-empty array');
|
||||
const agg = publicKeys
|
||||
.map(normP1)
|
||||
.reduce((sum, p) => sum.add(G1.ProjectivePoint.fromAffine(p)), G1.ProjectivePoint.ZERO);
|
||||
const aggAffine = agg.toAffine();
|
||||
if (publicKeys[0] instanceof G1.Point) {
|
||||
const agg = publicKeys.map(normP1).reduce((sum, p) => sum.add(p), G1.ProjectivePoint.ZERO);
|
||||
const aggAffine = agg; //.toAffine();
|
||||
if (publicKeys[0] instanceof G1.ProjectivePoint) {
|
||||
aggAffine.assertValidity();
|
||||
return aggAffine;
|
||||
}
|
||||
@@ -310,11 +303,9 @@ export function bls<Fp2, Fp6, Fp12>(
|
||||
function aggregateSignatures(signatures: G2[]): G2;
|
||||
function aggregateSignatures(signatures: G2Hex[]): Uint8Array | G2 {
|
||||
if (!signatures.length) throw new Error('Expected non-empty array');
|
||||
const agg = signatures
|
||||
.map(normP2)
|
||||
.reduce((sum, s) => sum.add(G2.ProjectivePoint.fromAffine(s)), G2.ProjectivePoint.ZERO);
|
||||
const aggAffine = agg.toAffine();
|
||||
if (signatures[0] instanceof G2.Point) {
|
||||
const agg = signatures.map(normP2).reduce((sum, s) => sum.add(s), G2.ProjectivePoint.ZERO);
|
||||
const aggAffine = agg; //.toAffine();
|
||||
if (signatures[0] instanceof G2.ProjectivePoint) {
|
||||
aggAffine.assertValidity();
|
||||
return aggAffine;
|
||||
}
|
||||
@@ -323,12 +314,20 @@ export function bls<Fp2, Fp6, Fp12>(
|
||||
|
||||
// https://ethresear.ch/t/fast-verification-of-multiple-bls-signatures/5407
|
||||
// e(G, S) = e(G, SUM(n)(Si)) = MUL(n)(e(G, Si))
|
||||
function verifyBatch(signature: G2Hex, messages: G2Hex[], publicKeys: G1Hex[]): boolean {
|
||||
function verifyBatch(
|
||||
signature: G2Hex,
|
||||
messages: G2Hex[],
|
||||
publicKeys: G1Hex[],
|
||||
htfOpts?: htf.htfBasicOpts
|
||||
): boolean {
|
||||
// @ts-ignore
|
||||
// console.log('verifyBatch', bytesToHex(signature as any), messages, publicKeys.map(bytesToHex));
|
||||
|
||||
if (!messages.length) throw new Error('Expected non-empty messages array');
|
||||
if (publicKeys.length !== messages.length)
|
||||
throw new Error('Pubkey count should equal msg count');
|
||||
const sig = normP2(signature);
|
||||
const nMessages = messages.map(normP2Hash);
|
||||
const nMessages = messages.map((i) => normP2Hash(i, htfOpts));
|
||||
const nPublicKeys = publicKeys.map(normP1);
|
||||
try {
|
||||
const paired = [];
|
||||
@@ -336,23 +335,23 @@ export function bls<Fp2, Fp6, Fp12>(
|
||||
const groupPublicKey = nMessages.reduce(
|
||||
(groupPublicKey, subMessage, i) =>
|
||||
subMessage === message ? groupPublicKey.add(nPublicKeys[i]) : groupPublicKey,
|
||||
G1.Point.ZERO
|
||||
G1.ProjectivePoint.ZERO
|
||||
);
|
||||
// const msg = message instanceof PointG2 ? message : await PointG2.hashToCurve(message);
|
||||
// Possible to batch pairing for same msg with different groupPublicKey here
|
||||
paired.push(pairing(groupPublicKey, message, false));
|
||||
}
|
||||
paired.push(pairing(G1.Point.BASE.negate(), sig, false));
|
||||
paired.push(pairing(G1.ProjectivePoint.BASE.negate(), sig, false));
|
||||
const product = paired.reduce((a, b) => Fp12.mul(a, b), Fp12.ONE);
|
||||
const exp = Fp12.finalExponentiate(product);
|
||||
return Fp12.equals(exp, Fp12.ONE);
|
||||
return Fp12.eql(exp, Fp12.ONE);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Pre-compute points. Refer to README.
|
||||
G1.Point.BASE._setWindowSize(4);
|
||||
G1.ProjectivePoint.BASE._setWindowSize(4);
|
||||
|
||||
return {
|
||||
CURVE,
|
||||
Fr,
|
||||
|
||||
@@ -1,22 +1,41 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
// Abelian group utilities
|
||||
import { Field, validateField, nLength } from './modular.js';
|
||||
import { validateObject } from './utils.js';
|
||||
const _0n = BigInt(0);
|
||||
const _1n = BigInt(1);
|
||||
|
||||
export type AffinePoint<T> = {
|
||||
x: T;
|
||||
y: T;
|
||||
} & { z?: never; t?: never };
|
||||
|
||||
export interface Group<T extends Group<T>> {
|
||||
double(): T;
|
||||
negate(): T;
|
||||
add(other: T): T;
|
||||
subtract(other: T): T;
|
||||
equals(other: T): boolean;
|
||||
multiply(scalar: number | bigint): T;
|
||||
multiply(scalar: bigint): T;
|
||||
}
|
||||
|
||||
export type GroupConstructor<T> = {
|
||||
BASE: T;
|
||||
ZERO: T;
|
||||
};
|
||||
// Not big, but pretty complex and it is easy to break stuff. To avoid too much copy paste
|
||||
export type Mapper<T> = (i: T[]) => T[];
|
||||
|
||||
// Elliptic curve multiplication of Point by scalar. 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) {
|
||||
const constTimeNegate = (condition: boolean, item: T): T => {
|
||||
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.
|
||||
* 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.
|
||||
* @returns 65K precomputed points, depending on W
|
||||
* Number of precomputed points depends on the curve size:
|
||||
* 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>[] {
|
||||
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 affinePoint optional 2d point to save cached precompute windows on it.
|
||||
* @param n bits
|
||||
* @param precomputes precomputed tables
|
||||
* @param n scalar (we don't check here, but should be less than curve order)
|
||||
* @returns real and fake (for const-time) points
|
||||
*/
|
||||
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
|
||||
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.
|
||||
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,19 +1,9 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
// Twisted Edwards curve. The formula is: ax² + y² = 1 + dx²y²
|
||||
|
||||
// Differences from @noble/ed25519 1.7:
|
||||
// 1. Variable field element lengths between EDDSA/ECDH:
|
||||
// EDDSA (RFC8032) is 456 bits / 57 bytes, ECDH (RFC7748) is 448 bits / 56 bytes
|
||||
// 2. Different addition formula (doubling is same)
|
||||
// 3. uvRatio differs between curves (half-expected, not only pow fn changes)
|
||||
// 4. Point decompression code is different (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 { mod } from './modular.js';
|
||||
import * as ut from './utils.js';
|
||||
import { ensureBytes, Hex, PrivKey } from './utils.js';
|
||||
import { Group, GroupConstructor, wNAF } from './group.js';
|
||||
import { hash_to_field as hashToField, htfOpts, validateHTFOpts } from './hash-to-curve.js';
|
||||
import { ensureBytes, FHash, Hex } from './utils.js';
|
||||
import { Group, GroupConstructor, wNAF, BasicCurve, validateBasic, AffinePoint } from './curve.js';
|
||||
|
||||
// Be friendly to bad ECMAScript parsers by not using bigint literals like 123n
|
||||
const _0n = BigInt(0);
|
||||
@@ -22,142 +12,92 @@ const _2n = BigInt(2);
|
||||
const _8n = BigInt(8);
|
||||
|
||||
// Edwards curves must declare params a & d.
|
||||
export type CurveType = ut.BasicCurve<bigint> & {
|
||||
// Params: a, d
|
||||
a: bigint;
|
||||
d: bigint;
|
||||
// Hashes
|
||||
// The interface, because we need outputLen for DRBG
|
||||
hash: ut.CHash;
|
||||
// CSPRNG
|
||||
randomBytes: (bytesLength?: number) => Uint8Array;
|
||||
// Probably clears bits in a byte array to produce a valid field element
|
||||
adjustScalarBytes?: (bytes: Uint8Array) => Uint8Array;
|
||||
// Used during hashing
|
||||
domain?: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array;
|
||||
// Ratio √(u/v)
|
||||
uvRatio?: (u: bigint, v: bigint) => { isValid: boolean; value: bigint };
|
||||
// RFC 8032 pre-hashing of messages to sign() / verify()
|
||||
preHash?: ut.CHash;
|
||||
// Hash to field options
|
||||
htfDefaults?: htfOpts;
|
||||
mapToCurve?: (scalar: bigint[]) => { x: bigint; y: bigint };
|
||||
export type CurveType = BasicCurve<bigint> & {
|
||||
a: bigint; // curve param a
|
||||
d: bigint; // curve param d
|
||||
hash: FHash; // Hashing
|
||||
randomBytes: (bytesLength?: number) => Uint8Array; // CSPRNG
|
||||
adjustScalarBytes?: (bytes: Uint8Array) => Uint8Array; // clears bits to get valid field elemtn
|
||||
domain?: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array; // Used for hashing
|
||||
uvRatio?: (u: bigint, v: bigint) => { isValid: boolean; value: bigint }; // Ratio √(u/v)
|
||||
preHash?: FHash; // RFC 8032 pre-hashing of messages to sign() / verify()
|
||||
mapToCurve?: (scalar: bigint[]) => AffinePoint<bigint>; // for hash-to-curve standard
|
||||
};
|
||||
|
||||
function validateOpts(curve: CurveType) {
|
||||
const opts = ut.validateOpts(curve);
|
||||
if (typeof opts.hash !== 'function' || !ut.isPositiveInt(opts.hash.outputLen))
|
||||
throw new Error('Invalid hash function');
|
||||
for (const i of ['a', 'd'] as const) {
|
||||
const val = opts[i];
|
||||
if (typeof val !== 'bigint') throw new Error(`Invalid curve param ${i}=${val} (${typeof val})`);
|
||||
const opts = validateBasic(curve);
|
||||
ut.validateObject(
|
||||
curve,
|
||||
{
|
||||
hash: 'function',
|
||||
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'] 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
|
||||
return Object.freeze({ ...opts } as const);
|
||||
}
|
||||
|
||||
// Instance
|
||||
export interface SignatureType {
|
||||
readonly r: PointType;
|
||||
readonly s: bigint;
|
||||
assertValidity(): SignatureType;
|
||||
toRawBytes(): Uint8Array;
|
||||
toHex(): string;
|
||||
}
|
||||
// Static methods
|
||||
export type SignatureConstructor = {
|
||||
new (r: PointType, s: bigint): SignatureType;
|
||||
fromHex(hex: Hex): SignatureType;
|
||||
};
|
||||
|
||||
// Instance of Extended Point with coordinates in X, Y, Z, T
|
||||
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;
|
||||
export interface ExtPointType extends Group<ExtPointType> {
|
||||
readonly ex: bigint;
|
||||
readonly ey: bigint;
|
||||
readonly ez: bigint;
|
||||
readonly et: bigint;
|
||||
assertValidity(): void;
|
||||
multiply(scalar: bigint): ExtPointType;
|
||||
multiplyUnsafe(scalar: bigint): ExtPointType;
|
||||
isSmallOrder(): boolean;
|
||||
isTorsionFree(): boolean;
|
||||
toAffine(invZ?: bigint): PointType;
|
||||
clearCofactor(): ExtendedPointType;
|
||||
clearCofactor(): ExtPointType;
|
||||
toAffine(iz?: bigint): AffinePoint<bigint>;
|
||||
}
|
||||
// Static methods of Extended Point with coordinates in X, Y, Z, T
|
||||
export interface ExtendedPointConstructor extends GroupConstructor<ExtendedPointType> {
|
||||
new (x: bigint, y: bigint, z: bigint, t: bigint): ExtendedPointType;
|
||||
fromAffine(p: PointType): ExtendedPointType;
|
||||
toAffineBatch(points: ExtendedPointType[]): PointType[];
|
||||
normalizeZ(points: ExtendedPointType[]): ExtendedPointType[];
|
||||
export interface ExtPointConstructor extends GroupConstructor<ExtPointType> {
|
||||
new (x: bigint, y: bigint, z: bigint, t: bigint): ExtPointType;
|
||||
fromAffine(p: AffinePoint<bigint>): ExtPointType;
|
||||
fromHex(hex: Hex): ExtPointType;
|
||||
fromPrivateKey(privateKey: Hex): ExtPointType;
|
||||
}
|
||||
|
||||
// Instance of Affine Point with coordinates in X, Y
|
||||
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 of Affine Point with coordinates in X, Y
|
||||
export interface PointConstructor extends GroupConstructor<PointType> {
|
||||
new (x: bigint, y: bigint): PointType;
|
||||
fromHex(hex: Hex): PointType;
|
||||
fromPrivateKey(privateKey: PrivKey): PointType;
|
||||
hashToCurve(msg: Hex, options?: Partial<htfOpts>): PointType;
|
||||
encodeToCurve(msg: Hex, options?: Partial<htfOpts>): PointType;
|
||||
}
|
||||
|
||||
export type PubKey = Hex | PointType;
|
||||
export type SigType = Hex | SignatureType;
|
||||
|
||||
export type CurveFn = {
|
||||
CURVE: ReturnType<typeof validateOpts>;
|
||||
getPublicKey: (privateKey: PrivKey, isCompressed?: boolean) => Uint8Array;
|
||||
getPublicKey: (privateKey: Hex) => Uint8Array;
|
||||
sign: (message: Hex, privateKey: Hex) => Uint8Array;
|
||||
verify: (sig: SigType, message: Hex, publicKey: PubKey) => boolean;
|
||||
Point: PointConstructor;
|
||||
ExtendedPoint: ExtendedPointConstructor;
|
||||
Signature: SignatureConstructor;
|
||||
verify: (sig: Hex, message: Hex, publicKey: Hex) => boolean;
|
||||
ExtendedPoint: ExtPointConstructor;
|
||||
utils: {
|
||||
randomPrivateKey: () => Uint8Array;
|
||||
getExtendedPublicKey: (key: PrivKey) => {
|
||||
getExtendedPublicKey: (key: Hex) => {
|
||||
head: Uint8Array;
|
||||
prefix: Uint8Array;
|
||||
scalar: bigint;
|
||||
point: PointType;
|
||||
point: ExtPointType;
|
||||
pointBytes: Uint8Array;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
// NOTE: it is not generic twisted curve for now, but ed25519/ed448 generic implementation
|
||||
// It is not generic twisted curve for now, but ed25519/ed448 generic implementation
|
||||
export function twistedEdwards(curveDef: CurveType): CurveFn {
|
||||
const CURVE = validateOpts(curveDef) as ReturnType<typeof validateOpts>;
|
||||
const Fp = CURVE.Fp;
|
||||
const CURVE_ORDER = CURVE.n;
|
||||
const maxGroupElement = _2n ** BigInt(CURVE.nByteLength * 8);
|
||||
|
||||
// Function overrides
|
||||
const { randomBytes } = CURVE;
|
||||
const modP = Fp.create;
|
||||
const { Fp, n: CURVE_ORDER, preHash, hash: cHash, randomBytes, nByteLength, h: cofactor } = CURVE;
|
||||
const MASK = _2n ** BigInt(nByteLength * 8);
|
||||
const modP = Fp.create; // Function overrides
|
||||
|
||||
// sqrt(u/v)
|
||||
const uvRatio =
|
||||
CURVE.uvRatio ||
|
||||
((u: bigint, v: bigint) => {
|
||||
try {
|
||||
return { isValid: true, value: Fp.sqrt(u * Fp.invert(v)) };
|
||||
return { isValid: true, value: Fp.sqrt(u * Fp.inv(v)) };
|
||||
} catch (e) {
|
||||
return { isValid: false, value: _0n };
|
||||
}
|
||||
@@ -169,41 +109,95 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
||||
if (ctx.length || phflag) throw new Error('Contexts/pre-hash are not supported');
|
||||
return data;
|
||||
}); // NOOP
|
||||
|
||||
/**
|
||||
* Extended Point works in extended coordinates: (x, y, z, t) ∋ (x=x/z, y=y/z, t=xy).
|
||||
* Default Point works in affine coordinates: (x, y)
|
||||
* https://en.wikipedia.org/wiki/Twisted_Edwards_curve#Extended_coordinates
|
||||
*/
|
||||
class ExtendedPoint implements ExtendedPointType {
|
||||
constructor(readonly x: bigint, readonly y: bigint, readonly z: bigint, readonly t: bigint) {}
|
||||
|
||||
static BASE = new ExtendedPoint(CURVE.Gx, CURVE.Gy, _1n, modP(CURVE.Gx * CURVE.Gy));
|
||||
static ZERO = new ExtendedPoint(_0n, _1n, _1n, _0n);
|
||||
static fromAffine(p: Point): ExtendedPoint {
|
||||
if (!(p instanceof Point)) {
|
||||
throw new TypeError('ExtendedPoint#fromAffine: expected Point');
|
||||
const 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}`);
|
||||
}
|
||||
if (p.equals(Point.ZERO)) return ExtendedPoint.ZERO;
|
||||
return new ExtendedPoint(p.x, p.y, _1n, modP(p.x * p.y));
|
||||
function assertGE0(n: bigint) {
|
||||
// n in [0..CURVE_ORDER-1]
|
||||
return n === _0n ? n : assertInRange(n, CURVE_ORDER); // GE = prime subgroup, not full group
|
||||
}
|
||||
// Takes a bunch of Jacobian Points but executes only one
|
||||
// invert on all of them. invert is very slow operation,
|
||||
// so this improves performance massively.
|
||||
static toAffineBatch(points: ExtendedPoint[]): Point[] {
|
||||
const toInv = Fp.invertBatch(points.map((p) => p.z));
|
||||
return points.map((p, i) => p.toAffine(toInv[i]));
|
||||
const pointPrecomputes = new Map<Point, Point[]>();
|
||||
function isPoint(other: unknown) {
|
||||
if (!(other instanceof Point)) throw new Error('ExtendedPoint expected');
|
||||
}
|
||||
// Extended Point works in extended coordinates: (x, y, z, t) ∋ (x=x/z, y=y/z, t=xy).
|
||||
// https://en.wikipedia.org/wiki/Twisted_Edwards_curve#Extended_coordinates
|
||||
class Point implements ExtPointType {
|
||||
static readonly BASE = new Point(CURVE.Gx, CURVE.Gy, _1n, modP(CURVE.Gx * CURVE.Gy));
|
||||
static readonly ZERO = new Point(_0n, _1n, _1n, _0n); // 0, 1, 1, 0
|
||||
|
||||
constructor(
|
||||
readonly ex: bigint,
|
||||
readonly ey: bigint,
|
||||
readonly ez: bigint,
|
||||
readonly et: bigint
|
||||
) {
|
||||
if (!in0MaskRange(ex)) throw new Error('x required');
|
||||
if (!in0MaskRange(ey)) throw new Error('y required');
|
||||
if (!in0MaskRange(ez)) throw new Error('z required');
|
||||
if (!in0MaskRange(et)) throw new Error('t required');
|
||||
}
|
||||
|
||||
static normalizeZ(points: ExtendedPoint[]): ExtendedPoint[] {
|
||||
return this.toAffineBatch(points).map(this.fromAffine);
|
||||
get x(): bigint {
|
||||
return this.toAffine().x;
|
||||
}
|
||||
get y(): bigint {
|
||||
return this.toAffine().y;
|
||||
}
|
||||
|
||||
static fromAffine(p: AffinePoint<bigint>): Point {
|
||||
if (p instanceof Point) throw new Error('extended point not allowed');
|
||||
const { x, y } = p || {};
|
||||
if (!in0MaskRange(x) || !in0MaskRange(y)) throw new Error('invalid affine point');
|
||||
return new Point(x, y, _1n, modP(x * y));
|
||||
}
|
||||
static normalizeZ(points: Point[]): Point[] {
|
||||
const toInv = Fp.invertBatch(points.map((p) => p.ez));
|
||||
return points.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine);
|
||||
}
|
||||
|
||||
// We calculate precomputes for elliptic curve point multiplication
|
||||
// using windowed method. This specifies window size and
|
||||
// stores precomputed values. Usually only base point would be precomputed.
|
||||
_WINDOW_SIZE?: number;
|
||||
|
||||
// "Private method", don't use it directly
|
||||
_setWindowSize(windowSize: number) {
|
||||
this._WINDOW_SIZE = windowSize;
|
||||
pointPrecomputes.delete(this);
|
||||
}
|
||||
// Not required for fromHex(), which always creates valid points.
|
||||
// Could be useful for fromAffine().
|
||||
assertValidity(): void {
|
||||
const { a, d } = CURVE;
|
||||
if (this.is0()) throw new Error('bad point: ZERO'); // TODO: optimize, with vars below?
|
||||
// Equation in affine coordinates: ax² + y² = 1 + dx²y²
|
||||
// Equation in projective coordinates (X/Z, Y/Z, Z): (aX² + Y²)Z² = Z⁴ + dX²Y²
|
||||
const { ex: X, ey: Y, ez: Z, et: T } = this;
|
||||
const X2 = modP(X * X); // X²
|
||||
const Y2 = modP(Y * Y); // Y²
|
||||
const Z2 = modP(Z * Z); // Z²
|
||||
const Z4 = modP(Z2 * Z2); // Z⁴
|
||||
const aX2 = modP(X2 * a); // aX²
|
||||
const left = modP(Z2 * modP(aX2 + Y2)); // (aX² + Y²)Z²
|
||||
const right = modP(Z4 + modP(d * modP(X2 * Y2))); // Z⁴ + dX²Y²
|
||||
if (left !== right) throw new Error('bad point: equation left != right (1)');
|
||||
// In Extended coordinates we also have T, which is x*y=T/Z: check X*Y == Z*T
|
||||
const XY = modP(X * Y);
|
||||
const ZT = modP(Z * T);
|
||||
if (XY !== ZT) throw new Error('bad point: equation left != right (2)');
|
||||
}
|
||||
|
||||
// Compare one point to another.
|
||||
equals(other: ExtendedPoint): boolean {
|
||||
assertExtPoint(other);
|
||||
const { x: X1, y: Y1, z: Z1 } = this;
|
||||
const { x: X2, y: Y2, z: Z2 } = other;
|
||||
equals(other: Point): boolean {
|
||||
isPoint(other);
|
||||
const { ex: X1, ey: Y1, ez: Z1 } = this;
|
||||
const { ex: X2, ey: Y2, ez: Z2 } = other;
|
||||
const X1Z2 = modP(X1 * Z2);
|
||||
const X2Z1 = modP(X2 * Z1);
|
||||
const Y1Z2 = modP(Y1 * Z2);
|
||||
@@ -211,17 +205,21 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
||||
return X1Z2 === X2Z1 && Y1Z2 === Y2Z1;
|
||||
}
|
||||
|
||||
// Inverses point to one corresponding to (x, -y) in Affine coordinates.
|
||||
negate(): ExtendedPoint {
|
||||
return new ExtendedPoint(modP(-this.x), this.y, this.z, modP(-this.t));
|
||||
protected is0(): boolean {
|
||||
return this.equals(Point.ZERO);
|
||||
}
|
||||
|
||||
negate(): Point {
|
||||
// Flips point sign to a negative one (-x, y in affine coords)
|
||||
return new Point(modP(-this.ex), this.ey, this.ez, modP(-this.et));
|
||||
}
|
||||
|
||||
// Fast algo for doubling Extended Point.
|
||||
// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd
|
||||
// Cost: 4M + 4S + 1*a + 6add + 1*2.
|
||||
double(): ExtendedPoint {
|
||||
double(): Point {
|
||||
const { a } = CURVE;
|
||||
const { x: X1, y: Y1, z: Z1 } = this;
|
||||
const { ex: X1, ey: Y1, ez: Z1 } = this;
|
||||
const A = modP(X1 * X1); // A = X12
|
||||
const B = modP(Y1 * Y1); // B = Y12
|
||||
const C = modP(_2n * modP(Z1 * Z1)); // C = 2*Z12
|
||||
@@ -235,17 +233,17 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
||||
const Y3 = modP(G * H); // Y3 = G*H
|
||||
const T3 = modP(E * H); // T3 = E*H
|
||||
const Z3 = modP(F * G); // Z3 = F*G
|
||||
return new ExtendedPoint(X3, Y3, Z3, T3);
|
||||
return new Point(X3, Y3, Z3, T3);
|
||||
}
|
||||
|
||||
// Fast algo for adding 2 Extended Points.
|
||||
// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#addition-add-2008-hwcd
|
||||
// Cost: 9M + 1*a + 1*d + 7add.
|
||||
add(other: ExtendedPoint) {
|
||||
assertExtPoint(other);
|
||||
add(other: Point) {
|
||||
isPoint(other);
|
||||
const { a, d } = CURVE;
|
||||
const { x: X1, y: Y1, z: Z1, t: T1 } = this;
|
||||
const { x: X2, y: Y2, z: Z2, t: T2 } = other;
|
||||
const { ex: X1, ey: Y1, ez: Z1, et: T1 } = this;
|
||||
const { ex: X2, ey: Y2, ez: Z2, et: T2 } = other;
|
||||
// Faster algo for adding 2 Extended Points when curve's a=-1.
|
||||
// http://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-4
|
||||
// Cost: 8M + 8add + 2*2.
|
||||
@@ -264,7 +262,7 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
||||
const Y3 = modP(G * H);
|
||||
const T3 = modP(E * H);
|
||||
const Z3 = modP(F * G);
|
||||
return new ExtendedPoint(X3, Y3, Z3, T3);
|
||||
return new Point(X3, Y3, Z3, T3);
|
||||
}
|
||||
const A = modP(X1 * X2); // A = X1*X2
|
||||
const B = modP(Y1 * Y2); // B = Y1*Y2
|
||||
@@ -279,44 +277,31 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
||||
const T3 = modP(E * H); // T3 = E*H
|
||||
const Z3 = modP(F * G); // Z3 = F*G
|
||||
|
||||
return new ExtendedPoint(X3, Y3, Z3, T3);
|
||||
return new Point(X3, Y3, Z3, T3);
|
||||
}
|
||||
|
||||
subtract(other: ExtendedPoint): ExtendedPoint {
|
||||
subtract(other: Point): Point {
|
||||
return this.add(other.negate());
|
||||
}
|
||||
|
||||
private wNAF(n: bigint, affinePoint?: Point): ExtendedPoint {
|
||||
if (!affinePoint && this.equals(ExtendedPoint.BASE)) affinePoint = Point.BASE;
|
||||
const W = (affinePoint && affinePoint._WINDOW_SIZE) || 1;
|
||||
let precomputes = affinePoint && pointPrecomputes.get(affinePoint);
|
||||
if (!precomputes) {
|
||||
precomputes = wnaf.precomputeWindow(this, W) as ExtendedPoint[];
|
||||
if (affinePoint && W !== 1) {
|
||||
precomputes = ExtendedPoint.normalizeZ(precomputes);
|
||||
pointPrecomputes.set(affinePoint, precomputes);
|
||||
}
|
||||
}
|
||||
const { p, f } = wnaf.wNAF(W, precomputes, n);
|
||||
return ExtendedPoint.normalizeZ([p, f])[0];
|
||||
private wNAF(n: bigint): { p: Point; f: Point } {
|
||||
return wnaf.wNAFCached(this, pointPrecomputes, n, Point.normalizeZ);
|
||||
}
|
||||
|
||||
// Constant time multiplication.
|
||||
// Uses wNAF method. Windowed method may be 10% faster,
|
||||
// but takes 2x longer to generate and consumes 2x memory.
|
||||
multiply(scalar: number | bigint, affinePoint?: Point): ExtendedPoint {
|
||||
return this.wNAF(normalizeScalar(scalar, CURVE_ORDER), affinePoint);
|
||||
// Constant-time multiplication.
|
||||
multiply(scalar: bigint): Point {
|
||||
const { p, f } = this.wNAF(assertInRange(scalar, CURVE_ORDER));
|
||||
return Point.normalizeZ([p, f])[0];
|
||||
}
|
||||
|
||||
// Non-constant-time multiplication. Uses double-and-add algorithm.
|
||||
// It's faster, but should only be used when you don't care about
|
||||
// an exposed private key e.g. sig verification.
|
||||
multiplyUnsafe(scalar: number | bigint): ExtendedPoint {
|
||||
let n = normalizeScalar(scalar, CURVE_ORDER, false);
|
||||
const P0 = ExtendedPoint.ZERO;
|
||||
if (n === _0n) return P0;
|
||||
if (this.equals(P0) || n === _1n) return this;
|
||||
if (this.equals(ExtendedPoint.BASE)) return this.wNAF(n);
|
||||
multiplyUnsafe(scalar: bigint): Point {
|
||||
let n = assertGE0(scalar);
|
||||
if (n === _0n) return I;
|
||||
if (this.equals(I) || n === _1n) return this;
|
||||
if (this.equals(G)) return this.wNAF(n).p;
|
||||
return wnaf.unsafeLadder(this, n);
|
||||
}
|
||||
|
||||
@@ -325,350 +310,149 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
||||
// point with torsion component.
|
||||
// Multiplies point by cofactor and checks if the result is 0.
|
||||
isSmallOrder(): boolean {
|
||||
return this.multiplyUnsafe(CURVE.h).equals(ExtendedPoint.ZERO);
|
||||
return this.multiplyUnsafe(cofactor).is0();
|
||||
}
|
||||
|
||||
// Multiplies point by curve order (very big scalar CURVE.n) and checks if the result is 0.
|
||||
// Multiplies point by curve order and checks if the result is 0.
|
||||
// Returns `false` is the point is dirty.
|
||||
isTorsionFree(): boolean {
|
||||
return wnaf.unsafeLadder(this, CURVE_ORDER).equals(ExtendedPoint.ZERO);
|
||||
return wnaf.unsafeLadder(this, CURVE_ORDER).is0();
|
||||
}
|
||||
|
||||
// Converts Extended point to default (x, y) coordinates.
|
||||
// Can accept precomputed Z^-1 - for example, from invertBatch.
|
||||
toAffine(invZ?: bigint): Point {
|
||||
const { x, y, z } = this;
|
||||
const is0 = this.equals(ExtendedPoint.ZERO);
|
||||
if (invZ == null) invZ = is0 ? _8n : (Fp.invert(z) as bigint); // 8 was chosen arbitrarily
|
||||
const ax = modP(x * invZ);
|
||||
const ay = modP(y * invZ);
|
||||
const zz = modP(z * invZ);
|
||||
if (is0) return Point.ZERO;
|
||||
toAffine(iz?: bigint): AffinePoint<bigint> {
|
||||
const { ex: x, ey: y, ez: z } = this;
|
||||
const is0 = this.is0();
|
||||
if (iz == null) iz = is0 ? _8n : (Fp.inv(z) as bigint); // 8 was chosen arbitrarily
|
||||
const ax = modP(x * iz);
|
||||
const ay = modP(y * iz);
|
||||
const zz = modP(z * iz);
|
||||
if (is0) return { x: _0n, y: _1n };
|
||||
if (zz !== _1n) throw new Error('invZ was invalid');
|
||||
return new Point(ax, ay);
|
||||
return { x: ax, y: ay };
|
||||
}
|
||||
clearCofactor(): ExtendedPoint {
|
||||
|
||||
clearCofactor(): Point {
|
||||
const { h: cofactor } = CURVE;
|
||||
if (cofactor === _1n) return this;
|
||||
return this.multiplyUnsafe(cofactor);
|
||||
}
|
||||
}
|
||||
const wnaf = wNAF(ExtendedPoint, CURVE.nByteLength * 8);
|
||||
|
||||
function assertExtPoint(other: unknown) {
|
||||
if (!(other instanceof ExtendedPoint)) throw new TypeError('ExtendedPoint expected');
|
||||
}
|
||||
// Stores precomputed values for points.
|
||||
const pointPrecomputes = new WeakMap<Point, ExtendedPoint[]>();
|
||||
|
||||
/**
|
||||
* Default Point works in affine coordinates: (x, y)
|
||||
*/
|
||||
class Point implements PointType {
|
||||
// Base point aka generator
|
||||
// public_key = Point.BASE * private_key
|
||||
static BASE: Point = new Point(CURVE.Gx, CURVE.Gy);
|
||||
// Identity point aka point at infinity
|
||||
// point = point + zero_point
|
||||
static ZERO: Point = new Point(_0n, _1n);
|
||||
// We calculate precomputes for elliptic curve point multiplication
|
||||
// using windowed method. This specifies window size and
|
||||
// stores precomputed values. Usually only base point would be precomputed.
|
||||
_WINDOW_SIZE?: number;
|
||||
|
||||
constructor(readonly x: bigint, readonly y: bigint) {}
|
||||
|
||||
// "Private method", don't use it directly.
|
||||
_setWindowSize(windowSize: number) {
|
||||
this._WINDOW_SIZE = windowSize;
|
||||
pointPrecomputes.delete(this);
|
||||
}
|
||||
|
||||
// Converts hash string or Uint8Array to Point.
|
||||
// Uses algo from RFC8032 5.1.3.
|
||||
static fromHex(hex: Hex, strict = true) {
|
||||
static fromHex(hex: Hex, strict = true): Point {
|
||||
const { d, a } = CURVE;
|
||||
const len = Fp.BYTES;
|
||||
hex = ensureBytes(hex, len);
|
||||
// 1. First, interpret the string as an integer in little-endian
|
||||
// representation. Bit 255 of this number is the least significant
|
||||
// bit of the x-coordinate and denote this value x_0. The
|
||||
// y-coordinate is recovered simply by clearing this bit. If the
|
||||
// resulting value is >= p, decoding fails.
|
||||
const normed = hex.slice();
|
||||
const lastByte = hex[len - 1];
|
||||
normed[len - 1] = lastByte & ~0x80;
|
||||
hex = ensureBytes('pointHex', hex, len); // copy hex to a new array
|
||||
const normed = hex.slice(); // copy again, we'll manipulate it
|
||||
const lastByte = hex[len - 1]; // select last byte
|
||||
normed[len - 1] = lastByte & ~0x80; // clear last bit
|
||||
const y = ut.bytesToNumberLE(normed);
|
||||
|
||||
if (strict && y >= Fp.ORDER) throw new Error('Expected 0 < hex < P');
|
||||
if (!strict && y >= maxGroupElement) throw new Error('Expected 0 < hex < CURVE.n');
|
||||
|
||||
// 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 = ut.numberToBytesLE(this.y, Fp.BYTES);
|
||||
bytes[Fp.BYTES - 1] |= this.x & _1n ? 0x80 : 0;
|
||||
return bytes;
|
||||
}
|
||||
|
||||
// Same as toRawBytes, but returns string.
|
||||
toHex(): string {
|
||||
return ut.bytesToHex(this.toRawBytes());
|
||||
}
|
||||
|
||||
// Determines if point is in prime-order subgroup.
|
||||
// Returns `false` is the point is dirty.
|
||||
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>) {
|
||||
const { mapToCurve, htfDefaults } = CURVE;
|
||||
if (!mapToCurve) throw new Error('No mapToCurve defined for curve');
|
||||
const u = hashToField(ensureBytes(msg), 2, { ...htfDefaults, ...options } as htfOpts);
|
||||
const { x: x0, y: y0 } = mapToCurve(u[0]);
|
||||
const { x: x1, y: y1 } = 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>) {
|
||||
const { mapToCurve, htfDefaults } = CURVE;
|
||||
if (!mapToCurve) throw new Error('No mapToCurve defined for curve');
|
||||
const u = hashToField(ensureBytes(msg), 1, { ...htfDefaults, ...options } as htfOpts);
|
||||
const { x, y } = 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 len = Fp.BYTES;
|
||||
const bytes = ensureBytes(hex, 2 * len);
|
||||
const r = Point.fromHex(bytes.slice(0, len), false);
|
||||
const s = ut.bytesToNumberLE(bytes.slice(len, 2 * len));
|
||||
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 ut.concatBytes(this.r.toRawBytes(), ut.numberToBytesLE(this.s, Fp.BYTES));
|
||||
}
|
||||
|
||||
toHex() {
|
||||
return ut.bytesToHex(this.toRawBytes());
|
||||
}
|
||||
}
|
||||
|
||||
// Little-endian SHA512 with modulo n
|
||||
function modnLE(hash: Uint8Array): bigint {
|
||||
return mod.mod(ut.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 (ut.isPositiveInt(num)) num = BigInt(num);
|
||||
if (typeof num === 'bigint' && num < max) {
|
||||
if (strict) {
|
||||
if (_0n < num) return num;
|
||||
if (y === _0n) {
|
||||
// y=0 is allowed
|
||||
} else {
|
||||
if (_0n <= num) return num;
|
||||
// RFC8032 prohibits >= p, but ZIP215 doesn't
|
||||
if (strict) assertInRange(y, Fp.ORDER); // strict=true [1..P-1] (2^255-19-1 for ed25519)
|
||||
else assertInRange(y, MASK); // strict=false [1..MASK-1] (2^256-1 for ed25519)
|
||||
}
|
||||
|
||||
// Ed25519: x² = (y²-1)/(dy²+1) mod p. Ed448: x² = (y²-1)/(dy²-1) mod p. Generic case:
|
||||
// ax²+y²=1+dx²y² => y²-1=dx²y²-ax² => y²-1=x²(dy²-a) => x²=(y²-1)/(dy²-a)
|
||||
const y2 = modP(y * y); // denominator is always non-0 mod p.
|
||||
const u = modP(y2 - _1n); // u = y² - 1
|
||||
const v = modP(d * y2 - a); // v = d y² + 1.
|
||||
let { isValid, value: x } = uvRatio(u, v); // √(u/v)
|
||||
if (!isValid) throw new Error('Point.fromHex: invalid y coordinate');
|
||||
const isXOdd = (x & _1n) === _1n; // There are 2 square roots. Use x_0 bit to select proper
|
||||
const isLastByteOdd = (lastByte & 0x80) !== 0; // if x=0 and x_0 = 1, fail
|
||||
if (isLastByteOdd !== isXOdd) x = modP(-x); // if x_0 != x mod 2, set x = p-x
|
||||
return Point.fromAffine({ x, y });
|
||||
}
|
||||
static fromPrivateKey(privKey: Hex) {
|
||||
return getExtendedPublicKey(privKey).point;
|
||||
}
|
||||
toRawBytes(): Uint8Array {
|
||||
const { x, y } = this.toAffine();
|
||||
const bytes = ut.numberToBytesLE(y, Fp.BYTES); // each y has 2 x values (x, -y)
|
||||
bytes[bytes.length - 1] |= x & _1n ? 0x80 : 0; // when compressing, it's enough to store y
|
||||
return bytes; // and use the last byte to encode sign of x
|
||||
}
|
||||
toHex(): string {
|
||||
return ut.bytesToHex(this.toRawBytes()); // Same as toRawBytes, but returns string.
|
||||
}
|
||||
}
|
||||
throw new TypeError(`Expected valid scalar: 0 < scalar < ${max}`);
|
||||
const { BASE: G, ZERO: I } = Point;
|
||||
const wnaf = wNAF(Point, nByteLength * 8);
|
||||
|
||||
function modN(a: bigint) {
|
||||
return mod(a, CURVE_ORDER);
|
||||
}
|
||||
// Little-endian SHA512 with modulo n
|
||||
function modN_LE(hash: Uint8Array): bigint {
|
||||
return modN(ut.bytesToNumberLE(hash));
|
||||
}
|
||||
|
||||
/** Convenience method that creates public key and other stuff. RFC8032 5.1.5 */
|
||||
function getExtendedPublicKey(key: PrivKey) {
|
||||
const groupLen = CURVE.nByteLength;
|
||||
// Normalize bigint / number / string to Uint8Array
|
||||
const keyb =
|
||||
typeof key === 'bigint' || typeof key === 'number'
|
||||
? ut.numberToBytesLE(normalizeScalar(key, maxGroupElement), groupLen)
|
||||
: key;
|
||||
function getExtendedPublicKey(key: Hex) {
|
||||
const len = nByteLength;
|
||||
key = ensureBytes('private key', key, len);
|
||||
// Hash private key with curve's hash function to produce uniformingly random input
|
||||
// We check byte lengths e.g.: ensureBytes(64, hash(ensureBytes(32, key)))
|
||||
const hashed = ensureBytes(CURVE.hash(ensureBytes(keyb, groupLen)), 2 * groupLen);
|
||||
|
||||
// First half's bits are cleared to produce a random field element.
|
||||
const head = adjustScalarBytes(hashed.slice(0, groupLen));
|
||||
// Second half is called key prefix (5.1.6)
|
||||
const prefix = hashed.slice(groupLen, 2 * groupLen);
|
||||
// The actual private scalar
|
||||
const scalar = modnLE(head);
|
||||
// Point on Edwards curve aka public key
|
||||
const point = Point.BASE.multiply(scalar);
|
||||
// Uint8Array representation
|
||||
const pointBytes = point.toRawBytes();
|
||||
// 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 ed25519 public key. RFC8032 5.1.5
|
||||
* 1. private key is hashed with sha512, then first 32 bytes are taken from the hash
|
||||
* 2. 3 least significant bits of the first byte are cleared
|
||||
*/
|
||||
function getPublicKey(privateKey: PrivKey): Uint8Array {
|
||||
return getExtendedPublicKey(privateKey).pointBytes;
|
||||
// Calculates EdDSA pub key. RFC8032 5.1.5. Privkey is hashed. Use first half with 3 bits cleared
|
||||
function getPublicKey(privKey: Hex): Uint8Array {
|
||||
return getExtendedPublicKey(privKey).pointBytes;
|
||||
}
|
||||
|
||||
const EMPTY = new Uint8Array();
|
||||
function hashDomainToScalar(message: Uint8Array, context: Hex = EMPTY) {
|
||||
context = ensureBytes(context);
|
||||
return modnLE(CURVE.hash(domain(message, context, !!CURVE.preHash)));
|
||||
// int('LE', SHA512(dom2(F, C) || msgs)) mod N
|
||||
function hashDomainToScalar(context: Hex = new Uint8Array(), ...msgs: Uint8Array[]) {
|
||||
const msg = ut.concatBytes(...msgs);
|
||||
return modN_LE(cHash(domain(msg, ensureBytes('context', context), !!preHash)));
|
||||
}
|
||||
|
||||
/** Signs message with privateKey. RFC8032 5.1.6 */
|
||||
function sign(message: Hex, privateKey: Hex, context?: Hex): Uint8Array {
|
||||
message = ensureBytes(message);
|
||||
if (CURVE.preHash) message = CURVE.preHash(message);
|
||||
const { prefix, scalar, pointBytes } = getExtendedPublicKey(privateKey);
|
||||
const r = hashDomainToScalar(ut.concatBytes(prefix, message), context);
|
||||
const R = Point.BASE.multiply(r); // R = rG
|
||||
const k = hashDomainToScalar(ut.concatBytes(R.toRawBytes(), pointBytes, message), context); // k = hash(R+P+msg)
|
||||
const s = mod.mod(r + k * scalar, CURVE_ORDER); // s = r + kp
|
||||
return new Signature(R, s).toRawBytes();
|
||||
function sign(msg: Hex, privKey: Hex, context?: Hex): Uint8Array {
|
||||
msg = ensureBytes('message', msg);
|
||||
if (preHash) msg = preHash(msg); // for ed25519ph etc.
|
||||
const { prefix, scalar, pointBytes } = getExtendedPublicKey(privKey);
|
||||
const r = hashDomainToScalar(context, prefix, msg); // r = dom2(F, C) || prefix || PH(M)
|
||||
const R = G.multiply(r).toRawBytes(); // R = rG
|
||||
const k = hashDomainToScalar(context, R, pointBytes, msg); // R || A || PH(M)
|
||||
const s = modN(r + k * scalar); // S = (r + k * s) mod L
|
||||
assertGE0(s); // 0 <= s < l
|
||||
const res = ut.concatBytes(R, ut.numberToBytesLE(s, Fp.BYTES));
|
||||
return ensureBytes('result', res, nByteLength * 2); // 64-byte signature
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies EdDSA signature against message and public key.
|
||||
* An extended group equation is checked.
|
||||
* RFC8032 5.1.7
|
||||
* Compliant with ZIP215:
|
||||
* 0 <= sig.R/publicKey < 2**256 (can be >= curve.P)
|
||||
* 0 <= sig.s < l
|
||||
* Not compliant with RFC8032: it's not possible to comply to both ZIP & RFC at the same time.
|
||||
*/
|
||||
function verify(sig: SigType, message: Hex, publicKey: PubKey, context?: Hex): boolean {
|
||||
message = ensureBytes(message);
|
||||
if (CURVE.preHash) message = CURVE.preHash(message);
|
||||
// When hex is passed, we check public key fully.
|
||||
// When Point instance is passed, we assume it has already been checked, for performance.
|
||||
// If user passes Point/Sig instance, we assume it has been already verified.
|
||||
// We don't check its equations for performance. We do check for valid bounds for s though
|
||||
// We always check for: a) s bounds. b) hex validity
|
||||
if (publicKey instanceof Point) {
|
||||
// ignore
|
||||
} else if (publicKey instanceof Uint8Array || typeof publicKey === 'string') {
|
||||
publicKey = Point.fromHex(publicKey, false);
|
||||
} else {
|
||||
throw new Error(`Invalid publicKey: ${publicKey}`);
|
||||
}
|
||||
|
||||
if (sig instanceof Signature) sig.assertValidity();
|
||||
else if (sig instanceof Uint8Array || typeof sig === 'string') sig = Signature.fromHex(sig);
|
||||
else throw new Error(`Wrong signature: ${sig}`);
|
||||
|
||||
const { r, s } = sig;
|
||||
const SB = ExtendedPoint.BASE.multiplyUnsafe(s);
|
||||
const k = hashDomainToScalar(
|
||||
ut.concatBytes(r.toRawBytes(), publicKey.toRawBytes(), message),
|
||||
context
|
||||
);
|
||||
const kA = ExtendedPoint.fromAffine(publicKey).multiplyUnsafe(k);
|
||||
const RkA = ExtendedPoint.fromAffine(r).add(kA);
|
||||
function verify(sig: Hex, msg: Hex, publicKey: Hex, context?: Hex): boolean {
|
||||
const len = Fp.BYTES; // Verifies EdDSA signature against message and public key. RFC8032 5.1.7.
|
||||
sig = ensureBytes('signature', sig, 2 * len); // An extended group equation is checked.
|
||||
msg = ensureBytes('message', msg); // ZIP215 compliant, which means not fully RFC8032 compliant.
|
||||
if (preHash) msg = preHash(msg); // for ed25519ph, etc
|
||||
const A = Point.fromHex(publicKey, false); // Check for s bounds, hex validity
|
||||
const R = Point.fromHex(sig.slice(0, len), false); // 0 <= R < 2^256: ZIP215 R can be >= P
|
||||
const s = ut.bytesToNumberLE(sig.slice(len, 2 * len)); // 0 <= s < l
|
||||
const SB = G.multiplyUnsafe(s);
|
||||
const k = hashDomainToScalar(context, R.toRawBytes(), A.toRawBytes(), msg);
|
||||
const RkA = R.add(A.multiplyUnsafe(k));
|
||||
// [8][S]B = [8]R + [8][k]A'
|
||||
return RkA.subtract(SB).clearCofactor().equals(ExtendedPoint.ZERO);
|
||||
return RkA.subtract(SB).clearCofactor().equals(Point.ZERO);
|
||||
}
|
||||
|
||||
// Enable precomputes. Slows down first publicKey computation by 20ms.
|
||||
Point.BASE._setWindowSize(8);
|
||||
G._setWindowSize(8); // Enable precomputes. Slows down first publicKey computation by 20ms.
|
||||
|
||||
const utils = {
|
||||
getExtendedPublicKey,
|
||||
/**
|
||||
* Not needed for ed25519 private keys. Needed if you use scalars directly (rare).
|
||||
*/
|
||||
hashToPrivateScalar: (hash: Hex): bigint => ut.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()
|
||||
*/
|
||||
// ed25519 private keys are uniform 32b. No need to check for modulo bias, like in secp256k1.
|
||||
randomPrivateKey: (): Uint8Array => randomBytes(Fp.BYTES),
|
||||
|
||||
/**
|
||||
@@ -677,11 +461,10 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
||||
* but allows to speed-up subsequent getPublicKey() calls up to 20x.
|
||||
* @param windowSize 2, 4, 8, 16
|
||||
*/
|
||||
precompute(windowSize = 8, point = Point.BASE): Point {
|
||||
const cached = point.equals(Point.BASE) ? point : new Point(point.x, point.y);
|
||||
cached._setWindowSize(windowSize);
|
||||
cached.multiply(_2n);
|
||||
return cached;
|
||||
precompute(windowSize = 8, point = Point.BASE): typeof Point.BASE {
|
||||
point._setWindowSize(windowSize);
|
||||
point.multiply(BigInt(3));
|
||||
return point;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -690,9 +473,7 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
||||
getPublicKey,
|
||||
sign,
|
||||
verify,
|
||||
ExtendedPoint,
|
||||
Point,
|
||||
Signature,
|
||||
ExtendedPoint: Point,
|
||||
utils,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,60 +1,35 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
import { CHash, concatBytes } from './utils.js';
|
||||
import * as mod from './modular.js';
|
||||
import type { Group, GroupConstructor, AffinePoint } from './curve.js';
|
||||
import { mod, Field } from './modular.js';
|
||||
import { bytesToNumberBE, CHash, concatBytes, utf8ToBytes, validateObject } from './utils.js';
|
||||
|
||||
export type htfOpts = {
|
||||
// DST: a domain separation tag
|
||||
// defined in section 2.2.5
|
||||
DST: string;
|
||||
// p: the characteristic of F
|
||||
// where F is a finite field of characteristic p and order q = p^m
|
||||
/**
|
||||
* * `DST` is a domain separation tag, defined in section 2.2.5
|
||||
* * `p` characteristic of F, where F is a finite field of characteristic p and order q = p^m
|
||||
* * `m` is extension degree (1 for prime fields)
|
||||
* * `k` is the target security target in bits (e.g. 128), from section 5.1
|
||||
* * `expand` is `xmd` (SHA2, SHA3, BLAKE) or `xof` (SHAKE, BLAKE-XOF)
|
||||
* * `hash` conforming to `utils.CHash` interface, with `outputLen` / `blockLen` props
|
||||
*/
|
||||
export type Opts = {
|
||||
DST: string | Uint8Array;
|
||||
p: bigint;
|
||||
// m: the extension degree of F, m >= 1
|
||||
// where F is a finite field of characteristic p and order q = p^m
|
||||
m: number;
|
||||
// k: the target security level for the suite in bits
|
||||
// defined in section 5.1
|
||||
k: number;
|
||||
// option to use a message that has already been processed by
|
||||
// expand_message_xmd
|
||||
expand?: 'xmd' | 'xof';
|
||||
// Hash functions for: expand_message_xmd is appropriate for use with a
|
||||
// wide range of hash functions, including SHA-2, SHA-3, BLAKE2, and others.
|
||||
// BBS+ uses blake2: https://github.com/hyperledger/aries-framework-go/issues/2247
|
||||
// TODO: verify that hash is shake if expand==='xof' via types
|
||||
hash: CHash;
|
||||
};
|
||||
|
||||
export function validateHTFOpts(opts: htfOpts) {
|
||||
if (typeof opts.DST !== 'string') throw new Error('Invalid htf/DST');
|
||||
if (typeof opts.p !== 'bigint') throw new Error('Invalid htf/p');
|
||||
if (typeof opts.m !== 'number') throw new Error('Invalid htf/m');
|
||||
if (typeof opts.k !== 'number') throw new Error('Invalid htf/k');
|
||||
if (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');
|
||||
function validateDST(dst: string | Uint8Array): Uint8Array {
|
||||
if (dst instanceof Uint8Array) return dst;
|
||||
if (typeof dst === 'string') return utf8ToBytes(dst);
|
||||
throw new Error('DST must be Uint8Array or string');
|
||||
}
|
||||
|
||||
// UTF8 to ui8a
|
||||
// TODO: looks broken, ASCII only, why not TextEncoder/TextDecoder? it is in hashes anyway
|
||||
export function stringToBytes(str: string) {
|
||||
const bytes = new Uint8Array(str.length);
|
||||
for (let i = 0; i < str.length; i++) bytes[i] = str.charCodeAt(i);
|
||||
return bytes;
|
||||
}
|
||||
// Octet Stream to Integer. "spec" implementation of os2ip is 2.5x slower vs bytesToNumberBE.
|
||||
const os2ip = bytesToNumberBE;
|
||||
|
||||
// Octet Stream to Integer (bytesToNumberBE)
|
||||
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
|
||||
// Integer to Octet Stream (numberToBytesBE)
|
||||
function i2osp(value: number, length: number): Uint8Array {
|
||||
if (value < 0 || value >= 1 << (8 * length)) {
|
||||
throw new Error(`bad I2OSP call: value=${value} length=${length}`);
|
||||
@@ -75,6 +50,13 @@ function strxor(a: Uint8Array, b: Uint8Array): Uint8Array {
|
||||
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
|
||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.4.1
|
||||
export function expand_message_xmd(
|
||||
@@ -83,15 +65,17 @@ export function expand_message_xmd(
|
||||
lenInBytes: number,
|
||||
H: CHash
|
||||
): Uint8Array {
|
||||
isBytes(msg);
|
||||
isBytes(DST);
|
||||
isNum(lenInBytes);
|
||||
// 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));
|
||||
const b_in_bytes = H.outputLen;
|
||||
const r_in_bytes = H.blockLen;
|
||||
if (DST.length > 255) DST = H(concatBytes(utf8ToBytes('H2C-OVERSIZE-DST-'), DST));
|
||||
const { outputLen: b_in_bytes, blockLen: r_in_bytes } = H;
|
||||
const ell = Math.ceil(lenInBytes / b_in_bytes);
|
||||
if (ell > 255) throw new Error('Invalid xmd length');
|
||||
const DST_prime = concatBytes(DST, i2osp(DST.length, 1));
|
||||
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_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));
|
||||
@@ -110,11 +94,14 @@ export function expand_message_xof(
|
||||
k: number,
|
||||
H: CHash
|
||||
): Uint8Array {
|
||||
isBytes(msg);
|
||||
isBytes(DST);
|
||||
isNum(lenInBytes);
|
||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-5.3.3
|
||||
// DST = H('H2C-OVERSIZE-DST-' || a_very_long_DST, Math.ceil((lenInBytes * k) / 8));
|
||||
if (DST.length > 255) {
|
||||
const dkLen = Math.ceil((2 * k) / 8);
|
||||
DST = H.create({ dkLen }).update(stringToBytes('H2C-OVERSIZE-DST-')).update(DST).digest();
|
||||
DST = H.create({ dkLen }).update(utf8ToBytes('H2C-OVERSIZE-DST-')).update(DST).digest();
|
||||
}
|
||||
if (lenInBytes > 65535 || DST.length > 255)
|
||||
throw new Error('expand_message_xof: invalid lenInBytes');
|
||||
@@ -134,36 +121,41 @@ export function expand_message_xof(
|
||||
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3
|
||||
* @param msg a byte string containing the message to hash
|
||||
* @param count the number of elements of F to output
|
||||
* @param options `{DST: string, p: bigint, m: number, k: number, expand: 'xmd' | 'xof', hash: H}`
|
||||
* @param options `{DST: string, p: bigint, m: number, k: number, expand: 'xmd' | 'xof', hash: H}`, see above
|
||||
* @returns [u_0, ..., u_(count - 1)], a list of field elements.
|
||||
*/
|
||||
export function hash_to_field(msg: Uint8Array, count: number, options: htfOpts): bigint[][] {
|
||||
// if options is provided but incomplete, fill any missing fields with the
|
||||
// value in hftDefaults (ie hash to G2).
|
||||
const log2p = options.p.toString(2).length;
|
||||
const L = Math.ceil((log2p + options.k) / 8); // section 5.1 of ietf draft link above
|
||||
const len_in_bytes = count * options.m * L;
|
||||
const DST = stringToBytes(options.DST);
|
||||
let pseudo_random_bytes = msg;
|
||||
if (options.expand === 'xmd') {
|
||||
pseudo_random_bytes = expand_message_xmd(msg, DST, len_in_bytes, options.hash);
|
||||
} else if (options.expand === 'xof') {
|
||||
pseudo_random_bytes = expand_message_xof(msg, DST, len_in_bytes, options.k, options.hash);
|
||||
export function hash_to_field(msg: Uint8Array, count: number, options: Opts): bigint[][] {
|
||||
const { p, k, m, hash, expand, DST: _DST } = options;
|
||||
isBytes(msg);
|
||||
isNum(count);
|
||||
const DST = validateDST(_DST);
|
||||
const log2p = p.toString(2).length;
|
||||
const L = Math.ceil((log2p + k) / 8); // section 5.1 of ietf draft link above
|
||||
const len_in_bytes = count * m * L;
|
||||
let prb; // pseudo_random_bytes
|
||||
if (expand === 'xmd') {
|
||||
prb = expand_message_xmd(msg, DST, len_in_bytes, hash);
|
||||
} else if (expand === 'xof') {
|
||||
prb = expand_message_xof(msg, DST, len_in_bytes, k, hash);
|
||||
} else if (expand === undefined) {
|
||||
prb = msg;
|
||||
} else {
|
||||
throw new Error('expand must be "xmd", "xof" or undefined');
|
||||
}
|
||||
const u = new Array(count);
|
||||
for (let i = 0; i < count; i++) {
|
||||
const e = new Array(options.m);
|
||||
for (let j = 0; j < options.m; j++) {
|
||||
const elm_offset = L * (j + i * options.m);
|
||||
const tv = pseudo_random_bytes.subarray(elm_offset, elm_offset + L);
|
||||
e[j] = mod.mod(os2ip(tv), options.p);
|
||||
const e = new Array(m);
|
||||
for (let j = 0; j < m; j++) {
|
||||
const elm_offset = L * (j + i * m);
|
||||
const tv = prb.subarray(elm_offset, elm_offset + L);
|
||||
e[j] = mod(os2ip(tv), p);
|
||||
}
|
||||
u[i] = e;
|
||||
}
|
||||
return u;
|
||||
}
|
||||
|
||||
export function isogenyMap<T, F extends mod.Field<T>>(field: F, map: [T[], T[], T[], T[]]) {
|
||||
export function isogenyMap<T, F extends Field<T>>(field: F, map: [T[], T[], T[], T[]]) {
|
||||
// Make same order as in spec
|
||||
const COEFF = map.map((i) => Array.from(i).reverse());
|
||||
return (x: T, y: T) => {
|
||||
@@ -175,3 +167,55 @@ export function isogenyMap<T, F extends mod.Field<T>>(field: F, map: [T[], T[],
|
||||
return { x, y };
|
||||
};
|
||||
}
|
||||
|
||||
export interface H2CPoint<T> extends Group<H2CPoint<T>> {
|
||||
add(rhs: H2CPoint<T>): H2CPoint<T>;
|
||||
toAffine(iz?: bigint): AffinePoint<T>;
|
||||
clearCofactor(): H2CPoint<T>;
|
||||
assertValidity(): void;
|
||||
}
|
||||
|
||||
export interface H2CPointConstructor<T> extends GroupConstructor<H2CPoint<T>> {
|
||||
fromAffine(ap: AffinePoint<T>): H2CPoint<T>;
|
||||
}
|
||||
|
||||
export type MapToCurve<T> = (scalar: bigint[]) => AffinePoint<T>;
|
||||
|
||||
// Separated from initialization opts, so users won't accidentally change per-curve parameters
|
||||
// (changing DST is ok!)
|
||||
export type htfBasicOpts = { DST: string };
|
||||
|
||||
export function createHasher<T>(
|
||||
Point: H2CPointConstructor<T>,
|
||||
mapToCurve: MapToCurve<T>,
|
||||
def: Opts & { encodeDST?: string }
|
||||
) {
|
||||
validateObject(def, {
|
||||
DST: 'string',
|
||||
p: 'bigint',
|
||||
m: 'isSafeInteger',
|
||||
k: 'isSafeInteger',
|
||||
hash: 'hash',
|
||||
});
|
||||
if (typeof mapToCurve !== 'function') throw new Error('mapToCurve() must be defined');
|
||||
return {
|
||||
// Encodes byte string to elliptic curve
|
||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3
|
||||
hashToCurve(msg: Uint8Array, options?: htfBasicOpts) {
|
||||
const u = hash_to_field(msg, 2, { ...def, DST: def.DST, ...options } as Opts);
|
||||
const u0 = Point.fromAffine(mapToCurve(u[0]));
|
||||
const u1 = Point.fromAffine(mapToCurve(u[1]));
|
||||
const P = u0.add(u1).clearCofactor();
|
||||
P.assertValidity();
|
||||
return P;
|
||||
},
|
||||
|
||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-3
|
||||
encodeToCurve(msg: Uint8Array, options?: htfBasicOpts) {
|
||||
const u = hash_to_field(msg, 1, { ...def, DST: def.encodeDST, ...options } as Opts);
|
||||
const P = Point.fromAffine(mapToCurve(u[0])).clearCofactor();
|
||||
P.assertValidity();
|
||||
return P;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
/*! 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
|
||||
import {
|
||||
bitMask,
|
||||
numberToBytesBE,
|
||||
numberToBytesLE,
|
||||
bytesToNumberBE,
|
||||
bytesToNumberLE,
|
||||
ensureBytes,
|
||||
validateObject,
|
||||
} from './utils.js';
|
||||
// prettier-ignore
|
||||
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3);
|
||||
// prettier-ignore
|
||||
@@ -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)
|
||||
// TODO: Fp version?
|
||||
export function pow2(x: bigint, power: bigint, modulo: bigint): bigint {
|
||||
let res = x;
|
||||
while (power-- > _0n) {
|
||||
@@ -92,7 +98,7 @@ export function tonelliShanks(P: bigint) {
|
||||
const p1div4 = (P + _1n) / _4n;
|
||||
return function tonelliFast<T>(Fp: Field<T>, n: T) {
|
||||
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;
|
||||
};
|
||||
}
|
||||
@@ -101,24 +107,24 @@ export function tonelliShanks(P: bigint) {
|
||||
const Q1div2 = (Q + _1n) / _2n;
|
||||
return function tonelliSlow<T>(Fp: Field<T>, n: T): T {
|
||||
// Step 0: Check that n is indeed a square: (n | p) should not be ≡ -1
|
||||
if (Fp.pow(n, legendreC) === Fp.negate(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 r = S;
|
||||
// TODO: will fail at Fp2/etc
|
||||
let g = Fp.pow(Fp.mul(Fp.ONE, Z), Q); // will update both x and b
|
||||
let x = Fp.pow(n, Q1div2); // first guess at the square root
|
||||
let b = Fp.pow(n, Q); // first guess at the fudge factor
|
||||
|
||||
while (!Fp.equals(b, Fp.ONE)) {
|
||||
if (Fp.equals(b, Fp.ZERO)) return Fp.ZERO; // https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm (4. If t = 0, return r = 0)
|
||||
while (!Fp.eql(b, Fp.ONE)) {
|
||||
if (Fp.eql(b, Fp.ZERO)) return Fp.ZERO; // https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm (4. If t = 0, return r = 0)
|
||||
// Find m such b^(2^m)==1
|
||||
let m = 1;
|
||||
for (let t2 = Fp.square(b); m < r; m++) {
|
||||
if (Fp.equals(t2, Fp.ONE)) break;
|
||||
t2 = Fp.square(t2); // t2 *= t2
|
||||
for (let t2 = Fp.sqr(b); m < r; m++) {
|
||||
if (Fp.eql(t2, Fp.ONE)) break;
|
||||
t2 = Fp.sqr(t2); // t2 *= t2
|
||||
}
|
||||
// NOTE: r-m-1 can be bigger than 32, need to convert to bigint before shift, otherwise there will be overflow
|
||||
const ge = Fp.pow(g, _1n << BigInt(r - m - 1)); // ge = 2^(r-m-1)
|
||||
g = Fp.square(ge); // g = ge * ge
|
||||
g = Fp.sqr(ge); // g = ge * ge
|
||||
x = Fp.mul(x, ge); // x *= ge
|
||||
b = Fp.mul(b, g); // b *= g
|
||||
r = m;
|
||||
@@ -142,7 +148,7 @@ export function FpSqrt(P: bigint) {
|
||||
return function sqrt3mod4<T>(Fp: Field<T>, n: T) {
|
||||
const root = Fp.pow(n, p1div4);
|
||||
// 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;
|
||||
};
|
||||
}
|
||||
@@ -156,7 +162,7 @@ export function FpSqrt(P: bigint) {
|
||||
const nv = Fp.mul(n, v);
|
||||
const i = Fp.mul(Fp.mul(nv, _2n), v);
|
||||
const root = Fp.mul(nv, Fp.sub(i, Fp.ONE));
|
||||
if (!Fp.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;
|
||||
};
|
||||
}
|
||||
@@ -206,13 +212,13 @@ export interface Field<T> {
|
||||
// 1-arg
|
||||
create: (num: T) => T;
|
||||
isValid: (num: T) => boolean;
|
||||
isZero: (num: T) => boolean;
|
||||
negate(num: T): T;
|
||||
invert(num: T): T;
|
||||
is0: (num: T) => boolean;
|
||||
neg(num: T): T;
|
||||
inv(num: T): T;
|
||||
sqrt(num: T): T;
|
||||
square(num: T): T;
|
||||
sqr(num: T): T;
|
||||
// 2-args
|
||||
equals(lhs: T, rhs: T): boolean;
|
||||
eql(lhs: T, rhs: T): boolean;
|
||||
add(lhs: T, rhs: T): T;
|
||||
sub(lhs: T, rhs: T): T;
|
||||
mul(lhs: T, rhs: T | bigint): T;
|
||||
@@ -222,13 +228,13 @@ export interface Field<T> {
|
||||
addN(lhs: T, rhs: T): T;
|
||||
subN(lhs: T, rhs: T): T;
|
||||
mulN(lhs: T, rhs: T | bigint): T;
|
||||
squareN(num: T): T;
|
||||
sqrN(num: T): T;
|
||||
|
||||
// Optional
|
||||
// Should be same as sgn0 function in https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/
|
||||
// NOTE: sgn0 is 'negative in LE', which is same as odd. And negative in LE is kinda strange definition anyway.
|
||||
isOdd?(num: T): boolean; // Odd instead of even since we have it for Fp2
|
||||
legendre?(num: T): T;
|
||||
// legendre?(num: T): T;
|
||||
pow(lhs: T, power: bigint): T;
|
||||
invertBatch: (lst: T[]) => T[];
|
||||
toBytes(num: T): Uint8Array;
|
||||
@@ -238,23 +244,22 @@ export interface Field<T> {
|
||||
}
|
||||
// prettier-ignore
|
||||
const FIELD_FIELDS = [
|
||||
'create', 'isValid', 'isZero', 'negate', 'invert', 'sqrt', 'square',
|
||||
'equals', 'add', 'sub', 'mul', 'pow', 'div',
|
||||
'addN', 'subN', 'mulN', 'squareN'
|
||||
'create', 'isValid', 'is0', 'neg', 'inv', 'sqrt', 'sqr',
|
||||
'eql', 'add', 'sub', 'mul', 'pow', 'div',
|
||||
'addN', 'subN', 'mulN', 'sqrN'
|
||||
] as const;
|
||||
export function validateField<T>(field: Field<T>) {
|
||||
for (const i of ['ORDER', 'MASK'] as const) {
|
||||
if (typeof field[i] !== 'bigint')
|
||||
throw new Error(`Invalid field param ${i}=${field[i]} (${typeof field[i]})`);
|
||||
}
|
||||
for (const i of ['BYTES', 'BITS'] as const) {
|
||||
if (typeof field[i] !== 'number')
|
||||
throw new Error(`Invalid field param ${i}=${field[i]} (${typeof field[i]})`);
|
||||
}
|
||||
for (const i of FIELD_FIELDS) {
|
||||
if (typeof field[i] !== 'function')
|
||||
throw new Error(`Invalid field param ${i}=${field[i]} (${typeof field[i]})`);
|
||||
}
|
||||
const initial = {
|
||||
ORDER: 'bigint',
|
||||
MASK: 'bigint',
|
||||
BYTES: 'isSafeInteger',
|
||||
BITS: 'isSafeInteger',
|
||||
} as Record<string, string>;
|
||||
const opts = FIELD_FIELDS.reduce((map, val: string) => {
|
||||
map[val] = 'function';
|
||||
return map;
|
||||
}, initial);
|
||||
return validateObject(field, opts);
|
||||
}
|
||||
|
||||
// Generic field functions
|
||||
@@ -268,7 +273,7 @@ export function FpPow<T>(f: Field<T>, num: T, power: bigint): T {
|
||||
let d = num;
|
||||
while (power > _0n) {
|
||||
if (power & _1n) p = f.mul(p, d);
|
||||
d = f.square(d);
|
||||
d = f.sqr(d);
|
||||
power >>= 1n;
|
||||
}
|
||||
return p;
|
||||
@@ -278,15 +283,15 @@ export function FpInvertBatch<T>(f: Field<T>, nums: T[]): T[] {
|
||||
const tmp = new Array(nums.length);
|
||||
// Walk from first to last, multiply them by each other MOD p
|
||||
const lastMultiplied = nums.reduce((acc, num, i) => {
|
||||
if (f.isZero(num)) return acc;
|
||||
if (f.is0(num)) return acc;
|
||||
tmp[i] = acc;
|
||||
return f.mul(acc, num);
|
||||
}, f.ONE);
|
||||
// Invert last element
|
||||
const inverted = f.invert(lastMultiplied);
|
||||
const inverted = f.inv(lastMultiplied);
|
||||
// Walk from last to first, multiply them by inverted each other MOD p
|
||||
nums.reduceRight((acc, num, i) => {
|
||||
if (f.isZero(num)) return acc;
|
||||
if (f.is0(num)) return acc;
|
||||
tmp[i] = f.mul(acc, tmp[i]);
|
||||
return f.mul(acc, num);
|
||||
}, inverted);
|
||||
@@ -294,7 +299,7 @@ export function FpInvertBatch<T>(f: Field<T>, nums: T[]): T[] {
|
||||
}
|
||||
|
||||
export function FpDiv<T>(f: Field<T>, lhs: T, rhs: T | bigint): T {
|
||||
return f.mul(lhs, typeof rhs === 'bigint' ? invert(rhs, f.ORDER) : f.invert(rhs));
|
||||
return f.mul(lhs, typeof rhs === 'bigint' ? invert(rhs, f.ORDER) : f.inv(rhs));
|
||||
}
|
||||
|
||||
// This function returns True whenever the value x is a square in the field F.
|
||||
@@ -302,10 +307,18 @@ export function FpIsSquare<T>(f: Field<T>) {
|
||||
const legendreConst = (f.ORDER - _1n) / _2n; // Integer arithmetic
|
||||
return (x: T): boolean => {
|
||||
const p = f.pow(x, legendreConst);
|
||||
return f.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:
|
||||
// - NonNormalized ops
|
||||
// - Object.freeze
|
||||
@@ -318,28 +331,28 @@ export function Fp(
|
||||
redef: Partial<Field<bigint>> = {}
|
||||
): Readonly<FpField> {
|
||||
if (ORDER <= _0n) throw new Error(`Expected Fp ORDER > 0, got ${ORDER}`);
|
||||
const { nBitLength: BITS, nByteLength: BYTES } = utils.nLength(ORDER, bitLen);
|
||||
const { nBitLength: BITS, nByteLength: BYTES } = nLength(ORDER, bitLen);
|
||||
if (BYTES > 2048) throw new Error('Field lengths over 2048 bytes are not supported');
|
||||
const sqrtP = FpSqrt(ORDER);
|
||||
const f: Readonly<FpField> = Object.freeze({
|
||||
ORDER,
|
||||
BITS,
|
||||
BYTES,
|
||||
MASK: utils.bitMask(BITS),
|
||||
MASK: bitMask(BITS),
|
||||
ZERO: _0n,
|
||||
ONE: _1n,
|
||||
create: (num) => mod(num, ORDER),
|
||||
isValid: (num) => {
|
||||
if (typeof num !== 'bigint')
|
||||
throw new Error(`Invalid field element: expected bigint, got ${typeof num}`);
|
||||
return _0n <= num && num < ORDER;
|
||||
return _0n <= num && num < ORDER; // 0 is valid element, but it's not invertible
|
||||
},
|
||||
isZero: (num) => num === _0n,
|
||||
is0: (num) => num === _0n,
|
||||
isOdd: (num) => (num & _1n) === _1n,
|
||||
negate: (num) => mod(-num, ORDER),
|
||||
equals: (lhs, rhs) => lhs === rhs,
|
||||
neg: (num) => mod(-num, ORDER),
|
||||
eql: (lhs, rhs) => lhs === rhs,
|
||||
|
||||
square: (num) => mod(num * num, ORDER),
|
||||
sqr: (num) => mod(num * num, ORDER),
|
||||
add: (lhs, rhs) => mod(lhs + rhs, ORDER),
|
||||
sub: (lhs, rhs) => mod(lhs - rhs, ORDER),
|
||||
mul: (lhs, rhs) => mod(lhs * rhs, ORDER),
|
||||
@@ -347,24 +360,22 @@ export function Fp(
|
||||
div: (lhs, rhs) => mod(lhs * invert(rhs, ORDER), ORDER),
|
||||
|
||||
// Same as above, but doesn't normalize
|
||||
squareN: (num) => num * num,
|
||||
sqrN: (num) => num * num,
|
||||
addN: (lhs, rhs) => lhs + rhs,
|
||||
subN: (lhs, rhs) => lhs - rhs,
|
||||
mulN: (lhs, rhs) => lhs * rhs,
|
||||
|
||||
invert: (num) => invert(num, ORDER),
|
||||
inv: (num) => invert(num, ORDER),
|
||||
sqrt: redef.sqrt || ((n) => sqrtP(f, n)),
|
||||
invertBatch: (lst) => FpInvertBatch(f, lst),
|
||||
// TODO: do we really need constant cmov?
|
||||
// We don't have const-time bigints anyway, so probably will be not very useful
|
||||
cmov: (a, b, c) => (c ? b : a),
|
||||
toBytes: (num) =>
|
||||
isLE ? utils.numberToBytesLE(num, BYTES) : utils.numberToBytesBE(num, BYTES),
|
||||
|
||||
toBytes: (num) => (isLE ? numberToBytesLE(num, BYTES) : numberToBytesBE(num, BYTES)),
|
||||
fromBytes: (bytes) => {
|
||||
if (bytes.length !== BYTES)
|
||||
throw new Error(`Fp.fromBytes: expected ${BYTES}, got ${bytes.length}`);
|
||||
return isLE ? utils.bytesToNumberLE(bytes) : utils.bytesToNumberBE(bytes);
|
||||
return isLE ? bytesToNumberLE(bytes) : bytesToNumberBE(bytes);
|
||||
},
|
||||
} as FpField);
|
||||
return Object.freeze(f);
|
||||
@@ -373,11 +384,34 @@ export function Fp(
|
||||
export function FpSqrtOdd<T>(Fp: Field<T>, elm: T) {
|
||||
if (!Fp.isOdd) throw new Error(`Field doesn't have isOdd`);
|
||||
const root = Fp.sqrt(elm);
|
||||
return Fp.isOdd(root) ? root : Fp.negate(root);
|
||||
return Fp.isOdd(root) ? root : Fp.neg(root);
|
||||
}
|
||||
|
||||
export function FpSqrtEven<T>(Fp: Field<T>, elm: T) {
|
||||
if (!Fp.isOdd) throw new Error(`Field doesn't have isOdd`);
|
||||
const root = Fp.sqrt(elm);
|
||||
return Fp.isOdd(root) ? Fp.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,52 +1,48 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
import * as mod from './modular.js';
|
||||
import { ensureBytes, numberToBytesLE, bytesToNumberLE, isPositiveInt } from './utils.js';
|
||||
import { mod, pow } from './modular.js';
|
||||
import { bytesToNumberLE, ensureBytes, numberToBytesLE, validateObject } from './utils.js';
|
||||
|
||||
const _0n = BigInt(0);
|
||||
const _1n = BigInt(1);
|
||||
type Hex = string | Uint8Array;
|
||||
|
||||
export type CurveType = {
|
||||
// Field over which we'll do calculations. Verify with:
|
||||
P: bigint;
|
||||
P: bigint; // finite field prime
|
||||
nByteLength: number;
|
||||
adjustScalarBytes?: (bytes: Uint8Array) => Uint8Array;
|
||||
domain?: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array;
|
||||
a24: bigint; // Related to d, but cannot be derived from it
|
||||
a: bigint;
|
||||
montgomeryBits: number;
|
||||
powPminus2?: (x: bigint) => bigint;
|
||||
xyToU?: (x: bigint, y: bigint) => bigint;
|
||||
Gu: string;
|
||||
Gu: bigint;
|
||||
randomBytes?: (bytesLength?: number) => Uint8Array;
|
||||
};
|
||||
export type CurveFn = {
|
||||
scalarMult: (scalar: Hex, u: Hex) => Uint8Array;
|
||||
scalarMultBase: (scalar: Hex) => Uint8Array;
|
||||
getSharedSecret: (privateKeyA: Hex, publicKeyB: Hex) => Uint8Array;
|
||||
getPublicKey: (privateKey: Hex) => Uint8Array;
|
||||
Gu: string;
|
||||
utils: { randomPrivateKey: () => Uint8Array };
|
||||
GuBytes: Uint8Array;
|
||||
};
|
||||
|
||||
function validateOpts(curve: CurveType) {
|
||||
for (const i of ['a24'] as const) {
|
||||
if (typeof curve[i] !== 'bigint')
|
||||
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
|
||||
}
|
||||
for (const i of ['montgomeryBits', 'nByteLength'] as const) {
|
||||
if (curve[i] === undefined) continue; // Optional
|
||||
if (!isPositiveInt(curve[i]))
|
||||
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
|
||||
}
|
||||
for (const fn of ['adjustScalarBytes', 'domain', 'powPminus2'] as const) {
|
||||
if (curve[fn] === undefined) continue; // Optional
|
||||
if (typeof curve[fn] !== 'function') throw new Error(`Invalid ${fn} function`);
|
||||
}
|
||||
for (const i of ['Gu'] as const) {
|
||||
if (curve[i] === undefined) continue; // Optional
|
||||
if (typeof curve[i] !== 'string')
|
||||
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
|
||||
validateObject(
|
||||
curve,
|
||||
{
|
||||
a: 'bigint',
|
||||
},
|
||||
{
|
||||
montgomeryBits: 'isSafeInteger',
|
||||
nByteLength: 'isSafeInteger',
|
||||
adjustScalarBytes: 'function',
|
||||
domain: 'function',
|
||||
powPminus2: 'function',
|
||||
Gu: 'bigint',
|
||||
}
|
||||
);
|
||||
// Set defaults
|
||||
// ...nLength(curve.n, curve.nBitLength),
|
||||
return Object.freeze({ ...curve } as const);
|
||||
}
|
||||
|
||||
@@ -55,34 +51,14 @@ function validateOpts(curve: CurveType) {
|
||||
export function montgomery(curveDef: CurveType): CurveFn {
|
||||
const CURVE = validateOpts(curveDef);
|
||||
const { P } = CURVE;
|
||||
const modP = (a: bigint) => mod.mod(a, P);
|
||||
const modP = (n: bigint) => mod(n, P);
|
||||
const montgomeryBits = CURVE.montgomeryBits;
|
||||
const montgomeryBytes = Math.ceil(montgomeryBits / 8);
|
||||
const fieldLen = CURVE.nByteLength;
|
||||
const adjustScalarBytes = CURVE.adjustScalarBytes || ((bytes: Uint8Array) => bytes);
|
||||
const powPminus2 = CURVE.powPminus2 || ((x: bigint) => mod.pow(x, P - BigInt(2), P));
|
||||
const powPminus2 = CURVE.powPminus2 || ((x: bigint) => pow(x, P - BigInt(2), P));
|
||||
|
||||
/**
|
||||
* Checks for num to be in range:
|
||||
* For strict == true: `0 < num < max`.
|
||||
* For strict == false: `0 <= num < max`.
|
||||
* Converts non-float safe numbers to bigints.
|
||||
*/
|
||||
function normalizeScalar(num: number | bigint, max: bigint, strict = true): bigint {
|
||||
if (!max) throw new TypeError('Specify max value');
|
||||
if (typeof num === 'number' && Number.isSafeInteger(num)) num = BigInt(num);
|
||||
if (typeof num === 'bigint' && num < max) {
|
||||
if (strict) {
|
||||
if (_0n < num) return num;
|
||||
} else {
|
||||
if (_0n <= num) return num;
|
||||
}
|
||||
}
|
||||
throw new TypeError('Expected valid scalar: 0 < scalar < max');
|
||||
}
|
||||
|
||||
// cswap from RFC7748
|
||||
// NOTE: cswap is not from RFC7748!
|
||||
// cswap from RFC7748. But it is not from RFC7748!
|
||||
/*
|
||||
cswap(swap, x_2, x_3):
|
||||
dummy = mask(swap) AND (x_2 XOR x_3)
|
||||
@@ -99,7 +75,15 @@ export function montgomery(curveDef: CurveType): CurveFn {
|
||||
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
|
||||
// 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
|
||||
@@ -107,13 +91,10 @@ export function montgomery(curveDef: CurveType): CurveFn {
|
||||
* @returns new Point on Montgomery curve
|
||||
*/
|
||||
function montgomeryLadder(pointU: bigint, scalar: bigint): bigint {
|
||||
const { P } = CURVE;
|
||||
const u = normalizeScalar(pointU, P);
|
||||
const u = assertFieldElement(pointU);
|
||||
// Section 5: Implementations MUST accept non-canonical values and process them as
|
||||
// if they had been reduced modulo the field prime.
|
||||
const k = normalizeScalar(scalar, P);
|
||||
// The constant a24 is (486662 - 2) / 4 = 121665 for curve25519/X25519
|
||||
const a24 = CURVE.a24;
|
||||
const k = assertFieldElement(scalar);
|
||||
const x_1 = u;
|
||||
let x_2 = _1n;
|
||||
let z_2 = _0n;
|
||||
@@ -167,28 +148,21 @@ export function montgomery(curveDef: CurveType): CurveFn {
|
||||
}
|
||||
|
||||
function decodeUCoordinate(uEnc: Hex): bigint {
|
||||
const u = ensureBytes(uEnc, montgomeryBytes);
|
||||
// Section 5: When receiving such an array, implementations of X25519
|
||||
// MUST mask the most significant bit in the final byte.
|
||||
// This is very ugly way, but it works because fieldLen-1 is outside of bounds for X448, so this becomes NOOP
|
||||
// fieldLen - scalaryBytes = 1 for X448 and = 0 for X25519
|
||||
u[fieldLen - 1] &= 127; // 0b0111_1111
|
||||
const u = ensureBytes('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);
|
||||
}
|
||||
|
||||
function decodeScalar(n: Hex): bigint {
|
||||
const bytes = ensureBytes(n);
|
||||
const bytes = ensureBytes('scalar', n);
|
||||
if (bytes.length !== montgomeryBytes && bytes.length !== fieldLen)
|
||||
throw new Error(`Expected ${montgomeryBytes} or ${fieldLen} bytes, got ${bytes.length}`);
|
||||
return bytesToNumberLE(adjustScalarBytes(bytes));
|
||||
}
|
||||
/**
|
||||
* Computes shared secret between private key "scalar" and public key's "u" (x) coordinate.
|
||||
* We can get 'y' coordinate from 'u',
|
||||
* but Point.fromHex also wants 'x' coordinate oddity flag,
|
||||
* and we cannot get 'x' without knowing 'v'.
|
||||
* Need to add generic conversion between twisted edwards and complimentary curve for JubJub.
|
||||
*/
|
||||
function scalarMult(scalar: Hex, u: Hex): Uint8Array {
|
||||
const pointU = decodeUCoordinate(u);
|
||||
const _scalar = decodeScalar(scalar);
|
||||
@@ -198,14 +172,10 @@ export function montgomery(curveDef: CurveType): CurveFn {
|
||||
if (pu === _0n) throw new Error('Invalid private or public key received');
|
||||
return encodeUCoordinate(pu);
|
||||
}
|
||||
/**
|
||||
* Computes public key from private.
|
||||
* Executes scalar multiplication of curve's base point by scalar.
|
||||
* @param scalar private key
|
||||
* @returns new public key
|
||||
*/
|
||||
// Computes public key from private. By doing scalar multiplication of base point.
|
||||
const GuBytes = encodeUCoordinate(CURVE.Gu);
|
||||
function scalarMultBase(scalar: Hex): Uint8Array {
|
||||
return scalarMult(scalar, CURVE.Gu);
|
||||
return scalarMult(scalar, GuBytes);
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -213,6 +183,7 @@ export function montgomery(curveDef: CurveType): CurveFn {
|
||||
scalarMultBase,
|
||||
getSharedSecret: (privateKey: Hex, publicKey: Hex) => scalarMult(privateKey, publicKey),
|
||||
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,75 +1,28 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
import * as mod from './modular.js';
|
||||
const _0n = BigInt(0);
|
||||
const _1n = BigInt(1);
|
||||
const _2n = BigInt(2);
|
||||
const u8a = (a: any): a is Uint8Array => a instanceof Uint8Array;
|
||||
|
||||
// We accept hex strings besides Uint8Array for simplicity
|
||||
export type Hex = Uint8Array | string;
|
||||
// Very few implementations accept numbers, we do it to ease learning curve
|
||||
export type PrivKey = Hex | bigint | number;
|
||||
export type PrivKey = Hex | bigint;
|
||||
export type CHash = {
|
||||
(message: Uint8Array | string): Uint8Array;
|
||||
blockLen: number;
|
||||
outputLen: number;
|
||||
create(opts?: { dkLen?: number }): any; // For shake
|
||||
};
|
||||
|
||||
// NOTE: these are generic, even if curve is on some polynominal field (bls), it will still have P/n/h
|
||||
// But generator can be different (Fp2/Fp6 for bls?)
|
||||
export type BasicCurve<T> = {
|
||||
// Field over which we'll do calculations (Fp)
|
||||
Fp: mod.Field<T>;
|
||||
// Curve order, total count of valid points in the field
|
||||
n: bigint;
|
||||
// Bit/byte length of curve order
|
||||
nBitLength?: number;
|
||||
nByteLength?: number;
|
||||
// Cofactor
|
||||
// NOTE: we can assign default value of 1, but then users will just ignore it, without validating with spec
|
||||
// Has not use for now, but nice to have in API
|
||||
h: bigint;
|
||||
hEff?: bigint; // Number to multiply to clear cofactor
|
||||
// Base point (x, y) aka generator point
|
||||
Gx: T;
|
||||
Gy: T;
|
||||
// Wrap private key by curve order (% CURVE.n instead of throwing error)
|
||||
wrapPrivateKey?: boolean;
|
||||
// Point at infinity is perfectly valid point, but not valid public key.
|
||||
// Disabled by default because of compatibility reasons with @noble/secp256k1
|
||||
allowInfinityPoint?: boolean;
|
||||
};
|
||||
|
||||
// Bans floats and integers above 2^53-1
|
||||
export function isPositiveInt(num: any): num is number {
|
||||
return typeof num === 'number' && Number.isSafeInteger(num) && num > 0;
|
||||
}
|
||||
|
||||
export function validateOpts<FP, T>(curve: BasicCurve<FP> & T) {
|
||||
mod.validateField(curve.Fp);
|
||||
for (const i of ['n', 'h'] as const) {
|
||||
const val = curve[i];
|
||||
if (typeof val !== 'bigint') throw new Error(`Invalid curve param ${i}=${val} (${typeof val})`);
|
||||
}
|
||||
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) {
|
||||
const val = curve[i];
|
||||
if (val === undefined) continue; // Optional
|
||||
if (!isPositiveInt(val)) throw new Error(`Invalid curve param ${i}=${val} (${typeof val})`);
|
||||
}
|
||||
// Set defaults
|
||||
return Object.freeze({ ...nLength(curve.n, curve.nBitLength), ...curve } as const);
|
||||
}
|
||||
export type FHash = (message: Uint8Array | string) => Uint8Array;
|
||||
|
||||
const hexes = Array.from({ length: 256 }, (v, i) => i.toString(16).padStart(2, '0'));
|
||||
export function bytesToHex(uint8a: Uint8Array): string {
|
||||
if (!(uint8a instanceof Uint8Array)) throw new Error('Expected Uint8Array');
|
||||
export function bytesToHex(bytes: Uint8Array): string {
|
||||
if (!u8a(bytes)) throw new Error('Uint8Array expected');
|
||||
// pre-caching improves the speed 6x
|
||||
let hex = '';
|
||||
for (let i = 0; i < uint8a.length; i++) {
|
||||
hex += hexes[uint8a[i]];
|
||||
for (let i = 0; i < bytes.length; i++) {
|
||||
hex += hexes[bytes[i]];
|
||||
}
|
||||
return hex;
|
||||
}
|
||||
@@ -80,25 +33,21 @@ export function numberToHexUnpadded(num: number | bigint): string {
|
||||
}
|
||||
|
||||
export function hexToNumber(hex: string): bigint {
|
||||
if (typeof hex !== 'string') {
|
||||
throw new TypeError('hexToNumber: expected string, got ' + typeof hex);
|
||||
}
|
||||
if (typeof hex !== 'string') throw new Error('hex string expected, got ' + typeof hex);
|
||||
// Big Endian
|
||||
return BigInt(`0x${hex}`);
|
||||
return BigInt(hex === '' ? '0' : `0x${hex}`);
|
||||
}
|
||||
|
||||
// Caching slows it down 2-3x
|
||||
export function hexToBytes(hex: string): Uint8Array {
|
||||
if (typeof hex !== 'string') {
|
||||
throw new TypeError('hexToBytes: expected string, got ' + typeof hex);
|
||||
}
|
||||
if (hex.length % 2) throw new Error('hexToBytes: received invalid unpadded hex ' + hex.length);
|
||||
if (typeof hex !== 'string') throw new Error('hex string expected, got ' + typeof hex);
|
||||
if (hex.length % 2) throw new Error('hex string is invalid: unpadded ' + hex.length);
|
||||
const array = new Uint8Array(hex.length / 2);
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
const j = i * 2;
|
||||
const hexByte = hex.slice(j, j + 2);
|
||||
const byte = Number.parseInt(hexByte, 16);
|
||||
if (Number.isNaN(byte) || byte < 0) throw new Error('Invalid byte sequence');
|
||||
if (Number.isNaN(byte) || byte < 0) throw new Error('invalid byte sequence');
|
||||
array[i] = byte;
|
||||
}
|
||||
return array;
|
||||
@@ -108,63 +57,48 @@ export function hexToBytes(hex: string): Uint8Array {
|
||||
export function bytesToNumberBE(bytes: Uint8Array): bigint {
|
||||
return hexToNumber(bytesToHex(bytes));
|
||||
}
|
||||
export function bytesToNumberLE(uint8a: Uint8Array): bigint {
|
||||
if (!(uint8a instanceof Uint8Array)) throw new Error('Expected Uint8Array');
|
||||
return BigInt('0x' + bytesToHex(Uint8Array.from(uint8a).reverse()));
|
||||
export function bytesToNumberLE(bytes: Uint8Array): bigint {
|
||||
if (!u8a(bytes)) throw new Error('Uint8Array expected');
|
||||
return hexToNumber(bytesToHex(Uint8Array.from(bytes).reverse()));
|
||||
}
|
||||
|
||||
export const numberToBytesBE = (n: bigint, len: number) =>
|
||||
hexToBytes(n.toString(16).padStart(len * 2, '0'));
|
||||
export const numberToBytesLE = (n: bigint, len: number) => numberToBytesBE(n, len).reverse();
|
||||
// Returns variable number bytes (minimal bigint encoding?)
|
||||
export const numberToVarBytesBE = (n: bigint) => hexToBytes(numberToHexUnpadded(n));
|
||||
|
||||
export function ensureBytes(hex: Hex, expectedLength?: number): Uint8Array {
|
||||
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
|
||||
// is instance of Uint8Array, and its slice() creates **mutable** copy
|
||||
const bytes = hex instanceof Uint8Array ? Uint8Array.from(hex) : hexToBytes(hex);
|
||||
if (typeof expectedLength === 'number' && bytes.length !== expectedLength)
|
||||
throw new Error(`Expected ${expectedLength} bytes`);
|
||||
return bytes;
|
||||
res = Uint8Array.from(hex);
|
||||
} else {
|
||||
throw new Error(`${title} must be hex string or Uint8Array`);
|
||||
}
|
||||
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.
|
||||
export function concatBytes(...arrays: Uint8Array[]): Uint8Array {
|
||||
if (!arrays.every((b) => b instanceof Uint8Array)) throw new Error('Uint8Array list expected');
|
||||
if (arrays.length === 1) return arrays[0];
|
||||
const length = arrays.reduce((a, arr) => a + arr.length, 0);
|
||||
const result = new Uint8Array(length);
|
||||
for (let i = 0, pad = 0; i < arrays.length; i++) {
|
||||
const arr = arrays[i];
|
||||
result.set(arr, pad);
|
||||
pad += arr.length;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// CURVE.n lengths
|
||||
export function nLength(n: bigint, nBitLength?: number) {
|
||||
// Bit size, byte size of CURVE.n
|
||||
const _nBitLength = nBitLength !== undefined ? nBitLength : n.toString(2).length;
|
||||
const nByteLength = Math.ceil(_nBitLength / 8);
|
||||
return { nBitLength: _nBitLength, nByteLength };
|
||||
}
|
||||
|
||||
/**
|
||||
* 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: Hex, groupOrder: bigint, isLE = false): bigint {
|
||||
hash = ensureBytes(hash);
|
||||
const hashLen = hash.length;
|
||||
const minLen = nLength(groupOrder).nByteLength + 8;
|
||||
if (minLen < 24 || hashLen < minLen || hashLen > 1024)
|
||||
throw new Error(`hashToPrivateScalar: expected ${minLen}-1024 bytes of input, got ${hashLen}`);
|
||||
const num = isLE ? bytesToNumberLE(hash) : bytesToNumberBE(hash);
|
||||
return mod.mod(num, groupOrder - _1n) + _1n;
|
||||
export function concatBytes(...arrs: Uint8Array[]): Uint8Array {
|
||||
const r = new Uint8Array(arrs.reduce((sum, a) => sum + a.length, 0));
|
||||
let pad = 0; // walk through each item, ensure they have proper type
|
||||
arrs.forEach((a) => {
|
||||
if (!u8a(a)) throw new Error('Uint8Array expected');
|
||||
r.set(a, pad);
|
||||
pad += a.length;
|
||||
});
|
||||
return r;
|
||||
}
|
||||
|
||||
export function equalBytes(b1: Uint8Array, b2: Uint8Array) {
|
||||
@@ -174,6 +108,16 @@ export function equalBytes(b1: Uint8Array, b2: Uint8Array) {
|
||||
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
|
||||
|
||||
// Amount of bits inside bigint (Same as n.toString(2).length)
|
||||
@@ -191,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('')}`))
|
||||
// Not using ** operator with bigints for old engines.
|
||||
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
215
src/bls12-381.ts
215
src/bls12-381.ts
@@ -1,9 +1,43 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
|
||||
// The pairing-friendly Barreto-Lynn-Scott elliptic curve construction allows to:
|
||||
// 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.
|
||||
// Differences from @noble/bls12-381 1.4:
|
||||
// - 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
|
||||
@@ -16,7 +50,7 @@ import { randomBytes } from '@noble/hashes/utils';
|
||||
import { bls, CurveFn } from './abstract/bls.js';
|
||||
import * as mod from './abstract/modular.js';
|
||||
import {
|
||||
concatBytes,
|
||||
concatBytes as concatB,
|
||||
ensureBytes,
|
||||
numberToBytesBE,
|
||||
bytesToNumberBE,
|
||||
@@ -28,10 +62,10 @@ import {
|
||||
} from './abstract/utils.js';
|
||||
// Types
|
||||
import {
|
||||
PointType,
|
||||
ProjectivePointType,
|
||||
ProjectiveConstructor,
|
||||
ProjPointType,
|
||||
ProjConstructor,
|
||||
mapToCurveSimpleSWU,
|
||||
AffinePoint,
|
||||
} from './abstract/weierstrass.js';
|
||||
import { isogenyMap } from './abstract/hash-to-curve.js';
|
||||
|
||||
@@ -99,25 +133,24 @@ const Fp2: mod.Field<Fp2> & Fp2Utils = {
|
||||
ONE: { c0: Fp.ONE, c1: Fp.ZERO },
|
||||
create: (num) => num,
|
||||
isValid: ({ c0, c1 }) => typeof c0 === 'bigint' && typeof c1 === 'bigint',
|
||||
isZero: ({ c0, c1 }) => Fp.isZero(c0) && Fp.isZero(c1),
|
||||
equals: ({ c0, c1 }: Fp2, { c0: r0, c1: r1 }: Fp2) => Fp.equals(c0, r0) && Fp.equals(c1, r1),
|
||||
negate: ({ c0, c1 }) => ({ c0: Fp.negate(c0), c1: Fp.negate(c1) }),
|
||||
is0: ({ c0, c1 }) => Fp.is0(c0) && Fp.is0(c1),
|
||||
eql: ({ c0, c1 }: Fp2, { c0: r0, c1: r1 }: Fp2) => Fp.eql(c0, r0) && Fp.eql(c1, r1),
|
||||
neg: ({ c0, c1 }) => ({ c0: Fp.neg(c0), c1: Fp.neg(c1) }),
|
||||
pow: (num, power) => mod.FpPow(Fp2, num, power),
|
||||
invertBatch: (nums) => mod.FpInvertBatch(Fp2, nums),
|
||||
// Normalized
|
||||
add: Fp2Add,
|
||||
sub: Fp2Subtract,
|
||||
mul: Fp2Multiply,
|
||||
square: Fp2Square,
|
||||
sqr: Fp2Square,
|
||||
// NonNormalized stuff
|
||||
addN: Fp2Add,
|
||||
subN: Fp2Subtract,
|
||||
mulN: Fp2Multiply,
|
||||
squareN: Fp2Square,
|
||||
sqrN: Fp2Square,
|
||||
// Why inversion for bigint inside Fp instead of Fp2? it is even used in that context?
|
||||
div: (lhs, rhs) =>
|
||||
Fp2.mul(lhs, typeof rhs === 'bigint' ? Fp.invert(Fp.create(rhs)) : Fp2.invert(rhs)),
|
||||
invert: ({ c0: a, c1: b }) => {
|
||||
div: (lhs, rhs) => Fp2.mul(lhs, typeof rhs === 'bigint' ? Fp.inv(Fp.create(rhs)) : Fp2.inv(rhs)),
|
||||
inv: ({ c0: a, c1: b }) => {
|
||||
// We wish to find the multiplicative inverse of a nonzero
|
||||
// element a + bu in Fp2. We leverage an identity
|
||||
//
|
||||
@@ -131,11 +164,11 @@ const Fp2: mod.Field<Fp2> & Fp2Utils = {
|
||||
// This gives that (a - bu)/(a² + b²) is the inverse
|
||||
// of (a + bu). Importantly, this can be computing using
|
||||
// only a single inversion in Fp.
|
||||
const factor = Fp.invert(Fp.create(a * a + b * b));
|
||||
const factor = Fp.inv(Fp.create(a * a + b * b));
|
||||
return { c0: Fp.mul(factor, Fp.create(a)), c1: Fp.mul(factor, Fp.create(-b)) };
|
||||
},
|
||||
sqrt: (num) => {
|
||||
if (Fp2.equals(num, Fp2.ZERO)) return Fp2.ZERO; // Algo doesn't handles this case
|
||||
if (Fp2.eql(num, Fp2.ZERO)) return Fp2.ZERO; // Algo doesn't handles this case
|
||||
// TODO: Optimize this line. It's extremely slow.
|
||||
// Speeding this up would boost aggregateSignatures.
|
||||
// https://eprint.iacr.org/2012/685.pdf applicable?
|
||||
@@ -143,15 +176,15 @@ const Fp2: mod.Field<Fp2> & Fp2Utils = {
|
||||
// https://github.com/supranational/blst/blob/aae0c7d70b799ac269ff5edf29d8191dbd357876/src/exp2.c#L1
|
||||
// Inspired by https://github.com/dalek-cryptography/curve25519-dalek/blob/17698df9d4c834204f83a3574143abacb4fc81a5/src/field.rs#L99
|
||||
const candidateSqrt = Fp2.pow(num, (Fp2.ORDER + 8n) / 16n);
|
||||
const check = Fp2.div(Fp2.square(candidateSqrt), num); // candidateSqrt.square().div(this);
|
||||
const check = Fp2.div(Fp2.sqr(candidateSqrt), num); // candidateSqrt.square().div(this);
|
||||
const R = FP2_ROOTS_OF_UNITY;
|
||||
const divisor = [R[0], R[2], R[4], R[6]].find((r) => Fp2.equals(r, check));
|
||||
const divisor = [R[0], R[2], R[4], R[6]].find((r) => Fp2.eql(r, check));
|
||||
if (!divisor) throw new Error('No root');
|
||||
const index = R.indexOf(divisor);
|
||||
const root = R[index / 2];
|
||||
if (!root) throw new Error('Invalid root');
|
||||
const x1 = Fp2.div(candidateSqrt, root);
|
||||
const x2 = Fp2.negate(x1);
|
||||
const x2 = Fp2.neg(x1);
|
||||
const { re: re1, im: im1 } = Fp2.reim(x1);
|
||||
const { re: re2, im: im2 } = Fp2.reim(x2);
|
||||
if (im1 > im2 || (im1 === im2 && re1 > re2)) return x1;
|
||||
@@ -170,7 +203,7 @@ const Fp2: mod.Field<Fp2> & Fp2Utils = {
|
||||
if (b.length !== Fp2.BYTES) throw new Error(`fromBytes wrong length=${b.length}`);
|
||||
return { c0: Fp.fromBytes(b.subarray(0, Fp.BYTES)), c1: Fp.fromBytes(b.subarray(Fp.BYTES)) };
|
||||
},
|
||||
toBytes: ({ c0, c1 }) => concatBytes(Fp.toBytes(c0), Fp.toBytes(c1)),
|
||||
toBytes: ({ c0, c1 }) => concatB(Fp.toBytes(c0), Fp.toBytes(c1)),
|
||||
cmov: ({ c0, c1 }, { c0: r0, c1: r1 }, c) => ({
|
||||
c0: Fp.cmov(c0, r0, c),
|
||||
c1: Fp.cmov(c1, r1, c),
|
||||
@@ -280,18 +313,15 @@ const Fp6Multiply = ({ c0, c1, c2 }: Fp6, rhs: Fp6 | bigint) => {
|
||||
};
|
||||
};
|
||||
const Fp6Square = ({ c0, c1, c2 }: Fp6) => {
|
||||
let t0 = Fp2.square(c0); // c0²
|
||||
let t0 = Fp2.sqr(c0); // c0²
|
||||
let t1 = Fp2.mul(Fp2.mul(c0, c1), 2n); // 2 * c0 * c1
|
||||
let t3 = Fp2.mul(Fp2.mul(c1, c2), 2n); // 2 * c1 * c2
|
||||
let t4 = Fp2.square(c2); // c2²
|
||||
let t4 = Fp2.sqr(c2); // c2²
|
||||
return {
|
||||
c0: Fp2.add(Fp2.mulByNonresidue(t3), t0), // T3 * (u + 1) + T0
|
||||
c1: Fp2.add(Fp2.mulByNonresidue(t4), t1), // T4 * (u + 1) + T1
|
||||
// T1 + (c0 - c1 + c2)² + T3 - T0 - T4
|
||||
c2: Fp2.sub(
|
||||
Fp2.sub(Fp2.add(Fp2.add(t1, Fp2.square(Fp2.add(Fp2.sub(c0, c1), c2))), t3), t0),
|
||||
t4
|
||||
),
|
||||
c2: Fp2.sub(Fp2.sub(Fp2.add(Fp2.add(t1, Fp2.sqr(Fp2.add(Fp2.sub(c0, c1), c2))), t3), t0), t4),
|
||||
};
|
||||
};
|
||||
type Fp6Utils = {
|
||||
@@ -312,35 +342,34 @@ const Fp6: mod.Field<Fp6> & Fp6Utils = {
|
||||
ONE: { c0: Fp2.ONE, c1: Fp2.ZERO, c2: Fp2.ZERO },
|
||||
create: (num) => num,
|
||||
isValid: ({ c0, c1, c2 }) => Fp2.isValid(c0) && Fp2.isValid(c1) && Fp2.isValid(c2),
|
||||
isZero: ({ c0, c1, c2 }) => Fp2.isZero(c0) && Fp2.isZero(c1) && Fp2.isZero(c2),
|
||||
negate: ({ c0, c1, c2 }) => ({ c0: Fp2.negate(c0), c1: Fp2.negate(c1), c2: Fp2.negate(c2) }),
|
||||
equals: ({ c0, c1, c2 }, { c0: r0, c1: r1, c2: r2 }) =>
|
||||
Fp2.equals(c0, r0) && Fp2.equals(c1, r1) && Fp2.equals(c2, r2),
|
||||
is0: ({ c0, c1, c2 }) => Fp2.is0(c0) && Fp2.is0(c1) && Fp2.is0(c2),
|
||||
neg: ({ c0, c1, c2 }) => ({ c0: Fp2.neg(c0), c1: Fp2.neg(c1), c2: Fp2.neg(c2) }),
|
||||
eql: ({ c0, c1, c2 }, { c0: r0, c1: r1, c2: r2 }) =>
|
||||
Fp2.eql(c0, r0) && Fp2.eql(c1, r1) && Fp2.eql(c2, r2),
|
||||
sqrt: () => {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
// Do we need division by bigint at all? Should be done via order:
|
||||
div: (lhs, rhs) =>
|
||||
Fp6.mul(lhs, typeof rhs === 'bigint' ? Fp.invert(Fp.create(rhs)) : Fp6.invert(rhs)),
|
||||
div: (lhs, rhs) => Fp6.mul(lhs, typeof rhs === 'bigint' ? Fp.inv(Fp.create(rhs)) : Fp6.inv(rhs)),
|
||||
pow: (num, power) => mod.FpPow(Fp6, num, power),
|
||||
invertBatch: (nums) => mod.FpInvertBatch(Fp6, nums),
|
||||
// Normalized
|
||||
add: Fp6Add,
|
||||
sub: Fp6Subtract,
|
||||
mul: Fp6Multiply,
|
||||
square: Fp6Square,
|
||||
sqr: Fp6Square,
|
||||
// NonNormalized stuff
|
||||
addN: Fp6Add,
|
||||
subN: Fp6Subtract,
|
||||
mulN: Fp6Multiply,
|
||||
squareN: Fp6Square,
|
||||
sqrN: Fp6Square,
|
||||
|
||||
invert: ({ c0, c1, c2 }) => {
|
||||
let t0 = Fp2.sub(Fp2.square(c0), Fp2.mulByNonresidue(Fp2.mul(c2, c1))); // c0² - c2 * c1 * (u + 1)
|
||||
let t1 = Fp2.sub(Fp2.mulByNonresidue(Fp2.square(c2)), Fp2.mul(c0, c1)); // c2² * (u + 1) - c0 * c1
|
||||
let t2 = Fp2.sub(Fp2.square(c1), Fp2.mul(c0, c2)); // c1² - c0 * c2
|
||||
inv: ({ c0, c1, c2 }) => {
|
||||
let t0 = Fp2.sub(Fp2.sqr(c0), Fp2.mulByNonresidue(Fp2.mul(c2, c1))); // c0² - c2 * c1 * (u + 1)
|
||||
let t1 = Fp2.sub(Fp2.mulByNonresidue(Fp2.sqr(c2)), Fp2.mul(c0, c1)); // c2² * (u + 1) - c0 * c1
|
||||
let t2 = Fp2.sub(Fp2.sqr(c1), Fp2.mul(c0, c2)); // c1² - c0 * c2
|
||||
// 1/(((c2 * T1 + c1 * T2) * v) + c0 * T0)
|
||||
let t4 = Fp2.invert(
|
||||
let t4 = Fp2.inv(
|
||||
Fp2.add(Fp2.mulByNonresidue(Fp2.add(Fp2.mul(c2, t1), Fp2.mul(c1, t2))), Fp2.mul(c0, t0))
|
||||
);
|
||||
return { c0: Fp2.mul(t4, t0), c1: Fp2.mul(t4, t1), c2: Fp2.mul(t4, t2) };
|
||||
@@ -355,7 +384,7 @@ const Fp6: mod.Field<Fp6> & Fp6Utils = {
|
||||
};
|
||||
},
|
||||
toBytes: ({ c0, c1, c2 }): Uint8Array =>
|
||||
concatBytes(Fp2.toBytes(c0), Fp2.toBytes(c1), Fp2.toBytes(c2)),
|
||||
concatB(Fp2.toBytes(c0), Fp2.toBytes(c1), Fp2.toBytes(c2)),
|
||||
cmov: ({ c0, c1, c2 }: Fp6, { c0: r0, c1: r1, c2: r2 }: Fp6, c) => ({
|
||||
c0: Fp2.cmov(c0, r0, c),
|
||||
c1: Fp2.cmov(c1, r1, c),
|
||||
@@ -498,11 +527,11 @@ const Fp12Square = ({ c0, c1 }: Fp12) => {
|
||||
}; // AB + AB
|
||||
};
|
||||
function Fp4Square(a: Fp2, b: Fp2): { first: Fp2; second: Fp2 } {
|
||||
const a2 = Fp2.square(a);
|
||||
const b2 = Fp2.square(b);
|
||||
const a2 = Fp2.sqr(a);
|
||||
const b2 = Fp2.sqr(b);
|
||||
return {
|
||||
first: Fp2.add(Fp2.mulByNonresidue(b2), a2), // b² * Nonresidue + a²
|
||||
second: Fp2.sub(Fp2.sub(Fp2.square(Fp2.add(a, b)), a2), b2), // (a + b)² - a² - b²
|
||||
second: Fp2.sub(Fp2.sub(Fp2.sqr(Fp2.add(a, b)), a2), b2), // (a + b)² - a² - b²
|
||||
};
|
||||
}
|
||||
type Fp12Utils = {
|
||||
@@ -525,30 +554,30 @@ const Fp12: mod.Field<Fp12> & Fp12Utils = {
|
||||
ONE: { c0: Fp6.ONE, c1: Fp6.ZERO },
|
||||
create: (num) => num,
|
||||
isValid: ({ c0, c1 }) => Fp6.isValid(c0) && Fp6.isValid(c1),
|
||||
isZero: ({ c0, c1 }) => Fp6.isZero(c0) && Fp6.isZero(c1),
|
||||
negate: ({ c0, c1 }) => ({ c0: Fp6.negate(c0), c1: Fp6.negate(c1) }),
|
||||
equals: ({ c0, c1 }, { c0: r0, c1: r1 }) => Fp6.equals(c0, r0) && Fp6.equals(c1, r1),
|
||||
is0: ({ c0, c1 }) => Fp6.is0(c0) && Fp6.is0(c1),
|
||||
neg: ({ c0, c1 }) => ({ c0: Fp6.neg(c0), c1: Fp6.neg(c1) }),
|
||||
eql: ({ c0, c1 }, { c0: r0, c1: r1 }) => Fp6.eql(c0, r0) && Fp6.eql(c1, r1),
|
||||
sqrt: () => {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
invert: ({ c0, c1 }) => {
|
||||
let t = Fp6.invert(Fp6.sub(Fp6.square(c0), Fp6.mulByNonresidue(Fp6.square(c1)))); // 1 / (c0² - c1² * v)
|
||||
return { c0: Fp6.mul(c0, t), c1: Fp6.negate(Fp6.mul(c1, t)) }; // ((C0 * T) * T) + (-C1 * T) * w
|
||||
inv: ({ c0, c1 }) => {
|
||||
let t = Fp6.inv(Fp6.sub(Fp6.sqr(c0), Fp6.mulByNonresidue(Fp6.sqr(c1)))); // 1 / (c0² - c1² * v)
|
||||
return { c0: Fp6.mul(c0, t), c1: Fp6.neg(Fp6.mul(c1, t)) }; // ((C0 * T) * T) + (-C1 * T) * w
|
||||
},
|
||||
div: (lhs, rhs) =>
|
||||
Fp12.mul(lhs, typeof rhs === 'bigint' ? Fp.invert(Fp.create(rhs)) : Fp12.invert(rhs)),
|
||||
Fp12.mul(lhs, typeof rhs === 'bigint' ? Fp.inv(Fp.create(rhs)) : Fp12.inv(rhs)),
|
||||
pow: (num, power) => mod.FpPow(Fp12, num, power),
|
||||
invertBatch: (nums) => mod.FpInvertBatch(Fp12, nums),
|
||||
// Normalized
|
||||
add: Fp12Add,
|
||||
sub: Fp12Subtract,
|
||||
mul: Fp12Multiply,
|
||||
square: Fp12Square,
|
||||
sqr: Fp12Square,
|
||||
// NonNormalized stuff
|
||||
addN: Fp12Add,
|
||||
subN: Fp12Subtract,
|
||||
mulN: Fp12Multiply,
|
||||
squareN: Fp12Square,
|
||||
sqrN: Fp12Square,
|
||||
|
||||
// Bytes utils
|
||||
fromBytes: (b: Uint8Array): Fp12 => {
|
||||
@@ -558,7 +587,7 @@ const Fp12: mod.Field<Fp12> & Fp12Utils = {
|
||||
c1: Fp6.fromBytes(b.subarray(Fp6.BYTES)),
|
||||
};
|
||||
},
|
||||
toBytes: ({ c0, c1 }): Uint8Array => concatBytes(Fp6.toBytes(c0), Fp6.toBytes(c1)),
|
||||
toBytes: ({ c0, c1 }): Uint8Array => concatB(Fp6.toBytes(c0), Fp6.toBytes(c1)),
|
||||
cmov: ({ c0, c1 }, { c0: r0, c1: r1 }, c) => ({
|
||||
c0: Fp6.cmov(c0, r0, c),
|
||||
c1: Fp6.cmov(c1, r1, c),
|
||||
@@ -602,7 +631,7 @@ const Fp12: mod.Field<Fp12> & Fp12Utils = {
|
||||
c0: Fp6.multiplyByFp2(c0, rhs),
|
||||
c1: Fp6.multiplyByFp2(c1, rhs),
|
||||
}),
|
||||
conjugate: ({ c0, c1 }): Fp12 => ({ c0, c1: Fp6.negate(c1) }),
|
||||
conjugate: ({ c0, c1 }): Fp12 => ({ c0, c1: Fp6.neg(c1) }),
|
||||
|
||||
// A cyclotomic group is a subgroup of Fp^n defined by
|
||||
// GΦₙ(p) = {α ∈ Fpⁿ : α^Φₙ(p) = 1}
|
||||
@@ -886,7 +915,7 @@ function psi(x: Fp2, y: Fp2): [Fp2, Fp2] {
|
||||
return [x2, y2];
|
||||
}
|
||||
// Ψ endomorphism
|
||||
function G2psi(c: ProjectiveConstructor<Fp2>, P: ProjectivePointType<Fp2>) {
|
||||
function G2psi(c: ProjConstructor<Fp2>, P: ProjPointType<Fp2>) {
|
||||
const affine = P.toAffine();
|
||||
const p = psi(affine.x, affine.y);
|
||||
return new c(p[0], p[1], Fp2.ONE);
|
||||
@@ -897,9 +926,9 @@ const PSI2_C1 =
|
||||
0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaacn;
|
||||
|
||||
function psi2(x: Fp2, y: Fp2): [Fp2, Fp2] {
|
||||
return [Fp2.mul(x, PSI2_C1), Fp2.negate(y)];
|
||||
return [Fp2.mul(x, PSI2_C1), Fp2.neg(y)];
|
||||
}
|
||||
function G2psi2(c: ProjectiveConstructor<Fp2>, P: ProjectivePointType<Fp2>) {
|
||||
function G2psi2(c: ProjConstructor<Fp2>, P: ProjPointType<Fp2>) {
|
||||
const affine = P.toAffine();
|
||||
const p = psi2(affine.x, affine.y);
|
||||
return new c(p[0], p[1], Fp2.ONE);
|
||||
@@ -915,11 +944,12 @@ function G2psi2(c: ProjectiveConstructor<Fp2>, P: ProjectivePointType<Fp2>) {
|
||||
// p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab
|
||||
// m = 2 (or 1 for G1 see section 8.8.1)
|
||||
// k = 128
|
||||
const htfDefaults = {
|
||||
const htfDefaults = Object.freeze({
|
||||
// DST: a domain separation tag
|
||||
// defined in section 2.2.5
|
||||
// Use utils.getDSTLabel(), utils.setDSTLabel(value)
|
||||
DST: 'BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_',
|
||||
encodeDST: 'BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_',
|
||||
// p: the characteristic of F
|
||||
// where F is a finite field of characteristic p and order q = p^m
|
||||
p: Fp.ORDER,
|
||||
@@ -936,7 +966,7 @@ const htfDefaults = {
|
||||
// wide range of hash functions, including SHA-2, SHA-3, BLAKE2, and others.
|
||||
// BBS+ uses blake2: https://github.com/hyperledger/aries-framework-go/issues/2247
|
||||
hash: sha256,
|
||||
} as const;
|
||||
} as const);
|
||||
|
||||
// Encoding utils
|
||||
// Point on G1 curve: (x, y)
|
||||
@@ -989,7 +1019,7 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
||||
// φ endomorphism
|
||||
const cubicRootOfUnityModP =
|
||||
0x5f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffen;
|
||||
const phi = new c(Fp.mul(point.x, cubicRootOfUnityModP), point.y, point.z);
|
||||
const phi = new c(Fp.mul(point.px, cubicRootOfUnityModP), point.py, point.pz);
|
||||
|
||||
// todo: unroll
|
||||
const xP = point.multiplyUnsafe(bls12_381.CURVE.x).negate(); // [x]P
|
||||
@@ -1018,7 +1048,7 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
||||
const { x, y } = G1_SWU(Fp.create(scalars[0]));
|
||||
return isogenyMapG1(x, y);
|
||||
},
|
||||
fromBytes: (bytes: Uint8Array): { x: Fp; y: Fp } => {
|
||||
fromBytes: (bytes: Uint8Array): AffinePoint<Fp> => {
|
||||
if (bytes.length === 48) {
|
||||
const P = Fp.ORDER;
|
||||
const compressedValue = bytesToNumberBE(bytes);
|
||||
@@ -1030,11 +1060,11 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
||||
let y = Fp.sqrt(right);
|
||||
if (!y) throw new Error('Invalid compressed G1 point');
|
||||
const aflag = bitGet(compressedValue, C_BIT_POS);
|
||||
if ((y * 2n) / P !== aflag) y = Fp.negate(y);
|
||||
if ((y * 2n) / P !== aflag) y = Fp.neg(y);
|
||||
return { x: Fp.create(x), y: Fp.create(y) };
|
||||
} else if (bytes.length === 96) {
|
||||
// Check if the infinity flag is set
|
||||
if ((bytes[0] & (1 << 6)) !== 0) return bls12_381.G1.Point.ZERO;
|
||||
if ((bytes[0] & (1 << 6)) !== 0) return bls12_381.G1.ProjectivePoint.ZERO.toAffine();
|
||||
const x = bytesToNumberBE(bytes.slice(0, Fp.BYTES));
|
||||
const y = bytesToNumberBE(bytes.slice(Fp.BYTES));
|
||||
return { x: Fp.create(x), y: Fp.create(y) };
|
||||
@@ -1044,7 +1074,7 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
||||
},
|
||||
toBytes: (c, point, isCompressed) => {
|
||||
const isZero = point.equals(c.ZERO);
|
||||
const { x, y } = point;
|
||||
const { x, y } = point.toAffine();
|
||||
if (isCompressed) {
|
||||
if (isZero) return COMPRESSED_ZERO.slice();
|
||||
const P = Fp.ORDER;
|
||||
@@ -1055,10 +1085,10 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
||||
} else {
|
||||
if (isZero) {
|
||||
// 2x PUBLIC_KEY_LENGTH
|
||||
const x = concatBytes(new Uint8Array([0x40]), new Uint8Array(2 * Fp.BYTES - 1));
|
||||
const x = concatB(new Uint8Array([0x40]), new Uint8Array(2 * Fp.BYTES - 1));
|
||||
return x;
|
||||
} else {
|
||||
return concatBytes(numberToBytesBE(x, Fp.BYTES), numberToBytesBE(y, Fp.BYTES));
|
||||
return concatB(numberToBytesBE(x, Fp.BYTES), numberToBytesBE(y, Fp.BYTES));
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1120,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
|
||||
return Q; // [x²-x-1]P + [x-1]Ψ(P) + Ψ²(2P)
|
||||
},
|
||||
fromBytes: (bytes: Uint8Array): { x: Fp2; y: Fp2 } => {
|
||||
fromBytes: (bytes: Uint8Array): AffinePoint<Fp2> => {
|
||||
const m_byte = bytes[0] & 0xe0;
|
||||
if (m_byte === 0x20 || m_byte === 0x60 || m_byte === 0xe0) {
|
||||
throw new Error('Invalid encoding flag: ' + m_byte);
|
||||
@@ -1128,6 +1158,8 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
||||
const bitC = m_byte & 0x80; // compression bit
|
||||
const bitI = m_byte & 0x40; // point at infinity bit
|
||||
const bitS = m_byte & 0x20; // sign bit
|
||||
const L = Fp.BYTES;
|
||||
const slc = (b: Uint8Array, from: number, to?: number) => bytesToNumberBE(b.slice(from, to));
|
||||
if (bytes.length === 96 && bitC) {
|
||||
const { b } = bls12_381.CURVE.G2;
|
||||
const P = Fp.ORDER;
|
||||
@@ -1140,23 +1172,23 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
||||
}
|
||||
return { x: Fp2.ZERO, y: Fp2.ZERO };
|
||||
}
|
||||
const x_1 = bytesToNumberBE(bytes.slice(0, Fp.BYTES));
|
||||
const x_0 = bytesToNumberBE(bytes.slice(Fp.BYTES));
|
||||
const x_1 = slc(bytes, 0, L);
|
||||
const x_0 = slc(bytes, L, 2 * L);
|
||||
const x = Fp2.create({ c0: Fp.create(x_0), c1: Fp.create(x_1) });
|
||||
const right = Fp2.add(Fp2.pow(x, 3n), b); // y² = x³ + 4 * (u+1) = x³ + b
|
||||
let y = Fp2.sqrt(right);
|
||||
const Y_bit = y.c1 === 0n ? (y.c0 * 2n) / P : (y.c1 * 2n) / P ? 1n : 0n;
|
||||
y = bitS > 0 && Y_bit > 0 ? y : Fp2.negate(y);
|
||||
y = bitS > 0 && Y_bit > 0 ? y : Fp2.neg(y);
|
||||
return { x, y };
|
||||
} else if (bytes.length === 192 && !bitC) {
|
||||
// Check if the infinity flag is set
|
||||
if ((bytes[0] & (1 << 6)) !== 0) {
|
||||
return { x: Fp2.ZERO, y: Fp2.ZERO };
|
||||
}
|
||||
const x1 = bytesToNumberBE(bytes.slice(0, Fp.BYTES));
|
||||
const x0 = bytesToNumberBE(bytes.slice(Fp.BYTES, 2 * Fp.BYTES));
|
||||
const y1 = bytesToNumberBE(bytes.slice(2 * Fp.BYTES, 3 * Fp.BYTES));
|
||||
const y0 = bytesToNumberBE(bytes.slice(3 * Fp.BYTES));
|
||||
const x1 = slc(bytes, 0, L);
|
||||
const x0 = slc(bytes, L, 2 * L);
|
||||
const y1 = slc(bytes, 2 * L, 3 * L);
|
||||
const y0 = slc(bytes, 3 * L, 4 * L);
|
||||
return { x: Fp2.fromBigTuple([x0, x1]), y: Fp2.fromBigTuple([y0, y1]) };
|
||||
} else {
|
||||
throw new Error('Invalid point G2, expected 96/192 bytes');
|
||||
@@ -1164,20 +1196,20 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
||||
},
|
||||
toBytes: (c, point, isCompressed) => {
|
||||
const isZero = point.equals(c.ZERO);
|
||||
const { x, y } = point;
|
||||
const { x, y } = point.toAffine();
|
||||
if (isCompressed) {
|
||||
const P = Fp.ORDER;
|
||||
if (isZero) return concatBytes(COMPRESSED_ZERO, numberToBytesBE(0n, Fp.BYTES));
|
||||
if (isZero) return concatB(COMPRESSED_ZERO, numberToBytesBE(0n, Fp.BYTES));
|
||||
const flag = Boolean(y.c1 === 0n ? (y.c0 * 2n) / P : (y.c1 * 2n) / P);
|
||||
// set compressed & sign bits (looks like different offsets than for G1/Fp?)
|
||||
let x_1 = bitSet(x.c1, C_BIT_POS, flag);
|
||||
x_1 = bitSet(x_1, S_BIT_POS, true);
|
||||
return concatBytes(numberToBytesBE(x_1, Fp.BYTES), numberToBytesBE(x.c0, Fp.BYTES));
|
||||
return concatB(numberToBytesBE(x_1, Fp.BYTES), numberToBytesBE(x.c0, Fp.BYTES));
|
||||
} else {
|
||||
if (isZero) return concatBytes(new Uint8Array([0x40]), new Uint8Array(4 * Fp.BYTES - 1)); // bytes[0] |= 1 << 6;
|
||||
if (isZero) return concatB(new Uint8Array([0x40]), new Uint8Array(4 * Fp.BYTES - 1)); // bytes[0] |= 1 << 6;
|
||||
const { re: x0, im: x1 } = Fp2.reim(x);
|
||||
const { re: y0, im: y1 } = Fp2.reim(y);
|
||||
return concatBytes(
|
||||
return concatB(
|
||||
numberToBytesBE(x1, Fp.BYTES),
|
||||
numberToBytesBE(x0, Fp.BYTES),
|
||||
numberToBytesBE(y1, Fp.BYTES),
|
||||
@@ -1187,8 +1219,8 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
||||
},
|
||||
Signature: {
|
||||
// TODO: Optimize, it's very slow because of sqrt.
|
||||
decode(hex: Hex): PointType<Fp2> {
|
||||
hex = ensureBytes(hex);
|
||||
decode(hex: Hex): ProjPointType<Fp2> {
|
||||
hex = ensureBytes('signatureHex', hex);
|
||||
const P = Fp.ORDER;
|
||||
const half = hex.length / 2;
|
||||
if (half !== 48 && half !== 96)
|
||||
@@ -1197,7 +1229,7 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
||||
const z2 = bytesToNumberBE(hex.slice(half));
|
||||
// Indicates the infinity point
|
||||
const bflag1 = bitGet(z1, I_BIT_POS);
|
||||
if (bflag1 === 1n) return bls12_381.G2.Point.ZERO;
|
||||
if (bflag1 === 1n) return bls12_381.G2.ProjectivePoint.ZERO;
|
||||
|
||||
const x1 = Fp.create(z1 & Fp.MASK);
|
||||
const x2 = Fp.create(z2);
|
||||
@@ -1213,23 +1245,24 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
||||
const aflag1 = bitGet(z1, 381);
|
||||
const isGreater = y1 > 0n && (y1 * 2n) / P !== aflag1;
|
||||
const isZero = y1 === 0n && (y0 * 2n) / P !== aflag1;
|
||||
if (isGreater || isZero) y = Fp2.negate(y);
|
||||
const point = new bls12_381.G2.Point(x, y);
|
||||
if (isGreater || isZero) y = Fp2.neg(y);
|
||||
const point = bls12_381.G2.ProjectivePoint.fromAffine({ x, y });
|
||||
point.assertValidity();
|
||||
return point;
|
||||
},
|
||||
encode(point: PointType<Fp2>) {
|
||||
encode(point: ProjPointType<Fp2>) {
|
||||
// NOTE: by some reasons it was missed in bls12-381, looks like bug
|
||||
point.assertValidity();
|
||||
if (point.equals(bls12_381.G2.Point.ZERO))
|
||||
return concatBytes(COMPRESSED_ZERO, numberToBytesBE(0n, Fp.BYTES));
|
||||
const { re: x0, im: x1 } = Fp2.reim(point.x);
|
||||
const { re: y0, im: y1 } = Fp2.reim(point.y);
|
||||
if (point.equals(bls12_381.G2.ProjectivePoint.ZERO))
|
||||
return concatB(COMPRESSED_ZERO, numberToBytesBE(0n, Fp.BYTES));
|
||||
const a = point.toAffine();
|
||||
const { re: x0, im: x1 } = Fp2.reim(a.x);
|
||||
const { re: y0, im: y1 } = Fp2.reim(a.y);
|
||||
const tmp = y1 > 0n ? y1 * 2n : y0 * 2n;
|
||||
const aflag1 = Boolean((tmp / Fp.ORDER) & 1n);
|
||||
const z1 = bitSet(bitSet(x1, 381, aflag1), S_BIT_POS, true);
|
||||
const z2 = x0;
|
||||
return concatBytes(numberToBytesBE(z1, Fp.BYTES), numberToBytesBE(z2, Fp.BYTES));
|
||||
return concatB(numberToBytesBE(z1, Fp.BYTES), numberToBytesBE(z2, Fp.BYTES));
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
import { weierstrass } from './abstract/weierstrass.js';
|
||||
import { sha256 } from '@noble/hashes/sha256';
|
||||
import { weierstrass } from './abstract/weierstrass.js';
|
||||
import { getHash } from './_shortw_utils.js';
|
||||
import { Fp } from './abstract/modular.js';
|
||||
/**
|
||||
|
||||
201
src/ed25519.ts
201
src/ed25519.ts
@@ -1,17 +1,18 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
import { sha512 } from '@noble/hashes/sha512';
|
||||
import { concatBytes, randomBytes, utf8ToBytes } from '@noble/hashes/utils';
|
||||
import { twistedEdwards, ExtendedPointType } from './abstract/edwards.js';
|
||||
import { twistedEdwards, ExtPointType } from './abstract/edwards.js';
|
||||
import { montgomery } from './abstract/montgomery.js';
|
||||
import { mod, pow2, isNegativeLE, Fp as Field, FpSqrtEven } from './abstract/modular.js';
|
||||
import {
|
||||
ensureBytes,
|
||||
equalBytes,
|
||||
bytesToHex,
|
||||
bytesToNumberLE,
|
||||
numberToBytesLE,
|
||||
Hex,
|
||||
ensureBytes,
|
||||
} from './abstract/utils.js';
|
||||
import * as htf from './abstract/hash-to-curve.js';
|
||||
|
||||
/**
|
||||
* ed25519 Twisted Edwards curve with following addons:
|
||||
@@ -93,79 +94,6 @@ export const ED25519_TORSION_SUBGROUP = [
|
||||
|
||||
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 = {
|
||||
// Param: a
|
||||
a: BigInt(-1),
|
||||
@@ -189,15 +117,6 @@ const ED25519_DEF = {
|
||||
// Ratio of u to v. Allows us to combine inversion and square root. Uses algo from RFC8032 5.1.3.
|
||||
// Constant-time, u/√v
|
||||
uvRatio,
|
||||
htfDefaults: {
|
||||
DST: 'edwards25519_XMD:SHA-512_ELL2_RO_',
|
||||
p: Fp.ORDER,
|
||||
m: 1,
|
||||
k: 128,
|
||||
expand: 'xmd',
|
||||
hash: sha512,
|
||||
},
|
||||
mapToCurve: (scalars: bigint[]) => map_to_curve_elligator2_edwards25519(scalars[0]),
|
||||
} as const;
|
||||
|
||||
export const ed25519 = twistedEdwards(ED25519_DEF);
|
||||
@@ -219,10 +138,10 @@ export const ed25519ph = twistedEdwards({
|
||||
|
||||
export const x25519 = montgomery({
|
||||
P: ED25519_P,
|
||||
a24: BigInt('121665'),
|
||||
a: BigInt(486662),
|
||||
montgomeryBits: 255, // n is 253 bits
|
||||
nByteLength: 32,
|
||||
Gu: '0900000000000000000000000000000000000000000000000000000000000000',
|
||||
Gu: BigInt(9),
|
||||
powPminus2: (x: bigint): bigint => {
|
||||
const P = ED25519_P;
|
||||
// x^(p-2) aka x^(2^255-21)
|
||||
@@ -230,10 +149,98 @@ export const x25519 = montgomery({
|
||||
return mod(pow2(pow_p_5_8, BigInt(3), P) * b2, P);
|
||||
},
|
||||
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) {
|
||||
if (!(other instanceof RistrettoPoint)) throw new TypeError('RistrettoPoint expected');
|
||||
if (!(other instanceof RistrettoPoint)) throw new Error('RistrettoPoint expected');
|
||||
}
|
||||
// √(-1) aka √(a) aka 2^((p-1)/4)
|
||||
const SQRT_M1 = BigInt(
|
||||
@@ -262,7 +269,7 @@ const MAX_255B = BigInt('0x7ffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
const bytes255ToNumberLE = (bytes: Uint8Array) =>
|
||||
ed25519.CURVE.Fp.create(bytesToNumberLE(bytes) & MAX_255B);
|
||||
|
||||
type ExtendedPoint = ExtendedPointType;
|
||||
type ExtendedPoint = ExtPointType;
|
||||
|
||||
// Computes Elligator map for Ristretto
|
||||
// https://ristretto.group/formulas/elligator.html
|
||||
@@ -310,7 +317,7 @@ export class RistrettoPoint {
|
||||
* @param hex 64-bit output of a hash function
|
||||
*/
|
||||
static hashToCurve(hex: Hex): RistrettoPoint {
|
||||
hex = ensureBytes(hex, 64);
|
||||
hex = ensureBytes('ristrettoHash', hex, 64);
|
||||
const r1 = bytes255ToNumberLE(hex.slice(0, 32));
|
||||
const R1 = calcElligatorRistrettoMap(r1);
|
||||
const r2 = bytes255ToNumberLE(hex.slice(32, 64));
|
||||
@@ -324,7 +331,7 @@ export class RistrettoPoint {
|
||||
* @param hex Ristretto-encoded 32 bytes. Not every 32-byte string is valid ristretto encoding
|
||||
*/
|
||||
static fromHex(hex: Hex): RistrettoPoint {
|
||||
hex = ensureBytes(hex, 32);
|
||||
hex = ensureBytes('ristrettoHex', hex, 32);
|
||||
const { a, d } = ed25519.CURVE;
|
||||
const P = ed25519.CURVE.Fp.ORDER;
|
||||
const mod = ed25519.CURVE.Fp.create;
|
||||
@@ -355,7 +362,7 @@ export class RistrettoPoint {
|
||||
* https://ristretto.group/formulas/encoding.html
|
||||
*/
|
||||
toRawBytes(): Uint8Array {
|
||||
let { x, y, z, t } = this.ep;
|
||||
let { ex: x, ey: y, ez: z, et: t } = this.ep;
|
||||
const P = ed25519.CURVE.Fp.ORDER;
|
||||
const mod = ed25519.CURVE.Fp.create;
|
||||
const u1 = mod(mod(z + y) * mod(z - y)); // 1
|
||||
@@ -393,12 +400,12 @@ export class RistrettoPoint {
|
||||
// Compare one point to another.
|
||||
equals(other: RistrettoPoint): boolean {
|
||||
assertRstPoint(other);
|
||||
const a = this.ep;
|
||||
const b = other.ep;
|
||||
const { ex: X1, ey: Y1 } = this.ep;
|
||||
const { ex: X2, ey: Y2 } = this.ep;
|
||||
const mod = ed25519.CURVE.Fp.create;
|
||||
// (x1 * y2 == y1 * x2) | (y1 * y2 == x1 * x2)
|
||||
const one = mod(a.x * b.y) === mod(a.y * b.x);
|
||||
const two = mod(a.y * b.y) === mod(a.x * b.x);
|
||||
const one = mod(X1 * Y2) === mod(Y1 * X2);
|
||||
const two = mod(Y1 * Y2) === mod(X1 * X2);
|
||||
return one || two;
|
||||
}
|
||||
|
||||
@@ -412,11 +419,11 @@ export class RistrettoPoint {
|
||||
return new RistrettoPoint(this.ep.subtract(other.ep));
|
||||
}
|
||||
|
||||
multiply(scalar: number | bigint): RistrettoPoint {
|
||||
multiply(scalar: bigint): RistrettoPoint {
|
||||
return new RistrettoPoint(this.ep.multiply(scalar));
|
||||
}
|
||||
|
||||
multiplyUnsafe(scalar: number | bigint): RistrettoPoint {
|
||||
multiplyUnsafe(scalar: bigint): RistrettoPoint {
|
||||
return new RistrettoPoint(this.ep.multiplyUnsafe(scalar));
|
||||
}
|
||||
}
|
||||
|
||||
180
src/ed448.ts
180
src/ed448.ts
@@ -4,6 +4,7 @@ import { concatBytes, randomBytes, utf8ToBytes, wrapConstructor } from '@noble/h
|
||||
import { twistedEdwards } from './abstract/edwards.js';
|
||||
import { mod, pow2, Fp as Field } from './abstract/modular.js';
|
||||
import { montgomery } from './abstract/montgomery.js';
|
||||
import * as htf from './abstract/hash-to-curve.js';
|
||||
|
||||
/**
|
||||
* Edwards448 (not Ed448-Goldilocks) curve with following addons:
|
||||
@@ -54,81 +55,6 @@ function adjustScalarBytes(bytes: Uint8Array): Uint8Array {
|
||||
|
||||
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 = {
|
||||
// Param: a
|
||||
a: BigInt(1),
|
||||
@@ -189,15 +115,6 @@ const ED448_DEF = {
|
||||
// square root exists, and the decoding fails.
|
||||
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;
|
||||
|
||||
export const ed448 = twistedEdwards(ED448_DEF);
|
||||
@@ -205,11 +122,11 @@ export const ed448 = twistedEdwards(ED448_DEF);
|
||||
export const ed448ph = twistedEdwards({ ...ED448_DEF, preHash: shake256_64 });
|
||||
|
||||
export const x448 = montgomery({
|
||||
a24: BigInt(39081),
|
||||
a: BigInt(156326),
|
||||
montgomeryBits: 448,
|
||||
nByteLength: 57,
|
||||
P: ed448P,
|
||||
Gu: '0500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
|
||||
Gu: BigInt(5),
|
||||
powPminus2: (x: bigint): bigint => {
|
||||
const P = ed448P;
|
||||
const Pminus3div4 = ed448_pow_Pminus3div4(x);
|
||||
@@ -217,6 +134,7 @@ export const x448 = montgomery({
|
||||
return mod(Pminus3 * x, P); // Pminus3 * x = Pminus2
|
||||
},
|
||||
adjustScalarBytes,
|
||||
randomBytes,
|
||||
// The 4-isogeny maps between the Montgomery curve and this Edwards
|
||||
// curve are:
|
||||
// (u, v) = (y^2/x^2, (2 - x^2 - y^2)*y/x^3)
|
||||
@@ -232,3 +150,93 @@ export const x448 = montgomery({
|
||||
// return numberToBytesLE(u, 56);
|
||||
// },
|
||||
});
|
||||
|
||||
// Hash To Curve Elligator2 Map
|
||||
const ELL2_C1 = (Fp.ORDER - BigInt(3)) / BigInt(4); // 1. c1 = (q - 3) / 4 # Integer arithmetic
|
||||
const ELL2_J = BigInt(156326);
|
||||
function map_to_curve_elligator2_curve448(u: bigint) {
|
||||
let tv1 = Fp.sqr(u); // 1. tv1 = u^2
|
||||
let e1 = Fp.eql(tv1, Fp.ONE); // 2. e1 = tv1 == 1
|
||||
tv1 = Fp.cmov(tv1, Fp.ZERO, e1); // 3. tv1 = CMOV(tv1, 0, e1) # If Z * u^2 == -1, set tv1 = 0
|
||||
let xd = Fp.sub(Fp.ONE, tv1); // 4. xd = 1 - tv1
|
||||
let x1n = Fp.neg(ELL2_J); // 5. x1n = -J
|
||||
let tv2 = Fp.sqr(xd); // 6. tv2 = xd^2
|
||||
let gxd = Fp.mul(tv2, xd); // 7. gxd = tv2 * xd # gxd = xd^3
|
||||
let gx1 = Fp.mul(tv1, Fp.neg(ELL2_J)); // 8. gx1 = -J * tv1 # x1n + J * xd
|
||||
gx1 = Fp.mul(gx1, x1n); // 9. gx1 = gx1 * x1n # x1n^2 + J * x1n * xd
|
||||
gx1 = Fp.add(gx1, tv2); // 10. gx1 = gx1 + tv2 # x1n^2 + J * x1n * xd + xd^2
|
||||
gx1 = Fp.mul(gx1, x1n); // 11. gx1 = gx1 * x1n # x1n^3 + J * x1n^2 * xd + x1n * xd^2
|
||||
let tv3 = Fp.sqr(gxd); // 12. tv3 = gxd^2
|
||||
tv2 = Fp.mul(gx1, gxd); // 13. tv2 = gx1 * gxd # gx1 * gxd
|
||||
tv3 = Fp.mul(tv3, tv2); // 14. tv3 = tv3 * tv2 # gx1 * gxd^3
|
||||
let y1 = Fp.pow(tv3, ELL2_C1); // 15. y1 = tv3^c1 # (gx1 * gxd^3)^((p - 3) / 4)
|
||||
y1 = Fp.mul(y1, tv2); // 16. y1 = y1 * tv2 # gx1 * gxd * (gx1 * gxd^3)^((p - 3) / 4)
|
||||
let x2n = Fp.mul(x1n, Fp.neg(tv1)); // 17. x2n = -tv1 * x1n # x2 = x2n / xd = -1 * u^2 * x1n / xd
|
||||
let y2 = Fp.mul(y1, u); // 18. y2 = y1 * u
|
||||
y2 = Fp.cmov(y2, Fp.ZERO, e1); // 19. y2 = CMOV(y2, 0, e1)
|
||||
tv2 = Fp.sqr(y1); // 20. tv2 = y1^2
|
||||
tv2 = Fp.mul(tv2, gxd); // 21. tv2 = tv2 * gxd
|
||||
let e2 = Fp.eql(tv2, gx1); // 22. e2 = tv2 == gx1
|
||||
let xn = Fp.cmov(x2n, x1n, e2); // 23. xn = CMOV(x2n, x1n, e2) # If e2, x = x1, else x = x2
|
||||
let y = Fp.cmov(y2, y1, e2); // 24. y = CMOV(y2, y1, e2) # If e2, y = y1, else y = y2
|
||||
let e3 = Fp.isOdd(y); // 25. e3 = sgn0(y) == 1 # Fix sign of y
|
||||
y = Fp.cmov(y, Fp.neg(y), e2 !== e3); // 26. y = CMOV(y, -y, e2 XOR e3)
|
||||
return { xn, xd, yn: y, yd: Fp.ONE }; // 27. return (xn, xd, y, 1)
|
||||
}
|
||||
function map_to_curve_elligator2_edwards448(u: bigint) {
|
||||
let { xn, xd, yn, yd } = map_to_curve_elligator2_curve448(u); // 1. (xn, xd, yn, yd) = map_to_curve_elligator2_curve448(u)
|
||||
let xn2 = Fp.sqr(xn); // 2. xn2 = xn^2
|
||||
let xd2 = Fp.sqr(xd); // 3. xd2 = xd^2
|
||||
let xd4 = Fp.sqr(xd2); // 4. xd4 = xd2^2
|
||||
let yn2 = Fp.sqr(yn); // 5. yn2 = yn^2
|
||||
let yd2 = Fp.sqr(yd); // 6. yd2 = yd^2
|
||||
let xEn = Fp.sub(xn2, xd2); // 7. xEn = xn2 - xd2
|
||||
let tv2 = Fp.sub(xEn, xd2); // 8. tv2 = xEn - xd2
|
||||
xEn = Fp.mul(xEn, xd2); // 9. xEn = xEn * xd2
|
||||
xEn = Fp.mul(xEn, yd); // 10. xEn = xEn * yd
|
||||
xEn = Fp.mul(xEn, yn); // 11. xEn = xEn * yn
|
||||
xEn = Fp.mul(xEn, 4n); // 12. xEn = xEn * 4
|
||||
tv2 = Fp.mul(tv2, xn2); // 13. tv2 = tv2 * xn2
|
||||
tv2 = Fp.mul(tv2, yd2); // 14. tv2 = tv2 * yd2
|
||||
let tv3 = Fp.mul(yn2, 4n); // 15. tv3 = 4 * yn2
|
||||
let tv1 = Fp.add(tv3, yd2); // 16. tv1 = tv3 + yd2
|
||||
tv1 = Fp.mul(tv1, xd4); // 17. tv1 = tv1 * xd4
|
||||
let xEd = Fp.add(tv1, tv2); // 18. xEd = tv1 + tv2
|
||||
tv2 = Fp.mul(tv2, xn); // 19. tv2 = tv2 * xn
|
||||
let tv4 = Fp.mul(xn, xd4); // 20. tv4 = xn * xd4
|
||||
let yEn = Fp.sub(tv3, yd2); // 21. yEn = tv3 - yd2
|
||||
yEn = Fp.mul(yEn, tv4); // 22. yEn = yEn * tv4
|
||||
yEn = Fp.sub(yEn, tv2); // 23. yEn = yEn - tv2
|
||||
tv1 = Fp.add(xn2, xd2); // 24. tv1 = xn2 + xd2
|
||||
tv1 = Fp.mul(tv1, xd2); // 25. tv1 = tv1 * xd2
|
||||
tv1 = Fp.mul(tv1, xd); // 26. tv1 = tv1 * xd
|
||||
tv1 = Fp.mul(tv1, yn2); // 27. tv1 = tv1 * yn2
|
||||
tv1 = Fp.mul(tv1, BigInt(-2)); // 28. tv1 = -2 * tv1
|
||||
let yEd = Fp.add(tv2, tv1); // 29. yEd = tv2 + tv1
|
||||
tv4 = Fp.mul(tv4, yd2); // 30. tv4 = tv4 * yd2
|
||||
yEd = Fp.add(yEd, tv4); // 31. yEd = yEd + tv4
|
||||
tv1 = Fp.mul(xEd, yEd); // 32. tv1 = xEd * yEd
|
||||
let e = Fp.eql(tv1, Fp.ZERO); // 33. e = tv1 == 0
|
||||
xEn = Fp.cmov(xEn, Fp.ZERO, e); // 34. xEn = CMOV(xEn, 0, e)
|
||||
xEd = Fp.cmov(xEd, Fp.ONE, e); // 35. xEd = CMOV(xEd, 1, e)
|
||||
yEn = Fp.cmov(yEn, Fp.ONE, e); // 36. yEn = CMOV(yEn, 1, e)
|
||||
yEd = Fp.cmov(yEd, Fp.ONE, e); // 37. yEd = CMOV(yEd, 1, e)
|
||||
|
||||
const inv = Fp.invertBatch([xEd, yEd]); // batch division
|
||||
return { x: Fp.mul(xEn, inv[0]), y: Fp.mul(yEn, inv[1]) }; // 38. return (xEn, xEd, yEn, yEd)
|
||||
}
|
||||
|
||||
const { hashToCurve, encodeToCurve } = htf.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 };
|
||||
|
||||
@@ -39,7 +39,7 @@ export function groupHash(tag: Uint8Array, personalization: Uint8Array) {
|
||||
h.update(GH_FIRST_BLOCK);
|
||||
h.update(tag);
|
||||
// NOTE: returns ExtendedPoint, in case it will be multiplied later
|
||||
let p = jubjub.ExtendedPoint.fromAffine(jubjub.Point.fromHex(h.digest()));
|
||||
let p = jubjub.ExtendedPoint.fromHex(h.digest());
|
||||
// NOTE: cannot replace with isSmallOrder, returns Point*8
|
||||
p = p.multiply(jubjub.CURVE.h);
|
||||
if (p.equals(jubjub.ExtendedPoint.ZERO)) throw new Error('Point has small order');
|
||||
|
||||
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 { Fp as Field } from './abstract/modular.js';
|
||||
import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
|
||||
import * as htf from './abstract/hash-to-curve.js';
|
||||
|
||||
// NIST secp256r1 aka P256
|
||||
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/nist/P-256
|
||||
@@ -31,16 +32,22 @@ export const P256 = createCurve(
|
||||
Gy: BigInt('0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5'),
|
||||
h: BigInt(1),
|
||||
lowS: false,
|
||||
mapToCurve: (scalars: bigint[]) => mapSWU(scalars[0]),
|
||||
htfDefaults: {
|
||||
} as const,
|
||||
sha256
|
||||
);
|
||||
export const secp256r1 = P256;
|
||||
|
||||
const { hashToCurve, encodeToCurve } = htf.createHasher(
|
||||
secp256r1.ProjectivePoint,
|
||||
(scalars: bigint[]) => mapSWU(scalars[0]),
|
||||
{
|
||||
DST: 'P256_XMD:SHA-256_SSWU_RO_',
|
||||
encodeDST: 'P256_XMD:SHA-256_SSWU_NU_',
|
||||
p: Fp.ORDER,
|
||||
m: 1,
|
||||
k: 128,
|
||||
expand: 'xmd',
|
||||
hash: sha256,
|
||||
},
|
||||
} 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 { Fp as Field } from './abstract/modular.js';
|
||||
import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
|
||||
import * as htf from './abstract/hash-to-curve.js';
|
||||
|
||||
// NIST secp384r1 aka P384
|
||||
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/nist/P-384
|
||||
@@ -35,16 +36,22 @@ export const P384 = createCurve({
|
||||
Gy: BigInt('0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f'),
|
||||
h: BigInt(1),
|
||||
lowS: false,
|
||||
mapToCurve: (scalars: bigint[]) => mapSWU(scalars[0]),
|
||||
htfDefaults: {
|
||||
} as const,
|
||||
sha384
|
||||
);
|
||||
export const secp384r1 = P384;
|
||||
|
||||
const { hashToCurve, encodeToCurve } = htf.createHasher(
|
||||
secp384r1.ProjectivePoint,
|
||||
(scalars: bigint[]) => mapSWU(scalars[0]),
|
||||
{
|
||||
DST: 'P384_XMD:SHA-384_SSWU_RO_',
|
||||
encodeDST: 'P384_XMD:SHA-384_SSWU_NU_',
|
||||
p: Fp.ORDER,
|
||||
m: 1,
|
||||
k: 192,
|
||||
expand: 'xmd',
|
||||
hash: sha384,
|
||||
},
|
||||
} 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) */
|
||||
import { createCurve } from './_shortw_utils.js';
|
||||
import { sha512 } from '@noble/hashes/sha512';
|
||||
import { bytesToHex, PrivKey } from './abstract/utils.js';
|
||||
import { Fp as Field } from './abstract/modular.js';
|
||||
import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
|
||||
import * as htf from './abstract/hash-to-curve.js';
|
||||
|
||||
// NIST secp521r1 aka P521
|
||||
// Note that it's 521, which differs from 512 of its hash function.
|
||||
@@ -37,25 +37,21 @@ export const P521 = createCurve({
|
||||
Gy: BigInt('0x011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650'),
|
||||
h: BigInt(1),
|
||||
lowS: false,
|
||||
// P521 keys could be 130, 131, 132 bytes - which doesn't play nicely.
|
||||
// We ensure all keys are 132 bytes.
|
||||
// Does not replace validation; invalid keys would still be rejected.
|
||||
normalizePrivateKey(key: PrivKey) {
|
||||
if (typeof key === 'bigint') return key;
|
||||
if (key instanceof Uint8Array) key = bytesToHex(key);
|
||||
if (typeof key !== 'string' || !([130, 131, 132].includes(key.length))) {
|
||||
throw new Error('Invalid key');
|
||||
}
|
||||
return key.padStart(66 * 2, '0');
|
||||
},
|
||||
mapToCurve: (scalars: bigint[]) => mapSWU(scalars[0]),
|
||||
htfDefaults: {
|
||||
allowedPrivateKeyLengths: [130, 131, 132] // P521 keys are variable-length. Normalize to 132b
|
||||
} as const, sha512);
|
||||
export const secp521r1 = P521;
|
||||
|
||||
const { hashToCurve, encodeToCurve } = htf.createHasher(
|
||||
secp521r1.ProjectivePoint,
|
||||
(scalars: bigint[]) => mapSWU(scalars[0]),
|
||||
{
|
||||
DST: 'P521_XMD:SHA-512_SSWU_RO_',
|
||||
encodeDST: 'P521_XMD:SHA-512_SSWU_NU_',
|
||||
p: Fp.ORDER,
|
||||
m: 1,
|
||||
k: 256,
|
||||
expand: 'xmd',
|
||||
hash: sha512,
|
||||
},
|
||||
} as const, sha512);
|
||||
export const secp521r1 = P521;
|
||||
}
|
||||
);
|
||||
export { hashToCurve, encodeToCurve };
|
||||
|
||||
417
src/secp256k1.ts
417
src/secp256k1.ts
@@ -1,26 +1,12 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
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 { isogenyMap } from './abstract/hash-to-curve.js';
|
||||
|
||||
/**
|
||||
* secp256k1 belongs to Koblitz curves: it has efficiently computable endomorphism.
|
||||
* Endomorphism uses 2x less RAM, speeds up precomputation by 2x and ECDH / key recovery by 20%.
|
||||
* Should always be used for Projective's double-and-add multiplication.
|
||||
* For affines cached multiplication, it trades off 1/2 init time & 1/3 ram for 20% perf hit.
|
||||
* https://gist.github.com/paulmillr/eb670806793e84df628a7c434a873066
|
||||
*/
|
||||
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';
|
||||
import { bytesToNumberBE, concatBytes, ensureBytes, numberToBytesBE } from './abstract/utils.js';
|
||||
import * as htf from './abstract/hash-to-curve.js';
|
||||
import { createCurve } from './_shortw_utils.js';
|
||||
|
||||
const secp256k1P = BigInt('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f');
|
||||
const secp256k1N = BigInt('0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141');
|
||||
@@ -29,10 +15,7 @@ const _2n = BigInt(2);
|
||||
const divNearest = (a: bigint, b: bigint) => (a + b / _2n) / b;
|
||||
|
||||
/**
|
||||
* Allows to compute square root √y 2x faster.
|
||||
* To calculate √y, we need to exponentiate it to a very big number:
|
||||
* `y² = x³ + ax + b; y = y² ^ (p+1)/4`
|
||||
* We are unwrapping the loop and multiplying it bit-by-bit.
|
||||
* √n = n^((p+1)/4) for fields p = 3 mod 4. We unwrap the loop and multiply bit-by-bit.
|
||||
* (P+1n/4n).toString(2) would produce bits [223x 1, 0, 22x 1, 4x 0, 11, 00]
|
||||
*/
|
||||
function sqrtMod(y: bigint): bigint {
|
||||
@@ -55,14 +38,184 @@ function sqrtMod(y: bigint): bigint {
|
||||
const t1 = (pow2(b223, _23n, P) * b22) % P;
|
||||
const t2 = (pow2(t1, _6n, P) * b2) % 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;
|
||||
}
|
||||
|
||||
const Fp = Field(secp256k1P, undefined, undefined, { sqrt: sqrtMod });
|
||||
type Fp = bigint;
|
||||
|
||||
const isoMap = isogenyMap(
|
||||
export const secp256k1 = createCurve(
|
||||
{
|
||||
a: BigInt(0), // equation params: a, b
|
||||
b: BigInt(7), // Seem to be rigid: bitcointalk.org/index.php?topic=289795.msg3183975#msg3183975
|
||||
Fp, // Field's prime: 2n**256n - 2n**32n - 2n**9n - 2n**8n - 2n**7n - 2n**6n - 2n**4n - 1n
|
||||
n: secp256k1N, // Curve order, total count of valid points in the field
|
||||
// Base point (x, y) aka generator point
|
||||
Gx: BigInt('55066263022277343669578718895168534326250603453777594175500187360389116729240'),
|
||||
Gy: BigInt('32670510020758816978083085130507043184471273380659243275938904335757337482424'),
|
||||
h: BigInt(1), // Cofactor
|
||||
lowS: true, // Allow only low-S signatures by default in sign() and verify()
|
||||
/**
|
||||
* secp256k1 belongs to Koblitz curves: it has efficiently computable endomorphism.
|
||||
* Endomorphism uses 2x less RAM, speeds up precomputation by 2x and ECDH / key recovery by 20%.
|
||||
* For precomputed wNAF it trades off 1/2 init time & 1/3 ram for 20% perf hit.
|
||||
* Explanation: https://gist.github.com/paulmillr/eb670806793e84df628a7c434a873066
|
||||
*/
|
||||
endo: {
|
||||
beta: BigInt('0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee'),
|
||||
splitScalar: (k: bigint) => {
|
||||
const n = secp256k1N;
|
||||
const a1 = BigInt('0x3086d221a7d46bcde86c90e49284eb15');
|
||||
const b1 = -_1n * BigInt('0xe4437ed6010e88286f547fa90abfe4c3');
|
||||
const a2 = BigInt('0x114ca50f7a8e2f3f657c1108d9d44cfd8');
|
||||
const b2 = a1;
|
||||
const POW_2_128 = BigInt('0x100000000000000000000000000000000'); // (2n**128n).toString(16)
|
||||
|
||||
const c1 = divNearest(b2 * k, n);
|
||||
const c2 = divNearest(-b1 * k, n);
|
||||
let k1 = mod(k - c1 * a1 - c2 * a2, n);
|
||||
let k2 = mod(-c1 * b1 - c2 * b2, n);
|
||||
const k1neg = k1 > POW_2_128;
|
||||
const k2neg = k2 > POW_2_128;
|
||||
if (k1neg) k1 = n - k1;
|
||||
if (k2neg) k2 = n - k2;
|
||||
if (k1 > POW_2_128 || k2 > POW_2_128) {
|
||||
throw new Error('splitScalar: Endomorphism failed, k=' + k);
|
||||
}
|
||||
return { k1neg, k1, k2neg, k2 };
|
||||
},
|
||||
},
|
||||
},
|
||||
sha256
|
||||
);
|
||||
|
||||
// Schnorr signatures are superior to ECDSA from above. Below is Schnorr-specific BIP0340 code.
|
||||
// https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki
|
||||
const _0n = BigInt(0);
|
||||
const fe = (x: bigint) => typeof x === 'bigint' && _0n < x && x < secp256k1P;
|
||||
const ge = (x: bigint) => typeof x === 'bigint' && _0n < x && x < secp256k1N;
|
||||
/** An object mapping tags to their tagged hash prefix of [SHA256(tag) | SHA256(tag)] */
|
||||
const TAGGED_HASH_PREFIXES: { [tag: string]: Uint8Array } = {};
|
||||
function taggedHash(tag: string, ...messages: Uint8Array[]): Uint8Array {
|
||||
let tagP = TAGGED_HASH_PREFIXES[tag];
|
||||
if (tagP === undefined) {
|
||||
const tagH = sha256(Uint8Array.from(tag, (c) => c.charCodeAt(0)));
|
||||
tagP = concatBytes(tagH, tagH);
|
||||
TAGGED_HASH_PREFIXES[tag] = tagP;
|
||||
}
|
||||
return sha256(concatBytes(tagP, ...messages));
|
||||
}
|
||||
|
||||
// ECDSA compact points are 33-byte. Schnorr is 32: we strip first byte 0x02 or 0x03
|
||||
const pointToBytes = (point: PointType<bigint>) => point.toRawBytes(true).slice(1);
|
||||
const numTo32b = (n: bigint) => numberToBytesBE(n, 32);
|
||||
const modP = (x: bigint) => mod(x, secp256k1P);
|
||||
const modN = (x: bigint) => mod(x, secp256k1N);
|
||||
const Point = secp256k1.ProjectivePoint;
|
||||
const GmulAdd = (Q: PointType<bigint>, a: bigint, b: bigint) =>
|
||||
Point.BASE.multiplyAndAddUnsafe(Q, a, b);
|
||||
// Calculate point, scalar and bytes
|
||||
function schnorrGetExtPubKey(priv: PrivKey) {
|
||||
const d = secp256k1.utils.normPrivateKeyToScalar(priv); // same method executed in fromPrivateKey
|
||||
const point = Point.fromPrivateKey(d); // P = d'⋅G; 0 < d' < n check is done inside
|
||||
const scalar = point.hasEvenY() ? d : modN(-d); // d = d' if has_even_y(P), otherwise d = n-d'
|
||||
return { point, scalar, bytes: pointToBytes(point) };
|
||||
}
|
||||
/**
|
||||
* lift_x from BIP340. Convert 32-byte x coordinate to elliptic curve point.
|
||||
* @returns valid point checked for being on-curve
|
||||
*/
|
||||
function lift_x(x: bigint): PointType<bigint> {
|
||||
if (!fe(x)) throw new Error('bad x: need 0 < x < p'); // Fail if x ≥ p.
|
||||
const xx = modP(x * x);
|
||||
const c = modP(xx * x + BigInt(7)); // Let c = x³ + 7 mod p.
|
||||
let y = sqrtMod(c); // Let y = c^(p+1)/4 mod p.
|
||||
if (y % 2n !== 0n) y = modP(-y); // Return the unique point P such that x(P) = x and
|
||||
const p = new Point(x, y, _1n); // y(P) = y if y mod 2 = 0 or y(P) = p-y otherwise.
|
||||
p.assertValidity();
|
||||
return p;
|
||||
}
|
||||
/**
|
||||
* Create tagged hash, convert it to bigint, reduce modulo-n.
|
||||
*/
|
||||
function challenge(...args: Uint8Array[]): bigint {
|
||||
return modN(bytesToNumberBE(taggedHash('BIP0340/challenge', ...args)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Schnorr public key is just `x` coordinate of Point as per BIP340.
|
||||
*/
|
||||
function schnorrGetPublicKey(privateKey: Hex): Uint8Array {
|
||||
return schnorrGetExtPubKey(privateKey).bytes; // d'=int(sk). Fail if d'=0 or d'≥n. Ret bytes(d'⋅G)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates Schnorr signature as per BIP340. Verifies itself before returning anything.
|
||||
* auxRand is optional and is not the sole source of k generation: bad CSPRNG won't be dangerous.
|
||||
*/
|
||||
function schnorrSign(
|
||||
message: Hex,
|
||||
privateKey: PrivKey,
|
||||
auxRand: Hex = randomBytes(32)
|
||||
): Uint8Array {
|
||||
const m = ensureBytes('message', message);
|
||||
const { bytes: px, scalar: d } = schnorrGetExtPubKey(privateKey); // checks for isWithinCurveOrder
|
||||
const a = ensureBytes('auxRand', auxRand, 32); // Auxiliary random data a: a 32-byte array
|
||||
const t = numTo32b(d ^ bytesToNumberBE(taggedHash('BIP0340/aux', a))); // Let t be the byte-wise xor of bytes(d) and hash/aux(a)
|
||||
const rand = taggedHash('BIP0340/nonce', t, px, m); // Let rand = hash/nonce(t || bytes(P) || m)
|
||||
const k_ = modN(bytesToNumberBE(rand)); // Let k' = int(rand) mod n
|
||||
if (k_ === _0n) throw new Error('sign failed: k is zero'); // Fail if k' = 0.
|
||||
const { point: R, bytes: rx, scalar: k } = schnorrGetExtPubKey(k_); // Let R = k'⋅G.
|
||||
const e = challenge(rx, px, m); // Let e = int(hash/challenge(bytes(R) || bytes(P) || m)) mod n.
|
||||
const sig = new Uint8Array(64); // Let sig = bytes(R) || bytes((k + ed) mod n).
|
||||
sig.set(numTo32b(R.px), 0);
|
||||
sig.set(numTo32b(modN(k + e * d)), 32);
|
||||
// If Verify(bytes(P), m, sig) (see below) returns failure, abort
|
||||
if (!schnorrVerify(sig, m, px)) throw new Error('sign: Invalid signature produced');
|
||||
return sig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies Schnorr signature.
|
||||
* Will swallow errors & return false except for initial type validation of arguments.
|
||||
*/
|
||||
function schnorrVerify(signature: Hex, message: Hex, publicKey: Hex): boolean {
|
||||
const sig = ensureBytes('signature', signature, 64);
|
||||
const m = ensureBytes('message', message);
|
||||
const pub = ensureBytes('publicKey', publicKey, 32);
|
||||
try {
|
||||
const P = lift_x(bytesToNumberBE(pub)); // P = lift_x(int(pk)); fail if that fails
|
||||
const r = bytesToNumberBE(sig.subarray(0, 32)); // Let r = int(sig[0:32]); fail if r ≥ p.
|
||||
if (!fe(r)) return false;
|
||||
const s = bytesToNumberBE(sig.subarray(32, 64)); // Let s = int(sig[32:64]); fail if s ≥ n.
|
||||
if (!ge(s)) return false;
|
||||
const e = challenge(numTo32b(r), pointToBytes(P), m); // int(challenge(bytes(r)||bytes(P)||m))%n
|
||||
const R = GmulAdd(P, s, modN(-e)); // R = s⋅G - e⋅P
|
||||
if (!R || !R.hasEvenY() || R.toAffine().x !== r) return false; // -eP == (n-e)P
|
||||
return true; // Fail if is_infinite(R) / not has_even_y(R) / x(R) ≠ r.
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export const schnorr = {
|
||||
getPublicKey: schnorrGetPublicKey,
|
||||
sign: schnorrSign,
|
||||
verify: schnorrVerify,
|
||||
utils: {
|
||||
randomPrivateKey: secp256k1.utils.randomPrivateKey,
|
||||
getExtendedPublicKey: schnorrGetExtPubKey,
|
||||
lift_x,
|
||||
pointToBytes,
|
||||
numberToBytesBE,
|
||||
bytesToNumberBE,
|
||||
taggedHash,
|
||||
mod,
|
||||
},
|
||||
};
|
||||
|
||||
const isoMap = htf.isogenyMap(
|
||||
Fp,
|
||||
[
|
||||
// xNum
|
||||
@@ -94,224 +247,24 @@ const isoMap = isogenyMap(
|
||||
],
|
||||
].map((i) => i.map((j) => BigInt(j))) as [Fp[], Fp[], Fp[], Fp[]]
|
||||
);
|
||||
|
||||
const mapSWU = mapToCurveSimpleSWU(Fp, {
|
||||
A: BigInt('0x3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533'),
|
||||
B: BigInt('1771'),
|
||||
Z: Fp.create(BigInt('-11')),
|
||||
});
|
||||
|
||||
export const secp256k1 = createCurve(
|
||||
{
|
||||
// Params: a, b
|
||||
// Seem to be rigid https://bitcointalk.org/index.php?topic=289795.msg3183975#msg3183975
|
||||
a: BigInt(0),
|
||||
b: BigInt(7),
|
||||
// Field over which we'll do calculations;
|
||||
// 2n**256n - 2n**32n - 2n**9n - 2n**8n - 2n**7n - 2n**6n - 2n**4n - 1n
|
||||
Fp,
|
||||
// Curve order, total count of valid points in the field
|
||||
n: secp256k1N,
|
||||
// Base point (x, y) aka generator point
|
||||
Gx: BigInt('55066263022277343669578718895168534326250603453777594175500187360389116729240'),
|
||||
Gy: BigInt('32670510020758816978083085130507043184471273380659243275938904335757337482424'),
|
||||
h: BigInt(1),
|
||||
// Alllow only low-S signatures by default in sign() and verify()
|
||||
lowS: true,
|
||||
endo: {
|
||||
// Params taken from https://gist.github.com/paulmillr/eb670806793e84df628a7c434a873066
|
||||
beta: BigInt('0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee'),
|
||||
splitScalar: (k: bigint) => {
|
||||
const n = secp256k1N;
|
||||
const a1 = BigInt('0x3086d221a7d46bcde86c90e49284eb15');
|
||||
const b1 = -_1n * BigInt('0xe4437ed6010e88286f547fa90abfe4c3');
|
||||
const a2 = BigInt('0x114ca50f7a8e2f3f657c1108d9d44cfd8');
|
||||
const b2 = a1;
|
||||
const POW_2_128 = BigInt('0x100000000000000000000000000000000'); // (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 };
|
||||
},
|
||||
},
|
||||
mapToCurve: (scalars: bigint[]) => {
|
||||
export const { hashToCurve, encodeToCurve } = htf.createHasher(
|
||||
secp256k1.ProjectivePoint,
|
||||
(scalars: bigint[]) => {
|
||||
const { x, y } = mapSWU(Fp.create(scalars[0]));
|
||||
return isoMap(x, y);
|
||||
},
|
||||
htfDefaults: {
|
||||
{
|
||||
DST: 'secp256k1_XMD:SHA-256_SSWU_RO_',
|
||||
encodeDST: 'secp256k1_XMD:SHA-256_SSWU_NU_',
|
||||
p: Fp.ORDER,
|
||||
m: 1,
|
||||
k: 128,
|
||||
expand: 'xmd',
|
||||
hash: sha256,
|
||||
},
|
||||
},
|
||||
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) throw new Error('Schnorr pubkeys must be 32 bytes');
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
const len = 32; // group length
|
||||
if (bytes.length !== 2 * len)
|
||||
throw new TypeError(
|
||||
`SchnorrSignature.fromHex: expected ${2 * len} bytes, not ${bytes.length}`
|
||||
);
|
||||
const r = bytesToNumberBE(bytes.subarray(0, len));
|
||||
const s = bytesToNumberBE(bytes.subarray(len, 2 * len));
|
||||
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,
|
||||
};
|
||||
|
||||
343
src/stark.ts
343
src/stark.ts
@@ -1,158 +1,126 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
import { keccak_256 } from '@noble/hashes/sha3';
|
||||
import { sha256 } from '@noble/hashes/sha256';
|
||||
import { weierstrass, ProjectivePointType } from './abstract/weierstrass.js';
|
||||
import * as cutils from './abstract/utils.js';
|
||||
import { Fp } from './abstract/modular.js';
|
||||
import { utf8ToBytes } from '@noble/hashes/utils';
|
||||
import { Fp, mod, Field, validateField } from './abstract/modular.js';
|
||||
import { poseidon } from './abstract/poseidon.js';
|
||||
import { weierstrass, ProjPointType, SignatureType } from './abstract/weierstrass.js';
|
||||
import {
|
||||
Hex,
|
||||
bitMask,
|
||||
bytesToHex,
|
||||
bytesToNumberBE,
|
||||
concatBytes,
|
||||
ensureBytes as ensureBytesOrig,
|
||||
hexToBytes,
|
||||
hexToNumber,
|
||||
numberToVarBytesBE,
|
||||
} from './abstract/utils.js';
|
||||
import { getHash } from './_shortw_utils.js';
|
||||
|
||||
type ProjectivePoint = ProjectivePointType<bigint>;
|
||||
// Stark-friendly elliptic curve
|
||||
// https://docs.starkware.co/starkex/stark-curve.html
|
||||
|
||||
const CURVE_N = BigInt(
|
||||
type ProjectivePoint = ProjPointType<bigint>;
|
||||
const CURVE_ORDER = BigInt(
|
||||
'3618502788666131213697322783095070105526743751716087489154079457884512865583'
|
||||
);
|
||||
const nBitLength = 252;
|
||||
export const starkCurve = weierstrass({
|
||||
// Params: a, b
|
||||
a: BigInt(1),
|
||||
function bits2int(bytes: Uint8Array): bigint {
|
||||
while (bytes[0] === 0) bytes = bytes.subarray(1); // strip leading 0s
|
||||
// Copy-pasted from weierstrass.ts
|
||||
const delta = bytes.length * 8 - nBitLength;
|
||||
const num = bytesToNumberBE(bytes);
|
||||
return delta > 0 ? num >> BigInt(delta) : num;
|
||||
}
|
||||
function hex0xToBytes(hex: string): Uint8Array {
|
||||
if (typeof hex === 'string') {
|
||||
hex = strip0x(hex); // allow 0x prefix
|
||||
if (hex.length & 1) hex = '0' + hex; // allow unpadded hex
|
||||
}
|
||||
return hexToBytes(hex);
|
||||
}
|
||||
const curve = weierstrass({
|
||||
a: BigInt(1), // Params: a, b
|
||||
b: BigInt('3141592653589793238462643383279502884197169399375105820974944592307816406665'),
|
||||
// Field over which we'll do calculations; 2n**251n + 17n * 2n**192n + 1n
|
||||
// There is no efficient sqrt for field (P%4==1)
|
||||
Fp: Fp(BigInt('0x800000000000011000000000000000000000000000000000000000000000001')),
|
||||
// Curve order, total count of valid points in the field.
|
||||
n: CURVE_N,
|
||||
nBitLength: nBitLength, // len(bin(N).replace('0b',''))
|
||||
n: CURVE_ORDER, // Curve order, total count of valid points in the field.
|
||||
nBitLength, // len(bin(N).replace('0b',''))
|
||||
// Base point (x, y) aka generator point
|
||||
Gx: BigInt('874739451078007766457464989774322083649278607533249481151382481072868806602'),
|
||||
Gy: BigInt('152666792071518830868575557812948353041420400780739481342941381225525861407'),
|
||||
h: BigInt(1),
|
||||
// Default options
|
||||
lowS: false,
|
||||
h: BigInt(1), // cofactor
|
||||
lowS: false, // Allow high-s signatures
|
||||
...getHash(sha256),
|
||||
truncateHash: (hash: Uint8Array, truncateOnly = false): bigint => {
|
||||
// TODO: cleanup, ugly code
|
||||
// Fix truncation
|
||||
if (!truncateOnly) {
|
||||
let hashS = bytesToNumber0x(hash).toString(16);
|
||||
if (hashS.length === 63) {
|
||||
hashS += '0';
|
||||
hash = hexToBytes0x(hashS);
|
||||
}
|
||||
}
|
||||
// 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;
|
||||
// Custom truncation routines for stark curve
|
||||
bits2int,
|
||||
bits2int_modN: (bytes: Uint8Array): bigint => {
|
||||
// 2102820b232636d200cb21f1d330f20d096cae09d1bf3edb1cc333ddee11318 =>
|
||||
// 2102820b232636d200cb21f1d330f20d096cae09d1bf3edb1cc333ddee113180
|
||||
const hex = bytesToNumberBE(bytes).toString(16); // toHex unpadded
|
||||
if (hex.length === 63) bytes = hex0xToBytes(hex + '0'); // append trailing 0
|
||||
return mod(bits2int(bytes), CURVE_ORDER);
|
||||
},
|
||||
});
|
||||
export const _starkCurve = curve;
|
||||
|
||||
// Custom Starknet type conversion functions that can handle 0x and unpadded hex
|
||||
function hexToBytes0x(hex: string): Uint8Array {
|
||||
if (typeof hex !== 'string') {
|
||||
throw new TypeError('hexToBytes: expected string, got ' + typeof hex);
|
||||
}
|
||||
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 ensureBytes(hex: Hex): Uint8Array {
|
||||
return ensureBytesOrig('', typeof hex === 'string' ? hex0xToBytes(hex) : hex);
|
||||
}
|
||||
|
||||
function normalizePrivateKey(privKey: Hex) {
|
||||
return cutils.bytesToHex(ensureBytes0x(privKey)).padStart(32 * 2, '0');
|
||||
function normPrivKey(privKey: Hex): string {
|
||||
return bytesToHex(ensureBytes(privKey)).padStart(64, '0');
|
||||
}
|
||||
function getPublicKey0x(privKey: Hex, isCompressed?: boolean) {
|
||||
return starkCurve.getPublicKey(normalizePrivateKey(privKey), isCompressed);
|
||||
export function getPublicKey(privKey: Hex, isCompressed = false): Uint8Array {
|
||||
return curve.getPublicKey(normPrivKey(privKey), isCompressed);
|
||||
}
|
||||
function getSharedSecret0x(privKeyA: Hex, pubKeyB: Hex) {
|
||||
return starkCurve.getSharedSecret(normalizePrivateKey(privKeyA), pubKeyB);
|
||||
export function getSharedSecret(privKeyA: Hex, pubKeyB: Hex): Uint8Array {
|
||||
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) {
|
||||
if (typeof privKey === 'string') privKey = strip0x(privKey).padStart(64, '0');
|
||||
return starkCurve.sign(ensureBytes0x(msgHash), normalizePrivateKey(privKey), opts);
|
||||
const { CURVE, ProjectivePoint, Signature, utils } = curve;
|
||||
export { CURVE, ProjectivePoint, Signature, utils };
|
||||
|
||||
function extractX(bytes: Uint8Array): string {
|
||||
const hex = bytesToHex(bytes.subarray(1));
|
||||
const stripped = hex.replace(/^0+/gm, ''); // strip leading 0s
|
||||
return `0x${stripped}`;
|
||||
}
|
||||
function verify0x(signature: Hex, msgHash: Hex, pubKey: Hex) {
|
||||
const sig = signature instanceof Signature ? signature : ensureBytes0x(signature);
|
||||
return starkCurve.verify(sig, ensureBytes0x(msgHash), ensureBytes0x(pubKey));
|
||||
}
|
||||
|
||||
const { CURVE, Point, ProjectivePoint, Signature } = starkCurve;
|
||||
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))));
|
||||
function strip0x(hex: string) {
|
||||
return hex.replace(/^0x/i, '');
|
||||
}
|
||||
function numberTo0x16(num: bigint) {
|
||||
// can't use utils.numberToHexUnpadded: adds leading 0 for even byte length
|
||||
return `0x${num.toString(16)}`;
|
||||
}
|
||||
|
||||
// seed generation
|
||||
export function grindKey(seed: Hex) {
|
||||
const _seed = ensureBytes0x(seed);
|
||||
const _seed = ensureBytes(seed);
|
||||
const sha256mask = 2n ** 256n;
|
||||
const Fn = Fp(CURVE.n);
|
||||
const limit = sha256mask - Fn.create(sha256mask);
|
||||
const limit = sha256mask - mod(sha256mask, CURVE_ORDER);
|
||||
for (let i = 0; ; i++) {
|
||||
const key = hashKeyWithIndex(_seed, i);
|
||||
// key should be in [0, limit)
|
||||
if (key < limit) return Fn.create(key).toString(16);
|
||||
const key = sha256Num(concatBytes(_seed, numberToVarBytesBE(BigInt(i))));
|
||||
if (key < limit) return mod(key, CURVE_ORDER).toString(16); // key should be in [0, limit)
|
||||
if (i === 100000) throw new Error('grindKey is broken: tried 100k vals'); // prevent dos
|
||||
}
|
||||
}
|
||||
|
||||
export function getStarkKey(privateKey: Hex) {
|
||||
return bytesToHexEth(getPublicKey0x(privateKey, true).slice(1));
|
||||
export function getStarkKey(privateKey: Hex): string {
|
||||
return extractX(getPublicKey(privateKey, true));
|
||||
}
|
||||
|
||||
export function ethSigToPrivate(signature: string) {
|
||||
signature = strip0x(signature.replace(/^0x/, ''));
|
||||
export function ethSigToPrivate(signature: string): string {
|
||||
signature = strip0x(signature);
|
||||
if (signature.length !== 130) throw new Error('Wrong ethereum signature');
|
||||
return grindKey(signature.substring(0, 64));
|
||||
}
|
||||
@@ -164,38 +132,41 @@ export function getAccountPath(
|
||||
application: string,
|
||||
ethereumAddress: string,
|
||||
index: number
|
||||
) {
|
||||
const layerNum = int31(bytesToNumber0x(sha256(layer)));
|
||||
const applicationNum = int31(bytesToNumber0x(sha256(application)));
|
||||
const eth = hexToNumber0x(ethereumAddress);
|
||||
): string {
|
||||
const layerNum = int31(sha256Num(layer));
|
||||
const applicationNum = int31(sha256Num(application));
|
||||
const eth = hexToNumber(strip0x(ethereumAddress));
|
||||
return `m/2645'/${layerNum}'/${applicationNum}'/${int31(eth)}'/${int31(eth >> 31n)}'/${index}`;
|
||||
}
|
||||
|
||||
// https://docs.starkware.co/starkex/pedersen-hash-function.html
|
||||
const PEDERSEN_POINTS_AFFINE = [
|
||||
new Point(
|
||||
const PEDERSEN_POINTS = [
|
||||
new ProjectivePoint(
|
||||
2089986280348253421170679821480865132823066470938446095505822317253594081284n,
|
||||
1713931329540660377023406109199410414810705867260802078187082345529207694986n
|
||||
1713931329540660377023406109199410414810705867260802078187082345529207694986n,
|
||||
1n
|
||||
),
|
||||
new Point(
|
||||
new ProjectivePoint(
|
||||
996781205833008774514500082376783249102396023663454813447423147977397232763n,
|
||||
1668503676786377725805489344771023921079126552019160156920634619255970485781n
|
||||
1668503676786377725805489344771023921079126552019160156920634619255970485781n,
|
||||
1n
|
||||
),
|
||||
new Point(
|
||||
new ProjectivePoint(
|
||||
2251563274489750535117886426533222435294046428347329203627021249169616184184n,
|
||||
1798716007562728905295480679789526322175868328062420237419143593021674992973n
|
||||
1798716007562728905295480679789526322175868328062420237419143593021674992973n,
|
||||
1n
|
||||
),
|
||||
new Point(
|
||||
new ProjectivePoint(
|
||||
2138414695194151160943305727036575959195309218611738193261179310511854807447n,
|
||||
113410276730064486255102093846540133784865286929052426931474106396135072156n
|
||||
113410276730064486255102093846540133784865286929052426931474106396135072156n,
|
||||
1n
|
||||
),
|
||||
new Point(
|
||||
new ProjectivePoint(
|
||||
2379962749567351885752724891227938183011949129833673362440656643086021394946n,
|
||||
776496453633298175483985398648758586525933812536653089401905292063708816422n
|
||||
776496453633298175483985398648758586525933812536653089401905292063708816422n,
|
||||
1n
|
||||
),
|
||||
];
|
||||
// for (const p of PEDERSEN_POINTS) p._setWindowSize(8);
|
||||
const PEDERSEN_POINTS = PEDERSEN_POINTS_AFFINE.map(ProjectivePoint.fromAffine);
|
||||
|
||||
function pedersenPrecompute(p1: ProjectivePoint, p2: ProjectivePoint): ProjectivePoint[] {
|
||||
const out: ProjectivePoint[] = [];
|
||||
@@ -219,14 +190,16 @@ const PEDERSEN_POINTS2 = pedersenPrecompute(PEDERSEN_POINTS[3], PEDERSEN_POINTS[
|
||||
type PedersenArg = Hex | bigint | number;
|
||||
function pedersenArg(arg: PedersenArg): bigint {
|
||||
let value: bigint;
|
||||
if (typeof arg === 'bigint') value = arg;
|
||||
else if (typeof arg === 'number') {
|
||||
if (typeof arg === 'bigint') {
|
||||
value = arg;
|
||||
} else if (typeof arg === 'number') {
|
||||
if (!Number.isSafeInteger(arg)) throw new Error(`Invalid pedersenArg: ${arg}`);
|
||||
value = BigInt(arg);
|
||||
} else value = bytesToNumber0x(ensureBytes0x(arg));
|
||||
// [0..Fp)
|
||||
if (!(0n <= value && value < starkCurve.CURVE.Fp.ORDER))
|
||||
throw new Error(`PedersenArg should be 0 <= value < CURVE.P: ${value}`);
|
||||
} else {
|
||||
value = bytesToNumberBE(ensureBytes(arg));
|
||||
}
|
||||
if (!(0n <= value && value < curve.CURVE.Fp.ORDER))
|
||||
throw new Error(`PedersenArg should be 0 <= value < CURVE.P: ${value}`); // [0..Fp)
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -234,7 +207,7 @@ function pedersenSingle(point: ProjectivePoint, value: PedersenArg, constants: P
|
||||
let x = pedersenArg(value);
|
||||
for (let j = 0; j < 252; j++) {
|
||||
const pt = constants[j];
|
||||
if (pt.x === point.x) throw new Error('Same point');
|
||||
if (pt.px === point.px) throw new Error('Same point');
|
||||
if ((x & 1n) !== 0n) point = point.add(pt);
|
||||
x >>= 1n;
|
||||
}
|
||||
@@ -242,17 +215,17 @@ function pedersenSingle(point: ProjectivePoint, value: PedersenArg, constants: P
|
||||
}
|
||||
|
||||
// shift_point + x_low * P_0 + x_high * P1 + y_low * P2 + y_high * P3
|
||||
export function pedersen(x: PedersenArg, y: PedersenArg) {
|
||||
export function pedersen(x: PedersenArg, y: PedersenArg): string {
|
||||
let point: ProjectivePoint = PEDERSEN_POINTS[0];
|
||||
point = pedersenSingle(point, x, PEDERSEN_POINTS1);
|
||||
point = pedersenSingle(point, y, PEDERSEN_POINTS2);
|
||||
return bytesToHexEth(point.toAffine().toRawBytes(true).slice(1));
|
||||
return extractX(point.toRawBytes(true));
|
||||
}
|
||||
|
||||
export function hashChain(data: PedersenArg[], fn = pedersen) {
|
||||
if (!Array.isArray(data) || data.length < 1)
|
||||
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)
|
||||
.reverse()
|
||||
.reduce((acc, i) => fn(i, acc));
|
||||
@@ -261,5 +234,85 @@ export function hashChain(data: PedersenArg[], fn = pedersen) {
|
||||
export const computeHashOnElements = (data: PedersenArg[], fn = pedersen) =>
|
||||
[0, ...data, data.length].reduce((x, y) => fn(x, y));
|
||||
|
||||
const MASK_250 = 2n ** 250n - 1n;
|
||||
export const keccak = (data: Uint8Array) => bytesToNumber0x(keccak_256(data)) & MASK_250;
|
||||
const MASK_250 = bitMask(250);
|
||||
export const keccak = (data: Uint8Array): bigint => bytesToNumberBE(keccak_256(data)) & MASK_250;
|
||||
const sha256Num = (data: Uint8Array | string): bigint => bytesToNumberBE(sha256(data));
|
||||
|
||||
// Poseidon hash
|
||||
export const Fp253 = Fp(
|
||||
BigInt('14474011154664525231415395255581126252639794253786371766033694892385558855681')
|
||||
); // 2^253 + 2^199 + 1
|
||||
export const Fp251 = Fp(
|
||||
BigInt('3618502788666131213697322783095070105623107215331596699973092056135872020481')
|
||||
); // 2^251 + 17 * 2^192 + 1
|
||||
|
||||
function poseidonRoundConstant(Fp: Field<bigint>, name: string, idx: number) {
|
||||
const val = Fp.fromBytes(sha256(utf8ToBytes(`${name}${idx}`)));
|
||||
return Fp.create(val);
|
||||
}
|
||||
|
||||
// NOTE: doesn't check eiginvalues and possible can create unsafe matrix. But any filtration here will break compatibility with starknet
|
||||
// Please use only if you really know what you doing.
|
||||
// https://eprint.iacr.org/2019/458.pdf Section 2.3 (Avoiding Insecure Matrices)
|
||||
export function _poseidonMDS(Fp: Field<bigint>, name: string, m: number, attempt = 0) {
|
||||
const x_values: bigint[] = [];
|
||||
const y_values: bigint[] = [];
|
||||
for (let i = 0; i < m; i++) {
|
||||
x_values.push(poseidonRoundConstant(Fp, `${name}x`, attempt * m + i));
|
||||
y_values.push(poseidonRoundConstant(Fp, `${name}y`, attempt * m + i));
|
||||
}
|
||||
if (new Set([...x_values, ...y_values]).size !== 2 * m)
|
||||
throw new Error('X and Y values are not distinct');
|
||||
return x_values.map((x) => y_values.map((y) => Fp.inv(Fp.sub(x, y))));
|
||||
}
|
||||
|
||||
const MDS_SMALL = [
|
||||
[3, 1, 1],
|
||||
[1, -1, 1],
|
||||
[1, 1, -2],
|
||||
].map((i) => i.map(BigInt));
|
||||
|
||||
export type PoseidonOpts = {
|
||||
Fp: Field<bigint>;
|
||||
rate: number;
|
||||
capacity: number;
|
||||
roundsFull: number;
|
||||
roundsPartial: number;
|
||||
};
|
||||
|
||||
export function poseidonBasic(opts: PoseidonOpts, mds: bigint[][]) {
|
||||
validateField(opts.Fp);
|
||||
if (!Number.isSafeInteger(opts.rate) || !Number.isSafeInteger(opts.capacity))
|
||||
throw new Error(`Wrong poseidon opts: ${opts}`);
|
||||
const m = opts.rate + opts.capacity;
|
||||
const rounds = opts.roundsFull + opts.roundsPartial;
|
||||
const roundConstants = [];
|
||||
for (let i = 0; i < rounds; i++) {
|
||||
const row = [];
|
||||
for (let j = 0; j < m; j++) row.push(poseidonRoundConstant(opts.Fp, 'Hades', m * i + j));
|
||||
roundConstants.push(row);
|
||||
}
|
||||
return poseidon({
|
||||
...opts,
|
||||
t: m,
|
||||
sboxPower: 3,
|
||||
reversePartialPowIdx: true, // Why?!
|
||||
mds,
|
||||
roundConstants,
|
||||
});
|
||||
}
|
||||
|
||||
export function poseidonCreate(opts: PoseidonOpts, mdsAttempt = 0) {
|
||||
const m = opts.rate + opts.capacity;
|
||||
if (!Number.isSafeInteger(mdsAttempt)) throw new Error(`Wrong mdsAttempt=${mdsAttempt}`);
|
||||
return poseidonBasic(opts, _poseidonMDS(opts.Fp, 'HadesMDS', m, mdsAttempt));
|
||||
}
|
||||
|
||||
export const poseidonSmall = poseidonBasic(
|
||||
{ Fp: Fp251, rate: 2, capacity: 1, roundsFull: 8, roundsPartial: 83 },
|
||||
MDS_SMALL
|
||||
);
|
||||
|
||||
export function poseidonHash(x: bigint, y: bigint, fn = poseidonSmall) {
|
||||
return fn([x, y, 2n])[0];
|
||||
}
|
||||
|
||||
44
test/_more-curves.helpers.js
Normal file
44
test/_more-curves.helpers.js
Normal file
@@ -0,0 +1,44 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
import { createCurve } from '../_shortw_utils.js';
|
||||
import { sha224, sha256 } from '@noble/hashes/sha256';
|
||||
import { Fp } from '../abstract/modular.js';
|
||||
|
||||
// NIST secp192r1 aka P192
|
||||
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/secg/secp192r1
|
||||
export const P192 = createCurve(
|
||||
{
|
||||
// Params: a, b
|
||||
a: BigInt('0xfffffffffffffffffffffffffffffffefffffffffffffffc'),
|
||||
b: BigInt('0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1'),
|
||||
// Field over which we'll do calculations; 2n ** 192n - 2n ** 64n - 1n
|
||||
Fp: Fp(BigInt('0xfffffffffffffffffffffffffffffffeffffffffffffffff')),
|
||||
// Curve order, total count of valid points in the field.
|
||||
n: BigInt('0xffffffffffffffffffffffff99def836146bc9b1b4d22831'),
|
||||
// Base point (x, y) aka generator point
|
||||
Gx: BigInt('0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012'),
|
||||
Gy: BigInt('0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811'),
|
||||
h: BigInt(1),
|
||||
lowS: false,
|
||||
},
|
||||
sha256
|
||||
);
|
||||
export const secp192r1 = P192;
|
||||
|
||||
export const P224 = createCurve(
|
||||
{
|
||||
// Params: a, b
|
||||
a: BigInt('0xfffffffffffffffffffffffffffffffefffffffffffffffffffffffe'),
|
||||
b: BigInt('0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4'),
|
||||
// Field over which we'll do calculations;
|
||||
Fp: Fp(BigInt('0xffffffffffffffffffffffffffffffff000000000000000000000001')),
|
||||
// Curve order, total count of valid points in the field
|
||||
n: BigInt('0xffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d'),
|
||||
// Base point (x, y) aka generator point
|
||||
Gx: BigInt('0xb70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21'),
|
||||
Gy: BigInt('0xbd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34'),
|
||||
h: BigInt(1),
|
||||
lowS: false,
|
||||
},
|
||||
sha224
|
||||
);
|
||||
export const secp224r1 = P224;
|
||||
@@ -1,22 +1,21 @@
|
||||
import { deepStrictEqual, throws } from 'assert';
|
||||
import { should, describe } from 'micro-should';
|
||||
import * as fc from 'fast-check';
|
||||
import * as mod from '../lib/esm/abstract/modular.js';
|
||||
import { bytesToHex as toHex } from '../lib/esm/abstract/utils.js';
|
||||
import * as mod from '../esm/abstract/modular.js';
|
||||
import { bytesToHex as toHex } from '../esm/abstract/utils.js';
|
||||
// Generic tests for all curves in package
|
||||
import { secp192r1 } from '../lib/esm/p192.js';
|
||||
import { secp224r1 } from '../lib/esm/p224.js';
|
||||
import { secp256r1 } from '../lib/esm/p256.js';
|
||||
import { secp384r1 } from '../lib/esm/p384.js';
|
||||
import { secp521r1 } from '../lib/esm/p521.js';
|
||||
import { secp256k1 } from '../lib/esm/secp256k1.js';
|
||||
import { ed25519, ed25519ctx, ed25519ph } from '../lib/esm/ed25519.js';
|
||||
import { ed448, ed448ph } from '../lib/esm/ed448.js';
|
||||
import { starkCurve } from '../lib/esm/stark.js';
|
||||
import { pallas, vesta } from '../lib/esm/pasta.js';
|
||||
import { bn254 } from '../lib/esm/bn.js';
|
||||
import { jubjub } from '../lib/esm/jubjub.js';
|
||||
import { bls12_381 } from '../lib/esm/bls12-381.js';
|
||||
import { secp192r1, secp224r1 } from './_more-curves.helpers.js';
|
||||
import { secp256r1 } from '../esm/p256.js';
|
||||
import { secp384r1 } from '../esm/p384.js';
|
||||
import { secp521r1 } from '../esm/p521.js';
|
||||
import { secp256k1 } from '../esm/secp256k1.js';
|
||||
import { ed25519, ed25519ctx, ed25519ph, x25519 } from '../esm/ed25519.js';
|
||||
import { ed448, ed448ph } from '../esm/ed448.js';
|
||||
import { _starkCurve as starkCurve } from '../esm/stark.js';
|
||||
import { pallas, vesta } from '../esm/pasta.js';
|
||||
import { bn254 } from '../esm/bn.js';
|
||||
import { jubjub } from '../esm/jubjub.js';
|
||||
import { bls12_381 } from '../esm/bls12-381.js';
|
||||
|
||||
// Fields tests
|
||||
const FIELDS = {
|
||||
@@ -68,8 +67,8 @@ for (const c in FIELDS) {
|
||||
fc.property(FC_BIGINT, (num) => {
|
||||
const a = create(num);
|
||||
const b = create(num);
|
||||
deepStrictEqual(Fp.equals(a, b), true);
|
||||
deepStrictEqual(Fp.equals(b, a), true);
|
||||
deepStrictEqual(Fp.eql(a, b), true);
|
||||
deepStrictEqual(Fp.eql(b, a), true);
|
||||
})
|
||||
);
|
||||
});
|
||||
@@ -78,8 +77,8 @@ for (const c in FIELDS) {
|
||||
fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => {
|
||||
const a = create(num1);
|
||||
const b = create(num2);
|
||||
deepStrictEqual(Fp.equals(a, b), num1 === num2);
|
||||
deepStrictEqual(Fp.equals(b, a), num1 === num2);
|
||||
deepStrictEqual(Fp.eql(a, b), num1 === num2);
|
||||
deepStrictEqual(Fp.eql(b, a), num1 === num2);
|
||||
})
|
||||
);
|
||||
});
|
||||
@@ -124,8 +123,8 @@ for (const c in FIELDS) {
|
||||
fc.property(FC_BIGINT, (num1) => {
|
||||
const a = create(num1);
|
||||
const b = create(num1);
|
||||
deepStrictEqual(Fp.sub(Fp.ZERO, a), Fp.negate(a));
|
||||
deepStrictEqual(Fp.sub(a, b), Fp.add(a, Fp.negate(b)));
|
||||
deepStrictEqual(Fp.sub(Fp.ZERO, a), Fp.neg(a));
|
||||
deepStrictEqual(Fp.sub(a, b), Fp.add(a, Fp.neg(b)));
|
||||
deepStrictEqual(Fp.sub(a, b), Fp.add(a, Fp.mul(b, Fp.create(-1n))));
|
||||
})
|
||||
);
|
||||
@@ -134,13 +133,13 @@ for (const c in FIELDS) {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (num) => {
|
||||
const a = create(num);
|
||||
deepStrictEqual(Fp.negate(a), Fp.sub(Fp.ZERO, a));
|
||||
deepStrictEqual(Fp.negate(a), Fp.mul(a, Fp.create(-1n)));
|
||||
deepStrictEqual(Fp.neg(a), Fp.sub(Fp.ZERO, a));
|
||||
deepStrictEqual(Fp.neg(a), Fp.mul(a, Fp.create(-1n)));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('negate(0)', () => {
|
||||
deepStrictEqual(Fp.negate(Fp.ZERO), Fp.ZERO);
|
||||
deepStrictEqual(Fp.neg(Fp.ZERO), Fp.ZERO);
|
||||
});
|
||||
|
||||
should('multiply/commutativity', () => {
|
||||
@@ -190,7 +189,7 @@ for (const c in FIELDS) {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (num) => {
|
||||
const a = create(num);
|
||||
deepStrictEqual(Fp.square(a), Fp.mul(a, a));
|
||||
deepStrictEqual(Fp.sqr(a), Fp.mul(a, a));
|
||||
})
|
||||
);
|
||||
});
|
||||
@@ -207,18 +206,18 @@ for (const c in FIELDS) {
|
||||
});
|
||||
|
||||
should('square(0)', () => {
|
||||
deepStrictEqual(Fp.square(Fp.ZERO), Fp.ZERO);
|
||||
deepStrictEqual(Fp.sqr(Fp.ZERO), Fp.ZERO);
|
||||
deepStrictEqual(Fp.mul(Fp.ZERO, Fp.ZERO), Fp.ZERO);
|
||||
});
|
||||
|
||||
should('square(1)', () => {
|
||||
deepStrictEqual(Fp.square(Fp.ONE), Fp.ONE);
|
||||
deepStrictEqual(Fp.sqr(Fp.ONE), Fp.ONE);
|
||||
deepStrictEqual(Fp.mul(Fp.ONE, Fp.ONE), Fp.ONE);
|
||||
});
|
||||
|
||||
should('square(-1)', () => {
|
||||
const minus1 = Fp.negate(Fp.ONE);
|
||||
deepStrictEqual(Fp.square(minus1), Fp.ONE);
|
||||
const minus1 = Fp.neg(Fp.ONE);
|
||||
deepStrictEqual(Fp.sqr(minus1), Fp.ONE);
|
||||
deepStrictEqual(Fp.mul(minus1, minus1), Fp.ONE);
|
||||
});
|
||||
|
||||
@@ -237,8 +236,13 @@ for (const c in FIELDS) {
|
||||
return;
|
||||
}
|
||||
deepStrictEqual(isSquare(a), true);
|
||||
deepStrictEqual(Fp.equals(Fp.square(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(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);
|
||||
})
|
||||
);
|
||||
});
|
||||
@@ -247,7 +251,7 @@ for (const c in FIELDS) {
|
||||
deepStrictEqual(Fp.sqrt(Fp.ZERO), Fp.ZERO);
|
||||
const sqrt1 = Fp.sqrt(Fp.ONE);
|
||||
deepStrictEqual(
|
||||
Fp.equals(sqrt1, Fp.ONE) || Fp.equals(sqrt1, Fp.negate(Fp.ONE)),
|
||||
Fp.eql(sqrt1, Fp.ONE) || Fp.eql(sqrt1, Fp.neg(Fp.ONE)),
|
||||
true,
|
||||
'sqrt(1) = 1 or -1'
|
||||
);
|
||||
@@ -258,9 +262,12 @@ for (const c in FIELDS) {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (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, a), Fp.ONE);
|
||||
// FpDiv tests
|
||||
deepStrictEqual(mod.FpDiv(Fp, a, Fp.ONE), a);
|
||||
deepStrictEqual(mod.FpDiv(Fp, a, a), Fp.ONE);
|
||||
})
|
||||
);
|
||||
});
|
||||
@@ -269,6 +276,7 @@ for (const c in FIELDS) {
|
||||
fc.property(FC_BIGINT, (num) => {
|
||||
const a = create(num);
|
||||
deepStrictEqual(Fp.div(Fp.ZERO, a), Fp.ZERO);
|
||||
deepStrictEqual(mod.FpDiv(Fp, Fp.ZERO, a), Fp.ZERO);
|
||||
})
|
||||
);
|
||||
});
|
||||
@@ -279,6 +287,10 @@ for (const c in FIELDS) {
|
||||
const b = create(num2);
|
||||
const c = create(num3);
|
||||
deepStrictEqual(Fp.div(Fp.add(a, b), c), Fp.add(Fp.div(a, c), Fp.div(b, c)));
|
||||
deepStrictEqual(
|
||||
mod.FpDiv(Fp, Fp.add(a, b), c),
|
||||
Fp.add(mod.FpDiv(Fp, a, c), mod.FpDiv(Fp, b, c))
|
||||
);
|
||||
})
|
||||
);
|
||||
});
|
||||
@@ -287,7 +299,7 @@ for (const c in FIELDS) {
|
||||
fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => {
|
||||
const a = create(num1);
|
||||
const b = create(num2);
|
||||
deepStrictEqual(Fp.div(a, b), Fp.mul(a, Fp.invert(b)));
|
||||
deepStrictEqual(Fp.div(a, b), Fp.mul(a, Fp.inv(b)));
|
||||
})
|
||||
);
|
||||
});
|
||||
@@ -313,12 +325,12 @@ const NUM_RUNS = 5;
|
||||
const getXY = (p) => ({ x: p.x, y: p.y });
|
||||
|
||||
function equal(a, b, comment) {
|
||||
deepStrictEqual(a.equals(b), true, 'eq(${comment})');
|
||||
deepStrictEqual(a.equals(b), true, `eq(${comment})`);
|
||||
if (a.toAffine && b.toAffine) {
|
||||
deepStrictEqual(getXY(a.toAffine()), getXY(b.toAffine()), 'eqToAffine(${comment})');
|
||||
deepStrictEqual(getXY(a.toAffine()), getXY(b.toAffine()), `eqToAffine(${comment})`);
|
||||
} else if (!a.toAffine && !b.toAffine) {
|
||||
// Already affine
|
||||
deepStrictEqual(getXY(a), getXY(b), 'eqAffine(${comment})');
|
||||
deepStrictEqual(getXY(a), getXY(b), `eqAffine(${comment})`);
|
||||
} else throw new Error('Different point types');
|
||||
}
|
||||
|
||||
@@ -342,50 +354,50 @@ for (const name in CURVES) {
|
||||
if (!p) continue;
|
||||
|
||||
const G = [p.ZERO, p.BASE];
|
||||
for (let i = 2; i < 10; i++) G.push(G[1].multiply(i));
|
||||
for (let i = 2n; i < 10n; i++) G.push(G[1].multiply(i));
|
||||
const title = `${name}/${pointName}`;
|
||||
describe(title, () => {
|
||||
describe('basic group laws', () => {
|
||||
// Here we check basic group laws, to verify that points works as group
|
||||
should('(zero)', () => {
|
||||
should('zero', () => {
|
||||
equal(G[0].double(), G[0], '(0*G).double() = 0');
|
||||
equal(G[0].add(G[0]), G[0], '0*G + 0*G = 0');
|
||||
equal(G[0].subtract(G[0]), G[0], '0*G - 0*G = 0');
|
||||
equal(G[0].negate(), G[0], '-0 = 0');
|
||||
for (let i = 0; i < G.length; i++) {
|
||||
const p = G[i];
|
||||
equal(p, p.add(G[0]), '${i}*G + 0 = ${i}*G');
|
||||
equal(G[0].multiply(i + 1), G[0], '${i + 1}*0 = 0');
|
||||
equal(p, p.add(G[0]), `${i}*G + 0 = ${i}*G`);
|
||||
equal(G[0].multiply(BigInt(i + 1)), G[0], `${i + 1}*0 = 0`);
|
||||
}
|
||||
});
|
||||
should('(one)', () => {
|
||||
should('one', () => {
|
||||
equal(G[1].double(), G[2], '(1*G).double() = 2*G');
|
||||
equal(G[1].subtract(G[1]), G[0], '1*G - 1*G = 0');
|
||||
equal(G[1].add(G[1]), G[2], '1*G + 1*G = 2*G');
|
||||
});
|
||||
should('(sanity tests)', () => {
|
||||
should('sanity tests', () => {
|
||||
equal(G[2].double(), G[4], '(2*G).double() = 4*G');
|
||||
equal(G[2].add(G[2]), G[4], '2*G + 2*G = 4*G');
|
||||
equal(G[7].add(G[3].negate()), G[4], '7*G - 3*G = 4*G');
|
||||
});
|
||||
should('(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[2]).add(G[2]), '4*G + 3*G = 3*G + 2*G + 2*G');
|
||||
});
|
||||
should('(double)', () => {
|
||||
should('double', () => {
|
||||
equal(G[3].double(), G[6], '(3*G).double() = 6*G');
|
||||
});
|
||||
should('(multiply)', () => {
|
||||
equal(G[2].multiply(3), G[6], '(2*G).multiply(3) = 6*G');
|
||||
should('multiply', () => {
|
||||
equal(G[2].multiply(3n), G[6], '(2*G).multiply(3) = 6*G');
|
||||
});
|
||||
should('(same point addition)', () => {
|
||||
should('add same-point', () => {
|
||||
equal(G[3].add(G[3]), G[6], '3*G + 3*G = 6*G');
|
||||
});
|
||||
should('(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].subtract(G[3]), G[0], '3*G - 3*G = 0*G');
|
||||
});
|
||||
should('(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[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');
|
||||
@@ -393,7 +405,7 @@ for (const name in CURVES) {
|
||||
const carry = CURVE_ORDER % 2n === 1n ? G[1] : G[0];
|
||||
equal(G[1].multiply(half).double().add(carry), G[0], '((N/2) * G).double() = 0');
|
||||
});
|
||||
should('(inversion)', () => {
|
||||
should('inversion', () => {
|
||||
const a = 1234n;
|
||||
const b = 5678n;
|
||||
const c = a * b;
|
||||
@@ -401,7 +413,7 @@ for (const name in CURVES) {
|
||||
const inv = mod.invert(b, CURVE_ORDER);
|
||||
equal(G[1].multiply(c).multiply(inv), G[1].multiply(a), 'c*G * (1/b)*G = a*G');
|
||||
});
|
||||
should('(multiply, rand)', () =>
|
||||
should('multiply, rand', () =>
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, (a, b) => {
|
||||
const c = mod.mod(a + b, CURVE_ORDER);
|
||||
@@ -415,7 +427,7 @@ for (const name in CURVES) {
|
||||
{ numRuns: NUM_RUNS }
|
||||
)
|
||||
);
|
||||
should('(multiply2, rand)', () =>
|
||||
should('multiply2, rand', () =>
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, (a, b) => {
|
||||
const c = mod.mod(a * b, CURVE_ORDER);
|
||||
@@ -436,9 +448,16 @@ for (const name in CURVES) {
|
||||
throws(() => G[1][op](0n), '0n');
|
||||
G[1][op](G[2]);
|
||||
throws(() => G[1][op](CURVE_ORDER), 'CURVE_ORDER');
|
||||
throws(() => G[1][op](-123n), '-123n');
|
||||
throws(() => G[1][op](123), '123');
|
||||
throws(() => G[1][op](123.456), '123.456');
|
||||
throws(() => G[1][op](true), 'true');
|
||||
throws(() => G[1][op](false), 'false');
|
||||
throws(() => G[1][op](null), 'null');
|
||||
throws(() => G[1][op](undefined), 'undefined');
|
||||
throws(() => G[1][op]('1'), "'1'");
|
||||
throws(() => G[1][op]({ x: 1n, y: 1n }), '{ x: 1n, y: 1n }');
|
||||
throws(() => G[1][op]({ x: 1n, y: 1n, z: 1n }), '{ x: 1n, y: 1n, z: 1n }');
|
||||
throws(
|
||||
() => G[1][op]({ x: 1n, y: 1n, z: 1n, t: 1n }),
|
||||
'{ x: 1n, y: 1n, z: 1n, t: 1n }'
|
||||
@@ -447,8 +466,8 @@ for (const name in CURVES) {
|
||||
throws(() => G[1][op](new Uint8Array([0])), 'ui8a([0])');
|
||||
throws(() => G[1][op](new Uint8Array([1])), 'ui8a([1])');
|
||||
throws(() => G[1][op](new Uint8Array(4096).fill(1)), 'ui8a(4096*[1])');
|
||||
if (G[1].toAffine) throws(() => G[1][op](C.Point.BASE), 'Point ${op} ${pointName}');
|
||||
throws(() => G[1][op](o.BASE), '${op}/other curve point');
|
||||
// if (G[1].toAffine) throws(() => G[1][op](C.Point.BASE), `Point ${op} ${pointName}`);
|
||||
throws(() => G[1][op](o.BASE), `${op}/other curve point`);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -468,7 +487,7 @@ for (const name in CURVES) {
|
||||
throws(() => G[1].equals(new Uint8Array([0])), 'ui8a([0])');
|
||||
throws(() => G[1].equals(new Uint8Array([1])), 'ui8a([1])');
|
||||
throws(() => G[1].equals(new Uint8Array(4096).fill(1)), 'ui8a(4096*[1])');
|
||||
if (G[1].toAffine) throws(() => G[1].equals(C.Point.BASE), 'Point.equals(${pointName})');
|
||||
// if (G[1].toAffine) throws(() => G[1].equals(C.Point.BASE), 'Point.equals(${pointName})');
|
||||
throws(() => G[1].equals(o.BASE), 'other curve point');
|
||||
});
|
||||
|
||||
@@ -497,25 +516,39 @@ for (const name in CURVES) {
|
||||
});
|
||||
}
|
||||
// Complex point (Extended/Jacobian/Projective?)
|
||||
if (p.BASE.toAffine) {
|
||||
should('toAffine()', () => {
|
||||
equal(p.ZERO.toAffine(), C.Point.ZERO, '0 = 0');
|
||||
equal(p.BASE.toAffine(), C.Point.BASE, '1 = 1');
|
||||
});
|
||||
}
|
||||
if (p.fromAffine) {
|
||||
should('fromAffine()', () => {
|
||||
equal(p.ZERO, p.fromAffine(C.Point.ZERO), '0 = 0');
|
||||
equal(p.BASE, p.fromAffine(C.Point.BASE), '1 = 1');
|
||||
});
|
||||
}
|
||||
// if (p.BASE.toAffine && C.Point) {
|
||||
// should('toAffine()', () => {
|
||||
// equal(p.ZERO.toAffine(), C.Point.ZERO, '0 = 0');
|
||||
// equal(p.BASE.toAffine(), C.Point.BASE, '1 = 1');
|
||||
// });
|
||||
// }
|
||||
// if (p.fromAffine && C.Point) {
|
||||
// should('fromAffine()', () => {
|
||||
// equal(p.ZERO, p.fromAffine(C.Point.ZERO), '0 = 0');
|
||||
// equal(p.BASE, p.fromAffine(C.Point.BASE), '1 = 1');
|
||||
// });
|
||||
// }
|
||||
// toHex/fromHex (if available)
|
||||
if (p.fromHex && p.BASE.toHex) {
|
||||
should('fromHex(toHex()) roundtrip', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (x) => {
|
||||
const hex = p.BASE.multiply(x).toHex();
|
||||
const point = p.BASE.multiply(x);
|
||||
const hex = point.toHex();
|
||||
const bytes = point.toRawBytes();
|
||||
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);
|
||||
})
|
||||
);
|
||||
});
|
||||
@@ -524,20 +557,26 @@ for (const name in CURVES) {
|
||||
}
|
||||
describe(name, () => {
|
||||
// Generic complex things (getPublicKey/sign/verify/getSharedSecret)
|
||||
should('getPublicKey type check', () => {
|
||||
should('.getPublicKey() type check', () => {
|
||||
throws(() => C.getPublicKey(0), '0');
|
||||
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(true), 'true');
|
||||
throws(() => C.getPublicKey(false), 'false');
|
||||
throws(() => C.getPublicKey(null), 'null');
|
||||
throws(() => C.getPublicKey(undefined), 'undefined');
|
||||
throws(() => C.getPublicKey(''), "''");
|
||||
// NOTE: passes because of disabled hex padding checks for starknet, maybe enable?
|
||||
//throws(() => C.getPublicKey('1'), "'1'");
|
||||
// throws(() => C.getPublicKey('1'), "'1'");
|
||||
throws(() => C.getPublicKey('key'), "'key'");
|
||||
throws(() => C.getPublicKey({}));
|
||||
throws(() => C.getPublicKey(new Uint8Array([])));
|
||||
throws(() => C.getPublicKey(new Uint8Array([0])));
|
||||
throws(() => C.getPublicKey(new Uint8Array([1])));
|
||||
throws(() => C.getPublicKey(new Uint8Array(4096).fill(1)));
|
||||
throws(() => C.getPublicKey(Array(32).fill(1)));
|
||||
});
|
||||
should('.verify() should verify random signatures', () =>
|
||||
fc.assert(
|
||||
@@ -548,25 +587,96 @@ for (const name in CURVES) {
|
||||
deepStrictEqual(
|
||||
C.verify(sig, msg, pub),
|
||||
true,
|
||||
'priv=${toHex(priv)},pub=${toHex(pub)},msg=${msg}'
|
||||
`priv=${toHex(priv)},pub=${toHex(pub)},msg=${msg}`
|
||||
);
|
||||
}),
|
||||
{ numRuns: NUM_RUNS }
|
||||
)
|
||||
);
|
||||
should('.verify() should verify empty signatures', () => {
|
||||
const msg = new Uint8Array([]);
|
||||
const priv = C.utils.randomPrivateKey();
|
||||
const pub = C.getPublicKey(priv);
|
||||
const sig = C.sign(msg, priv);
|
||||
deepStrictEqual(
|
||||
C.verify(sig, msg, pub),
|
||||
true,
|
||||
'priv=${toHex(priv)},pub=${toHex(pub)},msg=${msg}'
|
||||
);
|
||||
});
|
||||
should('.sign() edge cases', () => {
|
||||
throws(() => C.sign());
|
||||
throws(() => C.sign(''));
|
||||
throws(() => C.sign('', ''));
|
||||
throws(() => C.sign(new Uint8Array(), new Uint8Array()));
|
||||
});
|
||||
|
||||
should('.verify() should not verify signature with wrong hash', () => {
|
||||
const MSG = '01'.repeat(32);
|
||||
const PRIV_KEY = 0x2n;
|
||||
const WRONG_MSG = '11'.repeat(32);
|
||||
const signature = C.sign(MSG, PRIV_KEY);
|
||||
const publicKey = C.getPublicKey(PRIV_KEY);
|
||||
deepStrictEqual(C.verify(signature, WRONG_MSG, publicKey), false);
|
||||
describe('verify()', () => {
|
||||
const msg = '01'.repeat(32);
|
||||
should('true for proper signatures', () => {
|
||||
const priv = C.utils.randomPrivateKey();
|
||||
const sig = C.sign(msg, priv);
|
||||
const pub = C.getPublicKey(priv);
|
||||
deepStrictEqual(C.verify(sig, msg, pub), true);
|
||||
});
|
||||
should('false for wrong messages', () => {
|
||||
const priv = C.utils.randomPrivateKey();
|
||||
const sig = C.sign(msg, priv);
|
||||
const pub = C.getPublicKey(priv);
|
||||
deepStrictEqual(C.verify(sig, '11'.repeat(32), pub), false);
|
||||
});
|
||||
should('false for wrong keys', () => {
|
||||
const priv = C.utils.randomPrivateKey();
|
||||
const sig = C.sign(msg, priv);
|
||||
deepStrictEqual(C.verify(sig, msg, C.getPublicKey(C.utils.randomPrivateKey())), false);
|
||||
});
|
||||
});
|
||||
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,
|
||||
// need to check what other implementations do. Empty message != new Uint8Array([0]), but what scalar should be in that case?
|
||||
// should('should not verify signature with wrong message', () => {
|
||||
@@ -618,10 +728,20 @@ should('secp224k1 sqrt bug', () => {
|
||||
23621584063597419797792593680131996961517196803742576047493035507225n
|
||||
);
|
||||
deepStrictEqual(
|
||||
Fp.negate(sqrtMinus1),
|
||||
Fp.neg(sqrtMinus1),
|
||||
3338362603553219996874421406887633712040719456283732096017030791656n
|
||||
);
|
||||
deepStrictEqual(Fp.square(sqrtMinus1), Fp.create(-1n));
|
||||
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.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
290
test/ed25519-addons.test.js
Normal file
290
test/ed25519-addons.test.js
Normal file
@@ -0,0 +1,290 @@
|
||||
import { sha512 } from '@noble/hashes/sha512';
|
||||
import { hexToBytes, bytesToHex, randomBytes } from '@noble/hashes/utils';
|
||||
import { deepStrictEqual, strictEqual, throws } from 'assert';
|
||||
import { describe, should } from 'micro-should';
|
||||
import { numberToBytesLE } from '../esm/abstract/utils.js';
|
||||
import { default as x25519vectors } from './wycheproof/x25519_test.json' assert { type: 'json' };
|
||||
import { ed25519ctx, ed25519ph, RistrettoPoint, x25519 } from '../esm/ed25519.js';
|
||||
|
||||
// const ed = ed25519;
|
||||
const hex = bytesToHex;
|
||||
// const Point = ed.ExtendedPoint;
|
||||
|
||||
const VECTORS_RFC8032_CTX = [
|
||||
{
|
||||
secretKey: '0305334e381af78f141cb666f6199f57bc3495335a256a95bd2a55bf546663f6',
|
||||
publicKey: 'dfc9425e4f968f7f0c29f0259cf5f9aed6851c2bb4ad8bfb860cfee0ab248292',
|
||||
message: 'f726936d19c800494e3fdaff20b276a8',
|
||||
context: '666f6f',
|
||||
signature:
|
||||
'55a4cc2f70a54e04288c5f4cd1e45a7b' +
|
||||
'b520b36292911876cada7323198dd87a' +
|
||||
'8b36950b95130022907a7fb7c4e9b2d5' +
|
||||
'f6cca685a587b4b21f4b888e4e7edb0d',
|
||||
},
|
||||
{
|
||||
secretKey: '0305334e381af78f141cb666f6199f57bc3495335a256a95bd2a55bf546663f6',
|
||||
publicKey: 'dfc9425e4f968f7f0c29f0259cf5f9aed6851c2bb4ad8bfb860cfee0ab248292',
|
||||
message: 'f726936d19c800494e3fdaff20b276a8',
|
||||
context: '626172',
|
||||
signature:
|
||||
'fc60d5872fc46b3aa69f8b5b4351d580' +
|
||||
'8f92bcc044606db097abab6dbcb1aee3' +
|
||||
'216c48e8b3b66431b5b186d1d28f8ee1' +
|
||||
'5a5ca2df6668346291c2043d4eb3e90d',
|
||||
},
|
||||
{
|
||||
secretKey: '0305334e381af78f141cb666f6199f57bc3495335a256a95bd2a55bf546663f6',
|
||||
publicKey: 'dfc9425e4f968f7f0c29f0259cf5f9aed6851c2bb4ad8bfb860cfee0ab248292',
|
||||
message: '508e9e6882b979fea900f62adceaca35',
|
||||
context: '666f6f',
|
||||
signature:
|
||||
'8b70c1cc8310e1de20ac53ce28ae6e72' +
|
||||
'07f33c3295e03bb5c0732a1d20dc6490' +
|
||||
'8922a8b052cf99b7c4fe107a5abb5b2c' +
|
||||
'4085ae75890d02df26269d8945f84b0b',
|
||||
},
|
||||
{
|
||||
secretKey: 'ab9c2853ce297ddab85c993b3ae14bcad39b2c682beabc27d6d4eb20711d6560',
|
||||
publicKey: '0f1d1274943b91415889152e893d80e93275a1fc0b65fd71b4b0dda10ad7d772',
|
||||
message: 'f726936d19c800494e3fdaff20b276a8',
|
||||
context: '666f6f',
|
||||
signature:
|
||||
'21655b5f1aa965996b3f97b3c849eafb' +
|
||||
'a922a0a62992f73b3d1b73106a84ad85' +
|
||||
'e9b86a7b6005ea868337ff2d20a7f5fb' +
|
||||
'd4cd10b0be49a68da2b2e0dc0ad8960f',
|
||||
},
|
||||
];
|
||||
|
||||
describe('RFC8032ctx', () => {
|
||||
for (let i = 0; i < VECTORS_RFC8032_CTX.length; i++) {
|
||||
const v = VECTORS_RFC8032_CTX[i];
|
||||
should(`${i}`, () => {
|
||||
deepStrictEqual(hex(ed25519ctx.getPublicKey(v.secretKey)), v.publicKey);
|
||||
deepStrictEqual(hex(ed25519ctx.sign(v.message, v.secretKey, v.context)), v.signature);
|
||||
deepStrictEqual(ed25519ctx.verify(v.signature, v.message, v.publicKey, v.context), true);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const VECTORS_RFC8032_PH = [
|
||||
{
|
||||
secretKey: '833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42',
|
||||
publicKey: 'ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf',
|
||||
message: '616263',
|
||||
signature:
|
||||
'98a70222f0b8121aa9d30f813d683f80' +
|
||||
'9e462b469c7ff87639499bb94e6dae41' +
|
||||
'31f85042463c2a355a2003d062adf5aa' +
|
||||
'a10b8c61e636062aaad11c2a26083406',
|
||||
},
|
||||
];
|
||||
|
||||
describe('RFC8032ph', () => {
|
||||
for (let i = 0; i < VECTORS_RFC8032_PH.length; i++) {
|
||||
const v = VECTORS_RFC8032_PH[i];
|
||||
should(`${i}`, () => {
|
||||
deepStrictEqual(hex(ed25519ph.getPublicKey(v.secretKey)), v.publicKey);
|
||||
deepStrictEqual(hex(ed25519ph.sign(v.message, v.secretKey)), v.signature);
|
||||
deepStrictEqual(ed25519ph.verify(v.signature, v.message, v.publicKey), true);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// x25519
|
||||
should('X25519 base point', () => {
|
||||
const { y } = ed25519ph.ExtendedPoint.BASE;
|
||||
const { Fp } = ed25519ph.CURVE;
|
||||
const u = Fp.create((y + 1n) * Fp.inv(1n - y));
|
||||
deepStrictEqual(numberToBytesLE(u, 32), x25519.GuBytes);
|
||||
});
|
||||
|
||||
describe('RFC7748', () => {
|
||||
const rfc7748Mul = [
|
||||
{
|
||||
scalar: 'a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4',
|
||||
u: 'e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c',
|
||||
outputU: 'c3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552',
|
||||
},
|
||||
{
|
||||
scalar: '4b66e9d4d1b4673c5ad22691957d6af5c11b6421e0ea01d42ca4169e7918ba0d',
|
||||
u: 'e5210f12786811d3f4b7959d0538ae2c31dbe7106fc03c3efc4cd549c715a493',
|
||||
outputU: '95cbde9476e8907d7aade45cb4b873f88b595a68799fa152e6f8f7647aac7957',
|
||||
},
|
||||
];
|
||||
for (let i = 0; i < rfc7748Mul.length; i++) {
|
||||
const v = rfc7748Mul[i];
|
||||
should(`scalarMult (${i})`, () => {
|
||||
deepStrictEqual(hex(x25519.scalarMult(v.scalar, v.u)), v.outputU);
|
||||
});
|
||||
}
|
||||
|
||||
const rfc7748Iter = [
|
||||
{ scalar: '422c8e7a6227d7bca1350b3e2bb7279f7897b87bb6854b783c60e80311ae3079', iters: 1 },
|
||||
{ scalar: '684cf59ba83309552800ef566f2f4d3c1c3887c49360e3875f2eb94d99532c51', iters: 1000 },
|
||||
// { scalar: '7c3911e0ab2586fd864497297e575e6f3bc601c0883c30df5f4dd2d24f665424', iters: 1000000 },
|
||||
];
|
||||
for (let i = 0; i < rfc7748Iter.length; i++) {
|
||||
const { scalar, iters } = rfc7748Iter[i];
|
||||
should(`scalarMult iteration (${i})`, () => {
|
||||
let k = x25519.GuBytes;
|
||||
for (let i = 0, u = k; i < iters; i++) [k, u] = [x25519.scalarMult(k, u), k];
|
||||
deepStrictEqual(hex(k), scalar);
|
||||
});
|
||||
}
|
||||
|
||||
should('getSharedKey', () => {
|
||||
const alicePrivate = '77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a';
|
||||
const alicePublic = '8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a';
|
||||
const bobPrivate = '5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb';
|
||||
const bobPublic = 'de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f';
|
||||
const shared = '4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742';
|
||||
deepStrictEqual(alicePublic, hex(x25519.getPublicKey(alicePrivate)));
|
||||
deepStrictEqual(bobPublic, hex(x25519.getPublicKey(bobPrivate)));
|
||||
deepStrictEqual(hex(x25519.scalarMult(alicePrivate, bobPublic)), shared);
|
||||
deepStrictEqual(hex(x25519.scalarMult(bobPrivate, alicePublic)), shared);
|
||||
});
|
||||
});
|
||||
describe('Wycheproof', () => {
|
||||
const group = x25519vectors.testGroups[0];
|
||||
should(`X25519`, () => {
|
||||
for (let i = 0; i < group.tests.length; i++) {
|
||||
const v = group.tests[i];
|
||||
const comment = `(${i}, ${v.result}) ${v.comment}`;
|
||||
if (v.result === 'valid' || v.result === 'acceptable') {
|
||||
try {
|
||||
const shared = hex(x25519.scalarMult(v.private, v.public));
|
||||
deepStrictEqual(shared, v.shared, comment);
|
||||
} catch (e) {
|
||||
// We are more strict
|
||||
if (e.message.includes('Expected valid scalar')) return;
|
||||
if (e.message.includes('Invalid private or public key received')) return;
|
||||
throw e;
|
||||
}
|
||||
} else if (v.result === 'invalid') {
|
||||
let failed = false;
|
||||
try {
|
||||
x25519.scalarMult(v.private, v.public);
|
||||
} catch (error) {
|
||||
failed = true;
|
||||
}
|
||||
deepStrictEqual(failed, true, comment);
|
||||
} else throw new Error('unknown test result');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function utf8ToBytes(str) {
|
||||
if (typeof str !== 'string') {
|
||||
throw new Error(`utf8ToBytes expected string, got ${typeof str}`);
|
||||
}
|
||||
return new TextEncoder().encode(str);
|
||||
}
|
||||
|
||||
describe('ristretto255', () => {
|
||||
should('follow the byte encodings of small multiples', () => {
|
||||
const encodingsOfSmallMultiples = [
|
||||
// This is the identity point
|
||||
'0000000000000000000000000000000000000000000000000000000000000000',
|
||||
// This is the basepoint
|
||||
'e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76',
|
||||
// These are small multiples of the basepoint
|
||||
'6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919',
|
||||
'94741f5d5d52755ece4f23f044ee27d5d1ea1e2bd196b462166b16152a9d0259',
|
||||
'da80862773358b466ffadfe0b3293ab3d9fd53c5ea6c955358f568322daf6a57',
|
||||
'e882b131016b52c1d3337080187cf768423efccbb517bb495ab812c4160ff44e',
|
||||
'f64746d3c92b13050ed8d80236a7f0007c3b3f962f5ba793d19a601ebb1df403',
|
||||
'44f53520926ec81fbd5a387845beb7df85a96a24ece18738bdcfa6a7822a176d',
|
||||
'903293d8f2287ebe10e2374dc1a53e0bc887e592699f02d077d5263cdd55601c',
|
||||
'02622ace8f7303a31cafc63f8fc48fdc16e1c8c8d234b2f0d6685282a9076031',
|
||||
'20706fd788b2720a1ed2a5dad4952b01f413bcf0e7564de8cdc816689e2db95f',
|
||||
'bce83f8ba5dd2fa572864c24ba1810f9522bc6004afe95877ac73241cafdab42',
|
||||
'e4549ee16b9aa03099ca208c67adafcafa4c3f3e4e5303de6026e3ca8ff84460',
|
||||
'aa52e000df2e16f55fb1032fc33bc42742dad6bd5a8fc0be0167436c5948501f',
|
||||
'46376b80f409b29dc2b5f6f0c52591990896e5716f41477cd30085ab7f10301e',
|
||||
'e0c418f7c8d9c4cdd7395b93ea124f3ad99021bb681dfc3302a9d99a2e53e64e',
|
||||
];
|
||||
let B = RistrettoPoint.BASE;
|
||||
let P = RistrettoPoint.ZERO;
|
||||
for (const encoded of encodingsOfSmallMultiples) {
|
||||
deepStrictEqual(P.toHex(), encoded);
|
||||
deepStrictEqual(RistrettoPoint.fromHex(encoded).toHex(), encoded);
|
||||
P = P.add(B);
|
||||
}
|
||||
});
|
||||
should('not convert bad bytes encoding', () => {
|
||||
const badEncodings = [
|
||||
// These are all bad because they're non-canonical field encodings.
|
||||
'00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
|
||||
'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f',
|
||||
'f3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f',
|
||||
'edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f',
|
||||
// These are all bad because they're negative field elements.
|
||||
'0100000000000000000000000000000000000000000000000000000000000000',
|
||||
'01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f',
|
||||
'ed57ffd8c914fb201471d1c3d245ce3c746fcbe63a3679d51b6a516ebebe0e20',
|
||||
'c34c4e1826e5d403b78e246e88aa051c36ccf0aafebffe137d148a2bf9104562',
|
||||
'c940e5a4404157cfb1628b108db051a8d439e1a421394ec4ebccb9ec92a8ac78',
|
||||
'47cfc5497c53dc8e61c91d17fd626ffb1c49e2bca94eed052281b510b1117a24',
|
||||
'f1c6165d33367351b0da8f6e4511010c68174a03b6581212c71c0e1d026c3c72',
|
||||
'87260f7a2f12495118360f02c26a470f450dadf34a413d21042b43b9d93e1309',
|
||||
// These are all bad because they give a nonsquare x².
|
||||
'26948d35ca62e643e26a83177332e6b6afeb9d08e4268b650f1f5bbd8d81d371',
|
||||
'4eac077a713c57b4f4397629a4145982c661f48044dd3f96427d40b147d9742f',
|
||||
'de6a7b00deadc788eb6b6c8d20c0ae96c2f2019078fa604fee5b87d6e989ad7b',
|
||||
'bcab477be20861e01e4a0e295284146a510150d9817763caf1a6f4b422d67042',
|
||||
'2a292df7e32cababbd9de088d1d1abec9fc0440f637ed2fba145094dc14bea08',
|
||||
'f4a9e534fc0d216c44b218fa0c42d99635a0127ee2e53c712f70609649fdff22',
|
||||
'8268436f8c4126196cf64b3c7ddbda90746a378625f9813dd9b8457077256731',
|
||||
'2810e5cbc2cc4d4eece54f61c6f69758e289aa7ab440b3cbeaa21995c2f4232b',
|
||||
// These are all bad because they give a negative xy value.
|
||||
'3eb858e78f5a7254d8c9731174a94f76755fd3941c0ac93735c07ba14579630e',
|
||||
'a45fdc55c76448c049a1ab33f17023edfb2be3581e9c7aade8a6125215e04220',
|
||||
'd483fe813c6ba647ebbfd3ec41adca1c6130c2beeee9d9bf065c8d151c5f396e',
|
||||
'8a2e1d30050198c65a54483123960ccc38aef6848e1ec8f5f780e8523769ba32',
|
||||
'32888462f8b486c68ad7dd9610be5192bbeaf3b443951ac1a8118419d9fa097b',
|
||||
'227142501b9d4355ccba290404bde41575b037693cef1f438c47f8fbf35d1165',
|
||||
'5c37cc491da847cfeb9281d407efc41e15144c876e0170b499a96a22ed31e01e',
|
||||
'445425117cb8c90edcbc7c1cc0e74f747f2c1efa5630a967c64f287792a48a4b',
|
||||
// This is s = -1, which causes y = 0.
|
||||
'ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f',
|
||||
];
|
||||
for (const badBytes of badEncodings) {
|
||||
const b = hexToBytes(badBytes);
|
||||
throws(() => RistrettoPoint.fromHex(b), badBytes);
|
||||
}
|
||||
});
|
||||
should('create right points from uniform hash', () => {
|
||||
const labels = [
|
||||
'Ristretto is traditionally a short shot of espresso coffee',
|
||||
'made with the normal amount of ground coffee but extracted with',
|
||||
'about half the amount of water in the same amount of time',
|
||||
'by using a finer grind.',
|
||||
'This produces a concentrated shot of coffee per volume.',
|
||||
'Just pulling a normal shot short will produce a weaker shot',
|
||||
'and is not a Ristretto as some believe.',
|
||||
];
|
||||
const encodedHashToPoints = [
|
||||
'3066f82a1a747d45120d1740f14358531a8f04bbffe6a819f86dfe50f44a0a46',
|
||||
'f26e5b6f7d362d2d2a94c5d0e7602cb4773c95a2e5c31a64f133189fa76ed61b',
|
||||
'006ccd2a9e6867e6a2c5cea83d3302cc9de128dd2a9a57dd8ee7b9d7ffe02826',
|
||||
'f8f0c87cf237953c5890aec3998169005dae3eca1fbb04548c635953c817f92a',
|
||||
'ae81e7dedf20a497e10c304a765c1767a42d6e06029758d2d7e8ef7cc4c41179',
|
||||
'e2705652ff9f5e44d3e841bf1c251cf7dddb77d140870d1ab2ed64f1a9ce8628',
|
||||
'80bd07262511cdde4863f8a7434cef696750681cb9510eea557088f76d9e5065',
|
||||
];
|
||||
|
||||
for (let i = 0; i < labels.length; i++) {
|
||||
const hash = sha512(utf8ToBytes(labels[i]));
|
||||
const point = RistrettoPoint.hashToCurve(hash);
|
||||
deepStrictEqual(point.toHex(), encodedHashToPoints[i]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// ESM is broken.
|
||||
import url from 'url';
|
||||
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
|
||||
should.run();
|
||||
}
|
||||
1
test/ed25519.helpers.js
Normal file
1
test/ed25519.helpers.js
Normal file
@@ -0,0 +1 @@
|
||||
export { ed25519, ED25519_TORSION_SUBGROUP } from '../esm/ed25519.js';
|
||||
@@ -1,25 +1,16 @@
|
||||
import { deepEqual, deepStrictEqual, strictEqual, throws } from 'assert';
|
||||
import { describe, should } from 'micro-should';
|
||||
import * as fc from 'fast-check';
|
||||
import {
|
||||
ed25519,
|
||||
ed25519ctx,
|
||||
ed25519ph,
|
||||
x25519,
|
||||
RistrettoPoint,
|
||||
ED25519_TORSION_SUBGROUP,
|
||||
} from '../lib/esm/ed25519.js';
|
||||
import { deepStrictEqual, strictEqual, throws } from 'assert';
|
||||
import { readFileSync } from 'fs';
|
||||
import { default as zip215 } from './ed25519/zip215.json' assert { type: 'json' };
|
||||
import { hexToBytes, bytesToHex, randomBytes } from '@noble/hashes/utils';
|
||||
import { numberToBytesLE } from '../lib/esm/abstract/utils.js';
|
||||
import { sha512 } from '@noble/hashes/sha512';
|
||||
import * as fc from 'fast-check';
|
||||
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 x25519vectors } from './wycheproof/x25519_test.json' assert { type: 'json' };
|
||||
import { default as zip215 } from './ed25519/zip215.json' assert { type: 'json' };
|
||||
|
||||
describe('ed25519', () => {
|
||||
const ed = ed25519;
|
||||
const hex = bytesToHex;
|
||||
const Point = ed.ExtendedPoint;
|
||||
|
||||
function to32Bytes(numOrStr) {
|
||||
let hex = typeof numOrStr === 'string' ? numOrStr : numOrStr.toString(16);
|
||||
@@ -28,7 +19,7 @@ describe('ed25519', () => {
|
||||
|
||||
function utf8ToBytes(str) {
|
||||
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);
|
||||
}
|
||||
@@ -78,6 +69,7 @@ describe('ed25519', () => {
|
||||
);
|
||||
});
|
||||
const privKey = to32Bytes('a665a45920422f9d417e4867ef');
|
||||
const wrongPriv = to32Bytes('a675a45920422f9d417e4867ef');
|
||||
const msg = hexToBytes('874f9960c5d2b7a9b5fad383e1ba44719ebb743a');
|
||||
const wrongMsg = hexToBytes('589d8c7f1da0a24bc07b7381ad48b1cfc211af1c');
|
||||
describe('basic methods', () => {
|
||||
@@ -86,16 +78,6 @@ describe('ed25519', () => {
|
||||
const signature = ed.sign(msg, privKey);
|
||||
deepStrictEqual(ed.verify(signature, msg, publicKey), true);
|
||||
});
|
||||
should('not verify signature with wrong public key', () => {
|
||||
const publicKey = ed.getPublicKey(12);
|
||||
const signature = ed.sign(msg, privKey);
|
||||
deepStrictEqual(ed.verify(signature, msg, publicKey), false);
|
||||
});
|
||||
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);
|
||||
});
|
||||
});
|
||||
describe('sync methods', () => {
|
||||
should('sign and verify', () => {
|
||||
@@ -104,7 +86,7 @@ describe('ed25519', () => {
|
||||
deepStrictEqual(ed.verify(signature, msg, publicKey), true);
|
||||
});
|
||||
should('not verify signature with wrong public key', () => {
|
||||
const publicKey = ed.getPublicKey(12);
|
||||
const publicKey = ed.getPublicKey(wrongPriv);
|
||||
const signature = ed.sign(msg, privKey);
|
||||
deepStrictEqual(ed.verify(signature, msg, publicKey), false);
|
||||
});
|
||||
@@ -114,56 +96,46 @@ describe('ed25519', () => {
|
||||
deepStrictEqual(ed.verify(signature, wrongMsg, publicKey), false);
|
||||
});
|
||||
});
|
||||
describe('BASE_POINT.multiply()', () => {
|
||||
// https://xmr.llcoins.net/addresstests.html
|
||||
should(
|
||||
'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 =
|
||||
ed.Point.BASE.multiply(0x90af56259a4b6bfbc4337980d5d75fbe3c074630368ff3804d33028e5dbfa77n);
|
||||
Point.BASE.multiply(0x90af56259a4b6bfbc4337980d5d75fbe3c074630368ff3804d33028e5dbfa77n);
|
||||
deepStrictEqual(
|
||||
publicKey.toHex(),
|
||||
'0f3b913371411b27e646b537e888f685bf929ea7aab93c950ed84433f064480d'
|
||||
);
|
||||
}
|
||||
);
|
||||
should(
|
||||
'ed25519/BASE_POINT.multiply()/should create right publicKey without SHA-512 hashing TEST 2',
|
||||
() => {
|
||||
});
|
||||
should('create right publicKey without SHA-512 hashing TEST 2', () => {
|
||||
const publicKey =
|
||||
ed.Point.BASE.multiply(0x364e8711a60780382a5d57b061c126f039940f28a9e91fe039d4d3094d8b88n);
|
||||
Point.BASE.multiply(0x364e8711a60780382a5d57b061c126f039940f28a9e91fe039d4d3094d8b88n);
|
||||
deepStrictEqual(
|
||||
publicKey.toHex(),
|
||||
'ad545340b58610f0cd62f17d55af1ab11ecde9c084d5476865ddb4dbda015349'
|
||||
);
|
||||
}
|
||||
);
|
||||
should(
|
||||
'ed25519/BASE_POINT.multiply()/should create right publicKey without SHA-512 hashing TEST 3',
|
||||
() => {
|
||||
});
|
||||
should('create right publicKey without SHA-512 hashing TEST 3', () => {
|
||||
const publicKey =
|
||||
ed.Point.BASE.multiply(0xb9bf90ff3abec042752cac3a07a62f0c16cfb9d32a3fc2305d676ec2d86e941n);
|
||||
Point.BASE.multiply(0xb9bf90ff3abec042752cac3a07a62f0c16cfb9d32a3fc2305d676ec2d86e941n);
|
||||
deepStrictEqual(
|
||||
publicKey.toHex(),
|
||||
'e097c4415fe85724d522b2e449e8fd78dd40d20097bdc9ae36fe8ec6fe12cb8c'
|
||||
);
|
||||
}
|
||||
);
|
||||
should(
|
||||
'ed25519/BASE_POINT.multiply()/should create right publicKey without SHA-512 hashing TEST 4',
|
||||
() => {
|
||||
});
|
||||
should('create right publicKey without SHA-512 hashing TEST 4', () => {
|
||||
const publicKey =
|
||||
ed.Point.BASE.multiply(0x69d896f02d79524c9878e080308180e2859d07f9f54454e0800e8db0847a46en);
|
||||
Point.BASE.multiply(0x69d896f02d79524c9878e080308180e2859d07f9f54454e0800e8db0847a46en);
|
||||
deepStrictEqual(
|
||||
publicKey.toHex(),
|
||||
'f12cb7c43b59971395926f278ce7c2eaded9444fbce62ca717564cb508a0db1d'
|
||||
);
|
||||
}
|
||||
);
|
||||
should('ed25519/BASE_POINT.multiply()/should throw Point#multiply on TEST 5', () => {
|
||||
});
|
||||
should('throw Point#multiply on TEST 5', () => {
|
||||
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.input
|
||||
@@ -184,7 +156,7 @@ describe('ed25519', () => {
|
||||
// Calculate
|
||||
const pub = ed.getPublicKey(to32Bytes(priv));
|
||||
deepStrictEqual(hex(pub), expectedPub);
|
||||
deepStrictEqual(pub, ed.Point.fromHex(pub).toRawBytes());
|
||||
deepStrictEqual(pub, Point.fromHex(pub).toRawBytes());
|
||||
|
||||
const signature = hex(ed.sign(msg, priv));
|
||||
// console.log('vector', i);
|
||||
@@ -310,104 +282,6 @@ describe('ed25519', () => {
|
||||
// // const signature = await ristretto25519.sign(MESSAGE, PRIVATE_KEY);
|
||||
// // 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++) {
|
||||
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();
|
||||
@@ -440,58 +314,14 @@ describe('ed25519', () => {
|
||||
} catch (e) {
|
||||
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);
|
||||
// sig.R = BASE, sig.s = N+1
|
||||
const sig =
|
||||
'5866666666666666666666666666666666666666666666666666666666666666eed3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010';
|
||||
throws(() => ed.verify(sig, 'deadbeef', Point.BASE));
|
||||
});
|
||||
|
||||
// should('X25519/getSharedSecret() should be commutative', () => {
|
||||
@@ -511,40 +341,11 @@ describe('ed25519', () => {
|
||||
|
||||
// should('X25519: should convert base point to montgomery using fromPoint', () => {
|
||||
// deepStrictEqual(
|
||||
// hex(ed.montgomeryCurve.UfromPoint(ed.Point.BASE)),
|
||||
// hex(ed.montgomeryCurve.UfromPoint(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(`Wycheproof/ED25519`, () => {
|
||||
for (let g = 0; g < ed25519vectors.testGroups.length; g++) {
|
||||
const group = ed25519vectors.testGroups[g];
|
||||
@@ -576,95 +377,10 @@ describe('ed25519', () => {
|
||||
deepStrictEqual(ed.verify(signature, message, publicKey), true);
|
||||
});
|
||||
|
||||
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',
|
||||
},
|
||||
];
|
||||
|
||||
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 { Fp } = ed25519.CURVE;
|
||||
const u = Fp.create((y + 1n) * Fp.invert(1n - y));
|
||||
deepStrictEqual(hex(numberToBytesLE(u, 32)), x25519.Gu);
|
||||
});
|
||||
|
||||
should('isTorsionFree()', () => {
|
||||
const orig = ed.utils.getExtendedPublicKey(ed.utils.randomPrivateKey()).point;
|
||||
for (const hex of ED25519_TORSION_SUBGROUP.slice(1)) {
|
||||
const dirty = orig.add(ed.Point.fromHex(hex));
|
||||
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}`);
|
||||
@@ -673,6 +389,15 @@ describe('ed25519', () => {
|
||||
});
|
||||
});
|
||||
|
||||
should('ed25519 bug', () => {
|
||||
const t = 81718630521762619991978402609047527194981150691135404693881672112315521837062n;
|
||||
const point = ed25519.ExtendedPoint.fromAffine({ x: t, y: t });
|
||||
throws(() => point.assertValidity());
|
||||
// Otherwise (without assertValidity):
|
||||
// const point2 = point.double();
|
||||
// point2.toAffine(); // crash!
|
||||
});
|
||||
|
||||
// ESM is broken.
|
||||
import url from 'url';
|
||||
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { deepStrictEqual, throws } from 'assert';
|
||||
import { describe, should } from 'micro-should';
|
||||
import * as fc from 'fast-check';
|
||||
import { ed448, ed448ph, x448 } from '../lib/esm/ed448.js';
|
||||
import { ed448, ed448ph, x448 } from '../esm/ed448.js';
|
||||
import { hexToBytes, bytesToHex, randomBytes } from '@noble/hashes/utils';
|
||||
import { numberToBytesLE } from '../lib/esm/abstract/utils.js';
|
||||
import { numberToBytesLE } from '../esm/abstract/utils.js';
|
||||
import { default as ed448vectors } from './wycheproof/ed448_test.json' assert { type: 'json' };
|
||||
import { default as x448vectors } from './wycheproof/x448_test.json' assert { type: 'json' };
|
||||
|
||||
@@ -11,9 +11,10 @@ describe('ed448', () => {
|
||||
const ed = ed448;
|
||||
const hex = bytesToHex;
|
||||
ed.utils.precompute(4);
|
||||
const Point = ed.ExtendedPoint;
|
||||
|
||||
should(`Basic`, () => {
|
||||
const G1 = ed.Point.BASE;
|
||||
const G1 = Point.BASE.toAffine();
|
||||
deepStrictEqual(
|
||||
G1.x,
|
||||
224580040295924300187604334099896036246789641632564134246125461686950415467406032909029192869357953282578032075146446173674602635247710n
|
||||
@@ -22,7 +23,7 @@ describe('ed448', () => {
|
||||
G1.y,
|
||||
298819210078481492676017930443930673437544040154080242095928241372331506189835876003536878655418784733982303233503462500531545062832660n
|
||||
);
|
||||
const G2 = ed.Point.BASE.multiply(2n);
|
||||
const G2 = Point.BASE.multiply(2n).toAffine();
|
||||
deepStrictEqual(
|
||||
G2.x,
|
||||
484559149530404593699549205258669689569094240458212040187660132787056912146709081364401144455726350866276831544947397859048262938744149n
|
||||
@@ -31,7 +32,7 @@ describe('ed448', () => {
|
||||
G2.y,
|
||||
494088759867433727674302672526735089350544552303727723746126484473087719117037293890093462157703888342865036477787453078312060500281069n
|
||||
);
|
||||
const G3 = ed.Point.BASE.multiply(3n);
|
||||
const G3 = Point.BASE.multiply(3n).toAffine();
|
||||
deepStrictEqual(
|
||||
G3.x,
|
||||
23839778817283171003887799738662344287085130522697782688245073320169861206004018274567429238677677920280078599146891901463786155880335n
|
||||
@@ -43,12 +44,12 @@ describe('ed448', () => {
|
||||
});
|
||||
|
||||
should('Basic/decompress', () => {
|
||||
const G1 = ed.Point.BASE;
|
||||
const G2 = ed.Point.BASE.multiply(2n);
|
||||
const G3 = ed.Point.BASE.multiply(3n);
|
||||
const G1 = Point.BASE;
|
||||
const G2 = Point.BASE.multiply(2n);
|
||||
const G3 = Point.BASE.multiply(3n);
|
||||
const points = [G1, G2, G3];
|
||||
const getXY = (p) => ({ x: p.x, y: p.y });
|
||||
for (const p of points) deepStrictEqual(getXY(ed.Point.fromHex(p.toHex())), getXY(p));
|
||||
const getXY = (p) => p.toAffine();
|
||||
for (const p of points) deepStrictEqual(getXY(Point.fromHex(p.toHex())), getXY(p));
|
||||
});
|
||||
|
||||
const VECTORS_RFC8032 = [
|
||||
@@ -315,16 +316,18 @@ describe('ed448', () => {
|
||||
},
|
||||
];
|
||||
|
||||
describe('RFC8032', () => {
|
||||
for (let i = 0; i < VECTORS_RFC8032.length; i++) {
|
||||
const v = VECTORS_RFC8032[i];
|
||||
should(`RFC8032/${i}`, () => {
|
||||
should(`${i}`, () => {
|
||||
deepStrictEqual(hex(ed.getPublicKey(v.secretKey)), v.publicKey);
|
||||
deepStrictEqual(hex(ed.sign(v.message, v.secretKey)), v.signature);
|
||||
deepStrictEqual(ed.verify(v.signature, v.message, v.publicKey), true);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
should('not accept >57byte private keys', async () => {
|
||||
should('not accept >57byte private keys', () => {
|
||||
const invalidPriv =
|
||||
100000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800073278156000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000n;
|
||||
throws(() => ed.getPublicKey(invalidPriv));
|
||||
@@ -382,7 +385,24 @@ describe('ed448', () => {
|
||||
deepStrictEqual(ed.verify(signature, msg, publicKey), true);
|
||||
});
|
||||
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);
|
||||
deepStrictEqual(ed.verify(signature, msg, publicKey), false);
|
||||
});
|
||||
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);
|
||||
});
|
||||
});
|
||||
describe('sync methods', () => {
|
||||
should('sign and verify', () => {
|
||||
const publicKey = ed.getPublicKey(privKey);
|
||||
const signature = ed.sign(msg, privKey);
|
||||
deepStrictEqual(ed.verify(signature, msg, publicKey), true);
|
||||
});
|
||||
should('not verify signature with wrong public key', () => {
|
||||
const publicKey = ed.getPublicKey(ed.utils.randomPrivateKey());
|
||||
const signature = ed.sign(msg, privKey);
|
||||
deepStrictEqual(ed.verify(signature, msg, publicKey), false);
|
||||
});
|
||||
@@ -393,27 +413,9 @@ describe('ed448', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('sync methods', () => {
|
||||
should('sign and verify', () => {
|
||||
const publicKey = ed.getPublicKey(privKey);
|
||||
const signature = ed.sign(msg, privKey);
|
||||
deepStrictEqual(ed.verify(signature, msg, publicKey), true);
|
||||
});
|
||||
should('not verify signature with wrong public key', async () => {
|
||||
const publicKey = ed.getPublicKey(12);
|
||||
const signature = ed.sign(msg, privKey);
|
||||
deepStrictEqual(ed.verify(signature, msg, publicKey), false);
|
||||
});
|
||||
should('not verify signature with wrong hash', async () => {
|
||||
const publicKey = ed.getPublicKey(privKey);
|
||||
const signature = ed.sign(msg, privKey);
|
||||
deepStrictEqual(ed.verify(signature, wrongMsg, publicKey), false);
|
||||
});
|
||||
});
|
||||
|
||||
should('BASE_POINT.multiply() throws in Point#multiply on TEST 5', () => {
|
||||
for (const num of [0n, 0, -1n, -1, 1.1]) {
|
||||
throws(() => ed.Point.BASE.multiply(num));
|
||||
throws(() => ed.ExtendedPoint.BASE.multiply(num));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -437,14 +439,14 @@ describe('ed448', () => {
|
||||
}
|
||||
});
|
||||
|
||||
{
|
||||
describe('wycheproof', () => {
|
||||
for (let g = 0; g < ed448vectors.testGroups.length; g++) {
|
||||
const group = ed448vectors.testGroups[g];
|
||||
const key = group.key;
|
||||
should(`Wycheproof/ED448(${g}, public)`, () => {
|
||||
should(`ED448(${g}, public)`, () => {
|
||||
deepStrictEqual(hex(ed.getPublicKey(key.sk)), key.pk);
|
||||
});
|
||||
should(`Wycheproof/ED448`, () => {
|
||||
should(`ED448`, () => {
|
||||
for (let i = 0; i < group.tests.length; i++) {
|
||||
const v = group.tests[i];
|
||||
const index = `${g}/${i} ${v.comment}`;
|
||||
@@ -463,7 +465,7 @@ describe('ed448', () => {
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// ECDH
|
||||
const rfc7748Mul = [
|
||||
@@ -482,12 +484,14 @@ describe('ed448', () => {
|
||||
'884a02576239ff7a2f2f63b2db6a9ff37047ac13568e1e30fe63c4a7ad1b3ee3a5700df34321d62077e63633c575c1c954514e99da7c179d',
|
||||
},
|
||||
];
|
||||
describe('RFC7748', () => {
|
||||
for (let i = 0; i < rfc7748Mul.length; i++) {
|
||||
const v = rfc7748Mul[i];
|
||||
should(`RFC7748: scalarMult (${i})`, () => {
|
||||
should(`scalarMult (${i})`, () => {
|
||||
deepStrictEqual(hex(x448.scalarMult(v.scalar, v.u)), v.outputU);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const rfc7748Iter = [
|
||||
{
|
||||
@@ -505,7 +509,7 @@ describe('ed448', () => {
|
||||
for (let i = 0; i < rfc7748Iter.length; i++) {
|
||||
const { scalar, iters } = rfc7748Iter[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];
|
||||
deepStrictEqual(hex(k), scalar);
|
||||
});
|
||||
@@ -528,9 +532,9 @@ describe('ed448', () => {
|
||||
deepStrictEqual(hex(x448.scalarMult(bobPrivate, alicePublic)), shared);
|
||||
});
|
||||
|
||||
{
|
||||
describe('wycheproof', () => {
|
||||
const group = x448vectors.testGroups[0];
|
||||
should(`Wycheproof/X448`, () => {
|
||||
should(`X448`, () => {
|
||||
for (let i = 0; i < group.tests.length; i++) {
|
||||
const v = group.tests[i];
|
||||
const index = `(${i}, ${v.result}) ${v.comment}`;
|
||||
@@ -556,11 +560,11 @@ describe('ed448', () => {
|
||||
} else throw new Error('unknown test result');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// should('X448: should convert base point to montgomery using fromPoint', () => {
|
||||
// deepStrictEqual(
|
||||
// hex(ed.montgomeryCurve.UfromPoint(ed.Point.BASE)),
|
||||
// hex(ed.montgomeryCurve.UfromPoint(Point.BASE)),
|
||||
// ed.montgomeryCurve.BASE_POINT_U
|
||||
// );
|
||||
// });
|
||||
@@ -655,12 +659,12 @@ describe('ed448', () => {
|
||||
}
|
||||
|
||||
should('X448 base point', () => {
|
||||
const { x, y } = ed448.Point.BASE;
|
||||
const { x, y } = Point.BASE;
|
||||
const { Fp } = ed448.CURVE;
|
||||
// const invX = Fp.invert(x * x); // x²
|
||||
const u = Fp.div(Fp.create(y * y), Fp.create(x * x)); // (y²/x²)
|
||||
// const u = Fp.create(y * y * invX);
|
||||
deepStrictEqual(hex(numberToBytesLE(u, 56)), x448.Gu);
|
||||
deepStrictEqual(numberToBytesLE(u, 56), x448.GuBytes);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -5,18 +5,15 @@ import { bytesToHex } from '@noble/hashes/utils';
|
||||
import { sha256 } from '@noble/hashes/sha256';
|
||||
import { sha512 } from '@noble/hashes/sha512';
|
||||
import { shake128, shake256 } from '@noble/hashes/sha3';
|
||||
import { secp256r1 } from '../lib/esm/p256.js';
|
||||
import { secp384r1 } from '../lib/esm/p384.js';
|
||||
import { secp521r1 } from '../lib/esm/p521.js';
|
||||
import { ed25519 } from '../lib/esm/ed25519.js';
|
||||
import { ed448 } from '../lib/esm/ed448.js';
|
||||
import { secp256k1 } from '../lib/esm/secp256k1.js';
|
||||
import { bls12_381 } from '../lib/esm/bls12-381.js';
|
||||
import {
|
||||
stringToBytes,
|
||||
expand_message_xmd,
|
||||
expand_message_xof,
|
||||
} from '../lib/esm/abstract/hash-to-curve.js';
|
||||
import * as secp256r1 from '../esm/p256.js';
|
||||
import * as secp384r1 from '../esm/p384.js';
|
||||
import * as secp521r1 from '../esm/p521.js';
|
||||
import * as ed25519 from '../esm/ed25519.js';
|
||||
import * as ed448 from '../esm/ed448.js';
|
||||
import * as secp256k1 from '../esm/secp256k1.js';
|
||||
import { bls12_381 } from '../esm/bls12-381.js';
|
||||
import { expand_message_xmd, expand_message_xof } from '../esm/abstract/hash-to-curve.js';
|
||||
import { utf8ToBytes } from '../esm/abstract/utils.js';
|
||||
// XMD
|
||||
import { default as xmd_sha256_38 } from './hash-to-curve/expand_message_xmd_SHA256_38.json' assert { type: 'json' };
|
||||
import { default as xmd_sha256_256 } from './hash-to-curve/expand_message_xmd_SHA256_256.json' assert { type: 'json' };
|
||||
@@ -56,9 +53,9 @@ function testExpandXMD(hash, vectors) {
|
||||
const t = vectors.tests[i];
|
||||
should(`${vectors.hash}/${vectors.DST.length}/${i}`, () => {
|
||||
const p = expand_message_xmd(
|
||||
stringToBytes(t.msg),
|
||||
stringToBytes(vectors.DST),
|
||||
t.len_in_bytes,
|
||||
utf8ToBytes(t.msg),
|
||||
utf8ToBytes(vectors.DST),
|
||||
Number.parseInt(t.len_in_bytes),
|
||||
hash
|
||||
);
|
||||
deepStrictEqual(bytesToHex(p), t.uniform_bytes);
|
||||
@@ -79,9 +76,9 @@ function testExpandXOF(hash, vectors) {
|
||||
const t = vectors.tests[i];
|
||||
should(`${i}`, () => {
|
||||
const p = expand_message_xof(
|
||||
stringToBytes(t.msg),
|
||||
stringToBytes(vectors.DST),
|
||||
+t.len_in_bytes,
|
||||
utf8ToBytes(t.msg),
|
||||
utf8ToBytes(vectors.DST),
|
||||
Number.parseInt(t.len_in_bytes),
|
||||
vectors.k,
|
||||
hash
|
||||
);
|
||||
@@ -111,9 +108,11 @@ function testCurve(curve, ro, nu) {
|
||||
for (let i = 0; i < ro.vectors.length; i++) {
|
||||
const t = ro.vectors[i];
|
||||
should(`(${i})`, () => {
|
||||
const p = curve.Point.hashToCurve(stringToBytes(t.msg), {
|
||||
const p = curve
|
||||
.hashToCurve(utf8ToBytes(t.msg), {
|
||||
DST: ro.dst,
|
||||
});
|
||||
})
|
||||
.toAffine();
|
||||
deepStrictEqual(p.x, stringToFp(t.P.x), 'Px');
|
||||
deepStrictEqual(p.y, stringToFp(t.P.y), 'Py');
|
||||
});
|
||||
@@ -123,9 +122,11 @@ function testCurve(curve, ro, nu) {
|
||||
for (let i = 0; i < nu.vectors.length; i++) {
|
||||
const t = nu.vectors[i];
|
||||
should(`(${i})`, () => {
|
||||
const p = curve.Point.encodeToCurve(stringToBytes(t.msg), {
|
||||
const p = curve
|
||||
.encodeToCurve(utf8ToBytes(t.msg), {
|
||||
DST: nu.dst,
|
||||
});
|
||||
})
|
||||
.toAffine();
|
||||
deepStrictEqual(p.x, stringToFp(t.P.x), 'Px');
|
||||
deepStrictEqual(p.y, stringToFp(t.P.y), 'Py');
|
||||
});
|
||||
@@ -136,7 +137,6 @@ function testCurve(curve, ro, nu) {
|
||||
testCurve(secp256r1, p256_ro, p256_nu);
|
||||
testCurve(secp384r1, p384_ro, p384_nu);
|
||||
testCurve(secp521r1, p521_ro, p521_nu);
|
||||
// TODO: remove same tests from bls12
|
||||
testCurve(bls12_381.G1, g1_ro, g1_nu);
|
||||
testCurve(bls12_381.G2, g2_ro, g2_nu);
|
||||
testCurve(secp256k1, secp256k1_ro, secp256k1_nu);
|
||||
|
||||
@@ -6,7 +6,8 @@ import './nist.test.js';
|
||||
import './ed448.test.js';
|
||||
import './ed25519.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 './bls12-381.test.js';
|
||||
import './hash-to-curve.test.js';
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { jubjub, findGroupHash } from '../lib/esm/jubjub.js';
|
||||
import { jubjub, findGroupHash } from '../esm/jubjub.js';
|
||||
import { describe, should } from 'micro-should';
|
||||
import { deepStrictEqual, throws } from 'assert';
|
||||
import { hexToBytes, bytesToHex } from '@noble/hashes/utils';
|
||||
const Point = jubjub.ExtendedPoint;
|
||||
|
||||
const G_SPEND = new jubjub.ExtendedPoint(
|
||||
const G_SPEND = new Point(
|
||||
0x055f1f24f0f0512287e51c3c5a0a6903fc0baf8711de9eafd7c0e66f69d8d2dbn,
|
||||
0x566178b2505fdd52132a5007d80a04652842e78ffb376897588f406278214ed7n,
|
||||
0x0141fafa1f11088a3b2007c14d652375888f3b37838ba6bdffae096741ceddfen,
|
||||
0x12eada93c0b7d595f5f04f5ebfb4b7d033ef2884136475cab5e41ce17db5be9cn
|
||||
);
|
||||
const G_PROOF = new jubjub.ExtendedPoint(
|
||||
const G_PROOF = new Point(
|
||||
0x0174d54ce9fad258a2f8a86a1deabf15c7a2b51106b0fbcd9d29020f78936f71n,
|
||||
0x16871d6d877dcd222e4ec3bccb3f37cb1865a2d37dd3a5dcbc032a69b62b4445n,
|
||||
0x57a3cd31e496d82bd4aa78bd5ecd751cfb76d54a5d3f4560866379f9fc11c9b3n,
|
||||
@@ -22,7 +22,7 @@ describe('jubjub', () => {
|
||||
should('toHex/fromHex', () => {
|
||||
// More than field
|
||||
throws(() =>
|
||||
jubjub.Point.fromHex(
|
||||
Point.fromHex(
|
||||
new Uint8Array([
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
@@ -31,14 +31,14 @@ describe('jubjub', () => {
|
||||
);
|
||||
// Multiplicative generator (sqrt == null), not on curve.
|
||||
throws(() =>
|
||||
jubjub.Point.fromHex(
|
||||
Point.fromHex(
|
||||
new Uint8Array([
|
||||
7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0,
|
||||
])
|
||||
)
|
||||
);
|
||||
const tmp = jubjub.Point.fromHex(
|
||||
const tmp = Point.fromHex(
|
||||
new Uint8Array([
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0,
|
||||
@@ -47,14 +47,14 @@ describe('jubjub', () => {
|
||||
deepStrictEqual(tmp.x, 0x8d51ccce760304d0ec030002760300000001000000000000n);
|
||||
deepStrictEqual(tmp.y, 0n);
|
||||
|
||||
const S = G_SPEND.toAffine().toRawBytes();
|
||||
const S2 = G_SPEND.double().toAffine().toRawBytes();
|
||||
const P = G_PROOF.toAffine().toRawBytes();
|
||||
const P2 = G_PROOF.double().toAffine().toRawBytes();
|
||||
const S_exp = jubjub.Point.fromHex(S);
|
||||
const S2_exp = jubjub.Point.fromHex(S2);
|
||||
const P_exp = jubjub.Point.fromHex(P);
|
||||
const P2_exp = jubjub.Point.fromHex(P2);
|
||||
const S = G_SPEND.toRawBytes();
|
||||
const S2 = G_SPEND.double().toRawBytes();
|
||||
const P = G_PROOF.toRawBytes();
|
||||
const P2 = G_PROOF.double().toRawBytes();
|
||||
const S_exp = Point.fromHex(S);
|
||||
const S2_exp = Point.fromHex(S2);
|
||||
const P_exp = Point.fromHex(P);
|
||||
const P2_exp = Point.fromHex(P2);
|
||||
deepStrictEqual(getXY(G_SPEND.toAffine()), getXY(S_exp));
|
||||
deepStrictEqual(getXY(G_SPEND.double().toAffine()), getXY(S2_exp));
|
||||
deepStrictEqual(getXY(G_PROOF.toAffine()), getXY(P_exp));
|
||||
|
||||
@@ -1,174 +1,20 @@
|
||||
import { deepStrictEqual, throws } from 'assert';
|
||||
import { deepStrictEqual } from 'assert';
|
||||
import { describe, should } from 'micro-should';
|
||||
import { secp192r1, P192 } from '../lib/esm/p192.js';
|
||||
import { secp224r1, P224 } from '../lib/esm/p224.js';
|
||||
import { secp256r1, P256 } from '../lib/esm/p256.js';
|
||||
import { secp384r1, P384 } from '../lib/esm/p384.js';
|
||||
import { secp521r1, P521 } from '../lib/esm/p521.js';
|
||||
import { secp256k1 } from '../lib/esm/secp256k1.js';
|
||||
import { hexToBytes, bytesToHex } from '../lib/esm/abstract/utils.js';
|
||||
import { secp192r1, secp224r1, P192, P224 } from './_more-curves.helpers.js';
|
||||
import { secp256r1, P256 } from '../esm/p256.js';
|
||||
import { secp384r1, P384 } from '../esm/p384.js';
|
||||
import { secp521r1, P521 } from '../esm/p521.js';
|
||||
import { secp256k1 } from '../esm/secp256k1.js';
|
||||
import { hexToBytes, bytesToHex } from '../esm/abstract/utils.js';
|
||||
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 rfc6979 } from './fixtures/rfc6979.json' assert { type: 'json' };
|
||||
|
||||
const hex = bytesToHex;
|
||||
|
||||
// prettier-ignore
|
||||
const NIST = {
|
||||
secp192r1, P192,
|
||||
secp224r1, P224,
|
||||
secp256r1, P256,
|
||||
secp384r1, P384,
|
||||
secp521r1, P521,
|
||||
secp256k1,
|
||||
};
|
||||
|
||||
should('Curve Fields', () => {
|
||||
const vectors = {
|
||||
secp192r1: 0xfffffffffffffffffffffffffffffffeffffffffffffffffn,
|
||||
secp224r1: 0xffffffffffffffffffffffffffffffff000000000000000000000001n,
|
||||
secp256r1: 0xffffffff00000001000000000000000000000000ffffffffffffffffffffffffn,
|
||||
secp256k1: 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2fn,
|
||||
secp384r1:
|
||||
0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffffn,
|
||||
secp521r1:
|
||||
0x01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffn,
|
||||
};
|
||||
for (const n in vectors) deepStrictEqual(NIST[n].CURVE.Fp.ORDER, vectors[n]);
|
||||
});
|
||||
|
||||
should('wychenproof ECDSA vectors', () => {
|
||||
for (const group of ecdsa.testGroups) {
|
||||
// Tested in secp256k1.test.js
|
||||
if (group.key.curve === 'secp256k1') continue;
|
||||
let CURVE = NIST[group.key.curve];
|
||||
if (!CURVE) continue;
|
||||
if (group.key.curve === 'secp224r1' && group.sha !== 'SHA-224') {
|
||||
if (group.sha === 'SHA-256') CURVE = CURVE.create(sha256);
|
||||
}
|
||||
const pubKey = CURVE.Point.fromHex(group.key.uncompressed);
|
||||
deepStrictEqual(pubKey.x, BigInt(`0x${group.key.wx}`));
|
||||
deepStrictEqual(pubKey.y, BigInt(`0x${group.key.wy}`));
|
||||
for (const test of group.tests) {
|
||||
if (['Hash weaker than DL-group'].includes(test.comment)) {
|
||||
continue;
|
||||
}
|
||||
const m = CURVE.CURVE.hash(hexToBytes(test.msg));
|
||||
if (test.result === 'valid' || test.result === 'acceptable') {
|
||||
try {
|
||||
CURVE.Signature.fromDER(test.sig);
|
||||
} catch (e) {
|
||||
// Some test has invalid signature which we don't accept
|
||||
if (e.message.includes('Invalid signature: incorrect length')) continue;
|
||||
throw e;
|
||||
}
|
||||
const verified = CURVE.verify(test.sig, m, pubKey);
|
||||
deepStrictEqual(verified, true, 'valid');
|
||||
} else if (test.result === 'invalid') {
|
||||
let failed = false;
|
||||
try {
|
||||
failed = !CURVE.verify(test.sig, m, pubKey);
|
||||
} catch (error) {
|
||||
failed = true;
|
||||
}
|
||||
deepStrictEqual(failed, true, 'invalid');
|
||||
} else throw new Error('unknown test result');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
should('wychenproof ECDH vectors', () => {
|
||||
for (const group of ecdh.testGroups) {
|
||||
// // Tested in secp256k1.test.js
|
||||
// if (group.key.curve === 'secp256k1') continue;
|
||||
// We don't have SHA-224
|
||||
const CURVE = NIST[group.curve];
|
||||
if (!CURVE) continue;
|
||||
for (const test of group.tests) {
|
||||
if (test.result === 'valid' || test.result === 'acceptable') {
|
||||
try {
|
||||
const pub = CURVE.Point.fromHex(test.public);
|
||||
} catch (e) {
|
||||
if (e.message.includes('Point.fromHex: received invalid point.')) continue;
|
||||
throw e;
|
||||
}
|
||||
const shared = CURVE.getSharedSecret(test.private, test.public);
|
||||
deepStrictEqual(shared, test.shared, 'valid');
|
||||
} else if (test.result === 'invalid') {
|
||||
let failed = false;
|
||||
try {
|
||||
CURVE.getSharedSecret(test.private, test.public);
|
||||
} catch (error) {
|
||||
failed = true;
|
||||
}
|
||||
deepStrictEqual(failed, true, 'invalid');
|
||||
} else throw new Error('unknown test result');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
import { default as ecdh_secp224r1_test } from './wycheproof/ecdh_secp224r1_test.json' assert { type: 'json' };
|
||||
import { default as ecdh_secp256r1_test } from './wycheproof/ecdh_secp256r1_test.json' assert { type: 'json' };
|
||||
import { default as ecdh_secp256k1_test } from './wycheproof/ecdh_secp256k1_test.json' assert { type: 'json' };
|
||||
import { default as ecdh_secp384r1_test } from './wycheproof/ecdh_secp384r1_test.json' assert { type: 'json' };
|
||||
import { default as ecdh_secp521r1_test } from './wycheproof/ecdh_secp521r1_test.json' assert { type: 'json' };
|
||||
|
||||
// More per curve tests
|
||||
const WYCHEPROOF_ECDH = {
|
||||
P224: {
|
||||
curve: P224,
|
||||
tests: [ecdh_secp224r1_test],
|
||||
},
|
||||
P256: {
|
||||
curve: P256,
|
||||
tests: [ecdh_secp256r1_test],
|
||||
},
|
||||
secp256k1: {
|
||||
curve: secp256k1,
|
||||
tests: [ecdh_secp256k1_test],
|
||||
},
|
||||
P384: {
|
||||
curve: P384,
|
||||
tests: [ecdh_secp384r1_test],
|
||||
},
|
||||
P521: {
|
||||
curve: P521,
|
||||
tests: [ecdh_secp521r1_test],
|
||||
},
|
||||
};
|
||||
|
||||
for (const name in WYCHEPROOF_ECDH) {
|
||||
const { curve, tests } = WYCHEPROOF_ECDH[name];
|
||||
for (let i = 0; i < tests.length; i++) {
|
||||
const test = tests[i];
|
||||
for (let j = 0; j < test.testGroups.length; j++) {
|
||||
const group = test.testGroups[j];
|
||||
should(`Wycheproof/ECDH ${name} (${i}/${j})`, () => {
|
||||
for (const test of group.tests) {
|
||||
if (test.result === 'valid' || test.result === 'acceptable') {
|
||||
try {
|
||||
const pub = curve.Point.fromHex(test.public);
|
||||
} catch (e) {
|
||||
if (e.message.includes('Point.fromHex: received invalid point.')) continue;
|
||||
throw e;
|
||||
}
|
||||
const shared = curve.getSharedSecret(test.private, test.public);
|
||||
deepStrictEqual(hex(shared), test.shared, 'valid');
|
||||
} else if (test.result === 'invalid') {
|
||||
let failed = false;
|
||||
try {
|
||||
curve.getSharedSecret(test.private, test.public);
|
||||
} catch (error) {
|
||||
failed = true;
|
||||
}
|
||||
deepStrictEqual(failed, true, 'invalid');
|
||||
} else throw new Error('unknown test result');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tests with custom hashes
|
||||
import { default as secp224r1_sha224_test } from './wycheproof/ecdsa_secp224r1_sha224_test.json' assert { type: 'json' };
|
||||
import { default as secp224r1_sha256_test } from './wycheproof/ecdsa_secp224r1_sha256_test.json' assert { type: 'json' };
|
||||
@@ -199,6 +45,123 @@ import { sha3_224, sha3_256, sha3_384, sha3_512 } from '@noble/hashes/sha3';
|
||||
import { sha512, sha384 } from '@noble/hashes/sha512';
|
||||
import { sha224, sha256 } from '@noble/hashes/sha256';
|
||||
|
||||
const hex = bytesToHex;
|
||||
|
||||
// prettier-ignore
|
||||
const NIST = {
|
||||
secp192r1, P192,
|
||||
secp224r1, P224,
|
||||
secp256r1, P256,
|
||||
secp384r1, P384,
|
||||
secp521r1, P521,
|
||||
secp256k1,
|
||||
};
|
||||
|
||||
describe('NIST curves', () => {});
|
||||
should('fields', () => {
|
||||
const vectors = {
|
||||
secp192r1: 0xfffffffffffffffffffffffffffffffeffffffffffffffffn,
|
||||
secp224r1: 0xffffffffffffffffffffffffffffffff000000000000000000000001n,
|
||||
secp256r1: 0xffffffff00000001000000000000000000000000ffffffffffffffffffffffffn,
|
||||
secp256k1: 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2fn,
|
||||
secp384r1:
|
||||
0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffffn,
|
||||
secp521r1:
|
||||
0x01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffn,
|
||||
};
|
||||
for (const n in vectors) deepStrictEqual(NIST[n].CURVE.Fp.ORDER, vectors[n]);
|
||||
});
|
||||
|
||||
describe('wycheproof ECDH', () => {
|
||||
for (const group of ecdh.testGroups) {
|
||||
// // Tested in secp256k1.test.js
|
||||
// if (group.key.curve === 'secp256k1') continue;
|
||||
// We don't have SHA-224
|
||||
const CURVE = NIST[group.curve];
|
||||
if (!CURVE) continue;
|
||||
should(group.curve, () => {
|
||||
for (const test of group.tests) {
|
||||
if (test.result === 'valid' || test.result === 'acceptable') {
|
||||
try {
|
||||
const pub = CURVE.ProjectivePoint.fromHex(test.public);
|
||||
} catch (e) {
|
||||
// Our strict validation filter doesn't let weird-length DER vectors
|
||||
if (e.message.startsWith('Point of length')) continue;
|
||||
throw e;
|
||||
}
|
||||
const shared = CURVE.getSharedSecret(test.private, test.public);
|
||||
deepStrictEqual(shared, test.shared, 'valid');
|
||||
} else if (test.result === 'invalid') {
|
||||
let failed = false;
|
||||
try {
|
||||
CURVE.getSharedSecret(test.private, test.public);
|
||||
} catch (error) {
|
||||
failed = true;
|
||||
}
|
||||
deepStrictEqual(failed, true, 'invalid');
|
||||
} else throw new Error('unknown test result');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// More per curve tests
|
||||
const WYCHEPROOF_ECDH = {
|
||||
P224: {
|
||||
curve: P224,
|
||||
tests: [ecdh_secp224r1_test],
|
||||
},
|
||||
P256: {
|
||||
curve: P256,
|
||||
tests: [ecdh_secp256r1_test],
|
||||
},
|
||||
secp256k1: {
|
||||
curve: secp256k1,
|
||||
tests: [ecdh_secp256k1_test],
|
||||
},
|
||||
P384: {
|
||||
curve: P384,
|
||||
tests: [ecdh_secp384r1_test],
|
||||
},
|
||||
P521: {
|
||||
curve: P521,
|
||||
tests: [ecdh_secp521r1_test],
|
||||
},
|
||||
};
|
||||
|
||||
for (const name in WYCHEPROOF_ECDH) {
|
||||
const { curve, tests } = WYCHEPROOF_ECDH[name];
|
||||
for (let i = 0; i < tests.length; i++) {
|
||||
const test = tests[i];
|
||||
for (let j = 0; j < test.testGroups.length; j++) {
|
||||
const group = test.testGroups[j];
|
||||
should(`additional ${name} (${i}/${j})`, () => {
|
||||
for (const test of group.tests) {
|
||||
if (test.result === 'valid' || test.result === 'acceptable') {
|
||||
try {
|
||||
const pub = curve.ProjectivePoint.fromHex(test.public);
|
||||
} catch (e) {
|
||||
// Our strict validation filter doesn't let weird-length DER vectors
|
||||
if (e.message.includes('Point of length')) continue;
|
||||
throw e;
|
||||
}
|
||||
const shared = curve.getSharedSecret(test.private, test.public);
|
||||
deepStrictEqual(hex(shared), test.shared, 'valid');
|
||||
} else if (test.result === 'invalid') {
|
||||
let failed = false;
|
||||
try {
|
||||
curve.getSharedSecret(test.private, test.public);
|
||||
} catch (error) {
|
||||
failed = true;
|
||||
}
|
||||
deepStrictEqual(failed, true, 'invalid');
|
||||
} else throw new Error('unknown test result');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const WYCHEPROOF_ECDSA = {
|
||||
P224: {
|
||||
curve: P224,
|
||||
@@ -232,7 +195,6 @@ const WYCHEPROOF_ECDSA = {
|
||||
secp256k1: {
|
||||
curve: secp256k1,
|
||||
hashes: {
|
||||
// TODO: debug why fails, can be bug
|
||||
sha256: {
|
||||
hash: sha256,
|
||||
tests: [secp256k1_sha256_test],
|
||||
@@ -309,31 +271,33 @@ const WYCHEPROOF_ECDSA = {
|
||||
};
|
||||
|
||||
function runWycheproof(name, CURVE, group, index) {
|
||||
const pubKey = CURVE.Point.fromHex(group.key.uncompressed);
|
||||
const pubKey = CURVE.ProjectivePoint.fromHex(group.key.uncompressed);
|
||||
deepStrictEqual(pubKey.x, BigInt(`0x${group.key.wx}`));
|
||||
deepStrictEqual(pubKey.y, BigInt(`0x${group.key.wy}`));
|
||||
const pubR = pubKey.toRawBytes();
|
||||
for (const test of group.tests) {
|
||||
const m = CURVE.CURVE.hash(hexToBytes(test.msg));
|
||||
const { sig } = test;
|
||||
|
||||
if (test.result === 'valid' || test.result === 'acceptable') {
|
||||
try {
|
||||
CURVE.Signature.fromDER(test.sig);
|
||||
CURVE.Signature.fromDER(sig);
|
||||
} catch (e) {
|
||||
// Some tests has invalid signature which we don't accept
|
||||
if (e.message.includes('Invalid signature: incorrect length')) continue;
|
||||
throw e;
|
||||
}
|
||||
const verified = CURVE.verify(test.sig, m, pubKey);
|
||||
const verified = CURVE.verify(sig, m, pubR);
|
||||
if (name === 'secp256k1') {
|
||||
// lowS: true for secp256k1
|
||||
deepStrictEqual(verified, !CURVE.Signature.fromDER(test.sig).hasHighS(), `${index}: valid`);
|
||||
deepStrictEqual(verified, !CURVE.Signature.fromDER(sig).hasHighS(), `${index}: valid`);
|
||||
} else {
|
||||
deepStrictEqual(verified, true, `${index}: valid`);
|
||||
}
|
||||
} else if (test.result === 'invalid') {
|
||||
let failed = false;
|
||||
try {
|
||||
failed = !CURVE.verify(test.sig, m, pubKey);
|
||||
failed = !CURVE.verify(sig, m, pubR);
|
||||
} catch (error) {
|
||||
failed = true;
|
||||
}
|
||||
@@ -342,9 +306,49 @@ 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];
|
||||
describe('Wycheproof/WYCHEPROOF_ECDSA', () => {
|
||||
describe(name, () => {
|
||||
for (const hName in hashes) {
|
||||
const { hash, tests } = hashes[hName];
|
||||
const CURVE = curve.create(hash);
|
||||
@@ -359,15 +363,17 @@ for (const name in WYCHEPROOF_ECDSA) {
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const hexToBigint = (hex) => BigInt(`0x${hex}`);
|
||||
should('RFC6979', () => {
|
||||
describe('RFC6979', () => {
|
||||
for (const v of rfc6979) {
|
||||
should(v.curve, () => {
|
||||
const curve = NIST[v.curve];
|
||||
deepStrictEqual(curve.CURVE.n, hexToBigint(v.q));
|
||||
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.y, hexToBigint(v.Uy));
|
||||
for (const c of v.cases) {
|
||||
@@ -378,6 +384,7 @@ should('RFC6979', () => {
|
||||
deepStrictEqual(curve.verify(sigObj.toDERRawBytes(), h, pubKey), true, 'verify(1)');
|
||||
deepStrictEqual(curve.verify(sigObj, h, pubKey), true, 'verify(2)');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
375
test/poseidon.test.js
Normal file
375
test/poseidon.test.js
Normal file
@@ -0,0 +1,375 @@
|
||||
import { deepStrictEqual, throws } from 'assert';
|
||||
import { should, describe } from 'micro-should';
|
||||
import * as poseidon from '../esm/abstract/poseidon.js';
|
||||
import * as stark from '../esm/stark.js';
|
||||
import * as mod from '../esm/abstract/modular.js';
|
||||
import { default as pvectors } from './vectors/poseidon.json' assert { type: 'json' };
|
||||
const { st1, st2, st3, st4 } = pvectors;
|
||||
|
||||
describe('Stark', () => {
|
||||
should('poseidonMdsMatrixUnsafe', () => {
|
||||
const matrix = [
|
||||
[
|
||||
2778560475384578201077246683568670693743746494974613838537993780462451025202n,
|
||||
1175299404131241652930097281601393692628174430208909163156444576599667748918n,
|
||||
459930634481240293374476654621049426021644833445120509139335338093973616187n,
|
||||
],
|
||||
[
|
||||
2699370377471722242958186781613316939129713429759631049128040020458992590651n,
|
||||
1488831960940040807419416081499284128899207850625157044437836107358246188803n,
|
||||
3405112981980800875534081635548548562399171531483475155039499736396630179833n,
|
||||
],
|
||||
|
||||
[
|
||||
1860070716810022053527433635909648527418980081585070357136946388030401399342n,
|
||||
2606527819893847364468965441606872534271438365089422719512470850627617054272n,
|
||||
2715867691630559973784374069384091521307896505826088878858115800121387149186n,
|
||||
],
|
||||
];
|
||||
deepStrictEqual(stark._poseidonMDS(stark.Fp251, 'HadesMDS', 3, 0), matrix);
|
||||
});
|
||||
|
||||
should('HadesPermutation', () => {
|
||||
deepStrictEqual(
|
||||
stark.poseidonSmall([
|
||||
4379311784651118086770398084575492314150568148003994287303975907890254409956n,
|
||||
5329163686893598957822497554130545759427567507701132391649270915797304266381n,
|
||||
1081797873147645298856697595691862435558345225505029083672323747888463248125n,
|
||||
]),
|
||||
[
|
||||
1342232677189718451682683203787286758407058155581807117466384919996430343159n,
|
||||
380853961496438693334706417244065195303131974442781224856980145160981376662n,
|
||||
1919212703304954644851339421413808305076993030243665926017858381407659820613n,
|
||||
]
|
||||
);
|
||||
});
|
||||
should('HadesPermutation (custom)', () => {
|
||||
const h = stark.poseidonCreate({
|
||||
Fp: stark.Fp251,
|
||||
rate: 2,
|
||||
capacity: 1,
|
||||
roundsFull: 8,
|
||||
roundsPartial: 83,
|
||||
});
|
||||
deepStrictEqual(
|
||||
h([
|
||||
4379311784651118086770398084575492314150568148003994287303975907890254409956n,
|
||||
5329163686893598957822497554130545759427567507701132391649270915797304266381n,
|
||||
1081797873147645298856697595691862435558345225505029083672323747888463248125n,
|
||||
]),
|
||||
[
|
||||
2864461397224564530993577865807718592436235694918699912757414692654057505365n,
|
||||
1576206983934669422583425346343473837630736957734769961428118554039862202613n,
|
||||
1607006208879950753054674913136990521997740361932184292107790666308092455675n,
|
||||
]
|
||||
);
|
||||
});
|
||||
should('HadesPermutation (custom, Fp253)', () => {
|
||||
const h = stark.poseidonCreate({
|
||||
Fp: stark.Fp253,
|
||||
rate: 2,
|
||||
capacity: 1,
|
||||
roundsFull: 8,
|
||||
roundsPartial: 83,
|
||||
});
|
||||
deepStrictEqual(
|
||||
h([
|
||||
4379311784651118086770398084575492314150568148003994287303975907890254409956n,
|
||||
5329163686893598957822497554130545759427567507701132391649270915797304266381n,
|
||||
1081797873147645298856697595691862435558345225505029083672323747888463248125n,
|
||||
]),
|
||||
[
|
||||
11142411210283675631592374649001218595612035205233832049083369488791454026844n,
|
||||
98304838055259883374145304326851527594402230455144399354815642835291000581n,
|
||||
8643534790068701259242695637167859384191499281344739826454631748110172472997n,
|
||||
]
|
||||
);
|
||||
});
|
||||
should('PoseidonHash', () => {
|
||||
deepStrictEqual(
|
||||
stark.poseidonHash(
|
||||
4379311784651118086770398084575492314150568148003994287303975907890254409956n,
|
||||
5329163686893598957822497554130545759427567507701132391649270915797304266381n
|
||||
),
|
||||
2457757238178986673695038558497063891521456354791980183317105434323761563347n
|
||||
);
|
||||
});
|
||||
should('PoseidonHash (custom)', () => {
|
||||
const h = stark.poseidonCreate({
|
||||
Fp: stark.Fp251,
|
||||
rate: 2,
|
||||
capacity: 1,
|
||||
roundsFull: 8,
|
||||
roundsPartial: 83,
|
||||
});
|
||||
deepStrictEqual(
|
||||
stark.poseidonHash(
|
||||
4379311784651118086770398084575492314150568148003994287303975907890254409956n,
|
||||
5329163686893598957822497554130545759427567507701132391649270915797304266381n,
|
||||
h
|
||||
),
|
||||
654164301216498483748450956182386165976155551413834652546305861430119544536n
|
||||
);
|
||||
});
|
||||
should('PoseidonHash (custom, Fp253)', () => {
|
||||
const h = stark.poseidonCreate({
|
||||
Fp: stark.Fp253,
|
||||
rate: 2,
|
||||
capacity: 1,
|
||||
roundsFull: 8,
|
||||
roundsPartial: 83,
|
||||
});
|
||||
deepStrictEqual(
|
||||
stark.poseidonHash(
|
||||
4379311784651118086770398084575492314150568148003994287303975907890254409956n,
|
||||
5329163686893598957822497554130545759427567507701132391649270915797304266381n,
|
||||
h
|
||||
),
|
||||
9557424461253897982213839283192966960594440725760392861778010931094267239786n
|
||||
);
|
||||
});
|
||||
});
|
||||
// Official vectors: https://extgit.iaik.tugraz.at/krypto/hadeshash/-/blob/master/code/test_vectors.txt
|
||||
|
||||
should('poseidonperm_x5_255_3', () => {
|
||||
const Fp = mod.Fp(BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001'));
|
||||
|
||||
const mds = [
|
||||
[
|
||||
0x3d955d6c02fe4d7cb500e12f2b55eff668a7b4386bd27413766713c93f2acfcdn,
|
||||
0x3798866f4e6058035dcf8addb2cf1771fac234bcc8fc05d6676e77e797f224bfn,
|
||||
0x2c51456a7bf2467eac813649f3f25ea896eac27c5da020dae54a6e640278fda2n,
|
||||
],
|
||||
[
|
||||
0x20088ca07bbcd7490a0218ebc0ecb31d0ea34840e2dc2d33a1a5adfecff83b43n,
|
||||
0x1d04ba0915e7807c968ea4b1cb2d610c7f9a16b4033f02ebacbb948c86a988c3n,
|
||||
0x5387ccd5729d7acbd09d96714d1d18bbd0eeaefb2ddee3d2ef573c9c7f953307n,
|
||||
],
|
||||
[
|
||||
0x1e208f585a72558534281562cad89659b428ec61433293a8d7f0f0e38a6726acn,
|
||||
0x0455ebf862f0b60f69698e97d36e8aafd4d107cae2b61be1858b23a3363642e0n,
|
||||
0x569e2c206119e89455852059f707370e2c1fc9721f6c50991cedbbf782daef54n,
|
||||
],
|
||||
];
|
||||
|
||||
const t = 3;
|
||||
|
||||
const roundConstants = poseidon.splitConstants(st1.map(BigInt), t);
|
||||
|
||||
const poseidon_x5_255_3 = poseidon.poseidon({
|
||||
Fp,
|
||||
t,
|
||||
roundsFull: 8,
|
||||
roundsPartial: 57,
|
||||
mds,
|
||||
roundConstants,
|
||||
});
|
||||
deepStrictEqual(
|
||||
poseidon_x5_255_3([
|
||||
0x0000000000000000000000000000000000000000000000000000000000000000n,
|
||||
0x0000000000000000000000000000000000000000000000000000000000000001n,
|
||||
0x0000000000000000000000000000000000000000000000000000000000000002n,
|
||||
]),
|
||||
[
|
||||
0x28ce19420fc246a05553ad1e8c98f5c9d67166be2c18e9e4cb4b4e317dd2a78an,
|
||||
0x51f3e312c95343a896cfd8945ea82ba956c1118ce9b9859b6ea56637b4b1ddc4n,
|
||||
0x3b2b69139b235626a0bfb56c9527ae66a7bf486ad8c11c14d1da0c69bbe0f79an,
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
should('poseidonperm_x5_255_5', () => {
|
||||
const Fp = mod.Fp(0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001n);
|
||||
const t = 5;
|
||||
|
||||
const mds = [
|
||||
[
|
||||
0x354423b163d1078b0dd645be56316e34a9b98e52dcf9f469be44b108be46c107n,
|
||||
0x44778737e8bc1154aca1cd92054a1e5b83808403705f7d54da88bbd1920e1053n,
|
||||
0x5872eefb5ab6b2946556524168a2aebb69afd513a2fff91e50167b1f6e4055e0n,
|
||||
0x43dff85b25129835819bc8c95819f1a34136f6114e900cd3656e1b9e0e13f86an,
|
||||
0x07803d2ffe72940596803f244ac090a9cf2d3616546520bc360c7eed0b81cbf8n,
|
||||
],
|
||||
[
|
||||
0x45d6bc4b818e2b9a53e0e2c0a08f70c34167fd8128e05ac800651ddfee0932d1n,
|
||||
0x08317abbb9e5046b22dfb79e64c8184855107c1d95dddd2b63ca10dddea9ff1an,
|
||||
0x1bb80eba77c5dcffafb55ccba4ae39ac8f94a054f2a0ee3006b362f709d5e470n,
|
||||
0x038e75bdcf8be7fd3a1e844c4de7333531bbd5a8d2c3779627df88e7480e7c5cn,
|
||||
0x2dd797a699e620ea6b31b91ba3fad4a82f40cffb3e8a30c0b7a546ff69a9002bn,
|
||||
],
|
||||
[
|
||||
0x4b906f9ee339b196e958e3541b555b4b53e540a113b2f1cabba627be16eb5608n,
|
||||
0x605f0c707b82ef287f46431f9241fe4acf0b7ddb151803cbcf1e7bbd27c3e974n,
|
||||
0x100c514bf38f6ff10df1c83bb428397789cfff7bb0b1280f52343861e8c8737en,
|
||||
0x2d40ce8af8a252f5611701c3d6b1e517161d0549ef27f443570c81fcdfe3706bn,
|
||||
0x3e6418bdf0313f59afc5f40b4450e56881110ea9a0532e8092efb06a12a8b0f1n,
|
||||
],
|
||||
[
|
||||
0x71788bf7f6c0cebae5627c5629d012d5fba52428d1f25cdaa0a7434e70e014d0n,
|
||||
0x55cc73296f7e7d26d10b9339721d7983ca06145675255025ab00b34342557db7n,
|
||||
0x0f043b29be2def73a6c6ec92168ea4b47bc9f434a5e6b5d48677670a7ca4d285n,
|
||||
0x62ccc9cdfed859a610f103d74ea04dec0f6874a9b36f3b4e9b47fd73368d45b4n,
|
||||
0x55fb349dd6200b34eaba53a67e74f47d08e473da139dc47e44df50a26423d2d1n,
|
||||
],
|
||||
[
|
||||
0x45bfbe5ed2f4a01c13b15f20bba00ff577b1154a81b3f318a6aff86369a66735n,
|
||||
0x6a008906685587af05dce9ad2c65ea1d42b1ec32609597bd00c01f58443329efn,
|
||||
0x004feebd0dbdb9b71176a1d43c9eb495e16419382cdf7864e4bce7b37440cd58n,
|
||||
0x09f080180ce23a5aef3a07e60b28ffeb2cf1771aefbc565c2a3059b39ed82f43n,
|
||||
0x2f7126ddc54648ab6d02493dbe9907f29f4ef3967ad8cd609f0d9467e1694607n,
|
||||
],
|
||||
];
|
||||
|
||||
const roundConstants = poseidon.splitConstants(st2.map(BigInt), t);
|
||||
|
||||
const poseidon_x5_255_5 = poseidon.poseidon({
|
||||
Fp,
|
||||
t,
|
||||
roundsFull: 8,
|
||||
roundsPartial: 60,
|
||||
mds,
|
||||
roundConstants,
|
||||
});
|
||||
|
||||
deepStrictEqual(
|
||||
poseidon_x5_255_5([
|
||||
0x0000000000000000000000000000000000000000000000000000000000000000n,
|
||||
0x0000000000000000000000000000000000000000000000000000000000000001n,
|
||||
0x0000000000000000000000000000000000000000000000000000000000000002n,
|
||||
0x0000000000000000000000000000000000000000000000000000000000000003n,
|
||||
0x0000000000000000000000000000000000000000000000000000000000000004n,
|
||||
]),
|
||||
[
|
||||
0x2a918b9c9f9bd7bb509331c81e297b5707f6fc7393dcee1b13901a0b22202e18n,
|
||||
0x65ebf8671739eeb11fb217f2d5c5bf4a0c3f210e3f3cd3b08b5db75675d797f7n,
|
||||
0x2cc176fc26bc70737a696a9dfd1b636ce360ee76926d182390cdb7459cf585cen,
|
||||
0x4dc4e29d283afd2a491fe6aef122b9a968e74eff05341f3cc23fda1781dcb566n,
|
||||
0x03ff622da276830b9451b88b85e6184fd6ae15c8ab3ee25a5667be8592cce3b1n,
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
should('poseidonperm_x5_254_3', () => {
|
||||
const Fp = mod.Fp(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001n);
|
||||
const t = 3;
|
||||
|
||||
const mds = [
|
||||
[
|
||||
0x109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118bn,
|
||||
0x16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e0n,
|
||||
0x2b90bba00fca0589f617e7dcbfe82e0df706ab640ceb247b791a93b74e36736dn,
|
||||
],
|
||||
[
|
||||
0x2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771n,
|
||||
0x2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe23n,
|
||||
0x101071f0032379b697315876690f053d148d4e109f5fb065c8aacc55a0f89bfan,
|
||||
],
|
||||
[
|
||||
0x143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7n,
|
||||
0x176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee2911n,
|
||||
0x19a3fc0a56702bf417ba7fee3802593fa644470307043f7773279cd71d25d5e0n,
|
||||
],
|
||||
];
|
||||
|
||||
const roundConstants = poseidon.splitConstants(st3.map(BigInt), t);
|
||||
|
||||
const poseidon_x5_254_3 = poseidon.poseidon({
|
||||
Fp,
|
||||
t,
|
||||
roundsFull: 8,
|
||||
roundsPartial: 57,
|
||||
mds,
|
||||
roundConstants,
|
||||
});
|
||||
|
||||
deepStrictEqual(
|
||||
poseidon_x5_254_3([
|
||||
0x0000000000000000000000000000000000000000000000000000000000000000n,
|
||||
0x0000000000000000000000000000000000000000000000000000000000000001n,
|
||||
0x0000000000000000000000000000000000000000000000000000000000000002n,
|
||||
]),
|
||||
[
|
||||
0x115cc0f5e7d690413df64c6b9662e9cf2a3617f2743245519e19607a4417189an,
|
||||
0x0fca49b798923ab0239de1c9e7a4a9a2210312b6a2f616d18b5a87f9b628ae29n,
|
||||
0x0e7ae82e40091e63cbd4f16a6d16310b3729d4b6e138fcf54110e2867045a30cn,
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
should('poseidonperm_x5_254_5', () => {
|
||||
const Fp = mod.Fp(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001n);
|
||||
const t = 5;
|
||||
|
||||
const mds = [
|
||||
[
|
||||
0x251e7fdf99591080080b0af133b9e4369f22e57ace3cd7f64fc6fdbcf38d7da1n,
|
||||
0x25fb50b65acf4fb047cbd3b1c17d97c7fe26ea9ca238d6e348550486e91c7765n,
|
||||
0x293d617d7da72102355f39ebf62f91b06deb5325f367a4556ea1e31ed5767833n,
|
||||
0x104d0295ab00c85e960111ac25da474366599e575a9b7edf6145f14ba6d3c1c4n,
|
||||
0x0aaa35e2c84baf117dea3e336cd96a39792b3813954fe9bf3ed5b90f2f69c977n,
|
||||
],
|
||||
[
|
||||
0x2a70b9f1d4bbccdbc03e17c1d1dcdb02052903dc6609ea6969f661b2eb74c839n,
|
||||
0x281154651c921e746315a9934f1b8a1bba9f92ad8ef4b979115b8e2e991ccd7an,
|
||||
0x28c2be2f8264f95f0b53c732134efa338ccd8fdb9ee2b45fb86a894f7db36c37n,
|
||||
0x21888041e6febd546d427c890b1883bb9b626d8cb4dc18dcc4ec8fa75e530a13n,
|
||||
0x14ddb5fada0171db80195b9592d8cf2be810930e3ea4574a350d65e2cbff4941n,
|
||||
],
|
||||
[
|
||||
0x2f69a7198e1fbcc7dea43265306a37ed55b91bff652ad69aa4fa8478970d401dn,
|
||||
0x001c1edd62645b73ad931ab80e37bbb267ba312b34140e716d6a3747594d3052n,
|
||||
0x15b98ce93e47bc64ce2f2c96c69663c439c40c603049466fa7f9a4b228bfc32bn,
|
||||
0x12c7e2adfa524e5958f65be2fbac809fcba8458b28e44d9265051de33163cf9cn,
|
||||
0x2efc2b90d688134849018222e7b8922eaf67ce79816ef468531ec2de53bbd167n,
|
||||
],
|
||||
[
|
||||
0x0c3f050a6bf5af151981e55e3e1a29a13c3ffa4550bd2514f1afd6c5f721f830n,
|
||||
0x0dec54e6dbf75205fa75ba7992bd34f08b2efe2ecd424a73eda7784320a1a36en,
|
||||
0x1c482a25a729f5df20225815034b196098364a11f4d988fb7cc75cf32d8136fan,
|
||||
0x2625ce48a7b39a4252732624e4ab94360812ac2fc9a14a5fb8b607ae9fd8514an,
|
||||
0x07f017a7ebd56dd086f7cd4fd710c509ed7ef8e300b9a8bb9fb9f28af710251fn,
|
||||
],
|
||||
[
|
||||
0x2a20e3a4a0e57d92f97c9d6186c6c3ea7c5e55c20146259be2f78c2ccc2e3595n,
|
||||
0x1049f8210566b51faafb1e9a5d63c0ee701673aed820d9c4403b01feb727a549n,
|
||||
0x02ecac687ef5b4b568002bd9d1b96b4bef357a69e3e86b5561b9299b82d69c8en,
|
||||
0x2d3a1aea2e6d44466808f88c9ba903d3bdcb6b58ba40441ed4ebcf11bbe1e37bn,
|
||||
0x14074bb14c982c81c9ad171e4f35fe49b39c4a7a72dbb6d9c98d803bfed65e64n,
|
||||
],
|
||||
];
|
||||
|
||||
const roundConstants = poseidon.splitConstants(st4.map(BigInt), t);
|
||||
|
||||
const poseidon_x5_254_5 = poseidon.poseidon({
|
||||
Fp,
|
||||
t,
|
||||
roundsFull: 8,
|
||||
roundsPartial: 60,
|
||||
mds,
|
||||
roundConstants,
|
||||
});
|
||||
|
||||
deepStrictEqual(
|
||||
poseidon_x5_254_5([
|
||||
0x0000000000000000000000000000000000000000000000000000000000000000n,
|
||||
0x0000000000000000000000000000000000000000000000000000000000000001n,
|
||||
0x0000000000000000000000000000000000000000000000000000000000000002n,
|
||||
0x0000000000000000000000000000000000000000000000000000000000000003n,
|
||||
0x0000000000000000000000000000000000000000000000000000000000000004n,
|
||||
]),
|
||||
[
|
||||
0x299c867db6c1fdd79dcefa40e4510b9837e60ebb1ce0663dbaa525df65250465n,
|
||||
0x1148aaef609aa338b27dafd89bb98862d8bb2b429aceac47d86206154ffe053dn,
|
||||
0x24febb87fed7462e23f6665ff9a0111f4044c38ee1672c1ac6b0637d34f24907n,
|
||||
0x0eb08f6d809668a981c186beaf6110060707059576406b248e5d9cf6e78b3d3en,
|
||||
0x07748bc6877c9b82c8b98666ee9d0626ec7f5be4205f79ee8528ef1c4a376fc7n,
|
||||
]
|
||||
);
|
||||
});
|
||||
// Startadperm is unsupported, since it is non prime field
|
||||
|
||||
// ESM is broken.
|
||||
import url from 'url';
|
||||
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
|
||||
should.run();
|
||||
}
|
||||
34
test/secp256k1-schnorr.test.js
Normal file
34
test/secp256k1-schnorr.test.js
Normal file
@@ -0,0 +1,34 @@
|
||||
import { deepStrictEqual, throws } from 'assert';
|
||||
import { readFileSync } from 'fs';
|
||||
import { should, describe } from 'micro-should';
|
||||
import { bytesToHex as hex } from '@noble/hashes/utils';
|
||||
import { schnorr } from '../esm/secp256k1.js';
|
||||
const schCsv = readFileSync('./test/vectors/schnorr.csv', 'utf-8');
|
||||
|
||||
describe('schnorr.sign()', () => {
|
||||
// index,secret key,public key,aux_rand,message,signature,verification result,comment
|
||||
const vectors = schCsv
|
||||
.split('\n')
|
||||
.map((line) => line.split(','))
|
||||
.slice(1, -1);
|
||||
for (let vec of vectors) {
|
||||
const [index, sec, pub, rnd, msg, expSig, passes, comment] = vec;
|
||||
should(`${comment || 'vector ' + index}`, () => {
|
||||
if (sec) {
|
||||
deepStrictEqual(hex(schnorr.getPublicKey(sec)), pub.toLowerCase());
|
||||
const sig = schnorr.sign(msg, sec, rnd);
|
||||
deepStrictEqual(hex(sig), expSig.toLowerCase());
|
||||
deepStrictEqual(schnorr.verify(sig, msg, pub), true);
|
||||
} else {
|
||||
const passed = schnorr.verify(expSig, msg, pub);
|
||||
deepStrictEqual(passed, passes === 'TRUE');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// ESM is broken.
|
||||
import url from 'url';
|
||||
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
|
||||
should.run();
|
||||
}
|
||||
14
test/secp256k1.helpers.js
Normal file
14
test/secp256k1.helpers.js
Normal file
@@ -0,0 +1,14 @@
|
||||
// @ts-ignore
|
||||
export { secp256k1 as secp } from '../esm/secp256k1.js';
|
||||
import { secp256k1 as _secp } from '../esm/secp256k1.js';
|
||||
export { bytesToNumberBE, numberToBytesBE } from '../esm/abstract/utils.js';
|
||||
export { mod } from '../esm/abstract/modular.js';
|
||||
export const sigFromDER = (der) => {
|
||||
return _secp.Signature.fromDER(der);
|
||||
};
|
||||
export const sigToDER = (sig) => sig.toDERHex();
|
||||
export const selectHash = (secp) => secp.CURVE.hash;
|
||||
export const normVerifySig = (s) => _secp.Signature.fromDER(s);
|
||||
// export const bytesToNumberBE = secp256k1.utils.bytesToNumberBE;
|
||||
// export const numberToBytesBE = secp256k1.utils.numberToBytesBE;
|
||||
// export const mod = mod_;
|
||||
@@ -1,20 +1,21 @@
|
||||
import { hexToBytes, bytesToHex as hex } from '@noble/hashes/utils';
|
||||
import { deepStrictEqual, throws } from 'assert';
|
||||
import * as fc from 'fast-check';
|
||||
import { secp256k1, schnorr } from '../lib/esm/secp256k1.js';
|
||||
import { Fp } from '../lib/esm/abstract/modular.js';
|
||||
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 ecdh } from './vectors/ecdh.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 wp } from './vectors/wychenproof.json' assert { type: 'json' };
|
||||
import { should, describe } from 'micro-should';
|
||||
import { deepStrictEqual, throws } from 'assert';
|
||||
import { hexToBytes, bytesToHex } from '@noble/hashes/utils';
|
||||
|
||||
const hex = bytesToHex;
|
||||
const secp = secp256k1;
|
||||
const Point = secp.ProjectivePoint;
|
||||
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);
|
||||
// prettier-ignore
|
||||
@@ -24,7 +25,7 @@ const toBEHex = (n) => n.toString(16).padStart(64, '0');
|
||||
|
||||
function hexToNumber(hex) {
|
||||
if (typeof hex !== 'string') {
|
||||
throw new TypeError('hexToNumber: expected string, got ' + typeof hex);
|
||||
throw new Error('hexToNumber: expected string, got ' + typeof hex);
|
||||
}
|
||||
// Big Endian
|
||||
return BigInt(`0x${hex}`);
|
||||
@@ -37,15 +38,15 @@ describe('secp256k1', () => {
|
||||
.filter((line) => line)
|
||||
.map((line) => line.split(':'));
|
||||
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.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.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.y), y);
|
||||
}
|
||||
@@ -62,71 +63,72 @@ describe('secp256k1', () => {
|
||||
.filter((line) => line)
|
||||
.map((line) => line.split(':'));
|
||||
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.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.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.y), y);
|
||||
}
|
||||
});
|
||||
|
||||
should('Point.isValidPoint()', () => {
|
||||
describe('Point', () => {
|
||||
should('fromHex() assertValidity', () => {
|
||||
for (const vector of points.valid.isPoint) {
|
||||
const { P, expected } = vector;
|
||||
if (expected) {
|
||||
secp.Point.fromHex(P);
|
||||
Point.fromHex(P);
|
||||
} else {
|
||||
throws(() => secp.Point.fromHex(P));
|
||||
throws(() => Point.fromHex(P));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
should('Point.fromPrivateKey()', () => {
|
||||
should('.fromPrivateKey()', () => {
|
||||
for (const vector of points.valid.pointFromScalar) {
|
||||
const { d, expected } = vector;
|
||||
let p = secp.Point.fromPrivateKey(d);
|
||||
let p = Point.fromPrivateKey(d);
|
||||
deepStrictEqual(p.toHex(true), expected);
|
||||
}
|
||||
});
|
||||
|
||||
should('Point#toHex(compressed)', () => {
|
||||
should('#toHex(compressed)', () => {
|
||||
for (const vector of points.valid.pointCompress) {
|
||||
const { P, compress, expected } = vector;
|
||||
let p = secp.Point.fromHex(P);
|
||||
let p = Point.fromHex(P);
|
||||
deepStrictEqual(p.toHex(compress), expected);
|
||||
}
|
||||
});
|
||||
|
||||
should('Point#toHex() roundtrip (failed case)', () => {
|
||||
should('#toHex() roundtrip (failed case)', () => {
|
||||
const point1 =
|
||||
secp.Point.fromPrivateKey(
|
||||
Point.fromPrivateKey(
|
||||
88572218780422190464634044548753414301110513745532121983949500266768436236425n
|
||||
);
|
||||
// const hex = point1.toHex(true);
|
||||
// deepStrictEqual(secp.Point.fromHex(hex).toHex(true), hex);
|
||||
// deepStrictEqual(Point.fromHex(hex).toHex(true), hex);
|
||||
});
|
||||
|
||||
should('Point#toHex() roundtrip', () => {
|
||||
should('#toHex() roundtrip', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (x) => {
|
||||
const point1 = secp.Point.fromPrivateKey(x);
|
||||
const point1 = Point.fromPrivateKey(x);
|
||||
const hex = point1.toHex(true);
|
||||
deepStrictEqual(secp.Point.fromHex(hex).toHex(true), hex);
|
||||
deepStrictEqual(Point.fromHex(hex).toHex(true), hex);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
should('Point#add(other)', () => {
|
||||
should('#add(other)', () => {
|
||||
for (const vector of points.valid.pointAdd) {
|
||||
const { P, Q, expected } = vector;
|
||||
let p = secp.Point.fromHex(P);
|
||||
let q = secp.Point.fromHex(Q);
|
||||
let p = Point.fromHex(P);
|
||||
let q = Point.fromHex(Q);
|
||||
if (expected) {
|
||||
deepStrictEqual(p.add(q).toHex(true), expected);
|
||||
} else {
|
||||
@@ -137,12 +139,12 @@ describe('secp256k1', () => {
|
||||
}
|
||||
});
|
||||
|
||||
should('Point#multiply(privateKey)', () => {
|
||||
should('#multiply(privateKey)', () => {
|
||||
for (const vector of points.valid.pointMultiply) {
|
||||
const { P, d, expected } = vector;
|
||||
const p = secp.Point.fromHex(P);
|
||||
const p = Point.fromHex(P);
|
||||
if (expected) {
|
||||
deepStrictEqual(p.multiply(hexToNumber(d)).toHex(true), expected);
|
||||
deepStrictEqual(p.multiply(hexToNumber(d)).toHex(true), expected, P);
|
||||
} else {
|
||||
throws(() => {
|
||||
p.multiply(hexToNumber(d)).toHex(true);
|
||||
@@ -154,15 +156,16 @@ describe('secp256k1', () => {
|
||||
const { P, d } = vector;
|
||||
if (hexToNumber(d) < secp.CURVE.n) {
|
||||
throws(() => {
|
||||
const p = secp.Point.fromHex(P);
|
||||
const p = Point.fromHex(P);
|
||||
p.multiply(hexToNumber(d)).toHex(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
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()
|
||||
// should('ProjectivePoint#multiplyUnsafe', () => {
|
||||
@@ -175,8 +178,8 @@ describe('secp256k1', () => {
|
||||
// console.log(p0.multiply(z));
|
||||
// console.log(secp.ProjectivePoint.normalizeZ([p0.multiplyUnsafe(z)])[0])
|
||||
// });
|
||||
|
||||
should('Signature.fromCompactHex() roundtrip', () => {
|
||||
describe('Signature', () => {
|
||||
should('.fromCompactHex() roundtrip', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, (r, s) => {
|
||||
const sig = new secp.Signature(r, s);
|
||||
@@ -185,16 +188,18 @@ describe('secp256k1', () => {
|
||||
);
|
||||
});
|
||||
|
||||
should('Signature.fromDERHex() roundtrip', () => {
|
||||
should('.fromDERHex() roundtrip', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, (r, s) => {
|
||||
const sig = new secp.Signature(r, s);
|
||||
deepStrictEqual(secp.Signature.fromDER(sig.toDERHex()), sig);
|
||||
deepStrictEqual(sigFromDER(sigToDER(sig)), sig);
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
should('sign()/should create deterministic signatures with RFC 6979', () => {
|
||||
describe('sign()', () => {
|
||||
should('create deterministic signatures with RFC 6979', () => {
|
||||
for (const vector of ecdsa.valid) {
|
||||
let usig = secp.sign(vector.m, vector.d);
|
||||
let sig = usig.toCompactHex();
|
||||
@@ -204,21 +209,18 @@ describe('secp256k1', () => {
|
||||
}
|
||||
});
|
||||
|
||||
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) {
|
||||
throws(() => secp.sign(vector.m, vector.d));
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
should('sign()/edge cases', () => {
|
||||
should('edge cases', () => {
|
||||
throws(() => secp.sign());
|
||||
throws(() => secp.sign(''));
|
||||
});
|
||||
|
||||
should('sign()/should create correct DER encoding against libsecp256k1', () => {
|
||||
should('create correct DER encoding against libsecp256k1', () => {
|
||||
const CASES = [
|
||||
[
|
||||
'd1a9dc8ed4e46a6a3e5e594615ca351d7d7ef44df1e4c94c1802f3592183794b',
|
||||
@@ -233,15 +235,17 @@ describe('secp256k1', () => {
|
||||
'3045022100d18990bba7832bb283e3ecf8700b67beb39acc73f4200ed1c331247c46edccc602202e5c8bbfe47ae159512c583b30a3fa86575cddc62527a03de7756517ae4c6c73',
|
||||
],
|
||||
];
|
||||
const privKey = hexToBytes('0101010101010101010101010101010101010101010101010101010101010101');
|
||||
const privKey = hexToBytes(
|
||||
'0101010101010101010101010101010101010101010101010101010101010101'
|
||||
);
|
||||
for (const [msg, exp] of CASES) {
|
||||
const res = secp.sign(msg, privKey, { extraEntropy: undefined });
|
||||
deepStrictEqual(res.toDERHex(), exp);
|
||||
const rs = secp.Signature.fromDER(res.toDERHex()).toCompactHex();
|
||||
deepStrictEqual(secp.Signature.fromCompact(rs).toDERHex(), exp);
|
||||
deepStrictEqual(sigToDER(res), exp);
|
||||
const rs = sigFromDER(sigToDER(res)).toCompactHex();
|
||||
deepStrictEqual(sigToDER(secp.Signature.fromCompact(rs)), exp);
|
||||
}
|
||||
});
|
||||
should('sign()/sign ecdsa extraData', () => {
|
||||
should('handle {extraData} option', () => {
|
||||
const ent1 = '0000000000000000000000000000000000000000000000000000000000000000';
|
||||
const ent2 = '0000000000000000000000000000000000000000000000000000000000000001';
|
||||
const ent3 = '6e723d3fd94ed5d2b6bdd4f123364b0f3ca52af829988a63f8afe91d29db1c33';
|
||||
@@ -261,34 +265,36 @@ describe('secp256k1', () => {
|
||||
deepStrictEqual(sign(ent5), e.extraEntropyMax);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
should('verify()/should verify signature', () => {
|
||||
describe('verify()', () => {
|
||||
should('verify signature', () => {
|
||||
const MSG = '01'.repeat(32);
|
||||
const PRIV_KEY = 0x2n;
|
||||
const signature = secp.sign(MSG, PRIV_KEY);
|
||||
const publicKey = secp.getPublicKey(PRIV_KEY);
|
||||
deepStrictEqual(publicKey.length, 65);
|
||||
deepStrictEqual(publicKey.length, 33);
|
||||
deepStrictEqual(secp.verify(signature, MSG, publicKey), true);
|
||||
});
|
||||
should('verify()/should not verify signature with wrong public key', () => {
|
||||
should(' not verify signature with wrong public key', () => {
|
||||
const MSG = '01'.repeat(32);
|
||||
const PRIV_KEY = 0x2n;
|
||||
const WRONG_PRIV_KEY = 0x22n;
|
||||
const PRIV_KEY = '01'.repeat(32);
|
||||
const WRONG_PRIV_KEY = '02'.repeat(32);
|
||||
const signature = secp.sign(MSG, PRIV_KEY);
|
||||
const publicKey = secp.Point.fromPrivateKey(WRONG_PRIV_KEY).toHex();
|
||||
deepStrictEqual(publicKey.length, 130);
|
||||
const publicKey = Point.fromPrivateKey(WRONG_PRIV_KEY).toHex();
|
||||
deepStrictEqual(publicKey.length, 66);
|
||||
deepStrictEqual(secp.verify(signature, MSG, publicKey), false);
|
||||
});
|
||||
should('verify()/should not verify signature with wrong hash', () => {
|
||||
should('not verify signature with wrong hash', () => {
|
||||
const MSG = '01'.repeat(32);
|
||||
const PRIV_KEY = 0x2n;
|
||||
const WRONG_MSG = '11'.repeat(32);
|
||||
const signature = secp.sign(MSG, PRIV_KEY);
|
||||
const publicKey = secp.getPublicKey(PRIV_KEY);
|
||||
deepStrictEqual(publicKey.length, 65);
|
||||
deepStrictEqual(publicKey.length, 33);
|
||||
deepStrictEqual(secp.verify(signature, WRONG_MSG, publicKey), false);
|
||||
});
|
||||
should('verify()/should verify random signatures', () =>
|
||||
should('verify random signatures', () =>
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, fc.hexaString({ minLength: 64, maxLength: 64 }), (privKey, msg) => {
|
||||
const pub = secp.getPublicKey(privKey);
|
||||
@@ -297,7 +303,7 @@ describe('secp256k1', () => {
|
||||
})
|
||||
)
|
||||
);
|
||||
should('verify()/should not verify signature with invalid r/s', () => {
|
||||
should('not verify signature with invalid r/s', () => {
|
||||
const msg = new Uint8Array([
|
||||
0xbb, 0x5a, 0x52, 0xf4, 0x2f, 0x9c, 0x92, 0x61, 0xed, 0x43, 0x61, 0xf5, 0x94, 0x22, 0xa1,
|
||||
0xe3, 0x00, 0x36, 0xe7, 0xc3, 0x2b, 0x27, 0x0c, 0x88, 0x07, 0xa4, 0x19, 0xfe, 0xca, 0x60,
|
||||
@@ -308,7 +314,7 @@ describe('secp256k1', () => {
|
||||
const r = 1n;
|
||||
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);
|
||||
signature.r = r;
|
||||
signature.s = s;
|
||||
@@ -317,60 +323,38 @@ describe('secp256k1', () => {
|
||||
// Verifies, but it shouldn't, because signature S > curve order
|
||||
deepStrictEqual(verified, false);
|
||||
});
|
||||
should('verify()/should not verify msg = curve order', () => {
|
||||
should('not verify msg = curve order', () => {
|
||||
const msg = 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141';
|
||||
const x = 55066263022277343669578718895168534326250603453777594175500187360389116729240n;
|
||||
const y = 32670510020758816978083085130507043184471273380659243275938904335757337482424n;
|
||||
const r = 104546003225722045112039007203142344920046999340768276760147352389092131869133n;
|
||||
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);
|
||||
deepStrictEqual(secp.verify(sig, msg, pub), false);
|
||||
});
|
||||
should('verify()/should verify non-strict msg bb5a...', () => {
|
||||
should('verify non-strict msg bb5a...', () => {
|
||||
const msg = 'bb5a52f42f9c9261ed4361f59422a1e30036e7c32b270c8807a419feca605023';
|
||||
const x = 3252872872578928810725465493269682203671229454553002637820453004368632726370n;
|
||||
const y = 17482644437196207387910659778872952193236850502325156318830589868678978890912n;
|
||||
const r = 432420386565659656852420866390673177323n;
|
||||
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);
|
||||
deepStrictEqual(secp.verify(sig, msg, pub, { strict: false }), true);
|
||||
deepStrictEqual(secp.verify(sig, msg, pub, { lowS: false }), true);
|
||||
});
|
||||
should(
|
||||
'secp256k1.verify()/should not verify invalid deterministic signatures with RFC 6979',
|
||||
() => {
|
||||
should('not verify invalid deterministic signatures with RFC 6979', () => {
|
||||
for (const vector of ecdsa.invalid.verify) {
|
||||
const res = secp.verify(vector.signature, vector.m, vector.Q);
|
||||
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');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
should('recoverPublicKey()/should recover public key from recovery bit', () => {
|
||||
});
|
||||
describe('recoverPublicKey()', () => {
|
||||
should('recover public key from recovery bit', () => {
|
||||
const message = '00000000000000000000000000000000000000000000000000000000deadbeef';
|
||||
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 recoveredPubkey = sig.recoverPublicKey(message);
|
||||
// const recoveredPubkey = secp.recoverPublicKey(message, signature, recovery);
|
||||
@@ -378,36 +362,38 @@ describe('secp256k1', () => {
|
||||
deepStrictEqual(recoveredPubkey.toHex(false), publicKey);
|
||||
deepStrictEqual(secp.verify(sig, message, publicKey), true);
|
||||
});
|
||||
should('recoverPublicKey()/should not recover zero points', () => {
|
||||
should('not recover zero points', () => {
|
||||
const msgHash = '6b8d2c81b11b2d699528dde488dbdf2f94293d0d33c32e347f255fa4a6c1f0a9';
|
||||
const sig =
|
||||
'79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817986b8d2c81b11b2d699528dde488dbdf2f94293d0d33c32e347f255fa4a6c1f0a9';
|
||||
const recovery = 0;
|
||||
throws(() => secp.recoverPublicKey(msgHash, sig, recovery));
|
||||
});
|
||||
should('recoverPublicKey()/should handle all-zeros msghash', () => {
|
||||
should('handle all-zeros msghash', () => {
|
||||
const privKey = secp.utils.randomPrivateKey();
|
||||
const pub = secp.getPublicKey(privKey);
|
||||
const zeros = '0000000000000000000000000000000000000000000000000000000000000000';
|
||||
const sig = secp.sign(zeros, privKey, { recovered: true });
|
||||
const sig = secp.sign(zeros, privKey);
|
||||
const recoveredKey = sig.recoverPublicKey(zeros);
|
||||
deepStrictEqual(recoveredKey.toRawBytes(), pub);
|
||||
});
|
||||
should('recoverPublicKey()/should handle RFC 6979 vectors', () => {
|
||||
should('handle RFC 6979 vectors', () => {
|
||||
for (const vector of ecdsa.valid) {
|
||||
let usig = secp.sign(vector.m, vector.d);
|
||||
let sig = usig.toDERHex();
|
||||
let sig = sigToDER(usig);
|
||||
const vpub = secp.getPublicKey(vector.d);
|
||||
const recovered = usig.recoverPublicKey(vector.m);
|
||||
deepStrictEqual(recovered.toHex(), hex(vpub));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('getSharedSecret()', () => {
|
||||
// TODO: Real implementation.
|
||||
function derToPub(der) {
|
||||
return der.slice(46);
|
||||
}
|
||||
should('getSharedSecret()/should produce correct results', () => {
|
||||
should('produce correct results', () => {
|
||||
// TODO: Once der is there, run all tests.
|
||||
for (const vector of ecdh.testGroups[0].tests.slice(0, 230)) {
|
||||
if (vector.result === 'invalid' || vector.private.length !== 64) {
|
||||
@@ -420,7 +406,7 @@ describe('secp256k1', () => {
|
||||
}
|
||||
}
|
||||
});
|
||||
should('getSharedSecret()/priv/pub order matters', () => {
|
||||
should('priv/pub order matters', () => {
|
||||
for (const vector of ecdh.testGroups[0].tests.slice(0, 100)) {
|
||||
if (vector.result === 'valid') {
|
||||
let priv = vector.private;
|
||||
@@ -429,9 +415,10 @@ describe('secp256k1', () => {
|
||||
}
|
||||
}
|
||||
});
|
||||
should('getSharedSecret()/rejects invalid keys', () => {
|
||||
should('reject invalid keys', () => {
|
||||
throws(() => secp.getSharedSecret('01', '02'));
|
||||
});
|
||||
});
|
||||
|
||||
should('utils.isValidPrivateKey()', () => {
|
||||
for (const vector of privates.valid.isPrivate) {
|
||||
@@ -442,58 +429,52 @@ describe('secp256k1', () => {
|
||||
should('have proper curve equation in assertValidity()', () => {
|
||||
throws(() => {
|
||||
const { Fp } = secp.CURVE;
|
||||
let point = new secp.Point(Fp.create(-2n), Fp.create(-1n));
|
||||
let point = new Point(Fp.create(-2n), Fp.create(-1n), Fp.create(1n));
|
||||
point.assertValidity();
|
||||
});
|
||||
});
|
||||
|
||||
const Fn = Fp(secp.CURVE.n);
|
||||
const normal = secp.utils._normalizePrivateKey;
|
||||
describe('tweak utilities (legacy)', () => {
|
||||
const normal = secp.utils.normPrivateKeyToScalar;
|
||||
const tweakUtils = {
|
||||
privateAdd: (privateKey, tweak) => {
|
||||
const p = normal(privateKey);
|
||||
const t = normal(tweak);
|
||||
return secp.utils._bigintToBytes(Fn.create(p + t));
|
||||
return numberToBytesBE(mod(normal(privateKey) + normal(tweak), secp.CURVE.n), 32);
|
||||
},
|
||||
|
||||
privateNegate: (privateKey) => {
|
||||
const p = normal(privateKey);
|
||||
return secp.utils._bigintToBytes(Fn.negate(p));
|
||||
return numberToBytesBE(mod(-normal(privateKey), secp.CURVE.n), 32);
|
||||
},
|
||||
|
||||
pointAddScalar: (p, tweak, isCompressed) => {
|
||||
const P = secp.Point.fromHex(p);
|
||||
const t = normal(tweak);
|
||||
const Q = secp.Point.BASE.multiplyAndAddUnsafe(P, t, 1n);
|
||||
if (!Q) throw new Error('Tweaked point at infinity');
|
||||
return Q.toRawBytes(isCompressed);
|
||||
const tweaked = Point.fromHex(p).add(Point.fromPrivateKey(tweak));
|
||||
if (tweaked.equals(Point.ZERO)) throw new Error('Tweaked point at infinity');
|
||||
return tweaked.toRawBytes(isCompressed);
|
||||
},
|
||||
|
||||
pointMultiply: (p, tweak, isCompressed) => {
|
||||
const P = secp.Point.fromHex(p);
|
||||
const h = typeof tweak === 'string' ? tweak : bytesToHex(tweak);
|
||||
const t = BigInt(`0x${h}`);
|
||||
return P.multiply(t).toRawBytes(isCompressed);
|
||||
if (typeof tweak === 'string') tweak = hexToBytes(tweak);
|
||||
const t = bytesToNumberBE(tweak);
|
||||
return Point.fromHex(p).multiply(t).toRawBytes(isCompressed);
|
||||
},
|
||||
};
|
||||
|
||||
should('privateAdd()', () => {
|
||||
for (const vector of privates.valid.add) {
|
||||
const { a, b, expected } = vector;
|
||||
deepStrictEqual(bytesToHex(tweakUtils.privateAdd(a, b)), expected);
|
||||
deepStrictEqual(hex(tweakUtils.privateAdd(a, b)), expected);
|
||||
}
|
||||
});
|
||||
should('privateNegate()', () => {
|
||||
for (const vector of privates.valid.negate) {
|
||||
const { a, expected } = vector;
|
||||
deepStrictEqual(bytesToHex(tweakUtils.privateNegate(a)), expected);
|
||||
deepStrictEqual(hex(tweakUtils.privateNegate(a)), expected);
|
||||
}
|
||||
});
|
||||
should('pointAddScalar()', () => {
|
||||
for (const vector of points.valid.pointAddScalar) {
|
||||
const { description, P, d, expected } = vector;
|
||||
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('pointAddScalar() invalid', () => {
|
||||
@@ -505,7 +486,7 @@ describe('secp256k1', () => {
|
||||
should('pointMultiply()', () => {
|
||||
for (const vector of points.valid.pointMultiply) {
|
||||
const { P, d, expected } = vector;
|
||||
deepStrictEqual(bytesToHex(tweakUtils.pointMultiply(P, d, true)), expected);
|
||||
deepStrictEqual(hex(tweakUtils.pointMultiply(P, d, true)), expected);
|
||||
}
|
||||
});
|
||||
should('pointMultiply() invalid', () => {
|
||||
@@ -514,15 +495,19 @@ describe('secp256k1', () => {
|
||||
throws(() => tweakUtils.pointMultiply(P, d));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
should('wychenproof vectors', () => {
|
||||
should('wycheproof vectors', () => {
|
||||
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) {
|
||||
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') {
|
||||
const verified = secp.verify(test.sig, m, pubKey);
|
||||
if (secp.Signature.fromDER(test.sig).hasHighS()) {
|
||||
const verified = secp.verify(normVerifySig(test.sig), m, pubKey);
|
||||
if (sigFromDER(test.sig).hasHighS()) {
|
||||
deepStrictEqual(verified, false);
|
||||
} else {
|
||||
deepStrictEqual(verified, true);
|
||||
|
||||
@@ -1,57 +1,60 @@
|
||||
import { deepStrictEqual, throws } from 'assert';
|
||||
import { should } from 'micro-should';
|
||||
import * as starknet from '../../lib/esm/stark.js';
|
||||
import { describe, should } from 'micro-should';
|
||||
import * as starknet from '../../esm/stark.js';
|
||||
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', () => {
|
||||
const g1 = starknet.Point.BASE;
|
||||
describe('starknet basic', () => {
|
||||
should('Basic elliptic sanity check', () => {
|
||||
const g1 = starknet.ProjectivePoint.BASE;
|
||||
deepStrictEqual(
|
||||
g1.x.toString(16),
|
||||
g1.toAffine().x.toString(16),
|
||||
'1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca'
|
||||
);
|
||||
deepStrictEqual(
|
||||
g1.y.toString(16),
|
||||
g1.toAffine().y.toString(16),
|
||||
'5668060aa49730b7be4801df46ec62de53ecd11abe43a32873000c36e8dc1f'
|
||||
);
|
||||
const g2 = g1.double();
|
||||
deepStrictEqual(
|
||||
g2.x.toString(16),
|
||||
g2.toAffine().x.toString(16),
|
||||
'759ca09377679ecd535a81e83039658bf40959283187c654c5416f439403cf5'
|
||||
);
|
||||
deepStrictEqual(
|
||||
g2.y.toString(16),
|
||||
g2.toAffine().y.toString(16),
|
||||
'6f524a3400e7708d5c01a28598ad272e7455aa88778b19f93b562d7a9646c41'
|
||||
);
|
||||
const g3 = g2.add(g1);
|
||||
deepStrictEqual(
|
||||
g3.x.toString(16),
|
||||
g3.toAffine().x.toString(16),
|
||||
'411494b501a98abd8262b0da1351e17899a0c4ef23dd2f96fec5ba847310b20'
|
||||
);
|
||||
deepStrictEqual(
|
||||
g3.y.toString(16),
|
||||
g3.toAffine().y.toString(16),
|
||||
'7e1b3ebac08924d2c26f409549191fcf94f3bf6f301ed3553e22dfb802f0686'
|
||||
);
|
||||
const g32 = g1.multiply(3);
|
||||
const g32 = g1.multiply(3n);
|
||||
deepStrictEqual(
|
||||
g32.x.toString(16),
|
||||
g32.toAffine().x.toString(16),
|
||||
'411494b501a98abd8262b0da1351e17899a0c4ef23dd2f96fec5ba847310b20'
|
||||
);
|
||||
deepStrictEqual(
|
||||
g32.y.toString(16),
|
||||
g32.toAffine().y.toString(16),
|
||||
'7e1b3ebac08924d2c26f409549191fcf94f3bf6f301ed3553e22dfb802f0686'
|
||||
);
|
||||
const minus1 = g1.multiply(starknet.CURVE.n - 1n);
|
||||
deepStrictEqual(
|
||||
minus1.x.toString(16),
|
||||
minus1.toAffine().x.toString(16),
|
||||
'1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca'
|
||||
);
|
||||
deepStrictEqual(
|
||||
minus1.y.toString(16),
|
||||
minus1.toAffine().y.toString(16),
|
||||
'7a997f9f55b68e04841b7fe20b9139d21ac132ee541bc5cd78cfff3c91723e2'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
should('Pedersen', () => {
|
||||
should('Pedersen', () => {
|
||||
deepStrictEqual(
|
||||
starknet.pedersen(2, 3),
|
||||
'0x5774fa77b3d843ae9167abd61cf80365a9b2b02218fc2f628494b5bdc9b33b8'
|
||||
@@ -64,16 +67,16 @@ should('Pedersen', () => {
|
||||
starknet.pedersen(3, 4),
|
||||
'0x262697b88544f733e5c6907c3e1763131e9f14c51ee7951258abbfb29415fbf'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
should('Hash chain', () => {
|
||||
should('Hash chain', () => {
|
||||
deepStrictEqual(
|
||||
starknet.hashChain([1, 2, 3]),
|
||||
'0x5d9d62d4040b977c3f8d2389d494e4e89a96a8b45c44b1368f1cc6ec5418915'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
should('Pedersen hash edgecases', () => {
|
||||
should('Pedersen hash edgecases', () => {
|
||||
// >>> pedersen_hash(0,0)
|
||||
const zero = '0x49ee3eba8c1600700ee1b87eb599f16716b0b1022947733551fde4050ca6804';
|
||||
deepStrictEqual(starknet.pedersen(0, 0), zero);
|
||||
@@ -100,9 +103,9 @@ should('Pedersen hash edgecases', () => {
|
||||
throws(() => starknet.pedersen(false, false), 'false');
|
||||
throws(() => starknet.pedersen(true, true), 'true');
|
||||
throws(() => starknet.pedersen(10.1, 10.1), 'float');
|
||||
});
|
||||
});
|
||||
|
||||
should('hashChain edgecases', () => {
|
||||
should('hashChain edgecases', () => {
|
||||
deepStrictEqual(starknet.hashChain([32312321312321312312312321n]), '0x1aba6672c014b4838cc201');
|
||||
deepStrictEqual(
|
||||
starknet.hashChain([1n, 2n]),
|
||||
@@ -118,9 +121,9 @@ should('hashChain edgecases', () => {
|
||||
starknet.hashChain([1, 2]),
|
||||
'0x5bb9440e27889a364bcb678b1f679ecd1347acdedcbf36e83494f857cc58026'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
should('Pedersen hash, issue #2', () => {
|
||||
should('Pedersen hash, issue #2', () => {
|
||||
// Verified with starnet.js
|
||||
deepStrictEqual(
|
||||
starknet.computeHashOnElements(issue2),
|
||||
@@ -134,12 +137,10 @@ should('Pedersen hash, issue #2', () => {
|
||||
starknet.computeHashOnElements([1]),
|
||||
'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 application = 'starkdeployement';
|
||||
const mnemonic =
|
||||
@@ -153,18 +154,18 @@ should('Seed derivation (example)', () => {
|
||||
starknet.grindKey(hdKey.privateKey),
|
||||
'6cf0a8bf113352eb863157a45c5e5567abb34f8d32cddafd2c22aa803f4892c'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
should('Compressed keys', () => {
|
||||
const G = starknet.Point.BASE;
|
||||
should('Compressed keys', () => {
|
||||
const G = starknet.ProjectivePoint.BASE;
|
||||
const half = starknet.CURVE.n / 2n;
|
||||
const last = starknet.CURVE.n;
|
||||
const vectors = [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
1n,
|
||||
2n,
|
||||
3n,
|
||||
4n,
|
||||
5n,
|
||||
half - 5n,
|
||||
half - 4n,
|
||||
half - 3n,
|
||||
@@ -182,17 +183,17 @@ should('Compressed keys', () => {
|
||||
last - 2n,
|
||||
last - 1n,
|
||||
].map((i) => G.multiply(i));
|
||||
const fixPoint = (pt) => ({ ...pt, _WINDOW_SIZE: undefined });
|
||||
const fixPoint = (pt) => pt.toAffine();
|
||||
for (const v of vectors) {
|
||||
const uncompressed = v.toHex();
|
||||
const compressed = v.toHex(true);
|
||||
const exp = fixPoint(v);
|
||||
deepStrictEqual(fixPoint(starknet.Point.fromHex(uncompressed)), exp);
|
||||
deepStrictEqual(fixPoint(starknet.Point.fromHex(compressed)), exp);
|
||||
deepStrictEqual(starknet.Point.fromHex(compressed).toHex(), uncompressed);
|
||||
deepStrictEqual(fixPoint(starknet.ProjectivePoint.fromHex(uncompressed)), exp);
|
||||
deepStrictEqual(fixPoint(starknet.ProjectivePoint.fromHex(compressed)), exp);
|
||||
deepStrictEqual(starknet.ProjectivePoint.fromHex(compressed).toHex(), uncompressed);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// ESM is broken.
|
||||
import url from 'url';
|
||||
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as microStark from '../../../lib/esm/stark.js';
|
||||
import * as microStark from '../../../esm/stark.js';
|
||||
import * as starkwareCrypto from '@starkware-industries/starkware-crypto-utils';
|
||||
import * as bench from 'micro-bmark';
|
||||
const { run, mark } = bench; // or bench.mark
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
|
||||
import { describe, should } from 'micro-should';
|
||||
import './basic.test.js';
|
||||
import './stark.test.js';
|
||||
import './property.test.js';
|
||||
import './poseidon.test.js';
|
||||
|
||||
// ESM is broken.
|
||||
import url from 'url';
|
||||
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
|
||||
should.run();
|
||||
}
|
||||
|
||||
114
test/stark/poseidon.test.js
Normal file
114
test/stark/poseidon.test.js
Normal file
@@ -0,0 +1,114 @@
|
||||
import { deepStrictEqual, throws } from 'assert';
|
||||
import { describe, should } from 'micro-should';
|
||||
import * as starknet from '../../esm/stark.js';
|
||||
import * as fs from 'fs';
|
||||
|
||||
function parseTest(path) {
|
||||
let data = fs.readFileSync(path, 'ascii');
|
||||
// Remove whitespaces
|
||||
data = data.replace(/[ |\t]/g, '');
|
||||
const pattern =
|
||||
'Rate=(\\d+)\n' +
|
||||
'Capacity=(\\d+)\n' +
|
||||
'FullRounds=(\\d+)\n' +
|
||||
'PartialRounds=(\\d+)\n' +
|
||||
'MDS=\\[(.+)\\]\n' +
|
||||
'RoundKeys=\\(?\n?\\[\n?(.+)\n?\\]\n?\\)?';
|
||||
const r = data.match(new RegExp(pattern, 'ms'));
|
||||
|
||||
function parseArray(s) {
|
||||
// Remove new lines
|
||||
s = s.replace(/\n/gms, '');
|
||||
return s.match(/(\[.+?\])/g).map((i) =>
|
||||
i
|
||||
.replace(/^\[(.+)\]$/, '$1')
|
||||
.split(',')
|
||||
.filter((i) => !!i)
|
||||
);
|
||||
}
|
||||
const res = {
|
||||
rate: +r[1],
|
||||
capacity: +r[2],
|
||||
roundsFull: +r[3],
|
||||
roundsPartial: +r[4],
|
||||
MDS: parseArray(r[5]).map((i) => i.map((j) => BigInt(j))),
|
||||
roundConstants: parseArray(r[6]).map((i) => i.map((j) => BigInt(j))),
|
||||
};
|
||||
return res;
|
||||
}
|
||||
|
||||
function mapPoseidon(parsed) {
|
||||
return starknet.poseidonBasic(
|
||||
{
|
||||
Fp: starknet.Fp251,
|
||||
rate: parsed.rate,
|
||||
capacity: parsed.capacity,
|
||||
roundsFull: parsed.roundsFull,
|
||||
roundsPartial: parsed.roundsPartial,
|
||||
},
|
||||
parsed.MDS
|
||||
);
|
||||
}
|
||||
|
||||
const parsed = {
|
||||
poseidon3: parseTest('./test/stark/poseidon/poseidon3.txt'),
|
||||
poseidon4: parseTest('./test/stark/poseidon/poseidon4.txt'),
|
||||
poseidon5: parseTest('./test/stark/poseidon/poseidon5.txt'),
|
||||
poseidon9: parseTest('./test/stark/poseidon/poseidon9.txt'),
|
||||
};
|
||||
|
||||
function poseidonTest(name, parsed) {
|
||||
should(`${name}`, () => {
|
||||
const fn = mapPoseidon(parsed);
|
||||
deepStrictEqual(fn.roundConstants, parsed.roundConstants);
|
||||
});
|
||||
}
|
||||
|
||||
describe('poseidon txt vectors', () => {
|
||||
poseidonTest('poseidon3', parsed.poseidon3);
|
||||
poseidonTest('poseidon4', parsed.poseidon4);
|
||||
poseidonTest('poseidon5', parsed.poseidon5);
|
||||
poseidonTest('poseidon9', parsed.poseidon9);
|
||||
});
|
||||
|
||||
should('Poseidon examples', () => {
|
||||
const p3 = mapPoseidon(parsed.poseidon3);
|
||||
deepStrictEqual(p3([0n, 0n, 0n]), [
|
||||
3446325744004048536138401612021367625846492093718951375866996507163446763827n,
|
||||
1590252087433376791875644726012779423683501236913937337746052470473806035332n,
|
||||
867921192302518434283879514999422690776342565400001269945778456016268852423n,
|
||||
]);
|
||||
const p4 = mapPoseidon(parsed.poseidon4);
|
||||
deepStrictEqual(p4([0n, 0n, 0n, 0n]), [
|
||||
535071095200566880914603862188010633478042591441142518549720701573192347548n,
|
||||
3567335813488551850156302853280844225974867890860330236555401145692518003968n,
|
||||
229995103310401763929738317978722680640995513996113588430855556460153357543n,
|
||||
3513983790849716360905369754287999509206472929684378838050290392634812839312n,
|
||||
]);
|
||||
const p5 = mapPoseidon(parsed.poseidon5);
|
||||
deepStrictEqual(p5([0n, 0n, 0n, 0n, 0n]), [
|
||||
2337689130971531876049206831496963607805116499042700598724344149414565980684n,
|
||||
3230969295497815870174763682436655274044379544854667759151474216427142025631n,
|
||||
3297330512217530111610698859408044542971696143761201570393504997742535648562n,
|
||||
2585480844700786541432072704002477919020588246983274666988914431019064343941n,
|
||||
3595308260654382824623573767385493361624474708214823462901432822513585995028n,
|
||||
]);
|
||||
const p9 = mapPoseidon(parsed.poseidon9);
|
||||
deepStrictEqual(p9([0n, 0n, 0n, 0n, 0n, 0n, 0n, 0n, 0n]), [
|
||||
1534116856660032929112709488204491699743182428465681149262739677337223235050n,
|
||||
1710856073207389764546990138116985223517553616229641666885337928044617114700n,
|
||||
3165864635055638516987240200217592641540231237468651257819894959934472989427n,
|
||||
1003007637710164252047715558598366312649052908276423203724288341354608811559n,
|
||||
68117303579957054409211824649914588822081700129416361923518488718489651489n,
|
||||
1123395637839379807713801282868237406546107732595903195840754789810160564711n,
|
||||
478590974834311070537087181212389392308746075734019180430422247431982932503n,
|
||||
835322726024358888065061514739954009068852229059154336727219387089732433787n,
|
||||
3129703030204995742174502162918848446737407262178341733578946634564864233056n,
|
||||
]);
|
||||
});
|
||||
|
||||
// ESM is broken.
|
||||
import url from 'url';
|
||||
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
|
||||
should.run();
|
||||
}
|
||||
201
test/stark/poseidon/LICENSE
Normal file
201
test/stark/poseidon/LICENSE
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
35
test/stark/poseidon/README.md
Normal file
35
test/stark/poseidon/README.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# StarkWare's Poseidon Hash
|
||||
|
||||
[Poseidon](https://www.poseidon-hash.info/) is a family of hash functions designed for being very efficient as algebraic circuits.
|
||||
As such, they may be very useful in ZK proving systems such as STARKs and others.
|
||||
|
||||
This repository provides the official parameters of StarkWare's Poseidon hash implementations.
|
||||
|
||||
All the instances are over the prime field:
|
||||
p = 2^251 + 17 * 2^192 + 1 = 3618502788666131213697322783095070105623107215331596699973092056135872020481
|
||||
|
||||
A few examples hash results with the different parameters:
|
||||
|
||||
```
|
||||
Poseidon3([0,0,0]) = [3446325744004048536138401612021367625846492093718951375866996507163446763827,
|
||||
1590252087433376791875644726012779423683501236913937337746052470473806035332,
|
||||
867921192302518434283879514999422690776342565400001269945778456016268852423]
|
||||
Poseidon4([0,0,0,0]) = [535071095200566880914603862188010633478042591441142518549720701573192347548,
|
||||
3567335813488551850156302853280844225974867890860330236555401145692518003968,
|
||||
229995103310401763929738317978722680640995513996113588430855556460153357543,
|
||||
3513983790849716360905369754287999509206472929684378838050290392634812839312]
|
||||
Poseidon5([0,0,0,0,0]) = [2337689130971531876049206831496963607805116499042700598724344149414565980684,
|
||||
3230969295497815870174763682436655274044379544854667759151474216427142025631,
|
||||
3297330512217530111610698859408044542971696143761201570393504997742535648562,
|
||||
2585480844700786541432072704002477919020588246983274666988914431019064343941,
|
||||
3595308260654382824623573767385493361624474708214823462901432822513585995028]
|
||||
Poseidon9([0,0,0,0,0,0,0,0,0]) = [1534116856660032929112709488204491699743182428465681149262739677337223235050,
|
||||
1710856073207389764546990138116985223517553616229641666885337928044617114700,
|
||||
3165864635055638516987240200217592641540231237468651257819894959934472989427,
|
||||
1003007637710164252047715558598366312649052908276423203724288341354608811559,
|
||||
68117303579957054409211824649914588822081700129416361923518488718489651489,
|
||||
1123395637839379807713801282868237406546107732595903195840754789810160564711,
|
||||
478590974834311070537087181212389392308746075734019180430422247431982932503,
|
||||
835322726024358888065061514739954009068852229059154336727219387089732433787,
|
||||
3129703030204995742174502162918848446737407262178341733578946634564864233056]
|
||||
```
|
||||
462
test/stark/poseidon/poseidon3.txt
Normal file
462
test/stark/poseidon/poseidon3.txt
Normal file
@@ -0,0 +1,462 @@
|
||||
Rate = 2
|
||||
Capacity = 1
|
||||
FullRounds = 8
|
||||
PartialRounds = 83
|
||||
MDS = [[3, 1, 1], [1, -1, 1], [1, 1, -2]]
|
||||
RoundKeys = [
|
||||
[
|
||||
2950795762459345168613727575620414179244544320470208355568817838579231751791,
|
||||
1587446564224215276866294500450702039420286416111469274423465069420553242820,
|
||||
1645965921169490687904413452218868659025437693527479459426157555728339600137,
|
||||
],
|
||||
[
|
||||
2782373324549879794752287702905278018819686065818504085638398966973694145741,
|
||||
3409172630025222641379726933524480516420204828329395644967085131392375707302,
|
||||
2379053116496905638239090788901387719228422033660130943198035907032739387135,
|
||||
],
|
||||
[
|
||||
2570819397480941104144008784293466051718826502582588529995520356691856497111,
|
||||
3546220846133880637977653625763703334841539452343273304410918449202580719746,
|
||||
2720682389492889709700489490056111332164748138023159726590726667539759963454,
|
||||
],
|
||||
[
|
||||
1899653471897224903834726250400246354200311275092866725547887381599836519005,
|
||||
2369443697923857319844855392163763375394720104106200469525915896159690979559,
|
||||
2354174693689535854311272135513626412848402744119855553970180659094265527996,
|
||||
],
|
||||
[
|
||||
2404084503073127963385083467393598147276436640877011103379112521338973185443,
|
||||
950320777137731763811524327595514151340412860090489448295239456547370725376,
|
||||
2121140748740143694053732746913428481442990369183417228688865837805149503386,
|
||||
],
|
||||
[
|
||||
2372065044800422557577242066480215868569521938346032514014152523102053709709,
|
||||
2618497439310693947058545060953893433487994458443568169824149550389484489896,
|
||||
3518297267402065742048564133910509847197496119850246255805075095266319996916,
|
||||
],
|
||||
[
|
||||
340529752683340505065238931581518232901634742162506851191464448040657139775,
|
||||
1954876811294863748406056845662382214841467408616109501720437541211031966538,
|
||||
813813157354633930267029888722341725864333883175521358739311868164460385261,
|
||||
],
|
||||
[
|
||||
71901595776070443337150458310956362034911936706490730914901986556638720031,
|
||||
2789761472166115462625363403490399263810962093264318361008954888847594113421,
|
||||
2628791615374802560074754031104384456692791616314774034906110098358135152410,
|
||||
],
|
||||
[
|
||||
3617032588734559635167557152518265808024917503198278888820567553943986939719,
|
||||
2624012360209966117322788103333497793082705816015202046036057821340914061980,
|
||||
149101987103211771991327927827692640556911620408176100290586418839323044234,
|
||||
],
|
||||
[
|
||||
1039927963829140138166373450440320262590862908847727961488297105916489431045,
|
||||
2213946951050724449162431068646025833746639391992751674082854766704900195669,
|
||||
2792724903541814965769131737117981991997031078369482697195201969174353468597,
|
||||
],
|
||||
[
|
||||
3212031629728871219804596347439383805499808476303618848198208101593976279441,
|
||||
3343514080098703935339621028041191631325798327656683100151836206557453199613,
|
||||
614054702436541219556958850933730254992710988573177298270089989048553060199,
|
||||
],
|
||||
[
|
||||
148148081026449726283933484730968827750202042869875329032965774667206931170,
|
||||
1158283532103191908366672518396366136968613180867652172211392033571980848414,
|
||||
1032400527342371389481069504520755916075559110755235773196747439146396688513,
|
||||
],
|
||||
[
|
||||
806900704622005851310078578853499250941978435851598088619290797134710613736,
|
||||
462498083559902778091095573017508352472262817904991134671058825705968404510,
|
||||
1003580119810278869589347418043095667699674425582646347949349245557449452503,
|
||||
],
|
||||
[
|
||||
619074932220101074089137133998298830285661916867732916607601635248249357793,
|
||||
2635090520059500019661864086615522409798872905401305311748231832709078452746,
|
||||
978252636251682252755279071140187792306115352460774007308726210405257135181,
|
||||
],
|
||||
[
|
||||
1766912167973123409669091967764158892111310474906691336473559256218048677083,
|
||||
1663265127259512472182980890707014969235283233442916350121860684522654120381,
|
||||
3532407621206959585000336211742670185380751515636605428496206887841428074250,
|
||||
],
|
||||
[
|
||||
2507023127157093845256722098502856938353143387711652912931112668310034975446,
|
||||
3321152907858462102434883844787153373036767230808678981306827073335525034593,
|
||||
3039253036806065280643845548147711477270022154459620569428286684179698125661,
|
||||
],
|
||||
[
|
||||
103480338868480851881924519768416587261556021758163719199282794248762465380,
|
||||
2394049781357087698434751577708655768465803975478348134669006211289636928495,
|
||||
2660531560345476340796109810821127229446538730404600368347902087220064379579,
|
||||
],
|
||||
[
|
||||
3603166934034556203649050570865466556260359798872408576857928196141785055563,
|
||||
1553799760191949768532188139643704561532896296986025007089826672890485412324,
|
||||
2744284717053657689091306578463476341218866418732695211367062598446038965164,
|
||||
],
|
||||
[
|
||||
320745764922149897598257794663594419839885234101078803811049904310835548856,
|
||||
979382242100682161589753881721708883681034024104145498709287731138044566302,
|
||||
1860426855810549882740147175136418997351054138609396651615467358416651354991,
|
||||
],
|
||||
[
|
||||
336173081054369235994909356892506146234495707857220254489443629387613956145,
|
||||
1632470326779699229772327605759783482411227247311431865655466227711078175883,
|
||||
921958250077481394074960433988881176409497663777043304881055317463712938502,
|
||||
],
|
||||
[
|
||||
3034358982193370602048539901033542101022185309652879937418114324899281842797,
|
||||
25626282149517463867572353922222474817434101087272320606729439087234878607,
|
||||
3002662261401575565838149305485737102400501329139562227180277188790091853682,
|
||||
],
|
||||
[
|
||||
2939684373453383817196521641512509179310654199629514917426341354023324109367,
|
||||
1076484609897998179434851570277297233169621096172424141759873688902355505136,
|
||||
2575095284833160494841112025725243274091830284746697961080467506739203605049,
|
||||
],
|
||||
[
|
||||
3565075264617591783581665711620369529657840830498005563542124551465195621851,
|
||||
2197016502533303822395077038351174326125210255869204501838837289716363437993,
|
||||
331415322883530754594261416546036195982886300052707474899691116664327869405,
|
||||
],
|
||||
[
|
||||
1935011233711290003793244296594669823169522055520303479680359990463281661839,
|
||||
3495901467168087413996941216661589517270845976538454329511167073314577412322,
|
||||
954195417117133246453562983448451025087661597543338750600301835944144520375,
|
||||
],
|
||||
[
|
||||
1271840477709992894995746871435810599280944810893784031132923384456797925777,
|
||||
2565310762274337662754531859505158700827688964841878141121196528015826671847,
|
||||
3365022288251637014588279139038152521653896670895105540140002607272936852513,
|
||||
],
|
||||
[
|
||||
1660592021628965529963974299647026602622092163312666588591285654477111176051,
|
||||
970104372286014048279296575474974982288801187216974504035759997141059513421,
|
||||
2617024574317953753849168721871770134225690844968986289121504184985993971227,
|
||||
],
|
||||
[
|
||||
999899815343607746071464113462778273556695659506865124478430189024755832262,
|
||||
2228536129413411161615629030408828764980855956560026807518714080003644769896,
|
||||
2701953891198001564547196795777701119629537795442025393867364730330476403227,
|
||||
],
|
||||
[
|
||||
837078355588159388741598313782044128527494922918203556465116291436461597853,
|
||||
2121749601840466143704862369657561429793951309962582099604848281796392359214,
|
||||
771812260179247428733132708063116523892339056677915387749121983038690154755,
|
||||
],
|
||||
[
|
||||
3317336423132806446086732225036532603224267214833263122557471741829060578219,
|
||||
481570067997721834712647566896657604857788523050900222145547508314620762046,
|
||||
242195042559343964206291740270858862066153636168162642380846129622127460192,
|
||||
],
|
||||
[
|
||||
2855462178889999218204481481614105202770810647859867354506557827319138379686,
|
||||
3525521107148375040131784770413887305850308357895464453970651672160034885202,
|
||||
1320839531502392535964065058804908871811967681250362364246430459003920305799,
|
||||
],
|
||||
[
|
||||
2514191518588387125173345107242226637171897291221681115249521904869763202419,
|
||||
2798335750958827619666318316247381695117827718387653874070218127140615157902,
|
||||
2808467767967035643407948058486565877867906577474361783201337540214875566395,
|
||||
],
|
||||
[
|
||||
3551834385992706206273955480294669176699286104229279436819137165202231595747,
|
||||
1219439673853113792340300173186247996249367102884530407862469123523013083971,
|
||||
761519904537984520554247997444508040636526566551719396202550009393012691157,
|
||||
],
|
||||
[
|
||||
3355402549169351700500518865338783382387571349497391475317206324155237401353,
|
||||
199541098009731541347317515995192175813554789571447733944970283654592727138,
|
||||
192100490643078165121235261796864975568292640203635147901612231594408079071,
|
||||
],
|
||||
[
|
||||
1187019357602953326192019968809486933768550466167033084944727938441427050581,
|
||||
189525349641911362389041124808934468936759383310282010671081989585219065700,
|
||||
2831653363992091308880573627558515686245403755586311978724025292003353336665,
|
||||
],
|
||||
[
|
||||
2052859812632218952608271535089179639890275494426396974475479657192657094698,
|
||||
1670756178709659908159049531058853320846231785448204274277900022176591811072,
|
||||
3538757242013734574731807289786598937548399719866320954894004830207085723125,
|
||||
],
|
||||
[
|
||||
710549042741321081781917034337800036872214466705318638023070812391485261299,
|
||||
2345013122330545298606028187653996682275206910242635100920038943391319595180,
|
||||
3528369671971445493932880023233332035122954362711876290904323783426765912206,
|
||||
],
|
||||
[
|
||||
1167120829038120978297497195837406760848728897181138760506162680655977700764,
|
||||
3073243357129146594530765548901087443775563058893907738967898816092270628884,
|
||||
378514724418106317738164464176041649567501099164061863402473942795977719726,
|
||||
],
|
||||
[
|
||||
333391138410406330127594722511180398159664250722328578952158227406762627796,
|
||||
1727570175639917398410201375510924114487348765559913502662122372848626931905,
|
||||
968312190621809249603425066974405725769739606059422769908547372904403793174,
|
||||
],
|
||||
[
|
||||
360659316299446405855194688051178331671817370423873014757323462844775818348,
|
||||
1386580151907705298970465943238806620109618995410132218037375811184684929291,
|
||||
3604888328937389309031638299660239238400230206645344173700074923133890528967,
|
||||
],
|
||||
[
|
||||
2496185632263372962152518155651824899299616724241852816983268163379540137546,
|
||||
486538168871046887467737983064272608432052269868418721234810979756540672990,
|
||||
1558415498960552213241704009433360128041672577274390114589014204605400783336,
|
||||
],
|
||||
[
|
||||
3512058327686147326577190314835092911156317204978509183234511559551181053926,
|
||||
2235429387083113882635494090887463486491842634403047716936833563914243946191,
|
||||
1290896777143878193192832813769470418518651727840187056683408155503813799882,
|
||||
],
|
||||
[
|
||||
1143310336918357319571079551779316654556781203013096026972411429993634080835,
|
||||
3235435208525081966062419599803346573407862428113723170955762956243193422118,
|
||||
1293239921425673430660897025143433077974838969258268884994339615096356996604,
|
||||
],
|
||||
[
|
||||
236252269127612784685426260840574970698541177557674806964960352572864382971,
|
||||
1733907592497266237374827232200506798207318263912423249709509725341212026275,
|
||||
302004309771755665128395814807589350526779835595021835389022325987048089868,
|
||||
],
|
||||
[
|
||||
3018926838139221755384801385583867283206879023218491758435446265703006270945,
|
||||
39701437664873825906031098349904330565195980985885489447836580931425171297,
|
||||
908381723021746969965674308809436059628307487140174335882627549095646509778,
|
||||
],
|
||||
[
|
||||
219062858908229855064136253265968615354041842047384625689776811853821594358,
|
||||
1283129863776453589317845316917890202859466483456216900835390291449830275503,
|
||||
418512623547417594896140369190919231877873410935689672661226540908900544012,
|
||||
],
|
||||
[
|
||||
1792181590047131972851015200157890246436013346535432437041535789841136268632,
|
||||
370546432987510607338044736824316856592558876687225326692366316978098770516,
|
||||
3323437805230586112013581113386626899534419826098235300155664022709435756946,
|
||||
],
|
||||
[
|
||||
910076621742039763058481476739499965761942516177975130656340375573185415877,
|
||||
1762188042455633427137702520675816545396284185254002959309669405982213803405,
|
||||
2186362253913140345102191078329764107619534641234549431429008219905315900520,
|
||||
],
|
||||
[
|
||||
2230647725927681765419218738218528849146504088716182944327179019215826045083,
|
||||
1069243907556644434301190076451112491469636357133398376850435321160857761825,
|
||||
2695241469149243992683268025359863087303400907336026926662328156934068747593,
|
||||
],
|
||||
[
|
||||
1361519681544413849831669554199151294308350560528931040264950307931824877035,
|
||||
1339116632207878730171031743761550901312154740800549632983325427035029084904,
|
||||
790593524918851401449292693473498591068920069246127392274811084156907468875,
|
||||
],
|
||||
[
|
||||
2723400368331924254840192318398326090089058735091724263333980290765736363637,
|
||||
3457180265095920471443772463283225391927927225993685928066766687141729456030,
|
||||
1483675376954327086153452545475557749815683871577400883707749788555424847954,
|
||||
],
|
||||
[
|
||||
2926303836265506736227240325795090239680154099205721426928300056982414025239,
|
||||
543969119775473768170832347411484329362572550684421616624136244239799475526,
|
||||
237401230683847084256617415614300816373730178313253487575312839074042461932,
|
||||
],
|
||||
[
|
||||
844568412840391587862072008674263874021460074878949862892685736454654414423,
|
||||
151922054871708336050647150237534498235916969120198637893731715254687336644,
|
||||
1299332034710622815055321547569101119597030148120309411086203580212105652312,
|
||||
],
|
||||
[
|
||||
487046922649899823989594814663418784068895385009696501386459462815688122993,
|
||||
1104883249092599185744249485896585912845784382683240114120846423960548576851,
|
||||
1458388705536282069567179348797334876446380557083422364875248475157495514484,
|
||||
],
|
||||
[
|
||||
850248109622750774031817200193861444623975329881731864752464222442574976566,
|
||||
2885843173858536690032695698009109793537724845140477446409245651176355435722,
|
||||
3027068551635372249579348422266406787688980506275086097330568993357835463816,
|
||||
],
|
||||
[
|
||||
3231892723647447539926175383213338123506134054432701323145045438168976970994,
|
||||
1719080830641935421242626784132692936776388194122314954558418655725251172826,
|
||||
1172253756541066126131022537343350498482225068791630219494878195815226839450,
|
||||
],
|
||||
[
|
||||
1619232269633026603732619978083169293258272967781186544174521481891163985093,
|
||||
3495680684841853175973173610562400042003100419811771341346135531754869014567,
|
||||
1576161515913099892951745452471618612307857113799539794680346855318958552758,
|
||||
],
|
||||
[
|
||||
2618326122974253423403350731396350223238201817594761152626832144510903048529,
|
||||
2696245132758436974032479782852265185094623165224532063951287925001108567649,
|
||||
930116505665110070247395429730201844026054810856263733273443066419816003444,
|
||||
],
|
||||
[
|
||||
2786389174502246248523918824488629229455088716707062764363111940462137404076,
|
||||
1555260846425735320214671887347115247546042526197895180675436886484523605116,
|
||||
2306241912153325247392671742757902161446877415586158295423293240351799505917,
|
||||
],
|
||||
[
|
||||
411529621724849932999694270803131456243889635467661223241617477462914950626,
|
||||
1542495485262286701469125140275904136434075186064076910329015697714211835205,
|
||||
1853045663799041100600825096887578544265580718909350942241802897995488264551,
|
||||
],
|
||||
[
|
||||
2963055259497271220202739837493041799968576111953080503132045092194513937286,
|
||||
2303806870349915764285872605046527036748108533406243381676768310692344456050,
|
||||
2622104986201990620910286730213140904984256464479840856728424375142929278875,
|
||||
],
|
||||
[
|
||||
2369987021925266811581727383184031736927816625797282287927222602539037105864,
|
||||
285070227712021899602056480426671736057274017903028992288878116056674401781,
|
||||
3034087076179360957800568733595959058628497428787907887933697691951454610691,
|
||||
],
|
||||
[
|
||||
469095854351700119980323115747590868855368701825706298740201488006320881056,
|
||||
360001976264385426746283365024817520563236378289230404095383746911725100012,
|
||||
3438709327109021347267562000879503009590697221730578667498351600602230296178,
|
||||
],
|
||||
[
|
||||
63573904800572228121671659287593650438456772568903228287754075619928214969,
|
||||
3470881855042989871434874691030920672110111605547839662680968354703074556970,
|
||||
724559311507950497340993415408274803001166693839947519425501269424891465492,
|
||||
],
|
||||
[
|
||||
880409284677518997550768549487344416321062350742831373397603704465823658986,
|
||||
6876255662475867703077362872097208259197756317287339941435193538565586230,
|
||||
2701916445133770775447884812906226786217969545216086200932273680400909154638,
|
||||
],
|
||||
[
|
||||
425152119158711585559310064242720816611629181537672850898056934507216982586,
|
||||
1475552998258917706756737045704649573088377604240716286977690565239187213744,
|
||||
2413772448122400684309006716414417978370152271397082147158000439863002593561,
|
||||
],
|
||||
[
|
||||
392160855822256520519339260245328807036619920858503984710539815951012864164,
|
||||
1075036996503791536261050742318169965707018400307026402939804424927087093987,
|
||||
2176439430328703902070742432016450246365760303014562857296722712989275658921,
|
||||
],
|
||||
[
|
||||
1413865976587623331051814207977382826721471106513581745229680113383908569693,
|
||||
4879283427490523253696177116563427032332223531862961281430108575019551814,
|
||||
3392583297537374046875199552977614390492290683707960975137418536812266544902,
|
||||
],
|
||||
[
|
||||
3600854486849487646325182927019642276644093512133907046667282144129939150983,
|
||||
2779924664161372134024229593301361846129279572186444474616319283535189797834,
|
||||
2722699960903170449291146429799738181514821447014433304730310678334403972040,
|
||||
],
|
||||
[
|
||||
819109815049226540285781191874507704729062681836086010078910930707209464699,
|
||||
3046121243742768013822760785918001632929744274211027071381357122228091333823,
|
||||
1339019590803056172509793134119156250729668216522001157582155155947567682278,
|
||||
],
|
||||
[
|
||||
1933279639657506214789316403763326578443023901555983256955812717638093967201,
|
||||
2138221547112520744699126051903811860205771600821672121643894708182292213541,
|
||||
2694713515543641924097704224170357995809887124438248292930846280951601597065,
|
||||
],
|
||||
[
|
||||
2471734202930133750093618989223585244499567111661178960753938272334153710615,
|
||||
504903761112092757611047718215309856203214372330635774577409639907729993533,
|
||||
1943979703748281357156510253941035712048221353507135074336243405478613241290,
|
||||
],
|
||||
[
|
||||
684525210957572142559049112233609445802004614280157992196913315652663518936,
|
||||
1705585400798782397786453706717059483604368413512485532079242223503960814508,
|
||||
192429517716023021556170942988476050278432319516032402725586427701913624665,
|
||||
],
|
||||
[
|
||||
1586493702243128040549584165333371192888583026298039652930372758731750166765,
|
||||
686072673323546915014972146032384917012218151266600268450347114036285993377,
|
||||
3464340397998075738891129996710075228740496767934137465519455338004332839215,
|
||||
],
|
||||
[
|
||||
2805249176617071054530589390406083958753103601524808155663551392362371834663,
|
||||
667746464250968521164727418691487653339733392025160477655836902744186489526,
|
||||
1131527712905109997177270289411406385352032457456054589588342450404257139778,
|
||||
],
|
||||
[
|
||||
1908969485750011212309284349900149072003218505891252313183123635318886241171,
|
||||
1025257076985551890132050019084873267454083056307650830147063480409707787695,
|
||||
2153175291918371429502545470578981828372846236838301412119329786849737957977,
|
||||
],
|
||||
[
|
||||
3410257749736714576487217882785226905621212230027780855361670645857085424384,
|
||||
3442969106887588154491488961893254739289120695377621434680934888062399029952,
|
||||
3029953900235731770255937704976720759948880815387104275525268727341390470237,
|
||||
],
|
||||
[
|
||||
85453456084781138713939104192561924536933417707871501802199311333127894466,
|
||||
2730629666577257820220329078741301754580009106438115341296453318350676425129,
|
||||
178242450661072967256438102630920745430303027840919213764087927763335940415,
|
||||
],
|
||||
[
|
||||
2844589222514708695700541363167856718216388819406388706818431442998498677557,
|
||||
3547876269219141094308889387292091231377253967587961309624916269569559952944,
|
||||
2525005406762984211707203144785482908331876505006839217175334833739957826850,
|
||||
],
|
||||
[
|
||||
3096397013555211396701910432830904669391580557191845136003938801598654871345,
|
||||
574424067119200181933992948252007230348512600107123873197603373898923821490,
|
||||
1714030696055067278349157346067719307863507310709155690164546226450579547098,
|
||||
],
|
||||
[
|
||||
2339895272202694698739231405357972261413383527237194045718815176814132612501,
|
||||
3562501318971895161271663840954705079797767042115717360959659475564651685069,
|
||||
69069358687197963617161747606993436483967992689488259107924379545671193749,
|
||||
],
|
||||
[
|
||||
2614502738369008850475068874731531583863538486212691941619835266611116051561,
|
||||
655247349763023251625727726218660142895322325659927266813592114640858573566,
|
||||
2305235672527595714255517865498269719545193172975330668070873705108690670678,
|
||||
],
|
||||
[
|
||||
926416070297755413261159098243058134401665060349723804040714357642180531931,
|
||||
866523735635840246543516964237513287099659681479228450791071595433217821460,
|
||||
2284334068466681424919271582037156124891004191915573957556691163266198707693,
|
||||
],
|
||||
[
|
||||
1812588309302477291425732810913354633465435706480768615104211305579383928792,
|
||||
2836899808619013605432050476764608707770404125005720004551836441247917488507,
|
||||
2989087789022865112405242078196235025698647423649950459911546051695688370523,
|
||||
],
|
||||
[
|
||||
68056284404189102136488263779598243992465747932368669388126367131855404486,
|
||||
505425339250887519581119854377342241317528319745596963584548343662758204398,
|
||||
2118963546856545068961709089296976921067035227488975882615462246481055679215,
|
||||
],
|
||||
[
|
||||
2253872596319969096156004495313034590996995209785432485705134570745135149681,
|
||||
1625090409149943603241183848936692198923183279116014478406452426158572703264,
|
||||
179139838844452470348634657368199622305888473747024389514258107503778442495,
|
||||
],
|
||||
[
|
||||
1567067018147735642071130442904093290030432522257811793540290101391210410341,
|
||||
2737301854006865242314806979738760349397411136469975337509958305470398783585,
|
||||
3002738216460904473515791428798860225499078134627026021350799206894618186256,
|
||||
],
|
||||
[
|
||||
374029488099466837453096950537275565120689146401077127482884887409712315162,
|
||||
973403256517481077805460710540468856199855789930951602150773500862180885363,
|
||||
2691967457038172130555117632010860984519926022632800605713473799739632878867,
|
||||
],
|
||||
[
|
||||
3515906794910381201365530594248181418811879320679684239326734893975752012109,
|
||||
148057579455448384062325089530558091463206199724854022070244924642222283388,
|
||||
1541588700238272710315890873051237741033408846596322948443180470429851502842,
|
||||
],
|
||||
[
|
||||
147013865879011936545137344076637170977925826031496203944786839068852795297,
|
||||
2630278389304735265620281704608245039972003761509102213752997636382302839857,
|
||||
1359048670759642844930007747955701205155822111403150159614453244477853867621,
|
||||
],
|
||||
[
|
||||
2438984569205812336319229336885480537793786558293523767186829418969842616677,
|
||||
2137792255841525507649318539501906353254503076308308692873313199435029594138,
|
||||
2262318076430740712267739371170174514379142884859595360065535117601097652755,
|
||||
],
|
||||
[
|
||||
2792703718581084537295613508201818489836796608902614779596544185252826291584,
|
||||
2294173715793292812015960640392421991604150133581218254866878921346561546149,
|
||||
2770011224727997178743274791849308200493823127651418989170761007078565678171,
|
||||
],
|
||||
]
|
||||
559
test/stark/poseidon/poseidon4.txt
Normal file
559
test/stark/poseidon/poseidon4.txt
Normal file
@@ -0,0 +1,559 @@
|
||||
Rate = 3
|
||||
Capacity = 1
|
||||
FullRounds = 8
|
||||
PartialRounds = 84
|
||||
MDS = [[2, 1, 1, 1], [1, 1, 1, 1], [1, 1, 0, 1], [1, 1, 1, -1]]
|
||||
RoundKeys = [
|
||||
[
|
||||
2950795762459345168613727575620414179244544320470208355568817838579231751791,
|
||||
1587446564224215276866294500450702039420286416111469274423465069420553242820,
|
||||
1645965921169490687904413452218868659025437693527479459426157555728339600137,
|
||||
2782373324549879794752287702905278018819686065818504085638398966973694145741,
|
||||
],
|
||||
[
|
||||
3409172630025222641379726933524480516420204828329395644967085131392375707302,
|
||||
2379053116496905638239090788901387719228422033660130943198035907032739387135,
|
||||
2570819397480941104144008784293466051718826502582588529995520356691856497111,
|
||||
3546220846133880637977653625763703334841539452343273304410918449202580719746,
|
||||
],
|
||||
[
|
||||
2720682389492889709700489490056111332164748138023159726590726667539759963454,
|
||||
1899653471897224903834726250400246354200311275092866725547887381599836519005,
|
||||
2369443697923857319844855392163763375394720104106200469525915896159690979559,
|
||||
2354174693689535854311272135513626412848402744119855553970180659094265527996,
|
||||
],
|
||||
[
|
||||
2404084503073127963385083467393598147276436640877011103379112521338973185443,
|
||||
950320777137731763811524327595514151340412860090489448295239456547370725376,
|
||||
2121140748740143694053732746913428481442990369183417228688865837805149503386,
|
||||
2372065044800422557577242066480215868569521938346032514014152523102053709709,
|
||||
],
|
||||
[
|
||||
2618497439310693947058545060953893433487994458443568169824149550389484489896,
|
||||
3518297267402065742048564133910509847197496119850246255805075095266319996916,
|
||||
340529752683340505065238931581518232901634742162506851191464448040657139775,
|
||||
1954876811294863748406056845662382214841467408616109501720437541211031966538,
|
||||
],
|
||||
[
|
||||
813813157354633930267029888722341725864333883175521358739311868164460385261,
|
||||
71901595776070443337150458310956362034911936706490730914901986556638720031,
|
||||
2789761472166115462625363403490399263810962093264318361008954888847594113421,
|
||||
2628791615374802560074754031104384456692791616314774034906110098358135152410,
|
||||
],
|
||||
[
|
||||
3617032588734559635167557152518265808024917503198278888820567553943986939719,
|
||||
2624012360209966117322788103333497793082705816015202046036057821340914061980,
|
||||
149101987103211771991327927827692640556911620408176100290586418839323044234,
|
||||
1039927963829140138166373450440320262590862908847727961488297105916489431045,
|
||||
],
|
||||
[
|
||||
2213946951050724449162431068646025833746639391992751674082854766704900195669,
|
||||
2792724903541814965769131737117981991997031078369482697195201969174353468597,
|
||||
3212031629728871219804596347439383805499808476303618848198208101593976279441,
|
||||
3343514080098703935339621028041191631325798327656683100151836206557453199613,
|
||||
],
|
||||
[
|
||||
614054702436541219556958850933730254992710988573177298270089989048553060199,
|
||||
148148081026449726283933484730968827750202042869875329032965774667206931170,
|
||||
1158283532103191908366672518396366136968613180867652172211392033571980848414,
|
||||
1032400527342371389481069504520755916075559110755235773196747439146396688513,
|
||||
],
|
||||
[
|
||||
806900704622005851310078578853499250941978435851598088619290797134710613736,
|
||||
462498083559902778091095573017508352472262817904991134671058825705968404510,
|
||||
1003580119810278869589347418043095667699674425582646347949349245557449452503,
|
||||
619074932220101074089137133998298830285661916867732916607601635248249357793,
|
||||
],
|
||||
[
|
||||
2635090520059500019661864086615522409798872905401305311748231832709078452746,
|
||||
978252636251682252755279071140187792306115352460774007308726210405257135181,
|
||||
1766912167973123409669091967764158892111310474906691336473559256218048677083,
|
||||
1663265127259512472182980890707014969235283233442916350121860684522654120381,
|
||||
],
|
||||
[
|
||||
3532407621206959585000336211742670185380751515636605428496206887841428074250,
|
||||
2507023127157093845256722098502856938353143387711652912931112668310034975446,
|
||||
3321152907858462102434883844787153373036767230808678981306827073335525034593,
|
||||
3039253036806065280643845548147711477270022154459620569428286684179698125661,
|
||||
],
|
||||
[
|
||||
103480338868480851881924519768416587261556021758163719199282794248762465380,
|
||||
2394049781357087698434751577708655768465803975478348134669006211289636928495,
|
||||
2660531560345476340796109810821127229446538730404600368347902087220064379579,
|
||||
3603166934034556203649050570865466556260359798872408576857928196141785055563,
|
||||
],
|
||||
[
|
||||
1553799760191949768532188139643704561532896296986025007089826672890485412324,
|
||||
2744284717053657689091306578463476341218866418732695211367062598446038965164,
|
||||
320745764922149897598257794663594419839885234101078803811049904310835548856,
|
||||
979382242100682161589753881721708883681034024104145498709287731138044566302,
|
||||
],
|
||||
[
|
||||
1860426855810549882740147175136418997351054138609396651615467358416651354991,
|
||||
336173081054369235994909356892506146234495707857220254489443629387613956145,
|
||||
1632470326779699229772327605759783482411227247311431865655466227711078175883,
|
||||
921958250077481394074960433988881176409497663777043304881055317463712938502,
|
||||
],
|
||||
[
|
||||
3034358982193370602048539901033542101022185309652879937418114324899281842797,
|
||||
25626282149517463867572353922222474817434101087272320606729439087234878607,
|
||||
3002662261401575565838149305485737102400501329139562227180277188790091853682,
|
||||
2939684373453383817196521641512509179310654199629514917426341354023324109367,
|
||||
],
|
||||
[
|
||||
1076484609897998179434851570277297233169621096172424141759873688902355505136,
|
||||
2575095284833160494841112025725243274091830284746697961080467506739203605049,
|
||||
3565075264617591783581665711620369529657840830498005563542124551465195621851,
|
||||
2197016502533303822395077038351174326125210255869204501838837289716363437993,
|
||||
],
|
||||
[
|
||||
331415322883530754594261416546036195982886300052707474899691116664327869405,
|
||||
1935011233711290003793244296594669823169522055520303479680359990463281661839,
|
||||
3495901467168087413996941216661589517270845976538454329511167073314577412322,
|
||||
954195417117133246453562983448451025087661597543338750600301835944144520375,
|
||||
],
|
||||
[
|
||||
1271840477709992894995746871435810599280944810893784031132923384456797925777,
|
||||
2565310762274337662754531859505158700827688964841878141121196528015826671847,
|
||||
3365022288251637014588279139038152521653896670895105540140002607272936852513,
|
||||
1660592021628965529963974299647026602622092163312666588591285654477111176051,
|
||||
],
|
||||
[
|
||||
970104372286014048279296575474974982288801187216974504035759997141059513421,
|
||||
2617024574317953753849168721871770134225690844968986289121504184985993971227,
|
||||
999899815343607746071464113462778273556695659506865124478430189024755832262,
|
||||
2228536129413411161615629030408828764980855956560026807518714080003644769896,
|
||||
],
|
||||
[
|
||||
2701953891198001564547196795777701119629537795442025393867364730330476403227,
|
||||
837078355588159388741598313782044128527494922918203556465116291436461597853,
|
||||
2121749601840466143704862369657561429793951309962582099604848281796392359214,
|
||||
771812260179247428733132708063116523892339056677915387749121983038690154755,
|
||||
],
|
||||
[
|
||||
3317336423132806446086732225036532603224267214833263122557471741829060578219,
|
||||
481570067997721834712647566896657604857788523050900222145547508314620762046,
|
||||
242195042559343964206291740270858862066153636168162642380846129622127460192,
|
||||
2855462178889999218204481481614105202770810647859867354506557827319138379686,
|
||||
],
|
||||
[
|
||||
3525521107148375040131784770413887305850308357895464453970651672160034885202,
|
||||
1320839531502392535964065058804908871811967681250362364246430459003920305799,
|
||||
2514191518588387125173345107242226637171897291221681115249521904869763202419,
|
||||
2798335750958827619666318316247381695117827718387653874070218127140615157902,
|
||||
],
|
||||
[
|
||||
2808467767967035643407948058486565877867906577474361783201337540214875566395,
|
||||
3551834385992706206273955480294669176699286104229279436819137165202231595747,
|
||||
1219439673853113792340300173186247996249367102884530407862469123523013083971,
|
||||
761519904537984520554247997444508040636526566551719396202550009393012691157,
|
||||
],
|
||||
[
|
||||
3355402549169351700500518865338783382387571349497391475317206324155237401353,
|
||||
199541098009731541347317515995192175813554789571447733944970283654592727138,
|
||||
192100490643078165121235261796864975568292640203635147901612231594408079071,
|
||||
1187019357602953326192019968809486933768550466167033084944727938441427050581,
|
||||
],
|
||||
[
|
||||
189525349641911362389041124808934468936759383310282010671081989585219065700,
|
||||
2831653363992091308880573627558515686245403755586311978724025292003353336665,
|
||||
2052859812632218952608271535089179639890275494426396974475479657192657094698,
|
||||
1670756178709659908159049531058853320846231785448204274277900022176591811072,
|
||||
],
|
||||
[
|
||||
3538757242013734574731807289786598937548399719866320954894004830207085723125,
|
||||
710549042741321081781917034337800036872214466705318638023070812391485261299,
|
||||
2345013122330545298606028187653996682275206910242635100920038943391319595180,
|
||||
3528369671971445493932880023233332035122954362711876290904323783426765912206,
|
||||
],
|
||||
[
|
||||
1167120829038120978297497195837406760848728897181138760506162680655977700764,
|
||||
3073243357129146594530765548901087443775563058893907738967898816092270628884,
|
||||
378514724418106317738164464176041649567501099164061863402473942795977719726,
|
||||
333391138410406330127594722511180398159664250722328578952158227406762627796,
|
||||
],
|
||||
[
|
||||
1727570175639917398410201375510924114487348765559913502662122372848626931905,
|
||||
968312190621809249603425066974405725769739606059422769908547372904403793174,
|
||||
360659316299446405855194688051178331671817370423873014757323462844775818348,
|
||||
1386580151907705298970465943238806620109618995410132218037375811184684929291,
|
||||
],
|
||||
[
|
||||
3604888328937389309031638299660239238400230206645344173700074923133890528967,
|
||||
2496185632263372962152518155651824899299616724241852816983268163379540137546,
|
||||
486538168871046887467737983064272608432052269868418721234810979756540672990,
|
||||
1558415498960552213241704009433360128041672577274390114589014204605400783336,
|
||||
],
|
||||
[
|
||||
3512058327686147326577190314835092911156317204978509183234511559551181053926,
|
||||
2235429387083113882635494090887463486491842634403047716936833563914243946191,
|
||||
1290896777143878193192832813769470418518651727840187056683408155503813799882,
|
||||
1143310336918357319571079551779316654556781203013096026972411429993634080835,
|
||||
],
|
||||
[
|
||||
3235435208525081966062419599803346573407862428113723170955762956243193422118,
|
||||
1293239921425673430660897025143433077974838969258268884994339615096356996604,
|
||||
236252269127612784685426260840574970698541177557674806964960352572864382971,
|
||||
1733907592497266237374827232200506798207318263912423249709509725341212026275,
|
||||
],
|
||||
[
|
||||
302004309771755665128395814807589350526779835595021835389022325987048089868,
|
||||
3018926838139221755384801385583867283206879023218491758435446265703006270945,
|
||||
39701437664873825906031098349904330565195980985885489447836580931425171297,
|
||||
908381723021746969965674308809436059628307487140174335882627549095646509778,
|
||||
],
|
||||
[
|
||||
219062858908229855064136253265968615354041842047384625689776811853821594358,
|
||||
1283129863776453589317845316917890202859466483456216900835390291449830275503,
|
||||
418512623547417594896140369190919231877873410935689672661226540908900544012,
|
||||
1792181590047131972851015200157890246436013346535432437041535789841136268632,
|
||||
],
|
||||
[
|
||||
370546432987510607338044736824316856592558876687225326692366316978098770516,
|
||||
3323437805230586112013581113386626899534419826098235300155664022709435756946,
|
||||
910076621742039763058481476739499965761942516177975130656340375573185415877,
|
||||
1762188042455633427137702520675816545396284185254002959309669405982213803405,
|
||||
],
|
||||
[
|
||||
2186362253913140345102191078329764107619534641234549431429008219905315900520,
|
||||
2230647725927681765419218738218528849146504088716182944327179019215826045083,
|
||||
1069243907556644434301190076451112491469636357133398376850435321160857761825,
|
||||
2695241469149243992683268025359863087303400907336026926662328156934068747593,
|
||||
],
|
||||
[
|
||||
1361519681544413849831669554199151294308350560528931040264950307931824877035,
|
||||
1339116632207878730171031743761550901312154740800549632983325427035029084904,
|
||||
790593524918851401449292693473498591068920069246127392274811084156907468875,
|
||||
2723400368331924254840192318398326090089058735091724263333980290765736363637,
|
||||
],
|
||||
[
|
||||
3457180265095920471443772463283225391927927225993685928066766687141729456030,
|
||||
1483675376954327086153452545475557749815683871577400883707749788555424847954,
|
||||
2926303836265506736227240325795090239680154099205721426928300056982414025239,
|
||||
543969119775473768170832347411484329362572550684421616624136244239799475526,
|
||||
],
|
||||
[
|
||||
237401230683847084256617415614300816373730178313253487575312839074042461932,
|
||||
844568412840391587862072008674263874021460074878949862892685736454654414423,
|
||||
151922054871708336050647150237534498235916969120198637893731715254687336644,
|
||||
1299332034710622815055321547569101119597030148120309411086203580212105652312,
|
||||
],
|
||||
[
|
||||
487046922649899823989594814663418784068895385009696501386459462815688122993,
|
||||
1104883249092599185744249485896585912845784382683240114120846423960548576851,
|
||||
1458388705536282069567179348797334876446380557083422364875248475157495514484,
|
||||
850248109622750774031817200193861444623975329881731864752464222442574976566,
|
||||
],
|
||||
[
|
||||
2885843173858536690032695698009109793537724845140477446409245651176355435722,
|
||||
3027068551635372249579348422266406787688980506275086097330568993357835463816,
|
||||
3231892723647447539926175383213338123506134054432701323145045438168976970994,
|
||||
1719080830641935421242626784132692936776388194122314954558418655725251172826,
|
||||
],
|
||||
[
|
||||
1172253756541066126131022537343350498482225068791630219494878195815226839450,
|
||||
1619232269633026603732619978083169293258272967781186544174521481891163985093,
|
||||
3495680684841853175973173610562400042003100419811771341346135531754869014567,
|
||||
1576161515913099892951745452471618612307857113799539794680346855318958552758,
|
||||
],
|
||||
[
|
||||
2618326122974253423403350731396350223238201817594761152626832144510903048529,
|
||||
2696245132758436974032479782852265185094623165224532063951287925001108567649,
|
||||
930116505665110070247395429730201844026054810856263733273443066419816003444,
|
||||
2786389174502246248523918824488629229455088716707062764363111940462137404076,
|
||||
],
|
||||
[
|
||||
1555260846425735320214671887347115247546042526197895180675436886484523605116,
|
||||
2306241912153325247392671742757902161446877415586158295423293240351799505917,
|
||||
411529621724849932999694270803131456243889635467661223241617477462914950626,
|
||||
1542495485262286701469125140275904136434075186064076910329015697714211835205,
|
||||
],
|
||||
[
|
||||
1853045663799041100600825096887578544265580718909350942241802897995488264551,
|
||||
2963055259497271220202739837493041799968576111953080503132045092194513937286,
|
||||
2303806870349915764285872605046527036748108533406243381676768310692344456050,
|
||||
2622104986201990620910286730213140904984256464479840856728424375142929278875,
|
||||
],
|
||||
[
|
||||
2369987021925266811581727383184031736927816625797282287927222602539037105864,
|
||||
285070227712021899602056480426671736057274017903028992288878116056674401781,
|
||||
3034087076179360957800568733595959058628497428787907887933697691951454610691,
|
||||
469095854351700119980323115747590868855368701825706298740201488006320881056,
|
||||
],
|
||||
[
|
||||
360001976264385426746283365024817520563236378289230404095383746911725100012,
|
||||
3438709327109021347267562000879503009590697221730578667498351600602230296178,
|
||||
63573904800572228121671659287593650438456772568903228287754075619928214969,
|
||||
3470881855042989871434874691030920672110111605547839662680968354703074556970,
|
||||
],
|
||||
[
|
||||
724559311507950497340993415408274803001166693839947519425501269424891465492,
|
||||
880409284677518997550768549487344416321062350742831373397603704465823658986,
|
||||
6876255662475867703077362872097208259197756317287339941435193538565586230,
|
||||
2701916445133770775447884812906226786217969545216086200932273680400909154638,
|
||||
],
|
||||
[
|
||||
425152119158711585559310064242720816611629181537672850898056934507216982586,
|
||||
1475552998258917706756737045704649573088377604240716286977690565239187213744,
|
||||
2413772448122400684309006716414417978370152271397082147158000439863002593561,
|
||||
392160855822256520519339260245328807036619920858503984710539815951012864164,
|
||||
],
|
||||
[
|
||||
1075036996503791536261050742318169965707018400307026402939804424927087093987,
|
||||
2176439430328703902070742432016450246365760303014562857296722712989275658921,
|
||||
1413865976587623331051814207977382826721471106513581745229680113383908569693,
|
||||
4879283427490523253696177116563427032332223531862961281430108575019551814,
|
||||
],
|
||||
[
|
||||
3392583297537374046875199552977614390492290683707960975137418536812266544902,
|
||||
3600854486849487646325182927019642276644093512133907046667282144129939150983,
|
||||
2779924664161372134024229593301361846129279572186444474616319283535189797834,
|
||||
2722699960903170449291146429799738181514821447014433304730310678334403972040,
|
||||
],
|
||||
[
|
||||
819109815049226540285781191874507704729062681836086010078910930707209464699,
|
||||
3046121243742768013822760785918001632929744274211027071381357122228091333823,
|
||||
1339019590803056172509793134119156250729668216522001157582155155947567682278,
|
||||
1933279639657506214789316403763326578443023901555983256955812717638093967201,
|
||||
],
|
||||
[
|
||||
2138221547112520744699126051903811860205771600821672121643894708182292213541,
|
||||
2694713515543641924097704224170357995809887124438248292930846280951601597065,
|
||||
2471734202930133750093618989223585244499567111661178960753938272334153710615,
|
||||
504903761112092757611047718215309856203214372330635774577409639907729993533,
|
||||
],
|
||||
[
|
||||
1943979703748281357156510253941035712048221353507135074336243405478613241290,
|
||||
684525210957572142559049112233609445802004614280157992196913315652663518936,
|
||||
1705585400798782397786453706717059483604368413512485532079242223503960814508,
|
||||
192429517716023021556170942988476050278432319516032402725586427701913624665,
|
||||
],
|
||||
[
|
||||
1586493702243128040549584165333371192888583026298039652930372758731750166765,
|
||||
686072673323546915014972146032384917012218151266600268450347114036285993377,
|
||||
3464340397998075738891129996710075228740496767934137465519455338004332839215,
|
||||
2805249176617071054530589390406083958753103601524808155663551392362371834663,
|
||||
],
|
||||
[
|
||||
667746464250968521164727418691487653339733392025160477655836902744186489526,
|
||||
1131527712905109997177270289411406385352032457456054589588342450404257139778,
|
||||
1908969485750011212309284349900149072003218505891252313183123635318886241171,
|
||||
1025257076985551890132050019084873267454083056307650830147063480409707787695,
|
||||
],
|
||||
[
|
||||
2153175291918371429502545470578981828372846236838301412119329786849737957977,
|
||||
3410257749736714576487217882785226905621212230027780855361670645857085424384,
|
||||
3442969106887588154491488961893254739289120695377621434680934888062399029952,
|
||||
3029953900235731770255937704976720759948880815387104275525268727341390470237,
|
||||
],
|
||||
[
|
||||
85453456084781138713939104192561924536933417707871501802199311333127894466,
|
||||
2730629666577257820220329078741301754580009106438115341296453318350676425129,
|
||||
178242450661072967256438102630920745430303027840919213764087927763335940415,
|
||||
2844589222514708695700541363167856718216388819406388706818431442998498677557,
|
||||
],
|
||||
[
|
||||
3547876269219141094308889387292091231377253967587961309624916269569559952944,
|
||||
2525005406762984211707203144785482908331876505006839217175334833739957826850,
|
||||
3096397013555211396701910432830904669391580557191845136003938801598654871345,
|
||||
574424067119200181933992948252007230348512600107123873197603373898923821490,
|
||||
],
|
||||
[
|
||||
1714030696055067278349157346067719307863507310709155690164546226450579547098,
|
||||
2339895272202694698739231405357972261413383527237194045718815176814132612501,
|
||||
3562501318971895161271663840954705079797767042115717360959659475564651685069,
|
||||
69069358687197963617161747606993436483967992689488259107924379545671193749,
|
||||
],
|
||||
[
|
||||
2614502738369008850475068874731531583863538486212691941619835266611116051561,
|
||||
655247349763023251625727726218660142895322325659927266813592114640858573566,
|
||||
2305235672527595714255517865498269719545193172975330668070873705108690670678,
|
||||
926416070297755413261159098243058134401665060349723804040714357642180531931,
|
||||
],
|
||||
[
|
||||
866523735635840246543516964237513287099659681479228450791071595433217821460,
|
||||
2284334068466681424919271582037156124891004191915573957556691163266198707693,
|
||||
1812588309302477291425732810913354633465435706480768615104211305579383928792,
|
||||
2836899808619013605432050476764608707770404125005720004551836441247917488507,
|
||||
],
|
||||
[
|
||||
2989087789022865112405242078196235025698647423649950459911546051695688370523,
|
||||
68056284404189102136488263779598243992465747932368669388126367131855404486,
|
||||
505425339250887519581119854377342241317528319745596963584548343662758204398,
|
||||
2118963546856545068961709089296976921067035227488975882615462246481055679215,
|
||||
],
|
||||
[
|
||||
2253872596319969096156004495313034590996995209785432485705134570745135149681,
|
||||
1625090409149943603241183848936692198923183279116014478406452426158572703264,
|
||||
179139838844452470348634657368199622305888473747024389514258107503778442495,
|
||||
1567067018147735642071130442904093290030432522257811793540290101391210410341,
|
||||
],
|
||||
[
|
||||
2737301854006865242314806979738760349397411136469975337509958305470398783585,
|
||||
3002738216460904473515791428798860225499078134627026021350799206894618186256,
|
||||
374029488099466837453096950537275565120689146401077127482884887409712315162,
|
||||
973403256517481077805460710540468856199855789930951602150773500862180885363,
|
||||
],
|
||||
[
|
||||
2691967457038172130555117632010860984519926022632800605713473799739632878867,
|
||||
3515906794910381201365530594248181418811879320679684239326734893975752012109,
|
||||
148057579455448384062325089530558091463206199724854022070244924642222283388,
|
||||
1541588700238272710315890873051237741033408846596322948443180470429851502842,
|
||||
],
|
||||
[
|
||||
147013865879011936545137344076637170977925826031496203944786839068852795297,
|
||||
2630278389304735265620281704608245039972003761509102213752997636382302839857,
|
||||
1359048670759642844930007747955701205155822111403150159614453244477853867621,
|
||||
2438984569205812336319229336885480537793786558293523767186829418969842616677,
|
||||
],
|
||||
[
|
||||
2137792255841525507649318539501906353254503076308308692873313199435029594138,
|
||||
2262318076430740712267739371170174514379142884859595360065535117601097652755,
|
||||
2792703718581084537295613508201818489836796608902614779596544185252826291584,
|
||||
2294173715793292812015960640392421991604150133581218254866878921346561546149,
|
||||
],
|
||||
[
|
||||
2770011224727997178743274791849308200493823127651418989170761007078565678171,
|
||||
3321642244537785916275181932172303118112488081726311374164578600576901819844,
|
||||
3522708517589950573320671158134804505970724681591943826922697952040487655044,
|
||||
3417974441436557992524691506735790206623600049454586729879955931972546347402,
|
||||
],
|
||||
[
|
||||
175039333145381316571259690443853067809784261609912638686739799135919647022,
|
||||
1930713062131033273316869231148248962041053029742760224583505092759642967464,
|
||||
2971452932574554603554350185069538580257636405419430340233233400633251319042,
|
||||
2774781903758215341037075610938953949868289195845367046186588750871862784919,
|
||||
],
|
||||
[
|
||||
666516874137869653699423537799457099346460194092311952417454613224504932738,
|
||||
1900462225013533249140457703727169176351786259991305560412832202759625668041,
|
||||
2665631186082687279121709429531834469477679375137509769347092380798929714377,
|
||||
837840745988147279235494664091280091563355097569199320366973125128366540061,
|
||||
],
|
||||
[
|
||||
3391544118305848781823721719916289805455110924839794510205940718821197620955,
|
||||
2888553035909938253628892138500390690221493345071933642853222968481016605919,
|
||||
3386241569867597612447901482685846444743718781330869478721963580925825915450,
|
||||
1205126220630896984850042596877918177217334376800874965105642874206963597698,
|
||||
],
|
||||
[
|
||||
3590072615491710252422997155203204584659171612188004116415640739580250394190,
|
||||
692469013329617220154003334549812915100479873898898958632988703738125356983,
|
||||
1623178235190707102808841905143937367808788834203621005714003335195182126335,
|
||||
1972826180775011489122426045504602288576507493792470102803637471568052321297,
|
||||
],
|
||||
[
|
||||
3415141329098504418158191749675997877417539760075593313736376750580696083073,
|
||||
587811537889727046473915684463981273175495137461951211739526104349163747811,
|
||||
2523982964351069134084525951849317400231659428055762640605248929856135518199,
|
||||
2686176526711834950207666047281383173339057216783586039351834948812568447629,
|
||||
],
|
||||
[
|
||||
983144446441739425577690449774542566745526459152966545642451764143532586964,
|
||||
171558252019175695567663688494555626159399786667979998273792882504784080805,
|
||||
332337623010057542760158225837623039780806442976079546879646069338600179518,
|
||||
1264669683963885571544813806669118319675288608634733888843804451222546848295,
|
||||
],
|
||||
[
|
||||
2426165115815723668018318268486497504249785449504758403912155206515511627681,
|
||||
11387399609384288947733630450855186629703576293221897150193655994854764608,
|
||||
2541728569046079092074077754414781968906176513081761690404588216304985421091,
|
||||
47685947554980329431290582269851186106577733250761848107645535357326439312,
|
||||
],
|
||||
[
|
||||
472176388418187405374813530639596064799362505024895746833751173199773896628,
|
||||
2764298116617383397920343358525617195195060562266243809245480210157942112738,
|
||||
486863835068754002670800862273365477867695879270721744227071001883208334054,
|
||||
2973492686137102577527656941792991264994301121122130295965761350095846874635,
|
||||
],
|
||||
[
|
||||
178385615141132702906181473263873416748818415607305319148067639744074654009,
|
||||
533624640096756667052211553746016402543259206286603356120804827761339634127,
|
||||
819406716720171922688026098737835227857400444543748198788964759773510472096,
|
||||
531851793767260921861217458033110066464894334064526987603936107947006031387,
|
||||
],
|
||||
[
|
||||
3269709072483585277009748181134917746036523619604017812342933951952104134829,
|
||||
838191718603413598040249006803464503100808192944407407147899973659013630611,
|
||||
1574561296941310904780257598780779812250055948216417844567262310524022037406,
|
||||
551394354289003977607664358739006072556227894953233419144430578080352094737,
|
||||
],
|
||||
[
|
||||
445076790942318675726839050057337819004979443030540904213920669247413907302,
|
||||
1963946696292687224902912968478695543164747600779913024040878700455222386521,
|
||||
484284614181963381509745298932402076252103342403432879800905151752488144767,
|
||||
2240507606126946994415203252302826782042951346966859379502140796364876543253,
|
||||
],
|
||||
[
|
||||
3237135638753992982179886898758938279897590886053928839613434762582576319619,
|
||||
2334333034701915027889533058426879447140084891006486138782876488162658230991,
|
||||
14411091399844539897439754491034751977136685514851444574462584316609631592,
|
||||
1264480371592407258420308876448697804787923638319277097663041109464608464284,
|
||||
],
|
||||
[
|
||||
671929312763821646360589403212798993954209530574443543917757335777610372144,
|
||||
2513909805455654095962542944994577107405216428214873444765576238504714067396,
|
||||
870121102846043786263357605823753628974859886859187558617096145653709171231,
|
||||
399132620893316356411986266679786708905730068946836982293484206366500277754,
|
||||
],
|
||||
[
|
||||
2855046250836680633532995284655778407402587437073106249445470889390454667586,
|
||||
2063679741125384345396981490971605710211281905716315529671473143278849561151,
|
||||
1433753212258929925682201698758056443128516570551146995210728194816988328337,
|
||||
3334984763425011856632257855270507440816274246647423607159847074739331865077,
|
||||
],
|
||||
[
|
||||
337911293622078184850923533628334646725451591671907148383867096651211846605,
|
||||
559587005295238702015018022040357402231957131094636365177008701077975941644,
|
||||
885963059604819264377490633589388189646118257469490919900554134369512794660,
|
||||
1957748763518471091057032383332840331641373304981058387824598000170709016333,
|
||||
],
|
||||
[
|
||||
3175295982155056798972302481564899381103533409383494814704562889625572018450,
|
||||
498987160612401618114584726510347771865331516606886613019084323862447372555,
|
||||
947374835104260364630171441676101001841507588423166778786886198914150312958,
|
||||
906933977754491302438795274167251538820934378773708095543613756654712689280,
|
||||
],
|
||||
[
|
||||
2170116291766863179909957030577284618726490893598499117272497866180009722894,
|
||||
1801335399574515889082584621772588704763181408217893911806726119813067220453,
|
||||
1942500232535842474530840356353427989892065499159260166135596750084681859966,
|
||||
62936080219825306823124060587235998278756755377419521154040408253893795176,
|
||||
],
|
||||
[
|
||||
3091993939935137795359769774909373279950941171574748645375255810204590357753,
|
||||
1283528386884634267663661033944552098742115012555712906773586466375284501324,
|
||||
1581820717639229420476069802992937438655873471854930764425841549067913106065,
|
||||
2301986095388751633126546121528329200085681648876910655269533407603441046514,
|
||||
],
|
||||
[
|
||||
2850003828037698751961753862613545302539465803982364898225617297398939302949,
|
||||
48024691078494936445046366770271288984930221238071705874025261821606393528,
|
||||
1482336297033144958942154923925185950152551534403871620222916667536030875354,
|
||||
3081177564717719643771186007689458633949181485535169123213511264603782950049,
|
||||
],
|
||||
[
|
||||
3315701127039521853279746297714590495201061397709680410650043502532250578075,
|
||||
3514407611000441301995070394422463400067690470546731164089622325748803106020,
|
||||
368970178199930154322724953487299516224498421233447528815195701420122548537,
|
||||
584353160413525267849669053228533951552602295860601556035386665117717227391,
|
||||
],
|
||||
[
|
||||
752038702160385294706011538400822066722189014251268673051846350397729870418,
|
||||
3594041683498798688197194521326299097635429790757880308152971477196489335154,
|
||||
1367902435756906062215608264424138718742854099315395230911274560900857414183,
|
||||
1828549068951502746189364466794037234789986878381694857475972053743463890779,
|
||||
],
|
||||
[
|
||||
488172495141237210878388657234137733008417573114482400652274985829148564248,
|
||||
962906242461930394022372340919543491337923491322497419797555620396501785566,
|
||||
2275418085010046236619290386129138234541669589549771944697082317642065048898,
|
||||
1966395064658902622886154686288219600816893261614483533899715888994623208964,
|
||||
],
|
||||
[
|
||||
3496095878293416917311185659829821476802828534554531050412634978086916288609,
|
||||
3368478822390537245916137403277928093536087427951052230723275731232142463388,
|
||||
3397410259276620127103231993277518800970669191016277541098821699302368873803,
|
||||
2662600899665871010006649609856695263727220473364611552472965243032255906029,
|
||||
],
|
||||
]
|
||||
651
test/stark/poseidon/poseidon5.txt
Normal file
651
test/stark/poseidon/poseidon5.txt
Normal file
@@ -0,0 +1,651 @@
|
||||
Rate = 4
|
||||
Capacity = 1
|
||||
FullRounds = 8
|
||||
PartialRounds = 84
|
||||
MDS = [[3, 1, 1, 1, 1], [1, 2, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, -1, 1], [1, 1, 1, 1, -2]]
|
||||
RoundKeys = [
|
||||
[
|
||||
2950795762459345168613727575620414179244544320470208355568817838579231751791,
|
||||
1587446564224215276866294500450702039420286416111469274423465069420553242820,
|
||||
1645965921169490687904413452218868659025437693527479459426157555728339600137,
|
||||
2782373324549879794752287702905278018819686065818504085638398966973694145741,
|
||||
3409172630025222641379726933524480516420204828329395644967085131392375707302,
|
||||
],
|
||||
[
|
||||
2379053116496905638239090788901387719228422033660130943198035907032739387135,
|
||||
2570819397480941104144008784293466051718826502582588529995520356691856497111,
|
||||
3546220846133880637977653625763703334841539452343273304410918449202580719746,
|
||||
2720682389492889709700489490056111332164748138023159726590726667539759963454,
|
||||
1899653471897224903834726250400246354200311275092866725547887381599836519005,
|
||||
],
|
||||
[
|
||||
2369443697923857319844855392163763375394720104106200469525915896159690979559,
|
||||
2354174693689535854311272135513626412848402744119855553970180659094265527996,
|
||||
2404084503073127963385083467393598147276436640877011103379112521338973185443,
|
||||
950320777137731763811524327595514151340412860090489448295239456547370725376,
|
||||
2121140748740143694053732746913428481442990369183417228688865837805149503386,
|
||||
],
|
||||
[
|
||||
2372065044800422557577242066480215868569521938346032514014152523102053709709,
|
||||
2618497439310693947058545060953893433487994458443568169824149550389484489896,
|
||||
3518297267402065742048564133910509847197496119850246255805075095266319996916,
|
||||
340529752683340505065238931581518232901634742162506851191464448040657139775,
|
||||
1954876811294863748406056845662382214841467408616109501720437541211031966538,
|
||||
],
|
||||
[
|
||||
813813157354633930267029888722341725864333883175521358739311868164460385261,
|
||||
71901595776070443337150458310956362034911936706490730914901986556638720031,
|
||||
2789761472166115462625363403490399263810962093264318361008954888847594113421,
|
||||
2628791615374802560074754031104384456692791616314774034906110098358135152410,
|
||||
3617032588734559635167557152518265808024917503198278888820567553943986939719,
|
||||
],
|
||||
[
|
||||
2624012360209966117322788103333497793082705816015202046036057821340914061980,
|
||||
149101987103211771991327927827692640556911620408176100290586418839323044234,
|
||||
1039927963829140138166373450440320262590862908847727961488297105916489431045,
|
||||
2213946951050724449162431068646025833746639391992751674082854766704900195669,
|
||||
2792724903541814965769131737117981991997031078369482697195201969174353468597,
|
||||
],
|
||||
[
|
||||
3212031629728871219804596347439383805499808476303618848198208101593976279441,
|
||||
3343514080098703935339621028041191631325798327656683100151836206557453199613,
|
||||
614054702436541219556958850933730254992710988573177298270089989048553060199,
|
||||
148148081026449726283933484730968827750202042869875329032965774667206931170,
|
||||
1158283532103191908366672518396366136968613180867652172211392033571980848414,
|
||||
],
|
||||
[
|
||||
1032400527342371389481069504520755916075559110755235773196747439146396688513,
|
||||
806900704622005851310078578853499250941978435851598088619290797134710613736,
|
||||
462498083559902778091095573017508352472262817904991134671058825705968404510,
|
||||
1003580119810278869589347418043095667699674425582646347949349245557449452503,
|
||||
619074932220101074089137133998298830285661916867732916607601635248249357793,
|
||||
],
|
||||
[
|
||||
2635090520059500019661864086615522409798872905401305311748231832709078452746,
|
||||
978252636251682252755279071140187792306115352460774007308726210405257135181,
|
||||
1766912167973123409669091967764158892111310474906691336473559256218048677083,
|
||||
1663265127259512472182980890707014969235283233442916350121860684522654120381,
|
||||
3532407621206959585000336211742670185380751515636605428496206887841428074250,
|
||||
],
|
||||
[
|
||||
2507023127157093845256722098502856938353143387711652912931112668310034975446,
|
||||
3321152907858462102434883844787153373036767230808678981306827073335525034593,
|
||||
3039253036806065280643845548147711477270022154459620569428286684179698125661,
|
||||
103480338868480851881924519768416587261556021758163719199282794248762465380,
|
||||
2394049781357087698434751577708655768465803975478348134669006211289636928495,
|
||||
],
|
||||
[
|
||||
2660531560345476340796109810821127229446538730404600368347902087220064379579,
|
||||
3603166934034556203649050570865466556260359798872408576857928196141785055563,
|
||||
1553799760191949768532188139643704561532896296986025007089826672890485412324,
|
||||
2744284717053657689091306578463476341218866418732695211367062598446038965164,
|
||||
320745764922149897598257794663594419839885234101078803811049904310835548856,
|
||||
],
|
||||
[
|
||||
979382242100682161589753881721708883681034024104145498709287731138044566302,
|
||||
1860426855810549882740147175136418997351054138609396651615467358416651354991,
|
||||
336173081054369235994909356892506146234495707857220254489443629387613956145,
|
||||
1632470326779699229772327605759783482411227247311431865655466227711078175883,
|
||||
921958250077481394074960433988881176409497663777043304881055317463712938502,
|
||||
],
|
||||
[
|
||||
3034358982193370602048539901033542101022185309652879937418114324899281842797,
|
||||
25626282149517463867572353922222474817434101087272320606729439087234878607,
|
||||
3002662261401575565838149305485737102400501329139562227180277188790091853682,
|
||||
2939684373453383817196521641512509179310654199629514917426341354023324109367,
|
||||
1076484609897998179434851570277297233169621096172424141759873688902355505136,
|
||||
],
|
||||
[
|
||||
2575095284833160494841112025725243274091830284746697961080467506739203605049,
|
||||
3565075264617591783581665711620369529657840830498005563542124551465195621851,
|
||||
2197016502533303822395077038351174326125210255869204501838837289716363437993,
|
||||
331415322883530754594261416546036195982886300052707474899691116664327869405,
|
||||
1935011233711290003793244296594669823169522055520303479680359990463281661839,
|
||||
],
|
||||
[
|
||||
3495901467168087413996941216661589517270845976538454329511167073314577412322,
|
||||
954195417117133246453562983448451025087661597543338750600301835944144520375,
|
||||
1271840477709992894995746871435810599280944810893784031132923384456797925777,
|
||||
2565310762274337662754531859505158700827688964841878141121196528015826671847,
|
||||
3365022288251637014588279139038152521653896670895105540140002607272936852513,
|
||||
],
|
||||
[
|
||||
1660592021628965529963974299647026602622092163312666588591285654477111176051,
|
||||
970104372286014048279296575474974982288801187216974504035759997141059513421,
|
||||
2617024574317953753849168721871770134225690844968986289121504184985993971227,
|
||||
999899815343607746071464113462778273556695659506865124478430189024755832262,
|
||||
2228536129413411161615629030408828764980855956560026807518714080003644769896,
|
||||
],
|
||||
[
|
||||
2701953891198001564547196795777701119629537795442025393867364730330476403227,
|
||||
837078355588159388741598313782044128527494922918203556465116291436461597853,
|
||||
2121749601840466143704862369657561429793951309962582099604848281796392359214,
|
||||
771812260179247428733132708063116523892339056677915387749121983038690154755,
|
||||
3317336423132806446086732225036532603224267214833263122557471741829060578219,
|
||||
],
|
||||
[
|
||||
481570067997721834712647566896657604857788523050900222145547508314620762046,
|
||||
242195042559343964206291740270858862066153636168162642380846129622127460192,
|
||||
2855462178889999218204481481614105202770810647859867354506557827319138379686,
|
||||
3525521107148375040131784770413887305850308357895464453970651672160034885202,
|
||||
1320839531502392535964065058804908871811967681250362364246430459003920305799,
|
||||
],
|
||||
[
|
||||
2514191518588387125173345107242226637171897291221681115249521904869763202419,
|
||||
2798335750958827619666318316247381695117827718387653874070218127140615157902,
|
||||
2808467767967035643407948058486565877867906577474361783201337540214875566395,
|
||||
3551834385992706206273955480294669176699286104229279436819137165202231595747,
|
||||
1219439673853113792340300173186247996249367102884530407862469123523013083971,
|
||||
],
|
||||
[
|
||||
761519904537984520554247997444508040636526566551719396202550009393012691157,
|
||||
3355402549169351700500518865338783382387571349497391475317206324155237401353,
|
||||
199541098009731541347317515995192175813554789571447733944970283654592727138,
|
||||
192100490643078165121235261796864975568292640203635147901612231594408079071,
|
||||
1187019357602953326192019968809486933768550466167033084944727938441427050581,
|
||||
],
|
||||
[
|
||||
189525349641911362389041124808934468936759383310282010671081989585219065700,
|
||||
2831653363992091308880573627558515686245403755586311978724025292003353336665,
|
||||
2052859812632218952608271535089179639890275494426396974475479657192657094698,
|
||||
1670756178709659908159049531058853320846231785448204274277900022176591811072,
|
||||
3538757242013734574731807289786598937548399719866320954894004830207085723125,
|
||||
],
|
||||
[
|
||||
710549042741321081781917034337800036872214466705318638023070812391485261299,
|
||||
2345013122330545298606028187653996682275206910242635100920038943391319595180,
|
||||
3528369671971445493932880023233332035122954362711876290904323783426765912206,
|
||||
1167120829038120978297497195837406760848728897181138760506162680655977700764,
|
||||
3073243357129146594530765548901087443775563058893907738967898816092270628884,
|
||||
],
|
||||
[
|
||||
378514724418106317738164464176041649567501099164061863402473942795977719726,
|
||||
333391138410406330127594722511180398159664250722328578952158227406762627796,
|
||||
1727570175639917398410201375510924114487348765559913502662122372848626931905,
|
||||
968312190621809249603425066974405725769739606059422769908547372904403793174,
|
||||
360659316299446405855194688051178331671817370423873014757323462844775818348,
|
||||
],
|
||||
[
|
||||
1386580151907705298970465943238806620109618995410132218037375811184684929291,
|
||||
3604888328937389309031638299660239238400230206645344173700074923133890528967,
|
||||
2496185632263372962152518155651824899299616724241852816983268163379540137546,
|
||||
486538168871046887467737983064272608432052269868418721234810979756540672990,
|
||||
1558415498960552213241704009433360128041672577274390114589014204605400783336,
|
||||
],
|
||||
[
|
||||
3512058327686147326577190314835092911156317204978509183234511559551181053926,
|
||||
2235429387083113882635494090887463486491842634403047716936833563914243946191,
|
||||
1290896777143878193192832813769470418518651727840187056683408155503813799882,
|
||||
1143310336918357319571079551779316654556781203013096026972411429993634080835,
|
||||
3235435208525081966062419599803346573407862428113723170955762956243193422118,
|
||||
],
|
||||
[
|
||||
1293239921425673430660897025143433077974838969258268884994339615096356996604,
|
||||
236252269127612784685426260840574970698541177557674806964960352572864382971,
|
||||
1733907592497266237374827232200506798207318263912423249709509725341212026275,
|
||||
302004309771755665128395814807589350526779835595021835389022325987048089868,
|
||||
3018926838139221755384801385583867283206879023218491758435446265703006270945,
|
||||
],
|
||||
[
|
||||
39701437664873825906031098349904330565195980985885489447836580931425171297,
|
||||
908381723021746969965674308809436059628307487140174335882627549095646509778,
|
||||
219062858908229855064136253265968615354041842047384625689776811853821594358,
|
||||
1283129863776453589317845316917890202859466483456216900835390291449830275503,
|
||||
418512623547417594896140369190919231877873410935689672661226540908900544012,
|
||||
],
|
||||
[
|
||||
1792181590047131972851015200157890246436013346535432437041535789841136268632,
|
||||
370546432987510607338044736824316856592558876687225326692366316978098770516,
|
||||
3323437805230586112013581113386626899534419826098235300155664022709435756946,
|
||||
910076621742039763058481476739499965761942516177975130656340375573185415877,
|
||||
1762188042455633427137702520675816545396284185254002959309669405982213803405,
|
||||
],
|
||||
[
|
||||
2186362253913140345102191078329764107619534641234549431429008219905315900520,
|
||||
2230647725927681765419218738218528849146504088716182944327179019215826045083,
|
||||
1069243907556644434301190076451112491469636357133398376850435321160857761825,
|
||||
2695241469149243992683268025359863087303400907336026926662328156934068747593,
|
||||
1361519681544413849831669554199151294308350560528931040264950307931824877035,
|
||||
],
|
||||
[
|
||||
1339116632207878730171031743761550901312154740800549632983325427035029084904,
|
||||
790593524918851401449292693473498591068920069246127392274811084156907468875,
|
||||
2723400368331924254840192318398326090089058735091724263333980290765736363637,
|
||||
3457180265095920471443772463283225391927927225993685928066766687141729456030,
|
||||
1483675376954327086153452545475557749815683871577400883707749788555424847954,
|
||||
],
|
||||
[
|
||||
2926303836265506736227240325795090239680154099205721426928300056982414025239,
|
||||
543969119775473768170832347411484329362572550684421616624136244239799475526,
|
||||
237401230683847084256617415614300816373730178313253487575312839074042461932,
|
||||
844568412840391587862072008674263874021460074878949862892685736454654414423,
|
||||
151922054871708336050647150237534498235916969120198637893731715254687336644,
|
||||
],
|
||||
[
|
||||
1299332034710622815055321547569101119597030148120309411086203580212105652312,
|
||||
487046922649899823989594814663418784068895385009696501386459462815688122993,
|
||||
1104883249092599185744249485896585912845784382683240114120846423960548576851,
|
||||
1458388705536282069567179348797334876446380557083422364875248475157495514484,
|
||||
850248109622750774031817200193861444623975329881731864752464222442574976566,
|
||||
],
|
||||
[
|
||||
2885843173858536690032695698009109793537724845140477446409245651176355435722,
|
||||
3027068551635372249579348422266406787688980506275086097330568993357835463816,
|
||||
3231892723647447539926175383213338123506134054432701323145045438168976970994,
|
||||
1719080830641935421242626784132692936776388194122314954558418655725251172826,
|
||||
1172253756541066126131022537343350498482225068791630219494878195815226839450,
|
||||
],
|
||||
[
|
||||
1619232269633026603732619978083169293258272967781186544174521481891163985093,
|
||||
3495680684841853175973173610562400042003100419811771341346135531754869014567,
|
||||
1576161515913099892951745452471618612307857113799539794680346855318958552758,
|
||||
2618326122974253423403350731396350223238201817594761152626832144510903048529,
|
||||
2696245132758436974032479782852265185094623165224532063951287925001108567649,
|
||||
],
|
||||
[
|
||||
930116505665110070247395429730201844026054810856263733273443066419816003444,
|
||||
2786389174502246248523918824488629229455088716707062764363111940462137404076,
|
||||
1555260846425735320214671887347115247546042526197895180675436886484523605116,
|
||||
2306241912153325247392671742757902161446877415586158295423293240351799505917,
|
||||
411529621724849932999694270803131456243889635467661223241617477462914950626,
|
||||
],
|
||||
[
|
||||
1542495485262286701469125140275904136434075186064076910329015697714211835205,
|
||||
1853045663799041100600825096887578544265580718909350942241802897995488264551,
|
||||
2963055259497271220202739837493041799968576111953080503132045092194513937286,
|
||||
2303806870349915764285872605046527036748108533406243381676768310692344456050,
|
||||
2622104986201990620910286730213140904984256464479840856728424375142929278875,
|
||||
],
|
||||
[
|
||||
2369987021925266811581727383184031736927816625797282287927222602539037105864,
|
||||
285070227712021899602056480426671736057274017903028992288878116056674401781,
|
||||
3034087076179360957800568733595959058628497428787907887933697691951454610691,
|
||||
469095854351700119980323115747590868855368701825706298740201488006320881056,
|
||||
360001976264385426746283365024817520563236378289230404095383746911725100012,
|
||||
],
|
||||
[
|
||||
3438709327109021347267562000879503009590697221730578667498351600602230296178,
|
||||
63573904800572228121671659287593650438456772568903228287754075619928214969,
|
||||
3470881855042989871434874691030920672110111605547839662680968354703074556970,
|
||||
724559311507950497340993415408274803001166693839947519425501269424891465492,
|
||||
880409284677518997550768549487344416321062350742831373397603704465823658986,
|
||||
],
|
||||
[
|
||||
6876255662475867703077362872097208259197756317287339941435193538565586230,
|
||||
2701916445133770775447884812906226786217969545216086200932273680400909154638,
|
||||
425152119158711585559310064242720816611629181537672850898056934507216982586,
|
||||
1475552998258917706756737045704649573088377604240716286977690565239187213744,
|
||||
2413772448122400684309006716414417978370152271397082147158000439863002593561,
|
||||
],
|
||||
[
|
||||
392160855822256520519339260245328807036619920858503984710539815951012864164,
|
||||
1075036996503791536261050742318169965707018400307026402939804424927087093987,
|
||||
2176439430328703902070742432016450246365760303014562857296722712989275658921,
|
||||
1413865976587623331051814207977382826721471106513581745229680113383908569693,
|
||||
4879283427490523253696177116563427032332223531862961281430108575019551814,
|
||||
],
|
||||
[
|
||||
3392583297537374046875199552977614390492290683707960975137418536812266544902,
|
||||
3600854486849487646325182927019642276644093512133907046667282144129939150983,
|
||||
2779924664161372134024229593301361846129279572186444474616319283535189797834,
|
||||
2722699960903170449291146429799738181514821447014433304730310678334403972040,
|
||||
819109815049226540285781191874507704729062681836086010078910930707209464699,
|
||||
],
|
||||
[
|
||||
3046121243742768013822760785918001632929744274211027071381357122228091333823,
|
||||
1339019590803056172509793134119156250729668216522001157582155155947567682278,
|
||||
1933279639657506214789316403763326578443023901555983256955812717638093967201,
|
||||
2138221547112520744699126051903811860205771600821672121643894708182292213541,
|
||||
2694713515543641924097704224170357995809887124438248292930846280951601597065,
|
||||
],
|
||||
[
|
||||
2471734202930133750093618989223585244499567111661178960753938272334153710615,
|
||||
504903761112092757611047718215309856203214372330635774577409639907729993533,
|
||||
1943979703748281357156510253941035712048221353507135074336243405478613241290,
|
||||
684525210957572142559049112233609445802004614280157992196913315652663518936,
|
||||
1705585400798782397786453706717059483604368413512485532079242223503960814508,
|
||||
],
|
||||
[
|
||||
192429517716023021556170942988476050278432319516032402725586427701913624665,
|
||||
1586493702243128040549584165333371192888583026298039652930372758731750166765,
|
||||
686072673323546915014972146032384917012218151266600268450347114036285993377,
|
||||
3464340397998075738891129996710075228740496767934137465519455338004332839215,
|
||||
2805249176617071054530589390406083958753103601524808155663551392362371834663,
|
||||
],
|
||||
[
|
||||
667746464250968521164727418691487653339733392025160477655836902744186489526,
|
||||
1131527712905109997177270289411406385352032457456054589588342450404257139778,
|
||||
1908969485750011212309284349900149072003218505891252313183123635318886241171,
|
||||
1025257076985551890132050019084873267454083056307650830147063480409707787695,
|
||||
2153175291918371429502545470578981828372846236838301412119329786849737957977,
|
||||
],
|
||||
[
|
||||
3410257749736714576487217882785226905621212230027780855361670645857085424384,
|
||||
3442969106887588154491488961893254739289120695377621434680934888062399029952,
|
||||
3029953900235731770255937704976720759948880815387104275525268727341390470237,
|
||||
85453456084781138713939104192561924536933417707871501802199311333127894466,
|
||||
2730629666577257820220329078741301754580009106438115341296453318350676425129,
|
||||
],
|
||||
[
|
||||
178242450661072967256438102630920745430303027840919213764087927763335940415,
|
||||
2844589222514708695700541363167856718216388819406388706818431442998498677557,
|
||||
3547876269219141094308889387292091231377253967587961309624916269569559952944,
|
||||
2525005406762984211707203144785482908331876505006839217175334833739957826850,
|
||||
3096397013555211396701910432830904669391580557191845136003938801598654871345,
|
||||
],
|
||||
[
|
||||
574424067119200181933992948252007230348512600107123873197603373898923821490,
|
||||
1714030696055067278349157346067719307863507310709155690164546226450579547098,
|
||||
2339895272202694698739231405357972261413383527237194045718815176814132612501,
|
||||
3562501318971895161271663840954705079797767042115717360959659475564651685069,
|
||||
69069358687197963617161747606993436483967992689488259107924379545671193749,
|
||||
],
|
||||
[
|
||||
2614502738369008850475068874731531583863538486212691941619835266611116051561,
|
||||
655247349763023251625727726218660142895322325659927266813592114640858573566,
|
||||
2305235672527595714255517865498269719545193172975330668070873705108690670678,
|
||||
926416070297755413261159098243058134401665060349723804040714357642180531931,
|
||||
866523735635840246543516964237513287099659681479228450791071595433217821460,
|
||||
],
|
||||
[
|
||||
2284334068466681424919271582037156124891004191915573957556691163266198707693,
|
||||
1812588309302477291425732810913354633465435706480768615104211305579383928792,
|
||||
2836899808619013605432050476764608707770404125005720004551836441247917488507,
|
||||
2989087789022865112405242078196235025698647423649950459911546051695688370523,
|
||||
68056284404189102136488263779598243992465747932368669388126367131855404486,
|
||||
],
|
||||
[
|
||||
505425339250887519581119854377342241317528319745596963584548343662758204398,
|
||||
2118963546856545068961709089296976921067035227488975882615462246481055679215,
|
||||
2253872596319969096156004495313034590996995209785432485705134570745135149681,
|
||||
1625090409149943603241183848936692198923183279116014478406452426158572703264,
|
||||
179139838844452470348634657368199622305888473747024389514258107503778442495,
|
||||
],
|
||||
[
|
||||
1567067018147735642071130442904093290030432522257811793540290101391210410341,
|
||||
2737301854006865242314806979738760349397411136469975337509958305470398783585,
|
||||
3002738216460904473515791428798860225499078134627026021350799206894618186256,
|
||||
374029488099466837453096950537275565120689146401077127482884887409712315162,
|
||||
973403256517481077805460710540468856199855789930951602150773500862180885363,
|
||||
],
|
||||
[
|
||||
2691967457038172130555117632010860984519926022632800605713473799739632878867,
|
||||
3515906794910381201365530594248181418811879320679684239326734893975752012109,
|
||||
148057579455448384062325089530558091463206199724854022070244924642222283388,
|
||||
1541588700238272710315890873051237741033408846596322948443180470429851502842,
|
||||
147013865879011936545137344076637170977925826031496203944786839068852795297,
|
||||
],
|
||||
[
|
||||
2630278389304735265620281704608245039972003761509102213752997636382302839857,
|
||||
1359048670759642844930007747955701205155822111403150159614453244477853867621,
|
||||
2438984569205812336319229336885480537793786558293523767186829418969842616677,
|
||||
2137792255841525507649318539501906353254503076308308692873313199435029594138,
|
||||
2262318076430740712267739371170174514379142884859595360065535117601097652755,
|
||||
],
|
||||
[
|
||||
2792703718581084537295613508201818489836796608902614779596544185252826291584,
|
||||
2294173715793292812015960640392421991604150133581218254866878921346561546149,
|
||||
2770011224727997178743274791849308200493823127651418989170761007078565678171,
|
||||
3321642244537785916275181932172303118112488081726311374164578600576901819844,
|
||||
3522708517589950573320671158134804505970724681591943826922697952040487655044,
|
||||
],
|
||||
[
|
||||
3417974441436557992524691506735790206623600049454586729879955931972546347402,
|
||||
175039333145381316571259690443853067809784261609912638686739799135919647022,
|
||||
1930713062131033273316869231148248962041053029742760224583505092759642967464,
|
||||
2971452932574554603554350185069538580257636405419430340233233400633251319042,
|
||||
2774781903758215341037075610938953949868289195845367046186588750871862784919,
|
||||
],
|
||||
[
|
||||
666516874137869653699423537799457099346460194092311952417454613224504932738,
|
||||
1900462225013533249140457703727169176351786259991305560412832202759625668041,
|
||||
2665631186082687279121709429531834469477679375137509769347092380798929714377,
|
||||
837840745988147279235494664091280091563355097569199320366973125128366540061,
|
||||
3391544118305848781823721719916289805455110924839794510205940718821197620955,
|
||||
],
|
||||
[
|
||||
2888553035909938253628892138500390690221493345071933642853222968481016605919,
|
||||
3386241569867597612447901482685846444743718781330869478721963580925825915450,
|
||||
1205126220630896984850042596877918177217334376800874965105642874206963597698,
|
||||
3590072615491710252422997155203204584659171612188004116415640739580250394190,
|
||||
692469013329617220154003334549812915100479873898898958632988703738125356983,
|
||||
],
|
||||
[
|
||||
1623178235190707102808841905143937367808788834203621005714003335195182126335,
|
||||
1972826180775011489122426045504602288576507493792470102803637471568052321297,
|
||||
3415141329098504418158191749675997877417539760075593313736376750580696083073,
|
||||
587811537889727046473915684463981273175495137461951211739526104349163747811,
|
||||
2523982964351069134084525951849317400231659428055762640605248929856135518199,
|
||||
],
|
||||
[
|
||||
2686176526711834950207666047281383173339057216783586039351834948812568447629,
|
||||
983144446441739425577690449774542566745526459152966545642451764143532586964,
|
||||
171558252019175695567663688494555626159399786667979998273792882504784080805,
|
||||
332337623010057542760158225837623039780806442976079546879646069338600179518,
|
||||
1264669683963885571544813806669118319675288608634733888843804451222546848295,
|
||||
],
|
||||
[
|
||||
2426165115815723668018318268486497504249785449504758403912155206515511627681,
|
||||
11387399609384288947733630450855186629703576293221897150193655994854764608,
|
||||
2541728569046079092074077754414781968906176513081761690404588216304985421091,
|
||||
47685947554980329431290582269851186106577733250761848107645535357326439312,
|
||||
472176388418187405374813530639596064799362505024895746833751173199773896628,
|
||||
],
|
||||
[
|
||||
2764298116617383397920343358525617195195060562266243809245480210157942112738,
|
||||
486863835068754002670800862273365477867695879270721744227071001883208334054,
|
||||
2973492686137102577527656941792991264994301121122130295965761350095846874635,
|
||||
178385615141132702906181473263873416748818415607305319148067639744074654009,
|
||||
533624640096756667052211553746016402543259206286603356120804827761339634127,
|
||||
],
|
||||
[
|
||||
819406716720171922688026098737835227857400444543748198788964759773510472096,
|
||||
531851793767260921861217458033110066464894334064526987603936107947006031387,
|
||||
3269709072483585277009748181134917746036523619604017812342933951952104134829,
|
||||
838191718603413598040249006803464503100808192944407407147899973659013630611,
|
||||
1574561296941310904780257598780779812250055948216417844567262310524022037406,
|
||||
],
|
||||
[
|
||||
551394354289003977607664358739006072556227894953233419144430578080352094737,
|
||||
445076790942318675726839050057337819004979443030540904213920669247413907302,
|
||||
1963946696292687224902912968478695543164747600779913024040878700455222386521,
|
||||
484284614181963381509745298932402076252103342403432879800905151752488144767,
|
||||
2240507606126946994415203252302826782042951346966859379502140796364876543253,
|
||||
],
|
||||
[
|
||||
3237135638753992982179886898758938279897590886053928839613434762582576319619,
|
||||
2334333034701915027889533058426879447140084891006486138782876488162658230991,
|
||||
14411091399844539897439754491034751977136685514851444574462584316609631592,
|
||||
1264480371592407258420308876448697804787923638319277097663041109464608464284,
|
||||
671929312763821646360589403212798993954209530574443543917757335777610372144,
|
||||
],
|
||||
[
|
||||
2513909805455654095962542944994577107405216428214873444765576238504714067396,
|
||||
870121102846043786263357605823753628974859886859187558617096145653709171231,
|
||||
399132620893316356411986266679786708905730068946836982293484206366500277754,
|
||||
2855046250836680633532995284655778407402587437073106249445470889390454667586,
|
||||
2063679741125384345396981490971605710211281905716315529671473143278849561151,
|
||||
],
|
||||
[
|
||||
1433753212258929925682201698758056443128516570551146995210728194816988328337,
|
||||
3334984763425011856632257855270507440816274246647423607159847074739331865077,
|
||||
337911293622078184850923533628334646725451591671907148383867096651211846605,
|
||||
559587005295238702015018022040357402231957131094636365177008701077975941644,
|
||||
885963059604819264377490633589388189646118257469490919900554134369512794660,
|
||||
],
|
||||
[
|
||||
1957748763518471091057032383332840331641373304981058387824598000170709016333,
|
||||
3175295982155056798972302481564899381103533409383494814704562889625572018450,
|
||||
498987160612401618114584726510347771865331516606886613019084323862447372555,
|
||||
947374835104260364630171441676101001841507588423166778786886198914150312958,
|
||||
906933977754491302438795274167251538820934378773708095543613756654712689280,
|
||||
],
|
||||
[
|
||||
2170116291766863179909957030577284618726490893598499117272497866180009722894,
|
||||
1801335399574515889082584621772588704763181408217893911806726119813067220453,
|
||||
1942500232535842474530840356353427989892065499159260166135596750084681859966,
|
||||
62936080219825306823124060587235998278756755377419521154040408253893795176,
|
||||
3091993939935137795359769774909373279950941171574748645375255810204590357753,
|
||||
],
|
||||
[
|
||||
1283528386884634267663661033944552098742115012555712906773586466375284501324,
|
||||
1581820717639229420476069802992937438655873471854930764425841549067913106065,
|
||||
2301986095388751633126546121528329200085681648876910655269533407603441046514,
|
||||
2850003828037698751961753862613545302539465803982364898225617297398939302949,
|
||||
48024691078494936445046366770271288984930221238071705874025261821606393528,
|
||||
],
|
||||
[
|
||||
1482336297033144958942154923925185950152551534403871620222916667536030875354,
|
||||
3081177564717719643771186007689458633949181485535169123213511264603782950049,
|
||||
3315701127039521853279746297714590495201061397709680410650043502532250578075,
|
||||
3514407611000441301995070394422463400067690470546731164089622325748803106020,
|
||||
368970178199930154322724953487299516224498421233447528815195701420122548537,
|
||||
],
|
||||
[
|
||||
584353160413525267849669053228533951552602295860601556035386665117717227391,
|
||||
752038702160385294706011538400822066722189014251268673051846350397729870418,
|
||||
3594041683498798688197194521326299097635429790757880308152971477196489335154,
|
||||
1367902435756906062215608264424138718742854099315395230911274560900857414183,
|
||||
1828549068951502746189364466794037234789986878381694857475972053743463890779,
|
||||
],
|
||||
[
|
||||
488172495141237210878388657234137733008417573114482400652274985829148564248,
|
||||
962906242461930394022372340919543491337923491322497419797555620396501785566,
|
||||
2275418085010046236619290386129138234541669589549771944697082317642065048898,
|
||||
1966395064658902622886154686288219600816893261614483533899715888994623208964,
|
||||
3496095878293416917311185659829821476802828534554531050412634978086916288609,
|
||||
],
|
||||
[
|
||||
3368478822390537245916137403277928093536087427951052230723275731232142463388,
|
||||
3397410259276620127103231993277518800970669191016277541098821699302368873803,
|
||||
2662600899665871010006649609856695263727220473364611552472965243032255906029,
|
||||
2854831720595596992200155718152374313555878203864206470581502555480894633975,
|
||||
2417859092561967752135741161218626374900182454089059862468108240576782064037,
|
||||
],
|
||||
[
|
||||
1064506915903089299531724594973601253341866933071158266140674053459433520889,
|
||||
243845138053687262800349059300355289745206315347524675450796070948867090098,
|
||||
1952653154963756062322124110012629666160000286707762177032475477295929736283,
|
||||
2760979128531476595658428672038276216079708408852493051222686009638650156041,
|
||||
3341178930260137001230946104398194306290005446746057811731360203227371301716,
|
||||
],
|
||||
[
|
||||
1033242545866274439991875444609632860132556714736615395036273942261573810479,
|
||||
3567973410830779135148598005871071456943945697865168835204985462698751038238,
|
||||
23014034649293369426970379738102323014738017168969687350330825050016457105,
|
||||
1146720508452451012445869043641390200263192255569203352823376998708972325392,
|
||||
2553707028642376593497768606567528232999203496079990242456254686325586089356,
|
||||
],
|
||||
[
|
||||
269729857648436699208023125596593246149228245518586029792966091405383426269,
|
||||
276912682886955358118649215147238115764108757952690361549816619060658800027,
|
||||
2367180947887796341722261610916728725977893583923967218630363334645641817362,
|
||||
2398694802751362950028137620758033447242325333923222365760836442417755445092,
|
||||
984868389243025029364428136317275892280780834039611841422502834917752411391,
|
||||
],
|
||||
[
|
||||
861353329558771468244040268521983016756775808329676883407171471251365927595,
|
||||
2498672969617384807617108262141800974986393948110233099680635130601163654234,
|
||||
1336236634145657673540555267430353130305889434115514586892320600753700983325,
|
||||
980337801407886250576371882962628290239239581416378379141354256717803603922,
|
||||
2308558359523317875952657835109605515063994805873180719205156915762120497245,
|
||||
],
|
||||
[
|
||||
2116737905426837141304542819940293184404010538896700217242374222514653607487,
|
||||
2143995283326808680518644927890182524580312777400009071739277407358043120199,
|
||||
3038758768133404431511594054950351369492648883179154555267474054094234927849,
|
||||
981824005865625678985009911415023115269386212492064371040001594972137748141,
|
||||
2427990511715778580869565219059895697855813782250850855111162965998948386792,
|
||||
],
|
||||
[
|
||||
1987498156785173719076522405088076990979859292718600184358583152317049836167,
|
||||
1633834915134208237423144264187482951766302060112099587851513525797020813799,
|
||||
2895454976388515752029424688351979030650325184941524820409482023485820781526,
|
||||
941019661238578826272324221721825852217063629464317974190162904813488515671,
|
||||
2529926057929249454763690180607677568685011502604470585585763159431333258299,
|
||||
],
|
||||
[
|
||||
2604831509257756199338105380847564711923112853239827243306562341166492672823,
|
||||
2300475954087415591738767759767032267163723345312082546282694920273655145455,
|
||||
1954000528502201000509342111010021527425422549437946241062907964768089317082,
|
||||
1179936151696782249912570883839105595634344582873818018332922940963046083567,
|
||||
3077707030301573630126144767923697288658782137457660869231140049571827937228,
|
||||
],
|
||||
[
|
||||
1062324397142900251844488719868780667589966366756786302007970554437994421840,
|
||||
353718609497993885193404630053532608155520921625518104461520254335222009911,
|
||||
770557645309607171206012551080400276506165720184677119001983749356594531977,
|
||||
3043628430985247363392058521341757139056029350680498644930013342982472853636,
|
||||
1694968537785457252742656255724723357998402478572600479401200420305593921487,
|
||||
],
|
||||
[
|
||||
539865665379093791531434211889371819368504193082947002067781562776138072582,
|
||||
3473466148775696692731190426971123680342615414200262605154732883324298196699,
|
||||
482783534456196983135936103604928650836406142744767857356485953118411089098,
|
||||
2389101033971236780034779577432189630800997581132154923233144722790749715251,
|
||||
845264223568475649981141803833883014312596504303895519674002924871878791033,
|
||||
],
|
||||
[
|
||||
3027004059915270231142566724881373969831662022738947178800901294120992473905,
|
||||
2169574859350740480088697859610203373582027214052754592019828328614087431593,
|
||||
3515527080764222354309565181793838292349410992793070639041305826153436624160,
|
||||
1817926918350512904327755405973355211358017834277255662858654992240629698587,
|
||||
1999148133619270973098477176176178514394558202995832714883251820350860287223,
|
||||
],
|
||||
[
|
||||
1203131300029280096510929599113528018338088236684405405384757591977164161039,
|
||||
336815403657101171302040383579077521911288747438919304948637997306314852594,
|
||||
986661060847815533035934253464295060766339947679669645818832311132001095573,
|
||||
2291116974939980228917916563988261327966840303336559854772343651559589512651,
|
||||
3421243089992476528970346847858594146122972226790673723411896208702859892637,
|
||||
],
|
||||
[
|
||||
1015505198663386486420800821559060487156096175034250154764824837183581949724,
|
||||
1165880582987807286271819576391581724550686829511475839624601920297855380101,
|
||||
904232961143172831178860280790910264843503022179578981166030973682571903458,
|
||||
261322216292849827900157598748641385787016033372999683866859675894253115357,
|
||||
3060676319159217735181388708455879854358158161989877552543698103915296690395,
|
||||
],
|
||||
[
|
||||
1175560144527845912984609340783959238735643215413930887771084560168082442967,
|
||||
2813871258576082360085006002528268796351819524936446195552260262614692343332,
|
||||
1841341101531851399935829271555098629075809587212843292354556374386667658235,
|
||||
3076135575511709688509914361447080149794919016880133063891720256749999834767,
|
||||
753111801049754117414662684453226478940731922961768343984187479992842213733,
|
||||
],
|
||||
[
|
||||
1405657437118503342762241742745888533114216548278983907019917904938403345580,
|
||||
3111186124713876864436867307979940633543281080828379725576742174555539054855,
|
||||
3404463650394703220454952017098727360005393139199301323890695570346564876407,
|
||||
2024087816190101179456573591359233695334184711688920998987373624570170649371,
|
||||
2770035625774572095496575568588054654502991645588385802705097377675051032967,
|
||||
],
|
||||
[
|
||||
437058215235292632621847481185406671372191763951486300610124033096831557414,
|
||||
1345792773780982398809956395232061067669190682958320579442454533085407626029,
|
||||
925357273912625669941681596445839316566672314870287993638671283923476231904,
|
||||
3288133122086768300615066039539687885053110015077924175836976549020438910830,
|
||||
666190075990703867784232802074474372379358766701681865975596503982238839889,
|
||||
],
|
||||
[
|
||||
2664898601165892062970298960258838238925231697327906221693001926762280012052,
|
||||
2075648691532387787722427044464731934171216054855867223374228487601569118337,
|
||||
3173725544188532489243684991828985285646224157242834030308807120745121062293,
|
||||
1517474443612606408422643323550409253700128234157734252330869178582583531320,
|
||||
1593950878945144789965609248470060076911813704207225832606804796819386297511,
|
||||
],
|
||||
[
|
||||
141195541167651298813588829225208004611326987855926870823948793274702167509,
|
||||
2990187949585642302497822222637786229364740008175968941859105979392907839776,
|
||||
2893807105405820282316438050347503569385510241526138409321358916388308586443,
|
||||
1379719211597875648759619903854862028510320482486109668868067715175935658353,
|
||||
2702780364788282233075255946852944970202849869091427738791947810055591218061,
|
||||
],
|
||||
[
|
||||
1825815734419326277729273926504439575157952821379179501821641713286627304656,
|
||||
1481344458867016048625916723816339719872443766684158199301690902395849166360,
|
||||
2014084774259125722186109781197998076881266739680534358898592778318128968629,
|
||||
2612744185006548312909661512508122065214170543806989291921289897662387203493,
|
||||
2486291022451231582267428921150634472835925206862678364689227838329114330247,
|
||||
],
|
||||
]
|
||||
1031
test/stark/poseidon/poseidon9.txt
Normal file
1031
test/stark/poseidon/poseidon9.txt
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,48 +1,50 @@
|
||||
import { deepStrictEqual, throws } from 'assert';
|
||||
import { should } from 'micro-should';
|
||||
import * as starknet from '../../lib/esm/stark.js';
|
||||
import { describe, should } from 'micro-should';
|
||||
import * as starknet from '../../esm/stark.js';
|
||||
import * as fc from 'fast-check';
|
||||
|
||||
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.property(FC_BIGINT, (x) => {
|
||||
const point1 = starknet.Point.fromPrivateKey(x);
|
||||
const point1 = starknet.ProjectivePoint.fromPrivateKey(x);
|
||||
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.property(FC_BIGINT, FC_BIGINT, (r, s) => {
|
||||
const sig = new starknet.Signature(r, s);
|
||||
deepStrictEqual(starknet.Signature.fromCompact(sig.toCompactHex()), sig);
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
should('Signature.fromDERHex() roundtrip', () => {
|
||||
should('Signature.fromDERHex() roundtrip', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, (r, s) => {
|
||||
const sig = new starknet.Signature(r, s);
|
||||
deepStrictEqual(starknet.Signature.fromDER(sig.toDERHex()), sig);
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
should('verify()/should verify random signatures', () =>
|
||||
should('verify()/should verify random signatures', () =>
|
||||
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 pub = starknet.getPublicKey(privKey);
|
||||
const sig = starknet.sign(msg, privKey);
|
||||
deepStrictEqual(starknet.verify(sig, msg, pub), true);
|
||||
})
|
||||
)
|
||||
);
|
||||
);
|
||||
});
|
||||
|
||||
// ESM is broken.
|
||||
import url from 'url';
|
||||
|
||||
@@ -1,28 +1,29 @@
|
||||
import { deepStrictEqual, throws } from 'assert';
|
||||
import { should } from 'micro-should';
|
||||
import { hex, utf8 } from '@scure/base';
|
||||
import { describe, should } from 'micro-should';
|
||||
import { utf8ToBytes } from '@noble/hashes/utils';
|
||||
import * as bip32 from '@scure/bip32';
|
||||
import * as bip39 from '@scure/bip39';
|
||||
import * as starknet from '../../lib/esm/stark.js';
|
||||
import * as starknet from '../../esm/stark.js';
|
||||
import { default as sigVec } from './fixtures/rfc6979_signature_test_vector.json' assert { type: 'json' };
|
||||
import { default as precomputedKeys } from './fixtures/keys_precomputed.json' assert { type: 'json' };
|
||||
|
||||
should('Starknet keccak', () => {
|
||||
const value = starknet.keccak(utf8.decode('hello'));
|
||||
describe('starknet', () => {
|
||||
should('custom keccak', () => {
|
||||
const value = starknet.keccak(utf8ToBytes('hello'));
|
||||
deepStrictEqual(value, 0x8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8n);
|
||||
deepStrictEqual(value < 2n ** 250n, true);
|
||||
});
|
||||
});
|
||||
|
||||
should('RFC6979', () => {
|
||||
should('RFC6979', () => {
|
||||
for (const msg of sigVec.messages) {
|
||||
const { r, s } = starknet.sign(msg.hash, sigVec.private_key);
|
||||
// const { r, s } = starknet.Signature.fromDER(sig);
|
||||
deepStrictEqual(r.toString(10), msg.r);
|
||||
deepStrictEqual(s.toString(10), msg.s);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
should('Signatures', () => {
|
||||
should('Signatures', () => {
|
||||
const vectors = [
|
||||
{
|
||||
// Message hash of length 61.
|
||||
@@ -59,9 +60,9 @@ should('Signatures', () => {
|
||||
deepStrictEqual(s.toString(16), v.s, 's equality');
|
||||
deepStrictEqual(starknet.verify(sig, v.msg, publicKey), true, 'verify');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
should('Invalid signatures', () => {
|
||||
should('Invalid signatures', () => {
|
||||
/*
|
||||
|
||||
it('should not verify invalid signature inputs lengths', () => {
|
||||
@@ -155,11 +156,11 @@ should('Invalid signatures', () => {
|
||||
starkwareCrypto.verify(keyPairPub, msgHash.toString(16), msgSignature)
|
||||
).to.be.false;
|
||||
});
|
||||
});
|
||||
});
|
||||
*/
|
||||
});
|
||||
});
|
||||
|
||||
should('Pedersen', () => {
|
||||
should('Pedersen', () => {
|
||||
deepStrictEqual(
|
||||
starknet.pedersen(
|
||||
'0x3d937c035c878245caf64531a5756109c53068da139362728feb561405371cb',
|
||||
@@ -174,13 +175,13 @@ should('Pedersen', () => {
|
||||
),
|
||||
'0x68cc0b76cddd1dd4ed2301ada9b7c872b23875d5ff837b3a87993e0d9996b87'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
should('Hash chain', () => {
|
||||
should('Hash chain', () => {
|
||||
deepStrictEqual(starknet.hashChain([1, 2, 3]), starknet.pedersen(1, starknet.pedersen(2, 3)));
|
||||
});
|
||||
});
|
||||
|
||||
should('Key grinding', () => {
|
||||
should('Key grinding', () => {
|
||||
deepStrictEqual(
|
||||
starknet.grindKey('86F3E7293141F20A8BAFF320E8EE4ACCB9D4A4BF2B4D295E8CEE784DB46E0519'),
|
||||
'5c8c8683596c732541a59e03007b2d30dbbbb873556fe65b5fb63c16688f941'
|
||||
@@ -190,9 +191,9 @@ should('Key grinding', () => {
|
||||
starknet.grindKey('94F3E7293141F20A8BAFF320E8EE4ACCB9D4A4BF2B4D295E8CEE784DB46E0595'),
|
||||
'33880b9aba464c1c01c9f8f5b4fc1134698f9b0a8d18505cab6cdd34d93dc02'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
should('Private to stark key', () => {
|
||||
should('Private to stark key', () => {
|
||||
deepStrictEqual(
|
||||
starknet.getStarkKey('0x178047D3869489C055D7EA54C014FFB834A069C9595186ABE04EA4D1223A03F'),
|
||||
'0x1895a6a77ae14e7987b9cb51329a5adfb17bd8e7c638f92d6892d76e51cebcf'
|
||||
@@ -200,9 +201,9 @@ should('Private to stark key', () => {
|
||||
for (const [privKey, expectedPubKey] of Object.entries(precomputedKeys)) {
|
||||
deepStrictEqual(starknet.getStarkKey(privKey), expectedPubKey);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
should('Private stark key from eth signature', () => {
|
||||
should('Private stark key from eth signature', () => {
|
||||
const ethSignature =
|
||||
'0x21fbf0696d5e0aa2ef41a2b4ffb623bcaf070461d61cf7251c74161f82fec3a43' +
|
||||
'70854bc0a34b3ab487c1bc021cd318c734c51ae29374f2beb0e6f2dd49b4bf41c';
|
||||
@@ -210,9 +211,9 @@ should('Private stark key from eth signature', () => {
|
||||
starknet.ethSigToPrivate(ethSignature),
|
||||
'766f11e90cd7c7b43085b56da35c781f8c067ac0d578eabdceebc4886435bda'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
should('Key derivation', () => {
|
||||
should('Key derivation', () => {
|
||||
const layer = 'starkex';
|
||||
const application = 'starkdeployement';
|
||||
const mnemonic =
|
||||
@@ -242,10 +243,10 @@ should('Key derivation', () => {
|
||||
deepStrictEqual(realPath, path);
|
||||
deepStrictEqual(starknet.grindKey(hd.derive(realPath).privateKey), privateKey);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Verified against starknet.js
|
||||
should('Starknet.js cross-tests', () => {
|
||||
// Verified against starknet.js
|
||||
should('Starknet.js cross-tests', () => {
|
||||
const privateKey = '0x019800ea6a9a73f94aee6a3d2edf018fc770443e90c7ba121e8303ec6b349279';
|
||||
// NOTE: there is no compressed keys here, getPubKey returns stark-key (which is schnorr-like X coordinate)
|
||||
// But it is not used in signing/verifying
|
||||
@@ -255,7 +256,7 @@ should('Starknet.js cross-tests', () => {
|
||||
);
|
||||
const msgHash = '0x6d1706bd3d1ba7c517be2a2a335996f63d4738e2f182144d078a1dd9997062e';
|
||||
const sig = starknet.sign(msgHash, privateKey);
|
||||
const { r, s } = (sig);
|
||||
const { r, s } = sig;
|
||||
|
||||
deepStrictEqual(
|
||||
r.toString(),
|
||||
@@ -277,6 +278,7 @@ should('Starknet.js cross-tests', () => {
|
||||
2440689354481625417078677634625227600823892606910345662891037256374285369343n
|
||||
);
|
||||
deepStrictEqual(starknet.verify(sig2.toDERHex(), hashMsg2, pubKey), true);
|
||||
});
|
||||
});
|
||||
|
||||
// ESM is broken.
|
||||
|
||||
1080
test/vectors/poseidon.json
Normal file
1080
test/vectors/poseidon.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,12 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"outDir": "lib/esm",
|
||||
"outDir": "esm",
|
||||
"target": "es2020",
|
||||
"module": "es6",
|
||||
"moduleResolution": "node16",
|
||||
"noUnusedLocals": true,
|
||||
"sourceMap": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@noble/hashes/crypto": [ "src/crypto" ]
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"declaration": true,
|
||||
"outDir": "lib",
|
||||
"declarationMap": true,
|
||||
"outDir": ".",
|
||||
"target": "es2020",
|
||||
"lib": ["es2020"], // Set explicitly to remove DOM
|
||||
"sourceMap": true,
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"noUnusedLocals": true,
|
||||
|
||||
Reference in New Issue
Block a user