All files: rename Fp to Field

This commit is contained in:
Paul Miller 2023-03-21 05:51:18 +00:00
parent f3c21eb347
commit 8fabc7ff06
No known key found for this signature in database
GPG Key ID: 697079DA6878B89B
23 changed files with 93 additions and 76 deletions

@ -223,7 +223,7 @@ There are following zero-dependency algorithms:
```ts ```ts
import { weierstrass } from '@noble/curves/abstract/weierstrass'; import { weierstrass } from '@noble/curves/abstract/weierstrass';
import { Fp } from '@noble/curves/abstract/modular'; // finite field for mod arithmetics import { Field } from '@noble/curves/abstract/modular'; // finite field for mod arithmetics
import { sha256 } from '@noble/hashes/sha256'; // 3rd-party sha256() of type utils.CHash import { sha256 } from '@noble/hashes/sha256'; // 3rd-party sha256() of type utils.CHash
import { hmac } from '@noble/hashes/hmac'; // 3rd-party hmac() that will accept sha256() import { hmac } from '@noble/hashes/hmac'; // 3rd-party hmac() that will accept sha256()
import { concatBytes, randomBytes } from '@noble/hashes/utils'; // 3rd-party utilities import { concatBytes, randomBytes } from '@noble/hashes/utils'; // 3rd-party utilities
@ -233,7 +233,7 @@ const secq256k1 = weierstrass({
// https://zcash.github.io/halo2/background/curves.html#cycles-of-curves // https://zcash.github.io/halo2/background/curves.html#cycles-of-curves
a: 0n, a: 0n,
b: 7n, b: 7n,
Fp: Fp(2n ** 256n - 432420386565659656852420866394968145599n), Fp: Field(2n ** 256n - 432420386565659656852420866394968145599n),
n: 2n ** 256n - 2n ** 32n - 2n ** 9n - 2n ** 8n - 2n ** 7n - 2n ** 6n - 2n ** 4n - 1n, n: 2n ** 256n - 2n ** 32n - 2n ** 9n - 2n ** 8n - 2n ** 7n - 2n ** 6n - 2n ** 4n - 1n,
Gx: 55066263022277343669578718895168534326250603453777594175500187360389116729240n, Gx: 55066263022277343669578718895168534326250603453777594175500187360389116729240n,
Gy: 32670510020758816978083085130507043184471273380659243275938904335757337482424n, Gy: 32670510020758816978083085130507043184471273380659243275938904335757337482424n,
@ -241,6 +241,8 @@ const secq256k1 = weierstrass({
hmac: (key: Uint8Array, ...msgs: Uint8Array[]) => hmac(sha256, key, concatBytes(...msgs)), hmac: (key: Uint8Array, ...msgs: Uint8Array[]) => hmac(sha256, key, concatBytes(...msgs)),
randomBytes, randomBytes,
}); });
// weierstrassPoints can also be used if you don't need ECDSA, hash, hmac, randomBytes
``` ```
Short Weierstrass curve's formula is `y² = x³ + ax + b`. `weierstrass` Short Weierstrass curve's formula is `y² = x³ + ax + b`. `weierstrass`
@ -272,6 +274,7 @@ type CHash = {
6. Have `toAffine()` and `x` / `y` getters which convert to 2d xy affine coordinates 6. Have `toAffine()` and `x` / `y` getters which convert to 2d xy affine coordinates
```ts ```ts
// `weierstrassPoints()` returns `CURVE` and `ProjectivePoint`
// `weierstrass()` returns `CurveFn` // `weierstrass()` returns `CurveFn`
type SignOpts = { lowS?: boolean; prehash?: boolean; extraEntropy: boolean | Uint8Array }; type SignOpts = { lowS?: boolean; prehash?: boolean; extraEntropy: boolean | Uint8Array };
type CurveFn = { type CurveFn = {
@ -379,15 +382,15 @@ fast.multiply(privKey); // much faster ECDH now
```ts ```ts
import { twistedEdwards } from '@noble/curves/abstract/edwards'; import { twistedEdwards } from '@noble/curves/abstract/edwards';
import { Fp } from '@noble/curves/abstract/modular'; import { Field } from '@noble/curves/abstract/modular';
import { sha512 } from '@noble/hashes/sha512'; import { sha512 } from '@noble/hashes/sha512';
import { randomBytes } from '@noble/hashes/utils'; import { randomBytes } from '@noble/hashes/utils';
const fp = Fp(2n ** 255n - 19n); const Fp = Field(2n ** 255n - 19n);
const ed25519 = twistedEdwards({ const ed25519 = twistedEdwards({
a: -1n, a: -1n,
d: fp.div(-121665n, 121666n), // -121665n/121666n mod p d: Fp.div(-121665n, 121666n), // -121665n/121666n mod p
Fp: fp, Fp: Fp,
n: 2n ** 252n + 27742317777372353535851937790883648493n, n: 2n ** 252n + 27742317777372353535851937790883648493n,
h: 8n, h: 8n,
Gx: 15112221349535400772501151409588531511454012693041857206046113283949847762202n, Gx: 15112221349535400772501151409588531511454012693041857206046113283949847762202n,
@ -465,12 +468,12 @@ interface ExtPointConstructor extends GroupConstructor<ExtPointType> {
```typescript ```typescript
import { montgomery } from '@noble/curves/abstract/montgomery'; import { montgomery } from '@noble/curves/abstract/montgomery';
import { Fp } from '@noble/curves/abstract/modular'; import { Field } from '@noble/curves/abstract/modular';
const x25519 = montgomery({ const x25519 = montgomery({
Fp: Fp(2n ** 255n - 19n),
a: 486662n, a: 486662n,
Gu: 9n, Gu: 9n,
Fp: Field(2n ** 255n - 19n),
montgomeryBits: 255, montgomeryBits: 255,
nByteLength: 32, nByteLength: 32,
// Optional param // Optional param

@ -1,6 +1,6 @@
import { run, mark } from 'micro-bmark'; import { run, mark } from 'micro-bmark';
import { secp256k1 } from '../secp256k1.js'; import { secp256k1 } from '../secp256k1.js';
import { Fp } from '../abstract/modular.js'; import { Field as Fp } from '../abstract/modular.js';
run(async () => { run(async () => {
console.log(`\x1b[36mmodular, secp256k1 field\x1b[0m`); console.log(`\x1b[36mmodular, secp256k1 field\x1b[0m`);

@ -12,7 +12,7 @@
* Some projects may prefer to swap this relation, it is not supported for now. * Some projects may prefer to swap this relation, it is not supported for now.
*/ */
import { AffinePoint } from './curve.js'; import { AffinePoint } from './curve.js';
import { Field, hashToPrivateScalar } from './modular.js'; import { IField, hashToPrivateScalar } from './modular.js';
import { Hex, PrivKey, CHash, bitLen, bitGet, ensureBytes } from './utils.js'; import { Hex, PrivKey, CHash, bitLen, bitGet, ensureBytes } from './utils.js';
import * as htf from './hash-to-curve.js'; import * as htf from './hash-to-curve.js';
import { import {
@ -41,15 +41,15 @@ export type CurveType<Fp, Fp2, Fp6, Fp12> = {
htfDefaults: htf.Opts; htfDefaults: htf.Opts;
}; };
x: bigint; x: bigint;
Fp: Field<Fp>; Fp: IField<Fp>;
Fr: Field<bigint>; Fr: IField<bigint>;
Fp2: Field<Fp2> & { Fp2: IField<Fp2> & {
reim: (num: Fp2) => { re: bigint; im: bigint }; reim: (num: Fp2) => { re: bigint; im: bigint };
multiplyByB: (num: Fp2) => Fp2; multiplyByB: (num: Fp2) => Fp2;
frobeniusMap(num: Fp2, power: number): Fp2; frobeniusMap(num: Fp2, power: number): Fp2;
}; };
Fp6: Field<Fp6>; Fp6: IField<Fp6>;
Fp12: Field<Fp12> & { Fp12: IField<Fp12> & {
frobeniusMap(num: Fp12, power: number): Fp12; frobeniusMap(num: Fp12, power: number): Fp12;
multiplyBy014(num: Fp12, o0: Fp2, o1: Fp2, o4: Fp2): Fp12; multiplyBy014(num: Fp12, o0: Fp2, o1: Fp2, o4: Fp2): Fp12;
conjugate(num: Fp12): Fp12; conjugate(num: Fp12): Fp12;
@ -62,11 +62,11 @@ export type CurveType<Fp, Fp2, Fp6, Fp12> = {
export type CurveFn<Fp, Fp2, Fp6, Fp12> = { export type CurveFn<Fp, Fp2, Fp6, Fp12> = {
CURVE: CurveType<Fp, Fp2, Fp6, Fp12>; CURVE: CurveType<Fp, Fp2, Fp6, Fp12>;
Fr: Field<bigint>; Fr: IField<bigint>;
Fp: Field<Fp>; Fp: IField<Fp>;
Fp2: Field<Fp2>; Fp2: IField<Fp2>;
Fp6: Field<Fp6>; Fp6: IField<Fp6>;
Fp12: Field<Fp12>; Fp12: IField<Fp12>;
G1: CurvePointsRes<Fp> & ReturnType<typeof htf.createHasher<Fp>>; G1: CurvePointsRes<Fp> & ReturnType<typeof htf.createHasher<Fp>>;
G2: CurvePointsRes<Fp2> & ReturnType<typeof htf.createHasher<Fp2>>; G2: CurvePointsRes<Fp2> & ReturnType<typeof htf.createHasher<Fp2>>;
Signature: SignatureCoder<Fp2>; Signature: SignatureCoder<Fp2>;

@ -1,6 +1,6 @@
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
// Abelian group utilities // Abelian group utilities
import { Field, validateField, nLength } from './modular.js'; import { IField, validateField, nLength } from './modular.js';
import { validateObject } from './utils.js'; import { validateObject } from './utils.js';
const _0n = BigInt(0); const _0n = BigInt(0);
const _1n = BigInt(1); const _1n = BigInt(1);
@ -168,7 +168,7 @@ export function wNAF<T extends Group<T>>(c: GroupConstructor<T>, bits: number) {
// Generic BasicCurve interface: works even for polynomial fields (BLS): P, n, h would be ok. // Generic BasicCurve interface: works even for polynomial fields (BLS): P, n, h would be ok.
// Though generator can be different (Fp2 / Fp6 for BLS). // Though generator can be different (Fp2 / Fp6 for BLS).
export type BasicCurve<T> = { export type BasicCurve<T> = {
Fp: Field<T>; // Field over which we'll do calculations (Fp) Fp: IField<T>; // Field over which we'll do calculations (Fp)
n: bigint; // Curve order, total count of valid points in the field n: bigint; // Curve order, total count of valid points in the field
nBitLength?: number; // bit length of curve order nBitLength?: number; // bit length of curve order
nByteLength?: number; // byte length of curve order nByteLength?: number; // byte length of curve order
@ -195,5 +195,9 @@ export function validateBasic<FP, T>(curve: BasicCurve<FP> & T) {
} }
); );
// Set defaults // Set defaults
return Object.freeze({ ...nLength(curve.n, curve.nBitLength), ...curve, ...{p: curve.Fp.ORDER} } as const); return Object.freeze({
...nLength(curve.n, curve.nBitLength),
...curve,
...{ p: curve.Fp.ORDER },
} as const);
} }

@ -1,6 +1,6 @@
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
import type { Group, GroupConstructor, AffinePoint } from './curve.js'; import type { Group, GroupConstructor, AffinePoint } from './curve.js';
import { mod, Field } from './modular.js'; import { mod, IField } from './modular.js';
import { bytesToNumberBE, CHash, concatBytes, utf8ToBytes, validateObject } from './utils.js'; import { bytesToNumberBE, CHash, concatBytes, utf8ToBytes, validateObject } from './utils.js';
/** /**
@ -163,7 +163,7 @@ export function hash_to_field(msg: Uint8Array, count: number, options: Opts): bi
return u; return u;
} }
export function isogenyMap<T, F extends Field<T>>(field: F, map: [T[], T[], T[], T[]]) { export function isogenyMap<T, F extends IField<T>>(field: F, map: [T[], T[], T[], T[]]) {
// Make same order as in spec // Make same order as in spec
const COEFF = map.map((i) => Array.from(i).reverse()); const COEFF = map.map((i) => Array.from(i).reverse());
return (x: T, y: T) => { return (x: T, y: T) => {

@ -97,7 +97,7 @@ export function tonelliShanks(P: bigint) {
// Fast-path // Fast-path
if (S === 1) { if (S === 1) {
const p1div4 = (P + _1n) / _4n; const p1div4 = (P + _1n) / _4n;
return function tonelliFast<T>(Fp: Field<T>, n: T) { return function tonelliFast<T>(Fp: IField<T>, n: T) {
const root = Fp.pow(n, p1div4); const root = Fp.pow(n, p1div4);
if (!Fp.eql(Fp.sqr(root), n)) throw new Error('Cannot find square root'); if (!Fp.eql(Fp.sqr(root), n)) throw new Error('Cannot find square root');
return root; return root;
@ -106,7 +106,7 @@ export function tonelliShanks(P: bigint) {
// Slow-path // Slow-path
const Q1div2 = (Q + _1n) / _2n; const Q1div2 = (Q + _1n) / _2n;
return function tonelliSlow<T>(Fp: Field<T>, n: T): T { return function tonelliSlow<T>(Fp: IField<T>, n: T): T {
// Step 0: Check that n is indeed a square: (n | p) should not be ≡ -1 // Step 0: Check that n is indeed a square: (n | p) should not be ≡ -1
if (Fp.pow(n, legendreC) === Fp.neg(Fp.ONE)) throw new Error('Cannot find square root'); if (Fp.pow(n, legendreC) === Fp.neg(Fp.ONE)) throw new Error('Cannot find square root');
let r = S; let r = S;
@ -146,7 +146,7 @@ export function FpSqrt(P: bigint) {
// 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaabn; // 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaabn;
// const NUM = 72057594037927816n; // const NUM = 72057594037927816n;
const p1div4 = (P + _1n) / _4n; const p1div4 = (P + _1n) / _4n;
return function sqrt3mod4<T>(Fp: Field<T>, n: T) { return function sqrt3mod4<T>(Fp: IField<T>, n: T) {
const root = Fp.pow(n, p1div4); const root = Fp.pow(n, p1div4);
// Throw if root**2 != n // Throw if root**2 != n
if (!Fp.eql(Fp.sqr(root), n)) throw new Error('Cannot find square root'); if (!Fp.eql(Fp.sqr(root), n)) throw new Error('Cannot find square root');
@ -157,7 +157,7 @@ export function FpSqrt(P: bigint) {
// Atkin algorithm for q ≡ 5 (mod 8), https://eprint.iacr.org/2012/685.pdf (page 10) // Atkin algorithm for q ≡ 5 (mod 8), https://eprint.iacr.org/2012/685.pdf (page 10)
if (P % _8n === _5n) { if (P % _8n === _5n) {
const c1 = (P - _5n) / _8n; const c1 = (P - _5n) / _8n;
return function sqrt5mod8<T>(Fp: Field<T>, n: T) { return function sqrt5mod8<T>(Fp: IField<T>, n: T) {
const n2 = Fp.mul(n, _2n); const n2 = Fp.mul(n, _2n);
const v = Fp.pow(n2, c1); const v = Fp.pow(n2, c1);
const nv = Fp.mul(n, v); const nv = Fp.mul(n, v);
@ -203,7 +203,7 @@ export const isNegativeLE = (num: bigint, modulo: bigint) => (mod(num, modulo) &
// - unreadable mess: addition, multiply, square, squareRoot, inversion, divide, power, equals, subtract // - unreadable mess: addition, multiply, square, squareRoot, inversion, divide, power, equals, subtract
// Field is not always over prime, Fp2 for example has ORDER(q)=p^m // Field is not always over prime, Fp2 for example has ORDER(q)=p^m
export interface Field<T> { export interface IField<T> {
ORDER: bigint; ORDER: bigint;
BYTES: number; BYTES: number;
BITS: number; BITS: number;
@ -249,7 +249,7 @@ const FIELD_FIELDS = [
'eql', 'add', 'sub', 'mul', 'pow', 'div', 'eql', 'add', 'sub', 'mul', 'pow', 'div',
'addN', 'subN', 'mulN', 'sqrN' 'addN', 'subN', 'mulN', 'sqrN'
] as const; ] as const;
export function validateField<T>(field: Field<T>) { export function validateField<T>(field: IField<T>) {
const initial = { const initial = {
ORDER: 'bigint', ORDER: 'bigint',
MASK: 'bigint', MASK: 'bigint',
@ -264,7 +264,7 @@ export function validateField<T>(field: Field<T>) {
} }
// Generic field functions // Generic field functions
export function FpPow<T>(f: Field<T>, num: T, power: bigint): T { export function FpPow<T>(f: IField<T>, num: T, power: bigint): T {
// Should have same speed as pow for bigints // Should have same speed as pow for bigints
// TODO: benchmark! // TODO: benchmark!
if (power < _0n) throw new Error('Expected power > 0'); if (power < _0n) throw new Error('Expected power > 0');
@ -280,7 +280,7 @@ export function FpPow<T>(f: Field<T>, num: T, power: bigint): T {
return p; return p;
} }
export function FpInvertBatch<T>(f: Field<T>, nums: T[]): T[] { export function FpInvertBatch<T>(f: IField<T>, nums: T[]): T[] {
const tmp = new Array(nums.length); const tmp = new Array(nums.length);
// Walk from first to last, multiply them by each other MOD p // Walk from first to last, multiply them by each other MOD p
const lastMultiplied = nums.reduce((acc, num, i) => { const lastMultiplied = nums.reduce((acc, num, i) => {
@ -299,12 +299,12 @@ export function FpInvertBatch<T>(f: Field<T>, nums: T[]): T[] {
return tmp; return tmp;
} }
export function FpDiv<T>(f: Field<T>, lhs: T, rhs: T | bigint): T { export function FpDiv<T>(f: IField<T>, lhs: T, rhs: T | bigint): T {
return f.mul(lhs, typeof rhs === 'bigint' ? invert(rhs, f.ORDER) : f.inv(rhs)); return f.mul(lhs, typeof rhs === 'bigint' ? invert(rhs, f.ORDER) : f.inv(rhs));
} }
// This function returns True whenever the value x is a square in the field F. // This function returns True whenever the value x is a square in the field F.
export function FpIsSquare<T>(f: Field<T>) { export function FpIsSquare<T>(f: IField<T>) {
const legendreConst = (f.ORDER - _1n) / _2n; // Integer arithmetic const legendreConst = (f.ORDER - _1n) / _2n; // Integer arithmetic
return (x: T): boolean => { return (x: T): boolean => {
const p = f.pow(x, legendreConst); const p = f.pow(x, legendreConst);
@ -320,16 +320,24 @@ export function nLength(n: bigint, nBitLength?: number) {
return { nBitLength: _nBitLength, nByteLength }; return { nBitLength: _nBitLength, nByteLength };
} }
// NOTE: very fragile, always bench. Major performance points: type FpField = IField<bigint> & Required<Pick<IField<bigint>, 'isOdd'>>;
// - NonNormalized ops /**
// - Object.freeze * Initializes a galois field over prime. Non-primes are not supported for now.
// - same shape of object (don't add/remove keys) * Do not init in loop: slow. Very fragile: always run a benchmark on change.
type FpField = Field<bigint> & Required<Pick<Field<bigint>, 'isOdd'>>; * Major performance gains:
export function Fp( * a) non-normalized operations like mulN instead of mul
* b) `Object.freeze`
* c) Same object shape: never add or remove keys
* @param ORDER prime positive bigint
* @param bitLen how many bits the field consumes
* @param isLE (def: false) if encoding / decoding should be in little-endian
* @param redef optional faster redefinitions of sqrt and other methods
*/
export function Field(
ORDER: bigint, ORDER: bigint,
bitLen?: number, bitLen?: number,
isLE = false, isLE = false,
redef: Partial<Field<bigint>> = {} redef: Partial<IField<bigint>> = {}
): Readonly<FpField> { ): Readonly<FpField> {
if (ORDER <= _0n) throw new Error(`Expected Fp ORDER > 0, got ${ORDER}`); if (ORDER <= _0n) throw new Error(`Expected Fp ORDER > 0, got ${ORDER}`);
const { nBitLength: BITS, nByteLength: BYTES } = nLength(ORDER, bitLen); const { nBitLength: BITS, nByteLength: BYTES } = nLength(ORDER, bitLen);
@ -382,13 +390,13 @@ export function Fp(
return Object.freeze(f); return Object.freeze(f);
} }
export function FpSqrtOdd<T>(Fp: Field<T>, elm: T) { export function FpSqrtOdd<T>(Fp: IField<T>, elm: T) {
if (!Fp.isOdd) throw new Error(`Field doesn't have isOdd`); if (!Fp.isOdd) throw new Error(`Field doesn't have isOdd`);
const root = Fp.sqrt(elm); const root = Fp.sqrt(elm);
return Fp.isOdd(root) ? root : Fp.neg(root); return Fp.isOdd(root) ? root : Fp.neg(root);
} }
export function FpSqrtEven<T>(Fp: Field<T>, elm: T) { export function FpSqrtEven<T>(Fp: IField<T>, elm: T) {
if (!Fp.isOdd) throw new Error(`Field doesn't have isOdd`); if (!Fp.isOdd) throw new Error(`Field doesn't have isOdd`);
const root = Fp.sqrt(elm); const root = Fp.sqrt(elm);
return Fp.isOdd(root) ? Fp.neg(root) : root; return Fp.isOdd(root) ? Fp.neg(root) : root;

@ -1,10 +1,10 @@
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
// Poseidon Hash: https://eprint.iacr.org/2019/458.pdf, https://www.poseidon-hash.info // Poseidon Hash: https://eprint.iacr.org/2019/458.pdf, https://www.poseidon-hash.info
import { Field, FpPow, validateField } from './modular.js'; import { IField, FpPow, validateField } from './modular.js';
// We don't provide any constants, since different implementations use different constants. // We don't provide any constants, since different implementations use different constants.
// For reference constants see './test/poseidon.test.js'. // For reference constants see './test/poseidon.test.js'.
export type PoseidonOpts = { export type PoseidonOpts = {
Fp: Field<bigint>; Fp: IField<bigint>;
t: number; t: number;
roundsFull: number; roundsFull: number;
roundsPartial: number; roundsPartial: number;

@ -670,8 +670,7 @@ export type CurveFn = {
export function weierstrass(curveDef: CurveType): CurveFn { export function weierstrass(curveDef: CurveType): CurveFn {
const CURVE = validateOpts(curveDef) as ReturnType<typeof validateOpts>; const CURVE = validateOpts(curveDef) as ReturnType<typeof validateOpts>;
const CURVE_ORDER = CURVE.n; const { Fp, n: CURVE_ORDER } = CURVE;
const Fp = CURVE.Fp;
const compressedLen = Fp.BYTES + 1; // e.g. 33 for 32 const compressedLen = Fp.BYTES + 1; // e.g. 33 for 32
const uncompressedLen = 2 * Fp.BYTES + 1; // e.g. 65 for 32 const uncompressedLen = 2 * Fp.BYTES + 1; // e.g. 65 for 32
@ -1076,7 +1075,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
// TODO: check if there is a way to merge this with uvRatio in Edwards && move to modular? // TODO: check if there is a way to merge this with uvRatio in Edwards && move to modular?
// b = True and y = sqrt(u / v) if (u / v) is square in F, and // b = True and y = sqrt(u / v) if (u / v) is square in F, and
// b = False and y = sqrt(Z * (u / v)) otherwise. // b = False and y = sqrt(Z * (u / v)) otherwise.
export function SWUFpSqrtRatio<T>(Fp: mod.Field<T>, Z: T) { export function SWUFpSqrtRatio<T>(Fp: mod.IField<T>, Z: T) {
// Generic implementation // Generic implementation
const q = Fp.ORDER; const q = Fp.ORDER;
let l = 0n; let l = 0n;
@ -1141,7 +1140,7 @@ export function SWUFpSqrtRatio<T>(Fp: mod.Field<T>, Z: T) {
} }
// From draft-irtf-cfrg-hash-to-curve-16 // From draft-irtf-cfrg-hash-to-curve-16
export function mapToCurveSimpleSWU<T>( export function mapToCurveSimpleSWU<T>(
Fp: mod.Field<T>, Fp: mod.IField<T>,
opts: { opts: {
A: T; A: T;
B: T; B: T;

@ -72,13 +72,13 @@ import { isogenyMap } from './abstract/hash-to-curve.js';
// CURVE FIELDS // CURVE FIELDS
// Finite field over p. // Finite field over p.
const Fp = const Fp =
mod.Fp( mod.Field(
0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaabn 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaabn
); );
type Fp = bigint; type Fp = bigint;
// Finite field over r. // Finite field over r.
// This particular field is not used anywhere in bls12-381, but it is still useful. // This particular field is not used anywhere in bls12-381, but it is still useful.
const Fr = mod.Fp(0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001n); const Fr = mod.Field(0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001n);
// Fp₂ over complex plane // Fp₂ over complex plane
type BigintTuple = [bigint, bigint]; type BigintTuple = [bigint, bigint];
@ -124,7 +124,7 @@ const FP2_ORDER =
0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaabn ** 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaabn **
2n; 2n;
const Fp2: mod.Field<Fp2> & Fp2Utils = { const Fp2: mod.IField<Fp2> & Fp2Utils = {
ORDER: FP2_ORDER, ORDER: FP2_ORDER,
BITS: bitLen(FP2_ORDER), BITS: bitLen(FP2_ORDER),
BYTES: Math.ceil(bitLen(FP2_ORDER) / 8), BYTES: Math.ceil(bitLen(FP2_ORDER) / 8),
@ -333,7 +333,7 @@ type Fp6Utils = {
multiplyByFp2(lhs: Fp6, rhs: Fp2): Fp6; multiplyByFp2(lhs: Fp6, rhs: Fp2): Fp6;
}; };
const Fp6: mod.Field<Fp6> & Fp6Utils = { const Fp6: mod.IField<Fp6> & Fp6Utils = {
ORDER: Fp2.ORDER, // TODO: unused, but need to verify ORDER: Fp2.ORDER, // TODO: unused, but need to verify
BITS: 3 * Fp2.BITS, BITS: 3 * Fp2.BITS,
BYTES: 3 * Fp2.BYTES, BYTES: 3 * Fp2.BYTES,
@ -545,7 +545,7 @@ type Fp12Utils = {
_cyclotomicExp(num: Fp12, n: bigint): Fp12; _cyclotomicExp(num: Fp12, n: bigint): Fp12;
}; };
const Fp12: mod.Field<Fp12> & Fp12Utils = { const Fp12: mod.IField<Fp12> & Fp12Utils = {
ORDER: Fp2.ORDER, // TODO: unused, but need to verify ORDER: Fp2.ORDER, // TODO: unused, but need to verify
BITS: 2 * Fp2.BITS, BITS: 2 * Fp2.BITS,
BYTES: 2 * Fp2.BYTES, BYTES: 2 * Fp2.BYTES,

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

@ -3,7 +3,7 @@ import { sha512 } from '@noble/hashes/sha512';
import { concatBytes, randomBytes, utf8ToBytes } from '@noble/hashes/utils'; import { concatBytes, randomBytes, utf8ToBytes } from '@noble/hashes/utils';
import { twistedEdwards, ExtPointType } from './abstract/edwards.js'; import { twistedEdwards, ExtPointType } from './abstract/edwards.js';
import { montgomery } from './abstract/montgomery.js'; import { montgomery } from './abstract/montgomery.js';
import { mod, pow2, isNegativeLE, Fp as Field, FpSqrtEven } from './abstract/modular.js'; import { mod, pow2, isNegativeLE, Field, FpSqrtEven } from './abstract/modular.js';
import { import {
equalBytes, equalBytes,
bytesToHex, bytesToHex,

@ -2,7 +2,7 @@
import { shake256 } from '@noble/hashes/sha3'; import { shake256 } from '@noble/hashes/sha3';
import { concatBytes, randomBytes, utf8ToBytes, wrapConstructor } from '@noble/hashes/utils'; import { concatBytes, randomBytes, utf8ToBytes, wrapConstructor } from '@noble/hashes/utils';
import { twistedEdwards } from './abstract/edwards.js'; import { twistedEdwards } from './abstract/edwards.js';
import { mod, pow2, Fp as Field } from './abstract/modular.js'; import { mod, pow2, Field } from './abstract/modular.js';
import { montgomery } from './abstract/montgomery.js'; import { montgomery } from './abstract/montgomery.js';
import * as htf from './abstract/hash-to-curve.js'; import * as htf from './abstract/hash-to-curve.js';

@ -3,7 +3,7 @@ import { sha512 } from '@noble/hashes/sha512';
import { concatBytes, randomBytes, utf8ToBytes } from '@noble/hashes/utils'; import { concatBytes, randomBytes, utf8ToBytes } from '@noble/hashes/utils';
import { twistedEdwards } from './abstract/edwards.js'; import { twistedEdwards } from './abstract/edwards.js';
import { blake2s } from '@noble/hashes/blake2s'; import { blake2s } from '@noble/hashes/blake2s';
import { Fp } from './abstract/modular.js'; import { Field } from './abstract/modular.js';
/** /**
* jubjub Twisted Edwards curve. * jubjub Twisted Edwards curve.
@ -17,7 +17,7 @@ export const jubjub = twistedEdwards({
d: BigInt('0x2a9318e74bfa2b48f5fd9207e6bd7fd4292d7f6d37579d2601065fd6d6343eb1'), d: BigInt('0x2a9318e74bfa2b48f5fd9207e6bd7fd4292d7f6d37579d2601065fd6d6343eb1'),
// Finite field 𝔽p over which we'll do calculations // Finite field 𝔽p over which we'll do calculations
// Same value as bls12-381 Fr (not Fp) // Same value as bls12-381 Fr (not Fp)
Fp: Fp(BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001')), Fp: Field(BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001')),
// Subgroup order: how many points curve has // Subgroup order: how many points curve has
n: BigInt('0xe7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7'), n: BigInt('0xe7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7'),
// Cofactor // Cofactor

@ -1,7 +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 as Field } from './abstract/modular.js'; import { Field } from './abstract/modular.js';
import { mapToCurveSimpleSWU } from './abstract/weierstrass.js'; import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
import * as htf from './abstract/hash-to-curve.js'; import * as htf from './abstract/hash-to-curve.js';

@ -1,7 +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 { sha384 } from '@noble/hashes/sha512'; import { sha384 } from '@noble/hashes/sha512';
import { Fp as Field } from './abstract/modular.js'; import { Field } from './abstract/modular.js';
import { mapToCurveSimpleSWU } from './abstract/weierstrass.js'; import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
import * as htf from './abstract/hash-to-curve.js'; import * as htf from './abstract/hash-to-curve.js';

@ -1,7 +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 { sha512 } from '@noble/hashes/sha512'; import { sha512 } from '@noble/hashes/sha512';
import { Fp as Field } from './abstract/modular.js'; import { Field } from './abstract/modular.js';
import { mapToCurveSimpleSWU } from './abstract/weierstrass.js'; import { mapToCurveSimpleSWU } from './abstract/weierstrass.js';
import * as htf from './abstract/hash-to-curve.js'; import * as htf from './abstract/hash-to-curve.js';

@ -11,7 +11,7 @@ export const q = BigInt('0x40000000000000000000000000000000224698fc0994a8dd8c46e
export const pallas = weierstrass({ export const pallas = weierstrass({
a: BigInt(0), a: BigInt(0),
b: BigInt(5), b: BigInt(5),
Fp: mod.Fp(p), Fp: mod.Field(p),
n: q, n: q,
Gx: mod.mod(BigInt(-1), p), Gx: mod.mod(BigInt(-1), p),
Gy: BigInt(2), Gy: BigInt(2),
@ -22,7 +22,7 @@ export const pallas = weierstrass({
export const vesta = weierstrass({ export const vesta = weierstrass({
a: BigInt(0), a: BigInt(0),
b: BigInt(5), b: BigInt(5),
Fp: mod.Fp(q), Fp: mod.Field(q),
n: p, n: p,
Gx: mod.mod(BigInt(-1), q), Gx: mod.mod(BigInt(-1), q),
Gy: BigInt(2), Gy: BigInt(2),

@ -1,7 +1,7 @@
/*! 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 { randomBytes } from '@noble/hashes/utils'; import { randomBytes } from '@noble/hashes/utils';
import { Fp as Field, mod, pow2 } from './abstract/modular.js'; import { Field, mod, pow2 } from './abstract/modular.js';
import { ProjPointType as PointType, mapToCurveSimpleSWU } from './abstract/weierstrass.js'; import { ProjPointType as PointType, mapToCurveSimpleSWU } from './abstract/weierstrass.js';
import type { Hex, PrivKey } from './abstract/utils.js'; import type { Hex, PrivKey } from './abstract/utils.js';
import { bytesToNumberBE, concatBytes, ensureBytes, numberToBytesBE } from './abstract/utils.js'; import { bytesToNumberBE, concatBytes, ensureBytes, numberToBytesBE } from './abstract/utils.js';
@ -43,7 +43,6 @@ function sqrtMod(y: bigint): bigint {
} }
const Fp = Field(secp256k1P, undefined, undefined, { sqrt: sqrtMod }); const Fp = Field(secp256k1P, undefined, undefined, { sqrt: sqrtMod });
type Fp = bigint;
export const secp256k1 = createCurve( export const secp256k1 = createCurve(
{ {
@ -245,7 +244,7 @@ const isoMap = htf.isogenyMap(
'0x6484aa716545ca2cf3a70c3fa8fe337e0a3d21162f0d6299a7bf8192bfd2a76f', '0x6484aa716545ca2cf3a70c3fa8fe337e0a3d21162f0d6299a7bf8192bfd2a76f',
'0x0000000000000000000000000000000000000000000000000000000000000001', // LAST 1 '0x0000000000000000000000000000000000000000000000000000000000000001', // LAST 1
], ],
].map((i) => i.map((j) => BigInt(j))) as [Fp[], Fp[], Fp[], Fp[]] ].map((i) => i.map((j) => BigInt(j))) as [bigint[], bigint[], bigint[], bigint[]]
); );
const mapSWU = mapToCurveSimpleSWU(Fp, { const mapSWU = mapToCurveSimpleSWU(Fp, {
A: BigInt('0x3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533'), A: BigInt('0x3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533'),

@ -1,7 +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 '../esm/_shortw_utils.js'; import { createCurve } from '../esm/_shortw_utils.js';
import { sha224, sha256 } from '@noble/hashes/sha256'; import { sha224, sha256 } from '@noble/hashes/sha256';
import { Fp } from '../esm/abstract/modular.js'; import { Field as Fp } from '../esm/abstract/modular.js';
// NIST secp192r1 aka P192 // NIST secp192r1 aka P192
// https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/secg/secp192r1 // https://www.secg.org/sec2-v2.pdf, https://neuromancer.sk/std/secg/secp192r1

@ -1,7 +1,7 @@
/*! 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 { utf8ToBytes } from '@noble/hashes/utils'; import { utf8ToBytes } from '@noble/hashes/utils';
import { Fp, validateField } from '../esm/abstract/modular.js'; import { Field as Fp, validateField } from '../esm/abstract/modular.js';
import { poseidon } from '../esm/abstract/poseidon.js'; import { poseidon } from '../esm/abstract/poseidon.js';
import * as u from '../esm/abstract/utils.js'; import * as u from '../esm/abstract/utils.js';

@ -553,6 +553,7 @@ for (const name in CURVES) {
}); });
} }
describe(name, () => { describe(name, () => {
if (['bn254', 'pallas', 'vesta'].includes(name)) return;
// Generic complex things (getPublicKey/sign/verify/getSharedSecret) // Generic complex things (getPublicKey/sign/verify/getSharedSecret)
should('.getPublicKey() type check', () => { should('.getPublicKey() type check', () => {
throws(() => C.getPublicKey(0), '0'); throws(() => C.getPublicKey(0), '0');

@ -8,7 +8,8 @@ import './ed25519.test.js';
import './secp256k1.test.js'; import './secp256k1.test.js';
import './secp256k1-schnorr.test.js'; import './secp256k1-schnorr.test.js';
import './jubjub.test.js'; import './jubjub.test.js';
import './bls12-381.test.js';
import './hash-to-curve.test.js'; import './hash-to-curve.test.js';
import './poseidon.test.js';
import './bls12-381.test.js';
should.run(); should.run();

@ -132,7 +132,9 @@ describe('Stark', () => {
// Official vectors: https://extgit.iaik.tugraz.at/krypto/hadeshash/-/blob/master/code/test_vectors.txt // Official vectors: https://extgit.iaik.tugraz.at/krypto/hadeshash/-/blob/master/code/test_vectors.txt
should('poseidonperm_x5_255_3', () => { should('poseidonperm_x5_255_3', () => {
const Fp = mod.Fp(BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001')); const Fp = mod.Field(
BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001')
);
const mds = [ const mds = [
[ [
@ -179,7 +181,7 @@ should('poseidonperm_x5_255_3', () => {
}); });
should('poseidonperm_x5_255_5', () => { should('poseidonperm_x5_255_5', () => {
const Fp = mod.Fp(0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001n); const Fp = mod.Field(0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001n);
const t = 5; const t = 5;
const mds = [ const mds = [
@ -250,7 +252,7 @@ should('poseidonperm_x5_255_5', () => {
}); });
should('poseidonperm_x5_254_3', () => { should('poseidonperm_x5_254_3', () => {
const Fp = mod.Fp(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001n); const Fp = mod.Field(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001n);
const t = 3; const t = 3;
const mds = [ const mds = [
@ -297,7 +299,7 @@ should('poseidonperm_x5_254_3', () => {
}); });
should('poseidonperm_x5_254_5', () => { should('poseidonperm_x5_254_5', () => {
const Fp = mod.Fp(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001n); const Fp = mod.Field(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001n);
const t = 5; const t = 5;
const mds = [ const mds = [