Compare commits
190 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d61c7ae4e5 | ||
|
|
d3de7c8863 | ||
|
|
6316643f51 | ||
|
|
7199f113c6 | ||
|
|
71f6948612 | ||
|
|
d3d03ff115 | ||
|
|
e2c3560686 | ||
|
|
4e9c40b3e5 | ||
|
|
09085d2ee1 | ||
|
|
8c4d781479 | ||
|
|
123431de66 | ||
|
|
7503aff45c | ||
|
|
81e6046698 | ||
|
|
30f7d78c82 | ||
|
|
00665b21ab | ||
|
|
5d54bba846 | ||
|
|
851af4f1bc | ||
|
|
6ea40d9dab | ||
|
|
8beb922ded | ||
|
|
fe380da8c9 | ||
|
|
113d906233 | ||
|
|
65c0dc6c59 | ||
|
|
ed3ba3de6e | ||
|
|
d424c661fb | ||
|
|
31d92cce11 | ||
|
|
c15c964f77 | ||
|
|
37ebe6c40f | ||
|
|
18eabfd3be | ||
|
|
19f04a4c1c | ||
|
|
d0c3bee4de | ||
|
|
4244f97d38 | ||
|
|
618508d32c | ||
|
|
3936449e7b | ||
|
|
0ffa38db6b | ||
|
|
c4c580edc0 | ||
|
|
abe8adac7b | ||
|
|
4fd2ae82b6 | ||
|
|
e2411f7dfd | ||
|
|
cb61e4f292 | ||
|
|
bb875791bd | ||
|
|
3df2553ced | ||
|
|
8fabc7ff06 | ||
|
|
f3c21eb347 | ||
|
|
a8b8192714 | ||
|
|
1c6aa07ff7 | ||
|
|
e110237298 | ||
|
|
45393db807 | ||
|
|
acc3a9dc4d | ||
|
|
9295b0dbae | ||
|
|
5784ef23f6 | ||
|
|
ef55efe842 | ||
|
|
1cfd6a76ca | ||
|
|
89f81b2204 | ||
|
|
d77ac16f51 | ||
|
|
fe68da61f6 | ||
|
|
32c0841bed | ||
|
|
49a659b248 | ||
|
|
9d0a2e25dc | ||
|
|
7c461af2b2 | ||
|
|
4a8f447c8d | ||
|
|
4b2d31ce7f | ||
|
|
16115f27a6 | ||
|
|
0e0d0f530d | ||
|
|
fa5105aef2 | ||
|
|
11f1626ecc | ||
|
|
53ff287bf7 | ||
|
|
214c9aa553 | ||
|
|
ec2c3e1248 | ||
|
|
e64a9d654c | ||
|
|
088edd0fbb | ||
|
|
3e90930e9d | ||
|
|
b8b2e91f74 | ||
|
|
9ee694ae23 | ||
|
|
6bc4b35cf4 | ||
|
|
0163b63532 | ||
|
|
7e825520f1 | ||
|
|
d739297b2c | ||
|
|
285aa6375d | ||
|
|
8c77331ef2 | ||
|
|
669641e0a3 | ||
|
|
68dd57ed31 | ||
|
|
a9fdd6df9f | ||
|
|
d485d8b0e6 | ||
|
|
0fdd763dc7 | ||
|
|
586e2ad5fb | ||
|
|
ed81707bdc | ||
|
|
6d56b2d78e | ||
|
|
8397241a8f | ||
|
|
001d0cc24a | ||
|
|
ce9d165657 | ||
|
|
2902b0299a | ||
|
|
e1cb8549e8 | ||
|
|
26ebb5dcce | ||
|
|
8b2863aeac | ||
|
|
b1f50d9364 | ||
|
|
b81d74d3cb | ||
|
|
d5fe537159 | ||
|
|
cde1d5c488 | ||
|
|
3486bbf6b8 | ||
|
|
0d7a8296c5 | ||
|
|
0f1e7a5a43 | ||
|
|
3da48cf899 | ||
|
|
4ec46dd65d | ||
|
|
7073f63c6b | ||
|
|
80966cbd03 | ||
|
|
98ea15dca4 | ||
|
|
e1910e85ea | ||
|
|
4d311d7294 | ||
|
|
c36d90cae6 | ||
|
|
af5aa8424f | ||
|
|
67b99652fc | ||
|
|
c8d292976b | ||
|
|
daffaa2339 | ||
|
|
a462fc5779 | ||
|
|
fe3491c5aa | ||
|
|
c0877ba69a | ||
|
|
8e449cc78c | ||
|
|
1b6071cabd | ||
|
|
debb9d9709 | ||
|
|
d2c6459756 | ||
|
|
47533b6336 | ||
|
|
00b73b68d3 | ||
|
|
cef4b52d12 | ||
|
|
47ce547dcf | ||
|
|
e2a7594eae | ||
|
|
823149ecd9 | ||
|
|
e57aec63d8 | ||
|
|
837aca98c9 | ||
|
|
dbb16b0e5e | ||
|
|
e14af67254 | ||
|
|
4780850748 | ||
|
|
3374a70f47 | ||
|
|
131f88b504 | ||
|
|
4333e9a686 | ||
|
|
a60d15ff05 | ||
|
|
ceffbc69da | ||
|
|
c75129e629 | ||
|
|
f39fb80c52 | ||
|
|
fcd422d246 | ||
|
|
ed9bf89038 | ||
|
|
7262b4219f | ||
|
|
02b0b25147 | ||
|
|
79100c2d47 | ||
|
|
4ef2cad685 | ||
|
|
69b3ab5a57 | ||
|
|
9465e60d30 | ||
|
|
0fb78b7097 | ||
|
|
be0b2a32a5 | ||
|
|
3d77422731 | ||
|
|
c46914f1bc | ||
|
|
f250f355e8 | ||
|
|
c095d74673 | ||
|
|
ac52fea952 | ||
|
|
f2ee24bee4 | ||
|
|
cffea91061 | ||
|
|
5fc38fc0e7 | ||
|
|
849dc38f3c | ||
|
|
0422e6ef38 | ||
|
|
21d2438a33 | ||
|
|
cea4696599 | ||
|
|
f14b8d2be5 | ||
|
|
2ed27da8eb | ||
|
|
17e5be5f1b | ||
|
|
a49f0d266e | ||
|
|
bfbcf733e6 | ||
|
|
7fda6de619 | ||
|
|
2b908ad602 | ||
|
|
ceb3f67faa | ||
|
|
a2c87f9c2f | ||
|
|
e1fd346279 | ||
|
|
11e78aadbf | ||
|
|
055147f1be | ||
|
|
6f99f6042e | ||
|
|
1e47bf2372 | ||
|
|
40530eae0c | ||
|
|
b9482bb17d | ||
|
|
74475dca68 | ||
|
|
f4cf21b9c8 | ||
|
|
5312d92b2c | ||
|
|
d1770c0ac7 | ||
|
|
2d37edf7d1 | ||
|
|
36998fede8 | ||
|
|
83960d445d | ||
|
|
23cc2aa5d1 | ||
|
|
e45d7c2d25 | ||
|
|
bfe929aac3 | ||
|
|
069452dbe7 | ||
|
|
2e81f31d2e | ||
|
|
9f7df0f13b | ||
|
|
5600629bca |
14
.gitignore
vendored
14
.gitignore
vendored
@@ -1,7 +1,13 @@
|
|||||||
build/
|
build/
|
||||||
node_modules/
|
node_modules/
|
||||||
coverage/
|
coverage/
|
||||||
/lib/**/*.js
|
/*.js
|
||||||
/lib/**/*.ts
|
/*.ts
|
||||||
/lib/**/*.d.ts.map
|
/*.js.map
|
||||||
/curve-definitions/lib
|
/*.d.ts.map
|
||||||
|
/esm/*.js
|
||||||
|
/esm/*.ts
|
||||||
|
/esm/*.js.map
|
||||||
|
/esm/*.d.ts.map
|
||||||
|
/esm/abstract
|
||||||
|
/abstract/
|
||||||
|
|||||||
6
.vscode/settings.json
vendored
Normal file
6
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"files.exclude": {
|
||||||
|
"*.{js,d.ts,js.map,d.ts.map}": true,
|
||||||
|
"esm/*.{js,d.ts,js.map,d.ts.map}": true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,8 +4,8 @@
|
|||||||
|
|
||||||
| Version | Supported |
|
| Version | Supported |
|
||||||
| ------- | ------------------ |
|
| ------- | ------------------ |
|
||||||
| >=0.5.0 | :white_check_mark: |
|
| >=1.0.0 | :white_check_mark: |
|
||||||
| <0.5.0 | :x: |
|
| <1.0.0 | :x: |
|
||||||
|
|
||||||
## Reporting a Vulnerability
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
|||||||
BIN
audit/2023-01-trailofbits-audit-curves.pdf
Normal file
BIN
audit/2023-01-trailofbits-audit-curves.pdf
Normal file
Binary file not shown.
11
audit/README.md
Normal file
11
audit/README.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# Audit
|
||||||
|
|
||||||
|
The library has been audited during Jan-Feb 2023 by an independent security firm [Trail of Bits](https://www.trailofbits.com):
|
||||||
|
[PDF](https://github.com/trailofbits/publications/blob/master/reviews/2023-01-ryanshea-noblecurveslibrary-securityreview.pdf).
|
||||||
|
The audit has been funded by Ryan Shea. Audit scope was abstract modules `curve`, `hash-to-curve`, `modular`, `poseidon`, `utils`, `weierstrass`, and top-level modules `_shortw_utils` and `secp256k1`. See [changes since audit](https://github.com/paulmillr/noble-curves/compare/0.7.3..main).
|
||||||
|
|
||||||
|
File in the directory was saved from
|
||||||
|
[github.com/trailofbits/publications](https://github.com/trailofbits/publications).
|
||||||
|
Check out their repo and verify checksums to ensure the PDF in this directory has not been altered.
|
||||||
|
|
||||||
|
See information about fuzzing in root [README](../README.md).
|
||||||
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();
|
||||||
|
}
|
||||||
|
});
|
||||||
18
benchmark/ecdh.js
Normal file
18
benchmark/ecdh.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { run, compare } from 'micro-bmark';
|
||||||
|
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, expand: 'xmd', p: N, m: 1, k: 128 })
|
||||||
|
);
|
||||||
|
const msg = utf8ToBytes('message');
|
||||||
|
for (let [title, fn] of Object.entries({ secp256k1, p256, p384, p521, ed25519, ed448 })) {
|
||||||
|
await mark(`hashToCurve ${title}`, 1000, () => fn(msg));
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -1,416 +0,0 @@
|
|||||||
import * as bench from 'micro-bmark';
|
|
||||||
const { run, mark } = bench; // or bench.mark
|
|
||||||
import { readFileSync } from 'fs';
|
|
||||||
|
|
||||||
// Curves
|
|
||||||
import { secp256k1 } from '../lib/secp256k1.js';
|
|
||||||
import { P256 } from '../lib/p256.js';
|
|
||||||
import { P384 } from '../lib/p384.js';
|
|
||||||
import { P521 } from '../lib/p521.js';
|
|
||||||
import { ed25519 } from '../lib/ed25519.js';
|
|
||||||
import { ed448 } from '../lib/ed448.js';
|
|
||||||
import { bls12_381 as bls } from '../lib/bls12-381.js';
|
|
||||||
|
|
||||||
// Others
|
|
||||||
import { hmac } from '@noble/hashes/hmac';
|
|
||||||
import { sha256 } from '@noble/hashes/sha256';
|
|
||||||
import { sha512 } from '@noble/hashes/sha512';
|
|
||||||
|
|
||||||
import * as old_secp from '@noble/secp256k1';
|
|
||||||
import * as old_bls from '@noble/bls12-381';
|
|
||||||
import { concatBytes, hexToBytes } from '@noble/hashes/utils';
|
|
||||||
|
|
||||||
import * as starkwareCrypto from '@starkware-industries/starkware-crypto-utils';
|
|
||||||
import * as stark from '../lib/stark.js';
|
|
||||||
|
|
||||||
old_secp.utils.sha256Sync = (...msgs) =>
|
|
||||||
sha256
|
|
||||||
.create()
|
|
||||||
.update(concatBytes(...msgs))
|
|
||||||
.digest();
|
|
||||||
old_secp.utils.hmacSha256Sync = (key, ...msgs) =>
|
|
||||||
hmac
|
|
||||||
.create(sha256, key)
|
|
||||||
.update(concatBytes(...msgs))
|
|
||||||
.digest();
|
|
||||||
import * as noble_ed25519 from '@noble/ed25519';
|
|
||||||
noble_ed25519.utils.sha512Sync = (...m) => sha512(concatBytes(...m));
|
|
||||||
|
|
||||||
// BLS
|
|
||||||
const G2_VECTORS = readFileSync('../test/bls12-381/bls12-381-g2-test-vectors.txt', 'utf-8')
|
|
||||||
.trim()
|
|
||||||
.split('\n')
|
|
||||||
.map((l) => l.split(':'));
|
|
||||||
let p1, p2, oldp1, oldp2;
|
|
||||||
// /BLS
|
|
||||||
|
|
||||||
for (let item of [secp256k1, ed25519, ed448, P256, P384, P521, old_secp, noble_ed25519]) {
|
|
||||||
item.utils.precompute(8);
|
|
||||||
}
|
|
||||||
|
|
||||||
const ONLY_NOBLE = process.argv[2] === 'noble';
|
|
||||||
|
|
||||||
function generateData(namespace) {
|
|
||||||
const priv = namespace.utils.randomPrivateKey();
|
|
||||||
const pub = namespace.getPublicKey(priv);
|
|
||||||
const msg = namespace.utils.randomPrivateKey();
|
|
||||||
const sig = namespace.sign(msg, priv);
|
|
||||||
return { priv, pub, msg, sig };
|
|
||||||
}
|
|
||||||
|
|
||||||
export const CURVES = {
|
|
||||||
secp256k1: {
|
|
||||||
data: () => {
|
|
||||||
return generateData(secp256k1);
|
|
||||||
},
|
|
||||||
getPublicKey1: {
|
|
||||||
samples: 10000,
|
|
||||||
secp256k1_old: () => old_secp.getPublicKey(3n),
|
|
||||||
secp256k1: () => secp256k1.getPublicKey(3n),
|
|
||||||
},
|
|
||||||
getPublicKey255: {
|
|
||||||
samples: 10000,
|
|
||||||
secp256k1_old: () => old_secp.getPublicKey(2n ** 255n - 1n),
|
|
||||||
secp256k1: () => secp256k1.getPublicKey(2n ** 255n - 1n),
|
|
||||||
},
|
|
||||||
sign: {
|
|
||||||
samples: 5000,
|
|
||||||
secp256k1_old: ({ msg, priv }) => old_secp.signSync(msg, priv),
|
|
||||||
secp256k1: ({ msg, priv }) => secp256k1.sign(msg, priv),
|
|
||||||
},
|
|
||||||
verify: {
|
|
||||||
samples: 1000,
|
|
||||||
secp256k1_old: ({ sig, msg, pub }) => {
|
|
||||||
return old_secp.verify(new old_secp.Signature(sig.r, sig.s), msg, pub);
|
|
||||||
},
|
|
||||||
secp256k1: ({ sig, msg, pub }) => secp256k1.verify(sig, msg, pub),
|
|
||||||
},
|
|
||||||
getSharedSecret: {
|
|
||||||
samples: 1000,
|
|
||||||
secp256k1_old: ({ pub, priv }) => old_secp.getSharedSecret(priv, pub),
|
|
||||||
secp256k1: ({ pub, priv }) => secp256k1.getSharedSecret(priv, pub),
|
|
||||||
},
|
|
||||||
recoverPublicKey: {
|
|
||||||
samples: 1000,
|
|
||||||
secp256k1_old: ({ sig, msg }) =>
|
|
||||||
old_secp.recoverPublicKey(msg, new old_secp.Signature(sig.r, sig.s), sig.recovery),
|
|
||||||
secp256k1: ({ sig, msg }) => sig.recoverPublicKey(msg),
|
|
||||||
},
|
|
||||||
hashToCurve: {
|
|
||||||
samples: 500,
|
|
||||||
noble: () => secp256k1.Point.hashToCurve('abcd'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ed25519: {
|
|
||||||
data: () => {
|
|
||||||
function to32Bytes(numOrStr) {
|
|
||||||
const hex = typeof numOrStr === 'string' ? numOrStr : numOrStr.toString(16);
|
|
||||||
return hexToBytes(hex.padStart(64, '0'));
|
|
||||||
}
|
|
||||||
const priv = to32Bytes(0x9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60n);
|
|
||||||
const pub = noble_ed25519.sync.getPublicKey(priv);
|
|
||||||
const msg = to32Bytes('deadbeefdeadbeefdeadbeefdeadbeefdeadbeef');
|
|
||||||
const sig = noble_ed25519.sync.sign(msg, priv);
|
|
||||||
return { pub, priv, msg, sig };
|
|
||||||
},
|
|
||||||
getPublicKey: {
|
|
||||||
samples: 10000,
|
|
||||||
old: () => noble_ed25519.sync.getPublicKey(noble_ed25519.utils.randomPrivateKey()),
|
|
||||||
noble: () => ed25519.getPublicKey(ed25519.utils.randomPrivateKey()),
|
|
||||||
},
|
|
||||||
sign: {
|
|
||||||
samples: 5000,
|
|
||||||
old: ({ msg, priv }) => noble_ed25519.sync.sign(msg, priv),
|
|
||||||
noble: ({ msg, priv }) => ed25519.sign(msg, priv),
|
|
||||||
},
|
|
||||||
verify: {
|
|
||||||
samples: 1000,
|
|
||||||
old: ({ sig, msg, pub }) => noble_ed25519.sync.verify(sig, msg, pub),
|
|
||||||
noble: ({ sig, msg, pub }) => ed25519.verify(sig, msg, pub),
|
|
||||||
},
|
|
||||||
hashToCurve: {
|
|
||||||
samples: 500,
|
|
||||||
noble: () => ed25519.Point.hashToCurve('abcd'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ed448: {
|
|
||||||
data: () => {
|
|
||||||
const priv = ed448.utils.randomPrivateKey();
|
|
||||||
const pub = ed448.getPublicKey(priv);
|
|
||||||
const msg = ed448.utils.randomPrivateKey();
|
|
||||||
const sig = ed448.sign(msg, priv);
|
|
||||||
return { priv, pub, msg, sig };
|
|
||||||
},
|
|
||||||
getPublicKey: {
|
|
||||||
samples: 5000,
|
|
||||||
noble: () => ed448.getPublicKey(ed448.utils.randomPrivateKey()),
|
|
||||||
},
|
|
||||||
sign: {
|
|
||||||
samples: 2500,
|
|
||||||
noble: ({ msg, priv }) => ed448.sign(msg, priv),
|
|
||||||
},
|
|
||||||
verify: {
|
|
||||||
samples: 500,
|
|
||||||
noble: ({ sig, msg, pub }) => ed448.verify(sig, msg, pub),
|
|
||||||
},
|
|
||||||
hashToCurve: {
|
|
||||||
samples: 500,
|
|
||||||
noble: () => ed448.Point.hashToCurve('abcd'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
nist: {
|
|
||||||
data: () => {
|
|
||||||
return { p256: generateData(P256), p384: generateData(P384), p521: generateData(P521) };
|
|
||||||
},
|
|
||||||
getPublicKey: {
|
|
||||||
samples: 2500,
|
|
||||||
P256: () => P256.getPublicKey(P256.utils.randomPrivateKey()),
|
|
||||||
P384: () => P384.getPublicKey(P384.utils.randomPrivateKey()),
|
|
||||||
P521: () => P521.getPublicKey(P521.utils.randomPrivateKey()),
|
|
||||||
},
|
|
||||||
sign: {
|
|
||||||
samples: 1000,
|
|
||||||
P256: ({ p256: { msg, priv } }) => P256.sign(msg, priv),
|
|
||||||
P384: ({ p384: { msg, priv } }) => P384.sign(msg, priv),
|
|
||||||
P521: ({ p521: { msg, priv } }) => P521.sign(msg, priv),
|
|
||||||
},
|
|
||||||
verify: {
|
|
||||||
samples: 250,
|
|
||||||
P256: ({ p256: { sig, msg, pub } }) => P256.verify(sig, msg, pub),
|
|
||||||
P384: ({ p384: { sig, msg, pub } }) => P384.verify(sig, msg, pub),
|
|
||||||
P521: ({ p521: { sig, msg, pub } }) => P521.verify(sig, msg, pub),
|
|
||||||
},
|
|
||||||
hashToCurve: {
|
|
||||||
samples: 500,
|
|
||||||
P256: () => P256.Point.hashToCurve('abcd'),
|
|
||||||
P384: () => P384.Point.hashToCurve('abcd'),
|
|
||||||
P521: () => P521.Point.hashToCurve('abcd'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
stark: {
|
|
||||||
data: () => {
|
|
||||||
const priv = '2dccce1da22003777062ee0870e9881b460a8b7eca276870f57c601f182136c';
|
|
||||||
const msg = 'c465dd6b1bbffdb05442eb17f5ca38ad1aa78a6f56bf4415bdee219114a47';
|
|
||||||
const pub = stark.getPublicKey(priv);
|
|
||||||
const sig = stark.sign(msg, priv);
|
|
||||||
|
|
||||||
const privateKey = '2dccce1da22003777062ee0870e9881b460a8b7eca276870f57c601f182136c';
|
|
||||||
const msgHash = 'c465dd6b1bbffdb05442eb17f5ca38ad1aa78a6f56bf4415bdee219114a47';
|
|
||||||
const keyPair = starkwareCrypto.default.ec.keyFromPrivate(privateKey, 'hex');
|
|
||||||
const publicKeyStark = starkwareCrypto.default.ec.keyFromPublic(
|
|
||||||
keyPair.getPublic(true, 'hex'),
|
|
||||||
'hex'
|
|
||||||
);
|
|
||||||
|
|
||||||
return { priv, sig, msg, pub, publicKeyStark, msgHash, keyPair };
|
|
||||||
},
|
|
||||||
pedersen: {
|
|
||||||
samples: 500,
|
|
||||||
old: () => {
|
|
||||||
return starkwareCrypto.default.pedersen([
|
|
||||||
'3d937c035c878245caf64531a5756109c53068da139362728feb561405371cb',
|
|
||||||
'208a0a10250e382e1e4bbe2880906c2791bf6275695e02fbbc6aeff9cd8b31a',
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
noble: () => {
|
|
||||||
return stark.pedersen(
|
|
||||||
'3d937c035c878245caf64531a5756109c53068da139362728feb561405371cb',
|
|
||||||
'208a0a10250e382e1e4bbe2880906c2791bf6275695e02fbbc6aeff9cd8b31a'
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
verify: {
|
|
||||||
samples: 500,
|
|
||||||
old: ({ publicKeyStark, msgHash, keyPair }) => {
|
|
||||||
return starkwareCrypto.default.verify(
|
|
||||||
publicKeyStark,
|
|
||||||
msgHash,
|
|
||||||
starkwareCrypto.default.sign(keyPair, msgHash)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
noble: ({ priv, msg, pub }) => {
|
|
||||||
return stark.verify(stark.sign(msg, priv), msg, pub);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'bls12-381': {
|
|
||||||
data: async () => {
|
|
||||||
const priv = '28b90deaf189015d3a325908c5e0e4bf00f84f7e639b056ff82d7e70b6eede4c';
|
|
||||||
const pubs = G2_VECTORS.map((v) => bls.getPublicKey(v[0]));
|
|
||||||
const sigs = G2_VECTORS.map((v) => v[2]);
|
|
||||||
const pub = bls.getPublicKey(priv);
|
|
||||||
const pub512 = pubs.slice(0, 512); // .map(bls.PointG1.fromHex)
|
|
||||||
const pub32 = pub512.slice(0, 32);
|
|
||||||
const pub128 = pub512.slice(0, 128);
|
|
||||||
const pub2048 = pub512.concat(pub512, pub512, pub512);
|
|
||||||
const sig512 = sigs.slice(0, 512); // .map(bls.PointG2.fromSignature);
|
|
||||||
const sig32 = sig512.slice(0, 32);
|
|
||||||
const sig128 = sig512.slice(0, 128);
|
|
||||||
const sig2048 = sig512.concat(sig512, sig512, sig512);
|
|
||||||
return {
|
|
||||||
priv,
|
|
||||||
pubs,
|
|
||||||
sigs,
|
|
||||||
pub,
|
|
||||||
pub512,
|
|
||||||
pub32,
|
|
||||||
pub128,
|
|
||||||
pub2048,
|
|
||||||
sig32,
|
|
||||||
sig128,
|
|
||||||
sig512,
|
|
||||||
sig2048,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
init: {
|
|
||||||
samples: 1,
|
|
||||||
old: () => {
|
|
||||||
oldp1 =
|
|
||||||
old_bls.PointG1.BASE.multiply(
|
|
||||||
0x28b90deaf189015d3a325908c5e0e4bf00f84f7e639b056ff82d7e70b6eede4cn
|
|
||||||
);
|
|
||||||
oldp2 =
|
|
||||||
old_bls.PointG2.BASE.multiply(
|
|
||||||
0x28b90deaf189015d3a325908c5e0e4bf00f84f7e639b056ff82d7e70b6eede4dn
|
|
||||||
);
|
|
||||||
old_bls.pairing(oldp1, oldp2);
|
|
||||||
},
|
|
||||||
noble: () => {
|
|
||||||
p1 =
|
|
||||||
bls.G1.Point.BASE.multiply(
|
|
||||||
0x28b90deaf189015d3a325908c5e0e4bf00f84f7e639b056ff82d7e70b6eede4cn
|
|
||||||
);
|
|
||||||
p2 =
|
|
||||||
bls.G2.Point.BASE.multiply(
|
|
||||||
0x28b90deaf189015d3a325908c5e0e4bf00f84f7e639b056ff82d7e70b6eede4dn
|
|
||||||
);
|
|
||||||
bls.pairing(p1, p2);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'getPublicKey (1-bit)': {
|
|
||||||
samples: 1000,
|
|
||||||
old: () => old_bls.getPublicKey('2'.padStart(64, '0')),
|
|
||||||
noble: () => bls.getPublicKey('2'.padStart(64, '0')),
|
|
||||||
},
|
|
||||||
getPublicKey: {
|
|
||||||
samples: 1000,
|
|
||||||
old: ({ priv }) => old_bls.getPublicKey(priv),
|
|
||||||
noble: ({ priv }) => bls.getPublicKey(priv),
|
|
||||||
},
|
|
||||||
sign: {
|
|
||||||
samples: 50,
|
|
||||||
old: ({ priv }) => old_bls.sign('09', priv),
|
|
||||||
noble: ({ priv }) => bls.sign('09', priv),
|
|
||||||
},
|
|
||||||
verify: {
|
|
||||||
samples: 50,
|
|
||||||
old: ({ pub }) =>
|
|
||||||
old_bls.verify(
|
|
||||||
'8647aa9680cd0cdf065b94e818ff2bb948cc97838bcee987b9bc1b76d0a0a6e0d85db4e9d75aaedfc79d4ea2733a21ae0579014de7636dd2943d45b87c82b1c66a289006b0b9767921bb8edd3f6c5c5dec0d54cd65f61513113c50cc977849e5',
|
|
||||||
'09',
|
|
||||||
pub
|
|
||||||
),
|
|
||||||
noble: ({ pub }) =>
|
|
||||||
bls.verify(
|
|
||||||
'8647aa9680cd0cdf065b94e818ff2bb948cc97838bcee987b9bc1b76d0a0a6e0d85db4e9d75aaedfc79d4ea2733a21ae0579014de7636dd2943d45b87c82b1c66a289006b0b9767921bb8edd3f6c5c5dec0d54cd65f61513113c50cc977849e5',
|
|
||||||
'09',
|
|
||||||
pub
|
|
||||||
),
|
|
||||||
},
|
|
||||||
pairing: {
|
|
||||||
samples: 100,
|
|
||||||
old: () => old_bls.pairing(oldp1, oldp2),
|
|
||||||
noble: () => bls.pairing(p1, p2),
|
|
||||||
},
|
|
||||||
'hashToCurve/G1': {
|
|
||||||
samples: 500,
|
|
||||||
old: () => old_bls.PointG1.hashToCurve('abcd'),
|
|
||||||
noble: () => bls.G1.Point.hashToCurve('abcd'),
|
|
||||||
},
|
|
||||||
'hashToCurve/G2': {
|
|
||||||
samples: 200,
|
|
||||||
old: () => old_bls.PointG2.hashToCurve('abcd'),
|
|
||||||
noble: () => bls.G2.Point.hashToCurve('abcd'),
|
|
||||||
},
|
|
||||||
// SLOW PART
|
|
||||||
// Requires points which we cannot init before (data fn same for all)
|
|
||||||
// await mark('sign/nc', 30, () => bls.sign(msgp, priv));
|
|
||||||
// await mark('verify/nc', 30, () => bls.verify(sigp, msgp, pubp));
|
|
||||||
'aggregatePublicKeys/8': {
|
|
||||||
samples: 100,
|
|
||||||
old: ({ pubs }) => old_bls.aggregatePublicKeys(pubs.slice(0, 8)),
|
|
||||||
noble: ({ pubs }) => bls.aggregatePublicKeys(pubs.slice(0, 8)),
|
|
||||||
},
|
|
||||||
'aggregatePublicKeys/32': {
|
|
||||||
samples: 50,
|
|
||||||
old: ({ pub32 }) => old_bls.aggregatePublicKeys(pub32.map(old_bls.PointG1.fromHex)),
|
|
||||||
noble: ({ pub32 }) => bls.aggregatePublicKeys(pub32.map(bls.G1.Point.fromHex)),
|
|
||||||
},
|
|
||||||
'aggregatePublicKeys/128': {
|
|
||||||
samples: 20,
|
|
||||||
old: ({ pub128 }) => old_bls.aggregatePublicKeys(pub128.map(old_bls.PointG1.fromHex)),
|
|
||||||
noble: ({ pub128 }) => bls.aggregatePublicKeys(pub128.map(bls.G1.Point.fromHex)),
|
|
||||||
},
|
|
||||||
'aggregatePublicKeys/512': {
|
|
||||||
samples: 10,
|
|
||||||
old: ({ pub512 }) => old_bls.aggregatePublicKeys(pub512.map(old_bls.PointG1.fromHex)),
|
|
||||||
noble: ({ pub512 }) => bls.aggregatePublicKeys(pub512.map(bls.G1.Point.fromHex)),
|
|
||||||
},
|
|
||||||
'aggregatePublicKeys/2048': {
|
|
||||||
samples: 5,
|
|
||||||
old: ({ pub2048 }) => old_bls.aggregatePublicKeys(pub2048.map(old_bls.PointG1.fromHex)),
|
|
||||||
noble: ({ pub2048 }) => bls.aggregatePublicKeys(pub2048.map(bls.G1.Point.fromHex)),
|
|
||||||
},
|
|
||||||
'aggregateSignatures/8': {
|
|
||||||
samples: 50,
|
|
||||||
old: ({ sigs }) => old_bls.aggregateSignatures(sigs.slice(0, 8)),
|
|
||||||
noble: ({ sigs }) => bls.aggregateSignatures(sigs.slice(0, 8)),
|
|
||||||
},
|
|
||||||
'aggregateSignatures/32': {
|
|
||||||
samples: 10,
|
|
||||||
old: ({ sig32 }) => old_bls.aggregateSignatures(sig32.map(old_bls.PointG2.fromSignature)),
|
|
||||||
noble: ({ sig32 }) => bls.aggregateSignatures(sig32.map(bls.Signature.decode)),
|
|
||||||
},
|
|
||||||
'aggregateSignatures/128': {
|
|
||||||
samples: 5,
|
|
||||||
old: ({ sig128 }) => old_bls.aggregateSignatures(sig128.map(old_bls.PointG2.fromSignature)),
|
|
||||||
noble: ({ sig128 }) => bls.aggregateSignatures(sig128.map(bls.Signature.decode)),
|
|
||||||
},
|
|
||||||
'aggregateSignatures/512': {
|
|
||||||
samples: 3,
|
|
||||||
old: ({ sig512 }) => old_bls.aggregateSignatures(sig512.map(old_bls.PointG2.fromSignature)),
|
|
||||||
noble: ({ sig512 }) => bls.aggregateSignatures(sig512.map(bls.Signature.decode)),
|
|
||||||
},
|
|
||||||
'aggregateSignatures/2048': {
|
|
||||||
samples: 2,
|
|
||||||
old: ({ sig2048 }) => old_bls.aggregateSignatures(sig2048.map(old_bls.PointG2.fromSignature)),
|
|
||||||
noble: ({ sig2048 }) => bls.aggregateSignatures(sig2048.map(bls.Signature.decode)),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const main = () =>
|
|
||||||
run(async () => {
|
|
||||||
for (const [name, curve] of Object.entries(CURVES)) {
|
|
||||||
console.log(`==== ${name} ====`);
|
|
||||||
const data = await curve.data();
|
|
||||||
for (const [fnName, libs] of Object.entries(curve)) {
|
|
||||||
if (fnName === 'data') continue;
|
|
||||||
const samples = libs.samples;
|
|
||||||
console.log(` - ${fnName} (samples: ${samples})`);
|
|
||||||
for (const [lib, fn] of Object.entries(libs)) {
|
|
||||||
if (lib === 'samples') continue;
|
|
||||||
if (ONLY_NOBLE && lib !== 'noble') continue;
|
|
||||||
await mark(` ${lib}`, samples, () => fn(data));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Log current RAM
|
|
||||||
bench.logMem();
|
|
||||||
});
|
|
||||||
|
|
||||||
// ESM is broken.
|
|
||||||
import url from 'url';
|
|
||||||
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
|
|
||||||
main();
|
|
||||||
}
|
|
||||||
13
benchmark/modular.js
Normal file
13
benchmark/modular.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { run, mark } from 'micro-bmark';
|
||||||
|
import { secp256k1 } from '../secp256k1.js';
|
||||||
|
import { Field as Fp } from '../abstract/modular.js';
|
||||||
|
|
||||||
|
run(async () => {
|
||||||
|
console.log(`\x1b[36mmodular, secp256k1 field\x1b[0m`);
|
||||||
|
const { Fp: secpFp } = secp256k1.CURVE;
|
||||||
|
await mark('invert a', 300000, () => secpFp.inv(2n ** 232n - 5910n));
|
||||||
|
await mark('invert b', 300000, () => secpFp.inv(2n ** 231n - 5910n));
|
||||||
|
await mark('sqrt p = 3 mod 4', 15000, () => secpFp.sqrt(2n ** 231n - 5910n));
|
||||||
|
const FpStark = Fp(BigInt('0x800000000000011000000000000000000000000000000000000000000000001'));
|
||||||
|
await mark('sqrt tonneli-shanks', 500, () => FpStark.sqrt(2n ** 231n - 5909n))
|
||||||
|
});
|
||||||
@@ -1,26 +1,21 @@
|
|||||||
{
|
{
|
||||||
"name": "benchmark",
|
"name": "benchmark",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "benchmarks",
|
"description": "benchmarks",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"bench": "node index.js"
|
"bench": "node index.js"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"micro-bmark": "0.2.1"
|
"micro-bmark": "0.3.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@noble/bls12-381": "^1.4.0",
|
"@noble/hashes": "^1.1.5",
|
||||||
"@noble/ed25519": "^1.7.1",
|
"elliptic": "^6.5.4"
|
||||||
"@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();
|
||||||
|
});
|
||||||
@@ -2,6 +2,6 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"browser": {
|
"browser": {
|
||||||
"crypto": false,
|
"crypto": false,
|
||||||
"./crypto": "./esm/cryptoBrowser.js"
|
"./crypto": "./esm/crypto.js"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
112
package-lock.json
generated
Normal file
112
package-lock.json
generated
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
{
|
||||||
|
"name": "@noble/curves",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "@noble/curves",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@noble/hashes": "1.3.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"fast-check": "3.0.0",
|
||||||
|
"micro-bmark": "0.3.1",
|
||||||
|
"micro-should": "0.4.0",
|
||||||
|
"prettier": "2.8.4",
|
||||||
|
"typescript": "5.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@noble/hashes": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"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.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz",
|
||||||
|
"integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==",
|
||||||
|
"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": "5.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.2.tgz",
|
||||||
|
"integrity": "sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"tsc": "bin/tsc",
|
||||||
|
"tsserver": "bin/tsserver"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.20"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
185
package.json
185
package.json
@@ -1,14 +1,21 @@
|
|||||||
{
|
{
|
||||||
"name": "@noble/curves",
|
"name": "@noble/curves",
|
||||||
"version": "0.5.1",
|
"version": "1.0.0",
|
||||||
"description": "Minimal, auditable JS implementation of elliptic curve cryptography",
|
"description": "Audited & minimal JS implementation of elliptic curve cryptography",
|
||||||
"files": [
|
"files": [
|
||||||
"lib"
|
"abstract",
|
||||||
|
"esm",
|
||||||
|
"src",
|
||||||
|
"*.js",
|
||||||
|
"*.js.map",
|
||||||
|
"*.d.ts",
|
||||||
|
"*.d.ts.map"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"bench": "node benchmark/index.js",
|
"bench": "cd benchmark; node secp256k1.js; node curves.js; node ecdh.js; node hash-to-curve.js; node modular.js; node bls.js",
|
||||||
"build": "tsc && tsc -p tsconfig.esm.json",
|
"build": "tsc && tsc -p tsconfig.esm.json",
|
||||||
"build:release": "rollup -c rollup.config.js",
|
"build:release": "rollup -c rollup.config.js",
|
||||||
|
"build:clean": "rm *.{js,d.ts,d.ts.map,js.map} esm/*.{js,d.ts,d.ts.map,js.map} 2> /dev/null",
|
||||||
"lint": "prettier --check 'src/**/*.{js,ts}' 'test/*.js'",
|
"lint": "prettier --check 'src/**/*.{js,ts}' 'test/*.js'",
|
||||||
"format": "prettier --write 'src/**/*.{js,ts}' 'test/*.js'",
|
"format": "prettier --write 'src/**/*.{js,ts}' 'test/*.js'",
|
||||||
"test": "node test/index.test.js"
|
"test": "node test/index.test.js"
|
||||||
@@ -21,142 +28,126 @@
|
|||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@noble/hashes": "1.1.5"
|
"@noble/hashes": "1.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-node-resolve": "13.3.0",
|
|
||||||
"@scure/base": "~1.1.1",
|
|
||||||
"@scure/bip32": "~1.1.1",
|
|
||||||
"@scure/bip39": "~1.1.0",
|
|
||||||
"@types/node": "18.11.3",
|
|
||||||
"fast-check": "3.0.0",
|
"fast-check": "3.0.0",
|
||||||
"micro-bmark": "0.2.0",
|
"micro-bmark": "0.3.1",
|
||||||
"micro-should": "0.2.0",
|
"micro-should": "0.4.0",
|
||||||
"prettier": "2.6.2",
|
"prettier": "2.8.4",
|
||||||
"rollup": "2.75.5",
|
"typescript": "5.0.2"
|
||||||
"typescript": "4.7.3"
|
|
||||||
},
|
},
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": {
|
".": {
|
||||||
"types": "./lib/index.d.ts",
|
"types": "./index.d.ts",
|
||||||
"import": "./lib/esm/index.js",
|
"import": "./esm/index.js",
|
||||||
"default": "./lib/index.js"
|
"default": "./index.js"
|
||||||
},
|
},
|
||||||
"./abstract/edwards": {
|
"./abstract/edwards": {
|
||||||
"types": "./lib/abstract/edwards.d.ts",
|
"types": "./abstract/edwards.d.ts",
|
||||||
"import": "./lib/esm/abstract/edwards.js",
|
"import": "./esm/abstract/edwards.js",
|
||||||
"default": "./lib/abstract/edwards.js"
|
"default": "./abstract/edwards.js"
|
||||||
},
|
},
|
||||||
"./abstract/modular": {
|
"./abstract/modular": {
|
||||||
"types": "./lib/abstract/modular.d.ts",
|
"types": "./abstract/modular.d.ts",
|
||||||
"import": "./lib/esm/abstract/modular.js",
|
"import": "./esm/abstract/modular.js",
|
||||||
"default": "./lib/abstract/modular.js"
|
"default": "./abstract/modular.js"
|
||||||
},
|
},
|
||||||
"./abstract/montgomery": {
|
"./abstract/montgomery": {
|
||||||
"types": "./lib/abstract/montgomery.d.ts",
|
"types": "./abstract/montgomery.d.ts",
|
||||||
"import": "./lib/esm/abstract/montgomery.js",
|
"import": "./esm/abstract/montgomery.js",
|
||||||
"default": "./lib/abstract/montgomery.js"
|
"default": "./abstract/montgomery.js"
|
||||||
},
|
},
|
||||||
"./abstract/weierstrass": {
|
"./abstract/weierstrass": {
|
||||||
"types": "./lib/abstract/weierstrass.d.ts",
|
"types": "./abstract/weierstrass.d.ts",
|
||||||
"import": "./lib/esm/abstract/weierstrass.js",
|
"import": "./esm/abstract/weierstrass.js",
|
||||||
"default": "./lib/abstract/weierstrass.js"
|
"default": "./abstract/weierstrass.js"
|
||||||
},
|
},
|
||||||
"./abstract/bls": {
|
"./abstract/bls": {
|
||||||
"types": "./lib/abstract/bls.d.ts",
|
"types": "./abstract/bls.d.ts",
|
||||||
"import": "./lib/esm/abstract/bls.js",
|
"import": "./esm/abstract/bls.js",
|
||||||
"default": "./lib/abstract/bls.js"
|
"default": "./abstract/bls.js"
|
||||||
},
|
},
|
||||||
"./abstract/hash-to-curve": {
|
"./abstract/hash-to-curve": {
|
||||||
"types": "./lib/abstract/hash-to-curve.d.ts",
|
"types": "./abstract/hash-to-curve.d.ts",
|
||||||
"import": "./lib/esm/abstract/hash-to-curve.js",
|
"import": "./esm/abstract/hash-to-curve.js",
|
||||||
"default": "./lib/abstract/hash-to-curve.js"
|
"default": "./abstract/hash-to-curve.js"
|
||||||
},
|
},
|
||||||
"./abstract/group": {
|
"./abstract/curve": {
|
||||||
"types": "./lib/abstract/group.d.ts",
|
"types": "./abstract/curve.d.ts",
|
||||||
"import": "./lib/esm/abstract/group.js",
|
"import": "./esm/abstract/curve.js",
|
||||||
"default": "./lib/abstract/group.js"
|
"default": "./abstract/curve.js"
|
||||||
},
|
},
|
||||||
"./abstract/utils": {
|
"./abstract/utils": {
|
||||||
"types": "./lib/abstract/utils.d.ts",
|
"types": "./abstract/utils.d.ts",
|
||||||
"import": "./lib/esm/abstract/utils.js",
|
"import": "./esm/abstract/utils.js",
|
||||||
"default": "./lib/abstract/utils.js"
|
"default": "./abstract/utils.js"
|
||||||
|
},
|
||||||
|
"./abstract/poseidon": {
|
||||||
|
"types": "./abstract/poseidon.d.ts",
|
||||||
|
"import": "./esm/abstract/poseidon.js",
|
||||||
|
"default": "./abstract/poseidon.js"
|
||||||
},
|
},
|
||||||
"./_shortw_utils": {
|
"./_shortw_utils": {
|
||||||
"types": "./lib/_shortw_utils.d.ts",
|
"types": "./_shortw_utils.d.ts",
|
||||||
"import": "./lib/esm/_shortw_utils.js",
|
"import": "./esm/_shortw_utils.js",
|
||||||
"default": "./lib/_shortw_utils.js"
|
"default": "./_shortw_utils.js"
|
||||||
},
|
},
|
||||||
"./bls12-381": {
|
"./bls12-381": {
|
||||||
"types": "./lib/bls12-381.d.ts",
|
"types": "./bls12-381.d.ts",
|
||||||
"import": "./lib/esm/bls12-381.js",
|
"import": "./esm/bls12-381.js",
|
||||||
"default": "./lib/bls12-381.js"
|
"default": "./bls12-381.js"
|
||||||
},
|
},
|
||||||
"./bn": {
|
"./bn254": {
|
||||||
"types": "./lib/bn.d.ts",
|
"types": "./bn254.d.ts",
|
||||||
"import": "./lib/esm/bn.js",
|
"import": "./esm/bn254.js",
|
||||||
"default": "./lib/bn.js"
|
"default": "./bn254.js"
|
||||||
},
|
},
|
||||||
"./ed25519": {
|
"./ed25519": {
|
||||||
"types": "./lib/ed25519.d.ts",
|
"types": "./ed25519.d.ts",
|
||||||
"import": "./lib/esm/ed25519.js",
|
"import": "./esm/ed25519.js",
|
||||||
"default": "./lib/ed25519.js"
|
"default": "./ed25519.js"
|
||||||
},
|
},
|
||||||
"./ed448": {
|
"./ed448": {
|
||||||
"types": "./lib/ed448.d.ts",
|
"types": "./ed448.d.ts",
|
||||||
"import": "./lib/esm/ed448.js",
|
"import": "./esm/ed448.js",
|
||||||
"default": "./lib/ed448.js"
|
"default": "./ed448.js"
|
||||||
},
|
},
|
||||||
"./index": {
|
"./index": {
|
||||||
"types": "./lib/index.d.ts",
|
"types": "./index.d.ts",
|
||||||
"import": "./lib/esm/index.js",
|
"import": "./esm/index.js",
|
||||||
"default": "./lib/index.js"
|
"default": "./index.js"
|
||||||
},
|
},
|
||||||
"./jubjub": {
|
"./jubjub": {
|
||||||
"types": "./lib/jubjub.d.ts",
|
"types": "./jubjub.d.ts",
|
||||||
"import": "./lib/esm/jubjub.js",
|
"import": "./esm/jubjub.js",
|
||||||
"default": "./lib/jubjub.js"
|
"default": "./jubjub.js"
|
||||||
},
|
|
||||||
"./p192": {
|
|
||||||
"types": "./lib/p192.d.ts",
|
|
||||||
"import": "./lib/esm/p192.js",
|
|
||||||
"default": "./lib/p192.js"
|
|
||||||
},
|
|
||||||
"./p224": {
|
|
||||||
"types": "./lib/p224.d.ts",
|
|
||||||
"import": "./lib/esm/p224.js",
|
|
||||||
"default": "./lib/p224.js"
|
|
||||||
},
|
},
|
||||||
"./p256": {
|
"./p256": {
|
||||||
"types": "./lib/p256.d.ts",
|
"types": "./p256.d.ts",
|
||||||
"import": "./lib/esm/p256.js",
|
"import": "./esm/p256.js",
|
||||||
"default": "./lib/p256.js"
|
"default": "./p256.js"
|
||||||
},
|
},
|
||||||
"./p384": {
|
"./p384": {
|
||||||
"types": "./lib/p384.d.ts",
|
"types": "./p384.d.ts",
|
||||||
"import": "./lib/esm/p384.js",
|
"import": "./esm/p384.js",
|
||||||
"default": "./lib/p384.js"
|
"default": "./p384.js"
|
||||||
},
|
},
|
||||||
"./p521": {
|
"./p521": {
|
||||||
"types": "./lib/p521.d.ts",
|
"types": "./p521.d.ts",
|
||||||
"import": "./lib/esm/p521.js",
|
"import": "./esm/p521.js",
|
||||||
"default": "./lib/p521.js"
|
"default": "./p521.js"
|
||||||
},
|
},
|
||||||
"./pasta": {
|
"./pasta": {
|
||||||
"types": "./lib/pasta.d.ts",
|
"types": "./pasta.d.ts",
|
||||||
"import": "./lib/esm/pasta.js",
|
"import": "./esm/pasta.js",
|
||||||
"default": "./lib/pasta.js"
|
"default": "./pasta.js"
|
||||||
},
|
},
|
||||||
"./secp256k1": {
|
"./secp256k1": {
|
||||||
"types": "./lib/secp256k1.d.ts",
|
"types": "./secp256k1.d.ts",
|
||||||
"import": "./lib/esm/secp256k1.js",
|
"import": "./esm/secp256k1.js",
|
||||||
"default": "./lib/secp256k1.js"
|
"default": "./secp256k1.js"
|
||||||
},
|
|
||||||
"./stark": {
|
|
||||||
"types": "./lib/stark.d.ts",
|
|
||||||
"import": "./lib/esm/stark.js",
|
|
||||||
"default": "./lib/stark.js"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
@@ -177,7 +168,7 @@
|
|||||||
"bn254",
|
"bn254",
|
||||||
"pasta",
|
"pasta",
|
||||||
"bls",
|
"bls",
|
||||||
"nist",
|
"noble",
|
||||||
"ecc",
|
"ecc",
|
||||||
"ecdsa",
|
"ecdsa",
|
||||||
"eddsa",
|
"eddsa",
|
||||||
@@ -189,4 +180,4 @@
|
|||||||
"url": "https://paulmillr.com/funding/"
|
"url": "https://paulmillr.com/funding/"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@ import { concatBytes, randomBytes } from '@noble/hashes/utils';
|
|||||||
import { weierstrass, CurveType } from './abstract/weierstrass.js';
|
import { weierstrass, CurveType } from './abstract/weierstrass.js';
|
||||||
import { CHash } from './abstract/utils.js';
|
import { CHash } from './abstract/utils.js';
|
||||||
|
|
||||||
|
// connects noble-curves to noble-hashes
|
||||||
export function getHash(hash: CHash) {
|
export function getHash(hash: CHash) {
|
||||||
return {
|
return {
|
||||||
hash,
|
hash,
|
||||||
|
|||||||
@@ -1,110 +1,133 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
// Barreto-Lynn-Scott Curves. A family of pairing friendly curves, with embedding degree = 12 or 24
|
/**
|
||||||
// NOTE: only 12 supported for now
|
* BLS (Barreto-Lynn-Scott) family of pairing-friendly curves.
|
||||||
// Constructed from pair of weierstrass curves, based pairing logic
|
* Implements BLS (Boneh-Lynn-Shacham) signatures.
|
||||||
import * as mod from './modular.js';
|
* Consists of two curves: G1 and G2:
|
||||||
import { ensureBytes, numberToBytesBE, bytesToNumberBE, bitLen, bitGet } from './utils.js';
|
* - G1 is a subgroup of (x, y) E(Fq) over y² = x³ + 4.
|
||||||
import * as utils from './utils.js';
|
* - G2 is a subgroup of ((x₁, x₂+i), (y₁, y₂+i)) E(Fq²) over y² = x³ + 4(1 + i) where i is √-1
|
||||||
// Types
|
* - Gt, created by bilinear (ate) pairing e(G1, G2), consists of p-th roots of unity in
|
||||||
import { hexToBytes, bytesToHex, Hex, PrivKey } from './utils.js';
|
* Fq^k where k is embedding degree. Only degree 12 is currently supported, 24 is not.
|
||||||
import { htfOpts, stringToBytes, hash_to_field, expand_message_xmd } from './hash-to-curve.js';
|
* Pairing is used to aggregate and verify signatures.
|
||||||
import { CurvePointsType, PointType, CurvePointsRes, weierstrassPoints } from './weierstrass.js';
|
* We are using Fp for private keys (shorter) and Fp₂ for signatures (longer).
|
||||||
|
* Some projects may prefer to swap this relation, it is not supported for now.
|
||||||
|
*/
|
||||||
|
import { AffinePoint } from './curve.js';
|
||||||
|
import { IField, hashToPrivateScalar } from './modular.js';
|
||||||
|
import { Hex, PrivKey, CHash, bitLen, bitGet, ensureBytes } from './utils.js';
|
||||||
|
import * as htf from './hash-to-curve.js';
|
||||||
|
import {
|
||||||
|
CurvePointsType,
|
||||||
|
ProjPointType as ProjPointType,
|
||||||
|
CurvePointsRes,
|
||||||
|
weierstrassPoints,
|
||||||
|
} from './weierstrass.js';
|
||||||
|
|
||||||
type Fp = bigint; // Can be different field?
|
type Fp = bigint; // Can be different field?
|
||||||
|
|
||||||
|
// prettier-ignore
|
||||||
|
const _2n = BigInt(2), _3n = BigInt(3);
|
||||||
|
|
||||||
export type SignatureCoder<Fp2> = {
|
export type SignatureCoder<Fp2> = {
|
||||||
decode(hex: Hex): PointType<Fp2>;
|
fromHex(hex: Hex): ProjPointType<Fp2>;
|
||||||
encode(point: PointType<Fp2>): Uint8Array;
|
toRawBytes(point: ProjPointType<Fp2>): Uint8Array;
|
||||||
|
toHex(point: ProjPointType<Fp2>): string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CurveType<Fp, Fp2, Fp6, Fp12> = {
|
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'> & {
|
G2: Omit<CurvePointsType<Fp2>, 'n'> & {
|
||||||
Signature: SignatureCoder<Fp2>;
|
Signature: SignatureCoder<Fp2>;
|
||||||
|
mapToCurve: htf.MapToCurve<Fp2>;
|
||||||
|
htfDefaults: htf.Opts;
|
||||||
};
|
};
|
||||||
x: bigint;
|
fields: {
|
||||||
Fp: mod.Field<Fp>;
|
Fp: IField<Fp>;
|
||||||
Fr: mod.Field<bigint>;
|
Fr: IField<bigint>;
|
||||||
Fp2: mod.Field<Fp2> & {
|
Fp2: IField<Fp2> & {
|
||||||
reim: (num: Fp2) => { re: bigint; im: bigint };
|
reim: (num: Fp2) => { re: bigint; im: bigint };
|
||||||
multiplyByB: (num: Fp2) => Fp2;
|
multiplyByB: (num: Fp2) => Fp2;
|
||||||
frobeniusMap(num: Fp2, power: number): Fp2;
|
frobeniusMap(num: Fp2, power: number): Fp2;
|
||||||
|
};
|
||||||
|
Fp6: IField<Fp6>;
|
||||||
|
Fp12: IField<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;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
Fp6: mod.Field<Fp6>;
|
params: {
|
||||||
Fp12: mod.Field<Fp12> & {
|
x: bigint;
|
||||||
frobeniusMap(num: Fp12, power: number): Fp12;
|
r: bigint;
|
||||||
multiplyBy014(num: Fp12, o0: Fp2, o1: Fp2, o4: Fp2): Fp12;
|
|
||||||
conjugate(num: Fp12): Fp12;
|
|
||||||
finalExponentiate(num: Fp12): Fp12;
|
|
||||||
};
|
};
|
||||||
htfDefaults: htfOpts;
|
htfDefaults: htf.Opts;
|
||||||
hash: utils.CHash; // Because we need outputLen for DRBG
|
hash: CHash; // Because we need outputLen for DRBG
|
||||||
randomBytes: (bytesLength?: number) => Uint8Array;
|
randomBytes: (bytesLength?: number) => Uint8Array;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CurveFn<Fp, Fp2, Fp6, Fp12> = {
|
export type CurveFn<Fp, Fp2, Fp6, Fp12> = {
|
||||||
CURVE: CurveType<Fp, Fp2, Fp6, Fp12>;
|
|
||||||
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>;
|
|
||||||
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;
|
|
||||||
getPublicKey: (privateKey: PrivKey) => Uint8Array;
|
getPublicKey: (privateKey: PrivKey) => Uint8Array;
|
||||||
sign: {
|
sign: {
|
||||||
(message: Hex, privateKey: PrivKey): Uint8Array;
|
(message: Hex, privateKey: PrivKey): Uint8Array;
|
||||||
(message: PointType<Fp2>, privateKey: PrivKey): PointType<Fp2>;
|
(message: ProjPointType<Fp2>, privateKey: PrivKey): ProjPointType<Fp2>;
|
||||||
};
|
};
|
||||||
verify: (
|
verify: (
|
||||||
signature: Hex | PointType<Fp2>,
|
signature: Hex | ProjPointType<Fp2>,
|
||||||
message: Hex | PointType<Fp2>,
|
message: Hex | ProjPointType<Fp2>,
|
||||||
publicKey: Hex | PointType<Fp>
|
publicKey: Hex | ProjPointType<Fp>
|
||||||
|
) => boolean;
|
||||||
|
verifyBatch: (
|
||||||
|
signature: Hex | ProjPointType<Fp2>,
|
||||||
|
messages: (Hex | ProjPointType<Fp2>)[],
|
||||||
|
publicKeys: (Hex | ProjPointType<Fp>)[]
|
||||||
) => boolean;
|
) => boolean;
|
||||||
aggregatePublicKeys: {
|
aggregatePublicKeys: {
|
||||||
(publicKeys: Hex[]): Uint8Array;
|
(publicKeys: Hex[]): Uint8Array;
|
||||||
(publicKeys: PointType<Fp>[]): PointType<Fp>;
|
(publicKeys: ProjPointType<Fp>[]): ProjPointType<Fp>;
|
||||||
};
|
};
|
||||||
aggregateSignatures: {
|
aggregateSignatures: {
|
||||||
(signatures: Hex[]): Uint8Array;
|
(signatures: Hex[]): Uint8Array;
|
||||||
(signatures: PointType<Fp2>[]): PointType<Fp2>;
|
(signatures: ProjPointType<Fp2>[]): ProjPointType<Fp2>;
|
||||||
|
};
|
||||||
|
millerLoop: (ell: [Fp2, Fp2, Fp2][], g1: [Fp, Fp]) => Fp12;
|
||||||
|
pairing: (P: ProjPointType<Fp>, Q: ProjPointType<Fp2>, withFinalExponent?: boolean) => Fp12;
|
||||||
|
G1: CurvePointsRes<Fp> & ReturnType<typeof htf.createHasher<Fp>>;
|
||||||
|
G2: CurvePointsRes<Fp2> & ReturnType<typeof htf.createHasher<Fp2>>;
|
||||||
|
Signature: SignatureCoder<Fp2>;
|
||||||
|
params: {
|
||||||
|
x: bigint;
|
||||||
|
r: bigint;
|
||||||
|
G1b: bigint;
|
||||||
|
G2b: Fp2;
|
||||||
|
};
|
||||||
|
fields: {
|
||||||
|
Fp: IField<Fp>;
|
||||||
|
Fp2: IField<Fp2>;
|
||||||
|
Fp6: IField<Fp6>;
|
||||||
|
Fp12: IField<Fp12>;
|
||||||
|
Fr: IField<bigint>;
|
||||||
};
|
};
|
||||||
verifyBatch: (
|
|
||||||
signature: Hex | PointType<Fp2>,
|
|
||||||
messages: (Hex | PointType<Fp2>)[],
|
|
||||||
publicKeys: (Hex | PointType<Fp>)[]
|
|
||||||
) => boolean;
|
|
||||||
utils: {
|
utils: {
|
||||||
bytesToHex: typeof utils.bytesToHex;
|
randomPrivateKey: () => Uint8Array;
|
||||||
hexToBytes: typeof utils.hexToBytes;
|
calcPairingPrecomputes: (p: AffinePoint<Fp2>) => [Fp2, Fp2, Fp2][];
|
||||||
stringToBytes: typeof stringToBytes;
|
|
||||||
hashToField: typeof hash_to_field;
|
|
||||||
expandMessageXMD: typeof expand_message_xmd;
|
|
||||||
mod: typeof mod.mod;
|
|
||||||
getDSTLabel: () => string;
|
|
||||||
setDSTLabel(newLabel: string): void;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export function bls<Fp2, Fp6, Fp12>(
|
export function bls<Fp2, Fp6, Fp12>(
|
||||||
CURVE: CurveType<Fp, Fp2, Fp6, Fp12>
|
CURVE: CurveType<Fp, Fp2, Fp6, Fp12>
|
||||||
): CurveFn<Fp, Fp2, Fp6, Fp12> {
|
): CurveFn<Fp, Fp2, Fp6, Fp12> {
|
||||||
// Fields looks pretty specific for curve, so for now we need to pass them with options
|
// Fields are specific for curve, so for now we'll need to pass them with opts
|
||||||
const Fp = CURVE.Fp;
|
const { Fp, Fr, Fp2, Fp6, Fp12 } = CURVE.fields;
|
||||||
const Fr = CURVE.Fr;
|
const BLS_X_LEN = bitLen(CURVE.params.x);
|
||||||
const Fp2 = CURVE.Fp2;
|
const groupLen = 32; // TODO: calculate; hardcoded for now
|
||||||
const Fp6 = CURVE.Fp6;
|
|
||||||
const Fp12 = CURVE.Fp12;
|
|
||||||
const BLS_X_LEN = bitLen(CURVE.x);
|
|
||||||
|
|
||||||
// Pre-compute coefficients for sparse multiplication
|
// Pre-compute coefficients for sparse multiplication
|
||||||
// Point addition and point double calculations is reused for coefficients
|
// Point addition and point double calculations is reused for coefficients
|
||||||
function calcPairingPrecomputes(x: Fp2, y: Fp2) {
|
function calcPairingPrecomputes(p: AffinePoint<Fp2>) {
|
||||||
|
const { x, y } = p;
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
const Qx = x, Qy = y, Qz = Fp2.ONE;
|
const Qx = x, Qy = y, Qz = Fp2.ONE;
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
@@ -112,32 +135,32 @@ export function bls<Fp2, Fp6, Fp12>(
|
|||||||
let ell_coeff: [Fp2, Fp2, Fp2][] = [];
|
let ell_coeff: [Fp2, Fp2, Fp2][] = [];
|
||||||
for (let i = BLS_X_LEN - 2; i >= 0; i--) {
|
for (let i = BLS_X_LEN - 2; i >= 0; i--) {
|
||||||
// Double
|
// Double
|
||||||
let t0 = Fp2.square(Ry); // Ry²
|
let t0 = Fp2.sqr(Ry); // Ry²
|
||||||
let t1 = Fp2.square(Rz); // Rz²
|
let t1 = Fp2.sqr(Rz); // Rz²
|
||||||
let t2 = Fp2.multiplyByB(Fp2.mul(t1, 3n)); // 3 * T1 * B
|
let t2 = Fp2.multiplyByB(Fp2.mul(t1, _3n)); // 3 * T1 * B
|
||||||
let t3 = Fp2.mul(t2, 3n); // 3 * T2
|
let t3 = Fp2.mul(t2, _3n); // 3 * T2
|
||||||
let t4 = Fp2.sub(Fp2.sub(Fp2.square(Fp2.add(Ry, Rz)), t1), t0); // (Ry + Rz)² - T1 - T0
|
let t4 = Fp2.sub(Fp2.sub(Fp2.sqr(Fp2.add(Ry, Rz)), t1), t0); // (Ry + Rz)² - T1 - T0
|
||||||
ell_coeff.push([
|
ell_coeff.push([
|
||||||
Fp2.sub(t2, t0), // T2 - T0
|
Fp2.sub(t2, t0), // T2 - T0
|
||||||
Fp2.mul(Fp2.square(Rx), 3n), // 3 * Rx²
|
Fp2.mul(Fp2.sqr(Rx), _3n), // 3 * Rx²
|
||||||
Fp2.negate(t4), // -T4
|
Fp2.neg(t4), // -T4
|
||||||
]);
|
]);
|
||||||
Rx = Fp2.div(Fp2.mul(Fp2.mul(Fp2.sub(t0, t3), Rx), Ry), 2n); // ((T0 - T3) * Rx * Ry) / 2
|
Rx = Fp2.div(Fp2.mul(Fp2.mul(Fp2.sub(t0, t3), Rx), Ry), _2n); // ((T0 - T3) * Rx * Ry) / 2
|
||||||
Ry = Fp2.sub(Fp2.square(Fp2.div(Fp2.add(t0, t3), 2n)), Fp2.mul(Fp2.square(t2), 3n)); // ((T0 + T3) / 2)² - 3 * T2²
|
Ry = Fp2.sub(Fp2.sqr(Fp2.div(Fp2.add(t0, t3), _2n)), Fp2.mul(Fp2.sqr(t2), _3n)); // ((T0 + T3) / 2)² - 3 * T2²
|
||||||
Rz = Fp2.mul(t0, t4); // T0 * T4
|
Rz = Fp2.mul(t0, t4); // T0 * T4
|
||||||
if (bitGet(CURVE.x, i)) {
|
if (bitGet(CURVE.params.x, i)) {
|
||||||
// Addition
|
// Addition
|
||||||
let t0 = Fp2.sub(Ry, Fp2.mul(Qy, Rz)); // Ry - Qy * Rz
|
let t0 = Fp2.sub(Ry, Fp2.mul(Qy, Rz)); // Ry - Qy * Rz
|
||||||
let t1 = Fp2.sub(Rx, Fp2.mul(Qx, Rz)); // Rx - Qx * Rz
|
let t1 = Fp2.sub(Rx, Fp2.mul(Qx, Rz)); // Rx - Qx * Rz
|
||||||
ell_coeff.push([
|
ell_coeff.push([
|
||||||
Fp2.sub(Fp2.mul(t0, Qx), Fp2.mul(t1, Qy)), // T0 * Qx - T1 * Qy
|
Fp2.sub(Fp2.mul(t0, Qx), Fp2.mul(t1, Qy)), // T0 * Qx - T1 * Qy
|
||||||
Fp2.negate(t0), // -T0
|
Fp2.neg(t0), // -T0
|
||||||
t1, // T1
|
t1, // T1
|
||||||
]);
|
]);
|
||||||
let t2 = Fp2.square(t1); // T1²
|
let t2 = Fp2.sqr(t1); // T1²
|
||||||
let t3 = Fp2.mul(t2, t1); // T2 * T1
|
let t3 = Fp2.mul(t2, t1); // T2 * T1
|
||||||
let t4 = Fp2.mul(t2, Rx); // T2 * Rx
|
let t4 = Fp2.mul(t2, Rx); // T2 * Rx
|
||||||
let t5 = Fp2.add(Fp2.sub(t3, Fp2.mul(t4, 2n)), Fp2.mul(Fp2.square(t0), Rz)); // T3 - 2 * T4 + T0² * Rz
|
let t5 = Fp2.add(Fp2.sub(t3, Fp2.mul(t4, _2n)), Fp2.mul(Fp2.sqr(t0), Rz)); // T3 - 2 * T4 + T0² * Rz
|
||||||
Rx = Fp2.mul(t1, t5); // T1 * T5
|
Rx = Fp2.mul(t1, t5); // T1 * T5
|
||||||
Ry = Fp2.sub(Fp2.mul(Fp2.sub(t4, t5), t0), Fp2.mul(t3, Ry)); // (T4 - T5) * T0 - T3 * Ry
|
Ry = Fp2.sub(Fp2.mul(Fp2.sub(t4, t5), t0), Fp2.mul(t3, Ry)); // (T4 - T5) * T0 - T3 * Ry
|
||||||
Rz = Fp2.mul(Rz, t3); // Rz * T3
|
Rz = Fp2.mul(Rz, t3); // Rz * T3
|
||||||
@@ -147,102 +170,39 @@ export function bls<Fp2, Fp6, Fp12>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function millerLoop(ell: [Fp2, Fp2, Fp2][], g1: [Fp, Fp]): Fp12 {
|
function millerLoop(ell: [Fp2, Fp2, Fp2][], g1: [Fp, Fp]): Fp12 {
|
||||||
|
const { x } = CURVE.params;
|
||||||
const Px = g1[0];
|
const Px = g1[0];
|
||||||
const Py = g1[1];
|
const Py = g1[1];
|
||||||
let f12 = Fp12.ONE;
|
let f12 = Fp12.ONE;
|
||||||
for (let j = 0, i = BLS_X_LEN - 2; i >= 0; i--, j++) {
|
for (let j = 0, i = BLS_X_LEN - 2; i >= 0; i--, j++) {
|
||||||
const E = ell[j];
|
const E = ell[j];
|
||||||
f12 = Fp12.multiplyBy014(f12, E[0], Fp2.mul(E[1], Px), Fp2.mul(E[2], Py));
|
f12 = Fp12.multiplyBy014(f12, E[0], Fp2.mul(E[1], Px), Fp2.mul(E[2], Py));
|
||||||
if (bitGet(CURVE.x, i)) {
|
if (bitGet(x, i)) {
|
||||||
j += 1;
|
j += 1;
|
||||||
const F = ell[j];
|
const F = ell[j];
|
||||||
f12 = Fp12.multiplyBy014(f12, F[0], Fp2.mul(F[1], Px), Fp2.mul(F[2], Py));
|
f12 = Fp12.multiplyBy014(f12, F[0], Fp2.mul(F[1], Px), Fp2.mul(F[2], Py));
|
||||||
}
|
}
|
||||||
if (i !== 0) f12 = Fp12.square(f12);
|
if (i !== 0) f12 = Fp12.sqr(f12);
|
||||||
}
|
}
|
||||||
return Fp12.conjugate(f12);
|
return Fp12.conjugate(f12);
|
||||||
}
|
}
|
||||||
|
|
||||||
// bls12-381 is a construction of two curves:
|
|
||||||
// 1. Fp: (x, y)
|
|
||||||
// 2. Fp₂: ((x₁, x₂+i), (y₁, y₂+i)) - (complex numbers)
|
|
||||||
//
|
|
||||||
// Bilinear Pairing (ate pairing) is used to combine both elements into a paired one:
|
|
||||||
// Fp₁₂ = e(Fp, Fp2)
|
|
||||||
// where Fp₁₂ = 12-degree polynomial
|
|
||||||
// Pairing is used to verify signatures.
|
|
||||||
//
|
|
||||||
// We are using Fp for private keys (shorter) and Fp2 for signatures (longer).
|
|
||||||
// Some projects may prefer to swap this relation, it is not supported for now.
|
|
||||||
|
|
||||||
const htfDefaults = { ...CURVE.htfDefaults };
|
|
||||||
|
|
||||||
function isWithinCurveOrder(num: bigint): boolean {
|
|
||||||
return 0 < num && num < CURVE.r;
|
|
||||||
}
|
|
||||||
|
|
||||||
const utils = {
|
const utils = {
|
||||||
hexToBytes: hexToBytes,
|
randomPrivateKey: (): Uint8Array => {
|
||||||
bytesToHex: bytesToHex,
|
return Fr.toBytes(hashToPrivateScalar(CURVE.randomBytes(groupLen + 8), CURVE.params.r));
|
||||||
mod: mod.mod,
|
|
||||||
stringToBytes,
|
|
||||||
// TODO: do we need to export it here?
|
|
||||||
hashToField: (msg: Uint8Array, count: number, options: Partial<typeof htfDefaults> = {}) =>
|
|
||||||
hash_to_field(msg, count, { ...CURVE.htfDefaults, ...options }),
|
|
||||||
expandMessageXMD: (msg: Uint8Array, DST: Uint8Array, lenInBytes: number, H = CURVE.hash) =>
|
|
||||||
expand_message_xmd(msg, DST, lenInBytes, H),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Can take 40 or more bytes of uniform input e.g. from CSPRNG or KDF
|
|
||||||
* and convert them into private key, with the modulo bias being negligible.
|
|
||||||
* As per FIPS 186 B.1.1.
|
|
||||||
* https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
|
|
||||||
* @param hash hash output from sha512, or a similar function
|
|
||||||
* @returns valid private key
|
|
||||||
*/
|
|
||||||
hashToPrivateKey: (hash: Hex): Uint8Array => {
|
|
||||||
hash = ensureBytes(hash);
|
|
||||||
if (hash.length < 40 || hash.length > 1024)
|
|
||||||
throw new Error('Expected 40-1024 bytes of private key as per FIPS 186');
|
|
||||||
// hashToPrivateScalar(hash, CURVE.r)
|
|
||||||
// NOTE: doesn't add +/-1
|
|
||||||
const num = mod.mod(bytesToNumberBE(hash), CURVE.r);
|
|
||||||
// This should never happen
|
|
||||||
if (num === 0n || num === 1n) throw new Error('Invalid private key');
|
|
||||||
return numberToBytesBE(num, 32);
|
|
||||||
},
|
|
||||||
|
|
||||||
randomBytes: (bytesLength: number = 32): Uint8Array => CURVE.randomBytes(bytesLength),
|
|
||||||
// NIST SP 800-56A rev 3, section 5.6.1.2.2
|
|
||||||
// https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
|
|
||||||
randomPrivateKey: (): Uint8Array => utils.hashToPrivateKey(utils.randomBytes(40)),
|
|
||||||
getDSTLabel: () => htfDefaults.DST,
|
|
||||||
setDSTLabel(newLabel: string) {
|
|
||||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3.1
|
|
||||||
if (typeof newLabel !== 'string' || newLabel.length > 2048 || newLabel.length === 0) {
|
|
||||||
throw new TypeError('Invalid DST');
|
|
||||||
}
|
|
||||||
htfDefaults.DST = newLabel;
|
|
||||||
},
|
},
|
||||||
|
calcPairingPrecomputes,
|
||||||
};
|
};
|
||||||
|
|
||||||
function normalizePrivKey(key: PrivKey): bigint {
|
|
||||||
let int: bigint;
|
|
||||||
if (key instanceof Uint8Array && key.length === 32) int = bytesToNumberBE(key);
|
|
||||||
else if (typeof key === 'string' && key.length === 64) int = BigInt(`0x${key}`);
|
|
||||||
else if (typeof key === 'number' && key > 0 && Number.isSafeInteger(key)) int = BigInt(key);
|
|
||||||
else if (typeof key === 'bigint' && key > 0n) int = key;
|
|
||||||
else throw new TypeError('Expected valid private key');
|
|
||||||
int = mod.mod(int, CURVE.r);
|
|
||||||
if (!isWithinCurveOrder(int)) throw new Error('Private key must be 0 < key < CURVE.r');
|
|
||||||
return int;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Point on G1 curve: (x, y)
|
// Point on G1 curve: (x, y)
|
||||||
const G1 = weierstrassPoints({
|
const G1_ = weierstrassPoints({ n: Fr.ORDER, ...CURVE.G1 });
|
||||||
n: Fr.ORDER,
|
const G1 = Object.assign(
|
||||||
...CURVE.G1,
|
G1_,
|
||||||
});
|
htf.createHasher(G1_.ProjectivePoint, CURVE.G1.mapToCurve, {
|
||||||
|
...CURVE.htfDefaults,
|
||||||
|
...CURVE.G1.htfDefaults,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
// Sparse multiplication against precomputed coefficients
|
// Sparse multiplication against precomputed coefficients
|
||||||
// TODO: replace with weakmap?
|
// TODO: replace with weakmap?
|
||||||
@@ -250,83 +210,92 @@ export function bls<Fp2, Fp6, Fp12>(
|
|||||||
function pairingPrecomputes(point: G2): [Fp2, Fp2, Fp2][] {
|
function pairingPrecomputes(point: G2): [Fp2, Fp2, Fp2][] {
|
||||||
const p = point as G2 & withPairingPrecomputes;
|
const p = point as G2 & withPairingPrecomputes;
|
||||||
if (p._PPRECOMPUTES) return p._PPRECOMPUTES;
|
if (p._PPRECOMPUTES) return p._PPRECOMPUTES;
|
||||||
p._PPRECOMPUTES = calcPairingPrecomputes(p.x, p.y);
|
p._PPRECOMPUTES = calcPairingPrecomputes(point.toAffine());
|
||||||
return p._PPRECOMPUTES;
|
return p._PPRECOMPUTES;
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearPairingPrecomputes(point: G2) {
|
// TODO: export
|
||||||
const p = point as G2 & withPairingPrecomputes;
|
// function clearPairingPrecomputes(point: G2) {
|
||||||
p._PPRECOMPUTES = undefined;
|
// const p = point as G2 & withPairingPrecomputes;
|
||||||
}
|
// p._PPRECOMPUTES = undefined;
|
||||||
clearPairingPrecomputes;
|
// }
|
||||||
|
|
||||||
function millerLoopG1(Q: G1, P: G2): Fp12 {
|
|
||||||
return millerLoop(pairingPrecomputes(P), [Q.x, Q.y]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Point on G2 curve (complex numbers): (x₁, x₂+i), (y₁, y₂+i)
|
// Point on G2 curve (complex numbers): (x₁, x₂+i), (y₁, y₂+i)
|
||||||
const G2 = weierstrassPoints({
|
const G2_ = weierstrassPoints({ n: Fr.ORDER, ...CURVE.G2 });
|
||||||
n: Fr.ORDER,
|
const G2 = Object.assign(
|
||||||
...CURVE.G2,
|
G2_,
|
||||||
});
|
htf.createHasher(G2_.ProjectivePoint as htf.H2CPointConstructor<Fp2>, CURVE.G2.mapToCurve, {
|
||||||
|
...CURVE.htfDefaults,
|
||||||
|
...CURVE.G2.htfDefaults,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
const { Signature } = CURVE.G2;
|
const { Signature } = CURVE.G2;
|
||||||
|
|
||||||
// Calculates bilinear pairing
|
// Calculates bilinear pairing
|
||||||
function pairing(P: G1, Q: G2, withFinalExponent: boolean = true): Fp12 {
|
function pairing(Q: G1, P: G2, withFinalExponent: boolean = true): Fp12 {
|
||||||
if (P.equals(G1.Point.ZERO) || Q.equals(G2.Point.ZERO))
|
if (Q.equals(G1.ProjectivePoint.ZERO) || P.equals(G2.ProjectivePoint.ZERO))
|
||||||
throw new Error('No pairings at point of Infinity');
|
throw new Error('pairing is not available for ZERO point');
|
||||||
P.assertValidity();
|
|
||||||
Q.assertValidity();
|
Q.assertValidity();
|
||||||
|
P.assertValidity();
|
||||||
// Performance: 9ms for millerLoop and ~14ms for exp.
|
// Performance: 9ms for millerLoop and ~14ms for exp.
|
||||||
const looped = millerLoopG1(P, Q);
|
const Qa = Q.toAffine();
|
||||||
|
const looped = millerLoop(pairingPrecomputes(P), [Qa.x, Qa.y]);
|
||||||
return withFinalExponent ? Fp12.finalExponentiate(looped) : looped;
|
return withFinalExponent ? Fp12.finalExponentiate(looped) : looped;
|
||||||
}
|
}
|
||||||
type G1 = typeof G1.Point.BASE;
|
type G1 = typeof G1.ProjectivePoint.BASE;
|
||||||
type G2 = typeof G2.Point.BASE;
|
type G2 = typeof G2.ProjectivePoint.BASE;
|
||||||
|
|
||||||
type G1Hex = Hex | G1;
|
type G1Hex = Hex | G1;
|
||||||
type G2Hex = Hex | G2;
|
type G2Hex = Hex | G2;
|
||||||
function normP1(point: G1Hex): G1 {
|
function normP1(point: G1Hex): G1 {
|
||||||
return point instanceof G1.Point ? (point as G1) : G1.Point.fromHex(point);
|
return point instanceof G1.ProjectivePoint ? (point as G1) : G1.ProjectivePoint.fromHex(point);
|
||||||
}
|
}
|
||||||
function normP2(point: G2Hex): G2 {
|
function normP2(point: G2Hex): G2 {
|
||||||
return point instanceof G2.Point ? point : Signature.decode(point);
|
return point instanceof G2.ProjectivePoint ? point : Signature.fromHex(point);
|
||||||
}
|
}
|
||||||
function normP2Hash(point: G2Hex): G2 {
|
function normP2Hash(point: G2Hex, htfOpts?: htf.htfBasicOpts): G2 {
|
||||||
return point instanceof G2.Point ? point : G2.Point.hashToCurve(point);
|
return point instanceof G2.ProjectivePoint
|
||||||
|
? point
|
||||||
|
: (G2.hashToCurve(ensureBytes('point', point), htfOpts) as G2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Multiplies generator by private key.
|
// Multiplies generator by private key.
|
||||||
// P = pk x G
|
// P = pk x G
|
||||||
function getPublicKey(privateKey: PrivKey): Uint8Array {
|
function getPublicKey(privateKey: PrivKey): Uint8Array {
|
||||||
return G1.Point.fromPrivateKey(privateKey).toRawBytes(true);
|
return G1.ProjectivePoint.fromPrivateKey(privateKey).toRawBytes(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Executes `hashToCurve` on the message and then multiplies the result by private key.
|
// Executes `hashToCurve` on the message and then multiplies the result by private key.
|
||||||
// S = pk x H(m)
|
// S = pk x H(m)
|
||||||
function sign(message: Hex, privateKey: PrivKey): Uint8Array;
|
function sign(message: Hex, privateKey: PrivKey, htfOpts?: htf.htfBasicOpts): Uint8Array;
|
||||||
function sign(message: G2, privateKey: PrivKey): G2;
|
function sign(message: G2, privateKey: PrivKey, htfOpts?: htf.htfBasicOpts): G2;
|
||||||
function sign(message: G2Hex, privateKey: PrivKey): Uint8Array | G2 {
|
function sign(message: G2Hex, privateKey: PrivKey, htfOpts?: htf.htfBasicOpts): Uint8Array | G2 {
|
||||||
const msgPoint = normP2Hash(message);
|
const msgPoint = normP2Hash(message, htfOpts);
|
||||||
msgPoint.assertValidity();
|
msgPoint.assertValidity();
|
||||||
const sigPoint = msgPoint.multiply(normalizePrivKey(privateKey));
|
const sigPoint = msgPoint.multiply(G1.normPrivateKeyToScalar(privateKey));
|
||||||
if (message instanceof G2.Point) return sigPoint;
|
if (message instanceof G2.ProjectivePoint) return sigPoint;
|
||||||
return Signature.encode(sigPoint);
|
return Signature.toRawBytes(sigPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks if pairing of public key & hash is equal to pairing of generator & signature.
|
// Checks if pairing of public key & hash is equal to pairing of generator & signature.
|
||||||
// e(P, H(m)) == e(G, S)
|
// e(P, H(m)) == e(G, S)
|
||||||
function verify(signature: G2Hex, message: G2Hex, publicKey: G1Hex): boolean {
|
function verify(
|
||||||
|
signature: G2Hex,
|
||||||
|
message: G2Hex,
|
||||||
|
publicKey: G1Hex,
|
||||||
|
htfOpts?: htf.htfBasicOpts
|
||||||
|
): boolean {
|
||||||
const P = normP1(publicKey);
|
const P = normP1(publicKey);
|
||||||
const Hm = normP2Hash(message);
|
const Hm = normP2Hash(message, htfOpts);
|
||||||
const G = G1.Point.BASE;
|
const G = G1.ProjectivePoint.BASE;
|
||||||
const S = normP2(signature);
|
const S = normP2(signature);
|
||||||
// Instead of doing 2 exponentiations, we use property of billinear maps
|
// Instead of doing 2 exponentiations, we use property of billinear maps
|
||||||
// and do one exp after multiplying 2 points.
|
// and do one exp after multiplying 2 points.
|
||||||
const ePHm = pairing(P.negate(), Hm, false);
|
const ePHm = pairing(P.negate(), Hm, false);
|
||||||
const eGS = pairing(G, S, false);
|
const eGS = pairing(G, S, false);
|
||||||
const exp = Fp12.finalExponentiate(Fp12.mul(eGS, ePHm));
|
const exp = Fp12.finalExponentiate(Fp12.mul(eGS, ePHm));
|
||||||
return Fp12.equals(exp, Fp12.ONE);
|
return Fp12.eql(exp, Fp12.ONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds a bunch of public key points together.
|
// Adds a bunch of public key points together.
|
||||||
@@ -335,11 +304,9 @@ export function bls<Fp2, Fp6, Fp12>(
|
|||||||
function aggregatePublicKeys(publicKeys: G1[]): G1;
|
function aggregatePublicKeys(publicKeys: G1[]): G1;
|
||||||
function aggregatePublicKeys(publicKeys: G1Hex[]): Uint8Array | G1 {
|
function aggregatePublicKeys(publicKeys: G1Hex[]): Uint8Array | G1 {
|
||||||
if (!publicKeys.length) throw new Error('Expected non-empty array');
|
if (!publicKeys.length) throw new Error('Expected non-empty array');
|
||||||
const agg = publicKeys
|
const agg = publicKeys.map(normP1).reduce((sum, p) => sum.add(p), G1.ProjectivePoint.ZERO);
|
||||||
.map(normP1)
|
const aggAffine = agg; //.toAffine();
|
||||||
.reduce((sum, p) => sum.add(G1.ProjectivePoint.fromAffine(p)), G1.ProjectivePoint.ZERO);
|
if (publicKeys[0] instanceof G1.ProjectivePoint) {
|
||||||
const aggAffine = agg.toAffine();
|
|
||||||
if (publicKeys[0] instanceof G1.Point) {
|
|
||||||
aggAffine.assertValidity();
|
aggAffine.assertValidity();
|
||||||
return aggAffine;
|
return aggAffine;
|
||||||
}
|
}
|
||||||
@@ -352,25 +319,31 @@ export function bls<Fp2, Fp6, Fp12>(
|
|||||||
function aggregateSignatures(signatures: G2[]): G2;
|
function aggregateSignatures(signatures: G2[]): G2;
|
||||||
function aggregateSignatures(signatures: G2Hex[]): Uint8Array | G2 {
|
function aggregateSignatures(signatures: G2Hex[]): Uint8Array | G2 {
|
||||||
if (!signatures.length) throw new Error('Expected non-empty array');
|
if (!signatures.length) throw new Error('Expected non-empty array');
|
||||||
const agg = signatures
|
const agg = signatures.map(normP2).reduce((sum, s) => sum.add(s), G2.ProjectivePoint.ZERO);
|
||||||
.map(normP2)
|
const aggAffine = agg; //.toAffine();
|
||||||
.reduce((sum, s) => sum.add(G2.ProjectivePoint.fromAffine(s)), G2.ProjectivePoint.ZERO);
|
if (signatures[0] instanceof G2.ProjectivePoint) {
|
||||||
const aggAffine = agg.toAffine();
|
|
||||||
if (signatures[0] instanceof G2.Point) {
|
|
||||||
aggAffine.assertValidity();
|
aggAffine.assertValidity();
|
||||||
return aggAffine;
|
return aggAffine;
|
||||||
}
|
}
|
||||||
return Signature.encode(aggAffine);
|
return Signature.toRawBytes(aggAffine);
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://ethresear.ch/t/fast-verification-of-multiple-bls-signatures/5407
|
// https://ethresear.ch/t/fast-verification-of-multiple-bls-signatures/5407
|
||||||
// e(G, S) = e(G, SUM(n)(Si)) = MUL(n)(e(G, Si))
|
// e(G, S) = e(G, SUM(n)(Si)) = MUL(n)(e(G, Si))
|
||||||
function verifyBatch(signature: G2Hex, messages: G2Hex[], publicKeys: G1Hex[]): boolean {
|
function verifyBatch(
|
||||||
|
signature: G2Hex,
|
||||||
|
messages: G2Hex[],
|
||||||
|
publicKeys: G1Hex[],
|
||||||
|
htfOpts?: htf.htfBasicOpts
|
||||||
|
): boolean {
|
||||||
|
// @ts-ignore
|
||||||
|
// console.log('verifyBatch', bytesToHex(signature as any), messages, publicKeys.map(bytesToHex));
|
||||||
|
|
||||||
if (!messages.length) throw new Error('Expected non-empty messages array');
|
if (!messages.length) throw new Error('Expected non-empty messages array');
|
||||||
if (publicKeys.length !== messages.length)
|
if (publicKeys.length !== messages.length)
|
||||||
throw new Error('Pubkey count should equal msg count');
|
throw new Error('Pubkey count should equal msg count');
|
||||||
const sig = normP2(signature);
|
const sig = normP2(signature);
|
||||||
const nMessages = messages.map(normP2Hash);
|
const nMessages = messages.map((i) => normP2Hash(i, htfOpts));
|
||||||
const nPublicKeys = publicKeys.map(normP1);
|
const nPublicKeys = publicKeys.map(normP1);
|
||||||
try {
|
try {
|
||||||
const paired = [];
|
const paired = [];
|
||||||
@@ -378,42 +351,48 @@ export function bls<Fp2, Fp6, Fp12>(
|
|||||||
const groupPublicKey = nMessages.reduce(
|
const groupPublicKey = nMessages.reduce(
|
||||||
(groupPublicKey, subMessage, i) =>
|
(groupPublicKey, subMessage, i) =>
|
||||||
subMessage === message ? groupPublicKey.add(nPublicKeys[i]) : groupPublicKey,
|
subMessage === message ? groupPublicKey.add(nPublicKeys[i]) : groupPublicKey,
|
||||||
G1.Point.ZERO
|
G1.ProjectivePoint.ZERO
|
||||||
);
|
);
|
||||||
// const msg = message instanceof PointG2 ? message : await PointG2.hashToCurve(message);
|
// const msg = message instanceof PointG2 ? message : await PointG2.hashToCurve(message);
|
||||||
// Possible to batch pairing for same msg with different groupPublicKey here
|
// Possible to batch pairing for same msg with different groupPublicKey here
|
||||||
paired.push(pairing(groupPublicKey, message, false));
|
paired.push(pairing(groupPublicKey, message, false));
|
||||||
}
|
}
|
||||||
paired.push(pairing(G1.Point.BASE.negate(), sig, false));
|
paired.push(pairing(G1.ProjectivePoint.BASE.negate(), sig, false));
|
||||||
const product = paired.reduce((a, b) => Fp12.mul(a, b), Fp12.ONE);
|
const product = paired.reduce((a, b) => Fp12.mul(a, b), Fp12.ONE);
|
||||||
const exp = Fp12.finalExponentiate(product);
|
const exp = Fp12.finalExponentiate(product);
|
||||||
return Fp12.equals(exp, Fp12.ONE);
|
return Fp12.eql(exp, Fp12.ONE);
|
||||||
} catch {
|
} catch {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pre-compute points. Refer to README.
|
G1.ProjectivePoint.BASE._setWindowSize(4);
|
||||||
G1.Point.BASE._setWindowSize(4);
|
|
||||||
return {
|
return {
|
||||||
CURVE,
|
|
||||||
Fr,
|
|
||||||
Fp,
|
|
||||||
Fp2,
|
|
||||||
Fp6,
|
|
||||||
Fp12,
|
|
||||||
G1,
|
|
||||||
G2,
|
|
||||||
Signature,
|
|
||||||
millerLoop,
|
|
||||||
calcPairingPrecomputes,
|
|
||||||
pairing,
|
|
||||||
getPublicKey,
|
getPublicKey,
|
||||||
sign,
|
sign,
|
||||||
verify,
|
verify,
|
||||||
|
verifyBatch,
|
||||||
aggregatePublicKeys,
|
aggregatePublicKeys,
|
||||||
aggregateSignatures,
|
aggregateSignatures,
|
||||||
verifyBatch,
|
millerLoop,
|
||||||
|
pairing,
|
||||||
|
G1,
|
||||||
|
G2,
|
||||||
|
Signature,
|
||||||
|
fields: {
|
||||||
|
Fr,
|
||||||
|
Fp,
|
||||||
|
Fp2,
|
||||||
|
Fp6,
|
||||||
|
Fp12,
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
x: CURVE.params.x,
|
||||||
|
r: CURVE.params.r,
|
||||||
|
G1b: CURVE.G1.b,
|
||||||
|
G2b: CURVE.G2.b,
|
||||||
|
},
|
||||||
utils,
|
utils,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,41 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
// Abelian group utilities
|
// Abelian group utilities
|
||||||
|
import { IField, validateField, nLength } from './modular.js';
|
||||||
|
import { validateObject } from './utils.js';
|
||||||
const _0n = BigInt(0);
|
const _0n = BigInt(0);
|
||||||
const _1n = BigInt(1);
|
const _1n = BigInt(1);
|
||||||
|
|
||||||
|
export type AffinePoint<T> = {
|
||||||
|
x: T;
|
||||||
|
y: T;
|
||||||
|
} & { z?: never; t?: never };
|
||||||
|
|
||||||
export interface Group<T extends Group<T>> {
|
export interface Group<T extends Group<T>> {
|
||||||
double(): T;
|
double(): T;
|
||||||
negate(): T;
|
negate(): T;
|
||||||
add(other: T): T;
|
add(other: T): T;
|
||||||
subtract(other: T): T;
|
subtract(other: T): T;
|
||||||
equals(other: T): boolean;
|
equals(other: T): boolean;
|
||||||
multiply(scalar: number | bigint): T;
|
multiply(scalar: bigint): T;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type GroupConstructor<T> = {
|
export type GroupConstructor<T> = {
|
||||||
BASE: T;
|
BASE: T;
|
||||||
ZERO: T;
|
ZERO: T;
|
||||||
};
|
};
|
||||||
// Not big, but pretty complex and it is easy to break stuff. To avoid too much copy paste
|
export type Mapper<T> = (i: T[]) => T[];
|
||||||
|
|
||||||
|
// Elliptic curve multiplication of Point by scalar. Fragile.
|
||||||
|
// Scalars should always be less than curve order: this should be checked inside of a curve itself.
|
||||||
|
// Creates precomputation tables for fast multiplication:
|
||||||
|
// - private scalar is split by fixed size windows of W bits
|
||||||
|
// - every window point is collected from window's table & added to accumulator
|
||||||
|
// - since windows are different, same point inside tables won't be accessed more than once per calc
|
||||||
|
// - each multiplication is 'Math.ceil(CURVE_ORDER / 𝑊) + 1' point additions (fixed for any scalar)
|
||||||
|
// - +1 window is neccessary for wNAF
|
||||||
|
// - wNAF reduces table size: 2x less memory + 2x faster generation, but 10% slower multiplication
|
||||||
|
// TODO: Research returning 2d JS array of windows, instead of a single window. This would allow
|
||||||
|
// windows to be in different memory locations
|
||||||
export function wNAF<T extends Group<T>>(c: GroupConstructor<T>, bits: number) {
|
export function wNAF<T extends Group<T>>(c: GroupConstructor<T>, bits: number) {
|
||||||
const constTimeNegate = (condition: boolean, item: T): T => {
|
const constTimeNegate = (condition: boolean, item: T): T => {
|
||||||
const neg = item.negate();
|
const neg = item.negate();
|
||||||
@@ -44,8 +63,12 @@ export function wNAF<T extends Group<T>>(c: GroupConstructor<T>, bits: number) {
|
|||||||
/**
|
/**
|
||||||
* Creates a wNAF precomputation window. Used for caching.
|
* Creates a wNAF precomputation window. Used for caching.
|
||||||
* Default window size is set by `utils.precompute()` and is equal to 8.
|
* Default window size is set by `utils.precompute()` and is equal to 8.
|
||||||
* Which means we are caching 65536 points: 256 points for every bit from 0 to 256.
|
* Number of precomputed points depends on the curve size:
|
||||||
* @returns 65K precomputed points, depending on W
|
* 2^(𝑊−1) * (Math.ceil(𝑛 / 𝑊) + 1), where:
|
||||||
|
* - 𝑊 is the window size
|
||||||
|
* - 𝑛 is the bitlength of the curve order.
|
||||||
|
* For a 256-bit curve and window size 8, the number of precomputed points is 128 * 33 = 4224.
|
||||||
|
* @returns precomputed point tables flattened to a single array
|
||||||
*/
|
*/
|
||||||
precomputeWindow(elm: T, W: number): Group<T>[] {
|
precomputeWindow(elm: T, W: number): Group<T>[] {
|
||||||
const { windows, windowSize } = opts(W);
|
const { windows, windowSize } = opts(W);
|
||||||
@@ -66,14 +89,14 @@ export function wNAF<T extends Group<T>>(c: GroupConstructor<T>, bits: number) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements w-ary non-adjacent form for calculating ec multiplication.
|
* Implements ec multiplication using precomputed tables and w-ary non-adjacent form.
|
||||||
* @param W window size
|
* @param W window size
|
||||||
* @param affinePoint optional 2d point to save cached precompute windows on it.
|
* @param precomputes precomputed tables
|
||||||
* @param n bits
|
* @param n scalar (we don't check here, but should be less than curve order)
|
||||||
* @returns real and fake (for const-time) points
|
* @returns real and fake (for const-time) points
|
||||||
*/
|
*/
|
||||||
wNAF(W: number, precomputes: T[], n: bigint): { p: T; f: T } {
|
wNAF(W: number, precomputes: T[], n: bigint): { p: T; f: T } {
|
||||||
// TODO: maybe check that scalar is less than group order? wNAF will fail otherwise
|
// TODO: maybe check that scalar is less than group order? wNAF behavious is undefined otherwise
|
||||||
// But need to carefully remove other checks before wNAF. ORDER == bits here
|
// But need to carefully remove other checks before wNAF. ORDER == bits here
|
||||||
const { windows, windowSize } = opts(W);
|
const { windows, windowSize } = opts(W);
|
||||||
|
|
||||||
@@ -125,5 +148,56 @@ export function wNAF<T extends Group<T>>(c: GroupConstructor<T>, bits: number) {
|
|||||||
// which makes it less const-time: around 1 bigint multiply.
|
// which makes it less const-time: around 1 bigint multiply.
|
||||||
return { p, f };
|
return { p, f };
|
||||||
},
|
},
|
||||||
|
|
||||||
|
wNAFCached(P: T, precomputesMap: Map<T, T[]>, n: bigint, transform: Mapper<T>): { p: T; f: T } {
|
||||||
|
// @ts-ignore
|
||||||
|
const W: number = P._WINDOW_SIZE || 1;
|
||||||
|
// Calculate precomputes on a first run, reuse them after
|
||||||
|
let comp = precomputesMap.get(P);
|
||||||
|
if (!comp) {
|
||||||
|
comp = this.precomputeWindow(P, W) as T[];
|
||||||
|
if (W !== 1) {
|
||||||
|
precomputesMap.set(P, transform(comp));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.wNAF(W, comp, n);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generic BasicCurve interface: works even for polynomial fields (BLS): P, n, h would be ok.
|
||||||
|
// Though generator can be different (Fp2 / Fp6 for BLS).
|
||||||
|
export type BasicCurve<T> = {
|
||||||
|
Fp: IField<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,
|
||||||
|
...{ p: curve.Fp.ORDER },
|
||||||
|
} as const);
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,60 +1,36 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
import { CHash, concatBytes } from './utils.js';
|
import type { Group, GroupConstructor, AffinePoint } from './curve.js';
|
||||||
import * as mod from './modular.js';
|
import { mod, IField } from './modular.js';
|
||||||
|
import { bytesToNumberBE, CHash, concatBytes, utf8ToBytes, validateObject } from './utils.js';
|
||||||
|
|
||||||
export type htfOpts = {
|
/**
|
||||||
// DST: a domain separation tag
|
* * `DST` is a domain separation tag, defined in section 2.2.5
|
||||||
// defined in section 2.2.5
|
* * `p` characteristic of F, where F is a finite field of characteristic p and order q = p^m
|
||||||
DST: string;
|
* * `m` is extension degree (1 for prime fields)
|
||||||
// p: the characteristic of F
|
* * `k` is the target security target in bits (e.g. 128), from section 5.1
|
||||||
// where F is a finite field of characteristic p and order q = p^m
|
* * `expand` is `xmd` (SHA2, SHA3, BLAKE) or `xof` (SHAKE, BLAKE-XOF)
|
||||||
|
* * `hash` conforming to `utils.CHash` interface, with `outputLen` / `blockLen` props
|
||||||
|
*/
|
||||||
|
type UnicodeOrBytes = string | Uint8Array;
|
||||||
|
export type Opts = {
|
||||||
|
DST: UnicodeOrBytes;
|
||||||
p: bigint;
|
p: bigint;
|
||||||
// m: the extension degree of F, m >= 1
|
|
||||||
// where F is a finite field of characteristic p and order q = p^m
|
|
||||||
m: number;
|
m: number;
|
||||||
// k: the target security level for the suite in bits
|
|
||||||
// defined in section 5.1
|
|
||||||
k: number;
|
k: number;
|
||||||
// option to use a message that has already been processed by
|
expand: 'xmd' | 'xof';
|
||||||
// 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;
|
hash: CHash;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function validateHTFOpts(opts: htfOpts) {
|
function validateDST(dst: UnicodeOrBytes): Uint8Array {
|
||||||
if (typeof opts.DST !== 'string') throw new Error('Invalid htf/DST');
|
if (dst instanceof Uint8Array) return dst;
|
||||||
if (typeof opts.p !== 'bigint') throw new Error('Invalid htf/p');
|
if (typeof dst === 'string') return utf8ToBytes(dst);
|
||||||
if (typeof opts.m !== 'number') throw new Error('Invalid htf/m');
|
throw new Error('DST must be Uint8Array or string');
|
||||||
if (typeof opts.k !== 'number') throw new Error('Invalid htf/k');
|
|
||||||
if (opts.expand !== 'xmd' && opts.expand !== 'xof' && opts.expand !== undefined)
|
|
||||||
throw new Error('Invalid htf/expand');
|
|
||||||
if (typeof opts.hash !== 'function' || !Number.isSafeInteger(opts.hash.outputLen))
|
|
||||||
throw new Error('Invalid htf/hash function');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UTF8 to ui8a
|
// Octet Stream to Integer. "spec" implementation of os2ip is 2.5x slower vs bytesToNumberBE.
|
||||||
// TODO: looks broken, ASCII only, why not TextEncoder/TextDecoder? it is in hashes anyway
|
const os2ip = bytesToNumberBE;
|
||||||
export function stringToBytes(str: string) {
|
|
||||||
const bytes = new Uint8Array(str.length);
|
|
||||||
for (let i = 0; i < str.length; i++) bytes[i] = str.charCodeAt(i);
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Octet Stream to Integer (bytesToNumberBE)
|
// Integer to Octet Stream (numberToBytesBE)
|
||||||
function os2ip(bytes: Uint8Array): bigint {
|
|
||||||
let result = 0n;
|
|
||||||
for (let i = 0; i < bytes.length; i++) {
|
|
||||||
result <<= 8n;
|
|
||||||
result += BigInt(bytes[i]);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Integer to Octet Stream
|
|
||||||
function i2osp(value: number, length: number): Uint8Array {
|
function i2osp(value: number, length: number): Uint8Array {
|
||||||
if (value < 0 || value >= 1 << (8 * length)) {
|
if (value < 0 || value >= 1 << (8 * length)) {
|
||||||
throw new Error(`bad I2OSP call: value=${value} length=${length}`);
|
throw new Error(`bad I2OSP call: value=${value} length=${length}`);
|
||||||
@@ -75,6 +51,13 @@ function strxor(a: Uint8Array, b: Uint8Array): Uint8Array {
|
|||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isBytes(item: unknown): void {
|
||||||
|
if (!(item instanceof Uint8Array)) throw new Error('Uint8Array expected');
|
||||||
|
}
|
||||||
|
function isNum(item: unknown): void {
|
||||||
|
if (!Number.isSafeInteger(item)) throw new Error('number expected');
|
||||||
|
}
|
||||||
|
|
||||||
// Produces a uniformly random byte string using a cryptographic hash function H that outputs b bits
|
// Produces a uniformly random byte string using a cryptographic hash function H that outputs b bits
|
||||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.4.1
|
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.4.1
|
||||||
export function expand_message_xmd(
|
export function expand_message_xmd(
|
||||||
@@ -83,15 +66,17 @@ export function expand_message_xmd(
|
|||||||
lenInBytes: number,
|
lenInBytes: number,
|
||||||
H: CHash
|
H: CHash
|
||||||
): Uint8Array {
|
): Uint8Array {
|
||||||
|
isBytes(msg);
|
||||||
|
isBytes(DST);
|
||||||
|
isNum(lenInBytes);
|
||||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-5.3.3
|
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-5.3.3
|
||||||
if (DST.length > 255) DST = H(concatBytes(stringToBytes('H2C-OVERSIZE-DST-'), DST));
|
if (DST.length > 255) DST = H(concatBytes(utf8ToBytes('H2C-OVERSIZE-DST-'), DST));
|
||||||
const b_in_bytes = H.outputLen;
|
const { outputLen: b_in_bytes, blockLen: r_in_bytes } = H;
|
||||||
const r_in_bytes = H.blockLen;
|
|
||||||
const ell = Math.ceil(lenInBytes / b_in_bytes);
|
const ell = Math.ceil(lenInBytes / b_in_bytes);
|
||||||
if (ell > 255) throw new Error('Invalid xmd length');
|
if (ell > 255) throw new Error('Invalid xmd length');
|
||||||
const DST_prime = concatBytes(DST, i2osp(DST.length, 1));
|
const DST_prime = concatBytes(DST, i2osp(DST.length, 1));
|
||||||
const Z_pad = i2osp(0, r_in_bytes);
|
const Z_pad = i2osp(0, r_in_bytes);
|
||||||
const l_i_b_str = i2osp(lenInBytes, 2);
|
const l_i_b_str = i2osp(lenInBytes, 2); // len_in_bytes_str
|
||||||
const b = new Array<Uint8Array>(ell);
|
const b = new Array<Uint8Array>(ell);
|
||||||
const b_0 = H(concatBytes(Z_pad, msg, l_i_b_str, i2osp(0, 1), DST_prime));
|
const b_0 = H(concatBytes(Z_pad, msg, l_i_b_str, i2osp(0, 1), DST_prime));
|
||||||
b[0] = H(concatBytes(b_0, i2osp(1, 1), DST_prime));
|
b[0] = H(concatBytes(b_0, i2osp(1, 1), DST_prime));
|
||||||
@@ -110,11 +95,14 @@ export function expand_message_xof(
|
|||||||
k: number,
|
k: number,
|
||||||
H: CHash
|
H: CHash
|
||||||
): Uint8Array {
|
): Uint8Array {
|
||||||
|
isBytes(msg);
|
||||||
|
isBytes(DST);
|
||||||
|
isNum(lenInBytes);
|
||||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-5.3.3
|
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-5.3.3
|
||||||
// DST = H('H2C-OVERSIZE-DST-' || a_very_long_DST, Math.ceil((lenInBytes * k) / 8));
|
// DST = H('H2C-OVERSIZE-DST-' || a_very_long_DST, Math.ceil((lenInBytes * k) / 8));
|
||||||
if (DST.length > 255) {
|
if (DST.length > 255) {
|
||||||
const dkLen = Math.ceil((2 * k) / 8);
|
const dkLen = Math.ceil((2 * k) / 8);
|
||||||
DST = H.create({ dkLen }).update(stringToBytes('H2C-OVERSIZE-DST-')).update(DST).digest();
|
DST = H.create({ dkLen }).update(utf8ToBytes('H2C-OVERSIZE-DST-')).update(DST).digest();
|
||||||
}
|
}
|
||||||
if (lenInBytes > 65535 || DST.length > 255)
|
if (lenInBytes > 65535 || DST.length > 255)
|
||||||
throw new Error('expand_message_xof: invalid lenInBytes');
|
throw new Error('expand_message_xof: invalid lenInBytes');
|
||||||
@@ -134,36 +122,49 @@ export function expand_message_xof(
|
|||||||
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3
|
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3
|
||||||
* @param msg a byte string containing the message to hash
|
* @param msg a byte string containing the message to hash
|
||||||
* @param count the number of elements of F to output
|
* @param count the number of elements of F to output
|
||||||
* @param options `{DST: string, p: bigint, m: number, k: number, expand: 'xmd' | 'xof', hash: H}`
|
* @param options `{DST: string, p: bigint, m: number, k: number, expand: 'xmd' | 'xof', hash: H}`, see above
|
||||||
* @returns [u_0, ..., u_(count - 1)], a list of field elements.
|
* @returns [u_0, ..., u_(count - 1)], a list of field elements.
|
||||||
*/
|
*/
|
||||||
export function hash_to_field(msg: Uint8Array, count: number, options: htfOpts): bigint[][] {
|
export function hash_to_field(msg: Uint8Array, count: number, options: Opts): bigint[][] {
|
||||||
// if options is provided but incomplete, fill any missing fields with the
|
validateObject(options, {
|
||||||
// value in hftDefaults (ie hash to G2).
|
DST: 'string',
|
||||||
const log2p = options.p.toString(2).length;
|
p: 'bigint',
|
||||||
const L = Math.ceil((log2p + options.k) / 8); // section 5.1 of ietf draft link above
|
m: 'isSafeInteger',
|
||||||
const len_in_bytes = count * options.m * L;
|
k: 'isSafeInteger',
|
||||||
const DST = stringToBytes(options.DST);
|
hash: 'hash',
|
||||||
let pseudo_random_bytes = msg;
|
});
|
||||||
if (options.expand === 'xmd') {
|
const { p, k, m, hash, expand, DST: _DST } = options;
|
||||||
pseudo_random_bytes = expand_message_xmd(msg, DST, len_in_bytes, options.hash);
|
isBytes(msg);
|
||||||
} else if (options.expand === 'xof') {
|
isNum(count);
|
||||||
pseudo_random_bytes = expand_message_xof(msg, DST, len_in_bytes, options.k, options.hash);
|
const DST = validateDST(_DST);
|
||||||
|
const log2p = p.toString(2).length;
|
||||||
|
const L = Math.ceil((log2p + k) / 8); // section 5.1 of ietf draft link above
|
||||||
|
const len_in_bytes = count * m * L;
|
||||||
|
let prb; // pseudo_random_bytes
|
||||||
|
if (expand === 'xmd') {
|
||||||
|
prb = expand_message_xmd(msg, DST, len_in_bytes, hash);
|
||||||
|
} else if (expand === 'xof') {
|
||||||
|
prb = expand_message_xof(msg, DST, len_in_bytes, k, hash);
|
||||||
|
} else if (expand === '_internal_pass') {
|
||||||
|
// for internal tests only
|
||||||
|
prb = msg;
|
||||||
|
} else {
|
||||||
|
throw new Error('expand must be "xmd" or "xof"');
|
||||||
}
|
}
|
||||||
const u = new Array(count);
|
const u = new Array(count);
|
||||||
for (let i = 0; i < count; i++) {
|
for (let i = 0; i < count; i++) {
|
||||||
const e = new Array(options.m);
|
const e = new Array(m);
|
||||||
for (let j = 0; j < options.m; j++) {
|
for (let j = 0; j < m; j++) {
|
||||||
const elm_offset = L * (j + i * options.m);
|
const elm_offset = L * (j + i * m);
|
||||||
const tv = pseudo_random_bytes.subarray(elm_offset, elm_offset + L);
|
const tv = prb.subarray(elm_offset, elm_offset + L);
|
||||||
e[j] = mod.mod(os2ip(tv), options.p);
|
e[j] = mod(os2ip(tv), p);
|
||||||
}
|
}
|
||||||
u[i] = e;
|
u[i] = e;
|
||||||
}
|
}
|
||||||
return u;
|
return u;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isogenyMap<T, F extends mod.Field<T>>(field: F, map: [T[], T[], T[], T[]]) {
|
export function isogenyMap<T, F extends IField<T>>(field: F, map: [T[], T[], T[], T[]]) {
|
||||||
// Make same order as in spec
|
// Make same order as in spec
|
||||||
const COEFF = map.map((i) => Array.from(i).reverse());
|
const COEFF = map.map((i) => Array.from(i).reverse());
|
||||||
return (x: T, y: T) => {
|
return (x: T, y: T) => {
|
||||||
@@ -175,3 +176,48 @@ export function isogenyMap<T, F extends mod.Field<T>>(field: F, map: [T[], T[],
|
|||||||
return { x, y };
|
return { x, y };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface H2CPoint<T> extends Group<H2CPoint<T>> {
|
||||||
|
add(rhs: H2CPoint<T>): H2CPoint<T>;
|
||||||
|
toAffine(iz?: bigint): AffinePoint<T>;
|
||||||
|
clearCofactor(): H2CPoint<T>;
|
||||||
|
assertValidity(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface H2CPointConstructor<T> extends GroupConstructor<H2CPoint<T>> {
|
||||||
|
fromAffine(ap: AffinePoint<T>): H2CPoint<T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MapToCurve<T> = (scalar: bigint[]) => AffinePoint<T>;
|
||||||
|
|
||||||
|
// Separated from initialization opts, so users won't accidentally change per-curve parameters
|
||||||
|
// (changing DST is ok!)
|
||||||
|
export type htfBasicOpts = { DST: UnicodeOrBytes };
|
||||||
|
|
||||||
|
export function createHasher<T>(
|
||||||
|
Point: H2CPointConstructor<T>,
|
||||||
|
mapToCurve: MapToCurve<T>,
|
||||||
|
def: Opts & { encodeDST?: UnicodeOrBytes }
|
||||||
|
) {
|
||||||
|
if (typeof mapToCurve !== 'function') throw new Error('mapToCurve() must be defined');
|
||||||
|
return {
|
||||||
|
// Encodes byte string to elliptic curve
|
||||||
|
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-3
|
||||||
|
hashToCurve(msg: Uint8Array, options?: htfBasicOpts) {
|
||||||
|
const u = hash_to_field(msg, 2, { ...def, DST: def.DST, ...options } as Opts);
|
||||||
|
const u0 = Point.fromAffine(mapToCurve(u[0]));
|
||||||
|
const u1 = Point.fromAffine(mapToCurve(u[1]));
|
||||||
|
const P = u0.add(u1).clearCofactor();
|
||||||
|
P.assertValidity();
|
||||||
|
return P;
|
||||||
|
},
|
||||||
|
|
||||||
|
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-3
|
||||||
|
encodeToCurve(msg: Uint8Array, options?: htfBasicOpts) {
|
||||||
|
const u = hash_to_field(msg, 1, { ...def, DST: def.encodeDST, ...options } as Opts);
|
||||||
|
const P = Point.fromAffine(mapToCurve(u[0])).clearCofactor();
|
||||||
|
P.assertValidity();
|
||||||
|
return P;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
// TODO: remove circular imports
|
|
||||||
import * as utils from './utils.js';
|
|
||||||
// Utilities for modular arithmetics and finite fields
|
// Utilities for modular arithmetics and finite fields
|
||||||
|
import {
|
||||||
|
bitMask,
|
||||||
|
numberToBytesBE,
|
||||||
|
numberToBytesLE,
|
||||||
|
bytesToNumberBE,
|
||||||
|
bytesToNumberLE,
|
||||||
|
ensureBytes,
|
||||||
|
validateObject,
|
||||||
|
} from './utils.js';
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3);
|
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3);
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
@@ -34,7 +41,6 @@ export function pow(num: bigint, power: bigint, modulo: bigint): bigint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Does x ^ (2 ^ power) mod p. pow2(30, 4) == 30 ^ (2 ^ 4)
|
// Does x ^ (2 ^ power) mod p. pow2(30, 4) == 30 ^ (2 ^ 4)
|
||||||
// TODO: Fp version?
|
|
||||||
export function pow2(x: bigint, power: bigint, modulo: bigint): bigint {
|
export function pow2(x: bigint, power: bigint, modulo: bigint): bigint {
|
||||||
let res = x;
|
let res = x;
|
||||||
while (power-- > _0n) {
|
while (power-- > _0n) {
|
||||||
@@ -50,11 +56,13 @@ export function invert(number: bigint, modulo: bigint): bigint {
|
|||||||
throw new Error(`invert: expected positive integers, got n=${number} mod=${modulo}`);
|
throw new Error(`invert: expected positive integers, got n=${number} mod=${modulo}`);
|
||||||
}
|
}
|
||||||
// Eucledian GCD https://brilliant.org/wiki/extended-euclidean-algorithm/
|
// Eucledian GCD https://brilliant.org/wiki/extended-euclidean-algorithm/
|
||||||
|
// Fermat's little theorem "CT-like" version inv(n) = n^(m-2) mod m is 30x slower.
|
||||||
let a = mod(number, modulo);
|
let a = mod(number, modulo);
|
||||||
let b = modulo;
|
let b = modulo;
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
let x = _0n, y = _1n, u = _1n, v = _0n;
|
let x = _0n, y = _1n, u = _1n, v = _0n;
|
||||||
while (a !== _0n) {
|
while (a !== _0n) {
|
||||||
|
// JIT applies optimization if those two lines follow each other
|
||||||
const q = b / a;
|
const q = b / a;
|
||||||
const r = b % a;
|
const r = b % a;
|
||||||
const m = x - u * q;
|
const m = x - u * q;
|
||||||
@@ -68,7 +76,8 @@ export function invert(number: bigint, modulo: bigint): bigint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Tonelli-Shanks algorithm
|
// Tonelli-Shanks algorithm
|
||||||
// https://eprint.iacr.org/2012/685.pdf (page 12)
|
// Paper 1: https://eprint.iacr.org/2012/685.pdf (page 12)
|
||||||
|
// Paper 2: Square Roots from 1; 24, 51, 10 to Dan Shanks
|
||||||
export function tonelliShanks(P: bigint) {
|
export function tonelliShanks(P: bigint) {
|
||||||
// Legendre constant: used to calculate Legendre symbol (a | p),
|
// Legendre constant: used to calculate Legendre symbol (a | p),
|
||||||
// which denotes the value of a^((p-1)/2) (mod p).
|
// which denotes the value of a^((p-1)/2) (mod p).
|
||||||
@@ -79,7 +88,7 @@ export function tonelliShanks(P: bigint) {
|
|||||||
|
|
||||||
let Q: bigint, S: number, Z: bigint;
|
let Q: bigint, S: number, Z: bigint;
|
||||||
// Step 1: By factoring out powers of 2 from p - 1,
|
// Step 1: By factoring out powers of 2 from p - 1,
|
||||||
// find q and s such that p - 1 = q2s with q odd
|
// find q and s such that p - 1 = q*(2^s) with q odd
|
||||||
for (Q = P - _1n, S = 0; Q % _2n === _0n; Q /= _2n, S++);
|
for (Q = P - _1n, S = 0; Q % _2n === _0n; Q /= _2n, S++);
|
||||||
|
|
||||||
// Step 2: Select a non-square z such that (z | p) ≡ -1 and set c ≡ zq
|
// Step 2: Select a non-square z such that (z | p) ≡ -1 and set c ≡ zq
|
||||||
@@ -88,41 +97,40 @@ export function tonelliShanks(P: bigint) {
|
|||||||
// Fast-path
|
// Fast-path
|
||||||
if (S === 1) {
|
if (S === 1) {
|
||||||
const p1div4 = (P + _1n) / _4n;
|
const p1div4 = (P + _1n) / _4n;
|
||||||
return function tonelliFast<T>(Fp: Field<T>, n: T) {
|
return function tonelliFast<T>(Fp: IField<T>, n: T) {
|
||||||
const root = Fp.pow(n, p1div4);
|
const root = Fp.pow(n, p1div4);
|
||||||
if (!Fp.equals(Fp.square(root), n)) throw new Error('Cannot find square root');
|
if (!Fp.eql(Fp.sqr(root), n)) throw new Error('Cannot find square root');
|
||||||
return root;
|
return root;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Slow-path
|
// Slow-path
|
||||||
const Q1div2 = (Q + _1n) / _2n;
|
const Q1div2 = (Q + _1n) / _2n;
|
||||||
return function tonelliSlow<T>(Fp: Field<T>, n: T): T {
|
return function tonelliSlow<T>(Fp: IField<T>, n: T): T {
|
||||||
// Step 0: Check that n is indeed a square: (n | p) must be ≡ 1
|
// Step 0: Check that n is indeed a square: (n | p) should not be ≡ -1
|
||||||
if (Fp.pow(n, legendreC) !== Fp.ONE) throw new Error('Cannot find square root');
|
if (Fp.pow(n, legendreC) === Fp.neg(Fp.ONE)) throw new Error('Cannot find square root');
|
||||||
let s = S;
|
let r = S;
|
||||||
|
// TODO: will fail at Fp2/etc
|
||||||
|
let g = Fp.pow(Fp.mul(Fp.ONE, Z), Q); // will update both x and b
|
||||||
|
let x = Fp.pow(n, Q1div2); // first guess at the square root
|
||||||
|
let b = Fp.pow(n, Q); // first guess at the fudge factor
|
||||||
|
|
||||||
let c = pow(Z, Q, P);
|
while (!Fp.eql(b, Fp.ONE)) {
|
||||||
let r = Fp.pow(n, Q1div2);
|
if (Fp.eql(b, Fp.ZERO)) return Fp.ZERO; // https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm (4. If t = 0, return r = 0)
|
||||||
let t = Fp.pow(n, Q);
|
// Find m such b^(2^m)==1
|
||||||
|
let m = 1;
|
||||||
let t2 = Fp.ZERO;
|
for (let t2 = Fp.sqr(b); m < r; m++) {
|
||||||
while (!Fp.equals(Fp.sub(t, Fp.ONE), Fp.ZERO)) {
|
if (Fp.eql(t2, Fp.ONE)) break;
|
||||||
t2 = Fp.square(t);
|
t2 = Fp.sqr(t2); // t2 *= t2
|
||||||
let i;
|
|
||||||
for (i = 1; i < s; i++) {
|
|
||||||
// stop if t2-1 == 0
|
|
||||||
if (Fp.equals(Fp.sub(t2, Fp.ONE), Fp.ZERO)) break;
|
|
||||||
// t2 *= t2
|
|
||||||
t2 = Fp.square(t2);
|
|
||||||
}
|
}
|
||||||
let b = pow(c, BigInt(1 << (s - i - 1)), P);
|
// NOTE: r-m-1 can be bigger than 32, need to convert to bigint before shift, otherwise there will be overflow
|
||||||
r = Fp.mul(r, b);
|
const ge = Fp.pow(g, _1n << BigInt(r - m - 1)); // ge = 2^(r-m-1)
|
||||||
c = mod(b * b, P);
|
g = Fp.sqr(ge); // g = ge * ge
|
||||||
t = Fp.mul(t, c);
|
x = Fp.mul(x, ge); // x *= ge
|
||||||
s = i;
|
b = Fp.mul(b, g); // b *= g
|
||||||
|
r = m;
|
||||||
}
|
}
|
||||||
return r;
|
return x;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,10 +146,10 @@ export function FpSqrt(P: bigint) {
|
|||||||
// 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaabn;
|
// 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaabn;
|
||||||
// const NUM = 72057594037927816n;
|
// const NUM = 72057594037927816n;
|
||||||
const p1div4 = (P + _1n) / _4n;
|
const p1div4 = (P + _1n) / _4n;
|
||||||
return function sqrt3mod4<T>(Fp: Field<T>, n: T) {
|
return function sqrt3mod4<T>(Fp: IField<T>, n: T) {
|
||||||
const root = Fp.pow(n, p1div4);
|
const root = Fp.pow(n, p1div4);
|
||||||
// Throw if root**2 != n
|
// Throw if root**2 != n
|
||||||
if (!Fp.equals(Fp.square(root), n)) throw new Error('Cannot find square root');
|
if (!Fp.eql(Fp.sqr(root), n)) throw new Error('Cannot find square root');
|
||||||
return root;
|
return root;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -149,13 +157,13 @@ export function FpSqrt(P: bigint) {
|
|||||||
// Atkin algorithm for q ≡ 5 (mod 8), https://eprint.iacr.org/2012/685.pdf (page 10)
|
// Atkin algorithm for q ≡ 5 (mod 8), https://eprint.iacr.org/2012/685.pdf (page 10)
|
||||||
if (P % _8n === _5n) {
|
if (P % _8n === _5n) {
|
||||||
const c1 = (P - _5n) / _8n;
|
const c1 = (P - _5n) / _8n;
|
||||||
return function sqrt5mod8<T>(Fp: Field<T>, n: T) {
|
return function sqrt5mod8<T>(Fp: IField<T>, n: T) {
|
||||||
const n2 = Fp.mul(n, _2n);
|
const n2 = Fp.mul(n, _2n);
|
||||||
const v = Fp.pow(n2, c1);
|
const v = Fp.pow(n2, c1);
|
||||||
const nv = Fp.mul(n, v);
|
const nv = Fp.mul(n, v);
|
||||||
const i = Fp.mul(Fp.mul(nv, _2n), v);
|
const i = Fp.mul(Fp.mul(nv, _2n), v);
|
||||||
const root = Fp.mul(nv, Fp.sub(i, Fp.ONE));
|
const root = Fp.mul(nv, Fp.sub(i, Fp.ONE));
|
||||||
if (!Fp.equals(Fp.square(root), n)) throw new Error('Cannot find square root');
|
if (!Fp.eql(Fp.sqr(root), n)) throw new Error('Cannot find square root');
|
||||||
return root;
|
return root;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -195,7 +203,7 @@ export const isNegativeLE = (num: bigint, modulo: bigint) => (mod(num, modulo) &
|
|||||||
// - unreadable mess: addition, multiply, square, squareRoot, inversion, divide, power, equals, subtract
|
// - unreadable mess: addition, multiply, square, squareRoot, inversion, divide, power, equals, subtract
|
||||||
|
|
||||||
// Field is not always over prime, Fp2 for example has ORDER(q)=p^m
|
// Field is not always over prime, Fp2 for example has ORDER(q)=p^m
|
||||||
export interface Field<T> {
|
export interface IField<T> {
|
||||||
ORDER: bigint;
|
ORDER: bigint;
|
||||||
BYTES: number;
|
BYTES: number;
|
||||||
BITS: number;
|
BITS: number;
|
||||||
@@ -205,13 +213,13 @@ export interface Field<T> {
|
|||||||
// 1-arg
|
// 1-arg
|
||||||
create: (num: T) => T;
|
create: (num: T) => T;
|
||||||
isValid: (num: T) => boolean;
|
isValid: (num: T) => boolean;
|
||||||
isZero: (num: T) => boolean;
|
is0: (num: T) => boolean;
|
||||||
negate(num: T): T;
|
neg(num: T): T;
|
||||||
invert(num: T): T;
|
inv(num: T): T;
|
||||||
sqrt(num: T): T;
|
sqrt(num: T): T;
|
||||||
square(num: T): T;
|
sqr(num: T): T;
|
||||||
// 2-args
|
// 2-args
|
||||||
equals(lhs: T, rhs: T): boolean;
|
eql(lhs: T, rhs: T): boolean;
|
||||||
add(lhs: T, rhs: T): T;
|
add(lhs: T, rhs: T): T;
|
||||||
sub(lhs: T, rhs: T): T;
|
sub(lhs: T, rhs: T): T;
|
||||||
mul(lhs: T, rhs: T | bigint): T;
|
mul(lhs: T, rhs: T | bigint): T;
|
||||||
@@ -221,13 +229,13 @@ export interface Field<T> {
|
|||||||
addN(lhs: T, rhs: T): T;
|
addN(lhs: T, rhs: T): T;
|
||||||
subN(lhs: T, rhs: T): T;
|
subN(lhs: T, rhs: T): T;
|
||||||
mulN(lhs: T, rhs: T | bigint): T;
|
mulN(lhs: T, rhs: T | bigint): T;
|
||||||
squareN(num: T): T;
|
sqrN(num: T): T;
|
||||||
|
|
||||||
// Optional
|
// Optional
|
||||||
// Should be same as sgn0 function in https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/
|
// Should be same as sgn0 function in https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/
|
||||||
// NOTE: sgn0 is 'negative in LE', which is same as odd. And negative in LE is kinda strange definition anyway.
|
// NOTE: sgn0 is 'negative in LE', which is same as odd. And negative in LE is kinda strange definition anyway.
|
||||||
isOdd?(num: T): boolean; // Odd instead of even since we have it for Fp2
|
isOdd?(num: T): boolean; // Odd instead of even since we have it for Fp2
|
||||||
legendre?(num: T): T;
|
// legendre?(num: T): T;
|
||||||
pow(lhs: T, power: bigint): T;
|
pow(lhs: T, power: bigint): T;
|
||||||
invertBatch: (lst: T[]) => T[];
|
invertBatch: (lst: T[]) => T[];
|
||||||
toBytes(num: T): Uint8Array;
|
toBytes(num: T): Uint8Array;
|
||||||
@@ -237,27 +245,26 @@ export interface Field<T> {
|
|||||||
}
|
}
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
const FIELD_FIELDS = [
|
const FIELD_FIELDS = [
|
||||||
'create', 'isValid', 'isZero', 'negate', 'invert', 'sqrt', 'square',
|
'create', 'isValid', 'is0', 'neg', 'inv', 'sqrt', 'sqr',
|
||||||
'equals', 'add', 'sub', 'mul', 'pow', 'div',
|
'eql', 'add', 'sub', 'mul', 'pow', 'div',
|
||||||
'addN', 'subN', 'mulN', 'squareN'
|
'addN', 'subN', 'mulN', 'sqrN'
|
||||||
] as const;
|
] as const;
|
||||||
export function validateField<T>(field: Field<T>) {
|
export function validateField<T>(field: IField<T>) {
|
||||||
for (const i of ['ORDER', 'MASK'] as const) {
|
const initial = {
|
||||||
if (typeof field[i] !== 'bigint')
|
ORDER: 'bigint',
|
||||||
throw new Error(`Invalid field param ${i}=${field[i]} (${typeof field[i]})`);
|
MASK: 'bigint',
|
||||||
}
|
BYTES: 'isSafeInteger',
|
||||||
for (const i of ['BYTES', 'BITS'] as const) {
|
BITS: 'isSafeInteger',
|
||||||
if (typeof field[i] !== 'number')
|
} as Record<string, string>;
|
||||||
throw new Error(`Invalid field param ${i}=${field[i]} (${typeof field[i]})`);
|
const opts = FIELD_FIELDS.reduce((map, val: string) => {
|
||||||
}
|
map[val] = 'function';
|
||||||
for (const i of FIELD_FIELDS) {
|
return map;
|
||||||
if (typeof field[i] !== 'function')
|
}, initial);
|
||||||
throw new Error(`Invalid field param ${i}=${field[i]} (${typeof field[i]})`);
|
return validateObject(field, opts);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generic field functions
|
// Generic field functions
|
||||||
export function FpPow<T>(f: Field<T>, num: T, power: bigint): T {
|
export function FpPow<T>(f: IField<T>, num: T, power: bigint): T {
|
||||||
// Should have same speed as pow for bigints
|
// Should have same speed as pow for bigints
|
||||||
// TODO: benchmark!
|
// TODO: benchmark!
|
||||||
if (power < _0n) throw new Error('Expected power > 0');
|
if (power < _0n) throw new Error('Expected power > 0');
|
||||||
@@ -267,78 +274,95 @@ export function FpPow<T>(f: Field<T>, num: T, power: bigint): T {
|
|||||||
let d = num;
|
let d = num;
|
||||||
while (power > _0n) {
|
while (power > _0n) {
|
||||||
if (power & _1n) p = f.mul(p, d);
|
if (power & _1n) p = f.mul(p, d);
|
||||||
d = f.square(d);
|
d = f.sqr(d);
|
||||||
power >>= 1n;
|
power >>= _1n;
|
||||||
}
|
}
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FpInvertBatch<T>(f: Field<T>, nums: T[]): T[] {
|
// 0 is non-invertible: non-batched version will throw on 0
|
||||||
|
export function FpInvertBatch<T>(f: IField<T>, nums: T[]): T[] {
|
||||||
const tmp = new Array(nums.length);
|
const tmp = new Array(nums.length);
|
||||||
// Walk from first to last, multiply them by each other MOD p
|
// Walk from first to last, multiply them by each other MOD p
|
||||||
const lastMultiplied = nums.reduce((acc, num, i) => {
|
const lastMultiplied = nums.reduce((acc, num, i) => {
|
||||||
if (f.isZero(num)) return acc;
|
if (f.is0(num)) return acc;
|
||||||
tmp[i] = acc;
|
tmp[i] = acc;
|
||||||
return f.mul(acc, num);
|
return f.mul(acc, num);
|
||||||
}, f.ONE);
|
}, f.ONE);
|
||||||
// Invert last element
|
// Invert last element
|
||||||
const inverted = f.invert(lastMultiplied);
|
const inverted = f.inv(lastMultiplied);
|
||||||
// Walk from last to first, multiply them by inverted each other MOD p
|
// Walk from last to first, multiply them by inverted each other MOD p
|
||||||
nums.reduceRight((acc, num, i) => {
|
nums.reduceRight((acc, num, i) => {
|
||||||
if (f.isZero(num)) return acc;
|
if (f.is0(num)) return acc;
|
||||||
tmp[i] = f.mul(acc, tmp[i]);
|
tmp[i] = f.mul(acc, tmp[i]);
|
||||||
return f.mul(acc, num);
|
return f.mul(acc, num);
|
||||||
}, inverted);
|
}, inverted);
|
||||||
return tmp;
|
return tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FpDiv<T>(f: Field<T>, lhs: T, rhs: T | bigint): T {
|
export function FpDiv<T>(f: IField<T>, lhs: T, rhs: T | bigint): T {
|
||||||
return f.mul(lhs, typeof rhs === 'bigint' ? invert(rhs, f.ORDER) : f.invert(rhs));
|
return f.mul(lhs, typeof rhs === 'bigint' ? invert(rhs, f.ORDER) : f.inv(rhs));
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function returns True whenever the value x is a square in the field F.
|
// This function returns True whenever the value x is a square in the field F.
|
||||||
export function FpIsSquare<T>(f: Field<T>) {
|
export function FpIsSquare<T>(f: IField<T>) {
|
||||||
const legendreConst = (f.ORDER - _1n) / _2n; // Integer arithmetic
|
const legendreConst = (f.ORDER - _1n) / _2n; // Integer arithmetic
|
||||||
return (x: T): boolean => {
|
return (x: T): boolean => {
|
||||||
const p = f.pow(x, legendreConst);
|
const p = f.pow(x, legendreConst);
|
||||||
return f.equals(p, f.ZERO) || f.equals(p, f.ONE);
|
return f.eql(p, f.ZERO) || f.eql(p, f.ONE);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: very fragile, always bench. Major performance points:
|
// CURVE.n lengths
|
||||||
// - NonNormalized ops
|
export function nLength(n: bigint, nBitLength?: number) {
|
||||||
// - Object.freeze
|
// Bit size, byte size of CURVE.n
|
||||||
// - same shape of object (don't add/remove keys)
|
const _nBitLength = nBitLength !== undefined ? nBitLength : n.toString(2).length;
|
||||||
type FpField = Field<bigint> & Required<Pick<Field<bigint>, 'isOdd'>>;
|
const nByteLength = Math.ceil(_nBitLength / 8);
|
||||||
export function Fp(
|
return { nBitLength: _nBitLength, nByteLength };
|
||||||
|
}
|
||||||
|
|
||||||
|
type FpField = IField<bigint> & Required<Pick<IField<bigint>, 'isOdd'>>;
|
||||||
|
/**
|
||||||
|
* Initializes a galois field over prime. Non-primes are not supported for now.
|
||||||
|
* Do not init in loop: slow. Very fragile: always run a benchmark on change.
|
||||||
|
* Major performance gains:
|
||||||
|
* a) non-normalized operations like mulN instead of mul
|
||||||
|
* b) `Object.freeze`
|
||||||
|
* c) Same object shape: never add or remove keys
|
||||||
|
* @param ORDER prime positive bigint
|
||||||
|
* @param bitLen how many bits the field consumes
|
||||||
|
* @param isLE (def: false) if encoding / decoding should be in little-endian
|
||||||
|
* @param redef optional faster redefinitions of sqrt and other methods
|
||||||
|
*/
|
||||||
|
export function Field(
|
||||||
ORDER: bigint,
|
ORDER: bigint,
|
||||||
bitLen?: number,
|
bitLen?: number,
|
||||||
isLE = false,
|
isLE = false,
|
||||||
redef: Partial<Field<bigint>> = {}
|
redef: Partial<IField<bigint>> = {}
|
||||||
): Readonly<FpField> {
|
): Readonly<FpField> {
|
||||||
if (ORDER <= _0n) throw new Error(`Expected Fp ORDER > 0, got ${ORDER}`);
|
if (ORDER <= _0n) throw new Error(`Expected Fp ORDER > 0, got ${ORDER}`);
|
||||||
const { nBitLength: BITS, nByteLength: BYTES } = utils.nLength(ORDER, bitLen);
|
const { nBitLength: BITS, nByteLength: BYTES } = nLength(ORDER, bitLen);
|
||||||
if (BYTES > 2048) throw new Error('Field lengths over 2048 bytes are not supported');
|
if (BYTES > 2048) throw new Error('Field lengths over 2048 bytes are not supported');
|
||||||
const sqrtP = FpSqrt(ORDER);
|
const sqrtP = FpSqrt(ORDER);
|
||||||
const f: Readonly<FpField> = Object.freeze({
|
const f: Readonly<FpField> = Object.freeze({
|
||||||
ORDER,
|
ORDER,
|
||||||
BITS,
|
BITS,
|
||||||
BYTES,
|
BYTES,
|
||||||
MASK: utils.bitMask(BITS),
|
MASK: bitMask(BITS),
|
||||||
ZERO: _0n,
|
ZERO: _0n,
|
||||||
ONE: _1n,
|
ONE: _1n,
|
||||||
create: (num) => mod(num, ORDER),
|
create: (num) => mod(num, ORDER),
|
||||||
isValid: (num) => {
|
isValid: (num) => {
|
||||||
if (typeof num !== 'bigint')
|
if (typeof num !== 'bigint')
|
||||||
throw new Error(`Invalid field element: expected bigint, got ${typeof num}`);
|
throw new Error(`Invalid field element: expected bigint, got ${typeof num}`);
|
||||||
return _0n <= num && num < ORDER;
|
return _0n <= num && num < ORDER; // 0 is valid element, but it's not invertible
|
||||||
},
|
},
|
||||||
isZero: (num) => num === _0n,
|
is0: (num) => num === _0n,
|
||||||
isOdd: (num) => (num & _1n) === _1n,
|
isOdd: (num) => (num & _1n) === _1n,
|
||||||
negate: (num) => mod(-num, ORDER),
|
neg: (num) => mod(-num, ORDER),
|
||||||
equals: (lhs, rhs) => lhs === rhs,
|
eql: (lhs, rhs) => lhs === rhs,
|
||||||
|
|
||||||
square: (num) => mod(num * num, ORDER),
|
sqr: (num) => mod(num * num, ORDER),
|
||||||
add: (lhs, rhs) => mod(lhs + rhs, ORDER),
|
add: (lhs, rhs) => mod(lhs + rhs, ORDER),
|
||||||
sub: (lhs, rhs) => mod(lhs - rhs, ORDER),
|
sub: (lhs, rhs) => mod(lhs - rhs, ORDER),
|
||||||
mul: (lhs, rhs) => mod(lhs * rhs, ORDER),
|
mul: (lhs, rhs) => mod(lhs * rhs, ORDER),
|
||||||
@@ -346,37 +370,58 @@ export function Fp(
|
|||||||
div: (lhs, rhs) => mod(lhs * invert(rhs, ORDER), ORDER),
|
div: (lhs, rhs) => mod(lhs * invert(rhs, ORDER), ORDER),
|
||||||
|
|
||||||
// Same as above, but doesn't normalize
|
// Same as above, but doesn't normalize
|
||||||
squareN: (num) => num * num,
|
sqrN: (num) => num * num,
|
||||||
addN: (lhs, rhs) => lhs + rhs,
|
addN: (lhs, rhs) => lhs + rhs,
|
||||||
subN: (lhs, rhs) => lhs - rhs,
|
subN: (lhs, rhs) => lhs - rhs,
|
||||||
mulN: (lhs, rhs) => lhs * rhs,
|
mulN: (lhs, rhs) => lhs * rhs,
|
||||||
|
|
||||||
invert: (num) => invert(num, ORDER),
|
inv: (num) => invert(num, ORDER),
|
||||||
sqrt: redef.sqrt || ((n) => sqrtP(f, n)),
|
sqrt: redef.sqrt || ((n) => sqrtP(f, n)),
|
||||||
invertBatch: (lst) => FpInvertBatch(f, lst),
|
invertBatch: (lst) => FpInvertBatch(f, lst),
|
||||||
// TODO: do we really need constant cmov?
|
// TODO: do we really need constant cmov?
|
||||||
// We don't have const-time bigints anyway, so probably will be not very useful
|
// We don't have const-time bigints anyway, so probably will be not very useful
|
||||||
cmov: (a, b, c) => (c ? b : a),
|
cmov: (a, b, c) => (c ? b : a),
|
||||||
toBytes: (num) =>
|
toBytes: (num) => (isLE ? numberToBytesLE(num, BYTES) : numberToBytesBE(num, BYTES)),
|
||||||
isLE ? utils.numberToBytesLE(num, BYTES) : utils.numberToBytesBE(num, BYTES),
|
|
||||||
|
|
||||||
fromBytes: (bytes) => {
|
fromBytes: (bytes) => {
|
||||||
if (bytes.length !== BYTES)
|
if (bytes.length !== BYTES)
|
||||||
throw new Error(`Fp.fromBytes: expected ${BYTES}, got ${bytes.length}`);
|
throw new Error(`Fp.fromBytes: expected ${BYTES}, got ${bytes.length}`);
|
||||||
return isLE ? utils.bytesToNumberLE(bytes) : utils.bytesToNumberBE(bytes);
|
return isLE ? bytesToNumberLE(bytes) : bytesToNumberBE(bytes);
|
||||||
},
|
},
|
||||||
} as FpField);
|
} as FpField);
|
||||||
return Object.freeze(f);
|
return Object.freeze(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FpSqrtOdd<T>(Fp: Field<T>, elm: T) {
|
export function FpSqrtOdd<T>(Fp: IField<T>, elm: T) {
|
||||||
if (!Fp.isOdd) throw new Error(`Field doesn't have isOdd`);
|
if (!Fp.isOdd) throw new Error(`Field doesn't have isOdd`);
|
||||||
const root = Fp.sqrt(elm);
|
const root = Fp.sqrt(elm);
|
||||||
return Fp.isOdd(root) ? root : Fp.negate(root);
|
return Fp.isOdd(root) ? root : Fp.neg(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FpSqrtEven<T>(Fp: Field<T>, elm: T) {
|
export function FpSqrtEven<T>(Fp: IField<T>, elm: T) {
|
||||||
if (!Fp.isOdd) throw new Error(`Field doesn't have isOdd`);
|
if (!Fp.isOdd) throw new Error(`Field doesn't have isOdd`);
|
||||||
const root = Fp.sqrt(elm);
|
const root = Fp.sqrt(elm);
|
||||||
return Fp.isOdd(root) ? Fp.negate(root) : root;
|
return Fp.isOdd(root) ? Fp.neg(root) : root;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FIPS 186 B.4.1-compliant "constant-time" private key generation utility.
|
||||||
|
* Can take (n+8) or more bytes of uniform input e.g. from CSPRNG or KDF
|
||||||
|
* and convert them into private scalar, with the modulo bias being neglible.
|
||||||
|
* Needs at least 40 bytes of input for 32-byte private key.
|
||||||
|
* https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
|
||||||
|
* @param hash hash output from SHA3 or a similar function
|
||||||
|
* @returns valid private scalar
|
||||||
|
*/
|
||||||
|
export function hashToPrivateScalar(
|
||||||
|
hash: string | Uint8Array,
|
||||||
|
groupOrder: bigint,
|
||||||
|
isLE = false
|
||||||
|
): bigint {
|
||||||
|
hash = ensureBytes('privateHash', hash);
|
||||||
|
const hashLen = hash.length;
|
||||||
|
const minLen = nLength(groupOrder).nByteLength + 8;
|
||||||
|
if (minLen < 24 || hashLen < minLen || hashLen > 1024)
|
||||||
|
throw new Error(`hashToPrivateScalar: expected ${minLen}-1024 bytes of input, got ${hashLen}`);
|
||||||
|
const num = isLE ? bytesToNumberLE(hash) : bytesToNumberBE(hash);
|
||||||
|
return mod(num, groupOrder - _1n) + _1n;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,57 +1,48 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
import * as mod from './modular.js';
|
import { mod, pow } from './modular.js';
|
||||||
import {
|
import { bytesToNumberLE, ensureBytes, numberToBytesLE, validateObject } from './utils.js';
|
||||||
ensureBytes,
|
|
||||||
numberToBytesLE,
|
|
||||||
bytesToNumberLE,
|
|
||||||
// nLength,
|
|
||||||
} from './utils.js';
|
|
||||||
|
|
||||||
const _0n = BigInt(0);
|
const _0n = BigInt(0);
|
||||||
const _1n = BigInt(1);
|
const _1n = BigInt(1);
|
||||||
type Hex = string | Uint8Array;
|
type Hex = string | Uint8Array;
|
||||||
|
|
||||||
export type CurveType = {
|
export type CurveType = {
|
||||||
// Field over which we'll do calculations. Verify with:
|
P: bigint; // finite field prime
|
||||||
P: bigint;
|
|
||||||
nByteLength: number;
|
nByteLength: number;
|
||||||
adjustScalarBytes?: (bytes: Uint8Array) => Uint8Array;
|
adjustScalarBytes?: (bytes: Uint8Array) => Uint8Array;
|
||||||
domain?: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array;
|
domain?: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array;
|
||||||
a24: bigint; // Related to d, but cannot be derived from it
|
a: bigint;
|
||||||
montgomeryBits: number;
|
montgomeryBits: number;
|
||||||
powPminus2?: (x: bigint) => bigint;
|
powPminus2?: (x: bigint) => bigint;
|
||||||
xyToU?: (x: bigint, y: bigint) => bigint;
|
xyToU?: (x: bigint, y: bigint) => bigint;
|
||||||
Gu: string;
|
Gu: bigint;
|
||||||
|
randomBytes?: (bytesLength?: number) => Uint8Array;
|
||||||
};
|
};
|
||||||
export type CurveFn = {
|
export type CurveFn = {
|
||||||
scalarMult: (scalar: Hex, u: Hex) => Uint8Array;
|
scalarMult: (scalar: Hex, u: Hex) => Uint8Array;
|
||||||
scalarMultBase: (scalar: Hex) => Uint8Array;
|
scalarMultBase: (scalar: Hex) => Uint8Array;
|
||||||
getSharedSecret: (privateKeyA: Hex, publicKeyB: Hex) => Uint8Array;
|
getSharedSecret: (privateKeyA: Hex, publicKeyB: Hex) => Uint8Array;
|
||||||
getPublicKey: (privateKey: Hex) => Uint8Array;
|
getPublicKey: (privateKey: Hex) => Uint8Array;
|
||||||
Gu: string;
|
utils: { randomPrivateKey: () => Uint8Array };
|
||||||
|
GuBytes: Uint8Array;
|
||||||
};
|
};
|
||||||
|
|
||||||
function validateOpts(curve: CurveType) {
|
function validateOpts(curve: CurveType) {
|
||||||
for (const i of ['a24'] as const) {
|
validateObject(
|
||||||
if (typeof curve[i] !== 'bigint')
|
curve,
|
||||||
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
|
{
|
||||||
}
|
a: 'bigint',
|
||||||
for (const i of ['montgomeryBits', 'nByteLength'] as const) {
|
},
|
||||||
if (curve[i] === undefined) continue; // Optional
|
{
|
||||||
if (!Number.isSafeInteger(curve[i]))
|
montgomeryBits: 'isSafeInteger',
|
||||||
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
|
nByteLength: 'isSafeInteger',
|
||||||
}
|
adjustScalarBytes: 'function',
|
||||||
for (const fn of ['adjustScalarBytes', 'domain', 'powPminus2'] as const) {
|
domain: 'function',
|
||||||
if (curve[fn] === undefined) continue; // Optional
|
powPminus2: 'function',
|
||||||
if (typeof curve[fn] !== 'function') throw new Error(`Invalid ${fn} function`);
|
Gu: 'bigint',
|
||||||
}
|
}
|
||||||
for (const i of ['Gu'] as const) {
|
);
|
||||||
if (curve[i] === undefined) continue; // Optional
|
|
||||||
if (typeof curve[i] !== 'string')
|
|
||||||
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
|
|
||||||
}
|
|
||||||
// Set defaults
|
// Set defaults
|
||||||
// ...nLength(curve.n, curve.nBitLength),
|
|
||||||
return Object.freeze({ ...curve } as const);
|
return Object.freeze({ ...curve } as const);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,34 +51,14 @@ function validateOpts(curve: CurveType) {
|
|||||||
export function montgomery(curveDef: CurveType): CurveFn {
|
export function montgomery(curveDef: CurveType): CurveFn {
|
||||||
const CURVE = validateOpts(curveDef);
|
const CURVE = validateOpts(curveDef);
|
||||||
const { P } = CURVE;
|
const { P } = CURVE;
|
||||||
const modP = (a: bigint) => mod.mod(a, P);
|
const modP = (n: bigint) => mod(n, P);
|
||||||
const montgomeryBits = CURVE.montgomeryBits;
|
const montgomeryBits = CURVE.montgomeryBits;
|
||||||
const montgomeryBytes = Math.ceil(montgomeryBits / 8);
|
const montgomeryBytes = Math.ceil(montgomeryBits / 8);
|
||||||
const fieldLen = CURVE.nByteLength;
|
const fieldLen = CURVE.nByteLength;
|
||||||
const adjustScalarBytes = CURVE.adjustScalarBytes || ((bytes: Uint8Array) => bytes);
|
const adjustScalarBytes = CURVE.adjustScalarBytes || ((bytes: Uint8Array) => bytes);
|
||||||
const powPminus2 = CURVE.powPminus2 || ((x: bigint) => mod.pow(x, P - BigInt(2), P));
|
const powPminus2 = CURVE.powPminus2 || ((x: bigint) => pow(x, P - BigInt(2), P));
|
||||||
|
|
||||||
/**
|
// cswap from RFC7748. But it is not from RFC7748!
|
||||||
* Checks for num to be in range:
|
|
||||||
* For strict == true: `0 < num < max`.
|
|
||||||
* For strict == false: `0 <= num < max`.
|
|
||||||
* Converts non-float safe numbers to bigints.
|
|
||||||
*/
|
|
||||||
function normalizeScalar(num: number | bigint, max: bigint, strict = true): bigint {
|
|
||||||
if (!max) throw new TypeError('Specify max value');
|
|
||||||
if (typeof num === 'number' && Number.isSafeInteger(num)) num = BigInt(num);
|
|
||||||
if (typeof num === 'bigint' && num < max) {
|
|
||||||
if (strict) {
|
|
||||||
if (_0n < num) return num;
|
|
||||||
} else {
|
|
||||||
if (_0n <= num) return num;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new TypeError('Expected valid scalar: 0 < scalar < max');
|
|
||||||
}
|
|
||||||
|
|
||||||
// cswap from RFC7748
|
|
||||||
// NOTE: cswap is not from RFC7748!
|
|
||||||
/*
|
/*
|
||||||
cswap(swap, x_2, x_3):
|
cswap(swap, x_2, x_3):
|
||||||
dummy = mask(swap) AND (x_2 XOR x_3)
|
dummy = mask(swap) AND (x_2 XOR x_3)
|
||||||
@@ -104,7 +75,15 @@ export function montgomery(curveDef: CurveType): CurveFn {
|
|||||||
return [x_2, x_3];
|
return [x_2, x_3];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Accepts 0 as well
|
||||||
|
function assertFieldElement(n: bigint): bigint {
|
||||||
|
if (typeof n === 'bigint' && _0n <= n && n < P) return n;
|
||||||
|
throw new Error('Expected valid scalar 0 < scalar < CURVE.P');
|
||||||
|
}
|
||||||
|
|
||||||
// x25519 from 4
|
// x25519 from 4
|
||||||
|
// The constant a24 is (486662 - 2) / 4 = 121665 for curve25519/X25519
|
||||||
|
const a24 = (CURVE.a - BigInt(2)) / BigInt(4);
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param pointU u coordinate (x) on Montgomery Curve 25519
|
* @param pointU u coordinate (x) on Montgomery Curve 25519
|
||||||
@@ -112,13 +91,10 @@ export function montgomery(curveDef: CurveType): CurveFn {
|
|||||||
* @returns new Point on Montgomery curve
|
* @returns new Point on Montgomery curve
|
||||||
*/
|
*/
|
||||||
function montgomeryLadder(pointU: bigint, scalar: bigint): bigint {
|
function montgomeryLadder(pointU: bigint, scalar: bigint): bigint {
|
||||||
const { P } = CURVE;
|
const u = assertFieldElement(pointU);
|
||||||
const u = normalizeScalar(pointU, P);
|
|
||||||
// Section 5: Implementations MUST accept non-canonical values and process them as
|
// Section 5: Implementations MUST accept non-canonical values and process them as
|
||||||
// if they had been reduced modulo the field prime.
|
// if they had been reduced modulo the field prime.
|
||||||
const k = normalizeScalar(scalar, P);
|
const k = assertFieldElement(scalar);
|
||||||
// The constant a24 is (486662 - 2) / 4 = 121665 for curve25519/X25519
|
|
||||||
const a24 = CURVE.a24;
|
|
||||||
const x_1 = u;
|
const x_1 = u;
|
||||||
let x_2 = _1n;
|
let x_2 = _1n;
|
||||||
let z_2 = _0n;
|
let z_2 = _0n;
|
||||||
@@ -172,28 +148,21 @@ export function montgomery(curveDef: CurveType): CurveFn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function decodeUCoordinate(uEnc: Hex): bigint {
|
function decodeUCoordinate(uEnc: Hex): bigint {
|
||||||
const u = ensureBytes(uEnc, montgomeryBytes);
|
|
||||||
// Section 5: When receiving such an array, implementations of X25519
|
// Section 5: When receiving such an array, implementations of X25519
|
||||||
// MUST mask the most significant bit in the final byte.
|
// MUST mask the most significant bit in the final byte.
|
||||||
// This is very ugly way, but it works because fieldLen-1 is outside of bounds for X448, so this becomes NOOP
|
// This is very ugly way, but it works because fieldLen-1 is outside of bounds for X448, so this becomes NOOP
|
||||||
// fieldLen - scalaryBytes = 1 for X448 and = 0 for X25519
|
// fieldLen - scalaryBytes = 1 for X448 and = 0 for X25519
|
||||||
u[fieldLen - 1] &= 127; // 0b0111_1111
|
const u = ensureBytes('u coordinate', uEnc, montgomeryBytes);
|
||||||
|
// u[fieldLen-1] crashes QuickJS (TypeError: out-of-bound numeric index)
|
||||||
|
if (fieldLen === montgomeryBytes) u[fieldLen - 1] &= 127; // 0b0111_1111
|
||||||
return bytesToNumberLE(u);
|
return bytesToNumberLE(u);
|
||||||
}
|
}
|
||||||
|
|
||||||
function decodeScalar(n: Hex): bigint {
|
function decodeScalar(n: Hex): bigint {
|
||||||
const bytes = ensureBytes(n);
|
const bytes = ensureBytes('scalar', n);
|
||||||
if (bytes.length !== montgomeryBytes && bytes.length !== fieldLen)
|
if (bytes.length !== montgomeryBytes && bytes.length !== fieldLen)
|
||||||
throw new Error(`Expected ${montgomeryBytes} or ${fieldLen} bytes, got ${bytes.length}`);
|
throw new Error(`Expected ${montgomeryBytes} or ${fieldLen} bytes, got ${bytes.length}`);
|
||||||
return bytesToNumberLE(adjustScalarBytes(bytes));
|
return bytesToNumberLE(adjustScalarBytes(bytes));
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* Computes shared secret between private key "scalar" and public key's "u" (x) coordinate.
|
|
||||||
* We can get 'y' coordinate from 'u',
|
|
||||||
* but Point.fromHex also wants 'x' coordinate oddity flag,
|
|
||||||
* and we cannot get 'x' without knowing 'v'.
|
|
||||||
* Need to add generic conversion between twisted edwards and complimentary curve for JubJub.
|
|
||||||
*/
|
|
||||||
function scalarMult(scalar: Hex, u: Hex): Uint8Array {
|
function scalarMult(scalar: Hex, u: Hex): Uint8Array {
|
||||||
const pointU = decodeUCoordinate(u);
|
const pointU = decodeUCoordinate(u);
|
||||||
const _scalar = decodeScalar(scalar);
|
const _scalar = decodeScalar(scalar);
|
||||||
@@ -203,14 +172,10 @@ export function montgomery(curveDef: CurveType): CurveFn {
|
|||||||
if (pu === _0n) throw new Error('Invalid private or public key received');
|
if (pu === _0n) throw new Error('Invalid private or public key received');
|
||||||
return encodeUCoordinate(pu);
|
return encodeUCoordinate(pu);
|
||||||
}
|
}
|
||||||
/**
|
// Computes public key from private. By doing scalar multiplication of base point.
|
||||||
* Computes public key from private.
|
const GuBytes = encodeUCoordinate(CURVE.Gu);
|
||||||
* Executes scalar multiplication of curve's base point by scalar.
|
|
||||||
* @param scalar private key
|
|
||||||
* @returns new public key
|
|
||||||
*/
|
|
||||||
function scalarMultBase(scalar: Hex): Uint8Array {
|
function scalarMultBase(scalar: Hex): Uint8Array {
|
||||||
return scalarMult(scalar, CURVE.Gu);
|
return scalarMult(scalar, GuBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -218,6 +183,7 @@ export function montgomery(curveDef: CurveType): CurveFn {
|
|||||||
scalarMultBase,
|
scalarMultBase,
|
||||||
getSharedSecret: (privateKey: Hex, publicKey: Hex) => scalarMult(privateKey, publicKey),
|
getSharedSecret: (privateKey: Hex, publicKey: Hex) => scalarMult(privateKey, publicKey),
|
||||||
getPublicKey: (privateKey: Hex): Uint8Array => scalarMultBase(privateKey),
|
getPublicKey: (privateKey: Hex): Uint8Array => scalarMultBase(privateKey),
|
||||||
Gu: CURVE.Gu,
|
utils: { randomPrivateKey: () => CURVE.randomBytes!(CURVE.nByteLength) },
|
||||||
|
GuBytes: GuBytes,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
119
src/abstract/poseidon.ts
Normal file
119
src/abstract/poseidon.ts
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
|
// Poseidon Hash: https://eprint.iacr.org/2019/458.pdf, https://www.poseidon-hash.info
|
||||||
|
import { IField, 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: IField<bigint>;
|
||||||
|
t: number;
|
||||||
|
roundsFull: number;
|
||||||
|
roundsPartial: number;
|
||||||
|
sboxPower?: number;
|
||||||
|
reversePartialPowIdx?: boolean; // Hack for stark
|
||||||
|
mds: bigint[][];
|
||||||
|
roundConstants: bigint[][];
|
||||||
|
};
|
||||||
|
|
||||||
|
export function validateOpts(opts: PoseidonOpts) {
|
||||||
|
const { Fp } = opts;
|
||||||
|
validateField(Fp);
|
||||||
|
for (const i of ['t', 'roundsFull', 'roundsPartial'] as const) {
|
||||||
|
if (typeof opts[i] !== 'number' || !Number.isSafeInteger(opts[i]))
|
||||||
|
throw new Error(`Poseidon: invalid param ${i}=${opts[i]} (${typeof opts[i]})`);
|
||||||
|
}
|
||||||
|
if (opts.reversePartialPowIdx !== undefined && typeof opts.reversePartialPowIdx !== 'boolean')
|
||||||
|
throw new Error(`Poseidon: invalid param reversePartialPowIdx=${opts.reversePartialPowIdx}`);
|
||||||
|
// Default is 5, but by some reasons stark uses 3
|
||||||
|
let sboxPower = opts.sboxPower;
|
||||||
|
if (sboxPower === undefined) sboxPower = 5;
|
||||||
|
if (typeof sboxPower !== 'number' || !Number.isSafeInteger(sboxPower))
|
||||||
|
throw new Error(`Poseidon wrong sboxPower=${sboxPower}`);
|
||||||
|
|
||||||
|
const _sboxPower = BigInt(sboxPower);
|
||||||
|
let sboxFn = (n: bigint) => FpPow(Fp, n, _sboxPower);
|
||||||
|
// Unwrapped sbox power for common cases (195->142μs)
|
||||||
|
if (sboxPower === 3) sboxFn = (n: bigint) => Fp.mul(Fp.sqrN(n), n);
|
||||||
|
else if (sboxPower === 5) sboxFn = (n: bigint) => Fp.mul(Fp.sqrN(Fp.sqrN(n)), n);
|
||||||
|
|
||||||
|
if (opts.roundsFull % 2 !== 0)
|
||||||
|
throw new Error(`Poseidon roundsFull is not even: ${opts.roundsFull}`);
|
||||||
|
const rounds = opts.roundsFull + opts.roundsPartial;
|
||||||
|
|
||||||
|
if (!Array.isArray(opts.roundConstants) || opts.roundConstants.length !== rounds)
|
||||||
|
throw new Error('Poseidon: wrong round constants');
|
||||||
|
const roundConstants = opts.roundConstants.map((rc) => {
|
||||||
|
if (!Array.isArray(rc) || rc.length !== opts.t)
|
||||||
|
throw new Error(`Poseidon wrong round constants: ${rc}`);
|
||||||
|
return rc.map((i) => {
|
||||||
|
if (typeof i !== 'bigint' || !Fp.isValid(i))
|
||||||
|
throw new Error(`Poseidon wrong round constant=${i}`);
|
||||||
|
return Fp.create(i);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// MDS is TxT matrix
|
||||||
|
if (!Array.isArray(opts.mds) || opts.mds.length !== opts.t)
|
||||||
|
throw new Error('Poseidon: wrong MDS matrix');
|
||||||
|
const mds = opts.mds.map((mdsRow) => {
|
||||||
|
if (!Array.isArray(mdsRow) || mdsRow.length !== opts.t)
|
||||||
|
throw new Error(`Poseidon MDS matrix row: ${mdsRow}`);
|
||||||
|
return mdsRow.map((i) => {
|
||||||
|
if (typeof i !== 'bigint') throw new Error(`Poseidon MDS matrix value=${i}`);
|
||||||
|
return Fp.create(i);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return Object.freeze({ ...opts, rounds, sboxFn, roundConstants, mds });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function splitConstants(rc: bigint[], t: number) {
|
||||||
|
if (typeof t !== 'number') throw new Error('poseidonSplitConstants: wrong t');
|
||||||
|
if (!Array.isArray(rc) || rc.length % t) throw new Error('poseidonSplitConstants: wrong rc');
|
||||||
|
const res = [];
|
||||||
|
let tmp = [];
|
||||||
|
for (let i = 0; i < rc.length; i++) {
|
||||||
|
tmp.push(rc[i]);
|
||||||
|
if (tmp.length === t) {
|
||||||
|
res.push(tmp);
|
||||||
|
tmp = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function poseidon(opts: PoseidonOpts) {
|
||||||
|
const { t, Fp, rounds, sboxFn, reversePartialPowIdx } = validateOpts(opts);
|
||||||
|
const halfRoundsFull = Math.floor(opts.roundsFull / 2);
|
||||||
|
const partialIdx = reversePartialPowIdx ? t - 1 : 0;
|
||||||
|
const poseidonRound = (values: bigint[], isFull: boolean, idx: number) => {
|
||||||
|
values = values.map((i, j) => Fp.add(i, opts.roundConstants[idx][j]));
|
||||||
|
|
||||||
|
if (isFull) values = values.map((i) => sboxFn(i));
|
||||||
|
else values[partialIdx] = sboxFn(values[partialIdx]);
|
||||||
|
// Matrix multiplication
|
||||||
|
values = opts.mds.map((i) =>
|
||||||
|
i.reduce((acc, i, j) => Fp.add(acc, Fp.mulN(i, values[j])), Fp.ZERO)
|
||||||
|
);
|
||||||
|
return values;
|
||||||
|
};
|
||||||
|
const poseidonHash = function poseidonHash(values: bigint[]) {
|
||||||
|
if (!Array.isArray(values) || values.length !== t)
|
||||||
|
throw new Error(`Poseidon: wrong values (expected array of bigints with length ${t})`);
|
||||||
|
values = values.map((i) => {
|
||||||
|
if (typeof i !== 'bigint') throw new Error(`Poseidon: wrong value=${i} (${typeof i})`);
|
||||||
|
return Fp.create(i);
|
||||||
|
});
|
||||||
|
let round = 0;
|
||||||
|
// Apply r_f/2 full rounds.
|
||||||
|
for (let i = 0; i < halfRoundsFull; i++) values = poseidonRound(values, true, round++);
|
||||||
|
// Apply r_p partial rounds.
|
||||||
|
for (let i = 0; i < opts.roundsPartial; i++) values = poseidonRound(values, false, round++);
|
||||||
|
// Apply r_f/2 full rounds.
|
||||||
|
for (let i = 0; i < halfRoundsFull; i++) values = poseidonRound(values, true, round++);
|
||||||
|
|
||||||
|
if (round !== rounds)
|
||||||
|
throw new Error(`Poseidon: wrong number of rounds: last round=${round}, total=${rounds}`);
|
||||||
|
return values;
|
||||||
|
};
|
||||||
|
// For verification in tests
|
||||||
|
poseidonHash.roundConstants = opts.roundConstants;
|
||||||
|
return poseidonHash;
|
||||||
|
}
|
||||||
@@ -1,70 +1,28 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
import * as mod from './modular.js';
|
|
||||||
const _0n = BigInt(0);
|
const _0n = BigInt(0);
|
||||||
const _1n = BigInt(1);
|
const _1n = BigInt(1);
|
||||||
const _2n = BigInt(2);
|
const _2n = BigInt(2);
|
||||||
|
const u8a = (a: any): a is Uint8Array => a instanceof Uint8Array;
|
||||||
|
|
||||||
// We accept hex strings besides Uint8Array for simplicity
|
// We accept hex strings besides Uint8Array for simplicity
|
||||||
export type Hex = Uint8Array | string;
|
export type Hex = Uint8Array | string;
|
||||||
// Very few implementations accept numbers, we do it to ease learning curve
|
// Very few implementations accept numbers, we do it to ease learning curve
|
||||||
export type PrivKey = Hex | bigint | number;
|
export type PrivKey = Hex | bigint;
|
||||||
export type CHash = {
|
export type CHash = {
|
||||||
(message: Uint8Array | string): Uint8Array;
|
(message: Uint8Array | string): Uint8Array;
|
||||||
blockLen: number;
|
blockLen: number;
|
||||||
outputLen: number;
|
outputLen: number;
|
||||||
create(opts?: { dkLen?: number }): any; // For shake
|
create(opts?: { dkLen?: number }): any; // For shake
|
||||||
};
|
};
|
||||||
|
export type FHash = (message: Uint8Array | string) => Uint8Array;
|
||||||
// NOTE: these are generic, even if curve is on some polynominal field (bls), it will still have P/n/h
|
|
||||||
// But generator can be different (Fp2/Fp6 for bls?)
|
|
||||||
export type BasicCurve<T> = {
|
|
||||||
// Field over which we'll do calculations (Fp)
|
|
||||||
Fp: mod.Field<T>;
|
|
||||||
// Curve order, total count of valid points in the field
|
|
||||||
n: bigint;
|
|
||||||
// Bit/byte length of curve order
|
|
||||||
nBitLength?: number;
|
|
||||||
nByteLength?: number;
|
|
||||||
// Cofactor
|
|
||||||
// NOTE: we can assign default value of 1, but then users will just ignore it, without validating with spec
|
|
||||||
// Has not use for now, but nice to have in API
|
|
||||||
h: bigint;
|
|
||||||
hEff?: bigint; // Number to multiply to clear cofactor
|
|
||||||
// Base point (x, y) aka generator point
|
|
||||||
Gx: T;
|
|
||||||
Gy: T;
|
|
||||||
// Wrap private key by curve order (% CURVE.n instead of throwing error)
|
|
||||||
wrapPrivateKey?: boolean;
|
|
||||||
// Point at infinity is perfectly valid point, but not valid public key.
|
|
||||||
// Disabled by default because of compatibility reasons with @noble/secp256k1
|
|
||||||
allowInfinityPoint?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function validateOpts<FP, T>(curve: BasicCurve<FP> & T) {
|
|
||||||
mod.validateField(curve.Fp);
|
|
||||||
for (const i of ['n', 'h'] as const) {
|
|
||||||
if (typeof curve[i] !== 'bigint')
|
|
||||||
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
|
|
||||||
}
|
|
||||||
if (!curve.Fp.isValid(curve.Gx)) throw new Error('Invalid generator X coordinate Fp element');
|
|
||||||
if (!curve.Fp.isValid(curve.Gy)) throw new Error('Invalid generator Y coordinate Fp element');
|
|
||||||
|
|
||||||
for (const i of ['nBitLength', 'nByteLength'] as const) {
|
|
||||||
if (curve[i] === undefined) continue; // Optional
|
|
||||||
if (!Number.isSafeInteger(curve[i]))
|
|
||||||
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
|
|
||||||
}
|
|
||||||
// Set defaults
|
|
||||||
return Object.freeze({ ...nLength(curve.n, curve.nBitLength), ...curve } as const);
|
|
||||||
}
|
|
||||||
|
|
||||||
const hexes = Array.from({ length: 256 }, (v, i) => i.toString(16).padStart(2, '0'));
|
const hexes = Array.from({ length: 256 }, (v, i) => i.toString(16).padStart(2, '0'));
|
||||||
export function bytesToHex(uint8a: Uint8Array): string {
|
export function bytesToHex(bytes: Uint8Array): string {
|
||||||
if (!(uint8a instanceof Uint8Array)) throw new Error('Expected Uint8Array');
|
if (!u8a(bytes)) throw new Error('Uint8Array expected');
|
||||||
// pre-caching improves the speed 6x
|
// pre-caching improves the speed 6x
|
||||||
let hex = '';
|
let hex = '';
|
||||||
for (let i = 0; i < uint8a.length; i++) {
|
for (let i = 0; i < bytes.length; i++) {
|
||||||
hex += hexes[uint8a[i]];
|
hex += hexes[bytes[i]];
|
||||||
}
|
}
|
||||||
return hex;
|
return hex;
|
||||||
}
|
}
|
||||||
@@ -75,25 +33,21 @@ export function numberToHexUnpadded(num: number | bigint): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function hexToNumber(hex: string): bigint {
|
export function hexToNumber(hex: string): bigint {
|
||||||
if (typeof hex !== 'string') {
|
if (typeof hex !== 'string') throw new Error('hex string expected, got ' + typeof hex);
|
||||||
throw new TypeError('hexToNumber: expected string, got ' + typeof hex);
|
|
||||||
}
|
|
||||||
// Big Endian
|
// Big Endian
|
||||||
return BigInt(`0x${hex}`);
|
return BigInt(hex === '' ? '0' : `0x${hex}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Caching slows it down 2-3x
|
// Caching slows it down 2-3x
|
||||||
export function hexToBytes(hex: string): Uint8Array {
|
export function hexToBytes(hex: string): Uint8Array {
|
||||||
if (typeof hex !== 'string') {
|
if (typeof hex !== 'string') throw new Error('hex string expected, got ' + typeof hex);
|
||||||
throw new TypeError('hexToBytes: expected string, got ' + typeof hex);
|
if (hex.length % 2) throw new Error('hex string is invalid: unpadded ' + hex.length);
|
||||||
}
|
|
||||||
if (hex.length % 2) throw new Error('hexToBytes: received invalid unpadded hex ' + hex.length);
|
|
||||||
const array = new Uint8Array(hex.length / 2);
|
const array = new Uint8Array(hex.length / 2);
|
||||||
for (let i = 0; i < array.length; i++) {
|
for (let i = 0; i < array.length; i++) {
|
||||||
const j = i * 2;
|
const j = i * 2;
|
||||||
const hexByte = hex.slice(j, j + 2);
|
const hexByte = hex.slice(j, j + 2);
|
||||||
const byte = Number.parseInt(hexByte, 16);
|
const byte = Number.parseInt(hexByte, 16);
|
||||||
if (Number.isNaN(byte) || byte < 0) throw new Error('Invalid byte sequence');
|
if (Number.isNaN(byte) || byte < 0) throw new Error('invalid byte sequence');
|
||||||
array[i] = byte;
|
array[i] = byte;
|
||||||
}
|
}
|
||||||
return array;
|
return array;
|
||||||
@@ -103,62 +57,48 @@ export function hexToBytes(hex: string): Uint8Array {
|
|||||||
export function bytesToNumberBE(bytes: Uint8Array): bigint {
|
export function bytesToNumberBE(bytes: Uint8Array): bigint {
|
||||||
return hexToNumber(bytesToHex(bytes));
|
return hexToNumber(bytesToHex(bytes));
|
||||||
}
|
}
|
||||||
export function bytesToNumberLE(uint8a: Uint8Array): bigint {
|
export function bytesToNumberLE(bytes: Uint8Array): bigint {
|
||||||
if (!(uint8a instanceof Uint8Array)) throw new Error('Expected Uint8Array');
|
if (!u8a(bytes)) throw new Error('Uint8Array expected');
|
||||||
return BigInt('0x' + bytesToHex(Uint8Array.from(uint8a).reverse()));
|
return hexToNumber(bytesToHex(Uint8Array.from(bytes).reverse()));
|
||||||
}
|
}
|
||||||
|
|
||||||
export const numberToBytesBE = (n: bigint, len: number) =>
|
export const numberToBytesBE = (n: bigint, len: number) =>
|
||||||
hexToBytes(n.toString(16).padStart(len * 2, '0'));
|
hexToBytes(n.toString(16).padStart(len * 2, '0'));
|
||||||
export const numberToBytesLE = (n: bigint, len: number) => numberToBytesBE(n, len).reverse();
|
export const numberToBytesLE = (n: bigint, len: number) => numberToBytesBE(n, len).reverse();
|
||||||
|
// Returns variable number bytes (minimal bigint encoding?)
|
||||||
|
export const numberToVarBytesBE = (n: bigint) => hexToBytes(numberToHexUnpadded(n));
|
||||||
|
|
||||||
export function ensureBytes(hex: Hex, expectedLength?: number): Uint8Array {
|
export function ensureBytes(title: string, hex: Hex, expectedLength?: number): Uint8Array {
|
||||||
// Uint8Array.from() instead of hash.slice() because node.js Buffer
|
let res: Uint8Array;
|
||||||
// is instance of Uint8Array, and its slice() creates **mutable** copy
|
if (typeof hex === 'string') {
|
||||||
const bytes = hex instanceof Uint8Array ? Uint8Array.from(hex) : hexToBytes(hex);
|
try {
|
||||||
if (typeof expectedLength === 'number' && bytes.length !== expectedLength)
|
res = hexToBytes(hex);
|
||||||
throw new Error(`Expected ${expectedLength} bytes`);
|
} catch (e) {
|
||||||
return bytes;
|
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
|
||||||
|
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.
|
// Copies several Uint8Arrays into one.
|
||||||
export function concatBytes(...arrays: Uint8Array[]): Uint8Array {
|
export function concatBytes(...arrs: Uint8Array[]): Uint8Array {
|
||||||
if (!arrays.every((b) => b instanceof Uint8Array)) throw new Error('Uint8Array list expected');
|
const r = new Uint8Array(arrs.reduce((sum, a) => sum + a.length, 0));
|
||||||
if (arrays.length === 1) return arrays[0];
|
let pad = 0; // walk through each item, ensure they have proper type
|
||||||
const length = arrays.reduce((a, arr) => a + arr.length, 0);
|
arrs.forEach((a) => {
|
||||||
const result = new Uint8Array(length);
|
if (!u8a(a)) throw new Error('Uint8Array expected');
|
||||||
for (let i = 0, pad = 0; i < arrays.length; i++) {
|
r.set(a, pad);
|
||||||
const arr = arrays[i];
|
pad += a.length;
|
||||||
result.set(arr, pad);
|
});
|
||||||
pad += arr.length;
|
return r;
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// CURVE.n lengths
|
|
||||||
export function nLength(n: bigint, nBitLength?: number) {
|
|
||||||
// Bit size, byte size of CURVE.n
|
|
||||||
const _nBitLength = nBitLength !== undefined ? nBitLength : n.toString(2).length;
|
|
||||||
const nByteLength = Math.ceil(_nBitLength / 8);
|
|
||||||
return { nBitLength: _nBitLength, nByteLength };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Can take (n+8) or more bytes of uniform input e.g. from CSPRNG or KDF
|
|
||||||
* and convert them into private scalar, with the modulo bias being neglible.
|
|
||||||
* As per FIPS 186 B.4.1.
|
|
||||||
* https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
|
|
||||||
* @param hash hash output from sha512, or a similar function
|
|
||||||
* @returns valid private scalar
|
|
||||||
*/
|
|
||||||
export function hashToPrivateScalar(hash: Hex, CURVE_ORDER: bigint, isLE = false): bigint {
|
|
||||||
hash = ensureBytes(hash);
|
|
||||||
const orderLen = nLength(CURVE_ORDER).nByteLength;
|
|
||||||
const minLen = orderLen + 8;
|
|
||||||
if (orderLen < 16 || hash.length < minLen || hash.length > 1024)
|
|
||||||
throw new Error('Expected valid bytes of private key as per FIPS 186');
|
|
||||||
const num = isLE ? bytesToNumberLE(hash) : bytesToNumberBE(hash);
|
|
||||||
return mod.mod(num, CURVE_ORDER - _1n) + _1n;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function equalBytes(b1: Uint8Array, b2: Uint8Array) {
|
export function equalBytes(b1: Uint8Array, b2: Uint8Array) {
|
||||||
@@ -168,20 +108,139 @@ export function equalBytes(b1: Uint8Array, b2: Uint8Array) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Global symbols in both browsers and Node.js since v11
|
||||||
|
// See https://github.com/microsoft/TypeScript/issues/31535
|
||||||
|
declare const TextEncoder: any;
|
||||||
|
export function utf8ToBytes(str: string): Uint8Array {
|
||||||
|
if (typeof str !== 'string') {
|
||||||
|
throw new Error(`utf8ToBytes expected string, got ${typeof str}`);
|
||||||
|
}
|
||||||
|
return new TextEncoder().encode(str);
|
||||||
|
}
|
||||||
|
|
||||||
// Bit operations
|
// Bit operations
|
||||||
|
|
||||||
// Amount of bits inside bigint (Same as n.toString(2).length)
|
// Amount of bits inside bigint (Same as n.toString(2).length)
|
||||||
export function bitLen(n: bigint) {
|
export function bitLen(n: bigint) {
|
||||||
let len;
|
let len;
|
||||||
for (len = 0; n > 0n; n >>= _1n, len += 1);
|
for (len = 0; n > _0n; n >>= _1n, len += 1);
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
// Gets single bit at position. NOTE: first bit position is 0 (same as arrays)
|
// Gets single bit at position. NOTE: first bit position is 0 (same as arrays)
|
||||||
// Same as !!+Array.from(n.toString(2)).reverse()[pos]
|
// Same as !!+Array.from(n.toString(2)).reverse()[pos]
|
||||||
export const bitGet = (n: bigint, pos: number) => (n >> BigInt(pos)) & 1n;
|
export const bitGet = (n: bigint, pos: number) => (n >> BigInt(pos)) & _1n;
|
||||||
// Sets single bit at position
|
// Sets single bit at position
|
||||||
export const bitSet = (n: bigint, pos: number, value: boolean) =>
|
export const bitSet = (n: bigint, pos: number, value: boolean) =>
|
||||||
n | ((value ? _1n : _0n) << BigInt(pos));
|
n | ((value ? _1n : _0n) << BigInt(pos));
|
||||||
// Return mask for N bits (Same as BigInt(`0b${Array(i).fill('1').join('')}`))
|
// Return mask for N bits (Same as BigInt(`0b${Array(i).fill('1').join('')}`))
|
||||||
// Not using ** operator with bigints for old engines.
|
// Not using ** operator with bigints for old engines.
|
||||||
export const bitMask = (n: number) => (_2n << BigInt(n - 1)) - _1n;
|
export const bitMask = (n: number) => (_2n << BigInt(n - 1)) - _1n;
|
||||||
|
|
||||||
|
// DRBG
|
||||||
|
|
||||||
|
const u8n = (data?: any) => new Uint8Array(data); // creates Uint8Array
|
||||||
|
const u8fr = (arr: any) => Uint8Array.from(arr); // another shortcut
|
||||||
|
type Pred<T> = (v: Uint8Array) => T | undefined;
|
||||||
|
/**
|
||||||
|
* Minimal HMAC-DRBG from NIST 800-90 for RFC6979 sigs.
|
||||||
|
* @returns function that will call DRBG until 2nd arg returns something meaningful
|
||||||
|
* @example
|
||||||
|
* const drbg = createHmacDRBG<Key>(32, 32, hmac);
|
||||||
|
* drbg(seed, bytesToKey); // bytesToKey must return Key or undefined
|
||||||
|
*/
|
||||||
|
export function createHmacDrbg<T>(
|
||||||
|
hashLen: number,
|
||||||
|
qByteLen: number,
|
||||||
|
hmacFn: (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array
|
||||||
|
): (seed: Uint8Array, predicate: Pred<T>) => T {
|
||||||
|
if (typeof hashLen !== 'number' || hashLen < 2) throw new Error('hashLen must be a number');
|
||||||
|
if (typeof qByteLen !== 'number' || qByteLen < 2) throw new Error('qByteLen must be a number');
|
||||||
|
if (typeof hmacFn !== 'function') throw new Error('hmacFn must be a function');
|
||||||
|
// Step B, Step C: set hashLen to 8*ceil(hlen/8)
|
||||||
|
let v = u8n(hashLen); // Minimal non-full-spec HMAC-DRBG from NIST 800-90 for RFC6979 sigs.
|
||||||
|
let k = u8n(hashLen); // Steps B and C of RFC6979 3.2: set hashLen, in our case always same
|
||||||
|
let i = 0; // Iterations counter, will throw when over 1000
|
||||||
|
const reset = () => {
|
||||||
|
v.fill(1);
|
||||||
|
k.fill(0);
|
||||||
|
i = 0;
|
||||||
|
};
|
||||||
|
const h = (...b: Uint8Array[]) => hmacFn(k, v, ...b); // hmac(k)(v, ...values)
|
||||||
|
const reseed = (seed = u8n()) => {
|
||||||
|
// HMAC-DRBG reseed() function. Steps D-G
|
||||||
|
k = h(u8fr([0x00]), seed); // k = hmac(k || v || 0x00 || seed)
|
||||||
|
v = h(); // v = hmac(k || v)
|
||||||
|
if (seed.length === 0) return;
|
||||||
|
k = h(u8fr([0x01]), seed); // k = hmac(k || v || 0x01 || seed)
|
||||||
|
v = h(); // v = hmac(k || v)
|
||||||
|
};
|
||||||
|
const gen = () => {
|
||||||
|
// HMAC-DRBG generate() function
|
||||||
|
if (i++ >= 1000) throw new Error('drbg: tried 1000 values');
|
||||||
|
let len = 0;
|
||||||
|
const out: Uint8Array[] = [];
|
||||||
|
while (len < qByteLen) {
|
||||||
|
v = h();
|
||||||
|
const sl = v.slice();
|
||||||
|
out.push(sl);
|
||||||
|
len += v.length;
|
||||||
|
}
|
||||||
|
return concatBytes(...out);
|
||||||
|
};
|
||||||
|
const genUntil = (seed: Uint8Array, pred: Pred<T>): T => {
|
||||||
|
reset();
|
||||||
|
reseed(seed); // Steps D-G
|
||||||
|
let res: T | undefined = undefined; // Step H: grind until k is in [1..n-1]
|
||||||
|
while (!(res = pred(gen()))) reseed();
|
||||||
|
reset();
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
return genUntil;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validating curves and fields
|
||||||
|
|
||||||
|
const validatorFns = {
|
||||||
|
bigint: (val: any) => typeof val === 'bigint',
|
||||||
|
function: (val: any) => typeof val === 'function',
|
||||||
|
boolean: (val: any) => typeof val === 'boolean',
|
||||||
|
string: (val: any) => typeof val === 'string',
|
||||||
|
isSafeInteger: (val: any) => Number.isSafeInteger(val),
|
||||||
|
array: (val: any) => Array.isArray(val),
|
||||||
|
field: (val: any, object: any) => (object as any).Fp.isValid(val),
|
||||||
|
hash: (val: any) => typeof val === 'function' && Number.isSafeInteger(val.outputLen),
|
||||||
|
} as const;
|
||||||
|
type Validator = keyof typeof validatorFns;
|
||||||
|
type ValMap<T extends Record<string, any>> = { [K in keyof T]?: Validator };
|
||||||
|
// type Record<K extends string | number | symbol, T> = { [P in K]: T; }
|
||||||
|
|
||||||
|
export function validateObject<T extends Record<string, any>>(
|
||||||
|
object: T,
|
||||||
|
validators: ValMap<T>,
|
||||||
|
optValidators: ValMap<T> = {}
|
||||||
|
) {
|
||||||
|
const checkField = (fieldName: keyof T, type: Validator, isOptional: boolean) => {
|
||||||
|
const checkVal = validatorFns[type];
|
||||||
|
if (typeof checkVal !== 'function')
|
||||||
|
throw new Error(`Invalid validator "${type}", expected function`);
|
||||||
|
|
||||||
|
const val = object[fieldName as keyof typeof object];
|
||||||
|
if (isOptional && val === undefined) return;
|
||||||
|
if (!checkVal(val, object)) {
|
||||||
|
throw new Error(
|
||||||
|
`Invalid param ${String(fieldName)}=${val} (${typeof val}), expected ${type}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
for (const [fieldName, type] of Object.entries(validators)) checkField(fieldName, type!, false);
|
||||||
|
for (const [fieldName, type] of Object.entries(optValidators)) checkField(fieldName, type!, true);
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
// validate type tests
|
||||||
|
// const o: { a: number; b: number; c: number } = { a: 1, b: 5, c: 6 };
|
||||||
|
// const z0 = validateObject(o, { a: 'isSafeInteger' }, { c: 'bigint' }); // Ok!
|
||||||
|
// // Should fail type-check
|
||||||
|
// const z1 = validateObject(o, { a: 'tmp' }, { c: 'zz' });
|
||||||
|
// const z2 = validateObject(o, { a: 'isSafeInteger' }, { c: 'zz' });
|
||||||
|
// const z3 = validateObject(o, { test: 'boolean', z: 'bug' });
|
||||||
|
// const z4 = validateObject(o, { a: 'boolean', z: 'bug' });
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
591
src/bls12-381.ts
591
src/bls12-381.ts
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,8 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
import { weierstrass } from './abstract/weierstrass.js';
|
|
||||||
import { sha256 } from '@noble/hashes/sha256';
|
import { sha256 } from '@noble/hashes/sha256';
|
||||||
|
import { weierstrass } from './abstract/weierstrass.js';
|
||||||
import { getHash } from './_shortw_utils.js';
|
import { getHash } from './_shortw_utils.js';
|
||||||
import { Fp } from './abstract/modular.js';
|
import { Field } from './abstract/modular.js';
|
||||||
/**
|
/**
|
||||||
* bn254 pairing-friendly curve.
|
* bn254 pairing-friendly curve.
|
||||||
* Previously known as alt_bn_128, when it had 128-bit security.
|
* Previously known as alt_bn_128, when it had 128-bit security.
|
||||||
@@ -12,7 +12,7 @@ import { Fp } from './abstract/modular.js';
|
|||||||
export const bn254 = weierstrass({
|
export const bn254 = weierstrass({
|
||||||
a: BigInt(0),
|
a: BigInt(0),
|
||||||
b: BigInt(3),
|
b: BigInt(3),
|
||||||
Fp: Fp(BigInt('0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47')),
|
Fp: Field(BigInt('0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47')),
|
||||||
n: BigInt('0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001'),
|
n: BigInt('0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001'),
|
||||||
Gx: BigInt(1),
|
Gx: BigInt(1),
|
||||||
Gy: BigInt(2),
|
Gy: BigInt(2),
|
||||||
259
src/ed25519.ts
259
src/ed25519.ts
@@ -1,17 +1,19 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
import { sha512 } from '@noble/hashes/sha512';
|
import { sha512 } from '@noble/hashes/sha512';
|
||||||
import { concatBytes, randomBytes, utf8ToBytes } from '@noble/hashes/utils';
|
import { concatBytes, randomBytes, utf8ToBytes } from '@noble/hashes/utils';
|
||||||
import { twistedEdwards, ExtendedPointType } from './abstract/edwards.js';
|
import { twistedEdwards, ExtPointType } from './abstract/edwards.js';
|
||||||
import { montgomery } from './abstract/montgomery.js';
|
import { montgomery } from './abstract/montgomery.js';
|
||||||
import { mod, pow2, isNegativeLE, Fp as Field, FpSqrtEven } from './abstract/modular.js';
|
import { mod, pow2, isNegativeLE, Field, FpSqrtEven } from './abstract/modular.js';
|
||||||
import {
|
import {
|
||||||
ensureBytes,
|
|
||||||
equalBytes,
|
equalBytes,
|
||||||
bytesToHex,
|
bytesToHex,
|
||||||
bytesToNumberLE,
|
bytesToNumberLE,
|
||||||
numberToBytesLE,
|
numberToBytesLE,
|
||||||
Hex,
|
Hex,
|
||||||
|
ensureBytes,
|
||||||
} from './abstract/utils.js';
|
} from './abstract/utils.js';
|
||||||
|
import * as htf from './abstract/hash-to-curve.js';
|
||||||
|
import { AffinePoint } from './abstract/curve.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ed25519 Twisted Edwards curve with following addons:
|
* ed25519 Twisted Edwards curve with following addons:
|
||||||
@@ -93,88 +95,15 @@ export const ED25519_TORSION_SUBGROUP = [
|
|||||||
|
|
||||||
const Fp = Field(ED25519_P, undefined, true);
|
const Fp = Field(ED25519_P, undefined, true);
|
||||||
|
|
||||||
// Hash To Curve Elligator2 Map (NOTE: different from ristretto255 elligator)
|
const ed25519Defaults = {
|
||||||
// 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
|
// Param: a
|
||||||
a: BigInt(-1),
|
a: BigInt(-1), // Fp.create(-1) is proper; our way still works and is faster
|
||||||
// Equal to -121665/121666 over finite field.
|
// d is equal to -121665/121666 over finite field.
|
||||||
// Negative number is P - number, and division is invert(number, P)
|
// Negative number is P - number, and division is invert(number, P)
|
||||||
d: BigInt('37095705934669439343138083508754565189542113879843219016388785533085940283555'),
|
d: BigInt('37095705934669439343138083508754565189542113879843219016388785533085940283555'),
|
||||||
// Finite field 𝔽p over which we'll do calculations; 2n ** 255n - 19n
|
// Finite field 𝔽p over which we'll do calculations; 2n ** 255n - 19n
|
||||||
Fp,
|
Fp,
|
||||||
// Subgroup order: how many points ed25519 has
|
// Subgroup order: how many points curve has
|
||||||
// 2n ** 252n + 27742317777372353535851937790883648493n;
|
// 2n ** 252n + 27742317777372353535851937790883648493n;
|
||||||
n: BigInt('7237005577332262213973186563042994240857116359379907606001950938285454250989'),
|
n: BigInt('7237005577332262213973186563042994240857116359379907606001950938285454250989'),
|
||||||
// Cofactor
|
// Cofactor
|
||||||
@@ -189,18 +118,9 @@ const ED25519_DEF = {
|
|||||||
// Ratio of u to v. Allows us to combine inversion and square root. Uses algo from RFC8032 5.1.3.
|
// Ratio of u to v. Allows us to combine inversion and square root. Uses algo from RFC8032 5.1.3.
|
||||||
// Constant-time, u/√v
|
// Constant-time, u/√v
|
||||||
uvRatio,
|
uvRatio,
|
||||||
htfDefaults: {
|
|
||||||
DST: 'edwards25519_XMD:SHA-512_ELL2_RO_',
|
|
||||||
p: Fp.ORDER,
|
|
||||||
m: 1,
|
|
||||||
k: 128,
|
|
||||||
expand: 'xmd',
|
|
||||||
hash: sha512,
|
|
||||||
},
|
|
||||||
mapToCurve: (scalars: bigint[]) => map_to_curve_elligator2_edwards25519(scalars[0]),
|
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const ed25519 = twistedEdwards(ED25519_DEF);
|
export const ed25519 = twistedEdwards(ed25519Defaults);
|
||||||
function ed25519_domain(data: Uint8Array, ctx: Uint8Array, phflag: boolean) {
|
function ed25519_domain(data: Uint8Array, ctx: Uint8Array, phflag: boolean) {
|
||||||
if (ctx.length > 255) throw new Error('Context is too big');
|
if (ctx.length > 255) throw new Error('Context is too big');
|
||||||
return concatBytes(
|
return concatBytes(
|
||||||
@@ -210,19 +130,19 @@ function ed25519_domain(data: Uint8Array, ctx: Uint8Array, phflag: boolean) {
|
|||||||
data
|
data
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
export const ed25519ctx = twistedEdwards({ ...ED25519_DEF, domain: ed25519_domain });
|
export const ed25519ctx = twistedEdwards({ ...ed25519Defaults, domain: ed25519_domain });
|
||||||
export const ed25519ph = twistedEdwards({
|
export const ed25519ph = twistedEdwards({
|
||||||
...ED25519_DEF,
|
...ed25519Defaults,
|
||||||
domain: ed25519_domain,
|
domain: ed25519_domain,
|
||||||
preHash: sha512,
|
prehash: sha512,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const x25519 = montgomery({
|
export const x25519 = montgomery({
|
||||||
P: ED25519_P,
|
P: ED25519_P,
|
||||||
a24: BigInt('121665'),
|
a: BigInt(486662),
|
||||||
montgomeryBits: 255, // n is 253 bits
|
montgomeryBits: 255, // n is 253 bits
|
||||||
nByteLength: 32,
|
nByteLength: 32,
|
||||||
Gu: '0900000000000000000000000000000000000000000000000000000000000000',
|
Gu: BigInt(9),
|
||||||
powPminus2: (x: bigint): bigint => {
|
powPminus2: (x: bigint): bigint => {
|
||||||
const P = ED25519_P;
|
const P = ED25519_P;
|
||||||
// x^(p-2) aka x^(2^255-21)
|
// x^(p-2) aka x^(2^255-21)
|
||||||
@@ -230,10 +150,112 @@ export const x25519 = montgomery({
|
|||||||
return mod(pow2(pow_p_5_8, BigInt(3), P) * b2, P);
|
return mod(pow2(pow_p_5_8, BigInt(3), P) * b2, P);
|
||||||
},
|
},
|
||||||
adjustScalarBytes,
|
adjustScalarBytes,
|
||||||
|
randomBytes,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts ed25519 public key to x25519 public key. Uses formula:
|
||||||
|
* * `(u, v) = ((1+y)/(1-y), sqrt(-486664)*u/x)`
|
||||||
|
* * `(x, y) = (sqrt(-486664)*u/v, (u-1)/(u+1))`
|
||||||
|
* @example
|
||||||
|
* const aPub = ed25519.getPublicKey(utils.randomPrivateKey());
|
||||||
|
* x25519.getSharedSecret(edwardsToMontgomery(aPub), edwardsToMontgomery(someonesPub))
|
||||||
|
*/
|
||||||
|
export function edwardsToMontgomery(edwardsPub: Hex): Uint8Array {
|
||||||
|
const { y } = ed25519.ExtendedPoint.fromHex(edwardsPub);
|
||||||
|
const _1n = BigInt(1);
|
||||||
|
return Fp.toBytes(Fp.create((y - _1n) * Fp.inv(y + _1n)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash To Curve Elligator2 Map (NOTE: different from ristretto255 elligator)
|
||||||
|
// NOTE: very important part is usage of FpSqrtEven for ELL2_C1_EDWARDS, since
|
||||||
|
// SageMath returns different root first and everything falls apart
|
||||||
|
|
||||||
|
const ELL2_C1 = (Fp.ORDER + BigInt(3)) / BigInt(8); // 1. c1 = (q + 3) / 8 # Integer arithmetic
|
||||||
|
|
||||||
|
const ELL2_C2 = Fp.pow(_2n, ELL2_C1); // 2. c2 = 2^c1
|
||||||
|
const ELL2_C3 = Fp.sqrt(Fp.neg(Fp.ONE)); // 3. c3 = sqrt(-1)
|
||||||
|
const ELL2_C4 = (Fp.ORDER - BigInt(5)) / BigInt(8); // 4. c4 = (q - 5) / 8 # Integer arithmetic
|
||||||
|
const ELL2_J = BigInt(486662);
|
||||||
|
|
||||||
|
// prettier-ignore
|
||||||
|
function map_to_curve_elligator2_curve25519(u: bigint) {
|
||||||
|
let tv1 = Fp.sqr(u); // 1. tv1 = u^2
|
||||||
|
tv1 = Fp.mul(tv1, _2n); // 2. tv1 = 2 * tv1
|
||||||
|
let xd = Fp.add(tv1, Fp.ONE); // 3. xd = tv1 + 1 # Nonzero: -1 is square (mod p), tv1 is not
|
||||||
|
let x1n = Fp.neg(ELL2_J); // 4. x1n = -J # x1 = x1n / xd = -J / (1 + 2 * u^2)
|
||||||
|
let tv2 = Fp.sqr(xd); // 5. tv2 = xd^2
|
||||||
|
let gxd = Fp.mul(tv2, xd); // 6. gxd = tv2 * xd # gxd = xd^3
|
||||||
|
let gx1 = Fp.mul(tv1, ELL2_J); // 7. gx1 = J * tv1 # x1n + J * xd
|
||||||
|
gx1 = Fp.mul(gx1, x1n); // 8. gx1 = gx1 * x1n # x1n^2 + J * x1n * xd
|
||||||
|
gx1 = Fp.add(gx1, tv2); // 9. gx1 = gx1 + tv2 # x1n^2 + J * x1n * xd + xd^2
|
||||||
|
gx1 = Fp.mul(gx1, x1n); // 10. gx1 = gx1 * x1n # x1n^3 + J * x1n^2 * xd + x1n * xd^2
|
||||||
|
let tv3 = Fp.sqr(gxd); // 11. tv3 = gxd^2
|
||||||
|
tv2 = Fp.sqr(tv3); // 12. tv2 = tv3^2 # gxd^4
|
||||||
|
tv3 = Fp.mul(tv3, gxd); // 13. tv3 = tv3 * gxd # gxd^3
|
||||||
|
tv3 = Fp.mul(tv3, gx1); // 14. tv3 = tv3 * gx1 # gx1 * gxd^3
|
||||||
|
tv2 = Fp.mul(tv2, tv3); // 15. tv2 = tv2 * tv3 # gx1 * gxd^7
|
||||||
|
let y11 = Fp.pow(tv2, ELL2_C4); // 16. y11 = tv2^c4 # (gx1 * gxd^7)^((p - 5) / 8)
|
||||||
|
y11 = Fp.mul(y11, tv3); // 17. y11 = y11 * tv3 # gx1*gxd^3*(gx1*gxd^7)^((p-5)/8)
|
||||||
|
let y12 = Fp.mul(y11, ELL2_C3); // 18. y12 = y11 * c3
|
||||||
|
tv2 = Fp.sqr(y11); // 19. tv2 = y11^2
|
||||||
|
tv2 = Fp.mul(tv2, gxd); // 20. tv2 = tv2 * gxd
|
||||||
|
let e1 = Fp.eql(tv2, gx1); // 21. e1 = tv2 == gx1
|
||||||
|
let y1 = Fp.cmov(y12, y11, e1); // 22. y1 = CMOV(y12, y11, e1) # If g(x1) is square, this is its sqrt
|
||||||
|
let x2n = Fp.mul(x1n, tv1); // 23. x2n = x1n * tv1 # x2 = x2n / xd = 2 * u^2 * x1n / xd
|
||||||
|
let y21 = Fp.mul(y11, u); // 24. y21 = y11 * u
|
||||||
|
y21 = Fp.mul(y21, ELL2_C2); // 25. y21 = y21 * c2
|
||||||
|
let y22 = Fp.mul(y21, ELL2_C3); // 26. y22 = y21 * c3
|
||||||
|
let gx2 = Fp.mul(gx1, tv1); // 27. gx2 = gx1 * tv1 # g(x2) = gx2 / gxd = 2 * u^2 * g(x1)
|
||||||
|
tv2 = Fp.sqr(y21); // 28. tv2 = y21^2
|
||||||
|
tv2 = Fp.mul(tv2, gxd); // 29. tv2 = tv2 * gxd
|
||||||
|
let e2 = Fp.eql(tv2, gx2); // 30. e2 = tv2 == gx2
|
||||||
|
let y2 = Fp.cmov(y22, y21, e2); // 31. y2 = CMOV(y22, y21, e2) # If g(x2) is square, this is its sqrt
|
||||||
|
tv2 = Fp.sqr(y1); // 32. tv2 = y1^2
|
||||||
|
tv2 = Fp.mul(tv2, gxd); // 33. tv2 = tv2 * gxd
|
||||||
|
let e3 = Fp.eql(tv2, gx1); // 34. e3 = tv2 == gx1
|
||||||
|
let xn = Fp.cmov(x2n, x1n, e3); // 35. xn = CMOV(x2n, x1n, e3) # If e3, x = x1, else x = x2
|
||||||
|
let y = Fp.cmov(y2, y1, e3); // 36. y = CMOV(y2, y1, e3) # If e3, y = y1, else y = y2
|
||||||
|
let e4 = Fp.isOdd(y); // 37. e4 = sgn0(y) == 1 # Fix sign of y
|
||||||
|
y = Fp.cmov(y, Fp.neg(y), e3 !== e4); // 38. y = CMOV(y, -y, e3 XOR e4)
|
||||||
|
return { xMn: xn, xMd: xd, yMn: y, yMd: _1n }; // 39. return (xn, xd, y, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const ELL2_C1_EDWARDS = FpSqrtEven(Fp, Fp.neg(BigInt(486664))); // sgn0(c1) MUST equal 0
|
||||||
|
function map_to_curve_elligator2_edwards25519(u: bigint) {
|
||||||
|
const { xMn, xMd, yMn, yMd } = map_to_curve_elligator2_curve25519(u); // 1. (xMn, xMd, yMn, yMd) = map_to_curve_elligator2_curve25519(u)
|
||||||
|
let xn = Fp.mul(xMn, yMd); // 2. xn = xMn * yMd
|
||||||
|
xn = Fp.mul(xn, ELL2_C1_EDWARDS); // 3. xn = xn * c1
|
||||||
|
let xd = Fp.mul(xMd, yMn); // 4. xd = xMd * yMn # xn / xd = c1 * xM / yM
|
||||||
|
let yn = Fp.sub(xMn, xMd); // 5. yn = xMn - xMd
|
||||||
|
let yd = Fp.add(xMn, xMd); // 6. yd = xMn + xMd # (n / d - 1) / (n / d + 1) = (n - d) / (n + d)
|
||||||
|
let tv1 = Fp.mul(xd, yd); // 7. tv1 = xd * yd
|
||||||
|
let e = Fp.eql(tv1, Fp.ZERO); // 8. e = tv1 == 0
|
||||||
|
xn = Fp.cmov(xn, Fp.ZERO, e); // 9. xn = CMOV(xn, 0, e)
|
||||||
|
xd = Fp.cmov(xd, Fp.ONE, e); // 10. xd = CMOV(xd, 1, e)
|
||||||
|
yn = Fp.cmov(yn, Fp.ONE, e); // 11. yn = CMOV(yn, 1, e)
|
||||||
|
yd = Fp.cmov(yd, Fp.ONE, e); // 12. yd = CMOV(yd, 1, e)
|
||||||
|
|
||||||
|
const inv = Fp.invertBatch([xd, yd]); // batch division
|
||||||
|
return { x: Fp.mul(xn, inv[0]), y: Fp.mul(yn, inv[1]) }; // 13. return (xn, xd, yn, yd)
|
||||||
|
}
|
||||||
|
const { hashToCurve, encodeToCurve } = htf.createHasher(
|
||||||
|
ed25519.ExtendedPoint,
|
||||||
|
(scalars: bigint[]) => map_to_curve_elligator2_edwards25519(scalars[0]),
|
||||||
|
{
|
||||||
|
DST: 'edwards25519_XMD:SHA-512_ELL2_RO_',
|
||||||
|
encodeDST: 'edwards25519_XMD:SHA-512_ELL2_NU_',
|
||||||
|
p: Fp.ORDER,
|
||||||
|
m: 1,
|
||||||
|
k: 128,
|
||||||
|
expand: 'xmd',
|
||||||
|
hash: sha512,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
export { hashToCurve, encodeToCurve };
|
||||||
|
|
||||||
function assertRstPoint(other: unknown) {
|
function assertRstPoint(other: unknown) {
|
||||||
if (!(other instanceof RistrettoPoint)) throw new TypeError('RistrettoPoint expected');
|
if (!(other instanceof RistrettoPoint)) throw new Error('RistrettoPoint expected');
|
||||||
}
|
}
|
||||||
// √(-1) aka √(a) aka 2^((p-1)/4)
|
// √(-1) aka √(a) aka 2^((p-1)/4)
|
||||||
const SQRT_M1 = BigInt(
|
const SQRT_M1 = BigInt(
|
||||||
@@ -260,16 +282,16 @@ const invertSqrt = (number: bigint) => uvRatio(_1n, number);
|
|||||||
|
|
||||||
const MAX_255B = BigInt('0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
|
const MAX_255B = BigInt('0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
|
||||||
const bytes255ToNumberLE = (bytes: Uint8Array) =>
|
const bytes255ToNumberLE = (bytes: Uint8Array) =>
|
||||||
ed25519.utils.mod(bytesToNumberLE(bytes) & MAX_255B);
|
ed25519.CURVE.Fp.create(bytesToNumberLE(bytes) & MAX_255B);
|
||||||
|
|
||||||
type ExtendedPoint = ExtendedPointType;
|
type ExtendedPoint = ExtPointType;
|
||||||
|
|
||||||
// Computes Elligator map for Ristretto
|
// Computes Elligator map for Ristretto
|
||||||
// https://ristretto.group/formulas/elligator.html
|
// https://ristretto.group/formulas/elligator.html
|
||||||
function calcElligatorRistrettoMap(r0: bigint): ExtendedPoint {
|
function calcElligatorRistrettoMap(r0: bigint): ExtendedPoint {
|
||||||
const { d } = ed25519.CURVE;
|
const { d } = ed25519.CURVE;
|
||||||
const P = ed25519.CURVE.Fp.ORDER;
|
const P = ed25519.CURVE.Fp.ORDER;
|
||||||
const { mod } = ed25519.utils;
|
const mod = ed25519.CURVE.Fp.create;
|
||||||
const r = mod(SQRT_M1 * r0 * r0); // 1
|
const r = mod(SQRT_M1 * r0 * r0); // 1
|
||||||
const Ns = mod((r + _1n) * ONE_MINUS_D_SQ); // 2
|
const Ns = mod((r + _1n) * ONE_MINUS_D_SQ); // 2
|
||||||
let c = BigInt(-1); // 3
|
let c = BigInt(-1); // 3
|
||||||
@@ -302,6 +324,11 @@ export class RistrettoPoint {
|
|||||||
// Private property to discourage combining ExtendedPoint + RistrettoPoint
|
// Private property to discourage combining ExtendedPoint + RistrettoPoint
|
||||||
// Always use Ristretto encoding/decoding instead.
|
// Always use Ristretto encoding/decoding instead.
|
||||||
constructor(private readonly ep: ExtendedPoint) {}
|
constructor(private readonly ep: ExtendedPoint) {}
|
||||||
|
|
||||||
|
static fromAffine(ap: AffinePoint<bigint>) {
|
||||||
|
return new RistrettoPoint(ed25519.ExtendedPoint.fromAffine(ap));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Takes uniform output of 64-bit hash function like sha512 and converts it to `RistrettoPoint`.
|
* Takes uniform output of 64-bit hash function like sha512 and converts it to `RistrettoPoint`.
|
||||||
* The hash-to-group operation applies Elligator twice and adds the results.
|
* The hash-to-group operation applies Elligator twice and adds the results.
|
||||||
@@ -310,7 +337,7 @@ export class RistrettoPoint {
|
|||||||
* @param hex 64-bit output of a hash function
|
* @param hex 64-bit output of a hash function
|
||||||
*/
|
*/
|
||||||
static hashToCurve(hex: Hex): RistrettoPoint {
|
static hashToCurve(hex: Hex): RistrettoPoint {
|
||||||
hex = ensureBytes(hex, 64);
|
hex = ensureBytes('ristrettoHash', hex, 64);
|
||||||
const r1 = bytes255ToNumberLE(hex.slice(0, 32));
|
const r1 = bytes255ToNumberLE(hex.slice(0, 32));
|
||||||
const R1 = calcElligatorRistrettoMap(r1);
|
const R1 = calcElligatorRistrettoMap(r1);
|
||||||
const r2 = bytes255ToNumberLE(hex.slice(32, 64));
|
const r2 = bytes255ToNumberLE(hex.slice(32, 64));
|
||||||
@@ -324,10 +351,10 @@ export class RistrettoPoint {
|
|||||||
* @param hex Ristretto-encoded 32 bytes. Not every 32-byte string is valid ristretto encoding
|
* @param hex Ristretto-encoded 32 bytes. Not every 32-byte string is valid ristretto encoding
|
||||||
*/
|
*/
|
||||||
static fromHex(hex: Hex): RistrettoPoint {
|
static fromHex(hex: Hex): RistrettoPoint {
|
||||||
hex = ensureBytes(hex, 32);
|
hex = ensureBytes('ristrettoHex', hex, 32);
|
||||||
const { a, d } = ed25519.CURVE;
|
const { a, d } = ed25519.CURVE;
|
||||||
const P = ed25519.CURVE.Fp.ORDER;
|
const P = ed25519.CURVE.Fp.ORDER;
|
||||||
const { mod } = ed25519.utils;
|
const mod = ed25519.CURVE.Fp.create;
|
||||||
const emsg = 'RistrettoPoint.fromHex: the hex is not valid encoding of RistrettoPoint';
|
const emsg = 'RistrettoPoint.fromHex: the hex is not valid encoding of RistrettoPoint';
|
||||||
const s = bytes255ToNumberLE(hex);
|
const s = bytes255ToNumberLE(hex);
|
||||||
// 1. Check that s_bytes is the canonical encoding of a field element, or else abort.
|
// 1. Check that s_bytes is the canonical encoding of a field element, or else abort.
|
||||||
@@ -355,9 +382,9 @@ export class RistrettoPoint {
|
|||||||
* https://ristretto.group/formulas/encoding.html
|
* https://ristretto.group/formulas/encoding.html
|
||||||
*/
|
*/
|
||||||
toRawBytes(): Uint8Array {
|
toRawBytes(): Uint8Array {
|
||||||
let { x, y, z, t } = this.ep;
|
let { ex: x, ey: y, ez: z, et: t } = this.ep;
|
||||||
const P = ed25519.CURVE.Fp.ORDER;
|
const P = ed25519.CURVE.Fp.ORDER;
|
||||||
const { mod } = ed25519.utils;
|
const mod = ed25519.CURVE.Fp.create;
|
||||||
const u1 = mod(mod(z + y) * mod(z - y)); // 1
|
const u1 = mod(mod(z + y) * mod(z - y)); // 1
|
||||||
const u2 = mod(x * y); // 2
|
const u2 = mod(x * y); // 2
|
||||||
// Square root always exists
|
// Square root always exists
|
||||||
@@ -393,12 +420,12 @@ export class RistrettoPoint {
|
|||||||
// Compare one point to another.
|
// Compare one point to another.
|
||||||
equals(other: RistrettoPoint): boolean {
|
equals(other: RistrettoPoint): boolean {
|
||||||
assertRstPoint(other);
|
assertRstPoint(other);
|
||||||
const a = this.ep;
|
const { ex: X1, ey: Y1 } = this.ep;
|
||||||
const b = other.ep;
|
const { ex: X2, ey: Y2 } = other.ep;
|
||||||
const { mod } = ed25519.utils;
|
const mod = ed25519.CURVE.Fp.create;
|
||||||
// (x1 * y2 == y1 * x2) | (y1 * y2 == x1 * x2)
|
// (x1 * y2 == y1 * x2) | (y1 * y2 == x1 * x2)
|
||||||
const one = mod(a.x * b.y) === mod(a.y * b.x);
|
const one = mod(X1 * Y2) === mod(Y1 * X2);
|
||||||
const two = mod(a.y * b.y) === mod(a.x * b.x);
|
const two = mod(Y1 * Y2) === mod(X1 * X2);
|
||||||
return one || two;
|
return one || two;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -412,11 +439,21 @@ export class RistrettoPoint {
|
|||||||
return new RistrettoPoint(this.ep.subtract(other.ep));
|
return new RistrettoPoint(this.ep.subtract(other.ep));
|
||||||
}
|
}
|
||||||
|
|
||||||
multiply(scalar: number | bigint): RistrettoPoint {
|
multiply(scalar: bigint): RistrettoPoint {
|
||||||
return new RistrettoPoint(this.ep.multiply(scalar));
|
return new RistrettoPoint(this.ep.multiply(scalar));
|
||||||
}
|
}
|
||||||
|
|
||||||
multiplyUnsafe(scalar: number | bigint): RistrettoPoint {
|
multiplyUnsafe(scalar: bigint): RistrettoPoint {
|
||||||
return new RistrettoPoint(this.ep.multiplyUnsafe(scalar));
|
return new RistrettoPoint(this.ep.multiplyUnsafe(scalar));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/14/
|
||||||
|
// Appendix B. Hashing to ristretto255
|
||||||
|
export const hash_to_ristretto255 = (msg: Uint8Array, options: htf.htfBasicOpts) => {
|
||||||
|
const d = options.DST;
|
||||||
|
const DST = typeof d === 'string' ? utf8ToBytes(d) : d;
|
||||||
|
const uniform_bytes = htf.expand_message_xmd(msg, DST, 64, sha512);
|
||||||
|
const P = RistrettoPoint.hashToCurve(uniform_bytes);
|
||||||
|
return P;
|
||||||
|
};
|
||||||
|
|||||||
216
src/ed448.ts
216
src/ed448.ts
@@ -2,8 +2,9 @@
|
|||||||
import { shake256 } from '@noble/hashes/sha3';
|
import { shake256 } from '@noble/hashes/sha3';
|
||||||
import { concatBytes, randomBytes, utf8ToBytes, wrapConstructor } from '@noble/hashes/utils';
|
import { concatBytes, randomBytes, utf8ToBytes, wrapConstructor } from '@noble/hashes/utils';
|
||||||
import { twistedEdwards } from './abstract/edwards.js';
|
import { twistedEdwards } from './abstract/edwards.js';
|
||||||
import { mod, pow2, Fp as Field } from './abstract/modular.js';
|
import { mod, pow2, Field } from './abstract/modular.js';
|
||||||
import { montgomery } from './abstract/montgomery.js';
|
import { montgomery } from './abstract/montgomery.js';
|
||||||
|
import * as htf from './abstract/hash-to-curve.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Edwards448 (not Ed448-Goldilocks) curve with following addons:
|
* Edwards448 (not Ed448-Goldilocks) curve with following addons:
|
||||||
@@ -53,81 +54,7 @@ function adjustScalarBytes(bytes: Uint8Array): Uint8Array {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Fp = Field(ed448P, 456, true);
|
const Fp = Field(ed448P, 456, true);
|
||||||
|
const _4n = BigInt(4);
|
||||||
// Hash To Curve Elligator2 Map
|
|
||||||
const ELL2_C1 = (Fp.ORDER - BigInt(3)) / BigInt(4); // 1. c1 = (q - 3) / 4 # Integer arithmetic
|
|
||||||
const ELL2_J = BigInt(156326);
|
|
||||||
function map_to_curve_elligator2_curve448(u: bigint) {
|
|
||||||
let tv1 = Fp.square(u); // 1. tv1 = u^2
|
|
||||||
let e1 = Fp.equals(tv1, Fp.ONE); // 2. e1 = tv1 == 1
|
|
||||||
tv1 = Fp.cmov(tv1, Fp.ZERO, e1); // 3. tv1 = CMOV(tv1, 0, e1) # If Z * u^2 == -1, set tv1 = 0
|
|
||||||
let xd = Fp.sub(Fp.ONE, tv1); // 4. xd = 1 - tv1
|
|
||||||
let x1n = Fp.negate(ELL2_J); // 5. x1n = -J
|
|
||||||
let tv2 = Fp.square(xd); // 6. tv2 = xd^2
|
|
||||||
let gxd = Fp.mul(tv2, xd); // 7. gxd = tv2 * xd # gxd = xd^3
|
|
||||||
let gx1 = Fp.mul(tv1, Fp.negate(ELL2_J)); // 8. gx1 = -J * tv1 # x1n + J * xd
|
|
||||||
gx1 = Fp.mul(gx1, x1n); // 9. gx1 = gx1 * x1n # x1n^2 + J * x1n * xd
|
|
||||||
gx1 = Fp.add(gx1, tv2); // 10. gx1 = gx1 + tv2 # x1n^2 + J * x1n * xd + xd^2
|
|
||||||
gx1 = Fp.mul(gx1, x1n); // 11. gx1 = gx1 * x1n # x1n^3 + J * x1n^2 * xd + x1n * xd^2
|
|
||||||
let tv3 = Fp.square(gxd); // 12. tv3 = gxd^2
|
|
||||||
tv2 = Fp.mul(gx1, gxd); // 13. tv2 = gx1 * gxd # gx1 * gxd
|
|
||||||
tv3 = Fp.mul(tv3, tv2); // 14. tv3 = tv3 * tv2 # gx1 * gxd^3
|
|
||||||
let y1 = Fp.pow(tv3, ELL2_C1); // 15. y1 = tv3^c1 # (gx1 * gxd^3)^((p - 3) / 4)
|
|
||||||
y1 = Fp.mul(y1, tv2); // 16. y1 = y1 * tv2 # gx1 * gxd * (gx1 * gxd^3)^((p - 3) / 4)
|
|
||||||
let x2n = Fp.mul(x1n, Fp.negate(tv1)); // 17. x2n = -tv1 * x1n # x2 = x2n / xd = -1 * u^2 * x1n / xd
|
|
||||||
let y2 = Fp.mul(y1, u); // 18. y2 = y1 * u
|
|
||||||
y2 = Fp.cmov(y2, Fp.ZERO, e1); // 19. y2 = CMOV(y2, 0, e1)
|
|
||||||
tv2 = Fp.square(y1); // 20. tv2 = y1^2
|
|
||||||
tv2 = Fp.mul(tv2, gxd); // 21. tv2 = tv2 * gxd
|
|
||||||
let e2 = Fp.equals(tv2, gx1); // 22. e2 = tv2 == gx1
|
|
||||||
let xn = Fp.cmov(x2n, x1n, e2); // 23. xn = CMOV(x2n, x1n, e2) # If e2, x = x1, else x = x2
|
|
||||||
let y = Fp.cmov(y2, y1, e2); // 24. y = CMOV(y2, y1, e2) # If e2, y = y1, else y = y2
|
|
||||||
let e3 = Fp.isOdd(y); // 25. e3 = sgn0(y) == 1 # Fix sign of y
|
|
||||||
y = Fp.cmov(y, Fp.negate(y), e2 !== e3); // 26. y = CMOV(y, -y, e2 XOR e3)
|
|
||||||
return { xn, xd, yn: y, yd: Fp.ONE }; // 27. return (xn, xd, y, 1)
|
|
||||||
}
|
|
||||||
function map_to_curve_elligator2_edwards448(u: bigint) {
|
|
||||||
let { xn, xd, yn, yd } = map_to_curve_elligator2_curve448(u); // 1. (xn, xd, yn, yd) = map_to_curve_elligator2_curve448(u)
|
|
||||||
let xn2 = Fp.square(xn); // 2. xn2 = xn^2
|
|
||||||
let xd2 = Fp.square(xd); // 3. xd2 = xd^2
|
|
||||||
let xd4 = Fp.square(xd2); // 4. xd4 = xd2^2
|
|
||||||
let yn2 = Fp.square(yn); // 5. yn2 = yn^2
|
|
||||||
let yd2 = Fp.square(yd); // 6. yd2 = yd^2
|
|
||||||
let xEn = Fp.sub(xn2, xd2); // 7. xEn = xn2 - xd2
|
|
||||||
let tv2 = Fp.sub(xEn, xd2); // 8. tv2 = xEn - xd2
|
|
||||||
xEn = Fp.mul(xEn, xd2); // 9. xEn = xEn * xd2
|
|
||||||
xEn = Fp.mul(xEn, yd); // 10. xEn = xEn * yd
|
|
||||||
xEn = Fp.mul(xEn, yn); // 11. xEn = xEn * yn
|
|
||||||
xEn = Fp.mul(xEn, 4n); // 12. xEn = xEn * 4
|
|
||||||
tv2 = Fp.mul(tv2, xn2); // 13. tv2 = tv2 * xn2
|
|
||||||
tv2 = Fp.mul(tv2, yd2); // 14. tv2 = tv2 * yd2
|
|
||||||
let tv3 = Fp.mul(yn2, 4n); // 15. tv3 = 4 * yn2
|
|
||||||
let tv1 = Fp.add(tv3, yd2); // 16. tv1 = tv3 + yd2
|
|
||||||
tv1 = Fp.mul(tv1, xd4); // 17. tv1 = tv1 * xd4
|
|
||||||
let xEd = Fp.add(tv1, tv2); // 18. xEd = tv1 + tv2
|
|
||||||
tv2 = Fp.mul(tv2, xn); // 19. tv2 = tv2 * xn
|
|
||||||
let tv4 = Fp.mul(xn, xd4); // 20. tv4 = xn * xd4
|
|
||||||
let yEn = Fp.sub(tv3, yd2); // 21. yEn = tv3 - yd2
|
|
||||||
yEn = Fp.mul(yEn, tv4); // 22. yEn = yEn * tv4
|
|
||||||
yEn = Fp.sub(yEn, tv2); // 23. yEn = yEn - tv2
|
|
||||||
tv1 = Fp.add(xn2, xd2); // 24. tv1 = xn2 + xd2
|
|
||||||
tv1 = Fp.mul(tv1, xd2); // 25. tv1 = tv1 * xd2
|
|
||||||
tv1 = Fp.mul(tv1, xd); // 26. tv1 = tv1 * xd
|
|
||||||
tv1 = Fp.mul(tv1, yn2); // 27. tv1 = tv1 * yn2
|
|
||||||
tv1 = Fp.mul(tv1, BigInt(-2)); // 28. tv1 = -2 * tv1
|
|
||||||
let yEd = Fp.add(tv2, tv1); // 29. yEd = tv2 + tv1
|
|
||||||
tv4 = Fp.mul(tv4, yd2); // 30. tv4 = tv4 * yd2
|
|
||||||
yEd = Fp.add(yEd, tv4); // 31. yEd = yEd + tv4
|
|
||||||
tv1 = Fp.mul(xEd, yEd); // 32. tv1 = xEd * yEd
|
|
||||||
let e = Fp.equals(tv1, Fp.ZERO); // 33. e = tv1 == 0
|
|
||||||
xEn = Fp.cmov(xEn, Fp.ZERO, e); // 34. xEn = CMOV(xEn, 0, e)
|
|
||||||
xEd = Fp.cmov(xEd, Fp.ONE, e); // 35. xEd = CMOV(xEd, 1, e)
|
|
||||||
yEn = Fp.cmov(yEn, Fp.ONE, e); // 36. yEn = CMOV(yEn, 1, e)
|
|
||||||
yEd = Fp.cmov(yEd, Fp.ONE, e); // 37. yEd = CMOV(yEd, 1, e)
|
|
||||||
|
|
||||||
const inv = Fp.invertBatch([xEd, yEd]); // batch division
|
|
||||||
return { x: Fp.mul(xEn, inv[0]), y: Fp.mul(yEn, inv[1]) }; // 38. return (xEn, xEd, yEn, yEd)
|
|
||||||
}
|
|
||||||
|
|
||||||
const ED448_DEF = {
|
const ED448_DEF = {
|
||||||
// Param: a
|
// Param: a
|
||||||
@@ -138,7 +65,8 @@ const ED448_DEF = {
|
|||||||
),
|
),
|
||||||
// Finite field 𝔽p over which we'll do calculations; 2n ** 448n - 2n ** 224n - 1n
|
// Finite field 𝔽p over which we'll do calculations; 2n ** 448n - 2n ** 224n - 1n
|
||||||
Fp,
|
Fp,
|
||||||
// Subgroup order: how many points ed448 has; 2n**446n - 13818066809895115352007386748515426880336692474882178609894547503885n
|
// Subgroup order: how many points curve has;
|
||||||
|
// 2n**446n - 13818066809895115352007386748515426880336692474882178609894547503885n
|
||||||
n: BigInt(
|
n: BigInt(
|
||||||
'181709681073901722637330951972001133588410340171829515070372549795146003961539585716195755291692375963310293709091662304773755859649779'
|
'181709681073901722637330951972001133588410340171829515070372549795146003961539585716195755291692375963310293709091662304773755859649779'
|
||||||
),
|
),
|
||||||
@@ -188,27 +116,18 @@ const ED448_DEF = {
|
|||||||
// square root exists, and the decoding fails.
|
// square root exists, and the decoding fails.
|
||||||
return { isValid: mod(x2 * v, P) === u, value: x };
|
return { isValid: mod(x2 * v, P) === u, value: x };
|
||||||
},
|
},
|
||||||
htfDefaults: {
|
|
||||||
DST: 'edwards448_XOF:SHAKE256_ELL2_RO_',
|
|
||||||
p: Fp.ORDER,
|
|
||||||
m: 1,
|
|
||||||
k: 224,
|
|
||||||
expand: 'xof',
|
|
||||||
hash: shake256,
|
|
||||||
},
|
|
||||||
mapToCurve: (scalars: bigint[]) => map_to_curve_elligator2_edwards448(scalars[0]),
|
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const ed448 = twistedEdwards(ED448_DEF);
|
export const ed448 = twistedEdwards(ED448_DEF);
|
||||||
// NOTE: there is no ed448ctx, since ed448 supports ctx by default
|
// NOTE: there is no ed448ctx, since ed448 supports ctx by default
|
||||||
export const ed448ph = twistedEdwards({ ...ED448_DEF, preHash: shake256_64 });
|
export const ed448ph = twistedEdwards({ ...ED448_DEF, prehash: shake256_64 });
|
||||||
|
|
||||||
export const x448 = montgomery({
|
export const x448 = montgomery({
|
||||||
a24: BigInt(39081),
|
a: BigInt(156326),
|
||||||
montgomeryBits: 448,
|
montgomeryBits: 448,
|
||||||
nByteLength: 57,
|
nByteLength: 57,
|
||||||
P: ed448P,
|
P: ed448P,
|
||||||
Gu: '0500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
|
Gu: BigInt(5),
|
||||||
powPminus2: (x: bigint): bigint => {
|
powPminus2: (x: bigint): bigint => {
|
||||||
const P = ed448P;
|
const P = ed448P;
|
||||||
const Pminus3div4 = ed448_pow_Pminus3div4(x);
|
const Pminus3div4 = ed448_pow_Pminus3div4(x);
|
||||||
@@ -216,18 +135,109 @@ export const x448 = montgomery({
|
|||||||
return mod(Pminus3 * x, P); // Pminus3 * x = Pminus2
|
return mod(Pminus3 * x, P); // Pminus3 * x = Pminus2
|
||||||
},
|
},
|
||||||
adjustScalarBytes,
|
adjustScalarBytes,
|
||||||
// The 4-isogeny maps between the Montgomery curve and this Edwards
|
randomBytes,
|
||||||
// curve are:
|
|
||||||
// (u, v) = (y^2/x^2, (2 - x^2 - y^2)*y/x^3)
|
|
||||||
// (x, y) = (4*v*(u^2 - 1)/(u^4 - 2*u^2 + 4*v^2 + 1),
|
|
||||||
// -(u^5 - 2*u^3 - 4*u*v^2 + u)/
|
|
||||||
// (u^5 - 2*u^2*v^2 - 2*u^3 - 2*v^2 + u))
|
|
||||||
// xyToU: (p: PointType) => {
|
|
||||||
// const P = ed448P;
|
|
||||||
// const { x, y } = p;
|
|
||||||
// if (x === _0n) throw new Error(`Point with x=0 doesn't have mapping`);
|
|
||||||
// const invX = invert(x * x, P); // x^2
|
|
||||||
// const u = mod(y * y * invX, P); // (y^2/x^2)
|
|
||||||
// return numberToBytesLE(u, 56);
|
|
||||||
// },
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts edwards448 public key to x448 public key. Uses formula:
|
||||||
|
* * `(u, v) = ((y-1)/(y+1), sqrt(156324)*u/x)`
|
||||||
|
* * `(x, y) = (sqrt(156324)*u/v, (1+u)/(1-u))`
|
||||||
|
* @example
|
||||||
|
* const aPub = ed448.getPublicKey(utils.randomPrivateKey());
|
||||||
|
* x448.getSharedSecret(edwardsToMontgomery(aPub), edwardsToMontgomery(someonesPub))
|
||||||
|
*/
|
||||||
|
export function edwardsToMontgomery(edwardsPub: string | Uint8Array): Uint8Array {
|
||||||
|
const { y } = ed448.ExtendedPoint.fromHex(edwardsPub);
|
||||||
|
const _1n = BigInt(1);
|
||||||
|
return Fp.toBytes(Fp.create((y - _1n) * Fp.inv(y + _1n)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash To Curve Elligator2 Map
|
||||||
|
const ELL2_C1 = (Fp.ORDER - BigInt(3)) / BigInt(4); // 1. c1 = (q - 3) / 4 # Integer arithmetic
|
||||||
|
const ELL2_J = BigInt(156326);
|
||||||
|
function map_to_curve_elligator2_curve448(u: bigint) {
|
||||||
|
let tv1 = Fp.sqr(u); // 1. tv1 = u^2
|
||||||
|
let e1 = Fp.eql(tv1, Fp.ONE); // 2. e1 = tv1 == 1
|
||||||
|
tv1 = Fp.cmov(tv1, Fp.ZERO, e1); // 3. tv1 = CMOV(tv1, 0, e1) # If Z * u^2 == -1, set tv1 = 0
|
||||||
|
let xd = Fp.sub(Fp.ONE, tv1); // 4. xd = 1 - tv1
|
||||||
|
let x1n = Fp.neg(ELL2_J); // 5. x1n = -J
|
||||||
|
let tv2 = Fp.sqr(xd); // 6. tv2 = xd^2
|
||||||
|
let gxd = Fp.mul(tv2, xd); // 7. gxd = tv2 * xd # gxd = xd^3
|
||||||
|
let gx1 = Fp.mul(tv1, Fp.neg(ELL2_J)); // 8. gx1 = -J * tv1 # x1n + J * xd
|
||||||
|
gx1 = Fp.mul(gx1, x1n); // 9. gx1 = gx1 * x1n # x1n^2 + J * x1n * xd
|
||||||
|
gx1 = Fp.add(gx1, tv2); // 10. gx1 = gx1 + tv2 # x1n^2 + J * x1n * xd + xd^2
|
||||||
|
gx1 = Fp.mul(gx1, x1n); // 11. gx1 = gx1 * x1n # x1n^3 + J * x1n^2 * xd + x1n * xd^2
|
||||||
|
let tv3 = Fp.sqr(gxd); // 12. tv3 = gxd^2
|
||||||
|
tv2 = Fp.mul(gx1, gxd); // 13. tv2 = gx1 * gxd # gx1 * gxd
|
||||||
|
tv3 = Fp.mul(tv3, tv2); // 14. tv3 = tv3 * tv2 # gx1 * gxd^3
|
||||||
|
let y1 = Fp.pow(tv3, ELL2_C1); // 15. y1 = tv3^c1 # (gx1 * gxd^3)^((p - 3) / 4)
|
||||||
|
y1 = Fp.mul(y1, tv2); // 16. y1 = y1 * tv2 # gx1 * gxd * (gx1 * gxd^3)^((p - 3) / 4)
|
||||||
|
let x2n = Fp.mul(x1n, Fp.neg(tv1)); // 17. x2n = -tv1 * x1n # x2 = x2n / xd = -1 * u^2 * x1n / xd
|
||||||
|
let y2 = Fp.mul(y1, u); // 18. y2 = y1 * u
|
||||||
|
y2 = Fp.cmov(y2, Fp.ZERO, e1); // 19. y2 = CMOV(y2, 0, e1)
|
||||||
|
tv2 = Fp.sqr(y1); // 20. tv2 = y1^2
|
||||||
|
tv2 = Fp.mul(tv2, gxd); // 21. tv2 = tv2 * gxd
|
||||||
|
let e2 = Fp.eql(tv2, gx1); // 22. e2 = tv2 == gx1
|
||||||
|
let xn = Fp.cmov(x2n, x1n, e2); // 23. xn = CMOV(x2n, x1n, e2) # If e2, x = x1, else x = x2
|
||||||
|
let y = Fp.cmov(y2, y1, e2); // 24. y = CMOV(y2, y1, e2) # If e2, y = y1, else y = y2
|
||||||
|
let e3 = Fp.isOdd(y); // 25. e3 = sgn0(y) == 1 # Fix sign of y
|
||||||
|
y = Fp.cmov(y, Fp.neg(y), e2 !== e3); // 26. y = CMOV(y, -y, e2 XOR e3)
|
||||||
|
return { xn, xd, yn: y, yd: Fp.ONE }; // 27. return (xn, xd, y, 1)
|
||||||
|
}
|
||||||
|
function map_to_curve_elligator2_edwards448(u: bigint) {
|
||||||
|
let { xn, xd, yn, yd } = map_to_curve_elligator2_curve448(u); // 1. (xn, xd, yn, yd) = map_to_curve_elligator2_curve448(u)
|
||||||
|
let xn2 = Fp.sqr(xn); // 2. xn2 = xn^2
|
||||||
|
let xd2 = Fp.sqr(xd); // 3. xd2 = xd^2
|
||||||
|
let xd4 = Fp.sqr(xd2); // 4. xd4 = xd2^2
|
||||||
|
let yn2 = Fp.sqr(yn); // 5. yn2 = yn^2
|
||||||
|
let yd2 = Fp.sqr(yd); // 6. yd2 = yd^2
|
||||||
|
let xEn = Fp.sub(xn2, xd2); // 7. xEn = xn2 - xd2
|
||||||
|
let tv2 = Fp.sub(xEn, xd2); // 8. tv2 = xEn - xd2
|
||||||
|
xEn = Fp.mul(xEn, xd2); // 9. xEn = xEn * xd2
|
||||||
|
xEn = Fp.mul(xEn, yd); // 10. xEn = xEn * yd
|
||||||
|
xEn = Fp.mul(xEn, yn); // 11. xEn = xEn * yn
|
||||||
|
xEn = Fp.mul(xEn, _4n); // 12. xEn = xEn * 4
|
||||||
|
tv2 = Fp.mul(tv2, xn2); // 13. tv2 = tv2 * xn2
|
||||||
|
tv2 = Fp.mul(tv2, yd2); // 14. tv2 = tv2 * yd2
|
||||||
|
let tv3 = Fp.mul(yn2, _4n); // 15. tv3 = 4 * yn2
|
||||||
|
let tv1 = Fp.add(tv3, yd2); // 16. tv1 = tv3 + yd2
|
||||||
|
tv1 = Fp.mul(tv1, xd4); // 17. tv1 = tv1 * xd4
|
||||||
|
let xEd = Fp.add(tv1, tv2); // 18. xEd = tv1 + tv2
|
||||||
|
tv2 = Fp.mul(tv2, xn); // 19. tv2 = tv2 * xn
|
||||||
|
let tv4 = Fp.mul(xn, xd4); // 20. tv4 = xn * xd4
|
||||||
|
let yEn = Fp.sub(tv3, yd2); // 21. yEn = tv3 - yd2
|
||||||
|
yEn = Fp.mul(yEn, tv4); // 22. yEn = yEn * tv4
|
||||||
|
yEn = Fp.sub(yEn, tv2); // 23. yEn = yEn - tv2
|
||||||
|
tv1 = Fp.add(xn2, xd2); // 24. tv1 = xn2 + xd2
|
||||||
|
tv1 = Fp.mul(tv1, xd2); // 25. tv1 = tv1 * xd2
|
||||||
|
tv1 = Fp.mul(tv1, xd); // 26. tv1 = tv1 * xd
|
||||||
|
tv1 = Fp.mul(tv1, yn2); // 27. tv1 = tv1 * yn2
|
||||||
|
tv1 = Fp.mul(tv1, BigInt(-2)); // 28. tv1 = -2 * tv1
|
||||||
|
let yEd = Fp.add(tv2, tv1); // 29. yEd = tv2 + tv1
|
||||||
|
tv4 = Fp.mul(tv4, yd2); // 30. tv4 = tv4 * yd2
|
||||||
|
yEd = Fp.add(yEd, tv4); // 31. yEd = yEd + tv4
|
||||||
|
tv1 = Fp.mul(xEd, yEd); // 32. tv1 = xEd * yEd
|
||||||
|
let e = Fp.eql(tv1, Fp.ZERO); // 33. e = tv1 == 0
|
||||||
|
xEn = Fp.cmov(xEn, Fp.ZERO, e); // 34. xEn = CMOV(xEn, 0, e)
|
||||||
|
xEd = Fp.cmov(xEd, Fp.ONE, e); // 35. xEd = CMOV(xEd, 1, e)
|
||||||
|
yEn = Fp.cmov(yEn, Fp.ONE, e); // 36. yEn = CMOV(yEn, 1, e)
|
||||||
|
yEd = Fp.cmov(yEd, Fp.ONE, e); // 37. yEd = CMOV(yEd, 1, e)
|
||||||
|
|
||||||
|
const inv = Fp.invertBatch([xEd, yEd]); // batch division
|
||||||
|
return { x: Fp.mul(xEn, inv[0]), y: Fp.mul(yEn, inv[1]) }; // 38. return (xEn, xEd, yEn, yEd)
|
||||||
|
}
|
||||||
|
|
||||||
|
const { hashToCurve, encodeToCurve } = htf.createHasher(
|
||||||
|
ed448.ExtendedPoint,
|
||||||
|
(scalars: bigint[]) => map_to_curve_elligator2_edwards448(scalars[0]),
|
||||||
|
{
|
||||||
|
DST: 'edwards448_XOF:SHAKE256_ELL2_RO_',
|
||||||
|
encodeDST: 'edwards448_XOF:SHAKE256_ELL2_NU_',
|
||||||
|
p: Fp.ORDER,
|
||||||
|
m: 1,
|
||||||
|
k: 224,
|
||||||
|
expand: 'xof',
|
||||||
|
hash: shake256,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
export { hashToCurve, encodeToCurve };
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
import { sha256 } from '@noble/hashes/sha256';
|
import { sha512 } from '@noble/hashes/sha512';
|
||||||
import { concatBytes, randomBytes, utf8ToBytes } from '@noble/hashes/utils';
|
import { concatBytes, randomBytes, utf8ToBytes } from '@noble/hashes/utils';
|
||||||
import { twistedEdwards } from './abstract/edwards.js';
|
import { twistedEdwards } from './abstract/edwards.js';
|
||||||
import { blake2s } from '@noble/hashes/blake2s';
|
import { blake2s } from '@noble/hashes/blake2s';
|
||||||
import { Fp } from './abstract/modular.js';
|
import { Field } from './abstract/modular.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* jubjub Twisted Edwards curve.
|
* jubjub Twisted Edwards curve.
|
||||||
* https://neuromancer.sk/std/other/JubJub
|
* https://neuromancer.sk/std/other/JubJub
|
||||||
|
* jubjub does not use EdDSA, so `hash`/sha512 params are passed because interface expects them.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const jubjub = twistedEdwards({
|
export const jubjub = twistedEdwards({
|
||||||
@@ -15,16 +16,16 @@ export const jubjub = twistedEdwards({
|
|||||||
a: BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000'),
|
a: BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000'),
|
||||||
d: BigInt('0x2a9318e74bfa2b48f5fd9207e6bd7fd4292d7f6d37579d2601065fd6d6343eb1'),
|
d: BigInt('0x2a9318e74bfa2b48f5fd9207e6bd7fd4292d7f6d37579d2601065fd6d6343eb1'),
|
||||||
// Finite field 𝔽p over which we'll do calculations
|
// Finite field 𝔽p over which we'll do calculations
|
||||||
Fp: Fp(BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001')),
|
// Same value as bls12-381 Fr (not Fp)
|
||||||
// Subgroup order: how many points ed25519 has
|
Fp: Field(BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001')),
|
||||||
// 2n ** 252n + 27742317777372353535851937790883648493n;
|
// Subgroup order: how many points curve has
|
||||||
n: BigInt('0xe7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7'),
|
n: BigInt('0xe7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7'),
|
||||||
// Cofactor
|
// Cofactor
|
||||||
h: BigInt(8),
|
h: BigInt(8),
|
||||||
// Base point (x, y) aka generator point
|
// Base point (x, y) aka generator point
|
||||||
Gx: BigInt('0x11dafe5d23e1218086a365b99fbf3d3be72f6afd7d1f72623e6b071492d1122b'),
|
Gx: BigInt('0x11dafe5d23e1218086a365b99fbf3d3be72f6afd7d1f72623e6b071492d1122b'),
|
||||||
Gy: BigInt('0x1d523cf1ddab1a1793132e78c866c0c33e26ba5cc220fed7cc3f870e59d292aa'),
|
Gy: BigInt('0x1d523cf1ddab1a1793132e78c866c0c33e26ba5cc220fed7cc3f870e59d292aa'),
|
||||||
hash: sha256,
|
hash: sha512,
|
||||||
randomBytes,
|
randomBytes,
|
||||||
} as const);
|
} as const);
|
||||||
|
|
||||||
@@ -38,7 +39,7 @@ export function groupHash(tag: Uint8Array, personalization: Uint8Array) {
|
|||||||
h.update(GH_FIRST_BLOCK);
|
h.update(GH_FIRST_BLOCK);
|
||||||
h.update(tag);
|
h.update(tag);
|
||||||
// NOTE: returns ExtendedPoint, in case it will be multiplied later
|
// NOTE: returns ExtendedPoint, in case it will be multiplied later
|
||||||
let p = jubjub.ExtendedPoint.fromAffine(jubjub.Point.fromHex(h.digest()));
|
let p = jubjub.ExtendedPoint.fromHex(h.digest());
|
||||||
// NOTE: cannot replace with isSmallOrder, returns Point*8
|
// NOTE: cannot replace with isSmallOrder, returns Point*8
|
||||||
p = p.multiply(jubjub.CURVE.h);
|
p = p.multiply(jubjub.CURVE.h);
|
||||||
if (p.equals(jubjub.ExtendedPoint.ZERO)) throw new Error('Point has small order');
|
if (p.equals(jubjub.ExtendedPoint.ZERO)) throw new Error('Point has small order');
|
||||||
|
|||||||
25
src/p192.ts
25
src/p192.ts
@@ -1,25 +0,0 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
||||||
import { createCurve } from './_shortw_utils.js';
|
|
||||||
import { sha256 } from '@noble/hashes/sha256';
|
|
||||||
import { Fp } from './abstract/modular.js';
|
|
||||||
|
|
||||||
// NIST secp192r1 aka P192
|
|
||||||
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/secg/secp192r1
|
|
||||||
export const P192 = createCurve(
|
|
||||||
{
|
|
||||||
// Params: a, b
|
|
||||||
a: BigInt('0xfffffffffffffffffffffffffffffffefffffffffffffffc'),
|
|
||||||
b: BigInt('0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1'),
|
|
||||||
// Field over which we'll do calculations; 2n ** 192n - 2n ** 64n - 1n
|
|
||||||
Fp: Fp(BigInt('0xfffffffffffffffffffffffffffffffeffffffffffffffff')),
|
|
||||||
// Curve order, total count of valid points in the field.
|
|
||||||
n: BigInt('0xffffffffffffffffffffffff99def836146bc9b1b4d22831'),
|
|
||||||
// Base point (x, y) aka generator point
|
|
||||||
Gx: BigInt('0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012'),
|
|
||||||
Gy: BigInt('0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811'),
|
|
||||||
h: BigInt(1),
|
|
||||||
lowS: false,
|
|
||||||
} as const,
|
|
||||||
sha256
|
|
||||||
);
|
|
||||||
export const secp192r1 = P192;
|
|
||||||
25
src/p224.ts
25
src/p224.ts
@@ -1,25 +0,0 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
||||||
import { createCurve } from './_shortw_utils.js';
|
|
||||||
import { sha224 } from '@noble/hashes/sha256';
|
|
||||||
import { Fp } from './abstract/modular.js';
|
|
||||||
|
|
||||||
// NIST secp224r1 aka P224
|
|
||||||
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/nist/P-224
|
|
||||||
export const P224 = createCurve(
|
|
||||||
{
|
|
||||||
// Params: a, b
|
|
||||||
a: BigInt('0xfffffffffffffffffffffffffffffffefffffffffffffffffffffffe'),
|
|
||||||
b: BigInt('0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4'),
|
|
||||||
// Field over which we'll do calculations; 2n**224n - 2n**96n + 1n
|
|
||||||
Fp: Fp(BigInt('0xffffffffffffffffffffffffffffffff000000000000000000000001')),
|
|
||||||
// Curve order, total count of valid points in the field
|
|
||||||
n: BigInt('0xffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d'),
|
|
||||||
// Base point (x, y) aka generator point
|
|
||||||
Gx: BigInt('0xb70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21'),
|
|
||||||
Gy: BigInt('0xbd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34'),
|
|
||||||
h: BigInt(1),
|
|
||||||
lowS: false,
|
|
||||||
} as const,
|
|
||||||
sha224
|
|
||||||
);
|
|
||||||
export const secp224r1 = P224;
|
|
||||||
57
src/p256.ts
57
src/p256.ts
@@ -1,13 +1,13 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
import { createCurve } from './_shortw_utils.js';
|
import { createCurve } from './_shortw_utils.js';
|
||||||
import { sha256 } from '@noble/hashes/sha256';
|
import { sha256 } from '@noble/hashes/sha256';
|
||||||
import { Fp as Field } from './abstract/modular.js';
|
import { Field } from './abstract/modular.js';
|
||||||
import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
|
import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
|
||||||
|
import * as htf from './abstract/hash-to-curve.js';
|
||||||
|
|
||||||
// NIST secp256r1 aka P256
|
// NIST secp256r1 aka p256
|
||||||
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/nist/P-256
|
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/nist/P-256
|
||||||
|
|
||||||
// Field over which we'll do calculations; 2n**224n * (2n**32n-1n) + 2n**192n + 2n**96n-1n
|
|
||||||
const Fp = Field(BigInt('0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff'));
|
const Fp = Field(BigInt('0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff'));
|
||||||
const CURVE_A = Fp.create(BigInt('-3'));
|
const CURVE_A = Fp.create(BigInt('-3'));
|
||||||
const CURVE_B = BigInt('0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b');
|
const CURVE_B = BigInt('0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b');
|
||||||
@@ -18,29 +18,32 @@ const mapSWU = mapToCurveSimpleSWU(Fp, {
|
|||||||
Z: Fp.create(BigInt('-10')),
|
Z: Fp.create(BigInt('-10')),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const P256 = createCurve(
|
// prettier-ignore
|
||||||
|
export const p256 = createCurve({
|
||||||
|
a: CURVE_A, // Equation params: a, b
|
||||||
|
b: CURVE_B,
|
||||||
|
Fp, // Field: 2n**224n * (2n**32n-1n) + 2n**192n + 2n**96n-1n
|
||||||
|
// Curve order, total count of valid points in the field
|
||||||
|
n: BigInt('0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551'),
|
||||||
|
// Base (generator) point (x, y)
|
||||||
|
Gx: BigInt('0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296'),
|
||||||
|
Gy: BigInt('0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5'),
|
||||||
|
h: BigInt(1),
|
||||||
|
lowS: false,
|
||||||
|
} as const, sha256);
|
||||||
|
export const secp256r1 = p256;
|
||||||
|
|
||||||
|
const { hashToCurve, encodeToCurve } = htf.createHasher(
|
||||||
|
secp256r1.ProjectivePoint,
|
||||||
|
(scalars: bigint[]) => mapSWU(scalars[0]),
|
||||||
{
|
{
|
||||||
// Params: a, b
|
DST: 'P256_XMD:SHA-256_SSWU_RO_',
|
||||||
a: CURVE_A,
|
encodeDST: 'P256_XMD:SHA-256_SSWU_NU_',
|
||||||
b: CURVE_B,
|
p: Fp.ORDER,
|
||||||
Fp,
|
m: 1,
|
||||||
// Curve order, total count of valid points in the field
|
k: 128,
|
||||||
n: BigInt('0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551'),
|
expand: 'xmd',
|
||||||
// Base point (x, y) aka generator point
|
hash: sha256,
|
||||||
Gx: BigInt('0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296'),
|
}
|
||||||
Gy: BigInt('0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5'),
|
|
||||||
h: BigInt(1),
|
|
||||||
lowS: false,
|
|
||||||
mapToCurve: (scalars: bigint[]) => mapSWU(scalars[0]),
|
|
||||||
htfDefaults: {
|
|
||||||
DST: 'P256_XMD:SHA-256_SSWU_RO_',
|
|
||||||
p: Fp.ORDER,
|
|
||||||
m: 1,
|
|
||||||
k: 128,
|
|
||||||
expand: 'xmd',
|
|
||||||
hash: sha256,
|
|
||||||
},
|
|
||||||
} as const,
|
|
||||||
sha256
|
|
||||||
);
|
);
|
||||||
export const secp256r1 = P256;
|
export { hashToCurve, encodeToCurve };
|
||||||
|
|||||||
61
src/p384.ts
61
src/p384.ts
@@ -1,13 +1,14 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
import { createCurve } from './_shortw_utils.js';
|
import { createCurve } from './_shortw_utils.js';
|
||||||
import { sha384 } from '@noble/hashes/sha512';
|
import { sha384 } from '@noble/hashes/sha512';
|
||||||
import { Fp as Field } from './abstract/modular.js';
|
import { Field } from './abstract/modular.js';
|
||||||
import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
|
import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
|
||||||
|
import * as htf from './abstract/hash-to-curve.js';
|
||||||
|
|
||||||
// NIST secp384r1 aka P384
|
// NIST secp384r1 aka p384
|
||||||
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/nist/P-384
|
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/nist/P-384
|
||||||
|
|
||||||
// Field over which we'll do calculations. 2n**384n - 2n**128n - 2n**96n + 2n**32n - 1n
|
// Field over which we'll do calculations.
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
const P = BigInt('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff');
|
const P = BigInt('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff');
|
||||||
const Fp = Field(P);
|
const Fp = Field(P);
|
||||||
@@ -15,36 +16,38 @@ const CURVE_A = Fp.create(BigInt('-3'));
|
|||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
const CURVE_B = BigInt('0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef');
|
const CURVE_B = BigInt('0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef');
|
||||||
|
|
||||||
|
// prettier-ignore
|
||||||
|
export const p384 = createCurve({
|
||||||
|
a: CURVE_A, // Equation params: a, b
|
||||||
|
b: CURVE_B,
|
||||||
|
Fp, // Field: 2n**384n - 2n**128n - 2n**96n + 2n**32n - 1n
|
||||||
|
// Curve order, total count of valid points in the field.
|
||||||
|
n: BigInt('0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973'),
|
||||||
|
// Base (generator) point (x, y)
|
||||||
|
Gx: BigInt('0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7'),
|
||||||
|
Gy: BigInt('0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f'),
|
||||||
|
h: BigInt(1),
|
||||||
|
lowS: false,
|
||||||
|
} as const, sha384);
|
||||||
|
export const secp384r1 = p384;
|
||||||
|
|
||||||
const mapSWU = mapToCurveSimpleSWU(Fp, {
|
const mapSWU = mapToCurveSimpleSWU(Fp, {
|
||||||
A: CURVE_A,
|
A: CURVE_A,
|
||||||
B: CURVE_B,
|
B: CURVE_B,
|
||||||
Z: Fp.create(BigInt('-12')),
|
Z: Fp.create(BigInt('-12')),
|
||||||
});
|
});
|
||||||
|
|
||||||
// prettier-ignore
|
const { hashToCurve, encodeToCurve } = htf.createHasher(
|
||||||
export const P384 = createCurve({
|
secp384r1.ProjectivePoint,
|
||||||
// Params: a, b
|
(scalars: bigint[]) => mapSWU(scalars[0]),
|
||||||
a: CURVE_A,
|
{
|
||||||
b: CURVE_B,
|
DST: 'P384_XMD:SHA-384_SSWU_RO_',
|
||||||
// Field over which we'll do calculations. 2n**384n - 2n**128n - 2n**96n + 2n**32n - 1n
|
encodeDST: 'P384_XMD:SHA-384_SSWU_NU_',
|
||||||
Fp,
|
p: Fp.ORDER,
|
||||||
// Curve order, total count of valid points in the field.
|
m: 1,
|
||||||
n: BigInt('0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973'),
|
k: 192,
|
||||||
// Base point (x, y) aka generator point
|
expand: 'xmd',
|
||||||
Gx: BigInt('0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7'),
|
hash: sha384,
|
||||||
Gy: BigInt('0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f'),
|
}
|
||||||
h: BigInt(1),
|
|
||||||
lowS: false,
|
|
||||||
mapToCurve: (scalars: bigint[]) => mapSWU(scalars[0]),
|
|
||||||
htfDefaults: {
|
|
||||||
DST: 'P384_XMD:SHA-384_SSWU_RO_',
|
|
||||||
p: Fp.ORDER,
|
|
||||||
m: 1,
|
|
||||||
k: 192,
|
|
||||||
expand: 'xmd',
|
|
||||||
hash: sha384,
|
|
||||||
},
|
|
||||||
} as const,
|
|
||||||
sha384
|
|
||||||
);
|
);
|
||||||
export const secp384r1 = P384;
|
export { hashToCurve, encodeToCurve };
|
||||||
|
|||||||
84
src/p521.ts
84
src/p521.ts
@@ -1,61 +1,69 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
import { createCurve } from './_shortw_utils.js';
|
import { createCurve } from './_shortw_utils.js';
|
||||||
import { sha512 } from '@noble/hashes/sha512';
|
import { sha512 } from '@noble/hashes/sha512';
|
||||||
import { bytesToHex, PrivKey } from './abstract/utils.js';
|
import { Field } from './abstract/modular.js';
|
||||||
import { Fp as Field } from './abstract/modular.js';
|
|
||||||
import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
|
import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
|
||||||
|
import * as htf from './abstract/hash-to-curve.js';
|
||||||
|
|
||||||
// NIST secp521r1 aka P521
|
// NIST secp521r1 aka p521
|
||||||
// Note that it's 521, which differs from 512 of its hash function.
|
// Note that it's 521, which differs from 512 of its hash function.
|
||||||
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/nist/P-521
|
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/nist/P-521
|
||||||
|
|
||||||
// Field over which we'll do calculations; 2n**521n - 1n
|
// Field over which we'll do calculations.
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
const P = BigInt('0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
|
const P = BigInt('0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
|
||||||
const Fp = Field(P);
|
const Fp = Field(P);
|
||||||
|
|
||||||
const CURVE_A = Fp.create(BigInt('-3'));
|
const CURVE = {
|
||||||
|
a: Fp.create(BigInt('-3')),
|
||||||
|
b: BigInt(
|
||||||
|
'0x0051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00'
|
||||||
|
),
|
||||||
|
Fp,
|
||||||
|
n: BigInt(
|
||||||
|
'0x01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409'
|
||||||
|
),
|
||||||
|
Gx: BigInt(
|
||||||
|
'0x00c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66'
|
||||||
|
),
|
||||||
|
Gy: BigInt(
|
||||||
|
'0x011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650'
|
||||||
|
),
|
||||||
|
h: BigInt(1),
|
||||||
|
};
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
const CURVE_B = BigInt('0x0051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00');
|
export const p521 = createCurve({
|
||||||
|
a: CURVE.a, // Equation params: a, b
|
||||||
|
b: CURVE.b,
|
||||||
|
Fp, // Field: 2n**521n - 1n
|
||||||
|
// Curve order, total count of valid points in the field
|
||||||
|
n: CURVE.n,
|
||||||
|
Gx: CURVE.Gx, // Base point (x, y) aka generator point
|
||||||
|
Gy: CURVE.Gy,
|
||||||
|
h: CURVE.h,
|
||||||
|
lowS: false,
|
||||||
|
allowedPrivateKeyLengths: [130, 131, 132] // P521 keys are variable-length. Normalize to 132b
|
||||||
|
} as const, sha512);
|
||||||
|
export const secp521r1 = p521;
|
||||||
|
|
||||||
const mapSWU = mapToCurveSimpleSWU(Fp, {
|
const mapSWU = mapToCurveSimpleSWU(Fp, {
|
||||||
A: CURVE_A,
|
A: CURVE.a,
|
||||||
B: CURVE_B,
|
B: CURVE.b,
|
||||||
Z: Fp.create(BigInt('-4')),
|
Z: Fp.create(BigInt('-4')),
|
||||||
});
|
});
|
||||||
|
|
||||||
// prettier-ignore
|
const { hashToCurve, encodeToCurve } = htf.createHasher(
|
||||||
export const P521 = createCurve({
|
secp521r1.ProjectivePoint,
|
||||||
// Params: a, b
|
(scalars: bigint[]) => mapSWU(scalars[0]),
|
||||||
a: CURVE_A,
|
{
|
||||||
b: CURVE_B,
|
|
||||||
Fp,
|
|
||||||
// Curve order, total count of valid points in the field
|
|
||||||
n: BigInt('0x01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409'),
|
|
||||||
// Base point (x, y) aka generator point
|
|
||||||
Gx: BigInt('0x00c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66'),
|
|
||||||
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: {
|
|
||||||
DST: 'P521_XMD:SHA-512_SSWU_RO_',
|
DST: 'P521_XMD:SHA-512_SSWU_RO_',
|
||||||
|
encodeDST: 'P521_XMD:SHA-512_SSWU_NU_',
|
||||||
p: Fp.ORDER,
|
p: Fp.ORDER,
|
||||||
m: 1,
|
m: 1,
|
||||||
k: 256,
|
k: 256,
|
||||||
expand: 'xmd',
|
expand: 'xmd',
|
||||||
hash: sha512,
|
hash: sha512,
|
||||||
},
|
}
|
||||||
} as const, sha512);
|
);
|
||||||
export const secp521r1 = P521;
|
export { hashToCurve, encodeToCurve };
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export const q = BigInt('0x40000000000000000000000000000000224698fc0994a8dd8c46e
|
|||||||
export const pallas = weierstrass({
|
export const pallas = weierstrass({
|
||||||
a: BigInt(0),
|
a: BigInt(0),
|
||||||
b: BigInt(5),
|
b: BigInt(5),
|
||||||
Fp: mod.Fp(p),
|
Fp: mod.Field(p),
|
||||||
n: q,
|
n: q,
|
||||||
Gx: mod.mod(BigInt(-1), p),
|
Gx: mod.mod(BigInt(-1), p),
|
||||||
Gy: BigInt(2),
|
Gy: BigInt(2),
|
||||||
@@ -22,7 +22,7 @@ export const pallas = weierstrass({
|
|||||||
export const vesta = weierstrass({
|
export const vesta = weierstrass({
|
||||||
a: BigInt(0),
|
a: BigInt(0),
|
||||||
b: BigInt(5),
|
b: BigInt(5),
|
||||||
Fp: mod.Fp(q),
|
Fp: mod.Field(q),
|
||||||
n: p,
|
n: p,
|
||||||
Gx: mod.mod(BigInt(-1), q),
|
Gx: mod.mod(BigInt(-1), q),
|
||||||
Gy: BigInt(2),
|
Gy: BigInt(2),
|
||||||
|
|||||||
438
src/secp256k1.ts
438
src/secp256k1.ts
@@ -1,28 +1,12 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
import { sha256 } from '@noble/hashes/sha256';
|
import { sha256 } from '@noble/hashes/sha256';
|
||||||
import { Fp as Field, mod, pow2 } from './abstract/modular.js';
|
|
||||||
import { createCurve } from './_shortw_utils.js';
|
|
||||||
import { PointType, mapToCurveSimpleSWU } from './abstract/weierstrass.js';
|
|
||||||
import {
|
|
||||||
ensureBytes,
|
|
||||||
concatBytes,
|
|
||||||
Hex,
|
|
||||||
hexToBytes,
|
|
||||||
bytesToNumberBE,
|
|
||||||
PrivKey,
|
|
||||||
} from './abstract/utils.js';
|
|
||||||
import { randomBytes } from '@noble/hashes/utils';
|
import { randomBytes } from '@noble/hashes/utils';
|
||||||
import { isogenyMap } from './abstract/hash-to-curve.js';
|
import { Field, mod, pow2 } from './abstract/modular.js';
|
||||||
|
import { ProjPointType as PointType, mapToCurveSimpleSWU } from './abstract/weierstrass.js';
|
||||||
/**
|
import type { Hex, PrivKey } from './abstract/utils.js';
|
||||||
* secp256k1 belongs to Koblitz curves: it has
|
import { bytesToNumberBE, concatBytes, ensureBytes, numberToBytesBE } from './abstract/utils.js';
|
||||||
* efficiently computable Frobenius endomorphism.
|
import * as htf from './abstract/hash-to-curve.js';
|
||||||
* Endomorphism improves efficiency:
|
import { createCurve } from './_shortw_utils.js';
|
||||||
* Uses 2x less RAM, speeds up precomputation by 2x and ECDH / sign key recovery by 20%.
|
|
||||||
* Should always be used for Projective's double-and-add multiplication.
|
|
||||||
* For affines cached multiplication, it trades off 1/2 init time & 1/3 ram for 20% perf hit.
|
|
||||||
* https://gist.github.com/paulmillr/eb670806793e84df628a7c434a873066
|
|
||||||
*/
|
|
||||||
|
|
||||||
const secp256k1P = BigInt('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f');
|
const secp256k1P = BigInt('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f');
|
||||||
const secp256k1N = BigInt('0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141');
|
const secp256k1N = BigInt('0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141');
|
||||||
@@ -31,10 +15,7 @@ const _2n = BigInt(2);
|
|||||||
const divNearest = (a: bigint, b: bigint) => (a + b / _2n) / b;
|
const divNearest = (a: bigint, b: bigint) => (a + b / _2n) / b;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows to compute square root √y 2x faster.
|
* √n = n^((p+1)/4) for fields p = 3 mod 4. We unwrap the loop and multiply bit-by-bit.
|
||||||
* To calculate √y, we need to exponentiate it to a very big number:
|
|
||||||
* `y² = x³ + ax + b; y = y² ^ (p+1)/4`
|
|
||||||
* We are unwrapping the loop and multiplying it bit-by-bit.
|
|
||||||
* (P+1n/4n).toString(2) would produce bits [223x 1, 0, 22x 1, 4x 0, 11, 00]
|
* (P+1n/4n).toString(2) would produce bits [223x 1, 0, 22x 1, 4x 0, 11, 00]
|
||||||
*/
|
*/
|
||||||
function sqrtMod(y: bigint): bigint {
|
function sqrtMod(y: bigint): bigint {
|
||||||
@@ -57,14 +38,183 @@ function sqrtMod(y: bigint): bigint {
|
|||||||
const t1 = (pow2(b223, _23n, P) * b22) % P;
|
const t1 = (pow2(b223, _23n, P) * b22) % P;
|
||||||
const t2 = (pow2(t1, _6n, P) * b2) % P;
|
const t2 = (pow2(t1, _6n, P) * b2) % P;
|
||||||
const root = pow2(t2, _2n, P);
|
const root = pow2(t2, _2n, P);
|
||||||
if (!Fp.equals(Fp.square(root), y)) throw new Error('Cannot find square root');
|
if (!Fp.eql(Fp.sqr(root), y)) throw new Error('Cannot find square root');
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Fp = Field(secp256k1P, undefined, undefined, { sqrt: sqrtMod });
|
const Fp = Field(secp256k1P, undefined, undefined, { sqrt: sqrtMod });
|
||||||
type Fp = bigint;
|
|
||||||
|
|
||||||
const isoMap = isogenyMap(
|
export const secp256k1 = createCurve(
|
||||||
|
{
|
||||||
|
a: BigInt(0), // equation params: a, b
|
||||||
|
b: BigInt(7), // Seem to be rigid: bitcointalk.org/index.php?topic=289795.msg3183975#msg3183975
|
||||||
|
Fp, // Field's prime: 2n**256n - 2n**32n - 2n**9n - 2n**8n - 2n**7n - 2n**6n - 2n**4n - 1n
|
||||||
|
n: secp256k1N, // Curve order, total count of valid points in the field
|
||||||
|
// Base point (x, y) aka generator point
|
||||||
|
Gx: BigInt('55066263022277343669578718895168534326250603453777594175500187360389116729240'),
|
||||||
|
Gy: BigInt('32670510020758816978083085130507043184471273380659243275938904335757337482424'),
|
||||||
|
h: BigInt(1), // Cofactor
|
||||||
|
lowS: true, // Allow only low-S signatures by default in sign() and verify()
|
||||||
|
/**
|
||||||
|
* secp256k1 belongs to Koblitz curves: it has efficiently computable endomorphism.
|
||||||
|
* Endomorphism uses 2x less RAM, speeds up precomputation by 2x and ECDH / key recovery by 20%.
|
||||||
|
* For precomputed wNAF it trades off 1/2 init time & 1/3 ram for 20% perf hit.
|
||||||
|
* Explanation: https://gist.github.com/paulmillr/eb670806793e84df628a7c434a873066
|
||||||
|
*/
|
||||||
|
endo: {
|
||||||
|
beta: BigInt('0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee'),
|
||||||
|
splitScalar: (k: bigint) => {
|
||||||
|
const n = secp256k1N;
|
||||||
|
const a1 = BigInt('0x3086d221a7d46bcde86c90e49284eb15');
|
||||||
|
const b1 = -_1n * BigInt('0xe4437ed6010e88286f547fa90abfe4c3');
|
||||||
|
const a2 = BigInt('0x114ca50f7a8e2f3f657c1108d9d44cfd8');
|
||||||
|
const b2 = a1;
|
||||||
|
const POW_2_128 = BigInt('0x100000000000000000000000000000000'); // (2n**128n).toString(16)
|
||||||
|
|
||||||
|
const c1 = divNearest(b2 * k, n);
|
||||||
|
const c2 = divNearest(-b1 * k, n);
|
||||||
|
let k1 = mod(k - c1 * a1 - c2 * a2, n);
|
||||||
|
let k2 = mod(-c1 * b1 - c2 * b2, n);
|
||||||
|
const k1neg = k1 > POW_2_128;
|
||||||
|
const k2neg = k2 > POW_2_128;
|
||||||
|
if (k1neg) k1 = n - k1;
|
||||||
|
if (k2neg) k2 = n - k2;
|
||||||
|
if (k1 > POW_2_128 || k2 > POW_2_128) {
|
||||||
|
throw new Error('splitScalar: Endomorphism failed, k=' + k);
|
||||||
|
}
|
||||||
|
return { k1neg, k1, k2neg, k2 };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
sha256
|
||||||
|
);
|
||||||
|
|
||||||
|
// Schnorr signatures are superior to ECDSA from above. Below is Schnorr-specific BIP0340 code.
|
||||||
|
// https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki
|
||||||
|
const _0n = BigInt(0);
|
||||||
|
const fe = (x: bigint) => typeof x === 'bigint' && _0n < x && x < secp256k1P;
|
||||||
|
const ge = (x: bigint) => typeof x === 'bigint' && _0n < x && x < secp256k1N;
|
||||||
|
/** An object mapping tags to their tagged hash prefix of [SHA256(tag) | SHA256(tag)] */
|
||||||
|
const TAGGED_HASH_PREFIXES: { [tag: string]: Uint8Array } = {};
|
||||||
|
function taggedHash(tag: string, ...messages: Uint8Array[]): Uint8Array {
|
||||||
|
let tagP = TAGGED_HASH_PREFIXES[tag];
|
||||||
|
if (tagP === undefined) {
|
||||||
|
const tagH = sha256(Uint8Array.from(tag, (c) => c.charCodeAt(0)));
|
||||||
|
tagP = concatBytes(tagH, tagH);
|
||||||
|
TAGGED_HASH_PREFIXES[tag] = tagP;
|
||||||
|
}
|
||||||
|
return sha256(concatBytes(tagP, ...messages));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ECDSA compact points are 33-byte. Schnorr is 32: we strip first byte 0x02 or 0x03
|
||||||
|
const pointToBytes = (point: PointType<bigint>) => point.toRawBytes(true).slice(1);
|
||||||
|
const numTo32b = (n: bigint) => numberToBytesBE(n, 32);
|
||||||
|
const modP = (x: bigint) => mod(x, secp256k1P);
|
||||||
|
const modN = (x: bigint) => mod(x, secp256k1N);
|
||||||
|
const Point = secp256k1.ProjectivePoint;
|
||||||
|
const GmulAdd = (Q: PointType<bigint>, a: bigint, b: bigint) =>
|
||||||
|
Point.BASE.multiplyAndAddUnsafe(Q, a, b);
|
||||||
|
|
||||||
|
// Calculate point, scalar and bytes
|
||||||
|
function schnorrGetExtPubKey(priv: PrivKey) {
|
||||||
|
let d_ = secp256k1.utils.normPrivateKeyToScalar(priv); // same method executed in fromPrivateKey
|
||||||
|
let p = Point.fromPrivateKey(d_); // P = d'⋅G; 0 < d' < n check is done inside
|
||||||
|
const scalar = p.hasEvenY() ? d_ : modN(-d_);
|
||||||
|
return { scalar: scalar, bytes: pointToBytes(p) };
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* lift_x from BIP340. Convert 32-byte x coordinate to elliptic curve point.
|
||||||
|
* @returns valid point checked for being on-curve
|
||||||
|
*/
|
||||||
|
function lift_x(x: bigint): PointType<bigint> {
|
||||||
|
if (!fe(x)) throw new Error('bad x: need 0 < x < p'); // Fail if x ≥ p.
|
||||||
|
const xx = modP(x * x);
|
||||||
|
const c = modP(xx * x + BigInt(7)); // Let c = x³ + 7 mod p.
|
||||||
|
let y = sqrtMod(c); // Let y = c^(p+1)/4 mod p.
|
||||||
|
if (y % _2n !== _0n) y = modP(-y); // Return the unique point P such that x(P) = x and
|
||||||
|
const p = new Point(x, y, _1n); // y(P) = y if y mod 2 = 0 or y(P) = p-y otherwise.
|
||||||
|
p.assertValidity();
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Create tagged hash, convert it to bigint, reduce modulo-n.
|
||||||
|
*/
|
||||||
|
function challenge(...args: Uint8Array[]): bigint {
|
||||||
|
return modN(bytesToNumberBE(taggedHash('BIP0340/challenge', ...args)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schnorr public key is just `x` coordinate of Point as per BIP340.
|
||||||
|
*/
|
||||||
|
function schnorrGetPublicKey(privateKey: Hex): Uint8Array {
|
||||||
|
return schnorrGetExtPubKey(privateKey).bytes; // d'=int(sk). Fail if d'=0 or d'≥n. Ret bytes(d'⋅G)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates Schnorr signature as per BIP340. Verifies itself before returning anything.
|
||||||
|
* auxRand is optional and is not the sole source of k generation: bad CSPRNG won't be dangerous.
|
||||||
|
*/
|
||||||
|
function schnorrSign(
|
||||||
|
message: Hex,
|
||||||
|
privateKey: PrivKey,
|
||||||
|
auxRand: Hex = randomBytes(32)
|
||||||
|
): Uint8Array {
|
||||||
|
const m = ensureBytes('message', message);
|
||||||
|
const { bytes: px, scalar: d } = schnorrGetExtPubKey(privateKey); // checks for isWithinCurveOrder
|
||||||
|
const a = ensureBytes('auxRand', auxRand, 32); // Auxiliary random data a: a 32-byte array
|
||||||
|
const t = numTo32b(d ^ bytesToNumberBE(taggedHash('BIP0340/aux', a))); // Let t be the byte-wise xor of bytes(d) and hash/aux(a)
|
||||||
|
const rand = taggedHash('BIP0340/nonce', t, px, m); // Let rand = hash/nonce(t || bytes(P) || m)
|
||||||
|
const k_ = modN(bytesToNumberBE(rand)); // Let k' = int(rand) mod n
|
||||||
|
if (k_ === _0n) throw new Error('sign failed: k is zero'); // Fail if k' = 0.
|
||||||
|
const { bytes: rx, scalar: k } = schnorrGetExtPubKey(k_); // Let R = k'⋅G.
|
||||||
|
const e = challenge(rx, px, m); // Let e = int(hash/challenge(bytes(R) || bytes(P) || m)) mod n.
|
||||||
|
const sig = new Uint8Array(64); // Let sig = bytes(R) || bytes((k + ed) mod n).
|
||||||
|
sig.set(rx, 0);
|
||||||
|
sig.set(numTo32b(modN(k + e * d)), 32);
|
||||||
|
// If Verify(bytes(P), m, sig) (see below) returns failure, abort
|
||||||
|
if (!schnorrVerify(sig, m, px)) throw new Error('sign: Invalid signature produced');
|
||||||
|
return sig;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies Schnorr signature.
|
||||||
|
* Will swallow errors & return false except for initial type validation of arguments.
|
||||||
|
*/
|
||||||
|
function schnorrVerify(signature: Hex, message: Hex, publicKey: Hex): boolean {
|
||||||
|
const sig = ensureBytes('signature', signature, 64);
|
||||||
|
const m = ensureBytes('message', message);
|
||||||
|
const pub = ensureBytes('publicKey', publicKey, 32);
|
||||||
|
try {
|
||||||
|
const P = lift_x(bytesToNumberBE(pub)); // P = lift_x(int(pk)); fail if that fails
|
||||||
|
const r = bytesToNumberBE(sig.subarray(0, 32)); // Let r = int(sig[0:32]); fail if r ≥ p.
|
||||||
|
if (!fe(r)) return false;
|
||||||
|
const s = bytesToNumberBE(sig.subarray(32, 64)); // Let s = int(sig[32:64]); fail if s ≥ n.
|
||||||
|
if (!ge(s)) return false;
|
||||||
|
const e = challenge(numTo32b(r), pointToBytes(P), m); // int(challenge(bytes(r)||bytes(P)||m))%n
|
||||||
|
const R = GmulAdd(P, s, modN(-e)); // R = s⋅G - e⋅P
|
||||||
|
if (!R || !R.hasEvenY() || R.toAffine().x !== r) return false; // -eP == (n-e)P
|
||||||
|
return true; // Fail if is_infinite(R) / not has_even_y(R) / x(R) ≠ r.
|
||||||
|
} catch (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const schnorr = {
|
||||||
|
getPublicKey: schnorrGetPublicKey,
|
||||||
|
sign: schnorrSign,
|
||||||
|
verify: schnorrVerify,
|
||||||
|
utils: {
|
||||||
|
randomPrivateKey: secp256k1.utils.randomPrivateKey,
|
||||||
|
lift_x,
|
||||||
|
pointToBytes,
|
||||||
|
numberToBytesBE,
|
||||||
|
bytesToNumberBE,
|
||||||
|
taggedHash,
|
||||||
|
mod,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const isoMap = htf.isogenyMap(
|
||||||
Fp,
|
Fp,
|
||||||
[
|
[
|
||||||
// xNum
|
// xNum
|
||||||
@@ -94,226 +244,26 @@ const isoMap = isogenyMap(
|
|||||||
'0x6484aa716545ca2cf3a70c3fa8fe337e0a3d21162f0d6299a7bf8192bfd2a76f',
|
'0x6484aa716545ca2cf3a70c3fa8fe337e0a3d21162f0d6299a7bf8192bfd2a76f',
|
||||||
'0x0000000000000000000000000000000000000000000000000000000000000001', // LAST 1
|
'0x0000000000000000000000000000000000000000000000000000000000000001', // LAST 1
|
||||||
],
|
],
|
||||||
].map((i) => i.map((j) => BigInt(j))) as [Fp[], Fp[], Fp[], Fp[]]
|
].map((i) => i.map((j) => BigInt(j))) as [bigint[], bigint[], bigint[], bigint[]]
|
||||||
);
|
);
|
||||||
|
|
||||||
const mapSWU = mapToCurveSimpleSWU(Fp, {
|
const mapSWU = mapToCurveSimpleSWU(Fp, {
|
||||||
A: BigInt('0x3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533'),
|
A: BigInt('0x3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533'),
|
||||||
B: BigInt('1771'),
|
B: BigInt('1771'),
|
||||||
Z: Fp.create(BigInt('-11')),
|
Z: Fp.create(BigInt('-11')),
|
||||||
});
|
});
|
||||||
|
export const { hashToCurve, encodeToCurve } = htf.createHasher(
|
||||||
export const secp256k1 = createCurve(
|
secp256k1.ProjectivePoint,
|
||||||
{
|
(scalars: bigint[]) => {
|
||||||
// Params: a, b
|
const { x, y } = mapSWU(Fp.create(scalars[0]));
|
||||||
// Seem to be rigid https://bitcointalk.org/index.php?topic=289795.msg3183975#msg3183975
|
return isoMap(x, y);
|
||||||
a: BigInt(0),
|
|
||||||
b: BigInt(7),
|
|
||||||
// Field over which we'll do calculations;
|
|
||||||
// 2n**256n - 2n**32n - 2n**9n - 2n**8n - 2n**7n - 2n**6n - 2n**4n - 1n
|
|
||||||
Fp,
|
|
||||||
// Curve order, total count of valid points in the field
|
|
||||||
n: secp256k1N,
|
|
||||||
// Base point (x, y) aka generator point
|
|
||||||
Gx: BigInt('55066263022277343669578718895168534326250603453777594175500187360389116729240'),
|
|
||||||
Gy: BigInt('32670510020758816978083085130507043184471273380659243275938904335757337482424'),
|
|
||||||
h: BigInt(1),
|
|
||||||
// Alllow only low-S signatures by default in sign() and verify()
|
|
||||||
lowS: true,
|
|
||||||
endo: {
|
|
||||||
// Params taken from https://gist.github.com/paulmillr/eb670806793e84df628a7c434a873066
|
|
||||||
beta: BigInt('0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee'),
|
|
||||||
splitScalar: (k: bigint) => {
|
|
||||||
const n = secp256k1N;
|
|
||||||
const a1 = BigInt('0x3086d221a7d46bcde86c90e49284eb15');
|
|
||||||
const b1 = -_1n * BigInt('0xe4437ed6010e88286f547fa90abfe4c3');
|
|
||||||
const a2 = BigInt('0x114ca50f7a8e2f3f657c1108d9d44cfd8');
|
|
||||||
const b2 = a1;
|
|
||||||
const POW_2_128 = BigInt('0x100000000000000000000000000000000');
|
|
||||||
|
|
||||||
const c1 = divNearest(b2 * k, n);
|
|
||||||
const c2 = divNearest(-b1 * k, n);
|
|
||||||
let k1 = mod(k - c1 * a1 - c2 * a2, n);
|
|
||||||
let k2 = mod(-c1 * b1 - c2 * b2, n);
|
|
||||||
const k1neg = k1 > POW_2_128;
|
|
||||||
const k2neg = k2 > POW_2_128;
|
|
||||||
if (k1neg) k1 = n - k1;
|
|
||||||
if (k2neg) k2 = n - k2;
|
|
||||||
if (k1 > POW_2_128 || k2 > POW_2_128) {
|
|
||||||
throw new Error('splitScalar: Endomorphism failed, k=' + k);
|
|
||||||
}
|
|
||||||
return { k1neg, k1, k2neg, k2 };
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mapToCurve: (scalars: bigint[]) => {
|
|
||||||
const { x, y } = mapSWU(Fp.create(scalars[0]));
|
|
||||||
return isoMap(x, y);
|
|
||||||
},
|
|
||||||
htfDefaults: {
|
|
||||||
DST: 'secp256k1_XMD:SHA-256_SSWU_RO_',
|
|
||||||
p: Fp.ORDER,
|
|
||||||
m: 1,
|
|
||||||
k: 128,
|
|
||||||
expand: 'xmd',
|
|
||||||
hash: sha256,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
sha256
|
{
|
||||||
|
DST: 'secp256k1_XMD:SHA-256_SSWU_RO_',
|
||||||
|
encodeDST: 'secp256k1_XMD:SHA-256_SSWU_NU_',
|
||||||
|
p: Fp.ORDER,
|
||||||
|
m: 1,
|
||||||
|
k: 128,
|
||||||
|
expand: 'xmd',
|
||||||
|
hash: sha256,
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// Schnorr
|
|
||||||
const _0n = BigInt(0);
|
|
||||||
const numTo32b = secp256k1.utils._bigintToBytes;
|
|
||||||
const numTo32bStr = secp256k1.utils._bigintToString;
|
|
||||||
const normalizePrivateKey = secp256k1.utils._normalizePrivateKey;
|
|
||||||
|
|
||||||
// TODO: export?
|
|
||||||
function normalizePublicKey(publicKey: Hex | PointType<bigint>): PointType<bigint> {
|
|
||||||
if (publicKey instanceof secp256k1.Point) {
|
|
||||||
publicKey.assertValidity();
|
|
||||||
return publicKey;
|
|
||||||
} else {
|
|
||||||
const bytes = ensureBytes(publicKey);
|
|
||||||
// Schnorr is 32 bytes
|
|
||||||
if (bytes.length === 32) {
|
|
||||||
const x = bytesToNumberBE(bytes);
|
|
||||||
if (!isValidFieldElement(x)) throw new Error('Point is not on curve');
|
|
||||||
const y2 = secp256k1.utils._weierstrassEquation(x); // y² = x³ + ax + b
|
|
||||||
let y = sqrtMod(y2); // y = y² ^ (p+1)/4
|
|
||||||
const isYOdd = (y & _1n) === _1n;
|
|
||||||
// Schnorr
|
|
||||||
if (isYOdd) y = secp256k1.CURVE.Fp.negate(y);
|
|
||||||
const point = new secp256k1.Point(x, y);
|
|
||||||
point.assertValidity();
|
|
||||||
return point;
|
|
||||||
}
|
|
||||||
// Do we need that in schnorr at all?
|
|
||||||
return secp256k1.Point.fromHex(publicKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const isWithinCurveOrder = secp256k1.utils._isWithinCurveOrder;
|
|
||||||
const isValidFieldElement = secp256k1.utils._isValidFieldElement;
|
|
||||||
|
|
||||||
const TAGS = {
|
|
||||||
challenge: 'BIP0340/challenge',
|
|
||||||
aux: 'BIP0340/aux',
|
|
||||||
nonce: 'BIP0340/nonce',
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
/** An object mapping tags to their tagged hash prefix of [SHA256(tag) | SHA256(tag)] */
|
|
||||||
const TAGGED_HASH_PREFIXES: { [tag: string]: Uint8Array } = {};
|
|
||||||
export function taggedHash(tag: string, ...messages: Uint8Array[]): Uint8Array {
|
|
||||||
let tagP = TAGGED_HASH_PREFIXES[tag];
|
|
||||||
if (tagP === undefined) {
|
|
||||||
const tagH = sha256(Uint8Array.from(tag, (c) => c.charCodeAt(0)));
|
|
||||||
tagP = concatBytes(tagH, tagH);
|
|
||||||
TAGGED_HASH_PREFIXES[tag] = tagP;
|
|
||||||
}
|
|
||||||
return sha256(concatBytes(tagP, ...messages));
|
|
||||||
}
|
|
||||||
|
|
||||||
const toRawX = (point: PointType<bigint>) => point.toRawBytes(true).slice(1);
|
|
||||||
|
|
||||||
// Schnorr signatures are superior to ECDSA from above.
|
|
||||||
// Below is Schnorr-specific code as per BIP0340.
|
|
||||||
function schnorrChallengeFinalize(ch: Uint8Array): bigint {
|
|
||||||
return mod(bytesToNumberBE(ch), secp256k1.CURVE.n);
|
|
||||||
}
|
|
||||||
// Do we need this at all for Schnorr?
|
|
||||||
class SchnorrSignature {
|
|
||||||
constructor(readonly r: bigint, readonly s: bigint) {
|
|
||||||
this.assertValidity();
|
|
||||||
}
|
|
||||||
static fromHex(hex: Hex) {
|
|
||||||
const bytes = ensureBytes(hex);
|
|
||||||
if (bytes.length !== 64)
|
|
||||||
throw new TypeError(`SchnorrSignature.fromHex: expected 64 bytes, not ${bytes.length}`);
|
|
||||||
const r = bytesToNumberBE(bytes.subarray(0, 32));
|
|
||||||
const s = bytesToNumberBE(bytes.subarray(32, 64));
|
|
||||||
return new SchnorrSignature(r, s);
|
|
||||||
}
|
|
||||||
assertValidity() {
|
|
||||||
const { r, s } = this;
|
|
||||||
if (!isValidFieldElement(r) || !isWithinCurveOrder(s)) throw new Error('Invalid signature');
|
|
||||||
}
|
|
||||||
toHex(): string {
|
|
||||||
return numTo32bStr(this.r) + numTo32bStr(this.s);
|
|
||||||
}
|
|
||||||
toRawBytes(): Uint8Array {
|
|
||||||
return hexToBytes(this.toHex());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function schnorrGetScalar(priv: bigint) {
|
|
||||||
const point = secp256k1.Point.fromPrivateKey(priv);
|
|
||||||
const scalar = point.hasEvenY() ? priv : secp256k1.CURVE.n - priv;
|
|
||||||
return { point, scalar, x: toRawX(point) };
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Synchronously creates Schnorr signature. Improved security: verifies itself before
|
|
||||||
* producing an output.
|
|
||||||
* @param msg message (not message hash)
|
|
||||||
* @param privateKey private key
|
|
||||||
* @param auxRand random bytes that would be added to k. Bad RNG won't break it.
|
|
||||||
*/
|
|
||||||
function schnorrSign(
|
|
||||||
message: Hex,
|
|
||||||
privateKey: PrivKey,
|
|
||||||
auxRand: Hex = randomBytes(32)
|
|
||||||
): Uint8Array {
|
|
||||||
if (message == null) throw new TypeError(`sign: Expected valid message, not "${message}"`);
|
|
||||||
const m = ensureBytes(message);
|
|
||||||
// checks for isWithinCurveOrder
|
|
||||||
const { x: px, scalar: d } = schnorrGetScalar(normalizePrivateKey(privateKey));
|
|
||||||
const rand = ensureBytes(auxRand);
|
|
||||||
if (rand.length !== 32) throw new TypeError('sign: Expected 32 bytes of aux randomness');
|
|
||||||
const tag = taggedHash;
|
|
||||||
const t0h = tag(TAGS.aux, rand);
|
|
||||||
const t = numTo32b(d ^ bytesToNumberBE(t0h));
|
|
||||||
const k0h = tag(TAGS.nonce, t, px, m);
|
|
||||||
const k0 = mod(bytesToNumberBE(k0h), secp256k1.CURVE.n);
|
|
||||||
if (k0 === _0n) throw new Error('sign: Creation of signature failed. k is zero');
|
|
||||||
const { point: R, x: rx, scalar: k } = schnorrGetScalar(k0);
|
|
||||||
const e = schnorrChallengeFinalize(tag(TAGS.challenge, rx, px, m));
|
|
||||||
const sig = new SchnorrSignature(R.x, mod(k + e * d, secp256k1.CURVE.n)).toRawBytes();
|
|
||||||
if (!schnorrVerify(sig, m, px)) throw new Error('sign: Invalid signature produced');
|
|
||||||
return sig;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verifies Schnorr signature synchronously.
|
|
||||||
*/
|
|
||||||
function schnorrVerify(signature: Hex, message: Hex, publicKey: Hex): boolean {
|
|
||||||
try {
|
|
||||||
const raw = signature instanceof SchnorrSignature;
|
|
||||||
const sig: SchnorrSignature = raw ? signature : SchnorrSignature.fromHex(signature);
|
|
||||||
if (raw) sig.assertValidity(); // just in case
|
|
||||||
|
|
||||||
const { r, s } = sig;
|
|
||||||
const m = ensureBytes(message);
|
|
||||||
const P = normalizePublicKey(publicKey);
|
|
||||||
const e = schnorrChallengeFinalize(taggedHash(TAGS.challenge, numTo32b(r), toRawX(P), m));
|
|
||||||
// Finalize
|
|
||||||
// R = s⋅G - e⋅P
|
|
||||||
// -eP == (n-e)P
|
|
||||||
const R = secp256k1.Point.BASE.multiplyAndAddUnsafe(
|
|
||||||
P,
|
|
||||||
normalizePrivateKey(s),
|
|
||||||
mod(-e, secp256k1.CURVE.n)
|
|
||||||
);
|
|
||||||
if (!R || !R.hasEvenY() || R.x !== r) return false;
|
|
||||||
return true;
|
|
||||||
} catch (error) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const schnorr = {
|
|
||||||
Signature: SchnorrSignature,
|
|
||||||
// Schnorr's pubkey is just `x` of Point (BIP340)
|
|
||||||
getPublicKey: (privateKey: PrivKey): Uint8Array =>
|
|
||||||
toRawX(secp256k1.Point.fromPrivateKey(privateKey)),
|
|
||||||
sign: schnorrSign,
|
|
||||||
verify: schnorrVerify,
|
|
||||||
};
|
|
||||||
|
|||||||
264
src/stark.ts
264
src/stark.ts
@@ -1,264 +0,0 @@
|
|||||||
/*! 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 { 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(
|
|
||||||
'3618502788666131213697322783095070105526743751716087489154079457884512865583'
|
|
||||||
);
|
|
||||||
const nBitLength = 252;
|
|
||||||
export const starkCurve = weierstrass({
|
|
||||||
// Params: a, b
|
|
||||||
a: BigInt(1),
|
|
||||||
b: BigInt('3141592653589793238462643383279502884197169399375105820974944592307816406665'),
|
|
||||||
// Field over which we'll do calculations; 2n**251n + 17n * 2n**192n + 1n
|
|
||||||
// There is no efficient sqrt for field (P%4==1)
|
|
||||||
Fp: Fp(BigInt('0x800000000000011000000000000000000000000000000000000000000000001')),
|
|
||||||
// Curve order, total count of valid points in the field.
|
|
||||||
n: CURVE_N,
|
|
||||||
nBitLength: nBitLength, // len(bin(N).replace('0b',''))
|
|
||||||
// Base point (x, y) aka generator point
|
|
||||||
Gx: BigInt('874739451078007766457464989774322083649278607533249481151382481072868806602'),
|
|
||||||
Gy: BigInt('152666792071518830868575557812948353041420400780739481342941381225525861407'),
|
|
||||||
h: BigInt(1),
|
|
||||||
// Default options
|
|
||||||
lowS: false,
|
|
||||||
...getHash(sha256),
|
|
||||||
truncateHash: (hash: Uint8Array, truncateOnly = false): bigint => {
|
|
||||||
// TODO: cleanup, ugly code
|
|
||||||
// Fix truncation
|
|
||||||
if (!truncateOnly) {
|
|
||||||
let hashS = bytesToNumber0x(hash).toString(16);
|
|
||||||
if (hashS.length === 63) {
|
|
||||||
hashS += '0';
|
|
||||||
hash = hexToBytes0x(hashS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 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 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 normalizePrivateKey(privKey: Hex) {
|
|
||||||
return cutils.bytesToHex(ensureBytes0x(privKey)).padStart(32 * 2, '0');
|
|
||||||
}
|
|
||||||
function getPublicKey0x(privKey: Hex, isCompressed?: boolean) {
|
|
||||||
return starkCurve.getPublicKey(normalizePrivateKey(privKey), isCompressed);
|
|
||||||
}
|
|
||||||
function getSharedSecret0x(privKeyA: Hex, pubKeyB: Hex) {
|
|
||||||
return starkCurve.getSharedSecret(normalizePrivateKey(privKeyA), pubKeyB);
|
|
||||||
}
|
|
||||||
|
|
||||||
function sign0x(msgHash: Hex, privKey: Hex, opts?: any) {
|
|
||||||
if (typeof privKey === 'string') privKey = strip0x(privKey).padStart(64, '0');
|
|
||||||
return starkCurve.sign(ensureBytes0x(msgHash), normalizePrivateKey(privKey), opts);
|
|
||||||
}
|
|
||||||
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))));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function grindKey(seed: Hex) {
|
|
||||||
const _seed = ensureBytes0x(seed);
|
|
||||||
const sha256mask = 2n ** 256n;
|
|
||||||
const limit = sha256mask - starkCurve.utils.mod(sha256mask, starkCurve.CURVE.n);
|
|
||||||
for (let i = 0; ; i++) {
|
|
||||||
const key = hashKeyWithIndex(_seed, i);
|
|
||||||
// key should be in [0, limit)
|
|
||||||
if (key < limit) return starkCurve.utils.mod(key, starkCurve.CURVE.n).toString(16);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getStarkKey(privateKey: Hex) {
|
|
||||||
return bytesToHexEth(getPublicKey0x(privateKey, true).slice(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ethSigToPrivate(signature: string) {
|
|
||||||
signature = strip0x(signature.replace(/^0x/, ''));
|
|
||||||
if (signature.length !== 130) throw new Error('Wrong ethereum signature');
|
|
||||||
return grindKey(signature.substring(0, 64));
|
|
||||||
}
|
|
||||||
|
|
||||||
const MASK_31 = 2n ** 31n - 1n;
|
|
||||||
const int31 = (n: bigint) => Number(n & MASK_31);
|
|
||||||
export function getAccountPath(
|
|
||||||
layer: string,
|
|
||||||
application: string,
|
|
||||||
ethereumAddress: string,
|
|
||||||
index: number
|
|
||||||
) {
|
|
||||||
const layerNum = int31(bytesToNumber0x(sha256(layer)));
|
|
||||||
const applicationNum = int31(bytesToNumber0x(sha256(application)));
|
|
||||||
const eth = hexToNumber0x(ethereumAddress);
|
|
||||||
return `m/2645'/${layerNum}'/${applicationNum}'/${int31(eth)}'/${int31(eth >> 31n)}'/${index}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://docs.starkware.co/starkex/pedersen-hash-function.html
|
|
||||||
const PEDERSEN_POINTS_AFFINE = [
|
|
||||||
new Point(
|
|
||||||
2089986280348253421170679821480865132823066470938446095505822317253594081284n,
|
|
||||||
1713931329540660377023406109199410414810705867260802078187082345529207694986n
|
|
||||||
),
|
|
||||||
new Point(
|
|
||||||
996781205833008774514500082376783249102396023663454813447423147977397232763n,
|
|
||||||
1668503676786377725805489344771023921079126552019160156920634619255970485781n
|
|
||||||
),
|
|
||||||
new Point(
|
|
||||||
2251563274489750535117886426533222435294046428347329203627021249169616184184n,
|
|
||||||
1798716007562728905295480679789526322175868328062420237419143593021674992973n
|
|
||||||
),
|
|
||||||
new Point(
|
|
||||||
2138414695194151160943305727036575959195309218611738193261179310511854807447n,
|
|
||||||
113410276730064486255102093846540133784865286929052426931474106396135072156n
|
|
||||||
),
|
|
||||||
new Point(
|
|
||||||
2379962749567351885752724891227938183011949129833673362440656643086021394946n,
|
|
||||||
776496453633298175483985398648758586525933812536653089401905292063708816422n
|
|
||||||
),
|
|
||||||
];
|
|
||||||
// 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[] = [];
|
|
||||||
let p = p1;
|
|
||||||
for (let i = 0; i < 248; i++) {
|
|
||||||
out.push(p);
|
|
||||||
p = p.double();
|
|
||||||
}
|
|
||||||
// NOTE: we cannot use wNAF here, because last 4 bits will require full 248 bits multiplication
|
|
||||||
// We can add support for this to wNAF, but it will complicate wNAF.
|
|
||||||
p = p2;
|
|
||||||
for (let i = 0; i < 4; i++) {
|
|
||||||
out.push(p);
|
|
||||||
p = p.double();
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
const PEDERSEN_POINTS1 = pedersenPrecompute(PEDERSEN_POINTS[1], PEDERSEN_POINTS[2]);
|
|
||||||
const PEDERSEN_POINTS2 = pedersenPrecompute(PEDERSEN_POINTS[3], PEDERSEN_POINTS[4]);
|
|
||||||
|
|
||||||
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 (!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}`);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
function pedersenSingle(point: ProjectivePoint, value: PedersenArg, constants: ProjectivePoint[]) {
|
|
||||||
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 ((x & 1n) !== 0n) point = point.add(pt);
|
|
||||||
x >>= 1n;
|
|
||||||
}
|
|
||||||
return point;
|
|
||||||
}
|
|
||||||
|
|
||||||
// shift_point + x_low * P_0 + x_high * P1 + y_low * P2 + y_high * P3
|
|
||||||
export function pedersen(x: PedersenArg, y: PedersenArg) {
|
|
||||||
let point: ProjectivePoint = PEDERSEN_POINTS[0];
|
|
||||||
point = pedersenSingle(point, x, PEDERSEN_POINTS1);
|
|
||||||
point = pedersenSingle(point, y, PEDERSEN_POINTS2);
|
|
||||||
return bytesToHexEth(point.toAffine().toRawBytes(true).slice(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
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]));
|
|
||||||
return Array.from(data)
|
|
||||||
.reverse()
|
|
||||||
.reduce((acc, i) => fn(i, acc));
|
|
||||||
}
|
|
||||||
// Same as hashChain, but computes hash even for single element and order is not revesed
|
|
||||||
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;
|
|
||||||
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 '../esm/_shortw_utils.js';
|
||||||
|
import { sha224, sha256 } from '@noble/hashes/sha256';
|
||||||
|
import { Field as Fp } from '../esm/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;
|
||||||
103
test/_poseidon.helpers.js
Normal file
103
test/_poseidon.helpers.js
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
|
import { sha256 } from '@noble/hashes/sha256';
|
||||||
|
import { utf8ToBytes } from '@noble/hashes/utils';
|
||||||
|
import { Field as Fp, validateField } from '../esm/abstract/modular.js';
|
||||||
|
import { poseidon } from '../esm/abstract/poseidon.js';
|
||||||
|
import * as u from '../esm/abstract/utils.js';
|
||||||
|
|
||||||
|
// Poseidon hash https://docs.starkware.co/starkex/stark-curve.html
|
||||||
|
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, name, idx) {
|
||||||
|
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, name, m, attempt = 0) {
|
||||||
|
const x_values = [];
|
||||||
|
const y_values = [];
|
||||||
|
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 function poseidonBasic(opts, mds) {
|
||||||
|
validateField(opts.Fp);
|
||||||
|
if (!Number.isSafeInteger(opts.rate) || !Number.isSafeInteger(opts.capacity))
|
||||||
|
throw new Error(`Wrong poseidon opts: ${opts}`);
|
||||||
|
const m = opts.rate + opts.capacity;
|
||||||
|
const rounds = opts.roundsFull + opts.roundsPartial;
|
||||||
|
const roundConstants = [];
|
||||||
|
for (let i = 0; i < rounds; i++) {
|
||||||
|
const row = [];
|
||||||
|
for (let j = 0; j < m; j++) row.push(poseidonRoundConstant(opts.Fp, 'Hades', m * i + j));
|
||||||
|
roundConstants.push(row);
|
||||||
|
}
|
||||||
|
const res = poseidon({
|
||||||
|
...opts,
|
||||||
|
t: m,
|
||||||
|
sboxPower: 3,
|
||||||
|
reversePartialPowIdx: true, // Why?!
|
||||||
|
mds,
|
||||||
|
roundConstants,
|
||||||
|
});
|
||||||
|
res.m = m;
|
||||||
|
res.rate = opts.rate;
|
||||||
|
res.capacity = opts.capacity;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function poseidonCreate(opts, 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, y, fn = poseidonSmall) {
|
||||||
|
return fn([x, y, 2n])[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function poseidonHashFunc(x, y, fn = poseidonSmall) {
|
||||||
|
return u.numberToVarBytesBE(poseidonHash(u.bytesToNumberBE(x), u.bytesToNumberBE(y), fn));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function poseidonHashSingle(x, fn = poseidonSmall) {
|
||||||
|
return fn([x, 0n, 1n])[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function poseidonHashMany(values, fn = poseidonSmall) {
|
||||||
|
const { m, rate } = fn;
|
||||||
|
if (!Array.isArray(values)) throw new Error('bigint array expected in values');
|
||||||
|
const padded = Array.from(values); // copy
|
||||||
|
padded.push(1n);
|
||||||
|
while (padded.length % rate !== 0) padded.push(0n);
|
||||||
|
let state = new Array(m).fill(0n);
|
||||||
|
for (let i = 0; i < padded.length; i += rate) {
|
||||||
|
for (let j = 0; j < rate; j++) state[j] += padded[i + j];
|
||||||
|
state = fn(state);
|
||||||
|
}
|
||||||
|
return state[0];
|
||||||
|
}
|
||||||
1151
test/basic.test.js
1151
test/basic.test.js
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
304
test/ed25519-addons.test.js
Normal file
304
test/ed25519-addons.test.js
Normal file
@@ -0,0 +1,304 @@
|
|||||||
|
import { sha512 } from '@noble/hashes/sha512';
|
||||||
|
import { hexToBytes, bytesToHex as hex } from '@noble/hashes/utils';
|
||||||
|
import { deepStrictEqual, throws } from 'assert';
|
||||||
|
import { describe, should } from 'micro-should';
|
||||||
|
import { bytesToNumberLE, 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 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, { context: v.context })),
|
||||||
|
v.signature
|
||||||
|
);
|
||||||
|
deepStrictEqual(
|
||||||
|
ed25519ctx.verify(v.signature, v.message, v.publicKey, { context: 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
|
||||||
|
describe('RFC7748 X25519 ECDH', () => {
|
||||||
|
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 x${iters}`, () => {
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
|
||||||
|
should('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);
|
||||||
|
});
|
||||||
|
|
||||||
|
const group = x25519vectors.testGroups[0];
|
||||||
|
should('wycheproof', () => {
|
||||||
|
for (let i = 0; i < group.tests.length; i++) {
|
||||||
|
const v = group.tests[i];
|
||||||
|
const comment = `(${i}, ${v.result}) ${v.comment}`;
|
||||||
|
if (v.result === 'valid' || v.result === 'acceptable') {
|
||||||
|
try {
|
||||||
|
const shared = hex(x25519.scalarMult(v.private, v.public));
|
||||||
|
deepStrictEqual(shared, v.shared, comment);
|
||||||
|
} catch (e) {
|
||||||
|
// We are more strict
|
||||||
|
if (e.message.includes('Expected valid scalar')) return;
|
||||||
|
if (e.message.includes('Invalid private or public key received')) return;
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
} else if (v.result === 'invalid') {
|
||||||
|
let failed = false;
|
||||||
|
try {
|
||||||
|
x25519.scalarMult(v.private, v.public);
|
||||||
|
} catch (error) {
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
|
deepStrictEqual(failed, true, comment);
|
||||||
|
} else throw new Error('unknown test result');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function utf8ToBytes(str) {
|
||||||
|
if (typeof str !== 'string') {
|
||||||
|
throw new Error(`utf8ToBytes expected string, got ${typeof str}`);
|
||||||
|
}
|
||||||
|
return new TextEncoder().encode(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('ristretto255', () => {
|
||||||
|
should('follow the byte encodings of small multiples', () => {
|
||||||
|
const encodingsOfSmallMultiples = [
|
||||||
|
// This is the identity point
|
||||||
|
'0000000000000000000000000000000000000000000000000000000000000000',
|
||||||
|
// This is the basepoint
|
||||||
|
'e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76',
|
||||||
|
// These are small multiples of the basepoint
|
||||||
|
'6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919',
|
||||||
|
'94741f5d5d52755ece4f23f044ee27d5d1ea1e2bd196b462166b16152a9d0259',
|
||||||
|
'da80862773358b466ffadfe0b3293ab3d9fd53c5ea6c955358f568322daf6a57',
|
||||||
|
'e882b131016b52c1d3337080187cf768423efccbb517bb495ab812c4160ff44e',
|
||||||
|
'f64746d3c92b13050ed8d80236a7f0007c3b3f962f5ba793d19a601ebb1df403',
|
||||||
|
'44f53520926ec81fbd5a387845beb7df85a96a24ece18738bdcfa6a7822a176d',
|
||||||
|
'903293d8f2287ebe10e2374dc1a53e0bc887e592699f02d077d5263cdd55601c',
|
||||||
|
'02622ace8f7303a31cafc63f8fc48fdc16e1c8c8d234b2f0d6685282a9076031',
|
||||||
|
'20706fd788b2720a1ed2a5dad4952b01f413bcf0e7564de8cdc816689e2db95f',
|
||||||
|
'bce83f8ba5dd2fa572864c24ba1810f9522bc6004afe95877ac73241cafdab42',
|
||||||
|
'e4549ee16b9aa03099ca208c67adafcafa4c3f3e4e5303de6026e3ca8ff84460',
|
||||||
|
'aa52e000df2e16f55fb1032fc33bc42742dad6bd5a8fc0be0167436c5948501f',
|
||||||
|
'46376b80f409b29dc2b5f6f0c52591990896e5716f41477cd30085ab7f10301e',
|
||||||
|
'e0c418f7c8d9c4cdd7395b93ea124f3ad99021bb681dfc3302a9d99a2e53e64e',
|
||||||
|
];
|
||||||
|
let B = RistrettoPoint.BASE;
|
||||||
|
let P = RistrettoPoint.ZERO;
|
||||||
|
for (const encoded of encodingsOfSmallMultiples) {
|
||||||
|
deepStrictEqual(P.toHex(), encoded);
|
||||||
|
deepStrictEqual(RistrettoPoint.fromHex(encoded).toHex(), encoded);
|
||||||
|
P = P.add(B);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
should('not convert bad bytes encoding', () => {
|
||||||
|
const badEncodings = [
|
||||||
|
// These are all bad because they're non-canonical field encodings.
|
||||||
|
'00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
|
||||||
|
'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f',
|
||||||
|
'f3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f',
|
||||||
|
'edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f',
|
||||||
|
// These are all bad because they're negative field elements.
|
||||||
|
'0100000000000000000000000000000000000000000000000000000000000000',
|
||||||
|
'01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f',
|
||||||
|
'ed57ffd8c914fb201471d1c3d245ce3c746fcbe63a3679d51b6a516ebebe0e20',
|
||||||
|
'c34c4e1826e5d403b78e246e88aa051c36ccf0aafebffe137d148a2bf9104562',
|
||||||
|
'c940e5a4404157cfb1628b108db051a8d439e1a421394ec4ebccb9ec92a8ac78',
|
||||||
|
'47cfc5497c53dc8e61c91d17fd626ffb1c49e2bca94eed052281b510b1117a24',
|
||||||
|
'f1c6165d33367351b0da8f6e4511010c68174a03b6581212c71c0e1d026c3c72',
|
||||||
|
'87260f7a2f12495118360f02c26a470f450dadf34a413d21042b43b9d93e1309',
|
||||||
|
// These are all bad because they give a nonsquare x².
|
||||||
|
'26948d35ca62e643e26a83177332e6b6afeb9d08e4268b650f1f5bbd8d81d371',
|
||||||
|
'4eac077a713c57b4f4397629a4145982c661f48044dd3f96427d40b147d9742f',
|
||||||
|
'de6a7b00deadc788eb6b6c8d20c0ae96c2f2019078fa604fee5b87d6e989ad7b',
|
||||||
|
'bcab477be20861e01e4a0e295284146a510150d9817763caf1a6f4b422d67042',
|
||||||
|
'2a292df7e32cababbd9de088d1d1abec9fc0440f637ed2fba145094dc14bea08',
|
||||||
|
'f4a9e534fc0d216c44b218fa0c42d99635a0127ee2e53c712f70609649fdff22',
|
||||||
|
'8268436f8c4126196cf64b3c7ddbda90746a378625f9813dd9b8457077256731',
|
||||||
|
'2810e5cbc2cc4d4eece54f61c6f69758e289aa7ab440b3cbeaa21995c2f4232b',
|
||||||
|
// These are all bad because they give a negative xy value.
|
||||||
|
'3eb858e78f5a7254d8c9731174a94f76755fd3941c0ac93735c07ba14579630e',
|
||||||
|
'a45fdc55c76448c049a1ab33f17023edfb2be3581e9c7aade8a6125215e04220',
|
||||||
|
'd483fe813c6ba647ebbfd3ec41adca1c6130c2beeee9d9bf065c8d151c5f396e',
|
||||||
|
'8a2e1d30050198c65a54483123960ccc38aef6848e1ec8f5f780e8523769ba32',
|
||||||
|
'32888462f8b486c68ad7dd9610be5192bbeaf3b443951ac1a8118419d9fa097b',
|
||||||
|
'227142501b9d4355ccba290404bde41575b037693cef1f438c47f8fbf35d1165',
|
||||||
|
'5c37cc491da847cfeb9281d407efc41e15144c876e0170b499a96a22ed31e01e',
|
||||||
|
'445425117cb8c90edcbc7c1cc0e74f747f2c1efa5630a967c64f287792a48a4b',
|
||||||
|
// This is s = -1, which causes y = 0.
|
||||||
|
'ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f',
|
||||||
|
];
|
||||||
|
for (const badBytes of badEncodings) {
|
||||||
|
const b = hexToBytes(badBytes);
|
||||||
|
throws(() => RistrettoPoint.fromHex(b), badBytes);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
should('create right points from uniform hash', () => {
|
||||||
|
const labels = [
|
||||||
|
'Ristretto is traditionally a short shot of espresso coffee',
|
||||||
|
'made with the normal amount of ground coffee but extracted with',
|
||||||
|
'about half the amount of water in the same amount of time',
|
||||||
|
'by using a finer grind.',
|
||||||
|
'This produces a concentrated shot of coffee per volume.',
|
||||||
|
'Just pulling a normal shot short will produce a weaker shot',
|
||||||
|
'and is not a Ristretto as some believe.',
|
||||||
|
];
|
||||||
|
const encodedHashToPoints = [
|
||||||
|
'3066f82a1a747d45120d1740f14358531a8f04bbffe6a819f86dfe50f44a0a46',
|
||||||
|
'f26e5b6f7d362d2d2a94c5d0e7602cb4773c95a2e5c31a64f133189fa76ed61b',
|
||||||
|
'006ccd2a9e6867e6a2c5cea83d3302cc9de128dd2a9a57dd8ee7b9d7ffe02826',
|
||||||
|
'f8f0c87cf237953c5890aec3998169005dae3eca1fbb04548c635953c817f92a',
|
||||||
|
'ae81e7dedf20a497e10c304a765c1767a42d6e06029758d2d7e8ef7cc4c41179',
|
||||||
|
'e2705652ff9f5e44d3e841bf1c251cf7dddb77d140870d1ab2ed64f1a9ce8628',
|
||||||
|
'80bd07262511cdde4863f8a7434cef696750681cb9510eea557088f76d9e5065',
|
||||||
|
];
|
||||||
|
|
||||||
|
for (let i = 0; i < labels.length; i++) {
|
||||||
|
const hash = sha512(utf8ToBytes(labels[i]));
|
||||||
|
const point = RistrettoPoint.hashToCurve(hash);
|
||||||
|
deepStrictEqual(point.toHex(), encodedHashToPoints[i]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
should('have proper equality testing', () => {
|
||||||
|
const MAX_255B = BigInt('0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
|
||||||
|
const bytes255ToNumberLE = (bytes) =>
|
||||||
|
ed25519ctx.CURVE.Fp.create(bytesToNumberLE(bytes) & MAX_255B);
|
||||||
|
|
||||||
|
const priv = new Uint8Array([
|
||||||
|
198, 101, 65, 165, 93, 120, 37, 238, 16, 133, 10, 35, 253, 243, 161, 246, 229, 135, 12, 137,
|
||||||
|
202, 114, 222, 139, 146, 123, 4, 125, 152, 173, 1, 7,
|
||||||
|
]);
|
||||||
|
const pub = RistrettoPoint.BASE.multiply(bytes255ToNumberLE(priv));
|
||||||
|
deepStrictEqual(pub.equals(RistrettoPoint.ZERO), false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ESM is broken.
|
||||||
|
import url from 'url';
|
||||||
|
import { assert } from 'console';
|
||||||
|
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
|
||||||
|
should.run();
|
||||||
|
}
|
||||||
2
test/ed25519.helpers.js
Normal file
2
test/ed25519.helpers.js
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export { numberToBytesLE } from '../esm/abstract/utils.js';
|
||||||
|
export { ed25519, ED25519_TORSION_SUBGROUP } from '../esm/ed25519.js';
|
||||||
1037
test/ed25519.test.js
1037
test/ed25519.test.js
File diff suppressed because it is too large
Load Diff
1329
test/ed448.test.js
1329
test/ed448.test.js
File diff suppressed because it is too large
Load Diff
908
test/ed448/ed448_test_OLD.json
Normal file
908
test/ed448/ed448_test_OLD.json
Normal file
@@ -0,0 +1,908 @@
|
|||||||
|
{
|
||||||
|
"algorithm" : "EDDSA",
|
||||||
|
"generatorVersion" : "0.8r12",
|
||||||
|
"numberOfTests" : 86,
|
||||||
|
"header" : [
|
||||||
|
"Test vectors of type EddsaVerify are intended for testing",
|
||||||
|
"the verification of Eddsa signatures."
|
||||||
|
],
|
||||||
|
"notes" : {
|
||||||
|
"SignatureMalleability" : "EdDSA signatures are non-malleable, if implemented accordingly. Failing to check the range of S allows to modify signatures. See RFC 8032, Section 5.2.7 and Section 8.4."
|
||||||
|
},
|
||||||
|
"schema" : "eddsa_verify_schema.json",
|
||||||
|
"testGroups" : [
|
||||||
|
{
|
||||||
|
"jwk" : {
|
||||||
|
"crv" : "Ed448",
|
||||||
|
"d" : "iDAeB2UY01N_kwLuD1Ij5LY-HwFgB9PC69_sX3CZfoEZxrrQrnuAP0h5HKjsVJqiobhi96UVkLnV",
|
||||||
|
"kid" : "none",
|
||||||
|
"kty" : "OKP",
|
||||||
|
"x" : "QZYQpTSvEn9YOwSBjNt_D_MAsCXy4BaCvK4z_Wkc7gOVEd8M3caQ7peEJuizjlDOWvfc-6UPcEwA"
|
||||||
|
},
|
||||||
|
"key" : {
|
||||||
|
"curve" : "edwards448",
|
||||||
|
"keySize" : 448,
|
||||||
|
"pk" : "419610a534af127f583b04818cdb7f0ff300b025f2e01682bcae33fd691cee039511df0cddc690ee978426e8b38e50ce5af7dcfba50f704c00",
|
||||||
|
"sk" : "88301e076518d3537f9302ee0f5223e4b63e1f016007d3c2ebdfec5f70997e8119c6bad0ae7b803f48791ca8ec549aa2a1b862f7a51590b9d5",
|
||||||
|
"type" : "EDDSAKeyPair"
|
||||||
|
},
|
||||||
|
"keyDer" : "3043300506032b6571033a00419610a534af127f583b04818cdb7f0ff300b025f2e01682bcae33fd691cee039511df0cddc690ee978426e8b38e50ce5af7dcfba50f704c00",
|
||||||
|
"keyPem" : "-----BEGIN PUBLIC KEY-----\nMEMwBQYDK2VxAzoAQZYQpTSvEn9YOwSBjNt/D/MAsCXy4BaCvK4z/Wkc7gOVEd8M3caQ7peEJuizjlDOWvfc+6UPcEwA\n-----END PUBLIC KEY-----\n",
|
||||||
|
"type" : "EddsaVerify",
|
||||||
|
"tests" : [
|
||||||
|
{
|
||||||
|
"tcId" : 1,
|
||||||
|
"comment" : "",
|
||||||
|
"msg" : "",
|
||||||
|
"sig" : "cf7953007666e12f73af9ec92e3e018da5ee5a8d5b17f5100a354c58f1d5f4bb37ab835c52f72374c72d612689149cf6d36a70db6dc5a6c400b597348e0e31e51e65bb144e63c892a367b4c055c036aa6cd7e728cdd2a098963bda863903e6dd025b5a5d891209f4e28537694804e50b0800",
|
||||||
|
"result" : "valid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 2,
|
||||||
|
"comment" : "",
|
||||||
|
"msg" : "78",
|
||||||
|
"sig" : "c56e94d5c9ca860c244f33db556bf6b3cec38b024b77604a35d6a07211b1316b9a027133c374b86f72665cc45ce01583a2e0f2775c6172da801acef168717cab1196cddfb149359dfef589756257cc2d6b02fc516d8d41b4adaa3f11428f41410ef0dc3c1b008d3d052173d4389508ed0100",
|
||||||
|
"result" : "valid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 3,
|
||||||
|
"comment" : "",
|
||||||
|
"msg" : "54657374",
|
||||||
|
"sig" : "5d053ff5b71f6ec3284525d35d77933178c8e19879886d08eccc6c7d27e9e5b5e02537dbc4d4723506e8d171fc1733857573dd02d18f48f28031d67d699a188a9ca46b4eabe2107aef237ca609cb462e24c91d25d286402b6ef7862b78a386950246ff38d6d2f458136d12e3c97fdd982600",
|
||||||
|
"result" : "valid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 4,
|
||||||
|
"comment" : "",
|
||||||
|
"msg" : "48656c6c6f",
|
||||||
|
"sig" : "442e33780f199dd7bc71d1335f74df7f3a0ec789e21a175c1bffddb6e50091998d969ac8194b3acefb7702f6c222f84f7eeca3b80406f1fe80687915e7925bf52deb47b6b779e26d30eec7c5fef03580f280a089eefd0bacc9fbbb6a4d73a591d1671d192e6bbcfdb79ad3db5673a1263000",
|
||||||
|
"result" : "valid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 5,
|
||||||
|
"comment" : "",
|
||||||
|
"msg" : "313233343030",
|
||||||
|
"sig" : "5db94c53101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff28060a05236fc9c1682b0e55b60a082c9a57bffe61ef4dda5ce65df539805122b3a09a05976d41ad68ab52df85428152c57da93531e5d16920e00",
|
||||||
|
"result" : "valid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 6,
|
||||||
|
"comment" : "",
|
||||||
|
"msg" : "000000000000000000000000",
|
||||||
|
"sig" : "a8ca64d1ab00eae77fd2854d8422db3ae12fca91c14f274f30a44df98590786ec4cbb96a9564fc1b9b16c22d2bd00aa65f0876323729f5ac809fb0b89a4d3f27afbabb596851d835173d60ea34e0875359f3d6adb13cef1395b7eaa5f9147583ff38b4deb183062874915bf194ae61072300",
|
||||||
|
"result" : "valid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 7,
|
||||||
|
"comment" : "",
|
||||||
|
"msg" : "6161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161",
|
||||||
|
"sig" : "b205d3e24ccef64c1e86f15f48ddfa682453503489475188b04a8f55860b3c8a9c01e6de820bb7d9b15daff8de25a4a870e987157a115ec1802da0d0606da12842ea7eab658b5eea6dd1f3a641a5174425578003cd318b8d6b8dcb4de954b5078d1912c578ad8281515d6df3672b94173f00",
|
||||||
|
"result" : "valid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 8,
|
||||||
|
"comment" : "",
|
||||||
|
"msg" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f60",
|
||||||
|
"sig" : "3492ef66e5fdf1503e9e206c5c2f0d4b7891aad793575527d2251e0df1b97c2feac188bc382ce3c92c4bc36ba2695f32bedadd480eaa932300d0db1f9a9c60844d2ea5aea64933c7be46c4f9d21cb48b39eae23d08496de7ce9501197185cc5d4ff8aa4b018ce7ad321f6a7d778c4a070400",
|
||||||
|
"result" : "valid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 9,
|
||||||
|
"comment" : "",
|
||||||
|
"msg" : "ffffffffffffffffffffffffffffffff",
|
||||||
|
"sig" : "545e1905af1b5886552eaf78e17304c6f83fcfb3444df2d1ea056486db615e3bb29131bb0c1fd295364dc515dae581967148eb23c6c9012e806d3623baff00548c648e3cb3756aaaaf659f2fb7dd2e71c7611448593ca63f2a98913ab7f182e6820eaf1334e2745e0e7bc0dccab98de71600",
|
||||||
|
"result" : "valid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 10,
|
||||||
|
"comment" : "special values for r and s",
|
||||||
|
"msg" : "3f",
|
||||||
|
"sig" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 11,
|
||||||
|
"comment" : "special values for r and s",
|
||||||
|
"msg" : "3f",
|
||||||
|
"sig" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 12,
|
||||||
|
"comment" : "special values for r and s",
|
||||||
|
"msg" : "3f",
|
||||||
|
"sig" : "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f24458ab92c27823558fc58d72c26c219036d6ae49db4ec4e923ca7cffffffffffffffffffffffffffffffffffffffffffffffffffffff3f",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 13,
|
||||||
|
"comment" : "special values for r and s",
|
||||||
|
"msg" : "3f",
|
||||||
|
"sig" : "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f34458ab92c27823558fc58d72c26c219036d6ae49db4ec4e923ca7cffffffffffffffffffffffffffffffffffffffffffffffffffffff3f",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 14,
|
||||||
|
"comment" : "special values for r and s",
|
||||||
|
"msg" : "3f",
|
||||||
|
"sig" : "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 15,
|
||||||
|
"comment" : "special values for r and s",
|
||||||
|
"msg" : "3f",
|
||||||
|
"sig" : "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 16,
|
||||||
|
"comment" : "special values for r and s",
|
||||||
|
"msg" : "3f",
|
||||||
|
"sig" : "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 17,
|
||||||
|
"comment" : "special values for r and s",
|
||||||
|
"msg" : "3f",
|
||||||
|
"sig" : "0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f24458ab92c27823558fc58d72c26c219036d6ae49db4ec4e923ca7cffffffffffffffffffffffffffffffffffffffffffffffffffffff3f",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 18,
|
||||||
|
"comment" : "special values for r and s",
|
||||||
|
"msg" : "3f",
|
||||||
|
"sig" : "0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f34458ab92c27823558fc58d72c26c219036d6ae49db4ec4e923ca7cffffffffffffffffffffffffffffffffffffffffffffffffffffff3f",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 19,
|
||||||
|
"comment" : "special values for r and s",
|
||||||
|
"msg" : "3f",
|
||||||
|
"sig" : "0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 20,
|
||||||
|
"comment" : "special values for r and s",
|
||||||
|
"msg" : "3f",
|
||||||
|
"sig" : "f34458ab92c27823558fc58d72c26c219036d6ae49db4ec4e923ca7cffffffffffffffffffffffffffffffffffffffffffffffffffffff3f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 21,
|
||||||
|
"comment" : "special values for r and s",
|
||||||
|
"msg" : "3f",
|
||||||
|
"sig" : "f34458ab92c27823558fc58d72c26c219036d6ae49db4ec4e923ca7cffffffffffffffffffffffffffffffffffffffffffffffffffffff3f0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 22,
|
||||||
|
"comment" : "special values for r and s",
|
||||||
|
"msg" : "3f",
|
||||||
|
"sig" : "f34458ab92c27823558fc58d72c26c219036d6ae49db4ec4e923ca7cffffffffffffffffffffffffffffffffffffffffffffffffffffff3ff24458ab92c27823558fc58d72c26c219036d6ae49db4ec4e923ca7cffffffffffffffffffffffffffffffffffffffffffffffffffffff3f",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 23,
|
||||||
|
"comment" : "special values for r and s",
|
||||||
|
"msg" : "3f",
|
||||||
|
"sig" : "f34458ab92c27823558fc58d72c26c219036d6ae49db4ec4e923ca7cffffffffffffffffffffffffffffffffffffffffffffffffffffff3ff34458ab92c27823558fc58d72c26c219036d6ae49db4ec4e923ca7cffffffffffffffffffffffffffffffffffffffffffffffffffffff3f",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 24,
|
||||||
|
"comment" : "special values for r and s",
|
||||||
|
"msg" : "3f",
|
||||||
|
"sig" : "f34458ab92c27823558fc58d72c26c219036d6ae49db4ec4e923ca7cffffffffffffffffffffffffffffffffffffffffffffffffffffff3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 25,
|
||||||
|
"comment" : "special values for r and s",
|
||||||
|
"msg" : "3f",
|
||||||
|
"sig" : "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 26,
|
||||||
|
"comment" : "special values for r and s",
|
||||||
|
"msg" : "3f",
|
||||||
|
"sig" : "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffff0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 27,
|
||||||
|
"comment" : "special values for r and s",
|
||||||
|
"msg" : "3f",
|
||||||
|
"sig" : "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffffffffffffffffffffffffffffffffffffffffffffffffffff24458ab92c27823558fc58d72c26c219036d6ae49db4ec4e923ca7cffffffffffffffffffffffffffffffffffffffffffffffffffffff3f",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 28,
|
||||||
|
"comment" : "special values for r and s",
|
||||||
|
"msg" : "3f",
|
||||||
|
"sig" : "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffffffffffffffffffffffffffffffffffffffffffffffffffff34458ab92c27823558fc58d72c26c219036d6ae49db4ec4e923ca7cffffffffffffffffffffffffffffffffffffffffffffffffffffff3f",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 29,
|
||||||
|
"comment" : "special values for r and s",
|
||||||
|
"msg" : "3f",
|
||||||
|
"sig" : "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 30,
|
||||||
|
"comment" : "empty signature",
|
||||||
|
"msg" : "54657374",
|
||||||
|
"sig" : "",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 31,
|
||||||
|
"comment" : "s missing",
|
||||||
|
"msg" : "54657374",
|
||||||
|
"sig" : "5d053ff5b71f6ec3284525d35d77933178c8e19879886d08eccc6c7d27e9e5b5e02537dbc4d4723506e8d171fc1733857573dd02d18f48f280",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 32,
|
||||||
|
"comment" : "signature too short",
|
||||||
|
"msg" : "54657374",
|
||||||
|
"sig" : "5d053ff5b71f6ec3284525d35d77933178c8e19879886d08eccc6c7d27e9e5b5e02537dbc4d4723506e8d171fc1733857573dd02d18f48f28031d67d699a188a9ca46b4eabe2107aef237ca609cb462e24c91d25d286402b6ef7862b78a386950246ff38d6d2f458136d12e3c97fdd98",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 33,
|
||||||
|
"comment" : "signature too long",
|
||||||
|
"msg" : "54657374",
|
||||||
|
"sig" : "5d053ff5b71f6ec3284525d35d77933178c8e19879886d08eccc6c7d27e9e5b5e02537dbc4d4723506e8d171fc1733857573dd02d18f48f28031d67d699a188a9ca46b4eabe2107aef237ca609cb462e24c91d25d286402b6ef7862b78a386950246ff38d6d2f458136d12e3c97fdd9826002020",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 34,
|
||||||
|
"comment" : "include pk in signature",
|
||||||
|
"msg" : "54657374",
|
||||||
|
"sig" : "5d053ff5b71f6ec3284525d35d77933178c8e19879886d08eccc6c7d27e9e5b5e02537dbc4d4723506e8d171fc1733857573dd02d18f48f28031d67d699a188a9ca46b4eabe2107aef237ca609cb462e24c91d25d286402b6ef7862b78a386950246ff38d6d2f458136d12e3c97fdd982600419610a534af127f583b04818cdb7f0ff300b025f2e01682bcae33fd691cee039511df0cddc690ee978426e8b38e50ce5af7dcfba50f704c00",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 35,
|
||||||
|
"comment" : "prepending 0 byte to signature",
|
||||||
|
"msg" : "54657374",
|
||||||
|
"sig" : "005d053ff5b71f6ec3284525d35d77933178c8e19879886d08eccc6c7d27e9e5b5e02537dbc4d4723506e8d171fc1733857573dd02d18f48f28031d67d699a188a9ca46b4eabe2107aef237ca609cb462e24c91d25d286402b6ef7862b78a386950246ff38d6d2f458136d12e3c97fdd982600",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 36,
|
||||||
|
"comment" : "prepending 0 byte to s",
|
||||||
|
"msg" : "54657374",
|
||||||
|
"sig" : "5d053ff5b71f6ec3284525d35d77933178c8e19879886d08eccc6c7d27e9e5b5e02537dbc4d4723506e8d171fc1733857573dd02d18f48f2800031d67d699a188a9ca46b4eabe2107aef237ca609cb462e24c91d25d286402b6ef7862b78a386950246ff38d6d2f458136d12e3c97fdd982600",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 37,
|
||||||
|
"comment" : "appending 0 byte to signature",
|
||||||
|
"msg" : "54657374",
|
||||||
|
"sig" : "5d053ff5b71f6ec3284525d35d77933178c8e19879886d08eccc6c7d27e9e5b5e02537dbc4d4723506e8d171fc1733857573dd02d18f48f28031d67d699a188a9ca46b4eabe2107aef237ca609cb462e24c91d25d286402b6ef7862b78a386950246ff38d6d2f458136d12e3c97fdd98260000",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 38,
|
||||||
|
"comment" : "removing 0 byte from signature",
|
||||||
|
"msg" : "5465737430",
|
||||||
|
"sig" : "dbd6384516ab6b0eb2d609414564ec217383b66040dfb0676128251ae24c1d7c179c21a9ee307dc13f8fe6550bc40187f093da85617bcf5d009d3ee8b798ad978b6e683bc4e911940ea82ea0b7e95dc24fe0b29e44663211892c2aaa3451379d22c289b94378f11fb700f1689d4a00d73e",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 39,
|
||||||
|
"comment" : "removing 0 byte from signature",
|
||||||
|
"msg" : "546573743535",
|
||||||
|
"sig" : "ce2b2fff0bf445a36813cf2a76e0cc5619a4f16ee53f0fe3cd46fc0414db7248b32fbda54bbb37e708d6238076ea12bf850b964b044520bb80fbaf0e1d1ed3bcab261462df5e7f2de73ac9cbae26dfa29015039acf90575961fc9b91b9ca276dae7d5fa805bd202c5579a0f4c66e801400",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 40,
|
||||||
|
"comment" : "dropping byte from signature",
|
||||||
|
"msg" : "546573743633",
|
||||||
|
"sig" : "c283ed36d78c275a5d02f7939aed2c4ef68320ae1bf6fc25e834b758046a6d52a480216a942dfe771f3bd307f4ce7d3f446e0824961bd5de80cda42b5cc38e6ec3d53f386978b9877d3c98a28ac8fc66630ffd178933a18de1aee23cab5011c9ff4c9277311b4c6c33acb8e82b8c693c00",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 41,
|
||||||
|
"comment" : "removing leading 0 byte from signature",
|
||||||
|
"msg" : "54657374333631",
|
||||||
|
"sig" : "62e629bd2b8f595df401c362c766216d45de89fceecd99c69d323b5c53ad5ac3ea7224963feba2f2895551d94f548248ef8597d2a959f880d59934a5e8f07847834d66ba1a6b09de5dba692172b13f768f0c29e8196144c130d2353445d63cbd0b690794fdad30a48e8bb7cc2504f80700",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 42,
|
||||||
|
"comment" : "modified bit 0 in R",
|
||||||
|
"msg" : "313233343030",
|
||||||
|
"sig" : "5cb94c53101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff280afc33a525116cc12e0d1c3a1fde6de518a6544f360d0fe18d5be7770b057a2bf792db4b7648fa84a6eaecae909e33fa59c5dfe4804ba2623",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 43,
|
||||||
|
"comment" : "modified bit 1 in R",
|
||||||
|
"msg" : "313233343030",
|
||||||
|
"sig" : "5fb94c53101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff280f91386c3e9dd9e7c9af7ca6bbef8b7a44ae3d68eeade449d7dfbb31de8419eb943e2ecbcdd06df5227e82b9ded519a56e70f0a1c0fc17b06",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 44,
|
||||||
|
"comment" : "modified bit 2 in R",
|
||||||
|
"msg" : "313233343030",
|
||||||
|
"sig" : "59b94c53101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff280f1aab07b4ad069dfafc01b4532e1e44cbf7177e1bdda197fc87434046db5b935afd9114ac5e1138eaead23c3b59dba9026d2da4a86fe800b",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 45,
|
||||||
|
"comment" : "modified bit 7 in R",
|
||||||
|
"msg" : "313233343030",
|
||||||
|
"sig" : "ddb94c53101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff2807668402b7b093fc754019324077c1f842a7d2e35adf7b87094115cec459ad5419e162988ef42b1988d9b944d9d5a7ce09c6f342afa500839",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 46,
|
||||||
|
"comment" : "modified bit 8 in R",
|
||||||
|
"msg" : "313233343030",
|
||||||
|
"sig" : "5db84c53101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff280279b70338586b9e13e669191cc0dfc2a937d50a6118758de04a4ca41f4877abdb971afa87fe4b83bc243b8dfd2cb368aa389a4cb11e83e31",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 47,
|
||||||
|
"comment" : "modified bit 16 in R",
|
||||||
|
"msg" : "313233343030",
|
||||||
|
"sig" : "5db94d53101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff280c7b847556b3a6f9447483899ab730a23004c695054dd57b1c3214fa87f632f39c8ff1471f0532b8eee4154930e1ca30d574b8f9e85b0432b",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 48,
|
||||||
|
"comment" : "modified bit 31 in R",
|
||||||
|
"msg" : "313233343030",
|
||||||
|
"sig" : "5db94cd3101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff2800b017917472b130a1cc1c8e995a252617d5ddaf1f3d48930b4876fa0d2cfedec90a8c85c8274892a1ca3b6cfce63ebfebc307210b844ae0c",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 49,
|
||||||
|
"comment" : "modified bit 32 in R",
|
||||||
|
"msg" : "313233343030",
|
||||||
|
"sig" : "5db94c53111f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff2805f38f6371860fcc4f2ec515afd35cb05d8941e2448cc469a15b8537e758b16d46b123581613462c2bb20d8a07299ab795d0998e1e4277931",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 50,
|
||||||
|
"comment" : "modified bit 63 in R",
|
||||||
|
"msg" : "313233343030",
|
||||||
|
"sig" : "5db94c53101f529f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff28017111ba6fefd45e2490f1d53a184007fa073470706d7f4a9606fcad2954e74c32116ba7701d225b76e55164e64df3245c1031f0df734bd31",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 51,
|
||||||
|
"comment" : "modified bit 64 in R",
|
||||||
|
"msg" : "313233343030",
|
||||||
|
"sig" : "5db94c53101f521f6d1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff2808d7d0aa1fd81d0e31789921771c654338f96f0b557b615e3da55670271608a0e022e4e8cf393e309f8f6412281b6147e7fce42b089eb1e0c",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 52,
|
||||||
|
"comment" : "modified bit 97 in R",
|
||||||
|
"msg" : "313233343030",
|
||||||
|
"sig" : "5db94c53101f521f6c1f43b60ca4d7e06fbd49c2e8afaf4fcc289e645e0880a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff280b08d3be6ebf4e60bf6d74e105ea2fa9b965c62816bbd22ea3bb0c1acfd12300523ca76f94b6f789488a957fbeb212d713baccf95fd594f3d",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 53,
|
||||||
|
"comment" : "modified bit 127 in R",
|
||||||
|
"msg" : "313233343030",
|
||||||
|
"sig" : "5db94c53101f521f6c1f43b60ea4d7606fbd49c2e8afaf4fcc289e645e0880a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff280a23f54857e9b0f72b2ef90d2768834590464d75933ed08c454faa762b3702a2b631c33c339d05b2e24c20a8214f99af31f93f80f416a1129",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 54,
|
||||||
|
"comment" : "modified bit 240 in R",
|
||||||
|
"msg" : "313233343030",
|
||||||
|
"sig" : "5db94c53101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0881a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff280734bdc399273d3403d934ceaae16e87a68c6bff6b77d8037ff41c97922498a58e704c29ab519d41bab70735f71fc26f589361e2b21754300",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 55,
|
||||||
|
"comment" : "modified bit 247 in R",
|
||||||
|
"msg" : "313233343030",
|
||||||
|
"sig" : "5db94c53101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0800a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff280ba961cc8d0765c99d57470ee1c0c77f0a562a198fd0175eddb0c033e0fb8525328c5e2c516e2b00f73609c7f769195eb1a02ff54090d781f",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 56,
|
||||||
|
"comment" : "modified bit 248 in R",
|
||||||
|
"msg" : "313233343030",
|
||||||
|
"sig" : "5db94c53101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880a97b8e55858df4cf2291a7303ffda446b82a117b4dd408cff280e72685907da9e5a64e4142ed02fc0c6bf95763201db5942aac055fa87e6fdd32e483fd21ed4110d5d7ef619b740fef2ad8a71fe821e42a2a",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 57,
|
||||||
|
"comment" : "modified bit 253 in R",
|
||||||
|
"msg" : "313233343030",
|
||||||
|
"sig" : "5db94c53101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880887b8e55858df4cf2291a7303ffda446b82a117b4dd408cff280500646d67c74f13471f0ad034da530f7238fe7897e532af8ec2977643a410b1d054934df567e170276389e66b3f3ccb3c15aed239d04f72b",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 58,
|
||||||
|
"comment" : "modified bit 254 in R",
|
||||||
|
"msg" : "313233343030",
|
||||||
|
"sig" : "5db94c53101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880e87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff2807bb153b8e350aa736a91c921217578539600c1299ab76522ef8f6902d79c93f274073ee6beafe6200ecaf59f7cd11bb1c833f24bf30ed52d",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 59,
|
||||||
|
"comment" : "modified bit 255 in R",
|
||||||
|
"msg" : "313233343030",
|
||||||
|
"sig" : "5db94c53101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880287b8e55858df4cf2291a7303ffda446b82a117b4dd408cff2804a67b22be599d6433b87ea961c82c457ab50f64ac6b7efb0b2f90988927f83742303c278f8248e02d5679b41ed505aba0fb51110d0def810",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 60,
|
||||||
|
"comment" : "modified bit 440 in R",
|
||||||
|
"msg" : "313233343030",
|
||||||
|
"sig" : "5db94c53101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff3807f452efb0cd97dab5506028b7b876830dee02a9c0cbd140dcde509638d4d546c30856b2151bdf79930df5bbb11f2beb66bcdc25ad75f2116",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 61,
|
||||||
|
"comment" : "modified bit 441 in R",
|
||||||
|
"msg" : "313233343030",
|
||||||
|
"sig" : "5db94c53101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff0808d78231bb3c9a87c5b8d168fe05f8197503a3d73a6d700f436b5a76ab866388baa6930191a077aca7970058932c88b7f9e6ecb13c89dcd1d",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 62,
|
||||||
|
"comment" : "modified bit 447 in R",
|
||||||
|
"msg" : "313233343030",
|
||||||
|
"sig" : "5db94c53101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cf72809e5a8406063fb3545f0fb627f841b2e3a85ad5d378018e8b58fe58e14ee5520d57abc9140e9c5a75a8b09ac3334dd0cad69b48771284321d",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 63,
|
||||||
|
"comment" : "modified bit 448 in R",
|
||||||
|
"msg" : "313233343030",
|
||||||
|
"sig" : "5db94c53101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff2811adf92201088e051ee48b57aecf46edfc68e5baeed5ae4910ba5681d370f75ab593811e18293ef0808581c254196bcbf2b4c454136a6711b",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 64,
|
||||||
|
"comment" : "modified bit 449 in R",
|
||||||
|
"msg" : "313233343030",
|
||||||
|
"sig" : "5db94c53101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff2825e06c3999e8308be439c40940b0075d3e4f65147c1608cbe6e9c432e33bed6686f9393ae2568f0ad60febcb4b6179c0d90d034e7c3c46810",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 65,
|
||||||
|
"comment" : "modified bit 454 in R",
|
||||||
|
"msg" : "313233343030",
|
||||||
|
"sig" : "5db94c53101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff2c02456bbd141df048dbf1843be6d5fef402483314c2af547b361a09f3319489eaede43404df9faf634c1298d678b5261c808b0be3726013e39",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 66,
|
||||||
|
"comment" : "modified bit 455 in R",
|
||||||
|
"msg" : "313233343030",
|
||||||
|
"sig" : "5db94c53101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff2007106d2a896a7fec6dee53eea272d9b6e738c340295416b50f39a9463a5635450b9f93c4c06737affd42ae06cee5879c96c0bd58a91345503",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 67,
|
||||||
|
"comment" : "R==0",
|
||||||
|
"msg" : "313233343030",
|
||||||
|
"sig" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027ab98ab862e4e7ec3361a45ac1993e9b47d9ac40db91faed752399cee0413122b47346594fd7d2c8949b43e4cabaf17d8339ea0e307023f",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 68,
|
||||||
|
"comment" : "invalid R",
|
||||||
|
"msg" : "313233343030",
|
||||||
|
"sig" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd11bae33a0999fd3fd2bed6fa5577685e8fd595e79c006e58fd35f69f91b1d853553fb4006019a07725aa37773883dbe12253812887ac828",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 69,
|
||||||
|
"comment" : "all bits flipped in R",
|
||||||
|
"msg" : "313233343030",
|
||||||
|
"sig" : "a246b3acefe0ade093e0bc49f15b281f9042b63d175050b033d7619ba1f77f578471aa7a720b30dd6e58cfc0025bb947d5ee84b22bf7300d7f334e48141af0fade1469f5dedb851c9e725d27bd65012bada05e70cde641aad9ce0bea4983164f73816b6f13095e6b93eb03e850cad0cf0d",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 70,
|
||||||
|
"comment" : "checking malleability ",
|
||||||
|
"msg" : "54657374",
|
||||||
|
"sig" : "5d053ff5b71f6ec3284525d35d77933178c8e19879886d08eccc6c7d27e9e5b5e02537dbc4d4723506e8d171fc1733857573dd02d18f48f280241bd6142ddb02c0f9fa133955d3e610b4b27cb814227de8b241ef4e86402b6ef7862b78a386950246ff38d6d2f458136d12e3c97fdd9866",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : [
|
||||||
|
"SignatureMalleability"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 71,
|
||||||
|
"comment" : "checking malleability ",
|
||||||
|
"msg" : "54657374",
|
||||||
|
"sig" : "5d053ff5b71f6ec3284525d35d77933178c8e19879886d08eccc6c7d27e9e5b5e02537dbc4d4723506e8d171fc1733857573dd02d18f48f28017602ec0bf9d7be34e8ad9c6c795533244e952675efdcbac9c65b9cb85402b6ef7862b78a386950246ff38d6d2f458136d12e3c97fdd98a6",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : [
|
||||||
|
"SignatureMalleability"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 72,
|
||||||
|
"comment" : "checking malleability ",
|
||||||
|
"msg" : "54657374",
|
||||||
|
"sig" : "5d053ff5b71f6ec3284525d35d77933178c8e19879886d08eccc6c7d27e9e5b5e02537dbc4d4723506e8d171fc1733857573dd02d18f48f280fde9de16e5226d2af9a864e2ac1a2d756456ffc4f1b3693570ad4dc584402b6ef7862b78a386950246ff38d6d2f458136d12e3c97fdd9826",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : [
|
||||||
|
"SignatureMalleability"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 73,
|
||||||
|
"comment" : "checking malleability ",
|
||||||
|
"msg" : "54657374",
|
||||||
|
"sig" : "5d053ff5b71f6ec3284525d35d77933178c8e19879886d08eccc6c7d27e9e5b5e02537dbc4d4723506e8d171fc1733857573dd02d18f48f280c9fd3fc42f2d50b84de67a197724e0faa43058801821a546173d76b882402b6ef7862b78a386950246ff38d6d2f458136d12e3c97fdd9826",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : [
|
||||||
|
"SignatureMalleability"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 74,
|
||||||
|
"comment" : "checking malleability ",
|
||||||
|
"msg" : "54657374",
|
||||||
|
"sig" : "5d053ff5b71f6ec3284525d35d77933178c8e19879886d08eccc6c7d27e9e5b5e02537dbc4d4723506e8d171fc1733857573dd02d18f48f28031d67d699a188a9ca46b4eabe2107aef237ca609cb462e24c91d25d286402b6ef7862b78a386950246ff38d6d2f458136d12e3c97fdd9866",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : [
|
||||||
|
"SignatureMalleability"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 75,
|
||||||
|
"comment" : "checking malleability ",
|
||||||
|
"msg" : "54657374",
|
||||||
|
"sig" : "5d053ff5b71f6ec3284525d35d77933178c8e19879886d08eccc6c7d27e9e5b5e02537dbc4d4723506e8d171fc1733857573dd02d18f48f28031d67d699a188a9ca46b4eabe2107aef237ca609cb462e24c91d25d286402b6ef7862b78a386950246ff38d6d2f458136d12e3c97fdd98a6",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : [
|
||||||
|
"SignatureMalleability"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 76,
|
||||||
|
"comment" : "checking malleability ",
|
||||||
|
"msg" : "54657374",
|
||||||
|
"sig" : "5d053ff5b71f6ec3284525d35d77933178c8e19879886d08eccc6c7d27e9e5b5e02537dbc4d4723506e8d171fc1733857573dd02d18f48f28031d67d699a188a9ca46b4eabe2107aef237ca609cb462e24c91d25d286402b6ef7862b78a386950246ff38d6d2f458136d12e3c97fdd9826",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : [
|
||||||
|
"SignatureMalleability"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 77,
|
||||||
|
"comment" : "checking malleability ",
|
||||||
|
"msg" : "54657374",
|
||||||
|
"sig" : "5d053ff5b71f6ec3284525d35d77933178c8e19879886d08eccc6c7d27e9e5b5e02537dbc4d4723506e8d171fc1733857573dd02d18f48f28030d67d699a188a9ca46b4eabe2107aef237ca609cb462e24c91d25d285402b6ef7862b78a386950246ff38d6d2f458136d12e3c97fdd9826",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : [
|
||||||
|
"SignatureMalleability"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"jwk" : {
|
||||||
|
"crv" : "Ed448",
|
||||||
|
"d" : "bIKlYsuAjRDWMr6JyFE-v2ySnzTd-oyfY8mWDvbjSKNSjIo_zC8ETjmj_FuUSS-PAy51SaIAmPlb",
|
||||||
|
"kid" : "none",
|
||||||
|
"kty" : "OKP",
|
||||||
|
"x" : "X9dEm1m0Yf0s54fsYWrUah2hNCSFpw4fig6nXYDpZ3jt8SR2m0bHBhvWeD3x5Q9s0foavq_oJWGA"
|
||||||
|
},
|
||||||
|
"key" : {
|
||||||
|
"curve" : "edwards448",
|
||||||
|
"keySize" : 448,
|
||||||
|
"pk" : "5fd7449b59b461fd2ce787ec616ad46a1da1342485a70e1f8a0ea75d80e96778edf124769b46c7061bd6783df1e50f6cd1fa1abeafe8256180",
|
||||||
|
"sk" : "6c82a562cb808d10d632be89c8513ebf6c929f34ddfa8c9f63c9960ef6e348a3528c8a3fcc2f044e39a3fc5b94492f8f032e7549a20098f95b",
|
||||||
|
"type" : "EDDSAKeyPair"
|
||||||
|
},
|
||||||
|
"keyDer" : "3043300506032b6571033a005fd7449b59b461fd2ce787ec616ad46a1da1342485a70e1f8a0ea75d80e96778edf124769b46c7061bd6783df1e50f6cd1fa1abeafe8256180",
|
||||||
|
"keyPem" : "-----BEGIN PUBLIC KEY-----\nMEMwBQYDK2VxAzoAX9dEm1m0Yf0s54fsYWrUah2hNCSFpw4fig6nXYDpZ3jt8SR2m0bHBhvWeD3x5Q9s0foavq/oJWGA\n-----END PUBLIC KEY-----\n",
|
||||||
|
"type" : "EddsaVerify",
|
||||||
|
"tests" : [
|
||||||
|
{
|
||||||
|
"tcId" : 78,
|
||||||
|
"comment" : "RFC 8032",
|
||||||
|
"msg" : "",
|
||||||
|
"sig" : "533a37f6bbe457251f023c0d88f976ae2dfb504a843e34d2074fd823d41a591f2b233f034f628281f2fd7a22ddd47d7828c59bd0a21bfd3980ff0d2028d4b18a9df63e006c5d1c2d345b925d8dc00b4104852db99ac5c7cdda8530a113a0f4dbb61149f05a7363268c71d95808ff2e652600",
|
||||||
|
"result" : "valid",
|
||||||
|
"flags" : []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"jwk" : {
|
||||||
|
"crv" : "Ed448",
|
||||||
|
"d" : "xOqwXTVwB8Yy89u0hImSTVUrCP4MNToNSh8ArNosRjr76mfF6NKHfF47w5emWZSe-AIelU4KEidO",
|
||||||
|
"kid" : "none",
|
||||||
|
"kty" : "OKP",
|
||||||
|
"x" : "Q7oo9DDN_0Vq5TFUX37NCsg0pV2TWMA3K_oMbGeYwIZq6gHrAHQoArhDjqTLghacI1FgYntMOpSA"
|
||||||
|
},
|
||||||
|
"key" : {
|
||||||
|
"curve" : "edwards448",
|
||||||
|
"keySize" : 448,
|
||||||
|
"pk" : "43ba28f430cdff456ae531545f7ecd0ac834a55d9358c0372bfa0c6c6798c0866aea01eb00742802b8438ea4cb82169c235160627b4c3a9480",
|
||||||
|
"sk" : "c4eab05d357007c632f3dbb48489924d552b08fe0c353a0d4a1f00acda2c463afbea67c5e8d2877c5e3bc397a659949ef8021e954e0a12274e",
|
||||||
|
"type" : "EDDSAKeyPair"
|
||||||
|
},
|
||||||
|
"keyDer" : "3043300506032b6571033a0043ba28f430cdff456ae531545f7ecd0ac834a55d9358c0372bfa0c6c6798c0866aea01eb00742802b8438ea4cb82169c235160627b4c3a9480",
|
||||||
|
"keyPem" : "-----BEGIN PUBLIC KEY-----\nMEMwBQYDK2VxAzoAQ7oo9DDN/0Vq5TFUX37NCsg0pV2TWMA3K/oMbGeYwIZq6gHrAHQoArhDjqTLghacI1FgYntMOpSA\n-----END PUBLIC KEY-----\n",
|
||||||
|
"type" : "EddsaVerify",
|
||||||
|
"tests" : [
|
||||||
|
{
|
||||||
|
"tcId" : 79,
|
||||||
|
"comment" : "RFC 8032: 1 octet",
|
||||||
|
"msg" : "03",
|
||||||
|
"sig" : "26b8f91727bd62897af15e41eb43c377efb9c610d48f2335cb0bd0087810f4352541b143c4b981b7e18f62de8ccdf633fc1bf037ab7cd779805e0dbcc0aae1cbcee1afb2e027df36bc04dcecbf154336c19f0af7e0a6472905e799f1953d2a0ff3348ab21aa4adafd1d234441cf807c03a00",
|
||||||
|
"result" : "valid",
|
||||||
|
"flags" : []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 80,
|
||||||
|
"comment" : "RFC 8032: 1 octet with context",
|
||||||
|
"msg" : "03",
|
||||||
|
"sig" : "d4f8f6131770dd46f40867d6fd5d5055de43541f8c5e35abbcd001b32a89f7d2151f7647f11d8ca2ae279fb842d607217fce6e042f6815ea000c85741de5c8da1144a6a1aba7f96de42505d7a7298524fda538fccbbb754f578c1cad10d54d0d5428407e85dcbc98a49155c13764e66c3c00",
|
||||||
|
"result" : "invalid",
|
||||||
|
"flags" : []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"jwk" : {
|
||||||
|
"crv" : "Ed448",
|
||||||
|
"d" : "zSPST3FCdOdENDI3uTKQ9RH2Ql-Y5kRZ_yA-iYUIP_32BQBVOrwOBc0CGEvbicTM1n4YeVEmfrMo",
|
||||||
|
"kid" : "none",
|
||||||
|
"kty" : "OKP",
|
||||||
|
"x" : "3OqeePNaG_NJmoMbELhskKrAHNhLZ6AQm1WjbpMoseNl_OFh1xznExpUPqTLX36fHYsAaWRHABQA"
|
||||||
|
},
|
||||||
|
"key" : {
|
||||||
|
"curve" : "edwards448",
|
||||||
|
"keySize" : 448,
|
||||||
|
"pk" : "dcea9e78f35a1bf3499a831b10b86c90aac01cd84b67a0109b55a36e9328b1e365fce161d71ce7131a543ea4cb5f7e9f1d8b00696447001400",
|
||||||
|
"sk" : "cd23d24f714274e744343237b93290f511f6425f98e64459ff203e8985083ffdf60500553abc0e05cd02184bdb89c4ccd67e187951267eb328",
|
||||||
|
"type" : "EDDSAKeyPair"
|
||||||
|
},
|
||||||
|
"keyDer" : "3043300506032b6571033a00dcea9e78f35a1bf3499a831b10b86c90aac01cd84b67a0109b55a36e9328b1e365fce161d71ce7131a543ea4cb5f7e9f1d8b00696447001400",
|
||||||
|
"keyPem" : "-----BEGIN PUBLIC KEY-----\nMEMwBQYDK2VxAzoA3OqeePNaG/NJmoMbELhskKrAHNhLZ6AQm1WjbpMoseNl/OFh1xznExpUPqTLX36fHYsAaWRHABQA\n-----END PUBLIC KEY-----\n",
|
||||||
|
"type" : "EddsaVerify",
|
||||||
|
"tests" : [
|
||||||
|
{
|
||||||
|
"tcId" : 81,
|
||||||
|
"comment" : "RFC 8032: 11 bytes",
|
||||||
|
"msg" : "0c3e544074ec63b0265e0c",
|
||||||
|
"sig" : "1f0a8888ce25e8d458a21130879b840a9089d999aaba039eaf3e3afa090a09d389dba82c4ff2ae8ac5cdfb7c55e94d5d961a29fe0109941e00b8dbdeea6d3b051068df7254c0cdc129cbe62db2dc957dbb47b51fd3f213fb8698f064774250a5028961c9bf8ffd973fe5d5c206492b140e00",
|
||||||
|
"result" : "valid",
|
||||||
|
"flags" : []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"jwk" : {
|
||||||
|
"crv" : "Ed448",
|
||||||
|
"d" : "JYzdStoy7Zyf9U5jdWrlgvuPqyrHIfLI5nanJ2hRPZOfY93bVWCRM_Ka34bsmSncy1LBxf0v9-Ib",
|
||||||
|
"kid" : "none",
|
||||||
|
"kty" : "OKP",
|
||||||
|
"x" : "O6FtoMbyzB8wGHdAdW9eeY1rxfwBXXxjzJUQ7j_UStwk2OlotuRub5TRm5RTYXJr114UnvCYF_WA"
|
||||||
|
},
|
||||||
|
"key" : {
|
||||||
|
"curve" : "edwards448",
|
||||||
|
"keySize" : 448,
|
||||||
|
"pk" : "3ba16da0c6f2cc1f30187740756f5e798d6bc5fc015d7c63cc9510ee3fd44adc24d8e968b6e46e6f94d19b945361726bd75e149ef09817f580",
|
||||||
|
"sk" : "258cdd4ada32ed9c9ff54e63756ae582fb8fab2ac721f2c8e676a72768513d939f63dddb55609133f29adf86ec9929dccb52c1c5fd2ff7e21b",
|
||||||
|
"type" : "EDDSAKeyPair"
|
||||||
|
},
|
||||||
|
"keyDer" : "3043300506032b6571033a003ba16da0c6f2cc1f30187740756f5e798d6bc5fc015d7c63cc9510ee3fd44adc24d8e968b6e46e6f94d19b945361726bd75e149ef09817f580",
|
||||||
|
"keyPem" : "-----BEGIN PUBLIC KEY-----\nMEMwBQYDK2VxAzoAO6FtoMbyzB8wGHdAdW9eeY1rxfwBXXxjzJUQ7j/UStwk2OlotuRub5TRm5RTYXJr114UnvCYF/WA\n-----END PUBLIC KEY-----\n",
|
||||||
|
"type" : "EddsaVerify",
|
||||||
|
"tests" : [
|
||||||
|
{
|
||||||
|
"tcId" : 82,
|
||||||
|
"comment" : "RFC 8032: 12 bytes",
|
||||||
|
"msg" : "64a65f3cdedcdd66811e2915",
|
||||||
|
"sig" : "7eeeab7c4e50fb799b418ee5e3197ff6bf15d43a14c34389b59dd1a7b1b85b4ae90438aca634bea45e3a2695f1270f07fdcdf7c62b8efeaf00b45c2c96ba457eb1a8bf075a3db28e5c24f6b923ed4ad747c3c9e03c7079efb87cb110d3a99861e72003cbae6d6b8b827e4e6c143064ff3c00",
|
||||||
|
"result" : "valid",
|
||||||
|
"flags" : []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"jwk" : {
|
||||||
|
"crv" : "Ed448",
|
||||||
|
"d" : "fvToRUQjZ1L7tWuPMaI6EOQoFPX1XKA3zcwRxkyaOylJwbtgcAMUYRcypsL-qY7rwCZqEak5cBAO",
|
||||||
|
"kid" : "none",
|
||||||
|
"kty" : "OKP",
|
||||||
|
"x" : "s9oHmwqkk6V3ICnwRnuuvuWoES2dOiJTI2HaKU97s4FcXcWeF2tNnzgcoJOOE8bAexdL5l36V46A"
|
||||||
|
},
|
||||||
|
"key" : {
|
||||||
|
"curve" : "edwards448",
|
||||||
|
"keySize" : 448,
|
||||||
|
"pk" : "b3da079b0aa493a5772029f0467baebee5a8112d9d3a22532361da294f7bb3815c5dc59e176b4d9f381ca0938e13c6c07b174be65dfa578e80",
|
||||||
|
"sk" : "7ef4e84544236752fbb56b8f31a23a10e42814f5f55ca037cdcc11c64c9a3b2949c1bb60700314611732a6c2fea98eebc0266a11a93970100e",
|
||||||
|
"type" : "EDDSAKeyPair"
|
||||||
|
},
|
||||||
|
"keyDer" : "3043300506032b6571033a00b3da079b0aa493a5772029f0467baebee5a8112d9d3a22532361da294f7bb3815c5dc59e176b4d9f381ca0938e13c6c07b174be65dfa578e80",
|
||||||
|
"keyPem" : "-----BEGIN PUBLIC KEY-----\nMEMwBQYDK2VxAzoAs9oHmwqkk6V3ICnwRnuuvuWoES2dOiJTI2HaKU97s4FcXcWeF2tNnzgcoJOOE8bAexdL5l36V46A\n-----END PUBLIC KEY-----\n",
|
||||||
|
"type" : "EddsaVerify",
|
||||||
|
"tests" : [
|
||||||
|
{
|
||||||
|
"tcId" : 83,
|
||||||
|
"comment" : "RFC 8032: 13 bytes",
|
||||||
|
"msg" : "64a65f3cdedcdd66811e2915e7",
|
||||||
|
"sig" : "6a12066f55331b6c22acd5d5bfc5d71228fbda80ae8dec26bdd306743c5027cb4890810c162c027468675ecf645a83176c0d7323a2ccde2d80efe5a1268e8aca1d6fbc194d3f77c44986eb4ab4177919ad8bec33eb47bbb5fc6e28196fd1caf56b4e7e0ba5519234d047155ac727a1053100",
|
||||||
|
"result" : "valid",
|
||||||
|
"flags" : []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"jwk" : {
|
||||||
|
"crv" : "Ed448",
|
||||||
|
"d" : "1l3zQa0T4AhWdoi67dqOnc3BfcAkl06ltCJ7ZTDjOb_yH5nmjKaWjzzKbf4PufT6tPoTXVVC6j8B",
|
||||||
|
"kid" : "none",
|
||||||
|
"kty" : "OKP",
|
||||||
|
"x" : "35cF9Y7bq4Asf4Njz-VWCrHGEywgqfHdFjSDom-KxTo51oCL9KHfvSYbCZuwOz-1CQbLKL2KCB8A"
|
||||||
|
},
|
||||||
|
"key" : {
|
||||||
|
"curve" : "edwards448",
|
||||||
|
"keySize" : 448,
|
||||||
|
"pk" : "df9705f58edbab802c7f8363cfe5560ab1c6132c20a9f1dd163483a26f8ac53a39d6808bf4a1dfbd261b099bb03b3fb50906cb28bd8a081f00",
|
||||||
|
"sk" : "d65df341ad13e008567688baedda8e9dcdc17dc024974ea5b4227b6530e339bff21f99e68ca6968f3cca6dfe0fb9f4fab4fa135d5542ea3f01",
|
||||||
|
"type" : "EDDSAKeyPair"
|
||||||
|
},
|
||||||
|
"keyDer" : "3043300506032b6571033a00df9705f58edbab802c7f8363cfe5560ab1c6132c20a9f1dd163483a26f8ac53a39d6808bf4a1dfbd261b099bb03b3fb50906cb28bd8a081f00",
|
||||||
|
"keyPem" : "-----BEGIN PUBLIC KEY-----\nMEMwBQYDK2VxAzoA35cF9Y7bq4Asf4Njz+VWCrHGEywgqfHdFjSDom+KxTo51oCL9KHfvSYbCZuwOz+1CQbLKL2KCB8A\n-----END PUBLIC KEY-----\n",
|
||||||
|
"type" : "EddsaVerify",
|
||||||
|
"tests" : [
|
||||||
|
{
|
||||||
|
"tcId" : 84,
|
||||||
|
"comment" : "RFC 8032: 64 bytes",
|
||||||
|
"msg" : "bd0f6a3747cd561bdddf4640a332461a4a30a12a434cd0bf40d766d9c6d458e5512204a30c17d1f50b5079631f64eb3112182da3005835461113718d1a5ef944",
|
||||||
|
"sig" : "554bc2480860b49eab8532d2a533b7d578ef473eeb58c98bb2d0e1ce488a98b18dfde9b9b90775e67f47d4a1c3482058efc9f40d2ca033a0801b63d45b3b722ef552bad3b4ccb667da350192b61c508cf7b6b5adadc2c8d9a446ef003fb05cba5f30e88e36ec2703b349ca229c2670833900",
|
||||||
|
"result" : "valid",
|
||||||
|
"flags" : []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"jwk" : {
|
||||||
|
"crv" : "Ed448",
|
||||||
|
"d" : "LsX-PBcEWr2xNqXmqRPjKrda5otT0vwUm3flBBMtN1abfnZrp0oZvWFiNDohyFkKqc68qQFMY231",
|
||||||
|
"kid" : "none",
|
||||||
|
"kty" : "OKP",
|
||||||
|
"x" : "eXVvAU3P4gefXdnnGL5BceLvJIagjyUYb2v_Q6mTa5v-EkArCK5leYo9geIunsgOdpCGLvPU7ToA"
|
||||||
|
},
|
||||||
|
"key" : {
|
||||||
|
"curve" : "edwards448",
|
||||||
|
"keySize" : 448,
|
||||||
|
"pk" : "79756f014dcfe2079f5dd9e718be4171e2ef2486a08f25186f6bff43a9936b9bfe12402b08ae65798a3d81e22e9ec80e7690862ef3d4ed3a00",
|
||||||
|
"sk" : "2ec5fe3c17045abdb136a5e6a913e32ab75ae68b53d2fc149b77e504132d37569b7e766ba74a19bd6162343a21c8590aa9cebca9014c636df5",
|
||||||
|
"type" : "EDDSAKeyPair"
|
||||||
|
},
|
||||||
|
"keyDer" : "3043300506032b6571033a0079756f014dcfe2079f5dd9e718be4171e2ef2486a08f25186f6bff43a9936b9bfe12402b08ae65798a3d81e22e9ec80e7690862ef3d4ed3a00",
|
||||||
|
"keyPem" : "-----BEGIN PUBLIC KEY-----\nMEMwBQYDK2VxAzoAeXVvAU3P4gefXdnnGL5BceLvJIagjyUYb2v/Q6mTa5v+EkArCK5leYo9geIunsgOdpCGLvPU7ToA\n-----END PUBLIC KEY-----\n",
|
||||||
|
"type" : "EddsaVerify",
|
||||||
|
"tests" : [
|
||||||
|
{
|
||||||
|
"tcId" : 85,
|
||||||
|
"comment" : "RFC 8032: 256 bytes",
|
||||||
|
"msg" : "15777532b0bdd0d1389f636c5f6b9ba734c90af572877e2d272dd078aa1e567cfa80e12928bb542330e8409f3174504107ecd5efac61ae7504dabe2a602ede89e5cca6257a7c77e27a702b3ae39fc769fc54f2395ae6a1178cab4738e543072fc1c177fe71e92e25bf03e4ecb72f47b64d0465aaea4c7fad372536c8ba516a6039c3c2a39f0e4d832be432dfa9a706a6e5c7e19f397964ca4258002f7c0541b590316dbc5622b6b2a6fe7a4abffd96105eca76ea7b98816af0748c10df048ce012d901015a51f189f3888145c03650aa23ce894c3bd889e030d565071c59f409a9981b51878fd6fc110624dcbcde0bf7a69ccce38fabdf86f3bef6044819de11",
|
||||||
|
"sig" : "c650ddbb0601c19ca11439e1640dd931f43c518ea5bea70d3dcde5f4191fe53f00cf966546b72bcc7d58be2b9badef28743954e3a44a23f880e8d4f1cfce2d7a61452d26da05896f0a50da66a239a8a188b6d825b3305ad77b73fbac0836ecc60987fd08527c1a8e80d5823e65cafe2a3d00",
|
||||||
|
"result" : "valid",
|
||||||
|
"flags" : []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"jwk" : {
|
||||||
|
"crv" : "Ed448",
|
||||||
|
"d" : "hy0JN4D103MN98ISZks3uKDyT1aBDaqDgs1Po_d2NOxE3FTxwu2b6ob6-3Yy2L4ZnqFl9a1V3Zzo",
|
||||||
|
"kid" : "none",
|
||||||
|
"kty" : "OKP",
|
||||||
|
"x" : "qBsuinClrJT_28ybrfw_6wgB8lhXi7EUrUTs4ewOeZ2gjv-4HF1oXAxW9k7srvjN8RzDhzeDjPQA"
|
||||||
|
},
|
||||||
|
"key" : {
|
||||||
|
"curve" : "edwards448",
|
||||||
|
"keySize" : 448,
|
||||||
|
"pk" : "a81b2e8a70a5ac94ffdbcc9badfc3feb0801f258578bb114ad44ece1ec0e799da08effb81c5d685c0c56f64eecaef8cdf11cc38737838cf400",
|
||||||
|
"sk" : "872d093780f5d3730df7c212664b37b8a0f24f56810daa8382cd4fa3f77634ec44dc54f1c2ed9bea86fafb7632d8be199ea165f5ad55dd9ce8",
|
||||||
|
"type" : "EDDSAKeyPair"
|
||||||
|
},
|
||||||
|
"keyDer" : "3043300506032b6571033a00a81b2e8a70a5ac94ffdbcc9badfc3feb0801f258578bb114ad44ece1ec0e799da08effb81c5d685c0c56f64eecaef8cdf11cc38737838cf400",
|
||||||
|
"keyPem" : "-----BEGIN PUBLIC KEY-----\nMEMwBQYDK2VxAzoAqBsuinClrJT/28ybrfw/6wgB8lhXi7EUrUTs4ewOeZ2gjv+4HF1oXAxW9k7srvjN8RzDhzeDjPQA\n-----END PUBLIC KEY-----\n",
|
||||||
|
"type" : "EddsaVerify",
|
||||||
|
"tests" : [
|
||||||
|
{
|
||||||
|
"tcId" : 86,
|
||||||
|
"comment" : "RFC 8032: 1023 bytes",
|
||||||
|
"msg" : "6ddf802e1aae4986935f7f981ba3f0351d6273c0a0c22c9c0e8339168e675412a3debfaf435ed651558007db4384b650fcc07e3b586a27a4f7a00ac8a6fec2cd86ae4bf1570c41e6a40c931db27b2faa15a8cedd52cff7362c4e6e23daec0fbc3a79b6806e316efcc7b68119bf46bc76a26067a53f296dafdbdc11c77f7777e972660cf4b6a9b369a6665f02e0cc9b6edfad136b4fabe723d2813db3136cfde9b6d044322fee2947952e031b73ab5c603349b307bdc27bc6cb8b8bbd7bd323219b8033a581b59eadebb09b3c4f3d2277d4f0343624acc817804728b25ab797172b4c5c21a22f9c7839d64300232eb66e53f31c723fa37fe387c7d3e50bdf9813a30e5bb12cf4cd930c40cfb4e1fc622592a49588794494d56d24ea4b40c89fc0596cc9ebb961c8cb10adde976a5d602b1c3f85b9b9a001ed3c6a4d3b1437f52096cd1956d042a597d561a596ecd3d1735a8d570ea0ec27225a2c4aaff26306d1526c1af3ca6d9cf5a2c98f47e1c46db9a33234cfd4d81f2c98538a09ebe76998d0d8fd25997c7d255c6d66ece6fa56f11144950f027795e653008f4bd7ca2dee85d8e90f3dc315130ce2a00375a318c7c3d97be2c8ce5b6db41a6254ff264fa6155baee3b0773c0f497c573f19bb4f4240281f0b1f4f7be857a4e59d416c06b4c50fa09e1810ddc6b1467baeac5a3668d11b6ecaa901440016f389f80acc4db977025e7f5924388c7e340a732e554440e76570f8dd71b7d640b3450d1fd5f0410a18f9a3494f707c717b79b4bf75c98400b096b21653b5d217cf3565c9597456f70703497a078763829bc01bb1cbc8fa04eadc9a6e3f6699587a9e75c94e5bab0036e0b2e711392cff0047d0d6b05bd2a588bc109718954259f1d86678a579a3120f19cfb2963f177aeb70f2d4844826262e51b80271272068ef5b3856fa8535aa2a88b2d41f2a0e2fda7624c2850272ac4a2f561f8f2f7a318bfd5caf9696149e4ac824ad3460538fdc25421beec2cc6818162d06bbed0c40a387192349db67a118bada6cd5ab0140ee273204f628aad1c135f770279a651e24d8c14d75a6059d76b96a6fd857def5e0b354b27ab937a5815d16b5fae407ff18222c6d1ed263be68c95f32d908bd895cd76207ae726487567f9a67dad79abec316f683b17f2d02bf07e0ac8b5bc6162cf94697b3c27cd1fea49b27f23ba2901871962506520c392da8b6ad0d99f7013fbc06c2c17a569500c8a7696481c1cd33e9b14e40b82e79a5f5db82571ba97bae3ad3e0479515bb0e2b0f3bfcd1fd33034efc6245eddd7ee2086ddae2600d8ca73e214e8c2b0bdb2b047c6a464a562ed77b73d2d841c4b34973551257713b753632efba348169abc90a68f42611a40126d7cb21b58695568186f7e569d2ff0f9e745d0487dd2eb997cafc5abf9dd102e62ff66cba87",
|
||||||
|
"sig" : "e301345a41a39a4d72fff8df69c98075a0cc082b802fc9b2b6bc503f926b65bddf7f4c8f1cb49f6396afc8a70abe6d8aef0db478d4c6b2970076c6a0484fe76d76b3a97625d79f1ce240e7c576750d295528286f719b413de9ada3e8eb78ed573603ce30d8bb761785dc30dbc320869e1a00",
|
||||||
|
"result" : "valid",
|
||||||
|
"flags" : []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
107
test/fixtures/rfc6979.json
vendored
107
test/fixtures/rfc6979.json
vendored
@@ -1,107 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"curve": "P192",
|
|
||||||
"q": "FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831",
|
|
||||||
"private": "6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4",
|
|
||||||
"Ux": "AC2C77F529F91689FEA0EA5EFEC7F210D8EEA0B9E047ED56",
|
|
||||||
"Uy": "3BC723E57670BD4887EBC732C523063D0A7C957BC97C1C43",
|
|
||||||
"cases": [
|
|
||||||
{
|
|
||||||
"k": "32B1B6D7D42A05CB449065727A84804FB1A3E34D8F261496",
|
|
||||||
"message": "sample",
|
|
||||||
"r": "4B0B8CE98A92866A2820E20AA6B75B56382E0F9BFD5ECB55",
|
|
||||||
"s": "CCDB006926EA9565CBADC840829D8C384E06DE1F1E381B85"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"k": "5C4CE89CF56D9E7C77C8585339B006B97B5F0680B4306C6C",
|
|
||||||
"message": "test",
|
|
||||||
"r": "3A718BD8B4926C3B52EE6BBE67EF79B18CB6EB62B1AD97AE",
|
|
||||||
"s": "5662E6848A4A19B1F1AE2F72ACD4B8BBE50F1EAC65D9124F"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"curve": "P224",
|
|
||||||
"q": "FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D",
|
|
||||||
"private": "F220266E1105BFE3083E03EC7A3A654651F45E37167E88600BF257C1",
|
|
||||||
"Ux": "00CF08DA5AD719E42707FA431292DEA11244D64FC51610D94B130D6C",
|
|
||||||
"Uy": "EEAB6F3DEBE455E3DBF85416F7030CBD94F34F2D6F232C69F3C1385A",
|
|
||||||
"cases": [
|
|
||||||
{
|
|
||||||
"k": "C1D1F2F10881088301880506805FEB4825FE09ACB6816C36991AA06D",
|
|
||||||
"message": "sample",
|
|
||||||
"r": "1CDFE6662DDE1E4A1EC4CDEDF6A1F5A2FB7FBD9145C12113E6ABFD3E",
|
|
||||||
"s": "A6694FD7718A21053F225D3F46197CA699D45006C06F871808F43EBC"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"k": "DF8B38D40DCA3E077D0AC520BF56B6D565134D9B5F2EAE0D34900524",
|
|
||||||
"message": "test",
|
|
||||||
"r": "C441CE8E261DED634E4CF84910E4C5D1D22C5CF3B732BB204DBEF019",
|
|
||||||
"s": "902F42847A63BDC5F6046ADA114953120F99442D76510150F372A3F4"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"curve": "P256",
|
|
||||||
"q": "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551",
|
|
||||||
"private": "C9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721",
|
|
||||||
"Ux": "60FED4BA255A9D31C961EB74C6356D68C049B8923B61FA6CE669622E60F29FB6",
|
|
||||||
"Uy": "7903FE1008B8BC99A41AE9E95628BC64F2F1B20C2D7E9F5177A3C294D4462299",
|
|
||||||
"cases": [
|
|
||||||
{
|
|
||||||
"k": "A6E3C57DD01ABE90086538398355DD4C3B17AA873382B0F24D6129493D8AAD60",
|
|
||||||
"message": "sample",
|
|
||||||
"r": "EFD48B2AACB6A8FD1140DD9CD45E81D69D2C877B56AAF991C34D0EA84EAF3716",
|
|
||||||
"s": "F7CB1C942D657C41D436C7A1B6E29F65F3E900DBB9AFF4064DC4AB2F843ACDA8"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"k": "D16B6AE827F17175E040871A1C7EC3500192C4C92677336EC2537ACAEE0008E0",
|
|
||||||
"message": "test",
|
|
||||||
"r": "F1ABB023518351CD71D881567B1EA663ED3EFCF6C5132B354F28D3B0B7D38367",
|
|
||||||
"s": "019F4113742A2B14BD25926B49C649155F267E60D3814B4C0CC84250E46F0083"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"curve": "P384",
|
|
||||||
"q": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973",
|
|
||||||
"private": "6B9D3DAD2E1B8C1C05B19875B6659F4DE23C3B667BF297BA9AA47740787137D896D5724E4C70A825F872C9EA60D2EDF5",
|
|
||||||
"Ux": "EC3A4E415B4E19A4568618029F427FA5DA9A8BC4AE92E02E06AAE5286B300C64DEF8F0EA9055866064A254515480BC13",
|
|
||||||
"Uy": "8015D9B72D7D57244EA8EF9AC0C621896708A59367F9DFB9F54CA84B3F1C9DB1288B231C3AE0D4FE7344FD2533264720",
|
|
||||||
"cases": [
|
|
||||||
{
|
|
||||||
"k": "94ED910D1A099DAD3254E9242AE85ABDE4BA15168EAF0CA87A555FD56D10FBCA2907E3E83BA95368623B8C4686915CF9",
|
|
||||||
"message": "sample",
|
|
||||||
"r": "94EDBB92A5ECB8AAD4736E56C691916B3F88140666CE9FA73D64C4EA95AD133C81A648152E44ACF96E36DD1E80FABE46",
|
|
||||||
"s": "99EF4AEB15F178CEA1FE40DB2603138F130E740A19624526203B6351D0A3A94FA329C145786E679E7B82C71A38628AC8"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"k": "015EE46A5BF88773ED9123A5AB0807962D193719503C527B031B4C2D225092ADA71F4A459BC0DA98ADB95837DB8312EA",
|
|
||||||
"message": "test",
|
|
||||||
"r": "8203B63D3C853E8D77227FB377BCF7B7B772E97892A80F36AB775D509D7A5FEB0542A7F0812998DA8F1DD3CA3CF023DB",
|
|
||||||
"s": "DDD0760448D42D8A43AF45AF836FCE4DE8BE06B485E9B61B827C2F13173923E06A739F040649A667BF3B828246BAA5A5"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"curve": "P521",
|
|
||||||
"q": "1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409",
|
|
||||||
"private": "0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83538",
|
|
||||||
"Ux": "1894550D0785932E00EAA23B694F213F8C3121F86DC97A04E5A7167DB4E5BCD371123D46E45DB6B5D5370A7F20FB633155D38FFA16D2BD761DCAC474B9A2F5023A4",
|
|
||||||
"Uy": "0493101C962CD4D2FDDF782285E64584139C2F91B47F87FF82354D6630F746A28A0DB25741B5B34A828008B22ACC23F924FAAFBD4D33F81EA66956DFEAA2BFDFCF5",
|
|
||||||
"cases": [
|
|
||||||
{
|
|
||||||
"k": "1DAE2EA071F8110DC26882D4D5EAE0621A3256FC8847FB9022E2B7D28E6F10198B1574FDD03A9053C08A1854A168AA5A57470EC97DD5CE090124EF52A2F7ECBFFD3",
|
|
||||||
"message": "sample",
|
|
||||||
"r": "0C328FAFCBD79DD77850370C46325D987CB525569FB63C5D3BC53950E6D4C5F174E25A1EE9017B5D450606ADD152B534931D7D4E8455CC91F9B15BF05EC36E377FA",
|
|
||||||
"s": "0617CCE7CF5064806C467F678D3B4080D6F1CC50AF26CA209417308281B68AF282623EAA63E5B5C0723D8B8C37FF0777B1A20F8CCB1DCCC43997F1EE0E44DA4A67A"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"k": "16200813020EC986863BEDFC1B121F605C1215645018AEA1A7B215A564DE9EB1B38A67AA1128B80CE391C4FB71187654AAA3431027BFC7F395766CA988C964DC56D",
|
|
||||||
"message": "test",
|
|
||||||
"r": "13E99020ABF5CEE7525D16B69B229652AB6BDF2AFFCAEF38773B4B7D08725F10CDB93482FDCC54EDCEE91ECA4166B2A7C6265EF0CE2BD7051B7CEF945BABD47EE6D",
|
|
||||||
"s": "1FBD0013C674AA79CB39849527916CE301C66EA7CE8B80682786AD60F98F7E78A19CA69EFF5C57400E3B3A0AD66CE0978214D13BAF4E9AC60752F7B155E2DE4DCE3"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@@ -1,22 +1,19 @@
|
|||||||
import { deepStrictEqual } from 'assert';
|
import { deepStrictEqual } from 'assert';
|
||||||
import { should } from 'micro-should';
|
import { describe, should } from 'micro-should';
|
||||||
import { bytesToHex } from '@noble/hashes/utils';
|
import { bytesToHex } from '@noble/hashes/utils';
|
||||||
// Generic tests for all curves in package
|
// Generic tests for all curves in package
|
||||||
import { sha256 } from '@noble/hashes/sha256';
|
import { sha256 } from '@noble/hashes/sha256';
|
||||||
import { sha512 } from '@noble/hashes/sha512';
|
import { sha512 } from '@noble/hashes/sha512';
|
||||||
import { shake128, shake256 } from '@noble/hashes/sha3';
|
import { shake128, shake256 } from '@noble/hashes/sha3';
|
||||||
import { secp256r1 } from '../lib/esm/p256.js';
|
import * as secp256r1 from '../esm/p256.js';
|
||||||
import { secp384r1 } from '../lib/esm/p384.js';
|
import * as secp384r1 from '../esm/p384.js';
|
||||||
import { secp521r1 } from '../lib/esm/p521.js';
|
import * as secp521r1 from '../esm/p521.js';
|
||||||
import { ed25519 } from '../lib/esm/ed25519.js';
|
import * as ed25519 from '../esm/ed25519.js';
|
||||||
import { ed448 } from '../lib/esm/ed448.js';
|
import * as ed448 from '../esm/ed448.js';
|
||||||
import { secp256k1 } from '../lib/esm/secp256k1.js';
|
import * as secp256k1 from '../esm/secp256k1.js';
|
||||||
import { bls12_381 } from '../lib/esm/bls12-381.js';
|
import { bls12_381 } from '../esm/bls12-381.js';
|
||||||
import {
|
import { expand_message_xmd, expand_message_xof } from '../esm/abstract/hash-to-curve.js';
|
||||||
stringToBytes,
|
import { utf8ToBytes } from '../esm/abstract/utils.js';
|
||||||
expand_message_xmd,
|
|
||||||
expand_message_xof,
|
|
||||||
} from '../lib/esm/abstract/hash-to-curve.js';
|
|
||||||
// XMD
|
// XMD
|
||||||
import { default as xmd_sha256_38 } from './hash-to-curve/expand_message_xmd_SHA256_38.json' assert { type: 'json' };
|
import { default as xmd_sha256_38 } from './hash-to-curve/expand_message_xmd_SHA256_38.json' assert { type: 'json' };
|
||||||
import { default as xmd_sha256_256 } from './hash-to-curve/expand_message_xmd_SHA256_256.json' assert { type: 'json' };
|
import { default as xmd_sha256_256 } from './hash-to-curve/expand_message_xmd_SHA256_256.json' assert { type: 'json' };
|
||||||
@@ -51,43 +48,51 @@ import { default as ed448_ro } from './hash-to-curve/edwards448_XOF:SHAKE256_ELL
|
|||||||
import { default as ed448_nu } from './hash-to-curve/edwards448_XOF:SHAKE256_ELL2_NU_.json' assert { type: 'json' };
|
import { default as ed448_nu } from './hash-to-curve/edwards448_XOF:SHAKE256_ELL2_NU_.json' assert { type: 'json' };
|
||||||
|
|
||||||
function testExpandXMD(hash, vectors) {
|
function testExpandXMD(hash, vectors) {
|
||||||
for (let i = 0; i < vectors.tests.length; i++) {
|
describe(`${vectors.hash}/${vectors.DST.length}`, () => {
|
||||||
const t = vectors.tests[i];
|
for (let i = 0; i < vectors.tests.length; i++) {
|
||||||
should(`expand_message_xmd/${vectors.hash}/${vectors.DST.length}/${i}`, () => {
|
const t = vectors.tests[i];
|
||||||
const p = expand_message_xmd(
|
should(`${vectors.hash}/${vectors.DST.length}/${i}`, () => {
|
||||||
stringToBytes(t.msg),
|
const p = expand_message_xmd(
|
||||||
stringToBytes(vectors.DST),
|
utf8ToBytes(t.msg),
|
||||||
t.len_in_bytes,
|
utf8ToBytes(vectors.DST),
|
||||||
hash
|
Number.parseInt(t.len_in_bytes),
|
||||||
);
|
hash
|
||||||
deepStrictEqual(bytesToHex(p), t.uniform_bytes);
|
);
|
||||||
});
|
deepStrictEqual(bytesToHex(p), t.uniform_bytes);
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
testExpandXMD(sha256, xmd_sha256_38);
|
describe('expand_message_xmd', () => {
|
||||||
testExpandXMD(sha256, xmd_sha256_256);
|
testExpandXMD(sha256, xmd_sha256_38);
|
||||||
testExpandXMD(sha512, xmd_sha512_38);
|
testExpandXMD(sha256, xmd_sha256_256);
|
||||||
|
testExpandXMD(sha512, xmd_sha512_38);
|
||||||
|
});
|
||||||
|
|
||||||
function testExpandXOF(hash, vectors) {
|
function testExpandXOF(hash, vectors) {
|
||||||
for (let i = 0; i < vectors.tests.length; i++) {
|
describe(`${vectors.hash}/${vectors.DST.length}`, () => {
|
||||||
const t = vectors.tests[i];
|
for (let i = 0; i < vectors.tests.length; i++) {
|
||||||
should(`expand_message_xof/${vectors.hash}/${vectors.DST.length}/${i}`, () => {
|
const t = vectors.tests[i];
|
||||||
const p = expand_message_xof(
|
should(`${i}`, () => {
|
||||||
stringToBytes(t.msg),
|
const p = expand_message_xof(
|
||||||
stringToBytes(vectors.DST),
|
utf8ToBytes(t.msg),
|
||||||
+t.len_in_bytes,
|
utf8ToBytes(vectors.DST),
|
||||||
vectors.k,
|
Number.parseInt(t.len_in_bytes),
|
||||||
hash
|
vectors.k,
|
||||||
);
|
hash
|
||||||
deepStrictEqual(bytesToHex(p), t.uniform_bytes);
|
);
|
||||||
});
|
deepStrictEqual(bytesToHex(p), t.uniform_bytes);
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
testExpandXOF(shake128, xof_shake128_36);
|
describe('expand_message_xof', () => {
|
||||||
testExpandXOF(shake128, xof_shake128_256);
|
testExpandXOF(shake128, xof_shake128_36);
|
||||||
testExpandXOF(shake256, xof_shake256_36);
|
testExpandXOF(shake128, xof_shake128_256);
|
||||||
|
testExpandXOF(shake256, xof_shake256_36);
|
||||||
|
});
|
||||||
|
|
||||||
function stringToFp(s) {
|
function stringToFp(s) {
|
||||||
// bls-G2 support
|
// bls-G2 support
|
||||||
@@ -99,32 +104,39 @@ function stringToFp(s) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function testCurve(curve, ro, nu) {
|
function testCurve(curve, ro, nu) {
|
||||||
for (let i = 0; i < ro.vectors.length; i++) {
|
describe(`${ro.curve}/${ro.ciphersuite}`, () => {
|
||||||
const t = ro.vectors[i];
|
for (let i = 0; i < ro.vectors.length; i++) {
|
||||||
should(`${ro.curve}/${ro.ciphersuite}(${i})`, () => {
|
const t = ro.vectors[i];
|
||||||
const p = curve.Point.hashToCurve(stringToBytes(t.msg), {
|
should(`(${i})`, () => {
|
||||||
DST: ro.dst,
|
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');
|
||||||
});
|
});
|
||||||
deepStrictEqual(p.x, stringToFp(t.P.x), 'Px');
|
}
|
||||||
deepStrictEqual(p.y, stringToFp(t.P.y), 'Py');
|
});
|
||||||
});
|
describe(`${nu.curve}/${nu.ciphersuite}`, () => {
|
||||||
}
|
for (let i = 0; i < nu.vectors.length; i++) {
|
||||||
for (let i = 0; i < nu.vectors.length; i++) {
|
const t = nu.vectors[i];
|
||||||
const t = nu.vectors[i];
|
should(`(${i})`, () => {
|
||||||
should(`${nu.curve}/${nu.ciphersuite}(${i})`, () => {
|
const p = curve
|
||||||
const p = curve.Point.encodeToCurve(stringToBytes(t.msg), {
|
.encodeToCurve(utf8ToBytes(t.msg), {
|
||||||
DST: nu.dst,
|
DST: nu.dst,
|
||||||
|
})
|
||||||
|
.toAffine();
|
||||||
|
deepStrictEqual(p.x, stringToFp(t.P.x), 'Px');
|
||||||
|
deepStrictEqual(p.y, stringToFp(t.P.y), 'Py');
|
||||||
});
|
});
|
||||||
deepStrictEqual(p.x, stringToFp(t.P.x), 'Px');
|
}
|
||||||
deepStrictEqual(p.y, stringToFp(t.P.y), 'Py');
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
testCurve(secp256r1, p256_ro, p256_nu);
|
testCurve(secp256r1, p256_ro, p256_nu);
|
||||||
testCurve(secp384r1, p384_ro, p384_nu);
|
testCurve(secp384r1, p384_ro, p384_nu);
|
||||||
testCurve(secp521r1, p521_ro, p521_nu);
|
testCurve(secp521r1, p521_ro, p521_nu);
|
||||||
// TODO: remove same tests from bls12
|
|
||||||
testCurve(bls12_381.G1, g1_ro, g1_nu);
|
testCurve(bls12_381.G1, g1_ro, g1_nu);
|
||||||
testCurve(bls12_381.G2, g2_ro, g2_nu);
|
testCurve(bls12_381.G2, g2_ro, g2_nu);
|
||||||
testCurve(secp256k1, secp256k1_ro, secp256k1_nu);
|
testCurve(secp256k1, secp256k1_ro, secp256k1_nu);
|
||||||
|
|||||||
@@ -6,9 +6,10 @@ import './nist.test.js';
|
|||||||
import './ed448.test.js';
|
import './ed448.test.js';
|
||||||
import './ed25519.test.js';
|
import './ed25519.test.js';
|
||||||
import './secp256k1.test.js';
|
import './secp256k1.test.js';
|
||||||
import './stark/stark.test.js';
|
import './secp256k1-schnorr.test.js';
|
||||||
import './jubjub.test.js';
|
import './jubjub.test.js';
|
||||||
import './bls12-381.test.js';
|
|
||||||
import './hash-to-curve.test.js';
|
import './hash-to-curve.test.js';
|
||||||
|
import './poseidon.test.js';
|
||||||
|
import './bls12-381.test.js';
|
||||||
|
|
||||||
should.run();
|
should.run();
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import { jubjub, findGroupHash } from '../lib/esm/jubjub.js';
|
import { jubjub, findGroupHash } from '../esm/jubjub.js';
|
||||||
import { should } from 'micro-should';
|
import { describe, should } from 'micro-should';
|
||||||
import { deepStrictEqual, throws } from 'assert';
|
import { deepStrictEqual, throws } from 'assert';
|
||||||
import { hexToBytes, bytesToHex } from '@noble/hashes/utils';
|
const Point = jubjub.ExtendedPoint;
|
||||||
|
|
||||||
const G_SPEND = new jubjub.ExtendedPoint(
|
const G_SPEND = new Point(
|
||||||
0x055f1f24f0f0512287e51c3c5a0a6903fc0baf8711de9eafd7c0e66f69d8d2dbn,
|
0x055f1f24f0f0512287e51c3c5a0a6903fc0baf8711de9eafd7c0e66f69d8d2dbn,
|
||||||
0x566178b2505fdd52132a5007d80a04652842e78ffb376897588f406278214ed7n,
|
0x566178b2505fdd52132a5007d80a04652842e78ffb376897588f406278214ed7n,
|
||||||
0x0141fafa1f11088a3b2007c14d652375888f3b37838ba6bdffae096741ceddfen,
|
0x0141fafa1f11088a3b2007c14d652375888f3b37838ba6bdffae096741ceddfen,
|
||||||
0x12eada93c0b7d595f5f04f5ebfb4b7d033ef2884136475cab5e41ce17db5be9cn
|
0x12eada93c0b7d595f5f04f5ebfb4b7d033ef2884136475cab5e41ce17db5be9cn
|
||||||
);
|
);
|
||||||
const G_PROOF = new jubjub.ExtendedPoint(
|
const G_PROOF = new Point(
|
||||||
0x0174d54ce9fad258a2f8a86a1deabf15c7a2b51106b0fbcd9d29020f78936f71n,
|
0x0174d54ce9fad258a2f8a86a1deabf15c7a2b51106b0fbcd9d29020f78936f71n,
|
||||||
0x16871d6d877dcd222e4ec3bccb3f37cb1865a2d37dd3a5dcbc032a69b62b4445n,
|
0x16871d6d877dcd222e4ec3bccb3f37cb1865a2d37dd3a5dcbc032a69b62b4445n,
|
||||||
0x57a3cd31e496d82bd4aa78bd5ecd751cfb76d54a5d3f4560866379f9fc11c9b3n,
|
0x57a3cd31e496d82bd4aa78bd5ecd751cfb76d54a5d3f4560866379f9fc11c9b3n,
|
||||||
@@ -18,53 +18,61 @@ const G_PROOF = new jubjub.ExtendedPoint(
|
|||||||
|
|
||||||
const getXY = (p) => ({ x: p.x, y: p.y });
|
const getXY = (p) => ({ x: p.x, y: p.y });
|
||||||
|
|
||||||
should('toHex/fromHex', () => {
|
describe('jubjub', () => {
|
||||||
// More than field
|
should('toHex/fromHex', () => {
|
||||||
throws(() =>
|
// More than field
|
||||||
jubjub.Point.fromHex(
|
throws(() =>
|
||||||
|
Point.fromHex(
|
||||||
|
new Uint8Array([
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
])
|
||||||
|
)
|
||||||
|
);
|
||||||
|
// Multiplicative generator (sqrt == null), not on curve.
|
||||||
|
throws(() =>
|
||||||
|
Point.fromHex(
|
||||||
|
new Uint8Array([
|
||||||
|
7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0,
|
||||||
|
])
|
||||||
|
)
|
||||||
|
);
|
||||||
|
const tmp = Point.fromHex(
|
||||||
new Uint8Array([
|
new Uint8Array([
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
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,
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
|
||||||
])
|
|
||||||
)
|
|
||||||
);
|
|
||||||
// Multiplicative generator (sqrt == null), not on curve.
|
|
||||||
throws(() =>
|
|
||||||
jubjub.Point.fromHex(
|
|
||||||
new Uint8Array([
|
|
||||||
7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0,
|
0,
|
||||||
])
|
])
|
||||||
)
|
);
|
||||||
);
|
deepStrictEqual(tmp.x, 0x8d51ccce760304d0ec030002760300000001000000000000n);
|
||||||
const tmp = jubjub.Point.fromHex(
|
deepStrictEqual(tmp.y, 0n);
|
||||||
new Uint8Array([
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0,
|
|
||||||
])
|
|
||||||
);
|
|
||||||
deepStrictEqual(tmp.x, 0x8d51ccce760304d0ec030002760300000001000000000000n);
|
|
||||||
deepStrictEqual(tmp.y, 0n);
|
|
||||||
|
|
||||||
const S = G_SPEND.toAffine().toRawBytes();
|
const S = G_SPEND.toRawBytes();
|
||||||
const S2 = G_SPEND.double().toAffine().toRawBytes();
|
const S2 = G_SPEND.double().toRawBytes();
|
||||||
const P = G_PROOF.toAffine().toRawBytes();
|
const P = G_PROOF.toRawBytes();
|
||||||
const P2 = G_PROOF.double().toAffine().toRawBytes();
|
const P2 = G_PROOF.double().toRawBytes();
|
||||||
const S_exp = jubjub.Point.fromHex(S);
|
const S_exp = Point.fromHex(S);
|
||||||
const S2_exp = jubjub.Point.fromHex(S2);
|
const S2_exp = Point.fromHex(S2);
|
||||||
const P_exp = jubjub.Point.fromHex(P);
|
const P_exp = Point.fromHex(P);
|
||||||
const P2_exp = jubjub.Point.fromHex(P2);
|
const P2_exp = Point.fromHex(P2);
|
||||||
deepStrictEqual(getXY(G_SPEND.toAffine()), getXY(S_exp));
|
deepStrictEqual(getXY(G_SPEND.toAffine()), getXY(S_exp));
|
||||||
deepStrictEqual(getXY(G_SPEND.double().toAffine()), getXY(S2_exp));
|
deepStrictEqual(getXY(G_SPEND.double().toAffine()), getXY(S2_exp));
|
||||||
deepStrictEqual(getXY(G_PROOF.toAffine()), getXY(P_exp));
|
deepStrictEqual(getXY(G_PROOF.toAffine()), getXY(P_exp));
|
||||||
deepStrictEqual(getXY(G_PROOF.double().toAffine()), getXY(P2_exp));
|
deepStrictEqual(getXY(G_PROOF.double().toAffine()), getXY(P2_exp));
|
||||||
});
|
});
|
||||||
|
|
||||||
should('Find generators', () => {
|
should('Find generators', () => {
|
||||||
const spend = findGroupHash(new Uint8Array(), new Uint8Array([90, 99, 97, 115, 104, 95, 71, 95]));
|
const spend = findGroupHash(
|
||||||
const proof = findGroupHash(new Uint8Array(), new Uint8Array([90, 99, 97, 115, 104, 95, 72, 95]));
|
new Uint8Array(),
|
||||||
deepStrictEqual(getXY(spend.toAffine()), getXY(G_SPEND.toAffine()));
|
new Uint8Array([90, 99, 97, 115, 104, 95, 71, 95])
|
||||||
deepStrictEqual(getXY(proof.toAffine()), getXY(G_PROOF.toAffine()));
|
);
|
||||||
|
const proof = findGroupHash(
|
||||||
|
new Uint8Array(),
|
||||||
|
new Uint8Array([90, 99, 97, 115, 104, 95, 72, 95])
|
||||||
|
);
|
||||||
|
deepStrictEqual(getXY(spend.toAffine()), getXY(G_SPEND.toAffine()));
|
||||||
|
deepStrictEqual(getXY(proof.toAffine()), getXY(G_PROOF.toAffine()));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// ESM is broken.
|
// ESM is broken.
|
||||||
|
|||||||
@@ -1,29 +1,86 @@
|
|||||||
import { deepStrictEqual, throws } from 'assert';
|
import { deepStrictEqual, throws } from 'assert';
|
||||||
import { should } from 'micro-should';
|
import { describe, should } from 'micro-should';
|
||||||
import { secp192r1, P192 } from '../lib/esm/p192.js';
|
import { secp192r1, secp224r1, p192, p224 } from './_more-curves.helpers.js';
|
||||||
import { secp224r1, P224 } from '../lib/esm/p224.js';
|
import { DER } from '../esm/abstract/weierstrass.js';
|
||||||
import { secp256r1, P256 } from '../lib/esm/p256.js';
|
import { secp256r1, p256 } from '../esm/p256.js';
|
||||||
import { secp384r1, P384 } from '../lib/esm/p384.js';
|
import { secp384r1, p384 } from '../esm/p384.js';
|
||||||
import { secp521r1, P521 } from '../lib/esm/p521.js';
|
import { secp521r1, p521 } from '../esm/p521.js';
|
||||||
import { secp256k1 } from '../lib/esm/secp256k1.js';
|
import { secp256k1 } from '../esm/secp256k1.js';
|
||||||
import { hexToBytes, bytesToHex } from '../lib/esm/abstract/utils.js';
|
import { hexToBytes, bytesToHex } from '../esm/abstract/utils.js';
|
||||||
import { default as ecdsa } from './wycheproof/ecdsa_test.json' assert { type: 'json' };
|
import { default as ecdsa } from './wycheproof/ecdsa_test.json' assert { type: 'json' };
|
||||||
import { default as ecdh } from './wycheproof/ecdh_test.json' assert { type: 'json' };
|
import { default as ecdh } from './wycheproof/ecdh_test.json' assert { type: 'json' };
|
||||||
import { default as rfc6979 } from './fixtures/rfc6979.json' assert { type: 'json' };
|
import { default as rfc6979 } from './vectors/rfc6979.json' assert { type: 'json' };
|
||||||
|
|
||||||
|
import { default as ecdh_secp224r1_test } from './wycheproof/ecdh_secp224r1_test.json' assert { type: 'json' };
|
||||||
|
import { default as ecdh_secp256r1_test } from './wycheproof/ecdh_secp256r1_test.json' assert { type: 'json' };
|
||||||
|
import { default as ecdh_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' };
|
||||||
|
// 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' };
|
||||||
|
import { default as secp224r1_sha3_224_test } from './wycheproof/ecdsa_secp224r1_sha3_224_test.json' assert { type: 'json' };
|
||||||
|
import { default as secp224r1_sha3_256_test } from './wycheproof/ecdsa_secp224r1_sha3_256_test.json' assert { type: 'json' };
|
||||||
|
import { default as secp224r1_sha3_512_test } from './wycheproof/ecdsa_secp224r1_sha3_512_test.json' assert { type: 'json' };
|
||||||
|
import { default as secp224r1_sha512_test } from './wycheproof/ecdsa_secp224r1_sha512_test.json' assert { type: 'json' };
|
||||||
|
import { default as secp224r1_shake128_test } from './wycheproof/ecdsa_secp224r1_shake128_test.json' assert { type: 'json' };
|
||||||
|
|
||||||
|
import { default as secp256k1_sha256_bitcoin_test } from './wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.json' assert { type: 'json' };
|
||||||
|
import { default as secp256k1_sha256_test } from './wycheproof/ecdsa_secp256k1_sha256_test.json' assert { type: 'json' };
|
||||||
|
import { default as secp256k1_sha3_256_test } from './wycheproof/ecdsa_secp256k1_sha3_256_test.json' assert { type: 'json' };
|
||||||
|
import { default as secp256k1_sha3_512_test } from './wycheproof/ecdsa_secp256k1_sha3_512_test.json' assert { type: 'json' };
|
||||||
|
import { default as secp256k1_sha512_test } from './wycheproof/ecdsa_secp256k1_sha512_test.json' assert { type: 'json' };
|
||||||
|
import { default as secp256k1_shake128_test } from './wycheproof/ecdsa_secp256k1_shake128_test.json' assert { type: 'json' };
|
||||||
|
import { default as secp256k1_shake256_test } from './wycheproof/ecdsa_secp256k1_shake256_test.json' assert { type: 'json' };
|
||||||
|
|
||||||
|
import { default as secp256r1_sha256_test } from './wycheproof/ecdsa_secp256r1_sha256_test.json' assert { type: 'json' };
|
||||||
|
import { default as secp256r1_sha3_256_test } from './wycheproof/ecdsa_secp256r1_sha3_256_test.json' assert { type: 'json' };
|
||||||
|
import { default as secp256r1_sha3_512_test } from './wycheproof/ecdsa_secp256r1_sha3_512_test.json' assert { type: 'json' };
|
||||||
|
import { default as secp256r1_sha512_test } from './wycheproof/ecdsa_secp256r1_sha512_test.json' assert { type: 'json' };
|
||||||
|
import { default as secp256r1_shake128_test } from './wycheproof/ecdsa_secp256r1_shake128_test.json' assert { type: 'json' };
|
||||||
|
|
||||||
|
import { default as secp384r1_sha384_test } from './wycheproof/ecdsa_secp384r1_sha384_test.json' assert { type: 'json' };
|
||||||
|
import { default as secp384r1_sha3_384_test } from './wycheproof/ecdsa_secp384r1_sha3_384_test.json' assert { type: 'json' };
|
||||||
|
import { default as secp384r1_sha3_512_test } from './wycheproof/ecdsa_secp384r1_sha3_512_test.json' assert { type: 'json' };
|
||||||
|
import { default as secp384r1_sha512_test } from './wycheproof/ecdsa_secp384r1_sha512_test.json' assert { type: 'json' };
|
||||||
|
import { default as secp384r1_shake256_test } from './wycheproof/ecdsa_secp384r1_shake256_test.json' assert { type: 'json' };
|
||||||
|
|
||||||
|
import { default as secp521r1_sha3_512_test } from './wycheproof/ecdsa_secp521r1_sha3_512_test.json' assert { type: 'json' };
|
||||||
|
import { default as secp521r1_sha512_test } from './wycheproof/ecdsa_secp521r1_sha512_test.json' assert { type: 'json' };
|
||||||
|
import { default as secp521r1_shake256_test } from './wycheproof/ecdsa_secp521r1_shake256_test.json' assert { type: 'json' };
|
||||||
|
|
||||||
|
import { sha3_224, sha3_256, sha3_384, sha3_512, shake128, shake256 } from '@noble/hashes/sha3';
|
||||||
|
import { sha512, sha384 } from '@noble/hashes/sha512';
|
||||||
|
import { sha224, sha256 } from '@noble/hashes/sha256';
|
||||||
|
|
||||||
|
// TODO: maybe add to noble-hashes?
|
||||||
|
const wrapShake = (shake, dkLen) => {
|
||||||
|
const hashC = (msg) => shake(msg, { dkLen });
|
||||||
|
hashC.outputLen = dkLen;
|
||||||
|
hashC.blockLen = shake.blockLen;
|
||||||
|
hashC.create = () => shake.create({ dkLen });
|
||||||
|
return hashC;
|
||||||
|
};
|
||||||
|
const shake128_224 = wrapShake(shake128, 224 / 8);
|
||||||
|
const shake128_256 = wrapShake(shake128, 256 / 8);
|
||||||
|
const shake256_256 = wrapShake(shake256, 256 / 8);
|
||||||
|
const shake256_384 = wrapShake(shake256, 384 / 8);
|
||||||
|
const shake256_512 = wrapShake(shake256, 512 / 8);
|
||||||
|
|
||||||
const hex = bytesToHex;
|
const hex = bytesToHex;
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
const NIST = {
|
const NIST = {
|
||||||
secp192r1, P192,
|
secp192r1, P192: p192,
|
||||||
secp224r1, P224,
|
secp224r1, P224: p224,
|
||||||
secp256r1, P256,
|
secp256r1, P256: p256,
|
||||||
secp384r1, P384,
|
secp384r1, P384: p384,
|
||||||
secp521r1, P521,
|
secp521r1, P521: p521,
|
||||||
secp256k1,
|
secp256k1,
|
||||||
};
|
};
|
||||||
|
|
||||||
should('Curve Fields', () => {
|
describe('NIST curves', () => {});
|
||||||
|
should('fields', () => {
|
||||||
const vectors = {
|
const vectors = {
|
||||||
secp192r1: 0xfffffffffffffffffffffffffffffffeffffffffffffffffn,
|
secp192r1: 0xfffffffffffffffffffffffffffffffeffffffffffffffffn,
|
||||||
secp224r1: 0xffffffffffffffffffffffffffffffff000000000000000000000001n,
|
secp224r1: 0xffffffffffffffffffffffffffffffff000000000000000000000001n,
|
||||||
@@ -37,171 +94,96 @@ should('Curve Fields', () => {
|
|||||||
for (const n in vectors) deepStrictEqual(NIST[n].CURVE.Fp.ORDER, vectors[n]);
|
for (const n in vectors) deepStrictEqual(NIST[n].CURVE.Fp.ORDER, vectors[n]);
|
||||||
});
|
});
|
||||||
|
|
||||||
should('wychenproof ECDSA vectors', () => {
|
describe('wycheproof ECDH', () => {
|
||||||
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) {
|
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];
|
const CURVE = NIST[group.curve];
|
||||||
if (!CURVE) continue;
|
if (!CURVE) continue;
|
||||||
for (const test of group.tests) {
|
should(group.curve, () => {
|
||||||
if (test.result === 'valid' || test.result === 'acceptable') {
|
for (const test of group.tests) {
|
||||||
try {
|
if (test.result === 'valid' || test.result === 'acceptable') {
|
||||||
const pub = CURVE.Point.fromHex(test.public);
|
try {
|
||||||
} catch (e) {
|
const pub = CURVE.ProjectivePoint.fromHex(test.public);
|
||||||
if (e.message.includes('Point.fromHex: received invalid point.')) continue;
|
} catch (e) {
|
||||||
throw e;
|
// Our strict validation filter doesn't let weird-length DER vectors
|
||||||
}
|
if (e.message.startsWith('Point of length')) continue;
|
||||||
const shared = CURVE.getSharedSecret(test.private, test.public);
|
throw e;
|
||||||
deepStrictEqual(shared, test.shared, 'valid');
|
}
|
||||||
} else if (test.result === 'invalid') {
|
const shared = CURVE.getSharedSecret(test.private, test.public);
|
||||||
let failed = false;
|
deepStrictEqual(shared, test.shared, 'valid');
|
||||||
try {
|
} else if (test.result === 'invalid') {
|
||||||
CURVE.getSharedSecret(test.private, test.public);
|
let failed = false;
|
||||||
} catch (error) {
|
try {
|
||||||
failed = true;
|
CURVE.getSharedSecret(test.private, test.public);
|
||||||
}
|
} catch (error) {
|
||||||
deepStrictEqual(failed, true, 'invalid');
|
failed = true;
|
||||||
} else throw new Error('unknown test result');
|
}
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
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' };
|
|
||||||
import { default as secp224r1_sha3_224_test } from './wycheproof/ecdsa_secp224r1_sha3_224_test.json' assert { type: 'json' };
|
|
||||||
import { default as secp224r1_sha3_256_test } from './wycheproof/ecdsa_secp224r1_sha3_256_test.json' assert { type: 'json' };
|
|
||||||
import { default as secp224r1_sha3_512_test } from './wycheproof/ecdsa_secp224r1_sha3_512_test.json' assert { type: 'json' };
|
|
||||||
import { default as secp224r1_sha512_test } from './wycheproof/ecdsa_secp224r1_sha512_test.json' assert { type: 'json' };
|
|
||||||
|
|
||||||
import { default as secp256k1_sha256_test } from './wycheproof/ecdsa_secp256k1_sha256_test.json' assert { type: 'json' };
|
|
||||||
import { default as secp256k1_sha3_256_test } from './wycheproof/ecdsa_secp256k1_sha3_256_test.json' assert { type: 'json' };
|
|
||||||
import { default as secp256k1_sha3_512_test } from './wycheproof/ecdsa_secp256k1_sha3_512_test.json' assert { type: 'json' };
|
|
||||||
import { default as secp256k1_sha512_test } from './wycheproof/ecdsa_secp256k1_sha512_test.json' assert { type: 'json' };
|
|
||||||
|
|
||||||
import { default as secp256r1_sha256_test } from './wycheproof/ecdsa_secp256r1_sha256_test.json' assert { type: 'json' };
|
|
||||||
import { default as secp256r1_sha3_256_test } from './wycheproof/ecdsa_secp256r1_sha3_256_test.json' assert { type: 'json' };
|
|
||||||
import { default as secp256r1_sha3_512_test } from './wycheproof/ecdsa_secp256r1_sha3_512_test.json' assert { type: 'json' };
|
|
||||||
import { default as secp256r1_sha512_test } from './wycheproof/ecdsa_secp256r1_sha512_test.json' assert { type: 'json' };
|
|
||||||
|
|
||||||
import { default as secp384r1_sha384_test } from './wycheproof/ecdsa_secp384r1_sha384_test.json' assert { type: 'json' };
|
|
||||||
import { default as secp384r1_sha3_384_test } from './wycheproof/ecdsa_secp384r1_sha3_384_test.json' assert { type: 'json' };
|
|
||||||
import { default as secp384r1_sha3_512_test } from './wycheproof/ecdsa_secp384r1_sha3_512_test.json' assert { type: 'json' };
|
|
||||||
import { default as secp384r1_sha512_test } from './wycheproof/ecdsa_secp384r1_sha512_test.json' assert { type: 'json' };
|
|
||||||
|
|
||||||
import { default as secp521r1_sha3_512_test } from './wycheproof/ecdsa_secp521r1_sha3_512_test.json' assert { type: 'json' };
|
|
||||||
import { default as secp521r1_sha512_test } from './wycheproof/ecdsa_secp521r1_sha512_test.json' assert { type: 'json' };
|
|
||||||
|
|
||||||
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 WYCHEPROOF_ECDSA = {
|
const WYCHEPROOF_ECDSA = {
|
||||||
P224: {
|
p224: {
|
||||||
curve: P224,
|
curve: p224,
|
||||||
hashes: {
|
hashes: {
|
||||||
sha224: {
|
sha224: {
|
||||||
hash: sha224,
|
hash: sha224,
|
||||||
@@ -227,15 +209,18 @@ const WYCHEPROOF_ECDSA = {
|
|||||||
hash: sha512,
|
hash: sha512,
|
||||||
tests: [secp224r1_sha512_test],
|
tests: [secp224r1_sha512_test],
|
||||||
},
|
},
|
||||||
|
shake128: {
|
||||||
|
hash: shake128_224,
|
||||||
|
tests: [secp224r1_shake128_test],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
secp256k1: {
|
secp256k1: {
|
||||||
curve: secp256k1,
|
curve: secp256k1,
|
||||||
hashes: {
|
hashes: {
|
||||||
// TODO: debug why fails, can be bug
|
|
||||||
sha256: {
|
sha256: {
|
||||||
hash: sha256,
|
hash: sha256,
|
||||||
tests: [secp256k1_sha256_test],
|
tests: [secp256k1_sha256_test, secp256k1_sha256_bitcoin_test],
|
||||||
},
|
},
|
||||||
sha3_256: {
|
sha3_256: {
|
||||||
hash: sha3_256,
|
hash: sha3_256,
|
||||||
@@ -249,10 +234,18 @@ const WYCHEPROOF_ECDSA = {
|
|||||||
hash: sha512,
|
hash: sha512,
|
||||||
tests: [secp256k1_sha512_test],
|
tests: [secp256k1_sha512_test],
|
||||||
},
|
},
|
||||||
|
shake128: {
|
||||||
|
hash: shake128_256,
|
||||||
|
tests: [secp256k1_shake128_test],
|
||||||
|
},
|
||||||
|
shake256: {
|
||||||
|
hash: shake256_256,
|
||||||
|
tests: [secp256k1_shake256_test],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
P256: {
|
p256: {
|
||||||
curve: P256,
|
curve: p256,
|
||||||
hashes: {
|
hashes: {
|
||||||
sha256: {
|
sha256: {
|
||||||
hash: sha256,
|
hash: sha256,
|
||||||
@@ -270,10 +263,14 @@ const WYCHEPROOF_ECDSA = {
|
|||||||
hash: sha512,
|
hash: sha512,
|
||||||
tests: [secp256r1_sha512_test],
|
tests: [secp256r1_sha512_test],
|
||||||
},
|
},
|
||||||
|
shake128: {
|
||||||
|
hash: shake128_256,
|
||||||
|
tests: [secp256r1_shake128_test],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
P384: {
|
p384: {
|
||||||
curve: P384,
|
curve: p384,
|
||||||
hashes: {
|
hashes: {
|
||||||
sha384: {
|
sha384: {
|
||||||
hash: sha384,
|
hash: sha384,
|
||||||
@@ -291,10 +288,14 @@ const WYCHEPROOF_ECDSA = {
|
|||||||
hash: sha512,
|
hash: sha512,
|
||||||
tests: [secp384r1_sha512_test],
|
tests: [secp384r1_sha512_test],
|
||||||
},
|
},
|
||||||
|
shake256: {
|
||||||
|
hash: shake256_384,
|
||||||
|
tests: [secp384r1_shake256_test],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
P521: {
|
p521: {
|
||||||
curve: P521,
|
curve: p521,
|
||||||
hashes: {
|
hashes: {
|
||||||
sha3_512: {
|
sha3_512: {
|
||||||
hash: sha3_512,
|
hash: sha3_512,
|
||||||
@@ -304,36 +305,42 @@ const WYCHEPROOF_ECDSA = {
|
|||||||
hash: sha512,
|
hash: sha512,
|
||||||
tests: [secp521r1_sha512_test],
|
tests: [secp521r1_sha512_test],
|
||||||
},
|
},
|
||||||
|
shake256: {
|
||||||
|
hash: shake256_512,
|
||||||
|
tests: [secp521r1_shake256_test],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
function runWycheproof(name, CURVE, group, index) {
|
function runWycheproof(name, CURVE, group, index) {
|
||||||
const pubKey = CURVE.Point.fromHex(group.key.uncompressed);
|
const key = group.publicKey;
|
||||||
deepStrictEqual(pubKey.x, BigInt(`0x${group.key.wx}`));
|
const pubKey = CURVE.ProjectivePoint.fromHex(key.uncompressed);
|
||||||
deepStrictEqual(pubKey.y, BigInt(`0x${group.key.wy}`));
|
deepStrictEqual(pubKey.x, BigInt(`0x${key.wx}`));
|
||||||
|
deepStrictEqual(pubKey.y, BigInt(`0x${key.wy}`));
|
||||||
|
const pubR = pubKey.toRawBytes();
|
||||||
for (const test of group.tests) {
|
for (const test of group.tests) {
|
||||||
const m = CURVE.CURVE.hash(hexToBytes(test.msg));
|
const m = CURVE.CURVE.hash(hexToBytes(test.msg));
|
||||||
|
const { sig } = test;
|
||||||
if (test.result === 'valid' || test.result === 'acceptable') {
|
if (test.result === 'valid' || test.result === 'acceptable') {
|
||||||
try {
|
try {
|
||||||
CURVE.Signature.fromDER(test.sig);
|
CURVE.Signature.fromDER(sig);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Some tests has invalid signature which we don't accept
|
// Some tests has invalid signature which we don't accept
|
||||||
if (e.message.includes('Invalid signature: incorrect length')) continue;
|
if (e.message.includes('Invalid signature: incorrect length')) continue;
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
const verified = CURVE.verify(test.sig, m, pubKey);
|
const verified = CURVE.verify(sig, m, pubR);
|
||||||
if (name === 'secp256k1') {
|
if (name === 'secp256k1') {
|
||||||
// lowS: true for secp256k1
|
// lowS: true for secp256k1
|
||||||
deepStrictEqual(verified, !CURVE.Signature.fromDER(test.sig).hasHighS(), `${index}: valid`);
|
deepStrictEqual(verified, !CURVE.Signature.fromDER(sig).hasHighS(), `${index}: valid`);
|
||||||
} else {
|
} else {
|
||||||
deepStrictEqual(verified, true, `${index}: valid`);
|
deepStrictEqual(verified, true, `${index}: valid`);
|
||||||
}
|
}
|
||||||
} else if (test.result === 'invalid') {
|
} else if (test.result === 'invalid') {
|
||||||
let failed = false;
|
let failed = false;
|
||||||
try {
|
try {
|
||||||
failed = !CURVE.verify(test.sig, m, pubKey);
|
failed = !CURVE.verify(sig, m, pubR);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
failed = true;
|
failed = true;
|
||||||
}
|
}
|
||||||
@@ -342,43 +349,122 @@ function runWycheproof(name, CURVE, group, index) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const name in WYCHEPROOF_ECDSA) {
|
describe('wycheproof ECDSA', () => {
|
||||||
const { curve, hashes } = WYCHEPROOF_ECDSA[name];
|
should('generic', () => {
|
||||||
for (const hName in hashes) {
|
for (const group of ecdsa.testGroups) {
|
||||||
const { hash, tests } = hashes[hName];
|
// Tested in secp256k1.test.js
|
||||||
const CURVE = curve.create(hash);
|
let CURVE = NIST[group.key.curve];
|
||||||
should(`Wycheproof/WYCHEPROOF_ECDSA ${name}/${hName}`, () => {
|
if (!CURVE) continue;
|
||||||
for (let i = 0; i < tests.length; i++) {
|
if (group.key.curve === 'secp224r1' && group.sha !== 'SHA-224') {
|
||||||
const groups = tests[i].testGroups;
|
if (group.sha === 'SHA-256') CURVE = CURVE.create(sha256);
|
||||||
for (let j = 0; j < groups.length; j++) {
|
}
|
||||||
const group = groups[j];
|
const pubKey = CURVE.ProjectivePoint.fromHex(group.key.uncompressed);
|
||||||
runWycheproof(name, CURVE, group, `${i}/${j}`);
|
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;
|
||||||
}
|
}
|
||||||
|
// These old Wycheproof vectors which still accept missing zero, new one is not.
|
||||||
|
if (test.flags.includes('MissingZero') && test.result === 'acceptable')
|
||||||
|
test.result = 'invalid';
|
||||||
|
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());
|
||||||
|
if (group.key.curve === 'secp256k1') {
|
||||||
|
// lowS: true for secp256k1
|
||||||
|
deepStrictEqual(verified, !CURVE.Signature.fromDER(test.sig).hasHighS(), `valid`);
|
||||||
|
} else {
|
||||||
|
deepStrictEqual(verified, true, `valid`);
|
||||||
|
}
|
||||||
|
} else if (test.result === 'invalid') {
|
||||||
|
let failed = false;
|
||||||
|
try {
|
||||||
|
failed = !CURVE.verify(test.sig, m, pubKey.toHex());
|
||||||
|
} catch (error) {
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
|
deepStrictEqual(failed, true, 'invalid');
|
||||||
|
} else throw new Error('unknown test result');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
for (const name in WYCHEPROOF_ECDSA) {
|
||||||
|
const { curve, hashes } = WYCHEPROOF_ECDSA[name];
|
||||||
|
describe(name, () => {
|
||||||
|
for (const hName in hashes) {
|
||||||
|
const { hash, tests } = hashes[hName];
|
||||||
|
const CURVE = curve.create(hash);
|
||||||
|
should(`${name}/${hName}`, () => {
|
||||||
|
for (let i = 0; i < tests.length; i++) {
|
||||||
|
const groups = tests[i].testGroups;
|
||||||
|
for (let j = 0; j < groups.length; j++) {
|
||||||
|
const group = groups[j];
|
||||||
|
runWycheproof(name, CURVE, group, `${i}/${j}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
const hexToBigint = (hex) => BigInt(`0x${hex}`);
|
const hexToBigint = (hex) => BigInt(`0x${hex}`);
|
||||||
should('RFC6979', () => {
|
describe('RFC6979', () => {
|
||||||
for (const v of rfc6979) {
|
for (const v of rfc6979) {
|
||||||
const curve = NIST[v.curve];
|
should(v.curve, () => {
|
||||||
deepStrictEqual(curve.CURVE.n, hexToBigint(v.q));
|
const curve = NIST[v.curve];
|
||||||
const pubKey = curve.getPublicKey(v.private);
|
deepStrictEqual(curve.CURVE.n, hexToBigint(v.q));
|
||||||
const pubPoint = curve.Point.fromHex(pubKey);
|
const pubKey = curve.getPublicKey(v.private);
|
||||||
deepStrictEqual(pubPoint.x, hexToBigint(v.Ux));
|
const pubPoint = curve.ProjectivePoint.fromHex(pubKey);
|
||||||
deepStrictEqual(pubPoint.y, hexToBigint(v.Uy));
|
deepStrictEqual(pubPoint.x, hexToBigint(v.Ux));
|
||||||
for (const c of v.cases) {
|
deepStrictEqual(pubPoint.y, hexToBigint(v.Uy));
|
||||||
const h = curve.CURVE.hash(c.message);
|
for (const c of v.cases) {
|
||||||
const sigObj = curve.sign(h, v.private);
|
const h = curve.CURVE.hash(c.message);
|
||||||
deepStrictEqual(sigObj.r, hexToBigint(c.r), 'R');
|
const sigObj = curve.sign(h, v.private);
|
||||||
deepStrictEqual(sigObj.s, hexToBigint(c.s), 'S');
|
deepStrictEqual(sigObj.r, hexToBigint(c.r), 'R');
|
||||||
deepStrictEqual(curve.verify(sigObj.toDERRawBytes(), h, pubKey), true, 'verify(1)');
|
deepStrictEqual(sigObj.s, hexToBigint(c.s), 'S');
|
||||||
deepStrictEqual(curve.verify(sigObj, h, pubKey), true, 'verify(2)');
|
deepStrictEqual(curve.verify(sigObj.toDERRawBytes(), h, pubKey), true, 'verify(1)');
|
||||||
}
|
deepStrictEqual(curve.verify(sigObj, h, pubKey), true, 'verify(2)');
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
should('DER Leading zero', () => {
|
||||||
|
// Valid DER
|
||||||
|
deepStrictEqual(
|
||||||
|
DER.toSig(
|
||||||
|
'303c021c70049af31f8348673d56cece2b27e587a402f2a48f0b21a7911a480a021c2840bf24f6f66be287066b7cbf38788e1b7770b18fd1aa6a26d7c6dc'
|
||||||
|
),
|
||||||
|
{
|
||||||
|
r: 11796871166002955884468185727465595477481802908758874298363724580874n,
|
||||||
|
s: 4239126896857047637966364941684493209162496401998708914961872570076n,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
// Invalid DER (missing trailing zero)
|
||||||
|
throws(() =>
|
||||||
|
DER.toSig(
|
||||||
|
'303c021c70049af31f8348673d56cece2b27e587a402f2a48f0b21a7911a480a021cd7bf40db0909941d78f9948340c69e14c5417f8c840b7edb35846361'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
// Correctly adds trailing zero
|
||||||
|
deepStrictEqual(
|
||||||
|
DER.hexFromSig({
|
||||||
|
r: 11796871166002955884468185727465595477481802908758874298363724580874n,
|
||||||
|
s: 22720819770293592156700650145335132731295311312425682806720849797985n,
|
||||||
|
}),
|
||||||
|
'303d021c70049af31f8348673d56cece2b27e587a402f2a48f0b21a7911a480a021d00d7bf40db0909941d78f9948340c69e14c5417f8c840b7edb35846361'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
// ESM is broken.
|
// ESM is broken.
|
||||||
import url from 'url';
|
import url from 'url';
|
||||||
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
|
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
|
||||||
|
|||||||
377
test/poseidon.test.js
Normal file
377
test/poseidon.test.js
Normal file
@@ -0,0 +1,377 @@
|
|||||||
|
import { deepStrictEqual, throws } from 'assert';
|
||||||
|
import { should, describe } from 'micro-should';
|
||||||
|
import * as poseidon from '../esm/abstract/poseidon.js';
|
||||||
|
import * as stark from './_poseidon.helpers.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.Field(
|
||||||
|
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.Field(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.Field(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.Field(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/secp256k1/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();
|
||||||
|
}
|
||||||
11
test/secp256k1.helpers.js
Normal file
11
test/secp256k1.helpers.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
// @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);
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,200 +0,0 @@
|
|||||||
import { deepStrictEqual, throws } from 'assert';
|
|
||||||
import { should } from 'micro-should';
|
|
||||||
import * as starknet from '../../lib/esm/stark.js';
|
|
||||||
import { default as issue2 } from './fixtures/issue2.json' assert { type: 'json' };
|
|
||||||
|
|
||||||
should('Basic elliptic sanity check', () => {
|
|
||||||
const g1 = starknet.Point.BASE;
|
|
||||||
deepStrictEqual(
|
|
||||||
g1.x.toString(16),
|
|
||||||
'1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca'
|
|
||||||
);
|
|
||||||
deepStrictEqual(
|
|
||||||
g1.y.toString(16),
|
|
||||||
'5668060aa49730b7be4801df46ec62de53ecd11abe43a32873000c36e8dc1f'
|
|
||||||
);
|
|
||||||
const g2 = g1.double();
|
|
||||||
deepStrictEqual(
|
|
||||||
g2.x.toString(16),
|
|
||||||
'759ca09377679ecd535a81e83039658bf40959283187c654c5416f439403cf5'
|
|
||||||
);
|
|
||||||
deepStrictEqual(
|
|
||||||
g2.y.toString(16),
|
|
||||||
'6f524a3400e7708d5c01a28598ad272e7455aa88778b19f93b562d7a9646c41'
|
|
||||||
);
|
|
||||||
const g3 = g2.add(g1);
|
|
||||||
deepStrictEqual(
|
|
||||||
g3.x.toString(16),
|
|
||||||
'411494b501a98abd8262b0da1351e17899a0c4ef23dd2f96fec5ba847310b20'
|
|
||||||
);
|
|
||||||
deepStrictEqual(
|
|
||||||
g3.y.toString(16),
|
|
||||||
'7e1b3ebac08924d2c26f409549191fcf94f3bf6f301ed3553e22dfb802f0686'
|
|
||||||
);
|
|
||||||
const g32 = g1.multiply(3);
|
|
||||||
deepStrictEqual(
|
|
||||||
g32.x.toString(16),
|
|
||||||
'411494b501a98abd8262b0da1351e17899a0c4ef23dd2f96fec5ba847310b20'
|
|
||||||
);
|
|
||||||
deepStrictEqual(
|
|
||||||
g32.y.toString(16),
|
|
||||||
'7e1b3ebac08924d2c26f409549191fcf94f3bf6f301ed3553e22dfb802f0686'
|
|
||||||
);
|
|
||||||
const minus1 = g1.multiply(starknet.CURVE.n - 1n);
|
|
||||||
deepStrictEqual(
|
|
||||||
minus1.x.toString(16),
|
|
||||||
'1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca'
|
|
||||||
);
|
|
||||||
deepStrictEqual(
|
|
||||||
minus1.y.toString(16),
|
|
||||||
'7a997f9f55b68e04841b7fe20b9139d21ac132ee541bc5cd78cfff3c91723e2'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
should('Pedersen', () => {
|
|
||||||
deepStrictEqual(
|
|
||||||
starknet.pedersen(2, 3),
|
|
||||||
'0x5774fa77b3d843ae9167abd61cf80365a9b2b02218fc2f628494b5bdc9b33b8'
|
|
||||||
);
|
|
||||||
deepStrictEqual(
|
|
||||||
starknet.pedersen(1, 2),
|
|
||||||
'0x5bb9440e27889a364bcb678b1f679ecd1347acdedcbf36e83494f857cc58026'
|
|
||||||
);
|
|
||||||
deepStrictEqual(
|
|
||||||
starknet.pedersen(3, 4),
|
|
||||||
'0x262697b88544f733e5c6907c3e1763131e9f14c51ee7951258abbfb29415fbf'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
should('Hash chain', () => {
|
|
||||||
deepStrictEqual(
|
|
||||||
starknet.hashChain([1, 2, 3]),
|
|
||||||
'0x5d9d62d4040b977c3f8d2389d494e4e89a96a8b45c44b1368f1cc6ec5418915'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
should('Pedersen hash edgecases', () => {
|
|
||||||
// >>> pedersen_hash(0,0)
|
|
||||||
const zero = '0x49ee3eba8c1600700ee1b87eb599f16716b0b1022947733551fde4050ca6804';
|
|
||||||
deepStrictEqual(starknet.pedersen(0, 0), zero);
|
|
||||||
deepStrictEqual(starknet.pedersen(0n, 0n), zero);
|
|
||||||
deepStrictEqual(starknet.pedersen('0', '0'), zero);
|
|
||||||
deepStrictEqual(starknet.pedersen('0x0', '0x0'), zero);
|
|
||||||
// >>> pedersen_hash(3618502788666131213697322783095070105623107215331596699973092056135872020475,3618502788666131213697322783095070105623107215331596699973092056135872020475)
|
|
||||||
// 3226051580231087455100099637526672350308978851161639703631919449959447036451
|
|
||||||
const big = 3618502788666131213697322783095070105623107215331596699973092056135872020475n;
|
|
||||||
const bigExp = '0x721e167a36655994e88efa865e2ed8a0488d36db4d988fec043cda755728223';
|
|
||||||
deepStrictEqual(starknet.pedersen(big, big), bigExp);
|
|
||||||
// >= FIELD
|
|
||||||
const big2 = 36185027886661312136973227830950701056231072153315966999730920561358720204751n;
|
|
||||||
throws(() => starknet.pedersen(big2, big2), 'big2');
|
|
||||||
|
|
||||||
// FIELD -1
|
|
||||||
const big3 = 3618502788666131213697322783095070105623107215331596699973092056135872020480n;
|
|
||||||
const big3exp = '0x7258fccaf3371fad51b117471d9d888a1786c5694c3e6099160477b593a576e';
|
|
||||||
deepStrictEqual(starknet.pedersen(big3, big3), big3exp, 'big3');
|
|
||||||
// FIELD
|
|
||||||
const big4 = 3618502788666131213697322783095070105623107215331596699973092056135872020481n;
|
|
||||||
throws(() => starknet.pedersen(big4, big4), 'big4');
|
|
||||||
throws(() => starknet.pedersen(-1, -1), 'neg');
|
|
||||||
throws(() => starknet.pedersen(false, false), 'false');
|
|
||||||
throws(() => starknet.pedersen(true, true), 'true');
|
|
||||||
throws(() => starknet.pedersen(10.1, 10.1), 'float');
|
|
||||||
});
|
|
||||||
|
|
||||||
should('hashChain edgecases', () => {
|
|
||||||
deepStrictEqual(starknet.hashChain([32312321312321312312312321n]), '0x1aba6672c014b4838cc201');
|
|
||||||
deepStrictEqual(
|
|
||||||
starknet.hashChain([1n, 2n]),
|
|
||||||
'0x5bb9440e27889a364bcb678b1f679ecd1347acdedcbf36e83494f857cc58026'
|
|
||||||
);
|
|
||||||
deepStrictEqual(
|
|
||||||
starknet.hashChain([1, 2]),
|
|
||||||
'0x5bb9440e27889a364bcb678b1f679ecd1347acdedcbf36e83494f857cc58026'
|
|
||||||
);
|
|
||||||
throws(() => starknet.hashChain([]));
|
|
||||||
throws(() => starknet.hashChain('123'));
|
|
||||||
deepStrictEqual(
|
|
||||||
starknet.hashChain([1, 2]),
|
|
||||||
'0x5bb9440e27889a364bcb678b1f679ecd1347acdedcbf36e83494f857cc58026'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
should('Pedersen hash, issue #2', () => {
|
|
||||||
// Verified with starnet.js
|
|
||||||
deepStrictEqual(
|
|
||||||
starknet.computeHashOnElements(issue2),
|
|
||||||
'0x22064462ea33a6ce5272a295e0f551c5da3834f80d8444e7a4df68190b1bc42'
|
|
||||||
);
|
|
||||||
deepStrictEqual(
|
|
||||||
starknet.computeHashOnElements([]),
|
|
||||||
'0x49ee3eba8c1600700ee1b87eb599f16716b0b1022947733551fde4050ca6804'
|
|
||||||
);
|
|
||||||
deepStrictEqual(
|
|
||||||
starknet.computeHashOnElements([1]),
|
|
||||||
'0x78d74f61aeaa8286418fd34b3a12a610445eba11d00ecc82ecac2542d55f7a4'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
import * as bip32 from '@scure/bip32';
|
|
||||||
import * as bip39 from '@scure/bip39';
|
|
||||||
|
|
||||||
should('Seed derivation (example)', () => {
|
|
||||||
const layer = 'starkex';
|
|
||||||
const application = 'starkdeployement';
|
|
||||||
const mnemonic =
|
|
||||||
'range mountain blast problem vibrant void vivid doctor cluster enough melody ' +
|
|
||||||
'salt layer language laptop boat major space monkey unit glimpse pause change vibrant';
|
|
||||||
const ethAddress = '0xa4864d977b944315389d1765ffa7e66F74ee8cd7';
|
|
||||||
const hdKey = bip32.HDKey.fromMasterSeed(bip39.mnemonicToSeedSync(mnemonic)).derive(
|
|
||||||
starknet.getAccountPath(layer, application, ethAddress, 0)
|
|
||||||
);
|
|
||||||
deepStrictEqual(
|
|
||||||
starknet.grindKey(hdKey.privateKey),
|
|
||||||
'6cf0a8bf113352eb863157a45c5e5567abb34f8d32cddafd2c22aa803f4892c'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
should('Compressed keys', () => {
|
|
||||||
const G = starknet.Point.BASE;
|
|
||||||
const half = starknet.CURVE.n / 2n;
|
|
||||||
const last = starknet.CURVE.n;
|
|
||||||
const vectors = [
|
|
||||||
1,
|
|
||||||
2,
|
|
||||||
3,
|
|
||||||
4,
|
|
||||||
5,
|
|
||||||
half - 5n,
|
|
||||||
half - 4n,
|
|
||||||
half - 3n,
|
|
||||||
half - 2n,
|
|
||||||
half - 1n,
|
|
||||||
half,
|
|
||||||
half + 1n,
|
|
||||||
half + 2n,
|
|
||||||
half + 3n,
|
|
||||||
half + 4n,
|
|
||||||
half + 5n,
|
|
||||||
last - 5n,
|
|
||||||
last - 4n,
|
|
||||||
last - 3n,
|
|
||||||
last - 2n,
|
|
||||||
last - 1n,
|
|
||||||
].map((i) => G.multiply(i));
|
|
||||||
const fixPoint = (pt) => ({ ...pt, _WINDOW_SIZE: undefined });
|
|
||||||
for (const v of vectors) {
|
|
||||||
const uncompressed = v.toHex();
|
|
||||||
const compressed = v.toHex(true);
|
|
||||||
const exp = fixPoint(v);
|
|
||||||
deepStrictEqual(fixPoint(starknet.Point.fromHex(uncompressed)), exp);
|
|
||||||
deepStrictEqual(fixPoint(starknet.Point.fromHex(compressed)), exp);
|
|
||||||
deepStrictEqual(starknet.Point.fromHex(compressed).toHex(), uncompressed);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// ESM is broken.
|
|
||||||
import url from 'url';
|
|
||||||
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
|
|
||||||
should.run();
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
import * as microStark from '../../../lib/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
|
|
||||||
|
|
||||||
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'
|
|
||||||
);
|
|
||||||
const publicKeyMicro = microStark.getPublicKey(privateKey);
|
|
||||||
|
|
||||||
const FNS = {
|
|
||||||
pedersenHash: {
|
|
||||||
samples: 250,
|
|
||||||
starkware: () =>
|
|
||||||
starkwareCrypto.default.pedersen([
|
|
||||||
'3d937c035c878245caf64531a5756109c53068da139362728feb561405371cb',
|
|
||||||
'208a0a10250e382e1e4bbe2880906c2791bf6275695e02fbbc6aeff9cd8b31a',
|
|
||||||
]),
|
|
||||||
'micro-starknet': () =>
|
|
||||||
microStark.pedersen(
|
|
||||||
'3d937c035c878245caf64531a5756109c53068da139362728feb561405371cb',
|
|
||||||
'208a0a10250e382e1e4bbe2880906c2791bf6275695e02fbbc6aeff9cd8b31a'
|
|
||||||
),
|
|
||||||
},
|
|
||||||
signVerify: {
|
|
||||||
samples: 500,
|
|
||||||
starkware: () =>
|
|
||||||
starkwareCrypto.default.verify(
|
|
||||||
publicKeyStark,
|
|
||||||
msgHash,
|
|
||||||
starkwareCrypto.default.sign(keyPair, msgHash)
|
|
||||||
),
|
|
||||||
'micro-starknet': () =>
|
|
||||||
microStark.verify(microStark.sign(msgHash, privateKey), msgHash, publicKeyMicro),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const main = () =>
|
|
||||||
run(async () => {
|
|
||||||
for (let [k, libs] of Object.entries(FNS)) {
|
|
||||||
console.log(`==== ${k} ====`);
|
|
||||||
for (const [lib, fn] of Object.entries(libs)) {
|
|
||||||
if (lib === 'samples') continue;
|
|
||||||
let title = `${k} (${lib})`;
|
|
||||||
await mark(title, libs.samples, () => fn());
|
|
||||||
}
|
|
||||||
console.log();
|
|
||||||
}
|
|
||||||
// Log current RAM
|
|
||||||
bench.logMem();
|
|
||||||
});
|
|
||||||
|
|
||||||
main();
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "benchmark",
|
|
||||||
"private": true,
|
|
||||||
"version": "0.1.0",
|
|
||||||
"description": "benchmarks",
|
|
||||||
"main": "index.js",
|
|
||||||
"type": "module",
|
|
||||||
"scripts": {
|
|
||||||
"bench": "node index.js"
|
|
||||||
},
|
|
||||||
"keywords": [],
|
|
||||||
"author": "",
|
|
||||||
"license": "MIT",
|
|
||||||
"devDependencies": {
|
|
||||||
"@starkware-industries/starkware-crypto-utils": "^0.0.2",
|
|
||||||
"micro-bmark": "0.2.0",
|
|
||||||
"micro-should": "0.2.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,32 +0,0 @@
|
|||||||
{
|
|
||||||
"0x1": "0x1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca",
|
|
||||||
"0x2": "0x759ca09377679ecd535a81e83039658bf40959283187c654c5416f439403cf5",
|
|
||||||
"0x3": "0x411494b501a98abd8262b0da1351e17899a0c4ef23dd2f96fec5ba847310b20",
|
|
||||||
"0x4": "0xa7da05a4d664859ccd6e567b935cdfbfe3018c7771cb980892ef38878ae9bc",
|
|
||||||
"0x5": "0x788435d61046d3eec54d77d25bd194525f4fa26ebe6575536bc6f656656b74c",
|
|
||||||
"0x6": "0x1efc3d7c9649900fcbd03f578a8248d095bc4b6a13b3c25f9886ef971ff96fa",
|
|
||||||
"0x7": "0x743829e0a179f8afe223fc8112dfc8d024ab6b235fd42283c4f5970259ce7b7",
|
|
||||||
"0x8": "0x6eeee2b0c71d681692559735e08a2c3ba04e7347c0c18d4d49b83bb89771591",
|
|
||||||
"0x9": "0x216b4f076ff47e03a05032d1c6ee17933d8de8b2b4c43eb5ad5a7e1b25d3849",
|
|
||||||
"0x800000000000000000000000000000000000000000000000000000000000000": "0x5c79074e7f7b834c12c81a9bb0d46691a5e7517767a849d9d98cb84e2176ed2",
|
|
||||||
"0x800000000000000000000000000000000000000000000000000000000000001": "0x1c4f24e3bd16db0e2457bc005a9d61965105a535554c6b338871e34cb8e2d3a",
|
|
||||||
"0x800000000000000000000000000000000000000000000000000000000000002": "0xdfbb89b39288a9ddacf3942b4481b04d4fa2f8ed3c424757981cc6357f27ac",
|
|
||||||
"0x800000000000000000000000000000000000000000000000000000000000003": "0x41bef28265fd750b102f4f2d1e0231de7f4a33900a214f191a63d4fec4e72f4",
|
|
||||||
"0x800000000000000000000000000000000000000000000000000000000000004": "0x24de66eb164797d4b414e81ded0cfa1a592ef0a9363ebbcb440d4d03cb18af1",
|
|
||||||
"0x800000000000000000000000000000000000000000000000000000000000005": "0x5efb18c3bc9b69003746acc85fb6ee0cfbdc6adfb982f089cc63e1e5495daad",
|
|
||||||
"0x800000000000000000000000000000000000000000000000000000000000006": "0x10dc71f00918a8ebfe4085c834d41dd22b251b9f81eef8b9a4fab77e7e1afe9",
|
|
||||||
"0x800000000000000000000000000000000000000000000000000000000000007": "0x4267ebfd379b1c8caae73febc5920b0c95bd6f9f3536f47c5ddad1259c332ff",
|
|
||||||
"0x800000000000000000000000000000000000000000000000000000000000008": "0x6da515118c8e01fd5b2e96b814ee95bad7d60be4d2ba6b47e0d283f579d9671",
|
|
||||||
"0x800000000000000000000000000000000000000000000000000000000000009": "0x7a5b4797f4e56ed1473876bc2693fbe3f2fef7e050717cbae924ff23d426052",
|
|
||||||
"0x2e9c99d8382fa004dcbbee720aef8a97002de0e991f6a8344e6dc636a71b59e": "0x1ff6803ae740e7e596504ac5c6afbea472e53679361e214f12be0155b13e25d",
|
|
||||||
"0x8620458785138df8722214e073a91b8f55076ea78197cf41007692dd27fd90": "0x5967da40b90d7ca1e36dc4024381d7d4b403c6ac1a0ab358b0743984934a805",
|
|
||||||
"0x1b920e7dfb49ba5ada673882af5342e7448d3e9335e0ac37feb6280cd7289ce": "0x78c7ab46333968fbde3201cf512c1eeb5529360259072c459a158dee4449b57",
|
|
||||||
"0x704170dbfd5dc63caef69d2ce6dfc2b2dbb2af6e75851242bbe79fb6e62a118": "0x534bd8d6ebe4bb2f6992e2d7c19ef3146247e10c2849f357e44eddd283b2af6",
|
|
||||||
"0x4b58bf4228f39550eca59b5c96a0cb606036cc9495eef9a546f24f01b1b7829": "0x1097a8c5a46d94596f1c8e70ca66941f2bb11e3c8d4fd58fdc4589f09965be8",
|
|
||||||
"0x2e93226c90fb7a2381a24e940a94b98433e3553dcbf745d3f54d62963c75604": "0x369f0e8c8e984f244290267393a004dba435a4df091767ad5063fece7b1884c",
|
|
||||||
"0x4615f94598cd756ad1a551d7e57fd725916adfd0054eb773ceb482eef87d0b2": "0x1ee5b8d612102a2408cde59ce52a6498d2e38fe8789bb26d400dea310684ec9",
|
|
||||||
"0x6ade54b7debd7ca1d4e8e932f9545f8fa4024d73be1efcc86df86367fc333f8": "0x37de3bf52412b2fb9b0030d232ca9dd921cd8f71fd67975cdc62546826e121",
|
|
||||||
"0x618e7467dd24c2a3449c4df640439c12cdd0f8ea779afcee6e252b2cf494354": "0x71c2b578c432f2d305d3808bb645ecc46dd670cb43d4f4a076f75ccbff74fbc",
|
|
||||||
"0x7eae185e1f41ec76d214d763f0592f194933622a9dd5f3d52d0209f71619c1a": "0x2b0160052e70176e5b0ff2a6eff90896ae07b732fc27219e36e077735abd57e",
|
|
||||||
"0x178047D3869489C055D7EA54C014FFB834A069C9595186ABE04EA4D1223A03F": "0x1895a6a77ae14e7987b9cb51329a5adfb17bd8e7c638f92d6892d76e51cebcf"
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
{
|
|
||||||
"private_key": "0x3c1e9550e66958296d11b60f8e8e7a7ad990d07fa65d5f7652c4a6c87d4e3cc",
|
|
||||||
"messages": [
|
|
||||||
{
|
|
||||||
"hash": "0x1",
|
|
||||||
"r": "3162358736122783857144396205516927012128897537504463716197279730251407200037",
|
|
||||||
"s": "1447067116407676619871126378936374427636662490882969509559888874644844560850"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"hash": "0x11",
|
|
||||||
"r": "2282960348362869237018441985726545922711140064809058182483721438101695251648",
|
|
||||||
"s": "2905868291002627709651322791912000820756370440695830310841564989426104902684"
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"hash": "0x223",
|
|
||||||
"r": "2851492577225522862152785068304516872062840835882746625971400995051610132955",
|
|
||||||
"s": "2227464623243182122770469099770977514100002325017609907274766387592987135410"
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"hash": "0x9999",
|
|
||||||
"r": "3551214266795401081823453828727326248401688527835302880992409448142527576296",
|
|
||||||
"s": "2580950807716503852408066180369610390914312729170066679103651110985466032285"
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"hash": "0x387e76d1667c4454bfb835144120583af836f8e32a516765497d23eabe16b3f",
|
|
||||||
"r": "3518448914047769356425227827389998721396724764083236823647519654917215164512",
|
|
||||||
"s": "3042321032945513635364267149196358883053166552342928199041742035443537684462"
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"hash": "0x3a7e76d1697c4455bfb835144120283af236f8e32a516765497d23eabe16b2",
|
|
||||||
"r": "2261926635950780594216378185339927576862772034098248230433352748057295357217",
|
|
||||||
"s": "2708700003762962638306717009307430364534544393269844487939098184375356178572"
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"hash": "0xfa5f0cd1ebff93c9e6474379a213ba111f9e42f2f1cb361b0327e0737203",
|
|
||||||
"r": "3016953906936760149710218073693613509330129567629289734816320774638425763370",
|
|
||||||
"s": "306146275372136078470081798635201810092238376869367156373203048583896337506"
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"hash": "0x4c1e9550e66958296d11b60f8e8e7f7ae99dd0cfa6bd5fa652c1a6c87d4e2cc",
|
|
||||||
"r": "3562728603055564208884290243634917206833465920158600288670177317979301056463",
|
|
||||||
"s": "1958799632261808501999574190111106370256896588537275453140683641951899459876"
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"hash": "0x6362b40c218fb4c8a8bd42ca482145e8513b78e00faa0de76a98ba14fc37ae8",
|
|
||||||
"r": "3485557127492692423490706790022678621438670833185864153640824729109010175518",
|
|
||||||
"s": "897592218067946175671768586886915961592526001156186496738437723857225288280"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
|
|
||||||
import './basic.test.js';
|
|
||||||
import './stark.test.js';
|
|
||||||
import './property.test.js';
|
|
||||||
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
import { deepStrictEqual, throws } from 'assert';
|
|
||||||
import { should } from 'micro-should';
|
|
||||||
import * as starknet from '../../lib/esm/stark.js';
|
|
||||||
import * as fc from 'fast-check';
|
|
||||||
|
|
||||||
const FC_BIGINT = fc.bigInt(1n + 1n, starknet.CURVE.n - 1n);
|
|
||||||
|
|
||||||
should('Point#toHex() roundtrip', () => {
|
|
||||||
fc.assert(
|
|
||||||
fc.property(FC_BIGINT, (x) => {
|
|
||||||
const point1 = starknet.Point.fromPrivateKey(x);
|
|
||||||
const hex = point1.toHex(true);
|
|
||||||
deepStrictEqual(starknet.Point.fromHex(hex).toHex(true), hex);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
should('Signature.fromCompactHex() roundtrip', () => {
|
|
||||||
fc.assert(
|
|
||||||
fc.property(FC_BIGINT, FC_BIGINT, (r, s) => {
|
|
||||||
const sig = new starknet.Signature(r, s);
|
|
||||||
deepStrictEqual(starknet.Signature.fromCompact(sig.toCompactHex()), sig);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
should('Signature.fromDERHex() roundtrip', () => {
|
|
||||||
fc.assert(
|
|
||||||
fc.property(FC_BIGINT, FC_BIGINT, (r, s) => {
|
|
||||||
const sig = new starknet.Signature(r, s);
|
|
||||||
deepStrictEqual(starknet.Signature.fromDER(sig.toDERHex()), sig);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
should('verify()/should verify random signatures', () =>
|
|
||||||
fc.assert(
|
|
||||||
fc.asyncProperty(FC_BIGINT, fc.hexaString({ minLength: 64, maxLength: 64 }), (privNum, msg) => {
|
|
||||||
const privKey = privNum.toString(16).padStart(64, '0');
|
|
||||||
const pub = starknet.getPublicKey(privKey);
|
|
||||||
const sig = starknet.sign(msg, privKey);
|
|
||||||
deepStrictEqual(starknet.verify(sig, msg, pub), true);
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// ESM is broken.
|
|
||||||
import url from 'url';
|
|
||||||
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
|
|
||||||
should.run();
|
|
||||||
}
|
|
||||||
@@ -1,286 +0,0 @@
|
|||||||
import { deepStrictEqual, throws } from 'assert';
|
|
||||||
import { should } from 'micro-should';
|
|
||||||
import { hex, utf8 } from '@scure/base';
|
|
||||||
import * as bip32 from '@scure/bip32';
|
|
||||||
import * as bip39 from '@scure/bip39';
|
|
||||||
import * as starknet from '../../lib/esm/stark.js';
|
|
||||||
import { default as sigVec } from './fixtures/rfc6979_signature_test_vector.json' assert { type: 'json' };
|
|
||||||
import { default as precomputedKeys } from './fixtures/keys_precomputed.json' assert { type: 'json' };
|
|
||||||
|
|
||||||
should('Starknet keccak', () => {
|
|
||||||
const value = starknet.keccak(utf8.decode('hello'));
|
|
||||||
deepStrictEqual(value, 0x8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8n);
|
|
||||||
deepStrictEqual(value < 2n ** 250n, true);
|
|
||||||
});
|
|
||||||
|
|
||||||
should('RFC6979', () => {
|
|
||||||
for (const msg of sigVec.messages) {
|
|
||||||
const { r, s } = starknet.sign(msg.hash, sigVec.private_key);
|
|
||||||
// const { r, s } = starknet.Signature.fromDER(sig);
|
|
||||||
deepStrictEqual(r.toString(10), msg.r);
|
|
||||||
deepStrictEqual(s.toString(10), msg.s);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
should('Signatures', () => {
|
|
||||||
const vectors = [
|
|
||||||
{
|
|
||||||
// Message hash of length 61.
|
|
||||||
msg: 'c465dd6b1bbffdb05442eb17f5ca38ad1aa78a6f56bf4415bdee219114a47',
|
|
||||||
r: '5f496f6f210b5810b2711c74c15c05244dad43d18ecbbdbe6ed55584bc3b0a2',
|
|
||||||
s: '4e8657b153787f741a67c0666bad6426c3741b478c8eaa3155196fc571416f3',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Message hash of length 61, with leading zeros.
|
|
||||||
msg: '00c465dd6b1bbffdb05442eb17f5ca38ad1aa78a6f56bf4415bdee219114a47',
|
|
||||||
r: '5f496f6f210b5810b2711c74c15c05244dad43d18ecbbdbe6ed55584bc3b0a2',
|
|
||||||
s: '4e8657b153787f741a67c0666bad6426c3741b478c8eaa3155196fc571416f3',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Message hash of length 62.
|
|
||||||
msg: 'c465dd6b1bbffdb05442eb17f5ca38ad1aa78a6f56bf4415bdee219114a47a',
|
|
||||||
r: '233b88c4578f0807b4a7480c8076eca5cfefa29980dd8e2af3c46a253490e9c',
|
|
||||||
s: '28b055e825bc507349edfb944740a35c6f22d377443c34742c04e0d82278cf1',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Message hash of length 63.
|
|
||||||
msg: '7465dd6b1bbffdb05442eb17f5ca38ad1aa78a6f56bf4415bdee219114a47a1',
|
|
||||||
r: 'b6bee8010f96a723f6de06b5fa06e820418712439c93850dd4e9bde43ddf',
|
|
||||||
s: '1a3d2bc954ed77e22986f507d68d18115fa543d1901f5b4620db98e2f6efd80',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
const privateKey = '2dccce1da22003777062ee0870e9881b460a8b7eca276870f57c601f182136c';
|
|
||||||
const publicKey = starknet.getPublicKey(privateKey);
|
|
||||||
for (const v of vectors) {
|
|
||||||
const sig = starknet.sign(v.msg, privateKey);
|
|
||||||
const { r, s } = sig;
|
|
||||||
// const { r, s } = starknet.Signature.fromDER(sig);
|
|
||||||
deepStrictEqual(r.toString(16), v.r, 'r equality');
|
|
||||||
deepStrictEqual(s.toString(16), v.s, 's equality');
|
|
||||||
deepStrictEqual(starknet.verify(sig, v.msg, publicKey), true, 'verify');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
should('Invalid signatures', () => {
|
|
||||||
/*
|
|
||||||
|
|
||||||
it('should not verify invalid signature inputs lengths', () => {
|
|
||||||
const ecOrder = starkwareCrypto.ec.n;
|
|
||||||
const {maxEcdsaVal} = starkwareCrypto;
|
|
||||||
const maxMsgHash = maxEcdsaVal.sub(oneBn);
|
|
||||||
const maxR = maxEcdsaVal.sub(oneBn);
|
|
||||||
const maxS = ecOrder.sub(oneBn).sub(oneBn);
|
|
||||||
const maxStarkKey = maxEcdsaVal.sub(oneBn);
|
|
||||||
|
|
||||||
// Test invalid message length.
|
|
||||||
expect(() =>
|
|
||||||
starkwareCrypto.verify(maxStarkKey, maxMsgHash.add(oneBn).toString(16), {
|
|
||||||
r: maxR,
|
|
||||||
s: maxS
|
|
||||||
})
|
|
||||||
).to.throw('Message not signable, invalid msgHash length.');
|
|
||||||
// Test invalid r length.
|
|
||||||
expect(() =>
|
|
||||||
starkwareCrypto.verify(maxStarkKey, maxMsgHash.toString(16), {
|
|
||||||
r: maxR.add(oneBn),
|
|
||||||
s: maxS
|
|
||||||
})
|
|
||||||
).to.throw('Message not signable, invalid r length.');
|
|
||||||
// Test invalid w length.
|
|
||||||
expect(() =>
|
|
||||||
starkwareCrypto.verify(maxStarkKey, maxMsgHash.toString(16), {
|
|
||||||
r: maxR,
|
|
||||||
s: maxS.add(oneBn)
|
|
||||||
})
|
|
||||||
).to.throw('Message not signable, invalid w length.');
|
|
||||||
// Test invalid s length.
|
|
||||||
expect(() =>
|
|
||||||
starkwareCrypto.verify(maxStarkKey, maxMsgHash.toString(16), {
|
|
||||||
r: maxR,
|
|
||||||
s: maxS.add(oneBn).add(oneBn)
|
|
||||||
})
|
|
||||||
).to.throw('Message not signable, invalid s length.');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not verify invalid signatures', () => {
|
|
||||||
const privKey = generateRandomStarkPrivateKey();
|
|
||||||
const keyPair = starkwareCrypto.ec.keyFromPrivate(privKey, 'hex');
|
|
||||||
const keyPairPub = starkwareCrypto.ec.keyFromPublic(
|
|
||||||
keyPair.getPublic(),
|
|
||||||
'BN'
|
|
||||||
);
|
|
||||||
const msgHash = new BN(randomHexString(61));
|
|
||||||
const msgSignature = starkwareCrypto.sign(keyPair, msgHash);
|
|
||||||
|
|
||||||
// Test invalid public key.
|
|
||||||
const invalidKeyPairPub = starkwareCrypto.ec.keyFromPublic(
|
|
||||||
{x: keyPairPub.pub.getX().add(oneBn), y: keyPairPub.pub.getY()},
|
|
||||||
'BN'
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
starkwareCrypto.verify(
|
|
||||||
invalidKeyPairPub,
|
|
||||||
msgHash.toString(16),
|
|
||||||
msgSignature
|
|
||||||
)
|
|
||||||
).to.be.false;
|
|
||||||
// Test invalid message.
|
|
||||||
expect(
|
|
||||||
starkwareCrypto.verify(
|
|
||||||
keyPair,
|
|
||||||
msgHash.add(oneBn).toString(16),
|
|
||||||
msgSignature
|
|
||||||
)
|
|
||||||
).to.be.false;
|
|
||||||
expect(
|
|
||||||
starkwareCrypto.verify(
|
|
||||||
keyPairPub,
|
|
||||||
msgHash.add(oneBn).toString(16),
|
|
||||||
msgSignature
|
|
||||||
)
|
|
||||||
).to.be.false;
|
|
||||||
// Test invalid r.
|
|
||||||
msgSignature.r.iadd(oneBn);
|
|
||||||
expect(starkwareCrypto.verify(keyPair, msgHash.toString(16), msgSignature))
|
|
||||||
.to.be.false;
|
|
||||||
expect(
|
|
||||||
starkwareCrypto.verify(keyPairPub, msgHash.toString(16), msgSignature)
|
|
||||||
).to.be.false;
|
|
||||||
// Test invalid s.
|
|
||||||
msgSignature.r.isub(oneBn);
|
|
||||||
msgSignature.s.iadd(oneBn);
|
|
||||||
expect(starkwareCrypto.verify(keyPair, msgHash.toString(16), msgSignature))
|
|
||||||
.to.be.false;
|
|
||||||
expect(
|
|
||||||
starkwareCrypto.verify(keyPairPub, msgHash.toString(16), msgSignature)
|
|
||||||
).to.be.false;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
});
|
|
||||||
|
|
||||||
should('Pedersen', () => {
|
|
||||||
deepStrictEqual(
|
|
||||||
starknet.pedersen(
|
|
||||||
'0x3d937c035c878245caf64531a5756109c53068da139362728feb561405371cb',
|
|
||||||
'0x208a0a10250e382e1e4bbe2880906c2791bf6275695e02fbbc6aeff9cd8b31a'
|
|
||||||
),
|
|
||||||
'0x30e480bed5fe53fa909cc0f8c4d99b8f9f2c016be4c41e13a4848797979c662'
|
|
||||||
);
|
|
||||||
deepStrictEqual(
|
|
||||||
starknet.pedersen(
|
|
||||||
'0x58f580910a6ca59b28927c08fe6c43e2e303ca384badc365795fc645d479d45',
|
|
||||||
'0x78734f65a067be9bdb39de18434d71e79f7b6466a4b66bbd979ab9e7515fe0b'
|
|
||||||
),
|
|
||||||
'0x68cc0b76cddd1dd4ed2301ada9b7c872b23875d5ff837b3a87993e0d9996b87'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
should('Hash chain', () => {
|
|
||||||
deepStrictEqual(starknet.hashChain([1, 2, 3]), starknet.pedersen(1, starknet.pedersen(2, 3)));
|
|
||||||
});
|
|
||||||
|
|
||||||
should('Key grinding', () => {
|
|
||||||
deepStrictEqual(
|
|
||||||
starknet.grindKey('86F3E7293141F20A8BAFF320E8EE4ACCB9D4A4BF2B4D295E8CEE784DB46E0519'),
|
|
||||||
'5c8c8683596c732541a59e03007b2d30dbbbb873556fe65b5fb63c16688f941'
|
|
||||||
);
|
|
||||||
// Loops more than once (verified manually)
|
|
||||||
deepStrictEqual(
|
|
||||||
starknet.grindKey('94F3E7293141F20A8BAFF320E8EE4ACCB9D4A4BF2B4D295E8CEE784DB46E0595'),
|
|
||||||
'33880b9aba464c1c01c9f8f5b4fc1134698f9b0a8d18505cab6cdd34d93dc02'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
should('Private to stark key', () => {
|
|
||||||
deepStrictEqual(
|
|
||||||
starknet.getStarkKey('0x178047D3869489C055D7EA54C014FFB834A069C9595186ABE04EA4D1223A03F'),
|
|
||||||
'0x1895a6a77ae14e7987b9cb51329a5adfb17bd8e7c638f92d6892d76e51cebcf'
|
|
||||||
);
|
|
||||||
for (const [privKey, expectedPubKey] of Object.entries(precomputedKeys)) {
|
|
||||||
deepStrictEqual(starknet.getStarkKey(privKey), expectedPubKey);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
should('Private stark key from eth signature', () => {
|
|
||||||
const ethSignature =
|
|
||||||
'0x21fbf0696d5e0aa2ef41a2b4ffb623bcaf070461d61cf7251c74161f82fec3a43' +
|
|
||||||
'70854bc0a34b3ab487c1bc021cd318c734c51ae29374f2beb0e6f2dd49b4bf41c';
|
|
||||||
deepStrictEqual(
|
|
||||||
starknet.ethSigToPrivate(ethSignature),
|
|
||||||
'766f11e90cd7c7b43085b56da35c781f8c067ac0d578eabdceebc4886435bda'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
should('Key derivation', () => {
|
|
||||||
const layer = 'starkex';
|
|
||||||
const application = 'starkdeployement';
|
|
||||||
const mnemonic =
|
|
||||||
'range mountain blast problem vibrant void vivid doctor cluster enough melody ' +
|
|
||||||
'salt layer language laptop boat major space monkey unit glimpse pause change vibrant';
|
|
||||||
const ethAddress = '0xa4864d977b944315389d1765ffa7e66F74ee8cd7';
|
|
||||||
const VECTORS = [
|
|
||||||
{
|
|
||||||
index: 0,
|
|
||||||
path: "m/2645'/579218131'/891216374'/1961790679'/2135936222'/0",
|
|
||||||
privateKey: '6cf0a8bf113352eb863157a45c5e5567abb34f8d32cddafd2c22aa803f4892c',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
index: 7,
|
|
||||||
path: "m/2645'/579218131'/891216374'/1961790679'/2135936222'/7",
|
|
||||||
privateKey: '341751bdc42841da35ab74d13a1372c1f0250617e8a2ef96034d9f46e6847af',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
index: 598,
|
|
||||||
path: "m/2645'/579218131'/891216374'/1961790679'/2135936222'/598",
|
|
||||||
privateKey: '41a4d591a868353d28b7947eb132aa4d00c4a022743689ffd20a3628d6ca28c',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
const hd = bip32.HDKey.fromMasterSeed(bip39.mnemonicToSeedSync(mnemonic));
|
|
||||||
for (const { index, path, privateKey } of VECTORS) {
|
|
||||||
const realPath = starknet.getAccountPath(layer, application, ethAddress, index);
|
|
||||||
deepStrictEqual(realPath, path);
|
|
||||||
deepStrictEqual(starknet.grindKey(hd.derive(realPath).privateKey), privateKey);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Verified against starknet.js
|
|
||||||
should('Starknet.js cross-tests', () => {
|
|
||||||
const privateKey = '0x019800ea6a9a73f94aee6a3d2edf018fc770443e90c7ba121e8303ec6b349279';
|
|
||||||
// NOTE: there is no compressed keys here, getPubKey returns stark-key (which is schnorr-like X coordinate)
|
|
||||||
// But it is not used in signing/verifying
|
|
||||||
deepStrictEqual(
|
|
||||||
starknet.getStarkKey(privateKey),
|
|
||||||
'0x33f45f07e1bd1a51b45fc24ec8c8c9908db9e42191be9e169bfcac0c0d99745'
|
|
||||||
);
|
|
||||||
const msgHash = '0x6d1706bd3d1ba7c517be2a2a335996f63d4738e2f182144d078a1dd9997062e';
|
|
||||||
const sig = starknet.sign(msgHash, privateKey);
|
|
||||||
const { r, s } = (sig);
|
|
||||||
|
|
||||||
deepStrictEqual(
|
|
||||||
r.toString(),
|
|
||||||
'1427981024487605678086498726488552139932400435436186597196374630267616399345'
|
|
||||||
);
|
|
||||||
deepStrictEqual(
|
|
||||||
s.toString(),
|
|
||||||
'1853664302719670721837677288395394946745467311923401353018029119631574115563'
|
|
||||||
);
|
|
||||||
const hashMsg2 = starknet.pedersen(
|
|
||||||
'0x33f45f07e1bd1a51b45fc24ec8c8c9908db9e42191be9e169bfcac0c0d99745',
|
|
||||||
'1'
|
|
||||||
);
|
|
||||||
deepStrictEqual(hashMsg2, '0x2b0d4d43acce8ff68416f667f92ec7eab2b96f1d2224abd4d9d4d1e7fa4bb00');
|
|
||||||
const pubKey =
|
|
||||||
'04033f45f07e1bd1a51b45fc24ec8c8c9908db9e42191be9e169bfcac0c0d997450319d0f53f6ca077c4fa5207819144a2a4165daef6ee47a7c1d06c0dcaa3e456';
|
|
||||||
const sig2 = new starknet.Signature(
|
|
||||||
558858382392827003930138586379728730695763862039474863361948210004201119180n,
|
|
||||||
2440689354481625417078677634625227600823892606910345662891037256374285369343n
|
|
||||||
);
|
|
||||||
deepStrictEqual(starknet.verify(sig2.toDERHex(), hashMsg2, pubKey), true);
|
|
||||||
});
|
|
||||||
|
|
||||||
// ESM is broken.
|
|
||||||
import url from 'url';
|
|
||||||
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
|
|
||||||
should.run();
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
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,72 +1,107 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"message": "test data",
|
"curve": "P192",
|
||||||
"d": "fee0a1f7afebf9d2a5a80c0c98a31c709681cce195cbcd06342b517970c0be1e",
|
"q": "FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831",
|
||||||
"k0": "fcce1de7a9bcd6b2d3defade6afa1913fb9229e3b7ddf4749b55c4848b2a196e",
|
"private": "6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4",
|
||||||
"k1": "727fbcb59eb48b1d7d46f95a04991fc512eb9dbf9105628e3aec87428df28fd8",
|
"Ux": "AC2C77F529F91689FEA0EA5EFEC7F210D8EEA0B9E047ED56",
|
||||||
"k15": "398f0e2c9f79728f7b3d84d447ac3a86d8b2083c8f234a0ffa9c4043d68bd258"
|
"Uy": "3BC723E57670BD4887EBC732C523063D0A7C957BC97C1C43",
|
||||||
},
|
"cases": [
|
||||||
{
|
{
|
||||||
"message": "Everything should be made as simple as possible, but not simpler.",
|
"k": "32B1B6D7D42A05CB449065727A84804FB1A3E34D8F261496",
|
||||||
"d": "0000000000000000000000000000000000000000000000000000000000000001",
|
"message": "sample",
|
||||||
"k0": "ec633bd56a5774a0940cb97e27a9e4e51dc94af737596a0c5cbb3d30332d92a5",
|
"r": "4B0B8CE98A92866A2820E20AA6B75B56382E0F9BFD5ECB55",
|
||||||
"k1": "df55b6d1b5c48184622b0ead41a0e02bfa5ac3ebdb4c34701454e80aabf36f56",
|
"s": "CCDB006926EA9565CBADC840829D8C384E06DE1F1E381B85"
|
||||||
"k15": "def007a9a3c2f7c769c75da9d47f2af84075af95cadd1407393dc1e26086ef87"
|
},
|
||||||
},
|
{
|
||||||
{
|
"k": "5C4CE89CF56D9E7C77C8585339B006B97B5F0680B4306C6C",
|
||||||
"message": "Satoshi Nakamoto",
|
"message": "test",
|
||||||
"d": "0000000000000000000000000000000000000000000000000000000000000002",
|
"r": "3A718BD8B4926C3B52EE6BBE67EF79B18CB6EB62B1AD97AE",
|
||||||
"k0": "d3edc1b8224e953f6ee05c8bbf7ae228f461030e47caf97cde91430b4607405e",
|
"s": "5662E6848A4A19B1F1AE2F72ACD4B8BBE50F1EAC65D9124F"
|
||||||
"k1": "f86d8e43c09a6a83953f0ab6d0af59fb7446b4660119902e9967067596b58374",
|
}
|
||||||
"k15": "241d1f57d6cfd2f73b1ada7907b199951f95ef5ad362b13aed84009656e0254a"
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"message": "Diffie Hellman",
|
"curve": "P224",
|
||||||
"d": "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
|
"q": "FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D",
|
||||||
"k0": "c378a41cb17dce12340788dd3503635f54f894c306d52f6e9bc4b8f18d27afcc",
|
"private": "F220266E1105BFE3083E03EC7A3A654651F45E37167E88600BF257C1",
|
||||||
"k1": "90756c96fef41152ac9abe08819c4e95f16da2af472880192c69a2b7bac29114",
|
"Ux": "00CF08DA5AD719E42707FA431292DEA11244D64FC51610D94B130D6C",
|
||||||
"k15": "7b3f53300ab0ccd0f698f4d67db87c44cf3e9e513d9df61137256652b2e94e7c"
|
"Uy": "EEAB6F3DEBE455E3DBF85416F7030CBD94F34F2D6F232C69F3C1385A",
|
||||||
},
|
"cases": [
|
||||||
{
|
{
|
||||||
"message": "Japan",
|
"k": "C1D1F2F10881088301880506805FEB4825FE09ACB6816C36991AA06D",
|
||||||
"d": "8080808080808080808080808080808080808080808080808080808080808080",
|
"message": "sample",
|
||||||
"k0": "f471e61b51d2d8db78f3dae19d973616f57cdc54caaa81c269394b8c34edcf59",
|
"r": "1CDFE6662DDE1E4A1EC4CDEDF6A1F5A2FB7FBD9145C12113E6ABFD3E",
|
||||||
"k1": "6819d85b9730acc876fdf59e162bf309e9f63dd35550edf20869d23c2f3e6d17",
|
"s": "A6694FD7718A21053F225D3F46197CA699D45006C06F871808F43EBC"
|
||||||
"k15": "d8e8bae3ee330a198d1f5e00ad7c5f9ed7c24c357c0a004322abca5d9cd17847"
|
},
|
||||||
},
|
{
|
||||||
{
|
"k": "DF8B38D40DCA3E077D0AC520BF56B6D565134D9B5F2EAE0D34900524",
|
||||||
"message": "Bitcoin",
|
"message": "test",
|
||||||
"d": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
|
"r": "C441CE8E261DED634E4CF84910E4C5D1D22C5CF3B732BB204DBEF019",
|
||||||
"k0": "36c848ffb2cbecc5422c33a994955b807665317c1ce2a0f59c689321aaa631cc",
|
"s": "902F42847A63BDC5F6046ADA114953120F99442D76510150F372A3F4"
|
||||||
"k1": "4ed8de1ec952a4f5b3bd79d1ff96446bcd45cabb00fc6ca127183e14671bcb85",
|
}
|
||||||
"k15": "56b6f47babc1662c011d3b1f93aa51a6e9b5f6512e9f2e16821a238d450a31f8"
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"message": "i2FLPP8WEus5WPjpoHwheXOMSobUJVaZM1JPMQZq",
|
"curve": "P256",
|
||||||
"d": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
|
"q": "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551",
|
||||||
"k0": "6e9b434fcc6bbb081a0463c094356b47d62d7efae7da9c518ed7bac23f4e2ed6",
|
"private": "C9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721",
|
||||||
"k1": "ae5323ae338d6117ce8520a43b92eacd2ea1312ae514d53d8e34010154c593bb",
|
"Ux": "60FED4BA255A9D31C961EB74C6356D68C049B8923B61FA6CE669622E60F29FB6",
|
||||||
"k15": "3eaa1b61d1b8ab2f1ca71219c399f2b8b3defa624719f1e96fe3957628c2c4ea"
|
"Uy": "7903FE1008B8BC99A41AE9E95628BC64F2F1B20C2D7E9F5177A3C294D4462299",
|
||||||
},
|
"cases": [
|
||||||
{
|
{
|
||||||
"message": "lEE55EJNP7aLrMtjkeJKKux4Yg0E8E1SAJnWTCEh",
|
"k": "A6E3C57DD01ABE90086538398355DD4C3B17AA873382B0F24D6129493D8AAD60",
|
||||||
"d": "3881e5286abc580bb6139fe8e83d7c8271c6fe5e5c2d640c1f0ed0e1ee37edc9",
|
"message": "sample",
|
||||||
"k0": "5b606665a16da29cc1c5411d744ab554640479dd8abd3c04ff23bd6b302e7034",
|
"r": "EFD48B2AACB6A8FD1140DD9CD45E81D69D2C877B56AAF991C34D0EA84EAF3716",
|
||||||
"k1": "f8b25263152c042807c992eacd2ac2cc5790d1e9957c394f77ea368e3d9923bd",
|
"s": "F7CB1C942D657C41D436C7A1B6E29F65F3E900DBB9AFF4064DC4AB2F843ACDA8"
|
||||||
"k15": "ea624578f7e7964ac1d84adb5b5087dd14f0ee78b49072aa19051cc15dab6f33"
|
},
|
||||||
},
|
{
|
||||||
{
|
"k": "D16B6AE827F17175E040871A1C7EC3500192C4C92677336EC2537ACAEE0008E0",
|
||||||
"message": "2SaVPvhxkAPrayIVKcsoQO5DKA8Uv5X/esZFlf+y",
|
"message": "test",
|
||||||
"d": "7259dff07922de7f9c4c5720d68c9745e230b32508c497dd24cb95ef18856631",
|
"r": "F1ABB023518351CD71D881567B1EA663ED3EFCF6C5132B354F28D3B0B7D38367",
|
||||||
"k0": "3ab6c19ab5d3aea6aa0c6da37516b1d6e28e3985019b3adb388714e8f536686b",
|
"s": "019F4113742A2B14BD25926B49C649155F267E60D3814B4C0CC84250E46F0083"
|
||||||
"k1": "19af21b05004b0ce9cdca82458a371a9d2cf0dc35a813108c557b551c08eb52e",
|
}
|
||||||
"k15": "117a32665fca1b7137a91c4739ac5719fec0cf2e146f40f8e7c21b45a07ebc6a"
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"message": "00A0OwO2THi7j5Z/jp0FmN6nn7N/DQd6eBnCS+/b",
|
"curve": "P384",
|
||||||
"d": "0d6ea45d62b334777d6995052965c795a4f8506044b4fd7dc59c15656a28f7aa",
|
"q": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973",
|
||||||
"k0": "79487de0c8799158294d94c0eb92ee4b567e4dc7ca18addc86e49d31ce1d2db6",
|
"private": "6B9D3DAD2E1B8C1C05B19875B6659F4DE23C3B667BF297BA9AA47740787137D896D5724E4C70A825F872C9EA60D2EDF5",
|
||||||
"k1": "9561d2401164a48a8f600882753b3105ebdd35e2358f4f808c4f549c91490009",
|
"Ux": "EC3A4E415B4E19A4568618029F427FA5DA9A8BC4AE92E02E06AAE5286B300C64DEF8F0EA9055866064A254515480BC13",
|
||||||
"k15": "b0d273634129ff4dbdf0df317d4062a1dbc58818f88878ffdb4ec511c77976c0"
|
"Uy": "8015D9B72D7D57244EA8EF9AC0C621896708A59367F9DFB9F54CA84B3F1C9DB1288B231C3AE0D4FE7344FD2533264720",
|
||||||
}
|
"cases": [
|
||||||
]
|
{
|
||||||
|
"k": "94ED910D1A099DAD3254E9242AE85ABDE4BA15168EAF0CA87A555FD56D10FBCA2907E3E83BA95368623B8C4686915CF9",
|
||||||
|
"message": "sample",
|
||||||
|
"r": "94EDBB92A5ECB8AAD4736E56C691916B3F88140666CE9FA73D64C4EA95AD133C81A648152E44ACF96E36DD1E80FABE46",
|
||||||
|
"s": "99EF4AEB15F178CEA1FE40DB2603138F130E740A19624526203B6351D0A3A94FA329C145786E679E7B82C71A38628AC8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"k": "015EE46A5BF88773ED9123A5AB0807962D193719503C527B031B4C2D225092ADA71F4A459BC0DA98ADB95837DB8312EA",
|
||||||
|
"message": "test",
|
||||||
|
"r": "8203B63D3C853E8D77227FB377BCF7B7B772E97892A80F36AB775D509D7A5FEB0542A7F0812998DA8F1DD3CA3CF023DB",
|
||||||
|
"s": "DDD0760448D42D8A43AF45AF836FCE4DE8BE06B485E9B61B827C2F13173923E06A739F040649A667BF3B828246BAA5A5"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"curve": "P521",
|
||||||
|
"q": "1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409",
|
||||||
|
"private": "0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83538",
|
||||||
|
"Ux": "1894550D0785932E00EAA23B694F213F8C3121F86DC97A04E5A7167DB4E5BCD371123D46E45DB6B5D5370A7F20FB633155D38FFA16D2BD761DCAC474B9A2F5023A4",
|
||||||
|
"Uy": "0493101C962CD4D2FDDF782285E64584139C2F91B47F87FF82354D6630F746A28A0DB25741B5B34A828008B22ACC23F924FAAFBD4D33F81EA66956DFEAA2BFDFCF5",
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"k": "1DAE2EA071F8110DC26882D4D5EAE0621A3256FC8847FB9022E2B7D28E6F10198B1574FDD03A9053C08A1854A168AA5A57470EC97DD5CE090124EF52A2F7ECBFFD3",
|
||||||
|
"message": "sample",
|
||||||
|
"r": "0C328FAFCBD79DD77850370C46325D987CB525569FB63C5D3BC53950E6D4C5F174E25A1EE9017B5D450606ADD152B534931D7D4E8455CC91F9B15BF05EC36E377FA",
|
||||||
|
"s": "0617CCE7CF5064806C467F678D3B4080D6F1CC50AF26CA209417308281B68AF282623EAA63E5B5C0723D8B8C37FF0777B1A20F8CCB1DCCC43997F1EE0E44DA4A67A"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"k": "16200813020EC986863BEDFC1B121F605C1215645018AEA1A7B215A564DE9EB1B38A67AA1128B80CE391C4FB71187654AAA3431027BFC7F395766CA988C964DC56D",
|
||||||
|
"message": "test",
|
||||||
|
"r": "13E99020ABF5CEE7525D16B69B229652AB6BDF2AFFCAEF38773B4B7D08725F10CDB93482FDCC54EDCEE91ECA4166B2A7C6265EF0CE2BD7051B7CEF945BABD47EE6D",
|
||||||
|
"s": "1FBD0013C674AA79CB39849527916CE301C66EA7CE8B80682786AD60F98F7E78A19CA69EFF5C57400E3B3A0AD66CE0978214D13BAF4E9AC60752F7B155E2DE4DCE3"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
72
test/vectors/secp256k1/rfc6979.json
Normal file
72
test/vectors/secp256k1/rfc6979.json
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"message": "test data",
|
||||||
|
"d": "fee0a1f7afebf9d2a5a80c0c98a31c709681cce195cbcd06342b517970c0be1e",
|
||||||
|
"k0": "fcce1de7a9bcd6b2d3defade6afa1913fb9229e3b7ddf4749b55c4848b2a196e",
|
||||||
|
"k1": "727fbcb59eb48b1d7d46f95a04991fc512eb9dbf9105628e3aec87428df28fd8",
|
||||||
|
"k15": "398f0e2c9f79728f7b3d84d447ac3a86d8b2083c8f234a0ffa9c4043d68bd258"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"message": "Everything should be made as simple as possible, but not simpler.",
|
||||||
|
"d": "0000000000000000000000000000000000000000000000000000000000000001",
|
||||||
|
"k0": "ec633bd56a5774a0940cb97e27a9e4e51dc94af737596a0c5cbb3d30332d92a5",
|
||||||
|
"k1": "df55b6d1b5c48184622b0ead41a0e02bfa5ac3ebdb4c34701454e80aabf36f56",
|
||||||
|
"k15": "def007a9a3c2f7c769c75da9d47f2af84075af95cadd1407393dc1e26086ef87"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"message": "Satoshi Nakamoto",
|
||||||
|
"d": "0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
"k0": "d3edc1b8224e953f6ee05c8bbf7ae228f461030e47caf97cde91430b4607405e",
|
||||||
|
"k1": "f86d8e43c09a6a83953f0ab6d0af59fb7446b4660119902e9967067596b58374",
|
||||||
|
"k15": "241d1f57d6cfd2f73b1ada7907b199951f95ef5ad362b13aed84009656e0254a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"message": "Diffie Hellman",
|
||||||
|
"d": "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
|
||||||
|
"k0": "c378a41cb17dce12340788dd3503635f54f894c306d52f6e9bc4b8f18d27afcc",
|
||||||
|
"k1": "90756c96fef41152ac9abe08819c4e95f16da2af472880192c69a2b7bac29114",
|
||||||
|
"k15": "7b3f53300ab0ccd0f698f4d67db87c44cf3e9e513d9df61137256652b2e94e7c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"message": "Japan",
|
||||||
|
"d": "8080808080808080808080808080808080808080808080808080808080808080",
|
||||||
|
"k0": "f471e61b51d2d8db78f3dae19d973616f57cdc54caaa81c269394b8c34edcf59",
|
||||||
|
"k1": "6819d85b9730acc876fdf59e162bf309e9f63dd35550edf20869d23c2f3e6d17",
|
||||||
|
"k15": "d8e8bae3ee330a198d1f5e00ad7c5f9ed7c24c357c0a004322abca5d9cd17847"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"message": "Bitcoin",
|
||||||
|
"d": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
|
||||||
|
"k0": "36c848ffb2cbecc5422c33a994955b807665317c1ce2a0f59c689321aaa631cc",
|
||||||
|
"k1": "4ed8de1ec952a4f5b3bd79d1ff96446bcd45cabb00fc6ca127183e14671bcb85",
|
||||||
|
"k15": "56b6f47babc1662c011d3b1f93aa51a6e9b5f6512e9f2e16821a238d450a31f8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"message": "i2FLPP8WEus5WPjpoHwheXOMSobUJVaZM1JPMQZq",
|
||||||
|
"d": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
|
||||||
|
"k0": "6e9b434fcc6bbb081a0463c094356b47d62d7efae7da9c518ed7bac23f4e2ed6",
|
||||||
|
"k1": "ae5323ae338d6117ce8520a43b92eacd2ea1312ae514d53d8e34010154c593bb",
|
||||||
|
"k15": "3eaa1b61d1b8ab2f1ca71219c399f2b8b3defa624719f1e96fe3957628c2c4ea"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"message": "lEE55EJNP7aLrMtjkeJKKux4Yg0E8E1SAJnWTCEh",
|
||||||
|
"d": "3881e5286abc580bb6139fe8e83d7c8271c6fe5e5c2d640c1f0ed0e1ee37edc9",
|
||||||
|
"k0": "5b606665a16da29cc1c5411d744ab554640479dd8abd3c04ff23bd6b302e7034",
|
||||||
|
"k1": "f8b25263152c042807c992eacd2ac2cc5790d1e9957c394f77ea368e3d9923bd",
|
||||||
|
"k15": "ea624578f7e7964ac1d84adb5b5087dd14f0ee78b49072aa19051cc15dab6f33"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"message": "2SaVPvhxkAPrayIVKcsoQO5DKA8Uv5X/esZFlf+y",
|
||||||
|
"d": "7259dff07922de7f9c4c5720d68c9745e230b32508c497dd24cb95ef18856631",
|
||||||
|
"k0": "3ab6c19ab5d3aea6aa0c6da37516b1d6e28e3985019b3adb388714e8f536686b",
|
||||||
|
"k1": "19af21b05004b0ce9cdca82458a371a9d2cf0dc35a813108c557b551c08eb52e",
|
||||||
|
"k15": "117a32665fca1b7137a91c4739ac5719fec0cf2e146f40f8e7c21b45a07ebc6a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"message": "00A0OwO2THi7j5Z/jp0FmN6nn7N/DQd6eBnCS+/b",
|
||||||
|
"d": "0d6ea45d62b334777d6995052965c795a4f8506044b4fd7dc59c15656a28f7aa",
|
||||||
|
"k0": "79487de0c8799158294d94c0eb92ee4b567e4dc7ca18addc86e49d31ce1d2db6",
|
||||||
|
"k1": "9561d2401164a48a8f600882753b3105ebdd35e2358f4f808c4f549c91490009",
|
||||||
|
"k15": "b0d273634129ff4dbdf0df317d4062a1dbc58818f88878ffdb4ec511c77976c0"
|
||||||
|
}
|
||||||
|
]
|
||||||
File diff suppressed because it is too large
Load Diff
434
test/wycheproof/ec_prime_order_curves_test.json
Normal file
434
test/wycheproof/ec_prime_order_curves_test.json
Normal file
@@ -0,0 +1,434 @@
|
|||||||
|
{
|
||||||
|
"algorithm" : "EcCurveTest",
|
||||||
|
"schema" : "ec_curve_test_schema.json",
|
||||||
|
"generatorVersion" : "0.9rc5",
|
||||||
|
"numberOfTests" : 26,
|
||||||
|
"header" : [
|
||||||
|
"Test vectors of type EcCurveTest are for checking curve parameters."
|
||||||
|
],
|
||||||
|
"notes" : {
|
||||||
|
},
|
||||||
|
"testGroups" : [
|
||||||
|
{
|
||||||
|
"type" : "EcCurveTest",
|
||||||
|
"tests" : [
|
||||||
|
{
|
||||||
|
"tcId" : 1,
|
||||||
|
"comment" : "",
|
||||||
|
"flags" : [],
|
||||||
|
"name" : "secp224r1",
|
||||||
|
"oid" : "1.3.132.0.33",
|
||||||
|
"ref" : "ANSI X9.62",
|
||||||
|
"p" : "00ffffffffffffffffffffffffffffffff000000000000000000000001",
|
||||||
|
"n" : "00ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d",
|
||||||
|
"a" : "00fffffffffffffffffffffffffffffffefffffffffffffffffffffffe",
|
||||||
|
"b" : "00b4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4",
|
||||||
|
"gx" : "00b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21",
|
||||||
|
"gy" : "00bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34",
|
||||||
|
"h" : 1,
|
||||||
|
"result" : "valid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 2,
|
||||||
|
"comment" : "",
|
||||||
|
"flags" : [],
|
||||||
|
"name" : "secp256r1",
|
||||||
|
"oid" : "1.2.840.10045.3.1.7",
|
||||||
|
"ref" : "ANSI X9.62",
|
||||||
|
"p" : "00ffffffff00000001000000000000000000000000ffffffffffffffffffffffff",
|
||||||
|
"n" : "00ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551",
|
||||||
|
"a" : "00ffffffff00000001000000000000000000000000fffffffffffffffffffffffc",
|
||||||
|
"b" : "5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b",
|
||||||
|
"gx" : "6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296",
|
||||||
|
"gy" : "4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5",
|
||||||
|
"h" : 1,
|
||||||
|
"result" : "valid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 3,
|
||||||
|
"comment" : "",
|
||||||
|
"flags" : [],
|
||||||
|
"name" : "secp384r1",
|
||||||
|
"oid" : "1.3.132.0.34",
|
||||||
|
"ref" : "ANSI X9.62",
|
||||||
|
"p" : "00fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff",
|
||||||
|
"n" : "00ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973",
|
||||||
|
"a" : "00fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000fffffffc",
|
||||||
|
"b" : "00b3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef",
|
||||||
|
"gx" : "00aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7",
|
||||||
|
"gy" : "3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f",
|
||||||
|
"h" : 1,
|
||||||
|
"result" : "valid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 4,
|
||||||
|
"comment" : "",
|
||||||
|
"flags" : [],
|
||||||
|
"name" : "secp521r1",
|
||||||
|
"oid" : "1.3.132.0.35",
|
||||||
|
"ref" : "ANSI X9.62",
|
||||||
|
"p" : "01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||||
|
"n" : "01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409",
|
||||||
|
"a" : "01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc",
|
||||||
|
"b" : "51953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00",
|
||||||
|
"gx" : "00c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66",
|
||||||
|
"gy" : "011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650",
|
||||||
|
"h" : 1,
|
||||||
|
"result" : "valid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 5,
|
||||||
|
"comment" : "",
|
||||||
|
"flags" : [],
|
||||||
|
"name" : "secp256k1",
|
||||||
|
"oid" : "1.3.132.0.10",
|
||||||
|
"ref" : "https://www.secg.org/sec2-v2.pdf",
|
||||||
|
"p" : "00fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f",
|
||||||
|
"n" : "00fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141",
|
||||||
|
"a" : "00",
|
||||||
|
"b" : "07",
|
||||||
|
"gx" : "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
|
||||||
|
"gy" : "483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8",
|
||||||
|
"h" : 1,
|
||||||
|
"result" : "valid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 6,
|
||||||
|
"comment" : "",
|
||||||
|
"flags" : [],
|
||||||
|
"name" : "secp224k1",
|
||||||
|
"oid" : "1.3.132.0.32",
|
||||||
|
"ref" : "ANSI X9.62",
|
||||||
|
"p" : "00fffffffffffffffffffffffffffffffffffffffffffffffeffffe56d",
|
||||||
|
"n" : "010000000000000000000000000001dce8d2ec6184caf0a971769fb1f7",
|
||||||
|
"a" : "00",
|
||||||
|
"b" : "05",
|
||||||
|
"gx" : "00a1455b334df099df30fc28a169a467e9e47075a90f7e650eb6b7a45c",
|
||||||
|
"gy" : "7e089fed7fba344282cafbd6f7e319f7c0b0bd59e2ca4bdb556d61a5",
|
||||||
|
"h" : 1,
|
||||||
|
"result" : "valid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 7,
|
||||||
|
"comment" : "",
|
||||||
|
"flags" : [],
|
||||||
|
"name" : "brainpoolP224r1",
|
||||||
|
"oid" : "1.3.36.3.3.2.8.1.1.5",
|
||||||
|
"ref" : "RFC 5639",
|
||||||
|
"p" : "00d7c134aa264366862a18302575d1d787b09f075797da89f57ec8c0ff",
|
||||||
|
"n" : "00d7c134aa264366862a18302575d0fb98d116bc4b6ddebca3a5a7939f",
|
||||||
|
"a" : "68a5e62ca9ce6c1c299803a6c1530b514e182ad8b0042a59cad29f43",
|
||||||
|
"b" : "2580f63ccfe44138870713b1a92369e33e2135d266dbb372386c400b",
|
||||||
|
"gx" : "0d9029ad2c7e5cf4340823b2a87dc68c9e4ce3174c1e6efdee12c07d",
|
||||||
|
"gy" : "58aa56f772c0726f24c6b89e4ecdac24354b9e99caa3f6d3761402cd",
|
||||||
|
"h" : 1,
|
||||||
|
"result" : "valid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 8,
|
||||||
|
"comment" : "",
|
||||||
|
"flags" : [],
|
||||||
|
"name" : "brainpoolP256r1",
|
||||||
|
"oid" : "1.3.36.3.3.2.8.1.1.7",
|
||||||
|
"ref" : "RFC 5639",
|
||||||
|
"p" : "00a9fb57dba1eea9bc3e660a909d838d726e3bf623d52620282013481d1f6e5377",
|
||||||
|
"n" : "00a9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f7901e0e82974856a7",
|
||||||
|
"a" : "7d5a0975fc2c3057eef67530417affe7fb8055c126dc5c6ce94a4b44f330b5d9",
|
||||||
|
"b" : "26dc5c6ce94a4b44f330b5d9bbd77cbf958416295cf7e1ce6bccdc18ff8c07b6",
|
||||||
|
"gx" : "008bd2aeb9cb7e57cb2c4b482ffc81b7afb9de27e1e3bd23c23a4453bd9ace3262",
|
||||||
|
"gy" : "547ef835c3dac4fd97f8461a14611dc9c27745132ded8e545c1d54c72f046997",
|
||||||
|
"h" : 1,
|
||||||
|
"result" : "valid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 9,
|
||||||
|
"comment" : "",
|
||||||
|
"flags" : [],
|
||||||
|
"name" : "brainpoolP320r1",
|
||||||
|
"oid" : "1.3.36.3.3.2.8.1.1.9",
|
||||||
|
"ref" : "RFC 5639",
|
||||||
|
"p" : "00d35e472036bc4fb7e13c785ed201e065f98fcfa6f6f40def4f92b9ec7893ec28fcd412b1f1b32e27",
|
||||||
|
"n" : "00d35e472036bc4fb7e13c785ed201e065f98fcfa5b68f12a32d482ec7ee8658e98691555b44c59311",
|
||||||
|
"a" : "3ee30b568fbab0f883ccebd46d3f3bb8a2a73513f5eb79da66190eb085ffa9f492f375a97d860eb4",
|
||||||
|
"b" : "520883949dfdbc42d3ad198640688a6fe13f41349554b49acc31dccd884539816f5eb4ac8fb1f1a6",
|
||||||
|
"gx" : "43bd7e9afb53d8b85289bcc48ee5bfe6f20137d10a087eb6e7871e2a10a599c710af8d0d39e20611",
|
||||||
|
"gy" : "14fdd05545ec1cc8ab4093247f77275e0743ffed117182eaa9c77877aaac6ac7d35245d1692e8ee1",
|
||||||
|
"h" : 1,
|
||||||
|
"result" : "valid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 10,
|
||||||
|
"comment" : "",
|
||||||
|
"flags" : [],
|
||||||
|
"name" : "brainpoolP384r1",
|
||||||
|
"oid" : "1.3.36.3.3.2.8.1.1.11",
|
||||||
|
"ref" : "RFC 5639",
|
||||||
|
"p" : "008cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b412b1da197fb71123acd3a729901d1a71874700133107ec53",
|
||||||
|
"n" : "008cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b31f166e6cac0425a7cf3ab6af6b7fc3103b883202e9046565",
|
||||||
|
"a" : "7bc382c63d8c150c3c72080ace05afa0c2bea28e4fb22787139165efba91f90f8aa5814a503ad4eb04a8c7dd22ce2826",
|
||||||
|
"b" : "04a8c7dd22ce28268b39b55416f0447c2fb77de107dcd2a62e880ea53eeb62d57cb4390295dbc9943ab78696fa504c11",
|
||||||
|
"gx" : "1d1c64f068cf45ffa2a63a81b7c13f6b8847a3e77ef14fe3db7fcafe0cbd10e8e826e03436d646aaef87b2e247d4af1e",
|
||||||
|
"gy" : "008abe1d7520f9c2a45cb1eb8e95cfd55262b70b29feec5864e19c054ff99129280e4646217791811142820341263c5315",
|
||||||
|
"h" : 1,
|
||||||
|
"result" : "valid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 11,
|
||||||
|
"comment" : "",
|
||||||
|
"flags" : [],
|
||||||
|
"name" : "brainpoolP512r1",
|
||||||
|
"oid" : "1.3.36.3.3.2.8.1.1.13",
|
||||||
|
"ref" : "RFC 5639",
|
||||||
|
"p" : "00aadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca703308717d4d9b009bc66842aecda12ae6a380e62881ff2f2d82c68528aa6056583a48f3",
|
||||||
|
"n" : "00aadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca70330870553e5c414ca92619418661197fac10471db1d381085ddaddb58796829ca90069",
|
||||||
|
"a" : "7830a3318b603b89e2327145ac234cc594cbdd8d3df91610a83441caea9863bc2ded5d5aa8253aa10a2ef1c98b9ac8b57f1117a72bf2c7b9e7c1ac4d77fc94ca",
|
||||||
|
"b" : "3df91610a83441caea9863bc2ded5d5aa8253aa10a2ef1c98b9ac8b57f1117a72bf2c7b9e7c1ac4d77fc94cadc083e67984050b75ebae5dd2809bd638016f723",
|
||||||
|
"gx" : "0081aee4bdd82ed9645a21322e9c4c6a9385ed9f70b5d916c1b43b62eef4d0098eff3b1f78e2d0d48d50d1687b93b97d5f7c6d5047406a5e688b352209bcb9f822",
|
||||||
|
"gy" : "7dde385d566332ecc0eabfa9cf7822fdf209f70024a57b1aa000c55b881f8111b2dcde494a5f485e5bca4bd88a2763aed1ca2b2fa8f0540678cd1e0f3ad80892",
|
||||||
|
"h" : 1,
|
||||||
|
"result" : "valid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 12,
|
||||||
|
"comment" : "",
|
||||||
|
"flags" : [],
|
||||||
|
"name" : "brainpoolP224t1",
|
||||||
|
"oid" : "1.3.36.3.3.2.8.1.1.6",
|
||||||
|
"ref" : "RFC 5639",
|
||||||
|
"p" : "00d7c134aa264366862a18302575d1d787b09f075797da89f57ec8c0ff",
|
||||||
|
"n" : "00d7c134aa264366862a18302575d0fb98d116bc4b6ddebca3a5a7939f",
|
||||||
|
"a" : "00d7c134aa264366862a18302575d1d787b09f075797da89f57ec8c0fc",
|
||||||
|
"b" : "4b337d934104cd7bef271bf60ced1ed20da14c08b3bb64f18a60888d",
|
||||||
|
"gx" : "6ab1e344ce25ff3896424e7ffe14762ecb49f8928ac0c76029b4d580",
|
||||||
|
"gy" : "0374e9f5143e568cd23f3f4d7c0d4b1e41c8cc0d1c6abd5f1a46db4c",
|
||||||
|
"h" : 1,
|
||||||
|
"result" : "valid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 13,
|
||||||
|
"comment" : "",
|
||||||
|
"flags" : [],
|
||||||
|
"name" : "brainpoolP256t1",
|
||||||
|
"oid" : "1.3.36.3.3.2.8.1.1.8",
|
||||||
|
"ref" : "RFC 5639",
|
||||||
|
"p" : "00a9fb57dba1eea9bc3e660a909d838d726e3bf623d52620282013481d1f6e5377",
|
||||||
|
"n" : "00a9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f7901e0e82974856a7",
|
||||||
|
"a" : "00a9fb57dba1eea9bc3e660a909d838d726e3bf623d52620282013481d1f6e5374",
|
||||||
|
"b" : "662c61c430d84ea4fe66a7733d0b76b7bf93ebc4af2f49256ae58101fee92b04",
|
||||||
|
"gx" : "00a3e8eb3cc1cfe7b7732213b23a656149afa142c47aafbc2b79a191562e1305f4",
|
||||||
|
"gy" : "2d996c823439c56d7f7b22e14644417e69bcb6de39d027001dabe8f35b25c9be",
|
||||||
|
"h" : 1,
|
||||||
|
"result" : "valid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 14,
|
||||||
|
"comment" : "",
|
||||||
|
"flags" : [],
|
||||||
|
"name" : "brainpoolP320t1",
|
||||||
|
"oid" : "1.3.36.3.3.2.8.1.1.10",
|
||||||
|
"ref" : "RFC 5639",
|
||||||
|
"p" : "00d35e472036bc4fb7e13c785ed201e065f98fcfa6f6f40def4f92b9ec7893ec28fcd412b1f1b32e27",
|
||||||
|
"n" : "00d35e472036bc4fb7e13c785ed201e065f98fcfa5b68f12a32d482ec7ee8658e98691555b44c59311",
|
||||||
|
"a" : "00d35e472036bc4fb7e13c785ed201e065f98fcfa6f6f40def4f92b9ec7893ec28fcd412b1f1b32e24",
|
||||||
|
"b" : "00a7f561e038eb1ed560b3d147db782013064c19f27ed27c6780aaf77fb8a547ceb5b4fef422340353",
|
||||||
|
"gx" : "00925be9fb01afc6fb4d3e7d4990010f813408ab106c4f09cb7ee07868cc136fff3357f624a21bed52",
|
||||||
|
"gy" : "63ba3a7a27483ebf6671dbef7abb30ebee084e58a0b077ad42a5a0989d1ee71b1b9bc0455fb0d2c3",
|
||||||
|
"h" : 1,
|
||||||
|
"result" : "valid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 15,
|
||||||
|
"comment" : "",
|
||||||
|
"flags" : [],
|
||||||
|
"name" : "brainpoolP384t1",
|
||||||
|
"oid" : "1.3.36.3.3.2.8.1.1.12",
|
||||||
|
"ref" : "RFC 5639",
|
||||||
|
"p" : "008cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b412b1da197fb71123acd3a729901d1a71874700133107ec53",
|
||||||
|
"n" : "008cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b31f166e6cac0425a7cf3ab6af6b7fc3103b883202e9046565",
|
||||||
|
"a" : "008cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b412b1da197fb71123acd3a729901d1a71874700133107ec50",
|
||||||
|
"b" : "7f519eada7bda81bd826dba647910f8c4b9346ed8ccdc64e4b1abd11756dce1d2074aa263b88805ced70355a33b471ee",
|
||||||
|
"gx" : "18de98b02db9a306f2afcd7235f72a819b80ab12ebd653172476fecd462aabffc4ff191b946a5f54d8d0aa2f418808cc",
|
||||||
|
"gy" : "25ab056962d30651a114afd2755ad336747f93475b7a1fca3b88f2b6a208ccfe469408584dc2b2912675bf5b9e582928",
|
||||||
|
"h" : 1,
|
||||||
|
"result" : "valid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 16,
|
||||||
|
"comment" : "",
|
||||||
|
"flags" : [],
|
||||||
|
"name" : "brainpoolP512t1",
|
||||||
|
"oid" : "1.3.36.3.3.2.8.1.1.14",
|
||||||
|
"ref" : "RFC 5639",
|
||||||
|
"p" : "00aadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca703308717d4d9b009bc66842aecda12ae6a380e62881ff2f2d82c68528aa6056583a48f3",
|
||||||
|
"n" : "00aadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca70330870553e5c414ca92619418661197fac10471db1d381085ddaddb58796829ca90069",
|
||||||
|
"a" : "00aadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca703308717d4d9b009bc66842aecda12ae6a380e62881ff2f2d82c68528aa6056583a48f0",
|
||||||
|
"b" : "7cbbbcf9441cfab76e1890e46884eae321f70c0bcb4981527897504bec3e36a62bcdfa2304976540f6450085f2dae145c22553b465763689180ea2571867423e",
|
||||||
|
"gx" : "640ece5c12788717b9c1ba06cbc2a6feba85842458c56dde9db1758d39c0313d82ba51735cdb3ea499aa77a7d6943a64f7a3f25fe26f06b51baa2696fa9035da",
|
||||||
|
"gy" : "5b534bd595f5af0fa2c892376c84ace1bb4e3019b71634c01131159cae03cee9d9932184beef216bd71df2dadf86a627306ecff96dbb8bace198b61e00f8b332",
|
||||||
|
"h" : 1,
|
||||||
|
"result" : "valid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 17,
|
||||||
|
"comment" : "",
|
||||||
|
"flags" : [],
|
||||||
|
"name" : "FRP256v1",
|
||||||
|
"oid" : "1.2.250.1.223.101.256.1",
|
||||||
|
"ref" : "https://www.legifrance.gouv.fr/jorf/id/JORFTEXT000024668816",
|
||||||
|
"p" : "00f1fd178c0b3ad58f10126de8ce42435b3961adbcabc8ca6de8fcf353d86e9c03",
|
||||||
|
"n" : "00f1fd178c0b3ad58f10126de8ce42435b53dc67e140d2bf941ffdd459c6d655e1",
|
||||||
|
"a" : "00f1fd178c0b3ad58f10126de8ce42435b3961adbcabc8ca6de8fcf353d86e9c00",
|
||||||
|
"b" : "00ee353fca5428a9300d4aba754a44c00fdfec0c9ae4b1a1803075ed967b7bb73f",
|
||||||
|
"gx" : "00b6b3d4c356c139eb31183d4749d423958c27d2dcaf98b70164c97a2dd98f5cff",
|
||||||
|
"gy" : "6142e0f7c8b204911f9271f0f3ecef8c2701c307e8e4c9e183115a1554062cfb",
|
||||||
|
"h" : 1,
|
||||||
|
"result" : "valid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 18,
|
||||||
|
"comment" : "",
|
||||||
|
"flags" : [],
|
||||||
|
"name" : "secp192k1",
|
||||||
|
"oid" : "1.3.132.0.31",
|
||||||
|
"ref" : "ANSI X9.62",
|
||||||
|
"p" : "00fffffffffffffffffffffffffffffffffffffffeffffee37",
|
||||||
|
"n" : "00fffffffffffffffffffffffe26f2fc170f69466a74defd8d",
|
||||||
|
"a" : "00",
|
||||||
|
"b" : "03",
|
||||||
|
"gx" : "00db4ff10ec057e9ae26b07d0280b7f4341da5d1b1eae06c7d",
|
||||||
|
"gy" : "009b2f2f6d9c5628a7844163d015be86344082aa88d95e2f9d",
|
||||||
|
"h" : 1,
|
||||||
|
"result" : "valid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 19,
|
||||||
|
"comment" : "",
|
||||||
|
"flags" : [],
|
||||||
|
"name" : "secp192r1",
|
||||||
|
"oid" : "1.2.840.10045.3.1.1",
|
||||||
|
"ref" : "ANSI X9.62",
|
||||||
|
"p" : "00fffffffffffffffffffffffffffffffeffffffffffffffff",
|
||||||
|
"n" : "00ffffffffffffffffffffffff99def836146bc9b1b4d22831",
|
||||||
|
"a" : "00fffffffffffffffffffffffffffffffefffffffffffffffc",
|
||||||
|
"b" : "64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1",
|
||||||
|
"gx" : "188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012",
|
||||||
|
"gy" : "07192b95ffc8da78631011ed6b24cdd573f977a11e794811",
|
||||||
|
"h" : 1,
|
||||||
|
"result" : "valid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 20,
|
||||||
|
"comment" : "",
|
||||||
|
"flags" : [],
|
||||||
|
"name" : "secp160k1",
|
||||||
|
"oid" : "1.3.132.0.9",
|
||||||
|
"ref" : "https://www.secg.org/SEC2-Ver-1.0.pdf",
|
||||||
|
"p" : "00fffffffffffffffffffffffffffffffeffffac73",
|
||||||
|
"n" : "0100000000000000000001b8fa16dfab9aca16b6b3",
|
||||||
|
"a" : "00",
|
||||||
|
"b" : "07",
|
||||||
|
"gx" : "3b4c382ce37aa192a4019e763036f4f5dd4d7ebb",
|
||||||
|
"gy" : "00938cf935318fdced6bc28286531733c3f03c4fee",
|
||||||
|
"h" : 1,
|
||||||
|
"result" : "valid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 21,
|
||||||
|
"comment" : "",
|
||||||
|
"flags" : [],
|
||||||
|
"name" : "secp160r1",
|
||||||
|
"oid" : "1.3.132.0.8",
|
||||||
|
"ref" : "https://www.secg.org/SEC2-Ver-1.0.pdf",
|
||||||
|
"p" : "00ffffffffffffffffffffffffffffffff7fffffff",
|
||||||
|
"n" : "0100000000000000000001f4c8f927aed3ca752257",
|
||||||
|
"a" : "00ffffffffffffffffffffffffffffffff7ffffffc",
|
||||||
|
"b" : "1c97befc54bd7a8b65acf89f81d4d4adc565fa45",
|
||||||
|
"gx" : "4a96b5688ef573284664698968c38bb913cbfc82",
|
||||||
|
"gy" : "23a628553168947d59dcc912042351377ac5fb32",
|
||||||
|
"h" : 1,
|
||||||
|
"result" : "valid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 22,
|
||||||
|
"comment" : "",
|
||||||
|
"flags" : [],
|
||||||
|
"name" : "secp160r2",
|
||||||
|
"oid" : "1.3.132.0.30",
|
||||||
|
"ref" : "https://www.secg.org/SEC2-Ver-1.0.pdf",
|
||||||
|
"p" : "00fffffffffffffffffffffffffffffffeffffac73",
|
||||||
|
"n" : "0100000000000000000000351ee786a818f3a1a16b",
|
||||||
|
"a" : "00fffffffffffffffffffffffffffffffeffffac70",
|
||||||
|
"b" : "00b4e134d3fb59eb8bab57274904664d5af50388ba",
|
||||||
|
"gx" : "52dcb034293a117e1f4ff11b30f7199d3144ce6d",
|
||||||
|
"gy" : "00feaffef2e331f296e071fa0df9982cfea7d43f2e",
|
||||||
|
"h" : 1,
|
||||||
|
"result" : "valid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 23,
|
||||||
|
"comment" : "",
|
||||||
|
"flags" : [],
|
||||||
|
"name" : "brainpoolP160r1",
|
||||||
|
"oid" : "1.3.36.3.3.2.8.1.1.1",
|
||||||
|
"ref" : "RFC 5639",
|
||||||
|
"p" : "00e95e4a5f737059dc60dfc7ad95b3d8139515620f",
|
||||||
|
"n" : "00e95e4a5f737059dc60df5991d45029409e60fc09",
|
||||||
|
"a" : "340e7be2a280eb74e2be61bada745d97e8f7c300",
|
||||||
|
"b" : "1e589a8595423412134faa2dbdec95c8d8675e58",
|
||||||
|
"gx" : "00bed5af16ea3f6a4f62938c4631eb5af7bdbcdbc3",
|
||||||
|
"gy" : "1667cb477a1a8ec338f94741669c976316da6321",
|
||||||
|
"h" : 1,
|
||||||
|
"result" : "valid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 24,
|
||||||
|
"comment" : "",
|
||||||
|
"flags" : [],
|
||||||
|
"name" : "brainpoolP160t1",
|
||||||
|
"oid" : "1.3.36.3.3.2.8.1.1.2",
|
||||||
|
"ref" : "RFC 5639",
|
||||||
|
"p" : "00e95e4a5f737059dc60dfc7ad95b3d8139515620f",
|
||||||
|
"n" : "00e95e4a5f737059dc60df5991d45029409e60fc09",
|
||||||
|
"a" : "00e95e4a5f737059dc60dfc7ad95b3d8139515620c",
|
||||||
|
"b" : "7a556b6dae535b7b51ed2c4d7daa7a0b5c55f380",
|
||||||
|
"gx" : "00b199b13b9b34efc1397e64baeb05acc265ff2378",
|
||||||
|
"gy" : "00add6718b7c7c1961f0991b842443772152c9e0ad",
|
||||||
|
"h" : 1,
|
||||||
|
"result" : "valid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 25,
|
||||||
|
"comment" : "",
|
||||||
|
"flags" : [],
|
||||||
|
"name" : "brainpoolP192r1",
|
||||||
|
"oid" : "1.3.36.3.3.2.8.1.1.3",
|
||||||
|
"ref" : "RFC 5639",
|
||||||
|
"p" : "00c302f41d932a36cda7a3463093d18db78fce476de1a86297",
|
||||||
|
"n" : "00c302f41d932a36cda7a3462f9e9e916b5be8f1029ac4acc1",
|
||||||
|
"a" : "6a91174076b1e0e19c39c031fe8685c1cae040e5c69a28ef",
|
||||||
|
"b" : "469a28ef7c28cca3dc721d044f4496bcca7ef4146fbf25c9",
|
||||||
|
"gx" : "00c0a0647eaab6a48753b033c56cb0f0900a2f5c4853375fd6",
|
||||||
|
"gy" : "14b690866abd5bb88b5f4828c1490002e6773fa2fa299b8f",
|
||||||
|
"h" : 1,
|
||||||
|
"result" : "valid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcId" : 26,
|
||||||
|
"comment" : "",
|
||||||
|
"flags" : [],
|
||||||
|
"name" : "brainpoolP192t1",
|
||||||
|
"oid" : "1.3.36.3.3.2.8.1.1.4",
|
||||||
|
"ref" : "RFC 5639",
|
||||||
|
"p" : "00c302f41d932a36cda7a3463093d18db78fce476de1a86297",
|
||||||
|
"n" : "00c302f41d932a36cda7a3462f9e9e916b5be8f1029ac4acc1",
|
||||||
|
"a" : "00c302f41d932a36cda7a3463093d18db78fce476de1a86294",
|
||||||
|
"b" : "13d56ffaec78681e68f9deb43b35bec2fb68542e27897b79",
|
||||||
|
"gx" : "3ae9e58c82f63c30282e1fe7bbf43fa72c446af6f4618129",
|
||||||
|
"gy" : "097e2c5667c2223a902ab5ca449d0084b7e5b3de7ccc01c9",
|
||||||
|
"h" : 1,
|
||||||
|
"result" : "valid"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
5578
test/wycheproof/ecdsa_secp224k1_sha224_test.json
Normal file
5578
test/wycheproof/ecdsa_secp224k1_sha224_test.json
Normal file
File diff suppressed because one or more lines are too long
5882
test/wycheproof/ecdsa_secp224k1_sha256_test.json
Normal file
5882
test/wycheproof/ecdsa_secp224k1_sha256_test.json
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
6406
test/wycheproof/ecdsa_secp224r1_shake128_test.json
Normal file
6406
test/wycheproof/ecdsa_secp224r1_shake128_test.json
Normal file
File diff suppressed because one or more lines are too long
6360
test/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.json
Normal file
6360
test/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.json
Normal file
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user