Compare commits
457 Commits
Author | SHA1 | Date | |
---|---|---|---|
a8ea9d757f | |||
|
efeca9f478 | ||
|
8ad2f9a185 | ||
|
be576b4c17 | ||
|
819514576c | ||
|
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 | ||
|
debb9d9709 | ||
|
d2c6459756 | ||
|
47533b6336 | ||
|
00b73b68d3 | ||
|
cef4b52d12 | ||
|
47ce547dcf | ||
|
e2a7594eae | ||
|
823149ecd9 | ||
|
e57aec63d8 | ||
|
837aca98c9 | ||
|
dbb16b0e5e | ||
|
e14af67254 | ||
|
4780850748 | ||
|
3374a70f47 | ||
|
131f88b504 | ||
|
4333e9a686 | ||
|
a60d15ff05 | ||
|
ceffbc69da | ||
|
c75129e629 | ||
|
f39fb80c52 | ||
|
fcd422d246 | ||
|
ed9bf89038 | ||
|
7262b4219f | ||
|
02b0b25147 | ||
|
79100c2d47 | ||
|
4ef2cad685 | ||
|
69b3ab5a57 | ||
|
9465e60d30 | ||
|
0fb78b7097 | ||
|
be0b2a32a5 | ||
|
3d77422731 | ||
|
c46914f1bc | ||
|
f250f355e8 | ||
|
c095d74673 | ||
|
ac52fea952 | ||
|
f2ee24bee4 | ||
|
cffea91061 | ||
|
5fc38fc0e7 | ||
|
849dc38f3c | ||
|
0422e6ef38 | ||
|
21d2438a33 | ||
|
cea4696599 | ||
|
f14b8d2be5 | ||
|
2ed27da8eb | ||
|
17e5be5f1b | ||
|
a49f0d266e | ||
|
bfbcf733e6 | ||
|
7fda6de619 | ||
|
2b908ad602 | ||
|
ceb3f67faa | ||
|
a2c87f9c2f | ||
|
e1fd346279 | ||
|
11e78aadbf | ||
|
055147f1be | ||
|
6f99f6042e | ||
|
1e47bf2372 | ||
|
40530eae0c | ||
|
b9482bb17d | ||
|
74475dca68 | ||
|
f4cf21b9c8 | ||
|
5312d92b2c | ||
|
d1770c0ac7 | ||
|
2d37edf7d1 | ||
|
36998fede8 | ||
|
83960d445d | ||
|
23cc2aa5d1 | ||
|
e45d7c2d25 | ||
|
bfe929aac3 | ||
|
069452dbe7 | ||
|
2e81f31d2e | ||
|
9f7df0f13b | ||
|
5600629bca | ||
|
2bd5e9ac16 | ||
|
6890c26091 | ||
|
a15e3a93a9 | ||
|
910c508da9 | ||
|
12da04a2bb | ||
|
cc2c84f040 | ||
|
5d42549acc | ||
|
65d7256b9e | ||
|
d77a98a7aa | ||
|
1bfab42620 | ||
|
f1ab259941 | ||
|
242ee620c5 | ||
|
d837831d22 | ||
|
cae888d942 | ||
|
1ab77b95dd | ||
|
8b5819b12d | ||
|
4b5560ab4b | ||
|
ba121ff24c | ||
|
0277c01efd | ||
|
6ffe656871 | ||
|
135e69bd7b | ||
|
7a34c16c2b | ||
|
458cddcc7f | ||
|
ccfb8695d5 | ||
|
f165222425 | ||
|
785d74edb9 | ||
|
768b268baf | ||
|
4df1e8de02 | ||
|
dd7b48ac71 | ||
|
254bb712b4 | ||
|
31f780027a | ||
|
80edb3323a | ||
|
d30b1855ee | ||
|
f1d8650842 | ||
|
54c7cf8b33 | ||
|
56892cc164 | ||
|
7d746a7408 | ||
|
989af14b10 | ||
|
0592b16a49 | ||
|
fbf85ce732 | ||
|
cafe51a6e3 | ||
|
43b18ea13b | ||
|
fd75293334 | ||
|
20c6d11917 | ||
|
bbe46843fb | ||
|
9e5ad8dc85 | ||
|
5b305abe85 | ||
|
6b0d9611a5 | ||
|
b92866d9b8 | ||
|
c8fc24fd8f | ||
|
4c6ca2326a | ||
|
c660712fee | ||
|
5983975ada | ||
|
1ed861dbad | ||
|
211c887a57 |
.github
.gitignore.prettierrc.json.vscode
README.mdSECURITY.mdaudit
benchmark
_shared.jsbls.jscurves.jsdecaf448.jsecdh.jshash-to-curve.jsmodular.jspackage.jsonristretto255.jssecp256k1.jsutils.js
build
curve-definitions
LICENSEREADME.mdpackage.json
src
test
basic.test.js
tsconfig.jsonfixtures
ecdh_secp224r1_ecpoint_test.jsonecdh_secp224r1_test.jsonecdh_secp256k1_test.jsonecdh_secp256r1_ecpoint_test.jsonecdh_secp384r1_ecpoint_test.jsonecdh_secp384r1_test.jsonecdh_secp521r1_ecpoint_test.jsonecdh_secp521r1_test.json
index.test.jsrfc6979.test.jssecp256k1.test.jsstarknet
vectors
esm
index.jspackage-lock.jsonpackage.jsonsrc
_shortw_utils.ts
abstract
bls12-381.tsbn254.tsed25519.tsed448.tsindex.tsjubjub.tsmodular.tsp256.tsp384.tsp521.tspackage.jsonpasta.tssecp256k1.tsshortw.tsutils.tstest
1
.github/funding.yml
vendored
1
.github/funding.yml
vendored
@ -1,2 +1 @@
|
||||
github: paulmillr
|
||||
# custom: https://paulmillr.com/funding/
|
33
.github/workflows/nodejs.yml
vendored
33
.github/workflows/nodejs.yml
vendored
@ -1,18 +1,23 @@
|
||||
name: Node CI
|
||||
|
||||
on: [push, pull_request]
|
||||
name: Run node.js tests
|
||||
on:
|
||||
- push
|
||||
- pull_request
|
||||
jobs:
|
||||
test:
|
||||
name: v18 @ ubuntu-latest
|
||||
name: v${{ matrix.node }} @ ubuntu-latest
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node:
|
||||
- 18
|
||||
- 20
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Use Node.js ${{ matrix.node }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm install
|
||||
- run: npm run build --if-present
|
||||
- run: cd curve-definitions; npm install; npm run build --if-present
|
||||
- run: npm test
|
||||
- run: npm run lint --if-present
|
||||
- uses: actions/checkout@1e31de5234b9f8995739874a8ce0492dc87873e2 # v4
|
||||
- name: Use Node.js ${{ matrix.node }}
|
||||
uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
- run: npm install
|
||||
- run: npm run build --if-present
|
||||
- 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 }}
|
28
.github/workflows/upload-release.yml
vendored
Normal file
28
.github/workflows/upload-release.yml
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
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
|
||||
- run: npm ci
|
||||
- run: npm run build
|
||||
- run: |
|
||||
cd build
|
||||
npm ci
|
||||
npm run build:release
|
||||
cd ..
|
||||
- run: gh release upload ${{ github.event.release.tag_name }} build/`npx jsbt outfile`
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
16
.gitignore
vendored
16
.gitignore
vendored
@ -1,7 +1,9 @@
|
||||
build/
|
||||
node_modules/
|
||||
coverage/
|
||||
/lib/**/*.js
|
||||
/lib/**/*.ts
|
||||
/lib/**/*.d.ts.map
|
||||
/curve-definitions/lib
|
||||
node_modules
|
||||
/*.js
|
||||
/esm/*.js
|
||||
*.d.ts
|
||||
*.d.ts.map
|
||||
*.js.map
|
||||
/build
|
||||
/abstract
|
||||
/esm/abstract
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
}
|
20
SECURITY.md
Normal file
20
SECURITY.md
Normal file
@ -0,0 +1,20 @@
|
||||
# Security Policy
|
||||
|
||||
See [README's Security section](./README.md#security) for detailed description of internal security practices.
|
||||
|
||||
## Supported Versions
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| >=1.0.0 | :white_check_mark: |
|
||||
| <1.0.0 | :x: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Use maintainer's email specified at https://github.com/paulmillr.
|
||||
|
||||
It's preferred that you use
|
||||
PGP key from [pgp proof](https://paulmillr.com/pgp_proof.txt) (current is [697079DA6878B89B](https://paulmillr.com/pgp_proof.txt)).
|
||||
Ensure the pgp proof page has maintainer's site/github specified.
|
||||
|
||||
You will get an update as soon as the email is read; a "Security vulnerability" phrase in email's title would help.
|
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.
|
7
benchmark/_shared.js
Normal file
7
benchmark/_shared.js
Normal file
@ -0,0 +1,7 @@
|
||||
export function generateData(curve) {
|
||||
const priv = curve.utils.randomPrivateKey();
|
||||
const pub = curve.getPublicKey(priv);
|
||||
const msg = curve.utils.randomPrivateKey();
|
||||
const sig = curve.sign(msg, priv);
|
||||
return { priv, pub, msg, sig };
|
||||
}
|
67
benchmark/bls.js
Normal file
67
benchmark/bls.js
Normal file
@ -0,0 +1,67 @@
|
||||
import { readFileSync } from 'fs';
|
||||
import { mark, run } from 'micro-bmark';
|
||||
import { bls12_381 as bls } from '../bls12-381.js';
|
||||
|
||||
const G2_VECTORS = readFileSync('../test/bls12-381/bls12-381-g2-test-vectors.txt', 'utf-8')
|
||||
.trim()
|
||||
.split('\n')
|
||||
.map((l) => l.split(':'));
|
||||
|
||||
run(async () => {
|
||||
console.log(`\x1b[36mbls12-381\x1b[0m`);
|
||||
let p1, p2, sig;
|
||||
await mark('init', 1, () => {
|
||||
p1 =
|
||||
bls.G1.ProjectivePoint.BASE.multiply(
|
||||
0x28b90deaf189015d3a325908c5e0e4bf00f84f7e639b056ff82d7e70b6eede4cn
|
||||
);
|
||||
p2 =
|
||||
bls.G2.ProjectivePoint.BASE.multiply(
|
||||
0x28b90deaf189015d3a325908c5e0e4bf00f84f7e639b056ff82d7e70b6eede4dn
|
||||
);
|
||||
bls.pairing(p1, p2);
|
||||
});
|
||||
const priv = '28b90deaf189015d3a325908c5e0e4bf00f84f7e639b056ff82d7e70b6eede4c';
|
||||
sig = bls.sign('09', priv);
|
||||
const pubs = G2_VECTORS.map((v) => bls.getPublicKey(v[0]));
|
||||
const sigs = G2_VECTORS.map((v) => v[2]);
|
||||
const pub = bls.getPublicKey(priv);
|
||||
const pub512 = pubs.slice(0, 512); // .map(bls.PointG1.fromHex)
|
||||
const pub32 = pub512.slice(0, 32);
|
||||
const pub128 = pub512.slice(0, 128);
|
||||
const pub2048 = pub512.concat(pub512, pub512, pub512);
|
||||
const sig512 = sigs.slice(0, 512); // .map(bls.PointG2.fromSignature);
|
||||
const sig32 = sig512.slice(0, 32);
|
||||
const sig128 = sig512.slice(0, 128);
|
||||
const sig2048 = sig512.concat(sig512, sig512, sig512);
|
||||
await mark('getPublicKey 1-bit', 1000, () => bls.getPublicKey('2'.padStart(64, '0')));
|
||||
await mark('getPublicKey', 1000, () => bls.getPublicKey(priv));
|
||||
await mark('sign', 50, () => bls.sign('09', priv));
|
||||
await mark('verify', 50, () => bls.verify(sig, '09', pub));
|
||||
await mark('pairing', 100, () => bls.pairing(p1, p2));
|
||||
|
||||
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/32', 50, () => bls.aggregatePublicKeys(pub32));
|
||||
await mark('aggregatePublicKeys/128', 20, () => bls.aggregatePublicKeys(pub128));
|
||||
await mark('aggregatePublicKeys/512', 10, () => bls.aggregatePublicKeys(pub512));
|
||||
await mark('aggregatePublicKeys/2048', 5, () => bls.aggregatePublicKeys(pub2048));
|
||||
await mark('aggregateSignatures/8', 100, () => bls.aggregateSignatures(sigs.slice(0, 8)));
|
||||
await mark('aggregateSignatures/32', 50, () => bls.aggregateSignatures(sig32));
|
||||
await mark('aggregateSignatures/128', 20, () => bls.aggregateSignatures(sig128));
|
||||
await mark('aggregateSignatures/512', 10, () => bls.aggregateSignatures(sig512));
|
||||
await mark('aggregateSignatures/2048', 5, () => bls.aggregateSignatures(sig2048));
|
||||
});
|
23
benchmark/curves.js
Normal file
23
benchmark/curves.js
Normal file
@ -0,0 +1,23 @@
|
||||
import { run, mark, utils } from 'micro-bmark';
|
||||
import { generateData } from './_shared.js';
|
||||
import { p256 } from '../p256.js';
|
||||
import { p384 } from '../p384.js';
|
||||
import { p521 } from '../p521.js';
|
||||
import { ed25519 } from '../ed25519.js';
|
||||
import { ed448 } from '../ed448.js';
|
||||
|
||||
run(async () => {
|
||||
const RAM = false
|
||||
for (let kv of Object.entries({ ed25519, ed448, p256, p384, p521 })) {
|
||||
const [name, curve] = kv;
|
||||
console.log();
|
||||
console.log(`\x1b[36m${name}\x1b[0m`);
|
||||
if (RAM) utils.logMem();
|
||||
await mark('init', 1, () => curve.utils.precompute(8));
|
||||
const d = generateData(curve);
|
||||
await mark('getPublicKey', 5000, () => curve.getPublicKey(d.priv));
|
||||
await mark('sign', 5000, () => curve.sign(d.msg, d.priv));
|
||||
await mark('verify', 500, () => curve.verify(d.sig, d.msg, d.pub));
|
||||
if (RAM) utils.logMem();
|
||||
}
|
||||
});
|
18
benchmark/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))
|
||||
});
|
21
benchmark/package.json
Normal file
21
benchmark/package.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"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": {
|
||||
"micro-bmark": "0.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@noble/hashes": "^1.1.5",
|
||||
"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();
|
||||
});
|
22
benchmark/secp256k1.js
Normal file
22
benchmark/secp256k1.js
Normal file
@ -0,0 +1,22 @@
|
||||
import { run, mark, utils } from 'micro-bmark';
|
||||
import { secp256k1, schnorr } from '../secp256k1.js';
|
||||
import { generateData } from './_shared.js';
|
||||
|
||||
run(async () => {
|
||||
const RAM = false;
|
||||
if (RAM) utils.logMem();
|
||||
console.log(`\x1b[36msecp256k1\x1b[0m`);
|
||||
await mark('init', 1, () => secp256k1.utils.precompute(8));
|
||||
const d = generateData(secp256k1);
|
||||
await mark('getPublicKey', 10000, () => secp256k1.getPublicKey(d.priv));
|
||||
await mark('sign', 10000, () => secp256k1.sign(d.msg, d.priv));
|
||||
await mark('verify', 1000, () => secp256k1.verify(d.sig, d.msg, d.pub));
|
||||
const pub2 = secp256k1.getPublicKey(secp256k1.utils.randomPrivateKey());
|
||||
await mark('getSharedSecret', 1000, () => secp256k1.getSharedSecret(d.priv, pub2));
|
||||
await mark('recoverPublicKey', 1000, () => d.sig.recoverPublicKey(d.msg));
|
||||
const s = schnorr.sign(d.msg, d.priv);
|
||||
const spub = schnorr.getPublicKey(d.priv);
|
||||
await mark('schnorr.sign', 1000, () => schnorr.sign(d.msg, d.priv));
|
||||
await mark('schnorr.verify', 1000, () => schnorr.verify(s, d.msg, spub));
|
||||
if (RAM) utils.logMem();
|
||||
});
|
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 which contains everything.
|
||||
|
||||
The single 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 };
|
445
build/package-lock.json
generated
Normal file
445
build/package-lock.json
generated
Normal file
@ -0,0 +1,445 @@
|
||||
{
|
||||
"name": "build",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "build",
|
||||
"version": "1.0.0",
|
||||
"devDependencies": {
|
||||
"@noble/curves": "file:..",
|
||||
"esbuild": "0.20.1"
|
||||
}
|
||||
},
|
||||
"..": {
|
||||
"version": "1.4.0",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@noble/hashes": "1.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@paulmillr/jsbt": "0.1.0",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
14
build/package.json
Normal file
14
build/package.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "build",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"main": "input.js",
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
"@noble/curves": "file:..",
|
||||
"esbuild": "0.20.1"
|
||||
},
|
||||
"scripts": {
|
||||
"build:release": "npx esbuild --bundle input.js --outfile=`npx jsbt outfile` --global-name=`npx jsbt global`"
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2022 Paul Miller (https://paulmillr.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the “Software”), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
@ -1,28 +0,0 @@
|
||||
# micro-curve-definitions
|
||||
|
||||
Elliptic curves implementations. `@noble/curves` is zero-dependency library for internal arithmetics.
|
||||
|
||||
`micro-curve-definitions` is the actual implementations. Current functionality:
|
||||
|
||||
- NIST curves: P192, P224, P256, P384, P521 (ECDSA)
|
||||
- secp256k1 (ECDSA, without Schnorr)
|
||||
- stark curve
|
||||
- bn254
|
||||
|
||||
Pairings are not implemented.
|
||||
|
||||
## Usage
|
||||
|
||||
```sh
|
||||
npm install micro-curve-definitions
|
||||
```
|
||||
|
||||
```ts
|
||||
import * as nist from 'micro-curve-definitions';
|
||||
|
||||
// P192, P224, P256, P384, P521, bn254
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT (c) Paul Miller [(https://paulmillr.com)](https://paulmillr.com), see LICENSE file.
|
@ -1,62 +0,0 @@
|
||||
{
|
||||
"name": "micro-curve-definitions",
|
||||
"version": "0.1.0",
|
||||
"description": "Curve definitions for @noble/curves",
|
||||
"files": [
|
||||
"index.js",
|
||||
"index.d.ts",
|
||||
"index.d.ts.map",
|
||||
"index.ts"
|
||||
],
|
||||
"type": "module",
|
||||
"main": "index.js",
|
||||
"module": "index.js",
|
||||
"types": "index.d.ts",
|
||||
"dependencies": {
|
||||
"@noble/curves": "~0.1.0",
|
||||
"@noble/hashes": "1.1.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@scure/base": "~1.1.0",
|
||||
"@scure/bip32": "^1.1.1",
|
||||
"@scure/bip39": "^1.1.0",
|
||||
"@types/node": "^18.11.3",
|
||||
"fast-check": "3.0.0",
|
||||
"micro-should": "0.2.0",
|
||||
"prettier": "2.6.2",
|
||||
"typescript": "4.7.3"
|
||||
},
|
||||
"author": "Paul Miller (https://paulmillr.com)",
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/paulmillr/noble-curves",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/paulmillr/noble-curves.git"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"lint": "prettier --check index.ts",
|
||||
"test": "node test/index.test.js"
|
||||
},
|
||||
"keywords": [
|
||||
"secp192r1",
|
||||
"secp224r1",
|
||||
"secp256r1",
|
||||
"secp384r1",
|
||||
"secp521r1",
|
||||
"NIST P192",
|
||||
"NIST P224",
|
||||
"NIST P256",
|
||||
"NIST P384",
|
||||
"NIST P521",
|
||||
"NIST curves",
|
||||
"EC",
|
||||
"elliptic curves"
|
||||
],
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
]
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
/*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
import { weierstrass, CHash } from '@noble/curves/shortw';
|
||||
import { concatBytes, randomBytes } from '@noble/hashes/utils';
|
||||
import { hmac } from '@noble/hashes/hmac';
|
||||
import { sha256 } from '@noble/hashes/sha256';
|
||||
|
||||
function getHash(hash: CHash) {
|
||||
return {
|
||||
hash,
|
||||
hmac: (key: Uint8Array, ...msgs: Uint8Array[]) => hmac(hash, key, concatBytes(...msgs)),
|
||||
randomBytes,
|
||||
};
|
||||
}
|
||||
|
||||
// Was known as alt_bn128 when it had 128-bit security. Now that it's much lower, the naming
|
||||
// has been changed to its prime bit count.
|
||||
export const bn254 = weierstrass({
|
||||
a: 0n,
|
||||
b: 3n,
|
||||
P: 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47n,
|
||||
n: 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001n,
|
||||
Gx: 1n,
|
||||
Gy: 2n,
|
||||
...getHash(sha256),
|
||||
});
|
@ -1,190 +0,0 @@
|
||||
/*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
import { hmac } from '@noble/hashes/hmac';
|
||||
import { sha256 } from '@noble/hashes/sha256';
|
||||
import { sha384, sha512 } from '@noble/hashes/sha512';
|
||||
import { concatBytes, randomBytes } from '@noble/hashes/utils';
|
||||
import { weierstrass, CHash } from '@noble/curves/shortw';
|
||||
import { mod, pow2 } from '@noble/curves/modular';
|
||||
|
||||
function getHash(hash: CHash) {
|
||||
return {
|
||||
hash,
|
||||
hmac: (key: Uint8Array, ...msgs: Uint8Array[]) => hmac(hash, key, concatBytes(...msgs)),
|
||||
randomBytes,
|
||||
};
|
||||
}
|
||||
|
||||
// https://www.secg.org/sec2-v2.pdf
|
||||
// https://neuromancer.sk/std/secg/secp192r1
|
||||
export const P192 = weierstrass({
|
||||
// Params: a, b
|
||||
a: BigInt('0xfffffffffffffffffffffffffffffffefffffffffffffffc'),
|
||||
b: BigInt('0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1'),
|
||||
// Field over which we'll do calculations. Verify with: 2n ** 192n - 2n ** 64n - 1n
|
||||
P: BigInt('0xfffffffffffffffffffffffffffffffeffffffffffffffff'),
|
||||
// Curve order, total count of valid points in the field. Verify with:
|
||||
n: BigInt('0xffffffffffffffffffffffff99def836146bc9b1b4d22831'),
|
||||
// Base point (x, y) aka generator point
|
||||
Gx: BigInt('0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012'),
|
||||
Gy: BigInt('0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811'),
|
||||
lowS: false,
|
||||
// Default options
|
||||
...getHash(sha256),
|
||||
} as const);
|
||||
export const secp192r1 = P192;
|
||||
// https://neuromancer.sk/std/nist/P-224
|
||||
export const P224 = weierstrass({
|
||||
// Params: a, b
|
||||
a: BigInt('0xfffffffffffffffffffffffffffffffefffffffffffffffffffffffe'),
|
||||
b: BigInt('0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4'),
|
||||
// Field over which we'll do calculations. Verify with:
|
||||
P: 2n ** 224n - 2n ** 96n + 1n,
|
||||
// Curve order, total count of valid points in the field. Verify with:
|
||||
n: BigInt('0xffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d'),
|
||||
// Base point (x, y) aka generator point
|
||||
Gx: BigInt('0xb70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21'),
|
||||
Gy: BigInt('0xbd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34'),
|
||||
lowS: false,
|
||||
// Default options
|
||||
...getHash(sha256),
|
||||
} as const);
|
||||
export const secp224r1 = P224;
|
||||
// https://neuromancer.sk/std/nist/P-256
|
||||
export const P256 = weierstrass({
|
||||
// Params: a, b
|
||||
a: BigInt('0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc'),
|
||||
b: BigInt('0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b'),
|
||||
// Field over which we'll do calculations. Verify with:
|
||||
// 2n ** 224n * (2n ** 32n - 1n) + 2n ** 192n + 2n ** 96n - 1n,
|
||||
P: BigInt('0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff'),
|
||||
// Curve order, total count of valid points in the field. Verify with:
|
||||
n: BigInt('0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551'),
|
||||
// Base point (x, y) aka generator point
|
||||
Gx: BigInt('0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296'),
|
||||
Gy: BigInt('0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5'),
|
||||
lowS: false,
|
||||
// Default options
|
||||
...getHash(sha256),
|
||||
} as const);
|
||||
export const secp256r1 = P256;
|
||||
// https://neuromancer.sk/std/nist/P-384
|
||||
// prettier-ignore
|
||||
export const P384 = weierstrass({
|
||||
// Params: a, b
|
||||
a: BigInt('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000fffffffc'),
|
||||
b: BigInt('0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef'),
|
||||
// Field over which we'll do calculations. Verify with:
|
||||
// 2n ** 384n - 2n ** 128n - 2n ** 96n + 2n ** 32n - 1n
|
||||
P: BigInt('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff'),
|
||||
// Curve order, total count of valid points in the field. Verify with:
|
||||
n: BigInt('0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973'),
|
||||
// Base point (x, y) aka generator point
|
||||
Gx: BigInt('0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7'),
|
||||
Gy: BigInt('0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f'),
|
||||
lowS: false,
|
||||
// Default options
|
||||
...getHash(sha384),
|
||||
} as const);
|
||||
export const secp384r1 = P384;
|
||||
// https://neuromancer.sk/std/nist/P-521
|
||||
// prettier-ignore
|
||||
export const P521 = weierstrass({
|
||||
// Params: a, b
|
||||
a: BigInt('0x01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc'),
|
||||
b: BigInt('0x0051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00'),
|
||||
// Field over which we'll do calculations. Verify with:
|
||||
// 2n ** 521n - 1n,
|
||||
P: BigInt('0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'),
|
||||
// Curve order, total count of valid points in the field. Verify with:
|
||||
n: BigInt('0x01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409'),
|
||||
// Base point (x, y) aka generator point
|
||||
Gx: BigInt('0x00c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66'),
|
||||
Gy: BigInt('0x011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650'),
|
||||
lowS: false,
|
||||
// Default options
|
||||
...getHash(sha512),
|
||||
} as const);
|
||||
export const secp521r1 = P521;
|
||||
|
||||
/**
|
||||
* secp256k1 definition with efficient square root and endomorphism.
|
||||
* Endomorphism works only for Koblitz curves with a == 0.
|
||||
* It improves efficiency:
|
||||
* Uses 2x less RAM, speeds up precomputation by 2x and ECDH / sign key recovery by 20%.
|
||||
* Should always be used for Jacobian's double-and-add multiplication.
|
||||
* For affines cached multiplication, it trades off 1/2 init time & 1/3 ram for 20% perf hit.
|
||||
* https://gist.github.com/paulmillr/eb670806793e84df628a7c434a873066
|
||||
*/
|
||||
const secp256k1P = BigInt('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f');
|
||||
const secp256k1N = BigInt('0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141');
|
||||
const _1n = BigInt(1);
|
||||
const _2n = BigInt(2);
|
||||
const divNearest = (a: bigint, b: bigint) => (a + b / _2n) / b;
|
||||
export const secp256k1 = weierstrass({
|
||||
a: 0n,
|
||||
b: 7n,
|
||||
// Field over which we'll do calculations. Verify with:
|
||||
// 2n ** 256n - 2n ** 32n - 2n ** 9n - 2n ** 8n - 2n ** 7n - 2n ** 6n - 2n ** 4n - 1n
|
||||
P: secp256k1P,
|
||||
// Curve order, total count of valid points in the field. Verify with:
|
||||
n: secp256k1N,
|
||||
// Base point (x, y) aka generator point
|
||||
Gx: BigInt('55066263022277343669578718895168534326250603453777594175500187360389116729240'),
|
||||
Gy: BigInt('32670510020758816978083085130507043184471273380659243275938904335757337482424'),
|
||||
...getHash(sha256),
|
||||
// noble-secp256k1 compat
|
||||
lowS: true,
|
||||
// Used to calculate y - the square root of y².
|
||||
// Exponentiates it to very big number (P+1)/4.
|
||||
// We are unwrapping the loop because it's 2x faster.
|
||||
// (P+1n/4n).toString(2) would produce bits [223x 1, 0, 22x 1, 4x 0, 11, 00]
|
||||
// We are multiplying it bit-by-bit
|
||||
sqrtMod: (x: bigint): bigint => {
|
||||
const P = secp256k1P;
|
||||
const _3n = BigInt(3);
|
||||
const _6n = BigInt(6);
|
||||
const _11n = BigInt(11);
|
||||
const _22n = BigInt(22);
|
||||
const _23n = BigInt(23);
|
||||
const _44n = BigInt(44);
|
||||
const _88n = BigInt(88);
|
||||
const b2 = (x * x * x) % P; // x^3, 11
|
||||
const b3 = (b2 * b2 * x) % P; // x^7
|
||||
const b6 = (pow2(b3, _3n, P) * b3) % P;
|
||||
const b9 = (pow2(b6, _3n, P) * b3) % P;
|
||||
const b11 = (pow2(b9, _2n, P) * b2) % P;
|
||||
const b22 = (pow2(b11, _11n, P) * b11) % P;
|
||||
const b44 = (pow2(b22, _22n, P) * b22) % P;
|
||||
const b88 = (pow2(b44, _44n, P) * b44) % P;
|
||||
const b176 = (pow2(b88, _88n, P) * b88) % P;
|
||||
const b220 = (pow2(b176, _44n, P) * b44) % P;
|
||||
const b223 = (pow2(b220, _3n, P) * b3) % P;
|
||||
const t1 = (pow2(b223, _23n, P) * b22) % P;
|
||||
const t2 = (pow2(t1, _6n, P) * b2) % P;
|
||||
return pow2(t2, _2n, P);
|
||||
},
|
||||
endo: {
|
||||
beta: BigInt('0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee'),
|
||||
splitScalar: (k: bigint) => {
|
||||
const n = secp256k1N;
|
||||
const a1 = BigInt('0x3086d221a7d46bcde86c90e49284eb15');
|
||||
const b1 = -_1n * BigInt('0xe4437ed6010e88286f547fa90abfe4c3');
|
||||
const a2 = BigInt('0x114ca50f7a8e2f3f657c1108d9d44cfd8');
|
||||
const b2 = a1;
|
||||
const POW_2_128 = BigInt('0x100000000000000000000000000000000');
|
||||
|
||||
const c1 = divNearest(b2 * k, n);
|
||||
const c2 = divNearest(-b1 * k, n);
|
||||
let k1 = mod(k - c1 * a1 - c2 * a2, n);
|
||||
let k2 = mod(-c1 * b1 - c2 * b2, n);
|
||||
const k1neg = k1 > POW_2_128;
|
||||
const k2neg = k2 > POW_2_128;
|
||||
if (k1neg) k1 = n - k1;
|
||||
if (k2neg) k2 = n - k2;
|
||||
if (k1 > POW_2_128 || k2 > POW_2_128) {
|
||||
throw new Error('splitScalar: Endomorphism failed, k=' + k);
|
||||
}
|
||||
return { k1neg, k1, k2neg, k2 };
|
||||
},
|
||||
},
|
||||
});
|
@ -1,35 +0,0 @@
|
||||
/*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
import { hmac } from '@noble/hashes/hmac';
|
||||
import { sha256 } from '@noble/hashes/sha256';
|
||||
import { concatBytes, randomBytes } from '@noble/hashes/utils';
|
||||
import { weierstrass, CHash } from '@noble/curves/shortw';
|
||||
|
||||
function getHash(hash: CHash) {
|
||||
return {
|
||||
hash,
|
||||
hmac: (key: Uint8Array, ...msgs: Uint8Array[]) => hmac(hash, key, concatBytes(...msgs)),
|
||||
randomBytes,
|
||||
};
|
||||
}
|
||||
const p = BigInt('0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001');
|
||||
const q = BigInt('0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001');
|
||||
|
||||
export const pallas = weierstrass({
|
||||
a: BigInt(0),
|
||||
b: BigInt(5),
|
||||
P: p,
|
||||
n: q,
|
||||
Gx: BigInt(-1),
|
||||
Gy: BigInt(2),
|
||||
...getHash(sha256),
|
||||
});
|
||||
|
||||
export const vesta = weierstrass({
|
||||
a: BigInt(0),
|
||||
b: BigInt(5),
|
||||
P: q,
|
||||
n: p,
|
||||
Gx: BigInt(-1),
|
||||
Gy: BigInt(2),
|
||||
...getHash(sha256),
|
||||
});
|
@ -1,264 +0,0 @@
|
||||
/*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
import { keccak_256 } from '@noble/hashes/sha3';
|
||||
import { sha256 } from '@noble/hashes/sha256';
|
||||
import { hmac } from '@noble/hashes/hmac';
|
||||
import { concatBytes, randomBytes } from '@noble/hashes/utils';
|
||||
import { weierstrass, CHash, JacobianPointType } from '@noble/curves/shortw';
|
||||
import * as cutils from '@noble/curves/utils';
|
||||
|
||||
function getHash(hash: CHash) {
|
||||
return {
|
||||
hash,
|
||||
hmac: (key: Uint8Array, ...msgs: Uint8Array[]) => hmac(hash, key, concatBytes(...msgs)),
|
||||
randomBytes,
|
||||
};
|
||||
}
|
||||
|
||||
const CURVE_N = 3618502788666131213697322783095070105526743751716087489154079457884512865583n;
|
||||
const nBitLength = 252;
|
||||
// https://docs.starkware.co/starkex/stark-curve.html
|
||||
export const starkCurve = weierstrass({
|
||||
// Params: a, b
|
||||
a: 1n,
|
||||
b: 3141592653589793238462643383279502884197169399375105820974944592307816406665n,
|
||||
// Field over which we'll do calculations. Verify with:
|
||||
// NOTE: there is no efficient sqrt for field (P%4==1)
|
||||
P: 2n ** 251n + 17n * 2n ** 192n + 1n,
|
||||
// Curve order, total count of valid points in the field. Verify with:
|
||||
n: CURVE_N,
|
||||
nBitLength: nBitLength, // len(bin(N).replace('0b',''))
|
||||
// Base point (x, y) aka generator point
|
||||
Gx: 874739451078007766457464989774322083649278607533249481151382481072868806602n,
|
||||
Gy: 152666792071518830868575557812948353041420400780739481342941381225525861407n,
|
||||
// Default options
|
||||
lowS: false,
|
||||
...getHash(sha256),
|
||||
truncateHash: (hash: Uint8Array, truncateOnly = false): bigint => {
|
||||
// TODO: cleanup, ugly code
|
||||
// Fix truncation
|
||||
if (!truncateOnly) {
|
||||
let hashS = bytesToNumber0x(hash).toString(16);
|
||||
if (hashS.length === 63) {
|
||||
hashS += '0';
|
||||
hash = hexToBytes0x(hashS);
|
||||
}
|
||||
}
|
||||
// Truncate zero bytes on left (compat with elliptic)
|
||||
while (hash[0] === 0) hash = hash.subarray(1);
|
||||
const byteLength = hash.length;
|
||||
const delta = byteLength * 8 - nBitLength; // size of curve.n (252 bits)
|
||||
let h = hash.length ? bytesToNumber0x(hash) : 0n;
|
||||
if (delta > 0) h = h >> BigInt(delta);
|
||||
if (!truncateOnly && h >= CURVE_N) h -= CURVE_N;
|
||||
return h;
|
||||
},
|
||||
});
|
||||
|
||||
// Custom Starknet type conversion functions that can handle 0x and unpadded hex
|
||||
function hexToBytes0x(hex: string): Uint8Array {
|
||||
if (typeof hex !== 'string') {
|
||||
throw new TypeError('hexToBytes: expected string, got ' + typeof hex);
|
||||
}
|
||||
hex = strip0x(hex);
|
||||
if (hex.length & 1) hex = '0' + hex; // padding
|
||||
if (hex.length % 2) throw new Error('hexToBytes: received invalid unpadded hex ' + hex.length);
|
||||
const array = new Uint8Array(hex.length / 2);
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
const j = i * 2;
|
||||
const hexByte = hex.slice(j, j + 2);
|
||||
const byte = Number.parseInt(hexByte, 16);
|
||||
if (Number.isNaN(byte) || byte < 0) throw new Error('Invalid byte sequence');
|
||||
array[i] = byte;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
function hexToNumber0x(hex: string): bigint {
|
||||
if (typeof hex !== 'string') {
|
||||
throw new TypeError('hexToNumber: expected string, got ' + typeof hex);
|
||||
}
|
||||
// Big Endian
|
||||
// TODO: strip vs no strip?
|
||||
return BigInt(`0x${strip0x(hex)}`);
|
||||
}
|
||||
function bytesToNumber0x(bytes: Uint8Array): bigint {
|
||||
return hexToNumber0x(cutils.bytesToHex(bytes));
|
||||
}
|
||||
function ensureBytes0x(hex: Hex): Uint8Array {
|
||||
// Uint8Array.from() instead of hash.slice() because node.js Buffer
|
||||
// is instance of Uint8Array, and its slice() creates **mutable** copy
|
||||
return hex instanceof Uint8Array ? Uint8Array.from(hex) : hexToBytes0x(hex);
|
||||
}
|
||||
|
||||
function sign0x(msgHash: Hex, privKey: Hex, opts: any) {
|
||||
return starkCurve.sign(ensureBytes0x(msgHash), ensureBytes0x(privKey), opts);
|
||||
}
|
||||
function verify0x(signature: Hex, msgHash: Hex, pubKey: Hex) {
|
||||
const sig = signature instanceof Signature ? signature : ensureBytes0x(signature);
|
||||
return starkCurve.verify(sig, ensureBytes0x(msgHash), ensureBytes0x(pubKey));
|
||||
}
|
||||
|
||||
const { CURVE, Point, JacobianPoint, Signature, getPublicKey, getSharedSecret } = starkCurve;
|
||||
export const utils = starkCurve.utils;
|
||||
export {
|
||||
CURVE,
|
||||
Point,
|
||||
Signature,
|
||||
JacobianPoint,
|
||||
getPublicKey,
|
||||
getSharedSecret,
|
||||
sign0x as sign,
|
||||
verify0x as verify,
|
||||
};
|
||||
|
||||
const stripLeadingZeros = (s: string) => s.replace(/^0+/gm, '');
|
||||
export const bytesToHexEth = (uint8a: Uint8Array): string =>
|
||||
`0x${stripLeadingZeros(cutils.bytesToHex(uint8a))}`;
|
||||
export const strip0x = (hex: string) => hex.replace(/^0x/i, '');
|
||||
export const numberToHexEth = (num: bigint | number) => `0x${num.toString(16)}`;
|
||||
|
||||
// We accept hex strings besides Uint8Array for simplicity
|
||||
type Hex = Uint8Array | string;
|
||||
|
||||
// 1. seed generation
|
||||
function hashKeyWithIndex(key: Uint8Array, index: number) {
|
||||
let indexHex = cutils.numberToHexUnpadded(index);
|
||||
if (indexHex.length & 1) indexHex = '0' + indexHex;
|
||||
return bytesToNumber0x(sha256(cutils.concatBytes(key, hexToBytes0x(indexHex))));
|
||||
}
|
||||
|
||||
export function grindKey(seed: Hex) {
|
||||
const _seed = ensureBytes0x(seed);
|
||||
const sha256mask = 2n ** 256n;
|
||||
const limit = sha256mask - starkCurve.utils.mod(sha256mask, starkCurve.CURVE.n);
|
||||
for (let i = 0; ; i++) {
|
||||
const key = hashKeyWithIndex(_seed, i);
|
||||
// key should be in [0, limit)
|
||||
if (key < limit) return starkCurve.utils.mod(key, starkCurve.CURVE.n).toString(16);
|
||||
}
|
||||
}
|
||||
|
||||
export function getStarkKey(privateKey: Hex) {
|
||||
const priv = typeof privateKey === 'string' ? strip0x(privateKey) : privateKey;
|
||||
return bytesToHexEth(Point.fromPrivateKey(priv).toRawBytes(true).slice(1));
|
||||
}
|
||||
|
||||
export function ethSigToPrivate(signature: string) {
|
||||
signature = strip0x(signature.replace(/^0x/, ''));
|
||||
if (signature.length !== 130) throw new Error('Wrong ethereum signature');
|
||||
return grindKey(signature.substring(0, 64));
|
||||
}
|
||||
|
||||
const MASK_31 = 2n ** 31n - 1n;
|
||||
const int31 = (n: bigint) => Number(n & MASK_31);
|
||||
export function getAccountPath(
|
||||
layer: string,
|
||||
application: string,
|
||||
ethereumAddress: string,
|
||||
index: number
|
||||
) {
|
||||
const layerNum = int31(bytesToNumber0x(sha256(layer)));
|
||||
const applicationNum = int31(bytesToNumber0x(sha256(application)));
|
||||
const eth = hexToNumber0x(ethereumAddress);
|
||||
return `m/2645'/${layerNum}'/${applicationNum}'/${int31(eth)}'/${int31(eth >> 31n)}'/${index}`;
|
||||
}
|
||||
|
||||
// https://docs.starkware.co/starkex/pedersen-hash-function.html
|
||||
const PEDERSEN_POINTS = [
|
||||
new Point(
|
||||
2089986280348253421170679821480865132823066470938446095505822317253594081284n,
|
||||
1713931329540660377023406109199410414810705867260802078187082345529207694986n
|
||||
),
|
||||
new Point(
|
||||
996781205833008774514500082376783249102396023663454813447423147977397232763n,
|
||||
1668503676786377725805489344771023921079126552019160156920634619255970485781n
|
||||
),
|
||||
new Point(
|
||||
2251563274489750535117886426533222435294046428347329203627021249169616184184n,
|
||||
1798716007562728905295480679789526322175868328062420237419143593021674992973n
|
||||
),
|
||||
new Point(
|
||||
2138414695194151160943305727036575959195309218611738193261179310511854807447n,
|
||||
113410276730064486255102093846540133784865286929052426931474106396135072156n
|
||||
),
|
||||
new Point(
|
||||
2379962749567351885752724891227938183011949129833673362440656643086021394946n,
|
||||
776496453633298175483985398648758586525933812536653089401905292063708816422n
|
||||
),
|
||||
];
|
||||
// for (const p of PEDERSEN_POINTS) p._setWindowSize(8);
|
||||
const PEDERSEN_POINTS_JACOBIAN = PEDERSEN_POINTS.map(JacobianPoint.fromAffine);
|
||||
|
||||
function pedersenPrecompute(p1: JacobianPointType, p2: JacobianPointType): JacobianPointType[] {
|
||||
const out: JacobianPointType[] = [];
|
||||
let p = p1;
|
||||
for (let i = 0; i < 248; i++) {
|
||||
out.push(p);
|
||||
p = p.double();
|
||||
}
|
||||
p = p2;
|
||||
for (let i = 0; i < 4; i++) {
|
||||
out.push(p);
|
||||
p = p.double();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
const PEDERSEN_POINTS1 = pedersenPrecompute(
|
||||
PEDERSEN_POINTS_JACOBIAN[1],
|
||||
PEDERSEN_POINTS_JACOBIAN[2]
|
||||
);
|
||||
const PEDERSEN_POINTS2 = pedersenPrecompute(
|
||||
PEDERSEN_POINTS_JACOBIAN[3],
|
||||
PEDERSEN_POINTS_JACOBIAN[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.P))
|
||||
throw new Error(`PedersenArg should be 0 <= value < CURVE.P: ${value}`);
|
||||
return value;
|
||||
}
|
||||
|
||||
function pedersenSingle(
|
||||
point: JacobianPointType,
|
||||
value: PedersenArg,
|
||||
constants: JacobianPointType[]
|
||||
) {
|
||||
let x = pedersenArg(value);
|
||||
for (let j = 0; j < 252; j++) {
|
||||
const pt = constants[j];
|
||||
if (pt.x === point.x) throw new Error('Same point');
|
||||
if ((x & 1n) !== 0n) point = point.add(pt);
|
||||
x >>= 1n;
|
||||
}
|
||||
return point;
|
||||
}
|
||||
|
||||
// shift_point + x_low * P_0 + x_high * P1 + y_low * P2 + y_high * P3
|
||||
export function pedersen(x: PedersenArg, y: PedersenArg) {
|
||||
let point: JacobianPointType = PEDERSEN_POINTS_JACOBIAN[0];
|
||||
point = pedersenSingle(point, x, PEDERSEN_POINTS1);
|
||||
point = pedersenSingle(point, y, PEDERSEN_POINTS2);
|
||||
return bytesToHexEth(point.toAffine().toRawBytes(true).slice(1));
|
||||
}
|
||||
|
||||
export function hashChain(data: PedersenArg[], fn = pedersen) {
|
||||
if (!Array.isArray(data) || data.length < 1)
|
||||
throw new Error('data should be array of at least 1 element');
|
||||
if (data.length === 1) return numberToHexEth(pedersenArg(data[0]));
|
||||
return Array.from(data)
|
||||
.reverse()
|
||||
.reduce((acc, i) => fn(i, acc));
|
||||
}
|
||||
// Same as hashChain, but computes hash even for single element and order is not revesed
|
||||
export const computeHashOnElements = (data: PedersenArg[], fn = pedersen) =>
|
||||
[0, ...data, data.length].reduce((x, y) => fn(x, y));
|
||||
|
||||
const MASK_250 = 2n ** 250n - 1n;
|
||||
export const keccak = (data: Uint8Array) => bytesToNumber0x(keccak_256(data)) & MASK_250;
|
@ -1,97 +0,0 @@
|
||||
import { deepStrictEqual, throws } from 'assert';
|
||||
import { should } from 'micro-should';
|
||||
import * as nist from '../lib/nist.js';
|
||||
import { hexToBytes } from '@noble/curves/utils';
|
||||
import { default as ecdsa } from './fixtures/ecdsa_test.json' assert { type: 'json' };
|
||||
import { default as ecdh } from './fixtures/ecdh_test.json' assert { type: 'json' };
|
||||
|
||||
// import { hexToBytes } from '@noble/curves';
|
||||
|
||||
should('Curve Fields', () => {
|
||||
const vectors = {
|
||||
secp192r1: 0xfffffffffffffffffffffffffffffffeffffffffffffffffn,
|
||||
secp224r1: 0xffffffffffffffffffffffffffffffff000000000000000000000001n,
|
||||
secp256r1: 0xffffffff00000001000000000000000000000000ffffffffffffffffffffffffn,
|
||||
secp256k1: 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2fn,
|
||||
secp384r1:
|
||||
0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffffn,
|
||||
secp521r1:
|
||||
0x01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffn,
|
||||
};
|
||||
for (const n in vectors) deepStrictEqual(nist[n].CURVE.P, vectors[n]);
|
||||
});
|
||||
|
||||
should('wychenproof ECDSA vectors', () => {
|
||||
for (const group of ecdsa.testGroups) {
|
||||
// Tested in secp256k1.test.js
|
||||
if (group.key.curve === 'secp256k1') continue;
|
||||
// We don't have SHA-224
|
||||
if (group.key.curve === 'secp224r1' && group.sha === 'SHA-224') continue;
|
||||
const CURVE = nist[group.key.curve];
|
||||
if (!CURVE) continue;
|
||||
const pubKey = CURVE.Point.fromHex(group.key.uncompressed);
|
||||
deepStrictEqual(pubKey.x, BigInt(`0x${group.key.wx}`));
|
||||
deepStrictEqual(pubKey.y, BigInt(`0x${group.key.wy}`));
|
||||
for (const test of group.tests) {
|
||||
if (['Hash weaker than DL-group'].includes(test.comment)) {
|
||||
continue;
|
||||
}
|
||||
const m = CURVE.CURVE.hash(hexToBytes(test.msg));
|
||||
if (test.result === 'valid' || test.result === 'acceptable') {
|
||||
try {
|
||||
CURVE.Signature.fromDER(test.sig);
|
||||
} catch (e) {
|
||||
// Some test has invalid signature which we don't accept
|
||||
if (e.message.includes('Invalid signature: incorrect length')) continue;
|
||||
throw e;
|
||||
}
|
||||
const verified = CURVE.verify(test.sig, m, pubKey);
|
||||
deepStrictEqual(verified, true, 'valid');
|
||||
} else if (test.result === 'invalid') {
|
||||
let failed = false;
|
||||
try {
|
||||
failed = !CURVE.verify(test.sig, m, pubKey);
|
||||
} catch (error) {
|
||||
failed = true;
|
||||
}
|
||||
deepStrictEqual(failed, true, 'invalid');
|
||||
} else throw new Error('unknown test result');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
should('wychenproof ECDH vectors', () => {
|
||||
for (const group of ecdh.testGroups) {
|
||||
// // Tested in secp256k1.test.js
|
||||
// if (group.key.curve === 'secp256k1') continue;
|
||||
// We don't have SHA-224
|
||||
const CURVE = nist[group.curve];
|
||||
if (!CURVE) continue;
|
||||
for (const test of group.tests) {
|
||||
if (test.result === 'valid' || test.result === 'acceptable') {
|
||||
try {
|
||||
const pub = CURVE.Point.fromHex(test.public);
|
||||
} catch (e) {
|
||||
if (e.message.includes('Point.fromHex: received invalid point.')) continue;
|
||||
throw e;
|
||||
}
|
||||
const shared = CURVE.getSharedSecret(test.private, test.public);
|
||||
deepStrictEqual(shared, test.shared, 'valid');
|
||||
} else if (test.result === 'invalid') {
|
||||
let failed = false;
|
||||
try {
|
||||
CURVE.getSharedSecret(test.private, test.public);
|
||||
} catch (error) {
|
||||
failed = true;
|
||||
}
|
||||
deepStrictEqual(failed, true, 'invalid');
|
||||
} else throw new Error('unknown test result');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// ESM is broken.
|
||||
import url from 'url';
|
||||
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
|
||||
should.run();
|
||||
}
|
@ -1,898 +0,0 @@
|
||||
{
|
||||
"algorithm" : "ECDH",
|
||||
"generatorVersion" : "0.8r12",
|
||||
"numberOfTests" : 96,
|
||||
"header" : [
|
||||
"Test vectors of type EcdhWebTest are intended for",
|
||||
"testing an ECDH implementations where the public key",
|
||||
"is just an ASN encoded point."
|
||||
],
|
||||
"notes" : {
|
||||
"AddSubChain" : "The private key has a special value. Implementations using addition subtraction chains for the point multiplication may get the point at infinity as an intermediate result. See CVE_2017_10176",
|
||||
"CompressedPoint" : "The point in the public key is compressed. Not every library supports points in compressed format."
|
||||
},
|
||||
"schema" : "ecdh_ecpoint_test_schema.json",
|
||||
"testGroups" : [
|
||||
{
|
||||
"curve" : "secp224r1",
|
||||
"encoding" : "ecpoint",
|
||||
"type" : "EcdhEcpointTest",
|
||||
"tests" : [
|
||||
{
|
||||
"tcId" : 1,
|
||||
"comment" : "normal case",
|
||||
"public" : "047d8ac211e1228eb094e285a957d9912e93deee433ed777440ae9fc719b01d050dfbe653e72f39491be87fb1a2742daa6e0a2aada98bb1aca",
|
||||
"private" : "565577a49415ca761a0322ad54e4ad0ae7625174baf372c2816f5328",
|
||||
"shared" : "b8ecdb552d39228ee332bafe4886dbff272f7109edf933bc7542bd4f",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 2,
|
||||
"comment" : "compressed public key",
|
||||
"public" : "027d8ac211e1228eb094e285a957d9912e93deee433ed777440ae9fc71",
|
||||
"private" : "565577a49415ca761a0322ad54e4ad0ae7625174baf372c2816f5328",
|
||||
"shared" : "b8ecdb552d39228ee332bafe4886dbff272f7109edf933bc7542bd4f",
|
||||
"result" : "acceptable",
|
||||
"flags" : [
|
||||
"CompressedPoint"
|
||||
]
|
||||
},
|
||||
{
|
||||
"tcId" : 3,
|
||||
"comment" : "edge case for shared secret",
|
||||
"public" : "04e73a6ca72f3a2fae6e0a01a0ed03bfa3058b04576942eaf063095e62ca16fd31fa0f38eeb592cbeea1147751fdd2a5b6cc0ead404467a5b6",
|
||||
"private" : "00a2b6442a37f9201b56758034d2009be64b0ab7c02d7e398cac9665d6",
|
||||
"shared" : "00000000000000000000000000000000000000000000000000000003",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 4,
|
||||
"comment" : "edge case for shared secret",
|
||||
"public" : "045763fa2ae16367ad23d471cc9a52466f0d81d864e5640cefe384114594d9fecfbed4f254505ac8b41d2532055a07f0241c4818b552cbb636",
|
||||
"private" : "00a2b6442a37f9201b56758034d2009be64b0ab7c02d7e398cac9665d6",
|
||||
"shared" : "00000000000000000000000100000000000000000000000000000001",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 5,
|
||||
"comment" : "edge case for shared secret",
|
||||
"public" : "04142c1fd80fa2121a59aa898144084ec033f7a56a34eee0b499e29ae51c6d8c1bbb1ef2a76d565899fe44ffc1207d530d7f598fb77f4bb76b",
|
||||
"private" : "00a2b6442a37f9201b56758034d2009be64b0ab7c02d7e398cac9665d6",
|
||||
"shared" : "00000000000000ffffffffffffff0000000000000100000000000000",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 6,
|
||||
"comment" : "edge case for shared secret",
|
||||
"public" : "04ed6f793e10c80d12d871cf8988399c4898a9bf9ffd8f27399f63de25f0051cdf4eec7f368f922cfcd948893ceca0c92e540cc4367a99a66a",
|
||||
"private" : "00a2b6442a37f9201b56758034d2009be64b0ab7c02d7e398cac9665d6",
|
||||
"shared" : "00000000ffffffffffffffff00000000000000010000000000000000",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 7,
|
||||
"comment" : "edge case for shared secret",
|
||||
"public" : "0408fcfc1a63c82860be12e4137433dfc40be9acdd245f9a8c4e56be61a385fc09f808383383f4b1d0d5365b6e5dcfacdc19bc7bcfed221274",
|
||||
"private" : "00a2b6442a37f9201b56758034d2009be64b0ab7c02d7e398cac9665d6",
|
||||
"shared" : "0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 8,
|
||||
"comment" : "edge case for shared secret",
|
||||
"public" : "04d883ed77f1861e8712800d31df67888fe39f150c79a27aa88caeda6b180f3f623e2ff3ab5370cf8179165b085af3dd4502850c0104caed9a",
|
||||
"private" : "00a2b6442a37f9201b56758034d2009be64b0ab7c02d7e398cac9665d6",
|
||||
"shared" : "0003fffffff00000003fffffff00000003fffffff000000040000000",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 9,
|
||||
"comment" : "edge case for shared secret",
|
||||
"public" : "042b8b279b85ee3f3d2c0abeb36fdfc5aad6157d652d26489381a32cd73224bd757ef794acc92b0b3b9e7990618bb343a9a09bdb9d3616eff6",
|
||||
"private" : "00a2b6442a37f9201b56758034d2009be64b0ab7c02d7e398cac9665d6",
|
||||
"shared" : "01fffffffc00000007fffffff00000001fffffffc000000080000001",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 10,
|
||||
"comment" : "edge case for shared secret",
|
||||
"public" : "048bd5f03391eeeae1744e8fc53d314efffafa4d3fa4f1b95c3388a9cd7c86358b273119c537133eb55e79c6ac510b10980b379b919ccf2e2f",
|
||||
"private" : "00a2b6442a37f9201b56758034d2009be64b0ab7c02d7e398cac9665d6",
|
||||
"shared" : "0a15c112ff784b1445e889f955be7e3ffdf451a2c0e76ab5cb32cf41",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 11,
|
||||
"comment" : "edge case for shared secret",
|
||||
"public" : "04ce9631b6a16227778625c8e5421ae083cdd913abefde01dbe69f6c2b95386aff2b483b2c47151cfaabfd000614c683ce2e1778221ae42c1b",
|
||||
"private" : "00a2b6442a37f9201b56758034d2009be64b0ab7c02d7e398cac9665d6",
|
||||
"shared" : "62989eaaa26a16f07330c3c51e0a4631fd016bfcede26552816aee39",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 12,
|
||||
"comment" : "edge case for shared secret",
|
||||
"public" : "041f441c98eda956a6a7fdbfd8d21910860ab59d16c3e52f8e7fad6ca5df61a55fc508fc0499c55492f1e87bb2faa0cb4170b79f3a85ec2f3d",
|
||||
"private" : "00a2b6442a37f9201b56758034d2009be64b0ab7c02d7e398cac9665d6",
|
||||
"shared" : "661ac958c0febbc718ccf39cefc6b66c4231fbb9a76f35228a3bf5c3",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 13,
|
||||
"comment" : "edge case for shared secret",
|
||||
"public" : "04be74583cb9d3a05ae54923624e478a329a697d842dfae33141c844d7d9ba4fc96e0fe716ac0542e87368662fc2f0cb9b0ae57936ddec7190",
|
||||
"private" : "00a2b6442a37f9201b56758034d2009be64b0ab7c02d7e398cac9665d6",
|
||||
"shared" : "6d7e41821abe1094d430237923d2a50de31768ab51b12dce8a09e34c",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 14,
|
||||
"comment" : "edge case for shared secret",
|
||||
"public" : "04a281ad992b363597ac93ff0de8ab1f7e51a6672dcbb58f9d739ba430ce0192874038daefc3130eec65811c7255da70fea65c1003f6892faa",
|
||||
"private" : "00a2b6442a37f9201b56758034d2009be64b0ab7c02d7e398cac9665d6",
|
||||
"shared" : "7fffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 15,
|
||||
"comment" : "edge case for shared secret",
|
||||
"public" : "04be3e22133f51203f631b81dde8c020cdea5daa1f99cfc05c88fad2dc0f243798d6e72d1de9e3cdca4144e0a6c0f2a584d07589006972c197",
|
||||
"private" : "00a2b6442a37f9201b56758034d2009be64b0ab7c02d7e398cac9665d6",
|
||||
"shared" : "fffc0007fff0001fffc0007fff0001fffc0007fff0001fffc0008001",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 16,
|
||||
"comment" : "edge case for shared secret",
|
||||
"public" : "04af14547c20afbd91bfe64ea03d45a76a71241f23520ef897ff91eff1b54ca6ca8c25fd73852ec6654617434eff7f0225684d4dea7a4f8a97",
|
||||
"private" : "00a2b6442a37f9201b56758034d2009be64b0ab7c02d7e398cac9665d6",
|
||||
"shared" : "ffff0000003ffffff0000003ffffff0000003ffffff0000003ffffff",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 17,
|
||||
"comment" : "edge case for shared secret",
|
||||
"public" : "04b1e484925018729926acda56ff3e2f6c1e7e8f162b178d8e8afb45564fceaa6da5d998fe26b6b26a055169063a5ab6908852ca8b54e2de6c",
|
||||
"private" : "00a2b6442a37f9201b56758034d2009be64b0ab7c02d7e398cac9665d6",
|
||||
"shared" : "fffff0000007fffffe000000ffffffc000001ffffff8000003ffffff",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 18,
|
||||
"comment" : "edge case for shared secret",
|
||||
"public" : "04937eb09fb145c8829cb7df20a4cbeed396791373de277871d6c5f9cc3b5b4fd56464a71fc4a2a6af3bd251952bffa829489e68a8d06f96b6",
|
||||
"private" : "00a2b6442a37f9201b56758034d2009be64b0ab7c02d7e398cac9665d6",
|
||||
"shared" : "ffffffff00000000ffffffff00000000ffffffff00000000ffffffff",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 19,
|
||||
"comment" : "edge cases for ephemeral key",
|
||||
"public" : "04000000000000000000000000000000000000000000000000000000037cac269c67bd55ea14efff4eadefe5e74978514af14c88fab46ec046",
|
||||
"private" : "2bc15cf3981f4e15bbad387b506df647989e5478160be862f8c26969",
|
||||
"shared" : "3fa0b9ff70b884f9f57bb84f7a9532d93f6ba803f89dd8ff008177d7",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 20,
|
||||
"comment" : "edge cases for ephemeral key",
|
||||
"public" : "04000000000000000000000001000000000000000000000000000000012ea2f4917bdfdb008306cc10a18e2557633ba861001829dcbfb96fba",
|
||||
"private" : "2bc15cf3981f4e15bbad387b506df647989e5478160be862f8c26969",
|
||||
"shared" : "be1ded8cb7ff8a585181f96d681e31b332fe27dcae922dca2310300d",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 21,
|
||||
"comment" : "edge cases for ephemeral key",
|
||||
"public" : "0400000000000000ffffffffffffff000000000000010000000000000073ca5f8f104997a2399e0c7f25e72a75ec29fc4542533d3fea89a33a",
|
||||
"private" : "2bc15cf3981f4e15bbad387b506df647989e5478160be862f8c26969",
|
||||
"shared" : "a2e86a260e13515918a0cafdd87855f231b5624c560f976159e06a75",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 22,
|
||||
"comment" : "edge cases for ephemeral key",
|
||||
"public" : "0400000000ffffffffffffffff000000000000000100000000000000006fe6805f59b19b0dd389452a1d4a420bfeb6c369cf6fed5b12e6e654",
|
||||
"private" : "2bc15cf3981f4e15bbad387b506df647989e5478160be862f8c26969",
|
||||
"shared" : "31ef7c8d10404a0046994f313a70574b027e87f9028eca242c1b5bf5",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 23,
|
||||
"comment" : "edge cases for ephemeral key",
|
||||
"public" : "040000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff77c5cfa4e2c384938d48bd8dd98f54c86b279f1df8c0a1f6692439c9",
|
||||
"private" : "2bc15cf3981f4e15bbad387b506df647989e5478160be862f8c26969",
|
||||
"shared" : "d1976a8ef5f54f24f5a269ad504fdca849fc9c28587ba294ef267396",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 24,
|
||||
"comment" : "edge cases for ephemeral key",
|
||||
"public" : "040003fffffff00000003fffffff00000003fffffff00000004000000001f0828136016bb97445461bc59f2175d8d23557d6b9381f26136e3d",
|
||||
"private" : "2bc15cf3981f4e15bbad387b506df647989e5478160be862f8c26969",
|
||||
"shared" : "ce7890d108ddb2e5474e6417fcf7a9f2b3bd018816062f4835260dc8",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 25,
|
||||
"comment" : "edge cases for ephemeral key",
|
||||
"public" : "0401fffffffc00000007fffffff00000001fffffffc0000000800000012d8acca6f199d4a94b933ba1aa713a7debde8ac57b928f596ae66a66",
|
||||
"private" : "2bc15cf3981f4e15bbad387b506df647989e5478160be862f8c26969",
|
||||
"shared" : "30b6ff6e8051dae51e4fe34b2d9a0b1879153e007eb0b5bdf1791a9c",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 26,
|
||||
"comment" : "edge cases for ephemeral key",
|
||||
"public" : "040a15c112ff784b1445e889f955be7e3ffdf451a2c0e76ab5cb32cf413d4df973c563c6decdd435e4f864557e4c273096d9941ca4260a266e",
|
||||
"private" : "2bc15cf3981f4e15bbad387b506df647989e5478160be862f8c26969",
|
||||
"shared" : "77ec668a00f72d85aa527624abb16c039fe490d17dd6c455a1ed7fd8",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 27,
|
||||
"comment" : "edge cases for ephemeral key",
|
||||
"public" : "0462989eaaa26a16f07330c3c51e0a4631fd016bfcede26552816aee39389ee9436d616cab90032931aa7fbbfcfc13309f61e2423cc8dab93c",
|
||||
"private" : "2bc15cf3981f4e15bbad387b506df647989e5478160be862f8c26969",
|
||||
"shared" : "a3f432f6aba9a92f49a5ea64ffe7059a9d9b487a0b5223ddc988208b",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 28,
|
||||
"comment" : "edge cases for ephemeral key",
|
||||
"public" : "04661ac958c0febbc718ccf39cefc6b66c4231fbb9a76f35228a3bf5c3103b8040e3cb41966fc64a68cacb0c14053f87d27e8ed7bf2d7fe51b",
|
||||
"private" : "2bc15cf3981f4e15bbad387b506df647989e5478160be862f8c26969",
|
||||
"shared" : "1530fd9caf03737af34a4ba716b558cbecbc35d18402535a0a142313",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 29,
|
||||
"comment" : "edge cases for ephemeral key",
|
||||
"public" : "046d7e41821abe1094d430237923d2a50de31768ab51b12dce8a09e34c276cf273d75d367820dd556182def0957af0a314f48fed227c298dc0",
|
||||
"private" : "2bc15cf3981f4e15bbad387b506df647989e5478160be862f8c26969",
|
||||
"shared" : "cfc39ccacb94ad0e0552b2e47112f60fbbe7ae0dc32230b9273dd210",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 30,
|
||||
"comment" : "edge cases for ephemeral key",
|
||||
"public" : "047fffffffffffffffffffffffffffffffffffffffffffffffffffffff7d8dbca36c56bcaae92e3475f799294f30768038e816a7d5f7f07d77",
|
||||
"private" : "2bc15cf3981f4e15bbad387b506df647989e5478160be862f8c26969",
|
||||
"shared" : "73bd63bd384a0faafb75cfed3e95d3892cbacf0db10f282c3b644771",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 31,
|
||||
"comment" : "edge cases for ephemeral key",
|
||||
"public" : "04fffc0007fff0001fffc0007fff0001fffc0007fff0001fffc000800174f1ff5ea7fbc72b92f61e06556c26bab84c0b082dd6400ca1c1eb6d",
|
||||
"private" : "2bc15cf3981f4e15bbad387b506df647989e5478160be862f8c26969",
|
||||
"shared" : "85b079c62e1f5b0fd6841dfa16026e15b641f65e13a14042567166bb",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 32,
|
||||
"comment" : "edge cases for ephemeral key",
|
||||
"public" : "04ffff0000003ffffff0000003ffffff0000003ffffff0000003ffffff0126fdd5fccd0b5aa7fd5bb5b1308584b30556248cec80208a2fe962",
|
||||
"private" : "2bc15cf3981f4e15bbad387b506df647989e5478160be862f8c26969",
|
||||
"shared" : "8a834ff40e3fc9f9d412a481e18537ea799536c5520c6c7baaf12166",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 33,
|
||||
"comment" : "edge cases for ephemeral key",
|
||||
"public" : "04fffff0000007fffffe000000ffffffc000001ffffff8000003ffffff20cfa23077acc9fbcb71339c65880cd0b966b8a9497e65abed17f0b5",
|
||||
"private" : "2bc15cf3981f4e15bbad387b506df647989e5478160be862f8c26969",
|
||||
"shared" : "a0887269766e6efcbc81d2b38f2d4638663f12377468a23421044188",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 34,
|
||||
"comment" : "edge cases for ephemeral key",
|
||||
"public" : "04ffffffff00000000ffffffff00000000ffffffff00000000ffffffff1c05ac2d4f10b69877c3243d51f887277b7bf735c326ab2f0d70da8c",
|
||||
"private" : "2bc15cf3981f4e15bbad387b506df647989e5478160be862f8c26969",
|
||||
"shared" : "c65d1911bc076a74588d8793ce7a0dcabf5793460cd2ebb02754a1be",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 35,
|
||||
"comment" : "point with coordinate y = 1",
|
||||
"public" : "043b5889352ddf7468bf8c0729212aa1b2a3fcb1a844b8be91abb753d500000000000000000000000000000000000000000000000000000001",
|
||||
"private" : "00938f3dbe37135cdbdb9993a187a0e9b9f0def035fbc52ad59fc50421",
|
||||
"shared" : "e973c413cc7dd34d4e3637522b2e033c20815412b67574a1f2f6bdd7",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 36,
|
||||
"comment" : "point with coordinate y = 1",
|
||||
"public" : "04bf09e268942555c73ce9e00d272c9b12bf0c3fc13a639acc791167f6b05df0023c9bd41d0b0c461854582d0601182213f2219d44ea44914a",
|
||||
"private" : "00938f3dbe37135cdbdb9993a187a0e9b9f0def035fbc52ad59fc50421",
|
||||
"shared" : "ec856e807808a9c5332e886759e03f01be02437cfe0214613e4e7dc7",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 37,
|
||||
"comment" : "point with coordinate y = 1",
|
||||
"public" : "047b664cff2eef0a4f7dce24780113432f66feb25cb0931d033d63910f548ee514f6fdf1cb6f5709581c197d76a5eb218afaed19f205f4ab80",
|
||||
"private" : "00938f3dbe37135cdbdb9993a187a0e9b9f0def035fbc52ad59fc50421",
|
||||
"shared" : "91d424e122c9c01720bbed6b53ec1b37a86996fa4fcf74bfd30f723d",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 38,
|
||||
"comment" : "point with coordinate y = 1 in left to right addition chain",
|
||||
"public" : "045a2b3ec1053390550b587557712bcc0bf85654d23099420154877ec4138322ca02e5fceae870227a43ae8982b67276f6d8f1dd7e12692474",
|
||||
"private" : "00938f3dbe37135cdbdb9993a187a0e9b9f0def035fbc52ad59fc50421",
|
||||
"shared" : "012879a1ff456acb8726455836bc4f504c1bd799a4d96f514b3730c6",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 39,
|
||||
"comment" : "point with coordinate y = 1 in left to right addition chain",
|
||||
"public" : "04fc229bb1df3e11351e7e4224f68f40c0d0e194023c6e0840cd45ee5ca242112fbab5736e821dad26493e4006e2c6125342e7d9bc25272856",
|
||||
"private" : "00938f3dbe37135cdbdb9993a187a0e9b9f0def035fbc52ad59fc50421",
|
||||
"shared" : "fd6e5edb54d7dd554f8747ec87b8031258fc0bf1d2404b64db4540d4",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 40,
|
||||
"comment" : "point with coordinate y = 1 in left to right addition chain",
|
||||
"public" : "0469a65f62d4159235801a246f2d13e45c8983a3362da480e7a51d42a65b7047abfc2a179d943bb196fede7ac3ad8a4fcacd4c4caa717b6b26",
|
||||
"private" : "00938f3dbe37135cdbdb9993a187a0e9b9f0def035fbc52ad59fc50421",
|
||||
"shared" : "164e95bfa2a9c3a1f959feb88720bb7a37f988a08124639d8adf86df",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 41,
|
||||
"comment" : "point with coordinate y = 1 in left to right addition chain",
|
||||
"public" : "04dc68eb945528af0051cbf23e3eea43b2bc4c728976231e7031e63a2744ba65a4e1e34e8ec50cf7e8df4458582b16413ab83f568508c59037",
|
||||
"private" : "00938f3dbe37135cdbdb9993a187a0e9b9f0def035fbc52ad59fc50421",
|
||||
"shared" : "b0ffd55fa112aa48eddc960db4a1200d406e144aac9e109ad9892b2d",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 42,
|
||||
"comment" : "point with coordinate y = 1 in left to right addition chain",
|
||||
"public" : "0481c89369d7be252920e08e2d6c6841b887efb4fc747db31dd1030b1919bf8ccb629b58fea6234e39812083fb0833a0c937e348eda22ea0c0",
|
||||
"private" : "00938f3dbe37135cdbdb9993a187a0e9b9f0def035fbc52ad59fc50421",
|
||||
"shared" : "d6ab4567eff21277284be082d9e09eb08bb80685f4929dc3dca4b333",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 43,
|
||||
"comment" : "point with coordinate y = 1 in left to right addition chain",
|
||||
"public" : "0451d830f792795409f1ee972d3b94289f59206fe09e12166920739a73d2f1831b26677901bfaf8323f82b81e1012d9d3f1c9296c59c97970f",
|
||||
"private" : "00938f3dbe37135cdbdb9993a187a0e9b9f0def035fbc52ad59fc50421",
|
||||
"shared" : "b43de12912b40cbdd56e30fdfe9a2c24fb72687168c9cfe6b7476966",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 44,
|
||||
"comment" : "point with coordinate y = 1 in left to right addition chain",
|
||||
"public" : "04ab63ce55145842149f99023f37a0a89b9fc4ae6a878fdae8caf31d17ffd0d55830eed46f8255f94b6dcf98a22f1ff26dabf773d556788881",
|
||||
"private" : "00938f3dbe37135cdbdb9993a187a0e9b9f0def035fbc52ad59fc50421",
|
||||
"shared" : "588ee0af3bc60118a715325c6d56c850f73067dcb37b7596d0cfda5f",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 45,
|
||||
"comment" : "point with coordinate y = 1 in left to right addition chain",
|
||||
"public" : "041d64535d54bfcccb38165acbfac01ae33db20e802c5687343cb21b7eb59d86f1892a974741925624477eef21f4e72fa04ee6ce35dfffe5f2",
|
||||
"private" : "00938f3dbe37135cdbdb9993a187a0e9b9f0def035fbc52ad59fc50421",
|
||||
"shared" : "7219ef73ac9e47ac2e03dead23fa8382ae898e2415017cdeb4739f0f",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 46,
|
||||
"comment" : "point with coordinate y = 1 in left to right addition chain",
|
||||
"public" : "04d9d78436a3f9c1fa20e8c2318e61e62b94623e23a0ab746c5ac0cbc38262bd66c17515d3048944dae43b2bd6dd9d7c7a0f7042de2d1001c6",
|
||||
"private" : "00938f3dbe37135cdbdb9993a187a0e9b9f0def035fbc52ad59fc50421",
|
||||
"shared" : "267b069aac5d768a720acc62c92f20b786fc48c7da42f1f5677424ee",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 47,
|
||||
"comment" : "point with coordinate y = 1 in left to right addition chain",
|
||||
"public" : "0465eb3750c6401339caa69ebe6dec86dfc4d79bf657d68bbdd082c5a03eb81e85931352ff338ccbc3a1d332e2d8bc84342d516da06bef220f",
|
||||
"private" : "00938f3dbe37135cdbdb9993a187a0e9b9f0def035fbc52ad59fc50421",
|
||||
"shared" : "bbdd4ac5890b9c0412e4ef3135f666e5b3ddb658ec837691e8129be8",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 48,
|
||||
"comment" : "point with coordinate y = 1 in left to right addition chain",
|
||||
"public" : "04e92d3be1614555ae17a90647979fbb37468c55a1fff9e15f376d49994e470f515b7b3fe50cb55def16142df594c3e46d9d1354730778f9e8",
|
||||
"private" : "00938f3dbe37135cdbdb9993a187a0e9b9f0def035fbc52ad59fc50421",
|
||||
"shared" : "f793ff0d14bd7690840c733162b589cd3413d8c41f4488b427da496f",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 49,
|
||||
"comment" : "point with coordinate y = 1 in left to right addition chain",
|
||||
"public" : "043c92710c9a7f6f98bbec9d2a4fa617cc70e96bc96ecd4597e329143f4750a027c6972459c091ab02c0e2a3082fccec429a38d3596e7aff2b",
|
||||
"private" : "00938f3dbe37135cdbdb9993a187a0e9b9f0def035fbc52ad59fc50421",
|
||||
"shared" : "56c703d4716239c954109b9b841db75b04a790f1f72aa966aece3494",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 50,
|
||||
"comment" : "point with coordinate y = 1 in left to right addition chain",
|
||||
"public" : "04568dfbfa42efc94ce207322e637b4c94f37a5668ad230e987a91d048dcadd244fc059cffab5fa8820a969353620e708e85bd5eec8a0c68ec",
|
||||
"private" : "00938f3dbe37135cdbdb9993a187a0e9b9f0def035fbc52ad59fc50421",
|
||||
"shared" : "7823fe7eb642d50984fb32f911ef289419d85330c3398423d0eda05f",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 51,
|
||||
"comment" : "point with coordinate y = 1 in left to right addition chain",
|
||||
"public" : "04ec10837e495b644904dba58d8dd82133c905a285ae7c2a06d5ccaf6bf0fbf00d13e21a399dc95ae5524a1a37044193e94e3300259b70e058",
|
||||
"private" : "00938f3dbe37135cdbdb9993a187a0e9b9f0def035fbc52ad59fc50421",
|
||||
"shared" : "f7014d38f460836a51075cce9667b56b8851ba19011c8b0274b74a4b",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 52,
|
||||
"comment" : "point with coordinate y = 1 in left to right addition chain",
|
||||
"public" : "04bee2f9352f42ceeb3bf3109e90e6578d0bd4888458df7d179d746977e50e53503dee83eca1824a290566588fa3591645b1a2d56861bda760",
|
||||
"private" : "00938f3dbe37135cdbdb9993a187a0e9b9f0def035fbc52ad59fc50421",
|
||||
"shared" : "777f99f2bdaa72a1185388465ddda1d059872ad043c7cb85b94e28bb",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 53,
|
||||
"comment" : "point with coordinate y = 1 in left to right addition chain",
|
||||
"public" : "04546facbcaa8b551c51715a9add5edc3c8a66dcc47a6223f605614cf7af6d92f5bdebea738658a42c6231e53c08237ccf52f79399579b2dcc",
|
||||
"private" : "00938f3dbe37135cdbdb9993a187a0e9b9f0def035fbc52ad59fc50421",
|
||||
"shared" : "a1db178b716e51e0fa46c1d74a2603005326bca7e81170d4b33a3d2a",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 54,
|
||||
"comment" : "point with coordinate y = 1 in left to right addition chain",
|
||||
"public" : "0423b1811fee891adb33c8bfee289964e92a9d3358daf975d0efb73e229a3332668b7d6da290a2edc941e8bd6f2e33745fc606756eddc013bb",
|
||||
"private" : "00938f3dbe37135cdbdb9993a187a0e9b9f0def035fbc52ad59fc50421",
|
||||
"shared" : "f455c8273416199505019861266ddb9bcde7bee3c3f15a98ee54607b",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 55,
|
||||
"comment" : "point with coordinate y = 1 in precomputation or right to left addition chain",
|
||||
"public" : "0458f53d67332415fe5b4b81999f8332fb6dcdb965d96dbcbab0fac375f29efef7ab4d94bb2d25d25205eae29fe8d9a85b811114a50f6c6859",
|
||||
"private" : "00c1781d86cac2c0af3fb50d54c554a67bd75d25ca796f0486e3fa84f9",
|
||||
"shared" : "d3af1857aca1689514fcfee8d8c40b8637d40452ae35c404f9e67494",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 56,
|
||||
"comment" : "point with coordinate y = 1 in precomputation or right to left addition chain",
|
||||
"public" : "04f2d6e58fcd3ed3f656a9bc687fe4c789ba9614d0359967bc0468eabfa1658a14ef0633f2485e29141e2c4a13bd328ec9bf6af4c7a774131b",
|
||||
"private" : "00c1781d86cac2c0af3fb50d54c554a67bd75d25ca796f0486e3fa84f9",
|
||||
"shared" : "933c385d5fadb57de53e4a5d385118fce830430703c3f585a5d4d0b5",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 57,
|
||||
"comment" : "point with coordinate y = 1 in precomputation or right to left addition chain",
|
||||
"public" : "0402ca5d1b7638b7b88ad02176bd10ff1cfe8812a62f9769a6d62e0c6c787b3e3b2a063940911bf987fc38deebf542400b8bbd9dfeb7d90a8a",
|
||||
"private" : "00c1781d86cac2c0af3fb50d54c554a67bd75d25ca796f0486e3fa84f9",
|
||||
"shared" : "75aea79d99e5c7edaab0284443b548843371d1d9b55f2d73a1a9092f",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 58,
|
||||
"comment" : "point with coordinate y = 1 in precomputation or right to left addition chain",
|
||||
"public" : "04a394d8bf9b479ec3c7ac3fc6a631d01d57d338b9fb5a0ed6e5130e050cfc600cfb08e67727ac5a33345ec1d48d4a9a18516c2203acbd2667",
|
||||
"private" : "00c1781d86cac2c0af3fb50d54c554a67bd75d25ca796f0486e3fa84f9",
|
||||
"shared" : "8c1d0850691cda7523ffccf1cba44b4d472193e6a3bb0727e490a8b5",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 59,
|
||||
"comment" : "point with coordinate y = 1 in precomputation or right to left addition chain",
|
||||
"public" : "04642e26421e96fa88f956d098ac26f02f1d6faa80e460e701a3789a66c38dd95c6b33de8768c85cbe6879d0d77e29fe5a18b26a35cb60c0b6",
|
||||
"private" : "00c1781d86cac2c0af3fb50d54c554a67bd75d25ca796f0486e3fa84f9",
|
||||
"shared" : "50b9ed4d99e2f24e0096eaeded0b552cf8deff5ca8f976964ae47e92",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 60,
|
||||
"comment" : "point with coordinate y = 1 in precomputation or right to left addition chain",
|
||||
"public" : "04f974d1cbbf4171d4773c3e84eab80bc3c6c2858dadcfbd11d64316905df36fbe345f28a3ef663125649474c6fc1ebe175c3865c4469e192b",
|
||||
"private" : "00c1781d86cac2c0af3fb50d54c554a67bd75d25ca796f0486e3fa84f9",
|
||||
"shared" : "5616ee3e63dfb424d329c2b9b50cf378bb77a8bd7e314a241b5942c7",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 61,
|
||||
"comment" : "point with coordinate y = 1 in right to left addition chain",
|
||||
"public" : "0455561db3cc8fb08a71654ee9573a1a36a44f0913ca8ad7582cfafbfc62b31e5e78be98ad8c8ceab4bb82e8efc0acb29f1a8d031ed044046c",
|
||||
"private" : "00c1781d86cac2c0af3fb50d54c554a67bd75d25ca796f0486e3fa84f9",
|
||||
"shared" : "b1da14507b5c05159e15f77d085c017acd89f158011357a97802855d",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 62,
|
||||
"comment" : "point with coordinate y = 1 in right to left addition chain",
|
||||
"public" : "04a363bcb9bddd5de84a2f4433c039f7be3fce6057b0d3b4a3459e54a2ba32302871e7ba5c3dd7ec9b76946cdc702c15a8d9ec0f4a04e7afb6",
|
||||
"private" : "00c1781d86cac2c0af3fb50d54c554a67bd75d25ca796f0486e3fa84f9",
|
||||
"shared" : "2f1bd4a5a497481c4a21222320ff61f32674a95d540cc3f4f3ca5849",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 63,
|
||||
"comment" : "point with coordinate y = 1 in right to left addition chain",
|
||||
"public" : "043a656d0e25bce27282f256b121fbfcde0a180ccd7aa601a5929fc74002f89e45b4dcb873c56da5d1a28fbca33a126177b217a098e0952e62",
|
||||
"private" : "00c1781d86cac2c0af3fb50d54c554a67bd75d25ca796f0486e3fa84f9",
|
||||
"shared" : "8c807d65ba7b9fd3061dffef26c025a89524a26b942edd3a984fe51d",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 64,
|
||||
"comment" : "point with coordinate y = 1 in right to left addition chain",
|
||||
"public" : "04bf5f49ba0086eec289b068b783438ef24b6f28130bb1ed969ef8b041f11b0de95f15edcd835f01bab1f5faaa1749c2ca4f16a7d99d916ff4",
|
||||
"private" : "00c1781d86cac2c0af3fb50d54c554a67bd75d25ca796f0486e3fa84f9",
|
||||
"shared" : "8fda76f4d124e6727f855e5f4921cc05c48e2a8ed0fee7c75d6a8047",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 65,
|
||||
"comment" : "point with coordinate y = 1 in right to left addition chain",
|
||||
"public" : "04a57232560d9d604655181f775859b0723d4e01a4c867844eb9d81dabb5d19507bbe9cda3346bad7c184daa432e7f794a5b9b8b8d4e55be3a",
|
||||
"private" : "00c1781d86cac2c0af3fb50d54c554a67bd75d25ca796f0486e3fa84f9",
|
||||
"shared" : "daf35bb7bf3a056bb62bb01ba00f581c107f64de85842b3a49bc2a4a",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 66,
|
||||
"comment" : "edge case private key",
|
||||
"public" : "04478e73465bb1183583f4064e67e8b4343af4a05d29dfc04eb60ac2302e5b9a3a1b32e4208d4c284ff26822e09c3a9a4683443e4a35175504",
|
||||
"private" : "03",
|
||||
"shared" : "e71f2157bfe37697ea5193d4732dcc6e5412fa9d38387eacd391c1c6",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 67,
|
||||
"comment" : "edge case private key",
|
||||
"public" : "04478e73465bb1183583f4064e67e8b4343af4a05d29dfc04eb60ac2302e5b9a3a1b32e4208d4c284ff26822e09c3a9a4683443e4a35175504",
|
||||
"private" : "00ffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"shared" : "fa2664717c7fa0161ec2c669b2c0986cdc20456a6e5406302bb53c77",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 68,
|
||||
"comment" : "edge case private key",
|
||||
"public" : "04478e73465bb1183583f4064e67e8b4343af4a05d29dfc04eb60ac2302e5b9a3a1b32e4208d4c284ff26822e09c3a9a4683443e4a35175504",
|
||||
"private" : "01000000000000000000000000000000000000000000000000000000",
|
||||
"shared" : "af6e5ad34497bae0745f53ad78ce8b285d79f400d5c6e6a071f8e6bd",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 69,
|
||||
"comment" : "edge case private key",
|
||||
"public" : "04478e73465bb1183583f4064e67e8b4343af4a05d29dfc04eb60ac2302e5b9a3a1b32e4208d4c284ff26822e09c3a9a4683443e4a35175504",
|
||||
"private" : "7fffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"shared" : "12fd302ff8c13c55a9c111f8bb6b0a13ecf88299c0ae3032ce2bcaff",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 70,
|
||||
"comment" : "edge case private key",
|
||||
"public" : "04478e73465bb1183583f4064e67e8b4343af4a05d29dfc04eb60ac2302e5b9a3a1b32e4208d4c284ff26822e09c3a9a4683443e4a35175504",
|
||||
"private" : "0080000000000000000000000000000000000000000000000000000000",
|
||||
"shared" : "73f1a395b842f1a6752ae417e2c3dc90cafc4476d1d861b7e68ad030",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 71,
|
||||
"comment" : "edge case private key",
|
||||
"public" : "04478e73465bb1183583f4064e67e8b4343af4a05d29dfc04eb60ac2302e5b9a3a1b32e4208d4c284ff26822e09c3a9a4683443e4a35175504",
|
||||
"private" : "00ffffffffffffffffffffffffffff16a2e0b8f03d13dd29455c5c2a3d",
|
||||
"shared" : "b329c20ddb7c78ee4e622bb23a984c0d273ba34b6269f3d9e8f89f8e",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 72,
|
||||
"comment" : "edge case private key",
|
||||
"public" : "04478e73465bb1183583f4064e67e8b4343af4a05d29dfc04eb60ac2302e5b9a3a1b32e4208d4c284ff26822e09c3a9a4683443e4a35175504",
|
||||
"private" : "00ffffffffffffffffffffffffffff16a2e0b8f03e13cd29455c5c2a3d",
|
||||
"shared" : "6f48345209b290ffc5abbe754a201479e5d667a209468080d06197b4",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 73,
|
||||
"comment" : "edge case private key",
|
||||
"public" : "04478e73465bb1183583f4064e67e8b4343af4a05d29dfc04eb60ac2302e5b9a3a1b32e4208d4c284ff26822e09c3a9a4683443e4a35175504",
|
||||
"private" : "00ffffffffffffffffffffffffffff16a2e0b8f03e13d529455c5c2a3d",
|
||||
"shared" : "9f6e30c1c9dad42a153aacd4b49a8e5c721d085cd07b5d5aec244fc1",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 74,
|
||||
"comment" : "edge case private key",
|
||||
"public" : "04478e73465bb1183583f4064e67e8b4343af4a05d29dfc04eb60ac2302e5b9a3a1b32e4208d4c284ff26822e09c3a9a4683443e4a35175504",
|
||||
"private" : "00ffffffffffffffffffffffffffff16a2e0b8f03e13dd29445c5c2a3d",
|
||||
"shared" : "8cadfb19a80949e61bd5b829ad0e76d18a5bb2eeb9ed7fe2b901cecd",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 75,
|
||||
"comment" : "edge case private key",
|
||||
"public" : "04478e73465bb1183583f4064e67e8b4343af4a05d29dfc04eb60ac2302e5b9a3a1b32e4208d4c284ff26822e09c3a9a4683443e4a35175504",
|
||||
"private" : "00ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c29b7",
|
||||
"shared" : "475fd96e0eb8cb8f100a5d7fe043a7a6851d1d611da2643a3c6ae708",
|
||||
"result" : "valid",
|
||||
"flags" : [
|
||||
"AddSubChain"
|
||||
]
|
||||
},
|
||||
{
|
||||
"tcId" : 76,
|
||||
"comment" : "edge case private key",
|
||||
"public" : "04478e73465bb1183583f4064e67e8b4343af4a05d29dfc04eb60ac2302e5b9a3a1b32e4208d4c284ff26822e09c3a9a4683443e4a35175504",
|
||||
"private" : "00ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a37",
|
||||
"shared" : "41ef931d669d1f57d8bb95a01a92321da74be8c6cbc3bbe0b2e73ebd",
|
||||
"result" : "valid",
|
||||
"flags" : [
|
||||
"AddSubChain"
|
||||
]
|
||||
},
|
||||
{
|
||||
"tcId" : 77,
|
||||
"comment" : "edge case private key",
|
||||
"public" : "04478e73465bb1183583f4064e67e8b4343af4a05d29dfc04eb60ac2302e5b9a3a1b32e4208d4c284ff26822e09c3a9a4683443e4a35175504",
|
||||
"private" : "00ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3a",
|
||||
"shared" : "e71f2157bfe37697ea5193d4732dcc6e5412fa9d38387eacd391c1c6",
|
||||
"result" : "valid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 78,
|
||||
"comment" : "edge case private key",
|
||||
"public" : "04478e73465bb1183583f4064e67e8b4343af4a05d29dfc04eb60ac2302e5b9a3a1b32e4208d4c284ff26822e09c3a9a4683443e4a35175504",
|
||||
"private" : "00ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3b",
|
||||
"shared" : "11ff15126411299cbd49e2b7542e69e91ef132e2551a16ecfebb23a3",
|
||||
"result" : "valid",
|
||||
"flags" : [
|
||||
"AddSubChain"
|
||||
]
|
||||
},
|
||||
{
|
||||
"tcId" : 79,
|
||||
"comment" : "point is not on curve",
|
||||
"public" : "040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"private" : "00c6cafb74e2a5b5ed4b991cbbfbc28c18f6df208b6d05e7a2e6668014",
|
||||
"shared" : "",
|
||||
"result" : "invalid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 80,
|
||||
"comment" : "point is not on curve",
|
||||
"public" : "040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001",
|
||||
"private" : "00c6cafb74e2a5b5ed4b991cbbfbc28c18f6df208b6d05e7a2e6668014",
|
||||
"shared" : "",
|
||||
"result" : "invalid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 81,
|
||||
"comment" : "point is not on curve",
|
||||
"public" : "0400000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffff000000000000000000000000",
|
||||
"private" : "00c6cafb74e2a5b5ed4b991cbbfbc28c18f6df208b6d05e7a2e6668014",
|
||||
"shared" : "",
|
||||
"result" : "invalid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 82,
|
||||
"comment" : "point is not on curve",
|
||||
"public" : "0400000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffff000000000000000000000001",
|
||||
"private" : "00c6cafb74e2a5b5ed4b991cbbfbc28c18f6df208b6d05e7a2e6668014",
|
||||
"shared" : "",
|
||||
"result" : "invalid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 83,
|
||||
"comment" : "point is not on curve",
|
||||
"public" : "040000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000",
|
||||
"private" : "00c6cafb74e2a5b5ed4b991cbbfbc28c18f6df208b6d05e7a2e6668014",
|
||||
"shared" : "",
|
||||
"result" : "invalid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 84,
|
||||
"comment" : "point is not on curve",
|
||||
"public" : "040000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000001",
|
||||
"private" : "00c6cafb74e2a5b5ed4b991cbbfbc28c18f6df208b6d05e7a2e6668014",
|
||||
"shared" : "",
|
||||
"result" : "invalid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 85,
|
||||
"comment" : "point is not on curve",
|
||||
"public" : "0400000000000000000000000000000000000000000000000000000001ffffffffffffffffffffffffffffffff000000000000000000000000",
|
||||
"private" : "00c6cafb74e2a5b5ed4b991cbbfbc28c18f6df208b6d05e7a2e6668014",
|
||||
"shared" : "",
|
||||
"result" : "invalid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 86,
|
||||
"comment" : "point is not on curve",
|
||||
"public" : "0400000000000000000000000000000000000000000000000000000001ffffffffffffffffffffffffffffffff000000000000000000000001",
|
||||
"private" : "00c6cafb74e2a5b5ed4b991cbbfbc28c18f6df208b6d05e7a2e6668014",
|
||||
"shared" : "",
|
||||
"result" : "invalid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 87,
|
||||
"comment" : "point is not on curve",
|
||||
"public" : "04ffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"private" : "00c6cafb74e2a5b5ed4b991cbbfbc28c18f6df208b6d05e7a2e6668014",
|
||||
"shared" : "",
|
||||
"result" : "invalid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 88,
|
||||
"comment" : "point is not on curve",
|
||||
"public" : "04ffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000001",
|
||||
"private" : "00c6cafb74e2a5b5ed4b991cbbfbc28c18f6df208b6d05e7a2e6668014",
|
||||
"shared" : "",
|
||||
"result" : "invalid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 89,
|
||||
"comment" : "point is not on curve",
|
||||
"public" : "04ffffffffffffffffffffffffffffffff000000000000000000000000ffffffffffffffffffffffffffffffff000000000000000000000000",
|
||||
"private" : "00c6cafb74e2a5b5ed4b991cbbfbc28c18f6df208b6d05e7a2e6668014",
|
||||
"shared" : "",
|
||||
"result" : "invalid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 90,
|
||||
"comment" : "point is not on curve",
|
||||
"public" : "04ffffffffffffffffffffffffffffffff000000000000000000000000ffffffffffffffffffffffffffffffff000000000000000000000001",
|
||||
"private" : "00c6cafb74e2a5b5ed4b991cbbfbc28c18f6df208b6d05e7a2e6668014",
|
||||
"shared" : "",
|
||||
"result" : "invalid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 91,
|
||||
"comment" : "point is not on curve",
|
||||
"public" : "04ffffffffffffffffffffffffffffffff00000000000000000000000100000000000000000000000000000000000000000000000000000000",
|
||||
"private" : "00c6cafb74e2a5b5ed4b991cbbfbc28c18f6df208b6d05e7a2e6668014",
|
||||
"shared" : "",
|
||||
"result" : "invalid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 92,
|
||||
"comment" : "point is not on curve",
|
||||
"public" : "04ffffffffffffffffffffffffffffffff00000000000000000000000100000000000000000000000000000000000000000000000000000001",
|
||||
"private" : "00c6cafb74e2a5b5ed4b991cbbfbc28c18f6df208b6d05e7a2e6668014",
|
||||
"shared" : "",
|
||||
"result" : "invalid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 93,
|
||||
"comment" : "point is not on curve",
|
||||
"public" : "04ffffffffffffffffffffffffffffffff000000000000000000000001ffffffffffffffffffffffffffffffff000000000000000000000000",
|
||||
"private" : "00c6cafb74e2a5b5ed4b991cbbfbc28c18f6df208b6d05e7a2e6668014",
|
||||
"shared" : "",
|
||||
"result" : "invalid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 94,
|
||||
"comment" : "point is not on curve",
|
||||
"public" : "04ffffffffffffffffffffffffffffffff000000000000000000000001ffffffffffffffffffffffffffffffff000000000000000000000001",
|
||||
"private" : "00c6cafb74e2a5b5ed4b991cbbfbc28c18f6df208b6d05e7a2e6668014",
|
||||
"shared" : "",
|
||||
"result" : "invalid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 95,
|
||||
"comment" : "",
|
||||
"public" : "",
|
||||
"private" : "00c6cafb74e2a5b5ed4b991cbbfbc28c18f6df208b6d05e7a2e6668014",
|
||||
"shared" : "",
|
||||
"result" : "invalid",
|
||||
"flags" : []
|
||||
},
|
||||
{
|
||||
"tcId" : 96,
|
||||
"comment" : "invalid public key",
|
||||
"public" : "020ca753db5ddeca474241f8d2dafc0844343fd0e37eded2f0192d51b2",
|
||||
"private" : "00fc28a0ca0f8e36b0d4f71421845135a22aef543b9fddf8c775b2d18f",
|
||||
"shared" : "",
|
||||
"result" : "invalid",
|
||||
"flags" : [
|
||||
"CompressedPoint"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
3581
curve-definitions/test/fixtures/ecdh_secp224r1_test.json
vendored
3581
curve-definitions/test/fixtures/ecdh_secp224r1_test.json
vendored
File diff suppressed because it is too large
Load Diff
4545
curve-definitions/test/fixtures/ecdh_secp256k1_test.json
vendored
4545
curve-definitions/test/fixtures/ecdh_secp256k1_test.json
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
4366
curve-definitions/test/fixtures/ecdh_secp384r1_test.json
vendored
4366
curve-definitions/test/fixtures/ecdh_secp384r1_test.json
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
4868
curve-definitions/test/fixtures/ecdh_secp521r1_test.json
vendored
4868
curve-definitions/test/fixtures/ecdh_secp521r1_test.json
vendored
File diff suppressed because it is too large
Load Diff
@ -1,8 +0,0 @@
|
||||
import { should } from 'micro-should';
|
||||
|
||||
import './basic.test.js';
|
||||
import './rfc6979.test.js';
|
||||
import './secp256k1.test.js';
|
||||
import './starknet/starknet.test.js';
|
||||
|
||||
should.run();
|
@ -1,33 +0,0 @@
|
||||
import { deepStrictEqual } from 'assert';
|
||||
import { should } from 'micro-should';
|
||||
import * as nist from '../lib/nist.js';
|
||||
import { default as rfc6979 } from './fixtures/rfc6979.json' assert { type: 'json' };
|
||||
function hexToBigint(hex) {
|
||||
return BigInt('0x' + hex)
|
||||
}
|
||||
|
||||
should('RFC6979', () => {
|
||||
for (const v of rfc6979) {
|
||||
const curve = nist[v.curve];
|
||||
deepStrictEqual(curve.CURVE.n, hexToBigint(v.q));
|
||||
const pubKey = curve.getPublicKey(v.private);
|
||||
const pubPoint = curve.Point.fromHex(pubKey);
|
||||
deepStrictEqual(pubPoint.x, hexToBigint(v.Ux));
|
||||
deepStrictEqual(pubPoint.y, hexToBigint(v.Uy));
|
||||
for (const c of v.cases) {
|
||||
const h = curve.CURVE.hash(c.message);
|
||||
const sigObj = curve.sign(h, v.private);
|
||||
// const sigObj = curve.Signature.fromDER(sig);
|
||||
deepStrictEqual(sigObj.r, hexToBigint(c.r), 'R');
|
||||
deepStrictEqual(sigObj.s, hexToBigint(c.s), 'S');
|
||||
deepStrictEqual(curve.verify(sigObj.toDERRawBytes(), h, pubKey), true, 'verify(1)');
|
||||
deepStrictEqual(curve.verify(sigObj, h, pubKey), true, 'verify(2)');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// ESM is broken.
|
||||
import url from 'url';
|
||||
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
|
||||
should.run();
|
||||
}
|
@ -1,559 +0,0 @@
|
||||
import * as fc from 'fast-check';
|
||||
import * as nist from '../lib/nist.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 } from 'micro-should';
|
||||
import { deepStrictEqual, throws, rejects } from 'assert';
|
||||
import { hexToBytes, bytesToHex } from '@noble/hashes/utils';
|
||||
|
||||
const hex = bytesToHex;
|
||||
const secp = nist.secp256k1;
|
||||
const privatesTxt = readFileSync('./test/vectors/privates-2.txt', 'utf-8');
|
||||
const schCsv = readFileSync('./test/vectors/schnorr.csv', 'utf-8');
|
||||
|
||||
const FC_BIGINT = fc.bigInt(1n + 1n, secp.CURVE.n - 1n);
|
||||
// prettier-ignore
|
||||
const INVALID_ITEMS = ['deadbeef', Math.pow(2, 53), [1], 'xyzxyzxyxyzxyzxyxyzxyzxyxyzxyzxyxyzxyzxyxyzxyzxyxyzxyzxyxyzxyzxy', secp.CURVE.n + 2n];
|
||||
|
||||
const toBEHex = (n) => n.toString(16).padStart(64, '0');
|
||||
|
||||
function hexToNumber(hex) {
|
||||
if (typeof hex !== 'string') {
|
||||
throw new TypeError('hexToNumber: expected string, got ' + typeof hex);
|
||||
}
|
||||
// Big Endian
|
||||
return BigInt(`0x${hex}`);
|
||||
}
|
||||
|
||||
should('secp256k1.getPublicKey()', () => {
|
||||
const data = privatesTxt
|
||||
.split('\n')
|
||||
.filter((line) => line)
|
||||
.map((line) => line.split(':'));
|
||||
for (let [priv, x, y] of data) {
|
||||
const point = secp.Point.fromPrivateKey(BigInt(priv));
|
||||
deepStrictEqual(toBEHex(point.x), x);
|
||||
deepStrictEqual(toBEHex(point.y), y);
|
||||
|
||||
const point2 = secp.Point.fromHex(secp.getPublicKey(toBEHex(BigInt(priv))));
|
||||
deepStrictEqual(toBEHex(point2.x), x);
|
||||
deepStrictEqual(toBEHex(point2.y), y);
|
||||
|
||||
const point3 = secp.Point.fromHex(secp.getPublicKey(hexToBytes(toBEHex(BigInt(priv)))));
|
||||
deepStrictEqual(toBEHex(point3.x), x);
|
||||
deepStrictEqual(toBEHex(point3.y), y);
|
||||
}
|
||||
});
|
||||
should('secp256k1.getPublicKey() rejects invalid keys', () => {
|
||||
// for (const item of INVALID_ITEMS) {
|
||||
// throws(() => secp.getPublicKey(item));
|
||||
// }
|
||||
});
|
||||
should('secp256k1.precompute', () => {
|
||||
secp.utils.precompute(4);
|
||||
const data = privatesTxt
|
||||
.split('\n')
|
||||
.filter((line) => line)
|
||||
.map((line) => line.split(':'));
|
||||
for (let [priv, x, y] of data) {
|
||||
const point = secp.Point.fromPrivateKey(BigInt(priv));
|
||||
deepStrictEqual(toBEHex(point.x), x);
|
||||
deepStrictEqual(toBEHex(point.y), y);
|
||||
|
||||
const point2 = secp.Point.fromHex(secp.getPublicKey(toBEHex(BigInt(priv))));
|
||||
deepStrictEqual(toBEHex(point2.x), x);
|
||||
deepStrictEqual(toBEHex(point2.y), y);
|
||||
|
||||
const point3 = secp.Point.fromHex(secp.getPublicKey(hexToBytes(toBEHex(BigInt(priv)))));
|
||||
deepStrictEqual(toBEHex(point3.x), x);
|
||||
deepStrictEqual(toBEHex(point3.y), y);
|
||||
}
|
||||
});
|
||||
|
||||
should('secp256k1.Point.isValidPoint()', () => {
|
||||
for (const vector of points.valid.isPoint) {
|
||||
const { P, expected } = vector;
|
||||
if (expected) {
|
||||
secp.Point.fromHex(P);
|
||||
} else {
|
||||
throws(() => secp.Point.fromHex(P));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
should('secp256k1.Point.fromPrivateKey()', () => {
|
||||
for (const vector of points.valid.pointFromScalar) {
|
||||
const { d, expected } = vector;
|
||||
let p = secp.Point.fromPrivateKey(d);
|
||||
deepStrictEqual(p.toHex(true), expected);
|
||||
}
|
||||
});
|
||||
|
||||
should('secp256k1.Point#toHex(compressed)', () => {
|
||||
for (const vector of points.valid.pointCompress) {
|
||||
const { P, compress, expected } = vector;
|
||||
let p = secp.Point.fromHex(P);
|
||||
deepStrictEqual(p.toHex(compress), expected);
|
||||
}
|
||||
});
|
||||
|
||||
should('secp256k1.Point#toHex() roundtrip (failed case)', () => {
|
||||
const point1 =
|
||||
secp.Point.fromPrivateKey(
|
||||
88572218780422190464634044548753414301110513745532121983949500266768436236425n
|
||||
);
|
||||
// const hex = point1.toHex(true);
|
||||
// deepStrictEqual(secp.Point.fromHex(hex).toHex(true), hex);
|
||||
});
|
||||
|
||||
should('secp256k1.Point#toHex() roundtrip', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (x) => {
|
||||
const point1 = secp.Point.fromPrivateKey(x);
|
||||
const hex = point1.toHex(true);
|
||||
deepStrictEqual(secp.Point.fromHex(hex).toHex(true), hex);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
should('secp256k1.Point#add(other)', () => {
|
||||
for (const vector of points.valid.pointAdd) {
|
||||
const { P, Q, expected } = vector;
|
||||
let p = secp.Point.fromHex(P);
|
||||
let q = secp.Point.fromHex(Q);
|
||||
if (expected) {
|
||||
deepStrictEqual(p.add(q).toHex(true), expected);
|
||||
} else {
|
||||
if (!p.equals(q.negate())) {
|
||||
throws(() => p.add(q).toHex(true));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
should('secp256k1.Point#multiply(privateKey)', () => {
|
||||
for (const vector of points.valid.pointMultiply) {
|
||||
const { P, d, expected } = vector;
|
||||
const p = secp.Point.fromHex(P);
|
||||
if (expected) {
|
||||
deepStrictEqual(p.multiply(hexToNumber(d)).toHex(true), expected);
|
||||
} else {
|
||||
throws(() => {
|
||||
p.multiply(hexToNumber(d)).toHex(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (const vector of points.invalid.pointMultiply) {
|
||||
const { P, d } = vector;
|
||||
if (hexToNumber(d) < secp.CURVE.n) {
|
||||
throws(() => {
|
||||
const p = secp.Point.fromHex(P);
|
||||
p.multiply(hexToNumber(d)).toHex(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
for (const num of [0n, 0, -1n, -1, 1.1]) {
|
||||
throws(() => secp.Point.BASE.multiply(num));
|
||||
}
|
||||
});
|
||||
|
||||
// multiply() should equal multiplyUnsafe()
|
||||
// should('JacobianPoint#multiplyUnsafe', () => {
|
||||
// const p0 = new secp.JacobianPoint(
|
||||
// 55066263022277343669578718895168534326250603453777594175500187360389116729240n,
|
||||
// 32670510020758816978083085130507043184471273380659243275938904335757337482424n,
|
||||
// 1n
|
||||
// );
|
||||
// const z = 106011723082030650010038151861333186846790370053628296836951575624442507889495n;
|
||||
// console.log(p0.multiply(z));
|
||||
// console.log(secp.JacobianPoint.normalizeZ([p0.multiplyUnsafe(z)])[0])
|
||||
// });
|
||||
|
||||
should('secp256k1.Signature.fromCompactHex() roundtrip', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, (r, s) => {
|
||||
const sig = new secp.Signature(r, s);
|
||||
deepStrictEqual(secp.Signature.fromCompact(sig.toCompactHex()), sig);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
should('secp256k1.Signature.fromDERHex() roundtrip', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, (r, s) => {
|
||||
const sig = new secp.Signature(r, s);
|
||||
deepStrictEqual(secp.Signature.fromDER(sig.toDERHex()), sig);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
should('secp256k1.sign()/should create deterministic signatures with RFC 6979', async () => {
|
||||
for (const vector of ecdsa.valid) {
|
||||
let usig = await secp.sign(vector.m, vector.d);
|
||||
let sig = (usig.toCompactHex());
|
||||
const vsig = vector.signature;
|
||||
deepStrictEqual(sig.slice(0, 64), vsig.slice(0, 64));
|
||||
deepStrictEqual(sig.slice(64, 128), vsig.slice(64, 128));
|
||||
}
|
||||
});
|
||||
|
||||
should(
|
||||
'secp256k1.sign()/should not create invalid deterministic signatures with RFC 6979',
|
||||
async () => {
|
||||
for (const vector of ecdsa.invalid.sign) {
|
||||
throws(() => {
|
||||
return secp.sign(vector.m, vector.d);
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
should('secp256k1.sign()/edge cases', () => {
|
||||
// @ts-ignore
|
||||
rejects(async () => await secp.sign());
|
||||
// @ts-ignore
|
||||
rejects(async () => await secp.sign(''));
|
||||
});
|
||||
|
||||
should('secp256k1.sign()/should create correct DER encoding against libsecp256k1', async () => {
|
||||
const CASES = [
|
||||
[
|
||||
'd1a9dc8ed4e46a6a3e5e594615ca351d7d7ef44df1e4c94c1802f3592183794b',
|
||||
'304402203de2559fccb00c148574997f660e4d6f40605acc71267ee38101abf15ff467af02200950abdf40628fd13f547792ba2fc544681a485f2fdafb5c3b909a4df7350e6b',
|
||||
],
|
||||
[
|
||||
'5f97983254982546d3976d905c6165033976ee449d300d0e382099fa74deaf82',
|
||||
'3045022100c046d9ff0bd2845b9aa9dff9f997ecebb31e52349f80fe5a5a869747d31dcb88022011f72be2a6d48fe716b825e4117747b397783df26914a58139c3f4c5cbb0e66c',
|
||||
],
|
||||
[
|
||||
'0d7017a96b97cd9be21cf28aada639827b2814a654a478c81945857196187808',
|
||||
'3045022100d18990bba7832bb283e3ecf8700b67beb39acc73f4200ed1c331247c46edccc602202e5c8bbfe47ae159512c583b30a3fa86575cddc62527a03de7756517ae4c6c73',
|
||||
],
|
||||
];
|
||||
const privKey = hexToBytes('0101010101010101010101010101010101010101010101010101010101010101');
|
||||
for (let [msg, exp] of CASES) {
|
||||
const res = await secp.sign(msg, privKey, { extraEntropy: undefined });
|
||||
deepStrictEqual((res.toDERHex()), exp);
|
||||
const rs = secp.Signature.fromDER(res.toDERHex()).toCompactHex();
|
||||
deepStrictEqual(secp.Signature.fromCompact(rs).toDERHex(), exp);
|
||||
}
|
||||
});
|
||||
should('secp256k1.sign()/sign ecdsa extraData', async () => {
|
||||
const ent1 = '0000000000000000000000000000000000000000000000000000000000000000';
|
||||
const ent2 = '0000000000000000000000000000000000000000000000000000000000000001';
|
||||
const ent3 = '6e723d3fd94ed5d2b6bdd4f123364b0f3ca52af829988a63f8afe91d29db1c33';
|
||||
const ent4 = 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141';
|
||||
const ent5 = 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff';
|
||||
|
||||
for (const e of ecdsa.extraEntropy) {
|
||||
const sign = async (extraEntropy) => {
|
||||
const s = secp.sign(e.m, e.d, {extraEntropy }).toCompactHex();
|
||||
return s;
|
||||
};
|
||||
deepStrictEqual(await sign(), e.signature);
|
||||
deepStrictEqual(await sign(ent1), e.extraEntropy0);
|
||||
deepStrictEqual(await sign(ent2), e.extraEntropy1);
|
||||
deepStrictEqual(await sign(ent3), e.extraEntropyRand);
|
||||
deepStrictEqual(await sign(ent4), e.extraEntropyN);
|
||||
deepStrictEqual(await sign(ent5), e.extraEntropyMax);
|
||||
}
|
||||
});
|
||||
|
||||
should('secp256k1.verify()/should verify signature', async () => {
|
||||
const MSG = '01'.repeat(32);
|
||||
const PRIV_KEY = 0x2n;
|
||||
const signature = await secp.sign(MSG, PRIV_KEY);
|
||||
const publicKey = secp.getPublicKey(PRIV_KEY);
|
||||
deepStrictEqual(publicKey.length, 65);
|
||||
deepStrictEqual(secp.verify(signature, MSG, publicKey), true);
|
||||
});
|
||||
should('secp256k1.verify()/should not verify signature with wrong public key', async () => {
|
||||
const MSG = '01'.repeat(32);
|
||||
const PRIV_KEY = 0x2n;
|
||||
const WRONG_PRIV_KEY = 0x22n;
|
||||
const signature = await secp.sign(MSG, PRIV_KEY);
|
||||
const publicKey = secp.Point.fromPrivateKey(WRONG_PRIV_KEY).toHex();
|
||||
deepStrictEqual(publicKey.length, 130);
|
||||
deepStrictEqual(secp.verify(signature, MSG, publicKey), false);
|
||||
});
|
||||
should('secp256k1.verify()/should not verify signature with wrong hash', async () => {
|
||||
const MSG = '01'.repeat(32);
|
||||
const PRIV_KEY = 0x2n;
|
||||
const WRONG_MSG = '11'.repeat(32);
|
||||
const signature = await secp.sign(MSG, PRIV_KEY);
|
||||
const publicKey = secp.getPublicKey(PRIV_KEY);
|
||||
deepStrictEqual(publicKey.length, 65);
|
||||
deepStrictEqual(secp.verify(signature, WRONG_MSG, publicKey), false);
|
||||
});
|
||||
should('secp256k1.verify()/should verify random signatures', async () =>
|
||||
fc.assert(
|
||||
fc.asyncProperty(
|
||||
FC_BIGINT,
|
||||
fc.hexaString({ minLength: 64, maxLength: 64 }),
|
||||
async (privKey, msg) => {
|
||||
const pub = secp.getPublicKey(privKey);
|
||||
const sig = await secp.sign(msg, privKey);
|
||||
deepStrictEqual(secp.verify(sig, msg, pub), true);
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
should('secp256k1.verify()/should not verify signature with invalid r/s', () => {
|
||||
const msg = new Uint8Array([
|
||||
0xbb, 0x5a, 0x52, 0xf4, 0x2f, 0x9c, 0x92, 0x61, 0xed, 0x43, 0x61, 0xf5, 0x94, 0x22, 0xa1, 0xe3,
|
||||
0x00, 0x36, 0xe7, 0xc3, 0x2b, 0x27, 0x0c, 0x88, 0x07, 0xa4, 0x19, 0xfe, 0xca, 0x60, 0x50, 0x23,
|
||||
]);
|
||||
const x = 100260381870027870612475458630405506840396644859280795015145920502443964769584n;
|
||||
const y = 41096923727651821103518389640356553930186852801619204169823347832429067794568n;
|
||||
const r = 1n;
|
||||
const s = 115792089237316195423570985008687907852837564279074904382605163141518162728904n;
|
||||
|
||||
const pub = new secp.Point(x, y);
|
||||
const signature = new secp.Signature(2n, 2n);
|
||||
// @ts-ignore
|
||||
signature.r = r;
|
||||
// @ts-ignore
|
||||
signature.s = s;
|
||||
|
||||
const verified = secp.verify(signature, msg, pub);
|
||||
// Verifies, but it shouldn't, because signature S > curve order
|
||||
deepStrictEqual(verified, false);
|
||||
});
|
||||
should('secp256k1.verify()/should not verify msg = curve order', async () => {
|
||||
const msg = 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141';
|
||||
const x = 55066263022277343669578718895168534326250603453777594175500187360389116729240n;
|
||||
const y = 32670510020758816978083085130507043184471273380659243275938904335757337482424n;
|
||||
const r = 104546003225722045112039007203142344920046999340768276760147352389092131869133n;
|
||||
const s = 96900796730960181123786672629079577025401317267213807243199432755332205217369n;
|
||||
const pub = new secp.Point(x, y);
|
||||
const sig = new secp.Signature(r, s);
|
||||
deepStrictEqual(secp.verify(sig, msg, pub), false);
|
||||
});
|
||||
should('secp256k1.verify()/should verify non-strict msg bb5a...', async () => {
|
||||
const msg = 'bb5a52f42f9c9261ed4361f59422a1e30036e7c32b270c8807a419feca605023';
|
||||
const x = 3252872872578928810725465493269682203671229454553002637820453004368632726370n;
|
||||
const y = 17482644437196207387910659778872952193236850502325156318830589868678978890912n;
|
||||
const r = 432420386565659656852420866390673177323n;
|
||||
const s = 115792089237316195423570985008687907852837564279074904382605163141518161494334n;
|
||||
const pub = new secp.Point(x, y);
|
||||
const sig = new secp.Signature(r, s);
|
||||
deepStrictEqual(secp.verify(sig, msg, pub, { strict: false }), true);
|
||||
});
|
||||
should(
|
||||
'secp256k1.verify()/should not verify invalid deterministic signatures with RFC 6979',
|
||||
() => {
|
||||
for (const vector of ecdsa.invalid.verify) {
|
||||
const res = secp.verify(vector.signature, vector.m, vector.Q);
|
||||
deepStrictEqual(res, false);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// describe('schnorr', () => {
|
||||
// // index,secret key,public key,aux_rand,message,signature,verification result,comment
|
||||
// const vectors = schCsv
|
||||
// .split('\n')
|
||||
// .map((line: string) => line.split(','))
|
||||
// .slice(1, -1);
|
||||
// for (let vec of vectors) {
|
||||
// const [index, sec, pub, rnd, msg, expSig, passes, comment] = vec;
|
||||
// it(`should sign with Schnorr scheme vector ${index}`, async () => {
|
||||
// if (sec) {
|
||||
// expect(hex(secp.schnorr.getPublicKey(sec))).toBe(pub.toLowerCase());
|
||||
// const sig = await secp.schnorr.sign(msg, sec, rnd);
|
||||
// const sigS = secp.schnorr.signSync(msg, sec, rnd);
|
||||
// expect(hex(sig)).toBe(expSig.toLowerCase());
|
||||
// expect(hex(sigS)).toBe(expSig.toLowerCase());
|
||||
// expect(await secp.schnorr.verify(sigS, msg, pub)).toBe(true);
|
||||
// expect(secp.schnorr.verifySync(sig, msg, pub)).toBe(true);
|
||||
// } else {
|
||||
// const passed = await secp.schnorr.verify(expSig, msg, pub);
|
||||
// const passedS = secp.schnorr.verifySync(expSig, msg, pub);
|
||||
// if (passes === 'TRUE') {
|
||||
// expect(passed).toBeTruthy();
|
||||
// expect(passedS).toBeTruthy();
|
||||
// } else {
|
||||
// expect(passed).toBeFalsy();
|
||||
// expect(passedS).toBeFalsy();
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
|
||||
should('secp256k1.recoverPublicKey()/should recover public key from recovery bit', async () => {
|
||||
const message = '00000000000000000000000000000000000000000000000000000000deadbeef';
|
||||
const privateKey = 123456789n;
|
||||
const publicKey = secp.Point.fromHex(secp.getPublicKey(privateKey)).toHex(false);
|
||||
const sig = await secp.sign(message, privateKey);
|
||||
const recoveredPubkey = sig.recoverPublicKey(message);
|
||||
// const recoveredPubkey = secp.recoverPublicKey(message, signature, recovery);
|
||||
deepStrictEqual(recoveredPubkey !== null, true);
|
||||
deepStrictEqual((recoveredPubkey).toHex(), publicKey);
|
||||
deepStrictEqual(secp.verify(sig, message, publicKey), true);
|
||||
});
|
||||
should('secp256k1.recoverPublicKey()/should not recover zero points', () => {
|
||||
const msgHash = '6b8d2c81b11b2d699528dde488dbdf2f94293d0d33c32e347f255fa4a6c1f0a9';
|
||||
const sig =
|
||||
'79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817986b8d2c81b11b2d699528dde488dbdf2f94293d0d33c32e347f255fa4a6c1f0a9';
|
||||
const recovery = 0;
|
||||
throws(() => secp.recoverPublicKey(msgHash, sig, recovery));
|
||||
});
|
||||
should('secp256k1.recoverPublicKey()/should handle all-zeros msghash', async () => {
|
||||
const privKey = secp.utils.randomPrivateKey();
|
||||
const pub = secp.getPublicKey(privKey);
|
||||
const zeros = '0000000000000000000000000000000000000000000000000000000000000000';
|
||||
const sig = await secp.sign(zeros, privKey, { recovered: true });
|
||||
const recoveredKey = sig.recoverPublicKey(zeros);
|
||||
deepStrictEqual(recoveredKey.toRawBytes(), pub);
|
||||
});
|
||||
should('secp256k1.recoverPublicKey()/should handle RFC 6979 vectors', async () => {
|
||||
for (const vector of ecdsa.valid) {
|
||||
if (secp.utils.mod(hexToNumber(vector.m), secp.CURVE.n) === 0n) continue;
|
||||
let usig = secp.sign(vector.m, vector.d);
|
||||
let sig = (usig).toDERHex();
|
||||
const vpub = secp.getPublicKey(vector.d);
|
||||
const recovered = usig.recoverPublicKey(vector.m);
|
||||
deepStrictEqual((recovered).toHex(), hex(vpub));
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: Real implementation.
|
||||
function derToPub(der) {
|
||||
return der.slice(46);
|
||||
}
|
||||
should('secp256k1.getSharedSecret()/should produce correct results', () => {
|
||||
// TODO: Once der is there, run all tests.
|
||||
for (const vector of ecdh.testGroups[0].tests.slice(0, 230)) {
|
||||
if (vector.result === 'invalid' || vector.private.length !== 64) {
|
||||
// We support eth-like hexes
|
||||
if (vector.private.length < 64) continue;
|
||||
throws(() => {
|
||||
secp.getSharedSecret(vector.private, derToPub(vector.public), true);
|
||||
});
|
||||
} else if (vector.result === 'valid') {
|
||||
const res = secp.getSharedSecret(vector.private, derToPub(vector.public), true);
|
||||
deepStrictEqual(hex(res.slice(1)), `${vector.shared}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
should('secp256k1.getSharedSecret()/priv/pub order matters', () => {
|
||||
for (const vector of ecdh.testGroups[0].tests.slice(0, 100)) {
|
||||
if (vector.result === 'valid') {
|
||||
let priv = vector.private;
|
||||
priv = priv.length === 66 ? priv.slice(2) : priv;
|
||||
throws(() => secp.getSharedSecret(derToPub(vector.public), priv, true));
|
||||
}
|
||||
}
|
||||
});
|
||||
should('secp256k1.getSharedSecret()/rejects invalid keys', () => {
|
||||
throws(() => secp.getSharedSecret('01', '02'));
|
||||
});
|
||||
|
||||
should('secp256k1.utils.isValidPrivateKey()', () => {
|
||||
for (const vector of privates.valid.isPrivate) {
|
||||
const { d, expected } = vector;
|
||||
deepStrictEqual(secp.utils.isValidPrivateKey(d), expected);
|
||||
}
|
||||
});
|
||||
const normal = secp.utils._normalizePrivateKey;
|
||||
const tweakUtils = {
|
||||
privateAdd: (privateKey, tweak) => {
|
||||
const p = normal(privateKey);
|
||||
const t = normal(tweak);
|
||||
return secp.utils._bigintToBytes(secp.utils.mod(p + t, secp.CURVE.n));
|
||||
},
|
||||
|
||||
privateNegate: (privateKey) => {
|
||||
const p = normal(privateKey);
|
||||
return secp.utils._bigintToBytes(secp.CURVE.n - p);
|
||||
},
|
||||
|
||||
pointAddScalar: (p, tweak, isCompressed) => {
|
||||
const P = secp.Point.fromHex(p);
|
||||
const t = normal(tweak);
|
||||
const Q = secp.Point.BASE.multiplyAndAddUnsafe(P, t, 1n);
|
||||
if (!Q) throw new Error('Tweaked point at infinity');
|
||||
return Q.toRawBytes(isCompressed);
|
||||
},
|
||||
|
||||
pointMultiply: (p, tweak, isCompressed) => {
|
||||
const P = secp.Point.fromHex(p);
|
||||
const h = typeof tweak === 'string' ? tweak : bytesToHex(tweak);
|
||||
const t = BigInt(`0x${h}`);
|
||||
return P.multiply(t).toRawBytes(isCompressed);
|
||||
},
|
||||
};
|
||||
|
||||
should('secp256k1.privateAdd()', () => {
|
||||
for (const vector of privates.valid.add) {
|
||||
const { a, b, expected } = vector;
|
||||
deepStrictEqual(bytesToHex(tweakUtils.privateAdd(a, b)), expected);
|
||||
}
|
||||
});
|
||||
should('secp256k1.privateNegate()', () => {
|
||||
for (const vector of privates.valid.negate) {
|
||||
const { a, expected } = vector;
|
||||
deepStrictEqual(bytesToHex(tweakUtils.privateNegate(a)), expected);
|
||||
}
|
||||
});
|
||||
should('secp256k1.pointAddScalar()', () => {
|
||||
for (const vector of points.valid.pointAddScalar) {
|
||||
const { description, P, d, expected } = vector;
|
||||
const compressed = !!expected && expected.length === 66; // compressed === 33 bytes
|
||||
deepStrictEqual(bytesToHex(tweakUtils.pointAddScalar(P, d, compressed)), expected);
|
||||
}
|
||||
});
|
||||
should('secp256k1.pointAddScalar() invalid', () => {
|
||||
for (const vector of points.invalid.pointAddScalar) {
|
||||
const { P, d, exception } = vector;
|
||||
throws(() => tweakUtils.pointAddScalar(P, d));
|
||||
}
|
||||
});
|
||||
should('secp256k1.pointMultiply()', () => {
|
||||
for (const vector of points.valid.pointMultiply) {
|
||||
const { P, d, expected } = vector;
|
||||
deepStrictEqual(bytesToHex(tweakUtils.pointMultiply(P, d, true)), expected);
|
||||
}
|
||||
});
|
||||
should('secp256k1.pointMultiply() invalid', () => {
|
||||
for (const vector of points.invalid.pointMultiply) {
|
||||
const { P, d, exception } = vector;
|
||||
throws(() => tweakUtils.pointMultiply(P, d));
|
||||
}
|
||||
});
|
||||
|
||||
should('secp256k1.wychenproof vectors', () => {
|
||||
for (let group of wp.testGroups) {
|
||||
const pubKey = secp.Point.fromHex(group.key.uncompressed);
|
||||
for (let test of group.tests) {
|
||||
const m = secp.CURVE.hash(hexToBytes(test.msg));
|
||||
if (test.result === 'valid' || test.result === 'acceptable') {
|
||||
const verified = secp.verify(test.sig, m, pubKey);
|
||||
if (secp.Signature.fromDER(test.sig).hasHighS()) {
|
||||
deepStrictEqual(verified, false);
|
||||
} else {
|
||||
deepStrictEqual(verified, true);
|
||||
}
|
||||
} else if (test.result === 'invalid') {
|
||||
let failed = false;
|
||||
try {
|
||||
const verified = secp.verify(test.sig, m, pubKey);
|
||||
if (!verified) failed = true;
|
||||
} catch (error) {
|
||||
failed = true;
|
||||
}
|
||||
deepStrictEqual(failed, true);
|
||||
} else {
|
||||
deepStrictEqual(false, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
should.run();
|
@ -1,200 +0,0 @@
|
||||
import { deepStrictEqual, throws } from 'assert';
|
||||
import { should } from 'micro-should';
|
||||
import * as starknet from '../../lib/starknet.js';
|
||||
import { default as issue2 } from './fixtures/issue2.json' assert { type: 'json' };
|
||||
|
||||
should('Basic elliptic sanity check', () => {
|
||||
const g1 = starknet.Point.BASE;
|
||||
deepStrictEqual(
|
||||
g1.x.toString(16),
|
||||
'1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca'
|
||||
);
|
||||
deepStrictEqual(
|
||||
g1.y.toString(16),
|
||||
'5668060aa49730b7be4801df46ec62de53ecd11abe43a32873000c36e8dc1f'
|
||||
);
|
||||
const g2 = g1.double();
|
||||
deepStrictEqual(
|
||||
g2.x.toString(16),
|
||||
'759ca09377679ecd535a81e83039658bf40959283187c654c5416f439403cf5'
|
||||
);
|
||||
deepStrictEqual(
|
||||
g2.y.toString(16),
|
||||
'6f524a3400e7708d5c01a28598ad272e7455aa88778b19f93b562d7a9646c41'
|
||||
);
|
||||
const g3 = g2.add(g1);
|
||||
deepStrictEqual(
|
||||
g3.x.toString(16),
|
||||
'411494b501a98abd8262b0da1351e17899a0c4ef23dd2f96fec5ba847310b20'
|
||||
);
|
||||
deepStrictEqual(
|
||||
g3.y.toString(16),
|
||||
'7e1b3ebac08924d2c26f409549191fcf94f3bf6f301ed3553e22dfb802f0686'
|
||||
);
|
||||
const g32 = g1.multiply(3);
|
||||
deepStrictEqual(
|
||||
g32.x.toString(16),
|
||||
'411494b501a98abd8262b0da1351e17899a0c4ef23dd2f96fec5ba847310b20'
|
||||
);
|
||||
deepStrictEqual(
|
||||
g32.y.toString(16),
|
||||
'7e1b3ebac08924d2c26f409549191fcf94f3bf6f301ed3553e22dfb802f0686'
|
||||
);
|
||||
const minus1 = g1.multiply(starknet.CURVE.n - 1n);
|
||||
deepStrictEqual(
|
||||
minus1.x.toString(16),
|
||||
'1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca'
|
||||
);
|
||||
deepStrictEqual(
|
||||
minus1.y.toString(16),
|
||||
'7a997f9f55b68e04841b7fe20b9139d21ac132ee541bc5cd78cfff3c91723e2'
|
||||
);
|
||||
});
|
||||
|
||||
should('Pedersen', () => {
|
||||
deepStrictEqual(
|
||||
starknet.pedersen(2, 3),
|
||||
'0x5774fa77b3d843ae9167abd61cf80365a9b2b02218fc2f628494b5bdc9b33b8'
|
||||
);
|
||||
deepStrictEqual(
|
||||
starknet.pedersen(1, 2),
|
||||
'0x5bb9440e27889a364bcb678b1f679ecd1347acdedcbf36e83494f857cc58026'
|
||||
);
|
||||
deepStrictEqual(
|
||||
starknet.pedersen(3, 4),
|
||||
'0x262697b88544f733e5c6907c3e1763131e9f14c51ee7951258abbfb29415fbf'
|
||||
);
|
||||
});
|
||||
|
||||
should('Hash chain', () => {
|
||||
deepStrictEqual(
|
||||
starknet.hashChain([1, 2, 3]),
|
||||
'0x5d9d62d4040b977c3f8d2389d494e4e89a96a8b45c44b1368f1cc6ec5418915'
|
||||
);
|
||||
});
|
||||
|
||||
should('Pedersen hash edgecases', () => {
|
||||
// >>> pedersen_hash(0,0)
|
||||
const zero = '0x49ee3eba8c1600700ee1b87eb599f16716b0b1022947733551fde4050ca6804';
|
||||
deepStrictEqual(starknet.pedersen(0, 0), zero);
|
||||
deepStrictEqual(starknet.pedersen(0n, 0n), zero);
|
||||
deepStrictEqual(starknet.pedersen('0', '0'), zero);
|
||||
deepStrictEqual(starknet.pedersen('0x0', '0x0'), zero);
|
||||
// >>> pedersen_hash(3618502788666131213697322783095070105623107215331596699973092056135872020475,3618502788666131213697322783095070105623107215331596699973092056135872020475)
|
||||
// 3226051580231087455100099637526672350308978851161639703631919449959447036451
|
||||
const big = 3618502788666131213697322783095070105623107215331596699973092056135872020475n;
|
||||
const bigExp = '0x721e167a36655994e88efa865e2ed8a0488d36db4d988fec043cda755728223';
|
||||
deepStrictEqual(starknet.pedersen(big, big), bigExp);
|
||||
// >= FIELD
|
||||
const big2 = 36185027886661312136973227830950701056231072153315966999730920561358720204751n;
|
||||
throws(() => starknet.pedersen(big2, big2), 'big2');
|
||||
|
||||
// FIELD -1
|
||||
const big3 = 3618502788666131213697322783095070105623107215331596699973092056135872020480n;
|
||||
const big3exp = '0x7258fccaf3371fad51b117471d9d888a1786c5694c3e6099160477b593a576e';
|
||||
deepStrictEqual(starknet.pedersen(big3, big3), big3exp, 'big3');
|
||||
// FIELD
|
||||
const big4 = 3618502788666131213697322783095070105623107215331596699973092056135872020481n;
|
||||
throws(() => starknet.pedersen(big4, big4), 'big4');
|
||||
throws(() => starknet.pedersen(-1, -1), 'neg');
|
||||
throws(() => starknet.pedersen(false, false), 'false');
|
||||
throws(() => starknet.pedersen(true, true), 'true');
|
||||
throws(() => starknet.pedersen(10.1, 10.1), 'float');
|
||||
});
|
||||
|
||||
should('hashChain edgecases', () => {
|
||||
deepStrictEqual(starknet.hashChain([32312321312321312312312321n]), '0x1aba6672c014b4838cc201');
|
||||
deepStrictEqual(
|
||||
starknet.hashChain([1n, 2n]),
|
||||
'0x5bb9440e27889a364bcb678b1f679ecd1347acdedcbf36e83494f857cc58026'
|
||||
);
|
||||
deepStrictEqual(
|
||||
starknet.hashChain([1, 2]),
|
||||
'0x5bb9440e27889a364bcb678b1f679ecd1347acdedcbf36e83494f857cc58026'
|
||||
);
|
||||
throws(() => starknet.hashChain([]));
|
||||
throws(() => starknet.hashChain('123'));
|
||||
deepStrictEqual(
|
||||
starknet.hashChain([1, 2]),
|
||||
'0x5bb9440e27889a364bcb678b1f679ecd1347acdedcbf36e83494f857cc58026'
|
||||
);
|
||||
});
|
||||
|
||||
should('Pedersen hash, issue #2', () => {
|
||||
// Verified with starnet.js
|
||||
deepStrictEqual(
|
||||
starknet.computeHashOnElements(issue2),
|
||||
'0x22064462ea33a6ce5272a295e0f551c5da3834f80d8444e7a4df68190b1bc42'
|
||||
);
|
||||
deepStrictEqual(
|
||||
starknet.computeHashOnElements([]),
|
||||
'0x49ee3eba8c1600700ee1b87eb599f16716b0b1022947733551fde4050ca6804'
|
||||
);
|
||||
deepStrictEqual(
|
||||
starknet.computeHashOnElements([1]),
|
||||
'0x78d74f61aeaa8286418fd34b3a12a610445eba11d00ecc82ecac2542d55f7a4'
|
||||
);
|
||||
});
|
||||
|
||||
import * as bip32 from '@scure/bip32';
|
||||
import * as bip39 from '@scure/bip39';
|
||||
|
||||
should('Seed derivation (example)', () => {
|
||||
const layer = 'starkex';
|
||||
const application = 'starkdeployement';
|
||||
const mnemonic =
|
||||
'range mountain blast problem vibrant void vivid doctor cluster enough melody ' +
|
||||
'salt layer language laptop boat major space monkey unit glimpse pause change vibrant';
|
||||
const ethAddress = '0xa4864d977b944315389d1765ffa7e66F74ee8cd7';
|
||||
const hdKey = bip32.HDKey.fromMasterSeed(bip39.mnemonicToSeedSync(mnemonic)).derive(
|
||||
starknet.getAccountPath(layer, application, ethAddress, 0)
|
||||
);
|
||||
deepStrictEqual(
|
||||
starknet.grindKey(hdKey.privateKey),
|
||||
'6cf0a8bf113352eb863157a45c5e5567abb34f8d32cddafd2c22aa803f4892c'
|
||||
);
|
||||
});
|
||||
|
||||
should('Compressed keys', () => {
|
||||
const G = starknet.Point.BASE;
|
||||
const half = starknet.CURVE.n / 2n;
|
||||
const last = starknet.CURVE.n;
|
||||
const vectors = [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
half - 5n,
|
||||
half - 4n,
|
||||
half - 3n,
|
||||
half - 2n,
|
||||
half - 1n,
|
||||
half,
|
||||
half + 1n,
|
||||
half + 2n,
|
||||
half + 3n,
|
||||
half + 4n,
|
||||
half + 5n,
|
||||
last - 5n,
|
||||
last - 4n,
|
||||
last - 3n,
|
||||
last - 2n,
|
||||
last - 1n,
|
||||
].map((i) => G.multiply(i));
|
||||
const fixPoint = (pt) => ({ ...pt, _WINDOW_SIZE: undefined });
|
||||
for (const v of vectors) {
|
||||
const uncompressed = v.toHex();
|
||||
const compressed = v.toHex(true);
|
||||
const exp = fixPoint(v);
|
||||
deepStrictEqual(fixPoint(starknet.Point.fromHex(uncompressed)), exp);
|
||||
deepStrictEqual(fixPoint(starknet.Point.fromHex(compressed)), exp);
|
||||
deepStrictEqual(starknet.Point.fromHex(compressed).toHex(), uncompressed);
|
||||
}
|
||||
});
|
||||
|
||||
// ESM is broken.
|
||||
import url from 'url';
|
||||
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
|
||||
should.run();
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
import * as microStark from '../../../lib/starknet.js';
|
||||
import * as starkwareCrypto from '@starkware-industries/starkware-crypto-utils';
|
||||
import * as bench from 'micro-bmark';
|
||||
const { run, mark } = bench; // or bench.mark
|
||||
|
||||
const privateKey = '2dccce1da22003777062ee0870e9881b460a8b7eca276870f57c601f182136c';
|
||||
const msgHash = 'c465dd6b1bbffdb05442eb17f5ca38ad1aa78a6f56bf4415bdee219114a47';
|
||||
const keyPair = starkwareCrypto.default.ec.keyFromPrivate(privateKey, 'hex');
|
||||
const publicKeyStark = starkwareCrypto.default.ec.keyFromPublic(
|
||||
keyPair.getPublic(true, 'hex'),
|
||||
'hex'
|
||||
);
|
||||
const publicKeyMicro = microStark.getPublicKey(privateKey);
|
||||
|
||||
const FNS = {
|
||||
pedersenHash: {
|
||||
samples: 250,
|
||||
starkware: () =>
|
||||
starkwareCrypto.default.pedersen([
|
||||
'3d937c035c878245caf64531a5756109c53068da139362728feb561405371cb',
|
||||
'208a0a10250e382e1e4bbe2880906c2791bf6275695e02fbbc6aeff9cd8b31a',
|
||||
]),
|
||||
'micro-starknet': () =>
|
||||
microStark.pedersen(
|
||||
'3d937c035c878245caf64531a5756109c53068da139362728feb561405371cb',
|
||||
'208a0a10250e382e1e4bbe2880906c2791bf6275695e02fbbc6aeff9cd8b31a'
|
||||
),
|
||||
},
|
||||
signVerify: {
|
||||
samples: 500,
|
||||
starkware: () =>
|
||||
starkwareCrypto.default.verify(
|
||||
publicKeyStark,
|
||||
msgHash,
|
||||
starkwareCrypto.default.sign(keyPair, msgHash)
|
||||
),
|
||||
'micro-starknet': () =>
|
||||
microStark.verify(microStark.sign(msgHash, privateKey), msgHash, publicKeyMicro),
|
||||
},
|
||||
};
|
||||
|
||||
const main = () =>
|
||||
run(async () => {
|
||||
for (let [k, libs] of Object.entries(FNS)) {
|
||||
console.log(`==== ${k} ====`);
|
||||
for (const [lib, fn] of Object.entries(libs)) {
|
||||
if (lib === 'samples') continue;
|
||||
let title = `${k} (${lib})`;
|
||||
await mark(title, libs.samples, () => fn());
|
||||
}
|
||||
console.log();
|
||||
}
|
||||
// Log current RAM
|
||||
bench.logMem();
|
||||
});
|
||||
|
||||
main();
|
@ -1,19 +0,0 @@
|
||||
{
|
||||
"name": "benchmark",
|
||||
"private": true,
|
||||
"version": "0.1.0",
|
||||
"description": "benchmarks",
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"bench": "node index.js"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@starkware-industries/starkware-crypto-utils": "^0.0.2",
|
||||
"micro-bmark": "0.2.0",
|
||||
"micro-should": "0.2.0"
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,32 +0,0 @@
|
||||
{
|
||||
"0x1": "0x1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca",
|
||||
"0x2": "0x759ca09377679ecd535a81e83039658bf40959283187c654c5416f439403cf5",
|
||||
"0x3": "0x411494b501a98abd8262b0da1351e17899a0c4ef23dd2f96fec5ba847310b20",
|
||||
"0x4": "0xa7da05a4d664859ccd6e567b935cdfbfe3018c7771cb980892ef38878ae9bc",
|
||||
"0x5": "0x788435d61046d3eec54d77d25bd194525f4fa26ebe6575536bc6f656656b74c",
|
||||
"0x6": "0x1efc3d7c9649900fcbd03f578a8248d095bc4b6a13b3c25f9886ef971ff96fa",
|
||||
"0x7": "0x743829e0a179f8afe223fc8112dfc8d024ab6b235fd42283c4f5970259ce7b7",
|
||||
"0x8": "0x6eeee2b0c71d681692559735e08a2c3ba04e7347c0c18d4d49b83bb89771591",
|
||||
"0x9": "0x216b4f076ff47e03a05032d1c6ee17933d8de8b2b4c43eb5ad5a7e1b25d3849",
|
||||
"0x800000000000000000000000000000000000000000000000000000000000000": "0x5c79074e7f7b834c12c81a9bb0d46691a5e7517767a849d9d98cb84e2176ed2",
|
||||
"0x800000000000000000000000000000000000000000000000000000000000001": "0x1c4f24e3bd16db0e2457bc005a9d61965105a535554c6b338871e34cb8e2d3a",
|
||||
"0x800000000000000000000000000000000000000000000000000000000000002": "0xdfbb89b39288a9ddacf3942b4481b04d4fa2f8ed3c424757981cc6357f27ac",
|
||||
"0x800000000000000000000000000000000000000000000000000000000000003": "0x41bef28265fd750b102f4f2d1e0231de7f4a33900a214f191a63d4fec4e72f4",
|
||||
"0x800000000000000000000000000000000000000000000000000000000000004": "0x24de66eb164797d4b414e81ded0cfa1a592ef0a9363ebbcb440d4d03cb18af1",
|
||||
"0x800000000000000000000000000000000000000000000000000000000000005": "0x5efb18c3bc9b69003746acc85fb6ee0cfbdc6adfb982f089cc63e1e5495daad",
|
||||
"0x800000000000000000000000000000000000000000000000000000000000006": "0x10dc71f00918a8ebfe4085c834d41dd22b251b9f81eef8b9a4fab77e7e1afe9",
|
||||
"0x800000000000000000000000000000000000000000000000000000000000007": "0x4267ebfd379b1c8caae73febc5920b0c95bd6f9f3536f47c5ddad1259c332ff",
|
||||
"0x800000000000000000000000000000000000000000000000000000000000008": "0x6da515118c8e01fd5b2e96b814ee95bad7d60be4d2ba6b47e0d283f579d9671",
|
||||
"0x800000000000000000000000000000000000000000000000000000000000009": "0x7a5b4797f4e56ed1473876bc2693fbe3f2fef7e050717cbae924ff23d426052",
|
||||
"0x2e9c99d8382fa004dcbbee720aef8a97002de0e991f6a8344e6dc636a71b59e": "0x1ff6803ae740e7e596504ac5c6afbea472e53679361e214f12be0155b13e25d",
|
||||
"0x8620458785138df8722214e073a91b8f55076ea78197cf41007692dd27fd90": "0x5967da40b90d7ca1e36dc4024381d7d4b403c6ac1a0ab358b0743984934a805",
|
||||
"0x1b920e7dfb49ba5ada673882af5342e7448d3e9335e0ac37feb6280cd7289ce": "0x78c7ab46333968fbde3201cf512c1eeb5529360259072c459a158dee4449b57",
|
||||
"0x704170dbfd5dc63caef69d2ce6dfc2b2dbb2af6e75851242bbe79fb6e62a118": "0x534bd8d6ebe4bb2f6992e2d7c19ef3146247e10c2849f357e44eddd283b2af6",
|
||||
"0x4b58bf4228f39550eca59b5c96a0cb606036cc9495eef9a546f24f01b1b7829": "0x1097a8c5a46d94596f1c8e70ca66941f2bb11e3c8d4fd58fdc4589f09965be8",
|
||||
"0x2e93226c90fb7a2381a24e940a94b98433e3553dcbf745d3f54d62963c75604": "0x369f0e8c8e984f244290267393a004dba435a4df091767ad5063fece7b1884c",
|
||||
"0x4615f94598cd756ad1a551d7e57fd725916adfd0054eb773ceb482eef87d0b2": "0x1ee5b8d612102a2408cde59ce52a6498d2e38fe8789bb26d400dea310684ec9",
|
||||
"0x6ade54b7debd7ca1d4e8e932f9545f8fa4024d73be1efcc86df86367fc333f8": "0x37de3bf52412b2fb9b0030d232ca9dd921cd8f71fd67975cdc62546826e121",
|
||||
"0x618e7467dd24c2a3449c4df640439c12cdd0f8ea779afcee6e252b2cf494354": "0x71c2b578c432f2d305d3808bb645ecc46dd670cb43d4f4a076f75ccbff74fbc",
|
||||
"0x7eae185e1f41ec76d214d763f0592f194933622a9dd5f3d52d0209f71619c1a": "0x2b0160052e70176e5b0ff2a6eff90896ae07b732fc27219e36e077735abd57e",
|
||||
"0x178047D3869489C055D7EA54C014FFB834A069C9595186ABE04EA4D1223A03F": "0x1895a6a77ae14e7987b9cb51329a5adfb17bd8e7c638f92d6892d76e51cebcf"
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
{
|
||||
"private_key": "0x3c1e9550e66958296d11b60f8e8e7a7ad990d07fa65d5f7652c4a6c87d4e3cc",
|
||||
"messages": [
|
||||
{
|
||||
"hash": "0x1",
|
||||
"r": "3162358736122783857144396205516927012128897537504463716197279730251407200037",
|
||||
"s": "1447067116407676619871126378936374427636662490882969509559888874644844560850"
|
||||
},
|
||||
{
|
||||
"hash": "0x11",
|
||||
"r": "2282960348362869237018441985726545922711140064809058182483721438101695251648",
|
||||
"s": "2905868291002627709651322791912000820756370440695830310841564989426104902684"
|
||||
},
|
||||
|
||||
{
|
||||
"hash": "0x223",
|
||||
"r": "2851492577225522862152785068304516872062840835882746625971400995051610132955",
|
||||
"s": "2227464623243182122770469099770977514100002325017609907274766387592987135410"
|
||||
},
|
||||
|
||||
{
|
||||
"hash": "0x9999",
|
||||
"r": "3551214266795401081823453828727326248401688527835302880992409448142527576296",
|
||||
"s": "2580950807716503852408066180369610390914312729170066679103651110985466032285"
|
||||
},
|
||||
|
||||
{
|
||||
"hash": "0x387e76d1667c4454bfb835144120583af836f8e32a516765497d23eabe16b3f",
|
||||
"r": "3518448914047769356425227827389998721396724764083236823647519654917215164512",
|
||||
"s": "3042321032945513635364267149196358883053166552342928199041742035443537684462"
|
||||
},
|
||||
|
||||
{
|
||||
"hash": "0x3a7e76d1697c4455bfb835144120283af236f8e32a516765497d23eabe16b2",
|
||||
"r": "2261926635950780594216378185339927576862772034098248230433352748057295357217",
|
||||
"s": "2708700003762962638306717009307430364534544393269844487939098184375356178572"
|
||||
},
|
||||
|
||||
{
|
||||
"hash": "0xfa5f0cd1ebff93c9e6474379a213ba111f9e42f2f1cb361b0327e0737203",
|
||||
"r": "3016953906936760149710218073693613509330129567629289734816320774638425763370",
|
||||
"s": "306146275372136078470081798635201810092238376869367156373203048583896337506"
|
||||
},
|
||||
|
||||
{
|
||||
"hash": "0x4c1e9550e66958296d11b60f8e8e7f7ae99dd0cfa6bd5fa652c1a6c87d4e2cc",
|
||||
"r": "3562728603055564208884290243634917206833465920158600288670177317979301056463",
|
||||
"s": "1958799632261808501999574190111106370256896588537275453140683641951899459876"
|
||||
},
|
||||
|
||||
{
|
||||
"hash": "0x6362b40c218fb4c8a8bd42ca482145e8513b78e00faa0de76a98ba14fc37ae8",
|
||||
"r": "3485557127492692423490706790022678621438670833185864153640824729109010175518",
|
||||
"s": "897592218067946175671768586886915961592526001156186496738437723857225288280"
|
||||
}
|
||||
]
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
|
||||
import './basic.test.js';
|
||||
import './starknet.test.js';
|
||||
import './property.test.js';
|
||||
|
@ -1,51 +0,0 @@
|
||||
import { deepStrictEqual, throws } from 'assert';
|
||||
import { should } from 'micro-should';
|
||||
import * as starknet from '../../lib/starknet.js';
|
||||
import * as fc from 'fast-check';
|
||||
|
||||
const FC_BIGINT = fc.bigInt(1n + 1n, starknet.CURVE.n - 1n);
|
||||
|
||||
should('Point#toHex() roundtrip', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (x) => {
|
||||
const point1 = starknet.Point.fromPrivateKey(x);
|
||||
const hex = point1.toHex(true);
|
||||
deepStrictEqual(starknet.Point.fromHex(hex).toHex(true), hex);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
should('Signature.fromCompactHex() roundtrip', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, (r, s) => {
|
||||
const sig = new starknet.Signature(r, s);
|
||||
deepStrictEqual(starknet.Signature.fromCompact(sig.toCompactHex()), sig);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
should('Signature.fromDERHex() roundtrip', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, (r, s) => {
|
||||
const sig = new starknet.Signature(r, s);
|
||||
deepStrictEqual(starknet.Signature.fromDER(sig.toDERHex()), sig);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
should('verify()/should verify random signatures', () =>
|
||||
fc.assert(
|
||||
fc.asyncProperty(FC_BIGINT, fc.hexaString({ minLength: 64, maxLength: 64 }), (privNum, msg) => {
|
||||
const privKey = privNum.toString(16).padStart(64, '0');
|
||||
const pub = starknet.getPublicKey(privKey);
|
||||
const sig = starknet.sign(msg, privKey);
|
||||
deepStrictEqual(starknet.verify(sig, msg, pub), true);
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
// ESM is broken.
|
||||
import url from 'url';
|
||||
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
|
||||
should.run();
|
||||
}
|
@ -1,286 +0,0 @@
|
||||
import { deepStrictEqual, throws } from 'assert';
|
||||
import { should } from 'micro-should';
|
||||
import { hex, utf8 } from '@scure/base';
|
||||
import * as bip32 from '@scure/bip32';
|
||||
import * as bip39 from '@scure/bip39';
|
||||
import * as starknet from '../../lib/starknet.js';
|
||||
import { default as sigVec } from './fixtures/rfc6979_signature_test_vector.json' assert { type: 'json' };
|
||||
import { default as precomputedKeys } from './fixtures/keys_precomputed.json' assert { type: 'json' };
|
||||
|
||||
should('Starknet keccak', () => {
|
||||
const value = starknet.keccak(utf8.decode('hello'));
|
||||
deepStrictEqual(value, 0x8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8n);
|
||||
deepStrictEqual(value < 2n ** 250n, true);
|
||||
});
|
||||
|
||||
should('RFC6979', () => {
|
||||
for (const msg of sigVec.messages) {
|
||||
const { r, s } = starknet.sign(msg.hash, sigVec.private_key);
|
||||
// const { r, s } = starknet.Signature.fromDER(sig);
|
||||
deepStrictEqual(r.toString(10), msg.r);
|
||||
deepStrictEqual(s.toString(10), msg.s);
|
||||
}
|
||||
});
|
||||
|
||||
should('Signatures', () => {
|
||||
const vectors = [
|
||||
{
|
||||
// Message hash of length 61.
|
||||
msg: 'c465dd6b1bbffdb05442eb17f5ca38ad1aa78a6f56bf4415bdee219114a47',
|
||||
r: '5f496f6f210b5810b2711c74c15c05244dad43d18ecbbdbe6ed55584bc3b0a2',
|
||||
s: '4e8657b153787f741a67c0666bad6426c3741b478c8eaa3155196fc571416f3',
|
||||
},
|
||||
{
|
||||
// Message hash of length 61, with leading zeros.
|
||||
msg: '00c465dd6b1bbffdb05442eb17f5ca38ad1aa78a6f56bf4415bdee219114a47',
|
||||
r: '5f496f6f210b5810b2711c74c15c05244dad43d18ecbbdbe6ed55584bc3b0a2',
|
||||
s: '4e8657b153787f741a67c0666bad6426c3741b478c8eaa3155196fc571416f3',
|
||||
},
|
||||
{
|
||||
// Message hash of length 62.
|
||||
msg: 'c465dd6b1bbffdb05442eb17f5ca38ad1aa78a6f56bf4415bdee219114a47a',
|
||||
r: '233b88c4578f0807b4a7480c8076eca5cfefa29980dd8e2af3c46a253490e9c',
|
||||
s: '28b055e825bc507349edfb944740a35c6f22d377443c34742c04e0d82278cf1',
|
||||
},
|
||||
{
|
||||
// Message hash of length 63.
|
||||
msg: '7465dd6b1bbffdb05442eb17f5ca38ad1aa78a6f56bf4415bdee219114a47a1',
|
||||
r: 'b6bee8010f96a723f6de06b5fa06e820418712439c93850dd4e9bde43ddf',
|
||||
s: '1a3d2bc954ed77e22986f507d68d18115fa543d1901f5b4620db98e2f6efd80',
|
||||
},
|
||||
];
|
||||
const privateKey = '2dccce1da22003777062ee0870e9881b460a8b7eca276870f57c601f182136c';
|
||||
const publicKey = starknet.getPublicKey(privateKey);
|
||||
for (const v of vectors) {
|
||||
const sig = starknet.sign(v.msg, privateKey);
|
||||
const { r, s } = sig;
|
||||
// const { r, s } = starknet.Signature.fromDER(sig);
|
||||
deepStrictEqual(r.toString(16), v.r, 'r equality');
|
||||
deepStrictEqual(s.toString(16), v.s, 's equality');
|
||||
deepStrictEqual(starknet.verify(sig, v.msg, publicKey), true, 'verify');
|
||||
}
|
||||
});
|
||||
|
||||
should('Invalid signatures', () => {
|
||||
/*
|
||||
|
||||
it('should not verify invalid signature inputs lengths', () => {
|
||||
const ecOrder = starkwareCrypto.ec.n;
|
||||
const {maxEcdsaVal} = starkwareCrypto;
|
||||
const maxMsgHash = maxEcdsaVal.sub(oneBn);
|
||||
const maxR = maxEcdsaVal.sub(oneBn);
|
||||
const maxS = ecOrder.sub(oneBn).sub(oneBn);
|
||||
const maxStarkKey = maxEcdsaVal.sub(oneBn);
|
||||
|
||||
// Test invalid message length.
|
||||
expect(() =>
|
||||
starkwareCrypto.verify(maxStarkKey, maxMsgHash.add(oneBn).toString(16), {
|
||||
r: maxR,
|
||||
s: maxS
|
||||
})
|
||||
).to.throw('Message not signable, invalid msgHash length.');
|
||||
// Test invalid r length.
|
||||
expect(() =>
|
||||
starkwareCrypto.verify(maxStarkKey, maxMsgHash.toString(16), {
|
||||
r: maxR.add(oneBn),
|
||||
s: maxS
|
||||
})
|
||||
).to.throw('Message not signable, invalid r length.');
|
||||
// Test invalid w length.
|
||||
expect(() =>
|
||||
starkwareCrypto.verify(maxStarkKey, maxMsgHash.toString(16), {
|
||||
r: maxR,
|
||||
s: maxS.add(oneBn)
|
||||
})
|
||||
).to.throw('Message not signable, invalid w length.');
|
||||
// Test invalid s length.
|
||||
expect(() =>
|
||||
starkwareCrypto.verify(maxStarkKey, maxMsgHash.toString(16), {
|
||||
r: maxR,
|
||||
s: maxS.add(oneBn).add(oneBn)
|
||||
})
|
||||
).to.throw('Message not signable, invalid s length.');
|
||||
});
|
||||
|
||||
it('should not verify invalid signatures', () => {
|
||||
const privKey = generateRandomStarkPrivateKey();
|
||||
const keyPair = starkwareCrypto.ec.keyFromPrivate(privKey, 'hex');
|
||||
const keyPairPub = starkwareCrypto.ec.keyFromPublic(
|
||||
keyPair.getPublic(),
|
||||
'BN'
|
||||
);
|
||||
const msgHash = new BN(randomHexString(61));
|
||||
const msgSignature = starkwareCrypto.sign(keyPair, msgHash);
|
||||
|
||||
// Test invalid public key.
|
||||
const invalidKeyPairPub = starkwareCrypto.ec.keyFromPublic(
|
||||
{x: keyPairPub.pub.getX().add(oneBn), y: keyPairPub.pub.getY()},
|
||||
'BN'
|
||||
);
|
||||
expect(
|
||||
starkwareCrypto.verify(
|
||||
invalidKeyPairPub,
|
||||
msgHash.toString(16),
|
||||
msgSignature
|
||||
)
|
||||
).to.be.false;
|
||||
// Test invalid message.
|
||||
expect(
|
||||
starkwareCrypto.verify(
|
||||
keyPair,
|
||||
msgHash.add(oneBn).toString(16),
|
||||
msgSignature
|
||||
)
|
||||
).to.be.false;
|
||||
expect(
|
||||
starkwareCrypto.verify(
|
||||
keyPairPub,
|
||||
msgHash.add(oneBn).toString(16),
|
||||
msgSignature
|
||||
)
|
||||
).to.be.false;
|
||||
// Test invalid r.
|
||||
msgSignature.r.iadd(oneBn);
|
||||
expect(starkwareCrypto.verify(keyPair, msgHash.toString(16), msgSignature))
|
||||
.to.be.false;
|
||||
expect(
|
||||
starkwareCrypto.verify(keyPairPub, msgHash.toString(16), msgSignature)
|
||||
).to.be.false;
|
||||
// Test invalid s.
|
||||
msgSignature.r.isub(oneBn);
|
||||
msgSignature.s.iadd(oneBn);
|
||||
expect(starkwareCrypto.verify(keyPair, msgHash.toString(16), msgSignature))
|
||||
.to.be.false;
|
||||
expect(
|
||||
starkwareCrypto.verify(keyPairPub, msgHash.toString(16), msgSignature)
|
||||
).to.be.false;
|
||||
});
|
||||
});
|
||||
*/
|
||||
});
|
||||
|
||||
should('Pedersen', () => {
|
||||
deepStrictEqual(
|
||||
starknet.pedersen(
|
||||
'0x3d937c035c878245caf64531a5756109c53068da139362728feb561405371cb',
|
||||
'0x208a0a10250e382e1e4bbe2880906c2791bf6275695e02fbbc6aeff9cd8b31a'
|
||||
),
|
||||
'0x30e480bed5fe53fa909cc0f8c4d99b8f9f2c016be4c41e13a4848797979c662'
|
||||
);
|
||||
deepStrictEqual(
|
||||
starknet.pedersen(
|
||||
'0x58f580910a6ca59b28927c08fe6c43e2e303ca384badc365795fc645d479d45',
|
||||
'0x78734f65a067be9bdb39de18434d71e79f7b6466a4b66bbd979ab9e7515fe0b'
|
||||
),
|
||||
'0x68cc0b76cddd1dd4ed2301ada9b7c872b23875d5ff837b3a87993e0d9996b87'
|
||||
);
|
||||
});
|
||||
|
||||
should('Hash chain', () => {
|
||||
deepStrictEqual(starknet.hashChain([1, 2, 3]), starknet.pedersen(1, starknet.pedersen(2, 3)));
|
||||
});
|
||||
|
||||
should('Key grinding', () => {
|
||||
deepStrictEqual(
|
||||
starknet.grindKey('86F3E7293141F20A8BAFF320E8EE4ACCB9D4A4BF2B4D295E8CEE784DB46E0519'),
|
||||
'5c8c8683596c732541a59e03007b2d30dbbbb873556fe65b5fb63c16688f941'
|
||||
);
|
||||
// Loops more than once (verified manually)
|
||||
deepStrictEqual(
|
||||
starknet.grindKey('94F3E7293141F20A8BAFF320E8EE4ACCB9D4A4BF2B4D295E8CEE784DB46E0595'),
|
||||
'33880b9aba464c1c01c9f8f5b4fc1134698f9b0a8d18505cab6cdd34d93dc02'
|
||||
);
|
||||
});
|
||||
|
||||
should('Private to stark key', () => {
|
||||
deepStrictEqual(
|
||||
starknet.getStarkKey('0x178047D3869489C055D7EA54C014FFB834A069C9595186ABE04EA4D1223A03F'),
|
||||
'0x1895a6a77ae14e7987b9cb51329a5adfb17bd8e7c638f92d6892d76e51cebcf'
|
||||
);
|
||||
for (const [privKey, expectedPubKey] of Object.entries(precomputedKeys)) {
|
||||
deepStrictEqual(starknet.getStarkKey(privKey), expectedPubKey);
|
||||
}
|
||||
});
|
||||
|
||||
should('Private stark key from eth signature', () => {
|
||||
const ethSignature =
|
||||
'0x21fbf0696d5e0aa2ef41a2b4ffb623bcaf070461d61cf7251c74161f82fec3a43' +
|
||||
'70854bc0a34b3ab487c1bc021cd318c734c51ae29374f2beb0e6f2dd49b4bf41c';
|
||||
deepStrictEqual(
|
||||
starknet.ethSigToPrivate(ethSignature),
|
||||
'766f11e90cd7c7b43085b56da35c781f8c067ac0d578eabdceebc4886435bda'
|
||||
);
|
||||
});
|
||||
|
||||
should('Key derivation', () => {
|
||||
const layer = 'starkex';
|
||||
const application = 'starkdeployement';
|
||||
const mnemonic =
|
||||
'range mountain blast problem vibrant void vivid doctor cluster enough melody ' +
|
||||
'salt layer language laptop boat major space monkey unit glimpse pause change vibrant';
|
||||
const ethAddress = '0xa4864d977b944315389d1765ffa7e66F74ee8cd7';
|
||||
const VECTORS = [
|
||||
{
|
||||
index: 0,
|
||||
path: "m/2645'/579218131'/891216374'/1961790679'/2135936222'/0",
|
||||
privateKey: '6cf0a8bf113352eb863157a45c5e5567abb34f8d32cddafd2c22aa803f4892c',
|
||||
},
|
||||
{
|
||||
index: 7,
|
||||
path: "m/2645'/579218131'/891216374'/1961790679'/2135936222'/7",
|
||||
privateKey: '341751bdc42841da35ab74d13a1372c1f0250617e8a2ef96034d9f46e6847af',
|
||||
},
|
||||
{
|
||||
index: 598,
|
||||
path: "m/2645'/579218131'/891216374'/1961790679'/2135936222'/598",
|
||||
privateKey: '41a4d591a868353d28b7947eb132aa4d00c4a022743689ffd20a3628d6ca28c',
|
||||
},
|
||||
];
|
||||
const hd = bip32.HDKey.fromMasterSeed(bip39.mnemonicToSeedSync(mnemonic));
|
||||
for (const { index, path, privateKey } of VECTORS) {
|
||||
const realPath = starknet.getAccountPath(layer, application, ethAddress, index);
|
||||
deepStrictEqual(realPath, path);
|
||||
deepStrictEqual(starknet.grindKey(hd.derive(realPath).privateKey), privateKey);
|
||||
}
|
||||
});
|
||||
|
||||
// Verified against starknet.js
|
||||
should('Starknet.js cross-tests', () => {
|
||||
const privateKey = '0x019800ea6a9a73f94aee6a3d2edf018fc770443e90c7ba121e8303ec6b349279';
|
||||
// NOTE: there is no compressed keys here, getPubKey returns stark-key (which is schnorr-like X coordinate)
|
||||
// But it is not used in signing/verifying
|
||||
deepStrictEqual(
|
||||
starknet.getStarkKey(privateKey),
|
||||
'0x33f45f07e1bd1a51b45fc24ec8c8c9908db9e42191be9e169bfcac0c0d99745'
|
||||
);
|
||||
const msgHash = '0x6d1706bd3d1ba7c517be2a2a335996f63d4738e2f182144d078a1dd9997062e';
|
||||
const sig = starknet.sign(msgHash, privateKey);
|
||||
const { r, s } = (sig);
|
||||
|
||||
deepStrictEqual(
|
||||
r.toString(),
|
||||
'1427981024487605678086498726488552139932400435436186597196374630267616399345'
|
||||
);
|
||||
deepStrictEqual(
|
||||
s.toString(),
|
||||
'1853664302719670721837677288395394946745467311923401353018029119631574115563'
|
||||
);
|
||||
const hashMsg2 = starknet.pedersen(
|
||||
'0x33f45f07e1bd1a51b45fc24ec8c8c9908db9e42191be9e169bfcac0c0d99745',
|
||||
'1'
|
||||
);
|
||||
deepStrictEqual(hashMsg2, '0x2b0d4d43acce8ff68416f667f92ec7eab2b96f1d2224abd4d9d4d1e7fa4bb00');
|
||||
const pubKey =
|
||||
'04033f45f07e1bd1a51b45fc24ec8c8c9908db9e42191be9e169bfcac0c0d997450319d0f53f6ca077c4fa5207819144a2a4165daef6ee47a7c1d06c0dcaa3e456';
|
||||
const sig2 = new starknet.Signature(
|
||||
558858382392827003930138586379728730695763862039474863361948210004201119180n,
|
||||
2440689354481625417078677634625227600823892606910345662891037256374285369343n
|
||||
);
|
||||
deepStrictEqual(starknet.verify(sig2.toDERHex(), hashMsg2, pubKey), true);
|
||||
});
|
||||
|
||||
// ESM is broken.
|
||||
import url from 'url';
|
||||
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
|
||||
should.run();
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,25 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"target": "es2020",
|
||||
"lib": [
|
||||
"es2020",
|
||||
"dom"
|
||||
],
|
||||
"module": "es6",
|
||||
"moduleResolution": "node16",
|
||||
"outDir": "lib",
|
||||
"noImplicitAny": true,
|
||||
"preserveConstEnums": true,
|
||||
"baseUrl": ".",
|
||||
},
|
||||
"include": [
|
||||
"src",
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"lib"
|
||||
]
|
||||
}
|
4
esm/package.json
Normal file
4
esm/package.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"type": "module",
|
||||
"sideEffects": false
|
||||
}
|
1
index.js
1
index.js
@ -1 +0,0 @@
|
||||
throw new Error('Incorrect usage. Import submodules instead');
|
119
package-lock.json
generated
Normal file
119
package-lock.json
generated
Normal file
@ -0,0 +1,119 @@
|
||||
{
|
||||
"name": "@tornado/noble-curves",
|
||||
"version": "1.4.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@tornado/noble-curves",
|
||||
"version": "1.4.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@noble/hashes": "1.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@paulmillr/jsbt": "0.1.0",
|
||||
"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/@noble/hashes": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz",
|
||||
"integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==",
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@paulmillr/jsbt": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@paulmillr/jsbt/-/jsbt-0.1.0.tgz",
|
||||
"integrity": "sha512-TdowoHD36hkZARv6LW4jenkVTdK2vP0sy4ZM8E9MxaqAAIRdwmn3RlB+zWkEHi4hKTgLqMGkURfNkFtt0STX2Q==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"jsbt": "jsbt.js"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-check": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.0.0.tgz",
|
||||
"integrity": "sha512-uujtrFJEQQqnIMO52ARwzPcuV4omiL1OJBUBLE9WnNFeu0A97sREXDOmCIHY+Z6KLVcemUf09rWr0q0Xy/Y/Ew==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"pure-rand": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/fast-check"
|
||||
}
|
||||
},
|
||||
"node_modules/micro-bmark": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/micro-bmark/-/micro-bmark-0.3.1.tgz",
|
||||
"integrity": "sha512-bNaKObD4yPAAPrpEqp5jO6LJ2sEFgLoFSmRjEY809mJ62+2AehI/K3+RlVpN3Oo92RHpgC2RQhj6b1Tb4dmo+w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/micro-should": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/micro-should/-/micro-should-0.4.0.tgz",
|
||||
"integrity": "sha512-Vclj8yrngSYc9Y3dL2C+AdUlTkyx/syWc4R7LYfk4h7+icfF0DoUBGjjUIaEDzZA19RzoI+Hg8rW9IRoNGP0tQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.1.tgz",
|
||||
"integrity": "sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"prettier": "bin/prettier.cjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/pure-rand": {
|
||||
"version": "5.0.5",
|
||||
"resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-5.0.5.tgz",
|
||||
"integrity": "sha512-BwQpbqxSCBJVpamI6ydzcKqyFmnd5msMWUGvzXLm1aXvusbbgkbOto/EUPM00hjveJEaJtdbhUjKSzWRhQVkaw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/dubzzz"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/fast-check"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz",
|
||||
"integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
193
package.json
193
package.json
@ -1,75 +1,182 @@
|
||||
{
|
||||
"name": "@noble/curves",
|
||||
"version": "0.1.0",
|
||||
"description": "Minimal, zero-dependency JS implementation of elliptic curve cryptography",
|
||||
"name": "@tornado/noble-curves",
|
||||
"version": "1.4.0",
|
||||
"description": "Audited & minimal JS implementation of elliptic curve cryptography",
|
||||
"files": [
|
||||
"index.js",
|
||||
"lib",
|
||||
"lib/esm"
|
||||
"abstract",
|
||||
"esm",
|
||||
"src",
|
||||
"*.js",
|
||||
"*.js.map",
|
||||
"*.d.ts",
|
||||
"*.d.ts.map"
|
||||
],
|
||||
"scripts": {
|
||||
"bench": "node test/benchmark/index.js",
|
||||
"bench": "cd benchmark; node secp256k1.js; node curves.js; node ecdh.js; node hash-to-curve.js; node modular.js; node bls.js; node ristretto255.js; node decaf448.js",
|
||||
"build": "tsc && tsc -p tsconfig.esm.json",
|
||||
"build:release": "rollup -c rollup.config.js",
|
||||
"lint": "prettier --check 'src/**/*.{js,ts}' 'curve-definitions/src/**/*.{js,ts}'",
|
||||
"format": "prettier --write 'src/**/*.{js,ts}' 'curve-definitions/src/**/*.{js,ts}'",
|
||||
"test": "cd curve-definitions; node test/index.test.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'",
|
||||
"format": "prettier --write 'src/**/*.{js,ts}' 'test/*.js'",
|
||||
"test": "node test/index.test.js"
|
||||
},
|
||||
"author": "Paul Miller (https://paulmillr.com)",
|
||||
"homepage": "https://paulmillr.com/noble/",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/paulmillr/noble-curves.git"
|
||||
"url": "https://git.tornado.ws/tornado-packages/noble-curvest"
|
||||
},
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-node-resolve": "13.3.0",
|
||||
"micro-bmark": "0.2.0",
|
||||
"micro-should": "0.2.0",
|
||||
"prettier": "2.6.2",
|
||||
"rollup": "2.75.5",
|
||||
"typescript": "4.7.3"
|
||||
"dependencies": {
|
||||
"@noble/hashes": "1.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@paulmillr/jsbt": "0.1.0",
|
||||
"fast-check": "3.0.0",
|
||||
"micro-bmark": "0.3.1",
|
||||
"micro-should": "0.4.0",
|
||||
"prettier": "3.1.1",
|
||||
"typescript": "5.3.2"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"main": "index.js",
|
||||
"exports": {
|
||||
"./modular": {
|
||||
"types": "./lib/modular.d.ts",
|
||||
"import": "./lib/esm/modular.js",
|
||||
"default": "./lib/modular.js"
|
||||
".": {
|
||||
"types": "./index.d.ts",
|
||||
"import": "./esm/index.js",
|
||||
"default": "./index.js"
|
||||
},
|
||||
"./shortw": {
|
||||
"types": "./lib/shortw.d.ts",
|
||||
"import": "./lib/esm/shortw.js",
|
||||
"default": "./lib/shortw.js"
|
||||
"./abstract/edwards": {
|
||||
"types": "./abstract/edwards.d.ts",
|
||||
"import": "./esm/abstract/edwards.js",
|
||||
"default": "./abstract/edwards.js"
|
||||
},
|
||||
"./utils": {
|
||||
"types": "./lib/utils.d.ts",
|
||||
"import": "./lib/esm/utils.js",
|
||||
"default": "./lib/utils.js"
|
||||
"./abstract/modular": {
|
||||
"types": "./abstract/modular.d.ts",
|
||||
"import": "./esm/abstract/modular.js",
|
||||
"default": "./abstract/modular.js"
|
||||
},
|
||||
"./abstract/montgomery": {
|
||||
"types": "./abstract/montgomery.d.ts",
|
||||
"import": "./esm/abstract/montgomery.js",
|
||||
"default": "./abstract/montgomery.js"
|
||||
},
|
||||
"./abstract/weierstrass": {
|
||||
"types": "./abstract/weierstrass.d.ts",
|
||||
"import": "./esm/abstract/weierstrass.js",
|
||||
"default": "./abstract/weierstrass.js"
|
||||
},
|
||||
"./abstract/bls": {
|
||||
"types": "./abstract/bls.d.ts",
|
||||
"import": "./esm/abstract/bls.js",
|
||||
"default": "./abstract/bls.js"
|
||||
},
|
||||
"./abstract/hash-to-curve": {
|
||||
"types": "./abstract/hash-to-curve.d.ts",
|
||||
"import": "./esm/abstract/hash-to-curve.js",
|
||||
"default": "./abstract/hash-to-curve.js"
|
||||
},
|
||||
"./abstract/curve": {
|
||||
"types": "./abstract/curve.d.ts",
|
||||
"import": "./esm/abstract/curve.js",
|
||||
"default": "./abstract/curve.js"
|
||||
},
|
||||
"./abstract/utils": {
|
||||
"types": "./abstract/utils.d.ts",
|
||||
"import": "./esm/abstract/utils.js",
|
||||
"default": "./abstract/utils.js"
|
||||
},
|
||||
"./abstract/poseidon": {
|
||||
"types": "./abstract/poseidon.d.ts",
|
||||
"import": "./esm/abstract/poseidon.js",
|
||||
"default": "./abstract/poseidon.js"
|
||||
},
|
||||
"./_shortw_utils": {
|
||||
"types": "./_shortw_utils.d.ts",
|
||||
"import": "./esm/_shortw_utils.js",
|
||||
"default": "./_shortw_utils.js"
|
||||
},
|
||||
"./bls12-381": {
|
||||
"types": "./bls12-381.d.ts",
|
||||
"import": "./esm/bls12-381.js",
|
||||
"default": "./bls12-381.js"
|
||||
},
|
||||
"./bn254": {
|
||||
"types": "./bn254.d.ts",
|
||||
"import": "./esm/bn254.js",
|
||||
"default": "./bn254.js"
|
||||
},
|
||||
"./ed25519": {
|
||||
"types": "./ed25519.d.ts",
|
||||
"import": "./esm/ed25519.js",
|
||||
"default": "./ed25519.js"
|
||||
},
|
||||
"./ed448": {
|
||||
"types": "./ed448.d.ts",
|
||||
"import": "./esm/ed448.js",
|
||||
"default": "./ed448.js"
|
||||
},
|
||||
"./index": {
|
||||
"types": "./index.d.ts",
|
||||
"import": "./esm/index.js",
|
||||
"default": "./index.js"
|
||||
},
|
||||
"./jubjub": {
|
||||
"types": "./jubjub.d.ts",
|
||||
"import": "./esm/jubjub.js",
|
||||
"default": "./jubjub.js"
|
||||
},
|
||||
"./p256": {
|
||||
"types": "./p256.d.ts",
|
||||
"import": "./esm/p256.js",
|
||||
"default": "./p256.js"
|
||||
},
|
||||
"./p384": {
|
||||
"types": "./p384.d.ts",
|
||||
"import": "./esm/p384.js",
|
||||
"default": "./p384.js"
|
||||
},
|
||||
"./p521": {
|
||||
"types": "./p521.d.ts",
|
||||
"import": "./esm/p521.js",
|
||||
"default": "./p521.js"
|
||||
},
|
||||
"./pasta": {
|
||||
"types": "./pasta.d.ts",
|
||||
"import": "./esm/pasta.js",
|
||||
"default": "./pasta.js"
|
||||
},
|
||||
"./secp256k1": {
|
||||
"types": "./secp256k1.d.ts",
|
||||
"import": "./esm/secp256k1.js",
|
||||
"default": "./secp256k1.js"
|
||||
}
|
||||
},
|
||||
"keywords": [
|
||||
"elliptic",
|
||||
"curve",
|
||||
"cryptography",
|
||||
"hyperelliptic",
|
||||
"weierstrass",
|
||||
"montgomery",
|
||||
"edwards",
|
||||
"p256",
|
||||
"p384",
|
||||
"p521",
|
||||
"nist",
|
||||
"weierstrass",
|
||||
"edwards",
|
||||
"montgomery",
|
||||
"hashes",
|
||||
"secp256r1",
|
||||
"secp256k1",
|
||||
"ed25519",
|
||||
"ed448",
|
||||
"x25519",
|
||||
"ed25519",
|
||||
"bls12-381",
|
||||
"bn254",
|
||||
"pasta",
|
||||
"bls",
|
||||
"noble",
|
||||
"ecc",
|
||||
"ecdsa",
|
||||
"eddsa",
|
||||
"schnorr"
|
||||
],
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
]
|
||||
"funding": "https://paulmillr.com/funding/"
|
||||
}
|
||||
|
20
src/_shortw_utils.ts
Normal file
20
src/_shortw_utils.ts
Normal file
@ -0,0 +1,20 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
import { hmac } from '@noble/hashes/hmac';
|
||||
import { concatBytes, randomBytes } from '@noble/hashes/utils';
|
||||
import { weierstrass, CurveType } from './abstract/weierstrass.js';
|
||||
import { CHash } from './abstract/utils.js';
|
||||
|
||||
// connects noble-curves to noble-hashes
|
||||
export function getHash(hash: CHash) {
|
||||
return {
|
||||
hash,
|
||||
hmac: (key: Uint8Array, ...msgs: Uint8Array[]) => hmac(hash, key, concatBytes(...msgs)),
|
||||
randomBytes,
|
||||
};
|
||||
}
|
||||
// Same API as @noble/hashes, with ability to create curve with custom hash
|
||||
type CurveDef = Readonly<Omit<CurveType, 'hash' | 'hmac' | 'randomBytes'>>;
|
||||
export function createCurve(curveDef: CurveDef, defHash: CHash) {
|
||||
const create = (hash: CHash) => weierstrass({ ...curveDef, ...getHash(hash) });
|
||||
return Object.freeze({ ...create(defHash), create });
|
||||
}
|
496
src/abstract/bls.ts
Normal file
496
src/abstract/bls.ts
Normal file
@ -0,0 +1,496 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
/**
|
||||
* BLS (Barreto-Lynn-Scott) family of pairing-friendly curves.
|
||||
* Implements BLS (Boneh-Lynn-Shacham) signatures.
|
||||
* Consists of two curves: G1 and G2:
|
||||
* - G1 is a subgroup of (x, y) E(Fq) over y² = x³ + 4.
|
||||
* - G2 is a subgroup of ((x₁, x₂+i), (y₁, y₂+i)) E(Fq²) over y² = x³ + 4(1 + i) where i is √-1
|
||||
* - Gt, created by bilinear (ate) pairing e(G1, G2), consists of p-th roots of unity in
|
||||
* Fq^k where k is embedding degree. Only degree 12 is currently supported, 24 is not.
|
||||
* Pairing is used to aggregate and verify signatures.
|
||||
* We are using Fp for private keys (shorter) and Fp₂ for signatures (longer).
|
||||
* Some projects may prefer to swap this relation, it is not supported for now.
|
||||
*/
|
||||
import { AffinePoint } from './curve.js';
|
||||
import { IField, getMinHashLength, mapHashToField } from './modular.js';
|
||||
import { Hex, PrivKey, CHash, bitLen, bitGet, ensureBytes } from './utils.js';
|
||||
// prettier-ignore
|
||||
import {
|
||||
MapToCurve, Opts as HTFOpts, H2CPointConstructor, htfBasicOpts,
|
||||
createHasher
|
||||
} from './hash-to-curve.js';
|
||||
import {
|
||||
CurvePointsType,
|
||||
ProjPointType as ProjPointType,
|
||||
CurvePointsRes,
|
||||
weierstrassPoints,
|
||||
} from './weierstrass.js';
|
||||
|
||||
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> = {
|
||||
fromHex(hex: Hex): ProjPointType<Fp2>;
|
||||
toRawBytes(point: ProjPointType<Fp2>): Uint8Array;
|
||||
toHex(point: ProjPointType<Fp2>): string;
|
||||
};
|
||||
|
||||
export type CurveType<Fp, Fp2, Fp6, Fp12> = {
|
||||
G1: Omit<CurvePointsType<Fp>, 'n'> & {
|
||||
ShortSignature: SignatureCoder<Fp>;
|
||||
mapToCurve: MapToCurve<Fp>;
|
||||
htfDefaults: HTFOpts;
|
||||
};
|
||||
G2: Omit<CurvePointsType<Fp2>, 'n'> & {
|
||||
Signature: SignatureCoder<Fp2>;
|
||||
mapToCurve: MapToCurve<Fp2>;
|
||||
htfDefaults: HTFOpts;
|
||||
};
|
||||
fields: {
|
||||
Fp: IField<Fp>;
|
||||
Fr: IField<bigint>;
|
||||
Fp2: IField<Fp2> & {
|
||||
reim: (num: Fp2) => { re: bigint; im: bigint };
|
||||
multiplyByB: (num: Fp2) => Fp2;
|
||||
frobeniusMap(num: Fp2, power: number): Fp2;
|
||||
};
|
||||
Fp6: IField<Fp6>;
|
||||
Fp12: IField<Fp12> & {
|
||||
frobeniusMap(num: Fp12, power: number): Fp12;
|
||||
multiplyBy014(num: Fp12, o0: Fp2, o1: Fp2, o4: Fp2): Fp12;
|
||||
conjugate(num: Fp12): Fp12;
|
||||
finalExponentiate(num: Fp12): Fp12;
|
||||
};
|
||||
};
|
||||
params: {
|
||||
x: bigint;
|
||||
r: bigint;
|
||||
};
|
||||
htfDefaults: HTFOpts;
|
||||
hash: CHash; // Because we need outputLen for DRBG
|
||||
randomBytes: (bytesLength?: number) => Uint8Array;
|
||||
};
|
||||
|
||||
export type CurveFn<Fp, Fp2, Fp6, Fp12> = {
|
||||
getPublicKey: (privateKey: PrivKey) => Uint8Array;
|
||||
getPublicKeyForShortSignatures: (privateKey: PrivKey) => Uint8Array;
|
||||
sign: {
|
||||
(message: Hex, privateKey: PrivKey, htfOpts?: htfBasicOpts): Uint8Array;
|
||||
(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: (
|
||||
signature: Hex | ProjPointType<Fp2>,
|
||||
message: Hex | ProjPointType<Fp2>,
|
||||
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;
|
||||
aggregatePublicKeys: {
|
||||
(publicKeys: Hex[]): Uint8Array;
|
||||
(publicKeys: ProjPointType<Fp>[]): ProjPointType<Fp>;
|
||||
};
|
||||
aggregateSignatures: {
|
||||
(signatures: Hex[]): Uint8Array;
|
||||
(signatures: ProjPointType<Fp2>[]): ProjPointType<Fp2>;
|
||||
};
|
||||
aggregateShortSignatures: {
|
||||
(signatures: Hex[]): Uint8Array;
|
||||
(signatures: ProjPointType<Fp>[]): ProjPointType<Fp>;
|
||||
};
|
||||
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: {
|
||||
randomPrivateKey: () => Uint8Array;
|
||||
calcPairingPrecomputes: (p: AffinePoint<Fp2>) => [Fp2, Fp2, Fp2][];
|
||||
};
|
||||
};
|
||||
|
||||
export function bls<Fp2, Fp6, Fp12>(
|
||||
CURVE: CurveType<Fp, Fp2, Fp6, Fp12>
|
||||
): CurveFn<Fp, Fp2, Fp6, Fp12> {
|
||||
// Fields are specific for curve, so for now we'll need to pass them with opts
|
||||
const { Fp, Fr, Fp2, Fp6, Fp12 } = CURVE.fields;
|
||||
const BLS_X_LEN = bitLen(CURVE.params.x);
|
||||
|
||||
// Pre-compute coefficients for sparse multiplication
|
||||
// Point addition and point double calculations is reused for coefficients
|
||||
function calcPairingPrecomputes(p: AffinePoint<Fp2>) {
|
||||
const { x, y } = p;
|
||||
// prettier-ignore
|
||||
const Qx = x, Qy = y, Qz = Fp2.ONE;
|
||||
// prettier-ignore
|
||||
let Rx = Qx, Ry = Qy, Rz = Qz;
|
||||
let ell_coeff: [Fp2, Fp2, Fp2][] = [];
|
||||
for (let i = BLS_X_LEN - 2; i >= 0; i--) {
|
||||
// Double
|
||||
let t0 = Fp2.sqr(Ry); // Ry²
|
||||
let t1 = Fp2.sqr(Rz); // Rz²
|
||||
let t2 = Fp2.multiplyByB(Fp2.mul(t1, _3n)); // 3 * T1 * B
|
||||
let t3 = Fp2.mul(t2, _3n); // 3 * T2
|
||||
let t4 = Fp2.sub(Fp2.sub(Fp2.sqr(Fp2.add(Ry, Rz)), t1), t0); // (Ry + Rz)² - T1 - T0
|
||||
ell_coeff.push([
|
||||
Fp2.sub(t2, t0), // T2 - T0
|
||||
Fp2.mul(Fp2.sqr(Rx), _3n), // 3 * Rx²
|
||||
Fp2.neg(t4), // -T4
|
||||
]);
|
||||
Rx = Fp2.div(Fp2.mul(Fp2.mul(Fp2.sub(t0, t3), Rx), Ry), _2n); // ((T0 - T3) * Rx * Ry) / 2
|
||||
Ry = Fp2.sub(Fp2.sqr(Fp2.div(Fp2.add(t0, t3), _2n)), Fp2.mul(Fp2.sqr(t2), _3n)); // ((T0 + T3) / 2)² - 3 * T2²
|
||||
Rz = Fp2.mul(t0, t4); // T0 * T4
|
||||
if (bitGet(CURVE.params.x, i)) {
|
||||
// Addition
|
||||
let t0 = Fp2.sub(Ry, Fp2.mul(Qy, Rz)); // Ry - Qy * Rz
|
||||
let t1 = Fp2.sub(Rx, Fp2.mul(Qx, Rz)); // Rx - Qx * Rz
|
||||
ell_coeff.push([
|
||||
Fp2.sub(Fp2.mul(t0, Qx), Fp2.mul(t1, Qy)), // T0 * Qx - T1 * Qy
|
||||
Fp2.neg(t0), // -T0
|
||||
t1, // T1
|
||||
]);
|
||||
let t2 = Fp2.sqr(t1); // T1²
|
||||
let t3 = Fp2.mul(t2, t1); // T2 * T1
|
||||
let t4 = Fp2.mul(t2, Rx); // T2 * Rx
|
||||
let t5 = Fp2.add(Fp2.sub(t3, Fp2.mul(t4, _2n)), Fp2.mul(Fp2.sqr(t0), Rz)); // T3 - 2 * T4 + T0² * Rz
|
||||
Rx = Fp2.mul(t1, t5); // T1 * T5
|
||||
Ry = Fp2.sub(Fp2.mul(Fp2.sub(t4, t5), t0), Fp2.mul(t3, Ry)); // (T4 - T5) * T0 - T3 * Ry
|
||||
Rz = Fp2.mul(Rz, t3); // Rz * T3
|
||||
}
|
||||
}
|
||||
return ell_coeff;
|
||||
}
|
||||
|
||||
function millerLoop(ell: [Fp2, Fp2, Fp2][], g1: [Fp, Fp]): Fp12 {
|
||||
const { x } = CURVE.params;
|
||||
const Px = g1[0];
|
||||
const Py = g1[1];
|
||||
let f12 = Fp12.ONE;
|
||||
for (let j = 0, i = BLS_X_LEN - 2; i >= 0; i--, j++) {
|
||||
const E = ell[j];
|
||||
f12 = Fp12.multiplyBy014(f12, E[0], Fp2.mul(E[1], Px), Fp2.mul(E[2], Py));
|
||||
if (bitGet(x, i)) {
|
||||
j += 1;
|
||||
const F = ell[j];
|
||||
f12 = Fp12.multiplyBy014(f12, F[0], Fp2.mul(F[1], Px), Fp2.mul(F[2], Py));
|
||||
}
|
||||
if (i !== 0) f12 = Fp12.sqr(f12);
|
||||
}
|
||||
return Fp12.conjugate(f12);
|
||||
}
|
||||
|
||||
const utils = {
|
||||
randomPrivateKey: (): Uint8Array => {
|
||||
const length = getMinHashLength(Fr.ORDER);
|
||||
return mapHashToField(CURVE.randomBytes(length), Fr.ORDER);
|
||||
},
|
||||
calcPairingPrecomputes,
|
||||
};
|
||||
|
||||
// Point on G1 curve: (x, y)
|
||||
const G1_ = weierstrassPoints({ n: Fr.ORDER, ...CURVE.G1 });
|
||||
const G1 = Object.assign(
|
||||
G1_,
|
||||
createHasher(G1_.ProjectivePoint, CURVE.G1.mapToCurve, {
|
||||
...CURVE.htfDefaults,
|
||||
...CURVE.G1.htfDefaults,
|
||||
})
|
||||
);
|
||||
|
||||
// Sparse multiplication against precomputed coefficients
|
||||
// TODO: replace with weakmap?
|
||||
type withPairingPrecomputes = { _PPRECOMPUTES: [Fp2, Fp2, Fp2][] | undefined };
|
||||
function pairingPrecomputes(point: G2): [Fp2, Fp2, Fp2][] {
|
||||
const p = point as G2 & withPairingPrecomputes;
|
||||
if (p._PPRECOMPUTES) return p._PPRECOMPUTES;
|
||||
p._PPRECOMPUTES = calcPairingPrecomputes(point.toAffine());
|
||||
return p._PPRECOMPUTES;
|
||||
}
|
||||
|
||||
// TODO: export
|
||||
// function clearPairingPrecomputes(point: G2) {
|
||||
// const p = point as G2 & withPairingPrecomputes;
|
||||
// p._PPRECOMPUTES = undefined;
|
||||
// }
|
||||
|
||||
// Point on G2 curve (complex numbers): (x₁, x₂+i), (y₁, y₂+i)
|
||||
const G2_ = weierstrassPoints({ n: Fr.ORDER, ...CURVE.G2 });
|
||||
const G2 = Object.assign(
|
||||
G2_,
|
||||
createHasher(G2_.ProjectivePoint as H2CPointConstructor<Fp2>, CURVE.G2.mapToCurve, {
|
||||
...CURVE.htfDefaults,
|
||||
...CURVE.G2.htfDefaults,
|
||||
})
|
||||
);
|
||||
|
||||
const { ShortSignature } = CURVE.G1;
|
||||
const { Signature } = CURVE.G2;
|
||||
|
||||
// Calculates bilinear pairing
|
||||
function pairing(Q: G1, P: G2, withFinalExponent: boolean = true): Fp12 {
|
||||
if (Q.equals(G1.ProjectivePoint.ZERO) || P.equals(G2.ProjectivePoint.ZERO))
|
||||
throw new Error('pairing is not available for ZERO point');
|
||||
Q.assertValidity();
|
||||
P.assertValidity();
|
||||
// Performance: 9ms for millerLoop and ~14ms for exp.
|
||||
const Qa = Q.toAffine();
|
||||
const looped = millerLoop(pairingPrecomputes(P), [Qa.x, Qa.y]);
|
||||
return withFinalExponent ? Fp12.finalExponentiate(looped) : looped;
|
||||
}
|
||||
type G1 = typeof G1.ProjectivePoint.BASE;
|
||||
type G2 = typeof G2.ProjectivePoint.BASE;
|
||||
|
||||
type G1Hex = Hex | G1;
|
||||
type G2Hex = Hex | G2;
|
||||
function normP1(point: G1Hex): G1 {
|
||||
return point instanceof G1.ProjectivePoint ? (point as G1) : G1.ProjectivePoint.fromHex(point);
|
||||
}
|
||||
function normP1Hash(point: G1Hex, htfOpts?: htfBasicOpts): G1 {
|
||||
return point instanceof G1.ProjectivePoint
|
||||
? point
|
||||
: (G1.hashToCurve(ensureBytes('point', point), htfOpts) as G1);
|
||||
}
|
||||
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
|
||||
? point
|
||||
: (G2.hashToCurve(ensureBytes('point', point), htfOpts) as G2);
|
||||
}
|
||||
|
||||
// Multiplies generator (G1) by private key.
|
||||
// P = pk x G
|
||||
function getPublicKey(privateKey: PrivKey): Uint8Array {
|
||||
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.
|
||||
// S = pk x H(m)
|
||||
function sign(message: Hex, privateKey: PrivKey, htfOpts?: htfBasicOpts): Uint8Array;
|
||||
function sign(message: G2, privateKey: PrivKey, htfOpts?: htfBasicOpts): G2;
|
||||
function sign(message: G2Hex, privateKey: PrivKey, htfOpts?: htfBasicOpts): Uint8Array | G2 {
|
||||
const msgPoint = normP2Hash(message, htfOpts);
|
||||
msgPoint.assertValidity();
|
||||
const sigPoint = msgPoint.multiply(G1.normPrivateKeyToScalar(privateKey));
|
||||
if (message instanceof G2.ProjectivePoint) return 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.
|
||||
// e(P, H(m)) == e(G, S)
|
||||
function verify(
|
||||
signature: G2Hex,
|
||||
message: G2Hex,
|
||||
publicKey: G1Hex,
|
||||
htfOpts?: htfBasicOpts
|
||||
): boolean {
|
||||
const P = normP1(publicKey);
|
||||
const Hm = normP2Hash(message, htfOpts);
|
||||
const G = G1.ProjectivePoint.BASE;
|
||||
const S = normP2(signature);
|
||||
// Instead of doing 2 exponentiations, we use property of billinear maps
|
||||
// and do one exp after multiplying 2 points.
|
||||
const ePHm = pairing(P.negate(), Hm, false);
|
||||
const eGS = pairing(G, S, false);
|
||||
const exp = Fp12.finalExponentiate(Fp12.mul(eGS, ePHm));
|
||||
return Fp12.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.
|
||||
// pk1 + pk2 + pk3 = pkA
|
||||
function aggregatePublicKeys(publicKeys: Hex[]): Uint8Array;
|
||||
function aggregatePublicKeys(publicKeys: G1[]): G1;
|
||||
function aggregatePublicKeys(publicKeys: G1Hex[]): Uint8Array | G1 {
|
||||
if (!publicKeys.length) throw new Error('Expected non-empty array');
|
||||
const agg = publicKeys.map(normP1).reduce((sum, p) => sum.add(p), G1.ProjectivePoint.ZERO);
|
||||
const aggAffine = agg; //.toAffine();
|
||||
if (publicKeys[0] instanceof G1.ProjectivePoint) {
|
||||
aggAffine.assertValidity();
|
||||
return aggAffine;
|
||||
}
|
||||
// toRawBytes ensures point validity
|
||||
return aggAffine.toRawBytes(true);
|
||||
}
|
||||
|
||||
// Adds a bunch of signature points together.
|
||||
function aggregateSignatures(signatures: Hex[]): Uint8Array;
|
||||
function aggregateSignatures(signatures: G2[]): G2;
|
||||
function aggregateSignatures(signatures: G2Hex[]): Uint8Array | G2 {
|
||||
if (!signatures.length) throw new Error('Expected non-empty array');
|
||||
const agg = signatures.map(normP2).reduce((sum, s) => sum.add(s), G2.ProjectivePoint.ZERO);
|
||||
const aggAffine = agg; //.toAffine();
|
||||
if (signatures[0] instanceof G2.ProjectivePoint) {
|
||||
aggAffine.assertValidity();
|
||||
return 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
|
||||
// e(G, S) = e(G, SUM(n)(Si)) = MUL(n)(e(G, Si))
|
||||
function verifyBatch(
|
||||
signature: G2Hex,
|
||||
messages: G2Hex[],
|
||||
publicKeys: G1Hex[],
|
||||
htfOpts?: htfBasicOpts
|
||||
): boolean {
|
||||
// @ts-ignore
|
||||
// console.log('verifyBatch', bytesToHex(signature as any), messages, publicKeys.map(bytesToHex));
|
||||
|
||||
if (!messages.length) throw new Error('Expected non-empty messages array');
|
||||
if (publicKeys.length !== messages.length)
|
||||
throw new Error('Pubkey count should equal msg count');
|
||||
const sig = normP2(signature);
|
||||
const nMessages = messages.map((i) => normP2Hash(i, htfOpts));
|
||||
const nPublicKeys = publicKeys.map(normP1);
|
||||
try {
|
||||
const paired = [];
|
||||
for (const message of new Set(nMessages)) {
|
||||
const groupPublicKey = nMessages.reduce(
|
||||
(groupPublicKey, subMessage, i) =>
|
||||
subMessage === message ? groupPublicKey.add(nPublicKeys[i]) : groupPublicKey,
|
||||
G1.ProjectivePoint.ZERO
|
||||
);
|
||||
// const msg = message instanceof PointG2 ? message : await PointG2.hashToCurve(message);
|
||||
// Possible to batch pairing for same msg with different groupPublicKey here
|
||||
paired.push(pairing(groupPublicKey, message, false));
|
||||
}
|
||||
paired.push(pairing(G1.ProjectivePoint.BASE.negate(), sig, false));
|
||||
const product = paired.reduce((a, b) => Fp12.mul(a, b), Fp12.ONE);
|
||||
const exp = Fp12.finalExponentiate(product);
|
||||
return Fp12.eql(exp, Fp12.ONE);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
G1.ProjectivePoint.BASE._setWindowSize(4);
|
||||
|
||||
return {
|
||||
getPublicKey,
|
||||
getPublicKeyForShortSignatures,
|
||||
sign,
|
||||
signShortSignature,
|
||||
verify,
|
||||
verifyBatch,
|
||||
verifyShortSignature,
|
||||
aggregatePublicKeys,
|
||||
aggregateSignatures,
|
||||
aggregateShortSignatures,
|
||||
millerLoop,
|
||||
pairing,
|
||||
G1,
|
||||
G2,
|
||||
Signature,
|
||||
ShortSignature,
|
||||
fields: {
|
||||
Fr,
|
||||
Fp,
|
||||
Fp2,
|
||||
Fp6,
|
||||
Fp12,
|
||||
},
|
||||
params: {
|
||||
x: CURVE.params.x,
|
||||
r: CURVE.params.r,
|
||||
G1b: CURVE.G1.b,
|
||||
G2b: CURVE.G2.b,
|
||||
},
|
||||
utils,
|
||||
};
|
||||
}
|
203
src/abstract/curve.ts
Normal file
203
src/abstract/curve.ts
Normal file
@ -0,0 +1,203 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
// Abelian group utilities
|
||||
import { IField, validateField, nLength } from './modular.js';
|
||||
import { validateObject } from './utils.js';
|
||||
const _0n = BigInt(0);
|
||||
const _1n = BigInt(1);
|
||||
|
||||
export type AffinePoint<T> = {
|
||||
x: T;
|
||||
y: T;
|
||||
} & { z?: never; t?: never };
|
||||
|
||||
export interface Group<T extends Group<T>> {
|
||||
double(): T;
|
||||
negate(): T;
|
||||
add(other: T): T;
|
||||
subtract(other: T): T;
|
||||
equals(other: T): boolean;
|
||||
multiply(scalar: bigint): T;
|
||||
}
|
||||
|
||||
export type GroupConstructor<T> = {
|
||||
BASE: T;
|
||||
ZERO: T;
|
||||
};
|
||||
export type Mapper<T> = (i: T[]) => T[];
|
||||
|
||||
// Elliptic curve multiplication of Point by scalar. Fragile.
|
||||
// Scalars should always be less than curve order: this should be checked inside of a curve itself.
|
||||
// Creates precomputation tables for fast multiplication:
|
||||
// - private scalar is split by fixed size windows of W bits
|
||||
// - every window point is collected from window's table & added to accumulator
|
||||
// - since windows are different, same point inside tables won't be accessed more than once per calc
|
||||
// - each multiplication is 'Math.ceil(CURVE_ORDER / 𝑊) + 1' point additions (fixed for any scalar)
|
||||
// - +1 window is neccessary for wNAF
|
||||
// - wNAF reduces table size: 2x less memory + 2x faster generation, but 10% slower multiplication
|
||||
// TODO: Research returning 2d JS array of windows, instead of a single window. This would allow
|
||||
// windows to be in different memory locations
|
||||
export function wNAF<T extends Group<T>>(c: GroupConstructor<T>, bits: number) {
|
||||
const constTimeNegate = (condition: boolean, item: T): T => {
|
||||
const neg = item.negate();
|
||||
return condition ? neg : item;
|
||||
};
|
||||
const opts = (W: number) => {
|
||||
const windows = Math.ceil(bits / W) + 1; // +1, because
|
||||
const windowSize = 2 ** (W - 1); // -1 because we skip zero
|
||||
return { windows, windowSize };
|
||||
};
|
||||
return {
|
||||
constTimeNegate,
|
||||
// non-const time multiplication ladder
|
||||
unsafeLadder(elm: T, n: bigint) {
|
||||
let p = c.ZERO;
|
||||
let d: T = elm;
|
||||
while (n > _0n) {
|
||||
if (n & _1n) p = p.add(d);
|
||||
d = d.double();
|
||||
n >>= _1n;
|
||||
}
|
||||
return p;
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a wNAF precomputation window. Used for caching.
|
||||
* Default window size is set by `utils.precompute()` and is equal to 8.
|
||||
* Number of precomputed points depends on the curve size:
|
||||
* 2^(𝑊−1) * (Math.ceil(𝑛 / 𝑊) + 1), where:
|
||||
* - 𝑊 is the window size
|
||||
* - 𝑛 is the bitlength of the curve order.
|
||||
* For a 256-bit curve and window size 8, the number of precomputed points is 128 * 33 = 4224.
|
||||
* @returns precomputed point tables flattened to a single array
|
||||
*/
|
||||
precomputeWindow(elm: T, W: number): Group<T>[] {
|
||||
const { windows, windowSize } = opts(W);
|
||||
const points: T[] = [];
|
||||
let p: T = elm;
|
||||
let base = p;
|
||||
for (let window = 0; window < windows; window++) {
|
||||
base = p;
|
||||
points.push(base);
|
||||
// =1, because we skip zero
|
||||
for (let i = 1; i < windowSize; i++) {
|
||||
base = base.add(p);
|
||||
points.push(base);
|
||||
}
|
||||
p = base.double();
|
||||
}
|
||||
return points;
|
||||
},
|
||||
|
||||
/**
|
||||
* Implements ec multiplication using precomputed tables and w-ary non-adjacent form.
|
||||
* @param W window size
|
||||
* @param precomputes precomputed tables
|
||||
* @param n scalar (we don't check here, but should be less than curve order)
|
||||
* @returns real and fake (for const-time) points
|
||||
*/
|
||||
wNAF(W: number, precomputes: T[], n: bigint): { p: T; f: T } {
|
||||
// TODO: maybe check that scalar is less than group order? wNAF behavious is undefined otherwise
|
||||
// But need to carefully remove other checks before wNAF. ORDER == bits here
|
||||
const { windows, windowSize } = opts(W);
|
||||
|
||||
let p = c.ZERO;
|
||||
let f = c.BASE;
|
||||
|
||||
const mask = BigInt(2 ** W - 1); // Create mask with W ones: 0b1111 for W=4 etc.
|
||||
const maxNumber = 2 ** W;
|
||||
const shiftBy = BigInt(W);
|
||||
|
||||
for (let window = 0; window < windows; window++) {
|
||||
const offset = window * windowSize;
|
||||
// Extract W bits.
|
||||
let wbits = Number(n & mask);
|
||||
|
||||
// Shift number by W bits.
|
||||
n >>= shiftBy;
|
||||
|
||||
// If the bits are bigger than max size, we'll split those.
|
||||
// +224 => 256 - 32
|
||||
if (wbits > windowSize) {
|
||||
wbits -= maxNumber;
|
||||
n += _1n;
|
||||
}
|
||||
|
||||
// This code was first written with assumption that 'f' and 'p' will never be infinity point:
|
||||
// since each addition is multiplied by 2 ** W, it cannot cancel each other. However,
|
||||
// there is negate now: it is possible that negated element from low value
|
||||
// would be the same as high element, which will create carry into next window.
|
||||
// It's not obvious how this can fail, but still worth investigating later.
|
||||
|
||||
// Check if we're onto Zero point.
|
||||
// Add random point inside current window to f.
|
||||
const offset1 = offset;
|
||||
const offset2 = offset + Math.abs(wbits) - 1; // -1 because we skip zero
|
||||
const cond1 = window % 2 !== 0;
|
||||
const cond2 = wbits < 0;
|
||||
if (wbits === 0) {
|
||||
// The most important part for const-time getPublicKey
|
||||
f = f.add(constTimeNegate(cond1, precomputes[offset1]));
|
||||
} else {
|
||||
p = p.add(constTimeNegate(cond2, precomputes[offset2]));
|
||||
}
|
||||
}
|
||||
// JIT-compiler should not eliminate f here, since it will later be used in normalizeZ()
|
||||
// Even if the variable is still unused, there are some checks which will
|
||||
// throw an exception, so compiler needs to prove they won't happen, which is hard.
|
||||
// At this point there is a way to F be infinity-point even if p is not,
|
||||
// which makes it less const-time: around 1 bigint multiply.
|
||||
return { p, f };
|
||||
},
|
||||
|
||||
wNAFCached(P: T, precomputesMap: Map<T, T[]>, n: bigint, transform: Mapper<T>): { p: T; f: T } {
|
||||
// @ts-ignore
|
||||
const W: number = P._WINDOW_SIZE || 1;
|
||||
// Calculate precomputes on a first run, reuse them after
|
||||
let comp = precomputesMap.get(P);
|
||||
if (!comp) {
|
||||
comp = this.precomputeWindow(P, W) as T[];
|
||||
if (W !== 1) {
|
||||
precomputesMap.set(P, transform(comp));
|
||||
}
|
||||
}
|
||||
return this.wNAF(W, comp, n);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Generic BasicCurve interface: works even for polynomial fields (BLS): P, n, h would be ok.
|
||||
// Though generator can be different (Fp2 / Fp6 for BLS).
|
||||
export type BasicCurve<T> = {
|
||||
Fp: IField<T>; // Field over which we'll do calculations (Fp)
|
||||
n: bigint; // Curve order, total count of valid points in the field
|
||||
nBitLength?: number; // bit length of curve order
|
||||
nByteLength?: number; // byte length of curve order
|
||||
h: bigint; // cofactor. we can assign default=1, but users will just ignore it w/o validation
|
||||
hEff?: bigint; // Number to multiply to clear cofactor
|
||||
Gx: T; // base point X coordinate
|
||||
Gy: T; // base point Y coordinate
|
||||
allowInfinityPoint?: boolean; // bls12-381 requires it. ZERO point is valid, but invalid pubkey
|
||||
};
|
||||
|
||||
export function validateBasic<FP, T>(curve: BasicCurve<FP> & T) {
|
||||
validateField(curve.Fp);
|
||||
validateObject(
|
||||
curve,
|
||||
{
|
||||
n: 'bigint',
|
||||
h: 'bigint',
|
||||
Gx: 'field',
|
||||
Gy: 'field',
|
||||
},
|
||||
{
|
||||
nBitLength: 'isSafeInteger',
|
||||
nByteLength: 'isSafeInteger',
|
||||
}
|
||||
);
|
||||
// Set defaults
|
||||
return Object.freeze({
|
||||
...nLength(curve.n, curve.nBitLength),
|
||||
...curve,
|
||||
...{ p: curve.Fp.ORDER },
|
||||
} as const);
|
||||
}
|
513
src/abstract/edwards.ts
Normal file
513
src/abstract/edwards.ts
Normal file
@ -0,0 +1,513 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
// Twisted Edwards curve. The formula is: ax² + y² = 1 + dx²y²
|
||||
import { mod } from './modular.js';
|
||||
import * as ut from './utils.js';
|
||||
import { ensureBytes, FHash, Hex } from './utils.js';
|
||||
import { Group, GroupConstructor, wNAF, BasicCurve, validateBasic, AffinePoint } from './curve.js';
|
||||
|
||||
// Be friendly to bad ECMAScript parsers by not using bigint literals
|
||||
// prettier-ignore
|
||||
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _8n = BigInt(8);
|
||||
|
||||
// Edwards curves must declare params a & d.
|
||||
export type CurveType = BasicCurve<bigint> & {
|
||||
a: bigint; // curve param a
|
||||
d: bigint; // curve param d
|
||||
hash: FHash; // Hashing
|
||||
randomBytes: (bytesLength?: number) => Uint8Array; // CSPRNG
|
||||
adjustScalarBytes?: (bytes: Uint8Array) => Uint8Array; // clears bits to get valid field elemtn
|
||||
domain?: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array; // Used for hashing
|
||||
uvRatio?: (u: bigint, v: bigint) => { isValid: boolean; value: bigint }; // Ratio √(u/v)
|
||||
prehash?: FHash; // RFC 8032 pre-hashing of messages to sign() / verify()
|
||||
mapToCurve?: (scalar: bigint[]) => AffinePoint<bigint>; // for hash-to-curve standard
|
||||
};
|
||||
|
||||
// verification rule is either zip215 or rfc8032 / nist186-5. Consult fromHex:
|
||||
const VERIFY_DEFAULT = { zip215: true };
|
||||
|
||||
function validateOpts(curve: CurveType) {
|
||||
const opts = validateBasic(curve);
|
||||
ut.validateObject(
|
||||
curve,
|
||||
{
|
||||
hash: 'function',
|
||||
a: 'bigint',
|
||||
d: 'bigint',
|
||||
randomBytes: 'function',
|
||||
},
|
||||
{
|
||||
adjustScalarBytes: 'function',
|
||||
domain: 'function',
|
||||
uvRatio: 'function',
|
||||
mapToCurve: 'function',
|
||||
}
|
||||
);
|
||||
// Set defaults
|
||||
return Object.freeze({ ...opts } as const);
|
||||
}
|
||||
|
||||
// Instance of Extended Point with coordinates in X, Y, Z, T
|
||||
export interface ExtPointType extends Group<ExtPointType> {
|
||||
readonly ex: bigint;
|
||||
readonly ey: bigint;
|
||||
readonly ez: bigint;
|
||||
readonly et: bigint;
|
||||
get x(): bigint;
|
||||
get y(): bigint;
|
||||
assertValidity(): void;
|
||||
multiply(scalar: bigint): ExtPointType;
|
||||
multiplyUnsafe(scalar: bigint): ExtPointType;
|
||||
isSmallOrder(): boolean;
|
||||
isTorsionFree(): boolean;
|
||||
clearCofactor(): ExtPointType;
|
||||
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
|
||||
export interface ExtPointConstructor extends GroupConstructor<ExtPointType> {
|
||||
new (x: bigint, y: bigint, z: bigint, t: bigint): ExtPointType;
|
||||
fromAffine(p: AffinePoint<bigint>): ExtPointType;
|
||||
fromHex(hex: Hex): ExtPointType;
|
||||
fromPrivateKey(privateKey: Hex): ExtPointType;
|
||||
}
|
||||
|
||||
export type CurveFn = {
|
||||
CURVE: ReturnType<typeof validateOpts>;
|
||||
getPublicKey: (privateKey: Hex) => Uint8Array;
|
||||
sign: (message: Hex, privateKey: Hex, options?: { context?: Hex }) => Uint8Array;
|
||||
verify: (
|
||||
sig: Hex,
|
||||
message: Hex,
|
||||
publicKey: Hex,
|
||||
options?: { context?: Hex; zip215: boolean }
|
||||
) => boolean;
|
||||
ExtendedPoint: ExtPointConstructor;
|
||||
utils: {
|
||||
randomPrivateKey: () => Uint8Array;
|
||||
getExtendedPublicKey: (key: Hex) => {
|
||||
head: Uint8Array;
|
||||
prefix: Uint8Array;
|
||||
scalar: bigint;
|
||||
point: ExtPointType;
|
||||
pointBytes: Uint8Array;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
// It is not generic twisted curve for now, but ed25519/ed448 generic implementation
|
||||
export function twistedEdwards(curveDef: CurveType): CurveFn {
|
||||
const CURVE = validateOpts(curveDef) as ReturnType<typeof validateOpts>;
|
||||
const {
|
||||
Fp,
|
||||
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
|
||||
|
||||
// sqrt(u/v)
|
||||
const uvRatio =
|
||||
CURVE.uvRatio ||
|
||||
((u: bigint, v: bigint) => {
|
||||
try {
|
||||
return { isValid: true, value: Fp.sqrt(u * Fp.inv(v)) };
|
||||
} catch (e) {
|
||||
return { isValid: false, value: _0n };
|
||||
}
|
||||
});
|
||||
const adjustScalarBytes = CURVE.adjustScalarBytes || ((bytes: Uint8Array) => bytes); // NOOP
|
||||
const domain =
|
||||
CURVE.domain ||
|
||||
((data: Uint8Array, ctx: Uint8Array, phflag: boolean) => {
|
||||
if (ctx.length || phflag) throw new Error('Contexts/pre-hash are not supported');
|
||||
return data;
|
||||
}); // NOOP
|
||||
const inBig = (n: bigint) => typeof n === 'bigint' && _0n < n; // n in [1..]
|
||||
const inRange = (n: bigint, max: bigint) => inBig(n) && inBig(max) && n < max; // n in [1..max-1]
|
||||
const in0MaskRange = (n: bigint) => n === _0n || inRange(n, MASK); // n in [0..MASK-1]
|
||||
function assertInRange(n: bigint, max: bigint) {
|
||||
// n in [1..max-1]
|
||||
if (inRange(n, max)) return n;
|
||||
throw new Error(`Expected valid scalar < ${max}, got ${typeof n} ${n}`);
|
||||
}
|
||||
function assertGE0(n: bigint) {
|
||||
// n in [0..CURVE_ORDER-1]
|
||||
return n === _0n ? n : assertInRange(n, CURVE_ORDER); // GE = prime subgroup, not full group
|
||||
}
|
||||
const pointPrecomputes = new Map<Point, Point[]>();
|
||||
function isPoint(other: unknown) {
|
||||
if (!(other instanceof Point)) throw new Error('ExtendedPoint expected');
|
||||
}
|
||||
// Extended Point works in extended coordinates: (x, y, z, t) ∋ (x=x/z, y=y/z, t=xy).
|
||||
// https://en.wikipedia.org/wiki/Twisted_Edwards_curve#Extended_coordinates
|
||||
class Point implements ExtPointType {
|
||||
static readonly BASE = new Point(CURVE.Gx, CURVE.Gy, _1n, modP(CURVE.Gx * CURVE.Gy));
|
||||
static readonly ZERO = new Point(_0n, _1n, _1n, _0n); // 0, 1, 1, 0
|
||||
|
||||
constructor(
|
||||
readonly ex: bigint,
|
||||
readonly ey: bigint,
|
||||
readonly ez: bigint,
|
||||
readonly et: bigint
|
||||
) {
|
||||
if (!in0MaskRange(ex)) throw new Error('x required');
|
||||
if (!in0MaskRange(ey)) throw new Error('y required');
|
||||
if (!in0MaskRange(ez)) throw new Error('z required');
|
||||
if (!in0MaskRange(et)) throw new Error('t required');
|
||||
}
|
||||
|
||||
get x(): bigint {
|
||||
return this.toAffine().x;
|
||||
}
|
||||
get y(): bigint {
|
||||
return this.toAffine().y;
|
||||
}
|
||||
|
||||
static fromAffine(p: AffinePoint<bigint>): Point {
|
||||
if (p instanceof Point) throw new Error('extended point not allowed');
|
||||
const { x, y } = p || {};
|
||||
if (!in0MaskRange(x) || !in0MaskRange(y)) throw new Error('invalid affine point');
|
||||
return new Point(x, y, _1n, modP(x * y));
|
||||
}
|
||||
static normalizeZ(points: Point[]): Point[] {
|
||||
const toInv = Fp.invertBatch(points.map((p) => p.ez));
|
||||
return points.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine);
|
||||
}
|
||||
|
||||
// We calculate precomputes for elliptic curve point multiplication
|
||||
// using windowed method. This specifies window size and
|
||||
// stores precomputed values. Usually only base point would be precomputed.
|
||||
_WINDOW_SIZE?: number;
|
||||
|
||||
// "Private method", don't use it directly
|
||||
_setWindowSize(windowSize: number) {
|
||||
this._WINDOW_SIZE = windowSize;
|
||||
pointPrecomputes.delete(this);
|
||||
}
|
||||
// Not required for fromHex(), which always creates valid points.
|
||||
// Could be useful for fromAffine().
|
||||
assertValidity(): void {
|
||||
const { a, d } = CURVE;
|
||||
if (this.is0()) throw new Error('bad point: ZERO'); // TODO: optimize, with vars below?
|
||||
// Equation in affine coordinates: ax² + y² = 1 + dx²y²
|
||||
// Equation in projective coordinates (X/Z, Y/Z, Z): (aX² + Y²)Z² = Z⁴ + dX²Y²
|
||||
const { ex: X, ey: Y, ez: Z, et: T } = this;
|
||||
const X2 = modP(X * X); // X²
|
||||
const Y2 = modP(Y * Y); // Y²
|
||||
const Z2 = modP(Z * Z); // Z²
|
||||
const Z4 = modP(Z2 * Z2); // Z⁴
|
||||
const aX2 = modP(X2 * a); // aX²
|
||||
const left = modP(Z2 * modP(aX2 + Y2)); // (aX² + Y²)Z²
|
||||
const right = modP(Z4 + modP(d * modP(X2 * Y2))); // Z⁴ + dX²Y²
|
||||
if (left !== right) throw new Error('bad point: equation left != right (1)');
|
||||
// In Extended coordinates we also have T, which is x*y=T/Z: check X*Y == Z*T
|
||||
const XY = modP(X * Y);
|
||||
const ZT = modP(Z * T);
|
||||
if (XY !== ZT) throw new Error('bad point: equation left != right (2)');
|
||||
}
|
||||
|
||||
// Compare one point to another.
|
||||
equals(other: Point): boolean {
|
||||
isPoint(other);
|
||||
const { ex: X1, ey: Y1, ez: Z1 } = this;
|
||||
const { ex: X2, ey: Y2, ez: Z2 } = other;
|
||||
const X1Z2 = modP(X1 * Z2);
|
||||
const X2Z1 = modP(X2 * Z1);
|
||||
const Y1Z2 = modP(Y1 * Z2);
|
||||
const Y2Z1 = modP(Y2 * Z1);
|
||||
return X1Z2 === X2Z1 && Y1Z2 === Y2Z1;
|
||||
}
|
||||
|
||||
protected is0(): boolean {
|
||||
return this.equals(Point.ZERO);
|
||||
}
|
||||
|
||||
negate(): Point {
|
||||
// Flips point sign to a negative one (-x, y in affine coords)
|
||||
return new Point(modP(-this.ex), this.ey, this.ez, modP(-this.et));
|
||||
}
|
||||
|
||||
// Fast algo for doubling Extended Point.
|
||||
// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd
|
||||
// Cost: 4M + 4S + 1*a + 6add + 1*2.
|
||||
double(): Point {
|
||||
const { a } = CURVE;
|
||||
const { ex: X1, ey: Y1, ez: Z1 } = this;
|
||||
const A = modP(X1 * X1); // A = X12
|
||||
const B = modP(Y1 * Y1); // B = Y12
|
||||
const C = modP(_2n * modP(Z1 * Z1)); // C = 2*Z12
|
||||
const D = modP(a * A); // D = a*A
|
||||
const x1y1 = X1 + Y1;
|
||||
const E = modP(modP(x1y1 * x1y1) - A - B); // E = (X1+Y1)2-A-B
|
||||
const G = D + B; // G = D+B
|
||||
const F = G - C; // F = G-C
|
||||
const H = D - B; // H = D-B
|
||||
const X3 = modP(E * F); // X3 = E*F
|
||||
const Y3 = modP(G * H); // Y3 = G*H
|
||||
const T3 = modP(E * H); // T3 = E*H
|
||||
const Z3 = modP(F * G); // Z3 = F*G
|
||||
return new Point(X3, Y3, Z3, T3);
|
||||
}
|
||||
|
||||
// Fast algo for adding 2 Extended Points.
|
||||
// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#addition-add-2008-hwcd
|
||||
// Cost: 9M + 1*a + 1*d + 7add.
|
||||
add(other: Point) {
|
||||
isPoint(other);
|
||||
const { a, d } = CURVE;
|
||||
const { ex: X1, ey: Y1, ez: Z1, et: T1 } = this;
|
||||
const { ex: X2, ey: Y2, ez: Z2, et: T2 } = other;
|
||||
// Faster algo for adding 2 Extended Points when curve's a=-1.
|
||||
// http://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-4
|
||||
// Cost: 8M + 8add + 2*2.
|
||||
// Note: It does not check whether the `other` point is valid.
|
||||
if (a === BigInt(-1)) {
|
||||
const A = modP((Y1 - X1) * (Y2 + X2));
|
||||
const B = modP((Y1 + X1) * (Y2 - X2));
|
||||
const F = modP(B - A);
|
||||
if (F === _0n) return this.double(); // Same point. Tests say it doesn't affect timing
|
||||
const C = modP(Z1 * _2n * T2);
|
||||
const D = modP(T1 * _2n * Z2);
|
||||
const E = D + C;
|
||||
const G = B + A;
|
||||
const H = D - C;
|
||||
const X3 = modP(E * F);
|
||||
const Y3 = modP(G * H);
|
||||
const T3 = modP(E * H);
|
||||
const Z3 = modP(F * G);
|
||||
return new Point(X3, Y3, Z3, T3);
|
||||
}
|
||||
const A = modP(X1 * X2); // A = X1*X2
|
||||
const B = modP(Y1 * Y2); // B = Y1*Y2
|
||||
const C = modP(T1 * d * T2); // C = T1*d*T2
|
||||
const D = modP(Z1 * Z2); // D = Z1*Z2
|
||||
const E = modP((X1 + Y1) * (X2 + Y2) - A - B); // E = (X1+Y1)*(X2+Y2)-A-B
|
||||
const F = D - C; // F = D-C
|
||||
const G = D + C; // G = D+C
|
||||
const H = modP(B - a * A); // H = B-a*A
|
||||
const X3 = modP(E * F); // X3 = E*F
|
||||
const Y3 = modP(G * H); // Y3 = G*H
|
||||
const T3 = modP(E * H); // T3 = E*H
|
||||
const Z3 = modP(F * G); // Z3 = F*G
|
||||
|
||||
return new Point(X3, Y3, Z3, T3);
|
||||
}
|
||||
|
||||
subtract(other: Point): Point {
|
||||
return this.add(other.negate());
|
||||
}
|
||||
|
||||
private wNAF(n: bigint): { p: Point; f: Point } {
|
||||
return wnaf.wNAFCached(this, pointPrecomputes, n, Point.normalizeZ);
|
||||
}
|
||||
|
||||
// Constant-time multiplication.
|
||||
multiply(scalar: bigint): Point {
|
||||
const { p, f } = this.wNAF(assertInRange(scalar, CURVE_ORDER));
|
||||
return Point.normalizeZ([p, f])[0];
|
||||
}
|
||||
|
||||
// Non-constant-time multiplication. Uses double-and-add algorithm.
|
||||
// It's faster, but should only be used when you don't care about
|
||||
// an exposed private key e.g. sig verification.
|
||||
// Does NOT allow scalars higher than CURVE.n.
|
||||
multiplyUnsafe(scalar: bigint): Point {
|
||||
let n = assertGE0(scalar); // 0 <= scalar < CURVE.n
|
||||
if (n === _0n) return I;
|
||||
if (this.equals(I) || n === _1n) return this;
|
||||
if (this.equals(G)) return this.wNAF(n).p;
|
||||
return wnaf.unsafeLadder(this, n);
|
||||
}
|
||||
|
||||
// Checks if point is of small order.
|
||||
// If you add something to small order point, you will have "dirty"
|
||||
// point with torsion component.
|
||||
// Multiplies point by cofactor and checks if the result is 0.
|
||||
isSmallOrder(): boolean {
|
||||
return this.multiplyUnsafe(cofactor).is0();
|
||||
}
|
||||
|
||||
// Multiplies point by curve order and checks if the result is 0.
|
||||
// Returns `false` is the point is dirty.
|
||||
isTorsionFree(): boolean {
|
||||
return wnaf.unsafeLadder(this, CURVE_ORDER).is0();
|
||||
}
|
||||
|
||||
// Converts Extended point to default (x, y) coordinates.
|
||||
// Can accept precomputed Z^-1 - for example, from invertBatch.
|
||||
toAffine(iz?: bigint): AffinePoint<bigint> {
|
||||
const { ex: x, ey: y, ez: z } = this;
|
||||
const is0 = this.is0();
|
||||
if (iz == null) iz = is0 ? _8n : (Fp.inv(z) as bigint); // 8 was chosen arbitrarily
|
||||
const ax = modP(x * iz);
|
||||
const ay = modP(y * iz);
|
||||
const zz = modP(z * iz);
|
||||
if (is0) return { x: _0n, y: _1n };
|
||||
if (zz !== _1n) throw new Error('invZ was invalid');
|
||||
return { x: ax, y: ay };
|
||||
}
|
||||
|
||||
clearCofactor(): Point {
|
||||
const { h: cofactor } = CURVE;
|
||||
if (cofactor === _1n) return this;
|
||||
return this.multiplyUnsafe(cofactor);
|
||||
}
|
||||
|
||||
// Converts hash string or Uint8Array to Point.
|
||||
// Uses algo from RFC8032 5.1.3.
|
||||
static fromHex(hex: Hex, zip215 = false): Point {
|
||||
const { d, a } = CURVE;
|
||||
const len = Fp.BYTES;
|
||||
hex = ensureBytes('pointHex', hex, len); // copy hex to a new array
|
||||
const normed = hex.slice(); // copy again, we'll manipulate it
|
||||
const lastByte = hex[len - 1]; // select last byte
|
||||
normed[len - 1] = lastByte & ~0x80; // clear last bit
|
||||
const y = ut.bytesToNumberLE(normed);
|
||||
if (y === _0n) {
|
||||
// y=0 is allowed
|
||||
} else {
|
||||
// RFC8032 prohibits >= p, but ZIP215 doesn't
|
||||
if (zip215) assertInRange(y, MASK); // zip215=true [1..P-1] (2^255-19-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:
|
||||
// ax²+y²=1+dx²y² => y²-1=dx²y²-ax² => y²-1=x²(dy²-a) => x²=(y²-1)/(dy²-a)
|
||||
const y2 = modP(y * y); // denominator is always non-0 mod p.
|
||||
const u = modP(y2 - _1n); // u = y² - 1
|
||||
const v = modP(d * y2 - a); // v = d y² + 1.
|
||||
let { isValid, value: x } = uvRatio(u, v); // √(u/v)
|
||||
if (!isValid) throw new Error('Point.fromHex: invalid y coordinate');
|
||||
const isXOdd = (x & _1n) === _1n; // There are 2 square roots. Use x_0 bit to select proper
|
||||
const isLastByteOdd = (lastByte & 0x80) !== 0; // 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
|
||||
return Point.fromAffine({ x, y });
|
||||
}
|
||||
static fromPrivateKey(privKey: Hex) {
|
||||
return getExtendedPublicKey(privKey).point;
|
||||
}
|
||||
toRawBytes(): Uint8Array {
|
||||
const { x, y } = this.toAffine();
|
||||
const bytes = ut.numberToBytesLE(y, Fp.BYTES); // each y has 2 x values (x, -y)
|
||||
bytes[bytes.length - 1] |= x & _1n ? 0x80 : 0; // when compressing, it's enough to store y
|
||||
return bytes; // and use the last byte to encode sign of x
|
||||
}
|
||||
toHex(): string {
|
||||
return ut.bytesToHex(this.toRawBytes()); // Same as toRawBytes, but returns string.
|
||||
}
|
||||
}
|
||||
const { BASE: G, ZERO: I } = Point;
|
||||
const wnaf = wNAF(Point, nByteLength * 8);
|
||||
|
||||
function modN(a: bigint) {
|
||||
return mod(a, CURVE_ORDER);
|
||||
}
|
||||
// Little-endian SHA512 with modulo n
|
||||
function modN_LE(hash: Uint8Array): bigint {
|
||||
return modN(ut.bytesToNumberLE(hash));
|
||||
}
|
||||
|
||||
/** Convenience method that creates public key and other stuff. RFC8032 5.1.5 */
|
||||
function getExtendedPublicKey(key: Hex) {
|
||||
const len = nByteLength;
|
||||
key = ensureBytes('private key', key, len);
|
||||
// Hash private key with curve's hash function to produce uniformingly random input
|
||||
// Check byte lengths: ensure(64, h(ensure(32, key)))
|
||||
const hashed = ensureBytes('hashed private key', cHash(key), 2 * len);
|
||||
const head = adjustScalarBytes(hashed.slice(0, len)); // clear first half bits, produce FE
|
||||
const prefix = hashed.slice(len, 2 * len); // second half is called key prefix (5.1.6)
|
||||
const scalar = modN_LE(head); // The actual private scalar
|
||||
const point = G.multiply(scalar); // Point on Edwards curve aka public key
|
||||
const pointBytes = point.toRawBytes(); // Uint8Array representation
|
||||
return { head, prefix, scalar, point, pointBytes };
|
||||
}
|
||||
|
||||
// Calculates EdDSA pub key. RFC8032 5.1.5. Privkey is hashed. Use first half with 3 bits cleared
|
||||
function getPublicKey(privKey: Hex): Uint8Array {
|
||||
return getExtendedPublicKey(privKey).pointBytes;
|
||||
}
|
||||
|
||||
// int('LE', SHA512(dom2(F, C) || msgs)) mod N
|
||||
function hashDomainToScalar(context: Hex = new Uint8Array(), ...msgs: Uint8Array[]) {
|
||||
const msg = ut.concatBytes(...msgs);
|
||||
return modN_LE(cHash(domain(msg, ensureBytes('context', context), !!prehash)));
|
||||
}
|
||||
|
||||
/** Signs message with privateKey. RFC8032 5.1.6 */
|
||||
function sign(msg: Hex, privKey: Hex, options: { context?: Hex } = {}): Uint8Array {
|
||||
msg = ensureBytes('message', msg);
|
||||
if (prehash) msg = prehash(msg); // for ed25519ph etc.
|
||||
const { prefix, scalar, pointBytes } = getExtendedPublicKey(privKey);
|
||||
const r = hashDomainToScalar(options.context, prefix, msg); // r = dom2(F, C) || prefix || PH(M)
|
||||
const R = G.multiply(r).toRawBytes(); // R = rG
|
||||
const k = hashDomainToScalar(options.context, R, pointBytes, msg); // R || A || PH(M)
|
||||
const s = modN(r + k * scalar); // S = (r + k * s) mod L
|
||||
assertGE0(s); // 0 <= s < l
|
||||
const res = ut.concatBytes(R, ut.numberToBytesLE(s, Fp.BYTES));
|
||||
return ensureBytes('result', res, nByteLength * 2); // 64-byte signature
|
||||
}
|
||||
|
||||
const verifyOpts: { context?: Hex; zip215?: boolean } = VERIFY_DEFAULT;
|
||||
function verify(sig: Hex, msg: Hex, publicKey: Hex, options = verifyOpts): boolean {
|
||||
const { context, zip215 } = options;
|
||||
const len = Fp.BYTES; // Verifies EdDSA signature against message and public key. RFC8032 5.1.7.
|
||||
sig = ensureBytes('signature', sig, 2 * len); // An extended group equation is checked.
|
||||
msg = ensureBytes('message', msg);
|
||||
if (prehash) msg = prehash(msg); // for ed25519ph, etc
|
||||
|
||||
const s = ut.bytesToNumberLE(sig.slice(len, 2 * len));
|
||||
// zip215: true is good for consensus-critical apps and allows points < 2^256
|
||||
// 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 RkA = R.add(A.multiplyUnsafe(k));
|
||||
// [8][S]B = [8]R + [8][k]A'
|
||||
return RkA.subtract(SB).clearCofactor().equals(Point.ZERO);
|
||||
}
|
||||
|
||||
G._setWindowSize(8); // Enable precomputes. Slows down first publicKey computation by 20ms.
|
||||
|
||||
const utils = {
|
||||
getExtendedPublicKey,
|
||||
// ed25519 private keys are uniform 32b. No need to check for modulo bias, like in secp256k1.
|
||||
randomPrivateKey: (): Uint8Array => randomBytes(Fp.BYTES),
|
||||
|
||||
/**
|
||||
* We're doing scalar multiplication (used in getPublicKey etc) with precomputed BASE_POINT
|
||||
* values. This slows down first getPublicKey() by milliseconds (see Speed section),
|
||||
* but allows to speed-up subsequent getPublicKey() calls up to 20x.
|
||||
* @param windowSize 2, 4, 8, 16
|
||||
*/
|
||||
precompute(windowSize = 8, point = Point.BASE): typeof Point.BASE {
|
||||
point._setWindowSize(windowSize);
|
||||
point.multiply(BigInt(3));
|
||||
return point;
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
CURVE,
|
||||
getPublicKey,
|
||||
sign,
|
||||
verify,
|
||||
ExtendedPoint: Point,
|
||||
utils,
|
||||
};
|
||||
}
|
221
src/abstract/hash-to-curve.ts
Normal file
221
src/abstract/hash-to-curve.ts
Normal file
@ -0,0 +1,221 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
import type { Group, GroupConstructor, AffinePoint } from './curve.js';
|
||||
import { mod, IField } from './modular.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 = {
|
||||
DST: UnicodeOrBytes;
|
||||
p: bigint;
|
||||
m: number;
|
||||
k: number;
|
||||
expand: 'xmd' | 'xof';
|
||||
hash: CHash;
|
||||
};
|
||||
|
||||
// Octet Stream to Integer. "spec" implementation of os2ip is 2.5x slower vs bytesToNumberBE.
|
||||
const os2ip = bytesToNumberBE;
|
||||
|
||||
// Integer to Octet Stream (numberToBytesBE)
|
||||
function i2osp(value: number, length: number): Uint8Array {
|
||||
if (value < 0 || value >= 1 << (8 * length)) {
|
||||
throw new Error(`bad I2OSP call: value=${value} length=${length}`);
|
||||
}
|
||||
const res = Array.from({ length }).fill(0) as number[];
|
||||
for (let i = length - 1; i >= 0; i--) {
|
||||
res[i] = value & 0xff;
|
||||
value >>>= 8;
|
||||
}
|
||||
return new Uint8Array(res);
|
||||
}
|
||||
|
||||
function strxor(a: Uint8Array, b: Uint8Array): Uint8Array {
|
||||
const arr = new Uint8Array(a.length);
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
arr[i] = a[i] ^ b[i];
|
||||
}
|
||||
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
|
||||
// https://www.rfc-editor.org/rfc/rfc9380#section-5.3.1
|
||||
export function expand_message_xmd(
|
||||
msg: Uint8Array,
|
||||
DST: Uint8Array,
|
||||
lenInBytes: number,
|
||||
H: CHash
|
||||
): Uint8Array {
|
||||
abytes(msg);
|
||||
abytes(DST);
|
||||
anum(lenInBytes);
|
||||
// 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);
|
||||
if (ell > 255) throw new Error('Invalid xmd length');
|
||||
const DST_prime = concatBytes(DST, i2osp(DST.length, 1));
|
||||
const Z_pad = i2osp(0, r_in_bytes);
|
||||
const l_i_b_str = i2osp(lenInBytes, 2); // len_in_bytes_str
|
||||
const b = new Array<Uint8Array>(ell);
|
||||
const b_0 = H(concatBytes(Z_pad, msg, l_i_b_str, i2osp(0, 1), DST_prime));
|
||||
b[0] = H(concatBytes(b_0, i2osp(1, 1), DST_prime));
|
||||
for (let i = 1; i <= ell; i++) {
|
||||
const args = [strxor(b_0, b[i - 1]), i2osp(i + 1, 1), DST_prime];
|
||||
b[i] = H(concatBytes(...args));
|
||||
}
|
||||
const pseudo_random_bytes = concatBytes(...b);
|
||||
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(
|
||||
msg: Uint8Array,
|
||||
DST: Uint8Array,
|
||||
lenInBytes: number,
|
||||
k: number,
|
||||
H: CHash
|
||||
): Uint8Array {
|
||||
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));
|
||||
if (DST.length > 255) {
|
||||
const dkLen = Math.ceil((2 * k) / 8);
|
||||
DST = H.create({ dkLen }).update(utf8ToBytes('H2C-OVERSIZE-DST-')).update(DST).digest();
|
||||
}
|
||||
if (lenInBytes > 65535 || DST.length > 255)
|
||||
throw new Error('expand_message_xof: invalid lenInBytes');
|
||||
return (
|
||||
H.create({ dkLen: lenInBytes })
|
||||
.update(msg)
|
||||
.update(i2osp(lenInBytes, 2))
|
||||
// 2. DST_prime = DST || I2OSP(len(DST), 1)
|
||||
.update(DST)
|
||||
.update(i2osp(DST.length, 1))
|
||||
.digest()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hashes arbitrary-length byte strings to a list of one or more elements of a finite field F
|
||||
* https://www.rfc-editor.org/rfc/rfc9380#section-5.2
|
||||
* @param msg a byte string containing the message to hash
|
||||
* @param count the number of elements of F to output
|
||||
* @param options `{DST: string, p: bigint, m: number, k: number, expand: 'xmd' | 'xof', hash: H}`, see above
|
||||
* @returns [u_0, ..., u_(count - 1)], a list of field elements.
|
||||
*/
|
||||
export function hash_to_field(msg: Uint8Array, count: number, options: Opts): bigint[][] {
|
||||
validateObject(options, {
|
||||
DST: 'stringOrUint8Array',
|
||||
p: 'bigint',
|
||||
m: 'isSafeInteger',
|
||||
k: 'isSafeInteger',
|
||||
hash: 'hash',
|
||||
});
|
||||
const { p, k, m, hash, expand, DST: _DST } = options;
|
||||
abytes(msg);
|
||||
anum(count);
|
||||
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);
|
||||
for (let i = 0; i < count; i++) {
|
||||
const e = new Array(m);
|
||||
for (let j = 0; j < m; j++) {
|
||||
const elm_offset = L * (j + i * m);
|
||||
const tv = prb.subarray(elm_offset, elm_offset + L);
|
||||
e[j] = mod(os2ip(tv), p);
|
||||
}
|
||||
u[i] = e;
|
||||
}
|
||||
return u;
|
||||
}
|
||||
|
||||
export function isogenyMap<T, F extends IField<T>>(field: F, map: [T[], T[], T[], T[]]) {
|
||||
// Make same order as in spec
|
||||
const COEFF = map.map((i) => Array.from(i).reverse());
|
||||
return (x: T, y: T) => {
|
||||
const [xNum, xDen, yNum, yDen] = COEFF.map((val) =>
|
||||
val.reduce((acc, i) => field.add(field.mul(acc, x), i))
|
||||
);
|
||||
x = field.div(xNum, xDen); // xNum / xDen
|
||||
y = field.mul(y, field.div(yNum, yDen)); // y * (yNum / yDev)
|
||||
return { x, y };
|
||||
};
|
||||
}
|
||||
|
||||
export interface H2CPoint<T> extends Group<H2CPoint<T>> {
|
||||
add(rhs: H2CPoint<T>): H2CPoint<T>;
|
||||
toAffine(iz?: bigint): AffinePoint<T>;
|
||||
clearCofactor(): H2CPoint<T>;
|
||||
assertValidity(): void;
|
||||
}
|
||||
|
||||
export interface H2CPointConstructor<T> extends GroupConstructor<H2CPoint<T>> {
|
||||
fromAffine(ap: AffinePoint<T>): H2CPoint<T>;
|
||||
}
|
||||
|
||||
export type MapToCurve<T> = (scalar: bigint[]) => AffinePoint<T>;
|
||||
|
||||
// Separated from initialization opts, so users won't accidentally change per-curve parameters
|
||||
// (changing DST is ok!)
|
||||
export type htfBasicOpts = { DST: UnicodeOrBytes };
|
||||
|
||||
export function createHasher<T>(
|
||||
Point: H2CPointConstructor<T>,
|
||||
mapToCurve: MapToCurve<T>,
|
||||
def: Opts & { encodeDST?: UnicodeOrBytes }
|
||||
) {
|
||||
if (typeof mapToCurve !== 'function') throw new Error('mapToCurve() must be defined');
|
||||
return {
|
||||
// Encodes byte string to elliptic curve.
|
||||
// hash_to_curve from https://www.rfc-editor.org/rfc/rfc9380#section-3
|
||||
hashToCurve(msg: Uint8Array, options?: htfBasicOpts) {
|
||||
const u = hash_to_field(msg, 2, { ...def, DST: def.DST, ...options } as Opts);
|
||||
const u0 = Point.fromAffine(mapToCurve(u[0]));
|
||||
const u1 = Point.fromAffine(mapToCurve(u[1]));
|
||||
const P = u0.add(u1).clearCofactor();
|
||||
P.assertValidity();
|
||||
return P;
|
||||
},
|
||||
|
||||
// Encodes byte string to elliptic curve.
|
||||
// encode_to_curve from https://www.rfc-editor.org/rfc/rfc9380#section-3
|
||||
encodeToCurve(msg: Uint8Array, options?: htfBasicOpts) {
|
||||
const u = hash_to_field(msg, 1, { ...def, DST: def.encodeDST, ...options } as Opts);
|
||||
const P = Point.fromAffine(mapToCurve(u[0])).clearCofactor();
|
||||
P.assertValidity();
|
||||
return P;
|
||||
},
|
||||
};
|
||||
}
|
484
src/abstract/modular.ts
Normal file
484
src/abstract/modular.ts
Normal file
@ -0,0 +1,484 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
// Utilities for modular arithmetics and finite fields
|
||||
import {
|
||||
bitMask,
|
||||
numberToBytesBE,
|
||||
numberToBytesLE,
|
||||
bytesToNumberBE,
|
||||
bytesToNumberLE,
|
||||
ensureBytes,
|
||||
validateObject,
|
||||
} from './utils.js';
|
||||
// prettier-ignore
|
||||
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3);
|
||||
// prettier-ignore
|
||||
const _4n = BigInt(4), _5n = BigInt(5), _8n = BigInt(8);
|
||||
// prettier-ignore
|
||||
const _9n = BigInt(9), _16n = BigInt(16);
|
||||
|
||||
// Calculates a modulo b
|
||||
export function mod(a: bigint, b: bigint): bigint {
|
||||
const result = a % b;
|
||||
return result >= _0n ? result : b + result;
|
||||
}
|
||||
/**
|
||||
* Efficiently raise num to power and do modular division.
|
||||
* Unsafe in some contexts: uses ladder, so can expose bigint bits.
|
||||
* @example
|
||||
* pow(2n, 6n, 11n) // 64n % 11n == 9n
|
||||
*/
|
||||
// TODO: use field version && remove
|
||||
export function pow(num: bigint, power: bigint, modulo: bigint): bigint {
|
||||
if (modulo <= _0n || power < _0n) throw new Error('Expected power/modulo > 0');
|
||||
if (modulo === _1n) return _0n;
|
||||
let res = _1n;
|
||||
while (power > _0n) {
|
||||
if (power & _1n) res = (res * num) % modulo;
|
||||
num = (num * num) % modulo;
|
||||
power >>= _1n;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// Does x ^ (2 ^ power) mod p. pow2(30, 4) == 30 ^ (2 ^ 4)
|
||||
export function pow2(x: bigint, power: bigint, modulo: bigint): bigint {
|
||||
let res = x;
|
||||
while (power-- > _0n) {
|
||||
res *= res;
|
||||
res %= modulo;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// Inverses number over modulo
|
||||
export function invert(number: bigint, modulo: bigint): bigint {
|
||||
if (number === _0n || modulo <= _0n) {
|
||||
throw new Error(`invert: expected positive integers, got n=${number} mod=${modulo}`);
|
||||
}
|
||||
// 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 b = modulo;
|
||||
// prettier-ignore
|
||||
let x = _0n, y = _1n, u = _1n, v = _0n;
|
||||
while (a !== _0n) {
|
||||
// JIT applies optimization if those two lines follow each other
|
||||
const q = b / a;
|
||||
const r = b % a;
|
||||
const m = x - u * q;
|
||||
const n = y - v * q;
|
||||
// prettier-ignore
|
||||
b = a, a = r, x = u, y = v, u = m, v = n;
|
||||
}
|
||||
const gcd = b;
|
||||
if (gcd !== _1n) throw new Error('invert: does not exist');
|
||||
return mod(x, modulo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tonelli-Shanks square root search algorithm.
|
||||
* 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) {
|
||||
// Legendre constant: used to calculate Legendre symbol (a | p),
|
||||
// which denotes the value of a^((p-1)/2) (mod p).
|
||||
// (a | p) ≡ 1 if a is a square (mod p)
|
||||
// (a | p) ≡ -1 if a is not a square (mod p)
|
||||
// (a | p) ≡ 0 if a ≡ 0 (mod p)
|
||||
const legendreC = (P - _1n) / _2n;
|
||||
|
||||
let Q: bigint, S: number, Z: bigint;
|
||||
// Step 1: By factoring out powers of 2 from p - 1,
|
||||
// find q and s such that p - 1 = q*(2^s) with q odd
|
||||
for (Q = P - _1n, S = 0; Q % _2n === _0n; Q /= _2n, S++);
|
||||
|
||||
// Step 2: Select a non-square z such that (z | p) ≡ -1 and set c ≡ zq
|
||||
for (Z = _2n; Z < P && pow(Z, legendreC, P) !== P - _1n; Z++);
|
||||
|
||||
// Fast-path
|
||||
if (S === 1) {
|
||||
const p1div4 = (P + _1n) / _4n;
|
||||
return function tonelliFast<T>(Fp: IField<T>, n: T) {
|
||||
const root = Fp.pow(n, p1div4);
|
||||
if (!Fp.eql(Fp.sqr(root), n)) throw new Error('Cannot find square root');
|
||||
return root;
|
||||
};
|
||||
}
|
||||
|
||||
// Slow-path
|
||||
const Q1div2 = (Q + _1n) / _2n;
|
||||
return function tonelliSlow<T>(Fp: IField<T>, n: T): T {
|
||||
// Step 0: Check that n is indeed a square: (n | p) should not be ≡ -1
|
||||
if (Fp.pow(n, legendreC) === Fp.neg(Fp.ONE)) throw new Error('Cannot find square root');
|
||||
let r = S;
|
||||
// TODO: will fail at Fp2/etc
|
||||
let g = Fp.pow(Fp.mul(Fp.ONE, Z), Q); // will update both x and b
|
||||
let x = Fp.pow(n, Q1div2); // first guess at the square root
|
||||
let b = Fp.pow(n, Q); // first guess at the fudge factor
|
||||
|
||||
while (!Fp.eql(b, Fp.ONE)) {
|
||||
if (Fp.eql(b, Fp.ZERO)) return Fp.ZERO; // https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm (4. If t = 0, return r = 0)
|
||||
// Find m such b^(2^m)==1
|
||||
let m = 1;
|
||||
for (let t2 = Fp.sqr(b); m < r; m++) {
|
||||
if (Fp.eql(t2, Fp.ONE)) break;
|
||||
t2 = Fp.sqr(t2); // t2 *= t2
|
||||
}
|
||||
// NOTE: r-m-1 can be bigger than 32, need to convert to bigint before shift, otherwise there will be overflow
|
||||
const ge = Fp.pow(g, _1n << BigInt(r - m - 1)); // ge = 2^(r-m-1)
|
||||
g = Fp.sqr(ge); // g = ge * ge
|
||||
x = Fp.mul(x, ge); // x *= ge
|
||||
b = Fp.mul(b, g); // b *= g
|
||||
r = m;
|
||||
}
|
||||
return x;
|
||||
};
|
||||
}
|
||||
|
||||
export function FpSqrt(P: bigint) {
|
||||
// NOTE: different algorithms can give different roots, it is up to user to decide which one they want.
|
||||
// For example there is FpSqrtOdd/FpSqrtEven to choice root based on oddness (used for hash-to-curve).
|
||||
|
||||
// P ≡ 3 (mod 4)
|
||||
// √n = n^((P+1)/4)
|
||||
if (P % _4n === _3n) {
|
||||
// Not all roots possible!
|
||||
// const ORDER =
|
||||
// 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaabn;
|
||||
// const NUM = 72057594037927816n;
|
||||
const p1div4 = (P + _1n) / _4n;
|
||||
return function sqrt3mod4<T>(Fp: IField<T>, n: T) {
|
||||
const root = Fp.pow(n, p1div4);
|
||||
// Throw if root**2 != n
|
||||
if (!Fp.eql(Fp.sqr(root), n)) throw new Error('Cannot find square root');
|
||||
return root;
|
||||
};
|
||||
}
|
||||
|
||||
// Atkin algorithm for q ≡ 5 (mod 8), https://eprint.iacr.org/2012/685.pdf (page 10)
|
||||
if (P % _8n === _5n) {
|
||||
const c1 = (P - _5n) / _8n;
|
||||
return function sqrt5mod8<T>(Fp: IField<T>, n: T) {
|
||||
const n2 = Fp.mul(n, _2n);
|
||||
const v = Fp.pow(n2, c1);
|
||||
const nv = Fp.mul(n, v);
|
||||
const i = Fp.mul(Fp.mul(nv, _2n), v);
|
||||
const root = Fp.mul(nv, Fp.sub(i, Fp.ONE));
|
||||
if (!Fp.eql(Fp.sqr(root), n)) throw new Error('Cannot find square root');
|
||||
return root;
|
||||
};
|
||||
}
|
||||
|
||||
// P ≡ 9 (mod 16)
|
||||
if (P % _16n === _9n) {
|
||||
// NOTE: tonelli is too slow for bls-Fp2 calculations even on start
|
||||
// Means we cannot use sqrt for constants at all!
|
||||
//
|
||||
// const c1 = Fp.sqrt(Fp.negate(Fp.ONE)); // 1. c1 = sqrt(-1) in F, i.e., (c1^2) == -1 in F
|
||||
// const c2 = Fp.sqrt(c1); // 2. c2 = sqrt(c1) in F, i.e., (c2^2) == c1 in F
|
||||
// const c3 = Fp.sqrt(Fp.negate(c1)); // 3. c3 = sqrt(-c1) in F, i.e., (c3^2) == -c1 in F
|
||||
// const c4 = (P + _7n) / _16n; // 4. c4 = (q + 7) / 16 # Integer arithmetic
|
||||
// sqrt = (x) => {
|
||||
// let tv1 = Fp.pow(x, c4); // 1. tv1 = x^c4
|
||||
// let tv2 = Fp.mul(c1, tv1); // 2. tv2 = c1 * tv1
|
||||
// const tv3 = Fp.mul(c2, tv1); // 3. tv3 = c2 * tv1
|
||||
// let tv4 = Fp.mul(c3, tv1); // 4. tv4 = c3 * tv1
|
||||
// const e1 = Fp.equals(Fp.square(tv2), x); // 5. e1 = (tv2^2) == x
|
||||
// const e2 = Fp.equals(Fp.square(tv3), x); // 6. e2 = (tv3^2) == x
|
||||
// tv1 = Fp.cmov(tv1, tv2, e1); // 7. tv1 = CMOV(tv1, tv2, e1) # Select tv2 if (tv2^2) == x
|
||||
// tv2 = Fp.cmov(tv4, tv3, e2); // 8. tv2 = CMOV(tv4, tv3, e2) # Select tv3 if (tv3^2) == x
|
||||
// const e3 = Fp.equals(Fp.square(tv2), x); // 9. e3 = (tv2^2) == x
|
||||
// return Fp.cmov(tv1, tv2, e3); // 10. z = CMOV(tv1, tv2, e3) # Select the sqrt from tv1 and tv2
|
||||
// }
|
||||
}
|
||||
|
||||
// Other cases: Tonelli-Shanks algorithm
|
||||
return tonelliShanks(P);
|
||||
}
|
||||
|
||||
// Little-endian check for first LE bit (last BE bit);
|
||||
export const isNegativeLE = (num: bigint, modulo: bigint) => (mod(num, modulo) & _1n) === _1n;
|
||||
|
||||
// Field is not always over prime: for example, Fp2 has ORDER(q)=p^m
|
||||
export interface IField<T> {
|
||||
ORDER: bigint;
|
||||
BYTES: number;
|
||||
BITS: number;
|
||||
MASK: bigint;
|
||||
ZERO: T;
|
||||
ONE: T;
|
||||
// 1-arg
|
||||
create: (num: T) => T;
|
||||
isValid: (num: T) => boolean;
|
||||
is0: (num: T) => boolean;
|
||||
neg(num: T): T;
|
||||
inv(num: T): T;
|
||||
sqrt(num: T): T;
|
||||
sqr(num: T): T;
|
||||
// 2-args
|
||||
eql(lhs: T, rhs: T): boolean;
|
||||
add(lhs: T, rhs: T): T;
|
||||
sub(lhs: T, rhs: T): T;
|
||||
mul(lhs: T, rhs: T | bigint): T;
|
||||
pow(lhs: T, power: bigint): T;
|
||||
div(lhs: T, rhs: T | bigint): T;
|
||||
// N for NonNormalized (for now)
|
||||
addN(lhs: T, rhs: T): T;
|
||||
subN(lhs: T, rhs: T): T;
|
||||
mulN(lhs: T, rhs: T | bigint): T;
|
||||
sqrN(num: T): T;
|
||||
|
||||
// Optional
|
||||
// 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.
|
||||
isOdd?(num: T): boolean; // Odd instead of even since we have it for Fp2
|
||||
// legendre?(num: T): T;
|
||||
pow(lhs: T, power: bigint): T;
|
||||
invertBatch: (lst: T[]) => T[];
|
||||
toBytes(num: T): Uint8Array;
|
||||
fromBytes(bytes: Uint8Array): T;
|
||||
// If c is False, CMOV returns a, otherwise it returns b.
|
||||
cmov(a: T, b: T, c: boolean): T;
|
||||
}
|
||||
// prettier-ignore
|
||||
const FIELD_FIELDS = [
|
||||
'create', 'isValid', 'is0', 'neg', 'inv', 'sqrt', 'sqr',
|
||||
'eql', 'add', 'sub', 'mul', 'pow', 'div',
|
||||
'addN', 'subN', 'mulN', 'sqrN'
|
||||
] as const;
|
||||
export function validateField<T>(field: IField<T>) {
|
||||
const initial = {
|
||||
ORDER: 'bigint',
|
||||
MASK: 'bigint',
|
||||
BYTES: 'isSafeInteger',
|
||||
BITS: 'isSafeInteger',
|
||||
} as Record<string, string>;
|
||||
const opts = FIELD_FIELDS.reduce((map, val: string) => {
|
||||
map[val] = 'function';
|
||||
return map;
|
||||
}, initial);
|
||||
return validateObject(field, opts);
|
||||
}
|
||||
|
||||
// Generic field functions
|
||||
|
||||
/**
|
||||
* 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
|
||||
// TODO: benchmark!
|
||||
if (power < _0n) throw new Error('Expected power > 0');
|
||||
if (power === _0n) return f.ONE;
|
||||
if (power === _1n) return num;
|
||||
let p = f.ONE;
|
||||
let d = num;
|
||||
while (power > _0n) {
|
||||
if (power & _1n) p = f.mul(p, d);
|
||||
d = f.sqr(d);
|
||||
power >>= _1n;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
// Walk from first to last, multiply them by each other MOD p
|
||||
const lastMultiplied = nums.reduce((acc, num, i) => {
|
||||
if (f.is0(num)) return acc;
|
||||
tmp[i] = acc;
|
||||
return f.mul(acc, num);
|
||||
}, f.ONE);
|
||||
// Invert last element
|
||||
const inverted = f.inv(lastMultiplied);
|
||||
// Walk from last to first, multiply them by inverted each other MOD p
|
||||
nums.reduceRight((acc, num, i) => {
|
||||
if (f.is0(num)) return acc;
|
||||
tmp[i] = f.mul(acc, tmp[i]);
|
||||
return f.mul(acc, num);
|
||||
}, inverted);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
// This function returns True whenever the value x is a square in the field F.
|
||||
export function FpIsSquare<T>(f: IField<T>) {
|
||||
const legendreConst = (f.ORDER - _1n) / _2n; // Integer arithmetic
|
||||
return (x: T): boolean => {
|
||||
const p = f.pow(x, legendreConst);
|
||||
return f.eql(p, f.ZERO) || f.eql(p, f.ONE);
|
||||
};
|
||||
}
|
||||
|
||||
// CURVE.n lengths
|
||||
export function nLength(n: bigint, nBitLength?: number) {
|
||||
// Bit size, byte size of CURVE.n
|
||||
const _nBitLength = nBitLength !== undefined ? nBitLength : n.toString(2).length;
|
||||
const nByteLength = Math.ceil(_nBitLength / 8);
|
||||
return { nBitLength: _nBitLength, nByteLength };
|
||||
}
|
||||
|
||||
type FpField = IField<bigint> & Required<Pick<IField<bigint>, 'isOdd'>>;
|
||||
/**
|
||||
* Initializes a finite field over prime. **Non-primes are not supported.**
|
||||
* Do not init in loop: slow. Very fragile: always run a benchmark on a change.
|
||||
* Major performance optimizations:
|
||||
* * 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,
|
||||
bitLen?: number,
|
||||
isLE = false,
|
||||
redef: Partial<IField<bigint>> = {}
|
||||
): Readonly<FpField> {
|
||||
if (ORDER <= _0n) throw new Error(`Expected Field ORDER > 0, got ${ORDER}`);
|
||||
const { nBitLength: BITS, nByteLength: BYTES } = nLength(ORDER, bitLen);
|
||||
if (BYTES > 2048) throw new Error('Field lengths over 2048 bytes are not supported');
|
||||
const sqrtP = FpSqrt(ORDER);
|
||||
const f: Readonly<FpField> = Object.freeze({
|
||||
ORDER,
|
||||
BITS,
|
||||
BYTES,
|
||||
MASK: bitMask(BITS),
|
||||
ZERO: _0n,
|
||||
ONE: _1n,
|
||||
create: (num) => mod(num, ORDER),
|
||||
isValid: (num) => {
|
||||
if (typeof num !== 'bigint')
|
||||
throw new Error(`Invalid field element: expected bigint, got ${typeof num}`);
|
||||
return _0n <= num && num < ORDER; // 0 is valid element, but it's not invertible
|
||||
},
|
||||
is0: (num) => num === _0n,
|
||||
isOdd: (num) => (num & _1n) === _1n,
|
||||
neg: (num) => mod(-num, ORDER),
|
||||
eql: (lhs, rhs) => lhs === rhs,
|
||||
|
||||
sqr: (num) => mod(num * num, ORDER),
|
||||
add: (lhs, rhs) => mod(lhs + rhs, ORDER),
|
||||
sub: (lhs, rhs) => mod(lhs - rhs, ORDER),
|
||||
mul: (lhs, rhs) => mod(lhs * rhs, ORDER),
|
||||
pow: (num, power) => FpPow(f, num, power),
|
||||
div: (lhs, rhs) => mod(lhs * invert(rhs, ORDER), ORDER),
|
||||
|
||||
// Same as above, but doesn't normalize
|
||||
sqrN: (num) => num * num,
|
||||
addN: (lhs, rhs) => lhs + rhs,
|
||||
subN: (lhs, rhs) => lhs - rhs,
|
||||
mulN: (lhs, rhs) => lhs * rhs,
|
||||
|
||||
inv: (num) => invert(num, ORDER),
|
||||
sqrt: redef.sqrt || ((n) => sqrtP(f, n)),
|
||||
invertBatch: (lst) => FpInvertBatch(f, lst),
|
||||
// TODO: do we really need constant cmov?
|
||||
// We don't have const-time bigints anyway, so probably will be not very useful
|
||||
cmov: (a, b, c) => (c ? b : a),
|
||||
toBytes: (num) => (isLE ? numberToBytesLE(num, BYTES) : numberToBytesBE(num, BYTES)),
|
||||
fromBytes: (bytes) => {
|
||||
if (bytes.length !== BYTES)
|
||||
throw new Error(`Fp.fromBytes: expected ${BYTES}, got ${bytes.length}`);
|
||||
return isLE ? bytesToNumberLE(bytes) : bytesToNumberBE(bytes);
|
||||
},
|
||||
} as FpField);
|
||||
return Object.freeze(f);
|
||||
}
|
||||
|
||||
export function FpSqrtOdd<T>(Fp: IField<T>, elm: T) {
|
||||
if (!Fp.isOdd) throw new Error(`Field doesn't have isOdd`);
|
||||
const root = Fp.sqrt(elm);
|
||||
return Fp.isOdd(root) ? root : Fp.neg(root);
|
||||
}
|
||||
|
||||
export function FpSqrtEven<T>(Fp: IField<T>, elm: T) {
|
||||
if (!Fp.isOdd) throw new Error(`Field doesn't have isOdd`);
|
||||
const root = Fp.sqrt(elm);
|
||||
return Fp.isOdd(root) ? Fp.neg(root) : root;
|
||||
}
|
||||
|
||||
/**
|
||||
* "Constant-time" private key generation utility.
|
||||
* Same as mapKeyToField, but accepts less bytes (40 instead of 48 for 32-byte field).
|
||||
* Which makes it slightly more biased, less secure.
|
||||
* @deprecated use mapKeyToField instead
|
||||
*/
|
||||
export function hashToPrivateScalar(
|
||||
hash: string | Uint8Array,
|
||||
groupOrder: bigint,
|
||||
isLE = false
|
||||
): bigint {
|
||||
hash = ensureBytes('privateHash', hash);
|
||||
const hashLen = hash.length;
|
||||
const minLen = nLength(groupOrder).nByteLength + 8;
|
||||
if (minLen < 24 || hashLen < minLen || hashLen > 1024)
|
||||
throw new Error(`hashToPrivateScalar: expected ${minLen}-1024 bytes of input, got ${hashLen}`);
|
||||
const num = isLE ? bytesToNumberLE(hash) : bytesToNumberBE(hash);
|
||||
return mod(num, groupOrder - _1n) + _1n;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
187
src/abstract/montgomery.ts
Normal file
187
src/abstract/montgomery.ts
Normal file
@ -0,0 +1,187 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
import { mod, pow } from './modular.js';
|
||||
import { bytesToNumberLE, ensureBytes, numberToBytesLE, validateObject } from './utils.js';
|
||||
|
||||
const _0n = BigInt(0);
|
||||
const _1n = BigInt(1);
|
||||
type Hex = string | Uint8Array;
|
||||
|
||||
export type CurveType = {
|
||||
P: bigint; // finite field prime
|
||||
nByteLength: number;
|
||||
adjustScalarBytes?: (bytes: Uint8Array) => Uint8Array;
|
||||
domain?: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array;
|
||||
a: bigint;
|
||||
montgomeryBits: number;
|
||||
powPminus2?: (x: bigint) => bigint;
|
||||
xyToU?: (x: bigint, y: bigint) => bigint;
|
||||
Gu: bigint;
|
||||
randomBytes?: (bytesLength?: number) => Uint8Array;
|
||||
};
|
||||
export type CurveFn = {
|
||||
scalarMult: (scalar: Hex, u: Hex) => Uint8Array;
|
||||
scalarMultBase: (scalar: Hex) => Uint8Array;
|
||||
getSharedSecret: (privateKeyA: Hex, publicKeyB: Hex) => Uint8Array;
|
||||
getPublicKey: (privateKey: Hex) => Uint8Array;
|
||||
utils: { randomPrivateKey: () => Uint8Array };
|
||||
GuBytes: Uint8Array;
|
||||
};
|
||||
|
||||
function validateOpts(curve: CurveType) {
|
||||
validateObject(
|
||||
curve,
|
||||
{
|
||||
a: 'bigint',
|
||||
},
|
||||
{
|
||||
montgomeryBits: 'isSafeInteger',
|
||||
nByteLength: 'isSafeInteger',
|
||||
adjustScalarBytes: 'function',
|
||||
domain: 'function',
|
||||
powPminus2: 'function',
|
||||
Gu: 'bigint',
|
||||
}
|
||||
);
|
||||
// Set defaults
|
||||
return Object.freeze({ ...curve } as const);
|
||||
}
|
||||
|
||||
// NOTE: not really montgomery curve, just bunch of very specific methods for X25519/X448 (RFC 7748, https://www.rfc-editor.org/rfc/rfc7748)
|
||||
// Uses only one coordinate instead of two
|
||||
export function montgomery(curveDef: CurveType): CurveFn {
|
||||
const CURVE = validateOpts(curveDef);
|
||||
const { P } = CURVE;
|
||||
const modP = (n: bigint) => mod(n, P);
|
||||
const montgomeryBits = CURVE.montgomeryBits;
|
||||
const montgomeryBytes = Math.ceil(montgomeryBits / 8);
|
||||
const fieldLen = CURVE.nByteLength;
|
||||
const adjustScalarBytes = CURVE.adjustScalarBytes || ((bytes: Uint8Array) => bytes);
|
||||
const powPminus2 = CURVE.powPminus2 || ((x: bigint) => pow(x, P - BigInt(2), P));
|
||||
|
||||
// cswap from RFC7748. But it is not from RFC7748!
|
||||
/*
|
||||
cswap(swap, x_2, x_3):
|
||||
dummy = mask(swap) AND (x_2 XOR x_3)
|
||||
x_2 = x_2 XOR dummy
|
||||
x_3 = x_3 XOR dummy
|
||||
Return (x_2, x_3)
|
||||
Where mask(swap) is the all-1 or all-0 word of the same length as x_2
|
||||
and x_3, computed, e.g., as mask(swap) = 0 - swap.
|
||||
*/
|
||||
function cswap(swap: bigint, x_2: bigint, x_3: bigint): [bigint, bigint] {
|
||||
const dummy = modP(swap * (x_2 - x_3));
|
||||
x_2 = modP(x_2 - dummy);
|
||||
x_3 = modP(x_3 + dummy);
|
||||
return [x_2, x_3];
|
||||
}
|
||||
|
||||
// Accepts 0 as well
|
||||
function assertFieldElement(n: bigint): bigint {
|
||||
if (typeof n === 'bigint' && _0n <= n && n < P) return n;
|
||||
throw new Error('Expected valid scalar 0 < scalar < CURVE.P');
|
||||
}
|
||||
|
||||
// x25519 from 4
|
||||
// The constant a24 is (486662 - 2) / 4 = 121665 for curve25519/X25519
|
||||
const a24 = (CURVE.a - BigInt(2)) / BigInt(4);
|
||||
/**
|
||||
*
|
||||
* @param pointU u coordinate (x) on Montgomery Curve 25519
|
||||
* @param scalar by which the point would be multiplied
|
||||
* @returns new Point on Montgomery curve
|
||||
*/
|
||||
function montgomeryLadder(pointU: bigint, scalar: bigint): bigint {
|
||||
const u = assertFieldElement(pointU);
|
||||
// Section 5: Implementations MUST accept non-canonical values and process them as
|
||||
// if they had been reduced modulo the field prime.
|
||||
const k = assertFieldElement(scalar);
|
||||
const x_1 = u;
|
||||
let x_2 = _1n;
|
||||
let z_2 = _0n;
|
||||
let x_3 = u;
|
||||
let z_3 = _1n;
|
||||
let swap = _0n;
|
||||
let sw: [bigint, bigint];
|
||||
for (let t = BigInt(montgomeryBits - 1); t >= _0n; t--) {
|
||||
const k_t = (k >> t) & _1n;
|
||||
swap ^= k_t;
|
||||
sw = cswap(swap, x_2, x_3);
|
||||
x_2 = sw[0];
|
||||
x_3 = sw[1];
|
||||
sw = cswap(swap, z_2, z_3);
|
||||
z_2 = sw[0];
|
||||
z_3 = sw[1];
|
||||
swap = k_t;
|
||||
|
||||
const A = x_2 + z_2;
|
||||
const AA = modP(A * A);
|
||||
const B = x_2 - z_2;
|
||||
const BB = modP(B * B);
|
||||
const E = AA - BB;
|
||||
const C = x_3 + z_3;
|
||||
const D = x_3 - z_3;
|
||||
const DA = modP(D * A);
|
||||
const CB = modP(C * B);
|
||||
const dacb = DA + CB;
|
||||
const da_cb = DA - CB;
|
||||
x_3 = modP(dacb * dacb);
|
||||
z_3 = modP(x_1 * modP(da_cb * da_cb));
|
||||
x_2 = modP(AA * BB);
|
||||
z_2 = modP(E * (AA + modP(a24 * E)));
|
||||
}
|
||||
// (x_2, x_3) = cswap(swap, x_2, x_3)
|
||||
sw = cswap(swap, x_2, x_3);
|
||||
x_2 = sw[0];
|
||||
x_3 = sw[1];
|
||||
// (z_2, z_3) = cswap(swap, z_2, z_3)
|
||||
sw = cswap(swap, z_2, z_3);
|
||||
z_2 = sw[0];
|
||||
z_3 = sw[1];
|
||||
// z_2^(p - 2)
|
||||
const z2 = powPminus2(z_2);
|
||||
// Return x_2 * (z_2^(p - 2))
|
||||
return modP(x_2 * z2);
|
||||
}
|
||||
|
||||
function encodeUCoordinate(u: bigint): Uint8Array {
|
||||
return numberToBytesLE(modP(u), montgomeryBytes);
|
||||
}
|
||||
|
||||
function decodeUCoordinate(uEnc: Hex): bigint {
|
||||
// Section 5: When receiving such an array, implementations of X25519
|
||||
// MUST mask the most significant bit in the final byte.
|
||||
const u = ensureBytes('u coordinate', uEnc, montgomeryBytes);
|
||||
if (fieldLen === 32) u[31] &= 127; // 0b0111_1111
|
||||
return bytesToNumberLE(u);
|
||||
}
|
||||
function decodeScalar(n: Hex): bigint {
|
||||
const bytes = ensureBytes('scalar', n);
|
||||
const len = bytes.length;
|
||||
if (len !== montgomeryBytes && len !== fieldLen)
|
||||
throw new Error(`Expected ${montgomeryBytes} or ${fieldLen} bytes, got ${len}`);
|
||||
return bytesToNumberLE(adjustScalarBytes(bytes));
|
||||
}
|
||||
function scalarMult(scalar: Hex, u: Hex): Uint8Array {
|
||||
const pointU = decodeUCoordinate(u);
|
||||
const _scalar = decodeScalar(scalar);
|
||||
const pu = montgomeryLadder(pointU, _scalar);
|
||||
// The result was not contributory
|
||||
// https://cr.yp.to/ecdh.html#validate
|
||||
if (pu === _0n) throw new Error('Invalid private or public key received');
|
||||
return encodeUCoordinate(pu);
|
||||
}
|
||||
// Computes public key from private. By doing scalar multiplication of base point.
|
||||
const GuBytes = encodeUCoordinate(CURVE.Gu);
|
||||
function scalarMultBase(scalar: Hex): Uint8Array {
|
||||
return scalarMult(scalar, GuBytes);
|
||||
}
|
||||
|
||||
return {
|
||||
scalarMult,
|
||||
scalarMultBase,
|
||||
getSharedSecret: (privateKey: Hex, publicKey: Hex) => scalarMult(privateKey, publicKey),
|
||||
getPublicKey: (privateKey: Hex): Uint8Array => scalarMultBase(privateKey),
|
||||
utils: { randomPrivateKey: () => CURVE.randomBytes!(CURVE.nByteLength) },
|
||||
GuBytes: GuBytes,
|
||||
};
|
||||
}
|
118
src/abstract/poseidon.ts
Normal file
118
src/abstract/poseidon.ts
Normal file
@ -0,0 +1,118 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
// Poseidon Hash: https://eprint.iacr.org/2019/458.pdf, https://www.poseidon-hash.info
|
||||
import { IField, FpPow, validateField } from './modular.js';
|
||||
// We don't provide any constants, since different implementations use different constants.
|
||||
// For reference constants see './test/poseidon.test.js'.
|
||||
export type PoseidonOpts = {
|
||||
Fp: IField<bigint>;
|
||||
t: number;
|
||||
roundsFull: number;
|
||||
roundsPartial: number;
|
||||
sboxPower?: number;
|
||||
reversePartialPowIdx?: boolean; // Hack for stark
|
||||
mds: bigint[][];
|
||||
roundConstants: bigint[][];
|
||||
};
|
||||
|
||||
export function validateOpts(opts: PoseidonOpts) {
|
||||
const { Fp, mds, reversePartialPowIdx: rev, roundConstants: rc } = opts;
|
||||
const { roundsFull, roundsPartial, sboxPower, t } = opts;
|
||||
|
||||
validateField(Fp);
|
||||
for (const i of ['t', 'roundsFull', 'roundsPartial'] as const) {
|
||||
if (typeof opts[i] !== 'number' || !Number.isSafeInteger(opts[i]))
|
||||
throw new Error(`Poseidon: invalid param ${i}=${opts[i]} (${typeof opts[i]})`);
|
||||
}
|
||||
|
||||
// MDS is TxT matrix
|
||||
if (!Array.isArray(mds) || mds.length !== t) throw new Error('Poseidon: wrong MDS matrix');
|
||||
const _mds = mds.map((mdsRow) => {
|
||||
if (!Array.isArray(mdsRow) || mdsRow.length !== t)
|
||||
throw new Error(`Poseidon MDS matrix row: ${mdsRow}`);
|
||||
return mdsRow.map((i) => {
|
||||
if (typeof i !== 'bigint') throw new Error(`Poseidon MDS matrix value=${i}`);
|
||||
return Fp.create(i);
|
||||
});
|
||||
});
|
||||
|
||||
if (rev !== undefined && typeof rev !== 'boolean')
|
||||
throw new Error(`Poseidon: invalid param reversePartialPowIdx=${rev}`);
|
||||
|
||||
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');
|
||||
const roundConstants = rc.map((rc) => {
|
||||
if (!Array.isArray(rc) || rc.length !== t)
|
||||
throw new Error(`Poseidon wrong round constants: ${rc}`);
|
||||
return rc.map((i) => {
|
||||
if (typeof i !== 'bigint' || !Fp.isValid(i))
|
||||
throw new Error(`Poseidon wrong round constant=${i}`);
|
||||
return Fp.create(i);
|
||||
});
|
||||
});
|
||||
|
||||
if (!sboxPower || ![3, 5, 7].includes(sboxPower))
|
||||
throw new Error(`Poseidon wrong sboxPower=${sboxPower}`);
|
||||
const _sboxPower = BigInt(sboxPower);
|
||||
let sboxFn = (n: bigint) => FpPow(Fp, n, _sboxPower);
|
||||
// Unwrapped sbox power for common cases (195->142μs)
|
||||
if (sboxPower === 3) sboxFn = (n: bigint) => Fp.mul(Fp.sqrN(n), n);
|
||||
else if (sboxPower === 5) sboxFn = (n: bigint) => Fp.mul(Fp.sqrN(Fp.sqrN(n)), n);
|
||||
|
||||
return Object.freeze({ ...opts, rounds, sboxFn, roundConstants, mds: _mds });
|
||||
}
|
||||
|
||||
export function splitConstants(rc: bigint[], t: number) {
|
||||
if (typeof t !== 'number') throw new Error('poseidonSplitConstants: wrong t');
|
||||
if (!Array.isArray(rc) || rc.length % t) throw new Error('poseidonSplitConstants: wrong rc');
|
||||
const res = [];
|
||||
let tmp = [];
|
||||
for (let i = 0; i < rc.length; i++) {
|
||||
tmp.push(rc[i]);
|
||||
if (tmp.length === t) {
|
||||
res.push(tmp);
|
||||
tmp = [];
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
export function poseidon(opts: PoseidonOpts) {
|
||||
const _opts = validateOpts(opts);
|
||||
const { Fp, mds, roundConstants, rounds, roundsPartial, sboxFn, t } = _opts;
|
||||
const halfRoundsFull = _opts.roundsFull / 2;
|
||||
const partialIdx = _opts.reversePartialPowIdx ? t - 1 : 0;
|
||||
const poseidonRound = (values: bigint[], isFull: boolean, idx: number) => {
|
||||
values = values.map((i, j) => Fp.add(i, roundConstants[idx][j]));
|
||||
|
||||
if (isFull) values = values.map((i) => sboxFn(i));
|
||||
else values[partialIdx] = sboxFn(values[partialIdx]);
|
||||
// Matrix multiplication
|
||||
values = mds.map((i) => i.reduce((acc, i, j) => Fp.add(acc, Fp.mulN(i, values[j])), Fp.ZERO));
|
||||
return values;
|
||||
};
|
||||
const poseidonHash = function poseidonHash(values: bigint[]) {
|
||||
if (!Array.isArray(values) || values.length !== t)
|
||||
throw new Error(`Poseidon: wrong values (expected array of bigints with length ${t})`);
|
||||
values = values.map((i) => {
|
||||
if (typeof i !== 'bigint') throw new Error(`Poseidon: wrong value=${i} (${typeof i})`);
|
||||
return Fp.create(i);
|
||||
});
|
||||
let round = 0;
|
||||
// Apply r_f/2 full rounds.
|
||||
for (let i = 0; i < halfRoundsFull; i++) values = poseidonRound(values, true, round++);
|
||||
// Apply r_p partial rounds.
|
||||
for (let i = 0; i < roundsPartial; i++) values = poseidonRound(values, false, round++);
|
||||
// Apply r_f/2 full rounds.
|
||||
for (let i = 0; i < halfRoundsFull; i++) values = poseidonRound(values, true, round++);
|
||||
|
||||
if (round !== rounds)
|
||||
throw new Error(`Poseidon: wrong number of rounds: last round=${round}, total=${rounds}`);
|
||||
return values;
|
||||
};
|
||||
// For verification in tests
|
||||
poseidonHash.roundConstants = roundConstants;
|
||||
return poseidonHash;
|
||||
}
|
319
src/abstract/utils.ts
Normal file
319
src/abstract/utils.ts
Normal file
@ -0,0 +1,319 @@
|
||||
/*! 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 _1n = BigInt(1);
|
||||
const _2n = BigInt(2);
|
||||
export type Hex = Uint8Array | string; // hex strings are accepted for simplicity
|
||||
export type PrivKey = Hex | bigint; // bigints are accepted to ease learning curve
|
||||
export type CHash = {
|
||||
(message: Uint8Array | string): Uint8Array;
|
||||
blockLen: number;
|
||||
outputLen: number;
|
||||
create(opts?: { dkLen?: number }): any; // For shake
|
||||
};
|
||||
export type FHash = (message: Uint8Array | string) => Uint8Array;
|
||||
|
||||
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 {
|
||||
abytes(bytes);
|
||||
// pre-caching improves the speed 6x
|
||||
let hex = '';
|
||||
for (let i = 0; i < bytes.length; i++) {
|
||||
hex += hexes[bytes[i]];
|
||||
}
|
||||
return hex;
|
||||
}
|
||||
|
||||
export function numberToHexUnpadded(num: number | bigint): string {
|
||||
const hex = num.toString(16);
|
||||
return hex.length & 1 ? `0${hex}` : hex;
|
||||
}
|
||||
|
||||
export function hexToNumber(hex: string): bigint {
|
||||
if (typeof hex !== 'string') throw new Error('hex string expected, got ' + typeof hex);
|
||||
// Big Endian
|
||||
return BigInt(hex === '' ? '0' : `0x${hex}`);
|
||||
}
|
||||
|
||||
// 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 {
|
||||
if (typeof hex !== 'string') throw new Error('hex string expected, got ' + typeof hex);
|
||||
const hl = hex.length;
|
||||
const al = hl / 2;
|
||||
if (hl % 2) throw new Error('padded hex string expected, got unpadded hex of length ' + hl);
|
||||
const array = new Uint8Array(al);
|
||||
for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {
|
||||
const n1 = asciiToBase16(hex.charCodeAt(hi));
|
||||
const n2 = asciiToBase16(hex.charCodeAt(hi + 1));
|
||||
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;
|
||||
}
|
||||
|
||||
// BE: Big Endian, LE: Little Endian
|
||||
export function bytesToNumberBE(bytes: Uint8Array): bigint {
|
||||
return hexToNumber(bytesToHex(bytes));
|
||||
}
|
||||
export function bytesToNumberLE(bytes: Uint8Array): bigint {
|
||||
abytes(bytes);
|
||||
return hexToNumber(bytesToHex(Uint8Array.from(bytes).reverse()));
|
||||
}
|
||||
|
||||
export function numberToBytesBE(n: number | bigint, len: number): Uint8Array {
|
||||
return hexToBytes(n.toString(16).padStart(len * 2, '0'));
|
||||
}
|
||||
export function numberToBytesLE(n: number | bigint, len: number): Uint8Array {
|
||||
return numberToBytesBE(n, len).reverse();
|
||||
}
|
||||
// Unpadded, rarely used
|
||||
export function numberToVarBytesBE(n: number | bigint): Uint8Array {
|
||||
return hexToBytes(numberToHexUnpadded(n));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
// is instance of Uint8Array, and its slice() creates **mutable** copy
|
||||
res = Uint8Array.from(hex);
|
||||
} else {
|
||||
throw new Error(`${title} must be hex string or Uint8Array`);
|
||||
}
|
||||
const len = res.length;
|
||||
if (typeof expectedLength === 'number' && len !== expectedLength)
|
||||
throw new Error(`${title} expected ${expectedLength} bytes, got ${len}`);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies several Uint8Arrays into one.
|
||||
*/
|
||||
export function concatBytes(...arrays: Uint8Array[]): Uint8Array {
|
||||
let sum = 0;
|
||||
for (let i = 0; i < arrays.length; i++) {
|
||||
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;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// Compares 2 u8a-s in kinda constant time
|
||||
export function equalBytes(a: Uint8Array, b: Uint8Array) {
|
||||
if (a.length !== b.length) return false;
|
||||
let diff = 0;
|
||||
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
|
||||
|
||||
/**
|
||||
* Calculates amount of bits in a bigint.
|
||||
* Same as `n.toString(2).length`
|
||||
*/
|
||||
export function bitLen(n: bigint) {
|
||||
let len;
|
||||
for (len = 0; n > _0n; n >>= _1n, len += 1);
|
||||
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 function bitGet(n: bigint, pos: number) {
|
||||
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;
|
||||
|
||||
// DRBG
|
||||
|
||||
const u8n = (data?: any) => new Uint8Array(data); // creates Uint8Array
|
||||
const u8fr = (arr: any) => Uint8Array.from(arr); // another shortcut
|
||||
type Pred<T> = (v: Uint8Array) => T | undefined;
|
||||
/**
|
||||
* Minimal HMAC-DRBG from NIST 800-90 for RFC6979 sigs.
|
||||
* @returns function that will call DRBG until 2nd arg returns something meaningful
|
||||
* @example
|
||||
* const drbg = createHmacDRBG<Key>(32, 32, hmac);
|
||||
* drbg(seed, bytesToKey); // bytesToKey must return Key or undefined
|
||||
*/
|
||||
export function createHmacDrbg<T>(
|
||||
hashLen: number,
|
||||
qByteLen: number,
|
||||
hmacFn: (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array
|
||||
): (seed: Uint8Array, predicate: Pred<T>) => T {
|
||||
if (typeof hashLen !== 'number' || hashLen < 2) throw new Error('hashLen must be a number');
|
||||
if (typeof qByteLen !== 'number' || qByteLen < 2) throw new Error('qByteLen must be a number');
|
||||
if (typeof hmacFn !== 'function') throw new Error('hmacFn must be a function');
|
||||
// Step B, Step C: set hashLen to 8*ceil(hlen/8)
|
||||
let v = u8n(hashLen); // Minimal non-full-spec HMAC-DRBG from NIST 800-90 for RFC6979 sigs.
|
||||
let k = u8n(hashLen); // Steps B and C of RFC6979 3.2: set hashLen, in our case always same
|
||||
let i = 0; // Iterations counter, will throw when over 1000
|
||||
const reset = () => {
|
||||
v.fill(1);
|
||||
k.fill(0);
|
||||
i = 0;
|
||||
};
|
||||
const h = (...b: Uint8Array[]) => hmacFn(k, v, ...b); // hmac(k)(v, ...values)
|
||||
const reseed = (seed = u8n()) => {
|
||||
// HMAC-DRBG reseed() function. Steps D-G
|
||||
k = h(u8fr([0x00]), seed); // k = hmac(k || v || 0x00 || seed)
|
||||
v = h(); // v = hmac(k || v)
|
||||
if (seed.length === 0) return;
|
||||
k = h(u8fr([0x01]), seed); // k = hmac(k || v || 0x01 || seed)
|
||||
v = h(); // v = hmac(k || v)
|
||||
};
|
||||
const gen = () => {
|
||||
// HMAC-DRBG generate() function
|
||||
if (i++ >= 1000) throw new Error('drbg: tried 1000 values');
|
||||
let len = 0;
|
||||
const out: Uint8Array[] = [];
|
||||
while (len < qByteLen) {
|
||||
v = h();
|
||||
const sl = v.slice();
|
||||
out.push(sl);
|
||||
len += v.length;
|
||||
}
|
||||
return concatBytes(...out);
|
||||
};
|
||||
const genUntil = (seed: Uint8Array, pred: Pred<T>): T => {
|
||||
reset();
|
||||
reseed(seed); // Steps D-G
|
||||
let res: T | undefined = undefined; // Step H: grind until k is in [1..n-1]
|
||||
while (!(res = pred(gen()))) reseed();
|
||||
reset();
|
||||
return res;
|
||||
};
|
||||
return genUntil;
|
||||
}
|
||||
|
||||
// Validating curves and fields
|
||||
|
||||
const validatorFns = {
|
||||
bigint: (val: any) => typeof val === 'bigint',
|
||||
function: (val: any) => typeof val === 'function',
|
||||
boolean: (val: any) => typeof val === 'boolean',
|
||||
string: (val: any) => typeof val === 'string',
|
||||
stringOrUint8Array: (val: any) => typeof val === 'string' || isBytes(val),
|
||||
isSafeInteger: (val: any) => Number.isSafeInteger(val),
|
||||
array: (val: any) => Array.isArray(val),
|
||||
field: (val: any, object: any) => (object as any).Fp.isValid(val),
|
||||
hash: (val: any) => typeof val === 'function' && Number.isSafeInteger(val.outputLen),
|
||||
} as const;
|
||||
type Validator = keyof typeof validatorFns;
|
||||
type ValMap<T extends Record<string, any>> = { [K in keyof T]?: Validator };
|
||||
// type Record<K extends string | number | symbol, T> = { [P in K]: T; }
|
||||
|
||||
export function validateObject<T extends Record<string, any>>(
|
||||
object: T,
|
||||
validators: ValMap<T>,
|
||||
optValidators: ValMap<T> = {}
|
||||
) {
|
||||
const checkField = (fieldName: keyof T, type: Validator, isOptional: boolean) => {
|
||||
const checkVal = validatorFns[type];
|
||||
if (typeof checkVal !== 'function')
|
||||
throw new Error(`Invalid validator "${type}", expected function`);
|
||||
|
||||
const val = object[fieldName as keyof typeof object];
|
||||
if (isOptional && val === undefined) return;
|
||||
if (!checkVal(val, object)) {
|
||||
throw new Error(
|
||||
`Invalid param ${String(fieldName)}=${val} (${typeof val}), expected ${type}`
|
||||
);
|
||||
}
|
||||
};
|
||||
for (const [fieldName, type] of Object.entries(validators)) checkField(fieldName, type!, false);
|
||||
for (const [fieldName, type] of Object.entries(optValidators)) checkField(fieldName, type!, true);
|
||||
return object;
|
||||
}
|
||||
// validate type tests
|
||||
// const o: { a: number; b: number; c: number } = { a: 1, b: 5, c: 6 };
|
||||
// const z0 = validateObject(o, { a: 'isSafeInteger' }, { c: 'bigint' }); // Ok!
|
||||
// // Should fail type-check
|
||||
// const z1 = validateObject(o, { a: 'tmp' }, { c: 'zz' });
|
||||
// const z2 = validateObject(o, { a: 'isSafeInteger' }, { c: 'zz' });
|
||||
// const z3 = validateObject(o, { test: 'boolean', z: 'bug' });
|
||||
// const z4 = validateObject(o, { a: 'boolean', z: 'bug' });
|
1237
src/abstract/weierstrass.ts
Normal file
1237
src/abstract/weierstrass.ts
Normal file
File diff suppressed because it is too large
Load Diff
1408
src/bls12-381.ts
Normal file
1408
src/bls12-381.ts
Normal file
File diff suppressed because it is too large
Load Diff
22
src/bn254.ts
Normal file
22
src/bn254.ts
Normal file
@ -0,0 +1,22 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
import { sha256 } from '@noble/hashes/sha256';
|
||||
import { weierstrass } from './abstract/weierstrass.js';
|
||||
import { getHash } from './_shortw_utils.js';
|
||||
import { Field } from './abstract/modular.js';
|
||||
/**
|
||||
* bn254 pairing-friendly curve.
|
||||
* Previously known as alt_bn_128, when it had 128-bit security.
|
||||
* Barbulescu-Duquesne 2017 shown it's weaker: just about 100 bits,
|
||||
* so the naming has been adjusted to its prime bit count
|
||||
* https://hal.science/hal-01534101/file/main.pdf
|
||||
*/
|
||||
export const bn254 = weierstrass({
|
||||
a: BigInt(0),
|
||||
b: BigInt(3),
|
||||
Fp: Field(BigInt('0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47')),
|
||||
n: BigInt('0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001'),
|
||||
Gx: BigInt(1),
|
||||
Gy: BigInt(2),
|
||||
h: BigInt(1),
|
||||
...getHash(sha256),
|
||||
});
|
497
src/ed25519.ts
Normal file
497
src/ed25519.ts
Normal file
@ -0,0 +1,497 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
import { sha512 } from '@noble/hashes/sha512';
|
||||
import { concatBytes, randomBytes, utf8ToBytes } from '@noble/hashes/utils';
|
||||
import { ExtPointType, twistedEdwards } from './abstract/edwards.js';
|
||||
import { montgomery } from './abstract/montgomery.js';
|
||||
import { Field, FpSqrtEven, isNegativeLE, mod, pow2 } from './abstract/modular.js';
|
||||
import {
|
||||
bytesToHex,
|
||||
bytesToNumberLE,
|
||||
ensureBytes,
|
||||
equalBytes,
|
||||
Hex,
|
||||
numberToBytesLE,
|
||||
} from './abstract/utils.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:
|
||||
* - X25519 ECDH
|
||||
* - Ristretto cofactor elimination
|
||||
* - Elligator hash-to-group / point indistinguishability
|
||||
*/
|
||||
|
||||
const ED25519_P = BigInt(
|
||||
'57896044618658097711785492504343953926634992332820282019728792003956564819949'
|
||||
);
|
||||
// √(-1) aka √(a) aka 2^((p-1)/4)
|
||||
const ED25519_SQRT_M1 = BigInt(
|
||||
'19681161376707505956807079304988542015446066515923890162744021073123829784752'
|
||||
);
|
||||
|
||||
// prettier-ignore
|
||||
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _5n = BigInt(5);
|
||||
// prettier-ignore
|
||||
const _10n = BigInt(10), _20n = BigInt(20), _40n = BigInt(40), _80n = BigInt(80);
|
||||
|
||||
function ed25519_pow_2_252_3(x: bigint) {
|
||||
const P = ED25519_P;
|
||||
const x2 = (x * x) % P;
|
||||
const b2 = (x2 * x) % P; // x^3, 11
|
||||
const b4 = (pow2(b2, _2n, P) * b2) % P; // x^15, 1111
|
||||
const b5 = (pow2(b4, _1n, P) * x) % P; // x^31
|
||||
const b10 = (pow2(b5, _5n, P) * b5) % P;
|
||||
const b20 = (pow2(b10, _10n, P) * b10) % P;
|
||||
const b40 = (pow2(b20, _20n, P) * b20) % P;
|
||||
const b80 = (pow2(b40, _40n, P) * b40) % P;
|
||||
const b160 = (pow2(b80, _80n, P) * b80) % P;
|
||||
const b240 = (pow2(b160, _80n, P) * b80) % P;
|
||||
const b250 = (pow2(b240, _10n, P) * b10) % P;
|
||||
const pow_p_5_8 = (pow2(b250, _2n, P) * x) % P;
|
||||
// ^ To pow to (p+3)/8, multiply it by x.
|
||||
return { pow_p_5_8, b2 };
|
||||
}
|
||||
|
||||
function adjustScalarBytes(bytes: Uint8Array): Uint8Array {
|
||||
// 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
|
||||
bytes[0] &= 248; // 0b1111_1000
|
||||
// and the most significant bit of the last to zero,
|
||||
bytes[31] &= 127; // 0b0111_1111
|
||||
// set the second most significant bit of the last byte to 1
|
||||
bytes[31] |= 64; // 0b0100_0000
|
||||
return bytes;
|
||||
}
|
||||
|
||||
// sqrt(u/v)
|
||||
function uvRatio(u: bigint, v: bigint): { isValid: boolean; value: bigint } {
|
||||
const P = ED25519_P;
|
||||
const v3 = mod(v * v * v, P); // v³
|
||||
const v7 = mod(v3 * v3 * v, P); // v⁷
|
||||
// (p+3)/8 and (p-5)/8
|
||||
const pow = ed25519_pow_2_252_3(u * v7).pow_p_5_8;
|
||||
let x = mod(u * v3 * pow, P); // (uv³)(uv⁷)^(p-5)/8
|
||||
const vx2 = mod(v * x * x, P); // vx²
|
||||
const root1 = x; // First root candidate
|
||||
const root2 = mod(x * ED25519_SQRT_M1, P); // Second root candidate
|
||||
const useRoot1 = vx2 === u; // If vx² = u (mod p), x is a square root
|
||||
const useRoot2 = vx2 === mod(-u, P); // If vx² = -u, set x <-- x * 2^((p-1)/4)
|
||||
const noRoot = vx2 === mod(-u * ED25519_SQRT_M1, P); // There is no valid root, vx² = -u√(-1)
|
||||
if (useRoot1) x = root1;
|
||||
if (useRoot2 || noRoot) x = root2; // We return root2 anyway, for const-time
|
||||
if (isNegativeLE(x, P)) x = mod(-x, P);
|
||||
return { isValid: useRoot1 || useRoot2, value: x };
|
||||
}
|
||||
|
||||
// Just in case
|
||||
export const ED25519_TORSION_SUBGROUP = [
|
||||
'0100000000000000000000000000000000000000000000000000000000000000',
|
||||
'c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a',
|
||||
'0000000000000000000000000000000000000000000000000000000000000080',
|
||||
'26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05',
|
||||
'ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f',
|
||||
'26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85',
|
||||
'0000000000000000000000000000000000000000000000000000000000000000',
|
||||
'c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa',
|
||||
];
|
||||
|
||||
const Fp = Field(ED25519_P, undefined, true);
|
||||
|
||||
const ed25519Defaults = {
|
||||
// Param: a
|
||||
a: BigInt(-1), // Fp.create(-1) is proper; our way still works and is faster
|
||||
// d is equal to -121665/121666 over finite field.
|
||||
// Negative number is P - number, and division is invert(number, P)
|
||||
d: BigInt('37095705934669439343138083508754565189542113879843219016388785533085940283555'),
|
||||
// Finite field 𝔽p over which we'll do calculations; 2n**255n - 19n
|
||||
Fp,
|
||||
// Subgroup order: how many points curve has
|
||||
// 2n**252n + 27742317777372353535851937790883648493n;
|
||||
n: BigInt('7237005577332262213973186563042994240857116359379907606001950938285454250989'),
|
||||
// Cofactor
|
||||
h: BigInt(8),
|
||||
// Base point (x, y) aka generator point
|
||||
Gx: BigInt('15112221349535400772501151409588531511454012693041857206046113283949847762202'),
|
||||
Gy: BigInt('46316835694926478169428394003475163141307993866256225615783033603165251855960'),
|
||||
hash: sha512,
|
||||
randomBytes,
|
||||
adjustScalarBytes,
|
||||
// dom2
|
||||
// Ratio of u to v. Allows us to combine inversion and square root. Uses algo from RFC8032 5.1.3.
|
||||
// Constant-time, u/√v
|
||||
uvRatio,
|
||||
} as const;
|
||||
|
||||
export const ed25519 = /* @__PURE__ */ twistedEdwards(ed25519Defaults);
|
||||
|
||||
function ed25519_domain(data: Uint8Array, ctx: Uint8Array, phflag: boolean) {
|
||||
if (ctx.length > 255) throw new Error('Context is too big');
|
||||
return concatBytes(
|
||||
utf8ToBytes('SigEd25519 no Ed25519 collisions'),
|
||||
new Uint8Array([phflag ? 1 : 0, ctx.length]),
|
||||
ctx,
|
||||
data
|
||||
);
|
||||
}
|
||||
|
||||
export const ed25519ctx = /* @__PURE__ */ twistedEdwards({
|
||||
...ed25519Defaults,
|
||||
domain: ed25519_domain,
|
||||
});
|
||||
export const ed25519ph = /* @__PURE__ */ twistedEdwards({
|
||||
...ed25519Defaults,
|
||||
domain: ed25519_domain,
|
||||
prehash: sha512,
|
||||
});
|
||||
|
||||
export const x25519 = /* @__PURE__ */ (() =>
|
||||
montgomery({
|
||||
P: ED25519_P,
|
||||
a: BigInt(486662),
|
||||
montgomeryBits: 255, // n is 253 bits
|
||||
nByteLength: 32,
|
||||
Gu: BigInt(9),
|
||||
powPminus2: (x: bigint): bigint => {
|
||||
const P = ED25519_P;
|
||||
// x^(p-2) aka x^(2^255-21)
|
||||
const { pow_p_5_8, b2 } = ed25519_pow_2_252_3(x);
|
||||
return mod(pow2(pow_p_5_8, BigInt(3), P) * b2, P);
|
||||
},
|
||||
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)
|
||||
// NOTE: very important part is usage of FpSqrtEven for ELL2_C1_EDWARDS, since
|
||||
// SageMath returns different root first and everything falls apart
|
||||
|
||||
const ELL2_C1 = (Fp.ORDER + BigInt(3)) / BigInt(8); // 1. c1 = (q + 3) / 8 # Integer arithmetic
|
||||
|
||||
const ELL2_C2 = Fp.pow(_2n, ELL2_C1); // 2. c2 = 2^c1
|
||||
const ELL2_C3 = Fp.sqrt(Fp.neg(Fp.ONE)); // 3. c3 = sqrt(-1)
|
||||
const ELL2_C4 = (Fp.ORDER - BigInt(5)) / BigInt(8); // 4. c4 = (q - 5) / 8 # Integer arithmetic
|
||||
const ELL2_J = BigInt(486662);
|
||||
|
||||
// prettier-ignore
|
||||
function map_to_curve_elligator2_curve25519(u: bigint) {
|
||||
let tv1 = Fp.sqr(u); // 1. tv1 = u^2
|
||||
tv1 = Fp.mul(tv1, _2n); // 2. tv1 = 2 * tv1
|
||||
let xd = Fp.add(tv1, Fp.ONE); // 3. xd = tv1 + 1 # Nonzero: -1 is square (mod p), tv1 is not
|
||||
let x1n = Fp.neg(ELL2_J); // 4. x1n = -J # x1 = x1n / xd = -J / (1 + 2 * u^2)
|
||||
let tv2 = Fp.sqr(xd); // 5. tv2 = xd^2
|
||||
let gxd = Fp.mul(tv2, xd); // 6. gxd = tv2 * xd # gxd = xd^3
|
||||
let gx1 = Fp.mul(tv1, ELL2_J); // 7. gx1 = J * tv1 # x1n + J * xd
|
||||
gx1 = Fp.mul(gx1, x1n); // 8. gx1 = gx1 * x1n # x1n^2 + J * x1n * xd
|
||||
gx1 = Fp.add(gx1, tv2); // 9. gx1 = gx1 + tv2 # x1n^2 + J * x1n * xd + xd^2
|
||||
gx1 = Fp.mul(gx1, x1n); // 10. gx1 = gx1 * x1n # x1n^3 + J * x1n^2 * xd + x1n * xd^2
|
||||
let tv3 = Fp.sqr(gxd); // 11. tv3 = gxd^2
|
||||
tv2 = Fp.sqr(tv3); // 12. tv2 = tv3^2 # gxd^4
|
||||
tv3 = Fp.mul(tv3, gxd); // 13. tv3 = tv3 * gxd # gxd^3
|
||||
tv3 = Fp.mul(tv3, gx1); // 14. tv3 = tv3 * gx1 # gx1 * gxd^3
|
||||
tv2 = Fp.mul(tv2, tv3); // 15. tv2 = tv2 * tv3 # gx1 * gxd^7
|
||||
let y11 = Fp.pow(tv2, ELL2_C4); // 16. y11 = tv2^c4 # (gx1 * gxd^7)^((p - 5) / 8)
|
||||
y11 = Fp.mul(y11, tv3); // 17. y11 = y11 * tv3 # gx1*gxd^3*(gx1*gxd^7)^((p-5)/8)
|
||||
let y12 = Fp.mul(y11, ELL2_C3); // 18. y12 = y11 * c3
|
||||
tv2 = Fp.sqr(y11); // 19. tv2 = y11^2
|
||||
tv2 = Fp.mul(tv2, gxd); // 20. tv2 = tv2 * gxd
|
||||
let e1 = Fp.eql(tv2, gx1); // 21. e1 = tv2 == gx1
|
||||
let y1 = Fp.cmov(y12, y11, e1); // 22. y1 = CMOV(y12, y11, e1) # If g(x1) is square, this is its sqrt
|
||||
let x2n = Fp.mul(x1n, tv1); // 23. x2n = x1n * tv1 # x2 = x2n / xd = 2 * u^2 * x1n / xd
|
||||
let y21 = Fp.mul(y11, u); // 24. y21 = y11 * u
|
||||
y21 = Fp.mul(y21, ELL2_C2); // 25. y21 = y21 * c2
|
||||
let y22 = Fp.mul(y21, ELL2_C3); // 26. y22 = y21 * c3
|
||||
let gx2 = Fp.mul(gx1, tv1); // 27. gx2 = gx1 * tv1 # g(x2) = gx2 / gxd = 2 * u^2 * g(x1)
|
||||
tv2 = Fp.sqr(y21); // 28. tv2 = y21^2
|
||||
tv2 = Fp.mul(tv2, gxd); // 29. tv2 = tv2 * gxd
|
||||
let e2 = Fp.eql(tv2, gx2); // 30. e2 = tv2 == gx2
|
||||
let y2 = Fp.cmov(y22, y21, e2); // 31. y2 = CMOV(y22, y21, e2) # If g(x2) is square, this is its sqrt
|
||||
tv2 = Fp.sqr(y1); // 32. tv2 = y1^2
|
||||
tv2 = Fp.mul(tv2, gxd); // 33. tv2 = tv2 * gxd
|
||||
let e3 = Fp.eql(tv2, gx1); // 34. e3 = tv2 == gx1
|
||||
let xn = Fp.cmov(x2n, x1n, e3); // 35. xn = CMOV(x2n, x1n, e3) # If e3, x = x1, else x = x2
|
||||
let y = Fp.cmov(y2, y1, e3); // 36. y = CMOV(y2, y1, e3) # If e3, y = y1, else y = y2
|
||||
let e4 = Fp.isOdd(y); // 37. e4 = sgn0(y) == 1 # Fix sign of y
|
||||
y = Fp.cmov(y, Fp.neg(y), e3 !== e4); // 38. y = CMOV(y, -y, e3 XOR e4)
|
||||
return { xMn: xn, xMd: xd, yMn: y, yMd: _1n }; // 39. return (xn, xd, y, 1)
|
||||
}
|
||||
|
||||
const ELL2_C1_EDWARDS = FpSqrtEven(Fp, Fp.neg(BigInt(486664))); // sgn0(c1) MUST equal 0
|
||||
function map_to_curve_elligator2_edwards25519(u: bigint) {
|
||||
const { xMn, xMd, yMn, yMd } = map_to_curve_elligator2_curve25519(u); // 1. (xMn, xMd, yMn, yMd) =
|
||||
// map_to_curve_elligator2_curve25519(u)
|
||||
let xn = Fp.mul(xMn, yMd); // 2. xn = xMn * yMd
|
||||
xn = Fp.mul(xn, ELL2_C1_EDWARDS); // 3. xn = xn * c1
|
||||
let xd = Fp.mul(xMd, yMn); // 4. xd = xMd * yMn # xn / xd = c1 * xM / yM
|
||||
let yn = Fp.sub(xMn, xMd); // 5. yn = xMn - xMd
|
||||
let yd = Fp.add(xMn, xMd); // 6. yd = xMn + xMd # (n / d - 1) / (n / d + 1) = (n - d) / (n + d)
|
||||
let tv1 = Fp.mul(xd, yd); // 7. tv1 = xd * yd
|
||||
let e = Fp.eql(tv1, Fp.ZERO); // 8. e = tv1 == 0
|
||||
xn = Fp.cmov(xn, Fp.ZERO, e); // 9. xn = CMOV(xn, 0, e)
|
||||
xd = Fp.cmov(xd, Fp.ONE, e); // 10. xd = CMOV(xd, 1, e)
|
||||
yn = Fp.cmov(yn, Fp.ONE, e); // 11. yn = CMOV(yn, 1, e)
|
||||
yd = Fp.cmov(yd, Fp.ONE, e); // 12. yd = CMOV(yd, 1, e)
|
||||
|
||||
const inv = Fp.invertBatch([xd, yd]); // batch division
|
||||
return { x: Fp.mul(xn, inv[0]), y: Fp.mul(yn, inv[1]) }; // 13. return (xn, xd, yn, yd)
|
||||
}
|
||||
|
||||
const htf = /* @__PURE__ */ (() =>
|
||||
createHasher(
|
||||
ed25519.ExtendedPoint,
|
||||
(scalars: bigint[]) => map_to_curve_elligator2_edwards25519(scalars[0]),
|
||||
{
|
||||
DST: 'edwards25519_XMD:SHA-512_ELL2_RO_',
|
||||
encodeDST: 'edwards25519_XMD:SHA-512_ELL2_NU_',
|
||||
p: Fp.ORDER,
|
||||
m: 1,
|
||||
k: 128,
|
||||
expand: 'xmd',
|
||||
hash: sha512,
|
||||
}
|
||||
))();
|
||||
export const hashToCurve = /* @__PURE__ */ (() => htf.hashToCurve)();
|
||||
export const encodeToCurve = /* @__PURE__ */ (() => htf.encodeToCurve)();
|
||||
|
||||
function assertRstPoint(other: unknown) {
|
||||
if (!(other instanceof RistPoint)) throw new Error('RistrettoPoint expected');
|
||||
}
|
||||
|
||||
// √(-1) aka √(a) aka 2^((p-1)/4)
|
||||
const SQRT_M1 = ED25519_SQRT_M1;
|
||||
// √(ad - 1)
|
||||
const SQRT_AD_MINUS_ONE = BigInt(
|
||||
'25063068953384623474111414158702152701244531502492656460079210482610430750235'
|
||||
);
|
||||
// 1 / √(a-d)
|
||||
const INVSQRT_A_MINUS_D = BigInt(
|
||||
'54469307008909316920995813868745141605393597292927456921205312896311721017578'
|
||||
);
|
||||
// 1-d²
|
||||
const ONE_MINUS_D_SQ = BigInt(
|
||||
'1159843021668779879193775521855586647937357759715417654439879720876111806838'
|
||||
);
|
||||
// (d-1)²
|
||||
const D_MINUS_ONE_SQ = BigInt(
|
||||
'40440834346308536858101042469323190826248399146238708352240133220865137265952'
|
||||
);
|
||||
// Calculates 1/√(number)
|
||||
const invertSqrt = (number: bigint) => uvRatio(_1n, number);
|
||||
|
||||
const MAX_255B = BigInt('0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
|
||||
const bytes255ToNumberLE = (bytes: Uint8Array) =>
|
||||
ed25519.CURVE.Fp.create(bytesToNumberLE(bytes) & MAX_255B);
|
||||
|
||||
type ExtendedPoint = ExtPointType;
|
||||
|
||||
// Computes Elligator map for Ristretto
|
||||
// https://ristretto.group/formulas/elligator.html
|
||||
function calcElligatorRistrettoMap(r0: bigint): ExtendedPoint {
|
||||
const { d } = ed25519.CURVE;
|
||||
const P = ed25519.CURVE.Fp.ORDER;
|
||||
const mod = ed25519.CURVE.Fp.create;
|
||||
const r = mod(SQRT_M1 * r0 * r0); // 1
|
||||
const Ns = mod((r + _1n) * ONE_MINUS_D_SQ); // 2
|
||||
let c = BigInt(-1); // 3
|
||||
const D = mod((c - d * r) * mod(r + d)); // 4
|
||||
let { isValid: Ns_D_is_sq, value: s } = uvRatio(Ns, D); // 5
|
||||
let s_ = mod(s * r0); // 6
|
||||
if (!isNegativeLE(s_, P)) s_ = mod(-s_);
|
||||
if (!Ns_D_is_sq) s = s_; // 7
|
||||
if (!Ns_D_is_sq) c = r; // 8
|
||||
const Nt = mod(c * (r - _1n) * D_MINUS_ONE_SQ - D); // 9
|
||||
const s2 = s * s;
|
||||
const W0 = mod((s + s) * D); // 10
|
||||
const W1 = mod(Nt * SQRT_AD_MINUS_ONE); // 11
|
||||
const W2 = mod(_1n - s2); // 12
|
||||
const W3 = mod(_1n + s2); // 13
|
||||
return new ed25519.ExtendedPoint(mod(W0 * W3), mod(W2 * W1), mod(W1 * W3), mod(W0 * W2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Each ed25519/ExtendedPoint has 8 different equivalent points. This can be
|
||||
* a source of bugs for protocols like ring signatures. Ristretto was created to solve this.
|
||||
* Ristretto 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 RistPoint implements Group<RistPoint> {
|
||||
static BASE: RistPoint;
|
||||
static ZERO: RistPoint;
|
||||
// Private property to discourage combining ExtendedPoint + RistrettoPoint
|
||||
// Always use Ristretto encoding/decoding instead.
|
||||
constructor(private readonly ep: ExtendedPoint) {}
|
||||
|
||||
static fromAffine(ap: AffinePoint<bigint>) {
|
||||
return new RistPoint(ed25519.ExtendedPoint.fromAffine(ap));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* **Note:** this is one-way map, there is no conversion from point to hash.
|
||||
* https://ristretto.group/formulas/elligator.html
|
||||
* @param hex 64-byte output of a hash function
|
||||
*/
|
||||
static hashToCurve(hex: Hex): RistPoint {
|
||||
hex = ensureBytes('ristrettoHash', hex, 64);
|
||||
const r1 = bytes255ToNumberLE(hex.slice(0, 32));
|
||||
const R1 = calcElligatorRistrettoMap(r1);
|
||||
const r2 = bytes255ToNumberLE(hex.slice(32, 64));
|
||||
const R2 = calcElligatorRistrettoMap(r2);
|
||||
return new RistPoint(R1.add(R2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts ristretto-encoded string to ristretto point.
|
||||
* https://ristretto.group/formulas/decoding.html
|
||||
* @param hex Ristretto-encoded 32 bytes. Not every 32-byte string is valid ristretto encoding
|
||||
*/
|
||||
static fromHex(hex: Hex): RistPoint {
|
||||
hex = ensureBytes('ristrettoHex', hex, 32);
|
||||
const { a, d } = ed25519.CURVE;
|
||||
const P = ed25519.CURVE.Fp.ORDER;
|
||||
const mod = ed25519.CURVE.Fp.create;
|
||||
const emsg = 'RistrettoPoint.fromHex: the hex is not valid encoding of RistrettoPoint';
|
||||
const s = bytes255ToNumberLE(hex);
|
||||
// 1. Check that s_bytes is the canonical encoding of a field element, or else abort.
|
||||
// 3. Check that s is non-negative, or else abort
|
||||
if (!equalBytes(numberToBytesLE(s, 32), hex) || isNegativeLE(s, P)) throw new Error(emsg);
|
||||
const s2 = mod(s * s);
|
||||
const u1 = mod(_1n + a * s2); // 4 (a is -1)
|
||||
const u2 = mod(_1n - a * s2); // 5
|
||||
const u1_2 = mod(u1 * u1);
|
||||
const u2_2 = mod(u2 * u2);
|
||||
const v = mod(a * d * u1_2 - u2_2); // 6
|
||||
const { isValid, value: I } = invertSqrt(mod(v * u2_2)); // 7
|
||||
const Dx = mod(I * u2); // 8
|
||||
const Dy = mod(I * Dx * v); // 9
|
||||
let x = mod((s + s) * Dx); // 10
|
||||
if (isNegativeLE(x, P)) x = mod(-x); // 10
|
||||
const y = mod(u1 * Dy); // 11
|
||||
const t = mod(x * y); // 12
|
||||
if (!isValid || isNegativeLE(t, P) || y === _0n) throw new Error(emsg);
|
||||
return new RistPoint(new ed25519.ExtendedPoint(x, y, _1n, t));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes ristretto point to Uint8Array.
|
||||
* https://ristretto.group/formulas/encoding.html
|
||||
*/
|
||||
toRawBytes(): Uint8Array {
|
||||
let { ex: x, ey: y, ez: z, et: t } = this.ep;
|
||||
const P = ed25519.CURVE.Fp.ORDER;
|
||||
const mod = ed25519.CURVE.Fp.create;
|
||||
const u1 = mod(mod(z + y) * mod(z - y)); // 1
|
||||
const u2 = mod(x * y); // 2
|
||||
// Square root always exists
|
||||
const u2sq = mod(u2 * u2);
|
||||
const { value: invsqrt } = invertSqrt(mod(u1 * u2sq)); // 3
|
||||
const D1 = mod(invsqrt * u1); // 4
|
||||
const D2 = mod(invsqrt * u2); // 5
|
||||
const zInv = mod(D1 * D2 * t); // 6
|
||||
let D: bigint; // 7
|
||||
if (isNegativeLE(t * zInv, P)) {
|
||||
let _x = mod(y * SQRT_M1);
|
||||
let _y = mod(x * SQRT_M1);
|
||||
x = _x;
|
||||
y = _y;
|
||||
D = mod(D1 * INVSQRT_A_MINUS_D);
|
||||
} else {
|
||||
D = D2; // 8
|
||||
}
|
||||
if (isNegativeLE(x * zInv, P)) y = mod(-y); // 9
|
||||
let s = mod((z - y) * D); // 10 (check footer's note, no sqrt(-a))
|
||||
if (isNegativeLE(s, P)) s = mod(-s);
|
||||
return numberToBytesLE(s, 32); // 11
|
||||
}
|
||||
|
||||
toHex(): string {
|
||||
return bytesToHex(this.toRawBytes());
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return this.toHex();
|
||||
}
|
||||
|
||||
// Compare one point to another.
|
||||
equals(other: RistPoint): boolean {
|
||||
assertRstPoint(other);
|
||||
const { ex: X1, ey: Y1 } = this.ep;
|
||||
const { ex: X2, ey: Y2 } = other.ep;
|
||||
const mod = ed25519.CURVE.Fp.create;
|
||||
// (x1 * y2 == y1 * x2) | (y1 * y2 == x1 * x2)
|
||||
const one = mod(X1 * Y2) === mod(Y1 * X2);
|
||||
const two = mod(Y1 * Y2) === mod(X1 * X2);
|
||||
return one || two;
|
||||
}
|
||||
|
||||
add(other: RistPoint): RistPoint {
|
||||
assertRstPoint(other);
|
||||
return new RistPoint(this.ep.add(other.ep));
|
||||
}
|
||||
|
||||
subtract(other: RistPoint): RistPoint {
|
||||
assertRstPoint(other);
|
||||
return new RistPoint(this.ep.subtract(other.ep));
|
||||
}
|
||||
|
||||
multiply(scalar: bigint): RistPoint {
|
||||
return new RistPoint(this.ep.multiply(scalar));
|
||||
}
|
||||
|
||||
multiplyUnsafe(scalar: bigint): RistPoint {
|
||||
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
|
480
src/ed448.ts
Normal file
480
src/ed448.ts
Normal file
@ -0,0 +1,480 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
import { shake256 } from '@noble/hashes/sha3';
|
||||
import { concatBytes, randomBytes, utf8ToBytes, wrapConstructor } from '@noble/hashes/utils';
|
||||
import { ExtPointType, twistedEdwards } from './abstract/edwards.js';
|
||||
import { mod, pow2, Field, isNegativeLE } from './abstract/modular.js';
|
||||
import { montgomery } from './abstract/montgomery.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:
|
||||
* - 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
|
||||
*/
|
||||
|
||||
const shake256_114 = wrapConstructor(() => shake256.create({ dkLen: 114 }));
|
||||
const shake256_64 = wrapConstructor(() => shake256.create({ dkLen: 64 }));
|
||||
const ed448P = BigInt(
|
||||
'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.
|
||||
// Used for efficient square root calculation.
|
||||
// ((P-3)/4).toString(2) would produce bits [223x 1, 0, 222x 1]
|
||||
function ed448_pow_Pminus3div4(x: bigint): bigint {
|
||||
const P = ed448P;
|
||||
const b2 = (x * x * x) % P;
|
||||
const b3 = (b2 * b2 * x) % P;
|
||||
const b6 = (pow2(b3, _3n, P) * b3) % P;
|
||||
const b9 = (pow2(b6, _3n, P) * b3) % P;
|
||||
const b11 = (pow2(b9, _2n, P) * b2) % P;
|
||||
const b22 = (pow2(b11, _11n, P) * b11) % P;
|
||||
const b44 = (pow2(b22, _22n, P) * b22) % P;
|
||||
const b88 = (pow2(b44, _44n, P) * b44) % P;
|
||||
const b176 = (pow2(b88, _88n, P) * b88) % P;
|
||||
const b220 = (pow2(b176, _44n, P) * b44) % P;
|
||||
const b222 = (pow2(b220, _2n, P) * b2) % P;
|
||||
const b223 = (pow2(b222, _1n, P) * x) % P;
|
||||
return (pow2(b223, _223n, P) * b222) % P;
|
||||
}
|
||||
|
||||
function adjustScalarBytes(bytes: Uint8Array): Uint8Array {
|
||||
// Section 5: Likewise, for X448, set the two least significant bits of the first byte to 0, and the most
|
||||
// significant bit of the last byte to 1.
|
||||
bytes[0] &= 252; // 0b11111100
|
||||
// and the most significant bit of the last byte to 1.
|
||||
bytes[55] |= 128; // 0b10000000
|
||||
// NOTE: is is NOOP for 56 bytes scalars (X25519/X448)
|
||||
bytes[56] = 0; // Byte outside of group (456 buts vs 448 bits)
|
||||
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 ED448_DEF = {
|
||||
// Param: a
|
||||
a: BigInt(1),
|
||||
// -39081. Negative number is P - number
|
||||
d: BigInt(
|
||||
'726838724295606890549323807888004534353641360687318060281490199180612328166730772686396383698676545930088884461843637361053498018326358'
|
||||
),
|
||||
// Finite field 𝔽p over which we'll do calculations; 2n**448n - 2n**224n - 1n
|
||||
Fp,
|
||||
// Subgroup order: how many points curve has;
|
||||
// 2n**446n - 13818066809895115352007386748515426880336692474882178609894547503885n
|
||||
n: BigInt(
|
||||
'181709681073901722637330951972001133588410340171829515070372549795146003961539585716195755291692375963310293709091662304773755859649779'
|
||||
),
|
||||
// RFC 7748 has 56-byte keys, RFC 8032 has 57-byte keys
|
||||
nBitLength: 456,
|
||||
// Cofactor
|
||||
h: BigInt(4),
|
||||
// Base point (x, y) aka generator point
|
||||
Gx: BigInt(
|
||||
'224580040295924300187604334099896036246789641632564134246125461686950415467406032909029192869357953282578032075146446173674602635247710'
|
||||
),
|
||||
Gy: BigInt(
|
||||
'298819210078481492676017930443930673437544040154080242095928241372331506189835876003536878655418784733982303233503462500531545062832660'
|
||||
),
|
||||
// SHAKE256(dom4(phflag,context)||x, 114)
|
||||
hash: shake256_114,
|
||||
randomBytes,
|
||||
adjustScalarBytes,
|
||||
// dom4
|
||||
domain: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => {
|
||||
if (ctx.length > 255) throw new Error(`Context is too big: ${ctx.length}`);
|
||||
return concatBytes(
|
||||
utf8ToBytes('SigEd448'),
|
||||
new Uint8Array([phflag ? 1 : 0, ctx.length]),
|
||||
ctx,
|
||||
data
|
||||
);
|
||||
},
|
||||
uvRatio,
|
||||
} as const;
|
||||
|
||||
export const ed448 = /* @__PURE__ */ twistedEdwards(ED448_DEF);
|
||||
// NOTE: there is no ed448ctx, since ed448 supports ctx by default
|
||||
export const ed448ph = /* @__PURE__ */ twistedEdwards({ ...ED448_DEF, prehash: shake256_64 });
|
||||
|
||||
export const x448 = /* @__PURE__ */ (() =>
|
||||
montgomery({
|
||||
a: BigInt(156326),
|
||||
// RFC 7748 has 56-byte keys, RFC 8032 has 57-byte keys
|
||||
montgomeryBits: 448,
|
||||
nByteLength: 56,
|
||||
P: ed448P,
|
||||
Gu: BigInt(5),
|
||||
powPminus2: (x: bigint): bigint => {
|
||||
const P = ed448P;
|
||||
const Pminus3div4 = ed448_pow_Pminus3div4(x);
|
||||
const Pminus3 = pow2(Pminus3div4, BigInt(2), P);
|
||||
return mod(Pminus3 * x, P); // Pminus3 * x = Pminus2
|
||||
},
|
||||
adjustScalarBytes,
|
||||
randomBytes,
|
||||
}))();
|
||||
|
||||
/**
|
||||
* Converts edwards448 public key to x448 public key. Uses formula:
|
||||
* * `(u, v) = ((y-1)/(y+1), sqrt(156324)*u/x)`
|
||||
* * `(x, y) = (sqrt(156324)*u/v, (1+u)/(1-u))`
|
||||
* @example
|
||||
* const aPub = ed448.getPublicKey(utils.randomPrivateKey());
|
||||
* x448.getSharedSecret(edwardsToMontgomery(aPub), edwardsToMontgomery(someonesPub))
|
||||
*/
|
||||
export function edwardsToMontgomeryPub(edwardsPub: string | Uint8Array): Uint8Array {
|
||||
const { y } = ed448.ExtendedPoint.fromHex(edwardsPub);
|
||||
const _1n = BigInt(1);
|
||||
return Fp.toBytes(Fp.create((y - _1n) * Fp.inv(y + _1n)));
|
||||
}
|
||||
|
||||
export const edwardsToMontgomery = edwardsToMontgomeryPub; // deprecated
|
||||
// TODO: add edwardsToMontgomeryPriv, similar to ed25519 version
|
||||
|
||||
// Hash To Curve Elligator2 Map
|
||||
const ELL2_C1 = (Fp.ORDER - BigInt(3)) / BigInt(4); // 1. c1 = (q - 3) / 4 # Integer arithmetic
|
||||
const ELL2_J = BigInt(156326);
|
||||
|
||||
function map_to_curve_elligator2_curve448(u: bigint) {
|
||||
let tv1 = Fp.sqr(u); // 1. tv1 = u^2
|
||||
let e1 = Fp.eql(tv1, Fp.ONE); // 2. e1 = tv1 == 1
|
||||
tv1 = Fp.cmov(tv1, Fp.ZERO, e1); // 3. tv1 = CMOV(tv1, 0, e1) # If Z * u^2 == -1, set tv1 = 0
|
||||
let xd = Fp.sub(Fp.ONE, tv1); // 4. xd = 1 - tv1
|
||||
let x1n = Fp.neg(ELL2_J); // 5. x1n = -J
|
||||
let tv2 = Fp.sqr(xd); // 6. tv2 = xd^2
|
||||
let gxd = Fp.mul(tv2, xd); // 7. gxd = tv2 * xd # gxd = xd^3
|
||||
let gx1 = Fp.mul(tv1, Fp.neg(ELL2_J)); // 8. gx1 = -J * tv1 # x1n + J * xd
|
||||
gx1 = Fp.mul(gx1, x1n); // 9. gx1 = gx1 * x1n # x1n^2 + J * x1n * xd
|
||||
gx1 = Fp.add(gx1, tv2); // 10. gx1 = gx1 + tv2 # x1n^2 + J * x1n * xd + xd^2
|
||||
gx1 = Fp.mul(gx1, x1n); // 11. gx1 = gx1 * x1n # x1n^3 + J * x1n^2 * xd + x1n * xd^2
|
||||
let tv3 = Fp.sqr(gxd); // 12. tv3 = gxd^2
|
||||
tv2 = Fp.mul(gx1, gxd); // 13. tv2 = gx1 * gxd # gx1 * gxd
|
||||
tv3 = Fp.mul(tv3, tv2); // 14. tv3 = tv3 * tv2 # gx1 * gxd^3
|
||||
let y1 = Fp.pow(tv3, ELL2_C1); // 15. y1 = tv3^c1 # (gx1 * gxd^3)^((p - 3) / 4)
|
||||
y1 = Fp.mul(y1, tv2); // 16. y1 = y1 * tv2 # gx1 * gxd * (gx1 * gxd^3)^((p - 3) / 4)
|
||||
let x2n = Fp.mul(x1n, Fp.neg(tv1)); // 17. x2n = -tv1 * x1n # x2 = x2n / xd = -1 * u^2 * x1n / xd
|
||||
let y2 = Fp.mul(y1, u); // 18. y2 = y1 * u
|
||||
y2 = Fp.cmov(y2, Fp.ZERO, e1); // 19. y2 = CMOV(y2, 0, e1)
|
||||
tv2 = Fp.sqr(y1); // 20. tv2 = y1^2
|
||||
tv2 = Fp.mul(tv2, gxd); // 21. tv2 = tv2 * gxd
|
||||
let e2 = Fp.eql(tv2, gx1); // 22. e2 = tv2 == gx1
|
||||
let xn = Fp.cmov(x2n, x1n, e2); // 23. xn = CMOV(x2n, x1n, e2) # If e2, x = x1, else x = x2
|
||||
let y = Fp.cmov(y2, y1, e2); // 24. y = CMOV(y2, y1, e2) # If e2, y = y1, else y = y2
|
||||
let e3 = Fp.isOdd(y); // 25. e3 = sgn0(y) == 1 # Fix sign of y
|
||||
y = Fp.cmov(y, Fp.neg(y), e2 !== e3); // 26. y = CMOV(y, -y, e2 XOR e3)
|
||||
return { xn, xd, yn: y, yd: Fp.ONE }; // 27. return (xn, xd, y, 1)
|
||||
}
|
||||
|
||||
function map_to_curve_elligator2_edwards448(u: bigint) {
|
||||
let { xn, xd, yn, yd } = map_to_curve_elligator2_curve448(u); // 1. (xn, xd, yn, yd) = map_to_curve_elligator2_curve448(u)
|
||||
let xn2 = Fp.sqr(xn); // 2. xn2 = xn^2
|
||||
let xd2 = Fp.sqr(xd); // 3. xd2 = xd^2
|
||||
let xd4 = Fp.sqr(xd2); // 4. xd4 = xd2^2
|
||||
let yn2 = Fp.sqr(yn); // 5. yn2 = yn^2
|
||||
let yd2 = Fp.sqr(yd); // 6. yd2 = yd^2
|
||||
let xEn = Fp.sub(xn2, xd2); // 7. xEn = xn2 - xd2
|
||||
let tv2 = Fp.sub(xEn, xd2); // 8. tv2 = xEn - xd2
|
||||
xEn = Fp.mul(xEn, xd2); // 9. xEn = xEn * xd2
|
||||
xEn = Fp.mul(xEn, yd); // 10. xEn = xEn * yd
|
||||
xEn = Fp.mul(xEn, yn); // 11. xEn = xEn * yn
|
||||
xEn = Fp.mul(xEn, _4n); // 12. xEn = xEn * 4
|
||||
tv2 = Fp.mul(tv2, xn2); // 13. tv2 = tv2 * xn2
|
||||
tv2 = Fp.mul(tv2, yd2); // 14. tv2 = tv2 * yd2
|
||||
let tv3 = Fp.mul(yn2, _4n); // 15. tv3 = 4 * yn2
|
||||
let tv1 = Fp.add(tv3, yd2); // 16. tv1 = tv3 + yd2
|
||||
tv1 = Fp.mul(tv1, xd4); // 17. tv1 = tv1 * xd4
|
||||
let xEd = Fp.add(tv1, tv2); // 18. xEd = tv1 + tv2
|
||||
tv2 = Fp.mul(tv2, xn); // 19. tv2 = tv2 * xn
|
||||
let tv4 = Fp.mul(xn, xd4); // 20. tv4 = xn * xd4
|
||||
let yEn = Fp.sub(tv3, yd2); // 21. yEn = tv3 - yd2
|
||||
yEn = Fp.mul(yEn, tv4); // 22. yEn = yEn * tv4
|
||||
yEn = Fp.sub(yEn, tv2); // 23. yEn = yEn - tv2
|
||||
tv1 = Fp.add(xn2, xd2); // 24. tv1 = xn2 + xd2
|
||||
tv1 = Fp.mul(tv1, xd2); // 25. tv1 = tv1 * xd2
|
||||
tv1 = Fp.mul(tv1, xd); // 26. tv1 = tv1 * xd
|
||||
tv1 = Fp.mul(tv1, yn2); // 27. tv1 = tv1 * yn2
|
||||
tv1 = Fp.mul(tv1, BigInt(-2)); // 28. tv1 = -2 * tv1
|
||||
let yEd = Fp.add(tv2, tv1); // 29. yEd = tv2 + tv1
|
||||
tv4 = Fp.mul(tv4, yd2); // 30. tv4 = tv4 * yd2
|
||||
yEd = Fp.add(yEd, tv4); // 31. yEd = yEd + tv4
|
||||
tv1 = Fp.mul(xEd, yEd); // 32. tv1 = xEd * yEd
|
||||
let e = Fp.eql(tv1, Fp.ZERO); // 33. e = tv1 == 0
|
||||
xEn = Fp.cmov(xEn, Fp.ZERO, e); // 34. xEn = CMOV(xEn, 0, e)
|
||||
xEd = Fp.cmov(xEd, Fp.ONE, e); // 35. xEd = CMOV(xEd, 1, e)
|
||||
yEn = Fp.cmov(yEn, Fp.ONE, e); // 36. yEn = CMOV(yEn, 1, e)
|
||||
yEd = Fp.cmov(yEd, Fp.ONE, e); // 37. yEd = CMOV(yEd, 1, e)
|
||||
|
||||
const inv = Fp.invertBatch([xEd, yEd]); // batch division
|
||||
return { x: Fp.mul(xEn, inv[0]), y: Fp.mul(yEn, inv[1]) }; // 38. return (xEn, xEd, yEn, yEd)
|
||||
}
|
||||
|
||||
const htf = /* @__PURE__ */ (() =>
|
||||
createHasher(
|
||||
ed448.ExtendedPoint,
|
||||
(scalars: bigint[]) => map_to_curve_elligator2_edwards448(scalars[0]),
|
||||
{
|
||||
DST: 'edwards448_XOF:SHAKE256_ELL2_RO_',
|
||||
encodeDST: 'edwards448_XOF:SHAKE256_ELL2_NU_',
|
||||
p: Fp.ORDER,
|
||||
m: 1,
|
||||
k: 224,
|
||||
expand: 'xof',
|
||||
hash: shake256,
|
||||
}
|
||||
))();
|
||||
export 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'
|
||||
);
|
||||
// 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
|
1
src/index.ts
Normal file
1
src/index.ts
Normal file
@ -0,0 +1 @@
|
||||
throw new Error('Incorrect usage. Import submodules instead');
|
58
src/jubjub.ts
Normal file
58
src/jubjub.ts
Normal file
@ -0,0 +1,58 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
import { sha512 } from '@noble/hashes/sha512';
|
||||
import { concatBytes, randomBytes, utf8ToBytes } from '@noble/hashes/utils';
|
||||
import { twistedEdwards } from './abstract/edwards.js';
|
||||
import { blake2s } from '@noble/hashes/blake2s';
|
||||
import { Field } from './abstract/modular.js';
|
||||
|
||||
/**
|
||||
* jubjub Twisted Edwards curve.
|
||||
* https://neuromancer.sk/std/other/JubJub
|
||||
* jubjub does not use EdDSA, so `hash`/sha512 params are passed because interface expects them.
|
||||
*/
|
||||
|
||||
export const jubjub = /* @__PURE__ */ twistedEdwards({
|
||||
// Params: a, d
|
||||
a: BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000'),
|
||||
d: BigInt('0x2a9318e74bfa2b48f5fd9207e6bd7fd4292d7f6d37579d2601065fd6d6343eb1'),
|
||||
// Finite field 𝔽p over which we'll do calculations
|
||||
// Same value as bls12-381 Fr (not Fp)
|
||||
Fp: Field(BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001')),
|
||||
// Subgroup order: how many points curve has
|
||||
n: BigInt('0xe7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7'),
|
||||
// Cofactor
|
||||
h: BigInt(8),
|
||||
// Base point (x, y) aka generator point
|
||||
Gx: BigInt('0x11dafe5d23e1218086a365b99fbf3d3be72f6afd7d1f72623e6b071492d1122b'),
|
||||
Gy: BigInt('0x1d523cf1ddab1a1793132e78c866c0c33e26ba5cc220fed7cc3f870e59d292aa'),
|
||||
hash: sha512,
|
||||
randomBytes,
|
||||
} as const);
|
||||
|
||||
const GH_FIRST_BLOCK = utf8ToBytes(
|
||||
'096b36a5804bfacef1691e173c366a47ff5ba84a44f26ddd7e8d9f79d5b42df0'
|
||||
);
|
||||
|
||||
// Returns point at JubJub curve which is prime order and not zero
|
||||
export function groupHash(tag: Uint8Array, personalization: Uint8Array) {
|
||||
const h = blake2s.create({ personalization, dkLen: 32 });
|
||||
h.update(GH_FIRST_BLOCK);
|
||||
h.update(tag);
|
||||
// NOTE: returns ExtendedPoint, in case it will be multiplied later
|
||||
let p = jubjub.ExtendedPoint.fromHex(h.digest());
|
||||
// NOTE: cannot replace with isSmallOrder, returns Point*8
|
||||
p = p.multiply(jubjub.CURVE.h);
|
||||
if (p.equals(jubjub.ExtendedPoint.ZERO)) throw new Error('Point has small order');
|
||||
return p;
|
||||
}
|
||||
|
||||
export function findGroupHash(m: Uint8Array, personalization: Uint8Array) {
|
||||
const tag = concatBytes(m, new Uint8Array([0]));
|
||||
for (let i = 0; i < 256; i++) {
|
||||
tag[tag.length - 1] = i;
|
||||
try {
|
||||
return groupHash(tag, personalization);
|
||||
} catch (e) {}
|
||||
}
|
||||
throw new Error('findGroupHash tag overflow');
|
||||
}
|
146
src/modular.ts
146
src/modular.ts
@ -1,146 +0,0 @@
|
||||
/*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
const _0n = BigInt(0);
|
||||
const _1n = BigInt(1);
|
||||
const _2n = BigInt(2);
|
||||
const _3n = BigInt(3);
|
||||
const _4n = BigInt(4);
|
||||
const _5n = BigInt(5);
|
||||
const _8n = BigInt(8);
|
||||
|
||||
// Calculates a modulo b
|
||||
export function mod(a: bigint, b: bigint): bigint {
|
||||
const result = a % b;
|
||||
return result >= _0n ? result : b + result;
|
||||
}
|
||||
/**
|
||||
* Efficiently exponentiate num to power and do modular division.
|
||||
* @example
|
||||
* powMod(2n, 6n, 11n) // 64n % 11n == 9n
|
||||
*/
|
||||
export function pow(num: bigint, power: bigint, modulo: bigint): bigint {
|
||||
if (modulo <= _0n || power < _0n) throw new Error('Expected power/modulo > 0');
|
||||
if (modulo === _1n) return _0n;
|
||||
let res = _1n;
|
||||
while (power > _0n) {
|
||||
if (power & _1n) res = (res * num) % modulo;
|
||||
num = (num * num) % modulo;
|
||||
power >>= _1n;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// Does x ^ (2 ^ power) mod p. pow2(30, 4) == 30 ^ (2 ^ 4)
|
||||
export function pow2(x: bigint, power: bigint, modulo: bigint): bigint {
|
||||
let res = x;
|
||||
while (power-- > _0n) {
|
||||
res *= res;
|
||||
res %= modulo;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// Inverses number over modulo
|
||||
export function invert(number: bigint, modulo: bigint): bigint {
|
||||
if (number === _0n || modulo <= _0n) {
|
||||
throw new Error(`invert: expected positive integers, got n=${number} mod=${modulo}`);
|
||||
}
|
||||
// Eucledian GCD https://brilliant.org/wiki/extended-euclidean-algorithm/
|
||||
let a = mod(number, modulo);
|
||||
let b = modulo;
|
||||
// prettier-ignore
|
||||
let x = _0n, y = _1n, u = _1n, v = _0n;
|
||||
while (a !== _0n) {
|
||||
const q = b / a;
|
||||
const r = b % a;
|
||||
const m = x - u * q;
|
||||
const n = y - v * q;
|
||||
// prettier-ignore
|
||||
b = a, a = r, x = u, y = v, u = m, v = n;
|
||||
}
|
||||
const gcd = b;
|
||||
if (gcd !== _1n) throw new Error('invert: does not exist');
|
||||
return mod(x, modulo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a list of numbers, efficiently inverts all of them.
|
||||
* @param nums list of bigints
|
||||
* @param p modulo
|
||||
* @returns list of inverted bigints
|
||||
* @example
|
||||
* invertBatch([1n, 2n, 4n], 21n);
|
||||
* // => [1n, 11n, 16n]
|
||||
*/
|
||||
export function invertBatch(nums: bigint[], modulo: bigint): bigint[] {
|
||||
const scratch = new Array(nums.length);
|
||||
// Walk from first to last, multiply them by each other MOD p
|
||||
const lastMultiplied = nums.reduce((acc, num, i) => {
|
||||
if (num === _0n) return acc;
|
||||
scratch[i] = acc;
|
||||
return mod(acc * num, modulo);
|
||||
}, _1n);
|
||||
// Invert last element
|
||||
const inverted = invert(lastMultiplied, modulo);
|
||||
// Walk from last to first, multiply them by inverted each other MOD p
|
||||
nums.reduceRight((acc, num, i) => {
|
||||
if (num === _0n) return acc;
|
||||
scratch[i] = mod(acc * scratch[i], modulo);
|
||||
return mod(acc * num, modulo);
|
||||
}, inverted);
|
||||
return scratch;
|
||||
}
|
||||
|
||||
// Calculates Legendre symbol: num^((P-1)/2)
|
||||
export function legendre(num: bigint, fieldPrime: bigint): bigint {
|
||||
return pow(num, (fieldPrime - _1n) / _2n, fieldPrime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates square root of a number in a finite field.
|
||||
* Used to calculate y - the square root of y².
|
||||
*/
|
||||
export function sqrt(number: bigint, modulo: bigint): bigint {
|
||||
const n = number;
|
||||
const P = modulo;
|
||||
const p1div4 = (P + _1n) / _4n;
|
||||
|
||||
// P = 3 (mod 4)
|
||||
// sqrt n = n^((P+1)/4)
|
||||
if (P % _4n === _3n) return pow(n, p1div4, P);
|
||||
// P = 5 (mod 8)
|
||||
if (P % _8n === _5n) {
|
||||
const n2 = mod(n * _2n, P);
|
||||
const v = pow(n2, (P - _5n) / _8n, P);
|
||||
const nv = mod(n * v, P);
|
||||
const i = mod(_2n * nv * v, P);
|
||||
const r = mod(nv * (i - _1n), P);
|
||||
return r;
|
||||
}
|
||||
|
||||
// Other cases: Tonelli-Shanks algorithm
|
||||
if (legendre(n, P) !== _1n) throw new Error('Cannot find square root');
|
||||
let q: bigint, s: number, z: bigint;
|
||||
for (q = P - _1n, s = 0; q % _2n === _0n; q /= _2n, s++);
|
||||
if (s === 1) return pow(n, p1div4, P);
|
||||
for (z = _2n; z < P && legendre(z, P) !== P - _1n; z++);
|
||||
|
||||
let c = pow(z, q, P);
|
||||
let r = pow(n, (q + _1n) / _2n, P);
|
||||
let t = pow(n, q, P);
|
||||
|
||||
let t2 = _0n;
|
||||
while (mod(t - _1n, P) !== _0n) {
|
||||
t2 = mod(t * t, P);
|
||||
let i;
|
||||
for (i = 1; i < s; i++) {
|
||||
if (mod(t2 - _1n, P) === _0n) break;
|
||||
t2 = mod(t2 * t2, P);
|
||||
}
|
||||
let b = pow(c, BigInt(1 << (s - i - 1)), P);
|
||||
r = mod(r * b, P);
|
||||
c = mod(b * b, P);
|
||||
t = mod(t * c, P);
|
||||
s = i;
|
||||
}
|
||||
return r;
|
||||
}
|
48
src/p256.ts
Normal file
48
src/p256.ts
Normal file
@ -0,0 +1,48 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
import { createCurve } from './_shortw_utils.js';
|
||||
import { sha256 } from '@noble/hashes/sha256';
|
||||
import { Field } from './abstract/modular.js';
|
||||
import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
|
||||
import { createHasher } from './abstract/hash-to-curve.js';
|
||||
|
||||
// NIST secp256r1 aka p256
|
||||
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/nist/P-256
|
||||
|
||||
const Fp = Field(BigInt('0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff'));
|
||||
const CURVE_A = Fp.create(BigInt('-3'));
|
||||
const CURVE_B = BigInt('0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b');
|
||||
|
||||
// prettier-ignore
|
||||
export const p256 = createCurve({
|
||||
a: CURVE_A, // Equation params: a, b
|
||||
b: CURVE_B,
|
||||
Fp, // Field: 2n**224n * (2n**32n-1n) + 2n**192n + 2n**96n-1n
|
||||
// Curve order, total count of valid points in the field
|
||||
n: BigInt('0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551'),
|
||||
// Base (generator) point (x, y)
|
||||
Gx: BigInt('0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296'),
|
||||
Gy: BigInt('0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5'),
|
||||
h: BigInt(1),
|
||||
lowS: false,
|
||||
} as const, sha256);
|
||||
export const secp256r1 = p256;
|
||||
|
||||
const mapSWU = /* @__PURE__ */ (() =>
|
||||
mapToCurveSimpleSWU(Fp, {
|
||||
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_',
|
||||
encodeDST: 'P256_XMD:SHA-256_SSWU_NU_',
|
||||
p: Fp.ORDER,
|
||||
m: 1,
|
||||
k: 128,
|
||||
expand: 'xmd',
|
||||
hash: sha256,
|
||||
}))();
|
||||
export const hashToCurve = /* @__PURE__ */ (() => htf.hashToCurve)();
|
||||
export const encodeToCurve = /* @__PURE__ */ (() => htf.encodeToCurve)();
|
52
src/p384.ts
Normal file
52
src/p384.ts
Normal file
@ -0,0 +1,52 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
import { createCurve } from './_shortw_utils.js';
|
||||
import { sha384 } from '@noble/hashes/sha512';
|
||||
import { Field } from './abstract/modular.js';
|
||||
import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
|
||||
import { createHasher } from './abstract/hash-to-curve.js';
|
||||
|
||||
// NIST secp384r1 aka p384
|
||||
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/nist/P-384
|
||||
|
||||
// Field over which we'll do calculations.
|
||||
// prettier-ignore
|
||||
const P = BigInt('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff');
|
||||
const Fp = Field(P);
|
||||
const CURVE_A = Fp.create(BigInt('-3'));
|
||||
// prettier-ignore
|
||||
const CURVE_B = BigInt('0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef');
|
||||
|
||||
// prettier-ignore
|
||||
export const p384 = createCurve({
|
||||
a: CURVE_A, // Equation params: a, b
|
||||
b: CURVE_B,
|
||||
Fp, // Field: 2n**384n - 2n**128n - 2n**96n + 2n**32n - 1n
|
||||
// Curve order, total count of valid points in the field.
|
||||
n: BigInt('0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973'),
|
||||
// Base (generator) point (x, y)
|
||||
Gx: BigInt('0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7'),
|
||||
Gy: BigInt('0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f'),
|
||||
h: BigInt(1),
|
||||
lowS: false,
|
||||
} as const, sha384);
|
||||
export const secp384r1 = p384;
|
||||
|
||||
const mapSWU = /* @__PURE__ */ (() =>
|
||||
mapToCurveSimpleSWU(Fp, {
|
||||
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_',
|
||||
encodeDST: 'P384_XMD:SHA-384_SSWU_NU_',
|
||||
p: Fp.ORDER,
|
||||
m: 1,
|
||||
k: 192,
|
||||
expand: 'xmd',
|
||||
hash: sha384,
|
||||
}))();
|
||||
export const hashToCurve = /* @__PURE__ */ (() => htf.hashToCurve)();
|
||||
export const encodeToCurve = /* @__PURE__ */ (() => htf.encodeToCurve)();
|
68
src/p521.ts
Normal file
68
src/p521.ts
Normal file
@ -0,0 +1,68 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
import { createCurve } from './_shortw_utils.js';
|
||||
import { sha512 } from '@noble/hashes/sha512';
|
||||
import { Field } from './abstract/modular.js';
|
||||
import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
|
||||
import { createHasher } from './abstract/hash-to-curve.js';
|
||||
|
||||
// NIST secp521r1 aka p521
|
||||
// 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
|
||||
|
||||
// Field over which we'll do calculations.
|
||||
// prettier-ignore
|
||||
const P = BigInt('0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
|
||||
const Fp = Field(P);
|
||||
|
||||
const CURVE = {
|
||||
a: Fp.create(BigInt('-3')),
|
||||
b: BigInt(
|
||||
'0x0051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00'
|
||||
),
|
||||
Fp,
|
||||
n: BigInt(
|
||||
'0x01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409'
|
||||
),
|
||||
Gx: BigInt(
|
||||
'0x00c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66'
|
||||
),
|
||||
Gy: BigInt(
|
||||
'0x011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650'
|
||||
),
|
||||
h: BigInt(1),
|
||||
};
|
||||
|
||||
// prettier-ignore
|
||||
export const p521 = createCurve({
|
||||
a: CURVE.a, // Equation params: a, b
|
||||
b: CURVE.b,
|
||||
Fp, // Field: 2n**521n - 1n
|
||||
// Curve order, total count of valid points in the field
|
||||
n: CURVE.n,
|
||||
Gx: CURVE.Gx, // Base point (x, y) aka generator point
|
||||
Gy: CURVE.Gy,
|
||||
h: CURVE.h,
|
||||
lowS: false,
|
||||
allowedPrivateKeyLengths: [130, 131, 132] // P521 keys are variable-length. Normalize to 132b
|
||||
} as const, sha512);
|
||||
export const secp521r1 = p521;
|
||||
|
||||
const mapSWU = /* @__PURE__ */ (() =>
|
||||
mapToCurveSimpleSWU(Fp, {
|
||||
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_',
|
||||
encodeDST: 'P521_XMD:SHA-512_SSWU_NU_',
|
||||
p: Fp.ORDER,
|
||||
m: 1,
|
||||
k: 256,
|
||||
expand: 'xmd',
|
||||
hash: sha512,
|
||||
}))();
|
||||
export const hashToCurve = /* @__PURE__ */ (() => htf.hashToCurve)();
|
||||
export const encodeToCurve = /* @__PURE__ */ (() => htf.encodeToCurve)();
|
3
src/package.json
Normal file
3
src/package.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"type": "module"
|
||||
}
|
31
src/pasta.ts
Normal file
31
src/pasta.ts
Normal file
@ -0,0 +1,31 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
import { sha256 } from '@noble/hashes/sha256';
|
||||
import { weierstrass } from './abstract/weierstrass.js';
|
||||
import { getHash } from './_shortw_utils.js';
|
||||
import * as mod from './abstract/modular.js';
|
||||
|
||||
export const p = BigInt('0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001');
|
||||
export const q = BigInt('0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001');
|
||||
|
||||
// https://neuromancer.sk/std/other/Pallas
|
||||
export const pallas = weierstrass({
|
||||
a: BigInt(0),
|
||||
b: BigInt(5),
|
||||
Fp: mod.Field(p),
|
||||
n: q,
|
||||
Gx: mod.mod(BigInt(-1), p),
|
||||
Gy: BigInt(2),
|
||||
h: BigInt(1),
|
||||
...getHash(sha256),
|
||||
});
|
||||
// https://neuromancer.sk/std/other/Vesta
|
||||
export const vesta = weierstrass({
|
||||
a: BigInt(0),
|
||||
b: BigInt(5),
|
||||
Fp: mod.Field(q),
|
||||
n: p,
|
||||
Gx: mod.mod(BigInt(-1), q),
|
||||
Gy: BigInt(2),
|
||||
h: BigInt(1),
|
||||
...getHash(sha256),
|
||||
});
|
274
src/secp256k1.ts
Normal file
274
src/secp256k1.ts
Normal file
@ -0,0 +1,274 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
import { sha256 } from '@noble/hashes/sha256';
|
||||
import { randomBytes } from '@noble/hashes/utils';
|
||||
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';
|
||||
import { bytesToNumberBE, concatBytes, ensureBytes, numberToBytesBE } from './abstract/utils.js';
|
||||
import { createHasher, isogenyMap } from './abstract/hash-to-curve.js';
|
||||
import { createCurve } from './_shortw_utils.js';
|
||||
|
||||
const secp256k1P = BigInt('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f');
|
||||
const secp256k1N = BigInt('0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141');
|
||||
const _1n = BigInt(1);
|
||||
const _2n = BigInt(2);
|
||||
const divNearest = (a: bigint, b: bigint) => (a + b / _2n) / b;
|
||||
|
||||
/**
|
||||
* √n = n^((p+1)/4) for fields p = 3 mod 4. We unwrap the loop and multiply bit-by-bit.
|
||||
* (P+1n/4n).toString(2) would produce bits [223x 1, 0, 22x 1, 4x 0, 11, 00]
|
||||
*/
|
||||
function sqrtMod(y: bigint): bigint {
|
||||
const P = secp256k1P;
|
||||
// prettier-ignore
|
||||
const _3n = BigInt(3), _6n = BigInt(6), _11n = BigInt(11), _22n = BigInt(22);
|
||||
// prettier-ignore
|
||||
const _23n = BigInt(23), _44n = BigInt(44), _88n = BigInt(88);
|
||||
const b2 = (y * y * y) % P; // x^3, 11
|
||||
const b3 = (b2 * b2 * y) % P; // x^7
|
||||
const b6 = (pow2(b3, _3n, P) * b3) % P;
|
||||
const b9 = (pow2(b6, _3n, P) * b3) % P;
|
||||
const b11 = (pow2(b9, _2n, P) * b2) % P;
|
||||
const b22 = (pow2(b11, _11n, P) * b11) % P;
|
||||
const b44 = (pow2(b22, _22n, P) * b22) % P;
|
||||
const b88 = (pow2(b44, _44n, P) * b44) % P;
|
||||
const b176 = (pow2(b88, _88n, P) * b88) % P;
|
||||
const b220 = (pow2(b176, _44n, P) * b44) % P;
|
||||
const b223 = (pow2(b220, _3n, P) * b3) % P;
|
||||
const t1 = (pow2(b223, _23n, P) * b22) % P;
|
||||
const t2 = (pow2(t1, _6n, P) * b2) % P;
|
||||
const root = pow2(t2, _2n, P);
|
||||
if (!Fp.eql(Fp.sqr(root), y)) throw new Error('Cannot find square root');
|
||||
return root;
|
||||
}
|
||||
|
||||
const Fp = Field(secp256k1P, undefined, undefined, { sqrt: sqrtMod });
|
||||
|
||||
export const secp256k1 = createCurve(
|
||||
{
|
||||
a: BigInt(0), // equation params: a, b
|
||||
b: BigInt(7), // Seem to be rigid: bitcointalk.org/index.php?topic=289795.msg3183975#msg3183975
|
||||
Fp, // Field's prime: 2n**256n - 2n**32n - 2n**9n - 2n**8n - 2n**7n - 2n**6n - 2n**4n - 1n
|
||||
n: secp256k1N, // Curve order, total count of valid points in the field
|
||||
// Base point (x, y) aka generator point
|
||||
Gx: BigInt('55066263022277343669578718895168534326250603453777594175500187360389116729240'),
|
||||
Gy: BigInt('32670510020758816978083085130507043184471273380659243275938904335757337482424'),
|
||||
h: BigInt(1), // Cofactor
|
||||
lowS: true, // Allow only low-S signatures by default in sign() and verify()
|
||||
/**
|
||||
* secp256k1 belongs to Koblitz curves: it has efficiently computable endomorphism.
|
||||
* Endomorphism uses 2x less RAM, speeds up precomputation by 2x and ECDH / key recovery by 20%.
|
||||
* For precomputed wNAF it trades off 1/2 init time & 1/3 ram for 20% perf hit.
|
||||
* Explanation: https://gist.github.com/paulmillr/eb670806793e84df628a7c434a873066
|
||||
*/
|
||||
endo: {
|
||||
beta: BigInt('0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee'),
|
||||
splitScalar: (k: bigint) => {
|
||||
const n = secp256k1N;
|
||||
const a1 = BigInt('0x3086d221a7d46bcde86c90e49284eb15');
|
||||
const b1 = -_1n * BigInt('0xe4437ed6010e88286f547fa90abfe4c3');
|
||||
const a2 = BigInt('0x114ca50f7a8e2f3f657c1108d9d44cfd8');
|
||||
const b2 = a1;
|
||||
const POW_2_128 = BigInt('0x100000000000000000000000000000000'); // (2n**128n).toString(16)
|
||||
|
||||
const c1 = divNearest(b2 * k, n);
|
||||
const c2 = divNearest(-b1 * k, n);
|
||||
let k1 = mod(k - c1 * a1 - c2 * a2, n);
|
||||
let k2 = mod(-c1 * b1 - c2 * b2, n);
|
||||
const k1neg = k1 > POW_2_128;
|
||||
const k2neg = k2 > POW_2_128;
|
||||
if (k1neg) k1 = n - k1;
|
||||
if (k2neg) k2 = n - k2;
|
||||
if (k1 > POW_2_128 || k2 > POW_2_128) {
|
||||
throw new Error('splitScalar: Endomorphism failed, k=' + k);
|
||||
}
|
||||
return { k1neg, k1, k2neg, k2 };
|
||||
},
|
||||
},
|
||||
},
|
||||
sha256
|
||||
);
|
||||
|
||||
// Schnorr signatures are superior to ECDSA from above. Below is Schnorr-specific BIP0340 code.
|
||||
// https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki
|
||||
const _0n = BigInt(0);
|
||||
const fe = (x: bigint) => typeof x === 'bigint' && _0n < x && x < secp256k1P;
|
||||
const ge = (x: bigint) => typeof x === 'bigint' && _0n < x && x < secp256k1N;
|
||||
/** An object mapping tags to their tagged hash prefix of [SHA256(tag) | SHA256(tag)] */
|
||||
const TAGGED_HASH_PREFIXES: { [tag: string]: Uint8Array } = {};
|
||||
function taggedHash(tag: string, ...messages: Uint8Array[]): Uint8Array {
|
||||
let tagP = TAGGED_HASH_PREFIXES[tag];
|
||||
if (tagP === undefined) {
|
||||
const tagH = sha256(Uint8Array.from(tag, (c) => c.charCodeAt(0)));
|
||||
tagP = concatBytes(tagH, tagH);
|
||||
TAGGED_HASH_PREFIXES[tag] = tagP;
|
||||
}
|
||||
return sha256(concatBytes(tagP, ...messages));
|
||||
}
|
||||
|
||||
// ECDSA compact points are 33-byte. Schnorr is 32: we strip first byte 0x02 or 0x03
|
||||
const pointToBytes = (point: PointType<bigint>) => point.toRawBytes(true).slice(1);
|
||||
const numTo32b = (n: bigint) => numberToBytesBE(n, 32);
|
||||
const modP = (x: bigint) => mod(x, secp256k1P);
|
||||
const modN = (x: bigint) => mod(x, secp256k1N);
|
||||
const Point = secp256k1.ProjectivePoint;
|
||||
const GmulAdd = (Q: PointType<bigint>, a: bigint, b: bigint) =>
|
||||
Point.BASE.multiplyAndAddUnsafe(Q, a, b);
|
||||
|
||||
// Calculate point, scalar and bytes
|
||||
function schnorrGetExtPubKey(priv: PrivKey) {
|
||||
let d_ = secp256k1.utils.normPrivateKeyToScalar(priv); // same method executed in fromPrivateKey
|
||||
let p = Point.fromPrivateKey(d_); // P = d'⋅G; 0 < d' < n check is done inside
|
||||
const scalar = p.hasEvenY() ? d_ : modN(-d_);
|
||||
return { scalar: scalar, bytes: pointToBytes(p) };
|
||||
}
|
||||
/**
|
||||
* lift_x from BIP340. Convert 32-byte x coordinate to elliptic curve point.
|
||||
* @returns valid point checked for being on-curve
|
||||
*/
|
||||
function lift_x(x: bigint): PointType<bigint> {
|
||||
if (!fe(x)) throw new Error('bad x: need 0 < x < p'); // Fail if x ≥ p.
|
||||
const xx = modP(x * x);
|
||||
const c = modP(xx * x + BigInt(7)); // Let c = x³ + 7 mod p.
|
||||
let y = sqrtMod(c); // Let y = c^(p+1)/4 mod p.
|
||||
if (y % _2n !== _0n) y = modP(-y); // Return the unique point P such that x(P) = x and
|
||||
const p = new Point(x, y, _1n); // y(P) = y if y mod 2 = 0 or y(P) = p-y otherwise.
|
||||
p.assertValidity();
|
||||
return p;
|
||||
}
|
||||
/**
|
||||
* Create tagged hash, convert it to bigint, reduce modulo-n.
|
||||
*/
|
||||
function challenge(...args: Uint8Array[]): bigint {
|
||||
return modN(bytesToNumberBE(taggedHash('BIP0340/challenge', ...args)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Schnorr public key is just `x` coordinate of Point as per BIP340.
|
||||
*/
|
||||
function schnorrGetPublicKey(privateKey: Hex): Uint8Array {
|
||||
return schnorrGetExtPubKey(privateKey).bytes; // d'=int(sk). Fail if d'=0 or d'≥n. Ret bytes(d'⋅G)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates Schnorr signature as per BIP340. Verifies itself before returning anything.
|
||||
* auxRand is optional and is not the sole source of k generation: bad CSPRNG won't be dangerous.
|
||||
*/
|
||||
function schnorrSign(
|
||||
message: Hex,
|
||||
privateKey: PrivKey,
|
||||
auxRand: Hex = randomBytes(32)
|
||||
): Uint8Array {
|
||||
const m = ensureBytes('message', message);
|
||||
const { bytes: px, scalar: d } = schnorrGetExtPubKey(privateKey); // checks for isWithinCurveOrder
|
||||
const a = ensureBytes('auxRand', auxRand, 32); // Auxiliary random data a: a 32-byte array
|
||||
const t = numTo32b(d ^ bytesToNumberBE(taggedHash('BIP0340/aux', a))); // Let t be the byte-wise xor of bytes(d) and hash/aux(a)
|
||||
const rand = taggedHash('BIP0340/nonce', t, px, m); // Let rand = hash/nonce(t || bytes(P) || m)
|
||||
const k_ = modN(bytesToNumberBE(rand)); // Let k' = int(rand) mod n
|
||||
if (k_ === _0n) throw new Error('sign failed: k is zero'); // Fail if k' = 0.
|
||||
const { bytes: rx, scalar: k } = schnorrGetExtPubKey(k_); // Let R = k'⋅G.
|
||||
const e = challenge(rx, px, m); // Let e = int(hash/challenge(bytes(R) || bytes(P) || m)) mod n.
|
||||
const sig = new Uint8Array(64); // Let sig = bytes(R) || bytes((k + ed) mod n).
|
||||
sig.set(rx, 0);
|
||||
sig.set(numTo32b(modN(k + e * d)), 32);
|
||||
// If Verify(bytes(P), m, sig) (see below) returns failure, abort
|
||||
if (!schnorrVerify(sig, m, px)) throw new Error('sign: Invalid signature produced');
|
||||
return sig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies Schnorr signature.
|
||||
* Will swallow errors & return false except for initial type validation of arguments.
|
||||
*/
|
||||
function schnorrVerify(signature: Hex, message: Hex, publicKey: Hex): boolean {
|
||||
const sig = ensureBytes('signature', signature, 64);
|
||||
const m = ensureBytes('message', message);
|
||||
const pub = ensureBytes('publicKey', publicKey, 32);
|
||||
try {
|
||||
const P = lift_x(bytesToNumberBE(pub)); // P = lift_x(int(pk)); fail if that fails
|
||||
const r = bytesToNumberBE(sig.subarray(0, 32)); // Let r = int(sig[0:32]); fail if r ≥ p.
|
||||
if (!fe(r)) return false;
|
||||
const s = bytesToNumberBE(sig.subarray(32, 64)); // Let s = int(sig[32:64]); fail if s ≥ n.
|
||||
if (!ge(s)) return false;
|
||||
const e = challenge(numTo32b(r), pointToBytes(P), m); // int(challenge(bytes(r)||bytes(P)||m))%n
|
||||
const R = GmulAdd(P, s, modN(-e)); // R = s⋅G - e⋅P
|
||||
if (!R || !R.hasEvenY() || R.toAffine().x !== r) return false; // -eP == (n-e)P
|
||||
return true; // Fail if is_infinite(R) / not has_even_y(R) / x(R) ≠ r.
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export const schnorr = /* @__PURE__ */ (() => ({
|
||||
getPublicKey: schnorrGetPublicKey,
|
||||
sign: schnorrSign,
|
||||
verify: schnorrVerify,
|
||||
utils: {
|
||||
randomPrivateKey: secp256k1.utils.randomPrivateKey,
|
||||
lift_x,
|
||||
pointToBytes,
|
||||
numberToBytesBE,
|
||||
bytesToNumberBE,
|
||||
taggedHash,
|
||||
mod,
|
||||
},
|
||||
}))();
|
||||
|
||||
const isoMap = /* @__PURE__ */ (() =>
|
||||
isogenyMap(
|
||||
Fp,
|
||||
[
|
||||
// xNum
|
||||
[
|
||||
'0x8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa8c7',
|
||||
'0x7d3d4c80bc321d5b9f315cea7fd44c5d595d2fc0bf63b92dfff1044f17c6581',
|
||||
'0x534c328d23f234e6e2a413deca25caece4506144037c40314ecbd0b53d9dd262',
|
||||
'0x8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa88c',
|
||||
],
|
||||
// xDen
|
||||
[
|
||||
'0xd35771193d94918a9ca34ccbb7b640dd86cd409542f8487d9fe6b745781eb49b',
|
||||
'0xedadc6f64383dc1df7c4b2d51b54225406d36b641f5e41bbc52a56612a8c6d14',
|
||||
'0x0000000000000000000000000000000000000000000000000000000000000001', // LAST 1
|
||||
],
|
||||
// yNum
|
||||
[
|
||||
'0x4bda12f684bda12f684bda12f684bda12f684bda12f684bda12f684b8e38e23c',
|
||||
'0xc75e0c32d5cb7c0fa9d0a54b12a0a6d5647ab046d686da6fdffc90fc201d71a3',
|
||||
'0x29a6194691f91a73715209ef6512e576722830a201be2018a765e85a9ecee931',
|
||||
'0x2f684bda12f684bda12f684bda12f684bda12f684bda12f684bda12f38e38d84',
|
||||
],
|
||||
// yDen
|
||||
[
|
||||
'0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffff93b',
|
||||
'0x7a06534bb8bdb49fd5e9e6632722c2989467c1bfc8e8d978dfb425d2685c2573',
|
||||
'0x6484aa716545ca2cf3a70c3fa8fe337e0a3d21162f0d6299a7bf8192bfd2a76f',
|
||||
'0x0000000000000000000000000000000000000000000000000000000000000001', // LAST 1
|
||||
],
|
||||
].map((i) => i.map((j) => BigInt(j))) as [bigint[], bigint[], bigint[], bigint[]]
|
||||
))();
|
||||
const mapSWU = /* @__PURE__ */ (() =>
|
||||
mapToCurveSimpleSWU(Fp, {
|
||||
A: BigInt('0x3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533'),
|
||||
B: BigInt('1771'),
|
||||
Z: Fp.create(BigInt('-11')),
|
||||
}))();
|
||||
const htf = /* @__PURE__ */ (() =>
|
||||
createHasher(
|
||||
secp256k1.ProjectivePoint,
|
||||
(scalars: bigint[]) => {
|
||||
const { x, y } = mapSWU(Fp.create(scalars[0]));
|
||||
return isoMap(x, y);
|
||||
},
|
||||
{
|
||||
DST: 'secp256k1_XMD:SHA-256_SSWU_RO_',
|
||||
encodeDST: 'secp256k1_XMD:SHA-256_SSWU_NU_',
|
||||
p: Fp.ORDER,
|
||||
m: 1,
|
||||
k: 128,
|
||||
expand: 'xmd',
|
||||
hash: sha256,
|
||||
}
|
||||
))();
|
||||
export const hashToCurve = /* @__PURE__ */ (() => htf.hashToCurve)();
|
||||
export const encodeToCurve = /* @__PURE__ */ (() => htf.encodeToCurve)();
|
1262
src/shortw.ts
1262
src/shortw.ts
File diff suppressed because it is too large
Load Diff
69
src/utils.ts
69
src/utils.ts
@ -1,69 +0,0 @@
|
||||
/*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
// Convert between types
|
||||
// ---------------------
|
||||
|
||||
const hexes = Array.from({ length: 256 }, (v, i) => i.toString(16).padStart(2, '0'));
|
||||
export function bytesToHex(uint8a: Uint8Array): string {
|
||||
if (!(uint8a instanceof Uint8Array)) throw new Error('Expected Uint8Array');
|
||||
// pre-caching improves the speed 6x
|
||||
let hex = '';
|
||||
for (let i = 0; i < uint8a.length; i++) {
|
||||
hex += hexes[uint8a[i]];
|
||||
}
|
||||
return hex;
|
||||
}
|
||||
|
||||
export function numberToHexUnpadded(num: number | bigint): string {
|
||||
const hex = num.toString(16);
|
||||
return hex.length & 1 ? `0${hex}` : hex;
|
||||
}
|
||||
|
||||
export function hexToNumber(hex: string): bigint {
|
||||
if (typeof hex !== 'string') {
|
||||
throw new TypeError('hexToNumber: expected string, got ' + typeof hex);
|
||||
}
|
||||
// Big Endian
|
||||
return BigInt(`0x${hex}`);
|
||||
}
|
||||
|
||||
// Caching slows it down 2-3x
|
||||
export function hexToBytes(hex: string): Uint8Array {
|
||||
if (typeof hex !== 'string') {
|
||||
throw new TypeError('hexToBytes: expected string, got ' + typeof hex);
|
||||
}
|
||||
if (hex.length % 2) throw new Error('hexToBytes: received invalid unpadded hex ' + hex.length);
|
||||
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;
|
||||
}
|
||||
|
||||
// Big Endian
|
||||
export function bytesToNumber(bytes: Uint8Array): bigint {
|
||||
return hexToNumber(bytesToHex(bytes));
|
||||
}
|
||||
|
||||
export function ensureBytes(hex: string | Uint8Array): 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) : hexToBytes(hex);
|
||||
}
|
||||
|
||||
// Copies several Uint8Arrays into one.
|
||||
export function concatBytes(...arrays: Uint8Array[]): Uint8Array {
|
||||
if (!arrays.every((b) => b instanceof Uint8Array)) throw new Error('Uint8Array list expected');
|
||||
if (arrays.length === 1) return arrays[0];
|
||||
const length = arrays.reduce((a, arr) => a + arr.length, 0);
|
||||
const result = new Uint8Array(length);
|
||||
for (let i = 0, pad = 0; i < arrays.length; i++) {
|
||||
const arr = arrays[i];
|
||||
result.set(arr, pad);
|
||||
pad += arr.length;
|
||||
}
|
||||
return result;
|
||||
}
|
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];
|
||||
}
|
813
test/basic.test.js
Normal file
813
test/basic.test.js
Normal file
@ -0,0 +1,813 @@
|
||||
import { deepStrictEqual, throws } from 'assert';
|
||||
import { should, describe } from 'micro-should';
|
||||
import * as fc from 'fast-check';
|
||||
import * as mod from '../esm/abstract/modular.js';
|
||||
import { bytesToHex, isBytes, bytesToHex as toHex } from '../esm/abstract/utils.js';
|
||||
// Generic tests for all curves in package
|
||||
import { secp192r1, secp224r1 } from './_more-curves.helpers.js';
|
||||
import { secp256r1 } from '../esm/p256.js';
|
||||
import { secp384r1 } from '../esm/p384.js';
|
||||
import { secp521r1 } from '../esm/p521.js';
|
||||
import { secp256k1 } from '../esm/secp256k1.js';
|
||||
import { ed25519, ed25519ctx, ed25519ph, x25519 } from '../esm/ed25519.js';
|
||||
import { ed448, ed448ph } from '../esm/ed448.js';
|
||||
import { pallas, vesta } from '../esm/pasta.js';
|
||||
import { bn254 } from '../esm/bn254.js';
|
||||
import { jubjub } from '../esm/jubjub.js';
|
||||
import { bls12_381 } from '../esm/bls12-381.js';
|
||||
import { default as wyche_curves } from './wycheproof/ec_prime_order_curves_test.json' assert { type: 'json' };
|
||||
import { createCurve } from '../esm/_shortw_utils.js';
|
||||
import { Field } from '../esm/abstract/modular.js';
|
||||
import { sha256 } from '@noble/hashes/sha256';
|
||||
|
||||
// Fields tests
|
||||
const FIELDS = {
|
||||
secp192r1: { Fp: [secp192r1.CURVE.Fp] },
|
||||
secp224r1: { Fp: [secp224r1.CURVE.Fp] },
|
||||
secp256r1: { Fp: [secp256r1.CURVE.Fp] },
|
||||
secp521r1: { Fp: [secp521r1.CURVE.Fp] },
|
||||
secp256k1: { Fp: [secp256k1.CURVE.Fp] },
|
||||
jubjub: { Fp: [jubjub.CURVE.Fp] },
|
||||
ed25519: { Fp: [ed25519.CURVE.Fp] },
|
||||
ed448: { Fp: [ed448.CURVE.Fp] },
|
||||
bn254: { Fp: [bn254.CURVE.Fp] },
|
||||
pallas: { Fp: [pallas.CURVE.Fp] },
|
||||
vesta: { Fp: [vesta.CURVE.Fp] },
|
||||
bls12: {
|
||||
Fp: [bls12_381.fields.Fp],
|
||||
Fp2: [
|
||||
bls12_381.fields.Fp2,
|
||||
fc.array(fc.bigInt(1n, bls12_381.fields.Fp.ORDER - 1n), {
|
||||
minLength: 2,
|
||||
maxLength: 2,
|
||||
}),
|
||||
(Fp2, num) => Fp2.fromBigTuple([num[0], num[1]]),
|
||||
],
|
||||
// Fp6: [bls12_381.fields.Fp6],
|
||||
Fp12: [
|
||||
bls12_381.fields.Fp12,
|
||||
fc.array(fc.bigInt(1n, bls12_381.fields.Fp.ORDER - 1n), {
|
||||
minLength: 12,
|
||||
maxLength: 12,
|
||||
}),
|
||||
(Fp12, num) => Fp12.fromBigTwelve(num),
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
for (const c in FIELDS) {
|
||||
const curve = FIELDS[c];
|
||||
for (const f in curve) {
|
||||
const Fp = curve[f][0];
|
||||
const name = `${c}/${f}:`;
|
||||
const FC_BIGINT = curve[f][1] ? curve[f][1] : fc.bigInt(1n, Fp.ORDER - 1n);
|
||||
|
||||
const create = curve[f][2] ? curve[f][2].bind(null, Fp) : (num) => Fp.create(num);
|
||||
describe(name, () => {
|
||||
should('equality', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (num) => {
|
||||
const a = create(num);
|
||||
const b = create(num);
|
||||
deepStrictEqual(Fp.eql(a, b), true);
|
||||
deepStrictEqual(Fp.eql(b, a), true);
|
||||
})
|
||||
);
|
||||
});
|
||||
should('non-equality', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => {
|
||||
const a = create(num1);
|
||||
const b = create(num2);
|
||||
deepStrictEqual(Fp.eql(a, b), num1 === num2);
|
||||
deepStrictEqual(Fp.eql(b, a), num1 === num2);
|
||||
})
|
||||
);
|
||||
});
|
||||
should('add/subtract/commutativity', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => {
|
||||
const a = create(num1);
|
||||
const b = create(num2);
|
||||
deepStrictEqual(Fp.add(a, b), Fp.add(b, a));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('add/subtract/associativity', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, FC_BIGINT, (num1, num2, num3) => {
|
||||
const a = create(num1);
|
||||
const b = create(num2);
|
||||
const c = create(num3);
|
||||
deepStrictEqual(Fp.add(a, Fp.add(b, c)), Fp.add(Fp.add(a, b), c));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('add/subtract/x+0=x', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (num) => {
|
||||
const a = create(num);
|
||||
deepStrictEqual(Fp.add(a, Fp.ZERO), a);
|
||||
})
|
||||
);
|
||||
});
|
||||
should('add/subtract/x-0=x', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (num) => {
|
||||
const a = create(num);
|
||||
deepStrictEqual(Fp.sub(a, Fp.ZERO), a);
|
||||
deepStrictEqual(Fp.sub(a, a), Fp.ZERO);
|
||||
})
|
||||
);
|
||||
});
|
||||
should('add/subtract/negate equality', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (num1) => {
|
||||
const a = create(num1);
|
||||
const b = create(num1);
|
||||
deepStrictEqual(Fp.sub(Fp.ZERO, a), Fp.neg(a));
|
||||
deepStrictEqual(Fp.sub(a, b), Fp.add(a, Fp.neg(b)));
|
||||
deepStrictEqual(Fp.sub(a, b), Fp.add(a, Fp.mul(b, Fp.create(-1n))));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('add/subtract/negate', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (num) => {
|
||||
const a = create(num);
|
||||
deepStrictEqual(Fp.neg(a), Fp.sub(Fp.ZERO, a));
|
||||
deepStrictEqual(Fp.neg(a), Fp.mul(a, Fp.create(-1n)));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('negate(0)', () => {
|
||||
deepStrictEqual(Fp.neg(Fp.ZERO), Fp.ZERO);
|
||||
});
|
||||
|
||||
should('multiply/commutativity', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => {
|
||||
const a = create(num1);
|
||||
const b = create(num2);
|
||||
deepStrictEqual(Fp.mul(a, b), Fp.mul(b, a));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('multiply/associativity', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, FC_BIGINT, (num1, num2, num3) => {
|
||||
const a = create(num1);
|
||||
const b = create(num2);
|
||||
const c = create(num3);
|
||||
deepStrictEqual(Fp.mul(a, Fp.mul(b, c)), Fp.mul(Fp.mul(a, b), c));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('multiply/distributivity', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, FC_BIGINT, (num1, num2, num3) => {
|
||||
const a = create(num1);
|
||||
const b = create(num2);
|
||||
const c = create(num3);
|
||||
deepStrictEqual(Fp.mul(a, Fp.add(b, c)), Fp.add(Fp.mul(b, a), Fp.mul(c, a)));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('multiply/add equality', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (num) => {
|
||||
const a = create(num);
|
||||
deepStrictEqual(Fp.mul(a, 0n), Fp.ZERO);
|
||||
deepStrictEqual(Fp.mul(a, Fp.ZERO), Fp.ZERO);
|
||||
deepStrictEqual(Fp.mul(a, 1n), a);
|
||||
deepStrictEqual(Fp.mul(a, Fp.ONE), a);
|
||||
deepStrictEqual(Fp.mul(a, 2n), Fp.add(a, a));
|
||||
deepStrictEqual(Fp.mul(a, 3n), Fp.add(Fp.add(a, a), a));
|
||||
deepStrictEqual(Fp.mul(a, 4n), Fp.add(Fp.add(Fp.add(a, a), a), a));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('multiply/square equality', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (num) => {
|
||||
const a = create(num);
|
||||
deepStrictEqual(Fp.sqr(a), Fp.mul(a, a));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('multiply/pow equality', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (num) => {
|
||||
const a = create(num);
|
||||
deepStrictEqual(Fp.pow(a, 0n), Fp.ONE);
|
||||
deepStrictEqual(Fp.pow(a, 1n), a);
|
||||
deepStrictEqual(Fp.pow(a, 2n), Fp.mul(a, a));
|
||||
deepStrictEqual(Fp.pow(a, 3n), Fp.mul(Fp.mul(a, a), a));
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
should('square(0)', () => {
|
||||
deepStrictEqual(Fp.sqr(Fp.ZERO), Fp.ZERO);
|
||||
deepStrictEqual(Fp.mul(Fp.ZERO, Fp.ZERO), Fp.ZERO);
|
||||
});
|
||||
|
||||
should('square(1)', () => {
|
||||
deepStrictEqual(Fp.sqr(Fp.ONE), Fp.ONE);
|
||||
deepStrictEqual(Fp.mul(Fp.ONE, Fp.ONE), Fp.ONE);
|
||||
});
|
||||
|
||||
should('square(-1)', () => {
|
||||
const minus1 = Fp.neg(Fp.ONE);
|
||||
deepStrictEqual(Fp.sqr(minus1), Fp.ONE);
|
||||
deepStrictEqual(Fp.mul(minus1, minus1), Fp.ONE);
|
||||
});
|
||||
|
||||
const isSquare = mod.FpIsSquare(Fp);
|
||||
// Not implemented
|
||||
if (Fp !== bls12_381.fields.Fp12) {
|
||||
should('multiply/sqrt', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (num) => {
|
||||
const a = create(num);
|
||||
let root;
|
||||
try {
|
||||
root = Fp.sqrt(a);
|
||||
} catch (e) {
|
||||
deepStrictEqual(isSquare(a), false);
|
||||
return;
|
||||
}
|
||||
deepStrictEqual(isSquare(a), true);
|
||||
deepStrictEqual(Fp.eql(Fp.sqr(root), a), true, 'sqrt(a)^2 == a');
|
||||
deepStrictEqual(Fp.eql(Fp.sqr(Fp.neg(root)), a), true, '(-sqrt(a))^2 == a');
|
||||
// Returns odd/even element
|
||||
deepStrictEqual(Fp.isOdd(mod.FpSqrtOdd(Fp, a)), true);
|
||||
deepStrictEqual(Fp.isOdd(mod.FpSqrtEven(Fp, a)), false);
|
||||
deepStrictEqual(Fp.eql(Fp.sqr(mod.FpSqrtOdd(Fp, a)), a), true);
|
||||
deepStrictEqual(Fp.eql(Fp.sqr(mod.FpSqrtEven(Fp, a)), a), true);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
should('sqrt(0)', () => {
|
||||
deepStrictEqual(Fp.sqrt(Fp.ZERO), Fp.ZERO);
|
||||
const sqrt1 = Fp.sqrt(Fp.ONE);
|
||||
deepStrictEqual(
|
||||
Fp.eql(sqrt1, Fp.ONE) || Fp.eql(sqrt1, Fp.neg(Fp.ONE)),
|
||||
true,
|
||||
'sqrt(1) = 1 or -1'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
should('div/division by one equality', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (num) => {
|
||||
const a = create(num);
|
||||
if (Fp.eql(a, Fp.ZERO)) return; // No division by zero
|
||||
deepStrictEqual(Fp.div(a, Fp.ONE), a);
|
||||
deepStrictEqual(Fp.div(a, a), Fp.ONE);
|
||||
// FpDiv tests
|
||||
deepStrictEqual(mod.FpDiv(Fp, a, Fp.ONE), a);
|
||||
deepStrictEqual(mod.FpDiv(Fp, a, a), Fp.ONE);
|
||||
})
|
||||
);
|
||||
});
|
||||
should('zero division equality', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (num) => {
|
||||
const a = create(num);
|
||||
deepStrictEqual(Fp.div(Fp.ZERO, a), Fp.ZERO);
|
||||
deepStrictEqual(mod.FpDiv(Fp, Fp.ZERO, a), Fp.ZERO);
|
||||
})
|
||||
);
|
||||
});
|
||||
should('div/division distributivity', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, FC_BIGINT, (num1, num2, num3) => {
|
||||
const a = create(num1);
|
||||
const b = create(num2);
|
||||
const c = create(num3);
|
||||
deepStrictEqual(Fp.div(Fp.add(a, b), c), Fp.add(Fp.div(a, c), Fp.div(b, c)));
|
||||
deepStrictEqual(
|
||||
mod.FpDiv(Fp, Fp.add(a, b), c),
|
||||
Fp.add(mod.FpDiv(Fp, a, c), mod.FpDiv(Fp, b, c))
|
||||
);
|
||||
})
|
||||
);
|
||||
});
|
||||
should('div/division and multiplication equality', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => {
|
||||
const a = create(num1);
|
||||
const b = create(num2);
|
||||
deepStrictEqual(Fp.div(a, b), Fp.mul(a, Fp.inv(b)));
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Group tests
|
||||
// prettier-ignore
|
||||
const CURVES = {
|
||||
secp192r1, secp224r1, secp256r1, secp384r1, secp521r1,
|
||||
secp256k1,
|
||||
ed25519, ed25519ctx, ed25519ph,
|
||||
ed448, ed448ph,
|
||||
pallas, vesta,
|
||||
bn254,
|
||||
jubjub,
|
||||
};
|
||||
|
||||
const NUM_RUNS = 5;
|
||||
|
||||
const getXY = (p) => ({ x: p.x, y: p.y });
|
||||
|
||||
function equal(a, b, comment) {
|
||||
deepStrictEqual(a.equals(b), true, `eq(${comment})`);
|
||||
if (a.toAffine && b.toAffine) {
|
||||
deepStrictEqual(getXY(a.toAffine()), getXY(b.toAffine()), `eqToAffine(${comment})`);
|
||||
} else if (!a.toAffine && !b.toAffine) {
|
||||
// Already affine
|
||||
deepStrictEqual(getXY(a), getXY(b), `eqAffine(${comment})`);
|
||||
} else throw new Error('Different point types');
|
||||
}
|
||||
|
||||
for (const name in CURVES) {
|
||||
const C = CURVES[name];
|
||||
const CURVE_ORDER = C.CURVE.n;
|
||||
const FC_BIGINT = fc.bigInt(1n + 1n, CURVE_ORDER - 1n);
|
||||
|
||||
// Check that curve doesn't accept points from other curves
|
||||
const O = name === 'secp256k1' ? secp256r1 : secp256k1;
|
||||
const POINTS = {};
|
||||
const OTHER_POINTS = {};
|
||||
for (const name of ['Point', 'ProjectivePoint', 'ExtendedPoint', 'ProjectivePoint']) {
|
||||
POINTS[name] = C[name];
|
||||
OTHER_POINTS[name] = O[name];
|
||||
}
|
||||
|
||||
for (const pointName in POINTS) {
|
||||
const p = POINTS[pointName];
|
||||
const o = OTHER_POINTS[pointName];
|
||||
if (!p) continue;
|
||||
|
||||
const G = [p.ZERO, p.BASE];
|
||||
for (let i = 2n; i < 10n; i++) G.push(G[1].multiply(i));
|
||||
const title = `${name}/${pointName}`;
|
||||
describe(title, () => {
|
||||
describe('basic group laws', () => {
|
||||
// Here we check basic group laws, to verify that points works as group
|
||||
should('zero', () => {
|
||||
equal(G[0].double(), G[0], '(0*G).double() = 0');
|
||||
equal(G[0].add(G[0]), G[0], '0*G + 0*G = 0');
|
||||
equal(G[0].subtract(G[0]), G[0], '0*G - 0*G = 0');
|
||||
equal(G[0].negate(), G[0], '-0 = 0');
|
||||
for (let i = 0; i < G.length; i++) {
|
||||
const p = G[i];
|
||||
equal(p, p.add(G[0]), `${i}*G + 0 = ${i}*G`);
|
||||
equal(G[0].multiply(BigInt(i + 1)), G[0], `${i + 1}*0 = 0`);
|
||||
}
|
||||
});
|
||||
should('one', () => {
|
||||
equal(G[1].double(), G[2], '(1*G).double() = 2*G');
|
||||
equal(G[1].subtract(G[1]), G[0], '1*G - 1*G = 0');
|
||||
equal(G[1].add(G[1]), G[2], '1*G + 1*G = 2*G');
|
||||
});
|
||||
should('sanity tests', () => {
|
||||
equal(G[2].double(), G[4], '(2*G).double() = 4*G');
|
||||
equal(G[2].add(G[2]), G[4], '2*G + 2*G = 4*G');
|
||||
equal(G[7].add(G[3].negate()), G[4], '7*G - 3*G = 4*G');
|
||||
});
|
||||
should('add commutativity', () => {
|
||||
equal(G[4].add(G[3]), G[3].add(G[4]), '4*G + 3*G = 3*G + 4*G');
|
||||
equal(G[4].add(G[3]), G[3].add(G[2]).add(G[2]), '4*G + 3*G = 3*G + 2*G + 2*G');
|
||||
});
|
||||
should('double', () => {
|
||||
equal(G[3].double(), G[6], '(3*G).double() = 6*G');
|
||||
});
|
||||
should('multiply', () => {
|
||||
equal(G[2].multiply(3n), G[6], '(2*G).multiply(3) = 6*G');
|
||||
});
|
||||
should('add same-point', () => {
|
||||
equal(G[3].add(G[3]), G[6], '3*G + 3*G = 6*G');
|
||||
});
|
||||
should('add same-point negative', () => {
|
||||
equal(G[3].add(G[3].negate()), G[0], '3*G + (- 3*G) = 0*G');
|
||||
equal(G[3].subtract(G[3]), G[0], '3*G - 3*G = 0*G');
|
||||
});
|
||||
should('mul by curve order', () => {
|
||||
equal(G[1].multiply(CURVE_ORDER - 1n).add(G[1]), G[0], '(N-1)*G + G = 0');
|
||||
equal(G[1].multiply(CURVE_ORDER - 1n).add(G[2]), G[1], '(N-1)*G + 2*G = 1*G');
|
||||
equal(G[1].multiply(CURVE_ORDER - 2n).add(G[2]), G[0], '(N-2)*G + 2*G = 0');
|
||||
const half = CURVE_ORDER / 2n;
|
||||
const carry = CURVE_ORDER % 2n === 1n ? G[1] : G[0];
|
||||
equal(G[1].multiply(half).double().add(carry), G[0], '((N/2) * G).double() = 0');
|
||||
});
|
||||
should('inversion', () => {
|
||||
const a = 1234n;
|
||||
const b = 5678n;
|
||||
const c = a * b;
|
||||
equal(G[1].multiply(a).multiply(b), G[1].multiply(c), 'a*b*G = c*G');
|
||||
const inv = mod.invert(b, CURVE_ORDER);
|
||||
equal(G[1].multiply(c).multiply(inv), G[1].multiply(a), 'c*G * (1/b)*G = a*G');
|
||||
});
|
||||
should('multiply, rand', () =>
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, (a, b) => {
|
||||
const c = mod.mod(a + b, CURVE_ORDER);
|
||||
if (c === CURVE_ORDER || c < 1n) return;
|
||||
const pA = G[1].multiply(a);
|
||||
const pB = G[1].multiply(b);
|
||||
const pC = G[1].multiply(c);
|
||||
equal(pA.add(pB), pB.add(pA), 'pA + pB = pB + pA');
|
||||
equal(pA.add(pB), pC, 'pA + pB = pC');
|
||||
}),
|
||||
{ numRuns: NUM_RUNS }
|
||||
)
|
||||
);
|
||||
should('multiply2, rand', () =>
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, (a, b) => {
|
||||
const c = mod.mod(a * b, CURVE_ORDER);
|
||||
const pA = G[1].multiply(a);
|
||||
const pB = G[1].multiply(b);
|
||||
equal(pA.multiply(b), pB.multiply(a), 'b*pA = a*pB');
|
||||
equal(pA.multiply(b), G[1].multiply(c), 'b*pA = c*G');
|
||||
}),
|
||||
{ numRuns: NUM_RUNS }
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
for (const op of ['add', 'subtract']) {
|
||||
describe(op, () => {
|
||||
should('type check', () => {
|
||||
throws(() => G[1][op](0), '0');
|
||||
throws(() => G[1][op](0n), '0n');
|
||||
G[1][op](G[2]);
|
||||
throws(() => G[1][op](CURVE_ORDER), 'CURVE_ORDER');
|
||||
throws(() => G[1][op](-123n), '-123n');
|
||||
throws(() => G[1][op](123), '123');
|
||||
throws(() => G[1][op](123.456), '123.456');
|
||||
throws(() => G[1][op](true), 'true');
|
||||
throws(() => G[1][op](false), 'false');
|
||||
throws(() => G[1][op](null), 'null');
|
||||
throws(() => G[1][op](undefined), 'undefined');
|
||||
throws(() => G[1][op]('1'), "'1'");
|
||||
throws(() => G[1][op]({ x: 1n, y: 1n }), '{ x: 1n, y: 1n }');
|
||||
throws(() => G[1][op]({ x: 1n, y: 1n, z: 1n }), '{ x: 1n, y: 1n, z: 1n }');
|
||||
throws(
|
||||
() => G[1][op]({ x: 1n, y: 1n, z: 1n, t: 1n }),
|
||||
'{ x: 1n, y: 1n, z: 1n, t: 1n }'
|
||||
);
|
||||
throws(() => G[1][op](new Uint8Array([])), 'ui8a([])');
|
||||
throws(() => G[1][op](new Uint8Array([0])), 'ui8a([0])');
|
||||
throws(() => G[1][op](new Uint8Array([1])), 'ui8a([1])');
|
||||
throws(() => G[1][op](new Uint8Array(4096).fill(1)), 'ui8a(4096*[1])');
|
||||
// if (G[1].toAffine) throws(() => G[1][op](C.Point.BASE), `Point ${op} ${pointName}`);
|
||||
throws(() => G[1][op](o.BASE), `${op}/other curve point`);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
should('equals type check', () => {
|
||||
throws(() => G[1].equals(0), '0');
|
||||
throws(() => G[1].equals(0n), '0n');
|
||||
deepStrictEqual(G[1].equals(G[2]), false, '1*G != 2*G');
|
||||
deepStrictEqual(G[1].equals(G[1]), true, '1*G == 1*G');
|
||||
deepStrictEqual(G[2].equals(G[2]), true, '2*G == 2*G');
|
||||
throws(() => G[1].equals(CURVE_ORDER), 'CURVE_ORDER');
|
||||
throws(() => G[1].equals(123.456), '123.456');
|
||||
throws(() => G[1].equals(true), 'true');
|
||||
throws(() => G[1].equals('1'), "'1'");
|
||||
throws(() => G[1].equals({ x: 1n, y: 1n, z: 1n, t: 1n }), '{ x: 1n, y: 1n, z: 1n, t: 1n }');
|
||||
throws(() => G[1].equals(new Uint8Array([])), 'ui8a([])');
|
||||
throws(() => G[1].equals(new Uint8Array([0])), 'ui8a([0])');
|
||||
throws(() => G[1].equals(new Uint8Array([1])), 'ui8a([1])');
|
||||
throws(() => G[1].equals(new Uint8Array(4096).fill(1)), 'ui8a(4096*[1])');
|
||||
// if (G[1].toAffine) throws(() => G[1].equals(C.Point.BASE), 'Point.equals(${pointName})');
|
||||
throws(() => G[1].equals(o.BASE), 'other curve point');
|
||||
});
|
||||
|
||||
for (const op of ['multiply', 'multiplyUnsafe']) {
|
||||
if (!p.BASE[op]) continue;
|
||||
describe(op, () => {
|
||||
should('type check', () => {
|
||||
if (op !== 'multiplyUnsafe') {
|
||||
throws(() => G[1][op](0), '0');
|
||||
throws(() => G[1][op](0n), '0n');
|
||||
}
|
||||
G[1][op](1n);
|
||||
G[1][op](CURVE_ORDER - 1n);
|
||||
throws(() => G[1][op](G[2]), 'G[2]');
|
||||
throws(() => G[1][op](CURVE_ORDER), 'CURVE_ORDER');
|
||||
throws(() => G[1][op](CURVE_ORDER + 1n), 'CURVE_ORDER+1');
|
||||
throws(() => G[1][op](123.456), '123.456');
|
||||
throws(() => G[1][op](true), 'true');
|
||||
throws(() => G[1][op]('1'), '1');
|
||||
throws(() => G[1][op](new Uint8Array([])), 'ui8a([])');
|
||||
throws(() => G[1][op](new Uint8Array([0])), 'ui8a([0])');
|
||||
throws(() => G[1][op](new Uint8Array([1])), 'ui8a([1])');
|
||||
throws(() => G[1][op](new Uint8Array(4096).fill(1)), 'ui8a(4096*[1])');
|
||||
throws(() => G[1][op](o.BASE), 'other curve point');
|
||||
});
|
||||
});
|
||||
}
|
||||
// Complex point (Extended/Jacobian/Projective?)
|
||||
// if (p.BASE.toAffine && C.Point) {
|
||||
// should('toAffine()', () => {
|
||||
// equal(p.ZERO.toAffine(), C.Point.ZERO, '0 = 0');
|
||||
// equal(p.BASE.toAffine(), C.Point.BASE, '1 = 1');
|
||||
// });
|
||||
// }
|
||||
// if (p.fromAffine && C.Point) {
|
||||
// should('fromAffine()', () => {
|
||||
// equal(p.ZERO, p.fromAffine(C.Point.ZERO), '0 = 0');
|
||||
// equal(p.BASE, p.fromAffine(C.Point.BASE), '1 = 1');
|
||||
// });
|
||||
// }
|
||||
// toHex/fromHex (if available)
|
||||
if (p.fromHex && p.BASE.toHex) {
|
||||
should('fromHex(toHex()) roundtrip', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (x) => {
|
||||
const point = p.BASE.multiply(x);
|
||||
const hex = point.toHex();
|
||||
const bytes = point.toRawBytes();
|
||||
deepStrictEqual(p.fromHex(hex).toHex(), hex);
|
||||
deepStrictEqual(p.fromHex(bytes).toHex(), hex);
|
||||
})
|
||||
);
|
||||
});
|
||||
should('fromHex(toHex(compressed=true)) roundtrip', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (x) => {
|
||||
const point = p.BASE.multiply(x);
|
||||
const hex = point.toHex(true);
|
||||
const bytes = point.toRawBytes(true);
|
||||
deepStrictEqual(p.fromHex(hex).toHex(true), hex);
|
||||
deepStrictEqual(p.fromHex(bytes).toHex(true), hex);
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
describe(name, () => {
|
||||
if (['bn254', 'pallas', 'vesta'].includes(name)) return;
|
||||
// Generic complex things (getPublicKey/sign/verify/getSharedSecret)
|
||||
should('.getPublicKey() type check', () => {
|
||||
throws(() => C.getPublicKey(0), '0');
|
||||
throws(() => C.getPublicKey(0n), '0n');
|
||||
throws(() => C.getPublicKey(-123n), '-123n');
|
||||
throws(() => C.getPublicKey(123), '123');
|
||||
throws(() => C.getPublicKey(123.456), '123.456');
|
||||
throws(() => C.getPublicKey(true), 'true');
|
||||
throws(() => C.getPublicKey(false), 'false');
|
||||
throws(() => C.getPublicKey(null), 'null');
|
||||
throws(() => C.getPublicKey(undefined), 'undefined');
|
||||
throws(() => C.getPublicKey(''), "''");
|
||||
// NOTE: passes because of disabled hex padding checks for starknet, maybe enable?
|
||||
// throws(() => C.getPublicKey('1'), "'1'");
|
||||
throws(() => C.getPublicKey('key'), "'key'");
|
||||
throws(() => C.getPublicKey({}));
|
||||
throws(() => C.getPublicKey(new Uint8Array([])));
|
||||
throws(() => C.getPublicKey(new Uint8Array([0])));
|
||||
throws(() => C.getPublicKey(new Uint8Array([1])));
|
||||
throws(() => C.getPublicKey(new Uint8Array(4096).fill(1)));
|
||||
throws(() => C.getPublicKey(Array(32).fill(1)));
|
||||
});
|
||||
should('.verify() should verify random signatures', () =>
|
||||
fc.assert(
|
||||
fc.property(fc.hexaString({ minLength: 64, maxLength: 64 }), (msg) => {
|
||||
const priv = C.utils.randomPrivateKey();
|
||||
const pub = C.getPublicKey(priv);
|
||||
const sig = C.sign(msg, priv);
|
||||
deepStrictEqual(
|
||||
C.verify(sig, msg, pub),
|
||||
true,
|
||||
`priv=${toHex(priv)},pub=${toHex(pub)},msg=${msg}`
|
||||
);
|
||||
}),
|
||||
{ numRuns: NUM_RUNS }
|
||||
)
|
||||
);
|
||||
should('.verify() should verify random signatures in hex', () =>
|
||||
fc.assert(
|
||||
fc.property(fc.hexaString({ minLength: 64, maxLength: 64 }), (msg) => {
|
||||
const priv = toHex(C.utils.randomPrivateKey());
|
||||
const pub = toHex(C.getPublicKey(priv));
|
||||
const sig = C.sign(msg, priv);
|
||||
let sighex = isBytes(sig) ? toHex(sig) : sig.toCompactHex();
|
||||
deepStrictEqual(C.verify(sighex, msg, pub), true, `priv=${priv},pub=${pub},msg=${msg}`);
|
||||
}),
|
||||
{ numRuns: NUM_RUNS }
|
||||
)
|
||||
);
|
||||
should('.verify() should verify empty signatures', () => {
|
||||
const msg = new Uint8Array([]);
|
||||
const priv = C.utils.randomPrivateKey();
|
||||
const pub = C.getPublicKey(priv);
|
||||
const sig = C.sign(msg, priv);
|
||||
deepStrictEqual(
|
||||
C.verify(sig, msg, pub),
|
||||
true,
|
||||
'priv=${toHex(priv)},pub=${toHex(pub)},msg=${msg}'
|
||||
);
|
||||
});
|
||||
should('.sign() edge cases', () => {
|
||||
throws(() => C.sign());
|
||||
throws(() => C.sign(''));
|
||||
throws(() => C.sign('', ''));
|
||||
throws(() => C.sign(new Uint8Array(), new Uint8Array()));
|
||||
});
|
||||
|
||||
describe('verify()', () => {
|
||||
const msg = '01'.repeat(32);
|
||||
should('true for proper signatures', () => {
|
||||
const priv = C.utils.randomPrivateKey();
|
||||
const sig = C.sign(msg, priv);
|
||||
const pub = C.getPublicKey(priv);
|
||||
deepStrictEqual(C.verify(sig, msg, pub), true);
|
||||
});
|
||||
should('false for wrong messages', () => {
|
||||
const priv = C.utils.randomPrivateKey();
|
||||
const sig = C.sign(msg, priv);
|
||||
const pub = C.getPublicKey(priv);
|
||||
deepStrictEqual(C.verify(sig, '11'.repeat(32), pub), false);
|
||||
});
|
||||
should('false for wrong keys', () => {
|
||||
const priv = C.utils.randomPrivateKey();
|
||||
const sig = C.sign(msg, priv);
|
||||
deepStrictEqual(C.verify(sig, msg, C.getPublicKey(C.utils.randomPrivateKey())), false);
|
||||
});
|
||||
});
|
||||
if (C.Signature) {
|
||||
should('Signature serialization roundtrip', () =>
|
||||
fc.assert(
|
||||
fc.property(fc.hexaString({ minLength: 64, maxLength: 64 }), (msg) => {
|
||||
const priv = C.utils.randomPrivateKey();
|
||||
const sig = C.sign(msg, priv);
|
||||
const sigRS = (sig) => ({ s: sig.s, r: sig.r });
|
||||
// Compact
|
||||
deepStrictEqual(sigRS(C.Signature.fromCompact(sig.toCompactHex())), sigRS(sig));
|
||||
deepStrictEqual(sigRS(C.Signature.fromCompact(sig.toCompactRawBytes())), sigRS(sig));
|
||||
// DER
|
||||
deepStrictEqual(sigRS(C.Signature.fromDER(sig.toDERHex())), sigRS(sig));
|
||||
deepStrictEqual(sigRS(C.Signature.fromDER(sig.toDERRawBytes())), sigRS(sig));
|
||||
}),
|
||||
{ numRuns: NUM_RUNS }
|
||||
)
|
||||
);
|
||||
should('Signature.addRecoveryBit/Signature.recoveryPublicKey', () =>
|
||||
fc.assert(
|
||||
fc.property(fc.hexaString({ minLength: 64, maxLength: 64 }), (msg) => {
|
||||
const priv = C.utils.randomPrivateKey();
|
||||
const pub = C.getPublicKey(priv);
|
||||
const sig = C.sign(msg, priv);
|
||||
deepStrictEqual(sig.recoverPublicKey(msg).toRawBytes(), pub);
|
||||
const sig2 = C.Signature.fromCompact(sig.toCompactHex());
|
||||
throws(() => sig2.recoverPublicKey(msg));
|
||||
const sig3 = sig2.addRecoveryBit(sig.recovery);
|
||||
deepStrictEqual(sig3.recoverPublicKey(msg).toRawBytes(), pub);
|
||||
}),
|
||||
{ numRuns: NUM_RUNS }
|
||||
)
|
||||
);
|
||||
should('Signature.normalizeS', () =>
|
||||
fc.assert(
|
||||
fc.property(fc.hexaString({ minLength: 64, maxLength: 64 }), (msg) => {
|
||||
const priv = C.utils.randomPrivateKey();
|
||||
const pub = C.getPublicKey(priv);
|
||||
const sig = C.sign(msg, priv);
|
||||
const sig2 = sig.normalizeS();
|
||||
deepStrictEqual(sig2.hasHighS(), false);
|
||||
}),
|
||||
{ numRuns: NUM_RUNS }
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// NOTE: fails for ed, because of empty message. Since we convert it to scalar,
|
||||
// need to check what other implementations do. Empty message != new Uint8Array([0]), but what scalar should be in that case?
|
||||
// should('should not verify signature with wrong message', () => {
|
||||
// fc.assert(
|
||||
// fc.property(
|
||||
// fc.array(fc.integer({ min: 0x00, max: 0xff })),
|
||||
// fc.array(fc.integer({ min: 0x00, max: 0xff })),
|
||||
// (bytes, wrongBytes) => {
|
||||
// const privKey = C.utils.randomPrivateKey();
|
||||
// const message = new Uint8Array(bytes);
|
||||
// const wrongMessage = new Uint8Array(wrongBytes);
|
||||
// const publicKey = C.getPublicKey(privKey);
|
||||
// const signature = C.sign(message, privKey);
|
||||
// deepStrictEqual(
|
||||
// C.verify(signature, wrongMessage, publicKey),
|
||||
// bytes.toString() === wrongBytes.toString()
|
||||
// );
|
||||
// }
|
||||
// ),
|
||||
// { numRuns: NUM_RUNS }
|
||||
// );
|
||||
// });
|
||||
|
||||
if (C.getSharedSecret) {
|
||||
should('getSharedSecret() should be commutative', () => {
|
||||
for (let i = 0; i < NUM_RUNS; i++) {
|
||||
const asec = C.utils.randomPrivateKey();
|
||||
const apub = C.getPublicKey(asec);
|
||||
const bsec = C.utils.randomPrivateKey();
|
||||
const bpub = C.getPublicKey(bsec);
|
||||
try {
|
||||
deepStrictEqual(C.getSharedSecret(asec, bpub), C.getSharedSecret(bsec, apub));
|
||||
} catch (error) {
|
||||
console.error('not commutative', { asec, apub, bsec, bpub });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
should('secp224k1 sqrt bug', () => {
|
||||
const { Fp } = secp224r1.CURVE;
|
||||
const sqrtMinus1 = Fp.sqrt(-1n);
|
||||
// Verified against sage
|
||||
deepStrictEqual(
|
||||
sqrtMinus1,
|
||||
23621584063597419797792593680131996961517196803742576047493035507225n
|
||||
);
|
||||
deepStrictEqual(
|
||||
Fp.neg(sqrtMinus1),
|
||||
3338362603553219996874421406887633712040719456283732096017030791656n
|
||||
);
|
||||
deepStrictEqual(Fp.sqr(sqrtMinus1), Fp.create(-1n));
|
||||
});
|
||||
|
||||
should('bigInt private keys', () => {
|
||||
// Doesn't support bigints anymore
|
||||
throws(() => ed25519.sign('', 123n));
|
||||
throws(() => ed25519.getPublicKey(123n));
|
||||
throws(() => x25519.getPublicKey(123n));
|
||||
// Weierstrass still supports
|
||||
secp256k1.getPublicKey(123n);
|
||||
secp256k1.sign('', 123n);
|
||||
});
|
||||
|
||||
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.
|
||||
import url from 'url';
|
||||
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
|
||||
should.run();
|
||||
}
|
1489
test/bls12-381.test.js
Normal file
1489
test/bls12-381.test.js
Normal file
File diff suppressed because it is too large
Load Diff
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
|
559
test/bls12-381/bls12-381-g2-test-vectors.txt
Normal file
559
test/bls12-381/bls12-381-g2-test-vectors.txt
Normal file
@ -0,0 +1,559 @@
|
||||
db29a6e1db5d6dcb485f26c174d11dd0ab1ecc54125e31dde2949b03e925ef23::8e49a02dc374bb1702ec4c8aa514c6d72e0884d3611334ae749462eb545a4d0a6086ef0a6b1bbc5db2934d297bf766c60dbe665dfc33d3aa765d071712075b4102a71f518e16376fc58c982d30a6196fdd8319090bcb2728c9bbbd8045fc7863
|
||||
28b90deaf189015d3a325908c5e0e4bf00f84f7e639b056ff82d7e70b6eede4c:09:8647aa9680cd0cdf065b94e818ff2bb948cc97838bcee987b9bc1b76d0a0a6e0d85db4e9d75aaedfc79d4ea2733a21ae0579014de7636dd2943d45b87c82b1c66a289006b0b9767921bb8edd3f6c5c5dec0d54cd65f61513113c50cc977849e5
|
||||
177c50ec35b1da25f68b9f7bad8f1c443a01fa2b20de37be0caec8b62db5c902:d2:8166ef48e7c1de5f8256a670511aad114f956382eed36f3bb45a7a1bf473e982f0e399924b4dc92795d043d9475402aa0d065e10f05a2026a0961882b6c2e6c6ae429edd17c7a43586a814121044e41c5c1d3397452b2fc61fdc523cc943ef68
|
||||
e99d0f7a4f8a9e3f74a6bd9677b3fd5be32f7cea4e5b898ed3dea735fa647632:0d98:a70b42352845ca47713a20a243ec2aeb63bab7c4126d9cbd390b6f59372de383b93698c66f7e75452cbc569147b1f9f70be3b03539a4d110c65082f7918942b962543494469a5271208cdec9c234689bcae9c88c7cdc3ef9eefdf11a522794c6
|
||||
2c864f383cd664839ce6d7d049f8083870b628d7bb8a4539cc84a35bbd723736:4a35:88c76ed5872fb011f4fc0fc8ff2751513dd6f22a9b7cb125ed39bdfffdd996ba1b5d9ea6332e8b4c34e591cb9dcf4efe112a096754db0d723269bb1a758f986d5e0cfbdcc22f8678f72f1804459c98be59ee7632c666206255dcaa816188cf2b
|
||||
cd8ccf5c229e37a4a4b5490c84882af890cdc4c70170ad5f0ee2f3a86ffa3080:05caf3:83b4087858c3585a2f978fe1c79b94d9eb927500066e6c793392f1d5be2eff1ebfdd893555f6a89934f4e27ec810489a07f0740124c065fb82246a6d6f924f70069edfaf04fc4d1021f3314b9d09dfadc344fd2dcafaeb09586b194b3ea73374
|
||||
43a5b55a3823acebca9e55face42697b5cd0e2398a44a56ebc22d936a05c3893:94b0b9:8b4f817e0815a9ca1fb91a5fd9e88714d72c2d6b4ea037ab43cde4149691109336edca72fb55936b5ba8f4576ffd49780dde24d48f9a9d5a07d1527ac3a9bc38c05a0fc58bac710cd4983c6cd21e56175a6e597954ac68207db1a8b86efe84cf
|
||||
e2654a38c0148b5f0de1b8266737fdb6706fedc436d5fb70dc34bebe25b43d01:06a63bfb:b4a224ac879b67d20f452c40adac0b993560842e4dce928ace30751bc38d2a3a46e1f84dd1c04c0417ebaf069f5618f210c0dce7ae478f83a664f231e141b3fc677842bae20df43409476dde8c6a81cd1067999e6fae2896afe9630bc7441d3a
|
||||
736057053101fbb9240808bfe83121382a8cd559a97f8633ae170b3e1ac9efee:0d83b078:b028a3c5877113519d0da87fd67edebcb80b37869f58505997c459fad745194407e004503fb49f4d9e0ea289d5cd9c63151c2d40e09cd3988cf7b0191718d4f4d07fa149c965fdc4a25fece97efd72fb9337099b5f47979c6acb9b30f0c2fbe3
|
||||
dc7f613ec82e641c58d8390963ccb4cd307da37417aca9eec15b060fccdf73f3:0611983835:b4d7a115e96b4da4616b54bff471d5a9f7002100b61214337662f0aabf08a45ecdcdb581325cba3b5141a4083c301dcc10eeac2da4cd430ab1882e44cec5f8f08a789eb670653ab8072795e3d5724405bb6ffebde2e73229b353b17119f46e9c
|
||||
ba4eeeefe05a4515509d64f21639c24dde05122815a9d89f1d8d31ba90f7e7aa:64726e3da8:8d6d9d1ee5febfb549e1f7a2f3e69f01918fc105ea710bae881b60fec8dcdbbb2ae3e00249d5b82eb4c598f557c139e00bcc5dd098afc74303a54937a81e98b4d2e75ee792021200f291918980ea274ef8ea2d712e265089230042348dd4b872
|
||||
5737b8adab24e7ec10fdd0a420c603392cb2c43f6466d6033eb14d1012470dbb:07d6e7a31a2f:99d2bc134b268397759e77b7f59c26862264454a659b90664cd7ff9fd5701b083ef6aee3df959643816958515059e42205abf0c959ea64910c3bdbb447a1afca4caf271f7dbd50085d886c2e3f5294167ea3631767acfee5d89cb6e04def2dd8
|
||||
ecca05ab2c52c1b4861357c01d628a8a7b0ca0b6c72219cf47822e72858752bc:aa07552a7358:9023090506aa3ec0b14b0650d302baa54976e26104d011f3e131a066f3f7dacbd8831e4bcefe5702e4c83323e4e6f7700b343a29e7d4cb7277712c59e83fe6ee788967c4721f783b81024c8317b4042e0f4fad3c41fd861b6050654bc529af70
|
||||
b54f380ef6485006e52c279335e9aeb7adcf1dd24b02f6be0a25b6d0a70627b0:04583025f1b7da:897da4929a4ed71b70ba65e68f39f6f7967dbecbb97b610bd3ab23f6a08906ca44661523212a598f9a1e12e128c184440d1a56c2b64260c871e25ae5135b53944f7820aa74d20d48d90976cf60090427a442fa61e20f45ec6d0f348f6684fd32
|
||||
7865bb94878bac2960a60393588ce3c0e92d6d8eda6c4ac2da5fd4d2fbffa48f:9398f8937e1821:b8d60310a2c5cc322ed412f340c3fc05723c446c47cc529f7e914916a5cb7d78e8af8400a6d07d62bc280daca12988cb1073a88ea7b4aa2dff821ae011342aa8a8de691ddd370a8ba942fd5cc6892acec84db889f6421bb1d71f02f5b7830a0a
|
||||
97ec105f9f2487fa4b360eb484c4398add4c1fe8e274853e530292290e9b7a59:00dd6efc58fcd4ee:86c7fd60798ddc58d59de487a1ac4221e61efe70c188e78ac50b4d89ea022b592e472895595aa4725c9be82770cc05050fb0feacd6e1a3c062dfed9e6e63ae58838025cae35dcd8b1eaa90a3714e800b2cd0740d7262119e7abec3b5822d4497
|
||||
8e9b0e1280b0433182df7eca995a7279e704f728a88ad0b26518ed0a19f62113:1b3d4c68e007f478:ab1832ae67d695f941decf959ebbce93bb33e70d60dfb53321f582f81be7d4177d0aef680c56a587e905c051355a78ad14574e0adcb083e2d6c972dbc54b68290abfc8c7abb9472ee210078eb62a3148764defb967768ab5be36d36c5aaf6455
|
||||
281c1297034c8aaa2ea6368dd038833429673b99dd4da4622908de8d9674f99d:033d69a90f6073fceb:8bd05333f0d076558b8a25a75b76b14af7454f69452918d363a905a49089663e23c0ede69b4882790178a02ff83b8a3d0c945e6ce0e18c5c3abfb22c32e2bee71145fd319f4daef46463471df9691a372a8f30427c427fb025155109269ef363
|
||||
6fbbae45973a06226abf29b6d978429cc2775b4af6c736f6fa1eecb042c3b089:b28bfd61695d25590c:90f9298f281b350add0e8b1789e99d38a42021ea971d1a0b1a28f8d11906602bd0a22eeee4b149443541f149721a926b0ddd2a496a88ac47a9f59e0a80aa91f88baa129035d2f2d38028ab024ac38ec0ae1768c1bcd156c2349a26e01b94a516
|
||||
91a60d4b2440622e10f5ba7dc9585894b2125980fd5072cec4bbcd47ea49c57a:00581bba4bc7f26227a7:a95a631b6ea74646d9659f0d27a1e8c877e4018d47f81a32d0d0b203a724b980597880a84cdbe88cf41be05a58101e7a11b6de7b9bf7433e6c947e571b595b4496fff37f8a48d3eccab904ee824b31dd984c3f6c2a19acb52e895f1e326e788e
|
||||
b211045d27b3c191a7961f521dedbb6ac8f6a362ab19ec28969d583c9a2eb128:e02c048d0609137492b9:a1e3e50c7a70c9d1028996c5bc79343c5780b16310a8b930704d3107e93bc92f7b927393fcf55d64461369f95aff557d0f346db8d83d02f0d914b50b706908cb631114c279be622bbc801bf5bd93245827a3c7ac06a30fa88e579ba2c5adbf99
|
||||
342fb513e70e2a7cdee072a9d8c1cfc86aa9691687dbef91fca2d15870e4adaa:011ddb2e7d45df2e05fb8a:96324a72624c99ae74fc334cb8fe70885d7e9c840e4608e5b19a8a5ef8fe68277e7cac7c4b6889e206f6244dc49fc19a0a3ac2992e51d0f0076a528fa6c11afb4a3f127286714c1548a3c4c9eac7e67eb32bcd2c480e2c7d36504a5fa52d6ea1
|
||||
498f8139fa03ac326e5ff321a9c8083d22eb048de74fcec128de3621a1dd5675:64024e14b436cb94e3209c:8723e4effda2455dd5d552bcf6116f88dd70187b031edb7660fa673ce22bd0fbe9ccaf28be6d9c78b783fa6310a0fda00b29cbce1adcef4204d31694bb47cc84de102d2dfab833e907887bcc8371119244b5b69b791c9dcd667e899971a37295
|
||||
e5bcc31a88b1d23772bb5a282b8d8f7c0dd645b45b940027c1111ee13e62cfdf:00ca1d50caa232ab9ad64644:ab1e90fecee91a4c12537041281e91190e8bba02d503c491da48ab7b2fc9af11b01b168c83154c04f59c3e6d815665d6120d2547f7fba57d43ad9993c99e54c0b5488166c41416e29e9588d6b7e6d67c7d93c9be7ba3c7d769e0ccedec71b3a4
|
||||
e5bfacb0667cd3db0634fd1eaa35b362d827a205db6cdce6daecbd83413ac6cf:3f1e6ffc1d7ec3e890d52615:ac3dcdb2751d982d80b77a749cf2f1f698f4dee8c9007043766c602104cbb0f633a4ce437098775062bf64aff77b2d66071dbc770e6ea04b11809f24ef50e6ec3ae5a58d58896bf9e78de80194eb67ce0d44e0232014ec300b63f90ef5c335bb
|
||||
055873520343a11f937aa23abfdfee0d1929c461bd2fcfc85d8d07ef44230c69:04f00076b77983d6ee7ab6979d:afd0d6908f62ce579dde390b463d535dcfd1f5ee6664d46a583952c50178a61f15cf072153a21b4e6044dece53a755b1025170d84046c57252ea6c0a9c45ca42b0c77544e80856634e552c1e2b86eabf1f92535e1b27ea43281f8dc3a8672211
|
||||
d58dd7364d214cd9ca9a5188ff8ad95486f65bf642d857c902fc6fd32a843510:49c83ddfa5b465abca1a8b5c78:a087f6bb963265cb95ff4914d4bae832a4fc89ff0616bcd8a529badca39a2c85b0ff3e1c418ec7979bcaf93a6d3dfc35108c420956c0795bdd81d2ce6f53a2f0a0bc37876db80bd0b59ce2210404a5c84d16330621aaaaad718e518dff1ef3cb
|
||||
13ed1173e146edb9ae6a77e289155354d6e0107aa2fcd4dc89441619553448a3:07c50e920e060f06243084d32afe:a89faa2c99e76386d80f709e1af822e830385d07b0cc4bf8b46315273f0b11c9f77845e431d5bbb86d07e4b0e15c66bb1958e8d0728a0a0961e5f233fcba72278f5b0ffbbd67fbf62aa7b7a1572d9dababed755a60d37906dc30bdfc3d9d4c2e
|
||||
5c3f7aef904cd9aa5cf093f993f49d46c3f20b7450552d52b23569239849f31b:6f455d61ee48d1990ec61dd70dbd:aaede5d58e5c71c170b2fbff350cb596bf43f2d0ee6b0e83f8863350ea4fd756129150e856c83e45734db8649a3617150c4a7a37ec596d89ff6cc6749dbe6d54f41cb36662279b3a82335e3f4b946afd94740e3214e93a45643bbabdac93a25c
|
||||
00cc17223e74b58d2e465bad2f5d0ecc5a85081d143fc4b8ab8eb10b44c6c45f:0800350f624966388e327b17cbb9fa:8333c0c7d9da5f79c67ed5fd13d92bbc91c5680837cd0cf816f2876106720a93323477ba28dbce0f554ffc0f37db9fc20386aa12d005be7a2ce5a660ef8ff46c7381bd0446cabcc4a3f4b8b75b9608615ab2497beb425415c778c278f38a6d81
|
||||
21fa64295c0fc47d81f69ba31d4661604c4d9eae6d2077c094ff758b1757e3c1::845c89e3bc5350275cc5b2669bda8ae127b8bf731517bb1bd5ebf0670add6df037f6b8c382aa03f729c5e8049e817dab0cc310e8af80d78cff447f034d32e7e7a1e8f27c8df390c5b640a7e341d6ab94311c4ae35a754f3990371c9186f97f0e
|
||||
dd7466232c0fc5581e319721d4e0c7b773417432b0a0b756c3dc4e5cdff4723b:06:95f8a6c56e2de4742144e2213e13411f1a2d03ca783e509353276292fc97e087062b3050e967593e569e6ef80741adae0c5a7ab5bf0b0f0e7cd92d5e17181176ec2fe111e8306b9643901e228f0187bc9d429ca8cbd23a39be4bbb99bf29d84b
|
||||
fe7eab6ef7c43c8ced6e9b7524667c79f129eda2e8585328fde3b18b4eedddab:57:ae257f78c59769a67de12fab413c4ec6a6668eedf45c4bf6d5928f05acfa77cb12caf36fa9e42830519dbf816655fbd012472d84bab2d4f8c99d684f9652675493a4436258a881f0e9e27558f5125f614e38835a87ee0ee61dd7a946a3b13996
|
||||
93cc9ed7f1507a817608255cd036d6b1d10a974e7547d7100be15f70702e3e4b:0b76:8792277cf0d7d0318174e23356cd311a737169b08f52c65fc8ce7af4fc3cbc5ad75223d15f43a2caa3862c6b4ab39c140f2571804296c90f27f5ebdec3f135fc458cb1a441c4dcf4953273cc77947d604ceca8c10dda4f68874716f2684cd52f
|
||||
9f388e9e5e98b2cdbb28cfa3011be14cd658d0395d7c737ccc50679dce316d88:0c5f:959299104d5905f91d081e1ddeeb1ef4dec0013d3f961daa4e38db5367ad81acb657021dd540d96920188f1f1ad4ba39163fc9575652c3cf3bde0bd7533a505a7282777fbbae405e0f967806b4e903b9134ccd5fb5d090ea66ff26c69a1f5aff
|
||||
ff060b6cfcee3546fcaa93c4867bf97e56099949a3eadb4c4011ca544000c1b6:0f5459:8b694a9f0b96aef6766ec5d4f471ada3810d30fbd84fd12aec8a9b579568b27a4b21794e7c545f3f9d86603a51fed940007bd3fae9bc793b47e1c5ba2d18e7b39f75d52b0722dc60d537a9c437b28d766cc39cc7a2c0747fc4af09d4adb208aa
|
||||
8d750998b2143613209a3284da5d9bb2d63c5b7504f0ded55cba58d99902ba68:d626a5:8ab12e4ebbebbc60760f61eed87a65fc221abb342fe3909483db1dffe132ab2cd208568955821e7167f29bd2fed824f309904e2e77577ca6cb8a59e16077cd2847985a083838af48b0820336cea6011c6a7dbdea166fc3a733a8941ab53bf3f3
|
||||
4f35aaddd4587dcde04341ecfdb62636fb140bd363738c9dd8f53b45fe740d09:07d29e93:a20dc2b369dcf1f609e24f795ded8d606bd5931a2f3e8e045840eae489da759f4e00a036ac2eb678c61fab587dccb55f164241799be62a63958df72533fdac653fe23983ece98119991c3a7e7910d9f837ffb3b0579c326fb46a5c3b55527a09
|
||||
a81a2a4d890e39dc2ca7720ed6c301b1ee0addfc6a609d3282cfba541ac9639a:6f3b5edc:8c32cc120080a1b17b342e64bfecd79b45fcbe71a27cd1037135eb8b97326f971b89d84c03dddfe01249e5ac3472487915d7055b6b4f06177dfc5e66c88c2bd7c8929153c335e0afd0f230a97cbeeda238df6a31825649f46bfed3985bdd6180
|
||||
275d76e250f6f44585d23c95f9984ffe762afb7bec52906657ad4ffdbf264e7d:050c5aff03:85ca53a9b9ac376b09c3538f40ea50cd0ab4b16b205fd434befc6aece17e807204ef8333a63522a1df41b4c8f38a3d3c086565227513ffa50e31d3bcc9870f693d9527d10e9c26386dafd48cd8bca9e39363ec220e1196edd636fd2248ad113a
|
||||
7e363965c6d3a02c07f5b33b37617dc2b2843bec249968a7958ad55fa64fc0be:ea1a2c2541:ac3dd4ca0e6b6310217f163d5c5dbd771228febc96aeeef31bfb605431d1a23e37297bcdf5f684a2936980213497d561081c38a01c273dc0f53a1b50bf561e9fc2c7f5e0d55ce7537ce995addb6b20caa30e3840952a5f14aff90731c0d7612f
|
||||
697ef015b81f5a3c31ba3ba4482b1182f2a106aba7bd94948a073861297e9c4b:0caf34f67e39:a5aa61367ef1f50257c1614863c9a41f493e01487e522bd83fffa8c2d7eac5656ab355db421e4a3e9c6a325e8fa8ae4e05875a490b3baec6529799c8f65efd7526aa7e9fd33ebb1e7506ed36012b35ffbabddaa63748cbed227bebc2738eb647
|
||||
986e36b32e2a4cb454eb84c8e135b1568488d57ad35d5c2e8793572bdf5643ae:bb586523bbe7:af3d68b8da23dd29ee307bec34839ba5117477178ed2d752339dee1e5dd93199bd4c05680400cbf6b8553db1bb3e0f280aabc043eefad31d7c575947ae78fc4dc672374e1a8cc5cc04ffc63d0b1a53f4fe8b39cdc3c111085cea5f8456121842
|
||||
f0b6be124d8f563ef3411d87a7c42796a5675d4da1e4875d25b523aa772d48ca:00377779a4f4b7:85088daf12e34adf4f2f108c12b9c20a938df6923d3a1b94feb2e38a1eae6dbdcaf48e464936583ecfa12c45c116ba79001aa41ff2aeb628659cc0f70db863a9820b81594ac4f6051d64b5bcf9c6aa78fc5c81382884783718571d08680f5209
|
||||
daddd3b1096023b2435a20a435aa827b80c8978f827dd1d845078b5c197c769e:8c7f2a39715ef4:a61be5df8d598c6ff2785fbb9bef821a688b7b79b429a1897d4ac3db02705572694305a423bf2621a866cf7a985f810011201783f6154c4108b85234e2ae9b1a21cd4df0f48cef1d93a539fbcdef3fa001dac9be7643f37dd1bb6cf5e67e02fe
|
||||
930bc625ef72fd47e2a7d60801aefbbd98f1ad56353deae4b2e4830833f8c77b:0d290ab1a4b5fdc3:8552042e6132000eb3c44674e8e4e71c0f097bfb13cd367662a150b07fb7b670e166f738a7819377411b5eb5dde0ea950dcfd102b3718f84b071f14007560e5679ebc006fe76432e15fab2d18535f39b3fbf00bf7ce9572e5cbe5694c743ae71
|
||||
41dabe002f7aa9792a9f04527048159e6f1be97c7e0207ed1566c4262a12f789:968e85352fc3df11:8962a7d1541633a888526a8d3d010280a7c6c8c268074ef489019a8651ed0099c1c2ff60a99146b74a47f4c432d5d04a08c05fa9be0e048cdc283da719dd0fa1c7032c8353ad6846f81dce05f92ea083f75f1fd09ea67462bc8c8d46943654ac
|
||||
c4b3162757f21d8b57f9d701d416b5bf90a709f55557d19a8fe72c8bd4d01fd6:0c041934df4821a898:b88b710f7809f3901df4e877f03153f77fd1c41ce44abb0d4ff1b546ebaed40b5a80a4e383655fb188c38eedf2ab962c0e1b2dcf7bdcc781b21f1b8cfe0dd846269207a8a051b53c262ffe1c14d1d3a3e07d471780b638804219402e844e3513
|
||||
b786a05a2204a23bf247986914e09e60220ad345dfbb0082e31290b80f348dd6:29f5877b9dfe13722b:b1535e3fb429a8012516b23ce79664707a0a99a824ffee7e226090b0c41bf9005f2c72bca703ca27694adf585e9c7082024b8be701b5f9541f3e6e3f353facd33b9d0532a120fe029f4fdad84ccf1bc92e5a01995237036304bc167aad541dd7
|
||||
7a7a26440695378207f8d039d198c0a8bae7d174297a69bc566a9453e76fb2fe:0d7d8481e00dffd22bf7:a3be6cff0f954ce3c20feccd23808bb650e2b9618f3d9c52fb173521ab89b2654af7f2ec103e0c6be4336571ac203a2c0b0189887c7b06cb5738d10fbd84865dd24ae555ba3b3f46adaff5d3e99e8b417440f5d377408e05ac69e4b2fd690b84
|
||||
8789e7f56b38b17acfe2532a8516a43817a2dcc2e5037718171fa61c32f9b294:f495212d34a5343641b9:99bf5bd4cf4fa381db42f5bf214424709c5c1b282bbf63e75ed1d5420eb5c2f1eb5ddabcdf6cbaebad66b04f7293a0280f13a53175e02e5f6801be278fd2639e211aec2b268cf95d1b0300df63a9eb051c1f05d15f6ec1a8c5588b3682c69ebc
|
||||
944922d8040d748b4c77a9779b68e0e19816845e5969715377c88c760fbaa3b5:0a2ff5abcb19784fd8b111:885761f8d921cf5a6cecd94c26619157d2f548458ac36f5287dc205fc2cf091592cfa48d1e06544bd9bb04fc938917ec04d7df0752ebb314f0bbaa29cbe12d84bd9528cadc71d077497bfb04e44b64d2e08ec7630490950329425ff6f7a87eb9
|
||||
27d3382462061f32036e9f25746697298c03ac135eb53cce40e6c8d0b61a38e5:5c3c39865260362265879f:8e234853e19f022389dc7dd7240798c45f81c7be9e650088d380f3498743f30c961e856bf9579c21f280061cd087609b15f91f02936e082bee165690c5e2a792d336bbab9c6bc5f27256889953744b81d685356cd6f23827aed5e8a9a23236f8
|
||||
1e13a5be7f36189f5ffcf35c210de27b6571d64aa3d00111fa0831f48c4fa165:08ed73f10fd0e2fa0ebe1932:b1aa7f5fa8af643ebcd8410b580fb93dbd2ba82d64bf9a802846606b39d3a4fcd651f80736a9f189694a6910b41ab6ef0fc2b90bd374410feb42496002e435f6c11b18ebe8897d8e64630a1290398bd11d36bf83e398db9932bb7e0e94cd54cb
|
||||
9f2048b10d757490f72ca10e0ef4b380b57ce15afe83470e6109faca7b847921:ce013f91232820a5a3c4e275:98c52bbe5b1da89a930c4452e5b01f4359945d3e96ebb655d15fb8763a2d747d52f2628abb72459a302dd6a017ae0317031e99fffb2539ad6432ab1a09c51d2e70c0ce431e5fb53431ab963d4d23687718847455b53e389bd6faa919855c716f
|
||||
6ce4f45b393a54d6e12064738e740c870fc3066655827af9f071dadc7554a45f:0cb23a6f4693b24a16a0e5d11f:8a5e73d0e38f3e8786fdc7e2fb313b9a6b34ea7658e1eeb63c4270ac1ed80c42f22716283843ea29f72c599b1b69edd002903b75545342fd3f94c5633216cdf4e7a39d3aa146119f5c6fd555c359068ce15a3c344e18bc631d1f9ed94e266e2c
|
||||
83db8a3fd3891ee06a9a4fca5a694d76f78813fcc7fe0a1b19f1b86fb6524b7e:fa1183015e443acc22d5df8131:81cec4d2a64afb634e02b3b4d3986e1c6a360dfefef370224afffa15be78370f775ff0e617ad62fc21624275b84c3ba00f203e3b60b4de91b51a80750fa1aa609b99f49ab9fd638caa423bca2c0d2c9da90988e189953af2085d0a763c06f7e3
|
||||
80327a9a08e410b0749a2bf141579799e8cb9dcafc3c91245f8e8c98653b873c:0acb153156dbb95b071e3c79602e:80764c588d161cc35775e811b17484d1ec138ca72fb7be6d382ba0b0674e684e6dce4fd04c25db12520d3fd3d5e944d50f6bf07a63bbef2ade761de3c0b210b7cc9d2e95906d889df312ec062e67c703b56f5e572e1f808c1b57ee983579638a
|
||||
7d0de3987f99eedbb2ac0536ed6ddecff5600ebeecce340a9d5000005d8122dc:3353cacdf3a5d86bf7610061a6fb:920ccad897aec4302997390e7150ea9082782428be08ae4d0124ecc776aa458bc913a47192b3a02055527018e87f9c6417ed3d9cc17e813ba24265eb4dbbaa4746c62adb1e2d3c9a7a4d8c2dac4c6a14851358c6eb8a4e19e785dcc5b533d752
|
||||
abc26c442a73f260a1f7e41644a9904a58d47a50ff545fa5a5bc3b81dcc0e312:09fa75a09eeab060869233747305f5:83c41f6246d829bf19be3accf362e65bf102316e4882f57df3b8abc06bd9e0dd8080ff807326c10443383bda8760c252193cbce2f8ecbdb5b278413ce8fccb20e25bcb036454b7621bf630ae988b1652eb9b686a428a2d7ceefd9e18322e5a12
|
||||
330d6be474e54d85deec0aff3afa587eb4c5f803a94077d9ee86bd74a034a40d::a8492b918727dfe480e954c52126666872e243489ec6ef42d669c4483c3d09c83d5a49b8092685aa3410d1a1fab33fb6004c07e312df7faa3e88a53b414dd653fe4f417ef9450c7220a02ae99936a0a18b264ed1cfade17ffffe4f6c0b334dbb
|
||||
fdf519fe3b983dbd660f1dcc7c53b9b4003c54b0d4815cd5242c4b61bcaf9b58:02:9396ffec586fa35dc2a6659729a012f450bc83468900e3de53295af31cc8c6378c2ef7bd4a02e4417bbbb7c5aec566370aab2bb3681c1ba192716c90a1026042e648ab3dd6437273aabbd7285b77a2d2e2b0dd8dbc260c347a90e005b7dfd412
|
||||
8eb2ff351ded50e79a4bede161069ac6025e6e8cd0f081dd7af5228136c24e9e:d2:b56374f5b8a57d0f81013307a603785d54b6c2ed49f2f838901f3130a9569c8e0195a3490554c87f6620168ba1768bd20860894ba866c71b5769a6087d63ca3638b75ebae6c444fc5c52c317b5bba76ab020f55f3d673dc3aa6367889ea02461
|
||||
9a5eae3b2fc45fed7631c3c65e4d6668dce6f1eebc40ca19ae585ed1fd226ed2:0239:8b90f69f6e3db9277e4a6addd63903f306a6257752895925fda34253980aa0315cf221a67fcbbdd5ab1ce372ef5978bb0541117dc8fc497d6b4bfb6bcbff532971f8adae492c4c037fd5bc00d145787f9361ac005069873fb0cf102ad8d69660
|
||||
d20f1f3e536ed11959af2da39840eddadcb6dc50dd473c805989328c032b60ed:67c8:ad40bb4249dbf738e6fd8eec7e74c8a4516766e46b12c116030c4da28c41c2a224106195069bbb6364f28a8f8b6973bd03ee5a73e8066d6d27ebfcd88c47d9d24416f2209843700c91938e1c3a5d33e5245c7f9280a6474b4397d0af01a15f89
|
||||
d8ea32d81ddcbbe90a2f3df961c723da168001bda98d68820a7d3cb42e325ef4:0c9be6:8f956ef6e12de0b6ba15be54c8523ea25e6c9b94ec91326b9d9bda6a529c239e22888bd01f8b5f6ed13427345de3dcd80c9919783447a3ce70d261070cc0a1ee1bf3b3265ca25eeb39e9c54c32408f21a96121be5c91f4a36fc3946eabc80640
|
||||
461fbcff221f0655c19f9c5e1c2a5437c8842ba0910a983be007cff9df19f627:92e6e8:857d69d8830474de73df377034c57e619c954041b80b6c23ae769ea5dae40a5d42d8f855248d4f78c8703970435042110b256d1de0c1061c05e5a544de326035ece92e7bebc665f1575d5d8f0183303a8a7e1478a6600db96d47eeac05ef5645
|
||||
69a75aca260d679aa24f49481164930b71e72fdb5db887cf484a6db6852a72a4:0428b032:91f8b2609fd551c13eda568f103f5be9f7b2a2b42e2cf263da628c6cb735ad04d436b878fc7363baf969f415550d02800f562e606085ad0962be2adbe8c617b36a418c31d81bb10577f0e3743be8f39142e7fdf5d3637a120dced8b73ff79262
|
||||
182733a6720089b476c298a084a30a2a4a717b2a16ecb3b7be894b222c3ca5e1:26a7d91d:a39166d0499752ff885611e994ee3d39f8a9a6d734327ff43045535cfc882e3c4c99d62bc75da26474981013fa5214d6068dae1b90637e8ea5cad091d364badc6934288339c600059124d347a7a09b7a674435e1c7ba9d519d6b6d0b04051700
|
||||
75aae7b43b7315fe0ffca6f6255f62a815a83226cb16bfd12436f8599a59bd80:09abe5ed5f:ae30c591e798621380b1a5fb93b12e5a2b56bcb8937d3c78f67883a9e941dd301871ccfe57273a6ee6e5f0cdefcaa07b12318afb8d46c3ca85485c5f406e0501c02a58cd72ca766eecd7000fb222f12c95ee2c3d6e3a600bd205272dad25065e
|
||||
9c7b850171efc1bcc5d866ff6533fd7cba462b6f0523a223fb94f857f2ed5a16:42ead34e98:acec746ebb567eefe291695266d131cc81044899fb1fd2fdcc04757d8466e0e93bca13ad47364fbc0ac143c086a674af145ed5f793c50975db8ef9cce33cfdab065d6b9fc4c6442483f387f0af911933f789b00dba8f39f8d6d6fd3f04fab3f7
|
||||
53438f53eb749aba12412357262d5445e4cfb69ba645b30a9f116d6ec45a1994:0251be974654:80480c5941ae1fdfd3ffcb429a1b9928ffd08d8a759e5284dab6b5b73724b50bf76deae9d2dee2bc4250ba65feb9de620c2d7691e116476b81facfc873f8842a27c3de5f0730b480dabe0aaa4d24bca4df3564b71090e280191daba77c607089
|
||||
d7db36a4a43f7e91f27282937a4280a1eebdd67b18fce8b4ec5f581f14af66eb:aaa6cb92433d:8cd313fdc1ee2f5a15e1a66ccca3ed4236cdecb69951ef56cda437da8ff3f37288ef6a1e9756cc1dbd8718cdb060a8c8023ea48aae987d687486bc9c677073fc62526da457aca1b31fca39daade9978318ecc3ecd1273f883d064927795e3225
|
||||
e557e1443650a1ed64f17d9913dfe3f7ea8fa5b9b22b4175ac69d02faaa591d8:0020f39be6abdd:ae504f56ea36a8dd6c5792fe7ef70bd5bf61e45178319c85532bfd27bb775b213e1ed04a3c4ed087e95eb0880c1b11c715394287d1077ada5a3c0b7db4954d2145aad6b74ddd117c73f3b7850f900992e4b66590cac34e9f67b92eb032458a06
|
||||
566f89bee9480150bd9cd346e9fe7f681af96562ca82a98ab9be4cd319401a16:315a32bba44f89:aa8a8bedcb31ea6d67957fc7e09c336e561f63fd085c84c8bafafb610b961921fe1aca3c6b7c2a104fe84a72fd771dc20f51d9c28cfb7fbf842dbc9343d3cb4861f187c7ad02032f3a347dbff315233c3b2c12b8cae7dc40ce3bb3ae0cf28633
|
||||
ac8093326f925831b5c813c0f36749ab6f85f8e3b4a80920ca524cb1187ec8da:000268a1ab986936:a712a7352cfde24ae04b0187cf7c27d24cf993f6d415d01b8d4bc8d7f46d1773575038d0363dc5f1b6c61b119185f2cf13de4c13b42c5fbe5257588ba386d3ff2992d6f7f3e3c67972565d70ce7894bb9f7304969e2e9b7f7735f1ba9b2f100f
|
||||
e014a2800cbb1df2e42dc40bce9b81a84e6ec7f5ce1c1cb84f6de463607f58f1:e52a2f5a63181f64:aa83a49ad0dfab7c4683bf8363170b836b5c69952a20057167799e57c154de91440fc3da9a9a651fd0e815c583af9e970bd900743a2154145a4b5040f4a5e8b134d89412d9c2113dc9151e4754b2fae7c8a80ad3ecdca6df31b3be67ce8a0f6f
|
||||
6bb60100e45c2a7601a1075a70a8f804eaf016bdb2cfb67c76bea74c49c6a99f:07ce73a170e9e3b5d3:8c87485bbffeb5033466993d3924100696c90bbb6cb8a7cc25e92f1e72fc93dd65eced2046b35165278a41c75c261a310590bd11ecd4b79be049fa201c1387962912dff8d309a9d4ffb0a192bccd3fd51a43edb42447d86162e6c5e367a0ebb0
|
||||
ac514ecd85c4bc9c7bb5dd74d675a3d7e0ac5725ab8707743d69a4a4efd0d463:e479f849946dde9711:b8f4e70a28a300bda27525092ef417aabc44b0965a1b66e0fb9d0d85283d34d756ee28f5b8b0d4e2cba31a01ef1b97201603e8b11527dd3d59c04a1a49e6aec69caec7c825f32e8478ff685e7626aebf49972957da93c6e75328182d27e28c59
|
||||
0c5e59158036e95517480734dd946ccd840a2ae0d7bfd04afb561eb07b651f65:097fba98b1044aa38ecc:8b92528bb299945e6eaa1c1543982b4719ce6ffeaa46182b31f5fb12d04bc52d1edd242f26bbbd1d6bca7df14a6e713f07047747eb44048db73e17dd0cd86944525641e439686c0d926fa6aac347956b76448f6aed53cf5e150b26f62697eb0a
|
||||
482f5eec6aab3190b3e85c3358e906470e29f5028c1c1ff27334744a5389b046:f1903bd31ed169bd2ed5:b1bd64412e722ff2568737f965458f12923a38d94dbb9d08413a0122a993f159a4e69322bd142ffba038abf617809f4c183d1bc8397bb0d4e755854b06df29be5290b068bdaeb6a3bf66af947f9de650fc992b24795e109d66e1c1b8597c0e4d
|
||||
6cf0808dd7e4aafedc33bca4bdc5cd2ea5b199f09e60f818874d06edd808b711:053d7b09d32505e7f8a86a:915bbe14cd1bc66d46c572217d7834da1396304f1a00964717eb9fd078954db38ac2d3cfee23a2cadf2e239cbab2dd4004879ceaf6d2a0484284e8bb939e707f83166527df1b7f07f5b55e987e400183e051bcd02eaca19c8a825405f25a5fa0
|
||||
516a6fc917c2677b196e831913c67137d99bfbfcd279dab6c8189b8235777958:6244fe979e6507bf1affd7:96329a5e5c8c2f96941f8281b85f884fbf36732b9c20a497dbf2364ab5c885fdd2ea901a0b8142965643123b9959e6a0032fab81535cccec3f89f2f5743ab18ade1681b339dceb146a653356b676ba5044fd338a313edf9a1506c42a8eec2ec0
|
||||
db541ca7b63dd509c46a82844abab2e3fb63b41dc6f0aedd012dd5ee825ab336:0605a5861b234595538a364f:85f71fbbcb7e1bc833ac68121c3b255e92d54bce8c9f3daf4a51068f9f8ec830744c3fa2e0249a729724edab8d91370104d00bdb4e517bfde3c37ce445cc0a5382026ba0027f4645949b0a586dd12aeb8a4dc82047ad2e876faca9c5ed4b0f9c
|
||||
cfcdec94ca11b0a8964477d8f1cd2d991d9d5378ca7f03fb7838e85e013dac91:86d5eba501326894509e34a3:ad8ed6d864bcd3247b9939905d82d17623227754fdf0242de0abfbeab1f0560982e353238dfc43d654753908e13c0338073ad74b329a6058a89ecec84735b123cca657f9cf9d71d7cb2f8416a1599a275c848d6abea10123b27f3334aea2a94d
|
||||
64c8a98a1d399e67005062aa67492603ae501ecb1a954d61a30a4ebe4d161f2f:082fa093ef30ed114342696f1a:ae8d8bebf524ffb11e11ed6c29cdefab488749c11c2ee5cf3cbb02731d3b936ea11f4e41902d29cf977e483a21f398a20d904494d3ab7305015b4b8f6032df5240b29278689b72fb4a9496458879c00815fa3e29b3b5f3df78765e161ed3dc64
|
||||
2544f3764d9102fb80bb5fa446f4d9b9050e229f42e92cd36b1e17b8f91c850d:feb1f3d7e800a829cca384707f:afdee7bc926e4df643a69a431bfc91aadc659e912e83fc20a7d162ad675e37d07e95b04ea17ada1ddecda83799c7dddf0cc95db48879e6f694d7198f4d0637327df4d5d29b6542b71bfa773f66e6d518d8c700b58a677a089f82a1a838097ef4
|
||||
c8718d6afc9955bf189039c0e72adce0fa342f9830746a92092a7658835a0282:0a134902c0656d0b86458d182718:a2bd54f4fe5a95d32dbb9bc94fb4ad87d8a99d89057fbf571c7b1438023dea2d1894649fc3f68c221d99f01f9b50d5e714ae03b3fbf390ffce54b3a12041a664777a43f96351ee8988624599d07b2a9fd8b2542d136442a106e3dab3f04c369c
|
||||
6004e1aea40b85274c75b056608d372ce8f4fb1710e8f5a6ba6da20f9b559605:cb80b0a3621bb0528735ddb062a8:97d80bd51856d3efba40917d7463dcfd49411a00951b19f35eaf21b470fcbfbec7dcfabc4542146003ab4c691ce565670d49c7405f5cb3e40d4cd13808738c5f9c5da520ae8c8d664af17bdccc87872da5e314dc51455a7816e197dac9f3c37f
|
||||
aa30b992fb91f3e8d07f7cf58356d84b694c454086c662603f950df1280f4175:01beab7f3bf92b1f7effa24f8b5e9e:9233f8530094e2f779260f126eb3571f796dc41c9d9bbbb788d5a52f5e3ef903960cc187d4cea0f4ee00af8e64ef88e6029097775218ecb72270fe946c2bb7f9c9c3f00125f321afdaf90d01bc0452171f759409e4e435feb353a8753cd34c92
|
||||
06db1da0e8e44d06028401ff614557bc44d04330ebdeffacbef03278630f3f49::b82b0511b133a0723fcc1643c6ae16b16acb8328fa10782c3d8dd532bea9e6c3e5758bdfbcdaaf37771ad70989e107f309adabc1dd10359caabfc0471db16d5d9abd47e8e48c3a2ef07483989fc0babf6080e8a156d432b9bd2323d3b9beb152
|
||||
c00642364807d4df508cd504ca3787239e6c00ae8db91c98d21aef6de2fa7662:09:95f9f6599b49da7ed8024a42543593b695385b8d834b707afeb062d91736f94ea087d498ae64070e439c43841a2901a502b25cb6003751e637ba0a39bbf5814ea8140e4676414d1cc67d8f3319e0b8d0568c4c57e83e2482cd11bf5e919a6924
|
||||
4f9bd257d3cdd870d0774c460a7a819fcb05b328606c33d541843c4062631d1f:57:a5178ee9b4f18bab44f49dae208cc1c9a387b518d25fd0c9ab8c5545d3f8aa9802764dba201683b4fb36bb3f5f95a8150bfd63971e086acd76eb2ec7a941c454c8f32152dd0894ffed21265982f3da9f6f47c2aa76ee526fe252b5f921c59a95
|
||||
dd2422149b9bbeb9e915f6725f01c0c03dbbce8b031970e777e205cdd0b62a5d:05ca:b11972fa5d063b53503c809e26006d5c03d803527622d293c630f1337af957002a0644cda5a28659260929a4a76ee25c05cbfbeed58549955edd0d5a70f1a170f8484fccd69ca5241591bc1ecd9d636f324270d06ee7bf64b73e5a43efa4b327
|
||||
37114bc315ea4daed2d27c47dafe0b8c9a9280f3e9c5628ce6196d3e444b3a48:2e62:81c8811fe65a4528384d8f5a6f7840ac530d9d66a58888e8e550e73cf03b2d0695b3794dd8e2ccf7936407ee6bc3487d169a8f3c99718f1e8884032477e212c53427a7b5f18c02dfe52a14e73cce697c21fa210fb2c9d0b093347a82ed4a09eb
|
||||
952840582e0d1e7645c08401ed6fde3e63f9307c7e45815c0a71d37cccec3a50:0aab66:a760062f799ac72cb69d633e41457d7094f356d6b20d8d6635b906b31c1ed1e31fe88c6d249bc669b4b400e25131fbbc0e0d2dbbcf7d4881c809a24dd7a1f2ef26c42f327a23083303a7b3e05e53771306c5c706c3b6cc44e050b0c55aaef06f
|
||||
9cf6e3c7b9ec13b2d1e10082d36668eab962ce02d19b7ea1f0a876a0dcace272:cdc4bc:b5d3c46fdbdb27e20868aa8bf292cd0d5eddb67cec04408d21b54ad0a127070d78633846c50c757b7d14ccbcfebee4a102048d3885159afb3655e330593d37e9480480cf84e6e7fdc8a0a4fd0e4c658bcbbce79d4a091037912712808ad6dd30
|
||||
55be56ae7328ccf31edcfec667c1fadccde36d44c4f7d420cd13b563478163e2:09d14ad8:86b20e8dc583fd7727e1a1afe20b205a14fcc27339d7871db9ec5347dbde1818357c825c1d8158e730df80de1bc6979a037dfab2f893f710a201dc61792bc4e1f3291fbbd99babaa8ee17afd2b6abc6b9341edd3392c738876bedadbfa3f1c87
|
||||
0ce187daaf68a1af9fa954c1b00dfa0b4a7f99d72f2c0417dcbeb01bf3abbba1:06cf3372:878c0c0dbcf4810933c3033e0bc70b58a53e761ab2946e45c1806f8f2bc8228ca944794c59378e1775adb9b7e647e2c9108de40b3b067e82c6c2e432fff08925a1d97670403ea80318e0b1c51765c390594667db46e076e2141fce896cbe606a
|
||||
9efc5c31f466e255ab606debbea016a0d8c36c59e343af468a92aaabac08b9a1:0cd3d0bcb9:9895e0c9a3639ae728c5d21bca58c3ad87d2b7cbf728b3421f8fc92262835605a3edae741f4205564328adb71db3cf7c14fcc4bf52ea08affaa9fe780b7caac7615e7696da1029b7c9e44012b9941fc06d63e62360b74c553e87b180915527bb
|
||||
320f214da563ab7020bf54c97ae7c86868bc5c89abed25159c90896ab9fcd72c:14110a5a07:90799eb2c1b7f0970f1fef2d1de9f9c66be0992658b73f3c7690dc35f3923a9502c45bcfff360affb554d6469818622e035136a8acd2a73918ae85abf6379f22c8343bd7a2039ea2998cb5c47c68b6cd85408452983d5faf70fb4b1d3c8c00f8
|
||||
cf561deae5deca76ddb136de64d7ffafaa0dadfa0c1eaa3e402b98cbbbe77194:095f9457406a:99b7e34d2f4620cd7c3a28677b3936bceeba317a66bffc9220a2db122a6a5737bb4eea8dc197e4c359178d97cdbefdb6150dcbf76e0b72f13eeb0afb61c34a49aecece57c7448144deac3d213b60940aac157f7a9323ca8eca94f992bdc323b7
|
||||
6bc5dad1ad7f6cbda1f1817da2dbaaa1d35bad490ad2ead25cd2eea895324ead:42ed6e9eba7c:a95e3f826879865c2479853984da1075164dd2cd84cecf66ba253d16561510456dc346d1257cccb12155331c072bce5b05b294c2bc870d21c0ab9055f92e70dd880ce8c7c144a5f956c5395aa49d8a5186b45627c2c56f41fdae021da95f26ea
|
||||
fcdee7c031f2f14478368bcaa3fb3a0199e017942899c26cb5b2832e708a8c50:0010037b577fad:8e60fe3cdc55cffa121ef09dc84a8332e1d321b5a53809ffeb0de2ec41be7190f681a9b466a8f219c9980600fd05491f039338f9c6dffebaa05631910fcbd32119660809b901cf19ea844771ffbf61b007a1f89b1604471808beb58eb65509cc
|
||||
4223e4d4129f4b89380844bb31b46288ba449b37a9fb376d96cce1eaa628e2b2:a192e9b18da043:b47e432e1c6158e0c17620c61e801a1401dfe8578db5b61cb131567800f40ef1f5c125f1df5ef171770165481047f09117b2ba7ca45f5caebed2777cec8858db2a5bafec8f7adce972044429437b3afc7831640ea9181ed20ca023b109a056fc
|
||||
9f7c1e5075dcff0084434d9970267968f9d0b69fecbbd5899c4da5745b083811:0aa9c73dd809000d:affbdf547120be416186444b617a4a8f9b3d38f95ae1179fc1efe933aac187c415a62395ef8626a6b8e1f5a56d89e608155d5c52fa2a4b56870cf98e07e21b3511c26fc7ca0da24a4860b1193b7ad3a0d0377d895da05725a28e4062da7984d2
|
||||
85bcee14fccb75ed024df71d8c7136bc75c4bd2e904ff6e05a044b4da1605b9e:e58d1bf3c64e6fdc:895105813f080161615181296a5b882c490bc3be0fae374eae53314d26ca10e47b3a31d6578e9b101a2c66bcae723106022e6cc01a8b3b7a7a46bc0e6430a0f940db73f006b29848c424904b7dc3adb19d50494dd41f52aa514db537c5965223
|
||||
569d72679eb7a0f07797892b09fcf7a1dbb61fae4a73d4e798ef7cb35fc99f77:01ec22b832611852c2:883e8e05f6e32dd24c9640666d58f1952ccb32afcabe0eb7960a18abc20bdbcd3f5e00f1b633e4d35424a8cc71a341380fa31634c7f5e8f3d3c173b4fdfcced456d9d5dfadcf9a71fb8ad2182734f9aee6fe0b11e3c7331279d2530e5a042f71
|
||||
6f3c9137e679a12e1cbd241b7436e8bb6807aa491bbf083c6b68398894cadb4f:580e2bb1668220c7f7:848be794b037494823c76cae9de6083a69902dceee2979893d27b04efc18eee6b28a4754288d7e20f5970e540709cc4f040fc33482c6be06311d1793fbecd66194fc97f9b491631257e19babb61bb58a464888a02924d5e10ba4113b2e9d990f
|
||||
34ab9c6ba4a97fe9ef761c6f8ad6f4afe5dc390bf159e213b58e3f2175c142b7:0e30de7147d7eb5e2458:adea87d3e463b6a97e05dfd57b45a8f1a21953e2747bb0b324c54e8904da0e16a1e83565426ef32852d70af76dd654ac050c4c27b708066f52cff440196502e3ec527a6fedc68f21f1dda783bda9f09d415a4d47384836cae99b417089a3179d
|
||||
a95f51550aa2b053c006523bb3793568848725fdd96c513c47f411f71b344e3d:b0ded16e59492cad4ae0:9329d8054b71e8db5b6223f76e23bb76974524e5569e5f82f21ef812f4b52b2ecabf5f60d1f96cf533da962c9f3002b10a50459a031a11d7c0e25f2fd4c38d99f8c1a22aa3b90b2f1fe333bf185b8dec40725ce47c4ed2be32b19279de1d63f9
|
||||
0589e72a73238a9973bed6260ce69db89e210806dd01619cc1ccc9dc695a0f74:04e9af42b0a27a52d319d9:8d622962af2e158c2a363822c72616d6d071aaa93f628b50573d856a04da48ae8c907d1f42e680828b34fb70b53ca73f0bfe51ca9afe03f7892eb2ae754d247c7974949f4c35e7a21800d5e4b5cd7a6efe4f3bcc7b1a3a99d8bcf8164f47cde5
|
||||
d64f5aa8ac3c4e6acfac48c8966794c38b69cc56a3fafcf5629a1adb59225a71:a5947a1e1ea080371b227d:908fb9f48bbf4bec043a36c547a2369d6f9ea5e5b27dbf2d9e1a9e5db4f951865686830e072309e05fbb454b6c787f2916f5d24ad4dd231749f0bff9a5906a10674cddb9e6615a1d26421123b64e8439ca111e93b299f5b89dfd74002e8dfa53
|
||||
394fb7dd9c5847027d1c9cf411a2386e1f42adaaef4ec8a1b50204976189bf5a:04be6e8b23f4bcc91a8624d6:b8a5acf1925863fcebe3d59d5426186e852d14e93fc24910cef1a3d356a8279ef3dc6ab622ff23ab977a90c203587a000840ab5561de0713cc495cc35f1f00909435562d42ce852cc75776aee6b06b88e86920ae2cdf3f36bab3175c44018a7c
|
||||
ae01be984e0af84bcb741f19b395300fd4a4f3ecab6b2d59b1de8a65622b7351:10297f7bf93a1ef627b2b3fa:ae8f8c4c0b047c014e6d6e4d597a8ff350b57d653c8c02ea36970e2ebd6d52d0157601567768e72797f9f89b3599ce79120f34e50f82600ac3af5878bc6807f7e552604247702cfc7049926a86df04728dd9aaf64e9249886ce8e1492e93ad3e
|
||||
3750907c1368d256e95ff098a026cf81aaa0e8aab92560cb0cde140eabde4095:0e5665dad423cd2d935a3832ae:a9e64f0739ef9addd3bde83650b5b372562222d7c81db9c7c49c14fe8224ab18de355d23964dee080ed66d93a72f171901bb78cf7146957efecb006c5f7d49369569b02055389694f9ab44aca690eef31e3a0cc670dfdb53dcd4644de805c95e
|
||||
32812bafbe9535c8530406a5f81d88f9d677ee0693d6c5c63271b3e154ffdf23:a61dc6c0c4ff587b31ef4b0cba:86b48e37dd075088b860e7c04d0101ec1ba8705dd9e7e4d6db16c23a250a3628a3b904570f95598ea286c2ce7acebe22139fa6320fd132423b163cf76e34329145a609e1cb9e17133175df426ca598495e6f4962baef021e7ab6013143d47527
|
||||
768c79e74d0074c4c7c3ad74dbd62bfcca46bf2516d037f2b766e6b372109ca2:03acf045a0b276a634a185e97860:a423b892f8be1efcc5316e312f315575f2d2010457bb46dfea66fac971288b1d30ea103293244d4ee6ebb91d16f0e2ec07fff3f3071968bebf893bb98667da19ec22b5a621b0f9727870dfcc3d525ddc3f34f8bccbde06bb30dc46929f53ff3e
|
||||
e92bb67b632f0cba53b3eee8ec19e2bb92356452fb217a18a65433fe1a8cc6bd:247f00c23d0e934ccb565b29c778:a4e454e97466e5cb8f7ae7c6941c66bac5ad16a794e1d1d733002ee0fdecf87fef6566984acf2cc03e7abf0f3286e32001dbc86802d1033512aa74eec687ee1513fea97b511bf55241fe918849c6725782f54c8ccaf30fb463a4267edabd4ad6
|
||||
2ad285390978c933fda0b3d51bb58e6251f59083e89ebca66a565689eb373cc2:0d0470c09284578db66c65d1ed7117:9562202c7186f6c8a91e2284f7ce16546c8e9c29d9016caa9d3d8b2510b3af30c379f3fc459c8ec370bea57010df69051836658aa5d4fe2ab7d3748fcf1cc7a65996e157d83a522b5c7a09256024938ab32712e1dabc937b1b0e566202302377
|
||||
a2820ab3c2ba9df56defa674dd6cb563b577e1dd2f9a788ee2de6b9819853222::ab29cd6f87e1fe1417237ec813bf3e26adac7df06dcf75f4bc7991706f802f59a3790f0ba6e1c8be3893e48fefd6b30d1219bb6063ecebf140cbd0175eb0ed4d33064ee1801ec1b263c1af6c83301d5fe294fc76009254ff2fcc7bddc8524d58
|
||||
adc7da1db78381a3fcee8f0efa0dcef72b64fd2baf73fbb331ca10c6c8e2e1e1:04:87a58e28277301d79af6559d1468a2e295ddf214788c10e0614562ba88b2db7b6fc807047fb863373e8561ffc2c8ae9610d62061b56265aa493860731221dff6549aa5fdafd81dc8e1bcd4cd77647d5dfab1557fbdddcd245abbac2a75d7b7c1
|
||||
deb39c2509ba781594323f75ac114d1f1b50f22695f60919869b2c46b1c9c6c4:a3:ae6a680307044ef6123a79406e39bef49af1a27a9e8689ecbae6de92498fb530669efa9d6d74e00afa2f90fadaa3491f010c1d2e15b557c8bf646bc08ada2a9f708468d2b13b1a316114a970058f428c1e327faaf2711c406b7be5ad5754a4d2
|
||||
1d41aed618eb90a7a481032fc0624c3b381f899b5aba3ecf4945ecb7bcbba248:042a:8f48ff4108ae64b1d6475bb156fbdad895c6a38954ddbad95fafac32c9046800565bc80d05382072d3fa790275680850194effaf3c8f4c33e74e451bb4fed479f08bba1b25c70a3eed0236fe773cffcecd5136ca78793dfb6317b7bbebfcaeff
|
||||
166ab7cc55be4235e4dbdff65beb5dc78eb3bdcf74531ddf43c5dcf9231ab5b6:1894:aed80a028af501c35a3607f83a405d40695b0247856f615d87a5effc041bed61994d5ade2a446649ecfb53d35c802a6e1407e7eb7f580723dfa4472cec745b33972920a4e391ac8cd24081be533a0f77f82e92056edde55034fdb5a1e62cdc67
|
||||
b4c18feeb2ed734bbb97f2fd07bcf2b35c38aaef27b018ac20feb8c14c4990be:018426:b26329f66abf3d00f25c0ae8e4a94e3395b6550f9b9991e7ee9172f75de000167d84d02194af324a05b2f284eede6660143838070a7fc976e73c5ec443aae831f2f1656be1644aa006f153894dd488748e18c645da305a3bab7699654fa4cd43
|
||||
b8b35235736a2044338a01512b9caf41f9e8f747d957136fb1ada889b8c6808e:ae614a:a11a7ad28918d05c5746305b83f1a50aebbdeb1e82d2cb7b30ff8d370a52f9cef163338a7be4c0446e396af2f0bf43130e99d450879124884452ccf3b4b25cc84a955ce6e80c81c8a34b459fca1f0b6bacbb27a2135adf6fafe7473eb218c917
|
||||
868a1e248f596caa36ae397726d79456fdb69341c619191940a244af6690e0b1:0e3e0406:91d463c1a0bdd8dd9d2adc0cde8b8a54572f6b9b530978a52a2562d448a1ba741e403a58dbe2ecc34876b1ed5117aef009daaa1271fdba64fe820e5e93a47d29403479e5417b6199b84356f8ab6f0d7006a2f7e79c97bcba57a489cdef4e0f10
|
||||
6c3639c6647ea53eaa46bc577bbba99b305b2cb9853dfb2cfdc702665fbcb546:7ec9dcd7:85cdb3b39c6db0633cd69f414d984a990149d391314a179a8df5ca53aec19d99ff5589bc6ac1ead6194a176450760eb518631ccf74397565fe7d0f141c33c5d990df1d92aec61c7774673e6db2bc9c010660d1a960bd9bf04b34cfbbe7ec16fd
|
||||
efe2fd6210ad7b47bc60b935a23a60aab8c4abd54ae765db22a339228595180f:0a0eb3d674:976186898bf01a16c788b5622c088fc1f4e56c48d704a7c4a6a580e7674b9554bc606cbdb2a1ecaa825956bf4384bb8d019abe98d85610e5bebfddd35b306f9b375e45c136366335ff2db930da723058abd3d708d324f70065da480c42c01ce5
|
||||
d6baf02a6ca31e6e8c4194ee80eae4137ac267a7f0389eed15cbe814c934f34a:43e20cfa8e:8f9487a6ce3a9b5153009a606de649d36148180d50efad4b0655fef459cd34b92fe8b729d7935479f6276fc9a7364cb9022939ec84463c080f9d161b990e2d8c79ecb013b6d3913ec37e8a21f1515a9d9c07679bff74b182e5ef0b325b83fbd9
|
||||
49fa1f7a79dfa239e804f5e320337a708862416bb909bb9caec81301f2fe451e:0025641feaaa:a51b7b4da153ac896f5e9eeec421a1d61bc915fe349334644f7315d9bf74dfb46b24808c0874ee61f003173d678b59bc041fcb22788e0cb292b3ee98d827b476e0081fb63cd99441736d4ae6bf04d6b78de252825c42457c1415914cbe997ce9
|
||||
3c0bdcd071ba70b2ea73d1a4c5768a3cbbf3e3642f8ce5e3fbc62b558a1693d0:c43263af0875:9592ef2b65a5312262ed8dd2128f8ab8e610003c459b1eab81e93aa0a97316a327b9f8d2b894e70e585a93aaa82b79fb0c4b2ef732646fd290a388f5ed453ed27b038d0a62e7c5d22023cabbab6278884564721b56342eb0bbf3cb839bacf7d8
|
||||
faa3ef6ad535221fe8eaf077b796a4b92f67d13ce78a933d477ffd348180f30e:05207d5c5195b9:93bdb10c35d990b4a1ca7c8378df7416fe913664ae1a4f318ea05bac99d99ea74d0fcd8c2f5cf4a4066b89d9bbf44f750b625d04a0ebdbf844eb5e1df3e6da24db5a5532d00f00d0a2c3003dfe0354e2a59c187d50b6369a32d1c683a1f981a7
|
||||
177d9b3bb86c602d76157c078bf48e764ed0ef61a65a953e078fd13abd89c883:19b6771ccd870f:b1e8c3c34a731873f24420b7375304df17dc7162e70fb9c2f3899c30a012b01816edf2ae231554762db8f19a74eeb9681981bcd70b87bb078968f12de45537c4401524508de636975b038a9494ee47e324f842ddf19517a0be5bff0b4b5142b9
|
||||
0e98cba458139123b6478608980bfcbbac2a71b7b27c4a71757cf3b6b775eef5:07724a519ed1b1ec:92bed7e9beeb8d14899246a1f8b7212e10155a413e4c8e09436a6a3a2a8858b99410f4f06afe005f71de6caa9ae9858a123bbd12ef19f123a3de91811525dd41c5be9b87e07ded826c0130a85d3b3fb4f88fdf99cfe3b565b799fcfe85789d4b
|
||||
6412cd26afd7b8774454ac305e8a32ab2dc489c33ab2dd853ea23d5d7c2dd741:531f475669c44d40:941462a5e2ff1eb47f4ae8467fe10161dcfb5bd04cbe0249b6bd29f46f04dc82057681af846e76e6aba3f052db1008af043f532601dcb71cbf3635763191035a33f68d4c3b33a9ddff0ca041fae49c17154d5a0df5a56eaa9686a560e27c2576
|
||||
b13dfae119e4d416646328c7784d0f1f6fc9cdda06093d198048e6a287269857:097958dba547d1ddf8:96bddfdfb88baf179ed07c461805fa538981f9bda26607e0d99bc24bb07cdb187a13ad1aaf60d17104c683b8370e944112f65143415f0d29c7c5f906c070f5b1b8923c71755372f76c4f9417f3062654c4b149d5d4c5b4a086e324962d0a9916
|
||||
bbc9784021c00a44a4993b096d87d7ef6c6e927baf1b21bdf45b474fc521b189:245434316d187b4f02:8d402e4b9c300346551c93dd8ef883a27dc5536ec394d47e0b45d8ce852071d33c496206f22908e49dc0dec9d9e6261f17d37607caacf48e9fd490e5235439114138c672f5e9affd1c474f82888406f0608e6b144567935ea0793d24f307aa5c
|
||||
80ab4ace3bcb0d664d75716c39c533cbdbddac74795927b82ba6c7a56f7f7455:0322eb451950a34f9704:b33bb7eed3efb1ddab34f1de18dec549a84939f43dc388be6c9f43b3fc627c70e13ad188c26c0a295fa6b06749330b8302e814a0c7b20d4d468d8eb2ea2af377be9a6a5ded2f74029e2470a303e202062ab534fe07bb8f83c5ea6071bc820d9e
|
||||
f2e4181f779b7c3ce0e24dc2e5630191020a7b9d04ba132d5dae95f7c4010dae:97430a39a825c623505e:9282cee78bf7ca36a252185679e6bdd147897f02e5b731f1b79936211308aa0eb9a81e1aee31593790c4341fd64c684e0481933bb5a5dbe6ebffe87a4bd2ce471bcafb85c16dc65698209ee18a290e046e97bbe287b2f207b0847692a78bb54b
|
||||
a910c35660c25b2c636f26fe23349babc219db594ea73895e6fdcb4bebd8be75:0c24de4b18c3bd1206de31:80eaf929b05fcba00b04db3a8163a6d520c2f41b41f7287013c5c5949cd655fdaf591c75a46004e032a10dd73c28ce8610a6d542f69ed27f51ea4c1c0b7430ff4436f5df10336687c783f22ff1598d409834a86a78fae268dc0a2190ae4c77b2
|
||||
c860f534570ffa8c21b1d80e59139db3e12ba66af7674b0e3eddfc92ecbafb01:fbbe246ccb3c50f9d19dcc:b5da8cd719f062511d558d6977415121cb4413f2b6ef9480945050286909b5e283547c1b8bb13c80777ecb31e82687911958c5e91195494b20dd10e863ae9981cc40a089c15a8eaabe014c08fee5f77c2864532ca71d02f8bf404d4cb39815ce
|
||||
2b4736db74d642173c2e685c02b9ce4c4cf415334fa2ae0ff6dea6e475f0e6fe:07896277799fa172688b9d4f:8972f8c3910f79643f026bb5551fc9096bda8cf94efd19add195524dad7fbdbc7c28aa6b5f40175519f9b6117fef50b5015a920196b7a02943d6b01c77dc411872ed74a65fee33952f0f899ecce37d5d47adb8c0a9766da7d2c41f3f60a29d58
|
||||
b1033ef71917d3cd58731b284431a83cfd13c58efd0cde09c552bbab1588faa4:45be6616a653de4c8697d5d1:8b2cc66aba7f601320a79aea0d4c72a922311c3bc25114075e7ad9a6bc2f425542269b08b25549e608d47cc7ad0ed34919da4ea212de347edcf7a7bfe1ecdea3e8200a804071afcbffe0f06477a5fe8d581a1abbf87f7b41b127149094fb5a11
|
||||
9025178a2165d4905c38c9509e64c4df887b8b1c9a9e924106a23abec0229f1c:0717517ff121d2931a623d4de4:b17f3eb77eefa04ffb764934c6f95964ea969124a3d98637e5abae81d42464ee3af4da26a102e20c193d8b2f0e2469020774fca2745dfaf5ca1f8cdc7588adb7911461b722c74519d859d93717032b9d0a2aad506327908b651b8ed107c19259
|
||||
923b79bc1f6b0fe9448821ac98d51cac6c3a2850ef0b58daab63b768a4fce80a:0b101498ee27350dc7a5ab4123:85a22e44caf87c1e77d9ce041660659843dd2d377ec5e944cfea666d47c38678e1854ae7b1b501ed2eefd1aaaebf27830487d3a5c10375c07c427bb20f5c00815d366be7c31c7b0f92241c2ae86a11bf7da35ef77cf99cd8ff032a87c8a0a65f
|
||||
6886577263e5f25976f323beec475ba37e4060056ae6c32cd10aa3a47d17131d:0419e98cd72a702b453801a8cc73:96a39e6a1c6a3f2b282f4cd65d8e490c9d5117f9dd5503feb83b6cb44bbf3fa3993ea2d107297637a476ad7037679f5210802c078e65d791ff741d61986b8854ad3abf21323a5a9a2d6728dbdcd8c341dfcb3a2ac1c7535868b476c7d0258d8b
|
||||
4e0239f3665744032462d5e5999b62e4a224c4de38552e06d96fe0afc8a56579:2a5e3e96548d9a9a35ed8d497b54:b8bb08d7c5abed07781cc5d3120a6c62a5a41514eab9580be86007e0049c0cd2f8622bcf30a93d43752e1256310ec0e80895f6dd37df67140340be45743a0fd92089a174f4cf550b988a4e717f65b823fdef076a25d521402b6962198b6f81b4
|
||||
1cabe26d6ce80e9e03c326d02decd6970947a0e79f8e89b0ae3d31a96a79c698:0ffcc1f7376eedceef0e1195bf735f:b06db65ebc8cfbc7899b10a2c706d585783f3f10547f919b9d9460de50d292172813838b61c75fb1f409c833c98ceb2c1698a5c414ac31d60928ca4253ad36a90808e74797a11e1d50ed132b3278bf30669790ec6499b8e9eacfbe22a1790c44
|
||||
b719dc5db6dfea9bec433b77bc3461ac68c2ef86f61e3d1440c8ee1660aacfc2::a6a0083be79c89593156082c57931032aa8f726eaa8c88ff7841f703a85a4e28ea2848ed44c98aad04aa17e176063f0d13661c19c70b2e23a14482f737c391411cd46c9e6f156cc02bdd0c6be9f6837d45423eb98de094eae78c96e5b06ecd58
|
||||
1c9ed5349162e2d667b1972293b551b9e847b4cf5a0b271d8da70ec586f14186:0d:a103483d0dc05ad0a66d99f4faac54eb4a4e763ffb3aa337a84843f4c7be4b3010c10f9c8d0f0bca4813d9f5d1efe50917a587c6fed3a21fdc2e3f6cadfd1b7738d19434f54d370a2fc8672a9dce11b50c38c69b9eaccbbeba5f80af6d371edf
|
||||
4fa311dc95281b95c653d295510303839d609cfab1ae20e42f6ecdd8222b0232:96:8a153cd16762f07269ef6b019eece8e79d7590681d9ba154a4424531f6ba697ec0f8c773a445a6c1b6607bc904f8d4e60e6b4b05e152395ad25db09f6baab0d7d8b5979047a439566cbe19b9ef28d020c3272ecff416cc13a94ab0d12c2263ca
|
||||
f29461b0b0936131d4a1c6791f5f64ce17f9b2cf5c729230884375535f230e3b:0dab:990eb5e5d2c76a5189be57f2d96fc5194c73726303c2a31380e7bec1ee38ac220df2b82a1b7b53b4947c7e599cbd470809bf29fcf2dd846a9e2b9a16bfab547e44a43d680643333c96688be240b9dda7cf6dba812cdaa40dfb9d387f90a1cf8b
|
||||
17b308d35f2b2107ea16bb5805456c49829f328a25360b371003a91a33c02d1a:e3f4:a1da28132bd68317dce7f6a4b10e64e2fc6917829d50c4a8700c1493bea394e2926cd47c315f97900007f877d25b8156074a1acdcaec936a04ef3b7ce18e4145afee3977d3ba18c760becd1a0fb7c81c4ef0dec32d0aec97ce0a1cc8946fb793
|
||||
a1bcd7368293c73133680bc8d241e4350174e87f730876d588095384b20f2e87:05afe9:a624238c3447d01ad6a6f548346402adce9e9b8e8f0b0e94fc78135894df3a9054eba9ce8ba8f642cfbcf65c4954d7bc0fdbcc428528e13bbbd8c93d67575c1b9fe1d1b0033feb44ce97a3bf4ee1662a0030cb1b84ce0cffa79b68f60a6ca9d9
|
||||
d44e4344c56472510fe84340b9b601ba7d92c8794e8c5cea127ecc01abed98bb:2acdfb:b0f0a8a9fb3527699e71ddb9e9d7dc7f9a51dcb14d4f9eecb2ccc775adefbdf6d70f6dfafcea0119558c41fa4f56833f1253fa92472061c46cee4de504e9779a80b59f6f9a62136cd7665d28fb885bf97729c5da76a87385582d9059669810b9
|
||||
7bb733a9a83f79cca1407fc798df02075e1b747fd1537c66aaf68bc35c604f2c:0c344c30:882a49978a874f0afbf464218eeb219d846d90a23b32fc37c2c72e629958a7ab345e72f771c39bc7868d6c8f5fd03d4403ceca9781e6b19632d2ad287ee73c6c4edbd1b65af50b4740d5292cfbeb74c4086dc55f355e471e24664a5732f244bf
|
||||
0a5c7cfd06a465381127918d0d1ea38c1e4c933444dcb7b62262d87fa08b95cc:926bfa1a:a71c91e486195316156f3d78740a93421d8a8c03c4c37bba17f971cd14d964ed66973c9934acfb06478f0d76a26eb04410fb8aac44abfad7835cca9a2a8f4d21ba3b6364dd710c8e58ab1f06184126aadefbe10b1b5a82c0af6266ccf2d82f98
|
||||
bdca0d60906fd02276f1fa17b40a874ec15e70793b6cad48b0a7dd9cc26eb139:029a6c3bce:a645c427f86d68b0230eb7e01f406b2496b2723a5133ada2b744dee00d8adace24e084eabfe16de82466eb63130dbf3b067762c79586bcac0becc4f14f71f349a3811490832d46159420258416a22f6bcc3065d944999fb44441ce150e826545
|
||||
ae17e14c12b1b9c68cb127a8e6807ede5ad4ea0a8c74e6a3338a36568ebab6c9:fca00f9558:8af98e40a45f24b71942e86f89ea245c2bd7f91923dc60b365c7df6125ba95a6693c89767c8b7f34f2ba7c02c42467e410ca9809680bcef8f22d736e1a90e14ca171028746bb2ca12ad8217e945bfec3e004fb1a81b9c6bff55802811a829599
|
||||
d727fc289ff1790569cca790cccca75ef6f8a86a0a681be1a063d33c3795aa25:0a10f0e0d2dd:aa2a0cb8383c1a89d8659c7fc034d17feca0261e2193999d36a74a811c31cd7f891077ebb7e5d4fe7574f16b62057cb115c671c77063134e767aa5db0b91bd1e274e4f254c0b6586522724aa6ae1753d5b786fb04c0dee7f2cabb143f8c4efa0
|
||||
fa50baf847fab14a085aceafbb6759fba66ee9ee1bb94c5476ab3cd8d5f6c8c5:21477040a415:b62ba9af86a3a404a81ebc1af12fad9c2199c6a9417ff45e11abbca7b912bc656216e31213d4446132483cba4ab22c8015e6fddffaaefb767d6039e1a7c7ca6ea1c7cad2da6bac7c9a0a13e6f9a91d8704026996875e72b895a7016f2b82411c
|
||||
6459107c9acc6d52cc5fe0e1a7601e6f8004d5c78fd89d41bd4999aa5eb1a551:009e79cfe03cc0:8bce396182f6e23370ebe56bcb26284e082f0b07f6473be655c39863db041630c75ad570dc0edcad57d1ace944b44e6218ab9c714bebc6e5f81026b9fd53c736579e2287fc0760acacef4f20922ad7204656eb282d4f861f0e46ea23fa6c61e2
|
||||
ac03c6e5086c5bd204c1c1236f90d0dea9e4dda727fb946c5780d60b16be4d03:c677ddae3453c8:8a437767f8aa83872b6a56b7eea75a3e61bb8a86990fec00d16a319e302c5ef3c2b8667a3a5a09a208550dac2245abdb18fdf3abe7c2bdaaf358d9c5609a0214772f1050301828cb147a5968931e570a18cb4bdc90b1eeefa2d994357b907a9c
|
||||
df27dd0739187f28ea241dc88d467cf624e7ca97c41561f818fb354b3774d874:0efa46539d3159a2:ad04f726bfb5191cdc972f8f6b970c0728cfe44fc941eb54fb185f72d3fed33943c91918744ca0d68ac2e7f3849fbd7d0493ef9f621b217cf8d49d433ef8196c1123841b81d155f4b6cea59a27a01a1565380dec60626036d43d5fae915a15b7
|
||||
aca92bff9877bfc35d4c25cf9af70cc41cf0816b17cb1a51089aafb9b5bd7d88:31c9c23a9cc42236:81d8d25d277c486cab2ce34efe06b468f59a56b4c0e4d5d3119647d881aa3dc871d0cf131dfca3ed811149eb5ec53a35079293d27e8cdd03d7543934633c368f4311424a2b5a6af33855c428ab901a0ecdaf594465cdd85264d635029dd73bf1
|
||||
21bffeb6ce5ca75da4665264c683fa70fe0570d95a481dde0ea9aecd3cf53565:0a1c395c722f4fd83e:b98d13103b26b3728d140d535be1997e6125c204ee09e0ec5420718b34ce5bca87c5ca5df9b327128e124a458d4ab375128af9b687e55742931d9df5796b74c2bc10e772a5a849588baf8f8e805a5fcb431d2d2b2b646c3f0b9d9ecec29e9238
|
||||
3fcabeab0355751e59abbfcfaba97d1c018aadfdf56c9bfe6152a3554db48bd7:e6809c7244d39b55e2:93a0f2f5b469ee5396552e7a9038bbe989092d5fcbf6627e035f7107f362ffa212140fb8718b0c182b5f24e3bec720f30fee24cb1a3321ecd333a5380a64d691ba71365ba385b727d2b82f6185498ae4a6506c70b65a623fed121665ec95c891
|
||||
ced96826415c753d6220c495f8189342da0d3b7b2c98411303b60bb3c8836efd:0b7cb2a6fcf257ee62bf:9656dd2a188920a2d24e5c003fc86392f7fbcbe8868827ac984f10cf57379ef214a408bb2849d19560e39a1c54b38d300627b2370298b69dbe28bc4b455177d569b03082048692daae19cd89be763ca56384462f1e2669748754adb9db515ac3
|
||||
241762137d3743ecbe71f1cf05391a1a371399c2c8254f4c138aa1d362d795ec:c7f22dbd191d66754852:90fc4ef59d11bc5fd47f20fa97f8d7cdfd10c9569d2bb352c5ae521b0310d8d8b220914ae087b207c61ccadc53c7bf4019f524a1079f9868dbab853dc69a34de7cba72d7540be146282ab20a43add794d8a66032ac64e5d072ae492322176d88
|
||||
a251d29b73b0b81bd22b1da683807231569a3c339de9cf38198489ca91c4ae95:0efa18547d60e8d370006e:96df624e538d177c55d91c26ba6d678e3a3180a68aa9a11bf9e45760e726b9fdb303488564f879fc91b7129a0e14a3ff15e6c26a968dabe07b38773da43edb26657ec6c286db93c8ab0618e915fc8570c6b2b4f1ce53cef963e067a9d20082ae
|
||||
6f463430d2e2baeec5e16c4fbdce282a80db31b3a6cdf1fad692d261ba78821a:2122e2d2571c6649a02801:9353963e93194b91c1aa71ef9e9d66dddd868f368d58456effcf266024f131d1ff35e0adc9a6959b338049a4fc40b20619d0f509cbaef3bf5743890a414d6e7d39f2c4cf7e66e85aec70a33bfee3b58f3e84ebeb71c4e4edcb88e460783aa8f5
|
||||
ae68c25fa3d2d5186ef818270dd4738f122858e0431bb562370a55488b7ba4a0:05489e98fd13bf202586ac41:b4e67eb25699533de411c7d072a60ad42e916b26d8b9b779e7bebb4657d52fc079553ed98968e9d81ad7ed77e324fb4915d7124b3f006ea12370ca904d2ffd75506c54120a7ea3f901596e44c3ec5782e458624fa6561a20f895430fbf363695
|
||||
59e8933963a382d0d1634b52f6d789d09afe9f411de832caa89c88622470f70c:5264749d484be5119deec774:b716c49e3aec7684f8aaa1f61012cb8af522d73749717534c8097d95056611e3475e351e0a294937071c644dbe9db238023dc1a740bcb55c79ddec3b3b2cac01c8c8d975a4887166b7600ed1fe454b203b4e5ee49906dc7988bc72fb8b629d36
|
||||
328cac74a8f771183690de87e99ddb3be023ca62c37c243aa2ecb3ab096c32c0:0d9caae939afd6f2036b4c6466:908cced630b0c17a0b862479d99eaaf1773b6053b8f5b6fbb044ace6722da5d1fb4668fdf4e468c4dc5407718bfa490b0d7cbe01c0919a8b8a5f342c8fc3ba06e2c4529f82536122061281e4c3ca1f607cd9c2b75eba03585a5689b9427877ed
|
||||
760c5fc9f942cfb114ff0ec23d0fc22acdbecb31d5b15b57d1ad3a9459c40b8e:3a3573a3a6cdff2d2f2313addd:aac2acaed63652d44369876813daf3cd24e66d3cd667c03f3de977bb17be92ecaf15354c31447a51805f7c0381c8a4750d7e4edcbe3c7160382eb54390f7b29fc5fa125a625ce31f41f93c49c50cb5af0f9ab2d493f7cd28d54d1962060f0676
|
||||
2060526e5e104aaa6f56144304a6f4e2953b231fdc97ade6435835ac9d3ba7e4:0ceba10020c42ab9cf4d64ae3486:951fae47292841fd1b50ee4b32b06f961ab29d12a57c156a7fc2868b2a0b4bf42a472453a782d63bb7ef789c1e337faa040703b34f967e2446330b6120766266c70acdb659654daddf6446d80afa984cf89901149bc039cb7e541992363d3909
|
||||
f3fe92129f14598e7ce8231966fcdd535e77278b767149016a502c7483aba788:42a85f4ca158b692eccb2dbce8c6:b2bee8b5790c28a0ce3954fe06694f4c81051a08c6fe91c844d057d514c7d9de2be61bf02c80d73376ac5cf9fc21827b03e85aa71584963c418dd6cf0ab453d644303ff3b2e8148bdc7ec4d13f405e89dcbc685a48c9cf06cce43cc9d5f8f5eb
|
||||
95c22ab56885211c159714cf80c3336685ae64009db9d621c154e50761559ab0:0de6482e4597ce042879305f4f07a5:b32c8a3b62cad3eb41f9f83630e49b88a914a5df2b28cc624f028f7778d33d47fa4f01cf5269c1f3b45453ed0aeee2a909077803aa37814866a62b449dfd89770a05939d273aaffe33bf178e0526657c928ecbd0f80ceeffef9aea2dc1488dc2
|
||||
e2e213db79a576da7d0a616666f4a28038f37e3d70b389c75bfccc0404e1b023::8aedfb44ff3d22ce2d0b27fefb61b4aaa7f44456b7151375262cb4fba097dce4ebd54353e9f1048d7ae9b0bcf4166d7319b6055c2ee1a2a36cc32432f1195dc88112203108e4db786540e1334157e90aabdbd26ed3e0c9abe2e26150f5e65757
|
||||
f392a0265d6224ab1823f2923dd5da25af361e4c87734cd408632c0e34dd4166:0d:a232bfc8c1eba16b6092619432d5c9ca0ea2165a0cd83a4a7b2cc43bed4b283429d5f3d1f94d18ba89799f67ffc07050099d10d6c805adeded8aad6e082588caa3aacc1c1a1a9605fc682fb9ff582b83a44daaa65161b22f501e33fa32fd2f87
|
||||
c70726356fcb2dd2bb9a9ae6bb80868b2ef8e441f8bb651e0710bbe22bc182a5:a6:b449329a45d35461b1f0cedcc20906481121ca4ab8e04ba5e8dd8e6681b57d9ad0c40defee9357c189ff155796a9aeb503c5b1dd296c0545dd03e337cc6281ab5be61e433352b3cd65be0f245e475fe4d9369129a87849e78d3a2851b17ab77f
|
||||
0c87d91d1d69e322839804c1cf3c90034f39a9279014ee549d284a20e232bc7c:07cc:88ba6e38e788cf59896005510a3396bf326be507d91e9c45d10aadde83513996011b4d2eb100197d2d9c10783a5ea27b138adae4497a49af8439329062d7bf3da2a3bec50661aacec7d916e5da0484a9e58a8c0b054717dbe30eae80013f9e82
|
||||
97a7ca86ad085154ed4dfa215ded151b0188c16e3de7d488dcddb202c044483d:444a:acb0568ec50cdbc01213b93bace3522d3072bc13e1c577d161f5029845ad549b71d08c158c98078418900a6c9daabc5c16bc6bdaf9f6b749263f6f6d6a687418cdf0495e49c406ec4bbbc586b96bee78760c9aa2aba4e11cbf32c8e77b21f13d
|
||||
b8f5eb74435a252b35b172f7c7b863c78bff7e60e9c7c9368d33636300faf5da:05d89c:8eefab0eab1c9f12ec661d9b512cbffdc823c3a1f33555421f859818daa9f5542962d9265f297f6b2454bf9e9ef7876806b1783ae641cd1df807b4823cada155f7c849c23709f2c1191743dd7ac842e1e96df8fd7e165c61d8103bad131c4da4
|
||||
15981c773dca2cc1948c4614f26e6d6cd0beec3eb672dd7ca5b1ebb2d38b6430:350fd6:aed8d80da4ab66cf0dabed854284e88de19a97663698c0dd91e59120b363411e46fd455827cbcc933f97a781a035546301ba0f1bc2d5fa9d48416eb6c00cc2c673529009e89034c10a40fb9baa4da241e20e8bb01f33e675a4f01ae527761167
|
||||
14d83b474474caaf07e920cda1919759593bb48e125da09ac7013825b613942f:0db5516c:8ca43cd2679bbac2d9de3763aa28612e89a746fc2227b72e212a725c9acd6d9ecb983e4d665d64d76d64bc1c67d22ad701af6d74cbfeebebda30ba5c00de436c6f78b2163ee4fab88561e4121dcca80ac56df3379267e3be26c06a1034edff7d
|
||||
29f5e89e7a6afab3da8c1ad738665a9da193af9a647e47753c235bd043e9d8b2:095ce9c7:81057773b78041728ae80f63e3bff13d9253414f592162945995ee98a8beddbd5b61f2753a7ea62c53cbb80f935d3d530a143945ac5b3532589253e2b7cb42bf2a0e463b0de9027f89cbdf710195c2c6c0cf823ec3333360fce1c0e6349a5093
|
||||
4213489df97e792eda26d981492a7cdc3100e3cf4bbda19cf13676bfa5140584:020d6ab3af:81d792916be49cacb0d4f3950367bff32c035dfb083634912fd9bc043c0c5e60efd22199de3cc93374c1cced2e836b3e0c678e9b94b277734be7e127ede167c6051907587e43fa4532420ff77cc23e84e9622a16aa0e4c69de7c72da3d6ae605
|
||||
4db4f6404c0c0fadcee5f26717ece570d846f5d0bba9ea9d93713a067c54e03a:3b8cefb4a9:ae1a72ee38dc209ba1c6fe7a295abd821e2898e18db1a479edddfbc2cf889159b8355472d301c905619b170b91e20cb7161a5c823d6ae8090c90f6846b0fd0fb930c28a2fd03c293737a1568b3a135ce04009757287e58a4a3e914a14b13467d
|
||||
f7494e2b206323a2c626a625214dbf342affe585f1bbb0bd54b2c4f9ba7e86d1:012752047481:b3aaff35d780f9100b37f16a7f0c3a4e0b2306e2a5afd1486ae05b2dddedaf7c0de5abb96100166e38ec56fc34672cc418269e05261d2ae952386c08527ca7d89d1c4ee07ef2d6ef0e3412f52e1a0fe868c99e359926bf32ec9b3caf9631d2d9
|
||||
7101cdcf4291ca054a76be43a72be2e7c57b142d697d5f4591c416a2ba20a6ae:dd2ccabadb72:84a751209648d7dfdf43bb68863a3ed0a2c07281c5b8557ead46cb0168d4ff1d8491edb55cc69a0ea4eb06359a23285e0d844c8b0da3d91f7f7543e251f8652f210c0bce4f819f55ca1dac972fd4eddeb5277c9be73974df310fbfdd42090e9e
|
||||
dcdbfe35dd9694986d220b00450ad1c85a416fbdbb15fd5f0ba11c908c4e2242:0fae62a316d518:9037982e4aaf6e7f9894cc267ddc26114e5db5189b09c1e5401f1fb040fcf32efe5799f461f140af85553144b6f930ef0a89e83a1438f85b63e20dc82b00d223384a6c9120b56c6dbeb607ded2d7b268fb8d6d06f9d526ca701dfe409ffcea15
|
||||
5833ac79250158123f356effd33c11b69f4970480a1724b2c0a0a1f3c5d86e48:6fe2514b0b040a:a655dfcb28d84ffcefae998f1d0c87359d4c522d549f1d18307c374d54f189403c4f95065a2b5b366272c1a0e1938b2a0fb4ba410290ed83ac3b55532d406d02a34001d6c6f150721c21cc86792a9ef94a3c1e498092c318957f4559aa3fb4aa
|
||||
60759490c3c1dbc40d682ac8ec5b23e79df8679f527ce5bcd4b98da79172579a:03d670b0a31bb074:88adcbe23b4538911ca0555c28c4667b65993141ae797a71ee54573ee9b2d450c8f5902f24ec0dbf98a32ca7a4c84ba00c1675075b8804244f9ee5c8e746ab0ac18653802fa7eb2e8f44a1346b28a0299fe17fab2a2100d1932c1c8017c04790
|
||||
0c8beec5b19b6d49422b9be911384dfdb5f7b784fb7931a9fee03f2b2b1435ab:52d257e3246a8717:9602df4f595bda5df4fd932daac3ec093fdc743d3b3880e3099324ef3d33b87385ff8fe7b4932044c726645cec05911d0586eda7726ac6b96dd8a7fe19f75b98bd21c926433a2995bbb75aea2c1cd6ad443de37d64fdd54b8711b225813a6efb
|
||||
3e5ea9be9e47d5c03426c6b23851b839462cf2792e62c12ae9057785f0bfaacb:0edc3a78b6d6190566:b7aba102c730342367a96b64920ebe60f749b0d42ed4e91d51c18e31d791142895275fcd39ef09c7a6759d13651bdcc4022f8c93c77a0968a77e46202f8621ae7969e1410ff6cf5ed9ecf6df14905322a3c7dfdcd01afaeb0bf1bdd72545e7b7
|
||||
4bf3c40acac842af8eaffabbe90ef74faaa20030a57e029137a61663db033997:0ae34aa529a1053f8c:b2de94c41c453ff9b60696043f6ed76a3431bc1ab53bbd9e7c89eb482423872a2b59e8546c8fb9b8b37a1c8b76f43e29147b10e64485ba5db48c0012c7a969ba99fa7718b7540252d9e49968b5f41722f454cc4b1785551ad7c7427e49ca4012
|
||||
250d8a3e1d48cb69ae2841db1b6ba57cc0db311bc03570143204f9884bc5324d:0a9e59fb4f4d4ba10ec2:a0e4a43be108ddb82fddef1e59a6e7dd9fc84ad183ce393015d472b596b8bcf84a16b9a54a1b340ea001776bcc8d5e6502624aa4cc16bb3817da98669796dec60fb23c9d0a003cb78aae4c2b690d56e98026fb5a06257e39fe01e6530ed177f9
|
||||
480a573b009ad6847742cf6eb26226401a21fcb00d15605d61d07cb2dd36ce16:a450b2f5e5993ef50a4c:970893411b7a3187dd195dd078cb9d49e64d6fa70adbb2b0e900a82048ecfc2d95dc60cb2cbb066a04c2b036e9d8c952155197e6dad3ea08385f7c5e8ebfcffac5dee95a3ec24a894e9471341f7ec40ed9d51db2985d491fa90b959b952f8be1
|
||||
b17c398c3b5690a5f2d9df93f528879f720a32f5443c455e68c1ee600e1e3c73:0a8b1c86fc50e996c7a251:ae60f93c097e49ed430e9d4f13e67f601431032b980d5604774bf3c179182f8b9fe2bc7510daa71c7cf3cdaafda7748800e7eb19e250c640f54194fb979ea41d8657b545c4f4baa95503ae06848f6fa957b7951b4f38f72654f0a0f664b057f5
|
||||
6f3d3d9ae77d068a241ef8832a9532f77c810f5fb66837401cf9824a77fdfc4a:8ebaad7cb6874e96ac767b:99e80404714e26ac20dc0c1d87b548a08fd427723dd4bf8093eca93c7903657b49c7c8eaf36cc98de460fff8b5dd0055169f9a58b0a249cfb335ebc06318bb1a81ab24fe7df3aa2d299a23298e9b29b98bc5c0d64d349b398fa763398ee9ddfc
|
||||
fa93887ccdb277f5017081f848cf7fec935c112ccce72275c9a7b6b4758a6b54:083d040e50f2fd72f2ea77cf:8ae387dadb7cb41a776e39ba1e205686987f2171e5d9aef3bd2d9806e269a511e67b9cadc6c20d7d6b1f49fe7bac09000cdc5c6e765ed381b12cfde05e3134cbe8b51c26142e424bc26822a235f722332f71b4ed2c89999c05e2890f8411d7f8
|
||||
23bc491c2142f7ddee2e8985a3175857f897c399f697411762554cbeb00de659:91b32862312873e4b7b3cba7:82a5feea0abebef1b71659cbbb51e396d1d6a9bdaf3dbc45790023d6a5b46b20dbbb6c368af1b034c6e6e5f8039d1c28099ddcadf7b4153d4411af1f79277571764467f1d083f5815ad45d265184f431b5c05166e708d55ba049c6882683d331
|
||||
7272daf5146e93e068b6cce38fd07be2c254f6d2dc02c7d078f9e080bdf5408f:089e270bb1a4308c4578e0f6c0:916be2b7d46f009d2439a4c8b83caa6877b100cb0af4170ba0c5837bc92047d84e883295e441b44e0195dbefafcf8892126b27ca5bd2b126b6115b2a53ad8c5c889d3c78807dff9d72bc4e9a064743465ad025c1e2c08fbf99b0101b15264417
|
||||
47655f711cd82a199e9418d4a5baa779d206d939f7f41a9b6ffb9f87d6ceea07:d90c4391d34e0b2b40366115d1:a33b21611a27397b7c02e0891582c1f5023d2cbc69e0643a7c32d7267e6d8042ff8e65839e5093d1a753e97da63ea7d600ff1c5957771f57850c574d167a8ee54cc986096f32ccc40cbc42d9fdd83acbe11f4e4ef9db36b9e41f7aa558f4eed8
|
||||
f056b12559a6358e99a2c6a1d86f267dd162c3012e4788a314e298e7c416444a:0cc73ebec823353215affb7bacd5:ac150f8b9456801a2838d467ea2fea5f02f8a96cdfa4411df3d9fc30721468540f372e70f00607dee69270deedf7e39e0c6af55691fa12fb1300bb68289f351ed1d0d49aa745217c05533ca46db53192c3655a50fd169a41477346228096410d
|
||||
59014aad89bd97fa2489c2b43508c31347ae8437240d8306987e63eca954dc6b:b29de70979d3459bf0f52480634a:94a813a737aab6aa68919278613a28c7a79e6e64b3f925538a498338e7eae29c0f73ab43a2d1f0e9a99d06ceadf57f8d166e8c4295b50269a117aff566ddaa02a5e6d4fd40834a96e3257ee219089e5843c3d1e1fa97f7a6b3119d4a55a0693c
|
||||
079cb55cbf7d8668c79aa7be998336c9e44232f2040c19fe44b28651956888f3:06884140ea3d1aa1afa058dd19602f:add5d4e8f20484f247dbe0ae2705ff8a84f98ee988ee14d0398bf58ff2711c78064c3f9a284089267dfb1ad9828ae6f116f7118e93978f1c8ff92f1cc52c8860b509270a39523ad6473bc48029877a26df0f074f959532a5aafcbb348ad4f712
|
||||
86e665d99c887dc7b90a0c0a1eee2da0f559cf83a89f0b5867bae358a4f85122::8330d06e9c3958920fd81bc143c324a31048d97a4179ceed71af96e02db2b6c2fc78445142d3d7e7b930b38ea511f17509db0bff92a6f58aa332470db5149ed741f091f545cce07decc9028ac4ce1166392717f0919e0a035fde449bdd462084
|
||||
1d56829c94f18e598084ccc64e4d2fff82566d80a0d00446078fe2744ea47c29:04:8cc2f05194bb4fea2eb6c816626d6b17c33de7961c0597a74aadb51585d41fc0b04eea200fa70da10ba6701abe128a5409c374cc6ee11e516c408c11233a54175aba1f9acf49907560da8389745f9c3fe2622be20a68c9bc733d6a5ba63fed2b
|
||||
1e243e4a9db47f767b6c9dc99f211312c3dde97b50c7bed9ad6585d5bbd2be78:2b:a8f31f44ad30aeccb0d801adc3d0d6c850532eb82baeaea38cf3829cffbe9936a4ce0b76bf158ef2e011245c4c9b7f5307c47379f4767efb52366dad98393d67fa56defb7831cd786375558f2c1b4fc76d3338a468859c014c8563d7be49ac97
|
||||
785d7dc139f866d0809a4072cf6545e79e285347321349ee6d38126ccb5f0a33:05dd:ada77e687c122d58da63e7ad6b1612891d99e3d88302c861e1cf1917f48b49bf531c189ac2dae7dae09db6f6b10f0128016cac0d0caa55342ac8229c42e3d2b27913481989146b977eae6b323895ffaed10814a0314891edce5208c835e2b65a
|
||||
fbe9ade713df63e2d1f592c750cca4fe187e974cd1a03c60dc2aee1779d8db2e:e8d1:adea816b1732c2448f5af1679623dc11f3d8aea1ac75a07fc6ad287433b3223e123473f6d8bbdd59a4ad4db7148f3af61753a2b0e8419ea39a5b1018c24b76e6e72622b1b3cc5d05c02efc421560d6c35f3845a7020030f65a580ee0048adf6a
|
||||
d7aca7251337ac082a9e885f862c3e52af0daa44244444f9ad2a290937d46347:0b5cf4:849d9160feb1423868715cfde8a3d35a014cda0d02fa9ebb2ec0604393d9e61a4912e18fcd423af2db277cd2c4e3f8e81617e131f647f4f1165bf93fbc7cde92dd7056e027d21ef9e8f353941ff4726045ef32d4375ba5f43c5b5fa368a1fbb6
|
||||
3fa39be7a361c621ce0e944e01727db44e74baa23c82b4a48d9c0b350c680eb2:1de37a:ae2c347f8718815b992d93c9a8f8db741ec2958ec95123aba339537fdbc63f802c06f6cf41e65f0f6e3e5d567d3cda0207c9223f7144b1bdcc6a575b3e667f3d22724bea3e7bcacf28458e7112c96898d0bf5695159a7bdb5d7799834eba3fb9
|
||||
fc40ab6dece2a42366b1418dd249e808d376b5b63d9c15018ef7285d440d46b3:0edc1d2b:902ad68fe321ec263d0303b10546790399c5def45b7017b9b46a0261f0d3e907b3239eb63ecb9134285fe0b0d51c04210ac5d4b1c095b608223468e57edc61cc57e50385a4c471bb4a5e57d185d8404942ac0f5ef84c8914fffbbf2e9ae24e80
|
||||
3463763af0f0745e8e5ccecc3448d54521ec6360b31cd1f61461baa8d9430d85:81a2b9cf:b86cc9994a7664a88cc678bdc8074ef6fed1fd9d67487aaf51d11fba9c9677a8317bc3c6b93ebcd15faeaee195497a0e19393efb8a53aae96610701b7d827fbc72869c454bc3ed54abd119ce51873262e96c00e378778b7d6d7c44e8afd3da5f
|
||||
7875b2180b7a07743b340a85acc9096fc3b60f3774fc7d12af17761f6472574b:092a75c121:9352ad15bef39e8610dfee782301736b630b2cae357eb56dd16e8c32d2a4927e758f2e78ebe482c4162aad170ee8edbe0f73c90eb7705fb97b5c6c86df6d1928825ceac93e56dce8a61fa495179f14d9ceb794eaf6e1e18314c609ad7fe5348b
|
||||
43635608d29694b3fbe8c491bd2f56aafc95017e99a7c53028fc8aba1a6a655d:cc326cb7df:951d25da783cefe075e22de837614c0f03791a0e7113a6a6f5505a39c199538ccdf58e08a0e4f39acbb9459be5fcb4e30db05466f472c7cafa8cca9d0fa7a50d2644dd71c6c1f212d9fcda2a4038fbba2f16c8acc2af3098dd42257cf1953f6e
|
||||
c831f94ed4e49c83e82c311d298399e56bb64cea460418833a7f17deea072265:05d9bb10f795:98553a642571c558264f1c167e4f11753e7ce32eb5ec00cda23232ab6b1e84d9b2770fe73611604042b2575765ccd6201996223c32fee3fd37eec9012c949cdcbf56e1067f184bc3bbbab3a9143715a546e3f5ca8d91ebebe9bcdb93078ec941
|
||||
5062a749d8f40402f17c8793aa929f5cf33faed38663561a38d590bd26d1bd93:7bd0c2a0cd58:8e666a772334f4a599fc3360efd49c00e0062071dd6aab348acaab791a9faeee4c5156a7771cbf5342d49dca753f312d0e436cdb2d00003d25d41e06ed593d5dfe59f2a231a5fc890c59f6c784be18ec502eefe1fe90c2ad6c2f7f1b3ef561ce
|
||||
785215acd386bda56d7c58d543e0e31bcf5f87488dba7bd46642fceaf26e8d24:0b65e1b2eb4fcb:86621d09e52dd8f4c15d44000274483b93e2f3f9920a1f30e65c84cbdaa8f6e793c1e830a30d209e2c7a84ad14c14aa506291893da4c05fe16710d55c40ebbe2827ef67da4fff0520579f0273be8279a3b4f3d5343df8007d3f6161bf5c73723
|
||||
01064d4e8efdc6631fbfbcbf7389572e82cd5107f47356989b9eeb1626ae277d:1db604a6a3737f:8c8891f0bcd2331d4395a75f47d9206da3553c839054ca8e7e59ee64f991d69b0ed42e43e2c96956b216626a3da15e2403a620650189b389a13f14c39a75c44cda966f7dc69a93e79d6678f0420407d66e7916057ba8e2eeb19d0bebd508058a
|
||||
7e52d56764d5b7bd14d576913a63b04f28e3cfb816b3afc549e1190a2fcd7cd5:003ea498258fac12:8bd52db56c313ac3f0bddf4d11dda4e6d81716e212c9e82fa32ccf0dd0b1e2359e3e12c6e988b17d4f9882a58dac083f15c2ff4f1f8f82047e8ae8e5503d1d84c864163656f7136aa106d627aac5881461433f30230e378dff674ac9c7e31809
|
||||
07247477ab86d4b41a3ad3bced6c08f93bf4dafe52bb88215faebef0de956746:8fdebf997b921d22:b50e9f99992a08959991fcda9654f5462ecc015d3b894c3477752b29a65dc05248c75d7acf152d7cac32ec45df93671d0fc07dc7e0a46ab0f5246951575e290b74ea6483ce7512a106529dc5f342d172ace7f5a72c6cfa9a929b00f616411251
|
||||
49bc2b3d85e073e9d9bca649fe7d77624cd0c4186cc816ec767ee5907be6d4c7:08a6d70240e42b76c2:aa666c6474f8550ab282b480c15355737b1c3c3f496d0924e6dece36d052eb6b8c37adba328759dc6f930c1bf40e866a04cf9306d728be9d00d56115509c74c54a62b0456de48556e728335864f4f99f3835d03e5742219a0ba1fc135976ac0d
|
||||
6e1ee2e476dc25bb44a8ce9a100ba3719b0be25802292723688b6094fa1e47fc:9ab597b9bc1f9f0c04:98bb5a200468cc4a7bfff04f913d87c386a5f42fa67d39c22924b24c985726db18c066b45fd4715fd94c339246cd683312120e2d6abe091f5fd3b0d75c4621fb716e88588e9449db8ea0754a06fb5372000391c781aa9109e0675b210d8a6399
|
||||
912aec300cac053bf9e8dc0173d46c7ca7c5b6ba23bfb18b9fb681b56dd3dbba:00707016258cef3a42d9:88adc406bd477056f5aeb694effb0cfb816675b45e28904fb1e701a3ee87e8f75d49643ac1e3e1b688a6a2acff48d7be12f3374968a86f7451ca35d8a1c0fc835986e78e954b117b6dc12b074c0419fd462f606de08ad153836e83f70f9f6227
|
||||
9544249f393171d85c19e23a203e820974074033151070206931df1f104f44de:5e69f3e560c6e63050de:99c2d4a99976691da1f4bf084dfb86f62d10a48d6316e7fca5b0c1024fa2547f7c02f6d3b775ec4943f88b72779ecc7417c8390b4624435bd7826be5e9b06994224e17640a3a54818deca8bbc11f04fc7c694dcb84e533560978420185389d83
|
||||
51951b1c9e647dd724bbb68c7dd976d9e10f0eae1ac3f6fc1df04446c8f6558b:0d01c99a3ebabb5bb8d241:8a902cb59d6ded491e703682530990074ac486fc8ea5308f8797df59019e429a536f10a2acb5b9906d3a26bbf38c6bdc005bbf81104edd3b384b46553d6b66b2db8568c00e74037ce07eb9fca764850e9921d412e0287d9ed2a1bb7c3660d7fd
|
||||
106b77fe23dda6e79c770e3e7d8d0dc5f01f6a21859f1ea2e37211b7e3e9d20c:9778045c939a60ba245568:84586f8b09fd69b3a38d317e89d3f3f5885bc6aac18c9a3ed5d0801df91f07c95bd77db9864ea986ceb9a4bd19fb4ea419ec7c46c35e477251fa47c05d7b0a6175e3ccfbb24196aa3b2b2cb48ddf9c8c0882a6fb32707a83d0260980a9b5528d
|
||||
cb71f393c33749f189741023f86905a494811e69753ec416c4a0572167e8a8f8:062c15949918898fae30d13a:b9a2b6288eff5db9ccc9dee7c243d13d7331d6a146bd62b5f1be093c9e077554d9a414a4ac53372348717750543ab2a90151bd7fa36fd84132bb2c80dbf45ff37993656d91914d10ecfe87e4b664f5a569c440ec0be6dc98bb27ee78fbbfc103
|
||||
24182d5dcebb1f234add96901337c0b173a5dfc0735ab8476a44e8d1497eee11:2a590d539dbcd9cdb8dca9af:8b10c6a7efdf6afbbf600543bde5d43ca322a83e6b085e5dc08c0eb9b2cec92a69776e1dbc64de47a4d2f4d091426d79182531706ca25ef3ec81306da03edf70161bddc53df3bfd1447530da7ae1b94344cab0d829060abf14827a87568c114a
|
||||
29f81c0bdcb8098de92efc9aca45df19b581174bdddfa7f9dd4eafa217f46231:0c413b91f979ef7e3e3d0c2a43:8dcc40e3267a209ecdb869a11199a018acd9bc872a2738296a46769ba1f3d5f6ebb75471b2c063dcd3ee89ee34eb032918d78468608500ec66c6496c28d3f56f1baf87a4097de60617c0938895a55f10d54c362ed86b3899edf48fef00f0607e
|
||||
e44651d84d62555d5a6f5b432b51c4fd8440803bae8b0d477bd366b12a61dbd7:40c7087d70ab2ead5992c3dbbb:9750f6d6d5a53cf4ffe61047c9d9d2bf9f3bc6a727093b5d648b3abebe6c496bc30b1af8b8abf21d678a0054c03efd85105c36fe1cc61da2b5b18f6b71623db10bf93dca5bdc39c53b33e5cec5988da228218767a30bbb7d0d80abae1782f9b5
|
||||
bc16d0b8102bce82811b6d56ff13dffbe690577e70a36ad607d48ce77592432c:0ba94d4fe8655980c0caa90e3309:ac55fd7f2635a0ba937eef6176741b21533fea6d8a8b5dc3678f4269ba1e1e06d9e3824a226f7b130546c048db9eedda15bfc486d9ea7a69f4eb04d515627d839bf3fbb6cd252f2adad65c127eacd362ce7b842cc2052fad14d8e6c3299230ff
|
||||
2a9c0a14191b6c8d9da54c2e7323197fc5972d6c2113b8d3f4aba8eb2419f402:80d1192fbe8c7edd9ae58a5fa715:84b02c4f728778a29ce1273b6e77a393941a87173764e4e10593faffca0a04b97e444b1f512e6f92de1fc9096ce991a90f389e78e5745f64c5b589a331aa9e157a71a993c96917024e67f13c97ba2e070fe1e3b2db91e0b95882f1f3d64727ef
|
||||
344bb9ed7b08f405ac402c210d6ce9a251700f8f39d9c13e72921f993b0fa657:078698bed3b7f59cf6041cc5bbc27f:a0946c200a29a99ad8d348d3ec627c0944cb42a6ae105f18815ac85798d656b33acacdbb954adbbd65ba6bf031896e1c016a0dc7b667ec3662e54b8951d93a3e03a2c8bf3c3b88af29c02e5e3bc6d221166902d98a94842d855ec65506ad6911
|
||||
a5b50c63c3061e2e61ce3df7c5d2b67f666e3d0ed10529a9f2cbc475f836c4e3::99990d0c901c913e8385a4ac7a4d5808ab07e68cda6c891af55ef2e69afe6ca8310b9de5c1525dfe86c94e33c51f40541518a6065f6fb036c4804fa68e1c2f5d5fcdd5d1ff4d9f41d0f814baba6b2a00eea0ccb4d7d4ca6c3a87cc811efb60ec
|
||||
41e6f409f3a9ec0d1b53ec441b6302e388941b23005ac48c750924bffc28c95d:09:8002a5f881006ea6f1c6f318e7529b1c8d9d1cb7cc93d75fce7d14286f125e03c47090f9e2bb89350ab85ead94e172230cdf1e9d76265579cee29c487e8a15884d6ff78646e634d01f7d66a9e155bde7908657f655aa711ec53fff05d31a3d55
|
||||
c3eee1c7204b843714622abc2d7ac09747e8e95e0ee2e9217230cd97934bd46e:8a:b6eebbfa763dd3729a18836f1a8177ed6ca43cde55410880c23a0eb26363a1f807e02605f367899f361fe1a5bd6ea6110ec26a58fef050249a27e5d929262b12370c2fd8a7ae35abe35f8381a0c56c07a0774bd5dbda0293b165c6e9eb4a1a3c
|
||||
ca9f5cb4bbed875f6fda371f86d523b577622cf57c6df9aaf7b5a0a0ee68846d:0831:8971b2289e864164a53b6901e14c3faa35e1c6a68bd5eb81a8cc897bc8d17c0383ad617519b4dfadf5ba43a69c56077608ab6ff3508bd0d71f9738a53be7a907fb9f9f1bc41ff8b370e900fd70ae63da49e2186899631a8220023aab2f4ba1c2
|
||||
01ef1ede58b87f0f257a1cd96d71c1eb98fac782ee687a639833fc73cb64df93:79c2:afcea670ffff566a171fabc5892ce1c974aaecca3d86a968c479f9689ef37b7980614a837207e53c2f7ec0701daf17bc13ed6e80f50853a606c8d6d690cb3778094c161ad162666f46c2f76d315f24b8ff90cc57c1e57e15bbc27aa94d41049d
|
||||
e982fcad4de4d9e09a025864b9c254b4932c6b65441383e93509f48deec1b742:04a8e1:b1689c1450faa975c63fc9b23306020c89f9b855cda3d40a99c9f591e2c05ef2e8bb0983d9989193e52c4c58716bc13e0c0908f73922b0dafaaef2ebe7209c53efda40ef23ab6534c335b504288fcbecc3cd6ecb370cc55d1faf350cd9a50129
|
||||
1bb7932556e42f4f4fe9ba0f59ff17bb2245fd79203f78e8bc3de488fee40f11:8d0ef3:97dea1e05ef0d1af9289e64866c91e7b8fe6275aa87f6f70c3a9f16b411692332a0d1e86666799faa91eada62e23ba6705a69a55de44696b2d2de12bb8bafe1529217c87bfac57bac8d866b09aa208ca8ae62156568fb7172ceb0f411a171299
|
||||
6730284b18c2d383fedf4e0c32480b06e9d05b380530e7acb9bb055400fa33ee:05866e46:a81e828917d5e25475db2e23e35f36c069150f5ebd846061f00bfcf270d3124b42103b48f915bd73236fff0f88afab460508c356998e8951806ca54967f26eba9ac1e68ce22da4119b1b3ef4721bc286a0455048a7542df4af460e1b53d9c351
|
||||
1b538e8f7f079ccd4a564273a913e96a54c654a0c028e39d2e9597c9ff2a9813:c20027c1:b0636af64caa68aa331a61d5b6d6f826ee3a4838a6d1ec9693559227e80737b244829f72976c7908948e633040f0a47b073979d05bdbeebad1c49acaab6aec7a1dfbc45554bc102e0e88e924f49bb7ab492f7e1011da3814ea05071bb722c8eb
|
||||
4adb9229610309100770fca18fd5e6e707e7f27f4b911ec0f53a6114a906230e:02c7973ed4:94e16548d624b5493cb0449579376d234c2ae597b347d681f394ef3e55fb3279bd361d83e3194b32676c1210d616db8204064b33a8f0d67a21859db0689da9447b3fce39972996967e6a85a0839f9a0253bb63265ad751661e035c8326ad1fff
|
||||
c8eb027495c259329813356f1b9138f98bfa589b0f6aef6f9d57523b96e10863:2be4521856:af7790ac25ba5a6c789e81559619c89f9d2b48015add999c73efe4d2d87cf809e82874628672de403c7271f51b302a611773b67961db04d5328729b499e610aff65a8aacefd0eea0dd431c80abc242b409a65eadbdc1e8d63a4489e7d468c2d6
|
||||
fd5922ade6bb2ed28516a13ce17198222809c55ee9730a3d4a3172228a1740ad:02dfcd963c93:b1713a07caf39bc408abfa53b235ef610692f7990ebc61f28af2369ad690f20ad73562374f3101a9893401c22252e3c20bd5baefbe97f15afc68b411125cb0fb0a22b00f8f262964e07a4521ae0baf3cca7772334b180499827cbfd243d8faad
|
||||
0fc195c56cd4f54fc81f4f78a4abc2ba834b5b0018bfc150a64e7c6a5823933c:ed0afd7c4881:a40286e1cc833d2adde1d0640abbcc2199b2767b27ef540420dc96f12deb14af17eb655cd2e958a52a68ba2bed2e3e160344101b101eacbc33f3c5277fb1ee6bf6d9b866b5b8f5794f55a5d7236d3ca0de4356bc1e8cac30a2cb87f94eff4817
|
||||
64813900e765c40ab4e7eb9522e2c21c1c30756fd5fbe40489441f11c6908ed4:021256b10a08ae:a01a1eed626ffdb57dcfaf3123c4ebcf03eb441f287bf36f2209f4d36ea874ff42c3bf8f90a7fa164f0d133692e0cee50ae42593571b8b876152fd37ae62af446ac1b9f2aacd9e335e67e7735ec92e3b5aa85fc4b97379a08dd2c8cad23cbf30
|
||||
4a06c487852299ff3e247514706ef7aa6da0d2c1a1f7176b3317cc095d01a51e:5a04c28464f32d:b8b7111ca66dec968135c1b9716a3d162ad946f8e24d7587b27c0db7a39c70bf7d97c1d968691c569f913ae5255c3bb1120cf1f87fff3014a37d5f8f79a5ef9f9d1f86bbbc6c5b3662d1fa3de2fbdb3c20d0960671cd6dbc1336f16f3222f399
|
||||
e9d0db97e7573a44825b69d0fed6fa0a9587b947604d9740e44c1d59af929db0:083cf7dcc2603ac4:8b38447c730130d59c991a9e64601523d69ef43b05dd019dddf10e2fcf48a6de7574da216c9fd27013764c86a266c22219099e3f2b88adeeb528b4ce995c846288c2a368f78b40517f969e8f44fc42ae8ca8144e093b767e87abbe2f6d093e8c
|
||||
19ae9daeb0826f765cd2fee0f6678724da5cc4f8ad39a339fbeca86754f8b19e:39c99c18e1c62745:8b04d6db47c8acea29079dee12e9e31e435930bbac9ecd2dfb425898829b629ce3264661e16e6993be9540aa52828061079055fa69ce690fbfde7befc621147a02b94c86e43f2d7066e986f7f6857ac521e005e08c70a8558a19d549067bbe68
|
||||
c913eb03046a76eff671a21d6832ef1ce54845df87964c133b9cc9cdf0bde2ec:0dfe207168723748ad:b60d540d859105b3aff4a383ca545e0c5d9c1d799ec24986406de3079694d4e46dd0caf90b7543dc8e01c346f7331e5f15f0174b7c03dc164c76040b9c64e0771fb7724567dc93d3c686cfce08b34ce351f7e88cdd57101679600a27adb24c1b
|
||||
0a39e99b16a69b706fce5095dd0e7fe85b4dc521702e3bc99a6fa919415c73a5:619b95b11e4f8c670e:ae67f2f1f4d8134691100ad694bba172b1997f4d1888bffe4e0386382f09bfbcd56520a7a3647d2cf3565c7f8fd47d1808d7ea678acc1c17e272484296378fcf44f1719af50db7ff6ea3d64d5435e33bc20658b4fd6294ce6103138be0d74401
|
||||
d1094f56040bff0aac7f1e86ff4c599b61b7d2a1a190498920b00defa717511b:085bdf67568eaad8e6c1:982c5233aee8dbadc89f13e8190d3cbc630103edbf845320ff56eb1479334f4564bea9025d89a32536ac300e06d19809127283b6f9dab6e196f08fe9e32bf4456441fea4eda70e8b4bdf1aa1306ace1c3731432bc17ed0e1a3c29ea36724a506
|
||||
26828c6e8108d8e5965e8b91fc59b95dea696580517437c42c27bdeb650d3cc8:65267bef23e56c1361b4:843e467dc7ba66fa39dc0daf8cdbaaba07dc8c38493779320f8b7d148f9554e1a4eebfa8589f3fa403ce1fef29f649f00692a0342041527416e476ad36181eddda07556639f921c1d09b4f53767c9436c1b69925a3b391475580796bf449a960
|
||||
b70b1e21a37aa421a9f522ca818787ac9f13f9362c9b481f0394e4619c0beb54:0c3639a554e91d04d73f2d:9209ca4bd6a9e95853d16dcb42b8436730d4ee39b32dfc1f40cddbafb2f737ac89d19bb75b51c1c473560d6339b4e02f089bf22094a5870e37f6df236a72eeb6daae4b8d8fc56d53c2825e392306bd8d8ad1bb1d2198332a6ec0dd7121743549
|
||||
28cae903780d94f5a17c6f07cc27c0c55d8860decf2f923e1bac274533f04509:ebdabd1cb56e3150770e59:85075ccf9926542cf2f6e13463f4ac970cbb8d65711f84911c31fd0639a009102745cb328ce660cbd9b63b2acf87fa2b0e65f7416691f14bbba4b4e8e5a46b35435a2290e43022bb799bae0c697891ea4a0ae90022d7efcf192ba2ff730b9183
|
||||
9b2bdd74952263a3ada2b584d98bd92515897cd1708f1cb2061a71f567235228:0c89a5a5c90178ea4669327a:9952900b3581d96024159ec648da19985318dd89bc2917119b09e1f9b8619ed2078f58239ce74a9c13bb5027f1566ed30ec6ffcb65aa6c0e3a85581702e91a4fe360f8fa98db4ac513c8e53a8143c5d43f1780de901cbd03a8754d577d0d3bab
|
||||
e138142f55e120ea4bfceefa69fdd4b44f2e746120d2ac2071c78c003a6158bb:21fec9adfc423f5b7ec4ce42:84253268af8162cbab69b7e67e8510dfdae04478d2606500e2775d546c637542a50ecf5a2fdb6f567dedf7bc7831ae5a0f6b19f11fd2d1277628e7a9ac3ff9bd093891b844229ee245f606931e47e6c62c1b8a664f790d2cf708ec6863ca54a1
|
||||
e6f3222f95df5c99700e01e5458eb013f47cb0d9c25c5b882738ed1bad7891ae:0843c07218e88e9054dd0c0bcf:a8a7ff07c12969275cf9f554fbc0eb40d21dc03a106292f0862e1462b72f394fe9dda61a2bba28ffc7cf8bd0c56c505112a43474029ff8e2c6cfefcfed0680ebc87499b9ee11ba5474e71f7f6b2472531023b0cb1770aee6238941196ab56255
|
||||
43b47b97088f55c15c22418b81078b9678c7539d5b6ef30b3dceeb248fd7d610:e0f4c7d85159379b8d4dfa2ed3:8f0e1fcaa287321526d7f7974f68236187022c8ffe12e3dca46423745a4796292224f4e875824df2f2d5944dd547c80817e4bf1c4bb5f98dc2b17b2367bf048bedae6c0827be5f2128949fffa9787e4f9b2aaac78f19128ced220cb1cdce9376
|
||||
77c94ea4197d049d0b6ed62005679b2af50998d2c44d8be8f7b1652a3ac934b2:046a16c08199f8b28cb69fa7b095:a4a493b13b268772f674650dbf359c6a73bf664ff68ab288c650f0d4b4140b917c726b879bea1b50ffac35868a3aa268154ae245c5159620e727ec6e4b7eb8617331e35394ea26f0aa1dfcff87f0840f8976c5e95641df951d66b43d02d31f1c
|
||||
365b875da7ee5cf2a37716770bcd2a49012b03b5bbe828026ba4a17a999b2b1f:d5cdf315f32aa5e54d3b993e15ef:a0dc1746699105974d79a75f5a051bca821dc341d5e2921ea45ace70828d87ee672c293854fc18921f56ea6edf067e8209c2692b9e1365483d4dc9c6e70e1092a5b678318778975aa44e51a6c273283d66348915a9b1f66230dfc1a4f41b0423
|
||||
793c2fff43bdf604941341dc4d2d6d7e4c64d3f3d5b21efd8736b025fd657f5f:042704c4811785813258ae148ee2cb:b69e8bd1a4e57514ddb3722b3e4b43ac57cc71cabf6bdda81234f8d82ce1a371375457ba812ca6b22d0db5991eca2f5302184128fa38e67f53868d09cc4e76209a6c71e9b9341dc72312daa1ed826c74252111c3f100eb7209c8a6ed0e82495e
|
||||
4ae51471cb64596dddd4ba384f6914c62e635fe7fe2b48bfed3560b9853c9c5f::80d9dfb35f77f8087eb5bb5bb7b73b3361c7547aa9ce1e640052b348b61f113d22be6344300458369b64d5bc16bbdad20c13c1c0a7310fdac807c9660c5ce709a837d487d0bcc69d247b5973e470d982cafe077ab1a5244fe7a21fe99af4fdce
|
||||
24e5956ce7e1c26764a1212f5b70c8c635f53313eb8ffa24bd4b22178a70400e:0e:a1bade1469fd0949d6916cf467a67ad56245a9c234a9fb1da011489f5819a01da10b40983151650a3baff01d4931f117084e15225f8b2143196f8c55323e5e1dbcaed2c9339083504b97dfa31d01fe5b320660ac4b348867d97aa5bfae4708c3
|
||||
f1c2156e0f353a0e05564f455af66d94dae7e425ab5e9950362bb910aeaa6f05:5b:8c0b7235ba1291e802a58a4cb855215cb24cac84211bbd1b3d0a92a1553aaf360faf5b7d4f8e7bfe3c1866a4b44eda0d0900f3ec49b751ea68d1bf0817afaf15a0db7c25227d8946194df6c3cb2a2b10c2960d57df9e343b84c6c6df579695d6
|
||||
1d25ef5627abdf5331c07eb922d70e8c141a952a16aaa63602a664e30e193964:02a8:80970283042e91719fce9271122ae72d780709ae2bfe8476f37abf6384e4c1d741585f0679010a15f18aa87e132bd120030618a443a25096f1beca1958c93f28696f2e9938242f7f83d6287eae5e3f1093c63bd3c8c9841ab9a3f9d6afddbae6
|
||||
2f5307f7723b2702ef203499e5f01314f62ffbd448c859debab940d0d13acddf:9b71:83c20f7dfd803feab7b92e4619d3fcdeedd617056d3f4a863d2aa8c762ae2f0e9d10fb639fb1cb24cf775da646c887cd170e169aa3f99665227a8be8b74f37eb64c45d431afbb453a10c1059dec817550eca3ac5798fe97b6fcac44b05c3f39e
|
||||
4fc8dced9af2d9b5839fc71f2242f29b210a75012360b76fb73e7a0f67236ab8:0518a3:8282811085e6a7532edb00ce977b1cc0ca00c5c482d572764350f0e15e5ecd745bc0ba1f124906b72f10e704d7ad091805b1990e4f5749526bb3f33f3e145ff3a364bb0850efe302dd3f1de79e77df272b27c8a33d1bd1bc9dd594590949f92a
|
||||
d4b047a745d29f10977f7f85f2335e9e6cdbc9419e7f7c54f389eccba9222a8a:093d97:adf10bc3bf7673880aa95b623f4ac4e1eea1b4ab8d603823f88f5702979f251866511dd70df1e9214095cf281de5e6af18806fbed85d4286cabf4a2ea5f03af3666a25efef653f758c67a7e8ebeb312f22f43dd6f2dd7b50dd2c37b4202eeb4a
|
||||
110f155f50537bd283dcf6a0b32d76e33369875d25522912c8ac5c5999d15c92:060ce567:b6344848642b6fd3b15d5cfe8f58ba33803bdaf8d2499fb6cfebba69932fed5a25cebc0873cf7cae39ad0317e2cd62de069eb4563cf7efc33a644eea6bc6f3788805bdf3ca2371814854223fe970c39d66d7216da597865c7371ff3b3af6c7fa
|
||||
7222141d6c07c9311bd3ed536a0a9038844c025fe2241ba3e172a74c5dd055a6:a6623761:b1ddbf83dc85a9f597a16cded7074f8aec09c5260c7d4d896fcc5b9857c81c77a9f48339f65e916c5e464b02ee4275521072edadb67d97bfa0e01250e5236f90cddc5afd7c4731ea9a4cd97c54c229c4a1f0793f62d124abc6801c392bd84394
|
||||
80a54ce355ee0c2920098a860731eb59bd33962bbd8bd3978bfafab8fe206d21:0cd2734ce7:810dd97c12e3cebdb8a338515e592371a29c6f6370a668752994c1ede906852137f888e7fbfe03225185f54e987c14f3046e0b61c444097c24ba199117bffa055c0108e255fabe99d1076d96bb8897cb9f4edf03cca655b45631082b0111dbeb
|
||||
8bd743963ade02ea63fc37a7a733c39999e7d1b11b2318557ac0a135035d08fb:8eadfc5e31:997002df650fce37af6f759312169a6cbf015b17a3065f29cb181a327f6a56e5614667cb84da7e82b7c49a62fb587a990c892549fae72e5463e0eac0835b2bb82abfb485d90ac66a0ab4be07c10ad35eb9e63a83ff02e9f3292e2350880fc168
|
||||
f3b983c9ba5a887173e0f65b9c96b48ca00c24c95357af87e1eb94718c14a3e1:0d8386794920:a04c1eb2dc027397e5e5c139e3427637ed78d78065a6e498a2fb5510a0d98fa760f0fecc8190f14238e2cb5987b84cbe12b1adadb8fe5e5f6880419d6544b0b0f9a167c3e4134cba975f9bef6b73719d7e462b215172ac2ebde69784455f7226
|
||||
39643613cc7ca678aea24c2350bdfef54da2740dc84bea3eb0f5086d8f3757ae:ff5a601d9ae5:93694096221d3ce0fbb2ae96d606102bafccf7f4b286a44cba04dd41ac7ac52d2fd20a87f0757313d2b9cf16eba1ae68176845a18688852eb7fca668232088e707cd9367c7638ff586240b3012602f114740b8f23142fe5eb4d1c92a0aa3cdb7
|
||||
896f12deccc815f37571bd16a9875d191b4065446bb7a41e81363783d22d7bdc:0977015917af38:b668c89c8ef9e1a9e66770b569693c30f0baacac1a51d5bd50e79eb2217b3e676d4543a2e427c1ea6a706aaae32a0b090bfef45a18612fe33a531b04cfb91a9a835e5156d88f25b6fb739b09840fe02838a5fdf4ccdf5c0dd461f63823c3b5a1
|
||||
2f48137f2c80335ceedb56aea36d1d37630ceb603d25dc38c5aaad9edd6740f2:c596c4507b8c21:8b29ed205d9763ebe6f59787bd54490ccdeab56855382ab985cf6dcf02d3ab01b3074b858d2493917b156a9caa7dc0d513f3616b61f279410580ac6286175a8326ead7fb8b9319d3063da2d94dcc9331f85c9ac82d84ad1a3c0c14c00cbaf106
|
||||
5e104f67360bd7be86562856628f7ca8d6f6090e58d78d1bad5faf7b6d01ba96:0ddd0c7f788fa3c2:8abbf1c71d4b4d62046e7557f34ddefcf0ddf8b7991ae8674fa3d73de1865065e98f914fa49772348b4665f102f2ca1515d21364050ab724a0ae95002ac7f2bdfbbe7d7ec1bb8f6b343c4409ad993b39b0d2ebd4a1a001909a1570760d29c246
|
||||
f41439fd6f3c477089e6e6ea9a1e91e11ce62998db83b07ad2e0f114f9acdcbe:662c4514041b22a7:a23e7d4af64aee50d4bea6adb1ed6797e26613753c8dcf7d4aff5136ac8bab0be75dbe3d60d95f8639914dfc73fe620419a6288d6920d3bf95b86537acd12aac5be76804edd064e2a55af43ec73e42b4aefd10f01b64867f14bd452e34ce03dd
|
||||
2301e3208dc15a69334232fa6249a28238e7eebcbabca532719b50dcbb6eab16:03c1ad665dea01bfa5:820ef925b76285789c449fece5f0765661cf3ce95ef7b6482c6c35151e61f5be1f7f08a25cc647d1e2539e5e4811fe771829d666c84f6db43f351c41252e3fa27432a399af25272dade4b6d1da95aec40d6b83a5b82dee79b7ea3568e8a8dd6c
|
||||
a812cc9efa293804c8dd535d4d8c6ad424cf0b82855b58ea5dbc9dcc432aa311:3249c17e4e56f7b787:a1fb4d9bf530927b578d8317f7045c5531d03e9dcffb3cf4467953119f8d96c4c6fd094ad34b742daee909ed59e7f9340eacf47ee9cf353cc63f18748f86fcf237a893543acf9fa96ebdbeca5ef189a4e8f780c1c402017aa0ae9e0fec11c0a2
|
||||
d9a329774f9b39f814fde151cc32bf74e8a1cff727f83646f55bf001d2a6f603:055cdabebdb7c39371a4:b81b341a8a1f8ea7bfa3ae218f74eebf251526b9d2243247dd9b097521deab083ffe10a877f349a890d74a82c78240cb0929fdedd37e4eee257688adbbd921f7e3d30d32daa6924afc8172988490829a022a769517cca57feaab140f3ed91458
|
||||
5a0155ddda1e372e176dd4841998d8373a4db70f720e13f8191a323dae9151f8:ed74bc9283fe9f0df46a:97bf50cf9ce75ddb14e7fe58fbe911aba7f2ff24c5982ac33803ffc578aa7f956c2cca4d436ee7cc0f66c3378470251a05b6570964abbdfaee44f9761581b89468833d000ff8f4f6432c5bbb7f084757328ce3501c055d9b8d1a44a797e7b93b
|
||||
1620bf3cc64af7a12e3bf1a65859c37c0deceabc9765295f24294e5e408ad93f:0723c6a62f4e556ad97947:8abf62b2bf3d9c3524cb6639f3c94cb4e215aaead42ac8608d0a8c1495f766d7626299bed2eb309e3b3566d76ca87de7168bb651726d0427f6395ff81bea526afe56614486444fd1fdf01a2f836e35fd7dc740be034b02a2ccf585bb490e6f07
|
||||
25eaab4800d7b399f81ed647c1c2062db996f9edaeba7411c9b1e47936963816:51410ca4fbc1eef3fe15e8:8d0e31286039c789bc46dc89914a3a7afb47bf3c1b6e3e06b618e5310f8efbbd43f484bd182b1a25d4977fb4d6965e670db5b4fbb9be287d8f3a5596e118c43032d199f93fcc8a60546d45c4b0745b4fd8432534175e9a620782bbf5c72b6c79
|
||||
872cdcc462f7c537801beda2a5ab2490f2ccdecd0c075d05a7562b718d88bd76:046b1e0da58edc394def89fd:86f901ad63d6e46fcf31e6933b76300d355a23ff08710f34b8d20eb8ed7e1ab3a1a2536d10d4f8b307b36041de380769185a831dbd6bfb44cd40da1d7771379c9ad8fac09ccbc2ac67a92cfbed25848cc4aa32511b1c2739434c0c6599398418
|
||||
ac8fbd5b0a2a1d7a18279a4a77781f1176125de642bb3532db38fd1b9029caab:4e99b2a8d76e3a8599f34e0f:a30b77a191250947cd5653b09b21c0766aab216ba514e78027ce01e428211cd5c3c4268a546a51e0953e0ab1a278fcdf0f19285b85dc15b479e47ead63398d87272545c1928f0d62a6d7a7ffa507ca5a6a3738ffa341556aeaffd6a94268a2d4
|
||||
838dcf72e258d2a77bd369353baa2209c1c2f40812803bc74c7bd1c696260ae0:0155257765b6d64a092ca412d9:98c0da445f6f0e4b03d6a3c956ef55c9c77bd7828c84085a4c776b88963d2d190f05ba5834697bfcddd734850c55c99d07c932d98fa4cecff22c887d19837cb6a67122e96f2f58103c5baabe21da9d03efcdb7d91d58e1c7b6d5bf231b6628eb
|
||||
1c37c9dfbce948e9b854a16e5705d39d6814c99db8cd4d21bd17e4b489be8ec0:f29274076eb714c9a1e0cc7c4a:90436ee33fc91061cfeeec29ee065bbf0fb1504c588a057bc7a47e6d706826a03eb75916b5a0802e6bccd600e479579709af6bf0e69892f863cea2d17c7cee0b4aa0d01ad38a100c1f0a92a0d9bb8a662a4802e68fc61a6f89ae5a967e2ade24
|
||||
d588173ab8a6de5b4026218d4860ce9f7796dd05a6333caab951c1ee113f4418:00d7ea9c8a165b4dfd71242abc84:b3bbac928f55a5bc88d9fcfa166d3d5445efb48d5fb564d6fdb13aab32a443764994913dabdcf103c51999ee1abdf011117d77221165cfa0455c56a690a99aad5ecf0429c09fbf6d18facf5762cb1d2c759e2cca4fb1db3e23a54cb85d554750
|
||||
9044a9f62013273404561e373509bb12950260013cfaaf455036174611c62d4b:feb386e695739cd3a97061627019:b9f61eba10d0876ee182a899a5e09b78e1db4436960ceb6a9c7e56a133b1adbd3c46711d0e50f854b7839f07984b19a800ba90bdc779327e045ceba39caf33c6ddff31ed7a2b59de65b1361bf787ff05a7e09b706699d150a6e4225a9a3e0cb0
|
||||
78558ec1b78bada4d8ce4b1816eca078e19f96891b1198418c36306db8d013a0:0287a3353e386d8fa1a6477424dcf1:84df1942271b5488ccf46f85ea23391a54734fb778dcdc26b0c4472290496c02f82e28e28157ffc742006fd5f8cad89d16303b802ee4c0ec6b52a998c80fa848c90e7ed5f89349bfd8899ce0f4856f956db7f9e34f7b28b6374dc3e6d5426c95
|
||||
e491b15ebad91fb2e487850ad754edbe4e9542a699acd3d5dd186ddb65c57844::a019419fde4a1436a79fcdf62919000cde8cdfc1c3c827edb03b2224fd66951bdc168f74a02231ad3cc6fe821616b5b409e2dad609dc6175287d355a56dc8cbf34811a3cb89242c451ed69dc451fe8f32784fc1e84d078733eb3e43abc916f9c
|
||||
4e6237d14a3cd9ea629413caa7c05627220e7f4e87ad60fa17a35916e111d5b3:0b:ad095916915d21a753c5ed1036f8ce695bc763c753dacf18fc696a691cb2d9d4d305255f0371ed05459225b52f45a9ce102fced5a32e0776ef97b2f047c73833a90196bf6e7cfc424dc3b9538e4a49a41c584c093be2851d08f3ef7118e4a71c
|
||||
0d1bf48acabe43f47aa49c94e212dba63a3d375a788df8905cfb624c6e7a2f0b:ea:96c1a767ef41d3b3ea6fb01cc5c76951fc28189a8e34ebd0a5dadc330242ecac0fad824f4c6ed4bda3bcfda260b8e2b40b4a9f7c1a27976edb4a92e4cbdd4316dbf58bfe8e3224324a9a5c1e5e1fcadf4550f6c95ff446c1242e94b8cd84cae3
|
||||
2e5cd6061fa48f1596cd81dbe4e689696a8cd79f70194d18d1992404244a1395:0388:83ed89345a2f35aabffbfe3e386351928087554e65d611365620360707e0d7d772379afe3565f5d40ec75977f3a24edc1172fbe9e78a8392934b67934c81729cd3de2cefda2ae13a749d20e89492091b71d56b23e113a5caa28a6392c56f6a35
|
||||
89c35a9d863242f0766d20141a09432393f7dc85f9976f98983d66a45c21ed72:8c93:98395ae57556a60221b79b810f32e29768d48fe074b4cf7814e64ee18dae7c43fb7d457fa9db3c0d9e89f57586a407d6050c66c01c68d7061d487ac94047f351d6bf17fcfbcdec2362b035130b9b2bc93a6b293465d21c57d46d8aa72b6e6d3f
|
||||
ffe35fd644e7468e3385c959e280a1f7a17472a54ffd6d7c56277ca5f3f20eef:09edc2:909162428d6558bae348f8dd487794b4efa2bfa4600ad02d36caeecfb7d7b803f108e4a0f7b5f02d8785f45be26325a60091bc6912b98209418e0f9f6468361f6b37fb27c139050e0b993ea5bc31d57163969dc12b355c04e0decde0c51a2ef1
|
||||
9e3d51608a4a15a906873dd2b6582a6b313b1fc9d50c9f393813578b3a05dccd:eeeb52:96aab69f0e209470e58393d2101598c37b5b42282c8b5ec7cac7e49e2cd1c29c07a733c3b5cf839b20d71a2d589ce9f50ac2e34d06bff422aa23b5e5c6802edd8d1bfe59b4ffe005b826c082e4a6840c5251e53840acf1dfd0edea4d004f7d2a
|
||||
cb8c06b21c49cc95e1156ffbd2dd883ed907dac4b7eb42ac550062b5d71bc91b:088fe06a:aff958bed7a12d800f6fb300f47345697d9e6a0c1b2d95de6e187995865ea9ba0fdf72c19c92a6af6075be2f99c0e51c125d65fadab50af20acf9700f1c841af100aa8778a90f5410cfc06c4ae90ee2f08b91093ad72d274fd16ec3eb89dcd56
|
||||
c0dc13f11da239c34822ed610dafe71c5b0e4155e2793eed6d9c2d52d2ddacf6:d8e4e21e:a933a08cd8a869b373494a2c5c90a49faa8bde9f305416aafb3420d99babbbb5489fe184c76421d22c1956dc09b626da054e0a1bc4aea27a8f71740f4cd1eccf0c0bcf14ef0ba5504359be4dfd2315e8cc4c1050c084bd9de97a1a21f4ae44a6
|
||||
ef9cd845cdc661c88fb7de72bda8aaa98eedc17bd28fe7ea83d3cd5c7886b50c:0f56e8baf3:8e5c457a6e9a8e4bca3a055b0293f261ce05caa99364ebce72146caf286f945845eb0ccf0466cbdbb27b1603a259d08e0668dca7a32ad3c676822c7751cfc0848be1d6872cc1eb3cd2e4b420d5d5272222f6031b0c9270f230ebec20c3c7fe0b
|
||||
9d4df22f7b6fb59ab64767c21736b94dedd52cad168248e03b727a59bdcb0f20:d382c5149b:8ae1fd65d241b44a15c0c12bd3851d68ea160c775b6357bc147db64dbe665bd9f5eda00cb9cd6ec0ba3b19d5fa940062002bfc51c1fd69bf50c79d5cefc07f722635b3694ab327892d0c0824e3a36d81acb26e509c8761038cf1399380e29895
|
||||
584f578299fdedd0311bab4134250c8b3ef4580aeaffecb540a3050ab09bca6d:047eefc2ff88:8b67172d81f3ddd80ec9ef70a36629546639638086d97269bcf8d1d28c15a33dfd6ec0fda6a06277b0d9ed68537e92050d184ffd30e108e28e33c3d3c21e31f537f6eaa58652ff4c91326cc8faad3d3a705b8a66d4b4287fdb17db3caa0e1d9e
|
||||
cbaa74ed39b54e6a193da8992e923730e01893316da2fdd6c8d413b84607f793:d08941a7c302:98941b687685713412f11c82a2ca3f86189de56352e88e5f2888c7b323cfd782beeb6e5e75ed705576a90386411a2e3e125c257a00bcf698104695607a2394085bfc4aaec1f48bd4709be442dbfe1d2ed2d32e35cdd1b4759757d49cb1c7e387
|
||||
368f0355b86d025ed847ebba2b7fba334ba98eb7d3e1dd7a4225d550434fb67a:0b64f53253494b:a96613ec8e861672d7f25356e438682cbdd2fda9f1cf0e64662b770910203b25dc886fb02474b8c9baef1e91d9bdb6ad0402c5702d1aaf4c640679e7a9c54fad7d0069226cc74da8bfbac94bc4397cc1f1c9c39f5e2d9e2d7fe40145b2905aa1
|
||||
f78bc9ff5cc76fd74a0ab855f0fa03b65feb61ab466cc2e04213be30586a9a9a:80b4b35128ccd2:addc47e9ac39538d21ae3a98335f8b230902fa3bc1fa1861afcf195138443ebdab420dbd88967fb5ebc355200ef0d13f11d2f91eea7d8a04fee7c1f6e2baefb0435b6e8d09539f61c7ffca19af900367e898cafa119f4a587e78eee1be966758
|
||||
0126eedaf3dcec0112ce5f44f181618cc5eb70ef0ef899f7329b3f10067c932d:09577238413c7bcd:a15c5c64ec5e4efd56363ae703343f1d87a8571347562d3aaca2ca4ea24c71249a55e9fbc393207bddfc696c151dfc420feb0f33f27fd24f184ed877bab13a5cce779f0a446ad0dc7577d5658dddf0179ef5bdc604d492d9a3a062194c4b5cbd
|
||||
9973eb6a3eb08827754ce8088763e853cc97e6eaa1812bc32d08fc9ab26992f6:1413b6179c2d166b:b6629ea9d32194dc73ce3ac4e02858c7e62a09f039370c3c98b73a1ddb14c29fb59b9b10fa1809b38e4a6105c5aec96509ba4c83178297f17e05bc70ee1e0d06569c4bb276b3a335f076140787c466fe4a6bcfbf7d0de813133fd731e5b49187
|
||||
8dc01c9f5983c93e04e4eff82cc4a79676fd247b2a144640566fae5c9e3cfca4:00b47ee092f96fbe70:98e8172d6f676503c6f31a73c3e45ede4d53bb73d10668ad1d04d1d0e1f8601dfb171fa41b19cc4253b18d85a76afff604ef55b8128924d6fe8e76926b4718682a5bdea2da6fba97731f418442b31897b749e88839ed04a9a8312dc4969d2899
|
||||
ccbd235f576d3bc3d28b97334eca7a05d3cc5f2a709a1fece99976fdd2b9d773:198079f8da3599c376:a3ee5959fdd7143a9de5a759f28f7299ea48ed2fda18654d351591457c2f8d1961100e8cb17d5f974af2f3d4ed2d1f9003f3348a4db481614232949b0068f4b46e80e15479d28fabb883ada7404a2fd820064138a47d2f814af7ba06d80af0e9
|
||||
06123d03bdc50fb3bdfdc8fe60892ecc110cfd9b941d1c183470e79ab184fd64:010c6409424adb9edaf5:854031eef2a80b533862df645b40fa90c50c5e9572ae5a5732b9686db5a479282b0d46bede9df391a55f220c322369a900a8c3ce642af7154f5ea728711da75a6463608f3eac64af631b47561abad7a30d758fbe0379ccc545839fb3775bf5c2
|
||||
29dcd911b82d242702e281f02493107eda90028b53efd067f5a8b1b3b402f87f:9919aea3f2c254e0af98:91308917daa0c1f3833a3de67893c754e2f362eed5f6df9d410200fb9d0ba0e4587546cc88d241a1a0cb496960524f440ffabfc0d17126c8941a5cbf9913f547cd3fb59f856b46bd5d6880f566b88e419cac04a49c6ccf37b539eb3a80b4e7c7
|
||||
bfbd2cdf05e105f2a71640421904875a1378f733d07611d6d7dc5576dd1d11f5:0c9e846d9c1b4dcc7e6b7a:8c7d64a8dcaac26a2e121204f40d2fd67fb6358d597603dc9ccff91e705f6706f0087938d1838abfab4f663f2f44b5da172572afa50585a26245fbeb0a25ffa20aea55c955eb311575cf8bfd5934fa6b139ce4cbde6fea3990309ab15c210807
|
||||
f1f54419ce4518089b8e270ad7dcefef3cc06fc1b061febc5cdeb754d889edd2:3852a159c4ba27e465a83c:8b048ab27778fe7c52d1b62e3bee96a4eff22f9a53d76f767a60ac7bb5264e5278d9072f41db284d936109c47daa84dd172e41701bf9a44060a2ae91eab324a6f695d75aa786ceb0af68f2e109c9fe49ff12a01041da22560bc90570f4461518
|
||||
a20a9c53f69394593e823610c1bdcfe10fae21cd42b9e6e59f683aa5cf86e7ec:016db3c02fac44b97af92c2e:860c4c3ddfb15c3d8f57c5cb41a9e8114135ff2ace9fcf55a9a95a676073e0b87705c56ac3676ef280d00f4b86e9208409e54f0d4ea7ea1aa90b40c835ec3e8f44d0c8e968fe0201a11312f9e309847add0433420fd47d8c43214ebfadc148fc
|
||||
f4e297d963163769b5aaf4aafdd1969e78ac101fbb5f08f48d62f21b4dca91c2:a8d74c3c2d54faff2095a18c:81008cb4621bf919e027aaf6877618444953723e24625e6d1330554c599072d0fa9ac69d513d330cc102b6ae79220af10fd084898192663a701c599050c8cad6b7a37240ccecc2cd680474ae70d121cd64413d7428f32822467d6b695c890385
|
||||
3caf69b84927248e842ccbf1e5cf6f793cdc69b0966ba04df1f7a2669e7a9a38:07b4ed6bedd1e2ef2db8488baf:8dfaa7179531aabbd23bba0e27d43e9eab3de719e966b47c0a76493092947137815b23f4fa5c74f4d2bd96ef6d2430ce0c5dabcef379d98395caafa8e68739fcf2c1f3e840e1dac9ad703b2e090dc5ea437e008d5b8fe2f0b1be6708552033fe
|
||||
88dc6b21810e2bc476305c8a706b75062a0e199726041468cb8c4238771d4029:f1b293206aea9b717e147594cf:8de151ec56c6b65c5d93f6295f6a247a2ef2060f428b3f4e90ec9e5bbe3731a98af021c9f9bad0b7db054a45d17cbc37055fdc72b2683d27205ff95dd2dc33b5da84c1211e43052c8025f94c68a53423e5a283ff19cdfdc441e0a9917bce27bd
|
||||
cc25cd8691f46fa1780253ef5a2354bc1ff302d1577dcbbd3ac419d48742f812:0cc78d7d6e414102c4ed549dd1f2:93c03bc32480bcc4defca136f6e668f683d54b6e11c3ca537c23c142844f9ef441beb1d7f6269001aac6dd1ab996aaa50011e8e18eb3a03f71f4a6a0b36b01109fb242e0b5e4ab161e4edf67c46dd0b3684c92cb0860ec07d766f36affa0b7d8
|
||||
4d4792f59fe96d8f7418dee1d043e49f62ee63f0a2ab4b6271314f34bcb9409c:6930e7a417e42a0eae0debfdf30d:aae8cd57c22303af560fb04e7798bb68c649abb0fc1bbaed005db7b148e0a0df674ce75b8f023c31b8d4f35961c9712c0d6854d78d19f465ba844547acd18fb873cc1013b6f187ca491e5accaa20aac9fe43d254c916c9563d4ceb56b058836d
|
||||
1431fe22a4ca0320403bbab6c29089ca1591c5fb447f6dc35d102959594a6d16:028773ab2e2754ac5a75a715d78bff:a53aea82c839d80b8603699e271df8c7ae119be1ef69c912d3c1a68b3e1c419fa50f10435497853206c41524cb97ccef19eeded83fe6b4af2392e16ca7e660d3e2c79232665c396ab9b5fd3594ff2ea93067d8c1080df6dc8ff55c9cce9b35e5
|
||||
1f4d0636ba8bdd4ea49221a270176378c01a7458e3a7fe0ce4d4ffb088d1d4c0::99781a5e42b7dd8c022bd519ea9acc6008a7aa116245e8f14a12f40d87b02bd3ea071d3dbbc09db01395e6dd8b3485ed12ce0d5943637cae60e682e3d2b7530fc1966dee8b53b4f6f6f755865778691a7b5b3911801be279a0731a9aefaf8351
|
||||
ff8045cb4d0a119bab9221ad343aa802467c900101920b332c0c2492bea4b475:07:acc9f6b5d331dfc9e76a94edc08333780f8242013cf98fd946a3d3043d42af7c70013529e6d5caaa4a402c30563bad4b05c1259a914dd0f6ad4e853b08cb5f52f6394cb6ae7c3f458bd2c50db77aea76fa09c63d243c7cf0c8d79d4ce8be6251
|
||||
81561f34e382891d3094cf6b50f5f3fdcab90cd2d9330f8b56b8a0c9136767bb:1d:91a62de3f2b1d596ac3edf651d73c28ce925f0be6354118bd1504b69538ad81c571919ffbf2b08721cad0fb527c6d8af05e7f25478e6fa8523e7523d9fe79b2461dc283ad844a22934444f8c5d0b3a5a1d46d2a1491d6ba9819e968c5819c924
|
||||
f67f2d294d27e9a26fbf9a1324a199adbacd044ccea3a20799618fd3d242729b:0062:91c2ecf6692da57fb464aa61fd49eeb398c58a443e473a7fe9c481ad10b8d8ea5f141329604fbf7267cf2a3b57a5529506719721259a3f5feffdc6a6800dd32e6ed44edc7a018218ae1cc72e1787604c41e7ce43c59c7c644327402a51062b98
|
||||
2c8dba24af03feb0bd91fcf67d8b1ef2000305bbb8d4e0851ef151c21457c49b:aa4a:94442f44bd4ea1447f776ceb43590dcca856f02c1220147ab020438c7d52e61d002c4833198994b207422ab6d408fa9110b3d2316c740ebc11352c2658e0176fbfe9ef2fb880ad61c6ddc4ad362a5c806534ad8a37885ebafe89b2eaeebeb35b
|
||||
9229f4eaafb7d35ef0d5a820b911ddf5fc6d3b2c3b49c745dade597025f8680d:0c0efb:8345abb6eecf90c13c93990b54ed628f9e052bd622c3eb6e66bbbe6ce8cda13b4d9c4c1c85094fb44fe308f7efdf009604a9bb1d0dc54eca54ed353293928b59ab9a4909bbf284d95f4f9cee588cc1f21abc5d506b2e7058c51163e0202afe1f
|
||||
6968eed29d12fd07d53fba6e07fde6bf6f37d8a3d9f53673f6bd1db6c28e2cdd:84f05a:8ca8d57e7189f7648c2f2163d71b7868aca21ec8f196bb067f43aba576301282023ebbdb688ec4baf1bee5da5b719b1600a529c1169b0dbe796fd79714ad4ce6b67cce01c3fc955a33570923ad95a13b8ef0e7a953d56fbe3bd9f3fffdb8ec2d
|
||||
dd1b8907a547de4826115194b15ac42558a2c4559f1b236c6bcde09401777b24:0a95e2f2:add320ae8567ac9902990d75dd7c1ea3a053e80f7b924d9d68fecc4d5569f341bc665939f8b9695d310273d93f730793159844b008684883620ef11496a624c28b11b92efaf57226263182fcde837482ab61d5b2ae5daf78b3a7a7c0c97d7c27
|
||||
98d07d9f94dfbebafbb10c3411428262750973ac8f5bfff4f3218ddcd09d95e3:01ae77fc:b2553ee7517479b60dc95c0feabf6e60ff04d1576b1de291a220047ba19116fafba681efbad874fbe5b6080f5972adbb075e1ccede0df5f0bb87b0dce1bc0e78fcafd0ad06fef2332d415329ad1afdabc8b33d26c06baecd4d3f55923fdc9890
|
||||
5770504add317933bdbe89f35463e784d798ce20b5b1dd258569955e8df4068d:0f709c6798:817f1def1b321a6cb1d47dd1558e00692605b0072f9fe2ac07859fa30d1b9da5d8b8fe09a0f7eed404190a72778609e1010ca80ad5528cbdf5d54e4981a52534c096631e38a9533e52bc18b2d52309cc452f03949f6d50fd96f0251c765aaf8d
|
||||
24612966cd6601308edbd27423d622dfbcb27746b31b3e9f6939b571cc865f14:5c2b9e06cf:b6066c1bdd416fa78630fb1073c20fc1813596b6ce0bbd36c407ab18fbf488ad2f4cbe7526bfbf168c2014ad2f887dcd04825625e8a83728a7940c46a689512cd4a2025830a70df7ab84f2b76964f7acb839fd562def8b9021d3575a9d794d82
|
||||
e058866deca74d7becf8ad839ab19345c6fffbeba03bb5551569e6a77fc271a6:0b1412c98979:b300256fe69effc5bcf218426c36c3b863cc092d087dff27ea21d57e63f994c7df97498a37382c01aadbf6556b50403f00f09614134b80e3f777ab8737bbf61a4399a29bee437113fbcb8062fceccc3ea6a5461b3716fee446a2101b0dfc13f6
|
||||
6716da7e6965ea890857c0fae1c57828ce35279eb69f7dd8ba29c8aef11fbeec:2657f15d8adc:88198312d9975e2ec4823c5a0e490323e745c4fe41e75e25c4ba0c5dd32030b02597c90b24d2952e78c6cb91b90f920d13514e193eccd54212f7cd3a0dca90197f920fa7c0e85dd0c8a8009f1c98deb823a5eeb632b73bf44b102ed38c68eb67
|
||||
ff076024d1dcd908540d3cff13e39a12544e033e5e3fe033e351850fd75fec62:0f831b8f177833:849a8c12b4f619a87284b881808e806b5b810b90938d0fa1df01cab27896374542ecf8698f20725ad9f013400d11643f140ba02c03bdb318f06ee71b9ada27aa1024d6a9a51c19b25e302c7243f67a07ab5accd504e16e6cc08093d58619e5f7
|
||||
893af0a540d8972ffb5b0a7fa4d24fb180230acc3960f9f30d654cf6ae5c1531:58f13d5001e1de:b73f09066a90916c14f93d6027f78c1ef75f3cc0fd7b1d2a40708665960d9b6b09019a8652ac21fa30b8fa4195fc34261839beddc50af794652f0f3fd32513483564887f9ab1d81e9b1767d19211108882683e874a41fdcb193a9e82e741f147
|
||||
c81191beccf7c48bef093eb0fe53536d02ac32d01ecb86358ba90303d0fcc10f:01d6170490dead4b:877ad867bc97004fa2deff244bd2e356bb30129b1c0cb0c7f25e9429ec6527ed564288f1b83a8bb144ce75d4bcab189005349b397fe1d8d219bd8bc07c6b3af414e02f83180fce1853005fbf3b3e8eb62e7ec1f202db1b86b7fe5b3d23bf2da4
|
||||
a3f68634413db07bc22c448748011efddc9c0ff5e3ddff037559299f16199373:9d97a98ed3345f53:98b8056aefca86c699804c92ce7d1d72c4457c12b58e8768755e64bc4be94eb05e25e0587495c469f129210c2b6308620fcddff4ef61db0b3fc77b6c765c83291edc33a34e3d18e8cfcb7f0f51a89c0908a617bea3613f8f91caa47071d47aa9
|
||||
6a6f8a98610a5a4acdf6da669ea3990fb299e1aedf2ac327c97b0ce96adc753b:057de0b81f4f9a736c:b834e23d96490775c3f0b2d674d2506149d2345f94f157c3b124759ff61fe2f3d0773b6b9bb0e1566d370fdaf273f55808fff04433d5f8a5537a3142cb299e8996c42a420ae36e6c9335d2e630600c287bf283b745f5d5985b7819a6e889efbd
|
||||
6422d62c14662cc8ad1fc10875ae58813fa9ee349c019c452a917d2b7ec98404:8956593d05c7b5814d:b6abb8bf4ce2feea01a0f6fdb881cd276c6576bdd92e460e3827f6081d44263bf21498d5bad1c3b534753214f30b15500b0bced8b763ebc1b292d5676021c1a2db09e35d5d3f306b4d1436087bdf8b4720e4ddf3bf89c323804dc006ce9a6a91
|
||||
67cb55964dfc93f1ea615a89ea49b26c677ccb4a2a0f512935b45b3a54bf70a2:02c10d2e5e685de181b0:adda564e2367e13037727867cfdfc0a22be7ada7670a01a66f3954b54745256ea89ffea82dd5e68e98f4663fdcb4ca3d017fd21d25b491191c69fc3eedcf8064a5991ae58ae6ca81e05605212e166a6cb5d53f6f89fa96a0764c26674092ad45
|
||||
80e2aa0ce9bf22a06147e0b3d5dec44922422e976eec32493bb4f70a2c205082:385d300a9ac9f61a367d:acd05f67bd58438b7c4b1fce46505437ae7829a1d2f114e806cc46d2ce5adc07fa5cf6d0a08da486c9fb792bbbf62db5106294c6524db1838225eee010bf6e70bb99ce54efdf74e7deb544cac38f9b741412c0c061aa70fd0e151781746a08b7
|
||||
f8722dd9394845c2744e556933d4d7ed838e983f7088fd2ad69a769385005509:0ee559b4340c968008cfef:8c96413e8262e36871d252ab2060117c8590f4b3accb11db0c702a7bc6d206c4d767c4127591e17fbe81a0f2f6cdf436089005dbb0ab266fc81da3cd416580228cebc1e48349894886856993f43bf1a0b5e62fd89ad0f205917f295f2edde990
|
||||
d9ecf27ad6dd98e0ff55c5f8562eb5a7e4f46ef38a9245dffd788d6bb638cbe1:b8ca56c4258c05963bbd68:afbe6f9dff95a5d29af76a07b7400c75ca49e066186b76274182507aed8a9c32bc90f863eac72d22ff69e170caf58e5708f6ebba8aa69d61da0e1bb5f7b7b364e38fb6fca3ed27c8588a6077e89adb11087ed0e773aad50fa57a06e7ebe3c64b
|
||||
295b144cdec443e032d21c906e46b3594a1e50b3e6f29118028767f189ce12b6:07d1ad37496c013d3f099071:ab4b0c1a0402c5a63a76c88a9e2c2f36b20f22f1a1de61f620ee9634605d7d9dd12e27dc2d04f1d96538f9d28a1e93a10d5fb540051787346a2633759c2fa8a2f85db615b1e901158b4c22455402cfed378f4acdd4524de4a39ca4b18644bd35
|
||||
2b614a7871bf0d74e53bc6c7714372353b4a22ed47f1f627d3bbb1ef7532dc77:624a8baf7884b4568d2e6b31:85aa088a9bc03c585a4238a49e5e6aa36434609590a3fb8611d012442ff6b841d23aaed5011df2eb61e2be3389f684f600f219ddf20da10f8cde2dda151c74153020e2cb7e804999c9a99bd02b12a11ed65f8b1e73f8b27dd819faeb46ebc7bd
|
||||
422ad85c60720ef2d06cfc26ad0e7ee1af49cfd6ba025692d405b1cb41fcf71f:0ddbe34431448b194f1b29bb60:b8fa03a4ddcf857e50f142ed3949c3c43937ddab3b67491634137a478b0f74ff12a4eb17d030bdb3859ce2a3ceff5c13136b61832182c1808fe914c5471cdb963870f2477a6d59c842e4e23fd7558488fdfbb46899aaaa67c55b48b9c94f594e
|
||||
a7f1e76ab8164099b21ff25319a62b0b77afa45746dea6c57964eb605e6ddfe2:f817dc1a64750b045dd8145104:933e398eb69ca41ed5c34a4270bbcfd64bc0446e23c754703925b354097ca7a2ee8ca253eb279af79a5e4d2d907acf1b0ce632630899607a6a67fae6654b90fb7bb37578a1c828ac3ac650c752a645808592304fc7a32d6c1a0ab530fa3165ac
|
||||
b0eb65f46ed2a9931220920e3ec70edb39cfa90d04de48f53bde65e2fa3c7eb9:01cdea387f4c40da7949aa79fab4:b9a0c6a68ae34cb6d2646d9c14655b5259942cadfc7cd0982af5842894ef4fcd3e9136e83a1b6bdc1d9705e5843092c308fcf46df2e5e4b99534ae6acdac11aac1d558073cb0632f496307df850cbb8c199893bb8313f1db890cca245a174c61
|
||||
3a6ccf539c217a904fd7a718d2815c985325e3c9cfb7bf458a219b8ae78f775e:df33154deaa3d687d45a694ab80e:a269482d849517a2fffb267782152bf07daa7bbded7df8a18b7d74c9e5a4f46c09d6f795f7a9eefb8df107d10e5598f203cad912865152985d407f3d7303287fe9b0c16c650016bfd9dab49da95665e98442ac1e10ce427bb16c8bbe81130c08
|
||||
41c836e9c4069d15ad4e871a12a81fd91fa062ba39905b659ece6a8000dfb125:01214f40af921429a48897d9d286de:95a2dfaaf34691b69b9df2a3356bb25ccb9f416e0c619a5f6bc1a25b267d95de7c5fcbc50d0e6de1d79afbb083255b22158ede7ceeca628a162d3b7d4887574e7cd947e1ee5704b3970aaaf92e47697623089bbb032ee1ca15505604beaedeca
|
||||
25563baa30cdb1664317a7f6a11fadf24dbd7e33f758ae9edffb4a3bc6e47809::ae9b09587dde61b31dc3a252ee5da28b118778ea40e6940695c0cb14e927810dc1bdc3580421e6a6349770e468ef624a0b1edf2a2fca77e3afb1d4cecddd443db8c5b1e92d0bbf123f9a7d3ecbb94b6d7a72bd0754bb5339e807baec26fe6688
|
||||
324c59624d455e439661cd33bf2691779493f51ab154942bceaeaf9a3f8d2e5d:0c:89243606a295632aee72da9fa717b04274ca61ca99279b75705fdca338a6712f9d92939b9c868210ae1a80c2a315f9a10944326c7e753c7f7a62ed69fd2a0e27321a52f1305facfc2fb091722bda6f1edc3ffae29a990907d7340715dd763e0a
|
||||
e4168522cbb5b48039f1010ca723f64b540683a964b9d1ace259e814f946cc4a:58:ae7062fe9c1fd3eda7ea4aebfc3076f105a975cbbe9203dce3014a905fbfb753d94ad6d0bfc6a4ba564e376b422c326312c26c7f4eebe2737cd55098abab896823ba0862c6a064fb0eb814d773ff08199ae30cd992b8fdb3a98f155b8ff85f87
|
||||
3d7eff177604e52315b47e30500f9d1419496f5da2d9d2b4691b9e840b5a07fa:07c5:b5b0f787446da8bae5a3468d5efac5b06769486650d42ca5d0e8a9b1a7b4c47b9e84def60984469ec8fa311753fae2ff0feb1f42f107114031d8e6c25ed3d7ce8b882d05bc169c2fb30e301b5555211d31e24c4eb67242d7ae71770426336f4e
|
||||
cb047e496e0b4826382c35e58dd5b83157e070ff2ab4a500c775878b14af1382:6dcd:a38eb229aedee1a3f23afbab228165432c06180a41b5b8ad600a4d8ca734106de42e83d67d045c3dd5e5877fdca506b10a1128f199cc98bc826e3d42c57cee4b118af6be1b14eaaf0f76ed7bc02835a69065489d0054c74d4bdb342ac5e6d540
|
||||
90cc186f844b3e92ac371f89edcf9b55077ab4c43e2994d5824cd1a869969639:0f9f81:86f99b1a30d90328fe106eb036e4016ce2f58ac11c312e863f67886e89db2f61317696cf32f994df2a3a15d4acf654d2115681df1f4c99a5c581da34ccd08c408b8fccceea7f23daafe8a2e3797d768e04407121227d46a77a2e6464ca8d2dbf
|
||||
9a88979f86f9b645c18435713d1d9962e00b7eb668c9990063cddf048c5a833a:e35fd9:af1e6549066d0e1e1c795e05ad4efc277cf63ad5628b944294bda5c6c2cab553cf3c16ef35525190d6b6ae838d8d5c88007e4b33fff1d66857248606a4fd7cc6f3878265745a9e1874a1cc03a870b192e2b1bd1974eb380937222f4390463020
|
||||
dabed287fd58fa6e4f66d4ee5b1761f660e3bd29021c1887b317cd0a5ea8beaf:0ad95982:afa79271194f2d89f7ddd9af9db947211f24ce7ad576a157b1d4910c8c0b6f87eecb28f6f18a111f74f144fd0b1724a8007027c1033b055d2fd467f7c1ad60af663fbfea59d13d16fbc291828a8f11757fcfca6f26b6f3567dc42e05d9feb70f
|
||||
da069623c0621d0e18beba8609d0020c85893c4929b5500743101b87874cde8e:85c588cb:b7431c652478c889a562a8a1d3babfc1037cc065c2c850b334ecdeaec04311cbe3abc764a465ccc54d7afddc27b1d443085b32d605f1e4cdeff84782b2ce844efab05f2b4474ff70c037f5790897748d6af6b6417e8087c5b0c8e6cac0276e59
|
||||
a0f21a36aa3e9393082a284bd74357c31dfc34e332dfaedc0f06f45423fc495f:0121789325:b0c1a71f1a74a1e8c9cab9f51bf300ba198a135e759cb1845de10ff7160d13b5f3e0edbd63b023d32a178b9c598ff48d1366afc2b8f0bdca4c2f1572377baffe19daa9ea0b81eab56eb8589a6278ac57ad9210b763d9726073a091b4e3dbb9c2
|
||||
017efc554478e7f73876fb6ed0b036ba4c6c653f5baa2c13c8a705e8d936900d:5298548f3f:973c337a1e2632ee2355af6a1b259c764e3d1f99ba9e1c9840b1e3df0d27e3eb477b670976e3d0099c49b4eff639260c07f19d0c474d152ce3656f422ab27e65c6288d61760d5a96e8af9560f4460ef4dd53a802536fb19eef9abd257653332f
|
||||
4d7f9479becad152a443f2c759aab6e02a1f0feb15dfd49c11804d2a104b39f8:07bd8740d5e5:93b4364b292786fb255cb06ba55cfb0805923229ce394fb5370a3571c4773b7d8b6801318e6262a4499d73ff181b3bdb09347f73aa4a2ca7f6e99df8be366346ae43ca8faad4458e2d30d26adf049a7542e3f0ca72f86006cdbac3ceaf038515
|
||||
b2e5bee63c6c00e5f8fd15703ded33d45037da065cf3f498ecaf72a8e9a8f23e:7d202c324585:b0981781fcb2e04e418d7043602a61b7a14e8d5cb2151b40adf729689729aefecfa1063ba1b43ae571ffaa492bf81c14121fbb6c2292fcd0d1c853c37b9605c0f05a2647279de3d07c2f6855b5a81e019badf9d813757dad66eda78d02aa072a
|
||||
63c95845cf70d1470e0f6de57729b5838f977a8706c1979388829f9b5b3700e9:08aa3ec1c57996:a950729885949d9a8b50a031ad184d1887b2992601c0abaa9e5b6ed71e5beaac0534ea2c9d93a7c564b77d0c5c77bc5818fa164575d3d58714687c7f549058eaf3d0fb432d328e3bc8da3ae7646ffde2297421a2c418dff28026da3d29d1d498
|
||||
22059d7b3332f4def25399ce17b1e1481028ec4d2d8ab835a69b7ebdaa16b461:4195fc080b251a:91ca4b0a098ab6678c54643b2546a3535696b109bc49f3e50d8e6966a56ffa61ad37d2eb29b1c6a19793aeb30edfae53021b2a322a300bdae1ce9e9f67267727e682a4568845b1312e652ba439f6d0432c36342151234eb639c9cc8153ecf743
|
||||
6bc4bc3efec07ce22fa5a5f0e0fc4a4142f8fc4c078e6915ab7f14c9743752da:0fdc2eb64cd67bee:933de772ca7a3f1fa61c87a5a4ef914b1f6074facdc6a98c6e8fdb886a3e53e5acd2766390d9c6c8e8b48a268c4c227b056a5f4f7895590e48b44863d26c44677091acd4585a85c88c8de2ddcc1c530b918233d7655e858ead521bdf1d66b526
|
||||
9caa571a916d4005e08af68c36b8a65bb7ffd9890758ec276d30278a2107ff64:5067f524af933a64:843b7097035540b7c219e82760cb94aa749cb6579301110ee51984b8b63428207108871ed1fca275ab05850e3563a09a19b32457285f15c1a3c1f74abf105ede27dc47ccaea5c90623f5d038bb00e8a2277441e165cb8327e86f03c2f0dbc1c0
|
||||
2178be06e144b58fc271dd130182d6ce562fce547c545adae7df409c61262876:0f72ecc940599630cc:abb38edc8b7232a479a51b23e3d0d5fce3e6967190e2e68df73fb95e3e2367b2b1b6360fa0b06c508c664d90f8cc345a09e8b8255945b850ce8c1c5e389258767a709810e519220abad9ea7d9fc37d947038634ad059821d591a671595df762b
|
||||
1a1924318927acc40cd3cb8265dbe703db0386b0d94199dba5d67cfcbb497032:387376ba00fc30b6c0:a2e6514012c1fe9484cb9df2925a80c1451d33cda78b89302cee7f59aea190ff34a71a6c4a46a77652785b44dcbcee100aba9beb37612e961bf83139abb690449e6e05a351d8408939585b026fc83b1fa0ca0d0303295503c65a40bce402d12c
|
||||
9e203efa8eb1e65d053eaf644f9c3ae67c084af1a3aa7b2f4cd051f4ff870bc9:0c41bee51ab6c3976cf1:8f6b876d190c548c8cf130bf3c5c315fd6f0a8f6610d1cc6c0f5f199912fcee078f5e4f9d8b2d8b239fdcdf43c6efa9e0fb4fbb894a1fcd464321affbd64f07cfc63968fc5fe77d8c5cfe2ddade49f28ae60b7b26d97049b5964aaee79f244af
|
||||
30e446fc468b579fdda3a09ede2c456f9799fadd07c069c44794c1cabae3d555:25538c2c57aa03118f9f:98eafa9da5b2802b9932fa056ac3c81f09b4c4dddcfd5ad963de9374731c11720d7b85bfb5e3955c71f81a86f32b4e7611022aa8717dbd032e4fa4635dae9209df6ee3dd34598fdf504cdd4aa3fff1292d11f74f17af500703ccc127faec4c48
|
||||
6440fb50661c3b9c45c557d216accce15185891f98080b6a7c1c6dd701e029d4:0a9333dc2d78c73c53507e:8e633dee7c8fa3270472078f93dd5181f92f43e7b42929a9acf42563367256c59b3355d23c18ca8dcdfced828b5009e4133de13a3e875d2a6880977f9a0f00d17649550419ad5da3194df083f685162ac7241d864c8c72082cac5ecfbc581db8
|
||||
b0bd58b8665f372d71003e10b5c11d2e01f69d2daf5ba56e301f9cf433bf6a21:16cc073e29547abb287b26:a8080f4cdd6d89dcb03bb389be65930c6161d881d149673e8feb89ae07e70265016458b74734ec83d15099767e0c0e4e175501ed542691e4acb3446d060ac6ae971ebd85ee25a507528dd3f4211c608ef572f49d2ee894a5eefcd5ab86db9c50
|
||||
084caa7f9ee6060620c5db1416f5cadd4a54984c23dc804cf23ac5ec645cac2b:0f3cf7f7ffe9279288b56ab8:922c3c5844803907b8b2bd08df8edd00018df0ccc9e515faf8814ed865850fe7cae1240495bf4e947540c325a536d0020f1f15f5583cb6e5a127de49856515b933425ede9aad615c9007c533c85cf25a6380c7f7e45b48d644871f7f72152a27
|
||||
2581c1782074316f855d5d184ed4066962371b58935c672672943aab3a67d005:0d85756630cfa82ed2cd35ef:8ff6a1d75fe9ac8362c40d9405b1d8d3633eb6ae08e12b9008da03eddf47f632d780e6f7f257d27569ded6414ebbc7330f3d3eee1b1bfeed420288ae89511afe701d34e6973df7f9379cf5027ba456cc958b09288191b4a7ae157db428363a99
|
||||
bfed6cdc36912d4f49a87cd108d0f8e51ced50d5870dd80ce98a030d0b46fb9d:0c3255c7a617e8d3f717371300:98a6004ccee00c914b915f5f48a2875fa7f5849bf1bb02b41fef83712ed2e352c1eb3f1613697b3b5b77c0e99a99b6550a5e76d894529566c649ae4fd29d869b84ba5dfeff543db07047867329fa3f307dd4befd03f0fe71143ee46160f2887f
|
||||
a4ba0fe9090e0018df0e0d763fbd11aebc12b3f0c807ad0995db08a6bdc9b160:27842d981fd9a812efe6391c71:88b5e9dec395c4fe67641a618c4512e082abb8ea302755128ee6d2c0669b91424308b3bcc0ce6cb3058af4099cfa820603d93b557ad60c3e29f96131c8838eb1dbe9bc404abceb50f1ce22cebd6f970c107323731ef9ce736d30ebdc8e59ff7e
|
||||
ac42165d2ac95f081108888a94d0a56460400941c57f3145ff2fb6900a7b0c8b:0260471f10e5f35305906cb94df8:b62cb0b876a1edf2dbc35eb4df5627ad09c8e6d8d9da83a154c4c8d93e70ecea0794f412d53c50677bb064a4038abe12106e3581e622fbaa6fff3f86e06653b890defbc8cc4d190280ebc5bb4431a27677e0e52049ffaca5649cf75f22239a0a
|
||||
396f95f097863db7d343484be8ea7c26d2db9e022397e42845a3aba66eac2165:e46812d0412dbf137a84b49d4b0b:abd154aac031f055c524185d56e1e4f259800a87bf234b04fcc509181626d4e519e97c7a32da1fba04e6e322e0a8760b0424065e8b99348548ceafea44d107bebdd119d47f7df4d27ae0e14b5fff497408e8ce381256c41721477971925345bf
|
||||
a94eff505560f2686a73b2b56598b1d43956c93b54c05e8e04e845a517d2f454:03e19ddb791c70b3bbb0646679efa0:862897bd1566b53a083100384352ccc4477030f463c21135800255d3812876f33388007080aa431d84e70119b9c46759013b6ce9f5b68004085f1fc009ac06d3747c9ff008395ae6de90417a43e5f0dcdc00345bd14e8e1cb2b7598e1eb91bc4
|
||||
3b79d30b7558a89767dec3f3d1f3393d13ee5e648786ed6bac49186f8a2ca2c3::83ecd612c577ef99af50938c218bee6b7693d2d5aa1a06144346f6c8e46e5f741ca66a154106ad512bf5a2d9357b89da13b797530e42d298f6f74559cf5452694de538328301df16a8c91ffa5a6737befaced0d81ba45f68f3587639890f6ce7
|
||||
164811a45674f804c655c8c5ad3863b359ac9041c7f1e72c0ae20f80f41170c3:0e:9359a49c73685f2a8a9b6c46fb2773a56061a586ad1b7eb38d7789d23260f96741f4046ebbd119c06ebf64e9ed7b81f510cf95305be70c83193faa2d16866e76cfeab918c608d9b4dce6fff60e807358ae142eff4a8b55a01bb5da982e89aa37
|
||||
07b300ed4a2db3bc21642adc74521eb70ba2c81a5e0f07b7759a80ecd27033aa:05:a9a3b90336d9ef805992939d52302e641c74c10216cf320696ce6262dd1ce58ae82099c2d0575892650a13c6daf2017c11c84edae1256b035f82d467737c7586992d84d0d521882ac2c0babe65e06d8d0df64963667972ed2670fe25b8dd2804
|
||||
ed01008602bff20ae9a170fba17b328d675136e194d29e4663f9fbfb02433de8:0fac:8c25576acc891e64003d756e0d74e11b03b08d5e56db3dd39303e589cd8037d21be1fc217a94e040a9d5cf544f2bdf36083f8eda0481dd96b84df5d598d9f8e717cbb5c749b1278e6c023413d58ba47b8b513160f84b407f22c73392ad6fbd67
|
||||
1ebe5d4cde886b9f5f4555e7c02bc14987dd021999a713d020cf3c08ff9a4f60:405e:a49cb23f9b25095c9d28d94ff6b1763cdc257e5824350ee79c726fc4dfa4307ce365283342978bf2534782898afa949204e5ff271d495a577dce076bc8b42ecffcf5cc6219955a4e914c42f41c3fafe35a1464ad06bb35032b56408e44fa8ac9
|
||||
34df86999c7c40e1f0d423e8b788e5a617132c37dfe7865e4e6052b7b7c0c08f:09c6ab:99aca067bd01d52542141bee5e7941265cb45d058b090e3b5f2965d2c92d17f691e10f489148cac52a72c0428c470af2071d2f484b6bc772a4ec04cd9c9910bba345603371925c1c29789417f507d8539cf5981f07a77185b0adf8c9f7704199
|
||||
8edbb41c9d6f871f1148006b1047388eba52393561f71c41840861108bc0baf9:6e29a7:a061b2b9f869374ff143dda1e43f011f9326009c7f791b561358cd32605f7fe7c12b5822cbb3154c8e145eec99c0a9e510a3bb32c906331bafc87cf84bc90633bc61c4db5eb38941ba76567a7e29f3b9167d1ddf2d089a770e1f4f66ffc760b2
|
||||
74f0fde07929f00fdaed639fc0d108b53e44bcc264326b5be114dc766784837a:02b3c8ea:b2eba7ea2a3d079ed7ee8b151d66999a2e0038793f4659bed206c8911f2dc98397d7f2c9e97f1d7a9c687f4b0572d3f6084739d944f38d1fb01a0055a4c346ac54766c497991e8779d0f563d9678a5b47acdb753b1820a82f1295f984ab0ab08
|
||||
028927e8f7fa65d681ea43b9f38590748308f9f304e54f52de2ba454d0bf966b:fc76ab2e:8163b7efeed623df2c44a5cd27568bda4a11b66192fce2b1d46ed98f8b33c7823fe124632bdb42abbb6e1d0a0780d1cc0ee9e9ed28aa5e818bc8aa24bbd8befc01f8448f4b02cab157a3d6c46cb52e085ae5c67fd8ebdba1077c8eb87520d1f5
|
||||
ef9d9a26bca8d75efa517f150e8da7bdd568e59c2f82ed4fc5212e72910cdea5:0382c632af:b8e3126b58b22a853cb838c737237f1a527bcd4c2c8a4aff94d8e3a30b1c53181c0d28a8e8c46fed2e083a3a6219ce78030eec3bc926efb24b65a541e5715849398039d974381bba842a25210b1d6b699721cc82c4f0cd43d5bd268602eda42d
|
||||
ab5f317da1690eddc25786c39b496f06dc6e5a45c250c0d16ff6d02feb448463:3190dee04f:b263b2d49e3f384e2c73e0a55030b3cb479c0d7a10f6860cf148c3bce659d971f8b60c1265705e18a98d8b7f3949a0410e3b34dba3ec3ae66065bed68744281a5722cae8042fb094b03ceaf4435bd6bd1b4a3c6a277b613c24d1dd0aaf6425ca
|
||||
2dd2d83fe918f321e588516529cb8d8e2f9dfdfc7e24b03ddf4de7d68feff2c6:0eee5a001b13:8ec481206906594421366b291c9921d6044044b40c5a43f45a603eca52de182cc744b38d80d1e1f2346119d3a127bdf0084369d7d317a0664aadb36b0fc421c6ba61bdc97a406ce1bd9a05a351ffca89a78f21de6c06f9da474098c343918c39
|
||||
62f441d4421c9e841f5e6afa99346dd757ce3cf4d958815107f80ec965d52fe6:dbd901bfaeeb:85b1619d9be7bffa4fa4dfd3be3161d1be679a48c3e0bb5d637f55ef35ce43c86611a21d078f178908ffd329c709dc8214784af6b2603b944d84a6b70837a7c5b0c38baa97f0b0705c4619f8bb44d1f91f4b248d60ba3bbeb65f3fa189044216
|
||||
ccba9a32e4ad8827d36c5cf800d1536efc96f13bfb04646d39af25fb710880cc:086f8a51380947:a9d85d39f4a330011e6f7efb91ddac0164ace73e1dce16798c41e55fa7318a04f32700f690c95ae10542d19370e583c00e004e18ed3b4161c59e51e14fcfebf5608f403da4d0115a81cf5026d50b1f63aabb90002e89e47e0b55c136d5af98b5
|
||||
bb8d4790ca2b403a262c3aa0569c12abcc3e29e39caf9bc427508a146765b67b:310a7e2f80896b:ac5c0a6ed31609f8066f35826f23864fc373abb0ee7903e21890e21606b28c8ae6c3f2e85053f0b373c055d3caef41cb19a6f4076acf0298be6bfc5639dcb472f8380a88f73d48ac61b2abe134258124a9e7679a71c9aca322a1f69ee2ccbf75
|
||||
2ca95104bc934be172b9738942c9d929aa6c63e8fb38be3435aba875613fbfd7:0fb528fd90c1075b:ae6ecfe94e796391077d18644f61bdd6e9ca6e45c18856af798811199a8ce77dc408bd139c92a086b1a8541104b3b7e6187f5e6484fd5b3bc6fe125a78a8778ae7a658d74cf0b8bd29509996acae061af6c1cd26365defab654390b8fd130c41
|
||||
d02dda8a8f2a6934fdfdde0f0ce44c42c32d035e5befee776e151a1bb6dc947f:a5f1004395f76923:b4755f76590e56910f262b9e3203ea371da798a81899c0418a9ae5da53f0465119c4c3c76d3af1eda9777a5e69716aef01112a650817413353df188420956d5e8883b9625d38126f2c9d8e655a499358198c72a3a8702ba14ba4b5d7b177f264
|
||||
89a57207563f897d97f8b0f4db4b7ade769af29a08978a4732498e7d7865afdb:0914983e4f1f86c26b:95ec831076cf9d05010609f689a864ffc41af5dfcc4b58d7e7b49c9f362c2beddc46b872611a6abbcab9ef7ad80780c9005536dc992a0e06d162f54a561d089636b732c67a4faae391ab31868959f7e777ba44c24efa5778bbb719f7b25a5bbc
|
||||
2cb2345b9fe58baeec3e0d35f6c2bd71f7a460eecc1c1f24687b71c6ee8dac6b:8890d3f6a2efd9c8e5:94e89bb4e2da9fc43ef06ee1f02377b7645ff17d8c02e1c3035417c445279d0ad26dc4622fc864de7b93599dde21135218024b6d8e9bbeaca09d51b4b616c13788c7ec29b63eb8f9f87701010c7f6146e33a37a7c8bfa5920dcba068a304f1d4
|
||||
c9d26fa9f3309740961cea69700981cf51e500d068e5728846710136c4b8beb3:0a4515657fb50f595c50:a435a4692f4f8befe3bc270e38ad14878020d0486887ebcb25b066109312eb51b498813f0324e41b4bbf48297d2b52500938a84c94971178cb05da675fda6fece7a9411e835db608a85ea53340c560e0be32ed4869d00621a15e43b64ad16d0a
|
||||
54079aec7ad7a051158086c3986871746ba540c53c519617a01e559a5cb96360:d449f821249be81a006f:9679c59342a6b68f9350777c5a85d30dce3a9d51082406f39fd21a695a9014175e9b61ab668a1ecf06b0f571b3d5f6a80c5c8d53062129cf3665032c36d9a743a452f0366bad6de34a8264de28a1283f7d1cd584a070611db23322af5472349c
|
||||
aa3b143ce985e2d67b716e5288b0c764e67b93f0e3794f65da0216999d7059d9:0f765189834cd02d2cc4d1:af2de4278f8b0c3eccc5ecff9b35efa7215d32abed6af041bfbde73e60137bc283a68666909668b9ddb2dbb0c9691b75045357dbe068f9cc723177630b0e8d19eaf3d3b6ed1c695b658b0c7732b6e5da17a8372cdb72e8b8a58ea72687935eff
|
||||
608544d7bdda540aaccfc89a03a47946636c732226f4841b31f47a247e4a29ed:5dca12c82d6470eba98ac1:8dae26cfdec9849d7d4701e85c160f3db109aa9e4e26d9f4ab2c158008df740fa526f71541daefbfe61553d23b3fc94c194a3bc097ae33be51480c8f91804147f88e95b054e88ee0faf3ae718135fdbc92cbaa26277f76cae42315771599b2e9
|
||||
7368161af24ad459f868fb847e287532a3d7625fbdc97f912b513f5ce525a9f0:0d43de10726e1ded2f111aa8:8bbabb617042316158491db3e4f83ac5b9ea8acefaad2225b2663d500b6119e48fac354adcf994968e3ddd88e20684f805a88ce038caf4bdab372fa9444a471636e979f1862b1519efed6cf003af1ab9cd66b9707a8ffe4d9e1f9e4a4c59ee8f
|
||||
694d55ee8633e2f3ab91c1f79953d6368875e40afbb6e4674109132d5612aa9a:a72ea58ebdf4589cc1b0dd9b:b89f66033ab23bd10456d1cf977d9fc87dce07e1390f876815875a909128f4f5e5f5f1617c8862b01daffa868661a764097139b2198472db19448e7eb9449d32334193c10af650f56626dfa5f2a670ebd2eb1af484afde7f0410c2695a485280
|
||||
bcccaf8728a31871e0bb250ef42ffd55bb708774779ce59ff38679228312960e:077df870cf7308776ebdf15493:8018593b2946f374536067db76f1a45109b9cd7ed67d9b808c095ef51234c563e9715c62deb7779fb9779604f90c5b4f0509d43078bbc6cde4de440b9409619bd198b0e1bcc29f1ed9fd23f64dea82a888ac53d5f4e5ab2658b01360af8db42d
|
||||
30ddb0c578ed5b6d16b7c923efcc5003494faeebbdc509704126599ed93ba6d2:f2fbf39b84983f5edbdaceb18c:ae7880c31a54ab6c30fbe220415fbbd33a9e005d565f9f875c879b8e9ba20b22d6e054456f8e780faef0d30658b5e7920a9619f6b7608a9f84fe37ec1cf890f2382e8b77d570d6ff7cbf595e091cad7b74e70248c974825f8a93e9299ab2139f
|
||||
a2ad294f7cd59bb04df59146e2924d7f212f8ef4a06a9fdb95bf33ce7344d017:03290c3fded14b5450e5b983a389:af9c61b820c1cfd192e134f0976a949c7ff86d8c558f5fd0a5ec70b0672861494f9f32413379b915896ff7c7e4ed479910d1046b001ac0a6d3597b6f5b8e5ac08b532481dea5e2a79d09f9467b930461cd4efad286d141d81682eb04127462cf
|
||||
abd98b2f6a1b3223890745d67beec62637812d962b69cd589575d4d1edb81f17:3fdc0e101ad52086e0f85568d583:8640c6cbcbd2cdb1ca8ae5436cf1f6f27f9a17bf1d8c9f3e95b2120ec4ce91ae3a7aa3433a0842277b856691ff7f220a18ada5cb83dfb0fcd1f47435292f34b48806921ee2f5956e166f04505c5c3c407a08516c24a27e8973e579547095cfe9
|
||||
8bd98c4549f6d08588cd6cee5866c71696437da734c0835c3da9fac963de1a54:08a83444ae127c9fb9e9df7c73a82f:845f8391ab33e4a0fc29c5e8d9519397765c043e1a9d8eb506052808838266857a70109c2c890e3a815fce66a3e3a8c618131ca4717579997357a346ecd4e4beab44e005fdf929a9db37e80c8e70c89114ce7527141ce0652e24e22b2569db8f
|
||||
4a7a6e21534410573a72d8af48f592489ba283a9ddf4cf8844783b0e4e076694::a630c152277ba70a0316f42a01e07b875bc31b8c5dce53dd5001818ff5c4144017c848c6679004a9eca6edda08dbf64c0fcf94f891d2e957dd2988f2a459a51836483813f211690295dced48aaf19ac31a1bed284ec16b6465eb1fa9c8ab67ae
|
||||
ce3bf80673ace59c43bba9866fd2285c0ff341f05f19339873e6cccf740c6f9a:0a:8791fe3d88f99ee59e1e56685201568c78562199d4473a2f8f0a22e48d4bd8c0fde0e203a93f37584fc629beb3e6ff4b0a80c2bb4c91fc60f64e4c61a98851e7ea103e679b6a90add7290cf655ea9a32441babb5604e56a9006ff0b36552dcc5
|
||||
ef5f9e3816346d71607ee1482d862a27bbad5fac8eff68fc8c09b3bcd79fec6d:a6:899653bd64772ff175f0e4f209edd6a09cbd434a58b624053b5856f4b52f20f189bfa257ebdf5e1e7abb61d7ba3b8f19052aecd1f18fdef84cc5b85fc155fd333007338de79113efe2788f8b4e54d0ad5d453fc649809612140d63920e85a5f5
|
||||
f2ef5731a993762ccf4762e355e37c13d33f7d17a8edbd404ab7555924a88bbc:017b:b8d6ef60ac2c7c1cb84a6158a9c734b5ae008023e94b074ced09df46a61f9e296180b5c77da3c93f35fd6d13850fc081118403118f9ecd22332fb0daf53a0c3e87c148bdf91ae849b1736be9116ac1c0f9d5fdb6e8335a7543f11281a96e2c18
|
||||
7a0e9e9f87d909f7d3107156107407223e538f05069990969db2248e8aafd2b1:3b70:8e7bb94a73839d0149d443672d2db2527c59975054a79bef9f07166d309f497d1b72a3e63cb6379b783bfcfced66c764023baa16abff7722463871a7dc88df8cbe00eab89ea0cba5159582b52c07c4fbb4dcd724b3945995de8dc8f9ac97a2cc
|
||||
bb788ad68da2e63b9091d7e4ac152265f75da7f4a47e53725ba7775fd47d0406:0fb581:acd56cb90039ed1a35a8cd6c49bdfd09cd38bbedcd597030fa06dfaf2719a67b195dcc347e03208d5e1a3950f50f76230cc308702645765d38975bc6363f659bcbca47fd61e09f1f4a7cf9883326d464a3c3a5f37b3253e4a1a28e7cf65ebd95
|
||||
064588d00f156a6708a9fc75c95e25050d795f89f427be6df0948500f7d5665f:b16a2d:865a1462974af0147f0f382dbd9661f5864d7f18d309b19e332d2445b8c150b3c16dc7870b5aa2b1d5df89bfd600f24e177c08e1009c9e666d755797d8d220fbc35171c1415c9e183e86cbf2a06f9ec574e4a462fd942d1801e7da6dfa931972
|
||||
e1b5abd337c95cc49b620e155e5e48e1a65c26ffaeb3715a11c3f8c4b451d531:0b3d05b1:8e6c2eb8d543f81bf0699af4dec440da5a33e3a15151ea108acf9e78cf56edd5596976c662d94a93a8b0b9f4b58e55e10d36b24e3fb4df67e5ec54e6a79848cb01b43c1d82edf8ad8d5c3b3d08891c791b6aaa4ba86e27adada8f259ec18dd1c
|
||||
4c7469ccb41795860f7e228a4a50665828cf1ea62f64ca5af3dbcc90026b5173:e444ff3e:b06c465da8342a430774e64595e567e03a905735f443afd74e0d2dd06b99f8ae460062271e1e41ca7dad9e78ead9404e0e9f626b8af3275c84111956a7a146b29619435f357f5310276221c8c425023a792a561f7159ae4bea27eb6be013fb28
|
||||
7ec0f9ca06f01d5cdf11d9be1aa4ecd04875f9fda11322649f15048aeeeb4c1e:0beb32803e:8e87b48955886958dfbb1970eb1a01e8b84507de02be17a95fec0065c3c3d9274c6800f36297246c3264484fa8080b5903781ee5480e6ccc57872821e5905ef297730364a9c877573799ce0b38d6b24497e09e4200df46e228de05ab4f1568c4
|
||||
fab9d07eef72aeb16536abc68bd311f6a67fe0e8bffbb900a8ea40bc9239a652:412bd122c9:a1955d63b0b78e1bb0c1e661f918d21ce9f9a35a47ac6032e622525bd48fa5f32b84f0671b92fb157004972ab6b7041313410ab2e13c61ff7e0f50a6a3c4e552bb55978dfd631d11a2e0a26e337f6e91e9db53fa702c0477760349ae56167401
|
||||
d7c2c99aa03c9383559987e2887af33d8173834d4edbc4cdc536ea55a612a05f:03bcb81c4791:8a6884f75b3f2b3d6d27137d4a74fc8b04f9a4ad132776dc4e2baafd790354f364226947fa7d1da570d5cfea85dac9e30febeda1445f91580570334e4626bc91757dccb7e53c2a71ee540613d232274fd5c0a7e49c70e0711f3bf72adaa2c9e1
|
||||
31e6a9f2bfd89057675b9986a2326af50fc5b4fa6900e06392500d6a7cb8281e:41e77882d77d:83a3cf3e0e9b4726f76ce301c93db6d15e563bfdb5f93e885c37f0fcf28362e54c86e30d0e6a63f831f097eb624c153e15f8061c8dfd215e7d3a3da513b8f35061b2376457391db393bbafcf43e032f4b3c39a8dd20a1f4e8df3a217113ca007
|
||||
4fdfb35d853e9b99ce5bf7211f55d15aebb153258109c22be3cae0161928159b:053633d112c82e:b42f071afa8cd509dbfb7f4a4cdaa57d1d0910e2ad9307beadcf6ea5bd9518a2095d68cca7dad010907341aca3770134010b8fcae728a60d27f821d759107cfe8fafb2ed9aaf09f1765e3be3d758880b243c5377be3ab293ab6bafc85442f147
|
||||
fc02b27cdaab567346a3fb31e21e5280cf7dfc8caed228152cb602b2f5fefaa3:6b78dfecbe686c:99a058012e607d0a350e8b66193d0f0eefbad1fd31184ae01178a08a4959efa329a82666156d6ad81b6d97cca66ae3851630e61fd629e7f003d71a22389cab9ecb0533a4fe8c59b45f7f80d29080a9c0b90ee7d4b351cb1258c7b3a57fb6cf4c
|
||||
3c4a5789f7d5a24505f69fde12f2157e066612ce25e54c000812718f7b360a5f:038845e18495f26a:a141c5d16c5720fe2c37f0b79ed69ff6b0f92267d7b4b4acccc015876943d692174dae68b9bbf8c152e57f3fa1af159e000d2bd8ad67e5a884daf0a8c0fa9206b3f72c9d5e1a0464b2240b9b6eb22263e457c7c5c0b20564a79ae488178fe475
|
||||
bac507dc6edf7569f366512af88237d356ce017d89b78c5f7f9cd286d14781db:adabc96525ebb16a:af76b85670c18945cf66f8b5a16bc542ef1626d81cd47a68e4efbdbbc1e60a0c3947d4fb6e101c05b58f1c8cbabf5feb0e52127fdf6d268ef7bc9f9fd153d6a86d5f93441e60051348e3197b85bd5c565270cdd491572d4975b1a4e8a0852b7b
|
||||
a4716ee56d0e7e65c8627236f7bab717fd7238c95fcc293902ad5c5974bd0734:00f46038365090f18f:8b2a0c5fb54ab319262fc707efc46c093029e463864bb3e8ba7e9c5813cb4356354b797b8a006202dbd48fc86ffae84b174246ed951642cd1c41722652bd1d4f3f1859c2d0bf1f9d799bcab4f14729ab8b7e3d518122089db9dbf153cc882a5e
|
||||
764e65205f49486a2412d73a620c9cc56337b8b30efd0a6254085738095215f3:17582f469cfed9598c:9613a905fbd8e1f4ada58df87c91ab9335d5f72d813949ad14977d4a524660f00c12f63e4e65d16456c227de3bba02df14a4d61362fe8a7af7d7fe679e5d51cd1b979a81facb26d2501303d41f161c3443a975605fb0daee341fba692e3ef4d3
|
||||
da5402cffede58f9c3e0b1d54af0d230e22e670167b1df94b37a5dfc8ef274eb:040fb6fe593b255bb0f4:b18b19d8fc3fe426d25d759a6bf8127f71e77201a3c0b00953cdb39eba0f08f2f2dee147f2d079d296f7ff26a2c3ae1a11fbbb3f983f55bf5d9018c8c4723f42ab4166ec19be5f0299a9a58c321db4069801f6ee414324dd400e26f81be1165c
|
||||
bd2056e3125f653fa473265980d9c7e3f1a067466373baf59afaa1f62e448ee4:e9c6493ea4cc32c62780:80098bfd48c4026acb497e9bfcd33439f97aef045eb269cf271eca64a0631d99eb3a470ba53c2cde1c3b353760778ac8052311a016f8fc68de6520fce68228b56fd467b0f8527ef9ae0f50f42a28da05a3f27255be97c040a989be480df348f7
|
||||
2cf6644db3ab59850d71010dc15b65ac7e5741f6bff4d9034bb65b499d9ed4a3:064263c5a80c376f18b1cf:ae86f1e1d6d95316820b7c9ca82b2c642ee2fa2894ba52690817b571dbe031b7eb608cff31af60febbbec43a71a55add10944382f9037bd4c9294dd8403810992253f743ceca93e9a9c02390e7d12d5d1f0de8cd03572eb401309e3898e76528
|
||||
c3e1b5e8bbd163deb75cc4603809a7f186d6abed4bd868b3807c583492ee4d2e:d6cf8d3b69b4fc9784127f:96cd710812c0d6c46835430536690b1636a9fd2a8a554eb65d99f991849af4159bf00dca18a458f4df45bc474c4bb916056eb413bc1305a7399a2eb0c4a1aa7a060dada885b9fe64213fadb87135c51785c42048d0705bca81c06692722aacb9
|
||||
85aca5189ce1219ac39e67279fc7055d694b5ad8282f415a181bf09eca566c0d:0a6b5a5a1941892e20e40957:83ca28da0a089a592ed0622f9f025ba4c14231230c8c48880e447cfd3877da50c3eaf2beb193df8c86f674fe7b92a98616ee593a9ab67ca83004ab044038a10a0836e6ebf36293b18f0cd18c13febde11333c3567ea08690748c105445696c38
|
||||
29bcd632044c3f3d1f2b6c37b60a1fa531cbc380902ca7ea5e417081337631a4:5dd355519020c4a2979f09a3:b6400ded257d0e63d8ce4ff8935e6b312be17f24395b26563fe4e532e176906aa0d60d5efe7cdb8855f900dff31bb4b017b7675545f874d80e1e5fb4c0ca85d64b4bdb439a723c337226f70ea3fd9509b364665b3aac65b2e272a3f6a5e902ad
|
||||
a25735a8c6438183a3bf3d2e11a10b18b5bab9788fc7f571f6a5d7428b0bec57:0b43c1734bd438087ee7b95309:b485e3cbfdfb5ed78f9e7a7b70173710c0df336ed80b605403cc4da0d845a6511dba84f9e071f0dbb63a6c95e9e0de7d0496e3837f88c5cb34b653c8bcddfa5c5b9739769a872e58b8791bdbce97348dd00e1a6e52d52ff80a8323ba428b1e62
|
||||
fad6179f7388a19c0b15861cddc147d5b5a9f964a939139b271087352f6b2a94:65f21999adf89d3208de7441c8:b9928bcfd736386dd31bdd395ef2c2653682cc58c0479c1ae4051eba74b55fdc71cdeea3c8763b8d4dbbc965fe52a823011ec528a8d7ac213608ac1b42c135db749aaf3ac5b32fe5b00c3f7d58fbf4cb0acf77728e202c672c2b8dc6e8c791b3
|
||||
e36c61d05679845ead833c11fe2a8efe565d7e0cac6d92b73eecd6663a5e8598:0b4897ef020da13d0b8fd060408a:987c40a7feec7f3bb92f4f159e916224b963374e8815a615a2653aaf175f4de0a0ce63f30aed60761cad310bfe24934d11372881b3b2f6afda7dbd7a99bbaf46f3fb4890e902776e704bee74ef77b0c8217b80bb2ae833ab2d3e83d0166cfebe
|
||||
420594483d27e3b09be201b4d447480d41e61748f5b1ac863ac5b5b8349986f6:66f576cac592391bb05fec22e72b:b121a5242c9ac3c6ae0c5801b9823bcffcab4b9a0f5a507196e29c55941397c526f9c47569bc5f077bdfee8085a7ea0200d31cd16c8071c5a8816e3b2fe90f196fbfc4cd3c1c0bfdb972e93f88b1e9b10ca5853f79c780ab6b799819f71d8830
|
||||
3a7f21f4ad9cde926ff41a5b657d3665ca5391f50427bbbb397c7197aa465e34:0173f8cb823680bedb605f5faf82e8:a3bb11f1159447f09fef369475daf7cf10d51cc93f060d737d77a24268139f121a504ec373d094ce92f290d43a87f1ed136939270f0c1c33d57c285173ba3fb448990e908e5bbc9fdcf41096dd1684a5d0ac811ff3c85ace8ec7c35c80fd2d10
|
||||
7143d5b6a3a6475478b34b5317d3acd571bab149986804b50381cc326550689c::b04c05cb1b41a8a683b07fc0c768496bae0b4ec8f4b509101e1c581297ab12cf6341df1cfc5496cd7d0edd5f41903f9e1440e655fe2e9ade8d2e09ff4ba64576423a00712b6f66112d72b792c2238944f6b5a6d12ffca7ead0ad1217c2a68ccd
|
||||
5107fc58427eb056153b4c8e26e41b0de05d78339821f91bdc56cfc3f261f62b:00:8ceb7e8cbfbf9b3b04e70bc7e49df066799e6ab312ac17df4603c6af4ec3f5a0e0f6caa19916cf711bd15b3f54bc2837046d7e85b97f5570c3d13f1dc5d791db9a9089b61264aff25f3f4c5834dc41e1ca6d91bdc2e8b3bc33cdf4a5c3909f47
|
||||
d9cd4f44d0f610aa8d8bebc850b98a54a1a7d6965882d053218751922c8adb3f:eb:911cd633cfcf561e24754efda0ecd89483d058fb715ab3c149fe64d7f7ad5fc6269271364d195ed4694dc32b8bcabf57196ec07d0aeabc11a601c2b19060c586545a9e784c009fb60995825068a6271c9b080b35bba5e8fe65b0d7382dbdad9f
|
||||
a145fa89810ea439afe5263b79444c5e12629c04fc4a29feb802fd4707e1e091:0a09:949afe2cb8d74dfc2b160302a1f8c44c80480875821f275b73798a0e07adcf358f985b2c783f692b6f2195e0419d213d0fcbaad2355ca05aae0371b071bf805122152d106392a8c9c51f16a2399a2396fea84049ff4d3a171e02f092751b9d09
|
||||
bc386e77aaad8f0629cff07f33e35a1b989f900c58b971dc9c09a4edd0a40cfb:e910:b183d730da3d52afd07e28689e8be17b0df5d89cfd74c4ad2edf796fc905924ffaca94cd633554213ead81efcef7eb3c137fc90b5906c1b5dbd2c195c68634b60ac4f7eb7ec6df44e4b0c9da9cc2d9ba22e8701722b1eb21ada0f98af7334e6f
|
||||
d94a000a40b98acc52901f8ca0ccdfdc0497390eb7f5cc739c91516b219cf7a5:00e3c8:952405861daa32b0f47b829e08b4cab83bc8effbbf62a9e0a640de551dfa64a4739454312df4bfdebd84271dd0b43cfa15e2cf940e2c15877f7a2f7d88ed097ccbfcd60195b32029ff0498ec23a2acad986c3d201a707afe169ad5d7fa724ff9
|
||||
48b4784f1e9e070b95cb181904e3ecb7cf6234e327e2666ba598b88904cf0960:48ec55:ace8ac4ea4040d382f6bd290498940bffb49a25bca1903dfec23f0eca9edc0848bc6c1e6c1ccb7ea71732ff09ae369500e944bd739ebb760e41965978d0ecc6815a1174e3ec4c9fdd5967656953714b0c31f5a546fbe4505934f77628b554363
|
||||
c28db28412e04efbb248b34e127ce163ae3d7450accb3e493f24362d9e01fa85:09aebeb0:82ad16e002985a12132955fb034b8222f8d45a4cfe8a3def59e1ebb6de074d89980d2314cc7ae3a79795c29bb1d3394114cf923376778654ab8873c3ca0436f04b6ef0df0b8b7cf25eae993dc297e9c0375f64aa8c49188baf8687d630fbb2c5
|
||||
4bc72dda1b98ff6e1722ebe5ca4ae11940483948f73a6d44b87cdb93675b2cec:9883069a:a7499c26f769bc11a65e32e3c274987f5d272242016069882a28946854af8625eaa38953f1c35e4c8af78bdf3652a88a10a7c1a52be2dde6af14fd6298da96f8b7c9329b5cbf4e725528fafc813a8bb6151d7e9fb15123c7b60a2db34463fde9
|
||||
25e95bec8613b28e98fc135365d012acb38c72551d71f41989033a59aabb75c3:0d8db89e47:aba0be6046df2a730f3f00284c64b8121f77c0b10cdf8ea0f60c7b38e1a2cc583acbf5a27819ce7e510503ade8149ca8198793f2d84ce1c071655da4795a7281b3dbb6a522ba9a091f8b5a37262ec1a43935fb89bf0a9dd2ae59390bec7c605f
|
||||
fbef93b0f2ed1ff76d1bd67b99044cf6a39b435d5b623ad8d8ee28169b0a2393:2547fa99df:a572707d5056e22c14d314e741cec99e03b5d6a086966fe39d952812694ee0f673e23f13cf5518b19d2bdf3779fd53621719c43e1112f5ab9c2838ff299773b4308ae52cfdfe0dc672ae42fd7b9f13813149b9024427def6f4a72c1dd4f97a7f
|
||||
4e1a6cca760e6b46c90b190da25d4277d07ed704d4377a414a0d7aebb38c5962:05161ae4b3c2:b72f19ff530219ff699a73e523564e36254e73eaedbf22d9c7a354dc3945dee58deb89a1c095779a12df44a1b4a6c9560001d978c499eabb966d477560f5d171276a44c94bbebe3461c2b6c576f0e3861e9ea76931a4f3949399a514b2a49b3f
|
||||
cc3869b0f33e91780e12fc8d1d240b950776ca546f7d5f212f9c8a758ba9bfbc:5692a95e3a3c:90303542c2c4e4be801a2079acf3dcb5017da834d4fbc3a65e4ca90fc2ab2b3ff0ef98fcc57c16f6de07b1ac0b7bcc6409b7cd60e74cfe6545bf54b9e8585b120381994a5bc3f572490e3a5eda3e5caf4c7a6e8238d65f858a8f5665c29d8548
|
||||
c5bbd44adad8cf07a1ee21a19ed6648edcc0da89c8cfee9d04eb74340aca2829:00c21979aee718:b682fe36524f3ea36039a3d5e9791db535e410f69c55d4e7c97c28beab85fbe10a09fa806f783d634aca926ffb4a392b153094e6829a642bcd13c4664a97a6bc23d87c0f356530fee3411e910a92a52e221e61edc03d18d3ba9bdf325f57b9ea
|
||||
4b063743b91c96b198a889e2d68cc9cafdda87c4e49ebc7904eb3fe516e78a15:bdd472d8486708:a28e113b23dbf0c2faae0ec7755c9f826845f85827ed0e3d4d2033c818fbccba06d5645db7afe08ee18b92a2046019a911f3fbfdc84ed796181c76506c636e4f07a32dd2b6b4cc03d59ca0272dbebe4f2addd35f9b24ed7d880314ddfd30b9c6
|
||||
2d7b35ebbe2e91a94cd857f349142b354941e34c30916493307068d8a6cf2ebe:0ea8b5464c8ffdfd:824252f8347eb299d1105323304e2b7f6985ed6ab1860baee4a5cbcc476d49db56e1d834f5f6293d0e1c1143fa60bccd1551af939d5435d514afa710ff8f3dd80b76bd7c75f728c9abe76c636ed588f406efe7b509c1b747496bbb6b18ffefb0
|
||||
3e88b2286bc087bc7f6476652e444155922e8e239af3a1ebaea2ee36cd0dad1a:d0e9ee7908a105c0:ab625e2a5abdc53a9b882caba06492c661233c7e4fac9bafc8c1db9ea44cad455cbfd618e917703fb732e0540e3208fb0f198420380962c078d15304a7bbe9c1a259e8cf2ec49fdca4dbb39ff69f1ab5c58f97fec90e1756666115fb0b63219c
|
||||
5e3d08aeafc50403c0de0166ae2fc6f39615e6012a04f03552e6c8869a702a5e:02f34cdeaa6f8e5cbe:b5b111acd6950b2862627f2d0ba6fe5b24cd3541be4ee2ae887c86ddcdd480a0a09c8003f9639c88b35c2d233b75d2ee101a66be894f438e4e1dc87e9bf2c80db7b34a3486c213281aac163a276f0850ff8b6d7681ee73a7b2fdd0f7b832caa7
|
||||
276b38d5aa2723f4cefa785cfc077559323a5c98328b3caeff1884c0367cab4f:4dadca20bba5e6776b:8a8b0a03047d87df975304f41187c006a310858272d6aa84f37c8f4a44e78ebcfa70f968dcf5b5a6b308c9aceb8bc9b4113901fe031e4deddc7fd40d85759b38df88d8e22b29166f2deae074320a742ef10610cf4793255ebf5eb456606c583b
|
||||
fa5426eb33077b1b099667fe2d695b2711a44266c22788ebcc3bf1c5237f4809:0139fb2a7aaf639501ad:8f8139e53fdfa59cacde3b5821deb75c7b6f9f153b0e9fdd044ce79bbb7770398a8061278359d97f63ef52c4fa41da71170db14a99f48ee899702e62beaf09e2be453f9bb8adefac38dad3076835a9e12ef6b7f48e30413d53e728938413d9ac
|
||||
11c5ef8785ec31234ef347fdce2a1534a49967245245f9523c319938daaf0a53:b936534e227a3c5cf272:98ceb3072a2bf95c95c6523451c3ef7f2e5e852e7e3ef77e60dbd26ae6a4020230c558b1d044792e48f6ab2cc755143102e9a2d79e2047d90c1c1dc2c7e1d0befe1b9249bf9026f74e4aa229c6798721a694fc1204df374a52465fd1d85360bf
|
||||
dd9da6df2c114902adaf5c0b90f0a30ed497da31233b16b9fa0ba48aaa9c7d18:073f5b3bcdf9fc462d6fa9:aa11b0e66c652b9a3b638da4a6702d10edf774f6c704c0ae427805f01c3532c5e2ad48dd0bc345ccfdc496635876177b0ca105b77a4dec4a5d4d1b341bde0e61a04e38d88cc91e707fc269e2a47fbdbfeb536fb999994ef2fcb9b5f42eaa8c26
|
||||
e98729f51b9943d4ab9bfd2057feb4725c962a72d6af4849da9a79681858beac:d364782af1bd76a74a5860:b0bd5532f822f65c5ad300a13fd8a8ee76784f9295eb98a630082a708473d40a4a390e9857888d0c4cda282699ba8334173b25ef088a7b4d23967bed053dab15b9f6f9b1ad071a71e7fff14416b2cc7f59437e0a0e3bac09b695f27f44d56d8d
|
||||
33a895e2e1d632737f6f11ff5a6857f971cf37873c1b95df2cab888c6bd582db:0acf93e56587dc54e775f1dc:85f3f86e176ef7934ded7f5cbc419f99d08727770b7172645b94c235d73f1311451dcc54a8735e8d2bceb15caa53a04a0a402b14af9e7b49323cdb728b004a09a695761b5051eb944bc861ac7b656c162d0e4a9578559883f9419b3033dc0c83
|
||||
826621ca2a9a206f403d92d3cc6ae1df509ded64ee5a37749473fb4089b21b24:21f41b0f651e420444caa733:a6ae1976dcc46c1f038fc300f0236c07a375bbf3ec982fdc492c14132aa2107331136ab4de36917da8680885e0c76bf912f6faa529581980d9c379cfa4d5786b8a64adedecd5e7c7f3ef4b273ce886ca916ca87d3d8d0cd004c437e6f6750410
|
||||
356522ef840b1ac132686085f346558a94f2943b48b75988c2cf512cbdfeb678:07b7666dfcf9541697392813a2:903225b431efb0f426aa962edb914c1182e6bd208aab9ffa0f9e502ad0edf4f7cd1fe139ec67597c0c9ba2f2b96b890216aadc7fca55a57c38b236056fbe2aba71f033b143fcc73abff454269a8de40e7c92777c5b0391fd3bb4d6991de096bb
|
||||
2724b641a54745be949a396375b50735b1e4bba8b5c9df6df1ada80c4f9edebf:7e959c39b0c4f95e90e05d4e6b:8359dcc9a9c233b2d871991cd5587f83f2544bd1c0bf83e006dbe148609ee40c5fa7c72e4861061b6e226d8a3d28736d08820f8f8cbde45af586ce9877e8783df86094e0b28d3cd40c04fbf5450961d26e9b61901ccb80e457da79ee1687d53a
|
||||
bc4227976367e5b3906682af2f4c12e2334ec96b02f84ab8adbc8d6df156ac1a:0b3d791d2ef361575d5e20daab74:807ea26a6d18624ed13f0ba940f914d1552550054195d9cc82125dc7c5c77d1923af45e062971a92224e3d31a8b4f7c10a65d1dab9bb7a526181cfd9ad17d0c1d4b571a0f0ca4f67fe967940a37bf63a5c96926da314f0aee4bc157511c94eca
|
||||
e62a19ed9fcc12b49964ed582e1e4ec9206ead22fb3352802c9ed124ca6fe99b:353c45917ca7cc233906934ca47a:b31abceb41227aa7b3975ed26822644e601edd9fef05fa590b36d9e5db58f01677ab7607ad6437ecf93ccb6d3e73f8660a92edc51a8db71dc75702d4fb984ce458eea6be357bcdaa1eacf6c0860afb389c6cfa76a137ce6dfbc8b73f3f93814c
|
||||
de94b6b59425a13582b2884b5643ef3dc0687e562324ef16ce60ab915ee999d4:094448d5868efe496f6416638e3b6b:967dee15785d4825563c26be491b30e1e44235e5618e2139dc419d031528900afa5c20380a61d58df87c7a40c2f7b17b174c1547c6a73836fcf2ba795dce10b1a00baa630021529d5e7dbbb31e8f24d36d202b1b76af9738a2e7e39b1789f5f1
|
||||
beacb441f1d0df37e9ed5eb637f722b63d43a051279c6b4eb0896356448f922a::866a698873fef803bc935885148c8c84996881585e21207a2479f55376911c17eec651d876efc84419556abe1a169c4c17b290a8dce65ec8321c43676f5dba40edb177adf83f61b27d91fb1cfd941e160db1e53be16a56b9e4f6b232b8a66810
|
||||
a2e573f800a2ebdd162cf8140e809b345840b21a5902dc2e866f9115b8e4cfd2:03:884754c37d989ab3a8f22760666d109e39f02fd43b7d9f6b56b151de79a5ccd04bb7e161917a5db009c466bd646d9e9f0b014fc58d0b8b3c4fcf7a696117c085638196a060012a4f2c98fea11a8dba8300edc7b8d0d6caf64e2f076040adc729
|
||||
69e59beafbff787b405498a1c4258811a7855cee59cd24100cbc29e9a5338890:af:9899c81ec05be74370c381d6da9e537ab96b94a3a140baf23bef0a8ed396c885ac83355c0e4636f16fc609de2954952f110a5a9e928729a7baa9bfe7cc91151f1243e4e3e109c6828dd3a4b847fbb9ec0b950640dec5706e09fd97f00ea85c89
|
||||
3732ed34c394b61efbe6762470519f7cbc01b3238ccd16bb3811e76f78b28969:0f07:90b715bae0ff837187f346b8312a03450a61a6bc13f4b65e788570e4d04dad2098f29ec0024b4318792c4d276c5ebb8f176555d6eda15b985714cf68946d40f8ebd451e9d01ce383cde7c14e0701cadc68fb0bfd3d43f69ed3159b24f73b0ad5
|
||||
4e4e37b3e34497a6a2b9f29dc48bcc66c68b108ab5ae3ec7de6b9163c8089f53:dd2c:84386746faefd739875a51b6bf0a3f10ab8fd8ac06e7f9d3fc8be1140416429c33ba4e9617982cd9bebbff8b3b40f06c16627e316c6f4828d771314e56b2ca0fdde30b7ab5d7ed0c2046c70d1416cdbaf68a760fc9c3f16578a588c743f1e5e8
|
||||
840f953ed1e17c5676dbf8969c92416d2806f74fe0cb8149cb85366e51f8ea56:048edf:a4a727d126f064d49c6abbd0811ba5b728b03770fa4abb4961a56cdcd60a2ed14b7f52385697ac524166a9279957595a0df740cf1c8ae3d5974fc48bd23383d87ff96f70e62ffc5ef1455f2eefe1fcf99b8c07d063b8ccdaedae3a43ec37645f
|
||||
ac1eea17174be2d21133916f1649763e7ba8c183a23f7f01e8595c413fa9f796:8afdba:a541bcf10e028b7359ce47a7be89a8614483b9ecf852251376367d871f529ca6c279348def2ed815e56da57f148e551c021c0de8ba3295dbd8fbaa1fd4a95178549a12ce54503e5d65c281533247cc479c1eb3706f2c52430697a851776a6117
|
||||
0d64a2304ab3d884c758a634c8e02a39df28d28f45326ffcfa227a4d09d317dc:0c57df3a:8e666a9f47f8b5f671e69c2aa41bc1db30a397555509fba48b1d017010ee52970b190bd133ff5a33459b700602a13df20079005abf2d1273d64fecb6ecf0bb61c6f1d6f6cc89666aa102accce758bf8fdb2fcd82fb1fd7955c1256d18c7da5d8
|
||||
90496775585cd9ebb1f11a7cdca5c9f5aa77b8f54cc1a04629744a6efc793c0c:94d33984:ab566969389b382ab1360b06691c99089d5bd4a0d02d92ecfc47af5b6bc14ae6699ca2f269b970713e6e9f36d300e0f90bbcf2dc71feb1d47db99916bfee0608c15c9a8a714f84d0b16073adc87967aa1c468d4d3a05d3a8639043496db48d1a
|
||||
897f90e55ce1dbd9aa3dc72ceb0f890d63e97326df0e7049641ba99505ff3f71:0c5acbd21e:865d3cb8351aa4545138a31662a9f60b31fff86186021c4cbbbf46ceb0ef9365634ac48700a9516d1a3b49294b49cf8b1705cc8f57f3d814d827e1ef915bbc49285cdb83ff61a334b7296bb1d7f6bf0c11e39e84360809463aa15372b198d33d
|
||||
d7d223e28a2fdc60e284f4a894cd713fd3b5106396db99eb77fd0e3ad3450be2:644b9aca5c:88dc952ecf62f5f7f875a2365a61ec00fc4989056467454246dfbd4e5f559190b98a744e28e346ad1859853db7de2a69100c648fa64b05ac0529958ca80b7f97c386d3009ebfc6addfed32bb55f216b1c3b1294d0a14722bf4edb19da6d9272a
|
||||
be1fd55d56ec81af5becfc86a2e0cfb7378223f4cb272561648c230d27d68c8b:095c77b908da:a44e6cd4a6081c9c71948c6be2755ac271c1069ef3772f4470a7e138b596b75d7cb055e99950aa21eaa5cdc35353edf010a04dc39554730a690af15b998404456935b9c52d52e9a2467db9916d7824aa9746bb850b000d5bb9d22bab1dbbbd17
|
||||
58298a7fde5847078d221ac45fe91d7a5cacbe6facc6b0d1c1c846bdec788bf8:71d0ada1a638:9017e5e0d91dcda6a826f290bab407db01e3163d4f1bb47d6a2b9d8ec455006e078dcd193f8fa27655f6991b512e92130fb0e9b6d168a7d1df3c63b476cac683c10e061081c54ed0c648a07616a1de01f0a39d5b8c68bbea4253b1dd260e9a2d
|
||||
1f7d8693d3e0e5f58120e12386830ab8278db6af93a9aea5e317009d5c7abd90:0a9f29d796cf7d:942bffd18f0d166ecd548b79aefd37590814cfc02d793125d7e01f3f46e83ac50c9e4d4a7d7dbab364411ee8c3f57c200d05c047af7859678c8776d98de90f6830a74649d917ce8f950242b05a1b33f164970bd1aabdcc66f2070536ed175d9f
|
||||
e116f7c628880c89edddfe2ed67a99f4db7c1702271a635f9c62731574f3b07d:b46ee7f0cdc911:b317d9515771038a31d1574a61a700da81d5e143648342013803272b60d71bd61d92fb4d5b7018b9dc4ed0227b56d30c14d51c9d84914f6d53ebf296fe8932d086a0cd27bfcddbf8239b465ff9be41cfaf9828db2f6c4c263f09a57c596016a1
|
||||
2ef3156af349a534508e4448c8eb2cb32f38bf79061ee4960affee1592fe8a33:0f8cdbb84ee69ea1:b115204c338f8930528efe7c5d3a4a42018fe1944b738edf3128cb04c804ef73ffa2209f0f5eafe2d825ce73604db3a503cf47cf3b34b613ed5fe10a3f5e9de3c27d8ef2a3894d1287fc045b01b97a4d17ceee0f03d31b81cded5a52758e1fb9
|
||||
6087f431c3cf8344bd95e09c9b5891150c4dc82ab8cf49d4edb3445848525787:06930ab93e2941fb:b8aaa4968d500766e2cc15e5eb3b6841bf90fa2208b2699a1498d824509b79bbbed192e2e5142a6562d49826d020dbd30d06f3804c321b560c81b4ea02e281ae2d635cff541f82e5cbe2c96f22dcc8b41eb01e4c21957092c7bacfd4cbd367e3
|
||||
98fa7b94541ab754e6eef1b67714e04f324d25ca7fba72263a74ae705fe56ce2:0d5319c4e04c108b43:8567089bca14b9cd5336556283f3119c70bf2e2a649eb72e33f2de62314dee52d270854382e2851c23704c90cedeb5440692b5bc28d71364fdcb9e13ab51886a30e067ebc7b873f26ec31c1439d58ae87add0cbf970587b4ff929bf14f86fd87
|
||||
ce27a9320ce91af5cb25db85e49ef48c889cd2cd1876f1bd6a33577e89d01a9d:456502c375d647b287:8fb103caf39bde35ab3c9dfafe2cc93355d7745866ca999d93b311a5231f0f898fcb8da735c3b90e5631b043616229cf13ca431a73079e16dd4c65ceb54c8ee06f4c60fc0c143b734391db2422d035cdb98c637b2f53885da84f61fb3fb4fd8e
|
||||
82ec12537be95b513523032353671a31bfc30fa874394854f18fc22a83edfc08:0a0ad6660fb6b9360758:996c339f73f866376bcad28a65440154c3161013109a95fae84f3c7cc531bf072e661f099a23350aa84a7c64bafefe730e14c5939e4797decc3c49af9c5538d1d02fcf27f714c8de6aa14c4a51da1891ac076014b0c2b0d114ff673df6b39495
|
||||
e20cad243ac923e408ceac8701dc75b6a4901fba43ccf5d8834569437555ec88:93fe4e5afe409cf5779f:a7ea309bb1ade75ae5bfb6215aabecd1f734b85ec9523491aeafb7d3d31e7cdcb0c79e15838a75a5d5800685ee36bd6b0117a46d2dc83364f6ee1bf0164ffa02089f6d3d921b0a1aa9d62eef95bc17257aa2e1f4c969e546145acac32e05ec2a
|
||||
88385fadd78308b14a845f904a22fb8ef155004381611983af1a97dfbadd4cfe:03c626fa420c8ca589184e:a4fcc04bbbc6b6f5a67a4f20ac9d3359d2fe9d7e26e276276a50361c3f5d2aba5a5945b774c4eff1ad2bc0b71039fd56120cff7dce6d31289ee09df92f7216e327caf71259c55d095694fe892e8974296f18a370596ee0f9d77fd874df5a49f3
|
||||
2fa90ec2bb82b7523acd10174510e9e1f3cd8d914d5df1c1add12294f4201b55:31116320fd8ffbf65edf62:820727a6167a7d5d65e5718f3a8003e0a1bd19f82439f2398a0f7ea568aed18e177c8562806785afbb4dfc8625c5ab9c0a0afca6fc38309ed111ce68f3780fa49b1a48625fd960436c8033b0fbf78c58077633a964a513d95db6a0f22471df30
|
||||
fc9bb165ada95b2cff23e332c0b9f6b6a9e684c38677584e4c5ce5b16c78f66f:0915034dc3615740490ff744:ada0438d237761c391d3aae9b10fcd0b8ed03ce09e8414859642050dd88e03ddd69691efa26cf68b4f4aa051e01cd9cd08efe42f1346c952bf5d47a5a9cccb8c23fa2a667bcdcabcbc04ecd444e5215dba18397a22190424358b9fabd7579591
|
||||
3967f728a6fc9b97d815a8a76c5df791d8252cb44fbab8a52bee1aa59228f26a:96dc2c37e0f9efb8f177873b:8dda9f419ff275828006089d7e4e1342b5eb17dcf7c51a2fb7c5cd6ebfcc2064b987c3b2d100c166b8499d82d705905511682b1210616c5674b11c3f6b472fd92879b1532d2c60a2c05535d8224832efcfbae410cdeeaff9428af6159f07621f
|
||||
c0bf0882e28ae3ca0f4c9fa55f0e8c48f5cf4d5fb888005be03941b083e2bd75:0dfc99df951bacffe40d17fa40:a666faf2bbeae3b0ad5f6cc18fe8bddfe7ae77b63c2befe176bc2f18a3ceecb78e91453a989642b73d2a18f5e1883df2173313f9e68d968cf3d3cc1d65c6ba4da837f2a93a4700afe996d0b7e0ab3b0d9f9aeb4da76db355ac99f21901af7c9c
|
||||
926f0d9c037207a2ef042fc01888164837e999b668b22e72d1e090c030f2ac14:042fd0ac0b5cea9347f8bd6696:88c271e6a393f3ef9cfbc3022578197690d93b1cd7e90e14e1aedaa8f4f9372dd0df5f2be74c5f1ed7adfa3467f5e94617729d08f0339e0c7fcdbcf74fd0e375f0ef85df8c4a7031f4471af7bdc72a692f290c2f23a361a471e7f8e7892d688f
|
||||
1cc92dbca460512827bb5ae49b2f5ff3b0aa556470ebc4e08d06006794fa95e4:0cee8bc396fe6f2e44bb0c608bed:923613d71c89bedf640787cdb031492059309a702410308fd904c2b29c6d78a75d2fd5e6758e759a91c07c8dc55310120651f208d9f1184ce02f02bf287c20f85f05c660838b0baf964b1785dd4b72ad18a4c86410f14d79d5b6aaf8a8093c9b
|
||||
2d94bebfcdf3a3adf32af3036e6098bf51bacf2d8e97fbe53ecaf302fa176076:c1f3a335ed2198cf83bd46540c43:a40961a61862c95fa04a3327b9ba847d986cbc1e789b9a4009182a1921fd728e051cc3f879ed6fbae67b7ed40f8cd3350de0d91a7a58cb9ed3c9b6cf267a84c7021a7a83931c0834a79a432e75a8ff1603e974a3654da2bafdfd8d122ed6c4ef
|
||||
f223072deeabef9597b9404215b3b1f0ded345552365df94d7b92bb78593fe26:0f381538c11dfb0ef9bd9b585ef2cc:a6e4ca0f2fabe91fd4f67db13d8a52de8b09455e22b001246f9afb50dd48f31c3c249644544eca500b2042c824460803182c8a0d92797f16f3c45cbe16d7e40cae3b1f7102fbbbb45fb2c66413890836b7cdd3e685348bca4987c514c05c2299
|
||||
16c7703dbff0bc635e0f1616f11ccdae9c50c48030b50d17f40a583f4b8a8a42::afc773584e0e50446d784ff525edad44d3c774dd141e13346c7db92be76f34b356844d2206473f75c5b66f8962ec9cef082fa43a05398273bd066ce7620f59ee443f663c9c278ae9a64f181d332eb0cca3fbe3616bef48d5ad881f12301c4012
|
||||
2bc35b91d5972e10dbf0160c138ac436079d6c43e4eefbf3fcc9c06767bb31da:06:af9b614adc4f80d9554b0167e1361d0ffa413f9f36e16930d707c4da190fb83701984916a2584fd24d65f350a6dc93e606877656031e847dc9b31485213ddd3c318fbf27addfd209e80deef7c23e174be7e7f34639a1af09dd6fa018bd4ed1db
|
||||
4456acde3242387fce74bb201e5624c1097686e3ac5ce87d212f8fd1d2d4378c:85:99b45e3ed1021db6bc869adb284f025bb9b930a8782dc9646d1d5840b67b2c1e957b6a1200cd67e2c05b1e2a016bf58206d0067bfb59b47946e99fe99bbf8d1689ec20cb1bcd3312600b24e8345e43c1cac95f03c742e9cfc4629465a35643ee
|
||||
c7ac6866828fceba14885b50fc4b29275565231008fc8db8c33f0730493d7cc3:06ea:91e550dbc99350364a3722e2af1ef9f922d5c094faf39ec18649ad190ef13835038eb5201e25276d3a5f124f049610810e4157b4204d19da5a50ac38d913990e5c9402bffd51576f5509047922151ee6fbbc5760e588cef3296a805d65bf2495
|
||||
9cc0024de1d0ecc27bab1155a99eed06131d3db6748b96f72d04501a22a4c13d:7a98:99890de7549f5fcf2c0699b55bcb0bcee6e080339678c6e5b61cf5d00684a4070f8bf9d16d2211b0292cca06c255da3406623a84cd0da3044c8f190e132763ee62acc2f60a7a20a8f8c94a237ab72fcf4c9b08f4d36c0198be735ec0698106e3
|
||||
3a161a88b0434351865dc4b322ae2db86d38e26b354a8072ecb007bd8397c08c:0eea0b:962a8ab094224d90904a2a48d6b63efff7bb702ed993d9cac630dd219988cdbe264be9505ecc24c82821591e3c70b385093bb570ecfeb81f50f66f74b9d19335fab464b782f98ec0c7233b83b895458616ba23205478c048c7f43187353e5159
|
||||
93c67f70207b5707881aea562f3261f1415c8b6af2e4c95590986b12c8a7832d:5a638c:b5cc880c00f50b09ab58081251e0001ad0f091c1fc3ecd9cd6753004d1a2ad82056b61cf7ac64b3fde2bca8ed02b5cdc052035ab0af0d17f78f6ec97dc55b67d2a0a4627543b0b42aae01bade567eedd41154d254036accbc2820a280e8dca09
|
||||
4b9dfc470db493399476bd07c30de9b8463e6ff0e1a5461d7cb25920caa74e9e:0a77069c:a2ea5c1c6917c5c74352fdcbbf0320c86f09ad9ac0c29b44941d1c2fcf094a03105a3a1c7829f70c21282750f912f64a16b0d077b38e2e94b3841f3ddbbe29538bed0c3a15b9f3fb3365812c26423ed8769588ccb0ce4007500bcda73e934c5c
|
||||
182d193ba7412d09b8cd8b486c72f7163ab7d572b08b95b4aa8e41118072b63f:8f1ee0cb:a9eeb586a08cad5422594833c53cec8826e1ee18d62eb695226df14d68e8973c067c5a88522d72a424170733b05723f41925613e55bf883158bbb77a7944e867a71f5350558594467329ea8381f9f8ea064300f46e0b2436fb177af41b6465dc
|
||||
4a373adf9961e3ef98ae8009188e948ce309d3582d63d850a13fe2c304dd7065:08cbc08a8e:81664cbbfbee94ca1c567f980bb39e686e390e94e527a30d4e4f3dbf3f5a48c4adb5fdab07992633788a45750ddbbb1916d3ab5e77ee56d166ac28e81a3246dcb5e183a3692d7eb7f192a0487d219d79514c657d20db0c4ff1ac3d4af5fdf33f
|
||||
520133987bf9a4eccaa0da403ba0a0c44dc83d25ed8345976ddecfffe207fcb0:4f03ee2e2a:a1d71d93ff022b10538bad0580e0fe30c476abcdab5ed8114a5ebf1c5ff8b2818bf6bd963c9c69f38ca8fca73da91c1612fbdb7b3323396d1b27afcd05679dfb360bf79dbc0e2bd9b6afb8e64dad7f00ff9e50ed2dd199f53c45a58cc2d09cc2
|
||||
b1c3f3d39a291acc244b9614b505030bff47295fe8ded28458d62248098688e7:0d737d559984:938ea7b377846d5240727a706999b425e6420c2a2a6ee4603c66235b3b51301dc784faff02a55ada8d150f4ce50e2bf205b4853cd9a98e757027778d4884b4473f346d2781eb3e8d38205b9c93627e0bfea2c9414cd9da5e689526a076d754e6
|
||||
f43d8e4018143f5035008a1009a0a489a94791a6767051f13ebccb137638cf6a:914018d2bc6f:81c8d737713175d9539decce0ba7ec9fa8b2a1bec70bfcf0e4226fbff51a19cc3efefbfc157746500e5b316f836051a109e150906a9febc7d2471de3c94fe037b78bb68c8e8960d5793003b0545779554358b1c3479a6bd255ee9db60be335da
|
||||
5bbe2155b1ca1a37d4cb75762dabccdce894cec7452561bac5651f92b4ac1fe3:0cdde70cf2bf55:8474f69d3efaf0005b1c62e54650c5d49ea3117049923d108f00e7c67f9d57aa967e675d5b72b9619cafeaeb11ff057f10f952ddf35de97125a062874492aa7f90836bc1bdd1b942d877410706547f6a0ae0ab25e993c3a4aa65de7a180237a6
|
||||
123a411e3335542f1dc720d8354437476c9b81d568daf817b2db83981629c3d9:fd67000db24705:a5d2ff627c1a4b7f52a71d158926f2949aae9fdc3e206f9fdf9117ec88ae8c8cbe4d23d17c713b2e09e82d557d996dd71847b7e86bc1c74015c09abeeafb72873da6945e0fdf5b5cd91c622a1cfa9bc258cc6eff0125377d2e44b16630360381
|
||||
85d140112f583ab2ff5c04d3f1f3944efa63b0a34b3979bf9aff0a3241165e5a:01172e0a402caa46:8fee0a19081b0adef228ec3cce2cc069911ea922845aef15c97a919108bbd860cb241be476cae84e0149abb305981ba4045751809c174b2f89fc36e0a498ee7f325e062121267ffe27c0261e416ac7bdcbee7f9d19e37cf54fbeb9e57f23d26c
|
||||
498848b5fd8f6fe2008c039e5f972cf6d6ab2e909acd594ba8cddf7da3233167:640a244c2ecbeb26:ace162832f586c088e3ef9d558228a99c6475e1eaed0f875c512fed3f800f876409e8f5b28831a6563511ec1edbf16fe067cd73fa6413030c0892e6c67b8ca3a14451642959e50cbf18c7e6c0052dd7ac9b98145694cb5f83feb506c13238811
|
||||
f861f548187099ae19317eeea551a0fb9881485e0adde98e6a260d7b4604c755:0fd5f6f3687ebd0622:895925c8e398877ee20341c41f703d355eec6e1fa7cb8d9062379f693f489c325c94105d41848ef94b1700b5555128720ce287d39ea693dcb99127d764385d242f4a93c6e038e3defd68f791a013e3a2958e7ceda6f695969f5bfb31af826ef1
|
||||
370642447c6763c7f6ce66b4013df4b46a4de47ca9f34d2c2d1fc130b4e2c9e1:395025a2d1d88e0635:9259229b65e9c1e95c876ecac32d33acec12d5ba217d9aacb30f52d356338954feadd6f9fec797d974b79bc2ef0e41f60a82c3d4cfdcf3c399bef3ff026f0082cff184a3300d0de1a863fbb498cbc6c87e28967ae4250c8ebacb8ed3f4a4bfd9
|
||||
3832a8792ea9547d7fc8b3fe8d5df2f35660272dce5bf6ccbe7941ac7700b194:0ea3967ee0e677fce984:8b135befb49f3fc8ca4472935515b3c90c2d9ac6e13b0ed17da999fbdd5981b580ca387c0ffb3fab8c2b44559cff3c211404f792f243d32f5b775db0bab61ee480238540e2281ef0953cce57457d78ea77ff1342f695c16af62b5a9f97e94961
|
||||
9e052fc09efdc8a7ecea5545fce87ad988a42239e321ea0831ada650a76f63d0:4ab7c1680c49b6f0283e:b0c39cf1b66cb385f233ce068e7f81099d4de1709a8cd4bb7ace275ed49373217f57ec5cbf894db122481e2eb5f660440e612edb12feae8d7c82508f057bf4c5ac8e6fa54f400c261ca07416570295e6c9732be44c30c1f522fb3785292fb1aa
|
||||
409c98029b0014f8ee36ba5040a2e96deece25c632cf48aa97b58d95648be79f:0177c0f8148194de3e6477:91e278b31a481a970895fc0e5dccaa045a05cdd70bda29f64ec239a5c4fc0d5189eb6c3cbce3affb3294ff4251a2e84c0633db7d2363fb53250d092d3f3b59121dc6ad3a4a5b8455f280b03d173d71a18275e7817936016e7db825540885081b
|
||||
3174e6aaabd794e3be89e48aedd486ed9d9aecac2f8a3d5f7a179883765e136b:fd1c24b053333e986927fd:93176d6cb5987f8912225a77166a053f7de30345718c4c081f8dcd71c1b6560a4c8a1cd096e7458073d7b42084b0ba9b15f2e378e7a2a2f3df81b29e594aef47e877545b9d56e2427364e4adfcd6dbe5d6ffd2190fea6e791a695a3135e8111e
|
||||
49d8f16c96e0bd5c151911fe49f9d3125188b7461168badd9256066863490658:0e2c67ef06692c5c01ee1881:88b6ccc20bcb7bfda958cdb80ef563d8364655bac922d065a05579b9de50b81c5d72e9f3b7ec5b9a64306e79d67d48500f89e80ad1e06c1f94a2cdf1d4522ef8f19467cb7206cda4ba86f9c111cabf418cc60e46b3e2ca8e841f7cd2e4d66e48
|
||||
18e90f391db888d2173eece1532225b2c3eb31011772322ef7db8985db83c5e2:3cf06f1f7bf3f1fd5c1c34e0:98090e7c9348e3bddaeba1a423c1dd5d34ff397d48d9b295f7725f9cfeec287c34ef032a7a1017e98aa2b8db17d8894e0b0b72559cbeab2a3a6af1ff5b33373c24602ebf87c2341078d54c6d825ed6f883693a69b46dedb9e812fbe8627eb5ce
|
||||
a0d5b377c5a369e4770764fc42f1ab42ccd51b004b7456ba5855e95ddfc7415e:0a108577679816e1c20fab4aab:98392ab3b9465813b8b4af724c28dabf62be6327e89ffd80c328f251634a240adc893bee6ceb320a6e6dcc5fa4638dd9191aff395421d815486f86afcc3b8289f0f3b8456ad10296e31333b1ada7b3869e4562dfc8b81353ccd1410570af383e
|
||||
2f171467ffed42c0aad55340a3d29f42132032b4b1c0cafbbfe267d5d9b9d975:ff0fbc6886cf7119c13cac2047:ae45427a61d4acea474a57867e7276ebaf10f1065478c6cbec3861d14ccfcee9d91b2052fe976e145ef8c88518fafcf812cb469d5881ef0fd8ef4563634e498041e7cd2b0d269bfe592aed2d7cae94f9787de91528e15a18efa74715bfb8f289
|
||||
675943c3633d2906548ab8fd99d59c902c3613d931316631df8d892d21971f37:0662c76b721017ba5a676b65b5b3:b44020ba6ea5bb53aecc946da3422dcab97cbd38678043b3476d3e5570d5c55a422f5bf7981fad6ef90b15a13bb904bf17eb164d5e1824956e3972bf2971257b051593be8d2bf025b01938216bd30d65142a40575d2d2c752f5eae06e5eaeb1d
|
||||
de79fa57fa8b20d8f5c9d8760d7edbccac81d0e5ed6db1c1db3bc70142439f81:4f8b3d03d50fdd4428d90fb8a1ed:969eeaec43bf81bb610c8c0447f41798bc7e558f3ea5aa9147a4cf651e22fc99b1dc9343a4c0efd61945b1b8a1d92a8419abd88575783f0577c2548069e7f28661bcf702e851e9de2d735e6ad545bee4765c6498a2f7891ebd0690986aa3de74
|
||||
cc8b41dd972d7003bc57f06efd6287ba04ff88b532a1c9698337a751a29ea562:03c572d43837c86b2d5ab55f602e34:88974f2099572aff379b487255507b9347097baadf902f283c58137e4ed4af6d285dd63ab554ccef48790229d4fac08e1717572c08c4dce5cc7530659aa23eba1a680fff339f138f11ff06b0817623fc904b4e95ffa6ee186eed0376a933e3f2
|
||||
3d9039b23297115dba92c2d7b4ad882749bbb47bbc378da7a2e2308fe97762af::b68588e7e1728b2fe5769dd3c936c209059f05d89fa1c97e73de4d6ebae113d25a3ff152d59e8e080644b9ff405b60a5016700792fd7b3e15e1644492d5b49c9d529f75ff089483975721c55405de377d4601b983d4151f86b11ff7f259b2880
|
||||
360763630528bf256a78b0c8c0b8bde84d1db6edea081a08d7a9903eddb9a85f:0f:b70412c89cc6041217c89898d9b4902c38988d80018c29b5c9a160f641ac867a5b6e7460efe1e807d4b16927ba057abf0ad222b6c81eb3cf46bb18ea5a1f4640b62ed3436360f359baf3dd7443f68fbd490acbd7a25901bf8f3daef6157426e5
|
||||
673f104c3fda7085a1b6afb787ea36dda1eecb87b45375e4204919b19bdb4f0d:ce:a3097321693e4dc5f56e65dc09014d517c942f731b6d7aa2a630cbaa5616298cb589475134f6333c8fd3c95a851c9c0b0e300cda7469cfa3e426b1d393361738077e8610f1b61ea3a75ed9dc8d7c19e804dba831dcde6fb5180b272f336f7778
|
||||
165ccbe631f789288f180dd299248be3019185f0326aa2cb5c01b0f381e3f952:0858:b42d38c4a715d724b80a8ba5ba3937d51ef57b82c473fc0e363b68112c61fda0c0a51d252d5893075db03f96b46458420af2dbb16b2890d27ece7710d5a16bc208a903ad0ad0173b35c40bcc0abff09c6871382ad936aae88464320667edfd45
|
||||
3e0db6c5848ea10fbe9dfb7fa937cf57e96cb947f4d47b4f7dad8da1ad718a75:b696:8c36fccafcb8221a13835cf8f2ef34207e62d82ae27b35f9c24c16c12ef4d24d45a253a0ace92b3260dd63a65b4e22af14fd2e743951349401c770ec39060e926ba076a4ae4ba9be82761b891956254585fa8e2e73ab101a8cc80a1ab523c16f
|
||||
4efc64d65ec6317d11c98d299bdbb177b48afafb4c77276885a5bad19e86d898:0013ab:84ec582064629c67d7f2cd893680e3f1296c666fc23309063d8a26144cd0075abaaddddf2835f41ef971cdb3c8929d1e0637b3bbfd1f01ee02b94750fead9b7f331e145cf4551bdbe9e77ee7e1a7c18b81792d02437f23c8a14b90883f044961
|
||||
933acbc31ca221dad3677776e0b7b64a2d7c1ece1d9440b84cc7e77449d97163:5a0113:b3639ac94326001c2902bdd4ed1336dee41dbc5168b3d42cc7e7ddd6d05dcf4828ecd6d03e24388665f37413b48352b203b35af2f073a6b2e613ee6ab62e026643dfa8c4c4cc93126b2739517487d3bce138ccddb16cff765608bbdfdff8e3ab
|
||||
1aa58cd8fe28bc296fdfca3d5af1841643527635d15d7ba7b9f52781f920503c:0276ce38:8fa25f0ec24a14482424d15128f365ca70adef6c497c6a3f2ad3a4129e0f44d0552adda1a2242e7936cc38014475a2d402794449c904b8db66387d4fa6cf938f040e9adb4ca3fa0fba4ae3c2a4a74881c261d3dbfb155ea922936a2238789c33
|
||||
20213ae7786685baef612a88f1ef008dffb01d185db47155300674b934996e68:19b49baf:8678b97a5e70c4287cc3ce06ff106b811ee91a7fe2b4a2ada4e933c17bbbfda3fa99be519b80e2a3ca7777eec5127e740a3c6d0f5fbbb3a125aba2bdfd325c1de980e2ccbc34023d06afcda5c4d70cabef3961cd73eeda2ffbfaa7146848bdb5
|
||||
0bac27b57ca7109489235328a4fdf27ba4a84ac64cb3d89f76c3c53d148d83d4:09c78eb62a:afc8c64ca140a9706f972319f48edffee405b5eed88dbad1c946d9bb8bab48bec8547d066bde4cb1cc7e99eee0d53faf01057a99d49bee517d424d0156e02486733a0fd4f311951ed4420a2125b092fa9041a3e01fd3e642e1c2d844b3dd64d3
|
||||
ea697bc01540c1b708f03c9369d2ad84ac0e2ff087a17bf9c868b64d6076c6c0:f7a40db556:8151b87961f1f4f6a33993469131fa7d02f40ea3d7191fc72aee115a8bf8454e57f12b7c67fa4b56fcfb9767276c2c66017a25f362b59067a313a61fac44e6940bc82e445befb85e20f51f1444e54e353d641537b21e6d6fb2a865228b58edd1
|
||||
fda3cf7385cf4bb164645966281a68f0447a0fdcd1ec7e29bf7313860f4a3394:03d67ac13cee:a81fd948bd75d7fb1b9744bd12a83288d1794bce09a8d7a63cb39758b090296356c6290e3cd63d4cb1ebbd169bdfe1991063d3d323bf87af7c469cb5947a93870400518b1a48d96f98456211b706be4073a34f0af9bfeb86c2adccb64a005656
|
||||
b382e373ff8da48348d38447ef337570a9bafd91fab84c2a439f71880ea5e480:c89dc4de1fbc:8d3a93cbf8bce107c2dce15ddac3252792dfe2e9e9050e99100aae4d42ca1687b1a72abb372da6e01c761e7f37b6c6790ec1260c4e8052c3b21fc21c8a79fff22dbced49349f554f8efb34a20bc1aab0f252660eca89ece65a67097622e7e547
|
||||
d15ae5df8ca3c5e64b38d24653fc9bf809534e98f3c7162623b65d9e70d8d2c5:0ae955dfad3573:8ecfa5cd8164188ddbad47d4f7600751116c6ac96d5bf74cd8feccdd59366ceba5bceaba2241f0b4355b9ca7b3963e54076b31e58f38d9a8dbf8ef3c887514fec83bf2f3a82fa2d2bffced10fe7113388c86d9d441867b499caed7e2e45cd98e
|
||||
bdb098850c669ddde2018c5cea8b9f9a356c317868ddfc61ebf120d2ee9b6627:477405798be816:8108bdda52459a367f5ee6d2fb92a62144d2134cbf6755cc6c6ae4fde35f3d8c5a1ac6b5feb86290a76ffe5972cba95118dd0d095473e8d2e2027f9f10c67a388b4802f190ef8900eaef3246b26e2ab642c317a66f9571f339744dda641b9248
|
||||
f62d28e6fc14fb70610228ee1f4f7d2e065e05caaf18b766892eeabf7c05f924:06097228065d18c0:b82649d765a3fff1d72ee15b7906579c4f0a608fd03adb53c6214de0e5095383ea00b66d75b6a12b8c433251af517a61053d69cf3dfeebdd929275984d9d3b1f8eaadd308824fffb9bb4e5437539a2f3d68330dbb021524a1d0d4523e5adc34a
|
||||
27826e51f6e6446065e32c5b8e60aaf0109008d4eb9c2336a437ecb6fe415618:4f77125824409934:894e75525f6c5067b5274b52f39f3d0991ef27b9396ead5555f0dac07d07a962f3cc00b20e8324b69873abff1f8285d904428f93fc42354bb1cc50d95149014dd25b7000b161c4dbaaae54a90bcbde7e8c99c2111181e4e1e08648b4bec81cb4
|
||||
984cfc344dd7da98dc2dcbcb8427e0195989e6b06380dd7dde4d73e289e1013c:093906e014849826bb:b74fbc224468498a680c923e1e9e8b90e7e55e44ffcb7cf1d2cf5bcbac1964e238025e30565325f2a1854244fa4ab233091c298fb9d5ba2525620f900960819e21f2e3d364eeac93f82c1303a141b883d29bbf4de2796daeb41f20cc05f0cd48
|
||||
ac47cf8395c7858c34896e1501c77b1028bb00f15721d92b4208a13cad2fbdac:3c65e5bd83d0464de9:9414a283286bb2eebde7e00e5510e406e96750a3595a104b0a003fa181ca8022eb688d6ed5363711b59d86f3f0cc696d08ee7c6939b9d7ec44a8d597880e15700d6cdff0fdd76ff814797b7b928f123a8dc22425333064821992559baf7f4b4c
|
3
test/bls12-381/bls12-381-scalar-test-vectors.txt
Normal file
3
test/bls12-381/bls12-381-scalar-test-vectors.txt
Normal file
@ -0,0 +1,3 @@
|
||||
:0000000000000000000000000000000000000000000000000000000000000000
|
||||
aaaaaabbbbbbccccccddddddeeeeeeffffffgggggghhhhhh:2228450bf55d8fe62395161bd3677ff6fc28e45b89bc87e02a818eda11a8c5da
|
||||
111111222222333333444444555555666666777777888888:4aa543cbd2f0c8f37f8a375ce2e383eb343e7e3405f61e438b0a15fb8899d1ae
|
8
test/bls12-381/go_pairing_vectors/go.mod
Normal file
8
test/bls12-381/go_pairing_vectors/go.mod
Normal file
@ -0,0 +1,8 @@
|
||||
module blstests
|
||||
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/kilic/bls12-381 v0.1.0 // indirect
|
||||
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1 // indirect
|
||||
)
|
4
test/bls12-381/go_pairing_vectors/go.sum
Normal file
4
test/bls12-381/go_pairing_vectors/go.sum
Normal file
@ -0,0 +1,4 @@
|
||||
github.com/kilic/bls12-381 v0.1.0 h1:encrdjqKMEvabVQ7qYOKu1OvhqpK4s47wDYtNiPtlp4=
|
||||
github.com/kilic/bls12-381 v0.1.0/go.mod h1:vDTTHJONJ6G+P2R74EhnyotQDTliQDnFEwhdmfzw1ig=
|
||||
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1 h1:a/mKvvZr9Jcc8oKfcmgzyp7OwF73JPWsQLvH1z2Kxck=
|
||||
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
1
test/bls12-381/go_pairing_vectors/pairing.json
Normal file
1
test/bls12-381/go_pairing_vectors/pairing.json
Normal file
File diff suppressed because one or more lines are too long
29
test/bls12-381/go_pairing_vectors/t.go
Normal file
29
test/bls12-381/go_pairing_vectors/t.go
Normal file
@ -0,0 +1,29 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
kilic "github.com/kilic/bls12-381"
|
||||
)
|
||||
|
||||
func main() {
|
||||
g1 := kilic.NewG1()
|
||||
g2 := kilic.NewG2()
|
||||
gt := kilic.NewGT()
|
||||
p1 := g1.One()
|
||||
p2 := g2.One()
|
||||
bls := kilic.NewEngine()
|
||||
out := []string{}
|
||||
for i := 0; i < 1000; i++ {
|
||||
res := bls.AddPair(p1, p2).Result()
|
||||
out = append(out, hex.EncodeToString(gt.ToBytes(res)))
|
||||
g1.Add(p1, p1, g1.One())
|
||||
g2.Add(p2, p2, g2.One())
|
||||
|
||||
}
|
||||
bytes, _ := json.Marshal(out)
|
||||
fmt.Println(string(bytes))
|
||||
|
||||
}
|
25
test/bls12-381/zkcrypto/convert.js
Normal file
25
test/bls12-381/zkcrypto/convert.js
Normal file
@ -0,0 +1,25 @@
|
||||
// This can be done inside of tests, but ESM is broken, jest doesn't want to work with it,
|
||||
// and ESM itself contaminates everything it touches
|
||||
|
||||
(async () => {
|
||||
const P = await import('micro-packed');
|
||||
const { readFileSync } = require('fs');
|
||||
|
||||
const CompresedG1 = P.array(null, P.hex(48));
|
||||
const UncompresedG1 = P.array(null, P.hex(2 * 48));
|
||||
const CompresedG2 = P.array(null, P.hex(2 * 48));
|
||||
const UncompresedG2 = P.array(null, P.hex(4 * 48));
|
||||
|
||||
const out = {
|
||||
G1_Compressed: CompresedG1.decode(readFileSync('./g1_compressed_valid_test_vectors.dat')),
|
||||
G1_Uncompressed: UncompresedG1.decode(readFileSync('./g1_uncompressed_valid_test_vectors.dat')),
|
||||
G2_Compressed: CompresedG2.decode(readFileSync('./g2_compressed_valid_test_vectors.dat')),
|
||||
G2_Uncompressed: UncompresedG2.decode(readFileSync('./g2_uncompressed_valid_test_vectors.dat')),
|
||||
};
|
||||
// Should be 1000
|
||||
// console.log(
|
||||
// 'T',
|
||||
// Object.values(out).map((i) => i.length)
|
||||
// );
|
||||
console.log(JSON.stringify(out));
|
||||
})();
|
1
test/bls12-381/zkcrypto/converted.json
Normal file
1
test/bls12-381/zkcrypto/converted.json
Normal file
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user