mirror of
https://github.com/personaelabs/spartan-ecdsa.git
synced 2026-01-10 14:38:04 -05:00
Merge branch 'main' into lsankar/membership-verifier
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -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
|
||||
@@ -1,7 +1,6 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"packages/spartan_wasm",
|
||||
"packages/secpq_curves",
|
||||
"packages/secq256k1",
|
||||
"packages/poseidon",
|
||||
]
|
||||
@@ -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",
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@ethereumjs/util": "^8.0.3",
|
||||
"spartan-ecdsa": "*"
|
||||
"@personaelabs/spartan-ecdsa": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"ts-node": "^10.9.1",
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
defaultPubkeyMembershipPConfig,
|
||||
defaultPubkeyMembershipVConfig,
|
||||
MembershipVerifier
|
||||
} from "spartan-ecdsa";
|
||||
} from "@personaelabs/spartan-ecdsa";
|
||||
import {
|
||||
hashPersonalMessage,
|
||||
ecsign,
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
defaultPubkeyMembershipPConfig,
|
||||
defaultPubkeyMembershipVConfig,
|
||||
defaultAddressMembershipVConfig
|
||||
} from "spartan-ecdsa";
|
||||
} from "@personaelabs/spartan-ecdsa";
|
||||
import {
|
||||
ecrecover,
|
||||
ecsign,
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
]
|
||||
];
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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
6
packages/lib/.npmignore
Normal file
@@ -0,0 +1,6 @@
|
||||
/node_modules
|
||||
/src
|
||||
/tests
|
||||
tsconfig.json
|
||||
jest.config.js
|
||||
copy_artifacts.sh
|
||||
@@ -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");
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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 }
|
||||
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
20
packages/poseidon/sage/security_inequalities.sage
Normal file
20
packages/poseidon/sage/security_inequalities.sage
Normal 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)))
|
||||
@@ -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",
|
||||
],
|
||||
];
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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))
|
||||
@@ -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);
|
||||
@@ -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
@@ -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()
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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])
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -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()));
|
||||
// }
|
||||
@@ -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"
|
||||
// );
|
||||
// }
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
pub mod curve;
|
||||
pub mod field;
|
||||
@@ -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;
|
||||
|
||||
@@ -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])
|
||||
);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
223
packages/spartan_wasm/src/circom_reader.rs
Normal file
223
packages/spartan_wasm/src/circom_reader.rs
Normal 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,
|
||||
})
|
||||
}
|
||||
@@ -1 +1,4 @@
|
||||
pub mod wasm;
|
||||
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
pub mod circom_reader;
|
||||
|
||||
@@ -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
4
scripts/build.sh
Normal file
@@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
sh ./scripts/build_wasm.sh &&
|
||||
sh ./scripts/addr_membership_circuit.sh &&
|
||||
sh ./scripts/pubkey_membership_circuit.sh
|
||||
Reference in New Issue
Block a user