Compare commits

...

245 Commits
0.9.0 ... main

Author SHA1 Message Date
a8ea9d757f Build noble-curves for ES2015 for Tornado purposes 2024-04-18 14:08:07 -07:00
Paul Miller
efeca9f478
Add hex tests 2024-03-27 12:12:01 +01:00
Paul Miller
8ad2f9a185
Merge pull request from carleeto/main
Fix montgomery sample code in README.md
2024-03-25 08:28:17 +01:00
Carl Menezes
be576b4c17
Fix montgomery sample code in README.md
Resolves https://github.com/paulmillr/noble-curves/issues/132
2024-03-25 11:17:28 +13:00
Paul Miller
819514576c
Use jsbt for tsconfig and building 2024-03-17 14:19:18 +01:00
Paul Miller
a0e398489f
Release 1.4.0. 2024-03-14 07:29:55 +01:00
Paul Miller
85d194cb93
Bump hashes. Commit build lockfile 2024-03-14 07:27:17 +01:00
Paul Miller
1830124ca1
Merge pull request from xrchz/patch-1
Update README.md with Ethereum example
2024-03-08 00:21:59 +01:00
Ramana Kumar
72cc640bb1
Update README.md with Ethereum example
Shows how to use the DST to sign/verify messages with the configuration suitable for Ethereum beacon chain.
2024-03-07 21:15:01 +00:00
Paul Miller
32bda7926d
Fix bls short sig verification on hex. Closes gh-124 2024-03-01 22:22:49 +01:00
Paul Miller
38a4ca1e6b
readme: new noble library 2024-02-28 05:07:38 +01:00
Paul Miller
07f7e53e31
Update build deps 2024-02-28 04:26:25 +01:00
Paul Miller
537db4a968
hash-to-curve: adjust dst logic a bit 2024-02-27 23:34:30 +01:00
Paul Miller
a70501cec4
ci: add upload-release action 2024-02-27 23:34:03 +01:00
Paul Miller
89aaf264c1
readme 2024-02-27 23:25:10 +01:00
Paul Miller
e93caf9567
weierstrass: improve Entropy type. Use new utility. 2024-02-27 23:25:10 +01:00
Paul Miller
0ce103bd6b
utils, hash-to-curve: reduce code duplication 2024-02-27 23:25:10 +01:00
Paul Miller
6a85252dc3
Merge pull request from dhrubabasu/add-htfopts
bls: Add `htfOpts` parameter to `sign` functions
2024-02-17 17:50:59 +01:00
dhrubabasu
a8503d6819
bls: Add htfOpts parameter to sign functions 2024-02-16 18:03:27 -05:00
Paul Miller
8397b26b45
Update github workflows 2024-02-14 02:10:37 +01:00
Paul Miller
8c39a6ac5a
tsconfig: change module to Node16, copy noble-ciphers change 2024-02-14 02:10:32 +01:00
Paul Miller
a1a7dc9cbf
readme: Add link to gh discussions 2024-02-05 17:45:16 +01:00
Paul Miller
ba58a282bd
readme 2024-01-21 14:45:32 +04:00
Paul Miller
c4c479047e
Merge pull request from ardislu/fix-broken-links
README: fix broken links
2024-01-20 13:47:48 +04:00
Ardis Lu
2bf2e312a0
README: fix broken links 2024-01-19 23:39:52 -08:00
Paul Miller
0a663391bd
Improve gitignore 2024-01-14 10:02:50 +01:00
Paul Miller
7be1dfc55d
utils: copy concatBytes from hashes 2024-01-02 08:49:40 +01:00
Paul Miller
37eab5a28a
Refactor tsconfig: use inheritance 2023-12-23 18:30:48 +01:00
Paul Miller
2706fe9f79
README: mention secp256r1. 2023-12-14 16:20:40 +03:00
Paul Miller
b39b0d1daf
weierstrass: improve error wording for sqrt case 2023-12-13 15:58:51 +03:00
Paul Miller
4007ee975b
Release 1.3.0. 2023-12-12 02:21:29 +03:00
Paul Miller
f8af434b9c
Bump noble-hashes to 1.3.3 2023-12-12 02:18:35 +03:00
Paul Miller
be8033a2d8
readme 2023-12-11 01:43:32 +01:00
Paul Miller
b3c239981b
readme 2023-12-11 01:42:57 +01:00
Paul Miller
18b0bc6317
readme: Mention zip215 2023-12-11 01:40:43 +01:00
Paul Miller
30f68c9e54
utils: improve isBytes 2023-12-11 00:04:11 +01:00
Paul Miller
ada1ea5a19
bls: fix types. Closes gh-101 2023-12-10 23:42:42 +01:00
Paul Miller
0a3a13b3dc
Fix typescript esm config 2023-12-10 23:32:10 +01:00
Paul Miller
26a4fd4293
weierstrass, hash-to-curve: ensure to use utils.isBytes everywhere 2023-12-10 23:27:15 +01:00
Paul Miller
9db14fc6d0
utils: fix-up isBytes 2023-12-10 23:26:57 +01:00
Paul Miller
8e6c19de2b
utils: make equalBytes constant-time 2023-12-10 23:04:01 +01:00
Paul Miller
4ffb68853d
utils: make isBytes more resilient in weird envs, improve concatBytes type error resilience. 2023-12-10 23:00:49 +01:00
Paul Miller
008958364e
weierstrass: reformat after new prettier 2023-12-10 22:58:13 +01:00
Paul Miller
1c535a3287
deps: Bump prettier and typescript, reduce their sizes 2023-12-10 22:58:01 +01:00
Paul Miller
b8b12671ac
test: rename hash-to-curve vectors, remove colons. closes gh-102 2023-12-10 19:47:25 +01:00
Paul Miller
2f1460a4d7
BLS: Refactor mask-bit settings, improve encoding resiliency 2023-11-10 02:55:16 +01:00
Paul Miller
fb02e93ff6
ECDH tests: comment 2023-11-01 17:09:37 +01:00
Paul Miller
c525356916
ECDH tests: allow padded private keys 2023-11-01 17:06:40 +01:00
Paul Miller
a4abd8a202
ECDH tests: quick and dirty ASN1 parsing 2023-11-01 16:54:08 +01:00
Paul Miller
c19373a0b5
readme 2023-10-20 15:34:18 +02:00
Paul Miller
85006ed620
readme 2023-10-20 15:33:27 +02:00
Paul Miller
fae7f6612a
README 2023-10-20 15:27:08 +02:00
Paul Miller
36894729c0
readme note on csprng 2023-10-20 15:16:43 +02:00
Paul Miller
eabab627c7
Merge pull request from yhc125/patch-1 2023-10-16 17:36:43 +02:00
YoungHoon Cha
e1640eb74e
Update README.md
Added libraries missing from the code examples.
2023-10-17 00:28:21 +09:00
Paul Miller
7f851873f9
Merge pull request from secure12/main 2023-10-12 12:16:16 +02:00
Eric Ho
02099b9b4c
Add weierstrassPoints return type 2023-10-11 19:16:56 +01:00
Eric Ho
3b14683806
Update weierstrass.ts 2023-10-11 18:41:11 +01:00
Paul Miller
47169740c6
readme 2023-10-07 15:19:21 +02:00
Paul Miller
45c7cb560d
readme 2023-10-07 15:00:11 +02:00
Paul Miller
b36bf44f4b
readme 2023-10-07 14:48:25 +02:00
Paul Miller
30763066ac
readme 2023-10-07 14:43:51 +02:00
Paul Miller
911801ec0f
readme 2023-10-07 14:39:45 +02:00
Paul Miller
8ba25a1c40
readme 2023-10-07 14:36:25 +02:00
Paul Miller
43a06b669a
readme update 2023-10-07 14:35:00 +02:00
Paul Miller
e7720c1609
readme: Clarify ecdsa recovery 2023-10-05 06:46:31 +02:00
Paul Miller
2da6abb336
Fix x448 private keys: must be 56 bytes, not 57. Reported by @larabr 2023-10-03 01:15:43 +02:00
Paul Miller
4752ab1f1e
utils: optimize hexToBytes by 4% 2023-09-25 20:22:57 +02:00
Paul Miller
f58002e6d4
utils: refactor hexToBytes a bit 2023-09-25 19:21:18 +02:00
Paul Miller
d0294bb2a6
Clarify build:release script. Closes gh-86 2023-09-21 23:07:52 +02:00
Paul Miller
2b41e387de
Merge pull request from sublimator/nd-impl-group-x-for-x-in-decafpoint-ristrettopoint-2023-09-20
feat: impl Group<X> for X in DecafPoint/RistrettoPoint
2023-09-20 21:06:36 +02:00
Nicholas Dudfield
08850c2d6a feat: impl Group<X> for X in DecafPoint/RistrettoPoint 2023-09-20 16:23:41 +07:00
Paul Miller
ce7a8fda55
bls, bn: clarify their security level in comments 2023-09-14 03:02:10 +02:00
Paul Miller
728b485cd8
Merge pull request from arobsn/main
Improve `hexToBytes` performance
2023-09-14 00:17:42 +02:00
Paul Miller
eaefe9a272
benchmark add utils 2023-09-13 23:59:12 +02:00
Paul Miller
c935b398fe
abstract/utils: reformat hexToBytes. 2023-09-13 23:57:34 +02:00
Paul Miller
ddad219e7a
README 2023-09-13 23:29:05 +02:00
arobsn
1d83bab27d
add char code based hexToBytes function 2023-09-13 18:14:13 -03:00
Paul Miller
4be208e4b2
README: add new project using curves 2023-09-10 21:55:06 +02:00
Paul Miller
77bee0d54e
ed448: clarify why there are 56 or 57 byte keys 2023-09-10 03:00:51 +02:00
Paul Miller
6bcab6c24b
readme: add example for chash 2023-09-07 23:44:46 +02:00
Paul Miller
7befd5f881
readme 2023-09-07 15:34:29 +02:00
Paul Miller
8f78471703
Merge pull request from sublimator/patch-2
docs: audited by plural firms
2023-09-07 15:34:03 +02:00
Nicholas Dudfield
17294f4974
docs: audited by plural firms 2023-09-07 10:59:48 +07:00
Paul Miller
3890b79e7e
readme 2023-09-06 20:22:45 +02:00
Paul Miller
2acebc8176
Add new audit of noble-curves by kudelski security. 2023-09-06 01:48:09 +02:00
Paul Miller
1e67754943
Merge pull request from randombit/jack/check-short-sig-in-subgroup
Fix ShortSignature.fromHex to check the G1 point is valid
2023-08-31 20:26:17 +02:00
Jack Lloyd
156a1e909a Fix ShortSignature.fromHex to check the G1 point is valid 2023-08-31 13:28:53 -04:00
Paul Miller
ccea23a712
Fix README. Closes gh-80 2023-08-31 02:16:47 +02:00
Paul Miller
8661eef949
readme 2023-08-29 16:29:30 +02:00
Paul Miller
4743182bf7
README: update security section 2023-08-29 14:36:11 +02:00
Paul Miller
5c477a88fa
README: update security section 2023-08-29 14:25:58 +02:00
Paul Miller
df9d461adf
README: update security section 2023-08-29 14:00:53 +02:00
Paul Miller
5c21fa3855
Merge pull request from randombit/jack/update-readme-for-bls-short-sigs
Update the README to describe BLS short signature support
2023-08-28 17:02:19 +02:00
Jack Lloyd
6661a7db7b Update the README to describe BLS short signature support 2023-08-28 09:22:25 -04:00
Paul Miller
cf5f2268fb
ed448: add todo comment 2023-08-27 18:49:55 +02:00
Paul Miller
1d5286ffa7
single-file build: expose more methods 2023-08-27 18:49:55 +02:00
Paul Miller
e31efd91d8
Merge pull request from randombit/jack/add-short-signatures
Add verification of BLS short signatures
2023-08-26 00:50:42 +02:00
Jack Lloyd
c5e0e070d1 Complete BLS short signature support 2023-08-24 16:38:12 -04:00
Paul Miller
0d7756dceb
Release 1.2.0. 2023-08-23 20:00:32 +02:00
Paul Miller
b716b4603f
Update lockfile for 1.2 2023-08-23 19:58:55 +02:00
Paul Miller
d7a139822d
Release 1.2.0. 2023-08-23 19:55:35 +02:00
Paul Miller
fb6c379a26
Update README 2023-08-23 19:48:52 +02:00
Paul Miller
eeac255c88
update noble-hashes to 1.3.2 2023-08-23 19:45:47 +02:00
Paul Miller
925fc3f810
modular: adjust getFieldsBytseLength 2023-08-23 19:43:55 +02:00
Paul Miller
eb8e7ec964
hash-to-curve, weierstrass, bls, ed: upgrade h2c comments to rfc 9380 2023-08-23 19:43:14 +02:00
Paul Miller
e7ac5e85d3
poseidon: refactor params 2023-08-21 18:16:40 +02:00
Paul Miller
d285fcce06
modular: Document FpPow 2023-08-21 17:52:21 +02:00
Paul Miller
ef667bb404
poseidon: refactor validateOpts, fix tests 2023-08-21 17:48:34 +02:00
Paul Miller
62749382e7
poseidon: remove default sboxPower: 5 2023-08-21 17:04:58 +02:00
Paul Miller
f90e871725
weierstrass: prohibit (0, 0, 0) in assertValidity 2023-08-21 16:05:53 +02:00
Paul Miller
f049398718
modular: bring back 1.1.0 hashToPrivateScalar for clean diff 2023-08-18 23:14:08 +02:00
Paul Miller
ca99179bd8
bls, modular: lint 2023-08-18 23:09:53 +02:00
Paul Miller
1545230ee5
modular, weierstrass, bls: use new mapHashToField 2023-08-18 23:08:46 +02:00
Jack Lloyd
b082d41c29 Add verification of BLS short signatures 2023-08-18 15:36:17 -04:00
Paul Miller
2ce3b825f8
readme 2023-08-16 02:36:24 +02:00
Paul Miller
8315fe3580
readme 2023-08-16 02:33:10 +02:00
Paul Miller
9b7889e16f
README: improve docs for ecdh 2023-08-16 02:14:41 +02:00
Paul Miller
e8b9509c16
abstract/modular: add more comments everywhere 2023-08-11 12:23:52 +02:00
Paul Miller
d92c9d14ad
README: update Field documentation, reformat with prettier 2023-08-11 12:23:19 +02:00
Paul Miller
05794c0283
weierstrass, bls: improve randomPrivateKey security and decrease bias 2023-08-11 12:22:37 +02:00
Paul Miller
ca5583f713
ed25519, ed448: rename hash_to_ristretto to hashToRistretto. And decaf 2023-08-10 20:01:13 +02:00
Paul Miller
8c48abe16a
Lint 2023-08-08 15:43:31 +02:00
Paul Miller
08bb00cc8f
poseidon: prohibit sBoxPower other than 3, 5, 7 2023-08-08 15:43:14 +02:00
Paul Miller
1ef16033fe
readme 2023-08-07 13:54:02 +02:00
Paul Miller
113b6d7c00
readme 2023-08-07 13:48:18 +02:00
Paul Miller
5c3dc0be50
README: more blog posts 2023-08-07 13:45:38 +02:00
Paul Miller
e7d01f4038
Update README.md 2023-08-07 13:11:30 +02:00
Paul Miller
9a39625eda
test: lint 2023-08-05 11:25:56 +02:00
Paul Miller
af8462b09e
tests/bls12: fix crashes on zero messages 2023-08-05 10:56:52 +02:00
Paul Miller
bfd9ae040d
readme: add alt_bn128 2023-08-05 01:19:42 +02:00
Paul Miller
2bd437df4e
readme 2023-08-05 00:47:59 +02:00
Paul Miller
b0af0a8977
readme 2023-08-05 00:31:41 +02:00
Paul Miller
aee10c8141
readme 2023-07-18 09:11:24 +02:00
Paul Miller
ff92bafb6f
readme 2023-07-18 09:09:01 +02:00
Paul Miller
54679ff788
Usage 2023-07-18 09:08:28 +02:00
Paul Miller
ee4571c7a1
readme: toc 2023-07-18 09:07:11 +02:00
Paul Miller
fe7afdd392
readme 2023-07-16 06:31:52 +02:00
Paul Miller
dba2f0e732
lint 2023-07-12 23:58:30 +02:00
Paul Miller
52c5df0264
utils: add PURE flag 2023-07-12 20:28:45 +02:00
Paul Miller
ebea4a4bcd
weierstrass, bls12-381: adjust var names for typescript flag 2023-07-12 20:28:38 +02:00
Paul Miller
33a53006f7
build: update esbuild 2023-07-12 20:28:11 +02:00
Paul Miller
549e286ef0
package.json: declare side-effects free 2023-07-12 20:26:12 +02:00
Paul Miller
3f0c0b59f1
readme 2023-07-11 19:00:56 +02:00
Paul Miller
62205347e1
readme for finalExponentiate 2023-07-11 18:59:40 +02:00
Paul Miller
476e75104f
Merge pull request from steveluscher/pure-and-twisted
Add pure annotation to all calls to `twistedEdwards`
2023-07-01 04:27:31 +02:00
steveluscher
413725cfb3 Add pure annotation to all calls to twistedEdwards
This PR makes it so that if you only use _one_ export:

```ts
import { ed25519 } from '@noble/curves`;
```

…then only the `twistedEdwards` call that constructs that export will remain after bundling and tree-shaking.

Before this change, the compiled bundle contains all the code that constructs `ed25519ph` and `ed25519ctx` remains.

```js
var ed25519 = twistedEdwards(ed25519Defaults);
function ed25519_domain(data, ctx, phflag) {
  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);
}
twistedEdwards({ ...ed25519Defaults, domain: ed25519_domain });
twistedEdwards({
  ...ed25519Defaults,
  domain: ed25519_domain,
  prehash: sha512
});
```

```js
var ed25519 = twistedEdwards(ed25519Defaults);
```
2023-06-30 17:36:16 +00:00
Paul Miller
cf17f7fe01
readme 2023-06-28 17:33:13 +02:00
Paul Miller
49fb90ae9a
Add README link to new library noble-ciphers 2023-06-28 16:04:09 +02:00
Paul Miller
309d29a084
Merge pull request from CoinSpace/extraentropy
fix: check extraEntropy according to the spec
2023-06-28 14:44:56 +02:00
Evgeny Vlasenko
d3aa051770
feat: tests for extraEntropy 2023-06-28 16:12:44 +04:00
Paul Miller
5609ec7644
Adjust readme docs on sig key recovery 2023-06-27 01:38:02 +02:00
Paul Miller
af8c1eebee
Merge pull request from stknob/decaf448
Add decaf448
2023-06-27 00:32:53 +02:00
Stefan Knoblich
08ea57ce5c Expand ristretto255 and ed448 + decaf448 README section
Signed-off-by: Stefan Knoblich <stkn@bitplumber.de>
2023-06-26 22:48:48 +02:00
Stefan Knoblich
ee3d3815b4 Add benchmarks for hash_to_ristretto255 and hash_to_decaf448
Signed-off-by: Stefan Knoblich <stkn@bitplumber.de>
2023-06-26 22:48:48 +02:00
Stefan Knoblich
f471405798 Add benchmarks for ristretto255 and decaf448
Signed-off-by: Stefan Knoblich <stkn@bitplumber.de>
2023-06-26 22:48:48 +02:00
Stefan Knoblich
e3a4bbffe9 Add decaf448
Based on draft-irtf-cfrg-ristretto255-decaf448-07,
draft-irtf-cfrg-hash-to-curve-16 and the ristretto255 implementation.

Signed-off-by: Stefan Knoblich <stkn@bitplumber.de>
2023-06-26 22:48:48 +02:00
Paul Miller
c2edc97868
Merge pull request from sublimator/nd-validate-dst-as-stringoruint8array-closes-57-2023-06-22
fix: validate hash_to_field DST as stringOrUint8Array (closes )
2023-06-23 00:27:24 +02:00
Nicholas Dudfield
bf70ba9776 fix: validate hash_to_field DST as stringOrUint8Array (closes ) 2023-06-22 07:19:08 +07:00
Evgeny Vlasenko
c71920722c
fix: check extraEntropy according to the spec 2023-06-16 19:43:12 +04:00
Paul Miller
62e806cfaf
Release 1.1.0. 2023-06-03 14:31:43 +02:00
Paul Miller
6a72821185
readme 2023-06-03 14:27:05 +02:00
Paul Miller
8cee1f559f
Bump noble-hashes to 1.3.1 2023-06-03 14:23:18 +02:00
Paul Miller
6f10632ac0
Add build directory that allows to test tree-shaking 2023-06-02 17:16:56 +02:00
Paul Miller
b281167e8d
Fix utf8ToBytes in firefox extension context 2023-06-02 15:57:46 +02:00
Paul Miller
c6b4aadafb
utils: harmonize with noble-hashes 2023-06-02 15:35:37 +02:00
Paul Miller
aade023e48
pkg.json: Adjust funding field 2023-05-27 16:10:58 +02:00
Paul Miller
2e04d96ce9
readme 2023-05-26 13:27:41 +02:00
Paul Miller
79dd7d3426
readme 2023-05-20 12:34:51 +02:00
Paul Miller
ff5b231e31
secp256k1 & other implementations: reduce bundle size by 20% by using PURE.
PURE annotation helps bundlers during tree-shaking and eliminates dead code.

* secp256k1: 75.4kb => 62.3kb

* ed25519: 67.5kb => 51.1kb

* ed448: 55.1kb => 44.0kb

* p256: 67.8kb => 59.8kb

* p384: 75.4kb => 67.4kb

* p521: 75.8kb => 67.8kb
2023-05-20 10:49:50 +02:00
Paul Miller
648fd2cc07
benchmark: curves should bench ed25519 first 2023-05-19 09:58:30 +02:00
Paul Miller
f67134ca86
benchmark: add msm to bls 2023-05-19 09:58:13 +02:00
Paul Miller
6d0678b076
readme 2023-05-14 06:54:17 +02:00
Paul Miller
53ebde19ea
readme 2023-05-14 06:48:22 +02:00
Paul Miller
a7755332c8
readme 2023-05-14 06:40:09 +02:00
Paul Miller
5f0007ab24
readme 2023-05-13 01:31:55 +02:00
Paul Miller
1ee5a5c07f
CI: auto-publish to NPM on GH release 2023-05-12 20:53:24 +02:00
Paul Miller
708c0e14d5
readme 2023-05-12 19:03:17 +02:00
Paul Miller
624d7c9910
Merge pull request from sublimator/nd-sort-few-typos-things-editor-nagging-about-2023-05-09
docs(modular): sort few typos/things editor nagging about
2023-05-09 18:01:35 +02:00
Nicholas Dudfield
665ef2dd93 docs(modular): sort few typos/things editor nagging about 2023-05-09 07:35:33 +07:00
Paul Miller
acc1f26acf
readme 2023-05-07 23:32:41 +02:00
Paul Miller
3c4a25263e
readme 2023-05-06 22:05:33 +02:00
Paul Miller
e887d516ab
readme 2023-05-06 21:20:38 +02:00
Paul Miller
90e87f7ab1
weierstrass: adjust SWUFpSqrtRatio to not use exp operator 2023-05-06 14:38:53 +02:00
Paul Miller
5edafbac97
Merge pull request from sublimator/patch-1
ed25519: fix ristrettoHash size typo in hashToCurve
2023-05-05 17:48:08 +02:00
Nicholas Dudfield
554c94509e
ed25519: fix ristrettoHash size typo in hashToCurve 2023-05-05 18:17:40 +07:00
Paul Miller
7c11a021c0
Drop v16 from ci, it will be out in 4 months 2023-05-05 03:54:16 +02:00
Paul Miller
531b6a3a48
Adjust CI 2023-05-05 03:53:35 +02:00
Paul Miller
fb5cd9df39
README 2023-05-05 03:52:49 +02:00
Paul Miller
53a6d636d4
Merge pull request from legobeat/ci-node-version-matrix
ci: test nodejs v16/v18/v20
2023-05-05 03:50:00 +02:00
Paul Miller
42de620010
edwards: make zip215 false Strongly Binding Signature (SBS) secure. gh-40 2023-05-05 03:37:13 +02:00
Paul Miller
6621053c7d
edwards: ensure Point.fromHex fails when x=0 and first x bit is 1. gh-40 2023-05-05 01:39:53 +02:00
Paul Miller
9bee88888f
weierstrass: improve return type of sign(). Clarify comments. 2023-05-03 18:28:35 +02:00
legobt
103ba5f0a7
ci: test nodejs v16/v18/v20 2023-05-02 11:59:54 +09:00
Paul Miller
d5de5d2659
README: add more projects using curves 2023-04-28 02:46:07 +02:00
Paul Miller
217cf8c654
readme: more resources 2023-04-27 01:58:29 +02:00
Paul Miller
8e307d8f89
readme 2023-04-27 01:01:42 +02:00
Paul Miller
8c0018d57f
readme 2023-04-27 00:54:41 +02:00
Paul Miller
ca7f202839
Add secp256k1 compatibility layer URL to readme 2023-04-27 00:16:29 +02:00
Paul Miller
816077ac0a
README 2023-04-24 13:00:43 +02:00
Paul Miller
bc03a07043
readme 2023-04-23 20:31:29 +02:00
Paul Miller
63653255e1
ed448: rename to edwardsToMontgomeryPub 2023-04-23 20:29:17 +02:00
Paul Miller
895ee3a1a4
bls: refactor slightly 2023-04-23 20:29:03 +02:00
Paul Miller
16b31b9087
edwards: use bitmask instead of exp 2023-04-23 20:28:47 +02:00
Paul Miller
213796db4b
ed25519: rename to edwardsToMontgomeryPub 2023-04-23 20:28:28 +02:00
Paul Miller
049d3bce54
CI: node.js 20 2023-04-23 20:17:45 +02:00
Paul Miller
b2a04c2393
Merge pull request from mirceanis/31-fix-edwardsToMontgomery
ed25519: fix edwardsToMontgomery formula; implement edwardsToMontgomeryPriv
2023-04-23 20:16:22 +02:00
Paul Miller
cb5e9a6e96
Update benchmarks 2023-04-22 03:20:11 +02:00
Paul Miller
36af62357f
test: adjust ed and secp tests a bit 2023-04-22 02:24:41 +02:00
Mircea Nistor
88291eba33
ed25519: fix edwardsToMontgomery formula; implement edwardsToMontgomeryPriv; add tests 2023-04-20 13:37:21 +02:00
Paul Miller
848a1b0226
nist tests: add endomorphism test 2023-04-14 19:53:20 +02:00
Paul Miller
972e549dde
bls: no bigint literals 2023-04-13 17:18:39 +02:00
Paul Miller
d61c7ae4e5
Release 1.0.0. 2023-04-12 04:42:02 +02:00
Paul Miller
d3de7c8863
readme 2023-04-12 04:38:33 +02:00
Paul Miller
6316643f51
Rename bn to bn254 2023-04-12 04:33:32 +02:00
Paul Miller
7199f113c6
readme 2023-04-12 04:21:29 +02:00
Paul Miller
71f6948612
Adjust readme and ed25519 docs 2023-04-12 04:16:47 +02:00
Paul Miller
d3d03ff115
README, bls 2023-04-12 04:10:59 +02:00
Paul Miller
e2c3560686
Remove types/node 2023-04-12 03:57:30 +02:00
Paul Miller
4e9c40b3e5
readme 2023-04-11 15:19:02 +02:00
Paul Miller
09085d2ee1
readme 2023-04-11 15:17:24 +02:00
Paul Miller
8c4d781479
readme 2023-04-10 21:12:13 +02:00
Paul Miller
123431de66
nist curves: rename from P256 to p256 for consistency 2023-04-10 21:04:16 +02:00
Paul Miller
7503aff45c
README 2023-04-10 19:40:58 +02:00
Paul Miller
81e6046698
test: move vector 2023-04-10 05:23:35 +02:00
Paul Miller
30f7d78c82
hash-to-curve: update benchmarks, lint 2023-04-10 05:10:38 +02:00
Paul Miller
00665b21ab
htf: Prohibit expand: undefined. Closes gh-18 2023-04-10 05:02:36 +02:00
Paul Miller
5d54bba846
lint 2023-04-08 21:23:42 +02:00
Paul Miller
851af4f1bc
weierstrass: adjust 1 line 2023-04-08 21:23:35 +02:00
Paul Miller
6ea40d9dab
Update tests. Update wycheproof from v0.8 to v0.9 2023-04-08 19:00:18 +02:00
Paul Miller
8beb922ded
weierstrass: improve DER decoding. Validate curve creation. 2023-04-07 06:09:46 +02:00
Paul Miller
fe380da8c9
edwards: change strict option to zip215 2023-04-02 18:35:34 +02:00
Paul Miller
113d906233
edwards: change API. Add options.strict, context. Add edwardsToMontgomery 2023-04-02 17:35:03 +02:00
Paul Miller
65c0dc6c59
README 2023-04-02 17:34:33 +02:00
Paul Miller
ed3ba3de6e
Prettier 2023-04-02 16:50:54 +02:00
Paul Miller
d424c661fb
Fix tests 2023-04-02 16:50:27 +02:00
Paul Miller
31d92cce11
README update 2023-04-02 16:42:49 +02:00
Paul Miller
c15c964f77
Refactor BLS, change API 2023-04-02 16:38:03 +02:00
Paul Miller
37ebe6c40f
tests of ed25519, ed448: improve 2023-04-02 15:38:36 +02:00
Paul Miller
18eabfd3be
Remove unused devdeps 2023-04-02 14:17:50 +02:00
Paul Miller
19f04a4c1c
Release 0.9.1. 2023-03-31 10:02:05 +02:00
Paul Miller
d0c3bee4de
weierstrass, edwards: make points expose typescript x, y 2023-03-30 09:20:35 +02:00
Paul Miller
4244f97d38
bls: get rid of bigint literals. gh-22 2023-03-28 19:01:42 +02:00
Paul Miller
618508d32c
weierstrass, edwards: get rid of bigint literals. Closes gh-22 2023-03-28 19:01:00 +02:00
Paul Miller
3936449e7b
edwards: add toRawBytes to ts type 2023-03-26 15:54:04 +02:00
139 changed files with 267951 additions and 122755 deletions
.github
.gitignore.prettierrc.jsonREADME.mdSECURITY.md
audit
benchmark
build
esm
package-lock.jsonpackage.json
src
test

1
.github/funding.yml vendored

@ -1,2 +1 @@
github: paulmillr
# custom: https://paulmillr.com/funding/

@ -1,17 +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: npm run lint --if-present
- run: npm test
- 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

@ -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

@ -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 }}

18
.gitignore vendored

@ -1,13 +1,9 @@
build/
node_modules/
coverage/
node_modules
/*.js
/*.ts
/*.js.map
/*.d.ts.map
/esm/*.js
/esm/*.ts
/esm/*.js.map
/esm/*.d.ts.map
/esm/abstract
/abstract/
*.d.ts
*.d.ts.map
*.js.map
/build
/abstract
/esm/abstract

@ -1,4 +1,5 @@
{
"printWidth": 100,
"singleQuote": true
"singleQuote": true,
"trailingComma": "es5"
}

809
README.md

File diff suppressed because it is too large Load Diff

@ -1,5 +1,7 @@
# Security Policy
See [README's Security section](./README.md#security) for detailed description of internal security practices.
## Supported Versions
| Version | Supported |

Binary file not shown.

@ -1,11 +1,7 @@
# Audit
The library has been audited during Jan-Feb 2023 by an independent security firm [Trail of Bits](https://www.trailofbits.com):
[PDF](https://github.com/trailofbits/publications/blob/master/reviews/2023-01-ryanshea-noblecurveslibrary-securityreview.pdf).
The audit has been funded by Ryan Shea. Audit scope was abstract modules `curve`, `hash-to-curve`, `modular`, `poseidon`, `utils`, `weierstrass`, and top-level modules `_shortw_utils` and `secp256k1`. See [changes since audit](https://github.com/paulmillr/noble-curves/compare/0.7.3..main).
All audits of the library are described in [README's Security section](../README.md#security)
File in the directory was saved from
`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.
See information about fuzzing in root [README](../README.md).

@ -39,6 +39,21 @@ run(async () => {
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));

@ -1,14 +1,14 @@
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 { p256 } from '../p256.js';
import { p384 } from '../p384.js';
import { p521 } from '../p521.js';
import { ed25519 } from '../ed25519.js';
import { ed448 } from '../ed448.js';
run(async () => {
const RAM = false
for (let kv of Object.entries({ P256, P384, P521, ed25519, ed448 })) {
for (let kv of Object.entries({ ed25519, ed448, p256, p384, p521 })) {
const [name, curve] = kv;
console.log();
console.log(`\x1b[36m${name}\x1b[0m`);

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();
});

@ -1,14 +1,13 @@
import { run, mark, compare, utils } from 'micro-bmark';
import { generateData } from './_shared.js';
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 { 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 curves = { x25519, secp256k1, p256, p384, p521, x448 };
const fns = {};
for (let [k, c] of Object.entries(curves)) {
const pubB = c.getPublicKey(c.utils.randomPrivateKey());

@ -5,11 +5,11 @@ import { randomBytes } from '@noble/hashes/utils';
import { sha256 } from '@noble/hashes/sha256';
// import { generateData } from './_shared.js';
import { hashToCurve as secp256k1 } from '../secp256k1.js';
import { hashToCurve as P256 } from '../p256.js';
import { hashToCurve as P384 } from '../p384.js';
import { hashToCurve as P521 } from '../p521.js';
import { hashToCurve as ed25519 } from '../ed25519.js';
import { hashToCurve as ed448 } from '../ed448.js';
import { 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;
@ -20,10 +20,13 @@ run(async () => {
// - m, the extension degree of F, m >= 1
// - L = ceil((ceil(log2(p)) + k) / 8), where k is the security of suite (e.g. 128)
await mark('hash_to_field', 1000000, () =>
hash_to_field(rand, 1, { DST: 'secp256k1', hash: sha256, p: N, m: 1, k: 128 })
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 })) {
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_' }));
});

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();
});

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

@ -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

@ -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

@ -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

@ -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,7 +1,4 @@
{
"type": "module",
"browser": {
"crypto": false,
"./crypto": "./esm/crypto.js"
}
"sideEffects": false
}

130
package-lock.json generated

@ -1,110 +1,48 @@
{
"name": "@noble/curves",
"version": "0.8.3",
"name": "@tornado/noble-curves",
"version": "1.4.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@noble/curves",
"version": "0.8.3",
"funding": [
{
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
],
"name": "@tornado/noble-curves",
"version": "1.4.0",
"license": "MIT",
"dependencies": {
"@noble/hashes": "1.3.0"
"@noble/hashes": "1.4.0"
},
"devDependencies": {
"@scure/bip32": "~1.2.0",
"@scure/bip39": "~1.2.0",
"@types/node": "18.11.18",
"@paulmillr/jsbt": "0.1.0",
"fast-check": "3.0.0",
"micro-bmark": "0.3.1",
"micro-should": "0.4.0",
"prettier": "2.8.4",
"typescript": "5.0.2"
}
},
"node_modules/@noble/curves": {
"version": "0.8.3",
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-0.8.3.tgz",
"integrity": "sha512-OqaOf4RWDaCRuBKJLDURrgVxjLmneGsiCXGuzYB5y95YithZMA6w4uk34DHSm0rKMrrYiaeZj48/81EvaAScLQ==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
],
"dependencies": {
"@noble/hashes": "1.3.0"
"prettier": "3.1.1",
"typescript": "5.3.2"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@noble/hashes": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.0.tgz",
"integrity": "sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==",
"funding": [
{
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
]
},
"node_modules/@scure/base": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz",
"integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
]
},
"node_modules/@scure/bip32": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.2.0.tgz",
"integrity": "sha512-O+vT/hBVk+ag2i6j2CDemwd1E1MtGt+7O1KzrPNsaNvSsiEK55MyPIxJIMI2PS8Ijj464B2VbQlpRoQXxw1uHg==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
],
"dependencies": {
"@noble/curves": "~0.8.3",
"@noble/hashes": "~1.3.0",
"@scure/base": "~1.1.0"
"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/@scure/bip39": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.0.tgz",
"integrity": "sha512-SX/uKq52cuxm4YFXWFaVByaSHJh2w3BnokVSeUJVCv6K7WulT9u2BuNRBhuFl8vAuYnzx9bEu9WgpcNYTrYieg==",
"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,
"funding": [
{
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
],
"dependencies": {
"@noble/hashes": "~1.3.0",
"@scure/base": "~1.1.0"
"bin": {
"jsbt": "jsbt.js"
}
},
"node_modules/@types/node": {
"version": "18.11.18",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz",
"integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==",
"dev": true
},
"node_modules/fast-check": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.0.0.tgz",
@ -134,15 +72,15 @@
"dev": true
},
"node_modules/prettier": {
"version": "2.8.4",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz",
"integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==",
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.1.tgz",
"integrity": "sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==",
"dev": true,
"bin": {
"prettier": "bin-prettier.js"
"prettier": "bin/prettier.cjs"
},
"engines": {
"node": ">=10.13.0"
"node": ">=14"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
@ -165,16 +103,16 @@
]
},
"node_modules/typescript": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.2.tgz",
"integrity": "sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==",
"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": ">=12.20"
"node": ">=14.17"
}
}
}

@ -1,6 +1,6 @@
{
"name": "@noble/curves",
"version": "0.9.0",
"name": "@tornado/noble-curves",
"version": "1.4.0",
"description": "Audited & minimal JS implementation of elliptic curve cryptography",
"files": [
"abstract",
@ -12,9 +12,9 @@
"*.d.ts.map"
],
"scripts": {
"bench": "cd benchmark; node secp256k1.js; node curves.js; node ecdh.js; node hash-to-curve.js; node modular.js; node bls.js",
"bench": "cd benchmark; node secp256k1.js; node curves.js; node ecdh.js; node hash-to-curve.js; node modular.js; node bls.js; node ristretto255.js; node decaf448.js",
"build": "tsc && tsc -p tsconfig.esm.json",
"build:release": "rollup -c rollup.config.js",
"build:release": "cd build && npm i && npm run build",
"build:clean": "rm *.{js,d.ts,d.ts.map,js.map} esm/*.{js,d.ts,d.ts.map,js.map} 2> /dev/null",
"lint": "prettier --check 'src/**/*.{js,ts}' 'test/*.js'",
"format": "prettier --write 'src/**/*.{js,ts}' 'test/*.js'",
@ -24,22 +24,21 @@
"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",
"dependencies": {
"@noble/hashes": "1.3.0"
"@noble/hashes": "1.4.0"
},
"devDependencies": {
"@scure/bip32": "~1.2.0",
"@scure/bip39": "~1.2.0",
"@types/node": "18.11.18",
"@paulmillr/jsbt": "0.1.0",
"fast-check": "3.0.0",
"micro-bmark": "0.3.1",
"micro-should": "0.4.0",
"prettier": "2.8.4",
"typescript": "5.0.2"
"prettier": "3.1.1",
"typescript": "5.3.2"
},
"sideEffects": false,
"main": "index.js",
"exports": {
".": {
@ -102,10 +101,10 @@
"import": "./esm/bls12-381.js",
"default": "./bls12-381.js"
},
"./bn": {
"types": "./bn.d.ts",
"import": "./esm/bn.js",
"default": "./bn.js"
"./bn254": {
"types": "./bn254.d.ts",
"import": "./esm/bn254.js",
"default": "./bn254.js"
},
"./ed25519": {
"types": "./ed25519.d.ts",
@ -167,6 +166,8 @@
"secp256k1",
"ed25519",
"ed448",
"x25519",
"ed25519",
"bls12-381",
"bn254",
"pasta",
@ -177,10 +178,5 @@
"eddsa",
"schnorr"
],
"funding": [
{
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
]
}
"funding": "https://paulmillr.com/funding/"
}

@ -12,9 +12,13 @@
* Some projects may prefer to swap this relation, it is not supported for now.
*/
import { AffinePoint } from './curve.js';
import { IField, hashToPrivateScalar } from './modular.js';
import { IField, getMinHashLength, mapHashToField } from './modular.js';
import { Hex, PrivKey, CHash, bitLen, bitGet, ensureBytes } from './utils.js';
import * as htf from './hash-to-curve.js';
// prettier-ignore
import {
MapToCurve, Opts as HTFOpts, H2CPointConstructor, htfBasicOpts,
createHasher
} from './hash-to-curve.js';
import {
CurvePointsType,
ProjPointType as ProjPointType,
@ -24,64 +28,85 @@ import {
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> = {
decode(hex: Hex): ProjPointType<Fp2>;
encode(point: ProjPointType<Fp2>): Uint8Array;
fromHex(hex: Hex): ProjPointType<Fp2>;
toRawBytes(point: ProjPointType<Fp2>): Uint8Array;
toHex(point: ProjPointType<Fp2>): string;
};
export type CurveType<Fp, Fp2, Fp6, Fp12> = {
r: bigint;
G1: Omit<CurvePointsType<Fp>, 'n'> & {
mapToCurve: htf.MapToCurve<Fp>;
htfDefaults: htf.Opts;
ShortSignature: SignatureCoder<Fp>;
mapToCurve: MapToCurve<Fp>;
htfDefaults: HTFOpts;
};
G2: Omit<CurvePointsType<Fp2>, 'n'> & {
Signature: SignatureCoder<Fp2>;
mapToCurve: htf.MapToCurve<Fp2>;
htfDefaults: htf.Opts;
mapToCurve: MapToCurve<Fp2>;
htfDefaults: HTFOpts;
};
x: bigint;
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;
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;
};
};
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: htf.Opts;
htfDefaults: HTFOpts;
hash: CHash; // Because we need outputLen for DRBG
randomBytes: (bytesLength?: number) => Uint8Array;
};
export type CurveFn<Fp, Fp2, Fp6, Fp12> = {
CURVE: CurveType<Fp, Fp2, Fp6, Fp12>;
Fr: IField<bigint>;
Fp: IField<Fp>;
Fp2: IField<Fp2>;
Fp6: IField<Fp6>;
Fp12: IField<Fp12>;
G1: CurvePointsRes<Fp> & ReturnType<typeof htf.createHasher<Fp>>;
G2: CurvePointsRes<Fp2> & ReturnType<typeof htf.createHasher<Fp2>>;
Signature: SignatureCoder<Fp2>;
millerLoop: (ell: [Fp2, Fp2, Fp2][], g1: [Fp, Fp]) => Fp12;
calcPairingPrecomputes: (p: AffinePoint<Fp2>) => [Fp2, Fp2, Fp2][];
pairing: (P: ProjPointType<Fp>, Q: ProjPointType<Fp2>, withFinalExponent?: boolean) => Fp12;
getPublicKey: (privateKey: PrivKey) => Uint8Array;
getPublicKeyForShortSignatures: (privateKey: PrivKey) => Uint8Array;
sign: {
(message: Hex, privateKey: PrivKey): Uint8Array;
(message: ProjPointType<Fp2>, privateKey: PrivKey): ProjPointType<Fp2>;
(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>
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;
@ -91,23 +116,41 @@ export type CurveFn<Fp, Fp2, Fp6, Fp12> = {
(signatures: Hex[]): Uint8Array;
(signatures: ProjPointType<Fp2>[]): ProjPointType<Fp2>;
};
verifyBatch: (
signature: Hex | ProjPointType<Fp2>,
messages: (Hex | ProjPointType<Fp2>)[],
publicKeys: (Hex | ProjPointType<Fp>)[]
) => boolean;
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 looks pretty specific for curve, so for now we need to pass them with opts
const { Fp, Fr, Fp2, Fp6, Fp12 } = CURVE;
const BLS_X_LEN = bitLen(CURVE.x);
const groupLen = 32; // TODO: calculate; hardcoded for now
// 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
@ -122,18 +165,18 @@ export function bls<Fp2, Fp6, Fp12>(
// 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 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.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²
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.x, i)) {
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
@ -145,7 +188,7 @@ export function bls<Fp2, Fp6, Fp12>(
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
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
@ -155,7 +198,7 @@ export function bls<Fp2, Fp6, Fp12>(
}
function millerLoop(ell: [Fp2, Fp2, Fp2][], g1: [Fp, Fp]): Fp12 {
const { x } = CURVE;
const { x } = CURVE.params;
const Px = g1[0];
const Py = g1[1];
let f12 = Fp12.ONE;
@ -174,15 +217,17 @@ export function bls<Fp2, Fp6, Fp12>(
const utils = {
randomPrivateKey: (): Uint8Array => {
return Fr.toBytes(hashToPrivateScalar(CURVE.randomBytes(groupLen + 8), CURVE.r));
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_,
htf.createHasher(G1_.ProjectivePoint, CURVE.G1.mapToCurve, {
createHasher(G1_.ProjectivePoint, CURVE.G1.mapToCurve, {
...CURVE.htfDefaults,
...CURVE.G1.htfDefaults,
})
@ -208,12 +253,13 @@ export function bls<Fp2, Fp6, Fp12>(
const G2_ = weierstrassPoints({ n: Fr.ORDER, ...CURVE.G2 });
const G2 = Object.assign(
G2_,
htf.createHasher(G2_.ProjectivePoint as htf.H2CPointConstructor<Fp2>, CURVE.G2.mapToCurve, {
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
@ -235,31 +281,60 @@ export function bls<Fp2, Fp6, Fp12>(
function normP1(point: G1Hex): G1 {
return point instanceof G1.ProjectivePoint ? (point as G1) : G1.ProjectivePoint.fromHex(point);
}
function normP2(point: G2Hex): G2 {
return point instanceof G2.ProjectivePoint ? point : Signature.decode(point);
function normP1Hash(point: G1Hex, htfOpts?: htfBasicOpts): G1 {
return point instanceof G1.ProjectivePoint
? point
: (G1.hashToCurve(ensureBytes('point', point), htfOpts) as G1);
}
function normP2Hash(point: G2Hex, htfOpts?: htf.htfBasicOpts): G2 {
function normP2(point: G2Hex): G2 {
return point instanceof G2.ProjectivePoint ? point : Signature.fromHex(point);
}
function normP2Hash(point: G2Hex, htfOpts?: htfBasicOpts): G2 {
return point instanceof G2.ProjectivePoint
? point
: (G2.hashToCurve(ensureBytes('point', point), htfOpts) as G2);
}
// Multiplies generator by private key.
// 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?: htf.htfBasicOpts): Uint8Array;
function sign(message: G2, privateKey: PrivKey, htfOpts?: htf.htfBasicOpts): G2;
function sign(message: G2Hex, privateKey: PrivKey, htfOpts?: htf.htfBasicOpts): Uint8Array | G2 {
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.encode(sigPoint);
return Signature.toRawBytes(sigPoint);
}
function signShortSignature(
message: Hex,
privateKey: PrivKey,
htfOpts?: htfBasicOpts
): Uint8Array;
function signShortSignature(message: G1, privateKey: PrivKey, htfOpts?: htfBasicOpts): G1;
function signShortSignature(
message: G1Hex,
privateKey: PrivKey,
htfOpts?: htfBasicOpts
): Uint8Array | G1 {
const msgPoint = normP1Hash(message, htfOpts);
msgPoint.assertValidity();
const sigPoint = msgPoint.multiply(G1.normPrivateKeyToScalar(privateKey));
if (message instanceof G1.ProjectivePoint) return sigPoint;
return ShortSignature.toRawBytes(sigPoint);
}
// Checks if pairing of public key & hash is equal to pairing of generator & signature.
@ -268,7 +343,7 @@ export function bls<Fp2, Fp6, Fp12>(
signature: G2Hex,
message: G2Hex,
publicKey: G1Hex,
htfOpts?: htf.htfBasicOpts
htfOpts?: htfBasicOpts
): boolean {
const P = normP1(publicKey);
const Hm = normP2Hash(message, htfOpts);
@ -282,6 +357,26 @@ export function bls<Fp2, Fp6, Fp12>(
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;
@ -309,7 +404,21 @@ export function bls<Fp2, Fp6, Fp12>(
aggAffine.assertValidity();
return aggAffine;
}
return Signature.encode(aggAffine);
return Signature.toRawBytes(aggAffine);
}
// Adds a bunch of signature points together.
function aggregateShortSignatures(signatures: Hex[]): Uint8Array;
function aggregateShortSignatures(signatures: G1[]): G1;
function aggregateShortSignatures(signatures: G1Hex[]): Uint8Array | G1 {
if (!signatures.length) throw new Error('Expected non-empty array');
const agg = signatures.map(normP1).reduce((sum, s) => sum.add(s), G1.ProjectivePoint.ZERO);
const aggAffine = agg; //.toAffine();
if (signatures[0] instanceof G1.ProjectivePoint) {
aggAffine.assertValidity();
return aggAffine;
}
return ShortSignature.toRawBytes(aggAffine);
}
// https://ethresear.ch/t/fast-verification-of-multiple-bls-signatures/5407
@ -318,7 +427,7 @@ export function bls<Fp2, Fp6, Fp12>(
signature: G2Hex,
messages: G2Hex[],
publicKeys: G1Hex[],
htfOpts?: htf.htfBasicOpts
htfOpts?: htfBasicOpts
): boolean {
// @ts-ignore
// console.log('verifyBatch', bytesToHex(signature as any), messages, publicKeys.map(bytesToHex));
@ -353,24 +462,35 @@ export function bls<Fp2, Fp6, Fp12>(
G1.ProjectivePoint.BASE._setWindowSize(4);
return {
CURVE,
Fr,
Fp,
Fp2,
Fp6,
Fp12,
getPublicKey,
getPublicKeyForShortSignatures,
sign,
signShortSignature,
verify,
verifyBatch,
verifyShortSignature,
aggregatePublicKeys,
aggregateSignatures,
aggregateShortSignatures,
millerLoop,
pairing,
G1,
G2,
Signature,
millerLoop,
calcPairingPrecomputes,
pairing,
getPublicKey,
sign,
verify,
aggregatePublicKeys,
aggregateSignatures,
verifyBatch,
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,
};
}

@ -5,11 +5,9 @@ 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 like 123n
const _0n = BigInt(0);
const _1n = BigInt(1);
const _2n = BigInt(2);
const _8n = BigInt(8);
// 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> & {
@ -20,10 +18,13 @@ export type CurveType = BasicCurve<bigint> & {
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()
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(
@ -51,6 +52,8 @@ export interface ExtPointType extends Group<ExtPointType> {
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;
@ -58,6 +61,8 @@ export interface ExtPointType extends Group<ExtPointType> {
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> {
@ -70,8 +75,13 @@ export interface ExtPointConstructor extends GroupConstructor<ExtPointType> {
export type CurveFn = {
CURVE: ReturnType<typeof validateOpts>;
getPublicKey: (privateKey: Hex) => Uint8Array;
sign: (message: Hex, privateKey: Hex) => Uint8Array;
verify: (sig: Hex, message: Hex, publicKey: Hex) => boolean;
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;
@ -88,8 +98,16 @@ export type CurveFn = {
// 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, hash: cHash, randomBytes, nByteLength, h: cofactor } = CURVE;
const MASK = _2n ** BigInt(nByteLength * 8);
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)
@ -109,7 +127,7 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
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 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) {
@ -297,8 +315,9 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
// 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);
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;
@ -341,7 +360,7 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
// Converts hash string or Uint8Array to Point.
// Uses algo from RFC8032 5.1.3.
static fromHex(hex: Hex, strict = true): Point {
static fromHex(hex: Hex, zip215 = false): Point {
const { d, a } = CURVE;
const len = Fp.BYTES;
hex = ensureBytes('pointHex', hex, len); // copy hex to a new array
@ -353,8 +372,8 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
// y=0 is allowed
} else {
// RFC8032 prohibits >= p, but ZIP215 doesn't
if (strict) assertInRange(y, Fp.ORDER); // strict=true [1..P-1] (2^255-19-1 for ed25519)
else assertInRange(y, MASK); // strict=false [1..MASK-1] (2^256-1 for ed25519)
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:
@ -365,7 +384,10 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
let { isValid, value: x } = uvRatio(u, v); // √(u/v)
if (!isValid) throw new Error('Point.fromHex: invalid y coordinate');
const isXOdd = (x & _1n) === _1n; // There are 2 square roots. Use x_0 bit to select proper
const isLastByteOdd = (lastByte & 0x80) !== 0; // if x=0 and x_0 = 1, fail
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 });
}
@ -416,32 +438,44 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
// 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)));
return modN_LE(cHash(domain(msg, ensureBytes('context', context), !!prehash)));
}
/** Signs message with privateKey. RFC8032 5.1.6 */
function sign(msg: Hex, privKey: Hex, context?: Hex): Uint8Array {
function sign(msg: Hex, privKey: Hex, options: { context?: Hex } = {}): Uint8Array {
msg = ensureBytes('message', msg);
if (preHash) msg = preHash(msg); // for ed25519ph etc.
if (prehash) msg = prehash(msg); // for ed25519ph etc.
const { prefix, scalar, pointBytes } = getExtendedPublicKey(privKey);
const r = hashDomainToScalar(context, prefix, msg); // r = dom2(F, C) || prefix || PH(M)
const r = hashDomainToScalar(options.context, prefix, msg); // r = dom2(F, C) || prefix || PH(M)
const R = G.multiply(r).toRawBytes(); // R = rG
const k = hashDomainToScalar(context, R, pointBytes, msg); // R || A || PH(M)
const 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
}
function verify(sig: Hex, msg: Hex, publicKey: Hex, context?: Hex): boolean {
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); // ZIP215 compliant, which means not fully RFC8032 compliant.
if (preHash) msg = preHash(msg); // for ed25519ph, etc
const A = Point.fromHex(publicKey, false); // Check for s bounds, hex validity
const R = Point.fromHex(sig.slice(0, len), false); // 0 <= R < 2^256: ZIP215 R can be >= P
const s = ut.bytesToNumberLE(sig.slice(len, 2 * len)); // 0 <= s < l
const SB = G.multiplyUnsafe(s);
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'

@ -1,7 +1,8 @@
/*! 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 { bytesToNumberBE, CHash, concatBytes, utf8ToBytes, validateObject } from './utils.js';
import type { CHash } from './utils.js';
import { bytesToNumberBE, abytes, concatBytes, utf8ToBytes, validateObject } from './utils.js';
/**
* * `DST` is a domain separation tag, defined in section 2.2.5
@ -17,16 +18,10 @@ export type Opts = {
p: bigint;
m: number;
k: number;
expand?: 'xmd' | 'xof';
expand: 'xmd' | 'xof';
hash: CHash;
};
function validateDST(dst: UnicodeOrBytes): Uint8Array {
if (dst instanceof Uint8Array) return dst;
if (typeof dst === 'string') return utf8ToBytes(dst);
throw new Error('DST must be Uint8Array or string');
}
// Octet Stream to Integer. "spec" implementation of os2ip is 2.5x slower vs bytesToNumberBE.
const os2ip = bytesToNumberBE;
@ -51,25 +46,22 @@ function strxor(a: Uint8Array, b: Uint8Array): Uint8Array {
return arr;
}
function isBytes(item: unknown): void {
if (!(item instanceof Uint8Array)) throw new Error('Uint8Array expected');
}
function isNum(item: unknown): void {
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://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.4.1
// https://www.rfc-editor.org/rfc/rfc9380#section-5.3.1
export function expand_message_xmd(
msg: Uint8Array,
DST: Uint8Array,
lenInBytes: number,
H: CHash
): Uint8Array {
isBytes(msg);
isBytes(DST);
isNum(lenInBytes);
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-5.3.3
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);
@ -88,6 +80,11 @@ export function expand_message_xmd(
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,
@ -95,10 +92,10 @@ export function expand_message_xof(
k: number,
H: CHash
): Uint8Array {
isBytes(msg);
isBytes(DST);
isNum(lenInBytes);
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-5.3.3
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);
@ -119,7 +116,7 @@ export function expand_message_xof(
/**
* Hashes arbitrary-length byte strings to a list of one or more elements of a finite field F
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3
* https://www.rfc-editor.org/rfc/rfc9380#section-5.2
* @param msg a byte string containing the message to hash
* @param 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
@ -127,16 +124,16 @@ export function expand_message_xof(
*/
export function hash_to_field(msg: Uint8Array, count: number, options: Opts): bigint[][] {
validateObject(options, {
DST: 'string',
DST: 'stringOrUint8Array',
p: 'bigint',
m: 'isSafeInteger',
k: 'isSafeInteger',
hash: 'hash',
});
const { p, k, m, hash, expand, DST: _DST } = options;
isBytes(msg);
isNum(count);
const DST = validateDST(_DST);
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;
@ -145,10 +142,11 @@ export function hash_to_field(msg: Uint8Array, count: number, options: Opts): bi
prb = expand_message_xmd(msg, DST, len_in_bytes, hash);
} else if (expand === 'xof') {
prb = expand_message_xof(msg, DST, len_in_bytes, k, hash);
} else if (expand === undefined) {
} else if (expand === '_internal_pass') {
// for internal tests only
prb = msg;
} else {
throw new Error('expand must be "xmd", "xof" or undefined');
throw new Error('expand must be "xmd" or "xof"');
}
const u = new Array(count);
for (let i = 0; i < count; i++) {
@ -200,8 +198,8 @@ export function createHasher<T>(
) {
if (typeof mapToCurve !== 'function') throw new Error('mapToCurve() must be defined');
return {
// Encodes byte string to elliptic curve
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-3
// 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]));
@ -211,7 +209,8 @@ export function createHasher<T>(
return P;
},
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-3
// 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();

@ -22,10 +22,10 @@ export function mod(a: bigint, b: bigint): bigint {
return result >= _0n ? result : b + result;
}
/**
* Efficiently exponentiate num to power and do modular division.
* Efficiently raise num to power and do modular division.
* Unsafe in some contexts: uses ladder, so can expose bigint bits.
* @example
* powMod(2n, 6n, 11n) // 64n % 11n == 9n
* pow(2n, 6n, 11n) // 64n % 11n == 9n
*/
// TODO: use field version && remove
export function pow(num: bigint, power: bigint, modulo: bigint): bigint {
@ -55,7 +55,7 @@ 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/
// 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;
@ -75,9 +75,14 @@ export function invert(number: bigint, modulo: bigint): bigint {
return mod(x, modulo);
}
// Tonelli-Shanks algorithm
// Paper 1: https://eprint.iacr.org/2012/685.pdf (page 12)
// Paper 2: Square Roots from 1; 24, 51, 10 to Dan Shanks
/**
* 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).
@ -198,11 +203,7 @@ export function FpSqrt(P: bigint) {
// Little-endian check for first LE bit (last BE bit);
export const isNegativeLE = (num: bigint, modulo: bigint) => (mod(num, modulo) & _1n) === _1n;
// Currently completly inconsistent naming:
// - readable: add, mul, sqr, sqrt, inv, div, pow, eq, sub
// - unreadable mess: addition, multiply, square, squareRoot, inversion, divide, power, equals, subtract
// Field is not always over prime, Fp2 for example has ORDER(q)=p^m
// Field is not always over prime: for example, Fp2 has ORDER(q)=p^m
export interface IField<T> {
ORDER: bigint;
BYTES: number;
@ -232,7 +233,8 @@ export interface IField<T> {
sqrN(num: T): T;
// Optional
// Should be same as sgn0 function in https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/
// Should be same as sgn0 function in
// [RFC9380](https://www.rfc-editor.org/rfc/rfc9380#section-4.1).
// NOTE: sgn0 is 'negative in LE', which is same as odd. And negative in LE is kinda strange definition anyway.
isOdd?(num: T): boolean; // Odd instead of even since we have it for Fp2
// legendre?(num: T): T;
@ -264,6 +266,11 @@ export function validateField<T>(field: IField<T>) {
}
// 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!
@ -275,12 +282,15 @@ export function FpPow<T>(f: IField<T>, num: T, power: bigint): T {
while (power > _0n) {
if (power & _1n) p = f.mul(p, d);
d = f.sqr(d);
power >>= 1n;
power >>= _1n;
}
return p;
}
// 0 is non-invertible: non-batched version will throw on 0
/**
* 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
@ -323,12 +333,12 @@ export function nLength(n: bigint, nBitLength?: number) {
type FpField = IField<bigint> & Required<Pick<IField<bigint>, 'isOdd'>>;
/**
* Initializes a galois field over prime. Non-primes are not supported for now.
* Do not init in loop: slow. Very fragile: always run a benchmark on change.
* Major performance gains:
* a) non-normalized operations like mulN instead of mul
* b) `Object.freeze`
* c) Same object shape: never add or remove keys
* 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
@ -340,7 +350,7 @@ export function Field(
isLE = false,
redef: Partial<IField<bigint>> = {}
): Readonly<FpField> {
if (ORDER <= _0n) throw new Error(`Expected Fp ORDER > 0, got ${ORDER}`);
if (ORDER <= _0n) throw new Error(`Expected Field ORDER > 0, got ${ORDER}`);
const { nBitLength: BITS, nByteLength: BYTES } = nLength(ORDER, bitLen);
if (BYTES > 2048) throw new Error('Field lengths over 2048 bytes are not supported');
const sqrtP = FpSqrt(ORDER);
@ -404,13 +414,10 @@ export function FpSqrtEven<T>(Fp: IField<T>, elm: T) {
}
/**
* FIPS 186 B.4.1-compliant "constant-time" private key generation utility.
* Can take (n+8) or more bytes of uniform input e.g. from CSPRNG or KDF
* and convert them into private scalar, with the modulo bias being neglible.
* Needs at least 40 bytes of input for 32-byte private key.
* https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
* @param hash hash output from SHA3 or a similar function
* @returns valid private scalar
* "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,
@ -425,3 +432,53 @@ export function hashToPrivateScalar(
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);
}

@ -150,17 +150,15 @@ export function montgomery(curveDef: CurveType): CurveFn {
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.
// This is very ugly way, but it works because fieldLen-1 is outside of bounds for X448, so this becomes NOOP
// fieldLen - scalaryBytes = 1 for X448 and = 0 for X25519
const u = ensureBytes('u coordinate', uEnc, montgomeryBytes);
// u[fieldLen-1] crashes QuickJS (TypeError: out-of-bound numeric index)
if (fieldLen === montgomeryBytes) u[fieldLen - 1] &= 127; // 0b0111_1111
if (fieldLen === 32) u[31] &= 127; // 0b0111_1111
return bytesToNumberLE(u);
}
function decodeScalar(n: Hex): bigint {
const bytes = ensureBytes('scalar', n);
if (bytes.length !== montgomeryBytes && bytes.length !== fieldLen)
throw new Error(`Expected ${montgomeryBytes} or ${fieldLen} bytes, got ${bytes.length}`);
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 {

@ -15,34 +15,36 @@ export type PoseidonOpts = {
};
export function validateOpts(opts: PoseidonOpts) {
const { Fp } = opts;
const { Fp, mds, reversePartialPowIdx: rev, roundConstants: rc } = opts;
const { roundsFull, roundsPartial, sboxPower, t } = opts;
validateField(Fp);
for (const i of ['t', 'roundsFull', 'roundsPartial'] as const) {
if (typeof opts[i] !== 'number' || !Number.isSafeInteger(opts[i]))
throw new Error(`Poseidon: invalid param ${i}=${opts[i]} (${typeof opts[i]})`);
}
if (opts.reversePartialPowIdx !== undefined && typeof opts.reversePartialPowIdx !== 'boolean')
throw new Error(`Poseidon: invalid param reversePartialPowIdx=${opts.reversePartialPowIdx}`);
// Default is 5, but by some reasons stark uses 3
let sboxPower = opts.sboxPower;
if (sboxPower === undefined) sboxPower = 5;
if (typeof sboxPower !== 'number' || !Number.isSafeInteger(sboxPower))
throw new Error(`Poseidon wrong sboxPower=${sboxPower}`);
const _sboxPower = BigInt(sboxPower);
let sboxFn = (n: bigint) => FpPow(Fp, n, _sboxPower);
// Unwrapped sbox power for common cases (195->142μs)
if (sboxPower === 3) sboxFn = (n: bigint) => Fp.mul(Fp.sqrN(n), n);
else if (sboxPower === 5) sboxFn = (n: bigint) => Fp.mul(Fp.sqrN(Fp.sqrN(n)), n);
// 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 (opts.roundsFull % 2 !== 0)
throw new Error(`Poseidon roundsFull is not even: ${opts.roundsFull}`);
const rounds = opts.roundsFull + opts.roundsPartial;
if (rev !== undefined && typeof rev !== 'boolean')
throw new Error(`Poseidon: invalid param reversePartialPowIdx=${rev}`);
if (!Array.isArray(opts.roundConstants) || opts.roundConstants.length !== rounds)
if (roundsFull % 2 !== 0) throw new Error(`Poseidon roundsFull is not even: ${roundsFull}`);
const rounds = roundsFull + roundsPartial;
if (!Array.isArray(rc) || rc.length !== rounds)
throw new Error('Poseidon: wrong round constants');
const roundConstants = opts.roundConstants.map((rc) => {
if (!Array.isArray(rc) || rc.length !== opts.t)
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))
@ -50,18 +52,16 @@ export function validateOpts(opts: PoseidonOpts) {
return Fp.create(i);
});
});
// MDS is TxT matrix
if (!Array.isArray(opts.mds) || opts.mds.length !== opts.t)
throw new Error('Poseidon: wrong MDS matrix');
const mds = opts.mds.map((mdsRow) => {
if (!Array.isArray(mdsRow) || mdsRow.length !== opts.t)
throw new Error(`Poseidon MDS matrix row: ${mdsRow}`);
return mdsRow.map((i) => {
if (typeof i !== 'bigint') throw new Error(`Poseidon MDS matrix value=${i}`);
return Fp.create(i);
});
});
return Object.freeze({ ...opts, rounds, sboxFn, roundConstants, mds });
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) {
@ -80,18 +80,17 @@ export function splitConstants(rc: bigint[], t: number) {
}
export function poseidon(opts: PoseidonOpts) {
const { t, Fp, rounds, sboxFn, reversePartialPowIdx } = validateOpts(opts);
const halfRoundsFull = Math.floor(opts.roundsFull / 2);
const partialIdx = reversePartialPowIdx ? t - 1 : 0;
const _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, opts.roundConstants[idx][j]));
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 = opts.mds.map((i) =>
i.reduce((acc, i, j) => Fp.add(acc, Fp.mulN(i, values[j])), Fp.ZERO)
);
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[]) {
@ -105,7 +104,7 @@ export function poseidon(opts: PoseidonOpts) {
// Apply r_f/2 full rounds.
for (let i = 0; i < halfRoundsFull; i++) values = poseidonRound(values, true, round++);
// Apply r_p partial rounds.
for (let i = 0; i < opts.roundsPartial; i++) values = poseidonRound(values, false, round++);
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++);
@ -114,6 +113,6 @@ export function poseidon(opts: PoseidonOpts) {
return values;
};
// For verification in tests
poseidonHash.roundConstants = opts.roundConstants;
poseidonHash.roundConstants = roundConstants;
return poseidonHash;
}

@ -1,13 +1,13 @@
/*! 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);
const u8a = (a: any): a is Uint8Array => a instanceof Uint8Array;
// We accept hex strings besides Uint8Array for simplicity
export type Hex = Uint8Array | string;
// Very few implementations accept numbers, we do it to ease learning curve
export type PrivKey = Hex | bigint;
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;
@ -16,9 +16,26 @@ export type CHash = {
};
export type FHash = (message: Uint8Array | string) => Uint8Array;
const hexes = Array.from({ length: 256 }, (v, i) => i.toString(16).padStart(2, '0'));
export function isBytes(a: unknown): a is Uint8Array {
return (
a instanceof Uint8Array ||
(a != null && typeof a === 'object' && a.constructor.name === 'Uint8Array')
);
}
export function abytes(item: unknown): void {
if (!isBytes(item)) throw new Error('Uint8Array expected');
}
// Array where index 0xf0 (240) is mapped to string 'f0'
const hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) =>
i.toString(16).padStart(2, '0')
);
/**
* @example bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23])) // 'cafe0123'
*/
export function bytesToHex(bytes: Uint8Array): string {
if (!u8a(bytes)) throw new Error('Uint8Array expected');
abytes(bytes);
// pre-caching improves the speed 6x
let hex = '';
for (let i = 0; i < bytes.length; i++) {
@ -38,36 +55,65 @@ export function hexToNumber(hex: string): bigint {
return BigInt(hex === '' ? '0' : `0x${hex}`);
}
// Caching slows it down 2-3x
// We use optimized technique to convert hex string to byte array
const asciis = { _0: 48, _9: 57, _A: 65, _F: 70, _a: 97, _f: 102 } as const;
function asciiToBase16(char: number): number | undefined {
if (char >= asciis._0 && char <= asciis._9) return char - asciis._0;
if (char >= asciis._A && char <= asciis._F) return char - (asciis._A - 10);
if (char >= asciis._a && char <= asciis._f) return char - (asciis._a - 10);
return;
}
/**
* @example hexToBytes('cafe0123') // Uint8Array.from([0xca, 0xfe, 0x01, 0x23])
*/
export function hexToBytes(hex: string): Uint8Array {
if (typeof hex !== 'string') throw new Error('hex string expected, got ' + typeof hex);
if (hex.length % 2) throw new Error('hex string is invalid: unpadded ' + hex.length);
const array = new Uint8Array(hex.length / 2);
for (let i = 0; i < array.length; i++) {
const j = i * 2;
const hexByte = hex.slice(j, j + 2);
const byte = Number.parseInt(hexByte, 16);
if (Number.isNaN(byte) || byte < 0) throw new Error('invalid byte sequence');
array[i] = byte;
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;
}
// Big Endian
// BE: Big Endian, LE: Little Endian
export function bytesToNumberBE(bytes: Uint8Array): bigint {
return hexToNumber(bytesToHex(bytes));
}
export function bytesToNumberLE(bytes: Uint8Array): bigint {
if (!u8a(bytes)) throw new Error('Uint8Array expected');
abytes(bytes);
return hexToNumber(bytesToHex(Uint8Array.from(bytes).reverse()));
}
export const numberToBytesBE = (n: bigint, len: number) =>
hexToBytes(n.toString(16).padStart(len * 2, '0'));
export const numberToBytesLE = (n: bigint, len: number) => numberToBytesBE(n, len).reverse();
// Returns variable number bytes (minimal bigint encoding?)
export const numberToVarBytesBE = (n: bigint) => hexToBytes(numberToHexUnpadded(n));
export function 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') {
@ -76,7 +122,7 @@ export function ensureBytes(title: string, hex: Hex, expectedLength?: number): U
} catch (e) {
throw new Error(`${title} must be valid hex string, got "${hex}". Cause: ${e}`);
}
} else if (u8a(hex)) {
} 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);
@ -89,51 +135,77 @@ export function ensureBytes(title: string, hex: Hex, expectedLength?: number): U
return res;
}
// Copies several Uint8Arrays into one.
export function concatBytes(...arrs: Uint8Array[]): Uint8Array {
const r = new Uint8Array(arrs.reduce((sum, a) => sum + a.length, 0));
let pad = 0; // walk through each item, ensure they have proper type
arrs.forEach((a) => {
if (!u8a(a)) throw new Error('Uint8Array expected');
r.set(a, pad);
/**
* 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 r;
}
return res;
}
export function equalBytes(b1: Uint8Array, b2: Uint8Array) {
// We don't care about timing attacks here
if (b1.length !== b2.length) return false;
for (let i = 0; i < b1.length; i++) if (b1[i] !== b2[i]) return false;
return true;
// 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 TextEncoder().encode(str);
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
// Amount of bits inside bigint (Same as n.toString(2).length)
/**
* Calculates amount of bits in a bigint.
* Same as `n.toString(2).length`
*/
export function bitLen(n: bigint) {
let len;
for (len = 0; n > 0n; n >>= _1n, len += 1);
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 const bitGet = (n: bigint, pos: number) => (n >> BigInt(pos)) & 1n;
// Sets single bit at position
export const bitSet = (n: bigint, pos: number, value: boolean) =>
n | ((value ? _1n : _0n) << BigInt(pos));
// Return mask for N bits (Same as BigInt(`0b${Array(i).fill('1').join('')}`))
// Not using ** operator with bigints for old engines.
/**
* 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
@ -205,6 +277,7 @@ const validatorFns = {
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),

@ -27,7 +27,7 @@ export type BasicWCurve<T> = BasicCurve<T> & {
clearCofactor?: (c: ProjConstructor<T>, point: ProjPointType<T>) => ProjPointType<T>;
};
type Entropy = Hex | true;
type Entropy = Hex | boolean;
export type SignOpts = { lowS?: boolean; extraEntropy?: Entropy; prehash?: boolean };
export type VerOpts = { lowS?: boolean; prehash?: boolean };
@ -58,6 +58,8 @@ export interface ProjPointType<T> extends Group<ProjPointType<T>> {
readonly px: T;
readonly py: T;
readonly pz: T;
get x(): T;
get y(): T;
multiply(scalar: bigint): ProjPointType<T>;
toAffine(iz?: T): AffinePoint<T>;
isTorsionFree(): boolean;
@ -121,6 +123,7 @@ function validatePointOpts<T>(curve: CurvePointsType<T>) {
}
export type CurvePointsRes<T> = {
CURVE: ReturnType<typeof validatePointOpts<T>>;
ProjectivePoint: ProjConstructor<T>;
normPrivateKeyToScalar: (key: PrivKey) => bigint;
weierstrassEquation: (x: T) => T;
@ -129,7 +132,7 @@ export type CurvePointsRes<T> = {
// ASN.1 DER encoding utilities
const { bytesToNumberBE: b2n, hexToBytes: h2b } = ut;
const DER = {
export const DER = {
// asn.1 DER encoding utils
Err: class DERErr extends Error {
constructor(m = '') {
@ -142,16 +145,20 @@ const DER = {
const len = data[1];
const res = data.subarray(2, len + 2);
if (!len || res.length !== len) throw new E('Invalid signature integer: wrong length');
if (res[0] === 0x00 && res[1] <= 0x7f)
throw new E('Invalid signature integer: trailing length');
// ^ Weird condition: not about length, but about first bytes of number.
// https://crypto.stackexchange.com/a/57734 Leftmost bit of first byte is 'negative' flag,
// since we always use positive integers here. It must always be empty:
// - add zero byte if exists
// - if next byte doesn't have a flag, leading zero is not allowed (minimal encoding)
if (res[0] & 0b10000000) throw new E('Invalid signature integer: negative');
if (res[0] === 0x00 && !(res[1] & 0b10000000))
throw new E('Invalid signature integer: unnecessary leading zero');
return { d: b2n(res), l: data.subarray(len + 2) }; // d is data, l is left
},
toSig(hex: string | Uint8Array): { r: bigint; s: bigint } {
// parse DER signature
const { Err: E } = DER;
const data = typeof hex === 'string' ? h2b(hex) : hex;
if (!(data instanceof Uint8Array)) throw new Error('ui8a expected');
ut.abytes(data);
let l = data.length;
if (l < 2 || data[0] != 0x30) throw new E('Invalid signature tag');
if (data[1] !== l - 2) throw new E('Invalid signature: incorrect length');
@ -161,7 +168,8 @@ const DER = {
return { r, s };
},
hexFromSig(sig: { r: bigint; s: bigint }): string {
const slice = (s: string): string => (Number.parseInt(s[0], 16) >= 8 ? '00' + s : s); // slice DER
// Add leading zero if first byte has negative bit enabled. More details in '_parseInt'
const slice = (s: string): string => (Number.parseInt(s[0], 16) & 0b1000 ? '00' + s : s);
const h = (num: number | bigint) => {
const hex = num.toString(16);
return hex.length & 1 ? `0${hex}` : hex;
@ -176,17 +184,17 @@ const DER = {
},
};
// Be friendly to bad ECMAScript parsers by not using bigint literals like 123n
const _0n = BigInt(0);
const _1n = BigInt(1);
// Be friendly to bad ECMAScript parsers by not using bigint literals
// prettier-ignore
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n = BigInt(4);
export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T> {
const CURVE = validatePointOpts(opts);
const { Fp } = CURVE; // All curves has same field / group length as for now, but they can differ
const toBytes =
CURVE.toBytes ||
((c: ProjConstructor<T>, point: ProjPointType<T>, isCompressed: boolean) => {
((_c: ProjConstructor<T>, point: ProjPointType<T>, _isCompressed: boolean) => {
const a = point.toAffine();
return ut.concatBytes(Uint8Array.from([0x04]), Fp.toBytes(a.x), Fp.toBytes(a.y));
});
@ -211,6 +219,12 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
const x3 = Fp.mul(x2, x); // x2 * x
return Fp.add(Fp.add(x3, Fp.mul(x, a)), b); // x3 + a * x + b
}
// Validate whether the passed curve params are valid.
// We check if curve equation works for generator point.
// `assertValidity()` won't work: `isTorsionFree()` is not available at this point in bls12-381.
// ProjectivePoint class has not been initialized yet.
if (!Fp.eql(Fp.sqr(CURVE.Gy), weierstrassEquation(CURVE.Gx)))
throw new Error('bad generator point: equation left != right');
// Valid group elements reside in range 1..n-1
function isWithinCurveOrder(num: bigint): boolean {
@ -224,7 +238,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
function normPrivateKeyToScalar(key: PrivKey): bigint {
const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n } = CURVE;
if (lengths && typeof key !== 'bigint') {
if (key instanceof Uint8Array) key = ut.bytesToHex(key);
if (ut.isBytes(key)) key = ut.bytesToHex(key);
// Normalize to hex string, pad. E.g. P521 would norm 130-132 char hex to 132-char bytes
if (typeof key !== 'string' || !lengths.includes(key.length)) throw new Error('Invalid key');
key = key.padStart(nByteLength * 2, '0');
@ -256,7 +270,11 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
static readonly BASE = new Point(CURVE.Gx, CURVE.Gy, Fp.ONE);
static readonly ZERO = new Point(Fp.ZERO, Fp.ONE, Fp.ZERO);
constructor(readonly px: T, readonly py: T, readonly pz: T) {
constructor(
readonly px: T,
readonly py: T,
readonly pz: T
) {
if (px == null || !Fp.isValid(px)) throw new Error('x required');
if (py == null || !Fp.isValid(py)) throw new Error('y required');
if (pz == null || !Fp.isValid(pz)) throw new Error('z required');
@ -320,9 +338,11 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
// A point on curve is valid if it conforms to equation.
assertValidity(): void {
// Zero is valid point too!
if (this.is0()) {
if (CURVE.allowInfinityPoint) return;
// (0, 1, 0) aka ZERO is invalid in most contexts.
// In BLS, ZERO can be serialized, so we allow it.
// (0, 0, 0) is wrong representation of ZERO and is always invalid.
if (CURVE.allowInfinityPoint && !Fp.is0(this.py)) return;
throw new Error('bad point: ZERO');
}
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
@ -365,7 +385,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
// Cost: 8M + 3S + 3*a + 2*b3 + 15add.
double() {
const { a, b } = CURVE;
const b3 = Fp.mul(b, 3n);
const b3 = Fp.mul(b, _3n);
const { px: X1, py: Y1, pz: Z1 } = this;
let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO; // prettier-ignore
let t0 = Fp.mul(X1, X1); // step 1
@ -412,7 +432,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
const { px: X2, py: Y2, pz: Z2 } = other;
let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO; // prettier-ignore
const a = CURVE.a;
const b3 = Fp.mul(CURVE.b, 3n);
const b3 = Fp.mul(CURVE.b, _3n);
let t0 = Fp.mul(X1, X2); // step 1
let t1 = Fp.mul(Y1, Y2);
let t2 = Fp.mul(Z1, Z2);
@ -589,7 +609,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
}
const _bits = CURVE.nBitLength;
const wnaf = wNAF(Point, CURVE.endo ? Math.ceil(_bits / 2) : _bits);
// Validate if generator point is on curve
return {
CURVE,
ProjectivePoint: Point as ProjConstructor<T>,
@ -605,7 +625,7 @@ export interface SignatureType {
readonly s: bigint;
readonly recovery?: number;
assertValidity(): void;
addRecoveryBit(recovery: number): SignatureType;
addRecoveryBit(recovery: number): RecoveredSignatureType;
hasHighS(): boolean;
normalizeS(): SignatureType;
recoverPublicKey(msgHash: Hex): ProjPointType<bigint>;
@ -615,6 +635,9 @@ export interface SignatureType {
toDERRawBytes(isCompressed?: boolean): Uint8Array;
toDERHex(isCompressed?: boolean): string;
}
export type RecoveredSignatureType = SignatureType & {
readonly recovery: number;
};
// Static methods
export type SignatureConstructor = {
new (r: bigint, s: bigint): SignatureType;
@ -656,7 +679,7 @@ export type CurveFn = {
CURVE: ReturnType<typeof validateOpts>;
getPublicKey: (privateKey: PrivKey, isCompressed?: boolean) => Uint8Array;
getSharedSecret: (privateA: PrivKey, publicB: Hex, isCompressed?: boolean) => Uint8Array;
sign: (msgHash: Hex, privKey: PrivKey, opts?: SignOpts) => SignatureType;
sign: (msgHash: Hex, privKey: PrivKey, opts?: SignOpts) => RecoveredSignatureType;
verify: (signature: Hex | SignatureLike, msgHash: Hex, publicKey: Hex, opts?: VerOpts) => boolean;
ProjectivePoint: ProjConstructor<bigint>;
Signature: SignatureConstructor;
@ -691,7 +714,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
isWithinCurveOrder,
} = weierstrassPoints({
...CURVE,
toBytes(c, point, isCompressed: boolean): Uint8Array {
toBytes(_c, point, isCompressed: boolean): Uint8Array {
const a = point.toAffine();
const x = Fp.toBytes(a.x);
const cat = ut.concatBytes;
@ -710,7 +733,13 @@ export function weierstrass(curveDef: CurveType): CurveFn {
const x = ut.bytesToNumberBE(tail);
if (!isValidFieldElement(x)) throw new Error('Point is not on curve');
const y2 = weierstrassEquation(x); // y² = x³ + ax + b
let y = Fp.sqrt(y2); // y = y² ^ (p+1)/4
let y: bigint;
try {
y = Fp.sqrt(y2); // y = y² ^ (p+1)/4
} catch (sqrtError) {
const suffix = sqrtError instanceof Error ? ': ' + sqrtError.message : '';
throw new Error('Point is not on curve' + suffix);
}
const isYOdd = (y & _1n) === _1n;
// ECDSA
const isHeadOdd = (head & 1) === 1;
@ -745,7 +774,11 @@ export function weierstrass(curveDef: CurveType): CurveFn {
* ECDSA signature with its (r, s) properties. Supports DER & compact representations.
*/
class Signature implements SignatureType {
constructor(readonly r: bigint, readonly s: bigint, readonly recovery?: number) {
constructor(
readonly r: bigint,
readonly s: bigint,
readonly recovery?: number
) {
this.assertValidity();
}
@ -769,8 +802,8 @@ export function weierstrass(curveDef: CurveType): CurveFn {
if (!isWithinCurveOrder(this.s)) throw new Error('s must be 0 < s < CURVE.n');
}
addRecoveryBit(recovery: number) {
return new Signature(this.r, this.s, recovery);
addRecoveryBit(recovery: number): RecoveredSignature {
return new Signature(this.r, this.s, recovery) as RecoveredSignature;
}
recoverPublicKey(msgHash: Hex): typeof Point.BASE {
@ -815,6 +848,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
return numToNByteStr(this.r) + numToNByteStr(this.s);
}
}
type RecoveredSignature = Signature & { recovery: number };
const utils = {
isValidPrivateKey(privateKey: PrivKey) {
@ -828,13 +862,12 @@ export function weierstrass(curveDef: CurveType): CurveFn {
normPrivateKeyToScalar: normPrivateKeyToScalar,
/**
* Produces cryptographically secure private key from random of size (nBitLength+64)
* as per FIPS 186 B.4.1 with modulo bias being neglible.
* Produces cryptographically secure private key from random of size
* (groupLen + ceil(groupLen / 2)) with modulo bias being negligible.
*/
randomPrivateKey: (): Uint8Array => {
const rand = CURVE.randomBytes(Fp.BYTES + 8);
const num = mod.hashToPrivateScalar(rand, CURVE_ORDER);
return ut.numberToBytesBE(num, CURVE.nByteLength);
const length = mod.getMinHashLength(CURVE.n);
return mod.mapHashToField(CURVE.randomBytes(length), CURVE.n);
},
/**
@ -866,7 +899,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
* Quick and dirty check for item being public key. Does not validate hex, or being on-curve.
*/
function isProbPub(item: PrivKey | PubKey): boolean {
const arr = item instanceof Uint8Array;
const arr = ut.isBytes(item);
const str = typeof item === 'string';
const len = (arr || str) && (item as Hex).length;
if (arr) return len === compressedLen || len === uncompressedLen;
@ -944,15 +977,15 @@ export function weierstrass(curveDef: CurveType): CurveFn {
const d = normPrivateKeyToScalar(privateKey); // validate private key, convert to bigint
const seedArgs = [int2octets(d), int2octets(h1int)];
// extraEntropy. RFC6979 3.6: additional k' (optional).
if (ent != null) {
if (ent != null && ent !== false) {
// K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k')
const e = ent === true ? randomBytes(Fp.BYTES) : ent; // generate random bytes OR pass as-is
seedArgs.push(ensureBytes('extraEntropy', e, Fp.BYTES)); // check for being of size BYTES
seedArgs.push(ensureBytes('extraEntropy', e)); // check for being bytes
}
const seed = ut.concatBytes(...seedArgs); // Step D of RFC6979 3.2
const m = h1int; // NOTE: no need to call bits2int second time here, it is inside truncateHash!
// Converts signature params into point w r/s, checks result for validity.
function k2sig(kBytes: Uint8Array): Signature | undefined {
function k2sig(kBytes: Uint8Array): RecoveredSignature | undefined {
// RFC 6979 Section 3.2, step 3: k = bits2int(T)
const k = bits2int(kBytes); // Cannot use fields methods, since it is group element
if (!isWithinCurveOrder(k)) return; // Important: all mod() calls here must be done over N
@ -971,7 +1004,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
normS = normalizeS(s); // if lowS was passed, ensure s is always
recovery ^= 1; // // in the bottom half of N
}
return new Signature(r, normS, recovery); // use normS, not s
return new Signature(r, normS, recovery) as RecoveredSignature; // use normS, not s
}
return { seed, k2sig };
}
@ -979,18 +1012,22 @@ export function weierstrass(curveDef: CurveType): CurveFn {
const defaultVerOpts: VerOpts = { lowS: CURVE.lowS, prehash: false };
/**
* Signs message hash (not message: you need to hash it by yourself).
* Signs message hash with a private key.
* ```
* sign(m, d, k) where
* (x, y) = G × k
* r = x mod n
* s = (m + dr)/k mod n
* ```
* @param opts `lowS, extraEntropy, prehash`
* @param msgHash NOT message. msg needs to be hashed to `msgHash`, or use `prehash`.
* @param privKey private key
* @param opts lowS for non-malleable sigs. extraEntropy for mixing randomness into k. prehash will hash first arg.
* @returns signature with recovery param
*/
function sign(msgHash: Hex, privKey: PrivKey, opts = defaultSigOpts): Signature {
function sign(msgHash: Hex, privKey: PrivKey, opts = defaultSigOpts): RecoveredSignature {
const { seed, k2sig } = prepSig(msgHash, privKey, opts); // Steps A, D of RFC6979 3.2.
const drbg = ut.createHmacDrbg<Signature>(CURVE.hash.outputLen, CURVE.nByteLength, CURVE.hmac);
const C = CURVE;
const drbg = ut.createHmacDrbg<RecoveredSignature>(C.hash.outputLen, C.nByteLength, C.hmac);
return drbg(seed, k2sig); // Steps B, C, D, E, F, G
}
@ -1026,7 +1063,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
let _sig: Signature | undefined = undefined;
let P: ProjPointType<bigint>;
try {
if (typeof sg === 'string' || sg instanceof Uint8Array) {
if (typeof sg === 'string' || ut.isBytes(sg)) {
// Signature can be represented in 2 ways: compact (2*nByteLength) & DER (variable-length).
// Since DER can also be 2*nByteLength bytes, we check for it first.
try {
@ -1071,22 +1108,31 @@ export function weierstrass(curveDef: CurveType): CurveFn {
};
}
// Implementation of the Shallue and van de Woestijne method for any Weierstrass curve
// TODO: check if there is a way to merge this with uvRatio in Edwards && move to modular?
// b = True and y = sqrt(u / v) if (u / v) is square in F, and
// b = False and y = sqrt(Z * (u / v)) otherwise.
/**
* Implementation of the Shallue and van de Woestijne method for any weierstrass curve.
* TODO: check if there is a way to merge this with uvRatio in Edwards; move to modular.
* b = True and y = sqrt(u / v) if (u / v) is square in F, and
* b = False and y = sqrt(Z * (u / v)) otherwise.
* @param Fp
* @param Z
* @returns
*/
export function SWUFpSqrtRatio<T>(Fp: mod.IField<T>, Z: T) {
// Generic implementation
const q = Fp.ORDER;
let l = 0n;
for (let o = q - 1n; o % 2n === 0n; o /= 2n) l += 1n;
let l = _0n;
for (let o = q - _1n; o % _2n === _0n; o /= _2n) l += _1n;
const c1 = l; // 1. c1, the largest integer such that 2^c1 divides q - 1.
const c2 = (q - 1n) / 2n ** c1; // 2. c2 = (q - 1) / (2^c1) # Integer arithmetic
const c3 = (c2 - 1n) / 2n; // 3. c3 = (c2 - 1) / 2 # Integer arithmetic
const c4 = 2n ** c1 - 1n; // 4. c4 = 2^c1 - 1 # Integer arithmetic
const c5 = 2n ** (c1 - 1n); // 5. c5 = 2^(c1 - 1) # Integer arithmetic
// We need 2n ** c1 and 2n ** (c1-1). We can't use **; but we can use <<.
// 2n ** c1 == 2n << (c1-1)
const _2n_pow_c1_1 = _2n << (c1 - _1n - _1n);
const _2n_pow_c1 = _2n_pow_c1_1 * _2n;
const c2 = (q - _1n) / _2n_pow_c1; // 2. c2 = (q - 1) / (2^c1) # Integer arithmetic
const c3 = (c2 - _1n) / _2n; // 3. c3 = (c2 - 1) / 2 # Integer arithmetic
const c4 = _2n_pow_c1 - _1n; // 4. c4 = 2^c1 - 1 # Integer arithmetic
const c5 = _2n_pow_c1_1; // 5. c5 = 2^(c1 - 1) # Integer arithmetic
const c6 = Fp.pow(Z, c2); // 6. c6 = Z^c2
const c7 = Fp.pow(Z, (c2 + 1n) / 2n); // 7. c7 = Z^((c2 + 1) / 2)
const c7 = Fp.pow(Z, (c2 + _1n) / _2n); // 7. c7 = Z^((c2 + 1) / 2)
let sqrtRatio = (u: T, v: T): { isValid: boolean; value: T } => {
let tv1 = c6; // 1. tv1 = c6
let tv2 = Fp.pow(v, c4); // 2. tv2 = v^c4
@ -1105,8 +1151,9 @@ export function SWUFpSqrtRatio<T>(Fp: mod.IField<T>, Z: T) {
tv3 = Fp.cmov(tv2, tv3, isQR); // 15. tv3 = CMOV(tv2, tv3, isQR)
tv4 = Fp.cmov(tv5, tv4, isQR); // 16. tv4 = CMOV(tv5, tv4, isQR)
// 17. for i in (c1, c1 - 1, ..., 2):
for (let i = c1; i > 1; i--) {
let tv5 = 2n ** (i - 2n); // 18. tv5 = i - 2; 19. tv5 = 2^tv5
for (let i = c1; i > _1n; i--) {
let tv5 = i - _2n; // 18. tv5 = i - 2
tv5 = _2n << (tv5 - _1n); // 19. tv5 = 2^tv5
let tvv5 = Fp.pow(tv4, tv5); // 20. tv5 = tv4^tv5
const e1 = Fp.eql(tvv5, Fp.ONE); // 21. e1 = tv5 == 1
tv2 = Fp.mul(tv3, tv1); // 22. tv2 = tv3 * tv1
@ -1117,9 +1164,9 @@ export function SWUFpSqrtRatio<T>(Fp: mod.IField<T>, Z: T) {
}
return { isValid: isQR, value: tv3 };
};
if (Fp.ORDER % 4n === 3n) {
if (Fp.ORDER % _4n === _3n) {
// sqrt_ratio_3mod4(u, v)
const c1 = (Fp.ORDER - 3n) / 4n; // 1. c1 = (q - 3) / 4 # Integer arithmetic
const c1 = (Fp.ORDER - _3n) / _4n; // 1. c1 = (q - 3) / 4 # Integer arithmetic
const c2 = Fp.sqrt(Fp.neg(Z)); // 2. c2 = sqrt(-Z)
sqrtRatio = (u: T, v: T) => {
let tv1 = Fp.sqr(v); // 1. tv1 = v^2
@ -1135,10 +1182,13 @@ export function SWUFpSqrtRatio<T>(Fp: mod.IField<T>, Z: T) {
};
}
// No curves uses that
// if (Fp.ORDER % 8n === 5n) // sqrt_ratio_5mod8
// if (Fp.ORDER % _8n === _5n) // sqrt_ratio_5mod8
return sqrtRatio;
}
// From draft-irtf-cfrg-hash-to-curve-16
/**
* Simplified Shallue-van de Woestijne-Ulas Method
* https://www.rfc-editor.org/rfc/rfc9380#section-6.6.2
*/
export function mapToCurveSimpleSWU<T>(
Fp: mod.IField<T>,
opts: {

@ -1,15 +1,9 @@
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
// bls12-381 pairing-friendly Barreto-Lynn-Scott elliptic curve construction allows to:
// - Construct zk-SNARKs at the 128-bit security
// - Use threshold signatures, which allows a user to sign lots of messages with one signature and
// verify them swiftly in a batch, using Boneh-Lynn-Shacham signature scheme.
//
// The library uses G1 for public keys and G2 for signatures. Support for G1 signatures is planned.
// Compatible with Algorand, Chia, Dfinity, Ethereum, FIL, Zcash. Matches specs
// [pairing-curves-10](https://tools.ietf.org/html/draft-irtf-cfrg-pairing-friendly-curves-10),
// [bls-sigs-04](https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04),
// [hash-to-curve-12](https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-12).
// bls12-381 is pairing-friendly Barreto-Lynn-Scott elliptic curve construction allowing to:
// - Construct zk-SNARKs at the 120-bit security
// - Efficiently verify N aggregate signatures with 1 pairing and N ec additions:
// the Boneh-Lynn-Shacham signature scheme is orders of magnitude more efficient than Schnorr
//
// ### Summary
// 1. BLS Relies on Bilinear Pairing (expensive)
@ -25,26 +19,17 @@
// - `S = pk x H(m)` - signing
// - `e(P, H(m)) == e(G, S)` - verification using pairings
// - `e(G, S) = e(G, SUM(n)(Si)) = MUL(n)(e(G, Si))` - signature aggregation
// Filecoin uses little endian byte arrays for private keys -
// so ensure to reverse byte order if you'll use it with FIL.
//
// ### Resources
// - [BLS12-381 for the rest of us](https://hackmd.io/@benjaminion/bls12-381)
// - [Key concepts of pairings](https://medium.com/@alonmuroch_65570/bls-signatures-part-2-key-concepts-of-pairings-27a8a9533d0c)
// - Pairing over bls12-381:
// [part 1](https://research.nccgroup.com/2020/07/06/pairing-over-bls12-381-part-1-fields/),
// [part 2](https://research.nccgroup.com/2020/07/13/pairing-over-bls12-381-part-2-curves/),
// [part 3](https://research.nccgroup.com/2020/08/13/pairing-over-bls12-381-part-3-pairing/)
// - [Estimating the bit security of pairing-friendly curves](https://research.nccgroup.com/2022/02/03/estimating-the-bit-security-of-pairing-friendly-curves/)
//
// ### Differences from @noble/bls12-381 1.4
// - PointG1 -> G1.Point
// - PointG2 -> G2.Point
// - PointG2.fromSignature -> Signature.decode
// - PointG2.toSignature -> Signature.encode
// - Fixed Fp2 ORDER
// - Points now have only two coordinates
// ### Compatibility and notes
// 1. It is compatible with Algorand, Chia, Dfinity, Ethereum, Filecoin, ZEC
// Filecoin uses little endian byte arrays for private keys - make sure to reverse byte order.
// 2. Some projects use G2 for public keys and G1 for signatures. It's called "short signature"
// 3. Curve security level is about 120 bits as per Barbulescu-Duquesne 2017
// https://hal.science/hal-01534101/file/main.pdf
// 4. Compatible with specs:
// [cfrg-pairing-friendly-curves-11](https://tools.ietf.org/html/draft-irtf-cfrg-pairing-friendly-curves-11),
// [cfrg-bls-signature-05](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-bls-signature-05),
// [RFC 9380](https://www.rfc-editor.org/rfc/rfc9380).
import { sha256 } from '@noble/hashes/sha256';
import { randomBytes } from '@noble/hashes/utils';
import { bls, CurveFn } from './abstract/bls.js';
@ -55,10 +40,10 @@ import {
numberToBytesBE,
bytesToNumberBE,
bitLen,
bitSet,
bitGet,
Hex,
bitMask,
bytesToHex,
} from './abstract/utils.js';
// Types
import {
@ -69,16 +54,22 @@ import {
} from './abstract/weierstrass.js';
import { isogenyMap } from './abstract/hash-to-curve.js';
// Be friendly to bad ECMAScript parsers by not using bigint literals
// prettier-ignore
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n = BigInt(4);
// prettier-ignore
const _8n = BigInt(8), _16n = BigInt(16);
// CURVE FIELDS
// Finite field over p.
const Fp =
mod.Field(
0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaabn
);
const Fp_raw = BigInt(
'0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab'
);
const Fp = mod.Field(Fp_raw);
type Fp = bigint;
// Finite field over r.
// This particular field is not used anywhere in bls12-381, but it is still useful.
const Fr = mod.Field(0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001n);
const Fr = mod.Field(BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001'));
// Fp₂ over complex plane
type BigintTuple = [bigint, bigint];
@ -120,9 +111,7 @@ type Fp2Utils = {
// G² - 1
// h2q
// NOTE: ORDER was wrong!
const FP2_ORDER =
0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaabn **
2n;
const FP2_ORDER = Fp_raw * Fp_raw;
const Fp2: mod.IField<Fp2> & Fp2Utils = {
ORDER: FP2_ORDER,
@ -175,7 +164,7 @@ const Fp2: mod.IField<Fp2> & Fp2Utils = {
// https://github.com/zkcrypto/bls12_381/blob/080eaa74ec0e394377caa1ba302c8c121df08b07/src/fp2.rs#L250
// https://github.com/supranational/blst/blob/aae0c7d70b799ac269ff5edf29d8191dbd357876/src/exp2.c#L1
// Inspired by https://github.com/dalek-cryptography/curve25519-dalek/blob/17698df9d4c834204f83a3574143abacb4fc81a5/src/field.rs#L99
const candidateSqrt = Fp2.pow(num, (Fp2.ORDER + 8n) / 16n);
const candidateSqrt = Fp2.pow(num, (Fp2.ORDER + _8n) / _16n);
const check = Fp2.div(Fp2.sqr(candidateSqrt), num); // candidateSqrt.square().div(this);
const R = FP2_ROOTS_OF_UNITY;
const divisor = [R[0], R[2], R[4], R[6]].find((r) => Fp2.eql(r, check));
@ -190,13 +179,13 @@ const Fp2: mod.IField<Fp2> & Fp2Utils = {
if (im1 > im2 || (im1 === im2 && re1 > re2)) return x1;
return x2;
},
// Same as sgn0_fp2 in draft-irtf-cfrg-hash-to-curve-16
// Same as sgn0_m_eq_2 in RFC 9380
isOdd: (x: Fp2) => {
const { re: x0, im: x1 } = Fp2.reim(x);
const sign_0 = x0 % 2n;
const zero_0 = x0 === 0n;
const sign_1 = x1 % 2n;
return BigInt(sign_0 || (zero_0 && sign_1)) == 1n;
const sign_0 = x0 % _2n;
const zero_0 = x0 === _0n;
const sign_1 = x1 % _2n;
return BigInt(sign_0 || (zero_0 && sign_1)) == _1n;
},
// Bytes util
fromBytes(b: Uint8Array): Fp2 {
@ -216,8 +205,8 @@ const Fp2: mod.IField<Fp2> & Fp2Utils = {
// multiply by u + 1
mulByNonresidue: ({ c0, c1 }) => ({ c0: Fp.sub(c0, c1), c1: Fp.add(c0, c1) }),
multiplyByB: ({ c0, c1 }) => {
let t0 = Fp.mul(c0, 4n); // 4 * c0
let t1 = Fp.mul(c1, 4n); // 4 * c1
let t0 = Fp.mul(c0, _4n); // 4 * c0
let t1 = Fp.mul(c1, _4n); // 4 * c1
// (T0-T1) + (T0+T1)*i
return { c0: Fp.sub(t0, t1), c1: Fp.add(t0, t1) };
},
@ -234,33 +223,36 @@ const Fp2: mod.IField<Fp2> & Fp2Utils = {
// Finite extension field over irreducible polynominal.
// Fp(u) / (u² - β) where β = -1
const FP2_FROBENIUS_COEFFICIENTS = [
0x1n,
0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaan,
BigInt('0x1'),
BigInt(
'0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa'
),
].map((item) => Fp.create(item));
// For Fp2 roots of unity.
const rv1 =
0x6af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09n;
const rv1 = BigInt(
'0x6af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09'
);
// const ev1 =
// 0x699be3b8c6870965e5bf892ad5d2cc7b0e85a117402dfd83b7f4a947e02d978498255a2aaec0ac627b5afbdf1bf1c90n;
// BigInt('0x699be3b8c6870965e5bf892ad5d2cc7b0e85a117402dfd83b7f4a947e02d978498255a2aaec0ac627b5afbdf1bf1c90');
// const ev2 =
// 0x8157cd83046453f5dd0972b6e3949e4288020b5b8a9cc99ca07e27089a2ce2436d965026adad3ef7baba37f2183e9b5n;
// BigInt('0x8157cd83046453f5dd0972b6e3949e4288020b5b8a9cc99ca07e27089a2ce2436d965026adad3ef7baba37f2183e9b5');
// const ev3 =
// 0xab1c2ffdd6c253ca155231eb3e71ba044fd562f6f72bc5bad5ec46a0b7a3b0247cf08ce6c6317f40edbc653a72dee17n;
// BigInt('0xab1c2ffdd6c253ca155231eb3e71ba044fd562f6f72bc5bad5ec46a0b7a3b0247cf08ce6c6317f40edbc653a72dee17');
// const ev4 =
// 0xaa404866706722864480885d68ad0ccac1967c7544b447873cc37e0181271e006df72162a3d3e0287bf597fbf7f8fc1n;
// BigInt('0xaa404866706722864480885d68ad0ccac1967c7544b447873cc37e0181271e006df72162a3d3e0287bf597fbf7f8fc1');
// Eighth roots of unity, used for computing square roots in Fp2.
// To verify or re-calculate:
// Array(8).fill(new Fp2([1n, 1n])).map((fp2, k) => fp2.pow(Fp2.ORDER * BigInt(k) / 8n))
const FP2_ROOTS_OF_UNITY = [
[1n, 0n],
[_1n, _0n],
[rv1, -rv1],
[0n, 1n],
[_0n, _1n],
[rv1, rv1],
[-1n, 0n],
[-_1n, _0n],
[-rv1, rv1],
[0n, -1n],
[_0n, -_1n],
[-rv1, -rv1],
].map((pair) => Fp2.fromBigTuple(pair));
// eta values, used for computing sqrt(g(X1(t)))
@ -314,8 +306,8 @@ const Fp6Multiply = ({ c0, c1, c2 }: Fp6, rhs: Fp6 | bigint) => {
};
const Fp6Square = ({ c0, c1, c2 }: Fp6) => {
let t0 = Fp2.sqr(c0); // c0²
let t1 = Fp2.mul(Fp2.mul(c0, c1), 2n); // 2 * c0 * c1
let t3 = Fp2.mul(Fp2.mul(c1, c2), 2n); // 2 * c1 * c2
let t1 = Fp2.mul(Fp2.mul(c0, c1), _2n); // 2 * c0 * c1
let t3 = Fp2.mul(Fp2.mul(c1, c2), _2n); // 2 * c1 * c2
let t4 = Fp2.sqr(c2); // c2²
return {
c0: Fp2.add(Fp2.mulByNonresidue(t3), t0), // T3 * (u + 1) + T0
@ -440,46 +432,64 @@ const Fp6: mod.IField<Fp6> & Fp6Utils = {
};
const FP6_FROBENIUS_COEFFICIENTS_1 = [
[0x1n, 0x0n],
[BigInt('0x1'), BigInt('0x0')],
[
0x0n,
0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaacn,
BigInt('0x0'),
BigInt(
'0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac'
),
],
[
0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffen,
0x0n,
BigInt(
'0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe'
),
BigInt('0x0'),
],
[0x0n, 0x1n],
[BigInt('0x0'), BigInt('0x1')],
[
0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaacn,
0x0n,
BigInt(
'0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac'
),
BigInt('0x0'),
],
[
0x0n,
0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffen,
BigInt('0x0'),
BigInt(
'0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe'
),
],
].map((pair) => Fp2.fromBigTuple(pair));
const FP6_FROBENIUS_COEFFICIENTS_2 = [
[0x1n, 0x0n],
[BigInt('0x1'), BigInt('0x0')],
[
0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaadn,
0x0n,
BigInt(
'0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaad'
),
BigInt('0x0'),
],
[
0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaacn,
0x0n,
BigInt(
'0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac'
),
BigInt('0x0'),
],
[
0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaan,
0x0n,
BigInt(
'0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa'
),
BigInt('0x0'),
],
[
0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffen,
0x0n,
BigInt(
'0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe'
),
BigInt('0x0'),
],
[
0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffeffffn,
0x0n,
BigInt(
'0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffeffff'
),
BigInt('0x0'),
],
].map((pair) => Fp2.fromBigTuple(pair));
@ -488,7 +498,7 @@ const FP6_FROBENIUS_COEFFICIENTS_2 = [
// Fp₆(w) / (w² - γ) where γ = v
type Fp12 = { c0: Fp6; c1: Fp6 };
// The BLS parameter x for BLS12-381
const BLS_X = 0xd201000000010000n;
const BLS_X = BigInt('0xd201000000010000');
const BLS_X_LEN = bitLen(BLS_X);
// prettier-ignore
@ -646,14 +656,14 @@ const Fp12: mod.IField<Fp12> & Fp12Utils = {
let t9 = Fp2.mulByNonresidue(t8); // T8 * (u + 1)
return {
c0: Fp6.create({
c0: Fp2.add(Fp2.mul(Fp2.sub(t3, c0c0), 2n), t3), // 2 * (T3 - c0c0) + T3
c1: Fp2.add(Fp2.mul(Fp2.sub(t5, c0c1), 2n), t5), // 2 * (T5 - c0c1) + T5
c2: Fp2.add(Fp2.mul(Fp2.sub(t7, c0c2), 2n), t7),
c0: Fp2.add(Fp2.mul(Fp2.sub(t3, c0c0), _2n), t3), // 2 * (T3 - c0c0) + T3
c1: Fp2.add(Fp2.mul(Fp2.sub(t5, c0c1), _2n), t5), // 2 * (T5 - c0c1) + T5
c2: Fp2.add(Fp2.mul(Fp2.sub(t7, c0c2), _2n), t7),
}), // 2 * (T7 - c0c2) + T7
c1: Fp6.create({
c0: Fp2.add(Fp2.mul(Fp2.add(t9, c1c0), 2n), t9), // 2 * (T9 + c1c0) + T9
c1: Fp2.add(Fp2.mul(Fp2.add(t4, c1c1), 2n), t4), // 2 * (T4 + c1c1) + T4
c2: Fp2.add(Fp2.mul(Fp2.add(t6, c1c2), 2n), t6),
c0: Fp2.add(Fp2.mul(Fp2.add(t9, c1c0), _2n), t9), // 2 * (T9 + c1c0) + T9
c1: Fp2.add(Fp2.mul(Fp2.add(t4, c1c1), _2n), t4), // 2 * (T4 + c1c1) + T4
c2: Fp2.add(Fp2.mul(Fp2.add(t6, c1c2), _2n), t6),
}),
}; // 2 * (T6 + c1c2) + T6
},
@ -688,58 +698,91 @@ const Fp12: mod.IField<Fp12> & Fp12Utils = {
},
};
const FP12_FROBENIUS_COEFFICIENTS = [
[0x1n, 0x0n],
[BigInt('0x1'), BigInt('0x0')],
[
0x1904d3bf02bb0667c231beb4202c0d1f0fd603fd3cbd5f4f7b2443d784bab9c4f67ea53d63e7813d8d0775ed92235fb8n,
0x00fc3e2b36c4e03288e9e902231f9fb854a14787b6c7b36fec0c8ec971f63c5f282d5ac14d6c7ec22cf78a126ddc4af3n,
BigInt(
'0x1904d3bf02bb0667c231beb4202c0d1f0fd603fd3cbd5f4f7b2443d784bab9c4f67ea53d63e7813d8d0775ed92235fb8'
),
BigInt(
'0x00fc3e2b36c4e03288e9e902231f9fb854a14787b6c7b36fec0c8ec971f63c5f282d5ac14d6c7ec22cf78a126ddc4af3'
),
],
[
0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffeffffn,
0x0n,
BigInt(
'0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffeffff'
),
BigInt('0x0'),
],
[
0x135203e60180a68ee2e9c448d77a2cd91c3dedd930b1cf60ef396489f61eb45e304466cf3e67fa0af1ee7b04121bdea2n,
0x06af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09n,
BigInt(
'0x135203e60180a68ee2e9c448d77a2cd91c3dedd930b1cf60ef396489f61eb45e304466cf3e67fa0af1ee7b04121bdea2'
),
BigInt(
'0x06af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09'
),
],
[
0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffen,
0x0n,
BigInt(
'0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe'
),
BigInt('0x0'),
],
[
0x144e4211384586c16bd3ad4afa99cc9170df3560e77982d0db45f3536814f0bd5871c1908bd478cd1ee605167ff82995n,
0x05b2cfd9013a5fd8df47fa6b48b1e045f39816240c0b8fee8beadf4d8e9c0566c63a3e6e257f87329b18fae980078116n,
BigInt(
'0x144e4211384586c16bd3ad4afa99cc9170df3560e77982d0db45f3536814f0bd5871c1908bd478cd1ee605167ff82995'
),
BigInt(
'0x05b2cfd9013a5fd8df47fa6b48b1e045f39816240c0b8fee8beadf4d8e9c0566c63a3e6e257f87329b18fae980078116'
),
],
[
0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaan,
0x0n,
BigInt(
'0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa'
),
BigInt('0x0'),
],
[
0x00fc3e2b36c4e03288e9e902231f9fb854a14787b6c7b36fec0c8ec971f63c5f282d5ac14d6c7ec22cf78a126ddc4af3n,
0x1904d3bf02bb0667c231beb4202c0d1f0fd603fd3cbd5f4f7b2443d784bab9c4f67ea53d63e7813d8d0775ed92235fb8n,
BigInt(
'0x00fc3e2b36c4e03288e9e902231f9fb854a14787b6c7b36fec0c8ec971f63c5f282d5ac14d6c7ec22cf78a126ddc4af3'
),
BigInt(
'0x1904d3bf02bb0667c231beb4202c0d1f0fd603fd3cbd5f4f7b2443d784bab9c4f67ea53d63e7813d8d0775ed92235fb8'
),
],
[
0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaacn,
0x0n,
BigInt(
'0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac'
),
BigInt('0x0'),
],
[
0x06af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09n,
0x135203e60180a68ee2e9c448d77a2cd91c3dedd930b1cf60ef396489f61eb45e304466cf3e67fa0af1ee7b04121bdea2n,
BigInt(
'0x06af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09'
),
BigInt(
'0x135203e60180a68ee2e9c448d77a2cd91c3dedd930b1cf60ef396489f61eb45e304466cf3e67fa0af1ee7b04121bdea2'
),
],
[
0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaadn,
0x0n,
BigInt(
'0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaad'
),
BigInt('0x0'),
],
[
0x05b2cfd9013a5fd8df47fa6b48b1e045f39816240c0b8fee8beadf4d8e9c0566c63a3e6e257f87329b18fae980078116n,
0x144e4211384586c16bd3ad4afa99cc9170df3560e77982d0db45f3536814f0bd5871c1908bd478cd1ee605167ff82995n,
BigInt(
'0x05b2cfd9013a5fd8df47fa6b48b1e045f39816240c0b8fee8beadf4d8e9c0566c63a3e6e257f87329b18fae980078116'
),
BigInt(
'0x144e4211384586c16bd3ad4afa99cc9170df3560e77982d0db45f3536814f0bd5871c1908bd478cd1ee605167ff82995'
),
],
].map((n) => Fp2.fromBigTuple(n));
// END OF CURVE FIELDS
// HashToCurve
// 3-isogeny map from E' to E
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-E.3
// 3-isogeny map from E' to E https://www.rfc-editor.org/rfc/rfc9380#appendix-E.3
const isogenyMapG2 = isogenyMap(
Fp2,
[
@ -887,19 +930,23 @@ const isogenyMapG1 = isogenyMap(
// SWU Map - Fp2 to G2': y² = x³ + 240i * x + 1012 + 1012i
const G2_SWU = mapToCurveSimpleSWU(Fp2, {
A: Fp2.create({ c0: Fp.create(0n), c1: Fp.create(240n) }), // A' = 240 * I
B: Fp2.create({ c0: Fp.create(1012n), c1: Fp.create(1012n) }), // B' = 1012 * (1 + I)
Z: Fp2.create({ c0: Fp.create(-2n), c1: Fp.create(-1n) }), // Z: -(2 + I)
A: Fp2.create({ c0: Fp.create(_0n), c1: Fp.create(BigInt(240)) }), // A' = 240 * I
B: Fp2.create({ c0: Fp.create(BigInt(1012)), c1: Fp.create(BigInt(1012)) }), // B' = 1012 * (1 + I)
Z: Fp2.create({ c0: Fp.create(BigInt(-2)), c1: Fp.create(BigInt(-1)) }), // Z: -(2 + I)
});
// Optimized SWU Map - Fp to G1
const G1_SWU = mapToCurveSimpleSWU(Fp, {
A: Fp.create(
0x144698a3b8e9433d693a02c96d4982b0ea985383ee66a8d8e8981aefd881ac98936f8da0e0f97f5cf428082d584c1dn
BigInt(
'0x144698a3b8e9433d693a02c96d4982b0ea985383ee66a8d8e8981aefd881ac98936f8da0e0f97f5cf428082d584c1d'
)
),
B: Fp.create(
0x12e2908d11688030018b12e8753eee3b2016c1f0f24f4070a0b9c14fcef35ef55a23215a316ceaa5d1cc48e98e172be0n
BigInt(
'0x12e2908d11688030018b12e8753eee3b2016c1f0f24f4070a0b9c14fcef35ef55a23215a316ceaa5d1cc48e98e172be0'
)
),
Z: Fp.create(11n),
Z: Fp.create(BigInt(11)),
});
// Endomorphisms (for fast cofactor clearing)
@ -922,8 +969,9 @@ function G2psi(c: ProjConstructor<Fp2>, P: ProjPointType<Fp2>) {
}
// Ψ²(P) endomorphism
// 1 / F2(2)^((p-1)/3) in GF(p²)
const PSI2_C1 =
0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaacn;
const PSI2_C1 = BigInt(
'0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac'
);
function psi2(x: Fp2, y: Fp2): [Fp2, Fp2] {
return [Fp2.mul(x, PSI2_C1), Fp2.neg(y)];
@ -938,7 +986,7 @@ function G2psi2(c: ProjConstructor<Fp2>, P: ProjPointType<Fp2>) {
//
// Parameter definitions are in section 5.3 of the spec unless otherwise noted.
// Parameter values come from section 8.8.2 of the spec.
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-8.8.2
// https://www.rfc-editor.org/rfc/rfc9380#section-8.8.2
//
// Base field F is GF(p^m)
// p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab
@ -970,14 +1018,62 @@ const htfDefaults = Object.freeze({
// Encoding utils
// Point on G1 curve: (x, y)
const C_BIT_POS = Fp.BITS; // C_bit, compression bit for serialization flag
const I_BIT_POS = Fp.BITS + 1; // I_bit, point-at-infinity bit for serialization flag
const S_BIT_POS = Fp.BITS + 2; // S_bit, sign bit for serialization flag
// Compressed point of infinity
const COMPRESSED_ZERO = Fp.toBytes(bitSet(bitSet(0n, I_BIT_POS, true), S_BIT_POS, true)); // set compressed & point-at-infinity bits
const COMPRESSED_ZERO = setMask(Fp.toBytes(_0n), { infinity: true, compressed: true }); // set compressed & point-at-infinity bits
function parseMask(bytes: Uint8Array) {
// Copy, so we can remove mask data. It will be removed also later, when Fp.create will call modulo.
bytes = bytes.slice();
const mask = bytes[0] & 0b1110_0000;
const compressed = !!((mask >> 7) & 1); // compression bit (0b1000_0000)
const infinity = !!((mask >> 6) & 1); // point at infinity bit (0b0100_0000)
const sort = !!((mask >> 5) & 1); // sort bit (0b0010_0000)
bytes[0] &= 0b0001_1111; // clear mask (zero first 3 bits)
return { compressed, infinity, sort, value: bytes };
}
function setMask(
bytes: Uint8Array,
mask: { compressed?: boolean; infinity?: boolean; sort?: boolean }
) {
if (bytes[0] & 0b1110_0000) throw new Error('setMask: non-empty mask');
if (mask.compressed) bytes[0] |= 0b1000_0000;
if (mask.infinity) bytes[0] |= 0b0100_0000;
if (mask.sort) bytes[0] |= 0b0010_0000;
return bytes;
}
function signatureG1ToRawBytes(point: ProjPointType<Fp>) {
point.assertValidity();
const isZero = point.equals(bls12_381.G1.ProjectivePoint.ZERO);
const { x, y } = point.toAffine();
if (isZero) return COMPRESSED_ZERO.slice();
const P = Fp.ORDER;
const sort = Boolean((y * _2n) / P);
return setMask(numberToBytesBE(x, Fp.BYTES), { compressed: true, sort });
}
function signatureG2ToRawBytes(point: ProjPointType<Fp2>) {
// NOTE: by some reasons it was missed in bls12-381, looks like bug
point.assertValidity();
const len = Fp.BYTES;
if (point.equals(bls12_381.G2.ProjectivePoint.ZERO))
return concatB(COMPRESSED_ZERO, numberToBytesBE(_0n, len));
const { x, y } = point.toAffine();
const { re: x0, im: x1 } = Fp2.reim(x);
const { re: y0, im: y1 } = Fp2.reim(y);
const tmp = y1 > _0n ? y1 * _2n : y0 * _2n;
const sort = Boolean((tmp / Fp.ORDER) & _1n);
const z2 = x0;
return concatB(
setMask(numberToBytesBE(x1, len), { sort, compressed: true }),
numberToBytesBE(z2, len)
);
}
// To verify curve parameters, see pairing-friendly-curves spec:
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-pairing-friendly-curves-09
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-pairing-friendly-curves-11
// Basic math is done over finite fields over p.
// More complicated math is done over polynominal extension fields.
// To simplify calculations in Fp12, we construct extension tower:
@ -988,27 +1084,31 @@ const COMPRESSED_ZERO = Fp.toBytes(bitSet(bitSet(0n, I_BIT_POS, true), S_BIT_POS
// Here goes constants && point encoding format
export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
// Fields
Fr,
Fp,
Fp2,
Fp6,
Fp12,
// order; z⁴ z² + 1
r: Fr.ORDER, // Same as N in other curves
fields: {
Fp,
Fp2,
Fp6,
Fp12,
Fr,
},
// G1 is the order-q subgroup of E1(Fp) : y² = x³ + 4, #E1(Fp) = h1q, where
// characteristic; z + (z⁴ - z² + 1)(z - 1)²/3
G1: {
Fp,
// cofactor; (z - 1)²/3
h: 0x396c8c005555e1568c00aaab0000aaabn,
h: BigInt('0x396c8c005555e1568c00aaab0000aaab'),
// generator's coordinates
// x = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507
// y = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569
Gx: 0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bbn,
Gy: 0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1n,
Gx: BigInt(
'0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb'
),
Gy: BigInt(
'0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1'
),
a: Fp.ZERO,
b: 4n,
htfDefaults: { ...htfDefaults, m: 1 },
b: _4n,
htfDefaults: { ...htfDefaults, m: 1, DST: 'BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_NUL_' },
wrapPrivateKey: true,
allowInfinityPoint: true,
// Checks is the point resides in prime-order subgroup.
@ -1017,18 +1117,19 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
// https://eprint.iacr.org/2021/1130.pdf
isTorsionFree: (c, point): boolean => {
// φ endomorphism
const cubicRootOfUnityModP =
0x5f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffen;
const cubicRootOfUnityModP = BigInt(
'0x5f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe'
);
const phi = new c(Fp.mul(point.px, cubicRootOfUnityModP), point.py, point.pz);
// todo: unroll
const xP = point.multiplyUnsafe(bls12_381.CURVE.x).negate(); // [x]P
const u2P = xP.multiplyUnsafe(bls12_381.CURVE.x); // [u2]P
const xP = point.multiplyUnsafe(bls12_381.params.x).negate(); // [x]P
const u2P = xP.multiplyUnsafe(bls12_381.params.x); // [u2]P
return u2P.equals(phi);
// https://eprint.iacr.org/2019/814.pdf
// (z² 1)/3
// const c1 = 0x396c8c005555e1560000000055555555n;
// const c1 = BigInt('0x396c8c005555e1560000000055555555');
// const P = this;
// const S = P.sigma();
// const Q = S.double();
@ -1040,33 +1141,39 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
},
// Clear cofactor of G1
// https://eprint.iacr.org/2019/403
clearCofactor: (c, point) => {
clearCofactor: (_c, point) => {
// return this.multiplyUnsafe(CURVE.h);
return point.multiplyUnsafe(bls12_381.CURVE.x).add(point); // x*P + P
return point.multiplyUnsafe(bls12_381.params.x).add(point); // x*P + P
},
mapToCurve: (scalars: bigint[]) => {
const { x, y } = G1_SWU(Fp.create(scalars[0]));
return isogenyMapG1(x, y);
},
fromBytes: (bytes: Uint8Array): AffinePoint<Fp> => {
if (bytes.length === 48) {
const { compressed, infinity, sort, value } = parseMask(bytes);
if (value.length === 48 && compressed) {
// TODO: Fp.bytes
const P = Fp.ORDER;
const compressedValue = bytesToNumberBE(bytes);
const bflag = bitGet(compressedValue, I_BIT_POS);
const compressedValue = bytesToNumberBE(value);
// Zero
if (bflag === 1n) return { x: 0n, y: 0n };
const x = Fp.create(compressedValue & Fp.MASK);
const right = Fp.add(Fp.pow(x, 3n), Fp.create(bls12_381.CURVE.G1.b)); // y² = x³ + b
if (infinity) {
if (x !== _0n) throw new Error('G1: non-empty compressed point at infinity');
return { x: _0n, y: _0n };
}
const right = Fp.add(Fp.pow(x, _3n), Fp.create(bls12_381.params.G1b)); // y² = x³ + b
let y = Fp.sqrt(right);
if (!y) throw new Error('Invalid compressed G1 point');
const aflag = bitGet(compressedValue, C_BIT_POS);
if ((y * 2n) / P !== aflag) y = Fp.neg(y);
if ((y * _2n) / P !== BigInt(sort)) y = Fp.neg(y);
return { x: Fp.create(x), y: Fp.create(y) };
} else if (bytes.length === 96) {
} else if (value.length === 96 && !compressed) {
// Check if the infinity flag is set
if ((bytes[0] & (1 << 6)) !== 0) return bls12_381.G1.ProjectivePoint.ZERO.toAffine();
const x = bytesToNumberBE(bytes.slice(0, Fp.BYTES));
const y = bytesToNumberBE(bytes.slice(Fp.BYTES));
const x = bytesToNumberBE(value.subarray(0, Fp.BYTES));
const y = bytesToNumberBE(value.subarray(Fp.BYTES));
if (infinity) {
if (x !== _0n || y !== _0n) throw new Error('G1: non-empty point at infinity');
return bls12_381.G1.ProjectivePoint.ZERO.toAffine();
}
return { x: Fp.create(x), y: Fp.create(y) };
} else {
throw new Error('Invalid point G1, expected 48/96 bytes');
@ -1078,10 +1185,8 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
if (isCompressed) {
if (isZero) return COMPRESSED_ZERO.slice();
const P = Fp.ORDER;
let num;
num = bitSet(x, C_BIT_POS, Boolean((y * 2n) / P)); // set aflag
num = bitSet(num, S_BIT_POS, true);
return numberToBytesBE(num, Fp.BYTES);
const sort = Boolean((y * _2n) / P);
return setMask(numberToBytesBE(x, Fp.BYTES), { compressed: true, sort });
} else {
if (isZero) {
// 2x PUBLIC_KEY_LENGTH
@ -1092,6 +1197,30 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
}
}
},
ShortSignature: {
fromHex(hex: Hex): ProjPointType<Fp> {
const { infinity, sort, value } = parseMask(ensureBytes('signatureHex', hex, 48));
const P = Fp.ORDER;
const compressedValue = bytesToNumberBE(value);
// Zero
if (infinity) return bls12_381.G1.ProjectivePoint.ZERO;
const x = Fp.create(compressedValue & Fp.MASK);
const right = Fp.add(Fp.pow(x, _3n), Fp.create(bls12_381.params.G1b)); // y² = x³ + b
let y = Fp.sqrt(right);
if (!y) throw new Error('Invalid compressed G1 point');
const aflag = BigInt(sort);
if ((y * _2n) / P !== aflag) y = Fp.neg(y);
const point = bls12_381.G1.ProjectivePoint.fromAffine({ x, y });
point.assertValidity();
return point;
},
toRawBytes(point: ProjPointType<Fp>) {
return signatureG1ToRawBytes(point);
},
toHex(point: ProjPointType<Fp>) {
return bytesToHex(signatureG1ToRawBytes(point));
},
},
},
// G2 is the order-q subgroup of E2(Fp²) : y² = x³+4(1+√1),
// where Fp2 is Fp[√1]/(x2+1). #E2(Fp2 ) = h2q, where
@ -1100,21 +1229,33 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
G2: {
Fp: Fp2,
// cofactor
h: 0x5d543a95414e7f1091d50792876a202cd91de4547085abaa68a205b2e5a7ddfa628f1cb4d9e82ef21537e293a6691ae1616ec6e786f0c70cf1c38e31c7238e5n,
h: BigInt(
'0x5d543a95414e7f1091d50792876a202cd91de4547085abaa68a205b2e5a7ddfa628f1cb4d9e82ef21537e293a6691ae1616ec6e786f0c70cf1c38e31c7238e5'
),
Gx: Fp2.fromBigTuple([
0x024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8n,
0x13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7en,
BigInt(
'0x024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8'
),
BigInt(
'0x13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e'
),
]),
// y =
// 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582,
// 1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905
Gy: Fp2.fromBigTuple([
0x0ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801n,
0x0606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79ben,
BigInt(
'0x0ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801'
),
BigInt(
'0x0606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be'
),
]),
a: Fp2.ZERO,
b: Fp2.fromBigTuple([4n, 4n]),
hEff: 0xbc69f08f2ee75b3584c6a0ea91b352888e2a8e9145ad7689986ff031508ffe1329c2f178731db956d82bf015d1212b02ec0ec69d7477c1ae954cbc06689f6a359894c0adebbf6b4e8020005aaa95551n,
b: Fp2.fromBigTuple([_4n, _4n]),
hEff: BigInt(
'0xbc69f08f2ee75b3584c6a0ea91b352888e2a8e9145ad7689986ff031508ffe1329c2f178731db956d82bf015d1212b02ec0ec69d7477c1ae954cbc06689f6a359894c0adebbf6b4e8020005aaa95551'
),
htfDefaults: { ...htfDefaults },
wrapPrivateKey: true,
allowInfinityPoint: true,
@ -1127,7 +1268,7 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
// It returns false for shitty points.
// https://eprint.iacr.org/2021/1130.pdf
isTorsionFree: (c, P): boolean => {
return P.multiplyUnsafe(bls12_381.CURVE.x).negate().equals(G2psi(c, P)); // ψ(P) == [u](P)
return P.multiplyUnsafe(bls12_381.params.x).negate().equals(G2psi(c, P)); // ψ(P) == [u](P)
// Older version: https://eprint.iacr.org/2019/814.pdf
// Ψ²(P) => Ψ³(P) => [z]Ψ³(P) where z = -x => [z]Ψ³(P) - Ψ²(P) + P == O
// return P.psi2().psi().mulNegX().subtract(psi2).add(P).isZero();
@ -1137,7 +1278,7 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
// https://eprint.iacr.org/2017/419.pdf
// prettier-ignore
clearCofactor: (c, P) => {
const { x } = bls12_381.CURVE;
const x = bls12_381.params.x;
let t1 = P.multiplyUnsafe(x).negate(); // [-x]P
let t2 = G2psi(c, P); // Ψ(P)
let t3 = P.double(); // 2P
@ -1151,90 +1292,89 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
return Q; // [x²-x-1]P + [x-1]Ψ(P) + Ψ²(2P)
},
fromBytes: (bytes: Uint8Array): AffinePoint<Fp2> => {
const m_byte = bytes[0] & 0xe0;
if (m_byte === 0x20 || m_byte === 0x60 || m_byte === 0xe0) {
throw new Error('Invalid encoding flag: ' + m_byte);
const { compressed, infinity, sort, value } = parseMask(bytes);
if (
(!compressed && !infinity && sort) || // 00100000
(!compressed && infinity && sort) || // 01100000
(sort && infinity && compressed) // 11100000
) {
throw new Error('Invalid encoding flag: ' + (bytes[0] & 0b1110_0000));
}
const bitC = m_byte & 0x80; // compression bit
const bitI = m_byte & 0x40; // point at infinity bit
const bitS = m_byte & 0x20; // sign bit
const L = Fp.BYTES;
const slc = (b: Uint8Array, from: number, to?: number) => bytesToNumberBE(b.slice(from, to));
if (bytes.length === 96 && bitC) {
const { b } = bls12_381.CURVE.G2;
if (value.length === 96 && compressed) {
const b = bls12_381.params.G2b;
const P = Fp.ORDER;
bytes[0] = bytes[0] & 0x1f; // clear flags
if (bitI) {
if (infinity) {
// check that all bytes are 0
if (bytes.reduce((p, c) => (p !== 0 ? c + 1 : c), 0) > 0) {
if (value.reduce((p, c) => (p !== 0 ? c + 1 : c), 0) > 0) {
throw new Error('Invalid compressed G2 point');
}
return { x: Fp2.ZERO, y: Fp2.ZERO };
}
const x_1 = slc(bytes, 0, L);
const x_0 = slc(bytes, L, 2 * L);
const x_1 = slc(value, 0, L);
const x_0 = slc(value, L, 2 * L);
const x = Fp2.create({ c0: Fp.create(x_0), c1: Fp.create(x_1) });
const right = Fp2.add(Fp2.pow(x, 3n), b); // y² = x³ + 4 * (u+1) = x³ + b
const right = Fp2.add(Fp2.pow(x, _3n), b); // y² = x³ + 4 * (u+1) = x³ + b
let y = Fp2.sqrt(right);
const Y_bit = y.c1 === 0n ? (y.c0 * 2n) / P : (y.c1 * 2n) / P ? 1n : 0n;
y = bitS > 0 && Y_bit > 0 ? y : Fp2.neg(y);
const Y_bit = y.c1 === _0n ? (y.c0 * _2n) / P : (y.c1 * _2n) / P ? _1n : _0n;
y = sort && Y_bit > 0 ? y : Fp2.neg(y);
return { x, y };
} else if (bytes.length === 192 && !bitC) {
// Check if the infinity flag is set
if ((bytes[0] & (1 << 6)) !== 0) {
} else if (value.length === 192 && !compressed) {
if (infinity) {
if (value.reduce((p, c) => (p !== 0 ? c + 1 : c), 0) > 0) {
throw new Error('Invalid uncompressed G2 point');
}
return { x: Fp2.ZERO, y: Fp2.ZERO };
}
const x1 = slc(bytes, 0, L);
const x0 = slc(bytes, L, 2 * L);
const y1 = slc(bytes, 2 * L, 3 * L);
const y0 = slc(bytes, 3 * L, 4 * L);
const x1 = slc(value, 0, L);
const x0 = slc(value, L, 2 * L);
const y1 = slc(value, 2 * L, 3 * L);
const y0 = slc(value, 3 * L, 4 * L);
return { x: Fp2.fromBigTuple([x0, x1]), y: Fp2.fromBigTuple([y0, y1]) };
} else {
throw new Error('Invalid point G2, expected 96/192 bytes');
}
},
toBytes: (c, point, isCompressed) => {
const { BYTES: len, ORDER: P } = Fp;
const isZero = point.equals(c.ZERO);
const { x, y } = point.toAffine();
if (isCompressed) {
const P = Fp.ORDER;
if (isZero) return concatB(COMPRESSED_ZERO, numberToBytesBE(0n, Fp.BYTES));
const flag = Boolean(y.c1 === 0n ? (y.c0 * 2n) / P : (y.c1 * 2n) / P);
// set compressed & sign bits (looks like different offsets than for G1/Fp?)
let x_1 = bitSet(x.c1, C_BIT_POS, flag);
x_1 = bitSet(x_1, S_BIT_POS, true);
return concatB(numberToBytesBE(x_1, Fp.BYTES), numberToBytesBE(x.c0, Fp.BYTES));
if (isZero) return concatB(COMPRESSED_ZERO, numberToBytesBE(_0n, len));
const flag = Boolean(y.c1 === _0n ? (y.c0 * _2n) / P : (y.c1 * _2n) / P);
return concatB(
setMask(numberToBytesBE(x.c1, len), { compressed: true, sort: flag }),
numberToBytesBE(x.c0, len)
);
} else {
if (isZero) return concatB(new Uint8Array([0x40]), new Uint8Array(4 * Fp.BYTES - 1)); // bytes[0] |= 1 << 6;
if (isZero) return concatB(new Uint8Array([0x40]), new Uint8Array(4 * len - 1)); // bytes[0] |= 1 << 6;
const { re: x0, im: x1 } = Fp2.reim(x);
const { re: y0, im: y1 } = Fp2.reim(y);
return concatB(
numberToBytesBE(x1, Fp.BYTES),
numberToBytesBE(x0, Fp.BYTES),
numberToBytesBE(y1, Fp.BYTES),
numberToBytesBE(y0, Fp.BYTES)
numberToBytesBE(x1, len),
numberToBytesBE(x0, len),
numberToBytesBE(y1, len),
numberToBytesBE(y0, len)
);
}
},
Signature: {
// TODO: Optimize, it's very slow because of sqrt.
decode(hex: Hex): ProjPointType<Fp2> {
hex = ensureBytes('signatureHex', hex);
fromHex(hex: Hex): ProjPointType<Fp2> {
const { infinity, sort, value } = parseMask(ensureBytes('signatureHex', hex));
const P = Fp.ORDER;
const half = hex.length / 2;
const half = value.length / 2;
if (half !== 48 && half !== 96)
throw new Error('Invalid compressed signature length, must be 96 or 192');
const z1 = bytesToNumberBE(hex.slice(0, half));
const z2 = bytesToNumberBE(hex.slice(half));
const z1 = bytesToNumberBE(value.slice(0, half));
const z2 = bytesToNumberBE(value.slice(half));
// Indicates the infinity point
const bflag1 = bitGet(z1, I_BIT_POS);
if (bflag1 === 1n) return bls12_381.G2.ProjectivePoint.ZERO;
if (infinity) return bls12_381.G2.ProjectivePoint.ZERO;
const x1 = Fp.create(z1 & Fp.MASK);
const x2 = Fp.create(z2);
const x = Fp2.create({ c0: x2, c1: x1 });
const y2 = Fp2.add(Fp2.pow(x, 3n), bls12_381.CURVE.G2.b); // y² = x³ + 4
const y2 = Fp2.add(Fp2.pow(x, _3n), bls12_381.params.G2b); // y² = x³ + 4
// The slow part
let y = Fp2.sqrt(y2);
if (!y) throw new Error('Failed to find a square root');
@ -1242,32 +1382,26 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
// Choose the y whose leftmost bit of the imaginary part is equal to the a_flag1
// If y1 happens to be zero, then use the bit of y0
const { re: y0, im: y1 } = Fp2.reim(y);
const aflag1 = bitGet(z1, 381);
const isGreater = y1 > 0n && (y1 * 2n) / P !== aflag1;
const isZero = y1 === 0n && (y0 * 2n) / P !== aflag1;
const aflag1 = BigInt(sort);
const isGreater = y1 > _0n && (y1 * _2n) / P !== aflag1;
const isZero = y1 === _0n && (y0 * _2n) / P !== aflag1;
if (isGreater || isZero) y = Fp2.neg(y);
const point = bls12_381.G2.ProjectivePoint.fromAffine({ x, y });
point.assertValidity();
return point;
},
encode(point: ProjPointType<Fp2>) {
// NOTE: by some reasons it was missed in bls12-381, looks like bug
point.assertValidity();
if (point.equals(bls12_381.G2.ProjectivePoint.ZERO))
return concatB(COMPRESSED_ZERO, numberToBytesBE(0n, Fp.BYTES));
const a = point.toAffine();
const { re: x0, im: x1 } = Fp2.reim(a.x);
const { re: y0, im: y1 } = Fp2.reim(a.y);
const tmp = y1 > 0n ? y1 * 2n : y0 * 2n;
const aflag1 = Boolean((tmp / Fp.ORDER) & 1n);
const z1 = bitSet(bitSet(x1, 381, aflag1), S_BIT_POS, true);
const z2 = x0;
return concatB(numberToBytesBE(z1, Fp.BYTES), numberToBytesBE(z2, Fp.BYTES));
toRawBytes(point: ProjPointType<Fp2>) {
return signatureG2ToRawBytes(point);
},
toHex(point: ProjPointType<Fp2>) {
return bytesToHex(signatureG2ToRawBytes(point));
},
},
},
// The BLS parameter x for BLS12-381
x: BLS_X,
params: {
x: BLS_X, // The BLS parameter x for BLS12-381
r: Fr.ORDER, // order; z⁴ z² + 1; CURVE.n from other curves
},
htfDefaults,
hash: sha256,
randomBytes,

@ -6,8 +6,9 @@ import { Field } from './abstract/modular.js';
/**
* bn254 pairing-friendly curve.
* Previously known as alt_bn_128, when it had 128-bit security.
* Recent research shown it's weaker, the naming has been adjusted to its prime bit count.
* https://github.com/zcash/zcash/issues/2502
* 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),

@ -1,19 +1,19 @@
/*! 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, ExtPointType } from './abstract/edwards.js';
import { ExtPointType, twistedEdwards } from './abstract/edwards.js';
import { montgomery } from './abstract/montgomery.js';
import { mod, pow2, isNegativeLE, Field, FpSqrtEven } from './abstract/modular.js';
import { Field, FpSqrtEven, isNegativeLE, mod, pow2 } from './abstract/modular.js';
import {
equalBytes,
bytesToHex,
bytesToNumberLE,
numberToBytesLE,
Hex,
ensureBytes,
equalBytes,
Hex,
numberToBytesLE,
} from './abstract/utils.js';
import * as htf from './abstract/hash-to-curve.js';
import { AffinePoint } from './abstract/curve.js';
import { createHasher, htfBasicOpts, expand_message_xmd } from './abstract/hash-to-curve.js';
import { AffinePoint, Group } from './abstract/curve.js';
/**
* ed25519 Twisted Edwards curve with following addons:
@ -34,6 +34,7 @@ const ED25519_SQRT_M1 = BigInt(
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;
@ -51,6 +52,7 @@ function ed25519_pow_2_252_3(x: bigint) {
// ^ 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
@ -61,6 +63,7 @@ function adjustScalarBytes(bytes: Uint8Array): Uint8Array {
bytes[31] |= 64; // 0b0100_0000
return bytes;
}
// sqrt(u/v)
function uvRatio(u: bigint, v: bigint): { isValid: boolean; value: bigint } {
const P = ED25519_P;
@ -95,16 +98,16 @@ export const ED25519_TORSION_SUBGROUP = [
const Fp = Field(ED25519_P, undefined, true);
const ED25519_DEF = {
const ed25519Defaults = {
// Param: a
a: BigInt(-1),
// Equal to -121665/121666 over finite field.
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
// Finite field 𝔽p over which we'll do calculations; 2n**255n - 19n
Fp,
// Subgroup order: how many points ed25519 has
// 2n ** 252n + 27742317777372353535851937790883648493n;
// Subgroup order: how many points curve has
// 2n**252n + 27742317777372353535851937790883648493n;
n: BigInt('7237005577332262213973186563042994240857116359379907606001950938285454250989'),
// Cofactor
h: BigInt(8),
@ -120,7 +123,8 @@ const ED25519_DEF = {
uvRatio,
} as const;
export const ed25519 = twistedEdwards(ED25519_DEF);
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(
@ -130,28 +134,61 @@ function ed25519_domain(data: Uint8Array, ctx: Uint8Array, phflag: boolean) {
data
);
}
export const ed25519ctx = twistedEdwards({ ...ED25519_DEF, domain: ed25519_domain });
export const ed25519ph = twistedEdwards({
...ED25519_DEF,
export const ed25519ctx = /* @__PURE__ */ twistedEdwards({
...ed25519Defaults,
domain: ed25519_domain,
preHash: sha512,
});
export const ed25519ph = /* @__PURE__ */ twistedEdwards({
...ed25519Defaults,
domain: ed25519_domain,
prehash: sha512,
});
export const x25519 = 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,
});
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
@ -204,12 +241,13 @@ function map_to_curve_elligator2_curve25519(u: bigint) {
let y = Fp.cmov(y2, y1, e3); // 36. y = CMOV(y2, y1, e3) # If e3, y = y1, else y = y2
let 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)
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)
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
@ -225,28 +263,30 @@ function map_to_curve_elligator2_edwards25519(u: bigint) {
const inv = Fp.invertBatch([xd, yd]); // batch division
return { x: Fp.mul(xn, inv[0]), y: Fp.mul(yn, inv[1]) }; // 13. return (xn, xd, yn, yd)
}
const { hashToCurve, encodeToCurve } = htf.createHasher(
ed25519.ExtendedPoint,
(scalars: bigint[]) => map_to_curve_elligator2_edwards25519(scalars[0]),
{
DST: 'edwards25519_XMD:SHA-512_ELL2_RO_',
encodeDST: 'edwards25519_XMD:SHA-512_ELL2_NU_',
p: Fp.ORDER,
m: 1,
k: 128,
expand: 'xmd',
hash: sha512,
}
);
export { hashToCurve, encodeToCurve };
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 RistrettoPoint)) throw new Error('RistrettoPoint expected');
if (!(other instanceof RistPoint)) throw new Error('RistrettoPoint expected');
}
// √(-1) aka √(a) aka 2^((p-1)/4)
const SQRT_M1 = BigInt(
'19681161376707505956807079304988542015446066515923890162744021073123829784752'
);
const SQRT_M1 = ED25519_SQRT_M1;
// √(ad - 1)
const SQRT_AD_MINUS_ONE = BigInt(
'25063068953384623474111414158702152701244531502492656460079210482610430750235'
@ -303,32 +343,31 @@ function calcElligatorRistrettoMap(r0: bigint): 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
*/
export class RistrettoPoint {
static BASE = new RistrettoPoint(ed25519.ExtendedPoint.BASE);
static ZERO = new RistrettoPoint(ed25519.ExtendedPoint.ZERO);
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 RistrettoPoint(ed25519.ExtendedPoint.fromAffine(ap));
return new RistPoint(ed25519.ExtendedPoint.fromAffine(ap));
}
/**
* Takes uniform output of 64-bit hash function like sha512 and converts it to `RistrettoPoint`.
* Takes uniform output of 64-byte hash function like sha512 and converts it to `RistrettoPoint`.
* The hash-to-group operation applies Elligator twice and adds the results.
* **Note:** this is one-way map, there is no conversion from point to hash.
* https://ristretto.group/formulas/elligator.html
* @param hex 64-bit output of a hash function
* @param hex 64-byte output of a hash function
*/
static hashToCurve(hex: Hex): RistrettoPoint {
static hashToCurve(hex: Hex): RistPoint {
hex = ensureBytes('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 RistrettoPoint(R1.add(R2));
return new RistPoint(R1.add(R2));
}
/**
@ -336,7 +375,7 @@ export class RistrettoPoint {
* 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): RistrettoPoint {
static fromHex(hex: Hex): RistPoint {
hex = ensureBytes('ristrettoHex', hex, 32);
const { a, d } = ed25519.CURVE;
const P = ed25519.CURVE.Fp.ORDER;
@ -360,7 +399,7 @@ export class RistrettoPoint {
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 RistrettoPoint(new ed25519.ExtendedPoint(x, y, _1n, t));
return new RistPoint(new ed25519.ExtendedPoint(x, y, _1n, t));
}
/**
@ -404,7 +443,7 @@ export class RistrettoPoint {
}
// Compare one point to another.
equals(other: RistrettoPoint): boolean {
equals(other: RistPoint): boolean {
assertRstPoint(other);
const { ex: X1, ey: Y1 } = this.ep;
const { ex: X2, ey: Y2 } = other.ep;
@ -415,31 +454,44 @@ export class RistrettoPoint {
return one || two;
}
add(other: RistrettoPoint): RistrettoPoint {
add(other: RistPoint): RistPoint {
assertRstPoint(other);
return new RistrettoPoint(this.ep.add(other.ep));
return new RistPoint(this.ep.add(other.ep));
}
subtract(other: RistrettoPoint): RistrettoPoint {
subtract(other: RistPoint): RistPoint {
assertRstPoint(other);
return new RistrettoPoint(this.ep.subtract(other.ep));
return new RistPoint(this.ep.subtract(other.ep));
}
multiply(scalar: bigint): RistrettoPoint {
return new RistrettoPoint(this.ep.multiply(scalar));
multiply(scalar: bigint): RistPoint {
return new RistPoint(this.ep.multiply(scalar));
}
multiplyUnsafe(scalar: bigint): RistrettoPoint {
return new RistrettoPoint(this.ep.multiplyUnsafe(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;
})();
// https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/14/
// Appendix B. Hashing to ristretto255
export const hash_to_ristretto255 = (msg: Uint8Array, options: htf.htfBasicOpts) => {
// 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 = htf.expand_message_xmd(msg, DST, 64, sha512);
const P = RistrettoPoint.hashToCurve(uniform_bytes);
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

@ -1,14 +1,25 @@
/*! 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 { twistedEdwards } from './abstract/edwards.js';
import { mod, pow2, Field } from './abstract/modular.js';
import { ExtPointType, twistedEdwards } from './abstract/edwards.js';
import { mod, pow2, Field, isNegativeLE } from './abstract/modular.js';
import { montgomery } from './abstract/montgomery.js';
import * as htf from './abstract/hash-to-curve.js';
import { createHasher, htfBasicOpts, expand_message_xof } from './abstract/hash-to-curve.js';
import {
bytesToHex,
bytesToNumberLE,
ensureBytes,
equalBytes,
Hex,
numberToBytesLE,
} from './abstract/utils.js';
import { AffinePoint, Group } from './abstract/curve.js';
/**
* Edwards448 (not Ed448-Goldilocks) curve with following addons:
* * X448 ECDH
* - X448 ECDH
* - Decaf cofactor elimination
* - Elligator hash-to-group / point indistinguishability
* Conforms to RFC 8032 https://www.rfc-editor.org/rfc/rfc8032.html#section-5.2
*/
@ -18,15 +29,16 @@ 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;
// prettier-ignore
const _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _11n = BigInt(11);
// prettier-ignore
const _22n = BigInt(22), _44n = BigInt(44), _88n = BigInt(88), _223n = BigInt(223);
const b2 = (x * x * x) % P;
const b3 = (b2 * b2 * x) % P;
const b6 = (pow2(b3, _3n, P) * b3) % P;
@ -53,6 +65,28 @@ function adjustScalarBytes(bytes: Uint8Array): Uint8Array {
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 = {
@ -62,13 +96,14 @@ const ED448_DEF = {
d: BigInt(
'726838724295606890549323807888004534353641360687318060281490199180612328166730772686396383698676545930088884461843637361053498018326358'
),
// Finite field 𝔽p over which we'll do calculations; 2n ** 448n - 2n ** 224n - 1n
// Finite field 𝔽p over which we'll do calculations; 2n**448n - 2n**224n - 1n
Fp,
// 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),
@ -93,67 +128,52 @@ const ED448_DEF = {
data
);
},
// Constant-time ratio of u to v. Allows to combine inversion and square root u/√v.
// Uses algo from RFC8032 5.1.3.
uvRatio: (u: bigint, v: bigint): { isValid: boolean; value: bigint } => {
const P = ed448P;
// https://datatracker.ietf.org/doc/html/rfc8032#section-5.2.3
// To compute the square root of (u/v), the first step is to compute the
// candidate root x = (u/v)^((p+1)/4). This can be done using the
// following trick, to use a single modular powering for both the
// inversion of v and the square root:
// x = (u/v)^((p+1)/4) = u³v(u⁵v³)^((p-3)/4) (mod p)
const u2v = mod(u * u * v, P); // u²v
const u3v = mod(u2v * u, P); // u³v
const u5v3 = mod(u3v * u2v * v, P); // u⁵v³
const root = ed448_pow_Pminus3div4(u5v3);
const x = mod(u3v * root, P);
// Verify that root is exists
const x2 = mod(x * x, P); // x²
// If vx² = u, the recovered x-coordinate is x. Otherwise, no
// square root exists, and the decoding fails.
return { isValid: mod(x2 * v, P) === u, value: x };
},
uvRatio,
} as const;
export const ed448 = twistedEdwards(ED448_DEF);
export const ed448 = /* @__PURE__ */ twistedEdwards(ED448_DEF);
// NOTE: there is no ed448ctx, since ed448 supports ctx by default
export const ed448ph = twistedEdwards({ ...ED448_DEF, preHash: shake256_64 });
export const ed448ph = /* @__PURE__ */ twistedEdwards({ ...ED448_DEF, prehash: shake256_64 });
export const x448 = montgomery({
a: BigInt(156326),
montgomeryBits: 448,
nByteLength: 57,
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,
// The 4-isogeny maps between the Montgomery curve and this Edwards
// curve are:
// (u, v) = (y^2/x^2, (2 - x^2 - y^2)*y/x^3)
// (x, y) = (4*v*(u^2 - 1)/(u^4 - 2*u^2 + 4*v^2 + 1),
// -(u^5 - 2*u^3 - 4*u*v^2 + u)/
// (u^5 - 2*u^2*v^2 - 2*u^3 - 2*v^2 + u))
// xyToU: (p: PointType) => {
// const P = ed448P;
// const { x, y } = p;
// if (x === _0n) throw new Error(`Point with x=0 doesn't have mapping`);
// const invX = invert(x * x, P); // x^2
// const u = mod(y * y * invX, P); // (y^2/x^2)
// return numberToBytesLE(u, 56);
// },
});
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
@ -183,6 +203,7 @@ function map_to_curve_elligator2_curve448(u: bigint) {
y = Fp.cmov(y, Fp.neg(y), e2 !== e3); // 26. y = CMOV(y, -y, e2 XOR e3)
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
@ -195,10 +216,10 @@ function map_to_curve_elligator2_edwards448(u: bigint) {
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
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 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
@ -226,17 +247,234 @@ function map_to_curve_elligator2_edwards448(u: bigint) {
return { x: Fp.mul(xEn, inv[0]), y: Fp.mul(yEn, inv[1]) }; // 38. return (xEn, xEd, yEn, yEd)
}
const { hashToCurve, encodeToCurve } = htf.createHasher(
ed448.ExtendedPoint,
(scalars: bigint[]) => map_to_curve_elligator2_edwards448(scalars[0]),
{
DST: 'edwards448_XOF:SHAKE256_ELL2_RO_',
encodeDST: 'edwards448_XOF:SHAKE256_ELL2_NU_',
p: Fp.ORDER,
m: 1,
k: 224,
expand: 'xof',
hash: shake256,
}
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'
);
export { hashToCurve, encodeToCurve };
// 1 / √(-d)
const INVSQRT_MINUS_D = BigInt(
'315019913931389607337177038330951043522456072897266928557328499619017160722351061360252776265186336876723201881398623946864393857820716'
);
// Calculates 1/√(number)
const invertSqrt = (number: bigint) => uvRatio(_1n, number);
const MAX_448B = BigInt(
'0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
);
const bytes448ToNumberLE = (bytes: Uint8Array) =>
ed448.CURVE.Fp.create(bytesToNumberLE(bytes) & MAX_448B);
type ExtendedPoint = ExtPointType;
// Computes Elligator map for Decaf
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-ristretto255-decaf448-07#name-element-derivation-2
function calcElligatorDecafMap(r0: bigint): ExtendedPoint {
const { d } = ed448.CURVE;
const P = ed448.CURVE.Fp.ORDER;
const mod = ed448.CURVE.Fp.create;
const r = mod(-(r0 * r0)); // 1
const u0 = mod(d * (r - _1n)); // 2
const u1 = mod((u0 + _1n) * (u0 - r)); // 3
const { isValid: was_square, value: v } = uvRatio(ONE_MINUS_TWO_D, mod((r + _1n) * u1)); // 4
let v_prime = v; // 5
if (!was_square) v_prime = mod(r0 * v);
let sgn = _1n; // 6
if (!was_square) sgn = mod(-_1n);
const s = mod(v_prime * (r + _1n)); // 7
let s_abs = s;
if (isNegativeLE(s, P)) s_abs = mod(-s);
const s2 = s * s;
const W0 = mod(s_abs * _2n); // 8
const W1 = mod(s2 + _1n); // 9
const W2 = mod(s2 - _1n); // 10
const W3 = mod(v_prime * s * (r - _1n) * ONE_MINUS_TWO_D + sgn); // 11
return new ed448.ExtendedPoint(mod(W0 * W3), mod(W2 * W1), mod(W1 * W3), mod(W0 * W2));
}
/**
* Each ed448/ExtendedPoint has 4 different equivalent points. This can be
* a source of bugs for protocols like ring signatures. Decaf was created to solve this.
* Decaf point operates in X:Y:Z:T extended coordinates like ExtendedPoint,
* but it should work in its own namespace: do not combine those two.
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-ristretto255-decaf448
*/
class DcfPoint implements Group<DcfPoint> {
static BASE: DcfPoint;
static ZERO: DcfPoint;
// Private property to discourage combining ExtendedPoint + DecafPoint
// Always use Decaf encoding/decoding instead.
constructor(private readonly ep: ExtendedPoint) {}
static fromAffine(ap: AffinePoint<bigint>) {
return new DcfPoint(ed448.ExtendedPoint.fromAffine(ap));
}
/**
* Takes uniform output of 112-byte hash function like shake256 and converts it to `DecafPoint`.
* The hash-to-group operation applies Elligator twice and adds the results.
* **Note:** this is one-way map, there is no conversion from point to hash.
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-ristretto255-decaf448-07#name-element-derivation-2
* @param hex 112-byte output of a hash function
*/
static hashToCurve(hex: Hex): DcfPoint {
hex = ensureBytes('decafHash', hex, 112);
const r1 = bytes448ToNumberLE(hex.slice(0, 56));
const R1 = calcElligatorDecafMap(r1);
const r2 = bytes448ToNumberLE(hex.slice(56, 112));
const R2 = calcElligatorDecafMap(r2);
return new DcfPoint(R1.add(R2));
}
/**
* Converts decaf-encoded string to decaf point.
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-ristretto255-decaf448-07#name-decode-2
* @param hex Decaf-encoded 56 bytes. Not every 56-byte string is valid decaf encoding
*/
static fromHex(hex: Hex): DcfPoint {
hex = ensureBytes('decafHex', hex, 56);
const { d } = ed448.CURVE;
const P = ed448.CURVE.Fp.ORDER;
const mod = ed448.CURVE.Fp.create;
const emsg = 'DecafPoint.fromHex: the hex is not valid encoding of DecafPoint';
const s = bytes448ToNumberLE(hex);
// 1. Check that s_bytes is the canonical encoding of a field element, or else abort.
// 2. Check that s is non-negative, or else abort
if (!equalBytes(numberToBytesLE(s, 56), hex) || isNegativeLE(s, P)) throw new Error(emsg);
const s2 = mod(s * s); // 1
const u1 = mod(_1n + s2); // 2
const u1sq = mod(u1 * u1);
const u2 = mod(u1sq - _4n * d * s2); // 3
const { isValid, value: invsqrt } = invertSqrt(mod(u2 * u1sq)); // 4
let u3 = mod((s + s) * invsqrt * u1 * SQRT_MINUS_D); // 5
if (isNegativeLE(u3, P)) u3 = mod(-u3);
const x = mod(u3 * invsqrt * u2 * INVSQRT_MINUS_D); // 6
const y = mod((_1n - s2) * invsqrt * u1); // 7
const t = mod(x * y); // 8
if (!isValid) throw new Error(emsg);
return new DcfPoint(new ed448.ExtendedPoint(x, y, _1n, t));
}
/**
* Encodes decaf point to Uint8Array.
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-ristretto255-decaf448-07#name-encode-2
*/
toRawBytes(): Uint8Array {
let { ex: x, ey: _y, ez: z, et: t } = this.ep;
const P = ed448.CURVE.Fp.ORDER;
const mod = ed448.CURVE.Fp.create;
const u1 = mod(mod(x + t) * mod(x - t)); // 1
const x2 = mod(x * x);
const { value: invsqrt } = invertSqrt(mod(u1 * ONE_MINUS_D * x2)); // 2
let ratio = mod(invsqrt * u1 * SQRT_MINUS_D); // 3
if (isNegativeLE(ratio, P)) ratio = mod(-ratio);
const u2 = mod(INVSQRT_MINUS_D * ratio * z - t); // 4
let s = mod(ONE_MINUS_D * invsqrt * x * u2); // 5
if (isNegativeLE(s, P)) s = mod(-s);
return numberToBytesLE(s, 56);
}
toHex(): string {
return bytesToHex(this.toRawBytes());
}
toString(): string {
return this.toHex();
}
// Compare one point to another.
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-ristretto255-decaf448-07#name-equals-2
equals(other: DcfPoint): boolean {
assertDcfPoint(other);
const { ex: X1, ey: Y1 } = this.ep;
const { ex: X2, ey: Y2 } = other.ep;
const mod = ed448.CURVE.Fp.create;
// (x1 * y2 == y1 * x2)
return mod(X1 * Y2) === mod(Y1 * X2);
}
add(other: DcfPoint): DcfPoint {
assertDcfPoint(other);
return new DcfPoint(this.ep.add(other.ep));
}
subtract(other: DcfPoint): DcfPoint {
assertDcfPoint(other);
return new DcfPoint(this.ep.subtract(other.ep));
}
multiply(scalar: bigint): DcfPoint {
return new DcfPoint(this.ep.multiply(scalar));
}
multiplyUnsafe(scalar: bigint): DcfPoint {
return new DcfPoint(this.ep.multiplyUnsafe(scalar));
}
double(): DcfPoint {
return new DcfPoint(this.ep.double());
}
negate(): DcfPoint {
return new DcfPoint(this.ep.negate());
}
}
export const DecafPoint = /* @__PURE__ */ (() => {
// decaf448 base point is ed448 base x 2
// https://github.com/dalek-cryptography/curve25519-dalek/blob/59837c6ecff02b77b9d5ff84dbc239d0cf33ef90/vendor/ristretto.sage#L699
if (!DcfPoint.BASE) DcfPoint.BASE = new DcfPoint(ed448.ExtendedPoint.BASE).multiply(_2n);
if (!DcfPoint.ZERO) DcfPoint.ZERO = new DcfPoint(ed448.ExtendedPoint.ZERO);
return DcfPoint;
})();
// Hashing to decaf448. https://www.rfc-editor.org/rfc/rfc9380#appendix-C
export const hashToDecaf448 = (msg: Uint8Array, options: htfBasicOpts) => {
const d = options.DST;
const DST = typeof d === 'string' ? utf8ToBytes(d) : d;
const uniform_bytes = expand_message_xof(msg, DST, 112, 224, shake256);
const P = DcfPoint.hashToCurve(uniform_bytes);
return P;
};
export const hash_to_decaf448 = hashToDecaf448; // legacy

@ -11,7 +11,7 @@ import { Field } from './abstract/modular.js';
* jubjub does not use EdDSA, so `hash`/sha512 params are passed because interface expects them.
*/
export const jubjub = twistedEdwards({
export const jubjub = /* @__PURE__ */ twistedEdwards({
// Params: a, d
a: BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000'),
d: BigInt('0x2a9318e74bfa2b48f5fd9207e6bd7fd4292d7f6d37579d2601065fd6d6343eb1'),

@ -3,44 +3,39 @@ 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 * as htf from './abstract/hash-to-curve.js';
import { createHasher } from './abstract/hash-to-curve.js';
// NIST secp256r1 aka P256
// NIST secp256r1 aka p256
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/nist/P-256
// Field over which we'll do calculations; 2n**224n * (2n**32n-1n) + 2n**192n + 2n**96n-1n
const Fp = Field(BigInt('0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff'));
const CURVE_A = Fp.create(BigInt('-3'));
const CURVE_B = BigInt('0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b');
const mapSWU = mapToCurveSimpleSWU(Fp, {
A: CURVE_A,
B: CURVE_B,
Z: Fp.create(BigInt('-10')),
});
// 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;
export const P256 = createCurve(
{
// Params: a, b
a: CURVE_A,
b: CURVE_B,
Fp,
// Curve order, total count of valid points in the field
n: BigInt('0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551'),
// Base point (x, y) aka generator point
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 { hashToCurve, encodeToCurve } = htf.createHasher(
secp256r1.ProjectivePoint,
(scalars: bigint[]) => mapSWU(scalars[0]),
{
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,
@ -48,6 +43,6 @@ const { hashToCurve, encodeToCurve } = htf.createHasher(
k: 128,
expand: 'xmd',
hash: sha256,
}
);
export { hashToCurve, encodeToCurve };
}))();
export const hashToCurve = /* @__PURE__ */ (() => htf.hashToCurve)();
export const encodeToCurve = /* @__PURE__ */ (() => htf.encodeToCurve)();

@ -3,12 +3,12 @@ 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 * as htf from './abstract/hash-to-curve.js';
import { createHasher } from './abstract/hash-to-curve.js';
// NIST secp384r1 aka P384
// NIST secp384r1 aka p384
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/nist/P-384
// Field over which we'll do calculations. 2n**384n - 2n**128n - 2n**96n + 2n**32n - 1n
// Field over which we'll do calculations.
// prettier-ignore
const P = BigInt('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff');
const Fp = Field(P);
@ -16,35 +16,30 @@ const CURVE_A = Fp.create(BigInt('-3'));
// prettier-ignore
const CURVE_B = BigInt('0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef');
const mapSWU = mapToCurveSimpleSWU(Fp, {
A: CURVE_A,
B: CURVE_B,
Z: Fp.create(BigInt('-12')),
});
// prettier-ignore
export const P384 = createCurve({
// Params: a, b
a: CURVE_A,
b: CURVE_B,
// Field over which we'll do calculations. 2n**384n - 2n**128n - 2n**96n + 2n**32n - 1n
Fp,
// Curve order, total count of valid points in the field.
n: BigInt('0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973'),
// Base point (x, y) aka generator point
Gx: BigInt('0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7'),
Gy: BigInt('0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f'),
h: BigInt(1),
lowS: false,
} as const,
sha384
);
export const secp384r1 = P384;
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 { hashToCurve, encodeToCurve } = htf.createHasher(
secp384r1.ProjectivePoint,
(scalars: bigint[]) => mapSWU(scalars[0]),
{
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,
@ -52,6 +47,6 @@ const { hashToCurve, encodeToCurve } = htf.createHasher(
k: 192,
expand: 'xmd',
hash: sha384,
}
);
export { hashToCurve, encodeToCurve };
}))();
export const hashToCurve = /* @__PURE__ */ (() => htf.hashToCurve)();
export const encodeToCurve = /* @__PURE__ */ (() => htf.encodeToCurve)();

@ -3,48 +3,59 @@ 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 * as htf from './abstract/hash-to-curve.js';
import { createHasher } from './abstract/hash-to-curve.js';
// NIST secp521r1 aka P521
// NIST secp521r1 aka p521
// Note that it's 521, which differs from 512 of its hash function.
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/nist/P-521
// Field over which we'll do calculations; 2n**521n - 1n
// Field over which we'll do calculations.
// prettier-ignore
const P = BigInt('0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
const Fp = Field(P);
const CURVE_A = Fp.create(BigInt('-3'));
// prettier-ignore
const CURVE_B = BigInt('0x0051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00');
const mapSWU = mapToCurveSimpleSWU(Fp, {
A: CURVE_A,
B: CURVE_B,
Z: Fp.create(BigInt('-4')),
});
// prettier-ignore
export const P521 = createCurve({
// Params: a, b
a: CURVE_A,
b: CURVE_B,
const CURVE = {
a: Fp.create(BigInt('-3')),
b: BigInt(
'0x0051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00'
),
Fp,
// Curve order, total count of valid points in the field
n: BigInt('0x01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409'),
// Base point (x, y) aka generator point
Gx: BigInt('0x00c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66'),
Gy: BigInt('0x011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650'),
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;
export const secp521r1 = p521;
const { hashToCurve, encodeToCurve } = htf.createHasher(
secp521r1.ProjectivePoint,
(scalars: bigint[]) => mapSWU(scalars[0]),
{
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,
@ -52,6 +63,6 @@ const { hashToCurve, encodeToCurve } = htf.createHasher(
k: 256,
expand: 'xmd',
hash: sha512,
}
);
export { hashToCurve, encodeToCurve };
}))();
export const hashToCurve = /* @__PURE__ */ (() => htf.hashToCurve)();
export const encodeToCurve = /* @__PURE__ */ (() => htf.encodeToCurve)();

3
src/package.json Normal file

@ -0,0 +1,3 @@
{
"type": "module"
}

@ -5,7 +5,7 @@ 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 * as htf from './abstract/hash-to-curve.js';
import { createHasher, isogenyMap } from './abstract/hash-to-curve.js';
import { createCurve } from './_shortw_utils.js';
const secp256k1P = BigInt('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f');
@ -131,7 +131,7 @@ function lift_x(x: bigint): PointType<bigint> {
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
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;
@ -199,7 +199,7 @@ function schnorrVerify(signature: Hex, message: Hex, publicKey: Hex): boolean {
}
}
export const schnorr = {
export const schnorr = /* @__PURE__ */ (() => ({
getPublicKey: schnorrGetPublicKey,
sign: schnorrSign,
verify: schnorrVerify,
@ -212,58 +212,63 @@ export const schnorr = {
taggedHash,
mod,
},
};
}))();
const isoMap = htf.isogenyMap(
Fp,
[
// xNum
const isoMap = /* @__PURE__ */ (() =>
isogenyMap(
Fp,
[
'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 = mapToCurveSimpleSWU(Fp, {
A: BigInt('0x3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533'),
B: BigInt('1771'),
Z: Fp.create(BigInt('-11')),
});
export const { hashToCurve, encodeToCurve } = htf.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,
}
);
// 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)();

@ -3,9 +3,9 @@ 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
// NIST secp192r1 aka p192
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/secg/secp192r1
export const P192 = createCurve(
export const p192 = createCurve(
{
// Params: a, b
a: BigInt('0xfffffffffffffffffffffffffffffffefffffffffffffffc'),
@ -22,9 +22,9 @@ export const P192 = createCurve(
},
sha256
);
export const secp192r1 = P192;
export const secp192r1 = p192;
export const P224 = createCurve(
export const p224 = createCurve(
{
// Params: a, b
a: BigInt('0xfffffffffffffffffffffffffffffffefffffffffffffffffffffffe'),
@ -41,4 +41,4 @@ export const P224 = createCurve(
},
sha224
);
export const secp224r1 = P224;
export const secp224r1 = p224;

@ -2,7 +2,7 @@ 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 as toHex } from '../esm/abstract/utils.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';
@ -12,9 +12,13 @@ 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/bn.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 = {
@ -30,19 +34,19 @@ const FIELDS = {
pallas: { Fp: [pallas.CURVE.Fp] },
vesta: { Fp: [vesta.CURVE.Fp] },
bls12: {
Fp: [bls12_381.CURVE.Fp],
Fp: [bls12_381.fields.Fp],
Fp2: [
bls12_381.CURVE.Fp2,
fc.array(fc.bigInt(1n, bls12_381.CURVE.Fp.ORDER - 1n), {
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.CURVE.Fp6],
// Fp6: [bls12_381.fields.Fp6],
Fp12: [
bls12_381.CURVE.Fp12,
fc.array(fc.bigInt(1n, bls12_381.CURVE.Fp.ORDER - 1n), {
bls12_381.fields.Fp12,
fc.array(fc.bigInt(1n, bls12_381.fields.Fp.ORDER - 1n), {
minLength: 12,
maxLength: 12,
}),
@ -221,7 +225,7 @@ for (const c in FIELDS) {
const isSquare = mod.FpIsSquare(Fp);
// Not implemented
if (Fp !== bls12_381.CURVE.Fp12) {
if (Fp !== bls12_381.fields.Fp12) {
should('multiply/sqrt', () => {
fc.assert(
fc.property(FC_BIGINT, (num) => {
@ -591,6 +595,18 @@ for (const name in CURVES) {
{ 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();
@ -742,6 +758,54 @@ should('bigInt private keys', () => {
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) {

@ -5,10 +5,16 @@ import { describe, should } from 'micro-should';
import { wNAF } from '../esm/abstract/curve.js';
import { bytesToHex, utf8ToBytes } from '../esm/abstract/utils.js';
import { hash_to_field } from '../esm/abstract/hash-to-curve.js';
import { bls12_381 as bls } from '../esm/bls12-381.js';
import { bls12_381 as bls, bls12_381 } from '../esm/bls12-381.js';
import * as utils from '../esm/abstract/utils.js';
import zkVectors from './bls12-381/zkcrypto/converted.json' assert { type: 'json' };
import pairingVectors from './bls12-381/go_pairing_vectors/pairing.json' assert { type: 'json' };
const G1_VECTORS = readFileSync('./test/bls12-381/bls12-381-g1-test-vectors.txt', 'utf-8')
.trim()
.split('\n')
.map((l) => l.split(':'));
const G2_VECTORS = readFileSync('./test/bls12-381/bls12-381-g2-test-vectors.txt', 'utf-8')
.trim()
.split('\n')
@ -24,11 +30,10 @@ const SCALAR_VECTORS = readFileSync('./test/bls12-381/bls12-381-scalar-test-vect
const NUM_RUNS = Number(process.env.RUNS_COUNT || 10); // reduce to 1 to shorten test time
fc.configureGlobal({ numRuns: NUM_RUNS });
const { Fp2 } = bls;
const G1Point = bls.G1.ProjectivePoint;
const G2Point = bls.G2.ProjectivePoint;
const G1Aff = (x, y) => G1Point.fromAffine({ x, y });
const CURVE_ORDER = bls.CURVE.r;
const CURVE_ORDER = bls.params.r;
const FC_MSG = fc.hexaString({ minLength: 64, maxLength: 64 });
const FC_MSG_5 = fc.array(FC_MSG, { minLength: 5, maxLength: 5 });
@ -38,14 +43,19 @@ const B_192_40 = '40'.padEnd(192, '0');
const B_384_40 = '40'.padEnd(384, '0'); // [0x40, 0, 0...]
const getPubKey = (priv) => bls.getPublicKey(priv);
function replaceZeroPoint(item) {
const zeros = '0000000000000000000000000000000000000000000000000000000000000000';
const ones = '1000000000000000000000000000000000000000000000000000000000000001';
return item === zeros ? ones : item;
}
function equal(a, b, comment) {
deepStrictEqual(a.equals(b), true, `eq(${comment})`);
}
const { Fp, Fp2 } = bls.fields;
// Fp
describe('bls12-381 Fp', () => {
const Fp = bls.Fp;
const FC_BIGINT = fc.bigInt(1n, Fp.ORDER - 1n);
should('multiply/sqrt', () => {
@ -60,8 +70,7 @@ describe('bls12-381 Fp', () => {
// Fp2
describe('bls12-381 Fp2', () => {
const Fp = bls.Fp;
const Fp2 = bls.Fp2;
const { Fp, Fp2 } = bls.fields;
const FC_BIGINT = fc.bigInt(1n, Fp.ORDER - 1n);
const FC_BIGINT_2 = fc.array(FC_BIGINT, { minLength: 2, maxLength: 2 });
@ -149,7 +158,7 @@ describe('bls12-381 Fp2', () => {
// Point
describe('bls12-381 Point', () => {
const Fp = bls.Fp;
const { Fp } = bls.fields;
const FC_BIGINT = fc.bigInt(1n, Fp.ORDER - 1n);
const PointG1 = G1Point;
const PointG2 = G2Point;
@ -557,9 +566,12 @@ describe('bls12-381 Point', () => {
];
// Use wNAF allow scalars higher than CURVE.r
const w = wNAF(G2Point, 1);
const hEff = BigInt(
'0xbc69f08f2ee75b3584c6a0ea91b352888e2a8e9145ad7689986ff031508ffe1329c2f178731db956d82bf015d1212b02ec0ec69d7477c1ae954cbc06689f6a359894c0adebbf6b4e8020005aaa95551'
);
for (let p of points) {
const ours = p.clearCofactor();
const shouldBe = w.unsafeLadder(p, bls.CURVE.G2.hEff);
const shouldBe = w.unsafeLadder(p, hEff);
deepStrictEqual(ours.equals(shouldBe), true, 'clearLast');
}
});
@ -577,12 +589,12 @@ describe('bls12-381/basic', () => {
deepStrictEqual(g1.x, G1Point.ZERO.x);
deepStrictEqual(g1.y, G1Point.ZERO.y);
// Test Non-Zero
const x = bls.Fp.create(
const x = Fp.create(
BigInt(
'0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb'
)
);
const y = bls.Fp.create(
const y = Fp.create(
BigInt(
'0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1'
)
@ -603,12 +615,12 @@ describe('bls12-381/basic', () => {
deepStrictEqual(g1.x, G1Point.ZERO.x);
deepStrictEqual(g1.y, G1Point.ZERO.y);
// Test Non-Zero
const x = bls.Fp.create(
const x = Fp.create(
BigInt(
'0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb'
)
);
const y = bls.Fp.create(
const y = Fp.create(
BigInt(
'0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1'
)
@ -689,12 +701,12 @@ describe('bls12-381/basic', () => {
// Test Zero
deepStrictEqual(G1Point.ZERO.toHex(false), B_192_40);
// Test Non-Zero
const x = bls.Fp.create(
const x = Fp.create(
BigInt(
'0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb'
)
);
const y = bls.Fp.create(
const y = Fp.create(
BigInt(
'0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1'
)
@ -710,12 +722,12 @@ describe('bls12-381/basic', () => {
// Test Zero
deepStrictEqual(G1Point.ZERO.toHex(false), B_192_40);
// Test Non-Zero
const x = bls.Fp.create(
const x = Fp.create(
BigInt(
'0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb'
)
);
const y = bls.Fp.create(
const y = Fp.create(
BigInt(
'0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1'
)
@ -801,32 +813,32 @@ describe('bls12-381/basic', () => {
throws(() => G2Point.fromPrivateKey(0n));
});
const VALID_G1 = new G1Point(
bls.Fp.create(
Fp.create(
3609742242174788176010452839163620388872641749536604986743596621604118973777515189035770461528205168143692110933639n
),
bls.Fp.create(
Fp.create(
1619277690257184054444116778047375363103842303863153349133480657158810226683757397206929105479676799650932070320089n
),
bls.Fp.create(1n)
Fp.create(1n)
);
const VALID_G1_2 = new G1Point(
bls.Fp.create(
Fp.create(
1206972466279728255044019580914616126536509750250979180256809997983196363639429409634110400978470384566664128085207n
),
bls.Fp.create(
Fp.create(
2991142246317096160788653339959532007292638191110818490939476869616372888657136539642598243964263069435065725313423n
),
bls.Fp.create(1n)
Fp.create(1n)
);
const INVALID_G1 = new G1Point(
bls.Fp.create(
Fp.create(
499001545268060011619089734015590154568173930614466321429631711131511181286230338880376679848890024401335766847607n
),
bls.Fp.create(
Fp.create(
3934582309586258715640230772291917282844636728991757779640464479794033391537662970190753981664259511166946374555673n
),
bls.Fp.create(1n)
Fp.create(1n)
);
should('aggregate pubkeys', () => {
@ -846,6 +858,13 @@ describe('bls12-381/basic', () => {
});
// should aggregate signatures
should(`produce correct short signatures (${G1_VECTORS.length} vectors)`, () => {
for (let vector of G1_VECTORS) {
const [priv, msg, expected] = vector;
const sig = bls.signShortSignature(msg, priv);
deepStrictEqual(bytesToHex(sig), expected);
}
});
should(`produce correct signatures (${G2_VECTORS.length} vectors)`, () => {
for (let vector of G2_VECTORS) {
const [priv, msg, expected] = vector;
@ -855,15 +874,15 @@ describe('bls12-381/basic', () => {
});
should(`produce correct scalars (${SCALAR_VECTORS.length} vectors)`, () => {
const options = {
p: bls.CURVE.r,
p: bls.params.r,
m: 1,
expand: undefined,
expand: '_internal_pass',
};
for (let vector of SCALAR_VECTORS) {
const [okmAscii, expectedHex] = vector;
const expected = BigInt('0x' + expectedHex);
const okm = utf8ToBytes(okmAscii);
const scalars = hash_to_field(okm, 1, Object.assign({}, bls.CURVE.htfDefaults, options));
const scalars = hash_to_field(okm, 1, Object.assign({}, bls.G2.CURVE.htfDefaults, options));
deepStrictEqual(scalars[0][0], expected);
}
});
@ -871,7 +890,8 @@ describe('bls12-381/basic', () => {
// Pairing
describe('pairing', () => {
const { pairing, Fp12 } = bls;
const { pairing } = bls;
const { Fp12 } = bls.fields;
const G1 = G1Point.BASE;
const G2 = G2Point.BASE;
@ -968,7 +988,7 @@ describe('pairing', () => {
});
});
// hashToCurve
describe('hash-to-curve', () => {
describe('hash-to-curve (against Killic)', () => {
// Point G1
const VECTORS_G1 = [
{
@ -998,16 +1018,17 @@ describe('hash-to-curve', () => {
'047a85d6898416a0899e26219bca7c4f0fa682717199de196b02b95eaf9fb55456ac3b810e78571a1b7f5692b7c58ab6',
},
];
for (let i = 0; i < VECTORS_G1.length; i++) {
const t = VECTORS_G1[i];
should(`hashToCurve/G1 Killic (${i})`, () => {
const p = bls.G1.hashToCurve(t.msg, {
DST: 'BLS12381G1_XMD:SHA-256_SSWU_RO_TESTGEN',
describe('hashToCurve G1', () => {
for (let i = 0; i < VECTORS_G1.length; i++) {
const t = VECTORS_G1[i];
should(`${i}`, () => {
const p = bls.G1.hashToCurve(t.msg, {
DST: 'BLS12381G1_XMD:SHA-256_SSWU_RO_TESTGEN',
});
deepStrictEqual(p.toHex(false), t.expected);
});
deepStrictEqual(p.toHex(false), t.expected);
});
}
}
});
const VECTORS_ENCODE_G1 = [
{
msg: utf8ToBytes(''),
@ -1036,15 +1057,17 @@ describe('hash-to-curve', () => {
'094bfdfe3e552447433b5a00967498a3f1314b86ce7a7164c8a8f4131f99333b30a574607e301d5f774172c627fd0bca',
},
];
for (let i = 0; i < VECTORS_ENCODE_G1.length; i++) {
const t = VECTORS_ENCODE_G1[i];
should(`hashToCurve/G1 (Killic, encodeToCurve) (${i})`, () => {
const p = bls.G1.encodeToCurve(t.msg, {
DST: 'BLS12381G1_XMD:SHA-256_SSWU_NU_TESTGEN',
describe('encodeToCurve G1', () => {
for (let i = 0; i < VECTORS_ENCODE_G1.length; i++) {
const t = VECTORS_ENCODE_G1[i];
should(`(${i})`, () => {
const p = bls.G1.encodeToCurve(t.msg, {
DST: 'BLS12381G1_XMD:SHA-256_SSWU_NU_TESTGEN',
});
deepStrictEqual(p.toHex(false), t.expected);
});
deepStrictEqual(p.toHex(false), t.expected);
});
}
}
});
// Point G2
const VECTORS_G2 = [
{
@ -1082,16 +1105,17 @@ describe('hash-to-curve', () => {
'15c1d4f1a685bb63ee67ca1fd96155e3d091e852a684b78d085fd34f6091e5249ddddbdcf2e7ec82ce6c04c63647eeb7',
},
];
for (let i = 0; i < VECTORS_G2.length; i++) {
const t = VECTORS_G2[i];
should(`hashToCurve/G2 Killic (${i})`, () => {
const p = bls.G2.hashToCurve(t.msg, {
DST: 'BLS12381G2_XMD:SHA-256_SSWU_RO_TESTGEN',
describe('hashToCurve G2', () => {
for (let i = 0; i < VECTORS_G2.length; i++) {
const t = VECTORS_G2[i];
should(`${i}`, () => {
const p = bls.G2.hashToCurve(t.msg, {
DST: 'BLS12381G2_XMD:SHA-256_SSWU_RO_TESTGEN',
});
deepStrictEqual(p.toHex(false), t.expected);
});
deepStrictEqual(p.toHex(false), t.expected);
});
}
}
});
const VECTORS_ENCODE_G2 = [
{
msg: utf8ToBytes(''),
@ -1128,15 +1152,17 @@ describe('hash-to-curve', () => {
'09e5c8242dd7281ad32c03fe4af3f19167770016255fb25ad9b67ec51d62fade31a1af101e8f6172ec2ee8857662be3a',
},
];
for (let i = 0; i < VECTORS_ENCODE_G2.length; i++) {
const t = VECTORS_ENCODE_G2[i];
should(`hashToCurve/G2 (Killic, encodeToCurve) (${i})`, () => {
const p = bls.G2.encodeToCurve(t.msg, {
DST: 'BLS12381G2_XMD:SHA-256_SSWU_NU_TESTGEN',
describe('encodeToCurve G2', () => {
for (let i = 0; i < VECTORS_ENCODE_G2.length; i++) {
const t = VECTORS_ENCODE_G2[i];
should(`${i}`, () => {
const p = bls.G2.encodeToCurve(t.msg, {
DST: 'BLS12381G2_XMD:SHA-256_SSWU_NU_TESTGEN',
});
deepStrictEqual(p.toHex(false), t.expected);
});
deepStrictEqual(p.toHex(false), t.expected);
});
}
}
});
});
describe('verify()', () => {
@ -1147,6 +1173,8 @@ describe('verify()', () => {
const pub = bls.getPublicKey(priv);
const res = bls.verify(sig, msg, pub);
deepStrictEqual(res, true, `${priv}-${msg}`);
const resHex = bls.verify(bytesToHex(sig), msg, pub);
deepStrictEqual(resHex, true, `${priv}-${msg}-hex`);
}
});
should('not verify signature with wrong message', () => {
@ -1167,6 +1195,43 @@ describe('verify()', () => {
const invPub = bls.getPublicKey(invPriv);
const res = bls.verify(sig, msg, invPub);
deepStrictEqual(res, false);
const resHex = bls.verify(bytesToHex(sig), msg, invPub);
deepStrictEqual(resHex, false);
}
});
should('verify signed message (short signatures)', () => {
for (let i = 0; i < NUM_RUNS; i++) {
const [priv, msg] = G1_VECTORS[i];
const sig = bls.signShortSignature(msg, priv);
const pub = bls.getPublicKeyForShortSignatures(priv);
const res = bls.verifyShortSignature(sig, msg, pub);
deepStrictEqual(res, true, `${priv}-${msg}`);
const resHex = bls.verifyShortSignature(bytesToHex(sig), msg, pub);
deepStrictEqual(resHex, true, `${priv}-${msg}`);
}
});
should('not verify signature with wrong message (short signatures)', () => {
for (let i = 0; i < NUM_RUNS; i++) {
const [priv, msg] = G1_VECTORS[i];
const invMsg = G1_VECTORS[i + 1][1];
const sig = bls.signShortSignature(msg, priv);
const pub = bls.getPublicKeyForShortSignatures(priv);
const res = bls.verifyShortSignature(sig, invMsg, pub);
deepStrictEqual(res, false);
const resHex = bls.verifyShortSignature(bytesToHex(sig), invMsg, pub);
deepStrictEqual(resHex, false);
}
});
should('not verify signature with wrong key', () => {
for (let i = 0; i < NUM_RUNS; i++) {
const [priv, msg] = G1_VECTORS[i];
const sig = bls.signShortSignature(msg, priv);
const invPriv = G1_VECTORS[i + 1][1].padStart(64, '0');
const invPub = bls.getPublicKeyForShortSignatures(invPriv);
const res = bls.verifyShortSignature(sig, msg, invPub);
deepStrictEqual(res, false);
const resHex = bls.verifyShortSignature(bytesToHex(sig), msg, invPub);
deepStrictEqual(resHex, false);
}
});
describe('batch', () => {
@ -1179,6 +1244,10 @@ describe('verify()', () => {
const signatures = messages.map((message, i) => bls.sign(message, privateKeys[i]));
const aggregatedSignature = bls.aggregateSignatures(signatures);
deepStrictEqual(bls.verifyBatch(aggregatedSignature, messages, publicKey), true);
deepStrictEqual(
bls.verifyBatch(bytesToHex(aggregatedSignature), messages, publicKey),
true
);
})
);
});
@ -1197,6 +1266,10 @@ describe('verify()', () => {
bls.verifyBatch(aggregatedSignature, wrongMessages, publicKey),
messages.every((m, i) => m === wrongMessages[i])
);
deepStrictEqual(
bls.verifyBatch(bytesToHex(aggregatedSignature), wrongMessages, publicKey),
messages.every((m, i) => m === wrongMessages[i])
);
})
);
});
@ -1219,6 +1292,10 @@ describe('verify()', () => {
bls.verifyBatch(aggregatedSignature, messages, wrongPublicKeys),
wrongPrivateKeys.every((p, i) => p === privateKeys[i])
);
deepStrictEqual(
bls.verifyBatch(bytesToHex(aggregatedSignature), messages, wrongPublicKeys),
wrongPrivateKeys.every((p, i) => p === privateKeys[i])
);
}
)
);
@ -1226,17 +1303,23 @@ describe('verify()', () => {
should('verify multi-signature as simple signature', () => {
fc.assert(
fc.property(FC_MSG, FC_BIGINT_5, (message, privateKeys) => {
message = replaceZeroPoint(message);
const publicKey = privateKeys.map(getPubKey);
const signatures = privateKeys.map((privateKey) => bls.sign(message, privateKey));
const aggregatedSignature = bls.aggregateSignatures(signatures);
const aggregatedPublicKey = bls.aggregatePublicKeys(publicKey);
deepStrictEqual(bls.verify(aggregatedSignature, message, aggregatedPublicKey), true);
deepStrictEqual(
bls.verify(bytesToHex(aggregatedSignature), message, aggregatedPublicKey),
true
);
})
);
});
should('not verify wrong multi-signature as simple signature', () => {
fc.assert(
fc.property(FC_MSG, FC_MSG, FC_BIGINT_5, (message, wrongMessage, privateKeys) => {
message = replaceZeroPoint(message);
const publicKey = privateKeys.map(getPubKey);
const signatures = privateKeys.map((privateKey) => bls.sign(message, privateKey));
const aggregatedSignature = bls.aggregateSignatures(signatures);
@ -1245,6 +1328,10 @@ describe('verify()', () => {
bls.verify(aggregatedSignature, wrongMessage, aggregatedPublicKey),
message === wrongMessage
);
deepStrictEqual(
bls.verify(bytesToHex(aggregatedSignature), wrongMessage, aggregatedPublicKey),
message === wrongMessage
);
})
);
});
@ -1259,7 +1346,7 @@ describe('bls12-381 deterministic', () => {
.reverse()
.reduce((acc, i) => acc + i);
const Fp12 = bls.Fp12;
const { Fp12 } = bls.fields;
should('Killic based/Pairing', () => {
const t = bls.pairing(G1Point.BASE, G2Point.BASE);
@ -1360,6 +1447,37 @@ describe('bls12-381 deterministic', () => {
}
}
});
should(`zkcrypt/G1 & G2 encoding edge cases`, () => {
const Fp = bls12_381.fields.Fp;
const S_BIT_POS = Fp.BITS; // C_bit, compression bit for serialization flag
const I_BIT_POS = Fp.BITS + 1; // I_bit, point-at-infinity bit for serialization flag
const C_BIT_POS = Fp.BITS + 2; // S_bit, sort bit for serialization flag
const VECTORS = [
{ pos: C_BIT_POS, shift: 7 }, // compression_flag_set = Choice::from((bytes[0] >> 7) & 1);
{ pos: I_BIT_POS, shift: 6 }, // infinity_flag_set = Choice::from((bytes[0] >> 6) & 1)
{ pos: S_BIT_POS, shift: 5 }, // sort_flag_set = Choice::from((bytes[0] >> 5) & 1)
];
for (const { pos, shift } of VECTORS) {
const d = utils.numberToBytesBE(utils.bitSet(0n, pos, Boolean(true)), Fp.BYTES);
deepStrictEqual((d[0] >> shift) & 1, 1, `${pos}`);
}
const baseC = G1Point.BASE.toRawBytes();
deepStrictEqual(baseC.length, 48);
const baseU = G1Point.BASE.toRawBytes(false);
deepStrictEqual(baseU.length, 96);
const compressedBit = baseU.slice();
compressedBit[0] |= 0b1000_0000; // add compression bit
throws(() => G1Point.fromHex(compressedBit), 'compressed bit'); // uncompressed point with compressed length
const uncompressedBit = baseC.slice();
uncompressedBit[0] &= 0b0111_1111; // remove compression bit
throws(() => G1Point.fromHex(uncompressedBit), 'uncompressed bit');
const infinityUncompressed = baseU.slice();
infinityUncompressed[0] |= 0b0100_0000;
throws(() => G1Point.fromHex(compressedBit), 'infinity uncompressed');
const infinityCompressed = baseC.slice();
infinityCompressed[0] |= 0b0100_0000;
throws(() => G1Point.fromHex(compressedBit), 'infinity compressed');
});
});
// ESM is broken.

@ -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

@ -1,14 +1,18 @@
import { sha512 } from '@noble/hashes/sha512';
import { hexToBytes, bytesToHex, randomBytes } from '@noble/hashes/utils';
import { deepStrictEqual, strictEqual, throws } from 'assert';
import { bytesToHex as hex, hexToBytes } from '@noble/hashes/utils';
import { deepStrictEqual, throws } from 'assert';
import { describe, should } from 'micro-should';
import { bytesToNumberLE, numberToBytesLE } from '../esm/abstract/utils.js';
import { default as x25519vectors } from './wycheproof/x25519_test.json' assert { type: 'json' };
import { ed25519ctx, ed25519ph, RistrettoPoint, x25519 } from '../esm/ed25519.js';
// const ed = ed25519;
const hex = bytesToHex;
// const Point = ed.ExtendedPoint;
import {
ed25519,
ed25519ctx,
ed25519ph,
edwardsToMontgomeryPub,
edwardsToMontgomeryPriv,
RistrettoPoint,
x25519,
} from '../esm/ed25519.js';
const VECTORS_RFC8032_CTX = [
{
@ -62,8 +66,14 @@ describe('RFC8032ctx', () => {
const v = VECTORS_RFC8032_CTX[i];
should(`${i}`, () => {
deepStrictEqual(hex(ed25519ctx.getPublicKey(v.secretKey)), v.publicKey);
deepStrictEqual(hex(ed25519ctx.sign(v.message, v.secretKey, v.context)), v.signature);
deepStrictEqual(ed25519ctx.verify(v.signature, v.message, v.publicKey, v.context), true);
deepStrictEqual(
hex(ed25519ctx.sign(v.message, v.secretKey, { context: v.context })),
v.signature
);
deepStrictEqual(
ed25519ctx.verify(v.signature, v.message, v.publicKey, { context: v.context }),
true
);
});
}
});
@ -93,14 +103,7 @@ describe('RFC8032ph', () => {
});
// x25519
should('X25519 base point', () => {
const { y } = ed25519ph.ExtendedPoint.BASE;
const { Fp } = ed25519ph.CURVE;
const u = Fp.create((y + 1n) * Fp.inv(1n - y));
deepStrictEqual(numberToBytesLE(u, 32), x25519.GuBytes);
});
describe('RFC7748', () => {
describe('RFC7748 X25519 ECDH', () => {
const rfc7748Mul = [
{
scalar: 'a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4',
@ -127,7 +130,7 @@ describe('RFC7748', () => {
];
for (let i = 0; i < rfc7748Iter.length; i++) {
const { scalar, iters } = rfc7748Iter[i];
should(`scalarMult iteration (${i})`, () => {
should(`scalarMult iteration x${iters}`, () => {
let k = x25519.GuBytes;
for (let i = 0, u = k; i < iters; i++) [k, u] = [x25519.scalarMult(k, u), k];
deepStrictEqual(hex(k), scalar);
@ -145,10 +148,66 @@ describe('RFC7748', () => {
deepStrictEqual(hex(x25519.scalarMult(alicePrivate, bobPublic)), shared);
deepStrictEqual(hex(x25519.scalarMult(bobPrivate, alicePublic)), shared);
});
});
describe('Wycheproof', () => {
should('X25519/getSharedSecret() should be commutative', () => {
for (let i = 0; i < 512; i++) {
const asec = x25519.utils.randomPrivateKey();
const apub = x25519.getPublicKey(asec);
const bsec = x25519.utils.randomPrivateKey();
const bpub = x25519.getPublicKey(bsec);
try {
deepStrictEqual(x25519.getSharedSecret(asec, bpub), x25519.getSharedSecret(bsec, apub));
} catch (error) {
console.error('not commutative', { asec, apub, bsec, bpub });
throw error;
}
}
});
should('edwardsToMontgomery should produce correct output', () => {
const edSecret = hexToBytes('77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a');
const edPublic = ed25519.getPublicKey(edSecret);
const xPrivate = edwardsToMontgomeryPriv(edSecret);
deepStrictEqual(
hex(xPrivate),
'a8cd44eb8e93319c0570bc11005c0e0189d34ff02f6c17773411ad191293c94f'
);
const xPublic = edwardsToMontgomeryPub(edPublic);
deepStrictEqual(
hex(xPublic),
'ed7749b4d989f6957f3bfde6c56767e988e21c9f8784d91d610011cd553f9b06'
);
});
should('edwardsToMontgomery should produce correct keyPair', () => {
const edSecret = ed25519.utils.randomPrivateKey();
const edPublic = ed25519.getPublicKey(edSecret);
const xSecret = edwardsToMontgomeryPriv(edSecret);
const expectedXPublic = x25519.getPublicKey(xSecret);
const xPublic = edwardsToMontgomeryPub(edPublic);
deepStrictEqual(xPublic, expectedXPublic);
});
should('ECDH through edwardsToMontgomery should be commutative', () => {
const edSecret1 = ed25519.utils.randomPrivateKey();
const edPublic1 = ed25519.getPublicKey(edSecret1);
const edSecret2 = ed25519.utils.randomPrivateKey();
const edPublic2 = ed25519.getPublicKey(edSecret2);
deepStrictEqual(
x25519.getSharedSecret(edwardsToMontgomeryPriv(edSecret1), edwardsToMontgomeryPub(edPublic2)),
x25519.getSharedSecret(edwardsToMontgomeryPriv(edSecret2), edwardsToMontgomeryPub(edPublic1))
);
});
should('base point', () => {
const { y } = ed25519ph.ExtendedPoint.BASE;
const { Fp } = ed25519ph.CURVE;
const u = Fp.create((y + 1n) * Fp.inv(1n - y));
deepStrictEqual(numberToBytesLE(u, 32), x25519.GuBytes);
});
const group = x25519vectors.testGroups[0];
should(`X25519`, () => {
should('wycheproof', () => {
for (let i = 0; i < group.tests.length; i++) {
const v = group.tests[i];
const comment = `(${i}, ${v.result}) ${v.comment}`;
@ -297,7 +356,7 @@ describe('ristretto255', () => {
// ESM is broken.
import url from 'url';
import { assert } from 'console';
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
should.run();
}

@ -1 +1,2 @@
export { numberToBytesLE } from '../esm/abstract/utils.js';
export { ed25519, ED25519_TORSION_SUBGROUP } from '../esm/ed25519.js';

@ -1,14 +1,19 @@
import { deepStrictEqual, strictEqual, throws } from 'assert';
import { readFileSync } from 'fs';
import { hexToBytes, bytesToHex, randomBytes } from '@noble/hashes/utils';
import { bytesToHex, concatBytes, hexToBytes, utf8ToBytes, randomBytes } from '@noble/hashes/utils';
import * as fc from 'fast-check';
import { describe, should } from 'micro-should';
import { ed25519, ED25519_TORSION_SUBGROUP } from './ed25519.helpers.js';
import { default as ed25519vectors } from './wycheproof/eddsa_test.json' assert { type: 'json' };
import { ed25519 as ed, ED25519_TORSION_SUBGROUP, numberToBytesLE } from './ed25519.helpers.js';
// Old vectors allow to test sign() because they include private key
import { default as ed25519vectors_OLD } from './ed25519/ed25519_test_OLD.json' assert { type: 'json' };
import { default as ed25519vectors } from './wycheproof/ed25519_test.json' assert { type: 'json' };
import { default as zip215 } from './ed25519/zip215.json' assert { type: 'json' };
import { default as edgeCases } from './ed25519/edge-cases.json' assert { type: 'json' };
// Any changes to the file will need to be aware of the fact
// the file is shared between noble-curves and noble-ed25519.
describe('ed25519', () => {
const ed = ed25519;
const hex = bytesToHex;
const Point = ed.ExtendedPoint;
@ -17,13 +22,6 @@ describe('ed25519', () => {
return hexToBytes(hex.padStart(64, '0'));
}
function utf8ToBytes(str) {
if (typeof str !== 'string') {
throw new Error(`utf8ToBytes expected string, got ${typeof str}`);
}
return new TextEncoder().encode(str);
}
ed.utils.precompute(8);
should('not accept >32byte private keys', () => {
@ -305,23 +303,25 @@ describe('ed25519', () => {
// https://zips.z.cash/zip-0215
// Vectors from https://gist.github.com/hdevalence/93ed42d17ecab8e42138b213812c8cc7
should('ZIP-215 compliance tests/should pass all of them', () => {
const str = utf8ToBytes('Zcash');
for (let v of zip215) {
let noble = false;
try {
noble = ed.verify(v.sig_bytes, str, v.vk_bytes);
} catch (e) {
noble = false;
describe('ZIP215', () => {
should('pass all compliance tests', () => {
const str = utf8ToBytes('Zcash');
for (let v of zip215) {
let noble = false;
try {
noble = ed.verify(v.sig_bytes, str, v.vk_bytes);
} catch (e) {
noble = false;
}
deepStrictEqual(noble, v.valid_zip215, JSON.stringify(v));
}
deepStrictEqual(noble, v.valid_zip215, JSON.stringify(v));
}
});
should('ZIP-215 compliance tests/disallows sig.s >= CURVE.n', () => {
// sig.R = BASE, sig.s = N+1
const sig =
'5866666666666666666666666666666666666666666666666666666666666666eed3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010';
throws(() => ed.verify(sig, 'deadbeef', Point.BASE));
});
should('disallow sig.s >= CURVE.n', () => {
// sig.R = BASE, sig.s = N+1
const sig =
'5866666666666666666666666666666666666666666666666666666666666666eed3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010';
deepStrictEqual(ed.verify(sig, 'deadbeef', Point.BASE), false);
});
});
// should('X25519/getSharedSecret() should be commutative', () => {
@ -346,9 +346,9 @@ describe('ed25519', () => {
// );
// });
should(`Wycheproof/ED25519`, () => {
for (let g = 0; g < ed25519vectors.testGroups.length; g++) {
const group = ed25519vectors.testGroups[g];
should(`wycheproof/ED25519 (OLD)`, () => {
for (let g = 0; g < ed25519vectors_OLD.testGroups.length; g++) {
const group = ed25519vectors_OLD.testGroups[g];
const key = group.key;
deepStrictEqual(hex(ed.getPublicKey(key.sk)), key.pk, `(${g}, public)`);
for (let i = 0; i < group.tests.length; i++) {
@ -370,7 +370,29 @@ describe('ed25519', () => {
}
});
should('Property test issue #1', () => {
should(`wycheproof/ED25519`, () => {
for (let g = 0; g < ed25519vectors.testGroups.length; g++) {
const group = ed25519vectors.testGroups[g];
const key = group.publicKey;
for (let i = 0; i < group.tests.length; i++) {
const v = group.tests[i];
const comment = `(${g}/${i}, ${v.result}): ${v.comment}`;
if (v.result === 'valid' || v.result === 'acceptable') {
deepStrictEqual(ed.verify(v.sig, v.msg, key.pk), true, comment);
} else if (v.result === 'invalid') {
let failed = false;
try {
failed = !ed.verify(v.sig, v.msg, key.pk);
} catch (error) {
failed = true;
}
deepStrictEqual(failed, true, comment);
} else throw new Error('unknown test result');
}
}
});
should('not mutate inputs', () => {
const message = new Uint8Array([12, 12, 12]);
const signature = ed.sign(message, to32Bytes(1n));
const publicKey = ed.getPublicKey(to32Bytes(1n)); // <- was 1n
@ -387,15 +409,42 @@ describe('ed25519', () => {
strictEqual(cleared.isTorsionFree(), true, `cleared must be torsionFree: ${hex}`);
}
});
});
should('ed25519 bug', () => {
const t = 81718630521762619991978402609047527194981150691135404693881672112315521837062n;
const point = ed25519.ExtendedPoint.fromAffine({ x: t, y: t });
throws(() => point.assertValidity());
// Otherwise (without assertValidity):
// const point2 = point.double();
// point2.toAffine(); // crash!
should('have strict SUF-CMA and SBS properties', () => {
// https://eprint.iacr.org/2020/1244
const list = [0, 1, 6, 7, 8, 9, 10, 11].map((i) => edgeCases[i]);
for (let v of list) {
const result = ed.verify(v.signature, v.message, v.pub_key, { zip215: false });
strictEqual(result, false, `zip215: false must not validate: ${v.signature}`);
}
});
should('not verify when sig.s >= CURVE.n', () => {
const privateKey = ed.utils.randomPrivateKey();
const message = Uint8Array.from([0xab, 0xbc, 0xcd, 0xde]);
const publicKey = ed.getPublicKey(privateKey);
const signature = ed.sign(message, privateKey);
const R = signature.slice(0, 32);
let s = signature.slice(32, 64);
s = bytesToHex(s.slice().reverse());
s = BigInt('0x' + s);
s = s + ed.CURVE.n;
s = numberToBytesLE(s, 32);
const sig_invalid = concatBytes(R, s);
deepStrictEqual(ed.verify(sig_invalid, message, publicKey), false);
});
should('not accept point without z, t', () => {
const t = 81718630521762619991978402609047527194981150691135404693881672112315521837062n;
const point = Point.fromAffine({ x: t, y: t });
throws(() => point.assertValidity());
// Otherwise (without assertValidity):
// const point2 = point.double();
// point2.toAffine(); // crash!
});
});
// ESM is broken.

@ -0,0 +1 @@
[{"message":"8c93255d71dcab10e8f379c26200f3c7bd5f09d9bc3068d3ef4edeb4853022b6","pub_key":"c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa","signature":"c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000"},{"message":"9bd9f44f4dcc75bd531b56b2cd280b0bb38fc1cd6d1230e14861d861de092e79","pub_key":"c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa","signature":"f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43a5bb704786be79fc476f91d3f3f89b03984d8068dcf1bb7dfc6637b45450ac04"},{"message":"aebf3f2601a0c8c5d39cc7d8911642f740b78168218da8471772b35f9d35b9ab","pub_key":"f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43","signature":"c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa8c4bd45aecaca5b24fb97bc10ac27ac8751a7dfe1baff8b953ec9f5833ca260e"},{"message":"9bd9f44f4dcc75bd531b56b2cd280b0bb38fc1cd6d1230e14861d861de092e79","pub_key":"cdb267ce40c5cd45306fa5d2f29731459387dbf9eb933b7bd5aed9a765b88d4d","signature":"9046a64750444938de19f227bb80485e92b83fdb4b6506c160484c016cc1852f87909e14428a7a1d62e9f22f3d3ad7802db02eb2e688b6c52fcd6648a98bd009"},{"message":"e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec4011eaccd55b53f56c","pub_key":"cdb267ce40c5cd45306fa5d2f29731459387dbf9eb933b7bd5aed9a765b88d4d","signature":"160a1cb0dc9c0258cd0a7d23e94d8fa878bcb1925f2c64246b2dee1796bed5125ec6bc982a269b723e0668e540911a9a6a58921d6925e434ab10aa7940551a09"},{"message":"e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec4011eaccd55b53f56c","pub_key":"cdb267ce40c5cd45306fa5d2f29731459387dbf9eb933b7bd5aed9a765b88d4d","signature":"21122a84e0b5fca4052f5b1235c80a537878b38f3142356b2c2384ebad4668b7e40bc836dac0f71076f9abe3a53f9c03c1ceeeddb658d0030494ace586687405"},{"message":"85e241a07d148b41e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec40","pub_key":"442aad9f089ad9e14647b1ef9099a1ff4798d78589e66f28eca69c11f582a623","signature":"e96f66be976d82e60150baecff9906684aebb1ef181f67a7189ac78ea23b6c0e547f7690a0e2ddcd04d87dbc3490dc19b3b3052f7ff0538cb68afb369ba3a514"},{"message":"85e241a07d148b41e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec40","pub_key":"442aad9f089ad9e14647b1ef9099a1ff4798d78589e66f28eca69c11f582a623","signature":"8ce5b96c8f26d0ab6c47958c9e68b937104cd36e13c33566acd2fe8d38aa19427e71f98a473474f2f13f06f97c20d58cc3f54b8bd0d272f42b695dd7e89a8c22"},{"message":"9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41","pub_key":"f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43","signature":"ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03be9678ac102edcd92b0210bb34d7428d12ffc5df5f37e359941266a4e35f0f"},{"message":"9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41","pub_key":"f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43","signature":"ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffca8c5b64cd208982aa38d4936621a4775aa233aa0505711d8fdcfdaa943d4908"},{"message":"e96b7021eb39c1a163b6da4e3093dcd3f21387da4cc4572be588fafae23c155b","pub_key":"ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff","signature":"a9d55260f765261eb9b84e106f665e00b867287a761990d7135963ee0a7d59dca5bb704786be79fc476f91d3f3f89b03984d8068dcf1bb7dfc6637b45450ac04"},{"message":"39a591f5321bbe07fd5a23dc2f39d025d74526615746727ceefd6e82ae65c06f","pub_key":"ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff","signature":"a9d55260f765261eb9b84e106f665e00b867287a761990d7135963ee0a7d59dca5bb704786be79fc476f91d3f3f89b03984d8068dcf1bb7dfc6637b45450ac04"}]

117
test/ed448-addons.test.js Normal file

@ -0,0 +1,117 @@
import { bytesToHex as hex, hexToBytes } from '@noble/hashes/utils';
import { deepStrictEqual, throws } from 'assert';
import { describe, should } from 'micro-should';
import { bytesToNumberLE } from '../esm/abstract/utils.js';
import { ed448, DecafPoint } from '../esm/ed448.js';
describe('decaf448', () => {
should('follow the byte encodings of small multiples', () => {
const encodingsOfSmallMultiples = [
// This is the identity point
'0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
// This is the basepoint
'6666666666666666666666666666666666666666666666666666666633333333333333333333333333333333333333333333333333333333',
// These are small multiples of the basepoint
'c898eb4f87f97c564c6fd61fc7e49689314a1f818ec85eeb3bd5514ac816d38778f69ef347a89fca817e66defdedce178c7cc709b2116e75',
'a0c09bf2ba7208fda0f4bfe3d0f5b29a543012306d43831b5adc6fe7f8596fa308763db15468323b11cf6e4aeb8c18fe44678f44545a69bc',
'b46f1836aa287c0a5a5653f0ec5ef9e903f436e21c1570c29ad9e5f596da97eeaf17150ae30bcb3174d04bc2d712c8c7789d7cb4fda138f4',
'1c5bbecf4741dfaae79db72dface00eaaac502c2060934b6eaaeca6a20bd3da9e0be8777f7d02033d1b15884232281a41fc7f80eed04af5e',
'86ff0182d40f7f9edb7862515821bd67bfd6165a3c44de95d7df79b8779ccf6460e3c68b70c16aaa280f2d7b3f22d745b97a89906cfc476c',
'502bcb6842eb06f0e49032bae87c554c031d6d4d2d7694efbf9c468d48220c50f8ca28843364d70cee92d6fe246e61448f9db9808b3b2408',
'0c9810f1e2ebd389caa789374d78007974ef4d17227316f40e578b336827da3f6b482a4794eb6a3975b971b5e1388f52e91ea2f1bcb0f912',
'20d41d85a18d5657a29640321563bbd04c2ffbd0a37a7ba43a4f7d263ce26faf4e1f74f9f4b590c69229ae571fe37fa639b5b8eb48bd9a55',
'e6b4b8f408c7010d0601e7eda0c309a1a42720d6d06b5759fdc4e1efe22d076d6c44d42f508d67be462914d28b8edce32e7094305164af17',
'be88bbb86c59c13d8e9d09ab98105f69c2d1dd134dbcd3b0863658f53159db64c0e139d180f3c89b8296d0ae324419c06fa87fc7daaf34c1',
'a456f9369769e8f08902124a0314c7a06537a06e32411f4f93415950a17badfa7442b6217434a3a05ef45be5f10bd7b2ef8ea00c431edec5',
'186e452c4466aa4383b4c00210d52e7922dbf9771e8b47e229a9b7b73c8d10fd7ef0b6e41530f91f24a3ed9ab71fa38b98b2fe4746d51d68',
'4ae7fdcae9453f195a8ead5cbe1a7b9699673b52c40ab27927464887be53237f7f3a21b938d40d0ec9e15b1d5130b13ffed81373a53e2b43',
'841981c3bfeec3f60cfeca75d9d8dc17f46cf0106f2422b59aec580a58f342272e3a5e575a055ddb051390c54c24c6ecb1e0aceb075f6056',
];
let B = DecafPoint.BASE;
let P = DecafPoint.ZERO;
for (const encoded of encodingsOfSmallMultiples) {
deepStrictEqual(P.toHex(), encoded);
deepStrictEqual(DecafPoint.fromHex(encoded).toHex(), encoded);
P = P.add(B);
}
});
should('not convert bad bytes encoding', () => {
const badEncodings = [
// These are all bad because they're non-canonical field encodings.
'8e24f838059ee9fef1e209126defe53dcd74ef9b6304601c6966099effffffffffffffffffffffffffffffffffffffffffffffffffffffff',
'86fcc7212bd4a0b980928666dc28c444a605ef38e09fb569e28d4443ffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
'866d54bd4c4ff41a55d4eefdbeca73cbd653c7bd3135b383708ec0bdffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
'4a380ccdab9c86364a89e77a464d64f9157538cfdfa686adc0d5ece4ffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
'f22d9d4c945dd44d11e0b1d3d3d358d959b4844d83b08c44e659d79fffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
'8cdffc681aa99e9c818c8ef4c3808b58e86acdef1ab68c8477af185bffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
'0e1c12ac7b5920effbd044e897c57634e2d05b5c27f8fa3df8a086a1ffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
// These are all bad because they're negative field elements.
'15141bd2121837ef71a0016bd11be757507221c26542244f23806f3fd3496b7d4c36826276f3bf5deea2c60c4fa4cec69946876da497e795',
'455d380238434ab740a56267f4f46b7d2eb2dd8ee905e51d7b0ae8a6cb2bae501e67df34ab21fa45946068c9f233939b1d9521a998b7cb93',
'810b1d8e8bf3a9c023294bbfd3d905a97531709bdc0f42390feedd7010f77e98686d400c9c86ed250ceecd9de0a18888ffecda0f4ea1c60d',
'd3af9cc41be0e5de83c0c6273bedcb9351970110044a9a41c7b9b2267cdb9d7bf4dc9c2fdb8bed32878184604f1d9944305a8df4274ce301',
'9312bcab09009e4330ff89c4bc1e9e000d863efc3c863d3b6c507a40fd2cdefde1bf0892b4b5ed9780b91ed1398fb4a7344c605aa5efda74',
'53d11bce9e62a29d63ed82ae93761bdd76e38c21e2822d6ebee5eb1c5b8a03eaf9df749e2490eda9d8ac27d1f71150de93668074d18d1c3a',
'697c1aed3cd8858515d4be8ac158b229fe184d79cb2b06e49210a6f3a7cd537bcd9bd390d96c4ab6a4406da5d93640726285370cfa95df80',
// These are all bad because they give a nonsquare x².
'58ad48715c9a102569b68b88362a4b0645781f5a19eb7e59c6a4686fd0f0750ff42e3d7af1ab38c29d69b670f31258919c9fdbf6093d06c0',
'8ca37ee2b15693f06e910cf43c4e32f1d5551dda8b1e48cb6ddd55e440dbc7b296b601919a4e4069f59239ca247ff693f7daa42f086122b1',
'982c0ec7f43d9f97c0a74b36db0abd9ca6bfb98123a90782787242c8a523cdc76df14a910d54471127e7662a1059201f902940cd39d57af5',
'baa9ab82d07ca282b968a911a6c3728d74bf2fe258901925787f03ee4be7e3cb6684fd1bcfe5071a9a974ad249a4aaa8ca81264216c68574',
'2ed9ffe2ded67a372b181ac524996402c42970629db03f5e8636cbaf6074b523d154a7a8c4472c4c353ab88cd6fec7da7780834cc5bd5242',
'f063769e4241e76d815800e4933a3a144327a30ec40758ad3723a788388399f7b3f5d45b6351eb8eddefda7d5bff4ee920d338a8b89d8b63',
'5a0104f1f55d152ceb68bc138182499891d90ee8f09b40038ccc1e07cb621fd462f781d045732a4f0bda73f0b2acf94355424ff0388d4b9c',
];
for (const badBytes of badEncodings) {
const b = hexToBytes(badBytes);
throws(() => DecafPoint.fromHex(b), badBytes);
}
});
should('create right points from uniform hash', () => {
const hashes = [
'cbb8c991fd2f0b7e1913462d6463e4fd2ce4ccdd28274dc2ca1f4165d5ee6cdccea57be3416e166fd06718a31af45a2f8e987e301be59ae6673e963001dbbda80df47014a21a26d6c7eb4ebe0312aa6fffb8d1b26bc62ca40ed51f8057a635a02c2b8c83f48fa6a2d70f58a1185902c0',
'b6d8da654b13c3101d6634a231569e6b85961c3f4b460a08ac4a5857069576b64428676584baa45b97701be6d0b0ba18ac28d443403b45699ea0fbd1164f5893d39ad8f29e48e399aec5902508ea95e33bc1e9e4620489d684eb5c26bc1ad1e09aba61fabc2cdfee0b6b6862ffc8e55a',
'36a69976c3e5d74e4904776993cbac27d10f25f5626dd45c51d15dcf7b3e6a5446a6649ec912a56895d6baa9dc395ce9e34b868d9fb2c1fc72eb6495702ea4f446c9b7a188a4e0826b1506b0747a6709f37988ff1aeb5e3788d5076ccbb01a4bc6623c92ff147a1e21b29cc3fdd0e0f4',
'd5938acbba432ecd5617c555a6a777734494f176259bff9dab844c81aadcf8f7abd1a9001d89c7008c1957272c1786a4293bb0ee7cb37cf3988e2513b14e1b75249a5343643d3c5e5545a0c1a2a4d3c685927c38bc5e5879d68745464e2589e000b31301f1dfb7471a4f1300d6fd0f99',
'4dec58199a35f531a5f0a9f71a53376d7b4bdd6bbd2904234a8ea65bbacbce2a542291378157a8f4be7b6a092672a34d85e473b26ccfbd4cdc6739783dc3f4f6ee3537b7aed81df898c7ea0ae89a15b5559596c2a5eeacf8b2b362f3db2940e3798b63203cae77c4683ebaed71533e51',
'df2aa1536abb4acab26efa538ce07fd7bca921b13e17bc5ebcba7d1b6b733deda1d04c220f6b5ab35c61b6bcb15808251cab909a01465b8ae3fc770850c66246d5a9eae9e2877e0826e2b8dc1bc08009590bc6778a84e919fbd28e02a0f9c49b48dc689eb5d5d922dc01469968ee81b5',
'e9fb440282e07145f1f7f5ecf3c273212cd3d26b836b41b02f108431488e5e84bd15f2418b3d92a3380dd66a374645c2a995976a015632d36a6c2189f202fc766e1c82f50ad9189be190a1f0e8f9b9e69c9c18cc98fdd885608f68bf0fdedd7b894081a63f70016a8abf04953affbefa',
];
const encodedHashToPoints = [
'0c709c9607dbb01c94513358745b7c23953d03b33e39c7234e268d1d6e24f34014ccbc2216b965dd231d5327e591dc3c0e8844ccfd568848',
'76ab794e28ff1224c727fa1016bf7f1d329260b7218a39aea2fdb17d8bd9119017b093d641cedf74328c327184dc6f2a64bd90eddccfcdab',
'c8d7ac384143500e50890a1c25d643343accce584caf2544f9249b2bf4a6921082be0e7f3669bb5ec24535e6c45621e1f6dec676edd8b664',
'62beffc6b8ee11ccd79dbaac8f0252c750eb052b192f41eeecb12f2979713b563caf7d22588eca5e80995241ef963e7ad7cb7962f343a973',
'f4ccb31d263731ab88bed634304956d2603174c66da38742053fa37dd902346c3862155d68db63be87439e3d68758ad7268e239d39c4fd3b',
'7e79b00e8e0a76a67c0040f62713b8b8c6d6f05e9c6d02592e8a22ea896f5deacc7c7df5ed42beae6fedb9000285b482aa504e279fd49c32',
'20b171cb16be977f15e013b9752cf86c54c631c4fc8cbf7c03c4d3ac9b8e8640e7b0e9300b987fe0ab5044669314f6ed1650ae037db853f1',
];
for (let i = 0; i < hashes.length; i++) {
const hash = hexToBytes(hashes[i]);
const point = DecafPoint.hashToCurve(hash);
deepStrictEqual(point.toHex(), encodedHashToPoints[i]);
}
});
should('have proper equality testing', () => {
const MAX_448B = BigInt(
'0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
);
const bytes448ToNumberLE = (bytes) => ed448.CURVE.Fp.create(bytesToNumberLE(bytes) & MAX_448B);
const priv = new Uint8Array([
23, 211, 149, 179, 209, 108, 78, 37, 229, 45, 122, 220, 85, 38, 192, 182, 96, 40, 168, 63,
175, 194, 73, 202, 14, 175, 78, 15, 117, 175, 40, 32, 218, 221, 151, 58, 158, 91, 250, 141,
18, 175, 191, 119, 152, 124, 223, 101, 54, 218, 76, 158, 43, 112, 151, 32,
]);
const pub = DecafPoint.BASE.multiply(bytes448ToNumberLE(priv));
deepStrictEqual(pub.equals(DecafPoint.ZERO), false);
});
});
// ESM is broken.
import url from 'url';
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
should.run();
}

@ -2,8 +2,10 @@ import { deepStrictEqual, throws } from 'assert';
import { describe, should } from 'micro-should';
import * as fc from 'fast-check';
import { ed448, ed448ph, x448 } from '../esm/ed448.js';
import { hexToBytes, bytesToHex, randomBytes } from '@noble/hashes/utils';
import { bytesToHex, concatBytes, hexToBytes, randomBytes } from '@noble/hashes/utils';
import { numberToBytesLE } from '../esm/abstract/utils.js';
// Old vectors allow to test sign() because they include private key
import { default as ed448vectorsOld } from './ed448/ed448_test_OLD.json' assert { type: 'json' };
import { default as ed448vectors } from './wycheproof/ed448_test.json' assert { type: 'json' };
import { default as x448vectors } from './wycheproof/x448_test.json' assert { type: 'json' };
@ -439,9 +441,9 @@ describe('ed448', () => {
}
});
describe('wycheproof', () => {
for (let g = 0; g < ed448vectors.testGroups.length; g++) {
const group = ed448vectors.testGroups[g];
describe('wycheproof (OLD)', () => {
for (let g = 0; g < ed448vectorsOld.testGroups.length; g++) {
const group = ed448vectorsOld.testGroups[g];
const key = group.key;
should(`ED448(${g}, public)`, () => {
deepStrictEqual(hex(ed.getPublicKey(key.sk)), key.pk);
@ -467,74 +469,234 @@ describe('ed448', () => {
}
});
// ECDH
const rfc7748Mul = [
{
scalar:
'3d262fddf9ec8e88495266fea19a34d28882acef045104d0d1aae121700a779c984c24f8cdd78fbff44943eba368f54b29259a4f1c600ad3',
u: '06fce640fa3487bfda5f6cf2d5263f8aad88334cbd07437f020f08f9814dc031ddbdc38c19c6da2583fa5429db94ada18aa7a7fb4ef8a086',
outputU:
'ce3e4ff95a60dc6697da1db1d85e6afbdf79b50a2412d7546d5f239fe14fbaadeb445fc66a01b0779d98223961111e21766282f73dd96b6f',
},
{
scalar:
'203d494428b8399352665ddca42f9de8fef600908e0d461cb021f8c538345dd77c3e4806e25f46d3315c44e0a5b4371282dd2c8d5be3095f',
u: '0fbcc2f993cd56d3305b0b7d9e55d4c1a8fb5dbb52f8e9a1e9b6201b165d015894e56c4d3570bee52fe205e28a78b91cdfbde71ce8d157db',
outputU:
'884a02576239ff7a2f2f63b2db6a9ff37047ac13568e1e30fe63c4a7ad1b3ee3a5700df34321d62077e63633c575c1c954514e99da7c179d',
},
];
describe('RFC7748', () => {
describe('wycheproof', () => {
for (let g = 0; g < ed448vectors.testGroups.length; g++) {
const group = ed448vectors.testGroups[g];
const key = group.publicKey;
should(`ED448`, () => {
for (let i = 0; i < group.tests.length; i++) {
const v = group.tests[i];
const index = `${g}/${i} ${v.comment}`;
if (v.result === 'valid' || v.result === 'acceptable') {
deepStrictEqual(ed.verify(v.sig, v.msg, key.pk), true, index);
} else if (v.result === 'invalid') {
let failed = false;
try {
failed = !ed.verify(v.sig, v.msg, key.pk);
} catch (error) {
failed = true;
}
deepStrictEqual(failed, true, index);
} else throw new Error('unknown test result');
}
});
}
});
// should('X448: should convert base point to montgomery using fromPoint', () => {
// deepStrictEqual(
// hex(ed.montgomeryCurve.UfromPoint(Point.BASE)),
// ed.montgomeryCurve.BASE_POINT_U
// );
// });
// should('X448/getSharedSecret() should be commutative', async () => {
// for (let i = 0; i < 512; i++) {
// const asec = ed.utils.randomPrivateKey();
// const apub = ed.getPublicKey(asec);
// const bsec = ed.utils.randomPrivateKey();
// const bpub = ed.getPublicKey(bsec);
// try {
// deepStrictEqual(ed.getSharedSecret(asec, bpub), ed.getSharedSecret(bsec, apub));
// } catch (error) {
// console.error('not commutative', { asec, apub, bsec, bpub });
// throw error;
// }
// }
// });
describe('ed448ctx', () => {
const VECTORS_RFC8032_CTX = [
{
secretKey:
'c4eab05d357007c632f3dbb48489924d552b08fe0c353a0d4a1f00acda2c463afbea67c5e8d2877c5e3bc397a659949ef8021e954e0a12274e',
publicKey:
'43ba28f430cdff456ae531545f7ecd0ac834a55d9358c0372bfa0c6c6798c0866aea01eb00742802b8438ea4cb82169c235160627b4c3a9480',
message: '03',
context: '666f6f',
signature:
'd4f8f6131770dd46f40867d6fd5d5055' +
'de43541f8c5e35abbcd001b32a89f7d2' +
'151f7647f11d8ca2ae279fb842d60721' +
'7fce6e042f6815ea000c85741de5c8da' +
'1144a6a1aba7f96de42505d7a7298524' +
'fda538fccbbb754f578c1cad10d54d0d' +
'5428407e85dcbc98a49155c13764e66c' +
'3c00',
},
];
for (let i = 0; i < VECTORS_RFC8032_CTX.length; i++) {
const v = VECTORS_RFC8032_CTX[i];
should(`${i}`, () => {
deepStrictEqual(hex(ed.getPublicKey(v.secretKey)), v.publicKey);
deepStrictEqual(hex(ed.sign(v.message, v.secretKey, { context: v.context })), v.signature);
deepStrictEqual(
ed.verify(v.signature, v.message, v.publicKey, { context: v.context }),
true
);
});
}
});
describe('ed448ph', () => {
const VECTORS_RFC8032_PH = [
{
secretKey:
'833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42ef7822e0d5104127dc05d6dbefde69e3ab2cec7c867c6e2c49',
publicKey:
'259b71c19f83ef77a7abd26524cbdb3161b590a48f7d17de3ee0ba9c52beb743c09428a131d6b1b57303d90d8132c276d5ed3d5d01c0f53880',
message: '616263',
signature:
'822f6901f7480f3d5f562c592994d969' +
'3602875614483256505600bbc281ae38' +
'1f54d6bce2ea911574932f52a4e6cadd' +
'78769375ec3ffd1b801a0d9b3f4030cd' +
'433964b6457ea39476511214f97469b5' +
'7dd32dbc560a9a94d00bff07620464a3' +
'ad203df7dc7ce360c3cd3696d9d9fab9' +
'0f00',
},
{
secretKey:
'833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42ef7822e0d5104127dc05d6dbefde69e3ab2cec7c867c6e2c49',
publicKey:
'259b71c19f83ef77a7abd26524cbdb3161b590a48f7d17de3ee0ba9c52beb743c09428a131d6b1b57303d90d8132c276d5ed3d5d01c0f53880',
message: '616263',
context: '666f6f',
signature:
'c32299d46ec8ff02b54540982814dce9' +
'a05812f81962b649d528095916a2aa48' +
'1065b1580423ef927ecf0af5888f90da' +
'0f6a9a85ad5dc3f280d91224ba9911a3' +
'653d00e484e2ce232521481c8658df30' +
'4bb7745a73514cdb9bf3e15784ab7128' +
'4f8d0704a608c54a6b62d97beb511d13' +
'2100',
},
];
for (let i = 0; i < VECTORS_RFC8032_PH.length; i++) {
const v = VECTORS_RFC8032_PH[i];
should(`${i}`, () => {
deepStrictEqual(hex(ed448ph.getPublicKey(v.secretKey)), v.publicKey);
deepStrictEqual(
hex(ed448ph.sign(v.message, v.secretKey, { context: v.context })),
v.signature
);
deepStrictEqual(
ed448ph.verify(v.signature, v.message, v.publicKey, { context: v.context }),
true
);
});
}
});
should('not verify when sig.s >= CURVE.n', () => {
function get56bSig() {
const privateKey = ed448.utils.randomPrivateKey();
const message = Uint8Array.from([0xab, 0xbc, 0xcd, 0xde]);
const publicKey = ed448.getPublicKey(privateKey);
const signature = ed448.sign(message, privateKey);
const R = signature.slice(0, 56);
let s = signature.slice(56, 112);
s = bytesToHex(s.slice().reverse());
s = BigInt('0x' + s);
s = s + ed448.CURVE.n;
s = numberToBytesLE(s, 56);
const sig_invalid = concatBytes(R, s);
return { sig_invalid, message, publicKey };
}
let sig;
while (true) {
try {
sig = get56bSig();
break;
} catch (error) {
// non-56b sig was generated, try again
}
}
throws(() => {
ed448.verify(sig.sig_invalid, sig.message, sig.publicKey);
});
});
describe('RFC7748 X448 ECDH', () => {
// ECDH
const rfc7748Mul = [
{
scalar:
'3d262fddf9ec8e88495266fea19a34d28882acef045104d0d1aae121700a779c984c24f8cdd78fbff44943eba368f54b29259a4f1c600ad3',
u: '06fce640fa3487bfda5f6cf2d5263f8aad88334cbd07437f020f08f9814dc031ddbdc38c19c6da2583fa5429db94ada18aa7a7fb4ef8a086',
outputU:
'ce3e4ff95a60dc6697da1db1d85e6afbdf79b50a2412d7546d5f239fe14fbaadeb445fc66a01b0779d98223961111e21766282f73dd96b6f',
},
{
scalar:
'203d494428b8399352665ddca42f9de8fef600908e0d461cb021f8c538345dd77c3e4806e25f46d3315c44e0a5b4371282dd2c8d5be3095f',
u: '0fbcc2f993cd56d3305b0b7d9e55d4c1a8fb5dbb52f8e9a1e9b6201b165d015894e56c4d3570bee52fe205e28a78b91cdfbde71ce8d157db',
outputU:
'884a02576239ff7a2f2f63b2db6a9ff37047ac13568e1e30fe63c4a7ad1b3ee3a5700df34321d62077e63633c575c1c954514e99da7c179d',
},
];
for (let i = 0; i < rfc7748Mul.length; i++) {
const v = rfc7748Mul[i];
should(`scalarMult (${i})`, () => {
deepStrictEqual(hex(x448.scalarMult(v.scalar, v.u)), v.outputU);
});
}
});
const rfc7748Iter = [
{
scalar:
'3f482c8a9f19b01e6c46ee9711d9dc14fd4bf67af30765c2ae2b846a4d23a8cd0db897086239492caf350b51f833868b9bc2b3bca9cf4113',
iters: 1,
},
{
scalar:
'aa3b4749d55b9daf1e5b00288826c467274ce3ebbdd5c17b975e09d4af6c67cf10d087202db88286e2b79fceea3ec353ef54faa26e219f38',
iters: 1000,
},
// { scalar: '077f453681caca3693198420bbe515cae0002472519b3e67661a7e89cab94695c8f4bcd66e61b9b9c946da8d524de3d69bd9d9d66b997e37', iters: 1000000 },
];
for (let i = 0; i < rfc7748Iter.length; i++) {
const { scalar, iters } = rfc7748Iter[i];
should(`RFC7748: scalarMult iteration (${i})`, () => {
let k = x448.GuBytes;
for (let i = 0, u = k; i < iters; i++) [k, u] = [x448.scalarMult(k, u), k];
deepStrictEqual(hex(k), scalar);
const rfc7748Iter = [
{
scalar:
'3f482c8a9f19b01e6c46ee9711d9dc14fd4bf67af30765c2ae2b846a4d23a8cd0db897086239492caf350b51f833868b9bc2b3bca9cf4113',
iters: 1,
},
{
scalar:
'aa3b4749d55b9daf1e5b00288826c467274ce3ebbdd5c17b975e09d4af6c67cf10d087202db88286e2b79fceea3ec353ef54faa26e219f38',
iters: 1000,
},
// { scalar: '077f453681caca3693198420bbe515cae0002472519b3e67661a7e89cab94695c8f4bcd66e61b9b9c946da8d524de3d69bd9d9d66b997e37', iters: 1000000 },
];
for (let i = 0; i < rfc7748Iter.length; i++) {
const { scalar, iters } = rfc7748Iter[i];
should(`scalarMult iterated ${iters}x`, () => {
let k = x448.GuBytes;
for (let i = 0, u = k; i < iters; i++) [k, u] = [x448.scalarMult(k, u), k];
deepStrictEqual(hex(k), scalar);
});
}
should('getSharedKey', () => {
const alicePrivate =
'9a8f4925d1519f5775cf46b04b5800d4ee9ee8bae8bc5565d498c28dd9c9baf574a9419744897391006382a6f127ab1d9ac2d8c0a598726b';
const alicePublic =
'9b08f7cc31b7e3e67d22d5aea121074a273bd2b83de09c63faa73d2c22c5d9bbc836647241d953d40c5b12da88120d53177f80e532c41fa0';
const bobPrivate =
'1c306a7ac2a0e2e0990b294470cba339e6453772b075811d8fad0d1d6927c120bb5ee8972b0d3e21374c9c921b09d1b0366f10b65173992d';
const bobPublic =
'3eb7a829b0cd20f5bcfc0b599b6feccf6da4627107bdb0d4f345b43027d8b972fc3e34fb4232a13ca706dcb57aec3dae07bdc1c67bf33609';
const shared =
'07fff4181ac6cc95ec1c16a94a0f74d12da232ce40a77552281d282bb60c0b56fd2464c335543936521c24403085d59a449a5037514a879d';
deepStrictEqual(alicePublic, hex(x448.getPublicKey(alicePrivate)));
deepStrictEqual(bobPublic, hex(x448.getPublicKey(bobPrivate)));
deepStrictEqual(hex(x448.scalarMult(alicePrivate, bobPublic)), shared);
deepStrictEqual(hex(x448.scalarMult(bobPrivate, alicePublic)), shared);
});
}
should('RFC7748 getSharedKey', () => {
const alicePrivate =
'9a8f4925d1519f5775cf46b04b5800d4ee9ee8bae8bc5565d498c28dd9c9baf574a9419744897391006382a6f127ab1d9ac2d8c0a598726b';
const alicePublic =
'9b08f7cc31b7e3e67d22d5aea121074a273bd2b83de09c63faa73d2c22c5d9bbc836647241d953d40c5b12da88120d53177f80e532c41fa0';
const bobPrivate =
'1c306a7ac2a0e2e0990b294470cba339e6453772b075811d8fad0d1d6927c120bb5ee8972b0d3e21374c9c921b09d1b0366f10b65173992d';
const bobPublic =
'3eb7a829b0cd20f5bcfc0b599b6feccf6da4627107bdb0d4f345b43027d8b972fc3e34fb4232a13ca706dcb57aec3dae07bdc1c67bf33609';
const shared =
'07fff4181ac6cc95ec1c16a94a0f74d12da232ce40a77552281d282bb60c0b56fd2464c335543936521c24403085d59a449a5037514a879d';
deepStrictEqual(alicePublic, hex(x448.getPublicKey(alicePrivate)));
deepStrictEqual(bobPublic, hex(x448.getPublicKey(bobPrivate)));
deepStrictEqual(hex(x448.scalarMult(alicePrivate, bobPublic)), shared);
deepStrictEqual(hex(x448.scalarMult(bobPrivate, alicePublic)), shared);
});
describe('wycheproof', () => {
const group = x448vectors.testGroups[0];
should(`X448`, () => {
should('wycheproof', () => {
const group = x448vectors.testGroups[0];
for (let i = 0; i < group.tests.length; i++) {
const v = group.tests[i];
const index = `(${i}, ${v.result}) ${v.comment}`;
@ -560,111 +722,15 @@ describe('ed448', () => {
} else throw new Error('unknown test result');
}
});
});
// should('X448: should convert base point to montgomery using fromPoint', () => {
// deepStrictEqual(
// hex(ed.montgomeryCurve.UfromPoint(Point.BASE)),
// ed.montgomeryCurve.BASE_POINT_U
// );
// });
// should('X448/getSharedSecret() should be commutative', async () => {
// for (let i = 0; i < 512; i++) {
// const asec = ed.utils.randomPrivateKey();
// const apub = ed.getPublicKey(asec);
// const bsec = ed.utils.randomPrivateKey();
// const bpub = ed.getPublicKey(bsec);
// try {
// deepStrictEqual(ed.getSharedSecret(asec, bpub), ed.getSharedSecret(bsec, apub));
// } catch (error) {
// console.error('not commutative', { asec, apub, bsec, bpub });
// throw error;
// }
// }
// });
const VECTORS_RFC8032_CTX = [
{
secretKey:
'c4eab05d357007c632f3dbb48489924d552b08fe0c353a0d4a1f00acda2c463afbea67c5e8d2877c5e3bc397a659949ef8021e954e0a12274e',
publicKey:
'43ba28f430cdff456ae531545f7ecd0ac834a55d9358c0372bfa0c6c6798c0866aea01eb00742802b8438ea4cb82169c235160627b4c3a9480',
message: '03',
context: '666f6f',
signature:
'd4f8f6131770dd46f40867d6fd5d5055' +
'de43541f8c5e35abbcd001b32a89f7d2' +
'151f7647f11d8ca2ae279fb842d60721' +
'7fce6e042f6815ea000c85741de5c8da' +
'1144a6a1aba7f96de42505d7a7298524' +
'fda538fccbbb754f578c1cad10d54d0d' +
'5428407e85dcbc98a49155c13764e66c' +
'3c00',
},
];
for (let i = 0; i < VECTORS_RFC8032_CTX.length; i++) {
const v = VECTORS_RFC8032_CTX[i];
should(`RFC8032ctx/${i}`, () => {
deepStrictEqual(hex(ed.getPublicKey(v.secretKey)), v.publicKey);
deepStrictEqual(hex(ed.sign(v.message, v.secretKey, v.context)), v.signature);
deepStrictEqual(ed.verify(v.signature, v.message, v.publicKey, v.context), true);
should('have proper base point', () => {
const { x, y } = Point.BASE;
const { Fp } = ed448.CURVE;
// const invX = Fp.invert(x * x); // x²
const u = Fp.div(Fp.create(y * y), Fp.create(x * x)); // (y²/x²)
// const u = Fp.create(y * y * invX);
deepStrictEqual(numberToBytesLE(u, 56), x448.GuBytes);
});
}
const VECTORS_RFC8032_PH = [
{
secretKey:
'833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42ef7822e0d5104127dc05d6dbefde69e3ab2cec7c867c6e2c49',
publicKey:
'259b71c19f83ef77a7abd26524cbdb3161b590a48f7d17de3ee0ba9c52beb743c09428a131d6b1b57303d90d8132c276d5ed3d5d01c0f53880',
message: '616263',
signature:
'822f6901f7480f3d5f562c592994d969' +
'3602875614483256505600bbc281ae38' +
'1f54d6bce2ea911574932f52a4e6cadd' +
'78769375ec3ffd1b801a0d9b3f4030cd' +
'433964b6457ea39476511214f97469b5' +
'7dd32dbc560a9a94d00bff07620464a3' +
'ad203df7dc7ce360c3cd3696d9d9fab9' +
'0f00',
},
{
secretKey:
'833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42ef7822e0d5104127dc05d6dbefde69e3ab2cec7c867c6e2c49',
publicKey:
'259b71c19f83ef77a7abd26524cbdb3161b590a48f7d17de3ee0ba9c52beb743c09428a131d6b1b57303d90d8132c276d5ed3d5d01c0f53880',
message: '616263',
context: '666f6f',
signature:
'c32299d46ec8ff02b54540982814dce9' +
'a05812f81962b649d528095916a2aa48' +
'1065b1580423ef927ecf0af5888f90da' +
'0f6a9a85ad5dc3f280d91224ba9911a3' +
'653d00e484e2ce232521481c8658df30' +
'4bb7745a73514cdb9bf3e15784ab7128' +
'4f8d0704a608c54a6b62d97beb511d13' +
'2100',
},
];
for (let i = 0; i < VECTORS_RFC8032_PH.length; i++) {
const v = VECTORS_RFC8032_PH[i];
should(`RFC8032ph/${i}`, () => {
deepStrictEqual(hex(ed448ph.getPublicKey(v.secretKey)), v.publicKey);
deepStrictEqual(hex(ed448ph.sign(v.message, v.secretKey, v.context)), v.signature);
deepStrictEqual(ed448ph.verify(v.signature, v.message, v.publicKey, v.context), true);
});
}
should('X448 base point', () => {
const { x, y } = Point.BASE;
const { Fp } = ed448.CURVE;
// const invX = Fp.invert(x * x); // x²
const u = Fp.div(Fp.create(y * y), Fp.create(x * x)); // (y²/x²)
// const u = Fp.create(y * y * invX);
deepStrictEqual(numberToBytesLE(u, 56), x448.GuBytes);
});
});

@ -0,0 +1,908 @@
{
"algorithm" : "EDDSA",
"generatorVersion" : "0.8r12",
"numberOfTests" : 86,
"header" : [
"Test vectors of type EddsaVerify are intended for testing",
"the verification of Eddsa signatures."
],
"notes" : {
"SignatureMalleability" : "EdDSA signatures are non-malleable, if implemented accordingly. Failing to check the range of S allows to modify signatures. See RFC 8032, Section 5.2.7 and Section 8.4."
},
"schema" : "eddsa_verify_schema.json",
"testGroups" : [
{
"jwk" : {
"crv" : "Ed448",
"d" : "iDAeB2UY01N_kwLuD1Ij5LY-HwFgB9PC69_sX3CZfoEZxrrQrnuAP0h5HKjsVJqiobhi96UVkLnV",
"kid" : "none",
"kty" : "OKP",
"x" : "QZYQpTSvEn9YOwSBjNt_D_MAsCXy4BaCvK4z_Wkc7gOVEd8M3caQ7peEJuizjlDOWvfc-6UPcEwA"
},
"key" : {
"curve" : "edwards448",
"keySize" : 448,
"pk" : "419610a534af127f583b04818cdb7f0ff300b025f2e01682bcae33fd691cee039511df0cddc690ee978426e8b38e50ce5af7dcfba50f704c00",
"sk" : "88301e076518d3537f9302ee0f5223e4b63e1f016007d3c2ebdfec5f70997e8119c6bad0ae7b803f48791ca8ec549aa2a1b862f7a51590b9d5",
"type" : "EDDSAKeyPair"
},
"keyDer" : "3043300506032b6571033a00419610a534af127f583b04818cdb7f0ff300b025f2e01682bcae33fd691cee039511df0cddc690ee978426e8b38e50ce5af7dcfba50f704c00",
"keyPem" : "-----BEGIN PUBLIC KEY-----\nMEMwBQYDK2VxAzoAQZYQpTSvEn9YOwSBjNt/D/MAsCXy4BaCvK4z/Wkc7gOVEd8M3caQ7peEJuizjlDOWvfc+6UPcEwA\n-----END PUBLIC KEY-----\n",
"type" : "EddsaVerify",
"tests" : [
{
"tcId" : 1,
"comment" : "",
"msg" : "",
"sig" : "cf7953007666e12f73af9ec92e3e018da5ee5a8d5b17f5100a354c58f1d5f4bb37ab835c52f72374c72d612689149cf6d36a70db6dc5a6c400b597348e0e31e51e65bb144e63c892a367b4c055c036aa6cd7e728cdd2a098963bda863903e6dd025b5a5d891209f4e28537694804e50b0800",
"result" : "valid",
"flags" : []
},
{
"tcId" : 2,
"comment" : "",
"msg" : "78",
"sig" : "c56e94d5c9ca860c244f33db556bf6b3cec38b024b77604a35d6a07211b1316b9a027133c374b86f72665cc45ce01583a2e0f2775c6172da801acef168717cab1196cddfb149359dfef589756257cc2d6b02fc516d8d41b4adaa3f11428f41410ef0dc3c1b008d3d052173d4389508ed0100",
"result" : "valid",
"flags" : []
},
{
"tcId" : 3,
"comment" : "",
"msg" : "54657374",
"sig" : "5d053ff5b71f6ec3284525d35d77933178c8e19879886d08eccc6c7d27e9e5b5e02537dbc4d4723506e8d171fc1733857573dd02d18f48f28031d67d699a188a9ca46b4eabe2107aef237ca609cb462e24c91d25d286402b6ef7862b78a386950246ff38d6d2f458136d12e3c97fdd982600",
"result" : "valid",
"flags" : []
},
{
"tcId" : 4,
"comment" : "",
"msg" : "48656c6c6f",
"sig" : "442e33780f199dd7bc71d1335f74df7f3a0ec789e21a175c1bffddb6e50091998d969ac8194b3acefb7702f6c222f84f7eeca3b80406f1fe80687915e7925bf52deb47b6b779e26d30eec7c5fef03580f280a089eefd0bacc9fbbb6a4d73a591d1671d192e6bbcfdb79ad3db5673a1263000",
"result" : "valid",
"flags" : []
},
{
"tcId" : 5,
"comment" : "",
"msg" : "313233343030",
"sig" : "5db94c53101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff28060a05236fc9c1682b0e55b60a082c9a57bffe61ef4dda5ce65df539805122b3a09a05976d41ad68ab52df85428152c57da93531e5d16920e00",
"result" : "valid",
"flags" : []
},
{
"tcId" : 6,
"comment" : "",
"msg" : "000000000000000000000000",
"sig" : "a8ca64d1ab00eae77fd2854d8422db3ae12fca91c14f274f30a44df98590786ec4cbb96a9564fc1b9b16c22d2bd00aa65f0876323729f5ac809fb0b89a4d3f27afbabb596851d835173d60ea34e0875359f3d6adb13cef1395b7eaa5f9147583ff38b4deb183062874915bf194ae61072300",
"result" : "valid",
"flags" : []
},
{
"tcId" : 7,
"comment" : "",
"msg" : "6161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161",
"sig" : "b205d3e24ccef64c1e86f15f48ddfa682453503489475188b04a8f55860b3c8a9c01e6de820bb7d9b15daff8de25a4a870e987157a115ec1802da0d0606da12842ea7eab658b5eea6dd1f3a641a5174425578003cd318b8d6b8dcb4de954b5078d1912c578ad8281515d6df3672b94173f00",
"result" : "valid",
"flags" : []
},
{
"tcId" : 8,
"comment" : "",
"msg" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f60",
"sig" : "3492ef66e5fdf1503e9e206c5c2f0d4b7891aad793575527d2251e0df1b97c2feac188bc382ce3c92c4bc36ba2695f32bedadd480eaa932300d0db1f9a9c60844d2ea5aea64933c7be46c4f9d21cb48b39eae23d08496de7ce9501197185cc5d4ff8aa4b018ce7ad321f6a7d778c4a070400",
"result" : "valid",
"flags" : []
},
{
"tcId" : 9,
"comment" : "",
"msg" : "ffffffffffffffffffffffffffffffff",
"sig" : "545e1905af1b5886552eaf78e17304c6f83fcfb3444df2d1ea056486db615e3bb29131bb0c1fd295364dc515dae581967148eb23c6c9012e806d3623baff00548c648e3cb3756aaaaf659f2fb7dd2e71c7611448593ca63f2a98913ab7f182e6820eaf1334e2745e0e7bc0dccab98de71600",
"result" : "valid",
"flags" : []
},
{
"tcId" : 10,
"comment" : "special values for r and s",
"msg" : "3f",
"sig" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 11,
"comment" : "special values for r and s",
"msg" : "3f",
"sig" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 12,
"comment" : "special values for r and s",
"msg" : "3f",
"sig" : "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f24458ab92c27823558fc58d72c26c219036d6ae49db4ec4e923ca7cffffffffffffffffffffffffffffffffffffffffffffffffffffff3f",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 13,
"comment" : "special values for r and s",
"msg" : "3f",
"sig" : "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f34458ab92c27823558fc58d72c26c219036d6ae49db4ec4e923ca7cffffffffffffffffffffffffffffffffffffffffffffffffffffff3f",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 14,
"comment" : "special values for r and s",
"msg" : "3f",
"sig" : "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 15,
"comment" : "special values for r and s",
"msg" : "3f",
"sig" : "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 16,
"comment" : "special values for r and s",
"msg" : "3f",
"sig" : "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 17,
"comment" : "special values for r and s",
"msg" : "3f",
"sig" : "0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f24458ab92c27823558fc58d72c26c219036d6ae49db4ec4e923ca7cffffffffffffffffffffffffffffffffffffffffffffffffffffff3f",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 18,
"comment" : "special values for r and s",
"msg" : "3f",
"sig" : "0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f34458ab92c27823558fc58d72c26c219036d6ae49db4ec4e923ca7cffffffffffffffffffffffffffffffffffffffffffffffffffffff3f",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 19,
"comment" : "special values for r and s",
"msg" : "3f",
"sig" : "0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 20,
"comment" : "special values for r and s",
"msg" : "3f",
"sig" : "f34458ab92c27823558fc58d72c26c219036d6ae49db4ec4e923ca7cffffffffffffffffffffffffffffffffffffffffffffffffffffff3f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 21,
"comment" : "special values for r and s",
"msg" : "3f",
"sig" : "f34458ab92c27823558fc58d72c26c219036d6ae49db4ec4e923ca7cffffffffffffffffffffffffffffffffffffffffffffffffffffff3f0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 22,
"comment" : "special values for r and s",
"msg" : "3f",
"sig" : "f34458ab92c27823558fc58d72c26c219036d6ae49db4ec4e923ca7cffffffffffffffffffffffffffffffffffffffffffffffffffffff3ff24458ab92c27823558fc58d72c26c219036d6ae49db4ec4e923ca7cffffffffffffffffffffffffffffffffffffffffffffffffffffff3f",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 23,
"comment" : "special values for r and s",
"msg" : "3f",
"sig" : "f34458ab92c27823558fc58d72c26c219036d6ae49db4ec4e923ca7cffffffffffffffffffffffffffffffffffffffffffffffffffffff3ff34458ab92c27823558fc58d72c26c219036d6ae49db4ec4e923ca7cffffffffffffffffffffffffffffffffffffffffffffffffffffff3f",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 24,
"comment" : "special values for r and s",
"msg" : "3f",
"sig" : "f34458ab92c27823558fc58d72c26c219036d6ae49db4ec4e923ca7cffffffffffffffffffffffffffffffffffffffffffffffffffffff3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 25,
"comment" : "special values for r and s",
"msg" : "3f",
"sig" : "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 26,
"comment" : "special values for r and s",
"msg" : "3f",
"sig" : "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffff0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 27,
"comment" : "special values for r and s",
"msg" : "3f",
"sig" : "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffffffffffffffffffffffffffffffffffffffffffffffffffff24458ab92c27823558fc58d72c26c219036d6ae49db4ec4e923ca7cffffffffffffffffffffffffffffffffffffffffffffffffffffff3f",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 28,
"comment" : "special values for r and s",
"msg" : "3f",
"sig" : "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffffffffffffffffffffffffffffffffffffffffffffffffffff34458ab92c27823558fc58d72c26c219036d6ae49db4ec4e923ca7cffffffffffffffffffffffffffffffffffffffffffffffffffffff3f",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 29,
"comment" : "special values for r and s",
"msg" : "3f",
"sig" : "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 30,
"comment" : "empty signature",
"msg" : "54657374",
"sig" : "",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 31,
"comment" : "s missing",
"msg" : "54657374",
"sig" : "5d053ff5b71f6ec3284525d35d77933178c8e19879886d08eccc6c7d27e9e5b5e02537dbc4d4723506e8d171fc1733857573dd02d18f48f280",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 32,
"comment" : "signature too short",
"msg" : "54657374",
"sig" : "5d053ff5b71f6ec3284525d35d77933178c8e19879886d08eccc6c7d27e9e5b5e02537dbc4d4723506e8d171fc1733857573dd02d18f48f28031d67d699a188a9ca46b4eabe2107aef237ca609cb462e24c91d25d286402b6ef7862b78a386950246ff38d6d2f458136d12e3c97fdd98",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 33,
"comment" : "signature too long",
"msg" : "54657374",
"sig" : "5d053ff5b71f6ec3284525d35d77933178c8e19879886d08eccc6c7d27e9e5b5e02537dbc4d4723506e8d171fc1733857573dd02d18f48f28031d67d699a188a9ca46b4eabe2107aef237ca609cb462e24c91d25d286402b6ef7862b78a386950246ff38d6d2f458136d12e3c97fdd9826002020",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 34,
"comment" : "include pk in signature",
"msg" : "54657374",
"sig" : "5d053ff5b71f6ec3284525d35d77933178c8e19879886d08eccc6c7d27e9e5b5e02537dbc4d4723506e8d171fc1733857573dd02d18f48f28031d67d699a188a9ca46b4eabe2107aef237ca609cb462e24c91d25d286402b6ef7862b78a386950246ff38d6d2f458136d12e3c97fdd982600419610a534af127f583b04818cdb7f0ff300b025f2e01682bcae33fd691cee039511df0cddc690ee978426e8b38e50ce5af7dcfba50f704c00",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 35,
"comment" : "prepending 0 byte to signature",
"msg" : "54657374",
"sig" : "005d053ff5b71f6ec3284525d35d77933178c8e19879886d08eccc6c7d27e9e5b5e02537dbc4d4723506e8d171fc1733857573dd02d18f48f28031d67d699a188a9ca46b4eabe2107aef237ca609cb462e24c91d25d286402b6ef7862b78a386950246ff38d6d2f458136d12e3c97fdd982600",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 36,
"comment" : "prepending 0 byte to s",
"msg" : "54657374",
"sig" : "5d053ff5b71f6ec3284525d35d77933178c8e19879886d08eccc6c7d27e9e5b5e02537dbc4d4723506e8d171fc1733857573dd02d18f48f2800031d67d699a188a9ca46b4eabe2107aef237ca609cb462e24c91d25d286402b6ef7862b78a386950246ff38d6d2f458136d12e3c97fdd982600",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 37,
"comment" : "appending 0 byte to signature",
"msg" : "54657374",
"sig" : "5d053ff5b71f6ec3284525d35d77933178c8e19879886d08eccc6c7d27e9e5b5e02537dbc4d4723506e8d171fc1733857573dd02d18f48f28031d67d699a188a9ca46b4eabe2107aef237ca609cb462e24c91d25d286402b6ef7862b78a386950246ff38d6d2f458136d12e3c97fdd98260000",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 38,
"comment" : "removing 0 byte from signature",
"msg" : "5465737430",
"sig" : "dbd6384516ab6b0eb2d609414564ec217383b66040dfb0676128251ae24c1d7c179c21a9ee307dc13f8fe6550bc40187f093da85617bcf5d009d3ee8b798ad978b6e683bc4e911940ea82ea0b7e95dc24fe0b29e44663211892c2aaa3451379d22c289b94378f11fb700f1689d4a00d73e",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 39,
"comment" : "removing 0 byte from signature",
"msg" : "546573743535",
"sig" : "ce2b2fff0bf445a36813cf2a76e0cc5619a4f16ee53f0fe3cd46fc0414db7248b32fbda54bbb37e708d6238076ea12bf850b964b044520bb80fbaf0e1d1ed3bcab261462df5e7f2de73ac9cbae26dfa29015039acf90575961fc9b91b9ca276dae7d5fa805bd202c5579a0f4c66e801400",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 40,
"comment" : "dropping byte from signature",
"msg" : "546573743633",
"sig" : "c283ed36d78c275a5d02f7939aed2c4ef68320ae1bf6fc25e834b758046a6d52a480216a942dfe771f3bd307f4ce7d3f446e0824961bd5de80cda42b5cc38e6ec3d53f386978b9877d3c98a28ac8fc66630ffd178933a18de1aee23cab5011c9ff4c9277311b4c6c33acb8e82b8c693c00",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 41,
"comment" : "removing leading 0 byte from signature",
"msg" : "54657374333631",
"sig" : "62e629bd2b8f595df401c362c766216d45de89fceecd99c69d323b5c53ad5ac3ea7224963feba2f2895551d94f548248ef8597d2a959f880d59934a5e8f07847834d66ba1a6b09de5dba692172b13f768f0c29e8196144c130d2353445d63cbd0b690794fdad30a48e8bb7cc2504f80700",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 42,
"comment" : "modified bit 0 in R",
"msg" : "313233343030",
"sig" : "5cb94c53101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff280afc33a525116cc12e0d1c3a1fde6de518a6544f360d0fe18d5be7770b057a2bf792db4b7648fa84a6eaecae909e33fa59c5dfe4804ba2623",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 43,
"comment" : "modified bit 1 in R",
"msg" : "313233343030",
"sig" : "5fb94c53101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff280f91386c3e9dd9e7c9af7ca6bbef8b7a44ae3d68eeade449d7dfbb31de8419eb943e2ecbcdd06df5227e82b9ded519a56e70f0a1c0fc17b06",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 44,
"comment" : "modified bit 2 in R",
"msg" : "313233343030",
"sig" : "59b94c53101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff280f1aab07b4ad069dfafc01b4532e1e44cbf7177e1bdda197fc87434046db5b935afd9114ac5e1138eaead23c3b59dba9026d2da4a86fe800b",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 45,
"comment" : "modified bit 7 in R",
"msg" : "313233343030",
"sig" : "ddb94c53101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff2807668402b7b093fc754019324077c1f842a7d2e35adf7b87094115cec459ad5419e162988ef42b1988d9b944d9d5a7ce09c6f342afa500839",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 46,
"comment" : "modified bit 8 in R",
"msg" : "313233343030",
"sig" : "5db84c53101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff280279b70338586b9e13e669191cc0dfc2a937d50a6118758de04a4ca41f4877abdb971afa87fe4b83bc243b8dfd2cb368aa389a4cb11e83e31",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 47,
"comment" : "modified bit 16 in R",
"msg" : "313233343030",
"sig" : "5db94d53101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff280c7b847556b3a6f9447483899ab730a23004c695054dd57b1c3214fa87f632f39c8ff1471f0532b8eee4154930e1ca30d574b8f9e85b0432b",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 48,
"comment" : "modified bit 31 in R",
"msg" : "313233343030",
"sig" : "5db94cd3101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff2800b017917472b130a1cc1c8e995a252617d5ddaf1f3d48930b4876fa0d2cfedec90a8c85c8274892a1ca3b6cfce63ebfebc307210b844ae0c",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 49,
"comment" : "modified bit 32 in R",
"msg" : "313233343030",
"sig" : "5db94c53111f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff2805f38f6371860fcc4f2ec515afd35cb05d8941e2448cc469a15b8537e758b16d46b123581613462c2bb20d8a07299ab795d0998e1e4277931",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 50,
"comment" : "modified bit 63 in R",
"msg" : "313233343030",
"sig" : "5db94c53101f529f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff28017111ba6fefd45e2490f1d53a184007fa073470706d7f4a9606fcad2954e74c32116ba7701d225b76e55164e64df3245c1031f0df734bd31",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 51,
"comment" : "modified bit 64 in R",
"msg" : "313233343030",
"sig" : "5db94c53101f521f6d1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff2808d7d0aa1fd81d0e31789921771c654338f96f0b557b615e3da55670271608a0e022e4e8cf393e309f8f6412281b6147e7fce42b089eb1e0c",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 52,
"comment" : "modified bit 97 in R",
"msg" : "313233343030",
"sig" : "5db94c53101f521f6c1f43b60ca4d7e06fbd49c2e8afaf4fcc289e645e0880a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff280b08d3be6ebf4e60bf6d74e105ea2fa9b965c62816bbd22ea3bb0c1acfd12300523ca76f94b6f789488a957fbeb212d713baccf95fd594f3d",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 53,
"comment" : "modified bit 127 in R",
"msg" : "313233343030",
"sig" : "5db94c53101f521f6c1f43b60ea4d7606fbd49c2e8afaf4fcc289e645e0880a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff280a23f54857e9b0f72b2ef90d2768834590464d75933ed08c454faa762b3702a2b631c33c339d05b2e24c20a8214f99af31f93f80f416a1129",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 54,
"comment" : "modified bit 240 in R",
"msg" : "313233343030",
"sig" : "5db94c53101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0881a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff280734bdc399273d3403d934ceaae16e87a68c6bff6b77d8037ff41c97922498a58e704c29ab519d41bab70735f71fc26f589361e2b21754300",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 55,
"comment" : "modified bit 247 in R",
"msg" : "313233343030",
"sig" : "5db94c53101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0800a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff280ba961cc8d0765c99d57470ee1c0c77f0a562a198fd0175eddb0c033e0fb8525328c5e2c516e2b00f73609c7f769195eb1a02ff54090d781f",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 56,
"comment" : "modified bit 248 in R",
"msg" : "313233343030",
"sig" : "5db94c53101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880a97b8e55858df4cf2291a7303ffda446b82a117b4dd408cff280e72685907da9e5a64e4142ed02fc0c6bf95763201db5942aac055fa87e6fdd32e483fd21ed4110d5d7ef619b740fef2ad8a71fe821e42a2a",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 57,
"comment" : "modified bit 253 in R",
"msg" : "313233343030",
"sig" : "5db94c53101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880887b8e55858df4cf2291a7303ffda446b82a117b4dd408cff280500646d67c74f13471f0ad034da530f7238fe7897e532af8ec2977643a410b1d054934df567e170276389e66b3f3ccb3c15aed239d04f72b",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 58,
"comment" : "modified bit 254 in R",
"msg" : "313233343030",
"sig" : "5db94c53101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880e87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff2807bb153b8e350aa736a91c921217578539600c1299ab76522ef8f6902d79c93f274073ee6beafe6200ecaf59f7cd11bb1c833f24bf30ed52d",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 59,
"comment" : "modified bit 255 in R",
"msg" : "313233343030",
"sig" : "5db94c53101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880287b8e55858df4cf2291a7303ffda446b82a117b4dd408cff2804a67b22be599d6433b87ea961c82c457ab50f64ac6b7efb0b2f90988927f83742303c278f8248e02d5679b41ed505aba0fb51110d0def810",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 60,
"comment" : "modified bit 440 in R",
"msg" : "313233343030",
"sig" : "5db94c53101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff3807f452efb0cd97dab5506028b7b876830dee02a9c0cbd140dcde509638d4d546c30856b2151bdf79930df5bbb11f2beb66bcdc25ad75f2116",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 61,
"comment" : "modified bit 441 in R",
"msg" : "313233343030",
"sig" : "5db94c53101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff0808d78231bb3c9a87c5b8d168fe05f8197503a3d73a6d700f436b5a76ab866388baa6930191a077aca7970058932c88b7f9e6ecb13c89dcd1d",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 62,
"comment" : "modified bit 447 in R",
"msg" : "313233343030",
"sig" : "5db94c53101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cf72809e5a8406063fb3545f0fb627f841b2e3a85ad5d378018e8b58fe58e14ee5520d57abc9140e9c5a75a8b09ac3334dd0cad69b48771284321d",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 63,
"comment" : "modified bit 448 in R",
"msg" : "313233343030",
"sig" : "5db94c53101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff2811adf92201088e051ee48b57aecf46edfc68e5baeed5ae4910ba5681d370f75ab593811e18293ef0808581c254196bcbf2b4c454136a6711b",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 64,
"comment" : "modified bit 449 in R",
"msg" : "313233343030",
"sig" : "5db94c53101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff2825e06c3999e8308be439c40940b0075d3e4f65147c1608cbe6e9c432e33bed6686f9393ae2568f0ad60febcb4b6179c0d90d034e7c3c46810",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 65,
"comment" : "modified bit 454 in R",
"msg" : "313233343030",
"sig" : "5db94c53101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff2c02456bbd141df048dbf1843be6d5fef402483314c2af547b361a09f3319489eaede43404df9faf634c1298d678b5261c808b0be3726013e39",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 66,
"comment" : "modified bit 455 in R",
"msg" : "313233343030",
"sig" : "5db94c53101f521f6c1f43b60ea4d7e06fbd49c2e8afaf4fcc289e645e0880a87b8e55858df4cf2291a7303ffda446b82a117b4dd408cff2007106d2a896a7fec6dee53eea272d9b6e738c340295416b50f39a9463a5635450b9f93c4c06737affd42ae06cee5879c96c0bd58a91345503",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 67,
"comment" : "R==0",
"msg" : "313233343030",
"sig" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027ab98ab862e4e7ec3361a45ac1993e9b47d9ac40db91faed752399cee0413122b47346594fd7d2c8949b43e4cabaf17d8339ea0e307023f",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 68,
"comment" : "invalid R",
"msg" : "313233343030",
"sig" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd11bae33a0999fd3fd2bed6fa5577685e8fd595e79c006e58fd35f69f91b1d853553fb4006019a07725aa37773883dbe12253812887ac828",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 69,
"comment" : "all bits flipped in R",
"msg" : "313233343030",
"sig" : "a246b3acefe0ade093e0bc49f15b281f9042b63d175050b033d7619ba1f77f578471aa7a720b30dd6e58cfc0025bb947d5ee84b22bf7300d7f334e48141af0fade1469f5dedb851c9e725d27bd65012bada05e70cde641aad9ce0bea4983164f73816b6f13095e6b93eb03e850cad0cf0d",
"result" : "invalid",
"flags" : []
},
{
"tcId" : 70,
"comment" : "checking malleability ",
"msg" : "54657374",
"sig" : "5d053ff5b71f6ec3284525d35d77933178c8e19879886d08eccc6c7d27e9e5b5e02537dbc4d4723506e8d171fc1733857573dd02d18f48f280241bd6142ddb02c0f9fa133955d3e610b4b27cb814227de8b241ef4e86402b6ef7862b78a386950246ff38d6d2f458136d12e3c97fdd9866",
"result" : "invalid",
"flags" : [
"SignatureMalleability"
]
},
{
"tcId" : 71,
"comment" : "checking malleability ",
"msg" : "54657374",
"sig" : "5d053ff5b71f6ec3284525d35d77933178c8e19879886d08eccc6c7d27e9e5b5e02537dbc4d4723506e8d171fc1733857573dd02d18f48f28017602ec0bf9d7be34e8ad9c6c795533244e952675efdcbac9c65b9cb85402b6ef7862b78a386950246ff38d6d2f458136d12e3c97fdd98a6",
"result" : "invalid",
"flags" : [
"SignatureMalleability"
]
},
{
"tcId" : 72,
"comment" : "checking malleability ",
"msg" : "54657374",
"sig" : "5d053ff5b71f6ec3284525d35d77933178c8e19879886d08eccc6c7d27e9e5b5e02537dbc4d4723506e8d171fc1733857573dd02d18f48f280fde9de16e5226d2af9a864e2ac1a2d756456ffc4f1b3693570ad4dc584402b6ef7862b78a386950246ff38d6d2f458136d12e3c97fdd9826",
"result" : "invalid",
"flags" : [
"SignatureMalleability"
]
},
{
"tcId" : 73,
"comment" : "checking malleability ",
"msg" : "54657374",
"sig" : "5d053ff5b71f6ec3284525d35d77933178c8e19879886d08eccc6c7d27e9e5b5e02537dbc4d4723506e8d171fc1733857573dd02d18f48f280c9fd3fc42f2d50b84de67a197724e0faa43058801821a546173d76b882402b6ef7862b78a386950246ff38d6d2f458136d12e3c97fdd9826",
"result" : "invalid",
"flags" : [
"SignatureMalleability"
]
},
{
"tcId" : 74,
"comment" : "checking malleability ",
"msg" : "54657374",
"sig" : "5d053ff5b71f6ec3284525d35d77933178c8e19879886d08eccc6c7d27e9e5b5e02537dbc4d4723506e8d171fc1733857573dd02d18f48f28031d67d699a188a9ca46b4eabe2107aef237ca609cb462e24c91d25d286402b6ef7862b78a386950246ff38d6d2f458136d12e3c97fdd9866",
"result" : "invalid",
"flags" : [
"SignatureMalleability"
]
},
{
"tcId" : 75,
"comment" : "checking malleability ",
"msg" : "54657374",
"sig" : "5d053ff5b71f6ec3284525d35d77933178c8e19879886d08eccc6c7d27e9e5b5e02537dbc4d4723506e8d171fc1733857573dd02d18f48f28031d67d699a188a9ca46b4eabe2107aef237ca609cb462e24c91d25d286402b6ef7862b78a386950246ff38d6d2f458136d12e3c97fdd98a6",
"result" : "invalid",
"flags" : [
"SignatureMalleability"
]
},
{
"tcId" : 76,
"comment" : "checking malleability ",
"msg" : "54657374",
"sig" : "5d053ff5b71f6ec3284525d35d77933178c8e19879886d08eccc6c7d27e9e5b5e02537dbc4d4723506e8d171fc1733857573dd02d18f48f28031d67d699a188a9ca46b4eabe2107aef237ca609cb462e24c91d25d286402b6ef7862b78a386950246ff38d6d2f458136d12e3c97fdd9826",
"result" : "invalid",
"flags" : [
"SignatureMalleability"
]
},
{
"tcId" : 77,
"comment" : "checking malleability ",
"msg" : "54657374",
"sig" : "5d053ff5b71f6ec3284525d35d77933178c8e19879886d08eccc6c7d27e9e5b5e02537dbc4d4723506e8d171fc1733857573dd02d18f48f28030d67d699a188a9ca46b4eabe2107aef237ca609cb462e24c91d25d285402b6ef7862b78a386950246ff38d6d2f458136d12e3c97fdd9826",
"result" : "invalid",
"flags" : [
"SignatureMalleability"
]
}
]
},
{
"jwk" : {
"crv" : "Ed448",
"d" : "bIKlYsuAjRDWMr6JyFE-v2ySnzTd-oyfY8mWDvbjSKNSjIo_zC8ETjmj_FuUSS-PAy51SaIAmPlb",
"kid" : "none",
"kty" : "OKP",
"x" : "X9dEm1m0Yf0s54fsYWrUah2hNCSFpw4fig6nXYDpZ3jt8SR2m0bHBhvWeD3x5Q9s0foavq_oJWGA"
},
"key" : {
"curve" : "edwards448",
"keySize" : 448,
"pk" : "5fd7449b59b461fd2ce787ec616ad46a1da1342485a70e1f8a0ea75d80e96778edf124769b46c7061bd6783df1e50f6cd1fa1abeafe8256180",
"sk" : "6c82a562cb808d10d632be89c8513ebf6c929f34ddfa8c9f63c9960ef6e348a3528c8a3fcc2f044e39a3fc5b94492f8f032e7549a20098f95b",
"type" : "EDDSAKeyPair"
},
"keyDer" : "3043300506032b6571033a005fd7449b59b461fd2ce787ec616ad46a1da1342485a70e1f8a0ea75d80e96778edf124769b46c7061bd6783df1e50f6cd1fa1abeafe8256180",
"keyPem" : "-----BEGIN PUBLIC KEY-----\nMEMwBQYDK2VxAzoAX9dEm1m0Yf0s54fsYWrUah2hNCSFpw4fig6nXYDpZ3jt8SR2m0bHBhvWeD3x5Q9s0foavq/oJWGA\n-----END PUBLIC KEY-----\n",
"type" : "EddsaVerify",
"tests" : [
{
"tcId" : 78,
"comment" : "RFC 8032",
"msg" : "",
"sig" : "533a37f6bbe457251f023c0d88f976ae2dfb504a843e34d2074fd823d41a591f2b233f034f628281f2fd7a22ddd47d7828c59bd0a21bfd3980ff0d2028d4b18a9df63e006c5d1c2d345b925d8dc00b4104852db99ac5c7cdda8530a113a0f4dbb61149f05a7363268c71d95808ff2e652600",
"result" : "valid",
"flags" : []
}
]
},
{
"jwk" : {
"crv" : "Ed448",
"d" : "xOqwXTVwB8Yy89u0hImSTVUrCP4MNToNSh8ArNosRjr76mfF6NKHfF47w5emWZSe-AIelU4KEidO",
"kid" : "none",
"kty" : "OKP",
"x" : "Q7oo9DDN_0Vq5TFUX37NCsg0pV2TWMA3K_oMbGeYwIZq6gHrAHQoArhDjqTLghacI1FgYntMOpSA"
},
"key" : {
"curve" : "edwards448",
"keySize" : 448,
"pk" : "43ba28f430cdff456ae531545f7ecd0ac834a55d9358c0372bfa0c6c6798c0866aea01eb00742802b8438ea4cb82169c235160627b4c3a9480",
"sk" : "c4eab05d357007c632f3dbb48489924d552b08fe0c353a0d4a1f00acda2c463afbea67c5e8d2877c5e3bc397a659949ef8021e954e0a12274e",
"type" : "EDDSAKeyPair"
},
"keyDer" : "3043300506032b6571033a0043ba28f430cdff456ae531545f7ecd0ac834a55d9358c0372bfa0c6c6798c0866aea01eb00742802b8438ea4cb82169c235160627b4c3a9480",
"keyPem" : "-----BEGIN PUBLIC KEY-----\nMEMwBQYDK2VxAzoAQ7oo9DDN/0Vq5TFUX37NCsg0pV2TWMA3K/oMbGeYwIZq6gHrAHQoArhDjqTLghacI1FgYntMOpSA\n-----END PUBLIC KEY-----\n",
"type" : "EddsaVerify",
"tests" : [
{
"tcId" : 79,
"comment" : "RFC 8032: 1 octet",
"msg" : "03",
"sig" : "26b8f91727bd62897af15e41eb43c377efb9c610d48f2335cb0bd0087810f4352541b143c4b981b7e18f62de8ccdf633fc1bf037ab7cd779805e0dbcc0aae1cbcee1afb2e027df36bc04dcecbf154336c19f0af7e0a6472905e799f1953d2a0ff3348ab21aa4adafd1d234441cf807c03a00",
"result" : "valid",
"flags" : []
},
{
"tcId" : 80,
"comment" : "RFC 8032: 1 octet with context",
"msg" : "03",
"sig" : "d4f8f6131770dd46f40867d6fd5d5055de43541f8c5e35abbcd001b32a89f7d2151f7647f11d8ca2ae279fb842d607217fce6e042f6815ea000c85741de5c8da1144a6a1aba7f96de42505d7a7298524fda538fccbbb754f578c1cad10d54d0d5428407e85dcbc98a49155c13764e66c3c00",
"result" : "invalid",
"flags" : []
}
]
},
{
"jwk" : {
"crv" : "Ed448",
"d" : "zSPST3FCdOdENDI3uTKQ9RH2Ql-Y5kRZ_yA-iYUIP_32BQBVOrwOBc0CGEvbicTM1n4YeVEmfrMo",
"kid" : "none",
"kty" : "OKP",
"x" : "3OqeePNaG_NJmoMbELhskKrAHNhLZ6AQm1WjbpMoseNl_OFh1xznExpUPqTLX36fHYsAaWRHABQA"
},
"key" : {
"curve" : "edwards448",
"keySize" : 448,
"pk" : "dcea9e78f35a1bf3499a831b10b86c90aac01cd84b67a0109b55a36e9328b1e365fce161d71ce7131a543ea4cb5f7e9f1d8b00696447001400",
"sk" : "cd23d24f714274e744343237b93290f511f6425f98e64459ff203e8985083ffdf60500553abc0e05cd02184bdb89c4ccd67e187951267eb328",
"type" : "EDDSAKeyPair"
},
"keyDer" : "3043300506032b6571033a00dcea9e78f35a1bf3499a831b10b86c90aac01cd84b67a0109b55a36e9328b1e365fce161d71ce7131a543ea4cb5f7e9f1d8b00696447001400",
"keyPem" : "-----BEGIN PUBLIC KEY-----\nMEMwBQYDK2VxAzoA3OqeePNaG/NJmoMbELhskKrAHNhLZ6AQm1WjbpMoseNl/OFh1xznExpUPqTLX36fHYsAaWRHABQA\n-----END PUBLIC KEY-----\n",
"type" : "EddsaVerify",
"tests" : [
{
"tcId" : 81,
"comment" : "RFC 8032: 11 bytes",
"msg" : "0c3e544074ec63b0265e0c",
"sig" : "1f0a8888ce25e8d458a21130879b840a9089d999aaba039eaf3e3afa090a09d389dba82c4ff2ae8ac5cdfb7c55e94d5d961a29fe0109941e00b8dbdeea6d3b051068df7254c0cdc129cbe62db2dc957dbb47b51fd3f213fb8698f064774250a5028961c9bf8ffd973fe5d5c206492b140e00",
"result" : "valid",
"flags" : []
}
]
},
{
"jwk" : {
"crv" : "Ed448",
"d" : "JYzdStoy7Zyf9U5jdWrlgvuPqyrHIfLI5nanJ2hRPZOfY93bVWCRM_Ka34bsmSncy1LBxf0v9-Ib",
"kid" : "none",
"kty" : "OKP",
"x" : "O6FtoMbyzB8wGHdAdW9eeY1rxfwBXXxjzJUQ7j_UStwk2OlotuRub5TRm5RTYXJr114UnvCYF_WA"
},
"key" : {
"curve" : "edwards448",
"keySize" : 448,
"pk" : "3ba16da0c6f2cc1f30187740756f5e798d6bc5fc015d7c63cc9510ee3fd44adc24d8e968b6e46e6f94d19b945361726bd75e149ef09817f580",
"sk" : "258cdd4ada32ed9c9ff54e63756ae582fb8fab2ac721f2c8e676a72768513d939f63dddb55609133f29adf86ec9929dccb52c1c5fd2ff7e21b",
"type" : "EDDSAKeyPair"
},
"keyDer" : "3043300506032b6571033a003ba16da0c6f2cc1f30187740756f5e798d6bc5fc015d7c63cc9510ee3fd44adc24d8e968b6e46e6f94d19b945361726bd75e149ef09817f580",
"keyPem" : "-----BEGIN PUBLIC KEY-----\nMEMwBQYDK2VxAzoAO6FtoMbyzB8wGHdAdW9eeY1rxfwBXXxjzJUQ7j/UStwk2OlotuRub5TRm5RTYXJr114UnvCYF/WA\n-----END PUBLIC KEY-----\n",
"type" : "EddsaVerify",
"tests" : [
{
"tcId" : 82,
"comment" : "RFC 8032: 12 bytes",
"msg" : "64a65f3cdedcdd66811e2915",
"sig" : "7eeeab7c4e50fb799b418ee5e3197ff6bf15d43a14c34389b59dd1a7b1b85b4ae90438aca634bea45e3a2695f1270f07fdcdf7c62b8efeaf00b45c2c96ba457eb1a8bf075a3db28e5c24f6b923ed4ad747c3c9e03c7079efb87cb110d3a99861e72003cbae6d6b8b827e4e6c143064ff3c00",
"result" : "valid",
"flags" : []
}
]
},
{
"jwk" : {
"crv" : "Ed448",
"d" : "fvToRUQjZ1L7tWuPMaI6EOQoFPX1XKA3zcwRxkyaOylJwbtgcAMUYRcypsL-qY7rwCZqEak5cBAO",
"kid" : "none",
"kty" : "OKP",
"x" : "s9oHmwqkk6V3ICnwRnuuvuWoES2dOiJTI2HaKU97s4FcXcWeF2tNnzgcoJOOE8bAexdL5l36V46A"
},
"key" : {
"curve" : "edwards448",
"keySize" : 448,
"pk" : "b3da079b0aa493a5772029f0467baebee5a8112d9d3a22532361da294f7bb3815c5dc59e176b4d9f381ca0938e13c6c07b174be65dfa578e80",
"sk" : "7ef4e84544236752fbb56b8f31a23a10e42814f5f55ca037cdcc11c64c9a3b2949c1bb60700314611732a6c2fea98eebc0266a11a93970100e",
"type" : "EDDSAKeyPair"
},
"keyDer" : "3043300506032b6571033a00b3da079b0aa493a5772029f0467baebee5a8112d9d3a22532361da294f7bb3815c5dc59e176b4d9f381ca0938e13c6c07b174be65dfa578e80",
"keyPem" : "-----BEGIN PUBLIC KEY-----\nMEMwBQYDK2VxAzoAs9oHmwqkk6V3ICnwRnuuvuWoES2dOiJTI2HaKU97s4FcXcWeF2tNnzgcoJOOE8bAexdL5l36V46A\n-----END PUBLIC KEY-----\n",
"type" : "EddsaVerify",
"tests" : [
{
"tcId" : 83,
"comment" : "RFC 8032: 13 bytes",
"msg" : "64a65f3cdedcdd66811e2915e7",
"sig" : "6a12066f55331b6c22acd5d5bfc5d71228fbda80ae8dec26bdd306743c5027cb4890810c162c027468675ecf645a83176c0d7323a2ccde2d80efe5a1268e8aca1d6fbc194d3f77c44986eb4ab4177919ad8bec33eb47bbb5fc6e28196fd1caf56b4e7e0ba5519234d047155ac727a1053100",
"result" : "valid",
"flags" : []
}
]
},
{
"jwk" : {
"crv" : "Ed448",
"d" : "1l3zQa0T4AhWdoi67dqOnc3BfcAkl06ltCJ7ZTDjOb_yH5nmjKaWjzzKbf4PufT6tPoTXVVC6j8B",
"kid" : "none",
"kty" : "OKP",
"x" : "35cF9Y7bq4Asf4Njz-VWCrHGEywgqfHdFjSDom-KxTo51oCL9KHfvSYbCZuwOz-1CQbLKL2KCB8A"
},
"key" : {
"curve" : "edwards448",
"keySize" : 448,
"pk" : "df9705f58edbab802c7f8363cfe5560ab1c6132c20a9f1dd163483a26f8ac53a39d6808bf4a1dfbd261b099bb03b3fb50906cb28bd8a081f00",
"sk" : "d65df341ad13e008567688baedda8e9dcdc17dc024974ea5b4227b6530e339bff21f99e68ca6968f3cca6dfe0fb9f4fab4fa135d5542ea3f01",
"type" : "EDDSAKeyPair"
},
"keyDer" : "3043300506032b6571033a00df9705f58edbab802c7f8363cfe5560ab1c6132c20a9f1dd163483a26f8ac53a39d6808bf4a1dfbd261b099bb03b3fb50906cb28bd8a081f00",
"keyPem" : "-----BEGIN PUBLIC KEY-----\nMEMwBQYDK2VxAzoA35cF9Y7bq4Asf4Njz+VWCrHGEywgqfHdFjSDom+KxTo51oCL9KHfvSYbCZuwOz+1CQbLKL2KCB8A\n-----END PUBLIC KEY-----\n",
"type" : "EddsaVerify",
"tests" : [
{
"tcId" : 84,
"comment" : "RFC 8032: 64 bytes",
"msg" : "bd0f6a3747cd561bdddf4640a332461a4a30a12a434cd0bf40d766d9c6d458e5512204a30c17d1f50b5079631f64eb3112182da3005835461113718d1a5ef944",
"sig" : "554bc2480860b49eab8532d2a533b7d578ef473eeb58c98bb2d0e1ce488a98b18dfde9b9b90775e67f47d4a1c3482058efc9f40d2ca033a0801b63d45b3b722ef552bad3b4ccb667da350192b61c508cf7b6b5adadc2c8d9a446ef003fb05cba5f30e88e36ec2703b349ca229c2670833900",
"result" : "valid",
"flags" : []
}
]
},
{
"jwk" : {
"crv" : "Ed448",
"d" : "LsX-PBcEWr2xNqXmqRPjKrda5otT0vwUm3flBBMtN1abfnZrp0oZvWFiNDohyFkKqc68qQFMY231",
"kid" : "none",
"kty" : "OKP",
"x" : "eXVvAU3P4gefXdnnGL5BceLvJIagjyUYb2v_Q6mTa5v-EkArCK5leYo9geIunsgOdpCGLvPU7ToA"
},
"key" : {
"curve" : "edwards448",
"keySize" : 448,
"pk" : "79756f014dcfe2079f5dd9e718be4171e2ef2486a08f25186f6bff43a9936b9bfe12402b08ae65798a3d81e22e9ec80e7690862ef3d4ed3a00",
"sk" : "2ec5fe3c17045abdb136a5e6a913e32ab75ae68b53d2fc149b77e504132d37569b7e766ba74a19bd6162343a21c8590aa9cebca9014c636df5",
"type" : "EDDSAKeyPair"
},
"keyDer" : "3043300506032b6571033a0079756f014dcfe2079f5dd9e718be4171e2ef2486a08f25186f6bff43a9936b9bfe12402b08ae65798a3d81e22e9ec80e7690862ef3d4ed3a00",
"keyPem" : "-----BEGIN PUBLIC KEY-----\nMEMwBQYDK2VxAzoAeXVvAU3P4gefXdnnGL5BceLvJIagjyUYb2v/Q6mTa5v+EkArCK5leYo9geIunsgOdpCGLvPU7ToA\n-----END PUBLIC KEY-----\n",
"type" : "EddsaVerify",
"tests" : [
{
"tcId" : 85,
"comment" : "RFC 8032: 256 bytes",
"msg" : "15777532b0bdd0d1389f636c5f6b9ba734c90af572877e2d272dd078aa1e567cfa80e12928bb542330e8409f3174504107ecd5efac61ae7504dabe2a602ede89e5cca6257a7c77e27a702b3ae39fc769fc54f2395ae6a1178cab4738e543072fc1c177fe71e92e25bf03e4ecb72f47b64d0465aaea4c7fad372536c8ba516a6039c3c2a39f0e4d832be432dfa9a706a6e5c7e19f397964ca4258002f7c0541b590316dbc5622b6b2a6fe7a4abffd96105eca76ea7b98816af0748c10df048ce012d901015a51f189f3888145c03650aa23ce894c3bd889e030d565071c59f409a9981b51878fd6fc110624dcbcde0bf7a69ccce38fabdf86f3bef6044819de11",
"sig" : "c650ddbb0601c19ca11439e1640dd931f43c518ea5bea70d3dcde5f4191fe53f00cf966546b72bcc7d58be2b9badef28743954e3a44a23f880e8d4f1cfce2d7a61452d26da05896f0a50da66a239a8a188b6d825b3305ad77b73fbac0836ecc60987fd08527c1a8e80d5823e65cafe2a3d00",
"result" : "valid",
"flags" : []
}
]
},
{
"jwk" : {
"crv" : "Ed448",
"d" : "hy0JN4D103MN98ISZks3uKDyT1aBDaqDgs1Po_d2NOxE3FTxwu2b6ob6-3Yy2L4ZnqFl9a1V3Zzo",
"kid" : "none",
"kty" : "OKP",
"x" : "qBsuinClrJT_28ybrfw_6wgB8lhXi7EUrUTs4ewOeZ2gjv-4HF1oXAxW9k7srvjN8RzDhzeDjPQA"
},
"key" : {
"curve" : "edwards448",
"keySize" : 448,
"pk" : "a81b2e8a70a5ac94ffdbcc9badfc3feb0801f258578bb114ad44ece1ec0e799da08effb81c5d685c0c56f64eecaef8cdf11cc38737838cf400",
"sk" : "872d093780f5d3730df7c212664b37b8a0f24f56810daa8382cd4fa3f77634ec44dc54f1c2ed9bea86fafb7632d8be199ea165f5ad55dd9ce8",
"type" : "EDDSAKeyPair"
},
"keyDer" : "3043300506032b6571033a00a81b2e8a70a5ac94ffdbcc9badfc3feb0801f258578bb114ad44ece1ec0e799da08effb81c5d685c0c56f64eecaef8cdf11cc38737838cf400",
"keyPem" : "-----BEGIN PUBLIC KEY-----\nMEMwBQYDK2VxAzoAqBsuinClrJT/28ybrfw/6wgB8lhXi7EUrUTs4ewOeZ2gjv+4HF1oXAxW9k7srvjN8RzDhzeDjPQA\n-----END PUBLIC KEY-----\n",
"type" : "EddsaVerify",
"tests" : [
{
"tcId" : 86,
"comment" : "RFC 8032: 1023 bytes",
"msg" : "6ddf802e1aae4986935f7f981ba3f0351d6273c0a0c22c9c0e8339168e675412a3debfaf435ed651558007db4384b650fcc07e3b586a27a4f7a00ac8a6fec2cd86ae4bf1570c41e6a40c931db27b2faa15a8cedd52cff7362c4e6e23daec0fbc3a79b6806e316efcc7b68119bf46bc76a26067a53f296dafdbdc11c77f7777e972660cf4b6a9b369a6665f02e0cc9b6edfad136b4fabe723d2813db3136cfde9b6d044322fee2947952e031b73ab5c603349b307bdc27bc6cb8b8bbd7bd323219b8033a581b59eadebb09b3c4f3d2277d4f0343624acc817804728b25ab797172b4c5c21a22f9c7839d64300232eb66e53f31c723fa37fe387c7d3e50bdf9813a30e5bb12cf4cd930c40cfb4e1fc622592a49588794494d56d24ea4b40c89fc0596cc9ebb961c8cb10adde976a5d602b1c3f85b9b9a001ed3c6a4d3b1437f52096cd1956d042a597d561a596ecd3d1735a8d570ea0ec27225a2c4aaff26306d1526c1af3ca6d9cf5a2c98f47e1c46db9a33234cfd4d81f2c98538a09ebe76998d0d8fd25997c7d255c6d66ece6fa56f11144950f027795e653008f4bd7ca2dee85d8e90f3dc315130ce2a00375a318c7c3d97be2c8ce5b6db41a6254ff264fa6155baee3b0773c0f497c573f19bb4f4240281f0b1f4f7be857a4e59d416c06b4c50fa09e1810ddc6b1467baeac5a3668d11b6ecaa901440016f389f80acc4db977025e7f5924388c7e340a732e554440e76570f8dd71b7d640b3450d1fd5f0410a18f9a3494f707c717b79b4bf75c98400b096b21653b5d217cf3565c9597456f70703497a078763829bc01bb1cbc8fa04eadc9a6e3f6699587a9e75c94e5bab0036e0b2e711392cff0047d0d6b05bd2a588bc109718954259f1d86678a579a3120f19cfb2963f177aeb70f2d4844826262e51b80271272068ef5b3856fa8535aa2a88b2d41f2a0e2fda7624c2850272ac4a2f561f8f2f7a318bfd5caf9696149e4ac824ad3460538fdc25421beec2cc6818162d06bbed0c40a387192349db67a118bada6cd5ab0140ee273204f628aad1c135f770279a651e24d8c14d75a6059d76b96a6fd857def5e0b354b27ab937a5815d16b5fae407ff18222c6d1ed263be68c95f32d908bd895cd76207ae726487567f9a67dad79abec316f683b17f2d02bf07e0ac8b5bc6162cf94697b3c27cd1fea49b27f23ba2901871962506520c392da8b6ad0d99f7013fbc06c2c17a569500c8a7696481c1cd33e9b14e40b82e79a5f5db82571ba97bae3ad3e0479515bb0e2b0f3bfcd1fd33034efc6245eddd7ee2086ddae2600d8ca73e214e8c2b0bdb2b047c6a464a562ed77b73d2d841c4b34973551257713b753632efba348169abc90a68f42611a40126d7cb21b58695568186f7e569d2ff0f9e745d0487dd2eb997cafc5abf9dd102e62ff66cba87",
"sig" : "e301345a41a39a4d72fff8df69c98075a0cc082b802fc9b2b6bc503f926b65bddf7f4c8f1cb49f6396afc8a70abe6d8aef0db478d4c6b2970076c6a0484fe76d76b3a97625d79f1ce240e7c576750d295528286f719b413de9ada3e8eb78ed573603ce30d8bb761785dc30dbc320869e1a00",
"result" : "valid",
"flags" : []
}
]
}
]
}

@ -1,107 +0,0 @@
[
{
"curve": "P192",
"q": "FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831",
"private": "6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4",
"Ux": "AC2C77F529F91689FEA0EA5EFEC7F210D8EEA0B9E047ED56",
"Uy": "3BC723E57670BD4887EBC732C523063D0A7C957BC97C1C43",
"cases": [
{
"k": "32B1B6D7D42A05CB449065727A84804FB1A3E34D8F261496",
"message": "sample",
"r": "4B0B8CE98A92866A2820E20AA6B75B56382E0F9BFD5ECB55",
"s": "CCDB006926EA9565CBADC840829D8C384E06DE1F1E381B85"
},
{
"k": "5C4CE89CF56D9E7C77C8585339B006B97B5F0680B4306C6C",
"message": "test",
"r": "3A718BD8B4926C3B52EE6BBE67EF79B18CB6EB62B1AD97AE",
"s": "5662E6848A4A19B1F1AE2F72ACD4B8BBE50F1EAC65D9124F"
}
]
},
{
"curve": "P224",
"q": "FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D",
"private": "F220266E1105BFE3083E03EC7A3A654651F45E37167E88600BF257C1",
"Ux": "00CF08DA5AD719E42707FA431292DEA11244D64FC51610D94B130D6C",
"Uy": "EEAB6F3DEBE455E3DBF85416F7030CBD94F34F2D6F232C69F3C1385A",
"cases": [
{
"k": "C1D1F2F10881088301880506805FEB4825FE09ACB6816C36991AA06D",
"message": "sample",
"r": "1CDFE6662DDE1E4A1EC4CDEDF6A1F5A2FB7FBD9145C12113E6ABFD3E",
"s": "A6694FD7718A21053F225D3F46197CA699D45006C06F871808F43EBC"
},
{
"k": "DF8B38D40DCA3E077D0AC520BF56B6D565134D9B5F2EAE0D34900524",
"message": "test",
"r": "C441CE8E261DED634E4CF84910E4C5D1D22C5CF3B732BB204DBEF019",
"s": "902F42847A63BDC5F6046ADA114953120F99442D76510150F372A3F4"
}
]
},
{
"curve": "P256",
"q": "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551",
"private": "C9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721",
"Ux": "60FED4BA255A9D31C961EB74C6356D68C049B8923B61FA6CE669622E60F29FB6",
"Uy": "7903FE1008B8BC99A41AE9E95628BC64F2F1B20C2D7E9F5177A3C294D4462299",
"cases": [
{
"k": "A6E3C57DD01ABE90086538398355DD4C3B17AA873382B0F24D6129493D8AAD60",
"message": "sample",
"r": "EFD48B2AACB6A8FD1140DD9CD45E81D69D2C877B56AAF991C34D0EA84EAF3716",
"s": "F7CB1C942D657C41D436C7A1B6E29F65F3E900DBB9AFF4064DC4AB2F843ACDA8"
},
{
"k": "D16B6AE827F17175E040871A1C7EC3500192C4C92677336EC2537ACAEE0008E0",
"message": "test",
"r": "F1ABB023518351CD71D881567B1EA663ED3EFCF6C5132B354F28D3B0B7D38367",
"s": "019F4113742A2B14BD25926B49C649155F267E60D3814B4C0CC84250E46F0083"
}
]
},
{
"curve": "P384",
"q": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973",
"private": "6B9D3DAD2E1B8C1C05B19875B6659F4DE23C3B667BF297BA9AA47740787137D896D5724E4C70A825F872C9EA60D2EDF5",
"Ux": "EC3A4E415B4E19A4568618029F427FA5DA9A8BC4AE92E02E06AAE5286B300C64DEF8F0EA9055866064A254515480BC13",
"Uy": "8015D9B72D7D57244EA8EF9AC0C621896708A59367F9DFB9F54CA84B3F1C9DB1288B231C3AE0D4FE7344FD2533264720",
"cases": [
{
"k": "94ED910D1A099DAD3254E9242AE85ABDE4BA15168EAF0CA87A555FD56D10FBCA2907E3E83BA95368623B8C4686915CF9",
"message": "sample",
"r": "94EDBB92A5ECB8AAD4736E56C691916B3F88140666CE9FA73D64C4EA95AD133C81A648152E44ACF96E36DD1E80FABE46",
"s": "99EF4AEB15F178CEA1FE40DB2603138F130E740A19624526203B6351D0A3A94FA329C145786E679E7B82C71A38628AC8"
},
{
"k": "015EE46A5BF88773ED9123A5AB0807962D193719503C527B031B4C2D225092ADA71F4A459BC0DA98ADB95837DB8312EA",
"message": "test",
"r": "8203B63D3C853E8D77227FB377BCF7B7B772E97892A80F36AB775D509D7A5FEB0542A7F0812998DA8F1DD3CA3CF023DB",
"s": "DDD0760448D42D8A43AF45AF836FCE4DE8BE06B485E9B61B827C2F13173923E06A739F040649A667BF3B828246BAA5A5"
}
]
},
{
"curve": "P521",
"q": "1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409",
"private": "0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83538",
"Ux": "1894550D0785932E00EAA23B694F213F8C3121F86DC97A04E5A7167DB4E5BCD371123D46E45DB6B5D5370A7F20FB633155D38FFA16D2BD761DCAC474B9A2F5023A4",
"Uy": "0493101C962CD4D2FDDF782285E64584139C2F91B47F87FF82354D6630F746A28A0DB25741B5B34A828008B22ACC23F924FAAFBD4D33F81EA66956DFEAA2BFDFCF5",
"cases": [
{
"k": "1DAE2EA071F8110DC26882D4D5EAE0621A3256FC8847FB9022E2B7D28E6F10198B1574FDD03A9053C08A1854A168AA5A57470EC97DD5CE090124EF52A2F7ECBFFD3",
"message": "sample",
"r": "0C328FAFCBD79DD77850370C46325D987CB525569FB63C5D3BC53950E6D4C5F174E25A1EE9017B5D450606ADD152B534931D7D4E8455CC91F9B15BF05EC36E377FA",
"s": "0617CCE7CF5064806C467F678D3B4080D6F1CC50AF26CA209417308281B68AF282623EAA63E5B5C0723D8B8C37FF0777B1A20F8CCB1DCCC43997F1EE0E44DA4A67A"
},
{
"k": "16200813020EC986863BEDFC1B121F605C1215645018AEA1A7B215A564DE9EB1B38A67AA1128B80CE391C4FB71187654AAA3431027BFC7F395766CA988C964DC56D",
"message": "test",
"r": "13E99020ABF5CEE7525D16B69B229652AB6BDF2AFFCAEF38773B4B7D08725F10CDB93482FDCC54EDCEE91ECA4166B2A7C6265EF0CE2BD7051B7CEF945BABD47EE6D",
"s": "1FBD0013C674AA79CB39849527916CE301C66EA7CE8B80682786AD60F98F7E78A19CA69EFF5C57400E3B3A0AD66CE0978214D13BAF4E9AC60752F7B155E2DE4DCE3"
}
]
}
]

@ -23,29 +23,29 @@ import { default as xof_shake128_36 } from './hash-to-curve/expand_message_xof_S
import { default as xof_shake128_256 } from './hash-to-curve/expand_message_xof_SHAKE128_256.json' assert { type: 'json' };
import { default as xof_shake256_36 } from './hash-to-curve/expand_message_xof_SHAKE256_36.json' assert { type: 'json' };
// P256
import { default as p256_ro } from './hash-to-curve/P256_XMD:SHA-256_SSWU_RO_.json' assert { type: 'json' };
import { default as p256_nu } from './hash-to-curve/P256_XMD:SHA-256_SSWU_NU_.json' assert { type: 'json' };
import { default as p256_ro } from './hash-to-curve/P256_XMD_SHA-256_SSWU_RO_.json' assert { type: 'json' };
import { default as p256_nu } from './hash-to-curve/P256_XMD_SHA-256_SSWU_NU_.json' assert { type: 'json' };
// P384
import { default as p384_ro } from './hash-to-curve/P384_XMD:SHA-384_SSWU_RO_.json' assert { type: 'json' };
import { default as p384_nu } from './hash-to-curve/P384_XMD:SHA-384_SSWU_NU_.json' assert { type: 'json' };
import { default as p384_ro } from './hash-to-curve/P384_XMD_SHA-384_SSWU_RO_.json' assert { type: 'json' };
import { default as p384_nu } from './hash-to-curve/P384_XMD_SHA-384_SSWU_NU_.json' assert { type: 'json' };
// P521
import { default as p521_ro } from './hash-to-curve/P521_XMD:SHA-512_SSWU_RO_.json' assert { type: 'json' };
import { default as p521_nu } from './hash-to-curve/P521_XMD:SHA-512_SSWU_NU_.json' assert { type: 'json' };
import { default as p521_ro } from './hash-to-curve/P521_XMD_SHA-512_SSWU_RO_.json' assert { type: 'json' };
import { default as p521_nu } from './hash-to-curve/P521_XMD_SHA-512_SSWU_NU_.json' assert { type: 'json' };
// secp256k1
import { default as secp256k1_ro } from './hash-to-curve/secp256k1_XMD:SHA-256_SSWU_RO_.json' assert { type: 'json' };
import { default as secp256k1_nu } from './hash-to-curve/secp256k1_XMD:SHA-256_SSWU_NU_.json' assert { type: 'json' };
import { default as secp256k1_ro } from './hash-to-curve/secp256k1_XMD_SHA-256_SSWU_RO_.json' assert { type: 'json' };
import { default as secp256k1_nu } from './hash-to-curve/secp256k1_XMD_SHA-256_SSWU_NU_.json' assert { type: 'json' };
// bls-G1
import { default as g1_ro } from './hash-to-curve/BLS12381G1_XMD:SHA-256_SSWU_RO_.json' assert { type: 'json' };
import { default as g1_nu } from './hash-to-curve/BLS12381G1_XMD:SHA-256_SSWU_NU_.json' assert { type: 'json' };
import { default as g1_ro } from './hash-to-curve/BLS12381G1_XMD_SHA-256_SSWU_RO_.json' assert { type: 'json' };
import { default as g1_nu } from './hash-to-curve/BLS12381G1_XMD_SHA-256_SSWU_NU_.json' assert { type: 'json' };
// bls-G2
import { default as g2_ro } from './hash-to-curve/BLS12381G2_XMD:SHA-256_SSWU_RO_.json' assert { type: 'json' };
import { default as g2_nu } from './hash-to-curve/BLS12381G2_XMD:SHA-256_SSWU_NU_.json' assert { type: 'json' };
import { default as g2_ro } from './hash-to-curve/BLS12381G2_XMD_SHA-256_SSWU_RO_.json' assert { type: 'json' };
import { default as g2_nu } from './hash-to-curve/BLS12381G2_XMD_SHA-256_SSWU_NU_.json' assert { type: 'json' };
// ed25519
import { default as ed25519_ro } from './hash-to-curve/edwards25519_XMD:SHA-512_ELL2_RO_.json' assert { type: 'json' };
import { default as ed25519_nu } from './hash-to-curve/edwards25519_XMD:SHA-512_ELL2_NU_.json' assert { type: 'json' };
import { default as ed25519_ro } from './hash-to-curve/edwards25519_XMD_SHA-512_ELL2_RO_.json' assert { type: 'json' };
import { default as ed25519_nu } from './hash-to-curve/edwards25519_XMD_SHA-512_ELL2_NU_.json' assert { type: 'json' };
// ed448
import { default as ed448_ro } from './hash-to-curve/edwards448_XOF:SHAKE256_ELL2_RO_.json' assert { type: 'json' };
import { default as ed448_nu } from './hash-to-curve/edwards448_XOF:SHAKE256_ELL2_NU_.json' assert { type: 'json' };
import { default as ed448_ro } from './hash-to-curve/edwards448_XOF_SHAKE256_ELL2_RO_.json' assert { type: 'json' };
import { default as ed448_nu } from './hash-to-curve/edwards448_XOF_SHAKE256_ELL2_NU_.json' assert { type: 'json' };
function testExpandXMD(hash, vectors) {
describe(`${vectors.hash}/${vectors.DST.length}`, () => {

@ -4,7 +4,9 @@ import { should } from 'micro-should';
import './basic.test.js';
import './nist.test.js';
import './ed448.test.js';
import './ed448-addons.test.js';
import './ed25519.test.js';
import './ed25519-addons.test.js';
import './secp256k1.test.js';
import './secp256k1-schnorr.test.js';
import './jubjub.test.js';

@ -1,14 +1,16 @@
import { deepStrictEqual } from 'assert';
import { deepStrictEqual, throws } from 'assert';
import { describe, should } from 'micro-should';
import { secp192r1, secp224r1, P192, P224 } from './_more-curves.helpers.js';
import { secp256r1, P256 } from '../esm/p256.js';
import { secp384r1, P384 } from '../esm/p384.js';
import { secp521r1, P521 } from '../esm/p521.js';
import { secp192r1, secp224r1, p192, p224 } from './_more-curves.helpers.js';
import { DER } from '../esm/abstract/weierstrass.js';
import { secp256r1, p256 } from '../esm/p256.js';
import { secp384r1, p384 } from '../esm/p384.js';
import { secp521r1, p521 } from '../esm/p521.js';
import { secp256k1 } from '../esm/secp256k1.js';
import { hexToBytes, bytesToHex } from '../esm/abstract/utils.js';
import { default as ecdsa } from './wycheproof/ecdsa_test.json' assert { type: 'json' };
import { default as ecdh } from './wycheproof/ecdh_test.json' assert { type: 'json' };
import { default as rfc6979 } from './fixtures/rfc6979.json' assert { type: 'json' };
import { default as rfc6979 } from './vectors/rfc6979.json' assert { type: 'json' };
import { default as endoVectors } from './vectors/secp256k1/endomorphism.json' assert { type: 'json' };
import { default as ecdh_secp224r1_test } from './wycheproof/ecdh_secp224r1_test.json' assert { type: 'json' };
import { default as ecdh_secp256r1_test } from './wycheproof/ecdh_secp256r1_test.json' assert { type: 'json' };
@ -22,38 +24,59 @@ import { default as secp224r1_sha3_224_test } from './wycheproof/ecdsa_secp224r1
import { default as secp224r1_sha3_256_test } from './wycheproof/ecdsa_secp224r1_sha3_256_test.json' assert { type: 'json' };
import { default as secp224r1_sha3_512_test } from './wycheproof/ecdsa_secp224r1_sha3_512_test.json' assert { type: 'json' };
import { default as secp224r1_sha512_test } from './wycheproof/ecdsa_secp224r1_sha512_test.json' assert { type: 'json' };
import { default as secp224r1_shake128_test } from './wycheproof/ecdsa_secp224r1_shake128_test.json' assert { type: 'json' };
import { default as secp256k1_sha256_bitcoin_test } from './wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.json' assert { type: 'json' };
import { default as secp256k1_sha256_test } from './wycheproof/ecdsa_secp256k1_sha256_test.json' assert { type: 'json' };
import { default as secp256k1_sha3_256_test } from './wycheproof/ecdsa_secp256k1_sha3_256_test.json' assert { type: 'json' };
import { default as secp256k1_sha3_512_test } from './wycheproof/ecdsa_secp256k1_sha3_512_test.json' assert { type: 'json' };
import { default as secp256k1_sha512_test } from './wycheproof/ecdsa_secp256k1_sha512_test.json' assert { type: 'json' };
import { default as secp256k1_shake128_test } from './wycheproof/ecdsa_secp256k1_shake128_test.json' assert { type: 'json' };
import { default as secp256k1_shake256_test } from './wycheproof/ecdsa_secp256k1_shake256_test.json' assert { type: 'json' };
import { default as secp256r1_sha256_test } from './wycheproof/ecdsa_secp256r1_sha256_test.json' assert { type: 'json' };
import { default as secp256r1_sha3_256_test } from './wycheproof/ecdsa_secp256r1_sha3_256_test.json' assert { type: 'json' };
import { default as secp256r1_sha3_512_test } from './wycheproof/ecdsa_secp256r1_sha3_512_test.json' assert { type: 'json' };
import { default as secp256r1_sha512_test } from './wycheproof/ecdsa_secp256r1_sha512_test.json' assert { type: 'json' };
import { default as secp256r1_shake128_test } from './wycheproof/ecdsa_secp256r1_shake128_test.json' assert { type: 'json' };
import { default as secp384r1_sha384_test } from './wycheproof/ecdsa_secp384r1_sha384_test.json' assert { type: 'json' };
import { default as secp384r1_sha3_384_test } from './wycheproof/ecdsa_secp384r1_sha3_384_test.json' assert { type: 'json' };
import { default as secp384r1_sha3_512_test } from './wycheproof/ecdsa_secp384r1_sha3_512_test.json' assert { type: 'json' };
import { default as secp384r1_sha512_test } from './wycheproof/ecdsa_secp384r1_sha512_test.json' assert { type: 'json' };
import { default as secp384r1_shake256_test } from './wycheproof/ecdsa_secp384r1_shake256_test.json' assert { type: 'json' };
import { default as secp521r1_sha3_512_test } from './wycheproof/ecdsa_secp521r1_sha3_512_test.json' assert { type: 'json' };
import { default as secp521r1_sha512_test } from './wycheproof/ecdsa_secp521r1_sha512_test.json' assert { type: 'json' };
import { default as secp521r1_shake256_test } from './wycheproof/ecdsa_secp521r1_shake256_test.json' assert { type: 'json' };
import { sha3_224, sha3_256, sha3_384, sha3_512 } from '@noble/hashes/sha3';
import { sha3_224, sha3_256, sha3_384, sha3_512, shake128, shake256 } from '@noble/hashes/sha3';
import { sha512, sha384 } from '@noble/hashes/sha512';
import { sha224, sha256 } from '@noble/hashes/sha256';
// TODO: maybe add to noble-hashes?
const wrapShake = (shake, dkLen) => {
const hashC = (msg) => shake(msg, { dkLen });
hashC.outputLen = dkLen;
hashC.blockLen = shake.blockLen;
hashC.create = () => shake.create({ dkLen });
return hashC;
};
const shake128_224 = wrapShake(shake128, 224 / 8);
const shake128_256 = wrapShake(shake128, 256 / 8);
const shake256_256 = wrapShake(shake256, 256 / 8);
const shake256_384 = wrapShake(shake256, 384 / 8);
const shake256_512 = wrapShake(shake256, 512 / 8);
const hex = bytesToHex;
// prettier-ignore
const NIST = {
secp192r1, P192,
secp224r1, P224,
secp256r1, P256,
secp384r1, P384,
secp521r1, P521,
secp192r1, P192: p192,
secp224r1, P224: p224,
secp256r1, P256: p256,
secp384r1, P384: p384,
secp521r1, P521: p521,
secp256k1,
};
@ -72,58 +95,75 @@ should('fields', () => {
for (const n in vectors) deepStrictEqual(NIST[n].CURVE.Fp.ORDER, vectors[n]);
});
// We don't support ASN.1 encoding of points. For tests we've implemented quick
// and dirty parser: take X last bytes of ASN.1 encoded sequence.
// If that doesn't work, we ignore such vector.
function verifyECDHVector(test, curve) {
if (test.flags.includes('InvalidAsn')) return; // Ignore invalid ASN
if (test.result === 'valid' || test.result === 'acceptable') {
const fnLen = curve.CURVE.nByteLength; // 32 for P256
const fpLen = curve.CURVE.Fp.BYTES; // 32 for P256
const encodedHexLen = fpLen * 2 * 2 + 2; // 130 (65 * 2) for P256
const pubB = test.public.slice(-encodedHexLen); // slice(-130) for P256
let privA = test.private;
// Some wycheproof vectors are padded with 00:
// 00c6cafb74e2a50c83b3d232c4585237f44d4c5433c4b3f50ce978e6aeda3a4f5d
// instead of
// c6cafb74e2a50c83b3d232c4585237f44d4c5433c4b3f50ce978e6aeda3a4f5d
if (privA.length / 2 === fnLen + 1 && privA.startsWith('00')) privA = privA.slice(2);
if (!curve.utils.isValidPrivateKey(privA)) return; // Ignore invalid private key size
try {
curve.ProjectivePoint.fromHex(pubB);
} catch (e) {
if (e.message.startsWith('Point of length')) return; // Ignore
throw e;
}
const shared = curve.getSharedSecret(privA, pubB).subarray(1);
deepStrictEqual(hex(shared), test.shared, 'valid');
} else if (test.result === 'invalid') {
let failed = false;
try {
curve.getSharedSecret(test.private, test.public);
} catch (error) {
failed = true;
}
deepStrictEqual(failed, true, 'invalid');
} else throw new Error('unknown test result');
}
describe('wycheproof ECDH', () => {
for (const group of ecdh.testGroups) {
// // Tested in secp256k1.test.js
// if (group.key.curve === 'secp256k1') continue;
// We don't have SHA-224
const CURVE = NIST[group.curve];
if (!CURVE) continue;
const curve = NIST[group.curve];
if (!curve) continue;
should(group.curve, () => {
for (const test of group.tests) {
if (test.result === 'valid' || test.result === 'acceptable') {
try {
const pub = CURVE.ProjectivePoint.fromHex(test.public);
} catch (e) {
// Our strict validation filter doesn't let weird-length DER vectors
if (e.message.startsWith('Point of length')) continue;
throw e;
}
const shared = CURVE.getSharedSecret(test.private, test.public);
deepStrictEqual(shared, test.shared, 'valid');
} else if (test.result === 'invalid') {
let failed = false;
try {
CURVE.getSharedSecret(test.private, test.public);
} catch (error) {
failed = true;
}
deepStrictEqual(failed, true, 'invalid');
} else throw new Error('unknown test result');
verifyECDHVector(test, curve);
}
});
}
// More per curve tests
const WYCHEPROOF_ECDH = {
P224: {
curve: P224,
p224: {
curve: p224,
tests: [ecdh_secp224r1_test],
},
P256: {
curve: P256,
p256: {
curve: p256,
tests: [ecdh_secp256r1_test],
},
secp256k1: {
curve: secp256k1,
tests: [ecdh_secp256k1_test],
},
P384: {
curve: P384,
p384: {
curve: p384,
tests: [ecdh_secp384r1_test],
},
P521: {
curve: P521,
p521: {
curve: p521,
tests: [ecdh_secp521r1_test],
},
};
@ -131,30 +171,12 @@ describe('wycheproof ECDH', () => {
for (const name in WYCHEPROOF_ECDH) {
const { curve, tests } = WYCHEPROOF_ECDH[name];
for (let i = 0; i < tests.length; i++) {
const test = tests[i];
for (let j = 0; j < test.testGroups.length; j++) {
const group = test.testGroups[j];
should(`additional ${name} (${i}/${j})`, () => {
const curveTests = tests[i];
for (let j = 0; j < curveTests.testGroups.length; j++) {
const group = curveTests.testGroups[j];
should(`additional ${name} (${group.tests.length})`, () => {
for (const test of group.tests) {
if (test.result === 'valid' || test.result === 'acceptable') {
try {
const pub = curve.ProjectivePoint.fromHex(test.public);
} catch (e) {
// Our strict validation filter doesn't let weird-length DER vectors
if (e.message.includes('Point of length')) continue;
throw e;
}
const shared = curve.getSharedSecret(test.private, test.public);
deepStrictEqual(hex(shared), test.shared, 'valid');
} else if (test.result === 'invalid') {
let failed = false;
try {
curve.getSharedSecret(test.private, test.public);
} catch (error) {
failed = true;
}
deepStrictEqual(failed, true, 'invalid');
} else throw new Error('unknown test result');
verifyECDHVector(test, curve);
}
});
}
@ -163,8 +185,8 @@ describe('wycheproof ECDH', () => {
});
const WYCHEPROOF_ECDSA = {
P224: {
curve: P224,
p224: {
curve: p224,
hashes: {
sha224: {
hash: sha224,
@ -190,6 +212,10 @@ const WYCHEPROOF_ECDSA = {
hash: sha512,
tests: [secp224r1_sha512_test],
},
shake128: {
hash: shake128_224,
tests: [secp224r1_shake128_test],
},
},
},
secp256k1: {
@ -197,7 +223,7 @@ const WYCHEPROOF_ECDSA = {
hashes: {
sha256: {
hash: sha256,
tests: [secp256k1_sha256_test],
tests: [secp256k1_sha256_test, secp256k1_sha256_bitcoin_test],
},
sha3_256: {
hash: sha3_256,
@ -211,10 +237,18 @@ const WYCHEPROOF_ECDSA = {
hash: sha512,
tests: [secp256k1_sha512_test],
},
shake128: {
hash: shake128_256,
tests: [secp256k1_shake128_test],
},
shake256: {
hash: shake256_256,
tests: [secp256k1_shake256_test],
},
},
},
P256: {
curve: P256,
p256: {
curve: p256,
hashes: {
sha256: {
hash: sha256,
@ -232,10 +266,14 @@ const WYCHEPROOF_ECDSA = {
hash: sha512,
tests: [secp256r1_sha512_test],
},
shake128: {
hash: shake128_256,
tests: [secp256r1_shake128_test],
},
},
},
P384: {
curve: P384,
p384: {
curve: p384,
hashes: {
sha384: {
hash: sha384,
@ -253,10 +291,14 @@ const WYCHEPROOF_ECDSA = {
hash: sha512,
tests: [secp384r1_sha512_test],
},
shake256: {
hash: shake256_384,
tests: [secp384r1_shake256_test],
},
},
},
P521: {
curve: P521,
p521: {
curve: p521,
hashes: {
sha3_512: {
hash: sha3_512,
@ -266,19 +308,23 @@ const WYCHEPROOF_ECDSA = {
hash: sha512,
tests: [secp521r1_sha512_test],
},
shake256: {
hash: shake256_512,
tests: [secp521r1_shake256_test],
},
},
},
};
function runWycheproof(name, CURVE, group, index) {
const pubKey = CURVE.ProjectivePoint.fromHex(group.key.uncompressed);
deepStrictEqual(pubKey.x, BigInt(`0x${group.key.wx}`));
deepStrictEqual(pubKey.y, BigInt(`0x${group.key.wy}`));
const key = group.publicKey;
const pubKey = CURVE.ProjectivePoint.fromHex(key.uncompressed);
deepStrictEqual(pubKey.x, BigInt(`0x${key.wx}`));
deepStrictEqual(pubKey.y, BigInt(`0x${key.wy}`));
const pubR = pubKey.toRawBytes();
for (const test of group.tests) {
const m = CURVE.CURVE.hash(hexToBytes(test.msg));
const { sig } = test;
if (test.result === 'valid' || test.result === 'acceptable') {
try {
CURVE.Signature.fromDER(sig);
@ -310,7 +356,6 @@ describe('wycheproof ECDSA', () => {
should('generic', () => {
for (const group of ecdsa.testGroups) {
// Tested in secp256k1.test.js
if (group.key.curve === 'secp256k1') continue;
let CURVE = NIST[group.key.curve];
if (!CURVE) continue;
if (group.key.curve === 'secp224r1' && group.sha !== 'SHA-224') {
@ -323,6 +368,9 @@ describe('wycheproof ECDSA', () => {
if (['Hash weaker than DL-group'].includes(test.comment)) {
continue;
}
// These old Wycheproof vectors which still accept missing zero, new one is not.
if (test.flags.includes('MissingZero') && test.result === 'acceptable')
test.result = 'invalid';
const m = CURVE.CURVE.hash(hexToBytes(test.msg));
if (test.result === 'valid' || test.result === 'acceptable') {
try {
@ -333,7 +381,12 @@ describe('wycheproof ECDSA', () => {
throw e;
}
const verified = CURVE.verify(test.sig, m, pubKey.toHex());
deepStrictEqual(verified, true, 'valid');
if (group.key.curve === 'secp256k1') {
// lowS: true for secp256k1
deepStrictEqual(verified, !CURVE.Signature.fromDER(test.sig).hasHighS(), `valid`);
} else {
deepStrictEqual(verified, true, `valid`);
}
} else if (test.result === 'invalid') {
let failed = false;
try {
@ -388,6 +441,43 @@ describe('RFC6979', () => {
}
});
should('properly add leading zero to DER', () => {
// Valid DER
deepStrictEqual(
DER.toSig(
'303c021c70049af31f8348673d56cece2b27e587a402f2a48f0b21a7911a480a021c2840bf24f6f66be287066b7cbf38788e1b7770b18fd1aa6a26d7c6dc'
),
{
r: 11796871166002955884468185727465595477481802908758874298363724580874n,
s: 4239126896857047637966364941684493209162496401998708914961872570076n,
}
);
// Invalid DER (missing trailing zero)
throws(() =>
DER.toSig(
'303c021c70049af31f8348673d56cece2b27e587a402f2a48f0b21a7911a480a021cd7bf40db0909941d78f9948340c69e14c5417f8c840b7edb35846361'
)
);
// Correctly adds trailing zero
deepStrictEqual(
DER.hexFromSig({
r: 11796871166002955884468185727465595477481802908758874298363724580874n,
s: 22720819770293592156700650145335132731295311312425682806720849797985n,
}),
'303d021c70049af31f8348673d56cece2b27e587a402f2a48f0b21a7911a480a021d00d7bf40db0909941d78f9948340c69e14c5417f8c840b7edb35846361'
);
});
should('have proper GLV endomorphism logic in secp256k1', () => {
const Point = secp256k1.ProjectivePoint;
for (let item of endoVectors) {
const point = Point.fromAffine({ x: BigInt(item.ax), y: BigInt(item.ay) });
const c = point.multiplyUnsafe(BigInt(item.scalar)).toAffine();
deepStrictEqual(c.x, BigInt(item.cx));
deepStrictEqual(c.y, BigInt(item.cy));
}
});
// ESM is broken.
import url from 'url';
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {

@ -163,6 +163,7 @@ should('poseidonperm_x5_255_3', () => {
t,
roundsFull: 8,
roundsPartial: 57,
sboxPower: 5,
mds,
roundConstants,
});
@ -229,6 +230,7 @@ should('poseidonperm_x5_255_5', () => {
t,
roundsFull: 8,
roundsPartial: 60,
sboxPower: 5,
mds,
roundConstants,
});
@ -280,6 +282,7 @@ should('poseidonperm_x5_254_3', () => {
t,
roundsFull: 8,
roundsPartial: 57,
sboxPower: 5,
mds,
roundConstants,
});
@ -347,6 +350,7 @@ should('poseidonperm_x5_254_5', () => {
t,
roundsFull: 8,
roundsPartial: 60,
sboxPower: 5,
mds,
roundConstants,
});

@ -3,7 +3,7 @@ import { readFileSync } from 'fs';
import { should, describe } from 'micro-should';
import { bytesToHex as hex } from '@noble/hashes/utils';
import { schnorr } from '../esm/secp256k1.js';
const schCsv = readFileSync('./test/vectors/schnorr.csv', 'utf-8');
const schCsv = readFileSync('./test/vectors/secp256k1/schnorr.csv', 'utf-8');
describe('schnorr.sign()', () => {
// index,secret key,public key,aux_rand,message,signature,verification result,comment

@ -9,6 +9,3 @@ export const sigFromDER = (der) => {
export const sigToDER = (sig) => sig.toDERHex();
export const selectHash = (secp) => secp.CURVE.hash;
export const normVerifySig = (s) => _secp.Signature.fromDER(s);
// export const bytesToNumberBE = secp256k1.utils.bytesToNumberBE;
// export const numberToBytesBE = secp256k1.utils.numberToBytesBE;
// export const mod = mod_;

@ -8,14 +8,17 @@ import {
secp, sigFromDER, sigToDER, selectHash, normVerifySig, mod, bytesToNumberBE, numberToBytesBE
} from './secp256k1.helpers.js';
import { default as ecdsa } from './vectors/ecdsa.json' assert { type: 'json' };
import { default as ecdh } from './vectors/ecdh.json' assert { type: 'json' };
import { default as privates } from './vectors/privates.json' assert { type: 'json' };
import { default as points } from './vectors/points.json' assert { type: 'json' };
import { default as wp } from './vectors/wychenproof.json' assert { type: 'json' };
import { default as ecdsa } from './vectors/secp256k1/ecdsa.json' assert { type: 'json' };
import { default as ecdh } from './wycheproof/ecdh_secp256k1_test.json' assert { type: 'json' };
import { default as privates } from './vectors/secp256k1/privates.json' assert { type: 'json' };
import { default as points } from './vectors/secp256k1/points.json' assert { type: 'json' };
import { default as wp } from './wycheproof/ecdsa_secp256k1_sha256_test.json' assert { type: 'json' };
// Any changes to the file will need to be aware of the fact
// the file is shared between noble-curves and noble-secp256k1.
const Point = secp.ProjectivePoint;
const privatesTxt = readFileSync('./test/vectors/privates-2.txt', 'utf-8');
const privatesTxt = readFileSync('./test/vectors/secp256k1/privates-2.txt', 'utf-8');
const FC_BIGINT = fc.bigInt(1n + 1n, secp.CURVE.n - 1n);
// prettier-ignore
@ -265,6 +268,33 @@ describe('secp256k1', () => {
deepStrictEqual(sign(ent5), e.extraEntropyMax);
}
});
should('handle one byte {extraData}', () => {
const extraEntropy = '01';
const privKey = hexToBytes(
'0101010101010101010101010101010101010101010101010101010101010101'
);
const msg = 'd1a9dc8ed4e46a6a3e5e594615ca351d7d7ef44df1e4c94c1802f3592183794b';
const res = secp.sign(msg, privKey, { extraEntropy }).toCompactHex();
deepStrictEqual(
res,
'a250ec23a54bfdecf0e924cbf484077c5044410f915cdba86731cb2e4e925aaa5b1e4e3553d88be2c48a9a0d8d849ce2cc5720d25b2f97473e02f2550abe9545'
);
});
should('handle 48 bytes {extraData}', () => {
const extraEntropy =
'000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000001';
const privKey = hexToBytes(
'0101010101010101010101010101010101010101010101010101010101010101'
);
const msg = 'd1a9dc8ed4e46a6a3e5e594615ca351d7d7ef44df1e4c94c1802f3592183794b';
const res = secp.sign(msg, privKey, { extraEntropy }).toCompactHex();
deepStrictEqual(
res,
'2bdf40f42ac0e42ee12750d03bb12b75306dae58eb3c961c5a80d78efae93e595295b66e8eb28f1eb046bb129a976340312159ec0c20b97342667572e4a8379a'
);
});
});
describe('verify()', () => {
@ -500,14 +530,24 @@ describe('secp256k1', () => {
should('wycheproof vectors', () => {
for (let group of wp.testGroups) {
// const pubKey = Point.fromHex().toRawBytes();
const pubKey = group.key.uncompressed;
const key = group.publicKey;
const pubKey = key.uncompressed;
for (let test of group.tests) {
const h = selectHash(secp);
const m = h(hexToBytes(test.msg));
if (test.result === 'valid' || test.result === 'acceptable') {
let sig;
try {
sig = sigFromDER(test.sig);
} catch (e) {
// These old Wycheproof vectors which allows invalid behaviour of DER parser
if (e.message === 'Invalid signature integer: negative') continue;
throw e;
}
const verified = secp.verify(normVerifySig(test.sig), m, pubKey);
if (sigFromDER(test.sig).hasHighS()) {
if (sig.hasHighS()) {
deepStrictEqual(verified, false);
} else {
deepStrictEqual(verified, true);

File diff suppressed because it is too large Load Diff

@ -1,72 +1,107 @@
[
{
"message": "test data",
"d": "fee0a1f7afebf9d2a5a80c0c98a31c709681cce195cbcd06342b517970c0be1e",
"k0": "fcce1de7a9bcd6b2d3defade6afa1913fb9229e3b7ddf4749b55c4848b2a196e",
"k1": "727fbcb59eb48b1d7d46f95a04991fc512eb9dbf9105628e3aec87428df28fd8",
"k15": "398f0e2c9f79728f7b3d84d447ac3a86d8b2083c8f234a0ffa9c4043d68bd258"
},
{
"message": "Everything should be made as simple as possible, but not simpler.",
"d": "0000000000000000000000000000000000000000000000000000000000000001",
"k0": "ec633bd56a5774a0940cb97e27a9e4e51dc94af737596a0c5cbb3d30332d92a5",
"k1": "df55b6d1b5c48184622b0ead41a0e02bfa5ac3ebdb4c34701454e80aabf36f56",
"k15": "def007a9a3c2f7c769c75da9d47f2af84075af95cadd1407393dc1e26086ef87"
},
{
"message": "Satoshi Nakamoto",
"d": "0000000000000000000000000000000000000000000000000000000000000002",
"k0": "d3edc1b8224e953f6ee05c8bbf7ae228f461030e47caf97cde91430b4607405e",
"k1": "f86d8e43c09a6a83953f0ab6d0af59fb7446b4660119902e9967067596b58374",
"k15": "241d1f57d6cfd2f73b1ada7907b199951f95ef5ad362b13aed84009656e0254a"
},
{
"message": "Diffie Hellman",
"d": "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
"k0": "c378a41cb17dce12340788dd3503635f54f894c306d52f6e9bc4b8f18d27afcc",
"k1": "90756c96fef41152ac9abe08819c4e95f16da2af472880192c69a2b7bac29114",
"k15": "7b3f53300ab0ccd0f698f4d67db87c44cf3e9e513d9df61137256652b2e94e7c"
},
{
"message": "Japan",
"d": "8080808080808080808080808080808080808080808080808080808080808080",
"k0": "f471e61b51d2d8db78f3dae19d973616f57cdc54caaa81c269394b8c34edcf59",
"k1": "6819d85b9730acc876fdf59e162bf309e9f63dd35550edf20869d23c2f3e6d17",
"k15": "d8e8bae3ee330a198d1f5e00ad7c5f9ed7c24c357c0a004322abca5d9cd17847"
},
{
"message": "Bitcoin",
"d": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
"k0": "36c848ffb2cbecc5422c33a994955b807665317c1ce2a0f59c689321aaa631cc",
"k1": "4ed8de1ec952a4f5b3bd79d1ff96446bcd45cabb00fc6ca127183e14671bcb85",
"k15": "56b6f47babc1662c011d3b1f93aa51a6e9b5f6512e9f2e16821a238d450a31f8"
},
{
"message": "i2FLPP8WEus5WPjpoHwheXOMSobUJVaZM1JPMQZq",
"d": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
"k0": "6e9b434fcc6bbb081a0463c094356b47d62d7efae7da9c518ed7bac23f4e2ed6",
"k1": "ae5323ae338d6117ce8520a43b92eacd2ea1312ae514d53d8e34010154c593bb",
"k15": "3eaa1b61d1b8ab2f1ca71219c399f2b8b3defa624719f1e96fe3957628c2c4ea"
},
{
"message": "lEE55EJNP7aLrMtjkeJKKux4Yg0E8E1SAJnWTCEh",
"d": "3881e5286abc580bb6139fe8e83d7c8271c6fe5e5c2d640c1f0ed0e1ee37edc9",
"k0": "5b606665a16da29cc1c5411d744ab554640479dd8abd3c04ff23bd6b302e7034",
"k1": "f8b25263152c042807c992eacd2ac2cc5790d1e9957c394f77ea368e3d9923bd",
"k15": "ea624578f7e7964ac1d84adb5b5087dd14f0ee78b49072aa19051cc15dab6f33"
},
{
"message": "2SaVPvhxkAPrayIVKcsoQO5DKA8Uv5X/esZFlf+y",
"d": "7259dff07922de7f9c4c5720d68c9745e230b32508c497dd24cb95ef18856631",
"k0": "3ab6c19ab5d3aea6aa0c6da37516b1d6e28e3985019b3adb388714e8f536686b",
"k1": "19af21b05004b0ce9cdca82458a371a9d2cf0dc35a813108c557b551c08eb52e",
"k15": "117a32665fca1b7137a91c4739ac5719fec0cf2e146f40f8e7c21b45a07ebc6a"
},
{
"message": "00A0OwO2THi7j5Z/jp0FmN6nn7N/DQd6eBnCS+/b",
"d": "0d6ea45d62b334777d6995052965c795a4f8506044b4fd7dc59c15656a28f7aa",
"k0": "79487de0c8799158294d94c0eb92ee4b567e4dc7ca18addc86e49d31ce1d2db6",
"k1": "9561d2401164a48a8f600882753b3105ebdd35e2358f4f808c4f549c91490009",
"k15": "b0d273634129ff4dbdf0df317d4062a1dbc58818f88878ffdb4ec511c77976c0"
}
]
{
"curve": "P192",
"q": "FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831",
"private": "6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4",
"Ux": "AC2C77F529F91689FEA0EA5EFEC7F210D8EEA0B9E047ED56",
"Uy": "3BC723E57670BD4887EBC732C523063D0A7C957BC97C1C43",
"cases": [
{
"k": "32B1B6D7D42A05CB449065727A84804FB1A3E34D8F261496",
"message": "sample",
"r": "4B0B8CE98A92866A2820E20AA6B75B56382E0F9BFD5ECB55",
"s": "CCDB006926EA9565CBADC840829D8C384E06DE1F1E381B85"
},
{
"k": "5C4CE89CF56D9E7C77C8585339B006B97B5F0680B4306C6C",
"message": "test",
"r": "3A718BD8B4926C3B52EE6BBE67EF79B18CB6EB62B1AD97AE",
"s": "5662E6848A4A19B1F1AE2F72ACD4B8BBE50F1EAC65D9124F"
}
]
},
{
"curve": "P224",
"q": "FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D",
"private": "F220266E1105BFE3083E03EC7A3A654651F45E37167E88600BF257C1",
"Ux": "00CF08DA5AD719E42707FA431292DEA11244D64FC51610D94B130D6C",
"Uy": "EEAB6F3DEBE455E3DBF85416F7030CBD94F34F2D6F232C69F3C1385A",
"cases": [
{
"k": "C1D1F2F10881088301880506805FEB4825FE09ACB6816C36991AA06D",
"message": "sample",
"r": "1CDFE6662DDE1E4A1EC4CDEDF6A1F5A2FB7FBD9145C12113E6ABFD3E",
"s": "A6694FD7718A21053F225D3F46197CA699D45006C06F871808F43EBC"
},
{
"k": "DF8B38D40DCA3E077D0AC520BF56B6D565134D9B5F2EAE0D34900524",
"message": "test",
"r": "C441CE8E261DED634E4CF84910E4C5D1D22C5CF3B732BB204DBEF019",
"s": "902F42847A63BDC5F6046ADA114953120F99442D76510150F372A3F4"
}
]
},
{
"curve": "P256",
"q": "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551",
"private": "C9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721",
"Ux": "60FED4BA255A9D31C961EB74C6356D68C049B8923B61FA6CE669622E60F29FB6",
"Uy": "7903FE1008B8BC99A41AE9E95628BC64F2F1B20C2D7E9F5177A3C294D4462299",
"cases": [
{
"k": "A6E3C57DD01ABE90086538398355DD4C3B17AA873382B0F24D6129493D8AAD60",
"message": "sample",
"r": "EFD48B2AACB6A8FD1140DD9CD45E81D69D2C877B56AAF991C34D0EA84EAF3716",
"s": "F7CB1C942D657C41D436C7A1B6E29F65F3E900DBB9AFF4064DC4AB2F843ACDA8"
},
{
"k": "D16B6AE827F17175E040871A1C7EC3500192C4C92677336EC2537ACAEE0008E0",
"message": "test",
"r": "F1ABB023518351CD71D881567B1EA663ED3EFCF6C5132B354F28D3B0B7D38367",
"s": "019F4113742A2B14BD25926B49C649155F267E60D3814B4C0CC84250E46F0083"
}
]
},
{
"curve": "P384",
"q": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973",
"private": "6B9D3DAD2E1B8C1C05B19875B6659F4DE23C3B667BF297BA9AA47740787137D896D5724E4C70A825F872C9EA60D2EDF5",
"Ux": "EC3A4E415B4E19A4568618029F427FA5DA9A8BC4AE92E02E06AAE5286B300C64DEF8F0EA9055866064A254515480BC13",
"Uy": "8015D9B72D7D57244EA8EF9AC0C621896708A59367F9DFB9F54CA84B3F1C9DB1288B231C3AE0D4FE7344FD2533264720",
"cases": [
{
"k": "94ED910D1A099DAD3254E9242AE85ABDE4BA15168EAF0CA87A555FD56D10FBCA2907E3E83BA95368623B8C4686915CF9",
"message": "sample",
"r": "94EDBB92A5ECB8AAD4736E56C691916B3F88140666CE9FA73D64C4EA95AD133C81A648152E44ACF96E36DD1E80FABE46",
"s": "99EF4AEB15F178CEA1FE40DB2603138F130E740A19624526203B6351D0A3A94FA329C145786E679E7B82C71A38628AC8"
},
{
"k": "015EE46A5BF88773ED9123A5AB0807962D193719503C527B031B4C2D225092ADA71F4A459BC0DA98ADB95837DB8312EA",
"message": "test",
"r": "8203B63D3C853E8D77227FB377BCF7B7B772E97892A80F36AB775D509D7A5FEB0542A7F0812998DA8F1DD3CA3CF023DB",
"s": "DDD0760448D42D8A43AF45AF836FCE4DE8BE06B485E9B61B827C2F13173923E06A739F040649A667BF3B828246BAA5A5"
}
]
},
{
"curve": "P521",
"q": "1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409",
"private": "0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83538",
"Ux": "1894550D0785932E00EAA23B694F213F8C3121F86DC97A04E5A7167DB4E5BCD371123D46E45DB6B5D5370A7F20FB633155D38FFA16D2BD761DCAC474B9A2F5023A4",
"Uy": "0493101C962CD4D2FDDF782285E64584139C2F91B47F87FF82354D6630F746A28A0DB25741B5B34A828008B22ACC23F924FAAFBD4D33F81EA66956DFEAA2BFDFCF5",
"cases": [
{
"k": "1DAE2EA071F8110DC26882D4D5EAE0621A3256FC8847FB9022E2B7D28E6F10198B1574FDD03A9053C08A1854A168AA5A57470EC97DD5CE090124EF52A2F7ECBFFD3",
"message": "sample",
"r": "0C328FAFCBD79DD77850370C46325D987CB525569FB63C5D3BC53950E6D4C5F174E25A1EE9017B5D450606ADD152B534931D7D4E8455CC91F9B15BF05EC36E377FA",
"s": "0617CCE7CF5064806C467F678D3B4080D6F1CC50AF26CA209417308281B68AF282623EAA63E5B5C0723D8B8C37FF0777B1A20F8CCB1DCCC43997F1EE0E44DA4A67A"
},
{
"k": "16200813020EC986863BEDFC1B121F605C1215645018AEA1A7B215A564DE9EB1B38A67AA1128B80CE391C4FB71187654AAA3431027BFC7F395766CA988C964DC56D",
"message": "test",
"r": "13E99020ABF5CEE7525D16B69B229652AB6BDF2AFFCAEF38773B4B7D08725F10CDB93482FDCC54EDCEE91ECA4166B2A7C6265EF0CE2BD7051B7CEF945BABD47EE6D",
"s": "1FBD0013C674AA79CB39849527916CE301C66EA7CE8B80682786AD60F98F7E78A19CA69EFF5C57400E3B3A0AD66CE0978214D13BAF4E9AC60752F7B155E2DE4DCE3"
}
]
}
]

@ -0,0 +1,26 @@
[
{
"desc": "k1neg=true, k2neg=false",
"ax": "55066263022277343669578718895168534326250603453777594175500187360389116729240",
"ay": "32670510020758816978083085130507043184471273380659243275938904335757337482424",
"scalar": "2704427838213584814824020837927043695889",
"cx": "70912011419250646761259860556624974262679413898110209707622032756145750038852",
"cy": "46481114889376149700487001434152190585794282401306514438088690968308506923285"
},
{
"desc": "k1neg=false, k2neg=true",
"ax": "55066263022277343669578718895168534326250603453777594175500187360389116729240",
"ay": "32670510020758816978083085130507043184471273380659243275938904335757337482424",
"scalar": "367917413016453100223835821029139468248",
"cx": "10322688129782350538653828383726187034025074756440739323015371090593152139135",
"cy": "68793242610611269092604721689053086352541804982835045879816374698216278704126"
},
{
"desc": "k1neg=true, k2neg=true",
"ax": "55066263022277343669578718895168534326250603453777594175500187360389116729240",
"ay": "32670510020758816978083085130507043184471273380659243275938904335757337482424",
"scalar": "3808180077262944115495528301014462100633",
"cx": "14215418389480067884450074673878587420586762919133643262861030012154939932102",
"cy": "29847359538023735520768762420255189621104408153695873716448888266404867737302"
}
]

@ -0,0 +1,72 @@
[
{
"message": "test data",
"d": "fee0a1f7afebf9d2a5a80c0c98a31c709681cce195cbcd06342b517970c0be1e",
"k0": "fcce1de7a9bcd6b2d3defade6afa1913fb9229e3b7ddf4749b55c4848b2a196e",
"k1": "727fbcb59eb48b1d7d46f95a04991fc512eb9dbf9105628e3aec87428df28fd8",
"k15": "398f0e2c9f79728f7b3d84d447ac3a86d8b2083c8f234a0ffa9c4043d68bd258"
},
{
"message": "Everything should be made as simple as possible, but not simpler.",
"d": "0000000000000000000000000000000000000000000000000000000000000001",
"k0": "ec633bd56a5774a0940cb97e27a9e4e51dc94af737596a0c5cbb3d30332d92a5",
"k1": "df55b6d1b5c48184622b0ead41a0e02bfa5ac3ebdb4c34701454e80aabf36f56",
"k15": "def007a9a3c2f7c769c75da9d47f2af84075af95cadd1407393dc1e26086ef87"
},
{
"message": "Satoshi Nakamoto",
"d": "0000000000000000000000000000000000000000000000000000000000000002",
"k0": "d3edc1b8224e953f6ee05c8bbf7ae228f461030e47caf97cde91430b4607405e",
"k1": "f86d8e43c09a6a83953f0ab6d0af59fb7446b4660119902e9967067596b58374",
"k15": "241d1f57d6cfd2f73b1ada7907b199951f95ef5ad362b13aed84009656e0254a"
},
{
"message": "Diffie Hellman",
"d": "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
"k0": "c378a41cb17dce12340788dd3503635f54f894c306d52f6e9bc4b8f18d27afcc",
"k1": "90756c96fef41152ac9abe08819c4e95f16da2af472880192c69a2b7bac29114",
"k15": "7b3f53300ab0ccd0f698f4d67db87c44cf3e9e513d9df61137256652b2e94e7c"
},
{
"message": "Japan",
"d": "8080808080808080808080808080808080808080808080808080808080808080",
"k0": "f471e61b51d2d8db78f3dae19d973616f57cdc54caaa81c269394b8c34edcf59",
"k1": "6819d85b9730acc876fdf59e162bf309e9f63dd35550edf20869d23c2f3e6d17",
"k15": "d8e8bae3ee330a198d1f5e00ad7c5f9ed7c24c357c0a004322abca5d9cd17847"
},
{
"message": "Bitcoin",
"d": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
"k0": "36c848ffb2cbecc5422c33a994955b807665317c1ce2a0f59c689321aaa631cc",
"k1": "4ed8de1ec952a4f5b3bd79d1ff96446bcd45cabb00fc6ca127183e14671bcb85",
"k15": "56b6f47babc1662c011d3b1f93aa51a6e9b5f6512e9f2e16821a238d450a31f8"
},
{
"message": "i2FLPP8WEus5WPjpoHwheXOMSobUJVaZM1JPMQZq",
"d": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
"k0": "6e9b434fcc6bbb081a0463c094356b47d62d7efae7da9c518ed7bac23f4e2ed6",
"k1": "ae5323ae338d6117ce8520a43b92eacd2ea1312ae514d53d8e34010154c593bb",
"k15": "3eaa1b61d1b8ab2f1ca71219c399f2b8b3defa624719f1e96fe3957628c2c4ea"
},
{
"message": "lEE55EJNP7aLrMtjkeJKKux4Yg0E8E1SAJnWTCEh",
"d": "3881e5286abc580bb6139fe8e83d7c8271c6fe5e5c2d640c1f0ed0e1ee37edc9",
"k0": "5b606665a16da29cc1c5411d744ab554640479dd8abd3c04ff23bd6b302e7034",
"k1": "f8b25263152c042807c992eacd2ac2cc5790d1e9957c394f77ea368e3d9923bd",
"k15": "ea624578f7e7964ac1d84adb5b5087dd14f0ee78b49072aa19051cc15dab6f33"
},
{
"message": "2SaVPvhxkAPrayIVKcsoQO5DKA8Uv5X/esZFlf+y",
"d": "7259dff07922de7f9c4c5720d68c9745e230b32508c497dd24cb95ef18856631",
"k0": "3ab6c19ab5d3aea6aa0c6da37516b1d6e28e3985019b3adb388714e8f536686b",
"k1": "19af21b05004b0ce9cdca82458a371a9d2cf0dc35a813108c557b551c08eb52e",
"k15": "117a32665fca1b7137a91c4739ac5719fec0cf2e146f40f8e7c21b45a07ebc6a"
},
{
"message": "00A0OwO2THi7j5Z/jp0FmN6nn7N/DQd6eBnCS+/b",
"d": "0d6ea45d62b334777d6995052965c795a4f8506044b4fd7dc59c15656a28f7aa",
"k0": "79487de0c8799158294d94c0eb92ee4b567e4dc7ca18addc86e49d31ce1d2db6",
"k1": "9561d2401164a48a8f600882753b3105ebdd35e2358f4f808c4f549c91490009",
"k15": "b0d273634129ff4dbdf0df317d4062a1dbc58818f88878ffdb4ec511c77976c0"
}
]

File diff suppressed because it is too large Load Diff

@ -0,0 +1,434 @@
{
"algorithm" : "EcCurveTest",
"schema" : "ec_curve_test_schema.json",
"generatorVersion" : "0.9rc5",
"numberOfTests" : 26,
"header" : [
"Test vectors of type EcCurveTest are for checking curve parameters."
],
"notes" : {
},
"testGroups" : [
{
"type" : "EcCurveTest",
"tests" : [
{
"tcId" : 1,
"comment" : "",
"flags" : [],
"name" : "secp224r1",
"oid" : "1.3.132.0.33",
"ref" : "ANSI X9.62",
"p" : "00ffffffffffffffffffffffffffffffff000000000000000000000001",
"n" : "00ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d",
"a" : "00fffffffffffffffffffffffffffffffefffffffffffffffffffffffe",
"b" : "00b4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4",
"gx" : "00b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21",
"gy" : "00bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34",
"h" : 1,
"result" : "valid"
},
{
"tcId" : 2,
"comment" : "",
"flags" : [],
"name" : "secp256r1",
"oid" : "1.2.840.10045.3.1.7",
"ref" : "ANSI X9.62",
"p" : "00ffffffff00000001000000000000000000000000ffffffffffffffffffffffff",
"n" : "00ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551",
"a" : "00ffffffff00000001000000000000000000000000fffffffffffffffffffffffc",
"b" : "5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b",
"gx" : "6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296",
"gy" : "4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5",
"h" : 1,
"result" : "valid"
},
{
"tcId" : 3,
"comment" : "",
"flags" : [],
"name" : "secp384r1",
"oid" : "1.3.132.0.34",
"ref" : "ANSI X9.62",
"p" : "00fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff",
"n" : "00ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973",
"a" : "00fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000fffffffc",
"b" : "00b3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef",
"gx" : "00aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7",
"gy" : "3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f",
"h" : 1,
"result" : "valid"
},
{
"tcId" : 4,
"comment" : "",
"flags" : [],
"name" : "secp521r1",
"oid" : "1.3.132.0.35",
"ref" : "ANSI X9.62",
"p" : "01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"n" : "01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409",
"a" : "01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc",
"b" : "51953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00",
"gx" : "00c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66",
"gy" : "011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650",
"h" : 1,
"result" : "valid"
},
{
"tcId" : 5,
"comment" : "",
"flags" : [],
"name" : "secp256k1",
"oid" : "1.3.132.0.10",
"ref" : "https://www.secg.org/sec2-v2.pdf",
"p" : "00fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f",
"n" : "00fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141",
"a" : "00",
"b" : "07",
"gx" : "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
"gy" : "483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8",
"h" : 1,
"result" : "valid"
},
{
"tcId" : 6,
"comment" : "",
"flags" : [],
"name" : "secp224k1",
"oid" : "1.3.132.0.32",
"ref" : "ANSI X9.62",
"p" : "00fffffffffffffffffffffffffffffffffffffffffffffffeffffe56d",
"n" : "010000000000000000000000000001dce8d2ec6184caf0a971769fb1f7",
"a" : "00",
"b" : "05",
"gx" : "00a1455b334df099df30fc28a169a467e9e47075a90f7e650eb6b7a45c",
"gy" : "7e089fed7fba344282cafbd6f7e319f7c0b0bd59e2ca4bdb556d61a5",
"h" : 1,
"result" : "valid"
},
{
"tcId" : 7,
"comment" : "",
"flags" : [],
"name" : "brainpoolP224r1",
"oid" : "1.3.36.3.3.2.8.1.1.5",
"ref" : "RFC 5639",
"p" : "00d7c134aa264366862a18302575d1d787b09f075797da89f57ec8c0ff",
"n" : "00d7c134aa264366862a18302575d0fb98d116bc4b6ddebca3a5a7939f",
"a" : "68a5e62ca9ce6c1c299803a6c1530b514e182ad8b0042a59cad29f43",
"b" : "2580f63ccfe44138870713b1a92369e33e2135d266dbb372386c400b",
"gx" : "0d9029ad2c7e5cf4340823b2a87dc68c9e4ce3174c1e6efdee12c07d",
"gy" : "58aa56f772c0726f24c6b89e4ecdac24354b9e99caa3f6d3761402cd",
"h" : 1,
"result" : "valid"
},
{
"tcId" : 8,
"comment" : "",
"flags" : [],
"name" : "brainpoolP256r1",
"oid" : "1.3.36.3.3.2.8.1.1.7",
"ref" : "RFC 5639",
"p" : "00a9fb57dba1eea9bc3e660a909d838d726e3bf623d52620282013481d1f6e5377",
"n" : "00a9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f7901e0e82974856a7",
"a" : "7d5a0975fc2c3057eef67530417affe7fb8055c126dc5c6ce94a4b44f330b5d9",
"b" : "26dc5c6ce94a4b44f330b5d9bbd77cbf958416295cf7e1ce6bccdc18ff8c07b6",
"gx" : "008bd2aeb9cb7e57cb2c4b482ffc81b7afb9de27e1e3bd23c23a4453bd9ace3262",
"gy" : "547ef835c3dac4fd97f8461a14611dc9c27745132ded8e545c1d54c72f046997",
"h" : 1,
"result" : "valid"
},
{
"tcId" : 9,
"comment" : "",
"flags" : [],
"name" : "brainpoolP320r1",
"oid" : "1.3.36.3.3.2.8.1.1.9",
"ref" : "RFC 5639",
"p" : "00d35e472036bc4fb7e13c785ed201e065f98fcfa6f6f40def4f92b9ec7893ec28fcd412b1f1b32e27",
"n" : "00d35e472036bc4fb7e13c785ed201e065f98fcfa5b68f12a32d482ec7ee8658e98691555b44c59311",
"a" : "3ee30b568fbab0f883ccebd46d3f3bb8a2a73513f5eb79da66190eb085ffa9f492f375a97d860eb4",
"b" : "520883949dfdbc42d3ad198640688a6fe13f41349554b49acc31dccd884539816f5eb4ac8fb1f1a6",
"gx" : "43bd7e9afb53d8b85289bcc48ee5bfe6f20137d10a087eb6e7871e2a10a599c710af8d0d39e20611",
"gy" : "14fdd05545ec1cc8ab4093247f77275e0743ffed117182eaa9c77877aaac6ac7d35245d1692e8ee1",
"h" : 1,
"result" : "valid"
},
{
"tcId" : 10,
"comment" : "",
"flags" : [],
"name" : "brainpoolP384r1",
"oid" : "1.3.36.3.3.2.8.1.1.11",
"ref" : "RFC 5639",
"p" : "008cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b412b1da197fb71123acd3a729901d1a71874700133107ec53",
"n" : "008cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b31f166e6cac0425a7cf3ab6af6b7fc3103b883202e9046565",
"a" : "7bc382c63d8c150c3c72080ace05afa0c2bea28e4fb22787139165efba91f90f8aa5814a503ad4eb04a8c7dd22ce2826",
"b" : "04a8c7dd22ce28268b39b55416f0447c2fb77de107dcd2a62e880ea53eeb62d57cb4390295dbc9943ab78696fa504c11",
"gx" : "1d1c64f068cf45ffa2a63a81b7c13f6b8847a3e77ef14fe3db7fcafe0cbd10e8e826e03436d646aaef87b2e247d4af1e",
"gy" : "008abe1d7520f9c2a45cb1eb8e95cfd55262b70b29feec5864e19c054ff99129280e4646217791811142820341263c5315",
"h" : 1,
"result" : "valid"
},
{
"tcId" : 11,
"comment" : "",
"flags" : [],
"name" : "brainpoolP512r1",
"oid" : "1.3.36.3.3.2.8.1.1.13",
"ref" : "RFC 5639",
"p" : "00aadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca703308717d4d9b009bc66842aecda12ae6a380e62881ff2f2d82c68528aa6056583a48f3",
"n" : "00aadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca70330870553e5c414ca92619418661197fac10471db1d381085ddaddb58796829ca90069",
"a" : "7830a3318b603b89e2327145ac234cc594cbdd8d3df91610a83441caea9863bc2ded5d5aa8253aa10a2ef1c98b9ac8b57f1117a72bf2c7b9e7c1ac4d77fc94ca",
"b" : "3df91610a83441caea9863bc2ded5d5aa8253aa10a2ef1c98b9ac8b57f1117a72bf2c7b9e7c1ac4d77fc94cadc083e67984050b75ebae5dd2809bd638016f723",
"gx" : "0081aee4bdd82ed9645a21322e9c4c6a9385ed9f70b5d916c1b43b62eef4d0098eff3b1f78e2d0d48d50d1687b93b97d5f7c6d5047406a5e688b352209bcb9f822",
"gy" : "7dde385d566332ecc0eabfa9cf7822fdf209f70024a57b1aa000c55b881f8111b2dcde494a5f485e5bca4bd88a2763aed1ca2b2fa8f0540678cd1e0f3ad80892",
"h" : 1,
"result" : "valid"
},
{
"tcId" : 12,
"comment" : "",
"flags" : [],
"name" : "brainpoolP224t1",
"oid" : "1.3.36.3.3.2.8.1.1.6",
"ref" : "RFC 5639",
"p" : "00d7c134aa264366862a18302575d1d787b09f075797da89f57ec8c0ff",
"n" : "00d7c134aa264366862a18302575d0fb98d116bc4b6ddebca3a5a7939f",
"a" : "00d7c134aa264366862a18302575d1d787b09f075797da89f57ec8c0fc",
"b" : "4b337d934104cd7bef271bf60ced1ed20da14c08b3bb64f18a60888d",
"gx" : "6ab1e344ce25ff3896424e7ffe14762ecb49f8928ac0c76029b4d580",
"gy" : "0374e9f5143e568cd23f3f4d7c0d4b1e41c8cc0d1c6abd5f1a46db4c",
"h" : 1,
"result" : "valid"
},
{
"tcId" : 13,
"comment" : "",
"flags" : [],
"name" : "brainpoolP256t1",
"oid" : "1.3.36.3.3.2.8.1.1.8",
"ref" : "RFC 5639",
"p" : "00a9fb57dba1eea9bc3e660a909d838d726e3bf623d52620282013481d1f6e5377",
"n" : "00a9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f7901e0e82974856a7",
"a" : "00a9fb57dba1eea9bc3e660a909d838d726e3bf623d52620282013481d1f6e5374",
"b" : "662c61c430d84ea4fe66a7733d0b76b7bf93ebc4af2f49256ae58101fee92b04",
"gx" : "00a3e8eb3cc1cfe7b7732213b23a656149afa142c47aafbc2b79a191562e1305f4",
"gy" : "2d996c823439c56d7f7b22e14644417e69bcb6de39d027001dabe8f35b25c9be",
"h" : 1,
"result" : "valid"
},
{
"tcId" : 14,
"comment" : "",
"flags" : [],
"name" : "brainpoolP320t1",
"oid" : "1.3.36.3.3.2.8.1.1.10",
"ref" : "RFC 5639",
"p" : "00d35e472036bc4fb7e13c785ed201e065f98fcfa6f6f40def4f92b9ec7893ec28fcd412b1f1b32e27",
"n" : "00d35e472036bc4fb7e13c785ed201e065f98fcfa5b68f12a32d482ec7ee8658e98691555b44c59311",
"a" : "00d35e472036bc4fb7e13c785ed201e065f98fcfa6f6f40def4f92b9ec7893ec28fcd412b1f1b32e24",
"b" : "00a7f561e038eb1ed560b3d147db782013064c19f27ed27c6780aaf77fb8a547ceb5b4fef422340353",
"gx" : "00925be9fb01afc6fb4d3e7d4990010f813408ab106c4f09cb7ee07868cc136fff3357f624a21bed52",
"gy" : "63ba3a7a27483ebf6671dbef7abb30ebee084e58a0b077ad42a5a0989d1ee71b1b9bc0455fb0d2c3",
"h" : 1,
"result" : "valid"
},
{
"tcId" : 15,
"comment" : "",
"flags" : [],
"name" : "brainpoolP384t1",
"oid" : "1.3.36.3.3.2.8.1.1.12",
"ref" : "RFC 5639",
"p" : "008cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b412b1da197fb71123acd3a729901d1a71874700133107ec53",
"n" : "008cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b31f166e6cac0425a7cf3ab6af6b7fc3103b883202e9046565",
"a" : "008cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b412b1da197fb71123acd3a729901d1a71874700133107ec50",
"b" : "7f519eada7bda81bd826dba647910f8c4b9346ed8ccdc64e4b1abd11756dce1d2074aa263b88805ced70355a33b471ee",
"gx" : "18de98b02db9a306f2afcd7235f72a819b80ab12ebd653172476fecd462aabffc4ff191b946a5f54d8d0aa2f418808cc",
"gy" : "25ab056962d30651a114afd2755ad336747f93475b7a1fca3b88f2b6a208ccfe469408584dc2b2912675bf5b9e582928",
"h" : 1,
"result" : "valid"
},
{
"tcId" : 16,
"comment" : "",
"flags" : [],
"name" : "brainpoolP512t1",
"oid" : "1.3.36.3.3.2.8.1.1.14",
"ref" : "RFC 5639",
"p" : "00aadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca703308717d4d9b009bc66842aecda12ae6a380e62881ff2f2d82c68528aa6056583a48f3",
"n" : "00aadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca70330870553e5c414ca92619418661197fac10471db1d381085ddaddb58796829ca90069",
"a" : "00aadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca703308717d4d9b009bc66842aecda12ae6a380e62881ff2f2d82c68528aa6056583a48f0",
"b" : "7cbbbcf9441cfab76e1890e46884eae321f70c0bcb4981527897504bec3e36a62bcdfa2304976540f6450085f2dae145c22553b465763689180ea2571867423e",
"gx" : "640ece5c12788717b9c1ba06cbc2a6feba85842458c56dde9db1758d39c0313d82ba51735cdb3ea499aa77a7d6943a64f7a3f25fe26f06b51baa2696fa9035da",
"gy" : "5b534bd595f5af0fa2c892376c84ace1bb4e3019b71634c01131159cae03cee9d9932184beef216bd71df2dadf86a627306ecff96dbb8bace198b61e00f8b332",
"h" : 1,
"result" : "valid"
},
{
"tcId" : 17,
"comment" : "",
"flags" : [],
"name" : "FRP256v1",
"oid" : "1.2.250.1.223.101.256.1",
"ref" : "https://www.legifrance.gouv.fr/jorf/id/JORFTEXT000024668816",
"p" : "00f1fd178c0b3ad58f10126de8ce42435b3961adbcabc8ca6de8fcf353d86e9c03",
"n" : "00f1fd178c0b3ad58f10126de8ce42435b53dc67e140d2bf941ffdd459c6d655e1",
"a" : "00f1fd178c0b3ad58f10126de8ce42435b3961adbcabc8ca6de8fcf353d86e9c00",
"b" : "00ee353fca5428a9300d4aba754a44c00fdfec0c9ae4b1a1803075ed967b7bb73f",
"gx" : "00b6b3d4c356c139eb31183d4749d423958c27d2dcaf98b70164c97a2dd98f5cff",
"gy" : "6142e0f7c8b204911f9271f0f3ecef8c2701c307e8e4c9e183115a1554062cfb",
"h" : 1,
"result" : "valid"
},
{
"tcId" : 18,
"comment" : "",
"flags" : [],
"name" : "secp192k1",
"oid" : "1.3.132.0.31",
"ref" : "ANSI X9.62",
"p" : "00fffffffffffffffffffffffffffffffffffffffeffffee37",
"n" : "00fffffffffffffffffffffffe26f2fc170f69466a74defd8d",
"a" : "00",
"b" : "03",
"gx" : "00db4ff10ec057e9ae26b07d0280b7f4341da5d1b1eae06c7d",
"gy" : "009b2f2f6d9c5628a7844163d015be86344082aa88d95e2f9d",
"h" : 1,
"result" : "valid"
},
{
"tcId" : 19,
"comment" : "",
"flags" : [],
"name" : "secp192r1",
"oid" : "1.2.840.10045.3.1.1",
"ref" : "ANSI X9.62",
"p" : "00fffffffffffffffffffffffffffffffeffffffffffffffff",
"n" : "00ffffffffffffffffffffffff99def836146bc9b1b4d22831",
"a" : "00fffffffffffffffffffffffffffffffefffffffffffffffc",
"b" : "64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1",
"gx" : "188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012",
"gy" : "07192b95ffc8da78631011ed6b24cdd573f977a11e794811",
"h" : 1,
"result" : "valid"
},
{
"tcId" : 20,
"comment" : "",
"flags" : [],
"name" : "secp160k1",
"oid" : "1.3.132.0.9",
"ref" : "https://www.secg.org/SEC2-Ver-1.0.pdf",
"p" : "00fffffffffffffffffffffffffffffffeffffac73",
"n" : "0100000000000000000001b8fa16dfab9aca16b6b3",
"a" : "00",
"b" : "07",
"gx" : "3b4c382ce37aa192a4019e763036f4f5dd4d7ebb",
"gy" : "00938cf935318fdced6bc28286531733c3f03c4fee",
"h" : 1,
"result" : "valid"
},
{
"tcId" : 21,
"comment" : "",
"flags" : [],
"name" : "secp160r1",
"oid" : "1.3.132.0.8",
"ref" : "https://www.secg.org/SEC2-Ver-1.0.pdf",
"p" : "00ffffffffffffffffffffffffffffffff7fffffff",
"n" : "0100000000000000000001f4c8f927aed3ca752257",
"a" : "00ffffffffffffffffffffffffffffffff7ffffffc",
"b" : "1c97befc54bd7a8b65acf89f81d4d4adc565fa45",
"gx" : "4a96b5688ef573284664698968c38bb913cbfc82",
"gy" : "23a628553168947d59dcc912042351377ac5fb32",
"h" : 1,
"result" : "valid"
},
{
"tcId" : 22,
"comment" : "",
"flags" : [],
"name" : "secp160r2",
"oid" : "1.3.132.0.30",
"ref" : "https://www.secg.org/SEC2-Ver-1.0.pdf",
"p" : "00fffffffffffffffffffffffffffffffeffffac73",
"n" : "0100000000000000000000351ee786a818f3a1a16b",
"a" : "00fffffffffffffffffffffffffffffffeffffac70",
"b" : "00b4e134d3fb59eb8bab57274904664d5af50388ba",
"gx" : "52dcb034293a117e1f4ff11b30f7199d3144ce6d",
"gy" : "00feaffef2e331f296e071fa0df9982cfea7d43f2e",
"h" : 1,
"result" : "valid"
},
{
"tcId" : 23,
"comment" : "",
"flags" : [],
"name" : "brainpoolP160r1",
"oid" : "1.3.36.3.3.2.8.1.1.1",
"ref" : "RFC 5639",
"p" : "00e95e4a5f737059dc60dfc7ad95b3d8139515620f",
"n" : "00e95e4a5f737059dc60df5991d45029409e60fc09",
"a" : "340e7be2a280eb74e2be61bada745d97e8f7c300",
"b" : "1e589a8595423412134faa2dbdec95c8d8675e58",
"gx" : "00bed5af16ea3f6a4f62938c4631eb5af7bdbcdbc3",
"gy" : "1667cb477a1a8ec338f94741669c976316da6321",
"h" : 1,
"result" : "valid"
},
{
"tcId" : 24,
"comment" : "",
"flags" : [],
"name" : "brainpoolP160t1",
"oid" : "1.3.36.3.3.2.8.1.1.2",
"ref" : "RFC 5639",
"p" : "00e95e4a5f737059dc60dfc7ad95b3d8139515620f",
"n" : "00e95e4a5f737059dc60df5991d45029409e60fc09",
"a" : "00e95e4a5f737059dc60dfc7ad95b3d8139515620c",
"b" : "7a556b6dae535b7b51ed2c4d7daa7a0b5c55f380",
"gx" : "00b199b13b9b34efc1397e64baeb05acc265ff2378",
"gy" : "00add6718b7c7c1961f0991b842443772152c9e0ad",
"h" : 1,
"result" : "valid"
},
{
"tcId" : 25,
"comment" : "",
"flags" : [],
"name" : "brainpoolP192r1",
"oid" : "1.3.36.3.3.2.8.1.1.3",
"ref" : "RFC 5639",
"p" : "00c302f41d932a36cda7a3463093d18db78fce476de1a86297",
"n" : "00c302f41d932a36cda7a3462f9e9e916b5be8f1029ac4acc1",
"a" : "6a91174076b1e0e19c39c031fe8685c1cae040e5c69a28ef",
"b" : "469a28ef7c28cca3dc721d044f4496bcca7ef4146fbf25c9",
"gx" : "00c0a0647eaab6a48753b033c56cb0f0900a2f5c4853375fd6",
"gy" : "14b690866abd5bb88b5f4828c1490002e6773fa2fa299b8f",
"h" : 1,
"result" : "valid"
},
{
"tcId" : 26,
"comment" : "",
"flags" : [],
"name" : "brainpoolP192t1",
"oid" : "1.3.36.3.3.2.8.1.1.4",
"ref" : "RFC 5639",
"p" : "00c302f41d932a36cda7a3463093d18db78fce476de1a86297",
"n" : "00c302f41d932a36cda7a3462f9e9e916b5be8f1029ac4acc1",
"a" : "00c302f41d932a36cda7a3463093d18db78fce476de1a86294",
"b" : "13d56ffaec78681e68f9deb43b35bec2fb68542e27897b79",
"gx" : "3ae9e58c82f63c30282e1fe7bbf43fa72c446af6f4618129",
"gy" : "097e2c5667c2223a902ab5ca449d0084b7e5b3de7ccc01c9",
"h" : 1,
"result" : "valid"
}
]
}
]
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More