Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2bd5e9ac16 | ||
|
|
6890c26091 | ||
|
|
a15e3a93a9 | ||
|
|
910c508da9 | ||
|
|
12da04a2bb | ||
|
|
cc2c84f040 | ||
|
|
5d42549acc |
71
README.md
71
README.md
@@ -7,8 +7,8 @@ Minimal, auditable JS implementation of elliptic curve cryptography.
|
||||
- [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
|
||||
- Auditable, [fast](#speed)
|
||||
- 🔻 Tree-shaking-friendly: there is no entry point, which ensures small size of your app
|
||||
- 🔍 Unique tests ensure correctness. Wycheproof vectors included
|
||||
- 🔻 Tree-shaking-friendly: there is no entry point, which ensures small size of your app
|
||||
|
||||
There are two parts of the package:
|
||||
|
||||
@@ -84,11 +84,12 @@ To define a custom curve, check out API below.
|
||||
## API
|
||||
|
||||
- [Overview](#overview)
|
||||
- [abstract/edwards: Twisted Edwards curve](#abstract/edwards-twisted-edwards-curve)
|
||||
- [abstract/montgomery: Montgomery curve](#abstract/montgomery-montgomery-curve)
|
||||
- [abstract/weierstrass: Short Weierstrass curve](#abstract/weierstrass-short-weierstrass-curve)
|
||||
- [abstract/modular](#abstract/modular)
|
||||
- [abstract/utils](#abstract/utils)
|
||||
- [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/modular](#abstractmodular)
|
||||
- [abstract/utils](#abstractutils)
|
||||
|
||||
### Overview
|
||||
|
||||
@@ -324,20 +325,70 @@ export type CurveFn = {
|
||||
};
|
||||
```
|
||||
|
||||
### 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/modular
|
||||
|
||||
Modular arithmetics utilities.
|
||||
|
||||
```typescript
|
||||
import { mod, invert, div, invertBatch, sqrt, Fp } from '@noble/curves/abstract/modular';
|
||||
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
|
||||
const fp = Fp(2n ** 255n - 19n); // Finite field over 2^255-19
|
||||
fp.mul(591n, 932n);
|
||||
fp.pow(481n, 11024858120n);
|
||||
```
|
||||
|
||||
### abstract/utils
|
||||
|
||||
@@ -96,6 +96,10 @@ export const CURVES = {
|
||||
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: () => {
|
||||
@@ -124,6 +128,10 @@ export const CURVES = {
|
||||
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: () => {
|
||||
@@ -145,6 +153,10 @@ export const CURVES = {
|
||||
samples: 500,
|
||||
noble: ({ sig, msg, pub }) => ed448.verify(sig, msg, pub),
|
||||
},
|
||||
hashToCurve: {
|
||||
samples: 500,
|
||||
noble: () => ed448.Point.hashToCurve('abcd'),
|
||||
},
|
||||
},
|
||||
nist: {
|
||||
data: () => {
|
||||
@@ -168,6 +180,12 @@ export const CURVES = {
|
||||
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: () => {
|
||||
|
||||
@@ -12,13 +12,15 @@
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"micro-bmark": "0.2.0"
|
||||
"micro-bmark": "0.2.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@noble/bls12-381": "^1.4.0",
|
||||
"@noble/ed25519": "^1.7.1",
|
||||
"@noble/hashes": "^1.1.5",
|
||||
"@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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@noble/curves",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"description": "Minimal, auditable JS implementation of elliptic curve cryptography",
|
||||
"files": [
|
||||
"lib"
|
||||
|
||||
@@ -17,10 +17,11 @@ export type htfOpts = {
|
||||
k: number;
|
||||
// option to use a message that has already been processed by
|
||||
// expand_message_xmd
|
||||
expand: boolean;
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -29,7 +30,8 @@ export function validateHTFOpts(opts: htfOpts) {
|
||||
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 (typeof opts.expand !== 'boolean') throw new Error('Invalid htf/expand');
|
||||
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');
|
||||
}
|
||||
@@ -101,13 +103,40 @@ export function expand_message_xmd(
|
||||
return pseudo_random_bytes.slice(0, lenInBytes);
|
||||
}
|
||||
|
||||
// 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
|
||||
// Inputs:
|
||||
// msg - a byte string containing the message to hash.
|
||||
// count - the number of elements of F to output.
|
||||
// Outputs:
|
||||
// [u_0, ..., u_(count - 1)], a list of field elements.
|
||||
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: htfOpts): bigint[][] {
|
||||
// if options is provided but incomplete, fill any missing fields with the
|
||||
// value in hftDefaults (ie hash to G2).
|
||||
@@ -116,8 +145,10 @@ export function hash_to_field(msg: Uint8Array, count: number, options: htfOpts):
|
||||
const len_in_bytes = count * options.m * L;
|
||||
const DST = stringToBytes(options.DST);
|
||||
let pseudo_random_bytes = msg;
|
||||
if (options.expand) {
|
||||
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++) {
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
// TODO: remove circular imports
|
||||
import * as utils from './utils.js';
|
||||
// Utilities for modular arithmetics and finite fields
|
||||
// prettier-ignore
|
||||
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3);
|
||||
// prettier-ignore
|
||||
const _4n = BigInt(4), _5n = BigInt(5), _7n = BigInt(7), _8n = BigInt(8);
|
||||
const _4n = BigInt(4), _5n = BigInt(5), _8n = BigInt(8);
|
||||
// prettier-ignore
|
||||
const _9n = BigInt(9), _16n = BigInt(16);
|
||||
|
||||
@@ -66,26 +67,68 @@ export function invert(number: bigint, modulo: bigint): bigint {
|
||||
return mod(x, modulo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates 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)
|
||||
*/
|
||||
export function legendre(num: bigint, fieldPrime: bigint): bigint {
|
||||
return pow(num, (fieldPrime - _1n) / _2n, fieldPrime);
|
||||
// Tonelli-Shanks algorithm
|
||||
// https://eprint.iacr.org/2012/685.pdf (page 12)
|
||||
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 = q2s 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.equals(Fp.square(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) must be ≡ 1
|
||||
if (Fp.pow(n, legendreC) !== Fp.ONE) throw new Error('Cannot find square root');
|
||||
let s = S;
|
||||
|
||||
let c = pow(Z, Q, P);
|
||||
let r = Fp.pow(n, Q1div2);
|
||||
let t = Fp.pow(n, Q);
|
||||
|
||||
let t2 = Fp.ZERO;
|
||||
while (!Fp.equals(Fp.sub(t, Fp.ONE), Fp.ZERO)) {
|
||||
t2 = Fp.square(t);
|
||||
let i;
|
||||
for (i = 1; i < s; i++) {
|
||||
// stop if t2-1 == 0
|
||||
if (Fp.equals(Fp.sub(t2, Fp.ONE), Fp.ZERO)) break;
|
||||
// t2 *= t2
|
||||
t2 = Fp.square(t2);
|
||||
}
|
||||
let b = pow(c, BigInt(1 << (s - i - 1)), P);
|
||||
r = Fp.mul(r, b);
|
||||
c = mod(b * b, P);
|
||||
t = Fp.mul(t, c);
|
||||
s = i;
|
||||
}
|
||||
return r;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates square root of a number in a finite field.
|
||||
* √a mod P
|
||||
*/
|
||||
// TODO: rewrite as generic Fp function && remove bls versions
|
||||
export function sqrt(number: bigint, modulo: bigint): bigint {
|
||||
// prettier-ignore
|
||||
const n = number;
|
||||
const P = modulo;
|
||||
const p1div4 = (P + _1n) / _4n;
|
||||
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)
|
||||
@@ -94,48 +137,54 @@ export function sqrt(number: bigint, modulo: bigint): bigint {
|
||||
// const ORDER =
|
||||
// 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaabn;
|
||||
// const NUM = 72057594037927816n;
|
||||
// TODO: fix sqrtMod in secp256k1
|
||||
const root = pow(n, p1div4, P);
|
||||
if (mod(root * root, modulo) !== number) throw new Error('Cannot find square root');
|
||||
return root;
|
||||
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.equals(Fp.square(root), n)) throw new Error('Cannot find square root');
|
||||
return root;
|
||||
};
|
||||
}
|
||||
|
||||
// P ≡ 5 (mod 8)
|
||||
// Atkin algorithm for q ≡ 5 (mod 8), https://eprint.iacr.org/2012/685.pdf (page 10)
|
||||
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;
|
||||
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.equals(Fp.square(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
|
||||
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;
|
||||
return tonelliShanks(P);
|
||||
}
|
||||
|
||||
// Little-endian check for first LE bit (last BE bit);
|
||||
@@ -176,6 +225,7 @@ export interface Field<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;
|
||||
@@ -246,21 +296,31 @@ 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.invert(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.equals(p, f.ZERO) || f.equals(p, f.ONE);
|
||||
};
|
||||
}
|
||||
|
||||
// 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<Field<bigint>> {
|
||||
): Readonly<FpField> {
|
||||
if (ORDER <= _0n) throw new Error(`Expected Fp ORDER > 0, got ${ORDER}`);
|
||||
const { nBitLength: BITS, nByteLength: BYTES } = utils.nLength(ORDER, bitLen);
|
||||
if (BYTES > 2048) throw new Error('Field lengths over 2048 bytes are not supported');
|
||||
const sqrtP = (num: bigint) => sqrt(num, ORDER);
|
||||
const f: Field<bigint> = Object.freeze({
|
||||
const sqrtP = FpSqrt(ORDER);
|
||||
const f: Readonly<FpField> = Object.freeze({
|
||||
ORDER,
|
||||
BITS,
|
||||
BYTES,
|
||||
@@ -292,7 +352,7 @@ export function Fp(
|
||||
mulN: (lhs, rhs) => lhs * rhs,
|
||||
|
||||
invert: (num) => invert(num, ORDER),
|
||||
sqrt: redef.sqrt || sqrtP,
|
||||
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
|
||||
@@ -305,87 +365,18 @@ export function Fp(
|
||||
throw new Error(`Fp.fromBytes: expected ${BYTES}, got ${bytes.length}`);
|
||||
return isLE ? utils.bytesToNumberLE(bytes) : utils.bytesToNumberBE(bytes);
|
||||
},
|
||||
} as Field<bigint>);
|
||||
} as FpField);
|
||||
return Object.freeze(f);
|
||||
}
|
||||
|
||||
// TODO: re-use in bls/generic sqrt for field/etc?
|
||||
// Something like sqrtUnsafe which always returns value, but sqrt throws exception if non-square
|
||||
// From draft-irtf-cfrg-hash-to-curve-16
|
||||
export function FpSqrt<T>(Fp: Field<T>) {
|
||||
// NOTE: it requires another sqrt for constant precomputes, but no need for roots of unity,
|
||||
// probably we can simply bls code using it
|
||||
const q = Fp.ORDER;
|
||||
const squareConst = (q - _1n) / _2n;
|
||||
// is_square(x) := { True, if x^((q - 1) / 2) is 0 or 1 in F;
|
||||
// { False, otherwise.
|
||||
let isSquare: (x: T) => boolean = (x) => {
|
||||
const p = Fp.pow(x, squareConst);
|
||||
return Fp.equals(p, Fp.ZERO) || Fp.equals(p, Fp.ONE);
|
||||
};
|
||||
// Constant-time Tonelli-Shanks algorithm
|
||||
let l = _0n;
|
||||
for (let o = q - _1n; o % _2n === _0n; o /= _2n) l += _1n;
|
||||
const c1 = l; // 1. c1, the largest integer such that 2^c1 divides q - 1.
|
||||
const c2 = (q - _1n) / _2n ** c1; // 2. c2 = (q - 1) / (2^c1) # Integer arithmetic
|
||||
const c3 = (c2 - _1n) / _2n; // 3. c3 = (c2 - 1) / 2 # Integer arithmetic
|
||||
// 4. c4, a non-square value in F
|
||||
// 5. c5 = c4^c2 in F
|
||||
let c4 = Fp.ONE;
|
||||
while (isSquare(c4)) c4 = Fp.add(c4, Fp.ONE);
|
||||
const c5 = Fp.pow(c4, c2);
|
||||
|
||||
let sqrt: (x: T) => T = (x) => {
|
||||
let z = Fp.pow(x, c3); // 1. z = x^c3
|
||||
let t = Fp.square(z); // 2. t = z * z
|
||||
t = Fp.mul(t, x); // 3. t = t * x
|
||||
z = Fp.mul(z, x); // 4. z = z * x
|
||||
let b = t; // 5. b = t
|
||||
let c = c5; // 6. c = c5
|
||||
// 7. for i in (c1, c1 - 1, ..., 2):
|
||||
for (let i = c1; i > 1; i--) {
|
||||
// 8. for j in (1, 2, ..., i - 2):
|
||||
// 9. b = b * b
|
||||
for (let j = _1n; j < i - _1n; i++) b = Fp.square(b);
|
||||
const e = Fp.equals(b, Fp.ONE); // 10. e = b == 1
|
||||
const zt = Fp.mul(z, c); // 11. zt = z * c
|
||||
z = Fp.cmov(zt, z, e); // 12. z = CMOV(zt, z, e)
|
||||
c = Fp.square(c); // 13. c = c * c
|
||||
let tt = Fp.mul(t, c); // 14. tt = t * c
|
||||
t = Fp.cmov(tt, t, e); // 15. t = CMOV(tt, t, e)
|
||||
b = t; // 16. b = t
|
||||
}
|
||||
return z; // 17. return z
|
||||
};
|
||||
if (q % _4n === _3n) {
|
||||
const c1 = (q + _1n) / _4n; // 1. c1 = (q + 1) / 4 # Integer arithmetic
|
||||
sqrt = (x) => Fp.pow(x, c1);
|
||||
} else if (q % _8n === _5n) {
|
||||
const c1 = Fp.sqrt(Fp.negate(Fp.ONE)); // 1. c1 = sqrt(-1) in F, i.e., (c1^2) == -1 in F
|
||||
const c2 = (q + _3n) / _8n; // 2. c2 = (q + 3) / 8 # Integer arithmetic
|
||||
sqrt = (x) => {
|
||||
let tv1 = Fp.pow(x, c2); // 1. tv1 = x^c2
|
||||
let tv2 = Fp.mul(tv1, c1); // 2. tv2 = tv1 * c1
|
||||
let e = Fp.equals(Fp.square(tv1), x); // 3. e = (tv1^2) == x
|
||||
return Fp.cmov(tv2, tv1, e); // 4. z = CMOV(tv2, tv1, e)
|
||||
};
|
||||
} else if (Fp.ORDER % _16n === _9n) {
|
||||
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 = (Fp.ORDER + _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
|
||||
};
|
||||
}
|
||||
return { sqrt, isSquare };
|
||||
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.negate(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.negate(root) : root;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ export type CHash = {
|
||||
(message: Uint8Array | string): Uint8Array;
|
||||
blockLen: number;
|
||||
outputLen: number;
|
||||
create(): any;
|
||||
create(opts?: { dkLen?: number }): any; // For shake
|
||||
};
|
||||
|
||||
// NOTE: these are generic, even if curve is on some polynominal field (bls), it will still have P/n/h
|
||||
|
||||
@@ -926,12 +926,12 @@ const htfDefaults = {
|
||||
k: 128,
|
||||
// option to use a message that has already been processed by
|
||||
// expand_message_xmd
|
||||
expand: true,
|
||||
expand: 'xmd',
|
||||
// Hash functions for: expand_message_xmd is appropriate for use with a
|
||||
// wide range of hash functions, including SHA-2, SHA-3, BLAKE2, and others.
|
||||
// BBS+ uses blake2: https://github.com/hyperledger/aries-framework-go/issues/2247
|
||||
hash: sha256,
|
||||
};
|
||||
} as const;
|
||||
|
||||
// Encoding utils
|
||||
// Point on G1 curve: (x, y)
|
||||
|
||||
@@ -3,7 +3,7 @@ import { sha512 } from '@noble/hashes/sha512';
|
||||
import { concatBytes, randomBytes, utf8ToBytes } from '@noble/hashes/utils';
|
||||
import { twistedEdwards, ExtendedPointType } from './abstract/edwards.js';
|
||||
import { montgomery } from './abstract/montgomery.js';
|
||||
import { mod, pow2, isNegativeLE, Fp as Field } from './abstract/modular.js';
|
||||
import { mod, pow2, isNegativeLE, Fp as Field, FpSqrtEven } from './abstract/modular.js';
|
||||
import {
|
||||
ensureBytes,
|
||||
equalBytes,
|
||||
@@ -91,7 +91,80 @@ export const ED25519_TORSION_SUBGROUP = [
|
||||
'c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa',
|
||||
];
|
||||
|
||||
const Fp = Field(ED25519_P);
|
||||
const Fp = Field(ED25519_P, undefined, true);
|
||||
|
||||
// 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.negate(Fp.ONE)); // 3. c3 = sqrt(-1)
|
||||
const ELL2_C4 = (Fp.ORDER - BigInt(5)) / BigInt(8); // 4. c4 = (q - 5) / 8 # Integer arithmetic
|
||||
const ELL2_J = BigInt(486662);
|
||||
|
||||
// prettier-ignore
|
||||
function map_to_curve_elligator2_curve25519(u: bigint) {
|
||||
let tv1 = Fp.square(u); // 1. tv1 = u^2
|
||||
tv1 = Fp.mul(tv1, _2n); // 2. tv1 = 2 * tv1
|
||||
let xd = Fp.add(tv1, Fp.ONE); // 3. xd = tv1 + 1 # Nonzero: -1 is square (mod p), tv1 is not
|
||||
let x1n = Fp.negate(ELL2_J); // 4. x1n = -J # x1 = x1n / xd = -J / (1 + 2 * u^2)
|
||||
let tv2 = Fp.square(xd); // 5. tv2 = xd^2
|
||||
let gxd = Fp.mul(tv2, xd); // 6. gxd = tv2 * xd # gxd = xd^3
|
||||
let gx1 = Fp.mul(tv1, ELL2_J); // 7. gx1 = J * tv1 # x1n + J * xd
|
||||
gx1 = Fp.mul(gx1, x1n); // 8. gx1 = gx1 * x1n # x1n^2 + J * x1n * xd
|
||||
gx1 = Fp.add(gx1, tv2); // 9. gx1 = gx1 + tv2 # x1n^2 + J * x1n * xd + xd^2
|
||||
gx1 = Fp.mul(gx1, x1n); // 10. gx1 = gx1 * x1n # x1n^3 + J * x1n^2 * xd + x1n * xd^2
|
||||
let tv3 = Fp.square(gxd); // 11. tv3 = gxd^2
|
||||
tv2 = Fp.square(tv3); // 12. tv2 = tv3^2 # gxd^4
|
||||
tv3 = Fp.mul(tv3, gxd); // 13. tv3 = tv3 * gxd # gxd^3
|
||||
tv3 = Fp.mul(tv3, gx1); // 14. tv3 = tv3 * gx1 # gx1 * gxd^3
|
||||
tv2 = Fp.mul(tv2, tv3); // 15. tv2 = tv2 * tv3 # gx1 * gxd^7
|
||||
let y11 = Fp.pow(tv2, ELL2_C4); // 16. y11 = tv2^c4 # (gx1 * gxd^7)^((p - 5) / 8)
|
||||
y11 = Fp.mul(y11, tv3); // 17. y11 = y11 * tv3 # gx1*gxd^3*(gx1*gxd^7)^((p-5)/8)
|
||||
let y12 = Fp.mul(y11, ELL2_C3); // 18. y12 = y11 * c3
|
||||
tv2 = Fp.square(y11); // 19. tv2 = y11^2
|
||||
tv2 = Fp.mul(tv2, gxd); // 20. tv2 = tv2 * gxd
|
||||
let e1 = Fp.equals(tv2, gx1); // 21. e1 = tv2 == gx1
|
||||
let y1 = Fp.cmov(y12, y11, e1); // 22. y1 = CMOV(y12, y11, e1) # If g(x1) is square, this is its sqrt
|
||||
let x2n = Fp.mul(x1n, tv1); // 23. x2n = x1n * tv1 # x2 = x2n / xd = 2 * u^2 * x1n / xd
|
||||
let y21 = Fp.mul(y11, u); // 24. y21 = y11 * u
|
||||
y21 = Fp.mul(y21, ELL2_C2); // 25. y21 = y21 * c2
|
||||
let y22 = Fp.mul(y21, ELL2_C3); // 26. y22 = y21 * c3
|
||||
let gx2 = Fp.mul(gx1, tv1); // 27. gx2 = gx1 * tv1 # g(x2) = gx2 / gxd = 2 * u^2 * g(x1)
|
||||
tv2 = Fp.square(y21); // 28. tv2 = y21^2
|
||||
tv2 = Fp.mul(tv2, gxd); // 29. tv2 = tv2 * gxd
|
||||
let e2 = Fp.equals(tv2, gx2); // 30. e2 = tv2 == gx2
|
||||
let y2 = Fp.cmov(y22, y21, e2); // 31. y2 = CMOV(y22, y21, e2) # If g(x2) is square, this is its sqrt
|
||||
tv2 = Fp.square(y1); // 32. tv2 = y1^2
|
||||
tv2 = Fp.mul(tv2, gxd); // 33. tv2 = tv2 * gxd
|
||||
let e3 = Fp.equals(tv2, gx1); // 34. e3 = tv2 == gx1
|
||||
let xn = Fp.cmov(x2n, x1n, e3); // 35. xn = CMOV(x2n, x1n, e3) # If e3, x = x1, else x = x2
|
||||
let y = Fp.cmov(y2, y1, e3); // 36. y = CMOV(y2, y1, e3) # If e3, y = y1, else y = y2
|
||||
let e4 = Fp.isOdd(y); // 37. e4 = sgn0(y) == 1 # Fix sign of y
|
||||
y = Fp.cmov(y, Fp.negate(y), e3 !== e4); // 38. y = CMOV(y, -y, e3 XOR e4)
|
||||
return { xMn: xn, xMd: xd, yMn: y, yMd: 1n }; // 39. return (xn, xd, y, 1)
|
||||
}
|
||||
|
||||
const ELL2_C1_EDWARDS = FpSqrtEven(Fp, Fp.negate(BigInt(486664))); // sgn0(c1) MUST equal 0
|
||||
function map_to_curve_elligator2_edwards25519(u: bigint) {
|
||||
const { xMn, xMd, yMn, yMd } = map_to_curve_elligator2_curve25519(u); // 1. (xMn, xMd, yMn, yMd) = map_to_curve_elligator2_curve25519(u)
|
||||
let xn = Fp.mul(xMn, yMd); // 2. xn = xMn * yMd
|
||||
xn = Fp.mul(xn, ELL2_C1_EDWARDS); // 3. xn = xn * c1
|
||||
let xd = Fp.mul(xMd, yMn); // 4. xd = xMd * yMn # xn / xd = c1 * xM / yM
|
||||
let yn = Fp.sub(xMn, xMd); // 5. yn = xMn - xMd
|
||||
let yd = Fp.add(xMn, xMd); // 6. yd = xMn + xMd # (n / d - 1) / (n / d + 1) = (n - d) / (n + d)
|
||||
let tv1 = Fp.mul(xd, yd); // 7. tv1 = xd * yd
|
||||
let e = Fp.equals(tv1, Fp.ZERO); // 8. e = tv1 == 0
|
||||
xn = Fp.cmov(xn, Fp.ZERO, e); // 9. xn = CMOV(xn, 0, e)
|
||||
xd = Fp.cmov(xd, Fp.ONE, e); // 10. xd = CMOV(xd, 1, e)
|
||||
yn = Fp.cmov(yn, Fp.ONE, e); // 11. yn = CMOV(yn, 1, e)
|
||||
yd = Fp.cmov(yd, Fp.ONE, e); // 12. yd = CMOV(yd, 1, e)
|
||||
|
||||
const inv = Fp.invertBatch([xd, yd]); // batch division
|
||||
return { x: Fp.mul(xn, inv[0]), y: Fp.mul(yn, inv[1]) }; // 13. return (xn, xd, yn, yd)
|
||||
}
|
||||
|
||||
const ED25519_DEF = {
|
||||
// Param: a
|
||||
@@ -121,14 +194,10 @@ const ED25519_DEF = {
|
||||
p: Fp.ORDER,
|
||||
m: 1,
|
||||
k: 128,
|
||||
expand: true,
|
||||
expand: 'xmd',
|
||||
hash: sha512,
|
||||
},
|
||||
mapToCurve: (scalars: bigint[]): { x: bigint; y: bigint } => {
|
||||
throw new Error('Not supported yet');
|
||||
// const { x, y } = calcElligatorRistrettoMap(scalars[0]).toAffine();
|
||||
// return { x, y };
|
||||
},
|
||||
mapToCurve: (scalars: bigint[]) => map_to_curve_elligator2_edwards25519(scalars[0]),
|
||||
} as const;
|
||||
|
||||
export const ed25519 = twistedEdwards(ED25519_DEF);
|
||||
|
||||
90
src/ed448.ts
90
src/ed448.ts
@@ -2,7 +2,7 @@
|
||||
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 } from './abstract/modular.js';
|
||||
import { mod, pow2, Fp as Field } from './abstract/modular.js';
|
||||
import { montgomery } from './abstract/montgomery.js';
|
||||
|
||||
/**
|
||||
@@ -52,6 +52,83 @@ function adjustScalarBytes(bytes: Uint8Array): Uint8Array {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
const Fp = Field(ed448P, 456, true);
|
||||
|
||||
// Hash To Curve Elligator2 Map
|
||||
const ELL2_C1 = (Fp.ORDER - BigInt(3)) / BigInt(4); // 1. c1 = (q - 3) / 4 # Integer arithmetic
|
||||
const ELL2_J = BigInt(156326);
|
||||
function map_to_curve_elligator2_curve448(u: bigint) {
|
||||
let tv1 = Fp.square(u); // 1. tv1 = u^2
|
||||
let e1 = Fp.equals(tv1, Fp.ONE); // 2. e1 = tv1 == 1
|
||||
tv1 = Fp.cmov(tv1, Fp.ZERO, e1); // 3. tv1 = CMOV(tv1, 0, e1) # If Z * u^2 == -1, set tv1 = 0
|
||||
let xd = Fp.sub(Fp.ONE, tv1); // 4. xd = 1 - tv1
|
||||
let x1n = Fp.negate(ELL2_J); // 5. x1n = -J
|
||||
let tv2 = Fp.square(xd); // 6. tv2 = xd^2
|
||||
let gxd = Fp.mul(tv2, xd); // 7. gxd = tv2 * xd # gxd = xd^3
|
||||
let gx1 = Fp.mul(tv1, Fp.negate(ELL2_J)); // 8. gx1 = -J * tv1 # x1n + J * xd
|
||||
gx1 = Fp.mul(gx1, x1n); // 9. gx1 = gx1 * x1n # x1n^2 + J * x1n * xd
|
||||
gx1 = Fp.add(gx1, tv2); // 10. gx1 = gx1 + tv2 # x1n^2 + J * x1n * xd + xd^2
|
||||
gx1 = Fp.mul(gx1, x1n); // 11. gx1 = gx1 * x1n # x1n^3 + J * x1n^2 * xd + x1n * xd^2
|
||||
let tv3 = Fp.square(gxd); // 12. tv3 = gxd^2
|
||||
tv2 = Fp.mul(gx1, gxd); // 13. tv2 = gx1 * gxd # gx1 * gxd
|
||||
tv3 = Fp.mul(tv3, tv2); // 14. tv3 = tv3 * tv2 # gx1 * gxd^3
|
||||
let y1 = Fp.pow(tv3, ELL2_C1); // 15. y1 = tv3^c1 # (gx1 * gxd^3)^((p - 3) / 4)
|
||||
y1 = Fp.mul(y1, tv2); // 16. y1 = y1 * tv2 # gx1 * gxd * (gx1 * gxd^3)^((p - 3) / 4)
|
||||
let x2n = Fp.mul(x1n, Fp.negate(tv1)); // 17. x2n = -tv1 * x1n # x2 = x2n / xd = -1 * u^2 * x1n / xd
|
||||
let y2 = Fp.mul(y1, u); // 18. y2 = y1 * u
|
||||
y2 = Fp.cmov(y2, Fp.ZERO, e1); // 19. y2 = CMOV(y2, 0, e1)
|
||||
tv2 = Fp.square(y1); // 20. tv2 = y1^2
|
||||
tv2 = Fp.mul(tv2, gxd); // 21. tv2 = tv2 * gxd
|
||||
let e2 = Fp.equals(tv2, gx1); // 22. e2 = tv2 == gx1
|
||||
let xn = Fp.cmov(x2n, x1n, e2); // 23. xn = CMOV(x2n, x1n, e2) # If e2, x = x1, else x = x2
|
||||
let y = Fp.cmov(y2, y1, e2); // 24. y = CMOV(y2, y1, e2) # If e2, y = y1, else y = y2
|
||||
let e3 = Fp.isOdd(y); // 25. e3 = sgn0(y) == 1 # Fix sign of y
|
||||
y = Fp.cmov(y, Fp.negate(y), e2 !== e3); // 26. y = CMOV(y, -y, e2 XOR e3)
|
||||
return { xn, xd, yn: y, yd: Fp.ONE }; // 27. return (xn, xd, y, 1)
|
||||
}
|
||||
function map_to_curve_elligator2_edwards448(u: bigint) {
|
||||
let { xn, xd, yn, yd } = map_to_curve_elligator2_curve448(u); // 1. (xn, xd, yn, yd) = map_to_curve_elligator2_curve448(u)
|
||||
let xn2 = Fp.square(xn); // 2. xn2 = xn^2
|
||||
let xd2 = Fp.square(xd); // 3. xd2 = xd^2
|
||||
let xd4 = Fp.square(xd2); // 4. xd4 = xd2^2
|
||||
let yn2 = Fp.square(yn); // 5. yn2 = yn^2
|
||||
let yd2 = Fp.square(yd); // 6. yd2 = yd^2
|
||||
let xEn = Fp.sub(xn2, xd2); // 7. xEn = xn2 - xd2
|
||||
let tv2 = Fp.sub(xEn, xd2); // 8. tv2 = xEn - xd2
|
||||
xEn = Fp.mul(xEn, xd2); // 9. xEn = xEn * xd2
|
||||
xEn = Fp.mul(xEn, yd); // 10. xEn = xEn * yd
|
||||
xEn = Fp.mul(xEn, yn); // 11. xEn = xEn * yn
|
||||
xEn = Fp.mul(xEn, 4n); // 12. xEn = xEn * 4
|
||||
tv2 = Fp.mul(tv2, xn2); // 13. tv2 = tv2 * xn2
|
||||
tv2 = Fp.mul(tv2, yd2); // 14. tv2 = tv2 * yd2
|
||||
let tv3 = Fp.mul(yn2, 4n); // 15. tv3 = 4 * yn2
|
||||
let tv1 = Fp.add(tv3, yd2); // 16. tv1 = tv3 + yd2
|
||||
tv1 = Fp.mul(tv1, xd4); // 17. tv1 = tv1 * xd4
|
||||
let xEd = Fp.add(tv1, tv2); // 18. xEd = tv1 + tv2
|
||||
tv2 = Fp.mul(tv2, xn); // 19. tv2 = tv2 * xn
|
||||
let tv4 = Fp.mul(xn, xd4); // 20. tv4 = xn * xd4
|
||||
let yEn = Fp.sub(tv3, yd2); // 21. yEn = tv3 - yd2
|
||||
yEn = Fp.mul(yEn, tv4); // 22. yEn = yEn * tv4
|
||||
yEn = Fp.sub(yEn, tv2); // 23. yEn = yEn - tv2
|
||||
tv1 = Fp.add(xn2, xd2); // 24. tv1 = xn2 + xd2
|
||||
tv1 = Fp.mul(tv1, xd2); // 25. tv1 = tv1 * xd2
|
||||
tv1 = Fp.mul(tv1, xd); // 26. tv1 = tv1 * xd
|
||||
tv1 = Fp.mul(tv1, yn2); // 27. tv1 = tv1 * yn2
|
||||
tv1 = Fp.mul(tv1, BigInt(-2)); // 28. tv1 = -2 * tv1
|
||||
let yEd = Fp.add(tv2, tv1); // 29. yEd = tv2 + tv1
|
||||
tv4 = Fp.mul(tv4, yd2); // 30. tv4 = tv4 * yd2
|
||||
yEd = Fp.add(yEd, tv4); // 31. yEd = yEd + tv4
|
||||
tv1 = Fp.mul(xEd, yEd); // 32. tv1 = xEd * yEd
|
||||
let e = Fp.equals(tv1, Fp.ZERO); // 33. e = tv1 == 0
|
||||
xEn = Fp.cmov(xEn, Fp.ZERO, e); // 34. xEn = CMOV(xEn, 0, e)
|
||||
xEd = Fp.cmov(xEd, Fp.ONE, e); // 35. xEd = CMOV(xEd, 1, e)
|
||||
yEn = Fp.cmov(yEn, Fp.ONE, e); // 36. yEn = CMOV(yEn, 1, e)
|
||||
yEd = Fp.cmov(yEd, Fp.ONE, e); // 37. yEd = CMOV(yEd, 1, e)
|
||||
|
||||
const inv = Fp.invertBatch([xEd, yEd]); // batch division
|
||||
return { x: Fp.mul(xEn, inv[0]), y: Fp.mul(yEn, inv[1]) }; // 38. return (xEn, xEd, yEn, yEd)
|
||||
}
|
||||
|
||||
const ED448_DEF = {
|
||||
// Param: a
|
||||
a: BigInt(1),
|
||||
@@ -60,7 +137,7 @@ const ED448_DEF = {
|
||||
'726838724295606890549323807888004534353641360687318060281490199180612328166730772686396383698676545930088884461843637361053498018326358'
|
||||
),
|
||||
// Finite field 𝔽p over which we'll do calculations; 2n ** 448n - 2n ** 224n - 1n
|
||||
Fp: Fp(ed448P, 456),
|
||||
Fp,
|
||||
// Subgroup order: how many points ed448 has; 2n**446n - 13818066809895115352007386748515426880336692474882178609894547503885n
|
||||
n: BigInt(
|
||||
'181709681073901722637330951972001133588410340171829515070372549795146003961539585716195755291692375963310293709091662304773755859649779'
|
||||
@@ -111,6 +188,15 @@ const ED448_DEF = {
|
||||
// square root exists, and the decoding fails.
|
||||
return { isValid: mod(x2 * v, P) === u, value: x };
|
||||
},
|
||||
htfDefaults: {
|
||||
DST: 'edwards448_XOF:SHAKE256_ELL2_RO_',
|
||||
p: Fp.ORDER,
|
||||
m: 1,
|
||||
k: 224,
|
||||
expand: 'xof',
|
||||
hash: shake256,
|
||||
},
|
||||
mapToCurve: (scalars: bigint[]) => map_to_curve_elligator2_edwards448(scalars[0]),
|
||||
} as const;
|
||||
|
||||
export const ed448 = twistedEdwards(ED448_DEF);
|
||||
|
||||
@@ -37,7 +37,7 @@ export const P256 = createCurve(
|
||||
p: Fp.ORDER,
|
||||
m: 1,
|
||||
k: 128,
|
||||
expand: true,
|
||||
expand: 'xmd',
|
||||
hash: sha256,
|
||||
},
|
||||
} as const,
|
||||
|
||||
@@ -41,7 +41,7 @@ export const P384 = createCurve({
|
||||
p: Fp.ORDER,
|
||||
m: 1,
|
||||
k: 192,
|
||||
expand: true,
|
||||
expand: 'xmd',
|
||||
hash: sha384,
|
||||
},
|
||||
} as const,
|
||||
|
||||
@@ -54,7 +54,7 @@ export const P521 = createCurve({
|
||||
p: Fp.ORDER,
|
||||
m: 1,
|
||||
k: 256,
|
||||
expand: true,
|
||||
expand: 'xmd',
|
||||
hash: sha512,
|
||||
},
|
||||
} as const, sha512);
|
||||
|
||||
@@ -56,7 +56,9 @@ function sqrtMod(y: bigint): bigint {
|
||||
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);
|
||||
const root = pow2(t2, _2n, P);
|
||||
if (!Fp.equals(Fp.square(root), y)) throw new Error('Cannot find square root');
|
||||
return root;
|
||||
}
|
||||
|
||||
const Fp = Field(secp256k1P, undefined, undefined, { sqrt: sqrtMod });
|
||||
@@ -152,7 +154,7 @@ export const secp256k1 = createCurve(
|
||||
p: Fp.ORDER,
|
||||
m: 1,
|
||||
k: 128,
|
||||
expand: true,
|
||||
expand: 'xmd',
|
||||
hash: sha256,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -97,7 +97,7 @@ function getSharedSecret0x(privKeyA: Hex, pubKeyB: Hex) {
|
||||
return starkCurve.getSharedSecret(normalizePrivateKey(privKeyA), pubKeyB);
|
||||
}
|
||||
|
||||
function sign0x(msgHash: Hex, privKey: Hex, opts: any) {
|
||||
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);
|
||||
}
|
||||
@@ -203,6 +203,8 @@ function pedersenPrecompute(p1: ProjectivePoint, p2: ProjectivePoint): Projectiv
|
||||
out.push(p);
|
||||
p = p.double();
|
||||
}
|
||||
// NOTE: we cannot use wNAF here, because last 4 bits will require full 248 bits multiplication
|
||||
// We can add support for this to wNAF, but it will complicate wNAF.
|
||||
p = p2;
|
||||
for (let i = 0; i < 4; i++) {
|
||||
out.push(p);
|
||||
|
||||
@@ -15,7 +15,252 @@ 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);
|
||||
should(`${name} equality`, () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (num) => {
|
||||
const a = create(num);
|
||||
const b = create(num);
|
||||
deepStrictEqual(Fp.equals(a, b), true);
|
||||
deepStrictEqual(Fp.equals(b, a), true);
|
||||
})
|
||||
);
|
||||
});
|
||||
should(`${name} non-equality`, () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => {
|
||||
const a = create(num1);
|
||||
const b = create(num2);
|
||||
deepStrictEqual(Fp.equals(a, b), num1 === num2);
|
||||
deepStrictEqual(Fp.equals(b, a), num1 === num2);
|
||||
})
|
||||
);
|
||||
});
|
||||
should(`${name} 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(`${name} 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(`${name} add/subtract/x+0=x`, () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (num) => {
|
||||
const a = create(num);
|
||||
deepStrictEqual(Fp.add(a, Fp.ZERO), a);
|
||||
})
|
||||
);
|
||||
});
|
||||
should(`${name} 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(`${name} 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.negate(a));
|
||||
deepStrictEqual(Fp.sub(a, b), Fp.add(a, Fp.negate(b)));
|
||||
deepStrictEqual(Fp.sub(a, b), Fp.add(a, Fp.mul(b, Fp.create(-1n))));
|
||||
})
|
||||
);
|
||||
});
|
||||
should(`${name} add/subtract/negate`, () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (num) => {
|
||||
const a = create(num);
|
||||
deepStrictEqual(Fp.negate(a), Fp.sub(Fp.ZERO, a));
|
||||
deepStrictEqual(Fp.negate(a), Fp.mul(a, Fp.create(-1n)));
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
should(`${name} 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(`${name} 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(`${name} 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(`${name} 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(`${name} multiply/square equality`, () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (num) => {
|
||||
const a = create(num);
|
||||
deepStrictEqual(Fp.square(a), Fp.mul(a, a));
|
||||
})
|
||||
);
|
||||
});
|
||||
should(`${name} 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));
|
||||
})
|
||||
);
|
||||
});
|
||||
const isSquare = mod.FpIsSquare(Fp);
|
||||
should(`${name} multiply/sqrt`, () => {
|
||||
if (Fp === bls12_381.CURVE.Fp12) return; // Not implemented
|
||||
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.equals(Fp.square(root), a), true, 'sqrt(a)^2 == a');
|
||||
deepStrictEqual(Fp.equals(Fp.square(Fp.negate(root)), a), true, '(-sqrt(a))^2 == a');
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
should(`${name} div/division by one equality`, () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (num) => {
|
||||
const a = create(num);
|
||||
if (Fp.equals(a, Fp.ZERO)) return; // No division by zero
|
||||
deepStrictEqual(Fp.div(a, Fp.ONE), a);
|
||||
deepStrictEqual(Fp.div(a, a), Fp.ONE);
|
||||
})
|
||||
);
|
||||
});
|
||||
should(`${name} zero division equality`, () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (num) => {
|
||||
const a = create(num);
|
||||
deepStrictEqual(Fp.div(Fp.ZERO, a), Fp.ZERO);
|
||||
})
|
||||
);
|
||||
});
|
||||
should(`${name} 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(`${name} 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.invert(b)));
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Group tests
|
||||
// prettier-ignore
|
||||
const CURVES = {
|
||||
secp192r1, secp224r1, secp256r1, secp384r1, secp521r1,
|
||||
@@ -29,6 +274,7 @@ const CURVES = {
|
||||
};
|
||||
|
||||
const NUM_RUNS = 5;
|
||||
|
||||
const getXY = (p) => ({ x: p.x, y: p.y });
|
||||
|
||||
function equal(a, b, comment) {
|
||||
|
||||
@@ -41,145 +41,7 @@ const getPubKey = (priv) => bls.getPublicKey(priv);
|
||||
{
|
||||
const Fp = bls.Fp;
|
||||
const FC_BIGINT = fc.bigInt(1n, Fp.ORDER - 1n);
|
||||
should('bls12-381/Fp/equality', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (num) => {
|
||||
const a = Fp.create(num);
|
||||
const b = Fp.create(num);
|
||||
deepStrictEqual(Fp.equals(a, b), true);
|
||||
deepStrictEqual(Fp.equals(b, a), true);
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381/Fp/non-equality', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => {
|
||||
const a = Fp.create(num1);
|
||||
const b = Fp.create(num2);
|
||||
deepStrictEqual(Fp.equals(a, b), num1 === num2);
|
||||
deepStrictEqual(Fp.equals(b, a), num1 === num2);
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381/Fp/add/subtract/commutativity', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => {
|
||||
const a = Fp.create(num1);
|
||||
const b = Fp.create(num2);
|
||||
deepStrictEqual(Fp.add(a, b), Fp.add(b, a));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381/Fp/add/subtract/associativity', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, FC_BIGINT, (num1, num2, num3) => {
|
||||
const a = Fp.create(num1);
|
||||
const b = Fp.create(num2);
|
||||
const c = Fp.create(num3);
|
||||
deepStrictEqual(Fp.add(a, Fp.add(b, c)), Fp.add(Fp.add(a, b), c));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381/Fp/add/subtract/x+0=x', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (num) => {
|
||||
const a = Fp.create(num);
|
||||
deepStrictEqual(Fp.add(a, Fp.ZERO), a);
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381/Fp/add/subtract/x-0=x', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (num) => {
|
||||
const a = Fp.create(num);
|
||||
deepStrictEqual(Fp.sub(a, Fp.ZERO), a);
|
||||
deepStrictEqual(Fp.sub(a, a), Fp.ZERO);
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381/Fp/add/subtract/negate equality', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (num1) => {
|
||||
const a = Fp.create(num1);
|
||||
const b = Fp.create(num1);
|
||||
deepStrictEqual(Fp.sub(Fp.ZERO, a), Fp.negate(a));
|
||||
deepStrictEqual(Fp.sub(a, b), Fp.add(a, Fp.negate(b)));
|
||||
deepStrictEqual(Fp.sub(a, b), Fp.add(a, Fp.mul(b, Fp.create(-1n))));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381/Fp/add/subtract/negate', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (num) => {
|
||||
const a = Fp.create(num);
|
||||
deepStrictEqual(Fp.negate(a), Fp.sub(Fp.ZERO, a));
|
||||
deepStrictEqual(Fp.negate(a), Fp.mul(a, Fp.create(-1n)));
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
should('bls12-381/Fp/multiply/commutativity', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => {
|
||||
const a = Fp.create(num1);
|
||||
const b = Fp.create(num2);
|
||||
deepStrictEqual(Fp.mul(a, b), Fp.mul(b, a));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381/Fp/multiply/associativity', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, FC_BIGINT, (num1, num2, num3) => {
|
||||
const a = Fp.create(num1);
|
||||
const b = Fp.create(num2);
|
||||
const c = Fp.create(num3);
|
||||
deepStrictEqual(Fp.mul(a, Fp.mul(b, c)), Fp.mul(Fp.mul(a, b), c));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381/Fp/multiply/distributivity', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, FC_BIGINT, (num1, num2, num3) => {
|
||||
const a = Fp.create(num1);
|
||||
const b = Fp.create(num2);
|
||||
const c = Fp.create(num3);
|
||||
deepStrictEqual(Fp.mul(a, Fp.add(b, c)), Fp.add(Fp.mul(b, a), Fp.mul(c, a)));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381/Fp/multiply/add equality', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (num) => {
|
||||
const a = Fp.create(num);
|
||||
deepStrictEqual(Fp.mul(a, Fp.create(0n)), Fp.ZERO);
|
||||
deepStrictEqual(Fp.mul(a, Fp.ZERO), Fp.ZERO);
|
||||
deepStrictEqual(Fp.mul(a, Fp.create(1n)), a);
|
||||
deepStrictEqual(Fp.mul(a, Fp.ONE), a);
|
||||
deepStrictEqual(Fp.mul(a, Fp.create(2n)), Fp.add(a, a));
|
||||
deepStrictEqual(Fp.mul(a, Fp.create(3n)), Fp.add(Fp.add(a, a), a));
|
||||
deepStrictEqual(Fp.mul(a, Fp.create(4n)), Fp.add(Fp.add(Fp.add(a, a), a), a));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381/Fp/multiply/square equality', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (num) => {
|
||||
const a = Fp.create(num);
|
||||
deepStrictEqual(Fp.square(a), Fp.mul(a, a));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381/Fp/multiply/pow equality', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (num) => {
|
||||
const a = Fp.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('bls12-381/Fp/multiply/sqrt', () => {
|
||||
let sqr1 = Fp.sqrt(Fp.create(300855555557n));
|
||||
deepStrictEqual(
|
||||
@@ -188,43 +50,6 @@ const getPubKey = (priv) => bls.getPublicKey(priv);
|
||||
);
|
||||
throws(() => Fp.sqrt(Fp.create(72057594037927816n)));
|
||||
});
|
||||
|
||||
should('bls12-381/Fp/div/division by one equality', () => {
|
||||
fc.assert(
|
||||
fc.property(fc.bigInt(1n, Fp.ORDER - 1n), (num) => {
|
||||
const a = Fp.create(num);
|
||||
deepStrictEqual(Fp.div(a, Fp.ONE), a);
|
||||
deepStrictEqual(Fp.div(a, a), Fp.ONE);
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381/Fp/div/division by zero equality', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, (num) => {
|
||||
const a = Fp.create(num);
|
||||
deepStrictEqual(Fp.div(Fp.ZERO, a), Fp.ZERO);
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381/Fp/div/division distributivity', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, FC_BIGINT, (num1, num2, num3) => {
|
||||
const a = Fp.create(num1);
|
||||
const b = Fp.create(num2);
|
||||
const c = Fp.create(num3);
|
||||
deepStrictEqual(Fp.div(Fp.add(a, b), c), Fp.add(Fp.div(a, c), Fp.div(b, c)));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381/Fp/div/division and multiplication equality', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT, FC_BIGINT, (num1, num2) => {
|
||||
const a = Fp.create(num1);
|
||||
const b = Fp.create(num2);
|
||||
deepStrictEqual(Fp.div(a, b), Fp.mul(a, Fp.invert(b)));
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// Fp2
|
||||
@@ -234,16 +59,6 @@ const getPubKey = (priv) => bls.getPublicKey(priv);
|
||||
const FC_BIGINT = fc.bigInt(1n, Fp.ORDER - 1n);
|
||||
const FC_BIGINT_2 = fc.array(FC_BIGINT, { minLength: 2, maxLength: 2 });
|
||||
|
||||
should('bls12-381 Fp2/equality', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT_2, (num) => {
|
||||
const a = Fp2.fromBigTuple([num[0], num[1]]);
|
||||
const b = Fp2.fromBigTuple([num[0], num[1]]);
|
||||
deepStrictEqual(Fp2.equals(a, b), true);
|
||||
deepStrictEqual(Fp2.equals(b, a), true);
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381 Fp2/non-equality', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT_2, FC_BIGINT_2, (num1, num2) => {
|
||||
@@ -254,136 +69,7 @@ const getPubKey = (priv) => bls.getPublicKey(priv);
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381 Fp2/add/subtract/commutativity', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT_2, FC_BIGINT_2, (num1, num2) => {
|
||||
const a = Fp2.fromBigTuple([num1[0], num1[1]]);
|
||||
const b = Fp2.fromBigTuple([num2[0], num2[1]]);
|
||||
deepStrictEqual(Fp2.add(a, b), Fp2.add(b, a));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381 Fp2/add/subtract/associativity', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT_2, FC_BIGINT_2, FC_BIGINT_2, (num1, num2, num3) => {
|
||||
const a = Fp2.fromBigTuple([num1[0], num1[1]]);
|
||||
const b = Fp2.fromBigTuple([num2[0], num2[1]]);
|
||||
const c = Fp2.fromBigTuple([num3[0], num3[1]]);
|
||||
deepStrictEqual(Fp2.add(a, Fp2.add(b, c)), Fp2.add(Fp2.add(a, b), c));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381 Fp2/add/subtract/x+0=x', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT_2, (num) => {
|
||||
const a = Fp2.fromBigTuple([num[0], num[1]]);
|
||||
deepStrictEqual(Fp2.add(a, Fp2.ZERO), a);
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381 Fp2/add/subtract/x-0=x', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT_2, (num) => {
|
||||
const a = Fp2.fromBigTuple([num[0], num[1]]);
|
||||
deepStrictEqual(Fp2.sub(a, Fp2.ZERO), a);
|
||||
deepStrictEqual(Fp2.sub(a, a), Fp2.ZERO);
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381 Fp2/add/subtract/negate equality', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT_2, (num1) => {
|
||||
const a = Fp2.fromBigTuple([num1[0], num1[1]]);
|
||||
const b = Fp2.fromBigTuple([num1[0], num1[1]]);
|
||||
deepStrictEqual(Fp2.sub(Fp2.ZERO, a), Fp2.negate(a));
|
||||
deepStrictEqual(Fp2.sub(a, b), Fp2.add(a, Fp2.negate(b)));
|
||||
deepStrictEqual(Fp2.sub(a, b), Fp2.add(a, Fp2.mul(b, -1n)));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381 Fp2/add/subtract/negate', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT_2, (num) => {
|
||||
const a = Fp2.fromBigTuple([num[0], num[1]]);
|
||||
deepStrictEqual(Fp2.negate(a), Fp2.sub(Fp2.ZERO, a));
|
||||
deepStrictEqual(Fp2.negate(a), Fp2.mul(a, -1n));
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
should('bls12-381 Fp2/multiply/commutativity', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT_2, FC_BIGINT_2, (num1, num2) => {
|
||||
const a = Fp2.fromBigTuple([num1[0], num1[1]]);
|
||||
const b = Fp2.fromBigTuple([num2[0], num2[1]]);
|
||||
deepStrictEqual(Fp2.mul(a, b), Fp2.mul(b, a));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381 Fp2/multiply/associativity', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT_2, FC_BIGINT_2, FC_BIGINT_2, (num1, num2, num3) => {
|
||||
const a = Fp2.fromBigTuple([num1[0], num1[1]]);
|
||||
const b = Fp2.fromBigTuple([num2[0], num2[1]]);
|
||||
const c = Fp2.fromBigTuple([num3[0], num3[1]]);
|
||||
deepStrictEqual(Fp2.mul(a, Fp2.mul(b, c)), Fp2.mul(Fp2.mul(a, b), c));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381 Fp2/multiply/distributivity', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT_2, FC_BIGINT_2, FC_BIGINT_2, (num1, num2, num3) => {
|
||||
const a = Fp2.fromBigTuple([num1[0], num1[1]]);
|
||||
const b = Fp2.fromBigTuple([num2[0], num2[1]]);
|
||||
const c = Fp2.fromBigTuple([num3[0], num3[1]]);
|
||||
deepStrictEqual(Fp2.mul(a, Fp2.add(b, c)), Fp2.add(Fp2.mul(b, a), Fp2.mul(c, a)));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381 Fp2/multiply/add equality', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT_2, (num) => {
|
||||
const a = Fp2.fromBigTuple([num[0], num[1]]);
|
||||
deepStrictEqual(Fp2.mul(a, 0n), Fp2.ZERO);
|
||||
deepStrictEqual(Fp2.mul(a, Fp2.ZERO), Fp2.ZERO);
|
||||
deepStrictEqual(Fp2.mul(a, 1n), a);
|
||||
deepStrictEqual(Fp2.mul(a, Fp2.ONE), a);
|
||||
deepStrictEqual(Fp2.mul(a, 2n), Fp2.add(a, a));
|
||||
deepStrictEqual(Fp2.mul(a, 3n), Fp2.add(Fp2.add(a, a), a));
|
||||
deepStrictEqual(Fp2.mul(a, 4n), Fp2.add(Fp2.add(Fp2.add(a, a), a), a));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381 Fp2/multiply/square equality', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT_2, (num) => {
|
||||
const a = Fp2.fromBigTuple([num[0], num[1]]);
|
||||
deepStrictEqual(Fp2.square(a), Fp2.mul(a, a));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381 Fp2/multiply/pow equality', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT_2, (num) => {
|
||||
const a = Fp2.fromBigTuple([num[0], num[1]]);
|
||||
deepStrictEqual(Fp2.pow(a, 0n), Fp2.ONE);
|
||||
deepStrictEqual(Fp2.pow(a, 1n), a);
|
||||
deepStrictEqual(Fp2.pow(a, 2n), Fp2.mul(a, a));
|
||||
deepStrictEqual(Fp2.pow(a, 3n), Fp2.mul(Fp2.mul(a, a), a));
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
should('bls12-381 Fp2/div/distributivity', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT_2, FC_BIGINT_2, FC_BIGINT_2, (num1, num2, num3) => {
|
||||
const a = Fp2.fromBigTuple([num1[0], num1[1]]);
|
||||
const b = Fp2.fromBigTuple([num2[0], num2[1]]);
|
||||
const c = Fp2.fromBigTuple([num3[0], num3[1]]);
|
||||
deepStrictEqual(Fp2.div(Fp2.add(a, b), c), Fp2.add(Fp2.div(a, c), Fp2.div(b, c)));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381 Fp2/div/x/1=x', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT_2, (num) => {
|
||||
@@ -394,23 +80,6 @@ const getPubKey = (priv) => bls.getPublicKey(priv);
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381 Fp2/div/ x/0=0', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT_2, (num) => {
|
||||
const a = Fp2.fromBigTuple([num[0], num[1]]);
|
||||
deepStrictEqual(Fp2.div(Fp2.ZERO, a), Fp2.ZERO);
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381 Fp2/div/ multiply equality', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT_2, FC_BIGINT_2, (num1, num2) => {
|
||||
const a = Fp2.fromBigTuple([num1[0], num1[1]]);
|
||||
const b = Fp2.fromBigTuple([num2[0], num2[1]]);
|
||||
deepStrictEqual(Fp2.div(a, b), Fp2.mul(a, Fp2.invert(b)));
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
should('bls12-381 Fp2/frobenius', () => {
|
||||
// expect(Fp2.FROBENIUS_COEFFICIENTS[0].equals(Fp.ONE)).toBe(true);
|
||||
@@ -472,195 +141,6 @@ const getPubKey = (priv) => bls.getPublicKey(priv);
|
||||
});
|
||||
}
|
||||
|
||||
// Fp12
|
||||
{
|
||||
const Fp = bls.Fp;
|
||||
const Fp12 = bls.Fp12;
|
||||
const FC_BIGINT = fc.bigInt(1n, Fp.ORDER - 1n);
|
||||
const FC_BIGINT_12 = fc.array(FC_BIGINT, {
|
||||
minLength: 12,
|
||||
maxLength: 12,
|
||||
});
|
||||
|
||||
should('bls12-381 Fp12/equality', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT_12, (num) => {
|
||||
const a = Fp12.fromBigTwelve(num);
|
||||
const b = Fp12.fromBigTwelve(num);
|
||||
deepStrictEqual(Fp12.equals(a, b), true);
|
||||
deepStrictEqual(Fp12.equals(b, a), true);
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381 Fp12/non-equality', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT_12, FC_BIGINT_12, (num1, num2) => {
|
||||
const a = Fp12.fromBigTwelve(num1);
|
||||
const b = Fp12.fromBigTwelve(num2);
|
||||
deepStrictEqual(Fp12.equals(a, b), num1[0] === num2[0] && num1[1] === num2[1]);
|
||||
deepStrictEqual(Fp12.equals(b, a), num1[0] === num2[0] && num1[1] === num2[1]);
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381 Fp12/add/subtract/commutativuty', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT_12, FC_BIGINT_12, (num1, num2) => {
|
||||
const a = Fp12.fromBigTwelve(num1);
|
||||
const b = Fp12.fromBigTwelve(num2);
|
||||
deepStrictEqual(Fp12.add(a, b), Fp12.add(b, a));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381 Fp12/add/subtract/associativity', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT_12, FC_BIGINT_12, FC_BIGINT_12, (num1, num2, num3) => {
|
||||
const a = Fp12.fromBigTwelve(num1);
|
||||
const b = Fp12.fromBigTwelve(num2);
|
||||
const c = Fp12.fromBigTwelve(num3);
|
||||
deepStrictEqual(Fp12.add(a, Fp12.add(b, c)), Fp12.add(Fp12.add(a, b), c));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381 Fp12/add/subtract/x+0=x', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT_12, (num) => {
|
||||
const a = Fp12.fromBigTwelve(num);
|
||||
deepStrictEqual(Fp12.add(a, Fp12.ZERO), a);
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381 Fp12/add/subtract/x-0=x', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT_12, (num) => {
|
||||
const a = Fp12.fromBigTwelve(num);
|
||||
deepStrictEqual(Fp12.sub(a, Fp12.ZERO), a);
|
||||
deepStrictEqual(Fp12.sub(a, a), Fp12.ZERO);
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381 Fp12/add/subtract/negate equality', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT_12, (num1) => {
|
||||
const a = Fp12.fromBigTwelve(num1);
|
||||
const b = Fp12.fromBigTwelve(num1);
|
||||
deepStrictEqual(Fp12.sub(Fp12.ZERO, a), Fp12.negate(a));
|
||||
deepStrictEqual(Fp12.sub(a, b), Fp12.add(a, Fp12.negate(b)));
|
||||
deepStrictEqual(Fp12.sub(a, b), Fp12.add(a, Fp12.mul(b, -1n)));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381 Fp12/add/subtract/negate', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT_12, (num) => {
|
||||
const a = Fp12.fromBigTwelve(num);
|
||||
deepStrictEqual(Fp12.negate(a), Fp12.sub(Fp12.ZERO, a));
|
||||
deepStrictEqual(Fp12.negate(a), Fp12.mul(a, -1n));
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
should('bls12-381 Fp12/multiply/commutativity', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT_12, FC_BIGINT_12, (num1, num2) => {
|
||||
const a = Fp12.fromBigTwelve(num1);
|
||||
const b = Fp12.fromBigTwelve(num2);
|
||||
deepStrictEqual(Fp12.mul(a, b), Fp12.mul(b, a));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381 Fp12/multiply/associativity', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT_12, FC_BIGINT_12, FC_BIGINT_12, (num1, num2, num3) => {
|
||||
const a = Fp12.fromBigTwelve(num1);
|
||||
const b = Fp12.fromBigTwelve(num2);
|
||||
const c = Fp12.fromBigTwelve(num3);
|
||||
deepStrictEqual(Fp12.mul(a, Fp12.mul(b, c)), Fp12.mul(Fp12.mul(a, b), c));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381 Fp12/multiply/distributivity', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT_12, FC_BIGINT_12, FC_BIGINT_12, (num1, num2, num3) => {
|
||||
const a = Fp12.fromBigTwelve(num1);
|
||||
const b = Fp12.fromBigTwelve(num2);
|
||||
const c = Fp12.fromBigTwelve(num3);
|
||||
deepStrictEqual(Fp12.mul(a, Fp12.add(b, c)), Fp12.add(Fp12.mul(b, a), Fp12.mul(c, a)));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381 Fp12/multiply/add equality', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT_12, (num) => {
|
||||
const a = Fp12.fromBigTwelve(num);
|
||||
deepStrictEqual(Fp12.mul(a, 0n), Fp12.ZERO);
|
||||
deepStrictEqual(Fp12.mul(a, Fp12.ZERO), Fp12.ZERO);
|
||||
deepStrictEqual(Fp12.mul(a, 1n), a);
|
||||
deepStrictEqual(Fp12.mul(a, Fp12.ONE), a);
|
||||
deepStrictEqual(Fp12.mul(a, 2n), Fp12.add(a, a));
|
||||
deepStrictEqual(Fp12.mul(a, 3n), Fp12.add(Fp12.add(a, a), a));
|
||||
deepStrictEqual(Fp12.mul(a, 4n), Fp12.add(Fp12.add(Fp12.add(a, a), a), a));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381 Fp12/multiply/square equality', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT_12, (num) => {
|
||||
const a = Fp12.fromBigTwelve(num);
|
||||
deepStrictEqual(Fp12.square(a), Fp12.mul(a, a));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381 Fp12/multiply/pow equality', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT_12, (num) => {
|
||||
const a = Fp12.fromBigTwelve(num);
|
||||
deepStrictEqual(Fp12.pow(a, 0n), Fp12.ONE);
|
||||
deepStrictEqual(Fp12.pow(a, 1n), a);
|
||||
deepStrictEqual(Fp12.pow(a, 2n), Fp12.mul(a, a));
|
||||
deepStrictEqual(Fp12.pow(a, 3n), Fp12.mul(Fp12.mul(a, a), a));
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
should('bls12-381 Fp12/div/x/1=x', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT_12, (num) => {
|
||||
const a = Fp12.fromBigTwelve(num);
|
||||
deepStrictEqual(Fp12.div(a, 1n), a);
|
||||
deepStrictEqual(Fp12.div(a, Fp12.ONE), a);
|
||||
deepStrictEqual(Fp12.div(a, a), Fp12.ONE);
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381 Fp12/div/x/0=0', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT_12, (num) => {
|
||||
const a = Fp12.fromBigTwelve(num);
|
||||
deepStrictEqual(Fp12.div(Fp12.ZERO, a), Fp12.ZERO);
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381 Fp12/div/distributivity', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT_12, FC_BIGINT_12, FC_BIGINT_12, (num1, num2, num3) => {
|
||||
const a = Fp12.fromBigTwelve(num1);
|
||||
const b = Fp12.fromBigTwelve(num2);
|
||||
const c = Fp12.fromBigTwelve(num3);
|
||||
deepStrictEqual(Fp12.div(Fp12.add(a, b), c), Fp12.add(Fp12.div(a, c), Fp12.div(b, c)));
|
||||
})
|
||||
);
|
||||
});
|
||||
should('bls12-381 Fp12/div/multiply equality', () => {
|
||||
fc.assert(
|
||||
fc.property(FC_BIGINT_12, FC_BIGINT_12, (num1, num2) => {
|
||||
const a = Fp12.fromBigTwelve(num1);
|
||||
const b = Fp12.fromBigTwelve(num2);
|
||||
deepStrictEqual(Fp12.div(a, b), Fp12.mul(a, Fp12.invert(b)));
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// Point
|
||||
{
|
||||
const Fp = bls.Fp;
|
||||
|
||||
@@ -4,16 +4,27 @@ 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 { secp256r1 } from '../lib/esm/p256.js';
|
||||
import { secp384r1 } from '../lib/esm/p384.js';
|
||||
import { secp521r1 } from '../lib/esm/p521.js';
|
||||
import { ed25519 } from '../lib/esm/ed25519.js';
|
||||
import { ed448 } from '../lib/esm/ed448.js';
|
||||
import { secp256k1 } from '../lib/esm/secp256k1.js';
|
||||
import { bls12_381 } from '../lib/esm/bls12-381.js';
|
||||
import { stringToBytes, expand_message_xmd } from '../lib/esm/abstract/hash-to-curve.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' };
|
||||
@@ -58,6 +69,26 @@ testExpandXMD(sha256, xmd_sha256_38);
|
||||
testExpandXMD(sha256, xmd_sha256_256);
|
||||
testExpandXMD(sha512, xmd_sha512_38);
|
||||
|
||||
function testExpandXOF(hash, vectors) {
|
||||
for (let i = 0; i < vectors.tests.length; i++) {
|
||||
const t = vectors.tests[i];
|
||||
should(`expand_message_xof/${vectors.hash}/${vectors.DST.length}/${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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
testExpandXOF(shake128, xof_shake128_36);
|
||||
testExpandXOF(shake128, xof_shake128_256);
|
||||
testExpandXOF(shake256, xof_shake256_36);
|
||||
|
||||
function stringToFp(s) {
|
||||
// bls-G2 support
|
||||
if (s.includes(',')) {
|
||||
@@ -97,8 +128,8 @@ testCurve(secp521r1, p521_ro, p521_nu);
|
||||
testCurve(bls12_381.G1, g1_ro, g1_nu);
|
||||
testCurve(bls12_381.G2, g2_ro, g2_nu);
|
||||
testCurve(secp256k1, secp256k1_ro, secp256k1_nu);
|
||||
//testCurve(ed25519, ed25519_ro, ed25519_nu);
|
||||
//testCurve(ed448, ed448_ro, ed448_nu);
|
||||
testCurve(ed25519, ed25519_ro, ed25519_nu);
|
||||
testCurve(ed448, ed448_ro, ed448_nu);
|
||||
|
||||
// ESM is broken.
|
||||
import url from 'url';
|
||||
|
||||
Reference in New Issue
Block a user