86 Commits
0.2.0 ... 0.6.0

Author SHA1 Message Date
Paul Miller
79100c2d47 Release 0.6.0. 2023-01-26 06:31:16 +01:00
Paul Miller
4ef2cad685 hash-to-curve: assertValidity 2023-01-26 06:14:12 +01:00
Paul Miller
69b3ab5a57 Shuffle code 2023-01-26 05:46:14 +01:00
Paul Miller
9465e60d30 More refactoring 2023-01-26 05:24:41 +01:00
Paul Miller
0fb78b7097 Rename group to curve. More refactoring 2023-01-26 04:14:21 +01:00
Paul Miller
be0b2a32a5 Fp rename. Edwards refactor. Weierstrass Fn instead of mod 2023-01-26 03:07:45 +01:00
Paul Miller
3d77422731 Restructure tests 2023-01-26 03:06:28 +01:00
Paul Miller
c46914f1bc weierstrass: remove most private utils 2023-01-25 08:21:48 +01:00
Paul Miller
f250f355e8 Schnorr: remove all private methods 2023-01-25 08:14:53 +01:00
Paul Miller
c095d74673 More schnorr updates 2023-01-25 08:10:05 +01:00
Paul Miller
ac52fea952 Another schnorr adjustment 2023-01-25 07:55:21 +01:00
Paul Miller
f2ee24bee4 schnorr: remove packSig 2023-01-25 07:54:00 +01:00
Paul Miller
cffea91061 Schnorr, weierstrass: refactor 2023-01-25 07:48:53 +01:00
Paul Miller
5fc38fc0e7 weierstrass: prehash option in sign/verify. Remove _normalizePublicKey 2023-01-25 05:45:49 +01:00
Paul Miller
849dc38f3c Change TypeError to Error 2023-01-25 05:24:22 +01:00
Paul Miller
0422e6ef38 p.x, p.y are now getters executing toAffine() 2023-01-25 04:51:08 +01:00
Paul Miller
21d2438a33 BLS: fix tests. Poseidon: more tests 2023-01-25 00:30:53 +01:00
Paul Miller
cea4696599 BLS tests: remove async 2023-01-25 00:13:39 +01:00
Paul Miller
f14b8d2be5 More AffinePoint fixes 2023-01-25 00:07:25 +01:00
Paul Miller
2ed27da8eb weierstrass: remove affine Point 2023-01-24 06:42:44 +01:00
Paul Miller
17e5be5f1b edwards: affine Point removal tests 2023-01-24 05:37:53 +01:00
Paul Miller
a49f0d266e edwards: remove affine Point, Signature. Stricter types 2023-01-24 05:34:56 +01:00
Paul Miller
bfbcf733e6 Update tests 2023-01-24 04:02:45 +01:00
Paul Miller
7fda6de619 weierstrass: make points compressed by def. Rewrite drbg, k generation. 2023-01-24 04:02:38 +01:00
Paul Miller
2b908ad602 edwards: simplify bounds check 2023-01-24 04:01:28 +01:00
Paul Miller
ceb3f67faa stark: switch to new weierstrass methods 2023-01-23 23:07:21 +01:00
Paul Miller
a2c87f9c2f weierstrass: simplify bits2int, remove truncateHash 2023-01-23 23:06:43 +01:00
Paul Miller
e1fd346279 utils: small improvements 2023-01-23 23:06:24 +01:00
Paul Miller
11e78aadbf Edwards: prohibit number scalars, only allow bigints 2023-01-23 20:28:01 +01:00
Paul Miller
055147f1be Add poseidon252 snark-friendly hash 2023-01-23 19:41:19 +01:00
Paul Miller
6f99f6042e weierstrass: bits2int, int2octets, truncateHash now comply with standard 2023-01-21 19:03:39 +01:00
Paul Miller
1e47bf2372 Bump prettier to 2.8.3 because it fails to parse bls 2023-01-21 19:02:58 +01:00
Paul Miller
40530eae0c hash-to-curve: decrease coupling, improve tree shaking support 2023-01-21 19:02:46 +01:00
Paul Miller
b9482bb17d Release 0.5.2. 2023-01-13 16:23:52 +01:00
Paul Miller
74475dca68 Fix lint 2023-01-13 16:02:07 +01:00
Paul Miller
f4cf21b9c8 tests: Use describe() 2023-01-13 16:00:13 +01:00
Paul Miller
5312d92b2c edwards: Fix isTorsionFree() 2023-01-13 15:58:04 +01:00
Paul Miller
d1770c0ac7 Rename test 2023-01-13 01:29:54 +01:00
Paul Miller
2d37edf7d1 Remove utils.mod(), utils.invert() 2023-01-13 01:26:00 +01:00
Paul Miller
36998fede8 Fix sqrt 2023-01-13 01:21:51 +01:00
Paul Miller
83960d445d Refactor: weierstrass assertValidity and others 2023-01-12 21:18:51 +01:00
Paul Miller
23cc2aa5d1 edwards, montgomery, weierstrass: refactor 2023-01-12 20:40:16 +01:00
Paul Miller
e45d7c2d25 utils: new util; ed448: small adjustment 2023-01-12 20:39:43 +01:00
Paul Miller
bfe929aac3 modular: Tonneli-Shanks refactoring 2023-01-12 20:38:42 +01:00
Paul Miller
069452dbe7 BLS, jubjub refactoring 2023-01-12 20:38:10 +01:00
Paul Miller
2e81f31d2e ECDSA: signUnhashed(), support for key recovery from bits 2/3 2023-01-08 20:02:04 +01:00
Paul Miller
9f7df0f13b ECDSA adjustments 2023-01-08 18:46:55 +01:00
Paul Miller
5600629bca Refactor 2023-01-08 18:02:54 +01:00
Paul Miller
2bd5e9ac16 Release 0.5.1. 2022-12-31 10:31:10 +01:00
Paul Miller
6890c26091 Fix readme toc 2022-12-31 10:29:25 +01:00
Paul Miller
a15e3a93a9 Docs 2022-12-31 10:00:29 +01:00
Paul Miller
910c508da9 hash-to-curve: elligator in 25519, 448. Stark: adjust type 2022-12-31 07:51:29 +01:00
Paul Miller
12da04a2bb Improve modular math 2022-12-31 07:49:42 +01:00
Paul Miller
cc2c84f040 Improve field tests 2022-12-31 07:49:09 +01:00
Paul Miller
5d42549acc hash-to-curve: add xmd/xof support 2022-12-31 07:48:13 +01:00
Paul Miller
65d7256b9e Release 0.5.0. 2022-12-28 08:05:22 +01:00
Paul Miller
d77a98a7aa README, security 2022-12-28 08:04:55 +01:00
Paul Miller
1bfab42620 Update package.json 2022-12-28 07:57:42 +01:00
Paul Miller
f1ab259941 README 2022-12-28 07:52:04 +01:00
Paul Miller
242ee620c5 Merge packages into one 2022-12-28 07:37:45 +01:00
Paul Miller
d837831d22 Implement hash-to-curve for weierstrass curves, add test vectors 2022-12-28 06:31:41 +01:00
Paul Miller
cae888d942 P224: fix sha224 tests 2022-12-28 06:30:13 +01:00
Paul Miller
1ab77b95dd Comment 2022-12-28 06:20:08 +01:00
Paul Miller
8b5819b12d bls12: comments 2022-12-27 05:25:23 +01:00
Paul Miller
4b5560ab4b secp256k1 tests: remove test skips 2022-12-27 05:25:09 +01:00
Paul Miller
ba121ff24c README, lint 2022-12-27 03:16:45 +01:00
Paul Miller
0277c01efd Rename field methods: multiply to mul 2022-12-27 02:17:11 +01:00
Paul Miller
6ffe656871 x25519/x448: swap arguments 2022-12-27 02:02:37 +01:00
Paul Miller
135e69bd7b Utilize complete formulas for weierstrass curves 2022-12-27 01:27:09 +01:00
Paul Miller
7a34c16c2b Add some comments, refactor a bit 2022-12-26 05:37:12 +01:00
Paul Miller
458cddcc7f README 2022-12-24 14:04:06 +01:00
Paul Miller
ccfb8695d5 Fix ed448 import 2022-12-24 04:51:34 +01:00
Paul Miller
f165222425 Release 0.4.0. 2022-12-24 04:47:30 +01:00
Paul Miller
785d74edb9 Add BLS signatures. Fix stark/P521 privkeys. 2022-12-24 04:32:52 +01:00
Paul Miller
768b268baf readme 2022-12-20 17:35:24 +01:00
Paul Miller
4df1e8de02 Release 0.2.1. 2022-12-20 15:05:20 +01:00
Paul Miller
dd7b48ac71 Adjust weierstrass error 2022-12-20 15:03:41 +01:00
Paul Miller
254bb712b4 Docs 2022-12-17 01:38:48 +01:00
Paul Miller
31f780027a readme 2022-12-17 01:25:58 +01:00
Paul Miller
80edb3323a readme 2022-12-17 01:23:16 +01:00
Paul Miller
d30b1855ee README 2022-12-16 23:12:26 +01:00
Paul Miller
f1d8650842 add test/package.json to treat tests as esm 2022-12-16 03:14:36 +01:00
Paul Miller
54c7cf8b33 definitions: esm pkg.json 2022-12-16 03:13:46 +01:00
Paul Miller
56892cc164 Adjust curve-definitions to use double-module system 2022-12-16 03:09:51 +01:00
Paul Miller
7d746a7408 Add modular division 2022-12-15 23:11:40 +01:00
Paul Miller
989af14b10 Todo in stark 2022-12-15 22:52:46 +01:00
166 changed files with 19002 additions and 6321 deletions

View File

@@ -13,6 +13,5 @@ jobs:
node-version: 18 node-version: 18
- run: npm install - run: npm install
- run: npm run build --if-present - run: npm run build --if-present
- run: cd curve-definitions; npm install; npm run build --if-present
- run: npm test
- run: npm run lint --if-present - run: npm run lint --if-present
- run: npm test

576
README.md
View File

@@ -1,52 +1,263 @@
# noble-curves # noble-curves
Minimal, zero-dependency JS implementation of elliptic curve cryptography. Minimal, auditable JS implementation of elliptic curve cryptography.
- Short Weierstrass curve with ECDSA signatures - Short Weierstrass, Edwards, Montgomery curves
- Twisted Edwards curve with EdDSA signatures - ECDSA, EdDSA, Schnorr, BLS signature schemes, ECDH key agreement
- Montgomery curve for ECDH key agreement - [hash to curve](https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/)
for encoding or hashing an arbitrary string to a point on an elliptic curve
- [Poseidon](https://www.poseidon-hash.info) ZK-friendly hash
- Auditable
- 🏎 [Ultra-fast](#speed), hand-optimized for caveats of JS engines
- 🔍 Unique tests ensure correctness. Wycheproof vectors included
- 🔻 Tree-shaking-friendly: there is no entry point, which ensures small size of your app
To keep the package minimal, no curve definitions are provided out-of-box. Use `micro-curve-definitions` module: There are two parts of the package:
- It provides P192, P224, P256, P384, P521, secp256k1, stark curve, bn254, pasta (pallas/vesta) short weierstrass curves 1. `abstract/` directory specifies zero-dependency EC algorithms
- It also provides ed25519 and ed448 twisted edwards curves 2. root directory utilizes one dependency `@noble/hashes` and provides ready-to-use:
- Main reason for separate package is the fact hashing library (like `@noble/hashes`) is required for full functionality - NIST curves secp192r1/P192, secp224r1/P224, secp256r1/P256, secp384r1/P384, secp521r1/P521
- We may reconsider merging packages in future, when a stable version would be ready - SECG curve secp256k1
- pairing-friendly curves bls12-381, bn254
- ed25519/curve25519/x25519/ristretto, edwards448/curve448/x448 RFC7748 / RFC8032 / ZIP215 stuff
Future plans: Curves incorporate work from previous noble packages
([secp256k1](https://github.com/paulmillr/noble-secp256k1),
- hash to curve standard [ed25519](https://github.com/paulmillr/noble-ed25519),
- point indistinguishability [bls12-381](https://github.com/paulmillr/noble-bls12-381)),
- pairings which had security audits and were developed from 2019 to 2022.
### This library belongs to _noble_ crypto ### This library belongs to _noble_ crypto
> **noble-crypto** — high-security, easily auditable set of contained cryptographic libraries and tools. > **noble-crypto** — high-security, easily auditable set of contained cryptographic libraries and tools.
- No dependencies, small files - Minimal dependencies, small files
- Easily auditable TypeScript/JS code - Easily auditable TypeScript/JS code
- Supported in all major browsers and stable node.js versions - Supported in all major browsers and stable node.js versions
- All releases are signed with PGP keys - All releases are signed with PGP keys
- Check out [homepage](https://paulmillr.com/noble/) & all libraries: - Check out [homepage](https://paulmillr.com/noble/) & all libraries:
[secp256k1](https://github.com/paulmillr/noble-secp256k1), [curves](https://github.com/paulmillr/noble-curves) ([secp256k1](https://github.com/paulmillr/noble-secp256k1),
[ed25519](https://github.com/paulmillr/noble-ed25519), [ed25519](https://github.com/paulmillr/noble-ed25519),
[bls12-381](https://github.com/paulmillr/noble-bls12-381), [bls12-381](https://github.com/paulmillr/noble-bls12-381)),
[hashes](https://github.com/paulmillr/noble-hashes), [hashes](https://github.com/paulmillr/noble-hashes)
[curves](https://github.com/paulmillr/noble-curves)
## Usage ## Usage
Use NPM in node.js / browser, or include single file from Use NPM in node.js / browser, or include single file from
[GitHub's releases page](https://github.com/paulmillr/noble-curves/releases): [GitHub's releases page](https://github.com/paulmillr/noble-curves/releases):
## Usage > npm install @noble/curves
```sh The library does not have an entry point. It allows you to select specific primitives and drop everything else. If you only want to use secp256k1, just use the library with rollup or other bundlers. This is done to make your bundles tiny.
npm install @noble/curves
```
```ts ```ts
import { weierstrass } from '@noble/curves/weierstrass'; // Short Weierstrass curve // Common.js and ECMAScript Modules (ESM)
import { secp256k1 } from '@noble/curves/secp256k1';
const key = secp256k1.utils.randomPrivateKey();
const pub = secp256k1.getPublicKey(key);
const msg = new Uint8Array(32).fill(1);
const sig = secp256k1.sign(msg, key);
secp256k1.verify(sig, msg, pub) === true;
sig.recoverPublicKey(msg) === pub;
const someonesPub = secp256k1.getPublicKey(secp256k1.utils.randomPrivateKey());
const shared = secp256k1.getSharedSecret(key, someonesPub);
```
All curves:
```ts
import { secp256k1 } from '@noble/curves/secp256k1';
import { ed25519, ed25519ph, ed25519ctx, x25519, RistrettoPoint } from '@noble/curves/ed25519';
import { ed448, ed448ph, ed448ctx, x448 } from '@noble/curves/ed448';
import { p256 } from '@noble/curves/p256';
import { p384 } from '@noble/curves/p384';
import { p521 } from '@noble/curves/p521';
import { pallas, vesta } from '@noble/curves/pasta';
import * as stark from '@noble/curves/stark';
import { bls12_381 } from '@noble/curves/bls12-381';
import { bn254 } from '@noble/curves/bn';
import { jubjub } from '@noble/curves/jubjub';
```
To define a custom curve, check out API below.
## API
- [Overview](#overview)
- [abstract/edwards: Twisted Edwards curve](#abstractedwards-twisted-edwards-curve)
- [abstract/montgomery: Montgomery curve](#abstractmontgomery-montgomery-curve)
- [abstract/weierstrass: Short Weierstrass curve](#abstractweierstrass-short-weierstrass-curve)
- [abstract/hash-to-curve: Hashing strings to curve points](#abstracthash-to-curve-hashing-strings-to-curve-points)
- [abstract/poseidon: Poseidon hash](#abstractposeidon-poseidon-hash)
- [abstract/modular](#abstractmodular)
- [abstract/utils](#abstractutils)
### Overview
There are following zero-dependency abstract algorithms:
```ts
import { bls } from '@noble/curves/abstract/bls';
import { twistedEdwards } from '@noble/curves/abstract/edwards';
import { montgomery } from '@noble/curves/abstract/montgomery';
import { weierstrass } from '@noble/curves/abstract/weierstrass';
import * as mod from '@noble/curves/abstract/modular';
import * as utils from '@noble/curves/abstract/utils';
```
They allow to define a new curve in a few lines of code:
```ts
import { Fp } from '@noble/curves/abstract/modular';
import { weierstrass } from '@noble/curves/abstract/weierstrass';
import { hmac } from '@noble/hashes/hmac';
import { sha256 } from '@noble/hashes/sha256';
import { concatBytes, randomBytes } from '@noble/hashes/utils';
const secp256k1 = weierstrass({
a: 0n,
b: 7n,
Fp: Fp(2n ** 256n - 2n ** 32n - 2n ** 9n - 2n ** 8n - 2n ** 7n - 2n ** 6n - 2n ** 4n - 1n),
n: 2n ** 256n - 432420386565659656852420866394968145599n,
Gx: 55066263022277343669578718895168534326250603453777594175500187360389116729240n,
Gy: 32670510020758816978083085130507043184471273380659243275938904335757337482424n,
hash: sha256,
hmac: (key: Uint8Array, ...msgs: Uint8Array[]) => hmac(sha256, key, concatBytes(...msgs)),
randomBytes,
});
```
- To initialize new curve, you must specify its variables, order (number of points on curve), field prime (over which the modular division would be done)
- All curves expose same generic interface:
- `getPublicKey()`, `sign()`, `verify()` functions
- `Point` conforming to `Group` interface with add/multiply/double/negate/add/equals methods
- `CURVE` object with curve variables like `Gx`, `Gy`, `Fp` (field), `n` (order)
- `utils` object with `randomPrivateKey()`, `mod()`, `invert()` methods (`mod CURVE.P`)
- All arithmetics is done with JS bigints over finite fields, which is defined from `modular` sub-module
- Many features require hashing, which is not provided. `@noble/hashes` can be used for this purpose.
Any other library must conform to the CHash interface:
```ts
export type CHash = {
(message: Uint8Array): Uint8Array;
blockLen: number;
outputLen: number;
create(): any;
};
```
- w-ary non-adjacent form (wNAF) method with constant-time adjustments is used for point multiplication.
It is possible to enable precomputes for edwards & weierstrass curves.
Precomputes are calculated once (takes ~20-40ms), after that most `G` base point multiplications:
for example, `getPublicKey()`, `sign()` and similar methods - would be much faster.
Use `curve.utils.precompute()` to adjust precomputation window size
- You could use optional special params to tune performance:
- `Fp({sqrt})` square root calculation, used for point decompression
- `endo` endomorphism options for Koblitz curves
### abstract/edwards: Twisted Edwards curve
Twisted Edwards curve's formula is: ax² + y² = 1 + dx²y².
- You must specify curve params `a`, `d`, field `Fp`, order `n`, cofactor `h` and coordinates `Gx`, `Gy` of generator point
- For EdDSA signatures, params `hash` is also required. `adjustScalarBytes` which instructs how to change private scalars could be specified
```typescript
import { twistedEdwards } from '@noble/curves/abstract/edwards';
import { div } from '@noble/curves/abstract/modular';
import { sha512 } from '@noble/hashes/sha512';
const ed25519 = twistedEdwards({
a: -1n,
d: div(-121665n, 121666n, 2n ** 255n - 19n), // -121665n/121666n
P: 2n ** 255n - 19n,
n: 2n ** 252n + 27742317777372353535851937790883648493n,
h: 8n,
Gx: 15112221349535400772501151409588531511454012693041857206046113283949847762202n,
Gy: 46316835694926478169428394003475163141307993866256225615783033603165251855960n,
hash: sha512,
randomBytes,
adjustScalarBytes(bytes) {
// optional in general, mandatory in ed25519
bytes[0] &= 248;
bytes[31] &= 127;
bytes[31] |= 64;
return bytes;
},
} as const);
const key = ed25519.utils.randomPrivateKey();
const pub = ed25519.getPublicKey(key);
const msg = new TextEncoder().encode('hello world'); // strings not accepted, must be Uint8Array
const sig = ed25519.sign(msg, key);
ed25519.verify(sig, msg, pub) === true;
```
`twistedEdwards()` returns `CurveFn` of following type:
```ts
export type CurveFn = {
CURVE: ReturnType<typeof validateOpts>;
getPublicKey: (privateKey: PrivKey, isCompressed?: boolean) => Uint8Array;
sign: (message: Hex, privateKey: Hex) => Uint8Array;
verify: (sig: SigType, message: Hex, publicKey: PubKey) => boolean;
Point: PointConstructor;
ExtendedPoint: ExtendedPointConstructor;
Signature: SignatureConstructor;
utils: {
randomPrivateKey: () => Uint8Array;
getExtendedPublicKey: (key: PrivKey) => {
head: Uint8Array;
prefix: Uint8Array;
scalar: bigint;
point: PointType;
pointBytes: Uint8Array;
};
};
};
```
### abstract/montgomery: Montgomery curve
For now the module only contains methods for x-only ECDH on Curve25519 / Curve448 from RFC7748.
Proper Elliptic Curve Points are not implemented yet.
You must specify curve field, `a24` special variable, `montgomeryBits`, `nByteLength`, and coordinate `u` of generator point.
```typescript
import { montgomery } from '@noble/curves/abstract/montgomery';
const x25519 = montgomery({
P: 2n ** 255n - 19n,
a24: 121665n, // TODO: change to a
montgomeryBits: 255,
nByteLength: 32,
Gu: '0900000000000000000000000000000000000000000000000000000000000000',
// Optional params
powPminus2: (x: bigint): bigint => {
return mod.pow(x, P - 2, P);
},
adjustScalarBytes(bytes) {
bytes[0] &= 248;
bytes[31] &= 127;
bytes[31] |= 64;
return bytes;
},
});
```
### abstract/weierstrass: Short Weierstrass curve
Short Weierstrass curve's formula is: y² = x³ + ax + b. Uses deterministic ECDSA from RFC6979. You can also specify `extraEntropy` in `sign()`.
- You must specify curve params: `a`, `b`, field `Fp`, order `n`, cofactor `h` and coordinates `Gx`, `Gy` of generator point
- For ECDSA, you must specify `hash`, `hmac`. It is also possible to recover keys from signatures
- For ECDH, use `getSharedSecret(privKeyA, pubKeyB)`
- Optional params are `lowS` (default value) and `endo` (endomorphism)
```typescript
import { Fp } from '@noble/curves/abstract/modular';
import { weierstrass } from '@noble/curves/abstract/weierstrass'; // Short Weierstrass curve
import { sha256 } from '@noble/hashes/sha256'; import { sha256 } from '@noble/hashes/sha256';
import { hmac } from '@noble/hashes/hmac'; import { hmac } from '@noble/hashes/hmac';
import { concatBytes, randomBytes } from '@noble/hashes/utils'; import { concatBytes, randomBytes } from '@noble/hashes/utils';
@@ -54,102 +265,255 @@ import { concatBytes, randomBytes } from '@noble/hashes/utils';
const secp256k1 = weierstrass({ const secp256k1 = weierstrass({
a: 0n, a: 0n,
b: 7n, b: 7n,
P: 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2fn, Fp: Fp(2n ** 256n - 2n ** 32n - 2n ** 9n - 2n ** 8n - 2n ** 7n - 2n ** 6n - 2n ** 4n - 1n),
n: 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141n, n: 2n ** 256n - 432420386565659656852420866394968145599n,
Gx: 55066263022277343669578718895168534326250603453777594175500187360389116729240n, Gx: 55066263022277343669578718895168534326250603453777594175500187360389116729240n,
Gy: 32670510020758816978083085130507043184471273380659243275938904335757337482424n, Gy: 32670510020758816978083085130507043184471273380659243275938904335757337482424n,
hash: sha256, hash: sha256,
hmac: (k: Uint8Array, ...msgs: Uint8Array[]) => hmac(sha256, key, concatBytes(...msgs)), hmac: (k: Uint8Array, ...msgs: Uint8Array[]) => hmac(sha256, key, concatBytes(...msgs)),
randomBytes randomBytes,
// Optional params
h: 1n, // Cofactor
lowS: true, // Allow only low-S signatures by default in sign() and verify()
endo: {
// Endomorphism options for Koblitz curve
// Beta param
beta: 0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501een,
// Split scalar k into k1, k2
splitScalar: (k: bigint) => {
// return { k1neg: true, k1: 512n, k2neg: false, k2: 448n };
},
},
}); });
secp256k1.getPublicKey(secp256k1.utils.randomPrivateKey()); // Usage
secp256k1.sign(randomBytes(32), secp256k1.utils.randomPrivateKey()); const key = secp256k1.utils.randomPrivateKey();
// secp256k1.verify(sig, msg, pub) const pub = secp256k1.getPublicKey(key);
const msg = randomBytes(32);
import { twistedEdwards } from '@noble/curves/edwards'; // Twisted Edwards curve const sig = secp256k1.sign(msg, key);
import { sha512 } from '@noble/hashes/sha512'; secp256k1.verify(sig, msg, pub); // true
sig.recoverPublicKey(msg); // == pub
const ed25519 = twistedEdwards({ const someonesPubkey = secp256k1.getPublicKey(secp256k1.utils.randomPrivateKey());
a: -1n, const shared = secp256k1.getSharedSecret(key, someonesPubkey);
d: 37095705934669439343138083508754565189542113879843219016388785533085940283555n,
P: 57896044618658097711785492504343953926634992332820282019728792003956564819949n,
n: 7237005577332262213973186563042994240857116359379907606001950938285454250989n,
h: 8n,
Gx: 15112221349535400772501151409588531511454012693041857206046113283949847762202n,
Gy: 46316835694926478169428394003475163141307993866256225615783033603165251855960n,
hash: sha512,
randomBytes,
adjustScalarBytes(bytes) { // could be no-op
bytes[0] &= 248;
bytes[31] &= 127;
bytes[31] |= 64;
return bytes;
},
} as const);
ed25519.getPublicKey(ed25519.utils.randomPrivateKey());
``` ```
## Performance `weierstrass()` returns `CurveFn`:
```ts
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;
verify: (
signature: Hex | SignatureType,
msgHash: Hex,
publicKey: Hex,
opts?: { lowS?: boolean }
) => boolean;
Point: PointConstructor;
ProjectivePoint: ProjectivePointConstructor;
Signature: SignatureConstructor;
utils: {
isValidPrivateKey(privateKey: PrivKey): boolean;
hashToPrivateKey: (hash: Hex) => Uint8Array;
randomPrivateKey: () => Uint8Array;
};
};
```
### abstract/hash-to-curve: Hashing strings to curve points
The module allows to hash arbitrary strings to elliptic curve points.
- `expand_message_xmd` [(spec)](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.4.1) produces a uniformly random byte string using a cryptographic hash function H that outputs b bits..
```ts
function expand_message_xmd(
msg: Uint8Array, DST: Uint8Array, lenInBytes: number, H: CHash
): Uint8Array;
function expand_message_xof(
msg: Uint8Array, DST: Uint8Array, lenInBytes: number, k: number, H: CHash
): Uint8Array;
```
- `hash_to_field(msg, count, options)` [(spec)](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3)
hashes arbitrary-length byte strings to a list of one or more elements of a finite field F.
* `msg` a byte string containing the message to hash
* `count` the number of elements of F to output
* `options` `{DST: string, p: bigint, m: number, k: number, expand: 'xmd' | 'xof', hash: H}`
* Returns `[u_0, ..., u_(count - 1)]`, a list of field elements.
```ts
function hash_to_field(msg: Uint8Array, count: number, options: htfOpts): bigint[][];
type htfOpts = {
// DST: a domain separation tag
// defined in section 2.2.5
DST: string;
// p: the characteristic of F
// where F is a finite field of characteristic p and order q = p^m
p: bigint;
// m: the extension degree of F, m >= 1
// where F is a finite field of characteristic p and order q = p^m
m: number;
// k: the target security level for the suite in bits
// defined in section 5.1
k: number;
// option to use a message that has already been processed by
// expand_message_xmd
expand?: 'xmd' | 'xof';
// Hash functions for: expand_message_xmd is appropriate for use with a
// wide range of hash functions, including SHA-2, SHA-3, BLAKE2, and others.
// BBS+ uses blake2: https://github.com/hyperledger/aries-framework-go/issues/2247
// TODO: verify that hash is shake if expand==='xof' via types
hash: CHash;
};
```
### abstract/poseidon: Poseidon hash
Implements [Poseidon](https://www.poseidon-hash.info) ZK-friendly hash.
There are many poseidon instances with different constants. We don't provide them,
but we provide ability to specify them manually. For actual usage, check out
stark curve source code.
```ts
import { poseidon } from '@noble/curves/abstract/poseidon';
type PoseidonOpts = {
Fp: Field<bigint>;
t: number;
roundsFull: number;
roundsPartial: number;
sboxPower?: number;
reversePartialPowIdx?: boolean; // Hack for stark
mds: bigint[][];
roundConstants: bigint[][];
};
const instance = poseidon(opts: PoseidonOpts);
```
### abstract/modular
Modular arithmetics utilities.
```typescript
import { Fp, mod, invert, div, invertBatch, sqrt } from '@noble/curves/abstract/modular';
const fp = Fp(2n ** 255n - 19n); // Finite field over 2^255-19
fp.mul(591n, 932n);
fp.pow(481n, 11024858120n);
// Generic non-FP utils are also available
mod(21n, 10n); // 21 mod 10 == 1n; fixed version of 21 % 10
invert(17n, 10n); // invert(17) mod 10; modular multiplicative inverse
div(5n, 17n, 10n); // 5/17 mod 10 == 5 * invert(17) mod 10; division
invertBatch([1n, 2n, 4n], 21n); // => [1n, 11n, 16n] in one inversion
sqrt(21n, 73n); // √21 mod 73; square root
```
### abstract/utils
```typescript
import * as utils from '@noble/curves/abstract/utils';
utils.bytesToHex(Uint8Array.from([0xde, 0xad, 0xbe, 0xef]));
utils.hexToBytes('deadbeef');
utils.hexToNumber();
utils.bytesToNumberBE(Uint8Array.from([0xde, 0xad, 0xbe, 0xef]));
utils.bytesToNumberLE(Uint8Array.from([0xde, 0xad, 0xbe, 0xef]));
utils.numberToBytesBE(123n);
utils.numberToBytesLE(123n);
utils.numberToHexUnpadded(123n);
utils.concatBytes(Uint8Array.from([0xde, 0xad]), Uint8Array.from([0xbe, 0xef]));
utils.nLength(255n);
utils.hashToPrivateScalar(sha512_of_something, secp256r1.n);
utils.equalBytes(Uint8Array.from([0xde]), Uint8Array.from([0xde]));
```
## Security
The library had no prior security audit.
[Timing attack](https://en.wikipedia.org/wiki/Timing_attack) considerations: _JIT-compiler_ and _Garbage Collector_ make "constant time" extremely hard to achieve in a scripting language. Which means _any other JS library can't have constant-timeness_. Even statically typed Rust, a language without GC, [makes it harder to achieve constant-time](https://www.chosenplaintext.ca/open-source/rust-timing-shield/security) for some cases. If your goal is absolute security, don't use any JS lib — including bindings to native ones. Use low-level libraries & languages. Nonetheless we're targetting algorithmic constant time.
We consider infrastructure attacks like rogue NPM modules very important; that's why it's crucial to minimize the amount of 3rd-party dependencies & native bindings. If your app uses 500 dependencies, any dep could get hacked and you'll be downloading malware with every `npm install`. Our goal is to minimize this attack vector.
## Speed
Benchmark results on Apple M2 with node v18.10: Benchmark results on Apple M2 with node v18.10:
``` ```
==== secp256k1 ==== getPublicKey
- getPublicKey1 (samples: 10000) secp256k1 x 5,241 ops/sec @ 190μs/op
noble_old x 8,131 ops/sec @ 122μs/op P256 x 7,993 ops/sec @ 125μs/op
secp256k1 x 7,374 ops/sec @ 135μs/op P384 x 3,819 ops/sec @ 261μs/op
- getPublicKey255 (samples: 10000) P521 x 2,074 ops/sec @ 481μs/op
noble_old x 7,894 ops/sec @ 126μs/op ed25519 x 8,390 ops/sec @ 119μs/op
secp256k1 x 7,327 ops/sec @ 136μs/op ed448 x 3,224 ops/sec @ 310μs/op
- sign (samples: 5000) sign
noble_old x 5,243 ops/sec @ 190μs/op secp256k1 x 3,934 ops/sec @ 254μs/op
secp256k1 x 4,834 ops/sec @ 206μs/op P256 x 5,327 ops/sec @ 187μs/op
- getSharedSecret (samples: 1000) P384 x 2,728 ops/sec @ 366μs/op
noble_old x 653 ops/sec @ 1ms/op P521 x 1,594 ops/sec @ 626μs/op
secp256k1 x 634 ops/sec @ 1ms/op ed25519 x 4,233 ops/sec @ 236μs/op
- verify (samples: 1000) ed448 x 1,561 ops/sec @ 640μs/op
secp256k1_old x 1,038 ops/sec @ 962μs/op verify
secp256k1 x 1,009 ops/sec @ 990μs/op secp256k1 x 731 ops/sec @ 1ms/op
==== ed25519 ==== P256 x 806 ops/sec @ 1ms/op
- getPublicKey (samples: 10000) P384 x 353 ops/sec @ 2ms/op
old x 8,632 ops/sec @ 115μs/op P521 x 171 ops/sec @ 5ms/op
noble x 8,390 ops/sec @ 119μs/op ed25519 x 860 ops/sec @ 1ms/op
- sign (samples: 5000) ed448 x 313 ops/sec @ 3ms/op
old x 4,376 ops/sec @ 228μs/op getSharedSecret
noble x 4,233 ops/sec @ 236μs/op secp256k1 x 445 ops/sec @ 2ms/op
- verify (samples: 1000) recoverPublicKey
old x 865 ops/sec @ 1ms/op secp256k1 x 732 ops/sec @ 1ms/op
noble x 860 ops/sec @ 1ms/op ==== bls12-381 ====
==== ed448 ==== getPublicKey x 817 ops/sec @ 1ms/op
- getPublicKey (samples: 5000) sign x 50 ops/sec @ 19ms/op
noble x 3,224 ops/sec @ 310μs/op verify x 34 ops/sec @ 28ms/op
- sign (samples: 2500) pairing x 89 ops/sec @ 11ms/op
noble x 1,561 ops/sec @ 640μs/op
- verify (samples: 500)
noble x 313 ops/sec @ 3ms/op
==== nist ====
- getPublicKey (samples: 2500)
P256 x 7,993 ops/sec @ 125μs/op
P384 x 3,819 ops/sec @ 261μs/op
P521 x 2,074 ops/sec @ 481μs/op
- sign (samples: 1000)
P256 x 5,327 ops/sec @ 187μs/op
P384 x 2,728 ops/sec @ 366μs/op
P521 x 1,594 ops/sec @ 626μs/op
- verify (samples: 250)
P256 x 806 ops/sec @ 1ms/op
P384 x 353 ops/sec @ 2ms/op
P521 x 171 ops/sec @ 5ms/op
==== stark ==== ==== stark ====
- pedersen (samples: 500) pedersen
old x 85 ops/sec @ 11ms/op old x 85 ops/sec @ 11ms/op
noble x 1,216 ops/sec @ 822μs/op noble x 1,216 ops/sec @ 822μs/op
- verify (samples: 500) verify
old x 302 ops/sec @ 3ms/op old x 302 ops/sec @ 3ms/op
noble x 698 ops/sec @ 1ms/op noble x 698 ops/sec @ 1ms/op
``` ```
## Upgrading
Differences from @noble/secp256k1 1.7:
1. Different double() formula (but same addition)
2. Different sqrt() function
3. DRBG supports outputLen bigger than outputLen of hmac
4. Support for different hash functions
Differences from @noble/ed25519 1.7:
1. Variable field element lengths between EDDSA/ECDH:
EDDSA (RFC8032) is 456 bits / 57 bytes, ECDH (RFC7748) is 448 bits / 56 bytes
2. Different addition formula (doubling is same)
3. uvRatio differs between curves (half-expected, not only pow fn changes)
4. Point decompression code is different (unexpected), now using generalized formula
5. Domain function was no-op for ed25519, but adds some data even with empty context for ed448
## Contributing & testing
1. Clone the repository
2. `npm install` to install build dependencies like TypeScript
3. `npm run build` to compile TypeScript code
4. `npm run test` will execute all main tests
## License ## License
MIT (c) Paul Miller [(https://paulmillr.com)](https://paulmillr.com), see LICENSE file. The MIT License (MIT)
Copyright (c) 2022 Paul Miller [(https://paulmillr.com)](https://paulmillr.com)
See LICENSE file.

18
SECURITY.md Normal file
View File

@@ -0,0 +1,18 @@
# Security Policy
## Supported Versions
| Version | Supported |
| ------- | ------------------ |
| >=0.5.0 | :white_check_mark: |
| <0.5.0 | :x: |
## Reporting a Vulnerability
Use maintainer's email specified at https://github.com/paulmillr.
It's preferred that you use
PGP key from [pgp proof](https://paulmillr.com/pgp_proof.txt) (current is [697079DA6878B89B](https://paulmillr.com/pgp_proof.txt)).
Ensure the pgp proof page has maintainer's site/github specified.
You will get an update as soon as the email is read; a "Security vulnerability" phrase in email's title would help.

424
benchmark/index.js Normal file
View File

@@ -0,0 +1,424 @@
import * as bench from 'micro-bmark';
const { run, mark } = bench; // or bench.mark
import { readFileSync } from 'fs';
// Curves
import { secp256k1 } from '../lib/secp256k1.js';
import { P256 } from '../lib/p256.js';
import { P384 } from '../lib/p384.js';
import { P521 } from '../lib/p521.js';
import { ed25519 } from '../lib/ed25519.js';
import { ed448 } from '../lib/ed448.js';
import { bls12_381 as bls } from '../lib/bls12-381.js';
// Others
import { hmac } from '@noble/hashes/hmac';
import { sha256 } from '@noble/hashes/sha256';
import { sha512 } from '@noble/hashes/sha512';
import * as old_secp from '@noble/secp256k1';
import * as old_bls from '@noble/bls12-381';
import { concatBytes, hexToBytes } from '@noble/hashes/utils';
import * as starkwareCrypto from '@starkware-industries/starkware-crypto-utils';
import * as stark from '../lib/stark.js';
old_secp.utils.sha256Sync = (...msgs) =>
sha256
.create()
.update(concatBytes(...msgs))
.digest();
old_secp.utils.hmacSha256Sync = (key, ...msgs) =>
hmac
.create(sha256, key)
.update(concatBytes(...msgs))
.digest();
import * as noble_ed25519 from '@noble/ed25519';
noble_ed25519.utils.sha512Sync = (...m) => sha512(concatBytes(...m));
// BLS
const G2_VECTORS = readFileSync('../test/bls12-381/bls12-381-g2-test-vectors.txt', 'utf-8')
.trim()
.split('\n')
.map((l) => l.split(':'));
let p1, p2, oldp1, oldp2;
// /BLS
for (let item of [secp256k1, ed25519, ed448, P256, P384, P521]) item.utils.precompute(8);
for (let item of [old_secp, noble_ed25519]) item.utils.precompute(8);
const ONLY_NOBLE = process.argv[2] === 'noble';
function generateData(namespace) {
const priv = namespace.utils.randomPrivateKey();
const pub = namespace.getPublicKey(priv);
const msg = namespace.utils.randomPrivateKey();
const sig = namespace.sign(msg, priv);
return { priv, pub, msg, sig };
}
export const CURVES = {
secp256k1: {
data: () => {
return generateData(secp256k1);
},
getPublicKey1: {
samples: 10000,
secp256k1_old: () => old_secp.getPublicKey(3n),
secp256k1: () => secp256k1.getPublicKey(3n),
},
getPublicKey255: {
samples: 10000,
secp256k1_old: () => old_secp.getPublicKey(2n ** 255n - 1n),
secp256k1: () => secp256k1.getPublicKey(2n ** 255n - 1n),
},
sign: {
samples: 5000,
secp256k1_old: ({ msg, priv }) => old_secp.signSync(msg, priv),
secp256k1: ({ msg, priv }) => secp256k1.sign(msg, priv).toCompactRawBytes(),
},
verify: {
samples: 1000,
secp256k1_old: ({ sig, msg, pub }) => {
return old_secp.verify(new old_secp.Signature(sig.r, sig.s), msg, pub);
},
secp256k1: ({ sig, msg, pub }) => secp256k1.verify(sig, msg, pub),
},
getSharedSecret: {
samples: 1000,
secp256k1_old: ({ pub, priv }) => old_secp.getSharedSecret(priv, pub),
secp256k1: ({ pub, priv }) => secp256k1.getSharedSecret(priv, pub),
},
recoverPublicKey: {
samples: 1000,
secp256k1_old: ({ sig, msg }) =>
old_secp.recoverPublicKey(msg, new old_secp.Signature(sig.r, sig.s), sig.recovery),
secp256k1: ({ sig, msg }) => sig.recoverPublicKey(msg),
},
// hashToCurve: {
// samples: 500,
// noble: () => secp256k1.Point.hashToCurve('abcd'),
// },
},
ed25519: {
data: () => {
function to32Bytes(numOrStr) {
const hex = typeof numOrStr === 'string' ? numOrStr : numOrStr.toString(16);
return hexToBytes(hex.padStart(64, '0'));
}
const priv = to32Bytes(0x9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60n);
const pub = noble_ed25519.sync.getPublicKey(priv);
const msg = to32Bytes('deadbeefdeadbeefdeadbeefdeadbeefdeadbeef');
const sig = noble_ed25519.sync.sign(msg, priv);
return { pub, priv, msg, sig };
},
getPublicKey: {
samples: 10000,
old: () => noble_ed25519.sync.getPublicKey(noble_ed25519.utils.randomPrivateKey()),
noble: () => ed25519.getPublicKey(ed25519.utils.randomPrivateKey()),
},
sign: {
samples: 5000,
old: ({ msg, priv }) => noble_ed25519.sync.sign(msg, priv),
noble: ({ msg, priv }) => ed25519.sign(msg, priv),
},
verify: {
samples: 1000,
old: ({ sig, msg, pub }) => noble_ed25519.sync.verify(sig, msg, pub),
noble: ({ sig, msg, pub }) => ed25519.verify(sig, msg, pub),
},
// hashToCurve: {
// samples: 500,
// noble: () => ed25519.Point.hashToCurve('abcd'),
// },
},
ed448: {
data: () => {
const priv = ed448.utils.randomPrivateKey();
const pub = ed448.getPublicKey(priv);
const msg = ed448.utils.randomPrivateKey();
const sig = ed448.sign(msg, priv);
return { priv, pub, msg, sig };
},
getPublicKey: {
samples: 5000,
noble: () => ed448.getPublicKey(ed448.utils.randomPrivateKey()),
},
sign: {
samples: 2500,
noble: ({ msg, priv }) => ed448.sign(msg, priv),
},
verify: {
samples: 500,
noble: ({ sig, msg, pub }) => ed448.verify(sig, msg, pub),
},
// hashToCurve: {
// samples: 500,
// noble: () => ed448.Point.hashToCurve('abcd'),
// },
},
nist: {
data: () => {
return { p256: generateData(P256), p384: generateData(P384), p521: generateData(P521) };
},
getPublicKey: {
samples: 2500,
P256: () => P256.getPublicKey(P256.utils.randomPrivateKey()),
P384: () => P384.getPublicKey(P384.utils.randomPrivateKey()),
P521: () => P521.getPublicKey(P521.utils.randomPrivateKey()),
},
sign: {
samples: 1000,
P256: ({ p256: { msg, priv } }) => P256.sign(msg, priv),
P384: ({ p384: { msg, priv } }) => P384.sign(msg, priv),
P521: ({ p521: { msg, priv } }) => P521.sign(msg, priv),
},
verify: {
samples: 250,
P256: ({ p256: { sig, msg, pub } }) => P256.verify(sig, msg, pub),
P384: ({ p384: { sig, msg, pub } }) => P384.verify(sig, msg, pub),
P521: ({ p521: { sig, msg, pub } }) => P521.verify(sig, msg, pub),
},
// hashToCurve: {
// samples: 500,
// P256: () => P256.Point.hashToCurve('abcd'),
// P384: () => P384.Point.hashToCurve('abcd'),
// P521: () => P521.Point.hashToCurve('abcd'),
// },
},
stark: {
data: () => {
const priv = '2dccce1da22003777062ee0870e9881b460a8b7eca276870f57c601f182136c';
const msg = 'c465dd6b1bbffdb05442eb17f5ca38ad1aa78a6f56bf4415bdee219114a47';
const pub = stark.getPublicKey(priv);
const sig = stark.sign(msg, priv);
const privateKey = '2dccce1da22003777062ee0870e9881b460a8b7eca276870f57c601f182136c';
const msgHash = 'c465dd6b1bbffdb05442eb17f5ca38ad1aa78a6f56bf4415bdee219114a47';
const keyPair = starkwareCrypto.default.ec.keyFromPrivate(privateKey, 'hex');
const publicKeyStark = starkwareCrypto.default.ec.keyFromPublic(
keyPair.getPublic(true, 'hex'),
'hex'
);
return { priv, sig, msg, pub, publicKeyStark, msgHash, keyPair };
},
pedersen: {
samples: 500,
old: () => {
return starkwareCrypto.default.pedersen([
'3d937c035c878245caf64531a5756109c53068da139362728feb561405371cb',
'208a0a10250e382e1e4bbe2880906c2791bf6275695e02fbbc6aeff9cd8b31a',
]);
},
noble: () => {
return stark.pedersen(
'3d937c035c878245caf64531a5756109c53068da139362728feb561405371cb',
'208a0a10250e382e1e4bbe2880906c2791bf6275695e02fbbc6aeff9cd8b31a'
);
},
},
poseidon: {
samples: 2000,
noble: () => {
return stark.poseidonHash(
0x3d937c035c878245caf64531a5756109c53068da139362728feb561405371cbn,
0x208a0a10250e382e1e4bbe2880906c2791bf6275695e02fbbc6aeff9cd8b31an
);
},
},
verify: {
samples: 500,
old: ({ publicKeyStark, msgHash, keyPair }) => {
return starkwareCrypto.default.verify(
publicKeyStark,
msgHash,
starkwareCrypto.default.sign(keyPair, msgHash)
);
},
noble: ({ priv, msg, pub }) => {
return stark.verify(stark.sign(msg, priv), msg, pub);
},
},
},
'bls12-381': {
data: async () => {
const priv = '28b90deaf189015d3a325908c5e0e4bf00f84f7e639b056ff82d7e70b6eede4c';
const pubs = G2_VECTORS.map((v) => bls.getPublicKey(v[0]));
const sigs = G2_VECTORS.map((v) => v[2]);
const pub = bls.getPublicKey(priv);
const pub512 = pubs.slice(0, 512); // .map(bls.PointG1.fromHex)
const pub32 = pub512.slice(0, 32);
const pub128 = pub512.slice(0, 128);
const pub2048 = pub512.concat(pub512, pub512, pub512);
const sig512 = sigs.slice(0, 512); // .map(bls.PointG2.fromSignature);
const sig32 = sig512.slice(0, 32);
const sig128 = sig512.slice(0, 128);
const sig2048 = sig512.concat(sig512, sig512, sig512);
return {
priv,
pubs,
sigs,
pub,
pub512,
pub32,
pub128,
pub2048,
sig32,
sig128,
sig512,
sig2048,
};
},
init: {
samples: 1,
old: () => {
oldp1 =
old_bls.PointG1.BASE.multiply(
0x28b90deaf189015d3a325908c5e0e4bf00f84f7e639b056ff82d7e70b6eede4cn
);
oldp2 =
old_bls.PointG2.BASE.multiply(
0x28b90deaf189015d3a325908c5e0e4bf00f84f7e639b056ff82d7e70b6eede4dn
);
old_bls.pairing(oldp1, oldp2);
},
noble: () => {
p1 =
bls.G1.ProjectivePoint.BASE.multiply(
0x28b90deaf189015d3a325908c5e0e4bf00f84f7e639b056ff82d7e70b6eede4cn
);
p2 =
bls.G2.ProjectivePoint.BASE.multiply(
0x28b90deaf189015d3a325908c5e0e4bf00f84f7e639b056ff82d7e70b6eede4dn
);
bls.pairing(p1, p2);
},
},
'getPublicKey (1-bit)': {
samples: 1000,
old: () => old_bls.getPublicKey('2'.padStart(64, '0')),
noble: () => bls.getPublicKey('2'.padStart(64, '0')),
},
getPublicKey: {
samples: 1000,
old: ({ priv }) => old_bls.getPublicKey(priv),
noble: ({ priv }) => bls.getPublicKey(priv),
},
sign: {
samples: 50,
old: ({ priv }) => old_bls.sign('09', priv),
noble: ({ priv }) => bls.sign('09', priv),
},
verify: {
samples: 50,
old: ({ pub }) =>
old_bls.verify(
'8647aa9680cd0cdf065b94e818ff2bb948cc97838bcee987b9bc1b76d0a0a6e0d85db4e9d75aaedfc79d4ea2733a21ae0579014de7636dd2943d45b87c82b1c66a289006b0b9767921bb8edd3f6c5c5dec0d54cd65f61513113c50cc977849e5',
'09',
pub
),
noble: ({ pub }) =>
bls.verify(
'8647aa9680cd0cdf065b94e818ff2bb948cc97838bcee987b9bc1b76d0a0a6e0d85db4e9d75aaedfc79d4ea2733a21ae0579014de7636dd2943d45b87c82b1c66a289006b0b9767921bb8edd3f6c5c5dec0d54cd65f61513113c50cc977849e5',
'09',
pub
),
},
pairing: {
samples: 100,
old: () => old_bls.pairing(oldp1, oldp2),
noble: () => bls.pairing(p1, p2),
},
// 'hashToCurve/G1': {
// samples: 500,
// old: () => old_bls.PointG1.hashToCurve('abcd'),
// noble: () => bls.hashToCurve.G1.hashToCurve('abcd'),
// },
// 'hashToCurve/G2': {
// samples: 200,
// old: () => old_bls.PointG2.hashToCurve('abcd'),
// noble: () => bls.hashToCurve.G2.hashToCurve('abcd'),
// },
// SLOW PART
// Requires points which we cannot init before (data fn same for all)
// await mark('sign/nc', 30, () => bls.sign(msgp, priv));
// await mark('verify/nc', 30, () => bls.verify(sigp, msgp, pubp));
'aggregatePublicKeys/8': {
samples: 100,
old: ({ pubs }) => old_bls.aggregatePublicKeys(pubs.slice(0, 8)),
noble: ({ pubs }) => bls.aggregatePublicKeys(pubs.slice(0, 8)),
},
'aggregatePublicKeys/32': {
samples: 50,
old: ({ pub32 }) => old_bls.aggregatePublicKeys(pub32.map(old_bls.PointG1.fromHex)),
noble: ({ pub32 }) => bls.aggregatePublicKeys(pub32.map(bls.G1.ProjectivePoint.fromHex)),
},
'aggregatePublicKeys/128': {
samples: 20,
old: ({ pub128 }) => old_bls.aggregatePublicKeys(pub128.map(old_bls.PointG1.fromHex)),
noble: ({ pub128 }) => bls.aggregatePublicKeys(pub128.map(bls.G1.ProjectivePoint.fromHex)),
},
'aggregatePublicKeys/512': {
samples: 10,
old: ({ pub512 }) => old_bls.aggregatePublicKeys(pub512.map(old_bls.PointG1.fromHex)),
noble: ({ pub512 }) => bls.aggregatePublicKeys(pub512.map(bls.G1.ProjectivePoint.fromHex)),
},
'aggregatePublicKeys/2048': {
samples: 5,
old: ({ pub2048 }) => old_bls.aggregatePublicKeys(pub2048.map(old_bls.PointG1.fromHex)),
noble: ({ pub2048 }) => bls.aggregatePublicKeys(pub2048.map(bls.G1.ProjectivePoint.fromHex)),
},
'aggregateSignatures/8': {
samples: 50,
old: ({ sigs }) => old_bls.aggregateSignatures(sigs.slice(0, 8)),
noble: ({ sigs }) => bls.aggregateSignatures(sigs.slice(0, 8)),
},
'aggregateSignatures/32': {
samples: 10,
old: ({ sig32 }) => old_bls.aggregateSignatures(sig32.map(old_bls.PointG2.fromSignature)),
noble: ({ sig32 }) => bls.aggregateSignatures(sig32.map(bls.Signature.decode)),
},
'aggregateSignatures/128': {
samples: 5,
old: ({ sig128 }) => old_bls.aggregateSignatures(sig128.map(old_bls.PointG2.fromSignature)),
noble: ({ sig128 }) => bls.aggregateSignatures(sig128.map(bls.Signature.decode)),
},
'aggregateSignatures/512': {
samples: 3,
old: ({ sig512 }) => old_bls.aggregateSignatures(sig512.map(old_bls.PointG2.fromSignature)),
noble: ({ sig512 }) => bls.aggregateSignatures(sig512.map(bls.Signature.decode)),
},
'aggregateSignatures/2048': {
samples: 2,
old: ({ sig2048 }) => old_bls.aggregateSignatures(sig2048.map(old_bls.PointG2.fromSignature)),
noble: ({ sig2048 }) => bls.aggregateSignatures(sig2048.map(bls.Signature.decode)),
},
},
};
const main = () =>
run(async () => {
for (const [name, curve] of Object.entries(CURVES)) {
console.log(`==== ${name} ====`);
const data = await curve.data();
for (const [fnName, libs] of Object.entries(curve)) {
if (fnName === 'data') continue;
const samples = libs.samples;
console.log(` - ${fnName} (samples: ${samples})`);
for (const [lib, fn] of Object.entries(libs)) {
if (lib === 'samples') continue;
if (ONLY_NOBLE && lib !== 'noble') continue;
await mark(` ${lib}`, samples, () => fn(data));
}
}
}
// Log current RAM
bench.logMem();
});
// ESM is broken.
import url from 'url';
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
main();
}

View File

@@ -12,11 +12,15 @@
"author": "", "author": "",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"micro-bmark": "0.2.0" "micro-bmark": "0.2.1"
}, },
"dependencies": { "dependencies": {
"@noble/bls12-381": "^1.4.0",
"@noble/ed25519": "^1.7.1", "@noble/ed25519": "^1.7.1",
"@noble/hashes": "^1.1.5",
"@noble/secp256k1": "^1.7.0", "@noble/secp256k1": "^1.7.0",
"@starkware-industries/starkware-crypto-utils": "^0.0.2" "@starkware-industries/starkware-crypto-utils": "^0.0.2",
"calculate-correlation": "^1.2.3",
"elliptic": "^6.5.4"
} }
} }

View File

@@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) 2022 Paul Miller (https://paulmillr.com)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the “Software”), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -1,28 +0,0 @@
# micro-curve-definitions
Elliptic curves implementations. `@noble/curves` is zero-dependency library for internal arithmetics.
`micro-curve-definitions` is the actual implementations. Current functionality:
- NIST curves: P192, P224, P256, P384, P521 (ECDSA)
- secp256k1 (ECDSA, without Schnorr)
- stark curve
- bn254
Pairings are not implemented.
## Usage
```sh
npm install micro-curve-definitions
```
```ts
import * as nist from 'micro-curve-definitions';
// P192, P224, P256, P384, P521, bn254
```
## License
MIT (c) Paul Miller [(https://paulmillr.com)](https://paulmillr.com), see LICENSE file.

View File

@@ -1,231 +0,0 @@
import * as bench from 'micro-bmark';
const { run, mark } = bench; // or bench.mark
// Curves
import { secp256k1 } from '../lib/secp256k1.js';
import { P256 } from '../lib/p256.js';
import { P384 } from '../lib/p384.js';
import { P521 } from '../lib/p521.js';
import { ed25519 } from '../lib/ed25519.js';
import { ed448 } from '../lib/ed448.js';
// Others
import { hmac } from '@noble/hashes/hmac';
import { sha256 } from '@noble/hashes/sha256';
import { sha512 } from '@noble/hashes/sha512';
import * as old_secp from '@noble/secp256k1';
import { concatBytes, hexToBytes } from '@noble/hashes/utils';
import * as starkwareCrypto from '@starkware-industries/starkware-crypto-utils';
import * as stark from '../lib/stark.js';
old_secp.utils.sha256Sync = (...msgs) =>
sha256
.create()
.update(concatBytes(...msgs))
.digest();
old_secp.utils.hmacSha256Sync = (key, ...msgs) =>
hmac
.create(sha256, key)
.update(concatBytes(...msgs))
.digest();
import * as noble_ed25519 from '@noble/ed25519';
noble_ed25519.utils.sha512Sync = (...m) => sha512(concatBytes(...m));
for (let item of [secp256k1, ed25519, ed448, P256, P384, P521, old_secp, noble_ed25519]) {
item.utils.precompute(8);
}
const ONLY_NOBLE = process.argv[2] === 'noble';
function generateData(namespace) {
const priv = namespace.utils.randomPrivateKey();
const pub = namespace.getPublicKey(priv);
const msg = namespace.utils.randomPrivateKey();
const sig = namespace.sign(msg, priv);
return { priv, pub, msg, sig };
}
export const CURVES = {
secp256k1: {
data: () => {
return generateData(secp256k1);
},
getPublicKey1: {
samples: 10000,
secp256k1_old: () => old_secp.getPublicKey(3n),
secp256k1: () => secp256k1.getPublicKey(3n),
},
getPublicKey255: {
samples: 10000,
secp256k1_old: () => old_secp.getPublicKey(2n**255n-1n),
secp256k1: () => secp256k1.getPublicKey(2n**255n-1n),
},
sign: {
samples: 5000,
secp256k1_old: ({ msg, priv }) => old_secp.signSync(msg, priv),
secp256k1: ({ msg, priv }) => secp256k1.sign(msg, priv),
},
verify: {
samples: 1000,
secp256k1_old: ({ sig, msg, pub }) => {
return old_secp.verify((new old_secp.Signature(sig.r, sig.s)), msg, pub);
},
secp256k1: ({ sig, msg, pub }) => secp256k1.verify(sig, msg, pub)
},
getSharedSecret: {
samples: 1000,
secp256k1_old: ({ pub, priv }) => old_secp.getSharedSecret(priv, pub),
secp256k1: ({ pub, priv }) => secp256k1.getSharedSecret(priv, pub),
},
recoverPublicKey: {
samples: 1000,
secp256k1_old: ({ sig, msg }) =>
old_secp.recoverPublicKey(msg, (new old_secp.Signature(sig.r, sig.s)), sig.recovery),
secp256k1: ({ sig, msg }) => sig.recoverPublicKey(msg)
}
},
ed25519: {
data: () => {
function to32Bytes(numOrStr) {
const hex = typeof numOrStr === 'string' ? numOrStr : numOrStr.toString(16);
return hexToBytes(hex.padStart(64, '0'));
}
const priv = to32Bytes(0x9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60n);
const pub = noble_ed25519.sync.getPublicKey(priv);
const msg = to32Bytes('deadbeefdeadbeefdeadbeefdeadbeefdeadbeef');
const sig = noble_ed25519.sync.sign(msg, priv);
return { pub, priv, msg, sig };
},
getPublicKey: {
samples: 10000,
old: () => noble_ed25519.sync.getPublicKey(noble_ed25519.utils.randomPrivateKey()),
noble: () => ed25519.getPublicKey(ed25519.utils.randomPrivateKey()),
},
sign: {
samples: 5000,
old: ({ msg, priv }) => noble_ed25519.sync.sign(msg, priv),
noble: ({ msg, priv }) => ed25519.sign(msg, priv),
},
verify: {
samples: 1000,
old: ({ sig, msg, pub }) => noble_ed25519.sync.verify(sig, msg, pub),
noble: ({ sig, msg, pub }) => ed25519.verify(sig, msg, pub),
},
},
ed448: {
data: () => {
const priv = ed448.utils.randomPrivateKey();
const pub = ed448.getPublicKey(priv);
const msg = ed448.utils.randomPrivateKey();
const sig = ed448.sign(msg, priv);
return { priv, pub, msg, sig };
},
getPublicKey: {
samples: 5000,
noble: () => ed448.getPublicKey(ed448.utils.randomPrivateKey()),
},
sign: {
samples: 2500,
noble: ({ msg, priv }) => ed448.sign(msg, priv),
},
verify: {
samples: 500,
noble: ({ sig, msg, pub }) => ed448.verify(sig, msg, pub)
}
},
nist: {
data: () => {
return { p256: generateData(P256), p384: generateData(P384), p521: generateData(P521) }
},
getPublicKey: {
samples: 2500,
P256: () => P256.getPublicKey(P256.utils.randomPrivateKey()),
P384: () => P384.getPublicKey(P384.utils.randomPrivateKey()),
P521: () => P521.getPublicKey(P521.utils.randomPrivateKey()),
},
sign: {
samples: 1000,
P256: ({ p256: {msg, priv} }) => P256.sign(msg, priv),
P384: ({ p384: {msg, priv} }) => P384.sign(msg, priv),
P521: ({ p521: {msg, priv} }) => P521.sign(msg, priv),
},
verify: {
samples: 250,
P256: ({ p256: {sig, msg, pub} }) => P256.verify(sig, msg, pub),
P384: ({ p384: {sig, msg, pub} }) => P384.verify(sig, msg, pub),
P521: ({ p521: {sig, msg, pub} }) => P521.verify(sig, msg, pub),
}
},
stark: {
data: () => {
const priv = '2dccce1da22003777062ee0870e9881b460a8b7eca276870f57c601f182136c';
const msg = 'c465dd6b1bbffdb05442eb17f5ca38ad1aa78a6f56bf4415bdee219114a47';
const pub = stark.getPublicKey(priv);
const sig = stark.sign(msg, priv);
const privateKey = '2dccce1da22003777062ee0870e9881b460a8b7eca276870f57c601f182136c';
const msgHash = 'c465dd6b1bbffdb05442eb17f5ca38ad1aa78a6f56bf4415bdee219114a47';
const keyPair = starkwareCrypto.default.ec.keyFromPrivate(privateKey, 'hex');
const publicKeyStark = starkwareCrypto.default.ec.keyFromPublic(
keyPair.getPublic(true, 'hex'), 'hex'
);
return { priv, sig, msg, pub, publicKeyStark, msgHash, keyPair }
},
pedersen: {
samples: 500,
old: () => {
return starkwareCrypto.default.pedersen([
'3d937c035c878245caf64531a5756109c53068da139362728feb561405371cb',
'208a0a10250e382e1e4bbe2880906c2791bf6275695e02fbbc6aeff9cd8b31a',
])
},
noble: () => {
return stark.pedersen(
'3d937c035c878245caf64531a5756109c53068da139362728feb561405371cb',
'208a0a10250e382e1e4bbe2880906c2791bf6275695e02fbbc6aeff9cd8b31a'
)
}
},
verify: {
samples: 500,
old: ({ publicKeyStark, msgHash, keyPair }) => {
return starkwareCrypto.default.verify(
publicKeyStark,
msgHash,
starkwareCrypto.default.sign(keyPair, msgHash)
);
},
noble: ({ priv, msg, pub }) => {
return stark.verify(stark.sign(msg, priv), msg, pub)
}
}
}
};
const main = () =>
run(async () => {
for (const [name, curve] of Object.entries(CURVES)) {
console.log(`==== ${name} ====`);
const data = curve.data();
for (const [fnName, libs] of Object.entries(curve)) {
if (fnName === 'data') continue;
const samples = libs.samples;
console.log(` - ${fnName} (samples: ${samples})`);
for (const [lib, fn] of Object.entries(libs)) {
if (lib === 'samples') continue;
if (ONLY_NOBLE && lib !== 'noble') continue;
await mark(` ${lib}`, samples, () => fn(data));
}
}
}
// Log current RAM
bench.logMem();
});
// ESM is broken.
import url from 'url';
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
main();
}

View File

@@ -1,59 +0,0 @@
{
"name": "micro-curve-definitions",
"version": "0.2.0",
"description": "Curve definitions for @noble/curves",
"files": [
"lib"
],
"type": "module",
"main": "lib/index.js",
"module": "lib/index.js",
"types": "lib/index.d.ts",
"dependencies": {
"@noble/curves": "0.2.0",
"@noble/hashes": "1.1.5"
},
"devDependencies": {
"@scure/base": "~1.1.0",
"@scure/bip32": "^1.1.1",
"@scure/bip39": "^1.1.0",
"@types/node": "18.11.3",
"fast-check": "3.0.0",
"micro-should": "0.2.0",
"prettier": "2.6.2",
"typescript": "4.7.3"
},
"author": "Paul Miller (https://paulmillr.com)",
"license": "MIT",
"homepage": "https://github.com/paulmillr/noble-curves",
"repository": {
"type": "git",
"url": "git+https://github.com/paulmillr/noble-curves.git"
},
"scripts": {
"build": "tsc",
"lint": "prettier --check src",
"test": "node test/index.test.js"
},
"keywords": [
"secp192r1",
"secp224r1",
"secp256r1",
"secp384r1",
"secp521r1",
"NIST P192",
"NIST P224",
"NIST P256",
"NIST P384",
"NIST P521",
"NIST curves",
"EC",
"elliptic curves"
],
"funding": [
{
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
]
}

View File

@@ -1,146 +0,0 @@
/*! @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 '@noble/curves/edwards';
import { mod, pow2 } from '@noble/curves/modular';
import { montgomery } from '../../lib/montgomery.js';
/**
* Edwards448 (not Ed448-Goldilocks) curve with following addons:
* * X448 ECDH
* Conforms to RFC 8032 https://www.rfc-editor.org/rfc/rfc8032.html#section-5.2
*/
const shake256_114 = wrapConstructor(() => shake256.create({ dkLen: 114 }));
const shake256_64 = wrapConstructor(() => shake256.create({ dkLen: 64 }));
const ed448P = BigInt(
'726838724295606890549323807888004534353641360687318060281490199180612328166730772686396383698676545930088884461843637361053498018365439'
);
// powPminus3div4 calculates z = x^k mod p, where k = (p-3)/4.
function ed448_pow_Pminus3div4(x: bigint): bigint {
const P = ed448P;
// prettier-ignore
let [_1n, _2n, _3n, _11n, _22n, _44n, _88n, _223n] = [1, 2, 3, 11, 22, 44, 88, 223]
.map(n => BigInt(n));
// x ** ((P - 3n)/4n) % P
// [223 of 1, 0, 222 of 1], almost same as secp!
const b2 = (x * x * x) % P;
const b3 = (b2 * b2 * x) % P;
const b6 = (pow2(b3, _3n, P) * b3) % P;
const b9 = (pow2(b6, _3n, P) * b3) % P;
const b11 = (pow2(b9, _2n, P) * b2) % P;
const b22 = (pow2(b11, _11n, P) * b11) % P;
const b44 = (pow2(b22, _22n, P) * b22) % P;
const b88 = (pow2(b44, _44n, P) * b44) % P;
const b176 = (pow2(b88, _88n, P) * b88) % P;
const b220 = (pow2(b176, _44n, P) * b44) % P;
const b222 = (pow2(b220, _2n, P) * b2) % P;
const b223 = (pow2(b222, _1n, P) * x) % P;
return (pow2(b223, _223n, P) * b222) % P;
}
function adjustScalarBytes(bytes: Uint8Array): Uint8Array {
// Section 5: Likewise, for X448, set the two least significant bits of the first byte to 0, and the most
// significant bit of the last byte to 1.
bytes[0] &= 252; // 0b11111100
// and the most significant bit of the last byte to 1.
bytes[55] |= 128; // 0b10000000
// NOTE: is is NOOP for 56 bytes scalars (X25519/X448)
bytes[56] = 0; // Byte outside of group (456 buts vs 448 bits)
return bytes;
}
const ED448_DEF = {
// Param: a
a: BigInt(1),
// -39081. Negative number is P - number
d: BigInt(
'726838724295606890549323807888004534353641360687318060281490199180612328166730772686396383698676545930088884461843637361053498018326358'
),
// Finite field 𝔽p over which we'll do calculations; 2n ** 448n - 2n ** 224n - 1n
P: ed448P,
// Subgroup order: how many points ed448 has; 2n**446n - 13818066809895115352007386748515426880336692474882178609894547503885n
n: BigInt(
'181709681073901722637330951972001133588410340171829515070372549795146003961539585716195755291692375963310293709091662304773755859649779'
),
nBitLength: 456,
// Cofactor
h: BigInt(4),
// Base point (x, y) aka generator point
Gx: BigInt(
'224580040295924300187604334099896036246789641632564134246125461686950415467406032909029192869357953282578032075146446173674602635247710'
),
Gy: BigInt(
'298819210078481492676017930443930673437544040154080242095928241372331506189835876003536878655418784733982303233503462500531545062832660'
),
// SHAKE256(dom4(phflag,context)||x, 114)
hash: shake256_114,
randomBytes,
adjustScalarBytes,
// dom4
domain: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => {
if (ctx.length > 255) throw new Error(`Context is too big: ${ctx.length}`);
return concatBytes(
utf8ToBytes('SigEd448'),
new Uint8Array([phflag ? 1 : 0, ctx.length]),
ctx,
data
);
},
// 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:
// (p+1)/4 3 (p-3)/4
// x = (u/v) = u v (u^5 v^3) (mod p)
const u2v = mod(u * u * v, P);
const u3v = mod(u2v * u, P); // u^2v
const u5v3 = mod(u3v * u2v * v, P); // u^5v^3
const root = ed448_pow_Pminus3div4(u5v3);
const x = mod(u3v * root, P);
// Verify that root is exists
const x2 = mod(x * x, P); // x^2
// If v * x^2 = u, the recovered x-coordinate is x. Otherwise, no
// square root exists, and the decoding fails.
return { isValid: mod(x2 * v, P) === u, value: x };
},
} as const;
export const ed448 = 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 x448 = montgomery({
a24: BigInt(39081),
montgomeryBits: 448,
nByteLength: 57,
P: ed448P,
Gu: '0500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
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,
// 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);
// },
});

View File

@@ -1,24 +0,0 @@
/*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
import { createCurve } from './_shortw_utils.js';
import { sha256 } from '@noble/hashes/sha256';
// NIST secp256r1 aka P256
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/nist/P-256
export const P256 = createCurve(
{
// Params: a, b
a: BigInt('0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc'),
b: BigInt('0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b'),
// Field over which we'll do calculations; 2n**224n * (2n**32n-1n) + 2n**192n + 2n**96n-1n
P: BigInt('0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff'),
// 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;

View File

@@ -1,22 +0,0 @@
/*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
import { createCurve } from './_shortw_utils.js';
import { sha384 } from '@noble/hashes/sha512';
// NIST secp384r1 aka P384
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/nist/P-384
// prettier-ignore
export const P384 = createCurve({
// Params: a, b
a: BigInt('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000fffffffc'),
b: BigInt('0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef'),
// Field over which we'll do calculations. 2n**384n - 2n**128n - 2n**96n + 2n**32n - 1n
P: BigInt('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff'),
// 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;

View File

@@ -1,23 +0,0 @@
/*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
import { createCurve } from './_shortw_utils.js';
import { sha512 } from '@noble/hashes/sha512';
// 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
// prettier-ignore
export const P521 = createCurve({
// Params: a, b
a: BigInt('0x01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc'),
b: BigInt('0x0051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00'),
// Field over which we'll do calculations; 2n**521n - 1n
P: BigInt('0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'),
// Curve order, total count of valid points in the field
n: BigInt('0x01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409'),
// Base point (x, y) aka generator point
Gx: BigInt('0x00c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66'),
Gy: BigInt('0x011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650'),
h: BigInt(1),
lowS: false,
} as const, sha512);
export const secp521r1 = P521;

View File

@@ -1,262 +0,0 @@
/*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
import { sha256 } from '@noble/hashes/sha256';
import { mod, pow2 } from '@noble/curves/modular';
import { createCurve } from './_shortw_utils.js';
import { PointType } from '@noble/curves/weierstrass';
import {
ensureBytes,
concatBytes,
Hex,
hexToBytes,
bytesToNumberBE,
PrivKey,
} from '@noble/curves/utils';
import { randomBytes } from '@noble/hashes/utils';
/**
* secp256k1 belongs to Koblitz curves: it has
* efficiently computable Frobenius endomorphism.
* Endomorphism improves efficiency:
* Uses 2x less RAM, speeds up precomputation by 2x and ECDH / sign key recovery by 20%.
* Should always be used for Jacobian's double-and-add multiplication.
* For affines cached multiplication, it trades off 1/2 init time & 1/3 ram for 20% perf hit.
* https://gist.github.com/paulmillr/eb670806793e84df628a7c434a873066
*/
const secp256k1P = BigInt('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f');
const secp256k1N = BigInt('0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141');
const _1n = BigInt(1);
const _2n = BigInt(2);
const divNearest = (a: bigint, b: bigint) => (a + b / _2n) / b;
/**
* Allows to compute square root √y 2x faster.
* To calculate √y, we need to exponentiate it to a very big number:
* `y² = x³ + ax + b; y = y² ^ (p+1)/4`
* We are unwrapping the loop and multiplying it bit-by-bit.
* (P+1n/4n).toString(2) would produce bits [223x 1, 0, 22x 1, 4x 0, 11, 00]
*/
// prettier-ignore
function sqrtMod(y: bigint): bigint {
const P = secp256k1P;
const _3n = BigInt(3), _6n = BigInt(6), _11n = BigInt(11); const _22n = BigInt(22);
const _23n = BigInt(23), _44n = BigInt(44), _88n = BigInt(88);
const b2 = (y * y * y) % P; // x^3, 11
const b3 = (b2 * b2 * y) % P; // x^7
const b6 = (pow2(b3, _3n, P) * b3) % P;
const b9 = (pow2(b6, _3n, P) * b3) % P;
const b11 = (pow2(b9, _2n, P) * b2) % P;
const b22 = (pow2(b11, _11n, P) * b11) % P;
const b44 = (pow2(b22, _22n, P) * b22) % P;
const b88 = (pow2(b44, _44n, P) * b44) % P;
const b176 = (pow2(b88, _88n, P) * b88) % P;
const b220 = (pow2(b176, _44n, P) * b44) % P;
const b223 = (pow2(b220, _3n, P) * b3) % P;
const t1 = (pow2(b223, _23n, P) * b22) % P;
const t2 = (pow2(t1, _6n, P) * b2) % P;
return pow2(t2, _2n, P);
}
export const secp256k1 = createCurve(
{
// Params: a, b
// Seem to be rigid https://bitcointalk.org/index.php?topic=289795.msg3183975#msg3183975
a: BigInt(0),
b: BigInt(7),
// Field over which we'll do calculations;
// 2n**256n - 2n**32n - 2n**9n - 2n**8n - 2n**7n - 2n**6n - 2n**4n - 1n
P: secp256k1P,
// Curve order, total count of valid points in the field
n: secp256k1N,
// Base point (x, y) aka generator point
Gx: BigInt('55066263022277343669578718895168534326250603453777594175500187360389116729240'),
Gy: BigInt('32670510020758816978083085130507043184471273380659243275938904335757337482424'),
h: BigInt(1),
// Alllow only low-S signatures by default in sign() and verify()
lowS: true,
sqrtMod,
endo: {
// Params taken from https://gist.github.com/paulmillr/eb670806793e84df628a7c434a873066
beta: BigInt('0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee'),
splitScalar: (k: bigint) => {
const n = secp256k1N;
const a1 = BigInt('0x3086d221a7d46bcde86c90e49284eb15');
const b1 = -_1n * BigInt('0xe4437ed6010e88286f547fa90abfe4c3');
const a2 = BigInt('0x114ca50f7a8e2f3f657c1108d9d44cfd8');
const b2 = a1;
const POW_2_128 = BigInt('0x100000000000000000000000000000000');
const c1 = divNearest(b2 * k, n);
const c2 = divNearest(-b1 * k, n);
let k1 = mod(k - c1 * a1 - c2 * a2, n);
let k2 = mod(-c1 * b1 - c2 * b2, n);
const k1neg = k1 > POW_2_128;
const k2neg = k2 > POW_2_128;
if (k1neg) k1 = n - k1;
if (k2neg) k2 = n - k2;
if (k1 > POW_2_128 || k2 > POW_2_128) {
throw new Error('splitScalar: Endomorphism failed, k=' + k);
}
return { k1neg, k1, k2neg, k2 };
},
},
},
sha256
);
// Schnorr
const _0n = BigInt(0);
const numTo32b = secp256k1.utils._bigintToBytes;
const numTo32bStr = secp256k1.utils._bigintToString;
const normalizePrivateKey = secp256k1.utils._normalizePrivateKey;
// TODO: export?
function normalizePublicKey(publicKey: Hex | PointType): PointType {
if (publicKey instanceof secp256k1.Point) {
publicKey.assertValidity();
return publicKey;
} else {
const bytes = ensureBytes(publicKey);
// Schnorr is 32 bytes
if (bytes.length === 32) {
const x = bytesToNumberBE(bytes);
if (!isValidFieldElement(x)) throw new Error('Point is not on curve');
const y2 = secp256k1.utils._weierstrassEquation(x); // y² = x³ + ax + b
let y = sqrtMod(y2); // y = y² ^ (p+1)/4
const isYOdd = (y & _1n) === _1n;
// Schnorr
if (isYOdd) y = mod(-y, secp256k1.CURVE.P);
const point = new secp256k1.Point(x, y);
point.assertValidity();
return point;
}
// Do we need that in schnorr at all?
return secp256k1.Point.fromHex(publicKey);
}
}
const isWithinCurveOrder = secp256k1.utils._isWithinCurveOrder;
const isValidFieldElement = secp256k1.utils._isValidFieldElement;
const TAGS = {
challenge: 'BIP0340/challenge',
aux: 'BIP0340/aux',
nonce: 'BIP0340/nonce',
} as const;
/** An object mapping tags to their tagged hash prefix of [SHA256(tag) | SHA256(tag)] */
const TAGGED_HASH_PREFIXES: { [tag: string]: Uint8Array } = {};
export function taggedHash(tag: string, ...messages: Uint8Array[]): Uint8Array {
let tagP = TAGGED_HASH_PREFIXES[tag];
if (tagP === undefined) {
const tagH = sha256(Uint8Array.from(tag, (c) => c.charCodeAt(0)));
tagP = concatBytes(tagH, tagH);
TAGGED_HASH_PREFIXES[tag] = tagP;
}
return sha256(concatBytes(tagP, ...messages));
}
const toRawX = (point: PointType) => point.toRawBytes(true).slice(1);
// Schnorr signatures are superior to ECDSA from above.
// Below is Schnorr-specific code as per BIP0340.
function schnorrChallengeFinalize(ch: Uint8Array): bigint {
return mod(bytesToNumberBE(ch), secp256k1.CURVE.n);
}
// Do we need this at all for Schnorr?
class SchnorrSignature {
constructor(readonly r: bigint, readonly s: bigint) {
this.assertValidity();
}
static fromHex(hex: Hex) {
const bytes = ensureBytes(hex);
if (bytes.length !== 64)
throw new TypeError(`SchnorrSignature.fromHex: expected 64 bytes, not ${bytes.length}`);
const r = bytesToNumberBE(bytes.subarray(0, 32));
const s = bytesToNumberBE(bytes.subarray(32, 64));
return new SchnorrSignature(r, s);
}
assertValidity() {
const { r, s } = this;
if (!isValidFieldElement(r) || !isWithinCurveOrder(s)) throw new Error('Invalid signature');
}
toHex(): string {
return numTo32bStr(this.r) + numTo32bStr(this.s);
}
toRawBytes(): Uint8Array {
return hexToBytes(this.toHex());
}
}
function schnorrGetScalar(priv: bigint) {
const point = secp256k1.Point.fromPrivateKey(priv);
const scalar = point.hasEvenY() ? priv : secp256k1.CURVE.n - priv;
return { point, scalar, x: toRawX(point) };
}
/**
* Synchronously creates Schnorr signature. Improved security: verifies itself before
* producing an output.
* @param msg message (not message hash)
* @param privateKey private key
* @param auxRand random bytes that would be added to k. Bad RNG won't break it.
*/
function schnorrSign(
message: Hex,
privateKey: PrivKey,
auxRand: Hex = randomBytes(32)
): Uint8Array {
if (message == null) throw new TypeError(`sign: Expected valid message, not "${message}"`);
const m = ensureBytes(message);
// checks for isWithinCurveOrder
const { x: px, scalar: d } = schnorrGetScalar(normalizePrivateKey(privateKey));
const rand = ensureBytes(auxRand);
if (rand.length !== 32) throw new TypeError('sign: Expected 32 bytes of aux randomness');
const tag = taggedHash;
const t0h = tag(TAGS.aux, rand);
const t = numTo32b(d ^ bytesToNumberBE(t0h));
const k0h = tag(TAGS.nonce, t, px, m);
const k0 = mod(bytesToNumberBE(k0h), secp256k1.CURVE.n);
if (k0 === _0n) throw new Error('sign: Creation of signature failed. k is zero');
const { point: R, x: rx, scalar: k } = schnorrGetScalar(k0);
const e = schnorrChallengeFinalize(tag(TAGS.challenge, rx, px, m));
const sig = new SchnorrSignature(R.x, mod(k + e * d, secp256k1.CURVE.n)).toRawBytes();
if (!schnorrVerify(sig, m, px)) throw new Error('sign: Invalid signature produced');
return sig;
}
/**
* Verifies Schnorr signature synchronously.
*/
function schnorrVerify(signature: Hex, message: Hex, publicKey: Hex): boolean {
try {
const raw = signature instanceof SchnorrSignature;
const sig: SchnorrSignature = raw ? signature : SchnorrSignature.fromHex(signature);
if (raw) sig.assertValidity(); // just in case
const { r, s } = sig;
const m = ensureBytes(message);
const P = normalizePublicKey(publicKey);
const e = schnorrChallengeFinalize(taggedHash(TAGS.challenge, numTo32b(r), toRawX(P), m));
// Finalize
// R = s⋅G - e⋅P
// -eP == (n-e)P
const R = secp256k1.Point.BASE.multiplyAndAddUnsafe(
P,
normalizePrivateKey(s),
mod(-e, secp256k1.CURVE.n)
);
if (!R || !R.hasEvenY() || R.x !== r) return false;
return true;
} catch (error) {
return false;
}
}
export const schnorr = {
Signature: SchnorrSignature,
// Schnorr's pubkey is just `x` of Point (BIP340)
getPublicKey: (privateKey: PrivKey): Uint8Array =>
toRawX(secp256k1.Point.fromPrivateKey(privateKey)),
sign: schnorrSign,
verify: schnorrVerify,
};

View File

@@ -1,317 +0,0 @@
import { deepStrictEqual, throws } from 'assert';
import { should } from 'micro-should';
import * as fc from 'fast-check';
import * as mod from '@noble/curves/modular';
import { randomBytes } from '@noble/hashes/utils';
// Generic tests for all curves in package
import { secp192r1 } from '../lib/p192.js';
import { secp224r1 } from '../lib/p224.js';
import { secp256r1 } from '../lib/p256.js';
import { secp384r1 } from '../lib/p384.js';
import { secp521r1 } from '../lib/p521.js';
import { secp256k1 } from '../lib/secp256k1.js';
import { ed25519, ed25519ctx, ed25519ph } from '../lib/ed25519.js';
import { ed448, ed448ph } from '../lib/ed448.js';
import { starkCurve } from '../lib/stark.js';
import { pallas, vesta } from '../lib/pasta.js';
import { bn254 } from '../lib/bn.js';
import { jubjub } from '../lib/jubjub.js';
// prettier-ignore
const CURVES = {
secp192r1, secp224r1, secp256r1, secp384r1, secp521r1,
secp256k1,
ed25519, ed25519ctx, ed25519ph,
ed448, ed448ph,
starkCurve,
pallas, vesta,
bn254,
jubjub,
};
const NUM_RUNS = 5;
const getXY = (p) => ({ x: p.x, y: p.y });
function equal(a, b, comment) {
deepStrictEqual(a.equals(b), true, `eq(${comment})`);
if (a.toAffine && b.toAffine) {
deepStrictEqual(getXY(a.toAffine()), getXY(b.toAffine()), `eqToAffine(${comment})`);
} else if (!a.toAffine && !b.toAffine) {
// Already affine
deepStrictEqual(getXY(a), getXY(b), `eqAffine(${comment})`);
} else throw new Error('Different point types');
}
for (const name in CURVES) {
const C = CURVES[name];
const CURVE_ORDER = C.CURVE.n;
const FC_BIGINT = fc.bigInt(1n + 1n, CURVE_ORDER - 1n);
// Check that curve doesn't accept points from other curves
const O = name === 'secp256k1' ? secp256r1 : secp256k1;
const POINTS = {};
const OTHER_POINTS = {};
for (const name of ['Point', 'JacobianPoint', 'ExtendedPoint', 'ProjectivePoint']) {
POINTS[name] = C[name];
OTHER_POINTS[name] = O[name];
}
for (const pointName in POINTS) {
const p = POINTS[pointName];
const o = OTHER_POINTS[pointName];
if (!p) continue;
const G = [p.ZERO, p.BASE];
for (let i = 2; i < 10; i++) G.push(G[1].multiply(i));
// Here we check basic group laws, to verify that points works as group
should(`${name}/${pointName}/Basic group laws (zero)`, () => {
equal(G[0].double(), G[0], '(0*G).double() = 0');
equal(G[0].add(G[0]), G[0], '0*G + 0*G = 0');
equal(G[0].subtract(G[0]), G[0], '0*G - 0*G = 0');
equal(G[0].negate(), G[0], '-0 = 0');
for (let i = 0; i < G.length; i++) {
const p = G[i];
equal(p, p.add(G[0]), `${i}*G + 0 = ${i}*G`);
equal(G[0].multiply(i + 1), G[0], `${i + 1}*0 = 0`);
}
});
should(`${name}/${pointName}/Basic group laws (one)`, () => {
equal(G[1].double(), G[2], '(1*G).double() = 2*G');
equal(G[1].subtract(G[1]), G[0], '1*G - 1*G = 0');
equal(G[1].add(G[1]), G[2], '1*G + 1*G = 2*G');
});
should(`${name}/${pointName}/Basic group laws (sanity tests)`, () => {
equal(G[2].double(), G[4], `(2*G).double() = 4*G`);
equal(G[2].add(G[2]), G[4], `2*G + 2*G = 4*G`);
equal(G[7].add(G[3].negate()), G[4], `7*G - 3*G = 4*G`);
});
should(`${name}/${pointName}/Basic group laws (addition commutativity)`, () => {
equal(G[4].add(G[3]), G[3].add(G[4]), `4*G + 3*G = 3*G + 4*G`);
equal(G[4].add(G[3]), G[3].add(G[2]).add(G[2]), `4*G + 3*G = 3*G + 2*G + 2*G`);
});
should(`${name}/${pointName}/Basic group laws (double)`, () => {
equal(G[3].double(), G[6], '(3*G).double() = 6*G');
});
should(`${name}/${pointName}/Basic group laws (multiply)`, () => {
equal(G[2].multiply(3), G[6], '(2*G).multiply(3) = 6*G');
});
should(`${name}/${pointName}/Basic group laws (same point addition)`, () => {
equal(G[3].add(G[3]), G[6], `3*G + 3*G = 6*G`);
});
should(`${name}/${pointName}/Basic group laws (same point (negative) addition)`, () => {
equal(G[3].add(G[3].negate()), G[0], '3*G + (- 3*G) = 0*G');
equal(G[3].subtract(G[3]), G[0], '3*G - 3*G = 0*G');
});
should(`${name}/${pointName}/Basic group laws (curve order)`, () => {
equal(G[1].multiply(CURVE_ORDER - 1n).add(G[1]), G[0], '(N-1)*G + G = 0');
equal(G[1].multiply(CURVE_ORDER - 1n).add(G[2]), G[1], '(N-1)*G + 2*G = 1*G');
equal(G[1].multiply(CURVE_ORDER - 2n).add(G[2]), G[0], '(N-2)*G + 2*G = 0');
const half = CURVE_ORDER / 2n;
const carry = CURVE_ORDER % 2n === 1n ? G[1] : G[0];
equal(G[1].multiply(half).double().add(carry), G[0], '((N/2) * G).double() = 0');
});
should(`${name}/${pointName}/Basic group laws (inversion)`, () => {
const a = 1234n;
const b = 5678n;
const c = a * b;
equal(G[1].multiply(a).multiply(b), G[1].multiply(c), 'a*b*G = c*G');
const inv = mod.invert(b, CURVE_ORDER);
equal(G[1].multiply(c).multiply(inv), G[1].multiply(a), 'c*G * (1/b)*G = a*G');
});
should(`${name}/${pointName}/Basic group laws (multiply, rand)`, () =>
fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, (a, b) => {
const c = mod.mod(a + b, CURVE_ORDER);
if (c === CURVE_ORDER || c < 1n) return;
const pA = G[1].multiply(a);
const pB = G[1].multiply(b);
const pC = G[1].multiply(c);
equal(pA.add(pB), pB.add(pA), `pA + pB = pB + pA`);
equal(pA.add(pB), pC, `pA + pB = pC`);
}),
{ numRuns: NUM_RUNS }
)
);
should(`${name}/${pointName}/Basic group laws (multiply2, rand)`, () =>
fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, (a, b) => {
const c = mod.mod(a * b, CURVE_ORDER);
const pA = G[1].multiply(a);
const pB = G[1].multiply(b);
equal(pA.multiply(b), pB.multiply(a), `b*pA = a*pB`);
equal(pA.multiply(b), G[1].multiply(c), `b*pA = c*G`);
}),
{ numRuns: NUM_RUNS }
)
);
for (const op of ['add', 'subtract']) {
should(`${name}/${pointName}/${op} type check`, () => {
throws(() => G[1][op](0), '0');
throws(() => G[1][op](0n), '0n');
G[1][op](G[2]);
throws(() => G[1][op](CURVE_ORDER), 'CURVE_ORDER');
throws(() => G[1][op](123.456), '123.456');
throws(() => G[1][op](true), 'true');
throws(() => G[1][op]('1'), "'1'");
throws(() => G[1][op]({ x: 1n, y: 1n, z: 1n, t: 1n }), '{ x: 1n, y: 1n, z: 1n, t: 1n }');
throws(() => G[1][op](new Uint8Array([])), 'ui8a([])');
throws(() => G[1][op](new Uint8Array([0])), 'ui8a([0])');
throws(() => G[1][op](new Uint8Array([1])), 'ui8a([1])');
throws(() => G[1][op](new Uint8Array(4096).fill(1)), 'ui8a(4096*[1])');
if (G[1].toAffine) throws(() => G[1][op](C.Point.BASE), `Point ${op} ${pointName}`);
throws(() => G[1][op](o.BASE), `${op}/other curve point`);
});
}
should(`${name}/${pointName}/equals type check`, () => {
throws(() => G[1].equals(0), '0');
throws(() => G[1].equals(0n), '0n');
deepStrictEqual(G[1].equals(G[2]), false, '1*G != 2*G');
deepStrictEqual(G[1].equals(G[1]), true, '1*G == 1*G');
deepStrictEqual(G[2].equals(G[2]), true, '2*G == 2*G');
throws(() => G[1].equals(CURVE_ORDER), 'CURVE_ORDER');
throws(() => G[1].equals(123.456), '123.456');
throws(() => G[1].equals(true), 'true');
throws(() => G[1].equals('1'), "'1'");
throws(() => G[1].equals({ x: 1n, y: 1n, z: 1n, t: 1n }), '{ x: 1n, y: 1n, z: 1n, t: 1n }');
throws(() => G[1].equals(new Uint8Array([])), 'ui8a([])');
throws(() => G[1].equals(new Uint8Array([0])), 'ui8a([0])');
throws(() => G[1].equals(new Uint8Array([1])), 'ui8a([1])');
throws(() => G[1].equals(new Uint8Array(4096).fill(1)), 'ui8a(4096*[1])');
if (G[1].toAffine) throws(() => G[1].equals(C.Point.BASE), `Point.equals(${pointName})`);
throws(() => G[1].equals(o.BASE), 'other curve point');
});
for (const op of ['multiply', 'multiplyUnsafe']) {
if (!p.BASE[op]) continue;
should(`${name}/${pointName}/${op} type check`, () => {
if (op !== 'multiplyUnsafe') {
throws(() => G[1][op](0), '0');
throws(() => G[1][op](0n), '0n');
}
G[1][op](1n);
G[1][op](CURVE_ORDER - 1n);
throws(() => G[1][op](G[2]), 'G[2]');
throws(() => G[1][op](CURVE_ORDER), 'CURVE_ORDER');
throws(() => G[1][op](CURVE_ORDER + 1n), 'CURVE_ORDER+1');
throws(() => G[1][op](123.456), '123.456');
throws(() => G[1][op](true), 'true');
throws(() => G[1][op]('1'), '1');
throws(() => G[1][op](new Uint8Array([])), 'ui8a([])');
throws(() => G[1][op](new Uint8Array([0])), 'ui8a([0])');
throws(() => G[1][op](new Uint8Array([1])), 'ui8a([1])');
throws(() => G[1][op](new Uint8Array(4096).fill(1)), 'ui8a(4096*[1])');
throws(() => G[1][op](o.BASE), 'other curve point');
});
}
// Complex point (Extended/Jacobian/Projective?)
if (p.BASE.toAffine) {
should(`${name}/${pointName}/toAffine()`, () => {
equal(p.ZERO.toAffine(), C.Point.ZERO, `0 = 0`);
equal(p.BASE.toAffine(), C.Point.BASE, `1 = 1`);
});
}
if (p.fromAffine) {
should(`${name}/${pointName}/fromAffine()`, () => {
equal(p.ZERO, p.fromAffine(C.Point.ZERO), `0 = 0`);
equal(p.BASE, p.fromAffine(C.Point.BASE), `1 = 1`);
});
}
// toHex/fromHex (if available)
if (p.fromHex && p.BASE.toHex) {
should(`${name}/${pointName}/fromHex(toHex()) roundtrip`, () => {
fc.assert(
fc.property(FC_BIGINT, (x) => {
const hex = p.BASE.multiply(x).toHex();
deepStrictEqual(p.fromHex(hex).toHex(), hex);
})
);
});
}
}
// Generic complex things (getPublicKey/sign/verify/getSharedSecret)
should(`${name}/getPublicKey type check`, () => {
throws(() => C.getPublicKey(0), '0');
throws(() => C.getPublicKey(0n), '0n');
throws(() => C.getPublicKey(false), 'false');
throws(() => C.getPublicKey(123.456), '123.456');
throws(() => C.getPublicKey(true), 'true');
throws(() => C.getPublicKey(''), "''");
// NOTE: passes because of disabled hex padding checks for starknet, maybe enable?
//throws(() => C.getPublicKey('1'), "'1'");
throws(() => C.getPublicKey('key'), "'key'");
throws(() => C.getPublicKey(new Uint8Array([])));
throws(() => C.getPublicKey(new Uint8Array([0])));
throws(() => C.getPublicKey(new Uint8Array([1])));
throws(() => C.getPublicKey(new Uint8Array(4096).fill(1)));
});
should(`${name}.verify()/should verify random signatures`, () =>
fc.assert(
fc.property(fc.hexaString({ minLength: 64, maxLength: 64 }), (msg) => {
const priv = C.utils.randomPrivateKey();
const pub = C.getPublicKey(priv);
const sig = C.sign(msg, priv);
deepStrictEqual(C.verify(sig, msg, pub), true);
}),
{ numRuns: NUM_RUNS }
)
);
should(`${name}.sign()/edge cases`, () => {
throws(() => C.sign());
throws(() => C.sign(''));
});
should(`${name}.verify()/should not verify signature with wrong hash`, () => {
const MSG = '01'.repeat(32);
const PRIV_KEY = 0x2n;
const WRONG_MSG = '11'.repeat(32);
const signature = C.sign(MSG, PRIV_KEY);
const publicKey = C.getPublicKey(PRIV_KEY);
deepStrictEqual(C.verify(signature, WRONG_MSG, publicKey), false);
});
// NOTE: fails for ed, because of empty message. Since we convert it to scalar,
// need to check what other implementations do. Empty message != new Uint8Array([0]), but what scalar should be in that case?
// should(`${name}/should not verify signature with wrong message`, () => {
// fc.assert(
// fc.property(
// fc.array(fc.integer({ min: 0x00, max: 0xff })),
// fc.array(fc.integer({ min: 0x00, max: 0xff })),
// (bytes, wrongBytes) => {
// const privKey = C.utils.randomPrivateKey();
// const message = new Uint8Array(bytes);
// const wrongMessage = new Uint8Array(wrongBytes);
// const publicKey = C.getPublicKey(privKey);
// const signature = C.sign(message, privKey);
// deepStrictEqual(
// C.verify(signature, wrongMessage, publicKey),
// bytes.toString() === wrongBytes.toString()
// );
// }
// ),
// { numRuns: NUM_RUNS }
// );
// });
if (C.getSharedSecret) {
should(`${name}/getSharedSecret() should be commutative`, () => {
for (let i = 0; i < NUM_RUNS; i++) {
const asec = C.utils.randomPrivateKey();
const apub = C.getPublicKey(asec);
const bsec = C.utils.randomPrivateKey();
const bpub = C.getPublicKey(bsec);
try {
deepStrictEqual(C.getSharedSecret(asec, bpub), C.getSharedSecret(bsec, apub));
} catch (error) {
console.error('not commutative', { asec, apub, bsec, bpub });
throw error;
}
}
});
}
}
// ESM is broken.
import url from 'url';
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
should.run();
}

View File

@@ -1,657 +0,0 @@
import { deepStrictEqual, throws } from 'assert';
import { should } from 'micro-should';
import * as fc from 'fast-check';
import { ed25519, ed25519ctx, ed25519ph, x25519, RistrettoPoint } from '../lib/ed25519.js';
import { readFileSync } from 'fs';
import { default as zip215 } from './ed25519/zip215.json' assert { type: 'json' };
import { hexToBytes, bytesToHex, randomBytes } from '@noble/hashes/utils';
import { numberToBytesLE } from '@noble/curves/utils';
import { sha512 } from '@noble/hashes/sha512';
import { default as ed25519vectors } from './wycheproof/eddsa_test.json' assert { type: 'json' };
import { default as x25519vectors } from './wycheproof/x25519_test.json' assert { type: 'json' };
const ed = ed25519;
const hex = bytesToHex;
function to32Bytes(numOrStr) {
let hex = typeof numOrStr === 'string' ? numOrStr : numOrStr.toString(16);
return hexToBytes(hex.padStart(64, '0'));
}
function utf8ToBytes(str) {
if (typeof str !== 'string') {
throw new TypeError(`utf8ToBytes expected string, got ${typeof str}`);
}
return new TextEncoder().encode(str);
}
ed.utils.precompute(8);
should('ed25519/should not accept >32byte private keys', () => {
const invalidPriv =
100000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800073278156000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000n;
throws(() => ed.getPublicKey(invalidPriv));
});
should('ed25519/should verify recent signature', () => {
fc.assert(
fc.property(
fc.hexaString({ minLength: 2, maxLength: 32 }),
fc.bigInt(2n, ed.CURVE.n),
(message, privateKey) => {
const publicKey = ed.getPublicKey(to32Bytes(privateKey));
const signature = ed.sign(to32Bytes(message), to32Bytes(privateKey));
deepStrictEqual(publicKey.length, 32);
deepStrictEqual(signature.length, 64);
deepStrictEqual(ed.verify(signature, to32Bytes(message), publicKey), true);
}
),
{ numRuns: 5 }
);
});
should('ed25519/should not verify signature with wrong message', () => {
fc.assert(
fc.property(
fc.array(fc.integer({ min: 0x00, max: 0xff })),
fc.array(fc.integer({ min: 0x00, max: 0xff })),
fc.bigInt(1n, ed.CURVE.n),
(bytes, wrongBytes, privateKey) => {
const privKey = to32Bytes(privateKey);
const message = new Uint8Array(bytes);
const wrongMessage = new Uint8Array(wrongBytes);
const publicKey = ed.getPublicKey(privKey);
const signature = ed.sign(message, privKey);
deepStrictEqual(
ed.verify(signature, wrongMessage, publicKey),
bytes.toString() === wrongBytes.toString()
);
}
),
{ numRuns: 5 }
);
});
const privKey = to32Bytes('a665a45920422f9d417e4867ef');
const msg = hexToBytes('874f9960c5d2b7a9b5fad383e1ba44719ebb743a');
const wrongMsg = hexToBytes('589d8c7f1da0a24bc07b7381ad48b1cfc211af1c');
should('ed25519/basic methods/should sign and verify', () => {
const publicKey = ed.getPublicKey(privKey);
const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, msg, publicKey), true);
});
should('ed25519/basic methods/should not verify signature with wrong public key', () => {
const publicKey = ed.getPublicKey(12);
const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, msg, publicKey), false);
});
should('ed25519/basic methods/should not verify signature with wrong hash', () => {
const publicKey = ed.getPublicKey(privKey);
const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, wrongMsg, publicKey), false);
});
should('ed25519/sync methods/should sign and verify', () => {
const publicKey = ed.getPublicKey(privKey);
const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, msg, publicKey), true);
});
should('ed25519/sync methods/should not verify signature with wrong public key', () => {
const publicKey = ed.getPublicKey(12);
const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, msg, publicKey), false);
});
should('ed25519/sync methods/should not verify signature with wrong hash', () => {
const publicKey = ed.getPublicKey(privKey);
const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, wrongMsg, publicKey), false);
});
// https://xmr.llcoins.net/addresstests.html
should(
'ed25519/BASE_POINT.multiply()/should create right publicKey without SHA-512 hashing TEST 1',
() => {
const publicKey =
ed.Point.BASE.multiply(0x90af56259a4b6bfbc4337980d5d75fbe3c074630368ff3804d33028e5dbfa77n);
deepStrictEqual(
publicKey.toHex(),
'0f3b913371411b27e646b537e888f685bf929ea7aab93c950ed84433f064480d'
);
}
);
should(
'ed25519/BASE_POINT.multiply()/should create right publicKey without SHA-512 hashing TEST 2',
() => {
const publicKey =
ed.Point.BASE.multiply(0x364e8711a60780382a5d57b061c126f039940f28a9e91fe039d4d3094d8b88n);
deepStrictEqual(
publicKey.toHex(),
'ad545340b58610f0cd62f17d55af1ab11ecde9c084d5476865ddb4dbda015349'
);
}
);
should(
'ed25519/BASE_POINT.multiply()/should create right publicKey without SHA-512 hashing TEST 3',
() => {
const publicKey =
ed.Point.BASE.multiply(0xb9bf90ff3abec042752cac3a07a62f0c16cfb9d32a3fc2305d676ec2d86e941n);
deepStrictEqual(
publicKey.toHex(),
'e097c4415fe85724d522b2e449e8fd78dd40d20097bdc9ae36fe8ec6fe12cb8c'
);
}
);
should(
'ed25519/BASE_POINT.multiply()/should create right publicKey without SHA-512 hashing TEST 4',
() => {
const publicKey =
ed.Point.BASE.multiply(0x69d896f02d79524c9878e080308180e2859d07f9f54454e0800e8db0847a46en);
deepStrictEqual(
publicKey.toHex(),
'f12cb7c43b59971395926f278ce7c2eaded9444fbce62ca717564cb508a0db1d'
);
}
);
should('ed25519/BASE_POINT.multiply()/should throw Point#multiply on TEST 5', () => {
for (const num of [0n, 0, -1n, -1, 1.1]) {
throws(() => ed.Point.BASE.multiply(num));
}
});
// https://ed25519.cr.yp.to/python/sign.py
// https://ed25519.cr.yp.to/python/sign.input
const data = readFileSync('./test/ed25519/vectors.txt', 'utf-8');
const vectors = data
.trim()
.split('\n')
.map((line) => line.split(':'));
should('ed25519 official vectors/should match 1024 official vectors', () => {
for (let i = 0; i < vectors.length; i++) {
const vector = vectors[i];
// Extract.
const priv = vector[0].slice(0, 64);
const expectedPub = vector[1];
const msg = vector[2];
const expectedSignature = vector[3].slice(0, 128);
// Calculate
const pub = ed.getPublicKey(to32Bytes(priv));
deepStrictEqual(hex(pub), expectedPub);
deepStrictEqual(pub, ed.Point.fromHex(pub).toRawBytes());
const signature = hex(ed.sign(msg, priv));
// console.log('vector', i);
// expect(pub).toBe(expectedPub);
deepStrictEqual(signature, expectedSignature);
}
});
// https://tools.ietf.org/html/rfc8032#section-7
should('rfc8032 vectors/should create right signature for 0x9d and empty string', () => {
const privateKey = '9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60';
const publicKey = ed.getPublicKey(privateKey);
const message = '';
const signature = ed.sign(message, privateKey);
deepStrictEqual(
hex(publicKey),
'd75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a'
);
deepStrictEqual(
hex(signature),
'e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b'
);
});
should('rfc8032 vectors/should create right signature for 0x4c and 72', () => {
const privateKey = '4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb';
const publicKey = ed.getPublicKey(privateKey);
const message = '72';
const signature = ed.sign(message, privateKey);
deepStrictEqual(
hex(publicKey),
'3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c'
);
deepStrictEqual(
hex(signature),
'92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00'
);
});
should('rfc8032 vectors/should create right signature for 0x00 and 5a', () => {
const privateKey = '002fdd1f7641793ab064bb7aa848f762e7ec6e332ffc26eeacda141ae33b1783';
const publicKey = ed.getPublicKey(privateKey);
const message =
'5ac1dfc324f43e6cb79a87ab0470fa857b51fb944982e19074ca44b1e40082c1d07b92efa7ea55ad42b7c027e0b9e33756d95a2c1796a7c2066811dc41858377d4b835c1688d638884cd2ad8970b74c1a54aadd27064163928a77988b24403aa85af82ceab6b728e554761af7175aeb99215b7421e4474c04d213e01ff03e3529b11077cdf28964b8c49c5649e3a46fa0a09dcd59dcad58b9b922a83210acd5e65065531400234f5e40cddcf9804968e3e9ac6f5c44af65001e158067fc3a660502d13fa8874fa93332138d9606bc41b4cee7edc39d753dae12a873941bb357f7e92a4498847d6605456cb8c0b425a47d7d3ca37e54e903a41e6450a35ebe5237c6f0c1bbbc1fd71fb7cd893d189850295c199b7d88af26bc8548975fda1099ffefee42a52f3428ddff35e0173d3339562507ac5d2c45bbd2c19cfe89b';
const signature = ed.sign(message, privateKey);
deepStrictEqual(
hex(publicKey),
'77d1d8ebacd13f4e2f8a40e28c4a63bc9ce3bfb69716334bcb28a33eb134086c'
);
deepStrictEqual(
hex(signature),
'0df3aa0d0999ad3dc580378f52d152700d5b3b057f56a66f92112e441e1cb9123c66f18712c87efe22d2573777296241216904d7cdd7d5ea433928bd2872fa0c'
);
});
should('rfc8032 vectors/should create right signature for 0xf5 and long msg', () => {
const privateKey = 'f5e5767cf153319517630f226876b86c8160cc583bc013744c6bf255f5cc0ee5';
const publicKey = ed.getPublicKey(privateKey);
const message =
'08b8b2b733424243760fe426a4b54908632110a66c2f6591eabd3345e3e4eb98fa6e264bf09efe12ee50f8f54e9f77b1e355f6c50544e23fb1433ddf73be84d879de7c0046dc4996d9e773f4bc9efe5738829adb26c81b37c93a1b270b20329d658675fc6ea534e0810a4432826bf58c941efb65d57a338bbd2e26640f89ffbc1a858efcb8550ee3a5e1998bd177e93a7363c344fe6b199ee5d02e82d522c4feba15452f80288a821a579116ec6dad2b3b310da903401aa62100ab5d1a36553e06203b33890cc9b832f79ef80560ccb9a39ce767967ed628c6ad573cb116dbefefd75499da96bd68a8a97b928a8bbc103b6621fcde2beca1231d206be6cd9ec7aff6f6c94fcd7204ed3455c68c83f4a41da4af2b74ef5c53f1d8ac70bdcb7ed185ce81bd84359d44254d95629e9855a94a7c1958d1f8ada5d0532ed8a5aa3fb2d17ba70eb6248e594e1a2297acbbb39d502f1a8c6eb6f1ce22b3de1a1f40cc24554119a831a9aad6079cad88425de6bde1a9187ebb6092cf67bf2b13fd65f27088d78b7e883c8759d2c4f5c65adb7553878ad575f9fad878e80a0c9ba63bcbcc2732e69485bbc9c90bfbd62481d9089beccf80cfe2df16a2cf65bd92dd597b0707e0917af48bbb75fed413d238f5555a7a569d80c3414a8d0859dc65a46128bab27af87a71314f318c782b23ebfe808b82b0ce26401d2e22f04d83d1255dc51addd3b75a2b1ae0784504df543af8969be3ea7082ff7fc9888c144da2af58429ec96031dbcad3dad9af0dcbaaaf268cb8fcffead94f3c7ca495e056a9b47acdb751fb73e666c6c655ade8297297d07ad1ba5e43f1bca32301651339e22904cc8c42f58c30c04aafdb038dda0847dd988dcda6f3bfd15c4b4c4525004aa06eeff8ca61783aacec57fb3d1f92b0fe2fd1a85f6724517b65e614ad6808d6f6ee34dff7310fdc82aebfd904b01e1dc54b2927094b2db68d6f903b68401adebf5a7e08d78ff4ef5d63653a65040cf9bfd4aca7984a74d37145986780fc0b16ac451649de6188a7dbdf191f64b5fc5e2ab47b57f7f7276cd419c17a3ca8e1b939ae49e488acba6b965610b5480109c8b17b80e1b7b750dfc7598d5d5011fd2dcc5600a32ef5b52a1ecc820e308aa342721aac0943bf6686b64b2579376504ccc493d97e6aed3fb0f9cd71a43dd497f01f17c0e2cb3797aa2a2f256656168e6c496afc5fb93246f6b1116398a346f1a641f3b041e989f7914f90cc2c7fff357876e506b50d334ba77c225bc307ba537152f3f1610e4eafe595f6d9d90d11faa933a15ef1369546868a7f3a45a96768d40fd9d03412c091c6315cf4fde7cb68606937380db2eaaa707b4c4185c32eddcdd306705e4dc1ffc872eeee475a64dfac86aba41c0618983f8741c5ef68d3a101e8a3b8cac60c905c15fc910840b94c00a0b9d0';
const signature = ed.sign(message, privateKey);
deepStrictEqual(
hex(publicKey),
'278117fc144c72340f67d0f2316e8386ceffbf2b2428c9c51fef7c597f1d426e'
);
deepStrictEqual(
hex(signature),
'0aab4c900501b3e24d7cdf4663326a3a87df5e4843b2cbdb67cbf6e460fec350aa5371b1508f9f4528ecea23c436d94b5e8fcd4f681e30a6ac00a9704a188a03'
);
});
// const PRIVATE_KEY = 0xa665a45920422f9d417e4867efn;
// const MESSAGE = ripemd160(new Uint8Array([97, 98, 99, 100, 101, 102, 103]));
// prettier-ignore
// const MESSAGE = new Uint8Array([
// 135, 79, 153, 96, 197, 210, 183, 169, 181, 250, 211, 131, 225, 186, 68, 113, 158, 187, 116, 58,
// ]);
// const WRONG_MESSAGE = ripemd160(new Uint8Array([98, 99, 100, 101, 102, 103]));
// prettier-ignore
// const WRONG_MESSAGE = new Uint8Array([
// 88, 157, 140, 127, 29, 160, 162, 75, 192, 123, 115, 129, 173, 72, 177, 207, 194, 17, 175, 28,
// ]);
// // it("should verify just signed message", async () => {
// // await fc.assert(fc.asyncProperty(
// // fc.hexa(),
// // fc.bigInt(2n, ristretto25519.PRIME_ORDER),
// // async (message, privateKey) => {
// // const publicKey = await ristretto25519.getPublicKey(privateKey);
// // const signature = await ristretto25519.sign(message, privateKey);
// // expect(publicKey.length).toBe(32);
// // expect(signature.length).toBe(64);
// // expect(await ristretto25519.verify(signature, message, publicKey)).toBe(true);
// // }),
// // { numRuns: 1 }
// // );
// // });
// // it("should not verify sign with wrong message", async () => {
// // await fc.assert(fc.asyncProperty(
// // fc.array(fc.integer(0x00, 0xff)),
// // fc.array(fc.integer(0x00, 0xff)),
// // fc.bigInt(2n, ristretto25519.PRIME_ORDER),
// // async (bytes, wrongBytes, privateKey) => {
// // const message = new Uint8Array(bytes);
// // const wrongMessage = new Uint8Array(wrongBytes);
// // const publicKey = await ristretto25519.getPublicKey(privateKey);
// // const signature = await ristretto25519.sign(message, privateKey);
// // expect(await ristretto25519.verify(signature, wrongMessage, publicKey)).toBe(
// // bytes.toString() === wrongBytes.toString()
// // );
// // }),
// // { numRuns: 1 }
// // );
// // });
// // it("should sign and verify", async () => {
// // const publicKey = await ristretto25519.getPublicKey(PRIVATE_KEY);
// // const signature = await ristretto25519.sign(MESSAGE, PRIVATE_KEY);
// // expect(await ristretto25519.verify(signature, MESSAGE, publicKey)).toBe(true);
// // });
// // it("should not verify signature with wrong public key", async () => {
// // const publicKey = await ristretto25519.getPublicKey(12);
// // const signature = await ristretto25519.sign(MESSAGE, PRIVATE_KEY);
// // expect(await ristretto25519.verify(signature, MESSAGE, publicKey)).toBe(false);
// // });
// // it("should not verify signature with wrong hash", async () => {
// // const publicKey = await ristretto25519.getPublicKey(PRIVATE_KEY);
// // const signature = await ristretto25519.sign(MESSAGE, PRIVATE_KEY);
// // expect(await ristretto25519.verify(signature, WRONG_MESSAGE, publicKey)).toBe(false);
// // });
should('ristretto255/should follow the byte encodings of small multiples', () => {
const encodingsOfSmallMultiples = [
// This is the identity point
'0000000000000000000000000000000000000000000000000000000000000000',
// This is the basepoint
'e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76',
// These are small multiples of the basepoint
'6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919',
'94741f5d5d52755ece4f23f044ee27d5d1ea1e2bd196b462166b16152a9d0259',
'da80862773358b466ffadfe0b3293ab3d9fd53c5ea6c955358f568322daf6a57',
'e882b131016b52c1d3337080187cf768423efccbb517bb495ab812c4160ff44e',
'f64746d3c92b13050ed8d80236a7f0007c3b3f962f5ba793d19a601ebb1df403',
'44f53520926ec81fbd5a387845beb7df85a96a24ece18738bdcfa6a7822a176d',
'903293d8f2287ebe10e2374dc1a53e0bc887e592699f02d077d5263cdd55601c',
'02622ace8f7303a31cafc63f8fc48fdc16e1c8c8d234b2f0d6685282a9076031',
'20706fd788b2720a1ed2a5dad4952b01f413bcf0e7564de8cdc816689e2db95f',
'bce83f8ba5dd2fa572864c24ba1810f9522bc6004afe95877ac73241cafdab42',
'e4549ee16b9aa03099ca208c67adafcafa4c3f3e4e5303de6026e3ca8ff84460',
'aa52e000df2e16f55fb1032fc33bc42742dad6bd5a8fc0be0167436c5948501f',
'46376b80f409b29dc2b5f6f0c52591990896e5716f41477cd30085ab7f10301e',
'e0c418f7c8d9c4cdd7395b93ea124f3ad99021bb681dfc3302a9d99a2e53e64e',
];
let B = RistrettoPoint.BASE;
let P = RistrettoPoint.ZERO;
for (const encoded of encodingsOfSmallMultiples) {
deepStrictEqual(P.toHex(), encoded);
deepStrictEqual(RistrettoPoint.fromHex(encoded).toHex(), encoded);
P = P.add(B);
}
});
should('ristretto255/should not convert bad bytes encoding', () => {
const badEncodings = [
// These are all bad because they're non-canonical field encodings.
'00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f',
'f3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f',
'edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f',
// These are all bad because they're negative field elements.
'0100000000000000000000000000000000000000000000000000000000000000',
'01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f',
'ed57ffd8c914fb201471d1c3d245ce3c746fcbe63a3679d51b6a516ebebe0e20',
'c34c4e1826e5d403b78e246e88aa051c36ccf0aafebffe137d148a2bf9104562',
'c940e5a4404157cfb1628b108db051a8d439e1a421394ec4ebccb9ec92a8ac78',
'47cfc5497c53dc8e61c91d17fd626ffb1c49e2bca94eed052281b510b1117a24',
'f1c6165d33367351b0da8f6e4511010c68174a03b6581212c71c0e1d026c3c72',
'87260f7a2f12495118360f02c26a470f450dadf34a413d21042b43b9d93e1309',
// These are all bad because they give a nonsquare x^2.
'26948d35ca62e643e26a83177332e6b6afeb9d08e4268b650f1f5bbd8d81d371',
'4eac077a713c57b4f4397629a4145982c661f48044dd3f96427d40b147d9742f',
'de6a7b00deadc788eb6b6c8d20c0ae96c2f2019078fa604fee5b87d6e989ad7b',
'bcab477be20861e01e4a0e295284146a510150d9817763caf1a6f4b422d67042',
'2a292df7e32cababbd9de088d1d1abec9fc0440f637ed2fba145094dc14bea08',
'f4a9e534fc0d216c44b218fa0c42d99635a0127ee2e53c712f70609649fdff22',
'8268436f8c4126196cf64b3c7ddbda90746a378625f9813dd9b8457077256731',
'2810e5cbc2cc4d4eece54f61c6f69758e289aa7ab440b3cbeaa21995c2f4232b',
// These are all bad because they give a negative xy value.
'3eb858e78f5a7254d8c9731174a94f76755fd3941c0ac93735c07ba14579630e',
'a45fdc55c76448c049a1ab33f17023edfb2be3581e9c7aade8a6125215e04220',
'd483fe813c6ba647ebbfd3ec41adca1c6130c2beeee9d9bf065c8d151c5f396e',
'8a2e1d30050198c65a54483123960ccc38aef6848e1ec8f5f780e8523769ba32',
'32888462f8b486c68ad7dd9610be5192bbeaf3b443951ac1a8118419d9fa097b',
'227142501b9d4355ccba290404bde41575b037693cef1f438c47f8fbf35d1165',
'5c37cc491da847cfeb9281d407efc41e15144c876e0170b499a96a22ed31e01e',
'445425117cb8c90edcbc7c1cc0e74f747f2c1efa5630a967c64f287792a48a4b',
// This is s = -1, which causes y = 0.
'ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f',
];
for (const badBytes of badEncodings) {
const b = hexToBytes(badBytes);
throws(() => RistrettoPoint.fromHex(b), badBytes);
}
});
should('ristretto255/should create right points from uniform hash', async () => {
const labels = [
'Ristretto is traditionally a short shot of espresso coffee',
'made with the normal amount of ground coffee but extracted with',
'about half the amount of water in the same amount of time',
'by using a finer grind.',
'This produces a concentrated shot of coffee per volume.',
'Just pulling a normal shot short will produce a weaker shot',
'and is not a Ristretto as some believe.',
];
const encodedHashToPoints = [
'3066f82a1a747d45120d1740f14358531a8f04bbffe6a819f86dfe50f44a0a46',
'f26e5b6f7d362d2d2a94c5d0e7602cb4773c95a2e5c31a64f133189fa76ed61b',
'006ccd2a9e6867e6a2c5cea83d3302cc9de128dd2a9a57dd8ee7b9d7ffe02826',
'f8f0c87cf237953c5890aec3998169005dae3eca1fbb04548c635953c817f92a',
'ae81e7dedf20a497e10c304a765c1767a42d6e06029758d2d7e8ef7cc4c41179',
'e2705652ff9f5e44d3e841bf1c251cf7dddb77d140870d1ab2ed64f1a9ce8628',
'80bd07262511cdde4863f8a7434cef696750681cb9510eea557088f76d9e5065',
];
for (let i = 0; i < labels.length; i++) {
const hash = sha512(utf8ToBytes(labels[i]));
const point = RistrettoPoint.hashToCurve(hash);
deepStrictEqual(point.toHex(), encodedHashToPoints[i]);
}
});
should('input immutability: sign/verify are immutable', () => {
const privateKey = ed.utils.randomPrivateKey();
const publicKey = ed.getPublicKey(privateKey);
for (let i = 0; i < 100; i++) {
let payload = randomBytes(100);
let signature = ed.sign(payload, privateKey);
if (!ed.verify(signature, payload, publicKey)) {
throw new Error('Signature verification failed');
}
const signatureCopy = Buffer.alloc(signature.byteLength);
signatureCopy.set(signature, 0); // <-- breaks
payload = payload.slice();
signature = signature.slice();
if (!ed.verify(signatureCopy, payload, publicKey))
throw new Error('Copied signature verification failed');
}
});
// 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;
}
deepStrictEqual(noble, v.valid_zip215);
}
});
should('ZIP-215 compliance tests/disallows sig.s >= CURVE.n', () => {
const sig = new ed.Signature(ed.Point.BASE, 1n);
sig.s = ed.CURVE.n + 1n;
throws(() => ed.verify(sig, 'deadbeef', ed.Point.BASE));
});
const rfc7748Mul = [
{
scalar: 'a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4',
u: 'e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c',
outputU: 'c3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552',
},
{
scalar: '4b66e9d4d1b4673c5ad22691957d6af5c11b6421e0ea01d42ca4169e7918ba0d',
u: 'e5210f12786811d3f4b7959d0538ae2c31dbe7106fc03c3efc4cd549c715a493',
outputU: '95cbde9476e8907d7aade45cb4b873f88b595a68799fa152e6f8f7647aac7957',
},
];
for (let i = 0; i < rfc7748Mul.length; i++) {
const v = rfc7748Mul[i];
should(`RFC7748: scalarMult (${i})`, () => {
deepStrictEqual(hex(x25519.scalarMult(v.u, v.scalar)), v.outputU);
});
}
const rfc7748Iter = [
{ scalar: '422c8e7a6227d7bca1350b3e2bb7279f7897b87bb6854b783c60e80311ae3079', iters: 1 },
{ scalar: '684cf59ba83309552800ef566f2f4d3c1c3887c49360e3875f2eb94d99532c51', iters: 1000 },
// { scalar: '7c3911e0ab2586fd864497297e575e6f3bc601c0883c30df5f4dd2d24f665424', iters: 1000000 },
];
for (let i = 0; i < rfc7748Iter.length; i++) {
const { scalar, iters } = rfc7748Iter[i];
should(`RFC7748: scalarMult iteration (${i})`, () => {
let k = x25519.Gu;
for (let i = 0, u = k; i < iters; i++) [k, u] = [x25519.scalarMult(u, k), k];
deepStrictEqual(hex(k), scalar);
});
}
should('RFC7748 getSharedKey', () => {
const alicePrivate = '77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a';
const alicePublic = '8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a';
const bobPrivate = '5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb';
const bobPublic = 'de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f';
const shared = '4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742';
deepStrictEqual(alicePublic, hex(x25519.getPublicKey(alicePrivate)));
deepStrictEqual(bobPublic, hex(x25519.getPublicKey(bobPrivate)));
deepStrictEqual(hex(x25519.scalarMult(bobPublic, alicePrivate)), shared);
deepStrictEqual(hex(x25519.scalarMult(alicePublic, bobPrivate)), shared);
});
// should('X25519/getSharedSecret() should be commutative', () => {
// 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;
// }
// }
// });
// should('X25519: should convert base point to montgomery using fromPoint', () => {
// deepStrictEqual(
// hex(ed.montgomeryCurve.UfromPoint(ed.Point.BASE)),
// ed.montgomeryCurve.BASE_POINT_U
// );
// });
{
const group = x25519vectors.testGroups[0];
should(`Wycheproof/X25519`, () => {
for (let i = 0; i < group.tests.length; i++) {
const v = group.tests[i];
const comment = `(${i}, ${v.result}) ${v.comment}`;
if (v.result === 'valid' || v.result === 'acceptable') {
try {
const shared = hex(x25519.scalarMult(v.public, v.private));
deepStrictEqual(shared, v.shared, comment);
} catch (e) {
// We are more strict
if (e.message.includes('Expected valid scalar')) return;
if (e.message.includes('Invalid private or public key received')) return;
throw e;
}
} else if (v.result === 'invalid') {
let failed = false;
try {
x25519.scalarMult(v.public, v.private);
} catch (error) {
failed = true;
}
deepStrictEqual(failed, true, comment);
} else throw new Error('unknown test result');
}
});
}
should(`Wycheproof/ED25519`, () => {
for (let g = 0; g < ed25519vectors.testGroups.length; g++) {
const group = ed25519vectors.testGroups[g];
const key = group.key;
deepStrictEqual(hex(ed.getPublicKey(key.sk)), key.pk, `(${g}, public)`);
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(hex(ed.sign(v.msg, key.sk)), v.sig, comment);
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('Property test issue #1', () => {
const message = new Uint8Array([12, 12, 12]);
const signature = ed.sign(message, to32Bytes(1n));
const publicKey = ed.getPublicKey(to32Bytes(1n)); // <- was 1n
deepStrictEqual(ed.verify(signature, message, publicKey), true);
});
const VECTORS_RFC8032_CTX = [
{
secretKey: '0305334e381af78f141cb666f6199f57bc3495335a256a95bd2a55bf546663f6',
publicKey: 'dfc9425e4f968f7f0c29f0259cf5f9aed6851c2bb4ad8bfb860cfee0ab248292',
message: 'f726936d19c800494e3fdaff20b276a8',
context: '666f6f',
signature:
'55a4cc2f70a54e04288c5f4cd1e45a7b' +
'b520b36292911876cada7323198dd87a' +
'8b36950b95130022907a7fb7c4e9b2d5' +
'f6cca685a587b4b21f4b888e4e7edb0d',
},
{
secretKey: '0305334e381af78f141cb666f6199f57bc3495335a256a95bd2a55bf546663f6',
publicKey: 'dfc9425e4f968f7f0c29f0259cf5f9aed6851c2bb4ad8bfb860cfee0ab248292',
message: 'f726936d19c800494e3fdaff20b276a8',
context: '626172',
signature:
'fc60d5872fc46b3aa69f8b5b4351d580' +
'8f92bcc044606db097abab6dbcb1aee3' +
'216c48e8b3b66431b5b186d1d28f8ee1' +
'5a5ca2df6668346291c2043d4eb3e90d',
},
{
secretKey: '0305334e381af78f141cb666f6199f57bc3495335a256a95bd2a55bf546663f6',
publicKey: 'dfc9425e4f968f7f0c29f0259cf5f9aed6851c2bb4ad8bfb860cfee0ab248292',
message: '508e9e6882b979fea900f62adceaca35',
context: '666f6f',
signature:
'8b70c1cc8310e1de20ac53ce28ae6e72' +
'07f33c3295e03bb5c0732a1d20dc6490' +
'8922a8b052cf99b7c4fe107a5abb5b2c' +
'4085ae75890d02df26269d8945f84b0b',
},
{
secretKey: 'ab9c2853ce297ddab85c993b3ae14bcad39b2c682beabc27d6d4eb20711d6560',
publicKey: '0f1d1274943b91415889152e893d80e93275a1fc0b65fd71b4b0dda10ad7d772',
message: 'f726936d19c800494e3fdaff20b276a8',
context: '666f6f',
signature:
'21655b5f1aa965996b3f97b3c849eafb' +
'a922a0a62992f73b3d1b73106a84ad85' +
'e9b86a7b6005ea868337ff2d20a7f5fb' +
'd4cd10b0be49a68da2b2e0dc0ad8960f',
},
];
for (let i = 0; i < VECTORS_RFC8032_CTX.length; i++) {
const v = VECTORS_RFC8032_CTX[i];
should(`RFC8032ctx/${i}`, () => {
deepStrictEqual(hex(ed25519ctx.getPublicKey(v.secretKey)), v.publicKey);
deepStrictEqual(hex(ed25519ctx.sign(v.message, v.secretKey, v.context)), v.signature);
deepStrictEqual(ed25519ctx.verify(v.signature, v.message, v.publicKey, v.context), true);
});
}
const VECTORS_RFC8032_PH = [
{
secretKey: '833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42',
publicKey: 'ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf',
message: '616263',
signature:
'98a70222f0b8121aa9d30f813d683f80' +
'9e462b469c7ff87639499bb94e6dae41' +
'31f85042463c2a355a2003d062adf5aa' +
'a10b8c61e636062aaad11c2a26083406',
},
];
for (let i = 0; i < VECTORS_RFC8032_PH.length; i++) {
const v = VECTORS_RFC8032_PH[i];
should(`RFC8032ph/${i}`, () => {
deepStrictEqual(hex(ed25519ph.getPublicKey(v.secretKey)), v.publicKey);
deepStrictEqual(hex(ed25519ph.sign(v.message, v.secretKey)), v.signature);
deepStrictEqual(ed25519ph.verify(v.signature, v.message, v.publicKey), true);
});
}
should('X25519 base point', () => {
const { y } = ed25519.Point.BASE;
const u = ed25519.utils.mod((y + 1n) * ed25519.utils.invert(1n - y, ed25519.CURVE.P));
deepStrictEqual(hex(numberToBytesLE(u, 32)), x25519.Gu);
});
// ESM is broken.
import url from 'url';
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
should.run();
}

View File

@@ -1,664 +0,0 @@
import { deepStrictEqual, throws } from 'assert';
import { should } from 'micro-should';
import * as fc from 'fast-check';
import { ed448, ed448ph, x448 } from '../lib/ed448.js';
import { hexToBytes, bytesToHex, randomBytes } from '@noble/hashes/utils';
import { numberToBytesLE } from '@noble/curves/utils';
import { default as ed448vectors } from './wycheproof/ed448_test.json' assert { type: 'json' };
import { default as x448vectors } from './wycheproof/x448_test.json' assert { type: 'json' };
const ed = ed448;
const hex = bytesToHex;
ed.utils.precompute(4);
should(`Basic`, () => {
const G1 = ed.Point.BASE;
deepStrictEqual(
G1.x,
224580040295924300187604334099896036246789641632564134246125461686950415467406032909029192869357953282578032075146446173674602635247710n
);
deepStrictEqual(
G1.y,
298819210078481492676017930443930673437544040154080242095928241372331506189835876003536878655418784733982303233503462500531545062832660n
);
const G2 = ed.Point.BASE.multiply(2n);
deepStrictEqual(
G2.x,
484559149530404593699549205258669689569094240458212040187660132787056912146709081364401144455726350866276831544947397859048262938744149n
);
deepStrictEqual(
G2.y,
494088759867433727674302672526735089350544552303727723746126484473087719117037293890093462157703888342865036477787453078312060500281069n
);
const G3 = ed.Point.BASE.multiply(3n);
deepStrictEqual(
G3.x,
23839778817283171003887799738662344287085130522697782688245073320169861206004018274567429238677677920280078599146891901463786155880335n
);
deepStrictEqual(
G3.y,
636046652612779686502873775776967954190574036985351036782021535703553242737829645273154208057988851307101009474686328623630835377952508n
);
});
should('Basic/decompress', () => {
const G1 = ed.Point.BASE;
const G2 = ed.Point.BASE.multiply(2n);
const G3 = ed.Point.BASE.multiply(3n);
const points = [G1, G2, G3];
const getXY = (p) => ({ x: p.x, y: p.y });
for (const p of points) deepStrictEqual(getXY(ed.Point.fromHex(p.toHex())), getXY(p));
});
const VECTORS_RFC8032 = [
{
secretKey:
'6c82a562cb808d10d632be89c8513ebf' +
'6c929f34ddfa8c9f63c9960ef6e348a3' +
'528c8a3fcc2f044e39a3fc5b94492f8f' +
'032e7549a20098f95b',
publicKey:
'5fd7449b59b461fd2ce787ec616ad46a' +
'1da1342485a70e1f8a0ea75d80e96778' +
'edf124769b46c7061bd6783df1e50f6c' +
'd1fa1abeafe8256180',
message: '',
signature:
'533a37f6bbe457251f023c0d88f976ae' +
'2dfb504a843e34d2074fd823d41a591f' +
'2b233f034f628281f2fd7a22ddd47d78' +
'28c59bd0a21bfd3980ff0d2028d4b18a' +
'9df63e006c5d1c2d345b925d8dc00b41' +
'04852db99ac5c7cdda8530a113a0f4db' +
'b61149f05a7363268c71d95808ff2e65' +
'2600',
},
{
secretKey:
'c4eab05d357007c632f3dbb48489924d' +
'552b08fe0c353a0d4a1f00acda2c463a' +
'fbea67c5e8d2877c5e3bc397a659949e' +
'f8021e954e0a12274e',
publicKey:
'43ba28f430cdff456ae531545f7ecd0a' +
'c834a55d9358c0372bfa0c6c6798c086' +
'6aea01eb00742802b8438ea4cb82169c' +
'235160627b4c3a9480',
message: '03',
signature:
'26b8f91727bd62897af15e41eb43c377' +
'efb9c610d48f2335cb0bd0087810f435' +
'2541b143c4b981b7e18f62de8ccdf633' +
'fc1bf037ab7cd779805e0dbcc0aae1cb' +
'cee1afb2e027df36bc04dcecbf154336' +
'c19f0af7e0a6472905e799f1953d2a0f' +
'f3348ab21aa4adafd1d234441cf807c0' +
'3a00',
},
{
secretKey:
'cd23d24f714274e744343237b93290f5' +
'11f6425f98e64459ff203e8985083ffd' +
'f60500553abc0e05cd02184bdb89c4cc' +
'd67e187951267eb328',
publicKey:
'dcea9e78f35a1bf3499a831b10b86c90' +
'aac01cd84b67a0109b55a36e9328b1e3' +
'65fce161d71ce7131a543ea4cb5f7e9f' +
'1d8b00696447001400',
message: '0c3e544074ec63b0265e0c',
signature:
'1f0a8888ce25e8d458a21130879b840a' +
'9089d999aaba039eaf3e3afa090a09d3' +
'89dba82c4ff2ae8ac5cdfb7c55e94d5d' +
'961a29fe0109941e00b8dbdeea6d3b05' +
'1068df7254c0cdc129cbe62db2dc957d' +
'bb47b51fd3f213fb8698f064774250a5' +
'028961c9bf8ffd973fe5d5c206492b14' +
'0e00',
},
{
secretKey:
'258cdd4ada32ed9c9ff54e63756ae582' +
'fb8fab2ac721f2c8e676a72768513d93' +
'9f63dddb55609133f29adf86ec9929dc' +
'cb52c1c5fd2ff7e21b',
publicKey:
'3ba16da0c6f2cc1f30187740756f5e79' +
'8d6bc5fc015d7c63cc9510ee3fd44adc' +
'24d8e968b6e46e6f94d19b945361726b' +
'd75e149ef09817f580',
message: '64a65f3cdedcdd66811e2915',
signature:
'7eeeab7c4e50fb799b418ee5e3197ff6' +
'bf15d43a14c34389b59dd1a7b1b85b4a' +
'e90438aca634bea45e3a2695f1270f07' +
'fdcdf7c62b8efeaf00b45c2c96ba457e' +
'b1a8bf075a3db28e5c24f6b923ed4ad7' +
'47c3c9e03c7079efb87cb110d3a99861' +
'e72003cbae6d6b8b827e4e6c143064ff' +
'3c00',
},
{
secretKey:
'7ef4e84544236752fbb56b8f31a23a10' +
'e42814f5f55ca037cdcc11c64c9a3b29' +
'49c1bb60700314611732a6c2fea98eeb' +
'c0266a11a93970100e',
publicKey:
'b3da079b0aa493a5772029f0467baebe' +
'e5a8112d9d3a22532361da294f7bb381' +
'5c5dc59e176b4d9f381ca0938e13c6c0' +
'7b174be65dfa578e80',
message: '64a65f3cdedcdd66811e2915e7',
signature:
'6a12066f55331b6c22acd5d5bfc5d712' +
'28fbda80ae8dec26bdd306743c5027cb' +
'4890810c162c027468675ecf645a8317' +
'6c0d7323a2ccde2d80efe5a1268e8aca' +
'1d6fbc194d3f77c44986eb4ab4177919' +
'ad8bec33eb47bbb5fc6e28196fd1caf5' +
'6b4e7e0ba5519234d047155ac727a105' +
'3100',
},
{
secretKey:
'd65df341ad13e008567688baedda8e9d' +
'cdc17dc024974ea5b4227b6530e339bf' +
'f21f99e68ca6968f3cca6dfe0fb9f4fa' +
'b4fa135d5542ea3f01',
publicKey:
'df9705f58edbab802c7f8363cfe5560a' +
'b1c6132c20a9f1dd163483a26f8ac53a' +
'39d6808bf4a1dfbd261b099bb03b3fb5' +
'0906cb28bd8a081f00',
message:
'bd0f6a3747cd561bdddf4640a332461a' +
'4a30a12a434cd0bf40d766d9c6d458e5' +
'512204a30c17d1f50b5079631f64eb31' +
'12182da3005835461113718d1a5ef944',
signature:
'554bc2480860b49eab8532d2a533b7d5' +
'78ef473eeb58c98bb2d0e1ce488a98b1' +
'8dfde9b9b90775e67f47d4a1c3482058' +
'efc9f40d2ca033a0801b63d45b3b722e' +
'f552bad3b4ccb667da350192b61c508c' +
'f7b6b5adadc2c8d9a446ef003fb05cba' +
'5f30e88e36ec2703b349ca229c267083' +
'3900',
},
{
secretKey:
'2ec5fe3c17045abdb136a5e6a913e32a' +
'b75ae68b53d2fc149b77e504132d3756' +
'9b7e766ba74a19bd6162343a21c8590a' +
'a9cebca9014c636df5',
publicKey:
'79756f014dcfe2079f5dd9e718be4171' +
'e2ef2486a08f25186f6bff43a9936b9b' +
'fe12402b08ae65798a3d81e22e9ec80e' +
'7690862ef3d4ed3a00',
message:
'15777532b0bdd0d1389f636c5f6b9ba7' +
'34c90af572877e2d272dd078aa1e567c' +
'fa80e12928bb542330e8409f31745041' +
'07ecd5efac61ae7504dabe2a602ede89' +
'e5cca6257a7c77e27a702b3ae39fc769' +
'fc54f2395ae6a1178cab4738e543072f' +
'c1c177fe71e92e25bf03e4ecb72f47b6' +
'4d0465aaea4c7fad372536c8ba516a60' +
'39c3c2a39f0e4d832be432dfa9a706a6' +
'e5c7e19f397964ca4258002f7c0541b5' +
'90316dbc5622b6b2a6fe7a4abffd9610' +
'5eca76ea7b98816af0748c10df048ce0' +
'12d901015a51f189f3888145c03650aa' +
'23ce894c3bd889e030d565071c59f409' +
'a9981b51878fd6fc110624dcbcde0bf7' +
'a69ccce38fabdf86f3bef6044819de11',
signature:
'c650ddbb0601c19ca11439e1640dd931' +
'f43c518ea5bea70d3dcde5f4191fe53f' +
'00cf966546b72bcc7d58be2b9badef28' +
'743954e3a44a23f880e8d4f1cfce2d7a' +
'61452d26da05896f0a50da66a239a8a1' +
'88b6d825b3305ad77b73fbac0836ecc6' +
'0987fd08527c1a8e80d5823e65cafe2a' +
'3d00',
},
{
secretKey:
'872d093780f5d3730df7c212664b37b8' +
'a0f24f56810daa8382cd4fa3f77634ec' +
'44dc54f1c2ed9bea86fafb7632d8be19' +
'9ea165f5ad55dd9ce8',
publicKey:
'a81b2e8a70a5ac94ffdbcc9badfc3feb' +
'0801f258578bb114ad44ece1ec0e799d' +
'a08effb81c5d685c0c56f64eecaef8cd' +
'f11cc38737838cf400',
message:
'6ddf802e1aae4986935f7f981ba3f035' +
'1d6273c0a0c22c9c0e8339168e675412' +
'a3debfaf435ed651558007db4384b650' +
'fcc07e3b586a27a4f7a00ac8a6fec2cd' +
'86ae4bf1570c41e6a40c931db27b2faa' +
'15a8cedd52cff7362c4e6e23daec0fbc' +
'3a79b6806e316efcc7b68119bf46bc76' +
'a26067a53f296dafdbdc11c77f7777e9' +
'72660cf4b6a9b369a6665f02e0cc9b6e' +
'dfad136b4fabe723d2813db3136cfde9' +
'b6d044322fee2947952e031b73ab5c60' +
'3349b307bdc27bc6cb8b8bbd7bd32321' +
'9b8033a581b59eadebb09b3c4f3d2277' +
'd4f0343624acc817804728b25ab79717' +
'2b4c5c21a22f9c7839d64300232eb66e' +
'53f31c723fa37fe387c7d3e50bdf9813' +
'a30e5bb12cf4cd930c40cfb4e1fc6225' +
'92a49588794494d56d24ea4b40c89fc0' +
'596cc9ebb961c8cb10adde976a5d602b' +
'1c3f85b9b9a001ed3c6a4d3b1437f520' +
'96cd1956d042a597d561a596ecd3d173' +
'5a8d570ea0ec27225a2c4aaff26306d1' +
'526c1af3ca6d9cf5a2c98f47e1c46db9' +
'a33234cfd4d81f2c98538a09ebe76998' +
'd0d8fd25997c7d255c6d66ece6fa56f1' +
'1144950f027795e653008f4bd7ca2dee' +
'85d8e90f3dc315130ce2a00375a318c7' +
'c3d97be2c8ce5b6db41a6254ff264fa6' +
'155baee3b0773c0f497c573f19bb4f42' +
'40281f0b1f4f7be857a4e59d416c06b4' +
'c50fa09e1810ddc6b1467baeac5a3668' +
'd11b6ecaa901440016f389f80acc4db9' +
'77025e7f5924388c7e340a732e554440' +
'e76570f8dd71b7d640b3450d1fd5f041' +
'0a18f9a3494f707c717b79b4bf75c984' +
'00b096b21653b5d217cf3565c9597456' +
'f70703497a078763829bc01bb1cbc8fa' +
'04eadc9a6e3f6699587a9e75c94e5bab' +
'0036e0b2e711392cff0047d0d6b05bd2' +
'a588bc109718954259f1d86678a579a3' +
'120f19cfb2963f177aeb70f2d4844826' +
'262e51b80271272068ef5b3856fa8535' +
'aa2a88b2d41f2a0e2fda7624c2850272' +
'ac4a2f561f8f2f7a318bfd5caf969614' +
'9e4ac824ad3460538fdc25421beec2cc' +
'6818162d06bbed0c40a387192349db67' +
'a118bada6cd5ab0140ee273204f628aa' +
'd1c135f770279a651e24d8c14d75a605' +
'9d76b96a6fd857def5e0b354b27ab937' +
'a5815d16b5fae407ff18222c6d1ed263' +
'be68c95f32d908bd895cd76207ae7264' +
'87567f9a67dad79abec316f683b17f2d' +
'02bf07e0ac8b5bc6162cf94697b3c27c' +
'd1fea49b27f23ba2901871962506520c' +
'392da8b6ad0d99f7013fbc06c2c17a56' +
'9500c8a7696481c1cd33e9b14e40b82e' +
'79a5f5db82571ba97bae3ad3e0479515' +
'bb0e2b0f3bfcd1fd33034efc6245eddd' +
'7ee2086ddae2600d8ca73e214e8c2b0b' +
'db2b047c6a464a562ed77b73d2d841c4' +
'b34973551257713b753632efba348169' +
'abc90a68f42611a40126d7cb21b58695' +
'568186f7e569d2ff0f9e745d0487dd2e' +
'b997cafc5abf9dd102e62ff66cba87',
signature:
'e301345a41a39a4d72fff8df69c98075' +
'a0cc082b802fc9b2b6bc503f926b65bd' +
'df7f4c8f1cb49f6396afc8a70abe6d8a' +
'ef0db478d4c6b2970076c6a0484fe76d' +
'76b3a97625d79f1ce240e7c576750d29' +
'5528286f719b413de9ada3e8eb78ed57' +
'3603ce30d8bb761785dc30dbc320869e' +
'1a00',
},
];
for (let i = 0; i < VECTORS_RFC8032.length; i++) {
const v = VECTORS_RFC8032[i];
should(`RFC8032/${i}`, () => {
deepStrictEqual(hex(ed.getPublicKey(v.secretKey)), v.publicKey);
deepStrictEqual(hex(ed.sign(v.message, v.secretKey)), v.signature);
deepStrictEqual(ed.verify(v.signature, v.message, v.publicKey), true);
});
}
should('ed448/should not accept >57byte private keys', async () => {
const invalidPriv =
100000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800073278156000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000n;
throws(() => ed.getPublicKey(invalidPriv));
});
function to57Bytes(numOrStr) {
let hex = typeof numOrStr === 'string' ? numOrStr : numOrStr.toString(16);
return hexToBytes(hex.padStart(114, '0'));
}
should('ed448/should verify recent signature', () => {
fc.assert(
fc.property(
fc.hexaString({ minLength: 2, maxLength: 57 }),
fc.bigInt(2n, ed.CURVE.n),
(message, privateKey) => {
const publicKey = ed.getPublicKey(to57Bytes(privateKey));
const signature = ed.sign(to57Bytes(message), to57Bytes(privateKey));
deepStrictEqual(publicKey.length, 57);
deepStrictEqual(signature.length, 114);
deepStrictEqual(ed.verify(signature, to57Bytes(message), publicKey), true);
}
),
{ numRuns: 5 }
);
});
should('ed448/should not verify signature with wrong message', () => {
fc.assert(
fc.property(
fc.array(fc.integer({ min: 0x00, max: 0xff })),
fc.array(fc.integer({ min: 0x00, max: 0xff })),
fc.bigInt(1n, ed.CURVE.n),
(bytes, wrongBytes, privateKey) => {
const message = new Uint8Array(bytes);
const wrongMessage = new Uint8Array(wrongBytes);
const priv = to57Bytes(privateKey);
const publicKey = ed.getPublicKey(priv);
const signature = ed.sign(message, priv);
deepStrictEqual(
ed.verify(signature, wrongMessage, publicKey),
bytes.toString() === wrongBytes.toString()
);
}
),
{ numRuns: 5 }
);
});
const privKey = to57Bytes('a665a45920422f9d417e4867ef');
const msg = hexToBytes('874f9960c5d2b7a9b5fad383e1ba44719ebb743a');
const wrongMsg = hexToBytes('589d8c7f1da0a24bc07b7381ad48b1cfc211af1c');
should('ed25519/basic methods/should sign and verify', () => {
const publicKey = ed.getPublicKey(privKey);
const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, msg, publicKey), true);
});
should('ed25519/basic methods/should not verify signature with wrong public key', () => {
const publicKey = ed.getPublicKey(12);
const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, msg, publicKey), false);
});
should('ed25519/basic methods/should not verify signature with wrong hash', () => {
const publicKey = ed.getPublicKey(privKey);
const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, wrongMsg, publicKey), false);
});
should('ed25519/sync methods/should sign and verify', () => {
const publicKey = ed.getPublicKey(privKey);
const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, msg, publicKey), true);
});
should('ed25519/sync methods/should not verify signature with wrong public key', async () => {
const publicKey = ed.getPublicKey(12);
const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, msg, publicKey), false);
});
should('ed25519/sync methods/should not verify signature with wrong hash', async () => {
const publicKey = ed.getPublicKey(privKey);
const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, wrongMsg, publicKey), false);
});
should('ed25519/BASE_POINT.multiply()/should throw Point#multiply on TEST 5', () => {
for (const num of [0n, 0, -1n, -1, 1.1]) {
throws(() => ed.Point.BASE.multiply(num));
}
});
should('input immutability: sign/verify are immutable', () => {
const privateKey = ed.utils.randomPrivateKey();
const publicKey = ed.getPublicKey(privateKey);
for (let i = 0; i < 100; i++) {
let payload = randomBytes(100);
let signature = ed.sign(payload, privateKey);
if (!ed.verify(signature, payload, publicKey)) {
throw new Error('Signature verification failed');
}
const signatureCopy = Buffer.alloc(signature.byteLength);
signatureCopy.set(signature, 0); // <-- breaks
payload = payload.slice();
signature = signature.slice();
if (!ed.verify(signatureCopy, payload, publicKey))
throw new Error('Copied signature verification failed');
}
});
{
for (let g = 0; g < ed448vectors.testGroups.length; g++) {
const group = ed448vectors.testGroups[g];
const key = group.key;
should(`Wycheproof/ED448(${g}, public)`, () => {
deepStrictEqual(hex(ed.getPublicKey(key.sk)), key.pk);
});
should(`Wycheproof/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(hex(ed.sign(v.msg, key.sk)), v.sig, index);
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');
}
});
}
}
// 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(`RFC7748: scalarMult (${i})`, () => {
deepStrictEqual(hex(x448.scalarMult(v.u, v.scalar)), v.outputU);
});
}
const rfc7748Iter = [
{
scalar:
'3f482c8a9f19b01e6c46ee9711d9dc14fd4bf67af30765c2ae2b846a4d23a8cd0db897086239492caf350b51f833868b9bc2b3bca9cf4113',
iters: 1,
},
{
scalar:
'aa3b4749d55b9daf1e5b00288826c467274ce3ebbdd5c17b975e09d4af6c67cf10d087202db88286e2b79fceea3ec353ef54faa26e219f38',
iters: 1000,
},
// { scalar: '077f453681caca3693198420bbe515cae0002472519b3e67661a7e89cab94695c8f4bcd66e61b9b9c946da8d524de3d69bd9d9d66b997e37', iters: 1000000 },
];
for (let i = 0; i < rfc7748Iter.length; i++) {
const { scalar, iters } = rfc7748Iter[i];
should(`RFC7748: scalarMult iteration (${i})`, () => {
let k = x448.Gu;
for (let i = 0, u = k; i < iters; i++) [k, u] = [x448.scalarMult(u, k), k];
deepStrictEqual(hex(k), scalar);
});
}
should('RFC7748 getSharedKey', () => {
const alicePrivate =
'9a8f4925d1519f5775cf46b04b5800d4ee9ee8bae8bc5565d498c28dd9c9baf574a9419744897391006382a6f127ab1d9ac2d8c0a598726b';
const alicePublic =
'9b08f7cc31b7e3e67d22d5aea121074a273bd2b83de09c63faa73d2c22c5d9bbc836647241d953d40c5b12da88120d53177f80e532c41fa0';
const bobPrivate =
'1c306a7ac2a0e2e0990b294470cba339e6453772b075811d8fad0d1d6927c120bb5ee8972b0d3e21374c9c921b09d1b0366f10b65173992d';
const bobPublic =
'3eb7a829b0cd20f5bcfc0b599b6feccf6da4627107bdb0d4f345b43027d8b972fc3e34fb4232a13ca706dcb57aec3dae07bdc1c67bf33609';
const shared =
'07fff4181ac6cc95ec1c16a94a0f74d12da232ce40a77552281d282bb60c0b56fd2464c335543936521c24403085d59a449a5037514a879d';
deepStrictEqual(alicePublic, hex(x448.getPublicKey(alicePrivate)));
deepStrictEqual(bobPublic, hex(x448.getPublicKey(bobPrivate)));
deepStrictEqual(hex(x448.scalarMult(bobPublic, alicePrivate)), shared);
deepStrictEqual(hex(x448.scalarMult(alicePublic, bobPrivate)), shared);
});
{
const group = x448vectors.testGroups[0];
should(`Wycheproof/X448`, () => {
for (let i = 0; i < group.tests.length; i++) {
const v = group.tests[i];
const index = `(${i}, ${v.result}) ${v.comment}`;
if (v.result === 'valid' || v.result === 'acceptable') {
try {
const shared = hex(x448.scalarMult(v.public, v.private));
deepStrictEqual(shared, v.shared, index);
} catch (e) {
// We are more strict
if (e.message.includes('Expected valid scalar')) return;
if (e.message.includes('Invalid private or public key received')) return;
if (e.message.includes('Expected 56 bytes')) return;
throw e;
}
} else if (v.result === 'invalid') {
let failed = false;
try {
x448.scalarMult(v.public, v.private);
} 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(ed.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);
});
}
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 } = ed448.Point.BASE;
const { P } = ed448.CURVE;
const invX = ed448.utils.invert(x * x, P); // x^2
const u = ed448.utils.mod(y * y * invX, P); // (y^2/x^2)
deepStrictEqual(hex(numberToBytesLE(u, 56)), x448.Gu);
});
// ESM is broken.
import url from 'url';
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
should.run();
}

View File

@@ -1,74 +0,0 @@
import { jubjub, findGroupHash } from '../lib/jubjub.js';
import { should } from 'micro-should';
import { deepStrictEqual, throws } from 'assert';
import { hexToBytes, bytesToHex } from '@noble/hashes/utils';
const G_SPEND = new jubjub.ExtendedPoint(
0x055f1f24f0f0512287e51c3c5a0a6903fc0baf8711de9eafd7c0e66f69d8d2dbn,
0x566178b2505fdd52132a5007d80a04652842e78ffb376897588f406278214ed7n,
0x0141fafa1f11088a3b2007c14d652375888f3b37838ba6bdffae096741ceddfen,
0x12eada93c0b7d595f5f04f5ebfb4b7d033ef2884136475cab5e41ce17db5be9cn
);
const G_PROOF = new jubjub.ExtendedPoint(
0x0174d54ce9fad258a2f8a86a1deabf15c7a2b51106b0fbcd9d29020f78936f71n,
0x16871d6d877dcd222e4ec3bccb3f37cb1865a2d37dd3a5dcbc032a69b62b4445n,
0x57a3cd31e496d82bd4aa78bd5ecd751cfb76d54a5d3f4560866379f9fc11c9b3n,
0x42cc53f6b519d1f4f52c47ff1256463a616c2c2f49ffe77765481eca04c72081n
);
const getXY = (p) => ({ x: p.x, y: p.y });
should('toHex/fromHex', () => {
// More than field
throws(() =>
jubjub.Point.fromHex(
new Uint8Array([
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
])
)
);
// Multiplicative generator (sqrt == null), not on curve.
throws(() =>
jubjub.Point.fromHex(
new Uint8Array([
7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0,
])
)
);
const tmp = jubjub.Point.fromHex(
new Uint8Array([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0,
])
);
deepStrictEqual(tmp.x, 0x8d51ccce760304d0ec030002760300000001000000000000n);
deepStrictEqual(tmp.y, 0n);
const S = G_SPEND.toAffine().toRawBytes();
const S2 = G_SPEND.double().toAffine().toRawBytes();
const P = G_PROOF.toAffine().toRawBytes();
const P2 = G_PROOF.double().toAffine().toRawBytes();
const S_exp = jubjub.Point.fromHex(S);
const S2_exp = jubjub.Point.fromHex(S2);
const P_exp = jubjub.Point.fromHex(P);
const P2_exp = jubjub.Point.fromHex(P2);
deepStrictEqual(getXY(G_SPEND.toAffine()), getXY(S_exp));
deepStrictEqual(getXY(G_SPEND.double().toAffine()), getXY(S2_exp));
deepStrictEqual(getXY(G_PROOF.toAffine()), getXY(P_exp));
deepStrictEqual(getXY(G_PROOF.double().toAffine()), getXY(P2_exp));
});
should('Find generators', () => {
const spend = findGroupHash(new Uint8Array(), new Uint8Array([90, 99, 97, 115, 104, 95, 71, 95]));
const proof = findGroupHash(new Uint8Array(), new Uint8Array([90, 99, 97, 115, 104, 95, 72, 95]));
deepStrictEqual(getXY(spend.toAffine()), getXY(G_SPEND.toAffine()));
deepStrictEqual(getXY(proof.toAffine()), getXY(G_PROOF.toAffine()));
});
// ESM is broken.
import url from 'url';
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
should.run();
}

View File

@@ -1,534 +0,0 @@
import * as fc from 'fast-check';
import { secp256k1, schnorr } from '../lib/secp256k1.js';
import { readFileSync } from 'fs';
import { default as ecdsa } from './vectors/ecdsa.json' assert { type: 'json' };
import { default as ecdh } from './vectors/ecdh.json' assert { type: 'json' };
import { default as privates } from './vectors/privates.json' assert { type: 'json' };
import { default as points } from './vectors/points.json' assert { type: 'json' };
import { default as wp } from './vectors/wychenproof.json' assert { type: 'json' };
import { should } from 'micro-should';
import { deepStrictEqual, throws } from 'assert';
import { hexToBytes, bytesToHex } from '@noble/hashes/utils';
const hex = bytesToHex;
const secp = secp256k1;
const privatesTxt = readFileSync('./test/vectors/privates-2.txt', 'utf-8');
const schCsv = readFileSync('./test/vectors/schnorr.csv', 'utf-8');
const FC_BIGINT = fc.bigInt(1n + 1n, secp.CURVE.n - 1n);
// prettier-ignore
const INVALID_ITEMS = ['deadbeef', Math.pow(2, 53), [1], 'xyzxyzxyxyzxyzxyxyzxyzxyxyzxyzxyxyzxyzxyxyzxyzxyxyzxyzxyxyzxyzxy', secp.CURVE.n + 2n];
const toBEHex = (n) => n.toString(16).padStart(64, '0');
function hexToNumber(hex) {
if (typeof hex !== 'string') {
throw new TypeError('hexToNumber: expected string, got ' + typeof hex);
}
// Big Endian
return BigInt(`0x${hex}`);
}
should('secp256k1.getPublicKey()', () => {
const data = privatesTxt
.split('\n')
.filter((line) => line)
.map((line) => line.split(':'));
for (let [priv, x, y] of data) {
const point = secp.Point.fromPrivateKey(BigInt(priv));
deepStrictEqual(toBEHex(point.x), x);
deepStrictEqual(toBEHex(point.y), y);
const point2 = secp.Point.fromHex(secp.getPublicKey(toBEHex(BigInt(priv))));
deepStrictEqual(toBEHex(point2.x), x);
deepStrictEqual(toBEHex(point2.y), y);
const point3 = secp.Point.fromHex(secp.getPublicKey(hexToBytes(toBEHex(BigInt(priv)))));
deepStrictEqual(toBEHex(point3.x), x);
deepStrictEqual(toBEHex(point3.y), y);
}
});
should('secp256k1.getPublicKey() rejects invalid keys', () => {
// for (const item of INVALID_ITEMS) {
// throws(() => secp.getPublicKey(item));
// }
});
should('secp256k1.precompute', () => {
secp.utils.precompute(4);
const data = privatesTxt
.split('\n')
.filter((line) => line)
.map((line) => line.split(':'));
for (let [priv, x, y] of data) {
const point = secp.Point.fromPrivateKey(BigInt(priv));
deepStrictEqual(toBEHex(point.x), x);
deepStrictEqual(toBEHex(point.y), y);
const point2 = secp.Point.fromHex(secp.getPublicKey(toBEHex(BigInt(priv))));
deepStrictEqual(toBEHex(point2.x), x);
deepStrictEqual(toBEHex(point2.y), y);
const point3 = secp.Point.fromHex(secp.getPublicKey(hexToBytes(toBEHex(BigInt(priv)))));
deepStrictEqual(toBEHex(point3.x), x);
deepStrictEqual(toBEHex(point3.y), y);
}
});
should('secp256k1.Point.isValidPoint()', () => {
for (const vector of points.valid.isPoint) {
const { P, expected } = vector;
if (expected) {
secp.Point.fromHex(P);
} else {
throws(() => secp.Point.fromHex(P));
}
}
});
should('secp256k1.Point.fromPrivateKey()', () => {
for (const vector of points.valid.pointFromScalar) {
const { d, expected } = vector;
let p = secp.Point.fromPrivateKey(d);
deepStrictEqual(p.toHex(true), expected);
}
});
should('secp256k1.Point#toHex(compressed)', () => {
for (const vector of points.valid.pointCompress) {
const { P, compress, expected } = vector;
let p = secp.Point.fromHex(P);
deepStrictEqual(p.toHex(compress), expected);
}
});
should('secp256k1.Point#toHex() roundtrip (failed case)', () => {
const point1 =
secp.Point.fromPrivateKey(
88572218780422190464634044548753414301110513745532121983949500266768436236425n
);
// const hex = point1.toHex(true);
// deepStrictEqual(secp.Point.fromHex(hex).toHex(true), hex);
});
should('secp256k1.Point#toHex() roundtrip', () => {
fc.assert(
fc.property(FC_BIGINT, (x) => {
const point1 = secp.Point.fromPrivateKey(x);
const hex = point1.toHex(true);
deepStrictEqual(secp.Point.fromHex(hex).toHex(true), hex);
})
);
});
should('secp256k1.Point#add(other)', () => {
for (const vector of points.valid.pointAdd) {
const { P, Q, expected } = vector;
let p = secp.Point.fromHex(P);
let q = secp.Point.fromHex(Q);
if (expected) {
deepStrictEqual(p.add(q).toHex(true), expected);
} else {
if (!p.equals(q.negate())) {
throws(() => p.add(q).toHex(true));
}
}
}
});
should('secp256k1.Point#multiply(privateKey)', () => {
for (const vector of points.valid.pointMultiply) {
const { P, d, expected } = vector;
const p = secp.Point.fromHex(P);
if (expected) {
deepStrictEqual(p.multiply(hexToNumber(d)).toHex(true), expected);
} else {
throws(() => {
p.multiply(hexToNumber(d)).toHex(true);
});
}
}
for (const vector of points.invalid.pointMultiply) {
const { P, d } = vector;
if (hexToNumber(d) < secp.CURVE.n) {
throws(() => {
const p = secp.Point.fromHex(P);
p.multiply(hexToNumber(d)).toHex(true);
});
}
}
for (const num of [0n, 0, -1n, -1, 1.1]) {
throws(() => secp.Point.BASE.multiply(num));
}
});
// multiply() should equal multiplyUnsafe()
// should('JacobianPoint#multiplyUnsafe', () => {
// const p0 = new secp.JacobianPoint(
// 55066263022277343669578718895168534326250603453777594175500187360389116729240n,
// 32670510020758816978083085130507043184471273380659243275938904335757337482424n,
// 1n
// );
// const z = 106011723082030650010038151861333186846790370053628296836951575624442507889495n;
// console.log(p0.multiply(z));
// console.log(secp.JacobianPoint.normalizeZ([p0.multiplyUnsafe(z)])[0])
// });
should('secp256k1.Signature.fromCompactHex() roundtrip', () => {
fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, (r, s) => {
const sig = new secp.Signature(r, s);
deepStrictEqual(secp.Signature.fromCompact(sig.toCompactHex()), sig);
})
);
});
should('secp256k1.Signature.fromDERHex() roundtrip', () => {
fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, (r, s) => {
const sig = new secp.Signature(r, s);
deepStrictEqual(secp.Signature.fromDER(sig.toDERHex()), sig);
})
);
});
should('secp256k1.sign()/should create deterministic signatures with RFC 6979', () => {
for (const vector of ecdsa.valid) {
let usig = secp.sign(vector.m, vector.d);
let sig = usig.toCompactHex();
const vsig = vector.signature;
deepStrictEqual(sig.slice(0, 64), vsig.slice(0, 64));
deepStrictEqual(sig.slice(64, 128), vsig.slice(64, 128));
}
});
should('secp256k1.sign()/should not create invalid deterministic signatures with RFC 6979', () => {
for (const vector of ecdsa.invalid.sign) {
throws(() => secp.sign(vector.m, vector.d));
}
});
should('secp256k1.sign()/edge cases', () => {
throws(() => secp.sign());
throws(() => secp.sign(''));
});
should('secp256k1.sign()/should create correct DER encoding against libsecp256k1', () => {
const CASES = [
[
'd1a9dc8ed4e46a6a3e5e594615ca351d7d7ef44df1e4c94c1802f3592183794b',
'304402203de2559fccb00c148574997f660e4d6f40605acc71267ee38101abf15ff467af02200950abdf40628fd13f547792ba2fc544681a485f2fdafb5c3b909a4df7350e6b',
],
[
'5f97983254982546d3976d905c6165033976ee449d300d0e382099fa74deaf82',
'3045022100c046d9ff0bd2845b9aa9dff9f997ecebb31e52349f80fe5a5a869747d31dcb88022011f72be2a6d48fe716b825e4117747b397783df26914a58139c3f4c5cbb0e66c',
],
[
'0d7017a96b97cd9be21cf28aada639827b2814a654a478c81945857196187808',
'3045022100d18990bba7832bb283e3ecf8700b67beb39acc73f4200ed1c331247c46edccc602202e5c8bbfe47ae159512c583b30a3fa86575cddc62527a03de7756517ae4c6c73',
],
];
const privKey = hexToBytes('0101010101010101010101010101010101010101010101010101010101010101');
for (const [msg, exp] of CASES) {
const res = secp.sign(msg, privKey, { extraEntropy: undefined });
deepStrictEqual(res.toDERHex(), exp);
const rs = secp.Signature.fromDER(res.toDERHex()).toCompactHex();
deepStrictEqual(secp.Signature.fromCompact(rs).toDERHex(), exp);
}
});
should('secp256k1.sign()/sign ecdsa extraData', () => {
const ent1 = '0000000000000000000000000000000000000000000000000000000000000000';
const ent2 = '0000000000000000000000000000000000000000000000000000000000000001';
const ent3 = '6e723d3fd94ed5d2b6bdd4f123364b0f3ca52af829988a63f8afe91d29db1c33';
const ent4 = 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141';
const ent5 = 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff';
for (const e of ecdsa.extraEntropy) {
const sign = (extraEntropy) => {
const s = secp.sign(e.m, e.d, { extraEntropy }).toCompactHex();
return s;
};
deepStrictEqual(sign(), e.signature);
deepStrictEqual(sign(ent1), e.extraEntropy0);
deepStrictEqual(sign(ent2), e.extraEntropy1);
deepStrictEqual(sign(ent3), e.extraEntropyRand);
deepStrictEqual(sign(ent4), e.extraEntropyN);
deepStrictEqual(sign(ent5), e.extraEntropyMax);
}
});
should('secp256k1.verify()/should verify signature', () => {
const MSG = '01'.repeat(32);
const PRIV_KEY = 0x2n;
const signature = secp.sign(MSG, PRIV_KEY);
const publicKey = secp.getPublicKey(PRIV_KEY);
deepStrictEqual(publicKey.length, 65);
deepStrictEqual(secp.verify(signature, MSG, publicKey), true);
});
should('secp256k1.verify()/should not verify signature with wrong public key', () => {
const MSG = '01'.repeat(32);
const PRIV_KEY = 0x2n;
const WRONG_PRIV_KEY = 0x22n;
const signature = secp.sign(MSG, PRIV_KEY);
const publicKey = secp.Point.fromPrivateKey(WRONG_PRIV_KEY).toHex();
deepStrictEqual(publicKey.length, 130);
deepStrictEqual(secp.verify(signature, MSG, publicKey), false);
});
should('secp256k1.verify()/should not verify signature with wrong hash', () => {
const MSG = '01'.repeat(32);
const PRIV_KEY = 0x2n;
const WRONG_MSG = '11'.repeat(32);
const signature = secp.sign(MSG, PRIV_KEY);
const publicKey = secp.getPublicKey(PRIV_KEY);
deepStrictEqual(publicKey.length, 65);
deepStrictEqual(secp.verify(signature, WRONG_MSG, publicKey), false);
});
should('secp256k1.verify()/should verify random signatures', () =>
fc.assert(
fc.property(FC_BIGINT, fc.hexaString({ minLength: 64, maxLength: 64 }), (privKey, msg) => {
const pub = secp.getPublicKey(privKey);
const sig = secp.sign(msg, privKey);
deepStrictEqual(secp.verify(sig, msg, pub), true);
})
)
);
should('secp256k1.verify()/should not verify signature with invalid r/s', () => {
const msg = new Uint8Array([
0xbb, 0x5a, 0x52, 0xf4, 0x2f, 0x9c, 0x92, 0x61, 0xed, 0x43, 0x61, 0xf5, 0x94, 0x22, 0xa1, 0xe3,
0x00, 0x36, 0xe7, 0xc3, 0x2b, 0x27, 0x0c, 0x88, 0x07, 0xa4, 0x19, 0xfe, 0xca, 0x60, 0x50, 0x23,
]);
const x = 100260381870027870612475458630405506840396644859280795015145920502443964769584n;
const y = 41096923727651821103518389640356553930186852801619204169823347832429067794568n;
const r = 1n;
const s = 115792089237316195423570985008687907852837564279074904382605163141518162728904n;
const pub = new secp.Point(x, y);
const signature = new secp.Signature(2n, 2n);
signature.r = r;
signature.s = s;
const verified = secp.verify(signature, msg, pub);
// Verifies, but it shouldn't, because signature S > curve order
deepStrictEqual(verified, false);
});
should('secp256k1.verify()/should not verify msg = curve order', () => {
const msg = 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141';
const x = 55066263022277343669578718895168534326250603453777594175500187360389116729240n;
const y = 32670510020758816978083085130507043184471273380659243275938904335757337482424n;
const r = 104546003225722045112039007203142344920046999340768276760147352389092131869133n;
const s = 96900796730960181123786672629079577025401317267213807243199432755332205217369n;
const pub = new secp.Point(x, y);
const sig = new secp.Signature(r, s);
deepStrictEqual(secp.verify(sig, msg, pub), false);
});
should('secp256k1.verify()/should verify non-strict msg bb5a...', () => {
const msg = 'bb5a52f42f9c9261ed4361f59422a1e30036e7c32b270c8807a419feca605023';
const x = 3252872872578928810725465493269682203671229454553002637820453004368632726370n;
const y = 17482644437196207387910659778872952193236850502325156318830589868678978890912n;
const r = 432420386565659656852420866390673177323n;
const s = 115792089237316195423570985008687907852837564279074904382605163141518161494334n;
const pub = new secp.Point(x, y);
const sig = new secp.Signature(r, s);
deepStrictEqual(secp.verify(sig, msg, pub, { strict: false }), true);
});
should(
'secp256k1.verify()/should not verify invalid deterministic signatures with RFC 6979',
() => {
for (const vector of ecdsa.invalid.verify) {
const res = secp.verify(vector.signature, vector.m, vector.Q);
deepStrictEqual(res, false);
}
}
);
// index,secret key,public key,aux_rand,message,signature,verification result,comment
const vectors = schCsv
.split('\n')
.map((line) => line.split(','))
.slice(1, -1);
for (let vec of vectors) {
const [index, sec, pub, rnd, msg, expSig, passes, comment] = vec;
should(`sign with Schnorr scheme vector ${index}`, () => {
if (sec) {
deepStrictEqual(hex(schnorr.getPublicKey(sec)), pub.toLowerCase());
const sig = schnorr.sign(msg, sec, rnd);
deepStrictEqual(hex(sig), expSig.toLowerCase());
deepStrictEqual(schnorr.verify(sig, msg, pub), true);
} else {
const passed = schnorr.verify(expSig, msg, pub);
deepStrictEqual(passed, passes === 'TRUE');
}
});
}
should('secp256k1.recoverPublicKey()/should recover public key from recovery bit', () => {
const message = '00000000000000000000000000000000000000000000000000000000deadbeef';
const privateKey = 123456789n;
const publicKey = secp.Point.fromHex(secp.getPublicKey(privateKey)).toHex(false);
const sig = secp.sign(message, privateKey);
const recoveredPubkey = sig.recoverPublicKey(message);
// const recoveredPubkey = secp.recoverPublicKey(message, signature, recovery);
deepStrictEqual(recoveredPubkey !== null, true);
deepStrictEqual(recoveredPubkey.toHex(), publicKey);
deepStrictEqual(secp.verify(sig, message, publicKey), true);
});
should('secp256k1.recoverPublicKey()/should not recover zero points', () => {
const msgHash = '6b8d2c81b11b2d699528dde488dbdf2f94293d0d33c32e347f255fa4a6c1f0a9';
const sig =
'79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817986b8d2c81b11b2d699528dde488dbdf2f94293d0d33c32e347f255fa4a6c1f0a9';
const recovery = 0;
throws(() => secp.recoverPublicKey(msgHash, sig, recovery));
});
should('secp256k1.recoverPublicKey()/should handle all-zeros msghash', () => {
const privKey = secp.utils.randomPrivateKey();
const pub = secp.getPublicKey(privKey);
const zeros = '0000000000000000000000000000000000000000000000000000000000000000';
const sig = secp.sign(zeros, privKey, { recovered: true });
const recoveredKey = sig.recoverPublicKey(zeros);
deepStrictEqual(recoveredKey.toRawBytes(), pub);
});
should('secp256k1.recoverPublicKey()/should handle RFC 6979 vectors', () => {
for (const vector of ecdsa.valid) {
if (secp.utils.mod(hexToNumber(vector.m), secp.CURVE.n) === 0n) continue;
let usig = secp.sign(vector.m, vector.d);
let sig = usig.toDERHex();
const vpub = secp.getPublicKey(vector.d);
const recovered = usig.recoverPublicKey(vector.m);
deepStrictEqual(recovered.toHex(), hex(vpub));
}
});
// TODO: Real implementation.
function derToPub(der) {
return der.slice(46);
}
should('secp256k1.getSharedSecret()/should produce correct results', () => {
// TODO: Once der is there, run all tests.
for (const vector of ecdh.testGroups[0].tests.slice(0, 230)) {
if (vector.result === 'invalid' || vector.private.length !== 64) {
// We support eth-like hexes
if (vector.private.length < 64) continue;
throws(() => {
secp.getSharedSecret(vector.private, derToPub(vector.public), true);
});
} else if (vector.result === 'valid') {
const res = secp.getSharedSecret(vector.private, derToPub(vector.public), true);
deepStrictEqual(hex(res.slice(1)), `${vector.shared}`);
}
}
});
should('secp256k1.getSharedSecret()/priv/pub order matters', () => {
for (const vector of ecdh.testGroups[0].tests.slice(0, 100)) {
if (vector.result === 'valid') {
let priv = vector.private;
priv = priv.length === 66 ? priv.slice(2) : priv;
throws(() => secp.getSharedSecret(derToPub(vector.public), priv, true));
}
}
});
should('secp256k1.getSharedSecret()/rejects invalid keys', () => {
throws(() => secp.getSharedSecret('01', '02'));
});
should('secp256k1.utils.isValidPrivateKey()', () => {
for (const vector of privates.valid.isPrivate) {
const { d, expected } = vector;
deepStrictEqual(secp.utils.isValidPrivateKey(d), expected);
}
});
const normal = secp.utils._normalizePrivateKey;
const tweakUtils = {
privateAdd: (privateKey, tweak) => {
const p = normal(privateKey);
const t = normal(tweak);
return secp.utils._bigintToBytes(secp.utils.mod(p + t, secp.CURVE.n));
},
privateNegate: (privateKey) => {
const p = normal(privateKey);
return secp.utils._bigintToBytes(secp.CURVE.n - p);
},
pointAddScalar: (p, tweak, isCompressed) => {
const P = secp.Point.fromHex(p);
const t = normal(tweak);
const Q = secp.Point.BASE.multiplyAndAddUnsafe(P, t, 1n);
if (!Q) throw new Error('Tweaked point at infinity');
return Q.toRawBytes(isCompressed);
},
pointMultiply: (p, tweak, isCompressed) => {
const P = secp.Point.fromHex(p);
const h = typeof tweak === 'string' ? tweak : bytesToHex(tweak);
const t = BigInt(`0x${h}`);
return P.multiply(t).toRawBytes(isCompressed);
},
};
should('secp256k1.privateAdd()', () => {
for (const vector of privates.valid.add) {
const { a, b, expected } = vector;
deepStrictEqual(bytesToHex(tweakUtils.privateAdd(a, b)), expected);
}
});
should('secp256k1.privateNegate()', () => {
for (const vector of privates.valid.negate) {
const { a, expected } = vector;
deepStrictEqual(bytesToHex(tweakUtils.privateNegate(a)), expected);
}
});
should('secp256k1.pointAddScalar()', () => {
for (const vector of points.valid.pointAddScalar) {
const { description, P, d, expected } = vector;
const compressed = !!expected && expected.length === 66; // compressed === 33 bytes
deepStrictEqual(bytesToHex(tweakUtils.pointAddScalar(P, d, compressed)), expected);
}
});
should('secp256k1.pointAddScalar() invalid', () => {
for (const vector of points.invalid.pointAddScalar) {
const { P, d, exception } = vector;
throws(() => tweakUtils.pointAddScalar(P, d));
}
});
should('secp256k1.pointMultiply()', () => {
for (const vector of points.valid.pointMultiply) {
const { P, d, expected } = vector;
deepStrictEqual(bytesToHex(tweakUtils.pointMultiply(P, d, true)), expected);
}
});
should('secp256k1.pointMultiply() invalid', () => {
for (const vector of points.invalid.pointMultiply) {
const { P, d, exception } = vector;
throws(() => tweakUtils.pointMultiply(P, d));
}
});
should('secp256k1.wychenproof vectors', () => {
for (let group of wp.testGroups) {
const pubKey = secp.Point.fromHex(group.key.uncompressed);
for (let test of group.tests) {
const m = secp.CURVE.hash(hexToBytes(test.msg));
if (test.result === 'valid' || test.result === 'acceptable') {
const verified = secp.verify(test.sig, m, pubKey);
if (secp.Signature.fromDER(test.sig).hasHighS()) {
deepStrictEqual(verified, false);
} else {
deepStrictEqual(verified, true);
}
} else if (test.result === 'invalid') {
let failed = false;
try {
const verified = secp.verify(test.sig, m, pubKey);
if (!verified) failed = true;
} catch (error) {
failed = true;
}
deepStrictEqual(failed, true);
} else {
deepStrictEqual(false, true);
}
}
}
});
should.run();

View File

@@ -1,200 +0,0 @@
import { deepStrictEqual, throws } from 'assert';
import { should } from 'micro-should';
import * as starknet from '../../lib/stark.js';
import { default as issue2 } from './fixtures/issue2.json' assert { type: 'json' };
should('Basic elliptic sanity check', () => {
const g1 = starknet.Point.BASE;
deepStrictEqual(
g1.x.toString(16),
'1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca'
);
deepStrictEqual(
g1.y.toString(16),
'5668060aa49730b7be4801df46ec62de53ecd11abe43a32873000c36e8dc1f'
);
const g2 = g1.double();
deepStrictEqual(
g2.x.toString(16),
'759ca09377679ecd535a81e83039658bf40959283187c654c5416f439403cf5'
);
deepStrictEqual(
g2.y.toString(16),
'6f524a3400e7708d5c01a28598ad272e7455aa88778b19f93b562d7a9646c41'
);
const g3 = g2.add(g1);
deepStrictEqual(
g3.x.toString(16),
'411494b501a98abd8262b0da1351e17899a0c4ef23dd2f96fec5ba847310b20'
);
deepStrictEqual(
g3.y.toString(16),
'7e1b3ebac08924d2c26f409549191fcf94f3bf6f301ed3553e22dfb802f0686'
);
const g32 = g1.multiply(3);
deepStrictEqual(
g32.x.toString(16),
'411494b501a98abd8262b0da1351e17899a0c4ef23dd2f96fec5ba847310b20'
);
deepStrictEqual(
g32.y.toString(16),
'7e1b3ebac08924d2c26f409549191fcf94f3bf6f301ed3553e22dfb802f0686'
);
const minus1 = g1.multiply(starknet.CURVE.n - 1n);
deepStrictEqual(
minus1.x.toString(16),
'1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca'
);
deepStrictEqual(
minus1.y.toString(16),
'7a997f9f55b68e04841b7fe20b9139d21ac132ee541bc5cd78cfff3c91723e2'
);
});
should('Pedersen', () => {
deepStrictEqual(
starknet.pedersen(2, 3),
'0x5774fa77b3d843ae9167abd61cf80365a9b2b02218fc2f628494b5bdc9b33b8'
);
deepStrictEqual(
starknet.pedersen(1, 2),
'0x5bb9440e27889a364bcb678b1f679ecd1347acdedcbf36e83494f857cc58026'
);
deepStrictEqual(
starknet.pedersen(3, 4),
'0x262697b88544f733e5c6907c3e1763131e9f14c51ee7951258abbfb29415fbf'
);
});
should('Hash chain', () => {
deepStrictEqual(
starknet.hashChain([1, 2, 3]),
'0x5d9d62d4040b977c3f8d2389d494e4e89a96a8b45c44b1368f1cc6ec5418915'
);
});
should('Pedersen hash edgecases', () => {
// >>> pedersen_hash(0,0)
const zero = '0x49ee3eba8c1600700ee1b87eb599f16716b0b1022947733551fde4050ca6804';
deepStrictEqual(starknet.pedersen(0, 0), zero);
deepStrictEqual(starknet.pedersen(0n, 0n), zero);
deepStrictEqual(starknet.pedersen('0', '0'), zero);
deepStrictEqual(starknet.pedersen('0x0', '0x0'), zero);
// >>> pedersen_hash(3618502788666131213697322783095070105623107215331596699973092056135872020475,3618502788666131213697322783095070105623107215331596699973092056135872020475)
// 3226051580231087455100099637526672350308978851161639703631919449959447036451
const big = 3618502788666131213697322783095070105623107215331596699973092056135872020475n;
const bigExp = '0x721e167a36655994e88efa865e2ed8a0488d36db4d988fec043cda755728223';
deepStrictEqual(starknet.pedersen(big, big), bigExp);
// >= FIELD
const big2 = 36185027886661312136973227830950701056231072153315966999730920561358720204751n;
throws(() => starknet.pedersen(big2, big2), 'big2');
// FIELD -1
const big3 = 3618502788666131213697322783095070105623107215331596699973092056135872020480n;
const big3exp = '0x7258fccaf3371fad51b117471d9d888a1786c5694c3e6099160477b593a576e';
deepStrictEqual(starknet.pedersen(big3, big3), big3exp, 'big3');
// FIELD
const big4 = 3618502788666131213697322783095070105623107215331596699973092056135872020481n;
throws(() => starknet.pedersen(big4, big4), 'big4');
throws(() => starknet.pedersen(-1, -1), 'neg');
throws(() => starknet.pedersen(false, false), 'false');
throws(() => starknet.pedersen(true, true), 'true');
throws(() => starknet.pedersen(10.1, 10.1), 'float');
});
should('hashChain edgecases', () => {
deepStrictEqual(starknet.hashChain([32312321312321312312312321n]), '0x1aba6672c014b4838cc201');
deepStrictEqual(
starknet.hashChain([1n, 2n]),
'0x5bb9440e27889a364bcb678b1f679ecd1347acdedcbf36e83494f857cc58026'
);
deepStrictEqual(
starknet.hashChain([1, 2]),
'0x5bb9440e27889a364bcb678b1f679ecd1347acdedcbf36e83494f857cc58026'
);
throws(() => starknet.hashChain([]));
throws(() => starknet.hashChain('123'));
deepStrictEqual(
starknet.hashChain([1, 2]),
'0x5bb9440e27889a364bcb678b1f679ecd1347acdedcbf36e83494f857cc58026'
);
});
should('Pedersen hash, issue #2', () => {
// Verified with starnet.js
deepStrictEqual(
starknet.computeHashOnElements(issue2),
'0x22064462ea33a6ce5272a295e0f551c5da3834f80d8444e7a4df68190b1bc42'
);
deepStrictEqual(
starknet.computeHashOnElements([]),
'0x49ee3eba8c1600700ee1b87eb599f16716b0b1022947733551fde4050ca6804'
);
deepStrictEqual(
starknet.computeHashOnElements([1]),
'0x78d74f61aeaa8286418fd34b3a12a610445eba11d00ecc82ecac2542d55f7a4'
);
});
import * as bip32 from '@scure/bip32';
import * as bip39 from '@scure/bip39';
should('Seed derivation (example)', () => {
const layer = 'starkex';
const application = 'starkdeployement';
const mnemonic =
'range mountain blast problem vibrant void vivid doctor cluster enough melody ' +
'salt layer language laptop boat major space monkey unit glimpse pause change vibrant';
const ethAddress = '0xa4864d977b944315389d1765ffa7e66F74ee8cd7';
const hdKey = bip32.HDKey.fromMasterSeed(bip39.mnemonicToSeedSync(mnemonic)).derive(
starknet.getAccountPath(layer, application, ethAddress, 0)
);
deepStrictEqual(
starknet.grindKey(hdKey.privateKey),
'6cf0a8bf113352eb863157a45c5e5567abb34f8d32cddafd2c22aa803f4892c'
);
});
should('Compressed keys', () => {
const G = starknet.Point.BASE;
const half = starknet.CURVE.n / 2n;
const last = starknet.CURVE.n;
const vectors = [
1,
2,
3,
4,
5,
half - 5n,
half - 4n,
half - 3n,
half - 2n,
half - 1n,
half,
half + 1n,
half + 2n,
half + 3n,
half + 4n,
half + 5n,
last - 5n,
last - 4n,
last - 3n,
last - 2n,
last - 1n,
].map((i) => G.multiply(i));
const fixPoint = (pt) => ({ ...pt, _WINDOW_SIZE: undefined });
for (const v of vectors) {
const uncompressed = v.toHex();
const compressed = v.toHex(true);
const exp = fixPoint(v);
deepStrictEqual(fixPoint(starknet.Point.fromHex(uncompressed)), exp);
deepStrictEqual(fixPoint(starknet.Point.fromHex(compressed)), exp);
deepStrictEqual(starknet.Point.fromHex(compressed).toHex(), uncompressed);
}
});
// ESM is broken.
import url from 'url';
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
should.run();
}

View File

@@ -1,51 +0,0 @@
import { deepStrictEqual, throws } from 'assert';
import { should } from 'micro-should';
import * as starknet from '../../lib/stark.js';
import * as fc from 'fast-check';
const FC_BIGINT = fc.bigInt(1n + 1n, starknet.CURVE.n - 1n);
should('Point#toHex() roundtrip', () => {
fc.assert(
fc.property(FC_BIGINT, (x) => {
const point1 = starknet.Point.fromPrivateKey(x);
const hex = point1.toHex(true);
deepStrictEqual(starknet.Point.fromHex(hex).toHex(true), hex);
})
);
});
should('Signature.fromCompactHex() roundtrip', () => {
fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, (r, s) => {
const sig = new starknet.Signature(r, s);
deepStrictEqual(starknet.Signature.fromCompact(sig.toCompactHex()), sig);
})
);
});
should('Signature.fromDERHex() roundtrip', () => {
fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, (r, s) => {
const sig = new starknet.Signature(r, s);
deepStrictEqual(starknet.Signature.fromDER(sig.toDERHex()), sig);
})
);
});
should('verify()/should verify random signatures', () =>
fc.assert(
fc.asyncProperty(FC_BIGINT, fc.hexaString({ minLength: 64, maxLength: 64 }), (privNum, msg) => {
const privKey = privNum.toString(16).padStart(64, '0');
const pub = starknet.getPublicKey(privKey);
const sig = starknet.sign(msg, privKey);
deepStrictEqual(starknet.verify(sig, msg, pub), true);
})
)
);
// ESM is broken.
import url from 'url';
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
should.run();
}

View File

@@ -1,286 +0,0 @@
import { deepStrictEqual, throws } from 'assert';
import { should } from 'micro-should';
import { hex, utf8 } from '@scure/base';
import * as bip32 from '@scure/bip32';
import * as bip39 from '@scure/bip39';
import * as starknet from '../../lib/stark.js';
import { default as sigVec } from './fixtures/rfc6979_signature_test_vector.json' assert { type: 'json' };
import { default as precomputedKeys } from './fixtures/keys_precomputed.json' assert { type: 'json' };
should('Starknet keccak', () => {
const value = starknet.keccak(utf8.decode('hello'));
deepStrictEqual(value, 0x8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8n);
deepStrictEqual(value < 2n ** 250n, true);
});
should('RFC6979', () => {
for (const msg of sigVec.messages) {
const { r, s } = starknet.sign(msg.hash, sigVec.private_key);
// const { r, s } = starknet.Signature.fromDER(sig);
deepStrictEqual(r.toString(10), msg.r);
deepStrictEqual(s.toString(10), msg.s);
}
});
should('Signatures', () => {
const vectors = [
{
// Message hash of length 61.
msg: 'c465dd6b1bbffdb05442eb17f5ca38ad1aa78a6f56bf4415bdee219114a47',
r: '5f496f6f210b5810b2711c74c15c05244dad43d18ecbbdbe6ed55584bc3b0a2',
s: '4e8657b153787f741a67c0666bad6426c3741b478c8eaa3155196fc571416f3',
},
{
// Message hash of length 61, with leading zeros.
msg: '00c465dd6b1bbffdb05442eb17f5ca38ad1aa78a6f56bf4415bdee219114a47',
r: '5f496f6f210b5810b2711c74c15c05244dad43d18ecbbdbe6ed55584bc3b0a2',
s: '4e8657b153787f741a67c0666bad6426c3741b478c8eaa3155196fc571416f3',
},
{
// Message hash of length 62.
msg: 'c465dd6b1bbffdb05442eb17f5ca38ad1aa78a6f56bf4415bdee219114a47a',
r: '233b88c4578f0807b4a7480c8076eca5cfefa29980dd8e2af3c46a253490e9c',
s: '28b055e825bc507349edfb944740a35c6f22d377443c34742c04e0d82278cf1',
},
{
// Message hash of length 63.
msg: '7465dd6b1bbffdb05442eb17f5ca38ad1aa78a6f56bf4415bdee219114a47a1',
r: 'b6bee8010f96a723f6de06b5fa06e820418712439c93850dd4e9bde43ddf',
s: '1a3d2bc954ed77e22986f507d68d18115fa543d1901f5b4620db98e2f6efd80',
},
];
const privateKey = '2dccce1da22003777062ee0870e9881b460a8b7eca276870f57c601f182136c';
const publicKey = starknet.getPublicKey(privateKey);
for (const v of vectors) {
const sig = starknet.sign(v.msg, privateKey);
const { r, s } = sig;
// const { r, s } = starknet.Signature.fromDER(sig);
deepStrictEqual(r.toString(16), v.r, 'r equality');
deepStrictEqual(s.toString(16), v.s, 's equality');
deepStrictEqual(starknet.verify(sig, v.msg, publicKey), true, 'verify');
}
});
should('Invalid signatures', () => {
/*
it('should not verify invalid signature inputs lengths', () => {
const ecOrder = starkwareCrypto.ec.n;
const {maxEcdsaVal} = starkwareCrypto;
const maxMsgHash = maxEcdsaVal.sub(oneBn);
const maxR = maxEcdsaVal.sub(oneBn);
const maxS = ecOrder.sub(oneBn).sub(oneBn);
const maxStarkKey = maxEcdsaVal.sub(oneBn);
// Test invalid message length.
expect(() =>
starkwareCrypto.verify(maxStarkKey, maxMsgHash.add(oneBn).toString(16), {
r: maxR,
s: maxS
})
).to.throw('Message not signable, invalid msgHash length.');
// Test invalid r length.
expect(() =>
starkwareCrypto.verify(maxStarkKey, maxMsgHash.toString(16), {
r: maxR.add(oneBn),
s: maxS
})
).to.throw('Message not signable, invalid r length.');
// Test invalid w length.
expect(() =>
starkwareCrypto.verify(maxStarkKey, maxMsgHash.toString(16), {
r: maxR,
s: maxS.add(oneBn)
})
).to.throw('Message not signable, invalid w length.');
// Test invalid s length.
expect(() =>
starkwareCrypto.verify(maxStarkKey, maxMsgHash.toString(16), {
r: maxR,
s: maxS.add(oneBn).add(oneBn)
})
).to.throw('Message not signable, invalid s length.');
});
it('should not verify invalid signatures', () => {
const privKey = generateRandomStarkPrivateKey();
const keyPair = starkwareCrypto.ec.keyFromPrivate(privKey, 'hex');
const keyPairPub = starkwareCrypto.ec.keyFromPublic(
keyPair.getPublic(),
'BN'
);
const msgHash = new BN(randomHexString(61));
const msgSignature = starkwareCrypto.sign(keyPair, msgHash);
// Test invalid public key.
const invalidKeyPairPub = starkwareCrypto.ec.keyFromPublic(
{x: keyPairPub.pub.getX().add(oneBn), y: keyPairPub.pub.getY()},
'BN'
);
expect(
starkwareCrypto.verify(
invalidKeyPairPub,
msgHash.toString(16),
msgSignature
)
).to.be.false;
// Test invalid message.
expect(
starkwareCrypto.verify(
keyPair,
msgHash.add(oneBn).toString(16),
msgSignature
)
).to.be.false;
expect(
starkwareCrypto.verify(
keyPairPub,
msgHash.add(oneBn).toString(16),
msgSignature
)
).to.be.false;
// Test invalid r.
msgSignature.r.iadd(oneBn);
expect(starkwareCrypto.verify(keyPair, msgHash.toString(16), msgSignature))
.to.be.false;
expect(
starkwareCrypto.verify(keyPairPub, msgHash.toString(16), msgSignature)
).to.be.false;
// Test invalid s.
msgSignature.r.isub(oneBn);
msgSignature.s.iadd(oneBn);
expect(starkwareCrypto.verify(keyPair, msgHash.toString(16), msgSignature))
.to.be.false;
expect(
starkwareCrypto.verify(keyPairPub, msgHash.toString(16), msgSignature)
).to.be.false;
});
});
*/
});
should('Pedersen', () => {
deepStrictEqual(
starknet.pedersen(
'0x3d937c035c878245caf64531a5756109c53068da139362728feb561405371cb',
'0x208a0a10250e382e1e4bbe2880906c2791bf6275695e02fbbc6aeff9cd8b31a'
),
'0x30e480bed5fe53fa909cc0f8c4d99b8f9f2c016be4c41e13a4848797979c662'
);
deepStrictEqual(
starknet.pedersen(
'0x58f580910a6ca59b28927c08fe6c43e2e303ca384badc365795fc645d479d45',
'0x78734f65a067be9bdb39de18434d71e79f7b6466a4b66bbd979ab9e7515fe0b'
),
'0x68cc0b76cddd1dd4ed2301ada9b7c872b23875d5ff837b3a87993e0d9996b87'
);
});
should('Hash chain', () => {
deepStrictEqual(starknet.hashChain([1, 2, 3]), starknet.pedersen(1, starknet.pedersen(2, 3)));
});
should('Key grinding', () => {
deepStrictEqual(
starknet.grindKey('86F3E7293141F20A8BAFF320E8EE4ACCB9D4A4BF2B4D295E8CEE784DB46E0519'),
'5c8c8683596c732541a59e03007b2d30dbbbb873556fe65b5fb63c16688f941'
);
// Loops more than once (verified manually)
deepStrictEqual(
starknet.grindKey('94F3E7293141F20A8BAFF320E8EE4ACCB9D4A4BF2B4D295E8CEE784DB46E0595'),
'33880b9aba464c1c01c9f8f5b4fc1134698f9b0a8d18505cab6cdd34d93dc02'
);
});
should('Private to stark key', () => {
deepStrictEqual(
starknet.getStarkKey('0x178047D3869489C055D7EA54C014FFB834A069C9595186ABE04EA4D1223A03F'),
'0x1895a6a77ae14e7987b9cb51329a5adfb17bd8e7c638f92d6892d76e51cebcf'
);
for (const [privKey, expectedPubKey] of Object.entries(precomputedKeys)) {
deepStrictEqual(starknet.getStarkKey(privKey), expectedPubKey);
}
});
should('Private stark key from eth signature', () => {
const ethSignature =
'0x21fbf0696d5e0aa2ef41a2b4ffb623bcaf070461d61cf7251c74161f82fec3a43' +
'70854bc0a34b3ab487c1bc021cd318c734c51ae29374f2beb0e6f2dd49b4bf41c';
deepStrictEqual(
starknet.ethSigToPrivate(ethSignature),
'766f11e90cd7c7b43085b56da35c781f8c067ac0d578eabdceebc4886435bda'
);
});
should('Key derivation', () => {
const layer = 'starkex';
const application = 'starkdeployement';
const mnemonic =
'range mountain blast problem vibrant void vivid doctor cluster enough melody ' +
'salt layer language laptop boat major space monkey unit glimpse pause change vibrant';
const ethAddress = '0xa4864d977b944315389d1765ffa7e66F74ee8cd7';
const VECTORS = [
{
index: 0,
path: "m/2645'/579218131'/891216374'/1961790679'/2135936222'/0",
privateKey: '6cf0a8bf113352eb863157a45c5e5567abb34f8d32cddafd2c22aa803f4892c',
},
{
index: 7,
path: "m/2645'/579218131'/891216374'/1961790679'/2135936222'/7",
privateKey: '341751bdc42841da35ab74d13a1372c1f0250617e8a2ef96034d9f46e6847af',
},
{
index: 598,
path: "m/2645'/579218131'/891216374'/1961790679'/2135936222'/598",
privateKey: '41a4d591a868353d28b7947eb132aa4d00c4a022743689ffd20a3628d6ca28c',
},
];
const hd = bip32.HDKey.fromMasterSeed(bip39.mnemonicToSeedSync(mnemonic));
for (const { index, path, privateKey } of VECTORS) {
const realPath = starknet.getAccountPath(layer, application, ethAddress, index);
deepStrictEqual(realPath, path);
deepStrictEqual(starknet.grindKey(hd.derive(realPath).privateKey), privateKey);
}
});
// Verified against starknet.js
should('Starknet.js cross-tests', () => {
const privateKey = '0x019800ea6a9a73f94aee6a3d2edf018fc770443e90c7ba121e8303ec6b349279';
// NOTE: there is no compressed keys here, getPubKey returns stark-key (which is schnorr-like X coordinate)
// But it is not used in signing/verifying
deepStrictEqual(
starknet.getStarkKey(privateKey),
'0x33f45f07e1bd1a51b45fc24ec8c8c9908db9e42191be9e169bfcac0c0d99745'
);
const msgHash = '0x6d1706bd3d1ba7c517be2a2a335996f63d4738e2f182144d078a1dd9997062e';
const sig = starknet.sign(msgHash, privateKey);
const { r, s } = (sig);
deepStrictEqual(
r.toString(),
'1427981024487605678086498726488552139932400435436186597196374630267616399345'
);
deepStrictEqual(
s.toString(),
'1853664302719670721837677288395394946745467311923401353018029119631574115563'
);
const hashMsg2 = starknet.pedersen(
'0x33f45f07e1bd1a51b45fc24ec8c8c9908db9e42191be9e169bfcac0c0d99745',
'1'
);
deepStrictEqual(hashMsg2, '0x2b0d4d43acce8ff68416f667f92ec7eab2b96f1d2224abd4d9d4d1e7fa4bb00');
const pubKey =
'04033f45f07e1bd1a51b45fc24ec8c8c9908db9e42191be9e169bfcac0c0d997450319d0f53f6ca077c4fa5207819144a2a4165daef6ee47a7c1d06c0dcaa3e456';
const sig2 = new starknet.Signature(
558858382392827003930138586379728730695763862039474863361948210004201119180n,
2440689354481625417078677634625227600823892606910345662891037256374285369343n
);
deepStrictEqual(starknet.verify(sig2.toDERHex(), hashMsg2, pubKey), true);
});
// ESM is broken.
import url from 'url';
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
should.run();
}

View File

@@ -1,25 +0,0 @@
{
"compilerOptions": {
"strict": true,
"declaration": true,
"declarationMap": true,
"target": "es2020",
"lib": [
"es2020",
"dom"
],
"module": "es6",
"moduleResolution": "node16",
"outDir": "lib",
"noImplicitAny": true,
"preserveConstEnums": true,
"baseUrl": ".",
},
"include": [
"src",
],
"exclude": [
"node_modules",
"lib"
]
}

View File

@@ -1 +0,0 @@
throw new Error('Incorrect usage. Import submodules instead');

View File

@@ -1,19 +1,17 @@
{ {
"name": "@noble/curves", "name": "@noble/curves",
"version": "0.2.0", "version": "0.6.0",
"description": "Minimal, zero-dependency JS implementation of elliptic curve cryptography", "description": "Minimal, auditable JS implementation of elliptic curve cryptography",
"files": [ "files": [
"index.js", "lib"
"lib",
"lib/esm"
], ],
"scripts": { "scripts": {
"bench": "node curve-definitions/benchmark/index.js", "bench": "cd benchmark; node index.js",
"build": "tsc && tsc -p tsconfig.esm.json", "build": "tsc && tsc -p tsconfig.esm.json",
"build:release": "rollup -c rollup.config.js", "build:release": "rollup -c rollup.config.js",
"lint": "prettier --check 'src/**/*.{js,ts}' 'curve-definitions/src/**/*.{js,ts}'", "lint": "prettier --check 'src/**/*.{js,ts}' 'test/*.js'",
"format": "prettier --write 'src/**/*.{js,ts}' 'curve-definitions/src/**/*.{js,ts}'", "format": "prettier --write 'src/**/*.{js,ts}' 'test/*.js'",
"test": "cd curve-definitions; node test/index.test.js" "test": "node test/index.test.js"
}, },
"author": "Paul Miller (https://paulmillr.com)", "author": "Paul Miller (https://paulmillr.com)",
"homepage": "https://paulmillr.com/noble/", "homepage": "https://paulmillr.com/noble/",
@@ -22,56 +20,168 @@
"url": "https://github.com/paulmillr/noble-curves.git" "url": "https://github.com/paulmillr/noble-curves.git"
}, },
"license": "MIT", "license": "MIT",
"dependencies": {
"@noble/hashes": "1.1.5"
},
"devDependencies": { "devDependencies": {
"@rollup/plugin-node-resolve": "13.3.0", "@rollup/plugin-node-resolve": "13.3.0",
"@scure/base": "~1.1.1",
"@scure/bip32": "~1.1.1",
"@scure/bip39": "~1.1.0",
"@types/node": "18.11.3",
"fast-check": "3.0.0",
"micro-bmark": "0.2.0", "micro-bmark": "0.2.0",
"micro-should": "0.2.0", "micro-should": "0.3.0",
"prettier": "2.6.2", "prettier": "2.8.3",
"rollup": "2.75.5", "rollup": "2.75.5",
"typescript": "4.7.3" "typescript": "4.7.3"
}, },
"main": "index.js", "main": "index.js",
"exports": { "exports": {
"./edwards": { ".": {
"types": "./lib/edwards.d.ts", "types": "./lib/index.d.ts",
"import": "./lib/esm/edwards.js", "import": "./lib/esm/index.js",
"default": "./lib/edwards.js" "default": "./lib/index.js"
}, },
"./modular": { "./abstract/edwards": {
"types": "./lib/modular.d.ts", "types": "./lib/abstract/edwards.d.ts",
"import": "./lib/esm/modular.js", "import": "./lib/esm/abstract/edwards.js",
"default": "./lib/modular.js" "default": "./lib/abstract/edwards.js"
}, },
"./montgomery": { "./abstract/modular": {
"types": "./lib/montgomery.d.ts", "types": "./lib/abstract/modular.d.ts",
"import": "./lib/esm/montgomery.js", "import": "./lib/esm/abstract/modular.js",
"default": "./lib/montgomery.js" "default": "./lib/abstract/modular.js"
}, },
"./weierstrass": { "./abstract/montgomery": {
"types": "./lib/weierstrass.d.ts", "types": "./lib/abstract/montgomery.d.ts",
"import": "./lib/esm/weierstrass.js", "import": "./lib/esm/abstract/montgomery.js",
"default": "./lib/weierstrass.js" "default": "./lib/abstract/montgomery.js"
}, },
"./utils": { "./abstract/weierstrass": {
"types": "./lib/utils.d.ts", "types": "./lib/abstract/weierstrass.d.ts",
"import": "./lib/esm/utils.js", "import": "./lib/esm/abstract/weierstrass.js",
"default": "./lib/utils.js" "default": "./lib/abstract/weierstrass.js"
},
"./abstract/bls": {
"types": "./lib/abstract/bls.d.ts",
"import": "./lib/esm/abstract/bls.js",
"default": "./lib/abstract/bls.js"
},
"./abstract/hash-to-curve": {
"types": "./lib/abstract/hash-to-curve.d.ts",
"import": "./lib/esm/abstract/hash-to-curve.js",
"default": "./lib/abstract/hash-to-curve.js"
},
"./abstract/curve": {
"types": "./lib/abstract/curve.d.ts",
"import": "./lib/esm/abstract/curve.js",
"default": "./lib/abstract/curve.js"
},
"./abstract/utils": {
"types": "./lib/abstract/utils.d.ts",
"import": "./lib/esm/abstract/utils.js",
"default": "./lib/abstract/utils.js"
},
"./abstract/poseidon": {
"types": "./lib/abstract/poseidon.d.ts",
"import": "./lib/esm/abstract/poseidon.js",
"default": "./lib/abstract/poseidon.js"
},
"./_shortw_utils": {
"types": "./lib/_shortw_utils.d.ts",
"import": "./lib/esm/_shortw_utils.js",
"default": "./lib/_shortw_utils.js"
},
"./bls12-381": {
"types": "./lib/bls12-381.d.ts",
"import": "./lib/esm/bls12-381.js",
"default": "./lib/bls12-381.js"
},
"./bn": {
"types": "./lib/bn.d.ts",
"import": "./lib/esm/bn.js",
"default": "./lib/bn.js"
},
"./ed25519": {
"types": "./lib/ed25519.d.ts",
"import": "./lib/esm/ed25519.js",
"default": "./lib/ed25519.js"
},
"./ed448": {
"types": "./lib/ed448.d.ts",
"import": "./lib/esm/ed448.js",
"default": "./lib/ed448.js"
},
"./index": {
"types": "./lib/index.d.ts",
"import": "./lib/esm/index.js",
"default": "./lib/index.js"
},
"./jubjub": {
"types": "./lib/jubjub.d.ts",
"import": "./lib/esm/jubjub.js",
"default": "./lib/jubjub.js"
},
"./p192": {
"types": "./lib/p192.d.ts",
"import": "./lib/esm/p192.js",
"default": "./lib/p192.js"
},
"./p224": {
"types": "./lib/p224.d.ts",
"import": "./lib/esm/p224.js",
"default": "./lib/p224.js"
},
"./p256": {
"types": "./lib/p256.d.ts",
"import": "./lib/esm/p256.js",
"default": "./lib/p256.js"
},
"./p384": {
"types": "./lib/p384.d.ts",
"import": "./lib/esm/p384.js",
"default": "./lib/p384.js"
},
"./p521": {
"types": "./lib/p521.d.ts",
"import": "./lib/esm/p521.js",
"default": "./lib/p521.js"
},
"./pasta": {
"types": "./lib/pasta.d.ts",
"import": "./lib/esm/pasta.js",
"default": "./lib/pasta.js"
},
"./secp256k1": {
"types": "./lib/secp256k1.d.ts",
"import": "./lib/esm/secp256k1.js",
"default": "./lib/secp256k1.js"
},
"./stark": {
"types": "./lib/stark.d.ts",
"import": "./lib/esm/stark.js",
"default": "./lib/stark.js"
} }
}, },
"keywords": [ "keywords": [
"elliptic", "elliptic",
"curve", "curve",
"cryptography", "cryptography",
"hyperelliptic",
"weierstrass", "weierstrass",
"edwards",
"montgomery", "montgomery",
"secp256k1", "edwards",
"ed25519",
"ed448",
"p256", "p256",
"p384", "p384",
"p521", "p521",
"secp256r1",
"secp256k1",
"ed25519",
"ed448",
"bls12-381",
"bn254",
"pasta",
"bls",
"nist", "nist",
"ecc", "ecc",
"ecdsa", "ecdsa",

View File

@@ -1,6 +1,8 @@
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
import { hmac } from '@noble/hashes/hmac'; import { hmac } from '@noble/hashes/hmac';
import { concatBytes, randomBytes } from '@noble/hashes/utils'; import { concatBytes, randomBytes } from '@noble/hashes/utils';
import { weierstrass, CurveType, CHash } from '@noble/curves/weierstrass'; import { weierstrass, CurveType } from './abstract/weierstrass.js';
import { CHash } from './abstract/utils.js';
export function getHash(hash: CHash) { export function getHash(hash: CHash) {
return { return {

396
src/abstract/bls.ts Normal file
View File

@@ -0,0 +1,396 @@
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
/**
* BLS (Barreto-Lynn-Scott) family of pairing-friendly curves.
* Implements BLS (Boneh-Lynn-Shacham) signatures.
* Consists of two curves: G1 and G2:
* - G1 is a subgroup of (x, y) E(Fq) over y² = x³ + 4.
* - G2 is a subgroup of ((x₁, x₂+i), (y₁, y₂+i)) E(Fq²) over y² = x³ + 4(1 + i) where i is √-1
* - Gt, created by bilinear (ate) pairing e(G1, G2), consists of p-th roots of unity in
* Fq^k where k is embedding degree. Only degree 12 is currently supported, 24 is not.
* Pairing is used to aggregate and verify signatures.
* We are using Fp for private keys (shorter) and Fp₂ for signatures (longer).
* Some projects may prefer to swap this relation, it is not supported for now.
*/
import { AffinePoint } from './curve.js';
import { Field, hashToPrivateScalar } from './modular.js';
import { Hex, PrivKey, CHash, bitLen, bitGet, hexToBytes, bytesToHex } from './utils.js';
import * as htf from './hash-to-curve.js';
import {
CurvePointsType,
ProjPointType as ProjPointType,
CurvePointsRes,
weierstrassPoints,
} from './weierstrass.js';
type Fp = bigint; // Can be different field?
export type SignatureCoder<Fp2> = {
decode(hex: Hex): ProjPointType<Fp2>;
encode(point: ProjPointType<Fp2>): Uint8Array;
};
export type CurveType<Fp, Fp2, Fp6, Fp12> = {
r: bigint;
G1: Omit<CurvePointsType<Fp>, 'n'> & {
mapToCurve: htf.MapToCurve<Fp>;
htfDefaults: htf.Opts;
};
G2: Omit<CurvePointsType<Fp2>, 'n'> & {
Signature: SignatureCoder<Fp2>;
mapToCurve: htf.MapToCurve<Fp2>;
htfDefaults: htf.Opts;
};
x: bigint;
Fp: Field<Fp>;
Fr: Field<bigint>;
Fp2: Field<Fp2> & {
reim: (num: Fp2) => { re: bigint; im: bigint };
multiplyByB: (num: Fp2) => Fp2;
frobeniusMap(num: Fp2, power: number): Fp2;
};
Fp6: Field<Fp6>;
Fp12: Field<Fp12> & {
frobeniusMap(num: Fp12, power: number): Fp12;
multiplyBy014(num: Fp12, o0: Fp2, o1: Fp2, o4: Fp2): Fp12;
conjugate(num: Fp12): Fp12;
finalExponentiate(num: Fp12): Fp12;
};
htfDefaults: htf.Opts;
hash: CHash; // Because we need outputLen for DRBG
randomBytes: (bytesLength?: number) => Uint8Array;
};
export type CurveFn<Fp, Fp2, Fp6, Fp12> = {
CURVE: CurveType<Fp, Fp2, Fp6, Fp12>;
Fr: Field<bigint>;
Fp: Field<Fp>;
Fp2: Field<Fp2>;
Fp6: Field<Fp6>;
Fp12: Field<Fp12>;
G1: CurvePointsRes<Fp>;
G2: CurvePointsRes<Fp2>;
Signature: SignatureCoder<Fp2>;
millerLoop: (ell: [Fp2, Fp2, Fp2][], g1: [Fp, Fp]) => Fp12;
calcPairingPrecomputes: (p: AffinePoint<Fp2>) => [Fp2, Fp2, Fp2][];
// prettier-ignore
hashToCurve: {
G1: ReturnType<(typeof htf.hashToCurve<Fp>)>,
G2: ReturnType<(typeof htf.hashToCurve<Fp2>)>,
},
pairing: (P: ProjPointType<Fp>, Q: ProjPointType<Fp2>, withFinalExponent?: boolean) => Fp12;
getPublicKey: (privateKey: PrivKey) => Uint8Array;
sign: {
(message: Hex, privateKey: PrivKey): Uint8Array;
(message: ProjPointType<Fp2>, privateKey: PrivKey): ProjPointType<Fp2>;
};
verify: (
signature: Hex | ProjPointType<Fp2>,
message: Hex | ProjPointType<Fp2>,
publicKey: Hex | ProjPointType<Fp>
) => boolean;
aggregatePublicKeys: {
(publicKeys: Hex[]): Uint8Array;
(publicKeys: ProjPointType<Fp>[]): ProjPointType<Fp>;
};
aggregateSignatures: {
(signatures: Hex[]): Uint8Array;
(signatures: ProjPointType<Fp2>[]): ProjPointType<Fp2>;
};
verifyBatch: (
signature: Hex | ProjPointType<Fp2>,
messages: (Hex | ProjPointType<Fp2>)[],
publicKeys: (Hex | ProjPointType<Fp>)[]
) => boolean;
utils: {
stringToBytes: typeof htf.stringToBytes;
hashToField: typeof htf.hash_to_field;
expandMessageXMD: typeof htf.expand_message_xmd;
};
};
export function bls<Fp2, Fp6, Fp12>(
CURVE: CurveType<Fp, Fp2, Fp6, Fp12>
): CurveFn<Fp, Fp2, Fp6, Fp12> {
// Fields looks pretty specific for curve, so for now we need to pass them with options
const { Fp, Fr, Fp2, Fp6, Fp12 } = CURVE;
const BLS_X_LEN = bitLen(CURVE.x);
const groupLen = 32; // TODO: calculate; hardcoded for now
// Pre-compute coefficients for sparse multiplication
// Point addition and point double calculations is reused for coefficients
function calcPairingPrecomputes(p: AffinePoint<Fp2>) {
const { x, y } = p;
// prettier-ignore
const Qx = x, Qy = y, Qz = Fp2.ONE;
// prettier-ignore
let Rx = Qx, Ry = Qy, Rz = Qz;
let ell_coeff: [Fp2, Fp2, Fp2][] = [];
for (let i = BLS_X_LEN - 2; i >= 0; i--) {
// Double
let t0 = Fp2.sqr(Ry); // Ry²
let t1 = Fp2.sqr(Rz); // Rz²
let t2 = Fp2.multiplyByB(Fp2.mul(t1, 3n)); // 3 * T1 * B
let t3 = Fp2.mul(t2, 3n); // 3 * T2
let t4 = Fp2.sub(Fp2.sub(Fp2.sqr(Fp2.add(Ry, Rz)), t1), t0); // (Ry + Rz)² - T1 - T0
ell_coeff.push([
Fp2.sub(t2, t0), // T2 - T0
Fp2.mul(Fp2.sqr(Rx), 3n), // 3 * Rx²
Fp2.neg(t4), // -T4
]);
Rx = Fp2.div(Fp2.mul(Fp2.mul(Fp2.sub(t0, t3), Rx), Ry), 2n); // ((T0 - T3) * Rx * Ry) / 2
Ry = Fp2.sub(Fp2.sqr(Fp2.div(Fp2.add(t0, t3), 2n)), Fp2.mul(Fp2.sqr(t2), 3n)); // ((T0 + T3) / 2)² - 3 * T2²
Rz = Fp2.mul(t0, t4); // T0 * T4
if (bitGet(CURVE.x, i)) {
// Addition
let t0 = Fp2.sub(Ry, Fp2.mul(Qy, Rz)); // Ry - Qy * Rz
let t1 = Fp2.sub(Rx, Fp2.mul(Qx, Rz)); // Rx - Qx * Rz
ell_coeff.push([
Fp2.sub(Fp2.mul(t0, Qx), Fp2.mul(t1, Qy)), // T0 * Qx - T1 * Qy
Fp2.neg(t0), // -T0
t1, // T1
]);
let t2 = Fp2.sqr(t1); // T1²
let t3 = Fp2.mul(t2, t1); // T2 * T1
let t4 = Fp2.mul(t2, Rx); // T2 * Rx
let t5 = Fp2.add(Fp2.sub(t3, Fp2.mul(t4, 2n)), Fp2.mul(Fp2.sqr(t0), Rz)); // T3 - 2 * T4 + T0² * Rz
Rx = Fp2.mul(t1, t5); // T1 * T5
Ry = Fp2.sub(Fp2.mul(Fp2.sub(t4, t5), t0), Fp2.mul(t3, Ry)); // (T4 - T5) * T0 - T3 * Ry
Rz = Fp2.mul(Rz, t3); // Rz * T3
}
}
return ell_coeff;
}
function millerLoop(ell: [Fp2, Fp2, Fp2][], g1: [Fp, Fp]): Fp12 {
const { x } = CURVE;
const Px = g1[0];
const Py = g1[1];
let f12 = Fp12.ONE;
for (let j = 0, i = BLS_X_LEN - 2; i >= 0; i--, j++) {
const E = ell[j];
f12 = Fp12.multiplyBy014(f12, E[0], Fp2.mul(E[1], Px), Fp2.mul(E[2], Py));
if (bitGet(x, i)) {
j += 1;
const F = ell[j];
f12 = Fp12.multiplyBy014(f12, F[0], Fp2.mul(F[1], Px), Fp2.mul(F[2], Py));
}
if (i !== 0) f12 = Fp12.sqr(f12);
}
return Fp12.conjugate(f12);
}
const utils = {
hexToBytes: hexToBytes,
bytesToHex: bytesToHex,
stringToBytes: htf.stringToBytes,
// TODO: do we need to export it here?
hashToField: (
msg: Uint8Array,
count: number,
options: Partial<typeof CURVE.htfDefaults> = {}
) => htf.hash_to_field(msg, count, { ...CURVE.htfDefaults, ...options }),
expandMessageXMD: (msg: Uint8Array, DST: Uint8Array, lenInBytes: number, H = CURVE.hash) =>
htf.expand_message_xmd(msg, DST, lenInBytes, H),
hashToPrivateKey: (hash: Hex): Uint8Array => Fr.toBytes(hashToPrivateScalar(hash, CURVE.r)),
randomBytes: (bytesLength: number = groupLen): Uint8Array => CURVE.randomBytes(bytesLength),
randomPrivateKey: (): Uint8Array => utils.hashToPrivateKey(utils.randomBytes(groupLen + 8)),
};
// Point on G1 curve: (x, y)
const G1 = weierstrassPoints({
n: Fr.ORDER,
...CURVE.G1,
});
const G1HashToCurve = htf.hashToCurve(G1.ProjectivePoint, CURVE.G1.mapToCurve, {
...CURVE.htfDefaults,
...CURVE.G1.htfDefaults,
});
// Sparse multiplication against precomputed coefficients
// TODO: replace with weakmap?
type withPairingPrecomputes = { _PPRECOMPUTES: [Fp2, Fp2, Fp2][] | undefined };
function pairingPrecomputes(point: G2): [Fp2, Fp2, Fp2][] {
const p = point as G2 & withPairingPrecomputes;
if (p._PPRECOMPUTES) return p._PPRECOMPUTES;
p._PPRECOMPUTES = calcPairingPrecomputes(point.toAffine());
return p._PPRECOMPUTES;
}
// TODO: export
// function clearPairingPrecomputes(point: G2) {
// const p = point as G2 & withPairingPrecomputes;
// p._PPRECOMPUTES = undefined;
// }
// Point on G2 curve (complex numbers): (x₁, x₂+i), (y₁, y₂+i)
const G2 = weierstrassPoints({
n: Fr.ORDER,
...CURVE.G2,
});
const C = G2.ProjectivePoint as htf.H2CPointConstructor<Fp2>; // TODO: fix
const G2HashToCurve = htf.hashToCurve(C, CURVE.G2.mapToCurve, {
...CURVE.htfDefaults,
...CURVE.G2.htfDefaults,
});
const { Signature } = CURVE.G2;
// Calculates bilinear pairing
function pairing(Q: G1, P: G2, withFinalExponent: boolean = true): Fp12 {
if (Q.equals(G1.ProjectivePoint.ZERO) || P.equals(G2.ProjectivePoint.ZERO))
throw new Error('pairing is not available for ZERO point');
Q.assertValidity();
P.assertValidity();
// Performance: 9ms for millerLoop and ~14ms for exp.
const Qa = Q.toAffine();
const looped = millerLoop(pairingPrecomputes(P), [Qa.x, Qa.y]);
return withFinalExponent ? Fp12.finalExponentiate(looped) : looped;
}
type G1 = typeof G1.ProjectivePoint.BASE;
type G2 = typeof G2.ProjectivePoint.BASE;
type G1Hex = Hex | G1;
type G2Hex = Hex | G2;
function normP1(point: G1Hex): G1 {
return point instanceof G1.ProjectivePoint ? (point as G1) : G1.ProjectivePoint.fromHex(point);
}
function normP2(point: G2Hex): G2 {
return point instanceof G2.ProjectivePoint ? point : Signature.decode(point);
}
function normP2Hash(point: G2Hex, htfOpts?: htf.htfBasicOpts): G2 {
return point instanceof G2.ProjectivePoint
? point
: (G2HashToCurve.hashToCurve(point, htfOpts) as G2);
}
// Multiplies generator by private key.
// P = pk x G
function getPublicKey(privateKey: PrivKey): Uint8Array {
return G1.ProjectivePoint.fromPrivateKey(privateKey).toRawBytes(true);
}
// Executes `hashToCurve` on the message and then multiplies the result by private key.
// S = pk x H(m)
function sign(message: Hex, privateKey: PrivKey, htfOpts?: htf.htfBasicOpts): Uint8Array;
function sign(message: G2, privateKey: PrivKey, htfOpts?: htf.htfBasicOpts): G2;
function sign(message: G2Hex, privateKey: PrivKey, htfOpts?: htf.htfBasicOpts): Uint8Array | G2 {
const msgPoint = normP2Hash(message, htfOpts);
msgPoint.assertValidity();
const sigPoint = msgPoint.multiply(G1.normalizePrivateKey(privateKey));
if (message instanceof G2.ProjectivePoint) return sigPoint;
return Signature.encode(sigPoint);
}
// Checks if pairing of public key & hash is equal to pairing of generator & signature.
// e(P, H(m)) == e(G, S)
function verify(
signature: G2Hex,
message: G2Hex,
publicKey: G1Hex,
htfOpts?: htf.htfBasicOpts
): boolean {
const P = normP1(publicKey);
const Hm = normP2Hash(message, htfOpts);
const G = G1.ProjectivePoint.BASE;
const S = normP2(signature);
// Instead of doing 2 exponentiations, we use property of billinear maps
// and do one exp after multiplying 2 points.
const ePHm = pairing(P.negate(), Hm, false);
const eGS = pairing(G, S, false);
const exp = Fp12.finalExponentiate(Fp12.mul(eGS, ePHm));
return Fp12.eql(exp, Fp12.ONE);
}
// Adds a bunch of public key points together.
// pk1 + pk2 + pk3 = pkA
function aggregatePublicKeys(publicKeys: Hex[]): Uint8Array;
function aggregatePublicKeys(publicKeys: G1[]): G1;
function aggregatePublicKeys(publicKeys: G1Hex[]): Uint8Array | G1 {
if (!publicKeys.length) throw new Error('Expected non-empty array');
const agg = publicKeys.map(normP1).reduce((sum, p) => sum.add(p), G1.ProjectivePoint.ZERO);
const aggAffine = agg; //.toAffine();
if (publicKeys[0] instanceof G1.ProjectivePoint) {
aggAffine.assertValidity();
return aggAffine;
}
// toRawBytes ensures point validity
return aggAffine.toRawBytes(true);
}
// Adds a bunch of signature points together.
function aggregateSignatures(signatures: Hex[]): Uint8Array;
function aggregateSignatures(signatures: G2[]): G2;
function aggregateSignatures(signatures: G2Hex[]): Uint8Array | G2 {
if (!signatures.length) throw new Error('Expected non-empty array');
const agg = signatures.map(normP2).reduce((sum, s) => sum.add(s), G2.ProjectivePoint.ZERO);
const aggAffine = agg; //.toAffine();
if (signatures[0] instanceof G2.ProjectivePoint) {
aggAffine.assertValidity();
return aggAffine;
}
return Signature.encode(aggAffine);
}
// https://ethresear.ch/t/fast-verification-of-multiple-bls-signatures/5407
// e(G, S) = e(G, SUM(n)(Si)) = MUL(n)(e(G, Si))
function verifyBatch(
signature: G2Hex,
messages: G2Hex[],
publicKeys: G1Hex[],
htfOpts?: htf.htfBasicOpts
): boolean {
// @ts-ignore
// console.log('verifyBatch', bytesToHex(signature as any), messages, publicKeys.map(bytesToHex));
if (!messages.length) throw new Error('Expected non-empty messages array');
if (publicKeys.length !== messages.length)
throw new Error('Pubkey count should equal msg count');
const sig = normP2(signature);
const nMessages = messages.map((i) => normP2Hash(i, htfOpts));
const nPublicKeys = publicKeys.map(normP1);
try {
const paired = [];
for (const message of new Set(nMessages)) {
const groupPublicKey = nMessages.reduce(
(groupPublicKey, subMessage, i) =>
subMessage === message ? groupPublicKey.add(nPublicKeys[i]) : groupPublicKey,
G1.ProjectivePoint.ZERO
);
// const msg = message instanceof PointG2 ? message : await PointG2.hashToCurve(message);
// Possible to batch pairing for same msg with different groupPublicKey here
paired.push(pairing(groupPublicKey, message, false));
}
paired.push(pairing(G1.ProjectivePoint.BASE.negate(), sig, false));
const product = paired.reduce((a, b) => Fp12.mul(a, b), Fp12.ONE);
const exp = Fp12.finalExponentiate(product);
return Fp12.eql(exp, Fp12.ONE);
} catch {
return false;
}
}
G1.ProjectivePoint.BASE._setWindowSize(4);
return {
CURVE,
Fr,
Fp,
Fp2,
Fp6,
Fp12,
G1,
G2,
Signature,
millerLoop,
calcPairingPrecomputes,
hashToCurve: { G1: G1HashToCurve, G2: G2HashToCurve },
pairing,
getPublicKey,
sign,
verify,
aggregatePublicKeys,
aggregateSignatures,
verifyBatch,
utils,
};
}

View File

@@ -1,29 +1,37 @@
/*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
// Default group related functions // Abelian group utilities
import { Field, validateField, nLength } from './modular.js';
const _0n = BigInt(0); const _0n = BigInt(0);
const _1n = BigInt(1); const _1n = BigInt(1);
export type AffinePoint<T> = {
x: T;
y: T;
} & { z?: never; t?: never };
export interface Group<T extends Group<T>> { export interface Group<T extends Group<T>> {
double(): T; double(): T;
negate(): T; negate(): T;
add(other: T): T; add(other: T): T;
subtract(other: T): T; subtract(other: T): T;
equals(other: T): boolean; equals(other: T): boolean;
multiply(scalar: number | bigint): T; multiply(scalar: bigint): T;
} }
export type GroupConstructor<T> = { export type GroupConstructor<T> = {
BASE: T; BASE: T;
ZERO: T; ZERO: T;
}; };
// Not big, but pretty complex and it is easy to break stuff. To avoid too much copy paste export type Mapper<T> = (i: T[]) => T[];
// Elliptic curve multiplication of Point by scalar. Complicated and fragile. Uses wNAF method.
// Windowed method is 10% faster, but takes 2x longer to generate & consumes 2x memory.
export function wNAF<T extends Group<T>>(c: GroupConstructor<T>, bits: number) { export function wNAF<T extends Group<T>>(c: GroupConstructor<T>, bits: number) {
const constTimeNegate = (condition: boolean, item: T): T => { const constTimeNegate = (condition: boolean, item: T): T => {
const neg = item.negate(); const neg = item.negate();
return condition ? neg : item; return condition ? neg : item;
}; };
const opts = (W: number) => { const opts = (W: number) => {
if (256 % W) throw new Error('Invalid precomputation window, must be power of 2');
const windows = Math.ceil(bits / W) + 1; // +1, because const windows = Math.ceil(bits / W) + 1; // +1, because
const windowSize = 2 ** (W - 1); // -1 because we skip zero const windowSize = 2 ** (W - 1); // -1 because we skip zero
return { windows, windowSize }; return { windows, windowSize };
@@ -68,11 +76,14 @@ export function wNAF<T extends Group<T>>(c: GroupConstructor<T>, bits: number) {
/** /**
* Implements w-ary non-adjacent form for calculating ec multiplication. * Implements w-ary non-adjacent form for calculating ec multiplication.
* @param n * @param W window size
* @param affinePoint optional 2d point to save cached precompute windows on it. * @param affinePoint optional 2d point to save cached precompute windows on it.
* @param n bits
* @returns real and fake (for const-time) points * @returns real and fake (for const-time) points
*/ */
wNAF(W: number, precomputes: T[], n: bigint): { p: T; f: T } { wNAF(W: number, precomputes: T[], n: bigint): { p: T; f: T } {
// TODO: maybe check that scalar is less than group order? wNAF will fail otherwise
// But need to carefully remove other checks before wNAF. ORDER == bits here
const { windows, windowSize } = opts(W); const { windows, windowSize } = opts(W);
let p = c.ZERO; let p = c.ZERO;
@@ -123,5 +134,52 @@ export function wNAF<T extends Group<T>>(c: GroupConstructor<T>, bits: number) {
// which makes it less const-time: around 1 bigint multiply. // which makes it less const-time: around 1 bigint multiply.
return { p, f }; return { p, f };
}, },
wNAFCached(P: T, precomputesMap: Map<T, T[]>, n: bigint, transform: Mapper<T>): { p: T; f: T } {
// @ts-ignore
const W: number = P._WINDOW_SIZE || 1;
// Calculate precomputes on a first run, reuse them after
let comp = precomputesMap.get(P);
if (!comp) {
comp = this.precomputeWindow(P, W) as T[];
if (W !== 1) {
precomputesMap.set(P, transform(comp));
}
}
return this.wNAF(W, comp, n);
},
}; };
} }
// Generic BasicCurve interface: works even for polynomial fields (BLS): P, n, h would be ok.
// Though generator can be different (Fp2 / Fp6 for BLS).
export type AbstractCurve<T> = {
Fp: Field<T>; // Field over which we'll do calculations (Fp)
n: bigint; // Curve order, total count of valid points in the field
nBitLength?: number; // bit length of curve order
nByteLength?: number; // byte length of curve order
h: bigint; // cofactor. we can assign default=1, but users will just ignore it w/o validation
hEff?: bigint; // Number to multiply to clear cofactor
Gx: T; // base point X coordinate
Gy: T; // base point Y coordinate
wrapPrivateKey?: boolean; // bls12-381 requires mod(n) instead of rejecting keys >= n
allowInfinityPoint?: boolean; // bls12-381 requires it. ZERO point is valid, but invalid pubkey
};
export function validateAbsOpts<FP, T>(curve: AbstractCurve<FP> & T) {
validateField(curve.Fp);
for (const i of ['n', 'h'] as const) {
const val = curve[i];
if (typeof val !== 'bigint') throw new Error(`Invalid curve param ${i}=${val} (${typeof val})`);
}
if (!curve.Fp.isValid(curve.Gx)) throw new Error('Invalid generator X coordinate Fp element');
if (!curve.Fp.isValid(curve.Gy)) throw new Error('Invalid generator Y coordinate Fp element');
for (const i of ['nBitLength', 'nByteLength'] as const) {
const val = curve[i];
if (val === undefined) continue; // Optional
if (!Number.isSafeInteger(val)) throw new Error(`Invalid param ${i}=${val} (${typeof val})`);
}
// Set defaults
return Object.freeze({ ...nLength(curve.n, curve.nBitLength), ...curve } as const);
}

478
src/abstract/edwards.ts Normal file
View File

@@ -0,0 +1,478 @@
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
// Twisted Edwards curve. The formula is: ax² + y² = 1 + dx²y²
import { mod } from './modular.js';
import {
bytesToHex,
bytesToNumberLE,
concatBytes,
ensureBytes,
FHash,
Hex,
numberToBytesLE,
} from './utils.js';
import {
Group,
GroupConstructor,
wNAF,
AbstractCurve,
validateAbsOpts,
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);
// Edwards curves must declare params a & d.
export type CurveType = AbstractCurve<bigint> & {
a: bigint; // curve param a
d: bigint; // curve param d
hash: FHash; // Hashing
randomBytes: (bytesLength?: number) => Uint8Array; // CSPRNG
adjustScalarBytes?: (bytes: Uint8Array) => Uint8Array; // clears bits to get valid field elemtn
domain?: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array; // Used for hashing
uvRatio?: (u: bigint, v: bigint) => { isValid: boolean; value: bigint }; // Ratio √(u/v)
preHash?: FHash; // RFC 8032 pre-hashing of messages to sign() / verify()
mapToCurve?: (scalar: bigint[]) => AffinePoint<bigint>; // for hash-to-curve standard
};
function validateOpts(curve: CurveType) {
const opts = validateAbsOpts(curve);
if (typeof opts.hash !== 'function') throw new Error('Invalid hash function');
for (const i of ['a', 'd'] as const) {
const val = opts[i];
if (typeof val !== 'bigint') throw new Error(`Invalid curve param ${i}=${val} (${typeof val})`);
}
for (const fn of ['randomBytes'] as const) {
if (typeof opts[fn] !== 'function') throw new Error(`Invalid ${fn} function`);
}
for (const fn of ['adjustScalarBytes', 'domain', 'uvRatio', 'mapToCurve'] as const) {
if (opts[fn] === undefined) continue; // Optional
if (typeof opts[fn] !== 'function') throw new Error(`Invalid ${fn} function`);
}
// Set defaults
return Object.freeze({ ...opts } as const);
}
// Instance of Extended Point with coordinates in X, Y, Z, T
export interface ExtPointType extends Group<ExtPointType> {
readonly ex: bigint;
readonly ey: bigint;
readonly ez: bigint;
readonly et: bigint;
assertValidity(): void;
multiply(scalar: bigint): ExtPointType;
multiplyUnsafe(scalar: bigint): ExtPointType;
isSmallOrder(): boolean;
isTorsionFree(): boolean;
clearCofactor(): ExtPointType;
toAffine(iz?: bigint): AffinePoint<bigint>;
}
// Static methods of Extended Point with coordinates in X, Y, Z, T
export interface ExtPointConstructor extends GroupConstructor<ExtPointType> {
new (x: bigint, y: bigint, z: bigint, t: bigint): ExtPointType;
fromAffine(p: AffinePoint<bigint>): ExtPointType;
fromHex(hex: Hex): ExtPointType;
fromPrivateKey(privateKey: Hex): ExtPointType; // TODO: remove
}
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;
ExtendedPoint: ExtPointConstructor;
utils: {
randomPrivateKey: () => Uint8Array;
getExtendedPublicKey: (key: Hex) => {
head: Uint8Array;
prefix: Uint8Array;
scalar: bigint;
point: ExtPointType;
pointBytes: Uint8Array;
};
};
};
// It is not generic twisted curve for now, but ed25519/ed448 generic implementation
export function twistedEdwards(curveDef: CurveType): CurveFn {
const CURVE = validateOpts(curveDef) as ReturnType<typeof validateOpts>;
const { Fp, n: CURVE_ORDER, preHash, hash: cHash, randomBytes, nByteLength, h: cofactor } = CURVE;
const MASK = _2n ** BigInt(nByteLength * 8);
const modP = Fp.create; // Function overrides
// sqrt(u/v)
const uvRatio =
CURVE.uvRatio ||
((u: bigint, v: bigint) => {
try {
return { isValid: true, value: Fp.sqrt(u * Fp.inv(v)) };
} catch (e) {
return { isValid: false, value: _0n };
}
});
const adjustScalarBytes = CURVE.adjustScalarBytes || ((bytes: Uint8Array) => bytes); // NOOP
const domain =
CURVE.domain ||
((data: Uint8Array, ctx: Uint8Array, phflag: boolean) => {
if (ctx.length || phflag) throw new Error('Contexts/pre-hash are not supported');
return data;
}); // NOOP
const inBig = (n: bigint) => typeof n === 'bigint' && 0n < n; // n in [1..]
const inRange = (n: bigint, max: bigint) => inBig(n) && inBig(max) && n < max; // n in [1..max-1]
const in0MaskRange = (n: bigint) => n === _0n || inRange(n, MASK); // n in [0..MASK-1]
function assertInRange(n: bigint, max: bigint) {
// n in [1..max-1]
if (inRange(n, max)) return n;
throw new Error(`Expected valid scalar < ${max}, got ${typeof n} ${n}`);
}
function assertGE0(n: bigint) {
// n in [0..CURVE_ORDER-1]
return n === _0n ? n : assertInRange(n, CURVE_ORDER); // GE = prime subgroup, not full group
}
const pointPrecomputes = new Map<Point, Point[]>();
function isPoint(other: unknown) {
if (!(other instanceof Point)) throw new Error('ExtendedPoint expected');
}
// Extended Point works in extended coordinates: (x, y, z, t) ∋ (x=x/z, y=y/z, t=xy).
// https://en.wikipedia.org/wiki/Twisted_Edwards_curve#Extended_coordinates
class Point implements ExtPointType {
static readonly BASE = new Point(CURVE.Gx, CURVE.Gy, _1n, modP(CURVE.Gx * CURVE.Gy));
static readonly ZERO = new Point(_0n, _1n, _1n, _0n); // 0, 1, 1, 0
constructor(
readonly ex: bigint,
readonly ey: bigint,
readonly ez: bigint,
readonly et: bigint
) {
if (!in0MaskRange(ex)) throw new Error('x required');
if (!in0MaskRange(ey)) throw new Error('y required');
if (!in0MaskRange(ez)) throw new Error('z required');
if (!in0MaskRange(et)) throw new Error('t required');
}
get x(): bigint {
return this.toAffine().x;
}
get y(): bigint {
return this.toAffine().y;
}
static fromAffine(p: AffinePoint<bigint>): Point {
if (p instanceof Point) throw new Error('extended point not allowed');
const { x, y } = p || {};
if (!in0MaskRange(x) || !in0MaskRange(y)) throw new Error('invalid affine point');
return new Point(x, y, _1n, modP(x * y));
}
static normalizeZ(points: Point[]): Point[] {
const toInv = Fp.invertBatch(points.map((p) => p.ez));
return points.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine);
}
// We calculate precomputes for elliptic curve point multiplication
// using windowed method. This specifies window size and
// stores precomputed values. Usually only base point would be precomputed.
_WINDOW_SIZE?: number;
// "Private method", don't use it directly
_setWindowSize(windowSize: number) {
this._WINDOW_SIZE = windowSize;
pointPrecomputes.delete(this);
}
assertValidity(): void {}
// Compare one point to another.
equals(other: Point): boolean {
isPoint(other);
const { ex: X1, ey: Y1, ez: Z1 } = this;
const { ex: X2, ey: Y2, ez: Z2 } = other;
const X1Z2 = modP(X1 * Z2);
const X2Z1 = modP(X2 * Z1);
const Y1Z2 = modP(Y1 * Z2);
const Y2Z1 = modP(Y2 * Z1);
return X1Z2 === X2Z1 && Y1Z2 === Y2Z1;
}
protected is0(): boolean {
return this.equals(Point.ZERO);
}
negate(): Point {
// Flips point sign to a negative one (-x, y in affine coords)
return new Point(modP(-this.ex), this.ey, this.ez, modP(-this.et));
}
// Fast algo for doubling Extended Point.
// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd
// Cost: 4M + 4S + 1*a + 6add + 1*2.
double(): Point {
const { a } = CURVE;
const { ex: X1, ey: Y1, ez: Z1 } = this;
const A = modP(X1 * X1); // A = X12
const B = modP(Y1 * Y1); // B = Y12
const C = modP(_2n * modP(Z1 * Z1)); // C = 2*Z12
const D = modP(a * A); // D = a*A
const x1y1 = X1 + Y1;
const E = modP(modP(x1y1 * x1y1) - A - B); // E = (X1+Y1)2-A-B
const G = D + B; // G = D+B
const F = G - C; // F = G-C
const H = D - B; // H = D-B
const X3 = modP(E * F); // X3 = E*F
const Y3 = modP(G * H); // Y3 = G*H
const T3 = modP(E * H); // T3 = E*H
const Z3 = modP(F * G); // Z3 = F*G
return new Point(X3, Y3, Z3, T3);
}
// Fast algo for adding 2 Extended Points.
// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#addition-add-2008-hwcd
// Cost: 9M + 1*a + 1*d + 7add.
add(other: Point) {
isPoint(other);
const { a, d } = CURVE;
const { ex: X1, ey: Y1, ez: Z1, et: T1 } = this;
const { ex: X2, ey: Y2, ez: Z2, et: T2 } = other;
// Faster algo for adding 2 Extended Points when curve's a=-1.
// http://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-4
// Cost: 8M + 8add + 2*2.
// Note: It does not check whether the `other` point is valid.
if (a === BigInt(-1)) {
const A = modP((Y1 - X1) * (Y2 + X2));
const B = modP((Y1 + X1) * (Y2 - X2));
const F = modP(B - A);
if (F === _0n) return this.double(); // Same point. Tests say it doesn't affect timing
const C = modP(Z1 * _2n * T2);
const D = modP(T1 * _2n * Z2);
const E = D + C;
const G = B + A;
const H = D - C;
const X3 = modP(E * F);
const Y3 = modP(G * H);
const T3 = modP(E * H);
const Z3 = modP(F * G);
return new Point(X3, Y3, Z3, T3);
}
const A = modP(X1 * X2); // A = X1*X2
const B = modP(Y1 * Y2); // B = Y1*Y2
const C = modP(T1 * d * T2); // C = T1*d*T2
const D = modP(Z1 * Z2); // D = Z1*Z2
const E = modP((X1 + Y1) * (X2 + Y2) - A - B); // E = (X1+Y1)*(X2+Y2)-A-B
const F = D - C; // F = D-C
const G = D + C; // G = D+C
const H = modP(B - a * A); // H = B-a*A
const X3 = modP(E * F); // X3 = E*F
const Y3 = modP(G * H); // Y3 = G*H
const T3 = modP(E * H); // T3 = E*H
const Z3 = modP(F * G); // Z3 = F*G
return new Point(X3, Y3, Z3, T3);
}
subtract(other: Point): Point {
return this.add(other.negate());
}
private wNAF(n: bigint): { p: Point; f: Point } {
return wnaf.wNAFCached(this, pointPrecomputes, n, Point.normalizeZ);
}
// Constant-time multiplication.
multiply(scalar: bigint): Point {
const { p, f } = this.wNAF(assertInRange(scalar, CURVE_ORDER));
return Point.normalizeZ([p, f])[0];
}
// Non-constant-time multiplication. Uses double-and-add algorithm.
// It's faster, but should only be used when you don't care about
// an exposed private key e.g. sig verification.
multiplyUnsafe(scalar: bigint): Point {
let n = assertGE0(scalar);
if (n === _0n) return I;
if (this.equals(I) || n === _1n) return this;
if (this.equals(G)) return this.wNAF(n).p;
return wnaf.unsafeLadder(this, n);
}
// Checks if point is of small order.
// If you add something to small order point, you will have "dirty"
// point with torsion component.
// Multiplies point by cofactor and checks if the result is 0.
isSmallOrder(): boolean {
return this.multiplyUnsafe(cofactor).is0();
}
// Multiplies point by curve order and checks if the result is 0.
// Returns `false` is the point is dirty.
isTorsionFree(): boolean {
return wnaf.unsafeLadder(this, CURVE_ORDER).is0();
}
// Converts Extended point to default (x, y) coordinates.
// Can accept precomputed Z^-1 - for example, from invertBatch.
toAffine(iz?: bigint): AffinePoint<bigint> {
const { ex: x, ey: y, ez: z } = this;
const is0 = this.is0();
if (iz == null) iz = is0 ? _8n : (Fp.inv(z) as bigint); // 8 was chosen arbitrarily
const ax = modP(x * iz);
const ay = modP(y * iz);
const zz = modP(z * iz);
if (is0) return { x: _0n, y: _1n };
if (zz !== _1n) throw new Error('invZ was invalid');
return { x: ax, y: ay };
}
clearCofactor(): Point {
const { h: cofactor } = CURVE;
if (cofactor === _1n) return this;
return this.multiplyUnsafe(cofactor);
}
// Converts hash string or Uint8Array to Point.
// Uses algo from RFC8032 5.1.3.
static fromHex(hex: Hex, strict = true): Point {
const { d, a } = CURVE;
const len = Fp.BYTES;
hex = ensureBytes(hex, len); // copy hex to a new array
const normed = hex.slice(); // copy again, we'll manipulate it
const lastByte = hex[len - 1]; // select last byte
normed[len - 1] = lastByte & ~0x80; // clear last bit
const y = bytesToNumberLE(normed);
if (y === _0n) {
// 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)
}
// Ed25519: x² = (y²-1)/(dy²+1) mod p. Ed448: x² = (y²-1)/(dy²-1) mod p. Generic case:
// ax²+y²=1+dx²y² => y²-1=dx²y²-ax² => y²-1=x²(dy²-a) => x²=(y²-1)/(dy²-a)
const y2 = modP(y * y); // denominator is always non-0 mod p.
const u = modP(y2 - _1n); // u = y² - 1
const v = modP(d * y2 - a); // v = d y² + 1.
let { isValid, value: x } = uvRatio(u, v); // √(u/v)
if (!isValid) throw new Error('Point.fromHex: invalid y coordinate');
const isXOdd = (x & _1n) === _1n; // There are 2 square roots. Use x_0 bit to select proper
const isLastByteOdd = (lastByte & 0x80) !== 0; // if x=0 and x_0 = 1, fail
if (isLastByteOdd !== isXOdd) x = modP(-x); // if x_0 != x mod 2, set x = p-x
return Point.fromAffine({ x, y });
}
static fromPrivateKey(privKey: Hex) {
return getExtendedPublicKey(privKey).point;
}
toRawBytes(): Uint8Array {
const { x, y } = this.toAffine();
const bytes = numberToBytesLE(y, Fp.BYTES); // each y has 2 x values (x, -y)
bytes[bytes.length - 1] |= x & _1n ? 0x80 : 0; // when compressing, it's enough to store y
return bytes; // and use the last byte to encode sign of x
}
toHex(): string {
return bytesToHex(this.toRawBytes()); // Same as toRawBytes, but returns string.
}
}
const { BASE: G, ZERO: I } = Point;
const wnaf = wNAF(Point, nByteLength * 8);
function modN(a: bigint) {
return mod(a, CURVE_ORDER);
}
// Little-endian SHA512 with modulo n
function modN_LE(hash: Uint8Array): bigint {
return modN(bytesToNumberLE(hash));
}
function isHex(item: Hex, err: string) {
if (typeof item !== 'string' && !(item instanceof Uint8Array))
throw new Error(`${err} must be hex string or Uint8Array`);
}
/** Convenience method that creates public key and other stuff. RFC8032 5.1.5 */
function getExtendedPublicKey(key: Hex) {
isHex(key, 'private key');
const len = nByteLength;
// Hash private key with curve's hash function to produce uniformingly random input
// Check byte lengths: ensure(64, h(ensure(32, key)))
const hashed = ensureBytes(cHash(ensureBytes(key, len)), 2 * len);
const head = adjustScalarBytes(hashed.slice(0, len)); // clear first half bits, produce FE
const prefix = hashed.slice(len, 2 * len); // second half is called key prefix (5.1.6)
const scalar = modN_LE(head); // The actual private scalar
const point = G.multiply(scalar); // Point on Edwards curve aka public key
const pointBytes = point.toRawBytes(); // Uint8Array representation
return { head, prefix, scalar, point, pointBytes };
}
// Calculates EdDSA pub key. RFC8032 5.1.5. Privkey is hashed. Use first half with 3 bits cleared
function getPublicKey(privKey: Hex): Uint8Array {
return getExtendedPublicKey(privKey).pointBytes;
}
// int('LE', SHA512(dom2(F, C) || msgs)) mod N
function hashDomainToScalar(context: Hex = new Uint8Array(), ...msgs: Uint8Array[]) {
const msg = concatBytes(...msgs);
return modN_LE(cHash(domain(msg, ensureBytes(context), !!preHash)));
}
/** Signs message with privateKey. RFC8032 5.1.6 */
function sign(msg: Hex, privKey: Hex, context?: Hex): Uint8Array {
isHex(msg, 'message');
msg = ensureBytes(msg);
if (preHash) msg = preHash(msg); // for ed25519ph etc.
const { prefix, scalar, pointBytes } = getExtendedPublicKey(privKey);
const r = hashDomainToScalar(context, prefix, msg); // r = dom2(F, C) || prefix || PH(M)
const R = G.multiply(r).toRawBytes(); // R = rG
const k = hashDomainToScalar(context, R, pointBytes, msg); // R || A || PH(M)
const s = modN(r + k * scalar); // S = (r + k * s) mod L
assertGE0(s); // 0 <= s < l
const res = concatBytes(R, numberToBytesLE(s, Fp.BYTES));
return ensureBytes(res, nByteLength * 2); // 64-byte signature
}
function verify(sig: Hex, msg: Hex, publicKey: Hex, context?: Hex): boolean {
isHex(sig, 'sig');
isHex(msg, 'message');
const len = Fp.BYTES; // Verifies EdDSA signature against message and public key. RFC8032 5.1.7.
sig = ensureBytes(sig, 2 * len); // An extended group equation is checked.
msg = ensureBytes(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 = bytesToNumberLE(sig.slice(len, 2 * len)); // 0 <= s < l
const SB = G.multiplyUnsafe(s);
const k = hashDomainToScalar(context, R.toRawBytes(), A.toRawBytes(), msg);
const RkA = R.add(A.multiplyUnsafe(k));
// [8][S]B = [8]R + [8][k]A'
return RkA.subtract(SB).clearCofactor().equals(Point.ZERO);
}
G._setWindowSize(8); // Enable precomputes. Slows down first publicKey computation by 20ms.
const utils = {
getExtendedPublicKey,
// ed25519 private keys are uniform 32b. No need to check for modulo bias, like in secp256k1.
randomPrivateKey: (): Uint8Array => randomBytes(Fp.BYTES),
/**
* We're doing scalar multiplication (used in getPublicKey etc) with precomputed BASE_POINT
* values. This slows down first getPublicKey() by milliseconds (see Speed section),
* but allows to speed-up subsequent getPublicKey() calls up to 20x.
* @param windowSize 2, 4, 8, 16
*/
precompute(windowSize = 8, point = Point.BASE): typeof Point.BASE {
point._setWindowSize(windowSize);
point.multiply(BigInt(3));
return point;
},
};
return {
CURVE,
getPublicKey,
sign,
verify,
ExtendedPoint: Point,
utils,
};
}

View File

@@ -0,0 +1,236 @@
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
import type { Group, GroupConstructor, AffinePoint } from './curve.js';
import { mod, Field } from './modular.js';
import { CHash, Hex, concatBytes, ensureBytes } from './utils.js';
export type Opts = {
// DST: a domain separation tag
// defined in section 2.2.5
DST: string;
encodeDST: string;
// p: the characteristic of F
// where F is a finite field of characteristic p and order q = p^m
p: bigint;
// m: the extension degree of F, m >= 1
// where F is a finite field of characteristic p and order q = p^m
m: number;
// k: the target security level for the suite in bits
// defined in section 5.1
k: number;
// option to use a message that has already been processed by
// expand_message_xmd
expand?: 'xmd' | 'xof';
// Hash functions for: expand_message_xmd is appropriate for use with a
// wide range of hash functions, including SHA-2, SHA-3, BLAKE2, and others.
// BBS+ uses blake2: https://github.com/hyperledger/aries-framework-go/issues/2247
// TODO: verify that hash is shake if expand==='xof' via types
hash: CHash;
};
export function validateOpts(opts: Opts) {
if (typeof opts.DST !== 'string') throw new Error('Invalid htf/DST');
if (typeof opts.p !== 'bigint') throw new Error('Invalid htf/p');
if (typeof opts.m !== 'number') throw new Error('Invalid htf/m');
if (typeof opts.k !== 'number') throw new Error('Invalid htf/k');
if (opts.expand !== 'xmd' && opts.expand !== 'xof' && opts.expand !== undefined)
throw new Error('Invalid htf/expand');
if (typeof opts.hash !== 'function' || !Number.isSafeInteger(opts.hash.outputLen))
throw new Error('Invalid htf/hash function');
}
// Global symbols in both browsers and Node.js since v11
// See https://github.com/microsoft/TypeScript/issues/31535
declare const TextEncoder: any;
declare const TextDecoder: any;
export function stringToBytes(str: string): Uint8Array {
if (typeof str !== 'string') {
throw new TypeError(`utf8ToBytes expected string, got ${typeof str}`);
}
return new TextEncoder().encode(str);
}
// Octet Stream to Integer (bytesToNumberBE)
function os2ip(bytes: Uint8Array): bigint {
let result = 0n;
for (let i = 0; i < bytes.length; i++) {
result <<= 8n;
result += BigInt(bytes[i]);
}
return result;
}
// Integer to Octet Stream
function i2osp(value: number, length: number): Uint8Array {
if (value < 0 || value >= 1 << (8 * length)) {
throw new Error(`bad I2OSP call: value=${value} length=${length}`);
}
const res = Array.from({ length }).fill(0) as number[];
for (let i = length - 1; i >= 0; i--) {
res[i] = value & 0xff;
value >>>= 8;
}
return new Uint8Array(res);
}
function strxor(a: Uint8Array, b: Uint8Array): Uint8Array {
const arr = new Uint8Array(a.length);
for (let i = 0; i < a.length; i++) {
arr[i] = a[i] ^ b[i];
}
return arr;
}
// Produces a uniformly random byte string using a cryptographic hash function H that outputs b bits
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.4.1
export function expand_message_xmd(
msg: Uint8Array,
DST: Uint8Array,
lenInBytes: number,
H: CHash
): Uint8Array {
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-5.3.3
if (DST.length > 255) DST = H(concatBytes(stringToBytes('H2C-OVERSIZE-DST-'), DST));
const b_in_bytes = H.outputLen;
const r_in_bytes = H.blockLen;
const ell = Math.ceil(lenInBytes / b_in_bytes);
if (ell > 255) throw new Error('Invalid xmd length');
const DST_prime = concatBytes(DST, i2osp(DST.length, 1));
const Z_pad = i2osp(0, r_in_bytes);
const l_i_b_str = i2osp(lenInBytes, 2);
const b = new Array<Uint8Array>(ell);
const b_0 = H(concatBytes(Z_pad, msg, l_i_b_str, i2osp(0, 1), DST_prime));
b[0] = H(concatBytes(b_0, i2osp(1, 1), DST_prime));
for (let i = 1; i <= ell; i++) {
const args = [strxor(b_0, b[i - 1]), i2osp(i + 1, 1), DST_prime];
b[i] = H(concatBytes(...args));
}
const pseudo_random_bytes = concatBytes(...b);
return pseudo_random_bytes.slice(0, lenInBytes);
}
export function expand_message_xof(
msg: Uint8Array,
DST: Uint8Array,
lenInBytes: number,
k: number,
H: CHash
): Uint8Array {
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-5.3.3
// DST = H('H2C-OVERSIZE-DST-' || a_very_long_DST, Math.ceil((lenInBytes * k) / 8));
if (DST.length > 255) {
const dkLen = Math.ceil((2 * k) / 8);
DST = H.create({ dkLen }).update(stringToBytes('H2C-OVERSIZE-DST-')).update(DST).digest();
}
if (lenInBytes > 65535 || DST.length > 255)
throw new Error('expand_message_xof: invalid lenInBytes');
return (
H.create({ dkLen: lenInBytes })
.update(msg)
.update(i2osp(lenInBytes, 2))
// 2. DST_prime = DST || I2OSP(len(DST), 1)
.update(DST)
.update(i2osp(DST.length, 1))
.digest()
);
}
/**
* Hashes arbitrary-length byte strings to a list of one or more elements of a finite field F
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3
* @param msg a byte string containing the message to hash
* @param count the number of elements of F to output
* @param options `{DST: string, p: bigint, m: number, k: number, expand: 'xmd' | 'xof', hash: H}`
* @returns [u_0, ..., u_(count - 1)], a list of field elements.
*/
export function hash_to_field(msg: Uint8Array, count: number, options: Opts): bigint[][] {
// if options is provided but incomplete, fill any missing fields with the
// value in hftDefaults (ie hash to G2).
const log2p = options.p.toString(2).length;
const L = Math.ceil((log2p + options.k) / 8); // section 5.1 of ietf draft link above
const len_in_bytes = count * options.m * L;
const DST = stringToBytes(options.DST);
let pseudo_random_bytes = msg;
if (options.expand === 'xmd') {
pseudo_random_bytes = expand_message_xmd(msg, DST, len_in_bytes, options.hash);
} else if (options.expand === 'xof') {
pseudo_random_bytes = expand_message_xof(msg, DST, len_in_bytes, options.k, options.hash);
}
const u = new Array(count);
for (let i = 0; i < count; i++) {
const e = new Array(options.m);
for (let j = 0; j < options.m; j++) {
const elm_offset = L * (j + i * options.m);
const tv = pseudo_random_bytes.subarray(elm_offset, elm_offset + L);
e[j] = mod(os2ip(tv), options.p);
}
u[i] = e;
}
return u;
}
export function isogenyMap<T, F extends Field<T>>(field: F, map: [T[], T[], T[], T[]]) {
// Make same order as in spec
const COEFF = map.map((i) => Array.from(i).reverse());
return (x: T, y: T) => {
const [xNum, xDen, yNum, yDen] = COEFF.map((val) =>
val.reduce((acc, i) => field.add(field.mul(acc, x), i))
);
x = field.div(xNum, xDen); // xNum / xDen
y = field.mul(y, field.div(yNum, yDen)); // y * (yNum / yDev)
return { x, y };
};
}
export interface H2CPoint<T> extends Group<H2CPoint<T>> {
add(rhs: H2CPoint<T>): H2CPoint<T>;
toAffine(iz?: bigint): AffinePoint<T>;
clearCofactor(): H2CPoint<T>;
assertValidity(): void;
}
export interface H2CPointConstructor<T> extends GroupConstructor<H2CPoint<T>> {
fromAffine(ap: AffinePoint<T>): H2CPoint<T>;
}
export type MapToCurve<T> = (scalar: bigint[]) => AffinePoint<T>;
// Separated from initialization opts, so users won't accidentally change per-curve parameters (changing DST is ok!)
export type htfBasicOpts = {
DST: string;
};
export function hashToCurve<T>(
Point: H2CPointConstructor<T>,
mapToCurve: MapToCurve<T>,
def: Opts
) {
validateOpts(def);
if (typeof mapToCurve !== 'function')
throw new Error('hashToCurve: mapToCurve() has not been defined');
return {
// Encodes byte string to elliptic curve
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3
hashToCurve(msg: Hex, options?: htfBasicOpts) {
if (!mapToCurve) throw new Error('CURVE.mapToCurve() has not been defined');
msg = ensureBytes(msg);
const u = hash_to_field(msg, 2, { ...def, DST: def.DST, ...options } as Opts);
const P = Point.fromAffine(mapToCurve(u[0]))
.add(Point.fromAffine(mapToCurve(u[1])))
.clearCofactor();
P.assertValidity();
return P;
},
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-3
encodeToCurve(msg: Hex, options?: htfBasicOpts) {
if (!mapToCurve) throw new Error('CURVE.mapToCurve() has not been defined');
msg = ensureBytes(msg);
const u = hash_to_field(msg, 1, { ...def, DST: def.encodeDST, ...options } as Opts);
const P = Point.fromAffine(mapToCurve(u[0])).clearCofactor();
P.assertValidity();
return P;
},
};
}

418
src/abstract/modular.ts Normal file
View File

@@ -0,0 +1,418 @@
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
// Utilities for modular arithmetics and finite fields
import {
bitMask,
numberToBytesBE,
numberToBytesLE,
bytesToNumberBE,
bytesToNumberLE,
ensureBytes,
} from './utils.js';
// prettier-ignore
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3);
// prettier-ignore
const _4n = BigInt(4), _5n = BigInt(5), _8n = BigInt(8);
// prettier-ignore
const _9n = BigInt(9), _16n = BigInt(16);
// Calculates a modulo b
export function mod(a: bigint, b: bigint): bigint {
const result = a % b;
return result >= _0n ? result : b + result;
}
/**
* Efficiently exponentiate 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
*/
// TODO: use field version && remove
export function pow(num: bigint, power: bigint, modulo: bigint): bigint {
if (modulo <= _0n || power < _0n) throw new Error('Expected power/modulo > 0');
if (modulo === _1n) return _0n;
let res = _1n;
while (power > _0n) {
if (power & _1n) res = (res * num) % modulo;
num = (num * num) % modulo;
power >>= _1n;
}
return res;
}
// Does x ^ (2 ^ power) mod p. pow2(30, 4) == 30 ^ (2 ^ 4)
// TODO: Fp version?
export function pow2(x: bigint, power: bigint, modulo: bigint): bigint {
let res = x;
while (power-- > _0n) {
res *= res;
res %= modulo;
}
return res;
}
// Inverses number over modulo
export function invert(number: bigint, modulo: bigint): bigint {
if (number === _0n || modulo <= _0n) {
throw new Error(`invert: expected positive integers, got n=${number} mod=${modulo}`);
}
// Eucledian GCD https://brilliant.org/wiki/extended-euclidean-algorithm/
let a = mod(number, modulo);
let b = modulo;
// prettier-ignore
let x = _0n, y = _1n, u = _1n, v = _0n;
while (a !== _0n) {
// JIT applies optimization if those two lines follow each other
const q = b / a;
const r = b % a;
const m = x - u * q;
const n = y - v * q;
// prettier-ignore
b = a, a = r, x = u, y = v, u = m, v = n;
}
const gcd = b;
if (gcd !== _1n) throw new Error('invert: does not exist');
return mod(x, modulo);
}
// Tonelli-Shanks algorithm
// Paper 1: https://eprint.iacr.org/2012/685.pdf (page 12)
// Paper 2: Square Roots from 1; 24, 51, 10 to Dan Shanks
export function tonelliShanks(P: bigint) {
// Legendre constant: used to calculate Legendre symbol (a | p),
// which denotes the value of a^((p-1)/2) (mod p).
// (a | p) ≡ 1 if a is a square (mod p)
// (a | p) ≡ -1 if a is not a square (mod p)
// (a | p) ≡ 0 if a ≡ 0 (mod p)
const legendreC = (P - _1n) / _2n;
let Q: bigint, S: number, Z: bigint;
// Step 1: By factoring out powers of 2 from p - 1,
// find q and s such that p - 1 = q*(2^s) with q odd
for (Q = P - _1n, S = 0; Q % _2n === _0n; Q /= _2n, S++);
// Step 2: Select a non-square z such that (z | p) ≡ -1 and set c ≡ zq
for (Z = _2n; Z < P && pow(Z, legendreC, P) !== P - _1n; Z++);
// Fast-path
if (S === 1) {
const p1div4 = (P + _1n) / _4n;
return function tonelliFast<T>(Fp: Field<T>, n: T) {
const root = Fp.pow(n, p1div4);
if (!Fp.eql(Fp.sqr(root), n)) throw new Error('Cannot find square root');
return root;
};
}
// Slow-path
const Q1div2 = (Q + _1n) / _2n;
return function tonelliSlow<T>(Fp: Field<T>, n: T): T {
// Step 0: Check that n is indeed a square: (n | p) should not be ≡ -1
if (Fp.pow(n, legendreC) === Fp.neg(Fp.ONE)) throw new Error('Cannot find square root');
let r = S;
// TODO: will fail at Fp2/etc
let g = Fp.pow(Fp.mul(Fp.ONE, Z), Q); // will update both x and b
let x = Fp.pow(n, Q1div2); // first guess at the square root
let b = Fp.pow(n, Q); // first guess at the fudge factor
while (!Fp.eql(b, Fp.ONE)) {
if (Fp.eql(b, Fp.ZERO)) return Fp.ZERO; // https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm (4. If t = 0, return r = 0)
// Find m such b^(2^m)==1
let m = 1;
for (let t2 = Fp.sqr(b); m < r; m++) {
if (Fp.eql(t2, Fp.ONE)) break;
t2 = Fp.sqr(t2); // t2 *= t2
}
// NOTE: r-m-1 can be bigger than 32, need to convert to bigint before shift, otherwise there will be overflow
const ge = Fp.pow(g, _1n << BigInt(r - m - 1)); // ge = 2^(r-m-1)
g = Fp.sqr(ge); // g = ge * ge
x = Fp.mul(x, ge); // x *= ge
b = Fp.mul(b, g); // b *= g
r = m;
}
return x;
};
}
export function FpSqrt(P: bigint) {
// NOTE: different algorithms can give different roots, it is up to user to decide which one they want.
// For example there is FpSqrtOdd/FpSqrtEven to choice root based on oddness (used for hash-to-curve).
// P ≡ 3 (mod 4)
// √n = n^((P+1)/4)
if (P % _4n === _3n) {
// Not all roots possible!
// const ORDER =
// 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaabn;
// const NUM = 72057594037927816n;
const p1div4 = (P + _1n) / _4n;
return function sqrt3mod4<T>(Fp: Field<T>, n: T) {
const root = Fp.pow(n, p1div4);
// Throw if root**2 != n
if (!Fp.eql(Fp.sqr(root), n)) throw new Error('Cannot find square root');
return root;
};
}
// Atkin algorithm for q ≡ 5 (mod 8), https://eprint.iacr.org/2012/685.pdf (page 10)
if (P % _8n === _5n) {
const c1 = (P - _5n) / _8n;
return function sqrt5mod8<T>(Fp: Field<T>, n: T) {
const n2 = Fp.mul(n, _2n);
const v = Fp.pow(n2, c1);
const nv = Fp.mul(n, v);
const i = Fp.mul(Fp.mul(nv, _2n), v);
const root = Fp.mul(nv, Fp.sub(i, Fp.ONE));
if (!Fp.eql(Fp.sqr(root), n)) throw new Error('Cannot find square root');
return root;
};
}
// P ≡ 9 (mod 16)
if (P % _16n === _9n) {
// NOTE: tonelli is too slow for bls-Fp2 calculations even on start
// Means we cannot use sqrt for constants at all!
//
// const c1 = Fp.sqrt(Fp.negate(Fp.ONE)); // 1. c1 = sqrt(-1) in F, i.e., (c1^2) == -1 in F
// const c2 = Fp.sqrt(c1); // 2. c2 = sqrt(c1) in F, i.e., (c2^2) == c1 in F
// const c3 = Fp.sqrt(Fp.negate(c1)); // 3. c3 = sqrt(-c1) in F, i.e., (c3^2) == -c1 in F
// const c4 = (P + _7n) / _16n; // 4. c4 = (q + 7) / 16 # Integer arithmetic
// sqrt = (x) => {
// let tv1 = Fp.pow(x, c4); // 1. tv1 = x^c4
// let tv2 = Fp.mul(c1, tv1); // 2. tv2 = c1 * tv1
// const tv3 = Fp.mul(c2, tv1); // 3. tv3 = c2 * tv1
// let tv4 = Fp.mul(c3, tv1); // 4. tv4 = c3 * tv1
// const e1 = Fp.equals(Fp.square(tv2), x); // 5. e1 = (tv2^2) == x
// const e2 = Fp.equals(Fp.square(tv3), x); // 6. e2 = (tv3^2) == x
// tv1 = Fp.cmov(tv1, tv2, e1); // 7. tv1 = CMOV(tv1, tv2, e1) # Select tv2 if (tv2^2) == x
// tv2 = Fp.cmov(tv4, tv3, e2); // 8. tv2 = CMOV(tv4, tv3, e2) # Select tv3 if (tv3^2) == x
// const e3 = Fp.equals(Fp.square(tv2), x); // 9. e3 = (tv2^2) == x
// return Fp.cmov(tv1, tv2, e3); // 10. z = CMOV(tv1, tv2, e3) # Select the sqrt from tv1 and tv2
// }
}
// Other cases: Tonelli-Shanks algorithm
return tonelliShanks(P);
}
// Little-endian check for first LE bit (last BE bit);
export const isNegativeLE = (num: bigint, modulo: bigint) => (mod(num, modulo) & _1n) === _1n;
// 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
export interface Field<T> {
ORDER: bigint;
BYTES: number;
BITS: number;
MASK: bigint;
ZERO: T;
ONE: T;
// 1-arg
create: (num: T) => T;
isValid: (num: T) => boolean;
is0: (num: T) => boolean;
neg(num: T): T;
inv(num: T): T;
sqrt(num: T): T;
sqr(num: T): T;
// 2-args
eql(lhs: T, rhs: T): boolean;
add(lhs: T, rhs: T): T;
sub(lhs: T, rhs: T): T;
mul(lhs: T, rhs: T | bigint): T;
pow(lhs: T, power: bigint): T;
div(lhs: T, rhs: T | bigint): T;
// N for NonNormalized (for now)
addN(lhs: T, rhs: T): T;
subN(lhs: T, rhs: T): T;
mulN(lhs: T, rhs: T | bigint): T;
sqrN(num: T): T;
// Optional
// Should be same as sgn0 function in https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/
// NOTE: sgn0 is 'negative in LE', which is same as odd. And negative in LE is kinda strange definition anyway.
isOdd?(num: T): boolean; // Odd instead of even since we have it for Fp2
// legendre?(num: T): T;
pow(lhs: T, power: bigint): T;
invertBatch: (lst: T[]) => T[];
toBytes(num: T): Uint8Array;
fromBytes(bytes: Uint8Array): T;
// If c is False, CMOV returns a, otherwise it returns b.
cmov(a: T, b: T, c: boolean): T;
}
// prettier-ignore
const FIELD_FIELDS = [
'create', 'isValid', 'is0', 'neg', 'inv', 'sqrt', 'sqr',
'eql', 'add', 'sub', 'mul', 'pow', 'div',
'addN', 'subN', 'mulN', 'sqrN'
] as const;
export function validateField<T>(field: Field<T>) {
for (const i of ['ORDER', 'MASK'] as const) {
if (typeof field[i] !== 'bigint')
throw new Error(`Invalid field param ${i}=${field[i]} (${typeof field[i]})`);
}
for (const i of ['BYTES', 'BITS'] as const) {
if (typeof field[i] !== 'number')
throw new Error(`Invalid field param ${i}=${field[i]} (${typeof field[i]})`);
}
for (const i of FIELD_FIELDS) {
if (typeof field[i] !== 'function')
throw new Error(`Invalid field param ${i}=${field[i]} (${typeof field[i]})`);
}
}
// Generic field functions
export function FpPow<T>(f: Field<T>, num: T, power: bigint): T {
// Should have same speed as pow for bigints
// TODO: benchmark!
if (power < _0n) throw new Error('Expected power > 0');
if (power === _0n) return f.ONE;
if (power === _1n) return num;
let p = f.ONE;
let d = num;
while (power > _0n) {
if (power & _1n) p = f.mul(p, d);
d = f.sqr(d);
power >>= 1n;
}
return p;
}
export function FpInvertBatch<T>(f: Field<T>, nums: T[]): T[] {
const tmp = new Array(nums.length);
// Walk from first to last, multiply them by each other MOD p
const lastMultiplied = nums.reduce((acc, num, i) => {
if (f.is0(num)) return acc;
tmp[i] = acc;
return f.mul(acc, num);
}, f.ONE);
// Invert last element
const inverted = f.inv(lastMultiplied);
// Walk from last to first, multiply them by inverted each other MOD p
nums.reduceRight((acc, num, i) => {
if (f.is0(num)) return acc;
tmp[i] = f.mul(acc, tmp[i]);
return f.mul(acc, num);
}, inverted);
return tmp;
}
export function FpDiv<T>(f: Field<T>, lhs: T, rhs: T | bigint): T {
return f.mul(lhs, typeof rhs === 'bigint' ? invert(rhs, f.ORDER) : f.inv(rhs));
}
// This function returns True whenever the value x is a square in the field F.
export function FpIsSquare<T>(f: Field<T>) {
const legendreConst = (f.ORDER - _1n) / _2n; // Integer arithmetic
return (x: T): boolean => {
const p = f.pow(x, legendreConst);
return f.eql(p, f.ZERO) || f.eql(p, f.ONE);
};
}
// CURVE.n lengths
export function nLength(n: bigint, nBitLength?: number) {
// Bit size, byte size of CURVE.n
const _nBitLength = nBitLength !== undefined ? nBitLength : n.toString(2).length;
const nByteLength = Math.ceil(_nBitLength / 8);
return { nBitLength: _nBitLength, nByteLength };
}
// NOTE: very fragile, always bench. Major performance points:
// - NonNormalized ops
// - Object.freeze
// - same shape of object (don't add/remove keys)
type FpField = Field<bigint> & Required<Pick<Field<bigint>, 'isOdd'>>;
export function Fp(
ORDER: bigint,
bitLen?: number,
isLE = false,
redef: Partial<Field<bigint>> = {}
): Readonly<FpField> {
if (ORDER <= _0n) throw new Error(`Expected Fp ORDER > 0, got ${ORDER}`);
const { nBitLength: BITS, nByteLength: BYTES } = nLength(ORDER, bitLen);
if (BYTES > 2048) throw new Error('Field lengths over 2048 bytes are not supported');
const sqrtP = FpSqrt(ORDER);
const f: Readonly<FpField> = Object.freeze({
ORDER,
BITS,
BYTES,
MASK: bitMask(BITS),
ZERO: _0n,
ONE: _1n,
create: (num) => mod(num, ORDER),
isValid: (num) => {
if (typeof num !== 'bigint')
throw new Error(`Invalid field element: expected bigint, got ${typeof num}`);
return _0n <= num && num < ORDER; // 0 is valid element, but it's not invertible
},
is0: (num) => num === _0n,
isOdd: (num) => (num & _1n) === _1n,
neg: (num) => mod(-num, ORDER),
eql: (lhs, rhs) => lhs === rhs,
sqr: (num) => mod(num * num, ORDER),
add: (lhs, rhs) => mod(lhs + rhs, ORDER),
sub: (lhs, rhs) => mod(lhs - rhs, ORDER),
mul: (lhs, rhs) => mod(lhs * rhs, ORDER),
pow: (num, power) => FpPow(f, num, power),
div: (lhs, rhs) => mod(lhs * invert(rhs, ORDER), ORDER),
// Same as above, but doesn't normalize
sqrN: (num) => num * num,
addN: (lhs, rhs) => lhs + rhs,
subN: (lhs, rhs) => lhs - rhs,
mulN: (lhs, rhs) => lhs * rhs,
inv: (num) => invert(num, ORDER),
sqrt: redef.sqrt || ((n) => sqrtP(f, n)),
invertBatch: (lst) => FpInvertBatch(f, lst),
// TODO: do we really need constant cmov?
// We don't have const-time bigints anyway, so probably will be not very useful
cmov: (a, b, c) => (c ? b : a),
toBytes: (num) => (isLE ? numberToBytesLE(num, BYTES) : numberToBytesBE(num, BYTES)),
fromBytes: (bytes) => {
if (bytes.length !== BYTES)
throw new Error(`Fp.fromBytes: expected ${BYTES}, got ${bytes.length}`);
return isLE ? bytesToNumberLE(bytes) : bytesToNumberBE(bytes);
},
} as FpField);
return Object.freeze(f);
}
export function FpSqrtOdd<T>(Fp: Field<T>, elm: T) {
if (!Fp.isOdd) throw new Error(`Field doesn't have isOdd`);
const root = Fp.sqrt(elm);
return Fp.isOdd(root) ? root : Fp.neg(root);
}
export function FpSqrtEven<T>(Fp: Field<T>, elm: T) {
if (!Fp.isOdd) throw new Error(`Field doesn't have isOdd`);
const root = Fp.sqrt(elm);
return Fp.isOdd(root) ? Fp.neg(root) : root;
}
/**
* FIPS 186 B.4.1-compliant "constant-time" private key generation utility.
* Can take (n+8) or more bytes of uniform input e.g. from CSPRNG or KDF
* and convert them into private scalar, with the modulo bias being neglible.
* Needs at least 40 bytes of input for 32-byte private key.
* https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
* @param hash hash output from SHA3 or a similar function
* @returns valid private scalar
*/
export function hashToPrivateScalar(
hash: string | Uint8Array,
groupOrder: bigint,
isLE = false
): bigint {
hash = ensureBytes(hash);
const hashLen = hash.length;
const minLen = nLength(groupOrder).nByteLength + 8;
if (minLen < 24 || hashLen < minLen || hashLen > 1024)
throw new Error(`hashToPrivateScalar: expected ${minLen}-1024 bytes of input, got ${hashLen}`);
const num = isLE ? bytesToNumberLE(hash) : bytesToNumberBE(hash);
return mod(num, groupOrder - _1n) + _1n;
}

View File

@@ -1,10 +1,6 @@
import * as mod from './modular.js'; /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
import { import { mod, pow } from './modular.js';
ensureBytes, import { ensureBytes, numberToBytesLE, bytesToNumberLE } from './utils.js';
numberToBytesLE,
bytesToNumberLE,
// nLength,
} from './utils.js';
const _0n = BigInt(0); const _0n = BigInt(0);
const _1n = BigInt(1); const _1n = BigInt(1);
@@ -23,8 +19,9 @@ export type CurveType = {
Gu: string; Gu: string;
}; };
export type CurveFn = { export type CurveFn = {
scalarMult: (u: Hex, scalar: Hex) => Uint8Array; scalarMult: (scalar: Hex, u: Hex) => Uint8Array;
scalarMultBase: (scalar: Hex) => Uint8Array; scalarMultBase: (scalar: Hex) => Uint8Array;
getSharedSecret: (privateKeyA: Hex, publicKeyB: Hex) => Uint8Array;
getPublicKey: (privateKey: Hex) => Uint8Array; getPublicKey: (privateKey: Hex) => Uint8Array;
Gu: string; Gu: string;
}; };
@@ -49,7 +46,6 @@ function validateOpts(curve: CurveType) {
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`); throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
} }
// Set defaults // Set defaults
// ...nLength(curve.n, curve.nBitLength),
return Object.freeze({ ...curve } as const); return Object.freeze({ ...curve } as const);
} }
@@ -58,12 +54,12 @@ function validateOpts(curve: CurveType) {
export function montgomery(curveDef: CurveType): CurveFn { export function montgomery(curveDef: CurveType): CurveFn {
const CURVE = validateOpts(curveDef); const CURVE = validateOpts(curveDef);
const { P } = CURVE; const { P } = CURVE;
const modP = (a: bigint) => mod.mod(a, P); const modP = (a: bigint) => mod(a, P);
const montgomeryBits = CURVE.montgomeryBits; const montgomeryBits = CURVE.montgomeryBits;
const montgomeryBytes = Math.ceil(montgomeryBits / 8); const montgomeryBytes = Math.ceil(montgomeryBits / 8);
const fieldLen = CURVE.nByteLength; const fieldLen = CURVE.nByteLength;
const adjustScalarBytes = CURVE.adjustScalarBytes || ((bytes: Uint8Array) => bytes); const adjustScalarBytes = CURVE.adjustScalarBytes || ((bytes: Uint8Array) => bytes);
const powPminus2 = CURVE.powPminus2 || ((x: bigint) => mod.pow(x, P - BigInt(2), P)); const powPminus2 = CURVE.powPminus2 || ((x: bigint) => pow(x, P - BigInt(2), P));
/** /**
* Checks for num to be in range: * Checks for num to be in range:
@@ -71,7 +67,7 @@ export function montgomery(curveDef: CurveType): CurveFn {
* For strict == false: `0 <= num < max`. * For strict == false: `0 <= num < max`.
* Converts non-float safe numbers to bigints. * Converts non-float safe numbers to bigints.
*/ */
function normalizeScalar(num: number | bigint, max: bigint, strict = true): bigint { function normalizeScalar(num: bigint, max: bigint, strict = true): bigint {
if (!max) throw new TypeError('Specify max value'); if (!max) throw new TypeError('Specify max value');
if (typeof num === 'number' && Number.isSafeInteger(num)) num = BigInt(num); if (typeof num === 'number' && Number.isSafeInteger(num)) num = BigInt(num);
if (typeof num === 'bigint' && num < max) { if (typeof num === 'bigint' && num < max) {
@@ -86,7 +82,7 @@ export function montgomery(curveDef: CurveType): CurveFn {
// cswap from RFC7748 // cswap from RFC7748
// NOTE: cswap is not from RFC7748! // NOTE: cswap is not from RFC7748!
/* /*
cswap(swap, x_2, x_3): cswap(swap, x_2, x_3):
dummy = mask(swap) AND (x_2 XOR x_3) dummy = mask(swap) AND (x_2 XOR x_3)
x_2 = x_2 XOR dummy x_2 = x_2 XOR dummy
@@ -185,8 +181,14 @@ export function montgomery(curveDef: CurveType): CurveFn {
throw new Error(`Expected ${montgomeryBytes} or ${fieldLen} bytes, got ${bytes.length}`); throw new Error(`Expected ${montgomeryBytes} or ${fieldLen} bytes, got ${bytes.length}`);
return bytesToNumberLE(adjustScalarBytes(bytes)); return bytesToNumberLE(adjustScalarBytes(bytes));
} }
// Multiply point u by scalar /**
function scalarMult(u: Hex, scalar: Hex): Uint8Array { * Computes shared secret between private key "scalar" and public key's "u" (x) coordinate.
* We can get 'y' coordinate from 'u',
* but Point.fromHex also wants 'x' coordinate oddity flag,
* and we cannot get 'x' without knowing 'v'.
* Need to add generic conversion between twisted edwards and complimentary curve for JubJub.
*/
function scalarMult(scalar: Hex, u: Hex): Uint8Array {
const pointU = decodeUCoordinate(u); const pointU = decodeUCoordinate(u);
const _scalar = decodeScalar(scalar); const _scalar = decodeScalar(scalar);
const pu = montgomeryLadder(pointU, _scalar); const pu = montgomeryLadder(pointU, _scalar);
@@ -195,18 +197,20 @@ export function montgomery(curveDef: CurveType): CurveFn {
if (pu === _0n) throw new Error('Invalid private or public key received'); if (pu === _0n) throw new Error('Invalid private or public key received');
return encodeUCoordinate(pu); return encodeUCoordinate(pu);
} }
// Multiply base point by scalar /**
* Computes public key from private.
* Executes scalar multiplication of curve's base point by scalar.
* @param scalar private key
* @returns new public key
*/
function scalarMultBase(scalar: Hex): Uint8Array { function scalarMultBase(scalar: Hex): Uint8Array {
return scalarMult(CURVE.Gu, scalar); return scalarMult(scalar, CURVE.Gu);
} }
return { return {
// NOTE: we can get 'y' coordinate from 'u', but Point.fromHex also wants 'x' coordinate oddity flag, and we cannot get 'x' without knowing 'v'
// Need to add generic conversion between twisted edwards and complimentary curve for JubJub
scalarMult, scalarMult,
scalarMultBase, scalarMultBase,
// NOTE: these function work on complimentary montgomery curve getSharedSecret: (privateKey: Hex, publicKey: Hex) => scalarMult(privateKey, publicKey),
// getSharedSecret: (privateKey: Hex, publicKey: Hex) => scalarMult(publicKey, privateKey),
getPublicKey: (privateKey: Hex): Uint8Array => scalarMultBase(privateKey), getPublicKey: (privateKey: Hex): Uint8Array => scalarMultBase(privateKey),
Gu: CURVE.Gu, Gu: CURVE.Gu,
}; };

119
src/abstract/poseidon.ts Normal file
View File

@@ -0,0 +1,119 @@
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
// Poseidon Hash: https://eprint.iacr.org/2019/458.pdf, https://www.poseidon-hash.info
import { Field, validateField, FpPow } from './modular.js';
// We don't provide any constants, since different implementations use different constants.
// For reference constants see './test/poseidon.test.js'.
export type PoseidonOpts = {
Fp: Field<bigint>;
t: number;
roundsFull: number;
roundsPartial: number;
sboxPower?: number;
reversePartialPowIdx?: boolean; // Hack for stark
mds: bigint[][];
roundConstants: bigint[][];
};
export function validateOpts(opts: PoseidonOpts) {
const { Fp } = opts;
validateField(Fp);
for (const i of ['t', 'roundsFull', 'roundsPartial'] as const) {
if (typeof opts[i] !== 'number' || !Number.isSafeInteger(opts[i]))
throw new Error(`Poseidon: invalid param ${i}=${opts[i]} (${typeof opts[i]})`);
}
if (opts.reversePartialPowIdx !== undefined && typeof opts.reversePartialPowIdx !== 'boolean')
throw new Error(`Poseidon: invalid param reversePartialPowIdx=${opts.reversePartialPowIdx}`);
// Default is 5, but by some reasons stark uses 3
let sboxPower = opts.sboxPower;
if (sboxPower === undefined) sboxPower = 5;
if (typeof sboxPower !== 'number' || !Number.isSafeInteger(sboxPower))
throw new Error(`Poseidon wrong sboxPower=${sboxPower}`);
const _sboxPower = BigInt(sboxPower);
let sboxFn = (n: bigint) => FpPow(Fp, n, _sboxPower);
// Unwrapped sbox power for common cases (195->142μs)
if (sboxPower === 3) sboxFn = (n: bigint) => Fp.mul(Fp.sqrN(n), n);
else if (sboxPower === 5) sboxFn = (n: bigint) => Fp.mul(Fp.sqrN(Fp.sqrN(n)), n);
if (opts.roundsFull % 2 !== 0)
throw new Error(`Poseidon roundsFull is not even: ${opts.roundsFull}`);
const rounds = opts.roundsFull + opts.roundsPartial;
if (!Array.isArray(opts.roundConstants) || opts.roundConstants.length !== rounds)
throw new Error('Poseidon: wrong round constants');
const roundConstants = opts.roundConstants.map((rc) => {
if (!Array.isArray(rc) || rc.length !== opts.t)
throw new Error(`Poseidon wrong round constants: ${rc}`);
return rc.map((i) => {
if (typeof i !== 'bigint' || !Fp.isValid(i))
throw new Error(`Poseidon wrong round constant=${i}`);
return Fp.create(i);
});
});
// MDS is TxT matrix
if (!Array.isArray(opts.mds) || opts.mds.length !== opts.t)
throw new Error('Poseidon: wrong MDS matrix');
const mds = opts.mds.map((mdsRow) => {
if (!Array.isArray(mdsRow) || mdsRow.length !== opts.t)
throw new Error(`Poseidon MDS matrix row: ${mdsRow}`);
return mdsRow.map((i) => {
if (typeof i !== 'bigint') throw new Error(`Poseidon MDS matrix value=${i}`);
return Fp.create(i);
});
});
return Object.freeze({ ...opts, rounds, sboxFn, roundConstants, mds });
}
export function splitConstants(rc: bigint[], t: number) {
if (typeof t !== 'number') throw new Error('poseidonSplitConstants: wrong t');
if (!Array.isArray(rc) || rc.length % t) throw new Error('poseidonSplitConstants: wrong rc');
const res = [];
let tmp = [];
for (let i = 0; i < rc.length; i++) {
tmp.push(rc[i]);
if (tmp.length === t) {
res.push(tmp);
tmp = [];
}
}
return res;
}
export function poseidon(opts: PoseidonOpts) {
const { t, Fp, rounds, sboxFn, reversePartialPowIdx } = validateOpts(opts);
const halfRoundsFull = Math.floor(opts.roundsFull / 2);
const partialIdx = reversePartialPowIdx ? t - 1 : 0;
const poseidonRound = (values: bigint[], isFull: boolean, idx: number) => {
values = values.map((i, j) => Fp.add(i, opts.roundConstants[idx][j]));
if (isFull) values = values.map((i) => sboxFn(i));
else values[partialIdx] = sboxFn(values[partialIdx]);
// Matrix multiplication
values = opts.mds.map((i) =>
i.reduce((acc, i, j) => Fp.add(acc, Fp.mulN(i, values[j])), Fp.ZERO)
);
return values;
};
const poseidonHash = function poseidonHash(values: bigint[]) {
if (!Array.isArray(values) || values.length !== t)
throw new Error(`Poseidon: wrong values (expected array of bigints with length ${t})`);
values = values.map((i) => {
if (typeof i !== 'bigint') throw new Error(`Poseidon: wrong value=${i} (${typeof i})`);
return Fp.create(i);
});
let round = 0;
// Apply r_f/2 full rounds.
for (let i = 0; i < halfRoundsFull; i++) values = poseidonRound(values, true, round++);
// Apply r_p partial rounds.
for (let i = 0; i < opts.roundsPartial; i++) values = poseidonRound(values, false, round++);
// Apply r_f/2 full rounds.
for (let i = 0; i < halfRoundsFull; i++) values = poseidonRound(values, true, round++);
if (round !== rounds)
throw new Error(`Poseidon: wrong number of rounds: last round=${round}, total=${rounds}`);
return values;
};
// For verification in tests
poseidonHash.roundConstants = opts.roundConstants;
return poseidonHash;
}

121
src/abstract/utils.ts Normal file
View File

@@ -0,0 +1,121 @@
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
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 CHash = {
(message: Uint8Array | string): Uint8Array;
blockLen: number;
outputLen: number;
create(opts?: { dkLen?: number }): any; // For shake
};
export type FHash = (message: Uint8Array | string) => Uint8Array;
const hexes = Array.from({ length: 256 }, (v, i) => i.toString(16).padStart(2, '0'));
export function bytesToHex(bytes: Uint8Array): string {
if (!u8a(bytes)) throw new Error('Expected Uint8Array');
// pre-caching improves the speed 6x
let hex = '';
for (let i = 0; i < bytes.length; i++) {
hex += hexes[bytes[i]];
}
return hex;
}
export function numberToHexUnpadded(num: number | bigint): string {
const hex = num.toString(16);
return hex.length & 1 ? `0${hex}` : hex;
}
export function hexToNumber(hex: string): bigint {
if (typeof hex !== 'string') throw new Error('hexToNumber: expected string, got ' + typeof hex);
// Big Endian
return BigInt(`0x${hex}`);
}
// Caching slows it down 2-3x
export function hexToBytes(hex: string): Uint8Array {
if (typeof hex !== 'string') throw new Error('hexToBytes: expected string, got ' + typeof hex);
if (hex.length % 2) throw new Error('hexToBytes: received invalid unpadded hex ' + hex.length);
const array = new Uint8Array(hex.length / 2);
for (let i = 0; i < array.length; i++) {
const j = i * 2;
const hexByte = hex.slice(j, j + 2);
const byte = Number.parseInt(hexByte, 16);
if (Number.isNaN(byte) || byte < 0) throw new Error('Invalid byte sequence');
array[i] = byte;
}
return array;
}
// Big Endian
export function bytesToNumberBE(bytes: Uint8Array): bigint {
return hexToNumber(bytesToHex(bytes));
}
export function bytesToNumberLE(bytes: Uint8Array): bigint {
if (!u8a(bytes)) throw new Error('Expected Uint8Array');
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) => {
let hex = n.toString(16);
if (hex.length & 1) hex = '0' + hex;
return hexToBytes(hex);
};
export function ensureBytes(hex: Hex, expectedLength?: number): Uint8Array {
// Uint8Array.from() instead of hash.slice() because node.js Buffer
// is instance of Uint8Array, and its slice() creates **mutable** copy
const bytes = u8a(hex) ? Uint8Array.from(hex) : hexToBytes(hex);
if (typeof expectedLength === 'number' && bytes.length !== expectedLength)
throw new Error(`Expected ${expectedLength} bytes`);
return bytes;
}
// Copies several Uint8Arrays into one.
export function concatBytes(...arrays: Uint8Array[]): Uint8Array {
if (!arrays.every((b) => u8a(b))) throw new Error('Uint8Array list expected');
if (arrays.length === 1) return arrays[0];
const length = arrays.reduce((a, arr) => a + arr.length, 0);
const result = new Uint8Array(length);
for (let i = 0, pad = 0; i < arrays.length; i++) {
const arr = arrays[i];
result.set(arr, pad);
pad += arr.length;
}
return result;
}
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;
}
// Bit operations
// Amount of bits inside bigint (Same as n.toString(2).length)
export function bitLen(n: bigint) {
let len;
for (len = 0; n > 0n; n >>= _1n, len += 1);
return len;
}
// Gets single bit at position. NOTE: first bit position is 0 (same as arrays)
// Same as !!+Array.from(n.toString(2)).reverse()[pos]
export 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.
export const bitMask = (n: number) => (_2n << BigInt(n - 1)) - _1n;

1223
src/abstract/weierstrass.ts Normal file

File diff suppressed because it is too large Load Diff

1241
src/bls12-381.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,8 @@
/*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
import { weierstrass } from '@noble/curves/weierstrass';
import { sha256 } from '@noble/hashes/sha256'; import { sha256 } from '@noble/hashes/sha256';
import { weierstrass } from './abstract/weierstrass.js';
import { getHash } from './_shortw_utils.js'; import { getHash } from './_shortw_utils.js';
import { Fp } from './abstract/modular.js';
/** /**
* bn254 pairing-friendly curve. * bn254 pairing-friendly curve.
* Previously known as alt_bn_128, when it had 128-bit security. * Previously known as alt_bn_128, when it had 128-bit security.
@@ -12,7 +12,7 @@ import { getHash } from './_shortw_utils.js';
export const bn254 = weierstrass({ export const bn254 = weierstrass({
a: BigInt(0), a: BigInt(0),
b: BigInt(3), b: BigInt(3),
P: BigInt('0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47'), Fp: Fp(BigInt('0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47')),
n: BigInt('0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001'), n: BigInt('0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001'),
Gx: BigInt(1), Gx: BigInt(1),
Gy: BigInt(2), Gy: BigInt(2),

View File

@@ -1,9 +1,9 @@
/*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
import { sha512 } from '@noble/hashes/sha512'; import { sha512 } from '@noble/hashes/sha512';
import { concatBytes, randomBytes, utf8ToBytes } from '@noble/hashes/utils'; import { concatBytes, randomBytes, utf8ToBytes } from '@noble/hashes/utils';
import { twistedEdwards, ExtendedPointType } from '@noble/curves/edwards'; import { twistedEdwards, ExtPointType } from './abstract/edwards.js';
import { montgomery } from '@noble/curves/montgomery'; import { montgomery } from './abstract/montgomery.js';
import { mod, pow2, isNegativeLE } from '@noble/curves/modular'; import { mod, pow2, isNegativeLE, Fp as Field, FpSqrtEven } from './abstract/modular.js';
import { import {
ensureBytes, ensureBytes,
equalBytes, equalBytes,
@@ -11,7 +11,8 @@ import {
bytesToNumberLE, bytesToNumberLE,
numberToBytesLE, numberToBytesLE,
Hex, Hex,
} from '@noble/curves/utils'; } from './abstract/utils.js';
import * as htf from './abstract/hash-to-curve.js';
/** /**
* ed25519 Twisted Edwards curve with following addons: * ed25519 Twisted Edwards curve with following addons:
@@ -49,18 +50,14 @@ function ed25519_pow_2_252_3(x: bigint) {
// ^ To pow to (p+3)/8, multiply it by x. // ^ To pow to (p+3)/8, multiply it by x.
return { pow_p_5_8, b2 }; return { pow_p_5_8, b2 };
} }
/**
* For X25519, in order to decode 32 random bytes as an integer scalar,
* set the
* three least significant bits of the first byte 0b1111_1000,
* and the most significant bit of the last to zero 0b0111_1111,
* set the second most significant bit of the last byte to 1 0b0100_0000
*/
function adjustScalarBytes(bytes: Uint8Array): Uint8Array { function adjustScalarBytes(bytes: Uint8Array): Uint8Array {
bytes[0] &= 248; // Section 5: For X25519, in order to decode 32 random bytes as an integer scalar,
bytes[31] &= 127; // set the three least significant bits of the first byte
bytes[31] |= 64; bytes[0] &= 248; // 0b1111_1000
// and the most significant bit of the last to zero,
bytes[31] &= 127; // 0b0111_1111
// set the second most significant bit of the last byte to 1
bytes[31] |= 64; // 0b0100_0000
return bytes; return bytes;
} }
// sqrt(u/v) // sqrt(u/v)
@@ -95,6 +92,8 @@ export const ED25519_TORSION_SUBGROUP = [
'c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa', 'c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa',
]; ];
const Fp = Field(ED25519_P, undefined, true);
const ED25519_DEF = { const ED25519_DEF = {
// Param: a // Param: a
a: BigInt(-1), a: BigInt(-1),
@@ -102,7 +101,7 @@ const ED25519_DEF = {
// Negative number is P - number, and division is invert(number, P) // Negative number is P - number, and division is invert(number, P)
d: BigInt('37095705934669439343138083508754565189542113879843219016388785533085940283555'), d: BigInt('37095705934669439343138083508754565189542113879843219016388785533085940283555'),
// Finite field 𝔽p over which we'll do calculations; 2n ** 255n - 19n // Finite field 𝔽p over which we'll do calculations; 2n ** 255n - 19n
P: ED25519_P, Fp,
// Subgroup order: how many points ed25519 has // Subgroup order: how many points ed25519 has
// 2n ** 252n + 27742317777372353535851937790883648493n; // 2n ** 252n + 27742317777372353535851937790883648493n;
n: BigInt('7237005577332262213973186563042994240857116359379907606001950938285454250989'), n: BigInt('7237005577332262213973186563042994240857116359379907606001950938285454250989'),
@@ -152,8 +151,95 @@ export const x25519 = montgomery({
adjustScalarBytes, adjustScalarBytes,
}); });
// Hash To Curve Elligator2 Map (NOTE: different from ristretto255 elligator)
// NOTE: very important part is usage of FpSqrtEven for ELL2_C1_EDWARDS, since
// SageMath returns different root first and everything falls apart
const ELL2_C1 = (Fp.ORDER + BigInt(3)) / BigInt(8); // 1. c1 = (q + 3) / 8 # Integer arithmetic
const ELL2_C2 = Fp.pow(_2n, ELL2_C1); // 2. c2 = 2^c1
const ELL2_C3 = Fp.sqrt(Fp.neg(Fp.ONE)); // 3. c3 = sqrt(-1)
const ELL2_C4 = (Fp.ORDER - BigInt(5)) / BigInt(8); // 4. c4 = (q - 5) / 8 # Integer arithmetic
const ELL2_J = BigInt(486662);
// prettier-ignore
function map_to_curve_elligator2_curve25519(u: bigint) {
let tv1 = Fp.sqr(u); // 1. tv1 = u^2
tv1 = Fp.mul(tv1, _2n); // 2. tv1 = 2 * tv1
let xd = Fp.add(tv1, Fp.ONE); // 3. xd = tv1 + 1 # Nonzero: -1 is square (mod p), tv1 is not
let x1n = Fp.neg(ELL2_J); // 4. x1n = -J # x1 = x1n / xd = -J / (1 + 2 * u^2)
let tv2 = Fp.sqr(xd); // 5. tv2 = xd^2
let gxd = Fp.mul(tv2, xd); // 6. gxd = tv2 * xd # gxd = xd^3
let gx1 = Fp.mul(tv1, ELL2_J); // 7. gx1 = J * tv1 # x1n + J * xd
gx1 = Fp.mul(gx1, x1n); // 8. gx1 = gx1 * x1n # x1n^2 + J * x1n * xd
gx1 = Fp.add(gx1, tv2); // 9. gx1 = gx1 + tv2 # x1n^2 + J * x1n * xd + xd^2
gx1 = Fp.mul(gx1, x1n); // 10. gx1 = gx1 * x1n # x1n^3 + J * x1n^2 * xd + x1n * xd^2
let tv3 = Fp.sqr(gxd); // 11. tv3 = gxd^2
tv2 = Fp.sqr(tv3); // 12. tv2 = tv3^2 # gxd^4
tv3 = Fp.mul(tv3, gxd); // 13. tv3 = tv3 * gxd # gxd^3
tv3 = Fp.mul(tv3, gx1); // 14. tv3 = tv3 * gx1 # gx1 * gxd^3
tv2 = Fp.mul(tv2, tv3); // 15. tv2 = tv2 * tv3 # gx1 * gxd^7
let y11 = Fp.pow(tv2, ELL2_C4); // 16. y11 = tv2^c4 # (gx1 * gxd^7)^((p - 5) / 8)
y11 = Fp.mul(y11, tv3); // 17. y11 = y11 * tv3 # gx1*gxd^3*(gx1*gxd^7)^((p-5)/8)
let y12 = Fp.mul(y11, ELL2_C3); // 18. y12 = y11 * c3
tv2 = Fp.sqr(y11); // 19. tv2 = y11^2
tv2 = Fp.mul(tv2, gxd); // 20. tv2 = tv2 * gxd
let e1 = Fp.eql(tv2, gx1); // 21. e1 = tv2 == gx1
let y1 = Fp.cmov(y12, y11, e1); // 22. y1 = CMOV(y12, y11, e1) # If g(x1) is square, this is its sqrt
let x2n = Fp.mul(x1n, tv1); // 23. x2n = x1n * tv1 # x2 = x2n / xd = 2 * u^2 * x1n / xd
let y21 = Fp.mul(y11, u); // 24. y21 = y11 * u
y21 = Fp.mul(y21, ELL2_C2); // 25. y21 = y21 * c2
let y22 = Fp.mul(y21, ELL2_C3); // 26. y22 = y21 * c3
let gx2 = Fp.mul(gx1, tv1); // 27. gx2 = gx1 * tv1 # g(x2) = gx2 / gxd = 2 * u^2 * g(x1)
tv2 = Fp.sqr(y21); // 28. tv2 = y21^2
tv2 = Fp.mul(tv2, gxd); // 29. tv2 = tv2 * gxd
let e2 = Fp.eql(tv2, gx2); // 30. e2 = tv2 == gx2
let y2 = Fp.cmov(y22, y21, e2); // 31. y2 = CMOV(y22, y21, e2) # If g(x2) is square, this is its sqrt
tv2 = Fp.sqr(y1); // 32. tv2 = y1^2
tv2 = Fp.mul(tv2, gxd); // 33. tv2 = tv2 * gxd
let e3 = Fp.eql(tv2, gx1); // 34. e3 = tv2 == gx1
let xn = Fp.cmov(x2n, x1n, e3); // 35. xn = CMOV(x2n, x1n, e3) # If e3, x = x1, else x = x2
let y = Fp.cmov(y2, y1, e3); // 36. y = CMOV(y2, y1, e3) # If e3, y = y1, else y = y2
let e4 = Fp.isOdd(y); // 37. e4 = sgn0(y) == 1 # Fix sign of y
y = Fp.cmov(y, Fp.neg(y), e3 !== e4); // 38. y = CMOV(y, -y, e3 XOR e4)
return { xMn: xn, xMd: xd, yMn: y, yMd: 1n }; // 39. return (xn, xd, y, 1)
}
const ELL2_C1_EDWARDS = FpSqrtEven(Fp, Fp.neg(BigInt(486664))); // sgn0(c1) MUST equal 0
function map_to_curve_elligator2_edwards25519(u: bigint) {
const { xMn, xMd, yMn, yMd } = map_to_curve_elligator2_curve25519(u); // 1. (xMn, xMd, yMn, yMd) = map_to_curve_elligator2_curve25519(u)
let xn = Fp.mul(xMn, yMd); // 2. xn = xMn * yMd
xn = Fp.mul(xn, ELL2_C1_EDWARDS); // 3. xn = xn * c1
let xd = Fp.mul(xMd, yMn); // 4. xd = xMd * yMn # xn / xd = c1 * xM / yM
let yn = Fp.sub(xMn, xMd); // 5. yn = xMn - xMd
let yd = Fp.add(xMn, xMd); // 6. yd = xMn + xMd # (n / d - 1) / (n / d + 1) = (n - d) / (n + d)
let tv1 = Fp.mul(xd, yd); // 7. tv1 = xd * yd
let e = Fp.eql(tv1, Fp.ZERO); // 8. e = tv1 == 0
xn = Fp.cmov(xn, Fp.ZERO, e); // 9. xn = CMOV(xn, 0, e)
xd = Fp.cmov(xd, Fp.ONE, e); // 10. xd = CMOV(xd, 1, e)
yn = Fp.cmov(yn, Fp.ONE, e); // 11. yn = CMOV(yn, 1, e)
yd = Fp.cmov(yd, Fp.ONE, e); // 12. yd = CMOV(yd, 1, e)
const inv = Fp.invertBatch([xd, yd]); // batch division
return { x: Fp.mul(xn, inv[0]), y: Fp.mul(yn, inv[1]) }; // 13. return (xn, xd, yn, yd)
}
const { hashToCurve, encodeToCurve } = htf.hashToCurve(
ed25519.ExtendedPoint,
(scalars: bigint[]) => map_to_curve_elligator2_edwards25519(scalars[0]),
{
DST: 'edwards25519_XMD:SHA-512_ELL2_RO_',
encodeDST: 'edwards25519_XMD:SHA-512_ELL2_NU_',
p: Fp.ORDER,
m: 1,
k: 128,
expand: 'xmd',
hash: sha512,
}
);
export { hashToCurve, encodeToCurve };
function assertRstPoint(other: unknown) { function assertRstPoint(other: unknown) {
if (!(other instanceof RistrettoPoint)) throw new TypeError('RistrettoPoint expected'); if (!(other instanceof RistrettoPoint)) throw new Error('RistrettoPoint expected');
} }
// √(-1) aka √(a) aka 2^((p-1)/4) // √(-1) aka √(a) aka 2^((p-1)/4)
const SQRT_M1 = BigInt( const SQRT_M1 = BigInt(
@@ -180,9 +266,33 @@ const invertSqrt = (number: bigint) => uvRatio(_1n, number);
const MAX_255B = BigInt('0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'); const MAX_255B = BigInt('0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
const bytes255ToNumberLE = (bytes: Uint8Array) => const bytes255ToNumberLE = (bytes: Uint8Array) =>
ed25519.utils.mod(bytesToNumberLE(bytes) & MAX_255B); ed25519.CURVE.Fp.create(bytesToNumberLE(bytes) & MAX_255B);
type ExtendedPoint = ExtendedPointType; type ExtendedPoint = ExtPointType;
// Computes Elligator map for Ristretto
// https://ristretto.group/formulas/elligator.html
function calcElligatorRistrettoMap(r0: bigint): ExtendedPoint {
const { d } = ed25519.CURVE;
const P = ed25519.CURVE.Fp.ORDER;
const mod = ed25519.CURVE.Fp.create;
const r = mod(SQRT_M1 * r0 * r0); // 1
const Ns = mod((r + _1n) * ONE_MINUS_D_SQ); // 2
let c = BigInt(-1); // 3
const D = mod((c - d * r) * mod(r + d)); // 4
let { isValid: Ns_D_is_sq, value: s } = uvRatio(Ns, D); // 5
let s_ = mod(s * r0); // 6
if (!isNegativeLE(s_, P)) s_ = mod(-s_);
if (!Ns_D_is_sq) s = s_; // 7
if (!Ns_D_is_sq) c = r; // 8
const Nt = mod(c * (r - _1n) * D_MINUS_ONE_SQ - D); // 9
const s2 = s * s;
const W0 = mod((s + s) * D); // 10
const W1 = mod(Nt * SQRT_AD_MINUS_ONE); // 11
const W2 = mod(_1n - s2); // 12
const W3 = mod(_1n + s2); // 13
return new ed25519.ExtendedPoint(mod(W0 * W3), mod(W2 * W1), mod(W1 * W3), mod(W0 * W2));
}
/** /**
* Each ed25519/ExtendedPoint has 8 different equivalent points. This can be * Each ed25519/ExtendedPoint has 8 different equivalent points. This can be
@@ -198,30 +308,6 @@ export class RistrettoPoint {
// Private property to discourage combining ExtendedPoint + RistrettoPoint // Private property to discourage combining ExtendedPoint + RistrettoPoint
// Always use Ristretto encoding/decoding instead. // Always use Ristretto encoding/decoding instead.
constructor(private readonly ep: ExtendedPoint) {} constructor(private readonly ep: ExtendedPoint) {}
// Computes Elligator map for Ristretto
// https://ristretto.group/formulas/elligator.html
private static calcElligatorRistrettoMap(r0: bigint): ExtendedPoint {
const { d, P } = ed25519.CURVE;
const { mod } = ed25519.utils;
const r = mod(SQRT_M1 * r0 * r0); // 1
const Ns = mod((r + _1n) * ONE_MINUS_D_SQ); // 2
let c = BigInt(-1); // 3
const D = mod((c - d * r) * mod(r + d)); // 4
let { isValid: Ns_D_is_sq, value: s } = uvRatio(Ns, D); // 5
let s_ = mod(s * r0); // 6
if (!isNegativeLE(s_, P)) s_ = mod(-s_);
if (!Ns_D_is_sq) s = s_; // 7
if (!Ns_D_is_sq) c = r; // 8
const Nt = mod(c * (r - _1n) * D_MINUS_ONE_SQ - D); // 9
const s2 = s * s;
const W0 = mod((s + s) * D); // 10
const W1 = mod(Nt * SQRT_AD_MINUS_ONE); // 11
const W2 = mod(_1n - s2); // 12
const W3 = mod(_1n + s2); // 13
return new ed25519.ExtendedPoint(mod(W0 * W3), mod(W2 * W1), mod(W1 * W3), mod(W0 * W2));
}
/** /**
* Takes uniform output of 64-bit hash function like sha512 and converts it to `RistrettoPoint`. * Takes uniform output of 64-bit hash function like sha512 and converts it to `RistrettoPoint`.
* The hash-to-group operation applies Elligator twice and adds the results. * The hash-to-group operation applies Elligator twice and adds the results.
@@ -232,9 +318,9 @@ export class RistrettoPoint {
static hashToCurve(hex: Hex): RistrettoPoint { static hashToCurve(hex: Hex): RistrettoPoint {
hex = ensureBytes(hex, 64); hex = ensureBytes(hex, 64);
const r1 = bytes255ToNumberLE(hex.slice(0, 32)); const r1 = bytes255ToNumberLE(hex.slice(0, 32));
const R1 = this.calcElligatorRistrettoMap(r1); const R1 = calcElligatorRistrettoMap(r1);
const r2 = bytes255ToNumberLE(hex.slice(32, 64)); const r2 = bytes255ToNumberLE(hex.slice(32, 64));
const R2 = this.calcElligatorRistrettoMap(r2); const R2 = calcElligatorRistrettoMap(r2);
return new RistrettoPoint(R1.add(R2)); return new RistrettoPoint(R1.add(R2));
} }
@@ -245,8 +331,9 @@ export class RistrettoPoint {
*/ */
static fromHex(hex: Hex): RistrettoPoint { static fromHex(hex: Hex): RistrettoPoint {
hex = ensureBytes(hex, 32); hex = ensureBytes(hex, 32);
const { a, d, P } = ed25519.CURVE; const { a, d } = ed25519.CURVE;
const { mod } = ed25519.utils; const P = ed25519.CURVE.Fp.ORDER;
const mod = ed25519.CURVE.Fp.create;
const emsg = 'RistrettoPoint.fromHex: the hex is not valid encoding of RistrettoPoint'; const emsg = 'RistrettoPoint.fromHex: the hex is not valid encoding of RistrettoPoint';
const s = bytes255ToNumberLE(hex); const s = bytes255ToNumberLE(hex);
// 1. Check that s_bytes is the canonical encoding of a field element, or else abort. // 1. Check that s_bytes is the canonical encoding of a field element, or else abort.
@@ -274,9 +361,9 @@ export class RistrettoPoint {
* https://ristretto.group/formulas/encoding.html * https://ristretto.group/formulas/encoding.html
*/ */
toRawBytes(): Uint8Array { toRawBytes(): Uint8Array {
let { x, y, z, t } = this.ep; let { ex: x, ey: y, ez: z, et: t } = this.ep;
const { P } = ed25519.CURVE; const P = ed25519.CURVE.Fp.ORDER;
const { mod } = ed25519.utils; const mod = ed25519.CURVE.Fp.create;
const u1 = mod(mod(z + y) * mod(z - y)); // 1 const u1 = mod(mod(z + y) * mod(z - y)); // 1
const u2 = mod(x * y); // 2 const u2 = mod(x * y); // 2
// Square root always exists // Square root always exists
@@ -312,12 +399,12 @@ export class RistrettoPoint {
// Compare one point to another. // Compare one point to another.
equals(other: RistrettoPoint): boolean { equals(other: RistrettoPoint): boolean {
assertRstPoint(other); assertRstPoint(other);
const a = this.ep; const { ex: X1, ey: Y1 } = this.ep;
const b = other.ep; const { ex: X2, ey: Y2 } = this.ep;
const { mod } = ed25519.utils; const mod = ed25519.CURVE.Fp.create;
// (x1 * y2 == y1 * x2) | (y1 * y2 == x1 * x2) // (x1 * y2 == y1 * x2) | (y1 * y2 == x1 * x2)
const one = mod(a.x * b.y) === mod(a.y * b.x); const one = mod(X1 * Y2) === mod(Y1 * X2);
const two = mod(a.y * b.y) === mod(a.x * b.x); const two = mod(Y1 * Y2) === mod(X1 * X2);
return one || two; return one || two;
} }
@@ -331,11 +418,11 @@ export class RistrettoPoint {
return new RistrettoPoint(this.ep.subtract(other.ep)); return new RistrettoPoint(this.ep.subtract(other.ep));
} }
multiply(scalar: number | bigint): RistrettoPoint { multiply(scalar: bigint): RistrettoPoint {
return new RistrettoPoint(this.ep.multiply(scalar)); return new RistrettoPoint(this.ep.multiply(scalar));
} }
multiplyUnsafe(scalar: number | bigint): RistrettoPoint { multiplyUnsafe(scalar: bigint): RistrettoPoint {
return new RistrettoPoint(this.ep.multiplyUnsafe(scalar)); return new RistrettoPoint(this.ep.multiplyUnsafe(scalar));
} }
} }

241
src/ed448.ts Normal file
View File

@@ -0,0 +1,241 @@
/*! 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, Fp as Field } from './abstract/modular.js';
import { montgomery } from './abstract/montgomery.js';
import * as htf from './abstract/hash-to-curve.js';
/**
* Edwards448 (not Ed448-Goldilocks) curve with following addons:
* * X448 ECDH
* Conforms to RFC 8032 https://www.rfc-editor.org/rfc/rfc8032.html#section-5.2
*/
const shake256_114 = wrapConstructor(() => shake256.create({ dkLen: 114 }));
const shake256_64 = wrapConstructor(() => shake256.create({ dkLen: 64 }));
const ed448P = BigInt(
'726838724295606890549323807888004534353641360687318060281490199180612328166730772686396383698676545930088884461843637361053498018365439'
);
// 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;
const b9 = (pow2(b6, _3n, P) * b3) % P;
const b11 = (pow2(b9, _2n, P) * b2) % P;
const b22 = (pow2(b11, _11n, P) * b11) % P;
const b44 = (pow2(b22, _22n, P) * b22) % P;
const b88 = (pow2(b44, _44n, P) * b44) % P;
const b176 = (pow2(b88, _88n, P) * b88) % P;
const b220 = (pow2(b176, _44n, P) * b44) % P;
const b222 = (pow2(b220, _2n, P) * b2) % P;
const b223 = (pow2(b222, _1n, P) * x) % P;
return (pow2(b223, _223n, P) * b222) % P;
}
function adjustScalarBytes(bytes: Uint8Array): Uint8Array {
// Section 5: Likewise, for X448, set the two least significant bits of the first byte to 0, and the most
// significant bit of the last byte to 1.
bytes[0] &= 252; // 0b11111100
// and the most significant bit of the last byte to 1.
bytes[55] |= 128; // 0b10000000
// NOTE: is is NOOP for 56 bytes scalars (X25519/X448)
bytes[56] = 0; // Byte outside of group (456 buts vs 448 bits)
return bytes;
}
const Fp = Field(ed448P, 456, true);
const ED448_DEF = {
// Param: a
a: BigInt(1),
// -39081. Negative number is P - number
d: BigInt(
'726838724295606890549323807888004534353641360687318060281490199180612328166730772686396383698676545930088884461843637361053498018326358'
),
// Finite field 𝔽p over which we'll do calculations; 2n ** 448n - 2n ** 224n - 1n
Fp,
// Subgroup order: how many points curve has;
// 2n**446n - 13818066809895115352007386748515426880336692474882178609894547503885n
n: BigInt(
'181709681073901722637330951972001133588410340171829515070372549795146003961539585716195755291692375963310293709091662304773755859649779'
),
nBitLength: 456,
// Cofactor
h: BigInt(4),
// Base point (x, y) aka generator point
Gx: BigInt(
'224580040295924300187604334099896036246789641632564134246125461686950415467406032909029192869357953282578032075146446173674602635247710'
),
Gy: BigInt(
'298819210078481492676017930443930673437544040154080242095928241372331506189835876003536878655418784733982303233503462500531545062832660'
),
// SHAKE256(dom4(phflag,context)||x, 114)
hash: shake256_114,
randomBytes,
adjustScalarBytes,
// dom4
domain: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => {
if (ctx.length > 255) throw new Error(`Context is too big: ${ctx.length}`);
return concatBytes(
utf8ToBytes('SigEd448'),
new Uint8Array([phflag ? 1 : 0, ctx.length]),
ctx,
data
);
},
// Constant-time ratio of u to v. Allows to combine inversion and square root u/√v.
// Uses algo from RFC8032 5.1.3.
uvRatio: (u: bigint, v: bigint): { isValid: boolean; value: bigint } => {
const P = ed448P;
// https://datatracker.ietf.org/doc/html/rfc8032#section-5.2.3
// To compute the square root of (u/v), the first step is to compute the
// candidate root x = (u/v)^((p+1)/4). This can be done using the
// following trick, to use a single modular powering for both the
// inversion of v and the square root:
// x = (u/v)^((p+1)/4) = u³v(u⁵v³)^((p-3)/4) (mod p)
const u2v = mod(u * u * v, P); // u²v
const u3v = mod(u2v * u, P); // u³v
const u5v3 = mod(u3v * u2v * v, P); // u⁵v³
const root = ed448_pow_Pminus3div4(u5v3);
const x = mod(u3v * root, P);
// Verify that root is exists
const x2 = mod(x * x, P); // x²
// If vx² = u, the recovered x-coordinate is x. Otherwise, no
// square root exists, and the decoding fails.
return { isValid: mod(x2 * v, P) === u, value: x };
},
} as const;
export const ed448 = 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 x448 = montgomery({
a24: BigInt(39081),
montgomeryBits: 448,
nByteLength: 57,
P: ed448P,
Gu: '0500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
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,
// 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);
// },
});
// Hash To Curve Elligator2 Map
const ELL2_C1 = (Fp.ORDER - BigInt(3)) / BigInt(4); // 1. c1 = (q - 3) / 4 # Integer arithmetic
const ELL2_J = BigInt(156326);
function map_to_curve_elligator2_curve448(u: bigint) {
let tv1 = Fp.sqr(u); // 1. tv1 = u^2
let e1 = Fp.eql(tv1, Fp.ONE); // 2. e1 = tv1 == 1
tv1 = Fp.cmov(tv1, Fp.ZERO, e1); // 3. tv1 = CMOV(tv1, 0, e1) # If Z * u^2 == -1, set tv1 = 0
let xd = Fp.sub(Fp.ONE, tv1); // 4. xd = 1 - tv1
let x1n = Fp.neg(ELL2_J); // 5. x1n = -J
let tv2 = Fp.sqr(xd); // 6. tv2 = xd^2
let gxd = Fp.mul(tv2, xd); // 7. gxd = tv2 * xd # gxd = xd^3
let gx1 = Fp.mul(tv1, Fp.neg(ELL2_J)); // 8. gx1 = -J * tv1 # x1n + J * xd
gx1 = Fp.mul(gx1, x1n); // 9. gx1 = gx1 * x1n # x1n^2 + J * x1n * xd
gx1 = Fp.add(gx1, tv2); // 10. gx1 = gx1 + tv2 # x1n^2 + J * x1n * xd + xd^2
gx1 = Fp.mul(gx1, x1n); // 11. gx1 = gx1 * x1n # x1n^3 + J * x1n^2 * xd + x1n * xd^2
let tv3 = Fp.sqr(gxd); // 12. tv3 = gxd^2
tv2 = Fp.mul(gx1, gxd); // 13. tv2 = gx1 * gxd # gx1 * gxd
tv3 = Fp.mul(tv3, tv2); // 14. tv3 = tv3 * tv2 # gx1 * gxd^3
let y1 = Fp.pow(tv3, ELL2_C1); // 15. y1 = tv3^c1 # (gx1 * gxd^3)^((p - 3) / 4)
y1 = Fp.mul(y1, tv2); // 16. y1 = y1 * tv2 # gx1 * gxd * (gx1 * gxd^3)^((p - 3) / 4)
let x2n = Fp.mul(x1n, Fp.neg(tv1)); // 17. x2n = -tv1 * x1n # x2 = x2n / xd = -1 * u^2 * x1n / xd
let y2 = Fp.mul(y1, u); // 18. y2 = y1 * u
y2 = Fp.cmov(y2, Fp.ZERO, e1); // 19. y2 = CMOV(y2, 0, e1)
tv2 = Fp.sqr(y1); // 20. tv2 = y1^2
tv2 = Fp.mul(tv2, gxd); // 21. tv2 = tv2 * gxd
let e2 = Fp.eql(tv2, gx1); // 22. e2 = tv2 == gx1
let xn = Fp.cmov(x2n, x1n, e2); // 23. xn = CMOV(x2n, x1n, e2) # If e2, x = x1, else x = x2
let y = Fp.cmov(y2, y1, e2); // 24. y = CMOV(y2, y1, e2) # If e2, y = y1, else y = y2
let e3 = Fp.isOdd(y); // 25. e3 = sgn0(y) == 1 # Fix sign of y
y = Fp.cmov(y, Fp.neg(y), e2 !== e3); // 26. y = CMOV(y, -y, e2 XOR e3)
return { xn, xd, yn: y, yd: Fp.ONE }; // 27. return (xn, xd, y, 1)
}
function map_to_curve_elligator2_edwards448(u: bigint) {
let { xn, xd, yn, yd } = map_to_curve_elligator2_curve448(u); // 1. (xn, xd, yn, yd) = map_to_curve_elligator2_curve448(u)
let xn2 = Fp.sqr(xn); // 2. xn2 = xn^2
let xd2 = Fp.sqr(xd); // 3. xd2 = xd^2
let xd4 = Fp.sqr(xd2); // 4. xd4 = xd2^2
let yn2 = Fp.sqr(yn); // 5. yn2 = yn^2
let yd2 = Fp.sqr(yd); // 6. yd2 = yd^2
let xEn = Fp.sub(xn2, xd2); // 7. xEn = xn2 - xd2
let tv2 = Fp.sub(xEn, xd2); // 8. tv2 = xEn - xd2
xEn = Fp.mul(xEn, xd2); // 9. xEn = xEn * xd2
xEn = Fp.mul(xEn, yd); // 10. xEn = xEn * yd
xEn = Fp.mul(xEn, yn); // 11. xEn = xEn * yn
xEn = Fp.mul(xEn, 4n); // 12. xEn = xEn * 4
tv2 = Fp.mul(tv2, xn2); // 13. tv2 = tv2 * xn2
tv2 = Fp.mul(tv2, yd2); // 14. tv2 = tv2 * yd2
let tv3 = Fp.mul(yn2, 4n); // 15. tv3 = 4 * yn2
let tv1 = Fp.add(tv3, yd2); // 16. tv1 = tv3 + yd2
tv1 = Fp.mul(tv1, xd4); // 17. tv1 = tv1 * xd4
let xEd = Fp.add(tv1, tv2); // 18. xEd = tv1 + tv2
tv2 = Fp.mul(tv2, xn); // 19. tv2 = tv2 * xn
let tv4 = Fp.mul(xn, xd4); // 20. tv4 = xn * xd4
let yEn = Fp.sub(tv3, yd2); // 21. yEn = tv3 - yd2
yEn = Fp.mul(yEn, tv4); // 22. yEn = yEn * tv4
yEn = Fp.sub(yEn, tv2); // 23. yEn = yEn - tv2
tv1 = Fp.add(xn2, xd2); // 24. tv1 = xn2 + xd2
tv1 = Fp.mul(tv1, xd2); // 25. tv1 = tv1 * xd2
tv1 = Fp.mul(tv1, xd); // 26. tv1 = tv1 * xd
tv1 = Fp.mul(tv1, yn2); // 27. tv1 = tv1 * yn2
tv1 = Fp.mul(tv1, BigInt(-2)); // 28. tv1 = -2 * tv1
let yEd = Fp.add(tv2, tv1); // 29. yEd = tv2 + tv1
tv4 = Fp.mul(tv4, yd2); // 30. tv4 = tv4 * yd2
yEd = Fp.add(yEd, tv4); // 31. yEd = yEd + tv4
tv1 = Fp.mul(xEd, yEd); // 32. tv1 = xEd * yEd
let e = Fp.eql(tv1, Fp.ZERO); // 33. e = tv1 == 0
xEn = Fp.cmov(xEn, Fp.ZERO, e); // 34. xEn = CMOV(xEn, 0, e)
xEd = Fp.cmov(xEd, Fp.ONE, e); // 35. xEd = CMOV(xEd, 1, e)
yEn = Fp.cmov(yEn, Fp.ONE, e); // 36. yEn = CMOV(yEn, 1, e)
yEd = Fp.cmov(yEd, Fp.ONE, e); // 37. yEd = CMOV(yEd, 1, e)
const inv = Fp.invertBatch([xEd, yEd]); // batch division
return { x: Fp.mul(xEn, inv[0]), y: Fp.mul(yEn, inv[1]) }; // 38. return (xEn, xEd, yEn, yEd)
}
const { hashToCurve, encodeToCurve } = htf.hashToCurve(
ed448.ExtendedPoint,
(scalars: bigint[]) => map_to_curve_elligator2_edwards448(scalars[0]),
{
DST: 'edwards448_XOF:SHAKE256_ELL2_RO_',
encodeDST: 'edwards448_XOF:SHAKE256_ELL2_NU_',
p: Fp.ORDER,
m: 1,
k: 224,
expand: 'xof',
hash: shake256,
}
);
export { hashToCurve, encodeToCurve };

View File

@@ -1,684 +0,0 @@
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
// Implementation of Twisted Edwards curve. The formula is: ax² + y² = 1 + dx²y²
// Differences from @noble/ed25519 1.7:
// 1. Different field element lengths in ed448:
// EDDSA (RFC8032) is 456 bits / 57 bytes, ECDH (RFC7748) is 448 bits / 56 bytes
// 2. Different addition formula (doubling is same)
// 3. uvRatio differs between curves (half-expected, not only pow fn changes)
// 4. Point decompression code is different too (unexpected), now using generalized formula
// 5. Domain function was no-op for ed25519, but adds some data even with empty context for ed448
import * as mod from './modular.js';
import {
bytesToHex,
concatBytes,
ensureBytes,
numberToBytesLE,
bytesToNumberLE,
hashToPrivateScalar,
BasicCurve,
validateOpts as utilOpts,
Hex,
PrivKey,
} from './utils.js'; // TODO: import * as u from './utils.js'?
import { Group, GroupConstructor, wNAF } from './group.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);
export type CHash = {
(message: Uint8Array | string): Uint8Array;
blockLen: number;
outputLen: number;
create(): any;
};
export type CurveType = BasicCurve & {
// Params: a, d
a: bigint;
d: bigint;
// Hashes
hash: CHash; // Because we need outputLen for DRBG
randomBytes: (bytesLength?: number) => Uint8Array;
adjustScalarBytes?: (bytes: Uint8Array) => Uint8Array;
domain?: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array;
uvRatio?: (u: bigint, v: bigint) => { isValid: boolean; value: bigint };
preHash?: CHash;
};
// Should be separate from overrides, since overrides can use information about curve (for example nBits)
function validateOpts(curve: CurveType) {
const opts = utilOpts(curve);
if (typeof opts.hash !== 'function' || !Number.isSafeInteger(opts.hash.outputLen))
throw new Error('Invalid hash function');
for (const i of ['a', 'd'] as const) {
if (typeof opts[i] !== 'bigint')
throw new Error(`Invalid curve param ${i}=${opts[i]} (${typeof opts[i]})`);
}
for (const fn of ['randomBytes'] as const) {
if (typeof opts[fn] !== 'function') throw new Error(`Invalid ${fn} function`);
}
for (const fn of ['adjustScalarBytes', 'domain', 'uvRatio'] as const) {
if (opts[fn] === undefined) continue; // Optional
if (typeof opts[fn] !== 'function') throw new Error(`Invalid ${fn} function`);
}
// Set defaults
return Object.freeze({ ...opts } as const);
}
// Instance
export interface SignatureType {
readonly r: PointType;
readonly s: bigint;
assertValidity(): SignatureType;
toRawBytes(): Uint8Array;
toHex(): string;
}
// Static methods
export type SignatureConstructor = {
new (r: PointType, s: bigint): SignatureType;
fromHex(hex: Hex): SignatureType;
};
// Instance
export interface ExtendedPointType extends Group<ExtendedPointType> {
readonly x: bigint;
readonly y: bigint;
readonly z: bigint;
readonly t: bigint;
multiply(scalar: number | bigint, affinePoint?: PointType): ExtendedPointType;
multiplyUnsafe(scalar: number | bigint): ExtendedPointType;
isSmallOrder(): boolean;
isTorsionFree(): boolean;
toAffine(invZ?: bigint): PointType;
}
// Static methods
export interface ExtendedPointConstructor extends GroupConstructor<ExtendedPointType> {
new (x: bigint, y: bigint, z: bigint, t: bigint): ExtendedPointType;
fromAffine(p: PointType): ExtendedPointType;
toAffineBatch(points: ExtendedPointType[]): PointType[];
normalizeZ(points: ExtendedPointType[]): ExtendedPointType[];
}
// Instance
export interface PointType extends Group<PointType> {
readonly x: bigint;
readonly y: bigint;
_setWindowSize(windowSize: number): void;
toRawBytes(isCompressed?: boolean): Uint8Array;
toHex(isCompressed?: boolean): string;
isTorsionFree(): boolean;
}
// Static methods
export interface PointConstructor extends GroupConstructor<PointType> {
new (x: bigint, y: bigint): PointType;
fromHex(hex: Hex): PointType;
fromPrivateKey(privateKey: PrivKey): PointType;
}
export type PubKey = Hex | PointType;
export type SigType = Hex | SignatureType;
export type CurveFn = {
CURVE: ReturnType<typeof validateOpts>;
getPublicKey: (privateKey: PrivKey, isCompressed?: boolean) => Uint8Array;
sign: (message: Hex, privateKey: Hex) => Uint8Array;
verify: (sig: SigType, message: Hex, publicKey: PubKey) => boolean;
Point: PointConstructor;
ExtendedPoint: ExtendedPointConstructor;
Signature: SignatureConstructor;
utils: {
mod: (a: bigint, b?: bigint) => bigint;
invert: (number: bigint, modulo?: bigint) => bigint;
randomPrivateKey: () => Uint8Array;
getExtendedPublicKey: (key: PrivKey) => {
head: Uint8Array;
prefix: Uint8Array;
scalar: bigint;
point: PointType;
pointBytes: Uint8Array;
};
};
};
// NOTE: 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 CURVE_ORDER = CURVE.n;
const fieldLen = CURVE.nByteLength; // 32 (length of one field element)
if (fieldLen > 2048) throw new Error('Field lengths over 2048 are not supported');
const groupLen = CURVE.nByteLength;
// (2n ** 256n).toString(16);
const maxGroupElement = _2n ** BigInt(groupLen * 8); // previous POW_2_256
// Function overrides
const { P, randomBytes } = CURVE;
const modP = (a: bigint) => mod.mod(a, P);
// sqrt(u/v)
function _uvRatio(u: bigint, v: bigint) {
try {
const value = mod.sqrt(u * mod.invert(v, P), P);
return { isValid: true, value };
} catch (e) {
return { isValid: false, value: _0n };
}
}
const uvRatio = CURVE.uvRatio || _uvRatio;
const _adjustScalarBytes = (bytes: Uint8Array) => bytes; // NOOP
const adjustScalarBytes = CURVE.adjustScalarBytes || _adjustScalarBytes;
function _domain(data: Uint8Array, ctx: Uint8Array, phflag: boolean) {
if (ctx.length || phflag) throw new Error('Contexts/pre-hash are not supported');
return data;
}
const domain = CURVE.domain || _domain; // NOOP
/**
* Extended Point works in extended coordinates: (x, y, z, t) ∋ (x=x/z, y=y/z, t=xy).
* Default Point works in affine coordinates: (x, y)
* https://en.wikipedia.org/wiki/Twisted_Edwards_curve#Extended_coordinates
*/
class ExtendedPoint implements ExtendedPointType {
constructor(readonly x: bigint, readonly y: bigint, readonly z: bigint, readonly t: bigint) {}
static BASE = new ExtendedPoint(CURVE.Gx, CURVE.Gy, _1n, modP(CURVE.Gx * CURVE.Gy));
static ZERO = new ExtendedPoint(_0n, _1n, _1n, _0n);
static fromAffine(p: Point): ExtendedPoint {
if (!(p instanceof Point)) {
throw new TypeError('ExtendedPoint#fromAffine: expected Point');
}
if (p.equals(Point.ZERO)) return ExtendedPoint.ZERO;
return new ExtendedPoint(p.x, p.y, _1n, modP(p.x * p.y));
}
// Takes a bunch of Jacobian Points but executes only one
// invert on all of them. invert is very slow operation,
// so this improves performance massively.
static toAffineBatch(points: ExtendedPoint[]): Point[] {
const toInv = mod.invertBatch(
points.map((p) => p.z),
P
);
return points.map((p, i) => p.toAffine(toInv[i]));
}
static normalizeZ(points: ExtendedPoint[]): ExtendedPoint[] {
return this.toAffineBatch(points).map(this.fromAffine);
}
// Compare one point to another.
equals(other: ExtendedPoint): boolean {
assertExtPoint(other);
const { x: X1, y: Y1, z: Z1 } = this;
const { x: X2, y: Y2, z: Z2 } = other;
const X1Z2 = modP(X1 * Z2);
const X2Z1 = modP(X2 * Z1);
const Y1Z2 = modP(Y1 * Z2);
const Y2Z1 = modP(Y2 * Z1);
return X1Z2 === X2Z1 && Y1Z2 === Y2Z1;
}
// Inverses point to one corresponding to (x, -y) in Affine coordinates.
negate(): ExtendedPoint {
return new ExtendedPoint(modP(-this.x), this.y, this.z, modP(-this.t));
}
// Fast algo for doubling Extended Point.
// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd
// Cost: 4M + 4S + 1*a + 6add + 1*2.
double(): ExtendedPoint {
const { a } = CURVE;
const { x: X1, y: Y1, z: Z1 } = this;
const A = modP(X1 * X1); // A = X12
const B = modP(Y1 * Y1); // B = Y12
const C = modP(_2n * modP(Z1 * Z1)); // C = 2*Z12
const D = modP(a * A); // D = a*A
const x1y1 = X1 + Y1;
const E = modP(modP(x1y1 * x1y1) - A - B); // E = (X1+Y1)2-A-B
const G = D + B; // G = D+B
const F = G - C; // F = G-C
const H = D - B; // H = D-B
const X3 = modP(E * F); // X3 = E*F
const Y3 = modP(G * H); // Y3 = G*H
const T3 = modP(E * H); // T3 = E*H
const Z3 = modP(F * G); // Z3 = F*G
return new ExtendedPoint(X3, Y3, Z3, T3);
}
// Fast algo for adding 2 Extended Points.
// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#addition-add-2008-hwcd
// Cost: 9M + 1*a + 1*d + 7add.
add(other: ExtendedPoint) {
assertExtPoint(other);
const { a, d } = CURVE;
const { x: X1, y: Y1, z: Z1, t: T1 } = this;
const { x: X2, y: Y2, z: Z2, t: T2 } = other;
// Faster algo for adding 2 Extended Points when curve's a=-1.
// http://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-4
// Cost: 8M + 8add + 2*2.
// Note: It does not check whether the `other` point is valid.
if (a === BigInt(-1)) {
const A = modP((Y1 - X1) * (Y2 + X2));
const B = modP((Y1 + X1) * (Y2 - X2));
const F = modP(B - A);
if (F === _0n) return this.double(); // Same point.
const C = modP(Z1 * _2n * T2);
const D = modP(T1 * _2n * Z2);
const E = D + C;
const G = B + A;
const H = D - C;
const X3 = modP(E * F);
const Y3 = modP(G * H);
const T3 = modP(E * H);
const Z3 = modP(F * G);
return new ExtendedPoint(X3, Y3, Z3, T3);
}
const A = modP(X1 * X2); // A = X1*X2
const B = modP(Y1 * Y2); // B = Y1*Y2
const C = modP(T1 * d * T2); // C = T1*d*T2
const D = modP(Z1 * Z2); // D = Z1*Z2
const E = modP((X1 + Y1) * (X2 + Y2) - A - B); // E = (X1+Y1)*(X2+Y2)-A-B
// TODO: do we need to check for same point here? Looks like working without it
const F = D - C; // F = D-C
const G = D + C; // G = D+C
const H = modP(B - a * A); // H = B-a*A
const X3 = modP(E * F); // X3 = E*F
const Y3 = modP(G * H); // Y3 = G*H
const T3 = modP(E * H); // T3 = E*H
const Z3 = modP(F * G); // Z3 = F*G
return new ExtendedPoint(X3, Y3, Z3, T3);
}
subtract(other: ExtendedPoint): ExtendedPoint {
return this.add(other.negate());
}
private wNAF(n: bigint, affinePoint?: Point): ExtendedPoint {
if (!affinePoint && this.equals(ExtendedPoint.BASE)) affinePoint = Point.BASE;
const W = (affinePoint && affinePoint._WINDOW_SIZE) || 1;
let precomputes = affinePoint && pointPrecomputes.get(affinePoint);
if (!precomputes) {
precomputes = wnaf.precomputeWindow(this, W) as ExtendedPoint[];
if (affinePoint && W !== 1) {
precomputes = ExtendedPoint.normalizeZ(precomputes);
pointPrecomputes.set(affinePoint, precomputes);
}
}
const { p, f } = wnaf.wNAF(W, precomputes, n);
return ExtendedPoint.normalizeZ([p, f])[0];
}
// Constant time multiplication.
// Uses wNAF method. Windowed method may be 10% faster,
// but takes 2x longer to generate and consumes 2x memory.
multiply(scalar: number | bigint, affinePoint?: Point): ExtendedPoint {
return this.wNAF(normalizeScalar(scalar, CURVE_ORDER), affinePoint);
}
// 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.
// Allows scalar bigger than curve order, but less than 2^256
multiplyUnsafe(scalar: number | bigint): ExtendedPoint {
let n = normalizeScalar(scalar, CURVE_ORDER, false);
const G = ExtendedPoint.BASE;
const P0 = ExtendedPoint.ZERO;
if (n === _0n) return P0;
if (this.equals(P0) || n === _1n) return this;
if (this.equals(G)) return this.wNAF(n);
return wnaf.unsafeLadder(this, n);
}
// Multiplies point by cofactor and checks if the result is 0.
isSmallOrder(): boolean {
return this.multiplyUnsafe(CURVE.h).equals(ExtendedPoint.ZERO);
}
// Multiplies point by a very big scalar n and checks if the result is 0.
isTorsionFree(): boolean {
return this.multiplyUnsafe(CURVE_ORDER).equals(ExtendedPoint.ZERO);
}
// Converts Extended point to default (x, y) coordinates.
// Can accept precomputed Z^-1 - for example, from invertBatch.
toAffine(invZ?: bigint): Point {
const { x, y, z } = this;
const is0 = this.equals(ExtendedPoint.ZERO);
if (invZ == null) invZ = is0 ? _8n : mod.invert(z, P); // 8 was chosen arbitrarily
const ax = modP(x * invZ);
const ay = modP(y * invZ);
const zz = modP(z * invZ);
if (is0) return Point.ZERO;
if (zz !== _1n) throw new Error('invZ was invalid');
return new Point(ax, ay);
}
}
const wnaf = wNAF(ExtendedPoint, groupLen * 8);
function assertExtPoint(other: unknown) {
if (!(other instanceof ExtendedPoint)) throw new TypeError('ExtendedPoint expected');
}
// Stores precomputed values for points.
const pointPrecomputes = new WeakMap<Point, ExtendedPoint[]>();
/**
* Default Point works in affine coordinates: (x, y)
*/
class Point implements PointType {
// Base point aka generator
// public_key = Point.BASE * private_key
static BASE: Point = new Point(CURVE.Gx, CURVE.Gy);
// Identity point aka point at infinity
// point = point + zero_point
static ZERO: Point = new Point(_0n, _1n);
// We calculate precomputes for elliptic curve point multiplication
// using windowed method. This specifies window size and
// stores precomputed values. Usually only base point would be precomputed.
_WINDOW_SIZE?: number;
constructor(readonly x: bigint, readonly y: bigint) {}
// "Private method", don't use it directly.
_setWindowSize(windowSize: number) {
this._WINDOW_SIZE = windowSize;
pointPrecomputes.delete(this);
}
// Converts hash string or Uint8Array to Point.
// Uses algo from RFC8032 5.1.3.
static fromHex(hex: Hex, strict = true) {
const { d, P, a } = CURVE;
hex = ensureBytes(hex, fieldLen);
// 1. First, interpret the string as an integer in little-endian
// representation. Bit 255 of this number is the least significant
// bit of the x-coordinate and denote this value x_0. The
// y-coordinate is recovered simply by clearing this bit. If the
// resulting value is >= p, decoding fails.
const normed = hex.slice();
const lastByte = hex[fieldLen - 1];
normed[fieldLen - 1] = lastByte & ~0x80;
const y = bytesToNumberLE(normed);
if (strict && y >= P) throw new Error('Expected 0 < hex < P');
if (!strict && y >= maxGroupElement) throw new Error('Expected 0 < hex < 2**256');
// 2. To recover the x-coordinate, the curve equation implies
// Ed25519: x² = (y² - 1) / (d y² + 1) (mod p).
// Ed448: x² = (y² - 1) / (d y² - 1) (mod p).
// For generic case:
// a*x²+y²=1+d*x²*y²
// -> y²-1 = d*x²*y²-a*x²
// -> y²-1 = x² (d*y²-a)
// -> x² = (y²-1) / (d*y²-a)
// The denominator is always non-zero mod p. Let u = y² - 1 and v = d y² + 1.
const y2 = modP(y * y);
const u = modP(y2 - _1n);
const v = modP(d * y2 - a);
let { isValid, value: x } = uvRatio(u, v);
if (!isValid) throw new Error('Point.fromHex: invalid y coordinate');
// 4. Finally, use the x_0 bit to select the right square root. If
// x = 0, and x_0 = 1, decoding fails. Otherwise, if x_0 != x mod
// 2, set x <-- p - x. Return the decoded point (x,y).
const isXOdd = (x & _1n) === _1n;
const isLastByteOdd = (lastByte & 0x80) !== 0;
if (isLastByteOdd !== isXOdd) x = modP(-x);
return new Point(x, y);
}
static fromPrivateKey(privateKey: PrivKey) {
return getExtendedPublicKey(privateKey).point;
}
// There can always be only two x values (x, -x) for any y
// When compressing point, it's enough to only store its y coordinate
// and use the last byte to encode sign of x.
toRawBytes(): Uint8Array {
const bytes = numberToBytesLE(this.y, fieldLen);
bytes[fieldLen - 1] |= this.x & _1n ? 0x80 : 0;
return bytes;
}
// Same as toRawBytes, but returns string.
toHex(): string {
return bytesToHex(this.toRawBytes());
}
isTorsionFree(): boolean {
return ExtendedPoint.fromAffine(this).isTorsionFree();
}
equals(other: Point): boolean {
if (!(other instanceof Point)) throw new TypeError('Point#equals: expected Point');
return this.x === other.x && this.y === other.y;
}
negate(): Point {
return new Point(modP(-this.x), this.y);
}
double(): Point {
return ExtendedPoint.fromAffine(this).double().toAffine();
}
add(other: Point) {
return ExtendedPoint.fromAffine(this).add(ExtendedPoint.fromAffine(other)).toAffine();
}
subtract(other: Point) {
return this.add(other.negate());
}
/**
* Constant time multiplication.
* @param scalar Big-Endian number
* @returns new point
*/
multiply(scalar: number | bigint): Point {
return ExtendedPoint.fromAffine(this).multiply(scalar, this).toAffine();
}
}
/**
* EDDSA signature.
*/
class Signature implements SignatureType {
constructor(readonly r: Point, readonly s: bigint) {
this.assertValidity();
}
static fromHex(hex: Hex) {
const bytes = ensureBytes(hex, 2 * fieldLen);
const r = Point.fromHex(bytes.slice(0, fieldLen), false);
const s = bytesToNumberLE(bytes.slice(fieldLen, 2 * fieldLen));
return new Signature(r, s);
}
assertValidity() {
const { r, s } = this;
if (!(r instanceof Point)) throw new Error('Expected Point instance');
// 0 <= s < l
normalizeScalar(s, CURVE_ORDER, false);
return this;
}
toRawBytes() {
return concatBytes(this.r.toRawBytes(), numberToBytesLE(this.s, fieldLen));
}
toHex() {
return bytesToHex(this.toRawBytes());
}
}
// Little-endian SHA512 with modulo n
function modlLE(hash: Uint8Array): bigint {
return mod.mod(bytesToNumberLE(hash), CURVE_ORDER);
}
/**
* Checks for num to be in range:
* For strict == true: `0 < num < max`.
* For strict == false: `0 <= num < max`.
* Converts non-float safe numbers to bigints.
*/
function normalizeScalar(num: number | bigint, max: bigint, strict = true): bigint {
if (!max) throw new TypeError('Specify max value');
if (typeof num === 'number' && Number.isSafeInteger(num)) num = BigInt(num);
if (typeof num === 'bigint' && num < max) {
if (strict) {
if (_0n < num) return num;
} else {
if (_0n <= num) return num;
}
}
throw new TypeError('Expected valid scalar: 0 < scalar < max');
}
function checkPrivateKey(key: PrivKey) {
// Normalize bigint / number / string to Uint8Array
key =
typeof key === 'bigint' || typeof key === 'number'
? numberToBytesLE(normalizeScalar(key, maxGroupElement), groupLen)
: ensureBytes(key);
if (key.length !== groupLen) throw new Error(`Expected ${groupLen} bytes, got ${key.length}`);
return key;
}
// Takes 64 bytes
function getKeyFromHash(hashed: Uint8Array) {
// First 32 bytes of 64b uniformingly random input are taken,
// clears 3 bits of it to produce a random field element.
const head = adjustScalarBytes(hashed.slice(0, groupLen));
// Second 32 bytes is called key prefix (5.1.6)
const prefix = hashed.slice(groupLen, 2 * groupLen);
// The actual private scalar
const scalar = modlLE(head);
// Point on Edwards curve aka public key
const point = Point.BASE.multiply(scalar);
const pointBytes = point.toRawBytes();
return { head, prefix, scalar, point, pointBytes };
}
/** Convenience method that creates public key and other stuff. RFC8032 5.1.5 */
function getExtendedPublicKey(key: PrivKey) {
return getKeyFromHash(CURVE.hash(checkPrivateKey(key)));
}
/**
* Calculates ed25519 public key. RFC8032 5.1.5
* 1. private key is hashed with sha512, then first 32 bytes are taken from the hash
* 2. 3 least significant bits of the first byte are cleared
*/
function getPublicKey(privateKey: PrivKey): Uint8Array {
return getExtendedPublicKey(privateKey).pointBytes;
}
const EMPTY = new Uint8Array();
function hashDomainToScalar(message: Uint8Array, context: Hex = EMPTY) {
context = ensureBytes(context);
return modlLE(CURVE.hash(domain(message, context, !!CURVE.preHash)));
}
/** Signs message with privateKey. RFC8032 5.1.6 */
function sign(message: Hex, privateKey: Hex, context?: Hex): Uint8Array {
message = ensureBytes(message);
if (CURVE.preHash) message = CURVE.preHash(message);
const { prefix, scalar, pointBytes } = getExtendedPublicKey(privateKey);
const r = hashDomainToScalar(concatBytes(prefix, message), context);
const R = Point.BASE.multiply(r); // R = rG
const k = hashDomainToScalar(concatBytes(R.toRawBytes(), pointBytes, message), context); // k = hash(R+P+msg)
const s = mod.mod(r + k * scalar, CURVE_ORDER); // s = r + kp
return new Signature(R, s).toRawBytes();
}
/**
* Verifies EdDSA signature against message and public key.
* An extended group equation is checked.
* RFC8032 5.1.7
* Compliant with ZIP215:
* 0 <= sig.R/publicKey < 2**256 (can be >= curve.P)
* 0 <= sig.s < l
* Not compliant with RFC8032: it's not possible to comply to both ZIP & RFC at the same time.
*/
function verify(sig: SigType, message: Hex, publicKey: PubKey, context?: Hex): boolean {
message = ensureBytes(message);
if (CURVE.preHash) message = CURVE.preHash(message);
// When hex is passed, we check public key fully.
// When Point instance is passed, we assume it has already been checked, for performance.
// If user passes Point/Sig instance, we assume it has been already verified.
// We don't check its equations for performance. We do check for valid bounds for s though
// We always check for: a) s bounds. b) hex validity
if (publicKey instanceof Point) {
// ignore
} else if (publicKey instanceof Uint8Array || typeof publicKey === 'string') {
publicKey = Point.fromHex(publicKey, false);
} else {
throw new Error(`Invalid publicKey: ${publicKey}`);
}
if (sig instanceof Signature) sig.assertValidity();
else if (sig instanceof Uint8Array || typeof sig === 'string') sig = Signature.fromHex(sig);
else throw new Error(`Wrong signature: ${sig}`);
const { r, s } = sig;
const SB = ExtendedPoint.BASE.multiplyUnsafe(s);
const k = hashDomainToScalar(
concatBytes(r.toRawBytes(), publicKey.toRawBytes(), message),
context
);
const kA = ExtendedPoint.fromAffine(publicKey).multiplyUnsafe(k);
const RkA = ExtendedPoint.fromAffine(r).add(kA);
// [8][S]B = [8]R + [8][k]A'
return RkA.subtract(SB).multiplyUnsafe(CURVE.h).equals(ExtendedPoint.ZERO);
}
// Enable precomputes. Slows down first publicKey computation by 20ms.
Point.BASE._setWindowSize(8);
const utils = {
getExtendedPublicKey,
mod: modP,
invert: (a: bigint, m = CURVE.P) => mod.invert(a, m),
/**
* Not needed for ed25519 private keys. Needed if you use scalars directly (rare).
*/
hashToPrivateScalar: (hash: Hex): bigint => hashToPrivateScalar(hash, CURVE_ORDER, true),
/**
* ed25519 private keys are uniform 32-bit strings. We do not need to check for
* modulo bias like we do in secp256k1 randomPrivateKey()
*/
randomPrivateKey: (): Uint8Array => randomBytes(fieldLen),
/**
* We're doing scalar multiplication (used in getPublicKey etc) with precomputed BASE_POINT
* values. This slows down first getPublicKey() by milliseconds (see Speed section),
* but allows to speed-up subsequent getPublicKey() calls up to 20x.
* @param windowSize 2, 4, 8, 16
*/
precompute(windowSize = 8, point = Point.BASE): Point {
const cached = point.equals(Point.BASE) ? point : new Point(point.x, point.y);
cached._setWindowSize(windowSize);
cached.multiply(_2n);
return cached;
},
};
return {
CURVE,
getPublicKey,
sign,
verify,
ExtendedPoint,
Point,
Signature,
utils,
};
}

View File

@@ -1,12 +1,14 @@
/*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
import { sha256 } from '@noble/hashes/sha256'; import { sha512 } from '@noble/hashes/sha512';
import { concatBytes, randomBytes, utf8ToBytes } from '@noble/hashes/utils'; import { concatBytes, randomBytes, utf8ToBytes } from '@noble/hashes/utils';
import { twistedEdwards } from '@noble/curves/edwards'; import { twistedEdwards } from './abstract/edwards.js';
import { blake2s } from '@noble/hashes/blake2s'; import { blake2s } from '@noble/hashes/blake2s';
import { Fp } from './abstract/modular.js';
/** /**
* jubjub Twisted Edwards curve. * jubjub Twisted Edwards curve.
* https://neuromancer.sk/std/other/JubJub * https://neuromancer.sk/std/other/JubJub
* jubjub does not use EdDSA, so `hash`/sha512 params are passed because interface expects them.
*/ */
export const jubjub = twistedEdwards({ export const jubjub = twistedEdwards({
@@ -14,16 +16,16 @@ export const jubjub = twistedEdwards({
a: BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000'), a: BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000'),
d: BigInt('0x2a9318e74bfa2b48f5fd9207e6bd7fd4292d7f6d37579d2601065fd6d6343eb1'), d: BigInt('0x2a9318e74bfa2b48f5fd9207e6bd7fd4292d7f6d37579d2601065fd6d6343eb1'),
// Finite field 𝔽p over which we'll do calculations // Finite field 𝔽p over which we'll do calculations
P: BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001'), // Same value as bls12-381 Fr (not Fp)
// Subgroup order: how many points ed25519 has Fp: Fp(BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001')),
// 2n ** 252n + 27742317777372353535851937790883648493n; // Subgroup order: how many points curve has
n: BigInt('0xe7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7'), n: BigInt('0xe7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7'),
// Cofactor // Cofactor
h: BigInt(8), h: BigInt(8),
// Base point (x, y) aka generator point // Base point (x, y) aka generator point
Gx: BigInt('0x11dafe5d23e1218086a365b99fbf3d3be72f6afd7d1f72623e6b071492d1122b'), Gx: BigInt('0x11dafe5d23e1218086a365b99fbf3d3be72f6afd7d1f72623e6b071492d1122b'),
Gy: BigInt('0x1d523cf1ddab1a1793132e78c866c0c33e26ba5cc220fed7cc3f870e59d292aa'), Gy: BigInt('0x1d523cf1ddab1a1793132e78c866c0c33e26ba5cc220fed7cc3f870e59d292aa'),
hash: sha256, hash: sha512,
randomBytes, randomBytes,
} as const); } as const);
@@ -37,7 +39,7 @@ export function groupHash(tag: Uint8Array, personalization: Uint8Array) {
h.update(GH_FIRST_BLOCK); h.update(GH_FIRST_BLOCK);
h.update(tag); h.update(tag);
// NOTE: returns ExtendedPoint, in case it will be multiplied later // NOTE: returns ExtendedPoint, in case it will be multiplied later
let p = jubjub.ExtendedPoint.fromAffine(jubjub.Point.fromHex(h.digest())); let p = jubjub.ExtendedPoint.fromHex(h.digest());
// NOTE: cannot replace with isSmallOrder, returns Point*8 // NOTE: cannot replace with isSmallOrder, returns Point*8
p = p.multiply(jubjub.CURVE.h); p = p.multiply(jubjub.CURVE.h);
if (p.equals(jubjub.ExtendedPoint.ZERO)) throw new Error('Point has small order'); if (p.equals(jubjub.ExtendedPoint.ZERO)) throw new Error('Point has small order');

View File

@@ -1,159 +0,0 @@
/*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
// Utilities for modular arithmetics
const _0n = BigInt(0);
const _1n = BigInt(1);
const _2n = BigInt(2);
const _3n = BigInt(3);
const _4n = BigInt(4);
const _5n = BigInt(5);
const _8n = BigInt(8);
// Calculates a modulo b
export function mod(a: bigint, b: bigint): bigint {
const result = a % b;
return result >= _0n ? result : b + result;
}
/**
* Efficiently exponentiate num to power and do modular division.
* @example
* powMod(2n, 6n, 11n) // 64n % 11n == 9n
*/
export function pow(num: bigint, power: bigint, modulo: bigint): bigint {
if (modulo <= _0n || power < _0n) throw new Error('Expected power/modulo > 0');
if (modulo === _1n) return _0n;
let res = _1n;
while (power > _0n) {
if (power & _1n) res = (res * num) % modulo;
num = (num * num) % modulo;
power >>= _1n;
}
return res;
}
// Does x ^ (2 ^ power) mod p. pow2(30, 4) == 30 ^ (2 ^ 4)
export function pow2(x: bigint, power: bigint, modulo: bigint): bigint {
let res = x;
while (power-- > _0n) {
res *= res;
res %= modulo;
}
return res;
}
// Inverses number over modulo
export function invert(number: bigint, modulo: bigint): bigint {
if (number === _0n || modulo <= _0n) {
throw new Error(`invert: expected positive integers, got n=${number} mod=${modulo}`);
}
// Eucledian GCD https://brilliant.org/wiki/extended-euclidean-algorithm/
let a = mod(number, modulo);
let b = modulo;
// prettier-ignore
let x = _0n, y = _1n, u = _1n, v = _0n;
while (a !== _0n) {
const q = b / a;
const r = b % a;
const m = x - u * q;
const n = y - v * q;
// prettier-ignore
b = a, a = r, x = u, y = v, u = m, v = n;
}
const gcd = b;
if (gcd !== _1n) throw new Error('invert: does not exist');
return mod(x, modulo);
}
/**
* Takes a list of numbers, efficiently inverts all of them.
* @param nums list of bigints
* @param p modulo
* @returns list of inverted bigints
* @example
* invertBatch([1n, 2n, 4n], 21n);
* // => [1n, 11n, 16n]
*/
export function invertBatch(nums: bigint[], modulo: bigint): bigint[] {
const scratch = new Array(nums.length);
// Walk from first to last, multiply them by each other MOD p
const lastMultiplied = nums.reduce((acc, num, i) => {
if (num === _0n) return acc;
scratch[i] = acc;
return mod(acc * num, modulo);
}, _1n);
// Invert last element
const inverted = invert(lastMultiplied, modulo);
// Walk from last to first, multiply them by inverted each other MOD p
nums.reduceRight((acc, num, i) => {
if (num === _0n) return acc;
scratch[i] = mod(acc * scratch[i], modulo);
return mod(acc * num, modulo);
}, inverted);
return scratch;
}
// Calculates Legendre symbol: num^((P-1)/2)
export function legendre(num: bigint, fieldPrime: bigint): bigint {
return pow(num, (fieldPrime - _1n) / _2n, fieldPrime);
}
/**
* Calculates square root of a number in a finite field.
*/
export function sqrt(number: bigint, modulo: bigint): bigint {
const n = number;
const P = modulo;
const p1div4 = (P + _1n) / _4n;
// P = 3 (mod 4)
// sqrt n = n^((P+1)/4)
if (P % _4n === _3n) return pow(n, p1div4, P);
// P = 5 (mod 8)
if (P % _8n === _5n) {
const n2 = mod(n * _2n, P);
const v = pow(n2, (P - _5n) / _8n, P);
const nv = mod(n * v, P);
const i = mod(_2n * nv * v, P);
const r = mod(nv * (i - _1n), P);
return r;
}
// Other cases: Tonelli-Shanks algorithm
if (legendre(n, P) !== _1n) throw new Error('Cannot find square root');
let q: bigint, s: number, z: bigint;
for (q = P - _1n, s = 0; q % _2n === _0n; q /= _2n, s++);
if (s === 1) return pow(n, p1div4, P);
for (z = _2n; z < P && legendre(z, P) !== P - _1n; z++);
let c = pow(z, q, P);
let r = pow(n, (q + _1n) / _2n, P);
let t = pow(n, q, P);
let t2 = _0n;
while (mod(t - _1n, P) !== _0n) {
t2 = mod(t * t, P);
let i;
for (i = 1; i < s; i++) {
if (mod(t2 - _1n, P) === _0n) break;
t2 = mod(t2 * t2, P);
}
let b = pow(c, BigInt(1 << (s - i - 1)), P);
r = mod(r * b, P);
c = mod(b * b, P);
t = mod(t * c, P);
s = i;
}
return r;
}
// Little-endian check for first LE bit (last BE bit);
export const isNegativeLE = (num: bigint, modulo: bigint) => (mod(num, modulo) & _1n) === _1n;
// An idea on modular arithmetic for bls12-381:
// const FIELD = {add, pow, sqrt, mul};
// Functions will take field elements, no need for an additional class
// Could be faster. 1 bigint field will just do operations and mod later:
// instead of 'r = mod(r * b, P)' we will write r = mul(r, b);
// Could be insecure without shape check, so it needs to be done.
// Functions could be inlined by JIT.

View File

@@ -1,6 +1,7 @@
/*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
import { createCurve } from './_shortw_utils.js'; import { createCurve } from './_shortw_utils.js';
import { sha256 } from '@noble/hashes/sha256'; import { sha256 } from '@noble/hashes/sha256';
import { Fp } from './abstract/modular.js';
// NIST secp192r1 aka P192 // NIST secp192r1 aka P192
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/secg/secp192r1 // https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/secg/secp192r1
@@ -10,7 +11,7 @@ export const P192 = createCurve(
a: BigInt('0xfffffffffffffffffffffffffffffffefffffffffffffffc'), a: BigInt('0xfffffffffffffffffffffffffffffffefffffffffffffffc'),
b: BigInt('0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1'), b: BigInt('0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1'),
// Field over which we'll do calculations; 2n ** 192n - 2n ** 64n - 1n // Field over which we'll do calculations; 2n ** 192n - 2n ** 64n - 1n
P: BigInt('0xfffffffffffffffffffffffffffffffeffffffffffffffff'), Fp: Fp(BigInt('0xfffffffffffffffffffffffffffffffeffffffffffffffff')),
// Curve order, total count of valid points in the field. // Curve order, total count of valid points in the field.
n: BigInt('0xffffffffffffffffffffffff99def836146bc9b1b4d22831'), n: BigInt('0xffffffffffffffffffffffff99def836146bc9b1b4d22831'),
// Base point (x, y) aka generator point // Base point (x, y) aka generator point

View File

@@ -1,6 +1,7 @@
/*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
import { createCurve } from './_shortw_utils.js'; import { createCurve } from './_shortw_utils.js';
import { sha256 } from '@noble/hashes/sha256'; import { sha224 } from '@noble/hashes/sha256';
import { Fp } from './abstract/modular.js';
// NIST secp224r1 aka P224 // NIST secp224r1 aka P224
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/nist/P-224 // https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/nist/P-224
@@ -10,7 +11,7 @@ export const P224 = createCurve(
a: BigInt('0xfffffffffffffffffffffffffffffffefffffffffffffffffffffffe'), a: BigInt('0xfffffffffffffffffffffffffffffffefffffffffffffffffffffffe'),
b: BigInt('0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4'), b: BigInt('0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4'),
// Field over which we'll do calculations; 2n**224n - 2n**96n + 1n // Field over which we'll do calculations; 2n**224n - 2n**96n + 1n
P: BigInt('0xffffffffffffffffffffffffffffffff000000000000000000000001'), Fp: Fp(BigInt('0xffffffffffffffffffffffffffffffff000000000000000000000001')),
// Curve order, total count of valid points in the field // Curve order, total count of valid points in the field
n: BigInt('0xffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d'), n: BigInt('0xffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d'),
// Base point (x, y) aka generator point // Base point (x, y) aka generator point
@@ -19,6 +20,6 @@ export const P224 = createCurve(
h: BigInt(1), h: BigInt(1),
lowS: false, lowS: false,
} as const, } as const,
sha256 // TODO: replace with sha224 when new @noble/hashes released sha224
); );
export const secp224r1 = P224; export const secp224r1 = P224;

53
src/p256.ts Normal file
View File

@@ -0,0 +1,53 @@
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
import { createCurve } from './_shortw_utils.js';
import { sha256 } from '@noble/hashes/sha256';
import { Fp as Field } from './abstract/modular.js';
import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
import * as htf from './abstract/hash-to-curve.js';
// NIST secp256r1 aka P256
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/nist/P-256
// 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')),
});
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 { hashToCurve, encodeToCurve } = htf.hashToCurve(
secp256r1.ProjectivePoint,
(scalars: bigint[]) => mapSWU(scalars[0]),
{
DST: 'P256_XMD:SHA-256_SSWU_RO_',
encodeDST: 'P256_XMD:SHA-256_SSWU_NU_',
p: Fp.ORDER,
m: 1,
k: 128,
expand: 'xmd',
hash: sha256,
}
);
export { hashToCurve, encodeToCurve };

57
src/p384.ts Normal file
View File

@@ -0,0 +1,57 @@
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
import { createCurve } from './_shortw_utils.js';
import { sha384 } from '@noble/hashes/sha512';
import { Fp as Field } from './abstract/modular.js';
import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
import * as htf from './abstract/hash-to-curve.js';
// NIST secp384r1 aka P384
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/nist/P-384
// Field over which we'll do calculations. 2n**384n - 2n**128n - 2n**96n + 2n**32n - 1n
// prettier-ignore
const P = BigInt('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff');
const Fp = Field(P);
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;
const { hashToCurve, encodeToCurve } = htf.hashToCurve(
secp384r1.ProjectivePoint,
(scalars: bigint[]) => mapSWU(scalars[0]),
{
DST: 'P384_XMD:SHA-384_SSWU_RO_',
encodeDST: 'P384_XMD:SHA-384_SSWU_NU_',
p: Fp.ORDER,
m: 1,
k: 192,
expand: 'xmd',
hash: sha384,
}
);
export { hashToCurve, encodeToCurve };

67
src/p521.ts Normal file
View File

@@ -0,0 +1,67 @@
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
import { createCurve } from './_shortw_utils.js';
import { sha512 } from '@noble/hashes/sha512';
import { bytesToHex, PrivKey } from './abstract/utils.js';
import { Fp as Field } from './abstract/modular.js';
import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
import * as htf from './abstract/hash-to-curve.js';
// NIST secp521r1 aka P521
// Note that it's 521, which differs from 512 of its hash function.
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/nist/P-521
// Field over which we'll do calculations; 2n**521n - 1n
// 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,
Fp,
// Curve order, total count of valid points in the field
n: BigInt('0x01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409'),
// Base point (x, y) aka generator point
Gx: BigInt('0x00c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66'),
Gy: BigInt('0x011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650'),
h: BigInt(1),
lowS: false,
// P521 keys could be 130, 131, 132 bytes. We normalize to 132 bytes.
// Does not replace validation; invalid keys would still be rejected.
normalizePrivateKey(key: PrivKey) {
if (typeof key === 'bigint') return key;
if (key instanceof Uint8Array) key = bytesToHex(key);
if (typeof key !== 'string' || !([130, 131, 132].includes(key.length))) {
throw new Error('Invalid key');
}
return key.padStart(66 * 2, '0'); // ensure it's always 132 bytes
},
} as const, sha512);
export const secp521r1 = P521;
const { hashToCurve, encodeToCurve } = htf.hashToCurve(
secp521r1.ProjectivePoint,
(scalars: bigint[]) => mapSWU(scalars[0]),
{
DST: 'P521_XMD:SHA-512_SSWU_RO_',
encodeDST: 'P521_XMD:SHA-512_SSWU_NU_',
p: Fp.ORDER,
m: 1,
k: 256,
expand: 'xmd',
hash: sha512,
}
);
export { hashToCurve, encodeToCurve };

View File

@@ -1,17 +1,17 @@
/*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
import { sha256 } from '@noble/hashes/sha256'; import { sha256 } from '@noble/hashes/sha256';
import { weierstrass } from '@noble/curves/weierstrass'; import { weierstrass } from './abstract/weierstrass.js';
import { getHash } from './_shortw_utils.js'; import { getHash } from './_shortw_utils.js';
import * as mod from '@noble/curves/modular'; import * as mod from './abstract/modular.js';
const p = BigInt('0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001'); export const p = BigInt('0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001');
const q = BigInt('0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001'); export const q = BigInt('0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001');
// https://neuromancer.sk/std/other/Pallas // https://neuromancer.sk/std/other/Pallas
export const pallas = weierstrass({ export const pallas = weierstrass({
a: BigInt(0), a: BigInt(0),
b: BigInt(5), b: BigInt(5),
P: p, Fp: mod.Fp(p),
n: q, n: q,
Gx: mod.mod(BigInt(-1), p), Gx: mod.mod(BigInt(-1), p),
Gy: BigInt(2), Gy: BigInt(2),
@@ -22,7 +22,7 @@ export const pallas = weierstrass({
export const vesta = weierstrass({ export const vesta = weierstrass({
a: BigInt(0), a: BigInt(0),
b: BigInt(5), b: BigInt(5),
P: q, Fp: mod.Fp(q),
n: p, n: p,
Gx: mod.mod(BigInt(-1), q), Gx: mod.mod(BigInt(-1), q),
Gy: BigInt(2), Gy: BigInt(2),

275
src/secp256k1.ts Normal file
View File

@@ -0,0 +1,275 @@
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
import { sha256 } from '@noble/hashes/sha256';
import { Fp as Field, mod, pow2 } from './abstract/modular.js';
import { createCurve } from './_shortw_utils.js';
import { ProjPointType as PointType, mapToCurveSimpleSWU } from './abstract/weierstrass.js';
import {
ensureBytes,
concatBytes,
Hex,
bytesToNumberBE as bytesToNum,
PrivKey,
numberToBytesBE,
} from './abstract/utils.js';
import { randomBytes } from '@noble/hashes/utils';
import * as htf from './abstract/hash-to-curve.js';
/**
* secp256k1 belongs to Koblitz curves: it has efficiently computable endomorphism.
* Endomorphism uses 2x less RAM, speeds up precomputation by 2x and ECDH / key recovery by 20%.
* Should always be used for Projective's double-and-add multiplication.
* For affines cached multiplication, it trades off 1/2 init time & 1/3 ram for 20% perf hit.
* https://gist.github.com/paulmillr/eb670806793e84df628a7c434a873066
*/
const secp256k1P = BigInt('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f');
const secp256k1N = BigInt('0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141');
const _1n = BigInt(1);
const _2n = BigInt(2);
const divNearest = (a: bigint, b: bigint) => (a + b / _2n) / b;
/**
* √n = n^((p+1)/4) for fields p = 3 mod 4. We unwrap the loop and multiply bit-by-bit.
* (P+1n/4n).toString(2) would produce bits [223x 1, 0, 22x 1, 4x 0, 11, 00]
*/
function sqrtMod(y: bigint): bigint {
const P = secp256k1P;
// prettier-ignore
const _3n = BigInt(3), _6n = BigInt(6), _11n = BigInt(11), _22n = BigInt(22);
// prettier-ignore
const _23n = BigInt(23), _44n = BigInt(44), _88n = BigInt(88);
const b2 = (y * y * y) % P; // x^3, 11
const b3 = (b2 * b2 * y) % P; // x^7
const b6 = (pow2(b3, _3n, P) * b3) % P;
const b9 = (pow2(b6, _3n, P) * b3) % P;
const b11 = (pow2(b9, _2n, P) * b2) % P;
const b22 = (pow2(b11, _11n, P) * b11) % P;
const b44 = (pow2(b22, _22n, P) * b22) % P;
const b88 = (pow2(b44, _44n, P) * b44) % P;
const b176 = (pow2(b88, _88n, P) * b88) % P;
const b220 = (pow2(b176, _44n, P) * b44) % P;
const b223 = (pow2(b220, _3n, P) * b3) % P;
const t1 = (pow2(b223, _23n, P) * b22) % P;
const t2 = (pow2(t1, _6n, P) * b2) % P;
const root = pow2(t2, _2n, P);
if (!Fp.eql(Fp.sqr(root), y)) throw new Error('Cannot find square root');
return root;
}
const Fp = Field(secp256k1P, undefined, undefined, { sqrt: sqrtMod });
type Fp = bigint;
export const secp256k1 = createCurve(
{
// Params: a, b
// Seem to be rigid https://bitcointalk.org/index.php?topic=289795.msg3183975#msg3183975
a: BigInt(0),
b: BigInt(7),
// Field over which we'll do calculations;
// 2n**256n - 2n**32n - 2n**9n - 2n**8n - 2n**7n - 2n**6n - 2n**4n - 1n
Fp,
// Curve order, total count of valid points in the field
n: secp256k1N,
// Base point (x, y) aka generator point
Gx: BigInt('55066263022277343669578718895168534326250603453777594175500187360389116729240'),
Gy: BigInt('32670510020758816978083085130507043184471273380659243275938904335757337482424'),
h: BigInt(1),
// Alllow only low-S signatures by default in sign() and verify()
lowS: true,
endo: {
// Params taken from https://gist.github.com/paulmillr/eb670806793e84df628a7c434a873066
beta: BigInt('0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee'),
splitScalar: (k: bigint) => {
const n = secp256k1N;
const a1 = BigInt('0x3086d221a7d46bcde86c90e49284eb15');
const b1 = -_1n * BigInt('0xe4437ed6010e88286f547fa90abfe4c3');
const a2 = BigInt('0x114ca50f7a8e2f3f657c1108d9d44cfd8');
const b2 = a1;
const POW_2_128 = BigInt('0x100000000000000000000000000000000'); // (2n**128n).toString(16)
const c1 = divNearest(b2 * k, n);
const c2 = divNearest(-b1 * k, n);
let k1 = mod(k - c1 * a1 - c2 * a2, n);
let k2 = mod(-c1 * b1 - c2 * b2, n);
const k1neg = k1 > POW_2_128;
const k2neg = k2 > POW_2_128;
if (k1neg) k1 = n - k1;
if (k2neg) k2 = n - k2;
if (k1 > POW_2_128 || k2 > POW_2_128) {
throw new Error('splitScalar: Endomorphism failed, k=' + k);
}
return { k1neg, k1, k2neg, k2 };
},
},
},
sha256
);
// Schnorr signatures are superior to ECDSA from above.
// Below is Schnorr-specific code as per BIP0340.
// https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki
const _0n = BigInt(0);
const fe = (x: bigint) => typeof x === 'bigint' && _0n < x && x < secp256k1P;
const ge = (x: bigint) => typeof x === 'bigint' && _0n < x && x < secp256k1N;
const TAGS = {
challenge: 'BIP0340/challenge',
aux: 'BIP0340/aux',
nonce: 'BIP0340/nonce',
} as const;
/** An object mapping tags to their tagged hash prefix of [SHA256(tag) | SHA256(tag)] */
const TAGGED_HASH_PREFIXES: { [tag: string]: Uint8Array } = {};
function taggedHash(tag: string, ...messages: Uint8Array[]): Uint8Array {
let tagP = TAGGED_HASH_PREFIXES[tag];
if (tagP === undefined) {
const tagH = sha256(Uint8Array.from(tag, (c) => c.charCodeAt(0)));
tagP = concatBytes(tagH, tagH);
TAGGED_HASH_PREFIXES[tag] = tagP;
}
return sha256(concatBytes(tagP, ...messages));
}
const toRawX = (point: PointType<bigint>) => point.toRawBytes(true).slice(1);
const numTo32b = (n: bigint) => numberToBytesBE(n, 32);
const modN = (x: bigint) => mod(x, secp256k1N);
const _Point = secp256k1.ProjectivePoint;
const Gmul = (priv: PrivKey) => _Point.fromPrivateKey(priv);
const GmulAdd = (Q: PointType<bigint>, a: bigint, b: bigint) =>
_Point.BASE.multiplyAndAddUnsafe(Q, a, b);
function schnorrGetScalar(priv: bigint) {
// Let d' = int(sk)
// Fail if d' = 0 or d' ≥ n
// Let P = d'⋅G
// Let d = d' if has_even_y(P), otherwise let d = n - d' .
const point = Gmul(priv);
const scalar = point.hasEvenY() ? priv : modN(-priv);
return { point, scalar, x: toRawX(point) };
}
function lift_x(x: bigint): PointType<bigint> {
if (!fe(x)) throw new Error('bad x: need 0 < x < p'); // Fail if x ≥ p.
const c = mod(x * x * x + BigInt(7), secp256k1P); // Let c = x³ + 7 mod p.
let y = sqrtMod(c); // Let y = c^(p+1)/4 mod p.
if (y % 2n !== 0n) y = mod(-y, secp256k1P); // Return the unique point P such that x(P) = x and
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;
}
function challenge(...args: Uint8Array[]): bigint {
return modN(bytesToNum(taggedHash(TAGS.challenge, ...args)));
}
function schnorrGetPublicKey(privateKey: PrivKey): Uint8Array {
return toRawX(Gmul(privateKey)); // Let d' = int(sk). Fail if d' = 0 or d' ≥ n. Return bytes(d'⋅G)
}
/**
* Synchronously creates Schnorr signature. Improved security: verifies itself before
* producing an output.
* @param msg message (not message hash)
* @param privateKey private key
* @param auxRand random bytes that would be added to k. Bad RNG won't break it.
*/
function schnorrSign(message: Hex, privateKey: Hex, auxRand: Hex = randomBytes(32)): Uint8Array {
if (message == null) throw new Error(`sign: Expected valid message, not "${message}"`);
const m = ensureBytes(message);
// checks for isWithinCurveOrder
const { x: px, scalar: d } = schnorrGetScalar(bytesToNum(ensureBytes(privateKey, 32)));
const a = ensureBytes(auxRand, 32); // Auxiliary random data a: a 32-byte array
// TODO: replace with proper xor?
const t = numTo32b(d ^ bytesToNum(taggedHash(TAGS.aux, a))); // Let t be the byte-wise xor of bytes(d) and hash/aux(a)
const rand = taggedHash(TAGS.nonce, t, px, m); // Let rand = hash/nonce(t || bytes(P) || m)
const k_ = modN(bytesToNum(rand)); // Let k' = int(rand) mod n
if (k_ === _0n) throw new Error('sign failed: k is zero'); // Fail if k' = 0.
const { point: R, x: rx, scalar: k } = schnorrGetScalar(k_); // Let R = k'⋅G.
const e = challenge(rx, px, m); // Let e = int(hash/challenge(bytes(R) || bytes(P) || m)) mod n.
const sig = new Uint8Array(64); // Let sig = bytes(R) || bytes((k + ed) mod n).
sig.set(numTo32b(R.px), 0);
sig.set(numTo32b(modN(k + e * d)), 32);
// If Verify(bytes(P), m, sig) (see below) returns failure, abort
if (!schnorrVerify(sig, m, px)) throw new Error('sign: Invalid signature produced');
return sig;
}
/**
* Verifies Schnorr signature synchronously.
*/
function schnorrVerify(signature: Hex, message: Hex, publicKey: Hex): boolean {
try {
const P = lift_x(bytesToNum(ensureBytes(publicKey, 32))); // P = lift_x(int(pk)); fail if that fails
const sig = ensureBytes(signature, 64);
const r = bytesToNum(sig.subarray(0, 32)); // Let r = int(sig[0:32]); fail if r ≥ p.
if (!fe(r)) return false;
const s = bytesToNum(sig.subarray(32, 64)); // Let s = int(sig[32:64]); fail if s ≥ n.
if (!ge(s)) return false;
const m = ensureBytes(message);
const e = challenge(numTo32b(r), toRawX(P), m); // int(challenge(bytes(r)||bytes(P)||m)) mod n
const R = GmulAdd(P, s, modN(-e)); // R = s⋅G - e⋅P
if (!R || !R.hasEvenY() || R.toAffine().x !== r) return false; // -eP == (n-e)P
return true; // Fail if is_infinite(R) / not has_even_y(R) / x(R) ≠ r.
} catch (error) {
return false;
}
}
export const schnorr = {
// Schnorr's pubkey is just `x` of Point (BIP340)
getPublicKey: schnorrGetPublicKey,
sign: schnorrSign,
verify: schnorrVerify,
utils: { lift_x, int: bytesToNum, taggedHash },
};
const isoMap = htf.isogenyMap(
Fp,
[
// xNum
[
'0x8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa8c7',
'0x7d3d4c80bc321d5b9f315cea7fd44c5d595d2fc0bf63b92dfff1044f17c6581',
'0x534c328d23f234e6e2a413deca25caece4506144037c40314ecbd0b53d9dd262',
'0x8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa88c',
],
// xDen
[
'0xd35771193d94918a9ca34ccbb7b640dd86cd409542f8487d9fe6b745781eb49b',
'0xedadc6f64383dc1df7c4b2d51b54225406d36b641f5e41bbc52a56612a8c6d14',
'0x0000000000000000000000000000000000000000000000000000000000000001', // LAST 1
],
// yNum
[
'0x4bda12f684bda12f684bda12f684bda12f684bda12f684bda12f684b8e38e23c',
'0xc75e0c32d5cb7c0fa9d0a54b12a0a6d5647ab046d686da6fdffc90fc201d71a3',
'0x29a6194691f91a73715209ef6512e576722830a201be2018a765e85a9ecee931',
'0x2f684bda12f684bda12f684bda12f684bda12f684bda12f684bda12f38e38d84',
],
// yDen
[
'0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffff93b',
'0x7a06534bb8bdb49fd5e9e6632722c2989467c1bfc8e8d978dfb425d2685c2573',
'0x6484aa716545ca2cf3a70c3fa8fe337e0a3d21162f0d6299a7bf8192bfd2a76f',
'0x0000000000000000000000000000000000000000000000000000000000000001', // LAST 1
],
].map((i) => i.map((j) => BigInt(j))) as [Fp[], Fp[], Fp[], Fp[]]
);
const mapSWU = mapToCurveSimpleSWU(Fp, {
A: BigInt('0x3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533'),
B: BigInt('1771'),
Z: Fp.create(BigInt('-11')),
});
const { hashToCurve, encodeToCurve } = htf.hashToCurve(
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 { hashToCurve, encodeToCurve };

View File

@@ -1,33 +1,37 @@
/*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
import { keccak_256 } from '@noble/hashes/sha3'; import { keccak_256 } from '@noble/hashes/sha3';
import { sha256 } from '@noble/hashes/sha256'; import { sha256 } from '@noble/hashes/sha256';
import { hmac } from '@noble/hashes/hmac'; import { weierstrass, ProjPointType } from './abstract/weierstrass.js';
import { concatBytes, randomBytes } from '@noble/hashes/utils'; import * as cutils from './abstract/utils.js';
import { weierstrass, CHash, JacobianPointType } from '@noble/curves/weierstrass'; import { Fp, mod, Field, validateField } from './abstract/modular.js';
import * as cutils from '@noble/curves/utils'; import { getHash } from './_shortw_utils.js';
import * as poseidon from './abstract/poseidon.js';
import { utf8ToBytes } from '@noble/hashes/utils';
type ProjectivePoint = ProjPointType<bigint>;
// Stark-friendly elliptic curve // Stark-friendly elliptic curve
// https://docs.starkware.co/starkex/stark-curve.html // https://docs.starkware.co/starkex/stark-curve.html
function getHash(hash: CHash) {
return {
hash,
hmac: (key: Uint8Array, ...msgs: Uint8Array[]) => hmac(hash, key, concatBytes(...msgs)),
randomBytes,
};
}
const CURVE_N = BigInt( const CURVE_N = BigInt(
'3618502788666131213697322783095070105526743751716087489154079457884512865583' '3618502788666131213697322783095070105526743751716087489154079457884512865583'
); );
const nBitLength = 252; const nBitLength = 252;
// Copy-pasted from weierstrass.ts
function bits2int(bytes: Uint8Array): bigint {
const delta = bytes.length * 8 - nBitLength;
const num = cutils.bytesToNumberBE(bytes);
return delta > 0 ? num >> BigInt(delta) : num;
}
function bits2int_modN(bytes: Uint8Array): bigint {
return mod(bits2int(bytes), CURVE_N);
}
export const starkCurve = weierstrass({ export const starkCurve = weierstrass({
// Params: a, b // Params: a, b
a: BigInt(1), a: BigInt(1),
b: BigInt('3141592653589793238462643383279502884197169399375105820974944592307816406665'), b: BigInt('3141592653589793238462643383279502884197169399375105820974944592307816406665'),
// Field over which we'll do calculations; 2n**251n + 17n * 2n**192n + 1n // Field over which we'll do calculations; 2n**251n + 17n * 2n**192n + 1n
// There is no efficient sqrt for field (P%4==1) // There is no efficient sqrt for field (P%4==1)
P: BigInt('0x800000000000011000000000000000000000000000000000000000000000001'), Fp: Fp(BigInt('0x800000000000011000000000000000000000000000000000000000000000001')),
// Curve order, total count of valid points in the field. // Curve order, total count of valid points in the field.
n: CURVE_N, n: CURVE_N,
nBitLength: nBitLength, // len(bin(N).replace('0b','')) nBitLength: nBitLength, // len(bin(N).replace('0b',''))
@@ -38,31 +42,27 @@ export const starkCurve = weierstrass({
// Default options // Default options
lowS: false, lowS: false,
...getHash(sha256), ...getHash(sha256),
truncateHash: (hash: Uint8Array, truncateOnly = false): bigint => { // Custom truncation routines for stark curve
// TODO: cleanup, ugly code bits2int: (bytes: Uint8Array): bigint => {
// Fix truncation while (bytes[0] === 0) bytes = bytes.subarray(1);
if (!truncateOnly) { return bits2int(bytes);
let hashS = bytesToNumber0x(hash).toString(16); },
if (hashS.length === 63) { bits2int_modN: (bytes: Uint8Array): bigint => {
hashS += '0'; let hashS = cutils.bytesToNumberBE(bytes).toString(16);
hash = hexToBytes0x(hashS); if (hashS.length === 63) {
} hashS += '0';
bytes = hexToBytes0x(hashS);
} }
// Truncate zero bytes on left (compat with elliptic) // Truncate zero bytes on left (compat with elliptic)
while (hash[0] === 0) hash = hash.subarray(1); while (bytes[0] === 0) bytes = bytes.subarray(1);
const byteLength = hash.length; return bits2int_modN(bytes);
const delta = byteLength * 8 - nBitLength; // size of curve.n (252 bits)
let h = hash.length ? bytesToNumber0x(hash) : 0n;
if (delta > 0) h = h >> BigInt(delta);
if (!truncateOnly && h >= CURVE_N) h -= CURVE_N;
return h;
}, },
}); });
// Custom Starknet type conversion functions that can handle 0x and unpadded hex // Custom Starknet type conversion functions that can handle 0x and unpadded hex
function hexToBytes0x(hex: string): Uint8Array { function hexToBytes0x(hex: string): Uint8Array {
if (typeof hex !== 'string') { if (typeof hex !== 'string') {
throw new TypeError('hexToBytes: expected string, got ' + typeof hex); throw new Error('hexToBytes: expected string, got ' + typeof hex);
} }
hex = strip0x(hex); hex = strip0x(hex);
if (hex.length & 1) hex = '0' + hex; // padding if (hex.length & 1) hex = '0' + hex; // padding
@@ -79,7 +79,7 @@ function hexToBytes0x(hex: string): Uint8Array {
} }
function hexToNumber0x(hex: string): bigint { function hexToNumber0x(hex: string): bigint {
if (typeof hex !== 'string') { if (typeof hex !== 'string') {
throw new TypeError('hexToNumber: expected string, got ' + typeof hex); throw new Error('hexToNumber: expected string, got ' + typeof hex);
} }
// Big Endian // Big Endian
// TODO: strip vs no strip? // TODO: strip vs no strip?
@@ -94,23 +94,33 @@ function ensureBytes0x(hex: Hex): Uint8Array {
return hex instanceof Uint8Array ? Uint8Array.from(hex) : hexToBytes0x(hex); return hex instanceof Uint8Array ? Uint8Array.from(hex) : hexToBytes0x(hex);
} }
function sign0x(msgHash: Hex, privKey: Hex, opts: any) { function normalizePrivateKey(privKey: Hex) {
return starkCurve.sign(ensureBytes0x(msgHash), ensureBytes0x(privKey), opts); return cutils.bytesToHex(ensureBytes0x(privKey)).padStart(64, '0');
}
function getPublicKey0x(privKey: Hex, isCompressed?: boolean) {
return starkCurve.getPublicKey(normalizePrivateKey(privKey), isCompressed);
}
function getSharedSecret0x(privKeyA: Hex, pubKeyB: Hex) {
return starkCurve.getSharedSecret(normalizePrivateKey(privKeyA), pubKeyB);
}
function sign0x(msgHash: Hex, privKey: Hex, opts?: any) {
if (typeof privKey === 'string') privKey = strip0x(privKey).padStart(64, '0');
return starkCurve.sign(ensureBytes0x(msgHash), normalizePrivateKey(privKey), opts);
} }
function verify0x(signature: Hex, msgHash: Hex, pubKey: Hex) { function verify0x(signature: Hex, msgHash: Hex, pubKey: Hex) {
const sig = signature instanceof Signature ? signature : ensureBytes0x(signature); const sig = signature instanceof Signature ? signature : ensureBytes0x(signature);
return starkCurve.verify(sig, ensureBytes0x(msgHash), ensureBytes0x(pubKey)); return starkCurve.verify(sig, ensureBytes0x(msgHash), ensureBytes0x(pubKey));
} }
const { CURVE, Point, JacobianPoint, Signature, getPublicKey, getSharedSecret } = starkCurve; const { CURVE, ProjectivePoint, Signature } = starkCurve;
export const utils = starkCurve.utils; export const utils = starkCurve.utils;
export { export {
CURVE, CURVE,
Point,
Signature, Signature,
JacobianPoint, ProjectivePoint,
getPublicKey, getPublicKey0x as getPublicKey,
getSharedSecret, getSharedSecret0x as getSharedSecret,
sign0x as sign, sign0x as sign,
verify0x as verify, verify0x as verify,
}; };
@@ -128,23 +138,23 @@ type Hex = Uint8Array | string;
function hashKeyWithIndex(key: Uint8Array, index: number) { function hashKeyWithIndex(key: Uint8Array, index: number) {
let indexHex = cutils.numberToHexUnpadded(index); let indexHex = cutils.numberToHexUnpadded(index);
if (indexHex.length & 1) indexHex = '0' + indexHex; if (indexHex.length & 1) indexHex = '0' + indexHex;
return bytesToNumber0x(sha256(cutils.concatBytes(key, hexToBytes0x(indexHex)))); return sha256Num(cutils.concatBytes(key, hexToBytes0x(indexHex)));
} }
export function grindKey(seed: Hex) { export function grindKey(seed: Hex) {
const _seed = ensureBytes0x(seed); const _seed = ensureBytes0x(seed);
const sha256mask = 2n ** 256n; const sha256mask = 2n ** 256n;
const limit = sha256mask - starkCurve.utils.mod(sha256mask, starkCurve.CURVE.n);
const limit = sha256mask - mod(sha256mask, CURVE_N);
for (let i = 0; ; i++) { for (let i = 0; ; i++) {
const key = hashKeyWithIndex(_seed, i); const key = hashKeyWithIndex(_seed, i);
// key should be in [0, limit) // key should be in [0, limit)
if (key < limit) return starkCurve.utils.mod(key, starkCurve.CURVE.n).toString(16); if (key < limit) return mod(key, CURVE_N).toString(16);
} }
} }
export function getStarkKey(privateKey: Hex) { export function getStarkKey(privateKey: Hex) {
const priv = typeof privateKey === 'string' ? strip0x(privateKey) : privateKey; return bytesToHexEth(getPublicKey0x(privateKey, true).slice(1));
return bytesToHexEth(Point.fromPrivateKey(priv).toRawBytes(true).slice(1));
} }
export function ethSigToPrivate(signature: string) { export function ethSigToPrivate(signature: string) {
@@ -161,45 +171,52 @@ export function getAccountPath(
ethereumAddress: string, ethereumAddress: string,
index: number index: number
) { ) {
const layerNum = int31(bytesToNumber0x(sha256(layer))); const layerNum = int31(sha256Num(layer));
const applicationNum = int31(bytesToNumber0x(sha256(application))); const applicationNum = int31(sha256Num(application));
const eth = hexToNumber0x(ethereumAddress); const eth = hexToNumber0x(ethereumAddress);
return `m/2645'/${layerNum}'/${applicationNum}'/${int31(eth)}'/${int31(eth >> 31n)}'/${index}`; return `m/2645'/${layerNum}'/${applicationNum}'/${int31(eth)}'/${int31(eth >> 31n)}'/${index}`;
} }
// https://docs.starkware.co/starkex/pedersen-hash-function.html // https://docs.starkware.co/starkex/pedersen-hash-function.html
const PEDERSEN_POINTS = [ const PEDERSEN_POINTS_AFFINE = [
new Point( new ProjectivePoint(
2089986280348253421170679821480865132823066470938446095505822317253594081284n, 2089986280348253421170679821480865132823066470938446095505822317253594081284n,
1713931329540660377023406109199410414810705867260802078187082345529207694986n 1713931329540660377023406109199410414810705867260802078187082345529207694986n,
1n
), ),
new Point( new ProjectivePoint(
996781205833008774514500082376783249102396023663454813447423147977397232763n, 996781205833008774514500082376783249102396023663454813447423147977397232763n,
1668503676786377725805489344771023921079126552019160156920634619255970485781n 1668503676786377725805489344771023921079126552019160156920634619255970485781n,
1n
), ),
new Point( new ProjectivePoint(
2251563274489750535117886426533222435294046428347329203627021249169616184184n, 2251563274489750535117886426533222435294046428347329203627021249169616184184n,
1798716007562728905295480679789526322175868328062420237419143593021674992973n 1798716007562728905295480679789526322175868328062420237419143593021674992973n,
1n
), ),
new Point( new ProjectivePoint(
2138414695194151160943305727036575959195309218611738193261179310511854807447n, 2138414695194151160943305727036575959195309218611738193261179310511854807447n,
113410276730064486255102093846540133784865286929052426931474106396135072156n 113410276730064486255102093846540133784865286929052426931474106396135072156n,
1n
), ),
new Point( new ProjectivePoint(
2379962749567351885752724891227938183011949129833673362440656643086021394946n, 2379962749567351885752724891227938183011949129833673362440656643086021394946n,
776496453633298175483985398648758586525933812536653089401905292063708816422n 776496453633298175483985398648758586525933812536653089401905292063708816422n,
1n
), ),
]; ];
// for (const p of PEDERSEN_POINTS) p._setWindowSize(8); // for (const p of PEDERSEN_POINTS) p._setWindowSize(8);
const PEDERSEN_POINTS_JACOBIAN = PEDERSEN_POINTS.map(JacobianPoint.fromAffine); const PEDERSEN_POINTS = PEDERSEN_POINTS_AFFINE;
function pedersenPrecompute(p1: JacobianPointType, p2: JacobianPointType): JacobianPointType[] { function pedersenPrecompute(p1: ProjectivePoint, p2: ProjectivePoint): ProjectivePoint[] {
const out: JacobianPointType[] = []; const out: ProjectivePoint[] = [];
let p = p1; let p = p1;
for (let i = 0; i < 248; i++) { for (let i = 0; i < 248; i++) {
out.push(p); out.push(p);
p = p.double(); p = p.double();
} }
// NOTE: we cannot use wNAF here, because last 4 bits will require full 248 bits multiplication
// We can add support for this to wNAF, but it will complicate wNAF.
p = p2; p = p2;
for (let i = 0; i < 4; i++) { for (let i = 0; i < 4; i++) {
out.push(p); out.push(p);
@@ -207,14 +224,8 @@ function pedersenPrecompute(p1: JacobianPointType, p2: JacobianPointType): Jacob
} }
return out; return out;
} }
const PEDERSEN_POINTS1 = pedersenPrecompute( const PEDERSEN_POINTS1 = pedersenPrecompute(PEDERSEN_POINTS[1], PEDERSEN_POINTS[2]);
PEDERSEN_POINTS_JACOBIAN[1], const PEDERSEN_POINTS2 = pedersenPrecompute(PEDERSEN_POINTS[3], PEDERSEN_POINTS[4]);
PEDERSEN_POINTS_JACOBIAN[2]
);
const PEDERSEN_POINTS2 = pedersenPrecompute(
PEDERSEN_POINTS_JACOBIAN[3],
PEDERSEN_POINTS_JACOBIAN[4]
);
type PedersenArg = Hex | bigint | number; type PedersenArg = Hex | bigint | number;
function pedersenArg(arg: PedersenArg): bigint { function pedersenArg(arg: PedersenArg): bigint {
@@ -225,20 +236,16 @@ function pedersenArg(arg: PedersenArg): bigint {
value = BigInt(arg); value = BigInt(arg);
} else value = bytesToNumber0x(ensureBytes0x(arg)); } else value = bytesToNumber0x(ensureBytes0x(arg));
// [0..Fp) // [0..Fp)
if (!(0n <= value && value < starkCurve.CURVE.P)) if (!(0n <= value && value < starkCurve.CURVE.Fp.ORDER))
throw new Error(`PedersenArg should be 0 <= value < CURVE.P: ${value}`); throw new Error(`PedersenArg should be 0 <= value < CURVE.P: ${value}`);
return value; return value;
} }
function pedersenSingle( function pedersenSingle(point: ProjectivePoint, value: PedersenArg, constants: ProjectivePoint[]) {
point: JacobianPointType,
value: PedersenArg,
constants: JacobianPointType[]
) {
let x = pedersenArg(value); let x = pedersenArg(value);
for (let j = 0; j < 252; j++) { for (let j = 0; j < 252; j++) {
const pt = constants[j]; const pt = constants[j];
if (pt.x === point.x) throw new Error('Same point'); if (pt.px === point.px) throw new Error('Same point');
if ((x & 1n) !== 0n) point = point.add(pt); if ((x & 1n) !== 0n) point = point.add(pt);
x >>= 1n; x >>= 1n;
} }
@@ -247,10 +254,10 @@ function pedersenSingle(
// shift_point + x_low * P_0 + x_high * P1 + y_low * P2 + y_high * P3 // shift_point + x_low * P_0 + x_high * P1 + y_low * P2 + y_high * P3
export function pedersen(x: PedersenArg, y: PedersenArg) { export function pedersen(x: PedersenArg, y: PedersenArg) {
let point: JacobianPointType = PEDERSEN_POINTS_JACOBIAN[0]; let point: ProjectivePoint = PEDERSEN_POINTS[0];
point = pedersenSingle(point, x, PEDERSEN_POINTS1); point = pedersenSingle(point, x, PEDERSEN_POINTS1);
point = pedersenSingle(point, y, PEDERSEN_POINTS2); point = pedersenSingle(point, y, PEDERSEN_POINTS2);
return bytesToHexEth(point.toAffine().toRawBytes(true).slice(1)); return bytesToHexEth(point.toRawBytes(true).slice(1));
} }
export function hashChain(data: PedersenArg[], fn = pedersen) { export function hashChain(data: PedersenArg[], fn = pedersen) {
@@ -265,5 +272,85 @@ export function hashChain(data: PedersenArg[], fn = pedersen) {
export const computeHashOnElements = (data: PedersenArg[], fn = pedersen) => export const computeHashOnElements = (data: PedersenArg[], fn = pedersen) =>
[0, ...data, data.length].reduce((x, y) => fn(x, y)); [0, ...data, data.length].reduce((x, y) => fn(x, y));
const MASK_250 = 2n ** 250n - 1n; const MASK_250 = cutils.bitMask(250);
export const keccak = (data: Uint8Array) => bytesToNumber0x(keccak_256(data)) & MASK_250; export const keccak = (data: Uint8Array): bigint => bytesToNumber0x(keccak_256(data)) & MASK_250;
const sha256Num = (data: Uint8Array | string): bigint => cutils.bytesToNumberBE(sha256(data));
// Poseidon hash
export const Fp253 = Fp(
BigInt('14474011154664525231415395255581126252639794253786371766033694892385558855681')
); // 2^253 + 2^199 + 1
export const Fp251 = Fp(
BigInt('3618502788666131213697322783095070105623107215331596699973092056135872020481')
); // 2^251 + 17 * 2^192 + 1
function poseidonRoundConstant(Fp: Field<bigint>, name: string, idx: number) {
const val = Fp.fromBytes(sha256(utf8ToBytes(`${name}${idx}`)));
return Fp.create(val);
}
// NOTE: doesn't check eiginvalues and possible can create unsafe matrix. But any filtration here will break compatibility with starknet
// Please use only if you really know what you doing.
// https://eprint.iacr.org/2019/458.pdf Section 2.3 (Avoiding Insecure Matrices)
export function _poseidonMDS(Fp: Field<bigint>, name: string, m: number, attempt = 0) {
const x_values: bigint[] = [];
const y_values: bigint[] = [];
for (let i = 0; i < m; i++) {
x_values.push(poseidonRoundConstant(Fp, `${name}x`, attempt * m + i));
y_values.push(poseidonRoundConstant(Fp, `${name}y`, attempt * m + i));
}
if (new Set([...x_values, ...y_values]).size !== 2 * m)
throw new Error('X and Y values are not distinct');
return x_values.map((x) => y_values.map((y) => Fp.inv(Fp.sub(x, y))));
}
const MDS_SMALL = [
[3, 1, 1],
[1, -1, 1],
[1, 1, -2],
].map((i) => i.map(BigInt));
export type PoseidonOpts = {
Fp: Field<bigint>;
rate: number;
capacity: number;
roundsFull: number;
roundsPartial: number;
};
export function poseidonBasic(opts: PoseidonOpts, mds: bigint[][]) {
validateField(opts.Fp);
if (!Number.isSafeInteger(opts.rate) || !Number.isSafeInteger(opts.capacity))
throw new Error(`Wrong poseidon opts: ${opts}`);
const m = opts.rate + opts.capacity;
const rounds = opts.roundsFull + opts.roundsPartial;
const roundConstants = [];
for (let i = 0; i < rounds; i++) {
const row = [];
for (let j = 0; j < m; j++) row.push(poseidonRoundConstant(opts.Fp, 'Hades', m * i + j));
roundConstants.push(row);
}
return poseidon.poseidon({
...opts,
t: m,
sboxPower: 3,
reversePartialPowIdx: true, // Why?!
mds,
roundConstants,
});
}
export function poseidonCreate(opts: PoseidonOpts, mdsAttempt = 0) {
const m = opts.rate + opts.capacity;
if (!Number.isSafeInteger(mdsAttempt)) throw new Error(`Wrong mdsAttempt=${mdsAttempt}`);
return poseidonBasic(opts, _poseidonMDS(opts.Fp, 'HadesMDS', m, mdsAttempt));
}
export const poseidonSmall = poseidonBasic(
{ Fp: Fp251, rate: 2, capacity: 1, roundsFull: 8, roundsPartial: 83 },
MDS_SMALL
);
export function poseidonHash(x: bigint, y: bigint, fn = poseidonSmall) {
return fn([x, y, 2n])[0];
}

View File

@@ -1,152 +0,0 @@
/*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
// We accept hex strings besides Uint8Array for simplicity
export type Hex = Uint8Array | string;
// Very few implementations accept numbers, we do it to ease learning curve
export type PrivKey = Hex | bigint | number;
// NOTE: these are generic, even if curve is on some polynominal field (bls), it will still have P/n/h
// But generator can be different (Fp2/Fp6 for bls?)
export type BasicCurve = {
// Field over which we'll do calculations (Fp)
P: bigint;
// Curve order, total count of valid points in the field
n: bigint;
// Bit/byte length of curve order
nBitLength?: number;
nByteLength?: number;
// Cofactor
// NOTE: we can assign default value of 1, but then users will just ignore it, without validating with spec
// Has not use for now, but nice to have in API
h: bigint;
// Base point (x, y) aka generator point
Gx: bigint;
Gy: bigint;
};
export function validateOpts<T extends BasicCurve>(curve: T) {
for (const i of ['P', 'n', 'h', 'Gx', 'Gy'] as const) {
if (typeof curve[i] !== 'bigint')
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
}
for (const i of ['nBitLength', 'nByteLength'] as const) {
if (curve[i] === undefined) continue; // Optional
if (!Number.isSafeInteger(curve[i]))
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
}
// Set defaults
return Object.freeze({ ...nLength(curve.n, curve.nBitLength), ...curve } as const);
}
import * as mod from './modular.js';
const hexes = Array.from({ length: 256 }, (v, i) => i.toString(16).padStart(2, '0'));
export function bytesToHex(uint8a: Uint8Array): string {
if (!(uint8a instanceof Uint8Array)) throw new Error('Expected Uint8Array');
// pre-caching improves the speed 6x
let hex = '';
for (let i = 0; i < uint8a.length; i++) {
hex += hexes[uint8a[i]];
}
return hex;
}
export function numberToHexUnpadded(num: number | bigint): string {
const hex = num.toString(16);
return hex.length & 1 ? `0${hex}` : hex;
}
export function hexToNumber(hex: string): bigint {
if (typeof hex !== 'string') {
throw new TypeError('hexToNumber: expected string, got ' + typeof hex);
}
// Big Endian
return BigInt(`0x${hex}`);
}
// Caching slows it down 2-3x
export function hexToBytes(hex: string): Uint8Array {
if (typeof hex !== 'string') {
throw new TypeError('hexToBytes: expected string, got ' + typeof hex);
}
if (hex.length % 2) throw new Error('hexToBytes: received invalid unpadded hex ' + hex.length);
const array = new Uint8Array(hex.length / 2);
for (let i = 0; i < array.length; i++) {
const j = i * 2;
const hexByte = hex.slice(j, j + 2);
const byte = Number.parseInt(hexByte, 16);
if (Number.isNaN(byte) || byte < 0) throw new Error('Invalid byte sequence');
array[i] = byte;
}
return array;
}
// Big Endian
export function bytesToNumberBE(bytes: Uint8Array): bigint {
return hexToNumber(bytesToHex(bytes));
}
export function bytesToNumberLE(uint8a: Uint8Array): bigint {
if (!(uint8a instanceof Uint8Array)) throw new Error('Expected Uint8Array');
return BigInt('0x' + bytesToHex(Uint8Array.from(uint8a).reverse()));
}
export 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();
export function ensureBytes(hex: Hex, expectedLength?: number): Uint8Array {
// Uint8Array.from() instead of hash.slice() because node.js Buffer
// is instance of Uint8Array, and its slice() creates **mutable** copy
const bytes = hex instanceof Uint8Array ? Uint8Array.from(hex) : hexToBytes(hex);
if (typeof expectedLength === 'number' && bytes.length !== expectedLength)
throw new Error(`Expected ${expectedLength} bytes`);
return bytes;
}
// Copies several Uint8Arrays into one.
export function concatBytes(...arrays: Uint8Array[]): Uint8Array {
if (!arrays.every((b) => b instanceof Uint8Array)) throw new Error('Uint8Array list expected');
if (arrays.length === 1) return arrays[0];
const length = arrays.reduce((a, arr) => a + arr.length, 0);
const result = new Uint8Array(length);
for (let i = 0, pad = 0; i < arrays.length; i++) {
const arr = arrays[i];
result.set(arr, pad);
pad += arr.length;
}
return result;
}
// CURVE.n lengths
export function nLength(n: bigint, nBitLength?: number) {
// Bit size, byte size of CURVE.n
const _nBitLength = nBitLength !== undefined ? nBitLength : n.toString(2).length;
const nByteLength = Math.ceil(_nBitLength / 8);
return { nBitLength: _nBitLength, nByteLength };
}
/**
* Can take (n+8) or more bytes of uniform input e.g. from CSPRNG or KDF
* and convert them into private scalar, with the modulo bias being neglible.
* As per FIPS 186 B.4.1.
* https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
* @param hash hash output from sha512, or a similar function
* @returns valid private scalar
*/
const _1n = BigInt(1);
export function hashToPrivateScalar(hash: Hex, CURVE_ORDER: bigint, isLE = false): bigint {
hash = ensureBytes(hash);
const orderLen = nLength(CURVE_ORDER).nByteLength;
const minLen = orderLen + 8;
if (orderLen < 16 || hash.length < minLen || hash.length > 1024)
throw new Error('Expected valid bytes of private key as per FIPS 186');
const num = isLE ? bytesToNumberLE(hash) : bytesToNumberBE(hash);
return mod.mod(num, CURVE_ORDER - _1n) + _1n;
}
export function equalBytes(b1: Uint8Array, b2: Uint8Array) {
// 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;
}

File diff suppressed because it is too large Load Diff

648
test/basic.test.js Normal file
View File

@@ -0,0 +1,648 @@
import { deepStrictEqual, throws } from 'assert';
import { should, describe } from 'micro-should';
import * as fc from 'fast-check';
import * as mod from '../lib/esm/abstract/modular.js';
import { bytesToHex as toHex } from '../lib/esm/abstract/utils.js';
// Generic tests for all curves in package
import { secp192r1 } from '../lib/esm/p192.js';
import { secp224r1 } from '../lib/esm/p224.js';
import { secp256r1 } from '../lib/esm/p256.js';
import { secp384r1 } from '../lib/esm/p384.js';
import { secp521r1 } from '../lib/esm/p521.js';
import { secp256k1 } from '../lib/esm/secp256k1.js';
import { ed25519, ed25519ctx, ed25519ph } from '../lib/esm/ed25519.js';
import { ed448, ed448ph } from '../lib/esm/ed448.js';
import { starkCurve } from '../lib/esm/stark.js';
import { pallas, vesta } from '../lib/esm/pasta.js';
import { bn254 } from '../lib/esm/bn.js';
import { jubjub } from '../lib/esm/jubjub.js';
import { bls12_381 } from '../lib/esm/bls12-381.js';
// Fields tests
const FIELDS = {
secp192r1: { Fp: [secp192r1.CURVE.Fp] },
secp224r1: { Fp: [secp224r1.CURVE.Fp] },
secp256r1: { Fp: [secp256r1.CURVE.Fp] },
secp521r1: { Fp: [secp521r1.CURVE.Fp] },
secp256k1: { Fp: [secp256k1.CURVE.Fp] },
stark: { Fp: [starkCurve.CURVE.Fp] },
jubjub: { Fp: [jubjub.CURVE.Fp] },
ed25519: { Fp: [ed25519.CURVE.Fp] },
ed448: { Fp: [ed448.CURVE.Fp] },
bn254: { Fp: [bn254.CURVE.Fp] },
pallas: { Fp: [pallas.CURVE.Fp] },
vesta: { Fp: [vesta.CURVE.Fp] },
bls12: {
Fp: [bls12_381.CURVE.Fp],
Fp2: [
bls12_381.CURVE.Fp2,
fc.array(fc.bigInt(1n, bls12_381.CURVE.Fp.ORDER - 1n), {
minLength: 2,
maxLength: 2,
}),
(Fp2, num) => Fp2.fromBigTuple([num[0], num[1]]),
],
// Fp6: [bls12_381.CURVE.Fp6],
Fp12: [
bls12_381.CURVE.Fp12,
fc.array(fc.bigInt(1n, bls12_381.CURVE.Fp.ORDER - 1n), {
minLength: 12,
maxLength: 12,
}),
(Fp12, num) => Fp12.fromBigTwelve(num),
],
},
};
for (const c in FIELDS) {
const curve = FIELDS[c];
for (const f in curve) {
const Fp = curve[f][0];
const name = `${c}/${f}:`;
const FC_BIGINT = curve[f][1] ? curve[f][1] : fc.bigInt(1n, Fp.ORDER - 1n);
const create = curve[f][2] ? curve[f][2].bind(null, Fp) : (num) => Fp.create(num);
describe(name, () => {
should('equality', () => {
fc.assert(
fc.property(FC_BIGINT, (num) => {
const a = create(num);
const b = create(num);
deepStrictEqual(Fp.eql(a, b), true);
deepStrictEqual(Fp.eql(b, a), true);
})
);
});
should('non-equality', () => {
fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => {
const a = create(num1);
const b = create(num2);
deepStrictEqual(Fp.eql(a, b), num1 === num2);
deepStrictEqual(Fp.eql(b, a), num1 === num2);
})
);
});
should('add/subtract/commutativity', () => {
fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => {
const a = create(num1);
const b = create(num2);
deepStrictEqual(Fp.add(a, b), Fp.add(b, a));
})
);
});
should('add/subtract/associativity', () => {
fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, FC_BIGINT, (num1, num2, num3) => {
const a = create(num1);
const b = create(num2);
const c = create(num3);
deepStrictEqual(Fp.add(a, Fp.add(b, c)), Fp.add(Fp.add(a, b), c));
})
);
});
should('add/subtract/x+0=x', () => {
fc.assert(
fc.property(FC_BIGINT, (num) => {
const a = create(num);
deepStrictEqual(Fp.add(a, Fp.ZERO), a);
})
);
});
should('add/subtract/x-0=x', () => {
fc.assert(
fc.property(FC_BIGINT, (num) => {
const a = create(num);
deepStrictEqual(Fp.sub(a, Fp.ZERO), a);
deepStrictEqual(Fp.sub(a, a), Fp.ZERO);
})
);
});
should('add/subtract/negate equality', () => {
fc.assert(
fc.property(FC_BIGINT, (num1) => {
const a = create(num1);
const b = create(num1);
deepStrictEqual(Fp.sub(Fp.ZERO, a), Fp.neg(a));
deepStrictEqual(Fp.sub(a, b), Fp.add(a, Fp.neg(b)));
deepStrictEqual(Fp.sub(a, b), Fp.add(a, Fp.mul(b, Fp.create(-1n))));
})
);
});
should('add/subtract/negate', () => {
fc.assert(
fc.property(FC_BIGINT, (num) => {
const a = create(num);
deepStrictEqual(Fp.neg(a), Fp.sub(Fp.ZERO, a));
deepStrictEqual(Fp.neg(a), Fp.mul(a, Fp.create(-1n)));
})
);
});
should('negate(0)', () => {
deepStrictEqual(Fp.neg(Fp.ZERO), Fp.ZERO);
});
should('multiply/commutativity', () => {
fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => {
const a = create(num1);
const b = create(num2);
deepStrictEqual(Fp.mul(a, b), Fp.mul(b, a));
})
);
});
should('multiply/associativity', () => {
fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, FC_BIGINT, (num1, num2, num3) => {
const a = create(num1);
const b = create(num2);
const c = create(num3);
deepStrictEqual(Fp.mul(a, Fp.mul(b, c)), Fp.mul(Fp.mul(a, b), c));
})
);
});
should('multiply/distributivity', () => {
fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, FC_BIGINT, (num1, num2, num3) => {
const a = create(num1);
const b = create(num2);
const c = create(num3);
deepStrictEqual(Fp.mul(a, Fp.add(b, c)), Fp.add(Fp.mul(b, a), Fp.mul(c, a)));
})
);
});
should('multiply/add equality', () => {
fc.assert(
fc.property(FC_BIGINT, (num) => {
const a = create(num);
deepStrictEqual(Fp.mul(a, 0n), Fp.ZERO);
deepStrictEqual(Fp.mul(a, Fp.ZERO), Fp.ZERO);
deepStrictEqual(Fp.mul(a, 1n), a);
deepStrictEqual(Fp.mul(a, Fp.ONE), a);
deepStrictEqual(Fp.mul(a, 2n), Fp.add(a, a));
deepStrictEqual(Fp.mul(a, 3n), Fp.add(Fp.add(a, a), a));
deepStrictEqual(Fp.mul(a, 4n), Fp.add(Fp.add(Fp.add(a, a), a), a));
})
);
});
should('multiply/square equality', () => {
fc.assert(
fc.property(FC_BIGINT, (num) => {
const a = create(num);
deepStrictEqual(Fp.sqr(a), Fp.mul(a, a));
})
);
});
should('multiply/pow equality', () => {
fc.assert(
fc.property(FC_BIGINT, (num) => {
const a = create(num);
deepStrictEqual(Fp.pow(a, 0n), Fp.ONE);
deepStrictEqual(Fp.pow(a, 1n), a);
deepStrictEqual(Fp.pow(a, 2n), Fp.mul(a, a));
deepStrictEqual(Fp.pow(a, 3n), Fp.mul(Fp.mul(a, a), a));
})
);
});
should('square(0)', () => {
deepStrictEqual(Fp.sqr(Fp.ZERO), Fp.ZERO);
deepStrictEqual(Fp.mul(Fp.ZERO, Fp.ZERO), Fp.ZERO);
});
should('square(1)', () => {
deepStrictEqual(Fp.sqr(Fp.ONE), Fp.ONE);
deepStrictEqual(Fp.mul(Fp.ONE, Fp.ONE), Fp.ONE);
});
should('square(-1)', () => {
const minus1 = Fp.neg(Fp.ONE);
deepStrictEqual(Fp.sqr(minus1), Fp.ONE);
deepStrictEqual(Fp.mul(minus1, minus1), Fp.ONE);
});
const isSquare = mod.FpIsSquare(Fp);
// Not implemented
if (Fp !== bls12_381.CURVE.Fp12) {
should('multiply/sqrt', () => {
fc.assert(
fc.property(FC_BIGINT, (num) => {
const a = create(num);
let root;
try {
root = Fp.sqrt(a);
} catch (e) {
deepStrictEqual(isSquare(a), false);
return;
}
deepStrictEqual(isSquare(a), true);
deepStrictEqual(Fp.eql(Fp.sqr(root), a), true, 'sqrt(a)^2 == a');
deepStrictEqual(Fp.eql(Fp.sqr(Fp.neg(root)), a), true, '(-sqrt(a))^2 == a');
})
);
});
should('sqrt(0)', () => {
deepStrictEqual(Fp.sqrt(Fp.ZERO), Fp.ZERO);
const sqrt1 = Fp.sqrt(Fp.ONE);
deepStrictEqual(
Fp.eql(sqrt1, Fp.ONE) || Fp.eql(sqrt1, Fp.neg(Fp.ONE)),
true,
'sqrt(1) = 1 or -1'
);
});
}
should('div/division by one equality', () => {
fc.assert(
fc.property(FC_BIGINT, (num) => {
const a = create(num);
if (Fp.eql(a, Fp.ZERO)) return; // No division by zero
deepStrictEqual(Fp.div(a, Fp.ONE), a);
deepStrictEqual(Fp.div(a, a), Fp.ONE);
})
);
});
should('zero division equality', () => {
fc.assert(
fc.property(FC_BIGINT, (num) => {
const a = create(num);
deepStrictEqual(Fp.div(Fp.ZERO, a), Fp.ZERO);
})
);
});
should('div/division distributivity', () => {
fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, FC_BIGINT, (num1, num2, num3) => {
const a = create(num1);
const b = create(num2);
const c = create(num3);
deepStrictEqual(Fp.div(Fp.add(a, b), c), Fp.add(Fp.div(a, c), Fp.div(b, c)));
})
);
});
should('div/division and multiplication equality', () => {
fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => {
const a = create(num1);
const b = create(num2);
deepStrictEqual(Fp.div(a, b), Fp.mul(a, Fp.inv(b)));
})
);
});
});
}
}
// Group tests
// prettier-ignore
const CURVES = {
secp192r1, secp224r1, secp256r1, secp384r1, secp521r1,
secp256k1,
ed25519, ed25519ctx, ed25519ph,
ed448, ed448ph,
starkCurve,
pallas, vesta,
bn254,
jubjub,
};
const NUM_RUNS = 5;
const getXY = (p) => ({ x: p.x, y: p.y });
function equal(a, b, comment) {
deepStrictEqual(a.equals(b), true, `eq(${comment})`);
if (a.toAffine && b.toAffine) {
deepStrictEqual(getXY(a.toAffine()), getXY(b.toAffine()), `eqToAffine(${comment})`);
} else if (!a.toAffine && !b.toAffine) {
// Already affine
deepStrictEqual(getXY(a), getXY(b), `eqAffine(${comment})`);
} else throw new Error('Different point types');
}
for (const name in CURVES) {
const C = CURVES[name];
const CURVE_ORDER = C.CURVE.n;
const FC_BIGINT = fc.bigInt(1n + 1n, CURVE_ORDER - 1n);
// Check that curve doesn't accept points from other curves
const O = name === 'secp256k1' ? secp256r1 : secp256k1;
const POINTS = {};
const OTHER_POINTS = {};
for (const name of ['Point', 'ProjectivePoint', 'ExtendedPoint', 'ProjectivePoint']) {
POINTS[name] = C[name];
OTHER_POINTS[name] = O[name];
}
for (const pointName in POINTS) {
const p = POINTS[pointName];
const o = OTHER_POINTS[pointName];
if (!p) continue;
const G = [p.ZERO, p.BASE];
for (let i = 2n; i < 10n; i++) G.push(G[1].multiply(i));
const title = `${name}/${pointName}`;
describe(title, () => {
describe('basic group laws', () => {
// Here we check basic group laws, to verify that points works as group
should('zero', () => {
equal(G[0].double(), G[0], '(0*G).double() = 0');
equal(G[0].add(G[0]), G[0], '0*G + 0*G = 0');
equal(G[0].subtract(G[0]), G[0], '0*G - 0*G = 0');
equal(G[0].negate(), G[0], '-0 = 0');
for (let i = 0; i < G.length; i++) {
const p = G[i];
equal(p, p.add(G[0]), `${i}*G + 0 = ${i}*G`);
equal(G[0].multiply(BigInt(i + 1)), G[0], `${i + 1}*0 = 0`);
}
});
should('one', () => {
equal(G[1].double(), G[2], '(1*G).double() = 2*G');
equal(G[1].subtract(G[1]), G[0], '1*G - 1*G = 0');
equal(G[1].add(G[1]), G[2], '1*G + 1*G = 2*G');
});
should('sanity tests', () => {
equal(G[2].double(), G[4], '(2*G).double() = 4*G');
equal(G[2].add(G[2]), G[4], '2*G + 2*G = 4*G');
equal(G[7].add(G[3].negate()), G[4], '7*G - 3*G = 4*G');
});
should('add commutativity', () => {
equal(G[4].add(G[3]), G[3].add(G[4]), '4*G + 3*G = 3*G + 4*G');
equal(G[4].add(G[3]), G[3].add(G[2]).add(G[2]), '4*G + 3*G = 3*G + 2*G + 2*G');
});
should('double', () => {
equal(G[3].double(), G[6], '(3*G).double() = 6*G');
});
should('multiply', () => {
equal(G[2].multiply(3n), G[6], '(2*G).multiply(3) = 6*G');
});
should('add same-point', () => {
equal(G[3].add(G[3]), G[6], '3*G + 3*G = 6*G');
});
should('add same-point negative', () => {
equal(G[3].add(G[3].negate()), G[0], '3*G + (- 3*G) = 0*G');
equal(G[3].subtract(G[3]), G[0], '3*G - 3*G = 0*G');
});
should('mul by curve order', () => {
equal(G[1].multiply(CURVE_ORDER - 1n).add(G[1]), G[0], '(N-1)*G + G = 0');
equal(G[1].multiply(CURVE_ORDER - 1n).add(G[2]), G[1], '(N-1)*G + 2*G = 1*G');
equal(G[1].multiply(CURVE_ORDER - 2n).add(G[2]), G[0], '(N-2)*G + 2*G = 0');
const half = CURVE_ORDER / 2n;
const carry = CURVE_ORDER % 2n === 1n ? G[1] : G[0];
equal(G[1].multiply(half).double().add(carry), G[0], '((N/2) * G).double() = 0');
});
should('inversion', () => {
const a = 1234n;
const b = 5678n;
const c = a * b;
equal(G[1].multiply(a).multiply(b), G[1].multiply(c), 'a*b*G = c*G');
const inv = mod.invert(b, CURVE_ORDER);
equal(G[1].multiply(c).multiply(inv), G[1].multiply(a), 'c*G * (1/b)*G = a*G');
});
should('multiply, rand', () =>
fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, (a, b) => {
const c = mod.mod(a + b, CURVE_ORDER);
if (c === CURVE_ORDER || c < 1n) return;
const pA = G[1].multiply(a);
const pB = G[1].multiply(b);
const pC = G[1].multiply(c);
equal(pA.add(pB), pB.add(pA), 'pA + pB = pB + pA');
equal(pA.add(pB), pC, 'pA + pB = pC');
}),
{ numRuns: NUM_RUNS }
)
);
should('multiply2, rand', () =>
fc.assert(
fc.property(FC_BIGINT, FC_BIGINT, (a, b) => {
const c = mod.mod(a * b, CURVE_ORDER);
const pA = G[1].multiply(a);
const pB = G[1].multiply(b);
equal(pA.multiply(b), pB.multiply(a), 'b*pA = a*pB');
equal(pA.multiply(b), G[1].multiply(c), 'b*pA = c*G');
}),
{ numRuns: NUM_RUNS }
)
);
});
for (const op of ['add', 'subtract']) {
describe(op, () => {
should('type check', () => {
throws(() => G[1][op](0), '0');
throws(() => G[1][op](0n), '0n');
G[1][op](G[2]);
throws(() => G[1][op](CURVE_ORDER), 'CURVE_ORDER');
throws(() => G[1][op](123.456), '123.456');
throws(() => G[1][op](true), 'true');
throws(() => G[1][op]('1'), "'1'");
throws(
() => G[1][op]({ x: 1n, y: 1n, z: 1n, t: 1n }),
'{ x: 1n, y: 1n, z: 1n, t: 1n }'
);
throws(() => G[1][op](new Uint8Array([])), 'ui8a([])');
throws(() => G[1][op](new Uint8Array([0])), 'ui8a([0])');
throws(() => G[1][op](new Uint8Array([1])), 'ui8a([1])');
throws(() => G[1][op](new Uint8Array(4096).fill(1)), 'ui8a(4096*[1])');
// if (G[1].toAffine) throws(() => G[1][op](C.Point.BASE), `Point ${op} ${pointName}`);
throws(() => G[1][op](o.BASE), `${op}/other curve point`);
});
});
}
should('equals type check', () => {
throws(() => G[1].equals(0), '0');
throws(() => G[1].equals(0n), '0n');
deepStrictEqual(G[1].equals(G[2]), false, '1*G != 2*G');
deepStrictEqual(G[1].equals(G[1]), true, '1*G == 1*G');
deepStrictEqual(G[2].equals(G[2]), true, '2*G == 2*G');
throws(() => G[1].equals(CURVE_ORDER), 'CURVE_ORDER');
throws(() => G[1].equals(123.456), '123.456');
throws(() => G[1].equals(true), 'true');
throws(() => G[1].equals('1'), "'1'");
throws(() => G[1].equals({ x: 1n, y: 1n, z: 1n, t: 1n }), '{ x: 1n, y: 1n, z: 1n, t: 1n }');
throws(() => G[1].equals(new Uint8Array([])), 'ui8a([])');
throws(() => G[1].equals(new Uint8Array([0])), 'ui8a([0])');
throws(() => G[1].equals(new Uint8Array([1])), 'ui8a([1])');
throws(() => G[1].equals(new Uint8Array(4096).fill(1)), 'ui8a(4096*[1])');
// if (G[1].toAffine) throws(() => G[1].equals(C.Point.BASE), 'Point.equals(${pointName})');
throws(() => G[1].equals(o.BASE), 'other curve point');
});
for (const op of ['multiply', 'multiplyUnsafe']) {
if (!p.BASE[op]) continue;
describe(op, () => {
should('type check', () => {
if (op !== 'multiplyUnsafe') {
throws(() => G[1][op](0), '0');
throws(() => G[1][op](0n), '0n');
}
G[1][op](1n);
G[1][op](CURVE_ORDER - 1n);
throws(() => G[1][op](G[2]), 'G[2]');
throws(() => G[1][op](CURVE_ORDER), 'CURVE_ORDER');
throws(() => G[1][op](CURVE_ORDER + 1n), 'CURVE_ORDER+1');
throws(() => G[1][op](123.456), '123.456');
throws(() => G[1][op](true), 'true');
throws(() => G[1][op]('1'), '1');
throws(() => G[1][op](new Uint8Array([])), 'ui8a([])');
throws(() => G[1][op](new Uint8Array([0])), 'ui8a([0])');
throws(() => G[1][op](new Uint8Array([1])), 'ui8a([1])');
throws(() => G[1][op](new Uint8Array(4096).fill(1)), 'ui8a(4096*[1])');
throws(() => G[1][op](o.BASE), 'other curve point');
});
});
}
// Complex point (Extended/Jacobian/Projective?)
// if (p.BASE.toAffine && C.Point) {
// should('toAffine()', () => {
// equal(p.ZERO.toAffine(), C.Point.ZERO, '0 = 0');
// equal(p.BASE.toAffine(), C.Point.BASE, '1 = 1');
// });
// }
// if (p.fromAffine && C.Point) {
// should('fromAffine()', () => {
// equal(p.ZERO, p.fromAffine(C.Point.ZERO), '0 = 0');
// equal(p.BASE, p.fromAffine(C.Point.BASE), '1 = 1');
// });
// }
// toHex/fromHex (if available)
if (p.fromHex && p.BASE.toHex) {
should('fromHex(toHex()) roundtrip', () => {
fc.assert(
fc.property(FC_BIGINT, (x) => {
const hex = p.BASE.multiply(x).toHex();
deepStrictEqual(p.fromHex(hex).toHex(), hex);
})
);
});
}
});
}
describe(name, () => {
// Generic complex things (getPublicKey/sign/verify/getSharedSecret)
should('.getPublicKey() type check', () => {
throws(() => C.getPublicKey(0), '0');
throws(() => C.getPublicKey(0n), '0n');
throws(() => C.getPublicKey(false), 'false');
throws(() => C.getPublicKey(123), '123');
throws(() => C.getPublicKey(123.456), '123.456');
throws(() => C.getPublicKey(true), 'true');
throws(() => C.getPublicKey(''), "''");
// NOTE: passes because of disabled hex padding checks for starknet, maybe enable?
// throws(() => C.getPublicKey('1'), "'1'");
throws(() => C.getPublicKey('key'), "'key'");
throws(() => C.getPublicKey({}));
throws(() => C.getPublicKey(new Uint8Array([])));
throws(() => C.getPublicKey(new Uint8Array([0])));
throws(() => C.getPublicKey(new Uint8Array([1])));
throws(() => C.getPublicKey(new Uint8Array(4096).fill(1)));
throws(() => C.getPublicKey(Array(32).fill(1)));
});
should('.verify() should verify random signatures', () =>
fc.assert(
fc.property(fc.hexaString({ minLength: 64, maxLength: 64 }), (msg) => {
const priv = C.utils.randomPrivateKey();
const pub = C.getPublicKey(priv);
const sig = C.sign(msg, priv);
deepStrictEqual(
C.verify(sig, msg, pub),
true,
'priv=${toHex(priv)},pub=${toHex(pub)},msg=${msg}'
);
}),
{ numRuns: NUM_RUNS }
)
);
should('.sign() edge cases', () => {
throws(() => C.sign());
throws(() => C.sign(''));
});
describe('verify()', () => {
should('true for proper signatures', () => {
const msg = '01'.repeat(32);
const priv = C.utils.randomPrivateKey();
const sig = C.sign(msg, priv);
const pub = C.getPublicKey(priv);
deepStrictEqual(C.verify(sig, msg, pub), true);
});
should('false for wrong messages', () => {
const msg = '01'.repeat(32);
const priv = C.utils.randomPrivateKey();
const sig = C.sign(msg, priv);
const pub = C.getPublicKey(priv);
deepStrictEqual(C.verify(sig, '11'.repeat(32), pub), false);
});
should('false for wrong keys', () => {
const msg = '01'.repeat(32);
const priv = C.utils.randomPrivateKey();
const sig = C.sign(msg, priv);
deepStrictEqual(C.verify(sig, msg, C.getPublicKey(C.utils.randomPrivateKey())), false);
});
});
// NOTE: fails for ed, because of empty message. Since we convert it to scalar,
// need to check what other implementations do. Empty message != new Uint8Array([0]), but what scalar should be in that case?
// should('should not verify signature with wrong message', () => {
// fc.assert(
// fc.property(
// fc.array(fc.integer({ min: 0x00, max: 0xff })),
// fc.array(fc.integer({ min: 0x00, max: 0xff })),
// (bytes, wrongBytes) => {
// const privKey = C.utils.randomPrivateKey();
// const message = new Uint8Array(bytes);
// const wrongMessage = new Uint8Array(wrongBytes);
// const publicKey = C.getPublicKey(privKey);
// const signature = C.sign(message, privKey);
// deepStrictEqual(
// C.verify(signature, wrongMessage, publicKey),
// bytes.toString() === wrongBytes.toString()
// );
// }
// ),
// { numRuns: NUM_RUNS }
// );
// });
if (C.getSharedSecret) {
should('getSharedSecret() should be commutative', () => {
for (let i = 0; i < NUM_RUNS; i++) {
const asec = C.utils.randomPrivateKey();
const apub = C.getPublicKey(asec);
const bsec = C.utils.randomPrivateKey();
const bpub = C.getPublicKey(bsec);
try {
deepStrictEqual(C.getSharedSecret(asec, bpub), C.getSharedSecret(bsec, apub));
} catch (error) {
console.error('not commutative', { asec, apub, bsec, bpub });
throw error;
}
}
});
}
});
}
should('secp224k1 sqrt bug', () => {
const { Fp } = secp224r1.CURVE;
const sqrtMinus1 = Fp.sqrt(-1n);
// Verified against sage
deepStrictEqual(
sqrtMinus1,
23621584063597419797792593680131996961517196803742576047493035507225n
);
deepStrictEqual(
Fp.neg(sqrtMinus1),
3338362603553219996874421406887633712040719456283732096017030791656n
);
deepStrictEqual(Fp.sqr(sqrtMinus1), Fp.create(-1n));
});
// ESM is broken.
import url from 'url';
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
should.run();
}

1372
test/bls12-381.test.js Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,559 @@
db29a6e1db5d6dcb485f26c174d11dd0ab1ecc54125e31dde2949b03e925ef23::8e49a02dc374bb1702ec4c8aa514c6d72e0884d3611334ae749462eb545a4d0a6086ef0a6b1bbc5db2934d297bf766c60dbe665dfc33d3aa765d071712075b4102a71f518e16376fc58c982d30a6196fdd8319090bcb2728c9bbbd8045fc7863
28b90deaf189015d3a325908c5e0e4bf00f84f7e639b056ff82d7e70b6eede4c:09:8647aa9680cd0cdf065b94e818ff2bb948cc97838bcee987b9bc1b76d0a0a6e0d85db4e9d75aaedfc79d4ea2733a21ae0579014de7636dd2943d45b87c82b1c66a289006b0b9767921bb8edd3f6c5c5dec0d54cd65f61513113c50cc977849e5
177c50ec35b1da25f68b9f7bad8f1c443a01fa2b20de37be0caec8b62db5c902:d2:8166ef48e7c1de5f8256a670511aad114f956382eed36f3bb45a7a1bf473e982f0e399924b4dc92795d043d9475402aa0d065e10f05a2026a0961882b6c2e6c6ae429edd17c7a43586a814121044e41c5c1d3397452b2fc61fdc523cc943ef68
e99d0f7a4f8a9e3f74a6bd9677b3fd5be32f7cea4e5b898ed3dea735fa647632:0d98:a70b42352845ca47713a20a243ec2aeb63bab7c4126d9cbd390b6f59372de383b93698c66f7e75452cbc569147b1f9f70be3b03539a4d110c65082f7918942b962543494469a5271208cdec9c234689bcae9c88c7cdc3ef9eefdf11a522794c6
2c864f383cd664839ce6d7d049f8083870b628d7bb8a4539cc84a35bbd723736:4a35:88c76ed5872fb011f4fc0fc8ff2751513dd6f22a9b7cb125ed39bdfffdd996ba1b5d9ea6332e8b4c34e591cb9dcf4efe112a096754db0d723269bb1a758f986d5e0cfbdcc22f8678f72f1804459c98be59ee7632c666206255dcaa816188cf2b
cd8ccf5c229e37a4a4b5490c84882af890cdc4c70170ad5f0ee2f3a86ffa3080:05caf3:83b4087858c3585a2f978fe1c79b94d9eb927500066e6c793392f1d5be2eff1ebfdd893555f6a89934f4e27ec810489a07f0740124c065fb82246a6d6f924f70069edfaf04fc4d1021f3314b9d09dfadc344fd2dcafaeb09586b194b3ea73374
43a5b55a3823acebca9e55face42697b5cd0e2398a44a56ebc22d936a05c3893:94b0b9:8b4f817e0815a9ca1fb91a5fd9e88714d72c2d6b4ea037ab43cde4149691109336edca72fb55936b5ba8f4576ffd49780dde24d48f9a9d5a07d1527ac3a9bc38c05a0fc58bac710cd4983c6cd21e56175a6e597954ac68207db1a8b86efe84cf
e2654a38c0148b5f0de1b8266737fdb6706fedc436d5fb70dc34bebe25b43d01:06a63bfb:b4a224ac879b67d20f452c40adac0b993560842e4dce928ace30751bc38d2a3a46e1f84dd1c04c0417ebaf069f5618f210c0dce7ae478f83a664f231e141b3fc677842bae20df43409476dde8c6a81cd1067999e6fae2896afe9630bc7441d3a
736057053101fbb9240808bfe83121382a8cd559a97f8633ae170b3e1ac9efee:0d83b078:b028a3c5877113519d0da87fd67edebcb80b37869f58505997c459fad745194407e004503fb49f4d9e0ea289d5cd9c63151c2d40e09cd3988cf7b0191718d4f4d07fa149c965fdc4a25fece97efd72fb9337099b5f47979c6acb9b30f0c2fbe3
dc7f613ec82e641c58d8390963ccb4cd307da37417aca9eec15b060fccdf73f3:0611983835:b4d7a115e96b4da4616b54bff471d5a9f7002100b61214337662f0aabf08a45ecdcdb581325cba3b5141a4083c301dcc10eeac2da4cd430ab1882e44cec5f8f08a789eb670653ab8072795e3d5724405bb6ffebde2e73229b353b17119f46e9c
ba4eeeefe05a4515509d64f21639c24dde05122815a9d89f1d8d31ba90f7e7aa:64726e3da8:8d6d9d1ee5febfb549e1f7a2f3e69f01918fc105ea710bae881b60fec8dcdbbb2ae3e00249d5b82eb4c598f557c139e00bcc5dd098afc74303a54937a81e98b4d2e75ee792021200f291918980ea274ef8ea2d712e265089230042348dd4b872
5737b8adab24e7ec10fdd0a420c603392cb2c43f6466d6033eb14d1012470dbb:07d6e7a31a2f:99d2bc134b268397759e77b7f59c26862264454a659b90664cd7ff9fd5701b083ef6aee3df959643816958515059e42205abf0c959ea64910c3bdbb447a1afca4caf271f7dbd50085d886c2e3f5294167ea3631767acfee5d89cb6e04def2dd8
ecca05ab2c52c1b4861357c01d628a8a7b0ca0b6c72219cf47822e72858752bc:aa07552a7358:9023090506aa3ec0b14b0650d302baa54976e26104d011f3e131a066f3f7dacbd8831e4bcefe5702e4c83323e4e6f7700b343a29e7d4cb7277712c59e83fe6ee788967c4721f783b81024c8317b4042e0f4fad3c41fd861b6050654bc529af70
b54f380ef6485006e52c279335e9aeb7adcf1dd24b02f6be0a25b6d0a70627b0:04583025f1b7da:897da4929a4ed71b70ba65e68f39f6f7967dbecbb97b610bd3ab23f6a08906ca44661523212a598f9a1e12e128c184440d1a56c2b64260c871e25ae5135b53944f7820aa74d20d48d90976cf60090427a442fa61e20f45ec6d0f348f6684fd32
7865bb94878bac2960a60393588ce3c0e92d6d8eda6c4ac2da5fd4d2fbffa48f:9398f8937e1821:b8d60310a2c5cc322ed412f340c3fc05723c446c47cc529f7e914916a5cb7d78e8af8400a6d07d62bc280daca12988cb1073a88ea7b4aa2dff821ae011342aa8a8de691ddd370a8ba942fd5cc6892acec84db889f6421bb1d71f02f5b7830a0a
97ec105f9f2487fa4b360eb484c4398add4c1fe8e274853e530292290e9b7a59:00dd6efc58fcd4ee:86c7fd60798ddc58d59de487a1ac4221e61efe70c188e78ac50b4d89ea022b592e472895595aa4725c9be82770cc05050fb0feacd6e1a3c062dfed9e6e63ae58838025cae35dcd8b1eaa90a3714e800b2cd0740d7262119e7abec3b5822d4497
8e9b0e1280b0433182df7eca995a7279e704f728a88ad0b26518ed0a19f62113:1b3d4c68e007f478:ab1832ae67d695f941decf959ebbce93bb33e70d60dfb53321f582f81be7d4177d0aef680c56a587e905c051355a78ad14574e0adcb083e2d6c972dbc54b68290abfc8c7abb9472ee210078eb62a3148764defb967768ab5be36d36c5aaf6455
281c1297034c8aaa2ea6368dd038833429673b99dd4da4622908de8d9674f99d:033d69a90f6073fceb:8bd05333f0d076558b8a25a75b76b14af7454f69452918d363a905a49089663e23c0ede69b4882790178a02ff83b8a3d0c945e6ce0e18c5c3abfb22c32e2bee71145fd319f4daef46463471df9691a372a8f30427c427fb025155109269ef363
6fbbae45973a06226abf29b6d978429cc2775b4af6c736f6fa1eecb042c3b089:b28bfd61695d25590c:90f9298f281b350add0e8b1789e99d38a42021ea971d1a0b1a28f8d11906602bd0a22eeee4b149443541f149721a926b0ddd2a496a88ac47a9f59e0a80aa91f88baa129035d2f2d38028ab024ac38ec0ae1768c1bcd156c2349a26e01b94a516
91a60d4b2440622e10f5ba7dc9585894b2125980fd5072cec4bbcd47ea49c57a:00581bba4bc7f26227a7:a95a631b6ea74646d9659f0d27a1e8c877e4018d47f81a32d0d0b203a724b980597880a84cdbe88cf41be05a58101e7a11b6de7b9bf7433e6c947e571b595b4496fff37f8a48d3eccab904ee824b31dd984c3f6c2a19acb52e895f1e326e788e
b211045d27b3c191a7961f521dedbb6ac8f6a362ab19ec28969d583c9a2eb128:e02c048d0609137492b9:a1e3e50c7a70c9d1028996c5bc79343c5780b16310a8b930704d3107e93bc92f7b927393fcf55d64461369f95aff557d0f346db8d83d02f0d914b50b706908cb631114c279be622bbc801bf5bd93245827a3c7ac06a30fa88e579ba2c5adbf99
342fb513e70e2a7cdee072a9d8c1cfc86aa9691687dbef91fca2d15870e4adaa:011ddb2e7d45df2e05fb8a:96324a72624c99ae74fc334cb8fe70885d7e9c840e4608e5b19a8a5ef8fe68277e7cac7c4b6889e206f6244dc49fc19a0a3ac2992e51d0f0076a528fa6c11afb4a3f127286714c1548a3c4c9eac7e67eb32bcd2c480e2c7d36504a5fa52d6ea1
498f8139fa03ac326e5ff321a9c8083d22eb048de74fcec128de3621a1dd5675:64024e14b436cb94e3209c:8723e4effda2455dd5d552bcf6116f88dd70187b031edb7660fa673ce22bd0fbe9ccaf28be6d9c78b783fa6310a0fda00b29cbce1adcef4204d31694bb47cc84de102d2dfab833e907887bcc8371119244b5b69b791c9dcd667e899971a37295
e5bcc31a88b1d23772bb5a282b8d8f7c0dd645b45b940027c1111ee13e62cfdf:00ca1d50caa232ab9ad64644:ab1e90fecee91a4c12537041281e91190e8bba02d503c491da48ab7b2fc9af11b01b168c83154c04f59c3e6d815665d6120d2547f7fba57d43ad9993c99e54c0b5488166c41416e29e9588d6b7e6d67c7d93c9be7ba3c7d769e0ccedec71b3a4
e5bfacb0667cd3db0634fd1eaa35b362d827a205db6cdce6daecbd83413ac6cf:3f1e6ffc1d7ec3e890d52615:ac3dcdb2751d982d80b77a749cf2f1f698f4dee8c9007043766c602104cbb0f633a4ce437098775062bf64aff77b2d66071dbc770e6ea04b11809f24ef50e6ec3ae5a58d58896bf9e78de80194eb67ce0d44e0232014ec300b63f90ef5c335bb
055873520343a11f937aa23abfdfee0d1929c461bd2fcfc85d8d07ef44230c69:04f00076b77983d6ee7ab6979d:afd0d6908f62ce579dde390b463d535dcfd1f5ee6664d46a583952c50178a61f15cf072153a21b4e6044dece53a755b1025170d84046c57252ea6c0a9c45ca42b0c77544e80856634e552c1e2b86eabf1f92535e1b27ea43281f8dc3a8672211
d58dd7364d214cd9ca9a5188ff8ad95486f65bf642d857c902fc6fd32a843510:49c83ddfa5b465abca1a8b5c78:a087f6bb963265cb95ff4914d4bae832a4fc89ff0616bcd8a529badca39a2c85b0ff3e1c418ec7979bcaf93a6d3dfc35108c420956c0795bdd81d2ce6f53a2f0a0bc37876db80bd0b59ce2210404a5c84d16330621aaaaad718e518dff1ef3cb
13ed1173e146edb9ae6a77e289155354d6e0107aa2fcd4dc89441619553448a3:07c50e920e060f06243084d32afe:a89faa2c99e76386d80f709e1af822e830385d07b0cc4bf8b46315273f0b11c9f77845e431d5bbb86d07e4b0e15c66bb1958e8d0728a0a0961e5f233fcba72278f5b0ffbbd67fbf62aa7b7a1572d9dababed755a60d37906dc30bdfc3d9d4c2e
5c3f7aef904cd9aa5cf093f993f49d46c3f20b7450552d52b23569239849f31b:6f455d61ee48d1990ec61dd70dbd:aaede5d58e5c71c170b2fbff350cb596bf43f2d0ee6b0e83f8863350ea4fd756129150e856c83e45734db8649a3617150c4a7a37ec596d89ff6cc6749dbe6d54f41cb36662279b3a82335e3f4b946afd94740e3214e93a45643bbabdac93a25c
00cc17223e74b58d2e465bad2f5d0ecc5a85081d143fc4b8ab8eb10b44c6c45f:0800350f624966388e327b17cbb9fa:8333c0c7d9da5f79c67ed5fd13d92bbc91c5680837cd0cf816f2876106720a93323477ba28dbce0f554ffc0f37db9fc20386aa12d005be7a2ce5a660ef8ff46c7381bd0446cabcc4a3f4b8b75b9608615ab2497beb425415c778c278f38a6d81
21fa64295c0fc47d81f69ba31d4661604c4d9eae6d2077c094ff758b1757e3c1::845c89e3bc5350275cc5b2669bda8ae127b8bf731517bb1bd5ebf0670add6df037f6b8c382aa03f729c5e8049e817dab0cc310e8af80d78cff447f034d32e7e7a1e8f27c8df390c5b640a7e341d6ab94311c4ae35a754f3990371c9186f97f0e
dd7466232c0fc5581e319721d4e0c7b773417432b0a0b756c3dc4e5cdff4723b:06:95f8a6c56e2de4742144e2213e13411f1a2d03ca783e509353276292fc97e087062b3050e967593e569e6ef80741adae0c5a7ab5bf0b0f0e7cd92d5e17181176ec2fe111e8306b9643901e228f0187bc9d429ca8cbd23a39be4bbb99bf29d84b
fe7eab6ef7c43c8ced6e9b7524667c79f129eda2e8585328fde3b18b4eedddab:57:ae257f78c59769a67de12fab413c4ec6a6668eedf45c4bf6d5928f05acfa77cb12caf36fa9e42830519dbf816655fbd012472d84bab2d4f8c99d684f9652675493a4436258a881f0e9e27558f5125f614e38835a87ee0ee61dd7a946a3b13996
93cc9ed7f1507a817608255cd036d6b1d10a974e7547d7100be15f70702e3e4b:0b76:8792277cf0d7d0318174e23356cd311a737169b08f52c65fc8ce7af4fc3cbc5ad75223d15f43a2caa3862c6b4ab39c140f2571804296c90f27f5ebdec3f135fc458cb1a441c4dcf4953273cc77947d604ceca8c10dda4f68874716f2684cd52f
9f388e9e5e98b2cdbb28cfa3011be14cd658d0395d7c737ccc50679dce316d88:0c5f:959299104d5905f91d081e1ddeeb1ef4dec0013d3f961daa4e38db5367ad81acb657021dd540d96920188f1f1ad4ba39163fc9575652c3cf3bde0bd7533a505a7282777fbbae405e0f967806b4e903b9134ccd5fb5d090ea66ff26c69a1f5aff
ff060b6cfcee3546fcaa93c4867bf97e56099949a3eadb4c4011ca544000c1b6:0f5459:8b694a9f0b96aef6766ec5d4f471ada3810d30fbd84fd12aec8a9b579568b27a4b21794e7c545f3f9d86603a51fed940007bd3fae9bc793b47e1c5ba2d18e7b39f75d52b0722dc60d537a9c437b28d766cc39cc7a2c0747fc4af09d4adb208aa
8d750998b2143613209a3284da5d9bb2d63c5b7504f0ded55cba58d99902ba68:d626a5:8ab12e4ebbebbc60760f61eed87a65fc221abb342fe3909483db1dffe132ab2cd208568955821e7167f29bd2fed824f309904e2e77577ca6cb8a59e16077cd2847985a083838af48b0820336cea6011c6a7dbdea166fc3a733a8941ab53bf3f3
4f35aaddd4587dcde04341ecfdb62636fb140bd363738c9dd8f53b45fe740d09:07d29e93:a20dc2b369dcf1f609e24f795ded8d606bd5931a2f3e8e045840eae489da759f4e00a036ac2eb678c61fab587dccb55f164241799be62a63958df72533fdac653fe23983ece98119991c3a7e7910d9f837ffb3b0579c326fb46a5c3b55527a09
a81a2a4d890e39dc2ca7720ed6c301b1ee0addfc6a609d3282cfba541ac9639a:6f3b5edc:8c32cc120080a1b17b342e64bfecd79b45fcbe71a27cd1037135eb8b97326f971b89d84c03dddfe01249e5ac3472487915d7055b6b4f06177dfc5e66c88c2bd7c8929153c335e0afd0f230a97cbeeda238df6a31825649f46bfed3985bdd6180
275d76e250f6f44585d23c95f9984ffe762afb7bec52906657ad4ffdbf264e7d:050c5aff03:85ca53a9b9ac376b09c3538f40ea50cd0ab4b16b205fd434befc6aece17e807204ef8333a63522a1df41b4c8f38a3d3c086565227513ffa50e31d3bcc9870f693d9527d10e9c26386dafd48cd8bca9e39363ec220e1196edd636fd2248ad113a
7e363965c6d3a02c07f5b33b37617dc2b2843bec249968a7958ad55fa64fc0be:ea1a2c2541:ac3dd4ca0e6b6310217f163d5c5dbd771228febc96aeeef31bfb605431d1a23e37297bcdf5f684a2936980213497d561081c38a01c273dc0f53a1b50bf561e9fc2c7f5e0d55ce7537ce995addb6b20caa30e3840952a5f14aff90731c0d7612f
697ef015b81f5a3c31ba3ba4482b1182f2a106aba7bd94948a073861297e9c4b:0caf34f67e39:a5aa61367ef1f50257c1614863c9a41f493e01487e522bd83fffa8c2d7eac5656ab355db421e4a3e9c6a325e8fa8ae4e05875a490b3baec6529799c8f65efd7526aa7e9fd33ebb1e7506ed36012b35ffbabddaa63748cbed227bebc2738eb647
986e36b32e2a4cb454eb84c8e135b1568488d57ad35d5c2e8793572bdf5643ae:bb586523bbe7:af3d68b8da23dd29ee307bec34839ba5117477178ed2d752339dee1e5dd93199bd4c05680400cbf6b8553db1bb3e0f280aabc043eefad31d7c575947ae78fc4dc672374e1a8cc5cc04ffc63d0b1a53f4fe8b39cdc3c111085cea5f8456121842
f0b6be124d8f563ef3411d87a7c42796a5675d4da1e4875d25b523aa772d48ca:00377779a4f4b7:85088daf12e34adf4f2f108c12b9c20a938df6923d3a1b94feb2e38a1eae6dbdcaf48e464936583ecfa12c45c116ba79001aa41ff2aeb628659cc0f70db863a9820b81594ac4f6051d64b5bcf9c6aa78fc5c81382884783718571d08680f5209
daddd3b1096023b2435a20a435aa827b80c8978f827dd1d845078b5c197c769e:8c7f2a39715ef4:a61be5df8d598c6ff2785fbb9bef821a688b7b79b429a1897d4ac3db02705572694305a423bf2621a866cf7a985f810011201783f6154c4108b85234e2ae9b1a21cd4df0f48cef1d93a539fbcdef3fa001dac9be7643f37dd1bb6cf5e67e02fe
930bc625ef72fd47e2a7d60801aefbbd98f1ad56353deae4b2e4830833f8c77b:0d290ab1a4b5fdc3:8552042e6132000eb3c44674e8e4e71c0f097bfb13cd367662a150b07fb7b670e166f738a7819377411b5eb5dde0ea950dcfd102b3718f84b071f14007560e5679ebc006fe76432e15fab2d18535f39b3fbf00bf7ce9572e5cbe5694c743ae71
41dabe002f7aa9792a9f04527048159e6f1be97c7e0207ed1566c4262a12f789:968e85352fc3df11:8962a7d1541633a888526a8d3d010280a7c6c8c268074ef489019a8651ed0099c1c2ff60a99146b74a47f4c432d5d04a08c05fa9be0e048cdc283da719dd0fa1c7032c8353ad6846f81dce05f92ea083f75f1fd09ea67462bc8c8d46943654ac
c4b3162757f21d8b57f9d701d416b5bf90a709f55557d19a8fe72c8bd4d01fd6:0c041934df4821a898:b88b710f7809f3901df4e877f03153f77fd1c41ce44abb0d4ff1b546ebaed40b5a80a4e383655fb188c38eedf2ab962c0e1b2dcf7bdcc781b21f1b8cfe0dd846269207a8a051b53c262ffe1c14d1d3a3e07d471780b638804219402e844e3513
b786a05a2204a23bf247986914e09e60220ad345dfbb0082e31290b80f348dd6:29f5877b9dfe13722b:b1535e3fb429a8012516b23ce79664707a0a99a824ffee7e226090b0c41bf9005f2c72bca703ca27694adf585e9c7082024b8be701b5f9541f3e6e3f353facd33b9d0532a120fe029f4fdad84ccf1bc92e5a01995237036304bc167aad541dd7
7a7a26440695378207f8d039d198c0a8bae7d174297a69bc566a9453e76fb2fe:0d7d8481e00dffd22bf7:a3be6cff0f954ce3c20feccd23808bb650e2b9618f3d9c52fb173521ab89b2654af7f2ec103e0c6be4336571ac203a2c0b0189887c7b06cb5738d10fbd84865dd24ae555ba3b3f46adaff5d3e99e8b417440f5d377408e05ac69e4b2fd690b84
8789e7f56b38b17acfe2532a8516a43817a2dcc2e5037718171fa61c32f9b294:f495212d34a5343641b9:99bf5bd4cf4fa381db42f5bf214424709c5c1b282bbf63e75ed1d5420eb5c2f1eb5ddabcdf6cbaebad66b04f7293a0280f13a53175e02e5f6801be278fd2639e211aec2b268cf95d1b0300df63a9eb051c1f05d15f6ec1a8c5588b3682c69ebc
944922d8040d748b4c77a9779b68e0e19816845e5969715377c88c760fbaa3b5:0a2ff5abcb19784fd8b111:885761f8d921cf5a6cecd94c26619157d2f548458ac36f5287dc205fc2cf091592cfa48d1e06544bd9bb04fc938917ec04d7df0752ebb314f0bbaa29cbe12d84bd9528cadc71d077497bfb04e44b64d2e08ec7630490950329425ff6f7a87eb9
27d3382462061f32036e9f25746697298c03ac135eb53cce40e6c8d0b61a38e5:5c3c39865260362265879f:8e234853e19f022389dc7dd7240798c45f81c7be9e650088d380f3498743f30c961e856bf9579c21f280061cd087609b15f91f02936e082bee165690c5e2a792d336bbab9c6bc5f27256889953744b81d685356cd6f23827aed5e8a9a23236f8
1e13a5be7f36189f5ffcf35c210de27b6571d64aa3d00111fa0831f48c4fa165:08ed73f10fd0e2fa0ebe1932:b1aa7f5fa8af643ebcd8410b580fb93dbd2ba82d64bf9a802846606b39d3a4fcd651f80736a9f189694a6910b41ab6ef0fc2b90bd374410feb42496002e435f6c11b18ebe8897d8e64630a1290398bd11d36bf83e398db9932bb7e0e94cd54cb
9f2048b10d757490f72ca10e0ef4b380b57ce15afe83470e6109faca7b847921:ce013f91232820a5a3c4e275:98c52bbe5b1da89a930c4452e5b01f4359945d3e96ebb655d15fb8763a2d747d52f2628abb72459a302dd6a017ae0317031e99fffb2539ad6432ab1a09c51d2e70c0ce431e5fb53431ab963d4d23687718847455b53e389bd6faa919855c716f
6ce4f45b393a54d6e12064738e740c870fc3066655827af9f071dadc7554a45f:0cb23a6f4693b24a16a0e5d11f:8a5e73d0e38f3e8786fdc7e2fb313b9a6b34ea7658e1eeb63c4270ac1ed80c42f22716283843ea29f72c599b1b69edd002903b75545342fd3f94c5633216cdf4e7a39d3aa146119f5c6fd555c359068ce15a3c344e18bc631d1f9ed94e266e2c
83db8a3fd3891ee06a9a4fca5a694d76f78813fcc7fe0a1b19f1b86fb6524b7e:fa1183015e443acc22d5df8131:81cec4d2a64afb634e02b3b4d3986e1c6a360dfefef370224afffa15be78370f775ff0e617ad62fc21624275b84c3ba00f203e3b60b4de91b51a80750fa1aa609b99f49ab9fd638caa423bca2c0d2c9da90988e189953af2085d0a763c06f7e3
80327a9a08e410b0749a2bf141579799e8cb9dcafc3c91245f8e8c98653b873c:0acb153156dbb95b071e3c79602e:80764c588d161cc35775e811b17484d1ec138ca72fb7be6d382ba0b0674e684e6dce4fd04c25db12520d3fd3d5e944d50f6bf07a63bbef2ade761de3c0b210b7cc9d2e95906d889df312ec062e67c703b56f5e572e1f808c1b57ee983579638a
7d0de3987f99eedbb2ac0536ed6ddecff5600ebeecce340a9d5000005d8122dc:3353cacdf3a5d86bf7610061a6fb:920ccad897aec4302997390e7150ea9082782428be08ae4d0124ecc776aa458bc913a47192b3a02055527018e87f9c6417ed3d9cc17e813ba24265eb4dbbaa4746c62adb1e2d3c9a7a4d8c2dac4c6a14851358c6eb8a4e19e785dcc5b533d752
abc26c442a73f260a1f7e41644a9904a58d47a50ff545fa5a5bc3b81dcc0e312:09fa75a09eeab060869233747305f5:83c41f6246d829bf19be3accf362e65bf102316e4882f57df3b8abc06bd9e0dd8080ff807326c10443383bda8760c252193cbce2f8ecbdb5b278413ce8fccb20e25bcb036454b7621bf630ae988b1652eb9b686a428a2d7ceefd9e18322e5a12
330d6be474e54d85deec0aff3afa587eb4c5f803a94077d9ee86bd74a034a40d::a8492b918727dfe480e954c52126666872e243489ec6ef42d669c4483c3d09c83d5a49b8092685aa3410d1a1fab33fb6004c07e312df7faa3e88a53b414dd653fe4f417ef9450c7220a02ae99936a0a18b264ed1cfade17ffffe4f6c0b334dbb
fdf519fe3b983dbd660f1dcc7c53b9b4003c54b0d4815cd5242c4b61bcaf9b58:02:9396ffec586fa35dc2a6659729a012f450bc83468900e3de53295af31cc8c6378c2ef7bd4a02e4417bbbb7c5aec566370aab2bb3681c1ba192716c90a1026042e648ab3dd6437273aabbd7285b77a2d2e2b0dd8dbc260c347a90e005b7dfd412
8eb2ff351ded50e79a4bede161069ac6025e6e8cd0f081dd7af5228136c24e9e:d2:b56374f5b8a57d0f81013307a603785d54b6c2ed49f2f838901f3130a9569c8e0195a3490554c87f6620168ba1768bd20860894ba866c71b5769a6087d63ca3638b75ebae6c444fc5c52c317b5bba76ab020f55f3d673dc3aa6367889ea02461
9a5eae3b2fc45fed7631c3c65e4d6668dce6f1eebc40ca19ae585ed1fd226ed2:0239:8b90f69f6e3db9277e4a6addd63903f306a6257752895925fda34253980aa0315cf221a67fcbbdd5ab1ce372ef5978bb0541117dc8fc497d6b4bfb6bcbff532971f8adae492c4c037fd5bc00d145787f9361ac005069873fb0cf102ad8d69660
d20f1f3e536ed11959af2da39840eddadcb6dc50dd473c805989328c032b60ed:67c8:ad40bb4249dbf738e6fd8eec7e74c8a4516766e46b12c116030c4da28c41c2a224106195069bbb6364f28a8f8b6973bd03ee5a73e8066d6d27ebfcd88c47d9d24416f2209843700c91938e1c3a5d33e5245c7f9280a6474b4397d0af01a15f89
d8ea32d81ddcbbe90a2f3df961c723da168001bda98d68820a7d3cb42e325ef4:0c9be6:8f956ef6e12de0b6ba15be54c8523ea25e6c9b94ec91326b9d9bda6a529c239e22888bd01f8b5f6ed13427345de3dcd80c9919783447a3ce70d261070cc0a1ee1bf3b3265ca25eeb39e9c54c32408f21a96121be5c91f4a36fc3946eabc80640
461fbcff221f0655c19f9c5e1c2a5437c8842ba0910a983be007cff9df19f627:92e6e8:857d69d8830474de73df377034c57e619c954041b80b6c23ae769ea5dae40a5d42d8f855248d4f78c8703970435042110b256d1de0c1061c05e5a544de326035ece92e7bebc665f1575d5d8f0183303a8a7e1478a6600db96d47eeac05ef5645
69a75aca260d679aa24f49481164930b71e72fdb5db887cf484a6db6852a72a4:0428b032:91f8b2609fd551c13eda568f103f5be9f7b2a2b42e2cf263da628c6cb735ad04d436b878fc7363baf969f415550d02800f562e606085ad0962be2adbe8c617b36a418c31d81bb10577f0e3743be8f39142e7fdf5d3637a120dced8b73ff79262
182733a6720089b476c298a084a30a2a4a717b2a16ecb3b7be894b222c3ca5e1:26a7d91d:a39166d0499752ff885611e994ee3d39f8a9a6d734327ff43045535cfc882e3c4c99d62bc75da26474981013fa5214d6068dae1b90637e8ea5cad091d364badc6934288339c600059124d347a7a09b7a674435e1c7ba9d519d6b6d0b04051700
75aae7b43b7315fe0ffca6f6255f62a815a83226cb16bfd12436f8599a59bd80:09abe5ed5f:ae30c591e798621380b1a5fb93b12e5a2b56bcb8937d3c78f67883a9e941dd301871ccfe57273a6ee6e5f0cdefcaa07b12318afb8d46c3ca85485c5f406e0501c02a58cd72ca766eecd7000fb222f12c95ee2c3d6e3a600bd205272dad25065e
9c7b850171efc1bcc5d866ff6533fd7cba462b6f0523a223fb94f857f2ed5a16:42ead34e98:acec746ebb567eefe291695266d131cc81044899fb1fd2fdcc04757d8466e0e93bca13ad47364fbc0ac143c086a674af145ed5f793c50975db8ef9cce33cfdab065d6b9fc4c6442483f387f0af911933f789b00dba8f39f8d6d6fd3f04fab3f7
53438f53eb749aba12412357262d5445e4cfb69ba645b30a9f116d6ec45a1994:0251be974654:80480c5941ae1fdfd3ffcb429a1b9928ffd08d8a759e5284dab6b5b73724b50bf76deae9d2dee2bc4250ba65feb9de620c2d7691e116476b81facfc873f8842a27c3de5f0730b480dabe0aaa4d24bca4df3564b71090e280191daba77c607089
d7db36a4a43f7e91f27282937a4280a1eebdd67b18fce8b4ec5f581f14af66eb:aaa6cb92433d:8cd313fdc1ee2f5a15e1a66ccca3ed4236cdecb69951ef56cda437da8ff3f37288ef6a1e9756cc1dbd8718cdb060a8c8023ea48aae987d687486bc9c677073fc62526da457aca1b31fca39daade9978318ecc3ecd1273f883d064927795e3225
e557e1443650a1ed64f17d9913dfe3f7ea8fa5b9b22b4175ac69d02faaa591d8:0020f39be6abdd:ae504f56ea36a8dd6c5792fe7ef70bd5bf61e45178319c85532bfd27bb775b213e1ed04a3c4ed087e95eb0880c1b11c715394287d1077ada5a3c0b7db4954d2145aad6b74ddd117c73f3b7850f900992e4b66590cac34e9f67b92eb032458a06
566f89bee9480150bd9cd346e9fe7f681af96562ca82a98ab9be4cd319401a16:315a32bba44f89:aa8a8bedcb31ea6d67957fc7e09c336e561f63fd085c84c8bafafb610b961921fe1aca3c6b7c2a104fe84a72fd771dc20f51d9c28cfb7fbf842dbc9343d3cb4861f187c7ad02032f3a347dbff315233c3b2c12b8cae7dc40ce3bb3ae0cf28633
ac8093326f925831b5c813c0f36749ab6f85f8e3b4a80920ca524cb1187ec8da:000268a1ab986936:a712a7352cfde24ae04b0187cf7c27d24cf993f6d415d01b8d4bc8d7f46d1773575038d0363dc5f1b6c61b119185f2cf13de4c13b42c5fbe5257588ba386d3ff2992d6f7f3e3c67972565d70ce7894bb9f7304969e2e9b7f7735f1ba9b2f100f
e014a2800cbb1df2e42dc40bce9b81a84e6ec7f5ce1c1cb84f6de463607f58f1:e52a2f5a63181f64:aa83a49ad0dfab7c4683bf8363170b836b5c69952a20057167799e57c154de91440fc3da9a9a651fd0e815c583af9e970bd900743a2154145a4b5040f4a5e8b134d89412d9c2113dc9151e4754b2fae7c8a80ad3ecdca6df31b3be67ce8a0f6f
6bb60100e45c2a7601a1075a70a8f804eaf016bdb2cfb67c76bea74c49c6a99f:07ce73a170e9e3b5d3:8c87485bbffeb5033466993d3924100696c90bbb6cb8a7cc25e92f1e72fc93dd65eced2046b35165278a41c75c261a310590bd11ecd4b79be049fa201c1387962912dff8d309a9d4ffb0a192bccd3fd51a43edb42447d86162e6c5e367a0ebb0
ac514ecd85c4bc9c7bb5dd74d675a3d7e0ac5725ab8707743d69a4a4efd0d463:e479f849946dde9711:b8f4e70a28a300bda27525092ef417aabc44b0965a1b66e0fb9d0d85283d34d756ee28f5b8b0d4e2cba31a01ef1b97201603e8b11527dd3d59c04a1a49e6aec69caec7c825f32e8478ff685e7626aebf49972957da93c6e75328182d27e28c59
0c5e59158036e95517480734dd946ccd840a2ae0d7bfd04afb561eb07b651f65:097fba98b1044aa38ecc:8b92528bb299945e6eaa1c1543982b4719ce6ffeaa46182b31f5fb12d04bc52d1edd242f26bbbd1d6bca7df14a6e713f07047747eb44048db73e17dd0cd86944525641e439686c0d926fa6aac347956b76448f6aed53cf5e150b26f62697eb0a
482f5eec6aab3190b3e85c3358e906470e29f5028c1c1ff27334744a5389b046:f1903bd31ed169bd2ed5:b1bd64412e722ff2568737f965458f12923a38d94dbb9d08413a0122a993f159a4e69322bd142ffba038abf617809f4c183d1bc8397bb0d4e755854b06df29be5290b068bdaeb6a3bf66af947f9de650fc992b24795e109d66e1c1b8597c0e4d
6cf0808dd7e4aafedc33bca4bdc5cd2ea5b199f09e60f818874d06edd808b711:053d7b09d32505e7f8a86a:915bbe14cd1bc66d46c572217d7834da1396304f1a00964717eb9fd078954db38ac2d3cfee23a2cadf2e239cbab2dd4004879ceaf6d2a0484284e8bb939e707f83166527df1b7f07f5b55e987e400183e051bcd02eaca19c8a825405f25a5fa0
516a6fc917c2677b196e831913c67137d99bfbfcd279dab6c8189b8235777958:6244fe979e6507bf1affd7:96329a5e5c8c2f96941f8281b85f884fbf36732b9c20a497dbf2364ab5c885fdd2ea901a0b8142965643123b9959e6a0032fab81535cccec3f89f2f5743ab18ade1681b339dceb146a653356b676ba5044fd338a313edf9a1506c42a8eec2ec0
db541ca7b63dd509c46a82844abab2e3fb63b41dc6f0aedd012dd5ee825ab336:0605a5861b234595538a364f:85f71fbbcb7e1bc833ac68121c3b255e92d54bce8c9f3daf4a51068f9f8ec830744c3fa2e0249a729724edab8d91370104d00bdb4e517bfde3c37ce445cc0a5382026ba0027f4645949b0a586dd12aeb8a4dc82047ad2e876faca9c5ed4b0f9c
cfcdec94ca11b0a8964477d8f1cd2d991d9d5378ca7f03fb7838e85e013dac91:86d5eba501326894509e34a3:ad8ed6d864bcd3247b9939905d82d17623227754fdf0242de0abfbeab1f0560982e353238dfc43d654753908e13c0338073ad74b329a6058a89ecec84735b123cca657f9cf9d71d7cb2f8416a1599a275c848d6abea10123b27f3334aea2a94d
64c8a98a1d399e67005062aa67492603ae501ecb1a954d61a30a4ebe4d161f2f:082fa093ef30ed114342696f1a:ae8d8bebf524ffb11e11ed6c29cdefab488749c11c2ee5cf3cbb02731d3b936ea11f4e41902d29cf977e483a21f398a20d904494d3ab7305015b4b8f6032df5240b29278689b72fb4a9496458879c00815fa3e29b3b5f3df78765e161ed3dc64
2544f3764d9102fb80bb5fa446f4d9b9050e229f42e92cd36b1e17b8f91c850d:feb1f3d7e800a829cca384707f:afdee7bc926e4df643a69a431bfc91aadc659e912e83fc20a7d162ad675e37d07e95b04ea17ada1ddecda83799c7dddf0cc95db48879e6f694d7198f4d0637327df4d5d29b6542b71bfa773f66e6d518d8c700b58a677a089f82a1a838097ef4
c8718d6afc9955bf189039c0e72adce0fa342f9830746a92092a7658835a0282:0a134902c0656d0b86458d182718:a2bd54f4fe5a95d32dbb9bc94fb4ad87d8a99d89057fbf571c7b1438023dea2d1894649fc3f68c221d99f01f9b50d5e714ae03b3fbf390ffce54b3a12041a664777a43f96351ee8988624599d07b2a9fd8b2542d136442a106e3dab3f04c369c
6004e1aea40b85274c75b056608d372ce8f4fb1710e8f5a6ba6da20f9b559605:cb80b0a3621bb0528735ddb062a8:97d80bd51856d3efba40917d7463dcfd49411a00951b19f35eaf21b470fcbfbec7dcfabc4542146003ab4c691ce565670d49c7405f5cb3e40d4cd13808738c5f9c5da520ae8c8d664af17bdccc87872da5e314dc51455a7816e197dac9f3c37f
aa30b992fb91f3e8d07f7cf58356d84b694c454086c662603f950df1280f4175:01beab7f3bf92b1f7effa24f8b5e9e:9233f8530094e2f779260f126eb3571f796dc41c9d9bbbb788d5a52f5e3ef903960cc187d4cea0f4ee00af8e64ef88e6029097775218ecb72270fe946c2bb7f9c9c3f00125f321afdaf90d01bc0452171f759409e4e435feb353a8753cd34c92
06db1da0e8e44d06028401ff614557bc44d04330ebdeffacbef03278630f3f49::b82b0511b133a0723fcc1643c6ae16b16acb8328fa10782c3d8dd532bea9e6c3e5758bdfbcdaaf37771ad70989e107f309adabc1dd10359caabfc0471db16d5d9abd47e8e48c3a2ef07483989fc0babf6080e8a156d432b9bd2323d3b9beb152
c00642364807d4df508cd504ca3787239e6c00ae8db91c98d21aef6de2fa7662:09:95f9f6599b49da7ed8024a42543593b695385b8d834b707afeb062d91736f94ea087d498ae64070e439c43841a2901a502b25cb6003751e637ba0a39bbf5814ea8140e4676414d1cc67d8f3319e0b8d0568c4c57e83e2482cd11bf5e919a6924
4f9bd257d3cdd870d0774c460a7a819fcb05b328606c33d541843c4062631d1f:57:a5178ee9b4f18bab44f49dae208cc1c9a387b518d25fd0c9ab8c5545d3f8aa9802764dba201683b4fb36bb3f5f95a8150bfd63971e086acd76eb2ec7a941c454c8f32152dd0894ffed21265982f3da9f6f47c2aa76ee526fe252b5f921c59a95
dd2422149b9bbeb9e915f6725f01c0c03dbbce8b031970e777e205cdd0b62a5d:05ca:b11972fa5d063b53503c809e26006d5c03d803527622d293c630f1337af957002a0644cda5a28659260929a4a76ee25c05cbfbeed58549955edd0d5a70f1a170f8484fccd69ca5241591bc1ecd9d636f324270d06ee7bf64b73e5a43efa4b327
37114bc315ea4daed2d27c47dafe0b8c9a9280f3e9c5628ce6196d3e444b3a48:2e62:81c8811fe65a4528384d8f5a6f7840ac530d9d66a58888e8e550e73cf03b2d0695b3794dd8e2ccf7936407ee6bc3487d169a8f3c99718f1e8884032477e212c53427a7b5f18c02dfe52a14e73cce697c21fa210fb2c9d0b093347a82ed4a09eb
952840582e0d1e7645c08401ed6fde3e63f9307c7e45815c0a71d37cccec3a50:0aab66:a760062f799ac72cb69d633e41457d7094f356d6b20d8d6635b906b31c1ed1e31fe88c6d249bc669b4b400e25131fbbc0e0d2dbbcf7d4881c809a24dd7a1f2ef26c42f327a23083303a7b3e05e53771306c5c706c3b6cc44e050b0c55aaef06f
9cf6e3c7b9ec13b2d1e10082d36668eab962ce02d19b7ea1f0a876a0dcace272:cdc4bc:b5d3c46fdbdb27e20868aa8bf292cd0d5eddb67cec04408d21b54ad0a127070d78633846c50c757b7d14ccbcfebee4a102048d3885159afb3655e330593d37e9480480cf84e6e7fdc8a0a4fd0e4c658bcbbce79d4a091037912712808ad6dd30
55be56ae7328ccf31edcfec667c1fadccde36d44c4f7d420cd13b563478163e2:09d14ad8:86b20e8dc583fd7727e1a1afe20b205a14fcc27339d7871db9ec5347dbde1818357c825c1d8158e730df80de1bc6979a037dfab2f893f710a201dc61792bc4e1f3291fbbd99babaa8ee17afd2b6abc6b9341edd3392c738876bedadbfa3f1c87
0ce187daaf68a1af9fa954c1b00dfa0b4a7f99d72f2c0417dcbeb01bf3abbba1:06cf3372:878c0c0dbcf4810933c3033e0bc70b58a53e761ab2946e45c1806f8f2bc8228ca944794c59378e1775adb9b7e647e2c9108de40b3b067e82c6c2e432fff08925a1d97670403ea80318e0b1c51765c390594667db46e076e2141fce896cbe606a
9efc5c31f466e255ab606debbea016a0d8c36c59e343af468a92aaabac08b9a1:0cd3d0bcb9:9895e0c9a3639ae728c5d21bca58c3ad87d2b7cbf728b3421f8fc92262835605a3edae741f4205564328adb71db3cf7c14fcc4bf52ea08affaa9fe780b7caac7615e7696da1029b7c9e44012b9941fc06d63e62360b74c553e87b180915527bb
320f214da563ab7020bf54c97ae7c86868bc5c89abed25159c90896ab9fcd72c:14110a5a07:90799eb2c1b7f0970f1fef2d1de9f9c66be0992658b73f3c7690dc35f3923a9502c45bcfff360affb554d6469818622e035136a8acd2a73918ae85abf6379f22c8343bd7a2039ea2998cb5c47c68b6cd85408452983d5faf70fb4b1d3c8c00f8
cf561deae5deca76ddb136de64d7ffafaa0dadfa0c1eaa3e402b98cbbbe77194:095f9457406a:99b7e34d2f4620cd7c3a28677b3936bceeba317a66bffc9220a2db122a6a5737bb4eea8dc197e4c359178d97cdbefdb6150dcbf76e0b72f13eeb0afb61c34a49aecece57c7448144deac3d213b60940aac157f7a9323ca8eca94f992bdc323b7
6bc5dad1ad7f6cbda1f1817da2dbaaa1d35bad490ad2ead25cd2eea895324ead:42ed6e9eba7c:a95e3f826879865c2479853984da1075164dd2cd84cecf66ba253d16561510456dc346d1257cccb12155331c072bce5b05b294c2bc870d21c0ab9055f92e70dd880ce8c7c144a5f956c5395aa49d8a5186b45627c2c56f41fdae021da95f26ea
fcdee7c031f2f14478368bcaa3fb3a0199e017942899c26cb5b2832e708a8c50:0010037b577fad:8e60fe3cdc55cffa121ef09dc84a8332e1d321b5a53809ffeb0de2ec41be7190f681a9b466a8f219c9980600fd05491f039338f9c6dffebaa05631910fcbd32119660809b901cf19ea844771ffbf61b007a1f89b1604471808beb58eb65509cc
4223e4d4129f4b89380844bb31b46288ba449b37a9fb376d96cce1eaa628e2b2:a192e9b18da043:b47e432e1c6158e0c17620c61e801a1401dfe8578db5b61cb131567800f40ef1f5c125f1df5ef171770165481047f09117b2ba7ca45f5caebed2777cec8858db2a5bafec8f7adce972044429437b3afc7831640ea9181ed20ca023b109a056fc
9f7c1e5075dcff0084434d9970267968f9d0b69fecbbd5899c4da5745b083811:0aa9c73dd809000d:affbdf547120be416186444b617a4a8f9b3d38f95ae1179fc1efe933aac187c415a62395ef8626a6b8e1f5a56d89e608155d5c52fa2a4b56870cf98e07e21b3511c26fc7ca0da24a4860b1193b7ad3a0d0377d895da05725a28e4062da7984d2
85bcee14fccb75ed024df71d8c7136bc75c4bd2e904ff6e05a044b4da1605b9e:e58d1bf3c64e6fdc:895105813f080161615181296a5b882c490bc3be0fae374eae53314d26ca10e47b3a31d6578e9b101a2c66bcae723106022e6cc01a8b3b7a7a46bc0e6430a0f940db73f006b29848c424904b7dc3adb19d50494dd41f52aa514db537c5965223
569d72679eb7a0f07797892b09fcf7a1dbb61fae4a73d4e798ef7cb35fc99f77:01ec22b832611852c2:883e8e05f6e32dd24c9640666d58f1952ccb32afcabe0eb7960a18abc20bdbcd3f5e00f1b633e4d35424a8cc71a341380fa31634c7f5e8f3d3c173b4fdfcced456d9d5dfadcf9a71fb8ad2182734f9aee6fe0b11e3c7331279d2530e5a042f71
6f3c9137e679a12e1cbd241b7436e8bb6807aa491bbf083c6b68398894cadb4f:580e2bb1668220c7f7:848be794b037494823c76cae9de6083a69902dceee2979893d27b04efc18eee6b28a4754288d7e20f5970e540709cc4f040fc33482c6be06311d1793fbecd66194fc97f9b491631257e19babb61bb58a464888a02924d5e10ba4113b2e9d990f
34ab9c6ba4a97fe9ef761c6f8ad6f4afe5dc390bf159e213b58e3f2175c142b7:0e30de7147d7eb5e2458:adea87d3e463b6a97e05dfd57b45a8f1a21953e2747bb0b324c54e8904da0e16a1e83565426ef32852d70af76dd654ac050c4c27b708066f52cff440196502e3ec527a6fedc68f21f1dda783bda9f09d415a4d47384836cae99b417089a3179d
a95f51550aa2b053c006523bb3793568848725fdd96c513c47f411f71b344e3d:b0ded16e59492cad4ae0:9329d8054b71e8db5b6223f76e23bb76974524e5569e5f82f21ef812f4b52b2ecabf5f60d1f96cf533da962c9f3002b10a50459a031a11d7c0e25f2fd4c38d99f8c1a22aa3b90b2f1fe333bf185b8dec40725ce47c4ed2be32b19279de1d63f9
0589e72a73238a9973bed6260ce69db89e210806dd01619cc1ccc9dc695a0f74:04e9af42b0a27a52d319d9:8d622962af2e158c2a363822c72616d6d071aaa93f628b50573d856a04da48ae8c907d1f42e680828b34fb70b53ca73f0bfe51ca9afe03f7892eb2ae754d247c7974949f4c35e7a21800d5e4b5cd7a6efe4f3bcc7b1a3a99d8bcf8164f47cde5
d64f5aa8ac3c4e6acfac48c8966794c38b69cc56a3fafcf5629a1adb59225a71:a5947a1e1ea080371b227d:908fb9f48bbf4bec043a36c547a2369d6f9ea5e5b27dbf2d9e1a9e5db4f951865686830e072309e05fbb454b6c787f2916f5d24ad4dd231749f0bff9a5906a10674cddb9e6615a1d26421123b64e8439ca111e93b299f5b89dfd74002e8dfa53
394fb7dd9c5847027d1c9cf411a2386e1f42adaaef4ec8a1b50204976189bf5a:04be6e8b23f4bcc91a8624d6:b8a5acf1925863fcebe3d59d5426186e852d14e93fc24910cef1a3d356a8279ef3dc6ab622ff23ab977a90c203587a000840ab5561de0713cc495cc35f1f00909435562d42ce852cc75776aee6b06b88e86920ae2cdf3f36bab3175c44018a7c
ae01be984e0af84bcb741f19b395300fd4a4f3ecab6b2d59b1de8a65622b7351:10297f7bf93a1ef627b2b3fa:ae8f8c4c0b047c014e6d6e4d597a8ff350b57d653c8c02ea36970e2ebd6d52d0157601567768e72797f9f89b3599ce79120f34e50f82600ac3af5878bc6807f7e552604247702cfc7049926a86df04728dd9aaf64e9249886ce8e1492e93ad3e
3750907c1368d256e95ff098a026cf81aaa0e8aab92560cb0cde140eabde4095:0e5665dad423cd2d935a3832ae:a9e64f0739ef9addd3bde83650b5b372562222d7c81db9c7c49c14fe8224ab18de355d23964dee080ed66d93a72f171901bb78cf7146957efecb006c5f7d49369569b02055389694f9ab44aca690eef31e3a0cc670dfdb53dcd4644de805c95e
32812bafbe9535c8530406a5f81d88f9d677ee0693d6c5c63271b3e154ffdf23:a61dc6c0c4ff587b31ef4b0cba:86b48e37dd075088b860e7c04d0101ec1ba8705dd9e7e4d6db16c23a250a3628a3b904570f95598ea286c2ce7acebe22139fa6320fd132423b163cf76e34329145a609e1cb9e17133175df426ca598495e6f4962baef021e7ab6013143d47527
768c79e74d0074c4c7c3ad74dbd62bfcca46bf2516d037f2b766e6b372109ca2:03acf045a0b276a634a185e97860:a423b892f8be1efcc5316e312f315575f2d2010457bb46dfea66fac971288b1d30ea103293244d4ee6ebb91d16f0e2ec07fff3f3071968bebf893bb98667da19ec22b5a621b0f9727870dfcc3d525ddc3f34f8bccbde06bb30dc46929f53ff3e
e92bb67b632f0cba53b3eee8ec19e2bb92356452fb217a18a65433fe1a8cc6bd:247f00c23d0e934ccb565b29c778:a4e454e97466e5cb8f7ae7c6941c66bac5ad16a794e1d1d733002ee0fdecf87fef6566984acf2cc03e7abf0f3286e32001dbc86802d1033512aa74eec687ee1513fea97b511bf55241fe918849c6725782f54c8ccaf30fb463a4267edabd4ad6
2ad285390978c933fda0b3d51bb58e6251f59083e89ebca66a565689eb373cc2:0d0470c09284578db66c65d1ed7117:9562202c7186f6c8a91e2284f7ce16546c8e9c29d9016caa9d3d8b2510b3af30c379f3fc459c8ec370bea57010df69051836658aa5d4fe2ab7d3748fcf1cc7a65996e157d83a522b5c7a09256024938ab32712e1dabc937b1b0e566202302377
a2820ab3c2ba9df56defa674dd6cb563b577e1dd2f9a788ee2de6b9819853222::ab29cd6f87e1fe1417237ec813bf3e26adac7df06dcf75f4bc7991706f802f59a3790f0ba6e1c8be3893e48fefd6b30d1219bb6063ecebf140cbd0175eb0ed4d33064ee1801ec1b263c1af6c83301d5fe294fc76009254ff2fcc7bddc8524d58
adc7da1db78381a3fcee8f0efa0dcef72b64fd2baf73fbb331ca10c6c8e2e1e1:04:87a58e28277301d79af6559d1468a2e295ddf214788c10e0614562ba88b2db7b6fc807047fb863373e8561ffc2c8ae9610d62061b56265aa493860731221dff6549aa5fdafd81dc8e1bcd4cd77647d5dfab1557fbdddcd245abbac2a75d7b7c1
deb39c2509ba781594323f75ac114d1f1b50f22695f60919869b2c46b1c9c6c4:a3:ae6a680307044ef6123a79406e39bef49af1a27a9e8689ecbae6de92498fb530669efa9d6d74e00afa2f90fadaa3491f010c1d2e15b557c8bf646bc08ada2a9f708468d2b13b1a316114a970058f428c1e327faaf2711c406b7be5ad5754a4d2
1d41aed618eb90a7a481032fc0624c3b381f899b5aba3ecf4945ecb7bcbba248:042a:8f48ff4108ae64b1d6475bb156fbdad895c6a38954ddbad95fafac32c9046800565bc80d05382072d3fa790275680850194effaf3c8f4c33e74e451bb4fed479f08bba1b25c70a3eed0236fe773cffcecd5136ca78793dfb6317b7bbebfcaeff
166ab7cc55be4235e4dbdff65beb5dc78eb3bdcf74531ddf43c5dcf9231ab5b6:1894:aed80a028af501c35a3607f83a405d40695b0247856f615d87a5effc041bed61994d5ade2a446649ecfb53d35c802a6e1407e7eb7f580723dfa4472cec745b33972920a4e391ac8cd24081be533a0f77f82e92056edde55034fdb5a1e62cdc67
b4c18feeb2ed734bbb97f2fd07bcf2b35c38aaef27b018ac20feb8c14c4990be:018426:b26329f66abf3d00f25c0ae8e4a94e3395b6550f9b9991e7ee9172f75de000167d84d02194af324a05b2f284eede6660143838070a7fc976e73c5ec443aae831f2f1656be1644aa006f153894dd488748e18c645da305a3bab7699654fa4cd43
b8b35235736a2044338a01512b9caf41f9e8f747d957136fb1ada889b8c6808e:ae614a:a11a7ad28918d05c5746305b83f1a50aebbdeb1e82d2cb7b30ff8d370a52f9cef163338a7be4c0446e396af2f0bf43130e99d450879124884452ccf3b4b25cc84a955ce6e80c81c8a34b459fca1f0b6bacbb27a2135adf6fafe7473eb218c917
868a1e248f596caa36ae397726d79456fdb69341c619191940a244af6690e0b1:0e3e0406:91d463c1a0bdd8dd9d2adc0cde8b8a54572f6b9b530978a52a2562d448a1ba741e403a58dbe2ecc34876b1ed5117aef009daaa1271fdba64fe820e5e93a47d29403479e5417b6199b84356f8ab6f0d7006a2f7e79c97bcba57a489cdef4e0f10
6c3639c6647ea53eaa46bc577bbba99b305b2cb9853dfb2cfdc702665fbcb546:7ec9dcd7:85cdb3b39c6db0633cd69f414d984a990149d391314a179a8df5ca53aec19d99ff5589bc6ac1ead6194a176450760eb518631ccf74397565fe7d0f141c33c5d990df1d92aec61c7774673e6db2bc9c010660d1a960bd9bf04b34cfbbe7ec16fd
efe2fd6210ad7b47bc60b935a23a60aab8c4abd54ae765db22a339228595180f:0a0eb3d674:976186898bf01a16c788b5622c088fc1f4e56c48d704a7c4a6a580e7674b9554bc606cbdb2a1ecaa825956bf4384bb8d019abe98d85610e5bebfddd35b306f9b375e45c136366335ff2db930da723058abd3d708d324f70065da480c42c01ce5
d6baf02a6ca31e6e8c4194ee80eae4137ac267a7f0389eed15cbe814c934f34a:43e20cfa8e:8f9487a6ce3a9b5153009a606de649d36148180d50efad4b0655fef459cd34b92fe8b729d7935479f6276fc9a7364cb9022939ec84463c080f9d161b990e2d8c79ecb013b6d3913ec37e8a21f1515a9d9c07679bff74b182e5ef0b325b83fbd9
49fa1f7a79dfa239e804f5e320337a708862416bb909bb9caec81301f2fe451e:0025641feaaa:a51b7b4da153ac896f5e9eeec421a1d61bc915fe349334644f7315d9bf74dfb46b24808c0874ee61f003173d678b59bc041fcb22788e0cb292b3ee98d827b476e0081fb63cd99441736d4ae6bf04d6b78de252825c42457c1415914cbe997ce9
3c0bdcd071ba70b2ea73d1a4c5768a3cbbf3e3642f8ce5e3fbc62b558a1693d0:c43263af0875:9592ef2b65a5312262ed8dd2128f8ab8e610003c459b1eab81e93aa0a97316a327b9f8d2b894e70e585a93aaa82b79fb0c4b2ef732646fd290a388f5ed453ed27b038d0a62e7c5d22023cabbab6278884564721b56342eb0bbf3cb839bacf7d8
faa3ef6ad535221fe8eaf077b796a4b92f67d13ce78a933d477ffd348180f30e:05207d5c5195b9:93bdb10c35d990b4a1ca7c8378df7416fe913664ae1a4f318ea05bac99d99ea74d0fcd8c2f5cf4a4066b89d9bbf44f750b625d04a0ebdbf844eb5e1df3e6da24db5a5532d00f00d0a2c3003dfe0354e2a59c187d50b6369a32d1c683a1f981a7
177d9b3bb86c602d76157c078bf48e764ed0ef61a65a953e078fd13abd89c883:19b6771ccd870f:b1e8c3c34a731873f24420b7375304df17dc7162e70fb9c2f3899c30a012b01816edf2ae231554762db8f19a74eeb9681981bcd70b87bb078968f12de45537c4401524508de636975b038a9494ee47e324f842ddf19517a0be5bff0b4b5142b9
0e98cba458139123b6478608980bfcbbac2a71b7b27c4a71757cf3b6b775eef5:07724a519ed1b1ec:92bed7e9beeb8d14899246a1f8b7212e10155a413e4c8e09436a6a3a2a8858b99410f4f06afe005f71de6caa9ae9858a123bbd12ef19f123a3de91811525dd41c5be9b87e07ded826c0130a85d3b3fb4f88fdf99cfe3b565b799fcfe85789d4b
6412cd26afd7b8774454ac305e8a32ab2dc489c33ab2dd853ea23d5d7c2dd741:531f475669c44d40:941462a5e2ff1eb47f4ae8467fe10161dcfb5bd04cbe0249b6bd29f46f04dc82057681af846e76e6aba3f052db1008af043f532601dcb71cbf3635763191035a33f68d4c3b33a9ddff0ca041fae49c17154d5a0df5a56eaa9686a560e27c2576
b13dfae119e4d416646328c7784d0f1f6fc9cdda06093d198048e6a287269857:097958dba547d1ddf8:96bddfdfb88baf179ed07c461805fa538981f9bda26607e0d99bc24bb07cdb187a13ad1aaf60d17104c683b8370e944112f65143415f0d29c7c5f906c070f5b1b8923c71755372f76c4f9417f3062654c4b149d5d4c5b4a086e324962d0a9916
bbc9784021c00a44a4993b096d87d7ef6c6e927baf1b21bdf45b474fc521b189:245434316d187b4f02:8d402e4b9c300346551c93dd8ef883a27dc5536ec394d47e0b45d8ce852071d33c496206f22908e49dc0dec9d9e6261f17d37607caacf48e9fd490e5235439114138c672f5e9affd1c474f82888406f0608e6b144567935ea0793d24f307aa5c
80ab4ace3bcb0d664d75716c39c533cbdbddac74795927b82ba6c7a56f7f7455:0322eb451950a34f9704:b33bb7eed3efb1ddab34f1de18dec549a84939f43dc388be6c9f43b3fc627c70e13ad188c26c0a295fa6b06749330b8302e814a0c7b20d4d468d8eb2ea2af377be9a6a5ded2f74029e2470a303e202062ab534fe07bb8f83c5ea6071bc820d9e
f2e4181f779b7c3ce0e24dc2e5630191020a7b9d04ba132d5dae95f7c4010dae:97430a39a825c623505e:9282cee78bf7ca36a252185679e6bdd147897f02e5b731f1b79936211308aa0eb9a81e1aee31593790c4341fd64c684e0481933bb5a5dbe6ebffe87a4bd2ce471bcafb85c16dc65698209ee18a290e046e97bbe287b2f207b0847692a78bb54b
a910c35660c25b2c636f26fe23349babc219db594ea73895e6fdcb4bebd8be75:0c24de4b18c3bd1206de31:80eaf929b05fcba00b04db3a8163a6d520c2f41b41f7287013c5c5949cd655fdaf591c75a46004e032a10dd73c28ce8610a6d542f69ed27f51ea4c1c0b7430ff4436f5df10336687c783f22ff1598d409834a86a78fae268dc0a2190ae4c77b2
c860f534570ffa8c21b1d80e59139db3e12ba66af7674b0e3eddfc92ecbafb01:fbbe246ccb3c50f9d19dcc:b5da8cd719f062511d558d6977415121cb4413f2b6ef9480945050286909b5e283547c1b8bb13c80777ecb31e82687911958c5e91195494b20dd10e863ae9981cc40a089c15a8eaabe014c08fee5f77c2864532ca71d02f8bf404d4cb39815ce
2b4736db74d642173c2e685c02b9ce4c4cf415334fa2ae0ff6dea6e475f0e6fe:07896277799fa172688b9d4f:8972f8c3910f79643f026bb5551fc9096bda8cf94efd19add195524dad7fbdbc7c28aa6b5f40175519f9b6117fef50b5015a920196b7a02943d6b01c77dc411872ed74a65fee33952f0f899ecce37d5d47adb8c0a9766da7d2c41f3f60a29d58
b1033ef71917d3cd58731b284431a83cfd13c58efd0cde09c552bbab1588faa4:45be6616a653de4c8697d5d1:8b2cc66aba7f601320a79aea0d4c72a922311c3bc25114075e7ad9a6bc2f425542269b08b25549e608d47cc7ad0ed34919da4ea212de347edcf7a7bfe1ecdea3e8200a804071afcbffe0f06477a5fe8d581a1abbf87f7b41b127149094fb5a11
9025178a2165d4905c38c9509e64c4df887b8b1c9a9e924106a23abec0229f1c:0717517ff121d2931a623d4de4:b17f3eb77eefa04ffb764934c6f95964ea969124a3d98637e5abae81d42464ee3af4da26a102e20c193d8b2f0e2469020774fca2745dfaf5ca1f8cdc7588adb7911461b722c74519d859d93717032b9d0a2aad506327908b651b8ed107c19259
923b79bc1f6b0fe9448821ac98d51cac6c3a2850ef0b58daab63b768a4fce80a:0b101498ee27350dc7a5ab4123:85a22e44caf87c1e77d9ce041660659843dd2d377ec5e944cfea666d47c38678e1854ae7b1b501ed2eefd1aaaebf27830487d3a5c10375c07c427bb20f5c00815d366be7c31c7b0f92241c2ae86a11bf7da35ef77cf99cd8ff032a87c8a0a65f
6886577263e5f25976f323beec475ba37e4060056ae6c32cd10aa3a47d17131d:0419e98cd72a702b453801a8cc73:96a39e6a1c6a3f2b282f4cd65d8e490c9d5117f9dd5503feb83b6cb44bbf3fa3993ea2d107297637a476ad7037679f5210802c078e65d791ff741d61986b8854ad3abf21323a5a9a2d6728dbdcd8c341dfcb3a2ac1c7535868b476c7d0258d8b
4e0239f3665744032462d5e5999b62e4a224c4de38552e06d96fe0afc8a56579:2a5e3e96548d9a9a35ed8d497b54:b8bb08d7c5abed07781cc5d3120a6c62a5a41514eab9580be86007e0049c0cd2f8622bcf30a93d43752e1256310ec0e80895f6dd37df67140340be45743a0fd92089a174f4cf550b988a4e717f65b823fdef076a25d521402b6962198b6f81b4
1cabe26d6ce80e9e03c326d02decd6970947a0e79f8e89b0ae3d31a96a79c698:0ffcc1f7376eedceef0e1195bf735f:b06db65ebc8cfbc7899b10a2c706d585783f3f10547f919b9d9460de50d292172813838b61c75fb1f409c833c98ceb2c1698a5c414ac31d60928ca4253ad36a90808e74797a11e1d50ed132b3278bf30669790ec6499b8e9eacfbe22a1790c44
b719dc5db6dfea9bec433b77bc3461ac68c2ef86f61e3d1440c8ee1660aacfc2::a6a0083be79c89593156082c57931032aa8f726eaa8c88ff7841f703a85a4e28ea2848ed44c98aad04aa17e176063f0d13661c19c70b2e23a14482f737c391411cd46c9e6f156cc02bdd0c6be9f6837d45423eb98de094eae78c96e5b06ecd58
1c9ed5349162e2d667b1972293b551b9e847b4cf5a0b271d8da70ec586f14186:0d:a103483d0dc05ad0a66d99f4faac54eb4a4e763ffb3aa337a84843f4c7be4b3010c10f9c8d0f0bca4813d9f5d1efe50917a587c6fed3a21fdc2e3f6cadfd1b7738d19434f54d370a2fc8672a9dce11b50c38c69b9eaccbbeba5f80af6d371edf
4fa311dc95281b95c653d295510303839d609cfab1ae20e42f6ecdd8222b0232:96:8a153cd16762f07269ef6b019eece8e79d7590681d9ba154a4424531f6ba697ec0f8c773a445a6c1b6607bc904f8d4e60e6b4b05e152395ad25db09f6baab0d7d8b5979047a439566cbe19b9ef28d020c3272ecff416cc13a94ab0d12c2263ca
f29461b0b0936131d4a1c6791f5f64ce17f9b2cf5c729230884375535f230e3b:0dab:990eb5e5d2c76a5189be57f2d96fc5194c73726303c2a31380e7bec1ee38ac220df2b82a1b7b53b4947c7e599cbd470809bf29fcf2dd846a9e2b9a16bfab547e44a43d680643333c96688be240b9dda7cf6dba812cdaa40dfb9d387f90a1cf8b
17b308d35f2b2107ea16bb5805456c49829f328a25360b371003a91a33c02d1a:e3f4:a1da28132bd68317dce7f6a4b10e64e2fc6917829d50c4a8700c1493bea394e2926cd47c315f97900007f877d25b8156074a1acdcaec936a04ef3b7ce18e4145afee3977d3ba18c760becd1a0fb7c81c4ef0dec32d0aec97ce0a1cc8946fb793
a1bcd7368293c73133680bc8d241e4350174e87f730876d588095384b20f2e87:05afe9:a624238c3447d01ad6a6f548346402adce9e9b8e8f0b0e94fc78135894df3a9054eba9ce8ba8f642cfbcf65c4954d7bc0fdbcc428528e13bbbd8c93d67575c1b9fe1d1b0033feb44ce97a3bf4ee1662a0030cb1b84ce0cffa79b68f60a6ca9d9
d44e4344c56472510fe84340b9b601ba7d92c8794e8c5cea127ecc01abed98bb:2acdfb:b0f0a8a9fb3527699e71ddb9e9d7dc7f9a51dcb14d4f9eecb2ccc775adefbdf6d70f6dfafcea0119558c41fa4f56833f1253fa92472061c46cee4de504e9779a80b59f6f9a62136cd7665d28fb885bf97729c5da76a87385582d9059669810b9
7bb733a9a83f79cca1407fc798df02075e1b747fd1537c66aaf68bc35c604f2c:0c344c30:882a49978a874f0afbf464218eeb219d846d90a23b32fc37c2c72e629958a7ab345e72f771c39bc7868d6c8f5fd03d4403ceca9781e6b19632d2ad287ee73c6c4edbd1b65af50b4740d5292cfbeb74c4086dc55f355e471e24664a5732f244bf
0a5c7cfd06a465381127918d0d1ea38c1e4c933444dcb7b62262d87fa08b95cc:926bfa1a:a71c91e486195316156f3d78740a93421d8a8c03c4c37bba17f971cd14d964ed66973c9934acfb06478f0d76a26eb04410fb8aac44abfad7835cca9a2a8f4d21ba3b6364dd710c8e58ab1f06184126aadefbe10b1b5a82c0af6266ccf2d82f98
bdca0d60906fd02276f1fa17b40a874ec15e70793b6cad48b0a7dd9cc26eb139:029a6c3bce:a645c427f86d68b0230eb7e01f406b2496b2723a5133ada2b744dee00d8adace24e084eabfe16de82466eb63130dbf3b067762c79586bcac0becc4f14f71f349a3811490832d46159420258416a22f6bcc3065d944999fb44441ce150e826545
ae17e14c12b1b9c68cb127a8e6807ede5ad4ea0a8c74e6a3338a36568ebab6c9:fca00f9558:8af98e40a45f24b71942e86f89ea245c2bd7f91923dc60b365c7df6125ba95a6693c89767c8b7f34f2ba7c02c42467e410ca9809680bcef8f22d736e1a90e14ca171028746bb2ca12ad8217e945bfec3e004fb1a81b9c6bff55802811a829599
d727fc289ff1790569cca790cccca75ef6f8a86a0a681be1a063d33c3795aa25:0a10f0e0d2dd:aa2a0cb8383c1a89d8659c7fc034d17feca0261e2193999d36a74a811c31cd7f891077ebb7e5d4fe7574f16b62057cb115c671c77063134e767aa5db0b91bd1e274e4f254c0b6586522724aa6ae1753d5b786fb04c0dee7f2cabb143f8c4efa0
fa50baf847fab14a085aceafbb6759fba66ee9ee1bb94c5476ab3cd8d5f6c8c5:21477040a415:b62ba9af86a3a404a81ebc1af12fad9c2199c6a9417ff45e11abbca7b912bc656216e31213d4446132483cba4ab22c8015e6fddffaaefb767d6039e1a7c7ca6ea1c7cad2da6bac7c9a0a13e6f9a91d8704026996875e72b895a7016f2b82411c
6459107c9acc6d52cc5fe0e1a7601e6f8004d5c78fd89d41bd4999aa5eb1a551:009e79cfe03cc0:8bce396182f6e23370ebe56bcb26284e082f0b07f6473be655c39863db041630c75ad570dc0edcad57d1ace944b44e6218ab9c714bebc6e5f81026b9fd53c736579e2287fc0760acacef4f20922ad7204656eb282d4f861f0e46ea23fa6c61e2
ac03c6e5086c5bd204c1c1236f90d0dea9e4dda727fb946c5780d60b16be4d03:c677ddae3453c8:8a437767f8aa83872b6a56b7eea75a3e61bb8a86990fec00d16a319e302c5ef3c2b8667a3a5a09a208550dac2245abdb18fdf3abe7c2bdaaf358d9c5609a0214772f1050301828cb147a5968931e570a18cb4bdc90b1eeefa2d994357b907a9c
df27dd0739187f28ea241dc88d467cf624e7ca97c41561f818fb354b3774d874:0efa46539d3159a2:ad04f726bfb5191cdc972f8f6b970c0728cfe44fc941eb54fb185f72d3fed33943c91918744ca0d68ac2e7f3849fbd7d0493ef9f621b217cf8d49d433ef8196c1123841b81d155f4b6cea59a27a01a1565380dec60626036d43d5fae915a15b7
aca92bff9877bfc35d4c25cf9af70cc41cf0816b17cb1a51089aafb9b5bd7d88:31c9c23a9cc42236:81d8d25d277c486cab2ce34efe06b468f59a56b4c0e4d5d3119647d881aa3dc871d0cf131dfca3ed811149eb5ec53a35079293d27e8cdd03d7543934633c368f4311424a2b5a6af33855c428ab901a0ecdaf594465cdd85264d635029dd73bf1
21bffeb6ce5ca75da4665264c683fa70fe0570d95a481dde0ea9aecd3cf53565:0a1c395c722f4fd83e:b98d13103b26b3728d140d535be1997e6125c204ee09e0ec5420718b34ce5bca87c5ca5df9b327128e124a458d4ab375128af9b687e55742931d9df5796b74c2bc10e772a5a849588baf8f8e805a5fcb431d2d2b2b646c3f0b9d9ecec29e9238
3fcabeab0355751e59abbfcfaba97d1c018aadfdf56c9bfe6152a3554db48bd7:e6809c7244d39b55e2:93a0f2f5b469ee5396552e7a9038bbe989092d5fcbf6627e035f7107f362ffa212140fb8718b0c182b5f24e3bec720f30fee24cb1a3321ecd333a5380a64d691ba71365ba385b727d2b82f6185498ae4a6506c70b65a623fed121665ec95c891
ced96826415c753d6220c495f8189342da0d3b7b2c98411303b60bb3c8836efd:0b7cb2a6fcf257ee62bf:9656dd2a188920a2d24e5c003fc86392f7fbcbe8868827ac984f10cf57379ef214a408bb2849d19560e39a1c54b38d300627b2370298b69dbe28bc4b455177d569b03082048692daae19cd89be763ca56384462f1e2669748754adb9db515ac3
241762137d3743ecbe71f1cf05391a1a371399c2c8254f4c138aa1d362d795ec:c7f22dbd191d66754852:90fc4ef59d11bc5fd47f20fa97f8d7cdfd10c9569d2bb352c5ae521b0310d8d8b220914ae087b207c61ccadc53c7bf4019f524a1079f9868dbab853dc69a34de7cba72d7540be146282ab20a43add794d8a66032ac64e5d072ae492322176d88
a251d29b73b0b81bd22b1da683807231569a3c339de9cf38198489ca91c4ae95:0efa18547d60e8d370006e:96df624e538d177c55d91c26ba6d678e3a3180a68aa9a11bf9e45760e726b9fdb303488564f879fc91b7129a0e14a3ff15e6c26a968dabe07b38773da43edb26657ec6c286db93c8ab0618e915fc8570c6b2b4f1ce53cef963e067a9d20082ae
6f463430d2e2baeec5e16c4fbdce282a80db31b3a6cdf1fad692d261ba78821a:2122e2d2571c6649a02801:9353963e93194b91c1aa71ef9e9d66dddd868f368d58456effcf266024f131d1ff35e0adc9a6959b338049a4fc40b20619d0f509cbaef3bf5743890a414d6e7d39f2c4cf7e66e85aec70a33bfee3b58f3e84ebeb71c4e4edcb88e460783aa8f5
ae68c25fa3d2d5186ef818270dd4738f122858e0431bb562370a55488b7ba4a0:05489e98fd13bf202586ac41:b4e67eb25699533de411c7d072a60ad42e916b26d8b9b779e7bebb4657d52fc079553ed98968e9d81ad7ed77e324fb4915d7124b3f006ea12370ca904d2ffd75506c54120a7ea3f901596e44c3ec5782e458624fa6561a20f895430fbf363695
59e8933963a382d0d1634b52f6d789d09afe9f411de832caa89c88622470f70c:5264749d484be5119deec774:b716c49e3aec7684f8aaa1f61012cb8af522d73749717534c8097d95056611e3475e351e0a294937071c644dbe9db238023dc1a740bcb55c79ddec3b3b2cac01c8c8d975a4887166b7600ed1fe454b203b4e5ee49906dc7988bc72fb8b629d36
328cac74a8f771183690de87e99ddb3be023ca62c37c243aa2ecb3ab096c32c0:0d9caae939afd6f2036b4c6466:908cced630b0c17a0b862479d99eaaf1773b6053b8f5b6fbb044ace6722da5d1fb4668fdf4e468c4dc5407718bfa490b0d7cbe01c0919a8b8a5f342c8fc3ba06e2c4529f82536122061281e4c3ca1f607cd9c2b75eba03585a5689b9427877ed
760c5fc9f942cfb114ff0ec23d0fc22acdbecb31d5b15b57d1ad3a9459c40b8e:3a3573a3a6cdff2d2f2313addd:aac2acaed63652d44369876813daf3cd24e66d3cd667c03f3de977bb17be92ecaf15354c31447a51805f7c0381c8a4750d7e4edcbe3c7160382eb54390f7b29fc5fa125a625ce31f41f93c49c50cb5af0f9ab2d493f7cd28d54d1962060f0676
2060526e5e104aaa6f56144304a6f4e2953b231fdc97ade6435835ac9d3ba7e4:0ceba10020c42ab9cf4d64ae3486:951fae47292841fd1b50ee4b32b06f961ab29d12a57c156a7fc2868b2a0b4bf42a472453a782d63bb7ef789c1e337faa040703b34f967e2446330b6120766266c70acdb659654daddf6446d80afa984cf89901149bc039cb7e541992363d3909
f3fe92129f14598e7ce8231966fcdd535e77278b767149016a502c7483aba788:42a85f4ca158b692eccb2dbce8c6:b2bee8b5790c28a0ce3954fe06694f4c81051a08c6fe91c844d057d514c7d9de2be61bf02c80d73376ac5cf9fc21827b03e85aa71584963c418dd6cf0ab453d644303ff3b2e8148bdc7ec4d13f405e89dcbc685a48c9cf06cce43cc9d5f8f5eb
95c22ab56885211c159714cf80c3336685ae64009db9d621c154e50761559ab0:0de6482e4597ce042879305f4f07a5:b32c8a3b62cad3eb41f9f83630e49b88a914a5df2b28cc624f028f7778d33d47fa4f01cf5269c1f3b45453ed0aeee2a909077803aa37814866a62b449dfd89770a05939d273aaffe33bf178e0526657c928ecbd0f80ceeffef9aea2dc1488dc2
e2e213db79a576da7d0a616666f4a28038f37e3d70b389c75bfccc0404e1b023::8aedfb44ff3d22ce2d0b27fefb61b4aaa7f44456b7151375262cb4fba097dce4ebd54353e9f1048d7ae9b0bcf4166d7319b6055c2ee1a2a36cc32432f1195dc88112203108e4db786540e1334157e90aabdbd26ed3e0c9abe2e26150f5e65757
f392a0265d6224ab1823f2923dd5da25af361e4c87734cd408632c0e34dd4166:0d:a232bfc8c1eba16b6092619432d5c9ca0ea2165a0cd83a4a7b2cc43bed4b283429d5f3d1f94d18ba89799f67ffc07050099d10d6c805adeded8aad6e082588caa3aacc1c1a1a9605fc682fb9ff582b83a44daaa65161b22f501e33fa32fd2f87
c70726356fcb2dd2bb9a9ae6bb80868b2ef8e441f8bb651e0710bbe22bc182a5:a6:b449329a45d35461b1f0cedcc20906481121ca4ab8e04ba5e8dd8e6681b57d9ad0c40defee9357c189ff155796a9aeb503c5b1dd296c0545dd03e337cc6281ab5be61e433352b3cd65be0f245e475fe4d9369129a87849e78d3a2851b17ab77f
0c87d91d1d69e322839804c1cf3c90034f39a9279014ee549d284a20e232bc7c:07cc:88ba6e38e788cf59896005510a3396bf326be507d91e9c45d10aadde83513996011b4d2eb100197d2d9c10783a5ea27b138adae4497a49af8439329062d7bf3da2a3bec50661aacec7d916e5da0484a9e58a8c0b054717dbe30eae80013f9e82
97a7ca86ad085154ed4dfa215ded151b0188c16e3de7d488dcddb202c044483d:444a:acb0568ec50cdbc01213b93bace3522d3072bc13e1c577d161f5029845ad549b71d08c158c98078418900a6c9daabc5c16bc6bdaf9f6b749263f6f6d6a687418cdf0495e49c406ec4bbbc586b96bee78760c9aa2aba4e11cbf32c8e77b21f13d
b8f5eb74435a252b35b172f7c7b863c78bff7e60e9c7c9368d33636300faf5da:05d89c:8eefab0eab1c9f12ec661d9b512cbffdc823c3a1f33555421f859818daa9f5542962d9265f297f6b2454bf9e9ef7876806b1783ae641cd1df807b4823cada155f7c849c23709f2c1191743dd7ac842e1e96df8fd7e165c61d8103bad131c4da4
15981c773dca2cc1948c4614f26e6d6cd0beec3eb672dd7ca5b1ebb2d38b6430:350fd6:aed8d80da4ab66cf0dabed854284e88de19a97663698c0dd91e59120b363411e46fd455827cbcc933f97a781a035546301ba0f1bc2d5fa9d48416eb6c00cc2c673529009e89034c10a40fb9baa4da241e20e8bb01f33e675a4f01ae527761167
14d83b474474caaf07e920cda1919759593bb48e125da09ac7013825b613942f:0db5516c:8ca43cd2679bbac2d9de3763aa28612e89a746fc2227b72e212a725c9acd6d9ecb983e4d665d64d76d64bc1c67d22ad701af6d74cbfeebebda30ba5c00de436c6f78b2163ee4fab88561e4121dcca80ac56df3379267e3be26c06a1034edff7d
29f5e89e7a6afab3da8c1ad738665a9da193af9a647e47753c235bd043e9d8b2:095ce9c7:81057773b78041728ae80f63e3bff13d9253414f592162945995ee98a8beddbd5b61f2753a7ea62c53cbb80f935d3d530a143945ac5b3532589253e2b7cb42bf2a0e463b0de9027f89cbdf710195c2c6c0cf823ec3333360fce1c0e6349a5093
4213489df97e792eda26d981492a7cdc3100e3cf4bbda19cf13676bfa5140584:020d6ab3af:81d792916be49cacb0d4f3950367bff32c035dfb083634912fd9bc043c0c5e60efd22199de3cc93374c1cced2e836b3e0c678e9b94b277734be7e127ede167c6051907587e43fa4532420ff77cc23e84e9622a16aa0e4c69de7c72da3d6ae605
4db4f6404c0c0fadcee5f26717ece570d846f5d0bba9ea9d93713a067c54e03a:3b8cefb4a9:ae1a72ee38dc209ba1c6fe7a295abd821e2898e18db1a479edddfbc2cf889159b8355472d301c905619b170b91e20cb7161a5c823d6ae8090c90f6846b0fd0fb930c28a2fd03c293737a1568b3a135ce04009757287e58a4a3e914a14b13467d
f7494e2b206323a2c626a625214dbf342affe585f1bbb0bd54b2c4f9ba7e86d1:012752047481:b3aaff35d780f9100b37f16a7f0c3a4e0b2306e2a5afd1486ae05b2dddedaf7c0de5abb96100166e38ec56fc34672cc418269e05261d2ae952386c08527ca7d89d1c4ee07ef2d6ef0e3412f52e1a0fe868c99e359926bf32ec9b3caf9631d2d9
7101cdcf4291ca054a76be43a72be2e7c57b142d697d5f4591c416a2ba20a6ae:dd2ccabadb72:84a751209648d7dfdf43bb68863a3ed0a2c07281c5b8557ead46cb0168d4ff1d8491edb55cc69a0ea4eb06359a23285e0d844c8b0da3d91f7f7543e251f8652f210c0bce4f819f55ca1dac972fd4eddeb5277c9be73974df310fbfdd42090e9e
dcdbfe35dd9694986d220b00450ad1c85a416fbdbb15fd5f0ba11c908c4e2242:0fae62a316d518:9037982e4aaf6e7f9894cc267ddc26114e5db5189b09c1e5401f1fb040fcf32efe5799f461f140af85553144b6f930ef0a89e83a1438f85b63e20dc82b00d223384a6c9120b56c6dbeb607ded2d7b268fb8d6d06f9d526ca701dfe409ffcea15
5833ac79250158123f356effd33c11b69f4970480a1724b2c0a0a1f3c5d86e48:6fe2514b0b040a:a655dfcb28d84ffcefae998f1d0c87359d4c522d549f1d18307c374d54f189403c4f95065a2b5b366272c1a0e1938b2a0fb4ba410290ed83ac3b55532d406d02a34001d6c6f150721c21cc86792a9ef94a3c1e498092c318957f4559aa3fb4aa
60759490c3c1dbc40d682ac8ec5b23e79df8679f527ce5bcd4b98da79172579a:03d670b0a31bb074:88adcbe23b4538911ca0555c28c4667b65993141ae797a71ee54573ee9b2d450c8f5902f24ec0dbf98a32ca7a4c84ba00c1675075b8804244f9ee5c8e746ab0ac18653802fa7eb2e8f44a1346b28a0299fe17fab2a2100d1932c1c8017c04790
0c8beec5b19b6d49422b9be911384dfdb5f7b784fb7931a9fee03f2b2b1435ab:52d257e3246a8717:9602df4f595bda5df4fd932daac3ec093fdc743d3b3880e3099324ef3d33b87385ff8fe7b4932044c726645cec05911d0586eda7726ac6b96dd8a7fe19f75b98bd21c926433a2995bbb75aea2c1cd6ad443de37d64fdd54b8711b225813a6efb
3e5ea9be9e47d5c03426c6b23851b839462cf2792e62c12ae9057785f0bfaacb:0edc3a78b6d6190566:b7aba102c730342367a96b64920ebe60f749b0d42ed4e91d51c18e31d791142895275fcd39ef09c7a6759d13651bdcc4022f8c93c77a0968a77e46202f8621ae7969e1410ff6cf5ed9ecf6df14905322a3c7dfdcd01afaeb0bf1bdd72545e7b7
4bf3c40acac842af8eaffabbe90ef74faaa20030a57e029137a61663db033997:0ae34aa529a1053f8c:b2de94c41c453ff9b60696043f6ed76a3431bc1ab53bbd9e7c89eb482423872a2b59e8546c8fb9b8b37a1c8b76f43e29147b10e64485ba5db48c0012c7a969ba99fa7718b7540252d9e49968b5f41722f454cc4b1785551ad7c7427e49ca4012
250d8a3e1d48cb69ae2841db1b6ba57cc0db311bc03570143204f9884bc5324d:0a9e59fb4f4d4ba10ec2:a0e4a43be108ddb82fddef1e59a6e7dd9fc84ad183ce393015d472b596b8bcf84a16b9a54a1b340ea001776bcc8d5e6502624aa4cc16bb3817da98669796dec60fb23c9d0a003cb78aae4c2b690d56e98026fb5a06257e39fe01e6530ed177f9
480a573b009ad6847742cf6eb26226401a21fcb00d15605d61d07cb2dd36ce16:a450b2f5e5993ef50a4c:970893411b7a3187dd195dd078cb9d49e64d6fa70adbb2b0e900a82048ecfc2d95dc60cb2cbb066a04c2b036e9d8c952155197e6dad3ea08385f7c5e8ebfcffac5dee95a3ec24a894e9471341f7ec40ed9d51db2985d491fa90b959b952f8be1
b17c398c3b5690a5f2d9df93f528879f720a32f5443c455e68c1ee600e1e3c73:0a8b1c86fc50e996c7a251:ae60f93c097e49ed430e9d4f13e67f601431032b980d5604774bf3c179182f8b9fe2bc7510daa71c7cf3cdaafda7748800e7eb19e250c640f54194fb979ea41d8657b545c4f4baa95503ae06848f6fa957b7951b4f38f72654f0a0f664b057f5
6f3d3d9ae77d068a241ef8832a9532f77c810f5fb66837401cf9824a77fdfc4a:8ebaad7cb6874e96ac767b:99e80404714e26ac20dc0c1d87b548a08fd427723dd4bf8093eca93c7903657b49c7c8eaf36cc98de460fff8b5dd0055169f9a58b0a249cfb335ebc06318bb1a81ab24fe7df3aa2d299a23298e9b29b98bc5c0d64d349b398fa763398ee9ddfc
fa93887ccdb277f5017081f848cf7fec935c112ccce72275c9a7b6b4758a6b54:083d040e50f2fd72f2ea77cf:8ae387dadb7cb41a776e39ba1e205686987f2171e5d9aef3bd2d9806e269a511e67b9cadc6c20d7d6b1f49fe7bac09000cdc5c6e765ed381b12cfde05e3134cbe8b51c26142e424bc26822a235f722332f71b4ed2c89999c05e2890f8411d7f8
23bc491c2142f7ddee2e8985a3175857f897c399f697411762554cbeb00de659:91b32862312873e4b7b3cba7:82a5feea0abebef1b71659cbbb51e396d1d6a9bdaf3dbc45790023d6a5b46b20dbbb6c368af1b034c6e6e5f8039d1c28099ddcadf7b4153d4411af1f79277571764467f1d083f5815ad45d265184f431b5c05166e708d55ba049c6882683d331
7272daf5146e93e068b6cce38fd07be2c254f6d2dc02c7d078f9e080bdf5408f:089e270bb1a4308c4578e0f6c0:916be2b7d46f009d2439a4c8b83caa6877b100cb0af4170ba0c5837bc92047d84e883295e441b44e0195dbefafcf8892126b27ca5bd2b126b6115b2a53ad8c5c889d3c78807dff9d72bc4e9a064743465ad025c1e2c08fbf99b0101b15264417
47655f711cd82a199e9418d4a5baa779d206d939f7f41a9b6ffb9f87d6ceea07:d90c4391d34e0b2b40366115d1:a33b21611a27397b7c02e0891582c1f5023d2cbc69e0643a7c32d7267e6d8042ff8e65839e5093d1a753e97da63ea7d600ff1c5957771f57850c574d167a8ee54cc986096f32ccc40cbc42d9fdd83acbe11f4e4ef9db36b9e41f7aa558f4eed8
f056b12559a6358e99a2c6a1d86f267dd162c3012e4788a314e298e7c416444a:0cc73ebec823353215affb7bacd5:ac150f8b9456801a2838d467ea2fea5f02f8a96cdfa4411df3d9fc30721468540f372e70f00607dee69270deedf7e39e0c6af55691fa12fb1300bb68289f351ed1d0d49aa745217c05533ca46db53192c3655a50fd169a41477346228096410d
59014aad89bd97fa2489c2b43508c31347ae8437240d8306987e63eca954dc6b:b29de70979d3459bf0f52480634a:94a813a737aab6aa68919278613a28c7a79e6e64b3f925538a498338e7eae29c0f73ab43a2d1f0e9a99d06ceadf57f8d166e8c4295b50269a117aff566ddaa02a5e6d4fd40834a96e3257ee219089e5843c3d1e1fa97f7a6b3119d4a55a0693c
079cb55cbf7d8668c79aa7be998336c9e44232f2040c19fe44b28651956888f3:06884140ea3d1aa1afa058dd19602f:add5d4e8f20484f247dbe0ae2705ff8a84f98ee988ee14d0398bf58ff2711c78064c3f9a284089267dfb1ad9828ae6f116f7118e93978f1c8ff92f1cc52c8860b509270a39523ad6473bc48029877a26df0f074f959532a5aafcbb348ad4f712
86e665d99c887dc7b90a0c0a1eee2da0f559cf83a89f0b5867bae358a4f85122::8330d06e9c3958920fd81bc143c324a31048d97a4179ceed71af96e02db2b6c2fc78445142d3d7e7b930b38ea511f17509db0bff92a6f58aa332470db5149ed741f091f545cce07decc9028ac4ce1166392717f0919e0a035fde449bdd462084
1d56829c94f18e598084ccc64e4d2fff82566d80a0d00446078fe2744ea47c29:04:8cc2f05194bb4fea2eb6c816626d6b17c33de7961c0597a74aadb51585d41fc0b04eea200fa70da10ba6701abe128a5409c374cc6ee11e516c408c11233a54175aba1f9acf49907560da8389745f9c3fe2622be20a68c9bc733d6a5ba63fed2b
1e243e4a9db47f767b6c9dc99f211312c3dde97b50c7bed9ad6585d5bbd2be78:2b:a8f31f44ad30aeccb0d801adc3d0d6c850532eb82baeaea38cf3829cffbe9936a4ce0b76bf158ef2e011245c4c9b7f5307c47379f4767efb52366dad98393d67fa56defb7831cd786375558f2c1b4fc76d3338a468859c014c8563d7be49ac97
785d7dc139f866d0809a4072cf6545e79e285347321349ee6d38126ccb5f0a33:05dd:ada77e687c122d58da63e7ad6b1612891d99e3d88302c861e1cf1917f48b49bf531c189ac2dae7dae09db6f6b10f0128016cac0d0caa55342ac8229c42e3d2b27913481989146b977eae6b323895ffaed10814a0314891edce5208c835e2b65a
fbe9ade713df63e2d1f592c750cca4fe187e974cd1a03c60dc2aee1779d8db2e:e8d1:adea816b1732c2448f5af1679623dc11f3d8aea1ac75a07fc6ad287433b3223e123473f6d8bbdd59a4ad4db7148f3af61753a2b0e8419ea39a5b1018c24b76e6e72622b1b3cc5d05c02efc421560d6c35f3845a7020030f65a580ee0048adf6a
d7aca7251337ac082a9e885f862c3e52af0daa44244444f9ad2a290937d46347:0b5cf4:849d9160feb1423868715cfde8a3d35a014cda0d02fa9ebb2ec0604393d9e61a4912e18fcd423af2db277cd2c4e3f8e81617e131f647f4f1165bf93fbc7cde92dd7056e027d21ef9e8f353941ff4726045ef32d4375ba5f43c5b5fa368a1fbb6
3fa39be7a361c621ce0e944e01727db44e74baa23c82b4a48d9c0b350c680eb2:1de37a:ae2c347f8718815b992d93c9a8f8db741ec2958ec95123aba339537fdbc63f802c06f6cf41e65f0f6e3e5d567d3cda0207c9223f7144b1bdcc6a575b3e667f3d22724bea3e7bcacf28458e7112c96898d0bf5695159a7bdb5d7799834eba3fb9
fc40ab6dece2a42366b1418dd249e808d376b5b63d9c15018ef7285d440d46b3:0edc1d2b:902ad68fe321ec263d0303b10546790399c5def45b7017b9b46a0261f0d3e907b3239eb63ecb9134285fe0b0d51c04210ac5d4b1c095b608223468e57edc61cc57e50385a4c471bb4a5e57d185d8404942ac0f5ef84c8914fffbbf2e9ae24e80
3463763af0f0745e8e5ccecc3448d54521ec6360b31cd1f61461baa8d9430d85:81a2b9cf:b86cc9994a7664a88cc678bdc8074ef6fed1fd9d67487aaf51d11fba9c9677a8317bc3c6b93ebcd15faeaee195497a0e19393efb8a53aae96610701b7d827fbc72869c454bc3ed54abd119ce51873262e96c00e378778b7d6d7c44e8afd3da5f
7875b2180b7a07743b340a85acc9096fc3b60f3774fc7d12af17761f6472574b:092a75c121:9352ad15bef39e8610dfee782301736b630b2cae357eb56dd16e8c32d2a4927e758f2e78ebe482c4162aad170ee8edbe0f73c90eb7705fb97b5c6c86df6d1928825ceac93e56dce8a61fa495179f14d9ceb794eaf6e1e18314c609ad7fe5348b
43635608d29694b3fbe8c491bd2f56aafc95017e99a7c53028fc8aba1a6a655d:cc326cb7df:951d25da783cefe075e22de837614c0f03791a0e7113a6a6f5505a39c199538ccdf58e08a0e4f39acbb9459be5fcb4e30db05466f472c7cafa8cca9d0fa7a50d2644dd71c6c1f212d9fcda2a4038fbba2f16c8acc2af3098dd42257cf1953f6e
c831f94ed4e49c83e82c311d298399e56bb64cea460418833a7f17deea072265:05d9bb10f795:98553a642571c558264f1c167e4f11753e7ce32eb5ec00cda23232ab6b1e84d9b2770fe73611604042b2575765ccd6201996223c32fee3fd37eec9012c949cdcbf56e1067f184bc3bbbab3a9143715a546e3f5ca8d91ebebe9bcdb93078ec941
5062a749d8f40402f17c8793aa929f5cf33faed38663561a38d590bd26d1bd93:7bd0c2a0cd58:8e666a772334f4a599fc3360efd49c00e0062071dd6aab348acaab791a9faeee4c5156a7771cbf5342d49dca753f312d0e436cdb2d00003d25d41e06ed593d5dfe59f2a231a5fc890c59f6c784be18ec502eefe1fe90c2ad6c2f7f1b3ef561ce
785215acd386bda56d7c58d543e0e31bcf5f87488dba7bd46642fceaf26e8d24:0b65e1b2eb4fcb:86621d09e52dd8f4c15d44000274483b93e2f3f9920a1f30e65c84cbdaa8f6e793c1e830a30d209e2c7a84ad14c14aa506291893da4c05fe16710d55c40ebbe2827ef67da4fff0520579f0273be8279a3b4f3d5343df8007d3f6161bf5c73723
01064d4e8efdc6631fbfbcbf7389572e82cd5107f47356989b9eeb1626ae277d:1db604a6a3737f:8c8891f0bcd2331d4395a75f47d9206da3553c839054ca8e7e59ee64f991d69b0ed42e43e2c96956b216626a3da15e2403a620650189b389a13f14c39a75c44cda966f7dc69a93e79d6678f0420407d66e7916057ba8e2eeb19d0bebd508058a
7e52d56764d5b7bd14d576913a63b04f28e3cfb816b3afc549e1190a2fcd7cd5:003ea498258fac12:8bd52db56c313ac3f0bddf4d11dda4e6d81716e212c9e82fa32ccf0dd0b1e2359e3e12c6e988b17d4f9882a58dac083f15c2ff4f1f8f82047e8ae8e5503d1d84c864163656f7136aa106d627aac5881461433f30230e378dff674ac9c7e31809
07247477ab86d4b41a3ad3bced6c08f93bf4dafe52bb88215faebef0de956746:8fdebf997b921d22:b50e9f99992a08959991fcda9654f5462ecc015d3b894c3477752b29a65dc05248c75d7acf152d7cac32ec45df93671d0fc07dc7e0a46ab0f5246951575e290b74ea6483ce7512a106529dc5f342d172ace7f5a72c6cfa9a929b00f616411251
49bc2b3d85e073e9d9bca649fe7d77624cd0c4186cc816ec767ee5907be6d4c7:08a6d70240e42b76c2:aa666c6474f8550ab282b480c15355737b1c3c3f496d0924e6dece36d052eb6b8c37adba328759dc6f930c1bf40e866a04cf9306d728be9d00d56115509c74c54a62b0456de48556e728335864f4f99f3835d03e5742219a0ba1fc135976ac0d
6e1ee2e476dc25bb44a8ce9a100ba3719b0be25802292723688b6094fa1e47fc:9ab597b9bc1f9f0c04:98bb5a200468cc4a7bfff04f913d87c386a5f42fa67d39c22924b24c985726db18c066b45fd4715fd94c339246cd683312120e2d6abe091f5fd3b0d75c4621fb716e88588e9449db8ea0754a06fb5372000391c781aa9109e0675b210d8a6399
912aec300cac053bf9e8dc0173d46c7ca7c5b6ba23bfb18b9fb681b56dd3dbba:00707016258cef3a42d9:88adc406bd477056f5aeb694effb0cfb816675b45e28904fb1e701a3ee87e8f75d49643ac1e3e1b688a6a2acff48d7be12f3374968a86f7451ca35d8a1c0fc835986e78e954b117b6dc12b074c0419fd462f606de08ad153836e83f70f9f6227
9544249f393171d85c19e23a203e820974074033151070206931df1f104f44de:5e69f3e560c6e63050de:99c2d4a99976691da1f4bf084dfb86f62d10a48d6316e7fca5b0c1024fa2547f7c02f6d3b775ec4943f88b72779ecc7417c8390b4624435bd7826be5e9b06994224e17640a3a54818deca8bbc11f04fc7c694dcb84e533560978420185389d83
51951b1c9e647dd724bbb68c7dd976d9e10f0eae1ac3f6fc1df04446c8f6558b:0d01c99a3ebabb5bb8d241:8a902cb59d6ded491e703682530990074ac486fc8ea5308f8797df59019e429a536f10a2acb5b9906d3a26bbf38c6bdc005bbf81104edd3b384b46553d6b66b2db8568c00e74037ce07eb9fca764850e9921d412e0287d9ed2a1bb7c3660d7fd
106b77fe23dda6e79c770e3e7d8d0dc5f01f6a21859f1ea2e37211b7e3e9d20c:9778045c939a60ba245568:84586f8b09fd69b3a38d317e89d3f3f5885bc6aac18c9a3ed5d0801df91f07c95bd77db9864ea986ceb9a4bd19fb4ea419ec7c46c35e477251fa47c05d7b0a6175e3ccfbb24196aa3b2b2cb48ddf9c8c0882a6fb32707a83d0260980a9b5528d
cb71f393c33749f189741023f86905a494811e69753ec416c4a0572167e8a8f8:062c15949918898fae30d13a:b9a2b6288eff5db9ccc9dee7c243d13d7331d6a146bd62b5f1be093c9e077554d9a414a4ac53372348717750543ab2a90151bd7fa36fd84132bb2c80dbf45ff37993656d91914d10ecfe87e4b664f5a569c440ec0be6dc98bb27ee78fbbfc103
24182d5dcebb1f234add96901337c0b173a5dfc0735ab8476a44e8d1497eee11:2a590d539dbcd9cdb8dca9af:8b10c6a7efdf6afbbf600543bde5d43ca322a83e6b085e5dc08c0eb9b2cec92a69776e1dbc64de47a4d2f4d091426d79182531706ca25ef3ec81306da03edf70161bddc53df3bfd1447530da7ae1b94344cab0d829060abf14827a87568c114a
29f81c0bdcb8098de92efc9aca45df19b581174bdddfa7f9dd4eafa217f46231:0c413b91f979ef7e3e3d0c2a43:8dcc40e3267a209ecdb869a11199a018acd9bc872a2738296a46769ba1f3d5f6ebb75471b2c063dcd3ee89ee34eb032918d78468608500ec66c6496c28d3f56f1baf87a4097de60617c0938895a55f10d54c362ed86b3899edf48fef00f0607e
e44651d84d62555d5a6f5b432b51c4fd8440803bae8b0d477bd366b12a61dbd7:40c7087d70ab2ead5992c3dbbb:9750f6d6d5a53cf4ffe61047c9d9d2bf9f3bc6a727093b5d648b3abebe6c496bc30b1af8b8abf21d678a0054c03efd85105c36fe1cc61da2b5b18f6b71623db10bf93dca5bdc39c53b33e5cec5988da228218767a30bbb7d0d80abae1782f9b5
bc16d0b8102bce82811b6d56ff13dffbe690577e70a36ad607d48ce77592432c:0ba94d4fe8655980c0caa90e3309:ac55fd7f2635a0ba937eef6176741b21533fea6d8a8b5dc3678f4269ba1e1e06d9e3824a226f7b130546c048db9eedda15bfc486d9ea7a69f4eb04d515627d839bf3fbb6cd252f2adad65c127eacd362ce7b842cc2052fad14d8e6c3299230ff
2a9c0a14191b6c8d9da54c2e7323197fc5972d6c2113b8d3f4aba8eb2419f402:80d1192fbe8c7edd9ae58a5fa715:84b02c4f728778a29ce1273b6e77a393941a87173764e4e10593faffca0a04b97e444b1f512e6f92de1fc9096ce991a90f389e78e5745f64c5b589a331aa9e157a71a993c96917024e67f13c97ba2e070fe1e3b2db91e0b95882f1f3d64727ef
344bb9ed7b08f405ac402c210d6ce9a251700f8f39d9c13e72921f993b0fa657:078698bed3b7f59cf6041cc5bbc27f:a0946c200a29a99ad8d348d3ec627c0944cb42a6ae105f18815ac85798d656b33acacdbb954adbbd65ba6bf031896e1c016a0dc7b667ec3662e54b8951d93a3e03a2c8bf3c3b88af29c02e5e3bc6d221166902d98a94842d855ec65506ad6911
a5b50c63c3061e2e61ce3df7c5d2b67f666e3d0ed10529a9f2cbc475f836c4e3::99990d0c901c913e8385a4ac7a4d5808ab07e68cda6c891af55ef2e69afe6ca8310b9de5c1525dfe86c94e33c51f40541518a6065f6fb036c4804fa68e1c2f5d5fcdd5d1ff4d9f41d0f814baba6b2a00eea0ccb4d7d4ca6c3a87cc811efb60ec
41e6f409f3a9ec0d1b53ec441b6302e388941b23005ac48c750924bffc28c95d:09:8002a5f881006ea6f1c6f318e7529b1c8d9d1cb7cc93d75fce7d14286f125e03c47090f9e2bb89350ab85ead94e172230cdf1e9d76265579cee29c487e8a15884d6ff78646e634d01f7d66a9e155bde7908657f655aa711ec53fff05d31a3d55
c3eee1c7204b843714622abc2d7ac09747e8e95e0ee2e9217230cd97934bd46e:8a:b6eebbfa763dd3729a18836f1a8177ed6ca43cde55410880c23a0eb26363a1f807e02605f367899f361fe1a5bd6ea6110ec26a58fef050249a27e5d929262b12370c2fd8a7ae35abe35f8381a0c56c07a0774bd5dbda0293b165c6e9eb4a1a3c
ca9f5cb4bbed875f6fda371f86d523b577622cf57c6df9aaf7b5a0a0ee68846d:0831:8971b2289e864164a53b6901e14c3faa35e1c6a68bd5eb81a8cc897bc8d17c0383ad617519b4dfadf5ba43a69c56077608ab6ff3508bd0d71f9738a53be7a907fb9f9f1bc41ff8b370e900fd70ae63da49e2186899631a8220023aab2f4ba1c2
01ef1ede58b87f0f257a1cd96d71c1eb98fac782ee687a639833fc73cb64df93:79c2:afcea670ffff566a171fabc5892ce1c974aaecca3d86a968c479f9689ef37b7980614a837207e53c2f7ec0701daf17bc13ed6e80f50853a606c8d6d690cb3778094c161ad162666f46c2f76d315f24b8ff90cc57c1e57e15bbc27aa94d41049d
e982fcad4de4d9e09a025864b9c254b4932c6b65441383e93509f48deec1b742:04a8e1:b1689c1450faa975c63fc9b23306020c89f9b855cda3d40a99c9f591e2c05ef2e8bb0983d9989193e52c4c58716bc13e0c0908f73922b0dafaaef2ebe7209c53efda40ef23ab6534c335b504288fcbecc3cd6ecb370cc55d1faf350cd9a50129
1bb7932556e42f4f4fe9ba0f59ff17bb2245fd79203f78e8bc3de488fee40f11:8d0ef3:97dea1e05ef0d1af9289e64866c91e7b8fe6275aa87f6f70c3a9f16b411692332a0d1e86666799faa91eada62e23ba6705a69a55de44696b2d2de12bb8bafe1529217c87bfac57bac8d866b09aa208ca8ae62156568fb7172ceb0f411a171299
6730284b18c2d383fedf4e0c32480b06e9d05b380530e7acb9bb055400fa33ee:05866e46:a81e828917d5e25475db2e23e35f36c069150f5ebd846061f00bfcf270d3124b42103b48f915bd73236fff0f88afab460508c356998e8951806ca54967f26eba9ac1e68ce22da4119b1b3ef4721bc286a0455048a7542df4af460e1b53d9c351
1b538e8f7f079ccd4a564273a913e96a54c654a0c028e39d2e9597c9ff2a9813:c20027c1:b0636af64caa68aa331a61d5b6d6f826ee3a4838a6d1ec9693559227e80737b244829f72976c7908948e633040f0a47b073979d05bdbeebad1c49acaab6aec7a1dfbc45554bc102e0e88e924f49bb7ab492f7e1011da3814ea05071bb722c8eb
4adb9229610309100770fca18fd5e6e707e7f27f4b911ec0f53a6114a906230e:02c7973ed4:94e16548d624b5493cb0449579376d234c2ae597b347d681f394ef3e55fb3279bd361d83e3194b32676c1210d616db8204064b33a8f0d67a21859db0689da9447b3fce39972996967e6a85a0839f9a0253bb63265ad751661e035c8326ad1fff
c8eb027495c259329813356f1b9138f98bfa589b0f6aef6f9d57523b96e10863:2be4521856:af7790ac25ba5a6c789e81559619c89f9d2b48015add999c73efe4d2d87cf809e82874628672de403c7271f51b302a611773b67961db04d5328729b499e610aff65a8aacefd0eea0dd431c80abc242b409a65eadbdc1e8d63a4489e7d468c2d6
fd5922ade6bb2ed28516a13ce17198222809c55ee9730a3d4a3172228a1740ad:02dfcd963c93:b1713a07caf39bc408abfa53b235ef610692f7990ebc61f28af2369ad690f20ad73562374f3101a9893401c22252e3c20bd5baefbe97f15afc68b411125cb0fb0a22b00f8f262964e07a4521ae0baf3cca7772334b180499827cbfd243d8faad
0fc195c56cd4f54fc81f4f78a4abc2ba834b5b0018bfc150a64e7c6a5823933c:ed0afd7c4881:a40286e1cc833d2adde1d0640abbcc2199b2767b27ef540420dc96f12deb14af17eb655cd2e958a52a68ba2bed2e3e160344101b101eacbc33f3c5277fb1ee6bf6d9b866b5b8f5794f55a5d7236d3ca0de4356bc1e8cac30a2cb87f94eff4817
64813900e765c40ab4e7eb9522e2c21c1c30756fd5fbe40489441f11c6908ed4:021256b10a08ae:a01a1eed626ffdb57dcfaf3123c4ebcf03eb441f287bf36f2209f4d36ea874ff42c3bf8f90a7fa164f0d133692e0cee50ae42593571b8b876152fd37ae62af446ac1b9f2aacd9e335e67e7735ec92e3b5aa85fc4b97379a08dd2c8cad23cbf30
4a06c487852299ff3e247514706ef7aa6da0d2c1a1f7176b3317cc095d01a51e:5a04c28464f32d:b8b7111ca66dec968135c1b9716a3d162ad946f8e24d7587b27c0db7a39c70bf7d97c1d968691c569f913ae5255c3bb1120cf1f87fff3014a37d5f8f79a5ef9f9d1f86bbbc6c5b3662d1fa3de2fbdb3c20d0960671cd6dbc1336f16f3222f399
e9d0db97e7573a44825b69d0fed6fa0a9587b947604d9740e44c1d59af929db0:083cf7dcc2603ac4:8b38447c730130d59c991a9e64601523d69ef43b05dd019dddf10e2fcf48a6de7574da216c9fd27013764c86a266c22219099e3f2b88adeeb528b4ce995c846288c2a368f78b40517f969e8f44fc42ae8ca8144e093b767e87abbe2f6d093e8c
19ae9daeb0826f765cd2fee0f6678724da5cc4f8ad39a339fbeca86754f8b19e:39c99c18e1c62745:8b04d6db47c8acea29079dee12e9e31e435930bbac9ecd2dfb425898829b629ce3264661e16e6993be9540aa52828061079055fa69ce690fbfde7befc621147a02b94c86e43f2d7066e986f7f6857ac521e005e08c70a8558a19d549067bbe68
c913eb03046a76eff671a21d6832ef1ce54845df87964c133b9cc9cdf0bde2ec:0dfe207168723748ad:b60d540d859105b3aff4a383ca545e0c5d9c1d799ec24986406de3079694d4e46dd0caf90b7543dc8e01c346f7331e5f15f0174b7c03dc164c76040b9c64e0771fb7724567dc93d3c686cfce08b34ce351f7e88cdd57101679600a27adb24c1b
0a39e99b16a69b706fce5095dd0e7fe85b4dc521702e3bc99a6fa919415c73a5:619b95b11e4f8c670e:ae67f2f1f4d8134691100ad694bba172b1997f4d1888bffe4e0386382f09bfbcd56520a7a3647d2cf3565c7f8fd47d1808d7ea678acc1c17e272484296378fcf44f1719af50db7ff6ea3d64d5435e33bc20658b4fd6294ce6103138be0d74401
d1094f56040bff0aac7f1e86ff4c599b61b7d2a1a190498920b00defa717511b:085bdf67568eaad8e6c1:982c5233aee8dbadc89f13e8190d3cbc630103edbf845320ff56eb1479334f4564bea9025d89a32536ac300e06d19809127283b6f9dab6e196f08fe9e32bf4456441fea4eda70e8b4bdf1aa1306ace1c3731432bc17ed0e1a3c29ea36724a506
26828c6e8108d8e5965e8b91fc59b95dea696580517437c42c27bdeb650d3cc8:65267bef23e56c1361b4:843e467dc7ba66fa39dc0daf8cdbaaba07dc8c38493779320f8b7d148f9554e1a4eebfa8589f3fa403ce1fef29f649f00692a0342041527416e476ad36181eddda07556639f921c1d09b4f53767c9436c1b69925a3b391475580796bf449a960
b70b1e21a37aa421a9f522ca818787ac9f13f9362c9b481f0394e4619c0beb54:0c3639a554e91d04d73f2d:9209ca4bd6a9e95853d16dcb42b8436730d4ee39b32dfc1f40cddbafb2f737ac89d19bb75b51c1c473560d6339b4e02f089bf22094a5870e37f6df236a72eeb6daae4b8d8fc56d53c2825e392306bd8d8ad1bb1d2198332a6ec0dd7121743549
28cae903780d94f5a17c6f07cc27c0c55d8860decf2f923e1bac274533f04509:ebdabd1cb56e3150770e59:85075ccf9926542cf2f6e13463f4ac970cbb8d65711f84911c31fd0639a009102745cb328ce660cbd9b63b2acf87fa2b0e65f7416691f14bbba4b4e8e5a46b35435a2290e43022bb799bae0c697891ea4a0ae90022d7efcf192ba2ff730b9183
9b2bdd74952263a3ada2b584d98bd92515897cd1708f1cb2061a71f567235228:0c89a5a5c90178ea4669327a:9952900b3581d96024159ec648da19985318dd89bc2917119b09e1f9b8619ed2078f58239ce74a9c13bb5027f1566ed30ec6ffcb65aa6c0e3a85581702e91a4fe360f8fa98db4ac513c8e53a8143c5d43f1780de901cbd03a8754d577d0d3bab
e138142f55e120ea4bfceefa69fdd4b44f2e746120d2ac2071c78c003a6158bb:21fec9adfc423f5b7ec4ce42:84253268af8162cbab69b7e67e8510dfdae04478d2606500e2775d546c637542a50ecf5a2fdb6f567dedf7bc7831ae5a0f6b19f11fd2d1277628e7a9ac3ff9bd093891b844229ee245f606931e47e6c62c1b8a664f790d2cf708ec6863ca54a1
e6f3222f95df5c99700e01e5458eb013f47cb0d9c25c5b882738ed1bad7891ae:0843c07218e88e9054dd0c0bcf:a8a7ff07c12969275cf9f554fbc0eb40d21dc03a106292f0862e1462b72f394fe9dda61a2bba28ffc7cf8bd0c56c505112a43474029ff8e2c6cfefcfed0680ebc87499b9ee11ba5474e71f7f6b2472531023b0cb1770aee6238941196ab56255
43b47b97088f55c15c22418b81078b9678c7539d5b6ef30b3dceeb248fd7d610:e0f4c7d85159379b8d4dfa2ed3:8f0e1fcaa287321526d7f7974f68236187022c8ffe12e3dca46423745a4796292224f4e875824df2f2d5944dd547c80817e4bf1c4bb5f98dc2b17b2367bf048bedae6c0827be5f2128949fffa9787e4f9b2aaac78f19128ced220cb1cdce9376
77c94ea4197d049d0b6ed62005679b2af50998d2c44d8be8f7b1652a3ac934b2:046a16c08199f8b28cb69fa7b095:a4a493b13b268772f674650dbf359c6a73bf664ff68ab288c650f0d4b4140b917c726b879bea1b50ffac35868a3aa268154ae245c5159620e727ec6e4b7eb8617331e35394ea26f0aa1dfcff87f0840f8976c5e95641df951d66b43d02d31f1c
365b875da7ee5cf2a37716770bcd2a49012b03b5bbe828026ba4a17a999b2b1f:d5cdf315f32aa5e54d3b993e15ef:a0dc1746699105974d79a75f5a051bca821dc341d5e2921ea45ace70828d87ee672c293854fc18921f56ea6edf067e8209c2692b9e1365483d4dc9c6e70e1092a5b678318778975aa44e51a6c273283d66348915a9b1f66230dfc1a4f41b0423
793c2fff43bdf604941341dc4d2d6d7e4c64d3f3d5b21efd8736b025fd657f5f:042704c4811785813258ae148ee2cb:b69e8bd1a4e57514ddb3722b3e4b43ac57cc71cabf6bdda81234f8d82ce1a371375457ba812ca6b22d0db5991eca2f5302184128fa38e67f53868d09cc4e76209a6c71e9b9341dc72312daa1ed826c74252111c3f100eb7209c8a6ed0e82495e
4ae51471cb64596dddd4ba384f6914c62e635fe7fe2b48bfed3560b9853c9c5f::80d9dfb35f77f8087eb5bb5bb7b73b3361c7547aa9ce1e640052b348b61f113d22be6344300458369b64d5bc16bbdad20c13c1c0a7310fdac807c9660c5ce709a837d487d0bcc69d247b5973e470d982cafe077ab1a5244fe7a21fe99af4fdce
24e5956ce7e1c26764a1212f5b70c8c635f53313eb8ffa24bd4b22178a70400e:0e:a1bade1469fd0949d6916cf467a67ad56245a9c234a9fb1da011489f5819a01da10b40983151650a3baff01d4931f117084e15225f8b2143196f8c55323e5e1dbcaed2c9339083504b97dfa31d01fe5b320660ac4b348867d97aa5bfae4708c3
f1c2156e0f353a0e05564f455af66d94dae7e425ab5e9950362bb910aeaa6f05:5b:8c0b7235ba1291e802a58a4cb855215cb24cac84211bbd1b3d0a92a1553aaf360faf5b7d4f8e7bfe3c1866a4b44eda0d0900f3ec49b751ea68d1bf0817afaf15a0db7c25227d8946194df6c3cb2a2b10c2960d57df9e343b84c6c6df579695d6
1d25ef5627abdf5331c07eb922d70e8c141a952a16aaa63602a664e30e193964:02a8:80970283042e91719fce9271122ae72d780709ae2bfe8476f37abf6384e4c1d741585f0679010a15f18aa87e132bd120030618a443a25096f1beca1958c93f28696f2e9938242f7f83d6287eae5e3f1093c63bd3c8c9841ab9a3f9d6afddbae6
2f5307f7723b2702ef203499e5f01314f62ffbd448c859debab940d0d13acddf:9b71:83c20f7dfd803feab7b92e4619d3fcdeedd617056d3f4a863d2aa8c762ae2f0e9d10fb639fb1cb24cf775da646c887cd170e169aa3f99665227a8be8b74f37eb64c45d431afbb453a10c1059dec817550eca3ac5798fe97b6fcac44b05c3f39e
4fc8dced9af2d9b5839fc71f2242f29b210a75012360b76fb73e7a0f67236ab8:0518a3:8282811085e6a7532edb00ce977b1cc0ca00c5c482d572764350f0e15e5ecd745bc0ba1f124906b72f10e704d7ad091805b1990e4f5749526bb3f33f3e145ff3a364bb0850efe302dd3f1de79e77df272b27c8a33d1bd1bc9dd594590949f92a
d4b047a745d29f10977f7f85f2335e9e6cdbc9419e7f7c54f389eccba9222a8a:093d97:adf10bc3bf7673880aa95b623f4ac4e1eea1b4ab8d603823f88f5702979f251866511dd70df1e9214095cf281de5e6af18806fbed85d4286cabf4a2ea5f03af3666a25efef653f758c67a7e8ebeb312f22f43dd6f2dd7b50dd2c37b4202eeb4a
110f155f50537bd283dcf6a0b32d76e33369875d25522912c8ac5c5999d15c92:060ce567:b6344848642b6fd3b15d5cfe8f58ba33803bdaf8d2499fb6cfebba69932fed5a25cebc0873cf7cae39ad0317e2cd62de069eb4563cf7efc33a644eea6bc6f3788805bdf3ca2371814854223fe970c39d66d7216da597865c7371ff3b3af6c7fa
7222141d6c07c9311bd3ed536a0a9038844c025fe2241ba3e172a74c5dd055a6:a6623761:b1ddbf83dc85a9f597a16cded7074f8aec09c5260c7d4d896fcc5b9857c81c77a9f48339f65e916c5e464b02ee4275521072edadb67d97bfa0e01250e5236f90cddc5afd7c4731ea9a4cd97c54c229c4a1f0793f62d124abc6801c392bd84394
80a54ce355ee0c2920098a860731eb59bd33962bbd8bd3978bfafab8fe206d21:0cd2734ce7:810dd97c12e3cebdb8a338515e592371a29c6f6370a668752994c1ede906852137f888e7fbfe03225185f54e987c14f3046e0b61c444097c24ba199117bffa055c0108e255fabe99d1076d96bb8897cb9f4edf03cca655b45631082b0111dbeb
8bd743963ade02ea63fc37a7a733c39999e7d1b11b2318557ac0a135035d08fb:8eadfc5e31:997002df650fce37af6f759312169a6cbf015b17a3065f29cb181a327f6a56e5614667cb84da7e82b7c49a62fb587a990c892549fae72e5463e0eac0835b2bb82abfb485d90ac66a0ab4be07c10ad35eb9e63a83ff02e9f3292e2350880fc168
f3b983c9ba5a887173e0f65b9c96b48ca00c24c95357af87e1eb94718c14a3e1:0d8386794920:a04c1eb2dc027397e5e5c139e3427637ed78d78065a6e498a2fb5510a0d98fa760f0fecc8190f14238e2cb5987b84cbe12b1adadb8fe5e5f6880419d6544b0b0f9a167c3e4134cba975f9bef6b73719d7e462b215172ac2ebde69784455f7226
39643613cc7ca678aea24c2350bdfef54da2740dc84bea3eb0f5086d8f3757ae:ff5a601d9ae5:93694096221d3ce0fbb2ae96d606102bafccf7f4b286a44cba04dd41ac7ac52d2fd20a87f0757313d2b9cf16eba1ae68176845a18688852eb7fca668232088e707cd9367c7638ff586240b3012602f114740b8f23142fe5eb4d1c92a0aa3cdb7
896f12deccc815f37571bd16a9875d191b4065446bb7a41e81363783d22d7bdc:0977015917af38:b668c89c8ef9e1a9e66770b569693c30f0baacac1a51d5bd50e79eb2217b3e676d4543a2e427c1ea6a706aaae32a0b090bfef45a18612fe33a531b04cfb91a9a835e5156d88f25b6fb739b09840fe02838a5fdf4ccdf5c0dd461f63823c3b5a1
2f48137f2c80335ceedb56aea36d1d37630ceb603d25dc38c5aaad9edd6740f2:c596c4507b8c21:8b29ed205d9763ebe6f59787bd54490ccdeab56855382ab985cf6dcf02d3ab01b3074b858d2493917b156a9caa7dc0d513f3616b61f279410580ac6286175a8326ead7fb8b9319d3063da2d94dcc9331f85c9ac82d84ad1a3c0c14c00cbaf106
5e104f67360bd7be86562856628f7ca8d6f6090e58d78d1bad5faf7b6d01ba96:0ddd0c7f788fa3c2:8abbf1c71d4b4d62046e7557f34ddefcf0ddf8b7991ae8674fa3d73de1865065e98f914fa49772348b4665f102f2ca1515d21364050ab724a0ae95002ac7f2bdfbbe7d7ec1bb8f6b343c4409ad993b39b0d2ebd4a1a001909a1570760d29c246
f41439fd6f3c477089e6e6ea9a1e91e11ce62998db83b07ad2e0f114f9acdcbe:662c4514041b22a7:a23e7d4af64aee50d4bea6adb1ed6797e26613753c8dcf7d4aff5136ac8bab0be75dbe3d60d95f8639914dfc73fe620419a6288d6920d3bf95b86537acd12aac5be76804edd064e2a55af43ec73e42b4aefd10f01b64867f14bd452e34ce03dd
2301e3208dc15a69334232fa6249a28238e7eebcbabca532719b50dcbb6eab16:03c1ad665dea01bfa5:820ef925b76285789c449fece5f0765661cf3ce95ef7b6482c6c35151e61f5be1f7f08a25cc647d1e2539e5e4811fe771829d666c84f6db43f351c41252e3fa27432a399af25272dade4b6d1da95aec40d6b83a5b82dee79b7ea3568e8a8dd6c
a812cc9efa293804c8dd535d4d8c6ad424cf0b82855b58ea5dbc9dcc432aa311:3249c17e4e56f7b787:a1fb4d9bf530927b578d8317f7045c5531d03e9dcffb3cf4467953119f8d96c4c6fd094ad34b742daee909ed59e7f9340eacf47ee9cf353cc63f18748f86fcf237a893543acf9fa96ebdbeca5ef189a4e8f780c1c402017aa0ae9e0fec11c0a2
d9a329774f9b39f814fde151cc32bf74e8a1cff727f83646f55bf001d2a6f603:055cdabebdb7c39371a4:b81b341a8a1f8ea7bfa3ae218f74eebf251526b9d2243247dd9b097521deab083ffe10a877f349a890d74a82c78240cb0929fdedd37e4eee257688adbbd921f7e3d30d32daa6924afc8172988490829a022a769517cca57feaab140f3ed91458
5a0155ddda1e372e176dd4841998d8373a4db70f720e13f8191a323dae9151f8:ed74bc9283fe9f0df46a:97bf50cf9ce75ddb14e7fe58fbe911aba7f2ff24c5982ac33803ffc578aa7f956c2cca4d436ee7cc0f66c3378470251a05b6570964abbdfaee44f9761581b89468833d000ff8f4f6432c5bbb7f084757328ce3501c055d9b8d1a44a797e7b93b
1620bf3cc64af7a12e3bf1a65859c37c0deceabc9765295f24294e5e408ad93f:0723c6a62f4e556ad97947:8abf62b2bf3d9c3524cb6639f3c94cb4e215aaead42ac8608d0a8c1495f766d7626299bed2eb309e3b3566d76ca87de7168bb651726d0427f6395ff81bea526afe56614486444fd1fdf01a2f836e35fd7dc740be034b02a2ccf585bb490e6f07
25eaab4800d7b399f81ed647c1c2062db996f9edaeba7411c9b1e47936963816:51410ca4fbc1eef3fe15e8:8d0e31286039c789bc46dc89914a3a7afb47bf3c1b6e3e06b618e5310f8efbbd43f484bd182b1a25d4977fb4d6965e670db5b4fbb9be287d8f3a5596e118c43032d199f93fcc8a60546d45c4b0745b4fd8432534175e9a620782bbf5c72b6c79
872cdcc462f7c537801beda2a5ab2490f2ccdecd0c075d05a7562b718d88bd76:046b1e0da58edc394def89fd:86f901ad63d6e46fcf31e6933b76300d355a23ff08710f34b8d20eb8ed7e1ab3a1a2536d10d4f8b307b36041de380769185a831dbd6bfb44cd40da1d7771379c9ad8fac09ccbc2ac67a92cfbed25848cc4aa32511b1c2739434c0c6599398418
ac8fbd5b0a2a1d7a18279a4a77781f1176125de642bb3532db38fd1b9029caab:4e99b2a8d76e3a8599f34e0f:a30b77a191250947cd5653b09b21c0766aab216ba514e78027ce01e428211cd5c3c4268a546a51e0953e0ab1a278fcdf0f19285b85dc15b479e47ead63398d87272545c1928f0d62a6d7a7ffa507ca5a6a3738ffa341556aeaffd6a94268a2d4
838dcf72e258d2a77bd369353baa2209c1c2f40812803bc74c7bd1c696260ae0:0155257765b6d64a092ca412d9:98c0da445f6f0e4b03d6a3c956ef55c9c77bd7828c84085a4c776b88963d2d190f05ba5834697bfcddd734850c55c99d07c932d98fa4cecff22c887d19837cb6a67122e96f2f58103c5baabe21da9d03efcdb7d91d58e1c7b6d5bf231b6628eb
1c37c9dfbce948e9b854a16e5705d39d6814c99db8cd4d21bd17e4b489be8ec0:f29274076eb714c9a1e0cc7c4a:90436ee33fc91061cfeeec29ee065bbf0fb1504c588a057bc7a47e6d706826a03eb75916b5a0802e6bccd600e479579709af6bf0e69892f863cea2d17c7cee0b4aa0d01ad38a100c1f0a92a0d9bb8a662a4802e68fc61a6f89ae5a967e2ade24
d588173ab8a6de5b4026218d4860ce9f7796dd05a6333caab951c1ee113f4418:00d7ea9c8a165b4dfd71242abc84:b3bbac928f55a5bc88d9fcfa166d3d5445efb48d5fb564d6fdb13aab32a443764994913dabdcf103c51999ee1abdf011117d77221165cfa0455c56a690a99aad5ecf0429c09fbf6d18facf5762cb1d2c759e2cca4fb1db3e23a54cb85d554750
9044a9f62013273404561e373509bb12950260013cfaaf455036174611c62d4b:feb386e695739cd3a97061627019:b9f61eba10d0876ee182a899a5e09b78e1db4436960ceb6a9c7e56a133b1adbd3c46711d0e50f854b7839f07984b19a800ba90bdc779327e045ceba39caf33c6ddff31ed7a2b59de65b1361bf787ff05a7e09b706699d150a6e4225a9a3e0cb0
78558ec1b78bada4d8ce4b1816eca078e19f96891b1198418c36306db8d013a0:0287a3353e386d8fa1a6477424dcf1:84df1942271b5488ccf46f85ea23391a54734fb778dcdc26b0c4472290496c02f82e28e28157ffc742006fd5f8cad89d16303b802ee4c0ec6b52a998c80fa848c90e7ed5f89349bfd8899ce0f4856f956db7f9e34f7b28b6374dc3e6d5426c95
e491b15ebad91fb2e487850ad754edbe4e9542a699acd3d5dd186ddb65c57844::a019419fde4a1436a79fcdf62919000cde8cdfc1c3c827edb03b2224fd66951bdc168f74a02231ad3cc6fe821616b5b409e2dad609dc6175287d355a56dc8cbf34811a3cb89242c451ed69dc451fe8f32784fc1e84d078733eb3e43abc916f9c
4e6237d14a3cd9ea629413caa7c05627220e7f4e87ad60fa17a35916e111d5b3:0b:ad095916915d21a753c5ed1036f8ce695bc763c753dacf18fc696a691cb2d9d4d305255f0371ed05459225b52f45a9ce102fced5a32e0776ef97b2f047c73833a90196bf6e7cfc424dc3b9538e4a49a41c584c093be2851d08f3ef7118e4a71c
0d1bf48acabe43f47aa49c94e212dba63a3d375a788df8905cfb624c6e7a2f0b:ea:96c1a767ef41d3b3ea6fb01cc5c76951fc28189a8e34ebd0a5dadc330242ecac0fad824f4c6ed4bda3bcfda260b8e2b40b4a9f7c1a27976edb4a92e4cbdd4316dbf58bfe8e3224324a9a5c1e5e1fcadf4550f6c95ff446c1242e94b8cd84cae3
2e5cd6061fa48f1596cd81dbe4e689696a8cd79f70194d18d1992404244a1395:0388:83ed89345a2f35aabffbfe3e386351928087554e65d611365620360707e0d7d772379afe3565f5d40ec75977f3a24edc1172fbe9e78a8392934b67934c81729cd3de2cefda2ae13a749d20e89492091b71d56b23e113a5caa28a6392c56f6a35
89c35a9d863242f0766d20141a09432393f7dc85f9976f98983d66a45c21ed72:8c93:98395ae57556a60221b79b810f32e29768d48fe074b4cf7814e64ee18dae7c43fb7d457fa9db3c0d9e89f57586a407d6050c66c01c68d7061d487ac94047f351d6bf17fcfbcdec2362b035130b9b2bc93a6b293465d21c57d46d8aa72b6e6d3f
ffe35fd644e7468e3385c959e280a1f7a17472a54ffd6d7c56277ca5f3f20eef:09edc2:909162428d6558bae348f8dd487794b4efa2bfa4600ad02d36caeecfb7d7b803f108e4a0f7b5f02d8785f45be26325a60091bc6912b98209418e0f9f6468361f6b37fb27c139050e0b993ea5bc31d57163969dc12b355c04e0decde0c51a2ef1
9e3d51608a4a15a906873dd2b6582a6b313b1fc9d50c9f393813578b3a05dccd:eeeb52:96aab69f0e209470e58393d2101598c37b5b42282c8b5ec7cac7e49e2cd1c29c07a733c3b5cf839b20d71a2d589ce9f50ac2e34d06bff422aa23b5e5c6802edd8d1bfe59b4ffe005b826c082e4a6840c5251e53840acf1dfd0edea4d004f7d2a
cb8c06b21c49cc95e1156ffbd2dd883ed907dac4b7eb42ac550062b5d71bc91b:088fe06a:aff958bed7a12d800f6fb300f47345697d9e6a0c1b2d95de6e187995865ea9ba0fdf72c19c92a6af6075be2f99c0e51c125d65fadab50af20acf9700f1c841af100aa8778a90f5410cfc06c4ae90ee2f08b91093ad72d274fd16ec3eb89dcd56
c0dc13f11da239c34822ed610dafe71c5b0e4155e2793eed6d9c2d52d2ddacf6:d8e4e21e:a933a08cd8a869b373494a2c5c90a49faa8bde9f305416aafb3420d99babbbb5489fe184c76421d22c1956dc09b626da054e0a1bc4aea27a8f71740f4cd1eccf0c0bcf14ef0ba5504359be4dfd2315e8cc4c1050c084bd9de97a1a21f4ae44a6
ef9cd845cdc661c88fb7de72bda8aaa98eedc17bd28fe7ea83d3cd5c7886b50c:0f56e8baf3:8e5c457a6e9a8e4bca3a055b0293f261ce05caa99364ebce72146caf286f945845eb0ccf0466cbdbb27b1603a259d08e0668dca7a32ad3c676822c7751cfc0848be1d6872cc1eb3cd2e4b420d5d5272222f6031b0c9270f230ebec20c3c7fe0b
9d4df22f7b6fb59ab64767c21736b94dedd52cad168248e03b727a59bdcb0f20:d382c5149b:8ae1fd65d241b44a15c0c12bd3851d68ea160c775b6357bc147db64dbe665bd9f5eda00cb9cd6ec0ba3b19d5fa940062002bfc51c1fd69bf50c79d5cefc07f722635b3694ab327892d0c0824e3a36d81acb26e509c8761038cf1399380e29895
584f578299fdedd0311bab4134250c8b3ef4580aeaffecb540a3050ab09bca6d:047eefc2ff88:8b67172d81f3ddd80ec9ef70a36629546639638086d97269bcf8d1d28c15a33dfd6ec0fda6a06277b0d9ed68537e92050d184ffd30e108e28e33c3d3c21e31f537f6eaa58652ff4c91326cc8faad3d3a705b8a66d4b4287fdb17db3caa0e1d9e
cbaa74ed39b54e6a193da8992e923730e01893316da2fdd6c8d413b84607f793:d08941a7c302:98941b687685713412f11c82a2ca3f86189de56352e88e5f2888c7b323cfd782beeb6e5e75ed705576a90386411a2e3e125c257a00bcf698104695607a2394085bfc4aaec1f48bd4709be442dbfe1d2ed2d32e35cdd1b4759757d49cb1c7e387
368f0355b86d025ed847ebba2b7fba334ba98eb7d3e1dd7a4225d550434fb67a:0b64f53253494b:a96613ec8e861672d7f25356e438682cbdd2fda9f1cf0e64662b770910203b25dc886fb02474b8c9baef1e91d9bdb6ad0402c5702d1aaf4c640679e7a9c54fad7d0069226cc74da8bfbac94bc4397cc1f1c9c39f5e2d9e2d7fe40145b2905aa1
f78bc9ff5cc76fd74a0ab855f0fa03b65feb61ab466cc2e04213be30586a9a9a:80b4b35128ccd2:addc47e9ac39538d21ae3a98335f8b230902fa3bc1fa1861afcf195138443ebdab420dbd88967fb5ebc355200ef0d13f11d2f91eea7d8a04fee7c1f6e2baefb0435b6e8d09539f61c7ffca19af900367e898cafa119f4a587e78eee1be966758
0126eedaf3dcec0112ce5f44f181618cc5eb70ef0ef899f7329b3f10067c932d:09577238413c7bcd:a15c5c64ec5e4efd56363ae703343f1d87a8571347562d3aaca2ca4ea24c71249a55e9fbc393207bddfc696c151dfc420feb0f33f27fd24f184ed877bab13a5cce779f0a446ad0dc7577d5658dddf0179ef5bdc604d492d9a3a062194c4b5cbd
9973eb6a3eb08827754ce8088763e853cc97e6eaa1812bc32d08fc9ab26992f6:1413b6179c2d166b:b6629ea9d32194dc73ce3ac4e02858c7e62a09f039370c3c98b73a1ddb14c29fb59b9b10fa1809b38e4a6105c5aec96509ba4c83178297f17e05bc70ee1e0d06569c4bb276b3a335f076140787c466fe4a6bcfbf7d0de813133fd731e5b49187
8dc01c9f5983c93e04e4eff82cc4a79676fd247b2a144640566fae5c9e3cfca4:00b47ee092f96fbe70:98e8172d6f676503c6f31a73c3e45ede4d53bb73d10668ad1d04d1d0e1f8601dfb171fa41b19cc4253b18d85a76afff604ef55b8128924d6fe8e76926b4718682a5bdea2da6fba97731f418442b31897b749e88839ed04a9a8312dc4969d2899
ccbd235f576d3bc3d28b97334eca7a05d3cc5f2a709a1fece99976fdd2b9d773:198079f8da3599c376:a3ee5959fdd7143a9de5a759f28f7299ea48ed2fda18654d351591457c2f8d1961100e8cb17d5f974af2f3d4ed2d1f9003f3348a4db481614232949b0068f4b46e80e15479d28fabb883ada7404a2fd820064138a47d2f814af7ba06d80af0e9
06123d03bdc50fb3bdfdc8fe60892ecc110cfd9b941d1c183470e79ab184fd64:010c6409424adb9edaf5:854031eef2a80b533862df645b40fa90c50c5e9572ae5a5732b9686db5a479282b0d46bede9df391a55f220c322369a900a8c3ce642af7154f5ea728711da75a6463608f3eac64af631b47561abad7a30d758fbe0379ccc545839fb3775bf5c2
29dcd911b82d242702e281f02493107eda90028b53efd067f5a8b1b3b402f87f:9919aea3f2c254e0af98:91308917daa0c1f3833a3de67893c754e2f362eed5f6df9d410200fb9d0ba0e4587546cc88d241a1a0cb496960524f440ffabfc0d17126c8941a5cbf9913f547cd3fb59f856b46bd5d6880f566b88e419cac04a49c6ccf37b539eb3a80b4e7c7
bfbd2cdf05e105f2a71640421904875a1378f733d07611d6d7dc5576dd1d11f5:0c9e846d9c1b4dcc7e6b7a:8c7d64a8dcaac26a2e121204f40d2fd67fb6358d597603dc9ccff91e705f6706f0087938d1838abfab4f663f2f44b5da172572afa50585a26245fbeb0a25ffa20aea55c955eb311575cf8bfd5934fa6b139ce4cbde6fea3990309ab15c210807
f1f54419ce4518089b8e270ad7dcefef3cc06fc1b061febc5cdeb754d889edd2:3852a159c4ba27e465a83c:8b048ab27778fe7c52d1b62e3bee96a4eff22f9a53d76f767a60ac7bb5264e5278d9072f41db284d936109c47daa84dd172e41701bf9a44060a2ae91eab324a6f695d75aa786ceb0af68f2e109c9fe49ff12a01041da22560bc90570f4461518
a20a9c53f69394593e823610c1bdcfe10fae21cd42b9e6e59f683aa5cf86e7ec:016db3c02fac44b97af92c2e:860c4c3ddfb15c3d8f57c5cb41a9e8114135ff2ace9fcf55a9a95a676073e0b87705c56ac3676ef280d00f4b86e9208409e54f0d4ea7ea1aa90b40c835ec3e8f44d0c8e968fe0201a11312f9e309847add0433420fd47d8c43214ebfadc148fc
f4e297d963163769b5aaf4aafdd1969e78ac101fbb5f08f48d62f21b4dca91c2:a8d74c3c2d54faff2095a18c:81008cb4621bf919e027aaf6877618444953723e24625e6d1330554c599072d0fa9ac69d513d330cc102b6ae79220af10fd084898192663a701c599050c8cad6b7a37240ccecc2cd680474ae70d121cd64413d7428f32822467d6b695c890385
3caf69b84927248e842ccbf1e5cf6f793cdc69b0966ba04df1f7a2669e7a9a38:07b4ed6bedd1e2ef2db8488baf:8dfaa7179531aabbd23bba0e27d43e9eab3de719e966b47c0a76493092947137815b23f4fa5c74f4d2bd96ef6d2430ce0c5dabcef379d98395caafa8e68739fcf2c1f3e840e1dac9ad703b2e090dc5ea437e008d5b8fe2f0b1be6708552033fe
88dc6b21810e2bc476305c8a706b75062a0e199726041468cb8c4238771d4029:f1b293206aea9b717e147594cf:8de151ec56c6b65c5d93f6295f6a247a2ef2060f428b3f4e90ec9e5bbe3731a98af021c9f9bad0b7db054a45d17cbc37055fdc72b2683d27205ff95dd2dc33b5da84c1211e43052c8025f94c68a53423e5a283ff19cdfdc441e0a9917bce27bd
cc25cd8691f46fa1780253ef5a2354bc1ff302d1577dcbbd3ac419d48742f812:0cc78d7d6e414102c4ed549dd1f2:93c03bc32480bcc4defca136f6e668f683d54b6e11c3ca537c23c142844f9ef441beb1d7f6269001aac6dd1ab996aaa50011e8e18eb3a03f71f4a6a0b36b01109fb242e0b5e4ab161e4edf67c46dd0b3684c92cb0860ec07d766f36affa0b7d8
4d4792f59fe96d8f7418dee1d043e49f62ee63f0a2ab4b6271314f34bcb9409c:6930e7a417e42a0eae0debfdf30d:aae8cd57c22303af560fb04e7798bb68c649abb0fc1bbaed005db7b148e0a0df674ce75b8f023c31b8d4f35961c9712c0d6854d78d19f465ba844547acd18fb873cc1013b6f187ca491e5accaa20aac9fe43d254c916c9563d4ceb56b058836d
1431fe22a4ca0320403bbab6c29089ca1591c5fb447f6dc35d102959594a6d16:028773ab2e2754ac5a75a715d78bff:a53aea82c839d80b8603699e271df8c7ae119be1ef69c912d3c1a68b3e1c419fa50f10435497853206c41524cb97ccef19eeded83fe6b4af2392e16ca7e660d3e2c79232665c396ab9b5fd3594ff2ea93067d8c1080df6dc8ff55c9cce9b35e5
1f4d0636ba8bdd4ea49221a270176378c01a7458e3a7fe0ce4d4ffb088d1d4c0::99781a5e42b7dd8c022bd519ea9acc6008a7aa116245e8f14a12f40d87b02bd3ea071d3dbbc09db01395e6dd8b3485ed12ce0d5943637cae60e682e3d2b7530fc1966dee8b53b4f6f6f755865778691a7b5b3911801be279a0731a9aefaf8351
ff8045cb4d0a119bab9221ad343aa802467c900101920b332c0c2492bea4b475:07:acc9f6b5d331dfc9e76a94edc08333780f8242013cf98fd946a3d3043d42af7c70013529e6d5caaa4a402c30563bad4b05c1259a914dd0f6ad4e853b08cb5f52f6394cb6ae7c3f458bd2c50db77aea76fa09c63d243c7cf0c8d79d4ce8be6251
81561f34e382891d3094cf6b50f5f3fdcab90cd2d9330f8b56b8a0c9136767bb:1d:91a62de3f2b1d596ac3edf651d73c28ce925f0be6354118bd1504b69538ad81c571919ffbf2b08721cad0fb527c6d8af05e7f25478e6fa8523e7523d9fe79b2461dc283ad844a22934444f8c5d0b3a5a1d46d2a1491d6ba9819e968c5819c924
f67f2d294d27e9a26fbf9a1324a199adbacd044ccea3a20799618fd3d242729b:0062:91c2ecf6692da57fb464aa61fd49eeb398c58a443e473a7fe9c481ad10b8d8ea5f141329604fbf7267cf2a3b57a5529506719721259a3f5feffdc6a6800dd32e6ed44edc7a018218ae1cc72e1787604c41e7ce43c59c7c644327402a51062b98
2c8dba24af03feb0bd91fcf67d8b1ef2000305bbb8d4e0851ef151c21457c49b:aa4a:94442f44bd4ea1447f776ceb43590dcca856f02c1220147ab020438c7d52e61d002c4833198994b207422ab6d408fa9110b3d2316c740ebc11352c2658e0176fbfe9ef2fb880ad61c6ddc4ad362a5c806534ad8a37885ebafe89b2eaeebeb35b
9229f4eaafb7d35ef0d5a820b911ddf5fc6d3b2c3b49c745dade597025f8680d:0c0efb:8345abb6eecf90c13c93990b54ed628f9e052bd622c3eb6e66bbbe6ce8cda13b4d9c4c1c85094fb44fe308f7efdf009604a9bb1d0dc54eca54ed353293928b59ab9a4909bbf284d95f4f9cee588cc1f21abc5d506b2e7058c51163e0202afe1f
6968eed29d12fd07d53fba6e07fde6bf6f37d8a3d9f53673f6bd1db6c28e2cdd:84f05a:8ca8d57e7189f7648c2f2163d71b7868aca21ec8f196bb067f43aba576301282023ebbdb688ec4baf1bee5da5b719b1600a529c1169b0dbe796fd79714ad4ce6b67cce01c3fc955a33570923ad95a13b8ef0e7a953d56fbe3bd9f3fffdb8ec2d
dd1b8907a547de4826115194b15ac42558a2c4559f1b236c6bcde09401777b24:0a95e2f2:add320ae8567ac9902990d75dd7c1ea3a053e80f7b924d9d68fecc4d5569f341bc665939f8b9695d310273d93f730793159844b008684883620ef11496a624c28b11b92efaf57226263182fcde837482ab61d5b2ae5daf78b3a7a7c0c97d7c27
98d07d9f94dfbebafbb10c3411428262750973ac8f5bfff4f3218ddcd09d95e3:01ae77fc:b2553ee7517479b60dc95c0feabf6e60ff04d1576b1de291a220047ba19116fafba681efbad874fbe5b6080f5972adbb075e1ccede0df5f0bb87b0dce1bc0e78fcafd0ad06fef2332d415329ad1afdabc8b33d26c06baecd4d3f55923fdc9890
5770504add317933bdbe89f35463e784d798ce20b5b1dd258569955e8df4068d:0f709c6798:817f1def1b321a6cb1d47dd1558e00692605b0072f9fe2ac07859fa30d1b9da5d8b8fe09a0f7eed404190a72778609e1010ca80ad5528cbdf5d54e4981a52534c096631e38a9533e52bc18b2d52309cc452f03949f6d50fd96f0251c765aaf8d
24612966cd6601308edbd27423d622dfbcb27746b31b3e9f6939b571cc865f14:5c2b9e06cf:b6066c1bdd416fa78630fb1073c20fc1813596b6ce0bbd36c407ab18fbf488ad2f4cbe7526bfbf168c2014ad2f887dcd04825625e8a83728a7940c46a689512cd4a2025830a70df7ab84f2b76964f7acb839fd562def8b9021d3575a9d794d82
e058866deca74d7becf8ad839ab19345c6fffbeba03bb5551569e6a77fc271a6:0b1412c98979:b300256fe69effc5bcf218426c36c3b863cc092d087dff27ea21d57e63f994c7df97498a37382c01aadbf6556b50403f00f09614134b80e3f777ab8737bbf61a4399a29bee437113fbcb8062fceccc3ea6a5461b3716fee446a2101b0dfc13f6
6716da7e6965ea890857c0fae1c57828ce35279eb69f7dd8ba29c8aef11fbeec:2657f15d8adc:88198312d9975e2ec4823c5a0e490323e745c4fe41e75e25c4ba0c5dd32030b02597c90b24d2952e78c6cb91b90f920d13514e193eccd54212f7cd3a0dca90197f920fa7c0e85dd0c8a8009f1c98deb823a5eeb632b73bf44b102ed38c68eb67
ff076024d1dcd908540d3cff13e39a12544e033e5e3fe033e351850fd75fec62:0f831b8f177833:849a8c12b4f619a87284b881808e806b5b810b90938d0fa1df01cab27896374542ecf8698f20725ad9f013400d11643f140ba02c03bdb318f06ee71b9ada27aa1024d6a9a51c19b25e302c7243f67a07ab5accd504e16e6cc08093d58619e5f7
893af0a540d8972ffb5b0a7fa4d24fb180230acc3960f9f30d654cf6ae5c1531:58f13d5001e1de:b73f09066a90916c14f93d6027f78c1ef75f3cc0fd7b1d2a40708665960d9b6b09019a8652ac21fa30b8fa4195fc34261839beddc50af794652f0f3fd32513483564887f9ab1d81e9b1767d19211108882683e874a41fdcb193a9e82e741f147
c81191beccf7c48bef093eb0fe53536d02ac32d01ecb86358ba90303d0fcc10f:01d6170490dead4b:877ad867bc97004fa2deff244bd2e356bb30129b1c0cb0c7f25e9429ec6527ed564288f1b83a8bb144ce75d4bcab189005349b397fe1d8d219bd8bc07c6b3af414e02f83180fce1853005fbf3b3e8eb62e7ec1f202db1b86b7fe5b3d23bf2da4
a3f68634413db07bc22c448748011efddc9c0ff5e3ddff037559299f16199373:9d97a98ed3345f53:98b8056aefca86c699804c92ce7d1d72c4457c12b58e8768755e64bc4be94eb05e25e0587495c469f129210c2b6308620fcddff4ef61db0b3fc77b6c765c83291edc33a34e3d18e8cfcb7f0f51a89c0908a617bea3613f8f91caa47071d47aa9
6a6f8a98610a5a4acdf6da669ea3990fb299e1aedf2ac327c97b0ce96adc753b:057de0b81f4f9a736c:b834e23d96490775c3f0b2d674d2506149d2345f94f157c3b124759ff61fe2f3d0773b6b9bb0e1566d370fdaf273f55808fff04433d5f8a5537a3142cb299e8996c42a420ae36e6c9335d2e630600c287bf283b745f5d5985b7819a6e889efbd
6422d62c14662cc8ad1fc10875ae58813fa9ee349c019c452a917d2b7ec98404:8956593d05c7b5814d:b6abb8bf4ce2feea01a0f6fdb881cd276c6576bdd92e460e3827f6081d44263bf21498d5bad1c3b534753214f30b15500b0bced8b763ebc1b292d5676021c1a2db09e35d5d3f306b4d1436087bdf8b4720e4ddf3bf89c323804dc006ce9a6a91
67cb55964dfc93f1ea615a89ea49b26c677ccb4a2a0f512935b45b3a54bf70a2:02c10d2e5e685de181b0:adda564e2367e13037727867cfdfc0a22be7ada7670a01a66f3954b54745256ea89ffea82dd5e68e98f4663fdcb4ca3d017fd21d25b491191c69fc3eedcf8064a5991ae58ae6ca81e05605212e166a6cb5d53f6f89fa96a0764c26674092ad45
80e2aa0ce9bf22a06147e0b3d5dec44922422e976eec32493bb4f70a2c205082:385d300a9ac9f61a367d:acd05f67bd58438b7c4b1fce46505437ae7829a1d2f114e806cc46d2ce5adc07fa5cf6d0a08da486c9fb792bbbf62db5106294c6524db1838225eee010bf6e70bb99ce54efdf74e7deb544cac38f9b741412c0c061aa70fd0e151781746a08b7
f8722dd9394845c2744e556933d4d7ed838e983f7088fd2ad69a769385005509:0ee559b4340c968008cfef:8c96413e8262e36871d252ab2060117c8590f4b3accb11db0c702a7bc6d206c4d767c4127591e17fbe81a0f2f6cdf436089005dbb0ab266fc81da3cd416580228cebc1e48349894886856993f43bf1a0b5e62fd89ad0f205917f295f2edde990
d9ecf27ad6dd98e0ff55c5f8562eb5a7e4f46ef38a9245dffd788d6bb638cbe1:b8ca56c4258c05963bbd68:afbe6f9dff95a5d29af76a07b7400c75ca49e066186b76274182507aed8a9c32bc90f863eac72d22ff69e170caf58e5708f6ebba8aa69d61da0e1bb5f7b7b364e38fb6fca3ed27c8588a6077e89adb11087ed0e773aad50fa57a06e7ebe3c64b
295b144cdec443e032d21c906e46b3594a1e50b3e6f29118028767f189ce12b6:07d1ad37496c013d3f099071:ab4b0c1a0402c5a63a76c88a9e2c2f36b20f22f1a1de61f620ee9634605d7d9dd12e27dc2d04f1d96538f9d28a1e93a10d5fb540051787346a2633759c2fa8a2f85db615b1e901158b4c22455402cfed378f4acdd4524de4a39ca4b18644bd35
2b614a7871bf0d74e53bc6c7714372353b4a22ed47f1f627d3bbb1ef7532dc77:624a8baf7884b4568d2e6b31:85aa088a9bc03c585a4238a49e5e6aa36434609590a3fb8611d012442ff6b841d23aaed5011df2eb61e2be3389f684f600f219ddf20da10f8cde2dda151c74153020e2cb7e804999c9a99bd02b12a11ed65f8b1e73f8b27dd819faeb46ebc7bd
422ad85c60720ef2d06cfc26ad0e7ee1af49cfd6ba025692d405b1cb41fcf71f:0ddbe34431448b194f1b29bb60:b8fa03a4ddcf857e50f142ed3949c3c43937ddab3b67491634137a478b0f74ff12a4eb17d030bdb3859ce2a3ceff5c13136b61832182c1808fe914c5471cdb963870f2477a6d59c842e4e23fd7558488fdfbb46899aaaa67c55b48b9c94f594e
a7f1e76ab8164099b21ff25319a62b0b77afa45746dea6c57964eb605e6ddfe2:f817dc1a64750b045dd8145104:933e398eb69ca41ed5c34a4270bbcfd64bc0446e23c754703925b354097ca7a2ee8ca253eb279af79a5e4d2d907acf1b0ce632630899607a6a67fae6654b90fb7bb37578a1c828ac3ac650c752a645808592304fc7a32d6c1a0ab530fa3165ac
b0eb65f46ed2a9931220920e3ec70edb39cfa90d04de48f53bde65e2fa3c7eb9:01cdea387f4c40da7949aa79fab4:b9a0c6a68ae34cb6d2646d9c14655b5259942cadfc7cd0982af5842894ef4fcd3e9136e83a1b6bdc1d9705e5843092c308fcf46df2e5e4b99534ae6acdac11aac1d558073cb0632f496307df850cbb8c199893bb8313f1db890cca245a174c61
3a6ccf539c217a904fd7a718d2815c985325e3c9cfb7bf458a219b8ae78f775e:df33154deaa3d687d45a694ab80e:a269482d849517a2fffb267782152bf07daa7bbded7df8a18b7d74c9e5a4f46c09d6f795f7a9eefb8df107d10e5598f203cad912865152985d407f3d7303287fe9b0c16c650016bfd9dab49da95665e98442ac1e10ce427bb16c8bbe81130c08
41c836e9c4069d15ad4e871a12a81fd91fa062ba39905b659ece6a8000dfb125:01214f40af921429a48897d9d286de:95a2dfaaf34691b69b9df2a3356bb25ccb9f416e0c619a5f6bc1a25b267d95de7c5fcbc50d0e6de1d79afbb083255b22158ede7ceeca628a162d3b7d4887574e7cd947e1ee5704b3970aaaf92e47697623089bbb032ee1ca15505604beaedeca
25563baa30cdb1664317a7f6a11fadf24dbd7e33f758ae9edffb4a3bc6e47809::ae9b09587dde61b31dc3a252ee5da28b118778ea40e6940695c0cb14e927810dc1bdc3580421e6a6349770e468ef624a0b1edf2a2fca77e3afb1d4cecddd443db8c5b1e92d0bbf123f9a7d3ecbb94b6d7a72bd0754bb5339e807baec26fe6688
324c59624d455e439661cd33bf2691779493f51ab154942bceaeaf9a3f8d2e5d:0c:89243606a295632aee72da9fa717b04274ca61ca99279b75705fdca338a6712f9d92939b9c868210ae1a80c2a315f9a10944326c7e753c7f7a62ed69fd2a0e27321a52f1305facfc2fb091722bda6f1edc3ffae29a990907d7340715dd763e0a
e4168522cbb5b48039f1010ca723f64b540683a964b9d1ace259e814f946cc4a:58:ae7062fe9c1fd3eda7ea4aebfc3076f105a975cbbe9203dce3014a905fbfb753d94ad6d0bfc6a4ba564e376b422c326312c26c7f4eebe2737cd55098abab896823ba0862c6a064fb0eb814d773ff08199ae30cd992b8fdb3a98f155b8ff85f87
3d7eff177604e52315b47e30500f9d1419496f5da2d9d2b4691b9e840b5a07fa:07c5:b5b0f787446da8bae5a3468d5efac5b06769486650d42ca5d0e8a9b1a7b4c47b9e84def60984469ec8fa311753fae2ff0feb1f42f107114031d8e6c25ed3d7ce8b882d05bc169c2fb30e301b5555211d31e24c4eb67242d7ae71770426336f4e
cb047e496e0b4826382c35e58dd5b83157e070ff2ab4a500c775878b14af1382:6dcd:a38eb229aedee1a3f23afbab228165432c06180a41b5b8ad600a4d8ca734106de42e83d67d045c3dd5e5877fdca506b10a1128f199cc98bc826e3d42c57cee4b118af6be1b14eaaf0f76ed7bc02835a69065489d0054c74d4bdb342ac5e6d540
90cc186f844b3e92ac371f89edcf9b55077ab4c43e2994d5824cd1a869969639:0f9f81:86f99b1a30d90328fe106eb036e4016ce2f58ac11c312e863f67886e89db2f61317696cf32f994df2a3a15d4acf654d2115681df1f4c99a5c581da34ccd08c408b8fccceea7f23daafe8a2e3797d768e04407121227d46a77a2e6464ca8d2dbf
9a88979f86f9b645c18435713d1d9962e00b7eb668c9990063cddf048c5a833a:e35fd9:af1e6549066d0e1e1c795e05ad4efc277cf63ad5628b944294bda5c6c2cab553cf3c16ef35525190d6b6ae838d8d5c88007e4b33fff1d66857248606a4fd7cc6f3878265745a9e1874a1cc03a870b192e2b1bd1974eb380937222f4390463020
dabed287fd58fa6e4f66d4ee5b1761f660e3bd29021c1887b317cd0a5ea8beaf:0ad95982:afa79271194f2d89f7ddd9af9db947211f24ce7ad576a157b1d4910c8c0b6f87eecb28f6f18a111f74f144fd0b1724a8007027c1033b055d2fd467f7c1ad60af663fbfea59d13d16fbc291828a8f11757fcfca6f26b6f3567dc42e05d9feb70f
da069623c0621d0e18beba8609d0020c85893c4929b5500743101b87874cde8e:85c588cb:b7431c652478c889a562a8a1d3babfc1037cc065c2c850b334ecdeaec04311cbe3abc764a465ccc54d7afddc27b1d443085b32d605f1e4cdeff84782b2ce844efab05f2b4474ff70c037f5790897748d6af6b6417e8087c5b0c8e6cac0276e59
a0f21a36aa3e9393082a284bd74357c31dfc34e332dfaedc0f06f45423fc495f:0121789325:b0c1a71f1a74a1e8c9cab9f51bf300ba198a135e759cb1845de10ff7160d13b5f3e0edbd63b023d32a178b9c598ff48d1366afc2b8f0bdca4c2f1572377baffe19daa9ea0b81eab56eb8589a6278ac57ad9210b763d9726073a091b4e3dbb9c2
017efc554478e7f73876fb6ed0b036ba4c6c653f5baa2c13c8a705e8d936900d:5298548f3f:973c337a1e2632ee2355af6a1b259c764e3d1f99ba9e1c9840b1e3df0d27e3eb477b670976e3d0099c49b4eff639260c07f19d0c474d152ce3656f422ab27e65c6288d61760d5a96e8af9560f4460ef4dd53a802536fb19eef9abd257653332f
4d7f9479becad152a443f2c759aab6e02a1f0feb15dfd49c11804d2a104b39f8:07bd8740d5e5:93b4364b292786fb255cb06ba55cfb0805923229ce394fb5370a3571c4773b7d8b6801318e6262a4499d73ff181b3bdb09347f73aa4a2ca7f6e99df8be366346ae43ca8faad4458e2d30d26adf049a7542e3f0ca72f86006cdbac3ceaf038515
b2e5bee63c6c00e5f8fd15703ded33d45037da065cf3f498ecaf72a8e9a8f23e:7d202c324585:b0981781fcb2e04e418d7043602a61b7a14e8d5cb2151b40adf729689729aefecfa1063ba1b43ae571ffaa492bf81c14121fbb6c2292fcd0d1c853c37b9605c0f05a2647279de3d07c2f6855b5a81e019badf9d813757dad66eda78d02aa072a
63c95845cf70d1470e0f6de57729b5838f977a8706c1979388829f9b5b3700e9:08aa3ec1c57996:a950729885949d9a8b50a031ad184d1887b2992601c0abaa9e5b6ed71e5beaac0534ea2c9d93a7c564b77d0c5c77bc5818fa164575d3d58714687c7f549058eaf3d0fb432d328e3bc8da3ae7646ffde2297421a2c418dff28026da3d29d1d498
22059d7b3332f4def25399ce17b1e1481028ec4d2d8ab835a69b7ebdaa16b461:4195fc080b251a:91ca4b0a098ab6678c54643b2546a3535696b109bc49f3e50d8e6966a56ffa61ad37d2eb29b1c6a19793aeb30edfae53021b2a322a300bdae1ce9e9f67267727e682a4568845b1312e652ba439f6d0432c36342151234eb639c9cc8153ecf743
6bc4bc3efec07ce22fa5a5f0e0fc4a4142f8fc4c078e6915ab7f14c9743752da:0fdc2eb64cd67bee:933de772ca7a3f1fa61c87a5a4ef914b1f6074facdc6a98c6e8fdb886a3e53e5acd2766390d9c6c8e8b48a268c4c227b056a5f4f7895590e48b44863d26c44677091acd4585a85c88c8de2ddcc1c530b918233d7655e858ead521bdf1d66b526
9caa571a916d4005e08af68c36b8a65bb7ffd9890758ec276d30278a2107ff64:5067f524af933a64:843b7097035540b7c219e82760cb94aa749cb6579301110ee51984b8b63428207108871ed1fca275ab05850e3563a09a19b32457285f15c1a3c1f74abf105ede27dc47ccaea5c90623f5d038bb00e8a2277441e165cb8327e86f03c2f0dbc1c0
2178be06e144b58fc271dd130182d6ce562fce547c545adae7df409c61262876:0f72ecc940599630cc:abb38edc8b7232a479a51b23e3d0d5fce3e6967190e2e68df73fb95e3e2367b2b1b6360fa0b06c508c664d90f8cc345a09e8b8255945b850ce8c1c5e389258767a709810e519220abad9ea7d9fc37d947038634ad059821d591a671595df762b
1a1924318927acc40cd3cb8265dbe703db0386b0d94199dba5d67cfcbb497032:387376ba00fc30b6c0:a2e6514012c1fe9484cb9df2925a80c1451d33cda78b89302cee7f59aea190ff34a71a6c4a46a77652785b44dcbcee100aba9beb37612e961bf83139abb690449e6e05a351d8408939585b026fc83b1fa0ca0d0303295503c65a40bce402d12c
9e203efa8eb1e65d053eaf644f9c3ae67c084af1a3aa7b2f4cd051f4ff870bc9:0c41bee51ab6c3976cf1:8f6b876d190c548c8cf130bf3c5c315fd6f0a8f6610d1cc6c0f5f199912fcee078f5e4f9d8b2d8b239fdcdf43c6efa9e0fb4fbb894a1fcd464321affbd64f07cfc63968fc5fe77d8c5cfe2ddade49f28ae60b7b26d97049b5964aaee79f244af
30e446fc468b579fdda3a09ede2c456f9799fadd07c069c44794c1cabae3d555:25538c2c57aa03118f9f:98eafa9da5b2802b9932fa056ac3c81f09b4c4dddcfd5ad963de9374731c11720d7b85bfb5e3955c71f81a86f32b4e7611022aa8717dbd032e4fa4635dae9209df6ee3dd34598fdf504cdd4aa3fff1292d11f74f17af500703ccc127faec4c48
6440fb50661c3b9c45c557d216accce15185891f98080b6a7c1c6dd701e029d4:0a9333dc2d78c73c53507e:8e633dee7c8fa3270472078f93dd5181f92f43e7b42929a9acf42563367256c59b3355d23c18ca8dcdfced828b5009e4133de13a3e875d2a6880977f9a0f00d17649550419ad5da3194df083f685162ac7241d864c8c72082cac5ecfbc581db8
b0bd58b8665f372d71003e10b5c11d2e01f69d2daf5ba56e301f9cf433bf6a21:16cc073e29547abb287b26:a8080f4cdd6d89dcb03bb389be65930c6161d881d149673e8feb89ae07e70265016458b74734ec83d15099767e0c0e4e175501ed542691e4acb3446d060ac6ae971ebd85ee25a507528dd3f4211c608ef572f49d2ee894a5eefcd5ab86db9c50
084caa7f9ee6060620c5db1416f5cadd4a54984c23dc804cf23ac5ec645cac2b:0f3cf7f7ffe9279288b56ab8:922c3c5844803907b8b2bd08df8edd00018df0ccc9e515faf8814ed865850fe7cae1240495bf4e947540c325a536d0020f1f15f5583cb6e5a127de49856515b933425ede9aad615c9007c533c85cf25a6380c7f7e45b48d644871f7f72152a27
2581c1782074316f855d5d184ed4066962371b58935c672672943aab3a67d005:0d85756630cfa82ed2cd35ef:8ff6a1d75fe9ac8362c40d9405b1d8d3633eb6ae08e12b9008da03eddf47f632d780e6f7f257d27569ded6414ebbc7330f3d3eee1b1bfeed420288ae89511afe701d34e6973df7f9379cf5027ba456cc958b09288191b4a7ae157db428363a99
bfed6cdc36912d4f49a87cd108d0f8e51ced50d5870dd80ce98a030d0b46fb9d:0c3255c7a617e8d3f717371300:98a6004ccee00c914b915f5f48a2875fa7f5849bf1bb02b41fef83712ed2e352c1eb3f1613697b3b5b77c0e99a99b6550a5e76d894529566c649ae4fd29d869b84ba5dfeff543db07047867329fa3f307dd4befd03f0fe71143ee46160f2887f
a4ba0fe9090e0018df0e0d763fbd11aebc12b3f0c807ad0995db08a6bdc9b160:27842d981fd9a812efe6391c71:88b5e9dec395c4fe67641a618c4512e082abb8ea302755128ee6d2c0669b91424308b3bcc0ce6cb3058af4099cfa820603d93b557ad60c3e29f96131c8838eb1dbe9bc404abceb50f1ce22cebd6f970c107323731ef9ce736d30ebdc8e59ff7e
ac42165d2ac95f081108888a94d0a56460400941c57f3145ff2fb6900a7b0c8b:0260471f10e5f35305906cb94df8:b62cb0b876a1edf2dbc35eb4df5627ad09c8e6d8d9da83a154c4c8d93e70ecea0794f412d53c50677bb064a4038abe12106e3581e622fbaa6fff3f86e06653b890defbc8cc4d190280ebc5bb4431a27677e0e52049ffaca5649cf75f22239a0a
396f95f097863db7d343484be8ea7c26d2db9e022397e42845a3aba66eac2165:e46812d0412dbf137a84b49d4b0b:abd154aac031f055c524185d56e1e4f259800a87bf234b04fcc509181626d4e519e97c7a32da1fba04e6e322e0a8760b0424065e8b99348548ceafea44d107bebdd119d47f7df4d27ae0e14b5fff497408e8ce381256c41721477971925345bf
a94eff505560f2686a73b2b56598b1d43956c93b54c05e8e04e845a517d2f454:03e19ddb791c70b3bbb0646679efa0:862897bd1566b53a083100384352ccc4477030f463c21135800255d3812876f33388007080aa431d84e70119b9c46759013b6ce9f5b68004085f1fc009ac06d3747c9ff008395ae6de90417a43e5f0dcdc00345bd14e8e1cb2b7598e1eb91bc4
3b79d30b7558a89767dec3f3d1f3393d13ee5e648786ed6bac49186f8a2ca2c3::83ecd612c577ef99af50938c218bee6b7693d2d5aa1a06144346f6c8e46e5f741ca66a154106ad512bf5a2d9357b89da13b797530e42d298f6f74559cf5452694de538328301df16a8c91ffa5a6737befaced0d81ba45f68f3587639890f6ce7
164811a45674f804c655c8c5ad3863b359ac9041c7f1e72c0ae20f80f41170c3:0e:9359a49c73685f2a8a9b6c46fb2773a56061a586ad1b7eb38d7789d23260f96741f4046ebbd119c06ebf64e9ed7b81f510cf95305be70c83193faa2d16866e76cfeab918c608d9b4dce6fff60e807358ae142eff4a8b55a01bb5da982e89aa37
07b300ed4a2db3bc21642adc74521eb70ba2c81a5e0f07b7759a80ecd27033aa:05:a9a3b90336d9ef805992939d52302e641c74c10216cf320696ce6262dd1ce58ae82099c2d0575892650a13c6daf2017c11c84edae1256b035f82d467737c7586992d84d0d521882ac2c0babe65e06d8d0df64963667972ed2670fe25b8dd2804
ed01008602bff20ae9a170fba17b328d675136e194d29e4663f9fbfb02433de8:0fac:8c25576acc891e64003d756e0d74e11b03b08d5e56db3dd39303e589cd8037d21be1fc217a94e040a9d5cf544f2bdf36083f8eda0481dd96b84df5d598d9f8e717cbb5c749b1278e6c023413d58ba47b8b513160f84b407f22c73392ad6fbd67
1ebe5d4cde886b9f5f4555e7c02bc14987dd021999a713d020cf3c08ff9a4f60:405e:a49cb23f9b25095c9d28d94ff6b1763cdc257e5824350ee79c726fc4dfa4307ce365283342978bf2534782898afa949204e5ff271d495a577dce076bc8b42ecffcf5cc6219955a4e914c42f41c3fafe35a1464ad06bb35032b56408e44fa8ac9
34df86999c7c40e1f0d423e8b788e5a617132c37dfe7865e4e6052b7b7c0c08f:09c6ab:99aca067bd01d52542141bee5e7941265cb45d058b090e3b5f2965d2c92d17f691e10f489148cac52a72c0428c470af2071d2f484b6bc772a4ec04cd9c9910bba345603371925c1c29789417f507d8539cf5981f07a77185b0adf8c9f7704199
8edbb41c9d6f871f1148006b1047388eba52393561f71c41840861108bc0baf9:6e29a7:a061b2b9f869374ff143dda1e43f011f9326009c7f791b561358cd32605f7fe7c12b5822cbb3154c8e145eec99c0a9e510a3bb32c906331bafc87cf84bc90633bc61c4db5eb38941ba76567a7e29f3b9167d1ddf2d089a770e1f4f66ffc760b2
74f0fde07929f00fdaed639fc0d108b53e44bcc264326b5be114dc766784837a:02b3c8ea:b2eba7ea2a3d079ed7ee8b151d66999a2e0038793f4659bed206c8911f2dc98397d7f2c9e97f1d7a9c687f4b0572d3f6084739d944f38d1fb01a0055a4c346ac54766c497991e8779d0f563d9678a5b47acdb753b1820a82f1295f984ab0ab08
028927e8f7fa65d681ea43b9f38590748308f9f304e54f52de2ba454d0bf966b:fc76ab2e:8163b7efeed623df2c44a5cd27568bda4a11b66192fce2b1d46ed98f8b33c7823fe124632bdb42abbb6e1d0a0780d1cc0ee9e9ed28aa5e818bc8aa24bbd8befc01f8448f4b02cab157a3d6c46cb52e085ae5c67fd8ebdba1077c8eb87520d1f5
ef9d9a26bca8d75efa517f150e8da7bdd568e59c2f82ed4fc5212e72910cdea5:0382c632af:b8e3126b58b22a853cb838c737237f1a527bcd4c2c8a4aff94d8e3a30b1c53181c0d28a8e8c46fed2e083a3a6219ce78030eec3bc926efb24b65a541e5715849398039d974381bba842a25210b1d6b699721cc82c4f0cd43d5bd268602eda42d
ab5f317da1690eddc25786c39b496f06dc6e5a45c250c0d16ff6d02feb448463:3190dee04f:b263b2d49e3f384e2c73e0a55030b3cb479c0d7a10f6860cf148c3bce659d971f8b60c1265705e18a98d8b7f3949a0410e3b34dba3ec3ae66065bed68744281a5722cae8042fb094b03ceaf4435bd6bd1b4a3c6a277b613c24d1dd0aaf6425ca
2dd2d83fe918f321e588516529cb8d8e2f9dfdfc7e24b03ddf4de7d68feff2c6:0eee5a001b13:8ec481206906594421366b291c9921d6044044b40c5a43f45a603eca52de182cc744b38d80d1e1f2346119d3a127bdf0084369d7d317a0664aadb36b0fc421c6ba61bdc97a406ce1bd9a05a351ffca89a78f21de6c06f9da474098c343918c39
62f441d4421c9e841f5e6afa99346dd757ce3cf4d958815107f80ec965d52fe6:dbd901bfaeeb:85b1619d9be7bffa4fa4dfd3be3161d1be679a48c3e0bb5d637f55ef35ce43c86611a21d078f178908ffd329c709dc8214784af6b2603b944d84a6b70837a7c5b0c38baa97f0b0705c4619f8bb44d1f91f4b248d60ba3bbeb65f3fa189044216
ccba9a32e4ad8827d36c5cf800d1536efc96f13bfb04646d39af25fb710880cc:086f8a51380947:a9d85d39f4a330011e6f7efb91ddac0164ace73e1dce16798c41e55fa7318a04f32700f690c95ae10542d19370e583c00e004e18ed3b4161c59e51e14fcfebf5608f403da4d0115a81cf5026d50b1f63aabb90002e89e47e0b55c136d5af98b5
bb8d4790ca2b403a262c3aa0569c12abcc3e29e39caf9bc427508a146765b67b:310a7e2f80896b:ac5c0a6ed31609f8066f35826f23864fc373abb0ee7903e21890e21606b28c8ae6c3f2e85053f0b373c055d3caef41cb19a6f4076acf0298be6bfc5639dcb472f8380a88f73d48ac61b2abe134258124a9e7679a71c9aca322a1f69ee2ccbf75
2ca95104bc934be172b9738942c9d929aa6c63e8fb38be3435aba875613fbfd7:0fb528fd90c1075b:ae6ecfe94e796391077d18644f61bdd6e9ca6e45c18856af798811199a8ce77dc408bd139c92a086b1a8541104b3b7e6187f5e6484fd5b3bc6fe125a78a8778ae7a658d74cf0b8bd29509996acae061af6c1cd26365defab654390b8fd130c41
d02dda8a8f2a6934fdfdde0f0ce44c42c32d035e5befee776e151a1bb6dc947f:a5f1004395f76923:b4755f76590e56910f262b9e3203ea371da798a81899c0418a9ae5da53f0465119c4c3c76d3af1eda9777a5e69716aef01112a650817413353df188420956d5e8883b9625d38126f2c9d8e655a499358198c72a3a8702ba14ba4b5d7b177f264
89a57207563f897d97f8b0f4db4b7ade769af29a08978a4732498e7d7865afdb:0914983e4f1f86c26b:95ec831076cf9d05010609f689a864ffc41af5dfcc4b58d7e7b49c9f362c2beddc46b872611a6abbcab9ef7ad80780c9005536dc992a0e06d162f54a561d089636b732c67a4faae391ab31868959f7e777ba44c24efa5778bbb719f7b25a5bbc
2cb2345b9fe58baeec3e0d35f6c2bd71f7a460eecc1c1f24687b71c6ee8dac6b:8890d3f6a2efd9c8e5:94e89bb4e2da9fc43ef06ee1f02377b7645ff17d8c02e1c3035417c445279d0ad26dc4622fc864de7b93599dde21135218024b6d8e9bbeaca09d51b4b616c13788c7ec29b63eb8f9f87701010c7f6146e33a37a7c8bfa5920dcba068a304f1d4
c9d26fa9f3309740961cea69700981cf51e500d068e5728846710136c4b8beb3:0a4515657fb50f595c50:a435a4692f4f8befe3bc270e38ad14878020d0486887ebcb25b066109312eb51b498813f0324e41b4bbf48297d2b52500938a84c94971178cb05da675fda6fece7a9411e835db608a85ea53340c560e0be32ed4869d00621a15e43b64ad16d0a
54079aec7ad7a051158086c3986871746ba540c53c519617a01e559a5cb96360:d449f821249be81a006f:9679c59342a6b68f9350777c5a85d30dce3a9d51082406f39fd21a695a9014175e9b61ab668a1ecf06b0f571b3d5f6a80c5c8d53062129cf3665032c36d9a743a452f0366bad6de34a8264de28a1283f7d1cd584a070611db23322af5472349c
aa3b143ce985e2d67b716e5288b0c764e67b93f0e3794f65da0216999d7059d9:0f765189834cd02d2cc4d1:af2de4278f8b0c3eccc5ecff9b35efa7215d32abed6af041bfbde73e60137bc283a68666909668b9ddb2dbb0c9691b75045357dbe068f9cc723177630b0e8d19eaf3d3b6ed1c695b658b0c7732b6e5da17a8372cdb72e8b8a58ea72687935eff
608544d7bdda540aaccfc89a03a47946636c732226f4841b31f47a247e4a29ed:5dca12c82d6470eba98ac1:8dae26cfdec9849d7d4701e85c160f3db109aa9e4e26d9f4ab2c158008df740fa526f71541daefbfe61553d23b3fc94c194a3bc097ae33be51480c8f91804147f88e95b054e88ee0faf3ae718135fdbc92cbaa26277f76cae42315771599b2e9
7368161af24ad459f868fb847e287532a3d7625fbdc97f912b513f5ce525a9f0:0d43de10726e1ded2f111aa8:8bbabb617042316158491db3e4f83ac5b9ea8acefaad2225b2663d500b6119e48fac354adcf994968e3ddd88e20684f805a88ce038caf4bdab372fa9444a471636e979f1862b1519efed6cf003af1ab9cd66b9707a8ffe4d9e1f9e4a4c59ee8f
694d55ee8633e2f3ab91c1f79953d6368875e40afbb6e4674109132d5612aa9a:a72ea58ebdf4589cc1b0dd9b:b89f66033ab23bd10456d1cf977d9fc87dce07e1390f876815875a909128f4f5e5f5f1617c8862b01daffa868661a764097139b2198472db19448e7eb9449d32334193c10af650f56626dfa5f2a670ebd2eb1af484afde7f0410c2695a485280
bcccaf8728a31871e0bb250ef42ffd55bb708774779ce59ff38679228312960e:077df870cf7308776ebdf15493:8018593b2946f374536067db76f1a45109b9cd7ed67d9b808c095ef51234c563e9715c62deb7779fb9779604f90c5b4f0509d43078bbc6cde4de440b9409619bd198b0e1bcc29f1ed9fd23f64dea82a888ac53d5f4e5ab2658b01360af8db42d
30ddb0c578ed5b6d16b7c923efcc5003494faeebbdc509704126599ed93ba6d2:f2fbf39b84983f5edbdaceb18c:ae7880c31a54ab6c30fbe220415fbbd33a9e005d565f9f875c879b8e9ba20b22d6e054456f8e780faef0d30658b5e7920a9619f6b7608a9f84fe37ec1cf890f2382e8b77d570d6ff7cbf595e091cad7b74e70248c974825f8a93e9299ab2139f
a2ad294f7cd59bb04df59146e2924d7f212f8ef4a06a9fdb95bf33ce7344d017:03290c3fded14b5450e5b983a389:af9c61b820c1cfd192e134f0976a949c7ff86d8c558f5fd0a5ec70b0672861494f9f32413379b915896ff7c7e4ed479910d1046b001ac0a6d3597b6f5b8e5ac08b532481dea5e2a79d09f9467b930461cd4efad286d141d81682eb04127462cf
abd98b2f6a1b3223890745d67beec62637812d962b69cd589575d4d1edb81f17:3fdc0e101ad52086e0f85568d583:8640c6cbcbd2cdb1ca8ae5436cf1f6f27f9a17bf1d8c9f3e95b2120ec4ce91ae3a7aa3433a0842277b856691ff7f220a18ada5cb83dfb0fcd1f47435292f34b48806921ee2f5956e166f04505c5c3c407a08516c24a27e8973e579547095cfe9
8bd98c4549f6d08588cd6cee5866c71696437da734c0835c3da9fac963de1a54:08a83444ae127c9fb9e9df7c73a82f:845f8391ab33e4a0fc29c5e8d9519397765c043e1a9d8eb506052808838266857a70109c2c890e3a815fce66a3e3a8c618131ca4717579997357a346ecd4e4beab44e005fdf929a9db37e80c8e70c89114ce7527141ce0652e24e22b2569db8f
4a7a6e21534410573a72d8af48f592489ba283a9ddf4cf8844783b0e4e076694::a630c152277ba70a0316f42a01e07b875bc31b8c5dce53dd5001818ff5c4144017c848c6679004a9eca6edda08dbf64c0fcf94f891d2e957dd2988f2a459a51836483813f211690295dced48aaf19ac31a1bed284ec16b6465eb1fa9c8ab67ae
ce3bf80673ace59c43bba9866fd2285c0ff341f05f19339873e6cccf740c6f9a:0a:8791fe3d88f99ee59e1e56685201568c78562199d4473a2f8f0a22e48d4bd8c0fde0e203a93f37584fc629beb3e6ff4b0a80c2bb4c91fc60f64e4c61a98851e7ea103e679b6a90add7290cf655ea9a32441babb5604e56a9006ff0b36552dcc5
ef5f9e3816346d71607ee1482d862a27bbad5fac8eff68fc8c09b3bcd79fec6d:a6:899653bd64772ff175f0e4f209edd6a09cbd434a58b624053b5856f4b52f20f189bfa257ebdf5e1e7abb61d7ba3b8f19052aecd1f18fdef84cc5b85fc155fd333007338de79113efe2788f8b4e54d0ad5d453fc649809612140d63920e85a5f5
f2ef5731a993762ccf4762e355e37c13d33f7d17a8edbd404ab7555924a88bbc:017b:b8d6ef60ac2c7c1cb84a6158a9c734b5ae008023e94b074ced09df46a61f9e296180b5c77da3c93f35fd6d13850fc081118403118f9ecd22332fb0daf53a0c3e87c148bdf91ae849b1736be9116ac1c0f9d5fdb6e8335a7543f11281a96e2c18
7a0e9e9f87d909f7d3107156107407223e538f05069990969db2248e8aafd2b1:3b70:8e7bb94a73839d0149d443672d2db2527c59975054a79bef9f07166d309f497d1b72a3e63cb6379b783bfcfced66c764023baa16abff7722463871a7dc88df8cbe00eab89ea0cba5159582b52c07c4fbb4dcd724b3945995de8dc8f9ac97a2cc
bb788ad68da2e63b9091d7e4ac152265f75da7f4a47e53725ba7775fd47d0406:0fb581:acd56cb90039ed1a35a8cd6c49bdfd09cd38bbedcd597030fa06dfaf2719a67b195dcc347e03208d5e1a3950f50f76230cc308702645765d38975bc6363f659bcbca47fd61e09f1f4a7cf9883326d464a3c3a5f37b3253e4a1a28e7cf65ebd95
064588d00f156a6708a9fc75c95e25050d795f89f427be6df0948500f7d5665f:b16a2d:865a1462974af0147f0f382dbd9661f5864d7f18d309b19e332d2445b8c150b3c16dc7870b5aa2b1d5df89bfd600f24e177c08e1009c9e666d755797d8d220fbc35171c1415c9e183e86cbf2a06f9ec574e4a462fd942d1801e7da6dfa931972
e1b5abd337c95cc49b620e155e5e48e1a65c26ffaeb3715a11c3f8c4b451d531:0b3d05b1:8e6c2eb8d543f81bf0699af4dec440da5a33e3a15151ea108acf9e78cf56edd5596976c662d94a93a8b0b9f4b58e55e10d36b24e3fb4df67e5ec54e6a79848cb01b43c1d82edf8ad8d5c3b3d08891c791b6aaa4ba86e27adada8f259ec18dd1c
4c7469ccb41795860f7e228a4a50665828cf1ea62f64ca5af3dbcc90026b5173:e444ff3e:b06c465da8342a430774e64595e567e03a905735f443afd74e0d2dd06b99f8ae460062271e1e41ca7dad9e78ead9404e0e9f626b8af3275c84111956a7a146b29619435f357f5310276221c8c425023a792a561f7159ae4bea27eb6be013fb28
7ec0f9ca06f01d5cdf11d9be1aa4ecd04875f9fda11322649f15048aeeeb4c1e:0beb32803e:8e87b48955886958dfbb1970eb1a01e8b84507de02be17a95fec0065c3c3d9274c6800f36297246c3264484fa8080b5903781ee5480e6ccc57872821e5905ef297730364a9c877573799ce0b38d6b24497e09e4200df46e228de05ab4f1568c4
fab9d07eef72aeb16536abc68bd311f6a67fe0e8bffbb900a8ea40bc9239a652:412bd122c9:a1955d63b0b78e1bb0c1e661f918d21ce9f9a35a47ac6032e622525bd48fa5f32b84f0671b92fb157004972ab6b7041313410ab2e13c61ff7e0f50a6a3c4e552bb55978dfd631d11a2e0a26e337f6e91e9db53fa702c0477760349ae56167401
d7c2c99aa03c9383559987e2887af33d8173834d4edbc4cdc536ea55a612a05f:03bcb81c4791:8a6884f75b3f2b3d6d27137d4a74fc8b04f9a4ad132776dc4e2baafd790354f364226947fa7d1da570d5cfea85dac9e30febeda1445f91580570334e4626bc91757dccb7e53c2a71ee540613d232274fd5c0a7e49c70e0711f3bf72adaa2c9e1
31e6a9f2bfd89057675b9986a2326af50fc5b4fa6900e06392500d6a7cb8281e:41e77882d77d:83a3cf3e0e9b4726f76ce301c93db6d15e563bfdb5f93e885c37f0fcf28362e54c86e30d0e6a63f831f097eb624c153e15f8061c8dfd215e7d3a3da513b8f35061b2376457391db393bbafcf43e032f4b3c39a8dd20a1f4e8df3a217113ca007
4fdfb35d853e9b99ce5bf7211f55d15aebb153258109c22be3cae0161928159b:053633d112c82e:b42f071afa8cd509dbfb7f4a4cdaa57d1d0910e2ad9307beadcf6ea5bd9518a2095d68cca7dad010907341aca3770134010b8fcae728a60d27f821d759107cfe8fafb2ed9aaf09f1765e3be3d758880b243c5377be3ab293ab6bafc85442f147
fc02b27cdaab567346a3fb31e21e5280cf7dfc8caed228152cb602b2f5fefaa3:6b78dfecbe686c:99a058012e607d0a350e8b66193d0f0eefbad1fd31184ae01178a08a4959efa329a82666156d6ad81b6d97cca66ae3851630e61fd629e7f003d71a22389cab9ecb0533a4fe8c59b45f7f80d29080a9c0b90ee7d4b351cb1258c7b3a57fb6cf4c
3c4a5789f7d5a24505f69fde12f2157e066612ce25e54c000812718f7b360a5f:038845e18495f26a:a141c5d16c5720fe2c37f0b79ed69ff6b0f92267d7b4b4acccc015876943d692174dae68b9bbf8c152e57f3fa1af159e000d2bd8ad67e5a884daf0a8c0fa9206b3f72c9d5e1a0464b2240b9b6eb22263e457c7c5c0b20564a79ae488178fe475
bac507dc6edf7569f366512af88237d356ce017d89b78c5f7f9cd286d14781db:adabc96525ebb16a:af76b85670c18945cf66f8b5a16bc542ef1626d81cd47a68e4efbdbbc1e60a0c3947d4fb6e101c05b58f1c8cbabf5feb0e52127fdf6d268ef7bc9f9fd153d6a86d5f93441e60051348e3197b85bd5c565270cdd491572d4975b1a4e8a0852b7b
a4716ee56d0e7e65c8627236f7bab717fd7238c95fcc293902ad5c5974bd0734:00f46038365090f18f:8b2a0c5fb54ab319262fc707efc46c093029e463864bb3e8ba7e9c5813cb4356354b797b8a006202dbd48fc86ffae84b174246ed951642cd1c41722652bd1d4f3f1859c2d0bf1f9d799bcab4f14729ab8b7e3d518122089db9dbf153cc882a5e
764e65205f49486a2412d73a620c9cc56337b8b30efd0a6254085738095215f3:17582f469cfed9598c:9613a905fbd8e1f4ada58df87c91ab9335d5f72d813949ad14977d4a524660f00c12f63e4e65d16456c227de3bba02df14a4d61362fe8a7af7d7fe679e5d51cd1b979a81facb26d2501303d41f161c3443a975605fb0daee341fba692e3ef4d3
da5402cffede58f9c3e0b1d54af0d230e22e670167b1df94b37a5dfc8ef274eb:040fb6fe593b255bb0f4:b18b19d8fc3fe426d25d759a6bf8127f71e77201a3c0b00953cdb39eba0f08f2f2dee147f2d079d296f7ff26a2c3ae1a11fbbb3f983f55bf5d9018c8c4723f42ab4166ec19be5f0299a9a58c321db4069801f6ee414324dd400e26f81be1165c
bd2056e3125f653fa473265980d9c7e3f1a067466373baf59afaa1f62e448ee4:e9c6493ea4cc32c62780:80098bfd48c4026acb497e9bfcd33439f97aef045eb269cf271eca64a0631d99eb3a470ba53c2cde1c3b353760778ac8052311a016f8fc68de6520fce68228b56fd467b0f8527ef9ae0f50f42a28da05a3f27255be97c040a989be480df348f7
2cf6644db3ab59850d71010dc15b65ac7e5741f6bff4d9034bb65b499d9ed4a3:064263c5a80c376f18b1cf:ae86f1e1d6d95316820b7c9ca82b2c642ee2fa2894ba52690817b571dbe031b7eb608cff31af60febbbec43a71a55add10944382f9037bd4c9294dd8403810992253f743ceca93e9a9c02390e7d12d5d1f0de8cd03572eb401309e3898e76528
c3e1b5e8bbd163deb75cc4603809a7f186d6abed4bd868b3807c583492ee4d2e:d6cf8d3b69b4fc9784127f:96cd710812c0d6c46835430536690b1636a9fd2a8a554eb65d99f991849af4159bf00dca18a458f4df45bc474c4bb916056eb413bc1305a7399a2eb0c4a1aa7a060dada885b9fe64213fadb87135c51785c42048d0705bca81c06692722aacb9
85aca5189ce1219ac39e67279fc7055d694b5ad8282f415a181bf09eca566c0d:0a6b5a5a1941892e20e40957:83ca28da0a089a592ed0622f9f025ba4c14231230c8c48880e447cfd3877da50c3eaf2beb193df8c86f674fe7b92a98616ee593a9ab67ca83004ab044038a10a0836e6ebf36293b18f0cd18c13febde11333c3567ea08690748c105445696c38
29bcd632044c3f3d1f2b6c37b60a1fa531cbc380902ca7ea5e417081337631a4:5dd355519020c4a2979f09a3:b6400ded257d0e63d8ce4ff8935e6b312be17f24395b26563fe4e532e176906aa0d60d5efe7cdb8855f900dff31bb4b017b7675545f874d80e1e5fb4c0ca85d64b4bdb439a723c337226f70ea3fd9509b364665b3aac65b2e272a3f6a5e902ad
a25735a8c6438183a3bf3d2e11a10b18b5bab9788fc7f571f6a5d7428b0bec57:0b43c1734bd438087ee7b95309:b485e3cbfdfb5ed78f9e7a7b70173710c0df336ed80b605403cc4da0d845a6511dba84f9e071f0dbb63a6c95e9e0de7d0496e3837f88c5cb34b653c8bcddfa5c5b9739769a872e58b8791bdbce97348dd00e1a6e52d52ff80a8323ba428b1e62
fad6179f7388a19c0b15861cddc147d5b5a9f964a939139b271087352f6b2a94:65f21999adf89d3208de7441c8:b9928bcfd736386dd31bdd395ef2c2653682cc58c0479c1ae4051eba74b55fdc71cdeea3c8763b8d4dbbc965fe52a823011ec528a8d7ac213608ac1b42c135db749aaf3ac5b32fe5b00c3f7d58fbf4cb0acf77728e202c672c2b8dc6e8c791b3
e36c61d05679845ead833c11fe2a8efe565d7e0cac6d92b73eecd6663a5e8598:0b4897ef020da13d0b8fd060408a:987c40a7feec7f3bb92f4f159e916224b963374e8815a615a2653aaf175f4de0a0ce63f30aed60761cad310bfe24934d11372881b3b2f6afda7dbd7a99bbaf46f3fb4890e902776e704bee74ef77b0c8217b80bb2ae833ab2d3e83d0166cfebe
420594483d27e3b09be201b4d447480d41e61748f5b1ac863ac5b5b8349986f6:66f576cac592391bb05fec22e72b:b121a5242c9ac3c6ae0c5801b9823bcffcab4b9a0f5a507196e29c55941397c526f9c47569bc5f077bdfee8085a7ea0200d31cd16c8071c5a8816e3b2fe90f196fbfc4cd3c1c0bfdb972e93f88b1e9b10ca5853f79c780ab6b799819f71d8830
3a7f21f4ad9cde926ff41a5b657d3665ca5391f50427bbbb397c7197aa465e34:0173f8cb823680bedb605f5faf82e8:a3bb11f1159447f09fef369475daf7cf10d51cc93f060d737d77a24268139f121a504ec373d094ce92f290d43a87f1ed136939270f0c1c33d57c285173ba3fb448990e908e5bbc9fdcf41096dd1684a5d0ac811ff3c85ace8ec7c35c80fd2d10
7143d5b6a3a6475478b34b5317d3acd571bab149986804b50381cc326550689c::b04c05cb1b41a8a683b07fc0c768496bae0b4ec8f4b509101e1c581297ab12cf6341df1cfc5496cd7d0edd5f41903f9e1440e655fe2e9ade8d2e09ff4ba64576423a00712b6f66112d72b792c2238944f6b5a6d12ffca7ead0ad1217c2a68ccd
5107fc58427eb056153b4c8e26e41b0de05d78339821f91bdc56cfc3f261f62b:00:8ceb7e8cbfbf9b3b04e70bc7e49df066799e6ab312ac17df4603c6af4ec3f5a0e0f6caa19916cf711bd15b3f54bc2837046d7e85b97f5570c3d13f1dc5d791db9a9089b61264aff25f3f4c5834dc41e1ca6d91bdc2e8b3bc33cdf4a5c3909f47
d9cd4f44d0f610aa8d8bebc850b98a54a1a7d6965882d053218751922c8adb3f:eb:911cd633cfcf561e24754efda0ecd89483d058fb715ab3c149fe64d7f7ad5fc6269271364d195ed4694dc32b8bcabf57196ec07d0aeabc11a601c2b19060c586545a9e784c009fb60995825068a6271c9b080b35bba5e8fe65b0d7382dbdad9f
a145fa89810ea439afe5263b79444c5e12629c04fc4a29feb802fd4707e1e091:0a09:949afe2cb8d74dfc2b160302a1f8c44c80480875821f275b73798a0e07adcf358f985b2c783f692b6f2195e0419d213d0fcbaad2355ca05aae0371b071bf805122152d106392a8c9c51f16a2399a2396fea84049ff4d3a171e02f092751b9d09
bc386e77aaad8f0629cff07f33e35a1b989f900c58b971dc9c09a4edd0a40cfb:e910:b183d730da3d52afd07e28689e8be17b0df5d89cfd74c4ad2edf796fc905924ffaca94cd633554213ead81efcef7eb3c137fc90b5906c1b5dbd2c195c68634b60ac4f7eb7ec6df44e4b0c9da9cc2d9ba22e8701722b1eb21ada0f98af7334e6f
d94a000a40b98acc52901f8ca0ccdfdc0497390eb7f5cc739c91516b219cf7a5:00e3c8:952405861daa32b0f47b829e08b4cab83bc8effbbf62a9e0a640de551dfa64a4739454312df4bfdebd84271dd0b43cfa15e2cf940e2c15877f7a2f7d88ed097ccbfcd60195b32029ff0498ec23a2acad986c3d201a707afe169ad5d7fa724ff9
48b4784f1e9e070b95cb181904e3ecb7cf6234e327e2666ba598b88904cf0960:48ec55:ace8ac4ea4040d382f6bd290498940bffb49a25bca1903dfec23f0eca9edc0848bc6c1e6c1ccb7ea71732ff09ae369500e944bd739ebb760e41965978d0ecc6815a1174e3ec4c9fdd5967656953714b0c31f5a546fbe4505934f77628b554363
c28db28412e04efbb248b34e127ce163ae3d7450accb3e493f24362d9e01fa85:09aebeb0:82ad16e002985a12132955fb034b8222f8d45a4cfe8a3def59e1ebb6de074d89980d2314cc7ae3a79795c29bb1d3394114cf923376778654ab8873c3ca0436f04b6ef0df0b8b7cf25eae993dc297e9c0375f64aa8c49188baf8687d630fbb2c5
4bc72dda1b98ff6e1722ebe5ca4ae11940483948f73a6d44b87cdb93675b2cec:9883069a:a7499c26f769bc11a65e32e3c274987f5d272242016069882a28946854af8625eaa38953f1c35e4c8af78bdf3652a88a10a7c1a52be2dde6af14fd6298da96f8b7c9329b5cbf4e725528fafc813a8bb6151d7e9fb15123c7b60a2db34463fde9
25e95bec8613b28e98fc135365d012acb38c72551d71f41989033a59aabb75c3:0d8db89e47:aba0be6046df2a730f3f00284c64b8121f77c0b10cdf8ea0f60c7b38e1a2cc583acbf5a27819ce7e510503ade8149ca8198793f2d84ce1c071655da4795a7281b3dbb6a522ba9a091f8b5a37262ec1a43935fb89bf0a9dd2ae59390bec7c605f
fbef93b0f2ed1ff76d1bd67b99044cf6a39b435d5b623ad8d8ee28169b0a2393:2547fa99df:a572707d5056e22c14d314e741cec99e03b5d6a086966fe39d952812694ee0f673e23f13cf5518b19d2bdf3779fd53621719c43e1112f5ab9c2838ff299773b4308ae52cfdfe0dc672ae42fd7b9f13813149b9024427def6f4a72c1dd4f97a7f
4e1a6cca760e6b46c90b190da25d4277d07ed704d4377a414a0d7aebb38c5962:05161ae4b3c2:b72f19ff530219ff699a73e523564e36254e73eaedbf22d9c7a354dc3945dee58deb89a1c095779a12df44a1b4a6c9560001d978c499eabb966d477560f5d171276a44c94bbebe3461c2b6c576f0e3861e9ea76931a4f3949399a514b2a49b3f
cc3869b0f33e91780e12fc8d1d240b950776ca546f7d5f212f9c8a758ba9bfbc:5692a95e3a3c:90303542c2c4e4be801a2079acf3dcb5017da834d4fbc3a65e4ca90fc2ab2b3ff0ef98fcc57c16f6de07b1ac0b7bcc6409b7cd60e74cfe6545bf54b9e8585b120381994a5bc3f572490e3a5eda3e5caf4c7a6e8238d65f858a8f5665c29d8548
c5bbd44adad8cf07a1ee21a19ed6648edcc0da89c8cfee9d04eb74340aca2829:00c21979aee718:b682fe36524f3ea36039a3d5e9791db535e410f69c55d4e7c97c28beab85fbe10a09fa806f783d634aca926ffb4a392b153094e6829a642bcd13c4664a97a6bc23d87c0f356530fee3411e910a92a52e221e61edc03d18d3ba9bdf325f57b9ea
4b063743b91c96b198a889e2d68cc9cafdda87c4e49ebc7904eb3fe516e78a15:bdd472d8486708:a28e113b23dbf0c2faae0ec7755c9f826845f85827ed0e3d4d2033c818fbccba06d5645db7afe08ee18b92a2046019a911f3fbfdc84ed796181c76506c636e4f07a32dd2b6b4cc03d59ca0272dbebe4f2addd35f9b24ed7d880314ddfd30b9c6
2d7b35ebbe2e91a94cd857f349142b354941e34c30916493307068d8a6cf2ebe:0ea8b5464c8ffdfd:824252f8347eb299d1105323304e2b7f6985ed6ab1860baee4a5cbcc476d49db56e1d834f5f6293d0e1c1143fa60bccd1551af939d5435d514afa710ff8f3dd80b76bd7c75f728c9abe76c636ed588f406efe7b509c1b747496bbb6b18ffefb0
3e88b2286bc087bc7f6476652e444155922e8e239af3a1ebaea2ee36cd0dad1a:d0e9ee7908a105c0:ab625e2a5abdc53a9b882caba06492c661233c7e4fac9bafc8c1db9ea44cad455cbfd618e917703fb732e0540e3208fb0f198420380962c078d15304a7bbe9c1a259e8cf2ec49fdca4dbb39ff69f1ab5c58f97fec90e1756666115fb0b63219c
5e3d08aeafc50403c0de0166ae2fc6f39615e6012a04f03552e6c8869a702a5e:02f34cdeaa6f8e5cbe:b5b111acd6950b2862627f2d0ba6fe5b24cd3541be4ee2ae887c86ddcdd480a0a09c8003f9639c88b35c2d233b75d2ee101a66be894f438e4e1dc87e9bf2c80db7b34a3486c213281aac163a276f0850ff8b6d7681ee73a7b2fdd0f7b832caa7
276b38d5aa2723f4cefa785cfc077559323a5c98328b3caeff1884c0367cab4f:4dadca20bba5e6776b:8a8b0a03047d87df975304f41187c006a310858272d6aa84f37c8f4a44e78ebcfa70f968dcf5b5a6b308c9aceb8bc9b4113901fe031e4deddc7fd40d85759b38df88d8e22b29166f2deae074320a742ef10610cf4793255ebf5eb456606c583b
fa5426eb33077b1b099667fe2d695b2711a44266c22788ebcc3bf1c5237f4809:0139fb2a7aaf639501ad:8f8139e53fdfa59cacde3b5821deb75c7b6f9f153b0e9fdd044ce79bbb7770398a8061278359d97f63ef52c4fa41da71170db14a99f48ee899702e62beaf09e2be453f9bb8adefac38dad3076835a9e12ef6b7f48e30413d53e728938413d9ac
11c5ef8785ec31234ef347fdce2a1534a49967245245f9523c319938daaf0a53:b936534e227a3c5cf272:98ceb3072a2bf95c95c6523451c3ef7f2e5e852e7e3ef77e60dbd26ae6a4020230c558b1d044792e48f6ab2cc755143102e9a2d79e2047d90c1c1dc2c7e1d0befe1b9249bf9026f74e4aa229c6798721a694fc1204df374a52465fd1d85360bf
dd9da6df2c114902adaf5c0b90f0a30ed497da31233b16b9fa0ba48aaa9c7d18:073f5b3bcdf9fc462d6fa9:aa11b0e66c652b9a3b638da4a6702d10edf774f6c704c0ae427805f01c3532c5e2ad48dd0bc345ccfdc496635876177b0ca105b77a4dec4a5d4d1b341bde0e61a04e38d88cc91e707fc269e2a47fbdbfeb536fb999994ef2fcb9b5f42eaa8c26
e98729f51b9943d4ab9bfd2057feb4725c962a72d6af4849da9a79681858beac:d364782af1bd76a74a5860:b0bd5532f822f65c5ad300a13fd8a8ee76784f9295eb98a630082a708473d40a4a390e9857888d0c4cda282699ba8334173b25ef088a7b4d23967bed053dab15b9f6f9b1ad071a71e7fff14416b2cc7f59437e0a0e3bac09b695f27f44d56d8d
33a895e2e1d632737f6f11ff5a6857f971cf37873c1b95df2cab888c6bd582db:0acf93e56587dc54e775f1dc:85f3f86e176ef7934ded7f5cbc419f99d08727770b7172645b94c235d73f1311451dcc54a8735e8d2bceb15caa53a04a0a402b14af9e7b49323cdb728b004a09a695761b5051eb944bc861ac7b656c162d0e4a9578559883f9419b3033dc0c83
826621ca2a9a206f403d92d3cc6ae1df509ded64ee5a37749473fb4089b21b24:21f41b0f651e420444caa733:a6ae1976dcc46c1f038fc300f0236c07a375bbf3ec982fdc492c14132aa2107331136ab4de36917da8680885e0c76bf912f6faa529581980d9c379cfa4d5786b8a64adedecd5e7c7f3ef4b273ce886ca916ca87d3d8d0cd004c437e6f6750410
356522ef840b1ac132686085f346558a94f2943b48b75988c2cf512cbdfeb678:07b7666dfcf9541697392813a2:903225b431efb0f426aa962edb914c1182e6bd208aab9ffa0f9e502ad0edf4f7cd1fe139ec67597c0c9ba2f2b96b890216aadc7fca55a57c38b236056fbe2aba71f033b143fcc73abff454269a8de40e7c92777c5b0391fd3bb4d6991de096bb
2724b641a54745be949a396375b50735b1e4bba8b5c9df6df1ada80c4f9edebf:7e959c39b0c4f95e90e05d4e6b:8359dcc9a9c233b2d871991cd5587f83f2544bd1c0bf83e006dbe148609ee40c5fa7c72e4861061b6e226d8a3d28736d08820f8f8cbde45af586ce9877e8783df86094e0b28d3cd40c04fbf5450961d26e9b61901ccb80e457da79ee1687d53a
bc4227976367e5b3906682af2f4c12e2334ec96b02f84ab8adbc8d6df156ac1a:0b3d791d2ef361575d5e20daab74:807ea26a6d18624ed13f0ba940f914d1552550054195d9cc82125dc7c5c77d1923af45e062971a92224e3d31a8b4f7c10a65d1dab9bb7a526181cfd9ad17d0c1d4b571a0f0ca4f67fe967940a37bf63a5c96926da314f0aee4bc157511c94eca
e62a19ed9fcc12b49964ed582e1e4ec9206ead22fb3352802c9ed124ca6fe99b:353c45917ca7cc233906934ca47a:b31abceb41227aa7b3975ed26822644e601edd9fef05fa590b36d9e5db58f01677ab7607ad6437ecf93ccb6d3e73f8660a92edc51a8db71dc75702d4fb984ce458eea6be357bcdaa1eacf6c0860afb389c6cfa76a137ce6dfbc8b73f3f93814c
de94b6b59425a13582b2884b5643ef3dc0687e562324ef16ce60ab915ee999d4:094448d5868efe496f6416638e3b6b:967dee15785d4825563c26be491b30e1e44235e5618e2139dc419d031528900afa5c20380a61d58df87c7a40c2f7b17b174c1547c6a73836fcf2ba795dce10b1a00baa630021529d5e7dbbb31e8f24d36d202b1b76af9738a2e7e39b1789f5f1
beacb441f1d0df37e9ed5eb637f722b63d43a051279c6b4eb0896356448f922a::866a698873fef803bc935885148c8c84996881585e21207a2479f55376911c17eec651d876efc84419556abe1a169c4c17b290a8dce65ec8321c43676f5dba40edb177adf83f61b27d91fb1cfd941e160db1e53be16a56b9e4f6b232b8a66810
a2e573f800a2ebdd162cf8140e809b345840b21a5902dc2e866f9115b8e4cfd2:03:884754c37d989ab3a8f22760666d109e39f02fd43b7d9f6b56b151de79a5ccd04bb7e161917a5db009c466bd646d9e9f0b014fc58d0b8b3c4fcf7a696117c085638196a060012a4f2c98fea11a8dba8300edc7b8d0d6caf64e2f076040adc729
69e59beafbff787b405498a1c4258811a7855cee59cd24100cbc29e9a5338890:af:9899c81ec05be74370c381d6da9e537ab96b94a3a140baf23bef0a8ed396c885ac83355c0e4636f16fc609de2954952f110a5a9e928729a7baa9bfe7cc91151f1243e4e3e109c6828dd3a4b847fbb9ec0b950640dec5706e09fd97f00ea85c89
3732ed34c394b61efbe6762470519f7cbc01b3238ccd16bb3811e76f78b28969:0f07:90b715bae0ff837187f346b8312a03450a61a6bc13f4b65e788570e4d04dad2098f29ec0024b4318792c4d276c5ebb8f176555d6eda15b985714cf68946d40f8ebd451e9d01ce383cde7c14e0701cadc68fb0bfd3d43f69ed3159b24f73b0ad5
4e4e37b3e34497a6a2b9f29dc48bcc66c68b108ab5ae3ec7de6b9163c8089f53:dd2c:84386746faefd739875a51b6bf0a3f10ab8fd8ac06e7f9d3fc8be1140416429c33ba4e9617982cd9bebbff8b3b40f06c16627e316c6f4828d771314e56b2ca0fdde30b7ab5d7ed0c2046c70d1416cdbaf68a760fc9c3f16578a588c743f1e5e8
840f953ed1e17c5676dbf8969c92416d2806f74fe0cb8149cb85366e51f8ea56:048edf:a4a727d126f064d49c6abbd0811ba5b728b03770fa4abb4961a56cdcd60a2ed14b7f52385697ac524166a9279957595a0df740cf1c8ae3d5974fc48bd23383d87ff96f70e62ffc5ef1455f2eefe1fcf99b8c07d063b8ccdaedae3a43ec37645f
ac1eea17174be2d21133916f1649763e7ba8c183a23f7f01e8595c413fa9f796:8afdba:a541bcf10e028b7359ce47a7be89a8614483b9ecf852251376367d871f529ca6c279348def2ed815e56da57f148e551c021c0de8ba3295dbd8fbaa1fd4a95178549a12ce54503e5d65c281533247cc479c1eb3706f2c52430697a851776a6117
0d64a2304ab3d884c758a634c8e02a39df28d28f45326ffcfa227a4d09d317dc:0c57df3a:8e666a9f47f8b5f671e69c2aa41bc1db30a397555509fba48b1d017010ee52970b190bd133ff5a33459b700602a13df20079005abf2d1273d64fecb6ecf0bb61c6f1d6f6cc89666aa102accce758bf8fdb2fcd82fb1fd7955c1256d18c7da5d8
90496775585cd9ebb1f11a7cdca5c9f5aa77b8f54cc1a04629744a6efc793c0c:94d33984:ab566969389b382ab1360b06691c99089d5bd4a0d02d92ecfc47af5b6bc14ae6699ca2f269b970713e6e9f36d300e0f90bbcf2dc71feb1d47db99916bfee0608c15c9a8a714f84d0b16073adc87967aa1c468d4d3a05d3a8639043496db48d1a
897f90e55ce1dbd9aa3dc72ceb0f890d63e97326df0e7049641ba99505ff3f71:0c5acbd21e:865d3cb8351aa4545138a31662a9f60b31fff86186021c4cbbbf46ceb0ef9365634ac48700a9516d1a3b49294b49cf8b1705cc8f57f3d814d827e1ef915bbc49285cdb83ff61a334b7296bb1d7f6bf0c11e39e84360809463aa15372b198d33d
d7d223e28a2fdc60e284f4a894cd713fd3b5106396db99eb77fd0e3ad3450be2:644b9aca5c:88dc952ecf62f5f7f875a2365a61ec00fc4989056467454246dfbd4e5f559190b98a744e28e346ad1859853db7de2a69100c648fa64b05ac0529958ca80b7f97c386d3009ebfc6addfed32bb55f216b1c3b1294d0a14722bf4edb19da6d9272a
be1fd55d56ec81af5becfc86a2e0cfb7378223f4cb272561648c230d27d68c8b:095c77b908da:a44e6cd4a6081c9c71948c6be2755ac271c1069ef3772f4470a7e138b596b75d7cb055e99950aa21eaa5cdc35353edf010a04dc39554730a690af15b998404456935b9c52d52e9a2467db9916d7824aa9746bb850b000d5bb9d22bab1dbbbd17
58298a7fde5847078d221ac45fe91d7a5cacbe6facc6b0d1c1c846bdec788bf8:71d0ada1a638:9017e5e0d91dcda6a826f290bab407db01e3163d4f1bb47d6a2b9d8ec455006e078dcd193f8fa27655f6991b512e92130fb0e9b6d168a7d1df3c63b476cac683c10e061081c54ed0c648a07616a1de01f0a39d5b8c68bbea4253b1dd260e9a2d
1f7d8693d3e0e5f58120e12386830ab8278db6af93a9aea5e317009d5c7abd90:0a9f29d796cf7d:942bffd18f0d166ecd548b79aefd37590814cfc02d793125d7e01f3f46e83ac50c9e4d4a7d7dbab364411ee8c3f57c200d05c047af7859678c8776d98de90f6830a74649d917ce8f950242b05a1b33f164970bd1aabdcc66f2070536ed175d9f
e116f7c628880c89edddfe2ed67a99f4db7c1702271a635f9c62731574f3b07d:b46ee7f0cdc911:b317d9515771038a31d1574a61a700da81d5e143648342013803272b60d71bd61d92fb4d5b7018b9dc4ed0227b56d30c14d51c9d84914f6d53ebf296fe8932d086a0cd27bfcddbf8239b465ff9be41cfaf9828db2f6c4c263f09a57c596016a1
2ef3156af349a534508e4448c8eb2cb32f38bf79061ee4960affee1592fe8a33:0f8cdbb84ee69ea1:b115204c338f8930528efe7c5d3a4a42018fe1944b738edf3128cb04c804ef73ffa2209f0f5eafe2d825ce73604db3a503cf47cf3b34b613ed5fe10a3f5e9de3c27d8ef2a3894d1287fc045b01b97a4d17ceee0f03d31b81cded5a52758e1fb9
6087f431c3cf8344bd95e09c9b5891150c4dc82ab8cf49d4edb3445848525787:06930ab93e2941fb:b8aaa4968d500766e2cc15e5eb3b6841bf90fa2208b2699a1498d824509b79bbbed192e2e5142a6562d49826d020dbd30d06f3804c321b560c81b4ea02e281ae2d635cff541f82e5cbe2c96f22dcc8b41eb01e4c21957092c7bacfd4cbd367e3
98fa7b94541ab754e6eef1b67714e04f324d25ca7fba72263a74ae705fe56ce2:0d5319c4e04c108b43:8567089bca14b9cd5336556283f3119c70bf2e2a649eb72e33f2de62314dee52d270854382e2851c23704c90cedeb5440692b5bc28d71364fdcb9e13ab51886a30e067ebc7b873f26ec31c1439d58ae87add0cbf970587b4ff929bf14f86fd87
ce27a9320ce91af5cb25db85e49ef48c889cd2cd1876f1bd6a33577e89d01a9d:456502c375d647b287:8fb103caf39bde35ab3c9dfafe2cc93355d7745866ca999d93b311a5231f0f898fcb8da735c3b90e5631b043616229cf13ca431a73079e16dd4c65ceb54c8ee06f4c60fc0c143b734391db2422d035cdb98c637b2f53885da84f61fb3fb4fd8e
82ec12537be95b513523032353671a31bfc30fa874394854f18fc22a83edfc08:0a0ad6660fb6b9360758:996c339f73f866376bcad28a65440154c3161013109a95fae84f3c7cc531bf072e661f099a23350aa84a7c64bafefe730e14c5939e4797decc3c49af9c5538d1d02fcf27f714c8de6aa14c4a51da1891ac076014b0c2b0d114ff673df6b39495
e20cad243ac923e408ceac8701dc75b6a4901fba43ccf5d8834569437555ec88:93fe4e5afe409cf5779f:a7ea309bb1ade75ae5bfb6215aabecd1f734b85ec9523491aeafb7d3d31e7cdcb0c79e15838a75a5d5800685ee36bd6b0117a46d2dc83364f6ee1bf0164ffa02089f6d3d921b0a1aa9d62eef95bc17257aa2e1f4c969e546145acac32e05ec2a
88385fadd78308b14a845f904a22fb8ef155004381611983af1a97dfbadd4cfe:03c626fa420c8ca589184e:a4fcc04bbbc6b6f5a67a4f20ac9d3359d2fe9d7e26e276276a50361c3f5d2aba5a5945b774c4eff1ad2bc0b71039fd56120cff7dce6d31289ee09df92f7216e327caf71259c55d095694fe892e8974296f18a370596ee0f9d77fd874df5a49f3
2fa90ec2bb82b7523acd10174510e9e1f3cd8d914d5df1c1add12294f4201b55:31116320fd8ffbf65edf62:820727a6167a7d5d65e5718f3a8003e0a1bd19f82439f2398a0f7ea568aed18e177c8562806785afbb4dfc8625c5ab9c0a0afca6fc38309ed111ce68f3780fa49b1a48625fd960436c8033b0fbf78c58077633a964a513d95db6a0f22471df30
fc9bb165ada95b2cff23e332c0b9f6b6a9e684c38677584e4c5ce5b16c78f66f:0915034dc3615740490ff744:ada0438d237761c391d3aae9b10fcd0b8ed03ce09e8414859642050dd88e03ddd69691efa26cf68b4f4aa051e01cd9cd08efe42f1346c952bf5d47a5a9cccb8c23fa2a667bcdcabcbc04ecd444e5215dba18397a22190424358b9fabd7579591
3967f728a6fc9b97d815a8a76c5df791d8252cb44fbab8a52bee1aa59228f26a:96dc2c37e0f9efb8f177873b:8dda9f419ff275828006089d7e4e1342b5eb17dcf7c51a2fb7c5cd6ebfcc2064b987c3b2d100c166b8499d82d705905511682b1210616c5674b11c3f6b472fd92879b1532d2c60a2c05535d8224832efcfbae410cdeeaff9428af6159f07621f
c0bf0882e28ae3ca0f4c9fa55f0e8c48f5cf4d5fb888005be03941b083e2bd75:0dfc99df951bacffe40d17fa40:a666faf2bbeae3b0ad5f6cc18fe8bddfe7ae77b63c2befe176bc2f18a3ceecb78e91453a989642b73d2a18f5e1883df2173313f9e68d968cf3d3cc1d65c6ba4da837f2a93a4700afe996d0b7e0ab3b0d9f9aeb4da76db355ac99f21901af7c9c
926f0d9c037207a2ef042fc01888164837e999b668b22e72d1e090c030f2ac14:042fd0ac0b5cea9347f8bd6696:88c271e6a393f3ef9cfbc3022578197690d93b1cd7e90e14e1aedaa8f4f9372dd0df5f2be74c5f1ed7adfa3467f5e94617729d08f0339e0c7fcdbcf74fd0e375f0ef85df8c4a7031f4471af7bdc72a692f290c2f23a361a471e7f8e7892d688f
1cc92dbca460512827bb5ae49b2f5ff3b0aa556470ebc4e08d06006794fa95e4:0cee8bc396fe6f2e44bb0c608bed:923613d71c89bedf640787cdb031492059309a702410308fd904c2b29c6d78a75d2fd5e6758e759a91c07c8dc55310120651f208d9f1184ce02f02bf287c20f85f05c660838b0baf964b1785dd4b72ad18a4c86410f14d79d5b6aaf8a8093c9b
2d94bebfcdf3a3adf32af3036e6098bf51bacf2d8e97fbe53ecaf302fa176076:c1f3a335ed2198cf83bd46540c43:a40961a61862c95fa04a3327b9ba847d986cbc1e789b9a4009182a1921fd728e051cc3f879ed6fbae67b7ed40f8cd3350de0d91a7a58cb9ed3c9b6cf267a84c7021a7a83931c0834a79a432e75a8ff1603e974a3654da2bafdfd8d122ed6c4ef
f223072deeabef9597b9404215b3b1f0ded345552365df94d7b92bb78593fe26:0f381538c11dfb0ef9bd9b585ef2cc:a6e4ca0f2fabe91fd4f67db13d8a52de8b09455e22b001246f9afb50dd48f31c3c249644544eca500b2042c824460803182c8a0d92797f16f3c45cbe16d7e40cae3b1f7102fbbbb45fb2c66413890836b7cdd3e685348bca4987c514c05c2299
16c7703dbff0bc635e0f1616f11ccdae9c50c48030b50d17f40a583f4b8a8a42::afc773584e0e50446d784ff525edad44d3c774dd141e13346c7db92be76f34b356844d2206473f75c5b66f8962ec9cef082fa43a05398273bd066ce7620f59ee443f663c9c278ae9a64f181d332eb0cca3fbe3616bef48d5ad881f12301c4012
2bc35b91d5972e10dbf0160c138ac436079d6c43e4eefbf3fcc9c06767bb31da:06:af9b614adc4f80d9554b0167e1361d0ffa413f9f36e16930d707c4da190fb83701984916a2584fd24d65f350a6dc93e606877656031e847dc9b31485213ddd3c318fbf27addfd209e80deef7c23e174be7e7f34639a1af09dd6fa018bd4ed1db
4456acde3242387fce74bb201e5624c1097686e3ac5ce87d212f8fd1d2d4378c:85:99b45e3ed1021db6bc869adb284f025bb9b930a8782dc9646d1d5840b67b2c1e957b6a1200cd67e2c05b1e2a016bf58206d0067bfb59b47946e99fe99bbf8d1689ec20cb1bcd3312600b24e8345e43c1cac95f03c742e9cfc4629465a35643ee
c7ac6866828fceba14885b50fc4b29275565231008fc8db8c33f0730493d7cc3:06ea:91e550dbc99350364a3722e2af1ef9f922d5c094faf39ec18649ad190ef13835038eb5201e25276d3a5f124f049610810e4157b4204d19da5a50ac38d913990e5c9402bffd51576f5509047922151ee6fbbc5760e588cef3296a805d65bf2495
9cc0024de1d0ecc27bab1155a99eed06131d3db6748b96f72d04501a22a4c13d:7a98:99890de7549f5fcf2c0699b55bcb0bcee6e080339678c6e5b61cf5d00684a4070f8bf9d16d2211b0292cca06c255da3406623a84cd0da3044c8f190e132763ee62acc2f60a7a20a8f8c94a237ab72fcf4c9b08f4d36c0198be735ec0698106e3
3a161a88b0434351865dc4b322ae2db86d38e26b354a8072ecb007bd8397c08c:0eea0b:962a8ab094224d90904a2a48d6b63efff7bb702ed993d9cac630dd219988cdbe264be9505ecc24c82821591e3c70b385093bb570ecfeb81f50f66f74b9d19335fab464b782f98ec0c7233b83b895458616ba23205478c048c7f43187353e5159
93c67f70207b5707881aea562f3261f1415c8b6af2e4c95590986b12c8a7832d:5a638c:b5cc880c00f50b09ab58081251e0001ad0f091c1fc3ecd9cd6753004d1a2ad82056b61cf7ac64b3fde2bca8ed02b5cdc052035ab0af0d17f78f6ec97dc55b67d2a0a4627543b0b42aae01bade567eedd41154d254036accbc2820a280e8dca09
4b9dfc470db493399476bd07c30de9b8463e6ff0e1a5461d7cb25920caa74e9e:0a77069c:a2ea5c1c6917c5c74352fdcbbf0320c86f09ad9ac0c29b44941d1c2fcf094a03105a3a1c7829f70c21282750f912f64a16b0d077b38e2e94b3841f3ddbbe29538bed0c3a15b9f3fb3365812c26423ed8769588ccb0ce4007500bcda73e934c5c
182d193ba7412d09b8cd8b486c72f7163ab7d572b08b95b4aa8e41118072b63f:8f1ee0cb:a9eeb586a08cad5422594833c53cec8826e1ee18d62eb695226df14d68e8973c067c5a88522d72a424170733b05723f41925613e55bf883158bbb77a7944e867a71f5350558594467329ea8381f9f8ea064300f46e0b2436fb177af41b6465dc
4a373adf9961e3ef98ae8009188e948ce309d3582d63d850a13fe2c304dd7065:08cbc08a8e:81664cbbfbee94ca1c567f980bb39e686e390e94e527a30d4e4f3dbf3f5a48c4adb5fdab07992633788a45750ddbbb1916d3ab5e77ee56d166ac28e81a3246dcb5e183a3692d7eb7f192a0487d219d79514c657d20db0c4ff1ac3d4af5fdf33f
520133987bf9a4eccaa0da403ba0a0c44dc83d25ed8345976ddecfffe207fcb0:4f03ee2e2a:a1d71d93ff022b10538bad0580e0fe30c476abcdab5ed8114a5ebf1c5ff8b2818bf6bd963c9c69f38ca8fca73da91c1612fbdb7b3323396d1b27afcd05679dfb360bf79dbc0e2bd9b6afb8e64dad7f00ff9e50ed2dd199f53c45a58cc2d09cc2
b1c3f3d39a291acc244b9614b505030bff47295fe8ded28458d62248098688e7:0d737d559984:938ea7b377846d5240727a706999b425e6420c2a2a6ee4603c66235b3b51301dc784faff02a55ada8d150f4ce50e2bf205b4853cd9a98e757027778d4884b4473f346d2781eb3e8d38205b9c93627e0bfea2c9414cd9da5e689526a076d754e6
f43d8e4018143f5035008a1009a0a489a94791a6767051f13ebccb137638cf6a:914018d2bc6f:81c8d737713175d9539decce0ba7ec9fa8b2a1bec70bfcf0e4226fbff51a19cc3efefbfc157746500e5b316f836051a109e150906a9febc7d2471de3c94fe037b78bb68c8e8960d5793003b0545779554358b1c3479a6bd255ee9db60be335da
5bbe2155b1ca1a37d4cb75762dabccdce894cec7452561bac5651f92b4ac1fe3:0cdde70cf2bf55:8474f69d3efaf0005b1c62e54650c5d49ea3117049923d108f00e7c67f9d57aa967e675d5b72b9619cafeaeb11ff057f10f952ddf35de97125a062874492aa7f90836bc1bdd1b942d877410706547f6a0ae0ab25e993c3a4aa65de7a180237a6
123a411e3335542f1dc720d8354437476c9b81d568daf817b2db83981629c3d9:fd67000db24705:a5d2ff627c1a4b7f52a71d158926f2949aae9fdc3e206f9fdf9117ec88ae8c8cbe4d23d17c713b2e09e82d557d996dd71847b7e86bc1c74015c09abeeafb72873da6945e0fdf5b5cd91c622a1cfa9bc258cc6eff0125377d2e44b16630360381
85d140112f583ab2ff5c04d3f1f3944efa63b0a34b3979bf9aff0a3241165e5a:01172e0a402caa46:8fee0a19081b0adef228ec3cce2cc069911ea922845aef15c97a919108bbd860cb241be476cae84e0149abb305981ba4045751809c174b2f89fc36e0a498ee7f325e062121267ffe27c0261e416ac7bdcbee7f9d19e37cf54fbeb9e57f23d26c
498848b5fd8f6fe2008c039e5f972cf6d6ab2e909acd594ba8cddf7da3233167:640a244c2ecbeb26:ace162832f586c088e3ef9d558228a99c6475e1eaed0f875c512fed3f800f876409e8f5b28831a6563511ec1edbf16fe067cd73fa6413030c0892e6c67b8ca3a14451642959e50cbf18c7e6c0052dd7ac9b98145694cb5f83feb506c13238811
f861f548187099ae19317eeea551a0fb9881485e0adde98e6a260d7b4604c755:0fd5f6f3687ebd0622:895925c8e398877ee20341c41f703d355eec6e1fa7cb8d9062379f693f489c325c94105d41848ef94b1700b5555128720ce287d39ea693dcb99127d764385d242f4a93c6e038e3defd68f791a013e3a2958e7ceda6f695969f5bfb31af826ef1
370642447c6763c7f6ce66b4013df4b46a4de47ca9f34d2c2d1fc130b4e2c9e1:395025a2d1d88e0635:9259229b65e9c1e95c876ecac32d33acec12d5ba217d9aacb30f52d356338954feadd6f9fec797d974b79bc2ef0e41f60a82c3d4cfdcf3c399bef3ff026f0082cff184a3300d0de1a863fbb498cbc6c87e28967ae4250c8ebacb8ed3f4a4bfd9
3832a8792ea9547d7fc8b3fe8d5df2f35660272dce5bf6ccbe7941ac7700b194:0ea3967ee0e677fce984:8b135befb49f3fc8ca4472935515b3c90c2d9ac6e13b0ed17da999fbdd5981b580ca387c0ffb3fab8c2b44559cff3c211404f792f243d32f5b775db0bab61ee480238540e2281ef0953cce57457d78ea77ff1342f695c16af62b5a9f97e94961
9e052fc09efdc8a7ecea5545fce87ad988a42239e321ea0831ada650a76f63d0:4ab7c1680c49b6f0283e:b0c39cf1b66cb385f233ce068e7f81099d4de1709a8cd4bb7ace275ed49373217f57ec5cbf894db122481e2eb5f660440e612edb12feae8d7c82508f057bf4c5ac8e6fa54f400c261ca07416570295e6c9732be44c30c1f522fb3785292fb1aa
409c98029b0014f8ee36ba5040a2e96deece25c632cf48aa97b58d95648be79f:0177c0f8148194de3e6477:91e278b31a481a970895fc0e5dccaa045a05cdd70bda29f64ec239a5c4fc0d5189eb6c3cbce3affb3294ff4251a2e84c0633db7d2363fb53250d092d3f3b59121dc6ad3a4a5b8455f280b03d173d71a18275e7817936016e7db825540885081b
3174e6aaabd794e3be89e48aedd486ed9d9aecac2f8a3d5f7a179883765e136b:fd1c24b053333e986927fd:93176d6cb5987f8912225a77166a053f7de30345718c4c081f8dcd71c1b6560a4c8a1cd096e7458073d7b42084b0ba9b15f2e378e7a2a2f3df81b29e594aef47e877545b9d56e2427364e4adfcd6dbe5d6ffd2190fea6e791a695a3135e8111e
49d8f16c96e0bd5c151911fe49f9d3125188b7461168badd9256066863490658:0e2c67ef06692c5c01ee1881:88b6ccc20bcb7bfda958cdb80ef563d8364655bac922d065a05579b9de50b81c5d72e9f3b7ec5b9a64306e79d67d48500f89e80ad1e06c1f94a2cdf1d4522ef8f19467cb7206cda4ba86f9c111cabf418cc60e46b3e2ca8e841f7cd2e4d66e48
18e90f391db888d2173eece1532225b2c3eb31011772322ef7db8985db83c5e2:3cf06f1f7bf3f1fd5c1c34e0:98090e7c9348e3bddaeba1a423c1dd5d34ff397d48d9b295f7725f9cfeec287c34ef032a7a1017e98aa2b8db17d8894e0b0b72559cbeab2a3a6af1ff5b33373c24602ebf87c2341078d54c6d825ed6f883693a69b46dedb9e812fbe8627eb5ce
a0d5b377c5a369e4770764fc42f1ab42ccd51b004b7456ba5855e95ddfc7415e:0a108577679816e1c20fab4aab:98392ab3b9465813b8b4af724c28dabf62be6327e89ffd80c328f251634a240adc893bee6ceb320a6e6dcc5fa4638dd9191aff395421d815486f86afcc3b8289f0f3b8456ad10296e31333b1ada7b3869e4562dfc8b81353ccd1410570af383e
2f171467ffed42c0aad55340a3d29f42132032b4b1c0cafbbfe267d5d9b9d975:ff0fbc6886cf7119c13cac2047:ae45427a61d4acea474a57867e7276ebaf10f1065478c6cbec3861d14ccfcee9d91b2052fe976e145ef8c88518fafcf812cb469d5881ef0fd8ef4563634e498041e7cd2b0d269bfe592aed2d7cae94f9787de91528e15a18efa74715bfb8f289
675943c3633d2906548ab8fd99d59c902c3613d931316631df8d892d21971f37:0662c76b721017ba5a676b65b5b3:b44020ba6ea5bb53aecc946da3422dcab97cbd38678043b3476d3e5570d5c55a422f5bf7981fad6ef90b15a13bb904bf17eb164d5e1824956e3972bf2971257b051593be8d2bf025b01938216bd30d65142a40575d2d2c752f5eae06e5eaeb1d
de79fa57fa8b20d8f5c9d8760d7edbccac81d0e5ed6db1c1db3bc70142439f81:4f8b3d03d50fdd4428d90fb8a1ed:969eeaec43bf81bb610c8c0447f41798bc7e558f3ea5aa9147a4cf651e22fc99b1dc9343a4c0efd61945b1b8a1d92a8419abd88575783f0577c2548069e7f28661bcf702e851e9de2d735e6ad545bee4765c6498a2f7891ebd0690986aa3de74
cc8b41dd972d7003bc57f06efd6287ba04ff88b532a1c9698337a751a29ea562:03c572d43837c86b2d5ab55f602e34:88974f2099572aff379b487255507b9347097baadf902f283c58137e4ed4af6d285dd63ab554ccef48790229d4fac08e1717572c08c4dce5cc7530659aa23eba1a680fff339f138f11ff06b0817623fc904b4e95ffa6ee186eed0376a933e3f2
3d9039b23297115dba92c2d7b4ad882749bbb47bbc378da7a2e2308fe97762af::b68588e7e1728b2fe5769dd3c936c209059f05d89fa1c97e73de4d6ebae113d25a3ff152d59e8e080644b9ff405b60a5016700792fd7b3e15e1644492d5b49c9d529f75ff089483975721c55405de377d4601b983d4151f86b11ff7f259b2880
360763630528bf256a78b0c8c0b8bde84d1db6edea081a08d7a9903eddb9a85f:0f:b70412c89cc6041217c89898d9b4902c38988d80018c29b5c9a160f641ac867a5b6e7460efe1e807d4b16927ba057abf0ad222b6c81eb3cf46bb18ea5a1f4640b62ed3436360f359baf3dd7443f68fbd490acbd7a25901bf8f3daef6157426e5
673f104c3fda7085a1b6afb787ea36dda1eecb87b45375e4204919b19bdb4f0d:ce:a3097321693e4dc5f56e65dc09014d517c942f731b6d7aa2a630cbaa5616298cb589475134f6333c8fd3c95a851c9c0b0e300cda7469cfa3e426b1d393361738077e8610f1b61ea3a75ed9dc8d7c19e804dba831dcde6fb5180b272f336f7778
165ccbe631f789288f180dd299248be3019185f0326aa2cb5c01b0f381e3f952:0858:b42d38c4a715d724b80a8ba5ba3937d51ef57b82c473fc0e363b68112c61fda0c0a51d252d5893075db03f96b46458420af2dbb16b2890d27ece7710d5a16bc208a903ad0ad0173b35c40bcc0abff09c6871382ad936aae88464320667edfd45
3e0db6c5848ea10fbe9dfb7fa937cf57e96cb947f4d47b4f7dad8da1ad718a75:b696:8c36fccafcb8221a13835cf8f2ef34207e62d82ae27b35f9c24c16c12ef4d24d45a253a0ace92b3260dd63a65b4e22af14fd2e743951349401c770ec39060e926ba076a4ae4ba9be82761b891956254585fa8e2e73ab101a8cc80a1ab523c16f
4efc64d65ec6317d11c98d299bdbb177b48afafb4c77276885a5bad19e86d898:0013ab:84ec582064629c67d7f2cd893680e3f1296c666fc23309063d8a26144cd0075abaaddddf2835f41ef971cdb3c8929d1e0637b3bbfd1f01ee02b94750fead9b7f331e145cf4551bdbe9e77ee7e1a7c18b81792d02437f23c8a14b90883f044961
933acbc31ca221dad3677776e0b7b64a2d7c1ece1d9440b84cc7e77449d97163:5a0113:b3639ac94326001c2902bdd4ed1336dee41dbc5168b3d42cc7e7ddd6d05dcf4828ecd6d03e24388665f37413b48352b203b35af2f073a6b2e613ee6ab62e026643dfa8c4c4cc93126b2739517487d3bce138ccddb16cff765608bbdfdff8e3ab
1aa58cd8fe28bc296fdfca3d5af1841643527635d15d7ba7b9f52781f920503c:0276ce38:8fa25f0ec24a14482424d15128f365ca70adef6c497c6a3f2ad3a4129e0f44d0552adda1a2242e7936cc38014475a2d402794449c904b8db66387d4fa6cf938f040e9adb4ca3fa0fba4ae3c2a4a74881c261d3dbfb155ea922936a2238789c33
20213ae7786685baef612a88f1ef008dffb01d185db47155300674b934996e68:19b49baf:8678b97a5e70c4287cc3ce06ff106b811ee91a7fe2b4a2ada4e933c17bbbfda3fa99be519b80e2a3ca7777eec5127e740a3c6d0f5fbbb3a125aba2bdfd325c1de980e2ccbc34023d06afcda5c4d70cabef3961cd73eeda2ffbfaa7146848bdb5
0bac27b57ca7109489235328a4fdf27ba4a84ac64cb3d89f76c3c53d148d83d4:09c78eb62a:afc8c64ca140a9706f972319f48edffee405b5eed88dbad1c946d9bb8bab48bec8547d066bde4cb1cc7e99eee0d53faf01057a99d49bee517d424d0156e02486733a0fd4f311951ed4420a2125b092fa9041a3e01fd3e642e1c2d844b3dd64d3
ea697bc01540c1b708f03c9369d2ad84ac0e2ff087a17bf9c868b64d6076c6c0:f7a40db556:8151b87961f1f4f6a33993469131fa7d02f40ea3d7191fc72aee115a8bf8454e57f12b7c67fa4b56fcfb9767276c2c66017a25f362b59067a313a61fac44e6940bc82e445befb85e20f51f1444e54e353d641537b21e6d6fb2a865228b58edd1
fda3cf7385cf4bb164645966281a68f0447a0fdcd1ec7e29bf7313860f4a3394:03d67ac13cee:a81fd948bd75d7fb1b9744bd12a83288d1794bce09a8d7a63cb39758b090296356c6290e3cd63d4cb1ebbd169bdfe1991063d3d323bf87af7c469cb5947a93870400518b1a48d96f98456211b706be4073a34f0af9bfeb86c2adccb64a005656
b382e373ff8da48348d38447ef337570a9bafd91fab84c2a439f71880ea5e480:c89dc4de1fbc:8d3a93cbf8bce107c2dce15ddac3252792dfe2e9e9050e99100aae4d42ca1687b1a72abb372da6e01c761e7f37b6c6790ec1260c4e8052c3b21fc21c8a79fff22dbced49349f554f8efb34a20bc1aab0f252660eca89ece65a67097622e7e547
d15ae5df8ca3c5e64b38d24653fc9bf809534e98f3c7162623b65d9e70d8d2c5:0ae955dfad3573:8ecfa5cd8164188ddbad47d4f7600751116c6ac96d5bf74cd8feccdd59366ceba5bceaba2241f0b4355b9ca7b3963e54076b31e58f38d9a8dbf8ef3c887514fec83bf2f3a82fa2d2bffced10fe7113388c86d9d441867b499caed7e2e45cd98e
bdb098850c669ddde2018c5cea8b9f9a356c317868ddfc61ebf120d2ee9b6627:477405798be816:8108bdda52459a367f5ee6d2fb92a62144d2134cbf6755cc6c6ae4fde35f3d8c5a1ac6b5feb86290a76ffe5972cba95118dd0d095473e8d2e2027f9f10c67a388b4802f190ef8900eaef3246b26e2ab642c317a66f9571f339744dda641b9248
f62d28e6fc14fb70610228ee1f4f7d2e065e05caaf18b766892eeabf7c05f924:06097228065d18c0:b82649d765a3fff1d72ee15b7906579c4f0a608fd03adb53c6214de0e5095383ea00b66d75b6a12b8c433251af517a61053d69cf3dfeebdd929275984d9d3b1f8eaadd308824fffb9bb4e5437539a2f3d68330dbb021524a1d0d4523e5adc34a
27826e51f6e6446065e32c5b8e60aaf0109008d4eb9c2336a437ecb6fe415618:4f77125824409934:894e75525f6c5067b5274b52f39f3d0991ef27b9396ead5555f0dac07d07a962f3cc00b20e8324b69873abff1f8285d904428f93fc42354bb1cc50d95149014dd25b7000b161c4dbaaae54a90bcbde7e8c99c2111181e4e1e08648b4bec81cb4
984cfc344dd7da98dc2dcbcb8427e0195989e6b06380dd7dde4d73e289e1013c:093906e014849826bb:b74fbc224468498a680c923e1e9e8b90e7e55e44ffcb7cf1d2cf5bcbac1964e238025e30565325f2a1854244fa4ab233091c298fb9d5ba2525620f900960819e21f2e3d364eeac93f82c1303a141b883d29bbf4de2796daeb41f20cc05f0cd48
ac47cf8395c7858c34896e1501c77b1028bb00f15721d92b4208a13cad2fbdac:3c65e5bd83d0464de9:9414a283286bb2eebde7e00e5510e406e96750a3595a104b0a003fa181ca8022eb688d6ed5363711b59d86f3f0cc696d08ee7c6939b9d7ec44a8d597880e15700d6cdff0fdd76ff814797b7b928f123a8dc22425333064821992559baf7f4b4c

View File

@@ -0,0 +1,3 @@
:0000000000000000000000000000000000000000000000000000000000000000
aaaaaabbbbbbccccccddddddeeeeeeffffffgggggghhhhhh:2228450bf55d8fe62395161bd3677ff6fc28e45b89bc87e02a818eda11a8c5da
111111222222333333444444555555666666777777888888:4aa543cbd2f0c8f37f8a375ce2e383eb343e7e3405f61e438b0a15fb8899d1ae

View File

@@ -0,0 +1,8 @@
module blstests
go 1.19
require (
github.com/kilic/bls12-381 v0.1.0 // indirect
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1 // indirect
)

View File

@@ -0,0 +1,4 @@
github.com/kilic/bls12-381 v0.1.0 h1:encrdjqKMEvabVQ7qYOKu1OvhqpK4s47wDYtNiPtlp4=
github.com/kilic/bls12-381 v0.1.0/go.mod h1:vDTTHJONJ6G+P2R74EhnyotQDTliQDnFEwhdmfzw1ig=
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1 h1:a/mKvvZr9Jcc8oKfcmgzyp7OwF73JPWsQLvH1z2Kxck=
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,29 @@
package main
import (
"encoding/hex"
"encoding/json"
"fmt"
kilic "github.com/kilic/bls12-381"
)
func main() {
g1 := kilic.NewG1()
g2 := kilic.NewG2()
gt := kilic.NewGT()
p1 := g1.One()
p2 := g2.One()
bls := kilic.NewEngine()
out := []string{}
for i := 0; i < 1000; i++ {
res := bls.AddPair(p1, p2).Result()
out = append(out, hex.EncodeToString(gt.ToBytes(res)))
g1.Add(p1, p1, g1.One())
g2.Add(p2, p2, g2.One())
}
bytes, _ := json.Marshal(out)
fmt.Println(string(bytes))
}

View File

@@ -0,0 +1,25 @@
// This can be done inside of tests, but ESM is broken, jest doesn't want to work with it,
// and ESM itself contaminates everything it touches
(async () => {
const P = await import('micro-packed');
const { readFileSync } = require('fs');
const CompresedG1 = P.array(null, P.hex(48));
const UncompresedG1 = P.array(null, P.hex(2 * 48));
const CompresedG2 = P.array(null, P.hex(2 * 48));
const UncompresedG2 = P.array(null, P.hex(4 * 48));
const out = {
G1_Compressed: CompresedG1.decode(readFileSync('./g1_compressed_valid_test_vectors.dat')),
G1_Uncompressed: UncompresedG1.decode(readFileSync('./g1_uncompressed_valid_test_vectors.dat')),
G2_Compressed: CompresedG2.decode(readFileSync('./g2_compressed_valid_test_vectors.dat')),
G2_Uncompressed: UncompresedG2.decode(readFileSync('./g2_uncompressed_valid_test_vectors.dat')),
};
// Should be 1000
// console.log(
// 'T',
// Object.values(out).map((i) => i.length)
// );
console.log(JSON.stringify(out));
})();

File diff suppressed because one or more lines are too long

663
test/ed25519.test.js Normal file
View File

@@ -0,0 +1,663 @@
import { deepEqual, deepStrictEqual, strictEqual, throws } from 'assert';
import { describe, should } from 'micro-should';
import * as fc from 'fast-check';
import {
ed25519,
ed25519ctx,
ed25519ph,
x25519,
RistrettoPoint,
ED25519_TORSION_SUBGROUP,
} from '../lib/esm/ed25519.js';
import { readFileSync } from 'fs';
import { default as zip215 } from './ed25519/zip215.json' assert { type: 'json' };
import { hexToBytes, bytesToHex, randomBytes } from '@noble/hashes/utils';
import { numberToBytesLE } from '../lib/esm/abstract/utils.js';
import { sha512 } from '@noble/hashes/sha512';
import { default as ed25519vectors } from './wycheproof/eddsa_test.json' assert { type: 'json' };
import { default as x25519vectors } from './wycheproof/x25519_test.json' assert { type: 'json' };
describe('ed25519', () => {
const ed = ed25519;
const hex = bytesToHex;
const Point = ed.ExtendedPoint;
function to32Bytes(numOrStr) {
let hex = typeof numOrStr === 'string' ? numOrStr : numOrStr.toString(16);
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', () => {
const invalidPriv =
100000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800073278156000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000n;
throws(() => ed.getPublicKey(invalidPriv));
});
should('verify recent signature', () => {
fc.assert(
fc.property(
fc.hexaString({ minLength: 2, maxLength: 32 }),
fc.bigInt(2n, ed.CURVE.n),
(message, privateKey) => {
const publicKey = ed.getPublicKey(to32Bytes(privateKey));
const signature = ed.sign(to32Bytes(message), to32Bytes(privateKey));
deepStrictEqual(publicKey.length, 32);
deepStrictEqual(signature.length, 64);
deepStrictEqual(ed.verify(signature, to32Bytes(message), publicKey), true);
}
),
{ numRuns: 5 }
);
});
should('not verify signature with wrong message', () => {
fc.assert(
fc.property(
fc.array(fc.integer({ min: 0x00, max: 0xff })),
fc.array(fc.integer({ min: 0x00, max: 0xff })),
fc.bigInt(1n, ed.CURVE.n),
(bytes, wrongBytes, privateKey) => {
const privKey = to32Bytes(privateKey);
const message = new Uint8Array(bytes);
const wrongMessage = new Uint8Array(wrongBytes);
const publicKey = ed.getPublicKey(privKey);
const signature = ed.sign(message, privKey);
deepStrictEqual(
ed.verify(signature, wrongMessage, publicKey),
bytes.toString() === wrongBytes.toString()
);
}
),
{ numRuns: 5 }
);
});
const privKey = to32Bytes('a665a45920422f9d417e4867ef');
const wrongPriv = to32Bytes('a675a45920422f9d417e4867ef');
const msg = hexToBytes('874f9960c5d2b7a9b5fad383e1ba44719ebb743a');
const wrongMsg = hexToBytes('589d8c7f1da0a24bc07b7381ad48b1cfc211af1c');
describe('basic methods', () => {
should('sign and verify', () => {
const publicKey = ed.getPublicKey(privKey);
const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, msg, publicKey), true);
});
});
describe('sync methods', () => {
should('sign and verify', () => {
const publicKey = ed.getPublicKey(privKey);
const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, msg, publicKey), true);
});
should('not verify signature with wrong public key', () => {
const publicKey = ed.getPublicKey(wrongPriv);
const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, msg, publicKey), false);
});
should('not verify signature with wrong hash', () => {
const publicKey = ed.getPublicKey(privKey);
const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, wrongMsg, publicKey), false);
});
});
describe('BASE_POINT.multiply()', () => {
// https://xmr.llcoins.net/addresstests.html
should('create right publicKey without SHA-512 hashing TEST 1', () => {
const publicKey =
Point.BASE.multiply(0x90af56259a4b6bfbc4337980d5d75fbe3c074630368ff3804d33028e5dbfa77n);
deepStrictEqual(
publicKey.toHex(),
'0f3b913371411b27e646b537e888f685bf929ea7aab93c950ed84433f064480d'
);
});
should('create right publicKey without SHA-512 hashing TEST 2', () => {
const publicKey =
Point.BASE.multiply(0x364e8711a60780382a5d57b061c126f039940f28a9e91fe039d4d3094d8b88n);
deepStrictEqual(
publicKey.toHex(),
'ad545340b58610f0cd62f17d55af1ab11ecde9c084d5476865ddb4dbda015349'
);
});
should('create right publicKey without SHA-512 hashing TEST 3', () => {
const publicKey =
Point.BASE.multiply(0xb9bf90ff3abec042752cac3a07a62f0c16cfb9d32a3fc2305d676ec2d86e941n);
deepStrictEqual(
publicKey.toHex(),
'e097c4415fe85724d522b2e449e8fd78dd40d20097bdc9ae36fe8ec6fe12cb8c'
);
});
should('create right publicKey without SHA-512 hashing TEST 4', () => {
const publicKey =
Point.BASE.multiply(0x69d896f02d79524c9878e080308180e2859d07f9f54454e0800e8db0847a46en);
deepStrictEqual(
publicKey.toHex(),
'f12cb7c43b59971395926f278ce7c2eaded9444fbce62ca717564cb508a0db1d'
);
});
should('throw Point#multiply on TEST 5', () => {
for (const num of [0n, 0, -1n, -1, 1.1]) {
throws(() => Point.BASE.multiply(num));
}
});
});
// https://ed25519.cr.yp.to/python/sign.py
// https://ed25519.cr.yp.to/python/sign.input
const data = readFileSync('./test/ed25519/vectors.txt', 'utf-8');
const vectors = data
.trim()
.split('\n')
.map((line) => line.split(':'));
should('ed25519 official vectors/should match 1024 official vectors', () => {
for (let i = 0; i < vectors.length; i++) {
const vector = vectors[i];
// Extract.
const priv = vector[0].slice(0, 64);
const expectedPub = vector[1];
const msg = vector[2];
const expectedSignature = vector[3].slice(0, 128);
// Calculate
const pub = ed.getPublicKey(to32Bytes(priv));
deepStrictEqual(hex(pub), expectedPub);
deepStrictEqual(pub, Point.fromHex(pub).toRawBytes());
const signature = hex(ed.sign(msg, priv));
// console.log('vector', i);
// expect(pub).toBe(expectedPub);
deepStrictEqual(signature, expectedSignature);
}
});
// https://tools.ietf.org/html/rfc8032#section-7
should('rfc8032 vectors/should create right signature for 0x9d and empty string', () => {
const privateKey = '9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60';
const publicKey = ed.getPublicKey(privateKey);
const message = '';
const signature = ed.sign(message, privateKey);
deepStrictEqual(
hex(publicKey),
'd75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a'
);
deepStrictEqual(
hex(signature),
'e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b'
);
});
should('rfc8032 vectors/should create right signature for 0x4c and 72', () => {
const privateKey = '4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb';
const publicKey = ed.getPublicKey(privateKey);
const message = '72';
const signature = ed.sign(message, privateKey);
deepStrictEqual(
hex(publicKey),
'3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c'
);
deepStrictEqual(
hex(signature),
'92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00'
);
});
should('rfc8032 vectors/should create right signature for 0x00 and 5a', () => {
const privateKey = '002fdd1f7641793ab064bb7aa848f762e7ec6e332ffc26eeacda141ae33b1783';
const publicKey = ed.getPublicKey(privateKey);
const message =
'5ac1dfc324f43e6cb79a87ab0470fa857b51fb944982e19074ca44b1e40082c1d07b92efa7ea55ad42b7c027e0b9e33756d95a2c1796a7c2066811dc41858377d4b835c1688d638884cd2ad8970b74c1a54aadd27064163928a77988b24403aa85af82ceab6b728e554761af7175aeb99215b7421e4474c04d213e01ff03e3529b11077cdf28964b8c49c5649e3a46fa0a09dcd59dcad58b9b922a83210acd5e65065531400234f5e40cddcf9804968e3e9ac6f5c44af65001e158067fc3a660502d13fa8874fa93332138d9606bc41b4cee7edc39d753dae12a873941bb357f7e92a4498847d6605456cb8c0b425a47d7d3ca37e54e903a41e6450a35ebe5237c6f0c1bbbc1fd71fb7cd893d189850295c199b7d88af26bc8548975fda1099ffefee42a52f3428ddff35e0173d3339562507ac5d2c45bbd2c19cfe89b';
const signature = ed.sign(message, privateKey);
deepStrictEqual(
hex(publicKey),
'77d1d8ebacd13f4e2f8a40e28c4a63bc9ce3bfb69716334bcb28a33eb134086c'
);
deepStrictEqual(
hex(signature),
'0df3aa0d0999ad3dc580378f52d152700d5b3b057f56a66f92112e441e1cb9123c66f18712c87efe22d2573777296241216904d7cdd7d5ea433928bd2872fa0c'
);
});
should('rfc8032 vectors/should create right signature for 0xf5 and long msg', () => {
const privateKey = 'f5e5767cf153319517630f226876b86c8160cc583bc013744c6bf255f5cc0ee5';
const publicKey = ed.getPublicKey(privateKey);
const message =
'08b8b2b733424243760fe426a4b54908632110a66c2f6591eabd3345e3e4eb98fa6e264bf09efe12ee50f8f54e9f77b1e355f6c50544e23fb1433ddf73be84d879de7c0046dc4996d9e773f4bc9efe5738829adb26c81b37c93a1b270b20329d658675fc6ea534e0810a4432826bf58c941efb65d57a338bbd2e26640f89ffbc1a858efcb8550ee3a5e1998bd177e93a7363c344fe6b199ee5d02e82d522c4feba15452f80288a821a579116ec6dad2b3b310da903401aa62100ab5d1a36553e06203b33890cc9b832f79ef80560ccb9a39ce767967ed628c6ad573cb116dbefefd75499da96bd68a8a97b928a8bbc103b6621fcde2beca1231d206be6cd9ec7aff6f6c94fcd7204ed3455c68c83f4a41da4af2b74ef5c53f1d8ac70bdcb7ed185ce81bd84359d44254d95629e9855a94a7c1958d1f8ada5d0532ed8a5aa3fb2d17ba70eb6248e594e1a2297acbbb39d502f1a8c6eb6f1ce22b3de1a1f40cc24554119a831a9aad6079cad88425de6bde1a9187ebb6092cf67bf2b13fd65f27088d78b7e883c8759d2c4f5c65adb7553878ad575f9fad878e80a0c9ba63bcbcc2732e69485bbc9c90bfbd62481d9089beccf80cfe2df16a2cf65bd92dd597b0707e0917af48bbb75fed413d238f5555a7a569d80c3414a8d0859dc65a46128bab27af87a71314f318c782b23ebfe808b82b0ce26401d2e22f04d83d1255dc51addd3b75a2b1ae0784504df543af8969be3ea7082ff7fc9888c144da2af58429ec96031dbcad3dad9af0dcbaaaf268cb8fcffead94f3c7ca495e056a9b47acdb751fb73e666c6c655ade8297297d07ad1ba5e43f1bca32301651339e22904cc8c42f58c30c04aafdb038dda0847dd988dcda6f3bfd15c4b4c4525004aa06eeff8ca61783aacec57fb3d1f92b0fe2fd1a85f6724517b65e614ad6808d6f6ee34dff7310fdc82aebfd904b01e1dc54b2927094b2db68d6f903b68401adebf5a7e08d78ff4ef5d63653a65040cf9bfd4aca7984a74d37145986780fc0b16ac451649de6188a7dbdf191f64b5fc5e2ab47b57f7f7276cd419c17a3ca8e1b939ae49e488acba6b965610b5480109c8b17b80e1b7b750dfc7598d5d5011fd2dcc5600a32ef5b52a1ecc820e308aa342721aac0943bf6686b64b2579376504ccc493d97e6aed3fb0f9cd71a43dd497f01f17c0e2cb3797aa2a2f256656168e6c496afc5fb93246f6b1116398a346f1a641f3b041e989f7914f90cc2c7fff357876e506b50d334ba77c225bc307ba537152f3f1610e4eafe595f6d9d90d11faa933a15ef1369546868a7f3a45a96768d40fd9d03412c091c6315cf4fde7cb68606937380db2eaaa707b4c4185c32eddcdd306705e4dc1ffc872eeee475a64dfac86aba41c0618983f8741c5ef68d3a101e8a3b8cac60c905c15fc910840b94c00a0b9d0';
const signature = ed.sign(message, privateKey);
deepStrictEqual(
hex(publicKey),
'278117fc144c72340f67d0f2316e8386ceffbf2b2428c9c51fef7c597f1d426e'
);
deepStrictEqual(
hex(signature),
'0aab4c900501b3e24d7cdf4663326a3a87df5e4843b2cbdb67cbf6e460fec350aa5371b1508f9f4528ecea23c436d94b5e8fcd4f681e30a6ac00a9704a188a03'
);
});
// const PRIVATE_KEY = 0xa665a45920422f9d417e4867efn;
// const MESSAGE = ripemd160(new Uint8Array([97, 98, 99, 100, 101, 102, 103]));
// prettier-ignore
// const MESSAGE = new Uint8Array([
// 135, 79, 153, 96, 197, 210, 183, 169, 181, 250, 211, 131, 225, 186, 68, 113, 158, 187, 116, 58,
// ]);
// const WRONG_MESSAGE = ripemd160(new Uint8Array([98, 99, 100, 101, 102, 103]));
// prettier-ignore
// const WRONG_MESSAGE = new Uint8Array([
// 88, 157, 140, 127, 29, 160, 162, 75, 192, 123, 115, 129, 173, 72, 177, 207, 194, 17, 175, 28,
// ]);
// // it("should verify just signed message", async () => {
// // await fc.assert(fc.asyncProperty(
// // fc.hexa(),
// // fc.bigInt(2n, ristretto25519.PRIME_ORDER),
// // async (message, privateKey) => {
// // const publicKey = await ristretto25519.getPublicKey(privateKey);
// // const signature = await ristretto25519.sign(message, privateKey);
// // expect(publicKey.length).toBe(32);
// // expect(signature.length).toBe(64);
// // expect(await ristretto25519.verify(signature, message, publicKey)).toBe(true);
// // }),
// // { numRuns: 1 }
// // );
// // });
// // it("should not verify sign with wrong message", async () => {
// // await fc.assert(fc.asyncProperty(
// // fc.array(fc.integer(0x00, 0xff)),
// // fc.array(fc.integer(0x00, 0xff)),
// // fc.bigInt(2n, ristretto25519.PRIME_ORDER),
// // async (bytes, wrongBytes, privateKey) => {
// // const message = new Uint8Array(bytes);
// // const wrongMessage = new Uint8Array(wrongBytes);
// // const publicKey = await ristretto25519.getPublicKey(privateKey);
// // const signature = await ristretto25519.sign(message, privateKey);
// // expect(await ristretto25519.verify(signature, wrongMessage, publicKey)).toBe(
// // bytes.toString() === wrongBytes.toString()
// // );
// // }),
// // { numRuns: 1 }
// // );
// // });
// // it("should sign and verify", async () => {
// // const publicKey = await ristretto25519.getPublicKey(PRIVATE_KEY);
// // const signature = await ristretto25519.sign(MESSAGE, PRIVATE_KEY);
// // expect(await ristretto25519.verify(signature, MESSAGE, publicKey)).toBe(true);
// // });
// // it("should not verify signature with wrong public key", async () => {
// // const publicKey = await ristretto25519.getPublicKey(12);
// // const signature = await ristretto25519.sign(MESSAGE, PRIVATE_KEY);
// // expect(await ristretto25519.verify(signature, MESSAGE, publicKey)).toBe(false);
// // });
// // it("should not verify signature with wrong hash", async () => {
// // const publicKey = await ristretto25519.getPublicKey(PRIVATE_KEY);
// // const signature = await ristretto25519.sign(MESSAGE, PRIVATE_KEY);
// // expect(await ristretto25519.verify(signature, WRONG_MESSAGE, publicKey)).toBe(false);
// // });
should('ristretto255/should follow the byte encodings of small multiples', () => {
const encodingsOfSmallMultiples = [
// This is the identity point
'0000000000000000000000000000000000000000000000000000000000000000',
// This is the basepoint
'e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76',
// These are small multiples of the basepoint
'6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919',
'94741f5d5d52755ece4f23f044ee27d5d1ea1e2bd196b462166b16152a9d0259',
'da80862773358b466ffadfe0b3293ab3d9fd53c5ea6c955358f568322daf6a57',
'e882b131016b52c1d3337080187cf768423efccbb517bb495ab812c4160ff44e',
'f64746d3c92b13050ed8d80236a7f0007c3b3f962f5ba793d19a601ebb1df403',
'44f53520926ec81fbd5a387845beb7df85a96a24ece18738bdcfa6a7822a176d',
'903293d8f2287ebe10e2374dc1a53e0bc887e592699f02d077d5263cdd55601c',
'02622ace8f7303a31cafc63f8fc48fdc16e1c8c8d234b2f0d6685282a9076031',
'20706fd788b2720a1ed2a5dad4952b01f413bcf0e7564de8cdc816689e2db95f',
'bce83f8ba5dd2fa572864c24ba1810f9522bc6004afe95877ac73241cafdab42',
'e4549ee16b9aa03099ca208c67adafcafa4c3f3e4e5303de6026e3ca8ff84460',
'aa52e000df2e16f55fb1032fc33bc42742dad6bd5a8fc0be0167436c5948501f',
'46376b80f409b29dc2b5f6f0c52591990896e5716f41477cd30085ab7f10301e',
'e0c418f7c8d9c4cdd7395b93ea124f3ad99021bb681dfc3302a9d99a2e53e64e',
];
let B = RistrettoPoint.BASE;
let P = RistrettoPoint.ZERO;
for (const encoded of encodingsOfSmallMultiples) {
deepStrictEqual(P.toHex(), encoded);
deepStrictEqual(RistrettoPoint.fromHex(encoded).toHex(), encoded);
P = P.add(B);
}
});
should('ristretto255/should not convert bad bytes encoding', () => {
const badEncodings = [
// These are all bad because they're non-canonical field encodings.
'00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f',
'f3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f',
'edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f',
// These are all bad because they're negative field elements.
'0100000000000000000000000000000000000000000000000000000000000000',
'01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f',
'ed57ffd8c914fb201471d1c3d245ce3c746fcbe63a3679d51b6a516ebebe0e20',
'c34c4e1826e5d403b78e246e88aa051c36ccf0aafebffe137d148a2bf9104562',
'c940e5a4404157cfb1628b108db051a8d439e1a421394ec4ebccb9ec92a8ac78',
'47cfc5497c53dc8e61c91d17fd626ffb1c49e2bca94eed052281b510b1117a24',
'f1c6165d33367351b0da8f6e4511010c68174a03b6581212c71c0e1d026c3c72',
'87260f7a2f12495118360f02c26a470f450dadf34a413d21042b43b9d93e1309',
// These are all bad because they give a nonsquare x².
'26948d35ca62e643e26a83177332e6b6afeb9d08e4268b650f1f5bbd8d81d371',
'4eac077a713c57b4f4397629a4145982c661f48044dd3f96427d40b147d9742f',
'de6a7b00deadc788eb6b6c8d20c0ae96c2f2019078fa604fee5b87d6e989ad7b',
'bcab477be20861e01e4a0e295284146a510150d9817763caf1a6f4b422d67042',
'2a292df7e32cababbd9de088d1d1abec9fc0440f637ed2fba145094dc14bea08',
'f4a9e534fc0d216c44b218fa0c42d99635a0127ee2e53c712f70609649fdff22',
'8268436f8c4126196cf64b3c7ddbda90746a378625f9813dd9b8457077256731',
'2810e5cbc2cc4d4eece54f61c6f69758e289aa7ab440b3cbeaa21995c2f4232b',
// These are all bad because they give a negative xy value.
'3eb858e78f5a7254d8c9731174a94f76755fd3941c0ac93735c07ba14579630e',
'a45fdc55c76448c049a1ab33f17023edfb2be3581e9c7aade8a6125215e04220',
'd483fe813c6ba647ebbfd3ec41adca1c6130c2beeee9d9bf065c8d151c5f396e',
'8a2e1d30050198c65a54483123960ccc38aef6848e1ec8f5f780e8523769ba32',
'32888462f8b486c68ad7dd9610be5192bbeaf3b443951ac1a8118419d9fa097b',
'227142501b9d4355ccba290404bde41575b037693cef1f438c47f8fbf35d1165',
'5c37cc491da847cfeb9281d407efc41e15144c876e0170b499a96a22ed31e01e',
'445425117cb8c90edcbc7c1cc0e74f747f2c1efa5630a967c64f287792a48a4b',
// This is s = -1, which causes y = 0.
'ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f',
];
for (const badBytes of badEncodings) {
const b = hexToBytes(badBytes);
throws(() => RistrettoPoint.fromHex(b), badBytes);
}
});
should('ristretto255/should create right points from uniform hash', () => {
const labels = [
'Ristretto is traditionally a short shot of espresso coffee',
'made with the normal amount of ground coffee but extracted with',
'about half the amount of water in the same amount of time',
'by using a finer grind.',
'This produces a concentrated shot of coffee per volume.',
'Just pulling a normal shot short will produce a weaker shot',
'and is not a Ristretto as some believe.',
];
const encodedHashToPoints = [
'3066f82a1a747d45120d1740f14358531a8f04bbffe6a819f86dfe50f44a0a46',
'f26e5b6f7d362d2d2a94c5d0e7602cb4773c95a2e5c31a64f133189fa76ed61b',
'006ccd2a9e6867e6a2c5cea83d3302cc9de128dd2a9a57dd8ee7b9d7ffe02826',
'f8f0c87cf237953c5890aec3998169005dae3eca1fbb04548c635953c817f92a',
'ae81e7dedf20a497e10c304a765c1767a42d6e06029758d2d7e8ef7cc4c41179',
'e2705652ff9f5e44d3e841bf1c251cf7dddb77d140870d1ab2ed64f1a9ce8628',
'80bd07262511cdde4863f8a7434cef696750681cb9510eea557088f76d9e5065',
];
for (let i = 0; i < labels.length; i++) {
const hash = sha512(utf8ToBytes(labels[i]));
const point = RistrettoPoint.hashToCurve(hash);
deepStrictEqual(point.toHex(), encodedHashToPoints[i]);
}
});
should('input immutability: sign/verify are immutable', () => {
const privateKey = ed.utils.randomPrivateKey();
const publicKey = ed.getPublicKey(privateKey);
for (let i = 0; i < 100; i++) {
let payload = randomBytes(100);
let signature = ed.sign(payload, privateKey);
if (!ed.verify(signature, payload, publicKey)) {
throw new Error('Signature verification failed');
}
const signatureCopy = Buffer.alloc(signature.byteLength);
signatureCopy.set(signature, 0); // <-- breaks
payload = payload.slice();
signature = signature.slice();
if (!ed.verify(signatureCopy, payload, publicKey))
throw new Error('Copied signature verification failed');
}
});
// 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;
}
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));
});
const rfc7748Mul = [
{
scalar: 'a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4',
u: 'e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c',
outputU: 'c3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552',
},
{
scalar: '4b66e9d4d1b4673c5ad22691957d6af5c11b6421e0ea01d42ca4169e7918ba0d',
u: 'e5210f12786811d3f4b7959d0538ae2c31dbe7106fc03c3efc4cd549c715a493',
outputU: '95cbde9476e8907d7aade45cb4b873f88b595a68799fa152e6f8f7647aac7957',
},
];
for (let i = 0; i < rfc7748Mul.length; i++) {
const v = rfc7748Mul[i];
should(`RFC7748: scalarMult (${i})`, () => {
deepStrictEqual(hex(x25519.scalarMult(v.scalar, v.u)), v.outputU);
});
}
const rfc7748Iter = [
{ scalar: '422c8e7a6227d7bca1350b3e2bb7279f7897b87bb6854b783c60e80311ae3079', iters: 1 },
{ scalar: '684cf59ba83309552800ef566f2f4d3c1c3887c49360e3875f2eb94d99532c51', iters: 1000 },
// { scalar: '7c3911e0ab2586fd864497297e575e6f3bc601c0883c30df5f4dd2d24f665424', iters: 1000000 },
];
for (let i = 0; i < rfc7748Iter.length; i++) {
const { scalar, iters } = rfc7748Iter[i];
should(`RFC7748: scalarMult iteration (${i})`, () => {
let k = x25519.Gu;
for (let i = 0, u = k; i < iters; i++) [k, u] = [x25519.scalarMult(k, u), k];
deepStrictEqual(hex(k), scalar);
});
}
should('RFC7748 getSharedKey', () => {
const alicePrivate = '77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a';
const alicePublic = '8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a';
const bobPrivate = '5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb';
const bobPublic = 'de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f';
const shared = '4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742';
deepStrictEqual(alicePublic, hex(x25519.getPublicKey(alicePrivate)));
deepStrictEqual(bobPublic, hex(x25519.getPublicKey(bobPrivate)));
deepStrictEqual(hex(x25519.scalarMult(alicePrivate, bobPublic)), shared);
deepStrictEqual(hex(x25519.scalarMult(bobPrivate, alicePublic)), shared);
});
// should('X25519/getSharedSecret() should be commutative', () => {
// 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;
// }
// }
// });
// should('X25519: should convert base point to montgomery using fromPoint', () => {
// deepStrictEqual(
// hex(ed.montgomeryCurve.UfromPoint(Point.BASE)),
// ed.montgomeryCurve.BASE_POINT_U
// );
// });
{
const group = x25519vectors.testGroups[0];
should(`Wycheproof/X25519`, () => {
for (let i = 0; i < group.tests.length; i++) {
const v = group.tests[i];
const comment = `(${i}, ${v.result}) ${v.comment}`;
if (v.result === 'valid' || v.result === 'acceptable') {
try {
const shared = hex(x25519.scalarMult(v.private, v.public));
deepStrictEqual(shared, v.shared, comment);
} catch (e) {
// We are more strict
if (e.message.includes('Expected valid scalar')) return;
if (e.message.includes('Invalid private or public key received')) return;
throw e;
}
} else if (v.result === 'invalid') {
let failed = false;
try {
x25519.scalarMult(v.private, v.public);
} catch (error) {
failed = true;
}
deepStrictEqual(failed, true, comment);
} else throw new Error('unknown test result');
}
});
}
should(`Wycheproof/ED25519`, () => {
for (let g = 0; g < ed25519vectors.testGroups.length; g++) {
const group = ed25519vectors.testGroups[g];
const key = group.key;
deepStrictEqual(hex(ed.getPublicKey(key.sk)), key.pk, `(${g}, public)`);
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(hex(ed.sign(v.msg, key.sk)), v.sig, comment);
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('Property test issue #1', () => {
const message = new Uint8Array([12, 12, 12]);
const signature = ed.sign(message, to32Bytes(1n));
const publicKey = ed.getPublicKey(to32Bytes(1n)); // <- was 1n
deepStrictEqual(ed.verify(signature, message, publicKey), true);
});
const VECTORS_RFC8032_CTX = [
{
secretKey: '0305334e381af78f141cb666f6199f57bc3495335a256a95bd2a55bf546663f6',
publicKey: 'dfc9425e4f968f7f0c29f0259cf5f9aed6851c2bb4ad8bfb860cfee0ab248292',
message: 'f726936d19c800494e3fdaff20b276a8',
context: '666f6f',
signature:
'55a4cc2f70a54e04288c5f4cd1e45a7b' +
'b520b36292911876cada7323198dd87a' +
'8b36950b95130022907a7fb7c4e9b2d5' +
'f6cca685a587b4b21f4b888e4e7edb0d',
},
{
secretKey: '0305334e381af78f141cb666f6199f57bc3495335a256a95bd2a55bf546663f6',
publicKey: 'dfc9425e4f968f7f0c29f0259cf5f9aed6851c2bb4ad8bfb860cfee0ab248292',
message: 'f726936d19c800494e3fdaff20b276a8',
context: '626172',
signature:
'fc60d5872fc46b3aa69f8b5b4351d580' +
'8f92bcc044606db097abab6dbcb1aee3' +
'216c48e8b3b66431b5b186d1d28f8ee1' +
'5a5ca2df6668346291c2043d4eb3e90d',
},
{
secretKey: '0305334e381af78f141cb666f6199f57bc3495335a256a95bd2a55bf546663f6',
publicKey: 'dfc9425e4f968f7f0c29f0259cf5f9aed6851c2bb4ad8bfb860cfee0ab248292',
message: '508e9e6882b979fea900f62adceaca35',
context: '666f6f',
signature:
'8b70c1cc8310e1de20ac53ce28ae6e72' +
'07f33c3295e03bb5c0732a1d20dc6490' +
'8922a8b052cf99b7c4fe107a5abb5b2c' +
'4085ae75890d02df26269d8945f84b0b',
},
{
secretKey: 'ab9c2853ce297ddab85c993b3ae14bcad39b2c682beabc27d6d4eb20711d6560',
publicKey: '0f1d1274943b91415889152e893d80e93275a1fc0b65fd71b4b0dda10ad7d772',
message: 'f726936d19c800494e3fdaff20b276a8',
context: '666f6f',
signature:
'21655b5f1aa965996b3f97b3c849eafb' +
'a922a0a62992f73b3d1b73106a84ad85' +
'e9b86a7b6005ea868337ff2d20a7f5fb' +
'd4cd10b0be49a68da2b2e0dc0ad8960f',
},
];
for (let i = 0; i < VECTORS_RFC8032_CTX.length; i++) {
const v = VECTORS_RFC8032_CTX[i];
should(`RFC8032ctx/${i}`, () => {
deepStrictEqual(hex(ed25519ctx.getPublicKey(v.secretKey)), v.publicKey);
deepStrictEqual(hex(ed25519ctx.sign(v.message, v.secretKey, v.context)), v.signature);
deepStrictEqual(ed25519ctx.verify(v.signature, v.message, v.publicKey, v.context), true);
});
}
const VECTORS_RFC8032_PH = [
{
secretKey: '833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42',
publicKey: 'ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf',
message: '616263',
signature:
'98a70222f0b8121aa9d30f813d683f80' +
'9e462b469c7ff87639499bb94e6dae41' +
'31f85042463c2a355a2003d062adf5aa' +
'a10b8c61e636062aaad11c2a26083406',
},
];
for (let i = 0; i < VECTORS_RFC8032_PH.length; i++) {
const v = VECTORS_RFC8032_PH[i];
should(`RFC8032ph/${i}`, () => {
deepStrictEqual(hex(ed25519ph.getPublicKey(v.secretKey)), v.publicKey);
deepStrictEqual(hex(ed25519ph.sign(v.message, v.secretKey)), v.signature);
deepStrictEqual(ed25519ph.verify(v.signature, v.message, v.publicKey), true);
});
}
should('X25519 base point', () => {
const { y } = ed25519.ExtendedPoint.BASE;
const { Fp } = ed25519.CURVE;
const u = Fp.create((y + 1n) * Fp.inv(1n - y));
deepStrictEqual(hex(numberToBytesLE(u, 32)), x25519.Gu);
});
should('isTorsionFree()', () => {
const orig = ed.utils.getExtendedPublicKey(ed.utils.randomPrivateKey()).point;
for (const hex of ED25519_TORSION_SUBGROUP.slice(1)) {
const dirty = orig.add(Point.fromHex(hex));
const cleared = dirty.clearCofactor();
strictEqual(orig.isTorsionFree(), true, `orig must be torsionFree: ${hex}`);
strictEqual(dirty.isTorsionFree(), false, `dirty must not be torsionFree: ${hex}`);
strictEqual(cleared.isTorsionFree(), true, `cleared must be torsionFree: ${hex}`);
}
});
});
// ESM is broken.
import url from 'url';
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
should.run();
}

675
test/ed448.test.js Normal file
View File

@@ -0,0 +1,675 @@
import { deepStrictEqual, throws } from 'assert';
import { describe, should } from 'micro-should';
import * as fc from 'fast-check';
import { ed448, ed448ph, x448 } from '../lib/esm/ed448.js';
import { hexToBytes, bytesToHex, randomBytes } from '@noble/hashes/utils';
import { numberToBytesLE } from '../lib/esm/abstract/utils.js';
import { default as ed448vectors } from './wycheproof/ed448_test.json' assert { type: 'json' };
import { default as x448vectors } from './wycheproof/x448_test.json' assert { type: 'json' };
describe('ed448', () => {
const ed = ed448;
const hex = bytesToHex;
ed.utils.precompute(4);
const Point = ed.ExtendedPoint;
should(`Basic`, () => {
const G1 = Point.BASE.toAffine();
deepStrictEqual(
G1.x,
224580040295924300187604334099896036246789641632564134246125461686950415467406032909029192869357953282578032075146446173674602635247710n
);
deepStrictEqual(
G1.y,
298819210078481492676017930443930673437544040154080242095928241372331506189835876003536878655418784733982303233503462500531545062832660n
);
const G2 = Point.BASE.multiply(2n).toAffine();
deepStrictEqual(
G2.x,
484559149530404593699549205258669689569094240458212040187660132787056912146709081364401144455726350866276831544947397859048262938744149n
);
deepStrictEqual(
G2.y,
494088759867433727674302672526735089350544552303727723746126484473087719117037293890093462157703888342865036477787453078312060500281069n
);
const G3 = Point.BASE.multiply(3n).toAffine();
deepStrictEqual(
G3.x,
23839778817283171003887799738662344287085130522697782688245073320169861206004018274567429238677677920280078599146891901463786155880335n
);
deepStrictEqual(
G3.y,
636046652612779686502873775776967954190574036985351036782021535703553242737829645273154208057988851307101009474686328623630835377952508n
);
});
should('Basic/decompress', () => {
const G1 = Point.BASE;
const G2 = Point.BASE.multiply(2n);
const G3 = Point.BASE.multiply(3n);
const points = [G1, G2, G3];
const getXY = (p) => p.toAffine();
for (const p of points) deepStrictEqual(getXY(Point.fromHex(p.toHex())), getXY(p));
});
const VECTORS_RFC8032 = [
{
secretKey:
'6c82a562cb808d10d632be89c8513ebf' +
'6c929f34ddfa8c9f63c9960ef6e348a3' +
'528c8a3fcc2f044e39a3fc5b94492f8f' +
'032e7549a20098f95b',
publicKey:
'5fd7449b59b461fd2ce787ec616ad46a' +
'1da1342485a70e1f8a0ea75d80e96778' +
'edf124769b46c7061bd6783df1e50f6c' +
'd1fa1abeafe8256180',
message: '',
signature:
'533a37f6bbe457251f023c0d88f976ae' +
'2dfb504a843e34d2074fd823d41a591f' +
'2b233f034f628281f2fd7a22ddd47d78' +
'28c59bd0a21bfd3980ff0d2028d4b18a' +
'9df63e006c5d1c2d345b925d8dc00b41' +
'04852db99ac5c7cdda8530a113a0f4db' +
'b61149f05a7363268c71d95808ff2e65' +
'2600',
},
{
secretKey:
'c4eab05d357007c632f3dbb48489924d' +
'552b08fe0c353a0d4a1f00acda2c463a' +
'fbea67c5e8d2877c5e3bc397a659949e' +
'f8021e954e0a12274e',
publicKey:
'43ba28f430cdff456ae531545f7ecd0a' +
'c834a55d9358c0372bfa0c6c6798c086' +
'6aea01eb00742802b8438ea4cb82169c' +
'235160627b4c3a9480',
message: '03',
signature:
'26b8f91727bd62897af15e41eb43c377' +
'efb9c610d48f2335cb0bd0087810f435' +
'2541b143c4b981b7e18f62de8ccdf633' +
'fc1bf037ab7cd779805e0dbcc0aae1cb' +
'cee1afb2e027df36bc04dcecbf154336' +
'c19f0af7e0a6472905e799f1953d2a0f' +
'f3348ab21aa4adafd1d234441cf807c0' +
'3a00',
},
{
secretKey:
'cd23d24f714274e744343237b93290f5' +
'11f6425f98e64459ff203e8985083ffd' +
'f60500553abc0e05cd02184bdb89c4cc' +
'd67e187951267eb328',
publicKey:
'dcea9e78f35a1bf3499a831b10b86c90' +
'aac01cd84b67a0109b55a36e9328b1e3' +
'65fce161d71ce7131a543ea4cb5f7e9f' +
'1d8b00696447001400',
message: '0c3e544074ec63b0265e0c',
signature:
'1f0a8888ce25e8d458a21130879b840a' +
'9089d999aaba039eaf3e3afa090a09d3' +
'89dba82c4ff2ae8ac5cdfb7c55e94d5d' +
'961a29fe0109941e00b8dbdeea6d3b05' +
'1068df7254c0cdc129cbe62db2dc957d' +
'bb47b51fd3f213fb8698f064774250a5' +
'028961c9bf8ffd973fe5d5c206492b14' +
'0e00',
},
{
secretKey:
'258cdd4ada32ed9c9ff54e63756ae582' +
'fb8fab2ac721f2c8e676a72768513d93' +
'9f63dddb55609133f29adf86ec9929dc' +
'cb52c1c5fd2ff7e21b',
publicKey:
'3ba16da0c6f2cc1f30187740756f5e79' +
'8d6bc5fc015d7c63cc9510ee3fd44adc' +
'24d8e968b6e46e6f94d19b945361726b' +
'd75e149ef09817f580',
message: '64a65f3cdedcdd66811e2915',
signature:
'7eeeab7c4e50fb799b418ee5e3197ff6' +
'bf15d43a14c34389b59dd1a7b1b85b4a' +
'e90438aca634bea45e3a2695f1270f07' +
'fdcdf7c62b8efeaf00b45c2c96ba457e' +
'b1a8bf075a3db28e5c24f6b923ed4ad7' +
'47c3c9e03c7079efb87cb110d3a99861' +
'e72003cbae6d6b8b827e4e6c143064ff' +
'3c00',
},
{
secretKey:
'7ef4e84544236752fbb56b8f31a23a10' +
'e42814f5f55ca037cdcc11c64c9a3b29' +
'49c1bb60700314611732a6c2fea98eeb' +
'c0266a11a93970100e',
publicKey:
'b3da079b0aa493a5772029f0467baebe' +
'e5a8112d9d3a22532361da294f7bb381' +
'5c5dc59e176b4d9f381ca0938e13c6c0' +
'7b174be65dfa578e80',
message: '64a65f3cdedcdd66811e2915e7',
signature:
'6a12066f55331b6c22acd5d5bfc5d712' +
'28fbda80ae8dec26bdd306743c5027cb' +
'4890810c162c027468675ecf645a8317' +
'6c0d7323a2ccde2d80efe5a1268e8aca' +
'1d6fbc194d3f77c44986eb4ab4177919' +
'ad8bec33eb47bbb5fc6e28196fd1caf5' +
'6b4e7e0ba5519234d047155ac727a105' +
'3100',
},
{
secretKey:
'd65df341ad13e008567688baedda8e9d' +
'cdc17dc024974ea5b4227b6530e339bf' +
'f21f99e68ca6968f3cca6dfe0fb9f4fa' +
'b4fa135d5542ea3f01',
publicKey:
'df9705f58edbab802c7f8363cfe5560a' +
'b1c6132c20a9f1dd163483a26f8ac53a' +
'39d6808bf4a1dfbd261b099bb03b3fb5' +
'0906cb28bd8a081f00',
message:
'bd0f6a3747cd561bdddf4640a332461a' +
'4a30a12a434cd0bf40d766d9c6d458e5' +
'512204a30c17d1f50b5079631f64eb31' +
'12182da3005835461113718d1a5ef944',
signature:
'554bc2480860b49eab8532d2a533b7d5' +
'78ef473eeb58c98bb2d0e1ce488a98b1' +
'8dfde9b9b90775e67f47d4a1c3482058' +
'efc9f40d2ca033a0801b63d45b3b722e' +
'f552bad3b4ccb667da350192b61c508c' +
'f7b6b5adadc2c8d9a446ef003fb05cba' +
'5f30e88e36ec2703b349ca229c267083' +
'3900',
},
{
secretKey:
'2ec5fe3c17045abdb136a5e6a913e32a' +
'b75ae68b53d2fc149b77e504132d3756' +
'9b7e766ba74a19bd6162343a21c8590a' +
'a9cebca9014c636df5',
publicKey:
'79756f014dcfe2079f5dd9e718be4171' +
'e2ef2486a08f25186f6bff43a9936b9b' +
'fe12402b08ae65798a3d81e22e9ec80e' +
'7690862ef3d4ed3a00',
message:
'15777532b0bdd0d1389f636c5f6b9ba7' +
'34c90af572877e2d272dd078aa1e567c' +
'fa80e12928bb542330e8409f31745041' +
'07ecd5efac61ae7504dabe2a602ede89' +
'e5cca6257a7c77e27a702b3ae39fc769' +
'fc54f2395ae6a1178cab4738e543072f' +
'c1c177fe71e92e25bf03e4ecb72f47b6' +
'4d0465aaea4c7fad372536c8ba516a60' +
'39c3c2a39f0e4d832be432dfa9a706a6' +
'e5c7e19f397964ca4258002f7c0541b5' +
'90316dbc5622b6b2a6fe7a4abffd9610' +
'5eca76ea7b98816af0748c10df048ce0' +
'12d901015a51f189f3888145c03650aa' +
'23ce894c3bd889e030d565071c59f409' +
'a9981b51878fd6fc110624dcbcde0bf7' +
'a69ccce38fabdf86f3bef6044819de11',
signature:
'c650ddbb0601c19ca11439e1640dd931' +
'f43c518ea5bea70d3dcde5f4191fe53f' +
'00cf966546b72bcc7d58be2b9badef28' +
'743954e3a44a23f880e8d4f1cfce2d7a' +
'61452d26da05896f0a50da66a239a8a1' +
'88b6d825b3305ad77b73fbac0836ecc6' +
'0987fd08527c1a8e80d5823e65cafe2a' +
'3d00',
},
{
secretKey:
'872d093780f5d3730df7c212664b37b8' +
'a0f24f56810daa8382cd4fa3f77634ec' +
'44dc54f1c2ed9bea86fafb7632d8be19' +
'9ea165f5ad55dd9ce8',
publicKey:
'a81b2e8a70a5ac94ffdbcc9badfc3feb' +
'0801f258578bb114ad44ece1ec0e799d' +
'a08effb81c5d685c0c56f64eecaef8cd' +
'f11cc38737838cf400',
message:
'6ddf802e1aae4986935f7f981ba3f035' +
'1d6273c0a0c22c9c0e8339168e675412' +
'a3debfaf435ed651558007db4384b650' +
'fcc07e3b586a27a4f7a00ac8a6fec2cd' +
'86ae4bf1570c41e6a40c931db27b2faa' +
'15a8cedd52cff7362c4e6e23daec0fbc' +
'3a79b6806e316efcc7b68119bf46bc76' +
'a26067a53f296dafdbdc11c77f7777e9' +
'72660cf4b6a9b369a6665f02e0cc9b6e' +
'dfad136b4fabe723d2813db3136cfde9' +
'b6d044322fee2947952e031b73ab5c60' +
'3349b307bdc27bc6cb8b8bbd7bd32321' +
'9b8033a581b59eadebb09b3c4f3d2277' +
'd4f0343624acc817804728b25ab79717' +
'2b4c5c21a22f9c7839d64300232eb66e' +
'53f31c723fa37fe387c7d3e50bdf9813' +
'a30e5bb12cf4cd930c40cfb4e1fc6225' +
'92a49588794494d56d24ea4b40c89fc0' +
'596cc9ebb961c8cb10adde976a5d602b' +
'1c3f85b9b9a001ed3c6a4d3b1437f520' +
'96cd1956d042a597d561a596ecd3d173' +
'5a8d570ea0ec27225a2c4aaff26306d1' +
'526c1af3ca6d9cf5a2c98f47e1c46db9' +
'a33234cfd4d81f2c98538a09ebe76998' +
'd0d8fd25997c7d255c6d66ece6fa56f1' +
'1144950f027795e653008f4bd7ca2dee' +
'85d8e90f3dc315130ce2a00375a318c7' +
'c3d97be2c8ce5b6db41a6254ff264fa6' +
'155baee3b0773c0f497c573f19bb4f42' +
'40281f0b1f4f7be857a4e59d416c06b4' +
'c50fa09e1810ddc6b1467baeac5a3668' +
'd11b6ecaa901440016f389f80acc4db9' +
'77025e7f5924388c7e340a732e554440' +
'e76570f8dd71b7d640b3450d1fd5f041' +
'0a18f9a3494f707c717b79b4bf75c984' +
'00b096b21653b5d217cf3565c9597456' +
'f70703497a078763829bc01bb1cbc8fa' +
'04eadc9a6e3f6699587a9e75c94e5bab' +
'0036e0b2e711392cff0047d0d6b05bd2' +
'a588bc109718954259f1d86678a579a3' +
'120f19cfb2963f177aeb70f2d4844826' +
'262e51b80271272068ef5b3856fa8535' +
'aa2a88b2d41f2a0e2fda7624c2850272' +
'ac4a2f561f8f2f7a318bfd5caf969614' +
'9e4ac824ad3460538fdc25421beec2cc' +
'6818162d06bbed0c40a387192349db67' +
'a118bada6cd5ab0140ee273204f628aa' +
'd1c135f770279a651e24d8c14d75a605' +
'9d76b96a6fd857def5e0b354b27ab937' +
'a5815d16b5fae407ff18222c6d1ed263' +
'be68c95f32d908bd895cd76207ae7264' +
'87567f9a67dad79abec316f683b17f2d' +
'02bf07e0ac8b5bc6162cf94697b3c27c' +
'd1fea49b27f23ba2901871962506520c' +
'392da8b6ad0d99f7013fbc06c2c17a56' +
'9500c8a7696481c1cd33e9b14e40b82e' +
'79a5f5db82571ba97bae3ad3e0479515' +
'bb0e2b0f3bfcd1fd33034efc6245eddd' +
'7ee2086ddae2600d8ca73e214e8c2b0b' +
'db2b047c6a464a562ed77b73d2d841c4' +
'b34973551257713b753632efba348169' +
'abc90a68f42611a40126d7cb21b58695' +
'568186f7e569d2ff0f9e745d0487dd2e' +
'b997cafc5abf9dd102e62ff66cba87',
signature:
'e301345a41a39a4d72fff8df69c98075' +
'a0cc082b802fc9b2b6bc503f926b65bd' +
'df7f4c8f1cb49f6396afc8a70abe6d8a' +
'ef0db478d4c6b2970076c6a0484fe76d' +
'76b3a97625d79f1ce240e7c576750d29' +
'5528286f719b413de9ada3e8eb78ed57' +
'3603ce30d8bb761785dc30dbc320869e' +
'1a00',
},
];
describe('RFC8032', () => {
for (let i = 0; i < VECTORS_RFC8032.length; i++) {
const v = VECTORS_RFC8032[i];
should(`${i}`, () => {
deepStrictEqual(hex(ed.getPublicKey(v.secretKey)), v.publicKey);
deepStrictEqual(hex(ed.sign(v.message, v.secretKey)), v.signature);
deepStrictEqual(ed.verify(v.signature, v.message, v.publicKey), true);
});
}
});
should('not accept >57byte private keys', () => {
const invalidPriv =
100000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800073278156000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000n;
throws(() => ed.getPublicKey(invalidPriv));
});
function to57Bytes(numOrStr) {
let hex = typeof numOrStr === 'string' ? numOrStr : numOrStr.toString(16);
return hexToBytes(hex.padStart(114, '0'));
}
should('verify recent signature', () => {
fc.assert(
fc.property(
fc.hexaString({ minLength: 2, maxLength: 57 }),
fc.bigInt(2n, ed.CURVE.n),
(message, privateKey) => {
const publicKey = ed.getPublicKey(to57Bytes(privateKey));
const signature = ed.sign(to57Bytes(message), to57Bytes(privateKey));
deepStrictEqual(publicKey.length, 57);
deepStrictEqual(signature.length, 114);
deepStrictEqual(ed.verify(signature, to57Bytes(message), publicKey), true);
}
),
{ numRuns: 5 }
);
});
should('not verify signature with wrong message', () => {
fc.assert(
fc.property(
fc.array(fc.integer({ min: 0x00, max: 0xff })),
fc.array(fc.integer({ min: 0x00, max: 0xff })),
fc.bigInt(1n, ed.CURVE.n),
(bytes, wrongBytes, privateKey) => {
const message = new Uint8Array(bytes);
const wrongMessage = new Uint8Array(wrongBytes);
const priv = to57Bytes(privateKey);
const publicKey = ed.getPublicKey(priv);
const signature = ed.sign(message, priv);
deepStrictEqual(
ed.verify(signature, wrongMessage, publicKey),
bytes.toString() === wrongBytes.toString()
);
}
),
{ numRuns: 5 }
);
});
const privKey = to57Bytes('a665a45920422f9d417e4867ef');
const msg = hexToBytes('874f9960c5d2b7a9b5fad383e1ba44719ebb743a');
const wrongMsg = hexToBytes('589d8c7f1da0a24bc07b7381ad48b1cfc211af1c');
describe('basic methods', () => {
should('sign and verify', () => {
const publicKey = ed.getPublicKey(privKey);
const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, msg, publicKey), true);
});
should('not verify signature with wrong public key', () => {
const publicKey = ed.getPublicKey(ed.utils.randomPrivateKey());
const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, msg, publicKey), false);
});
should('not verify signature with wrong hash', () => {
const publicKey = ed.getPublicKey(privKey);
const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, wrongMsg, publicKey), false);
});
});
describe('sync methods', () => {
should('sign and verify', () => {
const publicKey = ed.getPublicKey(privKey);
const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, msg, publicKey), true);
});
should('not verify signature with wrong public key', () => {
const publicKey = ed.getPublicKey(ed.utils.randomPrivateKey());
const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, msg, publicKey), false);
});
should('not verify signature with wrong hash', () => {
const publicKey = ed.getPublicKey(privKey);
const signature = ed.sign(msg, privKey);
deepStrictEqual(ed.verify(signature, wrongMsg, publicKey), false);
});
});
should('BASE_POINT.multiply() throws in Point#multiply on TEST 5', () => {
for (const num of [0n, 0, -1n, -1, 1.1]) {
throws(() => ed.ExtendedPoint.BASE.multiply(num));
}
});
should('input immutability: sign/verify are immutable', () => {
const privateKey = ed.utils.randomPrivateKey();
const publicKey = ed.getPublicKey(privateKey);
for (let i = 0; i < 100; i++) {
let payload = randomBytes(100);
let signature = ed.sign(payload, privateKey);
if (!ed.verify(signature, payload, publicKey)) {
throw new Error('Signature verification failed');
}
const signatureCopy = Buffer.alloc(signature.byteLength);
signatureCopy.set(signature, 0); // <-- breaks
payload = payload.slice();
signature = signature.slice();
if (!ed.verify(signatureCopy, payload, publicKey))
throw new Error('Copied signature verification failed');
}
});
describe('wycheproof', () => {
for (let g = 0; g < ed448vectors.testGroups.length; g++) {
const group = ed448vectors.testGroups[g];
const key = group.key;
should(`ED448(${g}, public)`, () => {
deepStrictEqual(hex(ed.getPublicKey(key.sk)), key.pk);
});
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(hex(ed.sign(v.msg, key.sk)), v.sig, index);
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');
}
});
}
});
// ECDH
const rfc7748Mul = [
{
scalar:
'3d262fddf9ec8e88495266fea19a34d28882acef045104d0d1aae121700a779c984c24f8cdd78fbff44943eba368f54b29259a4f1c600ad3',
u: '06fce640fa3487bfda5f6cf2d5263f8aad88334cbd07437f020f08f9814dc031ddbdc38c19c6da2583fa5429db94ada18aa7a7fb4ef8a086',
outputU:
'ce3e4ff95a60dc6697da1db1d85e6afbdf79b50a2412d7546d5f239fe14fbaadeb445fc66a01b0779d98223961111e21766282f73dd96b6f',
},
{
scalar:
'203d494428b8399352665ddca42f9de8fef600908e0d461cb021f8c538345dd77c3e4806e25f46d3315c44e0a5b4371282dd2c8d5be3095f',
u: '0fbcc2f993cd56d3305b0b7d9e55d4c1a8fb5dbb52f8e9a1e9b6201b165d015894e56c4d3570bee52fe205e28a78b91cdfbde71ce8d157db',
outputU:
'884a02576239ff7a2f2f63b2db6a9ff37047ac13568e1e30fe63c4a7ad1b3ee3a5700df34321d62077e63633c575c1c954514e99da7c179d',
},
];
describe('RFC7748', () => {
for (let i = 0; i < rfc7748Mul.length; i++) {
const v = rfc7748Mul[i];
should(`scalarMult (${i})`, () => {
deepStrictEqual(hex(x448.scalarMult(v.scalar, v.u)), v.outputU);
});
}
});
const rfc7748Iter = [
{
scalar:
'3f482c8a9f19b01e6c46ee9711d9dc14fd4bf67af30765c2ae2b846a4d23a8cd0db897086239492caf350b51f833868b9bc2b3bca9cf4113',
iters: 1,
},
{
scalar:
'aa3b4749d55b9daf1e5b00288826c467274ce3ebbdd5c17b975e09d4af6c67cf10d087202db88286e2b79fceea3ec353ef54faa26e219f38',
iters: 1000,
},
// { scalar: '077f453681caca3693198420bbe515cae0002472519b3e67661a7e89cab94695c8f4bcd66e61b9b9c946da8d524de3d69bd9d9d66b997e37', iters: 1000000 },
];
for (let i = 0; i < rfc7748Iter.length; i++) {
const { scalar, iters } = rfc7748Iter[i];
should(`RFC7748: scalarMult iteration (${i})`, () => {
let k = x448.Gu;
for (let i = 0, u = k; i < iters; i++) [k, u] = [x448.scalarMult(k, u), k];
deepStrictEqual(hex(k), scalar);
});
}
should('RFC7748 getSharedKey', () => {
const alicePrivate =
'9a8f4925d1519f5775cf46b04b5800d4ee9ee8bae8bc5565d498c28dd9c9baf574a9419744897391006382a6f127ab1d9ac2d8c0a598726b';
const alicePublic =
'9b08f7cc31b7e3e67d22d5aea121074a273bd2b83de09c63faa73d2c22c5d9bbc836647241d953d40c5b12da88120d53177f80e532c41fa0';
const bobPrivate =
'1c306a7ac2a0e2e0990b294470cba339e6453772b075811d8fad0d1d6927c120bb5ee8972b0d3e21374c9c921b09d1b0366f10b65173992d';
const bobPublic =
'3eb7a829b0cd20f5bcfc0b599b6feccf6da4627107bdb0d4f345b43027d8b972fc3e34fb4232a13ca706dcb57aec3dae07bdc1c67bf33609';
const shared =
'07fff4181ac6cc95ec1c16a94a0f74d12da232ce40a77552281d282bb60c0b56fd2464c335543936521c24403085d59a449a5037514a879d';
deepStrictEqual(alicePublic, hex(x448.getPublicKey(alicePrivate)));
deepStrictEqual(bobPublic, hex(x448.getPublicKey(bobPrivate)));
deepStrictEqual(hex(x448.scalarMult(alicePrivate, bobPublic)), shared);
deepStrictEqual(hex(x448.scalarMult(bobPrivate, alicePublic)), shared);
});
describe('wycheproof', () => {
const group = x448vectors.testGroups[0];
should(`X448`, () => {
for (let i = 0; i < group.tests.length; i++) {
const v = group.tests[i];
const index = `(${i}, ${v.result}) ${v.comment}`;
if (v.result === 'valid' || v.result === 'acceptable') {
try {
const shared = hex(x448.scalarMult(v.private, v.public));
deepStrictEqual(shared, v.shared, index);
} catch (e) {
// We are more strict
if (e.message.includes('Expected valid scalar')) return;
if (e.message.includes('Invalid private or public key received')) return;
if (e.message.includes('Expected 56 bytes')) return;
throw e;
}
} else if (v.result === 'invalid') {
let failed = false;
try {
x448.scalarMult(v.private, v.public);
} catch (error) {
failed = true;
}
deepStrictEqual(failed, true, index);
} else throw new Error('unknown test result');
}
});
});
// should('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);
});
}
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(hex(numberToBytesLE(u, 56)), x448.Gu);
});
});
// ESM is broken.
import url from 'url';
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
should.run();
}

View File

@@ -28,16 +28,16 @@
"Uy": "EEAB6F3DEBE455E3DBF85416F7030CBD94F34F2D6F232C69F3C1385A", "Uy": "EEAB6F3DEBE455E3DBF85416F7030CBD94F34F2D6F232C69F3C1385A",
"cases": [ "cases": [
{ {
"k": "AD3029E0278F80643DE33917CE6908C70A8FF50A411F06E41DEDFCDC", "k": "C1D1F2F10881088301880506805FEB4825FE09ACB6816C36991AA06D",
"message": "sample", "message": "sample",
"r": "61AA3DA010E8E8406C656BC477A7A7189895E7E840CDFE8FF42307BA", "r": "1CDFE6662DDE1E4A1EC4CDEDF6A1F5A2FB7FBD9145C12113E6ABFD3E",
"s": "BC814050DAB5D23770879494F9E0A680DC1AF7161991BDE692B10101" "s": "A6694FD7718A21053F225D3F46197CA699D45006C06F871808F43EBC"
}, },
{ {
"k": "FF86F57924DA248D6E44E8154EB69F0AE2AEBAEE9931D0B5A969F904", "k": "DF8B38D40DCA3E077D0AC520BF56B6D565134D9B5F2EAE0D34900524",
"message": "test", "message": "test",
"r": "AD04DDE87B84747A243A631EA47A1BA6D1FAA059149AD2440DE6FBA6", "r": "C441CE8E261DED634E4CF84910E4C5D1D22C5CF3B732BB204DBEF019",
"s": "178D49B1AE90E3D8B629BE3DB5683915F4E8C99FDF6E666CF37ADCFD" "s": "902F42847A63BDC5F6046ADA114953120F99442D76510150F372A3F4"
} }
] ]
}, },

153
test/hash-to-curve.test.js Normal file
View File

@@ -0,0 +1,153 @@
import { deepStrictEqual } from 'assert';
import { describe, should } from 'micro-should';
import { bytesToHex } from '@noble/hashes/utils';
// Generic tests for all curves in package
import { sha256 } from '@noble/hashes/sha256';
import { sha512 } from '@noble/hashes/sha512';
import { shake128, shake256 } from '@noble/hashes/sha3';
import * as secp256r1 from '../lib/esm/p256.js';
import * as secp384r1 from '../lib/esm/p384.js';
import * as secp521r1 from '../lib/esm/p521.js';
import * as ed25519 from '../lib/esm/ed25519.js';
import * as ed448 from '../lib/esm/ed448.js';
import * as secp256k1 from '../lib/esm/secp256k1.js';
import { bls12_381 } from '../lib/esm/bls12-381.js';
import {
stringToBytes,
expand_message_xmd,
expand_message_xof,
} from '../lib/esm/abstract/hash-to-curve.js';
// XMD
import { default as xmd_sha256_38 } from './hash-to-curve/expand_message_xmd_SHA256_38.json' assert { type: 'json' };
import { default as xmd_sha256_256 } from './hash-to-curve/expand_message_xmd_SHA256_256.json' assert { type: 'json' };
import { default as xmd_sha512_38 } from './hash-to-curve/expand_message_xmd_SHA512_38.json' assert { type: 'json' };
// XOF
import { default as xof_shake128_36 } from './hash-to-curve/expand_message_xof_SHAKE128_36.json' assert { type: 'json' };
import { default as xof_shake128_256 } from './hash-to-curve/expand_message_xof_SHAKE128_256.json' assert { type: 'json' };
import { default as xof_shake256_36 } from './hash-to-curve/expand_message_xof_SHAKE256_36.json' assert { type: 'json' };
// 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' };
// 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' };
// 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' };
// 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' };
// 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' };
// 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' };
// 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' };
// 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' };
function testExpandXMD(hash, vectors) {
describe(`${vectors.hash}/${vectors.DST.length}`, () => {
for (let i = 0; i < vectors.tests.length; i++) {
const t = vectors.tests[i];
should(`${vectors.hash}/${vectors.DST.length}/${i}`, () => {
const p = expand_message_xmd(
stringToBytes(t.msg),
stringToBytes(vectors.DST),
t.len_in_bytes,
hash
);
deepStrictEqual(bytesToHex(p), t.uniform_bytes);
});
}
});
}
describe('expand_message_xmd', () => {
testExpandXMD(sha256, xmd_sha256_38);
testExpandXMD(sha256, xmd_sha256_256);
testExpandXMD(sha512, xmd_sha512_38);
});
function testExpandXOF(hash, vectors) {
describe(`${vectors.hash}/${vectors.DST.length}`, () => {
for (let i = 0; i < vectors.tests.length; i++) {
const t = vectors.tests[i];
should(`${i}`, () => {
const p = expand_message_xof(
stringToBytes(t.msg),
stringToBytes(vectors.DST),
+t.len_in_bytes,
vectors.k,
hash
);
deepStrictEqual(bytesToHex(p), t.uniform_bytes);
});
}
});
}
describe('expand_message_xof', () => {
testExpandXOF(shake128, xof_shake128_36);
testExpandXOF(shake128, xof_shake128_256);
testExpandXOF(shake256, xof_shake256_36);
});
function stringToFp(s) {
// bls-G2 support
if (s.includes(',')) {
const [c0, c1] = s.split(',').map(BigInt);
return { c0, c1 };
}
return BigInt(s);
}
function testCurve(curve, ro, nu) {
describe(`${ro.curve}/${ro.ciphersuite}`, () => {
for (let i = 0; i < ro.vectors.length; i++) {
const t = ro.vectors[i];
should(`(${i})`, () => {
const p = curve
.hashToCurve(stringToBytes(t.msg), {
DST: ro.dst,
})
.toAffine();
deepStrictEqual(p.x, stringToFp(t.P.x), 'Px');
deepStrictEqual(p.y, stringToFp(t.P.y), 'Py');
});
}
});
describe(`${nu.curve}/${nu.ciphersuite}`, () => {
for (let i = 0; i < nu.vectors.length; i++) {
const t = nu.vectors[i];
should(`(${i})`, () => {
const p = curve
.encodeToCurve(stringToBytes(t.msg), {
DST: nu.dst,
})
.toAffine();
deepStrictEqual(p.x, stringToFp(t.P.x), 'Px');
deepStrictEqual(p.y, stringToFp(t.P.y), 'Py');
});
}
});
}
testCurve(secp256r1, p256_ro, p256_nu);
testCurve(secp384r1, p384_ro, p384_nu);
testCurve(secp521r1, p521_ro, p521_nu);
testCurve(bls12_381.hashToCurve.G1, g1_ro, g1_nu);
testCurve(bls12_381.hashToCurve.G2, g2_ro, g2_nu);
testCurve(secp256k1, secp256k1_ro, secp256k1_nu);
testCurve(ed25519, ed25519_ro, ed25519_nu);
testCurve(ed448, ed448_ro, ed448_nu);
// ESM is broken.
import url from 'url';
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
should.run();
}

View File

@@ -0,0 +1,90 @@
{
"L": "0x40",
"Z": "0xb",
"ciphersuite": "BLS12381G1_XMD:SHA-256_SSWU_NU_",
"curve": "BLS12-381 G1",
"dst": "QUUX-V01-CS02-with-BLS12381G1_XMD:SHA-256_SSWU_NU_",
"expand": "XMD",
"field": {
"m": "0x1",
"p": "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab"
},
"hash": "sha256",
"k": "0x80",
"map": {
"name": "SSWU"
},
"randomOracle": false,
"vectors": [
{
"P": {
"x": "0x184bb665c37ff561a89ec2122dd343f20e0f4cbcaec84e3c3052ea81d1834e192c426074b02ed3dca4e7676ce4ce48ba",
"y": "0x04407b8d35af4dacc809927071fc0405218f1401a6d15af775810e4e460064bcc9468beeba82fdc751be70476c888bf3"
},
"Q": {
"x": "0x11398d3b324810a1b093f8e35aa8571cced95858207e7f49c4fd74656096d61d8a2f9a23cdb18a4dd11cd1d66f41f709",
"y": "0x19316b6fb2ba7717355d5d66a361899057e1e84a6823039efc7beccefe09d023fb2713b1c415fcf278eb0c39a89b4f72"
},
"msg": "",
"u": [
"0x156c8a6a2c184569d69a76be144b5cdc5141d2d2ca4fe341f011e25e3969c55ad9e9b9ce2eb833c81a908e5fa4ac5f03"
]
},
{
"P": {
"x": "0x009769f3ab59bfd551d53a5f846b9984c59b97d6842b20a2c565baa167945e3d026a3755b6345df8ec7e6acb6868ae6d",
"y": "0x1532c00cf61aa3d0ce3e5aa20c3b531a2abd2c770a790a2613818303c6b830ffc0ecf6c357af3317b9575c567f11cd2c"
},
"Q": {
"x": "0x1998321bc27ff6d71df3051b5aec12ff47363d81a5e9d2dff55f444f6ca7e7d6af45c56fd029c58237c266ef5cda5254",
"y": "0x034d274476c6307ae584f951c82e7ea85b84f72d28f4d6471732356121af8d62a49bc263e8eb913a6cf6f125995514ee"
},
"msg": "abc",
"u": [
"0x147e1ed29f06e4c5079b9d14fc89d2820d32419b990c1c7bb7dbea2a36a045124b31ffbde7c99329c05c559af1c6cc82"
]
},
{
"P": {
"x": "0x1974dbb8e6b5d20b84df7e625e2fbfecb2cdb5f77d5eae5fb2955e5ce7313cae8364bc2fff520a6c25619739c6bdcb6a",
"y": "0x15f9897e11c6441eaa676de141c8d83c37aab8667173cbe1dfd6de74d11861b961dccebcd9d289ac633455dfcc7013a3"
},
"Q": {
"x": "0x17d502fa43bd6a4cad2859049a0c3ecefd60240d129be65da271a4c03a9c38fa78163b9d2a919d2beb57df7d609b4919",
"y": "0x109019902ae93a8732abecf2ff7fecd2e4e305eb91f41c9c3267f16b6c19de138c7272947f25512745da6c466cdfd1ac"
},
"msg": "abcdef0123456789",
"u": [
"0x04090815ad598a06897dd89bcda860f25837d54e897298ce31e6947378134d3761dc59a572154963e8c954919ecfa82d"
]
},
{
"P": {
"x": "0x0a7a047c4a8397b3446450642c2ac64d7239b61872c9ae7a59707a8f4f950f101e766afe58223b3bff3a19a7f754027c",
"y": "0x1383aebba1e4327ccff7cf9912bda0dbc77de048b71ef8c8a81111d71dc33c5e3aa6edee9cf6f5fe525d50cc50b77cc9"
},
"Q": {
"x": "0x112eb92dd2b3aa9cd38b08de4bef603f2f9fb0ca226030626a9a2e47ad1e9847fe0a5ed13766c339e38f514bba143b21",
"y": "0x17542ce2f8d0a54f2c5ba8c4b14e10b22d5bcd7bae2af3c965c8c872b571058c720eac448276c99967ded2bf124490e1"
},
"msg": "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
"u": [
"0x08dccd088ca55b8bfbc96fb50bb25c592faa867a8bb78d4e94a8cc2c92306190244532e91feba2b7fed977e3c3bb5a1f"
]
},
{
"P": {
"x": "0x0e7a16a975904f131682edbb03d9560d3e48214c9986bd50417a77108d13dc957500edf96462a3d01e62dc6cd468ef11",
"y": "0x0ae89e677711d05c30a48d6d75e76ca9fb70fe06c6dd6ff988683d89ccde29ac7d46c53bb97a59b1901abf1db66052db"
},
"Q": {
"x": "0x1775d400a1bacc1c39c355da7e96d2d1c97baa9430c4a3476881f8521c09a01f921f592607961efc99c4cd46bd78ca19",
"y": "0x1109b5d59f65964315de65a7a143e86eabc053104ed289cf480949317a5685fad7254ff8e7fe6d24d3104e5d55ad6370"
},
"msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"u": [
"0x0dd824886d2123a96447f6c56e3a3fa992fbfefdba17b6673f9f630ff19e4d326529db37e1c1be43f905bf9202e0278d"
]
}
]
}

View File

@@ -0,0 +1,115 @@
{
"L": "0x40",
"Z": "0xb",
"ciphersuite": "BLS12381G1_XMD:SHA-256_SSWU_RO_",
"curve": "BLS12-381 G1",
"dst": "QUUX-V01-CS02-with-BLS12381G1_XMD:SHA-256_SSWU_RO_",
"expand": "XMD",
"field": {
"m": "0x1",
"p": "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab"
},
"hash": "sha256",
"k": "0x80",
"map": {
"name": "SSWU"
},
"randomOracle": true,
"vectors": [
{
"P": {
"x": "0x052926add2207b76ca4fa57a8734416c8dc95e24501772c814278700eed6d1e4e8cf62d9c09db0fac349612b759e79a1",
"y": "0x08ba738453bfed09cb546dbb0783dbb3a5f1f566ed67bb6be0e8c67e2e81a4cc68ee29813bb7994998f3eae0c9c6a265"
},
"Q0": {
"x": "0x11a3cce7e1d90975990066b2f2643b9540fa40d6137780df4e753a8054d07580db3b7f1f03396333d4a359d1fe3766fe",
"y": "0x0eeaf6d794e479e270da10fdaf768db4c96b650a74518fc67b04b03927754bac66f3ac720404f339ecdcc028afa091b7"
},
"Q1": {
"x": "0x160003aaf1632b13396dbad518effa00fff532f604de1a7fc2082ff4cb0afa2d63b2c32da1bef2bf6c5ca62dc6b72f9c",
"y": "0x0d8bb2d14e20cf9f6036152ed386d79189415b6d015a20133acb4e019139b94e9c146aaad5817f866c95d609a361735e"
},
"msg": "",
"u": [
"0x0ba14bd907ad64a016293ee7c2d276b8eae71f25a4b941eece7b0d89f17f75cb3ae5438a614fb61d6835ad59f29c564f",
"0x019b9bd7979f12657976de2884c7cce192b82c177c80e0ec604436a7f538d231552f0d96d9f7babe5fa3b19b3ff25ac9"
]
},
{
"P": {
"x": "0x03567bc5ef9c690c2ab2ecdf6a96ef1c139cc0b2f284dca0a9a7943388a49a3aee664ba5379a7655d3c68900be2f6903",
"y": "0x0b9c15f3fe6e5cf4211f346271d7b01c8f3b28be689c8429c85b67af215533311f0b8dfaaa154fa6b88176c229f2885d"
},
"Q0": {
"x": "0x125435adce8e1cbd1c803e7123f45392dc6e326d292499c2c45c5865985fd74fe8f042ecdeeec5ecac80680d04317d80",
"y": "0x0e8828948c989126595ee30e4f7c931cbd6f4570735624fd25aef2fa41d3f79cfb4b4ee7b7e55a8ce013af2a5ba20bf2"
},
"Q1": {
"x": "0x11def93719829ecda3b46aa8c31fc3ac9c34b428982b898369608e4f042babee6c77ab9218aad5c87ba785481eff8ae4",
"y": "0x0007c9cef122ccf2efd233d6eb9bfc680aa276652b0661f4f820a653cec1db7ff69899f8e52b8e92b025a12c822a6ce6"
},
"msg": "abc",
"u": [
"0x0d921c33f2bad966478a03ca35d05719bdf92d347557ea166e5bba579eea9b83e9afa5c088573c2281410369fbd32951",
"0x003574a00b109ada2f26a37a91f9d1e740dffd8d69ec0c35e1e9f4652c7dba61123e9dd2e76c655d956e2b3462611139"
]
},
{
"P": {
"x": "0x11e0b079dea29a68f0383ee94fed1b940995272407e3bb916bbf268c263ddd57a6a27200a784cbc248e84f357ce82d98",
"y": "0x03a87ae2caf14e8ee52e51fa2ed8eefe80f02457004ba4d486d6aa1f517c0889501dc7413753f9599b099ebcbbd2d709"
},
"Q0": {
"x": "0x08834484878c217682f6d09a4b51444802fdba3d7f2df9903a0ddadb92130ebbfa807fffa0eabf257d7b48272410afff",
"y": "0x0b318f7ecf77f45a0f038e62d7098221d2dbbca2a394164e2e3fe953dc714ac2cde412d8f2d7f0c03b259e6795a2508e"
},
"Q1": {
"x": "0x158418ed6b27e2549f05531a8281b5822b31c3bf3144277fbb977f8d6e2694fedceb7011b3c2b192f23e2a44b2bd106e",
"y": "0x1879074f344471fac5f839e2b4920789643c075792bec5af4282c73f7941cda5aa77b00085eb10e206171b9787c4169f"
},
"msg": "abcdef0123456789",
"u": [
"0x062d1865eb80ebfa73dcfc45db1ad4266b9f3a93219976a3790ab8d52d3e5f1e62f3b01795e36834b17b70e7b76246d4",
"0x0cdc3e2f271f29c4ff75020857ce6c5d36008c9b48385ea2f2bf6f96f428a3deb798aa033cd482d1cdc8b30178b08e3a"
]
},
{
"P": {
"x": "0x15f68eaa693b95ccb85215dc65fa81038d69629f70aeee0d0f677cf22285e7bf58d7cb86eefe8f2e9bc3f8cb84fac488",
"y": "0x1807a1d50c29f430b8cafc4f8638dfeeadf51211e1602a5f184443076715f91bb90a48ba1e370edce6ae1062f5e6dd38"
},
"Q0": {
"x": "0x0cbd7f84ad2c99643fea7a7ac8f52d63d66cefa06d9a56148e58b984b3dd25e1f41ff47154543343949c64f88d48a710",
"y": "0x052c00e4ed52d000d94881a5638ae9274d3efc8bc77bc0e5c650de04a000b2c334a9e80b85282a00f3148dfdface0865"
},
"Q1": {
"x": "0x06493fb68f0d513af08be0372f849436a787e7b701ae31cb964d968021d6ba6bd7d26a38aaa5a68e8c21a6b17dc8b579",
"y": "0x02e98f2ccf5802b05ffaac7c20018bc0c0b2fd580216c4aa2275d2909dc0c92d0d0bdc979226adeb57a29933536b6bb4"
},
"msg": "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
"u": [
"0x010476f6a060453c0b1ad0b628f3e57c23039ee16eea5e71bb87c3b5419b1255dc0e5883322e563b84a29543823c0e86",
"0x0b1a912064fb0554b180e07af7e787f1f883a0470759c03c1b6509eb8ce980d1670305ae7b928226bb58fdc0a419f46e"
]
},
{
"P": {
"x": "0x082aabae8b7dedb0e78aeb619ad3bfd9277a2f77ba7fad20ef6aabdc6c31d19ba5a6d12283553294c1825c4b3ca2dcfe",
"y": "0x05b84ae5a942248eea39e1d91030458c40153f3b654ab7872d779ad1e942856a20c438e8d99bc8abfbf74729ce1f7ac8"
},
"Q0": {
"x": "0x0cf97e6dbd0947857f3e578231d07b309c622ade08f2c08b32ff372bd90db19467b2563cc997d4407968d4ac80e154f8",
"y": "0x127f0cddf2613058101a5701f4cb9d0861fd6c2a1b8e0afe194fccf586a3201a53874a2761a9ab6d7220c68661a35ab3"
},
"Q1": {
"x": "0x092f1acfa62b05f95884c6791fba989bbe58044ee6355d100973bf9553ade52b47929264e6ae770fb264582d8dce512a",
"y": "0x028e6d0169a72cfedb737be45db6c401d3adfb12c58c619c82b93a5dfcccef12290de530b0480575ddc8397cda0bbebf"
},
"msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"u": [
"0x0a8ffa7447f6be1c5a2ea4b959c9454b431e29ccc0802bc052413a9c5b4f9aac67a93431bd480d15be1e057c8a08e8c6",
"0x05d487032f602c90fa7625dbafe0f4a49ef4a6b0b33d7bb349ff4cf5410d297fd6241876e3e77b651cfc8191e40a68b7"
]
}
]
}

View File

@@ -0,0 +1,90 @@
{
"L": "0x40",
"Z": "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaa9,0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa",
"ciphersuite": "BLS12381G2_XMD:SHA-256_SSWU_NU_",
"curve": "BLS12-381 G2",
"dst": "QUUX-V01-CS02-with-BLS12381G2_XMD:SHA-256_SSWU_NU_",
"expand": "XMD",
"field": {
"m": "0x2",
"p": "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab"
},
"hash": "sha256",
"k": "0x80",
"map": {
"name": "SSWU"
},
"randomOracle": false,
"vectors": [
{
"P": {
"x": "0x00e7f4568a82b4b7dc1f14c6aaa055edf51502319c723c4dc2688c7fe5944c213f510328082396515734b6612c4e7bb7,0x126b855e9e69b1f691f816e48ac6977664d24d99f8724868a184186469ddfd4617367e94527d4b74fc86413483afb35b",
"y": "0x0caead0fd7b6176c01436833c79d305c78be307da5f6af6c133c47311def6ff1e0babf57a0fb5539fce7ee12407b0a42,0x1498aadcf7ae2b345243e281ae076df6de84455d766ab6fcdaad71fab60abb2e8b980a440043cd305db09d283c895e3d"
},
"Q": {
"x": "0x18ed3794ad43c781816c523776188deafba67ab773189b8f18c49bc7aa841cd81525171f7a5203b2a340579192403bef,0x0727d90785d179e7b5732c8a34b660335fed03b913710b60903cf4954b651ed3466dc3728e21855ae822d4a0f1d06587",
"y": "0x00764a5cf6c5f61c52c838523460eb2168b5a5b43705e19cb612e006f29b717897facfd15dd1c8874c915f6d53d0342d,0x19290bb9797c12c1d275817aa2605ebe42275b66860f0e4d04487ebc2e47c50b36edd86c685a60c20a2bd584a82b011a"
},
"msg": "",
"u": [
"0x07355d25caf6e7f2f0cb2812ca0e513bd026ed09dda65b177500fa31714e09ea0ded3a078b526bed3307f804d4b93b04,0x02829ce3c021339ccb5caf3e187f6370e1e2a311dec9b75363117063ab2015603ff52c3d3b98f19c2f65575e99e8b78c"
]
},
{
"P": {
"x": "0x108ed59fd9fae381abfd1d6bce2fd2fa220990f0f837fa30e0f27914ed6e1454db0d1ee957b219f61da6ff8be0d6441f,0x0296238ea82c6d4adb3c838ee3cb2346049c90b96d602d7bb1b469b905c9228be25c627bffee872def773d5b2a2eb57d",
"y": "0x033f90f6057aadacae7963b0a0b379dd46750c1c94a6357c99b65f63b79e321ff50fe3053330911c56b6ceea08fee656,0x153606c417e59fb331b7ae6bce4fbf7c5190c33ce9402b5ebe2b70e44fca614f3f1382a3625ed5493843d0b0a652fc3f"
},
"Q": {
"x": "0x0f40e1d5025ecef0d850aa0bb7bbeceab21a3d4e85e6bee857805b09693051f5b25428c6be343edba5f14317fcc30143,0x02e0d261f2b9fee88b82804ec83db330caa75fbb12719cfa71ccce1c532dc4e1e79b0a6a281ed8d3817524286c8bc04c",
"y": "0x0cf4a4adc5c66da0bca4caddc6a57ecd97c8252d7526a8ff478e0dfed816c4d321b5c3039c6683ae9b1e6a3a38c9c0ae,0x11cad1646bb3768c04be2ab2bbe1f80263b7ff6f8f9488f5bc3b6850e5a3e97e20acc583613c69cf3d2bfe8489744ebb"
},
"msg": "abc",
"u": [
"0x138879a9559e24cecee8697b8b4ad32cced053138ab913b99872772dc753a2967ed50aabc907937aefb2439ba06cc50c,0x0a1ae7999ea9bab1dcc9ef8887a6cb6e8f1e22566015428d220b7eec90ffa70ad1f624018a9ad11e78d588bd3617f9f2"
]
},
{
"P": {
"x": "0x038af300ef34c7759a6caaa4e69363cafeed218a1f207e93b2c70d91a1263d375d6730bd6b6509dcac3ba5b567e85bf3,0x0da75be60fb6aa0e9e3143e40c42796edf15685cafe0279afd2a67c3dff1c82341f17effd402e4f1af240ea90f4b659b",
"y": "0x19b148cbdf163cf0894f29660d2e7bfb2b68e37d54cc83fd4e6e62c020eaa48709302ef8e746736c0e19342cc1ce3df4,0x0492f4fed741b073e5a82580f7c663f9b79e036b70ab3e51162359cec4e77c78086fe879b65ca7a47d34374c8315ac5e"
},
"Q": {
"x": "0x13a9d4a738a85c9f917c7be36b240915434b58679980010499b9ae8d7a1bf7fbe617a15b3cd6060093f40d18e0f19456,0x16fa88754e7670366a859d6f6899ad765bf5a177abedb2740aacc9252c43f90cd0421373fbd5b2b76bb8f5c4886b5d37",
"y": "0x0a7fa7d82c46797039398253e8765a4194100b330dfed6d7fbb46d6fbf01e222088779ac336e3675c7a7a0ee05bbb6e3,0x0c6ee170ab766d11fa9457cef53253f2628010b2cffc102b3b28351eb9df6c281d3cfc78e9934769d661b72a5265338d"
},
"msg": "abcdef0123456789",
"u": [
"0x18c16fe362b7dbdfa102e42bdfd3e2f4e6191d479437a59db4eb716986bf08ee1f42634db66bde97d6c16bbfd342b3b8,0x0e37812ce1b146d998d5f92bdd5ada2a31bfd63dfe18311aa91637b5f279dd045763166aa1615e46a50d8d8f475f184e"
]
},
{
"P": {
"x": "0x0c5ae723be00e6c3f0efe184fdc0702b64588fe77dda152ab13099a3bacd3876767fa7bbad6d6fd90b3642e902b208f9,0x12c8c05c1d5fc7bfa847f4d7d81e294e66b9a78bc9953990c358945e1f042eedafce608b67fdd3ab0cb2e6e263b9b1ad",
"y": "0x04e77ddb3ede41b5ec4396b7421dd916efc68a358a0d7425bddd253547f2fb4830522358491827265dfc5bcc1928a569,0x11c624c56dbe154d759d021eec60fab3d8b852395a89de497e48504366feedd4662d023af447d66926a28076813dd646"
},
"Q": {
"x": "0x0a08b2f639855dfdeaaed972702b109e2241a54de198b2b4cd12ad9f88fa419a6086a58d91fc805de812ea29bee427c2,0x04a7442e4cb8b42ef0f41dac9ee74e65ecad3ce0851f0746dc47568b0e7a8134121ed09ba054509232c49148aef62cda",
"y": "0x05d60b1f04212b2c87607458f71d770f43973511c260f0540eef3a565f42c7ce59aa1cea684bb2a7bcab84acd2f36c8c,0x1017aa5747ba15505ece266a86b0ca9c712f41a254b76ca04094ca442ce45ecd224bd5544cd16685d0d1b9d156dd0531"
},
"msg": "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
"u": [
"0x08d4a0997b9d52fecf99427abb721f0fa779479963315fe21c6445250de7183e3f63bfdf86570da8929489e421d4ee95,0x16cb4ccad91ec95aab070f22043916cd6a59c4ca94097f7f510043d48515526dc8eaaea27e586f09151ae613688d5a89"
]
},
{
"P": {
"x": "0x0ea4e7c33d43e17cc516a72f76437c4bf81d8f4eac69ac355d3bf9b71b8138d55dc10fd458be115afa798b55dac34be1,0x1565c2f625032d232f13121d3cfb476f45275c303a037faa255f9da62000c2c864ea881e2bcddd111edc4a3c0da3e88d",
"y": "0x043b6f5fe4e52c839148dc66f2b3751e69a0f6ebb3d056d6465d50d4108543ecd956e10fa1640dfd9bc0030cc2558d28,0x0f8991d2a1ad662e7b6f58ab787947f1fa607fce12dde171bc17903b012091b657e15333e11701edcf5b63ba2a561247"
},
"Q": {
"x": "0x19592c812d5a50c5601062faba14c7d670711745311c879de1235a0a11c75aab61327bf2d1725db07ec4d6996a682886,0x0eef4fa41ddc17ed47baf447a2c498548f3c72a02381313d13bef916e240b61ce125539090d62d9fbb14a900bf1b8e90",
"y": "0x1260d6e0987eae96af9ebe551e08de22b37791d53f4db9e0d59da736e66699735793e853e26362531fe4adf99c1883e3,0x0dbace5df0a4ac4ac2f45d8fdf8aee45484576fdd6efc4f98ab9b9f4112309e628255e183022d98ea5ed6e47ca00306c"
},
"msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"u": [
"0x03f80ce4ff0ca2f576d797a3660e3f65b274285c054feccc3215c879e2c0589d376e83ede13f93c32f05da0f68fd6a10,0x006488a837c5413746d868d1efb7232724da10eca410b07d8b505b9363bdccf0a1fc0029bad07d65b15ccfe6dd25e20d"
]
}
]
}

View File

@@ -0,0 +1,115 @@
{
"L": "0x40",
"Z": "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaa9,0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa",
"ciphersuite": "BLS12381G2_XMD:SHA-256_SSWU_RO_",
"curve": "BLS12-381 G2",
"dst": "QUUX-V01-CS02-with-BLS12381G2_XMD:SHA-256_SSWU_RO_",
"expand": "XMD",
"field": {
"m": "0x2",
"p": "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab"
},
"hash": "sha256",
"k": "0x80",
"map": {
"name": "SSWU"
},
"randomOracle": true,
"vectors": [
{
"P": {
"x": "0x0141ebfbdca40eb85b87142e130ab689c673cf60f1a3e98d69335266f30d9b8d4ac44c1038e9dcdd5393faf5c41fb78a,0x05cb8437535e20ecffaef7752baddf98034139c38452458baeefab379ba13dff5bf5dd71b72418717047f5b0f37da03d",
"y": "0x0503921d7f6a12805e72940b963c0cf3471c7b2a524950ca195d11062ee75ec076daf2d4bc358c4b190c0c98064fdd92,0x12424ac32561493f3fe3c260708a12b7c620e7be00099a974e259ddc7d1f6395c3c811cdd19f1e8dbf3e9ecfdcbab8d6"
},
"Q0": {
"x": "0x019ad3fc9c72425a998d7ab1ea0e646a1f6093444fc6965f1cad5a3195a7b1e099c050d57f45e3fa191cc6d75ed7458c,0x171c88b0b0efb5eb2b88913a9e74fe111a4f68867b59db252ce5868af4d1254bfab77ebde5d61cd1a86fb2fe4a5a1c1d",
"y": "0x0ba10604e62bdd9eeeb4156652066167b72c8d743b050fb4c1016c31b505129374f76e03fa127d6a156213576910fef3,0x0eb22c7a543d3d376e9716a49b72e79a89c9bfe9feee8533ed931cbb5373dde1fbcd7411d8052e02693654f71e15410a"
},
"Q1": {
"x": "0x113d2b9cd4bd98aee53470b27abc658d91b47a78a51584f3d4b950677cfb8a3e99c24222c406128c91296ef6b45608be,0x13855912321c5cb793e9d1e88f6f8d342d49c0b0dbac613ee9e17e3c0b3c97dfbb5a49cc3fb45102fdbaf65e0efe2632",
"y": "0x0fd3def0b7574a1d801be44fde617162aa2e89da47f464317d9bb5abc3a7071763ce74180883ad7ad9a723a9afafcdca,0x056f617902b3c0d0f78a9a8cbda43a26b65f602f8786540b9469b060db7b38417915b413ca65f875c130bebfaa59790c"
},
"msg": "",
"u": [
"0x03dbc2cce174e91ba93cbb08f26b917f98194a2ea08d1cce75b2b9cc9f21689d80bd79b594a613d0a68eb807dfdc1cf8,0x05a2acec64114845711a54199ea339abd125ba38253b70a92c876df10598bd1986b739cad67961eb94f7076511b3b39a",
"0x02f99798e8a5acdeed60d7e18e9120521ba1f47ec090984662846bc825de191b5b7641148c0dbc237726a334473eee94,0x145a81e418d4010cc027a68f14391b30074e89e60ee7a22f87217b2f6eb0c4b94c9115b436e6fa4607e95a98de30a435"
]
},
{
"P": {
"x": "0x02c2d18e033b960562aae3cab37a27ce00d80ccd5ba4b7fe0e7a210245129dbec7780ccc7954725f4168aff2787776e6,0x139cddbccdc5e91b9623efd38c49f81a6f83f175e80b06fc374de9eb4b41dfe4ca3a230ed250fbe3a2acf73a41177fd8",
"y": "0x1787327b68159716a37440985269cf584bcb1e621d3a7202be6ea05c4cfe244aeb197642555a0645fb87bf7466b2ba48,0x00aa65dae3c8d732d10ecd2c50f8a1baf3001578f71c694e03866e9f3d49ac1e1ce70dd94a733534f106d4cec0eddd16"
},
"Q0": {
"x": "0x12b2e525281b5f4d2276954e84ac4f42cf4e13b6ac4228624e17760faf94ce5706d53f0ca1952f1c5ef75239aeed55ad,0x05d8a724db78e570e34100c0bc4a5fa84ad5839359b40398151f37cff5a51de945c563463c9efbdda569850ee5a53e77",
"y": "0x02eacdc556d0bdb5d18d22f23dcb086dd106cad713777c7e6407943edbe0b3d1efe391eedf11e977fac55f9b94f2489c,0x04bbe48bfd5814648d0b9e30f0717b34015d45a861425fabc1ee06fdfce36384ae2c808185e693ae97dcde118f34de41"
},
"Q1": {
"x": "0x19f18cc5ec0c2f055e47c802acc3b0e40c337256a208001dde14b25afced146f37ea3d3ce16834c78175b3ed61f3c537,0x15b0dadc256a258b4c68ea43605dffa6d312eef215c19e6474b3e101d33b661dfee43b51abbf96fee68fc6043ac56a58",
"y": "0x05e47c1781286e61c7ade887512bd9c2cb9f640d3be9cf87ea0bad24bd0ebfe946497b48a581ab6c7d4ca74b5147287f,0x19f98db2f4a1fcdf56a9ced7b320ea9deecf57c8e59236b0dc21f6ee7229aa9705ce9ac7fe7a31c72edca0d92370c096"
},
"msg": "abc",
"u": [
"0x15f7c0aa8f6b296ab5ff9c2c7581ade64f4ee6f1bf18f55179ff44a2cf355fa53dd2a2158c5ecb17d7c52f63e7195771,0x01c8067bf4c0ba709aa8b9abc3d1cef589a4758e09ef53732d670fd8739a7274e111ba2fcaa71b3d33df2a3a0c8529dd",
"0x187111d5e088b6b9acfdfad078c4dacf72dcd17ca17c82be35e79f8c372a693f60a033b461d81b025864a0ad051a06e4,0x08b852331c96ed983e497ebc6dee9b75e373d923b729194af8e72a051ea586f3538a6ebb1e80881a082fa2b24df9f566"
]
},
{
"P": {
"x": "0x121982811d2491fde9ba7ed31ef9ca474f0e1501297f68c298e9f4c0028add35aea8bb83d53c08cfc007c1e005723cd0,0x190d119345b94fbd15497bcba94ecf7db2cbfd1e1fe7da034d26cbba169fb3968288b3fafb265f9ebd380512a71c3f2c",
"y": "0x05571a0f8d3c08d094576981f4a3b8eda0a8e771fcdcc8ecceaf1356a6acf17574518acb506e435b639353c2e14827c8,0x0bb5e7572275c567462d91807de765611490205a941a5a6af3b1691bfe596c31225d3aabdf15faff860cb4ef17c7c3be"
},
"Q0": {
"x": "0x0f48f1ea1318ddb713697708f7327781fb39718971d72a9245b9731faaca4dbaa7cca433d6c434a820c28b18e20ea208,0x06051467c8f85da5ba2540974758f7a1e0239a5981de441fdd87680a995649c211054869c50edbac1f3a86c561ba3162",
"y": "0x168b3d6df80069dbbedb714d41b32961ad064c227355e1ce5fac8e105de5e49d77f0c64867f3834848f152497eb76333,0x134e0e8331cee8cb12f9c2d0742714ed9eee78a84d634c9a95f6a7391b37125ed48bfc6e90bf3546e99930ff67cc97bc"
},
"Q1": {
"x": "0x004fd03968cd1c99a0dd84551f44c206c84dcbdb78076c5bfee24e89a92c8508b52b88b68a92258403cbe1ea2da3495f,0x1674338ea298281b636b2eb0fe593008d03171195fd6dcd4531e8a1ed1f02a72da238a17a635de307d7d24aa2d969a47",
"y": "0x0dc7fa13fff6b12558419e0a1e94bfc3cfaf67238009991c5f24ee94b632c3d09e27eca329989aee348a67b50d5e236c,0x169585e164c131103d85324f2d7747b23b91d66ae5d947c449c8194a347969fc6bbd967729768da485ba71868df8aed2"
},
"msg": "abcdef0123456789",
"u": [
"0x0313d9325081b415bfd4e5364efaef392ecf69b087496973b229303e1816d2080971470f7da112c4eb43053130b785e1,0x062f84cb21ed89406890c051a0e8b9cf6c575cf6e8e18ecf63ba86826b0ae02548d83b483b79e48512b82a6c0686df8f",
"0x1739123845406baa7be5c5dc74492051b6d42504de008c635f3535bb831d478a341420e67dcc7b46b2e8cba5379cca97,0x01897665d9cb5db16a27657760bbea7951f67ad68f8d55f7113f24ba6ddd82caef240a9bfa627972279974894701d975"
]
},
{
"P": {
"x": "0x19a84dd7248a1066f737cc34502ee5555bd3c19f2ecdb3c7d9e24dc65d4e25e50d83f0f77105e955d78f4762d33c17da,0x0934aba516a52d8ae479939a91998299c76d39cc0c035cd18813bec433f587e2d7a4fef038260eef0cef4d02aae3eb91",
"y": "0x14f81cd421617428bc3b9fe25afbb751d934a00493524bc4e065635b0555084dd54679df1536101b2c979c0152d09192,0x09bcccfa036b4847c9950780733633f13619994394c23ff0b32fa6b795844f4a0673e20282d07bc69641cee04f5e5662"
},
"Q0": {
"x": "0x09eccbc53df677f0e5814e3f86e41e146422834854a224bf5a83a50e4cc0a77bfc56718e8166ad180f53526ea9194b57,0x0c3633943f91daee715277bd644fba585168a72f96ded64fc5a384cce4ec884a4c3c30f08e09cd2129335dc8f67840ec",
"y": "0x0eb6186a0457d5b12d132902d4468bfeb7315d83320b6c32f1c875f344efcba979952b4aa418589cb01af712f98cc555,0x119e3cf167e69eb16c1c7830e8df88856d48be12e3ff0a40791a5cd2f7221311d4bf13b1847f371f467357b3f3c0b4c7"
},
"Q1": {
"x": "0x0eb3aabc1ddfce17ff18455fcc7167d15ce6b60ddc9eb9b59f8d40ab49420d35558686293d046fc1e42f864b7f60e381,0x198bdfb19d7441ebcca61e8ff774b29d17da16547d2c10c273227a635cacea3f16826322ae85717630f0867539b5ed8b",
"y": "0x0aaf1dee3adf3ed4c80e481c09b57ea4c705e1b8d25b897f0ceeec3990748716575f92abff22a1c8f4582aff7b872d52,0x0d058d9061ed27d4259848a06c96c5ca68921a5d269b078650c882cb3c2bd424a8702b7a6ee4e0ead9982baf6843e924"
},
"msg": "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
"u": [
"0x025820cefc7d06fd38de7d8e370e0da8a52498be9b53cba9927b2ef5c6de1e12e12f188bbc7bc923864883c57e49e253,0x034147b77ce337a52e5948f66db0bab47a8d038e712123bb381899b6ab5ad20f02805601e6104c29df18c254b8618c7b",
"0x0930315cae1f9a6017c3f0c8f2314baa130e1cf13f6532bff0a8a1790cd70af918088c3db94bda214e896e1543629795,0x10c4df2cacf67ea3cb3108b00d4cbd0b3968031ebc8eac4b1ebcefe84d6b715fde66bef0219951ece29d1facc8a520ef"
]
},
{
"P": {
"x": "0x01a6ba2f9a11fa5598b2d8ace0fbe0a0eacb65deceb476fbbcb64fd24557c2f4b18ecfc5663e54ae16a84f5ab7f62534,0x11fca2ff525572795a801eed17eb12785887c7b63fb77a42be46ce4a34131d71f7a73e95fee3f812aea3de78b4d01569",
"y": "0x0b6798718c8aed24bc19cb27f866f1c9effcdbf92397ad6448b5c9db90d2b9da6cbabf48adc1adf59a1a28344e79d57e,0x03a47f8e6d1763ba0cad63d6114c0accbef65707825a511b251a660a9b3994249ae4e63fac38b23da0c398689ee2ab52"
},
"Q0": {
"x": "0x17cadf8d04a1a170f8347d42856526a24cc466cb2ddfd506cff01191666b7f944e31244d662c904de5440516a2b09004,0x0d13ba91f2a8b0051cf3279ea0ee63a9f19bc9cb8bfcc7d78b3cbd8cc4fc43ba726774b28038213acf2b0095391c523e",
"y": "0x17ef19497d6d9246fa94d35575c0f8d06ee02f21a284dbeaa78768cb1e25abd564e3381de87bda26acd04f41181610c5,0x12c3c913ba4ed03c24f0721a81a6be7430f2971ffca8fd1729aafe496bb725807531b44b34b59b3ae5495e5a2dcbd5c8"
},
"Q1": {
"x": "0x16ec57b7fe04c71dfe34fb5ad84dbce5a2dbbd6ee085f1d8cd17f45e8868976fc3c51ad9eeda682c7869024d24579bfd,0x13103f7aace1ae1420d208a537f7d3a9679c287208026e4e3439ab8cd534c12856284d95e27f5e1f33eec2ce656533b0",
"y": "0x0958b2c4c2c10fcef5a6c59b9e92c4a67b0fae3e2e0f1b6b5edad9c940b8f3524ba9ebbc3f2ceb3cfe377655b3163bd7,0x0ccb594ed8bd14ca64ed9cb4e0aba221be540f25dd0d6ba15a4a4be5d67bcf35df7853b2d8dad3ba245f1ea3697f66aa"
},
"msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"u": [
"0x190b513da3e66fc9a3587b78c76d1d132b1152174d0b83e3c1114066392579a45824c5fa17649ab89299ddd4bda54935,0x12ab625b0fe0ebd1367fe9fac57bb1168891846039b4216b9d94007b674de2d79126870e88aeef54b2ec717a887dcf39",
"0x0e6a42010cf435fb5bacc156a585e1ea3294cc81d0ceb81924d95040298380b164f702275892cedd81b62de3aba3f6b5,0x117d9a0defc57a33ed208428cb84e54c85a6840e7648480ae428838989d25d97a0af8e3255be62b25c2a85630d2dddd8"
]
}
]
}

View File

@@ -0,0 +1,90 @@
{
"L": "0x30",
"Z": "0xffffffff00000001000000000000000000000000fffffffffffffffffffffff5",
"ciphersuite": "P256_XMD:SHA-256_SSWU_NU_",
"curve": "NIST P-256",
"dst": "QUUX-V01-CS02-with-P256_XMD:SHA-256_SSWU_NU_",
"expand": "XMD",
"field": {
"m": "0x1",
"p": "0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff"
},
"hash": "sha256",
"k": "0x80",
"map": {
"name": "SSWU"
},
"randomOracle": false,
"vectors": [
{
"P": {
"x": "0xf871caad25ea3b59c16cf87c1894902f7e7b2c822c3d3f73596c5ace8ddd14d1",
"y": "0x87b9ae23335bee057b99bac1e68588b18b5691af476234b8971bc4f011ddc99b"
},
"Q": {
"x": "0xf871caad25ea3b59c16cf87c1894902f7e7b2c822c3d3f73596c5ace8ddd14d1",
"y": "0x87b9ae23335bee057b99bac1e68588b18b5691af476234b8971bc4f011ddc99b"
},
"msg": "",
"u": [
"0xb22d487045f80e9edcb0ecc8d4bf77833e2bf1f3a54004d7df1d57f4802d311f"
]
},
{
"P": {
"x": "0xfc3f5d734e8dce41ddac49f47dd2b8a57257522a865c124ed02b92b5237befa4",
"y": "0xfe4d197ecf5a62645b9690599e1d80e82c500b22ac705a0b421fac7b47157866"
},
"Q": {
"x": "0xfc3f5d734e8dce41ddac49f47dd2b8a57257522a865c124ed02b92b5237befa4",
"y": "0xfe4d197ecf5a62645b9690599e1d80e82c500b22ac705a0b421fac7b47157866"
},
"msg": "abc",
"u": [
"0xc7f96eadac763e176629b09ed0c11992225b3a5ae99479760601cbd69c221e58"
]
},
{
"P": {
"x": "0xf164c6674a02207e414c257ce759d35eddc7f55be6d7f415e2cc177e5d8faa84",
"y": "0x3aa274881d30db70485368c0467e97da0e73c18c1d00f34775d012b6fcee7f97"
},
"Q": {
"x": "0xf164c6674a02207e414c257ce759d35eddc7f55be6d7f415e2cc177e5d8faa84",
"y": "0x3aa274881d30db70485368c0467e97da0e73c18c1d00f34775d012b6fcee7f97"
},
"msg": "abcdef0123456789",
"u": [
"0x314e8585fa92068b3ea2c3bab452d4257b38be1c097d58a21890456c2929614d"
]
},
{
"P": {
"x": "0x324532006312be4f162614076460315f7a54a6f85544da773dc659aca0311853",
"y": "0x8d8197374bcd52de2acfefc8a54fe2c8d8bebd2a39f16be9b710e4b1af6ef883"
},
"Q": {
"x": "0x324532006312be4f162614076460315f7a54a6f85544da773dc659aca0311853",
"y": "0x8d8197374bcd52de2acfefc8a54fe2c8d8bebd2a39f16be9b710e4b1af6ef883"
},
"msg": "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
"u": [
"0x752d8eaa38cd785a799a31d63d99c2ae4261823b4a367b133b2c6627f48858ab"
]
},
{
"P": {
"x": "0x5c4bad52f81f39c8e8de1260e9a06d72b8b00a0829a8ea004a610b0691bea5d9",
"y": "0xc801e7c0782af1f74f24fc385a8555da0582032a3ce038de637ccdcb16f7ef7b"
},
"Q": {
"x": "0x5c4bad52f81f39c8e8de1260e9a06d72b8b00a0829a8ea004a610b0691bea5d9",
"y": "0xc801e7c0782af1f74f24fc385a8555da0582032a3ce038de637ccdcb16f7ef7b"
},
"msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"u": [
"0x0e1527840b9df2dfbef966678ff167140f2b27c4dccd884c25014dce0e41dfa3"
]
}
]
}

View File

@@ -0,0 +1,115 @@
{
"L": "0x30",
"Z": "0xffffffff00000001000000000000000000000000fffffffffffffffffffffff5",
"ciphersuite": "P256_XMD:SHA-256_SSWU_RO_",
"curve": "NIST P-256",
"dst": "QUUX-V01-CS02-with-P256_XMD:SHA-256_SSWU_RO_",
"expand": "XMD",
"field": {
"m": "0x1",
"p": "0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff"
},
"hash": "sha256",
"k": "0x80",
"map": {
"name": "SSWU"
},
"randomOracle": true,
"vectors": [
{
"P": {
"x": "0x2c15230b26dbc6fc9a37051158c95b79656e17a1a920b11394ca91c44247d3e4",
"y": "0x8a7a74985cc5c776cdfe4b1f19884970453912e9d31528c060be9ab5c43e8415"
},
"Q0": {
"x": "0xab640a12220d3ff283510ff3f4b1953d09fad35795140b1c5d64f313967934d5",
"y": "0xdccb558863804a881d4fff3455716c836cef230e5209594ddd33d85c565b19b1"
},
"Q1": {
"x": "0x51cce63c50d972a6e51c61334f0f4875c9ac1cd2d3238412f84e31da7d980ef5",
"y": "0xb45d1a36d00ad90e5ec7840a60a4de411917fbe7c82c3949a6e699e5a1b66aac"
},
"msg": "",
"u": [
"0xad5342c66a6dd0ff080df1da0ea1c04b96e0330dd89406465eeba11582515009",
"0x8c0f1d43204bd6f6ea70ae8013070a1518b43873bcd850aafa0a9e220e2eea5a"
]
},
{
"P": {
"x": "0x0bb8b87485551aa43ed54f009230450b492fead5f1cc91658775dac4a3388a0f",
"y": "0x5c41b3d0731a27a7b14bc0bf0ccded2d8751f83493404c84a88e71ffd424212e"
},
"Q0": {
"x": "0x5219ad0ddef3cc49b714145e91b2f7de6ce0a7a7dc7406c7726c7e373c58cb48",
"y": "0x7950144e52d30acbec7b624c203b1996c99617d0b61c2442354301b191d93ecf"
},
"Q1": {
"x": "0x019b7cb4efcfeaf39f738fe638e31d375ad6837f58a852d032ff60c69ee3875f",
"y": "0x589a62d2b22357fed5449bc38065b760095ebe6aeac84b01156ee4252715446e"
},
"msg": "abc",
"u": [
"0xafe47f2ea2b10465cc26ac403194dfb68b7f5ee865cda61e9f3e07a537220af1",
"0x379a27833b0bfe6f7bdca08e1e83c760bf9a338ab335542704edcd69ce9e46e0"
]
},
{
"P": {
"x": "0x65038ac8f2b1def042a5df0b33b1f4eca6bff7cb0f9c6c1526811864e544ed80",
"y": "0xcad44d40a656e7aff4002a8de287abc8ae0482b5ae825822bb870d6df9b56ca3"
},
"Q0": {
"x": "0xa17bdf2965eb88074bc01157e644ed409dac97cfcf0c61c998ed0fa45e79e4a2",
"y": "0x4f1bc80c70d411a3cc1d67aeae6e726f0f311639fee560c7f5a664554e3c9c2e"
},
"Q1": {
"x": "0x7da48bb67225c1a17d452c983798113f47e438e4202219dd0715f8419b274d66",
"y": "0xb765696b2913e36db3016c47edb99e24b1da30e761a8a3215dc0ec4d8f96e6f9"
},
"msg": "abcdef0123456789",
"u": [
"0x0fad9d125a9477d55cf9357105b0eb3a5c4259809bf87180aa01d651f53d312c",
"0xb68597377392cd3419d8fcc7d7660948c8403b19ea78bbca4b133c9d2196c0fb"
]
},
{
"P": {
"x": "0x4be61ee205094282ba8a2042bcb48d88dfbb609301c49aa8b078533dc65a0b5d",
"y": "0x98f8df449a072c4721d241a3b1236d3caccba603f916ca680f4539d2bfb3c29e"
},
"Q0": {
"x": "0xc76aaa823aeadeb3f356909cb08f97eee46ecb157c1f56699b5efebddf0e6398",
"y": "0x776a6f45f528a0e8d289a4be12c4fab80762386ec644abf2bffb9b627e4352b1"
},
"Q1": {
"x": "0x418ac3d85a5ccc4ea8dec14f750a3a9ec8b85176c95a7022f391826794eb5a75",
"y": "0xfd6604f69e9d9d2b74b072d14ea13050db72c932815523305cb9e807cc900aff"
},
"msg": "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
"u": [
"0x3bbc30446f39a7befad080f4d5f32ed116b9534626993d2cc5033f6f8d805919",
"0x76bb02db019ca9d3c1e02f0c17f8baf617bbdae5c393a81d9ce11e3be1bf1d33"
]
},
{
"P": {
"x": "0x457ae2981f70ca85d8e24c308b14db22f3e3862c5ea0f652ca38b5e49cd64bc5",
"y": "0xecb9f0eadc9aeed232dabc53235368c1394c78de05dd96893eefa62b0f4757dc"
},
"Q0": {
"x": "0xd88b989ee9d1295df413d4456c5c850b8b2fb0f5402cc5c4c7e815412e926db8",
"y": "0xbb4a1edeff506cf16def96afff41b16fc74f6dbd55c2210e5b8f011ba32f4f40"
},
"Q1": {
"x": "0xa281e34e628f3a4d2a53fa87ff973537d68ad4fbc28d3be5e8d9f6a2571c5a4b",
"y": "0xf6ed88a7aab56a488100e6f1174fa9810b47db13e86be999644922961206e184"
},
"msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"u": [
"0x4ebc95a6e839b1ae3c63b847798e85cb3c12d3817ec6ebc10af6ee51adb29fec",
"0x4e21af88e22ea80156aff790750121035b3eefaa96b425a8716e0d20b4e269ee"
]
}
]
}

View File

@@ -0,0 +1,90 @@
{
"L": "0x48",
"Z": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000fffffff3",
"ciphersuite": "P384_XMD:SHA-384_SSWU_NU_",
"curve": "NIST P-384",
"dst": "QUUX-V01-CS02-with-P384_XMD:SHA-384_SSWU_NU_",
"expand": "XMD",
"field": {
"m": "0x1",
"p": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff"
},
"hash": "sha384",
"k": "0xc0",
"map": {
"name": "SSWU"
},
"randomOracle": false,
"vectors": [
{
"P": {
"x": "0xde5a893c83061b2d7ce6a0d8b049f0326f2ada4b966dc7e72927256b033ef61058029a3bfb13c1c7ececd6641881ae20",
"y": "0x63f46da6139785674da315c1947e06e9a0867f5608cf24724eb3793a1f5b3809ee28eb21a0c64be3be169afc6cdb38ca"
},
"Q": {
"x": "0xde5a893c83061b2d7ce6a0d8b049f0326f2ada4b966dc7e72927256b033ef61058029a3bfb13c1c7ececd6641881ae20",
"y": "0x63f46da6139785674da315c1947e06e9a0867f5608cf24724eb3793a1f5b3809ee28eb21a0c64be3be169afc6cdb38ca"
},
"msg": "",
"u": [
"0xbc7dc1b2cdc5d588a66de3276b0f24310d4aca4977efda7d6272e1be25187b001493d267dc53b56183c9e28282368e60"
]
},
{
"P": {
"x": "0x1f08108b87e703c86c872ab3eb198a19f2b708237ac4be53d7929fb4bd5194583f40d052f32df66afe5249c9915d139b",
"y": "0x1369dc8d5bf038032336b989994874a2270adadb67a7fcc32f0f8824bc5118613f0ac8de04a1041d90ff8a5ad555f96c"
},
"Q": {
"x": "0x1f08108b87e703c86c872ab3eb198a19f2b708237ac4be53d7929fb4bd5194583f40d052f32df66afe5249c9915d139b",
"y": "0x1369dc8d5bf038032336b989994874a2270adadb67a7fcc32f0f8824bc5118613f0ac8de04a1041d90ff8a5ad555f96c"
},
"msg": "abc",
"u": [
"0x9de6cf41e6e41c03e4a7784ac5c885b4d1e49d6de390b3cdd5a1ac5dd8c40afb3dfd7bb2686923bab644134483fc1926"
]
},
{
"P": {
"x": "0x4dac31ec8a82ee3c02ba2d7c9fa431f1e59ffe65bf977b948c59e1d813c2d7963c7be81aa6db39e78ff315a10115c0d0",
"y": "0x845333cdb5702ad5c525e603f302904d6fc84879f0ef2ee2014a6b13edd39131bfd66f7bd7cdc2d9ccf778f0c8892c3f"
},
"Q": {
"x": "0x4dac31ec8a82ee3c02ba2d7c9fa431f1e59ffe65bf977b948c59e1d813c2d7963c7be81aa6db39e78ff315a10115c0d0",
"y": "0x845333cdb5702ad5c525e603f302904d6fc84879f0ef2ee2014a6b13edd39131bfd66f7bd7cdc2d9ccf778f0c8892c3f"
},
"msg": "abcdef0123456789",
"u": [
"0x84e2d430a5e2543573e58e368af41821ca3ccc97baba7e9aab51a84543d5a0298638a22ceee6090d9d642921112af5b7"
]
},
{
"P": {
"x": "0x13c1f8c52a492183f7c28e379b0475486718a7e3ac1dfef39283b9ce5fb02b73f70c6c1f3dfe0c286b03e2af1af12d1d",
"y": "0x57e101887e73e40eab8963324ed16c177d55eb89f804ec9df06801579820420b5546b579008df2145fd770f584a1a54c"
},
"Q": {
"x": "0x13c1f8c52a492183f7c28e379b0475486718a7e3ac1dfef39283b9ce5fb02b73f70c6c1f3dfe0c286b03e2af1af12d1d",
"y": "0x57e101887e73e40eab8963324ed16c177d55eb89f804ec9df06801579820420b5546b579008df2145fd770f584a1a54c"
},
"msg": "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
"u": [
"0x504e4d5a529333b9205acaa283107bd1bffde753898f7744161f7dd19ba57fbb6a64214a2e00ddd2613d76cd508ddb30"
]
},
{
"P": {
"x": "0xaf129727a4207a8cb9e9dce656d88f79fce25edbcea350499d65e9bf1204537bdde73c7cefb752a6ed5ebcd44e183302",
"y": "0xce68a3d5e161b2e6a968e4ddaa9e51504ad1516ec170c7eef3ca6b5327943eca95d90b23b009ba45f58b72906f2a99e2"
},
"Q": {
"x": "0xaf129727a4207a8cb9e9dce656d88f79fce25edbcea350499d65e9bf1204537bdde73c7cefb752a6ed5ebcd44e183302",
"y": "0xce68a3d5e161b2e6a968e4ddaa9e51504ad1516ec170c7eef3ca6b5327943eca95d90b23b009ba45f58b72906f2a99e2"
},
"msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"u": [
"0x7b01ce9b8c5a60d9fbc202d6dde92822e46915d8c17e03fcb92ece1ed6074d01e149fc9236def40d673de903c1d4c166"
]
}
]
}

View File

@@ -0,0 +1,115 @@
{
"L": "0x48",
"Z": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000fffffff3",
"ciphersuite": "P384_XMD:SHA-384_SSWU_RO_",
"curve": "NIST P-384",
"dst": "QUUX-V01-CS02-with-P384_XMD:SHA-384_SSWU_RO_",
"expand": "XMD",
"field": {
"m": "0x1",
"p": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff"
},
"hash": "sha384",
"k": "0xc0",
"map": {
"name": "SSWU"
},
"randomOracle": true,
"vectors": [
{
"P": {
"x": "0xeb9fe1b4f4e14e7140803c1d99d0a93cd823d2b024040f9c067a8eca1f5a2eeac9ad604973527a356f3fa3aeff0e4d83",
"y": "0x0c21708cff382b7f4643c07b105c2eaec2cead93a917d825601e63c8f21f6abd9abc22c93c2bed6f235954b25048bb1a"
},
"Q0": {
"x": "0xe4717e29eef38d862bee4902a7d21b44efb58c464e3e1f0d03894d94de310f8ffc6de86786dd3e15a1541b18d4eb2846",
"y": "0x6b95a6e639822312298a47526bb77d9cd7bcf76244c991c8cd70075e2ee6e8b9a135c4a37e3c0768c7ca871c0ceb53d4"
},
"Q1": {
"x": "0x509527cfc0750eedc53147e6d5f78596c8a3b7360e0608e2fab0563a1670d58d8ae107c9f04bcf90e89489ace5650efd",
"y": "0x33337b13cb35e173fdea4cb9e8cce915d836ff57803dbbeb7998aa49d17df2ff09b67031773039d09fbd9305a1566bc4"
},
"msg": "",
"u": [
"0x25c8d7dc1acd4ee617766693f7f8829396065d1b447eedb155871feffd9c6653279ac7e5c46edb7010a0e4ff64c9f3b4",
"0x59428be4ed69131df59a0c6a8e188d2d4ece3f1b2a3a02602962b47efa4d7905945b1e2cc80b36aa35c99451073521ac"
]
},
{
"P": {
"x": "0xe02fc1a5f44a7519419dd314e29863f30df55a514da2d655775a81d413003c4d4e7fd59af0826dfaad4200ac6f60abe1",
"y": "0x01f638d04d98677d65bef99aef1a12a70a4cbb9270ec55248c04530d8bc1f8f90f8a6a859a7c1f1ddccedf8f96d675f6"
},
"Q0": {
"x": "0xfc853b69437aee9a19d5acf96a4ee4c5e04cf7b53406dfaa2afbdd7ad2351b7f554e4bbc6f5db4177d4d44f933a8f6ee",
"y": "0x7e042547e01834c9043b10f3a8221c4a879cb156f04f72bfccab0c047a304e30f2aa8b2e260d34c4592c0c33dd0c6482"
},
"Q1": {
"x": "0x57912293709b3556b43a2dfb137a315d256d573b82ded120ef8c782d607c05d930d958e50cb6dc1cc480b9afc38c45f1",
"y": "0xde9387dab0eef0bda219c6f168a92645a84665c4f2137c14270fb424b7532ff84843c3da383ceea24c47fa343c227bb8"
},
"msg": "abc",
"u": [
"0x53350214cb6bef0b51abb791b1c4209a2b4c16a0c67e1ab1401017fad774cd3b3f9a8bcdf7f6229dd8dd5a075cb149a0",
"0xc0473083898f63e03f26f14877a2407bd60c75ad491e7d26cbc6cc5ce815654075ec6b6898c7a41d74ceaf720a10c02e"
]
},
{
"P": {
"x": "0xbdecc1c1d870624965f19505be50459d363c71a699a496ab672f9a5d6b78676400926fbceee6fcd1780fe86e62b2aa89",
"y": "0x57cf1f99b5ee00f3c201139b3bfe4dd30a653193778d89a0accc5e0f47e46e4e4b85a0595da29c9494c1814acafe183c"
},
"Q0": {
"x": "0x0ceece45b73f89844671df962ad2932122e878ad2259e650626924e4e7f132589341dec1480ebcbbbe3509d11fb570b7",
"y": "0xfafd71a3115298f6be4ae5c6dfc96c400cfb55760f185b7b03f3fa45f3f91eb65d27628b3c705cafd0466fafa54883ce"
},
"Q1": {
"x": "0xdea1be8d3f9be4cbf4fab9d71d549dde76875b5d9b876832313a083ec81e528cbc2a0a1d0596b3bcb0ba77866b129776",
"y": "0xeb15fe71662214fb03b65541f40d3eb0f4cf5c3b559f647da138c9f9b7484c48a08760e02c16f1992762cb7298fa52cf"
},
"msg": "abcdef0123456789",
"u": [
"0xaab7fb87238cf6b2ab56cdcca7e028959bb2ea599d34f68484139dde85ec6548a6e48771d17956421bdb7790598ea52e",
"0x26e8d833552d7844d167833ca5a87c35bcfaa5a0d86023479fb28e5cd6075c18b168bf1f5d2a0ea146d057971336d8d1"
]
},
{
"P": {
"x": "0x03c3a9f401b78c6c36a52f07eeee0ec1289f178adf78448f43a3850e0456f5dd7f7633dd31676d990eda32882ab486c0",
"y": "0xcc183d0d7bdfd0a3af05f50e16a3f2de4abbc523215bf57c848d5ea662482b8c1f43dc453a93b94a8026db58f3f5d878"
},
"Q0": {
"x": "0x051a22105e0817a35d66196338c8d85bd52690d79bba373ead8a86dd9899411513bb9f75273f6483395a7847fb21edb4",
"y": "0xf168295c1bbcff5f8b01248e9dbc885335d6d6a04aea960f7384f746ba6502ce477e624151cc1d1392b00df0f5400c06"
},
"Q1": {
"x": "0x6ad7bc8ed8b841efd8ad0765c8a23d0b968ec9aa360a558ff33500f164faa02bee6c704f5f91507c4c5aad2b0dc5b943",
"y": "0x47313cc0a873ade774048338fc34ca5313f96bbf6ae22ac6ef475d85f03d24792dc6afba8d0b4a70170c1b4f0f716629"
},
"msg": "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
"u": [
"0x04c00051b0de6e726d228c85bf243bf5f4789efb512b22b498cde3821db9da667199b74bd5a09a79583c6d353a3bb41c",
"0x97580f218255f899f9204db64cd15e6a312cb4d8182375d1e5157c8f80f41d6a1a4b77fb1ded9dce56c32058b8d5202b"
]
},
{
"P": {
"x": "0x7b18d210b1f090ac701f65f606f6ca18fb8d081e3bc6cbd937c5604325f1cdea4c15c10a54ef303aabf2ea58bd9947a4",
"y": "0xea857285a33abb516732915c353c75c576bf82ccc96adb63c094dde580021eddeafd91f8c0bfee6f636528f3d0c47fd2"
},
"Q0": {
"x": "0x42e6666f505e854187186bad3011598d9278b9d6e3e4d2503c3d236381a56748dec5d139c223129b324df53fa147c4df",
"y": "0x8ee51dbda46413bf621838cc935d18d617881c6f33f3838a79c767a1e5618e34b22f79142df708d2432f75c7366c8512"
},
"Q1": {
"x": "0x4ff01ceeba60484fa1bc0d825fe1e5e383d8f79f1e5bb78e5fb26b7a7ef758153e31e78b9d60ce75c5e32e43869d4e12",
"y": "0x0f84b978fac8ceda7304b47e229d6037d32062e597dc7a9b95bcd9af441f3c56c619a901d21635f9ec6ab4710b9fcd0e"
},
"msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"u": [
"0x480cb3ac2c389db7f9dac9c396d2647ae946db844598971c26d1afd53912a1491199c0a5902811e4b809c26fcd37a014",
"0xd28435eb34680e148bf3908536e42231cba9e1f73ae2c6902a222a89db5c49c97db2f8fa4d4cd6e424b17ac60bdb9bb6"
]
}
]
}

View File

@@ -0,0 +1,90 @@
{
"L": "0x62",
"Z": "0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb",
"ciphersuite": "P521_XMD:SHA-512_SSWU_NU_",
"curve": "NIST P-521",
"dst": "QUUX-V01-CS02-with-P521_XMD:SHA-512_SSWU_NU_",
"expand": "XMD",
"field": {
"m": "0x1",
"p": "0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
},
"hash": "sha512",
"k": "0x100",
"map": {
"name": "SSWU"
},
"randomOracle": false,
"vectors": [
{
"P": {
"x": "0x01ec604b4e1e3e4c7449b7a41e366e876655538acf51fd40d08b97be066f7d020634e906b1b6942f9174b417027c953d75fb6ec64b8cee2a3672d4f1987d13974705",
"y": "0x00944fc439b4aad2463e5c9cfa0b0707af3c9a42e37c5a57bb4ecd12fef9fb21508568aedcdd8d2490472df4bbafd79081c81e99f4da3286eddf19be47e9c4cf0e91"
},
"Q": {
"x": "0x01ec604b4e1e3e4c7449b7a41e366e876655538acf51fd40d08b97be066f7d020634e906b1b6942f9174b417027c953d75fb6ec64b8cee2a3672d4f1987d13974705",
"y": "0x00944fc439b4aad2463e5c9cfa0b0707af3c9a42e37c5a57bb4ecd12fef9fb21508568aedcdd8d2490472df4bbafd79081c81e99f4da3286eddf19be47e9c4cf0e91"
},
"msg": "",
"u": [
"0x01e4947fe62a4e47792cee2798912f672fff820b2556282d9843b4b465940d7683a986f93ccb0e9a191fbc09a6e770a564490d2a4ae51b287ca39f69c3d910ba6a4f"
]
},
{
"P": {
"x": "0x00c720ab56aa5a7a4c07a7732a0a4e1b909e32d063ae1b58db5f0eb5e09f08a9884bff55a2bef4668f715788e692c18c1915cd034a6b998311fcf46924ce66a2be9a",
"y": "0x003570e87f91a4f3c7a56be2cb2a078ffc153862a53d5e03e5dad5bccc6c529b8bab0b7dbb157499e1949e4edab21cf5d10b782bc1e945e13d7421ad8121dbc72b1d"
},
"Q": {
"x": "0x00c720ab56aa5a7a4c07a7732a0a4e1b909e32d063ae1b58db5f0eb5e09f08a9884bff55a2bef4668f715788e692c18c1915cd034a6b998311fcf46924ce66a2be9a",
"y": "0x003570e87f91a4f3c7a56be2cb2a078ffc153862a53d5e03e5dad5bccc6c529b8bab0b7dbb157499e1949e4edab21cf5d10b782bc1e945e13d7421ad8121dbc72b1d"
},
"msg": "abc",
"u": [
"0x0019b85ef78596efc84783d42799e80d787591fe7432dee1d9fa2b7651891321be732ddf653fa8fefa34d86fb728db569d36b5b6ed3983945854b2fc2dc6a75aa25b"
]
},
{
"P": {
"x": "0x00bcaf32a968ff7971b3bbd9ce8edfbee1309e2019d7ff373c38387a782b005dce6ceffccfeda5c6511c8f7f312f343f3a891029c5858f45ee0bf370aba25fc990cc",
"y": "0x00923517e767532d82cb8a0b59705eec2b7779ce05f9181c7d5d5e25694ef8ebd4696343f0bc27006834d2517215ecf79482a84111f50c1bae25044fe1dd77744bbd"
},
"Q": {
"x": "0x00bcaf32a968ff7971b3bbd9ce8edfbee1309e2019d7ff373c38387a782b005dce6ceffccfeda5c6511c8f7f312f343f3a891029c5858f45ee0bf370aba25fc990cc",
"y": "0x00923517e767532d82cb8a0b59705eec2b7779ce05f9181c7d5d5e25694ef8ebd4696343f0bc27006834d2517215ecf79482a84111f50c1bae25044fe1dd77744bbd"
},
"msg": "abcdef0123456789",
"u": [
"0x01dba0d7fa26a562ee8a9014ebc2cca4d66fd9de036176aca8fc11ef254cd1bc208847ab7701dbca7af328b3f601b11a1737a899575a5c14f4dca5aaca45e9935e07"
]
},
{
"P": {
"x": "0x001ac69014869b6c4ad7aa8c443c255439d36b0e48a0f57b03d6fe9c40a66b4e2eaed2a93390679a5cc44b3a91862b34b673f0e92c83187da02bf3db967d867ce748",
"y": "0x00d5603d530e4d62b30fccfa1d90c2206654d74291c1db1c25b86a051ee3fffc294e5d56f2e776853406bd09206c63d40f37ad8829524cf89ad70b5d6e0b4a3b7341"
},
"Q": {
"x": "0x001ac69014869b6c4ad7aa8c443c255439d36b0e48a0f57b03d6fe9c40a66b4e2eaed2a93390679a5cc44b3a91862b34b673f0e92c83187da02bf3db967d867ce748",
"y": "0x00d5603d530e4d62b30fccfa1d90c2206654d74291c1db1c25b86a051ee3fffc294e5d56f2e776853406bd09206c63d40f37ad8829524cf89ad70b5d6e0b4a3b7341"
},
"msg": "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
"u": [
"0x00844da980675e1244cb209dcf3ea0aabec23bd54b2cda69fff86eb3acc318bf3d01bae96e9cd6f4c5ceb5539df9a7ad7fcc5e9d54696081ba9782f3a0f6d14987e3"
]
},
{
"P": {
"x": "0x01801de044c517a80443d2bd4f503a9e6866750d2f94a22970f62d721f96e4310e4a828206d9cdeaa8f2d476705cc3bbc490a6165c687668f15ec178a17e3d27349b",
"y": "0x0068889ea2e1442245fe42bfda9e58266828c0263119f35a61631a3358330f3bb84443fcb54fcd53a1d097fccbe310489b74ee143fc2938959a83a1f7dd4a6fd395b"
},
"Q": {
"x": "0x01801de044c517a80443d2bd4f503a9e6866750d2f94a22970f62d721f96e4310e4a828206d9cdeaa8f2d476705cc3bbc490a6165c687668f15ec178a17e3d27349b",
"y": "0x0068889ea2e1442245fe42bfda9e58266828c0263119f35a61631a3358330f3bb84443fcb54fcd53a1d097fccbe310489b74ee143fc2938959a83a1f7dd4a6fd395b"
},
"msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"u": [
"0x01aab1fb7e5cd44ba4d9f32353a383cb1bb9eb763ed40b32bdd5f666988970205998c0e44af6e2b5f6f8e48e969b3f649cae3c6ab463e1b274d968d91c02f00cce91"
]
}
]
}

View File

@@ -0,0 +1,115 @@
{
"L": "0x62",
"Z": "0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb",
"ciphersuite": "P521_XMD:SHA-512_SSWU_RO_",
"curve": "NIST P-521",
"dst": "QUUX-V01-CS02-with-P521_XMD:SHA-512_SSWU_RO_",
"expand": "XMD",
"field": {
"m": "0x1",
"p": "0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
},
"hash": "sha512",
"k": "0x100",
"map": {
"name": "SSWU"
},
"randomOracle": true,
"vectors": [
{
"P": {
"x": "0x00fd767cebb2452030358d0e9cf907f525f50920c8f607889a6a35680727f64f4d66b161fafeb2654bea0d35086bec0a10b30b14adef3556ed9f7f1bc23cecc9c088",
"y": "0x0169ba78d8d851e930680322596e39c78f4fe31b97e57629ef6460ddd68f8763fd7bd767a4e94a80d3d21a3c2ee98347e024fc73ee1c27166dc3fe5eeef782be411d"
},
"Q0": {
"x": "0x00b70ae99b6339fffac19cb9bfde2098b84f75e50ac1e80d6acb954e4534af5f0e9c4a5b8a9c10317b8e6421574bae2b133b4f2b8c6ce4b3063da1d91d34fa2b3a3c",
"y": "0x007f368d98a4ddbf381fb354de40e44b19e43bb11a1278759f4ea7b485e1b6db33e750507c071250e3e443c1aaed61f2c28541bb54b1b456843eda1eb15ec2a9b36e"
},
"Q1": {
"x": "0x01143d0e9cddcdacd6a9aafe1bcf8d218c0afc45d4451239e821f5d2a56df92be942660b532b2aa59a9c635ae6b30e803c45a6ac871432452e685d661cd41cf67214",
"y": "0x00ff75515df265e996d702a5380defffab1a6d2bc232234c7bcffa433cd8aa791fbc8dcf667f08818bffa739ae25773b32073213cae9a0f2a917a0b1301a242dda0c"
},
"msg": "",
"u": [
"0x01e5f09974e5724f25286763f00ce76238c7a6e03dc396600350ee2c4135fb17dc555be99a4a4bae0fd303d4f66d984ed7b6a3ba386093752a855d26d559d69e7e9e",
"0x00ae593b42ca2ef93ac488e9e09a5fe5a2f6fb330d18913734ff602f2a761fcaaf5f596e790bcc572c9140ec03f6cccc38f767f1c1975a0b4d70b392d95a0c7278aa"
]
},
{
"P": {
"x": "0x002f89a1677b28054b50d15e1f81ed6669b5a2158211118ebdef8a6efc77f8ccaa528f698214e4340155abc1fa08f8f613ef14a043717503d57e267d57155cf784a4",
"y": "0x010e0be5dc8e753da8ce51091908b72396d3deed14ae166f66d8ebf0a4e7059ead169ea4bead0232e9b700dd380b316e9361cfdba55a08c73545563a80966ecbb86d"
},
"Q0": {
"x": "0x01b254e1c99c835836f0aceebba7d77750c48366ecb07fb658e4f5b76e229ae6ca5d271bb0006ffcc42324e15a6d3daae587f9049de2dbb0494378ffb60279406f56",
"y": "0x01845f4af72fc2b1a5a2fe966f6a97298614288b456cfc385a425b686048b25c952fbb5674057e1eb055d04568c0679a8e2dda3158dc16ac598dbb1d006f5ad915b0"
},
"Q1": {
"x": "0x007f08e813c620e527c961b717ffc74aac7afccb9158cebc347d5715d5c2214f952c97e194f11d114d80d3481ed766ac0a3dba3eb73f6ff9ccb9304ad10bbd7b4a36",
"y": "0x0022468f92041f9970a7cc025d71d5b647f822784d29ca7b3bc3b0829d6bb8581e745f8d0cc9dc6279d0450e779ac2275c4c3608064ad6779108a7828ebd9954caeb"
},
"msg": "abc",
"u": [
"0x003d00c37e95f19f358adeeaa47288ec39998039c3256e13c2a4c00a7cb61a34c8969472960150a27276f2390eb5e53e47ab193351c2d2d9f164a85c6a5696d94fe8",
"0x01f3cbd3df3893a45a2f1fecdac4d525eb16f345b03e2820d69bc580f5cbe9cb89196fdf720ef933c4c0361fcfe29940fd0db0a5da6bafb0bee8876b589c41365f15"
]
},
{
"P": {
"x": "0x006e200e276a4a81760099677814d7f8794a4a5f3658442de63c18d2244dcc957c645e94cb0754f95fcf103b2aeaf94411847c24187b89fb7462ad3679066337cbc4",
"y": "0x001dd8dfa9775b60b1614f6f169089d8140d4b3e4012949b52f98db2deff3e1d97bf73a1fa4d437d1dcdf39b6360cc518d8ebcc0f899018206fded7617b654f6b168"
},
"Q0": {
"x": "0x0021482e8622aac14da60e656043f79a6a110cbae5012268a62dd6a152c41594549f373910ebed170ade892dd5a19f5d687fae7095a461d583f8c4295f7aaf8cd7da",
"y": "0x0177e2d8c6356b7de06e0b5712d8387d529b848748e54a8bc0ef5f1475aa569f8f492fa85c3ad1c5edc51faf7911f11359bfa2a12d2ef0bd73df9cb5abd1b101c8b1"
},
"Q1": {
"x": "0x00abeafb16fdbb5eb95095678d5a65c1f293291dfd20a3751dbe05d0a9bfe2d2eef19449fe59ec32cdd4a4adc3411177c0f2dffd0159438706159a1bbd0567d9b3d0",
"y": "0x007cc657f847db9db651d91c801741060d63dab4056d0a1d3524e2eb0e819954d8f677aa353bd056244a88f00017e00c3ce8beeedb4382d83d74418bd48930c6c182"
},
"msg": "abcdef0123456789",
"u": [
"0x00183ee1a9bbdc37181b09ec336bcaa34095f91ef14b66b1485c166720523dfb81d5c470d44afcb52a87b704dbc5c9bc9d0ef524dec29884a4795f55c1359945baf3",
"0x00504064fd137f06c81a7cf0f84aa7e92b6b3d56c2368f0a08f44776aa8930480da1582d01d7f52df31dca35ee0a7876500ece3d8fe0293cd285f790c9881c998d5e"
]
},
{
"P": {
"x": "0x01b264a630bd6555be537b000b99a06761a9325c53322b65bdc41bf196711f9708d58d34b3b90faf12640c27b91c70a507998e55940648caa8e71098bf2bc8d24664",
"y": "0x01ea9f445bee198b3ee4c812dcf7b0f91e0881f0251aab272a12201fd89b1a95733fd2a699c162b639e9acdcc54fdc2f6536129b6beb0432be01aa8da02df5e59aaa"
},
"Q0": {
"x": "0x0005eac7b0b81e38727efcab1e375f6779aea949c3e409b53a1d37aa2acbac87a7e6ad24aafbf3c52f82f7f0e21b872e88c55e17b7fa21ce08a94ea2121c42c2eb73",
"y": "0x00a173b6a53a7420dbd61d4a21a7c0a52de7a5c6ce05f31403bef747d16cc8604a039a73bdd6e114340e55dacd6bea8e217ffbadfb8c292afa3e1b2afc839a6ce7bb"
},
"Q1": {
"x": "0x01881e3c193a69e4d88d8180a6879b74782a0bc7e529233e9f84bf7f17d2f319c36920ffba26f9e57a1e045cc7822c834c239593b6e142a694aa00c757b0db79e5e8",
"y": "0x01558b16d396d866e476e001f2dd0758927655450b84e12f154032c7c2a6db837942cd9f44b814f79b4d729996ced61eec61d85c675139cbffe3fbf071d2c21cfecb"
},
"msg": "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
"u": [
"0x0159871e222689aad7694dc4c3480a49807b1eedd9c8cb4ae1b219d5ba51655ea5b38e2e4f56b36bf3e3da44a7b139849d28f598c816fe1bc7ed15893b22f63363c3",
"0x004ef0cffd475152f3858c0a8ccbdf7902d8261da92744e98df9b7fadb0a5502f29c5086e76e2cf498f47321434a40b1504911552ce44ad7356a04e08729ad9411f5"
]
},
{
"P": {
"x": "0x00c12bc3e28db07b6b4d2a2b1167ab9e26fc2fa85c7b0498a17b0347edf52392856d7e28b8fa7a2dd004611159505835b687ecf1a764857e27e9745848c436ef3925",
"y": "0x01cd287df9a50c22a9231beb452346720bb163344a41c5f5a24e8335b6ccc595fd436aea89737b1281aecb411eb835f0b939073fdd1dd4d5a2492e91ef4a3c55bcbd"
},
"Q0": {
"x": "0x00041f6eb92af8777260718e4c22328a7d74203350c6c8f5794d99d5789766698f459b83d5068276716f01429934e40af3d1111a22780b1e07e72238d2207e5386be",
"y": "0x001c712f0182813942b87cab8e72337db017126f52ed797dd234584ac9ae7e80dfe7abea11db02cf1855312eae1447dbaecc9d7e8c880a5e76a39f6258074e1bc2e0"
},
"Q1": {
"x": "0x0125c0b69bcf55eab49280b14f707883405028e05c927cd7625d4e04115bd0e0e6323b12f5d43d0d6d2eff16dbcf244542f84ec058911260dc3bb6512ab5db285fbd",
"y": "0x008bddfb803b3f4c761458eb5f8a0aee3e1f7f68e9d7424405fa69172919899317fb6ac1d6903a432d967d14e0f80af63e7035aaae0c123e56862ce969456f99f102"
},
"msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"u": [
"0x0033d06d17bc3b9a3efc081a05d65805a14a3050a0dd4dfb4884618eb5c73980a59c5a246b18f58ad022dd3630faa22889fbb8ba1593466515e6ab4aeb7381c26334",
"0x0092290ab99c3fea1a5b8fb2ca49f859994a04faee3301cefab312d34227f6a2d0c3322cf76861c6a3683bdaa2dd2a6daa5d6906c663e065338b2344d20e313f1114"
]
}
]
}

View File

@@ -0,0 +1,90 @@
{
"L": "0x30",
"Z": "0x2",
"ciphersuite": "curve25519_XMD:SHA-512_ELL2_NU_",
"curve": "curve25519",
"dst": "QUUX-V01-CS02-with-curve25519_XMD:SHA-512_ELL2_NU_",
"expand": "XMD",
"field": {
"m": "0x1",
"p": "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed"
},
"hash": "sha512",
"k": "0x80",
"map": {
"name": "ELL2"
},
"randomOracle": false,
"vectors": [
{
"P": {
"x": "0x1bb913f0c9daefa0b3375378ffa534bda5526c97391952a7789eb976edfe4d08",
"y": "0x4548368f4f983243e747b62a600840ae7c1dab5c723991f85d3a9768479f3ec4"
},
"Q": {
"x": "0x51125222da5e763d97f3c10fcc92ea6860b9ccbbd2eb1285728f566721c1e65b",
"y": "0x343d2204f812d3dfc5304a5808c6c0d81a903a5d228b342442aa3c9ba5520a3d"
},
"msg": "",
"u": [
"0x608d892b641f0328523802a6603427c26e55e6f27e71a91a478148d45b5093cd"
]
},
{
"P": {
"x": "0x7c22950b7d900fa866334262fcaea47a441a578df43b894b4625c9b450f9a026",
"y": "0x5547bc00e4c09685dcbc6cb6765288b386d8bdcb595fa5a6e3969e08097f0541"
},
"Q": {
"x": "0x7d56d1e08cb0ccb92baf069c18c49bb5a0dcd927eff8dcf75ca921ef7f3e6eeb",
"y": "0x404d9a7dc25c9c05c44ab9a94590e7c3fe2dcec74533a0b24b188a5d5dacf429"
},
"msg": "abc",
"u": [
"0x46f5b22494bfeaa7f232cc8d054be68561af50230234d7d1d63d1d9abeca8da5"
]
},
{
"P": {
"x": "0x31ad08a8b0deeb2a4d8b0206ca25f567ab4e042746f792f4b7973f3ae2096c52",
"y": "0x405070c28e78b4fa269427c82827261991b9718bd6c6e95d627d701a53c30db1"
},
"Q": {
"x": "0x3fbe66b9c9883d79e8407150e7c2a1c8680bee496c62fabe4619a72b3cabe90f",
"y": "0x08ec476147c9a0a3ff312d303dbbd076abb7551e5fce82b48ab14b433f8d0a7b"
},
"msg": "abcdef0123456789",
"u": [
"0x235fe40c443766ce7e18111c33862d66c3b33267efa50d50f9e8e5d252a40aaa"
]
},
{
"P": {
"x": "0x027877759d155b1997d0d84683a313eb78bdb493271d935b622900459d52ceaa",
"y": "0x54d691731a53baa30707f4a87121d5169fb5d587d70fb0292b5830dedbec4c18"
},
"Q": {
"x": "0x227e0bb89de700385d19ec40e857db6e6a3e634b1c32962f370d26f84ff19683",
"y": "0x5f86ff3851d262727326a32c1bf7655a03665830fa7f1b8b1e5a09d85bc66e4a"
},
"msg": "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
"u": [
"0x001e92a544463bda9bd04ddbe3d6eed248f82de32f522669efc5ddce95f46f5b"
]
},
{
"P": {
"x": "0x5fd892c0958d1a75f54c3182a18d286efab784e774d1e017ba2fb252998b5dc1",
"y": "0x750af3c66101737423a4519ac792fb93337bd74ee751f19da4cf1e94f4d6d0b8"
},
"Q": {
"x": "0x3bcd651ee54d5f7b6013898aab251ee8ecc0688166fce6e9548d38472f6bd196",
"y": "0x1bb36ad9197299f111b4ef21271c41f4b7ecf5543db8bb5931307ebdb2eaa465"
},
"msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"u": [
"0x1a68a1af9f663592291af987203393f707305c7bac9c8d63d6a729bdc553dc19"
]
}
]
}

View File

@@ -0,0 +1,115 @@
{
"L": "0x30",
"Z": "0x2",
"ciphersuite": "curve25519_XMD:SHA-512_ELL2_RO_",
"curve": "curve25519",
"dst": "QUUX-V01-CS02-with-curve25519_XMD:SHA-512_ELL2_RO_",
"expand": "XMD",
"field": {
"m": "0x1",
"p": "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed"
},
"hash": "sha512",
"k": "0x80",
"map": {
"name": "ELL2"
},
"randomOracle": true,
"vectors": [
{
"P": {
"x": "0x2de3780abb67e861289f5749d16d3e217ffa722192d16bbd9d1bfb9d112b98c0",
"y": "0x3b5dc2a498941a1033d176567d457845637554a2fe7a3507d21abd1c1bd6e878"
},
"Q0": {
"x": "0x36b4df0c864c64707cbf6cf36e9ee2c09a6cb93b28313c169be29561bb904f98",
"y": "0x6cd59d664fb58c66c892883cd0eb792e52055284dac3907dd756b45d15c3983d"
},
"Q1": {
"x": "0x3fa114783a505c0b2b2fbeef0102853c0b494e7757f2a089d0daae7ed9a0db2b",
"y": "0x76c0fe7fec932aaafb8eefb42d9cbb32eb931158f469ff3050af15cfdbbeff94"
},
"msg": "",
"u": [
"0x005fe8a7b8fef0a16c105e6cadf5a6740b3365e18692a9c05bfbb4d97f645a6a",
"0x1347edbec6a2b5d8c02e058819819bee177077c9d10a4ce165aab0fd0252261a"
]
},
{
"P": {
"x": "0x2b4419f1f2d48f5872de692b0aca72cc7b0a60915dd70bde432e826b6abc526d",
"y": "0x1b8235f255a268f0a6fa8763e97eb3d22d149343d495da1160eff9703f2d07dd"
},
"Q0": {
"x": "0x16b3d86e056b7970fa00165f6f48d90b619ad618791661b7b5e1ec78be10eac1",
"y": "0x4ab256422d84c5120b278cbdfc4e1facc5baadffeccecf8ee9bf3946106d50ca"
},
"Q1": {
"x": "0x7ec29ddbf34539c40adfa98fcb39ec36368f47f30e8f888cc7e86f4d46e0c264",
"y": "0x10d1abc1cae2d34c06e247f2141ba897657fb39f1080d54f09ce0af128067c74"
},
"msg": "abc",
"u": [
"0x49bed021c7a3748f09fa8cdfcac044089f7829d3531066ac9e74e0994e05bc7d",
"0x5c36525b663e63389d886105cee7ed712325d5a97e60e140aba7e2ce5ae851b6"
]
},
{
"P": {
"x": "0x68ca1ea5a6acf4e9956daa101709b1eee6c1bb0df1de3b90d4602382a104c036",
"y": "0x2a375b656207123d10766e68b938b1812a4a6625ff83cb8d5e86f58a4be08353"
},
"Q0": {
"x": "0x71de3dadfe268872326c35ac512164850860567aea0e7325e6b91a98f86533ad",
"y": "0x26a08b6e9a18084c56f2147bf515414b9b63f1522e1b6c5649f7d4b0324296ec"
},
"Q1": {
"x": "0x5704069021f61e41779e2ba6b932268316d6d2a6f064f997a22fef16d1eaeaca",
"y": "0x50483c7540f64fb4497619c050f2c7fe55454ec0f0e79870bb44302e34232210"
},
"msg": "abcdef0123456789",
"u": [
"0x6412b7485ba26d3d1b6c290a8e1435b2959f03721874939b21782df17323d160",
"0x24c7b46c1c6d9a21d32f5707be1380ab82db1054fde82865d5c9e3d968f287b2"
]
},
{
"P": {
"x": "0x096e9c8bae6c06b554c1ee69383bb0e82267e064236b3a30608d4ed20b73ac5a",
"y": "0x1eb5a62612cafb32b16c3329794645b5b948d9f8ffe501d4e26b073fef6de355"
},
"Q0": {
"x": "0x7a94d45a198fb5daa381f45f2619ab279744efdd8bd8ed587fc5b65d6cea1df0",
"y": "0x67d44f85d376e64bb7d713585230cdbfafc8e2676f7568e0b6ee59361116a6e1"
},
"Q1": {
"x": "0x30506fb7a32136694abd61b6113770270debe593027a968a01f271e146e60c18",
"y": "0x7eeee0e706b40c6b5174e551426a67f975ad5a977ee2f01e8e20a6d612458c3b"
},
"msg": "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
"u": [
"0x5e123990f11bbb5586613ffabdb58d47f64bb5f2fa115f8ea8df0188e0c9e1b5",
"0x5e8553eb00438a0bb1e7faa59dec6d8087f9c8011e5fb8ed9df31cb6c0d4ac19"
]
},
{
"P": {
"x": "0x1bc61845a138e912f047b5e70ba9606ba2a447a4dade024c8ef3dd42b7bbc5fe",
"y": "0x623d05e47b70e25f7f1d51dda6d7c23c9a18ce015fe3548df596ea9e38c69bf1"
},
"Q0": {
"x": "0x02d606e2699b918ee36f2818f2bc5013e437e673c9f9b9cdc15fd0c5ee913970",
"y": "0x29e9dc92297231ef211245db9e31767996c5625dfbf92e1c8107ef887365de1e"
},
"Q1": {
"x": "0x38920e9b988d1ab7449c0fa9a6058192c0c797bb3d42ac345724341a1aa98745",
"y": "0x24dcc1be7c4d591d307e89049fd2ed30aae8911245a9d8554bf6032e5aa40d3d"
},
"msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"u": [
"0x20f481e85da7a3bf60ac0fb11ed1d0558fc6f941b3ac5469aa8b56ec883d6d7d",
"0x017d57fd257e9a78913999a23b52ca988157a81b09c5442501d07fed20869465"
]
}
]
}

View File

@@ -0,0 +1,90 @@
{
"L": "0x54",
"Z": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffffffffffffffffffffffffffffffffffffffffffffffffffffe",
"ciphersuite": "curve448_XOF:SHAKE256_ELL2_NU_",
"curve": "curve448",
"dst": "QUUX-V01-CS02-with-curve448_XOF:SHAKE256_ELL2_NU_",
"expand": "XOF",
"field": {
"m": "0x1",
"p": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
},
"hash": "shake_256",
"k": "0xe0",
"map": {
"name": "ELL2"
},
"randomOracle": false,
"vectors": [
{
"P": {
"x": "0xb65e8dbb279fd656f926f68d463b13ca7a982b32f5da9c7cc58afcf6199e4729863fb75ca9ae3c95c6887d95a5102637a1c5c40ff0aafadc",
"y": "0xea1ea211cf29eca11c057fe8248181591a19f6ac51d45843a65d4bb8b71bc83a64c771ed7686218a278ef1c5d620f3d26b53162188645453"
},
"Q": {
"x": "0xe6304424de5af3f556d3e645600530c53ad949891c3e60ba041dd5f68a93901beff8440164477d348c13d28e27bfcd360c44c80b4c7d4cea",
"y": "0x4160a8f2043a347185406a6a7e50973b98b82edbdfa3209b0e1c90118e10eeb45045b0990d4b2b0708a30eca17df40ad53c9100f20c10b44"
},
"msg": "",
"u": [
"0x242c70f74eac8184116c71630d284cf8a742fc463e710545847ff64d8e9161cb9f599728a18a32dbd8b67c3bec5d64c9b1d2f2cde7b5888d"
]
},
{
"P": {
"x": "0x51aceca4fa95854bbaba58d8a5e17a86c07acadef32e1188cafda26232131800002cc2f27c7aec454e5e0c615bddffb7df6a5f7f0f14793f",
"y": "0xc590c9246eb28b08dee816d608ef233ea5d76e305dc458774a1e1bd880387e6734219e2018e4aa50a49486dce0ba8740065da37e6cf5212c"
},
"Q": {
"x": "0xde0dc93df9ce7953452f20e270699c1e7dacd5d571c226d77f53b7e3053d16f8a81b1601efb362054e973c8e733b663af93f00cb81baf130",
"y": "0x8c5bdec6fa6690905f6eff966b0f98f5a8161493bd04976684d4ec1f4512fa8743d86860b2ff2c5d67e9c145fd906f2cb89ff812c6b9883f"
},
"msg": "abc",
"u": [
"0xef6dcb75b696d325fb36d66b104700df1480c4c17ea9190d447eee1e7e4c9b7f36bbfb8ba7ba7c4cb6b07fed16531c1ac7a26a3618b40b34"
]
},
{
"P": {
"x": "0xc6d65987f146b8d0cb5d2c44e1872ac3af1f458f6a8bd8c232ffe8b9d09496229a5a27f350eb7d97305bcc4e0f38328718352e8e3129ed71",
"y": "0x4d2f901bf333fdc4135b954f20d59207e9f6a4ecf88ce5af11c892b44f79766ec4ecc9f60d669b95ca8940f39b1b7044140ac2040c1bf659"
},
"Q": {
"x": "0xdc29532761f03c24d57f530da4c24acc4c676d185becaa89fcc083266541fb7f10ecec91dac64a34cd988274633ae25c4d784aee52de47a8",
"y": "0xa5f6da11259c69f2e07fce6a7b6afec4c25bd2df83426765f9c0704111da24c6a0550d5c7aac7d648d55f7640d50be99c926195e852adaac"
},
"msg": "abcdef0123456789",
"u": [
"0x3012ba5d9b3bb648e4613833a26ecaeadb3e8c8bba07fc90ac3da0375769289c44d3dc87474b23df7f45f9a4030892cda689e343aeeea6ad"
]
},
{
"P": {
"x": "0x9b8d008863beb4a02fb9e4efefd2eba867307fb1c7ce01746115d32e1db551bb254e8e3e4532d5c74a83949a69a60519ecc9178083cbe943",
"y": "0x346a1fca454d1e67c628437c270ec0f0c4256bb774fe6c0e49de7004ff6d9199e2cd99d8f7575a96aafc4dc8db1811ba0a44317581f41371"
},
"Q": {
"x": "0x512803d89f59c57376e6570cd54c4e901643e089cd9456f549daa4372b8b52679860b68aa8bedfaa88970f15ab6098d5f252083ac98a58c9",
"y": "0x3d9b6593c7941a20d76161c9a171f1e507495a08f03dfcae33a2ac3602698e46a74d1039b583c984036f590eaa43d20ba5aada3ffb552f77"
},
"msg": "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
"u": [
"0xfe952ac0149f92436bba12ea2e542aa226f4fc074d79ff462c41b327968a649a495a8a93b6c3044af2273456abb5e166ce4fb8c9b10c8c2e"
]
},
{
"P": {
"x": "0x8746dc34799112d1f20acda9d7f722c9abb29b1fb6b7e9e566983843c20bd7c9bfad21b45c5166b808d2f5d44e188f1fdaf29cdee8a72e4c",
"y": "0x7c1293484c9287c298a1a0600c64347eee8530acf563cd8705e05728274d8cd8101835f8003b6f3b78b5beb28f5be188a3d7bce1ec5a36b1"
},
"Q": {
"x": "0x08aed6480793218034fd3b3b0867943d7e0bd1b6f76b4929e0885bd082b84d4449341da6038bb08229ad9eb7d518dff2c7ea50148e70a4db",
"y": "0xe00d32244561ebd4b5f4ef70fcac75a06416be0a1c1b304e7bd361a6a6586915bb902a323eaf73cf7738e70d34282f61485395ab2833d2c1"
},
"msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"u": [
"0xafd3d7ad9d819be7561706e050d4f30b634b203387ab682739365f62cd7393ca2cf18cd07a3d3af8dd163f043ac7457c2eb145b4a56170a9"
]
}
]
}

View File

@@ -0,0 +1,115 @@
{
"L": "0x54",
"Z": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffffffffffffffffffffffffffffffffffffffffffffffffffffe",
"ciphersuite": "curve448_XOF:SHAKE256_ELL2_RO_",
"curve": "curve448",
"dst": "QUUX-V01-CS02-with-curve448_XOF:SHAKE256_ELL2_RO_",
"expand": "XOF",
"field": {
"m": "0x1",
"p": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
},
"hash": "shake_256",
"k": "0xe0",
"map": {
"name": "ELL2"
},
"randomOracle": true,
"vectors": [
{
"P": {
"x": "0x5ea5ff623d27c75e73717514134e73e419f831a875ca9e82915fdfc7069d0a9f8b532cfb32b1d8dd04ddeedbe3fa1d0d681c01e825d6a9ea",
"y": "0xafadd8de789f8f8e3516efbbe313a7eba364c939ecba00dabf4ced5c563b18e70a284c17d8f46b564c4e6ce11784a3825d941116622128c1"
},
"Q0": {
"x": "0x3ba318806f89c19cc019f51e33eb6b8c038dab892e858ce7c7f2c2ac58618d06146a5fef31e49af49588d4d3db1bcf02bd4e4a733e37065d",
"y": "0xb30b4cfc2fd14d9d4b70456c0f5c6f6070be551788893d570e7955675a20f6c286d01d6e90d2fb500d2efb8f4e18db7f8268bb9b7fbc5975"
},
"Q1": {
"x": "0xf03a48cf003f63be61ca055fec87c750434da07a15f8aa6210389ff85943b5166484339c8bea1af9fc571313d35ed2fbb779408b760c4cbd",
"y": "0x23943a33b2954dc54b76a8222faf5b7e18405a41f5ecc61bf1b8df1f9cbfad057307ed0c7b721f19c0390b8ee3a2dec223671f9ff905fda7"
},
"msg": "",
"u": [
"0xc704c7b3d3b36614cf3eedd0324fe6fe7d1402c50efd16cff89ff63f50938506280d3843478c08e24f7842f4e3ef45f6e3c4897f9d976148",
"0xc25427dc97fff7a5ad0a78654e2c6c27b1c1127b5b53c7950cd1fd6edd2703646b25f341e73deedfebf022d1d3cecd02b93b4d585ead3ed7"
]
},
{
"P": {
"x": "0x9b2f7ce34878d7cebf34c582db14958308ea09366d1ec71f646411d3de0ae564d082b06f40cd30dfc08d9fb7cb21df390cf207806ad9d0e4",
"y": "0x138a0eef0a4993ea696152ed7db61f7ddb4e8100573591e7466d61c0c568ecaec939e36a84d276f34c402526d8989a96e99760c4869ed633"
},
"Q0": {
"x": "0x26714783887ec444fbade9ae350dc13e8d5a64150679232560726a73d36e28bd56766d7d0b0899d79c8d1c889ae333f601c57532ff3c4f09",
"y": "0x080e486f8f5740dbbe82305160cab9fac247b0b22a54d961de675037c3036fa68464c8756478c322ae0aeb9ba386fe626cebb0bcca46840c"
},
"Q1": {
"x": "0x0d9741d10421691a8ebc7778b5f623260fdf8b28ae28d776efcb8e0d5fbb65139a2f828617835f527cb2ca24a8f5fc8e84378343c43d096d",
"y": "0x54f4c499bf3d5b154511913f9615bd914969b65cfb74508d7ae5a169e9595b7cbcab9a1485e07b2ce426e4fbed052f03842c4313b7dbe39a"
},
"msg": "abc",
"u": [
"0x2dd95593dfee26fe0d218d3d9a0a23d9e1a262fd1d0b602483d08415213e75e2db3c69b0a5bc89e71bcefc8c723d2b6a0cf263f02ad2aa70",
"0x272e4c79a1290cc6d2bc4f4f9d31bf7fbe956ca303c04518f117d77c0e9d850796fc3e1e2bcb9c75e8eaaded5e150333cae9931868047c9d"
]
},
{
"P": {
"x": "0xf54ecd14b85a50eeeee0618452df3a75be7bfba11da5118774ae4ea55ac204e153f77285d780c4acee6c96abe3577a0c0b00be6e790cf194",
"y": "0x935247a64bf78c107069943c7e3ecc52acb27ce4a3230407c8357341685ea2152e8c3da93f8cd77da1bddb5bb759c6e7ae7d516dced42850"
},
"Q0": {
"x": "0x946d91bd50c90ef70743e0dd194bddd68bb630f4e67e5b93e15a9b94e62cb85134467993501759525c1f4fdbf06f10ddaf817847d735e062",
"y": "0x185cf511262ec1e9b3c3cbdc015ab93df4e71cbe87766917d81c9f3419d480407c1462385122c84982d4dae60c3ae4acce0089e37ad65934"
},
"Q1": {
"x": "0x01778f4797b717cd6f83c193b2dfb92a1606a36ede941b0f6ab0ac71ad0eac756d17604bf054398887da907e41065d3595f178ae802f2087",
"y": "0xb4ca727d0bda895e0eee7eb3cbc28710fa2e90a73b568cae26bd7c2e73b70a9fa0affe1096f0810198890ed65d8935886b6e60dc4c569dc6"
},
"msg": "abcdef0123456789",
"u": [
"0x6aab71a38391639f27e49eae8b1cb6b7172a1f478190ece293957e7cdb2391e7cc1c4261970d9c1bbf9c3915438f74fbd7eb5cd4d4d17ace",
"0xc80b8380ca47a3bcbf76caa75cef0e09f3d270d5ee8f676cde11aedf41aaca6741bd81a86232bd336ccb42efad39f06542bc06a67b65909e"
]
},
{
"P": {
"x": "0x5bd67c4f88adf6beb10f7e0d0054659776a55c97b809ec8b3101729e104fd0f684e103792f267fd87cc4afc25a073956ef4f268fb02824d5",
"y": "0xda1f5cb16a352719e4cb064cf47ba72aeba7752d03e8ca2c56229f419b4ef378785a5af1a53dd7ab4d467c1f92f7b139b3752faf29c96432"
},
"Q0": {
"x": "0xc2d275826d6ad55e41a22318f6b6240f1f862a2e231120ff41eadbec319756032e8cef2a7ac6c10214fa0608c17fcaf61ec2694a8a2b358b",
"y": "0x93d2e092762b135509840e609d413200df800d99da91d8b82840666cac30e7a3520adbaa4b089bfdc86132e42729f651d022f4782502f12c"
},
"Q1": {
"x": "0x3c0880ece7244036e9a45944a85599f9809d772f770cc237ac41b21aa71615e4f3bb08f64fca618896e4f6cf5bd92e16b89d2cf6e1956bfb",
"y": "0x45cce4beb96505cac5976b3d2673641e9bcd18d3462bbb453d293e5282740a6389cfeae610adc7bd425c728541ceec83fcc999164af43fb5"
},
"msg": "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
"u": [
"0xcb5c27e51f9c18ee8ffdb6be230f4eb4f2c2481963b2293484f08da2241c1ff59f80978e6defe9d70e34abba2fcbe12dc3a1eb2c5d3d2e4a",
"0xc895e8afecec5466e126fa70fc4aa784b8009063afb10e3ee06a9b22318256aa8693b0c85b955cf2d6540b8ed71e729af1b8d5ca3b116cd7"
]
},
{
"P": {
"x": "0xea441c10b3636ecedd5c0dfcae96384cc40de8390a0ab648765b4508da12c586d55dc981275776507ebca0e4d1bcaa302bb69dcfa31b3451",
"y": "0xfee0192d49bcc0c28d954763c2cbe739b9265c4bebe3883803c64971220cfda60b9ac99ad986cd908c0534b260b5cfca46f6c2b0f3f21bda"
},
"Q0": {
"x": "0x4321ab02a9849128691e9b80a5c5576793a218de14885fddccb91f17ceb1646ea00a28b69ad211e1f14f17739612dbde3782319bdf009689",
"y": "0x1b8a7b539519eec0ea9f7a46a43822e16cba39a439733d6847ac44a806b8adb3e1a75ea48a1228b8937ba85c6cb6ee01046e10cad8953b1e"
},
"Q1": {
"x": "0x126d744da6a14fddec0f78a9cee4571c1320ac7645b600187812e4d7021f98fc4703732c54daec787206e1f34d9dbbf4b292c68160b8bfbd",
"y": "0x136eebe6020f2389d448923899a1a38a4c8ad74254e0686e91c4f93c1f8f8e1bd619ffb7c1281467882a9c957d22d50f65c5b72b2aee11af"
},
"msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"u": [
"0x8cba93a007bb2c801b1769e026b1fa1640b14a34cf3029db3c7fd6392745d6fec0f7870b5071d6da4402cedbbde28ae4e50ab30e1049a238",
"0x4223746145069e4b8a981acc3404259d1a2c3ecfed5d864798a89d45f81a2c59e2d40eb1d5f0fe11478cbb2bb30246dd388cb932ad7bb330"
]
}
]
}

View File

@@ -0,0 +1,90 @@
{
"L": "0x30",
"Z": "0x2",
"ciphersuite": "edwards25519_XMD:SHA-512_ELL2_NU_",
"curve": "edwards25519",
"dst": "QUUX-V01-CS02-with-edwards25519_XMD:SHA-512_ELL2_NU_",
"expand": "XMD",
"field": {
"m": "0x1",
"p": "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed"
},
"hash": "sha512",
"k": "0x80",
"map": {
"name": "ELL2"
},
"randomOracle": false,
"vectors": [
{
"P": {
"x": "0x1ff2b70ecf862799e11b7ae744e3489aa058ce805dd323a936375a84695e76da",
"y": "0x222e314d04a4d5725e9f2aff9fb2a6b69ef375a1214eb19021ceab2d687f0f9b"
},
"Q": {
"x": "0x42836f691d05211ebc65ef8fcf01e0fb6328ec9c4737c26050471e50803022eb",
"y": "0x22cb4aaa555e23bd460262d2130d6a3c9207aa8bbb85060928beb263d6d42a95"
},
"msg": "",
"u": [
"0x7f3e7fb9428103ad7f52db32f9df32505d7b427d894c5093f7a0f0374a30641d"
]
},
{
"P": {
"x": "0x5f13cc69c891d86927eb37bd4afc6672360007c63f68a33ab423a3aa040fd2a8",
"y": "0x67732d50f9a26f73111dd1ed5dba225614e538599db58ba30aaea1f5c827fa42"
},
"Q": {
"x": "0x333e41b61c6dd43af220c1ac34a3663e1cf537f996bab50ab66e33c4bd8e4e19",
"y": "0x51b6f178eb08c4a782c820e306b82c6e273ab22e258d972cd0c511787b2a3443"
},
"msg": "abc",
"u": [
"0x09cfa30ad79bd59456594a0f5d3a76f6b71c6787b04de98be5cd201a556e253b"
]
},
{
"P": {
"x": "0x1dd2fefce934ecfd7aae6ec998de088d7dd03316aa1847198aecf699ba6613f1",
"y": "0x2f8a6c24dd1adde73909cada6a4a137577b0f179d336685c4a955a0a8e1a86fb"
},
"Q": {
"x": "0x55186c242c78e7d0ec5b6c9553f04c6aeef64e69ec2e824472394da32647cfc6",
"y": "0x5b9ea3c265ee42256a8f724f616307ef38496ef7eba391c08f99f3bea6fa88f0"
},
"msg": "abcdef0123456789",
"u": [
"0x475ccff99225ef90d78cc9338e9f6a6bb7b17607c0c4428937de75d33edba941"
]
},
{
"P": {
"x": "0x35fbdc5143e8a97afd3096f2b843e07df72e15bfca2eaf6879bf97c5d3362f73",
"y": "0x2af6ff6ef5ebba128b0774f4296cb4c2279a074658b083b8dcca91f57a603450"
},
"Q": {
"x": "0x024b6e1621606dca8071aa97b43dce4040ca78284f2a527dcf5d0fbfac2b07e7",
"y": "0x5102353883d739bdc9f8a3af650342b171217167dcce34f8db57208ec1dfdbf2"
},
"msg": "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
"u": [
"0x049a1c8bd51bcb2aec339f387d1ff51428b88d0763a91bcdf6929814ac95d03d"
]
},
{
"P": {
"x": "0x6e5e1f37e99345887fc12111575fc1c3e36df4b289b8759d23af14d774b66bff",
"y": "0x2c90c3d39eb18ff291d33441b35f3262cdd307162cc97c31bfcc7a4245891a37"
},
"Q": {
"x": "0x3e6368cff6e88a58e250c54bd27d2c989ae9b3acb6067f2651ad282ab8c21cd9",
"y": "0x38fb39f1566ca118ae6c7af42810c0bb9767ae5960abb5a8ca792530bfb9447d"
},
"msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"u": [
"0x3cb0178a8137cefa5b79a3a57c858d7eeeaa787b2781be4a362a2f0750d24fa0"
]
}
]
}

View File

@@ -0,0 +1,115 @@
{
"L": "0x30",
"Z": "0x2",
"ciphersuite": "edwards25519_XMD:SHA-512_ELL2_RO_",
"curve": "edwards25519",
"dst": "QUUX-V01-CS02-with-edwards25519_XMD:SHA-512_ELL2_RO_",
"expand": "XMD",
"field": {
"m": "0x1",
"p": "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed"
},
"hash": "sha512",
"k": "0x80",
"map": {
"name": "ELL2"
},
"randomOracle": true,
"vectors": [
{
"P": {
"x": "0x3c3da6925a3c3c268448dcabb47ccde5439559d9599646a8260e47b1e4822fc6",
"y": "0x09a6c8561a0b22bef63124c588ce4c62ea83a3c899763af26d795302e115dc21"
},
"Q0": {
"x": "0x6549118f65bb617b9e8b438decedc73c496eaed496806d3b2eb9ee60b88e09a7",
"y": "0x7315bcc8cf47ed68048d22bad602c6680b3382a08c7c5d3f439a973fb4cf9feb"
},
"Q1": {
"x": "0x31dcfc5c58aa1bee6e760bf78cbe71c2bead8cebb2e397ece0f37a3da19c9ed2",
"y": "0x7876d81474828d8a5928b50c82420b2bd0898d819e9550c5c82c39fc9bafa196"
},
"msg": "",
"u": [
"0x03fef4813c8cb5f98c6eef88fae174e6e7d5380de2b007799ac7ee712d203f3a",
"0x780bdddd137290c8f589dc687795aafae35f6b674668d92bf92ae793e6a60c75"
]
},
{
"P": {
"x": "0x608040b42285cc0d72cbb3985c6b04c935370c7361f4b7fbdb1ae7f8c1a8ecad",
"y": "0x1a8395b88338f22e435bbd301183e7f20a5f9de643f11882fb237f88268a5531"
},
"Q0": {
"x": "0x5c1525bd5d4b4e034512949d187c39d48e8cd84242aa4758956e4adc7d445573",
"y": "0x2bf426cf7122d1a90abc7f2d108befc2ef415ce8c2d09695a7407240faa01f29"
},
"Q1": {
"x": "0x37b03bba828860c6b459ddad476c83e0f9285787a269df2156219b7e5c86210c",
"y": "0x285ebf5412f84d0ad7bb4e136729a9ffd2195d5b8e73c0dc85110ce06958f432"
},
"msg": "abc",
"u": [
"0x5081955c4141e4e7d02ec0e36becffaa1934df4d7a270f70679c78f9bd57c227",
"0x005bdc17a9b378b6272573a31b04361f21c371b256252ae5463119aa0b925b76"
]
},
{
"P": {
"x": "0x6d7fabf47a2dc03fe7d47f7dddd21082c5fb8f86743cd020f3fb147d57161472",
"y": "0x53060a3d140e7fbcda641ed3cf42c88a75411e648a1add71217f70ea8ec561a6"
},
"Q0": {
"x": "0x3ac463dd7fddb773b069c5b2b01c0f6b340638f54ee3bd92d452fcec3015b52d",
"y": "0x7b03ba1e8db9ec0b390d5c90168a6a0b7107156c994c674b61fe696cbeb46baf"
},
"Q1": {
"x": "0x0757e7e904f5e86d2d2f4acf7e01c63827fde2d363985aa7432106f1b3a444ec",
"y": "0x50026c96930a24961e9d86aa91ea1465398ff8e42015e2ec1fa397d416f6a1c0"
},
"msg": "abcdef0123456789",
"u": [
"0x285ebaa3be701b79871bcb6e225ecc9b0b32dff2d60424b4c50642636a78d5b3",
"0x2e253e6a0ef658fedb8e4bd6a62d1544fd6547922acb3598ec6b369760b81b31"
]
},
{
"P": {
"x": "0x5fb0b92acedd16f3bcb0ef83f5c7b7a9466b5f1e0d8d217421878ea3686f8524",
"y": "0x2eca15e355fcfa39d2982f67ddb0eea138e2994f5956ed37b7f72eea5e89d2f7"
},
"Q0": {
"x": "0x703e69787ea7524541933edf41f94010a201cc841c1cce60205ec38513458872",
"y": "0x32bb192c4f89106466f0874f5fd56a0d6b6f101cb714777983336c159a9bec75"
},
"Q1": {
"x": "0x0c9077c5c31720ed9413abe59bf49ce768506128d810cb882435aa90f713ef6b",
"y": "0x7d5aec5210db638c53f050597964b74d6dda4be5b54fa73041bf909ccb3826cb"
},
"msg": "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
"u": [
"0x4fedd25431c41f2a606952e2945ef5e3ac905a42cf64b8b4d4a83c533bf321af",
"0x02f20716a5801b843987097a8276b6d869295b2e11253751ca72c109d37485a9"
]
},
{
"P": {
"x": "0x0efcfde5898a839b00997fbe40d2ebe950bc81181afbd5cd6b9618aa336c1e8c",
"y": "0x6dc2fc04f266c5c27f236a80b14f92ccd051ef1ff027f26a07f8c0f327d8f995"
},
"Q0": {
"x": "0x21091b2e3f9258c7dfa075e7ae513325a94a3d8a28e1b1cb3b5b6f5d65675592",
"y": "0x41a33d324c89f570e0682cdf7bdb78852295daf8084c669f2cc9692896ab5026"
},
"Q1": {
"x": "0x4c07ec48c373e39a23bd7954f9e9b66eeab9e5ee1279b867b3d5315aa815454f",
"y": "0x67ccac7c3cb8d1381242d8d6585c57eabaddbb5dca5243a68a8aeb5477d94b3a"
},
"msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"u": [
"0x6e34e04a5106e9bd59f64aba49601bf09d23b27f7b594e56d5de06df4a4ea33b",
"0x1c1c2cb59fc053f44b86c5d5eb8c1954b64976d0302d3729ff66e84068f5fd96"
]
}
]
}

View File

@@ -0,0 +1,90 @@
{
"L": "0x54",
"Z": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffffffffffffffffffffffffffffffffffffffffffffffffffffe",
"ciphersuite": "edwards448_XOF:SHAKE256_ELL2_NU_",
"curve": "edwards448",
"dst": "QUUX-V01-CS02-with-edwards448_XOF:SHAKE256_ELL2_NU_",
"expand": "XOF",
"field": {
"m": "0x1",
"p": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
},
"hash": "shake_256",
"k": "0xe0",
"map": {
"name": "ELL2"
},
"randomOracle": false,
"vectors": [
{
"P": {
"x": "0xeb5a1fc376fd73230af2de0f3374087cc7f279f0460114cf0a6c12d6d044c16de34ec2350c34b26bf110377655ab77936869d085406af71e",
"y": "0xdf5dcea6d42e8f494b279a500d09e895d26ac703d75ca6d118e8ca58bf6f608a2a383f292fce1563ff995dce75aede1fdc8e7c0c737ae9ad"
},
"Q": {
"x": "0x4b2abf8c0fca49d027c2a81bf73bb5990e05f3e76c7ba137cc0b89415ccd55ce7f191cc0c11b0560c1cdc2a8085dd56996079e05a3cd8dde",
"y": "0x82532f5b0cb3bfb8542d3228d055bfe61129dbeae8bace80cf61f17725e8ec8226a24f0e687f78f01da88e3b2715194a03dca7c0a96bbf04"
},
"msg": "",
"u": [
"0x1368aefc0416867ea2cfc515416bcbeecc9ec81c4ecbd52ccdb91e06996b3f359bc930eef6743c7a2dd7adb785bc7093ed044efed95086d7"
]
},
{
"P": {
"x": "0x4623a64bceaba3202df76cd8b6e3daf70164f3fcbda6d6e340f7fab5cdf89140d955f722524f5fe4d968fef6ba2853ff4ea086c2f67d8110",
"y": "0xabaac321a169761a8802ab5b5d10061fec1a83c670ac6bc95954700317ee5f82870120e0e2c5a21b12a0c7ad17ebd343363604c4bcecafd1"
},
"Q": {
"x": "0xb1ca5bef2f157673a210f56c9b0039db8399e4749585abac64f831f74ed1ec5f591928976c687c06d57686bacb98440e77af878349cdf2d2",
"y": "0x5bbfd6a3730d517b03c3cd9e2eed94af12891334ec090e0495c2edc588e9e10b6f63b03a62076808cbcd6da95adfb5af76c136b2d42e0dac"
},
"msg": "abc",
"u": [
"0xcda3b0ecfe054c4077007d7300969ec24f4c741300b630ec9188ebab31a5ae0065612ee22d9f793733179ffc2e10c53ca5b539057aafdc2f"
]
},
{
"P": {
"x": "0xe9eb562e76db093baa43a31b7edd04ec4aadcef3389a7b9c58a19cf87f8ae3d154e134b6b3ed45847a741e33df51903da681629a4b8bcc2e",
"y": "0x0cf6606927ad7eb15dbc193993bc7e4dda744b311a8ec4274c8f738f74f605934582474c79260f60280fe35bd37d4347e59184cbfa12cbc4"
},
"Q": {
"x": "0x958a51e2f02e0dfd3930709010d5d16f869adb9d8a8f7c01139911d206c20cdb7bfb40ee33ba30536a99f49362fa7633d0f417fc3914fe21",
"y": "0xf4307a36ab6612fa97501497f01afa109733ce85875935551c3ca90f0fa7e0097a8640bb7e5dbcc38ab32b23b748790f2261f2c44c3bf3ba"
},
"msg": "abcdef0123456789",
"u": [
"0xd36bae98351512c382c7a3e1eba22497574f11fef9867901b1a2700b39fa2cd0d38ed4380387a99162b7ba0240c743f0532ef60d577c413d"
]
},
{
"P": {
"x": "0x122a3234d34b26c69749f23356452bf9501efa2d94859d5ef741fef024156d9d191a03a2ad24c38186f93e02d05572575968b083d8a39738",
"y": "0xddf55e74eb4414c2c1fa4aa6bc37c4ab470a3fed6bb5af1e43570309b162fb61879bb15f9ea49c712efd42d0a71666430f9f0d4a20505050"
},
"Q": {
"x": "0xe7e1f2d13548ac2c8fcd346e4c63606545bf93652011721e83ac3b64226f77a8823d3881e164bc6ca45505b236e8e3721c028052fcc9ade5",
"y": "0x7e0f340501bf25f018b9d374c2acbdd43c07261d85a6ef3c855113d4e023634db59a87b8fab9efe04ed1fee302c8a4994e83bdda32bd9c0b"
},
"msg": "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
"u": [
"0x5945744d27122f89da3daf76ab4db9616053df64e25d30ec9a00667ee6710240579c1db8f8ef3386f3f4f413cfb325ac14094d582026a971"
]
},
{
"P": {
"x": "0x221704949b1ce1ab8dd174dc9b8c56fcffa27179569ce9219c0c2fe183d3d23343a4c42a0e2e9d6b9d0feb1df3883ec489b6671d1fa64089",
"y": "0xebdecfdc87142d1a919034bf22ecfad934c9a85effff14b594ae2c00943ca62a39d6ee3be9df0bb504ce8a9e1669bc6959c42ad6a1d3b686"
},
"Q": {
"x": "0x0fd3bb833c1d7a5b319d1d4117406a23b9aece976186ecb18a11a635e6fbdb920d47e04762b1f2a8c59d2f8435d0fdefe501f544cda23dbf",
"y": "0xf13b0dad4d5eeb120f2443ac4392f8096a1396f5014ec2a3506a347fef8076a7282035cf619599b1919cf29df5ce87711c11688aab7700a6"
},
"msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"u": [
"0x1192e378043f01cedc7ea0209321519213b0184ea0d8575816bcd9182a367823e1eecc2faf1df8f79b24027a4b9bfa208cd320e79bef06ea"
]
}
]
}

View File

@@ -0,0 +1,115 @@
{
"L": "0x54",
"Z": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffffffffffffffffffffffffffffffffffffffffffffffffffffe",
"ciphersuite": "edwards448_XOF:SHAKE256_ELL2_RO_",
"curve": "edwards448",
"dst": "QUUX-V01-CS02-with-edwards448_XOF:SHAKE256_ELL2_RO_",
"expand": "XOF",
"field": {
"m": "0x1",
"p": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
},
"hash": "shake_256",
"k": "0xe0",
"map": {
"name": "ELL2"
},
"randomOracle": true,
"vectors": [
{
"P": {
"x": "0x73036d4a88949c032f01507005c133884e2f0d81f9a950826245dda9e844fc78186c39daaa7147ead3e462cff60e9c6340b58134480b4d17",
"y": "0x94c1d61b43728e5d784ef4fcb1f38e1075f3aef5e99866911de5a234f1aafdc26b554344742e6ba0420b71b298671bbeb2b7736618634610"
},
"Q0": {
"x": "0xc08177330869db17fb81a5e6e53b36d29086d806269760f2e4cabaa4015f5dbadb7ca2ba594d96a89d0ca4f0944489e1ef393d53db85096f",
"y": "0x02e894598c050eeb7195f5791f1a5f65da3776b7534be37640bcbf95d4b915bd22333c50387583507169708fbd7bea0d7aa385dcc614be9c"
},
"Q1": {
"x": "0x770877fd3b6c5503398157b68a9d3609f585f40e1ebebdd69bb0e4d3d9aa811995ce75333fdadfa50db886a35959cc59cffd5c9710daca25",
"y": "0xb27fef77aa6231fbbc27538fa90eaca8abd03eb1e62fdae4ec5e828117c3b8b3ff8c34d0a6e6d79fff16d339b94ae8ede33331d5b464c792"
},
"msg": "",
"u": [
"0x0847c5ebf957d3370b1f98fde499fb3e659996d9fc9b5707176ade785ba72cd84b8a5597c12b1024be5f510fa5ba99642c4cec7f3f69d3e7",
"0xf8cbd8a7ae8c8deed071f3ac4b93e7cfcb8f1eac1645d699fd6d3881cb295a5d3006d9449ed7cad412a77a1fe61e84a9e41d59ef384d6f9a"
]
},
{
"P": {
"x": "0x4e0158acacffa545adb818a6ed8e0b870e6abc24dfc1dc45cf9a052e98469275d9ff0c168d6a5ac7ec05b742412ee090581f12aa398f9f8c",
"y": "0x894d3fa437b2d2e28cdc3bfaade035430f350ec5239b6b406b5501da6f6d6210ff26719cad83b63e97ab26a12df6dec851d6bf38e294af9a"
},
"Q0": {
"x": "0x7544612a97f4419c94ab0f621a1ee8ccf46c6657b8e0778ec9718bf4b41bc774487ad87d9b1e617aa49d3a4dd35a3cf57cd390ebf0429952",
"y": "0xd3ab703e60267d796b485bb58a28f934bd0133a6d1bbdfeda5277fa293310be262d7f653a5adffa608c37ed45c0e6008e54a16e1a342e4df"
},
"Q1": {
"x": "0x6262f18d064bc131ade1b8bbcf1cbdf984f4f88153fcc9f94c888af35d5e41aae84c12f169a55d8abf06e6de6c5b23079e587a58cf73303e",
"y": "0x6d57589e901abe7d947c93ab02c307ad9093ed9a83eb0b6e829fb7318d590381ca25f3cc628a36a924a9ddfcf3cbedf94edf3b338ea77403"
},
"msg": "abc",
"u": [
"0x04d975cd938ab49be3e81703d6a57cca84ed80d2ff6d4756d3f22947fb5b70ab0231f0087cbfb4b7cae73b41b0c9396b356a4831d9a14322",
"0x2547ca887ac3db7b5fad3a098aa476e90078afe1358af6c63d677d6edfd2100bc004e0f5db94dd2560fc5b308e223241d00488c9ca6b0ef2"
]
},
{
"P": {
"x": "0x2c25b4503fadc94b27391933b557abdecc601c13ed51c5de68389484f93dbd6c22e5f962d9babf7a39f39f994312f8ca23344847e1fbf176",
"y": "0xd5e6f5350f430e53a110f5ac7fcc82a96cb865aeca982029522d32601e41c042a9dfbdfbefa2b0bdcdc3bc58cca8a7cd546803083d3a8548"
},
"Q0": {
"x": "0x1457b60c12e00e47ceb3ce64b57e7c3c61636475443d704a8e2b2ab0a5ac7e4b3909435416784e16e19929c653b1bdcd9478a8e5331ca9ae",
"y": "0x935d9f75f7a0babbc39c0a1c3b412518ed8a24bc2c4886722fb4b7d4a747af98e4e2528c75221e2dffd3424abb436e10539a74caaafa3ea3"
},
"Q1": {
"x": "0xb44d9e34211b4028f24117e856585ed81448f3c8b934987a1c5939c86048737a08d85934fec6b3c2ef9f09cbd365cf22744f2e4ce69762a4",
"y": "0xdc996c1736f4319868f897d9a27c45b02dd3bc6b7ca356a039606e5406e131a0bbe8238208b327b00853e8af84b58b13443e705425563323"
},
"msg": "abcdef0123456789",
"u": [
"0x10659ce25588db4e4be6f7c791a79eb21a7f24aaaca76a6ca3b83b80aaf95aa328fe7d569a1ac99f9cd216edf3915d72632f1a8b990e250c",
"0x9243e5b6c480683fd533e81f4a778349a309ce00bd163a29eb9fa8dbc8f549242bef33e030db21cffacd408d2c4264b93e476c6a8590e7aa"
]
},
{
"P": {
"x": "0xa1861a9464ae31249a0e60bf38791f3663049a3f5378998499a83292e159a2fecff838eb9bc6939e5c6ae76eb074ad4aae39b55b72ca0b9a",
"y": "0x580a2798c5b904f8adfec5bd29fb49b4633cd9f8c2935eb4a0f12e5dfa0285680880296bb729c6405337525fb5ed3dff930c137314f60401"
},
"Q0": {
"x": "0x9d355251e245e4b13ed4ea3e5a3c55bf9b7211f1704771f2e1d8f1a65610c468b1cf70c6c2ce30dcaad54ad9e5439471ec554b862ec8875a",
"y": "0x6689ba36a242af69ac2aadb955d15e982d9b04f5d77f7609ebf7429587feb7e5ce27490b9c72114509f89565122074e46a614d7fd7c800bd"
},
"Q1": {
"x": "0xc4b3d3ad4d2d62739a62989532992c1081e9474a201085b4616da5706cab824693b9fb428a201bcd1639a4588cc43b9eb841dbca74219b1f",
"y": "0x265286f5dee8f3d894b5649da8565b58e96b4cfd44b462a2883ea64dbcda21a00706ea3fea53fc2d769084b0b74589e91d0384d7118909fb"
},
"msg": "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
"u": [
"0xc80390020e578f009ead417029eff6cd0926110922db63ab98395e3bdfdd5d8a65b1a2b8d495dc8c5e59b7f3518731f7dfc0f93ace5dee4b",
"0x1c4dc6653a445bbef2add81d8e90a6c8591a788deb91d0d3f1519a2e4a460313041b77c1b0817f2e80b388e5c3e49f37d787dc1f85e4324a"
]
},
{
"P": {
"x": "0x987c5ac19dd4b47835466a50b2d9feba7c8491b8885a04edf577e15a9f2c98b203ec2cd3e5390b3d20bba0fa6fc3eecefb5029a317234401",
"y": "0x5e273fcfff6b007bb6771e90509275a71ff1480c459ded26fc7b10664db0a68aaa98bc7ecb07e49cf05b80ae5ac653fbdd14276bbd35ccbc"
},
"Q0": {
"x": "0xd1a5eba4a332514b69760948af09ceaeddbbb9fd4cb1f19b78349c2ee4cf9ee86dbcf9064659a4a0566fe9c34d90aec86f0801edc131ad9b",
"y": "0x5d0a75a3014c3269c33b1b5da80706a4f097893461df286353484d8031cd607c98edc2a846c77a841f057c7251eb45077853c7b205957e52"
},
"Q1": {
"x": "0x69583b00dc6b2aced6ffa44630cc8c8cd0dd0649f57588dd0fb1daad2ce132e281d01e3f25ccd3f405be759975c6484268bfe8f5e5f23c30",
"y": "0x8418484035f60bdccf48cb488634c2dfb40272123435f7e654fb6f254c6c42e7e38f1fa79a637a168a28de6c275232b704f9ded0ff76dd94"
},
"msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"u": [
"0x163c79ab0210a4b5e4f44fb19437ea965bf5431ab233ef16606f0b03c5f16a3feb7d46a5a675ce8f606e9c2bf74ee5336c54a1e54919f13f",
"0xf99666bde4995c4088333d6c2734687e815f80a99c6da02c47df4b51f6c9d9ed466b4fecf7d9884990a8e0d0be6907fa437e0b1a27f49265"
]
}
]
}

View File

@@ -0,0 +1,78 @@
{
"DST": "QUUX-V01-CS02-with-expander-SHA256-128-long-DST-1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111",
"hash": "SHA256",
"k": 128,
"name": "expand_message_xmd",
"tests": [
{
"DST_prime": "412717974da474d0f8c420f320ff81e8432adb7c927d9bd082b4fb4d16c0a23620",
"len_in_bytes": "0x20",
"msg": "",
"msg_prime": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000412717974da474d0f8c420f320ff81e8432adb7c927d9bd082b4fb4d16c0a23620",
"uniform_bytes": "e8dc0c8b686b7ef2074086fbdd2f30e3f8bfbd3bdf177f73f04b97ce618a3ed3"
},
{
"DST_prime": "412717974da474d0f8c420f320ff81e8432adb7c927d9bd082b4fb4d16c0a23620",
"len_in_bytes": "0x20",
"msg": "abc",
"msg_prime": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000616263002000412717974da474d0f8c420f320ff81e8432adb7c927d9bd082b4fb4d16c0a23620",
"uniform_bytes": "52dbf4f36cf560fca57dedec2ad924ee9c266341d8f3d6afe5171733b16bbb12"
},
{
"DST_prime": "412717974da474d0f8c420f320ff81e8432adb7c927d9bd082b4fb4d16c0a23620",
"len_in_bytes": "0x20",
"msg": "abcdef0123456789",
"msg_prime": "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000061626364656630313233343536373839002000412717974da474d0f8c420f320ff81e8432adb7c927d9bd082b4fb4d16c0a23620",
"uniform_bytes": "35387dcf22618f3728e6c686490f8b431f76550b0b2c61cbc1ce7001536f4521"
},
{
"DST_prime": "412717974da474d0f8c420f320ff81e8432adb7c927d9bd082b4fb4d16c0a23620",
"len_in_bytes": "0x20",
"msg": "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
"msg_prime": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000713132385f7171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171002000412717974da474d0f8c420f320ff81e8432adb7c927d9bd082b4fb4d16c0a23620",
"uniform_bytes": "01b637612bb18e840028be900a833a74414140dde0c4754c198532c3a0ba42bc"
},
{
"DST_prime": "412717974da474d0f8c420f320ff81e8432adb7c927d9bd082b4fb4d16c0a23620",
"len_in_bytes": "0x20",
"msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"msg_prime": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000613531325f6161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161002000412717974da474d0f8c420f320ff81e8432adb7c927d9bd082b4fb4d16c0a23620",
"uniform_bytes": "20cce7033cabc5460743180be6fa8aac5a103f56d481cf369a8accc0c374431b"
},
{
"DST_prime": "412717974da474d0f8c420f320ff81e8432adb7c927d9bd082b4fb4d16c0a23620",
"len_in_bytes": "0x80",
"msg": "",
"msg_prime": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000412717974da474d0f8c420f320ff81e8432adb7c927d9bd082b4fb4d16c0a23620",
"uniform_bytes": "14604d85432c68b757e485c8894db3117992fc57e0e136f71ad987f789a0abc287c47876978e2388a02af86b1e8d1342e5ce4f7aaa07a87321e691f6fba7e0072eecc1218aebb89fb14a0662322d5edbd873f0eb35260145cd4e64f748c5dfe60567e126604bcab1a3ee2dc0778102ae8a5cfd1429ebc0fa6bf1a53c36f55dfc"
},
{
"DST_prime": "412717974da474d0f8c420f320ff81e8432adb7c927d9bd082b4fb4d16c0a23620",
"len_in_bytes": "0x80",
"msg": "abc",
"msg_prime": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000616263008000412717974da474d0f8c420f320ff81e8432adb7c927d9bd082b4fb4d16c0a23620",
"uniform_bytes": "1a30a5e36fbdb87077552b9d18b9f0aee16e80181d5b951d0471d55b66684914aef87dbb3626eaabf5ded8cd0686567e503853e5c84c259ba0efc37f71c839da2129fe81afdaec7fbdc0ccd4c794727a17c0d20ff0ea55e1389d6982d1241cb8d165762dbc39fb0cee4474d2cbbd468a835ae5b2f20e4f959f56ab24cd6fe267"
},
{
"DST_prime": "412717974da474d0f8c420f320ff81e8432adb7c927d9bd082b4fb4d16c0a23620",
"len_in_bytes": "0x80",
"msg": "abcdef0123456789",
"msg_prime": "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000061626364656630313233343536373839008000412717974da474d0f8c420f320ff81e8432adb7c927d9bd082b4fb4d16c0a23620",
"uniform_bytes": "d2ecef3635d2397f34a9f86438d772db19ffe9924e28a1caf6f1c8f15603d4028f40891044e5c7e39ebb9b31339979ff33a4249206f67d4a1e7c765410bcd249ad78d407e303675918f20f26ce6d7027ed3774512ef5b00d816e51bfcc96c3539601fa48ef1c07e494bdc37054ba96ecb9dbd666417e3de289d4f424f502a982"
},
{
"DST_prime": "412717974da474d0f8c420f320ff81e8432adb7c927d9bd082b4fb4d16c0a23620",
"len_in_bytes": "0x80",
"msg": "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
"msg_prime": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000713132385f7171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171008000412717974da474d0f8c420f320ff81e8432adb7c927d9bd082b4fb4d16c0a23620",
"uniform_bytes": "ed6e8c036df90111410431431a232d41a32c86e296c05d426e5f44e75b9a50d335b2412bc6c91e0a6dc131de09c43110d9180d0a70f0d6289cb4e43b05f7ee5e9b3f42a1fad0f31bac6a625b3b5c50e3a83316783b649e5ecc9d3b1d9471cb5024b7ccf40d41d1751a04ca0356548bc6e703fca02ab521b505e8e45600508d32"
},
{
"DST_prime": "412717974da474d0f8c420f320ff81e8432adb7c927d9bd082b4fb4d16c0a23620",
"len_in_bytes": "0x80",
"msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"msg_prime": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000613531325f6161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161008000412717974da474d0f8c420f320ff81e8432adb7c927d9bd082b4fb4d16c0a23620",
"uniform_bytes": "78b53f2413f3c688f07732c10e5ced29a17c6a16f717179ffbe38d92d6c9ec296502eb9889af83a1928cd162e845b0d3c5424e83280fed3d10cffb2f8431f14e7a23f4c68819d40617589e4c41169d0b56e0e3535be1fd71fbb08bb70c5b5ffed953d6c14bf7618b35fc1f4c4b30538236b4b08c9fbf90462447a8ada60be495"
}
]
}

View File

@@ -0,0 +1,78 @@
{
"DST": "QUUX-V01-CS02-with-expander-SHA256-128",
"hash": "SHA256",
"k": 128,
"name": "expand_message_xmd",
"tests": [
{
"DST_prime": "515555582d5630312d435330322d776974682d657870616e6465722d5348413235362d31323826",
"len_in_bytes": "0x20",
"msg": "",
"msg_prime": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000515555582d5630312d435330322d776974682d657870616e6465722d5348413235362d31323826",
"uniform_bytes": "68a985b87eb6b46952128911f2a4412bbc302a9d759667f87f7a21d803f07235"
},
{
"DST_prime": "515555582d5630312d435330322d776974682d657870616e6465722d5348413235362d31323826",
"len_in_bytes": "0x20",
"msg": "abc",
"msg_prime": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000616263002000515555582d5630312d435330322d776974682d657870616e6465722d5348413235362d31323826",
"uniform_bytes": "d8ccab23b5985ccea865c6c97b6e5b8350e794e603b4b97902f53a8a0d605615"
},
{
"DST_prime": "515555582d5630312d435330322d776974682d657870616e6465722d5348413235362d31323826",
"len_in_bytes": "0x20",
"msg": "abcdef0123456789",
"msg_prime": "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000061626364656630313233343536373839002000515555582d5630312d435330322d776974682d657870616e6465722d5348413235362d31323826",
"uniform_bytes": "eff31487c770a893cfb36f912fbfcbff40d5661771ca4b2cb4eafe524333f5c1"
},
{
"DST_prime": "515555582d5630312d435330322d776974682d657870616e6465722d5348413235362d31323826",
"len_in_bytes": "0x20",
"msg": "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
"msg_prime": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000713132385f7171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171002000515555582d5630312d435330322d776974682d657870616e6465722d5348413235362d31323826",
"uniform_bytes": "b23a1d2b4d97b2ef7785562a7e8bac7eed54ed6e97e29aa51bfe3f12ddad1ff9"
},
{
"DST_prime": "515555582d5630312d435330322d776974682d657870616e6465722d5348413235362d31323826",
"len_in_bytes": "0x20",
"msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"msg_prime": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000613531325f6161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161002000515555582d5630312d435330322d776974682d657870616e6465722d5348413235362d31323826",
"uniform_bytes": "4623227bcc01293b8c130bf771da8c298dede7383243dc0993d2d94823958c4c"
},
{
"DST_prime": "515555582d5630312d435330322d776974682d657870616e6465722d5348413235362d31323826",
"len_in_bytes": "0x80",
"msg": "",
"msg_prime": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000515555582d5630312d435330322d776974682d657870616e6465722d5348413235362d31323826",
"uniform_bytes": "af84c27ccfd45d41914fdff5df25293e221afc53d8ad2ac06d5e3e29485dadbee0d121587713a3e0dd4d5e69e93eb7cd4f5df4cd103e188cf60cb02edc3edf18eda8576c412b18ffb658e3dd6ec849469b979d444cf7b26911a08e63cf31f9dcc541708d3491184472c2c29bb749d4286b004ceb5ee6b9a7fa5b646c993f0ced"
},
{
"DST_prime": "515555582d5630312d435330322d776974682d657870616e6465722d5348413235362d31323826",
"len_in_bytes": "0x80",
"msg": "abc",
"msg_prime": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000616263008000515555582d5630312d435330322d776974682d657870616e6465722d5348413235362d31323826",
"uniform_bytes": "abba86a6129e366fc877aab32fc4ffc70120d8996c88aee2fe4b32d6c7b6437a647e6c3163d40b76a73cf6a5674ef1d890f95b664ee0afa5359a5c4e07985635bbecbac65d747d3d2da7ec2b8221b17b0ca9dc8a1ac1c07ea6a1e60583e2cb00058e77b7b72a298425cd1b941ad4ec65e8afc50303a22c0f99b0509b4c895f40"
},
{
"DST_prime": "515555582d5630312d435330322d776974682d657870616e6465722d5348413235362d31323826",
"len_in_bytes": "0x80",
"msg": "abcdef0123456789",
"msg_prime": "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000061626364656630313233343536373839008000515555582d5630312d435330322d776974682d657870616e6465722d5348413235362d31323826",
"uniform_bytes": "ef904a29bffc4cf9ee82832451c946ac3c8f8058ae97d8d629831a74c6572bd9ebd0df635cd1f208e2038e760c4994984ce73f0d55ea9f22af83ba4734569d4bc95e18350f740c07eef653cbb9f87910d833751825f0ebefa1abe5420bb52be14cf489b37fe1a72f7de2d10be453b2c9d9eb20c7e3f6edc5a60629178d9478df"
},
{
"DST_prime": "515555582d5630312d435330322d776974682d657870616e6465722d5348413235362d31323826",
"len_in_bytes": "0x80",
"msg": "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
"msg_prime": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000713132385f7171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171008000515555582d5630312d435330322d776974682d657870616e6465722d5348413235362d31323826",
"uniform_bytes": "80be107d0884f0d881bb460322f0443d38bd222db8bd0b0a5312a6fedb49c1bbd88fd75d8b9a09486c60123dfa1d73c1cc3169761b17476d3c6b7cbbd727acd0e2c942f4dd96ae3da5de368d26b32286e32de7e5a8cb2949f866a0b80c58116b29fa7fabb3ea7d520ee603e0c25bcaf0b9a5e92ec6a1fe4e0391d1cdbce8c68a"
},
{
"DST_prime": "515555582d5630312d435330322d776974682d657870616e6465722d5348413235362d31323826",
"len_in_bytes": "0x80",
"msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"msg_prime": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000613531325f6161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161008000515555582d5630312d435330322d776974682d657870616e6465722d5348413235362d31323826",
"uniform_bytes": "546aff5444b5b79aa6148bd81728704c32decb73a3ba76e9e75885cad9def1d06d6792f8a7d12794e90efed817d96920d728896a4510864370c207f99bd4a608ea121700ef01ed879745ee3e4ceef777eda6d9e5e38b90c86ea6fb0b36504ba4a45d22e86f6db5dd43d98a294bebb9125d5b794e9d2a81181066eb954966a487"
}
]
}

View File

@@ -0,0 +1,78 @@
{
"DST": "QUUX-V01-CS02-with-expander-SHA512-256",
"hash": "SHA512",
"k": 256,
"name": "expand_message_xmd",
"tests": [
{
"DST_prime": "515555582d5630312d435330322d776974682d657870616e6465722d5348413531322d32353626",
"len_in_bytes": "0x20",
"msg": "",
"msg_prime": "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000515555582d5630312d435330322d776974682d657870616e6465722d5348413531322d32353626",
"uniform_bytes": "6b9a7312411d92f921c6f68ca0b6380730a1a4d982c507211a90964c394179ba"
},
{
"DST_prime": "515555582d5630312d435330322d776974682d657870616e6465722d5348413531322d32353626",
"len_in_bytes": "0x20",
"msg": "abc",
"msg_prime": "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000616263002000515555582d5630312d435330322d776974682d657870616e6465722d5348413531322d32353626",
"uniform_bytes": "0da749f12fbe5483eb066a5f595055679b976e93abe9be6f0f6318bce7aca8dc"
},
{
"DST_prime": "515555582d5630312d435330322d776974682d657870616e6465722d5348413531322d32353626",
"len_in_bytes": "0x20",
"msg": "abcdef0123456789",
"msg_prime": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000061626364656630313233343536373839002000515555582d5630312d435330322d776974682d657870616e6465722d5348413531322d32353626",
"uniform_bytes": "087e45a86e2939ee8b91100af1583c4938e0f5fc6c9db4b107b83346bc967f58"
},
{
"DST_prime": "515555582d5630312d435330322d776974682d657870616e6465722d5348413531322d32353626",
"len_in_bytes": "0x20",
"msg": "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
"msg_prime": "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000713132385f7171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171002000515555582d5630312d435330322d776974682d657870616e6465722d5348413531322d32353626",
"uniform_bytes": "7336234ee9983902440f6bc35b348352013becd88938d2afec44311caf8356b3"
},
{
"DST_prime": "515555582d5630312d435330322d776974682d657870616e6465722d5348413531322d32353626",
"len_in_bytes": "0x20",
"msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"msg_prime": "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000613531325f6161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161002000515555582d5630312d435330322d776974682d657870616e6465722d5348413531322d32353626",
"uniform_bytes": "57b5f7e766d5be68a6bfe1768e3c2b7f1228b3e4b3134956dd73a59b954c66f4"
},
{
"DST_prime": "515555582d5630312d435330322d776974682d657870616e6465722d5348413531322d32353626",
"len_in_bytes": "0x80",
"msg": "",
"msg_prime": "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000515555582d5630312d435330322d776974682d657870616e6465722d5348413531322d32353626",
"uniform_bytes": "41b037d1734a5f8df225dd8c7de38f851efdb45c372887be655212d07251b921b052b62eaed99b46f72f2ef4cc96bfaf254ebbbec091e1a3b9e4fb5e5b619d2e0c5414800a1d882b62bb5cd1778f098b8eb6cb399d5d9d18f5d5842cf5d13d7eb00a7cff859b605da678b318bd0e65ebff70bec88c753b159a805d2c89c55961"
},
{
"DST_prime": "515555582d5630312d435330322d776974682d657870616e6465722d5348413531322d32353626",
"len_in_bytes": "0x80",
"msg": "abc",
"msg_prime": "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000616263008000515555582d5630312d435330322d776974682d657870616e6465722d5348413531322d32353626",
"uniform_bytes": "7f1dddd13c08b543f2e2037b14cefb255b44c83cc397c1786d975653e36a6b11bdd7732d8b38adb4a0edc26a0cef4bb45217135456e58fbca1703cd6032cb1347ee720b87972d63fbf232587043ed2901bce7f22610c0419751c065922b488431851041310ad659e4b23520e1772ab29dcdeb2002222a363f0c2b1c972b3efe1"
},
{
"DST_prime": "515555582d5630312d435330322d776974682d657870616e6465722d5348413531322d32353626",
"len_in_bytes": "0x80",
"msg": "abcdef0123456789",
"msg_prime": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000061626364656630313233343536373839008000515555582d5630312d435330322d776974682d657870616e6465722d5348413531322d32353626",
"uniform_bytes": "3f721f208e6199fe903545abc26c837ce59ac6fa45733f1baaf0222f8b7acb0424814fcb5eecf6c1d38f06e9d0a6ccfbf85ae612ab8735dfdf9ce84c372a77c8f9e1c1e952c3a61b7567dd0693016af51d2745822663d0c2367e3f4f0bed827feecc2aaf98c949b5ed0d35c3f1023d64ad1407924288d366ea159f46287e61ac"
},
{
"DST_prime": "515555582d5630312d435330322d776974682d657870616e6465722d5348413531322d32353626",
"len_in_bytes": "0x80",
"msg": "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
"msg_prime": "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000713132385f7171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171008000515555582d5630312d435330322d776974682d657870616e6465722d5348413531322d32353626",
"uniform_bytes": "b799b045a58c8d2b4334cf54b78260b45eec544f9f2fb5bd12fb603eaee70db7317bf807c406e26373922b7b8920fa29142703dd52bdf280084fb7ef69da78afdf80b3586395b433dc66cde048a258e476a561e9deba7060af40adf30c64249ca7ddea79806ee5beb9a1422949471d267b21bc88e688e4014087a0b592b695ed"
},
{
"DST_prime": "515555582d5630312d435330322d776974682d657870616e6465722d5348413531322d32353626",
"len_in_bytes": "0x80",
"msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"msg_prime": "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000613531325f6161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161008000515555582d5630312d435330322d776974682d657870616e6465722d5348413531322d32353626",
"uniform_bytes": "05b0bfef265dcee87654372777b7c44177e2ae4c13a27f103340d9cd11c86cb2426ffcad5bd964080c2aee97f03be1ca18e30a1f14e27bc11ebbd650f305269cc9fb1db08bf90bfc79b42a952b46daf810359e7bc36452684784a64952c343c52e5124cd1f71d474d5197fefc571a92929c9084ffe1112cf5eea5192ebff330b"
}
]
}

View File

@@ -0,0 +1,78 @@
{
"DST": "QUUX-V01-CS02-with-expander-SHAKE128-long-DST-111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111",
"hash": "SHAKE128",
"k": 128,
"name": "expand_message_xof",
"tests": [
{
"DST_prime": "acb9736c0867fdfbd6385519b90fc8c034b5af04a958973212950132d035792f20",
"len_in_bytes": "0x20",
"msg": "",
"msg_prime": "0020acb9736c0867fdfbd6385519b90fc8c034b5af04a958973212950132d035792f20",
"uniform_bytes": "827c6216330a122352312bccc0c8d6e7a146c5257a776dbd9ad9d75cd880fc53"
},
{
"DST_prime": "acb9736c0867fdfbd6385519b90fc8c034b5af04a958973212950132d035792f20",
"len_in_bytes": "0x20",
"msg": "abc",
"msg_prime": "6162630020acb9736c0867fdfbd6385519b90fc8c034b5af04a958973212950132d035792f20",
"uniform_bytes": "690c8d82c7213b4282c6cb41c00e31ea1d3e2005f93ad19bbf6da40f15790c5c"
},
{
"DST_prime": "acb9736c0867fdfbd6385519b90fc8c034b5af04a958973212950132d035792f20",
"len_in_bytes": "0x20",
"msg": "abcdef0123456789",
"msg_prime": "616263646566303132333435363738390020acb9736c0867fdfbd6385519b90fc8c034b5af04a958973212950132d035792f20",
"uniform_bytes": "979e3a15064afbbcf99f62cc09fa9c85028afcf3f825eb0711894dcfc2f57057"
},
{
"DST_prime": "acb9736c0867fdfbd6385519b90fc8c034b5af04a958973212950132d035792f20",
"len_in_bytes": "0x20",
"msg": "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
"msg_prime": "713132385f71717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171710020acb9736c0867fdfbd6385519b90fc8c034b5af04a958973212950132d035792f20",
"uniform_bytes": "c5a9220962d9edc212c063f4f65b609755a1ed96e62f9db5d1fd6adb5a8dc52b"
},
{
"DST_prime": "acb9736c0867fdfbd6385519b90fc8c034b5af04a958973212950132d035792f20",
"len_in_bytes": "0x20",
"msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"msg_prime": "613531325f61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161610020acb9736c0867fdfbd6385519b90fc8c034b5af04a958973212950132d035792f20",
"uniform_bytes": "f7b96a5901af5d78ce1d071d9c383cac66a1dfadb508300ec6aeaea0d62d5d62"
},
{
"DST_prime": "acb9736c0867fdfbd6385519b90fc8c034b5af04a958973212950132d035792f20",
"len_in_bytes": "0x80",
"msg": "",
"msg_prime": "0080acb9736c0867fdfbd6385519b90fc8c034b5af04a958973212950132d035792f20",
"uniform_bytes": "3890dbab00a2830be398524b71c2713bbef5f4884ac2e6f070b092effdb19208c7df943dc5dcbaee3094a78c267ef276632ee2c8ea0c05363c94b6348500fae4208345dd3475fe0c834c2beac7fa7bc181692fb728c0a53d809fc8111495222ce0f38468b11becb15b32060218e285c57a60162c2c8bb5b6bded13973cd41819"
},
{
"DST_prime": "acb9736c0867fdfbd6385519b90fc8c034b5af04a958973212950132d035792f20",
"len_in_bytes": "0x80",
"msg": "abc",
"msg_prime": "6162630080acb9736c0867fdfbd6385519b90fc8c034b5af04a958973212950132d035792f20",
"uniform_bytes": "41b7ffa7a301b5c1441495ebb9774e2a53dbbf4e54b9a1af6a20fd41eafd69ef7b9418599c5545b1ee422f363642b01d4a53449313f68da3e49dddb9cd25b97465170537d45dcbdf92391b5bdff344db4bd06311a05bca7dcd360b6caec849c299133e5c9194f4e15e3e23cfaab4003fab776f6ac0bfae9144c6e2e1c62e7d57"
},
{
"DST_prime": "acb9736c0867fdfbd6385519b90fc8c034b5af04a958973212950132d035792f20",
"len_in_bytes": "0x80",
"msg": "abcdef0123456789",
"msg_prime": "616263646566303132333435363738390080acb9736c0867fdfbd6385519b90fc8c034b5af04a958973212950132d035792f20",
"uniform_bytes": "55317e4a21318472cd2290c3082957e1242241d9e0d04f47026f03401643131401071f01aa03038b2783e795bdfa8a3541c194ad5de7cb9c225133e24af6c86e748deb52e560569bd54ef4dac03465111a3a44b0ea490fb36777ff8ea9f1a8a3e8e0de3cf0880b4b2f8dd37d3a85a8b82375aee4fa0e909f9763319b55778e71"
},
{
"DST_prime": "acb9736c0867fdfbd6385519b90fc8c034b5af04a958973212950132d035792f20",
"len_in_bytes": "0x80",
"msg": "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
"msg_prime": "713132385f71717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171710080acb9736c0867fdfbd6385519b90fc8c034b5af04a958973212950132d035792f20",
"uniform_bytes": "19fdd2639f082e31c77717ac9bb032a22ff0958382b2dbb39020cdc78f0da43305414806abf9a561cb2d0067eb2f7bc544482f75623438ed4b4e39dd9e6e2909dd858bd8f1d57cd0fce2d3150d90aa67b4498bdf2df98c0100dd1a173436ba5d0df6be1defb0b2ce55ccd2f4fc05eb7cb2c019c35d5398b85adc676da4238bc7"
},
{
"DST_prime": "acb9736c0867fdfbd6385519b90fc8c034b5af04a958973212950132d035792f20",
"len_in_bytes": "0x80",
"msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"msg_prime": "613531325f61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161610080acb9736c0867fdfbd6385519b90fc8c034b5af04a958973212950132d035792f20",
"uniform_bytes": "945373f0b3431a103333ba6a0a34f1efab2702efde41754c4cb1d5216d5b0a92a67458d968562bde7fa6310a83f53dda1383680a276a283438d58ceebfa7ab7ba72499d4a3eddc860595f63c93b1c5e823ea41fc490d938398a26db28f61857698553e93f0574eb8c5017bfed6249491f9976aaa8d23d9485339cc85ca329308"
}
]
}

View File

@@ -0,0 +1,78 @@
{
"DST": "QUUX-V01-CS02-with-expander-SHAKE128",
"hash": "SHAKE128",
"k": 128,
"name": "expand_message_xof",
"tests": [
{
"DST_prime": "515555582d5630312d435330322d776974682d657870616e6465722d5348414b4531323824",
"len_in_bytes": "0x20",
"msg": "",
"msg_prime": "0020515555582d5630312d435330322d776974682d657870616e6465722d5348414b4531323824",
"uniform_bytes": "86518c9cd86581486e9485aa74ab35ba150d1c75c88e26b7043e44e2acd735a2"
},
{
"DST_prime": "515555582d5630312d435330322d776974682d657870616e6465722d5348414b4531323824",
"len_in_bytes": "0x20",
"msg": "abc",
"msg_prime": "6162630020515555582d5630312d435330322d776974682d657870616e6465722d5348414b4531323824",
"uniform_bytes": "8696af52a4d862417c0763556073f47bc9b9ba43c99b505305cb1ec04a9ab468"
},
{
"DST_prime": "515555582d5630312d435330322d776974682d657870616e6465722d5348414b4531323824",
"len_in_bytes": "0x20",
"msg": "abcdef0123456789",
"msg_prime": "616263646566303132333435363738390020515555582d5630312d435330322d776974682d657870616e6465722d5348414b4531323824",
"uniform_bytes": "912c58deac4821c3509dbefa094df54b34b8f5d01a191d1d3108a2c89077acca"
},
{
"DST_prime": "515555582d5630312d435330322d776974682d657870616e6465722d5348414b4531323824",
"len_in_bytes": "0x20",
"msg": "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
"msg_prime": "713132385f71717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171710020515555582d5630312d435330322d776974682d657870616e6465722d5348414b4531323824",
"uniform_bytes": "1adbcc448aef2a0cebc71dac9f756b22e51839d348e031e63b33ebb50faeaf3f"
},
{
"DST_prime": "515555582d5630312d435330322d776974682d657870616e6465722d5348414b4531323824",
"len_in_bytes": "0x20",
"msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"msg_prime": "613531325f61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161610020515555582d5630312d435330322d776974682d657870616e6465722d5348414b4531323824",
"uniform_bytes": "df3447cc5f3e9a77da10f819218ddf31342c310778e0e4ef72bbaecee786a4fe"
},
{
"DST_prime": "515555582d5630312d435330322d776974682d657870616e6465722d5348414b4531323824",
"len_in_bytes": "0x80",
"msg": "",
"msg_prime": "0080515555582d5630312d435330322d776974682d657870616e6465722d5348414b4531323824",
"uniform_bytes": "7314ff1a155a2fb99a0171dc71b89ab6e3b2b7d59e38e64419b8b6294d03ffee42491f11370261f436220ef787f8f76f5b26bdcd850071920ce023f3ac46847744f4612b8714db8f5db83205b2e625d95afd7d7b4d3094d3bdde815f52850bb41ead9822e08f22cf41d615a303b0d9dde73263c049a7b9898208003a739a2e57"
},
{
"DST_prime": "515555582d5630312d435330322d776974682d657870616e6465722d5348414b4531323824",
"len_in_bytes": "0x80",
"msg": "abc",
"msg_prime": "6162630080515555582d5630312d435330322d776974682d657870616e6465722d5348414b4531323824",
"uniform_bytes": "c952f0c8e529ca8824acc6a4cab0e782fc3648c563ddb00da7399f2ae35654f4860ec671db2356ba7baa55a34a9d7f79197b60ddae6e64768a37d699a78323496db3878c8d64d909d0f8a7de4927dcab0d3dbbc26cb20a49eceb0530b431cdf47bc8c0fa3e0d88f53b318b6739fbed7d7634974f1b5c386d6230c76260d5337a"
},
{
"DST_prime": "515555582d5630312d435330322d776974682d657870616e6465722d5348414b4531323824",
"len_in_bytes": "0x80",
"msg": "abcdef0123456789",
"msg_prime": "616263646566303132333435363738390080515555582d5630312d435330322d776974682d657870616e6465722d5348414b4531323824",
"uniform_bytes": "19b65ee7afec6ac06a144f2d6134f08eeec185f1a890fe34e68f0e377b7d0312883c048d9b8a1d6ecc3b541cb4987c26f45e0c82691ea299b5e6889bbfe589153016d8131717ba26f07c3c14ffbef1f3eff9752e5b6183f43871a78219a75e7000fbac6a7072e2b83c790a3a5aecd9d14be79f9fd4fb180960a3772e08680495"
},
{
"DST_prime": "515555582d5630312d435330322d776974682d657870616e6465722d5348414b4531323824",
"len_in_bytes": "0x80",
"msg": "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
"msg_prime": "713132385f71717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171710080515555582d5630312d435330322d776974682d657870616e6465722d5348414b4531323824",
"uniform_bytes": "ca1b56861482b16eae0f4a26212112362fcc2d76dcc80c93c4182ed66c5113fe41733ed68be2942a3487394317f3379856f4822a611735e50528a60e7ade8ec8c71670fec6661e2c59a09ed36386513221688b35dc47e3c3111ee8c67ff49579089d661caa29db1ef10eb6eace575bf3dc9806e7c4016bd50f3c0e2a6481ee6d"
},
{
"DST_prime": "515555582d5630312d435330322d776974682d657870616e6465722d5348414b4531323824",
"len_in_bytes": "0x80",
"msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"msg_prime": "613531325f61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161610080515555582d5630312d435330322d776974682d657870616e6465722d5348414b4531323824",
"uniform_bytes": "9d763a5ce58f65c91531b4100c7266d479a5d9777ba761693d052acd37d149e7ac91c796a10b919cd74a591a1e38719fb91b7203e2af31eac3bff7ead2c195af7d88b8bc0a8adf3d1e90ab9bed6ddc2b7f655dd86c730bdeaea884e73741097142c92f0e3fc1811b699ba593c7fbd81da288a29d423df831652e3a01a9374999"
}
]
}

View File

@@ -0,0 +1,78 @@
{
"DST": "QUUX-V01-CS02-with-expander-SHAKE256",
"hash": "SHAKE256",
"k": 256,
"name": "expand_message_xof",
"tests": [
{
"DST_prime": "515555582d5630312d435330322d776974682d657870616e6465722d5348414b4532353624",
"len_in_bytes": "0x20",
"msg": "",
"msg_prime": "0020515555582d5630312d435330322d776974682d657870616e6465722d5348414b4532353624",
"uniform_bytes": "2ffc05c48ed32b95d72e807f6eab9f7530dd1c2f013914c8fed38c5ccc15ad76"
},
{
"DST_prime": "515555582d5630312d435330322d776974682d657870616e6465722d5348414b4532353624",
"len_in_bytes": "0x20",
"msg": "abc",
"msg_prime": "6162630020515555582d5630312d435330322d776974682d657870616e6465722d5348414b4532353624",
"uniform_bytes": "b39e493867e2767216792abce1f2676c197c0692aed061560ead251821808e07"
},
{
"DST_prime": "515555582d5630312d435330322d776974682d657870616e6465722d5348414b4532353624",
"len_in_bytes": "0x20",
"msg": "abcdef0123456789",
"msg_prime": "616263646566303132333435363738390020515555582d5630312d435330322d776974682d657870616e6465722d5348414b4532353624",
"uniform_bytes": "245389cf44a13f0e70af8665fe5337ec2dcd138890bb7901c4ad9cfceb054b65"
},
{
"DST_prime": "515555582d5630312d435330322d776974682d657870616e6465722d5348414b4532353624",
"len_in_bytes": "0x20",
"msg": "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
"msg_prime": "713132385f71717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171710020515555582d5630312d435330322d776974682d657870616e6465722d5348414b4532353624",
"uniform_bytes": "719b3911821e6428a5ed9b8e600f2866bcf23c8f0515e52d6c6c019a03f16f0e"
},
{
"DST_prime": "515555582d5630312d435330322d776974682d657870616e6465722d5348414b4532353624",
"len_in_bytes": "0x20",
"msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"msg_prime": "613531325f61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161610020515555582d5630312d435330322d776974682d657870616e6465722d5348414b4532353624",
"uniform_bytes": "9181ead5220b1963f1b5951f35547a5ea86a820562287d6ca4723633d17ccbbc"
},
{
"DST_prime": "515555582d5630312d435330322d776974682d657870616e6465722d5348414b4532353624",
"len_in_bytes": "0x80",
"msg": "",
"msg_prime": "0080515555582d5630312d435330322d776974682d657870616e6465722d5348414b4532353624",
"uniform_bytes": "7a1361d2d7d82d79e035b8880c5a3c86c5afa719478c007d96e6c88737a3f631dd74a2c88df79a4cb5e5d9f7504957c70d669ec6bfedc31e01e2bacc4ff3fdf9b6a00b17cc18d9d72ace7d6b81c2e481b4f73f34f9a7505dccbe8f5485f3d20c5409b0310093d5d6492dea4e18aa6979c23c8ea5de01582e9689612afbb353df"
},
{
"DST_prime": "515555582d5630312d435330322d776974682d657870616e6465722d5348414b4532353624",
"len_in_bytes": "0x80",
"msg": "abc",
"msg_prime": "6162630080515555582d5630312d435330322d776974682d657870616e6465722d5348414b4532353624",
"uniform_bytes": "a54303e6b172909783353ab05ef08dd435a558c3197db0c132134649708e0b9b4e34fb99b92a9e9e28fc1f1d8860d85897a8e021e6382f3eea10577f968ff6df6c45fe624ce65ca25932f679a42a404bc3681efe03fcd45ef73bb3a8f79ba784f80f55ea8a3c367408f30381299617f50c8cf8fbb21d0f1e1d70b0131a7b6fbe"
},
{
"DST_prime": "515555582d5630312d435330322d776974682d657870616e6465722d5348414b4532353624",
"len_in_bytes": "0x80",
"msg": "abcdef0123456789",
"msg_prime": "616263646566303132333435363738390080515555582d5630312d435330322d776974682d657870616e6465722d5348414b4532353624",
"uniform_bytes": "e42e4d9538a189316e3154b821c1bafb390f78b2f010ea404e6ac063deb8c0852fcd412e098e231e43427bd2be1330bb47b4039ad57b30ae1fc94e34993b162ff4d695e42d59d9777ea18d3848d9d336c25d2acb93adcad009bcfb9cde12286df267ada283063de0bb1505565b2eb6c90e31c48798ecdc71a71756a9110ff373"
},
{
"DST_prime": "515555582d5630312d435330322d776974682d657870616e6465722d5348414b4532353624",
"len_in_bytes": "0x80",
"msg": "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
"msg_prime": "713132385f71717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171717171710080515555582d5630312d435330322d776974682d657870616e6465722d5348414b4532353624",
"uniform_bytes": "4ac054dda0a38a65d0ecf7afd3c2812300027c8789655e47aecf1ecc1a2426b17444c7482c99e5907afd9c25b991990490bb9c686f43e79b4471a23a703d4b02f23c669737a886a7ec28bddb92c3a98de63ebf878aa363a501a60055c048bea11840c4717beae7eee28c3cfa42857b3d130188571943a7bd747de831bd6444e0"
},
{
"DST_prime": "515555582d5630312d435330322d776974682d657870616e6465722d5348414b4532353624",
"len_in_bytes": "0x80",
"msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"msg_prime": "613531325f61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161610080515555582d5630312d435330322d776974682d657870616e6465722d5348414b4532353624",
"uniform_bytes": "09afc76d51c2cccbc129c2315df66c2be7295a231203b8ab2dd7f95c2772c68e500bc72e20c602abc9964663b7a03a389be128c56971ce81001a0b875e7fd17822db9d69792ddf6a23a151bf470079c518279aef3e75611f8f828994a9988f4a8a256ddb8bae161e658d5a2a09bcfe839c6396dc06ee5c8ff3c22d3b1f9deb7e"
}
]
}

View File

@@ -0,0 +1,90 @@
{
"L": "0x30",
"Z": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc24",
"ciphersuite": "secp256k1_XMD:SHA-256_SSWU_NU_",
"curve": "secp256k1",
"dst": "QUUX-V01-CS02-with-secp256k1_XMD:SHA-256_SSWU_NU_",
"expand": "XMD",
"field": {
"m": "0x1",
"p": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"
},
"hash": "sha256",
"k": "0x80",
"map": {
"name": "SSWU"
},
"randomOracle": false,
"vectors": [
{
"P": {
"x": "0xa4792346075feae77ac3b30026f99c1441b4ecf666ded19b7522cf65c4c55c5b",
"y": "0x62c59e2a6aeed1b23be5883e833912b08ba06be7f57c0e9cdc663f31639ff3a7"
},
"Q": {
"x": "0xa4792346075feae77ac3b30026f99c1441b4ecf666ded19b7522cf65c4c55c5b",
"y": "0x62c59e2a6aeed1b23be5883e833912b08ba06be7f57c0e9cdc663f31639ff3a7"
},
"msg": "",
"u": [
"0x0137fcd23bc3da962e8808f97474d097a6c8aa2881fceef4514173635872cf3b"
]
},
{
"P": {
"x": "0x3f3b5842033fff837d504bb4ce2a372bfeadbdbd84a1d2b678b6e1d7ee426b9d",
"y": "0x902910d1fef15d8ae2006fc84f2a5a7bda0e0407dc913062c3a493c4f5d876a5"
},
"Q": {
"x": "0x3f3b5842033fff837d504bb4ce2a372bfeadbdbd84a1d2b678b6e1d7ee426b9d",
"y": "0x902910d1fef15d8ae2006fc84f2a5a7bda0e0407dc913062c3a493c4f5d876a5"
},
"msg": "abc",
"u": [
"0xe03f894b4d7caf1a50d6aa45cac27412c8867a25489e32c5ddeb503229f63a2e"
]
},
{
"P": {
"x": "0x07644fa6281c694709f53bdd21bed94dab995671e4a8cd1904ec4aa50c59bfdf",
"y": "0xc79f8d1dad79b6540426922f7fbc9579c3018dafeffcd4552b1626b506c21e7b"
},
"Q": {
"x": "0x07644fa6281c694709f53bdd21bed94dab995671e4a8cd1904ec4aa50c59bfdf",
"y": "0xc79f8d1dad79b6540426922f7fbc9579c3018dafeffcd4552b1626b506c21e7b"
},
"msg": "abcdef0123456789",
"u": [
"0xe7a6525ae7069ff43498f7f508b41c57f80563c1fe4283510b322446f32af41b"
]
},
{
"P": {
"x": "0xb734f05e9b9709ab631d960fa26d669c4aeaea64ae62004b9d34f483aa9acc33",
"y": "0x03fc8a4a5a78632e2eb4d8460d69ff33c1d72574b79a35e402e801f2d0b1d6ee"
},
"Q": {
"x": "0xb734f05e9b9709ab631d960fa26d669c4aeaea64ae62004b9d34f483aa9acc33",
"y": "0x03fc8a4a5a78632e2eb4d8460d69ff33c1d72574b79a35e402e801f2d0b1d6ee"
},
"msg": "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
"u": [
"0xd97cf3d176a2f26b9614a704d7d434739d194226a706c886c5c3c39806bc323c"
]
},
{
"P": {
"x": "0x17d22b867658977b5002dbe8d0ee70a8cfddec3eec50fb93f36136070fd9fa6c",
"y": "0xe9178ff02f4dab73480f8dd590328aea99856a7b6cc8e5a6cdf289ecc2a51718"
},
"Q": {
"x": "0x17d22b867658977b5002dbe8d0ee70a8cfddec3eec50fb93f36136070fd9fa6c",
"y": "0xe9178ff02f4dab73480f8dd590328aea99856a7b6cc8e5a6cdf289ecc2a51718"
},
"msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"u": [
"0xa9ffbeee1d6e41ac33c248fb3364612ff591b502386c1bf6ac4aaf1ea51f8c3b"
]
}
]
}

View File

@@ -0,0 +1,115 @@
{
"L": "0x30",
"Z": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc24",
"ciphersuite": "secp256k1_XMD:SHA-256_SSWU_RO_",
"curve": "secp256k1",
"dst": "QUUX-V01-CS02-with-secp256k1_XMD:SHA-256_SSWU_RO_",
"expand": "XMD",
"field": {
"m": "0x1",
"p": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"
},
"hash": "sha256",
"k": "0x80",
"map": {
"name": "SSWU"
},
"randomOracle": true,
"vectors": [
{
"P": {
"x": "0xc1cae290e291aee617ebaef1be6d73861479c48b841eaba9b7b5852ddfeb1346",
"y": "0x64fa678e07ae116126f08b022a94af6de15985c996c3a91b64c406a960e51067"
},
"Q0": {
"x": "0x74519ef88b32b425a095e4ebcc84d81b64e9e2c2675340a720bb1a1857b99f1e",
"y": "0xc174fa322ab7c192e11748beed45b508e9fdb1ce046dee9c2cd3a2a86b410936"
},
"Q1": {
"x": "0x44548adb1b399263ded3510554d28b4bead34b8cf9a37b4bd0bd2ba4db87ae63",
"y": "0x96eb8e2faf05e368efe5957c6167001760233e6dd2487516b46ae725c4cce0c6"
},
"msg": "",
"u": [
"0x6b0f9910dd2ba71c78f2ee9f04d73b5f4c5f7fc773a701abea1e573cab002fb3",
"0x1ae6c212e08fe1a5937f6202f929a2cc8ef4ee5b9782db68b0d5799fd8f09e16"
]
},
{
"P": {
"x": "0x3377e01eab42db296b512293120c6cee72b6ecf9f9205760bd9ff11fb3cb2c4b",
"y": "0x7f95890f33efebd1044d382a01b1bee0900fb6116f94688d487c6c7b9c8371f6"
},
"Q0": {
"x": "0x07dd9432d426845fb19857d1b3a91722436604ccbbbadad8523b8fc38a5322d7",
"y": "0x604588ef5138cffe3277bbd590b8550bcbe0e523bbaf1bed4014a467122eb33f"
},
"Q1": {
"x": "0xe9ef9794d15d4e77dde751e06c182782046b8dac05f8491eb88764fc65321f78",
"y": "0xcb07ce53670d5314bf236ee2c871455c562dd76314aa41f012919fe8e7f717b3"
},
"msg": "abc",
"u": [
"0x128aab5d3679a1f7601e3bdf94ced1f43e491f544767e18a4873f397b08a2b61",
"0x5897b65da3b595a813d0fdcc75c895dc531be76a03518b044daaa0f2e4689e00"
]
},
{
"P": {
"x": "0xbac54083f293f1fe08e4a70137260aa90783a5cb84d3f35848b324d0674b0e3a",
"y": "0x4436476085d4c3c4508b60fcf4389c40176adce756b398bdee27bca19758d828"
},
"Q0": {
"x": "0x576d43ab0260275adf11af990d130a5752704f79478628761720808862544b5d",
"y": "0x643c4a7fb68ae6cff55edd66b809087434bbaff0c07f3f9ec4d49bb3c16623c3"
},
"Q1": {
"x": "0xf89d6d261a5e00fe5cf45e827b507643e67c2a947a20fd9ad71039f8b0e29ff8",
"y": "0xb33855e0cc34a9176ead91c6c3acb1aacb1ce936d563bc1cee1dcffc806caf57"
},
"msg": "abcdef0123456789",
"u": [
"0xea67a7c02f2cd5d8b87715c169d055a22520f74daeb080e6180958380e2f98b9",
"0x7434d0d1a500d38380d1f9615c021857ac8d546925f5f2355319d823a478da18"
]
},
{
"P": {
"x": "0xe2167bc785333a37aa562f021f1e881defb853839babf52a7f72b102e41890e9",
"y": "0xf2401dd95cc35867ffed4f367cd564763719fbc6a53e969fb8496a1e6685d873"
},
"Q0": {
"x": "0x9c91513ccfe9520c9c645588dff5f9b4e92eaf6ad4ab6f1cd720d192eb58247a",
"y": "0xc7371dcd0134412f221e386f8d68f49e7fa36f9037676e163d4a063fbf8a1fb8"
},
"Q1": {
"x": "0x10fee3284d7be6bd5912503b972fc52bf4761f47141a0015f1c6ae36848d869b",
"y": "0x0b163d9b4bf21887364332be3eff3c870fa053cf508732900fc69a6eb0e1b672"
},
"msg": "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
"u": [
"0xeda89a5024fac0a8207a87e8cc4e85aa3bce10745d501a30deb87341b05bcdf5",
"0xdfe78cd116818fc2c16f3837fedbe2639fab012c407eac9dfe9245bf650ac51d"
]
},
{
"P": {
"x": "0xe3c8d35aaaf0b9b647e88a0a0a7ee5d5bed5ad38238152e4e6fd8c1f8cb7c998",
"y": "0x8446eeb6181bf12f56a9d24e262221cc2f0c4725c7e3803024b5888ee5823aa6"
},
"Q0": {
"x": "0xb32b0ab55977b936f1e93fdc68cec775e13245e161dbfe556bbb1f72799b4181",
"y": "0x2f5317098360b722f132d7156a94822641b615c91f8663be69169870a12af9e8"
},
"Q1": {
"x": "0x148f98780f19388b9fa93e7dc567b5a673e5fca7079cd9cdafd71982ec4c5e12",
"y": "0x3989645d83a433bc0c001f3dac29af861f33a6fd1e04f4b36873f5bff497298a"
},
"msg": "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"u": [
"0x8d862e7e7e23d7843fe16d811d46d7e6480127a6b78838c277bca17df6900e9f",
"0x68071d2530f040f081ba818d3c7188a94c900586761e9115efa47ae9bd847938"
]
}
]
}

View File

@@ -6,7 +6,9 @@ import './nist.test.js';
import './ed448.test.js'; import './ed448.test.js';
import './ed25519.test.js'; import './ed25519.test.js';
import './secp256k1.test.js'; import './secp256k1.test.js';
import './stark/stark.test.js'; import './stark/index.test.js';
import './jubjub.test.js'; import './jubjub.test.js';
import './bls12-381.test.js';
import './hash-to-curve.test.js';
should.run(); should.run();

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