Merge branch 'main' into lsankar/membership-verifier

This commit is contained in:
Lakshman Sankar
2023-01-30 13:16:25 -08:00
committed by GitHub
52 changed files with 397 additions and 4074 deletions

4
.gitignore vendored
View File

@@ -34,4 +34,6 @@ packages/prover/test_circuit/test_circuit_js/
#input files
packages/prover/test_circuit/*.json
packages/lib/src/circuits/
packages/lib/src/circuits/
packages/poseidon/sage/*.py

View File

@@ -1,7 +1,6 @@
[workspace]
members = [
"packages/spartan_wasm",
"packages/secpq_curves",
"packages/secq256k1",
"packages/poseidon",
]

View File

@@ -5,6 +5,9 @@
"main": "index.js",
"repository": "https://github.com/DanTehrani/spartan-wasm.git",
"author": "Daniel Tehrani <contact@dantehrani.com>",
"scripts": {
"build": "sh ./scripts/build.sh && lerna run build"
},
"devDependencies": {
"@types/jest": "^29.2.4",
"@typescript-eslint/eslint-plugin": "5.49.0",

View File

@@ -8,7 +8,7 @@
},
"dependencies": {
"@ethereumjs/util": "^8.0.3",
"spartan-ecdsa": "*"
"@personaelabs/spartan-ecdsa": "*"
},
"devDependencies": {
"ts-node": "^10.9.1",

View File

@@ -12,7 +12,7 @@ import {
defaultWasmConfig,
MembershipVerifier,
defaultAddressMembershipVConfig
} from "spartan-ecdsa";
} from "@personaelabs/spartan-ecdsa";
const benchAddrMembership = async () => {
const privKey = Buffer.from("".padStart(16, "🧙"), "utf16le");

View File

@@ -7,7 +7,7 @@ import {
defaultPubkeyMembershipPConfig,
defaultPubkeyMembershipVConfig,
MembershipVerifier
} from "spartan-ecdsa";
} from "@personaelabs/spartan-ecdsa";
import {
hashPersonalMessage,
ecsign,

View File

@@ -9,7 +9,7 @@
"lint": "next lint"
},
"dependencies": {
"spartan-ecdsa": "*",
"@personaelabs/spartan-ecdsa": "*",
"@ethereumjs/util": "^8.0.3",
"comlink": "^4.3.1",
"elliptic": "^6.5.4",

View File

@@ -10,7 +10,7 @@ import {
defaultPubkeyMembershipPConfig,
defaultPubkeyMembershipVConfig,
defaultAddressMembershipVConfig
} from "spartan-ecdsa";
} from "@personaelabs/spartan-ecdsa";
import {
ecrecover,
ecsign,

View File

@@ -12,7 +12,7 @@
"elliptic": "^6.5.4"
},
"devDependencies": {
"spartan-ecdsa": "*",
"@personaelabs/spartan-ecdsa": "*",
"@zk-kit/incremental-merkle-tree": "^1.0.0",
"jest": "^29.3.1",
"ts-jest": "^29.0.3",

View File

@@ -198,19 +198,19 @@ function ROUND_KEYS() {
function MDS_MATRIX() {
return [
[
23803367313109184982541472396887217823407491505476035214597249950862491174978,
99598264738478514965466329604335101878578484066948401815632841927360380418550,
115153730744008470922201668532093804785974940215174148228638117153769917815388
92469348809186613947252340883344274339611751744959319352506666082431267346705,
100938028378191533449096235266991198229563815869344032449592738345766724371160,
77486311749148948616988559783475694076613010381924638436641318334458515006661
],
[
113091279114074197935538706369506905304335018116466406918080279636714229809202,
52839207077696265966109252614080731042198774602627516394420475307554904992516,
31714026868000093936404190818455331704037953726328786865383369149182617311729
110352262556914082363749654180080464794716701228558638957603951672835474954408,
27607004873684391669404739690441550149894883072418944161048725383958774443141,
29671705769502357195586268679831947082918094959101307962374709600277676341325
],
[
70589234453346098611775931063930029749141121635522432940893113186813327872540,
89267224805530783265612822042944818966269909370588027009288423799106976950833,
13120047990786604980839974606890144531075498455983768009875235935875059189507
77762103796341032609398578911486222569419103128091016773380377798879650228751,
1753012011204964731088925227042671869111026487299375073665493007998674391999,
70274477372358662369456035572054501601454406272695978931839980644925236550307
]
];
}

View File

@@ -2,7 +2,12 @@ const wasm_tester = require("circom_tester").wasm;
var EC = require("elliptic").ec;
import * as path from "path";
const ec = new EC("secp256k1");
import { Poseidon, Tree, SpartanWasm, defaultWasmConfig } from "spartan-ecdsa";
import {
Poseidon,
Tree,
SpartanWasm,
defaultWasmConfig
} from "@personaelabs/spartan-ecdsa";
import { getEffEcdsaCircuitInput } from "./test_utils";
import { privateToAddress } from "@ethereumjs/util";

View File

@@ -20,7 +20,7 @@ describe("poseidon", () => {
const w = await circuit.calculateWitness(input, true);
await circuit.assertOut(w, {
out: "55864140790032987462805271262840606862500777900572169165625301625084550490622"
out: "46702443887670435486723478191273607819169644657419964658749776213559127696053"
});
await circuit.checkConstraints(w);

View File

@@ -1,7 +1,12 @@
const wasm_tester = require("circom_tester").wasm;
var EC = require("elliptic").ec;
import * as path from "path";
import { Poseidon, Tree, SpartanWasm, defaultWasmConfig } from "spartan-ecdsa";
import {
Poseidon,
Tree,
SpartanWasm,
defaultWasmConfig
} from "@personaelabs/spartan-ecdsa";
import { privateToPublic } from "@ethereumjs/util";
import { getEffEcdsaCircuitInput } from "./test_utils";

View File

@@ -1,5 +1,5 @@
import { hashPersonalMessage, ecsign } from "@ethereumjs/util";
import { EffEcdsaCircuitPubInput } from "spartan-ecdsa";
import { EffEcdsaCircuitPubInput } from "@personaelabs/spartan-ecdsa";
export const getEffEcdsaCircuitInput = (privKey: Buffer, msg: Buffer) => {
const msgHash = hashPersonalMessage(msg);

6
packages/lib/.npmignore Normal file
View File

@@ -0,0 +1,6 @@
/node_modules
/src
/tests
tsconfig.json
jest.config.js
copy_artifacts.sh

View File

@@ -64,7 +64,7 @@ import {
MembershipProver,
defaultAddressMembershipConfig,
defaultWasmConfig
} from "spartan-ecdsa";
} from "@personaelabs/spartan-ecdsa";
const privKey = Buffer.from("".padStart(16, "🧙"), "utf16le");
const msg = Buffer.from("harry potter");

View File

@@ -1,11 +1,16 @@
{
"name": "spartan-ecdsa",
"version": "1.0.0",
"name": "@personaelabs/spartan-ecdsa",
"version": "0.0.4",
"main": "./build/lib.js",
"types": "./build/lib.d.ts",
"license": "MIT",
"files": [
"build/**/*"
],
"scripts": {
"build": "tsc && sh ./copy_artifacts.sh",
"test": "jest"
"prepublishOnly": "yarn build"
},
"devDependencies": {
"@types/jest": "^29.2.5",

View File

@@ -40,6 +40,10 @@ export class MembershipProver extends Profiler implements IProver {
msgHash: Buffer,
merkleProof: MerkleProof
): Promise<NIZK> {
if (typeof this.spartanWasm === "undefined") {
throw new Error("wasm not initialized. Please call initWasm().");
}
const { r, s, v } = fromSig(sig);
const circuitPubInput = EffEcdsaCircuitPubInput.computeFromSig(

View File

@@ -10,7 +10,14 @@ export class Poseidon {
await this.wasm.init();
}
assertInitWasm() {
if (typeof this.wasm === "undefined") {
throw new Error("wasm not initialized. Please call initWasm().");
}
}
hash(inputs: bigint[]): bigint {
this.assertInitWasm();
const inputsBytes = new Uint8Array(32 * inputs.length);
for (let i = 0; i < inputs.length; i++) {
inputsBytes.set(bigIntToLeBytes(inputs[i], 32), i * 32);
@@ -21,6 +28,7 @@ export class Poseidon {
}
hashPubKey(pubKey: Buffer): bigint {
this.assertInitWasm();
const pubKeyX = BigInt("0x" + pubKey.toString("hex").slice(0, 64));
const pubKeyY = BigInt("0x" + pubKey.toString("hex").slice(64, 128));

View File

@@ -10,9 +10,7 @@ hex = "0.4.3"
hex-literal = "0.3.4"
secq256k1 = { path = "../secq256k1" }
getrandom = { version = "0.2.8", features = ["js"] }
[target.'cfg(not(target_family = "wasm"))'.dependencies]
typenum = "1.16.0"
neptune = "8.1.0"
blstrs = { version = "0.6.0" }
#typenum = { version = "1.16.0", optional = true }
#neptune = { version = "8.1.0", optional = true }
#blstrs = { version = "0.6.0", optional = true }

View File

@@ -1,4 +1,18 @@
Generate Poseidon params for the secp256k1 base field
```
sh ./k256_params.sh
```
```
## Parameters
We use the following parameters for our Poseidon instantiation (using the notation from the [Neptune specification](https://spec.filecoin.io/#section-algorithms.crypto.poseidon)). Security inequalities are checked in [security_inequalities.sage](https://github.com/personaelabs/spartan-ecdsa/blob/f6ffbb4fc8977c4e30ae6df4eba6f1da0c534722/packages/poseidon/sage/security_inequalities.sage).
```
M=128
t=3
p=0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f
Rf=8
Rp=56
a=5
```

View File

@@ -1 +1 @@
sage generate_params_poseidon.sage 1 0 256 3 5 128 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
sage ./sage/generate_params_poseidon.sage 1 0 256 3 5 128 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f

View File

@@ -1,3 +1,5 @@
# https://extgit.iaik.tugraz.at/krypto/hadeshash/-/blob/master/code/generate_params_poseidon.sage
from math import *
import sys
#from sage.rings.polynomial.polynomial_gf2x import GF2X_BuildIrred_list

View File

@@ -0,0 +1,20 @@
# Check security inequalities as specified in the Neptune specification
M=128
t=3
p=0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f
Rf=8
Rp=56
R=Rf + Rp
a=5
# this is defined in Section 5.5.1 https://eprint.iacr.org/2019/458.pdf
# (a = 5 then C = 2)
C = 2
# https://spec.filecoin.io/#section-algorithms.crypto.poseidon.security-inequalities
print("(1) 2^M <= p^t", 2^M <= p^t)
print("(2) M <= (⌊log2(p) - C)⌋・(t + 1)", M <= (floor(log(p, 2)).n() - C) * (t + 1)) # Section 5.5.1 https://eprint.iacr.org/2019/458.pdf
print("(3) R > M(log_a(2) + log_a(t)))", R > (M * log(2, a).n()) + log(t, a).n())
print("(4a) R > M * log(2, a).n() / 3", R > (M * log(2, a).n()) / 3)
print("(4b) R > t - 1 + (M * log(2, a).n() / (t + 1))", R > t - 1 + (M * log(2, a).n() / (t + 1)))

View File

@@ -3,19 +3,19 @@ pub const NUM_PARTIAL_ROUNDS: usize = 56;
pub static MDS_MATRIX: &'static [[&str; 3]; 3] = &[
[
"23803367313109184982541472396887217823407491505476035214597249950862491174978",
"99598264738478514965466329604335101878578484066948401815632841927360380418550",
"115153730744008470922201668532093804785974940215174148228638117153769917815388",
"92469348809186613947252340883344274339611751744959319352506666082431267346705",
"100938028378191533449096235266991198229563815869344032449592738345766724371160",
"77486311749148948616988559783475694076613010381924638436641318334458515006661",
],
[
"113091279114074197935538706369506905304335018116466406918080279636714229809202",
"52839207077696265966109252614080731042198774602627516394420475307554904992516",
"31714026868000093936404190818455331704037953726328786865383369149182617311729",
"110352262556914082363749654180080464794716701228558638957603951672835474954408",
"27607004873684391669404739690441550149894883072418944161048725383958774443141",
"29671705769502357195586268679831947082918094959101307962374709600277676341325",
],
[
"70589234453346098611775931063930029749141121635522432940893113186813327872540",
"89267224805530783265612822042944818966269909370588027009288423799106976950833",
"13120047990786604980839974606890144531075498455983768009875235935875059189507",
"77762103796341032609398578911486222569419103128091016773380377798879650228751",
"1753012011204964731088925227042671869111026487299375073665493007998674391999",
"70274477372358662369456035572054501601454406272695978931839980644925236550307",
],
];

View File

@@ -99,7 +99,7 @@ impl<F: PrimeField> Poseidon<F> {
// S-boxes
for i in 0..t {
self.state[i] = self.state[i].pow_vartime(&[5]);
self.state[i] = self.state[i].pow_vartime(&[5, 0, 0, 0]);
}
self.matrix_mul();
@@ -112,7 +112,7 @@ impl<F: PrimeField> Poseidon<F> {
self.add_constants();
// S-box
self.state[0] = self.state[0].pow_vartime(&[5]);
self.state[0] = self.state[0].pow_vartime(&[5, 0, 0, 0]);
self.matrix_mul();
@@ -124,7 +124,6 @@ impl<F: PrimeField> Poseidon<F> {
#[cfg(test)]
mod tests {
use super::*;
use blstrs;
use ff::Field;
use secq256k1::field::field_secp;
@@ -164,18 +163,22 @@ mod tests {
assert_eq!(
digest,
Scalar::from_bytes(&[
55, 65, 152, 83, 150, 215, 77, 156, 41, 188, 147, 242, 103, 178, 202, 106, 26, 32,
186, 27, 179, 162, 21, 251, 12, 220, 119, 154, 61, 114, 65, 138
68, 120, 17, 40, 199, 247, 48, 80, 236, 89, 92, 44, 207, 217, 83, 62, 184, 194,
173, 48, 66, 119, 238, 98, 175, 232, 78, 234, 75, 101, 229, 148
])
.unwrap()
);
}
use neptune::poseidon::{Poseidon as NeptunePoseidon, PoseidonConstants as NeptuneConstants};
use typenum::U2;
/*
#[test]
fn test_bls() {
use blstrs;
use neptune::poseidon::{
Poseidon as NeptunePoseidon, PoseidonConstants as NeptuneConstants,
};
use typenum::U2;
type Scalar = blstrs::Scalar;
let input = vec![Scalar::one(), Scalar::zero()];
@@ -198,4 +201,5 @@ mod tests {
// Check that the two implementations produce the same output
assert_eq!(digest, np_digest);
}
*/
}

View File

@@ -1,62 +0,0 @@
[package]
name = "secpq_curves"
version = "0.2.1"
authors = [
"Sean Bowe <ewillbefull@gmail.com>",
"Jack Grigg <jack@z.cash>",
"Alex Vlasov <alex.m.vlasov@gmail.com>",
"Alex Gluchowski <alex@gluchowski.net>"
]
license = "MIT/Apache-2.0"
edition = "2018"
repository = "https://github.com/kilic/pairing"
readme = "README.md"
description = "Elliptic curve implementations and wrappers for halo2 library"
[dev-dependencies]
criterion = { version = "0.3", features = ["html_reports"] }
rand_xorshift = "0.3"
ark-std = { version = "0.3", features = ["print-trace"] }
[dependencies]
subtle = "2.4"
ff = "0.12.0"
group = "0.12.0"
pasta_curves = "0.4.0"
static_assertions = "1.1.0"
rand = "0.8"
rand_core = { version = "0.6", default-features = false }
num-bigint = "0.4.3"
num-traits = "0.2"
# alloc dependencies
blake2b_simd = { version = "1", optional = true, default-features = false }
# sqrt-table dependencies
lazy_static = { version = "1.4.0", optional = true }
# gpu dependencies
ec-gpu = { version = "0.2.0", optional = true }
# serde dependencies
serde_crate = { version = "1.0.16", optional = true, default-features = false, features = ["alloc"], package = "serde" }
hex = { version = "0.4", optional = true, default-features = false, features = ["alloc", "serde"] }
[features]
default = ["bits", "sqrt-table"]
alloc = ["group/alloc", "blake2b_simd"]
bits = ["ff/bits"]
gpu = ["alloc", "ec-gpu"]
sqrt-table = ["alloc", "lazy_static"]
repr-c = []
uninline-portable = []
serde = ["hex", "serde_crate"]
[profile.bench]
opt-level = 3
debug = false
debug-assertions = false
overflow-checks = false
lto = true
incremental = false
codegen-units = 1

View File

@@ -1 +0,0 @@
Fork of [Vivek's secp/secq implementation](https://github.com/personaelabs/halo2-secp/tree/add/secp_hash_to_curve). The only difference being the version specified in rust-toolchain.

View File

@@ -1,58 +0,0 @@
import sage.schemes.elliptic_curves.isogeny_small_degree as isd
p = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f
print(p)
Fp = GF(p)
q = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
print(q)
Fq = GF(q)
pA = Fp(0x0000000000000000000000000000000000000000000000000000000000000000)
pB = Fp(0x0000000000000000000000000000000000000000000000000000000000000007)
Ep = EllipticCurve(Fp, (pA, pB))
Gp = Ep(0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798, 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8)
print("Order of Ep is q:", Ep.order() == q)
qA = Fq(0x0000000000000000000000000000000000000000000000000000000000000000)
qB = Fq(0x0000000000000000000000000000000000000000000000000000000000000007)
Eq = EllipticCurve(Fq, (qA, qB))
Gq = Eq(0x9214b8774eb1412be7590cbef17c26fc2cabb9347a25101b860fee175831bb20, 0x28cb5b51a30b5532ecc5b53440a7598a836a6e341a88892a14a1bc519466eb6b)
print("Order of Eq is p:", Eq.order() == p)
def find_iso(E):
for p_test in primes(30):
isos = [ i for i in isd.isogenies_prime_degree(E, p_test) if i.codomain().j_invariant() not in (0, 1728) ]
if len(isos) > 0:
return isos[0].dual()
return None
IsoEpA = 0x3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533
IsoEpB = 1771
IsoEp = EllipticCurve(GF(p), [IsoEpA, IsoEpB])
isogeny_ep = EllipticCurveIsogeny(E=Ep, kernel=None, codomain=IsoEp, degree=3).dual()
print(isogeny_ep.rational_maps())
isogeny_eq = find_iso(Eq)
print([k for k in isogeny_eq.rational_maps()])
def find_z_sswu(F, A, B):
R.<xx> = F[] # Polynomial ring over F
g = xx^3 + F(A) * xx + F(B) # y^2 = g(x) = x^3 + A * x + B
ctr = F.gen()
while True:
for Z_cand in (F(ctr), F(-ctr)):
# Criterion 1: Z is non-square in F.
if is_square(Z_cand):
continue
# Criterion 2: Z != -1 in F.
if Z_cand == F(-1):
continue
# Criterion 3: g(x) - Z is irreducible over F.
if not (g - Z_cand).is_irreducible():
continue
# Criterion 4: g(B / (Z * A)) is square in F.
if is_square(g(B / (Z_cand * A))):
return Z_cand
ctr += 1
print("Z for IsoEp", find_z_sswu(Fp, IsoEpA, IsoEpB), "=", Integer(find_z_sswu(Fp, IsoEpA, IsoEpB)) - Integer(p))
print("Z for IsoEq", find_z_sswu(Fq, isogeny_eq.domain().a4(), isogeny_eq.domain().a6()), "=", Integer(find_z_sswu(Fq, isogeny_eq.domain().a4(), isogeny_eq.domain().a6())) - Integer(q))

View File

@@ -1,27 +0,0 @@
function bigint_to_array(n: number, k: number, x: bigint) {
let mod: bigint = BigInt(1);
for (var idx = 0; idx < n; idx++) {
mod = mod * BigInt(2);
}
let ret: bigint[] = [];
var x_temp: bigint = x;
for (var idx = 0; idx < k; idx++) {
ret.push(x_temp % mod);
x_temp = x_temp / mod;
}
return ret;
}
let values = [
"60197513588986302554485582024885075108884032450952339817679072026166228089408",
"37718080363155996902926221483475020450927657555482586988616620542887997980018",
];
let output = "";
for (let idx = 0; idx < values.length; idx++) {
let conv = bigint_to_array(64, 4, BigInt(values[idx]));
output += "Fq::from_raw([" + conv + "]),\n";
}
console.log(output);

View File

@@ -1,105 +0,0 @@
//! This module provides common utilities, traits and structures for group and
//! field arithmetic.
//!
//! This module is temporary, and the extension traits defined here are expected to be
//! upstreamed into the `ff` and `group` crates after some refactoring.
use subtle::{Choice, ConditionallySelectable, CtOption};
pub(crate) fn sqrt_tonelli_shanks<F: ff::PrimeField, S: AsRef<[u64]>>(
f: &F,
tm1d2: S,
) -> CtOption<F> {
use subtle::ConstantTimeEq;
// w = self^((t - 1) // 2)
let w = f.pow_vartime(tm1d2);
let mut v = F::S;
let mut x = w * f;
let mut b = x * w;
// Initialize z as the 2^S root of unity.
let mut z = F::root_of_unity();
for max_v in (1..=F::S).rev() {
let mut k = 1;
let mut tmp = b.square();
let mut j_less_than_v: Choice = 1.into();
for j in 2..max_v {
let tmp_is_one = tmp.ct_eq(&F::one());
let squared = F::conditional_select(&tmp, &z, tmp_is_one).square();
tmp = F::conditional_select(&squared, &tmp, tmp_is_one);
let new_z = F::conditional_select(&z, &squared, tmp_is_one);
j_less_than_v &= !j.ct_eq(&v);
k = u32::conditional_select(&j, &k, tmp_is_one);
z = F::conditional_select(&z, &new_z, j_less_than_v);
}
let result = x * z;
x = F::conditional_select(&result, &x, b.ct_eq(&F::one()));
z = z.square();
b *= z;
v = k;
}
CtOption::new(
x,
(x * x).ct_eq(f), // Only return Some if it's the square root.
)
}
/// Compute a + b + carry, returning the result and the new carry over.
#[inline(always)]
pub(crate) const fn adc(a: u64, b: u64, carry: u64) -> (u64, u64) {
let ret = (a as u128) + (b as u128) + (carry as u128);
(ret as u64, (ret >> 64) as u64)
}
/// Compute a - (b + borrow), returning the result and the new borrow.
#[inline(always)]
pub(crate) const fn sbb(a: u64, b: u64, borrow: u64) -> (u64, u64) {
let ret = (a as u128).wrapping_sub((b as u128) + ((borrow >> 63) as u128));
(ret as u64, (ret >> 64) as u64)
}
/// Compute a + (b * c) + carry, returning the result and the new carry over.
#[inline(always)]
pub(crate) const fn mac(a: u64, b: u64, c: u64, carry: u64) -> (u64, u64) {
let ret = (a as u128) + ((b as u128) * (c as u128)) + (carry as u128);
(ret as u64, (ret >> 64) as u64)
}
/// Compute a + (b * c), returning the result and the new carry over.
#[inline(always)]
pub(crate) const fn macx(a: u64, b: u64, c: u64) -> (u64, u64) {
let res = (a as u128) + ((b as u128) * (c as u128));
(res as u64, (res >> 64) as u64)
}
/// Compute a * b, returning the result.
#[inline(always)]
pub(crate) fn mul_512(a: [u64; 4], b: [u64; 4]) -> [u64; 8] {
let (r0, carry) = macx(0, a[0], b[0]);
let (r1, carry) = macx(carry, a[0], b[1]);
let (r2, carry) = macx(carry, a[0], b[2]);
let (r3, carry_out) = macx(carry, a[0], b[3]);
let (r1, carry) = macx(r1, a[1], b[0]);
let (r2, carry) = mac(r2, a[1], b[1], carry);
let (r3, carry) = mac(r3, a[1], b[2], carry);
let (r4, carry_out) = mac(carry_out, a[1], b[3], carry);
let (r2, carry) = macx(r2, a[2], b[0]);
let (r3, carry) = mac(r3, a[2], b[1], carry);
let (r4, carry) = mac(r4, a[2], b[2], carry);
let (r5, carry_out) = mac(carry_out, a[2], b[3], carry);
let (r3, carry) = macx(r3, a[3], b[0]);
let (r4, carry) = mac(r4, a[3], b[1], carry);
let (r5, carry) = mac(r5, a[3], b[2], carry);
let (r6, carry_out) = mac(carry_out, a[3], b[3], carry);
[r0, r1, r2, r3, r4, r5, r6, carry_out]
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,21 +0,0 @@
//! This module contains implementations for the two finite fields of the
//! Secp256k1 and Secq256k1 curves
mod fp;
mod fq;
pub use fp::*;
pub use fq::*;
/// Converts 64-bit little-endian limbs to 32-bit little endian limbs.
#[cfg(feature = "gpu")]
fn u64_to_u32(limbs: &[u64]) -> alloc::vec::Vec<u32> {
limbs
.iter()
.flat_map(|limb| {
Some((limb & 0xFFFF_FFFF) as u32)
.into_iter()
.chain(Some((limb >> 32) as u32))
})
.collect()
}

View File

@@ -1,343 +0,0 @@
use core::convert::TryInto;
use core::fmt;
use core::ops::{Add, Mul, Neg, Sub};
use ff::{FieldBits, PrimeField, PrimeFieldBits};
use rand::RngCore;
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
use pasta_curves::arithmetic::{FieldExt, Group, SqrtRatio};
use crate::arithmetic::{adc, mac, sbb};
/// This represents an element of $\mathbb{F}_p$ where
///
/// `p = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f`
///
/// is the base field of the secp256k1 curve.
// The internal representation of this type is four 64-bit unsigned
// integers in little-endian order. `Fp` values are always in
// Montgomery form; i.e., Fp(a) = aR mod p, with R = 2^256.
#[derive(Clone, Copy, Eq)]
pub struct Fp(pub(crate) [u64; 4]);
/// Constant representing the modulus
/// p = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f
const MODULUS: Fp = Fp([
0xfffffffefffffc2f,
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff,
]);
/// The modulus as u32 limbs.
#[cfg(not(target_pointer_width = "64"))]
const MODULUS_LIMBS_32: [u32; 8] = [
0xffff_fc2f,
0xffff_fffe,
0xffff_ffff,
0xffff_ffff,
0xffff_ffff,
0xffff_ffff,
0xffff_ffff,
0xffff_ffff,
];
/// Constant representing the modolus as static str
const MODULUS_STR: &str = "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f";
/// INV = -(p^{-1} mod 2^64) mod 2^64
const INV: u64 = 0xd838091dd2253531;
/// R = 2^256 mod p
/// 0x1000003d1
const R: Fp = Fp([0x1000003d1, 0, 0, 0]);
/// R^2 = 2^512 mod p
/// 0x1000007a2000e90a1
const R2: Fp = Fp([0x000007a2000e90a1, 0x1, 0, 0]);
/// R^3 = 2^768 mod p
/// 0x100000b73002bb1e33795f671
const R3: Fp = Fp([0x002bb1e33795f671, 0x100000b73, 0, 0]);
/// `GENERATOR = 3 mod p` is a generator of the `p - 1` order multiplicative
/// subgroup, or in other words a primitive root of the field.
const GENERATOR: Fp = Fp::from_raw([0x03, 0x00, 0x00, 0x00]);
const ROOT_OF_UNITY: Fp = Fp::from_raw([
18446744069414583342,
18446744073709551615,
18446744073709551615,
18446744073709551615,
]);
const ROOT_OF_UNITY_INV: Fp = Fp::from_raw([
18446744069414583342,
18446744073709551615,
18446744073709551615,
18446744073709551615,
]);
/// 1 / 2 mod p
const TWO_INV: Fp = Fp::from_raw([
0xffffffff7ffffe18,
0xffffffffffffffff,
0xffffffffffffffff,
0x7fffffffffffffff,
]);
const ZETA: Fp = Fp::from_raw([
4523465425461901888,
7138124642204153450,
10492182485046905622,
9590017196889995503,
]);
const DELTA: Fp = Fp::from_raw([0x03, 0x00, 0x00, 0x00]).square();
impl_binops_additive!(Fp, Fp);
impl_binops_multiplicative!(Fp, Fp);
field_common!(
Fp,
MODULUS,
INV,
MODULUS_STR,
TWO_INV,
ROOT_OF_UNITY_INV,
DELTA,
ZETA,
R,
R2,
R3
);
field_arithmetic!(Fp, MODULUS, INV, dense);
impl Fp {
pub const fn size() -> usize {
32
}
}
impl ff::Field for Fp {
fn random(mut rng: impl RngCore) -> Self {
Self::from_u512([
rng.next_u64(),
rng.next_u64(),
rng.next_u64(),
rng.next_u64(),
rng.next_u64(),
rng.next_u64(),
rng.next_u64(),
rng.next_u64(),
])
}
fn zero() -> Self {
Self::zero()
}
fn one() -> Self {
Self::one()
}
fn double(&self) -> Self {
self.double()
}
#[inline(always)]
fn square(&self) -> Self {
self.square()
}
/// Computes the square root of this element, if it exists.
fn sqrt(&self) -> CtOption<Self> {
let tmp = self.pow(&[
0xffffffffbfffff0c,
0xffffffffffffffff,
0xffffffffffffffff,
0x3fffffffffffffff,
]);
CtOption::new(tmp, tmp.square().ct_eq(self))
}
/// Computes the multiplicative inverse of this element,
/// failing if the element is zero.
fn invert(&self) -> CtOption<Self> {
let tmp = self.pow_vartime(&[
0xfffffffefffffc2d,
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff,
]);
CtOption::new(tmp, !self.ct_eq(&Self::zero()))
}
fn pow_vartime<S: AsRef<[u64]>>(&self, exp: S) -> Self {
let mut res = Self::one();
let mut found_one = false;
for e in exp.as_ref().iter().rev() {
for i in (0..64).rev() {
if found_one {
res = res.square();
}
if ((*e >> i) & 1) == 1 {
found_one = true;
res *= self;
}
}
}
res
}
}
impl ff::PrimeField for Fp {
type Repr = [u8; 32];
const NUM_BITS: u32 = 256;
const CAPACITY: u32 = 255;
const S: u32 = 1;
fn from_repr(repr: Self::Repr) -> CtOption<Self> {
let mut tmp = Fp([0, 0, 0, 0]);
tmp.0[0] = u64::from_le_bytes(repr[0..8].try_into().unwrap());
tmp.0[1] = u64::from_le_bytes(repr[8..16].try_into().unwrap());
tmp.0[2] = u64::from_le_bytes(repr[16..24].try_into().unwrap());
tmp.0[3] = u64::from_le_bytes(repr[24..32].try_into().unwrap());
// Try to subtract the modulus
let (_, borrow) = sbb(tmp.0[0], MODULUS.0[0], 0);
let (_, borrow) = sbb(tmp.0[1], MODULUS.0[1], borrow);
let (_, borrow) = sbb(tmp.0[2], MODULUS.0[2], borrow);
let (_, borrow) = sbb(tmp.0[3], MODULUS.0[3], borrow);
// If the element is smaller than MODULUS then the
// subtraction will underflow, producing a borrow value
// of 0xffff...ffff. Otherwise, it'll be zero.
let is_some = (borrow as u8) & 1;
// Convert to Montgomery form by computing
// (a.R^0 * R^2) / R = a.R
tmp *= &R2;
CtOption::new(tmp, Choice::from(is_some))
}
fn to_repr(&self) -> Self::Repr {
// Turn into canonical form by computing
// (a.R) / R = a
let tmp = Fp::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0);
let mut res = [0; 32];
res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes());
res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes());
res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes());
res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes());
res
}
fn is_odd(&self) -> Choice {
Choice::from(self.to_repr()[0] & 1)
}
fn multiplicative_generator() -> Self {
GENERATOR
}
fn root_of_unity() -> Self {
ROOT_OF_UNITY
}
}
impl SqrtRatio for Fp {
const T_MINUS1_OVER2: [u64; 4] = [0, 0, 0, 0];
fn get_lower_32(&self) -> u32 {
let tmp = Fp::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0);
tmp.0[0] as u32
}
}
#[cfg(all(feature = "bits", not(target_pointer_width = "64")))]
type ReprBits = [u32; 8];
#[cfg(all(feature = "bits", target_pointer_width = "64"))]
type ReprBits = [u64; 4];
#[cfg(feature = "bits")]
#[cfg_attr(docsrs, doc(cfg(feature = "bits")))]
impl PrimeFieldBits for Fp {
type ReprBits = ReprBits;
fn to_le_bits(&self) -> FieldBits<Self::ReprBits> {
let bytes = self.to_repr();
#[cfg(not(target_pointer_width = "64"))]
let limbs = [
u32::from_le_bytes(bytes[0..4].try_into().unwrap()),
u32::from_le_bytes(bytes[4..8].try_into().unwrap()),
u32::from_le_bytes(bytes[8..12].try_into().unwrap()),
u32::from_le_bytes(bytes[12..16].try_into().unwrap()),
u32::from_le_bytes(bytes[16..20].try_into().unwrap()),
u32::from_le_bytes(bytes[20..24].try_into().unwrap()),
u32::from_le_bytes(bytes[24..28].try_into().unwrap()),
u32::from_le_bytes(bytes[28..32].try_into().unwrap()),
];
#[cfg(target_pointer_width = "64")]
let limbs = [
u64::from_le_bytes(bytes[0..8].try_into().unwrap()),
u64::from_le_bytes(bytes[8..16].try_into().unwrap()),
u64::from_le_bytes(bytes[16..24].try_into().unwrap()),
u64::from_le_bytes(bytes[24..32].try_into().unwrap()),
];
FieldBits::new(limbs)
}
fn char_le_bits() -> FieldBits<Self::ReprBits> {
#[cfg(not(target_pointer_width = "64"))]
{
FieldBits::new(MODULUS_LIMBS_32)
}
#[cfg(target_pointer_width = "64")]
FieldBits::new(MODULUS.0)
}
}
#[cfg(test)]
mod test {
use super::*;
use ff::Field;
use rand_core::OsRng;
#[test]
fn test_sqrt() {
// NB: TWO_INV is standing in as a "random" field element
let v = (Fp::TWO_INV).square().sqrt().unwrap();
assert!(v == Fp::TWO_INV || (-v) == Fp::TWO_INV);
for _ in 0..10000 {
let a = Fp::random(OsRng);
let mut b = a;
b = b.square();
let b = b.sqrt().unwrap();
let mut negb = b;
negb = negb.neg();
assert!(a == b || a == negb);
}
}
#[test]
fn test_field() {
crate::tests::field::random_field_tests::<Fp>("secp256k1 base".to_string());
}
}

View File

@@ -1,373 +0,0 @@
use core::convert::TryInto;
use core::fmt;
use core::ops::{Add, Mul, Neg, Sub};
use ff::{FieldBits, PrimeField, PrimeFieldBits};
use rand::RngCore;
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
use crate::arithmetic::{adc, mac, sbb};
use pasta_curves::arithmetic::{FieldExt, Group, SqrtRatio};
/// This represents an element of $\mathbb{F}_q$ where
///
/// `q = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141`
///
/// is the scalar field of the secp256k1 curve.
// The internal representation of this type is four 64-bit unsigned
// integers in little-endian order. `Fq` values are always in
// Montgomery form; i.e., Fq(a) = aR mod q, with R = 2^256.
#[derive(Clone, Copy, Eq)]
pub struct Fq(pub(crate) [u64; 4]);
/// Constant representing the modulus
/// q = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
const MODULUS: Fq = Fq([
0xbfd25e8cd0364141,
0xbaaedce6af48a03b,
0xfffffffffffffffe,
0xffffffffffffffff,
]);
/// The modulus as u32 limbs.
#[cfg(not(target_pointer_width = "64"))]
const MODULUS_LIMBS_32: [u32; 8] = [
0xd036_4141,
0xbfd2_5e8c,
0xaf48_a03b,
0xbaae_dce6,
0xffff_fffe,
0xffff_ffff,
0xffff_ffff,
0xffff_ffff,
];
///Constant representing the modulus as static str
const MODULUS_STR: &str = "0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141";
/// INV = -(q^{-1} mod 2^64) mod 2^64
const INV: u64 = 0x4b0dff665588b13f;
/// R = 2^256 mod q
/// 0x14551231950b75fc4402da1732fc9bebf
const R: Fq = Fq([0x402da1732fc9bebf, 0x4551231950b75fc4, 0x1, 0]);
/// R^2 = 2^512 mod q
/// 0x9d671cd581c69bc5e697f5e45bcd07c6741496c20e7cf878896cf21467d7d140
const R2: Fq = Fq([
0x896cf21467d7d140,
0x741496c20e7cf878,
0xe697f5e45bcd07c6,
0x9d671cd581c69bc5,
]);
/// R^3 = 2^768 mod q
/// 0x555d800c18ef116db1b31347f1d0b2da0017648444d4322c7bc0cfe0e9ff41ed
const R3: Fq = Fq([
0x7bc0cfe0e9ff41ed,
0x0017648444d4322c,
0xb1b31347f1d0b2da,
0x555d800c18ef116d,
]);
/// `GENERATOR = 7 mod r` is a generator of the `q - 1` order multiplicative
/// subgroup, or in other words a primitive root of the field.
const GENERATOR: Fq = Fq::from_raw([0x07, 0x00, 0x00, 0x00]);
/// GENERATOR^t where t * 2^s + 1 = r
/// with t odd. In other words, this
/// is a 2^s root of unity.
/// `0xc1dc060e7a91986df9879a3fbc483a898bdeab680756045992f4b5402b052f2`
const ROOT_OF_UNITY: Fq = Fq::from_raw([
0x992f4b5402b052f2,
0x98bdeab680756045,
0xdf9879a3fbc483a8,
0xc1dc060e7a91986,
]);
/// 1 / ROOT_OF_UNITY mod q
const ROOT_OF_UNITY_INV: Fq = Fq::from_raw([
0xb6fb30a0884f0d1c,
0x77a275910aa413c3,
0xefc7b0c75b8cbb72,
0xfd3ae181f12d7096,
]);
/// 1 / 2 mod q
const TWO_INV: Fq = Fq::from_raw([
0xdfe92f46681b20a1,
0x5d576e7357a4501d,
0xffffffffffffffff,
0x7fffffffffffffff,
]);
const ZETA: Fq = Fq::from_raw([
16069571880186789234,
1310022930574435960,
11900229862571533402,
6008836872998760672,
]);
const DELTA: Fq = Fq::from_raw([0x07, 0x00, 0x00, 0x00])
.square()
.square()
.square()
.square()
.square()
.square();
impl_binops_additive!(Fq, Fq);
impl_binops_multiplicative!(Fq, Fq);
field_common!(
Fq,
MODULUS,
INV,
MODULUS_STR,
TWO_INV,
ROOT_OF_UNITY_INV,
DELTA,
ZETA,
R,
R2,
R3
);
field_arithmetic!(Fq, MODULUS, INV, dense);
impl Fq {
pub const fn size() -> usize {
32
}
}
impl ff::Field for Fq {
fn random(mut rng: impl RngCore) -> Self {
Self::from_u512([
rng.next_u64(),
rng.next_u64(),
rng.next_u64(),
rng.next_u64(),
rng.next_u64(),
rng.next_u64(),
rng.next_u64(),
rng.next_u64(),
])
}
fn zero() -> Self {
Self::zero()
}
fn one() -> Self {
Self::one()
}
fn double(&self) -> Self {
self.double()
}
#[inline(always)]
fn square(&self) -> Self {
self.square()
}
/// Computes the square root of this element, if it exists.
fn sqrt(&self) -> CtOption<Self> {
crate::arithmetic::sqrt_tonelli_shanks(self, &<Self as SqrtRatio>::T_MINUS1_OVER2)
}
/// Computes the multiplicative inverse of this element,
/// failing if the element is zero.
fn invert(&self) -> CtOption<Self> {
let tmp = self.pow_vartime(&[
0xbfd25e8cd036413f,
0xbaaedce6af48a03b,
0xfffffffffffffffe,
0xffffffffffffffff,
]);
CtOption::new(tmp, !self.ct_eq(&Self::zero()))
}
fn pow_vartime<S: AsRef<[u64]>>(&self, exp: S) -> Self {
let mut res = Self::one();
let mut found_one = false;
for e in exp.as_ref().iter().rev() {
for i in (0..64).rev() {
if found_one {
res = res.square();
}
if ((*e >> i) & 1) == 1 {
found_one = true;
res *= self;
}
}
}
res
}
}
impl ff::PrimeField for Fq {
type Repr = [u8; 32];
const NUM_BITS: u32 = 256;
const CAPACITY: u32 = 255;
const S: u32 = 6;
fn from_repr(repr: Self::Repr) -> CtOption<Self> {
let mut tmp = Fq([0, 0, 0, 0]);
tmp.0[0] = u64::from_le_bytes(repr[0..8].try_into().unwrap());
tmp.0[1] = u64::from_le_bytes(repr[8..16].try_into().unwrap());
tmp.0[2] = u64::from_le_bytes(repr[16..24].try_into().unwrap());
tmp.0[3] = u64::from_le_bytes(repr[24..32].try_into().unwrap());
// Try to subtract the modulus
let (_, borrow) = sbb(tmp.0[0], MODULUS.0[0], 0);
let (_, borrow) = sbb(tmp.0[1], MODULUS.0[1], borrow);
let (_, borrow) = sbb(tmp.0[2], MODULUS.0[2], borrow);
let (_, borrow) = sbb(tmp.0[3], MODULUS.0[3], borrow);
// If the element is smaller than MODULUS then the
// subtraction will underflow, producing a borrow value
// of 0xffff...ffff. Otherwise, it'll be zero.
let is_some = (borrow as u8) & 1;
// Convert to Montgomery form by computing
// (a.R^0 * R^2) / R = a.R
tmp *= &R2;
CtOption::new(tmp, Choice::from(is_some))
}
fn to_repr(&self) -> Self::Repr {
// Turn into canonical form by computing
// (a.R) / R = a
let tmp = Fq::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0);
let mut res = [0; 32];
res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes());
res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes());
res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes());
res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes());
res
}
fn is_odd(&self) -> Choice {
Choice::from(self.to_repr()[0] & 1)
}
fn multiplicative_generator() -> Self {
GENERATOR
}
fn root_of_unity() -> Self {
ROOT_OF_UNITY
}
}
impl SqrtRatio for Fq {
const T_MINUS1_OVER2: [u64; 4] = [
0x777fa4bd19a06c82,
0xfd755db9cd5e9140,
0xffffffffffffffff,
0x01ffffffffffffff,
];
fn get_lower_32(&self) -> u32 {
let tmp = Fq::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0);
tmp.0[0] as u32
}
}
#[cfg(all(feature = "bits", not(target_pointer_width = "64")))]
type ReprBits = [u32; 8];
#[cfg(all(feature = "bits", target_pointer_width = "64"))]
type ReprBits = [u64; 4];
#[cfg(feature = "bits")]
impl PrimeFieldBits for Fq {
type ReprBits = ReprBits;
fn to_le_bits(&self) -> FieldBits<Self::ReprBits> {
let bytes = self.to_repr();
#[cfg(not(target_pointer_width = "64"))]
let limbs = [
u32::from_le_bytes(bytes[0..4].try_into().unwrap()),
u32::from_le_bytes(bytes[4..8].try_into().unwrap()),
u32::from_le_bytes(bytes[8..12].try_into().unwrap()),
u32::from_le_bytes(bytes[12..16].try_into().unwrap()),
u32::from_le_bytes(bytes[16..20].try_into().unwrap()),
u32::from_le_bytes(bytes[20..24].try_into().unwrap()),
u32::from_le_bytes(bytes[24..28].try_into().unwrap()),
u32::from_le_bytes(bytes[28..32].try_into().unwrap()),
];
#[cfg(target_pointer_width = "64")]
let limbs = [
u64::from_le_bytes(bytes[0..8].try_into().unwrap()),
u64::from_le_bytes(bytes[8..16].try_into().unwrap()),
u64::from_le_bytes(bytes[16..24].try_into().unwrap()),
u64::from_le_bytes(bytes[24..32].try_into().unwrap()),
];
FieldBits::new(limbs)
}
fn char_le_bits() -> FieldBits<Self::ReprBits> {
#[cfg(not(target_pointer_width = "64"))]
{
FieldBits::new(MODULUS_LIMBS_32)
}
#[cfg(target_pointer_width = "64")]
FieldBits::new(MODULUS.0)
}
}
#[cfg(test)]
mod test {
use super::*;
use ff::Field;
use rand_core::OsRng;
#[test]
fn test_sqrt() {
// NB: TWO_INV is standing in as a "random" field element
let v = (Fq::TWO_INV).square().sqrt().unwrap();
assert!(v == Fq::TWO_INV || (-v) == Fq::TWO_INV);
for _ in 0..10000 {
let a = Fq::random(OsRng);
let mut b = a;
b = b.square();
let b = b.sqrt().unwrap();
let mut negb = b;
negb = negb.neg();
assert!(a == b || a == negb);
}
}
#[test]
fn test_root_of_unity() {
assert_eq!(
Fq::root_of_unity().pow_vartime(&[1 << Fq::S, 0, 0, 0]),
Fq::one()
);
}
#[test]
fn test_inv_root_of_unity() {
assert_eq!(Fq::ROOT_OF_UNITY_INV, Fq::root_of_unity().invert().unwrap());
}
#[test]
fn test_field() {
crate::tests::field::random_field_tests::<Fq>("secp256k1 scalar".to_string());
}
}

View File

@@ -1,178 +0,0 @@
//! This module implements "simplified SWU" hashing to short Weierstrass curves
//! with a = 0.
use static_assertions::const_assert;
use subtle::ConstantTimeEq;
use pasta_curves::arithmetic::{CurveExt, FieldExt};
/// Hashes over a message and writes the output to all of `buf`.
pub fn hash_to_field<F: FieldExt>(
curve_id: &str,
domain_prefix: &str,
message: &[u8],
buf: &mut [F; 2],
) {
assert!(domain_prefix.len() < 256);
assert!((22 + curve_id.len() + domain_prefix.len()) < 256);
// Assume that the field size is 32 bytes and k is 256, where k is defined in
// <https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-10.html#name-security-considerations-3>.
const CHUNKLEN: usize = 64;
const_assert!(CHUNKLEN * 2 < 256);
// Input block size of BLAKE2b.
const R_IN_BYTES: usize = 128;
let personal = [0u8; 16];
let empty_hasher = blake2b_simd::Params::new()
.hash_length(CHUNKLEN)
.personal(&personal)
.to_state();
let b_0 = empty_hasher
.clone()
.update(&[0; R_IN_BYTES])
.update(message)
.update(&[0, (CHUNKLEN * 2) as u8, 0])
.update(domain_prefix.as_bytes())
.update(b"-")
.update(curve_id.as_bytes())
.update(b"_XMD:BLAKE2b_SSWU_RO_")
.update(&[(22 + curve_id.len() + domain_prefix.len()) as u8])
.finalize();
let b_1 = empty_hasher
.clone()
.update(b_0.as_array())
.update(&[1])
.update(domain_prefix.as_bytes())
.update(b"-")
.update(curve_id.as_bytes())
.update(b"_XMD:BLAKE2b_SSWU_RO_")
.update(&[(22 + curve_id.len() + domain_prefix.len()) as u8])
.finalize();
let b_2 = {
let mut empty_hasher = empty_hasher;
for (l, r) in b_0.as_array().iter().zip(b_1.as_array().iter()) {
empty_hasher.update(&[*l ^ *r]);
}
empty_hasher
.update(&[2])
.update(domain_prefix.as_bytes())
.update(b"-")
.update(curve_id.as_bytes())
.update(b"_XMD:BLAKE2b_SSWU_RO_")
.update(&[(22 + curve_id.len() + domain_prefix.len()) as u8])
.finalize()
};
for (big, buf) in [b_1, b_2].iter().zip(buf.iter_mut()) {
let mut little = [0u8; CHUNKLEN];
little.copy_from_slice(big.as_array());
little.reverse();
*buf = F::from_bytes_wide(&little);
}
}
/// Implements a degree 3 isogeny map.
pub fn iso_map<F: FieldExt, C: CurveExt<Base = F>, I: CurveExt<Base = F>>(
p: &I,
iso: &[C::Base; 13],
) -> C {
// The input and output are in Jacobian coordinates, using the method
// in "Avoiding inversions" [WB2019, section 4.3].
let (x, y, z) = p.jacobian_coordinates();
let z2 = z.square();
let z3 = z2 * z;
let z4 = z2.square();
let z6 = z3.square();
let num_x = ((iso[0] * x + iso[1] * z2) * x + iso[2] * z4) * x + iso[3] * z6;
let div_x = (z2 * x + iso[4] * z4) * x + iso[5] * z6;
let num_y = (((iso[6] * x + iso[7] * z2) * x + iso[8] * z4) * x + iso[9] * z6) * y;
let div_y = (((x + iso[10] * z2) * x + iso[11] * z4) * x + iso[12] * z6) * z3;
let zo = div_x * div_y;
let xo = num_x * div_y * zo;
let yo = num_y * div_x * zo.square();
C::new_jacobian(xo, yo, zo).unwrap()
}
#[allow(clippy::many_single_char_names)]
pub fn map_to_curve_simple_swu<F: FieldExt, C: CurveExt<Base = F>, I: CurveExt<Base = F>>(
u: &F,
root_of_unity_inv: F,
z: F,
) -> I {
// 1. tv1 = inv0(Z^2 * u^4 + Z * u^2)
// 2. x1 = (-B / A) * (1 + tv1)
// 3. If tv1 == 0, set x1 = B / (Z * A)
// 4. gx1 = x1^3 + A * x1 + B
//
// We use the "Avoiding inversions" optimization in [WB2019, section 4.2]
// (not to be confused with section 4.3):
//
// here [WB2019]
// ------- ---------------------------------
// Z ξ
// u t
// Z * u^2 ξ * t^2 (called u, confusingly)
// x1 X_0(t)
// x2 X_1(t)
// gx1 g(X_0(t))
// gx2 g(X_1(t))
//
// Using the "here" names:
// x1 = num_x1/div = [B*(Z^2 * u^4 + Z * u^2 + 1)] / [-A*(Z^2 * u^4 + Z * u^2]
// gx1 = num_gx1/div_gx1 = [num_x1^3 + A * num_x1 * div^2 + B * div^3] / div^3
let a = I::a();
let b = I::b();
let z_u2 = z * u.square();
let ta = z_u2.square() + z_u2;
let num_x1 = b * (ta + F::one());
let div = a * F::conditional_select(&-ta, &z, ta.is_zero());
let num2_x1 = num_x1.square();
let div2 = div.square();
let div3 = div2 * div;
let num_gx1 = (num2_x1 + a * div2) * num_x1 + b * div3;
// 5. x2 = Z * u^2 * x1
let num_x2 = z_u2 * num_x1; // same div
// 6. gx2 = x2^3 + A * x2 + B [optimized out; see below]
// 7. If is_square(gx1), set x = x1 and y = sqrt(gx1)
// 8. Else set x = x2 and y = sqrt(gx2)
let (gx1_square, y1) = F::sqrt_ratio(&num_gx1, &div3);
// This magic also comes from a generalization of [WB2019, section 4.2].
//
// The Sarkar square root algorithm with input s gives us a square root of
// h * s for free when s is not square, where h is a fixed nonsquare.
// In our implementation, h = ROOT_OF_UNITY.
// We know that Z / h is a square since both Z and h are
// nonsquares. Precompute theta as a square root of Z / ROOT_OF_UNITY.
//
// We have gx2 = g(Z * u^2 * x1) = Z^3 * u^6 * gx1
// = (Z * u^3)^2 * (Z/h * h * gx1)
// = (Z * theta * u^3)^2 * (h * gx1)
//
// When gx1 is not square, y1 is a square root of h * gx1, and so Z * theta * u^3 * y1
// is a square root of gx2. Note that we don't actually need to compute gx2.
let theta = (root_of_unity_inv * z).sqrt().unwrap();
let y2 = theta * z_u2 * u * y1;
let num_x = F::conditional_select(&num_x2, &num_x1, gx1_square);
let y = F::conditional_select(&y2, &y1, gx1_square);
// 9. If sgn0(u) != sgn0(y), set y = -y
let y = F::conditional_select(&(-y), &y, u.is_odd().ct_eq(&y.is_odd()));
I::new_jacobian(num_x * div, y * div3, div).unwrap()
}

View File

@@ -1,41 +0,0 @@
#![cfg_attr(feature = "asm", feature(asm_const))]
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(test)]
#[macro_use]
extern crate std;
#[macro_use]
mod macros;
mod curves;
mod fields;
pub use pasta_curves::arithmetic::{Coordinates, CurveAffine, CurveExt, FieldExt, Group};
pub mod arithmetic;
pub mod secp256k1;
pub mod secq256k1;
#[cfg(feature = "alloc")]
mod hashtocurve;
pub use curves::*;
pub use fields::*;
pub extern crate group;
#[cfg(test)]
pub mod tests;
#[cfg(all(feature = "prefetch", target_arch = "x86_64"))]
#[inline(always)]
pub fn prefetch<T>(data: &[T], offset: usize) {
use core::arch::x86_64::_mm_prefetch;
unsafe {
_mm_prefetch(
data.as_ptr().offset(offset as isize) as *const i8,
core::arch::x86_64::_MM_HINT_T0,
);
}
}

View File

@@ -1,675 +0,0 @@
macro_rules! impl_add_binop_specify_output {
($lhs:ident, $rhs:ident, $output:ident) => {
impl<'b> ::core::ops::Add<&'b $rhs> for $lhs {
type Output = $output;
#[inline]
fn add(self, rhs: &'b $rhs) -> $output {
&self + rhs
}
}
impl<'a> ::core::ops::Add<$rhs> for &'a $lhs {
type Output = $output;
#[inline]
fn add(self, rhs: $rhs) -> $output {
self + &rhs
}
}
impl ::core::ops::Add<$rhs> for $lhs {
type Output = $output;
#[inline]
fn add(self, rhs: $rhs) -> $output {
&self + &rhs
}
}
};
}
macro_rules! impl_sub_binop_specify_output {
($lhs:ident, $rhs:ident, $output:ident) => {
impl<'b> ::core::ops::Sub<&'b $rhs> for $lhs {
type Output = $output;
#[inline]
fn sub(self, rhs: &'b $rhs) -> $output {
&self - rhs
}
}
impl<'a> ::core::ops::Sub<$rhs> for &'a $lhs {
type Output = $output;
#[inline]
fn sub(self, rhs: $rhs) -> $output {
self - &rhs
}
}
impl ::core::ops::Sub<$rhs> for $lhs {
type Output = $output;
#[inline]
fn sub(self, rhs: $rhs) -> $output {
&self - &rhs
}
}
};
}
macro_rules! impl_binops_additive_specify_output {
($lhs:ident, $rhs:ident, $output:ident) => {
impl_add_binop_specify_output!($lhs, $rhs, $output);
impl_sub_binop_specify_output!($lhs, $rhs, $output);
};
}
macro_rules! impl_binops_multiplicative_mixed {
($lhs:ident, $rhs:ident, $output:ident) => {
impl<'b> ::core::ops::Mul<&'b $rhs> for $lhs {
type Output = $output;
#[inline]
fn mul(self, rhs: &'b $rhs) -> $output {
&self * rhs
}
}
impl<'a> ::core::ops::Mul<$rhs> for &'a $lhs {
type Output = $output;
#[inline]
fn mul(self, rhs: $rhs) -> $output {
self * &rhs
}
}
impl ::core::ops::Mul<$rhs> for $lhs {
type Output = $output;
#[inline]
fn mul(self, rhs: $rhs) -> $output {
&self * &rhs
}
}
};
}
macro_rules! impl_binops_additive {
($lhs:ident, $rhs:ident) => {
impl_binops_additive_specify_output!($lhs, $rhs, $lhs);
impl ::core::ops::SubAssign<$rhs> for $lhs {
#[inline]
fn sub_assign(&mut self, rhs: $rhs) {
*self = &*self - &rhs;
}
}
impl ::core::ops::AddAssign<$rhs> for $lhs {
#[inline]
fn add_assign(&mut self, rhs: $rhs) {
*self = &*self + &rhs;
}
}
impl<'b> ::core::ops::SubAssign<&'b $rhs> for $lhs {
#[inline]
fn sub_assign(&mut self, rhs: &'b $rhs) {
*self = &*self - rhs;
}
}
impl<'b> ::core::ops::AddAssign<&'b $rhs> for $lhs {
#[inline]
fn add_assign(&mut self, rhs: &'b $rhs) {
*self = &*self + rhs;
}
}
};
}
macro_rules! impl_binops_multiplicative {
($lhs:ident, $rhs:ident) => {
impl_binops_multiplicative_mixed!($lhs, $rhs, $lhs);
impl ::core::ops::MulAssign<$rhs> for $lhs {
#[inline]
fn mul_assign(&mut self, rhs: $rhs) {
*self = &*self * &rhs;
}
}
impl<'b> ::core::ops::MulAssign<&'b $rhs> for $lhs {
#[inline]
fn mul_assign(&mut self, rhs: &'b $rhs) {
*self = &*self * rhs;
}
}
};
}
macro_rules! field_common {
(
$field:ident,
$modulus:ident,
$inv:ident,
$modulus_str:ident,
$two_inv:ident,
$root_of_unity_inv:ident,
$delta:ident,
$zeta:ident,
$r:ident,
$r2:ident,
$r3:ident
) => {
impl $field {
/// Returns zero, the additive identity.
#[inline]
pub const fn zero() -> $field {
$field([0, 0, 0, 0])
}
/// Returns one, the multiplicative identity.
#[inline]
pub const fn one() -> $field {
$r
}
fn from_u512(limbs: [u64; 8]) -> $field {
// We reduce an arbitrary 512-bit number by decomposing it into two 256-bit digits
// with the higher bits multiplied by 2^256. Thus, we perform two reductions
//
// 1. the lower bits are multiplied by R^2, as normal
// 2. the upper bits are multiplied by R^2 * 2^256 = R^3
//
// and computing their sum in the field. It remains to see that arbitrary 256-bit
// numbers can be placed into Montgomery form safely using the reduction. The
// reduction works so long as the product is less than R=2^256 multiplied by
// the modulus. This holds because for any `c` smaller than the modulus, we have
// that (2^256 - 1)*c is an acceptable product for the reduction. Therefore, the
// reduction always works so long as `c` is in the field; in this case it is either the
// constant `R2` or `R3`.
let d0 = $field([limbs[0], limbs[1], limbs[2], limbs[3]]);
let d1 = $field([limbs[4], limbs[5], limbs[6], limbs[7]]);
// Convert to Montgomery form
d0 * $r2 + d1 * $r3
}
/// Converts from an integer represented in little endian
/// into its (congruent) `$field` representation.
pub const fn from_raw(val: [u64; 4]) -> Self {
(&$field(val)).mul(&$r2)
}
/// Attempts to convert a little-endian byte representation of
/// a scalar into a `Fr`, failing if the input is not canonical.
pub fn from_bytes(bytes: &[u8; 32]) -> CtOption<$field> {
<Self as ff::PrimeField>::from_repr(*bytes)
}
/// Converts an element of `Fr` into a byte representation in
/// little-endian byte order.
pub fn to_bytes(&self) -> [u8; 32] {
<Self as ff::PrimeField>::to_repr(self)
}
}
impl Group for $field {
type Scalar = Self;
fn group_zero() -> Self {
Self::zero()
}
fn group_add(&mut self, rhs: &Self) {
*self += *rhs;
}
fn group_sub(&mut self, rhs: &Self) {
*self -= *rhs;
}
fn group_scale(&mut self, by: &Self::Scalar) {
*self *= *by;
}
}
impl fmt::Debug for $field {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let tmp = self.to_repr();
write!(f, "0x")?;
for &b in tmp.iter().rev() {
write!(f, "{:02x}", b)?;
}
Ok(())
}
}
impl Default for $field {
#[inline]
fn default() -> Self {
Self::zero()
}
}
impl From<bool> for $field {
fn from(bit: bool) -> $field {
if bit {
$field::one()
} else {
$field::zero()
}
}
}
impl From<u64> for $field {
fn from(val: u64) -> $field {
$field([val, 0, 0, 0]) * $r2
}
}
impl ConstantTimeEq for $field {
fn ct_eq(&self, other: &Self) -> Choice {
self.0[0].ct_eq(&other.0[0])
& self.0[1].ct_eq(&other.0[1])
& self.0[2].ct_eq(&other.0[2])
& self.0[3].ct_eq(&other.0[3])
}
}
impl PartialEq for $field {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.ct_eq(other).unwrap_u8() == 1
}
}
impl core::cmp::Ord for $field {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
let left = self.to_repr();
let right = other.to_repr();
left.iter()
.zip(right.iter())
.rev()
.find_map(|(left_byte, right_byte)| match left_byte.cmp(right_byte) {
core::cmp::Ordering::Equal => None,
res => Some(res),
})
.unwrap_or(core::cmp::Ordering::Equal)
}
}
impl core::cmp::PartialOrd for $field {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl ConditionallySelectable for $field {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
$field([
u64::conditional_select(&a.0[0], &b.0[0], choice),
u64::conditional_select(&a.0[1], &b.0[1], choice),
u64::conditional_select(&a.0[2], &b.0[2], choice),
u64::conditional_select(&a.0[3], &b.0[3], choice),
])
}
}
impl<'a> Neg for &'a $field {
type Output = $field;
#[inline]
fn neg(self) -> $field {
self.neg()
}
}
impl Neg for $field {
type Output = $field;
#[inline]
fn neg(self) -> $field {
-&self
}
}
impl<'a, 'b> Sub<&'b $field> for &'a $field {
type Output = $field;
#[inline]
fn sub(self, rhs: &'b $field) -> $field {
self.sub(rhs)
}
}
impl<'a, 'b> Add<&'b $field> for &'a $field {
type Output = $field;
#[inline]
fn add(self, rhs: &'b $field) -> $field {
self.add(rhs)
}
}
impl<'a, 'b> Mul<&'b $field> for &'a $field {
type Output = $field;
#[inline]
fn mul(self, rhs: &'b $field) -> $field {
self.mul(rhs)
}
}
impl From<$field> for [u8; 32] {
fn from(value: $field) -> [u8; 32] {
value.to_repr()
}
}
impl<'a> From<&'a $field> for [u8; 32] {
fn from(value: &'a $field) -> [u8; 32] {
value.to_repr()
}
}
impl FieldExt for $field {
const MODULUS: &'static str = $modulus_str;
const TWO_INV: Self = $two_inv;
const ROOT_OF_UNITY_INV: Self = $root_of_unity_inv;
const DELTA: Self = $delta;
const ZETA: Self = $zeta;
fn from_u128(v: u128) -> Self {
$field::from_raw([v as u64, (v >> 64) as u64, 0, 0])
}
/// Converts a 512-bit little endian integer into
/// a `$field` by reducing by the modulus.
fn from_bytes_wide(bytes: &[u8; 64]) -> $field {
$field::from_u512([
u64::from_le_bytes(bytes[0..8].try_into().unwrap()),
u64::from_le_bytes(bytes[8..16].try_into().unwrap()),
u64::from_le_bytes(bytes[16..24].try_into().unwrap()),
u64::from_le_bytes(bytes[24..32].try_into().unwrap()),
u64::from_le_bytes(bytes[32..40].try_into().unwrap()),
u64::from_le_bytes(bytes[40..48].try_into().unwrap()),
u64::from_le_bytes(bytes[48..56].try_into().unwrap()),
u64::from_le_bytes(bytes[56..64].try_into().unwrap()),
])
}
fn get_lower_128(&self) -> u128 {
let tmp = $field::montgomery_reduce(
self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0,
);
u128::from(tmp.0[0]) | (u128::from(tmp.0[1]) << 64)
}
}
};
}
macro_rules! field_arithmetic {
($field:ident, $modulus:ident, $inv:ident, $field_type:ident) => {
field_specific!($field, $modulus, $inv, $field_type);
impl $field {
/// Doubles this field element.
#[inline]
pub const fn double(&self) -> $field {
self.add(self)
}
/// Squares this element.
#[inline]
pub const fn square(&self) -> $field {
let (r1, carry) = mac(0, self.0[0], self.0[1], 0);
let (r2, carry) = mac(0, self.0[0], self.0[2], carry);
let (r3, r4) = mac(0, self.0[0], self.0[3], carry);
let (r3, carry) = mac(r3, self.0[1], self.0[2], 0);
let (r4, r5) = mac(r4, self.0[1], self.0[3], carry);
let (r5, r6) = mac(r5, self.0[2], self.0[3], 0);
let r7 = r6 >> 63;
let r6 = (r6 << 1) | (r5 >> 63);
let r5 = (r5 << 1) | (r4 >> 63);
let r4 = (r4 << 1) | (r3 >> 63);
let r3 = (r3 << 1) | (r2 >> 63);
let r2 = (r2 << 1) | (r1 >> 63);
let r1 = r1 << 1;
let (r0, carry) = mac(0, self.0[0], self.0[0], 0);
let (r1, carry) = adc(0, r1, carry);
let (r2, carry) = mac(r2, self.0[1], self.0[1], carry);
let (r3, carry) = adc(0, r3, carry);
let (r4, carry) = mac(r4, self.0[2], self.0[2], carry);
let (r5, carry) = adc(0, r5, carry);
let (r6, carry) = mac(r6, self.0[3], self.0[3], carry);
let (r7, _) = adc(0, r7, carry);
$field::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7)
}
/// Multiplies `rhs` by `self`, returning the result.
#[inline]
pub const fn mul(&self, rhs: &Self) -> $field {
// Schoolbook multiplication
let (r0, carry) = mac(0, self.0[0], rhs.0[0], 0);
let (r1, carry) = mac(0, self.0[0], rhs.0[1], carry);
let (r2, carry) = mac(0, self.0[0], rhs.0[2], carry);
let (r3, r4) = mac(0, self.0[0], rhs.0[3], carry);
let (r1, carry) = mac(r1, self.0[1], rhs.0[0], 0);
let (r2, carry) = mac(r2, self.0[1], rhs.0[1], carry);
let (r3, carry) = mac(r3, self.0[1], rhs.0[2], carry);
let (r4, r5) = mac(r4, self.0[1], rhs.0[3], carry);
let (r2, carry) = mac(r2, self.0[2], rhs.0[0], 0);
let (r3, carry) = mac(r3, self.0[2], rhs.0[1], carry);
let (r4, carry) = mac(r4, self.0[2], rhs.0[2], carry);
let (r5, r6) = mac(r5, self.0[2], rhs.0[3], carry);
let (r3, carry) = mac(r3, self.0[3], rhs.0[0], 0);
let (r4, carry) = mac(r4, self.0[3], rhs.0[1], carry);
let (r5, carry) = mac(r5, self.0[3], rhs.0[2], carry);
let (r6, r7) = mac(r6, self.0[3], rhs.0[3], carry);
$field::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7)
}
/// Subtracts `rhs` from `self`, returning the result.
#[inline]
pub const fn sub(&self, rhs: &Self) -> Self {
let (d0, borrow) = sbb(self.0[0], rhs.0[0], 0);
let (d1, borrow) = sbb(self.0[1], rhs.0[1], borrow);
let (d2, borrow) = sbb(self.0[2], rhs.0[2], borrow);
let (d3, borrow) = sbb(self.0[3], rhs.0[3], borrow);
// If underflow occurred on the final limb, borrow = 0xfff...fff, otherwise
// borrow = 0x000...000. Thus, we use it as a mask to conditionally add the modulus.
let (d0, carry) = adc(d0, $modulus.0[0] & borrow, 0);
let (d1, carry) = adc(d1, $modulus.0[1] & borrow, carry);
let (d2, carry) = adc(d2, $modulus.0[2] & borrow, carry);
let (d3, _) = adc(d3, $modulus.0[3] & borrow, carry);
$field([d0, d1, d2, d3])
}
/// Negates `self`.
#[inline]
pub const fn neg(&self) -> Self {
// Subtract `self` from `MODULUS` to negate. Ignore the final
// borrow because it cannot underflow; self is guaranteed to
// be in the field.
let (d0, borrow) = sbb($modulus.0[0], self.0[0], 0);
let (d1, borrow) = sbb($modulus.0[1], self.0[1], borrow);
let (d2, borrow) = sbb($modulus.0[2], self.0[2], borrow);
let (d3, _) = sbb($modulus.0[3], self.0[3], borrow);
// `tmp` could be `MODULUS` if `self` was zero. Create a mask that is
// zero if `self` was zero, and `u64::max_value()` if self was nonzero.
let mask =
(((self.0[0] | self.0[1] | self.0[2] | self.0[3]) == 0) as u64).wrapping_sub(1);
$field([d0 & mask, d1 & mask, d2 & mask, d3 & mask])
}
}
};
}
macro_rules! field_specific {
($field:ident, $modulus:ident, $inv:ident, sparse) => {
impl $field {
/// Adds `rhs` to `self`, returning the result.
#[inline]
pub const fn add(&self, rhs: &Self) -> Self {
let (d0, carry) = adc(self.0[0], rhs.0[0], 0);
let (d1, carry) = adc(self.0[1], rhs.0[1], carry);
let (d2, carry) = adc(self.0[2], rhs.0[2], carry);
let (d3, _) = adc(self.0[3], rhs.0[3], carry);
// Attempt to subtract the modulus, to ensure the value
// is smaller than the modulus.
(&$field([d0, d1, d2, d3])).sub(&$modulus)
}
#[allow(clippy::too_many_arguments)]
#[inline(always)]
pub(crate) const fn montgomery_reduce(
r0: u64,
r1: u64,
r2: u64,
r3: u64,
r4: u64,
r5: u64,
r6: u64,
r7: u64,
) -> $field {
// The Montgomery reduction here is based on Algorithm 14.32 in
// Handbook of Applied Cryptography
// <http://cacr.uwaterloo.ca/hac/about/chap14.pdf>.
let k = r0.wrapping_mul($inv);
let (_, carry) = mac(r0, k, $modulus.0[0], 0);
let (r1, carry) = mac(r1, k, $modulus.0[1], carry);
let (r2, carry) = mac(r2, k, $modulus.0[2], carry);
let (r3, carry) = mac(r3, k, $modulus.0[3], carry);
let (r4, carry2) = adc(r4, 0, carry);
let k = r1.wrapping_mul($inv);
let (_, carry) = mac(r1, k, $modulus.0[0], 0);
let (r2, carry) = mac(r2, k, $modulus.0[1], carry);
let (r3, carry) = mac(r3, k, $modulus.0[2], carry);
let (r4, carry) = mac(r4, k, $modulus.0[3], carry);
let (r5, carry2) = adc(r5, carry2, carry);
let k = r2.wrapping_mul($inv);
let (_, carry) = mac(r2, k, $modulus.0[0], 0);
let (r3, carry) = mac(r3, k, $modulus.0[1], carry);
let (r4, carry) = mac(r4, k, $modulus.0[2], carry);
let (r5, carry) = mac(r5, k, $modulus.0[3], carry);
let (r6, carry2) = adc(r6, carry2, carry);
let k = r3.wrapping_mul($inv);
let (_, carry) = mac(r3, k, $modulus.0[0], 0);
let (r4, carry) = mac(r4, k, $modulus.0[1], carry);
let (r5, carry) = mac(r5, k, $modulus.0[2], carry);
let (r6, carry) = mac(r6, k, $modulus.0[3], carry);
let (r7, _) = adc(r7, carry2, carry);
// Result may be within MODULUS of the correct value
(&$field([r4, r5, r6, r7])).sub(&$modulus)
}
}
};
($field:ident, $modulus:ident, $inv:ident, dense) => {
impl $field {
/// Adds `rhs` to `self`, returning the result.
#[inline]
pub const fn add(&self, rhs: &Self) -> Self {
let (d0, carry) = adc(self.0[0], rhs.0[0], 0);
let (d1, carry) = adc(self.0[1], rhs.0[1], carry);
let (d2, carry) = adc(self.0[2], rhs.0[2], carry);
let (d3, carry) = adc(self.0[3], rhs.0[3], carry);
// Attempt to subtract the modulus, to ensure the value
// is smaller than the modulus.
let (d0, borrow) = sbb(d0, $modulus.0[0], 0);
let (d1, borrow) = sbb(d1, $modulus.0[1], borrow);
let (d2, borrow) = sbb(d2, $modulus.0[2], borrow);
let (d3, borrow) = sbb(d3, $modulus.0[3], borrow);
let (_, borrow) = sbb(carry, 0, borrow);
let (d0, carry) = adc(d0, $modulus.0[0] & borrow, 0);
let (d1, carry) = adc(d1, $modulus.0[1] & borrow, carry);
let (d2, carry) = adc(d2, $modulus.0[2] & borrow, carry);
let (d3, _) = adc(d3, $modulus.0[3] & borrow, carry);
$field([d0, d1, d2, d3])
}
#[allow(clippy::too_many_arguments)]
#[inline(always)]
pub(crate) const fn montgomery_reduce(
r0: u64,
r1: u64,
r2: u64,
r3: u64,
r4: u64,
r5: u64,
r6: u64,
r7: u64,
) -> Self {
// The Montgomery reduction here is based on Algorithm 14.32 in
// Handbook of Applied Cryptography
// <http://cacr.uwaterloo.ca/hac/about/chap14.pdf>.
let k = r0.wrapping_mul($inv);
let (_, carry) = mac(r0, k, $modulus.0[0], 0);
let (r1, carry) = mac(r1, k, $modulus.0[1], carry);
let (r2, carry) = mac(r2, k, $modulus.0[2], carry);
let (r3, carry) = mac(r3, k, $modulus.0[3], carry);
let (r4, carry2) = adc(r4, 0, carry);
let k = r1.wrapping_mul($inv);
let (_, carry) = mac(r1, k, $modulus.0[0], 0);
let (r2, carry) = mac(r2, k, $modulus.0[1], carry);
let (r3, carry) = mac(r3, k, $modulus.0[2], carry);
let (r4, carry) = mac(r4, k, $modulus.0[3], carry);
let (r5, carry2) = adc(r5, carry2, carry);
let k = r2.wrapping_mul($inv);
let (_, carry) = mac(r2, k, $modulus.0[0], 0);
let (r3, carry) = mac(r3, k, $modulus.0[1], carry);
let (r4, carry) = mac(r4, k, $modulus.0[2], carry);
let (r5, carry) = mac(r5, k, $modulus.0[3], carry);
let (r6, carry2) = adc(r6, carry2, carry);
let k = r3.wrapping_mul($inv);
let (_, carry) = mac(r3, k, $modulus.0[0], 0);
let (r4, carry) = mac(r4, k, $modulus.0[1], carry);
let (r5, carry) = mac(r5, k, $modulus.0[2], carry);
let (r6, carry) = mac(r6, k, $modulus.0[3], carry);
let (r7, carry2) = adc(r7, carry2, carry);
// Result may be within MODULUS of the correct value
let (d0, borrow) = sbb(r4, $modulus.0[0], 0);
let (d1, borrow) = sbb(r5, $modulus.0[1], borrow);
let (d2, borrow) = sbb(r6, $modulus.0[2], borrow);
let (d3, borrow) = sbb(r7, $modulus.0[3], borrow);
let (_, borrow) = sbb(carry2, 0, borrow);
let (d0, carry) = adc(d0, $modulus.0[0] & borrow, 0);
let (d1, carry) = adc(d1, $modulus.0[1] & borrow, carry);
let (d2, carry) = adc(d2, $modulus.0[2] & borrow, carry);
let (d3, _) = adc(d3, $modulus.0[3] & borrow, carry);
$field([d0, d1, d2, d3])
}
}
};
}

View File

@@ -1,165 +0,0 @@
//! The Pallas and iso-Pallas elliptic curve groups.
use super::{Fp, Fq, Secp256k1, Secp256k1Affine};
/// The base field of the Pallas and iso-Pallas curves.
pub type Base = Fp;
/// The scalar field of the Pallas and iso-Pallas curves.
pub type Scalar = Fq;
/// A Pallas point in the projective coordinate space.
pub type Point = Secp256k1;
/// A Pallas point in the affine coordinate space (or the point at infinity).
pub type Affine = Secp256k1Affine;
// TODO: redo ISO tests
// #[cfg(feature = "alloc")]
// #[test]
// #[allow(clippy::many_single_char_names)]
// fn test_iso_map() {
// use crate::arithmetic::CurveExt;
// use group::Group;
// // This is a regression test (it's the same input to iso_map as for hash_to_curve
// // with domain prefix "z.cash:test", Shake128, and input b"hello"). We don't
// // implement Shake128 any more but that's fine.
// let r = super::IsoEp::new_jacobian(
// Base::from_raw([
// 0xc37f111df5c4419e,
// 0x593c053e5e2337ad,
// 0x9c6cfc47bce1aba6,
// 0x0a881e4d556945aa,
// ]),
// Base::from_raw([
// 0xf234e04434502b47,
// 0x6979f7f2b0acf188,
// 0xa62eec46f662cb4e,
// 0x035e5c8a06d5cfb4,
// ]),
// Base::from_raw([
// 0x11ab791d4fb6f6b4,
// 0x575baa717958ef1f,
// 0x6ac4e343558dcbf3,
// 0x3af37975b0933125,
// ]),
// )
// .unwrap();
// let p = super::hashtocurve::iso_map::<_, Point, super::IsoEp>(&r, &Ep::ISOGENY_CONSTANTS);
// let (x, y, z) = p.jacobian_coordinates();
// assert!(
// format!("{:?}", x) == "0x318cc15f281662b3f26d0175cab97b924870c837879cac647e877be51a85e898"
// );
// assert!(
// format!("{:?}", y) == "0x1e91e2fa2a5a6a5bc86ff9564ae9336084470e7119dffcb85ae8c1383a3defd7"
// );
// assert!(
// format!("{:?}", z) == "0x1e049436efa754f5f189aec69c2c3a4a559eca6a12b45c3f2e4a769deeca6187"
// );
// // check that iso_map([2] r) = [2] iso_map(r)
// let r2 = r.double();
// assert!(bool::from(r2.is_on_curve()));
// let p2 = super::hashtocurve::iso_map::<_, Point, super::IsoEp>(&r2, &Ep::ISOGENY_CONSTANTS);
// assert!(bool::from(p2.is_on_curve()));
// assert!(p2 == p.double());
// }
// #[cfg(feature = "alloc")]
// #[test]
// fn test_iso_map_identity() {
// use crate::arithmetic::CurveExt;
// use group::Group;
// let r = super::IsoEp::new_jacobian(
// Base::from_raw([
// 0xc37f111df5c4419e,
// 0x593c053e5e2337ad,
// 0x9c6cfc47bce1aba6,
// 0x0a881e4d556945aa,
// ]),
// Base::from_raw([
// 0xf234e04434502b47,
// 0x6979f7f2b0acf188,
// 0xa62eec46f662cb4e,
// 0x035e5c8a06d5cfb4,
// ]),
// Base::from_raw([
// 0x11ab791d4fb6f6b4,
// 0x575baa717958ef1f,
// 0x6ac4e343558dcbf3,
// 0x3af37975b0933125,
// ]),
// )
// .unwrap();
// let r = (r * -Fq::one()) + r;
// assert!(bool::from(r.is_on_curve()));
// assert!(bool::from(r.is_identity()));
// let p = super::hashtocurve::iso_map::<_, Point, super::IsoEp>(&r, &Ep::ISOGENY_CONSTANTS);
// assert!(bool::from(p.is_on_curve()));
// assert!(bool::from(p.is_identity()));
// }
// #[cfg(feature = "alloc")]
// #[test]
// fn test_map_to_curve_simple_swu() {
// use crate::arithmetic::CurveExt;
// use crate::curves::IsoEp;
// use crate::hashtocurve::map_to_curve_simple_swu;
// // The zero input is a special case.
// let p: IsoEp = map_to_curve_simple_swu::<Fp, Ep, IsoEp>(&Fp::zero(), Ep::THETA, Ep::Z);
// let (x, y, z) = p.jacobian_coordinates();
// assert!(
// format!("{:?}", x) == "0x28c1a6a534f56c52e25295b339129a8af5f42525dea727f485ca3433519b096e"
// );
// assert!(
// format!("{:?}", y) == "0x3bfc658bee6653c63c7d7f0927083fd315d29c270207b7c7084fa1ee6ac5ae8d"
// );
// assert!(
// format!("{:?}", z) == "0x054b3ba10416dc104157b1318534a19d5d115472da7d746f8a5f250cd8cdef36"
// );
// let p: IsoEp = map_to_curve_simple_swu::<Fp, Ep, IsoEp>(&Fp::one(), Ep::THETA, Ep::Z);
// let (x, y, z) = p.jacobian_coordinates();
// assert!(
// format!("{:?}", x) == "0x010cba5957e876534af5e967c026a1856d64b071068280837913b9a5a3561505"
// );
// assert!(
// format!("{:?}", y) == "0x062fc61f9cd3118e7d6e65a065ebf46a547514d6b08078e976fa6d515dcc9c81"
// );
// assert!(
// format!("{:?}", z) == "0x3f86cb8c311250c3101c4e523e7793605ccff5623de1753a7c75bc9a29a73688"
// );
// }
// #[cfg(feature = "alloc")]
// #[test]
// fn test_hash_to_curve() {
// use crate::arithmetic::CurveExt;
// use group::Group;
// // This test vector is chosen so that the first map_to_curve_simple_swu takes the gx1 square
// // "branch" and the second takes the gx1 non-square "branch" (opposite to the Vesta test vector).
// let hash = Point::hash_to_curve("z.cash:test");
// let p: Point = hash(b"Trans rights now!");
// let (x, y, z) = p.jacobian_coordinates();
// assert!(
// format!("{:?}", x) == "0x36a6e3a9c50b7b6540cb002c977c82f37f8a875fb51eb35327ee1452e6ce7947"
// );
// assert!(
// format!("{:?}", y) == "0x01da3b4403d73252f2d7e9c19bc23dc6a080f2d02f8262fca4f7e3d756ac6a7c"
// );
// assert!(
// format!("{:?}", z) == "0x1d48103df8fcbb70d1809c1806c95651dd884a559fec0549658537ce9d94bed9"
// );
// assert!(bool::from(p.is_on_curve()));
// let p = (p * -Fq::one()) + p;
// assert!(bool::from(p.is_on_curve()));
// assert!(bool::from(p.is_identity()));
// }

View File

@@ -1,77 +0,0 @@
//! The Vesta and iso-Vesta elliptic curve groups.
use super::{Fp, Fq, Secq256k1, Secq256k1Affine};
/// The base field of the Vesta and iso-Vesta curves.
pub type Base = Fq;
/// The scalar field of the Vesta and iso-Vesta curves.
pub type Scalar = Fp;
/// A Vesta point in the projective coordinate space.
pub type Point = Secq256k1;
/// A Vesta point in the affine coordinate space (or the point at infinity).
pub type Affine = Secq256k1Affine;
// TODO: redo these tests
// #[cfg(feature = "alloc")]
// #[test]
// fn test_map_to_curve_simple_swu() {
// use crate::curves::IsoSecq256k1;
// use crate::hashtocurve::map_to_curve_simple_swu;
// use pasta_curves::arithmetic::CurveExt;
// // The zero input is a special case.
// let p: IsoSecq256k1 = map_to_curve_simple_swu::<Fq, Secq256k1, IsoSecq256k1>(
// &Fq::zero(),
// Secq256k1::THETA,
// Secq256k1::Z,
// );
// let (x, y, z) = p.jacobian_coordinates();
// assert!(
// format!("{:?}", x) == "0x2ccc4c6ec2660e5644305bc52527d904d408f92407f599df8f158d50646a2e78"
// );
// assert!(
// format!("{:?}", y) == "0x29a34381321d13d72d50b6b462bb4ea6a9e47393fa28a47227bf35bc0ee7aa59"
// );
// assert!(
// format!("{:?}", z) == "0x0b851e9e579403a76df1100f556e1f226e5656bdf38f3bf8601d8a3a9a15890b"
// );
// let p: IsoEq = map_to_curve_simple_swu::<Fq, Eq, IsoEq>(&Fq::one(), Eq::THETA, Eq::Z);
// let (x, y, z) = p.jacobian_coordinates();
// assert!(
// format!("{:?}", x) == "0x165f8b71841c5abc3d742ec13fb16f099d596b781e6f5c7d0b6682b1216a8258"
// );
// assert!(
// format!("{:?}", y) == "0x0dadef21de74ed7337a37dd74f126a92e4df73c3a704da501e36eaf59cf03120"
// );
// assert!(
// format!("{:?}", z) == "0x0a3d6f6c1af02bd9274cc0b80129759ce77edeef578d7de968d4a47d39026c82"
// );
// }
// #[cfg(feature = "alloc")]
// #[test]
// fn test_hash_to_curve() {
// use crate::arithmetic::CurveExt;
// // This test vector is chosen so that the first map_to_curve_simple_swu takes the gx1 non-square
// // "branch" and the second takes the gx1 square "branch" (opposite to the Pallas test vector).
// let hash = Point::hash_to_curve("z.cash:test");
// let p: Point = hash(b"hello");
// let (x, y, z) = p.jacobian_coordinates();
// assert!(
// format!("{:?}", x) == "0x12763505036e0e1a6684b7a7d8d5afb7378cc2b191a95e34f44824a06fcbd08e"
// );
// assert!(
// format!("{:?}", y) == "0x0256eafc0188b79bfa7c4b2b393893ddc298e90da500fa4a9aee17c2ea4240e6"
// );
// assert!(
// format!("{:?}", z) == "0x1b58d4aa4d68c3f4d9916b77c79ff9911597a27f2ee46244e98eb9615172d2ad"
// );
// }

View File

@@ -1,230 +0,0 @@
use ff::Field;
use group::prime::PrimeCurveAffine;
use pasta_curves::arithmetic::{CurveAffine, CurveExt};
use rand_core::OsRng;
pub fn curve_tests<G: CurveExt>() {
is_on_curve::<G>();
equality::<G>();
projective_to_affine_affine_to_projective::<G>();
projective_addition::<G>();
mixed_addition::<G>();
multiplication::<G>();
batch_normalize::<G>();
}
fn is_on_curve<G: CurveExt>() {
assert!(bool::from(G::identity().is_on_curve()));
assert!(bool::from(G::generator().is_on_curve()));
for _ in 0..100 {
let point = G::random(OsRng);
assert!(bool::from(point.is_on_curve()));
let affine_point: G::AffineExt = point.into();
assert!(bool::from(affine_point.is_on_curve()));
}
}
fn equality<G: CurveExt>() {
let a = G::generator();
let b = G::identity();
assert!(a == a);
assert!(b == b);
assert!(a != b);
assert!(b != a);
for _ in 0..100 {
let a = G::random(OsRng);
let b = G::random(OsRng);
assert!(a == a);
assert!(b == b);
assert!(a != b);
assert!(b != a);
let a: G::AffineExt = a.into();
let b: G::AffineExt = b.into();
assert!(a == a);
assert!(b == b);
assert!(a != b);
assert!(b != a);
}
}
fn projective_to_affine_affine_to_projective<G: CurveExt>() {
let a = G::generator();
let b = G::identity();
assert!(bool::from(G::AffineExt::from(a).is_on_curve()));
assert!(!bool::from(G::AffineExt::from(a).is_identity()));
assert!(bool::from(G::AffineExt::from(b).is_on_curve()));
assert!(bool::from(G::AffineExt::from(b).is_identity()));
let a = G::AffineExt::generator();
let b = G::AffineExt::identity();
assert!(bool::from(G::from(a).is_on_curve()));
assert!(!bool::from(G::from(a).is_identity()));
assert!(bool::from(G::from(b).is_on_curve()));
assert!(bool::from(G::from(b).is_identity()));
}
fn projective_addition<G: CurveExt>() {
let a = G::identity();
let b = G::identity();
let c = a + b;
assert!(bool::from(c.is_identity()));
assert!(bool::from(c.is_on_curve()));
let c = a - b;
assert!(bool::from(c.is_identity()));
assert!(bool::from(c.is_on_curve()));
let a = G::identity();
let a = -a;
assert!(bool::from(a.is_on_curve()));
assert!(bool::from(a.is_identity()));
let a = G::random(OsRng);
assert!(a == a + G::identity());
assert!(a == G::identity() + a);
assert!(-a == G::identity() - a);
let a = G::identity();
let a = a.double();
assert!(bool::from(c.is_on_curve()));
assert!(bool::from(a.is_identity()));
let a = G::generator();
let a = a.double();
assert!(bool::from(c.is_on_curve()));
assert_eq!(a, G::generator() + G::generator());
let a = G::random(OsRng);
assert!(a.double() - a == a);
let a = G::random(OsRng);
let b = G::random(OsRng);
let c = G::random(OsRng);
assert!(a + b == b + a);
assert!(a - b == -(b - a));
assert!(c + (a + b) == a + (c + b));
assert!((a - b) - c == (a - c) - b);
let a = G::generator().double().double(); // 4P
let b = G::generator().double(); // 2P
let c = a + b;
let mut d = G::generator();
for _ in 0..5 {
d += G::generator();
}
assert!(c == d);
assert!(!bool::from(c.is_identity()));
assert!(bool::from(c.is_on_curve()));
assert!(!bool::from(d.is_identity()));
assert!(bool::from(d.is_on_curve()));
}
fn mixed_addition<G: CurveExt>() {
let a = G::identity();
let b = G::AffineRepr::identity();
let c = a + b;
assert!(bool::from(c.is_identity()));
assert!(bool::from(c.is_on_curve()));
let c = a - b;
assert!(bool::from(c.is_identity()));
assert!(bool::from(c.is_on_curve()));
let a = G::identity();
let a = -a;
assert!(bool::from(a.is_on_curve()));
assert!(bool::from(a.is_identity()));
let a = G::AffineExt::identity();
let a = -a;
assert!(bool::from(a.is_on_curve()));
assert!(bool::from(a.is_identity()));
let a: G::AffineExt = G::random(OsRng).into();
assert!(a.to_curve() == a + G::AffineExt::identity());
let a = G::random(OsRng);
assert!(a.double() - a == a);
let a = G::random(OsRng);
let b: G::AffineExt = G::random(OsRng).into();
let c0 = a + b;
let c1 = a + G::from(b);
assert_eq!(c0, c1);
}
fn batch_normalize<G: CurveExt>() {
let a = G::generator().double();
let b = a.double();
let c = b.double();
for a_identity in (0..1).map(|n| n == 1) {
for b_identity in (0..1).map(|n| n == 1) {
for c_identity in (0..1).map(|n| n == 1) {
let mut v = [a, b, c];
if a_identity {
v[0] = G::identity()
}
if b_identity {
v[1] = G::identity()
}
if c_identity {
v[2] = G::identity()
}
let mut t = [
G::AffineExt::identity(),
G::AffineExt::identity(),
G::AffineExt::identity(),
];
let expected = [
G::AffineExt::from(v[0]),
G::AffineExt::from(v[1]),
G::AffineExt::from(v[2]),
];
G::batch_normalize(&v[..], &mut t[..]);
assert_eq!(&t[..], &expected[..]);
}
}
}
}
fn multiplication<G: CurveExt>() {
for _ in 1..1000 {
let s1 = G::ScalarExt::random(OsRng);
let s2 = G::ScalarExt::random(OsRng);
let t0 = G::identity() * s1;
assert!(bool::from(t0.is_identity()));
let a = G::random(OsRng);
let t0 = a * G::ScalarExt::one();
assert_eq!(a, t0);
let t0 = a * G::ScalarExt::zero();
assert!(bool::from(t0.is_identity()));
let t0 = a * s1 + a * s2;
let s3 = s1 + s2;
let t1 = a * s3;
assert_eq!(t0, t1);
let mut t0 = a * s1;
let mut t1 = a * s2;
t0 += t1;
let s3 = s1 + s2;
t1 = a * s3;
assert_eq!(t0, t1);
}
}

View File

@@ -1,209 +0,0 @@
use ark_std::{end_timer, start_timer};
use ff::Field;
use rand::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng;
pub fn random_field_tests<F: Field>(type_name: String) {
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
random_multiplication_tests::<F, _>(&mut rng, type_name.clone());
random_addition_tests::<F, _>(&mut rng, type_name.clone());
random_subtraction_tests::<F, _>(&mut rng, type_name.clone());
random_negation_tests::<F, _>(&mut rng, type_name.clone());
random_doubling_tests::<F, _>(&mut rng, type_name.clone());
random_squaring_tests::<F, _>(&mut rng, type_name.clone());
random_inversion_tests::<F, _>(&mut rng, type_name.clone());
random_expansion_tests::<F, _>(&mut rng, type_name);
assert_eq!(F::zero().is_zero().unwrap_u8(), 1);
{
let mut z = F::zero();
z = z.neg();
assert_eq!(z.is_zero().unwrap_u8(), 1);
}
assert!(bool::from(F::zero().invert().is_none()));
// Multiplication by zero
{
let mut a = F::random(&mut rng);
a.mul_assign(&F::zero());
assert_eq!(a.is_zero().unwrap_u8(), 1);
}
// Addition by zero
{
let mut a = F::random(&mut rng);
let copy = a;
a.add_assign(&F::zero());
assert_eq!(a, copy);
}
}
fn random_multiplication_tests<F: Field, R: RngCore>(mut rng: R, type_name: String) {
let message = format!("multiplication {}", type_name);
let start = start_timer!(|| message);
for _ in 0..1000000 {
let a = F::random(&mut rng);
let b = F::random(&mut rng);
let c = F::random(&mut rng);
let mut t0 = a; // (a * b) * c
t0.mul_assign(&b);
t0.mul_assign(&c);
let mut t1 = a; // (a * c) * b
t1.mul_assign(&c);
t1.mul_assign(&b);
let mut t2 = b; // (b * c) * a
t2.mul_assign(&c);
t2.mul_assign(&a);
assert_eq!(t0, t1);
assert_eq!(t1, t2);
}
end_timer!(start);
}
fn random_addition_tests<F: Field, R: RngCore>(mut rng: R, type_name: String) {
let message = format!("addition {}", type_name);
let start = start_timer!(|| message);
for _ in 0..1000000 {
let a = F::random(&mut rng);
let b = F::random(&mut rng);
let c = F::random(&mut rng);
let mut t0 = a; // (a + b) + c
t0.add_assign(&b);
t0.add_assign(&c);
let mut t1 = a; // (a + c) + b
t1.add_assign(&c);
t1.add_assign(&b);
let mut t2 = b; // (b + c) + a
t2.add_assign(&c);
t2.add_assign(&a);
assert_eq!(t0, t1);
assert_eq!(t1, t2);
}
end_timer!(start);
}
fn random_subtraction_tests<F: Field, R: RngCore>(mut rng: R, type_name: String) {
let message = format!("subtraction {}", type_name);
let start = start_timer!(|| message);
for _ in 0..1000000 {
let a = F::random(&mut rng);
let b = F::random(&mut rng);
let mut t0 = a; // (a - b)
t0.sub_assign(&b);
let mut t1 = b; // (b - a)
t1.sub_assign(&a);
let mut t2 = t0; // (a - b) + (b - a) = 0
t2.add_assign(&t1);
assert_eq!(t2.is_zero().unwrap_u8(), 1);
}
end_timer!(start);
}
fn random_negation_tests<F: Field, R: RngCore>(mut rng: R, type_name: String) {
let message = format!("negation {}", type_name);
let start = start_timer!(|| message);
for _ in 0..1000000 {
let a = F::random(&mut rng);
let mut b = a;
b = b.neg();
b.add_assign(&a);
assert_eq!(b.is_zero().unwrap_u8(), 1);
}
end_timer!(start);
}
fn random_doubling_tests<F: Field, R: RngCore>(mut rng: R, type_name: String) {
let message = format!("doubling {}", type_name);
let start = start_timer!(|| message);
for _ in 0..1000000 {
let mut a = F::random(&mut rng);
let mut b = a;
a.add_assign(&b);
b = b.double();
assert_eq!(a, b);
}
end_timer!(start);
}
fn random_squaring_tests<F: Field, R: RngCore>(mut rng: R, type_name: String) {
let message = format!("squaring {}", type_name);
let start = start_timer!(|| message);
for _ in 0..1000000 {
let mut a = F::random(&mut rng);
let mut b = a;
a.mul_assign(&b);
b = b.square();
assert_eq!(a, b);
}
end_timer!(start);
}
fn random_inversion_tests<F: Field, R: RngCore>(mut rng: R, type_name: String) {
assert!(bool::from(F::zero().invert().is_none()));
let message = format!("inversion {}", type_name);
let start = start_timer!(|| message);
for _ in 0..1000000 {
let mut a = F::random(&mut rng);
let b = a.invert().unwrap(); // probablistically nonzero
a.mul_assign(&b);
assert_eq!(a, F::one());
}
end_timer!(start);
}
fn random_expansion_tests<F: Field, R: RngCore>(mut rng: R, type_name: String) {
let message = format!("expansion {}", type_name);
let start = start_timer!(|| message);
for _ in 0..1000000 {
// Compare (a + b)(c + d) and (a*c + b*c + a*d + b*d)
let a = F::random(&mut rng);
let b = F::random(&mut rng);
let c = F::random(&mut rng);
let d = F::random(&mut rng);
let mut t0 = a;
t0.add_assign(&b);
let mut t1 = c;
t1.add_assign(&d);
t0.mul_assign(&t1);
let mut t2 = a;
t2.mul_assign(&c);
let mut t3 = b;
t3.mul_assign(&c);
let mut t4 = a;
t4.mul_assign(&d);
let mut t5 = b;
t5.mul_assign(&d);
t2.add_assign(&t3);
t2.add_assign(&t4);
t2.add_assign(&t5);
assert_eq!(t0, t2);
}
end_timer!(start);
}

View File

@@ -1,2 +0,0 @@
pub mod curve;
pub mod field;

View File

@@ -5,7 +5,7 @@ use std::ops::{AddAssign, SubAssign};
use super::{ProjectivePoint, Secq256K1};
use crate::{EncodedPoint, Scalar};
use k256::elliptic_curve::subtle::Choice;
use primeorder::elliptic_curve::group::Group;
pub use primeorder::elliptic_curve::group::Group;
use primeorder::elliptic_curve::sec1::FromEncodedPoint;
use primeorder::elliptic_curve::sec1::ToEncodedPoint;
use primeorder::elliptic_curve::subtle::CtOption;

View File

@@ -1117,21 +1117,23 @@ mod tests {
assert_eq!(
R2,
FieldElement::from_bytes_wide(&[
191, 190, 201, 47, 115, 161, 45, 64, 196, 95, 183, 80, 25, 35, 81, 69, 1, 0, 0, 0,
209, 3, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
0, 0, 0, 0, 0, 0, 0, 0, 0
])
);
}
#[test]
fn test_from_bytes_wide_negative_one() {
println!("{:?}", (-&FieldElement::one()).to_bytes());
assert_eq!(
-&FieldElement::one(),
FieldElement::from_bytes_wide(&[
64, 65, 54, 208, 140, 94, 210, 191, 59, 160, 72, 175, 230, 220, 174, 186, 254, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0,
46, 252, 255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0,
])
);
}
@@ -1279,13 +1281,6 @@ mod tests {
*/
}
#[test]
fn test_sqrt() {
let a = FieldElement::from(123);
let result = a.sqrt().unwrap();
println!("result {:?}", result);
}
#[test]
fn test_inversion() {
assert_eq!(FieldElement::zero().invert().is_none().unwrap_u8(), 1);
@@ -1315,9 +1310,9 @@ mod tests {
#[test]
fn test_invert_is_pow() {
let q_minus_2 = [
0xbfd25e8cd036413f,
0xbaaedce6af48a03b,
0xfffffffffffffffe,
0xfffffffefffffc2d,
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff,
];
@@ -1342,7 +1337,12 @@ mod tests {
#[test]
fn test_from_raw() {
assert_eq!(
FieldElement::from_raw([0x402da1732fc9bebe, 0x4551231950b75fc4, 0x1, 0x0]),
FieldElement::from_raw([
0x00000001000003d0,
0x0000000000000000,
0x0000000000000000,
0x0000000000000000,
]),
FieldElement::from_raw([0xffffffffffffffff; 4])
);

View File

@@ -1132,9 +1132,9 @@ mod tests {
}
const LARGEST: FieldElement = FieldElement([
0xfffffffefffffc2e,
0xffffffffffffffff,
0xffffffffffffffff,
0xbfd25e8cd0364140,
0xbaaedce6af48a03b,
0xfffffffffffffffe,
0xffffffffffffffff,
0,
]);
@@ -1145,9 +1145,9 @@ mod tests {
tmp += &LARGEST;
let target = FieldElement([
0xfffffffefffffc2d,
0xffffffffffffffff,
0xffffffffffffffff,
0xbfd25e8cd036413f,
0xbaaedce6af48a03b,
0xfffffffffffffffe,
0xffffffffffffffff,
0,
]);
@@ -1246,18 +1246,6 @@ mod tests {
}
}
#[test]
fn test_sqrt() {
/*
let a = FieldElement::from_be_hex(
"4f513cd2261276cff62ee29f160e37ab696186232f43ae681fe57fad91ef2135",
);
println!("a {:?}", a);
let result = a.sqrt().unwrap();
println!("result {:?}", result);
*/
}
#[test]
fn test_inversion() {
assert_eq!(FieldElement::zero().invert().is_none().unwrap_u8(), 1);

View File

@@ -21,7 +21,7 @@ num-bigint = "0.4.3"
serde = "1.0.151"
byteorder = "1.4.3"
ff = "0.12.0"
secpq_curves = { path = "../secpq_curves" }
secq256k1 = { path = "../secq256k1" }
serde-wasm-bindgen = "0.4.5"
bincode = "1.3.3"
# Not directly using getrandom in this crate,
@@ -29,14 +29,16 @@ bincode = "1.3.3"
# and the "js" features needs to be enabled for wasm compatibility
getrandom = { version = "0.2.8", features = ["js"] }
poseidon = { path = "../poseidon" }
itertools = "0.9.0"
group = "0.12.0"
# Do not compile these dependencies when targeting wasm
[target.'cfg(not(target_family = "wasm"))'.dependencies]
nova-scotia = { git = "https://github.com/DanTehrani/Nova-Scotia.git" }
nova-snark = "0.9.0"
ff = "0.12.1"
ark-std = { version = "0.3.0", features = ["print-trace"] }
#[target.'cfg(not(target_family = "wasm"))'.dependencies]
#nova-scotia = { git = "https://github.com/DanTehrani/Nova-Scotia.git" }
#nova-snark = "0.9.0"
#ff = "0.12.1"
#ark-std = { version = "0.3.0", features = ["print-trace"] }
[[bin]]
name = "gen_spartan_inst"

View File

@@ -2,8 +2,9 @@
use bincode;
use ff::PrimeField;
use libspartan::Instance;
use nova_scotia::circom::circuit::R1CS;
use nova_scotia::circom::reader::load_r1cs;
use secq256k1::AffinePoint;
use secq256k1::FieldBytes;
use spartan_wasm::circom_reader::{load_r1cs_from_bin_file, R1CS};
use std::env::{args, current_dir};
use std::fs::File;
use std::io::Write;
@@ -31,14 +32,14 @@ pub fn load_as_spartan_inst(circuit_file: PathBuf, num_pub_inputs: usize) -> Ins
let root = current_dir().unwrap();
let circuit_file = root.join(circuit_file);
let r1cs = load_r1cs(&circuit_file);
let (r1cs, _) = load_r1cs_from_bin_file::<AffinePoint>(&circuit_file);
let spartan_inst = convert_to_spartan_r1cs(&r1cs, num_pub_inputs);
spartan_inst
}
fn convert_to_spartan_r1cs<F: PrimeField<Repr = [u8; 32]>>(
fn convert_to_spartan_r1cs<F: PrimeField<Repr = FieldBytes>>(
r1cs: &R1CS<F>,
num_pub_inputs: usize,
) -> Instance {
@@ -54,18 +55,18 @@ fn convert_to_spartan_r1cs<F: PrimeField<Repr = [u8; 32]>>(
let (a, b, c) = constraint;
for (j, coeff) in a.iter() {
let bytes: [u8; 32] = coeff.to_repr();
let bytes: [u8; 32] = coeff.to_repr().into();
A.push((i, *j, bytes));
}
for (j, coeff) in b.iter() {
let bytes: [u8; 32] = coeff.to_repr();
let bytes: [u8; 32] = coeff.to_repr().into();
B.push((i, *j, bytes));
}
for (j, coeff) in c.iter() {
let bytes: [u8; 32] = coeff.to_repr();
let bytes: [u8; 32] = coeff.to_repr().into();
C.push((i, *j, bytes));
}
}

View File

@@ -0,0 +1,223 @@
// Code borrowed from Nova-Scotia https://github.com/nalinbhardwaj/Nova-Scotia
use byteorder::{LittleEndian, ReadBytesExt};
use ff::PrimeField;
use group::Group;
use itertools::Itertools;
use std::{
collections::HashMap,
io::{BufReader, Error, ErrorKind, Read, Result, Seek, SeekFrom},
};
pub type Constraint<Fr> = (Vec<(usize, Fr)>, Vec<(usize, Fr)>, Vec<(usize, Fr)>);
#[derive(Clone)]
pub struct R1CS<Fr: PrimeField> {
pub num_inputs: usize,
pub num_aux: usize,
pub num_variables: usize,
pub constraints: Vec<Constraint<Fr>>,
}
// R1CSFile's header
#[derive(Debug, Default)]
pub struct Header {
pub field_size: u32,
pub prime_size: Vec<u8>,
pub n_wires: u32,
pub n_pub_out: u32,
pub n_pub_in: u32,
pub n_prv_in: u32,
pub n_labels: u64,
pub n_constraints: u32,
}
// R1CSFile parse result
#[derive(Debug, Default)]
pub struct R1CSFile<Fr: PrimeField> {
pub version: u32,
pub header: Header,
pub constraints: Vec<Constraint<Fr>>,
pub wire_mapping: Vec<u64>,
}
use std::fs::{File, OpenOptions};
use std::path::Path;
pub fn load_r1cs_from_bin_file<G1: Group>(filename: &Path) -> (R1CS<G1::Scalar>, Vec<usize>) {
let reader = OpenOptions::new()
.read(true)
.open(filename)
.expect("unable to open.");
load_r1cs_from_bin::<G1, _>(BufReader::new(reader))
}
pub fn load_r1cs_from_bin<G1: Group, R: Read + Seek>(reader: R) -> (R1CS<G1::Scalar>, Vec<usize>) {
let file = from_reader::<G1, R>(reader).expect("unable to read.");
let num_inputs = (1 + file.header.n_pub_in + file.header.n_pub_out) as usize;
let num_variables = file.header.n_wires as usize;
let num_aux = num_variables - num_inputs;
(
R1CS {
num_aux,
num_inputs,
num_variables,
constraints: file.constraints,
},
file.wire_mapping.iter().map(|e| *e as usize).collect_vec(),
)
}
pub(crate) fn read_field<R: Read, Fr: PrimeField>(mut reader: R) -> Result<Fr> {
let mut repr = Fr::zero().to_repr();
for digit in repr.as_mut().iter_mut() {
// TODO: may need to reverse order?
*digit = reader.read_u8()?;
}
let fr = Fr::from_repr(repr).unwrap();
Ok(fr)
}
fn read_header<R: Read>(mut reader: R, size: u64) -> Result<Header> {
let field_size = reader.read_u32::<LittleEndian>()?;
let mut prime_size = vec![0u8; field_size as usize];
reader.read_exact(&mut prime_size)?;
if size != 32 + field_size as u64 {
return Err(Error::new(
ErrorKind::InvalidData,
"Invalid header section size",
));
}
Ok(Header {
field_size,
prime_size,
n_wires: reader.read_u32::<LittleEndian>()?,
n_pub_out: reader.read_u32::<LittleEndian>()?,
n_pub_in: reader.read_u32::<LittleEndian>()?,
n_prv_in: reader.read_u32::<LittleEndian>()?,
n_labels: reader.read_u64::<LittleEndian>()?,
n_constraints: reader.read_u32::<LittleEndian>()?,
})
}
fn read_constraint_vec<R: Read, Fr: PrimeField>(
mut reader: R,
header: &Header,
) -> Result<Vec<(usize, Fr)>> {
let n_vec = reader.read_u32::<LittleEndian>()? as usize;
let mut vec = Vec::with_capacity(n_vec);
for _ in 0..n_vec {
vec.push((
reader.read_u32::<LittleEndian>()? as usize,
read_field::<&mut R, Fr>(&mut reader)?,
));
}
Ok(vec)
}
fn read_constraints<R: Read, Fr: PrimeField>(
mut reader: R,
size: u64,
header: &Header,
) -> Result<Vec<Constraint<Fr>>> {
// todo check section size
let mut vec = Vec::with_capacity(header.n_constraints as usize);
for _ in 0..header.n_constraints {
vec.push((
read_constraint_vec::<&mut R, Fr>(&mut reader, header)?,
read_constraint_vec::<&mut R, Fr>(&mut reader, header)?,
read_constraint_vec::<&mut R, Fr>(&mut reader, header)?,
));
}
Ok(vec)
}
fn read_map<R: Read>(mut reader: R, size: u64, header: &Header) -> Result<Vec<u64>> {
if size != header.n_wires as u64 * 8 {
return Err(Error::new(
ErrorKind::InvalidData,
"Invalid map section size",
));
}
let mut vec = Vec::with_capacity(header.n_wires as usize);
for _ in 0..header.n_wires {
vec.push(reader.read_u64::<LittleEndian>()?);
}
if vec[0] != 0 {
return Err(Error::new(
ErrorKind::InvalidData,
"Wire 0 should always be mapped to 0",
));
}
Ok(vec)
}
pub fn from_reader<G1: Group, R: Read + Seek>(mut reader: R) -> Result<R1CSFile<G1::Scalar>> {
let mut magic = [0u8; 4];
reader.read_exact(&mut magic)?;
if magic != [0x72, 0x31, 0x63, 0x73] {
// magic = "r1cs"
return Err(Error::new(ErrorKind::InvalidData, "Invalid magic number"));
}
let version = reader.read_u32::<LittleEndian>()?;
if version != 1 {
return Err(Error::new(ErrorKind::InvalidData, "Unsupported version"));
}
let num_sections = reader.read_u32::<LittleEndian>()?;
// section type -> file offset
let mut section_offsets = HashMap::<u32, u64>::new();
let mut section_sizes = HashMap::<u32, u64>::new();
// get file offset of each section
for _ in 0..num_sections {
let section_type = reader.read_u32::<LittleEndian>()?;
let section_size = reader.read_u64::<LittleEndian>()?;
let offset = reader.seek(SeekFrom::Current(0))?;
section_offsets.insert(section_type, offset);
section_sizes.insert(section_type, section_size);
reader.seek(SeekFrom::Current(section_size as i64))?;
}
let header_type = 1;
let constraint_type = 2;
let wire2label_type = 3;
reader.seek(SeekFrom::Start(*section_offsets.get(&header_type).unwrap()))?;
let header = read_header(&mut reader, *section_sizes.get(&header_type).unwrap())?;
if header.field_size != 32 {
return Err(Error::new(
ErrorKind::InvalidData,
"This parser only supports 32-byte fields",
));
}
// if header.prime_size != hex!("010000f093f5e1439170b97948e833285d588181b64550b829a031e1724e6430") {
// return Err(Error::new(ErrorKind::InvalidData, "This parser only supports bn256"));
// }
reader.seek(SeekFrom::Start(
*section_offsets.get(&constraint_type).unwrap(),
))?;
let constraints = read_constraints::<&mut R, <G1 as Group>::Scalar>(
&mut reader,
*section_sizes.get(&constraint_type).unwrap(),
&header,
)?;
reader.seek(SeekFrom::Start(
*section_offsets.get(&wire2label_type).unwrap(),
))?;
let wire_mapping = read_map(
&mut reader,
*section_sizes.get(&wire2label_type).unwrap(),
&header,
)?;
Ok(R1CSFile {
version,
header,
constraints,
wire_mapping,
})
}

View File

@@ -1 +1,4 @@
pub mod wasm;
#[cfg(not(target_family = "wasm"))]
pub mod circom_reader;

View File

@@ -4,11 +4,11 @@ use ff::PrimeField;
use libspartan::{Assignment, Instance, NIZKGens, NIZK};
use merlin::Transcript;
use poseidon::poseidon_k256::{hash, FieldElement};
use secpq_curves::group::Group;
use secq256k1::affine::Group;
use std::io::{Error, Read};
use wasm_bindgen::prelude::*;
pub type G1 = secpq_curves::secq256k1::Point;
pub type G1 = secq256k1::AffinePoint;
pub type F1 = <G1 as Group>::Scalar;
#[wasm_bindgen]
@@ -21,7 +21,7 @@ pub fn prove(circuit: &[u8], vars: &[u8], public_inputs: &[u8]) -> Result<Vec<u8
let witness = load_witness_from_bin_reader::<F1, _>(vars).unwrap();
let witness_bytes = witness
.iter()
.map(|w| w.to_repr())
.map(|w| w.to_repr().into())
.collect::<Vec<[u8; 32]>>();
let assignment = Assignment::new(&witness_bytes).unwrap();
@@ -215,8 +215,8 @@ mod test {
assert_eq!(
result.as_slice(),
&[
254, 169, 62, 101, 167, 215, 208, 82, 46, 81, 56, 87, 19, 47, 139, 218, 83, 225,
108, 197, 66, 13, 197, 52, 48, 235, 107, 54, 178, 251, 129, 123
181, 226, 121, 200, 61, 3, 57, 70, 184, 30, 115, 145, 192, 7, 138, 73, 36, 8, 40,
132, 190, 141, 35, 89, 108, 149, 235, 51, 129, 165, 64, 103
]
)
}

4
scripts/build.sh Normal file
View File

@@ -0,0 +1,4 @@
#!/bin/bash
sh ./scripts/build_wasm.sh &&
sh ./scripts/addr_membership_circuit.sh &&
sh ./scripts/pubkey_membership_circuit.sh