Compare commits
325 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a0e398489f | ||
|
|
85d194cb93 | ||
|
|
1830124ca1 | ||
|
|
72cc640bb1 | ||
|
|
32bda7926d | ||
|
|
38a4ca1e6b | ||
|
|
07f7e53e31 | ||
|
|
537db4a968 | ||
|
|
a70501cec4 | ||
|
|
89aaf264c1 | ||
|
|
e93caf9567 | ||
|
|
0ce103bd6b | ||
|
|
6a85252dc3 | ||
|
|
a8503d6819 | ||
|
|
8397b26b45 | ||
|
|
8c39a6ac5a | ||
|
|
a1a7dc9cbf | ||
|
|
ba58a282bd | ||
|
|
c4c479047e | ||
|
|
2bf2e312a0 | ||
|
|
0a663391bd | ||
|
|
7be1dfc55d | ||
|
|
37eab5a28a | ||
|
|
2706fe9f79 | ||
|
|
b39b0d1daf | ||
|
|
4007ee975b | ||
|
|
f8af434b9c | ||
|
|
be8033a2d8 | ||
|
|
b3c239981b | ||
|
|
18b0bc6317 | ||
|
|
30f68c9e54 | ||
|
|
ada1ea5a19 | ||
|
|
0a3a13b3dc | ||
|
|
26a4fd4293 | ||
|
|
9db14fc6d0 | ||
|
|
8e6c19de2b | ||
|
|
4ffb68853d | ||
|
|
008958364e | ||
|
|
1c535a3287 | ||
|
|
b8b12671ac | ||
|
|
2f1460a4d7 | ||
|
|
fb02e93ff6 | ||
|
|
c525356916 | ||
|
|
a4abd8a202 | ||
|
|
c19373a0b5 | ||
|
|
85006ed620 | ||
|
|
fae7f6612a | ||
|
|
36894729c0 | ||
|
|
eabab627c7 | ||
|
|
e1640eb74e | ||
|
|
7f851873f9 | ||
|
|
02099b9b4c | ||
|
|
3b14683806 | ||
|
|
47169740c6 | ||
|
|
45c7cb560d | ||
|
|
b36bf44f4b | ||
|
|
30763066ac | ||
|
|
911801ec0f | ||
|
|
8ba25a1c40 | ||
|
|
43a06b669a | ||
|
|
e7720c1609 | ||
|
|
2da6abb336 | ||
|
|
4752ab1f1e | ||
|
|
f58002e6d4 | ||
|
|
d0294bb2a6 | ||
|
|
2b41e387de | ||
|
|
08850c2d6a | ||
|
|
ce7a8fda55 | ||
|
|
728b485cd8 | ||
|
|
eaefe9a272 | ||
|
|
c935b398fe | ||
|
|
ddad219e7a | ||
|
|
1d83bab27d | ||
|
|
4be208e4b2 | ||
|
|
77bee0d54e | ||
|
|
6bcab6c24b | ||
|
|
7befd5f881 | ||
|
|
8f78471703 | ||
|
|
17294f4974 | ||
|
|
3890b79e7e | ||
|
|
2acebc8176 | ||
|
|
1e67754943 | ||
|
|
156a1e909a | ||
|
|
ccea23a712 | ||
|
|
8661eef949 | ||
|
|
4743182bf7 | ||
|
|
5c477a88fa | ||
|
|
df9d461adf | ||
|
|
5c21fa3855 | ||
|
|
6661a7db7b | ||
|
|
cf5f2268fb | ||
|
|
1d5286ffa7 | ||
|
|
e31efd91d8 | ||
|
|
c5e0e070d1 | ||
|
|
0d7756dceb | ||
|
|
b716b4603f | ||
|
|
d7a139822d | ||
|
|
fb6c379a26 | ||
|
|
eeac255c88 | ||
|
|
925fc3f810 | ||
|
|
eb8e7ec964 | ||
|
|
e7ac5e85d3 | ||
|
|
d285fcce06 | ||
|
|
ef667bb404 | ||
|
|
62749382e7 | ||
|
|
f90e871725 | ||
|
|
f049398718 | ||
|
|
ca99179bd8 | ||
|
|
1545230ee5 | ||
|
|
b082d41c29 | ||
|
|
2ce3b825f8 | ||
|
|
8315fe3580 | ||
|
|
9b7889e16f | ||
|
|
e8b9509c16 | ||
|
|
d92c9d14ad | ||
|
|
05794c0283 | ||
|
|
ca5583f713 | ||
|
|
8c48abe16a | ||
|
|
08bb00cc8f | ||
|
|
1ef16033fe | ||
|
|
113b6d7c00 | ||
|
|
5c3dc0be50 | ||
|
|
e7d01f4038 | ||
|
|
9a39625eda | ||
|
|
af8462b09e | ||
|
|
bfd9ae040d | ||
|
|
2bd437df4e | ||
|
|
b0af0a8977 | ||
|
|
aee10c8141 | ||
|
|
ff92bafb6f | ||
|
|
54679ff788 | ||
|
|
ee4571c7a1 | ||
|
|
fe7afdd392 | ||
|
|
dba2f0e732 | ||
|
|
52c5df0264 | ||
|
|
ebea4a4bcd | ||
|
|
33a53006f7 | ||
|
|
549e286ef0 | ||
|
|
3f0c0b59f1 | ||
|
|
62205347e1 | ||
|
|
476e75104f | ||
|
|
413725cfb3 | ||
|
|
cf17f7fe01 | ||
|
|
49fb90ae9a | ||
|
|
309d29a084 | ||
|
|
d3aa051770 | ||
|
|
5609ec7644 | ||
|
|
af8c1eebee | ||
|
|
08ea57ce5c | ||
|
|
ee3d3815b4 | ||
|
|
f471405798 | ||
|
|
e3a4bbffe9 | ||
|
|
c2edc97868 | ||
|
|
bf70ba9776 | ||
|
|
c71920722c | ||
|
|
62e806cfaf | ||
|
|
6a72821185 | ||
|
|
8cee1f559f | ||
|
|
6f10632ac0 | ||
|
|
b281167e8d | ||
|
|
c6b4aadafb | ||
|
|
aade023e48 | ||
|
|
2e04d96ce9 | ||
|
|
79dd7d3426 | ||
|
|
ff5b231e31 | ||
|
|
648fd2cc07 | ||
|
|
f67134ca86 | ||
|
|
6d0678b076 | ||
|
|
53ebde19ea | ||
|
|
a7755332c8 | ||
|
|
5f0007ab24 | ||
|
|
1ee5a5c07f | ||
|
|
708c0e14d5 | ||
|
|
624d7c9910 | ||
|
|
665ef2dd93 | ||
|
|
acc1f26acf | ||
|
|
3c4a25263e | ||
|
|
e887d516ab | ||
|
|
90e87f7ab1 | ||
|
|
5edafbac97 | ||
|
|
554c94509e | ||
|
|
7c11a021c0 | ||
|
|
531b6a3a48 | ||
|
|
fb5cd9df39 | ||
|
|
53a6d636d4 | ||
|
|
42de620010 | ||
|
|
6621053c7d | ||
|
|
9bee88888f | ||
|
|
103ba5f0a7 | ||
|
|
d5de5d2659 | ||
|
|
217cf8c654 | ||
|
|
8e307d8f89 | ||
|
|
8c0018d57f | ||
|
|
ca7f202839 | ||
|
|
816077ac0a | ||
|
|
bc03a07043 | ||
|
|
63653255e1 | ||
|
|
895ee3a1a4 | ||
|
|
16b31b9087 | ||
|
|
213796db4b | ||
|
|
049d3bce54 | ||
|
|
b2a04c2393 | ||
|
|
cb5e9a6e96 | ||
|
|
36af62357f | ||
|
|
88291eba33 | ||
|
|
848a1b0226 | ||
|
|
972e549dde | ||
|
|
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 |
20
.github/workflows/nodejs.yml
vendored
20
.github/workflows/nodejs.yml
vendored
@@ -1,17 +1,23 @@
|
|||||||
name: Node CI
|
name: Node CI
|
||||||
|
on:
|
||||||
on: [push, pull_request]
|
- push
|
||||||
|
- pull_request
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
name: v18 @ ubuntu-latest
|
name: v${{ matrix.node }} @ ubuntu-latest
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
node:
|
||||||
|
- 18
|
||||||
|
- 20
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@1e31de5234b9f8995739874a8ce0492dc87873e2 # v4
|
||||||
- name: Use Node.js ${{ matrix.node }}
|
- name: Use Node.js ${{ matrix.node }}
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: ${{ matrix.node }}
|
||||||
- run: npm install
|
- run: npm install
|
||||||
- run: npm run build --if-present
|
- run: npm run build --if-present
|
||||||
- run: npm run lint --if-present
|
|
||||||
- run: npm test
|
- run: npm test
|
||||||
|
- run: npm run lint --if-present
|
||||||
|
|||||||
23
.github/workflows/publish-npm.yml
vendored
Normal file
23
.github/workflows/publish-npm.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
name: Publish Package to npm
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [created]
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
id-token: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@1e31de5234b9f8995739874a8ce0492dc87873e2 # v4
|
||||||
|
- uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
cache: npm
|
||||||
|
- run: npm install -g npm
|
||||||
|
- run: npm ci
|
||||||
|
- run: npm run build
|
||||||
|
- run: npm publish --provenance --access public
|
||||||
|
env:
|
||||||
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
|
||||||
27
.github/workflows/upload-release.yml
vendored
Normal file
27
.github/workflows/upload-release.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
name: Upload standalone file to GitHub Releases
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [created]
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
id-token: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@1e31de5234b9f8995739874a8ce0492dc87873e2 # v4
|
||||||
|
- uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
cache: npm
|
||||||
|
- run: |
|
||||||
|
npm install -g npm
|
||||||
|
npm ci
|
||||||
|
npm run build
|
||||||
|
cd build
|
||||||
|
npm ci
|
||||||
|
npm run build
|
||||||
|
gh release upload ${{ github.event.release.tag_name }} noble-curves.js
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
16
.gitignore
vendored
16
.gitignore
vendored
@@ -1,7 +1,9 @@
|
|||||||
build/
|
node_modules
|
||||||
node_modules/
|
/*.js
|
||||||
coverage/
|
/esm/*.js
|
||||||
/lib/**/*.js
|
*.d.ts
|
||||||
/lib/**/*.ts
|
*.d.ts.map
|
||||||
/lib/**/*.d.ts.map
|
*.js.map
|
||||||
/curve-definitions/lib
|
/build
|
||||||
|
/abstract
|
||||||
|
/esm/abstract
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
"printWidth": 100,
|
"printWidth": 100,
|
||||||
"singleQuote": true
|
"singleQuote": true,
|
||||||
|
"trailingComma": "es5"
|
||||||
}
|
}
|
||||||
|
|||||||
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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,13 @@
|
|||||||
# Security Policy
|
# Security Policy
|
||||||
|
|
||||||
|
See [README's Security section](./README.md#security) for detailed description of internal security practices.
|
||||||
|
|
||||||
## Supported Versions
|
## Supported Versions
|
||||||
|
|
||||||
| 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.
BIN
audit/2023-09-kudelski-audit-starknet.pdf
Normal file
BIN
audit/2023-09-kudelski-audit-starknet.pdf
Normal file
Binary file not shown.
7
audit/README.md
Normal file
7
audit/README.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# Audit
|
||||||
|
|
||||||
|
All audits of the library are described in [README's Security section](../README.md#security)
|
||||||
|
|
||||||
|
`2023-01-trailofbits-audit-curves.pdf` 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.
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { readFileSync } from 'fs';
|
import { readFileSync } from 'fs';
|
||||||
import { mark, run } from 'micro-bmark';
|
import { mark, run } from 'micro-bmark';
|
||||||
import { bls12_381 as bls } from '../lib/bls12-381.js';
|
import { bls12_381 as bls } from '../bls12-381.js';
|
||||||
|
|
||||||
const G2_VECTORS = readFileSync('../test/bls12-381/bls12-381-g2-test-vectors.txt', 'utf-8')
|
const G2_VECTORS = readFileSync('../test/bls12-381/bls12-381-g2-test-vectors.txt', 'utf-8')
|
||||||
.trim()
|
.trim()
|
||||||
@@ -39,6 +39,21 @@ run(async () => {
|
|||||||
await mark('sign', 50, () => bls.sign('09', priv));
|
await mark('sign', 50, () => bls.sign('09', priv));
|
||||||
await mark('verify', 50, () => bls.verify(sig, '09', pub));
|
await mark('verify', 50, () => bls.verify(sig, '09', pub));
|
||||||
await mark('pairing', 100, () => bls.pairing(p1, p2));
|
await mark('pairing', 100, () => bls.pairing(p1, p2));
|
||||||
|
|
||||||
|
const scalars1 = Array(4096).fill(0).map(i => 2n ** 235n - BigInt(i));
|
||||||
|
const scalars2 = Array(4096).fill(0).map(i => 2n ** 241n + BigInt(i));
|
||||||
|
const points = scalars1.map(s => bls.G1.ProjectivePoint.BASE.multiply(s));
|
||||||
|
await mark('MSM 4096 scalars x points', 1, () => {
|
||||||
|
// naive approach, not using multi-scalar-multiplication
|
||||||
|
let sum = bls.G1.ProjectivePoint.ZERO;
|
||||||
|
for (let i = 0; i < 4096; i++) {
|
||||||
|
const scalar = scalars2[i];
|
||||||
|
const G1 = points[i];
|
||||||
|
const mutliplied = G1.multiplyUnsafe(scalar);
|
||||||
|
sum = sum.add(mutliplied);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
await mark('aggregatePublicKeys/8', 100, () => bls.aggregatePublicKeys(pubs.slice(0, 8)));
|
await mark('aggregatePublicKeys/8', 100, () => bls.aggregatePublicKeys(pubs.slice(0, 8)));
|
||||||
await mark('aggregatePublicKeys/32', 50, () => bls.aggregatePublicKeys(pub32));
|
await mark('aggregatePublicKeys/32', 50, () => bls.aggregatePublicKeys(pub32));
|
||||||
await mark('aggregatePublicKeys/128', 20, () => bls.aggregatePublicKeys(pub128));
|
await mark('aggregatePublicKeys/128', 20, () => bls.aggregatePublicKeys(pub128));
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { run, mark, utils } from 'micro-bmark';
|
import { run, mark, utils } from 'micro-bmark';
|
||||||
import { generateData } from './_shared.js';
|
import { generateData } from './_shared.js';
|
||||||
import { P256 } from '../lib/p256.js';
|
import { p256 } from '../p256.js';
|
||||||
import { P384 } from '../lib/p384.js';
|
import { p384 } from '../p384.js';
|
||||||
import { P521 } from '../lib/p521.js';
|
import { p521 } from '../p521.js';
|
||||||
import { ed25519 } from '../lib/ed25519.js';
|
import { ed25519 } from '../ed25519.js';
|
||||||
import { ed448 } from '../lib/ed448.js';
|
import { ed448 } from '../ed448.js';
|
||||||
|
|
||||||
run(async () => {
|
run(async () => {
|
||||||
const RAM = false
|
const RAM = false
|
||||||
for (let kv of Object.entries({ P256, P384, P521, ed25519, ed448 })) {
|
for (let kv of Object.entries({ ed25519, ed448, p256, p384, p521 })) {
|
||||||
const [name, curve] = kv;
|
const [name, curve] = kv;
|
||||||
console.log();
|
console.log();
|
||||||
console.log(`\x1b[36m${name}\x1b[0m`);
|
console.log(`\x1b[36m${name}\x1b[0m`);
|
||||||
|
|||||||
18
benchmark/decaf448.js
Normal file
18
benchmark/decaf448.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { run, mark, utils } from 'micro-bmark';
|
||||||
|
import { shake256 } from '@noble/hashes/sha3';
|
||||||
|
import * as mod from '../abstract/modular.js';
|
||||||
|
import { ed448, DecafPoint } from '../ed448.js';
|
||||||
|
|
||||||
|
run(async () => {
|
||||||
|
const RAM = false;
|
||||||
|
if (RAM) utils.logMem();
|
||||||
|
console.log(`\x1b[36mdecaf448\x1b[0m`);
|
||||||
|
const priv = mod.hashToPrivateScalar(shake256(ed448.utils.randomPrivateKey(), { dkLen: 112 }), ed448.CURVE.n);
|
||||||
|
const pub = DecafPoint.BASE.multiply(priv);
|
||||||
|
const encoded = pub.toRawBytes();
|
||||||
|
await mark('add', 1000000, () => pub.add(DecafPoint.BASE));
|
||||||
|
await mark('multiply', 1000, () => DecafPoint.BASE.multiply(priv));
|
||||||
|
await mark('encode', 10000, () => DecafPoint.BASE.toRawBytes());
|
||||||
|
await mark('decode', 10000, () => DecafPoint.fromHex(encoded));
|
||||||
|
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);
|
||||||
|
});
|
||||||
32
benchmark/hash-to-curve.js
Normal file
32
benchmark/hash-to-curve.js
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
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, hash_to_ristretto255 } from '../ed25519.js';
|
||||||
|
import { hashToCurve as ed448, hash_to_decaf448 } 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
await mark('hash_to_ristretto255', 1000, () => hash_to_ristretto255(msg, { DST: 'ristretto255_XMD:SHA-512_R255MAP_RO_' }));
|
||||||
|
await mark('hash_to_decaf448', 1000, () => hash_to_decaf448(msg, { DST: 'decaf448_XOF:SHAKE256_D448MAP_RO_' }));
|
||||||
|
});
|
||||||
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))
|
||||||
|
});
|
||||||
@@ -16,7 +16,6 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@noble/hashes": "^1.1.5",
|
"@noble/hashes": "^1.1.5",
|
||||||
"@starkware-industries/starkware-crypto-utils": "^0.0.2",
|
|
||||||
"elliptic": "^6.5.4"
|
"elliptic": "^6.5.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
18
benchmark/ristretto255.js
Normal file
18
benchmark/ristretto255.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { run, mark, utils } from 'micro-bmark';
|
||||||
|
import { sha512 } from '@noble/hashes/sha512';
|
||||||
|
import * as mod from '../abstract/modular.js';
|
||||||
|
import { ed25519, RistrettoPoint } from '../ed25519.js';
|
||||||
|
|
||||||
|
run(async () => {
|
||||||
|
const RAM = false;
|
||||||
|
if (RAM) utils.logMem();
|
||||||
|
console.log(`\x1b[36mristretto255\x1b[0m`);
|
||||||
|
const priv = mod.hashToPrivateScalar(sha512(ed25519.utils.randomPrivateKey()), ed25519.CURVE.n);
|
||||||
|
const pub = RistrettoPoint.BASE.multiply(priv);
|
||||||
|
const encoded = pub.toRawBytes();
|
||||||
|
await mark('add', 1000000, () => pub.add(RistrettoPoint.BASE));
|
||||||
|
await mark('multiply', 10000, () => RistrettoPoint.BASE.multiply(priv));
|
||||||
|
await mark('encode', 10000, () => RistrettoPoint.BASE.toRawBytes());
|
||||||
|
await mark('decode', 10000, () => RistrettoPoint.fromHex(encoded));
|
||||||
|
if (RAM) utils.logMem();
|
||||||
|
});
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { run, mark, utils } from 'micro-bmark';
|
import { run, mark, utils } from 'micro-bmark';
|
||||||
import { secp256k1, schnorr } from '../lib/secp256k1.js';
|
import { secp256k1, schnorr } from '../secp256k1.js';
|
||||||
import { generateData } from './_shared.js';
|
import { generateData } from './_shared.js';
|
||||||
|
|
||||||
run(async () => {
|
run(async () => {
|
||||||
|
|||||||
@@ -1,56 +0,0 @@
|
|||||||
import { run, mark, compare, utils } from 'micro-bmark';
|
|
||||||
import * as starkwareCrypto from '@starkware-industries/starkware-crypto-utils';
|
|
||||||
import * as stark from '../lib/stark.js';
|
|
||||||
|
|
||||||
run(async () => {
|
|
||||||
const RAM = false;
|
|
||||||
if (RAM) utils.logMem();
|
|
||||||
console.log(`\x1b[36mstark\x1b[0m`);
|
|
||||||
await mark('init', 1, () => stark.utils.precompute(8));
|
|
||||||
const d = (() => {
|
|
||||||
const priv = '2dccce1da22003777062ee0870e9881b460a8b7eca276870f57c601f182136c';
|
|
||||||
const msg = 'c465dd6b1bbffdb05442eb17f5ca38ad1aa78a6f56bf4415bdee219114a47';
|
|
||||||
const pub = stark.getPublicKey(priv);
|
|
||||||
const sig = stark.sign(msg, priv);
|
|
||||||
|
|
||||||
const privateKey = '2dccce1da22003777062ee0870e9881b460a8b7eca276870f57c601f182136c';
|
|
||||||
const msgHash = 'c465dd6b1bbffdb05442eb17f5ca38ad1aa78a6f56bf4415bdee219114a47';
|
|
||||||
const keyPair = starkwareCrypto.default.ec.keyFromPrivate(privateKey, 'hex');
|
|
||||||
const publicKeyStark = starkwareCrypto.default.ec.keyFromPublic(
|
|
||||||
keyPair.getPublic(true, 'hex'),
|
|
||||||
'hex'
|
|
||||||
);
|
|
||||||
return { priv, sig, msg, pub, publicKeyStark, msgHash, keyPair };
|
|
||||||
})();
|
|
||||||
await compare('pedersen', 500, {
|
|
||||||
old: () => {
|
|
||||||
return starkwareCrypto.default.pedersen([
|
|
||||||
'3d937c035c878245caf64531a5756109c53068da139362728feb561405371cb',
|
|
||||||
'208a0a10250e382e1e4bbe2880906c2791bf6275695e02fbbc6aeff9cd8b31a',
|
|
||||||
]);
|
|
||||||
},
|
|
||||||
noble: () => {
|
|
||||||
return stark.pedersen(
|
|
||||||
'3d937c035c878245caf64531a5756109c53068da139362728feb561405371cb',
|
|
||||||
'208a0a10250e382e1e4bbe2880906c2791bf6275695e02fbbc6aeff9cd8b31a'
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
await mark('poseidon', 10000, () => stark.poseidonHash(
|
|
||||||
0x3d937c035c878245caf64531a5756109c53068da139362728feb561405371cbn,
|
|
||||||
0x208a0a10250e382e1e4bbe2880906c2791bf6275695e02fbbc6aeff9cd8b31an
|
|
||||||
));
|
|
||||||
await compare('verify', 500, {
|
|
||||||
old: () => {
|
|
||||||
return starkwareCrypto.default.verify(
|
|
||||||
d.publicKeyStark,
|
|
||||||
d.msgHash,
|
|
||||||
starkwareCrypto.default.sign(d.keyPair, d.msgHash)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
noble: () => {
|
|
||||||
return stark.verify(stark.sign(d.msg, d.priv), d.msg, d.pub);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
if (RAM) utils.logMem();
|
|
||||||
});
|
|
||||||
9
benchmark/utils.js
Normal file
9
benchmark/utils.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { hexToBytes } from '../abstract/utils.js';
|
||||||
|
import { run, mark } from 'micro-bmark';
|
||||||
|
|
||||||
|
run(async () => {
|
||||||
|
const hex32 = '0123456789abcdef'.repeat(4);
|
||||||
|
const hex256 = hex32.repeat(8);
|
||||||
|
await mark('hexToBytes 32b', 5000000, () => hexToBytes(hex32));
|
||||||
|
await mark('hexToBytes 256b', 500000, () => hexToBytes(hex256));
|
||||||
|
});
|
||||||
7
build/README.md
Normal file
7
build/README.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# build
|
||||||
|
|
||||||
|
The directory is used to build a single file `noble-curves.js` which contains everything.
|
||||||
|
|
||||||
|
The output file uses iife wrapper and can be used in browsers as-is.
|
||||||
|
|
||||||
|
Don't use it unless you can't use NPM/ESM, which support tree shaking.
|
||||||
20
build/input.js
Normal file
20
build/input.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { bytesToHex, concatBytes, hexToBytes, utf8ToBytes } from '@noble/curves/abstract/utils';
|
||||||
|
|
||||||
|
export { secp256k1, schnorr as secp256k1_schnorr } from '@noble/curves/secp256k1';
|
||||||
|
export {
|
||||||
|
ed25519,
|
||||||
|
x25519,
|
||||||
|
edwardsToMontgomeryPub as ed25519_edwardsToMontgomeryPub,
|
||||||
|
edwardsToMontgomeryPriv as ed25519_edwardsToMontgomeryPriv,
|
||||||
|
} from '@noble/curves/ed25519';
|
||||||
|
export {
|
||||||
|
ed448,
|
||||||
|
x448,
|
||||||
|
edwardsToMontgomeryPub as ed448_edwardsToMontgomeryPub,
|
||||||
|
} from '@noble/curves/ed448';
|
||||||
|
export { p256 } from '@noble/curves/p256';
|
||||||
|
export { p384 } from '@noble/curves/p384';
|
||||||
|
export { p521 } from '@noble/curves/p521';
|
||||||
|
export { bls12_381 } from '@noble/curves/bls12-381';
|
||||||
|
|
||||||
|
export const utils = { bytesToHex, concatBytes, hexToBytes, utf8ToBytes };
|
||||||
446
build/package-lock.json
generated
Normal file
446
build/package-lock.json
generated
Normal file
@@ -0,0 +1,446 @@
|
|||||||
|
{
|
||||||
|
"name": "build",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "build",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"license": "MIT",
|
||||||
|
"devDependencies": {
|
||||||
|
"@noble/curves": "file:..",
|
||||||
|
"esbuild": "0.20.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"..": {
|
||||||
|
"name": "@noble/curves",
|
||||||
|
"version": "1.4.0",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@noble/hashes": "1.4.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"fast-check": "3.0.0",
|
||||||
|
"micro-bmark": "0.3.1",
|
||||||
|
"micro-should": "0.4.0",
|
||||||
|
"prettier": "3.1.1",
|
||||||
|
"typescript": "5.3.2"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/aix-ppc64": {
|
||||||
|
"version": "0.20.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.1.tgz",
|
||||||
|
"integrity": "sha512-m55cpeupQ2DbuRGQMMZDzbv9J9PgVelPjlcmM5kxHnrBdBx6REaEd7LamYV7Dm8N7rCyR/XwU6rVP8ploKtIkA==",
|
||||||
|
"cpu": [
|
||||||
|
"ppc64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"aix"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/android-arm": {
|
||||||
|
"version": "0.20.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.1.tgz",
|
||||||
|
"integrity": "sha512-4j0+G27/2ZXGWR5okcJi7pQYhmkVgb4D7UKwxcqrjhvp5TKWx3cUjgB1CGj1mfdmJBQ9VnUGgUhign+FPF2Zgw==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"android"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/android-arm64": {
|
||||||
|
"version": "0.20.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.1.tgz",
|
||||||
|
"integrity": "sha512-hCnXNF0HM6AjowP+Zou0ZJMWWa1VkD77BXe959zERgGJBBxB+sV+J9f/rcjeg2c5bsukD/n17RKWXGFCO5dD5A==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"android"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/android-x64": {
|
||||||
|
"version": "0.20.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.1.tgz",
|
||||||
|
"integrity": "sha512-MSfZMBoAsnhpS+2yMFYIQUPs8Z19ajwfuaSZx+tSl09xrHZCjbeXXMsUF/0oq7ojxYEpsSo4c0SfjxOYXRbpaA==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"android"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/darwin-arm64": {
|
||||||
|
"version": "0.20.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.1.tgz",
|
||||||
|
"integrity": "sha512-Ylk6rzgMD8klUklGPzS414UQLa5NPXZD5tf8JmQU8GQrj6BrFA/Ic9tb2zRe1kOZyCbGl+e8VMbDRazCEBqPvA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/darwin-x64": {
|
||||||
|
"version": "0.20.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.1.tgz",
|
||||||
|
"integrity": "sha512-pFIfj7U2w5sMp52wTY1XVOdoxw+GDwy9FsK3OFz4BpMAjvZVs0dT1VXs8aQm22nhwoIWUmIRaE+4xow8xfIDZA==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/freebsd-arm64": {
|
||||||
|
"version": "0.20.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.1.tgz",
|
||||||
|
"integrity": "sha512-UyW1WZvHDuM4xDz0jWun4qtQFauNdXjXOtIy7SYdf7pbxSWWVlqhnR/T2TpX6LX5NI62spt0a3ldIIEkPM6RHw==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"freebsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/freebsd-x64": {
|
||||||
|
"version": "0.20.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.1.tgz",
|
||||||
|
"integrity": "sha512-itPwCw5C+Jh/c624vcDd9kRCCZVpzpQn8dtwoYIt2TJF3S9xJLiRohnnNrKwREvcZYx0n8sCSbvGH349XkcQeg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"freebsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-arm": {
|
||||||
|
"version": "0.20.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.1.tgz",
|
||||||
|
"integrity": "sha512-LojC28v3+IhIbfQ+Vu4Ut5n3wKcgTu6POKIHN9Wpt0HnfgUGlBuyDDQR4jWZUZFyYLiz4RBBBmfU6sNfn6RhLw==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-arm64": {
|
||||||
|
"version": "0.20.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.1.tgz",
|
||||||
|
"integrity": "sha512-cX8WdlF6Cnvw/DO9/X7XLH2J6CkBnz7Twjpk56cshk9sjYVcuh4sXQBy5bmTwzBjNVZze2yaV1vtcJS04LbN8w==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-ia32": {
|
||||||
|
"version": "0.20.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.1.tgz",
|
||||||
|
"integrity": "sha512-4H/sQCy1mnnGkUt/xszaLlYJVTz3W9ep52xEefGtd6yXDQbz/5fZE5dFLUgsPdbUOQANcVUa5iO6g3nyy5BJiw==",
|
||||||
|
"cpu": [
|
||||||
|
"ia32"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-loong64": {
|
||||||
|
"version": "0.20.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.1.tgz",
|
||||||
|
"integrity": "sha512-c0jgtB+sRHCciVXlyjDcWb2FUuzlGVRwGXgI+3WqKOIuoo8AmZAddzeOHeYLtD+dmtHw3B4Xo9wAUdjlfW5yYA==",
|
||||||
|
"cpu": [
|
||||||
|
"loong64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-mips64el": {
|
||||||
|
"version": "0.20.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.1.tgz",
|
||||||
|
"integrity": "sha512-TgFyCfIxSujyuqdZKDZ3yTwWiGv+KnlOeXXitCQ+trDODJ+ZtGOzLkSWngynP0HZnTsDyBbPy7GWVXWaEl6lhA==",
|
||||||
|
"cpu": [
|
||||||
|
"mips64el"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-ppc64": {
|
||||||
|
"version": "0.20.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.1.tgz",
|
||||||
|
"integrity": "sha512-b+yuD1IUeL+Y93PmFZDZFIElwbmFfIKLKlYI8M6tRyzE6u7oEP7onGk0vZRh8wfVGC2dZoy0EqX1V8qok4qHaw==",
|
||||||
|
"cpu": [
|
||||||
|
"ppc64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-riscv64": {
|
||||||
|
"version": "0.20.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.1.tgz",
|
||||||
|
"integrity": "sha512-wpDlpE0oRKZwX+GfomcALcouqjjV8MIX8DyTrxfyCfXxoKQSDm45CZr9fanJ4F6ckD4yDEPT98SrjvLwIqUCgg==",
|
||||||
|
"cpu": [
|
||||||
|
"riscv64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-s390x": {
|
||||||
|
"version": "0.20.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.1.tgz",
|
||||||
|
"integrity": "sha512-5BepC2Au80EohQ2dBpyTquqGCES7++p7G+7lXe1bAIvMdXm4YYcEfZtQrP4gaoZ96Wv1Ute61CEHFU7h4FMueQ==",
|
||||||
|
"cpu": [
|
||||||
|
"s390x"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-x64": {
|
||||||
|
"version": "0.20.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.1.tgz",
|
||||||
|
"integrity": "sha512-5gRPk7pKuaIB+tmH+yKd2aQTRpqlf1E4f/mC+tawIm/CGJemZcHZpp2ic8oD83nKgUPMEd0fNanrnFljiruuyA==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/netbsd-x64": {
|
||||||
|
"version": "0.20.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.1.tgz",
|
||||||
|
"integrity": "sha512-4fL68JdrLV2nVW2AaWZBv3XEm3Ae3NZn/7qy2KGAt3dexAgSVT+Hc97JKSZnqezgMlv9x6KV0ZkZY7UO5cNLCg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"netbsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/openbsd-x64": {
|
||||||
|
"version": "0.20.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.1.tgz",
|
||||||
|
"integrity": "sha512-GhRuXlvRE+twf2ES+8REbeCb/zeikNqwD3+6S5y5/x+DYbAQUNl0HNBs4RQJqrechS4v4MruEr8ZtAin/hK5iw==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"openbsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/sunos-x64": {
|
||||||
|
"version": "0.20.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.1.tgz",
|
||||||
|
"integrity": "sha512-ZnWEyCM0G1Ex6JtsygvC3KUUrlDXqOihw8RicRuQAzw+c4f1D66YlPNNV3rkjVW90zXVsHwZYWbJh3v+oQFM9Q==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"sunos"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/win32-arm64": {
|
||||||
|
"version": "0.20.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.1.tgz",
|
||||||
|
"integrity": "sha512-QZ6gXue0vVQY2Oon9WyLFCdSuYbXSoxaZrPuJ4c20j6ICedfsDilNPYfHLlMH7vGfU5DQR0czHLmJvH4Nzis/A==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/win32-ia32": {
|
||||||
|
"version": "0.20.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.1.tgz",
|
||||||
|
"integrity": "sha512-HzcJa1NcSWTAU0MJIxOho8JftNp9YALui3o+Ny7hCh0v5f90nprly1U3Sj1Ldj/CvKKdvvFsCRvDkpsEMp4DNw==",
|
||||||
|
"cpu": [
|
||||||
|
"ia32"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/win32-x64": {
|
||||||
|
"version": "0.20.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.1.tgz",
|
||||||
|
"integrity": "sha512-0MBh53o6XtI6ctDnRMeQ+xoCN8kD2qI1rY1KgF/xdWQwoFeKou7puvDfV8/Wv4Ctx2rRpET/gGdz3YlNtNACSA==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@noble/curves": {
|
||||||
|
"resolved": "..",
|
||||||
|
"link": true
|
||||||
|
},
|
||||||
|
"node_modules/esbuild": {
|
||||||
|
"version": "0.20.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.1.tgz",
|
||||||
|
"integrity": "sha512-OJwEgrpWm/PCMsLVWXKqvcjme3bHNpOgN7Tb6cQnR5n0TPbQx1/Xrn7rqM+wn17bYeT6MGB5sn1Bh5YiGi70nA==",
|
||||||
|
"dev": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"bin": {
|
||||||
|
"esbuild": "bin/esbuild"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@esbuild/aix-ppc64": "0.20.1",
|
||||||
|
"@esbuild/android-arm": "0.20.1",
|
||||||
|
"@esbuild/android-arm64": "0.20.1",
|
||||||
|
"@esbuild/android-x64": "0.20.1",
|
||||||
|
"@esbuild/darwin-arm64": "0.20.1",
|
||||||
|
"@esbuild/darwin-x64": "0.20.1",
|
||||||
|
"@esbuild/freebsd-arm64": "0.20.1",
|
||||||
|
"@esbuild/freebsd-x64": "0.20.1",
|
||||||
|
"@esbuild/linux-arm": "0.20.1",
|
||||||
|
"@esbuild/linux-arm64": "0.20.1",
|
||||||
|
"@esbuild/linux-ia32": "0.20.1",
|
||||||
|
"@esbuild/linux-loong64": "0.20.1",
|
||||||
|
"@esbuild/linux-mips64el": "0.20.1",
|
||||||
|
"@esbuild/linux-ppc64": "0.20.1",
|
||||||
|
"@esbuild/linux-riscv64": "0.20.1",
|
||||||
|
"@esbuild/linux-s390x": "0.20.1",
|
||||||
|
"@esbuild/linux-x64": "0.20.1",
|
||||||
|
"@esbuild/netbsd-x64": "0.20.1",
|
||||||
|
"@esbuild/openbsd-x64": "0.20.1",
|
||||||
|
"@esbuild/sunos-x64": "0.20.1",
|
||||||
|
"@esbuild/win32-arm64": "0.20.1",
|
||||||
|
"@esbuild/win32-ia32": "0.20.1",
|
||||||
|
"@esbuild/win32-x64": "0.20.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
build/package.json
Normal file
18
build/package.json
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"name": "build",
|
||||||
|
"private": true,
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Used to build a single file",
|
||||||
|
"main": "input.js",
|
||||||
|
"keywords": [],
|
||||||
|
"type": "module",
|
||||||
|
"author": "",
|
||||||
|
"license": "MIT",
|
||||||
|
"devDependencies": {
|
||||||
|
"@noble/curves": "file:..",
|
||||||
|
"esbuild": "0.20.1"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "npx esbuild --bundle input.js --outfile=noble-curves.js --global-name=nobleCurves"
|
||||||
|
}
|
||||||
|
}
|
||||||
4
esm/package.json
Normal file
4
esm/package.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"type": "module",
|
||||||
|
"sideEffects": false
|
||||||
|
}
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"type": "module",
|
|
||||||
"browser": {
|
|
||||||
"crypto": false,
|
|
||||||
"./crypto": "./esm/cryptoBrowser.js"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
126
package-lock.json
generated
126
package-lock.json
generated
@@ -1,107 +1,37 @@
|
|||||||
{
|
{
|
||||||
"name": "@noble/curves",
|
"name": "@noble/curves",
|
||||||
"version": "0.6.2",
|
"version": "1.3.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@noble/curves",
|
"name": "@noble/curves",
|
||||||
"version": "0.6.2",
|
"version": "1.3.0",
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"type": "individual",
|
|
||||||
"url": "https://paulmillr.com/funding/"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@noble/hashes": "1.2.0"
|
"@noble/hashes": "1.4.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@scure/base": "~1.1.1",
|
|
||||||
"@scure/bip32": "~1.1.5",
|
|
||||||
"@scure/bip39": "~1.1.1",
|
|
||||||
"@types/node": "18.11.3",
|
|
||||||
"fast-check": "3.0.0",
|
"fast-check": "3.0.0",
|
||||||
"micro-bmark": "0.3.0",
|
"micro-bmark": "0.3.1",
|
||||||
"micro-should": "0.4.0",
|
"micro-should": "0.4.0",
|
||||||
"prettier": "2.8.3",
|
"prettier": "3.1.1",
|
||||||
"typescript": "4.7.3"
|
"typescript": "5.3.2"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@noble/hashes": {
|
"node_modules/@noble/hashes": {
|
||||||
"version": "1.2.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz",
|
||||||
"integrity": "sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==",
|
"integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==",
|
||||||
"funding": [
|
"engines": {
|
||||||
{
|
"node": ">= 16"
|
||||||
"type": "individual",
|
},
|
||||||
|
"funding": {
|
||||||
"url": "https://paulmillr.com/funding/"
|
"url": "https://paulmillr.com/funding/"
|
||||||
}
|
}
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@noble/secp256k1": {
|
|
||||||
"version": "1.7.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.1.tgz",
|
|
||||||
"integrity": "sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==",
|
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"type": "individual",
|
|
||||||
"url": "https://paulmillr.com/funding/"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@scure/base": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz",
|
|
||||||
"integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==",
|
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"type": "individual",
|
|
||||||
"url": "https://paulmillr.com/funding/"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@scure/bip32": {
|
|
||||||
"version": "1.1.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.1.5.tgz",
|
|
||||||
"integrity": "sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw==",
|
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"type": "individual",
|
|
||||||
"url": "https://paulmillr.com/funding/"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"dependencies": {
|
|
||||||
"@noble/hashes": "~1.2.0",
|
|
||||||
"@noble/secp256k1": "~1.7.0",
|
|
||||||
"@scure/base": "~1.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@scure/bip39": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.1.tgz",
|
|
||||||
"integrity": "sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg==",
|
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"type": "individual",
|
|
||||||
"url": "https://paulmillr.com/funding/"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"dependencies": {
|
|
||||||
"@noble/hashes": "~1.2.0",
|
|
||||||
"@scure/base": "~1.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@types/node": {
|
|
||||||
"version": "18.11.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.3.tgz",
|
|
||||||
"integrity": "sha512-fNjDQzzOsZeKZu5NATgXUPsaFaTxeRgFXoosrHivTl8RGeV733OLawXsGfEk9a8/tySyZUyiZ6E8LcjPFZ2y1A==",
|
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/fast-check": {
|
"node_modules/fast-check": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
@@ -120,9 +50,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/micro-bmark": {
|
"node_modules/micro-bmark": {
|
||||||
"version": "0.3.0",
|
"version": "0.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/micro-bmark/-/micro-bmark-0.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/micro-bmark/-/micro-bmark-0.3.1.tgz",
|
||||||
"integrity": "sha512-rYu+AtUq8lC3zPCoxkOOtwhgJoMpCDGe0/BXUCkj6+H9f/U/TunH/n/qkN98yh04dCCtDV8Aj9uYO3+DKxYrcw==",
|
"integrity": "sha512-bNaKObD4yPAAPrpEqp5jO6LJ2sEFgLoFSmRjEY809mJ62+2AehI/K3+RlVpN3Oo92RHpgC2RQhj6b1Tb4dmo+w==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/micro-should": {
|
"node_modules/micro-should": {
|
||||||
@@ -132,15 +62,15 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/prettier": {
|
"node_modules/prettier": {
|
||||||
"version": "2.8.3",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.3.tgz",
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.1.tgz",
|
||||||
"integrity": "sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw==",
|
"integrity": "sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"prettier": "bin-prettier.js"
|
"prettier": "bin/prettier.cjs"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.13.0"
|
"node": ">=14"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/prettier/prettier?sponsor=1"
|
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||||
@@ -163,16 +93,16 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "4.7.3",
|
"version": "5.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz",
|
||||||
"integrity": "sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==",
|
"integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=4.2.0"
|
"node": ">=14.17"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
192
package.json
192
package.json
@@ -1,14 +1,21 @@
|
|||||||
{
|
{
|
||||||
"name": "@noble/curves",
|
"name": "@noble/curves",
|
||||||
"version": "0.6.3",
|
"version": "1.4.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": "cd benchmark; node secp256k1.js; node curves.js; node stark.js; node bls.js",
|
"bench": "cd benchmark; node secp256k1.js; node curves.js; node ecdh.js; node hash-to-curve.js; node modular.js; node bls.js; node ristretto255.js; node decaf448.js",
|
||||||
"build": "tsc && tsc -p tsconfig.esm.json",
|
"build": "tsc && tsc -p tsconfig.esm.json",
|
||||||
"build:release": "rollup -c rollup.config.js",
|
"build:release": "cd build && npm i && npm run build",
|
||||||
|
"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"
|
||||||
@@ -17,149 +24,131 @@
|
|||||||
"homepage": "https://paulmillr.com/noble/",
|
"homepage": "https://paulmillr.com/noble/",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/paulmillr/noble-curves.git"
|
"url": "git+https://github.com/paulmillr/noble-curves.git"
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@noble/hashes": "1.2.0"
|
"@noble/hashes": "1.4.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@scure/base": "~1.1.1",
|
|
||||||
"@scure/bip32": "~1.1.5",
|
|
||||||
"@scure/bip39": "~1.1.1",
|
|
||||||
"@types/node": "18.11.3",
|
|
||||||
"fast-check": "3.0.0",
|
"fast-check": "3.0.0",
|
||||||
"micro-bmark": "0.3.0",
|
"micro-bmark": "0.3.1",
|
||||||
"micro-should": "0.4.0",
|
"micro-should": "0.4.0",
|
||||||
"prettier": "2.8.3",
|
"prettier": "3.1.1",
|
||||||
"typescript": "4.7.3"
|
"typescript": "5.3.2"
|
||||||
},
|
},
|
||||||
|
"sideEffects": false,
|
||||||
"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/curve": {
|
"./abstract/curve": {
|
||||||
"types": "./lib/abstract/curve.d.ts",
|
"types": "./abstract/curve.d.ts",
|
||||||
"import": "./lib/esm/abstract/curve.js",
|
"import": "./esm/abstract/curve.js",
|
||||||
"default": "./lib/abstract/curve.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": {
|
"./abstract/poseidon": {
|
||||||
"types": "./lib/abstract/poseidon.d.ts",
|
"types": "./abstract/poseidon.d.ts",
|
||||||
"import": "./lib/esm/abstract/poseidon.js",
|
"import": "./esm/abstract/poseidon.js",
|
||||||
"default": "./lib/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": [
|
||||||
@@ -176,20 +165,17 @@
|
|||||||
"secp256k1",
|
"secp256k1",
|
||||||
"ed25519",
|
"ed25519",
|
||||||
"ed448",
|
"ed448",
|
||||||
|
"x25519",
|
||||||
|
"ed25519",
|
||||||
"bls12-381",
|
"bls12-381",
|
||||||
"bn254",
|
"bn254",
|
||||||
"pasta",
|
"pasta",
|
||||||
"bls",
|
"bls",
|
||||||
"nist",
|
"noble",
|
||||||
"ecc",
|
"ecc",
|
||||||
"ecdsa",
|
"ecdsa",
|
||||||
"eddsa",
|
"eddsa",
|
||||||
"schnorr"
|
"schnorr"
|
||||||
],
|
],
|
||||||
"funding": [
|
"funding": "https://paulmillr.com/funding/"
|
||||||
{
|
|
||||||
"type": "individual",
|
|
||||||
"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,
|
||||||
|
|||||||
@@ -12,9 +12,13 @@
|
|||||||
* Some projects may prefer to swap this relation, it is not supported for now.
|
* Some projects may prefer to swap this relation, it is not supported for now.
|
||||||
*/
|
*/
|
||||||
import { AffinePoint } from './curve.js';
|
import { AffinePoint } from './curve.js';
|
||||||
import { Field, hashToPrivateScalar } from './modular.js';
|
import { IField, getMinHashLength, mapHashToField } from './modular.js';
|
||||||
import { Hex, PrivKey, CHash, bitLen, bitGet, hexToBytes, bytesToHex } from './utils.js';
|
import { Hex, PrivKey, CHash, bitLen, bitGet, ensureBytes } from './utils.js';
|
||||||
import * as htf from './hash-to-curve.js';
|
// prettier-ignore
|
||||||
|
import {
|
||||||
|
MapToCurve, Opts as HTFOpts, H2CPointConstructor, htfBasicOpts,
|
||||||
|
createHasher
|
||||||
|
} from './hash-to-curve.js';
|
||||||
import {
|
import {
|
||||||
CurvePointsType,
|
CurvePointsType,
|
||||||
ProjPointType as ProjPointType,
|
ProjPointType as ProjPointType,
|
||||||
@@ -24,69 +28,85 @@ import {
|
|||||||
|
|
||||||
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 ShortSignatureCoder<Fp> = {
|
||||||
|
fromHex(hex: Hex): ProjPointType<Fp>;
|
||||||
|
toRawBytes(point: ProjPointType<Fp>): Uint8Array;
|
||||||
|
toHex(point: ProjPointType<Fp>): string;
|
||||||
|
};
|
||||||
|
|
||||||
export type SignatureCoder<Fp2> = {
|
export type SignatureCoder<Fp2> = {
|
||||||
decode(hex: Hex): ProjPointType<Fp2>;
|
fromHex(hex: Hex): ProjPointType<Fp2>;
|
||||||
encode(point: ProjPointType<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>;
|
ShortSignature: SignatureCoder<Fp>;
|
||||||
htfDefaults: htf.Opts;
|
mapToCurve: MapToCurve<Fp>;
|
||||||
|
htfDefaults: HTFOpts;
|
||||||
};
|
};
|
||||||
G2: Omit<CurvePointsType<Fp2>, 'n'> & {
|
G2: Omit<CurvePointsType<Fp2>, 'n'> & {
|
||||||
Signature: SignatureCoder<Fp2>;
|
Signature: SignatureCoder<Fp2>;
|
||||||
mapToCurve: htf.MapToCurve<Fp2>;
|
mapToCurve: MapToCurve<Fp2>;
|
||||||
htfDefaults: htf.Opts;
|
htfDefaults: HTFOpts;
|
||||||
};
|
};
|
||||||
x: bigint;
|
fields: {
|
||||||
Fp: Field<Fp>;
|
Fp: IField<Fp>;
|
||||||
Fr: Field<bigint>;
|
Fr: IField<bigint>;
|
||||||
Fp2: 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: Field<Fp6>;
|
Fp6: IField<Fp6>;
|
||||||
Fp12: Field<Fp12> & {
|
Fp12: IField<Fp12> & {
|
||||||
frobeniusMap(num: Fp12, power: number): Fp12;
|
frobeniusMap(num: Fp12, power: number): Fp12;
|
||||||
multiplyBy014(num: Fp12, o0: Fp2, o1: Fp2, o4: Fp2): Fp12;
|
multiplyBy014(num: Fp12, o0: Fp2, o1: Fp2, o4: Fp2): Fp12;
|
||||||
conjugate(num: Fp12): Fp12;
|
conjugate(num: Fp12): Fp12;
|
||||||
finalExponentiate(num: Fp12): Fp12;
|
finalExponentiate(num: Fp12): Fp12;
|
||||||
};
|
};
|
||||||
htfDefaults: htf.Opts;
|
};
|
||||||
|
params: {
|
||||||
|
x: bigint;
|
||||||
|
r: bigint;
|
||||||
|
};
|
||||||
|
htfDefaults: HTFOpts;
|
||||||
hash: 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: Field<bigint>;
|
|
||||||
Fp: Field<Fp>;
|
|
||||||
Fp2: Field<Fp2>;
|
|
||||||
Fp6: Field<Fp6>;
|
|
||||||
Fp12: Field<Fp12>;
|
|
||||||
G1: CurvePointsRes<Fp>;
|
|
||||||
G2: CurvePointsRes<Fp2>;
|
|
||||||
Signature: SignatureCoder<Fp2>;
|
|
||||||
millerLoop: (ell: [Fp2, Fp2, Fp2][], g1: [Fp, Fp]) => Fp12;
|
|
||||||
calcPairingPrecomputes: (p: AffinePoint<Fp2>) => [Fp2, Fp2, Fp2][];
|
|
||||||
// prettier-ignore
|
|
||||||
hashToCurve: {
|
|
||||||
G1: ReturnType<(typeof htf.hashToCurve<Fp>)>,
|
|
||||||
G2: ReturnType<(typeof htf.hashToCurve<Fp2>)>,
|
|
||||||
},
|
|
||||||
pairing: (P: ProjPointType<Fp>, Q: ProjPointType<Fp2>, withFinalExponent?: boolean) => Fp12;
|
|
||||||
getPublicKey: (privateKey: PrivKey) => Uint8Array;
|
getPublicKey: (privateKey: PrivKey) => Uint8Array;
|
||||||
|
getPublicKeyForShortSignatures: (privateKey: PrivKey) => Uint8Array;
|
||||||
sign: {
|
sign: {
|
||||||
(message: Hex, privateKey: PrivKey): Uint8Array;
|
(message: Hex, privateKey: PrivKey, htfOpts?: htfBasicOpts): Uint8Array;
|
||||||
(message: ProjPointType<Fp2>, privateKey: PrivKey): ProjPointType<Fp2>;
|
(message: ProjPointType<Fp2>, privateKey: PrivKey, htfOpts?: htfBasicOpts): ProjPointType<Fp2>;
|
||||||
|
};
|
||||||
|
signShortSignature: {
|
||||||
|
(message: Hex, privateKey: PrivKey, htfOpts?: htfBasicOpts): Uint8Array;
|
||||||
|
(message: ProjPointType<Fp>, privateKey: PrivKey, htfOpts?: htfBasicOpts): ProjPointType<Fp>;
|
||||||
};
|
};
|
||||||
verify: (
|
verify: (
|
||||||
signature: Hex | ProjPointType<Fp2>,
|
signature: Hex | ProjPointType<Fp2>,
|
||||||
message: Hex | ProjPointType<Fp2>,
|
message: Hex | ProjPointType<Fp2>,
|
||||||
publicKey: Hex | ProjPointType<Fp>
|
publicKey: Hex | ProjPointType<Fp>,
|
||||||
|
htfOpts?: htfBasicOpts
|
||||||
|
) => boolean;
|
||||||
|
verifyShortSignature: (
|
||||||
|
signature: Hex | ProjPointType<Fp>,
|
||||||
|
message: Hex | ProjPointType<Fp>,
|
||||||
|
publicKey: Hex | ProjPointType<Fp2>,
|
||||||
|
htfOpts?: htfBasicOpts
|
||||||
|
) => boolean;
|
||||||
|
verifyBatch: (
|
||||||
|
signature: Hex | ProjPointType<Fp2>,
|
||||||
|
messages: (Hex | ProjPointType<Fp2>)[],
|
||||||
|
publicKeys: (Hex | ProjPointType<Fp>)[],
|
||||||
|
htfOpts?: htfBasicOpts
|
||||||
) => boolean;
|
) => boolean;
|
||||||
aggregatePublicKeys: {
|
aggregatePublicKeys: {
|
||||||
(publicKeys: Hex[]): Uint8Array;
|
(publicKeys: Hex[]): Uint8Array;
|
||||||
@@ -96,25 +116,41 @@ export type CurveFn<Fp, Fp2, Fp6, Fp12> = {
|
|||||||
(signatures: Hex[]): Uint8Array;
|
(signatures: Hex[]): Uint8Array;
|
||||||
(signatures: ProjPointType<Fp2>[]): ProjPointType<Fp2>;
|
(signatures: ProjPointType<Fp2>[]): ProjPointType<Fp2>;
|
||||||
};
|
};
|
||||||
verifyBatch: (
|
aggregateShortSignatures: {
|
||||||
signature: Hex | ProjPointType<Fp2>,
|
(signatures: Hex[]): Uint8Array;
|
||||||
messages: (Hex | ProjPointType<Fp2>)[],
|
(signatures: ProjPointType<Fp>[]): ProjPointType<Fp>;
|
||||||
publicKeys: (Hex | ProjPointType<Fp>)[]
|
};
|
||||||
) => boolean;
|
millerLoop: (ell: [Fp2, Fp2, Fp2][], g1: [Fp, Fp]) => Fp12;
|
||||||
|
pairing: (P: ProjPointType<Fp>, Q: ProjPointType<Fp2>, withFinalExponent?: boolean) => Fp12;
|
||||||
|
G1: CurvePointsRes<Fp> & ReturnType<typeof createHasher<Fp>>;
|
||||||
|
G2: CurvePointsRes<Fp2> & ReturnType<typeof createHasher<Fp2>>;
|
||||||
|
Signature: SignatureCoder<Fp2>;
|
||||||
|
ShortSignature: ShortSignatureCoder<Fp>;
|
||||||
|
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>;
|
||||||
|
};
|
||||||
utils: {
|
utils: {
|
||||||
stringToBytes: typeof htf.stringToBytes;
|
randomPrivateKey: () => Uint8Array;
|
||||||
hashToField: typeof htf.hash_to_field;
|
calcPairingPrecomputes: (p: AffinePoint<Fp2>) => [Fp2, Fp2, Fp2][];
|
||||||
expandMessageXMD: typeof htf.expand_message_xmd;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
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, Fr, Fp2, Fp6, Fp12 } = CURVE;
|
const { Fp, Fr, Fp2, Fp6, Fp12 } = CURVE.fields;
|
||||||
const BLS_X_LEN = bitLen(CURVE.x);
|
const BLS_X_LEN = bitLen(CURVE.params.x);
|
||||||
const groupLen = 32; // TODO: calculate; hardcoded for now
|
|
||||||
|
|
||||||
// Pre-compute coefficients for sparse multiplication
|
// Pre-compute coefficients for sparse multiplication
|
||||||
// Point addition and point double calculations is reused for coefficients
|
// Point addition and point double calculations is reused for coefficients
|
||||||
@@ -129,18 +165,18 @@ export function bls<Fp2, Fp6, Fp12>(
|
|||||||
// Double
|
// Double
|
||||||
let t0 = Fp2.sqr(Ry); // Ry²
|
let t0 = Fp2.sqr(Ry); // Ry²
|
||||||
let t1 = Fp2.sqr(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.sqr(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.sqr(Rx), 3n), // 3 * Rx²
|
Fp2.mul(Fp2.sqr(Rx), _3n), // 3 * Rx²
|
||||||
Fp2.neg(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.sqr(Fp2.div(Fp2.add(t0, t3), 2n)), Fp2.mul(Fp2.sqr(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
|
||||||
@@ -152,7 +188,7 @@ export function bls<Fp2, Fp6, Fp12>(
|
|||||||
let t2 = Fp2.sqr(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.sqr(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
|
||||||
@@ -162,7 +198,7 @@ export function bls<Fp2, Fp6, Fp12>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function millerLoop(ell: [Fp2, Fp2, Fp2][], g1: [Fp, Fp]): Fp12 {
|
function millerLoop(ell: [Fp2, Fp2, Fp2][], g1: [Fp, Fp]): Fp12 {
|
||||||
const { x } = CURVE;
|
const { 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;
|
||||||
@@ -180,31 +216,22 @@ export function bls<Fp2, Fp6, Fp12>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const utils = {
|
const utils = {
|
||||||
hexToBytes: hexToBytes,
|
randomPrivateKey: (): Uint8Array => {
|
||||||
bytesToHex: bytesToHex,
|
const length = getMinHashLength(Fr.ORDER);
|
||||||
stringToBytes: htf.stringToBytes,
|
return mapHashToField(CURVE.randomBytes(length), Fr.ORDER);
|
||||||
// TODO: do we need to export it here?
|
},
|
||||||
hashToField: (
|
calcPairingPrecomputes,
|
||||||
msg: Uint8Array,
|
|
||||||
count: number,
|
|
||||||
options: Partial<typeof CURVE.htfDefaults> = {}
|
|
||||||
) => htf.hash_to_field(msg, count, { ...CURVE.htfDefaults, ...options }),
|
|
||||||
expandMessageXMD: (msg: Uint8Array, DST: Uint8Array, lenInBytes: number, H = CURVE.hash) =>
|
|
||||||
htf.expand_message_xmd(msg, DST, lenInBytes, H),
|
|
||||||
hashToPrivateKey: (hash: Hex): Uint8Array => Fr.toBytes(hashToPrivateScalar(hash, CURVE.r)),
|
|
||||||
randomBytes: (bytesLength: number = groupLen): Uint8Array => CURVE.randomBytes(bytesLength),
|
|
||||||
randomPrivateKey: (): Uint8Array => utils.hashToPrivateKey(utils.randomBytes(groupLen + 8)),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 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_,
|
||||||
});
|
createHasher(G1_.ProjectivePoint, CURVE.G1.mapToCurve, {
|
||||||
const G1HashToCurve = htf.hashToCurve(G1.ProjectivePoint, CURVE.G1.mapToCurve, {
|
|
||||||
...CURVE.htfDefaults,
|
...CURVE.htfDefaults,
|
||||||
...CURVE.G1.htfDefaults,
|
...CURVE.G1.htfDefaults,
|
||||||
});
|
})
|
||||||
|
);
|
||||||
|
|
||||||
// Sparse multiplication against precomputed coefficients
|
// Sparse multiplication against precomputed coefficients
|
||||||
// TODO: replace with weakmap?
|
// TODO: replace with weakmap?
|
||||||
@@ -223,16 +250,16 @@ export function bls<Fp2, Fp6, Fp12>(
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
// 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_,
|
||||||
});
|
createHasher(G2_.ProjectivePoint as H2CPointConstructor<Fp2>, CURVE.G2.mapToCurve, {
|
||||||
const C = G2.ProjectivePoint as htf.H2CPointConstructor<Fp2>; // TODO: fix
|
|
||||||
const G2HashToCurve = htf.hashToCurve(C, CURVE.G2.mapToCurve, {
|
|
||||||
...CURVE.htfDefaults,
|
...CURVE.htfDefaults,
|
||||||
...CURVE.G2.htfDefaults,
|
...CURVE.G2.htfDefaults,
|
||||||
});
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const { ShortSignature } = CURVE.G1;
|
||||||
const { Signature } = CURVE.G2;
|
const { Signature } = CURVE.G2;
|
||||||
|
|
||||||
// Calculates bilinear pairing
|
// Calculates bilinear pairing
|
||||||
@@ -254,31 +281,60 @@ export function bls<Fp2, Fp6, Fp12>(
|
|||||||
function normP1(point: G1Hex): G1 {
|
function normP1(point: G1Hex): G1 {
|
||||||
return point instanceof G1.ProjectivePoint ? (point as G1) : G1.ProjectivePoint.fromHex(point);
|
return point instanceof G1.ProjectivePoint ? (point as G1) : G1.ProjectivePoint.fromHex(point);
|
||||||
}
|
}
|
||||||
function normP2(point: G2Hex): G2 {
|
function normP1Hash(point: G1Hex, htfOpts?: htfBasicOpts): G1 {
|
||||||
return point instanceof G2.ProjectivePoint ? point : Signature.decode(point);
|
return point instanceof G1.ProjectivePoint
|
||||||
|
? point
|
||||||
|
: (G1.hashToCurve(ensureBytes('point', point), htfOpts) as G1);
|
||||||
}
|
}
|
||||||
function normP2Hash(point: G2Hex, htfOpts?: htf.htfBasicOpts): G2 {
|
function normP2(point: G2Hex): G2 {
|
||||||
|
return point instanceof G2.ProjectivePoint ? point : Signature.fromHex(point);
|
||||||
|
}
|
||||||
|
function normP2Hash(point: G2Hex, htfOpts?: htfBasicOpts): G2 {
|
||||||
return point instanceof G2.ProjectivePoint
|
return point instanceof G2.ProjectivePoint
|
||||||
? point
|
? point
|
||||||
: (G2HashToCurve.hashToCurve(point, htfOpts) as G2);
|
: (G2.hashToCurve(ensureBytes('point', point), htfOpts) as G2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Multiplies generator by private key.
|
// Multiplies generator (G1) by private key.
|
||||||
// P = pk x G
|
// P = pk x G
|
||||||
function getPublicKey(privateKey: PrivKey): Uint8Array {
|
function getPublicKey(privateKey: PrivKey): Uint8Array {
|
||||||
return G1.ProjectivePoint.fromPrivateKey(privateKey).toRawBytes(true);
|
return G1.ProjectivePoint.fromPrivateKey(privateKey).toRawBytes(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Multiplies generator (G2) by private key.
|
||||||
|
// P = pk x G
|
||||||
|
function getPublicKeyForShortSignatures(privateKey: PrivKey): Uint8Array {
|
||||||
|
return G2.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, htfOpts?: htf.htfBasicOpts): Uint8Array;
|
function sign(message: Hex, privateKey: PrivKey, htfOpts?: htfBasicOpts): Uint8Array;
|
||||||
function sign(message: G2, privateKey: PrivKey, htfOpts?: htf.htfBasicOpts): G2;
|
function sign(message: G2, privateKey: PrivKey, htfOpts?: htfBasicOpts): G2;
|
||||||
function sign(message: G2Hex, privateKey: PrivKey, htfOpts?: htf.htfBasicOpts): Uint8Array | G2 {
|
function sign(message: G2Hex, privateKey: PrivKey, htfOpts?: htfBasicOpts): Uint8Array | G2 {
|
||||||
const msgPoint = normP2Hash(message, htfOpts);
|
const msgPoint = normP2Hash(message, htfOpts);
|
||||||
msgPoint.assertValidity();
|
msgPoint.assertValidity();
|
||||||
const sigPoint = msgPoint.multiply(G1.normalizePrivateKey(privateKey));
|
const sigPoint = msgPoint.multiply(G1.normPrivateKeyToScalar(privateKey));
|
||||||
if (message instanceof G2.ProjectivePoint) return sigPoint;
|
if (message instanceof G2.ProjectivePoint) return sigPoint;
|
||||||
return Signature.encode(sigPoint);
|
return Signature.toRawBytes(sigPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
function signShortSignature(
|
||||||
|
message: Hex,
|
||||||
|
privateKey: PrivKey,
|
||||||
|
htfOpts?: htfBasicOpts
|
||||||
|
): Uint8Array;
|
||||||
|
function signShortSignature(message: G1, privateKey: PrivKey, htfOpts?: htfBasicOpts): G1;
|
||||||
|
function signShortSignature(
|
||||||
|
message: G1Hex,
|
||||||
|
privateKey: PrivKey,
|
||||||
|
htfOpts?: htfBasicOpts
|
||||||
|
): Uint8Array | G1 {
|
||||||
|
const msgPoint = normP1Hash(message, htfOpts);
|
||||||
|
msgPoint.assertValidity();
|
||||||
|
const sigPoint = msgPoint.multiply(G1.normPrivateKeyToScalar(privateKey));
|
||||||
|
if (message instanceof G1.ProjectivePoint) return sigPoint;
|
||||||
|
return ShortSignature.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.
|
||||||
@@ -287,7 +343,7 @@ export function bls<Fp2, Fp6, Fp12>(
|
|||||||
signature: G2Hex,
|
signature: G2Hex,
|
||||||
message: G2Hex,
|
message: G2Hex,
|
||||||
publicKey: G1Hex,
|
publicKey: G1Hex,
|
||||||
htfOpts?: htf.htfBasicOpts
|
htfOpts?: htfBasicOpts
|
||||||
): boolean {
|
): boolean {
|
||||||
const P = normP1(publicKey);
|
const P = normP1(publicKey);
|
||||||
const Hm = normP2Hash(message, htfOpts);
|
const Hm = normP2Hash(message, htfOpts);
|
||||||
@@ -301,6 +357,26 @@ export function bls<Fp2, Fp6, Fp12>(
|
|||||||
return Fp12.eql(exp, Fp12.ONE);
|
return Fp12.eql(exp, Fp12.ONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Checks if pairing of public key & hash is equal to pairing of generator & signature.
|
||||||
|
// e(S, G) == e(H(m), P)
|
||||||
|
function verifyShortSignature(
|
||||||
|
signature: G1Hex,
|
||||||
|
message: G1Hex,
|
||||||
|
publicKey: G2Hex,
|
||||||
|
htfOpts?: htfBasicOpts
|
||||||
|
): boolean {
|
||||||
|
const P = normP2(publicKey);
|
||||||
|
const Hm = normP1Hash(message, htfOpts);
|
||||||
|
const G = G2.ProjectivePoint.BASE;
|
||||||
|
const S = normP1(signature);
|
||||||
|
// Instead of doing 2 exponentiations, we use property of billinear maps
|
||||||
|
// and do one exp after multiplying 2 points.
|
||||||
|
const eHmP = pairing(Hm, P, false);
|
||||||
|
const eSG = pairing(S, G.negate(), false);
|
||||||
|
const exp = Fp12.finalExponentiate(Fp12.mul(eSG, eHmP));
|
||||||
|
return Fp12.eql(exp, Fp12.ONE);
|
||||||
|
}
|
||||||
|
|
||||||
// Adds a bunch of public key points together.
|
// Adds a bunch of public key points together.
|
||||||
// pk1 + pk2 + pk3 = pkA
|
// pk1 + pk2 + pk3 = pkA
|
||||||
function aggregatePublicKeys(publicKeys: Hex[]): Uint8Array;
|
function aggregatePublicKeys(publicKeys: Hex[]): Uint8Array;
|
||||||
@@ -328,7 +404,21 @@ export function bls<Fp2, Fp6, Fp12>(
|
|||||||
aggAffine.assertValidity();
|
aggAffine.assertValidity();
|
||||||
return aggAffine;
|
return aggAffine;
|
||||||
}
|
}
|
||||||
return Signature.encode(aggAffine);
|
return Signature.toRawBytes(aggAffine);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds a bunch of signature points together.
|
||||||
|
function aggregateShortSignatures(signatures: Hex[]): Uint8Array;
|
||||||
|
function aggregateShortSignatures(signatures: G1[]): G1;
|
||||||
|
function aggregateShortSignatures(signatures: G1Hex[]): Uint8Array | G1 {
|
||||||
|
if (!signatures.length) throw new Error('Expected non-empty array');
|
||||||
|
const agg = signatures.map(normP1).reduce((sum, s) => sum.add(s), G1.ProjectivePoint.ZERO);
|
||||||
|
const aggAffine = agg; //.toAffine();
|
||||||
|
if (signatures[0] instanceof G1.ProjectivePoint) {
|
||||||
|
aggAffine.assertValidity();
|
||||||
|
return aggAffine;
|
||||||
|
}
|
||||||
|
return ShortSignature.toRawBytes(aggAffine);
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://ethresear.ch/t/fast-verification-of-multiple-bls-signatures/5407
|
// https://ethresear.ch/t/fast-verification-of-multiple-bls-signatures/5407
|
||||||
@@ -337,7 +427,7 @@ export function bls<Fp2, Fp6, Fp12>(
|
|||||||
signature: G2Hex,
|
signature: G2Hex,
|
||||||
messages: G2Hex[],
|
messages: G2Hex[],
|
||||||
publicKeys: G1Hex[],
|
publicKeys: G1Hex[],
|
||||||
htfOpts?: htf.htfBasicOpts
|
htfOpts?: htfBasicOpts
|
||||||
): boolean {
|
): boolean {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
// console.log('verifyBatch', bytesToHex(signature as any), messages, publicKeys.map(bytesToHex));
|
// console.log('verifyBatch', bytesToHex(signature as any), messages, publicKeys.map(bytesToHex));
|
||||||
@@ -372,25 +462,35 @@ export function bls<Fp2, Fp6, Fp12>(
|
|||||||
G1.ProjectivePoint.BASE._setWindowSize(4);
|
G1.ProjectivePoint.BASE._setWindowSize(4);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
CURVE,
|
getPublicKey,
|
||||||
|
getPublicKeyForShortSignatures,
|
||||||
|
sign,
|
||||||
|
signShortSignature,
|
||||||
|
verify,
|
||||||
|
verifyBatch,
|
||||||
|
verifyShortSignature,
|
||||||
|
aggregatePublicKeys,
|
||||||
|
aggregateSignatures,
|
||||||
|
aggregateShortSignatures,
|
||||||
|
millerLoop,
|
||||||
|
pairing,
|
||||||
|
G1,
|
||||||
|
G2,
|
||||||
|
Signature,
|
||||||
|
ShortSignature,
|
||||||
|
fields: {
|
||||||
Fr,
|
Fr,
|
||||||
Fp,
|
Fp,
|
||||||
Fp2,
|
Fp2,
|
||||||
Fp6,
|
Fp6,
|
||||||
Fp12,
|
Fp12,
|
||||||
G1,
|
},
|
||||||
G2,
|
params: {
|
||||||
Signature,
|
x: CURVE.params.x,
|
||||||
millerLoop,
|
r: CURVE.params.r,
|
||||||
calcPairingPrecomputes,
|
G1b: CURVE.G1.b,
|
||||||
hashToCurve: { G1: G1HashToCurve, G2: G2HashToCurve },
|
G2b: CURVE.G2.b,
|
||||||
pairing,
|
},
|
||||||
getPublicKey,
|
|
||||||
sign,
|
|
||||||
verify,
|
|
||||||
aggregatePublicKeys,
|
|
||||||
aggregateSignatures,
|
|
||||||
verifyBatch,
|
|
||||||
utils,
|
utils,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
// Abelian group utilities
|
// Abelian group utilities
|
||||||
import { Field, validateField, nLength } from './modular.js';
|
import { IField, validateField, nLength } from './modular.js';
|
||||||
import { validateObject } from './utils.js';
|
import { validateObject } from './utils.js';
|
||||||
const _0n = BigInt(0);
|
const _0n = BigInt(0);
|
||||||
const _1n = BigInt(1);
|
const _1n = BigInt(1);
|
||||||
@@ -168,7 +168,7 @@ export function wNAF<T extends Group<T>>(c: GroupConstructor<T>, bits: number) {
|
|||||||
// Generic BasicCurve interface: works even for polynomial fields (BLS): P, n, h would be ok.
|
// Generic BasicCurve interface: works even for polynomial fields (BLS): P, n, h would be ok.
|
||||||
// Though generator can be different (Fp2 / Fp6 for BLS).
|
// Though generator can be different (Fp2 / Fp6 for BLS).
|
||||||
export type BasicCurve<T> = {
|
export type BasicCurve<T> = {
|
||||||
Fp: Field<T>; // Field over which we'll do calculations (Fp)
|
Fp: IField<T>; // Field over which we'll do calculations (Fp)
|
||||||
n: bigint; // Curve order, total count of valid points in the field
|
n: bigint; // Curve order, total count of valid points in the field
|
||||||
nBitLength?: number; // bit length of curve order
|
nBitLength?: number; // bit length of curve order
|
||||||
nByteLength?: number; // byte length of curve order
|
nByteLength?: number; // byte length of curve order
|
||||||
@@ -195,5 +195,9 @@ export function validateBasic<FP, T>(curve: BasicCurve<FP> & T) {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
// Set defaults
|
// Set defaults
|
||||||
return Object.freeze({ ...nLength(curve.n, curve.nBitLength), ...curve } as const);
|
return Object.freeze({
|
||||||
|
...nLength(curve.n, curve.nBitLength),
|
||||||
|
...curve,
|
||||||
|
...{ p: curve.Fp.ORDER },
|
||||||
|
} as const);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,11 +5,9 @@ import * as ut from './utils.js';
|
|||||||
import { ensureBytes, FHash, Hex } from './utils.js';
|
import { ensureBytes, FHash, Hex } from './utils.js';
|
||||||
import { Group, GroupConstructor, wNAF, BasicCurve, validateBasic, AffinePoint } from './curve.js';
|
import { Group, GroupConstructor, wNAF, BasicCurve, validateBasic, AffinePoint } from './curve.js';
|
||||||
|
|
||||||
// Be friendly to bad ECMAScript parsers by not using bigint literals like 123n
|
// Be friendly to bad ECMAScript parsers by not using bigint literals
|
||||||
const _0n = BigInt(0);
|
// prettier-ignore
|
||||||
const _1n = BigInt(1);
|
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _8n = BigInt(8);
|
||||||
const _2n = BigInt(2);
|
|
||||||
const _8n = BigInt(8);
|
|
||||||
|
|
||||||
// Edwards curves must declare params a & d.
|
// Edwards curves must declare params a & d.
|
||||||
export type CurveType = BasicCurve<bigint> & {
|
export type CurveType = BasicCurve<bigint> & {
|
||||||
@@ -20,10 +18,13 @@ export type CurveType = BasicCurve<bigint> & {
|
|||||||
adjustScalarBytes?: (bytes: Uint8Array) => Uint8Array; // clears bits to get valid field elemtn
|
adjustScalarBytes?: (bytes: Uint8Array) => Uint8Array; // clears bits to get valid field elemtn
|
||||||
domain?: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array; // Used for hashing
|
domain?: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array; // Used for hashing
|
||||||
uvRatio?: (u: bigint, v: bigint) => { isValid: boolean; value: bigint }; // Ratio √(u/v)
|
uvRatio?: (u: bigint, v: bigint) => { isValid: boolean; value: bigint }; // Ratio √(u/v)
|
||||||
preHash?: FHash; // RFC 8032 pre-hashing of messages to sign() / verify()
|
prehash?: FHash; // RFC 8032 pre-hashing of messages to sign() / verify()
|
||||||
mapToCurve?: (scalar: bigint[]) => AffinePoint<bigint>; // for hash-to-curve standard
|
mapToCurve?: (scalar: bigint[]) => AffinePoint<bigint>; // for hash-to-curve standard
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// verification rule is either zip215 or rfc8032 / nist186-5. Consult fromHex:
|
||||||
|
const VERIFY_DEFAULT = { zip215: true };
|
||||||
|
|
||||||
function validateOpts(curve: CurveType) {
|
function validateOpts(curve: CurveType) {
|
||||||
const opts = validateBasic(curve);
|
const opts = validateBasic(curve);
|
||||||
ut.validateObject(
|
ut.validateObject(
|
||||||
@@ -51,6 +52,8 @@ export interface ExtPointType extends Group<ExtPointType> {
|
|||||||
readonly ey: bigint;
|
readonly ey: bigint;
|
||||||
readonly ez: bigint;
|
readonly ez: bigint;
|
||||||
readonly et: bigint;
|
readonly et: bigint;
|
||||||
|
get x(): bigint;
|
||||||
|
get y(): bigint;
|
||||||
assertValidity(): void;
|
assertValidity(): void;
|
||||||
multiply(scalar: bigint): ExtPointType;
|
multiply(scalar: bigint): ExtPointType;
|
||||||
multiplyUnsafe(scalar: bigint): ExtPointType;
|
multiplyUnsafe(scalar: bigint): ExtPointType;
|
||||||
@@ -58,6 +61,8 @@ export interface ExtPointType extends Group<ExtPointType> {
|
|||||||
isTorsionFree(): boolean;
|
isTorsionFree(): boolean;
|
||||||
clearCofactor(): ExtPointType;
|
clearCofactor(): ExtPointType;
|
||||||
toAffine(iz?: bigint): AffinePoint<bigint>;
|
toAffine(iz?: bigint): AffinePoint<bigint>;
|
||||||
|
toRawBytes(isCompressed?: boolean): Uint8Array;
|
||||||
|
toHex(isCompressed?: boolean): string;
|
||||||
}
|
}
|
||||||
// Static methods of Extended Point with coordinates in X, Y, Z, T
|
// Static methods of Extended Point with coordinates in X, Y, Z, T
|
||||||
export interface ExtPointConstructor extends GroupConstructor<ExtPointType> {
|
export interface ExtPointConstructor extends GroupConstructor<ExtPointType> {
|
||||||
@@ -70,8 +75,13 @@ export interface ExtPointConstructor extends GroupConstructor<ExtPointType> {
|
|||||||
export type CurveFn = {
|
export type CurveFn = {
|
||||||
CURVE: ReturnType<typeof validateOpts>;
|
CURVE: ReturnType<typeof validateOpts>;
|
||||||
getPublicKey: (privateKey: Hex) => Uint8Array;
|
getPublicKey: (privateKey: Hex) => Uint8Array;
|
||||||
sign: (message: Hex, privateKey: Hex) => Uint8Array;
|
sign: (message: Hex, privateKey: Hex, options?: { context?: Hex }) => Uint8Array;
|
||||||
verify: (sig: Hex, message: Hex, publicKey: Hex) => boolean;
|
verify: (
|
||||||
|
sig: Hex,
|
||||||
|
message: Hex,
|
||||||
|
publicKey: Hex,
|
||||||
|
options?: { context?: Hex; zip215: boolean }
|
||||||
|
) => boolean;
|
||||||
ExtendedPoint: ExtPointConstructor;
|
ExtendedPoint: ExtPointConstructor;
|
||||||
utils: {
|
utils: {
|
||||||
randomPrivateKey: () => Uint8Array;
|
randomPrivateKey: () => Uint8Array;
|
||||||
@@ -88,8 +98,16 @@ export type CurveFn = {
|
|||||||
// It is not generic twisted curve for now, but ed25519/ed448 generic implementation
|
// It is not generic twisted curve for now, but ed25519/ed448 generic implementation
|
||||||
export function twistedEdwards(curveDef: CurveType): CurveFn {
|
export function twistedEdwards(curveDef: CurveType): CurveFn {
|
||||||
const CURVE = validateOpts(curveDef) as ReturnType<typeof validateOpts>;
|
const CURVE = validateOpts(curveDef) as ReturnType<typeof validateOpts>;
|
||||||
const { Fp, n: CURVE_ORDER, preHash, hash: cHash, randomBytes, nByteLength, h: cofactor } = CURVE;
|
const {
|
||||||
const MASK = _2n ** BigInt(nByteLength * 8);
|
Fp,
|
||||||
|
n: CURVE_ORDER,
|
||||||
|
prehash: prehash,
|
||||||
|
hash: cHash,
|
||||||
|
randomBytes,
|
||||||
|
nByteLength,
|
||||||
|
h: cofactor,
|
||||||
|
} = CURVE;
|
||||||
|
const MASK = _2n << (BigInt(nByteLength * 8) - _1n);
|
||||||
const modP = Fp.create; // Function overrides
|
const modP = Fp.create; // Function overrides
|
||||||
|
|
||||||
// sqrt(u/v)
|
// sqrt(u/v)
|
||||||
@@ -109,7 +127,7 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|||||||
if (ctx.length || phflag) throw new Error('Contexts/pre-hash are not supported');
|
if (ctx.length || phflag) throw new Error('Contexts/pre-hash are not supported');
|
||||||
return data;
|
return data;
|
||||||
}); // NOOP
|
}); // NOOP
|
||||||
const inBig = (n: bigint) => typeof n === 'bigint' && 0n < n; // n in [1..]
|
const inBig = (n: bigint) => typeof n === 'bigint' && _0n < n; // n in [1..]
|
||||||
const inRange = (n: bigint, max: bigint) => inBig(n) && inBig(max) && n < max; // n in [1..max-1]
|
const inRange = (n: bigint, max: bigint) => inBig(n) && inBig(max) && n < max; // n in [1..max-1]
|
||||||
const in0MaskRange = (n: bigint) => n === _0n || inRange(n, MASK); // n in [0..MASK-1]
|
const in0MaskRange = (n: bigint) => n === _0n || inRange(n, MASK); // n in [0..MASK-1]
|
||||||
function assertInRange(n: bigint, max: bigint) {
|
function assertInRange(n: bigint, max: bigint) {
|
||||||
@@ -297,8 +315,9 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|||||||
// Non-constant-time multiplication. Uses double-and-add algorithm.
|
// Non-constant-time multiplication. Uses double-and-add algorithm.
|
||||||
// It's faster, but should only be used when you don't care about
|
// It's faster, but should only be used when you don't care about
|
||||||
// an exposed private key e.g. sig verification.
|
// an exposed private key e.g. sig verification.
|
||||||
|
// Does NOT allow scalars higher than CURVE.n.
|
||||||
multiplyUnsafe(scalar: bigint): Point {
|
multiplyUnsafe(scalar: bigint): Point {
|
||||||
let n = assertGE0(scalar);
|
let n = assertGE0(scalar); // 0 <= scalar < CURVE.n
|
||||||
if (n === _0n) return I;
|
if (n === _0n) return I;
|
||||||
if (this.equals(I) || n === _1n) return this;
|
if (this.equals(I) || n === _1n) return this;
|
||||||
if (this.equals(G)) return this.wNAF(n).p;
|
if (this.equals(G)) return this.wNAF(n).p;
|
||||||
@@ -341,10 +360,10 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|||||||
|
|
||||||
// Converts hash string or Uint8Array to Point.
|
// Converts hash string or Uint8Array to Point.
|
||||||
// Uses algo from RFC8032 5.1.3.
|
// Uses algo from RFC8032 5.1.3.
|
||||||
static fromHex(hex: Hex, strict = true): Point {
|
static fromHex(hex: Hex, zip215 = false): Point {
|
||||||
const { d, a } = CURVE;
|
const { d, a } = CURVE;
|
||||||
const len = Fp.BYTES;
|
const len = Fp.BYTES;
|
||||||
hex = ensureBytes(hex, len); // copy hex to a new array
|
hex = ensureBytes('pointHex', hex, len); // copy hex to a new array
|
||||||
const normed = hex.slice(); // copy again, we'll manipulate it
|
const normed = hex.slice(); // copy again, we'll manipulate it
|
||||||
const lastByte = hex[len - 1]; // select last byte
|
const lastByte = hex[len - 1]; // select last byte
|
||||||
normed[len - 1] = lastByte & ~0x80; // clear last bit
|
normed[len - 1] = lastByte & ~0x80; // clear last bit
|
||||||
@@ -353,8 +372,8 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|||||||
// y=0 is allowed
|
// y=0 is allowed
|
||||||
} else {
|
} else {
|
||||||
// RFC8032 prohibits >= p, but ZIP215 doesn't
|
// RFC8032 prohibits >= p, but ZIP215 doesn't
|
||||||
if (strict) assertInRange(y, Fp.ORDER); // strict=true [1..P-1] (2^255-19-1 for ed25519)
|
if (zip215) assertInRange(y, MASK); // zip215=true [1..P-1] (2^255-19-1 for ed25519)
|
||||||
else assertInRange(y, MASK); // strict=false [1..MASK-1] (2^256-1 for ed25519)
|
else assertInRange(y, Fp.ORDER); // zip215=false [1..MASK-1] (2^256-1 for ed25519)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ed25519: x² = (y²-1)/(dy²+1) mod p. Ed448: x² = (y²-1)/(dy²-1) mod p. Generic case:
|
// Ed25519: x² = (y²-1)/(dy²+1) mod p. Ed448: x² = (y²-1)/(dy²-1) mod p. Generic case:
|
||||||
@@ -365,7 +384,10 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|||||||
let { isValid, value: x } = uvRatio(u, v); // √(u/v)
|
let { isValid, value: x } = uvRatio(u, v); // √(u/v)
|
||||||
if (!isValid) throw new Error('Point.fromHex: invalid y coordinate');
|
if (!isValid) throw new Error('Point.fromHex: invalid y coordinate');
|
||||||
const isXOdd = (x & _1n) === _1n; // There are 2 square roots. Use x_0 bit to select proper
|
const isXOdd = (x & _1n) === _1n; // There are 2 square roots. Use x_0 bit to select proper
|
||||||
const isLastByteOdd = (lastByte & 0x80) !== 0; // if x=0 and x_0 = 1, fail
|
const isLastByteOdd = (lastByte & 0x80) !== 0; // x_0, last bit
|
||||||
|
if (!zip215 && x === _0n && isLastByteOdd)
|
||||||
|
// if x=0 and x_0 = 1, fail
|
||||||
|
throw new Error('Point.fromHex: x=0 and x_0=1');
|
||||||
if (isLastByteOdd !== isXOdd) x = modP(-x); // if x_0 != x mod 2, set x = p-x
|
if (isLastByteOdd !== isXOdd) x = modP(-x); // if x_0 != x mod 2, set x = p-x
|
||||||
return Point.fromAffine({ x, y });
|
return Point.fromAffine({ x, y });
|
||||||
}
|
}
|
||||||
@@ -392,18 +414,14 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|||||||
function modN_LE(hash: Uint8Array): bigint {
|
function modN_LE(hash: Uint8Array): bigint {
|
||||||
return modN(ut.bytesToNumberLE(hash));
|
return modN(ut.bytesToNumberLE(hash));
|
||||||
}
|
}
|
||||||
function isHex(item: Hex, err: string) {
|
|
||||||
if (typeof item !== 'string' && !(item instanceof Uint8Array))
|
|
||||||
throw new Error(`${err} must be hex string or Uint8Array`);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Convenience method that creates public key and other stuff. RFC8032 5.1.5 */
|
/** Convenience method that creates public key and other stuff. RFC8032 5.1.5 */
|
||||||
function getExtendedPublicKey(key: Hex) {
|
function getExtendedPublicKey(key: Hex) {
|
||||||
isHex(key, 'private key');
|
|
||||||
const len = nByteLength;
|
const len = nByteLength;
|
||||||
|
key = ensureBytes('private key', key, len);
|
||||||
// Hash private key with curve's hash function to produce uniformingly random input
|
// Hash private key with curve's hash function to produce uniformingly random input
|
||||||
// Check byte lengths: ensure(64, h(ensure(32, key)))
|
// Check byte lengths: ensure(64, h(ensure(32, key)))
|
||||||
const hashed = ensureBytes(cHash(ensureBytes(key, len)), 2 * len);
|
const hashed = ensureBytes('hashed private key', cHash(key), 2 * len);
|
||||||
const head = adjustScalarBytes(hashed.slice(0, len)); // clear first half bits, produce FE
|
const head = adjustScalarBytes(hashed.slice(0, len)); // clear first half bits, produce FE
|
||||||
const prefix = hashed.slice(len, 2 * len); // second half is called key prefix (5.1.6)
|
const prefix = hashed.slice(len, 2 * len); // second half is called key prefix (5.1.6)
|
||||||
const scalar = modN_LE(head); // The actual private scalar
|
const scalar = modN_LE(head); // The actual private scalar
|
||||||
@@ -420,35 +438,44 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|||||||
// int('LE', SHA512(dom2(F, C) || msgs)) mod N
|
// int('LE', SHA512(dom2(F, C) || msgs)) mod N
|
||||||
function hashDomainToScalar(context: Hex = new Uint8Array(), ...msgs: Uint8Array[]) {
|
function hashDomainToScalar(context: Hex = new Uint8Array(), ...msgs: Uint8Array[]) {
|
||||||
const msg = ut.concatBytes(...msgs);
|
const msg = ut.concatBytes(...msgs);
|
||||||
return modN_LE(cHash(domain(msg, ensureBytes(context), !!preHash)));
|
return modN_LE(cHash(domain(msg, ensureBytes('context', context), !!prehash)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Signs message with privateKey. RFC8032 5.1.6 */
|
/** Signs message with privateKey. RFC8032 5.1.6 */
|
||||||
function sign(msg: Hex, privKey: Hex, context?: Hex): Uint8Array {
|
function sign(msg: Hex, privKey: Hex, options: { context?: Hex } = {}): Uint8Array {
|
||||||
isHex(msg, 'message');
|
msg = ensureBytes('message', msg);
|
||||||
msg = ensureBytes(msg);
|
if (prehash) msg = prehash(msg); // for ed25519ph etc.
|
||||||
if (preHash) msg = preHash(msg); // for ed25519ph etc.
|
|
||||||
const { prefix, scalar, pointBytes } = getExtendedPublicKey(privKey);
|
const { prefix, scalar, pointBytes } = getExtendedPublicKey(privKey);
|
||||||
const r = hashDomainToScalar(context, prefix, msg); // r = dom2(F, C) || prefix || PH(M)
|
const r = hashDomainToScalar(options.context, prefix, msg); // r = dom2(F, C) || prefix || PH(M)
|
||||||
const R = G.multiply(r).toRawBytes(); // R = rG
|
const R = G.multiply(r).toRawBytes(); // R = rG
|
||||||
const k = hashDomainToScalar(context, R, pointBytes, msg); // R || A || PH(M)
|
const k = hashDomainToScalar(options.context, R, pointBytes, msg); // R || A || PH(M)
|
||||||
const s = modN(r + k * scalar); // S = (r + k * s) mod L
|
const s = modN(r + k * scalar); // S = (r + k * s) mod L
|
||||||
assertGE0(s); // 0 <= s < l
|
assertGE0(s); // 0 <= s < l
|
||||||
const res = ut.concatBytes(R, ut.numberToBytesLE(s, Fp.BYTES));
|
const res = ut.concatBytes(R, ut.numberToBytesLE(s, Fp.BYTES));
|
||||||
return ensureBytes(res, nByteLength * 2); // 64-byte signature
|
return ensureBytes('result', res, nByteLength * 2); // 64-byte signature
|
||||||
}
|
}
|
||||||
|
|
||||||
function verify(sig: Hex, msg: Hex, publicKey: Hex, context?: Hex): boolean {
|
const verifyOpts: { context?: Hex; zip215?: boolean } = VERIFY_DEFAULT;
|
||||||
isHex(sig, 'sig');
|
function verify(sig: Hex, msg: Hex, publicKey: Hex, options = verifyOpts): boolean {
|
||||||
isHex(msg, 'message');
|
const { context, zip215 } = options;
|
||||||
const len = Fp.BYTES; // Verifies EdDSA signature against message and public key. RFC8032 5.1.7.
|
const len = Fp.BYTES; // Verifies EdDSA signature against message and public key. RFC8032 5.1.7.
|
||||||
sig = ensureBytes(sig, 2 * len); // An extended group equation is checked.
|
sig = ensureBytes('signature', sig, 2 * len); // An extended group equation is checked.
|
||||||
msg = ensureBytes(msg); // ZIP215 compliant, which means not fully RFC8032 compliant.
|
msg = ensureBytes('message', msg);
|
||||||
if (preHash) msg = preHash(msg); // for ed25519ph, etc
|
if (prehash) msg = prehash(msg); // for ed25519ph, etc
|
||||||
const A = Point.fromHex(publicKey, false); // Check for s bounds, hex validity
|
|
||||||
const R = Point.fromHex(sig.slice(0, len), false); // 0 <= R < 2^256: ZIP215 R can be >= P
|
const s = ut.bytesToNumberLE(sig.slice(len, 2 * len));
|
||||||
const s = ut.bytesToNumberLE(sig.slice(len, 2 * len)); // 0 <= s < l
|
// zip215: true is good for consensus-critical apps and allows points < 2^256
|
||||||
const SB = G.multiplyUnsafe(s);
|
// zip215: false follows RFC8032 / NIST186-5 and restricts points to CURVE.p
|
||||||
|
let A, R, SB;
|
||||||
|
try {
|
||||||
|
A = Point.fromHex(publicKey, zip215);
|
||||||
|
R = Point.fromHex(sig.slice(0, len), zip215);
|
||||||
|
SB = G.multiplyUnsafe(s); // 0 <= s < l is done inside
|
||||||
|
} catch (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!zip215 && A.isSmallOrder()) return false;
|
||||||
|
|
||||||
const k = hashDomainToScalar(context, R.toRawBytes(), A.toRawBytes(), msg);
|
const k = hashDomainToScalar(context, R.toRawBytes(), A.toRawBytes(), msg);
|
||||||
const RkA = R.add(A.multiplyUnsafe(k));
|
const RkA = R.add(A.multiplyUnsafe(k));
|
||||||
// [8][S]B = [8]R + [8][k]A'
|
// [8][S]B = [8]R + [8][k]A'
|
||||||
|
|||||||
@@ -1,45 +1,31 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
import type { Group, GroupConstructor, AffinePoint } from './curve.js';
|
import type { Group, GroupConstructor, AffinePoint } from './curve.js';
|
||||||
import { mod, Field } from './modular.js';
|
import { mod, IField } from './modular.js';
|
||||||
import { CHash, Hex, concatBytes, ensureBytes, validateObject } from './utils.js';
|
import type { CHash } from './utils.js';
|
||||||
|
import { bytesToNumberBE, abytes, concatBytes, utf8ToBytes, validateObject } from './utils.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* * `DST` is a domain separation tag, defined in section 2.2.5
|
||||||
|
* * `p` characteristic of F, where F is a finite field of characteristic p and order q = p^m
|
||||||
|
* * `m` is extension degree (1 for prime fields)
|
||||||
|
* * `k` is the target security target in bits (e.g. 128), from section 5.1
|
||||||
|
* * `expand` is `xmd` (SHA2, SHA3, BLAKE) or `xof` (SHAKE, BLAKE-XOF)
|
||||||
|
* * `hash` conforming to `utils.CHash` interface, with `outputLen` / `blockLen` props
|
||||||
|
*/
|
||||||
|
type UnicodeOrBytes = string | Uint8Array;
|
||||||
export type Opts = {
|
export type Opts = {
|
||||||
DST: string; // DST: a domain separation tag, defined in section 2.2.5
|
DST: UnicodeOrBytes;
|
||||||
encodeDST: string;
|
p: bigint;
|
||||||
p: bigint; // characteristic of F, where F is a finite field of characteristic p and order q = p^m
|
m: number;
|
||||||
m: number; // extension degree of F, m >= 1
|
k: number;
|
||||||
k: number; // k: the target security level for the suite in bits, defined in section 5.1
|
expand: 'xmd' | 'xof';
|
||||||
expand?: 'xmd' | 'xof'; // use a message that has already been processed by expand_message_xmd
|
|
||||||
// Hash functions for: expand_message_xmd is appropriate for use with a
|
|
||||||
// wide range of hash functions, including SHA-2, SHA-3, BLAKE2, and others.
|
|
||||||
// BBS+ uses blake2: https://github.com/hyperledger/aries-framework-go/issues/2247
|
|
||||||
// TODO: verify that hash is shake if expand==='xof' via types
|
|
||||||
hash: CHash;
|
hash: CHash;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Global symbols in both browsers and Node.js since v11
|
// Octet Stream to Integer. "spec" implementation of os2ip is 2.5x slower vs bytesToNumberBE.
|
||||||
// See https://github.com/microsoft/TypeScript/issues/31535
|
const os2ip = bytesToNumberBE;
|
||||||
declare const TextEncoder: any;
|
|
||||||
declare const TextDecoder: any;
|
|
||||||
|
|
||||||
export function stringToBytes(str: string): Uint8Array {
|
// Integer to Octet Stream (numberToBytesBE)
|
||||||
if (typeof str !== 'string') {
|
|
||||||
throw new Error(`utf8ToBytes expected string, got ${typeof str}`);
|
|
||||||
}
|
|
||||||
return new TextEncoder().encode(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Octet Stream to Integer (bytesToNumberBE)
|
|
||||||
function os2ip(bytes: Uint8Array): bigint {
|
|
||||||
let result = 0n;
|
|
||||||
for (let i = 0; i < bytes.length; i++) {
|
|
||||||
result <<= 8n;
|
|
||||||
result += BigInt(bytes[i]);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Integer to Octet Stream
|
|
||||||
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}`);
|
||||||
@@ -60,23 +46,29 @@ function strxor(a: Uint8Array, b: Uint8Array): Uint8Array {
|
|||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function anum(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://www.rfc-editor.org/rfc/rfc9380#section-5.3.1
|
||||||
export function expand_message_xmd(
|
export function expand_message_xmd(
|
||||||
msg: Uint8Array,
|
msg: Uint8Array,
|
||||||
DST: Uint8Array,
|
DST: Uint8Array,
|
||||||
lenInBytes: number,
|
lenInBytes: number,
|
||||||
H: CHash
|
H: CHash
|
||||||
): Uint8Array {
|
): Uint8Array {
|
||||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-5.3.3
|
abytes(msg);
|
||||||
if (DST.length > 255) DST = H(concatBytes(stringToBytes('H2C-OVERSIZE-DST-'), DST));
|
abytes(DST);
|
||||||
const b_in_bytes = H.outputLen;
|
anum(lenInBytes);
|
||||||
const r_in_bytes = H.blockLen;
|
// https://www.rfc-editor.org/rfc/rfc9380#section-5.3.3
|
||||||
|
if (DST.length > 255) DST = H(concatBytes(utf8ToBytes('H2C-OVERSIZE-DST-'), DST));
|
||||||
|
const { outputLen: b_in_bytes, blockLen: r_in_bytes } = H;
|
||||||
const ell = Math.ceil(lenInBytes / b_in_bytes);
|
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));
|
||||||
@@ -88,6 +80,11 @@ export function expand_message_xmd(
|
|||||||
return pseudo_random_bytes.slice(0, lenInBytes);
|
return pseudo_random_bytes.slice(0, lenInBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Produces a uniformly random byte string using an extendable-output function (XOF) H.
|
||||||
|
// 1. The collision resistance of H MUST be at least k bits.
|
||||||
|
// 2. H MUST be an XOF that has been proved indifferentiable from
|
||||||
|
// a random oracle under a reasonable cryptographic assumption.
|
||||||
|
// https://www.rfc-editor.org/rfc/rfc9380#section-5.3.2
|
||||||
export function expand_message_xof(
|
export function expand_message_xof(
|
||||||
msg: Uint8Array,
|
msg: Uint8Array,
|
||||||
DST: Uint8Array,
|
DST: Uint8Array,
|
||||||
@@ -95,11 +92,14 @@ export function expand_message_xof(
|
|||||||
k: number,
|
k: number,
|
||||||
H: CHash
|
H: CHash
|
||||||
): Uint8Array {
|
): Uint8Array {
|
||||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-5.3.3
|
abytes(msg);
|
||||||
|
abytes(DST);
|
||||||
|
anum(lenInBytes);
|
||||||
|
// https://www.rfc-editor.org/rfc/rfc9380#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');
|
||||||
@@ -116,39 +116,52 @@ export function expand_message_xof(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Hashes arbitrary-length byte strings to a list of one or more elements of a finite field F
|
* Hashes arbitrary-length byte strings to a list of one or more elements of a finite field F
|
||||||
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3
|
* https://www.rfc-editor.org/rfc/rfc9380#section-5.2
|
||||||
* @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: Opts): 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: 'stringOrUint8Array',
|
||||||
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);
|
abytes(msg);
|
||||||
} else if (options.expand === 'xof') {
|
anum(count);
|
||||||
pseudo_random_bytes = expand_message_xof(msg, DST, len_in_bytes, options.k, options.hash);
|
const DST = typeof _DST === 'string' ? utf8ToBytes(_DST) : _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(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 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) => {
|
||||||
@@ -176,42 +189,29 @@ export type MapToCurve<T> = (scalar: bigint[]) => AffinePoint<T>;
|
|||||||
|
|
||||||
// Separated from initialization opts, so users won't accidentally change per-curve parameters
|
// Separated from initialization opts, so users won't accidentally change per-curve parameters
|
||||||
// (changing DST is ok!)
|
// (changing DST is ok!)
|
||||||
export type htfBasicOpts = { DST: string };
|
export type htfBasicOpts = { DST: UnicodeOrBytes };
|
||||||
|
|
||||||
export function hashToCurve<T>(
|
export function createHasher<T>(
|
||||||
Point: H2CPointConstructor<T>,
|
Point: H2CPointConstructor<T>,
|
||||||
mapToCurve: MapToCurve<T>,
|
mapToCurve: MapToCurve<T>,
|
||||||
def: Opts
|
def: Opts & { encodeDST?: UnicodeOrBytes }
|
||||||
) {
|
) {
|
||||||
validateObject(def, {
|
if (typeof mapToCurve !== 'function') throw new Error('mapToCurve() must be defined');
|
||||||
DST: 'string',
|
|
||||||
p: 'bigint',
|
|
||||||
m: 'isSafeInteger',
|
|
||||||
k: 'isSafeInteger',
|
|
||||||
hash: 'hash',
|
|
||||||
});
|
|
||||||
if (def.expand !== 'xmd' && def.expand !== 'xof' && def.expand !== undefined)
|
|
||||||
throw new Error('Invalid htf/expand');
|
|
||||||
if (typeof mapToCurve !== 'function')
|
|
||||||
throw new Error('hashToCurve: mapToCurve() has not been defined');
|
|
||||||
return {
|
return {
|
||||||
// Encodes byte string to elliptic curve
|
// Encodes byte string to elliptic curve.
|
||||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3
|
// hash_to_curve from https://www.rfc-editor.org/rfc/rfc9380#section-3
|
||||||
hashToCurve(msg: Hex, options?: htfBasicOpts) {
|
hashToCurve(msg: Uint8Array, options?: htfBasicOpts) {
|
||||||
if (!mapToCurve) throw new Error('CURVE.mapToCurve() has not been defined');
|
|
||||||
msg = ensureBytes(msg);
|
|
||||||
const u = hash_to_field(msg, 2, { ...def, DST: def.DST, ...options } as Opts);
|
const u = hash_to_field(msg, 2, { ...def, DST: def.DST, ...options } as Opts);
|
||||||
const P = Point.fromAffine(mapToCurve(u[0]))
|
const u0 = Point.fromAffine(mapToCurve(u[0]));
|
||||||
.add(Point.fromAffine(mapToCurve(u[1])))
|
const u1 = Point.fromAffine(mapToCurve(u[1]));
|
||||||
.clearCofactor();
|
const P = u0.add(u1).clearCofactor();
|
||||||
P.assertValidity();
|
P.assertValidity();
|
||||||
return P;
|
return P;
|
||||||
},
|
},
|
||||||
|
|
||||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-3
|
// Encodes byte string to elliptic curve.
|
||||||
encodeToCurve(msg: Hex, options?: htfBasicOpts) {
|
// encode_to_curve from https://www.rfc-editor.org/rfc/rfc9380#section-3
|
||||||
if (!mapToCurve) throw new Error('CURVE.mapToCurve() has not been defined');
|
encodeToCurve(msg: Uint8Array, options?: htfBasicOpts) {
|
||||||
msg = ensureBytes(msg);
|
|
||||||
const u = hash_to_field(msg, 1, { ...def, DST: def.encodeDST, ...options } as Opts);
|
const u = hash_to_field(msg, 1, { ...def, DST: def.encodeDST, ...options } as Opts);
|
||||||
const P = Point.fromAffine(mapToCurve(u[0])).clearCofactor();
|
const P = Point.fromAffine(mapToCurve(u[0])).clearCofactor();
|
||||||
P.assertValidity();
|
P.assertValidity();
|
||||||
|
|||||||
@@ -22,10 +22,10 @@ export function mod(a: bigint, b: bigint): bigint {
|
|||||||
return result >= _0n ? result : b + result;
|
return result >= _0n ? result : b + result;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Efficiently exponentiate num to power and do modular division.
|
* Efficiently raise num to power and do modular division.
|
||||||
* Unsafe in some contexts: uses ladder, so can expose bigint bits.
|
* Unsafe in some contexts: uses ladder, so can expose bigint bits.
|
||||||
* @example
|
* @example
|
||||||
* powMod(2n, 6n, 11n) // 64n % 11n == 9n
|
* pow(2n, 6n, 11n) // 64n % 11n == 9n
|
||||||
*/
|
*/
|
||||||
// TODO: use field version && remove
|
// TODO: use field version && remove
|
||||||
export function pow(num: bigint, power: bigint, modulo: bigint): bigint {
|
export function pow(num: bigint, power: bigint, modulo: bigint): bigint {
|
||||||
@@ -55,7 +55,8 @@ export function invert(number: bigint, modulo: bigint): bigint {
|
|||||||
if (number === _0n || modulo <= _0n) {
|
if (number === _0n || modulo <= _0n) {
|
||||||
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/
|
// Euclidean 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
|
||||||
@@ -74,9 +75,14 @@ export function invert(number: bigint, modulo: bigint): bigint {
|
|||||||
return mod(x, modulo);
|
return mod(x, modulo);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tonelli-Shanks algorithm
|
/**
|
||||||
// Paper 1: https://eprint.iacr.org/2012/685.pdf (page 12)
|
* Tonelli-Shanks square root search algorithm.
|
||||||
// Paper 2: Square Roots from 1; 24, 51, 10 to Dan Shanks
|
* 1. https://eprint.iacr.org/2012/685.pdf (page 12)
|
||||||
|
* 2. Square Roots from 1; 24, 51, 10 to Dan Shanks
|
||||||
|
* Will start an infinite loop if field order P is not prime.
|
||||||
|
* @param P field order
|
||||||
|
* @returns function that takes field Fp (created from P) and number n
|
||||||
|
*/
|
||||||
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).
|
||||||
@@ -96,7 +102,7 @@ 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.eql(Fp.sqr(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;
|
||||||
@@ -105,7 +111,7 @@ export function tonelliShanks(P: bigint) {
|
|||||||
|
|
||||||
// Slow-path
|
// Slow-path
|
||||||
const Q1div2 = (Q + _1n) / _2n;
|
const Q1div2 = (Q + _1n) / _2n;
|
||||||
return function tonelliSlow<T>(Fp: Field<T>, n: T): T {
|
return function tonelliSlow<T>(Fp: IField<T>, n: T): T {
|
||||||
// Step 0: Check that n is indeed a square: (n | p) should not be ≡ -1
|
// Step 0: Check that n is indeed a square: (n | p) should not be ≡ -1
|
||||||
if (Fp.pow(n, legendreC) === Fp.neg(Fp.ONE)) throw new Error('Cannot find square root');
|
if (Fp.pow(n, legendreC) === Fp.neg(Fp.ONE)) throw new Error('Cannot find square root');
|
||||||
let r = S;
|
let r = S;
|
||||||
@@ -145,7 +151,7 @@ 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.eql(Fp.sqr(root), n)) throw new Error('Cannot find square root');
|
if (!Fp.eql(Fp.sqr(root), n)) throw new Error('Cannot find square root');
|
||||||
@@ -156,7 +162,7 @@ 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);
|
||||||
@@ -197,12 +203,8 @@ export function FpSqrt(P: bigint) {
|
|||||||
// Little-endian check for first LE bit (last BE bit);
|
// Little-endian check for first LE bit (last BE bit);
|
||||||
export const isNegativeLE = (num: bigint, modulo: bigint) => (mod(num, modulo) & _1n) === _1n;
|
export const isNegativeLE = (num: bigint, modulo: bigint) => (mod(num, modulo) & _1n) === _1n;
|
||||||
|
|
||||||
// Currently completly inconsistent naming:
|
// Field is not always over prime: for example, Fp2 has ORDER(q)=p^m
|
||||||
// - readable: add, mul, sqr, sqrt, inv, div, pow, eq, sub
|
export interface IField<T> {
|
||||||
// - 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
|
|
||||||
export interface Field<T> {
|
|
||||||
ORDER: bigint;
|
ORDER: bigint;
|
||||||
BYTES: number;
|
BYTES: number;
|
||||||
BITS: number;
|
BITS: number;
|
||||||
@@ -231,7 +233,8 @@ export interface Field<T> {
|
|||||||
sqrN(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
|
||||||
|
// [RFC9380](https://www.rfc-editor.org/rfc/rfc9380#section-4.1).
|
||||||
// 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;
|
||||||
@@ -248,7 +251,7 @@ const FIELD_FIELDS = [
|
|||||||
'eql', 'add', 'sub', 'mul', 'pow', 'div',
|
'eql', 'add', 'sub', 'mul', 'pow', 'div',
|
||||||
'addN', 'subN', 'mulN', 'sqrN'
|
'addN', 'subN', 'mulN', 'sqrN'
|
||||||
] as const;
|
] as const;
|
||||||
export function validateField<T>(field: Field<T>) {
|
export function validateField<T>(field: IField<T>) {
|
||||||
const initial = {
|
const initial = {
|
||||||
ORDER: 'bigint',
|
ORDER: 'bigint',
|
||||||
MASK: 'bigint',
|
MASK: 'bigint',
|
||||||
@@ -263,7 +266,12 @@ export function validateField<T>(field: Field<T>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generic field functions
|
// Generic field functions
|
||||||
export function FpPow<T>(f: Field<T>, num: T, power: bigint): T {
|
|
||||||
|
/**
|
||||||
|
* Same as `pow` but for Fp: non-constant-time.
|
||||||
|
* Unsafe in some contexts: uses ladder, so can expose bigint bits.
|
||||||
|
*/
|
||||||
|
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');
|
||||||
@@ -274,12 +282,16 @@ export function FpPow<T>(f: Field<T>, num: T, power: bigint): T {
|
|||||||
while (power > _0n) {
|
while (power > _0n) {
|
||||||
if (power & _1n) p = f.mul(p, d);
|
if (power & _1n) p = f.mul(p, d);
|
||||||
d = f.sqr(d);
|
d = f.sqr(d);
|
||||||
power >>= 1n;
|
power >>= _1n;
|
||||||
}
|
}
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FpInvertBatch<T>(f: Field<T>, nums: T[]): T[] {
|
/**
|
||||||
|
* Efficiently invert an array of Field elements.
|
||||||
|
* `inv(0)` will return `undefined` here: make sure to throw an error.
|
||||||
|
*/
|
||||||
|
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) => {
|
||||||
@@ -298,12 +310,12 @@ export function FpInvertBatch<T>(f: Field<T>, nums: T[]): T[] {
|
|||||||
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.inv(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);
|
||||||
@@ -319,18 +331,26 @@ export function nLength(n: bigint, nBitLength?: number) {
|
|||||||
return { nBitLength: _nBitLength, nByteLength };
|
return { nBitLength: _nBitLength, nByteLength };
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: very fragile, always bench. Major performance points:
|
type FpField = IField<bigint> & Required<Pick<IField<bigint>, 'isOdd'>>;
|
||||||
// - NonNormalized ops
|
/**
|
||||||
// - Object.freeze
|
* Initializes a finite field over prime. **Non-primes are not supported.**
|
||||||
// - same shape of object (don't add/remove keys)
|
* Do not init in loop: slow. Very fragile: always run a benchmark on a change.
|
||||||
type FpField = Field<bigint> & Required<Pick<Field<bigint>, 'isOdd'>>;
|
* Major performance optimizations:
|
||||||
export function Fp(
|
* * a) denormalized operations like mulN instead of mul
|
||||||
|
* * b) same object shape: never add or remove keys
|
||||||
|
* * c) Object.freeze
|
||||||
|
* @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 Field ORDER > 0, got ${ORDER}`);
|
||||||
const { nBitLength: BITS, nByteLength: BYTES } = 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);
|
||||||
@@ -381,33 +401,30 @@ export function Fp(
|
|||||||
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.neg(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.neg(root) : root;
|
return Fp.isOdd(root) ? Fp.neg(root) : root;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FIPS 186 B.4.1-compliant "constant-time" private key generation utility.
|
* "Constant-time" private key generation utility.
|
||||||
* Can take (n+8) or more bytes of uniform input e.g. from CSPRNG or KDF
|
* Same as mapKeyToField, but accepts less bytes (40 instead of 48 for 32-byte field).
|
||||||
* and convert them into private scalar, with the modulo bias being neglible.
|
* Which makes it slightly more biased, less secure.
|
||||||
* Needs at least 40 bytes of input for 32-byte private key.
|
* @deprecated use mapKeyToField instead
|
||||||
* 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(
|
export function hashToPrivateScalar(
|
||||||
hash: string | Uint8Array,
|
hash: string | Uint8Array,
|
||||||
groupOrder: bigint,
|
groupOrder: bigint,
|
||||||
isLE = false
|
isLE = false
|
||||||
): bigint {
|
): bigint {
|
||||||
hash = ensureBytes(hash);
|
hash = ensureBytes('privateHash', hash);
|
||||||
const hashLen = hash.length;
|
const hashLen = hash.length;
|
||||||
const minLen = nLength(groupOrder).nByteLength + 8;
|
const minLen = nLength(groupOrder).nByteLength + 8;
|
||||||
if (minLen < 24 || hashLen < minLen || hashLen > 1024)
|
if (minLen < 24 || hashLen < minLen || hashLen > 1024)
|
||||||
@@ -415,3 +432,53 @@ export function hashToPrivateScalar(
|
|||||||
const num = isLE ? bytesToNumberLE(hash) : bytesToNumberBE(hash);
|
const num = isLE ? bytesToNumberLE(hash) : bytesToNumberBE(hash);
|
||||||
return mod(num, groupOrder - _1n) + _1n;
|
return mod(num, groupOrder - _1n) + _1n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns total number of bytes consumed by the field element.
|
||||||
|
* For example, 32 bytes for usual 256-bit weierstrass curve.
|
||||||
|
* @param fieldOrder number of field elements, usually CURVE.n
|
||||||
|
* @returns byte length of field
|
||||||
|
*/
|
||||||
|
export function getFieldBytesLength(fieldOrder: bigint): number {
|
||||||
|
if (typeof fieldOrder !== 'bigint') throw new Error('field order must be bigint');
|
||||||
|
const bitLength = fieldOrder.toString(2).length;
|
||||||
|
return Math.ceil(bitLength / 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns minimal amount of bytes that can be safely reduced
|
||||||
|
* by field order.
|
||||||
|
* Should be 2^-128 for 128-bit curve such as P256.
|
||||||
|
* @param fieldOrder number of field elements, usually CURVE.n
|
||||||
|
* @returns byte length of target hash
|
||||||
|
*/
|
||||||
|
export function getMinHashLength(fieldOrder: bigint): number {
|
||||||
|
const length = getFieldBytesLength(fieldOrder);
|
||||||
|
return length + Math.ceil(length / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "Constant-time" private key generation utility.
|
||||||
|
* Can take (n + n/2) or more bytes of uniform input e.g. from CSPRNG or KDF
|
||||||
|
* and convert them into private scalar, with the modulo bias being negligible.
|
||||||
|
* Needs at least 48 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/
|
||||||
|
* FIPS 186-5, A.2 https://csrc.nist.gov/publications/detail/fips/186/5/final
|
||||||
|
* RFC 9380, https://www.rfc-editor.org/rfc/rfc9380#section-5
|
||||||
|
* @param hash hash output from SHA3 or a similar function
|
||||||
|
* @param groupOrder size of subgroup - (e.g. secp256k1.CURVE.n)
|
||||||
|
* @param isLE interpret hash bytes as LE num
|
||||||
|
* @returns valid private scalar
|
||||||
|
*/
|
||||||
|
export function mapHashToField(key: Uint8Array, fieldOrder: bigint, isLE = false): Uint8Array {
|
||||||
|
const len = key.length;
|
||||||
|
const fieldLen = getFieldBytesLength(fieldOrder);
|
||||||
|
const minLen = getMinHashLength(fieldOrder);
|
||||||
|
// No small numbers: need to understand bias story. No huge numbers: easier to detect JS timings.
|
||||||
|
if (len < 16 || len < minLen || len > 1024)
|
||||||
|
throw new Error(`expected ${minLen}-1024 bytes of input, got ${len}`);
|
||||||
|
const num = isLE ? bytesToNumberBE(key) : bytesToNumberLE(key);
|
||||||
|
// `mod(x, 11)` can sometimes produce 0. `mod(x, 10) + 1` is the same, but no 0
|
||||||
|
const reduced = mod(num, fieldOrder - _1n) + _1n;
|
||||||
|
return isLE ? numberToBytesLE(reduced, fieldLen) : numberToBytesBE(reduced, fieldLen);
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,25 +11,27 @@ export type CurveType = {
|
|||||||
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) {
|
||||||
validateObject(
|
validateObject(
|
||||||
curve,
|
curve,
|
||||||
{
|
{
|
||||||
a24: 'bigint',
|
a: 'bigint',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
montgomeryBits: 'isSafeInteger',
|
montgomeryBits: 'isSafeInteger',
|
||||||
@@ -37,7 +39,7 @@ function validateOpts(curve: CurveType) {
|
|||||||
adjustScalarBytes: 'function',
|
adjustScalarBytes: 'function',
|
||||||
domain: 'function',
|
domain: 'function',
|
||||||
powPminus2: 'function',
|
powPminus2: 'function',
|
||||||
Gu: 'string',
|
Gu: 'bigint',
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
// Set defaults
|
// Set defaults
|
||||||
@@ -49,7 +51,7 @@ 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(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;
|
||||||
@@ -73,12 +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 {
|
function assertFieldElement(n: bigint): bigint {
|
||||||
if (typeof n === 'bigint' && _0n <= n && n < P) return n;
|
if (typeof n === 'bigint' && _0n <= n && n < P) return n;
|
||||||
throw new Error('Expected valid scalar 0 < scalar < CURVE.P');
|
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
|
||||||
@@ -90,8 +95,6 @@ export function montgomery(curveDef: CurveType): CurveFn {
|
|||||||
// 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 = assertFieldElement(scalar);
|
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;
|
||||||
@@ -147,17 +150,15 @@ export function montgomery(curveDef: CurveType): CurveFn {
|
|||||||
function decodeUCoordinate(uEnc: Hex): bigint {
|
function decodeUCoordinate(uEnc: Hex): bigint {
|
||||||
// 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
|
const u = ensureBytes('u coordinate', uEnc, montgomeryBytes);
|
||||||
// fieldLen - scalaryBytes = 1 for X448 and = 0 for X25519
|
if (fieldLen === 32) u[31] &= 127; // 0b0111_1111
|
||||||
const u = ensureBytes(uEnc, montgomeryBytes);
|
|
||||||
// u[fieldLen-1] crashes QuickJS (TypeError: out-of-bound numeric index)
|
|
||||||
if (fieldLen === montgomeryBytes) u[fieldLen - 1] &= 127; // 0b0111_1111
|
|
||||||
return bytesToNumberLE(u);
|
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)
|
const len = bytes.length;
|
||||||
throw new Error(`Expected ${montgomeryBytes} or ${fieldLen} bytes, got ${bytes.length}`);
|
if (len !== montgomeryBytes && len !== fieldLen)
|
||||||
|
throw new Error(`Expected ${montgomeryBytes} or ${fieldLen} bytes, got ${len}`);
|
||||||
return bytesToNumberLE(adjustScalarBytes(bytes));
|
return bytesToNumberLE(adjustScalarBytes(bytes));
|
||||||
}
|
}
|
||||||
function scalarMult(scalar: Hex, u: Hex): Uint8Array {
|
function scalarMult(scalar: Hex, u: Hex): Uint8Array {
|
||||||
@@ -170,8 +171,9 @@ export function montgomery(curveDef: CurveType): CurveFn {
|
|||||||
return encodeUCoordinate(pu);
|
return encodeUCoordinate(pu);
|
||||||
}
|
}
|
||||||
// Computes public key from private. By doing scalar multiplication of base point.
|
// Computes public key from private. By doing scalar multiplication of base point.
|
||||||
|
const GuBytes = encodeUCoordinate(CURVE.Gu);
|
||||||
function scalarMultBase(scalar: Hex): Uint8Array {
|
function scalarMultBase(scalar: Hex): Uint8Array {
|
||||||
return scalarMult(scalar, CURVE.Gu);
|
return scalarMult(scalar, GuBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -179,6 +181,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,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
// Poseidon Hash: https://eprint.iacr.org/2019/458.pdf, https://www.poseidon-hash.info
|
// Poseidon Hash: https://eprint.iacr.org/2019/458.pdf, https://www.poseidon-hash.info
|
||||||
import { Field, FpPow, validateField } from './modular.js';
|
import { IField, FpPow, validateField } from './modular.js';
|
||||||
// We don't provide any constants, since different implementations use different constants.
|
// We don't provide any constants, since different implementations use different constants.
|
||||||
// For reference constants see './test/poseidon.test.js'.
|
// For reference constants see './test/poseidon.test.js'.
|
||||||
export type PoseidonOpts = {
|
export type PoseidonOpts = {
|
||||||
Fp: Field<bigint>;
|
Fp: IField<bigint>;
|
||||||
t: number;
|
t: number;
|
||||||
roundsFull: number;
|
roundsFull: number;
|
||||||
roundsPartial: number;
|
roundsPartial: number;
|
||||||
@@ -15,34 +15,36 @@ export type PoseidonOpts = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function validateOpts(opts: PoseidonOpts) {
|
export function validateOpts(opts: PoseidonOpts) {
|
||||||
const { Fp } = opts;
|
const { Fp, mds, reversePartialPowIdx: rev, roundConstants: rc } = opts;
|
||||||
|
const { roundsFull, roundsPartial, sboxPower, t } = opts;
|
||||||
|
|
||||||
validateField(Fp);
|
validateField(Fp);
|
||||||
for (const i of ['t', 'roundsFull', 'roundsPartial'] as const) {
|
for (const i of ['t', 'roundsFull', 'roundsPartial'] as const) {
|
||||||
if (typeof opts[i] !== 'number' || !Number.isSafeInteger(opts[i]))
|
if (typeof opts[i] !== 'number' || !Number.isSafeInteger(opts[i]))
|
||||||
throw new Error(`Poseidon: invalid param ${i}=${opts[i]} (${typeof 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);
|
// MDS is TxT matrix
|
||||||
let sboxFn = (n: bigint) => FpPow(Fp, n, _sboxPower);
|
if (!Array.isArray(mds) || mds.length !== t) throw new Error('Poseidon: wrong MDS matrix');
|
||||||
// Unwrapped sbox power for common cases (195->142μs)
|
const _mds = mds.map((mdsRow) => {
|
||||||
if (sboxPower === 3) sboxFn = (n: bigint) => Fp.mul(Fp.sqrN(n), n);
|
if (!Array.isArray(mdsRow) || mdsRow.length !== t)
|
||||||
else if (sboxPower === 5) sboxFn = (n: bigint) => Fp.mul(Fp.sqrN(Fp.sqrN(n)), n);
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
if (opts.roundsFull % 2 !== 0)
|
if (rev !== undefined && typeof rev !== 'boolean')
|
||||||
throw new Error(`Poseidon roundsFull is not even: ${opts.roundsFull}`);
|
throw new Error(`Poseidon: invalid param reversePartialPowIdx=${rev}`);
|
||||||
const rounds = opts.roundsFull + opts.roundsPartial;
|
|
||||||
|
|
||||||
if (!Array.isArray(opts.roundConstants) || opts.roundConstants.length !== rounds)
|
if (roundsFull % 2 !== 0) throw new Error(`Poseidon roundsFull is not even: ${roundsFull}`);
|
||||||
|
const rounds = roundsFull + roundsPartial;
|
||||||
|
|
||||||
|
if (!Array.isArray(rc) || rc.length !== rounds)
|
||||||
throw new Error('Poseidon: wrong round constants');
|
throw new Error('Poseidon: wrong round constants');
|
||||||
const roundConstants = opts.roundConstants.map((rc) => {
|
const roundConstants = rc.map((rc) => {
|
||||||
if (!Array.isArray(rc) || rc.length !== opts.t)
|
if (!Array.isArray(rc) || rc.length !== t)
|
||||||
throw new Error(`Poseidon wrong round constants: ${rc}`);
|
throw new Error(`Poseidon wrong round constants: ${rc}`);
|
||||||
return rc.map((i) => {
|
return rc.map((i) => {
|
||||||
if (typeof i !== 'bigint' || !Fp.isValid(i))
|
if (typeof i !== 'bigint' || !Fp.isValid(i))
|
||||||
@@ -50,18 +52,16 @@ export function validateOpts(opts: PoseidonOpts) {
|
|||||||
return Fp.create(i);
|
return Fp.create(i);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
// MDS is TxT matrix
|
|
||||||
if (!Array.isArray(opts.mds) || opts.mds.length !== opts.t)
|
if (!sboxPower || ![3, 5, 7].includes(sboxPower))
|
||||||
throw new Error('Poseidon: wrong MDS matrix');
|
throw new Error(`Poseidon wrong sboxPower=${sboxPower}`);
|
||||||
const mds = opts.mds.map((mdsRow) => {
|
const _sboxPower = BigInt(sboxPower);
|
||||||
if (!Array.isArray(mdsRow) || mdsRow.length !== opts.t)
|
let sboxFn = (n: bigint) => FpPow(Fp, n, _sboxPower);
|
||||||
throw new Error(`Poseidon MDS matrix row: ${mdsRow}`);
|
// Unwrapped sbox power for common cases (195->142μs)
|
||||||
return mdsRow.map((i) => {
|
if (sboxPower === 3) sboxFn = (n: bigint) => Fp.mul(Fp.sqrN(n), n);
|
||||||
if (typeof i !== 'bigint') throw new Error(`Poseidon MDS matrix value=${i}`);
|
else if (sboxPower === 5) sboxFn = (n: bigint) => Fp.mul(Fp.sqrN(Fp.sqrN(n)), n);
|
||||||
return Fp.create(i);
|
|
||||||
});
|
return Object.freeze({ ...opts, rounds, sboxFn, roundConstants, mds: _mds });
|
||||||
});
|
|
||||||
return Object.freeze({ ...opts, rounds, sboxFn, roundConstants, mds });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function splitConstants(rc: bigint[], t: number) {
|
export function splitConstants(rc: bigint[], t: number) {
|
||||||
@@ -80,18 +80,17 @@ export function splitConstants(rc: bigint[], t: number) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function poseidon(opts: PoseidonOpts) {
|
export function poseidon(opts: PoseidonOpts) {
|
||||||
const { t, Fp, rounds, sboxFn, reversePartialPowIdx } = validateOpts(opts);
|
const _opts = validateOpts(opts);
|
||||||
const halfRoundsFull = Math.floor(opts.roundsFull / 2);
|
const { Fp, mds, roundConstants, rounds, roundsPartial, sboxFn, t } = _opts;
|
||||||
const partialIdx = reversePartialPowIdx ? t - 1 : 0;
|
const halfRoundsFull = _opts.roundsFull / 2;
|
||||||
|
const partialIdx = _opts.reversePartialPowIdx ? t - 1 : 0;
|
||||||
const poseidonRound = (values: bigint[], isFull: boolean, idx: number) => {
|
const poseidonRound = (values: bigint[], isFull: boolean, idx: number) => {
|
||||||
values = values.map((i, j) => Fp.add(i, opts.roundConstants[idx][j]));
|
values = values.map((i, j) => Fp.add(i, roundConstants[idx][j]));
|
||||||
|
|
||||||
if (isFull) values = values.map((i) => sboxFn(i));
|
if (isFull) values = values.map((i) => sboxFn(i));
|
||||||
else values[partialIdx] = sboxFn(values[partialIdx]);
|
else values[partialIdx] = sboxFn(values[partialIdx]);
|
||||||
// Matrix multiplication
|
// Matrix multiplication
|
||||||
values = opts.mds.map((i) =>
|
values = mds.map((i) => i.reduce((acc, i, j) => Fp.add(acc, Fp.mulN(i, values[j])), Fp.ZERO));
|
||||||
i.reduce((acc, i, j) => Fp.add(acc, Fp.mulN(i, values[j])), Fp.ZERO)
|
|
||||||
);
|
|
||||||
return values;
|
return values;
|
||||||
};
|
};
|
||||||
const poseidonHash = function poseidonHash(values: bigint[]) {
|
const poseidonHash = function poseidonHash(values: bigint[]) {
|
||||||
@@ -105,7 +104,7 @@ export function poseidon(opts: PoseidonOpts) {
|
|||||||
// Apply r_f/2 full rounds.
|
// Apply r_f/2 full rounds.
|
||||||
for (let i = 0; i < halfRoundsFull; i++) values = poseidonRound(values, true, round++);
|
for (let i = 0; i < halfRoundsFull; i++) values = poseidonRound(values, true, round++);
|
||||||
// Apply r_p partial rounds.
|
// Apply r_p partial rounds.
|
||||||
for (let i = 0; i < opts.roundsPartial; i++) values = poseidonRound(values, false, round++);
|
for (let i = 0; i < roundsPartial; i++) values = poseidonRound(values, false, round++);
|
||||||
// Apply r_f/2 full rounds.
|
// Apply r_f/2 full rounds.
|
||||||
for (let i = 0; i < halfRoundsFull; i++) values = poseidonRound(values, true, round++);
|
for (let i = 0; i < halfRoundsFull; i++) values = poseidonRound(values, true, round++);
|
||||||
|
|
||||||
@@ -114,6 +113,6 @@ export function poseidon(opts: PoseidonOpts) {
|
|||||||
return values;
|
return values;
|
||||||
};
|
};
|
||||||
// For verification in tests
|
// For verification in tests
|
||||||
poseidonHash.roundConstants = opts.roundConstants;
|
poseidonHash.roundConstants = roundConstants;
|
||||||
return poseidonHash;
|
return poseidonHash;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
|
// 100 lines of code in the file are duplicated from noble-hashes (utils).
|
||||||
|
// This is OK: `abstract` directory does not use noble-hashes.
|
||||||
|
// User may opt-in into using different hashing library. This way, noble-hashes
|
||||||
|
// won't be included into their bundle.
|
||||||
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;
|
export type Hex = Uint8Array | string; // hex strings are accepted for simplicity
|
||||||
|
export type PrivKey = Hex | bigint; // bigints are accepted to ease learning curve
|
||||||
// We accept hex strings besides Uint8Array for simplicity
|
|
||||||
export type Hex = Uint8Array | string;
|
|
||||||
// Very few implementations accept numbers, we do it to ease learning curve
|
|
||||||
export type PrivKey = Hex | bigint;
|
|
||||||
export type CHash = {
|
export type CHash = {
|
||||||
(message: Uint8Array | string): Uint8Array;
|
(message: Uint8Array | string): Uint8Array;
|
||||||
blockLen: number;
|
blockLen: number;
|
||||||
@@ -16,9 +16,26 @@ export type CHash = {
|
|||||||
};
|
};
|
||||||
export type FHash = (message: Uint8Array | string) => Uint8Array;
|
export type FHash = (message: Uint8Array | string) => Uint8Array;
|
||||||
|
|
||||||
const hexes = Array.from({ length: 256 }, (v, i) => i.toString(16).padStart(2, '0'));
|
export function isBytes(a: unknown): a is Uint8Array {
|
||||||
|
return (
|
||||||
|
a instanceof Uint8Array ||
|
||||||
|
(a != null && typeof a === 'object' && a.constructor.name === 'Uint8Array')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function abytes(item: unknown): void {
|
||||||
|
if (!isBytes(item)) throw new Error('Uint8Array expected');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Array where index 0xf0 (240) is mapped to string 'f0'
|
||||||
|
const hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) =>
|
||||||
|
i.toString(16).padStart(2, '0')
|
||||||
|
);
|
||||||
|
/**
|
||||||
|
* @example bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23])) // 'cafe0123'
|
||||||
|
*/
|
||||||
export function bytesToHex(bytes: Uint8Array): string {
|
export function bytesToHex(bytes: Uint8Array): string {
|
||||||
if (!u8a(bytes)) throw new Error('Uint8Array expected');
|
abytes(bytes);
|
||||||
// pre-caching improves the speed 6x
|
// pre-caching improves the speed 6x
|
||||||
let hex = '';
|
let hex = '';
|
||||||
for (let i = 0; i < bytes.length; i++) {
|
for (let i = 0; i < bytes.length; i++) {
|
||||||
@@ -33,92 +50,234 @@ export function numberToHexUnpadded(num: number | bigint): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function hexToNumber(hex: string): bigint {
|
export function hexToNumber(hex: string): bigint {
|
||||||
if (typeof hex !== 'string') throw new Error('string expected, got ' + typeof hex);
|
if (typeof hex !== 'string') throw new Error('hex string expected, got ' + typeof hex);
|
||||||
// Big Endian
|
// Big Endian
|
||||||
return BigInt(hex === '' ? '0' : `0x${hex}`);
|
return BigInt(hex === '' ? '0' : `0x${hex}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Caching slows it down 2-3x
|
// We use optimized technique to convert hex string to byte array
|
||||||
|
const asciis = { _0: 48, _9: 57, _A: 65, _F: 70, _a: 97, _f: 102 } as const;
|
||||||
|
function asciiToBase16(char: number): number | undefined {
|
||||||
|
if (char >= asciis._0 && char <= asciis._9) return char - asciis._0;
|
||||||
|
if (char >= asciis._A && char <= asciis._F) return char - (asciis._A - 10);
|
||||||
|
if (char >= asciis._a && char <= asciis._f) return char - (asciis._a - 10);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @example hexToBytes('cafe0123') // Uint8Array.from([0xca, 0xfe, 0x01, 0x23])
|
||||||
|
*/
|
||||||
export function hexToBytes(hex: string): Uint8Array {
|
export function hexToBytes(hex: string): Uint8Array {
|
||||||
if (typeof hex !== 'string') throw new Error('string expected, got ' + typeof hex);
|
if (typeof hex !== 'string') throw new Error('hex string expected, got ' + typeof hex);
|
||||||
if (hex.length % 2) throw new Error('hex string is invalid: unpadded ' + hex.length);
|
const hl = hex.length;
|
||||||
const array = new Uint8Array(hex.length / 2);
|
const al = hl / 2;
|
||||||
for (let i = 0; i < array.length; i++) {
|
if (hl % 2) throw new Error('padded hex string expected, got unpadded hex of length ' + hl);
|
||||||
const j = i * 2;
|
const array = new Uint8Array(al);
|
||||||
const hexByte = hex.slice(j, j + 2);
|
for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {
|
||||||
const byte = Number.parseInt(hexByte, 16);
|
const n1 = asciiToBase16(hex.charCodeAt(hi));
|
||||||
if (Number.isNaN(byte) || byte < 0) throw new Error('invalid byte sequence');
|
const n2 = asciiToBase16(hex.charCodeAt(hi + 1));
|
||||||
array[i] = byte;
|
if (n1 === undefined || n2 === undefined) {
|
||||||
|
const char = hex[hi] + hex[hi + 1];
|
||||||
|
throw new Error('hex string expected, got non-hex character "' + char + '" at index ' + hi);
|
||||||
|
}
|
||||||
|
array[ai] = n1 * 16 + n2;
|
||||||
}
|
}
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Big Endian
|
// BE: Big Endian, LE: Little Endian
|
||||||
export function bytesToNumberBE(bytes: Uint8Array): bigint {
|
export function bytesToNumberBE(bytes: Uint8Array): bigint {
|
||||||
return hexToNumber(bytesToHex(bytes));
|
return hexToNumber(bytesToHex(bytes));
|
||||||
}
|
}
|
||||||
export function bytesToNumberLE(bytes: Uint8Array): bigint {
|
export function bytesToNumberLE(bytes: Uint8Array): bigint {
|
||||||
if (!u8a(bytes)) throw new Error('Uint8Array expected');
|
abytes(bytes);
|
||||||
return hexToNumber(bytesToHex(Uint8Array.from(bytes).reverse()));
|
return hexToNumber(bytesToHex(Uint8Array.from(bytes).reverse()));
|
||||||
}
|
}
|
||||||
|
|
||||||
export const numberToBytesBE = (n: bigint, len: number) =>
|
export function numberToBytesBE(n: number | bigint, len: number): Uint8Array {
|
||||||
hexToBytes(n.toString(16).padStart(len * 2, '0'));
|
return hexToBytes(n.toString(16).padStart(len * 2, '0'));
|
||||||
export const numberToBytesLE = (n: bigint, len: number) => numberToBytesBE(n, len).reverse();
|
}
|
||||||
// Returns variable number bytes (minimal bigint encoding?)
|
export function numberToBytesLE(n: number | bigint, len: number): Uint8Array {
|
||||||
export const numberToVarBytesBE = (n: bigint) => hexToBytes(numberToHexUnpadded(n));
|
return numberToBytesBE(n, len).reverse();
|
||||||
|
}
|
||||||
|
// Unpadded, rarely used
|
||||||
|
export function numberToVarBytesBE(n: number | bigint): Uint8Array {
|
||||||
|
return hexToBytes(numberToHexUnpadded(n));
|
||||||
|
}
|
||||||
|
|
||||||
export function ensureBytes(hex: Hex, expectedLength?: number): Uint8Array {
|
/**
|
||||||
|
* Takes hex string or Uint8Array, converts to Uint8Array.
|
||||||
|
* Validates output length.
|
||||||
|
* Will throw error for other types.
|
||||||
|
* @param title descriptive title for an error e.g. 'private key'
|
||||||
|
* @param hex hex string or Uint8Array
|
||||||
|
* @param expectedLength optional, will compare to result array's length
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function ensureBytes(title: string, hex: Hex, expectedLength?: number): Uint8Array {
|
||||||
|
let res: Uint8Array;
|
||||||
|
if (typeof hex === 'string') {
|
||||||
|
try {
|
||||||
|
res = hexToBytes(hex);
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(`${title} must be valid hex string, got "${hex}". Cause: ${e}`);
|
||||||
|
}
|
||||||
|
} else if (isBytes(hex)) {
|
||||||
// Uint8Array.from() instead of hash.slice() because node.js Buffer
|
// Uint8Array.from() instead of hash.slice() because node.js Buffer
|
||||||
// is instance of Uint8Array, and its slice() creates **mutable** copy
|
// is instance of Uint8Array, and its slice() creates **mutable** copy
|
||||||
const bytes = u8a(hex) ? Uint8Array.from(hex) : hexToBytes(hex);
|
res = Uint8Array.from(hex);
|
||||||
if (typeof expectedLength === 'number' && bytes.length !== expectedLength)
|
} else {
|
||||||
throw new Error(`Expected ${expectedLength} bytes`);
|
throw new Error(`${title} must be hex string or Uint8Array`);
|
||||||
return bytes;
|
}
|
||||||
|
const len = res.length;
|
||||||
|
if (typeof expectedLength === 'number' && len !== expectedLength)
|
||||||
|
throw new Error(`${title} expected ${expectedLength} bytes, got ${len}`);
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copies several Uint8Arrays into one.
|
/**
|
||||||
export function concatBytes(...arrs: Uint8Array[]): Uint8Array {
|
* Copies several Uint8Arrays into one.
|
||||||
const r = new Uint8Array(arrs.reduce((sum, a) => sum + a.length, 0));
|
*/
|
||||||
let pad = 0; // walk through each item, ensure they have proper type
|
export function concatBytes(...arrays: Uint8Array[]): Uint8Array {
|
||||||
arrs.forEach((a) => {
|
let sum = 0;
|
||||||
if (!u8a(a)) throw new Error('Uint8Array expected');
|
for (let i = 0; i < arrays.length; i++) {
|
||||||
r.set(a, pad);
|
const a = arrays[i];
|
||||||
|
abytes(a);
|
||||||
|
sum += a.length;
|
||||||
|
}
|
||||||
|
const res = new Uint8Array(sum);
|
||||||
|
for (let i = 0, pad = 0; i < arrays.length; i++) {
|
||||||
|
const a = arrays[i];
|
||||||
|
res.set(a, pad);
|
||||||
pad += a.length;
|
pad += a.length;
|
||||||
});
|
}
|
||||||
return r;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function equalBytes(b1: Uint8Array, b2: Uint8Array) {
|
// Compares 2 u8a-s in kinda constant time
|
||||||
// We don't care about timing attacks here
|
export function equalBytes(a: Uint8Array, b: Uint8Array) {
|
||||||
if (b1.length !== b2.length) return false;
|
if (a.length !== b.length) return false;
|
||||||
for (let i = 0; i < b1.length; i++) if (b1[i] !== b2[i]) return false;
|
let diff = 0;
|
||||||
return true;
|
for (let i = 0; i < a.length; i++) diff |= a[i] ^ b[i];
|
||||||
|
return diff === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Global symbols in both browsers and Node.js since v11
|
||||||
|
// See https://github.com/microsoft/TypeScript/issues/31535
|
||||||
|
declare const TextEncoder: any;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @example utf8ToBytes('abc') // new Uint8Array([97, 98, 99])
|
||||||
|
*/
|
||||||
|
export function utf8ToBytes(str: string): Uint8Array {
|
||||||
|
if (typeof str !== 'string') throw new Error(`utf8ToBytes expected string, got ${typeof str}`);
|
||||||
|
return new Uint8Array(new TextEncoder().encode(str)); // https://bugzil.la/1681809
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bit operations
|
// Bit operations
|
||||||
|
|
||||||
// Amount of bits inside bigint (Same as n.toString(2).length)
|
/**
|
||||||
|
* Calculates amount of bits in a 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)
|
|
||||||
// Same as !!+Array.from(n.toString(2)).reverse()[pos]
|
/**
|
||||||
export const bitGet = (n: bigint, pos: number) => (n >> BigInt(pos)) & 1n;
|
* Gets single bit at position.
|
||||||
// Sets single bit at position
|
* NOTE: first bit position is 0 (same as arrays)
|
||||||
export const bitSet = (n: bigint, pos: number, value: boolean) =>
|
* Same as `!!+Array.from(n.toString(2)).reverse()[pos]`
|
||||||
n | ((value ? _1n : _0n) << BigInt(pos));
|
*/
|
||||||
// Return mask for N bits (Same as BigInt(`0b${Array(i).fill('1').join('')}`))
|
export function bitGet(n: bigint, pos: number) {
|
||||||
// Not using ** operator with bigints for old engines.
|
return (n >> BigInt(pos)) & _1n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets single bit at position.
|
||||||
|
*/
|
||||||
|
export function bitSet(n: bigint, pos: number, value: boolean) {
|
||||||
|
return n | ((value ? _1n : _0n) << BigInt(pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate mask for N bits. Not using ** operator with bigints because of old engines.
|
||||||
|
* Same as BigInt(`0b${Array(i).fill('1').join('')}`)
|
||||||
|
*/
|
||||||
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 = {
|
const validatorFns = {
|
||||||
bigint: (val: any) => typeof val === 'bigint',
|
bigint: (val: any) => typeof val === 'bigint',
|
||||||
function: (val: any) => typeof val === 'function',
|
function: (val: any) => typeof val === 'function',
|
||||||
boolean: (val: any) => typeof val === 'boolean',
|
boolean: (val: any) => typeof val === 'boolean',
|
||||||
string: (val: any) => typeof val === 'string',
|
string: (val: any) => typeof val === 'string',
|
||||||
|
stringOrUint8Array: (val: any) => typeof val === 'string' || isBytes(val),
|
||||||
isSafeInteger: (val: any) => Number.isSafeInteger(val),
|
isSafeInteger: (val: any) => Number.isSafeInteger(val),
|
||||||
array: (val: any) => Array.isArray(val),
|
array: (val: any) => Array.isArray(val),
|
||||||
field: (val: any, object: any) => (object as any).Fp.isValid(val),
|
field: (val: any, object: any) => (object as any).Fp.isValid(val),
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export type BasicWCurve<T> = BasicCurve<T> & {
|
|||||||
clearCofactor?: (c: ProjConstructor<T>, point: ProjPointType<T>) => ProjPointType<T>;
|
clearCofactor?: (c: ProjConstructor<T>, point: ProjPointType<T>) => ProjPointType<T>;
|
||||||
};
|
};
|
||||||
|
|
||||||
type Entropy = Hex | true;
|
type Entropy = Hex | boolean;
|
||||||
export type SignOpts = { lowS?: boolean; extraEntropy?: Entropy; prehash?: boolean };
|
export type SignOpts = { lowS?: boolean; extraEntropy?: Entropy; prehash?: boolean };
|
||||||
export type VerOpts = { lowS?: boolean; prehash?: boolean };
|
export type VerOpts = { lowS?: boolean; prehash?: boolean };
|
||||||
|
|
||||||
@@ -58,10 +58,9 @@ export interface ProjPointType<T> extends Group<ProjPointType<T>> {
|
|||||||
readonly px: T;
|
readonly px: T;
|
||||||
readonly py: T;
|
readonly py: T;
|
||||||
readonly pz: T;
|
readonly pz: T;
|
||||||
|
get x(): T;
|
||||||
|
get y(): T;
|
||||||
multiply(scalar: bigint): ProjPointType<T>;
|
multiply(scalar: bigint): ProjPointType<T>;
|
||||||
multiplyUnsafe(scalar: bigint): ProjPointType<T>;
|
|
||||||
multiplyAndAddUnsafe(Q: ProjPointType<T>, a: bigint, b: bigint): ProjPointType<T> | undefined;
|
|
||||||
_setWindowSize(windowSize: number): void;
|
|
||||||
toAffine(iz?: T): AffinePoint<T>;
|
toAffine(iz?: T): AffinePoint<T>;
|
||||||
isTorsionFree(): boolean;
|
isTorsionFree(): boolean;
|
||||||
clearCofactor(): ProjPointType<T>;
|
clearCofactor(): ProjPointType<T>;
|
||||||
@@ -69,6 +68,10 @@ export interface ProjPointType<T> extends Group<ProjPointType<T>> {
|
|||||||
hasEvenY(): boolean;
|
hasEvenY(): boolean;
|
||||||
toRawBytes(isCompressed?: boolean): Uint8Array;
|
toRawBytes(isCompressed?: boolean): Uint8Array;
|
||||||
toHex(isCompressed?: boolean): string;
|
toHex(isCompressed?: boolean): string;
|
||||||
|
|
||||||
|
multiplyUnsafe(scalar: bigint): ProjPointType<T>;
|
||||||
|
multiplyAndAddUnsafe(Q: ProjPointType<T>, a: bigint, b: bigint): ProjPointType<T> | undefined;
|
||||||
|
_setWindowSize(windowSize: number): void;
|
||||||
}
|
}
|
||||||
// Static methods for 3d XYZ points
|
// Static methods for 3d XYZ points
|
||||||
export interface ProjConstructor<T> extends GroupConstructor<ProjPointType<T>> {
|
export interface ProjConstructor<T> extends GroupConstructor<ProjPointType<T>> {
|
||||||
@@ -81,8 +84,8 @@ export interface ProjConstructor<T> extends GroupConstructor<ProjPointType<T>> {
|
|||||||
|
|
||||||
export type CurvePointsType<T> = BasicWCurve<T> & {
|
export type CurvePointsType<T> = BasicWCurve<T> & {
|
||||||
// Bytes
|
// Bytes
|
||||||
fromBytes: (bytes: Uint8Array) => AffinePoint<T>;
|
fromBytes?: (bytes: Uint8Array) => AffinePoint<T>;
|
||||||
toBytes: (c: ProjConstructor<T>, point: ProjPointType<T>, compressed: boolean) => Uint8Array;
|
toBytes?: (c: ProjConstructor<T>, point: ProjPointType<T>, isCompressed: boolean) => Uint8Array;
|
||||||
};
|
};
|
||||||
|
|
||||||
function validatePointOpts<T>(curve: CurvePointsType<T>) {
|
function validatePointOpts<T>(curve: CurvePointsType<T>) {
|
||||||
@@ -92,8 +95,6 @@ function validatePointOpts<T>(curve: CurvePointsType<T>) {
|
|||||||
{
|
{
|
||||||
a: 'field',
|
a: 'field',
|
||||||
b: 'field',
|
b: 'field',
|
||||||
fromBytes: 'function',
|
|
||||||
toBytes: 'function',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
allowedPrivateKeyLengths: 'array',
|
allowedPrivateKeyLengths: 'array',
|
||||||
@@ -101,6 +102,8 @@ function validatePointOpts<T>(curve: CurvePointsType<T>) {
|
|||||||
isTorsionFree: 'function',
|
isTorsionFree: 'function',
|
||||||
clearCofactor: 'function',
|
clearCofactor: 'function',
|
||||||
allowInfinityPoint: 'boolean',
|
allowInfinityPoint: 'boolean',
|
||||||
|
fromBytes: 'function',
|
||||||
|
toBytes: 'function',
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
const { endo, Fp, a } = opts;
|
const { endo, Fp, a } = opts;
|
||||||
@@ -120,15 +123,16 @@ function validatePointOpts<T>(curve: CurvePointsType<T>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type CurvePointsRes<T> = {
|
export type CurvePointsRes<T> = {
|
||||||
|
CURVE: ReturnType<typeof validatePointOpts<T>>;
|
||||||
ProjectivePoint: ProjConstructor<T>;
|
ProjectivePoint: ProjConstructor<T>;
|
||||||
normalizePrivateKey: (key: PrivKey) => bigint;
|
normPrivateKeyToScalar: (key: PrivKey) => bigint;
|
||||||
weierstrassEquation: (x: T) => T;
|
weierstrassEquation: (x: T) => T;
|
||||||
isWithinCurveOrder: (num: bigint) => boolean;
|
isWithinCurveOrder: (num: bigint) => boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
// ASN.1 DER encoding utilities
|
// ASN.1 DER encoding utilities
|
||||||
const { bytesToNumberBE: b2n, hexToBytes: h2b } = ut;
|
const { bytesToNumberBE: b2n, hexToBytes: h2b } = ut;
|
||||||
const DER = {
|
export const DER = {
|
||||||
// asn.1 DER encoding utils
|
// asn.1 DER encoding utils
|
||||||
Err: class DERErr extends Error {
|
Err: class DERErr extends Error {
|
||||||
constructor(m = '') {
|
constructor(m = '') {
|
||||||
@@ -141,16 +145,20 @@ const DER = {
|
|||||||
const len = data[1];
|
const len = data[1];
|
||||||
const res = data.subarray(2, len + 2);
|
const res = data.subarray(2, len + 2);
|
||||||
if (!len || res.length !== len) throw new E('Invalid signature integer: wrong length');
|
if (!len || res.length !== len) throw new E('Invalid signature integer: wrong length');
|
||||||
if (res[0] === 0x00 && res[1] <= 0x7f)
|
// https://crypto.stackexchange.com/a/57734 Leftmost bit of first byte is 'negative' flag,
|
||||||
throw new E('Invalid signature integer: trailing length');
|
// since we always use positive integers here. It must always be empty:
|
||||||
// ^ Weird condition: not about length, but about first bytes of number.
|
// - add zero byte if exists
|
||||||
|
// - if next byte doesn't have a flag, leading zero is not allowed (minimal encoding)
|
||||||
|
if (res[0] & 0b10000000) throw new E('Invalid signature integer: negative');
|
||||||
|
if (res[0] === 0x00 && !(res[1] & 0b10000000))
|
||||||
|
throw new E('Invalid signature integer: unnecessary leading zero');
|
||||||
return { d: b2n(res), l: data.subarray(len + 2) }; // d is data, l is left
|
return { d: b2n(res), l: data.subarray(len + 2) }; // d is data, l is left
|
||||||
},
|
},
|
||||||
toSig(hex: string | Uint8Array): { r: bigint; s: bigint } {
|
toSig(hex: string | Uint8Array): { r: bigint; s: bigint } {
|
||||||
// parse DER signature
|
// parse DER signature
|
||||||
const { Err: E } = DER;
|
const { Err: E } = DER;
|
||||||
const data = typeof hex === 'string' ? h2b(hex) : hex;
|
const data = typeof hex === 'string' ? h2b(hex) : hex;
|
||||||
if (!(data instanceof Uint8Array)) throw new Error('ui8a expected');
|
ut.abytes(data);
|
||||||
let l = data.length;
|
let l = data.length;
|
||||||
if (l < 2 || data[0] != 0x30) throw new E('Invalid signature tag');
|
if (l < 2 || data[0] != 0x30) throw new E('Invalid signature tag');
|
||||||
if (data[1] !== l - 2) throw new E('Invalid signature: incorrect length');
|
if (data[1] !== l - 2) throw new E('Invalid signature: incorrect length');
|
||||||
@@ -160,7 +168,8 @@ const DER = {
|
|||||||
return { r, s };
|
return { r, s };
|
||||||
},
|
},
|
||||||
hexFromSig(sig: { r: bigint; s: bigint }): string {
|
hexFromSig(sig: { r: bigint; s: bigint }): string {
|
||||||
const slice = (s: string): string => (Number.parseInt(s[0], 16) >= 8 ? '00' + s : s); // slice DER
|
// Add leading zero if first byte has negative bit enabled. More details in '_parseInt'
|
||||||
|
const slice = (s: string): string => (Number.parseInt(s[0], 16) & 0b1000 ? '00' + s : s);
|
||||||
const h = (num: number | bigint) => {
|
const h = (num: number | bigint) => {
|
||||||
const hex = num.toString(16);
|
const hex = num.toString(16);
|
||||||
return hex.length & 1 ? `0${hex}` : hex;
|
return hex.length & 1 ? `0${hex}` : hex;
|
||||||
@@ -175,14 +184,31 @@ const DER = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Be friendly to bad ECMAScript parsers by not using bigint literals like 123n
|
// Be friendly to bad ECMAScript parsers by not using bigint literals
|
||||||
const _0n = BigInt(0);
|
// prettier-ignore
|
||||||
const _1n = BigInt(1);
|
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n = BigInt(4);
|
||||||
|
|
||||||
export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T> {
|
||||||
const CURVE = validatePointOpts(opts);
|
const CURVE = validatePointOpts(opts);
|
||||||
const { Fp } = CURVE; // All curves has same field / group length as for now, but they can differ
|
const { Fp } = CURVE; // All curves has same field / group length as for now, but they can differ
|
||||||
|
|
||||||
|
const toBytes =
|
||||||
|
CURVE.toBytes ||
|
||||||
|
((_c: ProjConstructor<T>, point: ProjPointType<T>, _isCompressed: boolean) => {
|
||||||
|
const a = point.toAffine();
|
||||||
|
return ut.concatBytes(Uint8Array.from([0x04]), Fp.toBytes(a.x), Fp.toBytes(a.y));
|
||||||
|
});
|
||||||
|
const fromBytes =
|
||||||
|
CURVE.fromBytes ||
|
||||||
|
((bytes: Uint8Array) => {
|
||||||
|
// const head = bytes[0];
|
||||||
|
const tail = bytes.subarray(1);
|
||||||
|
// if (head !== 0x04) throw new Error('Only non-compressed encoding is supported');
|
||||||
|
const x = Fp.fromBytes(tail.subarray(0, Fp.BYTES));
|
||||||
|
const y = Fp.fromBytes(tail.subarray(Fp.BYTES, 2 * Fp.BYTES));
|
||||||
|
return { x, y };
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* y² = x³ + ax + b: Short weierstrass curve formula
|
* y² = x³ + ax + b: Short weierstrass curve formula
|
||||||
* @returns y²
|
* @returns y²
|
||||||
@@ -193,6 +219,12 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|||||||
const x3 = Fp.mul(x2, x); // x2 * x
|
const x3 = Fp.mul(x2, x); // x2 * x
|
||||||
return Fp.add(Fp.add(x3, Fp.mul(x, a)), b); // x3 + a * x + b
|
return Fp.add(Fp.add(x3, Fp.mul(x, a)), b); // x3 + a * x + b
|
||||||
}
|
}
|
||||||
|
// Validate whether the passed curve params are valid.
|
||||||
|
// We check if curve equation works for generator point.
|
||||||
|
// `assertValidity()` won't work: `isTorsionFree()` is not available at this point in bls12-381.
|
||||||
|
// ProjectivePoint class has not been initialized yet.
|
||||||
|
if (!Fp.eql(Fp.sqr(CURVE.Gy), weierstrassEquation(CURVE.Gx)))
|
||||||
|
throw new Error('bad generator point: equation left != right');
|
||||||
|
|
||||||
// Valid group elements reside in range 1..n-1
|
// Valid group elements reside in range 1..n-1
|
||||||
function isWithinCurveOrder(num: bigint): boolean {
|
function isWithinCurveOrder(num: bigint): boolean {
|
||||||
@@ -202,18 +234,21 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|||||||
if (!isWithinCurveOrder(num)) throw new Error('Expected valid bigint: 0 < bigint < curve.n');
|
if (!isWithinCurveOrder(num)) throw new Error('Expected valid bigint: 0 < bigint < curve.n');
|
||||||
}
|
}
|
||||||
// Validates if priv key is valid and converts it to bigint.
|
// Validates if priv key is valid and converts it to bigint.
|
||||||
// Supports options CURVE.normalizePrivateKey and CURVE.wrapPrivateKey.
|
// Supports options allowedPrivateKeyLengths and wrapPrivateKey.
|
||||||
function normalizePrivateKey(key: PrivKey): bigint {
|
function normPrivateKeyToScalar(key: PrivKey): bigint {
|
||||||
const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n } = CURVE;
|
const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n } = CURVE;
|
||||||
if (lengths && typeof key !== 'bigint') {
|
if (lengths && typeof key !== 'bigint') {
|
||||||
if (key instanceof Uint8Array) key = ut.bytesToHex(key);
|
if (ut.isBytes(key)) key = ut.bytesToHex(key);
|
||||||
// Normalize to hex string, pad. E.g. P521 would norm 130-132 char hex to 132-char bytes
|
// Normalize to hex string, pad. E.g. P521 would norm 130-132 char hex to 132-char bytes
|
||||||
if (typeof key !== 'string' || !lengths.includes(key.length)) throw new Error('Invalid key');
|
if (typeof key !== 'string' || !lengths.includes(key.length)) throw new Error('Invalid key');
|
||||||
key = key.padStart(nByteLength * 2, '0');
|
key = key.padStart(nByteLength * 2, '0');
|
||||||
}
|
}
|
||||||
let num: bigint;
|
let num: bigint;
|
||||||
try {
|
try {
|
||||||
num = typeof key === 'bigint' ? key : ut.bytesToNumberBE(ensureBytes(key, nByteLength));
|
num =
|
||||||
|
typeof key === 'bigint'
|
||||||
|
? key
|
||||||
|
: ut.bytesToNumberBE(ensureBytes('private key', key, nByteLength));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(`private key must be ${nByteLength} bytes, hex or bigint, not ${typeof key}`);
|
throw new Error(`private key must be ${nByteLength} bytes, hex or bigint, not ${typeof key}`);
|
||||||
}
|
}
|
||||||
@@ -235,7 +270,11 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|||||||
static readonly BASE = new Point(CURVE.Gx, CURVE.Gy, Fp.ONE);
|
static readonly BASE = new Point(CURVE.Gx, CURVE.Gy, Fp.ONE);
|
||||||
static readonly ZERO = new Point(Fp.ZERO, Fp.ONE, Fp.ZERO);
|
static readonly ZERO = new Point(Fp.ZERO, Fp.ONE, Fp.ZERO);
|
||||||
|
|
||||||
constructor(readonly px: T, readonly py: T, readonly pz: T) {
|
constructor(
|
||||||
|
readonly px: T,
|
||||||
|
readonly py: T,
|
||||||
|
readonly pz: T
|
||||||
|
) {
|
||||||
if (px == null || !Fp.isValid(px)) throw new Error('x required');
|
if (px == null || !Fp.isValid(px)) throw new Error('x required');
|
||||||
if (py == null || !Fp.isValid(py)) throw new Error('y required');
|
if (py == null || !Fp.isValid(py)) throw new Error('y required');
|
||||||
if (pz == null || !Fp.isValid(pz)) throw new Error('z required');
|
if (pz == null || !Fp.isValid(pz)) throw new Error('z required');
|
||||||
@@ -276,14 +315,14 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|||||||
* @param hex short/long ECDSA hex
|
* @param hex short/long ECDSA hex
|
||||||
*/
|
*/
|
||||||
static fromHex(hex: Hex): Point {
|
static fromHex(hex: Hex): Point {
|
||||||
const P = Point.fromAffine(CURVE.fromBytes(ensureBytes(hex)));
|
const P = Point.fromAffine(fromBytes(ensureBytes('pointHex', hex)));
|
||||||
P.assertValidity();
|
P.assertValidity();
|
||||||
return P;
|
return P;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Multiplies generator point by privateKey.
|
// Multiplies generator point by privateKey.
|
||||||
static fromPrivateKey(privateKey: PrivKey) {
|
static fromPrivateKey(privateKey: PrivKey) {
|
||||||
return Point.BASE.multiply(normalizePrivateKey(privateKey));
|
return Point.BASE.multiply(normPrivateKeyToScalar(privateKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
// We calculate precomputes for elliptic curve point multiplication
|
// We calculate precomputes for elliptic curve point multiplication
|
||||||
@@ -299,9 +338,11 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|||||||
|
|
||||||
// A point on curve is valid if it conforms to equation.
|
// A point on curve is valid if it conforms to equation.
|
||||||
assertValidity(): void {
|
assertValidity(): void {
|
||||||
// Zero is valid point too!
|
|
||||||
if (this.is0()) {
|
if (this.is0()) {
|
||||||
if (CURVE.allowInfinityPoint) return;
|
// (0, 1, 0) aka ZERO is invalid in most contexts.
|
||||||
|
// In BLS, ZERO can be serialized, so we allow it.
|
||||||
|
// (0, 0, 0) is wrong representation of ZERO and is always invalid.
|
||||||
|
if (CURVE.allowInfinityPoint && !Fp.is0(this.py)) return;
|
||||||
throw new Error('bad point: ZERO');
|
throw new Error('bad point: ZERO');
|
||||||
}
|
}
|
||||||
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
|
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
|
||||||
@@ -344,7 +385,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|||||||
// Cost: 8M + 3S + 3*a + 2*b3 + 15add.
|
// Cost: 8M + 3S + 3*a + 2*b3 + 15add.
|
||||||
double() {
|
double() {
|
||||||
const { a, b } = CURVE;
|
const { a, b } = CURVE;
|
||||||
const b3 = Fp.mul(b, 3n);
|
const b3 = Fp.mul(b, _3n);
|
||||||
const { px: X1, py: Y1, pz: Z1 } = this;
|
const { px: X1, py: Y1, pz: Z1 } = this;
|
||||||
let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO; // prettier-ignore
|
let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO; // prettier-ignore
|
||||||
let t0 = Fp.mul(X1, X1); // step 1
|
let t0 = Fp.mul(X1, X1); // step 1
|
||||||
@@ -391,7 +432,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|||||||
const { px: X2, py: Y2, pz: Z2 } = other;
|
const { px: X2, py: Y2, pz: Z2 } = other;
|
||||||
let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO; // prettier-ignore
|
let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO; // prettier-ignore
|
||||||
const a = CURVE.a;
|
const a = CURVE.a;
|
||||||
const b3 = Fp.mul(CURVE.b, 3n);
|
const b3 = Fp.mul(CURVE.b, _3n);
|
||||||
let t0 = Fp.mul(X1, X2); // step 1
|
let t0 = Fp.mul(X1, X2); // step 1
|
||||||
let t1 = Fp.mul(Y1, Y2);
|
let t1 = Fp.mul(Y1, Y2);
|
||||||
let t2 = Fp.mul(Z1, Z2);
|
let t2 = Fp.mul(Z1, Z2);
|
||||||
@@ -484,8 +525,9 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|||||||
* Constant time multiplication.
|
* Constant time multiplication.
|
||||||
* Uses wNAF method. Windowed method may be 10% faster,
|
* Uses wNAF method. Windowed method may be 10% faster,
|
||||||
* but takes 2x longer to generate and consumes 2x memory.
|
* but takes 2x longer to generate and consumes 2x memory.
|
||||||
|
* Uses precomputes when available.
|
||||||
|
* Uses endomorphism for Koblitz curves.
|
||||||
* @param scalar by which the point would be multiplied
|
* @param scalar by which the point would be multiplied
|
||||||
* @param affinePoint optional point ot save cached precompute windows on it
|
|
||||||
* @returns New point
|
* @returns New point
|
||||||
*/
|
*/
|
||||||
multiply(scalar: bigint): Point {
|
multiply(scalar: bigint): Point {
|
||||||
@@ -513,6 +555,8 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Efficiently calculate `aP + bQ`. Unsafe, can expose private key, if used incorrectly.
|
* Efficiently calculate `aP + bQ`. Unsafe, can expose private key, if used incorrectly.
|
||||||
|
* Not using Strauss-Shamir trick: precomputation tables are faster.
|
||||||
|
* The trick could be useful if both P and Q are not G (not in our case).
|
||||||
* @returns non-zero affine point
|
* @returns non-zero affine point
|
||||||
*/
|
*/
|
||||||
multiplyAndAddUnsafe(Q: Point, a: bigint, b: bigint): Point | undefined {
|
multiplyAndAddUnsafe(Q: Point, a: bigint, b: bigint): Point | undefined {
|
||||||
@@ -556,7 +600,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|||||||
|
|
||||||
toRawBytes(isCompressed = true): Uint8Array {
|
toRawBytes(isCompressed = true): Uint8Array {
|
||||||
this.assertValidity();
|
this.assertValidity();
|
||||||
return CURVE.toBytes(Point, this, isCompressed);
|
return toBytes(Point, this, isCompressed);
|
||||||
}
|
}
|
||||||
|
|
||||||
toHex(isCompressed = true): string {
|
toHex(isCompressed = true): string {
|
||||||
@@ -565,10 +609,11 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|||||||
}
|
}
|
||||||
const _bits = CURVE.nBitLength;
|
const _bits = CURVE.nBitLength;
|
||||||
const wnaf = wNAF(Point, CURVE.endo ? Math.ceil(_bits / 2) : _bits);
|
const wnaf = wNAF(Point, CURVE.endo ? Math.ceil(_bits / 2) : _bits);
|
||||||
|
// Validate if generator point is on curve
|
||||||
return {
|
return {
|
||||||
|
CURVE,
|
||||||
ProjectivePoint: Point as ProjConstructor<T>,
|
ProjectivePoint: Point as ProjConstructor<T>,
|
||||||
normalizePrivateKey,
|
normPrivateKeyToScalar,
|
||||||
weierstrassEquation,
|
weierstrassEquation,
|
||||||
isWithinCurveOrder,
|
isWithinCurveOrder,
|
||||||
};
|
};
|
||||||
@@ -580,7 +625,7 @@ export interface SignatureType {
|
|||||||
readonly s: bigint;
|
readonly s: bigint;
|
||||||
readonly recovery?: number;
|
readonly recovery?: number;
|
||||||
assertValidity(): void;
|
assertValidity(): void;
|
||||||
addRecoveryBit(recovery: number): SignatureType;
|
addRecoveryBit(recovery: number): RecoveredSignatureType;
|
||||||
hasHighS(): boolean;
|
hasHighS(): boolean;
|
||||||
normalizeS(): SignatureType;
|
normalizeS(): SignatureType;
|
||||||
recoverPublicKey(msgHash: Hex): ProjPointType<bigint>;
|
recoverPublicKey(msgHash: Hex): ProjPointType<bigint>;
|
||||||
@@ -590,6 +635,9 @@ export interface SignatureType {
|
|||||||
toDERRawBytes(isCompressed?: boolean): Uint8Array;
|
toDERRawBytes(isCompressed?: boolean): Uint8Array;
|
||||||
toDERHex(isCompressed?: boolean): string;
|
toDERHex(isCompressed?: boolean): string;
|
||||||
}
|
}
|
||||||
|
export type RecoveredSignatureType = SignatureType & {
|
||||||
|
readonly recovery: number;
|
||||||
|
};
|
||||||
// Static methods
|
// Static methods
|
||||||
export type SignatureConstructor = {
|
export type SignatureConstructor = {
|
||||||
new (r: bigint, s: bigint): SignatureType;
|
new (r: bigint, s: bigint): SignatureType;
|
||||||
@@ -631,75 +679,21 @@ export type CurveFn = {
|
|||||||
CURVE: ReturnType<typeof validateOpts>;
|
CURVE: ReturnType<typeof validateOpts>;
|
||||||
getPublicKey: (privateKey: PrivKey, isCompressed?: boolean) => Uint8Array;
|
getPublicKey: (privateKey: PrivKey, isCompressed?: boolean) => Uint8Array;
|
||||||
getSharedSecret: (privateA: PrivKey, publicB: Hex, isCompressed?: boolean) => Uint8Array;
|
getSharedSecret: (privateA: PrivKey, publicB: Hex, isCompressed?: boolean) => Uint8Array;
|
||||||
sign: (msgHash: Hex, privKey: PrivKey, opts?: SignOpts) => SignatureType;
|
sign: (msgHash: Hex, privKey: PrivKey, opts?: SignOpts) => RecoveredSignatureType;
|
||||||
verify: (signature: Hex | SignatureLike, msgHash: Hex, publicKey: Hex, opts?: VerOpts) => boolean;
|
verify: (signature: Hex | SignatureLike, msgHash: Hex, publicKey: Hex, opts?: VerOpts) => boolean;
|
||||||
ProjectivePoint: ProjConstructor<bigint>;
|
ProjectivePoint: ProjConstructor<bigint>;
|
||||||
Signature: SignatureConstructor;
|
Signature: SignatureConstructor;
|
||||||
utils: {
|
utils: {
|
||||||
_normalizePrivateKey: (key: PrivKey) => bigint;
|
normPrivateKeyToScalar: (key: PrivKey) => bigint;
|
||||||
isValidPrivateKey(privateKey: PrivKey): boolean;
|
isValidPrivateKey(privateKey: PrivKey): boolean;
|
||||||
hashToPrivateKey: (hash: Hex) => Uint8Array;
|
|
||||||
randomPrivateKey: () => Uint8Array;
|
randomPrivateKey: () => Uint8Array;
|
||||||
|
precompute: (windowSize?: number, point?: ProjPointType<bigint>) => ProjPointType<bigint>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const u8n = (data?: any) => new Uint8Array(data); // creates Uint8Array
|
|
||||||
const u8fr = (arr: any) => Uint8Array.from(arr); // another shortcut
|
|
||||||
// Minimal HMAC-DRBG from NIST 800-90 for RFC6979 sigs.
|
|
||||||
type Pred<T> = (v: Uint8Array) => T | undefined;
|
|
||||||
function hmacDrbg<T>(
|
|
||||||
hashLen: number,
|
|
||||||
qByteLen: number,
|
|
||||||
hmacFn: HmacFnSync
|
|
||||||
): (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 ut.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;
|
|
||||||
}
|
|
||||||
export function weierstrass(curveDef: CurveType): CurveFn {
|
export function weierstrass(curveDef: CurveType): CurveFn {
|
||||||
const CURVE = validateOpts(curveDef) as ReturnType<typeof validateOpts>;
|
const CURVE = validateOpts(curveDef) as ReturnType<typeof validateOpts>;
|
||||||
const CURVE_ORDER = CURVE.n;
|
const { Fp, n: CURVE_ORDER } = CURVE;
|
||||||
const Fp = CURVE.Fp;
|
|
||||||
const compressedLen = Fp.BYTES + 1; // e.g. 33 for 32
|
const compressedLen = Fp.BYTES + 1; // e.g. 33 for 32
|
||||||
const uncompressedLen = 2 * Fp.BYTES + 1; // e.g. 65 for 32
|
const uncompressedLen = 2 * Fp.BYTES + 1; // e.g. 65 for 32
|
||||||
|
|
||||||
@@ -715,17 +709,16 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
ProjectivePoint: Point,
|
ProjectivePoint: Point,
|
||||||
normalizePrivateKey,
|
normPrivateKeyToScalar,
|
||||||
weierstrassEquation,
|
weierstrassEquation,
|
||||||
isWithinCurveOrder,
|
isWithinCurveOrder,
|
||||||
} = weierstrassPoints({
|
} = weierstrassPoints({
|
||||||
...CURVE,
|
...CURVE,
|
||||||
toBytes(c, point, isCompressed: boolean): Uint8Array {
|
toBytes(_c, point, isCompressed: boolean): Uint8Array {
|
||||||
const a = point.toAffine();
|
const a = point.toAffine();
|
||||||
const x = Fp.toBytes(a.x);
|
const x = Fp.toBytes(a.x);
|
||||||
const cat = ut.concatBytes;
|
const cat = ut.concatBytes;
|
||||||
if (isCompressed) {
|
if (isCompressed) {
|
||||||
// TODO: hasEvenY
|
|
||||||
return cat(Uint8Array.from([point.hasEvenY() ? 0x02 : 0x03]), x);
|
return cat(Uint8Array.from([point.hasEvenY() ? 0x02 : 0x03]), x);
|
||||||
} else {
|
} else {
|
||||||
return cat(Uint8Array.from([0x04]), x, Fp.toBytes(a.y));
|
return cat(Uint8Array.from([0x04]), x, Fp.toBytes(a.y));
|
||||||
@@ -740,7 +733,13 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
const x = ut.bytesToNumberBE(tail);
|
const x = ut.bytesToNumberBE(tail);
|
||||||
if (!isValidFieldElement(x)) throw new Error('Point is not on curve');
|
if (!isValidFieldElement(x)) throw new Error('Point is not on curve');
|
||||||
const y2 = weierstrassEquation(x); // y² = x³ + ax + b
|
const y2 = weierstrassEquation(x); // y² = x³ + ax + b
|
||||||
let y = Fp.sqrt(y2); // y = y² ^ (p+1)/4
|
let y: bigint;
|
||||||
|
try {
|
||||||
|
y = Fp.sqrt(y2); // y = y² ^ (p+1)/4
|
||||||
|
} catch (sqrtError) {
|
||||||
|
const suffix = sqrtError instanceof Error ? ': ' + sqrtError.message : '';
|
||||||
|
throw new Error('Point is not on curve' + suffix);
|
||||||
|
}
|
||||||
const isYOdd = (y & _1n) === _1n;
|
const isYOdd = (y & _1n) === _1n;
|
||||||
// ECDSA
|
// ECDSA
|
||||||
const isHeadOdd = (head & 1) === 1;
|
const isHeadOdd = (head & 1) === 1;
|
||||||
@@ -775,42 +774,43 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
* ECDSA signature with its (r, s) properties. Supports DER & compact representations.
|
* ECDSA signature with its (r, s) properties. Supports DER & compact representations.
|
||||||
*/
|
*/
|
||||||
class Signature implements SignatureType {
|
class Signature implements SignatureType {
|
||||||
constructor(readonly r: bigint, readonly s: bigint, readonly recovery?: number) {
|
constructor(
|
||||||
|
readonly r: bigint,
|
||||||
|
readonly s: bigint,
|
||||||
|
readonly recovery?: number
|
||||||
|
) {
|
||||||
this.assertValidity();
|
this.assertValidity();
|
||||||
}
|
}
|
||||||
|
|
||||||
// pair (bytes of r, bytes of s)
|
// pair (bytes of r, bytes of s)
|
||||||
static fromCompact(hex: Hex) {
|
static fromCompact(hex: Hex) {
|
||||||
const gl = CURVE.nByteLength;
|
const l = CURVE.nByteLength;
|
||||||
hex = ensureBytes(hex, gl * 2);
|
hex = ensureBytes('compactSignature', hex, l * 2);
|
||||||
return new Signature(slcNum(hex, 0, gl), slcNum(hex, gl, 2 * gl));
|
return new Signature(slcNum(hex, 0, l), slcNum(hex, l, 2 * l));
|
||||||
}
|
}
|
||||||
|
|
||||||
// DER encoded ECDSA signature
|
// DER encoded ECDSA signature
|
||||||
// https://bitcoin.stackexchange.com/questions/57644/what-are-the-parts-of-a-bitcoin-transaction-input-script
|
// https://bitcoin.stackexchange.com/questions/57644/what-are-the-parts-of-a-bitcoin-transaction-input-script
|
||||||
static fromDER(hex: Hex) {
|
static fromDER(hex: Hex) {
|
||||||
if (typeof hex !== 'string' && !(hex instanceof Uint8Array))
|
const { r, s } = DER.toSig(ensureBytes('DER', hex));
|
||||||
throw new Error(`Signature.fromDER: Expected string or Uint8Array`);
|
|
||||||
const { r, s } = DER.toSig(ensureBytes(hex));
|
|
||||||
return new Signature(r, s);
|
return new Signature(r, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
assertValidity(): void {
|
assertValidity(): void {
|
||||||
// can use assertGE here
|
// can use assertGE here
|
||||||
if (!isWithinCurveOrder(this.r)) throw new Error('r must be 0 < r < n');
|
if (!isWithinCurveOrder(this.r)) throw new Error('r must be 0 < r < CURVE.n');
|
||||||
if (!isWithinCurveOrder(this.s)) throw new Error('s must be 0 < s < n');
|
if (!isWithinCurveOrder(this.s)) throw new Error('s must be 0 < s < CURVE.n');
|
||||||
}
|
}
|
||||||
|
|
||||||
addRecoveryBit(recovery: number) {
|
addRecoveryBit(recovery: number): RecoveredSignature {
|
||||||
return new Signature(this.r, this.s, recovery);
|
return new Signature(this.r, this.s, recovery) as RecoveredSignature;
|
||||||
}
|
}
|
||||||
|
|
||||||
recoverPublicKey(msgHash: Hex): typeof Point.BASE {
|
recoverPublicKey(msgHash: Hex): typeof Point.BASE {
|
||||||
const { n: N } = CURVE; // ECDSA public key recovery secg.org/sec1-v2.pdf 4.1.6
|
|
||||||
const { r, s, recovery: rec } = this;
|
const { r, s, recovery: rec } = this;
|
||||||
const h = bits2int_modN(ensureBytes(msgHash)); // Truncate hash
|
const h = bits2int_modN(ensureBytes('msgHash', msgHash)); // Truncate hash
|
||||||
if (rec == null || ![0, 1, 2, 3].includes(rec)) throw new Error('recovery id invalid');
|
if (rec == null || ![0, 1, 2, 3].includes(rec)) throw new Error('recovery id invalid');
|
||||||
const radj = rec === 2 || rec === 3 ? r + N : r;
|
const radj = rec === 2 || rec === 3 ? r + CURVE.n : r;
|
||||||
if (radj >= Fp.ORDER) throw new Error('recovery id 2 or 3 invalid');
|
if (radj >= Fp.ORDER) throw new Error('recovery id 2 or 3 invalid');
|
||||||
const prefix = (rec & 1) === 0 ? '02' : '03';
|
const prefix = (rec & 1) === 0 ? '02' : '03';
|
||||||
const R = Point.fromHex(prefix + numToNByteStr(radj));
|
const R = Point.fromHex(prefix + numToNByteStr(radj));
|
||||||
@@ -848,41 +848,39 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
return numToNByteStr(this.r) + numToNByteStr(this.s);
|
return numToNByteStr(this.r) + numToNByteStr(this.s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
type RecoveredSignature = Signature & { recovery: number };
|
||||||
|
|
||||||
const utils = {
|
const utils = {
|
||||||
isValidPrivateKey(privateKey: PrivKey) {
|
isValidPrivateKey(privateKey: PrivKey) {
|
||||||
try {
|
try {
|
||||||
normalizePrivateKey(privateKey);
|
normPrivateKeyToScalar(privateKey);
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_normalizePrivateKey: normalizePrivateKey,
|
normPrivateKeyToScalar: normPrivateKeyToScalar,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts some bytes to a valid private key. Needs at least (nBitLength+64) bytes.
|
* Produces cryptographically secure private key from random of size
|
||||||
|
* (groupLen + ceil(groupLen / 2)) with modulo bias being negligible.
|
||||||
*/
|
*/
|
||||||
hashToPrivateKey: (hash: Hex): Uint8Array =>
|
randomPrivateKey: (): Uint8Array => {
|
||||||
ut.numberToBytesBE(mod.hashToPrivateScalar(hash, CURVE_ORDER), CURVE.nByteLength),
|
const length = mod.getMinHashLength(CURVE.n);
|
||||||
|
return mod.mapHashToField(CURVE.randomBytes(length), CURVE.n);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Produces cryptographically secure private key from random of size (nBitLength+64)
|
* Creates precompute table for an arbitrary EC point. Makes point "cached".
|
||||||
* as per FIPS 186 B.4.1 with modulo bias being neglible.
|
* Allows to massively speed-up `point.multiply(scalar)`.
|
||||||
*/
|
|
||||||
randomPrivateKey: (): Uint8Array => utils.hashToPrivateKey(CURVE.randomBytes(Fp.BYTES + 8)),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 1. Returns cached point which you can use to pass to `getSharedSecret` or `#multiply` by it.
|
|
||||||
* 2. Precomputes point multiplication table. Is done by default on first `getPublicKey()` call.
|
|
||||||
* If you want your first getPublicKey to take 0.16ms instead of 20ms, make sure to call
|
|
||||||
* utils.precompute() somewhere without arguments first.
|
|
||||||
* @param windowSize 2, 4, 8, 16
|
|
||||||
* @returns cached point
|
* @returns cached point
|
||||||
|
* @example
|
||||||
|
* const fast = utils.precompute(8, ProjectivePoint.fromHex(someonesPubKey));
|
||||||
|
* fast.multiply(privKey); // much faster ECDH now
|
||||||
*/
|
*/
|
||||||
precompute(windowSize = 8, point = Point.BASE): typeof Point.BASE {
|
precompute(windowSize = 8, point = Point.BASE): typeof Point.BASE {
|
||||||
point._setWindowSize(windowSize);
|
point._setWindowSize(windowSize);
|
||||||
point.multiply(BigInt(3));
|
point.multiply(BigInt(3)); // 3 is arbitrary, just need any number here
|
||||||
return point;
|
return point;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -901,7 +899,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
* Quick and dirty check for item being public key. Does not validate hex, or being on-curve.
|
* Quick and dirty check for item being public key. Does not validate hex, or being on-curve.
|
||||||
*/
|
*/
|
||||||
function isProbPub(item: PrivKey | PubKey): boolean {
|
function isProbPub(item: PrivKey | PubKey): boolean {
|
||||||
const arr = item instanceof Uint8Array;
|
const arr = ut.isBytes(item);
|
||||||
const str = typeof item === 'string';
|
const str = typeof item === 'string';
|
||||||
const len = (arr || str) && (item as Hex).length;
|
const len = (arr || str) && (item as Hex).length;
|
||||||
if (arr) return len === compressedLen || len === uncompressedLen;
|
if (arr) return len === compressedLen || len === uncompressedLen;
|
||||||
@@ -913,7 +911,8 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
/**
|
/**
|
||||||
* ECDH (Elliptic Curve Diffie Hellman).
|
* ECDH (Elliptic Curve Diffie Hellman).
|
||||||
* Computes shared public key from private key and public key.
|
* Computes shared public key from private key and public key.
|
||||||
* Checks: 1) private key validity 2) shared key is on-curve
|
* Checks: 1) private key validity 2) shared key is on-curve.
|
||||||
|
* Does NOT hash the result.
|
||||||
* @param privateA private key
|
* @param privateA private key
|
||||||
* @param publicB different public key
|
* @param publicB different public key
|
||||||
* @param isCompressed whether to return compact (default), or full key
|
* @param isCompressed whether to return compact (default), or full key
|
||||||
@@ -923,7 +922,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
if (isProbPub(privateA)) throw new Error('first arg must be private key');
|
if (isProbPub(privateA)) throw new Error('first arg must be private key');
|
||||||
if (!isProbPub(publicB)) throw new Error('second arg must be public key');
|
if (!isProbPub(publicB)) throw new Error('second arg must be public key');
|
||||||
const b = Point.fromHex(publicB); // check for being on-curve
|
const b = Point.fromHex(publicB); // check for being on-curve
|
||||||
return b.multiply(normalizePrivateKey(privateA)).toRawBytes(isCompressed);
|
return b.multiply(normPrivateKeyToScalar(privateA)).toRawBytes(isCompressed);
|
||||||
}
|
}
|
||||||
|
|
||||||
// RFC6979: ensure ECDSA msg is X bytes and < N. RFC suggests optional truncating via bits2octets.
|
// RFC6979: ensure ECDSA msg is X bytes and < N. RFC suggests optional truncating via bits2octets.
|
||||||
@@ -935,8 +934,8 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
function (bytes: Uint8Array): bigint {
|
function (bytes: Uint8Array): bigint {
|
||||||
// For curves with nBitLength % 8 !== 0: bits2octets(bits2octets(m)) !== bits2octets(m)
|
// For curves with nBitLength % 8 !== 0: bits2octets(bits2octets(m)) !== bits2octets(m)
|
||||||
// for some cases, since bytes.length * 8 is not actual bitLength.
|
// for some cases, since bytes.length * 8 is not actual bitLength.
|
||||||
const delta = bytes.length * 8 - CURVE.nBitLength; // truncate to nBitLength leftmost bits
|
|
||||||
const num = ut.bytesToNumberBE(bytes); // check for == u8 done here
|
const num = ut.bytesToNumberBE(bytes); // check for == u8 done here
|
||||||
|
const delta = bytes.length * 8 - CURVE.nBitLength; // truncate to nBitLength leftmost bits
|
||||||
return delta > 0 ? num >> BigInt(delta) : num;
|
return delta > 0 ? num >> BigInt(delta) : num;
|
||||||
};
|
};
|
||||||
const bits2int_modN =
|
const bits2int_modN =
|
||||||
@@ -946,10 +945,12 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
};
|
};
|
||||||
// NOTE: pads output with zero as per spec
|
// NOTE: pads output with zero as per spec
|
||||||
const ORDER_MASK = ut.bitMask(CURVE.nBitLength);
|
const ORDER_MASK = ut.bitMask(CURVE.nBitLength);
|
||||||
|
/**
|
||||||
|
* Converts to bytes. Checks if num in `[0..ORDER_MASK-1]` e.g.: `[0..2^256-1]`.
|
||||||
|
*/
|
||||||
function int2octets(num: bigint): Uint8Array {
|
function int2octets(num: bigint): Uint8Array {
|
||||||
if (typeof num !== 'bigint') throw new Error('bigint expected');
|
if (typeof num !== 'bigint') throw new Error('bigint expected');
|
||||||
if (!(_0n <= num && num < ORDER_MASK))
|
if (!(_0n <= num && num < ORDER_MASK))
|
||||||
// n in [0..ORDER_MASK-1]
|
|
||||||
throw new Error(`bigint expected < 2^${CURVE.nBitLength}`);
|
throw new Error(`bigint expected < 2^${CURVE.nBitLength}`);
|
||||||
// works with order, can have different size than numToField!
|
// works with order, can have different size than numToField!
|
||||||
return ut.numberToBytesBE(num, CURVE.nByteLength);
|
return ut.numberToBytesBE(num, CURVE.nByteLength);
|
||||||
@@ -961,31 +962,30 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
// NOTE: we cannot assume here that msgHash has same amount of bytes as curve order, this will be wrong at least for P521.
|
// NOTE: we cannot assume here that msgHash has same amount of bytes as curve order, this will be wrong at least for P521.
|
||||||
// Also it can be bigger for P224 + SHA256
|
// Also it can be bigger for P224 + SHA256
|
||||||
function prepSig(msgHash: Hex, privateKey: PrivKey, opts = defaultSigOpts) {
|
function prepSig(msgHash: Hex, privateKey: PrivKey, opts = defaultSigOpts) {
|
||||||
const { hash, randomBytes } = CURVE;
|
|
||||||
if (msgHash == null) throw new Error(`sign: expected valid message hash, not "${msgHash}"`);
|
|
||||||
if (['recovered', 'canonical'].some((k) => k in opts))
|
if (['recovered', 'canonical'].some((k) => k in opts))
|
||||||
// Ban legacy options
|
|
||||||
throw new Error('sign() legacy options not supported');
|
throw new Error('sign() legacy options not supported');
|
||||||
|
const { hash, randomBytes } = CURVE;
|
||||||
let { lowS, prehash, extraEntropy: ent } = opts; // generates low-s sigs by default
|
let { lowS, prehash, extraEntropy: ent } = opts; // generates low-s sigs by default
|
||||||
if (prehash) msgHash = hash(ensureBytes(msgHash));
|
|
||||||
if (lowS == null) lowS = true; // RFC6979 3.2: we skip step A, because we already provide hash
|
if (lowS == null) lowS = true; // RFC6979 3.2: we skip step A, because we already provide hash
|
||||||
|
msgHash = ensureBytes('msgHash', msgHash);
|
||||||
|
if (prehash) msgHash = ensureBytes('prehashed msgHash', hash(msgHash));
|
||||||
|
|
||||||
// We can't later call bits2octets, since nested bits2int is broken for curves
|
// We can't later call bits2octets, since nested bits2int is broken for curves
|
||||||
// with nBitLength % 8 !== 0. Because of that, we unwrap it here as int2octets call.
|
// with nBitLength % 8 !== 0. Because of that, we unwrap it here as int2octets call.
|
||||||
// const bits2octets = (bits) => int2octets(bits2int_modN(bits))
|
// const bits2octets = (bits) => int2octets(bits2int_modN(bits))
|
||||||
const h1int = bits2int_modN(ensureBytes(msgHash));
|
const h1int = bits2int_modN(msgHash);
|
||||||
const d = normalizePrivateKey(privateKey); // validate private key, convert to bigint
|
const d = normPrivateKeyToScalar(privateKey); // validate private key, convert to bigint
|
||||||
const seedArgs = [int2octets(d), int2octets(h1int)];
|
const seedArgs = [int2octets(d), int2octets(h1int)];
|
||||||
// extraEntropy. RFC6979 3.6: additional k' (optional).
|
// extraEntropy. RFC6979 3.6: additional k' (optional).
|
||||||
if (ent != null) {
|
if (ent != null && ent !== false) {
|
||||||
// K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k')
|
// K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k')
|
||||||
// Either pass as-is, or generate random bytes. Then validate for being ui8a of size BYTES
|
const e = ent === true ? randomBytes(Fp.BYTES) : ent; // generate random bytes OR pass as-is
|
||||||
seedArgs.push(ensureBytes(ent === true ? randomBytes(Fp.BYTES) : ent, Fp.BYTES));
|
seedArgs.push(ensureBytes('extraEntropy', e)); // check for being bytes
|
||||||
}
|
}
|
||||||
const seed = ut.concatBytes(...seedArgs); // Step D of RFC6979 3.2
|
const seed = ut.concatBytes(...seedArgs); // Step D of RFC6979 3.2
|
||||||
const m = h1int; // NOTE: no need to call bits2int second time here, it is inside truncateHash!
|
const m = h1int; // NOTE: no need to call bits2int second time here, it is inside truncateHash!
|
||||||
// Converts signature params into point w r/s, checks result for validity.
|
// Converts signature params into point w r/s, checks result for validity.
|
||||||
function k2sig(kBytes: Uint8Array): Signature | undefined {
|
function k2sig(kBytes: Uint8Array): RecoveredSignature | undefined {
|
||||||
// RFC 6979 Section 3.2, step 3: k = bits2int(T)
|
// RFC 6979 Section 3.2, step 3: k = bits2int(T)
|
||||||
const k = bits2int(kBytes); // Cannot use fields methods, since it is group element
|
const k = bits2int(kBytes); // Cannot use fields methods, since it is group element
|
||||||
if (!isWithinCurveOrder(k)) return; // Important: all mod() calls here must be done over N
|
if (!isWithinCurveOrder(k)) return; // Important: all mod() calls here must be done over N
|
||||||
@@ -993,7 +993,10 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
const q = Point.BASE.multiply(k).toAffine(); // q = Gk
|
const q = Point.BASE.multiply(k).toAffine(); // q = Gk
|
||||||
const r = modN(q.x); // r = q.x mod n
|
const r = modN(q.x); // r = q.x mod n
|
||||||
if (r === _0n) return;
|
if (r === _0n) return;
|
||||||
const s = modN(ik * modN(m + modN(d * r))); // s = k^-1(m + rd) mod n
|
// Can use scalar blinding b^-1(bm + bdr) where b ∈ [1,q−1] according to
|
||||||
|
// https://tches.iacr.org/index.php/TCHES/article/view/7337/6509. We've decided against it:
|
||||||
|
// a) dependency on CSPRNG b) 15% slowdown c) doesn't really help since bigints are not CT
|
||||||
|
const s = modN(ik * modN(m + r * d)); // Not using blinding here
|
||||||
if (s === _0n) return;
|
if (s === _0n) return;
|
||||||
let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n); // recovery bit (2 or 3, when q.x > n)
|
let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n); // recovery bit (2 or 3, when q.x > n)
|
||||||
let normS = s;
|
let normS = s;
|
||||||
@@ -1001,7 +1004,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
normS = normalizeS(s); // if lowS was passed, ensure s is always
|
normS = normalizeS(s); // if lowS was passed, ensure s is always
|
||||||
recovery ^= 1; // // in the bottom half of N
|
recovery ^= 1; // // in the bottom half of N
|
||||||
}
|
}
|
||||||
return new Signature(r, normS, recovery); // use normS, not s
|
return new Signature(r, normS, recovery) as RecoveredSignature; // use normS, not s
|
||||||
}
|
}
|
||||||
return { seed, k2sig };
|
return { seed, k2sig };
|
||||||
}
|
}
|
||||||
@@ -1009,19 +1012,23 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
const defaultVerOpts: VerOpts = { lowS: CURVE.lowS, prehash: false };
|
const defaultVerOpts: VerOpts = { lowS: CURVE.lowS, prehash: false };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signs message hash (not message: you need to hash it by yourself).
|
* Signs message hash with a private key.
|
||||||
* ```
|
* ```
|
||||||
* sign(m, d, k) where
|
* sign(m, d, k) where
|
||||||
* (x, y) = G × k
|
* (x, y) = G × k
|
||||||
* r = x mod n
|
* r = x mod n
|
||||||
* s = (m + dr)/k mod n
|
* s = (m + dr)/k mod n
|
||||||
* ```
|
* ```
|
||||||
* @param opts `lowS, extraEntropy, prehash`
|
* @param msgHash NOT message. msg needs to be hashed to `msgHash`, or use `prehash`.
|
||||||
|
* @param privKey private key
|
||||||
|
* @param opts lowS for non-malleable sigs. extraEntropy for mixing randomness into k. prehash will hash first arg.
|
||||||
|
* @returns signature with recovery param
|
||||||
*/
|
*/
|
||||||
function sign(msgHash: Hex, privKey: PrivKey, opts = defaultSigOpts): Signature {
|
function sign(msgHash: Hex, privKey: PrivKey, opts = defaultSigOpts): RecoveredSignature {
|
||||||
const { seed, k2sig } = prepSig(msgHash, privKey, opts); // Steps A, D of RFC6979 3.2.
|
const { seed, k2sig } = prepSig(msgHash, privKey, opts); // Steps A, D of RFC6979 3.2.
|
||||||
const genUntil = hmacDrbg<Signature>(CURVE.hash.outputLen, CURVE.nByteLength, CURVE.hmac);
|
const C = CURVE;
|
||||||
return genUntil(seed, k2sig); // Steps B, C, D, E, F, G
|
const drbg = ut.createHmacDrbg<RecoveredSignature>(C.hash.outputLen, C.nByteLength, C.hmac);
|
||||||
|
return drbg(seed, k2sig); // Steps B, C, D, E, F, G
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable precomputes. Slows down first publicKey computation by 20ms.
|
// Enable precomputes. Slows down first publicKey computation by 20ms.
|
||||||
@@ -1047,30 +1054,38 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
publicKey: Hex,
|
publicKey: Hex,
|
||||||
opts = defaultVerOpts
|
opts = defaultVerOpts
|
||||||
): boolean {
|
): boolean {
|
||||||
let P: ProjPointType<bigint>;
|
const sg = signature;
|
||||||
|
msgHash = ensureBytes('msgHash', msgHash);
|
||||||
|
publicKey = ensureBytes('publicKey', publicKey);
|
||||||
|
if ('strict' in opts) throw new Error('options.strict was renamed to lowS');
|
||||||
|
const { lowS, prehash } = opts;
|
||||||
|
|
||||||
let _sig: Signature | undefined = undefined;
|
let _sig: Signature | undefined = undefined;
|
||||||
if (publicKey instanceof Point) throw new Error('publicKey must be hex');
|
let P: ProjPointType<bigint>;
|
||||||
try {
|
try {
|
||||||
if (signature && typeof signature === 'object' && !(signature instanceof Uint8Array)) {
|
if (typeof sg === 'string' || ut.isBytes(sg)) {
|
||||||
const { r, s } = signature;
|
|
||||||
_sig = new Signature(r, s); // assertValidity() is executed on creation
|
|
||||||
} else {
|
|
||||||
// Signature can be represented in 2 ways: compact (2*nByteLength) & DER (variable-length).
|
// Signature can be represented in 2 ways: compact (2*nByteLength) & DER (variable-length).
|
||||||
// Since DER can also be 2*nByteLength bytes, we check for it first.
|
// Since DER can also be 2*nByteLength bytes, we check for it first.
|
||||||
try {
|
try {
|
||||||
_sig = Signature.fromDER(signature as Hex);
|
_sig = Signature.fromDER(sg);
|
||||||
} catch (derError) {
|
} catch (derError) {
|
||||||
if (!(derError instanceof DER.Err)) throw derError;
|
if (!(derError instanceof DER.Err)) throw derError;
|
||||||
_sig = Signature.fromCompact(signature as Hex);
|
_sig = Signature.fromCompact(sg);
|
||||||
}
|
}
|
||||||
|
} else if (typeof sg === 'object' && typeof sg.r === 'bigint' && typeof sg.s === 'bigint') {
|
||||||
|
const { r, s } = sg;
|
||||||
|
_sig = new Signature(r, s);
|
||||||
|
} else {
|
||||||
|
throw new Error('PARSE');
|
||||||
}
|
}
|
||||||
msgHash = ensureBytes(msgHash);
|
|
||||||
P = Point.fromHex(publicKey);
|
P = Point.fromHex(publicKey);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
if ((error as Error).message === 'PARSE')
|
||||||
|
throw new Error(`signature must be Signature instance, Uint8Array or hex string`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (opts.lowS && _sig.hasHighS()) return false;
|
if (lowS && _sig.hasHighS()) return false;
|
||||||
if (opts.prehash) msgHash = CURVE.hash(msgHash);
|
if (prehash) msgHash = CURVE.hash(msgHash);
|
||||||
const { r, s } = _sig;
|
const { r, s } = _sig;
|
||||||
const h = bits2int_modN(msgHash); // Cannot use fields methods, since it is group element
|
const h = bits2int_modN(msgHash); // Cannot use fields methods, since it is group element
|
||||||
const is = invN(s); // s^-1
|
const is = invN(s); // s^-1
|
||||||
@@ -1093,23 +1108,31 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implementation of the Shallue and van de Woestijne method for any Weierstrass curve
|
/**
|
||||||
|
* Implementation of the Shallue and van de Woestijne method for any weierstrass curve.
|
||||||
// TODO: check if there is a way to merge this with uvRatio in Edwards && move to modular?
|
* TODO: check if there is a way to merge this with uvRatio in Edwards; move to modular.
|
||||||
// b = True and y = sqrt(u / v) if (u / v) is square in F, and
|
* b = True and y = sqrt(u / v) if (u / v) is square in F, and
|
||||||
// b = False and y = sqrt(Z * (u / v)) otherwise.
|
* b = False and y = sqrt(Z * (u / v)) otherwise.
|
||||||
export function SWUFpSqrtRatio<T>(Fp: mod.Field<T>, Z: T) {
|
* @param Fp
|
||||||
|
* @param Z
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function SWUFpSqrtRatio<T>(Fp: mod.IField<T>, Z: T) {
|
||||||
// Generic implementation
|
// Generic implementation
|
||||||
const q = Fp.ORDER;
|
const q = Fp.ORDER;
|
||||||
let l = 0n;
|
let l = _0n;
|
||||||
for (let o = q - 1n; o % 2n === 0n; o /= 2n) l += 1n;
|
for (let o = q - _1n; o % _2n === _0n; o /= _2n) l += _1n;
|
||||||
const c1 = l; // 1. c1, the largest integer such that 2^c1 divides q - 1.
|
const c1 = l; // 1. c1, the largest integer such that 2^c1 divides q - 1.
|
||||||
const c2 = (q - 1n) / 2n ** c1; // 2. c2 = (q - 1) / (2^c1) # Integer arithmetic
|
// We need 2n ** c1 and 2n ** (c1-1). We can't use **; but we can use <<.
|
||||||
const c3 = (c2 - 1n) / 2n; // 3. c3 = (c2 - 1) / 2 # Integer arithmetic
|
// 2n ** c1 == 2n << (c1-1)
|
||||||
const c4 = 2n ** c1 - 1n; // 4. c4 = 2^c1 - 1 # Integer arithmetic
|
const _2n_pow_c1_1 = _2n << (c1 - _1n - _1n);
|
||||||
const c5 = 2n ** (c1 - 1n); // 5. c5 = 2^(c1 - 1) # Integer arithmetic
|
const _2n_pow_c1 = _2n_pow_c1_1 * _2n;
|
||||||
|
const c2 = (q - _1n) / _2n_pow_c1; // 2. c2 = (q - 1) / (2^c1) # Integer arithmetic
|
||||||
|
const c3 = (c2 - _1n) / _2n; // 3. c3 = (c2 - 1) / 2 # Integer arithmetic
|
||||||
|
const c4 = _2n_pow_c1 - _1n; // 4. c4 = 2^c1 - 1 # Integer arithmetic
|
||||||
|
const c5 = _2n_pow_c1_1; // 5. c5 = 2^(c1 - 1) # Integer arithmetic
|
||||||
const c6 = Fp.pow(Z, c2); // 6. c6 = Z^c2
|
const c6 = Fp.pow(Z, c2); // 6. c6 = Z^c2
|
||||||
const c7 = Fp.pow(Z, (c2 + 1n) / 2n); // 7. c7 = Z^((c2 + 1) / 2)
|
const c7 = Fp.pow(Z, (c2 + _1n) / _2n); // 7. c7 = Z^((c2 + 1) / 2)
|
||||||
let sqrtRatio = (u: T, v: T): { isValid: boolean; value: T } => {
|
let sqrtRatio = (u: T, v: T): { isValid: boolean; value: T } => {
|
||||||
let tv1 = c6; // 1. tv1 = c6
|
let tv1 = c6; // 1. tv1 = c6
|
||||||
let tv2 = Fp.pow(v, c4); // 2. tv2 = v^c4
|
let tv2 = Fp.pow(v, c4); // 2. tv2 = v^c4
|
||||||
@@ -1128,8 +1151,9 @@ export function SWUFpSqrtRatio<T>(Fp: mod.Field<T>, Z: T) {
|
|||||||
tv3 = Fp.cmov(tv2, tv3, isQR); // 15. tv3 = CMOV(tv2, tv3, isQR)
|
tv3 = Fp.cmov(tv2, tv3, isQR); // 15. tv3 = CMOV(tv2, tv3, isQR)
|
||||||
tv4 = Fp.cmov(tv5, tv4, isQR); // 16. tv4 = CMOV(tv5, tv4, isQR)
|
tv4 = Fp.cmov(tv5, tv4, isQR); // 16. tv4 = CMOV(tv5, tv4, isQR)
|
||||||
// 17. for i in (c1, c1 - 1, ..., 2):
|
// 17. for i in (c1, c1 - 1, ..., 2):
|
||||||
for (let i = c1; i > 1; i--) {
|
for (let i = c1; i > _1n; i--) {
|
||||||
let tv5 = 2n ** (i - 2n); // 18. tv5 = i - 2; 19. tv5 = 2^tv5
|
let tv5 = i - _2n; // 18. tv5 = i - 2
|
||||||
|
tv5 = _2n << (tv5 - _1n); // 19. tv5 = 2^tv5
|
||||||
let tvv5 = Fp.pow(tv4, tv5); // 20. tv5 = tv4^tv5
|
let tvv5 = Fp.pow(tv4, tv5); // 20. tv5 = tv4^tv5
|
||||||
const e1 = Fp.eql(tvv5, Fp.ONE); // 21. e1 = tv5 == 1
|
const e1 = Fp.eql(tvv5, Fp.ONE); // 21. e1 = tv5 == 1
|
||||||
tv2 = Fp.mul(tv3, tv1); // 22. tv2 = tv3 * tv1
|
tv2 = Fp.mul(tv3, tv1); // 22. tv2 = tv3 * tv1
|
||||||
@@ -1140,9 +1164,9 @@ export function SWUFpSqrtRatio<T>(Fp: mod.Field<T>, Z: T) {
|
|||||||
}
|
}
|
||||||
return { isValid: isQR, value: tv3 };
|
return { isValid: isQR, value: tv3 };
|
||||||
};
|
};
|
||||||
if (Fp.ORDER % 4n === 3n) {
|
if (Fp.ORDER % _4n === _3n) {
|
||||||
// sqrt_ratio_3mod4(u, v)
|
// sqrt_ratio_3mod4(u, v)
|
||||||
const c1 = (Fp.ORDER - 3n) / 4n; // 1. c1 = (q - 3) / 4 # Integer arithmetic
|
const c1 = (Fp.ORDER - _3n) / _4n; // 1. c1 = (q - 3) / 4 # Integer arithmetic
|
||||||
const c2 = Fp.sqrt(Fp.neg(Z)); // 2. c2 = sqrt(-Z)
|
const c2 = Fp.sqrt(Fp.neg(Z)); // 2. c2 = sqrt(-Z)
|
||||||
sqrtRatio = (u: T, v: T) => {
|
sqrtRatio = (u: T, v: T) => {
|
||||||
let tv1 = Fp.sqr(v); // 1. tv1 = v^2
|
let tv1 = Fp.sqr(v); // 1. tv1 = v^2
|
||||||
@@ -1158,12 +1182,15 @@ export function SWUFpSqrtRatio<T>(Fp: mod.Field<T>, Z: T) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
// No curves uses that
|
// No curves uses that
|
||||||
// if (Fp.ORDER % 8n === 5n) // sqrt_ratio_5mod8
|
// if (Fp.ORDER % _8n === _5n) // sqrt_ratio_5mod8
|
||||||
return sqrtRatio;
|
return sqrtRatio;
|
||||||
}
|
}
|
||||||
// From draft-irtf-cfrg-hash-to-curve-16
|
/**
|
||||||
|
* Simplified Shallue-van de Woestijne-Ulas Method
|
||||||
|
* https://www.rfc-editor.org/rfc/rfc9380#section-6.6.2
|
||||||
|
*/
|
||||||
export function mapToCurveSimpleSWU<T>(
|
export function mapToCurveSimpleSWU<T>(
|
||||||
Fp: mod.Field<T>,
|
Fp: mod.IField<T>,
|
||||||
opts: {
|
opts: {
|
||||||
A: T;
|
A: T;
|
||||||
B: T;
|
B: T;
|
||||||
|
|||||||
601
src/bls12-381.ts
601
src/bls12-381.ts
@@ -1,16 +1,35 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
|
|
||||||
// The pairing-friendly Barreto-Lynn-Scott elliptic curve construction allows to:
|
// bls12-381 is pairing-friendly Barreto-Lynn-Scott elliptic curve construction allowing to:
|
||||||
// - Construct zk-SNARKs at the 128-bit security
|
// - Construct zk-SNARKs at the 120-bit security
|
||||||
// - Use threshold signatures, which allows a user to sign lots of messages with one signature and verify them swiftly in a batch, using Boneh-Lynn-Shacham signature scheme.
|
// - Efficiently verify N aggregate signatures with 1 pairing and N ec additions:
|
||||||
// Differences from @noble/bls12-381 1.4:
|
// the Boneh-Lynn-Shacham signature scheme is orders of magnitude more efficient than Schnorr
|
||||||
// - PointG1 -> G1.Point
|
//
|
||||||
// - PointG2 -> G2.Point
|
// ### Summary
|
||||||
// - PointG2.fromSignature -> Signature.decode
|
// 1. BLS Relies on Bilinear Pairing (expensive)
|
||||||
// - PointG2.toSignature -> Signature.encode
|
// 2. Private Keys: 32 bytes
|
||||||
// - Fixed Fp2 ORDER
|
// 3. Public Keys: 48 bytes: 381 bit affine x coordinate, encoded into 48 big-endian bytes.
|
||||||
// - Points now have only two coordinates
|
// 4. Signatures: 96 bytes: two 381 bit integers (affine x coordinate), encoded into two 48 big-endian byte arrays.
|
||||||
|
// - The signature is a point on the G2 subgroup, which is defined over a finite field
|
||||||
|
// with elements twice as big as the G1 curve (G2 is over Fp2 rather than Fp. Fp2 is analogous to the complex numbers).
|
||||||
|
// 5. The 12 stands for the Embedding degree.
|
||||||
|
//
|
||||||
|
// ### Formulas
|
||||||
|
// - `P = pk x G` - public keys
|
||||||
|
// - `S = pk x H(m)` - signing
|
||||||
|
// - `e(P, H(m)) == e(G, S)` - verification using pairings
|
||||||
|
// - `e(G, S) = e(G, SUM(n)(Si)) = MUL(n)(e(G, Si))` - signature aggregation
|
||||||
|
//
|
||||||
|
// ### Compatibility and notes
|
||||||
|
// 1. It is compatible with Algorand, Chia, Dfinity, Ethereum, Filecoin, ZEC
|
||||||
|
// Filecoin uses little endian byte arrays for private keys - make sure to reverse byte order.
|
||||||
|
// 2. Some projects use G2 for public keys and G1 for signatures. It's called "short signature"
|
||||||
|
// 3. Curve security level is about 120 bits as per Barbulescu-Duquesne 2017
|
||||||
|
// https://hal.science/hal-01534101/file/main.pdf
|
||||||
|
// 4. Compatible with specs:
|
||||||
|
// [cfrg-pairing-friendly-curves-11](https://tools.ietf.org/html/draft-irtf-cfrg-pairing-friendly-curves-11),
|
||||||
|
// [cfrg-bls-signature-05](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-bls-signature-05),
|
||||||
|
// [RFC 9380](https://www.rfc-editor.org/rfc/rfc9380).
|
||||||
import { sha256 } from '@noble/hashes/sha256';
|
import { sha256 } from '@noble/hashes/sha256';
|
||||||
import { randomBytes } from '@noble/hashes/utils';
|
import { randomBytes } from '@noble/hashes/utils';
|
||||||
import { bls, CurveFn } from './abstract/bls.js';
|
import { bls, CurveFn } from './abstract/bls.js';
|
||||||
@@ -21,10 +40,10 @@ import {
|
|||||||
numberToBytesBE,
|
numberToBytesBE,
|
||||||
bytesToNumberBE,
|
bytesToNumberBE,
|
||||||
bitLen,
|
bitLen,
|
||||||
bitSet,
|
|
||||||
bitGet,
|
bitGet,
|
||||||
Hex,
|
Hex,
|
||||||
bitMask,
|
bitMask,
|
||||||
|
bytesToHex,
|
||||||
} from './abstract/utils.js';
|
} from './abstract/utils.js';
|
||||||
// Types
|
// Types
|
||||||
import {
|
import {
|
||||||
@@ -35,16 +54,22 @@ import {
|
|||||||
} from './abstract/weierstrass.js';
|
} from './abstract/weierstrass.js';
|
||||||
import { isogenyMap } from './abstract/hash-to-curve.js';
|
import { isogenyMap } from './abstract/hash-to-curve.js';
|
||||||
|
|
||||||
|
// Be friendly to bad ECMAScript parsers by not using bigint literals
|
||||||
|
// prettier-ignore
|
||||||
|
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n = BigInt(4);
|
||||||
|
// prettier-ignore
|
||||||
|
const _8n = BigInt(8), _16n = BigInt(16);
|
||||||
|
|
||||||
// CURVE FIELDS
|
// CURVE FIELDS
|
||||||
// Finite field over p.
|
// Finite field over p.
|
||||||
const Fp =
|
const Fp_raw = BigInt(
|
||||||
mod.Fp(
|
'0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab'
|
||||||
0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaabn
|
);
|
||||||
);
|
const Fp = mod.Field(Fp_raw);
|
||||||
type Fp = bigint;
|
type Fp = bigint;
|
||||||
// Finite field over r.
|
// Finite field over r.
|
||||||
// This particular field is not used anywhere in bls12-381, but it is still useful.
|
// This particular field is not used anywhere in bls12-381, but it is still useful.
|
||||||
const Fr = mod.Fp(0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001n);
|
const Fr = mod.Field(BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001'));
|
||||||
|
|
||||||
// Fp₂ over complex plane
|
// Fp₂ over complex plane
|
||||||
type BigintTuple = [bigint, bigint];
|
type BigintTuple = [bigint, bigint];
|
||||||
@@ -86,11 +111,9 @@ type Fp2Utils = {
|
|||||||
// G² - 1
|
// G² - 1
|
||||||
// h2q
|
// h2q
|
||||||
// NOTE: ORDER was wrong!
|
// NOTE: ORDER was wrong!
|
||||||
const FP2_ORDER =
|
const FP2_ORDER = Fp_raw * Fp_raw;
|
||||||
0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaabn **
|
|
||||||
2n;
|
|
||||||
|
|
||||||
const Fp2: mod.Field<Fp2> & Fp2Utils = {
|
const Fp2: mod.IField<Fp2> & Fp2Utils = {
|
||||||
ORDER: FP2_ORDER,
|
ORDER: FP2_ORDER,
|
||||||
BITS: bitLen(FP2_ORDER),
|
BITS: bitLen(FP2_ORDER),
|
||||||
BYTES: Math.ceil(bitLen(FP2_ORDER) / 8),
|
BYTES: Math.ceil(bitLen(FP2_ORDER) / 8),
|
||||||
@@ -141,7 +164,7 @@ const Fp2: mod.Field<Fp2> & Fp2Utils = {
|
|||||||
// https://github.com/zkcrypto/bls12_381/blob/080eaa74ec0e394377caa1ba302c8c121df08b07/src/fp2.rs#L250
|
// https://github.com/zkcrypto/bls12_381/blob/080eaa74ec0e394377caa1ba302c8c121df08b07/src/fp2.rs#L250
|
||||||
// https://github.com/supranational/blst/blob/aae0c7d70b799ac269ff5edf29d8191dbd357876/src/exp2.c#L1
|
// https://github.com/supranational/blst/blob/aae0c7d70b799ac269ff5edf29d8191dbd357876/src/exp2.c#L1
|
||||||
// Inspired by https://github.com/dalek-cryptography/curve25519-dalek/blob/17698df9d4c834204f83a3574143abacb4fc81a5/src/field.rs#L99
|
// Inspired by https://github.com/dalek-cryptography/curve25519-dalek/blob/17698df9d4c834204f83a3574143abacb4fc81a5/src/field.rs#L99
|
||||||
const candidateSqrt = Fp2.pow(num, (Fp2.ORDER + 8n) / 16n);
|
const candidateSqrt = Fp2.pow(num, (Fp2.ORDER + _8n) / _16n);
|
||||||
const check = Fp2.div(Fp2.sqr(candidateSqrt), num); // candidateSqrt.square().div(this);
|
const check = Fp2.div(Fp2.sqr(candidateSqrt), num); // candidateSqrt.square().div(this);
|
||||||
const R = FP2_ROOTS_OF_UNITY;
|
const R = FP2_ROOTS_OF_UNITY;
|
||||||
const divisor = [R[0], R[2], R[4], R[6]].find((r) => Fp2.eql(r, check));
|
const divisor = [R[0], R[2], R[4], R[6]].find((r) => Fp2.eql(r, check));
|
||||||
@@ -156,13 +179,13 @@ const Fp2: mod.Field<Fp2> & Fp2Utils = {
|
|||||||
if (im1 > im2 || (im1 === im2 && re1 > re2)) return x1;
|
if (im1 > im2 || (im1 === im2 && re1 > re2)) return x1;
|
||||||
return x2;
|
return x2;
|
||||||
},
|
},
|
||||||
// Same as sgn0_fp2 in draft-irtf-cfrg-hash-to-curve-16
|
// Same as sgn0_m_eq_2 in RFC 9380
|
||||||
isOdd: (x: Fp2) => {
|
isOdd: (x: Fp2) => {
|
||||||
const { re: x0, im: x1 } = Fp2.reim(x);
|
const { re: x0, im: x1 } = Fp2.reim(x);
|
||||||
const sign_0 = x0 % 2n;
|
const sign_0 = x0 % _2n;
|
||||||
const zero_0 = x0 === 0n;
|
const zero_0 = x0 === _0n;
|
||||||
const sign_1 = x1 % 2n;
|
const sign_1 = x1 % _2n;
|
||||||
return BigInt(sign_0 || (zero_0 && sign_1)) == 1n;
|
return BigInt(sign_0 || (zero_0 && sign_1)) == _1n;
|
||||||
},
|
},
|
||||||
// Bytes util
|
// Bytes util
|
||||||
fromBytes(b: Uint8Array): Fp2 {
|
fromBytes(b: Uint8Array): Fp2 {
|
||||||
@@ -182,8 +205,8 @@ const Fp2: mod.Field<Fp2> & Fp2Utils = {
|
|||||||
// multiply by u + 1
|
// multiply by u + 1
|
||||||
mulByNonresidue: ({ c0, c1 }) => ({ c0: Fp.sub(c0, c1), c1: Fp.add(c0, c1) }),
|
mulByNonresidue: ({ c0, c1 }) => ({ c0: Fp.sub(c0, c1), c1: Fp.add(c0, c1) }),
|
||||||
multiplyByB: ({ c0, c1 }) => {
|
multiplyByB: ({ c0, c1 }) => {
|
||||||
let t0 = Fp.mul(c0, 4n); // 4 * c0
|
let t0 = Fp.mul(c0, _4n); // 4 * c0
|
||||||
let t1 = Fp.mul(c1, 4n); // 4 * c1
|
let t1 = Fp.mul(c1, _4n); // 4 * c1
|
||||||
// (T0-T1) + (T0+T1)*i
|
// (T0-T1) + (T0+T1)*i
|
||||||
return { c0: Fp.sub(t0, t1), c1: Fp.add(t0, t1) };
|
return { c0: Fp.sub(t0, t1), c1: Fp.add(t0, t1) };
|
||||||
},
|
},
|
||||||
@@ -200,33 +223,36 @@ const Fp2: mod.Field<Fp2> & Fp2Utils = {
|
|||||||
// Finite extension field over irreducible polynominal.
|
// Finite extension field over irreducible polynominal.
|
||||||
// Fp(u) / (u² - β) where β = -1
|
// Fp(u) / (u² - β) where β = -1
|
||||||
const FP2_FROBENIUS_COEFFICIENTS = [
|
const FP2_FROBENIUS_COEFFICIENTS = [
|
||||||
0x1n,
|
BigInt('0x1'),
|
||||||
0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaan,
|
BigInt(
|
||||||
|
'0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa'
|
||||||
|
),
|
||||||
].map((item) => Fp.create(item));
|
].map((item) => Fp.create(item));
|
||||||
|
|
||||||
// For Fp2 roots of unity.
|
// For Fp2 roots of unity.
|
||||||
const rv1 =
|
const rv1 = BigInt(
|
||||||
0x6af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09n;
|
'0x6af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09'
|
||||||
|
);
|
||||||
// const ev1 =
|
// const ev1 =
|
||||||
// 0x699be3b8c6870965e5bf892ad5d2cc7b0e85a117402dfd83b7f4a947e02d978498255a2aaec0ac627b5afbdf1bf1c90n;
|
// BigInt('0x699be3b8c6870965e5bf892ad5d2cc7b0e85a117402dfd83b7f4a947e02d978498255a2aaec0ac627b5afbdf1bf1c90');
|
||||||
// const ev2 =
|
// const ev2 =
|
||||||
// 0x8157cd83046453f5dd0972b6e3949e4288020b5b8a9cc99ca07e27089a2ce2436d965026adad3ef7baba37f2183e9b5n;
|
// BigInt('0x8157cd83046453f5dd0972b6e3949e4288020b5b8a9cc99ca07e27089a2ce2436d965026adad3ef7baba37f2183e9b5');
|
||||||
// const ev3 =
|
// const ev3 =
|
||||||
// 0xab1c2ffdd6c253ca155231eb3e71ba044fd562f6f72bc5bad5ec46a0b7a3b0247cf08ce6c6317f40edbc653a72dee17n;
|
// BigInt('0xab1c2ffdd6c253ca155231eb3e71ba044fd562f6f72bc5bad5ec46a0b7a3b0247cf08ce6c6317f40edbc653a72dee17');
|
||||||
// const ev4 =
|
// const ev4 =
|
||||||
// 0xaa404866706722864480885d68ad0ccac1967c7544b447873cc37e0181271e006df72162a3d3e0287bf597fbf7f8fc1n;
|
// BigInt('0xaa404866706722864480885d68ad0ccac1967c7544b447873cc37e0181271e006df72162a3d3e0287bf597fbf7f8fc1');
|
||||||
|
|
||||||
// Eighth roots of unity, used for computing square roots in Fp2.
|
// Eighth roots of unity, used for computing square roots in Fp2.
|
||||||
// To verify or re-calculate:
|
// To verify or re-calculate:
|
||||||
// Array(8).fill(new Fp2([1n, 1n])).map((fp2, k) => fp2.pow(Fp2.ORDER * BigInt(k) / 8n))
|
// Array(8).fill(new Fp2([1n, 1n])).map((fp2, k) => fp2.pow(Fp2.ORDER * BigInt(k) / 8n))
|
||||||
const FP2_ROOTS_OF_UNITY = [
|
const FP2_ROOTS_OF_UNITY = [
|
||||||
[1n, 0n],
|
[_1n, _0n],
|
||||||
[rv1, -rv1],
|
[rv1, -rv1],
|
||||||
[0n, 1n],
|
[_0n, _1n],
|
||||||
[rv1, rv1],
|
[rv1, rv1],
|
||||||
[-1n, 0n],
|
[-_1n, _0n],
|
||||||
[-rv1, rv1],
|
[-rv1, rv1],
|
||||||
[0n, -1n],
|
[_0n, -_1n],
|
||||||
[-rv1, -rv1],
|
[-rv1, -rv1],
|
||||||
].map((pair) => Fp2.fromBigTuple(pair));
|
].map((pair) => Fp2.fromBigTuple(pair));
|
||||||
// eta values, used for computing sqrt(g(X1(t)))
|
// eta values, used for computing sqrt(g(X1(t)))
|
||||||
@@ -280,8 +306,8 @@ const Fp6Multiply = ({ c0, c1, c2 }: Fp6, rhs: Fp6 | bigint) => {
|
|||||||
};
|
};
|
||||||
const Fp6Square = ({ c0, c1, c2 }: Fp6) => {
|
const Fp6Square = ({ c0, c1, c2 }: Fp6) => {
|
||||||
let t0 = Fp2.sqr(c0); // c0²
|
let t0 = Fp2.sqr(c0); // c0²
|
||||||
let t1 = Fp2.mul(Fp2.mul(c0, c1), 2n); // 2 * c0 * c1
|
let t1 = Fp2.mul(Fp2.mul(c0, c1), _2n); // 2 * c0 * c1
|
||||||
let t3 = Fp2.mul(Fp2.mul(c1, c2), 2n); // 2 * c1 * c2
|
let t3 = Fp2.mul(Fp2.mul(c1, c2), _2n); // 2 * c1 * c2
|
||||||
let t4 = Fp2.sqr(c2); // c2²
|
let t4 = Fp2.sqr(c2); // c2²
|
||||||
return {
|
return {
|
||||||
c0: Fp2.add(Fp2.mulByNonresidue(t3), t0), // T3 * (u + 1) + T0
|
c0: Fp2.add(Fp2.mulByNonresidue(t3), t0), // T3 * (u + 1) + T0
|
||||||
@@ -299,7 +325,7 @@ type Fp6Utils = {
|
|||||||
multiplyByFp2(lhs: Fp6, rhs: Fp2): Fp6;
|
multiplyByFp2(lhs: Fp6, rhs: Fp2): Fp6;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Fp6: mod.Field<Fp6> & Fp6Utils = {
|
const Fp6: mod.IField<Fp6> & Fp6Utils = {
|
||||||
ORDER: Fp2.ORDER, // TODO: unused, but need to verify
|
ORDER: Fp2.ORDER, // TODO: unused, but need to verify
|
||||||
BITS: 3 * Fp2.BITS,
|
BITS: 3 * Fp2.BITS,
|
||||||
BYTES: 3 * Fp2.BYTES,
|
BYTES: 3 * Fp2.BYTES,
|
||||||
@@ -406,46 +432,64 @@ const Fp6: mod.Field<Fp6> & Fp6Utils = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const FP6_FROBENIUS_COEFFICIENTS_1 = [
|
const FP6_FROBENIUS_COEFFICIENTS_1 = [
|
||||||
[0x1n, 0x0n],
|
[BigInt('0x1'), BigInt('0x0')],
|
||||||
[
|
[
|
||||||
0x0n,
|
BigInt('0x0'),
|
||||||
0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaacn,
|
BigInt(
|
||||||
|
'0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac'
|
||||||
|
),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffen,
|
BigInt(
|
||||||
0x0n,
|
'0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe'
|
||||||
|
),
|
||||||
|
BigInt('0x0'),
|
||||||
],
|
],
|
||||||
[0x0n, 0x1n],
|
[BigInt('0x0'), BigInt('0x1')],
|
||||||
[
|
[
|
||||||
0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaacn,
|
BigInt(
|
||||||
0x0n,
|
'0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac'
|
||||||
|
),
|
||||||
|
BigInt('0x0'),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
0x0n,
|
BigInt('0x0'),
|
||||||
0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffen,
|
BigInt(
|
||||||
|
'0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe'
|
||||||
|
),
|
||||||
],
|
],
|
||||||
].map((pair) => Fp2.fromBigTuple(pair));
|
].map((pair) => Fp2.fromBigTuple(pair));
|
||||||
const FP6_FROBENIUS_COEFFICIENTS_2 = [
|
const FP6_FROBENIUS_COEFFICIENTS_2 = [
|
||||||
[0x1n, 0x0n],
|
[BigInt('0x1'), BigInt('0x0')],
|
||||||
[
|
[
|
||||||
0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaadn,
|
BigInt(
|
||||||
0x0n,
|
'0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaad'
|
||||||
|
),
|
||||||
|
BigInt('0x0'),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaacn,
|
BigInt(
|
||||||
0x0n,
|
'0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac'
|
||||||
|
),
|
||||||
|
BigInt('0x0'),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaan,
|
BigInt(
|
||||||
0x0n,
|
'0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa'
|
||||||
|
),
|
||||||
|
BigInt('0x0'),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffen,
|
BigInt(
|
||||||
0x0n,
|
'0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe'
|
||||||
|
),
|
||||||
|
BigInt('0x0'),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffeffffn,
|
BigInt(
|
||||||
0x0n,
|
'0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffeffff'
|
||||||
|
),
|
||||||
|
BigInt('0x0'),
|
||||||
],
|
],
|
||||||
].map((pair) => Fp2.fromBigTuple(pair));
|
].map((pair) => Fp2.fromBigTuple(pair));
|
||||||
|
|
||||||
@@ -454,7 +498,7 @@ const FP6_FROBENIUS_COEFFICIENTS_2 = [
|
|||||||
// Fp₆(w) / (w² - γ) where γ = v
|
// Fp₆(w) / (w² - γ) where γ = v
|
||||||
type Fp12 = { c0: Fp6; c1: Fp6 };
|
type Fp12 = { c0: Fp6; c1: Fp6 };
|
||||||
// The BLS parameter x for BLS12-381
|
// The BLS parameter x for BLS12-381
|
||||||
const BLS_X = 0xd201000000010000n;
|
const BLS_X = BigInt('0xd201000000010000');
|
||||||
const BLS_X_LEN = bitLen(BLS_X);
|
const BLS_X_LEN = bitLen(BLS_X);
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
@@ -511,7 +555,7 @@ type Fp12Utils = {
|
|||||||
_cyclotomicExp(num: Fp12, n: bigint): Fp12;
|
_cyclotomicExp(num: Fp12, n: bigint): Fp12;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Fp12: mod.Field<Fp12> & Fp12Utils = {
|
const Fp12: mod.IField<Fp12> & Fp12Utils = {
|
||||||
ORDER: Fp2.ORDER, // TODO: unused, but need to verify
|
ORDER: Fp2.ORDER, // TODO: unused, but need to verify
|
||||||
BITS: 2 * Fp2.BITS,
|
BITS: 2 * Fp2.BITS,
|
||||||
BYTES: 2 * Fp2.BYTES,
|
BYTES: 2 * Fp2.BYTES,
|
||||||
@@ -612,14 +656,14 @@ const Fp12: mod.Field<Fp12> & Fp12Utils = {
|
|||||||
let t9 = Fp2.mulByNonresidue(t8); // T8 * (u + 1)
|
let t9 = Fp2.mulByNonresidue(t8); // T8 * (u + 1)
|
||||||
return {
|
return {
|
||||||
c0: Fp6.create({
|
c0: Fp6.create({
|
||||||
c0: Fp2.add(Fp2.mul(Fp2.sub(t3, c0c0), 2n), t3), // 2 * (T3 - c0c0) + T3
|
c0: Fp2.add(Fp2.mul(Fp2.sub(t3, c0c0), _2n), t3), // 2 * (T3 - c0c0) + T3
|
||||||
c1: Fp2.add(Fp2.mul(Fp2.sub(t5, c0c1), 2n), t5), // 2 * (T5 - c0c1) + T5
|
c1: Fp2.add(Fp2.mul(Fp2.sub(t5, c0c1), _2n), t5), // 2 * (T5 - c0c1) + T5
|
||||||
c2: Fp2.add(Fp2.mul(Fp2.sub(t7, c0c2), 2n), t7),
|
c2: Fp2.add(Fp2.mul(Fp2.sub(t7, c0c2), _2n), t7),
|
||||||
}), // 2 * (T7 - c0c2) + T7
|
}), // 2 * (T7 - c0c2) + T7
|
||||||
c1: Fp6.create({
|
c1: Fp6.create({
|
||||||
c0: Fp2.add(Fp2.mul(Fp2.add(t9, c1c0), 2n), t9), // 2 * (T9 + c1c0) + T9
|
c0: Fp2.add(Fp2.mul(Fp2.add(t9, c1c0), _2n), t9), // 2 * (T9 + c1c0) + T9
|
||||||
c1: Fp2.add(Fp2.mul(Fp2.add(t4, c1c1), 2n), t4), // 2 * (T4 + c1c1) + T4
|
c1: Fp2.add(Fp2.mul(Fp2.add(t4, c1c1), _2n), t4), // 2 * (T4 + c1c1) + T4
|
||||||
c2: Fp2.add(Fp2.mul(Fp2.add(t6, c1c2), 2n), t6),
|
c2: Fp2.add(Fp2.mul(Fp2.add(t6, c1c2), _2n), t6),
|
||||||
}),
|
}),
|
||||||
}; // 2 * (T6 + c1c2) + T6
|
}; // 2 * (T6 + c1c2) + T6
|
||||||
},
|
},
|
||||||
@@ -654,58 +698,91 @@ const Fp12: mod.Field<Fp12> & Fp12Utils = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
const FP12_FROBENIUS_COEFFICIENTS = [
|
const FP12_FROBENIUS_COEFFICIENTS = [
|
||||||
[0x1n, 0x0n],
|
[BigInt('0x1'), BigInt('0x0')],
|
||||||
[
|
[
|
||||||
0x1904d3bf02bb0667c231beb4202c0d1f0fd603fd3cbd5f4f7b2443d784bab9c4f67ea53d63e7813d8d0775ed92235fb8n,
|
BigInt(
|
||||||
0x00fc3e2b36c4e03288e9e902231f9fb854a14787b6c7b36fec0c8ec971f63c5f282d5ac14d6c7ec22cf78a126ddc4af3n,
|
'0x1904d3bf02bb0667c231beb4202c0d1f0fd603fd3cbd5f4f7b2443d784bab9c4f67ea53d63e7813d8d0775ed92235fb8'
|
||||||
|
),
|
||||||
|
BigInt(
|
||||||
|
'0x00fc3e2b36c4e03288e9e902231f9fb854a14787b6c7b36fec0c8ec971f63c5f282d5ac14d6c7ec22cf78a126ddc4af3'
|
||||||
|
),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffeffffn,
|
BigInt(
|
||||||
0x0n,
|
'0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffeffff'
|
||||||
|
),
|
||||||
|
BigInt('0x0'),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
0x135203e60180a68ee2e9c448d77a2cd91c3dedd930b1cf60ef396489f61eb45e304466cf3e67fa0af1ee7b04121bdea2n,
|
BigInt(
|
||||||
0x06af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09n,
|
'0x135203e60180a68ee2e9c448d77a2cd91c3dedd930b1cf60ef396489f61eb45e304466cf3e67fa0af1ee7b04121bdea2'
|
||||||
|
),
|
||||||
|
BigInt(
|
||||||
|
'0x06af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09'
|
||||||
|
),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffen,
|
BigInt(
|
||||||
0x0n,
|
'0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe'
|
||||||
|
),
|
||||||
|
BigInt('0x0'),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
0x144e4211384586c16bd3ad4afa99cc9170df3560e77982d0db45f3536814f0bd5871c1908bd478cd1ee605167ff82995n,
|
BigInt(
|
||||||
0x05b2cfd9013a5fd8df47fa6b48b1e045f39816240c0b8fee8beadf4d8e9c0566c63a3e6e257f87329b18fae980078116n,
|
'0x144e4211384586c16bd3ad4afa99cc9170df3560e77982d0db45f3536814f0bd5871c1908bd478cd1ee605167ff82995'
|
||||||
|
),
|
||||||
|
BigInt(
|
||||||
|
'0x05b2cfd9013a5fd8df47fa6b48b1e045f39816240c0b8fee8beadf4d8e9c0566c63a3e6e257f87329b18fae980078116'
|
||||||
|
),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaan,
|
BigInt(
|
||||||
0x0n,
|
'0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa'
|
||||||
|
),
|
||||||
|
BigInt('0x0'),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
0x00fc3e2b36c4e03288e9e902231f9fb854a14787b6c7b36fec0c8ec971f63c5f282d5ac14d6c7ec22cf78a126ddc4af3n,
|
BigInt(
|
||||||
0x1904d3bf02bb0667c231beb4202c0d1f0fd603fd3cbd5f4f7b2443d784bab9c4f67ea53d63e7813d8d0775ed92235fb8n,
|
'0x00fc3e2b36c4e03288e9e902231f9fb854a14787b6c7b36fec0c8ec971f63c5f282d5ac14d6c7ec22cf78a126ddc4af3'
|
||||||
|
),
|
||||||
|
BigInt(
|
||||||
|
'0x1904d3bf02bb0667c231beb4202c0d1f0fd603fd3cbd5f4f7b2443d784bab9c4f67ea53d63e7813d8d0775ed92235fb8'
|
||||||
|
),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaacn,
|
BigInt(
|
||||||
0x0n,
|
'0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac'
|
||||||
|
),
|
||||||
|
BigInt('0x0'),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
0x06af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09n,
|
BigInt(
|
||||||
0x135203e60180a68ee2e9c448d77a2cd91c3dedd930b1cf60ef396489f61eb45e304466cf3e67fa0af1ee7b04121bdea2n,
|
'0x06af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09'
|
||||||
|
),
|
||||||
|
BigInt(
|
||||||
|
'0x135203e60180a68ee2e9c448d77a2cd91c3dedd930b1cf60ef396489f61eb45e304466cf3e67fa0af1ee7b04121bdea2'
|
||||||
|
),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaadn,
|
BigInt(
|
||||||
0x0n,
|
'0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaad'
|
||||||
|
),
|
||||||
|
BigInt('0x0'),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
0x05b2cfd9013a5fd8df47fa6b48b1e045f39816240c0b8fee8beadf4d8e9c0566c63a3e6e257f87329b18fae980078116n,
|
BigInt(
|
||||||
0x144e4211384586c16bd3ad4afa99cc9170df3560e77982d0db45f3536814f0bd5871c1908bd478cd1ee605167ff82995n,
|
'0x05b2cfd9013a5fd8df47fa6b48b1e045f39816240c0b8fee8beadf4d8e9c0566c63a3e6e257f87329b18fae980078116'
|
||||||
|
),
|
||||||
|
BigInt(
|
||||||
|
'0x144e4211384586c16bd3ad4afa99cc9170df3560e77982d0db45f3536814f0bd5871c1908bd478cd1ee605167ff82995'
|
||||||
|
),
|
||||||
],
|
],
|
||||||
].map((n) => Fp2.fromBigTuple(n));
|
].map((n) => Fp2.fromBigTuple(n));
|
||||||
// END OF CURVE FIELDS
|
// END OF CURVE FIELDS
|
||||||
|
|
||||||
// HashToCurve
|
// HashToCurve
|
||||||
|
|
||||||
// 3-isogeny map from E' to E
|
// 3-isogeny map from E' to E https://www.rfc-editor.org/rfc/rfc9380#appendix-E.3
|
||||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-E.3
|
|
||||||
const isogenyMapG2 = isogenyMap(
|
const isogenyMapG2 = isogenyMap(
|
||||||
Fp2,
|
Fp2,
|
||||||
[
|
[
|
||||||
@@ -853,19 +930,23 @@ const isogenyMapG1 = isogenyMap(
|
|||||||
|
|
||||||
// SWU Map - Fp2 to G2': y² = x³ + 240i * x + 1012 + 1012i
|
// SWU Map - Fp2 to G2': y² = x³ + 240i * x + 1012 + 1012i
|
||||||
const G2_SWU = mapToCurveSimpleSWU(Fp2, {
|
const G2_SWU = mapToCurveSimpleSWU(Fp2, {
|
||||||
A: Fp2.create({ c0: Fp.create(0n), c1: Fp.create(240n) }), // A' = 240 * I
|
A: Fp2.create({ c0: Fp.create(_0n), c1: Fp.create(BigInt(240)) }), // A' = 240 * I
|
||||||
B: Fp2.create({ c0: Fp.create(1012n), c1: Fp.create(1012n) }), // B' = 1012 * (1 + I)
|
B: Fp2.create({ c0: Fp.create(BigInt(1012)), c1: Fp.create(BigInt(1012)) }), // B' = 1012 * (1 + I)
|
||||||
Z: Fp2.create({ c0: Fp.create(-2n), c1: Fp.create(-1n) }), // Z: -(2 + I)
|
Z: Fp2.create({ c0: Fp.create(BigInt(-2)), c1: Fp.create(BigInt(-1)) }), // Z: -(2 + I)
|
||||||
});
|
});
|
||||||
// Optimized SWU Map - Fp to G1
|
// Optimized SWU Map - Fp to G1
|
||||||
const G1_SWU = mapToCurveSimpleSWU(Fp, {
|
const G1_SWU = mapToCurveSimpleSWU(Fp, {
|
||||||
A: Fp.create(
|
A: Fp.create(
|
||||||
0x144698a3b8e9433d693a02c96d4982b0ea985383ee66a8d8e8981aefd881ac98936f8da0e0f97f5cf428082d584c1dn
|
BigInt(
|
||||||
|
'0x144698a3b8e9433d693a02c96d4982b0ea985383ee66a8d8e8981aefd881ac98936f8da0e0f97f5cf428082d584c1d'
|
||||||
|
)
|
||||||
),
|
),
|
||||||
B: Fp.create(
|
B: Fp.create(
|
||||||
0x12e2908d11688030018b12e8753eee3b2016c1f0f24f4070a0b9c14fcef35ef55a23215a316ceaa5d1cc48e98e172be0n
|
BigInt(
|
||||||
|
'0x12e2908d11688030018b12e8753eee3b2016c1f0f24f4070a0b9c14fcef35ef55a23215a316ceaa5d1cc48e98e172be0'
|
||||||
|
)
|
||||||
),
|
),
|
||||||
Z: Fp.create(11n),
|
Z: Fp.create(BigInt(11)),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Endomorphisms (for fast cofactor clearing)
|
// Endomorphisms (for fast cofactor clearing)
|
||||||
@@ -888,8 +969,9 @@ function G2psi(c: ProjConstructor<Fp2>, P: ProjPointType<Fp2>) {
|
|||||||
}
|
}
|
||||||
// Ψ²(P) endomorphism
|
// Ψ²(P) endomorphism
|
||||||
// 1 / F2(2)^((p-1)/3) in GF(p²)
|
// 1 / F2(2)^((p-1)/3) in GF(p²)
|
||||||
const PSI2_C1 =
|
const PSI2_C1 = BigInt(
|
||||||
0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaacn;
|
'0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac'
|
||||||
|
);
|
||||||
|
|
||||||
function psi2(x: Fp2, y: Fp2): [Fp2, Fp2] {
|
function psi2(x: Fp2, y: Fp2): [Fp2, Fp2] {
|
||||||
return [Fp2.mul(x, PSI2_C1), Fp2.neg(y)];
|
return [Fp2.mul(x, PSI2_C1), Fp2.neg(y)];
|
||||||
@@ -904,13 +986,13 @@ function G2psi2(c: ProjConstructor<Fp2>, P: ProjPointType<Fp2>) {
|
|||||||
//
|
//
|
||||||
// Parameter definitions are in section 5.3 of the spec unless otherwise noted.
|
// Parameter definitions are in section 5.3 of the spec unless otherwise noted.
|
||||||
// Parameter values come from section 8.8.2 of the spec.
|
// Parameter values come from section 8.8.2 of the spec.
|
||||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-8.8.2
|
// https://www.rfc-editor.org/rfc/rfc9380#section-8.8.2
|
||||||
//
|
//
|
||||||
// Base field F is GF(p^m)
|
// Base field F is GF(p^m)
|
||||||
// p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab
|
// p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab
|
||||||
// m = 2 (or 1 for G1 see section 8.8.1)
|
// m = 2 (or 1 for G1 see section 8.8.1)
|
||||||
// k = 128
|
// k = 128
|
||||||
const htfDefaults = {
|
const htfDefaults = Object.freeze({
|
||||||
// DST: a domain separation tag
|
// DST: a domain separation tag
|
||||||
// defined in section 2.2.5
|
// defined in section 2.2.5
|
||||||
// Use utils.getDSTLabel(), utils.setDSTLabel(value)
|
// Use utils.getDSTLabel(), utils.setDSTLabel(value)
|
||||||
@@ -932,18 +1014,66 @@ const htfDefaults = {
|
|||||||
// wide range of hash functions, including SHA-2, SHA-3, BLAKE2, and others.
|
// wide range of hash functions, including SHA-2, SHA-3, BLAKE2, and others.
|
||||||
// BBS+ uses blake2: https://github.com/hyperledger/aries-framework-go/issues/2247
|
// BBS+ uses blake2: https://github.com/hyperledger/aries-framework-go/issues/2247
|
||||||
hash: sha256,
|
hash: sha256,
|
||||||
} as const;
|
} as const);
|
||||||
|
|
||||||
// Encoding utils
|
// Encoding utils
|
||||||
// Point on G1 curve: (x, y)
|
// Point on G1 curve: (x, y)
|
||||||
const C_BIT_POS = Fp.BITS; // C_bit, compression bit for serialization flag
|
|
||||||
const I_BIT_POS = Fp.BITS + 1; // I_bit, point-at-infinity bit for serialization flag
|
|
||||||
const S_BIT_POS = Fp.BITS + 2; // S_bit, sign bit for serialization flag
|
|
||||||
// Compressed point of infinity
|
// Compressed point of infinity
|
||||||
const COMPRESSED_ZERO = Fp.toBytes(bitSet(bitSet(0n, I_BIT_POS, true), S_BIT_POS, true)); // set compressed & point-at-infinity bits
|
const COMPRESSED_ZERO = setMask(Fp.toBytes(_0n), { infinity: true, compressed: true }); // set compressed & point-at-infinity bits
|
||||||
|
|
||||||
|
function parseMask(bytes: Uint8Array) {
|
||||||
|
// Copy, so we can remove mask data. It will be removed also later, when Fp.create will call modulo.
|
||||||
|
bytes = bytes.slice();
|
||||||
|
const mask = bytes[0] & 0b1110_0000;
|
||||||
|
const compressed = !!((mask >> 7) & 1); // compression bit (0b1000_0000)
|
||||||
|
const infinity = !!((mask >> 6) & 1); // point at infinity bit (0b0100_0000)
|
||||||
|
const sort = !!((mask >> 5) & 1); // sort bit (0b0010_0000)
|
||||||
|
bytes[0] &= 0b0001_1111; // clear mask (zero first 3 bits)
|
||||||
|
return { compressed, infinity, sort, value: bytes };
|
||||||
|
}
|
||||||
|
|
||||||
|
function setMask(
|
||||||
|
bytes: Uint8Array,
|
||||||
|
mask: { compressed?: boolean; infinity?: boolean; sort?: boolean }
|
||||||
|
) {
|
||||||
|
if (bytes[0] & 0b1110_0000) throw new Error('setMask: non-empty mask');
|
||||||
|
if (mask.compressed) bytes[0] |= 0b1000_0000;
|
||||||
|
if (mask.infinity) bytes[0] |= 0b0100_0000;
|
||||||
|
if (mask.sort) bytes[0] |= 0b0010_0000;
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
function signatureG1ToRawBytes(point: ProjPointType<Fp>) {
|
||||||
|
point.assertValidity();
|
||||||
|
const isZero = point.equals(bls12_381.G1.ProjectivePoint.ZERO);
|
||||||
|
const { x, y } = point.toAffine();
|
||||||
|
if (isZero) return COMPRESSED_ZERO.slice();
|
||||||
|
const P = Fp.ORDER;
|
||||||
|
const sort = Boolean((y * _2n) / P);
|
||||||
|
return setMask(numberToBytesBE(x, Fp.BYTES), { compressed: true, sort });
|
||||||
|
}
|
||||||
|
|
||||||
|
function signatureG2ToRawBytes(point: ProjPointType<Fp2>) {
|
||||||
|
// NOTE: by some reasons it was missed in bls12-381, looks like bug
|
||||||
|
point.assertValidity();
|
||||||
|
const len = Fp.BYTES;
|
||||||
|
if (point.equals(bls12_381.G2.ProjectivePoint.ZERO))
|
||||||
|
return concatB(COMPRESSED_ZERO, numberToBytesBE(_0n, len));
|
||||||
|
const { x, y } = point.toAffine();
|
||||||
|
const { re: x0, im: x1 } = Fp2.reim(x);
|
||||||
|
const { re: y0, im: y1 } = Fp2.reim(y);
|
||||||
|
const tmp = y1 > _0n ? y1 * _2n : y0 * _2n;
|
||||||
|
const sort = Boolean((tmp / Fp.ORDER) & _1n);
|
||||||
|
const z2 = x0;
|
||||||
|
return concatB(
|
||||||
|
setMask(numberToBytesBE(x1, len), { sort, compressed: true }),
|
||||||
|
numberToBytesBE(z2, len)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// To verify curve parameters, see pairing-friendly-curves spec:
|
// To verify curve parameters, see pairing-friendly-curves spec:
|
||||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-pairing-friendly-curves-09
|
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-pairing-friendly-curves-11
|
||||||
// Basic math is done over finite fields over p.
|
// Basic math is done over finite fields over p.
|
||||||
// More complicated math is done over polynominal extension fields.
|
// More complicated math is done over polynominal extension fields.
|
||||||
// To simplify calculations in Fp12, we construct extension tower:
|
// To simplify calculations in Fp12, we construct extension tower:
|
||||||
@@ -954,27 +1084,31 @@ const COMPRESSED_ZERO = Fp.toBytes(bitSet(bitSet(0n, I_BIT_POS, true), S_BIT_POS
|
|||||||
// Here goes constants && point encoding format
|
// Here goes constants && point encoding format
|
||||||
export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
||||||
// Fields
|
// Fields
|
||||||
Fr,
|
fields: {
|
||||||
Fp,
|
Fp,
|
||||||
Fp2,
|
Fp2,
|
||||||
Fp6,
|
Fp6,
|
||||||
Fp12,
|
Fp12,
|
||||||
// order; z⁴ − z² + 1
|
Fr,
|
||||||
r: Fr.ORDER, // Same as N in other curves
|
},
|
||||||
// G1 is the order-q subgroup of E1(Fp) : y² = x³ + 4, #E1(Fp) = h1q, where
|
// G1 is the order-q subgroup of E1(Fp) : y² = x³ + 4, #E1(Fp) = h1q, where
|
||||||
// characteristic; z + (z⁴ - z² + 1)(z - 1)²/3
|
// characteristic; z + (z⁴ - z² + 1)(z - 1)²/3
|
||||||
G1: {
|
G1: {
|
||||||
Fp,
|
Fp,
|
||||||
// cofactor; (z - 1)²/3
|
// cofactor; (z - 1)²/3
|
||||||
h: 0x396c8c005555e1568c00aaab0000aaabn,
|
h: BigInt('0x396c8c005555e1568c00aaab0000aaab'),
|
||||||
// generator's coordinates
|
// generator's coordinates
|
||||||
// x = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507
|
// x = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507
|
||||||
// y = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569
|
// y = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569
|
||||||
Gx: 0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bbn,
|
Gx: BigInt(
|
||||||
Gy: 0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1n,
|
'0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb'
|
||||||
|
),
|
||||||
|
Gy: BigInt(
|
||||||
|
'0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1'
|
||||||
|
),
|
||||||
a: Fp.ZERO,
|
a: Fp.ZERO,
|
||||||
b: 4n,
|
b: _4n,
|
||||||
htfDefaults: { ...htfDefaults, m: 1 },
|
htfDefaults: { ...htfDefaults, m: 1, DST: 'BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_NUL_' },
|
||||||
wrapPrivateKey: true,
|
wrapPrivateKey: true,
|
||||||
allowInfinityPoint: true,
|
allowInfinityPoint: true,
|
||||||
// Checks is the point resides in prime-order subgroup.
|
// Checks is the point resides in prime-order subgroup.
|
||||||
@@ -983,18 +1117,19 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|||||||
// https://eprint.iacr.org/2021/1130.pdf
|
// https://eprint.iacr.org/2021/1130.pdf
|
||||||
isTorsionFree: (c, point): boolean => {
|
isTorsionFree: (c, point): boolean => {
|
||||||
// φ endomorphism
|
// φ endomorphism
|
||||||
const cubicRootOfUnityModP =
|
const cubicRootOfUnityModP = BigInt(
|
||||||
0x5f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffen;
|
'0x5f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe'
|
||||||
|
);
|
||||||
const phi = new c(Fp.mul(point.px, cubicRootOfUnityModP), point.py, point.pz);
|
const phi = new c(Fp.mul(point.px, cubicRootOfUnityModP), point.py, point.pz);
|
||||||
|
|
||||||
// todo: unroll
|
// todo: unroll
|
||||||
const xP = point.multiplyUnsafe(bls12_381.CURVE.x).negate(); // [x]P
|
const xP = point.multiplyUnsafe(bls12_381.params.x).negate(); // [x]P
|
||||||
const u2P = xP.multiplyUnsafe(bls12_381.CURVE.x); // [u2]P
|
const u2P = xP.multiplyUnsafe(bls12_381.params.x); // [u2]P
|
||||||
return u2P.equals(phi);
|
return u2P.equals(phi);
|
||||||
|
|
||||||
// https://eprint.iacr.org/2019/814.pdf
|
// https://eprint.iacr.org/2019/814.pdf
|
||||||
// (z² − 1)/3
|
// (z² − 1)/3
|
||||||
// const c1 = 0x396c8c005555e1560000000055555555n;
|
// const c1 = BigInt('0x396c8c005555e1560000000055555555');
|
||||||
// const P = this;
|
// const P = this;
|
||||||
// const S = P.sigma();
|
// const S = P.sigma();
|
||||||
// const Q = S.double();
|
// const Q = S.double();
|
||||||
@@ -1006,33 +1141,39 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|||||||
},
|
},
|
||||||
// Clear cofactor of G1
|
// Clear cofactor of G1
|
||||||
// https://eprint.iacr.org/2019/403
|
// https://eprint.iacr.org/2019/403
|
||||||
clearCofactor: (c, point) => {
|
clearCofactor: (_c, point) => {
|
||||||
// return this.multiplyUnsafe(CURVE.h);
|
// return this.multiplyUnsafe(CURVE.h);
|
||||||
return point.multiplyUnsafe(bls12_381.CURVE.x).add(point); // x*P + P
|
return point.multiplyUnsafe(bls12_381.params.x).add(point); // x*P + P
|
||||||
},
|
},
|
||||||
mapToCurve: (scalars: bigint[]) => {
|
mapToCurve: (scalars: bigint[]) => {
|
||||||
const { x, y } = G1_SWU(Fp.create(scalars[0]));
|
const { x, y } = G1_SWU(Fp.create(scalars[0]));
|
||||||
return isogenyMapG1(x, y);
|
return isogenyMapG1(x, y);
|
||||||
},
|
},
|
||||||
fromBytes: (bytes: Uint8Array): AffinePoint<Fp> => {
|
fromBytes: (bytes: Uint8Array): AffinePoint<Fp> => {
|
||||||
if (bytes.length === 48) {
|
const { compressed, infinity, sort, value } = parseMask(bytes);
|
||||||
|
if (value.length === 48 && compressed) {
|
||||||
|
// TODO: Fp.bytes
|
||||||
const P = Fp.ORDER;
|
const P = Fp.ORDER;
|
||||||
const compressedValue = bytesToNumberBE(bytes);
|
const compressedValue = bytesToNumberBE(value);
|
||||||
const bflag = bitGet(compressedValue, I_BIT_POS);
|
|
||||||
// Zero
|
// Zero
|
||||||
if (bflag === 1n) return { x: 0n, y: 0n };
|
|
||||||
const x = Fp.create(compressedValue & Fp.MASK);
|
const x = Fp.create(compressedValue & Fp.MASK);
|
||||||
const right = Fp.add(Fp.pow(x, 3n), Fp.create(bls12_381.CURVE.G1.b)); // y² = x³ + b
|
if (infinity) {
|
||||||
|
if (x !== _0n) throw new Error('G1: non-empty compressed point at infinity');
|
||||||
|
return { x: _0n, y: _0n };
|
||||||
|
}
|
||||||
|
const right = Fp.add(Fp.pow(x, _3n), Fp.create(bls12_381.params.G1b)); // y² = x³ + b
|
||||||
let y = Fp.sqrt(right);
|
let y = Fp.sqrt(right);
|
||||||
if (!y) throw new Error('Invalid compressed G1 point');
|
if (!y) throw new Error('Invalid compressed G1 point');
|
||||||
const aflag = bitGet(compressedValue, C_BIT_POS);
|
if ((y * _2n) / P !== BigInt(sort)) y = Fp.neg(y);
|
||||||
if ((y * 2n) / P !== aflag) y = Fp.neg(y);
|
|
||||||
return { x: Fp.create(x), y: Fp.create(y) };
|
return { x: Fp.create(x), y: Fp.create(y) };
|
||||||
} else if (bytes.length === 96) {
|
} else if (value.length === 96 && !compressed) {
|
||||||
// Check if the infinity flag is set
|
// Check if the infinity flag is set
|
||||||
if ((bytes[0] & (1 << 6)) !== 0) return bls12_381.G1.ProjectivePoint.ZERO.toAffine();
|
const x = bytesToNumberBE(value.subarray(0, Fp.BYTES));
|
||||||
const x = bytesToNumberBE(bytes.slice(0, Fp.BYTES));
|
const y = bytesToNumberBE(value.subarray(Fp.BYTES));
|
||||||
const y = bytesToNumberBE(bytes.slice(Fp.BYTES));
|
if (infinity) {
|
||||||
|
if (x !== _0n || y !== _0n) throw new Error('G1: non-empty point at infinity');
|
||||||
|
return bls12_381.G1.ProjectivePoint.ZERO.toAffine();
|
||||||
|
}
|
||||||
return { x: Fp.create(x), y: Fp.create(y) };
|
return { x: Fp.create(x), y: Fp.create(y) };
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid point G1, expected 48/96 bytes');
|
throw new Error('Invalid point G1, expected 48/96 bytes');
|
||||||
@@ -1044,10 +1185,8 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|||||||
if (isCompressed) {
|
if (isCompressed) {
|
||||||
if (isZero) return COMPRESSED_ZERO.slice();
|
if (isZero) return COMPRESSED_ZERO.slice();
|
||||||
const P = Fp.ORDER;
|
const P = Fp.ORDER;
|
||||||
let num;
|
const sort = Boolean((y * _2n) / P);
|
||||||
num = bitSet(x, C_BIT_POS, Boolean((y * 2n) / P)); // set aflag
|
return setMask(numberToBytesBE(x, Fp.BYTES), { compressed: true, sort });
|
||||||
num = bitSet(num, S_BIT_POS, true);
|
|
||||||
return numberToBytesBE(num, Fp.BYTES);
|
|
||||||
} else {
|
} else {
|
||||||
if (isZero) {
|
if (isZero) {
|
||||||
// 2x PUBLIC_KEY_LENGTH
|
// 2x PUBLIC_KEY_LENGTH
|
||||||
@@ -1058,6 +1197,30 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
ShortSignature: {
|
||||||
|
fromHex(hex: Hex): ProjPointType<Fp> {
|
||||||
|
const { infinity, sort, value } = parseMask(ensureBytes('signatureHex', hex, 48));
|
||||||
|
const P = Fp.ORDER;
|
||||||
|
const compressedValue = bytesToNumberBE(value);
|
||||||
|
// Zero
|
||||||
|
if (infinity) return bls12_381.G1.ProjectivePoint.ZERO;
|
||||||
|
const x = Fp.create(compressedValue & Fp.MASK);
|
||||||
|
const right = Fp.add(Fp.pow(x, _3n), Fp.create(bls12_381.params.G1b)); // y² = x³ + b
|
||||||
|
let y = Fp.sqrt(right);
|
||||||
|
if (!y) throw new Error('Invalid compressed G1 point');
|
||||||
|
const aflag = BigInt(sort);
|
||||||
|
if ((y * _2n) / P !== aflag) y = Fp.neg(y);
|
||||||
|
const point = bls12_381.G1.ProjectivePoint.fromAffine({ x, y });
|
||||||
|
point.assertValidity();
|
||||||
|
return point;
|
||||||
|
},
|
||||||
|
toRawBytes(point: ProjPointType<Fp>) {
|
||||||
|
return signatureG1ToRawBytes(point);
|
||||||
|
},
|
||||||
|
toHex(point: ProjPointType<Fp>) {
|
||||||
|
return bytesToHex(signatureG1ToRawBytes(point));
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
// G2 is the order-q subgroup of E2(Fp²) : y² = x³+4(1+√−1),
|
// G2 is the order-q subgroup of E2(Fp²) : y² = x³+4(1+√−1),
|
||||||
// where Fp2 is Fp[√−1]/(x2+1). #E2(Fp2 ) = h2q, where
|
// where Fp2 is Fp[√−1]/(x2+1). #E2(Fp2 ) = h2q, where
|
||||||
@@ -1066,21 +1229,33 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|||||||
G2: {
|
G2: {
|
||||||
Fp: Fp2,
|
Fp: Fp2,
|
||||||
// cofactor
|
// cofactor
|
||||||
h: 0x5d543a95414e7f1091d50792876a202cd91de4547085abaa68a205b2e5a7ddfa628f1cb4d9e82ef21537e293a6691ae1616ec6e786f0c70cf1c38e31c7238e5n,
|
h: BigInt(
|
||||||
|
'0x5d543a95414e7f1091d50792876a202cd91de4547085abaa68a205b2e5a7ddfa628f1cb4d9e82ef21537e293a6691ae1616ec6e786f0c70cf1c38e31c7238e5'
|
||||||
|
),
|
||||||
Gx: Fp2.fromBigTuple([
|
Gx: Fp2.fromBigTuple([
|
||||||
0x024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8n,
|
BigInt(
|
||||||
0x13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7en,
|
'0x024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8'
|
||||||
|
),
|
||||||
|
BigInt(
|
||||||
|
'0x13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e'
|
||||||
|
),
|
||||||
]),
|
]),
|
||||||
// y =
|
// y =
|
||||||
// 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582,
|
// 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582,
|
||||||
// 1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905
|
// 1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905
|
||||||
Gy: Fp2.fromBigTuple([
|
Gy: Fp2.fromBigTuple([
|
||||||
0x0ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801n,
|
BigInt(
|
||||||
0x0606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79ben,
|
'0x0ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801'
|
||||||
|
),
|
||||||
|
BigInt(
|
||||||
|
'0x0606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be'
|
||||||
|
),
|
||||||
]),
|
]),
|
||||||
a: Fp2.ZERO,
|
a: Fp2.ZERO,
|
||||||
b: Fp2.fromBigTuple([4n, 4n]),
|
b: Fp2.fromBigTuple([_4n, _4n]),
|
||||||
hEff: 0xbc69f08f2ee75b3584c6a0ea91b352888e2a8e9145ad7689986ff031508ffe1329c2f178731db956d82bf015d1212b02ec0ec69d7477c1ae954cbc06689f6a359894c0adebbf6b4e8020005aaa95551n,
|
hEff: BigInt(
|
||||||
|
'0xbc69f08f2ee75b3584c6a0ea91b352888e2a8e9145ad7689986ff031508ffe1329c2f178731db956d82bf015d1212b02ec0ec69d7477c1ae954cbc06689f6a359894c0adebbf6b4e8020005aaa95551'
|
||||||
|
),
|
||||||
htfDefaults: { ...htfDefaults },
|
htfDefaults: { ...htfDefaults },
|
||||||
wrapPrivateKey: true,
|
wrapPrivateKey: true,
|
||||||
allowInfinityPoint: true,
|
allowInfinityPoint: true,
|
||||||
@@ -1093,7 +1268,7 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|||||||
// It returns false for shitty points.
|
// It returns false for shitty points.
|
||||||
// https://eprint.iacr.org/2021/1130.pdf
|
// https://eprint.iacr.org/2021/1130.pdf
|
||||||
isTorsionFree: (c, P): boolean => {
|
isTorsionFree: (c, P): boolean => {
|
||||||
return P.multiplyUnsafe(bls12_381.CURVE.x).negate().equals(G2psi(c, P)); // ψ(P) == [u](P)
|
return P.multiplyUnsafe(bls12_381.params.x).negate().equals(G2psi(c, P)); // ψ(P) == [u](P)
|
||||||
// Older version: https://eprint.iacr.org/2019/814.pdf
|
// Older version: https://eprint.iacr.org/2019/814.pdf
|
||||||
// Ψ²(P) => Ψ³(P) => [z]Ψ³(P) where z = -x => [z]Ψ³(P) - Ψ²(P) + P == O
|
// Ψ²(P) => Ψ³(P) => [z]Ψ³(P) where z = -x => [z]Ψ³(P) - Ψ²(P) + P == O
|
||||||
// return P.psi2().psi().mulNegX().subtract(psi2).add(P).isZero();
|
// return P.psi2().psi().mulNegX().subtract(psi2).add(P).isZero();
|
||||||
@@ -1103,7 +1278,7 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|||||||
// https://eprint.iacr.org/2017/419.pdf
|
// https://eprint.iacr.org/2017/419.pdf
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
clearCofactor: (c, P) => {
|
clearCofactor: (c, P) => {
|
||||||
const { x } = bls12_381.CURVE;
|
const x = bls12_381.params.x;
|
||||||
let t1 = P.multiplyUnsafe(x).negate(); // [-x]P
|
let t1 = P.multiplyUnsafe(x).negate(); // [-x]P
|
||||||
let t2 = G2psi(c, P); // Ψ(P)
|
let t2 = G2psi(c, P); // Ψ(P)
|
||||||
let t3 = P.double(); // 2P
|
let t3 = P.double(); // 2P
|
||||||
@@ -1117,90 +1292,89 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|||||||
return Q; // [x²-x-1]P + [x-1]Ψ(P) + Ψ²(2P)
|
return Q; // [x²-x-1]P + [x-1]Ψ(P) + Ψ²(2P)
|
||||||
},
|
},
|
||||||
fromBytes: (bytes: Uint8Array): AffinePoint<Fp2> => {
|
fromBytes: (bytes: Uint8Array): AffinePoint<Fp2> => {
|
||||||
const m_byte = bytes[0] & 0xe0;
|
const { compressed, infinity, sort, value } = parseMask(bytes);
|
||||||
if (m_byte === 0x20 || m_byte === 0x60 || m_byte === 0xe0) {
|
if (
|
||||||
throw new Error('Invalid encoding flag: ' + m_byte);
|
(!compressed && !infinity && sort) || // 00100000
|
||||||
|
(!compressed && infinity && sort) || // 01100000
|
||||||
|
(sort && infinity && compressed) // 11100000
|
||||||
|
) {
|
||||||
|
throw new Error('Invalid encoding flag: ' + (bytes[0] & 0b1110_0000));
|
||||||
}
|
}
|
||||||
const bitC = m_byte & 0x80; // compression bit
|
|
||||||
const bitI = m_byte & 0x40; // point at infinity bit
|
|
||||||
const bitS = m_byte & 0x20; // sign bit
|
|
||||||
const L = Fp.BYTES;
|
const L = Fp.BYTES;
|
||||||
const slc = (b: Uint8Array, from: number, to?: number) => bytesToNumberBE(b.slice(from, to));
|
const slc = (b: Uint8Array, from: number, to?: number) => bytesToNumberBE(b.slice(from, to));
|
||||||
if (bytes.length === 96 && bitC) {
|
if (value.length === 96 && compressed) {
|
||||||
const { b } = bls12_381.CURVE.G2;
|
const b = bls12_381.params.G2b;
|
||||||
const P = Fp.ORDER;
|
const P = Fp.ORDER;
|
||||||
|
if (infinity) {
|
||||||
bytes[0] = bytes[0] & 0x1f; // clear flags
|
|
||||||
if (bitI) {
|
|
||||||
// check that all bytes are 0
|
// check that all bytes are 0
|
||||||
if (bytes.reduce((p, c) => (p !== 0 ? c + 1 : c), 0) > 0) {
|
if (value.reduce((p, c) => (p !== 0 ? c + 1 : c), 0) > 0) {
|
||||||
throw new Error('Invalid compressed G2 point');
|
throw new Error('Invalid compressed G2 point');
|
||||||
}
|
}
|
||||||
return { x: Fp2.ZERO, y: Fp2.ZERO };
|
return { x: Fp2.ZERO, y: Fp2.ZERO };
|
||||||
}
|
}
|
||||||
const x_1 = slc(bytes, 0, L);
|
const x_1 = slc(value, 0, L);
|
||||||
const x_0 = slc(bytes, L, 2 * L);
|
const x_0 = slc(value, L, 2 * L);
|
||||||
const x = Fp2.create({ c0: Fp.create(x_0), c1: Fp.create(x_1) });
|
const x = Fp2.create({ c0: Fp.create(x_0), c1: Fp.create(x_1) });
|
||||||
const right = Fp2.add(Fp2.pow(x, 3n), b); // y² = x³ + 4 * (u+1) = x³ + b
|
const right = Fp2.add(Fp2.pow(x, _3n), b); // y² = x³ + 4 * (u+1) = x³ + b
|
||||||
let y = Fp2.sqrt(right);
|
let y = Fp2.sqrt(right);
|
||||||
const Y_bit = y.c1 === 0n ? (y.c0 * 2n) / P : (y.c1 * 2n) / P ? 1n : 0n;
|
const Y_bit = y.c1 === _0n ? (y.c0 * _2n) / P : (y.c1 * _2n) / P ? _1n : _0n;
|
||||||
y = bitS > 0 && Y_bit > 0 ? y : Fp2.neg(y);
|
y = sort && Y_bit > 0 ? y : Fp2.neg(y);
|
||||||
return { x, y };
|
return { x, y };
|
||||||
} else if (bytes.length === 192 && !bitC) {
|
} else if (value.length === 192 && !compressed) {
|
||||||
// Check if the infinity flag is set
|
if (infinity) {
|
||||||
if ((bytes[0] & (1 << 6)) !== 0) {
|
if (value.reduce((p, c) => (p !== 0 ? c + 1 : c), 0) > 0) {
|
||||||
|
throw new Error('Invalid uncompressed G2 point');
|
||||||
|
}
|
||||||
return { x: Fp2.ZERO, y: Fp2.ZERO };
|
return { x: Fp2.ZERO, y: Fp2.ZERO };
|
||||||
}
|
}
|
||||||
const x1 = slc(bytes, 0, L);
|
const x1 = slc(value, 0, L);
|
||||||
const x0 = slc(bytes, L, 2 * L);
|
const x0 = slc(value, L, 2 * L);
|
||||||
const y1 = slc(bytes, 2 * L, 3 * L);
|
const y1 = slc(value, 2 * L, 3 * L);
|
||||||
const y0 = slc(bytes, 3 * L, 4 * L);
|
const y0 = slc(value, 3 * L, 4 * L);
|
||||||
return { x: Fp2.fromBigTuple([x0, x1]), y: Fp2.fromBigTuple([y0, y1]) };
|
return { x: Fp2.fromBigTuple([x0, x1]), y: Fp2.fromBigTuple([y0, y1]) };
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid point G2, expected 96/192 bytes');
|
throw new Error('Invalid point G2, expected 96/192 bytes');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
toBytes: (c, point, isCompressed) => {
|
toBytes: (c, point, isCompressed) => {
|
||||||
|
const { BYTES: len, ORDER: P } = Fp;
|
||||||
const isZero = point.equals(c.ZERO);
|
const isZero = point.equals(c.ZERO);
|
||||||
const { x, y } = point.toAffine();
|
const { x, y } = point.toAffine();
|
||||||
if (isCompressed) {
|
if (isCompressed) {
|
||||||
const P = Fp.ORDER;
|
if (isZero) return concatB(COMPRESSED_ZERO, numberToBytesBE(_0n, len));
|
||||||
if (isZero) return concatB(COMPRESSED_ZERO, numberToBytesBE(0n, Fp.BYTES));
|
const flag = Boolean(y.c1 === _0n ? (y.c0 * _2n) / P : (y.c1 * _2n) / P);
|
||||||
const flag = Boolean(y.c1 === 0n ? (y.c0 * 2n) / P : (y.c1 * 2n) / P);
|
return concatB(
|
||||||
// set compressed & sign bits (looks like different offsets than for G1/Fp?)
|
setMask(numberToBytesBE(x.c1, len), { compressed: true, sort: flag }),
|
||||||
let x_1 = bitSet(x.c1, C_BIT_POS, flag);
|
numberToBytesBE(x.c0, len)
|
||||||
x_1 = bitSet(x_1, S_BIT_POS, true);
|
);
|
||||||
return concatB(numberToBytesBE(x_1, Fp.BYTES), numberToBytesBE(x.c0, Fp.BYTES));
|
|
||||||
} else {
|
} else {
|
||||||
if (isZero) return concatB(new Uint8Array([0x40]), new Uint8Array(4 * Fp.BYTES - 1)); // bytes[0] |= 1 << 6;
|
if (isZero) return concatB(new Uint8Array([0x40]), new Uint8Array(4 * len - 1)); // bytes[0] |= 1 << 6;
|
||||||
const { re: x0, im: x1 } = Fp2.reim(x);
|
const { re: x0, im: x1 } = Fp2.reim(x);
|
||||||
const { re: y0, im: y1 } = Fp2.reim(y);
|
const { re: y0, im: y1 } = Fp2.reim(y);
|
||||||
return concatB(
|
return concatB(
|
||||||
numberToBytesBE(x1, Fp.BYTES),
|
numberToBytesBE(x1, len),
|
||||||
numberToBytesBE(x0, Fp.BYTES),
|
numberToBytesBE(x0, len),
|
||||||
numberToBytesBE(y1, Fp.BYTES),
|
numberToBytesBE(y1, len),
|
||||||
numberToBytesBE(y0, Fp.BYTES)
|
numberToBytesBE(y0, len)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Signature: {
|
Signature: {
|
||||||
// TODO: Optimize, it's very slow because of sqrt.
|
// TODO: Optimize, it's very slow because of sqrt.
|
||||||
decode(hex: Hex): ProjPointType<Fp2> {
|
fromHex(hex: Hex): ProjPointType<Fp2> {
|
||||||
hex = ensureBytes(hex);
|
const { infinity, sort, value } = parseMask(ensureBytes('signatureHex', hex));
|
||||||
const P = Fp.ORDER;
|
const P = Fp.ORDER;
|
||||||
const half = hex.length / 2;
|
const half = value.length / 2;
|
||||||
if (half !== 48 && half !== 96)
|
if (half !== 48 && half !== 96)
|
||||||
throw new Error('Invalid compressed signature length, must be 96 or 192');
|
throw new Error('Invalid compressed signature length, must be 96 or 192');
|
||||||
const z1 = bytesToNumberBE(hex.slice(0, half));
|
const z1 = bytesToNumberBE(value.slice(0, half));
|
||||||
const z2 = bytesToNumberBE(hex.slice(half));
|
const z2 = bytesToNumberBE(value.slice(half));
|
||||||
// Indicates the infinity point
|
// Indicates the infinity point
|
||||||
const bflag1 = bitGet(z1, I_BIT_POS);
|
if (infinity) return bls12_381.G2.ProjectivePoint.ZERO;
|
||||||
if (bflag1 === 1n) return bls12_381.G2.ProjectivePoint.ZERO;
|
|
||||||
|
|
||||||
const x1 = Fp.create(z1 & Fp.MASK);
|
const x1 = Fp.create(z1 & Fp.MASK);
|
||||||
const x2 = Fp.create(z2);
|
const x2 = Fp.create(z2);
|
||||||
const x = Fp2.create({ c0: x2, c1: x1 });
|
const x = Fp2.create({ c0: x2, c1: x1 });
|
||||||
const y2 = Fp2.add(Fp2.pow(x, 3n), bls12_381.CURVE.G2.b); // y² = x³ + 4
|
const y2 = Fp2.add(Fp2.pow(x, _3n), bls12_381.params.G2b); // y² = x³ + 4
|
||||||
// The slow part
|
// The slow part
|
||||||
let y = Fp2.sqrt(y2);
|
let y = Fp2.sqrt(y2);
|
||||||
if (!y) throw new Error('Failed to find a square root');
|
if (!y) throw new Error('Failed to find a square root');
|
||||||
@@ -1208,33 +1382,26 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|||||||
// Choose the y whose leftmost bit of the imaginary part is equal to the a_flag1
|
// Choose the y whose leftmost bit of the imaginary part is equal to the a_flag1
|
||||||
// If y1 happens to be zero, then use the bit of y0
|
// If y1 happens to be zero, then use the bit of y0
|
||||||
const { re: y0, im: y1 } = Fp2.reim(y);
|
const { re: y0, im: y1 } = Fp2.reim(y);
|
||||||
const aflag1 = bitGet(z1, 381);
|
const aflag1 = BigInt(sort);
|
||||||
const isGreater = y1 > 0n && (y1 * 2n) / P !== aflag1;
|
const isGreater = y1 > _0n && (y1 * _2n) / P !== aflag1;
|
||||||
const isZero = y1 === 0n && (y0 * 2n) / P !== aflag1;
|
const isZero = y1 === _0n && (y0 * _2n) / P !== aflag1;
|
||||||
if (isGreater || isZero) y = Fp2.neg(y);
|
if (isGreater || isZero) y = Fp2.neg(y);
|
||||||
const point = bls12_381.G2.ProjectivePoint.fromAffine({ x, y });
|
const point = bls12_381.G2.ProjectivePoint.fromAffine({ x, y });
|
||||||
// console.log('Signature.decode', point);
|
|
||||||
point.assertValidity();
|
point.assertValidity();
|
||||||
return point;
|
return point;
|
||||||
},
|
},
|
||||||
encode(point: ProjPointType<Fp2>) {
|
toRawBytes(point: ProjPointType<Fp2>) {
|
||||||
// NOTE: by some reasons it was missed in bls12-381, looks like bug
|
return signatureG2ToRawBytes(point);
|
||||||
point.assertValidity();
|
},
|
||||||
if (point.equals(bls12_381.G2.ProjectivePoint.ZERO))
|
toHex(point: ProjPointType<Fp2>) {
|
||||||
return concatB(COMPRESSED_ZERO, numberToBytesBE(0n, Fp.BYTES));
|
return bytesToHex(signatureG2ToRawBytes(point));
|
||||||
const a = point.toAffine();
|
|
||||||
const { re: x0, im: x1 } = Fp2.reim(a.x);
|
|
||||||
const { re: y0, im: y1 } = Fp2.reim(a.y);
|
|
||||||
const tmp = y1 > 0n ? y1 * 2n : y0 * 2n;
|
|
||||||
const aflag1 = Boolean((tmp / Fp.ORDER) & 1n);
|
|
||||||
const z1 = bitSet(bitSet(x1, 381, aflag1), S_BIT_POS, true);
|
|
||||||
const z2 = x0;
|
|
||||||
return concatB(numberToBytesBE(z1, Fp.BYTES), numberToBytesBE(z2, Fp.BYTES));
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// The BLS parameter x for BLS12-381
|
params: {
|
||||||
x: BLS_X,
|
x: BLS_X, // The BLS parameter x for BLS12-381
|
||||||
|
r: Fr.ORDER, // order; z⁴ − z² + 1; CURVE.n from other curves
|
||||||
|
},
|
||||||
htfDefaults,
|
htfDefaults,
|
||||||
hash: sha256,
|
hash: sha256,
|
||||||
randomBytes,
|
randomBytes,
|
||||||
|
|||||||
@@ -2,17 +2,18 @@
|
|||||||
import { sha256 } from '@noble/hashes/sha256';
|
import { sha256 } from '@noble/hashes/sha256';
|
||||||
import { weierstrass } from './abstract/weierstrass.js';
|
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.
|
||||||
* Recent research shown it's weaker, the naming has been adjusted to its prime bit count.
|
* Barbulescu-Duquesne 2017 shown it's weaker: just about 100 bits,
|
||||||
* https://github.com/zcash/zcash/issues/2502
|
* so the naming has been adjusted to its prime bit count
|
||||||
|
* https://hal.science/hal-01534101/file/main.pdf
|
||||||
*/
|
*/
|
||||||
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),
|
||||||
173
src/ed25519.ts
173
src/ed25519.ts
@@ -1,18 +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, ExtPointType } from './abstract/edwards.js';
|
import { ExtPointType, twistedEdwards } 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 { Field, FpSqrtEven, isNegativeLE, mod, pow2 } from './abstract/modular.js';
|
||||||
import {
|
import {
|
||||||
ensureBytes,
|
|
||||||
equalBytes,
|
|
||||||
bytesToHex,
|
bytesToHex,
|
||||||
bytesToNumberLE,
|
bytesToNumberLE,
|
||||||
numberToBytesLE,
|
ensureBytes,
|
||||||
|
equalBytes,
|
||||||
Hex,
|
Hex,
|
||||||
|
numberToBytesLE,
|
||||||
} from './abstract/utils.js';
|
} from './abstract/utils.js';
|
||||||
import * as htf from './abstract/hash-to-curve.js';
|
import { createHasher, htfBasicOpts, expand_message_xmd } from './abstract/hash-to-curve.js';
|
||||||
|
import { AffinePoint, Group } from './abstract/curve.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ed25519 Twisted Edwards curve with following addons:
|
* ed25519 Twisted Edwards curve with following addons:
|
||||||
@@ -33,6 +34,7 @@ const ED25519_SQRT_M1 = BigInt(
|
|||||||
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _5n = BigInt(5);
|
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _5n = BigInt(5);
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
const _10n = BigInt(10), _20n = BigInt(20), _40n = BigInt(40), _80n = BigInt(80);
|
const _10n = BigInt(10), _20n = BigInt(20), _40n = BigInt(40), _80n = BigInt(80);
|
||||||
|
|
||||||
function ed25519_pow_2_252_3(x: bigint) {
|
function ed25519_pow_2_252_3(x: bigint) {
|
||||||
const P = ED25519_P;
|
const P = ED25519_P;
|
||||||
const x2 = (x * x) % P;
|
const x2 = (x * x) % P;
|
||||||
@@ -50,6 +52,7 @@ function ed25519_pow_2_252_3(x: bigint) {
|
|||||||
// ^ To pow to (p+3)/8, multiply it by x.
|
// ^ To pow to (p+3)/8, multiply it by x.
|
||||||
return { pow_p_5_8, b2 };
|
return { pow_p_5_8, b2 };
|
||||||
}
|
}
|
||||||
|
|
||||||
function adjustScalarBytes(bytes: Uint8Array): Uint8Array {
|
function adjustScalarBytes(bytes: Uint8Array): Uint8Array {
|
||||||
// Section 5: For X25519, in order to decode 32 random bytes as an integer scalar,
|
// Section 5: For X25519, in order to decode 32 random bytes as an integer scalar,
|
||||||
// set the three least significant bits of the first byte
|
// set the three least significant bits of the first byte
|
||||||
@@ -60,6 +63,7 @@ function adjustScalarBytes(bytes: Uint8Array): Uint8Array {
|
|||||||
bytes[31] |= 64; // 0b0100_0000
|
bytes[31] |= 64; // 0b0100_0000
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
// sqrt(u/v)
|
// sqrt(u/v)
|
||||||
function uvRatio(u: bigint, v: bigint): { isValid: boolean; value: bigint } {
|
function uvRatio(u: bigint, v: bigint): { isValid: boolean; value: bigint } {
|
||||||
const P = ED25519_P;
|
const P = ED25519_P;
|
||||||
@@ -94,16 +98,16 @@ export const ED25519_TORSION_SUBGROUP = [
|
|||||||
|
|
||||||
const Fp = Field(ED25519_P, undefined, true);
|
const Fp = Field(ED25519_P, undefined, true);
|
||||||
|
|
||||||
const ED25519_DEF = {
|
const ed25519Defaults = {
|
||||||
// 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
|
||||||
h: BigInt(8),
|
h: BigInt(8),
|
||||||
@@ -119,7 +123,8 @@ const ED25519_DEF = {
|
|||||||
uvRatio,
|
uvRatio,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const ed25519 = twistedEdwards(ED25519_DEF);
|
export const ed25519 = /* @__PURE__ */ 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(
|
||||||
@@ -129,19 +134,24 @@ function ed25519_domain(data: Uint8Array, ctx: Uint8Array, phflag: boolean) {
|
|||||||
data
|
data
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
export const ed25519ctx = twistedEdwards({ ...ED25519_DEF, domain: ed25519_domain });
|
|
||||||
export const ed25519ph = twistedEdwards({
|
export const ed25519ctx = /* @__PURE__ */ twistedEdwards({
|
||||||
...ED25519_DEF,
|
...ed25519Defaults,
|
||||||
domain: ed25519_domain,
|
domain: ed25519_domain,
|
||||||
preHash: sha512,
|
});
|
||||||
|
export const ed25519ph = /* @__PURE__ */ twistedEdwards({
|
||||||
|
...ed25519Defaults,
|
||||||
|
domain: ed25519_domain,
|
||||||
|
prehash: sha512,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const x25519 = montgomery({
|
export const x25519 = /* @__PURE__ */ (() =>
|
||||||
|
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)
|
||||||
@@ -149,7 +159,36 @@ 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 someonesPub = ed25519.getPublicKey(ed25519.utils.randomPrivateKey());
|
||||||
|
* const aPriv = x25519.utils.randomPrivateKey();
|
||||||
|
* x25519.getSharedSecret(aPriv, edwardsToMontgomeryPub(someonesPub))
|
||||||
|
*/
|
||||||
|
export function edwardsToMontgomeryPub(edwardsPub: Hex): Uint8Array {
|
||||||
|
const { y } = ed25519.ExtendedPoint.fromHex(edwardsPub);
|
||||||
|
const _1n = BigInt(1);
|
||||||
|
return Fp.toBytes(Fp.create((_1n + y) * Fp.inv(_1n - y)));
|
||||||
|
}
|
||||||
|
export const edwardsToMontgomery = edwardsToMontgomeryPub; // deprecated
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts ed25519 secret key to x25519 secret key.
|
||||||
|
* @example
|
||||||
|
* const someonesPub = x25519.getPublicKey(x25519.utils.randomPrivateKey());
|
||||||
|
* const aPriv = ed25519.utils.randomPrivateKey();
|
||||||
|
* x25519.getSharedSecret(edwardsToMontgomeryPriv(aPriv), someonesPub)
|
||||||
|
*/
|
||||||
|
export function edwardsToMontgomeryPriv(edwardsPriv: Uint8Array): Uint8Array {
|
||||||
|
const hashed = ed25519Defaults.hash(edwardsPriv.subarray(0, 32));
|
||||||
|
return ed25519Defaults.adjustScalarBytes(hashed).subarray(0, 32);
|
||||||
|
}
|
||||||
|
|
||||||
// Hash To Curve Elligator2 Map (NOTE: different from ristretto255 elligator)
|
// Hash To Curve Elligator2 Map (NOTE: different from ristretto255 elligator)
|
||||||
// NOTE: very important part is usage of FpSqrtEven for ELL2_C1_EDWARDS, since
|
// NOTE: very important part is usage of FpSqrtEven for ELL2_C1_EDWARDS, since
|
||||||
@@ -202,12 +241,13 @@ function map_to_curve_elligator2_curve25519(u: bigint) {
|
|||||||
let y = Fp.cmov(y2, y1, e3); // 36. y = CMOV(y2, y1, e3) # If e3, y = y1, else y = y2
|
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
|
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)
|
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)
|
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
|
const ELL2_C1_EDWARDS = FpSqrtEven(Fp, Fp.neg(BigInt(486664))); // sgn0(c1) MUST equal 0
|
||||||
function map_to_curve_elligator2_edwards25519(u: bigint) {
|
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)
|
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
|
let xn = Fp.mul(xMn, yMd); // 2. xn = xMn * yMd
|
||||||
xn = Fp.mul(xn, ELL2_C1_EDWARDS); // 3. xn = xn * c1
|
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 xd = Fp.mul(xMd, yMn); // 4. xd = xMd * yMn # xn / xd = c1 * xM / yM
|
||||||
@@ -223,7 +263,9 @@ function map_to_curve_elligator2_edwards25519(u: bigint) {
|
|||||||
const inv = Fp.invertBatch([xd, yd]); // batch division
|
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)
|
return { x: Fp.mul(xn, inv[0]), y: Fp.mul(yn, inv[1]) }; // 13. return (xn, xd, yn, yd)
|
||||||
}
|
}
|
||||||
const { hashToCurve, encodeToCurve } = htf.hashToCurve(
|
|
||||||
|
const htf = /* @__PURE__ */ (() =>
|
||||||
|
createHasher(
|
||||||
ed25519.ExtendedPoint,
|
ed25519.ExtendedPoint,
|
||||||
(scalars: bigint[]) => map_to_curve_elligator2_edwards25519(scalars[0]),
|
(scalars: bigint[]) => map_to_curve_elligator2_edwards25519(scalars[0]),
|
||||||
{
|
{
|
||||||
@@ -235,16 +277,16 @@ const { hashToCurve, encodeToCurve } = htf.hashToCurve(
|
|||||||
expand: 'xmd',
|
expand: 'xmd',
|
||||||
hash: sha512,
|
hash: sha512,
|
||||||
}
|
}
|
||||||
);
|
))();
|
||||||
export { hashToCurve, encodeToCurve };
|
export const hashToCurve = /* @__PURE__ */ (() => htf.hashToCurve)();
|
||||||
|
export const encodeToCurve = /* @__PURE__ */ (() => htf.encodeToCurve)();
|
||||||
|
|
||||||
function assertRstPoint(other: unknown) {
|
function assertRstPoint(other: unknown) {
|
||||||
if (!(other instanceof RistrettoPoint)) throw new Error('RistrettoPoint expected');
|
if (!(other instanceof RistPoint)) 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 = ED25519_SQRT_M1;
|
||||||
'19681161376707505956807079304988542015446066515923890162744021073123829784752'
|
|
||||||
);
|
|
||||||
// √(ad - 1)
|
// √(ad - 1)
|
||||||
const SQRT_AD_MINUS_ONE = BigInt(
|
const SQRT_AD_MINUS_ONE = BigInt(
|
||||||
'25063068953384623474111414158702152701244531502492656460079210482610430750235'
|
'25063068953384623474111414158702152701244531502492656460079210482610430750235'
|
||||||
@@ -301,27 +343,31 @@ function calcElligatorRistrettoMap(r0: bigint): ExtendedPoint {
|
|||||||
* but it should work in its own namespace: do not combine those two.
|
* but it should work in its own namespace: do not combine those two.
|
||||||
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-ristretto255-decaf448
|
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-ristretto255-decaf448
|
||||||
*/
|
*/
|
||||||
export class RistrettoPoint {
|
class RistPoint implements Group<RistPoint> {
|
||||||
static BASE = new RistrettoPoint(ed25519.ExtendedPoint.BASE);
|
static BASE: RistPoint;
|
||||||
static ZERO = new RistrettoPoint(ed25519.ExtendedPoint.ZERO);
|
static ZERO: RistPoint;
|
||||||
|
|
||||||
// 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 RistPoint(ed25519.ExtendedPoint.fromAffine(ap));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Takes uniform output of 64-bit hash function like sha512 and converts it to `RistrettoPoint`.
|
* Takes uniform output of 64-byte 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.
|
||||||
* **Note:** this is one-way map, there is no conversion from point to hash.
|
* **Note:** this is one-way map, there is no conversion from point to hash.
|
||||||
* https://ristretto.group/formulas/elligator.html
|
* https://ristretto.group/formulas/elligator.html
|
||||||
* @param hex 64-bit output of a hash function
|
* @param hex 64-byte output of a hash function
|
||||||
*/
|
*/
|
||||||
static hashToCurve(hex: Hex): RistrettoPoint {
|
static hashToCurve(hex: Hex): RistPoint {
|
||||||
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));
|
||||||
const R2 = calcElligatorRistrettoMap(r2);
|
const R2 = calcElligatorRistrettoMap(r2);
|
||||||
return new RistrettoPoint(R1.add(R2));
|
return new RistPoint(R1.add(R2));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -329,8 +375,8 @@ export class RistrettoPoint {
|
|||||||
* https://ristretto.group/formulas/decoding.html
|
* https://ristretto.group/formulas/decoding.html
|
||||||
* @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): RistPoint {
|
||||||
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.CURVE.Fp.create;
|
const mod = ed25519.CURVE.Fp.create;
|
||||||
@@ -353,7 +399,7 @@ export class RistrettoPoint {
|
|||||||
const y = mod(u1 * Dy); // 11
|
const y = mod(u1 * Dy); // 11
|
||||||
const t = mod(x * y); // 12
|
const t = mod(x * y); // 12
|
||||||
if (!isValid || isNegativeLE(t, P) || y === _0n) throw new Error(emsg);
|
if (!isValid || isNegativeLE(t, P) || y === _0n) throw new Error(emsg);
|
||||||
return new RistrettoPoint(new ed25519.ExtendedPoint(x, y, _1n, t));
|
return new RistPoint(new ed25519.ExtendedPoint(x, y, _1n, t));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -397,10 +443,10 @@ export class RistrettoPoint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Compare one point to another.
|
// Compare one point to another.
|
||||||
equals(other: RistrettoPoint): boolean {
|
equals(other: RistPoint): boolean {
|
||||||
assertRstPoint(other);
|
assertRstPoint(other);
|
||||||
const { ex: X1, ey: Y1 } = this.ep;
|
const { ex: X1, ey: Y1 } = this.ep;
|
||||||
const { ex: X2, ey: Y2 } = this.ep;
|
const { ex: X2, ey: Y2 } = other.ep;
|
||||||
const mod = ed25519.CURVE.Fp.create;
|
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(X1 * Y2) === mod(Y1 * X2);
|
const one = mod(X1 * Y2) === mod(Y1 * X2);
|
||||||
@@ -408,21 +454,44 @@ export class RistrettoPoint {
|
|||||||
return one || two;
|
return one || two;
|
||||||
}
|
}
|
||||||
|
|
||||||
add(other: RistrettoPoint): RistrettoPoint {
|
add(other: RistPoint): RistPoint {
|
||||||
assertRstPoint(other);
|
assertRstPoint(other);
|
||||||
return new RistrettoPoint(this.ep.add(other.ep));
|
return new RistPoint(this.ep.add(other.ep));
|
||||||
}
|
}
|
||||||
|
|
||||||
subtract(other: RistrettoPoint): RistrettoPoint {
|
subtract(other: RistPoint): RistPoint {
|
||||||
assertRstPoint(other);
|
assertRstPoint(other);
|
||||||
return new RistrettoPoint(this.ep.subtract(other.ep));
|
return new RistPoint(this.ep.subtract(other.ep));
|
||||||
}
|
}
|
||||||
|
|
||||||
multiply(scalar: bigint): RistrettoPoint {
|
multiply(scalar: bigint): RistPoint {
|
||||||
return new RistrettoPoint(this.ep.multiply(scalar));
|
return new RistPoint(this.ep.multiply(scalar));
|
||||||
}
|
}
|
||||||
|
|
||||||
multiplyUnsafe(scalar: bigint): RistrettoPoint {
|
multiplyUnsafe(scalar: bigint): RistPoint {
|
||||||
return new RistrettoPoint(this.ep.multiplyUnsafe(scalar));
|
return new RistPoint(this.ep.multiplyUnsafe(scalar));
|
||||||
|
}
|
||||||
|
|
||||||
|
double(): RistPoint {
|
||||||
|
return new RistPoint(this.ep.double());
|
||||||
|
}
|
||||||
|
|
||||||
|
negate(): RistPoint {
|
||||||
|
return new RistPoint(this.ep.negate());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
export const RistrettoPoint = /* @__PURE__ */ (() => {
|
||||||
|
if (!RistPoint.BASE) RistPoint.BASE = new RistPoint(ed25519.ExtendedPoint.BASE);
|
||||||
|
if (!RistPoint.ZERO) RistPoint.ZERO = new RistPoint(ed25519.ExtendedPoint.ZERO);
|
||||||
|
return RistPoint;
|
||||||
|
})();
|
||||||
|
|
||||||
|
// Hashing to ristretto255. https://www.rfc-editor.org/rfc/rfc9380#appendix-B
|
||||||
|
export const hashToRistretto255 = (msg: Uint8Array, options: htfBasicOpts) => {
|
||||||
|
const d = options.DST;
|
||||||
|
const DST = typeof d === 'string' ? utf8ToBytes(d) : d;
|
||||||
|
const uniform_bytes = expand_message_xmd(msg, DST, 64, sha512);
|
||||||
|
const P = RistPoint.hashToCurve(uniform_bytes);
|
||||||
|
return P;
|
||||||
|
};
|
||||||
|
export const hash_to_ristretto255 = hashToRistretto255; // legacy
|
||||||
|
|||||||
351
src/ed448.ts
351
src/ed448.ts
@@ -1,14 +1,25 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
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 { ExtPointType, twistedEdwards } from './abstract/edwards.js';
|
||||||
import { mod, pow2, Fp as Field } from './abstract/modular.js';
|
import { mod, pow2, Field, isNegativeLE } 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';
|
import { createHasher, htfBasicOpts, expand_message_xof } from './abstract/hash-to-curve.js';
|
||||||
|
import {
|
||||||
|
bytesToHex,
|
||||||
|
bytesToNumberLE,
|
||||||
|
ensureBytes,
|
||||||
|
equalBytes,
|
||||||
|
Hex,
|
||||||
|
numberToBytesLE,
|
||||||
|
} from './abstract/utils.js';
|
||||||
|
import { AffinePoint, Group } from './abstract/curve.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Edwards448 (not Ed448-Goldilocks) curve with following addons:
|
* Edwards448 (not Ed448-Goldilocks) curve with following addons:
|
||||||
* * X448 ECDH
|
* - X448 ECDH
|
||||||
|
* - Decaf cofactor elimination
|
||||||
|
* - Elligator hash-to-group / point indistinguishability
|
||||||
* Conforms to RFC 8032 https://www.rfc-editor.org/rfc/rfc8032.html#section-5.2
|
* Conforms to RFC 8032 https://www.rfc-editor.org/rfc/rfc8032.html#section-5.2
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -18,15 +29,16 @@ const ed448P = BigInt(
|
|||||||
'726838724295606890549323807888004534353641360687318060281490199180612328166730772686396383698676545930088884461843637361053498018365439'
|
'726838724295606890549323807888004534353641360687318060281490199180612328166730772686396383698676545930088884461843637361053498018365439'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// prettier-ignore
|
||||||
|
const _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n = BigInt(4), _11n = BigInt(11);
|
||||||
|
// prettier-ignore
|
||||||
|
const _22n = BigInt(22), _44n = BigInt(44), _88n = BigInt(88), _223n = BigInt(223);
|
||||||
|
|
||||||
// powPminus3div4 calculates z = x^k mod p, where k = (p-3)/4.
|
// powPminus3div4 calculates z = x^k mod p, where k = (p-3)/4.
|
||||||
// Used for efficient square root calculation.
|
// Used for efficient square root calculation.
|
||||||
// ((P-3)/4).toString(2) would produce bits [223x 1, 0, 222x 1]
|
// ((P-3)/4).toString(2) would produce bits [223x 1, 0, 222x 1]
|
||||||
function ed448_pow_Pminus3div4(x: bigint): bigint {
|
function ed448_pow_Pminus3div4(x: bigint): bigint {
|
||||||
const P = ed448P;
|
const P = ed448P;
|
||||||
// prettier-ignore
|
|
||||||
const _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _11n = BigInt(11);
|
|
||||||
// prettier-ignore
|
|
||||||
const _22n = BigInt(22), _44n = BigInt(44), _88n = BigInt(88), _223n = BigInt(223);
|
|
||||||
const b2 = (x * x * x) % P;
|
const b2 = (x * x * x) % P;
|
||||||
const b3 = (b2 * b2 * x) % P;
|
const b3 = (b2 * b2 * x) % P;
|
||||||
const b6 = (pow2(b3, _3n, P) * b3) % P;
|
const b6 = (pow2(b3, _3n, P) * b3) % P;
|
||||||
@@ -53,6 +65,28 @@ function adjustScalarBytes(bytes: Uint8Array): Uint8Array {
|
|||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Constant-time ratio of u to v. Allows to combine inversion and square root u/√v.
|
||||||
|
// Uses algo from RFC8032 5.1.3.
|
||||||
|
function uvRatio(u: bigint, v: bigint): { isValid: boolean; value: bigint } {
|
||||||
|
const P = ed448P;
|
||||||
|
// https://www.rfc-editor.org/rfc/rfc8032#section-5.2.3
|
||||||
|
// To compute the square root of (u/v), the first step is to compute the
|
||||||
|
// candidate root x = (u/v)^((p+1)/4). This can be done using the
|
||||||
|
// following trick, to use a single modular powering for both the
|
||||||
|
// inversion of v and the square root:
|
||||||
|
// x = (u/v)^((p+1)/4) = u³v(u⁵v³)^((p-3)/4) (mod p)
|
||||||
|
const u2v = mod(u * u * v, P); // u²v
|
||||||
|
const u3v = mod(u2v * u, P); // u³v
|
||||||
|
const u5v3 = mod(u3v * u2v * v, P); // u⁵v³
|
||||||
|
const root = ed448_pow_Pminus3div4(u5v3);
|
||||||
|
const x = mod(u3v * root, P);
|
||||||
|
// Verify that root is exists
|
||||||
|
const x2 = mod(x * x, P); // x²
|
||||||
|
// If vx² = u, the recovered x-coordinate is x. Otherwise, no
|
||||||
|
// square root exists, and the decoding fails.
|
||||||
|
return { isValid: mod(x2 * v, P) === u, value: x };
|
||||||
|
}
|
||||||
|
|
||||||
const Fp = Field(ed448P, 456, true);
|
const Fp = Field(ed448P, 456, true);
|
||||||
|
|
||||||
const ED448_DEF = {
|
const ED448_DEF = {
|
||||||
@@ -62,13 +96,14 @@ const ED448_DEF = {
|
|||||||
d: BigInt(
|
d: BigInt(
|
||||||
'726838724295606890549323807888004534353641360687318060281490199180612328166730772686396383698676545930088884461843637361053498018326358'
|
'726838724295606890549323807888004534353641360687318060281490199180612328166730772686396383698676545930088884461843637361053498018326358'
|
||||||
),
|
),
|
||||||
// Finite field 𝔽p over which we'll do calculations; 2n ** 448n - 2n ** 224n - 1n
|
// Finite field 𝔽p over which we'll do calculations; 2n**448n - 2n**224n - 1n
|
||||||
Fp,
|
Fp,
|
||||||
// Subgroup order: how many points curve has;
|
// Subgroup order: how many points curve has;
|
||||||
// 2n**446n - 13818066809895115352007386748515426880336692474882178609894547503885n
|
// 2n**446n - 13818066809895115352007386748515426880336692474882178609894547503885n
|
||||||
n: BigInt(
|
n: BigInt(
|
||||||
'181709681073901722637330951972001133588410340171829515070372549795146003961539585716195755291692375963310293709091662304773755859649779'
|
'181709681073901722637330951972001133588410340171829515070372549795146003961539585716195755291692375963310293709091662304773755859649779'
|
||||||
),
|
),
|
||||||
|
// RFC 7748 has 56-byte keys, RFC 8032 has 57-byte keys
|
||||||
nBitLength: 456,
|
nBitLength: 456,
|
||||||
// Cofactor
|
// Cofactor
|
||||||
h: BigInt(4),
|
h: BigInt(4),
|
||||||
@@ -93,40 +128,21 @@ const ED448_DEF = {
|
|||||||
data
|
data
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
uvRatio,
|
||||||
// Constant-time ratio of u to v. Allows to combine inversion and square root u/√v.
|
|
||||||
// Uses algo from RFC8032 5.1.3.
|
|
||||||
uvRatio: (u: bigint, v: bigint): { isValid: boolean; value: bigint } => {
|
|
||||||
const P = ed448P;
|
|
||||||
// https://datatracker.ietf.org/doc/html/rfc8032#section-5.2.3
|
|
||||||
// To compute the square root of (u/v), the first step is to compute the
|
|
||||||
// candidate root x = (u/v)^((p+1)/4). This can be done using the
|
|
||||||
// following trick, to use a single modular powering for both the
|
|
||||||
// inversion of v and the square root:
|
|
||||||
// x = (u/v)^((p+1)/4) = u³v(u⁵v³)^((p-3)/4) (mod p)
|
|
||||||
const u2v = mod(u * u * v, P); // u²v
|
|
||||||
const u3v = mod(u2v * u, P); // u³v
|
|
||||||
const u5v3 = mod(u3v * u2v * v, P); // u⁵v³
|
|
||||||
const root = ed448_pow_Pminus3div4(u5v3);
|
|
||||||
const x = mod(u3v * root, P);
|
|
||||||
// Verify that root is exists
|
|
||||||
const x2 = mod(x * x, P); // x²
|
|
||||||
// If vx² = u, the recovered x-coordinate is x. Otherwise, no
|
|
||||||
// square root exists, and the decoding fails.
|
|
||||||
return { isValid: mod(x2 * v, P) === u, value: x };
|
|
||||||
},
|
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const ed448 = twistedEdwards(ED448_DEF);
|
export const ed448 = /* @__PURE__ */ 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 = /* @__PURE__ */ twistedEdwards({ ...ED448_DEF, prehash: shake256_64 });
|
||||||
|
|
||||||
export const x448 = montgomery({
|
export const x448 = /* @__PURE__ */ (() =>
|
||||||
a24: BigInt(39081),
|
montgomery({
|
||||||
|
a: BigInt(156326),
|
||||||
|
// RFC 7748 has 56-byte keys, RFC 8032 has 57-byte keys
|
||||||
montgomeryBits: 448,
|
montgomeryBits: 448,
|
||||||
nByteLength: 57,
|
nByteLength: 56,
|
||||||
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);
|
||||||
@@ -134,25 +150,30 @@ 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)/
|
* Converts edwards448 public key to x448 public key. Uses formula:
|
||||||
// (u^5 - 2*u^2*v^2 - 2*u^3 - 2*v^2 + u))
|
* * `(u, v) = ((y-1)/(y+1), sqrt(156324)*u/x)`
|
||||||
// xyToU: (p: PointType) => {
|
* * `(x, y) = (sqrt(156324)*u/v, (1+u)/(1-u))`
|
||||||
// const P = ed448P;
|
* @example
|
||||||
// const { x, y } = p;
|
* const aPub = ed448.getPublicKey(utils.randomPrivateKey());
|
||||||
// if (x === _0n) throw new Error(`Point with x=0 doesn't have mapping`);
|
* x448.getSharedSecret(edwardsToMontgomery(aPub), edwardsToMontgomery(someonesPub))
|
||||||
// const invX = invert(x * x, P); // x^2
|
*/
|
||||||
// const u = mod(y * y * invX, P); // (y^2/x^2)
|
export function edwardsToMontgomeryPub(edwardsPub: string | Uint8Array): Uint8Array {
|
||||||
// return numberToBytesLE(u, 56);
|
const { y } = ed448.ExtendedPoint.fromHex(edwardsPub);
|
||||||
// },
|
const _1n = BigInt(1);
|
||||||
});
|
return Fp.toBytes(Fp.create((y - _1n) * Fp.inv(y + _1n)));
|
||||||
|
}
|
||||||
|
|
||||||
|
export const edwardsToMontgomery = edwardsToMontgomeryPub; // deprecated
|
||||||
|
// TODO: add edwardsToMontgomeryPriv, similar to ed25519 version
|
||||||
|
|
||||||
// Hash To Curve Elligator2 Map
|
// Hash To Curve Elligator2 Map
|
||||||
const ELL2_C1 = (Fp.ORDER - BigInt(3)) / BigInt(4); // 1. c1 = (q - 3) / 4 # Integer arithmetic
|
const ELL2_C1 = (Fp.ORDER - BigInt(3)) / BigInt(4); // 1. c1 = (q - 3) / 4 # Integer arithmetic
|
||||||
const ELL2_J = BigInt(156326);
|
const ELL2_J = BigInt(156326);
|
||||||
|
|
||||||
function map_to_curve_elligator2_curve448(u: bigint) {
|
function map_to_curve_elligator2_curve448(u: bigint) {
|
||||||
let tv1 = Fp.sqr(u); // 1. tv1 = u^2
|
let tv1 = Fp.sqr(u); // 1. tv1 = u^2
|
||||||
let e1 = Fp.eql(tv1, Fp.ONE); // 2. e1 = tv1 == 1
|
let e1 = Fp.eql(tv1, Fp.ONE); // 2. e1 = tv1 == 1
|
||||||
@@ -182,6 +203,7 @@ function map_to_curve_elligator2_curve448(u: bigint) {
|
|||||||
y = Fp.cmov(y, Fp.neg(y), e2 !== e3); // 26. y = CMOV(y, -y, e2 XOR e3)
|
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)
|
return { xn, xd, yn: y, yd: Fp.ONE }; // 27. return (xn, xd, y, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
function map_to_curve_elligator2_edwards448(u: bigint) {
|
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 { 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 xn2 = Fp.sqr(xn); // 2. xn2 = xn^2
|
||||||
@@ -194,10 +216,10 @@ function map_to_curve_elligator2_edwards448(u: bigint) {
|
|||||||
xEn = Fp.mul(xEn, xd2); // 9. xEn = xEn * xd2
|
xEn = Fp.mul(xEn, xd2); // 9. xEn = xEn * xd2
|
||||||
xEn = Fp.mul(xEn, yd); // 10. xEn = xEn * yd
|
xEn = Fp.mul(xEn, yd); // 10. xEn = xEn * yd
|
||||||
xEn = Fp.mul(xEn, yn); // 11. xEn = xEn * yn
|
xEn = Fp.mul(xEn, yn); // 11. xEn = xEn * yn
|
||||||
xEn = Fp.mul(xEn, 4n); // 12. xEn = xEn * 4
|
xEn = Fp.mul(xEn, _4n); // 12. xEn = xEn * 4
|
||||||
tv2 = Fp.mul(tv2, xn2); // 13. tv2 = tv2 * xn2
|
tv2 = Fp.mul(tv2, xn2); // 13. tv2 = tv2 * xn2
|
||||||
tv2 = Fp.mul(tv2, yd2); // 14. tv2 = tv2 * yd2
|
tv2 = Fp.mul(tv2, yd2); // 14. tv2 = tv2 * yd2
|
||||||
let tv3 = Fp.mul(yn2, 4n); // 15. tv3 = 4 * yn2
|
let tv3 = Fp.mul(yn2, _4n); // 15. tv3 = 4 * yn2
|
||||||
let tv1 = Fp.add(tv3, yd2); // 16. tv1 = tv3 + yd2
|
let tv1 = Fp.add(tv3, yd2); // 16. tv1 = tv3 + yd2
|
||||||
tv1 = Fp.mul(tv1, xd4); // 17. tv1 = tv1 * xd4
|
tv1 = Fp.mul(tv1, xd4); // 17. tv1 = tv1 * xd4
|
||||||
let xEd = Fp.add(tv1, tv2); // 18. xEd = tv1 + tv2
|
let xEd = Fp.add(tv1, tv2); // 18. xEd = tv1 + tv2
|
||||||
@@ -225,7 +247,8 @@ function map_to_curve_elligator2_edwards448(u: bigint) {
|
|||||||
return { x: Fp.mul(xEn, inv[0]), y: Fp.mul(yEn, inv[1]) }; // 38. return (xEn, xEd, yEn, yEd)
|
return { x: Fp.mul(xEn, inv[0]), y: Fp.mul(yEn, inv[1]) }; // 38. return (xEn, xEd, yEn, yEd)
|
||||||
}
|
}
|
||||||
|
|
||||||
const { hashToCurve, encodeToCurve } = htf.hashToCurve(
|
const htf = /* @__PURE__ */ (() =>
|
||||||
|
createHasher(
|
||||||
ed448.ExtendedPoint,
|
ed448.ExtendedPoint,
|
||||||
(scalars: bigint[]) => map_to_curve_elligator2_edwards448(scalars[0]),
|
(scalars: bigint[]) => map_to_curve_elligator2_edwards448(scalars[0]),
|
||||||
{
|
{
|
||||||
@@ -237,5 +260,221 @@ const { hashToCurve, encodeToCurve } = htf.hashToCurve(
|
|||||||
expand: 'xof',
|
expand: 'xof',
|
||||||
hash: shake256,
|
hash: shake256,
|
||||||
}
|
}
|
||||||
|
))();
|
||||||
|
export const hashToCurve = /* @__PURE__ */ (() => htf.hashToCurve)();
|
||||||
|
export const encodeToCurve = /* @__PURE__ */ (() => htf.encodeToCurve)();
|
||||||
|
|
||||||
|
function assertDcfPoint(other: unknown) {
|
||||||
|
if (!(other instanceof DcfPoint)) throw new Error('DecafPoint expected');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1-d
|
||||||
|
const ONE_MINUS_D = BigInt('39082');
|
||||||
|
// 1-2d
|
||||||
|
const ONE_MINUS_TWO_D = BigInt('78163');
|
||||||
|
// √(-d)
|
||||||
|
const SQRT_MINUS_D = BigInt(
|
||||||
|
'98944233647732219769177004876929019128417576295529901074099889598043702116001257856802131563896515373927712232092845883226922417596214'
|
||||||
);
|
);
|
||||||
export { hashToCurve, encodeToCurve };
|
// 1 / √(-d)
|
||||||
|
const INVSQRT_MINUS_D = BigInt(
|
||||||
|
'315019913931389607337177038330951043522456072897266928557328499619017160722351061360252776265186336876723201881398623946864393857820716'
|
||||||
|
);
|
||||||
|
// Calculates 1/√(number)
|
||||||
|
const invertSqrt = (number: bigint) => uvRatio(_1n, number);
|
||||||
|
|
||||||
|
const MAX_448B = BigInt(
|
||||||
|
'0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
|
||||||
|
);
|
||||||
|
const bytes448ToNumberLE = (bytes: Uint8Array) =>
|
||||||
|
ed448.CURVE.Fp.create(bytesToNumberLE(bytes) & MAX_448B);
|
||||||
|
|
||||||
|
type ExtendedPoint = ExtPointType;
|
||||||
|
|
||||||
|
// Computes Elligator map for Decaf
|
||||||
|
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-ristretto255-decaf448-07#name-element-derivation-2
|
||||||
|
function calcElligatorDecafMap(r0: bigint): ExtendedPoint {
|
||||||
|
const { d } = ed448.CURVE;
|
||||||
|
const P = ed448.CURVE.Fp.ORDER;
|
||||||
|
const mod = ed448.CURVE.Fp.create;
|
||||||
|
|
||||||
|
const r = mod(-(r0 * r0)); // 1
|
||||||
|
const u0 = mod(d * (r - _1n)); // 2
|
||||||
|
const u1 = mod((u0 + _1n) * (u0 - r)); // 3
|
||||||
|
|
||||||
|
const { isValid: was_square, value: v } = uvRatio(ONE_MINUS_TWO_D, mod((r + _1n) * u1)); // 4
|
||||||
|
|
||||||
|
let v_prime = v; // 5
|
||||||
|
if (!was_square) v_prime = mod(r0 * v);
|
||||||
|
|
||||||
|
let sgn = _1n; // 6
|
||||||
|
if (!was_square) sgn = mod(-_1n);
|
||||||
|
|
||||||
|
const s = mod(v_prime * (r + _1n)); // 7
|
||||||
|
let s_abs = s;
|
||||||
|
if (isNegativeLE(s, P)) s_abs = mod(-s);
|
||||||
|
|
||||||
|
const s2 = s * s;
|
||||||
|
const W0 = mod(s_abs * _2n); // 8
|
||||||
|
const W1 = mod(s2 + _1n); // 9
|
||||||
|
const W2 = mod(s2 - _1n); // 10
|
||||||
|
const W3 = mod(v_prime * s * (r - _1n) * ONE_MINUS_TWO_D + sgn); // 11
|
||||||
|
return new ed448.ExtendedPoint(mod(W0 * W3), mod(W2 * W1), mod(W1 * W3), mod(W0 * W2));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Each ed448/ExtendedPoint has 4 different equivalent points. This can be
|
||||||
|
* a source of bugs for protocols like ring signatures. Decaf was created to solve this.
|
||||||
|
* Decaf point operates in X:Y:Z:T extended coordinates like ExtendedPoint,
|
||||||
|
* but it should work in its own namespace: do not combine those two.
|
||||||
|
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-ristretto255-decaf448
|
||||||
|
*/
|
||||||
|
class DcfPoint implements Group<DcfPoint> {
|
||||||
|
static BASE: DcfPoint;
|
||||||
|
static ZERO: DcfPoint;
|
||||||
|
// Private property to discourage combining ExtendedPoint + DecafPoint
|
||||||
|
// Always use Decaf encoding/decoding instead.
|
||||||
|
constructor(private readonly ep: ExtendedPoint) {}
|
||||||
|
|
||||||
|
static fromAffine(ap: AffinePoint<bigint>) {
|
||||||
|
return new DcfPoint(ed448.ExtendedPoint.fromAffine(ap));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes uniform output of 112-byte hash function like shake256 and converts it to `DecafPoint`.
|
||||||
|
* The hash-to-group operation applies Elligator twice and adds the results.
|
||||||
|
* **Note:** this is one-way map, there is no conversion from point to hash.
|
||||||
|
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-ristretto255-decaf448-07#name-element-derivation-2
|
||||||
|
* @param hex 112-byte output of a hash function
|
||||||
|
*/
|
||||||
|
static hashToCurve(hex: Hex): DcfPoint {
|
||||||
|
hex = ensureBytes('decafHash', hex, 112);
|
||||||
|
const r1 = bytes448ToNumberLE(hex.slice(0, 56));
|
||||||
|
const R1 = calcElligatorDecafMap(r1);
|
||||||
|
const r2 = bytes448ToNumberLE(hex.slice(56, 112));
|
||||||
|
const R2 = calcElligatorDecafMap(r2);
|
||||||
|
return new DcfPoint(R1.add(R2));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts decaf-encoded string to decaf point.
|
||||||
|
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-ristretto255-decaf448-07#name-decode-2
|
||||||
|
* @param hex Decaf-encoded 56 bytes. Not every 56-byte string is valid decaf encoding
|
||||||
|
*/
|
||||||
|
static fromHex(hex: Hex): DcfPoint {
|
||||||
|
hex = ensureBytes('decafHex', hex, 56);
|
||||||
|
const { d } = ed448.CURVE;
|
||||||
|
const P = ed448.CURVE.Fp.ORDER;
|
||||||
|
const mod = ed448.CURVE.Fp.create;
|
||||||
|
const emsg = 'DecafPoint.fromHex: the hex is not valid encoding of DecafPoint';
|
||||||
|
const s = bytes448ToNumberLE(hex);
|
||||||
|
|
||||||
|
// 1. Check that s_bytes is the canonical encoding of a field element, or else abort.
|
||||||
|
// 2. Check that s is non-negative, or else abort
|
||||||
|
if (!equalBytes(numberToBytesLE(s, 56), hex) || isNegativeLE(s, P)) throw new Error(emsg);
|
||||||
|
|
||||||
|
const s2 = mod(s * s); // 1
|
||||||
|
const u1 = mod(_1n + s2); // 2
|
||||||
|
const u1sq = mod(u1 * u1);
|
||||||
|
const u2 = mod(u1sq - _4n * d * s2); // 3
|
||||||
|
|
||||||
|
const { isValid, value: invsqrt } = invertSqrt(mod(u2 * u1sq)); // 4
|
||||||
|
|
||||||
|
let u3 = mod((s + s) * invsqrt * u1 * SQRT_MINUS_D); // 5
|
||||||
|
if (isNegativeLE(u3, P)) u3 = mod(-u3);
|
||||||
|
|
||||||
|
const x = mod(u3 * invsqrt * u2 * INVSQRT_MINUS_D); // 6
|
||||||
|
const y = mod((_1n - s2) * invsqrt * u1); // 7
|
||||||
|
const t = mod(x * y); // 8
|
||||||
|
|
||||||
|
if (!isValid) throw new Error(emsg);
|
||||||
|
return new DcfPoint(new ed448.ExtendedPoint(x, y, _1n, t));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes decaf point to Uint8Array.
|
||||||
|
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-ristretto255-decaf448-07#name-encode-2
|
||||||
|
*/
|
||||||
|
toRawBytes(): Uint8Array {
|
||||||
|
let { ex: x, ey: _y, ez: z, et: t } = this.ep;
|
||||||
|
const P = ed448.CURVE.Fp.ORDER;
|
||||||
|
const mod = ed448.CURVE.Fp.create;
|
||||||
|
|
||||||
|
const u1 = mod(mod(x + t) * mod(x - t)); // 1
|
||||||
|
const x2 = mod(x * x);
|
||||||
|
const { value: invsqrt } = invertSqrt(mod(u1 * ONE_MINUS_D * x2)); // 2
|
||||||
|
|
||||||
|
let ratio = mod(invsqrt * u1 * SQRT_MINUS_D); // 3
|
||||||
|
if (isNegativeLE(ratio, P)) ratio = mod(-ratio);
|
||||||
|
|
||||||
|
const u2 = mod(INVSQRT_MINUS_D * ratio * z - t); // 4
|
||||||
|
|
||||||
|
let s = mod(ONE_MINUS_D * invsqrt * x * u2); // 5
|
||||||
|
if (isNegativeLE(s, P)) s = mod(-s);
|
||||||
|
|
||||||
|
return numberToBytesLE(s, 56);
|
||||||
|
}
|
||||||
|
|
||||||
|
toHex(): string {
|
||||||
|
return bytesToHex(this.toRawBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
toString(): string {
|
||||||
|
return this.toHex();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare one point to another.
|
||||||
|
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-ristretto255-decaf448-07#name-equals-2
|
||||||
|
equals(other: DcfPoint): boolean {
|
||||||
|
assertDcfPoint(other);
|
||||||
|
const { ex: X1, ey: Y1 } = this.ep;
|
||||||
|
const { ex: X2, ey: Y2 } = other.ep;
|
||||||
|
const mod = ed448.CURVE.Fp.create;
|
||||||
|
// (x1 * y2 == y1 * x2)
|
||||||
|
return mod(X1 * Y2) === mod(Y1 * X2);
|
||||||
|
}
|
||||||
|
|
||||||
|
add(other: DcfPoint): DcfPoint {
|
||||||
|
assertDcfPoint(other);
|
||||||
|
return new DcfPoint(this.ep.add(other.ep));
|
||||||
|
}
|
||||||
|
|
||||||
|
subtract(other: DcfPoint): DcfPoint {
|
||||||
|
assertDcfPoint(other);
|
||||||
|
return new DcfPoint(this.ep.subtract(other.ep));
|
||||||
|
}
|
||||||
|
|
||||||
|
multiply(scalar: bigint): DcfPoint {
|
||||||
|
return new DcfPoint(this.ep.multiply(scalar));
|
||||||
|
}
|
||||||
|
|
||||||
|
multiplyUnsafe(scalar: bigint): DcfPoint {
|
||||||
|
return new DcfPoint(this.ep.multiplyUnsafe(scalar));
|
||||||
|
}
|
||||||
|
|
||||||
|
double(): DcfPoint {
|
||||||
|
return new DcfPoint(this.ep.double());
|
||||||
|
}
|
||||||
|
|
||||||
|
negate(): DcfPoint {
|
||||||
|
return new DcfPoint(this.ep.negate());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DecafPoint = /* @__PURE__ */ (() => {
|
||||||
|
// decaf448 base point is ed448 base x 2
|
||||||
|
// https://github.com/dalek-cryptography/curve25519-dalek/blob/59837c6ecff02b77b9d5ff84dbc239d0cf33ef90/vendor/ristretto.sage#L699
|
||||||
|
if (!DcfPoint.BASE) DcfPoint.BASE = new DcfPoint(ed448.ExtendedPoint.BASE).multiply(_2n);
|
||||||
|
if (!DcfPoint.ZERO) DcfPoint.ZERO = new DcfPoint(ed448.ExtendedPoint.ZERO);
|
||||||
|
return DcfPoint;
|
||||||
|
})();
|
||||||
|
|
||||||
|
// Hashing to decaf448. https://www.rfc-editor.org/rfc/rfc9380#appendix-C
|
||||||
|
export const hashToDecaf448 = (msg: Uint8Array, options: htfBasicOpts) => {
|
||||||
|
const d = options.DST;
|
||||||
|
const DST = typeof d === 'string' ? utf8ToBytes(d) : d;
|
||||||
|
const uniform_bytes = expand_message_xof(msg, DST, 112, 224, shake256);
|
||||||
|
const P = DcfPoint.hashToCurve(uniform_bytes);
|
||||||
|
return P;
|
||||||
|
};
|
||||||
|
export const hash_to_decaf448 = hashToDecaf448; // legacy
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ 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.
|
||||||
@@ -11,13 +11,13 @@ import { Fp } from './abstract/modular.js';
|
|||||||
* jubjub does not use EdDSA, so `hash`/sha512 params are passed because interface expects them.
|
* jubjub does not use EdDSA, so `hash`/sha512 params are passed because interface expects them.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const jubjub = twistedEdwards({
|
export const jubjub = /* @__PURE__ */ twistedEdwards({
|
||||||
// Params: a, d
|
// Params: a, d
|
||||||
a: BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000'),
|
a: BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000'),
|
||||||
d: BigInt('0x2a9318e74bfa2b48f5fd9207e6bd7fd4292d7f6d37579d2601065fd6d6343eb1'),
|
d: BigInt('0x2a9318e74bfa2b48f5fd9207e6bd7fd4292d7f6d37579d2601065fd6d6343eb1'),
|
||||||
// Finite field 𝔽p over which we'll do calculations
|
// Finite field 𝔽p over which we'll do calculations
|
||||||
// Same value as bls12-381 Fr (not Fp)
|
// Same value as bls12-381 Fr (not Fp)
|
||||||
Fp: Fp(BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001')),
|
Fp: Field(BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001')),
|
||||||
// Subgroup order: how many points curve has
|
// Subgroup order: how many points curve has
|
||||||
n: BigInt('0xe7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7'),
|
n: BigInt('0xe7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7'),
|
||||||
// Cofactor
|
// Cofactor
|
||||||
|
|||||||
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;
|
|
||||||
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;
|
|
||||||
49
src/p256.ts
49
src/p256.ts
@@ -1,46 +1,41 @@
|
|||||||
/*! 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';
|
import { createHasher } 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');
|
||||||
|
|
||||||
const mapSWU = mapToCurveSimpleSWU(Fp, {
|
// prettier-ignore
|
||||||
A: CURVE_A,
|
export const p256 = createCurve({
|
||||||
B: CURVE_B,
|
a: CURVE_A, // Equation params: a, b
|
||||||
Z: Fp.create(BigInt('-10')),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const P256 = createCurve(
|
|
||||||
{
|
|
||||||
// Params: a, b
|
|
||||||
a: CURVE_A,
|
|
||||||
b: CURVE_B,
|
b: CURVE_B,
|
||||||
Fp,
|
Fp, // Field: 2n**224n * (2n**32n-1n) + 2n**192n + 2n**96n-1n
|
||||||
// Curve order, total count of valid points in the field
|
// Curve order, total count of valid points in the field
|
||||||
n: BigInt('0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551'),
|
n: BigInt('0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551'),
|
||||||
// Base point (x, y) aka generator point
|
// Base (generator) point (x, y)
|
||||||
Gx: BigInt('0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296'),
|
Gx: BigInt('0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296'),
|
||||||
Gy: BigInt('0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5'),
|
Gy: BigInt('0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5'),
|
||||||
h: BigInt(1),
|
h: BigInt(1),
|
||||||
lowS: false,
|
lowS: false,
|
||||||
} as const,
|
} as const, sha256);
|
||||||
sha256
|
export const secp256r1 = p256;
|
||||||
);
|
|
||||||
export const secp256r1 = P256;
|
|
||||||
|
|
||||||
const { hashToCurve, encodeToCurve } = htf.hashToCurve(
|
const mapSWU = /* @__PURE__ */ (() =>
|
||||||
secp256r1.ProjectivePoint,
|
mapToCurveSimpleSWU(Fp, {
|
||||||
(scalars: bigint[]) => mapSWU(scalars[0]),
|
A: CURVE_A,
|
||||||
{
|
B: CURVE_B,
|
||||||
|
Z: Fp.create(BigInt('-10')),
|
||||||
|
}))();
|
||||||
|
|
||||||
|
const htf = /* @__PURE__ */ (() =>
|
||||||
|
createHasher(secp256r1.ProjectivePoint, (scalars: bigint[]) => mapSWU(scalars[0]), {
|
||||||
DST: 'P256_XMD:SHA-256_SSWU_RO_',
|
DST: 'P256_XMD:SHA-256_SSWU_RO_',
|
||||||
encodeDST: 'P256_XMD:SHA-256_SSWU_NU_',
|
encodeDST: 'P256_XMD:SHA-256_SSWU_NU_',
|
||||||
p: Fp.ORDER,
|
p: Fp.ORDER,
|
||||||
@@ -48,6 +43,6 @@ const { hashToCurve, encodeToCurve } = htf.hashToCurve(
|
|||||||
k: 128,
|
k: 128,
|
||||||
expand: 'xmd',
|
expand: 'xmd',
|
||||||
hash: sha256,
|
hash: sha256,
|
||||||
}
|
}))();
|
||||||
);
|
export const hashToCurve = /* @__PURE__ */ (() => htf.hashToCurve)();
|
||||||
export { hashToCurve, encodeToCurve };
|
export const encodeToCurve = /* @__PURE__ */ (() => htf.encodeToCurve)();
|
||||||
|
|||||||
49
src/p384.ts
49
src/p384.ts
@@ -1,14 +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';
|
import { createHasher } 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);
|
||||||
@@ -16,35 +16,30 @@ const CURVE_A = Fp.create(BigInt('-3'));
|
|||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
const CURVE_B = BigInt('0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef');
|
const CURVE_B = BigInt('0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef');
|
||||||
|
|
||||||
const mapSWU = mapToCurveSimpleSWU(Fp, {
|
|
||||||
A: CURVE_A,
|
|
||||||
B: CURVE_B,
|
|
||||||
Z: Fp.create(BigInt('-12')),
|
|
||||||
});
|
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
export const P384 = createCurve({
|
export const p384 = createCurve({
|
||||||
// Params: a, b
|
a: CURVE_A, // Equation params: a, b
|
||||||
a: CURVE_A,
|
|
||||||
b: CURVE_B,
|
b: CURVE_B,
|
||||||
// Field over which we'll do calculations. 2n**384n - 2n**128n - 2n**96n + 2n**32n - 1n
|
Fp, // Field: 2n**384n - 2n**128n - 2n**96n + 2n**32n - 1n
|
||||||
Fp,
|
|
||||||
// Curve order, total count of valid points in the field.
|
// Curve order, total count of valid points in the field.
|
||||||
n: BigInt('0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973'),
|
n: BigInt('0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973'),
|
||||||
// Base point (x, y) aka generator point
|
// Base (generator) point (x, y)
|
||||||
Gx: BigInt('0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7'),
|
Gx: BigInt('0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7'),
|
||||||
Gy: BigInt('0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f'),
|
Gy: BigInt('0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f'),
|
||||||
h: BigInt(1),
|
h: BigInt(1),
|
||||||
lowS: false,
|
lowS: false,
|
||||||
} as const,
|
} as const, sha384);
|
||||||
sha384
|
export const secp384r1 = p384;
|
||||||
);
|
|
||||||
export const secp384r1 = P384;
|
|
||||||
|
|
||||||
const { hashToCurve, encodeToCurve } = htf.hashToCurve(
|
const mapSWU = /* @__PURE__ */ (() =>
|
||||||
secp384r1.ProjectivePoint,
|
mapToCurveSimpleSWU(Fp, {
|
||||||
(scalars: bigint[]) => mapSWU(scalars[0]),
|
A: CURVE_A,
|
||||||
{
|
B: CURVE_B,
|
||||||
|
Z: Fp.create(BigInt('-12')),
|
||||||
|
}))();
|
||||||
|
|
||||||
|
const htf = /* @__PURE__ */ (() =>
|
||||||
|
createHasher(secp384r1.ProjectivePoint, (scalars: bigint[]) => mapSWU(scalars[0]), {
|
||||||
DST: 'P384_XMD:SHA-384_SSWU_RO_',
|
DST: 'P384_XMD:SHA-384_SSWU_RO_',
|
||||||
encodeDST: 'P384_XMD:SHA-384_SSWU_NU_',
|
encodeDST: 'P384_XMD:SHA-384_SSWU_NU_',
|
||||||
p: Fp.ORDER,
|
p: Fp.ORDER,
|
||||||
@@ -52,6 +47,6 @@ const { hashToCurve, encodeToCurve } = htf.hashToCurve(
|
|||||||
k: 192,
|
k: 192,
|
||||||
expand: 'xmd',
|
expand: 'xmd',
|
||||||
hash: sha384,
|
hash: sha384,
|
||||||
}
|
}))();
|
||||||
);
|
export const hashToCurve = /* @__PURE__ */ (() => htf.hashToCurve)();
|
||||||
export { hashToCurve, encodeToCurve };
|
export const encodeToCurve = /* @__PURE__ */ (() => htf.encodeToCurve)();
|
||||||
|
|||||||
75
src/p521.ts
75
src/p521.ts
@@ -1,50 +1,61 @@
|
|||||||
/*! 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 { 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';
|
import { createHasher } 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 = {
|
||||||
// prettier-ignore
|
a: Fp.create(BigInt('-3')),
|
||||||
const CURVE_B = BigInt('0x0051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00');
|
b: BigInt(
|
||||||
|
'0x0051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00'
|
||||||
const mapSWU = mapToCurveSimpleSWU(Fp, {
|
),
|
||||||
A: CURVE_A,
|
|
||||||
B: CURVE_B,
|
|
||||||
Z: Fp.create(BigInt('-4')),
|
|
||||||
});
|
|
||||||
|
|
||||||
// prettier-ignore
|
|
||||||
export const P521 = createCurve({
|
|
||||||
// Params: a, b
|
|
||||||
a: CURVE_A,
|
|
||||||
b: CURVE_B,
|
|
||||||
Fp,
|
Fp,
|
||||||
// Curve order, total count of valid points in the field
|
n: BigInt(
|
||||||
n: BigInt('0x01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409'),
|
'0x01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409'
|
||||||
// Base point (x, y) aka generator point
|
),
|
||||||
Gx: BigInt('0x00c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66'),
|
Gx: BigInt(
|
||||||
Gy: BigInt('0x011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650'),
|
'0x00c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66'
|
||||||
|
),
|
||||||
|
Gy: BigInt(
|
||||||
|
'0x011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650'
|
||||||
|
),
|
||||||
h: BigInt(1),
|
h: BigInt(1),
|
||||||
|
};
|
||||||
|
|
||||||
|
// prettier-ignore
|
||||||
|
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,
|
lowS: false,
|
||||||
allowedPrivateKeyLengths: [130, 131, 132] // P521 keys are variable-length. Normalize to 132b
|
allowedPrivateKeyLengths: [130, 131, 132] // P521 keys are variable-length. Normalize to 132b
|
||||||
} as const, sha512);
|
} as const, sha512);
|
||||||
export const secp521r1 = P521;
|
export const secp521r1 = p521;
|
||||||
|
|
||||||
const { hashToCurve, encodeToCurve } = htf.hashToCurve(
|
const mapSWU = /* @__PURE__ */ (() =>
|
||||||
secp521r1.ProjectivePoint,
|
mapToCurveSimpleSWU(Fp, {
|
||||||
(scalars: bigint[]) => mapSWU(scalars[0]),
|
A: CURVE.a,
|
||||||
{
|
B: CURVE.b,
|
||||||
|
Z: Fp.create(BigInt('-4')),
|
||||||
|
}))();
|
||||||
|
|
||||||
|
const htf = /* @__PURE__ */ (() =>
|
||||||
|
createHasher(secp521r1.ProjectivePoint, (scalars: bigint[]) => mapSWU(scalars[0]), {
|
||||||
DST: 'P521_XMD:SHA-512_SSWU_RO_',
|
DST: 'P521_XMD:SHA-512_SSWU_RO_',
|
||||||
encodeDST: 'P521_XMD:SHA-512_SSWU_NU_',
|
encodeDST: 'P521_XMD:SHA-512_SSWU_NU_',
|
||||||
p: Fp.ORDER,
|
p: Fp.ORDER,
|
||||||
@@ -52,6 +63,6 @@ const { hashToCurve, encodeToCurve } = htf.hashToCurve(
|
|||||||
k: 256,
|
k: 256,
|
||||||
expand: 'xmd',
|
expand: 'xmd',
|
||||||
hash: sha512,
|
hash: sha512,
|
||||||
}
|
}))();
|
||||||
);
|
export const hashToCurve = /* @__PURE__ */ (() => htf.hashToCurve)();
|
||||||
export { hashToCurve, encodeToCurve };
|
export const encodeToCurve = /* @__PURE__ */ (() => htf.encodeToCurve)();
|
||||||
|
|||||||
3
src/package.json
Normal file
3
src/package.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"type": "module"
|
||||||
|
}
|
||||||
@@ -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),
|
||||||
|
|||||||
160
src/secp256k1.ts
160
src/secp256k1.ts
@@ -1,26 +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 { ProjPointType as PointType, mapToCurveSimpleSWU } from './abstract/weierstrass.js';
|
|
||||||
import {
|
|
||||||
ensureBytes,
|
|
||||||
concatBytes,
|
|
||||||
Hex,
|
|
||||||
bytesToNumberBE as bytesToInt,
|
|
||||||
PrivKey,
|
|
||||||
numberToBytesBE,
|
|
||||||
} from './abstract/utils.js';
|
|
||||||
import { randomBytes } from '@noble/hashes/utils';
|
import { randomBytes } from '@noble/hashes/utils';
|
||||||
import * as htf 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 efficiently computable endomorphism.
|
import { bytesToNumberBE, concatBytes, ensureBytes, numberToBytesBE } from './abstract/utils.js';
|
||||||
* Endomorphism uses 2x less RAM, speeds up precomputation by 2x and ECDH / key recovery by 20%.
|
import { createHasher, isogenyMap } from './abstract/hash-to-curve.js';
|
||||||
* Should always be used for Projective's double-and-add multiplication.
|
import { createCurve } from './_shortw_utils.js';
|
||||||
* 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');
|
||||||
@@ -57,27 +43,25 @@ function sqrtMod(y: bigint): bigint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Fp = Field(secp256k1P, undefined, undefined, { sqrt: sqrtMod });
|
const Fp = Field(secp256k1P, undefined, undefined, { sqrt: sqrtMod });
|
||||||
type Fp = bigint;
|
|
||||||
|
|
||||||
export const secp256k1 = createCurve(
|
export const secp256k1 = createCurve(
|
||||||
{
|
{
|
||||||
// Params: a, b
|
a: BigInt(0), // equation params: a, b
|
||||||
// Seem to be rigid https://bitcointalk.org/index.php?topic=289795.msg3183975#msg3183975
|
b: BigInt(7), // Seem to be rigid: bitcointalk.org/index.php?topic=289795.msg3183975#msg3183975
|
||||||
a: BigInt(0),
|
Fp, // Field's prime: 2n**256n - 2n**32n - 2n**9n - 2n**8n - 2n**7n - 2n**6n - 2n**4n - 1n
|
||||||
b: BigInt(7),
|
n: secp256k1N, // Curve order, total count of valid points in the field
|
||||||
// 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
|
// Base point (x, y) aka generator point
|
||||||
Gx: BigInt('55066263022277343669578718895168534326250603453777594175500187360389116729240'),
|
Gx: BigInt('55066263022277343669578718895168534326250603453777594175500187360389116729240'),
|
||||||
Gy: BigInt('32670510020758816978083085130507043184471273380659243275938904335757337482424'),
|
Gy: BigInt('32670510020758816978083085130507043184471273380659243275938904335757337482424'),
|
||||||
h: BigInt(1),
|
h: BigInt(1), // Cofactor
|
||||||
// Alllow only low-S signatures by default in sign() and verify()
|
lowS: true, // Allow only low-S signatures by default in sign() and verify()
|
||||||
lowS: true,
|
/**
|
||||||
|
* 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: {
|
endo: {
|
||||||
// Params taken from https://gist.github.com/paulmillr/eb670806793e84df628a7c434a873066
|
|
||||||
beta: BigInt('0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee'),
|
beta: BigInt('0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee'),
|
||||||
splitScalar: (k: bigint) => {
|
splitScalar: (k: bigint) => {
|
||||||
const n = secp256k1N;
|
const n = secp256k1N;
|
||||||
@@ -105,19 +89,11 @@ export const secp256k1 = createCurve(
|
|||||||
sha256
|
sha256
|
||||||
);
|
);
|
||||||
|
|
||||||
// Schnorr signatures are superior to ECDSA from above.
|
// Schnorr signatures are superior to ECDSA from above. Below is Schnorr-specific BIP0340 code.
|
||||||
// Below is Schnorr-specific code as per BIP0340.
|
|
||||||
// https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki
|
// https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki
|
||||||
const _0n = BigInt(0);
|
const _0n = BigInt(0);
|
||||||
const fe = (x: bigint) => typeof x === 'bigint' && _0n < x && x < secp256k1P;
|
const fe = (x: bigint) => typeof x === 'bigint' && _0n < x && x < secp256k1P;
|
||||||
const ge = (x: bigint) => typeof x === 'bigint' && _0n < x && x < secp256k1N;
|
const ge = (x: bigint) => typeof x === 'bigint' && _0n < x && x < secp256k1N;
|
||||||
|
|
||||||
const TAGS = {
|
|
||||||
challenge: 'BIP0340/challenge',
|
|
||||||
aux: 'BIP0340/aux',
|
|
||||||
nonce: 'BIP0340/nonce',
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
/** An object mapping tags to their tagged hash prefix of [SHA256(tag) | SHA256(tag)] */
|
/** An object mapping tags to their tagged hash prefix of [SHA256(tag) | SHA256(tag)] */
|
||||||
const TAGGED_HASH_PREFIXES: { [tag: string]: Uint8Array } = {};
|
const TAGGED_HASH_PREFIXES: { [tag: string]: Uint8Array } = {};
|
||||||
function taggedHash(tag: string, ...messages: Uint8Array[]): Uint8Array {
|
function taggedHash(tag: string, ...messages: Uint8Array[]): Uint8Array {
|
||||||
@@ -130,56 +106,70 @@ function taggedHash(tag: string, ...messages: Uint8Array[]): Uint8Array {
|
|||||||
return sha256(concatBytes(tagP, ...messages));
|
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 pointToBytes = (point: PointType<bigint>) => point.toRawBytes(true).slice(1);
|
||||||
const numTo32b = (n: bigint) => numberToBytesBE(n, 32);
|
const numTo32b = (n: bigint) => numberToBytesBE(n, 32);
|
||||||
|
const modP = (x: bigint) => mod(x, secp256k1P);
|
||||||
const modN = (x: bigint) => mod(x, secp256k1N);
|
const modN = (x: bigint) => mod(x, secp256k1N);
|
||||||
const Point = secp256k1.ProjectivePoint;
|
const Point = secp256k1.ProjectivePoint;
|
||||||
const GmulAdd = (Q: PointType<bigint>, a: bigint, b: bigint) =>
|
const GmulAdd = (Q: PointType<bigint>, a: bigint, b: bigint) =>
|
||||||
Point.BASE.multiplyAndAddUnsafe(Q, a, b);
|
Point.BASE.multiplyAndAddUnsafe(Q, a, b);
|
||||||
const hex32ToInt = (key: Hex) => bytesToInt(ensureBytes(key, 32));
|
|
||||||
|
// Calculate point, scalar and bytes
|
||||||
function schnorrGetExtPubKey(priv: PrivKey) {
|
function schnorrGetExtPubKey(priv: PrivKey) {
|
||||||
let d = typeof priv === 'bigint' ? priv : hex32ToInt(priv);
|
let d_ = secp256k1.utils.normPrivateKeyToScalar(priv); // same method executed in fromPrivateKey
|
||||||
const point = Point.fromPrivateKey(d); // P = d'⋅G; 0 < d' < n check is done inside
|
let p = Point.fromPrivateKey(d_); // P = d'⋅G; 0 < d' < n check is done inside
|
||||||
const scalar = point.hasEvenY() ? d : modN(-d); // d = d' if has_even_y(P), otherwise d = n-d'
|
const scalar = p.hasEvenY() ? d_ : modN(-d_);
|
||||||
return { point, scalar, bytes: pointToBytes(point) };
|
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> {
|
function lift_x(x: bigint): PointType<bigint> {
|
||||||
if (!fe(x)) throw new Error('bad x: need 0 < x < p'); // Fail if x ≥ p.
|
if (!fe(x)) throw new Error('bad x: need 0 < x < p'); // Fail if x ≥ p.
|
||||||
const c = mod(x * x * x + BigInt(7), secp256k1P); // Let c = x³ + 7 mod p.
|
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.
|
let y = sqrtMod(c); // Let y = c^(p+1)/4 mod p.
|
||||||
if (y % 2n !== 0n) y = mod(-y, secp256k1P); // Return the unique point P such that x(P) = x and
|
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.
|
const p = new Point(x, y, _1n); // y(P) = y if y mod 2 = 0 or y(P) = p-y otherwise.
|
||||||
p.assertValidity();
|
p.assertValidity();
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Create tagged hash, convert it to bigint, reduce modulo-n.
|
||||||
|
*/
|
||||||
function challenge(...args: Uint8Array[]): bigint {
|
function challenge(...args: Uint8Array[]): bigint {
|
||||||
return modN(bytesToInt(taggedHash(TAGS.challenge, ...args)));
|
return modN(bytesToNumberBE(taggedHash('BIP0340/challenge', ...args)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Schnorr's pubkey is just `x` of Point (BIP340)
|
/**
|
||||||
|
* Schnorr public key is just `x` coordinate of Point as per BIP340.
|
||||||
|
*/
|
||||||
function schnorrGetPublicKey(privateKey: Hex): Uint8Array {
|
function schnorrGetPublicKey(privateKey: Hex): Uint8Array {
|
||||||
return schnorrGetExtPubKey(privateKey).bytes; // d'=int(sk). Fail if d'=0 or d'≥n. Ret bytes(d'⋅G)
|
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
|
* 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(
|
function schnorrSign(
|
||||||
message: Hex,
|
message: Hex,
|
||||||
privateKey: PrivKey,
|
privateKey: PrivKey,
|
||||||
auxRand: Hex = randomBytes(32)
|
auxRand: Hex = randomBytes(32)
|
||||||
): Uint8Array {
|
): Uint8Array {
|
||||||
if (message == null) throw new Error(`sign: Expected valid message, not "${message}"`);
|
const m = ensureBytes('message', message);
|
||||||
const m = ensureBytes(message); // checks for isWithinCurveOrder
|
const { bytes: px, scalar: d } = schnorrGetExtPubKey(privateKey); // checks for isWithinCurveOrder
|
||||||
const { bytes: px, scalar: d } = schnorrGetExtPubKey(privateKey);
|
const a = ensureBytes('auxRand', auxRand, 32); // Auxiliary random data a: a 32-byte array
|
||||||
const a = ensureBytes(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 t = numTo32b(d ^ bytesToInt(taggedHash(TAGS.aux, a))); // Let t be the byte-wise xor of bytes(d) and hash/aux(a)
|
const rand = taggedHash('BIP0340/nonce', t, px, m); // Let rand = hash/nonce(t || bytes(P) || m)
|
||||||
const rand = taggedHash(TAGS.nonce, t, px, m); // Let rand = hash/nonce(t || bytes(P) || m)
|
const k_ = modN(bytesToNumberBE(rand)); // Let k' = int(rand) mod n
|
||||||
const k_ = modN(bytesToInt(rand)); // Let k' = int(rand) mod n
|
|
||||||
if (k_ === _0n) throw new Error('sign failed: k is zero'); // Fail if k' = 0.
|
if (k_ === _0n) throw new Error('sign failed: k is zero'); // Fail if k' = 0.
|
||||||
const { point: R, bytes: rx, scalar: k } = schnorrGetExtPubKey(k_); // Let R = k'⋅G.
|
const { 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 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).
|
const sig = new Uint8Array(64); // Let sig = bytes(R) || bytes((k + ed) mod n).
|
||||||
sig.set(numTo32b(R.px), 0);
|
sig.set(rx, 0);
|
||||||
sig.set(numTo32b(modN(k + e * d)), 32);
|
sig.set(numTo32b(modN(k + e * d)), 32);
|
||||||
// If Verify(bytes(P), m, sig) (see below) returns failure, abort
|
// If Verify(bytes(P), m, sig) (see below) returns failure, abort
|
||||||
if (!schnorrVerify(sig, m, px)) throw new Error('sign: Invalid signature produced');
|
if (!schnorrVerify(sig, m, px)) throw new Error('sign: Invalid signature produced');
|
||||||
@@ -187,18 +177,20 @@ function schnorrSign(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies Schnorr signature synchronously.
|
* Verifies Schnorr signature.
|
||||||
|
* Will swallow errors & return false except for initial type validation of arguments.
|
||||||
*/
|
*/
|
||||||
function schnorrVerify(signature: Hex, message: Hex, publicKey: Hex): boolean {
|
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 {
|
try {
|
||||||
const P = lift_x(hex32ToInt(publicKey)); // P = lift_x(int(pk)); fail if that fails
|
const P = lift_x(bytesToNumberBE(pub)); // P = lift_x(int(pk)); fail if that fails
|
||||||
const sig = ensureBytes(signature, 64);
|
const r = bytesToNumberBE(sig.subarray(0, 32)); // Let r = int(sig[0:32]); fail if r ≥ p.
|
||||||
const r = bytesToInt(sig.subarray(0, 32)); // Let r = int(sig[0:32]); fail if r ≥ p.
|
|
||||||
if (!fe(r)) return false;
|
if (!fe(r)) return false;
|
||||||
const s = bytesToInt(sig.subarray(32, 64)); // Let s = int(sig[32:64]); fail if s ≥ n.
|
const s = bytesToNumberBE(sig.subarray(32, 64)); // Let s = int(sig[32:64]); fail if s ≥ n.
|
||||||
if (!ge(s)) return false;
|
if (!ge(s)) return false;
|
||||||
const m = ensureBytes(message);
|
const e = challenge(numTo32b(r), pointToBytes(P), m); // int(challenge(bytes(r)||bytes(P)||m))%n
|
||||||
const e = challenge(numTo32b(r), pointToBytes(P), m); // int(challenge(bytes(r)||bytes(P)||m)) mod n
|
|
||||||
const R = GmulAdd(P, s, modN(-e)); // R = s⋅G - e⋅P
|
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
|
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.
|
return true; // Fail if is_infinite(R) / not has_even_y(R) / x(R) ≠ r.
|
||||||
@@ -207,22 +199,23 @@ function schnorrVerify(signature: Hex, message: Hex, publicKey: Hex): boolean {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const schnorr = {
|
export const schnorr = /* @__PURE__ */ (() => ({
|
||||||
getPublicKey: schnorrGetPublicKey,
|
getPublicKey: schnorrGetPublicKey,
|
||||||
sign: schnorrSign,
|
sign: schnorrSign,
|
||||||
verify: schnorrVerify,
|
verify: schnorrVerify,
|
||||||
utils: {
|
utils: {
|
||||||
getExtendedPublicKey: schnorrGetExtPubKey,
|
randomPrivateKey: secp256k1.utils.randomPrivateKey,
|
||||||
lift_x,
|
lift_x,
|
||||||
pointToBytes,
|
pointToBytes,
|
||||||
numberToBytesBE,
|
numberToBytesBE,
|
||||||
bytesToNumberBE: bytesToInt,
|
bytesToNumberBE,
|
||||||
taggedHash,
|
taggedHash,
|
||||||
mod,
|
mod,
|
||||||
},
|
},
|
||||||
};
|
}))();
|
||||||
|
|
||||||
const isoMap = htf.isogenyMap(
|
const isoMap = /* @__PURE__ */ (() =>
|
||||||
|
isogenyMap(
|
||||||
Fp,
|
Fp,
|
||||||
[
|
[
|
||||||
// xNum
|
// xNum
|
||||||
@@ -252,14 +245,16 @@ const isoMap = htf.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 = /* @__PURE__ */ (() =>
|
||||||
|
mapToCurveSimpleSWU(Fp, {
|
||||||
A: BigInt('0x3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533'),
|
A: BigInt('0x3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533'),
|
||||||
B: BigInt('1771'),
|
B: BigInt('1771'),
|
||||||
Z: Fp.create(BigInt('-11')),
|
Z: Fp.create(BigInt('-11')),
|
||||||
});
|
}))();
|
||||||
const { hashToCurve, encodeToCurve } = htf.hashToCurve(
|
const htf = /* @__PURE__ */ (() =>
|
||||||
|
createHasher(
|
||||||
secp256k1.ProjectivePoint,
|
secp256k1.ProjectivePoint,
|
||||||
(scalars: bigint[]) => {
|
(scalars: bigint[]) => {
|
||||||
const { x, y } = mapSWU(Fp.create(scalars[0]));
|
const { x, y } = mapSWU(Fp.create(scalars[0]));
|
||||||
@@ -274,5 +269,6 @@ const { hashToCurve, encodeToCurve } = htf.hashToCurve(
|
|||||||
expand: 'xmd',
|
expand: 'xmd',
|
||||||
hash: sha256,
|
hash: sha256,
|
||||||
}
|
}
|
||||||
);
|
))();
|
||||||
export { hashToCurve, encodeToCurve };
|
export const hashToCurve = /* @__PURE__ */ (() => htf.hashToCurve)();
|
||||||
|
export const encodeToCurve = /* @__PURE__ */ (() => htf.encodeToCurve)();
|
||||||
|
|||||||
356
src/stark.ts
356
src/stark.ts
@@ -1,356 +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, ProjPointType } from './abstract/weierstrass.js';
|
|
||||||
import * as cutils from './abstract/utils.js';
|
|
||||||
import { Fp, mod, Field, validateField } from './abstract/modular.js';
|
|
||||||
import { getHash } from './_shortw_utils.js';
|
|
||||||
import * as poseidon from './abstract/poseidon.js';
|
|
||||||
import { utf8ToBytes } from '@noble/hashes/utils';
|
|
||||||
|
|
||||||
type ProjectivePoint = ProjPointType<bigint>;
|
|
||||||
// Stark-friendly elliptic curve
|
|
||||||
// https://docs.starkware.co/starkex/stark-curve.html
|
|
||||||
|
|
||||||
const CURVE_N = BigInt(
|
|
||||||
'3618502788666131213697322783095070105526743751716087489154079457884512865583'
|
|
||||||
);
|
|
||||||
const nBitLength = 252;
|
|
||||||
// Copy-pasted from weierstrass.ts
|
|
||||||
function bits2int(bytes: Uint8Array): bigint {
|
|
||||||
const delta = bytes.length * 8 - nBitLength;
|
|
||||||
const num = cutils.bytesToNumberBE(bytes);
|
|
||||||
return delta > 0 ? num >> BigInt(delta) : num;
|
|
||||||
}
|
|
||||||
function bits2int_modN(bytes: Uint8Array): bigint {
|
|
||||||
return mod(bits2int(bytes), CURVE_N);
|
|
||||||
}
|
|
||||||
export const starkCurve = weierstrass({
|
|
||||||
// Params: a, b
|
|
||||||
a: BigInt(1),
|
|
||||||
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),
|
|
||||||
// Custom truncation routines for stark curve
|
|
||||||
bits2int: (bytes: Uint8Array): bigint => {
|
|
||||||
while (bytes[0] === 0) bytes = bytes.subarray(1);
|
|
||||||
return bits2int(bytes);
|
|
||||||
},
|
|
||||||
bits2int_modN: (bytes: Uint8Array): bigint => {
|
|
||||||
let hashS = cutils.bytesToNumberBE(bytes).toString(16);
|
|
||||||
if (hashS.length === 63) {
|
|
||||||
hashS += '0';
|
|
||||||
bytes = hexToBytes0x(hashS);
|
|
||||||
}
|
|
||||||
// Truncate zero bytes on left (compat with elliptic)
|
|
||||||
while (bytes[0] === 0) bytes = bytes.subarray(1);
|
|
||||||
return bits2int_modN(bytes);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Custom Starknet type conversion functions that can handle 0x and unpadded hex
|
|
||||||
function hexToBytes0x(hex: string): Uint8Array {
|
|
||||||
if (typeof hex !== 'string') {
|
|
||||||
throw new Error('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 Error('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(64, '0');
|
|
||||||
}
|
|
||||||
function getPublicKey0x(privKey: Hex, isCompressed = false) {
|
|
||||||
return starkCurve.getPublicKey(normalizePrivateKey(privKey), isCompressed);
|
|
||||||
}
|
|
||||||
function getSharedSecret0x(privKeyA: Hex, pubKeyB: Hex) {
|
|
||||||
return starkCurve.getSharedSecret(normalizePrivateKey(privKeyA), pubKeyB);
|
|
||||||
}
|
|
||||||
|
|
||||||
function sign0x(msgHash: Hex, privKey: Hex, opts?: any) {
|
|
||||||
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, ProjectivePoint, Signature } = starkCurve;
|
|
||||||
export const utils = starkCurve.utils;
|
|
||||||
export {
|
|
||||||
CURVE,
|
|
||||||
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 sha256Num(cutils.concatBytes(key, hexToBytes0x(indexHex)));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function grindKey(seed: Hex) {
|
|
||||||
const _seed = ensureBytes0x(seed);
|
|
||||||
const sha256mask = 2n ** 256n;
|
|
||||||
|
|
||||||
const limit = sha256mask - mod(sha256mask, CURVE_N);
|
|
||||||
for (let i = 0; ; i++) {
|
|
||||||
const key = hashKeyWithIndex(_seed, i);
|
|
||||||
// key should be in [0, limit)
|
|
||||||
if (key < limit) return mod(key, 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(sha256Num(layer));
|
|
||||||
const applicationNum = int31(sha256Num(application));
|
|
||||||
const eth = hexToNumber0x(ethereumAddress);
|
|
||||||
return `m/2645'/${layerNum}'/${applicationNum}'/${int31(eth)}'/${int31(eth >> 31n)}'/${index}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://docs.starkware.co/starkex/pedersen-hash-function.html
|
|
||||||
const PEDERSEN_POINTS_AFFINE = [
|
|
||||||
new ProjectivePoint(
|
|
||||||
2089986280348253421170679821480865132823066470938446095505822317253594081284n,
|
|
||||||
1713931329540660377023406109199410414810705867260802078187082345529207694986n,
|
|
||||||
1n
|
|
||||||
),
|
|
||||||
new ProjectivePoint(
|
|
||||||
996781205833008774514500082376783249102396023663454813447423147977397232763n,
|
|
||||||
1668503676786377725805489344771023921079126552019160156920634619255970485781n,
|
|
||||||
1n
|
|
||||||
),
|
|
||||||
new ProjectivePoint(
|
|
||||||
2251563274489750535117886426533222435294046428347329203627021249169616184184n,
|
|
||||||
1798716007562728905295480679789526322175868328062420237419143593021674992973n,
|
|
||||||
1n
|
|
||||||
),
|
|
||||||
new ProjectivePoint(
|
|
||||||
2138414695194151160943305727036575959195309218611738193261179310511854807447n,
|
|
||||||
113410276730064486255102093846540133784865286929052426931474106396135072156n,
|
|
||||||
1n
|
|
||||||
),
|
|
||||||
new ProjectivePoint(
|
|
||||||
2379962749567351885752724891227938183011949129833673362440656643086021394946n,
|
|
||||||
776496453633298175483985398648758586525933812536653089401905292063708816422n,
|
|
||||||
1n
|
|
||||||
),
|
|
||||||
];
|
|
||||||
// for (const p of PEDERSEN_POINTS) p._setWindowSize(8);
|
|
||||||
const PEDERSEN_POINTS = PEDERSEN_POINTS_AFFINE;
|
|
||||||
|
|
||||||
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.px === point.px) 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.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 = cutils.bitMask(250);
|
|
||||||
export const keccak = (data: Uint8Array): bigint => bytesToNumber0x(keccak_256(data)) & MASK_250;
|
|
||||||
const sha256Num = (data: Uint8Array | string): bigint => cutils.bytesToNumberBE(sha256(data));
|
|
||||||
|
|
||||||
// Poseidon hash
|
|
||||||
export const Fp253 = Fp(
|
|
||||||
BigInt('14474011154664525231415395255581126252639794253786371766033694892385558855681')
|
|
||||||
); // 2^253 + 2^199 + 1
|
|
||||||
export const Fp251 = Fp(
|
|
||||||
BigInt('3618502788666131213697322783095070105623107215331596699973092056135872020481')
|
|
||||||
); // 2^251 + 17 * 2^192 + 1
|
|
||||||
|
|
||||||
function poseidonRoundConstant(Fp: Field<bigint>, name: string, idx: number) {
|
|
||||||
const val = Fp.fromBytes(sha256(utf8ToBytes(`${name}${idx}`)));
|
|
||||||
return Fp.create(val);
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: doesn't check eiginvalues and possible can create unsafe matrix. But any filtration here will break compatibility with starknet
|
|
||||||
// Please use only if you really know what you doing.
|
|
||||||
// https://eprint.iacr.org/2019/458.pdf Section 2.3 (Avoiding Insecure Matrices)
|
|
||||||
export function _poseidonMDS(Fp: Field<bigint>, name: string, m: number, attempt = 0) {
|
|
||||||
const x_values: bigint[] = [];
|
|
||||||
const y_values: bigint[] = [];
|
|
||||||
for (let i = 0; i < m; i++) {
|
|
||||||
x_values.push(poseidonRoundConstant(Fp, `${name}x`, attempt * m + i));
|
|
||||||
y_values.push(poseidonRoundConstant(Fp, `${name}y`, attempt * m + i));
|
|
||||||
}
|
|
||||||
if (new Set([...x_values, ...y_values]).size !== 2 * m)
|
|
||||||
throw new Error('X and Y values are not distinct');
|
|
||||||
return x_values.map((x) => y_values.map((y) => Fp.inv(Fp.sub(x, y))));
|
|
||||||
}
|
|
||||||
|
|
||||||
const MDS_SMALL = [
|
|
||||||
[3, 1, 1],
|
|
||||||
[1, -1, 1],
|
|
||||||
[1, 1, -2],
|
|
||||||
].map((i) => i.map(BigInt));
|
|
||||||
|
|
||||||
export type PoseidonOpts = {
|
|
||||||
Fp: Field<bigint>;
|
|
||||||
rate: number;
|
|
||||||
capacity: number;
|
|
||||||
roundsFull: number;
|
|
||||||
roundsPartial: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function poseidonBasic(opts: PoseidonOpts, mds: bigint[][]) {
|
|
||||||
validateField(opts.Fp);
|
|
||||||
if (!Number.isSafeInteger(opts.rate) || !Number.isSafeInteger(opts.capacity))
|
|
||||||
throw new Error(`Wrong poseidon opts: ${opts}`);
|
|
||||||
const m = opts.rate + opts.capacity;
|
|
||||||
const rounds = opts.roundsFull + opts.roundsPartial;
|
|
||||||
const roundConstants = [];
|
|
||||||
for (let i = 0; i < rounds; i++) {
|
|
||||||
const row = [];
|
|
||||||
for (let j = 0; j < m; j++) row.push(poseidonRoundConstant(opts.Fp, 'Hades', m * i + j));
|
|
||||||
roundConstants.push(row);
|
|
||||||
}
|
|
||||||
return poseidon.poseidon({
|
|
||||||
...opts,
|
|
||||||
t: m,
|
|
||||||
sboxPower: 3,
|
|
||||||
reversePartialPowIdx: true, // Why?!
|
|
||||||
mds,
|
|
||||||
roundConstants,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function poseidonCreate(opts: PoseidonOpts, mdsAttempt = 0) {
|
|
||||||
const m = opts.rate + opts.capacity;
|
|
||||||
if (!Number.isSafeInteger(mdsAttempt)) throw new Error(`Wrong mdsAttempt=${mdsAttempt}`);
|
|
||||||
return poseidonBasic(opts, _poseidonMDS(opts.Fp, 'HadesMDS', m, mdsAttempt));
|
|
||||||
}
|
|
||||||
|
|
||||||
export const poseidonSmall = poseidonBasic(
|
|
||||||
{ Fp: Fp251, rate: 2, capacity: 1, roundsFull: 8, roundsPartial: 83 },
|
|
||||||
MDS_SMALL
|
|
||||||
);
|
|
||||||
|
|
||||||
export function poseidonHash(x: bigint, y: bigint, fn = poseidonSmall) {
|
|
||||||
return fn([x, y, 2n])[0];
|
|
||||||
}
|
|
||||||
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];
|
||||||
|
}
|
||||||
@@ -1,22 +1,24 @@
|
|||||||
import { deepStrictEqual, throws } from 'assert';
|
import { deepStrictEqual, throws } from 'assert';
|
||||||
import { should, describe } from 'micro-should';
|
import { should, describe } from 'micro-should';
|
||||||
import * as fc from 'fast-check';
|
import * as fc from 'fast-check';
|
||||||
import * as mod from '../lib/esm/abstract/modular.js';
|
import * as mod from '../esm/abstract/modular.js';
|
||||||
import { bytesToHex as toHex } from '../lib/esm/abstract/utils.js';
|
import { bytesToHex as toHex } from '../esm/abstract/utils.js';
|
||||||
// Generic tests for all curves in package
|
// Generic tests for all curves in package
|
||||||
import { secp192r1 } from '../lib/esm/p192.js';
|
import { secp192r1, secp224r1 } from './_more-curves.helpers.js';
|
||||||
import { secp224r1 } from '../lib/esm/p224.js';
|
import { secp256r1 } from '../esm/p256.js';
|
||||||
import { secp256r1 } from '../lib/esm/p256.js';
|
import { secp384r1 } from '../esm/p384.js';
|
||||||
import { secp384r1 } from '../lib/esm/p384.js';
|
import { secp521r1 } from '../esm/p521.js';
|
||||||
import { secp521r1 } from '../lib/esm/p521.js';
|
import { secp256k1 } from '../esm/secp256k1.js';
|
||||||
import { secp256k1 } from '../lib/esm/secp256k1.js';
|
import { ed25519, ed25519ctx, ed25519ph, x25519 } from '../esm/ed25519.js';
|
||||||
import { ed25519, ed25519ctx, ed25519ph, x25519 } from '../lib/esm/ed25519.js';
|
import { ed448, ed448ph } from '../esm/ed448.js';
|
||||||
import { ed448, ed448ph } from '../lib/esm/ed448.js';
|
import { pallas, vesta } from '../esm/pasta.js';
|
||||||
import { starkCurve } from '../lib/esm/stark.js';
|
import { bn254 } from '../esm/bn254.js';
|
||||||
import { pallas, vesta } from '../lib/esm/pasta.js';
|
import { jubjub } from '../esm/jubjub.js';
|
||||||
import { bn254 } from '../lib/esm/bn.js';
|
import { bls12_381 } from '../esm/bls12-381.js';
|
||||||
import { jubjub } from '../lib/esm/jubjub.js';
|
import { default as wyche_curves } from './wycheproof/ec_prime_order_curves_test.json' assert { type: 'json' };
|
||||||
import { bls12_381 } from '../lib/esm/bls12-381.js';
|
import { createCurve } from '../esm/_shortw_utils.js';
|
||||||
|
import { Field } from '../esm/abstract/modular.js';
|
||||||
|
import { sha256 } from '@noble/hashes/sha256';
|
||||||
|
|
||||||
// Fields tests
|
// Fields tests
|
||||||
const FIELDS = {
|
const FIELDS = {
|
||||||
@@ -25,7 +27,6 @@ const FIELDS = {
|
|||||||
secp256r1: { Fp: [secp256r1.CURVE.Fp] },
|
secp256r1: { Fp: [secp256r1.CURVE.Fp] },
|
||||||
secp521r1: { Fp: [secp521r1.CURVE.Fp] },
|
secp521r1: { Fp: [secp521r1.CURVE.Fp] },
|
||||||
secp256k1: { Fp: [secp256k1.CURVE.Fp] },
|
secp256k1: { Fp: [secp256k1.CURVE.Fp] },
|
||||||
stark: { Fp: [starkCurve.CURVE.Fp] },
|
|
||||||
jubjub: { Fp: [jubjub.CURVE.Fp] },
|
jubjub: { Fp: [jubjub.CURVE.Fp] },
|
||||||
ed25519: { Fp: [ed25519.CURVE.Fp] },
|
ed25519: { Fp: [ed25519.CURVE.Fp] },
|
||||||
ed448: { Fp: [ed448.CURVE.Fp] },
|
ed448: { Fp: [ed448.CURVE.Fp] },
|
||||||
@@ -33,19 +34,19 @@ const FIELDS = {
|
|||||||
pallas: { Fp: [pallas.CURVE.Fp] },
|
pallas: { Fp: [pallas.CURVE.Fp] },
|
||||||
vesta: { Fp: [vesta.CURVE.Fp] },
|
vesta: { Fp: [vesta.CURVE.Fp] },
|
||||||
bls12: {
|
bls12: {
|
||||||
Fp: [bls12_381.CURVE.Fp],
|
Fp: [bls12_381.fields.Fp],
|
||||||
Fp2: [
|
Fp2: [
|
||||||
bls12_381.CURVE.Fp2,
|
bls12_381.fields.Fp2,
|
||||||
fc.array(fc.bigInt(1n, bls12_381.CURVE.Fp.ORDER - 1n), {
|
fc.array(fc.bigInt(1n, bls12_381.fields.Fp.ORDER - 1n), {
|
||||||
minLength: 2,
|
minLength: 2,
|
||||||
maxLength: 2,
|
maxLength: 2,
|
||||||
}),
|
}),
|
||||||
(Fp2, num) => Fp2.fromBigTuple([num[0], num[1]]),
|
(Fp2, num) => Fp2.fromBigTuple([num[0], num[1]]),
|
||||||
],
|
],
|
||||||
// Fp6: [bls12_381.CURVE.Fp6],
|
// Fp6: [bls12_381.fields.Fp6],
|
||||||
Fp12: [
|
Fp12: [
|
||||||
bls12_381.CURVE.Fp12,
|
bls12_381.fields.Fp12,
|
||||||
fc.array(fc.bigInt(1n, bls12_381.CURVE.Fp.ORDER - 1n), {
|
fc.array(fc.bigInt(1n, bls12_381.fields.Fp.ORDER - 1n), {
|
||||||
minLength: 12,
|
minLength: 12,
|
||||||
maxLength: 12,
|
maxLength: 12,
|
||||||
}),
|
}),
|
||||||
@@ -224,7 +225,7 @@ for (const c in FIELDS) {
|
|||||||
|
|
||||||
const isSquare = mod.FpIsSquare(Fp);
|
const isSquare = mod.FpIsSquare(Fp);
|
||||||
// Not implemented
|
// Not implemented
|
||||||
if (Fp !== bls12_381.CURVE.Fp12) {
|
if (Fp !== bls12_381.fields.Fp12) {
|
||||||
should('multiply/sqrt', () => {
|
should('multiply/sqrt', () => {
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(FC_BIGINT, (num) => {
|
fc.property(FC_BIGINT, (num) => {
|
||||||
@@ -315,7 +316,6 @@ const CURVES = {
|
|||||||
secp256k1,
|
secp256k1,
|
||||||
ed25519, ed25519ctx, ed25519ph,
|
ed25519, ed25519ctx, ed25519ph,
|
||||||
ed448, ed448ph,
|
ed448, ed448ph,
|
||||||
starkCurve,
|
|
||||||
pallas, vesta,
|
pallas, vesta,
|
||||||
bn254,
|
bn254,
|
||||||
jubjub,
|
jubjub,
|
||||||
@@ -557,6 +557,7 @@ for (const name in CURVES) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
describe(name, () => {
|
describe(name, () => {
|
||||||
|
if (['bn254', 'pallas', 'vesta'].includes(name)) return;
|
||||||
// Generic complex things (getPublicKey/sign/verify/getSharedSecret)
|
// Generic complex things (getPublicKey/sign/verify/getSharedSecret)
|
||||||
should('.getPublicKey() type check', () => {
|
should('.getPublicKey() type check', () => {
|
||||||
throws(() => C.getPublicKey(0), '0');
|
throws(() => C.getPublicKey(0), '0');
|
||||||
@@ -745,6 +746,54 @@ should('bigInt private keys', () => {
|
|||||||
secp256k1.sign('', 123n);
|
secp256k1.sign('', 123n);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('wycheproof curve creation', () => {
|
||||||
|
const VECTORS = wyche_curves.testGroups[0].tests;
|
||||||
|
for (const v of VECTORS) {
|
||||||
|
should(`${v.name}`, () => {
|
||||||
|
const CURVE = createCurve(
|
||||||
|
{
|
||||||
|
Fp: Field(BigInt(`0x${v.p}`)),
|
||||||
|
a: BigInt(`0x${v.a}`),
|
||||||
|
b: BigInt(`0x${v.b}`),
|
||||||
|
n: BigInt(`0x${v.n}`),
|
||||||
|
h: BigInt(v.h),
|
||||||
|
Gx: BigInt(`0x${v.gx}`),
|
||||||
|
Gy: BigInt(`0x${v.gy}`),
|
||||||
|
},
|
||||||
|
sha256
|
||||||
|
);
|
||||||
|
});
|
||||||
|
const CURVE = CURVES[v.name];
|
||||||
|
if (!CURVE) continue;
|
||||||
|
should(`${v.name} parms verify`, () => {
|
||||||
|
deepStrictEqual(CURVE.CURVE.Fp.ORDER, BigInt(`0x${v.p}`));
|
||||||
|
deepStrictEqual(CURVE.CURVE.a, BigInt(`0x${v.a}`));
|
||||||
|
deepStrictEqual(CURVE.CURVE.b, BigInt(`0x${v.b}`));
|
||||||
|
deepStrictEqual(CURVE.CURVE.n, BigInt(`0x${v.n}`));
|
||||||
|
deepStrictEqual(CURVE.CURVE.Gx, BigInt(`0x${v.gx}`));
|
||||||
|
deepStrictEqual(CURVE.CURVE.Gy, BigInt(`0x${v.gy}`));
|
||||||
|
deepStrictEqual(CURVE.CURVE.h, BigInt(v.h));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
should('validate generator point is on curve', () => {
|
||||||
|
throws(() =>
|
||||||
|
createCurve(
|
||||||
|
{
|
||||||
|
Fp: Field(BigInt(`0x00c302f41d932a36cda7a3463093d18db78fce476de1a86297`)),
|
||||||
|
a: BigInt(`0x00c302f41d932a36cda7a3463093d18db78fce476de1a86294`),
|
||||||
|
b: BigInt(`0x13d56ffaec78681e68f9deb43b35bec2fb68542e27897b79`),
|
||||||
|
n: BigInt(`0x00c302f41d932a36cda7a3462f9e9e916b5be8f1029ac4acc1`),
|
||||||
|
h: BigInt(1),
|
||||||
|
Gx: BigInt(`0x3ae9e58c82f63c30282e1fe7bbf43fa72c446af6f4618129`),
|
||||||
|
Gy: BigInt(`0x097e2c5667c2223a902ab5ca449d0084b7e5b3de7ccc01c8`), // last 9 -> 8
|
||||||
|
},
|
||||||
|
sha256
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
// ESM is broken.
|
// ESM is broken.
|
||||||
import url from 'url';
|
import url from 'url';
|
||||||
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
|
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
|
||||||
|
|||||||
@@ -1,18 +1,20 @@
|
|||||||
import { bls12_381 } from '../lib/esm/bls12-381.js';
|
|
||||||
import { describe, should } from 'micro-should';
|
|
||||||
import { deepStrictEqual, notDeepStrictEqual, throws } from 'assert';
|
import { deepStrictEqual, notDeepStrictEqual, throws } from 'assert';
|
||||||
import { sha512 } from '@noble/hashes/sha512';
|
|
||||||
import * as fc from 'fast-check';
|
import * as fc from 'fast-check';
|
||||||
import { readFileSync } from 'fs';
|
import { readFileSync } from 'fs';
|
||||||
|
import { describe, should } from 'micro-should';
|
||||||
|
import { wNAF } from '../esm/abstract/curve.js';
|
||||||
|
import { bytesToHex, utf8ToBytes } from '../esm/abstract/utils.js';
|
||||||
|
import { hash_to_field } from '../esm/abstract/hash-to-curve.js';
|
||||||
|
import { bls12_381 as bls, bls12_381 } from '../esm/bls12-381.js';
|
||||||
|
|
||||||
|
import * as utils from '../esm/abstract/utils.js';
|
||||||
|
|
||||||
import zkVectors from './bls12-381/zkcrypto/converted.json' assert { type: 'json' };
|
import zkVectors from './bls12-381/zkcrypto/converted.json' assert { type: 'json' };
|
||||||
import pairingVectors from './bls12-381/go_pairing_vectors/pairing.json' assert { type: 'json' };
|
import pairingVectors from './bls12-381/go_pairing_vectors/pairing.json' assert { type: 'json' };
|
||||||
import { wNAF } from '../lib/esm/abstract/curve.js';
|
const G1_VECTORS = readFileSync('./test/bls12-381/bls12-381-g1-test-vectors.txt', 'utf-8')
|
||||||
const bls = bls12_381;
|
.trim()
|
||||||
const { Fp2 } = bls;
|
.split('\n')
|
||||||
const G1Point = bls.G1.ProjectivePoint;
|
.map((l) => l.split(':'));
|
||||||
const G2Point = bls.G2.ProjectivePoint;
|
|
||||||
const G1Aff = (x, y) => G1Point.fromAffine({ x, y });
|
|
||||||
|
|
||||||
const G2_VECTORS = readFileSync('./test/bls12-381/bls12-381-g2-test-vectors.txt', 'utf-8')
|
const G2_VECTORS = readFileSync('./test/bls12-381/bls12-381-g2-test-vectors.txt', 'utf-8')
|
||||||
.trim()
|
.trim()
|
||||||
.split('\n')
|
.split('\n')
|
||||||
@@ -28,8 +30,10 @@ const SCALAR_VECTORS = readFileSync('./test/bls12-381/bls12-381-scalar-test-vect
|
|||||||
const NUM_RUNS = Number(process.env.RUNS_COUNT || 10); // reduce to 1 to shorten test time
|
const NUM_RUNS = Number(process.env.RUNS_COUNT || 10); // reduce to 1 to shorten test time
|
||||||
fc.configureGlobal({ numRuns: NUM_RUNS });
|
fc.configureGlobal({ numRuns: NUM_RUNS });
|
||||||
|
|
||||||
// @ts-ignore
|
const G1Point = bls.G1.ProjectivePoint;
|
||||||
const CURVE_ORDER = bls.CURVE.r;
|
const G2Point = bls.G2.ProjectivePoint;
|
||||||
|
const G1Aff = (x, y) => G1Point.fromAffine({ x, y });
|
||||||
|
const CURVE_ORDER = bls.params.r;
|
||||||
|
|
||||||
const FC_MSG = fc.hexaString({ minLength: 64, maxLength: 64 });
|
const FC_MSG = fc.hexaString({ minLength: 64, maxLength: 64 });
|
||||||
const FC_MSG_5 = fc.array(FC_MSG, { minLength: 5, maxLength: 5 });
|
const FC_MSG_5 = fc.array(FC_MSG, { minLength: 5, maxLength: 5 });
|
||||||
@@ -39,14 +43,19 @@ const B_192_40 = '40'.padEnd(192, '0');
|
|||||||
const B_384_40 = '40'.padEnd(384, '0'); // [0x40, 0, 0...]
|
const B_384_40 = '40'.padEnd(384, '0'); // [0x40, 0, 0...]
|
||||||
|
|
||||||
const getPubKey = (priv) => bls.getPublicKey(priv);
|
const getPubKey = (priv) => bls.getPublicKey(priv);
|
||||||
|
function replaceZeroPoint(item) {
|
||||||
|
const zeros = '0000000000000000000000000000000000000000000000000000000000000000';
|
||||||
|
const ones = '1000000000000000000000000000000000000000000000000000000000000001';
|
||||||
|
return item === zeros ? ones : item;
|
||||||
|
}
|
||||||
|
|
||||||
function equal(a, b, comment) {
|
function equal(a, b, comment) {
|
||||||
deepStrictEqual(a.equals(b), true, `eq(${comment})`);
|
deepStrictEqual(a.equals(b), true, `eq(${comment})`);
|
||||||
}
|
}
|
||||||
|
const { Fp, Fp2 } = bls.fields;
|
||||||
|
|
||||||
// Fp
|
// Fp
|
||||||
describe('bls12-381 Fp', () => {
|
describe('bls12-381 Fp', () => {
|
||||||
const Fp = bls.Fp;
|
|
||||||
const FC_BIGINT = fc.bigInt(1n, Fp.ORDER - 1n);
|
const FC_BIGINT = fc.bigInt(1n, Fp.ORDER - 1n);
|
||||||
|
|
||||||
should('multiply/sqrt', () => {
|
should('multiply/sqrt', () => {
|
||||||
@@ -61,8 +70,7 @@ describe('bls12-381 Fp', () => {
|
|||||||
|
|
||||||
// Fp2
|
// Fp2
|
||||||
describe('bls12-381 Fp2', () => {
|
describe('bls12-381 Fp2', () => {
|
||||||
const Fp = bls.Fp;
|
const { Fp, Fp2 } = bls.fields;
|
||||||
const Fp2 = bls.Fp2;
|
|
||||||
const FC_BIGINT = fc.bigInt(1n, Fp.ORDER - 1n);
|
const FC_BIGINT = fc.bigInt(1n, Fp.ORDER - 1n);
|
||||||
const FC_BIGINT_2 = fc.array(FC_BIGINT, { minLength: 2, maxLength: 2 });
|
const FC_BIGINT_2 = fc.array(FC_BIGINT, { minLength: 2, maxLength: 2 });
|
||||||
|
|
||||||
@@ -150,7 +158,7 @@ describe('bls12-381 Fp2', () => {
|
|||||||
|
|
||||||
// Point
|
// Point
|
||||||
describe('bls12-381 Point', () => {
|
describe('bls12-381 Point', () => {
|
||||||
const Fp = bls.Fp;
|
const { Fp } = bls.fields;
|
||||||
const FC_BIGINT = fc.bigInt(1n, Fp.ORDER - 1n);
|
const FC_BIGINT = fc.bigInt(1n, Fp.ORDER - 1n);
|
||||||
const PointG1 = G1Point;
|
const PointG1 = G1Point;
|
||||||
const PointG2 = G2Point;
|
const PointG2 = G2Point;
|
||||||
@@ -558,9 +566,12 @@ describe('bls12-381 Point', () => {
|
|||||||
];
|
];
|
||||||
// Use wNAF allow scalars higher than CURVE.r
|
// Use wNAF allow scalars higher than CURVE.r
|
||||||
const w = wNAF(G2Point, 1);
|
const w = wNAF(G2Point, 1);
|
||||||
|
const hEff = BigInt(
|
||||||
|
'0xbc69f08f2ee75b3584c6a0ea91b352888e2a8e9145ad7689986ff031508ffe1329c2f178731db956d82bf015d1212b02ec0ec69d7477c1ae954cbc06689f6a359894c0adebbf6b4e8020005aaa95551'
|
||||||
|
);
|
||||||
for (let p of points) {
|
for (let p of points) {
|
||||||
const ours = p.clearCofactor();
|
const ours = p.clearCofactor();
|
||||||
const shouldBe = w.unsafeLadder(p, bls.CURVE.G2.hEff);
|
const shouldBe = w.unsafeLadder(p, hEff);
|
||||||
deepStrictEqual(ours.equals(shouldBe), true, 'clearLast');
|
deepStrictEqual(ours.equals(shouldBe), true, 'clearLast');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -578,12 +589,12 @@ describe('bls12-381/basic', () => {
|
|||||||
deepStrictEqual(g1.x, G1Point.ZERO.x);
|
deepStrictEqual(g1.x, G1Point.ZERO.x);
|
||||||
deepStrictEqual(g1.y, G1Point.ZERO.y);
|
deepStrictEqual(g1.y, G1Point.ZERO.y);
|
||||||
// Test Non-Zero
|
// Test Non-Zero
|
||||||
const x = bls.Fp.create(
|
const x = Fp.create(
|
||||||
BigInt(
|
BigInt(
|
||||||
'0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb'
|
'0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb'
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
const y = bls.Fp.create(
|
const y = Fp.create(
|
||||||
BigInt(
|
BigInt(
|
||||||
'0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1'
|
'0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1'
|
||||||
)
|
)
|
||||||
@@ -604,12 +615,12 @@ describe('bls12-381/basic', () => {
|
|||||||
deepStrictEqual(g1.x, G1Point.ZERO.x);
|
deepStrictEqual(g1.x, G1Point.ZERO.x);
|
||||||
deepStrictEqual(g1.y, G1Point.ZERO.y);
|
deepStrictEqual(g1.y, G1Point.ZERO.y);
|
||||||
// Test Non-Zero
|
// Test Non-Zero
|
||||||
const x = bls.Fp.create(
|
const x = Fp.create(
|
||||||
BigInt(
|
BigInt(
|
||||||
'0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb'
|
'0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb'
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
const y = bls.Fp.create(
|
const y = Fp.create(
|
||||||
BigInt(
|
BigInt(
|
||||||
'0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1'
|
'0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1'
|
||||||
)
|
)
|
||||||
@@ -690,12 +701,12 @@ describe('bls12-381/basic', () => {
|
|||||||
// Test Zero
|
// Test Zero
|
||||||
deepStrictEqual(G1Point.ZERO.toHex(false), B_192_40);
|
deepStrictEqual(G1Point.ZERO.toHex(false), B_192_40);
|
||||||
// Test Non-Zero
|
// Test Non-Zero
|
||||||
const x = bls.Fp.create(
|
const x = Fp.create(
|
||||||
BigInt(
|
BigInt(
|
||||||
'0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb'
|
'0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb'
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
const y = bls.Fp.create(
|
const y = Fp.create(
|
||||||
BigInt(
|
BigInt(
|
||||||
'0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1'
|
'0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1'
|
||||||
)
|
)
|
||||||
@@ -711,12 +722,12 @@ describe('bls12-381/basic', () => {
|
|||||||
// Test Zero
|
// Test Zero
|
||||||
deepStrictEqual(G1Point.ZERO.toHex(false), B_192_40);
|
deepStrictEqual(G1Point.ZERO.toHex(false), B_192_40);
|
||||||
// Test Non-Zero
|
// Test Non-Zero
|
||||||
const x = bls.Fp.create(
|
const x = Fp.create(
|
||||||
BigInt(
|
BigInt(
|
||||||
'0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb'
|
'0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb'
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
const y = bls.Fp.create(
|
const y = Fp.create(
|
||||||
BigInt(
|
BigInt(
|
||||||
'0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1'
|
'0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1'
|
||||||
)
|
)
|
||||||
@@ -802,32 +813,32 @@ describe('bls12-381/basic', () => {
|
|||||||
throws(() => G2Point.fromPrivateKey(0n));
|
throws(() => G2Point.fromPrivateKey(0n));
|
||||||
});
|
});
|
||||||
const VALID_G1 = new G1Point(
|
const VALID_G1 = new G1Point(
|
||||||
bls.Fp.create(
|
Fp.create(
|
||||||
3609742242174788176010452839163620388872641749536604986743596621604118973777515189035770461528205168143692110933639n
|
3609742242174788176010452839163620388872641749536604986743596621604118973777515189035770461528205168143692110933639n
|
||||||
),
|
),
|
||||||
bls.Fp.create(
|
Fp.create(
|
||||||
1619277690257184054444116778047375363103842303863153349133480657158810226683757397206929105479676799650932070320089n
|
1619277690257184054444116778047375363103842303863153349133480657158810226683757397206929105479676799650932070320089n
|
||||||
),
|
),
|
||||||
bls.Fp.create(1n)
|
Fp.create(1n)
|
||||||
);
|
);
|
||||||
const VALID_G1_2 = new G1Point(
|
const VALID_G1_2 = new G1Point(
|
||||||
bls.Fp.create(
|
Fp.create(
|
||||||
1206972466279728255044019580914616126536509750250979180256809997983196363639429409634110400978470384566664128085207n
|
1206972466279728255044019580914616126536509750250979180256809997983196363639429409634110400978470384566664128085207n
|
||||||
),
|
),
|
||||||
bls.Fp.create(
|
Fp.create(
|
||||||
2991142246317096160788653339959532007292638191110818490939476869616372888657136539642598243964263069435065725313423n
|
2991142246317096160788653339959532007292638191110818490939476869616372888657136539642598243964263069435065725313423n
|
||||||
),
|
),
|
||||||
bls.Fp.create(1n)
|
Fp.create(1n)
|
||||||
);
|
);
|
||||||
|
|
||||||
const INVALID_G1 = new G1Point(
|
const INVALID_G1 = new G1Point(
|
||||||
bls.Fp.create(
|
Fp.create(
|
||||||
499001545268060011619089734015590154568173930614466321429631711131511181286230338880376679848890024401335766847607n
|
499001545268060011619089734015590154568173930614466321429631711131511181286230338880376679848890024401335766847607n
|
||||||
),
|
),
|
||||||
bls.Fp.create(
|
Fp.create(
|
||||||
3934582309586258715640230772291917282844636728991757779640464479794033391537662970190753981664259511166946374555673n
|
3934582309586258715640230772291917282844636728991757779640464479794033391537662970190753981664259511166946374555673n
|
||||||
),
|
),
|
||||||
bls.Fp.create(1n)
|
Fp.create(1n)
|
||||||
);
|
);
|
||||||
|
|
||||||
should('aggregate pubkeys', () => {
|
should('aggregate pubkeys', () => {
|
||||||
@@ -847,24 +858,31 @@ describe('bls12-381/basic', () => {
|
|||||||
});
|
});
|
||||||
// should aggregate signatures
|
// should aggregate signatures
|
||||||
|
|
||||||
|
should(`produce correct short signatures (${G1_VECTORS.length} vectors)`, () => {
|
||||||
|
for (let vector of G1_VECTORS) {
|
||||||
|
const [priv, msg, expected] = vector;
|
||||||
|
const sig = bls.signShortSignature(msg, priv);
|
||||||
|
deepStrictEqual(bytesToHex(sig), expected);
|
||||||
|
}
|
||||||
|
});
|
||||||
should(`produce correct signatures (${G2_VECTORS.length} vectors)`, () => {
|
should(`produce correct signatures (${G2_VECTORS.length} vectors)`, () => {
|
||||||
for (let vector of G2_VECTORS) {
|
for (let vector of G2_VECTORS) {
|
||||||
const [priv, msg, expected] = vector;
|
const [priv, msg, expected] = vector;
|
||||||
const sig = bls.sign(msg, priv);
|
const sig = bls.sign(msg, priv);
|
||||||
deepStrictEqual(bls.utils.bytesToHex(sig), expected);
|
deepStrictEqual(bytesToHex(sig), expected);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
should(`produce correct scalars (${SCALAR_VECTORS.length} vectors)`, () => {
|
should(`produce correct scalars (${SCALAR_VECTORS.length} vectors)`, () => {
|
||||||
const options = {
|
const options = {
|
||||||
p: bls.CURVE.r,
|
p: bls.params.r,
|
||||||
m: 1,
|
m: 1,
|
||||||
expand: false,
|
expand: '_internal_pass',
|
||||||
};
|
};
|
||||||
for (let vector of SCALAR_VECTORS) {
|
for (let vector of SCALAR_VECTORS) {
|
||||||
const [okmAscii, expectedHex] = vector;
|
const [okmAscii, expectedHex] = vector;
|
||||||
const expected = BigInt('0x' + expectedHex);
|
const expected = BigInt('0x' + expectedHex);
|
||||||
const okm = new Uint8Array(okmAscii.split('').map((c) => c.charCodeAt(0)));
|
const okm = utf8ToBytes(okmAscii);
|
||||||
const scalars = bls.utils.hashToField(okm, 1, options);
|
const scalars = hash_to_field(okm, 1, Object.assign({}, bls.G2.CURVE.htfDefaults, options));
|
||||||
deepStrictEqual(scalars[0][0], expected);
|
deepStrictEqual(scalars[0][0], expected);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -872,7 +890,8 @@ describe('bls12-381/basic', () => {
|
|||||||
|
|
||||||
// Pairing
|
// Pairing
|
||||||
describe('pairing', () => {
|
describe('pairing', () => {
|
||||||
const { pairing, Fp12 } = bls;
|
const { pairing } = bls;
|
||||||
|
const { Fp12 } = bls.fields;
|
||||||
const G1 = G1Point.BASE;
|
const G1 = G1Point.BASE;
|
||||||
const G2 = G2Point.BASE;
|
const G2 = G2Point.BASE;
|
||||||
|
|
||||||
@@ -969,29 +988,29 @@ describe('pairing', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
// hashToCurve
|
// hashToCurve
|
||||||
describe('hash-to-curve', () => {
|
describe('hash-to-curve (against Killic)', () => {
|
||||||
// Point G1
|
// Point G1
|
||||||
const VECTORS_G1 = [
|
const VECTORS_G1 = [
|
||||||
{
|
{
|
||||||
msg: bls.utils.stringToBytes(''),
|
msg: utf8ToBytes(''),
|
||||||
expected:
|
expected:
|
||||||
'0576730ab036cbac1d95b38dca905586f28d0a59048db4e8778782d89bff856ddef89277ead5a21e2975c4a6e3d8c79e' +
|
'0576730ab036cbac1d95b38dca905586f28d0a59048db4e8778782d89bff856ddef89277ead5a21e2975c4a6e3d8c79e' +
|
||||||
'1273e568bebf1864393c517f999b87c1eaa1b8432f95aea8160cd981b5b05d8cd4a7cf00103b6ef87f728e4b547dd7ae',
|
'1273e568bebf1864393c517f999b87c1eaa1b8432f95aea8160cd981b5b05d8cd4a7cf00103b6ef87f728e4b547dd7ae',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
msg: bls.utils.stringToBytes('abc'),
|
msg: utf8ToBytes('abc'),
|
||||||
expected:
|
expected:
|
||||||
'061daf0cc00d8912dac1d4cf5a7c32fca97f8b3bf3f805121888e5eb89f77f9a9f406569027ac6d0e61b1229f42c43d6' +
|
'061daf0cc00d8912dac1d4cf5a7c32fca97f8b3bf3f805121888e5eb89f77f9a9f406569027ac6d0e61b1229f42c43d6' +
|
||||||
'0de1601e5ba02cb637c1d35266f5700acee9850796dc88e860d022d7b9e7e3dce5950952e97861e5bb16d215c87f030d',
|
'0de1601e5ba02cb637c1d35266f5700acee9850796dc88e860d022d7b9e7e3dce5950952e97861e5bb16d215c87f030d',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
msg: bls.utils.stringToBytes('abcdef0123456789'),
|
msg: utf8ToBytes('abcdef0123456789'),
|
||||||
expected:
|
expected:
|
||||||
'0fb3455436843e76079c7cf3dfef75e5a104dfe257a29a850c145568d500ad31ccfe79be9ae0ea31a722548070cf98cd' +
|
'0fb3455436843e76079c7cf3dfef75e5a104dfe257a29a850c145568d500ad31ccfe79be9ae0ea31a722548070cf98cd' +
|
||||||
'177989f7e2c751658df1b26943ee829d3ebcf131d8f805571712f3a7527ee5334ecff8a97fc2a50cea86f5e6212e9a57',
|
'177989f7e2c751658df1b26943ee829d3ebcf131d8f805571712f3a7527ee5334ecff8a97fc2a50cea86f5e6212e9a57',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
msg: bls.utils.stringToBytes(
|
msg: utf8ToBytes(
|
||||||
'a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
|
'a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
|
||||||
),
|
),
|
||||||
expected:
|
expected:
|
||||||
@@ -999,37 +1018,38 @@ describe('hash-to-curve', () => {
|
|||||||
'047a85d6898416a0899e26219bca7c4f0fa682717199de196b02b95eaf9fb55456ac3b810e78571a1b7f5692b7c58ab6',
|
'047a85d6898416a0899e26219bca7c4f0fa682717199de196b02b95eaf9fb55456ac3b810e78571a1b7f5692b7c58ab6',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
describe('hashToCurve G1', () => {
|
||||||
for (let i = 0; i < VECTORS_G1.length; i++) {
|
for (let i = 0; i < VECTORS_G1.length; i++) {
|
||||||
const t = VECTORS_G1[i];
|
const t = VECTORS_G1[i];
|
||||||
should(`hashToCurve/G1 Killic (${i})`, () => {
|
should(`${i}`, () => {
|
||||||
const p = bls.hashToCurve.G1.hashToCurve(t.msg, {
|
const p = bls.G1.hashToCurve(t.msg, {
|
||||||
DST: 'BLS12381G1_XMD:SHA-256_SSWU_RO_TESTGEN',
|
DST: 'BLS12381G1_XMD:SHA-256_SSWU_RO_TESTGEN',
|
||||||
});
|
});
|
||||||
deepStrictEqual(p.toHex(false), t.expected);
|
deepStrictEqual(p.toHex(false), t.expected);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
});
|
||||||
const VECTORS_ENCODE_G1 = [
|
const VECTORS_ENCODE_G1 = [
|
||||||
{
|
{
|
||||||
msg: bls.utils.stringToBytes(''),
|
msg: utf8ToBytes(''),
|
||||||
expected:
|
expected:
|
||||||
'1223effdbb2d38152495a864d78eee14cb0992d89a241707abb03819a91a6d2fd65854ab9a69e9aacb0cbebfd490732c' +
|
'1223effdbb2d38152495a864d78eee14cb0992d89a241707abb03819a91a6d2fd65854ab9a69e9aacb0cbebfd490732c' +
|
||||||
'0f925d61e0b235ecd945cbf0309291878df0d06e5d80d6b84aa4ff3e00633b26f9a7cb3523ef737d90e6d71e8b98b2d5',
|
'0f925d61e0b235ecd945cbf0309291878df0d06e5d80d6b84aa4ff3e00633b26f9a7cb3523ef737d90e6d71e8b98b2d5',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
msg: bls.utils.stringToBytes('abc'),
|
msg: utf8ToBytes('abc'),
|
||||||
expected:
|
expected:
|
||||||
'179d3fd0b4fb1da43aad06cea1fb3f828806ddb1b1fa9424b1e3944dfdbab6e763c42636404017da03099af0dcca0fd6' +
|
'179d3fd0b4fb1da43aad06cea1fb3f828806ddb1b1fa9424b1e3944dfdbab6e763c42636404017da03099af0dcca0fd6' +
|
||||||
'0d037cb1c6d495c0f5f22b061d23f1be3d7fe64d3c6820cfcd99b6b36fa69f7b4c1f4addba2ae7aa46fb25901ab483e4',
|
'0d037cb1c6d495c0f5f22b061d23f1be3d7fe64d3c6820cfcd99b6b36fa69f7b4c1f4addba2ae7aa46fb25901ab483e4',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
msg: bls.utils.stringToBytes('abcdef0123456789'),
|
msg: utf8ToBytes('abcdef0123456789'),
|
||||||
expected:
|
expected:
|
||||||
'15aa66c77eded1209db694e8b1ba49daf8b686733afaa7b68c683d0b01788dfb0617a2e2d04c0856db4981921d3004af' +
|
'15aa66c77eded1209db694e8b1ba49daf8b686733afaa7b68c683d0b01788dfb0617a2e2d04c0856db4981921d3004af' +
|
||||||
'0952bb2f61739dd1d201dd0a79d74cda3285403d47655ee886afe860593a8a4e51c5b77a22d2133e3a4280eaaaa8b788',
|
'0952bb2f61739dd1d201dd0a79d74cda3285403d47655ee886afe860593a8a4e51c5b77a22d2133e3a4280eaaaa8b788',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
msg: bls.utils.stringToBytes(
|
msg: utf8ToBytes(
|
||||||
'a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
|
'a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
|
||||||
),
|
),
|
||||||
expected:
|
expected:
|
||||||
@@ -1037,19 +1057,21 @@ describe('hash-to-curve', () => {
|
|||||||
'094bfdfe3e552447433b5a00967498a3f1314b86ce7a7164c8a8f4131f99333b30a574607e301d5f774172c627fd0bca',
|
'094bfdfe3e552447433b5a00967498a3f1314b86ce7a7164c8a8f4131f99333b30a574607e301d5f774172c627fd0bca',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
describe('encodeToCurve G1', () => {
|
||||||
for (let i = 0; i < VECTORS_ENCODE_G1.length; i++) {
|
for (let i = 0; i < VECTORS_ENCODE_G1.length; i++) {
|
||||||
const t = VECTORS_ENCODE_G1[i];
|
const t = VECTORS_ENCODE_G1[i];
|
||||||
should(`hashToCurve/G1 (Killic, encodeToCurve) (${i})`, () => {
|
should(`(${i})`, () => {
|
||||||
const p = bls.hashToCurve.G1.encodeToCurve(t.msg, {
|
const p = bls.G1.encodeToCurve(t.msg, {
|
||||||
DST: 'BLS12381G1_XMD:SHA-256_SSWU_NU_TESTGEN',
|
DST: 'BLS12381G1_XMD:SHA-256_SSWU_NU_TESTGEN',
|
||||||
});
|
});
|
||||||
deepStrictEqual(p.toHex(false), t.expected);
|
deepStrictEqual(p.toHex(false), t.expected);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
});
|
||||||
// Point G2
|
// Point G2
|
||||||
const VECTORS_G2 = [
|
const VECTORS_G2 = [
|
||||||
{
|
{
|
||||||
msg: bls.utils.stringToBytes(''),
|
msg: utf8ToBytes(''),
|
||||||
expected:
|
expected:
|
||||||
'0fbdae26f9f9586a46d4b0b70390d09064ef2afe5c99348438a3c7d9756471e015cb534204c1b6824617a85024c772dc' +
|
'0fbdae26f9f9586a46d4b0b70390d09064ef2afe5c99348438a3c7d9756471e015cb534204c1b6824617a85024c772dc' +
|
||||||
'0a650bd36ae7455cb3fe5d8bb1310594551456f5c6593aec9ee0c03d2f6cb693bd2c5e99d4e23cbaec767609314f51d3' +
|
'0a650bd36ae7455cb3fe5d8bb1310594551456f5c6593aec9ee0c03d2f6cb693bd2c5e99d4e23cbaec767609314f51d3' +
|
||||||
@@ -1057,7 +1079,7 @@ describe('hash-to-curve', () => {
|
|||||||
'0d8d49e7737d8f9fc5cef7c4b8817633103faf2613016cb86a1f3fc29968fe2413e232d9208d2d74a89bf7a48ac36f83',
|
'0d8d49e7737d8f9fc5cef7c4b8817633103faf2613016cb86a1f3fc29968fe2413e232d9208d2d74a89bf7a48ac36f83',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
msg: bls.utils.stringToBytes('abc'),
|
msg: utf8ToBytes('abc'),
|
||||||
expected:
|
expected:
|
||||||
'03578447618463deb106b60e609c6f7cc446dc6035f84a72801ba17c94cd800583b493b948eff0033f09086fdd7f6175' +
|
'03578447618463deb106b60e609c6f7cc446dc6035f84a72801ba17c94cd800583b493b948eff0033f09086fdd7f6175' +
|
||||||
'1953ce6d4267939c7360756d9cca8eb34aac4633ef35369a7dc249445069888e7d1b3f9d2e75fbd468fbcbba7110ea02' +
|
'1953ce6d4267939c7360756d9cca8eb34aac4633ef35369a7dc249445069888e7d1b3f9d2e75fbd468fbcbba7110ea02' +
|
||||||
@@ -1065,7 +1087,7 @@ describe('hash-to-curve', () => {
|
|||||||
'0882ab045b8fe4d7d557ebb59a63a35ac9f3d312581b509af0f8eaa2960cbc5e1e36bb969b6e22980b5cbdd0787fcf4e',
|
'0882ab045b8fe4d7d557ebb59a63a35ac9f3d312581b509af0f8eaa2960cbc5e1e36bb969b6e22980b5cbdd0787fcf4e',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
msg: bls.utils.stringToBytes('abcdef0123456789'),
|
msg: utf8ToBytes('abcdef0123456789'),
|
||||||
expected:
|
expected:
|
||||||
'195fad48982e186ce3c5c82133aefc9b26d55979b6f530992a8849d4263ec5d57f7a181553c8799bcc83da44847bdc8d' +
|
'195fad48982e186ce3c5c82133aefc9b26d55979b6f530992a8849d4263ec5d57f7a181553c8799bcc83da44847bdc8d' +
|
||||||
'17b461fc3b96a30c2408958cbfa5f5927b6063a8ad199d5ebf2d7cdeffa9c20c85487204804fab53f950b2f87db365aa' +
|
'17b461fc3b96a30c2408958cbfa5f5927b6063a8ad199d5ebf2d7cdeffa9c20c85487204804fab53f950b2f87db365aa' +
|
||||||
@@ -1073,7 +1095,7 @@ describe('hash-to-curve', () => {
|
|||||||
'174a3473a3af2d0302b9065e895ca4adba4ece6ce0b41148ba597001abb152f852dd9a96fb45c9de0a43d944746f833e',
|
'174a3473a3af2d0302b9065e895ca4adba4ece6ce0b41148ba597001abb152f852dd9a96fb45c9de0a43d944746f833e',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
msg: bls.utils.stringToBytes(
|
msg: utf8ToBytes(
|
||||||
'a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
|
'a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
|
||||||
),
|
),
|
||||||
expected:
|
expected:
|
||||||
@@ -1083,19 +1105,20 @@ describe('hash-to-curve', () => {
|
|||||||
'15c1d4f1a685bb63ee67ca1fd96155e3d091e852a684b78d085fd34f6091e5249ddddbdcf2e7ec82ce6c04c63647eeb7',
|
'15c1d4f1a685bb63ee67ca1fd96155e3d091e852a684b78d085fd34f6091e5249ddddbdcf2e7ec82ce6c04c63647eeb7',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
describe('hashToCurve G2', () => {
|
||||||
for (let i = 0; i < VECTORS_G2.length; i++) {
|
for (let i = 0; i < VECTORS_G2.length; i++) {
|
||||||
const t = VECTORS_G2[i];
|
const t = VECTORS_G2[i];
|
||||||
should(`hashToCurve/G2 Killic (${i})`, () => {
|
should(`${i}`, () => {
|
||||||
const p = bls.hashToCurve.G2.hashToCurve(t.msg, {
|
const p = bls.G2.hashToCurve(t.msg, {
|
||||||
DST: 'BLS12381G2_XMD:SHA-256_SSWU_RO_TESTGEN',
|
DST: 'BLS12381G2_XMD:SHA-256_SSWU_RO_TESTGEN',
|
||||||
});
|
});
|
||||||
deepStrictEqual(p.toHex(false), t.expected);
|
deepStrictEqual(p.toHex(false), t.expected);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
});
|
||||||
const VECTORS_ENCODE_G2 = [
|
const VECTORS_ENCODE_G2 = [
|
||||||
{
|
{
|
||||||
msg: bls.utils.stringToBytes(''),
|
msg: utf8ToBytes(''),
|
||||||
expected:
|
expected:
|
||||||
'0d4333b77becbf9f9dfa3ca928002233d1ecc854b1447e5a71f751c9042d000f42db91c1d6649a5e0ad22bd7bf7398b8' +
|
'0d4333b77becbf9f9dfa3ca928002233d1ecc854b1447e5a71f751c9042d000f42db91c1d6649a5e0ad22bd7bf7398b8' +
|
||||||
'027e4bfada0b47f9f07e04aec463c7371e68f2fd0c738cd517932ea3801a35acf09db018deda57387b0f270f7a219e4d' +
|
'027e4bfada0b47f9f07e04aec463c7371e68f2fd0c738cd517932ea3801a35acf09db018deda57387b0f270f7a219e4d' +
|
||||||
@@ -1103,7 +1126,7 @@ describe('hash-to-curve', () => {
|
|||||||
'053674cba9ef516ddc218fedb37324e6c47de27f88ab7ef123b006127d738293c0277187f7e2f80a299a24d84ed03da7',
|
'053674cba9ef516ddc218fedb37324e6c47de27f88ab7ef123b006127d738293c0277187f7e2f80a299a24d84ed03da7',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
msg: bls.utils.stringToBytes('abc'),
|
msg: utf8ToBytes('abc'),
|
||||||
expected:
|
expected:
|
||||||
'18f0f87b40af67c056915dbaf48534c592524e82c1c2b50c3734d02c0172c80df780a60b5683759298a3303c5d942778' +
|
'18f0f87b40af67c056915dbaf48534c592524e82c1c2b50c3734d02c0172c80df780a60b5683759298a3303c5d942778' +
|
||||||
'09349f1cb5b2e55489dcd45a38545343451cc30a1681c57acd4fb0a6db125f8352c09f4a67eb7d1d8242cb7d3405f97b' +
|
'09349f1cb5b2e55489dcd45a38545343451cc30a1681c57acd4fb0a6db125f8352c09f4a67eb7d1d8242cb7d3405f97b' +
|
||||||
@@ -1111,7 +1134,7 @@ describe('hash-to-curve', () => {
|
|||||||
'02f2d9deb2c7742512f5b8230bf0fd83ea42279d7d39779543c1a43b61c885982b611f6a7a24b514995e8a098496b811',
|
'02f2d9deb2c7742512f5b8230bf0fd83ea42279d7d39779543c1a43b61c885982b611f6a7a24b514995e8a098496b811',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
msg: bls.utils.stringToBytes('abcdef0123456789'),
|
msg: utf8ToBytes('abcdef0123456789'),
|
||||||
expected:
|
expected:
|
||||||
'19808ec5930a53c7cf5912ccce1cc33f1b3dcff24a53ce1cc4cba41fd6996dbed4843ccdd2eaf6a0cd801e562718d163' +
|
'19808ec5930a53c7cf5912ccce1cc33f1b3dcff24a53ce1cc4cba41fd6996dbed4843ccdd2eaf6a0cd801e562718d163' +
|
||||||
'149fe43777d34f0d25430dea463889bd9393bdfb4932946db23671727081c629ebb98a89604f3433fba1c67d356a4af7' +
|
'149fe43777d34f0d25430dea463889bd9393bdfb4932946db23671727081c629ebb98a89604f3433fba1c67d356a4af7' +
|
||||||
@@ -1119,7 +1142,7 @@ describe('hash-to-curve', () => {
|
|||||||
'04c0d6793a766233b2982087b5f4a254f261003ccb3262ea7c50903eecef3e871d1502c293f9e063d7d293f6384f4551',
|
'04c0d6793a766233b2982087b5f4a254f261003ccb3262ea7c50903eecef3e871d1502c293f9e063d7d293f6384f4551',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
msg: bls.utils.stringToBytes(
|
msg: utf8ToBytes(
|
||||||
'a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
|
'a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
|
||||||
),
|
),
|
||||||
expected:
|
expected:
|
||||||
@@ -1129,15 +1152,17 @@ describe('hash-to-curve', () => {
|
|||||||
'09e5c8242dd7281ad32c03fe4af3f19167770016255fb25ad9b67ec51d62fade31a1af101e8f6172ec2ee8857662be3a',
|
'09e5c8242dd7281ad32c03fe4af3f19167770016255fb25ad9b67ec51d62fade31a1af101e8f6172ec2ee8857662be3a',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
describe('encodeToCurve G2', () => {
|
||||||
for (let i = 0; i < VECTORS_ENCODE_G2.length; i++) {
|
for (let i = 0; i < VECTORS_ENCODE_G2.length; i++) {
|
||||||
const t = VECTORS_ENCODE_G2[i];
|
const t = VECTORS_ENCODE_G2[i];
|
||||||
should(`hashToCurve/G2 (Killic, encodeToCurve) (${i})`, () => {
|
should(`${i}`, () => {
|
||||||
const p = bls.hashToCurve.G2.encodeToCurve(t.msg, {
|
const p = bls.G2.encodeToCurve(t.msg, {
|
||||||
DST: 'BLS12381G2_XMD:SHA-256_SSWU_NU_TESTGEN',
|
DST: 'BLS12381G2_XMD:SHA-256_SSWU_NU_TESTGEN',
|
||||||
});
|
});
|
||||||
deepStrictEqual(p.toHex(false), t.expected);
|
deepStrictEqual(p.toHex(false), t.expected);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('verify()', () => {
|
describe('verify()', () => {
|
||||||
@@ -1148,6 +1173,8 @@ describe('verify()', () => {
|
|||||||
const pub = bls.getPublicKey(priv);
|
const pub = bls.getPublicKey(priv);
|
||||||
const res = bls.verify(sig, msg, pub);
|
const res = bls.verify(sig, msg, pub);
|
||||||
deepStrictEqual(res, true, `${priv}-${msg}`);
|
deepStrictEqual(res, true, `${priv}-${msg}`);
|
||||||
|
const resHex = bls.verify(bytesToHex(sig), msg, pub);
|
||||||
|
deepStrictEqual(resHex, true, `${priv}-${msg}-hex`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
should('not verify signature with wrong message', () => {
|
should('not verify signature with wrong message', () => {
|
||||||
@@ -1168,6 +1195,43 @@ describe('verify()', () => {
|
|||||||
const invPub = bls.getPublicKey(invPriv);
|
const invPub = bls.getPublicKey(invPriv);
|
||||||
const res = bls.verify(sig, msg, invPub);
|
const res = bls.verify(sig, msg, invPub);
|
||||||
deepStrictEqual(res, false);
|
deepStrictEqual(res, false);
|
||||||
|
const resHex = bls.verify(bytesToHex(sig), msg, invPub);
|
||||||
|
deepStrictEqual(resHex, false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
should('verify signed message (short signatures)', () => {
|
||||||
|
for (let i = 0; i < NUM_RUNS; i++) {
|
||||||
|
const [priv, msg] = G1_VECTORS[i];
|
||||||
|
const sig = bls.signShortSignature(msg, priv);
|
||||||
|
const pub = bls.getPublicKeyForShortSignatures(priv);
|
||||||
|
const res = bls.verifyShortSignature(sig, msg, pub);
|
||||||
|
deepStrictEqual(res, true, `${priv}-${msg}`);
|
||||||
|
const resHex = bls.verifyShortSignature(bytesToHex(sig), msg, pub);
|
||||||
|
deepStrictEqual(resHex, true, `${priv}-${msg}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
should('not verify signature with wrong message (short signatures)', () => {
|
||||||
|
for (let i = 0; i < NUM_RUNS; i++) {
|
||||||
|
const [priv, msg] = G1_VECTORS[i];
|
||||||
|
const invMsg = G1_VECTORS[i + 1][1];
|
||||||
|
const sig = bls.signShortSignature(msg, priv);
|
||||||
|
const pub = bls.getPublicKeyForShortSignatures(priv);
|
||||||
|
const res = bls.verifyShortSignature(sig, invMsg, pub);
|
||||||
|
deepStrictEqual(res, false);
|
||||||
|
const resHex = bls.verifyShortSignature(bytesToHex(sig), invMsg, pub);
|
||||||
|
deepStrictEqual(resHex, false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
should('not verify signature with wrong key', () => {
|
||||||
|
for (let i = 0; i < NUM_RUNS; i++) {
|
||||||
|
const [priv, msg] = G1_VECTORS[i];
|
||||||
|
const sig = bls.signShortSignature(msg, priv);
|
||||||
|
const invPriv = G1_VECTORS[i + 1][1].padStart(64, '0');
|
||||||
|
const invPub = bls.getPublicKeyForShortSignatures(invPriv);
|
||||||
|
const res = bls.verifyShortSignature(sig, msg, invPub);
|
||||||
|
deepStrictEqual(res, false);
|
||||||
|
const resHex = bls.verifyShortSignature(bytesToHex(sig), msg, invPub);
|
||||||
|
deepStrictEqual(resHex, false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
describe('batch', () => {
|
describe('batch', () => {
|
||||||
@@ -1180,6 +1244,10 @@ describe('verify()', () => {
|
|||||||
const signatures = messages.map((message, i) => bls.sign(message, privateKeys[i]));
|
const signatures = messages.map((message, i) => bls.sign(message, privateKeys[i]));
|
||||||
const aggregatedSignature = bls.aggregateSignatures(signatures);
|
const aggregatedSignature = bls.aggregateSignatures(signatures);
|
||||||
deepStrictEqual(bls.verifyBatch(aggregatedSignature, messages, publicKey), true);
|
deepStrictEqual(bls.verifyBatch(aggregatedSignature, messages, publicKey), true);
|
||||||
|
deepStrictEqual(
|
||||||
|
bls.verifyBatch(bytesToHex(aggregatedSignature), messages, publicKey),
|
||||||
|
true
|
||||||
|
);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -1198,6 +1266,10 @@ describe('verify()', () => {
|
|||||||
bls.verifyBatch(aggregatedSignature, wrongMessages, publicKey),
|
bls.verifyBatch(aggregatedSignature, wrongMessages, publicKey),
|
||||||
messages.every((m, i) => m === wrongMessages[i])
|
messages.every((m, i) => m === wrongMessages[i])
|
||||||
);
|
);
|
||||||
|
deepStrictEqual(
|
||||||
|
bls.verifyBatch(bytesToHex(aggregatedSignature), wrongMessages, publicKey),
|
||||||
|
messages.every((m, i) => m === wrongMessages[i])
|
||||||
|
);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -1220,6 +1292,10 @@ describe('verify()', () => {
|
|||||||
bls.verifyBatch(aggregatedSignature, messages, wrongPublicKeys),
|
bls.verifyBatch(aggregatedSignature, messages, wrongPublicKeys),
|
||||||
wrongPrivateKeys.every((p, i) => p === privateKeys[i])
|
wrongPrivateKeys.every((p, i) => p === privateKeys[i])
|
||||||
);
|
);
|
||||||
|
deepStrictEqual(
|
||||||
|
bls.verifyBatch(bytesToHex(aggregatedSignature), messages, wrongPublicKeys),
|
||||||
|
wrongPrivateKeys.every((p, i) => p === privateKeys[i])
|
||||||
|
);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@@ -1227,17 +1303,23 @@ describe('verify()', () => {
|
|||||||
should('verify multi-signature as simple signature', () => {
|
should('verify multi-signature as simple signature', () => {
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(FC_MSG, FC_BIGINT_5, (message, privateKeys) => {
|
fc.property(FC_MSG, FC_BIGINT_5, (message, privateKeys) => {
|
||||||
|
message = replaceZeroPoint(message);
|
||||||
const publicKey = privateKeys.map(getPubKey);
|
const publicKey = privateKeys.map(getPubKey);
|
||||||
const signatures = privateKeys.map((privateKey) => bls.sign(message, privateKey));
|
const signatures = privateKeys.map((privateKey) => bls.sign(message, privateKey));
|
||||||
const aggregatedSignature = bls.aggregateSignatures(signatures);
|
const aggregatedSignature = bls.aggregateSignatures(signatures);
|
||||||
const aggregatedPublicKey = bls.aggregatePublicKeys(publicKey);
|
const aggregatedPublicKey = bls.aggregatePublicKeys(publicKey);
|
||||||
deepStrictEqual(bls.verify(aggregatedSignature, message, aggregatedPublicKey), true);
|
deepStrictEqual(bls.verify(aggregatedSignature, message, aggregatedPublicKey), true);
|
||||||
|
deepStrictEqual(
|
||||||
|
bls.verify(bytesToHex(aggregatedSignature), message, aggregatedPublicKey),
|
||||||
|
true
|
||||||
|
);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
should('not verify wrong multi-signature as simple signature', () => {
|
should('not verify wrong multi-signature as simple signature', () => {
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(FC_MSG, FC_MSG, FC_BIGINT_5, (message, wrongMessage, privateKeys) => {
|
fc.property(FC_MSG, FC_MSG, FC_BIGINT_5, (message, wrongMessage, privateKeys) => {
|
||||||
|
message = replaceZeroPoint(message);
|
||||||
const publicKey = privateKeys.map(getPubKey);
|
const publicKey = privateKeys.map(getPubKey);
|
||||||
const signatures = privateKeys.map((privateKey) => bls.sign(message, privateKey));
|
const signatures = privateKeys.map((privateKey) => bls.sign(message, privateKey));
|
||||||
const aggregatedSignature = bls.aggregateSignatures(signatures);
|
const aggregatedSignature = bls.aggregateSignatures(signatures);
|
||||||
@@ -1246,6 +1328,10 @@ describe('verify()', () => {
|
|||||||
bls.verify(aggregatedSignature, wrongMessage, aggregatedPublicKey),
|
bls.verify(aggregatedSignature, wrongMessage, aggregatedPublicKey),
|
||||||
message === wrongMessage
|
message === wrongMessage
|
||||||
);
|
);
|
||||||
|
deepStrictEqual(
|
||||||
|
bls.verify(bytesToHex(aggregatedSignature), wrongMessage, aggregatedPublicKey),
|
||||||
|
message === wrongMessage
|
||||||
|
);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -1260,12 +1346,12 @@ describe('bls12-381 deterministic', () => {
|
|||||||
.reverse()
|
.reverse()
|
||||||
.reduce((acc, i) => acc + i);
|
.reduce((acc, i) => acc + i);
|
||||||
|
|
||||||
const Fp12 = bls.Fp12;
|
const { Fp12 } = bls.fields;
|
||||||
|
|
||||||
should('Killic based/Pairing', () => {
|
should('Killic based/Pairing', () => {
|
||||||
const t = bls.pairing(G1Point.BASE, G2Point.BASE);
|
const t = bls.pairing(G1Point.BASE, G2Point.BASE);
|
||||||
deepStrictEqual(
|
deepStrictEqual(
|
||||||
bls.utils.bytesToHex(Fp12.toBytes(t)),
|
bytesToHex(Fp12.toBytes(t)),
|
||||||
killicHex([
|
killicHex([
|
||||||
'0f41e58663bf08cf068672cbd01a7ec73baca4d72ca93544deff686bfd6df543d48eaa24afe47e1efde449383b676631',
|
'0f41e58663bf08cf068672cbd01a7ec73baca4d72ca93544deff686bfd6df543d48eaa24afe47e1efde449383b676631',
|
||||||
'04c581234d086a9902249b64728ffd21a189e87935a954051c7cdba7b3872629a4fafc05066245cb9108f0242d0fe3ef',
|
'04c581234d086a9902249b64728ffd21a189e87935a954051c7cdba7b3872629a4fafc05066245cb9108f0242d0fe3ef',
|
||||||
@@ -1287,7 +1373,7 @@ describe('bls12-381 deterministic', () => {
|
|||||||
let p2 = G2Point.BASE;
|
let p2 = G2Point.BASE;
|
||||||
for (let v of pairingVectors) {
|
for (let v of pairingVectors) {
|
||||||
deepStrictEqual(
|
deepStrictEqual(
|
||||||
bls.utils.bytesToHex(Fp12.toBytes(bls.pairing(p1, p2))),
|
bytesToHex(Fp12.toBytes(bls.pairing(p1, p2))),
|
||||||
// Reverse order
|
// Reverse order
|
||||||
v.match(/.{96}/g).reverse().join('')
|
v.match(/.{96}/g).reverse().join('')
|
||||||
);
|
);
|
||||||
@@ -1361,6 +1447,37 @@ describe('bls12-381 deterministic', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
should(`zkcrypt/G1 & G2 encoding edge cases`, () => {
|
||||||
|
const Fp = bls12_381.fields.Fp;
|
||||||
|
const S_BIT_POS = Fp.BITS; // C_bit, compression bit for serialization flag
|
||||||
|
const I_BIT_POS = Fp.BITS + 1; // I_bit, point-at-infinity bit for serialization flag
|
||||||
|
const C_BIT_POS = Fp.BITS + 2; // S_bit, sort bit for serialization flag
|
||||||
|
const VECTORS = [
|
||||||
|
{ pos: C_BIT_POS, shift: 7 }, // compression_flag_set = Choice::from((bytes[0] >> 7) & 1);
|
||||||
|
{ pos: I_BIT_POS, shift: 6 }, // infinity_flag_set = Choice::from((bytes[0] >> 6) & 1)
|
||||||
|
{ pos: S_BIT_POS, shift: 5 }, // sort_flag_set = Choice::from((bytes[0] >> 5) & 1)
|
||||||
|
];
|
||||||
|
for (const { pos, shift } of VECTORS) {
|
||||||
|
const d = utils.numberToBytesBE(utils.bitSet(0n, pos, Boolean(true)), Fp.BYTES);
|
||||||
|
deepStrictEqual((d[0] >> shift) & 1, 1, `${pos}`);
|
||||||
|
}
|
||||||
|
const baseC = G1Point.BASE.toRawBytes();
|
||||||
|
deepStrictEqual(baseC.length, 48);
|
||||||
|
const baseU = G1Point.BASE.toRawBytes(false);
|
||||||
|
deepStrictEqual(baseU.length, 96);
|
||||||
|
const compressedBit = baseU.slice();
|
||||||
|
compressedBit[0] |= 0b1000_0000; // add compression bit
|
||||||
|
throws(() => G1Point.fromHex(compressedBit), 'compressed bit'); // uncompressed point with compressed length
|
||||||
|
const uncompressedBit = baseC.slice();
|
||||||
|
uncompressedBit[0] &= 0b0111_1111; // remove compression bit
|
||||||
|
throws(() => G1Point.fromHex(uncompressedBit), 'uncompressed bit');
|
||||||
|
const infinityUncompressed = baseU.slice();
|
||||||
|
infinityUncompressed[0] |= 0b0100_0000;
|
||||||
|
throws(() => G1Point.fromHex(compressedBit), 'infinity uncompressed');
|
||||||
|
const infinityCompressed = baseC.slice();
|
||||||
|
infinityCompressed[0] |= 0b0100_0000;
|
||||||
|
throws(() => G1Point.fromHex(compressedBit), 'infinity compressed');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// ESM is broken.
|
// ESM is broken.
|
||||||
|
|||||||
128
test/bls12-381/bls12-381-g1-test-vectors.txt
Normal file
128
test/bls12-381/bls12-381-g1-test-vectors.txt
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
25d8cef413ba263e8d5732d3fca51fd369db74712655a5fd7b0b3a58d8095be8::800134e27aacc74dc91153a6bd65f96a5f8c8365c722da2f1e12eb048e0aed6987fa4168a51241ce41434fd05fd4bdd9
|
||||||
|
611810ebd8f5a7faad47b2249f9d13be0506131db987b6948f1ca3194fa6b643:68:94250c0cc62ae9041c6f6e5042202b3c327991ce4b2841a4145d270f6c8311bc95673c826ada72a6d69e92a833d649e6
|
||||||
|
419bb1de76e11a476f8d5cc5d85a648ec04f24bf75f6cf1f3fae43e57bf9a491:c8d0:898f660c5b26e8c9461ab3f42eb394465d5a115702c05d2a2bc761a8873ac0f33d21f9ea9cf4c435cd31391f5c8c0a91
|
||||||
|
0d1bd9077705325666408124339dca98c0c842b35a90bc3cea8e0c36f2d35583:c43623:94f60dc44a4dbb2505befe346c0c143190fc877ded5e877418f0f890b8ae357a40e8fcc189139aaa509d2b6500f623a5
|
||||||
|
50ff7bd9b21916e55debbd0757e945386b6159ef481d9d774ee67d9b07d0e4ed:7e846556:8d8d9e84012c9c0958018202fe944b4517b618cb7df0b61b1f1ce40b43c2da6330ee0c30a37ac6c7ba0f16aeaa5b99db
|
||||||
|
29a8af03f8c73c64e14807cdabae877cb0f273169bc5ebf17f3e4ef334690656:ce8e5953d7:b37528df8825b94349cfe90a8c8665915cc49e6c41d78e28f8f1a05c5956ee9af850b82e9be756f024e396fd85d9b1ca
|
||||||
|
732fbcaef0e216eae6420eff93c68e3547267b69ca48c7ae9d79d481a466fab9:ae9eb5f425ee:b0adf372fe871a5f7efd30ba8f4ea563460a14651b903789324b78fe12c06b23569766c2d7eecdfb734de4485fee2436
|
||||||
|
4a8135a8847019dad5c1f1b609b50ee72bf5e6459f9c4206ce43de04c2a7103a:01a6d7836c68ad:914a38d1fa13ffdff56cbadd1bd77a3108aae19f76ff2a99d18784cf5c7620d44543045d757f61bdd4fa66780b25eb46
|
||||||
|
0681339753344b5a346aeec93a9b3b9d1282d620a3cdfc4fb4f0e7a075a99fa0:c335129a7fa17398:a01d6f24c038ebc110d742babcc9dd0a32eb518e1e52fac73a3e0a3395012e708112e86a314649aa1edf90dc51007042
|
||||||
|
67a4cad01442be6649e8f3de5b14d126baee62c7525ac61e0b2fe3387e7681b5:bb8a6f6e15fce1f262:82aa70654ca48c6ef55ce3edc88ee77922e1064c763aa50fb0d4a2e8b206d4e14ed849b4d175b096481a6afac232c588
|
||||||
|
1e36e3af518a276dedb69eb0e9df882721116cdb336f692eb691a6d2c7f2ec15:5fbb696cc48ea826a789:8a8f9a764916f3fd5b6cc882f9869ebd1d6a24a057a6e436509c916a9a1e9308e5f891e8e49f39afa0e9afbd3d209cb5
|
||||||
|
5984b05cfa8100d150b3a9a0a0c1e2be149a09e2ff6218b0648651f82b4e773e:5d1bf1b69e2774fdb03500:91703b32c962a8bda991561258c29cb726fea6300742cfe37ed929f68087638169750a423b5c4b465f5498b64ec660ce
|
||||||
|
4d84a172794eeeda6217cf4d10fa36f1b21103742926d4948845a8a0e417d13f:8ec1a5032dfff9289fffefaa:92142c1955d234c700373b823ab4b4b308897218096a88ea504267b26c9330b939191c72e770aaed0af3281b418af173
|
||||||
|
4de6bd9f522e6edf20d0e54cc17cc22f558f115b58478ae6155291e67c28e096:3a5e5a964d6acedcae7b23189a:838d07c7d28c62b1e5aadf8c621c4f360407b3124ac7f7ae3a40a56b1b848b8104f59b4d74e278639e35f4ffa64a3767
|
||||||
|
64fe9caf26b773198b6700a23c2618d36c7382440339a60236e210fc7f61ade3:8873768b317b84b32fca283a4082:b9fb8bc5cfda68938437c17e9f5cb448ca4bb79be278d8f1eac42b9f9b03039673c3170af211c24d7006d1af522f805e
|
||||||
|
2b5bf5af15c13c167173ac0b4750a27cd36ebb90cb0ee90d6168fc81eea0c30a:0a93cd89817651705b4fd414054a44:a2216d350329553d3adc81d0d79d000c0edf634443dafe7c292e8f7193d09facf7a6361e02e5df957021429ae306d879
|
||||||
|
507bf8c00a3364d9f297b3df5f523fc786806b3fc60123d8b231831af5dabce5:691cec082f50711675043ed04233437d:aabdfee018464bba85027210a8a8322f9de64b452d296f31bc8c507c05289b70e8642fb2b6aaa33759d857ece7735231
|
||||||
|
46b4cd59574a6c1f845d2a57d41c42096db5d53ffc9ec8d4b080c1542e24f30b:9a877c2bef8fd2ef71b2852e35afde4912:88eaa4631bb68b510601e6b099376adfe05c1eed52757611897935974d82bcc2751036723bcbfaf29c7f8c09bfd93b1f
|
||||||
|
3e04f740b39a8792e414144a3cb0c8816350f5c4744cf6569f258fc9df82a7d9:e5781adf4d2c0501969d2669619934145ffe:92989055e334649063b2242af5890a445a3d9f5fedcc127318da402d3c68f0ef658d0dc0074579218e02e31cb5ece4f2
|
||||||
|
5ff00a071e807e2beee582b790e4f37ea23211273008e37ca683b34632546b90:75659a273db98e3b14cd464cdfb217823f0496:9721a974cdd68946477db08a4221db9b9c2ca07c01b1daf7307ffcaa16603ee9f12e0ea5e446af292bde7b21f5eb4d3d
|
||||||
|
59c6a3ac6fef4c048486c141825a539e5b65ecc5f0a4425c4aa1015735928f0b:d26e8b19e6065dbdf5a7a50954fcf52e046e4a79:8be8f6ec5b39a43b6d24967009b0444a4c30ad57f285ad737b632a963aa3e9511f6e6ef27a502071bea00c4b653fe01d
|
||||||
|
053840cde56e2fab07d92aad4ca4126db0c79b582ae5b6074336f10faa1ce27d:18277161538b41f2116b62f1b4f15f763db71bc95a:a0350c66b6c72e8e745ecacd973aca076c110b218e0275b34976e7e23a3c834b260256227dbd8902e8454bb1d620e92c
|
||||||
|
4dae7ee2946935ab3799f54a67f7f3a1ede349786ac2169c0d4b66bee8659c88:ff0bc27c426ba610feb7b8d7262d27314884f9e98438:b6b4f053da80dc91f5311b1ff12c3de305b0dca42a84818d600644a2e9955afd4f68f61415ba5e4f6684e33da0fc8071
|
||||||
|
2b36bee50f49a23bf9a01d51e5b67acd8dcca3efb310708a742d93e1cb7089ef:5d9bb535996b1a0158f988ad523cdaeb934a69b043b84a:ad4ffad27a2d8e375549a7cdbfea761712f5b422fbc6d619a2de7b985f4c401b524905adb067bf40e1f1cda75a99edbf
|
||||||
|
364aa3c72c66b518e1bb9f28febcfa56f29ca5825fc8f1bb60792703124a7638:cbe2e518132f31a040fdde8e4665130d05aa7ff233d12bc6:902dca0a36d551de11ad849888e93d91023493c6a87ebe242ffba54e299747f8a122d015135a70a2473dd83f401d88bc
|
||||||
|
49a257e61ae16bf1f02cd6e81333ec3dc3b509843a56267ed0d40191c44a823b:df0079e080bbe83a8c3255fbdd26bf143c174ddcf80c969e4b:a3b5027454751a60c5a1204e8b25fe88c278ae2f29449e77c91766feb55ad81c978562e60c00084ff8244b1d8c02a2cd
|
||||||
|
4f7443821af744a272e3fae9ac7350d6344d61d47bdecd23587f794cfe29758a:e8be2bbfa55b3767413a37778a1940104c4a941e018daca7e3b8:963af9ffa575e398bbbba35e68e8a99d8ba77c870f98a6dfe5b48a283a61ccb19071121398669418f2ebbfa910782f29
|
||||||
|
22144d627be8fdea6127df0dcc8b17a141a41b44041548bd367840e372c8de90:a4c9b9b49bf2267674fd979e1eecce161cf13b5042d4ad769b45ad:86eeca7b5d03763d96bece0a865e315d260db6b6d728519bad150ef1d086bd78848994ed769da6f8ecf5ef99550588d0
|
||||||
|
25682a90cf7d1672adc57afe312e0695039d130dc2fa052174d1754dd6bf6f2c:939f9bcd0063da5bba708f16520a7ded65857ac4824e79ab1d6acc8d:a9b56b5df672c163219eb68807c39911bfdd00a6d413f6f1ee75da967017b2ef9ab99528345ed9af70bccc30b49b424d
|
||||||
|
6f4dc51d61fc10750a53a448b0177ab4ddd4727bc3690031615ef5f3ee9a37fd:b64ca223bac328283a133a74cf95ae4b14d6d68784115dba9af1a14e55:b7c5f00708da137f5e8e90b13cf9b41305173f1a616a31f69b37b89d2832d0956e1e6da88838969ce67d90f49c5d4f2e
|
||||||
|
5ca692a6163c55c4945758e4640c46ff0ca34fb870cdee9e067a22b0c0bdabed:bcff7f9acc78a02edb0e163769d6bc4e3a97e9bd3677b98a68d82c6d3b90:93d2e2d57cfe2502ac87207653bad819fe1c13cb321dc34ac074aaf647f3b1637d00f99a3ff0cb527465d45f6be31809
|
||||||
|
2b617ae10e1dc16e41fae911b14e9c150196912a4e89e20981ebcf472b4dd5dc:fc4d92a56983fc61b38b9a8b10abf5f2e914100ce449d4ff8e0ad586e7314d:864b93049434dd8c32b29c8c164b9ce286772da4e61be06b009c4fdba74f9915545cfe005602cabf6b9dfe76e084f0b6
|
||||||
|
3d12a889a4c1cb6066919f7b97086faecaa640580c43a9df4b8263160177a94f:3f0660a2f3dbc7e532fb7961b7cd00e4b95f5a44702e6e19a04321bbd4fedc02:93070b7ff8f81c45cefa10209107e37d567ae22aa92a45ba1e7a922eab7c58f5c7ef7a77881c8be9de29fad6a3ba3081
|
||||||
|
012d893034dbeb4cd2f4a7e7afa16e885e7139ffb2770f4d508ce187ebd01e1a:ae6c892504739f742ca90a2d94b84e1092e63288f220a5a75829ea83c49cacd031:a910f08de82e243ae5feeac46648d19f51b10046959bed0a887b7a2e2e4d9c5c0791c6ee9769c81a85efdcbf51c408d0
|
||||||
|
417e34572c751f1f4cddb3fa89f48640d9471e857e2424a701aa8d7283ba72d1:0f942d30f2b1090003d0faa02a8b1f4fc14500e93ef0df241d0996c7e4711ccac3a8:a3c9cf0e9e9cffdf97ef6fd8c57c60fbf5175d01b6943923eee9c12861d059bcef315c40791e8952861fe3c04f65c203
|
||||||
|
2a4bc53e3c5dd8d2c46ce784b52db7a66b1509a80103329364b78c5243e3b52a:f387978fe8bb746d7500f470ebb28bcad43501780fad6a8dc116052f93831a205b4116:abcbc4a6f49ef189dab4205790c23c0053474d9a1b02bce3979017137566d21eb2b5ce05b7f9bce8be73654ef582349d
|
||||||
|
231fd161a30aae15d7069ea9e81e06bd8a43c483468f8095cfb4b255128df5ec:dd1e2f364fcb24ad18349e07d6f74353cbd48def87b6a8a7147f3d0a461882a61a9fc77d:b9328c61b63b045372ec8fec0541cc70eada8d99414934a385680d5d3c98dd1aae317cd030c7372c1150c117a405335b
|
||||||
|
0e6dc865b8ceacd9e9e1edf3e146a00de60c08aab08dde3cb200fefc24e41eb0:b19a008107d7d89d804ad8a6cae7c039e3d003fd40b93adc746fbee76af5bbf299076482c1:8ac945e4ce9bcbd1042df2d4f29574fedcdca79d25d2358de9acf2ef860c0fc0e528d13c311e6119b73024bfbdd1ff36
|
||||||
|
41c6a0777609d976880906dcfeee73104a93f8527a23c78d5d7f7917401183db:47766621f49ec5c8235c30275ae2a92a615435d29f6bae651bdc90082a6738741e76ce43a3d2:ad5e2a36345fe3e3f6781e8936dafbd6ca0b0371ea59ba1ecb7091a6c40ede7547a82fc8a28b13bdb06a948446542e4d
|
||||||
|
6e6f8e2f4652e14aaa4ef111d7fc8be7ac4d8ada4d051caa52465a4345181990:8a266363f67331b303b5c4594e222a343ae7f5512d94a6df766d3212d1ad4ec2ddd88e62d88c51:a9c21fb52afa29e1c4a8f993e0cf6327023a1fe00db739bb915d9ab3e3238205bda9bc8b6be2f9f87cc29ff69bff9233
|
||||||
|
256a2028788ae24683db9af7d8d976782cfa323ceaf5db0e62272c222c83d331:23204fef64b612a246c470551a58b7e3c4b8ae558edd55c118001ba74ce4c11d22831683f597169e:a382ec60cea2596f472b8805b0271a0978a125c680d523a1f2d4291fffb01a3aa5d22bfe62ca439573525065fd6ec885
|
||||||
|
37b73537fcbb0f6b8bff910fcc0116d905f0960a2233a564d4cbe0b4c53f88ab:43ac6d7da7b0a419d6c893e9ccdbf3b891ec5ca9460fd70d9b2fb6dc9bd482c835af88922d74e4ef38:aafcf57e5db3c378a4d37bfa461ed23113bfa95fa9aece77a4569cb836cc86d311800e9425448c5d0d4302fe180810d1
|
||||||
|
700fb2aa7050df22fede481f8fbc24a937812ddd19dc19404351e2b5c72dc21d:381d1c6c2357c8fa5a07865e5dd0f76f5c4d63d115a49c24a7302d4cd66117683e549be5796ecd16fc56:b4409c7e09d8c79dc2f7a083a23cb02e83ba3c8f4af8ccfca70ad4ce90991333e4ff742fc912afb0de93610c1ec83261
|
||||||
|
56dbc180d43e8688bece1a617d284f2d3880e570650a3f260e9a3abae32c2c3c:7f62eaa50b2ea4288c03ecbbb42a8178aa1289bd1dcd9bb1664be0b8cf971b023b5e29cf47dbeb779f0098:a3fdcffe61c67de9a482e4fccf42ddb9344c8aa4fc3733ff711750287ae87329a82e235c5f9954a8cc5015ce7877ab9a
|
||||||
|
30a003ea75cc507f2b0861d68af83522b451976fbf9f71c6be340ab4b96bc0d2:bf5f878d46a8fa3e3a476fd161a86d053cea93675f18c30fbaab758a1f8f5f6818aaa193fc37f3fee0467264:8c223b8239b826c91eae0c24327e016129c3c13e99ef187a2abd9d710c33db5efd6e05aa4547252182050e06921510f0
|
||||||
|
6c27dc9a7c6291647e61015e9d1f6aa46a38c4f32086b36acb476af525399c0b:1881b2ada37e78f7883d64ff35ad98de04b98d277104534d3d8ae6ef37fe5c584887bf8304ebcbea472bfac050:a6c834443faf4f7d068e48a9a927f15ad5c68c22cb245bbd206ea772493393e6d01b55a31a227643629098ff29b20309
|
||||||
|
5cbc4de784ef59caa11a1faf1c5919499ca1dedecc92840e19adc121cb2a7aa9:ee42c4b9217735f1d9e32ece935893008d8c4009abd98dffa7c2f8214f26e31467f5ebd125abe9f7b6a62b5789a8:8159b26a583da405b5c5dd4da330d358465268e6f65b82c87683f2bec8521d7eb2dc5e13665cad7ee7f8bdc3a9713657
|
||||||
|
3a7c8b649c0c3826efd2646d01ba9800690a39a58af824762412403838042cd9:9aac7c53d666ac80d21af3f9422bce65ae0588acb274b6efec9b2ea75e7b12848da9f038449a5f8f8ac453af28fd02:83660e10ea5050381dcc5a8d8354f5322d60ade1758734a700221abf2cf0e2a04cda83b4cc85783476304cb8431907e3
|
||||||
|
3e5c7d16ffee2ab46e45da4aa41975bc6ece396a8d78bf3d072cbcf7c3d0c687:bf7f6717ac2a33428ad090c12cbc27dcd12a94e143c9eb46aeb11a6c65e7b09d90dd0da5b855ba80620b0ddf48a3843c:83efa6580768eec9514cb4d3c0c22e6a584aea44aee4f7dbf72bff8375668c9ee1935ba5eaae2cda072e5159a0166dbe
|
||||||
|
67db8b638d15e0f17848dfafa0105f04dc100c6bbbb8ca44cfbd7308497f648e:a4e30c5dfe87cd43153142a023fd297a9d1dde2c996f0cf3253623d5f04b36c46a7a70d815774c99d836cfce29cc876464:8f792b13eed2694c24a97623679c8be7bf325b74ee14a3b7e4b764e4ad402618bc1213ed21b04ee43af0aa0d7117f1a5
|
||||||
|
0c0a951ad354113eb871b3b9dd9db522d0abdc98f09c6caabe617a22986838a6:ab9226d0a78fc564c1c1a8a961b90cfe029160cd71e5ba95e6adc258f2ed491c36456e639d9dffd53a338cfe3190a8ab7b83:b8c474f01a43a045047ad4d8b6cfb7296ba6660eca20bb1de26fc158bd8c3744abcbb9867781f7f2a04aabff7a498b19
|
||||||
|
64518ed6d49e33c45c7dbb6b53aa3ae8032be58907f952b7d6d2efcc9b2b1f75:e7c2ac2eab22fae320a9c4aec6fc173668aa9df68d7ab00a75a65da0b121db6283bad06b131282d7045c0ce50a2c9c786c5f88:86625b564b51dca8139c4178453c3376c08a42621de931c9e1abef3f7e23d8c23a23ec617ecce51c241e25dff8949325
|
||||||
|
1796013211bb13b2f2df46e8e8f430ac043fadfe36b46904ca77fe404bc54b1e:67690484edea6aa1ab2b3a0ceca61fc08eefd1362cd4839827b4e45604911f4f97607d989388f707ffddddf42cfa0c779bf4633b:8e283416d53afb6a1281986686e7f40ee59c2abdbf633e7fd8416f0d7a40e7232ea49630fab5d752b0eddc0849816d70
|
||||||
|
3c83aed296db0756243f1c33607cae018f02c30eff97384b788817ee98e08281:ee9b7b3741e1594515e755ce40998a535e9f2ed7d382714ba2137329b5d491ae8fd8a56ce7a74058131e98fed9b3282961fd11d7df:a353943c14bf9e8553ff5d1627e121e8c0819e4406f3346b3d2c7d2721b192863d666262ddaabd0bdef1b2606acfa75e
|
||||||
|
6ab5abc9b803639a1ba34750c7baeb2c0ba315c96dcf70fcfdf634fc1e5b2197:b8b107a526fc51ce96996fa008d806f29052ed82512e73178426e1d694066534b1c7337dc522b0a59dd50cc472700b2642b512d30707:a040803c3d4f0d631b6396e5413d2ee8e5f088bf9272bb6a53789c6c9b41b12d50357b140825483fa7e215499b912439
|
||||||
|
5f6d5c23ab6996e98a6ed399472d97ceedf551135ed029cf68cab520f6ab2313:37b88c5975d31b1206b769943df826568dca065ff27c17232f5bd04bfd1ed4d01a5f1b0f70a89f3e5dc5af7fc2917594c8ae0cb3908b4b:a6ac886556754f0f99489c7fe92fb1231461afd7f6076584564ba58bd80f4e387d035dd1976e1a815b790d350b2707de
|
||||||
|
1696edc94cdf62da22c85e36c9a18580408040012de878ab6d10eeff6c51f049:72df71d31b9843d13ccafee2580500fbd486a54ed42ef9db70e074eeaa2a496054ad1782f504f5e2a7e65a42249a589c358dbfb3e307b864:a4d11fa6abad203f8ad0aa6fc98ce40b8e00ee652a0cf3c2bbdb3b3934757db0cdcb9368585633e277af90417c2ce078
|
||||||
|
1b61072c204850b8ed425526568e9d57f5f9973cea8499b9d3f3e65ee411f7d1:752d9ca07b296305ce8addc54eaaa7e03472aed19626860796c00f3230593e6812dc8114c125a78e7f2c93bb66a8abd3be431b868579cbd193:afa0a0c0472878e0b97d7e4938ee560d4e5d7fb909228fa57567e01394011339adade27d99097e5f663b5aa56f4ed01a
|
||||||
|
49f8b73e487ea32e90fbfa5967d382f828cb03dea8b6e91420e3835590964bb8:11753040da26b28d466fdd0f88494801f9e2a03b42a671a740bbf7d43e90c38fb383a9fb9992912c171f65c66096de05cfc896e449e18a16e3b9:81ad82f0ca54744103651a5ed1daa464e89ad3fd8c26c4d6a2743f8a58ca3fd4693dd338e09264829f8285494d564eff
|
||||||
|
3d7a0771e3c698956350570578e3dba2093315388fb1958a32e387bbd33845f5:247362f06c8f20e956031eed27d8f3be62dbe2154dedf195bb1f9539aecb0ae77aae3c71e6fee8acbdbb6ef8d68244ccd9f6b5a32de290a4001ea5:8e806bd7479057360710060b40be772789f6e7bc52e7e781e6c82a3877d485bb017e69078c1c3f9be628a3f09e857e91
|
||||||
|
3245ff9022c5f0af88741b86344a9dc9473c4dbb28b595711cd4138ca1bffca6:b793d1c58943269274404568a01a756b7b576659334121dfc401963d51bd0de1cf011a6ab6c5d3c8f6a42ea0bc5ee5bed2f70a096c0c05e35356c03f:abb639f7c6d7e6aedf5b2aa0696f5ba1f327668cc1c352a0b5f713e043f5204dc951571d7be952f34a126133d05e8fac
|
||||||
|
26d1b6697b3189154389abdff3eb2d909fb12a0e8440694b59b8ec5a73c366fb:37ce9416c6e2b8d4944beb6cb3775d296bf0364ea3f6d6ebebde5c36a077d2ace37c8a629d8d8abc8a89cd0e7c1a182b7ce81d7173c3a376615a9515e8:a6c860a4ebf8b54adbca5507a5b8414cb1541283e817777350f2424848d290abf0c980fce31e7d3c93ebe409f4735b50
|
||||||
|
1e384f2be1cbda26e0ff77699e1cb94b9a4a58a24159f6cf8ad5413547393e07:822f7451153b2281f3f89715f1d2edaa76628deb8913c0c11fd7e6ca6783ffcf19c4f2ceebe002b0fbc63cbe335d6ebe3ee39c5548d60fae6896dbc2eecb:859a858fdf08c16b720d5a25f89f7660fdab3f1e4be76c7f36d8bb95b53add7232e06af5eac2f71ed0e4e85fd69f0edb
|
||||||
|
0f6c6c95d4d24a72caee5861097970c074842ca0183982006d0d5b9fdcb65513:67ae1492457369dc0b494a5142c0e721613848a76870d369ab53bc4e7599398cb49c89e08b703366bfb964301e09c7b99350283e31616ecf4ae999fbff00e7:ad5378c136d51ca64e4fa7f21aa6732222622b80f30c5dd4ecc572e645a9ee2001a72b43284658d32ef10eb4c1018b95
|
||||||
|
11fa08cd0740c0c37a7c0269215f272855e378dd0f8d81f45b99033f74b721f2:bd8b3e41b647eba1d285853d9254d1f121b2371d3e38c67f31a9d8a718c7d7898664dcf216355f41ecaff9f73f77c35f625ddd7a7614ac7fc4d1754778f84f58:8b02f921cc83b296ef4cfc6baf0ac306567846012223a4bcd89c532eb5c39c80e35e577b368a67b8fd8e687325350fe6
|
||||||
|
6f38baf1cee3a83a4a99b403ed5ae143233d5b228c80b9d421f5772c7439b05d:3fb1e8ef6e99240cba6d89e6642e402e18eb3c135e104f18466b95bb90258ac84b3fbf6327fa6cedbced6e942b64d4d636e40b59ed39d92acbdb2933014f6e9b44:8f82cc3207c5880dd276d59ea1b42e194504b8188467587700da208a6459ea00dbdc3dc54ef3105489dd2c71b0f30fef
|
||||||
|
5e653c12f6483d2fbd963ce05862562ac3843884d961298f7ebf65f05e958d1f:547ddc66f31911b05895c2011da903eb00feb4b1d752ccdd4b862a27ad4b0de4832161bf6b3e132dd0b238902deb0ab8e7edad34fcdfd959032ae311b7e01e40f32f:a12db7e5a27a1a581a5e601d08e907e8ff61cee91a27638e177e048a408a67e2224bf16358c24652e0ea768979c506db
|
||||||
|
08073f9d18132bece9c3f23225118fd7feeadcc0266c1861231d01990f3d3018:1c028e2d3b3376712b2203d418b705b6d317a03c9c257f104660f007737ff11b3f430182181567625f4afdb6358ee862aeaca19c67b3b253fed777595bd79f4f6b9d26:b2adb57dd304ca200aa95254c790784635235ec3cc418ba0deaf0efcc6232434705b00e29889f4e259e704ac4f353633
|
||||||
|
691ede2f41856cddd0cb79cc89f5ad5bfa16c62942b660cb01bf5cab13a22d98:a76de84ca3f22c96b2995e2ba8474ee7f8d7f36aba70f46f26375c1f647fa3bdfe05e13c9b18f16b7933b6809d1cbd0fee5f0e1b780cce726a5c414c406f54e090098345:85dcda6d83ef31d423bcfc1d87444a6226ebd57018f4da52ef8d9e7b45c100f9bf36028bc9b0537b4baf240cf11293cc
|
||||||
|
1a518478b36de27cdb26516d1a96939a515729bca7c51c1f1e240974f3aa73f8:ec6d1b89686f1692c0fb79f6ed782bc1265475764946494aef7ebe64572bbf70e34c72214c276cf9c3c1a1b3b22c01e8a1c1c709dbdd97a199dd854cf7ec23bf564a3d6f16:ae4fd56430e237111e2886d09cd7e5f37f959d32ffae5f87ec51fa2f7bd2faa2c0c81fc60e98bc146f236929def7fc5a
|
||||||
|
359a75965e374ebe361c795d1d7fdbfe40c5709227289966c65d93ba68372832:bbbbde79882192bb916805775b36b769e652a80332897ce32f4757bede663953b40be828ec62b717ae7d3872b4baacd37bbdd85f3c501c5e11f0c738b6d16fe7a66cbf18704f:8d498db4b54bd914a9be4cf650a988e063f7d016b7a3bdee3ce330d9ab4c978bcac3c2958afec8e67cb1e244fbcb1e05
|
||||||
|
67dc679b1a67908eed7a36e1b20a557c0c1eabe7eaaae1cf8e4899da020dd8d2:c01d86741a47ca1c78edad008b24246ba9684e5f12d57ff8659b8453c187efedb4a2f697f414a823f72ee805554fecfc48047d465592c6d8425bd9ab7a1135ac370a22478d52df:9520a5b80e5569a2b07b2365ca780dfee80ad21e12a904e9e974f8cf183ae1655f2b95a598f9f80d6b9bc52040f881df
|
||||||
|
13d5386a288b72437a9279c0caf667533ef11f707ced34362eb6a4570be82b2f:55ce5ac51a3b5da8a128f2e00af927584e8b59694972ee0e6f95e012c308a180e339121050c56a8a900b04fbbe9cddc09c4c3a234d30885da9833b2bef66754015e81b5413d98652:903b042c823a494e6d833dc6f7a0050d4750f80e8b68224712425ff086239b87ea5fbcad5a13f69d7ee7dacfe47e619a
|
||||||
|
2b7969fbf66336b8928f48f6afd3a161254ac01eb4cf94451fba62d9d475f6f1:f53eaf6b0f992c803b28d2984ac74d9292763b8ee599cc0cfe8f135f89bca18ddf87db85d0914760a55d52aa4412008217f836c8517f8dd7390ff82d47ee6a62a306791d27295761e2:b37da593bc046212fa4d0ef084cf456ca7d3fd165ec863d2e980472c441f597a0aa858bf368aa4f4e77dd4b8ba071e69
|
||||||
|
65013f0a26a6e628f8c598af20dcfee2b9b5419e393ca6f832cf5d97a6ba34fa:227d5ccd9bbe4b7a586aaefd083b6a674a126ab864f5d90826cf7e67c4dbaacc7994a1879c50ed2752667066dc00006cc3d47ca53bdafcda5d38995c5b66d95b68c142da786339136332:8204f9b1e6227be7c64b5e629e5d75bfa7bfac17b2cde876ed57ace0be3da8c108fed9c189171741f2840302f1756456
|
||||||
|
1b942e9b54bc8eb2ef0a3deaec60e3c37955a44a3543b9bc0980e16675a1f904:d47e4fe5c7f225d00de4dd5284bb29d2ec57fb8a854596de15669a80e3bb8b1d9b5cb0251f1142f0d5a4b58d2b1090d94799be1d38a7ad65009cd6863ec0e5020850e1b09e5c502a12e23c:b03e1643481f7fd0c98ead4b8185100cb20718c7d4b816aa8b8b3e94987d0c1cb4558860d8e73b198a1655d1138fbf53
|
||||||
|
19d8a8169d57cc62ba1e4c4d9d22a45d0b2280945b2462f031907cb8bd83bb4a:e3ba46786a411d27a815d8fbac5f44e5cfd6f4ffe799e978d606235fd0ee14d58b68c8fd06845632c0030d1d919c90efbcc42a69b22afb1cf3e503f9a7d8193c8d3c297d7ff25740e483af34:82595dfb2e1380680208ca15077487d563e4c02381d2f4663498ee5798307de8391b2694089fe62c9efd1d2e5cc0b089
|
||||||
|
1bff7b1d26603e3f6efbeccdf394ed922e7a1c707365496113d3ad6fcc871195:145b190cd7bf22f6c45aab5e7cb87cf37a4098c5ea1b0d8df9837bd776551f4dca8bc6a6a830ceffac56033ca67d6fcf1f31794abc831f9dc83505f0201e52961fb4816ab21974ed05241eda1f:852af6a672913c27a0e240b7246399e4d23089e4ee727d44ac9a0aa3a7b13b100be82abf201edd35e3ce8c4f506ce484
|
||||||
|
371c2a48d2cd9ae2a13b3dfce09d7fbc9a01b61cf328f096fa87dfeb9e3ac883:785412beb888a53f807e537200ae520df044246aeaf2f86e8d65dc3a30056b57056cc44084fc2762069c49634cdb557cda102d5a7ea8a45bec6813738481b3e5996367d80faad7138791d510ad81:9616060a912f463131d3c0e0c9b5f8e9a40f6cf00b9d6253dd105e77604a687fb2ea7b466ee9421833f3331c25fac1ca
|
||||||
|
61d7787ebbe947d746063f1599c9313f8df517be1494a38cbb7f196a31ee7cd3:4b797f6782ee555113b5ea4166e2c3a2cbe3359256034745c66e59149b97cfba790bd091aa6f809721d6341acca9673a47f34bcdc08499080e30bb1e81defca019f62c886677577ab289be4981436d:a337aa2a22010cb98c0736df043acf9a01d0de654448f144ccc6e35dc53b5f6cc78583c5a465b282ae8add2005caed30
|
||||||
|
6a1a038e5c2bfab87ad3cd29b808c8e7a8b12961f7722d62d4ff7fd8936c6eee:2fc8caa666aeb84beb71d7c6918a8456a23c406b1378a6476607e4b27d651c4c9fde2c8682ed6005ca757dce710c4451372efc5886972cfc89f1eb7e19d80648b9869ba74ca305c6f88b464388ae3f72:9876a586403cb4c0f2b56373996ee524a489d4ded44df55b7db74a749ab74795470cc0a66a6e58193730195c5c444e76
|
||||||
|
3f528ca57a9a03e0e1af999cff2a602d43a8a7fc9774a5b35b91d46ba2332590:5c52e68adbf3a47d0352d333bca88b4559579fe3dc2efe7369fce4c10acea51c4166e8ab22d243741d7e2c2ae49a0ba35f729456f8c37b7bd31e858205a968cc0a6e5afaf2b3964b09619e241b3438c6d7:a2b1e39069444e3b1f14aab6015a6e25543ed0baa4a23ab6b187ac300c54d433580ea036af283a3a25d5421a945409ce
|
||||||
|
1cb19f5b2b6d2d76b26eefeb36d2995bccb77a0048e886b47552b209253e04d9:e8804b79ae38a9ad21cfd3e6e538b9bce254dc020dd42ebd62d4f282fe5da900b97aa86d40d5cab39516c74c33b769ab3e0a644a63a97c4cf9b59e55dfb42c1df038b1bb4ebec3d344ded09a5f90f4bafca8:8c37c7eec66b0c88268aeb7326e85d30a2e8e851750a74aa95870a7259d20f6fddab8dff3e0d4955ae79ca8e80fb515b
|
||||||
|
689216f2c9e7a748c94c898640d7f95d57dd0582eb017ce04351c44f10265472:1b9e066095b608967db1d6b93691bcdb4417f6693e6065186fbd8d1ed5267951db49d215328044d35e3555f6e1ac89fee959625b6bcfe510fe63bfd05de60b7e1e9fb5df9e721141c65bcd7a7e3363e1b5b472:8041368450aa99afec459dc86fc883d9b0ddc846e63a826b93bc07cd64e3520cb09ea5efbd049522fd049f0ab55fb61e
|
||||||
|
0b32a4f24c0c259951660e96457c1e1fa18bb7928c4796dd085dec96a99b0e37:01db4eaaf51d3322aea498726538eac137f248085db057f1faa77fdc8091e331e1d497b4b3276a51a5dc420af871a826c55dbffac511afc9319e9658e68de1ea204808c282e93100a29df7b089ae5551ff2bf95d:9634bb34b8bdb100b233df021f1d99afc8c9c9e8e76a7ff7d3fc62d733ff0819df55e71cfda092f54505a98783f786ea
|
||||||
|
04633de27fae1f070ff87e490e10528e9b40857b5109175a64543eb0ec6c82b7:85fde85aa169a8e44917086910fb1a9bee9f1b30b2d29e154998c6d659206307b5b66a1a3b1af3603becf751d37605e5b1c110578b2094062ad1e62ebd3bb75121d3569bca60bcb26fef490288da106258b904509f:80db12ebb3064c79d372d4779d4900e10c4bd141f109ecaca9c25cb3e789fa4cfd727ce372b6845c956f206f6a73901b
|
||||||
|
44dc2ef437107d48be57678f252e523a08bf63dc720da85b8da7486e875740b2:ee998b1edd10ffaf7d3eb7b163842726e33116efc46d77476fe2d3e8bb3b79f44f065e9bab6d1b9a32912744c2b8538ebdb8dc634c58ed19e179a889d7ca53983eda22ca0dfac2e5b6761f5e7a129a950dcfadc27cdb:8c7cc3d5822bc3b6ff0f9be230cf9fe91f5d86caed86bf3ee8679deaaba06b545f1cf87f63fc601a56da381e74b39e3b
|
||||||
|
5fae658e1beb5ce5aca9025861b991ffe5f0210562e0383a89372c3e9bb01683:391cbe0fbe656e0ca05e1f7ba659d7b931c8c32fc1b4a7477128a3d36fcf2e04e70f930fce6c42d667595b6870da22b29c4b667e08d905f9be6b94d01c5cb6d652b44fab93ec2da57edf40234c2998581fbc6bef11f098:ad2d3e2f04aee369179540b8d78d358ab6f49f03e540b242505eb0e9212d48843e3a4de840cf6534e8e492f4af7468e9
|
||||||
|
3c47c2af3e6fde7084b94f7125003b4730274dc73da91c3a6436a36a58b2d371:34e74e595a04107b38cffc124b941d3d549cef01e3552a75487ed3f1f23ea31046fe6db758683e6b9f034c5d4c63b6b7e92beabe3b7d599efb98250b4dcca3aa6515456b6b19ba984314260fd115b0e12380a5e68ffbcaa1:b140516e7bf5edf67a20fb360ab2932e7af32f38f669053d9fd9506453e71870ad598251dac0ab34117e4562fc946766
|
||||||
|
2e8e531b369ccefd7ac3e91b3a5e4dd671db1b2a05863e5b8170ae0dc27840b6:bbb0b60a66dbdc06791effb0aa45b5e4dd40777822a00aa1e52dbd7d0ba9cd30797612fb128c7c7debf3aa24a4967ab032180a527da239f913bc3551050b23b972642156240d2e42265053cf84e5d870fdb7a1c9c6f4c185ae:90d33306d8ccbf0b4c2f439e59d3633118bcda2a2fee59df40f4dbcb15a5b07c3198385a5d635e0eb8abdf005fe68996
|
||||||
|
4f7f6c0df5d8fb728033a4c7927b121353505ed518112592381faaf17bffe927:c870ba3e0fd1477ef1140246404729dbc4b516e32dc033abaff6149b3ecf4b932243bf9257c26777e1c064b7f3c64bcb3a5fa2e3f0fc7d40bb1b20636d90bd00536de78958c64893fe07a2528f806e2811bddfae9958b241c026:a908968636312fda482872680603307f1f5549e592eb611517e27aa9fac9e9f509356105a5e4fa5013d97613a976ffab
|
||||||
|
0feea23e93e2bb1c9714af6e8a125b6fb179dcc24b2456e40548061359e83034:b927770e3c3ecaf04844ede61c8f82c5394a636a9b481245f03cdb0b6fc75b5263e65a3dddbeadb8e5699edf04fb6b5cc2aff7af1a2b4c042669a9e3f03c0b564fa378ea9332581b8851a88ddd08f9959e0b9f66333ed081733d19:b9e40340b6f649a87312eb663694e24ddf4e842da1c38e5ed90185a61429487877463a9ce0c091f6a6803daee43d0480
|
||||||
|
26af207030b1958690b8da361e81044ab71b4ffdfd9a26f853b090d1c3a0da84:84f1728578942b1f41af223ac189c0de40fbf013608711acc97568ca4d5eb3f357ec7f76902a0b59b94d28959a25c832bde18c56ebe2749e684fd7bd1d5cabcc3ff50088271bda5b12f8cc79e53334ae997493fbc0bda2c56e27acdb:8fb67df1fdf9759139547dac10312170a10d0e220cf83e74a321778198f6ba6f93fe2bbd7e64d061392a195e835f97bb
|
||||||
|
420560ba6da9075a590bff683af1b816c6ed855dbfb89e584cb4904a1c3c18fc:87b7039c154b1ea17e34125afc51b31eb1882d5b0a27f800859c8570f7084d35d9edeeb285aa034bd0de63c85b9f22fc39b6faa69f6d420dad742c0a7828c0e5c16f9dbe93db95c8baab1b20826af7f942872e5e78345b9346a1baf203:a748c3e20476dfde9dd8ae0b1da0be834938d1a3843a93acf1e24a4dbcb808780d0812e78707326f23919f573b529883
|
||||||
|
23d5808d404b06f00e2e97215d55c84b735c4d0552577d842e0138431f69aa4e:528f09550299e52818e5a3af380374b63615c820ee972f8af249167d38c76ff28ce387f6c8712c6a21e529a048ddce22e794f221f8da9efe720e793624f66b5fde02c12c3fad14324dc7923ca6f44b7c610d5ce51e456c3027a303c71b9d:afe5949e2d7d89387864394b31745b922b4a50f7c08a1f092155007b700efc0c4c0fb3aded7c764b4a89d4638b55e033
|
||||||
|
2c2d04eeaa29b0383ffcf3607828ac5f39ca1ab6bececaa6fc8d10c1896fae79:564a760045e175bb5bcf5199f0330c9ee8a9178d7b7e2b574b42c4e8f549d63f05729d7559e1dd43431ef6f0e78a05ab1e676d8e9e972aa625feba814c5ac5b6aecbadcfb926c8de16026fb25d66d347813e636c3356208a704520de0a2f0a:9983cc5cbe5a8bcbe56bc1a71b2ca5b4a32923518ac2959aff0949c28af4124658ebb111d8fb390ca2afa50f089a3e75
|
||||||
|
6ea04b1ac55b80bd9e6f19b34eb635f6f40d65603dc312bd976245aa3a7ec2e9:94ed5fd16860d3062a01b1596040a0d60bc09f9b3c214ead3403109ab805a23210fd385ccfd5a65e80488dd13c1993fb2ec65d1093c8d87095c73a74589abd071bf41a645b0f177f3561165ea3426d29cffcd2315855599bd1dca971a026c906:aa7526a6ba48e05d1be7150815fa8ac4f80a59a08f44b2b7d77c8926e17a058fd6436b5c681ce8dad46351155217b87a
|
||||||
|
272d038fdbc32dc27d9113e69838acc52d61e764a00dec66b9174d1296734b8f:d58f636770468f71344828aa13c8b5c7dcddfc3c00d13e6480102ce6d4a2e0ef04f834058475ae8674b5536c2f5bf1a253a0fd54a36247abb73d1bac90464c214e871bcf737b269045ac59fd176294cdd0a3ad01391d1fd9f1d44db5ceb36244cf:a53737c41fd01aea3fed126a1ac8dab7d7439df0338ccb90f20f7173f3b210da7b3c091d90cd672e8f38c7b7864a56b1
|
||||||
|
62a13093e8754c1423e0f7e73218eb645f38cfc64b072bcbf2a0265946574329:113d0efee5cb3e1f678e684ebe613889dedd0a7820e8120926f4979322ef70bcef21cabcbf8a974eda198deaaddeb7ec5d0f9220f1706aca8f1df10340ca8d40025fca3688ccfba6b010b59110fede77cf0c54b6764756551e99d7016a6728b935a0:a6c302484226d30ab4bb5331c138915c34dfc1bb1a634eaa741af2aaf7a389e4945083ea2ae6ff9142f7010b0ff7360e
|
||||||
|
328b4abd2dfe8702172058f7ac506b1974a5911c4a574e3130950044214c6fbb:821030677bab3b8219adea5fd2fda6987be0422a47acadb76a27792719865f21c433b53fec7cfafb044240918492c7e6da4a1743fe84a472411beaf7e3630862e04c5b53213bcdb7dfe1cde18fff29d049c191f8b72bc8d1fda7a5c57cae62f9c96824:821b0ea7d9ba44a7fe751d380cdad155a44c20959c04c7caa3489983bf25ade2822e53031ebf0ccfc5a76b83ae0344a4
|
||||||
|
3b23859211b5eab5590185e6ae39a645b35012906f894108586824df61906152:8a25a986cc1df8d66b18a058e697ca2df03abf385ec0c39eabfe89bd340046c298a5a2752a7f555a5fbe3a5cd51b7eed0d950ed2c9c5e3a093590fbdcf3e41496ab510f238019733cc43f3a19f0773bed46d101ef847dcb91260ac36dcc7bb11c405bd8d:aaf17bb51a15ecf27d7aafe6a223eac092f257d5aac5d76b989ecb555251d7989ec56e46e7cbf6f68d478dd37d74b740
|
||||||
|
40a563397ead8c7bd84fe395aaf3994ee4b0373a8066e5f6ccd8b01a548229fe:38a06d00930a6247ff7c2ab303dc4f88e07d55442597c0b063ba32ab9dcea20748220f88c7354da3a5d21708b7a01491d58280434914ed16cf64f65e83e2bdc000491d719a5aca31f86c3df94559df1ae950a9d64d948a44f468d87909cb1a1db15c8d5e14:953ef1cd533f2986ac1bc2280ebf4e2522521a8f7b8c2689f04c133f1911405b0b6ea46f879ee7153f99f71d888c4b7e
|
||||||
|
02b22feedeacb437a6c10fd8867c831ff07b370aa287b1d57dbafe34de46ee05:dc59ca5873612bf0e0b0039ac451a3fd6913521bde999bafd87beaf2923802ae09630c05cd07e9d3e8c8bb3497f5a5fa6882d2e6c221470728dce51d96959843e799f02e5a64e1b7d7fb8c9b0ead75e7cc748825be932f735e4b639cacd8d32e0de9242e78ce:96bb0c83dfdfab8b9ecc8b360b3ba2642e285531b4e3ba861ee5169f48d9fa8bd4c84aadbe433ae3e3732eeaff15b9fc
|
||||||
|
58604274117a63977ca1d67b67fd59ebc9562d603f8ee39a02e2e83c2b115f42:769e80b21c38e39215d69c1f1c485ffee1023950edb5d375cd30a2c0e00890a952336be867909c9a55daa9ccf48b9ab5d874bac77635331d13effcadfe2dc321135fa8922c212b81820e3f49045f001f746321465b91d6a0ed34632cc1529848e7ef9fbf41f7d1:8726065a9813ad3449498ddfd51ac0d35e103d05b491880d51418b2782061e4f034b3034880e49c7dbe1e168226dd150
|
||||||
|
4d462264104d551ed98c229ee16d4aa4df79bd2fe3151554b0adb12ca48d2753:86863eb1262d36d8e51130f7a9e85229828a35a458cfb810ec97020df5fbfba89568a81bfdcd6014593cc1621da57720c54523720398dd58e006b89747d75e6eeff0d1d7852f79afd83907f7749245e64a5023f14d4ad663a2b41927ae7f77c3572fa2963fe19615:afd58b032b13a3974494f15d2113a7acca322c50ee686c763948b999641e3334f970ec89c6235b65c59edf5f1a63fd84
|
||||||
|
4d7d222d9de5be19af35cb1a31f556efff92b5afd8e92ddeb0d6820f5cf4103d:8d03abe66a28da061a733753df81c97d5abbdf1d324aba4e4276b43065532f48898dbf7dc7b87cc40cb65d6dc3db0a2a7f084240360dd2485ab44406f1ab90a790d851d49a1cf78cf4dd218b26e16eabbc4f0f7da27d0573dd30032e01134ca7e3c9569eee3b29fcc5:a915963bc69bcca6ac41a68d5587d22042cb44dc9bb58f85448818420126a9438cec846a72ac50da1f1a5bfb9fddd5e2
|
||||||
|
6f2091abc6dd00690688ae8e0644b30fc8b8f931a716ba6fed186981673f929e:cea5ee44948390d5d5f3fded51538f5fcb2a6f3a79b88d5df17f0de46280acb25ae5a918f58275991598f414f6f9a00bea7c30555057a6c04393ba1c9ad6ac555d450b96fdc8abd0b0f6d280ecb6594e021f776415bddf392e6f96d1f5ea074ce6ef81fbb26d3cdf8fe9:a4254a9388bcec6d88f08da8f18973bc381a3065d4bd3cd8c16468e073970cd6508eb77217a9fe3643dfa471faf374d8
|
||||||
|
462a706b92aa64cc85b9e376b5d27cd62f970ec3b7edb817e5b7ac6e239a0e0a:73ef7f6ddc19ad065a9000768ceeae6c455a41281fc581ca31ddde892669a7d9cf1428abfe9f32d410103ad8ade80c1c7d44aacdad110ea4011750aa40ddff3f959c13b04228a5d9f4e9b6ca71ed4f4f06b9995a09050620f18751e18dbf22e7b1793990ec8016717e9be2:b6747c6b843526201d6153e545d3d4fc270da6979d8a9ff291e36b41ab43a3c9308215f3ecfb8873a491bae22b5df37b
|
||||||
|
65237177f0ab7dd39df8c2345e6f56db27159988c7751646bee0dd2cb35f43d5:f473dd71b0fb705be4b377aeb7071bef91dcd49ac24ab5e2a593ef6fbe402b70bb2db06178b3fd6ea7c5a8333e09e721cfea23d63057a363050a0e3afbbe6b7f0def485edb1ad7345cd1cfc52fef5e688a4b9bc205307699fc22fa3fb6cad8ef9fa3eec1c013285417ad256a:913c8c823160436437293495f66e6c91022530e4c9505d25b3cd174a34c4981147d9671f13f84cf85ec78b800d3b07e4
|
||||||
|
64a1fe88f385b2d200a3dc9a5e985a13a3c351d8af112cccbdee1bb62c780688:8206710e7302d8167d219794244b11c906eac5c1c4478343bdd4e88367e18a863423a0ac012ccc9358e76be56a973b4336c81e4f31b068a57eb72c7ccb58d0bd9f9782e5eab8e4137d225b875609ef530b26d85b7c552770c66df01396fad7dab75302acb4c27f752b75b0ce49:96df03f30fbe2c5da70980d45ad4024067a82d4796de091159c197ce448d548bc5b3bc954d18e348bad62dd3c2bcab39
|
||||||
|
32dbc3afc15773c332a2605f24ce2ca3df38567b17a3c7566e772b8fa49f0db8:049f4b3cec24969d680486ead26e96798af64b7a12bcf61da12004c72680bd4f17570d235babbd92cf00037b62da694d16ec1ab11ad22861b14d128c83d9e59b182a264b8fd01d3e69ca91aea576b02ed3038f330c148345b621c4c0fba4ded3003d5b1aa1aa13c1659a4803514c:add4db113f3133a78ee27610aece4df4ff99946b4241fe0cd125025623bd7690d08e2969a59878f9feb4f82e9021772b
|
||||||
|
50034db59355c0cc3205033a648315c297c6bd57f33c5335a66b6d0dd3ce54fa:8f15974518b575933f99af74fa1305c13c6ccb5b004e379c3df457e05eed5e03aacff98d3dbfb5f4b0ef54decfa025cf83765db9a4c9d39054b3e146ad0b4fde2e4a5c208b698d2ba3842544e9df5f6bc17aab787127329bbe8b45ff40245d77e88f437637cd9a1b71a9ca0dc00b77:b32b1cba0fe06dd6d780bcc1c631df9721e8d3fbcaef372da23f40bdf5f6354e366728185e41e6d2dc04ad3e01d8b4d0
|
||||||
|
6294ef0407265a0d19bb3ff7530babd8e32f10a34dff0b37135cd28e82984a8b:f822e5229abe4eabbc054bb516ac3586e182beabdcae28d33336fdbdd23cda050e06a33e03e652165c4a5c32734138126b970308bf20086e9074a3bc8a9d5bdd391fdf7205b21716ba441782fd91245be12ef7a68a07e66b22ada6235f1b3c480f78ce4f945f3b7f985d89106831adfb:a58f90af0ee34e2bc2d13ee421aa90d80bb7c2007cce5b65ac0991a0558dc3a1ea46a8c55819bff404ab297c6ae45066
|
||||||
|
527fce2b29171bbdb60fb573e3639342633b1266e3f695ebce316abc86dd371c:2e549082f9eeb2534d30b90664e5091cf3411ba33fe14e1d86c353837cef4ce5178a59c68f6fea06507291770b52c8f8f28c7791961b2809a0ba682ac78a36de351bc0bd0c54a2916723d5ff2be3c73fb754e7bef4c0388f914ddd31b0f27581369d7a69b78dffc0018f2e1cd9bb840891:a94d698e8362efb86b25b7249fa09c5e627b8f976793ecb85d0f9b06e2ff8077ebafcc8ca104ff869e29f403d9cc9dc7
|
||||||
|
5a03fe3b4164b562d93e129103200ce033da05153fb1a1dc35452c00c8133f6d:7be5bfa4f0ae60a0c2f5258de564ea6f6a42ccd2802928a3fd14b513262b040d78825696aa9e3891947625af2a8e5ab0f663892eb6afc4d463034ff5e7480538f7b32f93f1cb326a52af92d26305d8f3555807597cc66973d29421ce1721872753fa882a3665536f7a9e05ca536b11f8cc4d:84b36b5b7ede2b059a87e6f6b8d7daeb253b9c39438ec407b5885d4186d958e9388af554e236475642eb1102f774a956
|
||||||
|
6136e51d3eadb2a46fb631b099565e6e3cc9feff15f33b9c08b49598d4a664f3:c72773b9026f66fa1299f4412a3e0df893a53a1f9c890a1cd653097459691b72387acaaf5574e293f83820117dc074d7396fee2ebbac34fb69a14ad5a528c03ca409438047e02e9c0ea765c200e3b482224af6874e03484ae19595827374b6eb4ca23d399e9e66f2c8a3732a3f2a27a796703e:98b9ad1abf6655723b2d685b84cb29861d19f8244f505a7ae4d2e4cc97eb203fadb87d2eaec040b07c100b5a790f0432
|
||||||
|
5672c6aeba5dfecc8314b1625f509a4e0dcc36846035bab283e547a5f6d1d4f4:92ed1627ec780c62d968d13bcc181dfaf6470fc1704c92cf7a068a85eae494b7630159f5e044f42a9a7c02e2dde16c8ec81eaf650b4ded6e6cb4e92ac5e54ca4e0d69a4ad3c6d93a0e91a5eaafe989c96c07f5785bdc0b73295eecac04cb71f85fa3744da3cefb9689674d994b52f98ae6a4c784:ac129ded0f581e58ae8f7616633712c68194923db58498290a267552b2885715e437f9414a9b9fb99080f968b56d836a
|
||||||
|
46c3fc71ef1b3c5a5297f9e6a8e3298838c014ab9598eb9f9adbfe067edb4e16:cd6290b58efc9e7b269dd477525cf240a050b30aebf8784a9749183a139f2c8f2b620cb1555d0006cfb0e6610f5b2b3505cf3541c37ad881c898e8f49d6b1f2618792b3c122f93148c14abebc3be947443f968af0c6a0a77ed159eff04e18852d8ffa2f35ec73e21640a1408a51526011fd7a33161:b04276d98fd1b27645187512e20927e735df17d9dc5bf003cb45661119875a6353286f3dcae081df36f0fda36e71e367
|
||||||
|
502ee3c386728a0c1b0cd6787ca78b7d6eb8aed2a9fa0ce7cc50b7c91869917c:7f5a1e1ab71a43bf38cd7accfd20af1e99ae55fba55ec043c5f71e4bce2535ca4c8e50f2e85432658670b6b20a8a1d9620b204938fa1aab412b959a364a5272b7364a0204fc3de15490bca3ecb572ab1ae0dba017619cfabbd10138e65399687c9288caac4ad7f2e81e6156dcfe30ee7d1f7770e03c4:94ab75ad8fc599d4a0f077df652a0c9992486dafcacfd5a5d2d8ba3b5ba0db4fd64f322106d9647aff961766aa6ec719
|
||||||
|
0d59c2cbc5a01509788f8d78768f6b4c70e1e56a3d064ee3a5d214b051373de0:1fb41c046728bc666e16ad566131a5c38b8562942c0f8ab175f2a82886e710e55c3af18be343a496f2426144f985c1101a08ecdd9c39081d650c7192af82a203272661c8424aad5dfd70afbff908f8cdf1b6a12cb3078663e836e317b37b47c7f986b1b4e97f7166dfaaef116205e763eb9998d64964e4:ab511a041e0ada5b71f4699a8b52479c181e1ad068ecd2f5d87fb124f2ecae402f7672851f368ec78d1d0e5ee7514cab
|
||||||
|
03b0c798cc56bd541ccf9b674cb12a8580fd6a0829ac9f5d2f2ce198c1fabf59:6310c538bdba8e586904f3283421e272ad4518bdbde268184bcc3c33b6078d9460b1a295e927a2310dd291a9b9e2bb4b49cc52b972ac6ac9f7a2bd8d8d8b2737224660135cfa5ddcc874186c794dc3898be4302cc771cb6483a7463830fdfc611adcf09b9a28d71c3ef6ff45228e40c12d84459974b8c944:b8291ecf685152e7bc7c1f3753a4dd2bd213c0ffe969249ab8927a6676ecfab3b67a73f3f7ed0f6187fa976b3e8aeec0
|
||||||
|
5ae1bb72838eb38c1f4340c6e22686961e3e34b4d2d518d76ab4f881fd479c94:332c3bbd162c8cb774480ef81cede2aa52a0967a869663cab7c8d105e33038562599881f6ef77aa4e3f4225131fc864a27db9c58f5198e6190d5eec728ec4e698b96d1f982edc5d22231b3d76be10a7deeab8f32af36a2cf5d8aed42102ae20679cbb5c8befc8a65f13837710bf86f344590bc74f96e052eae:80e602213b6c748b3e68430ccd1c4fa288aa5c0a411466dd8994ff19ae8be60ad281da36e8a35e2f2b427ac2edc255cc
|
||||||
|
4113f2add0abb7f05cf56a1281457e9e4f7cfb39d093854b3b4e3bbde237ea23:a83d1cd6999e4940b0d71be96e000e46246a6e451c646756d893b3edb330f2f445858ed12d1d35412fc0eee90204aba0d4fa88583bbc37ee389f88fe0c8dd4c34ffaa8d8c6affeb7feb5f3064af0c06ad3f7a51e4b58f8edc95bd22a02ec4bbe2b2c660762e345d00548995ddcfb10299dd192344d45791ea8bb:a37b03f854b9cb12dc7fc109bea2ccc3423418b89d283715fb8c18db78a68d60b19bdd7e6acf869090b97f538a35df66
|
||||||
|
17c979dc43d5da70f57027cffc420c6df4245db222a2c3060c7499b2663f6093:c20c4351abcf4ecda9b5478cff8af859ec743eb0d67f6ce2b347349460a2854475d0a6e8165b5174434ca77c088e641c109ac8b8d5879586c034eadfa6bc3a1e66faa02a3dc0fed87af670cc8626df5c71a8360e1911159632558e7a99d4830ce21bcbf3f551465bd1061888b83db174343692b6d796460791ab8d:8ff5917a45cdda175f9d6f4a4a99308617ceec290794fda33d7585f4a3964febf0274479f77210373f3ca6350ae0639b
|
||||||
|
50adb11ff487000da2714e104241c0039dc6c2eca5e976a6bca8cc80fd2f22d5:3ca6b6f64aab22ea7dcf85eeb05e5eff6102731bb47c94bb737e77b389334742be939b79219fff38452b411052bba588b7872654d0b40f7be1516b8f243047a9aefb40fb70f93e53def38147fe564214605ef2d7508bd7ef44fdfee7882d754e735ba282e044bb953f18f131212d879def4d4d6923d0dcc0d3bb6a14:989e4389faf99c5cbf6798a147fd85cf1331e1e7faa966d6fbed2cd43c673d6a382e0e304f560e5ba816eb1a0b306cf9
|
||||||
|
55bdf02943f5a8130bcb537972f870043f5d7e3561df8717740e1a7a39bf73df:e288bfeeead0c3050ec6834694323af7bd77dcb52fee6cb54167a73181e487583ac75e63c95e71760cd9a1584b711842f602237f72afb268ef039a044d293d4091abc1807cbec9041ece11905b32ace59db1114047f60ae679c53b465afe03a8ba02ee89e85efebbb93226eaf1cd5c6ce1ef913fbf549934dfbda69dff:94c74729eaecf336b25d82a10798adc97e70cb345313413cf6550d622b0b92b9d4dcf606afb1b7e88db6b3ce106aba04
|
||||||
|
49a477ecf8786a9f5a44a9da0a83da977b844198068f1cadce28c599d9ebddbd:9944eeed83dde7b21b72ff1491cad3d7a1ceae3f9c5c880be49024d9ec055c55189de80b521df30fd17d558f7bc6ff6d5c9dcacae3ec1242929fae1fd8bd7fedea51acd344a1fa0120f60b1a4679e5177588fc27d713173f4fd47cccc16feb8b44a2d670d7b04c8c14bf37527230f3daa6d7c3a6e958c78376c5940f1063:8045a0358069c43170b238e7dc98952bf84e1caa0925905922b5822ab28b498297e901614909376fa04ced4c852c39da
|
||||||
|
6bbc2807b27b635285670a68a42d8fafb461fab581e4e2773c199b29088bc554:0c149764b95e6461e820206e5e2b7fa4acf62a3a132db955d5c4ff1cafbfef3b3816aab1bc9bbcf14af47a4e7753f0243842d9b53b3c3c26b9a2e244f2eb06461e2128949e9b437c96cddbddb52d9d6062d4692d05dc624f94fac39401ce51389576f0fd52e670841602645b4ed6a76cc845ec8454538782b3b179e44cd5ec:aaaa82c3fd810924f41b74bdf484460810f5f3274f8102520099f9ca384a5965e50202a36edfa5b0999c7b3c2548ae74
|
||||||
362
test/ed25519-addons.test.js
Normal file
362
test/ed25519-addons.test.js
Normal file
@@ -0,0 +1,362 @@
|
|||||||
|
import { sha512 } from '@noble/hashes/sha512';
|
||||||
|
import { bytesToHex as hex, hexToBytes } 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 {
|
||||||
|
ed25519,
|
||||||
|
ed25519ctx,
|
||||||
|
ed25519ph,
|
||||||
|
edwardsToMontgomeryPub,
|
||||||
|
edwardsToMontgomeryPriv,
|
||||||
|
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('X25519/getSharedSecret() should be commutative', () => {
|
||||||
|
for (let i = 0; i < 512; i++) {
|
||||||
|
const asec = x25519.utils.randomPrivateKey();
|
||||||
|
const apub = x25519.getPublicKey(asec);
|
||||||
|
const bsec = x25519.utils.randomPrivateKey();
|
||||||
|
const bpub = x25519.getPublicKey(bsec);
|
||||||
|
try {
|
||||||
|
deepStrictEqual(x25519.getSharedSecret(asec, bpub), x25519.getSharedSecret(bsec, apub));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('not commutative', { asec, apub, bsec, bpub });
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
should('edwardsToMontgomery should produce correct output', () => {
|
||||||
|
const edSecret = hexToBytes('77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a');
|
||||||
|
const edPublic = ed25519.getPublicKey(edSecret);
|
||||||
|
const xPrivate = edwardsToMontgomeryPriv(edSecret);
|
||||||
|
deepStrictEqual(
|
||||||
|
hex(xPrivate),
|
||||||
|
'a8cd44eb8e93319c0570bc11005c0e0189d34ff02f6c17773411ad191293c94f'
|
||||||
|
);
|
||||||
|
const xPublic = edwardsToMontgomeryPub(edPublic);
|
||||||
|
deepStrictEqual(
|
||||||
|
hex(xPublic),
|
||||||
|
'ed7749b4d989f6957f3bfde6c56767e988e21c9f8784d91d610011cd553f9b06'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
should('edwardsToMontgomery should produce correct keyPair', () => {
|
||||||
|
const edSecret = ed25519.utils.randomPrivateKey();
|
||||||
|
const edPublic = ed25519.getPublicKey(edSecret);
|
||||||
|
const xSecret = edwardsToMontgomeryPriv(edSecret);
|
||||||
|
const expectedXPublic = x25519.getPublicKey(xSecret);
|
||||||
|
const xPublic = edwardsToMontgomeryPub(edPublic);
|
||||||
|
deepStrictEqual(xPublic, expectedXPublic);
|
||||||
|
});
|
||||||
|
|
||||||
|
should('ECDH through edwardsToMontgomery should be commutative', () => {
|
||||||
|
const edSecret1 = ed25519.utils.randomPrivateKey();
|
||||||
|
const edPublic1 = ed25519.getPublicKey(edSecret1);
|
||||||
|
const edSecret2 = ed25519.utils.randomPrivateKey();
|
||||||
|
const edPublic2 = ed25519.getPublicKey(edSecret2);
|
||||||
|
deepStrictEqual(
|
||||||
|
x25519.getSharedSecret(edwardsToMontgomeryPriv(edSecret1), edwardsToMontgomeryPub(edPublic2)),
|
||||||
|
x25519.getSharedSecret(edwardsToMontgomeryPriv(edSecret2), edwardsToMontgomeryPub(edPublic1))
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
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';
|
||||||
|
|
||||||
|
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';
|
||||||
@@ -1,24 +1,19 @@
|
|||||||
import { deepEqual, deepStrictEqual, strictEqual, throws } from 'assert';
|
import { deepStrictEqual, strictEqual, throws } from 'assert';
|
||||||
import { describe, should } from 'micro-should';
|
|
||||||
import * as fc from 'fast-check';
|
|
||||||
import {
|
|
||||||
ed25519,
|
|
||||||
ed25519ctx,
|
|
||||||
ed25519ph,
|
|
||||||
x25519,
|
|
||||||
RistrettoPoint,
|
|
||||||
ED25519_TORSION_SUBGROUP,
|
|
||||||
} from '../lib/esm/ed25519.js';
|
|
||||||
import { readFileSync } from 'fs';
|
import { readFileSync } from 'fs';
|
||||||
|
import { bytesToHex, concatBytes, hexToBytes, utf8ToBytes, randomBytes } from '@noble/hashes/utils';
|
||||||
|
import * as fc from 'fast-check';
|
||||||
|
import { describe, should } from 'micro-should';
|
||||||
|
import { ed25519 as ed, ED25519_TORSION_SUBGROUP, numberToBytesLE } from './ed25519.helpers.js';
|
||||||
|
// Old vectors allow to test sign() because they include private key
|
||||||
|
import { default as ed25519vectors_OLD } from './ed25519/ed25519_test_OLD.json' assert { type: 'json' };
|
||||||
|
import { default as ed25519vectors } from './wycheproof/ed25519_test.json' assert { type: 'json' };
|
||||||
import { default as zip215 } from './ed25519/zip215.json' assert { type: 'json' };
|
import { default as zip215 } from './ed25519/zip215.json' assert { type: 'json' };
|
||||||
import { hexToBytes, bytesToHex, randomBytes } from '@noble/hashes/utils';
|
import { default as edgeCases } from './ed25519/edge-cases.json' assert { type: 'json' };
|
||||||
import { numberToBytesLE } from '../lib/esm/abstract/utils.js';
|
|
||||||
import { sha512 } from '@noble/hashes/sha512';
|
// Any changes to the file will need to be aware of the fact
|
||||||
import { default as ed25519vectors } from './wycheproof/eddsa_test.json' assert { type: 'json' };
|
// the file is shared between noble-curves and noble-ed25519.
|
||||||
import { default as x25519vectors } from './wycheproof/x25519_test.json' assert { type: 'json' };
|
|
||||||
|
|
||||||
describe('ed25519', () => {
|
describe('ed25519', () => {
|
||||||
const ed = ed25519;
|
|
||||||
const hex = bytesToHex;
|
const hex = bytesToHex;
|
||||||
const Point = ed.ExtendedPoint;
|
const Point = ed.ExtendedPoint;
|
||||||
|
|
||||||
@@ -27,13 +22,6 @@ describe('ed25519', () => {
|
|||||||
return hexToBytes(hex.padStart(64, '0'));
|
return hexToBytes(hex.padStart(64, '0'));
|
||||||
}
|
}
|
||||||
|
|
||||||
function utf8ToBytes(str) {
|
|
||||||
if (typeof str !== 'string') {
|
|
||||||
throw new Error(`utf8ToBytes expected string, got ${typeof str}`);
|
|
||||||
}
|
|
||||||
return new TextEncoder().encode(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
ed.utils.precompute(8);
|
ed.utils.precompute(8);
|
||||||
|
|
||||||
should('not accept >32byte private keys', () => {
|
should('not accept >32byte private keys', () => {
|
||||||
@@ -292,104 +280,6 @@ describe('ed25519', () => {
|
|||||||
// // const signature = await ristretto25519.sign(MESSAGE, PRIVATE_KEY);
|
// // const signature = await ristretto25519.sign(MESSAGE, PRIVATE_KEY);
|
||||||
// // expect(await ristretto25519.verify(signature, WRONG_MESSAGE, publicKey)).toBe(false);
|
// // expect(await ristretto25519.verify(signature, WRONG_MESSAGE, publicKey)).toBe(false);
|
||||||
// // });
|
// // });
|
||||||
should('ristretto255/should follow the byte encodings of small multiples', () => {
|
|
||||||
const encodingsOfSmallMultiples = [
|
|
||||||
// This is the identity point
|
|
||||||
'0000000000000000000000000000000000000000000000000000000000000000',
|
|
||||||
// This is the basepoint
|
|
||||||
'e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76',
|
|
||||||
// These are small multiples of the basepoint
|
|
||||||
'6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919',
|
|
||||||
'94741f5d5d52755ece4f23f044ee27d5d1ea1e2bd196b462166b16152a9d0259',
|
|
||||||
'da80862773358b466ffadfe0b3293ab3d9fd53c5ea6c955358f568322daf6a57',
|
|
||||||
'e882b131016b52c1d3337080187cf768423efccbb517bb495ab812c4160ff44e',
|
|
||||||
'f64746d3c92b13050ed8d80236a7f0007c3b3f962f5ba793d19a601ebb1df403',
|
|
||||||
'44f53520926ec81fbd5a387845beb7df85a96a24ece18738bdcfa6a7822a176d',
|
|
||||||
'903293d8f2287ebe10e2374dc1a53e0bc887e592699f02d077d5263cdd55601c',
|
|
||||||
'02622ace8f7303a31cafc63f8fc48fdc16e1c8c8d234b2f0d6685282a9076031',
|
|
||||||
'20706fd788b2720a1ed2a5dad4952b01f413bcf0e7564de8cdc816689e2db95f',
|
|
||||||
'bce83f8ba5dd2fa572864c24ba1810f9522bc6004afe95877ac73241cafdab42',
|
|
||||||
'e4549ee16b9aa03099ca208c67adafcafa4c3f3e4e5303de6026e3ca8ff84460',
|
|
||||||
'aa52e000df2e16f55fb1032fc33bc42742dad6bd5a8fc0be0167436c5948501f',
|
|
||||||
'46376b80f409b29dc2b5f6f0c52591990896e5716f41477cd30085ab7f10301e',
|
|
||||||
'e0c418f7c8d9c4cdd7395b93ea124f3ad99021bb681dfc3302a9d99a2e53e64e',
|
|
||||||
];
|
|
||||||
let B = RistrettoPoint.BASE;
|
|
||||||
let P = RistrettoPoint.ZERO;
|
|
||||||
for (const encoded of encodingsOfSmallMultiples) {
|
|
||||||
deepStrictEqual(P.toHex(), encoded);
|
|
||||||
deepStrictEqual(RistrettoPoint.fromHex(encoded).toHex(), encoded);
|
|
||||||
P = P.add(B);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
should('ristretto255/should not convert bad bytes encoding', () => {
|
|
||||||
const badEncodings = [
|
|
||||||
// These are all bad because they're non-canonical field encodings.
|
|
||||||
'00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
|
|
||||||
'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f',
|
|
||||||
'f3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f',
|
|
||||||
'edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f',
|
|
||||||
// These are all bad because they're negative field elements.
|
|
||||||
'0100000000000000000000000000000000000000000000000000000000000000',
|
|
||||||
'01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f',
|
|
||||||
'ed57ffd8c914fb201471d1c3d245ce3c746fcbe63a3679d51b6a516ebebe0e20',
|
|
||||||
'c34c4e1826e5d403b78e246e88aa051c36ccf0aafebffe137d148a2bf9104562',
|
|
||||||
'c940e5a4404157cfb1628b108db051a8d439e1a421394ec4ebccb9ec92a8ac78',
|
|
||||||
'47cfc5497c53dc8e61c91d17fd626ffb1c49e2bca94eed052281b510b1117a24',
|
|
||||||
'f1c6165d33367351b0da8f6e4511010c68174a03b6581212c71c0e1d026c3c72',
|
|
||||||
'87260f7a2f12495118360f02c26a470f450dadf34a413d21042b43b9d93e1309',
|
|
||||||
// These are all bad because they give a nonsquare x².
|
|
||||||
'26948d35ca62e643e26a83177332e6b6afeb9d08e4268b650f1f5bbd8d81d371',
|
|
||||||
'4eac077a713c57b4f4397629a4145982c661f48044dd3f96427d40b147d9742f',
|
|
||||||
'de6a7b00deadc788eb6b6c8d20c0ae96c2f2019078fa604fee5b87d6e989ad7b',
|
|
||||||
'bcab477be20861e01e4a0e295284146a510150d9817763caf1a6f4b422d67042',
|
|
||||||
'2a292df7e32cababbd9de088d1d1abec9fc0440f637ed2fba145094dc14bea08',
|
|
||||||
'f4a9e534fc0d216c44b218fa0c42d99635a0127ee2e53c712f70609649fdff22',
|
|
||||||
'8268436f8c4126196cf64b3c7ddbda90746a378625f9813dd9b8457077256731',
|
|
||||||
'2810e5cbc2cc4d4eece54f61c6f69758e289aa7ab440b3cbeaa21995c2f4232b',
|
|
||||||
// These are all bad because they give a negative xy value.
|
|
||||||
'3eb858e78f5a7254d8c9731174a94f76755fd3941c0ac93735c07ba14579630e',
|
|
||||||
'a45fdc55c76448c049a1ab33f17023edfb2be3581e9c7aade8a6125215e04220',
|
|
||||||
'd483fe813c6ba647ebbfd3ec41adca1c6130c2beeee9d9bf065c8d151c5f396e',
|
|
||||||
'8a2e1d30050198c65a54483123960ccc38aef6848e1ec8f5f780e8523769ba32',
|
|
||||||
'32888462f8b486c68ad7dd9610be5192bbeaf3b443951ac1a8118419d9fa097b',
|
|
||||||
'227142501b9d4355ccba290404bde41575b037693cef1f438c47f8fbf35d1165',
|
|
||||||
'5c37cc491da847cfeb9281d407efc41e15144c876e0170b499a96a22ed31e01e',
|
|
||||||
'445425117cb8c90edcbc7c1cc0e74f747f2c1efa5630a967c64f287792a48a4b',
|
|
||||||
// This is s = -1, which causes y = 0.
|
|
||||||
'ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f',
|
|
||||||
];
|
|
||||||
for (const badBytes of badEncodings) {
|
|
||||||
const b = hexToBytes(badBytes);
|
|
||||||
throws(() => RistrettoPoint.fromHex(b), badBytes);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
should('ristretto255/should create right points from uniform hash', () => {
|
|
||||||
const labels = [
|
|
||||||
'Ristretto is traditionally a short shot of espresso coffee',
|
|
||||||
'made with the normal amount of ground coffee but extracted with',
|
|
||||||
'about half the amount of water in the same amount of time',
|
|
||||||
'by using a finer grind.',
|
|
||||||
'This produces a concentrated shot of coffee per volume.',
|
|
||||||
'Just pulling a normal shot short will produce a weaker shot',
|
|
||||||
'and is not a Ristretto as some believe.',
|
|
||||||
];
|
|
||||||
const encodedHashToPoints = [
|
|
||||||
'3066f82a1a747d45120d1740f14358531a8f04bbffe6a819f86dfe50f44a0a46',
|
|
||||||
'f26e5b6f7d362d2d2a94c5d0e7602cb4773c95a2e5c31a64f133189fa76ed61b',
|
|
||||||
'006ccd2a9e6867e6a2c5cea83d3302cc9de128dd2a9a57dd8ee7b9d7ffe02826',
|
|
||||||
'f8f0c87cf237953c5890aec3998169005dae3eca1fbb04548c635953c817f92a',
|
|
||||||
'ae81e7dedf20a497e10c304a765c1767a42d6e06029758d2d7e8ef7cc4c41179',
|
|
||||||
'e2705652ff9f5e44d3e841bf1c251cf7dddb77d140870d1ab2ed64f1a9ce8628',
|
|
||||||
'80bd07262511cdde4863f8a7434cef696750681cb9510eea557088f76d9e5065',
|
|
||||||
];
|
|
||||||
|
|
||||||
for (let i = 0; i < labels.length; i++) {
|
|
||||||
const hash = sha512(utf8ToBytes(labels[i]));
|
|
||||||
const point = RistrettoPoint.hashToCurve(hash);
|
|
||||||
deepStrictEqual(point.toHex(), encodedHashToPoints[i]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
should('input immutability: sign/verify are immutable', () => {
|
should('input immutability: sign/verify are immutable', () => {
|
||||||
const privateKey = ed.utils.randomPrivateKey();
|
const privateKey = ed.utils.randomPrivateKey();
|
||||||
@@ -413,7 +303,8 @@ describe('ed25519', () => {
|
|||||||
|
|
||||||
// https://zips.z.cash/zip-0215
|
// https://zips.z.cash/zip-0215
|
||||||
// Vectors from https://gist.github.com/hdevalence/93ed42d17ecab8e42138b213812c8cc7
|
// Vectors from https://gist.github.com/hdevalence/93ed42d17ecab8e42138b213812c8cc7
|
||||||
should('ZIP-215 compliance tests/should pass all of them', () => {
|
describe('ZIP215', () => {
|
||||||
|
should('pass all compliance tests', () => {
|
||||||
const str = utf8ToBytes('Zcash');
|
const str = utf8ToBytes('Zcash');
|
||||||
for (let v of zip215) {
|
for (let v of zip215) {
|
||||||
let noble = false;
|
let noble = false;
|
||||||
@@ -425,56 +316,12 @@ describe('ed25519', () => {
|
|||||||
deepStrictEqual(noble, v.valid_zip215, JSON.stringify(v));
|
deepStrictEqual(noble, v.valid_zip215, JSON.stringify(v));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
should('ZIP-215 compliance tests/disallows sig.s >= CURVE.n', () => {
|
should('disallow sig.s >= CURVE.n', () => {
|
||||||
// sig.R = BASE, sig.s = N+1
|
// sig.R = BASE, sig.s = N+1
|
||||||
const sig =
|
const sig =
|
||||||
'5866666666666666666666666666666666666666666666666666666666666666eed3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010';
|
'5866666666666666666666666666666666666666666666666666666666666666eed3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010';
|
||||||
throws(() => ed.verify(sig, 'deadbeef', Point.BASE));
|
deepStrictEqual(ed.verify(sig, 'deadbeef', Point.BASE), false);
|
||||||
});
|
});
|
||||||
|
|
||||||
const rfc7748Mul = [
|
|
||||||
{
|
|
||||||
scalar: 'a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4',
|
|
||||||
u: 'e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c',
|
|
||||||
outputU: 'c3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
scalar: '4b66e9d4d1b4673c5ad22691957d6af5c11b6421e0ea01d42ca4169e7918ba0d',
|
|
||||||
u: 'e5210f12786811d3f4b7959d0538ae2c31dbe7106fc03c3efc4cd549c715a493',
|
|
||||||
outputU: '95cbde9476e8907d7aade45cb4b873f88b595a68799fa152e6f8f7647aac7957',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
for (let i = 0; i < rfc7748Mul.length; i++) {
|
|
||||||
const v = rfc7748Mul[i];
|
|
||||||
should(`RFC7748: scalarMult (${i})`, () => {
|
|
||||||
deepStrictEqual(hex(x25519.scalarMult(v.scalar, v.u)), v.outputU);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const rfc7748Iter = [
|
|
||||||
{ scalar: '422c8e7a6227d7bca1350b3e2bb7279f7897b87bb6854b783c60e80311ae3079', iters: 1 },
|
|
||||||
{ scalar: '684cf59ba83309552800ef566f2f4d3c1c3887c49360e3875f2eb94d99532c51', iters: 1000 },
|
|
||||||
// { scalar: '7c3911e0ab2586fd864497297e575e6f3bc601c0883c30df5f4dd2d24f665424', iters: 1000000 },
|
|
||||||
];
|
|
||||||
for (let i = 0; i < rfc7748Iter.length; i++) {
|
|
||||||
const { scalar, iters } = rfc7748Iter[i];
|
|
||||||
should(`RFC7748: scalarMult iteration (${i})`, () => {
|
|
||||||
let k = x25519.Gu;
|
|
||||||
for (let i = 0, u = k; i < iters; i++) [k, u] = [x25519.scalarMult(k, u), k];
|
|
||||||
deepStrictEqual(hex(k), scalar);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
should('RFC7748 getSharedKey', () => {
|
|
||||||
const alicePrivate = '77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a';
|
|
||||||
const alicePublic = '8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a';
|
|
||||||
const bobPrivate = '5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb';
|
|
||||||
const bobPublic = 'de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f';
|
|
||||||
const shared = '4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742';
|
|
||||||
deepStrictEqual(alicePublic, hex(x25519.getPublicKey(alicePrivate)));
|
|
||||||
deepStrictEqual(bobPublic, hex(x25519.getPublicKey(bobPrivate)));
|
|
||||||
deepStrictEqual(hex(x25519.scalarMult(alicePrivate, bobPublic)), shared);
|
|
||||||
deepStrictEqual(hex(x25519.scalarMult(bobPrivate, alicePublic)), shared);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// should('X25519/getSharedSecret() should be commutative', () => {
|
// should('X25519/getSharedSecret() should be commutative', () => {
|
||||||
@@ -499,38 +346,9 @@ describe('ed25519', () => {
|
|||||||
// );
|
// );
|
||||||
// });
|
// });
|
||||||
|
|
||||||
{
|
should(`wycheproof/ED25519 (OLD)`, () => {
|
||||||
const group = x25519vectors.testGroups[0];
|
for (let g = 0; g < ed25519vectors_OLD.testGroups.length; g++) {
|
||||||
should(`Wycheproof/X25519`, () => {
|
const group = ed25519vectors_OLD.testGroups[g];
|
||||||
for (let i = 0; i < group.tests.length; i++) {
|
|
||||||
const v = group.tests[i];
|
|
||||||
const comment = `(${i}, ${v.result}) ${v.comment}`;
|
|
||||||
if (v.result === 'valid' || v.result === 'acceptable') {
|
|
||||||
try {
|
|
||||||
const shared = hex(x25519.scalarMult(v.private, v.public));
|
|
||||||
deepStrictEqual(shared, v.shared, comment);
|
|
||||||
} catch (e) {
|
|
||||||
// We are more strict
|
|
||||||
if (e.message.includes('Expected valid scalar')) return;
|
|
||||||
if (e.message.includes('Invalid private or public key received')) return;
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
} else if (v.result === 'invalid') {
|
|
||||||
let failed = false;
|
|
||||||
try {
|
|
||||||
x25519.scalarMult(v.private, v.public);
|
|
||||||
} catch (error) {
|
|
||||||
failed = true;
|
|
||||||
}
|
|
||||||
deepStrictEqual(failed, true, comment);
|
|
||||||
} else throw new Error('unknown test result');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
should(`Wycheproof/ED25519`, () => {
|
|
||||||
for (let g = 0; g < ed25519vectors.testGroups.length; g++) {
|
|
||||||
const group = ed25519vectors.testGroups[g];
|
|
||||||
const key = group.key;
|
const key = group.key;
|
||||||
deepStrictEqual(hex(ed.getPublicKey(key.sk)), key.pk, `(${g}, public)`);
|
deepStrictEqual(hex(ed.getPublicKey(key.sk)), key.pk, `(${g}, public)`);
|
||||||
for (let i = 0; i < group.tests.length; i++) {
|
for (let i = 0; i < group.tests.length; i++) {
|
||||||
@@ -552,98 +370,35 @@ describe('ed25519', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
should('Property test issue #1', () => {
|
should(`wycheproof/ED25519`, () => {
|
||||||
|
for (let g = 0; g < ed25519vectors.testGroups.length; g++) {
|
||||||
|
const group = ed25519vectors.testGroups[g];
|
||||||
|
const key = group.publicKey;
|
||||||
|
for (let i = 0; i < group.tests.length; i++) {
|
||||||
|
const v = group.tests[i];
|
||||||
|
const comment = `(${g}/${i}, ${v.result}): ${v.comment}`;
|
||||||
|
if (v.result === 'valid' || v.result === 'acceptable') {
|
||||||
|
deepStrictEqual(ed.verify(v.sig, v.msg, key.pk), true, comment);
|
||||||
|
} else if (v.result === 'invalid') {
|
||||||
|
let failed = false;
|
||||||
|
try {
|
||||||
|
failed = !ed.verify(v.sig, v.msg, key.pk);
|
||||||
|
} catch (error) {
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
|
deepStrictEqual(failed, true, comment);
|
||||||
|
} else throw new Error('unknown test result');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
should('not mutate inputs', () => {
|
||||||
const message = new Uint8Array([12, 12, 12]);
|
const message = new Uint8Array([12, 12, 12]);
|
||||||
const signature = ed.sign(message, to32Bytes(1n));
|
const signature = ed.sign(message, to32Bytes(1n));
|
||||||
const publicKey = ed.getPublicKey(to32Bytes(1n)); // <- was 1n
|
const publicKey = ed.getPublicKey(to32Bytes(1n)); // <- was 1n
|
||||||
deepStrictEqual(ed.verify(signature, message, publicKey), true);
|
deepStrictEqual(ed.verify(signature, message, publicKey), true);
|
||||||
});
|
});
|
||||||
|
|
||||||
const VECTORS_RFC8032_CTX = [
|
|
||||||
{
|
|
||||||
secretKey: '0305334e381af78f141cb666f6199f57bc3495335a256a95bd2a55bf546663f6',
|
|
||||||
publicKey: 'dfc9425e4f968f7f0c29f0259cf5f9aed6851c2bb4ad8bfb860cfee0ab248292',
|
|
||||||
message: 'f726936d19c800494e3fdaff20b276a8',
|
|
||||||
context: '666f6f',
|
|
||||||
signature:
|
|
||||||
'55a4cc2f70a54e04288c5f4cd1e45a7b' +
|
|
||||||
'b520b36292911876cada7323198dd87a' +
|
|
||||||
'8b36950b95130022907a7fb7c4e9b2d5' +
|
|
||||||
'f6cca685a587b4b21f4b888e4e7edb0d',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
secretKey: '0305334e381af78f141cb666f6199f57bc3495335a256a95bd2a55bf546663f6',
|
|
||||||
publicKey: 'dfc9425e4f968f7f0c29f0259cf5f9aed6851c2bb4ad8bfb860cfee0ab248292',
|
|
||||||
message: 'f726936d19c800494e3fdaff20b276a8',
|
|
||||||
context: '626172',
|
|
||||||
signature:
|
|
||||||
'fc60d5872fc46b3aa69f8b5b4351d580' +
|
|
||||||
'8f92bcc044606db097abab6dbcb1aee3' +
|
|
||||||
'216c48e8b3b66431b5b186d1d28f8ee1' +
|
|
||||||
'5a5ca2df6668346291c2043d4eb3e90d',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
secretKey: '0305334e381af78f141cb666f6199f57bc3495335a256a95bd2a55bf546663f6',
|
|
||||||
publicKey: 'dfc9425e4f968f7f0c29f0259cf5f9aed6851c2bb4ad8bfb860cfee0ab248292',
|
|
||||||
message: '508e9e6882b979fea900f62adceaca35',
|
|
||||||
context: '666f6f',
|
|
||||||
signature:
|
|
||||||
'8b70c1cc8310e1de20ac53ce28ae6e72' +
|
|
||||||
'07f33c3295e03bb5c0732a1d20dc6490' +
|
|
||||||
'8922a8b052cf99b7c4fe107a5abb5b2c' +
|
|
||||||
'4085ae75890d02df26269d8945f84b0b',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
secretKey: 'ab9c2853ce297ddab85c993b3ae14bcad39b2c682beabc27d6d4eb20711d6560',
|
|
||||||
publicKey: '0f1d1274943b91415889152e893d80e93275a1fc0b65fd71b4b0dda10ad7d772',
|
|
||||||
message: 'f726936d19c800494e3fdaff20b276a8',
|
|
||||||
context: '666f6f',
|
|
||||||
signature:
|
|
||||||
'21655b5f1aa965996b3f97b3c849eafb' +
|
|
||||||
'a922a0a62992f73b3d1b73106a84ad85' +
|
|
||||||
'e9b86a7b6005ea868337ff2d20a7f5fb' +
|
|
||||||
'd4cd10b0be49a68da2b2e0dc0ad8960f',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
for (let i = 0; i < VECTORS_RFC8032_CTX.length; i++) {
|
|
||||||
const v = VECTORS_RFC8032_CTX[i];
|
|
||||||
should(`RFC8032ctx/${i}`, () => {
|
|
||||||
deepStrictEqual(hex(ed25519ctx.getPublicKey(v.secretKey)), v.publicKey);
|
|
||||||
deepStrictEqual(hex(ed25519ctx.sign(v.message, v.secretKey, v.context)), v.signature);
|
|
||||||
deepStrictEqual(ed25519ctx.verify(v.signature, v.message, v.publicKey, v.context), true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const VECTORS_RFC8032_PH = [
|
|
||||||
{
|
|
||||||
secretKey: '833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42',
|
|
||||||
publicKey: 'ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf',
|
|
||||||
message: '616263',
|
|
||||||
signature:
|
|
||||||
'98a70222f0b8121aa9d30f813d683f80' +
|
|
||||||
'9e462b469c7ff87639499bb94e6dae41' +
|
|
||||||
'31f85042463c2a355a2003d062adf5aa' +
|
|
||||||
'a10b8c61e636062aaad11c2a26083406',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
for (let i = 0; i < VECTORS_RFC8032_PH.length; i++) {
|
|
||||||
const v = VECTORS_RFC8032_PH[i];
|
|
||||||
should(`RFC8032ph/${i}`, () => {
|
|
||||||
deepStrictEqual(hex(ed25519ph.getPublicKey(v.secretKey)), v.publicKey);
|
|
||||||
deepStrictEqual(hex(ed25519ph.sign(v.message, v.secretKey)), v.signature);
|
|
||||||
deepStrictEqual(ed25519ph.verify(v.signature, v.message, v.publicKey), true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
should('X25519 base point', () => {
|
|
||||||
const { y } = ed25519.ExtendedPoint.BASE;
|
|
||||||
const { Fp } = ed25519.CURVE;
|
|
||||||
const u = Fp.create((y + 1n) * Fp.inv(1n - y));
|
|
||||||
deepStrictEqual(hex(numberToBytesLE(u, 32)), x25519.Gu);
|
|
||||||
});
|
|
||||||
|
|
||||||
should('isTorsionFree()', () => {
|
should('isTorsionFree()', () => {
|
||||||
const orig = ed.utils.getExtendedPublicKey(ed.utils.randomPrivateKey()).point;
|
const orig = ed.utils.getExtendedPublicKey(ed.utils.randomPrivateKey()).point;
|
||||||
for (const hex of ED25519_TORSION_SUBGROUP.slice(1)) {
|
for (const hex of ED25519_TORSION_SUBGROUP.slice(1)) {
|
||||||
@@ -654,15 +409,42 @@ describe('ed25519', () => {
|
|||||||
strictEqual(cleared.isTorsionFree(), true, `cleared must be torsionFree: ${hex}`);
|
strictEqual(cleared.isTorsionFree(), true, `cleared must be torsionFree: ${hex}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
should('ed25519 bug', () => {
|
should('have strict SUF-CMA and SBS properties', () => {
|
||||||
|
// https://eprint.iacr.org/2020/1244
|
||||||
|
const list = [0, 1, 6, 7, 8, 9, 10, 11].map((i) => edgeCases[i]);
|
||||||
|
for (let v of list) {
|
||||||
|
const result = ed.verify(v.signature, v.message, v.pub_key, { zip215: false });
|
||||||
|
strictEqual(result, false, `zip215: false must not validate: ${v.signature}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
should('not verify when sig.s >= CURVE.n', () => {
|
||||||
|
const privateKey = ed.utils.randomPrivateKey();
|
||||||
|
const message = Uint8Array.from([0xab, 0xbc, 0xcd, 0xde]);
|
||||||
|
const publicKey = ed.getPublicKey(privateKey);
|
||||||
|
const signature = ed.sign(message, privateKey);
|
||||||
|
|
||||||
|
const R = signature.slice(0, 32);
|
||||||
|
let s = signature.slice(32, 64);
|
||||||
|
|
||||||
|
s = bytesToHex(s.slice().reverse());
|
||||||
|
s = BigInt('0x' + s);
|
||||||
|
s = s + ed.CURVE.n;
|
||||||
|
s = numberToBytesLE(s, 32);
|
||||||
|
|
||||||
|
const sig_invalid = concatBytes(R, s);
|
||||||
|
deepStrictEqual(ed.verify(sig_invalid, message, publicKey), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
should('not accept point without z, t', () => {
|
||||||
const t = 81718630521762619991978402609047527194981150691135404693881672112315521837062n;
|
const t = 81718630521762619991978402609047527194981150691135404693881672112315521837062n;
|
||||||
const point = ed25519.ExtendedPoint.fromAffine({ x: t, y: t });
|
const point = Point.fromAffine({ x: t, y: t });
|
||||||
throws(() => point.assertValidity());
|
throws(() => point.assertValidity());
|
||||||
// Otherwise (without assertValidity):
|
// Otherwise (without assertValidity):
|
||||||
// const point2 = point.double();
|
// const point2 = point.double();
|
||||||
// point2.toAffine(); // crash!
|
// point2.toAffine(); // crash!
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// ESM is broken.
|
// ESM is broken.
|
||||||
|
|||||||
1
test/ed25519/edge-cases.json
Normal file
1
test/ed25519/edge-cases.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
[{"message":"8c93255d71dcab10e8f379c26200f3c7bd5f09d9bc3068d3ef4edeb4853022b6","pub_key":"c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa","signature":"c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000"},{"message":"9bd9f44f4dcc75bd531b56b2cd280b0bb38fc1cd6d1230e14861d861de092e79","pub_key":"c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa","signature":"f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43a5bb704786be79fc476f91d3f3f89b03984d8068dcf1bb7dfc6637b45450ac04"},{"message":"aebf3f2601a0c8c5d39cc7d8911642f740b78168218da8471772b35f9d35b9ab","pub_key":"f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43","signature":"c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa8c4bd45aecaca5b24fb97bc10ac27ac8751a7dfe1baff8b953ec9f5833ca260e"},{"message":"9bd9f44f4dcc75bd531b56b2cd280b0bb38fc1cd6d1230e14861d861de092e79","pub_key":"cdb267ce40c5cd45306fa5d2f29731459387dbf9eb933b7bd5aed9a765b88d4d","signature":"9046a64750444938de19f227bb80485e92b83fdb4b6506c160484c016cc1852f87909e14428a7a1d62e9f22f3d3ad7802db02eb2e688b6c52fcd6648a98bd009"},{"message":"e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec4011eaccd55b53f56c","pub_key":"cdb267ce40c5cd45306fa5d2f29731459387dbf9eb933b7bd5aed9a765b88d4d","signature":"160a1cb0dc9c0258cd0a7d23e94d8fa878bcb1925f2c64246b2dee1796bed5125ec6bc982a269b723e0668e540911a9a6a58921d6925e434ab10aa7940551a09"},{"message":"e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec4011eaccd55b53f56c","pub_key":"cdb267ce40c5cd45306fa5d2f29731459387dbf9eb933b7bd5aed9a765b88d4d","signature":"21122a84e0b5fca4052f5b1235c80a537878b38f3142356b2c2384ebad4668b7e40bc836dac0f71076f9abe3a53f9c03c1ceeeddb658d0030494ace586687405"},{"message":"85e241a07d148b41e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec40","pub_key":"442aad9f089ad9e14647b1ef9099a1ff4798d78589e66f28eca69c11f582a623","signature":"e96f66be976d82e60150baecff9906684aebb1ef181f67a7189ac78ea23b6c0e547f7690a0e2ddcd04d87dbc3490dc19b3b3052f7ff0538cb68afb369ba3a514"},{"message":"85e241a07d148b41e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec40","pub_key":"442aad9f089ad9e14647b1ef9099a1ff4798d78589e66f28eca69c11f582a623","signature":"8ce5b96c8f26d0ab6c47958c9e68b937104cd36e13c33566acd2fe8d38aa19427e71f98a473474f2f13f06f97c20d58cc3f54b8bd0d272f42b695dd7e89a8c22"},{"message":"9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41","pub_key":"f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43","signature":"ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03be9678ac102edcd92b0210bb34d7428d12ffc5df5f37e359941266a4e35f0f"},{"message":"9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41","pub_key":"f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43","signature":"ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffca8c5b64cd208982aa38d4936621a4775aa233aa0505711d8fdcfdaa943d4908"},{"message":"e96b7021eb39c1a163b6da4e3093dcd3f21387da4cc4572be588fafae23c155b","pub_key":"ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff","signature":"a9d55260f765261eb9b84e106f665e00b867287a761990d7135963ee0a7d59dca5bb704786be79fc476f91d3f3f89b03984d8068dcf1bb7dfc6637b45450ac04"},{"message":"39a591f5321bbe07fd5a23dc2f39d025d74526615746727ceefd6e82ae65c06f","pub_key":"ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff","signature":"a9d55260f765261eb9b84e106f665e00b867287a761990d7135963ee0a7d59dca5bb704786be79fc476f91d3f3f89b03984d8068dcf1bb7dfc6637b45450ac04"}]
|
||||||
117
test/ed448-addons.test.js
Normal file
117
test/ed448-addons.test.js
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
import { bytesToHex as hex, hexToBytes } from '@noble/hashes/utils';
|
||||||
|
import { deepStrictEqual, throws } from 'assert';
|
||||||
|
import { describe, should } from 'micro-should';
|
||||||
|
import { bytesToNumberLE } from '../esm/abstract/utils.js';
|
||||||
|
import { ed448, DecafPoint } from '../esm/ed448.js';
|
||||||
|
|
||||||
|
describe('decaf448', () => {
|
||||||
|
should('follow the byte encodings of small multiples', () => {
|
||||||
|
const encodingsOfSmallMultiples = [
|
||||||
|
// This is the identity point
|
||||||
|
'0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
|
||||||
|
// This is the basepoint
|
||||||
|
'6666666666666666666666666666666666666666666666666666666633333333333333333333333333333333333333333333333333333333',
|
||||||
|
// These are small multiples of the basepoint
|
||||||
|
'c898eb4f87f97c564c6fd61fc7e49689314a1f818ec85eeb3bd5514ac816d38778f69ef347a89fca817e66defdedce178c7cc709b2116e75',
|
||||||
|
'a0c09bf2ba7208fda0f4bfe3d0f5b29a543012306d43831b5adc6fe7f8596fa308763db15468323b11cf6e4aeb8c18fe44678f44545a69bc',
|
||||||
|
'b46f1836aa287c0a5a5653f0ec5ef9e903f436e21c1570c29ad9e5f596da97eeaf17150ae30bcb3174d04bc2d712c8c7789d7cb4fda138f4',
|
||||||
|
'1c5bbecf4741dfaae79db72dface00eaaac502c2060934b6eaaeca6a20bd3da9e0be8777f7d02033d1b15884232281a41fc7f80eed04af5e',
|
||||||
|
'86ff0182d40f7f9edb7862515821bd67bfd6165a3c44de95d7df79b8779ccf6460e3c68b70c16aaa280f2d7b3f22d745b97a89906cfc476c',
|
||||||
|
'502bcb6842eb06f0e49032bae87c554c031d6d4d2d7694efbf9c468d48220c50f8ca28843364d70cee92d6fe246e61448f9db9808b3b2408',
|
||||||
|
'0c9810f1e2ebd389caa789374d78007974ef4d17227316f40e578b336827da3f6b482a4794eb6a3975b971b5e1388f52e91ea2f1bcb0f912',
|
||||||
|
'20d41d85a18d5657a29640321563bbd04c2ffbd0a37a7ba43a4f7d263ce26faf4e1f74f9f4b590c69229ae571fe37fa639b5b8eb48bd9a55',
|
||||||
|
'e6b4b8f408c7010d0601e7eda0c309a1a42720d6d06b5759fdc4e1efe22d076d6c44d42f508d67be462914d28b8edce32e7094305164af17',
|
||||||
|
'be88bbb86c59c13d8e9d09ab98105f69c2d1dd134dbcd3b0863658f53159db64c0e139d180f3c89b8296d0ae324419c06fa87fc7daaf34c1',
|
||||||
|
'a456f9369769e8f08902124a0314c7a06537a06e32411f4f93415950a17badfa7442b6217434a3a05ef45be5f10bd7b2ef8ea00c431edec5',
|
||||||
|
'186e452c4466aa4383b4c00210d52e7922dbf9771e8b47e229a9b7b73c8d10fd7ef0b6e41530f91f24a3ed9ab71fa38b98b2fe4746d51d68',
|
||||||
|
'4ae7fdcae9453f195a8ead5cbe1a7b9699673b52c40ab27927464887be53237f7f3a21b938d40d0ec9e15b1d5130b13ffed81373a53e2b43',
|
||||||
|
'841981c3bfeec3f60cfeca75d9d8dc17f46cf0106f2422b59aec580a58f342272e3a5e575a055ddb051390c54c24c6ecb1e0aceb075f6056',
|
||||||
|
];
|
||||||
|
let B = DecafPoint.BASE;
|
||||||
|
let P = DecafPoint.ZERO;
|
||||||
|
for (const encoded of encodingsOfSmallMultiples) {
|
||||||
|
deepStrictEqual(P.toHex(), encoded);
|
||||||
|
deepStrictEqual(DecafPoint.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.
|
||||||
|
'8e24f838059ee9fef1e209126defe53dcd74ef9b6304601c6966099effffffffffffffffffffffffffffffffffffffffffffffffffffffff',
|
||||||
|
'86fcc7212bd4a0b980928666dc28c444a605ef38e09fb569e28d4443ffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
|
||||||
|
'866d54bd4c4ff41a55d4eefdbeca73cbd653c7bd3135b383708ec0bdffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
|
||||||
|
'4a380ccdab9c86364a89e77a464d64f9157538cfdfa686adc0d5ece4ffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
|
||||||
|
'f22d9d4c945dd44d11e0b1d3d3d358d959b4844d83b08c44e659d79fffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
|
||||||
|
'8cdffc681aa99e9c818c8ef4c3808b58e86acdef1ab68c8477af185bffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
|
||||||
|
'0e1c12ac7b5920effbd044e897c57634e2d05b5c27f8fa3df8a086a1ffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
|
||||||
|
// These are all bad because they're negative field elements.
|
||||||
|
'15141bd2121837ef71a0016bd11be757507221c26542244f23806f3fd3496b7d4c36826276f3bf5deea2c60c4fa4cec69946876da497e795',
|
||||||
|
'455d380238434ab740a56267f4f46b7d2eb2dd8ee905e51d7b0ae8a6cb2bae501e67df34ab21fa45946068c9f233939b1d9521a998b7cb93',
|
||||||
|
'810b1d8e8bf3a9c023294bbfd3d905a97531709bdc0f42390feedd7010f77e98686d400c9c86ed250ceecd9de0a18888ffecda0f4ea1c60d',
|
||||||
|
'd3af9cc41be0e5de83c0c6273bedcb9351970110044a9a41c7b9b2267cdb9d7bf4dc9c2fdb8bed32878184604f1d9944305a8df4274ce301',
|
||||||
|
'9312bcab09009e4330ff89c4bc1e9e000d863efc3c863d3b6c507a40fd2cdefde1bf0892b4b5ed9780b91ed1398fb4a7344c605aa5efda74',
|
||||||
|
'53d11bce9e62a29d63ed82ae93761bdd76e38c21e2822d6ebee5eb1c5b8a03eaf9df749e2490eda9d8ac27d1f71150de93668074d18d1c3a',
|
||||||
|
'697c1aed3cd8858515d4be8ac158b229fe184d79cb2b06e49210a6f3a7cd537bcd9bd390d96c4ab6a4406da5d93640726285370cfa95df80',
|
||||||
|
// These are all bad because they give a nonsquare x².
|
||||||
|
'58ad48715c9a102569b68b88362a4b0645781f5a19eb7e59c6a4686fd0f0750ff42e3d7af1ab38c29d69b670f31258919c9fdbf6093d06c0',
|
||||||
|
'8ca37ee2b15693f06e910cf43c4e32f1d5551dda8b1e48cb6ddd55e440dbc7b296b601919a4e4069f59239ca247ff693f7daa42f086122b1',
|
||||||
|
'982c0ec7f43d9f97c0a74b36db0abd9ca6bfb98123a90782787242c8a523cdc76df14a910d54471127e7662a1059201f902940cd39d57af5',
|
||||||
|
'baa9ab82d07ca282b968a911a6c3728d74bf2fe258901925787f03ee4be7e3cb6684fd1bcfe5071a9a974ad249a4aaa8ca81264216c68574',
|
||||||
|
'2ed9ffe2ded67a372b181ac524996402c42970629db03f5e8636cbaf6074b523d154a7a8c4472c4c353ab88cd6fec7da7780834cc5bd5242',
|
||||||
|
'f063769e4241e76d815800e4933a3a144327a30ec40758ad3723a788388399f7b3f5d45b6351eb8eddefda7d5bff4ee920d338a8b89d8b63',
|
||||||
|
'5a0104f1f55d152ceb68bc138182499891d90ee8f09b40038ccc1e07cb621fd462f781d045732a4f0bda73f0b2acf94355424ff0388d4b9c',
|
||||||
|
];
|
||||||
|
for (const badBytes of badEncodings) {
|
||||||
|
const b = hexToBytes(badBytes);
|
||||||
|
throws(() => DecafPoint.fromHex(b), badBytes);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
should('create right points from uniform hash', () => {
|
||||||
|
const hashes = [
|
||||||
|
'cbb8c991fd2f0b7e1913462d6463e4fd2ce4ccdd28274dc2ca1f4165d5ee6cdccea57be3416e166fd06718a31af45a2f8e987e301be59ae6673e963001dbbda80df47014a21a26d6c7eb4ebe0312aa6fffb8d1b26bc62ca40ed51f8057a635a02c2b8c83f48fa6a2d70f58a1185902c0',
|
||||||
|
'b6d8da654b13c3101d6634a231569e6b85961c3f4b460a08ac4a5857069576b64428676584baa45b97701be6d0b0ba18ac28d443403b45699ea0fbd1164f5893d39ad8f29e48e399aec5902508ea95e33bc1e9e4620489d684eb5c26bc1ad1e09aba61fabc2cdfee0b6b6862ffc8e55a',
|
||||||
|
'36a69976c3e5d74e4904776993cbac27d10f25f5626dd45c51d15dcf7b3e6a5446a6649ec912a56895d6baa9dc395ce9e34b868d9fb2c1fc72eb6495702ea4f446c9b7a188a4e0826b1506b0747a6709f37988ff1aeb5e3788d5076ccbb01a4bc6623c92ff147a1e21b29cc3fdd0e0f4',
|
||||||
|
'd5938acbba432ecd5617c555a6a777734494f176259bff9dab844c81aadcf8f7abd1a9001d89c7008c1957272c1786a4293bb0ee7cb37cf3988e2513b14e1b75249a5343643d3c5e5545a0c1a2a4d3c685927c38bc5e5879d68745464e2589e000b31301f1dfb7471a4f1300d6fd0f99',
|
||||||
|
'4dec58199a35f531a5f0a9f71a53376d7b4bdd6bbd2904234a8ea65bbacbce2a542291378157a8f4be7b6a092672a34d85e473b26ccfbd4cdc6739783dc3f4f6ee3537b7aed81df898c7ea0ae89a15b5559596c2a5eeacf8b2b362f3db2940e3798b63203cae77c4683ebaed71533e51',
|
||||||
|
'df2aa1536abb4acab26efa538ce07fd7bca921b13e17bc5ebcba7d1b6b733deda1d04c220f6b5ab35c61b6bcb15808251cab909a01465b8ae3fc770850c66246d5a9eae9e2877e0826e2b8dc1bc08009590bc6778a84e919fbd28e02a0f9c49b48dc689eb5d5d922dc01469968ee81b5',
|
||||||
|
'e9fb440282e07145f1f7f5ecf3c273212cd3d26b836b41b02f108431488e5e84bd15f2418b3d92a3380dd66a374645c2a995976a015632d36a6c2189f202fc766e1c82f50ad9189be190a1f0e8f9b9e69c9c18cc98fdd885608f68bf0fdedd7b894081a63f70016a8abf04953affbefa',
|
||||||
|
];
|
||||||
|
const encodedHashToPoints = [
|
||||||
|
'0c709c9607dbb01c94513358745b7c23953d03b33e39c7234e268d1d6e24f34014ccbc2216b965dd231d5327e591dc3c0e8844ccfd568848',
|
||||||
|
'76ab794e28ff1224c727fa1016bf7f1d329260b7218a39aea2fdb17d8bd9119017b093d641cedf74328c327184dc6f2a64bd90eddccfcdab',
|
||||||
|
'c8d7ac384143500e50890a1c25d643343accce584caf2544f9249b2bf4a6921082be0e7f3669bb5ec24535e6c45621e1f6dec676edd8b664',
|
||||||
|
'62beffc6b8ee11ccd79dbaac8f0252c750eb052b192f41eeecb12f2979713b563caf7d22588eca5e80995241ef963e7ad7cb7962f343a973',
|
||||||
|
'f4ccb31d263731ab88bed634304956d2603174c66da38742053fa37dd902346c3862155d68db63be87439e3d68758ad7268e239d39c4fd3b',
|
||||||
|
'7e79b00e8e0a76a67c0040f62713b8b8c6d6f05e9c6d02592e8a22ea896f5deacc7c7df5ed42beae6fedb9000285b482aa504e279fd49c32',
|
||||||
|
'20b171cb16be977f15e013b9752cf86c54c631c4fc8cbf7c03c4d3ac9b8e8640e7b0e9300b987fe0ab5044669314f6ed1650ae037db853f1',
|
||||||
|
];
|
||||||
|
|
||||||
|
for (let i = 0; i < hashes.length; i++) {
|
||||||
|
const hash = hexToBytes(hashes[i]);
|
||||||
|
const point = DecafPoint.hashToCurve(hash);
|
||||||
|
deepStrictEqual(point.toHex(), encodedHashToPoints[i]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
should('have proper equality testing', () => {
|
||||||
|
const MAX_448B = BigInt(
|
||||||
|
'0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
|
||||||
|
);
|
||||||
|
const bytes448ToNumberLE = (bytes) => ed448.CURVE.Fp.create(bytesToNumberLE(bytes) & MAX_448B);
|
||||||
|
|
||||||
|
const priv = new Uint8Array([
|
||||||
|
23, 211, 149, 179, 209, 108, 78, 37, 229, 45, 122, 220, 85, 38, 192, 182, 96, 40, 168, 63,
|
||||||
|
175, 194, 73, 202, 14, 175, 78, 15, 117, 175, 40, 32, 218, 221, 151, 58, 158, 91, 250, 141,
|
||||||
|
18, 175, 191, 119, 152, 124, 223, 101, 54, 218, 76, 158, 43, 112, 151, 32,
|
||||||
|
]);
|
||||||
|
const pub = DecafPoint.BASE.multiply(bytes448ToNumberLE(priv));
|
||||||
|
deepStrictEqual(pub.equals(DecafPoint.ZERO), false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ESM is broken.
|
||||||
|
import url from 'url';
|
||||||
|
|
||||||
|
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
|
||||||
|
should.run();
|
||||||
|
}
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
import { deepStrictEqual, throws } from 'assert';
|
import { deepStrictEqual, throws } from 'assert';
|
||||||
import { describe, should } from 'micro-should';
|
import { describe, should } from 'micro-should';
|
||||||
import * as fc from 'fast-check';
|
import * as fc from 'fast-check';
|
||||||
import { ed448, ed448ph, x448 } from '../lib/esm/ed448.js';
|
import { ed448, ed448ph, x448 } from '../esm/ed448.js';
|
||||||
import { hexToBytes, bytesToHex, randomBytes } from '@noble/hashes/utils';
|
import { bytesToHex, concatBytes, hexToBytes, randomBytes } from '@noble/hashes/utils';
|
||||||
import { numberToBytesLE } from '../lib/esm/abstract/utils.js';
|
import { numberToBytesLE } from '../esm/abstract/utils.js';
|
||||||
|
// Old vectors allow to test sign() because they include private key
|
||||||
|
import { default as ed448vectorsOld } from './ed448/ed448_test_OLD.json' assert { type: 'json' };
|
||||||
import { default as ed448vectors } from './wycheproof/ed448_test.json' assert { type: 'json' };
|
import { default as ed448vectors } from './wycheproof/ed448_test.json' assert { type: 'json' };
|
||||||
import { default as x448vectors } from './wycheproof/x448_test.json' assert { type: 'json' };
|
import { default as x448vectors } from './wycheproof/x448_test.json' assert { type: 'json' };
|
||||||
|
|
||||||
@@ -439,9 +441,9 @@ describe('ed448', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('wycheproof', () => {
|
describe('wycheproof (OLD)', () => {
|
||||||
for (let g = 0; g < ed448vectors.testGroups.length; g++) {
|
for (let g = 0; g < ed448vectorsOld.testGroups.length; g++) {
|
||||||
const group = ed448vectors.testGroups[g];
|
const group = ed448vectorsOld.testGroups[g];
|
||||||
const key = group.key;
|
const key = group.key;
|
||||||
should(`ED448(${g}, public)`, () => {
|
should(`ED448(${g}, public)`, () => {
|
||||||
deepStrictEqual(hex(ed.getPublicKey(key.sk)), key.pk);
|
deepStrictEqual(hex(ed.getPublicKey(key.sk)), key.pk);
|
||||||
@@ -467,92 +469,20 @@ describe('ed448', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// ECDH
|
|
||||||
const rfc7748Mul = [
|
|
||||||
{
|
|
||||||
scalar:
|
|
||||||
'3d262fddf9ec8e88495266fea19a34d28882acef045104d0d1aae121700a779c984c24f8cdd78fbff44943eba368f54b29259a4f1c600ad3',
|
|
||||||
u: '06fce640fa3487bfda5f6cf2d5263f8aad88334cbd07437f020f08f9814dc031ddbdc38c19c6da2583fa5429db94ada18aa7a7fb4ef8a086',
|
|
||||||
outputU:
|
|
||||||
'ce3e4ff95a60dc6697da1db1d85e6afbdf79b50a2412d7546d5f239fe14fbaadeb445fc66a01b0779d98223961111e21766282f73dd96b6f',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
scalar:
|
|
||||||
'203d494428b8399352665ddca42f9de8fef600908e0d461cb021f8c538345dd77c3e4806e25f46d3315c44e0a5b4371282dd2c8d5be3095f',
|
|
||||||
u: '0fbcc2f993cd56d3305b0b7d9e55d4c1a8fb5dbb52f8e9a1e9b6201b165d015894e56c4d3570bee52fe205e28a78b91cdfbde71ce8d157db',
|
|
||||||
outputU:
|
|
||||||
'884a02576239ff7a2f2f63b2db6a9ff37047ac13568e1e30fe63c4a7ad1b3ee3a5700df34321d62077e63633c575c1c954514e99da7c179d',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
describe('RFC7748', () => {
|
|
||||||
for (let i = 0; i < rfc7748Mul.length; i++) {
|
|
||||||
const v = rfc7748Mul[i];
|
|
||||||
should(`scalarMult (${i})`, () => {
|
|
||||||
deepStrictEqual(hex(x448.scalarMult(v.scalar, v.u)), v.outputU);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const rfc7748Iter = [
|
|
||||||
{
|
|
||||||
scalar:
|
|
||||||
'3f482c8a9f19b01e6c46ee9711d9dc14fd4bf67af30765c2ae2b846a4d23a8cd0db897086239492caf350b51f833868b9bc2b3bca9cf4113',
|
|
||||||
iters: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
scalar:
|
|
||||||
'aa3b4749d55b9daf1e5b00288826c467274ce3ebbdd5c17b975e09d4af6c67cf10d087202db88286e2b79fceea3ec353ef54faa26e219f38',
|
|
||||||
iters: 1000,
|
|
||||||
},
|
|
||||||
// { scalar: '077f453681caca3693198420bbe515cae0002472519b3e67661a7e89cab94695c8f4bcd66e61b9b9c946da8d524de3d69bd9d9d66b997e37', iters: 1000000 },
|
|
||||||
];
|
|
||||||
for (let i = 0; i < rfc7748Iter.length; i++) {
|
|
||||||
const { scalar, iters } = rfc7748Iter[i];
|
|
||||||
should(`RFC7748: scalarMult iteration (${i})`, () => {
|
|
||||||
let k = x448.Gu;
|
|
||||||
for (let i = 0, u = k; i < iters; i++) [k, u] = [x448.scalarMult(k, u), k];
|
|
||||||
deepStrictEqual(hex(k), scalar);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
should('RFC7748 getSharedKey', () => {
|
|
||||||
const alicePrivate =
|
|
||||||
'9a8f4925d1519f5775cf46b04b5800d4ee9ee8bae8bc5565d498c28dd9c9baf574a9419744897391006382a6f127ab1d9ac2d8c0a598726b';
|
|
||||||
const alicePublic =
|
|
||||||
'9b08f7cc31b7e3e67d22d5aea121074a273bd2b83de09c63faa73d2c22c5d9bbc836647241d953d40c5b12da88120d53177f80e532c41fa0';
|
|
||||||
const bobPrivate =
|
|
||||||
'1c306a7ac2a0e2e0990b294470cba339e6453772b075811d8fad0d1d6927c120bb5ee8972b0d3e21374c9c921b09d1b0366f10b65173992d';
|
|
||||||
const bobPublic =
|
|
||||||
'3eb7a829b0cd20f5bcfc0b599b6feccf6da4627107bdb0d4f345b43027d8b972fc3e34fb4232a13ca706dcb57aec3dae07bdc1c67bf33609';
|
|
||||||
const shared =
|
|
||||||
'07fff4181ac6cc95ec1c16a94a0f74d12da232ce40a77552281d282bb60c0b56fd2464c335543936521c24403085d59a449a5037514a879d';
|
|
||||||
deepStrictEqual(alicePublic, hex(x448.getPublicKey(alicePrivate)));
|
|
||||||
deepStrictEqual(bobPublic, hex(x448.getPublicKey(bobPrivate)));
|
|
||||||
deepStrictEqual(hex(x448.scalarMult(alicePrivate, bobPublic)), shared);
|
|
||||||
deepStrictEqual(hex(x448.scalarMult(bobPrivate, alicePublic)), shared);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('wycheproof', () => {
|
describe('wycheproof', () => {
|
||||||
const group = x448vectors.testGroups[0];
|
for (let g = 0; g < ed448vectors.testGroups.length; g++) {
|
||||||
should(`X448`, () => {
|
const group = ed448vectors.testGroups[g];
|
||||||
|
const key = group.publicKey;
|
||||||
|
should(`ED448`, () => {
|
||||||
for (let i = 0; i < group.tests.length; i++) {
|
for (let i = 0; i < group.tests.length; i++) {
|
||||||
const v = group.tests[i];
|
const v = group.tests[i];
|
||||||
const index = `(${i}, ${v.result}) ${v.comment}`;
|
const index = `${g}/${i} ${v.comment}`;
|
||||||
if (v.result === 'valid' || v.result === 'acceptable') {
|
if (v.result === 'valid' || v.result === 'acceptable') {
|
||||||
try {
|
deepStrictEqual(ed.verify(v.sig, v.msg, key.pk), true, index);
|
||||||
const shared = hex(x448.scalarMult(v.private, v.public));
|
|
||||||
deepStrictEqual(shared, v.shared, index);
|
|
||||||
} 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;
|
|
||||||
if (e.message.includes('Expected 56 bytes')) return;
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
} else if (v.result === 'invalid') {
|
} else if (v.result === 'invalid') {
|
||||||
let failed = false;
|
let failed = false;
|
||||||
try {
|
try {
|
||||||
x448.scalarMult(v.private, v.public);
|
failed = !ed.verify(v.sig, v.msg, key.pk);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
failed = true;
|
failed = true;
|
||||||
}
|
}
|
||||||
@@ -560,8 +490,8 @@ describe('ed448', () => {
|
|||||||
} else throw new Error('unknown test result');
|
} else throw new Error('unknown test result');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// should('X448: should convert base point to montgomery using fromPoint', () => {
|
// should('X448: should convert base point to montgomery using fromPoint', () => {
|
||||||
// deepStrictEqual(
|
// deepStrictEqual(
|
||||||
// hex(ed.montgomeryCurve.UfromPoint(Point.BASE)),
|
// hex(ed.montgomeryCurve.UfromPoint(Point.BASE)),
|
||||||
@@ -584,6 +514,7 @@ describe('ed448', () => {
|
|||||||
// }
|
// }
|
||||||
// });
|
// });
|
||||||
|
|
||||||
|
describe('ed448ctx', () => {
|
||||||
const VECTORS_RFC8032_CTX = [
|
const VECTORS_RFC8032_CTX = [
|
||||||
{
|
{
|
||||||
secretKey:
|
secretKey:
|
||||||
@@ -603,16 +534,20 @@ describe('ed448', () => {
|
|||||||
'3c00',
|
'3c00',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
for (let i = 0; i < VECTORS_RFC8032_CTX.length; i++) {
|
for (let i = 0; i < VECTORS_RFC8032_CTX.length; i++) {
|
||||||
const v = VECTORS_RFC8032_CTX[i];
|
const v = VECTORS_RFC8032_CTX[i];
|
||||||
should(`RFC8032ctx/${i}`, () => {
|
should(`${i}`, () => {
|
||||||
deepStrictEqual(hex(ed.getPublicKey(v.secretKey)), v.publicKey);
|
deepStrictEqual(hex(ed.getPublicKey(v.secretKey)), v.publicKey);
|
||||||
deepStrictEqual(hex(ed.sign(v.message, v.secretKey, v.context)), v.signature);
|
deepStrictEqual(hex(ed.sign(v.message, v.secretKey, { context: v.context })), v.signature);
|
||||||
deepStrictEqual(ed.verify(v.signature, v.message, v.publicKey, v.context), true);
|
deepStrictEqual(
|
||||||
|
ed.verify(v.signature, v.message, v.publicKey, { context: v.context }),
|
||||||
|
true
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('ed448ph', () => {
|
||||||
const VECTORS_RFC8032_PH = [
|
const VECTORS_RFC8032_PH = [
|
||||||
{
|
{
|
||||||
secretKey:
|
secretKey:
|
||||||
@@ -648,23 +583,154 @@ describe('ed448', () => {
|
|||||||
'2100',
|
'2100',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
for (let i = 0; i < VECTORS_RFC8032_PH.length; i++) {
|
for (let i = 0; i < VECTORS_RFC8032_PH.length; i++) {
|
||||||
const v = VECTORS_RFC8032_PH[i];
|
const v = VECTORS_RFC8032_PH[i];
|
||||||
should(`RFC8032ph/${i}`, () => {
|
should(`${i}`, () => {
|
||||||
deepStrictEqual(hex(ed448ph.getPublicKey(v.secretKey)), v.publicKey);
|
deepStrictEqual(hex(ed448ph.getPublicKey(v.secretKey)), v.publicKey);
|
||||||
deepStrictEqual(hex(ed448ph.sign(v.message, v.secretKey, v.context)), v.signature);
|
deepStrictEqual(
|
||||||
deepStrictEqual(ed448ph.verify(v.signature, v.message, v.publicKey, v.context), true);
|
hex(ed448ph.sign(v.message, v.secretKey, { context: v.context })),
|
||||||
|
v.signature
|
||||||
|
);
|
||||||
|
deepStrictEqual(
|
||||||
|
ed448ph.verify(v.signature, v.message, v.publicKey, { context: v.context }),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
should('not verify when sig.s >= CURVE.n', () => {
|
||||||
|
function get56bSig() {
|
||||||
|
const privateKey = ed448.utils.randomPrivateKey();
|
||||||
|
const message = Uint8Array.from([0xab, 0xbc, 0xcd, 0xde]);
|
||||||
|
const publicKey = ed448.getPublicKey(privateKey);
|
||||||
|
const signature = ed448.sign(message, privateKey);
|
||||||
|
|
||||||
|
const R = signature.slice(0, 56);
|
||||||
|
let s = signature.slice(56, 112);
|
||||||
|
|
||||||
|
s = bytesToHex(s.slice().reverse());
|
||||||
|
s = BigInt('0x' + s);
|
||||||
|
s = s + ed448.CURVE.n;
|
||||||
|
s = numberToBytesLE(s, 56);
|
||||||
|
|
||||||
|
const sig_invalid = concatBytes(R, s);
|
||||||
|
return { sig_invalid, message, publicKey };
|
||||||
|
}
|
||||||
|
let sig;
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
sig = get56bSig();
|
||||||
|
break;
|
||||||
|
} catch (error) {
|
||||||
|
// non-56b sig was generated, try again
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throws(() => {
|
||||||
|
ed448.verify(sig.sig_invalid, sig.message, sig.publicKey);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('RFC7748 X448 ECDH', () => {
|
||||||
|
// ECDH
|
||||||
|
const rfc7748Mul = [
|
||||||
|
{
|
||||||
|
scalar:
|
||||||
|
'3d262fddf9ec8e88495266fea19a34d28882acef045104d0d1aae121700a779c984c24f8cdd78fbff44943eba368f54b29259a4f1c600ad3',
|
||||||
|
u: '06fce640fa3487bfda5f6cf2d5263f8aad88334cbd07437f020f08f9814dc031ddbdc38c19c6da2583fa5429db94ada18aa7a7fb4ef8a086',
|
||||||
|
outputU:
|
||||||
|
'ce3e4ff95a60dc6697da1db1d85e6afbdf79b50a2412d7546d5f239fe14fbaadeb445fc66a01b0779d98223961111e21766282f73dd96b6f',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scalar:
|
||||||
|
'203d494428b8399352665ddca42f9de8fef600908e0d461cb021f8c538345dd77c3e4806e25f46d3315c44e0a5b4371282dd2c8d5be3095f',
|
||||||
|
u: '0fbcc2f993cd56d3305b0b7d9e55d4c1a8fb5dbb52f8e9a1e9b6201b165d015894e56c4d3570bee52fe205e28a78b91cdfbde71ce8d157db',
|
||||||
|
outputU:
|
||||||
|
'884a02576239ff7a2f2f63b2db6a9ff37047ac13568e1e30fe63c4a7ad1b3ee3a5700df34321d62077e63633c575c1c954514e99da7c179d',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
for (let i = 0; i < rfc7748Mul.length; i++) {
|
||||||
|
const v = rfc7748Mul[i];
|
||||||
|
should(`scalarMult (${i})`, () => {
|
||||||
|
deepStrictEqual(hex(x448.scalarMult(v.scalar, v.u)), v.outputU);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
should('X448 base point', () => {
|
const rfc7748Iter = [
|
||||||
|
{
|
||||||
|
scalar:
|
||||||
|
'3f482c8a9f19b01e6c46ee9711d9dc14fd4bf67af30765c2ae2b846a4d23a8cd0db897086239492caf350b51f833868b9bc2b3bca9cf4113',
|
||||||
|
iters: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scalar:
|
||||||
|
'aa3b4749d55b9daf1e5b00288826c467274ce3ebbdd5c17b975e09d4af6c67cf10d087202db88286e2b79fceea3ec353ef54faa26e219f38',
|
||||||
|
iters: 1000,
|
||||||
|
},
|
||||||
|
// { scalar: '077f453681caca3693198420bbe515cae0002472519b3e67661a7e89cab94695c8f4bcd66e61b9b9c946da8d524de3d69bd9d9d66b997e37', iters: 1000000 },
|
||||||
|
];
|
||||||
|
for (let i = 0; i < rfc7748Iter.length; i++) {
|
||||||
|
const { scalar, iters } = rfc7748Iter[i];
|
||||||
|
should(`scalarMult iterated ${iters}x`, () => {
|
||||||
|
let k = x448.GuBytes;
|
||||||
|
for (let i = 0, u = k; i < iters; i++) [k, u] = [x448.scalarMult(k, u), k];
|
||||||
|
deepStrictEqual(hex(k), scalar);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
should('getSharedKey', () => {
|
||||||
|
const alicePrivate =
|
||||||
|
'9a8f4925d1519f5775cf46b04b5800d4ee9ee8bae8bc5565d498c28dd9c9baf574a9419744897391006382a6f127ab1d9ac2d8c0a598726b';
|
||||||
|
const alicePublic =
|
||||||
|
'9b08f7cc31b7e3e67d22d5aea121074a273bd2b83de09c63faa73d2c22c5d9bbc836647241d953d40c5b12da88120d53177f80e532c41fa0';
|
||||||
|
const bobPrivate =
|
||||||
|
'1c306a7ac2a0e2e0990b294470cba339e6453772b075811d8fad0d1d6927c120bb5ee8972b0d3e21374c9c921b09d1b0366f10b65173992d';
|
||||||
|
const bobPublic =
|
||||||
|
'3eb7a829b0cd20f5bcfc0b599b6feccf6da4627107bdb0d4f345b43027d8b972fc3e34fb4232a13ca706dcb57aec3dae07bdc1c67bf33609';
|
||||||
|
const shared =
|
||||||
|
'07fff4181ac6cc95ec1c16a94a0f74d12da232ce40a77552281d282bb60c0b56fd2464c335543936521c24403085d59a449a5037514a879d';
|
||||||
|
deepStrictEqual(alicePublic, hex(x448.getPublicKey(alicePrivate)));
|
||||||
|
deepStrictEqual(bobPublic, hex(x448.getPublicKey(bobPrivate)));
|
||||||
|
deepStrictEqual(hex(x448.scalarMult(alicePrivate, bobPublic)), shared);
|
||||||
|
deepStrictEqual(hex(x448.scalarMult(bobPrivate, alicePublic)), shared);
|
||||||
|
});
|
||||||
|
|
||||||
|
should('wycheproof', () => {
|
||||||
|
const group = x448vectors.testGroups[0];
|
||||||
|
for (let i = 0; i < group.tests.length; i++) {
|
||||||
|
const v = group.tests[i];
|
||||||
|
const index = `(${i}, ${v.result}) ${v.comment}`;
|
||||||
|
if (v.result === 'valid' || v.result === 'acceptable') {
|
||||||
|
try {
|
||||||
|
const shared = hex(x448.scalarMult(v.private, v.public));
|
||||||
|
deepStrictEqual(shared, v.shared, index);
|
||||||
|
} 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;
|
||||||
|
if (e.message.includes('Expected 56 bytes')) return;
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
} else if (v.result === 'invalid') {
|
||||||
|
let failed = false;
|
||||||
|
try {
|
||||||
|
x448.scalarMult(v.private, v.public);
|
||||||
|
} catch (error) {
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
|
deepStrictEqual(failed, true, index);
|
||||||
|
} else throw new Error('unknown test result');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
should('have proper base point', () => {
|
||||||
const { x, y } = Point.BASE;
|
const { x, y } = Point.BASE;
|
||||||
const { Fp } = ed448.CURVE;
|
const { Fp } = ed448.CURVE;
|
||||||
// const invX = Fp.invert(x * x); // x²
|
// const invX = Fp.invert(x * x); // x²
|
||||||
const u = Fp.div(Fp.create(y * y), Fp.create(x * x)); // (y²/x²)
|
const u = Fp.div(Fp.create(y * y), Fp.create(x * x)); // (y²/x²)
|
||||||
// const u = Fp.create(y * y * invX);
|
// const u = Fp.create(y * y * invX);
|
||||||
deepStrictEqual(hex(numberToBytesLE(u, 56)), x448.Gu);
|
deepStrictEqual(numberToBytesLE(u, 56), x448.GuBytes);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
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"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@@ -5,18 +5,15 @@ import { bytesToHex } from '@noble/hashes/utils';
|
|||||||
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 * as secp256r1 from '../lib/esm/p256.js';
|
import * as secp256r1 from '../esm/p256.js';
|
||||||
import * as secp384r1 from '../lib/esm/p384.js';
|
import * as secp384r1 from '../esm/p384.js';
|
||||||
import * as secp521r1 from '../lib/esm/p521.js';
|
import * as secp521r1 from '../esm/p521.js';
|
||||||
import * as ed25519 from '../lib/esm/ed25519.js';
|
import * as ed25519 from '../esm/ed25519.js';
|
||||||
import * as ed448 from '../lib/esm/ed448.js';
|
import * as ed448 from '../esm/ed448.js';
|
||||||
import * as 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' };
|
||||||
@@ -26,29 +23,29 @@ import { default as xof_shake128_36 } from './hash-to-curve/expand_message_xof_S
|
|||||||
import { default as xof_shake128_256 } from './hash-to-curve/expand_message_xof_SHAKE128_256.json' assert { type: 'json' };
|
import { default as xof_shake128_256 } from './hash-to-curve/expand_message_xof_SHAKE128_256.json' assert { type: 'json' };
|
||||||
import { default as xof_shake256_36 } from './hash-to-curve/expand_message_xof_SHAKE256_36.json' assert { type: 'json' };
|
import { default as xof_shake256_36 } from './hash-to-curve/expand_message_xof_SHAKE256_36.json' assert { type: 'json' };
|
||||||
// P256
|
// P256
|
||||||
import { default as p256_ro } from './hash-to-curve/P256_XMD:SHA-256_SSWU_RO_.json' assert { type: 'json' };
|
import { default as p256_ro } from './hash-to-curve/P256_XMD_SHA-256_SSWU_RO_.json' assert { type: 'json' };
|
||||||
import { default as p256_nu } from './hash-to-curve/P256_XMD:SHA-256_SSWU_NU_.json' assert { type: 'json' };
|
import { default as p256_nu } from './hash-to-curve/P256_XMD_SHA-256_SSWU_NU_.json' assert { type: 'json' };
|
||||||
// P384
|
// P384
|
||||||
import { default as p384_ro } from './hash-to-curve/P384_XMD:SHA-384_SSWU_RO_.json' assert { type: 'json' };
|
import { default as p384_ro } from './hash-to-curve/P384_XMD_SHA-384_SSWU_RO_.json' assert { type: 'json' };
|
||||||
import { default as p384_nu } from './hash-to-curve/P384_XMD:SHA-384_SSWU_NU_.json' assert { type: 'json' };
|
import { default as p384_nu } from './hash-to-curve/P384_XMD_SHA-384_SSWU_NU_.json' assert { type: 'json' };
|
||||||
// P521
|
// P521
|
||||||
import { default as p521_ro } from './hash-to-curve/P521_XMD:SHA-512_SSWU_RO_.json' assert { type: 'json' };
|
import { default as p521_ro } from './hash-to-curve/P521_XMD_SHA-512_SSWU_RO_.json' assert { type: 'json' };
|
||||||
import { default as p521_nu } from './hash-to-curve/P521_XMD:SHA-512_SSWU_NU_.json' assert { type: 'json' };
|
import { default as p521_nu } from './hash-to-curve/P521_XMD_SHA-512_SSWU_NU_.json' assert { type: 'json' };
|
||||||
// secp256k1
|
// secp256k1
|
||||||
import { default as secp256k1_ro } from './hash-to-curve/secp256k1_XMD:SHA-256_SSWU_RO_.json' assert { type: 'json' };
|
import { default as secp256k1_ro } from './hash-to-curve/secp256k1_XMD_SHA-256_SSWU_RO_.json' assert { type: 'json' };
|
||||||
import { default as secp256k1_nu } from './hash-to-curve/secp256k1_XMD:SHA-256_SSWU_NU_.json' assert { type: 'json' };
|
import { default as secp256k1_nu } from './hash-to-curve/secp256k1_XMD_SHA-256_SSWU_NU_.json' assert { type: 'json' };
|
||||||
// bls-G1
|
// bls-G1
|
||||||
import { default as g1_ro } from './hash-to-curve/BLS12381G1_XMD:SHA-256_SSWU_RO_.json' assert { type: 'json' };
|
import { default as g1_ro } from './hash-to-curve/BLS12381G1_XMD_SHA-256_SSWU_RO_.json' assert { type: 'json' };
|
||||||
import { default as g1_nu } from './hash-to-curve/BLS12381G1_XMD:SHA-256_SSWU_NU_.json' assert { type: 'json' };
|
import { default as g1_nu } from './hash-to-curve/BLS12381G1_XMD_SHA-256_SSWU_NU_.json' assert { type: 'json' };
|
||||||
// bls-G2
|
// bls-G2
|
||||||
import { default as g2_ro } from './hash-to-curve/BLS12381G2_XMD:SHA-256_SSWU_RO_.json' assert { type: 'json' };
|
import { default as g2_ro } from './hash-to-curve/BLS12381G2_XMD_SHA-256_SSWU_RO_.json' assert { type: 'json' };
|
||||||
import { default as g2_nu } from './hash-to-curve/BLS12381G2_XMD:SHA-256_SSWU_NU_.json' assert { type: 'json' };
|
import { default as g2_nu } from './hash-to-curve/BLS12381G2_XMD_SHA-256_SSWU_NU_.json' assert { type: 'json' };
|
||||||
// ed25519
|
// ed25519
|
||||||
import { default as ed25519_ro } from './hash-to-curve/edwards25519_XMD:SHA-512_ELL2_RO_.json' assert { type: 'json' };
|
import { default as ed25519_ro } from './hash-to-curve/edwards25519_XMD_SHA-512_ELL2_RO_.json' assert { type: 'json' };
|
||||||
import { default as ed25519_nu } from './hash-to-curve/edwards25519_XMD:SHA-512_ELL2_NU_.json' assert { type: 'json' };
|
import { default as ed25519_nu } from './hash-to-curve/edwards25519_XMD_SHA-512_ELL2_NU_.json' assert { type: 'json' };
|
||||||
// ed448
|
// ed448
|
||||||
import { default as ed448_ro } from './hash-to-curve/edwards448_XOF:SHAKE256_ELL2_RO_.json' assert { type: 'json' };
|
import { default as ed448_ro } from './hash-to-curve/edwards448_XOF_SHAKE256_ELL2_RO_.json' assert { type: 'json' };
|
||||||
import { default as ed448_nu } from './hash-to-curve/edwards448_XOF:SHAKE256_ELL2_NU_.json' assert { type: 'json' };
|
import { default as ed448_nu } from './hash-to-curve/edwards448_XOF_SHAKE256_ELL2_NU_.json' assert { type: 'json' };
|
||||||
|
|
||||||
function testExpandXMD(hash, vectors) {
|
function testExpandXMD(hash, vectors) {
|
||||||
describe(`${vectors.hash}/${vectors.DST.length}`, () => {
|
describe(`${vectors.hash}/${vectors.DST.length}`, () => {
|
||||||
@@ -56,9 +53,9 @@ function testExpandXMD(hash, vectors) {
|
|||||||
const t = vectors.tests[i];
|
const t = vectors.tests[i];
|
||||||
should(`${vectors.hash}/${vectors.DST.length}/${i}`, () => {
|
should(`${vectors.hash}/${vectors.DST.length}/${i}`, () => {
|
||||||
const p = expand_message_xmd(
|
const p = expand_message_xmd(
|
||||||
stringToBytes(t.msg),
|
utf8ToBytes(t.msg),
|
||||||
stringToBytes(vectors.DST),
|
utf8ToBytes(vectors.DST),
|
||||||
t.len_in_bytes,
|
Number.parseInt(t.len_in_bytes),
|
||||||
hash
|
hash
|
||||||
);
|
);
|
||||||
deepStrictEqual(bytesToHex(p), t.uniform_bytes);
|
deepStrictEqual(bytesToHex(p), t.uniform_bytes);
|
||||||
@@ -79,9 +76,9 @@ function testExpandXOF(hash, vectors) {
|
|||||||
const t = vectors.tests[i];
|
const t = vectors.tests[i];
|
||||||
should(`${i}`, () => {
|
should(`${i}`, () => {
|
||||||
const p = expand_message_xof(
|
const p = expand_message_xof(
|
||||||
stringToBytes(t.msg),
|
utf8ToBytes(t.msg),
|
||||||
stringToBytes(vectors.DST),
|
utf8ToBytes(vectors.DST),
|
||||||
+t.len_in_bytes,
|
Number.parseInt(t.len_in_bytes),
|
||||||
vectors.k,
|
vectors.k,
|
||||||
hash
|
hash
|
||||||
);
|
);
|
||||||
@@ -112,7 +109,7 @@ function testCurve(curve, ro, nu) {
|
|||||||
const t = ro.vectors[i];
|
const t = ro.vectors[i];
|
||||||
should(`(${i})`, () => {
|
should(`(${i})`, () => {
|
||||||
const p = curve
|
const p = curve
|
||||||
.hashToCurve(stringToBytes(t.msg), {
|
.hashToCurve(utf8ToBytes(t.msg), {
|
||||||
DST: ro.dst,
|
DST: ro.dst,
|
||||||
})
|
})
|
||||||
.toAffine();
|
.toAffine();
|
||||||
@@ -126,7 +123,7 @@ function testCurve(curve, ro, nu) {
|
|||||||
const t = nu.vectors[i];
|
const t = nu.vectors[i];
|
||||||
should(`(${i})`, () => {
|
should(`(${i})`, () => {
|
||||||
const p = curve
|
const p = curve
|
||||||
.encodeToCurve(stringToBytes(t.msg), {
|
.encodeToCurve(utf8ToBytes(t.msg), {
|
||||||
DST: nu.dst,
|
DST: nu.dst,
|
||||||
})
|
})
|
||||||
.toAffine();
|
.toAffine();
|
||||||
@@ -140,8 +137,8 @@ function testCurve(curve, ro, nu) {
|
|||||||
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);
|
||||||
testCurve(bls12_381.hashToCurve.G1, g1_ro, g1_nu);
|
testCurve(bls12_381.G1, g1_ro, g1_nu);
|
||||||
testCurve(bls12_381.hashToCurve.G2, g2_ro, g2_nu);
|
testCurve(bls12_381.G2, g2_ro, g2_nu);
|
||||||
testCurve(secp256k1, secp256k1_ro, secp256k1_nu);
|
testCurve(secp256k1, secp256k1_ro, secp256k1_nu);
|
||||||
testCurve(ed25519, ed25519_ro, ed25519_nu);
|
testCurve(ed25519, ed25519_ro, ed25519_nu);
|
||||||
testCurve(ed448, ed448_ro, ed448_nu);
|
testCurve(ed448, ed448_ro, ed448_nu);
|
||||||
|
|||||||
@@ -4,11 +4,14 @@ import { should } from 'micro-should';
|
|||||||
import './basic.test.js';
|
import './basic.test.js';
|
||||||
import './nist.test.js';
|
import './nist.test.js';
|
||||||
import './ed448.test.js';
|
import './ed448.test.js';
|
||||||
|
import './ed448-addons.test.js';
|
||||||
import './ed25519.test.js';
|
import './ed25519.test.js';
|
||||||
|
import './ed25519-addons.test.js';
|
||||||
import './secp256k1.test.js';
|
import './secp256k1.test.js';
|
||||||
import './stark/index.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,4 +1,4 @@
|
|||||||
import { jubjub, findGroupHash } from '../lib/esm/jubjub.js';
|
import { jubjub, findGroupHash } from '../esm/jubjub.js';
|
||||||
import { describe, should } from 'micro-should';
|
import { describe, should } from 'micro-should';
|
||||||
import { deepStrictEqual, throws } from 'assert';
|
import { deepStrictEqual, throws } from 'assert';
|
||||||
const Point = jubjub.ExtendedPoint;
|
const Point = jubjub.ExtendedPoint;
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
import { deepStrictEqual, throws } from 'assert';
|
import { deepStrictEqual, throws } from 'assert';
|
||||||
import { describe, 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 endoVectors } from './vectors/secp256k1/endomorphism.json' assert { type: 'json' };
|
||||||
|
|
||||||
import { default as ecdh_secp224r1_test } from './wycheproof/ecdh_secp224r1_test.json' assert { type: 'json' };
|
import { default as ecdh_secp224r1_test } from './wycheproof/ecdh_secp224r1_test.json' assert { type: 'json' };
|
||||||
import { default as ecdh_secp256r1_test } from './wycheproof/ecdh_secp256r1_test.json' assert { type: 'json' };
|
import { default as ecdh_secp256r1_test } from './wycheproof/ecdh_secp256r1_test.json' assert { type: 'json' };
|
||||||
@@ -23,38 +24,59 @@ import { default as secp224r1_sha3_224_test } from './wycheproof/ecdsa_secp224r1
|
|||||||
import { default as secp224r1_sha3_256_test } from './wycheproof/ecdsa_secp224r1_sha3_256_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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_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 } from '@noble/hashes/sha3';
|
import { sha3_224, sha3_256, sha3_384, sha3_512, shake128, shake256 } from '@noble/hashes/sha3';
|
||||||
import { sha512, sha384 } from '@noble/hashes/sha512';
|
import { sha512, sha384 } from '@noble/hashes/sha512';
|
||||||
import { sha224, sha256 } from '@noble/hashes/sha256';
|
import { sha224, sha256 } from '@noble/hashes/sha256';
|
||||||
|
|
||||||
|
// 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,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -73,79 +95,32 @@ should('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]);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('wycheproof ECDH', () => {
|
// We don't support ASN.1 encoding of points. For tests we've implemented quick
|
||||||
for (const group of ecdh.testGroups) {
|
// and dirty parser: take X last bytes of ASN.1 encoded sequence.
|
||||||
// // Tested in secp256k1.test.js
|
// If that doesn't work, we ignore such vector.
|
||||||
// if (group.key.curve === 'secp256k1') continue;
|
function verifyECDHVector(test, curve) {
|
||||||
// We don't have SHA-224
|
if (test.flags.includes('InvalidAsn')) return; // Ignore invalid ASN
|
||||||
const CURVE = NIST[group.curve];
|
|
||||||
if (!CURVE) continue;
|
|
||||||
should(group.curve, () => {
|
|
||||||
for (const test of group.tests) {
|
|
||||||
if (test.result === 'valid' || test.result === 'acceptable') {
|
if (test.result === 'valid' || test.result === 'acceptable') {
|
||||||
|
const fnLen = curve.CURVE.nByteLength; // 32 for P256
|
||||||
|
const fpLen = curve.CURVE.Fp.BYTES; // 32 for P256
|
||||||
|
const encodedHexLen = fpLen * 2 * 2 + 2; // 130 (65 * 2) for P256
|
||||||
|
const pubB = test.public.slice(-encodedHexLen); // slice(-130) for P256
|
||||||
|
let privA = test.private;
|
||||||
|
|
||||||
|
// Some wycheproof vectors are padded with 00:
|
||||||
|
// 00c6cafb74e2a50c83b3d232c4585237f44d4c5433c4b3f50ce978e6aeda3a4f5d
|
||||||
|
// instead of
|
||||||
|
// c6cafb74e2a50c83b3d232c4585237f44d4c5433c4b3f50ce978e6aeda3a4f5d
|
||||||
|
if (privA.length / 2 === fnLen + 1 && privA.startsWith('00')) privA = privA.slice(2);
|
||||||
|
|
||||||
|
if (!curve.utils.isValidPrivateKey(privA)) return; // Ignore invalid private key size
|
||||||
try {
|
try {
|
||||||
const pub = CURVE.ProjectivePoint.fromHex(test.public);
|
curve.ProjectivePoint.fromHex(pubB);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Our strict validation filter doesn't let weird-length DER vectors
|
if (e.message.startsWith('Point of length')) return; // Ignore
|
||||||
if (e.message.startsWith('Point of length')) continue;
|
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
const shared = CURVE.getSharedSecret(test.private, test.public);
|
const shared = curve.getSharedSecret(privA, pubB).subarray(1);
|
||||||
deepStrictEqual(shared, test.shared, 'valid');
|
|
||||||
} else if (test.result === 'invalid') {
|
|
||||||
let failed = false;
|
|
||||||
try {
|
|
||||||
CURVE.getSharedSecret(test.private, test.public);
|
|
||||||
} catch (error) {
|
|
||||||
failed = true;
|
|
||||||
}
|
|
||||||
deepStrictEqual(failed, true, 'invalid');
|
|
||||||
} else throw new Error('unknown test result');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// More per curve tests
|
|
||||||
const WYCHEPROOF_ECDH = {
|
|
||||||
P224: {
|
|
||||||
curve: P224,
|
|
||||||
tests: [ecdh_secp224r1_test],
|
|
||||||
},
|
|
||||||
P256: {
|
|
||||||
curve: P256,
|
|
||||||
tests: [ecdh_secp256r1_test],
|
|
||||||
},
|
|
||||||
secp256k1: {
|
|
||||||
curve: secp256k1,
|
|
||||||
tests: [ecdh_secp256k1_test],
|
|
||||||
},
|
|
||||||
P384: {
|
|
||||||
curve: P384,
|
|
||||||
tests: [ecdh_secp384r1_test],
|
|
||||||
},
|
|
||||||
P521: {
|
|
||||||
curve: P521,
|
|
||||||
tests: [ecdh_secp521r1_test],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const name in WYCHEPROOF_ECDH) {
|
|
||||||
const { curve, tests } = WYCHEPROOF_ECDH[name];
|
|
||||||
for (let i = 0; i < tests.length; i++) {
|
|
||||||
const test = tests[i];
|
|
||||||
for (let j = 0; j < test.testGroups.length; j++) {
|
|
||||||
const group = test.testGroups[j];
|
|
||||||
should(`additional ${name} (${i}/${j})`, () => {
|
|
||||||
for (const test of group.tests) {
|
|
||||||
if (test.result === 'valid' || test.result === 'acceptable') {
|
|
||||||
try {
|
|
||||||
const pub = curve.ProjectivePoint.fromHex(test.public);
|
|
||||||
} catch (e) {
|
|
||||||
// Our strict validation filter doesn't let weird-length DER vectors
|
|
||||||
if (e.message.includes('Point of length')) continue;
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
const shared = curve.getSharedSecret(test.private, test.public);
|
|
||||||
deepStrictEqual(hex(shared), test.shared, 'valid');
|
deepStrictEqual(hex(shared), test.shared, 'valid');
|
||||||
} else if (test.result === 'invalid') {
|
} else if (test.result === 'invalid') {
|
||||||
let failed = false;
|
let failed = false;
|
||||||
@@ -156,6 +131,52 @@ describe('wycheproof ECDH', () => {
|
|||||||
}
|
}
|
||||||
deepStrictEqual(failed, true, 'invalid');
|
deepStrictEqual(failed, true, 'invalid');
|
||||||
} else throw new Error('unknown test result');
|
} else throw new Error('unknown test result');
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('wycheproof ECDH', () => {
|
||||||
|
for (const group of ecdh.testGroups) {
|
||||||
|
const curve = NIST[group.curve];
|
||||||
|
if (!curve) continue;
|
||||||
|
should(group.curve, () => {
|
||||||
|
for (const test of group.tests) {
|
||||||
|
verifyECDHVector(test, curve);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 curveTests = tests[i];
|
||||||
|
for (let j = 0; j < curveTests.testGroups.length; j++) {
|
||||||
|
const group = curveTests.testGroups[j];
|
||||||
|
should(`additional ${name} (${group.tests.length})`, () => {
|
||||||
|
for (const test of group.tests) {
|
||||||
|
verifyECDHVector(test, curve);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -164,8 +185,8 @@ describe('wycheproof ECDH', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const WYCHEPROOF_ECDSA = {
|
const WYCHEPROOF_ECDSA = {
|
||||||
P224: {
|
p224: {
|
||||||
curve: P224,
|
curve: p224,
|
||||||
hashes: {
|
hashes: {
|
||||||
sha224: {
|
sha224: {
|
||||||
hash: sha224,
|
hash: sha224,
|
||||||
@@ -191,6 +212,10 @@ 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: {
|
||||||
@@ -198,7 +223,7 @@ const WYCHEPROOF_ECDSA = {
|
|||||||
hashes: {
|
hashes: {
|
||||||
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,
|
||||||
@@ -212,10 +237,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: {
|
},
|
||||||
curve: P256,
|
p256: {
|
||||||
|
curve: p256,
|
||||||
hashes: {
|
hashes: {
|
||||||
sha256: {
|
sha256: {
|
||||||
hash: sha256,
|
hash: sha256,
|
||||||
@@ -233,10 +266,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: {
|
},
|
||||||
curve: P384,
|
p384: {
|
||||||
|
curve: p384,
|
||||||
hashes: {
|
hashes: {
|
||||||
sha384: {
|
sha384: {
|
||||||
hash: sha384,
|
hash: sha384,
|
||||||
@@ -254,10 +291,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: {
|
},
|
||||||
curve: P521,
|
p521: {
|
||||||
|
curve: p521,
|
||||||
hashes: {
|
hashes: {
|
||||||
sha3_512: {
|
sha3_512: {
|
||||||
hash: sha3_512,
|
hash: sha3_512,
|
||||||
@@ -267,19 +308,23 @@ 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.ProjectivePoint.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();
|
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;
|
const { sig } = test;
|
||||||
|
|
||||||
if (test.result === 'valid' || test.result === 'acceptable') {
|
if (test.result === 'valid' || test.result === 'acceptable') {
|
||||||
try {
|
try {
|
||||||
CURVE.Signature.fromDER(sig);
|
CURVE.Signature.fromDER(sig);
|
||||||
@@ -311,7 +356,6 @@ describe('wycheproof ECDSA', () => {
|
|||||||
should('generic', () => {
|
should('generic', () => {
|
||||||
for (const group of ecdsa.testGroups) {
|
for (const group of ecdsa.testGroups) {
|
||||||
// Tested in secp256k1.test.js
|
// Tested in secp256k1.test.js
|
||||||
if (group.key.curve === 'secp256k1') continue;
|
|
||||||
let CURVE = NIST[group.key.curve];
|
let CURVE = NIST[group.key.curve];
|
||||||
if (!CURVE) continue;
|
if (!CURVE) continue;
|
||||||
if (group.key.curve === 'secp224r1' && group.sha !== 'SHA-224') {
|
if (group.key.curve === 'secp224r1' && group.sha !== 'SHA-224') {
|
||||||
@@ -324,6 +368,9 @@ describe('wycheproof ECDSA', () => {
|
|||||||
if (['Hash weaker than DL-group'].includes(test.comment)) {
|
if (['Hash weaker than DL-group'].includes(test.comment)) {
|
||||||
continue;
|
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));
|
const m = CURVE.CURVE.hash(hexToBytes(test.msg));
|
||||||
if (test.result === 'valid' || test.result === 'acceptable') {
|
if (test.result === 'valid' || test.result === 'acceptable') {
|
||||||
try {
|
try {
|
||||||
@@ -334,7 +381,12 @@ describe('wycheproof ECDSA', () => {
|
|||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
const verified = CURVE.verify(test.sig, m, pubKey.toHex());
|
const verified = CURVE.verify(test.sig, m, pubKey.toHex());
|
||||||
deepStrictEqual(verified, true, 'valid');
|
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') {
|
} else if (test.result === 'invalid') {
|
||||||
let failed = false;
|
let failed = false;
|
||||||
try {
|
try {
|
||||||
@@ -389,6 +441,43 @@ describe('RFC6979', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
should('properly add leading zero to DER', () => {
|
||||||
|
// 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'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
should('have proper GLV endomorphism logic in secp256k1', () => {
|
||||||
|
const Point = secp256k1.ProjectivePoint;
|
||||||
|
for (let item of endoVectors) {
|
||||||
|
const point = Point.fromAffine({ x: BigInt(item.ax), y: BigInt(item.ay) });
|
||||||
|
const c = point.multiplyUnsafe(BigInt(item.scalar)).toAffine();
|
||||||
|
deepStrictEqual(c.x, BigInt(item.cx));
|
||||||
|
deepStrictEqual(c.y, BigInt(item.cy));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// ESM is broken.
|
// ESM is broken.
|
||||||
import url from 'url';
|
import url from 'url';
|
||||||
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
|
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { deepStrictEqual, throws } from 'assert';
|
import { deepStrictEqual, throws } from 'assert';
|
||||||
import { should, describe } from 'micro-should';
|
import { should, describe } from 'micro-should';
|
||||||
import * as poseidon from '../lib/esm/abstract/poseidon.js';
|
import * as poseidon from '../esm/abstract/poseidon.js';
|
||||||
import * as stark from '../lib/esm/stark.js';
|
import * as stark from './_poseidon.helpers.js';
|
||||||
import * as mod from '../lib/esm/abstract/modular.js';
|
import * as mod from '../esm/abstract/modular.js';
|
||||||
import { default as pvectors } from './vectors/poseidon.json' assert { type: 'json' };
|
import { default as pvectors } from './vectors/poseidon.json' assert { type: 'json' };
|
||||||
const { st1, st2, st3, st4 } = pvectors;
|
const { st1, st2, st3, st4 } = pvectors;
|
||||||
|
|
||||||
@@ -132,7 +132,9 @@ describe('Stark', () => {
|
|||||||
// Official vectors: https://extgit.iaik.tugraz.at/krypto/hadeshash/-/blob/master/code/test_vectors.txt
|
// Official vectors: https://extgit.iaik.tugraz.at/krypto/hadeshash/-/blob/master/code/test_vectors.txt
|
||||||
|
|
||||||
should('poseidonperm_x5_255_3', () => {
|
should('poseidonperm_x5_255_3', () => {
|
||||||
const Fp = mod.Fp(BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001'));
|
const Fp = mod.Field(
|
||||||
|
BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001')
|
||||||
|
);
|
||||||
|
|
||||||
const mds = [
|
const mds = [
|
||||||
[
|
[
|
||||||
@@ -161,6 +163,7 @@ should('poseidonperm_x5_255_3', () => {
|
|||||||
t,
|
t,
|
||||||
roundsFull: 8,
|
roundsFull: 8,
|
||||||
roundsPartial: 57,
|
roundsPartial: 57,
|
||||||
|
sboxPower: 5,
|
||||||
mds,
|
mds,
|
||||||
roundConstants,
|
roundConstants,
|
||||||
});
|
});
|
||||||
@@ -179,7 +182,7 @@ should('poseidonperm_x5_255_3', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
should('poseidonperm_x5_255_5', () => {
|
should('poseidonperm_x5_255_5', () => {
|
||||||
const Fp = mod.Fp(0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001n);
|
const Fp = mod.Field(0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001n);
|
||||||
const t = 5;
|
const t = 5;
|
||||||
|
|
||||||
const mds = [
|
const mds = [
|
||||||
@@ -227,6 +230,7 @@ should('poseidonperm_x5_255_5', () => {
|
|||||||
t,
|
t,
|
||||||
roundsFull: 8,
|
roundsFull: 8,
|
||||||
roundsPartial: 60,
|
roundsPartial: 60,
|
||||||
|
sboxPower: 5,
|
||||||
mds,
|
mds,
|
||||||
roundConstants,
|
roundConstants,
|
||||||
});
|
});
|
||||||
@@ -250,7 +254,7 @@ should('poseidonperm_x5_255_5', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
should('poseidonperm_x5_254_3', () => {
|
should('poseidonperm_x5_254_3', () => {
|
||||||
const Fp = mod.Fp(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001n);
|
const Fp = mod.Field(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001n);
|
||||||
const t = 3;
|
const t = 3;
|
||||||
|
|
||||||
const mds = [
|
const mds = [
|
||||||
@@ -278,6 +282,7 @@ should('poseidonperm_x5_254_3', () => {
|
|||||||
t,
|
t,
|
||||||
roundsFull: 8,
|
roundsFull: 8,
|
||||||
roundsPartial: 57,
|
roundsPartial: 57,
|
||||||
|
sboxPower: 5,
|
||||||
mds,
|
mds,
|
||||||
roundConstants,
|
roundConstants,
|
||||||
});
|
});
|
||||||
@@ -297,7 +302,7 @@ should('poseidonperm_x5_254_3', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
should('poseidonperm_x5_254_5', () => {
|
should('poseidonperm_x5_254_5', () => {
|
||||||
const Fp = mod.Fp(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001n);
|
const Fp = mod.Field(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001n);
|
||||||
const t = 5;
|
const t = 5;
|
||||||
|
|
||||||
const mds = [
|
const mds = [
|
||||||
@@ -345,6 +350,7 @@ should('poseidonperm_x5_254_5', () => {
|
|||||||
t,
|
t,
|
||||||
roundsFull: 8,
|
roundsFull: 8,
|
||||||
roundsPartial: 60,
|
roundsPartial: 60,
|
||||||
|
sboxPower: 5,
|
||||||
mds,
|
mds,
|
||||||
roundConstants,
|
roundConstants,
|
||||||
});
|
});
|
||||||
|
|||||||
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);
|
||||||
@@ -1,22 +1,24 @@
|
|||||||
import * as fc from 'fast-check';
|
import { hexToBytes, bytesToHex as hex } from '@noble/hashes/utils';
|
||||||
import { secp256k1, schnorr } from '../lib/esm/secp256k1.js';
|
|
||||||
import { Fp } from '../lib/esm/abstract/modular.js';
|
|
||||||
import { bytesToNumberBE, ensureBytes, numberToBytesBE } from '../lib/esm/abstract/utils.js';
|
|
||||||
import { readFileSync } from 'fs';
|
|
||||||
import { default as ecdsa } from './vectors/ecdsa.json' assert { type: 'json' };
|
|
||||||
import { default as ecdh } from './vectors/ecdh.json' assert { type: 'json' };
|
|
||||||
import { default as privates } from './vectors/privates.json' assert { type: 'json' };
|
|
||||||
import { default as points } from './vectors/points.json' assert { type: 'json' };
|
|
||||||
import { default as wp } from './vectors/wychenproof.json' assert { type: 'json' };
|
|
||||||
import { should, describe } from 'micro-should';
|
|
||||||
import { deepStrictEqual, throws } from 'assert';
|
import { deepStrictEqual, throws } from 'assert';
|
||||||
import { hexToBytes, bytesToHex } from '@noble/hashes/utils';
|
import * as fc from 'fast-check';
|
||||||
|
import { readFileSync } from 'fs';
|
||||||
|
import { should, describe } from 'micro-should';
|
||||||
|
// prettier-ignore
|
||||||
|
import {
|
||||||
|
secp, sigFromDER, sigToDER, selectHash, normVerifySig, mod, bytesToNumberBE, numberToBytesBE
|
||||||
|
} from './secp256k1.helpers.js';
|
||||||
|
|
||||||
|
import { default as ecdsa } from './vectors/secp256k1/ecdsa.json' assert { type: 'json' };
|
||||||
|
import { default as ecdh } from './wycheproof/ecdh_secp256k1_test.json' assert { type: 'json' };
|
||||||
|
import { default as privates } from './vectors/secp256k1/privates.json' assert { type: 'json' };
|
||||||
|
import { default as points } from './vectors/secp256k1/points.json' assert { type: 'json' };
|
||||||
|
import { default as wp } from './wycheproof/ecdsa_secp256k1_sha256_test.json' assert { type: 'json' };
|
||||||
|
|
||||||
|
// Any changes to the file will need to be aware of the fact
|
||||||
|
// the file is shared between noble-curves and noble-secp256k1.
|
||||||
|
|
||||||
const hex = bytesToHex;
|
|
||||||
const secp = secp256k1;
|
|
||||||
const Point = secp.ProjectivePoint;
|
const Point = secp.ProjectivePoint;
|
||||||
const privatesTxt = readFileSync('./test/vectors/privates-2.txt', 'utf-8');
|
const privatesTxt = readFileSync('./test/vectors/secp256k1/privates-2.txt', 'utf-8');
|
||||||
const schCsv = readFileSync('./test/vectors/schnorr.csv', 'utf-8');
|
|
||||||
|
|
||||||
const FC_BIGINT = fc.bigInt(1n + 1n, secp.CURVE.n - 1n);
|
const FC_BIGINT = fc.bigInt(1n + 1n, secp.CURVE.n - 1n);
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
@@ -193,7 +195,7 @@ describe('secp256k1', () => {
|
|||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(FC_BIGINT, FC_BIGINT, (r, s) => {
|
fc.property(FC_BIGINT, FC_BIGINT, (r, s) => {
|
||||||
const sig = new secp.Signature(r, s);
|
const sig = new secp.Signature(r, s);
|
||||||
deepStrictEqual(secp.Signature.fromDER(sig.toDERHex()), sig);
|
deepStrictEqual(sigFromDER(sigToDER(sig)), sig);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -241,9 +243,9 @@ describe('secp256k1', () => {
|
|||||||
);
|
);
|
||||||
for (const [msg, exp] of CASES) {
|
for (const [msg, exp] of CASES) {
|
||||||
const res = secp.sign(msg, privKey, { extraEntropy: undefined });
|
const res = secp.sign(msg, privKey, { extraEntropy: undefined });
|
||||||
deepStrictEqual(res.toDERHex(), exp);
|
deepStrictEqual(sigToDER(res), exp);
|
||||||
const rs = secp.Signature.fromDER(res.toDERHex()).toCompactHex();
|
const rs = sigFromDER(sigToDER(res)).toCompactHex();
|
||||||
deepStrictEqual(secp.Signature.fromCompact(rs).toDERHex(), exp);
|
deepStrictEqual(sigToDER(secp.Signature.fromCompact(rs)), exp);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
should('handle {extraData} option', () => {
|
should('handle {extraData} option', () => {
|
||||||
@@ -266,6 +268,33 @@ describe('secp256k1', () => {
|
|||||||
deepStrictEqual(sign(ent5), e.extraEntropyMax);
|
deepStrictEqual(sign(ent5), e.extraEntropyMax);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
should('handle one byte {extraData}', () => {
|
||||||
|
const extraEntropy = '01';
|
||||||
|
const privKey = hexToBytes(
|
||||||
|
'0101010101010101010101010101010101010101010101010101010101010101'
|
||||||
|
);
|
||||||
|
const msg = 'd1a9dc8ed4e46a6a3e5e594615ca351d7d7ef44df1e4c94c1802f3592183794b';
|
||||||
|
const res = secp.sign(msg, privKey, { extraEntropy }).toCompactHex();
|
||||||
|
deepStrictEqual(
|
||||||
|
res,
|
||||||
|
'a250ec23a54bfdecf0e924cbf484077c5044410f915cdba86731cb2e4e925aaa5b1e4e3553d88be2c48a9a0d8d849ce2cc5720d25b2f97473e02f2550abe9545'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
should('handle 48 bytes {extraData}', () => {
|
||||||
|
const extraEntropy =
|
||||||
|
'000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000001';
|
||||||
|
const privKey = hexToBytes(
|
||||||
|
'0101010101010101010101010101010101010101010101010101010101010101'
|
||||||
|
);
|
||||||
|
const msg = 'd1a9dc8ed4e46a6a3e5e594615ca351d7d7ef44df1e4c94c1802f3592183794b';
|
||||||
|
const res = secp.sign(msg, privKey, { extraEntropy }).toCompactHex();
|
||||||
|
deepStrictEqual(
|
||||||
|
res,
|
||||||
|
'2bdf40f42ac0e42ee12750d03bb12b75306dae58eb3c961c5a80d78efae93e595295b66e8eb28f1eb046bb129a976340312159ec0c20b97342667572e4a8379a'
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('verify()', () => {
|
describe('verify()', () => {
|
||||||
@@ -342,7 +371,7 @@ describe('secp256k1', () => {
|
|||||||
const s = 115792089237316195423570985008687907852837564279074904382605163141518161494334n;
|
const s = 115792089237316195423570985008687907852837564279074904382605163141518161494334n;
|
||||||
const pub = new Point(x, y, 1n).toRawBytes();
|
const pub = new Point(x, y, 1n).toRawBytes();
|
||||||
const sig = new secp.Signature(r, s);
|
const sig = new secp.Signature(r, s);
|
||||||
deepStrictEqual(secp.verify(sig, msg, pub, { strict: false }), true);
|
deepStrictEqual(secp.verify(sig, msg, pub, { lowS: false }), true);
|
||||||
});
|
});
|
||||||
should('not verify invalid deterministic signatures with RFC 6979', () => {
|
should('not verify invalid deterministic signatures with RFC 6979', () => {
|
||||||
for (const vector of ecdsa.invalid.verify) {
|
for (const vector of ecdsa.invalid.verify) {
|
||||||
@@ -351,29 +380,6 @@ describe('secp256k1', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
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');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('recoverPublicKey()', () => {
|
describe('recoverPublicKey()', () => {
|
||||||
should('recover public key from recovery bit', () => {
|
should('recover public key from recovery bit', () => {
|
||||||
const message = '00000000000000000000000000000000000000000000000000000000deadbeef';
|
const message = '00000000000000000000000000000000000000000000000000000000deadbeef';
|
||||||
@@ -404,7 +410,7 @@ describe('secp256k1', () => {
|
|||||||
should('handle RFC 6979 vectors', () => {
|
should('handle RFC 6979 vectors', () => {
|
||||||
for (const vector of ecdsa.valid) {
|
for (const vector of ecdsa.valid) {
|
||||||
let usig = secp.sign(vector.m, vector.d);
|
let usig = secp.sign(vector.m, vector.d);
|
||||||
let sig = usig.toDERHex();
|
let sig = sigToDER(usig);
|
||||||
const vpub = secp.getPublicKey(vector.d);
|
const vpub = secp.getPublicKey(vector.d);
|
||||||
const recovered = usig.recoverPublicKey(vector.m);
|
const recovered = usig.recoverPublicKey(vector.m);
|
||||||
deepStrictEqual(recovered.toHex(), hex(vpub));
|
deepStrictEqual(recovered.toHex(), hex(vpub));
|
||||||
@@ -459,24 +465,25 @@ describe('secp256k1', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('tweak utilities (legacy)', () => {
|
describe('tweak utilities (legacy)', () => {
|
||||||
const Fn = Fp(secp.CURVE.n);
|
const normal = secp.utils.normPrivateKeyToScalar;
|
||||||
const normal = secp.utils._normalizePrivateKey;
|
|
||||||
const tweakUtils = {
|
const tweakUtils = {
|
||||||
privateAdd: (privateKey, tweak) => {
|
privateAdd: (privateKey, tweak) => {
|
||||||
return numberToBytesBE(Fn.add(normal(privateKey), normal(tweak)), 32);
|
return numberToBytesBE(mod(normal(privateKey) + normal(tweak), secp.CURVE.n), 32);
|
||||||
},
|
},
|
||||||
|
|
||||||
privateNegate: (privateKey) => {
|
privateNegate: (privateKey) => {
|
||||||
return numberToBytesBE(Fn.neg(normal(privateKey)), 32);
|
return numberToBytesBE(mod(-normal(privateKey), secp.CURVE.n), 32);
|
||||||
},
|
},
|
||||||
|
|
||||||
pointAddScalar: (p, tweak, isCompressed) => {
|
pointAddScalar: (p, tweak, isCompressed) => {
|
||||||
// Will throw if tweaked point is at infinity
|
const tweaked = Point.fromHex(p).add(Point.fromPrivateKey(tweak));
|
||||||
return Point.fromHex(p).add(Point.fromPrivateKey(tweak)).toRawBytes(isCompressed);
|
if (tweaked.equals(Point.ZERO)) throw new Error('Tweaked point at infinity');
|
||||||
|
return tweaked.toRawBytes(isCompressed);
|
||||||
},
|
},
|
||||||
|
|
||||||
pointMultiply: (p, tweak, isCompressed) => {
|
pointMultiply: (p, tweak, isCompressed) => {
|
||||||
const t = bytesToNumberBE(ensureBytes(tweak));
|
if (typeof tweak === 'string') tweak = hexToBytes(tweak);
|
||||||
|
const t = bytesToNumberBE(tweak);
|
||||||
return Point.fromHex(p).multiply(t).toRawBytes(isCompressed);
|
return Point.fromHex(p).multiply(t).toRawBytes(isCompressed);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -484,20 +491,20 @@ describe('secp256k1', () => {
|
|||||||
should('privateAdd()', () => {
|
should('privateAdd()', () => {
|
||||||
for (const vector of privates.valid.add) {
|
for (const vector of privates.valid.add) {
|
||||||
const { a, b, expected } = vector;
|
const { a, b, expected } = vector;
|
||||||
deepStrictEqual(bytesToHex(tweakUtils.privateAdd(a, b)), expected);
|
deepStrictEqual(hex(tweakUtils.privateAdd(a, b)), expected);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
should('privateNegate()', () => {
|
should('privateNegate()', () => {
|
||||||
for (const vector of privates.valid.negate) {
|
for (const vector of privates.valid.negate) {
|
||||||
const { a, expected } = vector;
|
const { a, expected } = vector;
|
||||||
deepStrictEqual(bytesToHex(tweakUtils.privateNegate(a)), expected);
|
deepStrictEqual(hex(tweakUtils.privateNegate(a)), expected);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
should('pointAddScalar()', () => {
|
should('pointAddScalar()', () => {
|
||||||
for (const vector of points.valid.pointAddScalar) {
|
for (const vector of points.valid.pointAddScalar) {
|
||||||
const { description, P, d, expected } = vector;
|
const { description, P, d, expected } = vector;
|
||||||
const compressed = !!expected && expected.length === 66; // compressed === 33 bytes
|
const compressed = !!expected && expected.length === 66; // compressed === 33 bytes
|
||||||
deepStrictEqual(bytesToHex(tweakUtils.pointAddScalar(P, d, compressed)), expected);
|
deepStrictEqual(hex(tweakUtils.pointAddScalar(P, d, compressed)), expected);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
should('pointAddScalar() invalid', () => {
|
should('pointAddScalar() invalid', () => {
|
||||||
@@ -509,7 +516,7 @@ describe('secp256k1', () => {
|
|||||||
should('pointMultiply()', () => {
|
should('pointMultiply()', () => {
|
||||||
for (const vector of points.valid.pointMultiply) {
|
for (const vector of points.valid.pointMultiply) {
|
||||||
const { P, d, expected } = vector;
|
const { P, d, expected } = vector;
|
||||||
deepStrictEqual(bytesToHex(tweakUtils.pointMultiply(P, d, true)), expected);
|
deepStrictEqual(hex(tweakUtils.pointMultiply(P, d, true)), expected);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
should('pointMultiply() invalid', () => {
|
should('pointMultiply() invalid', () => {
|
||||||
@@ -523,12 +530,24 @@ describe('secp256k1', () => {
|
|||||||
should('wycheproof vectors', () => {
|
should('wycheproof vectors', () => {
|
||||||
for (let group of wp.testGroups) {
|
for (let group of wp.testGroups) {
|
||||||
// const pubKey = Point.fromHex().toRawBytes();
|
// const pubKey = Point.fromHex().toRawBytes();
|
||||||
const pubKey = group.key.uncompressed;
|
const key = group.publicKey;
|
||||||
|
const pubKey = key.uncompressed;
|
||||||
|
|
||||||
for (let test of group.tests) {
|
for (let test of group.tests) {
|
||||||
const m = secp.CURVE.hash(hexToBytes(test.msg));
|
const h = selectHash(secp);
|
||||||
|
|
||||||
|
const m = h(hexToBytes(test.msg));
|
||||||
if (test.result === 'valid' || test.result === 'acceptable') {
|
if (test.result === 'valid' || test.result === 'acceptable') {
|
||||||
const verified = secp.verify(test.sig, m, pubKey);
|
let sig;
|
||||||
if (secp.Signature.fromDER(test.sig).hasHighS()) {
|
try {
|
||||||
|
sig = sigFromDER(test.sig);
|
||||||
|
} catch (e) {
|
||||||
|
// These old Wycheproof vectors which allows invalid behaviour of DER parser
|
||||||
|
if (e.message === 'Invalid signature integer: negative') continue;
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
const verified = secp.verify(normVerifySig(test.sig), m, pubKey);
|
||||||
|
if (sig.hasHighS()) {
|
||||||
deepStrictEqual(verified, false);
|
deepStrictEqual(verified, false);
|
||||||
} else {
|
} else {
|
||||||
deepStrictEqual(verified, true);
|
deepStrictEqual(verified, true);
|
||||||
|
|||||||
@@ -1,201 +0,0 @@
|
|||||||
import { deepStrictEqual, throws } from 'assert';
|
|
||||||
import { describe, should } from 'micro-should';
|
|
||||||
import * as starknet from '../../lib/esm/stark.js';
|
|
||||||
import { default as issue2 } from './fixtures/issue2.json' assert { type: 'json' };
|
|
||||||
import * as bip32 from '@scure/bip32';
|
|
||||||
import * as bip39 from '@scure/bip39';
|
|
||||||
|
|
||||||
describe('starknet basic', () => {
|
|
||||||
should('Basic elliptic sanity check', () => {
|
|
||||||
const g1 = starknet.ProjectivePoint.BASE;
|
|
||||||
deepStrictEqual(
|
|
||||||
g1.toAffine().x.toString(16),
|
|
||||||
'1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca'
|
|
||||||
);
|
|
||||||
deepStrictEqual(
|
|
||||||
g1.toAffine().y.toString(16),
|
|
||||||
'5668060aa49730b7be4801df46ec62de53ecd11abe43a32873000c36e8dc1f'
|
|
||||||
);
|
|
||||||
const g2 = g1.double();
|
|
||||||
deepStrictEqual(
|
|
||||||
g2.toAffine().x.toString(16),
|
|
||||||
'759ca09377679ecd535a81e83039658bf40959283187c654c5416f439403cf5'
|
|
||||||
);
|
|
||||||
deepStrictEqual(
|
|
||||||
g2.toAffine().y.toString(16),
|
|
||||||
'6f524a3400e7708d5c01a28598ad272e7455aa88778b19f93b562d7a9646c41'
|
|
||||||
);
|
|
||||||
const g3 = g2.add(g1);
|
|
||||||
deepStrictEqual(
|
|
||||||
g3.toAffine().x.toString(16),
|
|
||||||
'411494b501a98abd8262b0da1351e17899a0c4ef23dd2f96fec5ba847310b20'
|
|
||||||
);
|
|
||||||
deepStrictEqual(
|
|
||||||
g3.toAffine().y.toString(16),
|
|
||||||
'7e1b3ebac08924d2c26f409549191fcf94f3bf6f301ed3553e22dfb802f0686'
|
|
||||||
);
|
|
||||||
const g32 = g1.multiply(3n);
|
|
||||||
deepStrictEqual(
|
|
||||||
g32.toAffine().x.toString(16),
|
|
||||||
'411494b501a98abd8262b0da1351e17899a0c4ef23dd2f96fec5ba847310b20'
|
|
||||||
);
|
|
||||||
deepStrictEqual(
|
|
||||||
g32.toAffine().y.toString(16),
|
|
||||||
'7e1b3ebac08924d2c26f409549191fcf94f3bf6f301ed3553e22dfb802f0686'
|
|
||||||
);
|
|
||||||
const minus1 = g1.multiply(starknet.CURVE.n - 1n);
|
|
||||||
deepStrictEqual(
|
|
||||||
minus1.toAffine().x.toString(16),
|
|
||||||
'1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca'
|
|
||||||
);
|
|
||||||
deepStrictEqual(
|
|
||||||
minus1.toAffine().y.toString(16),
|
|
||||||
'7a997f9f55b68e04841b7fe20b9139d21ac132ee541bc5cd78cfff3c91723e2'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
should('Pedersen', () => {
|
|
||||||
deepStrictEqual(
|
|
||||||
starknet.pedersen(2, 3),
|
|
||||||
'0x5774fa77b3d843ae9167abd61cf80365a9b2b02218fc2f628494b5bdc9b33b8'
|
|
||||||
);
|
|
||||||
deepStrictEqual(
|
|
||||||
starknet.pedersen(1, 2),
|
|
||||||
'0x5bb9440e27889a364bcb678b1f679ecd1347acdedcbf36e83494f857cc58026'
|
|
||||||
);
|
|
||||||
deepStrictEqual(
|
|
||||||
starknet.pedersen(3, 4),
|
|
||||||
'0x262697b88544f733e5c6907c3e1763131e9f14c51ee7951258abbfb29415fbf'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
should('Hash chain', () => {
|
|
||||||
deepStrictEqual(
|
|
||||||
starknet.hashChain([1, 2, 3]),
|
|
||||||
'0x5d9d62d4040b977c3f8d2389d494e4e89a96a8b45c44b1368f1cc6ec5418915'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
should('Pedersen hash edgecases', () => {
|
|
||||||
// >>> pedersen_hash(0,0)
|
|
||||||
const zero = '0x49ee3eba8c1600700ee1b87eb599f16716b0b1022947733551fde4050ca6804';
|
|
||||||
deepStrictEqual(starknet.pedersen(0, 0), zero);
|
|
||||||
deepStrictEqual(starknet.pedersen(0n, 0n), zero);
|
|
||||||
deepStrictEqual(starknet.pedersen('0', '0'), zero);
|
|
||||||
deepStrictEqual(starknet.pedersen('0x0', '0x0'), zero);
|
|
||||||
// >>> pedersen_hash(3618502788666131213697322783095070105623107215331596699973092056135872020475,3618502788666131213697322783095070105623107215331596699973092056135872020475)
|
|
||||||
// 3226051580231087455100099637526672350308978851161639703631919449959447036451
|
|
||||||
const big = 3618502788666131213697322783095070105623107215331596699973092056135872020475n;
|
|
||||||
const bigExp = '0x721e167a36655994e88efa865e2ed8a0488d36db4d988fec043cda755728223';
|
|
||||||
deepStrictEqual(starknet.pedersen(big, big), bigExp);
|
|
||||||
// >= FIELD
|
|
||||||
const big2 = 36185027886661312136973227830950701056231072153315966999730920561358720204751n;
|
|
||||||
throws(() => starknet.pedersen(big2, big2), 'big2');
|
|
||||||
|
|
||||||
// FIELD -1
|
|
||||||
const big3 = 3618502788666131213697322783095070105623107215331596699973092056135872020480n;
|
|
||||||
const big3exp = '0x7258fccaf3371fad51b117471d9d888a1786c5694c3e6099160477b593a576e';
|
|
||||||
deepStrictEqual(starknet.pedersen(big3, big3), big3exp, 'big3');
|
|
||||||
// FIELD
|
|
||||||
const big4 = 3618502788666131213697322783095070105623107215331596699973092056135872020481n;
|
|
||||||
throws(() => starknet.pedersen(big4, big4), 'big4');
|
|
||||||
throws(() => starknet.pedersen(-1, -1), 'neg');
|
|
||||||
throws(() => starknet.pedersen(false, false), 'false');
|
|
||||||
throws(() => starknet.pedersen(true, true), 'true');
|
|
||||||
throws(() => starknet.pedersen(10.1, 10.1), 'float');
|
|
||||||
});
|
|
||||||
|
|
||||||
should('hashChain edgecases', () => {
|
|
||||||
deepStrictEqual(starknet.hashChain([32312321312321312312312321n]), '0x1aba6672c014b4838cc201');
|
|
||||||
deepStrictEqual(
|
|
||||||
starknet.hashChain([1n, 2n]),
|
|
||||||
'0x5bb9440e27889a364bcb678b1f679ecd1347acdedcbf36e83494f857cc58026'
|
|
||||||
);
|
|
||||||
deepStrictEqual(
|
|
||||||
starknet.hashChain([1, 2]),
|
|
||||||
'0x5bb9440e27889a364bcb678b1f679ecd1347acdedcbf36e83494f857cc58026'
|
|
||||||
);
|
|
||||||
throws(() => starknet.hashChain([]));
|
|
||||||
throws(() => starknet.hashChain('123'));
|
|
||||||
deepStrictEqual(
|
|
||||||
starknet.hashChain([1, 2]),
|
|
||||||
'0x5bb9440e27889a364bcb678b1f679ecd1347acdedcbf36e83494f857cc58026'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
should('Pedersen hash, issue #2', () => {
|
|
||||||
// Verified with starnet.js
|
|
||||||
deepStrictEqual(
|
|
||||||
starknet.computeHashOnElements(issue2),
|
|
||||||
'0x22064462ea33a6ce5272a295e0f551c5da3834f80d8444e7a4df68190b1bc42'
|
|
||||||
);
|
|
||||||
deepStrictEqual(
|
|
||||||
starknet.computeHashOnElements([]),
|
|
||||||
'0x49ee3eba8c1600700ee1b87eb599f16716b0b1022947733551fde4050ca6804'
|
|
||||||
);
|
|
||||||
deepStrictEqual(
|
|
||||||
starknet.computeHashOnElements([1]),
|
|
||||||
'0x78d74f61aeaa8286418fd34b3a12a610445eba11d00ecc82ecac2542d55f7a4'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
should('Seed derivation (example)', () => {
|
|
||||||
const layer = 'starkex';
|
|
||||||
const application = 'starkdeployement';
|
|
||||||
const mnemonic =
|
|
||||||
'range mountain blast problem vibrant void vivid doctor cluster enough melody ' +
|
|
||||||
'salt layer language laptop boat major space monkey unit glimpse pause change vibrant';
|
|
||||||
const ethAddress = '0xa4864d977b944315389d1765ffa7e66F74ee8cd7';
|
|
||||||
const hdKey = bip32.HDKey.fromMasterSeed(bip39.mnemonicToSeedSync(mnemonic)).derive(
|
|
||||||
starknet.getAccountPath(layer, application, ethAddress, 0)
|
|
||||||
);
|
|
||||||
deepStrictEqual(
|
|
||||||
starknet.grindKey(hdKey.privateKey),
|
|
||||||
'6cf0a8bf113352eb863157a45c5e5567abb34f8d32cddafd2c22aa803f4892c'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
should('Compressed keys', () => {
|
|
||||||
const G = starknet.ProjectivePoint.BASE;
|
|
||||||
const half = starknet.CURVE.n / 2n;
|
|
||||||
const last = starknet.CURVE.n;
|
|
||||||
const vectors = [
|
|
||||||
1n,
|
|
||||||
2n,
|
|
||||||
3n,
|
|
||||||
4n,
|
|
||||||
5n,
|
|
||||||
half - 5n,
|
|
||||||
half - 4n,
|
|
||||||
half - 3n,
|
|
||||||
half - 2n,
|
|
||||||
half - 1n,
|
|
||||||
half,
|
|
||||||
half + 1n,
|
|
||||||
half + 2n,
|
|
||||||
half + 3n,
|
|
||||||
half + 4n,
|
|
||||||
half + 5n,
|
|
||||||
last - 5n,
|
|
||||||
last - 4n,
|
|
||||||
last - 3n,
|
|
||||||
last - 2n,
|
|
||||||
last - 1n,
|
|
||||||
].map((i) => G.multiply(i));
|
|
||||||
const fixPoint = (pt) => pt.toAffine();
|
|
||||||
for (const v of vectors) {
|
|
||||||
const uncompressed = v.toHex();
|
|
||||||
const compressed = v.toHex(true);
|
|
||||||
const exp = fixPoint(v);
|
|
||||||
deepStrictEqual(fixPoint(starknet.ProjectivePoint.fromHex(uncompressed)), exp);
|
|
||||||
deepStrictEqual(fixPoint(starknet.ProjectivePoint.fromHex(compressed)), exp);
|
|
||||||
deepStrictEqual(starknet.ProjectivePoint.fromHex(compressed).toHex(), uncompressed);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
// ESM is broken.
|
|
||||||
import url from 'url';
|
|
||||||
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
|
|
||||||
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
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user