mirror of
https://github.com/Qortal/pirate-librustzcash.git
synced 2025-01-30 07:22:15 +00:00
Merge branch 'develop'
This commit is contained in:
commit
2df2a2b2f2
18
.github/workflows/ci.yml
vendored
18
.github/workflows/ci.yml
vendored
@ -11,7 +11,7 @@ jobs:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.37.0
|
||||
toolchain: 1.39.0
|
||||
override: true
|
||||
|
||||
# cargo fmt does not build the code, and running it in a fresh clone of
|
||||
@ -30,6 +30,13 @@ jobs:
|
||||
command: fmt
|
||||
args: --all -- --check --color always
|
||||
|
||||
# Build benchmarks to prevent bitrot
|
||||
- name: Build benchmarks
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --all --benches
|
||||
|
||||
test:
|
||||
name: Test on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
@ -41,7 +48,7 @@ jobs:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.37.0
|
||||
toolchain: 1.39.0
|
||||
override: true
|
||||
- name: cargo fetch
|
||||
uses: actions-rs/cargo@v1
|
||||
@ -111,10 +118,3 @@ jobs:
|
||||
with:
|
||||
command: doc
|
||||
args: --all --document-private-items
|
||||
|
||||
# Build benchmarks to prevent bitrot
|
||||
- name: Build benchmarks
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --verbose --all --benches
|
||||
|
@ -1,6 +1,6 @@
|
||||
language: rust
|
||||
rust:
|
||||
- 1.37.0
|
||||
- 1.39.0
|
||||
|
||||
cache: cargo
|
||||
|
||||
|
432
Cargo.lock
generated
432
Cargo.lock
generated
@ -55,11 +55,26 @@ name = "assert_matches"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"hermit-abi 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "bech32"
|
||||
version = "0.7.1"
|
||||
@ -84,6 +99,7 @@ dependencies = [
|
||||
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_xorshift 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"subtle 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -152,6 +168,30 @@ dependencies = [
|
||||
"byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bls12_381"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"criterion 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"subtle 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex-automata 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "byte-tools"
|
||||
version = "0.3.1"
|
||||
@ -171,6 +211,14 @@ dependencies = [
|
||||
"ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cast"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.45"
|
||||
@ -181,6 +229,16 @@ name = "cfg-if"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.33.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cloudabi"
|
||||
version = "0.0.3"
|
||||
@ -194,6 +252,39 @@ name = "constant_time_eq"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "criterion"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"criterion-plot 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"csv 1.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"oorandom 11.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"plotters 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tinytemplate 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "criterion-plot"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam"
|
||||
version = "0.7.2"
|
||||
@ -201,7 +292,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -217,11 +308,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.7.1"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-epoch 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -237,6 +329,20 @@ dependencies = [
|
||||
"scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-queue"
|
||||
version = "0.1.2"
|
||||
@ -245,6 +351,15 @@ dependencies = [
|
||||
"crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-queue"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.6.6"
|
||||
@ -254,6 +369,16 @@ dependencies = [
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crunchy"
|
||||
version = "0.1.6"
|
||||
@ -272,6 +397,26 @@ dependencies = [
|
||||
"crypto_api 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "csv"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bstr 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"csv-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ryu 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "csv-core"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.8.1"
|
||||
@ -289,6 +434,11 @@ dependencies = [
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.6.2"
|
||||
@ -310,6 +460,7 @@ dependencies = [
|
||||
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ff_derive 0.6.0",
|
||||
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"subtle 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -382,6 +533,14 @@ dependencies = [
|
||||
"rand_xorshift 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.3.2"
|
||||
@ -404,6 +563,38 @@ dependencies = [
|
||||
"proc-macro-hack 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"wasm-bindgen 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jubjub"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"bls12_381 0.1.0",
|
||||
"criterion 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_xorshift 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"subtle 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
@ -439,6 +630,11 @@ dependencies = [
|
||||
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "maybe-uninit"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.3.3"
|
||||
@ -492,6 +688,11 @@ dependencies = [
|
||||
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oorandom"
|
||||
version = "11.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
version = "0.2.3"
|
||||
@ -502,10 +703,23 @@ name = "pairing"
|
||||
version = "0.16.0"
|
||||
dependencies = [
|
||||
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"criterion 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ff 0.6.0",
|
||||
"group 0.6.0",
|
||||
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_xorshift 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"subtle 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "plotters"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"js-sys 0.3.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasm-bindgen 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"web-sys 0.3.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -713,6 +927,28 @@ dependencies = [
|
||||
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rayon-core 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rdrand"
|
||||
version = "0.4.0"
|
||||
@ -732,6 +968,14 @@ dependencies = [
|
||||
"thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.16"
|
||||
@ -755,6 +999,19 @@ dependencies = [
|
||||
"semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.0.0"
|
||||
@ -781,6 +1038,31 @@ name = "semver-parser"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ryu 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.8.0"
|
||||
@ -807,6 +1089,14 @@ dependencies = [
|
||||
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.0.1"
|
||||
@ -815,21 +1105,103 @@ dependencies = [
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinytemplate"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.59"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasm-bindgen-macro 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.59"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bumpalo 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasm-bindgen-shared 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.59"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasm-bindgen-macro-support 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.59"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasm-bindgen-backend 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasm-bindgen-shared 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.59"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"js-sys 0.3.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasm-bindgen 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.8"
|
||||
@ -844,6 +1216,14 @@ name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
@ -884,6 +1264,7 @@ dependencies = [
|
||||
"blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"blake2s_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"criterion 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crypto_api_chachapoly 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ff 0.6.0",
|
||||
"fpe 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -898,6 +1279,7 @@ dependencies = [
|
||||
"ripemd160 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"secp256k1 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"subtle 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -923,7 +1305,9 @@ dependencies = [
|
||||
"checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee"
|
||||
"checksum arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba"
|
||||
"checksum assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7deb0a829ca7bcfaf5da70b073a8d128619259a7be8216a355e23f00763059e5"
|
||||
"checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
"checksum autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b671c8fb71b457dd4ae18c4ba1e59aa81793daacc361d82fcd410cef0d491875"
|
||||
"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
|
||||
"checksum bech32 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9e0089c35ab7c6f2bc55ab23f769913f0ac65b1023e7e74638a1f43128dd5df2"
|
||||
"checksum bigint 4.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ebecac13b3c745150d7b6c3ea7572d372f09d627c2077e893bf26c5c7f70d282"
|
||||
"checksum bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "02b4ff8b16e6076c3e14220b39fbc1fabb6737522281a388998046859400895f"
|
||||
@ -933,24 +1317,36 @@ dependencies = [
|
||||
"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
|
||||
"checksum block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774"
|
||||
"checksum block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6d4dc3af3ee2e12f3e5d224e5e1e3d73668abbeb69e566d361f7d5563a4fdf09"
|
||||
"checksum bstr 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "2889e6d50f394968c8bf4240dc3f2a7eb4680844d27308f798229ac9d4725f41"
|
||||
"checksum bumpalo 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1f359dc14ff8911330a51ef78022d376f25ed00248912803b58f00cb1c27f742"
|
||||
"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
|
||||
"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
|
||||
"checksum c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7d64d04786e0f528460fc884753cf8dddcc466be308f6026f8e355c41a0e4101"
|
||||
"checksum cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b9434b9a5aa1450faa3f9cb14ea0e8c53bb5d2b3c1bfd1ab4fc03e9f33fbfb0"
|
||||
"checksum cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)" = "4fc9a35e1f4290eb9e5fc54ba6cf40671ed2a2514c3eeb2b2a908dda2ea5a1be"
|
||||
"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33"
|
||||
"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
|
||||
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
|
||||
"checksum constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "995a44c877f9212528ccc74b21a232f66ad69001e40ede5bcee2ac9ef2657120"
|
||||
"checksum criterion 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1fc755679c12bda8e5523a71e4d654b6bf2e14bd838dfc48cde6559a05caf7d1"
|
||||
"checksum criterion-plot 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a01e15e0ea58e8234f96146b1f91fa9d0e4dd7a38da93ff7a75d42c0b9d3a545"
|
||||
"checksum crossbeam 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2d818a4990769aac0c7ff1360e233ef3a41adcb009ebb2036bf6915eb0f6b23c"
|
||||
"checksum crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c8ec7fcd21571dc78f96cc96243cab8d8f035247c3efd16c687be154c3fa9efa"
|
||||
"checksum crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71"
|
||||
"checksum crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285"
|
||||
"checksum crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9"
|
||||
"checksum crossbeam-epoch 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
|
||||
"checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b"
|
||||
"checksum crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db"
|
||||
"checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6"
|
||||
"checksum crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
|
||||
"checksum crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda"
|
||||
"checksum crypto_api 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2f855e87e75a4799e18b8529178adcde6fd4f97c1449ff4821e747ff728bb102"
|
||||
"checksum crypto_api_chachapoly 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "95b2ad7cab08fd71addba81df5077c49df208effdfb3118a1519f9cdeac5aaf2"
|
||||
"checksum csv 1.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "00affe7f6ab566df61b4be3ce8cf16bc2576bca0963ceb0955e45d514bf9a279"
|
||||
"checksum csv-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90"
|
||||
"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
|
||||
"checksum directories 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "72d337a64190607d4fcca2cb78982c5dd57f4916e19696b48a575fa746b6cb0f"
|
||||
"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
|
||||
"checksum env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3"
|
||||
"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
|
||||
"checksum fpe 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "21988a326139165b75e3196bc6962ca638e5fb0c95102fbf152a3743174b01e4"
|
||||
@ -959,12 +1355,17 @@ dependencies = [
|
||||
"checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4"
|
||||
"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
|
||||
"checksum getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "473a1265acc8ff1e808cd0a1af8cee3c2ee5200916058a2ca113c29f2d903571"
|
||||
"checksum hermit-abi 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1010591b26bbfe835e9faeabeb11866061cc7dcebffd56ad7d0942d0e61aefd8"
|
||||
"checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77"
|
||||
"checksum hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "961de220ec9a91af2e1e5bd80d02109155695e516771762381ef8581317066e0"
|
||||
"checksum hex-literal-impl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9d4c5c844e2fee0bf673d54c2c177f1713b3d2af2ff6e666b49cb7572e6cf42d"
|
||||
"checksum itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484"
|
||||
"checksum itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e"
|
||||
"checksum js-sys 0.3.36 (registry+https://github.com/rust-lang/crates.io-index)" = "1cb931d43e71f560c81badb0191596562bafad2be06a3f9025b845c847c60df5"
|
||||
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
"checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba"
|
||||
"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
|
||||
"checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
|
||||
"checksum memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
|
||||
"checksum memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce6075db033bbbb7ee5a0bbd3a3186bbae616f57fb001c485c7ff77955f8177f"
|
||||
"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945"
|
||||
@ -972,7 +1373,9 @@ dependencies = [
|
||||
"checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09"
|
||||
"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32"
|
||||
"checksum num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273"
|
||||
"checksum oorandom 11.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ebcec7c9c2a95cacc7cd0ecb89d8a8454eca13906f6deb55258ffff0adeb9405"
|
||||
"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
|
||||
"checksum plotters 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "4e3bb8da247d27ae212529352020f3e5ee16e83c0c258061d27b08ab92675eeb"
|
||||
"checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b"
|
||||
"checksum proc-macro-hack 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e688f31d92ffd7c1ddc57a1b4e6d773c0f2a14ee437a4b0a4f5a69c80eb221c8"
|
||||
"checksum proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e98a83a9f9b331f54b924e68a66acb1bb35cb01fb0a23645139967abefb697e8"
|
||||
@ -996,22 +1399,41 @@ dependencies = [
|
||||
"checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44"
|
||||
"checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c"
|
||||
"checksum rand_xorshift 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "77d416b86801d23dde1aa643023b775c3a462efc0ed96443add11546cdf1dca8"
|
||||
"checksum rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "db6ce3297f9c85e16621bb8cca38a06779ffc31bb8184e1be4bed2be4678a098"
|
||||
"checksum rayon-core 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "08a89b46efaf957e52b18062fb2f4660f8b8a4dde1807ca002690868ef2c85a9"
|
||||
"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
|
||||
"checksum regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "322cf97724bea3ee221b78fe25ac9c46114ebb51747ad5babd51a2fc6a8235a8"
|
||||
"checksum regex-automata 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4"
|
||||
"checksum regex-syntax 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)" = "1132f845907680735a84409c3bebc64d1364a5683ffbce899550cd09d5eaefc1"
|
||||
"checksum ripemd160 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ad5112e0dbbb87577bfbc56c42450235e3012ce336e29c5befd7807bd626da4a"
|
||||
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||
"checksum ryu 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535622e6be132bccd223f4bb2b8ac8d53cda3c7a6394944d3b2b33fb974f9d76"
|
||||
"checksum same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||
"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d"
|
||||
"checksum secp256k1 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e0344a794ff109f85547039536028e12f313178ac1545e49fdf16a530d900a7b"
|
||||
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
"checksum serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449"
|
||||
"checksum serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64"
|
||||
"checksum serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)" = "9371ade75d4c2d6cb154141b9752cf3781ec9c05e0e5cf35060e1e70ee7b9c25"
|
||||
"checksum sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d"
|
||||
"checksum subtle 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab3af2eb31c42e8f0ccf43548232556c42737e01a96db6e1777b0be108e79799"
|
||||
"checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf"
|
||||
"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||
"checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
|
||||
"checksum tinytemplate 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "57a3c6667d3e65eb1bc3aed6fd14011c6cbc3a0665218ab7f5daf040b9ec371a"
|
||||
"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9"
|
||||
"checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
|
||||
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
|
||||
"checksum walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d"
|
||||
"checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d"
|
||||
"checksum wasm-bindgen 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)" = "3557c397ab5a8e347d434782bcd31fc1483d927a6826804cec05cc792ee2519d"
|
||||
"checksum wasm-bindgen-backend 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)" = "e0da9c9a19850d3af6df1cb9574970b566d617ecfaf36eb0b706b6f3ef9bd2f8"
|
||||
"checksum wasm-bindgen-macro 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)" = "0f6fde1d36e75a714b5fe0cffbb78978f222ea6baebb726af13c78869fdb4205"
|
||||
"checksum wasm-bindgen-macro-support 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)" = "25bda4168030a6412ea8a047e27238cadf56f0e53516e1e83fec0a8b7c786f6d"
|
||||
"checksum wasm-bindgen-shared 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)" = "fc9f36ad51f25b0219a3d4d13b90eb44cd075dff8b6280cca015775d7acaddd8"
|
||||
"checksum web-sys 0.3.36 (registry+https://github.com/rust-lang/crates.io-index)" = "721c6263e2c66fd44501cc5efbfa2b7dfa775d13e4ea38c46299646ed1f9c70a"
|
||||
"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
|
||||
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
"checksum winapi-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4ccfbf554c6ad11084fb7517daca16cfdcaccbdadba4fc336f032a8b12c2ad80"
|
||||
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
@ -9,6 +9,8 @@ members = [
|
||||
"zcash_history",
|
||||
"zcash_primitives",
|
||||
"zcash_proofs",
|
||||
"jubjub",
|
||||
"bls12_381",
|
||||
]
|
||||
|
||||
[profile.release]
|
||||
|
@ -21,6 +21,7 @@ crossbeam = { version = "0.7", optional = true }
|
||||
pairing = { version = "0.16", path = "../pairing", optional = true }
|
||||
rand_core = "0.5"
|
||||
byteorder = "1"
|
||||
subtle = "2.2.1"
|
||||
|
||||
[dev-dependencies]
|
||||
hex-literal = "0.2"
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
use ff::{Field, PrimeField, ScalarEngine};
|
||||
use group::CurveProjective;
|
||||
use std::ops::{AddAssign, MulAssign, SubAssign};
|
||||
|
||||
use super::SynthesisError;
|
||||
|
||||
@ -62,7 +63,7 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
|
||||
// Compute omega, the 2^exp primitive root of unity
|
||||
let mut omega = E::Fr::root_of_unity();
|
||||
for _ in exp..E::Fr::S {
|
||||
omega.square();
|
||||
omega = omega.square();
|
||||
}
|
||||
|
||||
// Extend the coeffs vector with zeroes if necessary
|
||||
@ -72,11 +73,11 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
|
||||
coeffs,
|
||||
exp,
|
||||
omega,
|
||||
omegainv: omega.inverse().unwrap(),
|
||||
geninv: E::Fr::multiplicative_generator().inverse().unwrap(),
|
||||
omegainv: omega.invert().unwrap(),
|
||||
geninv: E::Fr::multiplicative_generator().invert().unwrap(),
|
||||
minv: E::Fr::from_str(&format!("{}", m))
|
||||
.unwrap()
|
||||
.inverse()
|
||||
.invert()
|
||||
.unwrap(),
|
||||
})
|
||||
}
|
||||
@ -105,7 +106,7 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
|
||||
worker.scope(self.coeffs.len(), |scope, chunk| {
|
||||
for (i, v) in self.coeffs.chunks_mut(chunk).enumerate() {
|
||||
scope.spawn(move |_scope| {
|
||||
let mut u = g.pow(&[(i * chunk) as u64]);
|
||||
let mut u = g.pow_vartime(&[(i * chunk) as u64]);
|
||||
for v in v.iter_mut() {
|
||||
v.group_mul_assign(&u);
|
||||
u.mul_assign(&g);
|
||||
@ -130,7 +131,7 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
|
||||
/// This evaluates t(tau) for this domain, which is
|
||||
/// tau^m - 1 for these radix-2 domains.
|
||||
pub fn z(&self, tau: &E::Fr) -> E::Fr {
|
||||
let mut tmp = tau.pow(&[self.coeffs.len() as u64]);
|
||||
let mut tmp = tau.pow_vartime(&[self.coeffs.len() as u64]);
|
||||
tmp.sub_assign(&E::Fr::one());
|
||||
|
||||
tmp
|
||||
@ -140,10 +141,7 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
|
||||
/// evaluation domain, so we must perform division over
|
||||
/// a coset.
|
||||
pub fn divide_by_z_on_coset(&mut self, worker: &Worker) {
|
||||
let i = self
|
||||
.z(&E::Fr::multiplicative_generator())
|
||||
.inverse()
|
||||
.unwrap();
|
||||
let i = self.z(&E::Fr::multiplicative_generator()).invert().unwrap();
|
||||
|
||||
worker.scope(self.coeffs.len(), |scope, chunk| {
|
||||
for v in self.coeffs.chunks_mut(chunk) {
|
||||
@ -296,7 +294,7 @@ fn serial_fft<E: ScalarEngine, T: Group<E>>(a: &mut [T], omega: &E::Fr, log_n: u
|
||||
|
||||
let mut m = 1;
|
||||
for _ in 0..log_n {
|
||||
let w_m = omega.pow(&[u64::from(n / (2 * m))]);
|
||||
let w_m = omega.pow_vartime(&[u64::from(n / (2 * m))]);
|
||||
|
||||
let mut k = 0;
|
||||
while k < n {
|
||||
@ -330,7 +328,7 @@ fn parallel_fft<E: ScalarEngine, T: Group<E>>(
|
||||
let num_cpus = 1 << log_cpus;
|
||||
let log_new_n = log_n - log_cpus;
|
||||
let mut tmp = vec![vec![T::group_zero(); 1 << log_new_n]; num_cpus];
|
||||
let new_omega = omega.pow(&[num_cpus as u64]);
|
||||
let new_omega = omega.pow_vartime(&[num_cpus as u64]);
|
||||
|
||||
worker.scope(0, |scope, _| {
|
||||
let a = &*a;
|
||||
@ -338,8 +336,8 @@ fn parallel_fft<E: ScalarEngine, T: Group<E>>(
|
||||
for (j, tmp) in tmp.iter_mut().enumerate() {
|
||||
scope.spawn(move |_scope| {
|
||||
// Shuffle into a sub-FFT
|
||||
let omega_j = omega.pow(&[j as u64]);
|
||||
let omega_step = omega.pow(&[(j as u64) << log_new_n]);
|
||||
let omega_j = omega.pow_vartime(&[j as u64]);
|
||||
let omega_step = omega.pow_vartime(&[(j as u64) << log_new_n]);
|
||||
|
||||
let mut elt = E::Fr::one();
|
||||
for (i, tmp) in tmp.iter_mut().enumerate() {
|
||||
|
@ -1,6 +1,7 @@
|
||||
//! Window table lookup gadgets.
|
||||
|
||||
use ff::{Field, ScalarEngine};
|
||||
use std::ops::{AddAssign, Neg};
|
||||
|
||||
use super::boolean::Boolean;
|
||||
use super::num::{AllocatedNum, Num};
|
||||
@ -15,8 +16,7 @@ where
|
||||
assert_eq!(assignment.len(), 1 << window_size);
|
||||
|
||||
for (i, constant) in constants.into_iter().enumerate() {
|
||||
let mut cur = assignment[i];
|
||||
cur.negate();
|
||||
let mut cur = assignment[i].neg();
|
||||
cur.add_assign(constant);
|
||||
assignment[i] = cur;
|
||||
for (j, eval) in assignment.iter_mut().enumerate().skip(i + 1) {
|
||||
@ -150,7 +150,7 @@ where
|
||||
let y = AllocatedNum::alloc(cs.namespace(|| "y"), || {
|
||||
let mut tmp = coords[*i.get()?].1;
|
||||
if *bits[2].get_value().get()? {
|
||||
tmp.negate();
|
||||
tmp = tmp.neg();
|
||||
}
|
||||
Ok(tmp)
|
||||
})?;
|
||||
@ -280,7 +280,7 @@ mod test {
|
||||
assert_eq!(res.0.get_value().unwrap(), points[index].0);
|
||||
let mut tmp = points[index].1;
|
||||
if c_val {
|
||||
tmp.negate()
|
||||
tmp = tmp.neg()
|
||||
}
|
||||
assert_eq!(res.1.get_value().unwrap(), tmp);
|
||||
}
|
||||
|
@ -50,7 +50,9 @@ impl<E: ScalarEngine, CS: ConstraintSystem<E>> MultiEq<E, CS> {
|
||||
|
||||
assert!((E::Fr::CAPACITY as usize) > (self.bits_used + num_bits));
|
||||
|
||||
let coeff = E::Fr::from_str("2").unwrap().pow(&[self.bits_used as u64]);
|
||||
let coeff = E::Fr::from_str("2")
|
||||
.unwrap()
|
||||
.pow_vartime(&[self.bits_used as u64]);
|
||||
self.lhs = self.lhs.clone() + (coeff, lhs);
|
||||
self.rhs = self.rhs.clone() + (coeff, rhs);
|
||||
self.bits_used += num_bits;
|
||||
|
@ -5,6 +5,7 @@ use super::num::Num;
|
||||
use super::Assignment;
|
||||
use crate::{ConstraintSystem, SynthesisError};
|
||||
use ff::{Field, PrimeField, ScalarEngine};
|
||||
use std::ops::AddAssign;
|
||||
|
||||
/// Takes a sequence of booleans and exposes them as compact
|
||||
/// public inputs
|
||||
@ -19,7 +20,7 @@ where
|
||||
for bit in bits {
|
||||
num = num.add_bool_with_coeff(CS::one(), bit, coeff);
|
||||
|
||||
coeff.double();
|
||||
coeff = coeff.double();
|
||||
}
|
||||
|
||||
let input = cs.alloc_input(|| format!("input {}", i), || Ok(*num.get_value().get()?))?;
|
||||
@ -62,7 +63,7 @@ pub fn compute_multipacking<E: ScalarEngine>(bits: &[bool]) -> Vec<E::Fr> {
|
||||
cur.add_assign(&coeff);
|
||||
}
|
||||
|
||||
coeff.double();
|
||||
coeff = coeff.double();
|
||||
}
|
||||
|
||||
result.push(cur);
|
||||
|
@ -1,6 +1,7 @@
|
||||
//! Gadgets representing numbers in the scalar field of the underlying curve.
|
||||
|
||||
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, ScalarEngine};
|
||||
use std::ops::{AddAssign, MulAssign};
|
||||
|
||||
use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable};
|
||||
|
||||
@ -176,7 +177,7 @@ impl<E: ScalarEngine> AllocatedNum<E> {
|
||||
for bit in result.iter().rev() {
|
||||
lc = lc + (coeff, bit.get_variable());
|
||||
|
||||
coeff.double();
|
||||
coeff = coeff.double();
|
||||
}
|
||||
|
||||
lc = lc - self.variable;
|
||||
@ -202,7 +203,7 @@ impl<E: ScalarEngine> AllocatedNum<E> {
|
||||
for bit in bits.iter() {
|
||||
lc = lc + (coeff, bit.get_variable());
|
||||
|
||||
coeff.double();
|
||||
coeff = coeff.double();
|
||||
}
|
||||
|
||||
lc = lc - self.variable;
|
||||
@ -253,8 +254,7 @@ impl<E: ScalarEngine> AllocatedNum<E> {
|
||||
let var = cs.alloc(
|
||||
|| "squared num",
|
||||
|| {
|
||||
let mut tmp = *self.value.get()?;
|
||||
tmp.square();
|
||||
let tmp = self.value.get()?.square();
|
||||
|
||||
value = Some(tmp);
|
||||
|
||||
@ -288,7 +288,7 @@ impl<E: ScalarEngine> AllocatedNum<E> {
|
||||
if tmp.is_zero() {
|
||||
Err(SynthesisError::DivisionByZero)
|
||||
} else {
|
||||
Ok(tmp.inverse().unwrap())
|
||||
Ok(tmp.invert().unwrap())
|
||||
}
|
||||
},
|
||||
)?;
|
||||
@ -416,6 +416,7 @@ mod test {
|
||||
use pairing::bls12_381::{Bls12, Fr};
|
||||
use rand_core::SeedableRng;
|
||||
use rand_xorshift::XorShiftRng;
|
||||
use std::ops::{Neg, SubAssign};
|
||||
|
||||
use super::{AllocatedNum, Boolean};
|
||||
use crate::gadgets::test::*;
|
||||
@ -517,8 +518,7 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_into_bits_strict() {
|
||||
let mut negone = Fr::one();
|
||||
negone.negate();
|
||||
let negone = Fr::one().neg();
|
||||
|
||||
let mut cs = TestConstraintSystem::<Bls12>::new();
|
||||
|
||||
|
@ -6,6 +6,7 @@ use crate::{ConstraintSystem, Index, LinearCombination, SynthesisError, Variable
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Write;
|
||||
use std::ops::{AddAssign, MulAssign, Neg};
|
||||
|
||||
use byteorder::{BigEndian, ByteOrder};
|
||||
use std::cmp::Ordering;
|
||||
@ -151,14 +152,10 @@ impl<E: ScalarEngine> TestConstraintSystem<E> {
|
||||
pub fn pretty_print(&self) -> String {
|
||||
let mut s = String::new();
|
||||
|
||||
let negone = {
|
||||
let mut tmp = E::Fr::one();
|
||||
tmp.negate();
|
||||
tmp
|
||||
};
|
||||
let negone = E::Fr::one().neg();
|
||||
|
||||
let powers_of_two = (0..E::Fr::NUM_BITS)
|
||||
.map(|i| E::Fr::from_str("2").unwrap().pow(&[u64::from(i)]))
|
||||
.map(|i| E::Fr::from_str("2").unwrap().pow_vartime(&[u64::from(i)]))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let pp = |s: &mut String, lc: &LinearCombination<E>| {
|
||||
|
@ -330,7 +330,7 @@ impl UInt32 {
|
||||
|
||||
all_constants &= bit.is_constant();
|
||||
|
||||
coeff.double();
|
||||
coeff = coeff.double();
|
||||
}
|
||||
}
|
||||
|
||||
@ -368,7 +368,7 @@ impl UInt32 {
|
||||
|
||||
max_value >>= 1;
|
||||
i += 1;
|
||||
coeff.double();
|
||||
coeff = coeff.double();
|
||||
}
|
||||
|
||||
// Enforce equality between the sum and result
|
||||
|
@ -1,5 +1,5 @@
|
||||
use rand_core::RngCore;
|
||||
|
||||
use std::ops::{AddAssign, MulAssign};
|
||||
use std::sync::Arc;
|
||||
|
||||
use ff::{Field, PrimeField};
|
||||
@ -215,8 +215,22 @@ where
|
||||
assembly.num_inputs + assembly.num_aux
|
||||
});
|
||||
|
||||
let gamma_inverse = gamma.inverse().ok_or(SynthesisError::UnexpectedIdentity)?;
|
||||
let delta_inverse = delta.inverse().ok_or(SynthesisError::UnexpectedIdentity)?;
|
||||
let gamma_inverse = {
|
||||
let inverse = gamma.invert();
|
||||
if bool::from(inverse.is_some()) {
|
||||
Ok(inverse.unwrap())
|
||||
} else {
|
||||
Err(SynthesisError::UnexpectedIdentity)
|
||||
}
|
||||
}?;
|
||||
let delta_inverse = {
|
||||
let inverse = delta.invert();
|
||||
if bool::from(inverse.is_some()) {
|
||||
Ok(inverse.unwrap())
|
||||
} else {
|
||||
Err(SynthesisError::UnexpectedIdentity)
|
||||
}
|
||||
}?;
|
||||
|
||||
let worker = Worker::new();
|
||||
|
||||
@ -228,7 +242,7 @@ where
|
||||
worker.scope(powers_of_tau.len(), |scope, chunk| {
|
||||
for (i, powers_of_tau) in powers_of_tau.chunks_mut(chunk).enumerate() {
|
||||
scope.spawn(move |_scope| {
|
||||
let mut current_tau_power = tau.pow(&[(i * chunk) as u64]);
|
||||
let mut current_tau_power = tau.pow_vartime(&[(i * chunk) as u64]);
|
||||
|
||||
for p in powers_of_tau {
|
||||
p.0 = current_tau_power;
|
||||
|
@ -474,6 +474,7 @@ mod test_with_bls12_381 {
|
||||
use ff::Field;
|
||||
use pairing::bls12_381::{Bls12, Fr};
|
||||
use rand::thread_rng;
|
||||
use std::ops::MulAssign;
|
||||
|
||||
#[test]
|
||||
fn serialization() {
|
||||
|
@ -1,5 +1,5 @@
|
||||
use rand_core::RngCore;
|
||||
|
||||
use std::ops::{AddAssign, MulAssign};
|
||||
use std::sync::Arc;
|
||||
|
||||
use futures::Future;
|
||||
@ -314,34 +314,34 @@ where
|
||||
}
|
||||
|
||||
let mut g_a = vk.delta_g1.mul(r);
|
||||
g_a.add_assign_mixed(&vk.alpha_g1);
|
||||
AddAssign::<&E::G1Affine>::add_assign(&mut g_a, &vk.alpha_g1);
|
||||
let mut g_b = vk.delta_g2.mul(s);
|
||||
g_b.add_assign_mixed(&vk.beta_g2);
|
||||
AddAssign::<&E::G2Affine>::add_assign(&mut g_b, &vk.beta_g2);
|
||||
let mut g_c;
|
||||
{
|
||||
let mut rs = r;
|
||||
rs.mul_assign(&s);
|
||||
|
||||
g_c = vk.delta_g1.mul(rs);
|
||||
g_c.add_assign(&vk.alpha_g1.mul(s));
|
||||
g_c.add_assign(&vk.beta_g1.mul(r));
|
||||
AddAssign::<&E::G1>::add_assign(&mut g_c, &vk.alpha_g1.mul(s));
|
||||
AddAssign::<&E::G1>::add_assign(&mut g_c, &vk.beta_g1.mul(r));
|
||||
}
|
||||
let mut a_answer = a_inputs.wait()?;
|
||||
a_answer.add_assign(&a_aux.wait()?);
|
||||
g_a.add_assign(&a_answer);
|
||||
AddAssign::<&E::G1>::add_assign(&mut a_answer, &a_aux.wait()?);
|
||||
AddAssign::<&E::G1>::add_assign(&mut g_a, &a_answer);
|
||||
a_answer.mul_assign(s);
|
||||
g_c.add_assign(&a_answer);
|
||||
AddAssign::<&E::G1>::add_assign(&mut g_c, &a_answer);
|
||||
|
||||
let mut b1_answer = b_g1_inputs.wait()?;
|
||||
b1_answer.add_assign(&b_g1_aux.wait()?);
|
||||
let mut b1_answer: E::G1 = b_g1_inputs.wait()?;
|
||||
AddAssign::<&E::G1>::add_assign(&mut b1_answer, &b_g1_aux.wait()?);
|
||||
let mut b2_answer = b_g2_inputs.wait()?;
|
||||
b2_answer.add_assign(&b_g2_aux.wait()?);
|
||||
AddAssign::<&E::G2>::add_assign(&mut b2_answer, &b_g2_aux.wait()?);
|
||||
|
||||
g_b.add_assign(&b2_answer);
|
||||
AddAssign::<&E::G2>::add_assign(&mut g_b, &b2_answer);
|
||||
b1_answer.mul_assign(r);
|
||||
g_c.add_assign(&b1_answer);
|
||||
g_c.add_assign(&h.wait()?);
|
||||
g_c.add_assign(&l.wait()?);
|
||||
AddAssign::<&E::G1>::add_assign(&mut g_c, &b1_answer);
|
||||
AddAssign::<&E::G1>::add_assign(&mut g_c, &h.wait()?);
|
||||
AddAssign::<&E::G1>::add_assign(&mut g_c, &l.wait()?);
|
||||
|
||||
Ok(Proof {
|
||||
a: g_a.into_affine(),
|
||||
|
@ -1,7 +1,4 @@
|
||||
use ff::{
|
||||
Field, LegendreSymbol, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr, ScalarEngine,
|
||||
SqrtField,
|
||||
};
|
||||
use ff::{Field, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr, ScalarEngine, SqrtField};
|
||||
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
|
||||
use pairing::{Engine, PairingCurveAffine};
|
||||
|
||||
@ -9,18 +6,143 @@ use rand_core::RngCore;
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt;
|
||||
use std::num::Wrapping;
|
||||
use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
|
||||
|
||||
const MODULUS_R: Wrapping<u32> = Wrapping(64513);
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Fr(Wrapping<u32>);
|
||||
|
||||
impl Default for Fr {
|
||||
fn default() -> Self {
|
||||
<Fr as Field>::zero()
|
||||
}
|
||||
}
|
||||
|
||||
impl ConstantTimeEq for Fr {
|
||||
fn ct_eq(&self, other: &Fr) -> Choice {
|
||||
(self.0).0.ct_eq(&(other.0).0)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Fr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
write!(f, "{}", (self.0).0)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConditionallySelectable for Fr {
|
||||
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
|
||||
Fr(Wrapping(u32::conditional_select(
|
||||
&(a.0).0,
|
||||
&(b.0).0,
|
||||
choice,
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for Fr {
|
||||
type Output = Self;
|
||||
|
||||
fn neg(mut self) -> Self {
|
||||
if !<Fr as Field>::is_zero(&self) {
|
||||
self.0 = MODULUS_R - self.0;
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> Add<&'r Fr> for Fr {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, other: &Self) -> Self {
|
||||
let mut ret = self;
|
||||
AddAssign::add_assign(&mut ret, other);
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Fr {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, other: Self) -> Self {
|
||||
self + &other
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> AddAssign<&'r Fr> for Fr {
|
||||
fn add_assign(&mut self, other: &Self) {
|
||||
self.0 = (self.0 + other.0) % MODULUS_R;
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign for Fr {
|
||||
fn add_assign(&mut self, other: Self) {
|
||||
AddAssign::add_assign(self, &other);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> Sub<&'r Fr> for Fr {
|
||||
type Output = Self;
|
||||
|
||||
fn sub(self, other: &Self) -> Self {
|
||||
let mut ret = self;
|
||||
SubAssign::sub_assign(&mut ret, other);
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for Fr {
|
||||
type Output = Self;
|
||||
|
||||
fn sub(self, other: Self) -> Self {
|
||||
self - &other
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> SubAssign<&'r Fr> for Fr {
|
||||
fn sub_assign(&mut self, other: &Self) {
|
||||
self.0 = ((MODULUS_R + self.0) - other.0) % MODULUS_R;
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign for Fr {
|
||||
fn sub_assign(&mut self, other: Self) {
|
||||
SubAssign::sub_assign(self, &other);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> Mul<&'r Fr> for Fr {
|
||||
type Output = Self;
|
||||
|
||||
fn mul(self, other: &Self) -> Self {
|
||||
let mut ret = self;
|
||||
MulAssign::mul_assign(&mut ret, other);
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul for Fr {
|
||||
type Output = Self;
|
||||
|
||||
fn mul(self, other: Self) -> Self {
|
||||
self * &other
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> MulAssign<&'r Fr> for Fr {
|
||||
fn mul_assign(&mut self, other: &Self) {
|
||||
self.0 = (self.0 * other.0) % MODULUS_R;
|
||||
}
|
||||
}
|
||||
|
||||
impl MulAssign for Fr {
|
||||
fn mul_assign(&mut self, other: Self) {
|
||||
MulAssign::mul_assign(self, &other);
|
||||
}
|
||||
}
|
||||
|
||||
impl Field for Fr {
|
||||
fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
|
||||
Fr(Wrapping(rng.next_u32()) % MODULUS_R)
|
||||
@ -38,37 +160,22 @@ impl Field for Fr {
|
||||
(self.0).0 == 0
|
||||
}
|
||||
|
||||
fn square(&mut self) {
|
||||
self.0 = (self.0 * self.0) % MODULUS_R;
|
||||
fn square(&self) -> Self {
|
||||
Fr((self.0 * self.0) % MODULUS_R)
|
||||
}
|
||||
|
||||
fn double(&mut self) {
|
||||
self.0 = (self.0 << 1) % MODULUS_R;
|
||||
fn double(&self) -> Self {
|
||||
Fr((self.0 << 1) % MODULUS_R)
|
||||
}
|
||||
|
||||
fn negate(&mut self) {
|
||||
if !<Fr as Field>::is_zero(self) {
|
||||
self.0 = MODULUS_R - self.0;
|
||||
}
|
||||
}
|
||||
|
||||
fn add_assign(&mut self, other: &Self) {
|
||||
self.0 = (self.0 + other.0) % MODULUS_R;
|
||||
}
|
||||
|
||||
fn sub_assign(&mut self, other: &Self) {
|
||||
self.0 = ((MODULUS_R + self.0) - other.0) % MODULUS_R;
|
||||
}
|
||||
|
||||
fn mul_assign(&mut self, other: &Self) {
|
||||
self.0 = (self.0 * other.0) % MODULUS_R;
|
||||
}
|
||||
|
||||
fn inverse(&self) -> Option<Self> {
|
||||
fn invert(&self) -> CtOption<Self> {
|
||||
if <Fr as Field>::is_zero(self) {
|
||||
None
|
||||
CtOption::new(<Fr as Field>::zero(), Choice::from(0))
|
||||
} else {
|
||||
Some(self.pow(&[(MODULUS_R.0 as u64) - 2]))
|
||||
CtOption::new(
|
||||
self.pow_vartime(&[(MODULUS_R.0 as u64) - 2]),
|
||||
Choice::from(1),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,58 +185,39 @@ impl Field for Fr {
|
||||
}
|
||||
|
||||
impl SqrtField for Fr {
|
||||
fn legendre(&self) -> LegendreSymbol {
|
||||
// s = self^((r - 1) // 2)
|
||||
let s = self.pow([32256]);
|
||||
if s == <Fr as Field>::zero() {
|
||||
LegendreSymbol::Zero
|
||||
} else if s == <Fr as Field>::one() {
|
||||
LegendreSymbol::QuadraticResidue
|
||||
} else {
|
||||
LegendreSymbol::QuadraticNonResidue
|
||||
}
|
||||
}
|
||||
|
||||
fn sqrt(&self) -> Option<Self> {
|
||||
fn sqrt(&self) -> CtOption<Self> {
|
||||
// Tonelli-Shank's algorithm for q mod 16 = 1
|
||||
// https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5)
|
||||
match self.legendre() {
|
||||
LegendreSymbol::Zero => Some(*self),
|
||||
LegendreSymbol::QuadraticNonResidue => None,
|
||||
LegendreSymbol::QuadraticResidue => {
|
||||
let mut c = Fr::root_of_unity();
|
||||
// r = self^((t + 1) // 2)
|
||||
let mut r = self.pow([32]);
|
||||
// t = self^t
|
||||
let mut t = self.pow([63]);
|
||||
let mut m = Fr::S;
|
||||
let mut c = Fr::root_of_unity();
|
||||
// r = self^((t + 1) // 2)
|
||||
let mut r = self.pow_vartime([32]);
|
||||
// t = self^t
|
||||
let mut t = self.pow_vartime([63]);
|
||||
let mut m = Fr::S;
|
||||
|
||||
while t != <Fr as Field>::one() {
|
||||
let mut i = 1;
|
||||
{
|
||||
let mut t2i = t;
|
||||
t2i.square();
|
||||
loop {
|
||||
if t2i == <Fr as Field>::one() {
|
||||
break;
|
||||
}
|
||||
t2i.square();
|
||||
i += 1;
|
||||
}
|
||||
while t != <Fr as Field>::one() {
|
||||
let mut i = 1;
|
||||
{
|
||||
let mut t2i = t.square();
|
||||
loop {
|
||||
if t2i == <Fr as Field>::one() {
|
||||
break;
|
||||
}
|
||||
|
||||
for _ in 0..(m - i - 1) {
|
||||
c.square();
|
||||
}
|
||||
<Fr as Field>::mul_assign(&mut r, &c);
|
||||
c.square();
|
||||
<Fr as Field>::mul_assign(&mut t, &c);
|
||||
m = i;
|
||||
t2i = t2i.square();
|
||||
i += 1;
|
||||
}
|
||||
|
||||
Some(r)
|
||||
}
|
||||
|
||||
for _ in 0..(m - i - 1) {
|
||||
c = c.square();
|
||||
}
|
||||
MulAssign::mul_assign(&mut r, &c);
|
||||
c = c.square();
|
||||
MulAssign::mul_assign(&mut t, &c);
|
||||
m = i;
|
||||
}
|
||||
|
||||
CtOption::new(r, (r * r).ct_eq(self))
|
||||
}
|
||||
}
|
||||
|
||||
@ -226,7 +314,7 @@ impl PrimeField for Fr {
|
||||
|
||||
fn from_repr(repr: FrRepr) -> Result<Self, PrimeFieldDecodingError> {
|
||||
if repr.0[0] >= (MODULUS_R.0 as u64) {
|
||||
Err(PrimeFieldDecodingError::NotInField(format!("{}", repr)))
|
||||
Err(PrimeFieldDecodingError::NotInField)
|
||||
} else {
|
||||
Ok(Fr(Wrapping(repr.0[0] as u32)))
|
||||
}
|
||||
@ -280,16 +368,16 @@ impl Engine for DummyEngine {
|
||||
|
||||
for &(a, b) in i {
|
||||
let mut tmp = *a;
|
||||
<Fr as Field>::mul_assign(&mut tmp, b);
|
||||
<Fr as Field>::add_assign(&mut acc, &tmp);
|
||||
MulAssign::mul_assign(&mut tmp, b);
|
||||
AddAssign::add_assign(&mut acc, &tmp);
|
||||
}
|
||||
|
||||
acc
|
||||
}
|
||||
|
||||
/// Perform final exponentiation of the result of a miller loop.
|
||||
fn final_exponentiation(this: &Self::Fqk) -> Option<Self::Fqk> {
|
||||
Some(*this)
|
||||
fn final_exponentiation(this: &Self::Fqk) -> CtOption<Self::Fqk> {
|
||||
CtOption::new(*this, Choice::from(1))
|
||||
}
|
||||
}
|
||||
|
||||
@ -322,25 +410,13 @@ impl CurveProjective for Fr {
|
||||
}
|
||||
|
||||
fn double(&mut self) {
|
||||
<Fr as Field>::double(self);
|
||||
}
|
||||
|
||||
fn add_assign(&mut self, other: &Self) {
|
||||
<Fr as Field>::add_assign(self, other);
|
||||
}
|
||||
|
||||
fn add_assign_mixed(&mut self, other: &Self) {
|
||||
<Fr as Field>::add_assign(self, other);
|
||||
}
|
||||
|
||||
fn negate(&mut self) {
|
||||
<Fr as Field>::negate(self);
|
||||
self.0 = <Fr as Field>::double(self).0;
|
||||
}
|
||||
|
||||
fn mul_assign<S: Into<<Self::Scalar as PrimeField>::Repr>>(&mut self, other: S) {
|
||||
let tmp = Fr::from_repr(other.into()).unwrap();
|
||||
|
||||
<Fr as Field>::mul_assign(self, &tmp);
|
||||
MulAssign::mul_assign(self, &tmp);
|
||||
}
|
||||
|
||||
fn into_affine(&self) -> Fr {
|
||||
@ -415,15 +491,11 @@ impl CurveAffine for Fr {
|
||||
<Fr as Field>::is_zero(self)
|
||||
}
|
||||
|
||||
fn negate(&mut self) {
|
||||
<Fr as Field>::negate(self);
|
||||
}
|
||||
|
||||
fn mul<S: Into<<Self::Scalar as PrimeField>::Repr>>(&self, other: S) -> Self::Projective {
|
||||
let mut res = *self;
|
||||
let tmp = Fr::from_repr(other.into()).unwrap();
|
||||
|
||||
<Fr as Field>::mul_assign(&mut res, &tmp);
|
||||
MulAssign::mul_assign(&mut res, &tmp);
|
||||
|
||||
res
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ mod dummy_engine;
|
||||
use self::dummy_engine::*;
|
||||
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::{AddAssign, MulAssign, SubAssign};
|
||||
|
||||
use crate::{Circuit, ConstraintSystem, SynthesisError};
|
||||
|
||||
@ -126,22 +127,22 @@ fn test_xordemo() {
|
||||
let mut root_of_unity = Fr::root_of_unity();
|
||||
|
||||
// We expect this to be a 2^10 root of unity
|
||||
assert_eq!(Fr::one(), root_of_unity.pow(&[1 << 10]));
|
||||
assert_eq!(Fr::one(), root_of_unity.pow_vartime(&[1 << 10]));
|
||||
|
||||
// Let's turn it into a 2^3 root of unity.
|
||||
root_of_unity = root_of_unity.pow(&[1 << 7]);
|
||||
assert_eq!(Fr::one(), root_of_unity.pow(&[1 << 3]));
|
||||
root_of_unity = root_of_unity.pow_vartime(&[1 << 7]);
|
||||
assert_eq!(Fr::one(), root_of_unity.pow_vartime(&[1 << 3]));
|
||||
assert_eq!(Fr::from_str("20201").unwrap(), root_of_unity);
|
||||
|
||||
// Let's compute all the points in our evaluation domain.
|
||||
let mut points = Vec::with_capacity(8);
|
||||
for i in 0..8 {
|
||||
points.push(root_of_unity.pow(&[i]));
|
||||
points.push(root_of_unity.pow_vartime(&[i]));
|
||||
}
|
||||
|
||||
// Let's compute t(tau) = (tau - p_0)(tau - p_1)...
|
||||
// = tau^8 - 1
|
||||
let mut t_at_tau = tau.pow(&[8]);
|
||||
let mut t_at_tau = tau.pow_vartime(&[8]);
|
||||
t_at_tau.sub_assign(&Fr::one());
|
||||
{
|
||||
let mut tmp = Fr::one();
|
||||
@ -155,8 +156,8 @@ fn test_xordemo() {
|
||||
|
||||
// We expect our H query to be 7 elements of the form...
|
||||
// {tau^i t(tau) / delta}
|
||||
let delta_inverse = delta.inverse().unwrap();
|
||||
let gamma_inverse = gamma.inverse().unwrap();
|
||||
let delta_inverse = delta.invert().unwrap();
|
||||
let gamma_inverse = gamma.invert().unwrap();
|
||||
{
|
||||
let mut coeff = delta_inverse;
|
||||
coeff.mul_assign(&t_at_tau);
|
||||
|
@ -1,16 +1,15 @@
|
||||
use ff::PrimeField;
|
||||
use group::{CurveAffine, CurveProjective};
|
||||
use pairing::{Engine, PairingCurveAffine};
|
||||
use std::ops::{AddAssign, Neg};
|
||||
|
||||
use super::{PreparedVerifyingKey, Proof, VerifyingKey};
|
||||
|
||||
use crate::SynthesisError;
|
||||
|
||||
pub fn prepare_verifying_key<E: Engine>(vk: &VerifyingKey<E>) -> PreparedVerifyingKey<E> {
|
||||
let mut gamma = vk.gamma_g2;
|
||||
gamma.negate();
|
||||
let mut delta = vk.delta_g2;
|
||||
delta.negate();
|
||||
let gamma = vk.gamma_g2.neg();
|
||||
let delta = vk.delta_g2.neg();
|
||||
|
||||
PreparedVerifyingKey {
|
||||
alpha_g1_beta_g2: E::pairing(vk.alpha_g1, vk.beta_g2),
|
||||
@ -32,7 +31,7 @@ pub fn verify_proof<'a, E: Engine>(
|
||||
let mut acc = pvk.ic[0].into_projective();
|
||||
|
||||
for (i, b) in public_inputs.iter().zip(pvk.ic.iter().skip(1)) {
|
||||
acc.add_assign(&b.mul(i.into_repr()));
|
||||
AddAssign::<&E::G1>::add_assign(&mut acc, &b.mul(i.into_repr()));
|
||||
}
|
||||
|
||||
// The original verification equation is:
|
||||
|
@ -148,7 +148,7 @@ use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::{Add, Sub};
|
||||
use std::ops::{Add, MulAssign, Neg, Sub};
|
||||
|
||||
/// Computations are expressed in terms of arithmetic circuits, in particular
|
||||
/// rank-1 quadratic constraint systems. The `Circuit` trait represents a
|
||||
@ -216,10 +216,8 @@ impl<E: ScalarEngine> Sub<(E::Fr, Variable)> for LinearCombination<E> {
|
||||
type Output = LinearCombination<E>;
|
||||
|
||||
#[allow(clippy::suspicious_arithmetic_impl)]
|
||||
fn sub(self, (mut coeff, var): (E::Fr, Variable)) -> LinearCombination<E> {
|
||||
coeff.negate();
|
||||
|
||||
self + (coeff, var)
|
||||
fn sub(self, (coeff, var): (E::Fr, Variable)) -> LinearCombination<E> {
|
||||
self + (coeff.neg(), var)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@ use futures::Future;
|
||||
use group::{CurveAffine, CurveProjective};
|
||||
use std::io;
|
||||
use std::iter;
|
||||
use std::ops::AddAssign;
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::SynthesisError;
|
||||
@ -18,16 +19,24 @@ pub trait SourceBuilder<G: CurveAffine>: Send + Sync + 'static + Clone {
|
||||
|
||||
/// A source of bases, like an iterator.
|
||||
pub trait Source<G: CurveAffine> {
|
||||
/// Parses the element from the source. Fails if the point is at infinity.
|
||||
fn add_assign_mixed(
|
||||
&mut self,
|
||||
to: &mut <G as CurveAffine>::Projective,
|
||||
) -> Result<(), SynthesisError>;
|
||||
fn next(&mut self) -> Result<&G, SynthesisError>;
|
||||
|
||||
/// Skips `amt` elements from the source, avoiding deserialization.
|
||||
fn skip(&mut self, amt: usize) -> Result<(), SynthesisError>;
|
||||
}
|
||||
|
||||
pub trait AddAssignFromSource: CurveProjective {
|
||||
/// Parses the element from the source. Fails if the point is at infinity.
|
||||
fn add_assign_from_source<S: Source<<Self as CurveProjective>::Affine>>(
|
||||
&mut self,
|
||||
source: &mut S,
|
||||
) -> Result<(), SynthesisError> {
|
||||
AddAssign::<&<Self as CurveProjective>::Affine>::add_assign(self, source.next()?);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl<G> AddAssignFromSource for G where G: CurveProjective {}
|
||||
|
||||
impl<G: CurveAffine> SourceBuilder<G> for (Arc<Vec<G>>, usize) {
|
||||
type Source = (Arc<Vec<G>>, usize);
|
||||
|
||||
@ -37,10 +46,7 @@ impl<G: CurveAffine> SourceBuilder<G> for (Arc<Vec<G>>, usize) {
|
||||
}
|
||||
|
||||
impl<G: CurveAffine> Source<G> for (Arc<Vec<G>>, usize) {
|
||||
fn add_assign_mixed(
|
||||
&mut self,
|
||||
to: &mut <G as CurveAffine>::Projective,
|
||||
) -> Result<(), SynthesisError> {
|
||||
fn next(&mut self) -> Result<&G, SynthesisError> {
|
||||
if self.0.len() <= self.1 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::UnexpectedEof,
|
||||
@ -53,11 +59,10 @@ impl<G: CurveAffine> Source<G> for (Arc<Vec<G>>, usize) {
|
||||
return Err(SynthesisError::UnexpectedIdentity);
|
||||
}
|
||||
|
||||
to.add_assign_mixed(&self.0[self.1]);
|
||||
|
||||
let ret = &self.0[self.1];
|
||||
self.1 += 1;
|
||||
|
||||
Ok(())
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
fn skip(&mut self, amt: usize) -> Result<(), SynthesisError> {
|
||||
@ -153,12 +158,12 @@ fn multiexp_inner<Q, D, G, S>(
|
||||
mut skip: u32,
|
||||
c: u32,
|
||||
handle_trivial: bool,
|
||||
) -> Box<dyn Future<Item = <G as CurveAffine>::Projective, Error = SynthesisError>>
|
||||
) -> Box<dyn Future<Item = G, Error = SynthesisError>>
|
||||
where
|
||||
for<'a> &'a Q: QueryDensity,
|
||||
D: Send + Sync + 'static + Clone + AsRef<Q>,
|
||||
G: CurveAffine,
|
||||
S: SourceBuilder<G>,
|
||||
G: CurveProjective,
|
||||
S: SourceBuilder<<G as CurveProjective>::Affine>,
|
||||
{
|
||||
// Perform this region of the multiexp
|
||||
let this = {
|
||||
@ -168,13 +173,13 @@ where
|
||||
|
||||
pool.compute(move || {
|
||||
// Accumulate the result
|
||||
let mut acc = G::Projective::zero();
|
||||
let mut acc = G::zero();
|
||||
|
||||
// Build a source for the bases
|
||||
let mut bases = bases.new();
|
||||
|
||||
// Create space for the buckets
|
||||
let mut buckets = vec![<G as CurveAffine>::Projective::zero(); (1 << c) - 1];
|
||||
let mut buckets = vec![G::zero(); (1 << c) - 1];
|
||||
|
||||
let zero = <G::Engine as ScalarEngine>::Fr::zero().into_repr();
|
||||
let one = <G::Engine as ScalarEngine>::Fr::one().into_repr();
|
||||
@ -186,7 +191,7 @@ where
|
||||
bases.skip(1)?;
|
||||
} else if exp == one {
|
||||
if handle_trivial {
|
||||
bases.add_assign_mixed(&mut acc)?;
|
||||
acc.add_assign_from_source(&mut bases)?;
|
||||
} else {
|
||||
bases.skip(1)?;
|
||||
}
|
||||
@ -196,7 +201,8 @@ where
|
||||
let exp = exp.as_ref()[0] % (1 << c);
|
||||
|
||||
if exp != 0 {
|
||||
bases.add_assign_mixed(&mut buckets[(exp - 1) as usize])?;
|
||||
(&mut buckets[(exp - 1) as usize])
|
||||
.add_assign_from_source(&mut bases)?;
|
||||
} else {
|
||||
bases.skip(1)?;
|
||||
}
|
||||
@ -208,7 +214,7 @@ where
|
||||
// e.g. 3a + 2b + 1c = a +
|
||||
// (a) + b +
|
||||
// ((a) + b) + c
|
||||
let mut running_sum = G::Projective::zero();
|
||||
let mut running_sum = G::zero();
|
||||
for exp in buckets.into_iter().rev() {
|
||||
running_sum.add_assign(&exp);
|
||||
acc.add_assign(&running_sum);
|
||||
@ -236,7 +242,7 @@ where
|
||||
c,
|
||||
false,
|
||||
))
|
||||
.map(move |(this, mut higher)| {
|
||||
.map(move |(this, mut higher): (_, G)| {
|
||||
for _ in 0..c {
|
||||
higher.double();
|
||||
}
|
||||
@ -256,12 +262,12 @@ pub fn multiexp<Q, D, G, S>(
|
||||
bases: S,
|
||||
density_map: D,
|
||||
exponents: Arc<Vec<<<G::Engine as ScalarEngine>::Fr as PrimeField>::Repr>>,
|
||||
) -> Box<dyn Future<Item = <G as CurveAffine>::Projective, Error = SynthesisError>>
|
||||
) -> Box<dyn Future<Item = G, Error = SynthesisError>>
|
||||
where
|
||||
for<'a> &'a Q: QueryDensity,
|
||||
D: Send + Sync + 'static + Clone + AsRef<Q>,
|
||||
G: CurveAffine,
|
||||
S: SourceBuilder<G>,
|
||||
G: CurveProjective,
|
||||
S: SourceBuilder<<G as CurveProjective>::Affine>,
|
||||
{
|
||||
let c = if exponents.len() < 32 {
|
||||
3u32
|
||||
@ -282,16 +288,16 @@ where
|
||||
#[cfg(feature = "pairing")]
|
||||
#[test]
|
||||
fn test_with_bls12() {
|
||||
fn naive_multiexp<G: CurveAffine>(
|
||||
bases: Arc<Vec<G>>,
|
||||
fn naive_multiexp<G: CurveProjective>(
|
||||
bases: Arc<Vec<<G as CurveProjective>::Affine>>,
|
||||
exponents: Arc<Vec<<G::Scalar as PrimeField>::Repr>>,
|
||||
) -> G::Projective {
|
||||
) -> G {
|
||||
assert_eq!(bases.len(), exponents.len());
|
||||
|
||||
let mut acc = G::Projective::zero();
|
||||
let mut acc = G::zero();
|
||||
|
||||
for (base, exp) in bases.iter().zip(exponents.iter()) {
|
||||
acc.add_assign(&base.mul(*exp));
|
||||
AddAssign::<&G>::add_assign(&mut acc, &base.mul(*exp));
|
||||
}
|
||||
|
||||
acc
|
||||
@ -314,7 +320,7 @@ fn test_with_bls12() {
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
|
||||
let naive = naive_multiexp(g.clone(), v.clone());
|
||||
let naive: <Bls12 as Engine>::G1 = naive_multiexp(g.clone(), v.clone());
|
||||
|
||||
let pool = Worker::new();
|
||||
|
||||
|
@ -7,6 +7,7 @@ use std::time::{Duration, Instant};
|
||||
// Bring in some tools for using pairing-friendly curves
|
||||
use ff::{Field, ScalarEngine};
|
||||
use pairing::Engine;
|
||||
use std::ops::{AddAssign, MulAssign};
|
||||
|
||||
// We're going to use the BLS12-381 pairing-friendly elliptic curve.
|
||||
use pairing::bls12_381::Bls12;
|
||||
@ -40,8 +41,7 @@ fn mimc<E: Engine>(mut xl: E::Fr, mut xr: E::Fr, constants: &[E::Fr]) -> E::Fr {
|
||||
for i in 0..MIMC_ROUNDS {
|
||||
let mut tmp1 = xl;
|
||||
tmp1.add_assign(&constants[i]);
|
||||
let mut tmp2 = tmp1;
|
||||
tmp2.square();
|
||||
let mut tmp2 = tmp1.square();
|
||||
tmp2.mul_assign(&tmp1);
|
||||
tmp2.add_assign(&xr);
|
||||
xr = xl;
|
||||
@ -87,8 +87,7 @@ impl<'a, E: Engine> Circuit<E> for MiMCDemo<'a, E> {
|
||||
// tmp = (xL + Ci)^2
|
||||
let tmp_value = xl_value.map(|mut e| {
|
||||
e.add_assign(&self.constants[i]);
|
||||
e.square();
|
||||
e
|
||||
e.square()
|
||||
});
|
||||
let tmp = cs.alloc(
|
||||
|| "tmp",
|
||||
|
95
bls12_381/.github/workflows/ci.yml
vendored
Normal file
95
bls12_381/.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,95 @@
|
||||
name: CI checks
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.36.0
|
||||
override: true
|
||||
|
||||
# Ensure all code has been formatted with rustfmt
|
||||
- run: rustup component add rustfmt
|
||||
- name: Check formatting
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: -- --check --color always
|
||||
|
||||
test:
|
||||
name: Test on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macOS-latest]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.36.0
|
||||
override: true
|
||||
- name: cargo fetch
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fetch
|
||||
- name: Build tests
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --verbose --release --tests
|
||||
- name: Run tests
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --verbose --release
|
||||
|
||||
no-std:
|
||||
name: Check no-std compatibility
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.36.0
|
||||
override: true
|
||||
- run: rustup target add thumbv6m-none-eabi
|
||||
- name: cargo fetch
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fetch
|
||||
- name: Build
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --verbose --target thumbv6m-none-eabi --no-default-features --features groups,pairings
|
||||
|
||||
doc-links:
|
||||
name: Nightly lint
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
override: true
|
||||
- name: cargo fetch
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fetch
|
||||
|
||||
# Ensure intra-documentation links all resolve correctly
|
||||
# Requires #![deny(intra_doc_link_resolution_failure)] in crate.
|
||||
- name: Check intra-doc links
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: doc
|
||||
args: --document-private-items
|
3
bls12_381/.gitignore
vendored
Normal file
3
bls12_381/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
/target
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
14
bls12_381/COPYRIGHT
Normal file
14
bls12_381/COPYRIGHT
Normal file
@ -0,0 +1,14 @@
|
||||
Copyrights in the "bls12_381" library are retained by their contributors. No
|
||||
copyright assignment is required to contribute to the "bls12_381" library.
|
||||
|
||||
The "bls12_381" library is licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, (see ./LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license (see ./LICENSE-MIT or http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally
|
||||
submitted for inclusion in the work by you, as defined in the Apache-2.0
|
||||
license, shall be dual licensed as above, without any additional terms or
|
||||
conditions.
|
32
bls12_381/Cargo.toml
Normal file
32
bls12_381/Cargo.toml
Normal file
@ -0,0 +1,32 @@
|
||||
[package]
|
||||
authors = ["Sean Bowe <ewillbefull@gmail.com>"]
|
||||
description = "Implementation of the BLS12-381 pairing-friendly elliptic curve construction"
|
||||
documentation = "https://docs.rs/bls12_381/"
|
||||
homepage = "https://github.com/zkcrypto/bls12_381"
|
||||
license = "MIT/Apache-2.0"
|
||||
name = "bls12_381"
|
||||
repository = "https://github.com/zkcrypto/bls12_381"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
rustdoc-args = [ "--html-in-header", "katex-header.html" ]
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.3"
|
||||
|
||||
[[bench]]
|
||||
name = "groups"
|
||||
harness = false
|
||||
required-features = ["groups"]
|
||||
|
||||
[dependencies.subtle]
|
||||
version = "2.2.1"
|
||||
default-features = false
|
||||
|
||||
[features]
|
||||
default = ["groups", "pairings", "alloc"]
|
||||
groups = []
|
||||
pairings = ["groups"]
|
||||
alloc = []
|
||||
nightly = ["subtle/nightly"]
|
201
bls12_381/LICENSE-APACHE
Normal file
201
bls12_381/LICENSE-APACHE
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
23
bls12_381/LICENSE-MIT
Normal file
23
bls12_381/LICENSE-MIT
Normal file
@ -0,0 +1,23 @@
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
63
bls12_381/README.md
Normal file
63
bls12_381/README.md
Normal file
@ -0,0 +1,63 @@
|
||||
# bls12_381 [![Crates.io](https://img.shields.io/crates/v/bls12_381.svg)](https://crates.io/crates/bls12_381) #
|
||||
|
||||
This crate provides an implementation of the BLS12-381 pairing-friendly elliptic curve construction.
|
||||
|
||||
* **This implementation has not been reviewed or audited. Use at your own risk.**
|
||||
* This implementation targets Rust `1.36` or later.
|
||||
* This implementation does not require the Rust standard library.
|
||||
* All operations are constant time unless explicitly noted.
|
||||
|
||||
## Features
|
||||
|
||||
* `groups` (on by default): Enables APIs for performing group arithmetic with G1, G2, and GT.
|
||||
* `pairings` (on by default): Enables some APIs for performing pairings.
|
||||
* `alloc` (on by default): Enables APIs that require an allocator; these include pairing optimizations.
|
||||
* `nightly`: Enables `subtle/nightly` which tries to prevent compiler optimizations that could jeopardize constant time operations. Requires the nightly Rust compiler.
|
||||
|
||||
## [Documentation](https://docs.rs/bls12_381)
|
||||
|
||||
## Curve Description
|
||||
|
||||
BLS12-381 is a pairing-friendly elliptic curve construction from the [BLS family](https://eprint.iacr.org/2002/088), with embedding degree 12. It is built over a 381-bit prime field `GF(p)` with...
|
||||
|
||||
* z = `-0xd201000000010000`
|
||||
* p = (z - 1)<sup>2</sup>(z<sup>4</sup> - z<sup>2</sup> + 1) / 3 + z
|
||||
* = `0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab`
|
||||
* q = z<sup>4</sup> - z<sup>2</sup> + 1
|
||||
* = `0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001`
|
||||
|
||||
... yielding two **source groups** G<sub>1</sub> and G<sub>2</sub>, each of 255-bit prime order `q`, such that an efficiently computable non-degenerate bilinear pairing function `e` exists into a third **target group** G<sub>T</sub>. Specifically, G<sub>1</sub> is the `q`-order subgroup of E(F<sub>p</sub>) : y<sup>2</sup> = x<sup>3</sup> + 4 and G<sub>2</sub> is the `q`-order subgroup of E'(F<sub>p<sup>2</sup></sub>) : y<sup>2</sup> = x<sup>3</sup> + 4(u + 1) where the extention field F<sub>p<sup>2</sup></sub> is defined as F<sub>p</sub>(u) / (u<sup>2</sup> + 1).
|
||||
|
||||
BLS12-381 is chosen so that `z` has small Hamming weight (to improve pairing performance) and also so that `GF(q)` has a large 2<sup>32</sup> primitive root of unity for performing radix-2 fast Fourier transforms for efficient multi-point evaluation and interpolation. It is also chosen so that it exists in a particularly efficient and rigid subfamily of BLS12 curves.
|
||||
|
||||
### Curve Security
|
||||
|
||||
Pairing-friendly elliptic curve constructions are (necessarily) less secure than conventional elliptic curves due to their small "embedding degree". Given a small enough embedding degree, the pairing function itself would allow for a break in DLP hardness if it projected into a weak target group, as weaknesses in this target group are immediately translated into weaknesses in the source group.
|
||||
|
||||
In order to achieve reasonable security without an unreasonably expensive pairing function, a careful choice of embedding degree, base field characteristic and prime subgroup order must be made. BLS12-381 uses an embedding degree of 12 to ensure fast pairing performance but a choice of a 381-bit base field characteristic to yeild a 255-bit subgroup order (for protection against [Pollard's rho algorithm](https://en.wikipedia.org/wiki/Pollard%27s_rho_algorithm)) while reaching close to a 128-bit security level.
|
||||
|
||||
There are [known optimizations](https://ellipticnews.wordpress.com/2016/05/02/kim-barbulescu-variant-of-the-number-field-sieve-to-compute-discrete-logarithms-in-finite-fields/) of the [Number Field Sieve algorithm](https://en.wikipedia.org/wiki/General_number_field_sieve) which could be used to weaken DLP security in the target group by taking advantage of its structure, as it is a multiplicative subgroup of a low-degree extension field. However, these attacks require an (as of yet unknown) efficient algorithm for scanning a large space of polynomials. Even if the attack were practical it would only reduce security to roughly 117 to 120 bits. (This contrasts with 254-bit BN curves which usually have less than 100 bits of security in the same situation.)
|
||||
|
||||
### Alternative Curves
|
||||
|
||||
Applications may wish to exchange pairing performance and/or G<sub>2</sub> performance by using BLS24 or KSS16 curves which conservatively target 128-bit security. In applications that need cycles of elliptic curves for e.g. arbitrary proof composition, MNT6/MNT4 curve cycles are known that target the 128-bit security level. In applications that only need fixed-depth proof composition, curves of this form have been constructed as part of Zexe.
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
Please see `Cargo.toml` for a list of primary authors of this codebase.
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally
|
||||
submitted for inclusion in the work by you, as defined in the Apache-2.0
|
||||
license, shall be dual licensed as above, without any additional terms or
|
||||
conditions.
|
3
bls12_381/RELEASES.md
Normal file
3
bls12_381/RELEASES.md
Normal file
@ -0,0 +1,3 @@
|
||||
# 0.1.0
|
||||
|
||||
Initial release.
|
170
bls12_381/benches/groups.rs
Normal file
170
bls12_381/benches/groups.rs
Normal file
@ -0,0 +1,170 @@
|
||||
#[macro_use]
|
||||
extern crate criterion;
|
||||
|
||||
extern crate bls12_381;
|
||||
use bls12_381::*;
|
||||
|
||||
use criterion::{black_box, Criterion};
|
||||
|
||||
fn criterion_benchmark(c: &mut Criterion) {
|
||||
// Pairings
|
||||
{
|
||||
let g = G1Affine::generator();
|
||||
let h = G2Affine::generator();
|
||||
c.bench_function("full pairing", move |b| {
|
||||
b.iter(|| pairing(black_box(&g), black_box(&h)))
|
||||
});
|
||||
c.bench_function("G2 preparation for pairing", move |b| {
|
||||
b.iter(|| G2Prepared::from(h))
|
||||
});
|
||||
let prep = G2Prepared::from(h);
|
||||
c.bench_function("miller loop for pairing", move |b| {
|
||||
b.iter(|| multi_miller_loop(&[(&g, &prep)]))
|
||||
});
|
||||
let prep = G2Prepared::from(h);
|
||||
let r = multi_miller_loop(&[(&g, &prep)]);
|
||||
c.bench_function("final exponentiation for pairing", move |b| {
|
||||
b.iter(|| r.final_exponentiation())
|
||||
});
|
||||
}
|
||||
// G1Affine
|
||||
{
|
||||
let name = "G1Affine";
|
||||
let a = G1Affine::generator();
|
||||
let s = Scalar::from_raw([1, 2, 3, 4]);
|
||||
let compressed = [0u8; 48];
|
||||
let uncompressed = [0u8; 96];
|
||||
c.bench_function(&format!("{} check on curve", name), move |b| {
|
||||
b.iter(|| black_box(a).is_on_curve())
|
||||
});
|
||||
c.bench_function(&format!("{} check equality", name), move |b| {
|
||||
b.iter(|| black_box(a) == black_box(a))
|
||||
});
|
||||
c.bench_function(&format!("{} scalar multiplication", name), move |b| {
|
||||
b.iter(|| black_box(a) * black_box(s))
|
||||
});
|
||||
c.bench_function(&format!("{} subgroup check", name), move |b| {
|
||||
b.iter(|| black_box(a).is_torsion_free())
|
||||
});
|
||||
c.bench_function(
|
||||
&format!("{} deserialize compressed point", name),
|
||||
move |b| b.iter(|| G1Affine::from_compressed(black_box(&compressed))),
|
||||
);
|
||||
c.bench_function(
|
||||
&format!("{} deserialize uncompressed point", name),
|
||||
move |b| b.iter(|| G1Affine::from_uncompressed(black_box(&uncompressed))),
|
||||
);
|
||||
}
|
||||
|
||||
// G1Projective
|
||||
{
|
||||
let name = "G1Projective";
|
||||
let a = G1Projective::generator();
|
||||
let a_affine = G1Affine::generator();
|
||||
let s = Scalar::from_raw([1, 2, 3, 4]);
|
||||
|
||||
const N: usize = 10000;
|
||||
let v = vec![G1Projective::generator(); N];
|
||||
let mut q = vec![G1Affine::identity(); N];
|
||||
|
||||
c.bench_function(&format!("{} check on curve", name), move |b| {
|
||||
b.iter(|| black_box(a).is_on_curve())
|
||||
});
|
||||
c.bench_function(&format!("{} check equality", name), move |b| {
|
||||
b.iter(|| black_box(a) == black_box(a))
|
||||
});
|
||||
c.bench_function(&format!("{} to affine", name), move |b| {
|
||||
b.iter(|| G1Affine::from(black_box(a)))
|
||||
});
|
||||
c.bench_function(&format!("{} doubling", name), move |b| {
|
||||
b.iter(|| black_box(a).double())
|
||||
});
|
||||
c.bench_function(&format!("{} addition", name), move |b| {
|
||||
b.iter(|| black_box(a).add(&a))
|
||||
});
|
||||
c.bench_function(&format!("{} mixed addition", name), move |b| {
|
||||
b.iter(|| black_box(a).add_mixed(&a_affine))
|
||||
});
|
||||
c.bench_function(&format!("{} scalar multiplication", name), move |b| {
|
||||
b.iter(|| black_box(a) * black_box(s))
|
||||
});
|
||||
c.bench_function(&format!("{} batch to affine n={}", name, N), move |b| {
|
||||
b.iter(|| {
|
||||
G1Projective::batch_normalize(black_box(&v), black_box(&mut q));
|
||||
black_box(&q)[0]
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// G2Affine
|
||||
{
|
||||
let name = "G2Affine";
|
||||
let a = G2Affine::generator();
|
||||
let s = Scalar::from_raw([1, 2, 3, 4]);
|
||||
let compressed = [0u8; 96];
|
||||
let uncompressed = [0u8; 192];
|
||||
c.bench_function(&format!("{} check on curve", name), move |b| {
|
||||
b.iter(|| black_box(a).is_on_curve())
|
||||
});
|
||||
c.bench_function(&format!("{} check equality", name), move |b| {
|
||||
b.iter(|| black_box(a) == black_box(a))
|
||||
});
|
||||
c.bench_function(&format!("{} scalar multiplication", name), move |b| {
|
||||
b.iter(|| black_box(a) * black_box(s))
|
||||
});
|
||||
c.bench_function(&format!("{} subgroup check", name), move |b| {
|
||||
b.iter(|| black_box(a).is_torsion_free())
|
||||
});
|
||||
c.bench_function(
|
||||
&format!("{} deserialize compressed point", name),
|
||||
move |b| b.iter(|| G2Affine::from_compressed(black_box(&compressed))),
|
||||
);
|
||||
c.bench_function(
|
||||
&format!("{} deserialize uncompressed point", name),
|
||||
move |b| b.iter(|| G2Affine::from_uncompressed(black_box(&uncompressed))),
|
||||
);
|
||||
}
|
||||
|
||||
// G2Projective
|
||||
{
|
||||
let name = "G2Projective";
|
||||
let a = G2Projective::generator();
|
||||
let a_affine = G2Affine::generator();
|
||||
let s = Scalar::from_raw([1, 2, 3, 4]);
|
||||
|
||||
const N: usize = 10000;
|
||||
let v = vec![G2Projective::generator(); N];
|
||||
let mut q = vec![G2Affine::identity(); N];
|
||||
|
||||
c.bench_function(&format!("{} check on curve", name), move |b| {
|
||||
b.iter(|| black_box(a).is_on_curve())
|
||||
});
|
||||
c.bench_function(&format!("{} check equality", name), move |b| {
|
||||
b.iter(|| black_box(a) == black_box(a))
|
||||
});
|
||||
c.bench_function(&format!("{} to affine", name), move |b| {
|
||||
b.iter(|| G2Affine::from(black_box(a)))
|
||||
});
|
||||
c.bench_function(&format!("{} doubling", name), move |b| {
|
||||
b.iter(|| black_box(a).double())
|
||||
});
|
||||
c.bench_function(&format!("{} addition", name), move |b| {
|
||||
b.iter(|| black_box(a).add(&a))
|
||||
});
|
||||
c.bench_function(&format!("{} mixed addition", name), move |b| {
|
||||
b.iter(|| black_box(a).add_mixed(&a_affine))
|
||||
});
|
||||
c.bench_function(&format!("{} scalar multiplication", name), move |b| {
|
||||
b.iter(|| black_box(a) * black_box(s))
|
||||
});
|
||||
c.bench_function(&format!("{} batch to affine n={}", name, N), move |b| {
|
||||
b.iter(|| {
|
||||
G2Projective::batch_normalize(black_box(&v), black_box(&mut q));
|
||||
black_box(&q)[0]
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
criterion_group!(benches, criterion_benchmark);
|
||||
criterion_main!(benches);
|
15
bls12_381/katex-header.html
Normal file
15
bls12_381/katex-header.html
Normal file
@ -0,0 +1,15 @@
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.10.0/dist/katex.min.css" integrity="sha384-9eLZqc9ds8eNjO3TmqPeYcDj8n+Qfa4nuSiGYa6DjLNcv9BtN69ZIulL9+8CqC9Y" crossorigin="anonymous">
|
||||
<script src="https://cdn.jsdelivr.net/npm/katex@0.10.0/dist/katex.min.js" integrity="sha384-K3vbOmF2BtaVai+Qk37uypf7VrgBubhQreNQe9aGsz9lB63dIFiQVlJbr92dw2Lx" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/katex@0.10.0/dist/contrib/auto-render.min.js" integrity="sha384-kmZOZB5ObwgQnS/DuDg6TScgOiWWBiVt0plIRkZCmE6rDZGrEOQeHM5PcHi+nyqe" crossorigin="anonymous"></script>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
renderMathInElement(document.body, {
|
||||
delimiters: [
|
||||
{left: "$$", right: "$$", display: true},
|
||||
{left: "\\(", right: "\\)", display: false},
|
||||
{left: "$", right: "$", display: false},
|
||||
{left: "\\[", right: "\\]", display: true}
|
||||
]
|
||||
});
|
||||
});
|
||||
</script>
|
1
bls12_381/rust-toolchain
Normal file
1
bls12_381/rust-toolchain
Normal file
@ -0,0 +1 @@
|
||||
1.36.0
|
866
bls12_381/src/fp.rs
Normal file
866
bls12_381/src/fp.rs
Normal file
@ -0,0 +1,866 @@
|
||||
//! This module provides an implementation of the BLS12-381 base field `GF(p)`
|
||||
//! where `p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab`
|
||||
|
||||
use core::convert::TryFrom;
|
||||
use core::fmt;
|
||||
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
||||
|
||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
|
||||
|
||||
use crate::util::{adc, mac, sbb};
|
||||
|
||||
// The internal representation of this type is six 64-bit unsigned
|
||||
// integers in little-endian order. `Fp` values are always in
|
||||
// Montgomery form; i.e., Scalar(a) = aR mod p, with R = 2^384.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Fp([u64; 6]);
|
||||
|
||||
impl fmt::Debug for Fp {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let tmp = self.to_bytes();
|
||||
write!(f, "0x")?;
|
||||
for &b in tmp.iter() {
|
||||
write!(f, "{:02x}", b)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Fp {
|
||||
fn default() -> Self {
|
||||
Fp::zero()
|
||||
}
|
||||
}
|
||||
|
||||
impl ConstantTimeEq for Fp {
|
||||
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])
|
||||
& self.0[4].ct_eq(&other.0[4])
|
||||
& self.0[5].ct_eq(&other.0[5])
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Fp {}
|
||||
impl PartialEq for Fp {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.ct_eq(other).unwrap_u8() == 1
|
||||
}
|
||||
}
|
||||
|
||||
impl ConditionallySelectable for Fp {
|
||||
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
|
||||
Fp([
|
||||
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),
|
||||
u64::conditional_select(&a.0[4], &b.0[4], choice),
|
||||
u64::conditional_select(&a.0[5], &b.0[5], choice),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
/// p = 4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787
|
||||
const MODULUS: [u64; 6] = [
|
||||
0xb9fe_ffff_ffff_aaab,
|
||||
0x1eab_fffe_b153_ffff,
|
||||
0x6730_d2a0_f6b0_f624,
|
||||
0x6477_4b84_f385_12bf,
|
||||
0x4b1b_a7b6_434b_acd7,
|
||||
0x1a01_11ea_397f_e69a,
|
||||
];
|
||||
|
||||
/// INV = -(p^{-1} mod 2^64) mod 2^64
|
||||
const INV: u64 = 0x89f3_fffc_fffc_fffd;
|
||||
|
||||
/// R = 2^384 mod p
|
||||
const R: Fp = Fp([
|
||||
0x7609_0000_0002_fffd,
|
||||
0xebf4_000b_c40c_0002,
|
||||
0x5f48_9857_53c7_58ba,
|
||||
0x77ce_5853_7052_5745,
|
||||
0x5c07_1a97_a256_ec6d,
|
||||
0x15f6_5ec3_fa80_e493,
|
||||
]);
|
||||
|
||||
/// R2 = 2^(384*2) mod p
|
||||
const R2: Fp = Fp([
|
||||
0xf4df_1f34_1c34_1746,
|
||||
0x0a76_e6a6_09d1_04f1,
|
||||
0x8de5_476c_4c95_b6d5,
|
||||
0x67eb_88a9_939d_83c0,
|
||||
0x9a79_3e85_b519_952d,
|
||||
0x1198_8fe5_92ca_e3aa,
|
||||
]);
|
||||
|
||||
impl<'a> Neg for &'a Fp {
|
||||
type Output = Fp;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Fp {
|
||||
self.neg()
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for Fp {
|
||||
type Output = Fp;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Fp {
|
||||
-&self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Sub<&'b Fp> for &'a Fp {
|
||||
type Output = Fp;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: &'b Fp) -> Fp {
|
||||
self.sub(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Add<&'b Fp> for &'a Fp {
|
||||
type Output = Fp;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: &'b Fp) -> Fp {
|
||||
self.add(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Mul<&'b Fp> for &'a Fp {
|
||||
type Output = Fp;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, rhs: &'b Fp) -> Fp {
|
||||
self.mul(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl_binops_additive!(Fp, Fp);
|
||||
impl_binops_multiplicative!(Fp, Fp);
|
||||
|
||||
impl Fp {
|
||||
/// Returns zero, the additive identity.
|
||||
#[inline]
|
||||
pub const fn zero() -> Fp {
|
||||
Fp([0, 0, 0, 0, 0, 0])
|
||||
}
|
||||
|
||||
/// Returns one, the multiplicative identity.
|
||||
#[inline]
|
||||
pub const fn one() -> Fp {
|
||||
R
|
||||
}
|
||||
|
||||
pub fn is_zero(&self) -> Choice {
|
||||
self.ct_eq(&Fp::zero())
|
||||
}
|
||||
|
||||
/// Attempts to convert a little-endian byte representation of
|
||||
/// a scalar into an `Fp`, failing if the input is not canonical.
|
||||
pub fn from_bytes(bytes: &[u8; 48]) -> CtOption<Fp> {
|
||||
let mut tmp = Fp([0, 0, 0, 0, 0, 0]);
|
||||
|
||||
tmp.0[5] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[0..8]).unwrap());
|
||||
tmp.0[4] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[8..16]).unwrap());
|
||||
tmp.0[3] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[16..24]).unwrap());
|
||||
tmp.0[2] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[24..32]).unwrap());
|
||||
tmp.0[1] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[32..40]).unwrap());
|
||||
tmp.0[0] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[40..48]).unwrap());
|
||||
|
||||
// Try to subtract the modulus
|
||||
let (_, borrow) = sbb(tmp.0[0], MODULUS[0], 0);
|
||||
let (_, borrow) = sbb(tmp.0[1], MODULUS[1], borrow);
|
||||
let (_, borrow) = sbb(tmp.0[2], MODULUS[2], borrow);
|
||||
let (_, borrow) = sbb(tmp.0[3], MODULUS[3], borrow);
|
||||
let (_, borrow) = sbb(tmp.0[4], MODULUS[4], borrow);
|
||||
let (_, borrow) = sbb(tmp.0[5], MODULUS[5], 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))
|
||||
}
|
||||
|
||||
/// Converts an element of `Fp` into a byte representation in
|
||||
/// big-endian byte order.
|
||||
pub fn to_bytes(&self) -> [u8; 48] {
|
||||
// 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], self.0[4], self.0[5], 0, 0, 0, 0, 0, 0,
|
||||
);
|
||||
|
||||
let mut res = [0; 48];
|
||||
res[0..8].copy_from_slice(&tmp.0[5].to_be_bytes());
|
||||
res[8..16].copy_from_slice(&tmp.0[4].to_be_bytes());
|
||||
res[16..24].copy_from_slice(&tmp.0[3].to_be_bytes());
|
||||
res[24..32].copy_from_slice(&tmp.0[2].to_be_bytes());
|
||||
res[32..40].copy_from_slice(&tmp.0[1].to_be_bytes());
|
||||
res[40..48].copy_from_slice(&tmp.0[0].to_be_bytes());
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
/// Returns whether or not this element is strictly lexicographically
|
||||
/// larger than its negation.
|
||||
pub fn lexicographically_largest(&self) -> Choice {
|
||||
// This can be determined by checking to see if the element is
|
||||
// larger than (p - 1) // 2. If we subtract by ((p - 1) // 2) + 1
|
||||
// and there is no underflow, then the element must be larger than
|
||||
// (p - 1) // 2.
|
||||
|
||||
// First, because self is in Montgomery form we need to reduce it
|
||||
let tmp = Fp::montgomery_reduce(
|
||||
self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], 0, 0, 0, 0, 0, 0,
|
||||
);
|
||||
|
||||
let (_, borrow) = sbb(tmp.0[0], 0xdcff_7fff_ffff_d556, 0);
|
||||
let (_, borrow) = sbb(tmp.0[1], 0x0f55_ffff_58a9_ffff, borrow);
|
||||
let (_, borrow) = sbb(tmp.0[2], 0xb398_6950_7b58_7b12, borrow);
|
||||
let (_, borrow) = sbb(tmp.0[3], 0xb23b_a5c2_79c2_895f, borrow);
|
||||
let (_, borrow) = sbb(tmp.0[4], 0x258d_d3db_21a5_d66b, borrow);
|
||||
let (_, borrow) = sbb(tmp.0[5], 0x0d00_88f5_1cbf_f34d, borrow);
|
||||
|
||||
// If the element was smaller, the subtraction will underflow
|
||||
// producing a borrow value of 0xffff...ffff, otherwise it will
|
||||
// be zero. We create a Choice representing true if there was
|
||||
// overflow (and so this element is not lexicographically larger
|
||||
// than its negation) and then negate it.
|
||||
|
||||
!Choice::from((borrow as u8) & 1)
|
||||
}
|
||||
|
||||
/// Constructs an element of `Fp` without checking that it is
|
||||
/// canonical.
|
||||
pub const fn from_raw_unchecked(v: [u64; 6]) -> Fp {
|
||||
Fp(v)
|
||||
}
|
||||
|
||||
/// Although this is labeled "vartime", it is only
|
||||
/// variable time with respect to the exponent. It
|
||||
/// is also not exposed in the public API.
|
||||
pub fn pow_vartime(&self, by: &[u64; 6]) -> Self {
|
||||
let mut res = Self::one();
|
||||
for e in by.iter().rev() {
|
||||
for i in (0..64).rev() {
|
||||
res = res.square();
|
||||
|
||||
if ((*e >> i) & 1) == 1 {
|
||||
res *= self;
|
||||
}
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn sqrt(&self) -> CtOption<Self> {
|
||||
// We use Shank's method, as p = 3 (mod 4). This means
|
||||
// we only need to exponentiate by (p+1)/4. This only
|
||||
// works for elements that are actually quadratic residue,
|
||||
// so we check that we got the correct result at the end.
|
||||
|
||||
let sqrt = self.pow_vartime(&[
|
||||
0xee7f_bfff_ffff_eaab,
|
||||
0x07aa_ffff_ac54_ffff,
|
||||
0xd9cc_34a8_3dac_3d89,
|
||||
0xd91d_d2e1_3ce1_44af,
|
||||
0x92c6_e9ed_90d2_eb35,
|
||||
0x0680_447a_8e5f_f9a6,
|
||||
]);
|
||||
|
||||
CtOption::new(sqrt, sqrt.square().ct_eq(self))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Computes the multiplicative inverse of this field
|
||||
/// element, returning None in the case that this element
|
||||
/// is zero.
|
||||
pub fn invert(&self) -> CtOption<Self> {
|
||||
// Exponentiate by p - 2
|
||||
let t = self.pow_vartime(&[
|
||||
0xb9fe_ffff_ffff_aaa9,
|
||||
0x1eab_fffe_b153_ffff,
|
||||
0x6730_d2a0_f6b0_f624,
|
||||
0x6477_4b84_f385_12bf,
|
||||
0x4b1b_a7b6_434b_acd7,
|
||||
0x1a01_11ea_397f_e69a,
|
||||
]);
|
||||
|
||||
CtOption::new(t, !self.is_zero())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
const fn subtract_p(&self) -> Fp {
|
||||
let (r0, borrow) = sbb(self.0[0], MODULUS[0], 0);
|
||||
let (r1, borrow) = sbb(self.0[1], MODULUS[1], borrow);
|
||||
let (r2, borrow) = sbb(self.0[2], MODULUS[2], borrow);
|
||||
let (r3, borrow) = sbb(self.0[3], MODULUS[3], borrow);
|
||||
let (r4, borrow) = sbb(self.0[4], MODULUS[4], borrow);
|
||||
let (r5, borrow) = sbb(self.0[5], MODULUS[5], borrow);
|
||||
|
||||
// If underflow occurred on the final limb, borrow = 0xfff...fff, otherwise
|
||||
// borrow = 0x000...000. Thus, we use it as a mask!
|
||||
let r0 = (self.0[0] & borrow) | (r0 & !borrow);
|
||||
let r1 = (self.0[1] & borrow) | (r1 & !borrow);
|
||||
let r2 = (self.0[2] & borrow) | (r2 & !borrow);
|
||||
let r3 = (self.0[3] & borrow) | (r3 & !borrow);
|
||||
let r4 = (self.0[4] & borrow) | (r4 & !borrow);
|
||||
let r5 = (self.0[5] & borrow) | (r5 & !borrow);
|
||||
|
||||
Fp([r0, r1, r2, r3, r4, r5])
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn add(&self, rhs: &Fp) -> Fp {
|
||||
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);
|
||||
let (d4, carry) = adc(self.0[4], rhs.0[4], carry);
|
||||
let (d5, _) = adc(self.0[5], rhs.0[5], carry);
|
||||
|
||||
// Attempt to subtract the modulus, to ensure the value
|
||||
// is smaller than the modulus.
|
||||
(&Fp([d0, d1, d2, d3, d4, d5])).subtract_p()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn neg(&self) -> Fp {
|
||||
let (d0, borrow) = sbb(MODULUS[0], self.0[0], 0);
|
||||
let (d1, borrow) = sbb(MODULUS[1], self.0[1], borrow);
|
||||
let (d2, borrow) = sbb(MODULUS[2], self.0[2], borrow);
|
||||
let (d3, borrow) = sbb(MODULUS[3], self.0[3], borrow);
|
||||
let (d4, borrow) = sbb(MODULUS[4], self.0[4], borrow);
|
||||
let (d5, _) = sbb(MODULUS[5], self.0[5], borrow);
|
||||
|
||||
// Let's use a mask if `self` was zero, which would mean
|
||||
// the result of the subtraction is p.
|
||||
let mask = (((self.0[0] | self.0[1] | self.0[2] | self.0[3] | self.0[4] | self.0[5]) == 0)
|
||||
as u64)
|
||||
.wrapping_sub(1);
|
||||
|
||||
Fp([
|
||||
d0 & mask,
|
||||
d1 & mask,
|
||||
d2 & mask,
|
||||
d3 & mask,
|
||||
d4 & mask,
|
||||
d5 & mask,
|
||||
])
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn sub(&self, rhs: &Fp) -> Fp {
|
||||
(&rhs.neg()).add(self)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
const fn montgomery_reduce(
|
||||
t0: u64,
|
||||
t1: u64,
|
||||
t2: u64,
|
||||
t3: u64,
|
||||
t4: u64,
|
||||
t5: u64,
|
||||
t6: u64,
|
||||
t7: u64,
|
||||
t8: u64,
|
||||
t9: u64,
|
||||
t10: u64,
|
||||
t11: 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 = t0.wrapping_mul(INV);
|
||||
let (_, carry) = mac(t0, k, MODULUS[0], 0);
|
||||
let (r1, carry) = mac(t1, k, MODULUS[1], carry);
|
||||
let (r2, carry) = mac(t2, k, MODULUS[2], carry);
|
||||
let (r3, carry) = mac(t3, k, MODULUS[3], carry);
|
||||
let (r4, carry) = mac(t4, k, MODULUS[4], carry);
|
||||
let (r5, carry) = mac(t5, k, MODULUS[5], carry);
|
||||
let (r6, r7) = adc(t6, 0, carry);
|
||||
|
||||
let k = r1.wrapping_mul(INV);
|
||||
let (_, carry) = mac(r1, k, MODULUS[0], 0);
|
||||
let (r2, carry) = mac(r2, k, MODULUS[1], carry);
|
||||
let (r3, carry) = mac(r3, k, MODULUS[2], carry);
|
||||
let (r4, carry) = mac(r4, k, MODULUS[3], carry);
|
||||
let (r5, carry) = mac(r5, k, MODULUS[4], carry);
|
||||
let (r6, carry) = mac(r6, k, MODULUS[5], carry);
|
||||
let (r7, r8) = adc(t7, r7, carry);
|
||||
|
||||
let k = r2.wrapping_mul(INV);
|
||||
let (_, carry) = mac(r2, k, MODULUS[0], 0);
|
||||
let (r3, carry) = mac(r3, k, MODULUS[1], carry);
|
||||
let (r4, carry) = mac(r4, k, MODULUS[2], carry);
|
||||
let (r5, carry) = mac(r5, k, MODULUS[3], carry);
|
||||
let (r6, carry) = mac(r6, k, MODULUS[4], carry);
|
||||
let (r7, carry) = mac(r7, k, MODULUS[5], carry);
|
||||
let (r8, r9) = adc(t8, r8, carry);
|
||||
|
||||
let k = r3.wrapping_mul(INV);
|
||||
let (_, carry) = mac(r3, k, MODULUS[0], 0);
|
||||
let (r4, carry) = mac(r4, k, MODULUS[1], carry);
|
||||
let (r5, carry) = mac(r5, k, MODULUS[2], carry);
|
||||
let (r6, carry) = mac(r6, k, MODULUS[3], carry);
|
||||
let (r7, carry) = mac(r7, k, MODULUS[4], carry);
|
||||
let (r8, carry) = mac(r8, k, MODULUS[5], carry);
|
||||
let (r9, r10) = adc(t9, r9, carry);
|
||||
|
||||
let k = r4.wrapping_mul(INV);
|
||||
let (_, carry) = mac(r4, k, MODULUS[0], 0);
|
||||
let (r5, carry) = mac(r5, k, MODULUS[1], carry);
|
||||
let (r6, carry) = mac(r6, k, MODULUS[2], carry);
|
||||
let (r7, carry) = mac(r7, k, MODULUS[3], carry);
|
||||
let (r8, carry) = mac(r8, k, MODULUS[4], carry);
|
||||
let (r9, carry) = mac(r9, k, MODULUS[5], carry);
|
||||
let (r10, r11) = adc(t10, r10, carry);
|
||||
|
||||
let k = r5.wrapping_mul(INV);
|
||||
let (_, carry) = mac(r5, k, MODULUS[0], 0);
|
||||
let (r6, carry) = mac(r6, k, MODULUS[1], carry);
|
||||
let (r7, carry) = mac(r7, k, MODULUS[2], carry);
|
||||
let (r8, carry) = mac(r8, k, MODULUS[3], carry);
|
||||
let (r9, carry) = mac(r9, k, MODULUS[4], carry);
|
||||
let (r10, carry) = mac(r10, k, MODULUS[5], carry);
|
||||
let (r11, _) = adc(t11, r11, carry);
|
||||
|
||||
// Attempt to subtract the modulus, to ensure the value
|
||||
// is smaller than the modulus.
|
||||
(&Fp([r6, r7, r8, r9, r10, r11])).subtract_p()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn mul(&self, rhs: &Fp) -> Fp {
|
||||
let (t0, carry) = mac(0, self.0[0], rhs.0[0], 0);
|
||||
let (t1, carry) = mac(0, self.0[0], rhs.0[1], carry);
|
||||
let (t2, carry) = mac(0, self.0[0], rhs.0[2], carry);
|
||||
let (t3, carry) = mac(0, self.0[0], rhs.0[3], carry);
|
||||
let (t4, carry) = mac(0, self.0[0], rhs.0[4], carry);
|
||||
let (t5, t6) = mac(0, self.0[0], rhs.0[5], carry);
|
||||
|
||||
let (t1, carry) = mac(t1, self.0[1], rhs.0[0], 0);
|
||||
let (t2, carry) = mac(t2, self.0[1], rhs.0[1], carry);
|
||||
let (t3, carry) = mac(t3, self.0[1], rhs.0[2], carry);
|
||||
let (t4, carry) = mac(t4, self.0[1], rhs.0[3], carry);
|
||||
let (t5, carry) = mac(t5, self.0[1], rhs.0[4], carry);
|
||||
let (t6, t7) = mac(t6, self.0[1], rhs.0[5], carry);
|
||||
|
||||
let (t2, carry) = mac(t2, self.0[2], rhs.0[0], 0);
|
||||
let (t3, carry) = mac(t3, self.0[2], rhs.0[1], carry);
|
||||
let (t4, carry) = mac(t4, self.0[2], rhs.0[2], carry);
|
||||
let (t5, carry) = mac(t5, self.0[2], rhs.0[3], carry);
|
||||
let (t6, carry) = mac(t6, self.0[2], rhs.0[4], carry);
|
||||
let (t7, t8) = mac(t7, self.0[2], rhs.0[5], carry);
|
||||
|
||||
let (t3, carry) = mac(t3, self.0[3], rhs.0[0], 0);
|
||||
let (t4, carry) = mac(t4, self.0[3], rhs.0[1], carry);
|
||||
let (t5, carry) = mac(t5, self.0[3], rhs.0[2], carry);
|
||||
let (t6, carry) = mac(t6, self.0[3], rhs.0[3], carry);
|
||||
let (t7, carry) = mac(t7, self.0[3], rhs.0[4], carry);
|
||||
let (t8, t9) = mac(t8, self.0[3], rhs.0[5], carry);
|
||||
|
||||
let (t4, carry) = mac(t4, self.0[4], rhs.0[0], 0);
|
||||
let (t5, carry) = mac(t5, self.0[4], rhs.0[1], carry);
|
||||
let (t6, carry) = mac(t6, self.0[4], rhs.0[2], carry);
|
||||
let (t7, carry) = mac(t7, self.0[4], rhs.0[3], carry);
|
||||
let (t8, carry) = mac(t8, self.0[4], rhs.0[4], carry);
|
||||
let (t9, t10) = mac(t9, self.0[4], rhs.0[5], carry);
|
||||
|
||||
let (t5, carry) = mac(t5, self.0[5], rhs.0[0], 0);
|
||||
let (t6, carry) = mac(t6, self.0[5], rhs.0[1], carry);
|
||||
let (t7, carry) = mac(t7, self.0[5], rhs.0[2], carry);
|
||||
let (t8, carry) = mac(t8, self.0[5], rhs.0[3], carry);
|
||||
let (t9, carry) = mac(t9, self.0[5], rhs.0[4], carry);
|
||||
let (t10, t11) = mac(t10, self.0[5], rhs.0[5], carry);
|
||||
|
||||
Self::montgomery_reduce(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11)
|
||||
}
|
||||
|
||||
/// Squares this element.
|
||||
#[inline]
|
||||
pub const fn square(&self) -> Self {
|
||||
let (t1, carry) = mac(0, self.0[0], self.0[1], 0);
|
||||
let (t2, carry) = mac(0, self.0[0], self.0[2], carry);
|
||||
let (t3, carry) = mac(0, self.0[0], self.0[3], carry);
|
||||
let (t4, carry) = mac(0, self.0[0], self.0[4], carry);
|
||||
let (t5, t6) = mac(0, self.0[0], self.0[5], carry);
|
||||
|
||||
let (t3, carry) = mac(t3, self.0[1], self.0[2], 0);
|
||||
let (t4, carry) = mac(t4, self.0[1], self.0[3], carry);
|
||||
let (t5, carry) = mac(t5, self.0[1], self.0[4], carry);
|
||||
let (t6, t7) = mac(t6, self.0[1], self.0[5], carry);
|
||||
|
||||
let (t5, carry) = mac(t5, self.0[2], self.0[3], 0);
|
||||
let (t6, carry) = mac(t6, self.0[2], self.0[4], carry);
|
||||
let (t7, t8) = mac(t7, self.0[2], self.0[5], carry);
|
||||
|
||||
let (t7, carry) = mac(t7, self.0[3], self.0[4], 0);
|
||||
let (t8, t9) = mac(t8, self.0[3], self.0[5], carry);
|
||||
|
||||
let (t9, t10) = mac(t9, self.0[4], self.0[5], 0);
|
||||
|
||||
let t11 = t10 >> 63;
|
||||
let t10 = (t10 << 1) | (t9 >> 63);
|
||||
let t9 = (t9 << 1) | (t8 >> 63);
|
||||
let t8 = (t8 << 1) | (t7 >> 63);
|
||||
let t7 = (t7 << 1) | (t6 >> 63);
|
||||
let t6 = (t6 << 1) | (t5 >> 63);
|
||||
let t5 = (t5 << 1) | (t4 >> 63);
|
||||
let t4 = (t4 << 1) | (t3 >> 63);
|
||||
let t3 = (t3 << 1) | (t2 >> 63);
|
||||
let t2 = (t2 << 1) | (t1 >> 63);
|
||||
let t1 = t1 << 1;
|
||||
|
||||
let (t0, carry) = mac(0, self.0[0], self.0[0], 0);
|
||||
let (t1, carry) = adc(t1, 0, carry);
|
||||
let (t2, carry) = mac(t2, self.0[1], self.0[1], carry);
|
||||
let (t3, carry) = adc(t3, 0, carry);
|
||||
let (t4, carry) = mac(t4, self.0[2], self.0[2], carry);
|
||||
let (t5, carry) = adc(t5, 0, carry);
|
||||
let (t6, carry) = mac(t6, self.0[3], self.0[3], carry);
|
||||
let (t7, carry) = adc(t7, 0, carry);
|
||||
let (t8, carry) = mac(t8, self.0[4], self.0[4], carry);
|
||||
let (t9, carry) = adc(t9, 0, carry);
|
||||
let (t10, carry) = mac(t10, self.0[5], self.0[5], carry);
|
||||
let (t11, _) = adc(t11, 0, carry);
|
||||
|
||||
Self::montgomery_reduce(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_conditional_selection() {
|
||||
let a = Fp([1, 2, 3, 4, 5, 6]);
|
||||
let b = Fp([7, 8, 9, 10, 11, 12]);
|
||||
|
||||
assert_eq!(
|
||||
ConditionallySelectable::conditional_select(&a, &b, Choice::from(0u8)),
|
||||
a
|
||||
);
|
||||
assert_eq!(
|
||||
ConditionallySelectable::conditional_select(&a, &b, Choice::from(1u8)),
|
||||
b
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_equality() {
|
||||
fn is_equal(a: &Fp, b: &Fp) -> bool {
|
||||
let eq = a == b;
|
||||
let ct_eq = a.ct_eq(&b);
|
||||
|
||||
assert_eq!(eq, ct_eq.unwrap_u8() == 1);
|
||||
|
||||
eq
|
||||
}
|
||||
|
||||
assert!(is_equal(&Fp([1, 2, 3, 4, 5, 6]), &Fp([1, 2, 3, 4, 5, 6])));
|
||||
|
||||
assert!(!is_equal(&Fp([7, 2, 3, 4, 5, 6]), &Fp([1, 2, 3, 4, 5, 6])));
|
||||
assert!(!is_equal(&Fp([1, 7, 3, 4, 5, 6]), &Fp([1, 2, 3, 4, 5, 6])));
|
||||
assert!(!is_equal(&Fp([1, 2, 7, 4, 5, 6]), &Fp([1, 2, 3, 4, 5, 6])));
|
||||
assert!(!is_equal(&Fp([1, 2, 3, 7, 5, 6]), &Fp([1, 2, 3, 4, 5, 6])));
|
||||
assert!(!is_equal(&Fp([1, 2, 3, 4, 7, 6]), &Fp([1, 2, 3, 4, 5, 6])));
|
||||
assert!(!is_equal(&Fp([1, 2, 3, 4, 5, 7]), &Fp([1, 2, 3, 4, 5, 6])));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_squaring() {
|
||||
let a = Fp([
|
||||
0xd215_d276_8e83_191b,
|
||||
0x5085_d80f_8fb2_8261,
|
||||
0xce9a_032d_df39_3a56,
|
||||
0x3e9c_4fff_2ca0_c4bb,
|
||||
0x6436_b6f7_f4d9_5dfb,
|
||||
0x1060_6628_ad4a_4d90,
|
||||
]);
|
||||
let b = Fp([
|
||||
0x33d9_c42a_3cb3_e235,
|
||||
0xdad1_1a09_4c4c_d455,
|
||||
0xa2f1_44bd_729a_aeba,
|
||||
0xd415_0932_be9f_feac,
|
||||
0xe27b_c7c4_7d44_ee50,
|
||||
0x14b6_a78d_3ec7_a560,
|
||||
]);
|
||||
|
||||
assert_eq!(a.square(), b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiplication() {
|
||||
let a = Fp([
|
||||
0x0397_a383_2017_0cd4,
|
||||
0x734c_1b2c_9e76_1d30,
|
||||
0x5ed2_55ad_9a48_beb5,
|
||||
0x095a_3c6b_22a7_fcfc,
|
||||
0x2294_ce75_d4e2_6a27,
|
||||
0x1333_8bd8_7001_1ebb,
|
||||
]);
|
||||
let b = Fp([
|
||||
0xb9c3_c7c5_b119_6af7,
|
||||
0x2580_e208_6ce3_35c1,
|
||||
0xf49a_ed3d_8a57_ef42,
|
||||
0x41f2_81e4_9846_e878,
|
||||
0xe076_2346_c384_52ce,
|
||||
0x0652_e893_26e5_7dc0,
|
||||
]);
|
||||
let c = Fp([
|
||||
0xf96e_f3d7_11ab_5355,
|
||||
0xe8d4_59ea_00f1_48dd,
|
||||
0x53f7_354a_5f00_fa78,
|
||||
0x9e34_a4f3_125c_5f83,
|
||||
0x3fbe_0c47_ca74_c19e,
|
||||
0x01b0_6a8b_bd4a_dfe4,
|
||||
]);
|
||||
|
||||
assert_eq!(a * b, c);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_addition() {
|
||||
let a = Fp([
|
||||
0x5360_bb59_7867_8032,
|
||||
0x7dd2_75ae_799e_128e,
|
||||
0x5c5b_5071_ce4f_4dcf,
|
||||
0xcdb2_1f93_078d_bb3e,
|
||||
0xc323_65c5_e73f_474a,
|
||||
0x115a_2a54_89ba_be5b,
|
||||
]);
|
||||
let b = Fp([
|
||||
0x9fd2_8773_3d23_dda0,
|
||||
0xb16b_f2af_738b_3554,
|
||||
0x3e57_a75b_d3cc_6d1d,
|
||||
0x900b_c0bd_627f_d6d6,
|
||||
0xd319_a080_efb2_45fe,
|
||||
0x15fd_caa4_e4bb_2091,
|
||||
]);
|
||||
let c = Fp([
|
||||
0x3934_42cc_b58b_b327,
|
||||
0x1092_685f_3bd5_47e3,
|
||||
0x3382_252c_ab6a_c4c9,
|
||||
0xf946_94cb_7688_7f55,
|
||||
0x4b21_5e90_93a5_e071,
|
||||
0x0d56_e30f_34f5_f853,
|
||||
]);
|
||||
|
||||
assert_eq!(a + b, c);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_subtraction() {
|
||||
let a = Fp([
|
||||
0x5360_bb59_7867_8032,
|
||||
0x7dd2_75ae_799e_128e,
|
||||
0x5c5b_5071_ce4f_4dcf,
|
||||
0xcdb2_1f93_078d_bb3e,
|
||||
0xc323_65c5_e73f_474a,
|
||||
0x115a_2a54_89ba_be5b,
|
||||
]);
|
||||
let b = Fp([
|
||||
0x9fd2_8773_3d23_dda0,
|
||||
0xb16b_f2af_738b_3554,
|
||||
0x3e57_a75b_d3cc_6d1d,
|
||||
0x900b_c0bd_627f_d6d6,
|
||||
0xd319_a080_efb2_45fe,
|
||||
0x15fd_caa4_e4bb_2091,
|
||||
]);
|
||||
let c = Fp([
|
||||
0x6d8d_33e6_3b43_4d3d,
|
||||
0xeb12_82fd_b766_dd39,
|
||||
0x8534_7bb6_f133_d6d5,
|
||||
0xa21d_aa5a_9892_f727,
|
||||
0x3b25_6cfb_3ad8_ae23,
|
||||
0x155d_7199_de7f_8464,
|
||||
]);
|
||||
|
||||
assert_eq!(a - b, c);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_negation() {
|
||||
let a = Fp([
|
||||
0x5360_bb59_7867_8032,
|
||||
0x7dd2_75ae_799e_128e,
|
||||
0x5c5b_5071_ce4f_4dcf,
|
||||
0xcdb2_1f93_078d_bb3e,
|
||||
0xc323_65c5_e73f_474a,
|
||||
0x115a_2a54_89ba_be5b,
|
||||
]);
|
||||
let b = Fp([
|
||||
0x669e_44a6_8798_2a79,
|
||||
0xa0d9_8a50_37b5_ed71,
|
||||
0x0ad5_822f_2861_a854,
|
||||
0x96c5_2bf1_ebf7_5781,
|
||||
0x87f8_41f0_5c0c_658c,
|
||||
0x08a6_e795_afc5_283e,
|
||||
]);
|
||||
|
||||
assert_eq!(-a, b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_debug() {
|
||||
assert_eq!(
|
||||
format!(
|
||||
"{:?}",
|
||||
Fp([
|
||||
0x5360_bb59_7867_8032,
|
||||
0x7dd2_75ae_799e_128e,
|
||||
0x5c5b_5071_ce4f_4dcf,
|
||||
0xcdb2_1f93_078d_bb3e,
|
||||
0xc323_65c5_e73f_474a,
|
||||
0x115a_2a54_89ba_be5b,
|
||||
])
|
||||
),
|
||||
"0x104bf052ad3bc99bcb176c24a06a6c3aad4eaf2308fc4d282e106c84a757d061052630515305e59bdddf8111bfdeb704"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_bytes() {
|
||||
let mut a = Fp([
|
||||
0xdc90_6d9b_e3f9_5dc8,
|
||||
0x8755_caf7_4596_91a1,
|
||||
0xcff1_a7f4_e958_3ab3,
|
||||
0x9b43_821f_849e_2284,
|
||||
0xf575_54f3_a297_4f3f,
|
||||
0x085d_bea8_4ed4_7f79,
|
||||
]);
|
||||
|
||||
for _ in 0..100 {
|
||||
a = a.square();
|
||||
let tmp = a.to_bytes();
|
||||
let b = Fp::from_bytes(&tmp).unwrap();
|
||||
|
||||
assert_eq!(a, b);
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
-Fp::one(),
|
||||
Fp::from_bytes(&[
|
||||
26, 1, 17, 234, 57, 127, 230, 154, 75, 27, 167, 182, 67, 75, 172, 215, 100, 119, 75,
|
||||
132, 243, 133, 18, 191, 103, 48, 210, 160, 246, 176, 246, 36, 30, 171, 255, 254, 177,
|
||||
83, 255, 255, 185, 254, 255, 255, 255, 255, 170, 170
|
||||
])
|
||||
.unwrap()
|
||||
);
|
||||
|
||||
assert!(
|
||||
Fp::from_bytes(&[
|
||||
27, 1, 17, 234, 57, 127, 230, 154, 75, 27, 167, 182, 67, 75, 172, 215, 100, 119, 75,
|
||||
132, 243, 133, 18, 191, 103, 48, 210, 160, 246, 176, 246, 36, 30, 171, 255, 254, 177,
|
||||
83, 255, 255, 185, 254, 255, 255, 255, 255, 170, 170
|
||||
])
|
||||
.is_none()
|
||||
.unwrap_u8()
|
||||
== 1
|
||||
);
|
||||
|
||||
assert!(Fp::from_bytes(&[0xff; 48]).is_none().unwrap_u8() == 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sqrt() {
|
||||
// a = 4
|
||||
let a = Fp::from_raw_unchecked([
|
||||
0xaa27_0000_000c_fff3,
|
||||
0x53cc_0032_fc34_000a,
|
||||
0x478f_e97a_6b0a_807f,
|
||||
0xb1d3_7ebe_e6ba_24d7,
|
||||
0x8ec9_733b_bf78_ab2f,
|
||||
0x09d6_4551_3d83_de7e,
|
||||
]);
|
||||
|
||||
assert_eq!(
|
||||
// sqrt(4) = -2
|
||||
-a.sqrt().unwrap(),
|
||||
// 2
|
||||
Fp::from_raw_unchecked([
|
||||
0x3213_0000_0006_554f,
|
||||
0xb93c_0018_d6c4_0005,
|
||||
0x5760_5e0d_b0dd_bb51,
|
||||
0x8b25_6521_ed1f_9bcb,
|
||||
0x6cf2_8d79_0162_2c03,
|
||||
0x11eb_ab9d_bb81_e28c,
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_inversion() {
|
||||
let a = Fp([
|
||||
0x43b4_3a50_78ac_2076,
|
||||
0x1ce0_7630_46f8_962b,
|
||||
0x724a_5276_486d_735c,
|
||||
0x6f05_c2a6_282d_48fd,
|
||||
0x2095_bd5b_b4ca_9331,
|
||||
0x03b3_5b38_94b0_f7da,
|
||||
]);
|
||||
let b = Fp([
|
||||
0x69ec_d704_0952_148f,
|
||||
0x985c_cc20_2219_0f55,
|
||||
0xe19b_ba36_a9ad_2f41,
|
||||
0x19bb_16c9_5219_dbd8,
|
||||
0x14dc_acfd_fb47_8693,
|
||||
0x115f_f58a_fff9_a8e1,
|
||||
]);
|
||||
|
||||
assert_eq!(a.invert().unwrap(), b);
|
||||
assert!(Fp::zero().invert().is_none().unwrap_u8() == 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lexicographic_largest() {
|
||||
assert!(!bool::from(Fp::zero().lexicographically_largest()));
|
||||
assert!(!bool::from(Fp::one().lexicographically_largest()));
|
||||
assert!(!bool::from(
|
||||
Fp::from_raw_unchecked([
|
||||
0xa1fa_ffff_fffe_5557,
|
||||
0x995b_fff9_76a3_fffe,
|
||||
0x03f4_1d24_d174_ceb4,
|
||||
0xf654_7998_c199_5dbd,
|
||||
0x778a_468f_507a_6034,
|
||||
0x0205_5993_1f7f_8103
|
||||
])
|
||||
.lexicographically_largest()
|
||||
));
|
||||
assert!(bool::from(
|
||||
Fp::from_raw_unchecked([
|
||||
0x1804_0000_0001_5554,
|
||||
0x8550_0005_3ab0_0001,
|
||||
0x633c_b57c_253c_276f,
|
||||
0x6e22_d1ec_31eb_b502,
|
||||
0xd391_6126_f2d1_4ca2,
|
||||
0x17fb_b857_1a00_6596,
|
||||
])
|
||||
.lexicographically_largest()
|
||||
));
|
||||
assert!(bool::from(
|
||||
Fp::from_raw_unchecked([
|
||||
0x43f5_ffff_fffc_aaae,
|
||||
0x32b7_fff2_ed47_fffd,
|
||||
0x07e8_3a49_a2e9_9d69,
|
||||
0xeca8_f331_8332_bb7a,
|
||||
0xef14_8d1e_a0f4_c069,
|
||||
0x040a_b326_3eff_0206,
|
||||
])
|
||||
.lexicographically_largest()
|
||||
));
|
||||
}
|
635
bls12_381/src/fp12.rs
Normal file
635
bls12_381/src/fp12.rs
Normal file
@ -0,0 +1,635 @@
|
||||
use crate::fp::*;
|
||||
use crate::fp2::*;
|
||||
use crate::fp6::*;
|
||||
|
||||
use core::fmt;
|
||||
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
|
||||
|
||||
/// This represents an element $c_0 + c_1 w$ of $\mathbb{F}_{p^12} = \mathbb{F}_{p^6} / w^2 - v$.
|
||||
pub struct Fp12 {
|
||||
pub c0: Fp6,
|
||||
pub c1: Fp6,
|
||||
}
|
||||
|
||||
impl From<Fp> for Fp12 {
|
||||
fn from(f: Fp) -> Fp12 {
|
||||
Fp12 {
|
||||
c0: Fp6::from(f),
|
||||
c1: Fp6::zero(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Fp2> for Fp12 {
|
||||
fn from(f: Fp2) -> Fp12 {
|
||||
Fp12 {
|
||||
c0: Fp6::from(f),
|
||||
c1: Fp6::zero(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Fp6> for Fp12 {
|
||||
fn from(f: Fp6) -> Fp12 {
|
||||
Fp12 {
|
||||
c0: f,
|
||||
c1: Fp6::zero(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Fp12 {
|
||||
fn eq(&self, other: &Fp12) -> bool {
|
||||
self.ct_eq(other).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Copy for Fp12 {}
|
||||
impl Clone for Fp12 {
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Fp12 {
|
||||
fn default() -> Self {
|
||||
Fp12::zero()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Fp12 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:?} + ({:?})*w", self.c0, self.c1)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConditionallySelectable for Fp12 {
|
||||
#[inline(always)]
|
||||
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
|
||||
Fp12 {
|
||||
c0: Fp6::conditional_select(&a.c0, &b.c0, choice),
|
||||
c1: Fp6::conditional_select(&a.c1, &b.c1, choice),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConstantTimeEq for Fp12 {
|
||||
#[inline(always)]
|
||||
fn ct_eq(&self, other: &Self) -> Choice {
|
||||
self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1)
|
||||
}
|
||||
}
|
||||
|
||||
impl Fp12 {
|
||||
#[inline]
|
||||
pub fn zero() -> Self {
|
||||
Fp12 {
|
||||
c0: Fp6::zero(),
|
||||
c1: Fp6::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn one() -> Self {
|
||||
Fp12 {
|
||||
c0: Fp6::one(),
|
||||
c1: Fp6::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mul_by_014(&self, c0: &Fp2, c1: &Fp2, c4: &Fp2) -> Fp12 {
|
||||
let aa = self.c0.mul_by_01(c0, c1);
|
||||
let bb = self.c1.mul_by_1(c4);
|
||||
let o = c1 + c4;
|
||||
let c1 = self.c1 + self.c0;
|
||||
let c1 = c1.mul_by_01(c0, &o);
|
||||
let c1 = c1 - aa - bb;
|
||||
let c0 = bb;
|
||||
let c0 = c0.mul_by_nonresidue();
|
||||
let c0 = c0 + aa;
|
||||
|
||||
Fp12 { c0, c1 }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn is_zero(&self) -> Choice {
|
||||
self.c0.is_zero() & self.c1.is_zero()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn conjugate(&self) -> Self {
|
||||
Fp12 {
|
||||
c0: self.c0,
|
||||
c1: -self.c1,
|
||||
}
|
||||
}
|
||||
|
||||
/// Raises this element to p.
|
||||
#[inline(always)]
|
||||
pub fn frobenius_map(&self) -> Self {
|
||||
let c0 = self.c0.frobenius_map();
|
||||
let c1 = self.c1.frobenius_map();
|
||||
|
||||
// c1 = c1 * (u + 1)^((p - 1) / 6)
|
||||
let c1 = c1
|
||||
* Fp6::from(Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x0708_9552_b319_d465,
|
||||
0xc669_5f92_b50a_8313,
|
||||
0x97e8_3ccc_d117_228f,
|
||||
0xa35b_aeca_b2dc_29ee,
|
||||
0x1ce3_93ea_5daa_ce4d,
|
||||
0x08f2_220f_b0fb_66eb,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xb2f6_6aad_4ce5_d646,
|
||||
0x5842_a06b_fc49_7cec,
|
||||
0xcf48_95d4_2599_d394,
|
||||
0xc11b_9cba_40a8_e8d0,
|
||||
0x2e38_13cb_e5a0_de89,
|
||||
0x110e_efda_8884_7faf,
|
||||
]),
|
||||
});
|
||||
|
||||
Fp12 { c0, c1 }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn square(&self) -> Self {
|
||||
let ab = self.c0 * self.c1;
|
||||
let c0c1 = self.c0 + self.c1;
|
||||
let c0 = self.c1.mul_by_nonresidue();
|
||||
let c0 = c0 + self.c0;
|
||||
let c0 = c0 * c0c1;
|
||||
let c0 = c0 - ab;
|
||||
let c1 = ab + ab;
|
||||
let c0 = c0 - ab.mul_by_nonresidue();
|
||||
|
||||
Fp12 { c0, c1 }
|
||||
}
|
||||
|
||||
pub fn invert(&self) -> CtOption<Self> {
|
||||
(self.c0.square() - self.c1.square().mul_by_nonresidue())
|
||||
.invert()
|
||||
.map(|t| Fp12 {
|
||||
c0: self.c0 * t,
|
||||
c1: self.c1 * -t,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Mul<&'b Fp12> for &'a Fp12 {
|
||||
type Output = Fp12;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, other: &'b Fp12) -> Self::Output {
|
||||
let aa = self.c0 * other.c0;
|
||||
let bb = self.c1 * other.c1;
|
||||
let o = other.c0 + other.c1;
|
||||
let c1 = self.c1 + self.c0;
|
||||
let c1 = c1 * o;
|
||||
let c1 = c1 - aa;
|
||||
let c1 = c1 - bb;
|
||||
let c0 = bb.mul_by_nonresidue();
|
||||
let c0 = c0 + aa;
|
||||
|
||||
Fp12 { c0, c1 }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Add<&'b Fp12> for &'a Fp12 {
|
||||
type Output = Fp12;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: &'b Fp12) -> Self::Output {
|
||||
Fp12 {
|
||||
c0: self.c0 + rhs.c0,
|
||||
c1: self.c1 + rhs.c1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Neg for &'a Fp12 {
|
||||
type Output = Fp12;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Self::Output {
|
||||
Fp12 {
|
||||
c0: -self.c0,
|
||||
c1: -self.c1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for Fp12 {
|
||||
type Output = Fp12;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Self::Output {
|
||||
-&self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Sub<&'b Fp12> for &'a Fp12 {
|
||||
type Output = Fp12;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: &'b Fp12) -> Self::Output {
|
||||
Fp12 {
|
||||
c0: self.c0 - rhs.c0,
|
||||
c1: self.c1 - rhs.c1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_binops_additive!(Fp12, Fp12);
|
||||
impl_binops_multiplicative!(Fp12, Fp12);
|
||||
|
||||
#[test]
|
||||
fn test_arithmetic() {
|
||||
use crate::fp::*;
|
||||
use crate::fp2::*;
|
||||
|
||||
let a = Fp12 {
|
||||
c0: Fp6 {
|
||||
c0: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x47f9_cb98_b1b8_2d58,
|
||||
0x5fe9_11eb_a3aa_1d9d,
|
||||
0x96bf_1b5f_4dd8_1db3,
|
||||
0x8100_d27c_c925_9f5b,
|
||||
0xafa2_0b96_7464_0eab,
|
||||
0x09bb_cea7_d8d9_497d,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x0303_cb98_b166_2daa,
|
||||
0xd931_10aa_0a62_1d5a,
|
||||
0xbfa9_820c_5be4_a468,
|
||||
0x0ba3_643e_cb05_a348,
|
||||
0xdc35_34bb_1f1c_25a6,
|
||||
0x06c3_05bb_19c0_e1c1,
|
||||
]),
|
||||
},
|
||||
c1: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x46f9_cb98_b162_d858,
|
||||
0x0be9_109c_f7aa_1d57,
|
||||
0xc791_bc55_fece_41d2,
|
||||
0xf84c_5770_4e38_5ec2,
|
||||
0xcb49_c1d9_c010_e60f,
|
||||
0x0acd_b8e1_58bf_e3c8,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x8aef_cb98_b15f_8306,
|
||||
0x3ea1_108f_e4f2_1d54,
|
||||
0xcf79_f69f_a1b7_df3b,
|
||||
0xe4f5_4aa1_d16b_1a3c,
|
||||
0xba5e_4ef8_6105_a679,
|
||||
0x0ed8_6c07_97be_e5cf,
|
||||
]),
|
||||
},
|
||||
c2: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xcee5_cb98_b15c_2db4,
|
||||
0x7159_1082_d23a_1d51,
|
||||
0xd762_30e9_44a1_7ca4,
|
||||
0xd19e_3dd3_549d_d5b6,
|
||||
0xa972_dc17_01fa_66e3,
|
||||
0x12e3_1f2d_d6bd_e7d6,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xad2a_cb98_b173_2d9d,
|
||||
0x2cfd_10dd_0696_1d64,
|
||||
0x0739_6b86_c6ef_24e8,
|
||||
0xbd76_e2fd_b1bf_c820,
|
||||
0x6afe_a7f6_de94_d0d5,
|
||||
0x1099_4b0c_5744_c040,
|
||||
]),
|
||||
},
|
||||
},
|
||||
c1: Fp6 {
|
||||
c0: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x47f9_cb98_b1b8_2d58,
|
||||
0x5fe9_11eb_a3aa_1d9d,
|
||||
0x96bf_1b5f_4dd8_1db3,
|
||||
0x8100_d27c_c925_9f5b,
|
||||
0xafa2_0b96_7464_0eab,
|
||||
0x09bb_cea7_d8d9_497d,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x0303_cb98_b166_2daa,
|
||||
0xd931_10aa_0a62_1d5a,
|
||||
0xbfa9_820c_5be4_a468,
|
||||
0x0ba3_643e_cb05_a348,
|
||||
0xdc35_34bb_1f1c_25a6,
|
||||
0x06c3_05bb_19c0_e1c1,
|
||||
]),
|
||||
},
|
||||
c1: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x46f9_cb98_b162_d858,
|
||||
0x0be9_109c_f7aa_1d57,
|
||||
0xc791_bc55_fece_41d2,
|
||||
0xf84c_5770_4e38_5ec2,
|
||||
0xcb49_c1d9_c010_e60f,
|
||||
0x0acd_b8e1_58bf_e3c8,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x8aef_cb98_b15f_8306,
|
||||
0x3ea1_108f_e4f2_1d54,
|
||||
0xcf79_f69f_a1b7_df3b,
|
||||
0xe4f5_4aa1_d16b_1a3c,
|
||||
0xba5e_4ef8_6105_a679,
|
||||
0x0ed8_6c07_97be_e5cf,
|
||||
]),
|
||||
},
|
||||
c2: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xcee5_cb98_b15c_2db4,
|
||||
0x7159_1082_d23a_1d51,
|
||||
0xd762_30e9_44a1_7ca4,
|
||||
0xd19e_3dd3_549d_d5b6,
|
||||
0xa972_dc17_01fa_66e3,
|
||||
0x12e3_1f2d_d6bd_e7d6,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xad2a_cb98_b173_2d9d,
|
||||
0x2cfd_10dd_0696_1d64,
|
||||
0x0739_6b86_c6ef_24e8,
|
||||
0xbd76_e2fd_b1bf_c820,
|
||||
0x6afe_a7f6_de94_d0d5,
|
||||
0x1099_4b0c_5744_c040,
|
||||
]),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
let b = Fp12 {
|
||||
c0: Fp6 {
|
||||
c0: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x47f9_cb98_b1b8_2d58,
|
||||
0x5fe9_11eb_a3aa_1d9d,
|
||||
0x96bf_1b5f_4dd8_1db3,
|
||||
0x8100_d272_c925_9f5b,
|
||||
0xafa2_0b96_7464_0eab,
|
||||
0x09bb_cea7_d8d9_497d,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x0303_cb98_b166_2daa,
|
||||
0xd931_10aa_0a62_1d5a,
|
||||
0xbfa9_820c_5be4_a468,
|
||||
0x0ba3_643e_cb05_a348,
|
||||
0xdc35_34bb_1f1c_25a6,
|
||||
0x06c3_05bb_19c0_e1c1,
|
||||
]),
|
||||
},
|
||||
c1: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x46f9_cb98_b162_d858,
|
||||
0x0be9_109c_f7aa_1d57,
|
||||
0xc791_bc55_fece_41d2,
|
||||
0xf84c_5770_4e38_5ec2,
|
||||
0xcb49_c1d9_c010_e60f,
|
||||
0x0acd_b8e1_58bf_e348,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x8aef_cb98_b15f_8306,
|
||||
0x3ea1_108f_e4f2_1d54,
|
||||
0xcf79_f69f_a1b7_df3b,
|
||||
0xe4f5_4aa1_d16b_1a3c,
|
||||
0xba5e_4ef8_6105_a679,
|
||||
0x0ed8_6c07_97be_e5cf,
|
||||
]),
|
||||
},
|
||||
c2: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xcee5_cb98_b15c_2db4,
|
||||
0x7159_1082_d23a_1d51,
|
||||
0xd762_30e9_44a1_7ca4,
|
||||
0xd19e_3dd3_549d_d5b6,
|
||||
0xa972_dc17_01fa_66e3,
|
||||
0x12e3_1f2d_d6bd_e7d6,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xad2a_cb98_b173_2d9d,
|
||||
0x2cfd_10dd_0696_1d64,
|
||||
0x0739_6b86_c6ef_24e8,
|
||||
0xbd76_e2fd_b1bf_c820,
|
||||
0x6afe_a7f6_de94_d0d5,
|
||||
0x1099_4b0c_5744_c040,
|
||||
]),
|
||||
},
|
||||
},
|
||||
c1: Fp6 {
|
||||
c0: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x47f9_cb98_b1b8_2d58,
|
||||
0x5fe9_11eb_a3aa_1d9d,
|
||||
0x96bf_1b5f_4dd2_1db3,
|
||||
0x8100_d27c_c925_9f5b,
|
||||
0xafa2_0b96_7464_0eab,
|
||||
0x09bb_cea7_d8d9_497d,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x0303_cb98_b166_2daa,
|
||||
0xd931_10aa_0a62_1d5a,
|
||||
0xbfa9_820c_5be4_a468,
|
||||
0x0ba3_643e_cb05_a348,
|
||||
0xdc35_34bb_1f1c_25a6,
|
||||
0x06c3_05bb_19c0_e1c1,
|
||||
]),
|
||||
},
|
||||
c1: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x46f9_cb98_b162_d858,
|
||||
0x0be9_109c_f7aa_1d57,
|
||||
0xc791_bc55_fece_41d2,
|
||||
0xf84c_5770_4e38_5ec2,
|
||||
0xcb49_c1d9_c010_e60f,
|
||||
0x0acd_b8e1_58bf_e3c8,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x8aef_cb98_b15f_8306,
|
||||
0x3ea1_108f_e4f2_1d54,
|
||||
0xcf79_f69f_a117_df3b,
|
||||
0xe4f5_4aa1_d16b_1a3c,
|
||||
0xba5e_4ef8_6105_a679,
|
||||
0x0ed8_6c07_97be_e5cf,
|
||||
]),
|
||||
},
|
||||
c2: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xcee5_cb98_b15c_2db4,
|
||||
0x7159_1082_d23a_1d51,
|
||||
0xd762_30e9_44a1_7ca4,
|
||||
0xd19e_3dd3_549d_d5b6,
|
||||
0xa972_dc17_01fa_66e3,
|
||||
0x12e3_1f2d_d6bd_e7d6,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xad2a_cb98_b173_2d9d,
|
||||
0x2cfd_10dd_0696_1d64,
|
||||
0x0739_6b86_c6ef_24e8,
|
||||
0xbd76_e2fd_b1bf_c820,
|
||||
0x6afe_a7f6_de94_d0d5,
|
||||
0x1099_4b0c_5744_c040,
|
||||
]),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
let c = Fp12 {
|
||||
c0: Fp6 {
|
||||
c0: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x47f9_cb98_71b8_2d58,
|
||||
0x5fe9_11eb_a3aa_1d9d,
|
||||
0x96bf_1b5f_4dd8_1db3,
|
||||
0x8100_d27c_c925_9f5b,
|
||||
0xafa2_0b96_7464_0eab,
|
||||
0x09bb_cea7_d8d9_497d,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x0303_cb98_b166_2daa,
|
||||
0xd931_10aa_0a62_1d5a,
|
||||
0xbfa9_820c_5be4_a468,
|
||||
0x0ba3_643e_cb05_a348,
|
||||
0xdc35_34bb_1f1c_25a6,
|
||||
0x06c3_05bb_19c0_e1c1,
|
||||
]),
|
||||
},
|
||||
c1: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x46f9_cb98_b162_d858,
|
||||
0x0be9_109c_f7aa_1d57,
|
||||
0x7791_bc55_fece_41d2,
|
||||
0xf84c_5770_4e38_5ec2,
|
||||
0xcb49_c1d9_c010_e60f,
|
||||
0x0acd_b8e1_58bf_e3c8,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x8aef_cb98_b15f_8306,
|
||||
0x3ea1_108f_e4f2_1d54,
|
||||
0xcf79_f69f_a1b7_df3b,
|
||||
0xe4f5_4aa1_d16b_133c,
|
||||
0xba5e_4ef8_6105_a679,
|
||||
0x0ed8_6c07_97be_e5cf,
|
||||
]),
|
||||
},
|
||||
c2: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xcee5_cb98_b15c_2db4,
|
||||
0x7159_1082_d23a_1d51,
|
||||
0xd762_40e9_44a1_7ca4,
|
||||
0xd19e_3dd3_549d_d5b6,
|
||||
0xa972_dc17_01fa_66e3,
|
||||
0x12e3_1f2d_d6bd_e7d6,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xad2a_cb98_b173_2d9d,
|
||||
0x2cfd_10dd_0696_1d64,
|
||||
0x0739_6b86_c6ef_24e8,
|
||||
0xbd76_e2fd_b1bf_c820,
|
||||
0x6afe_a7f6_de94_d0d5,
|
||||
0x1099_4b0c_1744_c040,
|
||||
]),
|
||||
},
|
||||
},
|
||||
c1: Fp6 {
|
||||
c0: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x47f9_cb98_b1b8_2d58,
|
||||
0x5fe9_11eb_a3aa_1d9d,
|
||||
0x96bf_1b5f_4dd8_1db3,
|
||||
0x8100_d27c_c925_9f5b,
|
||||
0xafa2_0b96_7464_0eab,
|
||||
0x09bb_cea7_d8d9_497d,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x0303_cb98_b166_2daa,
|
||||
0xd931_10aa_0a62_1d5a,
|
||||
0xbfa9_820c_5be4_a468,
|
||||
0x0ba3_643e_cb05_a348,
|
||||
0xdc35_34bb_1f1c_25a6,
|
||||
0x06c3_05bb_19c0_e1c1,
|
||||
]),
|
||||
},
|
||||
c1: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x46f9_cb98_b162_d858,
|
||||
0x0be9_109c_f7aa_1d57,
|
||||
0xc791_bc55_fece_41d2,
|
||||
0xf84c_5770_4e38_5ec2,
|
||||
0xcb49_c1d3_c010_e60f,
|
||||
0x0acd_b8e1_58bf_e3c8,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x8aef_cb98_b15f_8306,
|
||||
0x3ea1_108f_e4f2_1d54,
|
||||
0xcf79_f69f_a1b7_df3b,
|
||||
0xe4f5_4aa1_d16b_1a3c,
|
||||
0xba5e_4ef8_6105_a679,
|
||||
0x0ed8_6c07_97be_e5cf,
|
||||
]),
|
||||
},
|
||||
c2: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xcee5_cb98_b15c_2db4,
|
||||
0x7159_1082_d23a_1d51,
|
||||
0xd762_30e9_44a1_7ca4,
|
||||
0xd19e_3dd3_549d_d5b6,
|
||||
0xa972_dc17_01fa_66e3,
|
||||
0x12e3_1f2d_d6bd_e7d6,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xad2a_cb98_b173_2d9d,
|
||||
0x2cfd_10dd_0696_1d64,
|
||||
0x0739_6b86_c6ef_24e8,
|
||||
0xbd76_e2fd_b1bf_c820,
|
||||
0x6afe_a7f6_de94_d0d5,
|
||||
0x1099_4b0c_5744_1040,
|
||||
]),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// because a and b and c are similar to each other and
|
||||
// I was lazy, this is just some arbitrary way to make
|
||||
// them a little more different
|
||||
let a = a.square().invert().unwrap().square() + c;
|
||||
let b = b.square().invert().unwrap().square() + a;
|
||||
let c = c.square().invert().unwrap().square() + b;
|
||||
|
||||
assert_eq!(a.square(), a * a);
|
||||
assert_eq!(b.square(), b * b);
|
||||
assert_eq!(c.square(), c * c);
|
||||
|
||||
assert_eq!((a + b) * c.square(), (c * c * a) + (c * c * b));
|
||||
|
||||
assert_eq!(
|
||||
a.invert().unwrap() * b.invert().unwrap(),
|
||||
(a * b).invert().unwrap()
|
||||
);
|
||||
assert_eq!(a.invert().unwrap() * a, Fp12::one());
|
||||
|
||||
assert!(a != a.frobenius_map());
|
||||
assert_eq!(
|
||||
a,
|
||||
a.frobenius_map()
|
||||
.frobenius_map()
|
||||
.frobenius_map()
|
||||
.frobenius_map()
|
||||
.frobenius_map()
|
||||
.frobenius_map()
|
||||
.frobenius_map()
|
||||
.frobenius_map()
|
||||
.frobenius_map()
|
||||
.frobenius_map()
|
||||
.frobenius_map()
|
||||
.frobenius_map()
|
||||
);
|
||||
}
|
868
bls12_381/src/fp2.rs
Normal file
868
bls12_381/src/fp2.rs
Normal file
@ -0,0 +1,868 @@
|
||||
//! This module implements arithmetic over the quadratic extension field Fp2.
|
||||
|
||||
use core::fmt;
|
||||
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
||||
|
||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
|
||||
|
||||
use crate::fp::Fp;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Fp2 {
|
||||
pub c0: Fp,
|
||||
pub c1: Fp,
|
||||
}
|
||||
|
||||
impl fmt::Debug for Fp2 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:?} + {:?}*u", self.c0, self.c1)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Fp2 {
|
||||
fn default() -> Self {
|
||||
Fp2::zero()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Fp> for Fp2 {
|
||||
fn from(f: Fp) -> Fp2 {
|
||||
Fp2 {
|
||||
c0: f,
|
||||
c1: Fp::zero(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConstantTimeEq for Fp2 {
|
||||
fn ct_eq(&self, other: &Self) -> Choice {
|
||||
self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1)
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Fp2 {}
|
||||
impl PartialEq for Fp2 {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.ct_eq(other).unwrap_u8() == 1
|
||||
}
|
||||
}
|
||||
|
||||
impl ConditionallySelectable for Fp2 {
|
||||
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
|
||||
Fp2 {
|
||||
c0: Fp::conditional_select(&a.c0, &b.c0, choice),
|
||||
c1: Fp::conditional_select(&a.c1, &b.c1, choice),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Neg for &'a Fp2 {
|
||||
type Output = Fp2;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Fp2 {
|
||||
self.neg()
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for Fp2 {
|
||||
type Output = Fp2;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Fp2 {
|
||||
-&self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Sub<&'b Fp2> for &'a Fp2 {
|
||||
type Output = Fp2;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: &'b Fp2) -> Fp2 {
|
||||
self.sub(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Add<&'b Fp2> for &'a Fp2 {
|
||||
type Output = Fp2;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: &'b Fp2) -> Fp2 {
|
||||
self.add(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Mul<&'b Fp2> for &'a Fp2 {
|
||||
type Output = Fp2;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, rhs: &'b Fp2) -> Fp2 {
|
||||
self.mul(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl_binops_additive!(Fp2, Fp2);
|
||||
impl_binops_multiplicative!(Fp2, Fp2);
|
||||
|
||||
impl Fp2 {
|
||||
#[inline]
|
||||
pub const fn zero() -> Fp2 {
|
||||
Fp2 {
|
||||
c0: Fp::zero(),
|
||||
c1: Fp::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn one() -> Fp2 {
|
||||
Fp2 {
|
||||
c0: Fp::one(),
|
||||
c1: Fp::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_zero(&self) -> Choice {
|
||||
self.c0.is_zero() & self.c1.is_zero()
|
||||
}
|
||||
|
||||
/// Raises this element to p.
|
||||
#[inline(always)]
|
||||
pub fn frobenius_map(&self) -> Self {
|
||||
// This is always just a conjugation. If you're curious why, here's
|
||||
// an article about it: https://alicebob.cryptoland.net/the-frobenius-endomorphism-with-finite-fields/
|
||||
self.conjugate()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn conjugate(&self) -> Self {
|
||||
Fp2 {
|
||||
c0: self.c0,
|
||||
c1: -self.c1,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn mul_by_nonresidue(&self) -> Fp2 {
|
||||
// Multiply a + bu by u + 1, getting
|
||||
// au + a + bu^2 + bu
|
||||
// and because u^2 = -1, we get
|
||||
// (a - b) + (a + b)u
|
||||
|
||||
Fp2 {
|
||||
c0: self.c0 - self.c1,
|
||||
c1: self.c0 + self.c1,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether or not this element is strictly lexicographically
|
||||
/// larger than its negation.
|
||||
#[inline]
|
||||
pub fn lexicographically_largest(&self) -> Choice {
|
||||
// If this element's c1 coefficient is lexicographically largest
|
||||
// then it is lexicographically largest. Otherwise, in the event
|
||||
// the c1 coefficient is zero and the c0 coefficient is
|
||||
// lexicographically largest, then this element is lexicographically
|
||||
// largest.
|
||||
|
||||
self.c1.lexicographically_largest()
|
||||
| (self.c1.is_zero() & self.c0.lexicographically_largest())
|
||||
}
|
||||
|
||||
pub const fn square(&self) -> Fp2 {
|
||||
// Complex squaring:
|
||||
//
|
||||
// v0 = c0 * c1
|
||||
// c0' = (c0 + c1) * (c0 + \beta*c1) - v0 - \beta * v0
|
||||
// c1' = 2 * v0
|
||||
//
|
||||
// In BLS12-381's F_{p^2}, our \beta is -1 so we
|
||||
// can modify this formula:
|
||||
//
|
||||
// c0' = (c0 + c1) * (c0 - c1)
|
||||
// c1' = 2 * c0 * c1
|
||||
|
||||
let a = (&self.c0).add(&self.c1);
|
||||
let b = (&self.c0).sub(&self.c1);
|
||||
let c = (&self.c0).add(&self.c0);
|
||||
|
||||
Fp2 {
|
||||
c0: (&a).mul(&b),
|
||||
c1: (&c).mul(&self.c1),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn mul(&self, rhs: &Fp2) -> Fp2 {
|
||||
// Karatsuba multiplication:
|
||||
//
|
||||
// v0 = a0 * b0
|
||||
// v1 = a1 * b1
|
||||
// c0 = v0 + \beta * v1
|
||||
// c1 = (a0 + a1) * (b0 + b1) - v0 - v1
|
||||
//
|
||||
// In BLS12-381's F_{p^2}, our \beta is -1 so we
|
||||
// can modify this formula. (Also, since we always
|
||||
// subtract v1, we can compute v1 = -a1 * b1.)
|
||||
//
|
||||
// v0 = a0 * b0
|
||||
// v1 = (-a1) * b1
|
||||
// c0 = v0 + v1
|
||||
// c1 = (a0 + a1) * (b0 + b1) - v0 + v1
|
||||
|
||||
let v0 = (&self.c0).mul(&rhs.c0);
|
||||
let v1 = (&(&self.c1).neg()).mul(&rhs.c1);
|
||||
let c0 = (&v0).add(&v1);
|
||||
let c1 = (&(&self.c0).add(&self.c1)).mul(&(&rhs.c0).add(&rhs.c1));
|
||||
let c1 = (&c1).sub(&v0);
|
||||
let c1 = (&c1).add(&v1);
|
||||
|
||||
Fp2 { c0, c1 }
|
||||
}
|
||||
|
||||
pub const fn add(&self, rhs: &Fp2) -> Fp2 {
|
||||
Fp2 {
|
||||
c0: (&self.c0).add(&rhs.c0),
|
||||
c1: (&self.c1).add(&rhs.c1),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn sub(&self, rhs: &Fp2) -> Fp2 {
|
||||
Fp2 {
|
||||
c0: (&self.c0).sub(&rhs.c0),
|
||||
c1: (&self.c1).sub(&rhs.c1),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn neg(&self) -> Fp2 {
|
||||
Fp2 {
|
||||
c0: (&self.c0).neg(),
|
||||
c1: (&self.c1).neg(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sqrt(&self) -> CtOption<Self> {
|
||||
// Algorithm 9, https://eprint.iacr.org/2012/685.pdf
|
||||
// with constant time modifications.
|
||||
|
||||
CtOption::new(Fp2::zero(), self.is_zero()).or_else(|| {
|
||||
// a1 = self^((p - 3) / 4)
|
||||
let a1 = self.pow_vartime(&[
|
||||
0xee7f_bfff_ffff_eaaa,
|
||||
0x07aa_ffff_ac54_ffff,
|
||||
0xd9cc_34a8_3dac_3d89,
|
||||
0xd91d_d2e1_3ce1_44af,
|
||||
0x92c6_e9ed_90d2_eb35,
|
||||
0x0680_447a_8e5f_f9a6,
|
||||
]);
|
||||
|
||||
// alpha = a1^2 * self = self^((p - 3) / 2 + 1) = self^((p - 1) / 2)
|
||||
let alpha = a1.square() * self;
|
||||
|
||||
// x0 = self^((p + 1) / 4)
|
||||
let x0 = a1 * self;
|
||||
|
||||
// In the event that alpha = -1, the element is order p - 1 and so
|
||||
// we're just trying to get the square of an element of the subfield
|
||||
// Fp. This is given by x0 * u, since u = sqrt(-1). Since the element
|
||||
// x0 = a + bu has b = 0, the solution is therefore au.
|
||||
CtOption::new(
|
||||
Fp2 {
|
||||
c0: -x0.c1,
|
||||
c1: x0.c0,
|
||||
},
|
||||
alpha.ct_eq(&(&Fp2::one()).neg()),
|
||||
)
|
||||
// Otherwise, the correct solution is (1 + alpha)^((q - 1) // 2) * x0
|
||||
.or_else(|| {
|
||||
CtOption::new(
|
||||
(alpha + Fp2::one()).pow_vartime(&[
|
||||
0xdcff_7fff_ffff_d555,
|
||||
0x0f55_ffff_58a9_ffff,
|
||||
0xb398_6950_7b58_7b12,
|
||||
0xb23b_a5c2_79c2_895f,
|
||||
0x258d_d3db_21a5_d66b,
|
||||
0x0d00_88f5_1cbf_f34d,
|
||||
]) * x0,
|
||||
Choice::from(1),
|
||||
)
|
||||
})
|
||||
// Only return the result if it's really the square root (and so
|
||||
// self is actually quadratic nonresidue)
|
||||
.and_then(|sqrt| CtOption::new(sqrt, sqrt.square().ct_eq(self)))
|
||||
})
|
||||
}
|
||||
|
||||
/// Computes the multiplicative inverse of this field
|
||||
/// element, returning None in the case that this element
|
||||
/// is zero.
|
||||
pub fn invert(&self) -> CtOption<Self> {
|
||||
// We wish to find the multiplicative inverse of a nonzero
|
||||
// element a + bu in Fp2. We leverage an identity
|
||||
//
|
||||
// (a + bu)(a - bu) = a^2 + b^2
|
||||
//
|
||||
// which holds because u^2 = -1. This can be rewritten as
|
||||
//
|
||||
// (a + bu)(a - bu)/(a^2 + b^2) = 1
|
||||
//
|
||||
// because a^2 + b^2 = 0 has no nonzero solutions for (a, b).
|
||||
// This gives that (a - bu)/(a^2 + b^2) is the inverse
|
||||
// of (a + bu). Importantly, this can be computing using
|
||||
// only a single inversion in Fp.
|
||||
|
||||
(self.c0.square() + self.c1.square()).invert().map(|t| Fp2 {
|
||||
c0: self.c0 * t,
|
||||
c1: self.c1 * -t,
|
||||
})
|
||||
}
|
||||
|
||||
/// Although this is labeled "vartime", it is only
|
||||
/// variable time with respect to the exponent. It
|
||||
/// is also not exposed in the public API.
|
||||
pub fn pow_vartime(&self, by: &[u64; 6]) -> Self {
|
||||
let mut res = Self::one();
|
||||
for e in by.iter().rev() {
|
||||
for i in (0..64).rev() {
|
||||
res = res.square();
|
||||
|
||||
if ((*e >> i) & 1) == 1 {
|
||||
res *= self;
|
||||
}
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_conditional_selection() {
|
||||
let a = Fp2 {
|
||||
c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]),
|
||||
c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]),
|
||||
};
|
||||
let b = Fp2 {
|
||||
c0: Fp::from_raw_unchecked([13, 14, 15, 16, 17, 18]),
|
||||
c1: Fp::from_raw_unchecked([19, 20, 21, 22, 23, 24]),
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
ConditionallySelectable::conditional_select(&a, &b, Choice::from(0u8)),
|
||||
a
|
||||
);
|
||||
assert_eq!(
|
||||
ConditionallySelectable::conditional_select(&a, &b, Choice::from(1u8)),
|
||||
b
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_equality() {
|
||||
fn is_equal(a: &Fp2, b: &Fp2) -> bool {
|
||||
let eq = a == b;
|
||||
let ct_eq = a.ct_eq(&b);
|
||||
|
||||
assert_eq!(eq, ct_eq.unwrap_u8() == 1);
|
||||
|
||||
eq
|
||||
}
|
||||
|
||||
assert!(is_equal(
|
||||
&Fp2 {
|
||||
c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]),
|
||||
c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]),
|
||||
},
|
||||
&Fp2 {
|
||||
c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]),
|
||||
c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]),
|
||||
}
|
||||
));
|
||||
|
||||
assert!(!is_equal(
|
||||
&Fp2 {
|
||||
c0: Fp::from_raw_unchecked([2, 2, 3, 4, 5, 6]),
|
||||
c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]),
|
||||
},
|
||||
&Fp2 {
|
||||
c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]),
|
||||
c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]),
|
||||
}
|
||||
));
|
||||
|
||||
assert!(!is_equal(
|
||||
&Fp2 {
|
||||
c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]),
|
||||
c1: Fp::from_raw_unchecked([2, 8, 9, 10, 11, 12]),
|
||||
},
|
||||
&Fp2 {
|
||||
c0: Fp::from_raw_unchecked([1, 2, 3, 4, 5, 6]),
|
||||
c1: Fp::from_raw_unchecked([7, 8, 9, 10, 11, 12]),
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_squaring() {
|
||||
let a = Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xc9a2_1831_63ee_70d4,
|
||||
0xbc37_70a7_196b_5c91,
|
||||
0xa247_f8c1_304c_5f44,
|
||||
0xb01f_c2a3_726c_80b5,
|
||||
0xe1d2_93e5_bbd9_19c9,
|
||||
0x04b7_8e80_020e_f2ca,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x952e_a446_0462_618f,
|
||||
0x238d_5edd_f025_c62f,
|
||||
0xf6c9_4b01_2ea9_2e72,
|
||||
0x03ce_24ea_c1c9_3808,
|
||||
0x0559_50f9_45da_483c,
|
||||
0x010a_768d_0df4_eabc,
|
||||
]),
|
||||
};
|
||||
let b = Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xa1e0_9175_a4d2_c1fe,
|
||||
0x8b33_acfc_204e_ff12,
|
||||
0xe244_15a1_1b45_6e42,
|
||||
0x61d9_96b1_b6ee_1936,
|
||||
0x1164_dbe8_667c_853c,
|
||||
0x0788_557a_cc7d_9c79,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xda6a_87cc_6f48_fa36,
|
||||
0x0fc7_b488_277c_1903,
|
||||
0x9445_ac4a_dc44_8187,
|
||||
0x0261_6d5b_c909_9209,
|
||||
0xdbed_4677_2db5_8d48,
|
||||
0x11b9_4d50_76c7_b7b1,
|
||||
]),
|
||||
};
|
||||
|
||||
assert_eq!(a.square(), b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiplication() {
|
||||
let a = Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xc9a2_1831_63ee_70d4,
|
||||
0xbc37_70a7_196b_5c91,
|
||||
0xa247_f8c1_304c_5f44,
|
||||
0xb01f_c2a3_726c_80b5,
|
||||
0xe1d2_93e5_bbd9_19c9,
|
||||
0x04b7_8e80_020e_f2ca,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x952e_a446_0462_618f,
|
||||
0x238d_5edd_f025_c62f,
|
||||
0xf6c9_4b01_2ea9_2e72,
|
||||
0x03ce_24ea_c1c9_3808,
|
||||
0x0559_50f9_45da_483c,
|
||||
0x010a_768d_0df4_eabc,
|
||||
]),
|
||||
};
|
||||
let b = Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xa1e0_9175_a4d2_c1fe,
|
||||
0x8b33_acfc_204e_ff12,
|
||||
0xe244_15a1_1b45_6e42,
|
||||
0x61d9_96b1_b6ee_1936,
|
||||
0x1164_dbe8_667c_853c,
|
||||
0x0788_557a_cc7d_9c79,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xda6a_87cc_6f48_fa36,
|
||||
0x0fc7_b488_277c_1903,
|
||||
0x9445_ac4a_dc44_8187,
|
||||
0x0261_6d5b_c909_9209,
|
||||
0xdbed_4677_2db5_8d48,
|
||||
0x11b9_4d50_76c7_b7b1,
|
||||
]),
|
||||
};
|
||||
let c = Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xf597_483e_27b4_e0f7,
|
||||
0x610f_badf_811d_ae5f,
|
||||
0x8432_af91_7714_327a,
|
||||
0x6a9a_9603_cf88_f09e,
|
||||
0xf05a_7bf8_bad0_eb01,
|
||||
0x0954_9131_c003_ffae,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x963b_02d0_f93d_37cd,
|
||||
0xc95c_e1cd_b30a_73d4,
|
||||
0x3087_25fa_3126_f9b8,
|
||||
0x56da_3c16_7fab_0d50,
|
||||
0x6b50_86b5_f4b6_d6af,
|
||||
0x09c3_9f06_2f18_e9f2,
|
||||
]),
|
||||
};
|
||||
|
||||
assert_eq!(a * b, c);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_addition() {
|
||||
let a = Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xc9a2_1831_63ee_70d4,
|
||||
0xbc37_70a7_196b_5c91,
|
||||
0xa247_f8c1_304c_5f44,
|
||||
0xb01f_c2a3_726c_80b5,
|
||||
0xe1d2_93e5_bbd9_19c9,
|
||||
0x04b7_8e80_020e_f2ca,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x952e_a446_0462_618f,
|
||||
0x238d_5edd_f025_c62f,
|
||||
0xf6c9_4b01_2ea9_2e72,
|
||||
0x03ce_24ea_c1c9_3808,
|
||||
0x0559_50f9_45da_483c,
|
||||
0x010a_768d_0df4_eabc,
|
||||
]),
|
||||
};
|
||||
let b = Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xa1e0_9175_a4d2_c1fe,
|
||||
0x8b33_acfc_204e_ff12,
|
||||
0xe244_15a1_1b45_6e42,
|
||||
0x61d9_96b1_b6ee_1936,
|
||||
0x1164_dbe8_667c_853c,
|
||||
0x0788_557a_cc7d_9c79,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xda6a_87cc_6f48_fa36,
|
||||
0x0fc7_b488_277c_1903,
|
||||
0x9445_ac4a_dc44_8187,
|
||||
0x0261_6d5b_c909_9209,
|
||||
0xdbed_4677_2db5_8d48,
|
||||
0x11b9_4d50_76c7_b7b1,
|
||||
]),
|
||||
};
|
||||
let c = Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x6b82_a9a7_08c1_32d2,
|
||||
0x476b_1da3_39ba_5ba4,
|
||||
0x848c_0e62_4b91_cd87,
|
||||
0x11f9_5955_295a_99ec,
|
||||
0xf337_6fce_2255_9f06,
|
||||
0x0c3f_e3fa_ce8c_8f43,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x6f99_2c12_73ab_5bc5,
|
||||
0x3355_1366_17a1_df33,
|
||||
0x8b0e_f74c_0aed_aff9,
|
||||
0x062f_9246_8ad2_ca12,
|
||||
0xe146_9770_738f_d584,
|
||||
0x12c3_c3dd_84bc_a26d,
|
||||
]),
|
||||
};
|
||||
|
||||
assert_eq!(a + b, c);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_subtraction() {
|
||||
let a = Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xc9a2_1831_63ee_70d4,
|
||||
0xbc37_70a7_196b_5c91,
|
||||
0xa247_f8c1_304c_5f44,
|
||||
0xb01f_c2a3_726c_80b5,
|
||||
0xe1d2_93e5_bbd9_19c9,
|
||||
0x04b7_8e80_020e_f2ca,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x952e_a446_0462_618f,
|
||||
0x238d_5edd_f025_c62f,
|
||||
0xf6c9_4b01_2ea9_2e72,
|
||||
0x03ce_24ea_c1c9_3808,
|
||||
0x0559_50f9_45da_483c,
|
||||
0x010a_768d_0df4_eabc,
|
||||
]),
|
||||
};
|
||||
let b = Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xa1e0_9175_a4d2_c1fe,
|
||||
0x8b33_acfc_204e_ff12,
|
||||
0xe244_15a1_1b45_6e42,
|
||||
0x61d9_96b1_b6ee_1936,
|
||||
0x1164_dbe8_667c_853c,
|
||||
0x0788_557a_cc7d_9c79,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xda6a_87cc_6f48_fa36,
|
||||
0x0fc7_b488_277c_1903,
|
||||
0x9445_ac4a_dc44_8187,
|
||||
0x0261_6d5b_c909_9209,
|
||||
0xdbed_4677_2db5_8d48,
|
||||
0x11b9_4d50_76c7_b7b1,
|
||||
]),
|
||||
};
|
||||
let c = Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xe1c0_86bb_bf1b_5981,
|
||||
0x4faf_c3a9_aa70_5d7e,
|
||||
0x2734_b5c1_0bb7_e726,
|
||||
0xb2bd_7776_af03_7a3e,
|
||||
0x1b89_5fb3_98a8_4164,
|
||||
0x1730_4aef_6f11_3cec,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x74c3_1c79_9519_1204,
|
||||
0x3271_aa54_79fd_ad2b,
|
||||
0xc9b4_7157_4915_a30f,
|
||||
0x65e4_0313_ec44_b8be,
|
||||
0x7487_b238_5b70_67cb,
|
||||
0x0952_3b26_d0ad_19a4,
|
||||
]),
|
||||
};
|
||||
|
||||
assert_eq!(a - b, c);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_negation() {
|
||||
let a = Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xc9a2_1831_63ee_70d4,
|
||||
0xbc37_70a7_196b_5c91,
|
||||
0xa247_f8c1_304c_5f44,
|
||||
0xb01f_c2a3_726c_80b5,
|
||||
0xe1d2_93e5_bbd9_19c9,
|
||||
0x04b7_8e80_020e_f2ca,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x952e_a446_0462_618f,
|
||||
0x238d_5edd_f025_c62f,
|
||||
0xf6c9_4b01_2ea9_2e72,
|
||||
0x03ce_24ea_c1c9_3808,
|
||||
0x0559_50f9_45da_483c,
|
||||
0x010a_768d_0df4_eabc,
|
||||
]),
|
||||
};
|
||||
let b = Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xf05c_e7ce_9c11_39d7,
|
||||
0x6274_8f57_97e8_a36d,
|
||||
0xc4e8_d9df_c664_96df,
|
||||
0xb457_88e1_8118_9209,
|
||||
0x6949_13d0_8772_930d,
|
||||
0x1549_836a_3770_f3cf,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x24d0_5bb9_fb9d_491c,
|
||||
0xfb1e_a120_c12e_39d0,
|
||||
0x7067_879f_c807_c7b1,
|
||||
0x60a9_269a_31bb_dab6,
|
||||
0x45c2_56bc_fd71_649b,
|
||||
0x18f6_9b5d_2b8a_fbde,
|
||||
]),
|
||||
};
|
||||
|
||||
assert_eq!(-a, b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sqrt() {
|
||||
// a = 1488924004771393321054797166853618474668089414631333405711627789629391903630694737978065425271543178763948256226639*u + 784063022264861764559335808165825052288770346101304131934508881646553551234697082295473567906267937225174620141295
|
||||
let a = Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x2bee_d146_27d7_f9e9,
|
||||
0xb661_4e06_660e_5dce,
|
||||
0x06c4_cc7c_2f91_d42c,
|
||||
0x996d_7847_4b7a_63cc,
|
||||
0xebae_bc4c_820d_574e,
|
||||
0x1886_5e12_d93f_d845,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x7d82_8664_baf4_f566,
|
||||
0xd17e_6639_96ec_7339,
|
||||
0x679e_ad55_cb40_78d0,
|
||||
0xfe3b_2260_e001_ec28,
|
||||
0x3059_93d0_43d9_1b68,
|
||||
0x0626_f03c_0489_b72d,
|
||||
]),
|
||||
};
|
||||
|
||||
assert_eq!(a.sqrt().unwrap().square(), a);
|
||||
|
||||
// b = 5, which is a generator of the p - 1 order
|
||||
// multiplicative subgroup
|
||||
let b = Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x6631_0000_0010_5545,
|
||||
0x2114_0040_0eec_000d,
|
||||
0x3fa7_af30_c820_e316,
|
||||
0xc52a_8b8d_6387_695d,
|
||||
0x9fb4_e61d_1e83_eac5,
|
||||
0x005c_b922_afe8_4dc7,
|
||||
]),
|
||||
c1: Fp::zero(),
|
||||
};
|
||||
|
||||
assert_eq!(b.sqrt().unwrap().square(), b);
|
||||
|
||||
// c = 25, which is a generator of the (p - 1) / 2 order
|
||||
// multiplicative subgroup
|
||||
let c = Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x44f6_0000_0051_ffae,
|
||||
0x86b8_0141_9948_0043,
|
||||
0xd715_9952_f1f3_794a,
|
||||
0x755d_6e3d_fe1f_fc12,
|
||||
0xd36c_d6db_5547_e905,
|
||||
0x02f8_c8ec_bf18_67bb,
|
||||
]),
|
||||
c1: Fp::zero(),
|
||||
};
|
||||
|
||||
assert_eq!(c.sqrt().unwrap().square(), c);
|
||||
|
||||
// 2155129644831861015726826462986972654175647013268275306775721078997042729172900466542651176384766902407257452753362*u + 2796889544896299244102912275102369318775038861758288697415827248356648685135290329705805931514906495247464901062529
|
||||
// is nonsquare.
|
||||
assert!(bool::from(
|
||||
Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xc5fa_1bc8_fd00_d7f6,
|
||||
0x3830_ca45_4606_003b,
|
||||
0x2b28_7f11_04b1_02da,
|
||||
0xa7fb_30f2_8230_f23e,
|
||||
0x339c_db9e_e953_dbf0,
|
||||
0x0d78_ec51_d989_fc57,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x27ec_4898_cf87_f613,
|
||||
0x9de1_394e_1abb_05a5,
|
||||
0x0947_f85d_c170_fc14,
|
||||
0x586f_bc69_6b61_14b7,
|
||||
0x2b34_75a4_077d_7169,
|
||||
0x13e1_c895_cc4b_6c22,
|
||||
])
|
||||
}
|
||||
.sqrt()
|
||||
.is_none()
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_inversion() {
|
||||
let a = Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x1128_ecad_6754_9455,
|
||||
0x9e7a_1cff_3a4e_a1a8,
|
||||
0xeb20_8d51_e08b_cf27,
|
||||
0xe98a_d408_11f5_fc2b,
|
||||
0x736c_3a59_232d_511d,
|
||||
0x10ac_d42d_29cf_cbb6,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xd328_e37c_c2f5_8d41,
|
||||
0x948d_f085_8a60_5869,
|
||||
0x6032_f9d5_6f93_a573,
|
||||
0x2be4_83ef_3fff_dc87,
|
||||
0x30ef_61f8_8f48_3c2a,
|
||||
0x1333_f55a_3572_5be0,
|
||||
]),
|
||||
};
|
||||
|
||||
let b = Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x0581_a133_3d4f_48a6,
|
||||
0x5824_2f6e_f074_8500,
|
||||
0x0292_c955_349e_6da5,
|
||||
0xba37_721d_dd95_fcd0,
|
||||
0x70d1_6790_3aa5_dfc5,
|
||||
0x1189_5e11_8b58_a9d5,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x0eda_09d2_d7a8_5d17,
|
||||
0x8808_e137_a7d1_a2cf,
|
||||
0x43ae_2625_c1ff_21db,
|
||||
0xf85a_c9fd_f7a7_4c64,
|
||||
0x8fcc_dda5_b8da_9738,
|
||||
0x08e8_4f0c_b32c_d17d,
|
||||
]),
|
||||
};
|
||||
|
||||
assert_eq!(a.invert().unwrap(), b);
|
||||
|
||||
assert!(Fp2::zero().invert().is_none().unwrap_u8() == 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lexicographic_largest() {
|
||||
assert!(!bool::from(Fp2::zero().lexicographically_largest()));
|
||||
assert!(!bool::from(Fp2::one().lexicographically_largest()));
|
||||
assert!(bool::from(
|
||||
Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x1128_ecad_6754_9455,
|
||||
0x9e7a_1cff_3a4e_a1a8,
|
||||
0xeb20_8d51_e08b_cf27,
|
||||
0xe98a_d408_11f5_fc2b,
|
||||
0x736c_3a59_232d_511d,
|
||||
0x10ac_d42d_29cf_cbb6,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xd328_e37c_c2f5_8d41,
|
||||
0x948d_f085_8a60_5869,
|
||||
0x6032_f9d5_6f93_a573,
|
||||
0x2be4_83ef_3fff_dc87,
|
||||
0x30ef_61f8_8f48_3c2a,
|
||||
0x1333_f55a_3572_5be0,
|
||||
]),
|
||||
}
|
||||
.lexicographically_largest()
|
||||
));
|
||||
assert!(!bool::from(
|
||||
Fp2 {
|
||||
c0: -Fp::from_raw_unchecked([
|
||||
0x1128_ecad_6754_9455,
|
||||
0x9e7a_1cff_3a4e_a1a8,
|
||||
0xeb20_8d51_e08b_cf27,
|
||||
0xe98a_d408_11f5_fc2b,
|
||||
0x736c_3a59_232d_511d,
|
||||
0x10ac_d42d_29cf_cbb6,
|
||||
]),
|
||||
c1: -Fp::from_raw_unchecked([
|
||||
0xd328_e37c_c2f5_8d41,
|
||||
0x948d_f085_8a60_5869,
|
||||
0x6032_f9d5_6f93_a573,
|
||||
0x2be4_83ef_3fff_dc87,
|
||||
0x30ef_61f8_8f48_3c2a,
|
||||
0x1333_f55a_3572_5be0,
|
||||
]),
|
||||
}
|
||||
.lexicographically_largest()
|
||||
));
|
||||
assert!(!bool::from(
|
||||
Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x1128_ecad_6754_9455,
|
||||
0x9e7a_1cff_3a4e_a1a8,
|
||||
0xeb20_8d51_e08b_cf27,
|
||||
0xe98a_d408_11f5_fc2b,
|
||||
0x736c_3a59_232d_511d,
|
||||
0x10ac_d42d_29cf_cbb6,
|
||||
]),
|
||||
c1: Fp::zero(),
|
||||
}
|
||||
.lexicographically_largest()
|
||||
));
|
||||
assert!(bool::from(
|
||||
Fp2 {
|
||||
c0: -Fp::from_raw_unchecked([
|
||||
0x1128_ecad_6754_9455,
|
||||
0x9e7a_1cff_3a4e_a1a8,
|
||||
0xeb20_8d51_e08b_cf27,
|
||||
0xe98a_d408_11f5_fc2b,
|
||||
0x736c_3a59_232d_511d,
|
||||
0x10ac_d42d_29cf_cbb6,
|
||||
]),
|
||||
c1: Fp::zero(),
|
||||
}
|
||||
.lexicographically_largest()
|
||||
));
|
||||
}
|
504
bls12_381/src/fp6.rs
Normal file
504
bls12_381/src/fp6.rs
Normal file
@ -0,0 +1,504 @@
|
||||
use crate::fp::*;
|
||||
use crate::fp2::*;
|
||||
|
||||
use core::fmt;
|
||||
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
|
||||
|
||||
/// This represents an element $c_0 + c_1 v + c_2 v^2$ of $\mathbb{F}_{p^6} = \mathbb{F}_{p^2} / v^3 - u - 1$.
|
||||
pub struct Fp6 {
|
||||
pub c0: Fp2,
|
||||
pub c1: Fp2,
|
||||
pub c2: Fp2,
|
||||
}
|
||||
|
||||
impl From<Fp> for Fp6 {
|
||||
fn from(f: Fp) -> Fp6 {
|
||||
Fp6 {
|
||||
c0: Fp2::from(f),
|
||||
c1: Fp2::zero(),
|
||||
c2: Fp2::zero(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Fp2> for Fp6 {
|
||||
fn from(f: Fp2) -> Fp6 {
|
||||
Fp6 {
|
||||
c0: f,
|
||||
c1: Fp2::zero(),
|
||||
c2: Fp2::zero(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Fp6 {
|
||||
fn eq(&self, other: &Fp6) -> bool {
|
||||
self.ct_eq(other).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Copy for Fp6 {}
|
||||
impl Clone for Fp6 {
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Fp6 {
|
||||
fn default() -> Self {
|
||||
Fp6::zero()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Fp6 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:?} + ({:?})*v + ({:?})*v^2", self.c0, self.c1, self.c2)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConditionallySelectable for Fp6 {
|
||||
#[inline(always)]
|
||||
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
|
||||
Fp6 {
|
||||
c0: Fp2::conditional_select(&a.c0, &b.c0, choice),
|
||||
c1: Fp2::conditional_select(&a.c1, &b.c1, choice),
|
||||
c2: Fp2::conditional_select(&a.c2, &b.c2, choice),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConstantTimeEq for Fp6 {
|
||||
#[inline(always)]
|
||||
fn ct_eq(&self, other: &Self) -> Choice {
|
||||
self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1) & self.c2.ct_eq(&other.c2)
|
||||
}
|
||||
}
|
||||
|
||||
impl Fp6 {
|
||||
#[inline]
|
||||
pub fn zero() -> Self {
|
||||
Fp6 {
|
||||
c0: Fp2::zero(),
|
||||
c1: Fp2::zero(),
|
||||
c2: Fp2::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn one() -> Self {
|
||||
Fp6 {
|
||||
c0: Fp2::one(),
|
||||
c1: Fp2::zero(),
|
||||
c2: Fp2::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mul_by_1(&self, c1: &Fp2) -> Fp6 {
|
||||
let b_b = self.c1 * c1;
|
||||
|
||||
let t1 = (self.c1 + self.c2) * c1 - b_b;
|
||||
let t1 = t1.mul_by_nonresidue();
|
||||
|
||||
let t2 = (self.c0 + self.c1) * c1 - b_b;
|
||||
|
||||
Fp6 {
|
||||
c0: t1,
|
||||
c1: t2,
|
||||
c2: b_b,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mul_by_01(&self, c0: &Fp2, c1: &Fp2) -> Fp6 {
|
||||
let a_a = self.c0 * c0;
|
||||
let b_b = self.c1 * c1;
|
||||
|
||||
let t1 = (self.c1 + self.c2) * c1 - b_b;
|
||||
let t1 = t1.mul_by_nonresidue() + a_a;
|
||||
|
||||
let t2 = (c0 + c1) * (self.c0 + self.c1) - a_a - b_b;
|
||||
|
||||
let t3 = (self.c0 + self.c2) * c0 - a_a + b_b;
|
||||
|
||||
Fp6 {
|
||||
c0: t1,
|
||||
c1: t2,
|
||||
c2: t3,
|
||||
}
|
||||
}
|
||||
|
||||
/// Multiply by quadratic nonresidue v.
|
||||
pub fn mul_by_nonresidue(&self) -> Self {
|
||||
// Given a + bv + cv^2, this produces
|
||||
// av + bv^2 + cv^3
|
||||
// but because v^3 = u + 1, we have
|
||||
// c(u + 1) + av + v^2
|
||||
|
||||
Fp6 {
|
||||
c0: self.c2.mul_by_nonresidue(),
|
||||
c1: self.c0,
|
||||
c2: self.c1,
|
||||
}
|
||||
}
|
||||
|
||||
/// Raises this element to p.
|
||||
#[inline(always)]
|
||||
pub fn frobenius_map(&self) -> Self {
|
||||
let c0 = self.c0.frobenius_map();
|
||||
let c1 = self.c1.frobenius_map();
|
||||
let c2 = self.c2.frobenius_map();
|
||||
|
||||
// c1 = c1 * (u + 1)^((p - 1) / 3)
|
||||
let c1 = c1
|
||||
* Fp2 {
|
||||
c0: Fp::zero(),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xcd03_c9e4_8671_f071,
|
||||
0x5dab_2246_1fcd_a5d2,
|
||||
0x5870_42af_d385_1b95,
|
||||
0x8eb6_0ebe_01ba_cb9e,
|
||||
0x03f9_7d6e_83d0_50d2,
|
||||
0x18f0_2065_5463_8741,
|
||||
]),
|
||||
};
|
||||
|
||||
// c2 = c2 * (u + 1)^((2p - 2) / 3)
|
||||
let c2 = c2
|
||||
* Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x890d_c9e4_8675_45c3,
|
||||
0x2af3_2253_3285_a5d5,
|
||||
0x5088_0866_309b_7e2c,
|
||||
0xa20d_1b8c_7e88_1024,
|
||||
0x14e4_f04f_e2db_9068,
|
||||
0x14e5_6d3f_1564_853a,
|
||||
]),
|
||||
c1: Fp::zero(),
|
||||
};
|
||||
|
||||
Fp6 { c0, c1, c2 }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn is_zero(&self) -> Choice {
|
||||
self.c0.is_zero() & self.c1.is_zero() & self.c2.is_zero()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn square(&self) -> Self {
|
||||
let s0 = self.c0.square();
|
||||
let ab = self.c0 * self.c1;
|
||||
let s1 = ab + ab;
|
||||
let s2 = (self.c0 - self.c1 + self.c2).square();
|
||||
let bc = self.c1 * self.c2;
|
||||
let s3 = bc + bc;
|
||||
let s4 = self.c2.square();
|
||||
|
||||
Fp6 {
|
||||
c0: s3.mul_by_nonresidue() + s0,
|
||||
c1: s4.mul_by_nonresidue() + s1,
|
||||
c2: s1 + s2 + s3 - s0 - s4,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn invert(&self) -> CtOption<Self> {
|
||||
let c0 = (self.c1 * self.c2).mul_by_nonresidue();
|
||||
let c0 = self.c0.square() - c0;
|
||||
|
||||
let c1 = self.c2.square().mul_by_nonresidue();
|
||||
let c1 = c1 - (self.c0 * self.c1);
|
||||
|
||||
let c2 = self.c1.square();
|
||||
let c2 = c2 - (self.c0 * self.c2);
|
||||
|
||||
let tmp = ((self.c1 * c2) + (self.c2 * c1)).mul_by_nonresidue();
|
||||
let tmp = tmp + (self.c0 * c0);
|
||||
|
||||
tmp.invert().map(|t| Fp6 {
|
||||
c0: t * c0,
|
||||
c1: t * c1,
|
||||
c2: t * c2,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Mul<&'b Fp6> for &'a Fp6 {
|
||||
type Output = Fp6;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, other: &'b Fp6) -> Self::Output {
|
||||
let aa = self.c0 * other.c0;
|
||||
let bb = self.c1 * other.c1;
|
||||
let cc = self.c2 * other.c2;
|
||||
|
||||
let t1 = other.c1 + other.c2;
|
||||
let tmp = self.c1 + self.c2;
|
||||
let t1 = t1 * tmp;
|
||||
let t1 = t1 - bb;
|
||||
let t1 = t1 - cc;
|
||||
let t1 = t1.mul_by_nonresidue();
|
||||
let t1 = t1 + aa;
|
||||
|
||||
let t3 = other.c0 + other.c2;
|
||||
let tmp = self.c0 + self.c2;
|
||||
let t3 = t3 * tmp;
|
||||
let t3 = t3 - aa;
|
||||
let t3 = t3 + bb;
|
||||
let t3 = t3 - cc;
|
||||
|
||||
let t2 = other.c0 + other.c1;
|
||||
let tmp = self.c0 + self.c1;
|
||||
let t2 = t2 * tmp;
|
||||
let t2 = t2 - aa;
|
||||
let t2 = t2 - bb;
|
||||
let cc = cc.mul_by_nonresidue();
|
||||
let t2 = t2 + cc;
|
||||
|
||||
Fp6 {
|
||||
c0: t1,
|
||||
c1: t2,
|
||||
c2: t3,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Add<&'b Fp6> for &'a Fp6 {
|
||||
type Output = Fp6;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: &'b Fp6) -> Self::Output {
|
||||
Fp6 {
|
||||
c0: self.c0 + rhs.c0,
|
||||
c1: self.c1 + rhs.c1,
|
||||
c2: self.c2 + rhs.c2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Neg for &'a Fp6 {
|
||||
type Output = Fp6;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Self::Output {
|
||||
Fp6 {
|
||||
c0: -self.c0,
|
||||
c1: -self.c1,
|
||||
c2: -self.c2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for Fp6 {
|
||||
type Output = Fp6;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Self::Output {
|
||||
-&self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Sub<&'b Fp6> for &'a Fp6 {
|
||||
type Output = Fp6;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: &'b Fp6) -> Self::Output {
|
||||
Fp6 {
|
||||
c0: self.c0 - rhs.c0,
|
||||
c1: self.c1 - rhs.c1,
|
||||
c2: self.c2 - rhs.c2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_binops_additive!(Fp6, Fp6);
|
||||
impl_binops_multiplicative!(Fp6, Fp6);
|
||||
|
||||
#[test]
|
||||
fn test_arithmetic() {
|
||||
use crate::fp::*;
|
||||
|
||||
let a = Fp6 {
|
||||
c0: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x47f9_cb98_b1b8_2d58,
|
||||
0x5fe9_11eb_a3aa_1d9d,
|
||||
0x96bf_1b5f_4dd8_1db3,
|
||||
0x8100_d27c_c925_9f5b,
|
||||
0xafa2_0b96_7464_0eab,
|
||||
0x09bb_cea7_d8d9_497d,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x0303_cb98_b166_2daa,
|
||||
0xd931_10aa_0a62_1d5a,
|
||||
0xbfa9_820c_5be4_a468,
|
||||
0x0ba3_643e_cb05_a348,
|
||||
0xdc35_34bb_1f1c_25a6,
|
||||
0x06c3_05bb_19c0_e1c1,
|
||||
]),
|
||||
},
|
||||
c1: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x46f9_cb98_b162_d858,
|
||||
0x0be9_109c_f7aa_1d57,
|
||||
0xc791_bc55_fece_41d2,
|
||||
0xf84c_5770_4e38_5ec2,
|
||||
0xcb49_c1d9_c010_e60f,
|
||||
0x0acd_b8e1_58bf_e3c8,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x8aef_cb98_b15f_8306,
|
||||
0x3ea1_108f_e4f2_1d54,
|
||||
0xcf79_f69f_a1b7_df3b,
|
||||
0xe4f5_4aa1_d16b_1a3c,
|
||||
0xba5e_4ef8_6105_a679,
|
||||
0x0ed8_6c07_97be_e5cf,
|
||||
]),
|
||||
},
|
||||
c2: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xcee5_cb98_b15c_2db4,
|
||||
0x7159_1082_d23a_1d51,
|
||||
0xd762_30e9_44a1_7ca4,
|
||||
0xd19e_3dd3_549d_d5b6,
|
||||
0xa972_dc17_01fa_66e3,
|
||||
0x12e3_1f2d_d6bd_e7d6,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xad2a_cb98_b173_2d9d,
|
||||
0x2cfd_10dd_0696_1d64,
|
||||
0x0739_6b86_c6ef_24e8,
|
||||
0xbd76_e2fd_b1bf_c820,
|
||||
0x6afe_a7f6_de94_d0d5,
|
||||
0x1099_4b0c_5744_c040,
|
||||
]),
|
||||
},
|
||||
};
|
||||
|
||||
let b = Fp6 {
|
||||
c0: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xf120_cb98_b16f_d84b,
|
||||
0x5fb5_10cf_f3de_1d61,
|
||||
0x0f21_a5d0_69d8_c251,
|
||||
0xaa1f_d62f_34f2_839a,
|
||||
0x5a13_3515_7f89_913f,
|
||||
0x14a3_fe32_9643_c247,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x3516_cb98_b16c_82f9,
|
||||
0x926d_10c2_e126_1d5f,
|
||||
0x1709_e01a_0cc2_5fba,
|
||||
0x96c8_c960_b825_3f14,
|
||||
0x4927_c234_207e_51a9,
|
||||
0x18ae_b158_d542_c44e,
|
||||
]),
|
||||
},
|
||||
c1: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xbf0d_cb98_b169_82fc,
|
||||
0xa679_10b7_1d1a_1d5c,
|
||||
0xb7c1_47c2_b8fb_06ff,
|
||||
0x1efa_710d_47d2_e7ce,
|
||||
0xed20_a79c_7e27_653c,
|
||||
0x02b8_5294_dac1_dfba,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x9d52_cb98_b180_82e5,
|
||||
0x621d_1111_5176_1d6f,
|
||||
0xe798_8260_3b48_af43,
|
||||
0x0ad3_1637_a4f4_da37,
|
||||
0xaeac_737c_5ac1_cf2e,
|
||||
0x006e_7e73_5b48_b824,
|
||||
]),
|
||||
},
|
||||
c2: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xe148_cb98_b17d_2d93,
|
||||
0x94d5_1104_3ebe_1d6c,
|
||||
0xef80_bca9_de32_4cac,
|
||||
0xf77c_0969_2827_95b1,
|
||||
0x9dc1_009a_fbb6_8f97,
|
||||
0x0479_3199_9a47_ba2b,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x253e_cb98_b179_d841,
|
||||
0xc78d_10f7_2c06_1d6a,
|
||||
0xf768_f6f3_811b_ea15,
|
||||
0xe424_fc9a_ab5a_512b,
|
||||
0x8cd5_8db9_9cab_5001,
|
||||
0x0883_e4bf_d946_bc32,
|
||||
]),
|
||||
},
|
||||
};
|
||||
|
||||
let c = Fp6 {
|
||||
c0: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x6934_cb98_b176_82ef,
|
||||
0xfa45_10ea_194e_1d67,
|
||||
0xff51_313d_2405_877e,
|
||||
0xd0cd_efcc_2e8d_0ca5,
|
||||
0x7bea_1ad8_3da0_106b,
|
||||
0x0c8e_97e6_1845_be39,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x4779_cb98_b18d_82d8,
|
||||
0xb5e9_1144_4daa_1d7a,
|
||||
0x2f28_6bda_a653_2fc2,
|
||||
0xbca6_94f6_8bae_ff0f,
|
||||
0x3d75_e6b8_1a3a_7a5d,
|
||||
0x0a44_c3c4_98cc_96a3,
|
||||
]),
|
||||
},
|
||||
c1: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x8b6f_cb98_b18a_2d86,
|
||||
0xe8a1_1137_3af2_1d77,
|
||||
0x3710_a624_493c_cd2b,
|
||||
0xa94f_8828_0ee1_ba89,
|
||||
0x2c8a_73d6_bb2f_3ac7,
|
||||
0x0e4f_76ea_d7cb_98aa,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xcf65_cb98_b186_d834,
|
||||
0x1b59_112a_283a_1d74,
|
||||
0x3ef8_e06d_ec26_6a95,
|
||||
0x95f8_7b59_9214_7603,
|
||||
0x1b9f_00f5_5c23_fb31,
|
||||
0x125a_2a11_16ca_9ab1,
|
||||
]),
|
||||
},
|
||||
c2: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x135b_cb98_b183_82e2,
|
||||
0x4e11_111d_1582_1d72,
|
||||
0x46e1_1ab7_8f10_07fe,
|
||||
0x82a1_6e8b_1547_317d,
|
||||
0x0ab3_8e13_fd18_bb9b,
|
||||
0x1664_dd37_55c9_9cb8,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xce65_cb98_b131_8334,
|
||||
0xc759_0fdb_7c3a_1d2e,
|
||||
0x6fcb_8164_9d1c_8eb3,
|
||||
0x0d44_004d_1727_356a,
|
||||
0x3746_b738_a7d0_d296,
|
||||
0x136c_144a_96b1_34fc,
|
||||
]),
|
||||
},
|
||||
};
|
||||
|
||||
assert_eq!(a.square(), a * a);
|
||||
assert_eq!(b.square(), b * b);
|
||||
assert_eq!(c.square(), c * c);
|
||||
|
||||
assert_eq!((a + b) * c.square(), (c * c * a) + (c * c * b));
|
||||
|
||||
assert_eq!(
|
||||
a.invert().unwrap() * b.invert().unwrap(),
|
||||
(a * b).invert().unwrap()
|
||||
);
|
||||
assert_eq!(a.invert().unwrap() * a, Fp6::one());
|
||||
}
|
1347
bls12_381/src/g1.rs
Normal file
1347
bls12_381/src/g1.rs
Normal file
File diff suppressed because it is too large
Load Diff
1595
bls12_381/src/g2.rs
Normal file
1595
bls12_381/src/g2.rs
Normal file
File diff suppressed because it is too large
Load Diff
80
bls12_381/src/lib.rs
Normal file
80
bls12_381/src/lib.rs
Normal file
@ -0,0 +1,80 @@
|
||||
//! # `bls12_381`
|
||||
//!
|
||||
//! This crate provides an implementation of the BLS12-381 pairing-friendly elliptic
|
||||
//! curve construction.
|
||||
//!
|
||||
//! * **This implementation has not been reviewed or audited. Use at your own risk.**
|
||||
//! * This implementation targets Rust `1.36` or later.
|
||||
//! * This implementation does not require the Rust standard library.
|
||||
//! * All operations are constant time unless explicitly noted.
|
||||
|
||||
#![no_std]
|
||||
// Catch documentation errors caused by code changes.
|
||||
#![deny(intra_doc_link_resolution_failure)]
|
||||
#![deny(missing_debug_implementations)]
|
||||
#![deny(missing_docs)]
|
||||
#![deny(unsafe_code)]
|
||||
#![allow(clippy::too_many_arguments)]
|
||||
#![allow(clippy::many_single_char_names)]
|
||||
// This lint is described at
|
||||
// https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_arithmetic_impl
|
||||
// In our library, some of the arithmetic involving extension fields will necessarily
|
||||
// involve various binary operators, and so this lint is triggered unnecessarily.
|
||||
#![allow(clippy::suspicious_arithmetic_impl)]
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
extern crate alloc;
|
||||
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate std;
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(feature = "groups")]
|
||||
mod tests;
|
||||
|
||||
#[macro_use]
|
||||
mod util;
|
||||
|
||||
/// Notes about how the BLS12-381 elliptic curve is designed, specified
|
||||
/// and implemented by this library.
|
||||
pub mod notes {
|
||||
pub mod design;
|
||||
pub mod serialization;
|
||||
}
|
||||
|
||||
mod scalar;
|
||||
|
||||
pub use scalar::Scalar;
|
||||
|
||||
#[cfg(feature = "groups")]
|
||||
mod fp;
|
||||
#[cfg(feature = "groups")]
|
||||
mod fp2;
|
||||
#[cfg(feature = "groups")]
|
||||
mod g1;
|
||||
#[cfg(feature = "groups")]
|
||||
mod g2;
|
||||
|
||||
#[cfg(feature = "groups")]
|
||||
pub use g1::{G1Affine, G1Projective};
|
||||
#[cfg(feature = "groups")]
|
||||
pub use g2::{G2Affine, G2Projective};
|
||||
|
||||
#[cfg(feature = "groups")]
|
||||
mod fp12;
|
||||
#[cfg(feature = "groups")]
|
||||
mod fp6;
|
||||
|
||||
// The BLS parameter x for BLS12-381 is -0xd201000000010000
|
||||
const BLS_X: u64 = 0xd201_0000_0001_0000;
|
||||
const BLS_X_IS_NEGATIVE: bool = true;
|
||||
|
||||
#[cfg(feature = "pairings")]
|
||||
mod pairings;
|
||||
|
||||
#[cfg(feature = "pairings")]
|
||||
pub use pairings::{pairing, Gt, MillerLoopResult};
|
||||
|
||||
#[cfg(all(feature = "pairings", feature = "alloc"))]
|
||||
pub use pairings::{multi_miller_loop, G2Prepared};
|
63
bls12_381/src/notes/design.rs
Normal file
63
bls12_381/src/notes/design.rs
Normal file
@ -0,0 +1,63 @@
|
||||
//! # Design of BLS12-381
|
||||
//! ## Fixed Generators
|
||||
//!
|
||||
//! Although any generator produced by hashing to $\mathbb{G}_1$ or $\mathbb{G}_2$ is
|
||||
//! safe to use in a cryptographic protocol, we specify some simple, fixed generators.
|
||||
//!
|
||||
//! In order to derive these generators, we select the lexicographically smallest
|
||||
//! valid $x$-coordinate and the lexicographically smallest corresponding $y$-coordinate,
|
||||
//! and then scale the resulting point by the cofactor, such that the result is not the
|
||||
//! identity. This results in the following fixed generators:
|
||||
//!
|
||||
//! 1. $\mathbb{G}_1$
|
||||
//! * $x = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507$
|
||||
//! * $y = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569$
|
||||
//! 2. $\mathbb{G}_2$
|
||||
//! * $x = 352701069587466618187139116011060144890029952792775240219908644239793785735715026873347600343865175952761926303160 + 3059144344244213709971259814753781636986470325476647558659373206291635324768958432433509563104347017837885763365758 u$
|
||||
//! * $y = 1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905 + 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582 u$
|
||||
//!
|
||||
//! This can be derived using the following sage script:
|
||||
//!
|
||||
//! ```norun
|
||||
//! param = -0xd201000000010000
|
||||
//! def r(x):
|
||||
//! return (x**4) - (x**2) + 1
|
||||
//! def q(x):
|
||||
//! return (((x - 1) ** 2) * ((x**4) - (x**2) + 1) // 3) + x
|
||||
//! def g1_h(x):
|
||||
//! return ((x-1)**2) // 3
|
||||
//! def g2_h(x):
|
||||
//! return ((x**8) - (4 * (x**7)) + (5 * (x**6)) - (4 * (x**4)) + (6 * (x**3)) - (4 * (x**2)) - (4*x) + 13) // 9
|
||||
//! q = q(param)
|
||||
//! r = r(param)
|
||||
//! Fq = GF(q)
|
||||
//! ec = EllipticCurve(Fq, [0, 4])
|
||||
//! def psqrt(v):
|
||||
//! assert(not v.is_zero())
|
||||
//! a = sqrt(v)
|
||||
//! b = -a
|
||||
//! if a < b:
|
||||
//! return a
|
||||
//! else:
|
||||
//! return b
|
||||
//! for x in range(0,100):
|
||||
//! rhs = Fq(x)^3 + 4
|
||||
//! if rhs.is_square():
|
||||
//! y = psqrt(rhs)
|
||||
//! p = ec(x, y) * g1_h(param)
|
||||
//! if (not p.is_zero()) and (p * r).is_zero():
|
||||
//! print "g1 generator: %s" % p
|
||||
//! break
|
||||
//! Fqx.<j> = PolynomialRing(Fq, 'j')
|
||||
//! Fq2.<i> = GF(q^2, modulus=j^2 + 1)
|
||||
//! ec2 = EllipticCurve(Fq2, [0, (4 * (1 + i))])
|
||||
//! assert(ec2.order() == (r * g2_h(param)))
|
||||
//! for x in range(0,100):
|
||||
//! rhs = (Fq2(x))^3 + (4 * (1 + i))
|
||||
//! if rhs.is_square():
|
||||
//! y = psqrt(rhs)
|
||||
//! p = ec2(Fq2(x), y) * g2_h(param)
|
||||
//! if (not p.is_zero()) and (p * r).is_zero():
|
||||
//! print "g2 generator: %s" % p
|
||||
//! break
|
||||
//! ```
|
29
bls12_381/src/notes/serialization.rs
Normal file
29
bls12_381/src/notes/serialization.rs
Normal file
@ -0,0 +1,29 @@
|
||||
//! # BLS12-381 serialization
|
||||
//!
|
||||
//! * $\mathbb{F}\_p$ elements are encoded in big-endian form. They occupy 48
|
||||
//! bytes in this form.
|
||||
//! * $\mathbb{F}\_{p^2}$ elements are encoded in big-endian form, meaning that
|
||||
//! the $\mathbb{F}\_{p^2}$ element $c\_0 + c\_1 \cdot u$ is represented by the
|
||||
//! $\mathbb{F}\_p$ element $c\_1$ followed by the $\mathbb{F}\_p$ element $c\_0$.
|
||||
//! This means $\mathbb{F}_{p^2}$ elements occupy 96 bytes in this form.
|
||||
//! * The group $\mathbb{G}\_1$ uses $\mathbb{F}\_p$ elements for coordinates. The
|
||||
//! group $\mathbb{G}\_2$ uses $\mathbb{F}_{p^2}$ elements for coordinates.
|
||||
//! * $\mathbb{G}\_1$ and $\mathbb{G}\_2$ elements can be encoded in uncompressed
|
||||
//! form (the x-coordinate followed by the y-coordinate) or in compressed form
|
||||
//! (just the x-coordinate). $\mathbb{G}\_1$ elements occupy 96 bytes in
|
||||
//! uncompressed form, and 48 bytes in compressed form. $\mathbb{G}\_2$
|
||||
//! elements occupy 192 bytes in uncompressed form, and 96 bytes in compressed
|
||||
//! form.
|
||||
//!
|
||||
//! The most-significant three bits of a $\mathbb{G}\_1$ or $\mathbb{G}\_2$
|
||||
//! encoding should be masked away before the coordinate(s) are interpreted.
|
||||
//! These bits are used to unambiguously represent the underlying element:
|
||||
//! * The most significant bit, when set, indicates that the point is in
|
||||
//! compressed form. Otherwise, the point is in uncompressed form.
|
||||
//! * The second-most significant bit indicates that the point is at infinity.
|
||||
//! If this bit is set, the remaining bits of the group element's encoding
|
||||
//! should be set to zero.
|
||||
//! * The third-most significant bit is set if (and only if) this point is in
|
||||
//! compressed form _and_ it is not the point at infinity _and_ its
|
||||
//! y-coordinate is the lexicographically largest of the two associated with
|
||||
//! the encoded x-coordinate.
|
648
bls12_381/src/pairings.rs
Normal file
648
bls12_381/src/pairings.rs
Normal file
@ -0,0 +1,648 @@
|
||||
use crate::fp12::Fp12;
|
||||
use crate::fp2::Fp2;
|
||||
use crate::fp6::Fp6;
|
||||
use crate::{G1Affine, G2Affine, G2Projective, Scalar, BLS_X, BLS_X_IS_NEGATIVE};
|
||||
|
||||
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
||||
|
||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::vec::Vec;
|
||||
|
||||
/// Represents results of a Miller loop, one of the most expensive portions
|
||||
/// of the pairing function. `MillerLoopResult`s cannot be compared with each
|
||||
/// other until `.final_exponentiation()` is called, which is also expensive.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct MillerLoopResult(pub(crate) Fp12);
|
||||
|
||||
impl ConditionallySelectable for MillerLoopResult {
|
||||
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
|
||||
MillerLoopResult(Fp12::conditional_select(&a.0, &b.0, choice))
|
||||
}
|
||||
}
|
||||
|
||||
impl MillerLoopResult {
|
||||
/// This performs a "final exponentiation" routine to convert the result
|
||||
/// of a Miller loop into an element of `Gt` with help of efficient squaring
|
||||
/// operation in the so-called `cyclotomic subgroup` of `Fq6` so that
|
||||
/// it can be compared with other elements of `Gt`.
|
||||
pub fn final_exponentiation(&self) -> Gt {
|
||||
#[must_use]
|
||||
fn fp4_square(a: Fp2, b: Fp2) -> (Fp2, Fp2) {
|
||||
let t0 = a.square();
|
||||
let t1 = b.square();
|
||||
let mut t2 = t1.mul_by_nonresidue();
|
||||
let c0 = t2 + t0;
|
||||
t2 = a + b;
|
||||
t2 = t2.square();
|
||||
t2 -= t0;
|
||||
let c1 = t2 - t1;
|
||||
|
||||
(c0, c1)
|
||||
}
|
||||
// Adaptation of Algorithm 5.5.4, Guide to Pairing-Based Cryptography
|
||||
// Faster Squaring in the Cyclotomic Subgroup of Sixth Degree Extensions
|
||||
// https://eprint.iacr.org/2009/565.pdf
|
||||
#[must_use]
|
||||
fn cyclotomic_square(f: Fp12) -> Fp12 {
|
||||
let mut z0 = f.c0.c0;
|
||||
let mut z4 = f.c0.c1;
|
||||
let mut z3 = f.c0.c2;
|
||||
let mut z2 = f.c1.c0;
|
||||
let mut z1 = f.c1.c1;
|
||||
let mut z5 = f.c1.c2;
|
||||
|
||||
let (t0, t1) = fp4_square(z0, z1);
|
||||
|
||||
// For A
|
||||
z0 = t0 - z0;
|
||||
z0 = z0 + z0 + t0;
|
||||
|
||||
z1 = t1 + z1;
|
||||
z1 = z1 + z1 + t1;
|
||||
|
||||
let (mut t0, t1) = fp4_square(z2, z3);
|
||||
let (t2, t3) = fp4_square(z4, z5);
|
||||
|
||||
// For C
|
||||
z4 = t0 - z4;
|
||||
z4 = z4 + z4 + t0;
|
||||
|
||||
z5 = t1 + z5;
|
||||
z5 = z5 + z5 + t1;
|
||||
|
||||
// For B
|
||||
t0 = t3.mul_by_nonresidue();
|
||||
z2 = t0 + z2;
|
||||
z2 = z2 + z2 + t0;
|
||||
|
||||
z3 = t2 - z3;
|
||||
z3 = z3 + z3 + t2;
|
||||
|
||||
Fp12 {
|
||||
c0: Fp6 {
|
||||
c0: z0,
|
||||
c1: z4,
|
||||
c2: z3,
|
||||
},
|
||||
c1: Fp6 {
|
||||
c0: z2,
|
||||
c1: z1,
|
||||
c2: z5,
|
||||
},
|
||||
}
|
||||
}
|
||||
#[must_use]
|
||||
fn cycolotomic_exp(f: Fp12) -> Fp12 {
|
||||
let x = BLS_X;
|
||||
let mut tmp = Fp12::one();
|
||||
let mut found_one = false;
|
||||
for i in (0..64).rev().map(|b| ((x >> b) & 1) == 1) {
|
||||
if found_one {
|
||||
tmp = cyclotomic_square(tmp)
|
||||
} else {
|
||||
found_one = i;
|
||||
}
|
||||
|
||||
if i {
|
||||
tmp *= f;
|
||||
}
|
||||
}
|
||||
|
||||
tmp.conjugate()
|
||||
}
|
||||
|
||||
let mut f = self.0;
|
||||
let mut t0 = f
|
||||
.frobenius_map()
|
||||
.frobenius_map()
|
||||
.frobenius_map()
|
||||
.frobenius_map()
|
||||
.frobenius_map()
|
||||
.frobenius_map();
|
||||
Gt(f.invert()
|
||||
.map(|mut t1| {
|
||||
let mut t2 = t0 * t1;
|
||||
t1 = t2;
|
||||
t2 = t2.frobenius_map().frobenius_map();
|
||||
t2 *= t1;
|
||||
t1 = cyclotomic_square(t2).conjugate();
|
||||
let mut t3 = cycolotomic_exp(t2);
|
||||
let mut t4 = cyclotomic_square(t3);
|
||||
let mut t5 = t1 * t3;
|
||||
t1 = cycolotomic_exp(t5);
|
||||
t0 = cycolotomic_exp(t1);
|
||||
let mut t6 = cycolotomic_exp(t0);
|
||||
t6 *= t4;
|
||||
t4 = cycolotomic_exp(t6);
|
||||
t5 = t5.conjugate();
|
||||
t4 *= t5 * t2;
|
||||
t5 = t2.conjugate();
|
||||
t1 *= t2;
|
||||
t1 = t1.frobenius_map().frobenius_map().frobenius_map();
|
||||
t6 *= t5;
|
||||
t6 = t6.frobenius_map();
|
||||
t3 *= t0;
|
||||
t3 = t3.frobenius_map().frobenius_map();
|
||||
t3 *= t1;
|
||||
t3 *= t6;
|
||||
f = t3 * t4;
|
||||
|
||||
f
|
||||
})
|
||||
// We unwrap() because `MillerLoopResult` can only be constructed
|
||||
// by a function within this crate, and we uphold the invariant
|
||||
// that the enclosed value is nonzero.
|
||||
.unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Add<&'b MillerLoopResult> for &'a MillerLoopResult {
|
||||
type Output = MillerLoopResult;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: &'b MillerLoopResult) -> MillerLoopResult {
|
||||
MillerLoopResult(self.0 * rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl_add_binop_specify_output!(MillerLoopResult, MillerLoopResult, MillerLoopResult);
|
||||
|
||||
/// This is an element of $\mathbb{G}_T$, the target group of the pairing function. As with
|
||||
/// $\mathbb{G}_1$ and $\mathbb{G}_2$ this group has order $q$.
|
||||
///
|
||||
/// Typically, $\mathbb{G}_T$ is written multiplicatively but we will write it additively to
|
||||
/// keep code and abstractions consistent.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Gt(pub(crate) Fp12);
|
||||
|
||||
impl ConstantTimeEq for Gt {
|
||||
fn ct_eq(&self, other: &Self) -> Choice {
|
||||
self.0.ct_eq(&other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConditionallySelectable for Gt {
|
||||
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
|
||||
Gt(Fp12::conditional_select(&a.0, &b.0, choice))
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Gt {}
|
||||
impl PartialEq for Gt {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
bool::from(self.ct_eq(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Gt {
|
||||
/// Returns the group identity, which is $1$.
|
||||
pub fn identity() -> Gt {
|
||||
Gt(Fp12::one())
|
||||
}
|
||||
|
||||
/// Doubles this group element.
|
||||
pub fn double(&self) -> Gt {
|
||||
Gt(self.0.square())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Neg for &'a Gt {
|
||||
type Output = Gt;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Gt {
|
||||
// The element is unitary, so we just conjugate.
|
||||
Gt(self.0.conjugate())
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for Gt {
|
||||
type Output = Gt;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Gt {
|
||||
-&self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Add<&'b Gt> for &'a Gt {
|
||||
type Output = Gt;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: &'b Gt) -> Gt {
|
||||
Gt(self.0 * rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Sub<&'b Gt> for &'a Gt {
|
||||
type Output = Gt;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: &'b Gt) -> Gt {
|
||||
self + (-rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Mul<&'b Scalar> for &'a Gt {
|
||||
type Output = Gt;
|
||||
|
||||
fn mul(self, other: &'b Scalar) -> Self::Output {
|
||||
let mut acc = Gt::identity();
|
||||
|
||||
// This is a simple double-and-add implementation of group element
|
||||
// multiplication, moving from most significant to least
|
||||
// significant bit of the scalar.
|
||||
//
|
||||
// We skip the leading bit because it's always unset for Fq
|
||||
// elements.
|
||||
for bit in other
|
||||
.to_bytes()
|
||||
.iter()
|
||||
.rev()
|
||||
.flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8)))
|
||||
.skip(1)
|
||||
{
|
||||
acc = acc.double();
|
||||
acc = Gt::conditional_select(&acc, &(acc + self), bit);
|
||||
}
|
||||
|
||||
acc
|
||||
}
|
||||
}
|
||||
|
||||
impl_binops_additive!(Gt, Gt);
|
||||
impl_binops_multiplicative!(Gt, Scalar);
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[derive(Clone, Debug)]
|
||||
/// This structure contains cached computations pertaining to a $\mathbb{G}_2$
|
||||
/// element as part of the pairing function (specifically, the Miller loop) and
|
||||
/// so should be computed whenever a $\mathbb{G}_2$ element is being used in
|
||||
/// multiple pairings or is otherwise known in advance. This should be used in
|
||||
/// conjunction with the [`multi_miller_loop`](crate::multi_miller_loop)
|
||||
/// function provided by this crate.
|
||||
///
|
||||
/// Requires the `alloc` and `pairing` crate features to be enabled.
|
||||
pub struct G2Prepared {
|
||||
infinity: Choice,
|
||||
coeffs: Vec<(Fp2, Fp2, Fp2)>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl From<G2Affine> for G2Prepared {
|
||||
fn from(q: G2Affine) -> G2Prepared {
|
||||
struct Adder {
|
||||
cur: G2Projective,
|
||||
base: G2Affine,
|
||||
coeffs: Vec<(Fp2, Fp2, Fp2)>,
|
||||
}
|
||||
|
||||
impl MillerLoopDriver for Adder {
|
||||
type Output = ();
|
||||
|
||||
fn doubling_step(&mut self, _: Self::Output) -> Self::Output {
|
||||
let coeffs = doubling_step(&mut self.cur);
|
||||
self.coeffs.push(coeffs);
|
||||
}
|
||||
fn addition_step(&mut self, _: Self::Output) -> Self::Output {
|
||||
let coeffs = addition_step(&mut self.cur, &self.base);
|
||||
self.coeffs.push(coeffs);
|
||||
}
|
||||
fn square_output(_: Self::Output) -> Self::Output {}
|
||||
fn conjugate(_: Self::Output) -> Self::Output {}
|
||||
fn one() -> Self::Output {}
|
||||
}
|
||||
|
||||
let is_identity = q.is_identity();
|
||||
let q = G2Affine::conditional_select(&q, &G2Affine::generator(), is_identity);
|
||||
|
||||
let mut adder = Adder {
|
||||
cur: G2Projective::from(q),
|
||||
base: q,
|
||||
coeffs: Vec::with_capacity(68),
|
||||
};
|
||||
|
||||
miller_loop(&mut adder);
|
||||
|
||||
assert_eq!(adder.coeffs.len(), 68);
|
||||
|
||||
G2Prepared {
|
||||
infinity: is_identity,
|
||||
coeffs: adder.coeffs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
/// Computes $$\sum_{i=1}^n \textbf{ML}(a_i, b_i)$$ given a series of terms
|
||||
/// $$(a_1, b_1), (a_2, b_2), ..., (a_n, b_n).$$
|
||||
///
|
||||
/// Requires the `alloc` and `pairing` crate features to be enabled.
|
||||
pub fn multi_miller_loop(terms: &[(&G1Affine, &G2Prepared)]) -> MillerLoopResult {
|
||||
struct Adder<'a, 'b, 'c> {
|
||||
terms: &'c [(&'a G1Affine, &'b G2Prepared)],
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'c> MillerLoopDriver for Adder<'a, 'b, 'c> {
|
||||
type Output = Fp12;
|
||||
|
||||
fn doubling_step(&mut self, mut f: Self::Output) -> Self::Output {
|
||||
let index = self.index;
|
||||
for term in self.terms {
|
||||
let either_identity = term.0.is_identity() | term.1.infinity;
|
||||
|
||||
let new_f = ell(f, &term.1.coeffs[index], term.0);
|
||||
f = Fp12::conditional_select(&new_f, &f, either_identity);
|
||||
}
|
||||
self.index += 1;
|
||||
|
||||
f
|
||||
}
|
||||
fn addition_step(&mut self, mut f: Self::Output) -> Self::Output {
|
||||
let index = self.index;
|
||||
for term in self.terms {
|
||||
let either_identity = term.0.is_identity() | term.1.infinity;
|
||||
|
||||
let new_f = ell(f, &term.1.coeffs[index], term.0);
|
||||
f = Fp12::conditional_select(&new_f, &f, either_identity);
|
||||
}
|
||||
self.index += 1;
|
||||
|
||||
f
|
||||
}
|
||||
fn square_output(f: Self::Output) -> Self::Output {
|
||||
f.square()
|
||||
}
|
||||
fn conjugate(f: Self::Output) -> Self::Output {
|
||||
f.conjugate()
|
||||
}
|
||||
fn one() -> Self::Output {
|
||||
Fp12::one()
|
||||
}
|
||||
}
|
||||
|
||||
let mut adder = Adder { terms, index: 0 };
|
||||
|
||||
let tmp = miller_loop(&mut adder);
|
||||
|
||||
MillerLoopResult(tmp)
|
||||
}
|
||||
|
||||
/// Invoke the pairing function without the use of precomputation and other optimizations.
|
||||
pub fn pairing(p: &G1Affine, q: &G2Affine) -> Gt {
|
||||
struct Adder {
|
||||
cur: G2Projective,
|
||||
base: G2Affine,
|
||||
p: G1Affine,
|
||||
}
|
||||
|
||||
impl MillerLoopDriver for Adder {
|
||||
type Output = Fp12;
|
||||
|
||||
fn doubling_step(&mut self, f: Self::Output) -> Self::Output {
|
||||
let coeffs = doubling_step(&mut self.cur);
|
||||
ell(f, &coeffs, &self.p)
|
||||
}
|
||||
fn addition_step(&mut self, f: Self::Output) -> Self::Output {
|
||||
let coeffs = addition_step(&mut self.cur, &self.base);
|
||||
ell(f, &coeffs, &self.p)
|
||||
}
|
||||
fn square_output(f: Self::Output) -> Self::Output {
|
||||
f.square()
|
||||
}
|
||||
fn conjugate(f: Self::Output) -> Self::Output {
|
||||
f.conjugate()
|
||||
}
|
||||
fn one() -> Self::Output {
|
||||
Fp12::one()
|
||||
}
|
||||
}
|
||||
|
||||
let either_identity = p.is_identity() | q.is_identity();
|
||||
let p = G1Affine::conditional_select(&p, &G1Affine::generator(), either_identity);
|
||||
let q = G2Affine::conditional_select(&q, &G2Affine::generator(), either_identity);
|
||||
|
||||
let mut adder = Adder {
|
||||
cur: G2Projective::from(q),
|
||||
base: q,
|
||||
p,
|
||||
};
|
||||
|
||||
let tmp = miller_loop(&mut adder);
|
||||
let tmp = MillerLoopResult(Fp12::conditional_select(
|
||||
&tmp,
|
||||
&Fp12::one(),
|
||||
either_identity,
|
||||
));
|
||||
tmp.final_exponentiation()
|
||||
}
|
||||
|
||||
trait MillerLoopDriver {
|
||||
type Output;
|
||||
|
||||
fn doubling_step(&mut self, f: Self::Output) -> Self::Output;
|
||||
fn addition_step(&mut self, f: Self::Output) -> Self::Output;
|
||||
fn square_output(f: Self::Output) -> Self::Output;
|
||||
fn conjugate(f: Self::Output) -> Self::Output;
|
||||
fn one() -> Self::Output;
|
||||
}
|
||||
|
||||
/// This is a "generic" implementation of the Miller loop to avoid duplicating code
|
||||
/// structure elsewhere; instead, we'll write concrete instantiations of
|
||||
/// `MillerLoopDriver` for whatever purposes we need (such as caching modes).
|
||||
fn miller_loop<D: MillerLoopDriver>(driver: &mut D) -> D::Output {
|
||||
let mut f = D::one();
|
||||
|
||||
let mut found_one = false;
|
||||
for i in (0..64).rev().map(|b| (((BLS_X >> 1) >> b) & 1) == 1) {
|
||||
if !found_one {
|
||||
found_one = i;
|
||||
continue;
|
||||
}
|
||||
|
||||
f = driver.doubling_step(f);
|
||||
|
||||
if i {
|
||||
f = driver.addition_step(f);
|
||||
}
|
||||
|
||||
f = D::square_output(f);
|
||||
}
|
||||
|
||||
f = driver.doubling_step(f);
|
||||
|
||||
if BLS_X_IS_NEGATIVE {
|
||||
f = D::conjugate(f);
|
||||
}
|
||||
|
||||
f
|
||||
}
|
||||
|
||||
fn ell(f: Fp12, coeffs: &(Fp2, Fp2, Fp2), p: &G1Affine) -> Fp12 {
|
||||
let mut c0 = coeffs.0;
|
||||
let mut c1 = coeffs.1;
|
||||
|
||||
c0.c0 *= p.y;
|
||||
c0.c1 *= p.y;
|
||||
|
||||
c1.c0 *= p.x;
|
||||
c1.c1 *= p.x;
|
||||
|
||||
f.mul_by_014(&coeffs.2, &c1, &c0)
|
||||
}
|
||||
|
||||
fn doubling_step(r: &mut G2Projective) -> (Fp2, Fp2, Fp2) {
|
||||
// Adaptation of Algorithm 26, https://eprint.iacr.org/2010/354.pdf
|
||||
let tmp0 = r.x.square();
|
||||
let tmp1 = r.y.square();
|
||||
let tmp2 = tmp1.square();
|
||||
let tmp3 = (tmp1 + r.x).square() - tmp0 - tmp2;
|
||||
let tmp3 = tmp3 + tmp3;
|
||||
let tmp4 = tmp0 + tmp0 + tmp0;
|
||||
let tmp6 = r.x + tmp4;
|
||||
let tmp5 = tmp4.square();
|
||||
let zsquared = r.z.square();
|
||||
r.x = tmp5 - tmp3 - tmp3;
|
||||
r.z = (r.z + r.y).square() - tmp1 - zsquared;
|
||||
r.y = (tmp3 - r.x) * tmp4;
|
||||
let tmp2 = tmp2 + tmp2;
|
||||
let tmp2 = tmp2 + tmp2;
|
||||
let tmp2 = tmp2 + tmp2;
|
||||
r.y -= tmp2;
|
||||
let tmp3 = tmp4 * zsquared;
|
||||
let tmp3 = tmp3 + tmp3;
|
||||
let tmp3 = -tmp3;
|
||||
let tmp6 = tmp6.square() - tmp0 - tmp5;
|
||||
let tmp1 = tmp1 + tmp1;
|
||||
let tmp1 = tmp1 + tmp1;
|
||||
let tmp6 = tmp6 - tmp1;
|
||||
let tmp0 = r.z * zsquared;
|
||||
let tmp0 = tmp0 + tmp0;
|
||||
|
||||
(tmp0, tmp3, tmp6)
|
||||
}
|
||||
|
||||
fn addition_step(r: &mut G2Projective, q: &G2Affine) -> (Fp2, Fp2, Fp2) {
|
||||
// Adaptation of Algorithm 27, https://eprint.iacr.org/2010/354.pdf
|
||||
let zsquared = r.z.square();
|
||||
let ysquared = q.y.square();
|
||||
let t0 = zsquared * q.x;
|
||||
let t1 = ((q.y + r.z).square() - ysquared - zsquared) * zsquared;
|
||||
let t2 = t0 - r.x;
|
||||
let t3 = t2.square();
|
||||
let t4 = t3 + t3;
|
||||
let t4 = t4 + t4;
|
||||
let t5 = t4 * t2;
|
||||
let t6 = t1 - r.y - r.y;
|
||||
let t9 = t6 * q.x;
|
||||
let t7 = t4 * r.x;
|
||||
r.x = t6.square() - t5 - t7 - t7;
|
||||
r.z = (r.z + t2).square() - zsquared - t3;
|
||||
let t10 = q.y + r.z;
|
||||
let t8 = (t7 - r.x) * t6;
|
||||
let t0 = r.y * t5;
|
||||
let t0 = t0 + t0;
|
||||
r.y = t8 - t0;
|
||||
let t10 = t10.square() - ysquared;
|
||||
let ztsquared = r.z.square();
|
||||
let t10 = t10 - ztsquared;
|
||||
let t9 = t9 + t9 - t10;
|
||||
let t10 = r.z + r.z;
|
||||
let t6 = -t6;
|
||||
let t1 = t6 + t6;
|
||||
|
||||
(t10, t1, t9)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bilinearity() {
|
||||
use crate::Scalar;
|
||||
|
||||
let a = Scalar::from_raw([1, 2, 3, 4]).invert().unwrap().square();
|
||||
let b = Scalar::from_raw([5, 6, 7, 8]).invert().unwrap().square();
|
||||
let c = a * b;
|
||||
|
||||
let g = G1Affine::from(G1Affine::generator() * a);
|
||||
let h = G2Affine::from(G2Affine::generator() * b);
|
||||
let p = pairing(&g, &h);
|
||||
|
||||
assert!(p != Gt::identity());
|
||||
|
||||
let expected = G1Affine::from(G1Affine::generator() * c);
|
||||
|
||||
assert_eq!(p, pairing(&expected, &G2Affine::generator()));
|
||||
assert_eq!(
|
||||
p,
|
||||
pairing(&G1Affine::generator(), &G2Affine::generator()) * c
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unitary() {
|
||||
let g = G1Affine::generator();
|
||||
let h = G2Affine::generator();
|
||||
let p = -pairing(&g, &h);
|
||||
let q = pairing(&g, &-h);
|
||||
let r = pairing(&-g, &h);
|
||||
|
||||
assert_eq!(p, q);
|
||||
assert_eq!(q, r);
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[test]
|
||||
fn test_multi_miller_loop() {
|
||||
let a1 = G1Affine::generator();
|
||||
let b1 = G2Affine::generator();
|
||||
|
||||
let a2 = G1Affine::from(
|
||||
G1Affine::generator() * Scalar::from_raw([1, 2, 3, 4]).invert().unwrap().square(),
|
||||
);
|
||||
let b2 = G2Affine::from(
|
||||
G2Affine::generator() * Scalar::from_raw([4, 2, 2, 4]).invert().unwrap().square(),
|
||||
);
|
||||
|
||||
let a3 = G1Affine::identity();
|
||||
let b3 = G2Affine::from(
|
||||
G2Affine::generator() * Scalar::from_raw([9, 2, 2, 4]).invert().unwrap().square(),
|
||||
);
|
||||
|
||||
let a4 = G1Affine::from(
|
||||
G1Affine::generator() * Scalar::from_raw([5, 5, 5, 5]).invert().unwrap().square(),
|
||||
);
|
||||
let b4 = G2Affine::identity();
|
||||
|
||||
let a5 = G1Affine::from(
|
||||
G1Affine::generator() * Scalar::from_raw([323, 32, 3, 1]).invert().unwrap().square(),
|
||||
);
|
||||
let b5 = G2Affine::from(
|
||||
G2Affine::generator() * Scalar::from_raw([4, 2, 2, 9099]).invert().unwrap().square(),
|
||||
);
|
||||
|
||||
let b1_prepared = G2Prepared::from(b1);
|
||||
let b2_prepared = G2Prepared::from(b2);
|
||||
let b3_prepared = G2Prepared::from(b3);
|
||||
let b4_prepared = G2Prepared::from(b4);
|
||||
let b5_prepared = G2Prepared::from(b5);
|
||||
|
||||
let expected = pairing(&a1, &b1)
|
||||
+ pairing(&a2, &b2)
|
||||
+ pairing(&a3, &b3)
|
||||
+ pairing(&a4, &b4)
|
||||
+ pairing(&a5, &b5);
|
||||
|
||||
let test = multi_miller_loop(&[
|
||||
(&a1, &b1_prepared),
|
||||
(&a2, &b2_prepared),
|
||||
(&a3, &b3_prepared),
|
||||
(&a4, &b4_prepared),
|
||||
(&a5, &b5_prepared),
|
||||
])
|
||||
.final_exponentiation();
|
||||
|
||||
assert_eq!(expected, test);
|
||||
}
|
1076
bls12_381/src/scalar.rs
Normal file
1076
bls12_381/src/scalar.rs
Normal file
File diff suppressed because it is too large
Load Diff
BIN
bls12_381/src/tests/g1_compressed_valid_test_vectors.dat
Normal file
BIN
bls12_381/src/tests/g1_compressed_valid_test_vectors.dat
Normal file
Binary file not shown.
BIN
bls12_381/src/tests/g1_uncompressed_valid_test_vectors.dat
Normal file
BIN
bls12_381/src/tests/g1_uncompressed_valid_test_vectors.dat
Normal file
Binary file not shown.
BIN
bls12_381/src/tests/g2_compressed_valid_test_vectors.dat
Normal file
BIN
bls12_381/src/tests/g2_compressed_valid_test_vectors.dat
Normal file
Binary file not shown.
BIN
bls12_381/src/tests/g2_uncompressed_valid_test_vectors.dat
Normal file
BIN
bls12_381/src/tests/g2_uncompressed_valid_test_vectors.dat
Normal file
Binary file not shown.
230
bls12_381/src/tests/mod.rs
Normal file
230
bls12_381/src/tests/mod.rs
Normal file
@ -0,0 +1,230 @@
|
||||
use super::*;
|
||||
|
||||
macro_rules! test_vectors {
|
||||
($projective:ident, $affine:ident, $serialize:ident, $deserialize:ident, $expected:ident) => {
|
||||
let mut e = $projective::identity();
|
||||
|
||||
let mut v = vec![];
|
||||
{
|
||||
let mut expected = $expected;
|
||||
for _ in 0..1000 {
|
||||
let e_affine = $affine::from(e);
|
||||
let encoded = e_affine.$serialize();
|
||||
v.extend_from_slice(&encoded[..]);
|
||||
|
||||
let mut decoded = encoded;
|
||||
let len_of_encoding = decoded.len();
|
||||
(&mut decoded[..]).copy_from_slice(&expected[0..len_of_encoding]);
|
||||
expected = &expected[len_of_encoding..];
|
||||
let decoded = $affine::$deserialize(&decoded).unwrap();
|
||||
assert_eq!(e_affine, decoded);
|
||||
|
||||
e = &e + &$projective::generator();
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(&v[..], $expected);
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn g1_uncompressed_valid_test_vectors() {
|
||||
let bytes: &'static [u8] = include_bytes!("g1_uncompressed_valid_test_vectors.dat");
|
||||
test_vectors!(
|
||||
G1Projective,
|
||||
G1Affine,
|
||||
to_uncompressed,
|
||||
from_uncompressed,
|
||||
bytes
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn g1_compressed_valid_test_vectors() {
|
||||
let bytes: &'static [u8] = include_bytes!("g1_compressed_valid_test_vectors.dat");
|
||||
test_vectors!(
|
||||
G1Projective,
|
||||
G1Affine,
|
||||
to_compressed,
|
||||
from_compressed,
|
||||
bytes
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn g2_uncompressed_valid_test_vectors() {
|
||||
let bytes: &'static [u8] = include_bytes!("g2_uncompressed_valid_test_vectors.dat");
|
||||
test_vectors!(
|
||||
G2Projective,
|
||||
G2Affine,
|
||||
to_uncompressed,
|
||||
from_uncompressed,
|
||||
bytes
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn g2_compressed_valid_test_vectors() {
|
||||
let bytes: &'static [u8] = include_bytes!("g2_compressed_valid_test_vectors.dat");
|
||||
test_vectors!(
|
||||
G2Projective,
|
||||
G2Affine,
|
||||
to_compressed,
|
||||
from_compressed,
|
||||
bytes
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pairing_result_against_relic() {
|
||||
/*
|
||||
Sent to me from Diego Aranha (author of RELIC library):
|
||||
1250EBD871FC0A92 A7B2D83168D0D727 272D441BEFA15C50 3DD8E90CE98DB3E7 B6D194F60839C508 A84305AACA1789B6
|
||||
089A1C5B46E5110B 86750EC6A5323488 68A84045483C92B7 AF5AF689452EAFAB F1A8943E50439F1D 59882A98EAA0170F
|
||||
1368BB445C7C2D20 9703F239689CE34C 0378A68E72A6B3B2 16DA0E22A5031B54 DDFF57309396B38C 881C4C849EC23E87
|
||||
193502B86EDB8857 C273FA075A505129 37E0794E1E65A761 7C90D8BD66065B1F FFE51D7A579973B1 315021EC3C19934F
|
||||
01B2F522473D1713 91125BA84DC4007C FBF2F8DA752F7C74 185203FCCA589AC7 19C34DFFBBAAD843 1DAD1C1FB597AAA5
|
||||
018107154F25A764 BD3C79937A45B845 46DA634B8F6BE14A 8061E55CCEBA478B 23F7DACAA35C8CA7 8BEAE9624045B4B6
|
||||
19F26337D205FB46 9CD6BD15C3D5A04D C88784FBB3D0B2DB DEA54D43B2B73F2C BB12D58386A8703E 0F948226E47EE89D
|
||||
06FBA23EB7C5AF0D 9F80940CA771B6FF D5857BAAF222EB95 A7D2809D61BFE02E 1BFD1B68FF02F0B8 102AE1C2D5D5AB1A
|
||||
11B8B424CD48BF38 FCEF68083B0B0EC5 C81A93B330EE1A67 7D0D15FF7B984E89 78EF48881E32FAC9 1B93B47333E2BA57
|
||||
03350F55A7AEFCD3 C31B4FCB6CE5771C C6A0E9786AB59733 20C806AD36082910 7BA810C5A09FFDD9 BE2291A0C25A99A2
|
||||
04C581234D086A99 02249B64728FFD21 A189E87935A95405 1C7CDBA7B3872629 A4FAFC05066245CB 9108F0242D0FE3EF
|
||||
0F41E58663BF08CF 068672CBD01A7EC7 3BACA4D72CA93544 DEFF686BFD6DF543 D48EAA24AFE47E1E FDE449383B676631
|
||||
*/
|
||||
|
||||
let a = G1Affine::generator();
|
||||
let b = G2Affine::generator();
|
||||
|
||||
use super::fp::Fp;
|
||||
use super::fp12::Fp12;
|
||||
use super::fp2::Fp2;
|
||||
use super::fp6::Fp6;
|
||||
|
||||
let res = pairing(&a, &b);
|
||||
|
||||
let prep = G2Prepared::from(b);
|
||||
|
||||
assert_eq!(
|
||||
res,
|
||||
multi_miller_loop(&[(&a, &prep)]).final_exponentiation()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
res.0,
|
||||
Fp12 {
|
||||
c0: Fp6 {
|
||||
c0: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x1972_e433_a01f_85c5,
|
||||
0x97d3_2b76_fd77_2538,
|
||||
0xc8ce_546f_c96b_cdf9,
|
||||
0xcef6_3e73_66d4_0614,
|
||||
0xa611_3427_8184_3780,
|
||||
0x13f3_448a_3fc6_d825,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xd263_31b0_2e9d_6995,
|
||||
0x9d68_a482_f779_7e7d,
|
||||
0x9c9b_2924_8d39_ea92,
|
||||
0xf480_1ca2_e131_07aa,
|
||||
0xa16c_0732_bdbc_b066,
|
||||
0x083c_a4af_ba36_0478,
|
||||
])
|
||||
},
|
||||
c1: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x59e2_61db_0916_b641,
|
||||
0x2716_b6f4_b23e_960d,
|
||||
0xc8e5_5b10_a0bd_9c45,
|
||||
0x0bdb_0bd9_9c4d_eda8,
|
||||
0x8cf8_9ebf_57fd_aac5,
|
||||
0x12d6_b792_9e77_7a5e,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x5fc8_5188_b0e1_5f35,
|
||||
0x34a0_6e3a_8f09_6365,
|
||||
0xdb31_26a6_e02a_d62c,
|
||||
0xfc6f_5aa9_7d9a_990b,
|
||||
0xa12f_55f5_eb89_c210,
|
||||
0x1723_703a_926f_8889,
|
||||
])
|
||||
},
|
||||
c2: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x9358_8f29_7182_8778,
|
||||
0x43f6_5b86_11ab_7585,
|
||||
0x3183_aaf5_ec27_9fdf,
|
||||
0xfa73_d7e1_8ac9_9df6,
|
||||
0x64e1_76a6_a64c_99b0,
|
||||
0x179f_a78c_5838_8f1f,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x672a_0a11_ca2a_ef12,
|
||||
0x0d11_b9b5_2aa3_f16b,
|
||||
0xa444_12d0_699d_056e,
|
||||
0xc01d_0177_221a_5ba5,
|
||||
0x66e0_cede_6c73_5529,
|
||||
0x05f5_a71e_9fdd_c339,
|
||||
])
|
||||
}
|
||||
},
|
||||
c1: Fp6 {
|
||||
c0: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0xd30a_88a1_b062_c679,
|
||||
0x5ac5_6a5d_35fc_8304,
|
||||
0xd0c8_34a6_a81f_290d,
|
||||
0xcd54_30c2_da37_07c7,
|
||||
0xf0c2_7ff7_8050_0af0,
|
||||
0x0924_5da6_e2d7_2eae,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0x9f2e_0676_791b_5156,
|
||||
0xe2d1_c823_4918_fe13,
|
||||
0x4c9e_459f_3c56_1bf4,
|
||||
0xa3e8_5e53_b9d3_e3c1,
|
||||
0x820a_121e_21a7_0020,
|
||||
0x15af_6183_41c5_9acc,
|
||||
])
|
||||
},
|
||||
c1: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x7c95_658c_2499_3ab1,
|
||||
0x73eb_3872_1ca8_86b9,
|
||||
0x5256_d749_4774_34bc,
|
||||
0x8ba4_1902_ea50_4a8b,
|
||||
0x04a3_d3f8_0c86_ce6d,
|
||||
0x18a6_4a87_fb68_6eaa,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xbb83_e71b_b920_cf26,
|
||||
0x2a52_77ac_92a7_3945,
|
||||
0xfc0e_e59f_94f0_46a0,
|
||||
0x7158_cdf3_7860_58f7,
|
||||
0x7cc1_061b_82f9_45f6,
|
||||
0x03f8_47aa_9fdb_e567,
|
||||
])
|
||||
},
|
||||
c2: Fp2 {
|
||||
c0: Fp::from_raw_unchecked([
|
||||
0x8078_dba5_6134_e657,
|
||||
0x1cd7_ec9a_4399_8a6e,
|
||||
0xb1aa_599a_1a99_3766,
|
||||
0xc9a0_f62f_0842_ee44,
|
||||
0x8e15_9be3_b605_dffa,
|
||||
0x0c86_ba0d_4af1_3fc2,
|
||||
]),
|
||||
c1: Fp::from_raw_unchecked([
|
||||
0xe80f_f2a0_6a52_ffb1,
|
||||
0x7694_ca48_721a_906c,
|
||||
0x7583_183e_03b0_8514,
|
||||
0xf567_afdd_40ce_e4e2,
|
||||
0x9a6d_96d2_e526_a5fc,
|
||||
0x197e_9f49_861f_2242,
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
174
bls12_381/src/util.rs
Normal file
174
bls12_381/src/util.rs
Normal file
@ -0,0 +1,174 @@
|
||||
/// Compute a + b + carry, returning the result and the new carry over.
|
||||
#[inline(always)]
|
||||
pub 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 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 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)
|
||||
}
|
||||
|
||||
macro_rules! impl_add_binop_specify_output {
|
||||
($lhs:ident, $rhs:ident, $output:ident) => {
|
||||
impl<'b> Add<&'b $rhs> for $lhs {
|
||||
type Output = $output;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: &'b $rhs) -> $output {
|
||||
&self + rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Add<$rhs> for &'a $lhs {
|
||||
type Output = $output;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: $rhs) -> $output {
|
||||
self + &rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl 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> Sub<&'b $rhs> for $lhs {
|
||||
type Output = $output;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: &'b $rhs) -> $output {
|
||||
&self - rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Sub<$rhs> for &'a $lhs {
|
||||
type Output = $output;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: $rhs) -> $output {
|
||||
self - &rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl 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> Mul<&'b $rhs> for $lhs {
|
||||
type Output = $output;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, rhs: &'b $rhs) -> $output {
|
||||
&self * rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Mul<$rhs> for &'a $lhs {
|
||||
type Output = $output;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, rhs: $rhs) -> $output {
|
||||
self * &rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl 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 SubAssign<$rhs> for $lhs {
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, rhs: $rhs) {
|
||||
*self = &*self - &rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<$rhs> for $lhs {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, rhs: $rhs) {
|
||||
*self = &*self + &rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> SubAssign<&'b $rhs> for $lhs {
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, rhs: &'b $rhs) {
|
||||
*self = &*self - rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> 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 MulAssign<$rhs> for $lhs {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, rhs: $rhs) {
|
||||
*self = &*self * &rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> MulAssign<&'b $rhs> for $lhs {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, rhs: &'b $rhs) {
|
||||
*self = &*self * rhs;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
@ -11,13 +11,15 @@ repository = "https://github.com/ebfull/ff"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
byteorder = "1"
|
||||
byteorder = { version = "1", optional = true }
|
||||
ff_derive = { version = "0.6", path = "ff_derive", optional = true }
|
||||
rand_core = "0.5"
|
||||
rand_core = { version = "0.5", default-features = false }
|
||||
subtle = { version = "2.2.1", default-features = false, features = ["i128"] }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
default = ["std"]
|
||||
derive = ["ff_derive"]
|
||||
std = ["byteorder"]
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "actively-developed" }
|
||||
|
@ -40,18 +40,18 @@ pub fn prime_field(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let mut cur = BigUint::one() << 64; // always 64-bit limbs for now
|
||||
while cur < mod2 {
|
||||
limbs += 1;
|
||||
cur = cur << 64;
|
||||
cur <<= 64;
|
||||
}
|
||||
}
|
||||
|
||||
let mut gen = proc_macro2::TokenStream::new();
|
||||
|
||||
let (constants_impl, sqrt_impl) =
|
||||
prime_field_constants_and_sqrt(&ast.ident, &repr_ident, modulus, limbs, generator);
|
||||
prime_field_constants_and_sqrt(&ast.ident, &repr_ident, &modulus, limbs, generator);
|
||||
|
||||
gen.extend(constants_impl);
|
||||
gen.extend(prime_field_repr_impl(&repr_ident, limbs));
|
||||
gen.extend(prime_field_impl(&ast.ident, &repr_ident, limbs));
|
||||
gen.extend(prime_field_impl(&ast.ident, &repr_ident, &modulus, limbs));
|
||||
gen.extend(sqrt_impl);
|
||||
|
||||
// Return the generated impl
|
||||
@ -60,23 +60,16 @@ pub fn prime_field(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
|
||||
/// Fetches the ident being wrapped by the type we're deriving.
|
||||
fn fetch_wrapped_ident(body: &syn::Data) -> Option<syn::Ident> {
|
||||
match body {
|
||||
&syn::Data::Struct(ref variant_data) => match variant_data.fields {
|
||||
syn::Fields::Unnamed(ref fields) => {
|
||||
if fields.unnamed.len() == 1 {
|
||||
match fields.unnamed[0].ty {
|
||||
syn::Type::Path(ref path) => {
|
||||
if path.path.segments.len() == 1 {
|
||||
return Some(path.path.segments[0].ident.clone());
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
if let syn::Data::Struct(ref variant_data) = body {
|
||||
if let syn::Fields::Unnamed(ref fields) = variant_data.fields {
|
||||
if fields.unnamed.len() == 1 {
|
||||
if let syn::Type::Path(ref path) = fields.unnamed[0].ty {
|
||||
if path.path.segments.len() == 1 {
|
||||
return Some(path.path.segments[0].ident.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
};
|
||||
|
||||
None
|
||||
@ -113,9 +106,9 @@ fn prime_field_repr_impl(repr: &syn::Ident, limbs: usize) -> proc_macro2::TokenS
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Default)]
|
||||
pub struct #repr(pub [u64; #limbs]);
|
||||
|
||||
impl ::std::fmt::Debug for #repr
|
||||
impl ::core::fmt::Debug for #repr
|
||||
{
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
||||
write!(f, "0x")?;
|
||||
for i in self.0.iter().rev() {
|
||||
write!(f, "{:016x}", *i)?;
|
||||
@ -125,8 +118,8 @@ fn prime_field_repr_impl(repr: &syn::Ident, limbs: usize) -> proc_macro2::TokenS
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for #repr {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
impl ::core::fmt::Display for #repr {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
||||
write!(f, "0x")?;
|
||||
for i in self.0.iter().rev() {
|
||||
write!(f, "{:016x}", *i)?;
|
||||
@ -153,7 +146,7 @@ fn prime_field_repr_impl(repr: &syn::Ident, limbs: usize) -> proc_macro2::TokenS
|
||||
impl From<u64> for #repr {
|
||||
#[inline(always)]
|
||||
fn from(val: u64) -> #repr {
|
||||
use std::default::Default;
|
||||
use core::default::Default;
|
||||
|
||||
let mut repr = Self::default();
|
||||
repr.0[0] = val;
|
||||
@ -163,22 +156,22 @@ fn prime_field_repr_impl(repr: &syn::Ident, limbs: usize) -> proc_macro2::TokenS
|
||||
|
||||
impl Ord for #repr {
|
||||
#[inline(always)]
|
||||
fn cmp(&self, other: &#repr) -> ::std::cmp::Ordering {
|
||||
fn cmp(&self, other: &#repr) -> ::core::cmp::Ordering {
|
||||
for (a, b) in self.0.iter().rev().zip(other.0.iter().rev()) {
|
||||
if a < b {
|
||||
return ::std::cmp::Ordering::Less
|
||||
return ::core::cmp::Ordering::Less
|
||||
} else if a > b {
|
||||
return ::std::cmp::Ordering::Greater
|
||||
return ::core::cmp::Ordering::Greater
|
||||
}
|
||||
}
|
||||
|
||||
::std::cmp::Ordering::Equal
|
||||
::core::cmp::Ordering::Equal
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for #repr {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, other: &#repr) -> Option<::std::cmp::Ordering> {
|
||||
fn partial_cmp(&self, other: &#repr) -> Option<::core::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
@ -209,7 +202,7 @@ fn prime_field_repr_impl(repr: &syn::Ident, limbs: usize) -> proc_macro2::TokenS
|
||||
while n >= 64 {
|
||||
let mut t = 0;
|
||||
for i in self.0.iter_mut().rev() {
|
||||
::std::mem::swap(&mut t, i);
|
||||
::core::mem::swap(&mut t, i);
|
||||
}
|
||||
n -= 64;
|
||||
}
|
||||
@ -257,7 +250,7 @@ fn prime_field_repr_impl(repr: &syn::Ident, limbs: usize) -> proc_macro2::TokenS
|
||||
while n >= 64 {
|
||||
let mut t = 0;
|
||||
for i in &mut self.0 {
|
||||
::std::mem::swap(&mut t, i);
|
||||
::core::mem::swap(&mut t, i);
|
||||
}
|
||||
n -= 64;
|
||||
}
|
||||
@ -315,7 +308,7 @@ fn biguint_to_real_u64_vec(mut v: BigUint, limbs: usize) -> Vec<u64> {
|
||||
|
||||
while v > BigUint::zero() {
|
||||
ret.push((&v % &m).to_u64().unwrap());
|
||||
v = v >> 64;
|
||||
v >>= 64;
|
||||
}
|
||||
|
||||
while ret.len() < limbs {
|
||||
@ -337,7 +330,7 @@ fn biguint_num_bits(mut v: BigUint) -> u32 {
|
||||
let mut bits = 0;
|
||||
|
||||
while v != BigUint::zero() {
|
||||
v = v >> 1;
|
||||
v >>= 1;
|
||||
bits += 1;
|
||||
}
|
||||
|
||||
@ -383,7 +376,7 @@ fn test_exp() {
|
||||
fn prime_field_constants_and_sqrt(
|
||||
name: &syn::Ident,
|
||||
repr: &syn::Ident,
|
||||
modulus: BigUint,
|
||||
modulus: &BigUint,
|
||||
limbs: usize,
|
||||
generator: BigUint,
|
||||
) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
|
||||
@ -396,129 +389,102 @@ fn prime_field_constants_and_sqrt(
|
||||
let repr_shave_bits = (64 * limbs as u32) - biguint_num_bits(modulus.clone());
|
||||
|
||||
// Compute R = 2**(64 * limbs) mod m
|
||||
let r = (BigUint::one() << (limbs * 64)) % &modulus;
|
||||
let r = (BigUint::one() << (limbs * 64)) % modulus;
|
||||
|
||||
// modulus - 1 = 2^s * t
|
||||
let mut s: u32 = 0;
|
||||
let mut t = &modulus - BigUint::from_str("1").unwrap();
|
||||
let mut t = modulus - BigUint::from_str("1").unwrap();
|
||||
while t.is_even() {
|
||||
t = t >> 1;
|
||||
t >>= 1;
|
||||
s += 1;
|
||||
}
|
||||
|
||||
// Compute 2^s root of unity given the generator
|
||||
let root_of_unity = biguint_to_u64_vec(
|
||||
(exp(generator.clone(), &t, &modulus) * &r) % &modulus,
|
||||
limbs,
|
||||
);
|
||||
let generator = biguint_to_u64_vec((generator.clone() * &r) % &modulus, limbs);
|
||||
let root_of_unity =
|
||||
biguint_to_u64_vec((exp(generator.clone(), &t, &modulus) * &r) % modulus, limbs);
|
||||
let generator = biguint_to_u64_vec((generator.clone() * &r) % modulus, limbs);
|
||||
|
||||
let mod_minus_1_over_2 =
|
||||
biguint_to_u64_vec((&modulus - BigUint::from_str("1").unwrap()) >> 1, limbs);
|
||||
let legendre_impl = quote! {
|
||||
fn legendre(&self) -> ::ff::LegendreSymbol {
|
||||
// s = self^((modulus - 1) // 2)
|
||||
let s = self.pow(#mod_minus_1_over_2);
|
||||
if s == Self::zero() {
|
||||
::ff::LegendreSymbol::Zero
|
||||
} else if s == Self::one() {
|
||||
::ff::LegendreSymbol::QuadraticResidue
|
||||
} else {
|
||||
::ff::LegendreSymbol::QuadraticNonResidue
|
||||
let sqrt_impl = if (modulus % BigUint::from_str("4").unwrap())
|
||||
== BigUint::from_str("3").unwrap()
|
||||
{
|
||||
let mod_plus_1_over_4 =
|
||||
biguint_to_u64_vec((modulus + BigUint::from_str("1").unwrap()) >> 2, limbs);
|
||||
|
||||
quote! {
|
||||
impl ::ff::SqrtField for #name {
|
||||
fn sqrt(&self) -> ::subtle::CtOption<Self> {
|
||||
use ::subtle::ConstantTimeEq;
|
||||
|
||||
// Because r = 3 (mod 4)
|
||||
// sqrt can be done with only one exponentiation,
|
||||
// via the computation of self^((r + 1) // 4) (mod r)
|
||||
let sqrt = self.pow_vartime(#mod_plus_1_over_4);
|
||||
|
||||
::subtle::CtOption::new(
|
||||
sqrt,
|
||||
(sqrt * &sqrt).ct_eq(self), // Only return Some if it's the square root.
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (modulus % BigUint::from_str("16").unwrap()) == BigUint::from_str("1").unwrap() {
|
||||
let t_minus_1_over_2 = biguint_to_u64_vec((&t - BigUint::one()) >> 1, limbs);
|
||||
|
||||
quote! {
|
||||
impl ::ff::SqrtField for #name {
|
||||
fn sqrt(&self) -> ::subtle::CtOption<Self> {
|
||||
// Tonelli-Shank's algorithm for q mod 16 = 1
|
||||
// https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5)
|
||||
use ::subtle::{ConditionallySelectable, ConstantTimeEq};
|
||||
|
||||
// w = self^((t - 1) // 2)
|
||||
let w = self.pow_vartime(#t_minus_1_over_2);
|
||||
|
||||
let mut v = S;
|
||||
let mut x = *self * &w;
|
||||
let mut b = x * &w;
|
||||
|
||||
// Initialize z as the 2^S root of unity.
|
||||
let mut z = #name(ROOT_OF_UNITY);
|
||||
|
||||
for max_v in (1..=S).rev() {
|
||||
let mut k = 1;
|
||||
let mut tmp = b.square();
|
||||
let mut j_less_than_v: ::subtle::Choice = 1.into();
|
||||
|
||||
for j in 2..max_v {
|
||||
let tmp_is_one = tmp.ct_eq(&#name::one());
|
||||
let squared = #name::conditional_select(&tmp, &z, tmp_is_one).square();
|
||||
tmp = #name::conditional_select(&squared, &tmp, tmp_is_one);
|
||||
let new_z = #name::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 = #name::conditional_select(&z, &new_z, j_less_than_v);
|
||||
}
|
||||
|
||||
let result = x * &z;
|
||||
x = #name::conditional_select(&result, &x, b.ct_eq(&#name::one()));
|
||||
z = z.square();
|
||||
b *= &z;
|
||||
v = k;
|
||||
}
|
||||
|
||||
::subtle::CtOption::new(
|
||||
x,
|
||||
(x * &x).ct_eq(self), // Only return Some if it's the square root.
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {}
|
||||
};
|
||||
|
||||
let sqrt_impl =
|
||||
if (&modulus % BigUint::from_str("4").unwrap()) == BigUint::from_str("3").unwrap() {
|
||||
let mod_minus_3_over_4 =
|
||||
biguint_to_u64_vec((&modulus - BigUint::from_str("3").unwrap()) >> 2, limbs);
|
||||
|
||||
// Compute -R as (m - r)
|
||||
let rneg = biguint_to_u64_vec(&modulus - &r, limbs);
|
||||
|
||||
quote! {
|
||||
impl ::ff::SqrtField for #name {
|
||||
#legendre_impl
|
||||
|
||||
fn sqrt(&self) -> Option<Self> {
|
||||
// Shank's algorithm for q mod 4 = 3
|
||||
// https://eprint.iacr.org/2012/685.pdf (page 9, algorithm 2)
|
||||
|
||||
let mut a1 = self.pow(#mod_minus_3_over_4);
|
||||
|
||||
let mut a0 = a1;
|
||||
a0.square();
|
||||
a0.mul_assign(self);
|
||||
|
||||
if a0.0 == #repr(#rneg) {
|
||||
None
|
||||
} else {
|
||||
a1.mul_assign(self);
|
||||
Some(a1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (&modulus % BigUint::from_str("16").unwrap()) == BigUint::from_str("1").unwrap() {
|
||||
let t_plus_1_over_2 = biguint_to_u64_vec((&t + BigUint::one()) >> 1, limbs);
|
||||
let t = biguint_to_u64_vec(t.clone(), limbs);
|
||||
|
||||
quote! {
|
||||
impl ::ff::SqrtField for #name {
|
||||
#legendre_impl
|
||||
|
||||
fn sqrt(&self) -> Option<Self> {
|
||||
// Tonelli-Shank's algorithm for q mod 16 = 1
|
||||
// https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5)
|
||||
|
||||
match self.legendre() {
|
||||
::ff::LegendreSymbol::Zero => Some(*self),
|
||||
::ff::LegendreSymbol::QuadraticNonResidue => None,
|
||||
::ff::LegendreSymbol::QuadraticResidue => {
|
||||
let mut c = #name(ROOT_OF_UNITY);
|
||||
let mut r = self.pow(#t_plus_1_over_2);
|
||||
let mut t = self.pow(#t);
|
||||
let mut m = S;
|
||||
|
||||
while t != Self::one() {
|
||||
let mut i = 1;
|
||||
{
|
||||
let mut t2i = t;
|
||||
t2i.square();
|
||||
loop {
|
||||
if t2i == Self::one() {
|
||||
break;
|
||||
}
|
||||
t2i.square();
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
for _ in 0..(m - i - 1) {
|
||||
c.square();
|
||||
}
|
||||
r.mul_assign(&c);
|
||||
c.square();
|
||||
t.mul_assign(&c);
|
||||
m = i;
|
||||
}
|
||||
|
||||
Some(r)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {}
|
||||
};
|
||||
|
||||
// Compute R^2 mod m
|
||||
let r2 = biguint_to_u64_vec((&r * &r) % &modulus, limbs);
|
||||
let r2 = biguint_to_u64_vec((&r * &r) % modulus, limbs);
|
||||
|
||||
let r = biguint_to_u64_vec(r, limbs);
|
||||
let modulus = biguint_to_real_u64_vec(modulus, limbs);
|
||||
let modulus = biguint_to_real_u64_vec(modulus.clone(), limbs);
|
||||
|
||||
// Compute -m^-1 mod 2**64 by exponentiating by totient(2**64) - 1
|
||||
let mut inv = 1u64;
|
||||
@ -567,6 +533,7 @@ fn prime_field_constants_and_sqrt(
|
||||
fn prime_field_impl(
|
||||
name: &syn::Ident,
|
||||
repr: &syn::Ident,
|
||||
modulus: &BigUint,
|
||||
limbs: usize,
|
||||
) -> proc_macro2::TokenStream {
|
||||
// Returns r{n} as an ident.
|
||||
@ -710,12 +677,14 @@ fn prime_field_impl(
|
||||
|
||||
let mut mont_calling = proc_macro2::TokenStream::new();
|
||||
mont_calling.append_separated(
|
||||
(0..(limbs * 2)).map(|i| get_temp(i)),
|
||||
(0..(limbs * 2)).map(get_temp),
|
||||
proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone),
|
||||
);
|
||||
|
||||
gen.extend(quote! {
|
||||
self.mont_reduce(#mont_calling);
|
||||
let mut ret = *self;
|
||||
ret.mont_reduce(#mont_calling);
|
||||
ret
|
||||
});
|
||||
|
||||
gen
|
||||
@ -756,7 +725,7 @@ fn prime_field_impl(
|
||||
|
||||
let mut mont_calling = proc_macro2::TokenStream::new();
|
||||
mont_calling.append_separated(
|
||||
(0..(limbs * 2)).map(|i| get_temp(i)),
|
||||
(0..(limbs * 2)).map(get_temp),
|
||||
proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone),
|
||||
);
|
||||
|
||||
@ -767,10 +736,44 @@ fn prime_field_impl(
|
||||
gen
|
||||
}
|
||||
|
||||
/// Generates an implementation of multiplicative inversion within the target prime
|
||||
/// field.
|
||||
fn inv_impl(
|
||||
a: proc_macro2::TokenStream,
|
||||
name: &syn::Ident,
|
||||
modulus: &BigUint,
|
||||
limbs: usize,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let mod_minus_2 = biguint_to_u64_vec(modulus - BigUint::from(2u64), limbs);
|
||||
|
||||
// TODO: Improve on this by computing an addition chain for mod_minus_two
|
||||
quote! {
|
||||
use ::subtle::ConstantTimeEq;
|
||||
|
||||
// By Euler's theorem, if `a` is coprime to `p` (i.e. `gcd(a, p) = 1`), then:
|
||||
// a^-1 ≡ a^(phi(p) - 1) mod p
|
||||
//
|
||||
// `ff_derive` requires that `p` is prime; in this case, `phi(p) = p - 1`, and
|
||||
// thus:
|
||||
// a^-1 ≡ a^(p - 2) mod p
|
||||
let inv = #a.pow_vartime(#mod_minus_2);
|
||||
|
||||
::subtle::CtOption::new(inv, !#a.ct_eq(&#name::zero()))
|
||||
}
|
||||
}
|
||||
|
||||
let squaring_impl = sqr_impl(quote! {self}, limbs);
|
||||
let multiply_impl = mul_impl(quote! {self}, quote! {other}, limbs);
|
||||
let invert_impl = inv_impl(quote! {self}, name, modulus, limbs);
|
||||
let montgomery_impl = mont_impl(limbs);
|
||||
|
||||
// (self.0).0[0].ct_eq(&(other.0).0[0]) & (self.0).0[1].ct_eq(&(other.0).0[1]) & ...
|
||||
let mut ct_eq_impl = proc_macro2::TokenStream::new();
|
||||
ct_eq_impl.append_separated(
|
||||
(0..limbs).map(|i| quote! { (self.0).0[#i].ct_eq(&(other.0).0[#i]) }),
|
||||
proc_macro2::Punct::new('&', proc_macro2::Spacing::Alone),
|
||||
);
|
||||
|
||||
// (self.0).0[0], (self.0).0[1], ..., 0, 0, 0, 0, ...
|
||||
let mut into_repr_params = proc_macro2::TokenStream::new();
|
||||
into_repr_params.append_separated(
|
||||
@ -783,25 +786,37 @@ fn prime_field_impl(
|
||||
let top_limb_index = limbs - 1;
|
||||
|
||||
quote! {
|
||||
impl ::std::marker::Copy for #name { }
|
||||
impl ::core::marker::Copy for #name { }
|
||||
|
||||
impl ::std::clone::Clone for #name {
|
||||
impl ::core::clone::Clone for #name {
|
||||
fn clone(&self) -> #name {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::cmp::PartialEq for #name {
|
||||
impl ::core::default::Default for #name {
|
||||
fn default() -> #name {
|
||||
#name::zero()
|
||||
}
|
||||
}
|
||||
|
||||
impl ::subtle::ConstantTimeEq for #name {
|
||||
fn ct_eq(&self, other: &#name) -> ::subtle::Choice {
|
||||
#ct_eq_impl
|
||||
}
|
||||
}
|
||||
|
||||
impl ::core::cmp::PartialEq for #name {
|
||||
fn eq(&self, other: &#name) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::cmp::Eq for #name { }
|
||||
impl ::core::cmp::Eq for #name { }
|
||||
|
||||
impl ::std::fmt::Debug for #name
|
||||
impl ::core::fmt::Debug for #name
|
||||
{
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
||||
write!(f, "{}({:?})", stringify!(#name), self.into_repr())
|
||||
}
|
||||
}
|
||||
@ -809,20 +824,20 @@ fn prime_field_impl(
|
||||
/// Elements are ordered lexicographically.
|
||||
impl Ord for #name {
|
||||
#[inline(always)]
|
||||
fn cmp(&self, other: &#name) -> ::std::cmp::Ordering {
|
||||
fn cmp(&self, other: &#name) -> ::core::cmp::Ordering {
|
||||
self.into_repr().cmp(&other.into_repr())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for #name {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, other: &#name) -> Option<::std::cmp::Ordering> {
|
||||
fn partial_cmp(&self, other: &#name) -> Option<::core::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for #name {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
impl ::core::fmt::Display for #name {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
||||
write!(f, "{}({})", stringify!(#name), self.into_repr())
|
||||
}
|
||||
}
|
||||
@ -833,6 +848,144 @@ fn prime_field_impl(
|
||||
}
|
||||
}
|
||||
|
||||
impl ::subtle::ConditionallySelectable for #name {
|
||||
fn conditional_select(a: &#name, b: &#name, choice: ::subtle::Choice) -> #name {
|
||||
let mut res = [0u64; #limbs];
|
||||
for i in 0..#limbs {
|
||||
res[i] = u64::conditional_select(&(a.0).0[i], &(b.0).0[i], choice);
|
||||
}
|
||||
#name(#repr(res))
|
||||
}
|
||||
}
|
||||
|
||||
impl ::core::ops::Neg for #name {
|
||||
type Output = #name;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> #name {
|
||||
let mut ret = self;
|
||||
if !ret.is_zero() {
|
||||
let mut tmp = MODULUS;
|
||||
tmp.sub_noborrow(&ret.0);
|
||||
ret.0 = tmp;
|
||||
}
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> ::core::ops::Add<&'r #name> for #name {
|
||||
type Output = #name;
|
||||
|
||||
#[inline]
|
||||
fn add(self, other: &#name) -> #name {
|
||||
let mut ret = self;
|
||||
ret.add_assign(other);
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl ::core::ops::Add for #name {
|
||||
type Output = #name;
|
||||
|
||||
#[inline]
|
||||
fn add(self, other: #name) -> Self {
|
||||
self + &other
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> ::core::ops::AddAssign<&'r #name> for #name {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, other: &#name) {
|
||||
// This cannot exceed the backing capacity.
|
||||
self.0.add_nocarry(&other.0);
|
||||
|
||||
// However, it may need to be reduced.
|
||||
self.reduce();
|
||||
}
|
||||
}
|
||||
|
||||
impl ::core::ops::AddAssign for #name {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, other: #name) {
|
||||
self.add_assign(&other);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> ::core::ops::Sub<&'r #name> for #name {
|
||||
type Output = #name;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, other: &#name) -> Self {
|
||||
let mut ret = self;
|
||||
ret.sub_assign(other);
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl ::core::ops::Sub for #name {
|
||||
type Output = #name;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, other: #name) -> Self {
|
||||
self - &other
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> ::core::ops::SubAssign<&'r #name> for #name {
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, other: &#name) {
|
||||
// If `other` is larger than `self`, we'll need to add the modulus to self first.
|
||||
if other.0 > self.0 {
|
||||
self.0.add_nocarry(&MODULUS);
|
||||
}
|
||||
|
||||
self.0.sub_noborrow(&other.0);
|
||||
}
|
||||
}
|
||||
|
||||
impl ::core::ops::SubAssign for #name {
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, other: #name) {
|
||||
self.sub_assign(&other);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> ::core::ops::Mul<&'r #name> for #name {
|
||||
type Output = #name;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, other: &#name) -> Self {
|
||||
let mut ret = self;
|
||||
ret.mul_assign(other);
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl ::core::ops::Mul for #name {
|
||||
type Output = #name;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, other: #name) -> Self {
|
||||
self * &other
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> ::core::ops::MulAssign<&'r #name> for #name {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, other: &#name)
|
||||
{
|
||||
#multiply_impl
|
||||
}
|
||||
}
|
||||
|
||||
impl ::core::ops::MulAssign for #name {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, other: #name)
|
||||
{
|
||||
self.mul_assign(&other);
|
||||
}
|
||||
}
|
||||
|
||||
impl ::ff::PrimeField for #name {
|
||||
type Repr = #repr;
|
||||
|
||||
@ -843,7 +996,7 @@ fn prime_field_impl(
|
||||
|
||||
Ok(r)
|
||||
} else {
|
||||
Err(PrimeFieldDecodingError::NotInField(format!("{}", r.0)))
|
||||
Err(PrimeFieldDecodingError::NotInField)
|
||||
}
|
||||
}
|
||||
|
||||
@ -912,95 +1065,20 @@ fn prime_field_impl(
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn add_assign(&mut self, other: &#name) {
|
||||
fn double(&self) -> Self {
|
||||
let mut ret = *self;
|
||||
|
||||
// This cannot exceed the backing capacity.
|
||||
self.0.add_nocarry(&other.0);
|
||||
ret.0.mul2();
|
||||
|
||||
// However, it may need to be reduced.
|
||||
self.reduce();
|
||||
ret.reduce();
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn double(&mut self) {
|
||||
// This cannot exceed the backing capacity.
|
||||
self.0.mul2();
|
||||
|
||||
// However, it may need to be reduced.
|
||||
self.reduce();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, other: &#name) {
|
||||
// If `other` is larger than `self`, we'll need to add the modulus to self first.
|
||||
if other.0 > self.0 {
|
||||
self.0.add_nocarry(&MODULUS);
|
||||
}
|
||||
|
||||
self.0.sub_noborrow(&other.0);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn negate(&mut self) {
|
||||
if !self.is_zero() {
|
||||
let mut tmp = MODULUS;
|
||||
tmp.sub_noborrow(&self.0);
|
||||
self.0 = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
fn inverse(&self) -> Option<Self> {
|
||||
if self.is_zero() {
|
||||
None
|
||||
} else {
|
||||
// Guajardo Kumar Paar Pelzl
|
||||
// Efficient Software-Implementation of Finite Fields with Applications to Cryptography
|
||||
// Algorithm 16 (BEA for Inversion in Fp)
|
||||
|
||||
let one = #repr::from(1);
|
||||
|
||||
let mut u = self.0;
|
||||
let mut v = MODULUS;
|
||||
let mut b = #name(R2); // Avoids unnecessary reduction step.
|
||||
let mut c = Self::zero();
|
||||
|
||||
while u != one && v != one {
|
||||
while u.is_even() {
|
||||
u.div2();
|
||||
|
||||
if b.0.is_even() {
|
||||
b.0.div2();
|
||||
} else {
|
||||
b.0.add_nocarry(&MODULUS);
|
||||
b.0.div2();
|
||||
}
|
||||
}
|
||||
|
||||
while v.is_even() {
|
||||
v.div2();
|
||||
|
||||
if c.0.is_even() {
|
||||
c.0.div2();
|
||||
} else {
|
||||
c.0.add_nocarry(&MODULUS);
|
||||
c.0.div2();
|
||||
}
|
||||
}
|
||||
|
||||
if v < u {
|
||||
u.sub_noborrow(&v);
|
||||
b.sub_assign(&c);
|
||||
} else {
|
||||
v.sub_noborrow(&u);
|
||||
c.sub_assign(&b);
|
||||
}
|
||||
}
|
||||
|
||||
if u == one {
|
||||
Some(b)
|
||||
} else {
|
||||
Some(c)
|
||||
}
|
||||
}
|
||||
fn invert(&self) -> ::subtle::CtOption<Self> {
|
||||
#invert_impl
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@ -1009,13 +1087,7 @@ fn prime_field_impl(
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, other: &#name)
|
||||
{
|
||||
#multiply_impl
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn square(&mut self)
|
||||
fn square(&self) -> Self
|
||||
{
|
||||
#squaring_impl
|
||||
}
|
||||
|
122
ff/src/lib.rs
122
ff/src/lib.rs
@ -1,20 +1,50 @@
|
||||
//! This crate provides traits for working with finite fields.
|
||||
|
||||
// Catch documentation errors caused by code changes.
|
||||
#![no_std]
|
||||
#![deny(intra_doc_link_resolution_failure)]
|
||||
#![allow(unused_imports)]
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[macro_use]
|
||||
extern crate std;
|
||||
|
||||
#[cfg(feature = "derive")]
|
||||
pub use ff_derive::*;
|
||||
|
||||
use core::fmt;
|
||||
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
||||
use rand_core::RngCore;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
#[cfg(feature = "std")]
|
||||
use std::io::{self, Read, Write};
|
||||
use subtle::{ConditionallySelectable, CtOption};
|
||||
|
||||
/// This trait represents an element of a field.
|
||||
pub trait Field:
|
||||
Sized + Eq + Copy + Clone + Send + Sync + fmt::Debug + fmt::Display + 'static
|
||||
Sized
|
||||
+ Eq
|
||||
+ Copy
|
||||
+ Clone
|
||||
+ Default
|
||||
+ Send
|
||||
+ Sync
|
||||
+ fmt::Debug
|
||||
+ fmt::Display
|
||||
+ 'static
|
||||
+ ConditionallySelectable
|
||||
+ Add<Output = Self>
|
||||
+ Sub<Output = Self>
|
||||
+ Mul<Output = Self>
|
||||
+ Neg<Output = Self>
|
||||
+ for<'a> Add<&'a Self, Output = Self>
|
||||
+ for<'a> Mul<&'a Self, Output = Self>
|
||||
+ for<'a> Sub<&'a Self, Output = Self>
|
||||
+ MulAssign
|
||||
+ AddAssign
|
||||
+ SubAssign
|
||||
+ for<'a> MulAssign<&'a Self>
|
||||
+ for<'a> AddAssign<&'a Self>
|
||||
+ for<'a> SubAssign<&'a Self>
|
||||
{
|
||||
/// Returns an element chosen uniformly at random using a user-provided RNG.
|
||||
fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self;
|
||||
@ -29,46 +59,35 @@ pub trait Field:
|
||||
fn is_zero(&self) -> bool;
|
||||
|
||||
/// Squares this element.
|
||||
fn square(&mut self);
|
||||
#[must_use]
|
||||
fn square(&self) -> Self;
|
||||
|
||||
/// Doubles this element.
|
||||
fn double(&mut self);
|
||||
#[must_use]
|
||||
fn double(&self) -> Self;
|
||||
|
||||
/// Negates this element.
|
||||
fn negate(&mut self);
|
||||
|
||||
/// Adds another element to this element.
|
||||
fn add_assign(&mut self, other: &Self);
|
||||
|
||||
/// Subtracts another element from this element.
|
||||
fn sub_assign(&mut self, other: &Self);
|
||||
|
||||
/// Multiplies another element by this element.
|
||||
fn mul_assign(&mut self, other: &Self);
|
||||
|
||||
/// Computes the multiplicative inverse of this element, if nonzero.
|
||||
fn inverse(&self) -> Option<Self>;
|
||||
/// Computes the multiplicative inverse of this element,
|
||||
/// failing if the element is zero.
|
||||
fn invert(&self) -> CtOption<Self>;
|
||||
|
||||
/// Exponentiates this element by a power of the base prime modulus via
|
||||
/// the Frobenius automorphism.
|
||||
fn frobenius_map(&mut self, power: usize);
|
||||
|
||||
/// Exponentiates this element by a number represented with `u64` limbs,
|
||||
/// least significant digit first.
|
||||
fn pow<S: AsRef<[u64]>>(&self, exp: S) -> Self {
|
||||
/// Exponentiates `self` by `exp`, where `exp` is a little-endian order
|
||||
/// integer exponent.
|
||||
///
|
||||
/// **This operation is variable time with respect to the exponent.** If the
|
||||
/// exponent is fixed, this operation is effectively constant time.
|
||||
fn pow_vartime<S: AsRef<[u64]>>(&self, exp: S) -> Self {
|
||||
let mut res = Self::one();
|
||||
for e in exp.as_ref().iter().rev() {
|
||||
for i in (0..64).rev() {
|
||||
res = res.square();
|
||||
|
||||
let mut found_one = false;
|
||||
|
||||
for i in BitIterator::new(exp) {
|
||||
if found_one {
|
||||
res.square();
|
||||
} else {
|
||||
found_one = i;
|
||||
}
|
||||
|
||||
if i {
|
||||
res.mul_assign(self);
|
||||
if ((*e >> i) & 1) == 1 {
|
||||
res.mul_assign(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,12 +97,9 @@ pub trait Field:
|
||||
|
||||
/// This trait represents an element of a field that has a square root operation described for it.
|
||||
pub trait SqrtField: Field {
|
||||
/// Returns the Legendre symbol of the field element.
|
||||
fn legendre(&self) -> LegendreSymbol;
|
||||
|
||||
/// Returns the square root of the field element, if it is
|
||||
/// quadratic residue.
|
||||
fn sqrt(&self) -> Option<Self>;
|
||||
fn sqrt(&self) -> CtOption<Self>;
|
||||
}
|
||||
|
||||
/// This trait represents a wrapper around a biginteger which can encode any element of a particular
|
||||
@ -139,6 +155,7 @@ pub trait PrimeFieldRepr:
|
||||
fn shl(&mut self, amt: u32);
|
||||
|
||||
/// Writes this `PrimeFieldRepr` as a big endian integer.
|
||||
#[cfg(feature = "std")]
|
||||
fn write_be<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||
use byteorder::{BigEndian, WriteBytesExt};
|
||||
|
||||
@ -150,6 +167,7 @@ pub trait PrimeFieldRepr:
|
||||
}
|
||||
|
||||
/// Reads a big endian integer into this representation.
|
||||
#[cfg(feature = "std")]
|
||||
fn read_be<R: Read>(&mut self, mut reader: R) -> io::Result<()> {
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
|
||||
@ -161,6 +179,7 @@ pub trait PrimeFieldRepr:
|
||||
}
|
||||
|
||||
/// Writes this `PrimeFieldRepr` as a little endian integer.
|
||||
#[cfg(feature = "std")]
|
||||
fn write_le<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||
use byteorder::{LittleEndian, WriteBytesExt};
|
||||
|
||||
@ -172,6 +191,7 @@ pub trait PrimeFieldRepr:
|
||||
}
|
||||
|
||||
/// Reads a little endian integer into this representation.
|
||||
#[cfg(feature = "std")]
|
||||
fn read_le<R: Read>(&mut self, mut reader: R) -> io::Result<()> {
|
||||
use byteorder::{LittleEndian, ReadBytesExt};
|
||||
|
||||
@ -183,25 +203,19 @@ pub trait PrimeFieldRepr:
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum LegendreSymbol {
|
||||
Zero = 0,
|
||||
QuadraticResidue = 1,
|
||||
QuadraticNonResidue = -1,
|
||||
}
|
||||
|
||||
/// An error that may occur when trying to interpret a `PrimeFieldRepr` as a
|
||||
/// `PrimeField` element.
|
||||
#[derive(Debug)]
|
||||
pub enum PrimeFieldDecodingError {
|
||||
/// The encoded value is not in the field
|
||||
NotInField(String),
|
||||
NotInField,
|
||||
}
|
||||
|
||||
impl Error for PrimeFieldDecodingError {
|
||||
#[cfg(feature = "std")]
|
||||
impl std::error::Error for PrimeFieldDecodingError {
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
PrimeFieldDecodingError::NotInField(..) => "not an element of the field",
|
||||
PrimeFieldDecodingError::NotInField => "not an element of the field",
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -209,9 +223,7 @@ impl Error for PrimeFieldDecodingError {
|
||||
impl fmt::Display for PrimeFieldDecodingError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
PrimeFieldDecodingError::NotInField(ref repr) => {
|
||||
write!(f, "{} is not an element of the field", repr)
|
||||
}
|
||||
PrimeFieldDecodingError::NotInField => write!(f, "not an element of the field"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -330,7 +342,7 @@ impl<E: AsRef<[u64]>> Iterator for BitIterator<E> {
|
||||
|
||||
#[test]
|
||||
fn test_bit_iterator() {
|
||||
let mut a = BitIterator::new([0xa953d79b83f6ab59, 0x6dea2059e200bd39]);
|
||||
let mut a = BitIterator::new([0xa953_d79b_83f6_ab59, 0x6dea_2059_e200_bd39]);
|
||||
let expected = "01101101111010100010000001011001111000100000000010111101001110011010100101010011110101111001101110000011111101101010101101011001";
|
||||
|
||||
for e in expected.chars() {
|
||||
@ -342,10 +354,10 @@ fn test_bit_iterator() {
|
||||
let expected = "1010010101111110101010000101101011101000011101110101001000011001100100100011011010001011011011010001011011101100110100111011010010110001000011110100110001100110011101101000101100011100100100100100001010011101010111110011101011000011101000111011011101011001";
|
||||
|
||||
let mut a = BitIterator::new([
|
||||
0x429d5f3ac3a3b759,
|
||||
0xb10f4c66768b1c92,
|
||||
0x92368b6d16ecd3b4,
|
||||
0xa57ea85ae8775219,
|
||||
0x429d_5f3a_c3a3_b759,
|
||||
0xb10f_4c66_768b_1c92,
|
||||
0x9236_8b6d_16ec_d3b4,
|
||||
0xa57e_a85a_e877_5219,
|
||||
]);
|
||||
|
||||
for e in expected.chars() {
|
||||
|
@ -5,16 +5,46 @@ use ff::{PrimeField, PrimeFieldDecodingError, ScalarEngine, SqrtField};
|
||||
use rand::RngCore;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::ops::{Add, AddAssign, Neg, Sub, SubAssign};
|
||||
|
||||
pub mod tests;
|
||||
|
||||
mod wnaf;
|
||||
pub use self::wnaf::Wnaf;
|
||||
|
||||
/// A helper trait for types implementing group addition.
|
||||
pub trait CurveOps<Rhs = Self, Output = Self>:
|
||||
Add<Rhs, Output = Output> + Sub<Rhs, Output = Output> + AddAssign<Rhs> + SubAssign<Rhs>
|
||||
{
|
||||
}
|
||||
|
||||
impl<T, Rhs, Output> CurveOps<Rhs, Output> for T where
|
||||
T: Add<Rhs, Output = Output> + Sub<Rhs, Output = Output> + AddAssign<Rhs> + SubAssign<Rhs>
|
||||
{
|
||||
}
|
||||
|
||||
/// A helper trait for references implementing group addition.
|
||||
pub trait CurveOpsOwned<Rhs = Self, Output = Self>: for<'r> CurveOps<&'r Rhs, Output> {}
|
||||
impl<T, Rhs, Output> CurveOpsOwned<Rhs, Output> for T where T: for<'r> CurveOps<&'r Rhs, Output> {}
|
||||
|
||||
/// Projective representation of an elliptic curve point guaranteed to be
|
||||
/// in the correct prime order subgroup.
|
||||
pub trait CurveProjective:
|
||||
PartialEq + Eq + Sized + Copy + Clone + Send + Sync + fmt::Debug + fmt::Display + 'static
|
||||
PartialEq
|
||||
+ Eq
|
||||
+ Sized
|
||||
+ Copy
|
||||
+ Clone
|
||||
+ Send
|
||||
+ Sync
|
||||
+ fmt::Debug
|
||||
+ fmt::Display
|
||||
+ 'static
|
||||
+ Neg<Output = Self>
|
||||
+ CurveOps
|
||||
+ CurveOpsOwned
|
||||
+ CurveOps<<Self as CurveProjective>::Affine>
|
||||
+ CurveOpsOwned<<Self as CurveProjective>::Affine>
|
||||
{
|
||||
type Engine: ScalarEngine<Fr = Self::Scalar>;
|
||||
type Scalar: PrimeField + SqrtField;
|
||||
@ -44,22 +74,6 @@ pub trait CurveProjective:
|
||||
/// Doubles this element.
|
||||
fn double(&mut self);
|
||||
|
||||
/// Adds another element to this element.
|
||||
fn add_assign(&mut self, other: &Self);
|
||||
|
||||
/// Subtracts another element from this element.
|
||||
fn sub_assign(&mut self, other: &Self) {
|
||||
let mut tmp = *other;
|
||||
tmp.negate();
|
||||
self.add_assign(&tmp);
|
||||
}
|
||||
|
||||
/// Adds an affine element to this element.
|
||||
fn add_assign_mixed(&mut self, other: &Self::Affine);
|
||||
|
||||
/// Negates this element.
|
||||
fn negate(&mut self);
|
||||
|
||||
/// Performs scalar multiplication of this element.
|
||||
fn mul_assign<S: Into<<Self::Scalar as PrimeField>::Repr>>(&mut self, other: S);
|
||||
|
||||
@ -78,7 +92,17 @@ pub trait CurveProjective:
|
||||
/// Affine representation of an elliptic curve point guaranteed to be
|
||||
/// in the correct prime order subgroup.
|
||||
pub trait CurveAffine:
|
||||
Copy + Clone + Sized + Send + Sync + fmt::Debug + fmt::Display + PartialEq + Eq + 'static
|
||||
Copy
|
||||
+ Clone
|
||||
+ Sized
|
||||
+ Send
|
||||
+ Sync
|
||||
+ fmt::Debug
|
||||
+ fmt::Display
|
||||
+ PartialEq
|
||||
+ Eq
|
||||
+ 'static
|
||||
+ Neg<Output = Self>
|
||||
{
|
||||
type Engine: ScalarEngine<Fr = Self::Scalar>;
|
||||
type Scalar: PrimeField + SqrtField;
|
||||
@ -97,9 +121,6 @@ pub trait CurveAffine:
|
||||
/// additive identity.
|
||||
fn is_zero(&self) -> bool;
|
||||
|
||||
/// Negates this element.
|
||||
fn negate(&mut self);
|
||||
|
||||
/// Performs scalar multiplication of this element with mixed addition.
|
||||
fn mul<S: Into<<Self::Scalar as PrimeField>::Repr>>(&self, other: S) -> Self::Projective;
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
use ff::{Field, PrimeField};
|
||||
use rand::SeedableRng;
|
||||
use rand_xorshift::XorShiftRng;
|
||||
use std::ops::Neg;
|
||||
|
||||
use crate::{CurveAffine, CurveProjective, EncodedPoint};
|
||||
|
||||
@ -12,8 +13,7 @@ pub fn curve_tests<G: CurveProjective>() {
|
||||
|
||||
// Negation edge case with zero.
|
||||
{
|
||||
let mut z = G::zero();
|
||||
z.negate();
|
||||
let z = G::zero().neg();
|
||||
assert!(z.is_zero());
|
||||
}
|
||||
|
||||
@ -30,19 +30,19 @@ pub fn curve_tests<G: CurveProjective>() {
|
||||
let rcopy = r;
|
||||
r.add_assign(&G::zero());
|
||||
assert_eq!(r, rcopy);
|
||||
r.add_assign_mixed(&G::Affine::zero());
|
||||
r.add_assign(&G::Affine::zero());
|
||||
assert_eq!(r, rcopy);
|
||||
|
||||
let mut z = G::zero();
|
||||
z.add_assign(&G::zero());
|
||||
assert!(z.is_zero());
|
||||
z.add_assign_mixed(&G::Affine::zero());
|
||||
z.add_assign(&G::Affine::zero());
|
||||
assert!(z.is_zero());
|
||||
|
||||
let mut z2 = z;
|
||||
z2.add_assign(&r);
|
||||
|
||||
z.add_assign_mixed(&r.into_affine());
|
||||
z.add_assign(&r.into_affine());
|
||||
|
||||
assert_eq!(z, z2);
|
||||
assert_eq!(z, r);
|
||||
@ -67,7 +67,7 @@ pub fn curve_tests<G: CurveProjective>() {
|
||||
random_negation_tests::<G>();
|
||||
random_transformation_tests::<G>();
|
||||
random_wnaf_tests::<G>();
|
||||
random_encoding_tests::<G::Affine>();
|
||||
random_encoding_tests::<G>();
|
||||
}
|
||||
|
||||
fn random_wnaf_tests<G: CurveProjective>() {
|
||||
@ -199,8 +199,7 @@ fn random_negation_tests<G: CurveProjective>() {
|
||||
let r = G::random(&mut rng);
|
||||
|
||||
let s = G::Scalar::random(&mut rng);
|
||||
let mut sneg = s;
|
||||
sneg.negate();
|
||||
let sneg = s.neg();
|
||||
|
||||
let mut t1 = r;
|
||||
t1.mul_assign(s);
|
||||
@ -213,11 +212,10 @@ fn random_negation_tests<G: CurveProjective>() {
|
||||
assert!(t3.is_zero());
|
||||
|
||||
let mut t4 = t1;
|
||||
t4.add_assign_mixed(&t2.into_affine());
|
||||
t4.add_assign(&t2.into_affine());
|
||||
assert!(t4.is_zero());
|
||||
|
||||
t1.negate();
|
||||
assert_eq!(t1, t2);
|
||||
assert_eq!(t1.neg(), t2);
|
||||
}
|
||||
}
|
||||
|
||||
@ -244,7 +242,7 @@ fn random_doubling_tests<G: CurveProjective>() {
|
||||
tmp2.add_assign(&b);
|
||||
|
||||
let mut tmp3 = a;
|
||||
tmp3.add_assign_mixed(&b.into_affine());
|
||||
tmp3.add_assign(&b.into_affine());
|
||||
|
||||
assert_eq!(tmp1, tmp2);
|
||||
assert_eq!(tmp1, tmp3);
|
||||
@ -306,7 +304,7 @@ fn random_addition_tests<G: CurveProjective>() {
|
||||
aplusa.add_assign(&a);
|
||||
|
||||
let mut aplusamixed = a;
|
||||
aplusamixed.add_assign_mixed(&a.into_affine());
|
||||
aplusamixed.add_assign(&a.into_affine());
|
||||
|
||||
let mut adouble = a;
|
||||
adouble.double();
|
||||
@ -336,18 +334,18 @@ fn random_addition_tests<G: CurveProjective>() {
|
||||
|
||||
// (a + b) + c
|
||||
tmp[3] = a_affine.into_projective();
|
||||
tmp[3].add_assign_mixed(&b_affine);
|
||||
tmp[3].add_assign_mixed(&c_affine);
|
||||
tmp[3].add_assign(&b_affine);
|
||||
tmp[3].add_assign(&c_affine);
|
||||
|
||||
// a + (b + c)
|
||||
tmp[4] = b_affine.into_projective();
|
||||
tmp[4].add_assign_mixed(&c_affine);
|
||||
tmp[4].add_assign_mixed(&a_affine);
|
||||
tmp[4].add_assign(&c_affine);
|
||||
tmp[4].add_assign(&a_affine);
|
||||
|
||||
// (a + c) + b
|
||||
tmp[5] = a_affine.into_projective();
|
||||
tmp[5].add_assign_mixed(&c_affine);
|
||||
tmp[5].add_assign_mixed(&b_affine);
|
||||
tmp[5].add_assign(&c_affine);
|
||||
tmp[5].add_assign(&b_affine);
|
||||
|
||||
// Comparisons
|
||||
for i in 0..6 {
|
||||
@ -413,24 +411,24 @@ fn random_transformation_tests<G: CurveProjective>() {
|
||||
}
|
||||
}
|
||||
|
||||
fn random_encoding_tests<G: CurveAffine>() {
|
||||
fn random_encoding_tests<G: CurveProjective>() {
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
assert_eq!(
|
||||
G::zero().into_uncompressed().into_affine().unwrap(),
|
||||
G::zero()
|
||||
G::Affine::zero().into_uncompressed().into_affine().unwrap(),
|
||||
G::Affine::zero()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
G::zero().into_compressed().into_affine().unwrap(),
|
||||
G::zero()
|
||||
G::Affine::zero().into_compressed().into_affine().unwrap(),
|
||||
G::Affine::zero()
|
||||
);
|
||||
|
||||
for _ in 0..1000 {
|
||||
let mut r = G::Projective::random(&mut rng).into_affine();
|
||||
let mut r = G::random(&mut rng).into_affine();
|
||||
|
||||
let uncompressed = r.into_uncompressed();
|
||||
let de_uncompressed = uncompressed.into_affine().unwrap();
|
||||
@ -440,7 +438,7 @@ fn random_encoding_tests<G: CurveAffine>() {
|
||||
let de_compressed = compressed.into_affine().unwrap();
|
||||
assert_eq!(de_compressed, r);
|
||||
|
||||
r.negate();
|
||||
r = r.neg();
|
||||
|
||||
let compressed = r.into_compressed();
|
||||
let de_compressed = compressed.into_affine().unwrap();
|
||||
|
95
jubjub/.github/workflows/ci.yml
vendored
Normal file
95
jubjub/.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,95 @@
|
||||
name: CI checks
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.36.0
|
||||
override: true
|
||||
|
||||
# Ensure all code has been formatted with rustfmt
|
||||
- run: rustup component add rustfmt
|
||||
- name: Check formatting
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: -- --check --color always
|
||||
|
||||
test:
|
||||
name: Test on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macOS-latest]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.36.0
|
||||
override: true
|
||||
- name: cargo fetch
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fetch
|
||||
- name: Build tests
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --verbose --release --tests
|
||||
- name: Run tests
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --verbose --release
|
||||
|
||||
no-std:
|
||||
name: Check no-std compatibility
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.36.0
|
||||
override: true
|
||||
- run: rustup target add thumbv6m-none-eabi
|
||||
- name: cargo fetch
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fetch
|
||||
- name: Build
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --verbose --target thumbv6m-none-eabi --no-default-features
|
||||
|
||||
doc-links:
|
||||
name: Nightly lint
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
override: true
|
||||
- name: cargo fetch
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fetch
|
||||
|
||||
# Ensure intra-documentation links all resolve correctly
|
||||
# Requires #![deny(intra_doc_link_resolution_failure)] in crate.
|
||||
- name: Check intra-doc links
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: doc
|
||||
args: --document-private-items
|
3
jubjub/.gitignore
vendored
Normal file
3
jubjub/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
/target
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
14
jubjub/COPYRIGHT
Normal file
14
jubjub/COPYRIGHT
Normal file
@ -0,0 +1,14 @@
|
||||
Copyrights in the "jubjub" library are retained by their contributors. No
|
||||
copyright assignment is required to contribute to the "jubjub" library.
|
||||
|
||||
The "jubjub" library is licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, (see ./LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license (see ./LICENSE-MIT or http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally
|
||||
submitted for inclusion in the work by you, as defined in the Apache-2.0
|
||||
license, shall be dual licensed as above, without any additional terms or
|
||||
conditions.
|
49
jubjub/Cargo.toml
Normal file
49
jubjub/Cargo.toml
Normal file
@ -0,0 +1,49 @@
|
||||
[package]
|
||||
authors = [
|
||||
"Sean Bowe <ewillbefull@gmail.com>",
|
||||
"Eirik Ogilvie-Wigley <eowigley@gmail.com>",
|
||||
"Jack Grigg <thestr4d@gmail.com>",
|
||||
]
|
||||
description = "Implementation of the Jubjub elliptic curve group"
|
||||
documentation = "https://docs.rs/jubjub/"
|
||||
homepage = "https://github.com/zkcrypto/jubjub"
|
||||
license = "MIT/Apache-2.0"
|
||||
name = "jubjub"
|
||||
repository = "https://github.com/zkcrypto/jubjub"
|
||||
version = "0.3.0"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies.bls12_381]
|
||||
path = "../bls12_381"
|
||||
version = "0.1"
|
||||
default-features = false
|
||||
|
||||
[dependencies.subtle]
|
||||
version = "^2.2.1"
|
||||
default-features = false
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.3"
|
||||
|
||||
[dev-dependencies.rand_core]
|
||||
version = "0.5"
|
||||
default-features = false
|
||||
|
||||
[dev-dependencies.rand_xorshift]
|
||||
version = "0.2"
|
||||
default-features = false
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
[[bench]]
|
||||
name = "fq_bench"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "fr_bench"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "point_bench"
|
||||
harness = false
|
201
jubjub/LICENSE-APACHE
Normal file
201
jubjub/LICENSE-APACHE
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
23
jubjub/LICENSE-MIT
Normal file
23
jubjub/LICENSE-MIT
Normal file
@ -0,0 +1,23 @@
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
53
jubjub/README.md
Normal file
53
jubjub/README.md
Normal file
@ -0,0 +1,53 @@
|
||||
# jubjub [![Crates.io](https://img.shields.io/crates/v/jubjub.svg)](https://crates.io/crates/jubjub) #
|
||||
|
||||
<img
|
||||
width="15%"
|
||||
align="right"
|
||||
src="https://raw.githubusercontent.com/zcash/zips/master/protocol/jubjub.png"/>
|
||||
|
||||
This is a pure Rust implementation of the Jubjub elliptic curve group and its associated fields.
|
||||
|
||||
* **This implementation has not been reviewed or audited. Use at your own risk.**
|
||||
* This implementation targets Rust `1.36` or later.
|
||||
* All operations are constant time unless explicitly noted.
|
||||
* This implementation does not require the Rust standard library.
|
||||
|
||||
## [Documentation](https://docs.rs/jubjub)
|
||||
|
||||
## Curve Description
|
||||
|
||||
Jubjub is the [twisted Edwards curve](https://en.wikipedia.org/wiki/Twisted_Edwards_curve) `-u^2 + v^2 = 1 + d.u^2.v^2` of rational points over `GF(q)` with a subgroup of prime order `r` and cofactor `8`.
|
||||
|
||||
```
|
||||
q = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001
|
||||
r = 0x0e7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7
|
||||
d = -(10240/10241)
|
||||
```
|
||||
|
||||
The choice of `GF(q)` is made to be the scalar field of the BLS12-381 elliptic curve construction.
|
||||
|
||||
Jubjub is birationally equivalent to a [Montgomery curve](https://en.wikipedia.org/wiki/Montgomery_curve) `y^2 = x^3 + Ax^2 + x` over the same field with `A = 40962`. This value of `A` is the smallest integer such that `(A - 2) / 4` is a small integer, `A^2 - 4` is nonsquare in `GF(q)`, and the Montgomery curve and its quadratic twist have small cofactors `8` and `4`, respectively. This is identical to the relationship between Curve25519 and ed25519.
|
||||
|
||||
Please see [./doc/evidence/](./doc/evidence/) for supporting evidence that Jubjub meets the [SafeCurves](https://safecurves.cr.yp.to/index.html) criteria. The tool in [./doc/derive/](./doc/derive/) will derive the curve parameters via the above criteria to demonstrate rigidity.
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
Jubjub was designed by Sean Bowe. Daira Hopwood is responsible for its name and specification. The security evidence in [./doc/evidence/](./doc/evidence/) is the product of Daira Hopwood and based on SafeCurves by Daniel J. Bernstein and Tanja Lange. Peter Newell and Daira Hopwood are responsible for the Jubjub bird image.
|
||||
|
||||
Please see `Cargo.toml` for a list of primary authors of this codebase.
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally
|
||||
submitted for inclusion in the work by you, as defined in the Apache-2.0
|
||||
license, shall be dual licensed as above, without any additional terms or
|
||||
conditions.
|
24
jubjub/RELEASES.md
Normal file
24
jubjub/RELEASES.md
Normal file
@ -0,0 +1,24 @@
|
||||
# 0.3.0
|
||||
|
||||
This release now depends on the `bls12_381` crate, which exposes the `Fq` field type that we re-export.
|
||||
|
||||
* The `Fq` and `Fr` field types now have better constant function support for various operations and constructors.
|
||||
* We no longer depend on the `byteorder` crate.
|
||||
* We've bumped our `rand_core` dev-dependency up to 0.5.
|
||||
* We've removed the `std` and `nightly` features.
|
||||
* We've bumped our dependency of `subtle` up to `^2.2.1`.
|
||||
|
||||
# 0.2.0
|
||||
|
||||
This release switches to `subtle 2.1` to bring in the `CtOption` type, and also makes a few useful API changes.
|
||||
|
||||
* Implemented `Mul<Fr>` for `AffineNielsPoint` and `ExtendedNielsPoint`
|
||||
* Changed `AffinePoint::to_niels()` to be a `const` function so that constant curve points can be constructed without statics.
|
||||
* Implemented `multiply_bits` for `AffineNielsPoint`, `ExtendedNielsPoint`
|
||||
* Removed `CtOption` and replaced it with `CtOption` from `subtle` crate.
|
||||
* Modified receivers of some methods to reduce stack usage
|
||||
* Changed various `into_bytes` methods into `to_bytes`
|
||||
|
||||
# 0.1.0
|
||||
|
||||
Initial release.
|
58
jubjub/benches/fq_bench.rs
Normal file
58
jubjub/benches/fq_bench.rs
Normal file
@ -0,0 +1,58 @@
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use jubjub::*;
|
||||
|
||||
fn bench_add_assign(c: &mut Criterion) {
|
||||
let mut n = Fq::one();
|
||||
let neg_one = -Fq::one();
|
||||
c.bench_function("Fq add_assign", |b| {
|
||||
b.iter(move || {
|
||||
n += &neg_one;
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_sub_assign(c: &mut Criterion) {
|
||||
let mut n = Fq::one();
|
||||
let neg_one = -Fq::one();
|
||||
c.bench_function("Fq sub_assign", |b| {
|
||||
b.iter(move || {
|
||||
n -= &neg_one;
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_mul_assign(c: &mut Criterion) {
|
||||
let mut n = Fq::one();
|
||||
let neg_one = -Fq::one();
|
||||
c.bench_function("Fq mul_assign", |b| {
|
||||
b.iter(move || {
|
||||
n *= &neg_one;
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_square(c: &mut Criterion) {
|
||||
let n = Fq::one();
|
||||
c.bench_function("Fq square", |b| b.iter(move || n.square()));
|
||||
}
|
||||
|
||||
fn bench_invert(c: &mut Criterion) {
|
||||
let n = Fq::one();
|
||||
c.bench_function("Fq invert", |b| b.iter(move || n.invert()));
|
||||
}
|
||||
|
||||
fn bench_sqrt(c: &mut Criterion) {
|
||||
let n = Fq::one().double().double();
|
||||
c.bench_function("Fq sqrt", |b| b.iter(move || n.sqrt()));
|
||||
}
|
||||
|
||||
criterion_group!(
|
||||
benches,
|
||||
bench_add_assign,
|
||||
bench_sub_assign,
|
||||
bench_mul_assign,
|
||||
bench_square,
|
||||
bench_invert,
|
||||
bench_sqrt,
|
||||
);
|
||||
criterion_main!(benches);
|
58
jubjub/benches/fr_bench.rs
Normal file
58
jubjub/benches/fr_bench.rs
Normal file
@ -0,0 +1,58 @@
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use jubjub::*;
|
||||
|
||||
fn bench_add_assign(c: &mut Criterion) {
|
||||
let mut n = Fr::one();
|
||||
let neg_one = -Fr::one();
|
||||
c.bench_function("Fr add_assign", |b| {
|
||||
b.iter(move || {
|
||||
n += &neg_one;
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_sub_assign(c: &mut Criterion) {
|
||||
let mut n = Fr::one();
|
||||
let neg_one = -Fr::one();
|
||||
c.bench_function("Fr sub_assign", |b| {
|
||||
b.iter(move || {
|
||||
n -= &neg_one;
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_mul_assign(c: &mut Criterion) {
|
||||
let mut n = Fr::one();
|
||||
let neg_one = -Fr::one();
|
||||
c.bench_function("Fr mul_assign", |b| {
|
||||
b.iter(move || {
|
||||
n *= &neg_one;
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_square(c: &mut Criterion) {
|
||||
let n = Fr::one();
|
||||
c.bench_function("Fr square", |b| b.iter(move || n.square()));
|
||||
}
|
||||
|
||||
fn bench_invert(c: &mut Criterion) {
|
||||
let n = Fr::one();
|
||||
c.bench_function("Fr invert", |b| b.iter(move || n.invert()));
|
||||
}
|
||||
|
||||
fn bench_sqrt(c: &mut Criterion) {
|
||||
let n = Fr::one().double().double();
|
||||
c.bench_function("Fr sqrt", |b| b.iter(move || n.sqrt()));
|
||||
}
|
||||
|
||||
criterion_group!(
|
||||
benches,
|
||||
bench_add_assign,
|
||||
bench_sub_assign,
|
||||
bench_mul_assign,
|
||||
bench_square,
|
||||
bench_invert,
|
||||
bench_sqrt,
|
||||
);
|
||||
criterion_main!(benches);
|
73
jubjub/benches/point_bench.rs
Normal file
73
jubjub/benches/point_bench.rs
Normal file
@ -0,0 +1,73 @@
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use jubjub::*;
|
||||
|
||||
// Non-Niels
|
||||
|
||||
fn bench_point_doubling(c: &mut Criterion) {
|
||||
let a = ExtendedPoint::identity();
|
||||
c.bench_function("Jubjub point doubling", |bencher| {
|
||||
bencher.iter(move || a.double())
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_point_addition(c: &mut Criterion) {
|
||||
let a = ExtendedPoint::identity();
|
||||
let b = -ExtendedPoint::identity();
|
||||
c.bench_function("Jubjub point addition", |bencher| {
|
||||
bencher.iter(move || a + b)
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_point_subtraction(c: &mut Criterion) {
|
||||
let a = ExtendedPoint::identity();
|
||||
let b = -ExtendedPoint::identity();
|
||||
c.bench_function("Jubjub point subtraction", |bencher| {
|
||||
bencher.iter(move || a + b)
|
||||
});
|
||||
}
|
||||
|
||||
// Niels
|
||||
|
||||
fn bench_cached_point_addition(c: &mut Criterion) {
|
||||
let a = ExtendedPoint::identity();
|
||||
let b = ExtendedPoint::identity().to_niels();
|
||||
c.bench_function("Jubjub cached point addition", |bencher| {
|
||||
bencher.iter(move || a + b)
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_cached_point_subtraction(c: &mut Criterion) {
|
||||
let a = ExtendedPoint::identity();
|
||||
let b = ExtendedPoint::identity().to_niels();
|
||||
c.bench_function("Jubjub cached point subtraction", |bencher| {
|
||||
bencher.iter(move || a + b)
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_cached_affine_point_addition(c: &mut Criterion) {
|
||||
let a = ExtendedPoint::identity();
|
||||
let b = AffinePoint::identity().to_niels();
|
||||
c.bench_function("Jubjub cached affine point addition", |bencher| {
|
||||
bencher.iter(move || a + b)
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_cached_affine_point_subtraction(c: &mut Criterion) {
|
||||
let a = ExtendedPoint::identity();
|
||||
let b = AffinePoint::identity().to_niels();
|
||||
c.bench_function("Jubjub cached affine point subtraction", |bencher| {
|
||||
bencher.iter(move || a + b)
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(
|
||||
benches,
|
||||
bench_point_doubling,
|
||||
bench_point_addition,
|
||||
bench_point_subtraction,
|
||||
bench_cached_point_addition,
|
||||
bench_cached_point_subtraction,
|
||||
bench_cached_affine_point_addition,
|
||||
bench_cached_affine_point_subtraction,
|
||||
);
|
||||
criterion_main!(benches);
|
1
jubjub/doc/derive/.gitignore
vendored
Normal file
1
jubjub/doc/derive/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
*.sage.py
|
32
jubjub/doc/derive/derive.sage
Normal file
32
jubjub/doc/derive/derive.sage
Normal file
@ -0,0 +1,32 @@
|
||||
q = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001
|
||||
Fq = GF(q)
|
||||
|
||||
# We wish to find a Montgomery curve with B = 1 and A the smallest such
|
||||
# that (A - 2) / 4 is a small integer.
|
||||
def get_A(n):
|
||||
return (n * 4) + 2
|
||||
|
||||
# A = 2 is invalid (singular curve), so we start at i = 1 (A = 6)
|
||||
i = 1
|
||||
|
||||
while True:
|
||||
A = Fq(get_A(i))
|
||||
i = i + 1
|
||||
|
||||
# We also want that A^2 - 4 is nonsquare.
|
||||
if ((A^2) - 4).is_square():
|
||||
continue
|
||||
|
||||
ec = EllipticCurve(Fq, [0, A, 0, 1, 0])
|
||||
o = ec.order()
|
||||
|
||||
if (o % 8 == 0):
|
||||
o = o // 8
|
||||
if is_prime(o):
|
||||
twist = ec.quadratic_twist()
|
||||
otwist = twist.order()
|
||||
if (otwist % 4 == 0):
|
||||
otwist = otwist // 4
|
||||
if is_prime(otwist):
|
||||
print "A = %s" % A
|
||||
exit(0)
|
102
jubjub/doc/evidence/.gitignore
vendored
Normal file
102
jubjub/doc/evidence/.gitignore
vendored
Normal file
@ -0,0 +1,102 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
env/
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
.hypothesis/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# celery beat schedule file
|
||||
celerybeat-schedule
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# dotenv
|
||||
.env
|
||||
|
||||
# virtualenv
|
||||
.venv
|
||||
venv/
|
||||
ENV/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
|
19
jubjub/doc/evidence/LICENSE
Normal file
19
jubjub/doc/evidence/LICENSE
Normal file
@ -0,0 +1,19 @@
|
||||
Copyright (c) 2017 The Zcash developers
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
28
jubjub/doc/evidence/README.md
Normal file
28
jubjub/doc/evidence/README.md
Normal file
@ -0,0 +1,28 @@
|
||||
Jubjub supporting evidence
|
||||
--------------------------
|
||||
|
||||
This repository contains supporting evidence that the twisted Edwards curve
|
||||
-x^2 + y^2 = 1 - (10240/10241).x^2.y^2 of rational points over
|
||||
GF(52435875175126190479447740508185965837690552500527637822603658699938581184513),
|
||||
[also called "Jubjub"](https://z.cash/technology/jubjub.html),
|
||||
satisfies the [SafeCurves criteria](https://safecurves.cr.yp.to/index.html).
|
||||
|
||||
The script ``verify.sage`` is based on
|
||||
[this script from the SafeCurves site](https://safecurves.cr.yp.to/verify.html),
|
||||
modified
|
||||
|
||||
* to support twisted Edwards curves;
|
||||
* to generate a file 'primes' containing the primes needed for primality proofs,
|
||||
if it is not already present;
|
||||
* to change the directory in which Pocklington proof files are generated
|
||||
(``proof/`` rather than ``../../../proof``), and to create that directory
|
||||
if it does not exist.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
* apt-get install sagemath
|
||||
* pip install sortedcontainers
|
||||
|
||||
Run ``sage verify.sage .``, or ``./run.sh`` to also print out the results.
|
||||
|
||||
Note that the "rigidity" criterion cannot be checked automatically.
|
1
jubjub/doc/evidence/a
Normal file
1
jubjub/doc/evidence/a
Normal file
@ -0,0 +1 @@
|
||||
-1
|
1
jubjub/doc/evidence/d
Normal file
1
jubjub/doc/evidence/d
Normal file
@ -0,0 +1 @@
|
||||
19257038036680949359750312669786877991949435402254120286184196891950884077233
|
1
jubjub/doc/evidence/l
Normal file
1
jubjub/doc/evidence/l
Normal file
@ -0,0 +1 @@
|
||||
6554484396890773809930967563523245729705921265872317281365359162392183254199
|
1
jubjub/doc/evidence/p
Normal file
1
jubjub/doc/evidence/p
Normal file
@ -0,0 +1 @@
|
||||
52435875175126190479447740508185965837690552500527637822603658699938581184513
|
1
jubjub/doc/evidence/rigid
Normal file
1
jubjub/doc/evidence/rigid
Normal file
@ -0,0 +1 @@
|
||||
fully rigid
|
4
jubjub/doc/evidence/run.sh
Normal file
4
jubjub/doc/evidence/run.sh
Normal file
@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
sage verify.sage .
|
||||
grep -Rn '.' verify-* |grep '^verify-.*:1:' |sed 's/:1:/ = /'
|
||||
|
1
jubjub/doc/evidence/shape
Normal file
1
jubjub/doc/evidence/shape
Normal file
@ -0,0 +1 @@
|
||||
tedwards
|
444
jubjub/doc/evidence/verify.sage
Normal file
444
jubjub/doc/evidence/verify.sage
Normal file
@ -0,0 +1,444 @@
|
||||
import os
|
||||
import sys
|
||||
from errno import ENOENT, EEXIST
|
||||
from sortedcontainers import SortedSet
|
||||
|
||||
|
||||
def readfile(fn):
|
||||
fd = open(fn,'r')
|
||||
r = fd.read()
|
||||
fd.close()
|
||||
return r
|
||||
|
||||
def writefile(fn,s):
|
||||
fd = open(fn,'w')
|
||||
fd.write(s)
|
||||
fd.close()
|
||||
|
||||
def expand2(n):
|
||||
s = ""
|
||||
|
||||
while n != 0:
|
||||
j = 16
|
||||
while 2**j < abs(n): j += 1
|
||||
if 2**j - abs(n) > abs(n) - 2**(j-1): j -= 1
|
||||
|
||||
if abs(abs(n) - 2**j) > 2**(j - 10):
|
||||
if n > 0:
|
||||
if s != "": s += " + "
|
||||
s += str(n)
|
||||
else:
|
||||
s += " - " + str(-n)
|
||||
n = 0
|
||||
elif n > 0:
|
||||
if s != "": s += " + "
|
||||
s += "2^" + str(j)
|
||||
n -= 2**j
|
||||
else:
|
||||
s += " - 2^" + str(j)
|
||||
n += 2**j
|
||||
|
||||
return s
|
||||
|
||||
def requirement(fn,istrue):
|
||||
writefile(fn,str(istrue) + '\n')
|
||||
return istrue
|
||||
|
||||
def verify():
|
||||
try:
|
||||
os.mkdir('proof')
|
||||
except OSError as e:
|
||||
if e.errno != EEXIST: raise
|
||||
|
||||
try:
|
||||
s = set(map(Integer, readfile('primes').split()))
|
||||
except IOError, e:
|
||||
if e.errno != ENOENT: raise
|
||||
s = set()
|
||||
|
||||
needtofactor = SortedSet()
|
||||
V = SortedSet() # distinct verified primes
|
||||
verify_primes(V, s, needtofactor)
|
||||
verify_pass(V, needtofactor)
|
||||
|
||||
old = V
|
||||
needtofactor.update(V)
|
||||
while len(needtofactor) > len(old):
|
||||
k = len(needtofactor) - len(old)
|
||||
sys.stdout.write('Factoring %d integer%s' % (k, '' if k == 1 else 's'))
|
||||
sys.stdout.flush()
|
||||
for x in needtofactor:
|
||||
if x not in old:
|
||||
for (y, z) in factor(x):
|
||||
s.add(y)
|
||||
sys.stdout.write('.')
|
||||
sys.stdout.flush()
|
||||
|
||||
print('')
|
||||
|
||||
old = needtofactor.copy()
|
||||
verify_primes(V, s, needtofactor)
|
||||
|
||||
writefile('primes', '\n'.join(map(str, s)) + '\n')
|
||||
writefile('verify-primes', '<html><body>\n' +
|
||||
''.join(('2\n' if v == 2 else
|
||||
'<a href=proof/%s.html>%s</a>\n' % (v,v)) for v in V) +
|
||||
'</body></html>\n')
|
||||
|
||||
verify_pass(V, needtofactor)
|
||||
|
||||
|
||||
def verify_primes(V, s, needtofactor):
|
||||
for n in sorted(s):
|
||||
if not n.is_prime() or n in V: continue
|
||||
needtofactor.add(n-1)
|
||||
if n == 2:
|
||||
V.add(n)
|
||||
continue
|
||||
for trybase in primes(2,10000):
|
||||
base = Integers(n)(trybase)
|
||||
if base^(n-1) != 1: continue
|
||||
proof = 'Primality proof for n = %s:\n' % n
|
||||
proof += '<p>Take b = %s.\n' % base
|
||||
proof += '<p>b^(n-1) mod n = 1.\n'
|
||||
f = factor(1)
|
||||
for v in reversed(V):
|
||||
if f.prod()^2 <= n:
|
||||
if n % v == 1:
|
||||
u = base^((n-1)/v)-1
|
||||
if u.is_unit():
|
||||
if v == 2:
|
||||
proof += '<p>2 is prime.\n'
|
||||
else:
|
||||
proof += '<p><a href=%s.html>%s is prime.</a>\n' % (v,v)
|
||||
proof += '<br>b^((n-1)/%s)-1 mod n = %s, which is a unit, inverse %s.\n' % (v,u,1/u)
|
||||
f *= factor(v)^(n-1).valuation(v)
|
||||
if f.prod()^2 <= n: continue
|
||||
if n % f.prod() != 1: continue
|
||||
proof += '<p>(%s) divides n-1.\n' % f
|
||||
proof += '<p>(%s)^2 > n.\n' % f
|
||||
proof += "<p>n is prime by Pocklington's theorem.\n"
|
||||
proof += '\n'
|
||||
writefile('proof/%s.html' % n,proof)
|
||||
V.add(n)
|
||||
break
|
||||
|
||||
|
||||
def verify_pass(V, needtofactor):
|
||||
p = Integer(readfile('p'))
|
||||
k = GF(p)
|
||||
kz.<z> = k[]
|
||||
l = Integer(readfile('l'))
|
||||
x0 = Integer(readfile('x0'))
|
||||
y0 = Integer(readfile('y0'))
|
||||
x1 = Integer(readfile('x1'))
|
||||
y1 = Integer(readfile('y1'))
|
||||
shape = readfile('shape').strip()
|
||||
rigid = readfile('rigid').strip()
|
||||
|
||||
safefield = True
|
||||
safeeq = True
|
||||
safebase = True
|
||||
saferho = True
|
||||
safetransfer = True
|
||||
safedisc = True
|
||||
saferigid = True
|
||||
safeladder = True
|
||||
safetwist = True
|
||||
safecomplete = True
|
||||
safeind = True
|
||||
|
||||
pstatus = 'Unverified'
|
||||
if not p.is_prime(): pstatus = 'False'
|
||||
needtofactor.add(p)
|
||||
if p in V: pstatus = 'True'
|
||||
if pstatus != 'True': safefield = False
|
||||
writefile('verify-pisprime',pstatus + '\n')
|
||||
|
||||
pstatus = 'Unverified'
|
||||
if not l.is_prime(): pstatus = 'False'
|
||||
needtofactor.add(l)
|
||||
if l in V: pstatus = 'True'
|
||||
if pstatus != 'True': safebase = False
|
||||
writefile('verify-lisprime',pstatus + '\n')
|
||||
|
||||
writefile('expand2-p','= %s\n' % expand2(p))
|
||||
writefile('expand2-l','<br>= %s\n' % expand2(l))
|
||||
|
||||
writefile('hex-p',hex(p) + '\n')
|
||||
writefile('hex-l',hex(l) + '\n')
|
||||
writefile('hex-x0',hex(x0) + '\n')
|
||||
writefile('hex-x1',hex(x1) + '\n')
|
||||
writefile('hex-y0',hex(y0) + '\n')
|
||||
writefile('hex-y1',hex(y1) + '\n')
|
||||
|
||||
gcdlpis1 = gcd(l,p) == 1
|
||||
safetransfer &= requirement('verify-gcdlp1',gcdlpis1)
|
||||
|
||||
writefile('verify-movsafe','Unverified\n')
|
||||
writefile('verify-embeddingdegree','Unverified\n')
|
||||
if gcdlpis1 and l.is_prime():
|
||||
u = Integers(l)(p)
|
||||
d = l-1
|
||||
needtofactor.add(d)
|
||||
for v in V:
|
||||
while d % v == 0: d /= v
|
||||
if d == 1:
|
||||
d = l-1
|
||||
for v in V:
|
||||
while d % v == 0:
|
||||
if u^(d/v) != 1: break
|
||||
d /= v
|
||||
safetransfer &= requirement('verify-movsafe',(l-1)/d <= 100)
|
||||
writefile('verify-embeddingdegree','<font size=1>%s</font><br>= (l-1)/%s\n' % (d,(l-1)/d))
|
||||
|
||||
t = p+1-l*round((p+1)/l)
|
||||
if l^2 > 16*p:
|
||||
writefile('verify-trace','%s\n' % t)
|
||||
f = factor(1)
|
||||
d = (p+1-t)/l
|
||||
needtofactor.add(d)
|
||||
for v in V:
|
||||
while d % v == 0:
|
||||
d //= v
|
||||
f *= factor(v)
|
||||
writefile('verify-cofactor','%s\n' % f)
|
||||
else:
|
||||
writefile('verify-trace','Unverified\n')
|
||||
writefile('verify-cofactor','Unverified\n')
|
||||
|
||||
D = t^2-4*p
|
||||
needtofactor.add(D)
|
||||
for v in V:
|
||||
while D % v^2 == 0: D /= v^2
|
||||
if prod([v for v in V if D % v == 0]) != -D:
|
||||
writefile('verify-disc','Unverified\n')
|
||||
writefile('verify-discisbig','Unverified\n')
|
||||
safedisc = False
|
||||
else:
|
||||
f = -prod([factor(v) for v in V if D % v == 0])
|
||||
if D % 4 != 1:
|
||||
D *= 4
|
||||
f = factor(4) * f
|
||||
Dbits = (log(-D)/log(2)).numerical_approx()
|
||||
writefile('verify-disc','<font size=1>%s</font><br>= <font size=1>%s</font><br>≈ -2^%.1f\n' % (D,f,Dbits))
|
||||
safedisc &= requirement('verify-discisbig',D < -2^100)
|
||||
|
||||
pi4 = 0.78539816339744830961566084581987572105
|
||||
rho = log(pi4*l)/log(4)
|
||||
writefile('verify-rho','%.1f\n' % rho)
|
||||
saferho &= requirement('verify-rhoabove100',rho.numerical_approx() >= 100)
|
||||
|
||||
twistl = 'Unverified'
|
||||
d = p+1+t
|
||||
needtofactor.add(d)
|
||||
for v in V:
|
||||
while d % v == 0: d /= v
|
||||
if d == 1:
|
||||
d = p+1+t
|
||||
for v in V:
|
||||
if d % v == 0:
|
||||
if twistl == 'Unverified' or v > twistl: twistl = v
|
||||
|
||||
writefile('verify-twistl','%s\n' % twistl)
|
||||
writefile('verify-twistembeddingdegree','Unverified\n')
|
||||
writefile('verify-twistmovsafe','Unverified\n')
|
||||
if twistl == 'Unverified':
|
||||
writefile('hex-twistl','Unverified\n')
|
||||
writefile('expand2-twistl','Unverified\n')
|
||||
writefile('verify-twistcofactor','Unverified\n')
|
||||
writefile('verify-gcdtwistlp1','Unverified\n')
|
||||
writefile('verify-twistrho','Unverified\n')
|
||||
safetwist = False
|
||||
else:
|
||||
writefile('hex-twistl',hex(twistl) + '\n')
|
||||
writefile('expand2-twistl','<br>= %s\n' % expand2(twistl))
|
||||
f = factor(1)
|
||||
d = (p+1+t)/twistl
|
||||
needtofactor.add(d)
|
||||
for v in V:
|
||||
while d % v == 0:
|
||||
d //= v
|
||||
f *= factor(v)
|
||||
writefile('verify-twistcofactor','%s\n' % f)
|
||||
gcdtwistlpis1 = gcd(twistl,p) == 1
|
||||
safetwist &= requirement('verify-gcdtwistlp1',gcdtwistlpis1)
|
||||
|
||||
movsafe = 'Unverified'
|
||||
embeddingdegree = 'Unverified'
|
||||
if gcdtwistlpis1 and twistl.is_prime():
|
||||
u = Integers(twistl)(p)
|
||||
d = twistl-1
|
||||
needtofactor.add(d)
|
||||
for v in V:
|
||||
while d % v == 0: d /= v
|
||||
if d == 1:
|
||||
d = twistl-1
|
||||
for v in V:
|
||||
while d % v == 0:
|
||||
if u^(d/v) != 1: break
|
||||
d /= v
|
||||
safetwist &= requirement('verify-twistmovsafe',(twistl-1)/d <= 100)
|
||||
writefile('verify-twistembeddingdegree',"<font size=1>%s</font><br>= (l'-1)/%s\n" % (d,(twistl-1)/d))
|
||||
|
||||
rho = log(pi4*twistl)/log(4)
|
||||
writefile('verify-twistrho','%.1f\n' % rho)
|
||||
safetwist &= requirement('verify-twistrhoabove100',rho.numerical_approx() >= 100)
|
||||
|
||||
precomp = 0
|
||||
joint = l
|
||||
needtofactor.add(p+1-t)
|
||||
needtofactor.add(p+1+t)
|
||||
for v in V:
|
||||
d1 = p+1-t
|
||||
d2 = p+1+t
|
||||
while d1 % v == 0 or d2 % v == 0:
|
||||
if d1 % v == 0: d1 //= v
|
||||
if d2 % v == 0: d2 //= v
|
||||
# best case for attack: cyclic; each power is usable
|
||||
# also assume that kangaroo is as efficient as rho
|
||||
if v + sqrt(pi4*joint/v) < sqrt(pi4*joint):
|
||||
precomp += v
|
||||
joint /= v
|
||||
|
||||
rho = log(precomp + sqrt(pi4 * joint))/log(2)
|
||||
writefile('verify-jointrho','%.1f\n' % rho)
|
||||
safetwist &= requirement('verify-jointrhoabove100',rho.numerical_approx() >= 100)
|
||||
|
||||
|
||||
x0 = k(x0)
|
||||
y0 = k(y0)
|
||||
x1 = k(x1)
|
||||
y1 = k(y1)
|
||||
|
||||
if shape in ('edwards', 'tedwards'):
|
||||
d = Integer(readfile('d'))
|
||||
a = 1
|
||||
if shape == 'tedwards':
|
||||
a = Integer(readfile('a'))
|
||||
|
||||
writefile('verify-shape','Twisted Edwards\n')
|
||||
writefile('verify-equation','%sx^2+y^2 = 1%+dx^2y^2\n' % (a, d))
|
||||
if a == 1:
|
||||
writefile('verify-shape','Edwards\n')
|
||||
writefile('verify-equation','x^2+y^2 = 1%+dx^2y^2\n' % d)
|
||||
|
||||
a = k(a)
|
||||
d = k(d)
|
||||
elliptic = a*d*(a-d)
|
||||
level0 = a*x0^2+y0^2-1-d*x0^2*y0^2
|
||||
level1 = a*x1^2+y1^2-1-d*x1^2*y1^2
|
||||
|
||||
if shape == 'montgomery':
|
||||
writefile('verify-shape','Montgomery\n')
|
||||
A = Integer(readfile('A'))
|
||||
B = Integer(readfile('B'))
|
||||
equation = '%sy^2 = x^3<wbr>%+dx^2+x' % (B,A)
|
||||
if B == 1:
|
||||
equation = 'y^2 = x^3<wbr>%+dx^2+x' % A
|
||||
writefile('verify-equation',equation + '\n')
|
||||
|
||||
A = k(A)
|
||||
B = k(B)
|
||||
elliptic = B*(A^2-4)
|
||||
level0 = B*y0^2-x0^3-A*x0^2-x0
|
||||
level1 = B*y1^2-x1^3-A*x1^2-x1
|
||||
|
||||
if shape == 'shortw':
|
||||
writefile('verify-shape','short Weierstrass\n')
|
||||
a = Integer(readfile('a'))
|
||||
b = Integer(readfile('b'))
|
||||
writefile('verify-equation','y^2 = x^3<wbr>%+dx<wbr>%+d\n' % (a,b))
|
||||
|
||||
a = k(a)
|
||||
b = k(b)
|
||||
elliptic = 4*a^3+27*b^2
|
||||
level0 = y0^2-x0^3-a*x0-b
|
||||
level1 = y1^2-x1^3-a*x1-b
|
||||
|
||||
writefile('verify-elliptic',str(elliptic) + '\n')
|
||||
safeeq &= requirement('verify-iselliptic',elliptic != 0)
|
||||
safebase &= requirement('verify-isoncurve0',level0 == 0)
|
||||
safebase &= requirement('verify-isoncurve1',level1 == 0)
|
||||
|
||||
if shape in ('edwards', 'tedwards'):
|
||||
A = 2*(a+d)/(a-d)
|
||||
B = 4/(a-d)
|
||||
x0,y0 = (1+y0)/(1-y0),((1+y0)/(1-y0))/x0
|
||||
x1,y1 = (1+y1)/(1-y1),((1+y1)/(1-y1))/x1
|
||||
shape = 'montgomery'
|
||||
|
||||
if shape == 'montgomery':
|
||||
a = (3-A^2)/(3*B^2)
|
||||
b = (2*A^3-9*A)/(27*B^3)
|
||||
x0,y0 = (x0+A/3)/B,y0/B
|
||||
x1,y1 = (x1+A/3)/B,y1/B
|
||||
shape = 'shortw'
|
||||
|
||||
try:
|
||||
E = EllipticCurve([a,b])
|
||||
numorder2 = 0
|
||||
numorder4 = 0
|
||||
for P in E(0).division_points(4):
|
||||
if P != 0 and 2*P == 0:
|
||||
numorder2 += 1
|
||||
if 2*P != 0 and 4*P == 0:
|
||||
numorder4 += 1
|
||||
writefile('verify-numorder2',str(numorder2) + '\n')
|
||||
writefile('verify-numorder4',str(numorder4) + '\n')
|
||||
completesingle = False
|
||||
completemulti = False
|
||||
if numorder4 == 2 and numorder2 == 1:
|
||||
# complete edwards form, and montgomery with unique point of order 2
|
||||
completesingle = True
|
||||
completemulti = True
|
||||
# should extend this to allow complete twisted hessian
|
||||
safecomplete &= requirement('verify-completesingle',completesingle)
|
||||
safecomplete &= requirement('verify-completemulti',completemulti)
|
||||
safecomplete &= requirement('verify-ltimesbase1is0',l * E([x1,y1]) == 0)
|
||||
writefile('verify-ltimesbase1',str(l * E([x1,y1])) + '\n')
|
||||
writefile('verify-cofactorbase01',str(((p+1-t)//l) * E([x0,y0]) == E([x1,y1])) + '\n')
|
||||
except:
|
||||
writefile('verify-numorder2','Unverified\n')
|
||||
writefile('verify-numorder4','Unverified\n')
|
||||
writefile('verify-ltimesbase1','Unverified\n')
|
||||
writefile('verify-cofactorbase01','Unverified\n')
|
||||
safecomplete = False
|
||||
|
||||
montladder = False
|
||||
for r,e in (z^3+a*z+b).roots():
|
||||
if (3*r^2+a).is_square():
|
||||
montladder = True
|
||||
safeladder &= requirement('verify-montladder',montladder)
|
||||
|
||||
indistinguishability = False
|
||||
elligator2 = False
|
||||
if (p+1-t) % 2 == 0:
|
||||
if b != 0:
|
||||
indistinguishability = True
|
||||
elligator2 = True
|
||||
safeind &= requirement('verify-indistinguishability',indistinguishability)
|
||||
writefile('verify-ind-notes','Elligator 2: %s.\n' % ['No','Yes'][elligator2])
|
||||
|
||||
saferigid &= (rigid == 'fully rigid' or rigid == 'somewhat rigid')
|
||||
|
||||
safecurve = True
|
||||
safecurve &= requirement('verify-safefield',safefield)
|
||||
safecurve &= requirement('verify-safeeq',safeeq)
|
||||
safecurve &= requirement('verify-safebase',safebase)
|
||||
safecurve &= requirement('verify-saferho',saferho)
|
||||
safecurve &= requirement('verify-safetransfer',safetransfer)
|
||||
safecurve &= requirement('verify-safedisc',safedisc)
|
||||
safecurve &= requirement('verify-saferigid',saferigid)
|
||||
safecurve &= requirement('verify-safeladder',safeladder)
|
||||
safecurve &= requirement('verify-safetwist',safetwist)
|
||||
safecurve &= requirement('verify-safecomplete',safecomplete)
|
||||
safecurve &= requirement('verify-safeind',safeind)
|
||||
requirement('verify-safecurve',safecurve)
|
||||
|
||||
originaldir = os.open('.',os.O_RDONLY)
|
||||
for i in range(1,len(sys.argv)):
|
||||
os.fchdir(originaldir)
|
||||
os.chdir(sys.argv[i])
|
||||
verify()
|
||||
|
1
jubjub/doc/evidence/x0
Normal file
1
jubjub/doc/evidence/x0
Normal file
@ -0,0 +1 @@
|
||||
11076627216317271660298050606127911965867021807910416450833192264015104452986
|
1
jubjub/doc/evidence/x1
Normal file
1
jubjub/doc/evidence/x1
Normal file
@ -0,0 +1 @@
|
||||
8076246640662884909881801758704306714034609987455869804520522091855516602923
|
1
jubjub/doc/evidence/y0
Normal file
1
jubjub/doc/evidence/y0
Normal file
@ -0,0 +1 @@
|
||||
44412834903739585386157632289020980010620626017712148233229312325549216099227
|
1
jubjub/doc/evidence/y1
Normal file
1
jubjub/doc/evidence/y1
Normal file
@ -0,0 +1 @@
|
||||
13262374693698910701929044844600465831413122818447359594527400194675274060458
|
1024
jubjub/src/fr.rs
Normal file
1024
jubjub/src/fr.rs
Normal file
File diff suppressed because it is too large
Load Diff
1328
jubjub/src/lib.rs
Normal file
1328
jubjub/src/lib.rs
Normal file
File diff suppressed because it is too large
Load Diff
174
jubjub/src/util.rs
Normal file
174
jubjub/src/util.rs
Normal file
@ -0,0 +1,174 @@
|
||||
/// Compute a + b + carry, returning the result and the new carry over.
|
||||
#[inline(always)]
|
||||
pub 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 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 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)
|
||||
}
|
||||
|
||||
macro_rules! impl_add_binop_specify_output {
|
||||
($lhs:ident, $rhs:ident, $output:ident) => {
|
||||
impl<'b> Add<&'b $rhs> for $lhs {
|
||||
type Output = $output;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: &'b $rhs) -> $output {
|
||||
&self + rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Add<$rhs> for &'a $lhs {
|
||||
type Output = $output;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: $rhs) -> $output {
|
||||
self + &rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl 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> Sub<&'b $rhs> for $lhs {
|
||||
type Output = $output;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: &'b $rhs) -> $output {
|
||||
&self - rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Sub<$rhs> for &'a $lhs {
|
||||
type Output = $output;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: $rhs) -> $output {
|
||||
self - &rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl 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> Mul<&'b $rhs> for $lhs {
|
||||
type Output = $output;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, rhs: &'b $rhs) -> $output {
|
||||
&self * rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Mul<$rhs> for &'a $lhs {
|
||||
type Output = $output;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, rhs: $rhs) -> $output {
|
||||
self * &rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl 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 SubAssign<$rhs> for $lhs {
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, rhs: $rhs) {
|
||||
*self = &*self - &rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<$rhs> for $lhs {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, rhs: $rhs) {
|
||||
*self = &*self + &rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> SubAssign<&'b $rhs> for $lhs {
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, rhs: &'b $rhs) {
|
||||
*self = &*self - rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> 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 MulAssign<$rhs> for $lhs {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, rhs: $rhs) {
|
||||
*self = &*self * &rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> MulAssign<&'b $rhs> for $lhs {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, rhs: &'b $rhs) {
|
||||
*self = &*self * rhs;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
29
jubjub/tests/common.rs
Normal file
29
jubjub/tests/common.rs
Normal file
@ -0,0 +1,29 @@
|
||||
use jubjub::*;
|
||||
use rand_core::{RngCore, SeedableRng};
|
||||
use rand_xorshift::XorShiftRng;
|
||||
|
||||
pub const NUM_BLACK_BOX_CHECKS: u32 = 2000;
|
||||
|
||||
pub fn new_rng() -> XorShiftRng {
|
||||
XorShiftRng::from_seed([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])
|
||||
}
|
||||
|
||||
pub trait MyRandom {
|
||||
fn new_random<T: RngCore>(rng: &mut T) -> Self;
|
||||
}
|
||||
|
||||
impl MyRandom for Fq {
|
||||
fn new_random<T: RngCore>(rng: &mut T) -> Self {
|
||||
let mut random_bytes = [0u8; 64];
|
||||
rng.fill_bytes(&mut random_bytes);
|
||||
Fq::from_bytes_wide(&random_bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl MyRandom for Fr {
|
||||
fn new_random<T: RngCore>(rng: &mut T) -> Self {
|
||||
let mut random_bytes = [0u8; 64];
|
||||
rng.fill_bytes(&mut random_bytes);
|
||||
Fr::from_bytes_wide(&random_bytes)
|
||||
}
|
||||
}
|
120
jubjub/tests/fq_blackbox.rs
Normal file
120
jubjub/tests/fq_blackbox.rs
Normal file
@ -0,0 +1,120 @@
|
||||
mod common;
|
||||
|
||||
use common::{new_rng, MyRandom, NUM_BLACK_BOX_CHECKS};
|
||||
use jubjub::*;
|
||||
|
||||
#[test]
|
||||
fn test_to_and_from_bytes() {
|
||||
let mut rng = new_rng();
|
||||
for _ in 0..NUM_BLACK_BOX_CHECKS {
|
||||
let a = Fq::new_random(&mut rng);
|
||||
assert_eq!(a, Fq::from_bytes(&Fq::to_bytes(&a)).unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_additive_associativity() {
|
||||
let mut rng = new_rng();
|
||||
for _ in 0..NUM_BLACK_BOX_CHECKS {
|
||||
let a = Fq::new_random(&mut rng);
|
||||
let b = Fq::new_random(&mut rng);
|
||||
let c = Fq::new_random(&mut rng);
|
||||
assert_eq!((a + b) + c, a + (b + c))
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_additive_identity() {
|
||||
let mut rng = new_rng();
|
||||
for _ in 0..NUM_BLACK_BOX_CHECKS {
|
||||
let a = Fq::new_random(&mut rng);
|
||||
assert_eq!(a, a + Fq::zero());
|
||||
assert_eq!(a, Fq::zero() + a);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_subtract_additive_identity() {
|
||||
let mut rng = new_rng();
|
||||
for _ in 0..NUM_BLACK_BOX_CHECKS {
|
||||
let a = Fq::new_random(&mut rng);
|
||||
assert_eq!(a, a - Fq::zero());
|
||||
assert_eq!(a, Fq::zero() - -&a);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_additive_inverse() {
|
||||
let mut rng = new_rng();
|
||||
for _ in 0..NUM_BLACK_BOX_CHECKS {
|
||||
let a = Fq::new_random(&mut rng);
|
||||
let a_neg = -&a;
|
||||
assert_eq!(Fq::zero(), a + a_neg);
|
||||
assert_eq!(Fq::zero(), a_neg + a);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_additive_commutativity() {
|
||||
let mut rng = new_rng();
|
||||
for _ in 0..NUM_BLACK_BOX_CHECKS {
|
||||
let a = Fq::new_random(&mut rng);
|
||||
let b = Fq::new_random(&mut rng);
|
||||
assert_eq!(a + b, b + a);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiplicative_associativity() {
|
||||
let mut rng = new_rng();
|
||||
for _ in 0..NUM_BLACK_BOX_CHECKS {
|
||||
let a = Fq::new_random(&mut rng);
|
||||
let b = Fq::new_random(&mut rng);
|
||||
let c = Fq::new_random(&mut rng);
|
||||
assert_eq!((a * b) * c, a * (b * c))
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiplicative_identity() {
|
||||
let mut rng = new_rng();
|
||||
for _ in 0..NUM_BLACK_BOX_CHECKS {
|
||||
let a = Fq::new_random(&mut rng);
|
||||
assert_eq!(a, a * Fq::one());
|
||||
assert_eq!(a, Fq::one() * a);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiplicative_inverse() {
|
||||
let mut rng = new_rng();
|
||||
for _ in 0..NUM_BLACK_BOX_CHECKS {
|
||||
let a = Fq::new_random(&mut rng);
|
||||
if a == Fq::zero() {
|
||||
continue;
|
||||
}
|
||||
let a_inv = a.invert().unwrap();
|
||||
assert_eq!(Fq::one(), a * a_inv);
|
||||
assert_eq!(Fq::one(), a_inv * a);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiplicative_commutativity() {
|
||||
let mut rng = new_rng();
|
||||
for _ in 0..NUM_BLACK_BOX_CHECKS {
|
||||
let a = Fq::new_random(&mut rng);
|
||||
let b = Fq::new_random(&mut rng);
|
||||
assert_eq!(a * b, b * a);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiply_additive_identity() {
|
||||
let mut rng = new_rng();
|
||||
for _ in 0..NUM_BLACK_BOX_CHECKS {
|
||||
let a = Fq::new_random(&mut rng);
|
||||
assert_eq!(Fq::zero(), Fq::zero() * a);
|
||||
assert_eq!(Fq::zero(), a * Fq::zero());
|
||||
}
|
||||
}
|
120
jubjub/tests/fr_blackbox.rs
Normal file
120
jubjub/tests/fr_blackbox.rs
Normal file
@ -0,0 +1,120 @@
|
||||
mod common;
|
||||
|
||||
use common::{new_rng, MyRandom, NUM_BLACK_BOX_CHECKS};
|
||||
use jubjub::*;
|
||||
|
||||
#[test]
|
||||
fn test_to_and_from_bytes() {
|
||||
let mut rng = new_rng();
|
||||
for _ in 0..NUM_BLACK_BOX_CHECKS {
|
||||
let a = Fr::new_random(&mut rng);
|
||||
assert_eq!(a, Fr::from_bytes(&Fr::to_bytes(&a)).unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_additive_associativity() {
|
||||
let mut rng = new_rng();
|
||||
for _ in 0..NUM_BLACK_BOX_CHECKS {
|
||||
let a = Fr::new_random(&mut rng);
|
||||
let b = Fr::new_random(&mut rng);
|
||||
let c = Fr::new_random(&mut rng);
|
||||
assert_eq!((a + b) + c, a + (b + c))
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_additive_identity() {
|
||||
let mut rng = new_rng();
|
||||
for _ in 0..NUM_BLACK_BOX_CHECKS {
|
||||
let a = Fr::new_random(&mut rng);
|
||||
assert_eq!(a, a + Fr::zero());
|
||||
assert_eq!(a, Fr::zero() + a);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_subtract_additive_identity() {
|
||||
let mut rng = new_rng();
|
||||
for _ in 0..NUM_BLACK_BOX_CHECKS {
|
||||
let a = Fr::new_random(&mut rng);
|
||||
assert_eq!(a, a - Fr::zero());
|
||||
assert_eq!(a, Fr::zero() - -&a);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_additive_inverse() {
|
||||
let mut rng = new_rng();
|
||||
for _ in 0..NUM_BLACK_BOX_CHECKS {
|
||||
let a = Fr::new_random(&mut rng);
|
||||
let a_neg = -&a;
|
||||
assert_eq!(Fr::zero(), a + a_neg);
|
||||
assert_eq!(Fr::zero(), a_neg + a);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_additive_commutativity() {
|
||||
let mut rng = new_rng();
|
||||
for _ in 0..NUM_BLACK_BOX_CHECKS {
|
||||
let a = Fr::new_random(&mut rng);
|
||||
let b = Fr::new_random(&mut rng);
|
||||
assert_eq!(a + b, b + a);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiplicative_associativity() {
|
||||
let mut rng = new_rng();
|
||||
for _ in 0..NUM_BLACK_BOX_CHECKS {
|
||||
let a = Fr::new_random(&mut rng);
|
||||
let b = Fr::new_random(&mut rng);
|
||||
let c = Fr::new_random(&mut rng);
|
||||
assert_eq!((a * b) * c, a * (b * c))
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiplicative_identity() {
|
||||
let mut rng = new_rng();
|
||||
for _ in 0..NUM_BLACK_BOX_CHECKS {
|
||||
let a = Fr::new_random(&mut rng);
|
||||
assert_eq!(a, a * Fr::one());
|
||||
assert_eq!(a, Fr::one() * a);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiplicative_inverse() {
|
||||
let mut rng = new_rng();
|
||||
for _ in 0..NUM_BLACK_BOX_CHECKS {
|
||||
let a = Fr::new_random(&mut rng);
|
||||
if a == Fr::zero() {
|
||||
continue;
|
||||
}
|
||||
let a_inv = a.invert().unwrap();
|
||||
assert_eq!(Fr::one(), a * a_inv);
|
||||
assert_eq!(Fr::one(), a_inv * a);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiplicative_commutativity() {
|
||||
let mut rng = new_rng();
|
||||
for _ in 0..NUM_BLACK_BOX_CHECKS {
|
||||
let a = Fr::new_random(&mut rng);
|
||||
let b = Fr::new_random(&mut rng);
|
||||
assert_eq!(a * b, b * a);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiply_additive_identity() {
|
||||
let mut rng = new_rng();
|
||||
for _ in 0..NUM_BLACK_BOX_CHECKS {
|
||||
let a = Fr::new_random(&mut rng);
|
||||
assert_eq!(Fr::zero(), Fr::zero() * a);
|
||||
assert_eq!(Fr::zero(), a * Fr::zero());
|
||||
}
|
||||
}
|
@ -24,9 +24,8 @@ fn test_key_agreement() {
|
||||
let addr = loop {
|
||||
let mut d = [0; 11];
|
||||
rng.fill_bytes(&mut d);
|
||||
match vk.to_payment_address(Diversifier(d), ¶ms) {
|
||||
Some(a) => break a,
|
||||
None => {}
|
||||
if let Some(a) = vk.to_payment_address(Diversifier(d), ¶ms) {
|
||||
break a;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -21,8 +21,10 @@ byteorder = "1"
|
||||
ff = { version = "0.6", path = "../ff", features = ["derive"] }
|
||||
group = { version = "0.6", path = "../group" }
|
||||
rand_core = "0.5"
|
||||
subtle = "2.2.1"
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.3"
|
||||
rand_xorshift = "0.2"
|
||||
|
||||
[features]
|
||||
@ -30,5 +32,9 @@ unstable-features = ["expose-arith"]
|
||||
expose-arith = []
|
||||
default = []
|
||||
|
||||
[[bench]]
|
||||
name = "pairing_benches"
|
||||
harness = false
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "actively-developed" }
|
||||
|
@ -1,13 +1,14 @@
|
||||
mod g1 {
|
||||
pub(crate) mod g1 {
|
||||
use criterion::{criterion_group, Criterion};
|
||||
use rand_core::SeedableRng;
|
||||
use rand_xorshift::XorShiftRng;
|
||||
use std::ops::AddAssign;
|
||||
|
||||
use ff::Field;
|
||||
use group::CurveProjective;
|
||||
use pairing::bls12_381::*;
|
||||
|
||||
#[bench]
|
||||
fn bench_g1_mul_assign(b: &mut ::test::Bencher) {
|
||||
fn bench_g1_mul_assign(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -20,16 +21,17 @@ mod g1 {
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.mul_assign(v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("G1::mul_assign", |b| {
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.mul_assign(v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_g1_add_assign(b: &mut ::test::Bencher) {
|
||||
fn bench_g1_add_assign(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -42,16 +44,17 @@ mod g1 {
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.add_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("G1::add_assign", |b| {
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.add_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_g1_add_assign_mixed(b: &mut ::test::Bencher) {
|
||||
fn bench_g1_add_assign_mixed(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -64,25 +67,35 @@ mod g1 {
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.add_assign_mixed(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("G1::add_assign_mixed", |b| {
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.add_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(
|
||||
benches,
|
||||
bench_g1_add_assign,
|
||||
bench_g1_add_assign_mixed,
|
||||
bench_g1_mul_assign,
|
||||
);
|
||||
}
|
||||
|
||||
mod g2 {
|
||||
pub(crate) mod g2 {
|
||||
use criterion::{criterion_group, Criterion};
|
||||
use rand_core::SeedableRng;
|
||||
use rand_xorshift::XorShiftRng;
|
||||
use std::ops::AddAssign;
|
||||
|
||||
use ff::Field;
|
||||
use group::CurveProjective;
|
||||
use pairing::bls12_381::*;
|
||||
|
||||
#[bench]
|
||||
fn bench_g2_mul_assign(b: &mut ::test::Bencher) {
|
||||
fn bench_g2_mul_assign(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -95,16 +108,17 @@ mod g2 {
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.mul_assign(v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("G2::mul_assign", |b| {
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.mul_assign(v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_g2_add_assign(b: &mut ::test::Bencher) {
|
||||
fn bench_g2_add_assign(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -117,16 +131,17 @@ mod g2 {
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.add_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("G2::add_assign", |b| {
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.add_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_g2_add_assign_mixed(b: &mut ::test::Bencher) {
|
||||
fn bench_g2_add_assign_mixed(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -139,11 +154,20 @@ mod g2 {
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.add_assign_mixed(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("G2::add_assign_mixed", |b| {
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.add_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(
|
||||
benches,
|
||||
bench_g2_add_assign,
|
||||
bench_g2_add_assign_mixed,
|
||||
bench_g2_mul_assign,
|
||||
);
|
||||
}
|
||||
|
@ -1,11 +1,12 @@
|
||||
use criterion::{criterion_group, Criterion};
|
||||
use rand_core::SeedableRng;
|
||||
use rand_xorshift::XorShiftRng;
|
||||
use std::ops::{AddAssign, MulAssign, Neg, SubAssign};
|
||||
|
||||
use ff::{Field, PrimeField, PrimeFieldRepr, SqrtField};
|
||||
use pairing::bls12_381::*;
|
||||
|
||||
#[bench]
|
||||
fn bench_fq_repr_add_nocarry(b: &mut ::test::Bencher) {
|
||||
fn bench_fq_repr_add_nocarry(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -27,16 +28,17 @@ fn bench_fq_repr_add_nocarry(b: &mut ::test::Bencher) {
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.add_nocarry(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("FqRepr::add_nocarry", |b| {
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.add_nocarry(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq_repr_sub_noborrow(b: &mut ::test::Bencher) {
|
||||
fn bench_fq_repr_sub_noborrow(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -57,16 +59,17 @@ fn bench_fq_repr_sub_noborrow(b: &mut ::test::Bencher) {
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.sub_noborrow(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("FqRepr::sub_noborrow", |b| {
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.sub_noborrow(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq_repr_num_bits(b: &mut ::test::Bencher) {
|
||||
fn bench_fq_repr_num_bits(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -79,15 +82,16 @@ fn bench_fq_repr_num_bits(b: &mut ::test::Bencher) {
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let tmp = v[count].num_bits();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("FqRepr::num_bits", |b| {
|
||||
b.iter(|| {
|
||||
let tmp = v[count].num_bits();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq_repr_mul2(b: &mut ::test::Bencher) {
|
||||
fn bench_fq_repr_mul2(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -100,16 +104,17 @@ fn bench_fq_repr_mul2(b: &mut ::test::Bencher) {
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count];
|
||||
tmp.mul2();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("FqRepr::mul2", |b| {
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count];
|
||||
tmp.mul2();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq_repr_div2(b: &mut ::test::Bencher) {
|
||||
fn bench_fq_repr_div2(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -122,16 +127,17 @@ fn bench_fq_repr_div2(b: &mut ::test::Bencher) {
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count];
|
||||
tmp.div2();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("FqRepr::div2", |b| {
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count];
|
||||
tmp.div2();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq_add_assign(b: &mut ::test::Bencher) {
|
||||
fn bench_fq_add_assign(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -144,16 +150,17 @@ fn bench_fq_add_assign(b: &mut ::test::Bencher) {
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.add_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("Fq::add_assign", |b| {
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.add_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq_sub_assign(b: &mut ::test::Bencher) {
|
||||
fn bench_fq_sub_assign(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -166,16 +173,17 @@ fn bench_fq_sub_assign(b: &mut ::test::Bencher) {
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.sub_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("Fq::sub_assign", |b| {
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.sub_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq_mul_assign(b: &mut ::test::Bencher) {
|
||||
fn bench_fq_mul_assign(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -188,16 +196,17 @@ fn bench_fq_mul_assign(b: &mut ::test::Bencher) {
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.mul_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("Fq::mul_assign", |b| {
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.mul_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq_square(b: &mut ::test::Bencher) {
|
||||
fn bench_fq_square(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -208,16 +217,16 @@ fn bench_fq_square(b: &mut ::test::Bencher) {
|
||||
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count];
|
||||
tmp.square();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("Fq::square", |b| {
|
||||
b.iter(|| {
|
||||
let tmp = v[count].square();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq_inverse(b: &mut ::test::Bencher) {
|
||||
fn bench_fq_invert(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -228,14 +237,15 @@ fn bench_fq_inverse(b: &mut ::test::Bencher) {
|
||||
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
count = (count + 1) % SAMPLES;
|
||||
v[count].inverse()
|
||||
c.bench_function("Fq::invert", |b| {
|
||||
b.iter(|| {
|
||||
count = (count + 1) % SAMPLES;
|
||||
v[count].invert()
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq_negate(b: &mut ::test::Bencher) {
|
||||
fn bench_fq_neg(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -246,16 +256,16 @@ fn bench_fq_negate(b: &mut ::test::Bencher) {
|
||||
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count];
|
||||
tmp.negate();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("Fq::neg", |b| {
|
||||
b.iter(|| {
|
||||
let tmp = v[count].neg();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq_sqrt(b: &mut ::test::Bencher) {
|
||||
fn bench_fq_sqrt(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -264,22 +274,19 @@ fn bench_fq_sqrt(b: &mut ::test::Bencher) {
|
||||
]);
|
||||
|
||||
let v: Vec<Fq> = (0..SAMPLES)
|
||||
.map(|_| {
|
||||
let mut tmp = Fq::random(&mut rng);
|
||||
tmp.square();
|
||||
tmp
|
||||
})
|
||||
.map(|_| Fq::random(&mut rng).square())
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
count = (count + 1) % SAMPLES;
|
||||
v[count].sqrt()
|
||||
c.bench_function("Fq::sqrt", |b| {
|
||||
b.iter(|| {
|
||||
count = (count + 1) % SAMPLES;
|
||||
v[count].sqrt()
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq_into_repr(b: &mut ::test::Bencher) {
|
||||
fn bench_fq_into_repr(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -290,14 +297,15 @@ fn bench_fq_into_repr(b: &mut ::test::Bencher) {
|
||||
let v: Vec<Fq> = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
count = (count + 1) % SAMPLES;
|
||||
v[count].into_repr()
|
||||
c.bench_function("Fq::into_repr", |b| {
|
||||
b.iter(|| {
|
||||
count = (count + 1) % SAMPLES;
|
||||
v[count].into_repr()
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq_from_repr(b: &mut ::test::Bencher) {
|
||||
fn bench_fq_from_repr(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -310,8 +318,28 @@ fn bench_fq_from_repr(b: &mut ::test::Bencher) {
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
count = (count + 1) % SAMPLES;
|
||||
Fq::from_repr(v[count])
|
||||
c.bench_function("Fq::from_repr", |b| {
|
||||
b.iter(|| {
|
||||
count = (count + 1) % SAMPLES;
|
||||
Fq::from_repr(v[count])
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(
|
||||
benches,
|
||||
bench_fq_repr_add_nocarry,
|
||||
bench_fq_repr_sub_noborrow,
|
||||
bench_fq_repr_num_bits,
|
||||
bench_fq_repr_mul2,
|
||||
bench_fq_repr_div2,
|
||||
bench_fq_add_assign,
|
||||
bench_fq_sub_assign,
|
||||
bench_fq_mul_assign,
|
||||
bench_fq_square,
|
||||
bench_fq_invert,
|
||||
bench_fq_neg,
|
||||
bench_fq_sqrt,
|
||||
bench_fq_into_repr,
|
||||
bench_fq_from_repr,
|
||||
);
|
||||
|
@ -1,11 +1,12 @@
|
||||
use criterion::{criterion_group, Criterion};
|
||||
use rand_core::SeedableRng;
|
||||
use rand_xorshift::XorShiftRng;
|
||||
use std::ops::{AddAssign, MulAssign, SubAssign};
|
||||
|
||||
use ff::Field;
|
||||
use pairing::bls12_381::*;
|
||||
|
||||
#[bench]
|
||||
fn bench_fq12_add_assign(b: &mut ::test::Bencher) {
|
||||
fn bench_fq12_add_assign(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -18,16 +19,17 @@ fn bench_fq12_add_assign(b: &mut ::test::Bencher) {
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.add_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("Fq12::add_assign", |b| {
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.add_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq12_sub_assign(b: &mut ::test::Bencher) {
|
||||
fn bench_fq12_sub_assign(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -40,16 +42,17 @@ fn bench_fq12_sub_assign(b: &mut ::test::Bencher) {
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.sub_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("Fq12::sub_assign", |b| {
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.sub_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq12_mul_assign(b: &mut ::test::Bencher) {
|
||||
fn bench_fq12_mul_assign(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -62,16 +65,17 @@ fn bench_fq12_mul_assign(b: &mut ::test::Bencher) {
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.mul_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("Fq12::mul_assign", |b| {
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.mul_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq12_squaring(b: &mut ::test::Bencher) {
|
||||
fn bench_fq12_squaring(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -82,16 +86,16 @@ fn bench_fq12_squaring(b: &mut ::test::Bencher) {
|
||||
let v: Vec<Fq12> = (0..SAMPLES).map(|_| Fq12::random(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count];
|
||||
tmp.square();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("Fq12::square", |b| {
|
||||
b.iter(|| {
|
||||
let tmp = v[count].square();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq12_inverse(b: &mut ::test::Bencher) {
|
||||
fn bench_fq12_invert(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -102,9 +106,20 @@ fn bench_fq12_inverse(b: &mut ::test::Bencher) {
|
||||
let v: Vec<Fq12> = (0..SAMPLES).map(|_| Fq12::random(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let tmp = v[count].inverse();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("Fq12::invert", |b| {
|
||||
b.iter(|| {
|
||||
let tmp = v[count].invert();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(
|
||||
benches,
|
||||
bench_fq12_add_assign,
|
||||
bench_fq12_sub_assign,
|
||||
bench_fq12_mul_assign,
|
||||
bench_fq12_squaring,
|
||||
bench_fq12_invert,
|
||||
);
|
||||
|
@ -1,11 +1,12 @@
|
||||
use criterion::{criterion_group, Criterion};
|
||||
use rand_core::SeedableRng;
|
||||
use rand_xorshift::XorShiftRng;
|
||||
use std::ops::{AddAssign, MulAssign, SubAssign};
|
||||
|
||||
use ff::{Field, SqrtField};
|
||||
use pairing::bls12_381::*;
|
||||
|
||||
#[bench]
|
||||
fn bench_fq2_add_assign(b: &mut ::test::Bencher) {
|
||||
fn bench_fq2_add_assign(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -18,16 +19,17 @@ fn bench_fq2_add_assign(b: &mut ::test::Bencher) {
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.add_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("Fq2::add_assign", |b| {
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.add_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq2_sub_assign(b: &mut ::test::Bencher) {
|
||||
fn bench_fq2_sub_assign(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -40,16 +42,17 @@ fn bench_fq2_sub_assign(b: &mut ::test::Bencher) {
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.sub_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("Fq2::sub_assign", |b| {
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.sub_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq2_mul_assign(b: &mut ::test::Bencher) {
|
||||
fn bench_fq2_mul_assign(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -62,16 +65,17 @@ fn bench_fq2_mul_assign(b: &mut ::test::Bencher) {
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.mul_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("Fq2::mul_assign", |b| {
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.mul_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq2_squaring(b: &mut ::test::Bencher) {
|
||||
fn bench_fq2_squaring(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -82,16 +86,16 @@ fn bench_fq2_squaring(b: &mut ::test::Bencher) {
|
||||
let v: Vec<Fq2> = (0..SAMPLES).map(|_| Fq2::random(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count];
|
||||
tmp.square();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("Fq2::square", |b| {
|
||||
b.iter(|| {
|
||||
let tmp = v[count].square();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq2_inverse(b: &mut ::test::Bencher) {
|
||||
fn bench_fq2_invert(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -102,15 +106,16 @@ fn bench_fq2_inverse(b: &mut ::test::Bencher) {
|
||||
let v: Vec<Fq2> = (0..SAMPLES).map(|_| Fq2::random(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let tmp = v[count].inverse();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("Fq2::invert", |b| {
|
||||
b.iter(|| {
|
||||
let tmp = v[count].invert();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fq2_sqrt(b: &mut ::test::Bencher) {
|
||||
fn bench_fq2_sqrt(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -121,9 +126,21 @@ fn bench_fq2_sqrt(b: &mut ::test::Bencher) {
|
||||
let v: Vec<Fq2> = (0..SAMPLES).map(|_| Fq2::random(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let tmp = v[count].sqrt();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("Fq2::sqrt", |b| {
|
||||
b.iter(|| {
|
||||
let tmp = v[count].sqrt();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(
|
||||
benches,
|
||||
bench_fq2_add_assign,
|
||||
bench_fq2_sub_assign,
|
||||
bench_fq2_mul_assign,
|
||||
bench_fq2_squaring,
|
||||
bench_fq2_invert,
|
||||
bench_fq2_sqrt,
|
||||
);
|
||||
|
@ -1,11 +1,12 @@
|
||||
use criterion::{criterion_group, Criterion};
|
||||
use rand_core::SeedableRng;
|
||||
use rand_xorshift::XorShiftRng;
|
||||
use std::ops::{AddAssign, MulAssign, Neg, SubAssign};
|
||||
|
||||
use ff::{Field, PrimeField, PrimeFieldRepr, SqrtField};
|
||||
use pairing::bls12_381::*;
|
||||
|
||||
#[bench]
|
||||
fn bench_fr_repr_add_nocarry(b: &mut ::test::Bencher) {
|
||||
fn bench_fr_repr_add_nocarry(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -27,16 +28,17 @@ fn bench_fr_repr_add_nocarry(b: &mut ::test::Bencher) {
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.add_nocarry(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("FrRepr::add_nocarry", |b| {
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.add_nocarry(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fr_repr_sub_noborrow(b: &mut ::test::Bencher) {
|
||||
fn bench_fr_repr_sub_noborrow(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -57,16 +59,17 @@ fn bench_fr_repr_sub_noborrow(b: &mut ::test::Bencher) {
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.sub_noborrow(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("FrRepr::sub_noborrow", |b| {
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.sub_noborrow(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fr_repr_num_bits(b: &mut ::test::Bencher) {
|
||||
fn bench_fr_repr_num_bits(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -79,15 +82,16 @@ fn bench_fr_repr_num_bits(b: &mut ::test::Bencher) {
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let tmp = v[count].num_bits();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("FrRepr::num_bits", |b| {
|
||||
b.iter(|| {
|
||||
let tmp = v[count].num_bits();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fr_repr_mul2(b: &mut ::test::Bencher) {
|
||||
fn bench_fr_repr_mul2(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -100,16 +104,17 @@ fn bench_fr_repr_mul2(b: &mut ::test::Bencher) {
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count];
|
||||
tmp.mul2();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("FrRepr::mul2", |b| {
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count];
|
||||
tmp.mul2();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fr_repr_div2(b: &mut ::test::Bencher) {
|
||||
fn bench_fr_repr_div2(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -122,16 +127,17 @@ fn bench_fr_repr_div2(b: &mut ::test::Bencher) {
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count];
|
||||
tmp.div2();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("FrRepr::div2", |b| {
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count];
|
||||
tmp.div2();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fr_add_assign(b: &mut ::test::Bencher) {
|
||||
fn bench_fr_add_assign(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -144,16 +150,17 @@ fn bench_fr_add_assign(b: &mut ::test::Bencher) {
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.add_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("Fr::add_assign", |b| {
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.add_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fr_sub_assign(b: &mut ::test::Bencher) {
|
||||
fn bench_fr_sub_assign(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -166,16 +173,17 @@ fn bench_fr_sub_assign(b: &mut ::test::Bencher) {
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.sub_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("Fr::sub_assign", |b| {
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.sub_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fr_mul_assign(b: &mut ::test::Bencher) {
|
||||
fn bench_fr_mul_assign(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -188,16 +196,17 @@ fn bench_fr_mul_assign(b: &mut ::test::Bencher) {
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.mul_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("Fr::mul_assign", |b| {
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count].0;
|
||||
tmp.mul_assign(&v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fr_square(b: &mut ::test::Bencher) {
|
||||
fn bench_fr_square(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -208,16 +217,16 @@ fn bench_fr_square(b: &mut ::test::Bencher) {
|
||||
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::random(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count];
|
||||
tmp.square();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("Fr::square", |b| {
|
||||
b.iter(|| {
|
||||
let tmp = v[count].square();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fr_inverse(b: &mut ::test::Bencher) {
|
||||
fn bench_fr_invert(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -228,14 +237,15 @@ fn bench_fr_inverse(b: &mut ::test::Bencher) {
|
||||
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::random(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
count = (count + 1) % SAMPLES;
|
||||
v[count].inverse()
|
||||
c.bench_function("Fr::invert", |b| {
|
||||
b.iter(|| {
|
||||
count = (count + 1) % SAMPLES;
|
||||
v[count].invert()
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fr_negate(b: &mut ::test::Bencher) {
|
||||
fn bench_fr_neg(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -246,16 +256,16 @@ fn bench_fr_negate(b: &mut ::test::Bencher) {
|
||||
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::random(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let mut tmp = v[count];
|
||||
tmp.negate();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("Fr::neg", |b| {
|
||||
b.iter(|| {
|
||||
let tmp = v[count].neg();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fr_sqrt(b: &mut ::test::Bencher) {
|
||||
fn bench_fr_sqrt(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -264,22 +274,19 @@ fn bench_fr_sqrt(b: &mut ::test::Bencher) {
|
||||
]);
|
||||
|
||||
let v: Vec<Fr> = (0..SAMPLES)
|
||||
.map(|_| {
|
||||
let mut tmp = Fr::random(&mut rng);
|
||||
tmp.square();
|
||||
tmp
|
||||
})
|
||||
.map(|_| Fr::random(&mut rng).square())
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
count = (count + 1) % SAMPLES;
|
||||
v[count].sqrt()
|
||||
c.bench_function("Fr::sqrt", |b| {
|
||||
b.iter(|| {
|
||||
count = (count + 1) % SAMPLES;
|
||||
v[count].sqrt()
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fr_into_repr(b: &mut ::test::Bencher) {
|
||||
fn bench_fr_into_repr(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -290,14 +297,15 @@ fn bench_fr_into_repr(b: &mut ::test::Bencher) {
|
||||
let v: Vec<Fr> = (0..SAMPLES).map(|_| Fr::random(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
count = (count + 1) % SAMPLES;
|
||||
v[count].into_repr()
|
||||
c.bench_function("Fr::into_repr", |b| {
|
||||
b.iter(|| {
|
||||
count = (count + 1) % SAMPLES;
|
||||
v[count].into_repr()
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_fr_from_repr(b: &mut ::test::Bencher) {
|
||||
fn bench_fr_from_repr(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -310,8 +318,28 @@ fn bench_fr_from_repr(b: &mut ::test::Bencher) {
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
count = (count + 1) % SAMPLES;
|
||||
Fr::from_repr(v[count])
|
||||
c.bench_function("Fr::from_repr", |b| {
|
||||
b.iter(|| {
|
||||
count = (count + 1) % SAMPLES;
|
||||
Fr::from_repr(v[count])
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(
|
||||
benches,
|
||||
bench_fr_repr_add_nocarry,
|
||||
bench_fr_repr_sub_noborrow,
|
||||
bench_fr_repr_num_bits,
|
||||
bench_fr_repr_mul2,
|
||||
bench_fr_repr_div2,
|
||||
bench_fr_add_assign,
|
||||
bench_fr_sub_assign,
|
||||
bench_fr_mul_assign,
|
||||
bench_fr_square,
|
||||
bench_fr_invert,
|
||||
bench_fr_neg,
|
||||
bench_fr_sqrt,
|
||||
bench_fr_into_repr,
|
||||
bench_fr_from_repr,
|
||||
);
|
||||
|
@ -1,9 +1,10 @@
|
||||
mod ec;
|
||||
mod fq;
|
||||
mod fq12;
|
||||
mod fq2;
|
||||
mod fr;
|
||||
pub(crate) mod ec;
|
||||
pub(crate) mod fq;
|
||||
pub(crate) mod fq12;
|
||||
pub(crate) mod fq2;
|
||||
pub(crate) mod fr;
|
||||
|
||||
use criterion::{criterion_group, Criterion};
|
||||
use rand_core::SeedableRng;
|
||||
use rand_xorshift::XorShiftRng;
|
||||
|
||||
@ -11,8 +12,7 @@ use group::CurveProjective;
|
||||
use pairing::bls12_381::*;
|
||||
use pairing::{Engine, PairingCurveAffine};
|
||||
|
||||
#[bench]
|
||||
fn bench_pairing_g1_preparation(b: &mut ::test::Bencher) {
|
||||
fn bench_pairing_g1_preparation(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -23,15 +23,16 @@ fn bench_pairing_g1_preparation(b: &mut ::test::Bencher) {
|
||||
let v: Vec<G1> = (0..SAMPLES).map(|_| G1::random(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let tmp = G1Affine::from(v[count]).prepare();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("G1 preparation", |b| {
|
||||
b.iter(|| {
|
||||
let tmp = G1Affine::from(v[count]).prepare();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_pairing_g2_preparation(b: &mut ::test::Bencher) {
|
||||
fn bench_pairing_g2_preparation(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -42,15 +43,16 @@ fn bench_pairing_g2_preparation(b: &mut ::test::Bencher) {
|
||||
let v: Vec<G2> = (0..SAMPLES).map(|_| G2::random(&mut rng)).collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let tmp = G2Affine::from(v[count]).prepare();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("G2 preparation", |b| {
|
||||
b.iter(|| {
|
||||
let tmp = G2Affine::from(v[count]).prepare();
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_pairing_miller_loop(b: &mut ::test::Bencher) {
|
||||
fn bench_pairing_miller_loop(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -68,15 +70,16 @@ fn bench_pairing_miller_loop(b: &mut ::test::Bencher) {
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let tmp = Bls12::miller_loop(&[(&v[count].0, &v[count].1)]);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("Miller loop", |b| {
|
||||
b.iter(|| {
|
||||
let tmp = Bls12::miller_loop(&[(&v[count].0, &v[count].1)]);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_pairing_final_exponentiation(b: &mut ::test::Bencher) {
|
||||
fn bench_pairing_final_exponentiation(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -95,15 +98,16 @@ fn bench_pairing_final_exponentiation(b: &mut ::test::Bencher) {
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let tmp = Bls12::final_exponentiation(&v[count]);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("Final exponentiation", |b| {
|
||||
b.iter(|| {
|
||||
let tmp = Bls12::final_exponentiation(&v[count]);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_pairing_full(b: &mut ::test::Bencher) {
|
||||
fn bench_pairing_full(c: &mut Criterion) {
|
||||
const SAMPLES: usize = 1000;
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
@ -116,9 +120,20 @@ fn bench_pairing_full(b: &mut ::test::Bencher) {
|
||||
.collect();
|
||||
|
||||
let mut count = 0;
|
||||
b.iter(|| {
|
||||
let tmp = Bls12::pairing(v[count].0, v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
c.bench_function("Full pairing", |b| {
|
||||
b.iter(|| {
|
||||
let tmp = Bls12::pairing(v[count].0, v[count].1);
|
||||
count = (count + 1) % SAMPLES;
|
||||
tmp
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(
|
||||
benches,
|
||||
bench_pairing_g1_preparation,
|
||||
bench_pairing_g2_preparation,
|
||||
bench_pairing_miller_loop,
|
||||
bench_pairing_final_exponentiation,
|
||||
bench_pairing_full,
|
||||
);
|
||||
|
@ -1,10 +1,12 @@
|
||||
#![feature(test)]
|
||||
|
||||
extern crate ff;
|
||||
extern crate group;
|
||||
extern crate pairing;
|
||||
extern crate rand_core;
|
||||
extern crate rand_xorshift;
|
||||
extern crate test;
|
||||
|
||||
use criterion::criterion_main;
|
||||
mod bls12_381;
|
||||
|
||||
criterion_main!(
|
||||
bls12_381::benches,
|
||||
bls12_381::ec::g1::benches,
|
||||
bls12_381::ec::g2::benches,
|
||||
bls12_381::fq::benches,
|
||||
bls12_381::fq12::benches,
|
||||
bls12_381::fq2::benches,
|
||||
bls12_381::fr::benches,
|
||||
);
|
||||
|
@ -54,10 +54,8 @@ macro_rules! curve_impl {
|
||||
// are equal when (X * Z^2) = (X' * Z'^2)
|
||||
// and (Y * Z^3) = (Y' * Z'^3).
|
||||
|
||||
let mut z1 = self.z;
|
||||
z1.square();
|
||||
let mut z2 = other.z;
|
||||
z2.square();
|
||||
let mut z1 = self.z.square();
|
||||
let mut z2 = other.z.square();
|
||||
|
||||
let mut tmp1 = self.x;
|
||||
tmp1.mul_assign(&z2);
|
||||
@ -88,7 +86,7 @@ macro_rules! curve_impl {
|
||||
for i in bits {
|
||||
res.double();
|
||||
if i {
|
||||
res.add_assign_mixed(self)
|
||||
res.add_assign(self)
|
||||
}
|
||||
}
|
||||
res
|
||||
@ -99,16 +97,14 @@ macro_rules! curve_impl {
|
||||
///
|
||||
/// If and only if `greatest` is set will the lexicographically
|
||||
/// largest y-coordinate be selected.
|
||||
fn get_point_from_x(x: $basefield, greatest: bool) -> Option<$affine> {
|
||||
fn get_point_from_x(x: $basefield, greatest: bool) -> CtOption<$affine> {
|
||||
// Compute x^3 + b
|
||||
let mut x3b = x;
|
||||
x3b.square();
|
||||
let mut x3b = x.square();
|
||||
x3b.mul_assign(&x);
|
||||
x3b.add_assign(&$affine::get_coeff_b());
|
||||
|
||||
x3b.sqrt().map(|y| {
|
||||
let mut negy = y;
|
||||
negy.negate();
|
||||
let negy = y.neg();
|
||||
|
||||
$affine {
|
||||
x: x,
|
||||
@ -123,11 +119,9 @@ macro_rules! curve_impl {
|
||||
true
|
||||
} else {
|
||||
// Check that the point is on the curve
|
||||
let mut y2 = self.y;
|
||||
y2.square();
|
||||
let y2 = self.y.square();
|
||||
|
||||
let mut x3b = self.x;
|
||||
x3b.square();
|
||||
let mut x3b = self.x.square();
|
||||
x3b.mul_assign(&self.x);
|
||||
x3b.add_assign(&Self::get_coeff_b());
|
||||
|
||||
@ -140,6 +134,19 @@ macro_rules! curve_impl {
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::ops::Neg for $affine {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Self {
|
||||
let mut ret = self;
|
||||
if !ret.is_zero() {
|
||||
ret.y = ret.y.neg();
|
||||
}
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl CurveAffine for $affine {
|
||||
type Engine = Bls12;
|
||||
type Scalar = $scalarfield;
|
||||
@ -169,12 +176,6 @@ macro_rules! curve_impl {
|
||||
self.mul_bits(bits)
|
||||
}
|
||||
|
||||
fn negate(&mut self) {
|
||||
if !self.is_zero() {
|
||||
self.y.negate();
|
||||
}
|
||||
}
|
||||
|
||||
fn into_projective(&self) -> $projective {
|
||||
(*self).into()
|
||||
}
|
||||
@ -194,6 +195,309 @@ macro_rules! curve_impl {
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::ops::Neg for $projective {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Self {
|
||||
let mut ret = self;
|
||||
if !ret.is_zero() {
|
||||
ret.y = ret.y.neg();
|
||||
}
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> ::std::ops::Add<&'r $projective> for $projective {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn add(self, other: &Self) -> Self {
|
||||
let mut ret = self;
|
||||
ret.add_assign(other);
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::ops::Add for $projective {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn add(self, other: Self) -> Self {
|
||||
self + &other
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> ::std::ops::AddAssign<&'r $projective> for $projective {
|
||||
fn add_assign(&mut self, other: &Self) {
|
||||
if self.is_zero() {
|
||||
*self = *other;
|
||||
return;
|
||||
}
|
||||
|
||||
if other.is_zero() {
|
||||
return;
|
||||
}
|
||||
|
||||
// http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl
|
||||
|
||||
// Z1Z1 = Z1^2
|
||||
let z1z1 = self.z.square();
|
||||
|
||||
// Z2Z2 = Z2^2
|
||||
let z2z2 = other.z.square();
|
||||
|
||||
// U1 = X1*Z2Z2
|
||||
let mut u1 = self.x;
|
||||
u1.mul_assign(&z2z2);
|
||||
|
||||
// U2 = X2*Z1Z1
|
||||
let mut u2 = other.x;
|
||||
u2.mul_assign(&z1z1);
|
||||
|
||||
// S1 = Y1*Z2*Z2Z2
|
||||
let mut s1 = self.y;
|
||||
s1.mul_assign(&other.z);
|
||||
s1.mul_assign(&z2z2);
|
||||
|
||||
// S2 = Y2*Z1*Z1Z1
|
||||
let mut s2 = other.y;
|
||||
s2.mul_assign(&self.z);
|
||||
s2.mul_assign(&z1z1);
|
||||
|
||||
if u1 == u2 && s1 == s2 {
|
||||
// The two points are equal, so we double.
|
||||
self.double();
|
||||
} else {
|
||||
// If we're adding -a and a together, self.z becomes zero as H becomes zero.
|
||||
|
||||
// H = U2-U1
|
||||
let mut h = u2;
|
||||
h.sub_assign(&u1);
|
||||
|
||||
// I = (2*H)^2
|
||||
let i = h.double().square();
|
||||
|
||||
// J = H*I
|
||||
let mut j = h;
|
||||
j.mul_assign(&i);
|
||||
|
||||
// r = 2*(S2-S1)
|
||||
let mut r = s2;
|
||||
r.sub_assign(&s1);
|
||||
r = r.double();
|
||||
|
||||
// V = U1*I
|
||||
let mut v = u1;
|
||||
v.mul_assign(&i);
|
||||
|
||||
// X3 = r^2 - J - 2*V
|
||||
self.x = r.square();
|
||||
self.x.sub_assign(&j);
|
||||
self.x.sub_assign(&v);
|
||||
self.x.sub_assign(&v);
|
||||
|
||||
// Y3 = r*(V - X3) - 2*S1*J
|
||||
self.y = v;
|
||||
self.y.sub_assign(&self.x);
|
||||
self.y.mul_assign(&r);
|
||||
s1.mul_assign(&j); // S1 = S1 * J * 2
|
||||
s1 = s1.double();
|
||||
self.y.sub_assign(&s1);
|
||||
|
||||
// Z3 = ((Z1+Z2)^2 - Z1Z1 - Z2Z2)*H
|
||||
self.z.add_assign(&other.z);
|
||||
self.z = self.z.square();
|
||||
self.z.sub_assign(&z1z1);
|
||||
self.z.sub_assign(&z2z2);
|
||||
self.z.mul_assign(&h);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::ops::AddAssign for $projective {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, other: Self) {
|
||||
self.add_assign(&other);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> ::std::ops::Sub<&'r $projective> for $projective {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, other: &Self) -> Self {
|
||||
let mut ret = self;
|
||||
ret.sub_assign(other);
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::ops::Sub for $projective {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, other: Self) -> Self {
|
||||
self - &other
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> ::std::ops::SubAssign<&'r $projective> for $projective {
|
||||
fn sub_assign(&mut self, other: &Self) {
|
||||
self.add_assign(&other.neg());
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::ops::SubAssign for $projective {
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, other: Self) {
|
||||
self.sub_assign(&other);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> ::std::ops::Add<&'r <$projective as CurveProjective>::Affine> for $projective {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn add(self, other: &<$projective as CurveProjective>::Affine) -> Self {
|
||||
let mut ret = self;
|
||||
ret.add_assign(other);
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::ops::Add<<$projective as CurveProjective>::Affine> for $projective {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn add(self, other: <$projective as CurveProjective>::Affine) -> Self {
|
||||
self + &other
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> ::std::ops::AddAssign<&'r <$projective as CurveProjective>::Affine>
|
||||
for $projective
|
||||
{
|
||||
fn add_assign(&mut self, other: &<$projective as CurveProjective>::Affine) {
|
||||
if other.is_zero() {
|
||||
return;
|
||||
}
|
||||
|
||||
if self.is_zero() {
|
||||
self.x = other.x;
|
||||
self.y = other.y;
|
||||
self.z = $basefield::one();
|
||||
return;
|
||||
}
|
||||
|
||||
// http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-madd-2007-bl
|
||||
|
||||
// Z1Z1 = Z1^2
|
||||
let z1z1 = self.z.square();
|
||||
|
||||
// U2 = X2*Z1Z1
|
||||
let mut u2 = other.x;
|
||||
u2.mul_assign(&z1z1);
|
||||
|
||||
// S2 = Y2*Z1*Z1Z1
|
||||
let mut s2 = other.y;
|
||||
s2.mul_assign(&self.z);
|
||||
s2.mul_assign(&z1z1);
|
||||
|
||||
if self.x == u2 && self.y == s2 {
|
||||
// The two points are equal, so we double.
|
||||
self.double();
|
||||
} else {
|
||||
// If we're adding -a and a together, self.z becomes zero as H becomes zero.
|
||||
|
||||
// H = U2-X1
|
||||
let mut h = u2;
|
||||
h.sub_assign(&self.x);
|
||||
|
||||
// HH = H^2
|
||||
let hh = h.square();
|
||||
|
||||
// I = 4*HH
|
||||
let i = hh.double().double();
|
||||
|
||||
// J = H*I
|
||||
let mut j = h;
|
||||
j.mul_assign(&i);
|
||||
|
||||
// r = 2*(S2-Y1)
|
||||
let mut r = s2;
|
||||
r.sub_assign(&self.y);
|
||||
r = r.double();
|
||||
|
||||
// V = X1*I
|
||||
let mut v = self.x;
|
||||
v.mul_assign(&i);
|
||||
|
||||
// X3 = r^2 - J - 2*V
|
||||
self.x = r.square();
|
||||
self.x.sub_assign(&j);
|
||||
self.x.sub_assign(&v);
|
||||
self.x.sub_assign(&v);
|
||||
|
||||
// Y3 = r*(V-X3)-2*Y1*J
|
||||
j.mul_assign(&self.y); // J = 2*Y1*J
|
||||
j = j.double();
|
||||
self.y = v;
|
||||
self.y.sub_assign(&self.x);
|
||||
self.y.mul_assign(&r);
|
||||
self.y.sub_assign(&j);
|
||||
|
||||
// Z3 = (Z1+H)^2-Z1Z1-HH
|
||||
self.z.add_assign(&h);
|
||||
self.z = self.z.square();
|
||||
self.z.sub_assign(&z1z1);
|
||||
self.z.sub_assign(&hh);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::ops::AddAssign<<$projective as CurveProjective>::Affine> for $projective {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, other: <$projective as CurveProjective>::Affine) {
|
||||
self.add_assign(&other);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> ::std::ops::Sub<&'r <$projective as CurveProjective>::Affine> for $projective {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, other: &<$projective as CurveProjective>::Affine) -> Self {
|
||||
let mut ret = self;
|
||||
ret.sub_assign(other);
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::ops::Sub<<$projective as CurveProjective>::Affine> for $projective {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, other: <$projective as CurveProjective>::Affine) -> Self {
|
||||
self - &other
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> ::std::ops::SubAssign<&'r <$projective as CurveProjective>::Affine>
|
||||
for $projective
|
||||
{
|
||||
fn sub_assign(&mut self, other: &<$projective as CurveProjective>::Affine) {
|
||||
self.add_assign(&other.neg());
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::ops::SubAssign<<$projective as CurveProjective>::Affine> for $projective {
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, other: <$projective as CurveProjective>::Affine) {
|
||||
self.sub_assign(&other);
|
||||
}
|
||||
}
|
||||
|
||||
impl CurveProjective for $projective {
|
||||
type Engine = Bls12;
|
||||
type Scalar = $scalarfield;
|
||||
@ -205,8 +509,9 @@ macro_rules! curve_impl {
|
||||
let x = $basefield::random(rng);
|
||||
let greatest = rng.next_u32() % 2 != 0;
|
||||
|
||||
if let Some(p) = $affine::get_point_from_x(x, greatest) {
|
||||
let p = p.scale_by_cofactor();
|
||||
let p = $affine::get_point_from_x(x, greatest);
|
||||
if p.is_some().into() {
|
||||
let p = p.unwrap().scale_by_cofactor();
|
||||
|
||||
if !p.is_zero() {
|
||||
return p;
|
||||
@ -257,7 +562,7 @@ macro_rules! curve_impl {
|
||||
}
|
||||
|
||||
// Invert `tmp`.
|
||||
tmp = tmp.inverse().unwrap(); // Guaranteed to be nonzero.
|
||||
tmp = tmp.invert().unwrap(); // Guaranteed to be nonzero.
|
||||
|
||||
// Second pass: iterate backwards to compute inverses
|
||||
for (g, s) in v
|
||||
@ -284,8 +589,7 @@ macro_rules! curve_impl {
|
||||
|
||||
// Perform affine transformations
|
||||
for g in v.iter_mut().filter(|g| !g.is_normalized()) {
|
||||
let mut z = g.z; // 1/z
|
||||
z.square(); // 1/z^2
|
||||
let mut z = g.z.square(); // 1/z^2
|
||||
g.x.mul_assign(&z); // x/z^2
|
||||
z.mul_assign(&g.z); // 1/z^3
|
||||
g.y.mul_assign(&z); // y/z^3
|
||||
@ -306,37 +610,32 @@ macro_rules! curve_impl {
|
||||
// http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l
|
||||
|
||||
// A = X1^2
|
||||
let mut a = self.x;
|
||||
a.square();
|
||||
let a = self.x.square();
|
||||
|
||||
// B = Y1^2
|
||||
let mut b = self.y;
|
||||
b.square();
|
||||
let b = self.y.square();
|
||||
|
||||
// C = B^2
|
||||
let mut c = b;
|
||||
c.square();
|
||||
let mut c = b.square();
|
||||
|
||||
// D = 2*((X1+B)2-A-C)
|
||||
let mut d = self.x;
|
||||
d.add_assign(&b);
|
||||
d.square();
|
||||
d = d.square();
|
||||
d.sub_assign(&a);
|
||||
d.sub_assign(&c);
|
||||
d.double();
|
||||
d = d.double();
|
||||
|
||||
// E = 3*A
|
||||
let mut e = a;
|
||||
e.double();
|
||||
let mut e = a.double();
|
||||
e.add_assign(&a);
|
||||
|
||||
// F = E^2
|
||||
let mut f = e;
|
||||
f.square();
|
||||
let f = e.square();
|
||||
|
||||
// Z3 = 2*Y1*Z1
|
||||
self.z.mul_assign(&self.y);
|
||||
self.z.double();
|
||||
self.z = self.z.double();
|
||||
|
||||
// X3 = F-2*D
|
||||
self.x = f;
|
||||
@ -347,190 +646,10 @@ macro_rules! curve_impl {
|
||||
self.y = d;
|
||||
self.y.sub_assign(&self.x);
|
||||
self.y.mul_assign(&e);
|
||||
c.double();
|
||||
c.double();
|
||||
c.double();
|
||||
c = c.double().double().double();
|
||||
self.y.sub_assign(&c);
|
||||
}
|
||||
|
||||
fn add_assign(&mut self, other: &Self) {
|
||||
if self.is_zero() {
|
||||
*self = *other;
|
||||
return;
|
||||
}
|
||||
|
||||
if other.is_zero() {
|
||||
return;
|
||||
}
|
||||
|
||||
// http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl
|
||||
|
||||
// Z1Z1 = Z1^2
|
||||
let mut z1z1 = self.z;
|
||||
z1z1.square();
|
||||
|
||||
// Z2Z2 = Z2^2
|
||||
let mut z2z2 = other.z;
|
||||
z2z2.square();
|
||||
|
||||
// U1 = X1*Z2Z2
|
||||
let mut u1 = self.x;
|
||||
u1.mul_assign(&z2z2);
|
||||
|
||||
// U2 = X2*Z1Z1
|
||||
let mut u2 = other.x;
|
||||
u2.mul_assign(&z1z1);
|
||||
|
||||
// S1 = Y1*Z2*Z2Z2
|
||||
let mut s1 = self.y;
|
||||
s1.mul_assign(&other.z);
|
||||
s1.mul_assign(&z2z2);
|
||||
|
||||
// S2 = Y2*Z1*Z1Z1
|
||||
let mut s2 = other.y;
|
||||
s2.mul_assign(&self.z);
|
||||
s2.mul_assign(&z1z1);
|
||||
|
||||
if u1 == u2 && s1 == s2 {
|
||||
// The two points are equal, so we double.
|
||||
self.double();
|
||||
} else {
|
||||
// If we're adding -a and a together, self.z becomes zero as H becomes zero.
|
||||
|
||||
// H = U2-U1
|
||||
let mut h = u2;
|
||||
h.sub_assign(&u1);
|
||||
|
||||
// I = (2*H)^2
|
||||
let mut i = h;
|
||||
i.double();
|
||||
i.square();
|
||||
|
||||
// J = H*I
|
||||
let mut j = h;
|
||||
j.mul_assign(&i);
|
||||
|
||||
// r = 2*(S2-S1)
|
||||
let mut r = s2;
|
||||
r.sub_assign(&s1);
|
||||
r.double();
|
||||
|
||||
// V = U1*I
|
||||
let mut v = u1;
|
||||
v.mul_assign(&i);
|
||||
|
||||
// X3 = r^2 - J - 2*V
|
||||
self.x = r;
|
||||
self.x.square();
|
||||
self.x.sub_assign(&j);
|
||||
self.x.sub_assign(&v);
|
||||
self.x.sub_assign(&v);
|
||||
|
||||
// Y3 = r*(V - X3) - 2*S1*J
|
||||
self.y = v;
|
||||
self.y.sub_assign(&self.x);
|
||||
self.y.mul_assign(&r);
|
||||
s1.mul_assign(&j); // S1 = S1 * J * 2
|
||||
s1.double();
|
||||
self.y.sub_assign(&s1);
|
||||
|
||||
// Z3 = ((Z1+Z2)^2 - Z1Z1 - Z2Z2)*H
|
||||
self.z.add_assign(&other.z);
|
||||
self.z.square();
|
||||
self.z.sub_assign(&z1z1);
|
||||
self.z.sub_assign(&z2z2);
|
||||
self.z.mul_assign(&h);
|
||||
}
|
||||
}
|
||||
|
||||
fn add_assign_mixed(&mut self, other: &Self::Affine) {
|
||||
if other.is_zero() {
|
||||
return;
|
||||
}
|
||||
|
||||
if self.is_zero() {
|
||||
self.x = other.x;
|
||||
self.y = other.y;
|
||||
self.z = $basefield::one();
|
||||
return;
|
||||
}
|
||||
|
||||
// http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-madd-2007-bl
|
||||
|
||||
// Z1Z1 = Z1^2
|
||||
let mut z1z1 = self.z;
|
||||
z1z1.square();
|
||||
|
||||
// U2 = X2*Z1Z1
|
||||
let mut u2 = other.x;
|
||||
u2.mul_assign(&z1z1);
|
||||
|
||||
// S2 = Y2*Z1*Z1Z1
|
||||
let mut s2 = other.y;
|
||||
s2.mul_assign(&self.z);
|
||||
s2.mul_assign(&z1z1);
|
||||
|
||||
if self.x == u2 && self.y == s2 {
|
||||
// The two points are equal, so we double.
|
||||
self.double();
|
||||
} else {
|
||||
// If we're adding -a and a together, self.z becomes zero as H becomes zero.
|
||||
|
||||
// H = U2-X1
|
||||
let mut h = u2;
|
||||
h.sub_assign(&self.x);
|
||||
|
||||
// HH = H^2
|
||||
let mut hh = h;
|
||||
hh.square();
|
||||
|
||||
// I = 4*HH
|
||||
let mut i = hh;
|
||||
i.double();
|
||||
i.double();
|
||||
|
||||
// J = H*I
|
||||
let mut j = h;
|
||||
j.mul_assign(&i);
|
||||
|
||||
// r = 2*(S2-Y1)
|
||||
let mut r = s2;
|
||||
r.sub_assign(&self.y);
|
||||
r.double();
|
||||
|
||||
// V = X1*I
|
||||
let mut v = self.x;
|
||||
v.mul_assign(&i);
|
||||
|
||||
// X3 = r^2 - J - 2*V
|
||||
self.x = r;
|
||||
self.x.square();
|
||||
self.x.sub_assign(&j);
|
||||
self.x.sub_assign(&v);
|
||||
self.x.sub_assign(&v);
|
||||
|
||||
// Y3 = r*(V-X3)-2*Y1*J
|
||||
j.mul_assign(&self.y); // J = 2*Y1*J
|
||||
j.double();
|
||||
self.y = v;
|
||||
self.y.sub_assign(&self.x);
|
||||
self.y.mul_assign(&r);
|
||||
self.y.sub_assign(&j);
|
||||
|
||||
// Z3 = (Z1+H)^2-Z1Z1-HH
|
||||
self.z.add_assign(&h);
|
||||
self.z.square();
|
||||
self.z.sub_assign(&z1z1);
|
||||
self.z.sub_assign(&hh);
|
||||
}
|
||||
}
|
||||
|
||||
fn negate(&mut self) {
|
||||
if !self.is_zero() {
|
||||
self.y.negate()
|
||||
}
|
||||
}
|
||||
|
||||
fn mul_assign<S: Into<<Self::Scalar as PrimeField>::Repr>>(&mut self, other: S) {
|
||||
let mut res = Self::zero();
|
||||
|
||||
@ -544,7 +663,7 @@ macro_rules! curve_impl {
|
||||
}
|
||||
|
||||
if i {
|
||||
res.add_assign(self);
|
||||
res.add_assign(&*self);
|
||||
}
|
||||
}
|
||||
|
||||
@ -595,9 +714,8 @@ macro_rules! curve_impl {
|
||||
}
|
||||
} else {
|
||||
// Z is nonzero, so it must have an inverse in a field.
|
||||
let zinv = p.z.inverse().unwrap();
|
||||
let mut zinv_powered = zinv;
|
||||
zinv_powered.square();
|
||||
let zinv = p.z.invert().unwrap();
|
||||
let mut zinv_powered = zinv.square();
|
||||
|
||||
// X/Z^2
|
||||
let mut x = p.x;
|
||||
@ -627,6 +745,8 @@ pub mod g1 {
|
||||
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
|
||||
use rand_core::RngCore;
|
||||
use std::fmt;
|
||||
use std::ops::{AddAssign, MulAssign, Neg, SubAssign};
|
||||
use subtle::CtOption;
|
||||
|
||||
curve_impl!(
|
||||
"G1",
|
||||
@ -831,7 +951,12 @@ pub mod g1 {
|
||||
let x = Fq::from_repr(x)
|
||||
.map_err(|e| GroupDecodingError::CoordinateDecodingError("x coordinate", e))?;
|
||||
|
||||
G1Affine::get_point_from_x(x, greatest).ok_or(GroupDecodingError::NotOnCurve)
|
||||
let ret = G1Affine::get_point_from_x(x, greatest);
|
||||
if ret.is_some().into() {
|
||||
Ok(ret.unwrap())
|
||||
} else {
|
||||
Err(GroupDecodingError::NotOnCurve)
|
||||
}
|
||||
}
|
||||
}
|
||||
fn from_affine(affine: G1Affine) -> Self {
|
||||
@ -848,8 +973,7 @@ pub mod g1 {
|
||||
affine.x.into_repr().write_be(&mut writer).unwrap();
|
||||
}
|
||||
|
||||
let mut negy = affine.y;
|
||||
negy.negate();
|
||||
let negy = affine.y.neg();
|
||||
|
||||
// Set the third most significant bit if the correct y-coordinate
|
||||
// is lexicographically largest.
|
||||
@ -940,15 +1064,15 @@ pub mod g1 {
|
||||
let mut i = 0;
|
||||
loop {
|
||||
// y^2 = x^3 + b
|
||||
let mut rhs = x;
|
||||
rhs.square();
|
||||
let mut rhs = x.square();
|
||||
rhs.mul_assign(&x);
|
||||
rhs.add_assign(&G1Affine::get_coeff_b());
|
||||
|
||||
if let Some(y) = rhs.sqrt() {
|
||||
let y = rhs.sqrt();
|
||||
if y.is_some().into() {
|
||||
let y = y.unwrap();
|
||||
let yrepr = y.into_repr();
|
||||
let mut negy = y;
|
||||
negy.negate();
|
||||
let negy = y.neg();
|
||||
let negyrepr = negy.into_repr();
|
||||
|
||||
let p = G1Affine {
|
||||
@ -1276,7 +1400,7 @@ pub mod g1 {
|
||||
assert_eq!(tmp1, c.into_projective());
|
||||
|
||||
let mut tmp2 = a.into_projective();
|
||||
tmp2.add_assign_mixed(&b);
|
||||
tmp2.add_assign(&b);
|
||||
assert_eq!(tmp2.into_affine(), c);
|
||||
assert_eq!(tmp2, c.into_projective());
|
||||
}
|
||||
@ -1296,6 +1420,8 @@ pub mod g2 {
|
||||
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
|
||||
use rand_core::RngCore;
|
||||
use std::fmt;
|
||||
use std::ops::{AddAssign, MulAssign, Neg, SubAssign};
|
||||
use subtle::CtOption;
|
||||
|
||||
curve_impl!(
|
||||
"G2",
|
||||
@ -1524,7 +1650,12 @@ pub mod g2 {
|
||||
})?,
|
||||
};
|
||||
|
||||
G2Affine::get_point_from_x(x, greatest).ok_or(GroupDecodingError::NotOnCurve)
|
||||
let ret = G2Affine::get_point_from_x(x, greatest);
|
||||
if ret.is_some().into() {
|
||||
Ok(ret.unwrap())
|
||||
} else {
|
||||
Err(GroupDecodingError::NotOnCurve)
|
||||
}
|
||||
}
|
||||
}
|
||||
fn from_affine(affine: G2Affine) -> Self {
|
||||
@ -1542,8 +1673,7 @@ pub mod g2 {
|
||||
affine.x.c0.into_repr().write_be(&mut writer).unwrap();
|
||||
}
|
||||
|
||||
let mut negy = affine.y;
|
||||
negy.negate();
|
||||
let negy = affine.y.neg();
|
||||
|
||||
// Set the third most significant bit if the correct y-coordinate
|
||||
// is lexicographically largest.
|
||||
@ -1646,14 +1776,14 @@ pub mod g2 {
|
||||
let mut i = 0;
|
||||
loop {
|
||||
// y^2 = x^3 + b
|
||||
let mut rhs = x;
|
||||
rhs.square();
|
||||
let mut rhs = x.square();
|
||||
rhs.mul_assign(&x);
|
||||
rhs.add_assign(&G2Affine::get_coeff_b());
|
||||
|
||||
if let Some(y) = rhs.sqrt() {
|
||||
let mut negy = y;
|
||||
negy.negate();
|
||||
let y = rhs.sqrt();
|
||||
if y.is_some().into() {
|
||||
let y = y.unwrap();
|
||||
let negy = y.neg();
|
||||
|
||||
let p = G2Affine {
|
||||
x,
|
||||
|
@ -1,5 +1,9 @@
|
||||
use super::fq2::Fq2;
|
||||
use ff::{Field, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr};
|
||||
use std::ops::{AddAssign, MulAssign, SubAssign};
|
||||
|
||||
#[cfg(test)]
|
||||
use std::ops::Neg;
|
||||
|
||||
// B coefficient of BLS12-381 curve, 4.
|
||||
pub const B_COEFF: Fq = Fq(FqRepr([
|
||||
@ -454,14 +458,14 @@ fn test_b_coeff() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
fn test_frob_coeffs() {
|
||||
let mut nqr = Fq::one();
|
||||
nqr.negate();
|
||||
let nqr = Fq::one().neg();
|
||||
|
||||
assert_eq!(FROBENIUS_COEFF_FQ2_C1[0], Fq::one());
|
||||
assert_eq!(
|
||||
FROBENIUS_COEFF_FQ2_C1[1],
|
||||
nqr.pow([
|
||||
nqr.pow_vartime([
|
||||
0xdcff7fffffffd555,
|
||||
0xf55ffff58a9ffff,
|
||||
0xb39869507b587b12,
|
||||
@ -479,7 +483,7 @@ fn test_frob_coeffs() {
|
||||
assert_eq!(FROBENIUS_COEFF_FQ6_C1[0], Fq2::one());
|
||||
assert_eq!(
|
||||
FROBENIUS_COEFF_FQ6_C1[1],
|
||||
nqr.pow([
|
||||
nqr.pow_vartime([
|
||||
0x9354ffffffffe38e,
|
||||
0xa395554e5c6aaaa,
|
||||
0xcd104635a790520c,
|
||||
@ -490,7 +494,7 @@ fn test_frob_coeffs() {
|
||||
);
|
||||
assert_eq!(
|
||||
FROBENIUS_COEFF_FQ6_C1[2],
|
||||
nqr.pow([
|
||||
nqr.pow_vartime([
|
||||
0xb78e0000097b2f68,
|
||||
0xd44f23b47cbd64e3,
|
||||
0x5cb9668120b069a9,
|
||||
@ -507,7 +511,7 @@ fn test_frob_coeffs() {
|
||||
);
|
||||
assert_eq!(
|
||||
FROBENIUS_COEFF_FQ6_C1[3],
|
||||
nqr.pow([
|
||||
nqr.pow_vartime([
|
||||
0xdbc6fcd6f35b9e06,
|
||||
0x997dead10becd6aa,
|
||||
0x9dbbd24c17206460,
|
||||
@ -530,7 +534,7 @@ fn test_frob_coeffs() {
|
||||
);
|
||||
assert_eq!(
|
||||
FROBENIUS_COEFF_FQ6_C1[4],
|
||||
nqr.pow([
|
||||
nqr.pow_vartime([
|
||||
0x4649add3c71c6d90,
|
||||
0x43caa6528972a865,
|
||||
0xcda8445bbaaa0fbb,
|
||||
@ -559,7 +563,7 @@ fn test_frob_coeffs() {
|
||||
);
|
||||
assert_eq!(
|
||||
FROBENIUS_COEFF_FQ6_C1[5],
|
||||
nqr.pow([
|
||||
nqr.pow_vartime([
|
||||
0xf896f792732eb2be,
|
||||
0x49c86a6d1dc593a1,
|
||||
0xe5b31e94581f91c3,
|
||||
@ -596,7 +600,7 @@ fn test_frob_coeffs() {
|
||||
assert_eq!(FROBENIUS_COEFF_FQ6_C2[0], Fq2::one());
|
||||
assert_eq!(
|
||||
FROBENIUS_COEFF_FQ6_C2[1],
|
||||
nqr.pow([
|
||||
nqr.pow_vartime([
|
||||
0x26a9ffffffffc71c,
|
||||
0x1472aaa9cb8d5555,
|
||||
0x9a208c6b4f20a418,
|
||||
@ -607,7 +611,7 @@ fn test_frob_coeffs() {
|
||||
);
|
||||
assert_eq!(
|
||||
FROBENIUS_COEFF_FQ6_C2[2],
|
||||
nqr.pow([
|
||||
nqr.pow_vartime([
|
||||
0x6f1c000012f65ed0,
|
||||
0xa89e4768f97ac9c7,
|
||||
0xb972cd024160d353,
|
||||
@ -624,7 +628,7 @@ fn test_frob_coeffs() {
|
||||
);
|
||||
assert_eq!(
|
||||
FROBENIUS_COEFF_FQ6_C2[3],
|
||||
nqr.pow([
|
||||
nqr.pow_vartime([
|
||||
0xb78df9ade6b73c0c,
|
||||
0x32fbd5a217d9ad55,
|
||||
0x3b77a4982e40c8c1,
|
||||
@ -647,7 +651,7 @@ fn test_frob_coeffs() {
|
||||
);
|
||||
assert_eq!(
|
||||
FROBENIUS_COEFF_FQ6_C2[4],
|
||||
nqr.pow([
|
||||
nqr.pow_vartime([
|
||||
0x8c935ba78e38db20,
|
||||
0x87954ca512e550ca,
|
||||
0x9b5088b775541f76,
|
||||
@ -676,7 +680,7 @@ fn test_frob_coeffs() {
|
||||
);
|
||||
assert_eq!(
|
||||
FROBENIUS_COEFF_FQ6_C2[5],
|
||||
nqr.pow([
|
||||
nqr.pow_vartime([
|
||||
0xf12def24e65d657c,
|
||||
0x9390d4da3b8b2743,
|
||||
0xcb663d28b03f2386,
|
||||
@ -713,7 +717,7 @@ fn test_frob_coeffs() {
|
||||
assert_eq!(FROBENIUS_COEFF_FQ12_C1[0], Fq2::one());
|
||||
assert_eq!(
|
||||
FROBENIUS_COEFF_FQ12_C1[1],
|
||||
nqr.pow([
|
||||
nqr.pow_vartime([
|
||||
0x49aa7ffffffff1c7,
|
||||
0x51caaaa72e35555,
|
||||
0xe688231ad3c82906,
|
||||
@ -724,7 +728,7 @@ fn test_frob_coeffs() {
|
||||
);
|
||||
assert_eq!(
|
||||
FROBENIUS_COEFF_FQ12_C1[2],
|
||||
nqr.pow([
|
||||
nqr.pow_vartime([
|
||||
0xdbc7000004bd97b4,
|
||||
0xea2791da3e5eb271,
|
||||
0x2e5cb340905834d4,
|
||||
@ -741,7 +745,7 @@ fn test_frob_coeffs() {
|
||||
);
|
||||
assert_eq!(
|
||||
FROBENIUS_COEFF_FQ12_C1[3],
|
||||
nqr.pow(vec![
|
||||
nqr.pow_vartime(vec![
|
||||
0x6de37e6b79adcf03,
|
||||
0x4cbef56885f66b55,
|
||||
0x4edde9260b903230,
|
||||
@ -764,7 +768,7 @@ fn test_frob_coeffs() {
|
||||
);
|
||||
assert_eq!(
|
||||
FROBENIUS_COEFF_FQ12_C1[4],
|
||||
nqr.pow(vec![
|
||||
nqr.pow_vartime(vec![
|
||||
0xa324d6e9e38e36c8,
|
||||
0xa1e5532944b95432,
|
||||
0x66d4222ddd5507dd,
|
||||
@ -793,7 +797,7 @@ fn test_frob_coeffs() {
|
||||
);
|
||||
assert_eq!(
|
||||
FROBENIUS_COEFF_FQ12_C1[5],
|
||||
nqr.pow(vec![
|
||||
nqr.pow_vartime(vec![
|
||||
0xfc4b7bc93997595f,
|
||||
0xa4e435368ee2c9d0,
|
||||
0xf2d98f4a2c0fc8e1,
|
||||
@ -828,7 +832,7 @@ fn test_frob_coeffs() {
|
||||
);
|
||||
assert_eq!(
|
||||
FROBENIUS_COEFF_FQ12_C1[6],
|
||||
nqr.pow(vec![
|
||||
nqr.pow_vartime(vec![
|
||||
0x21219610a012ba3c,
|
||||
0xa5c19ad35375325,
|
||||
0x4e9df1e497674396,
|
||||
@ -869,7 +873,7 @@ fn test_frob_coeffs() {
|
||||
);
|
||||
assert_eq!(
|
||||
FROBENIUS_COEFF_FQ12_C1[7],
|
||||
nqr.pow(vec![
|
||||
nqr.pow_vartime(vec![
|
||||
0x742754a1f22fdb,
|
||||
0x2a1955c2dec3a702,
|
||||
0x9747b28c796d134e,
|
||||
@ -916,7 +920,7 @@ fn test_frob_coeffs() {
|
||||
);
|
||||
assert_eq!(
|
||||
FROBENIUS_COEFF_FQ12_C1[8],
|
||||
nqr.pow(vec![
|
||||
nqr.pow_vartime(vec![
|
||||
0x802f5720d0b25710,
|
||||
0x6714f0a258b85c7c,
|
||||
0x31394c90afdf16e,
|
||||
@ -969,7 +973,7 @@ fn test_frob_coeffs() {
|
||||
);
|
||||
assert_eq!(
|
||||
FROBENIUS_COEFF_FQ12_C1[9],
|
||||
nqr.pow(vec![
|
||||
nqr.pow_vartime(vec![
|
||||
0x4af4accf7de0b977,
|
||||
0x742485e21805b4ee,
|
||||
0xee388fbc4ac36dec,
|
||||
@ -1028,7 +1032,7 @@ fn test_frob_coeffs() {
|
||||
);
|
||||
assert_eq!(
|
||||
FROBENIUS_COEFF_FQ12_C1[10],
|
||||
nqr.pow(vec![
|
||||
nqr.pow_vartime(vec![
|
||||
0xe5953a4f96cdda44,
|
||||
0x336b2d734cbc32bb,
|
||||
0x3f79bfe3cd7410e,
|
||||
@ -1093,7 +1097,7 @@ fn test_frob_coeffs() {
|
||||
);
|
||||
assert_eq!(
|
||||
FROBENIUS_COEFF_FQ12_C1[11],
|
||||
nqr.pow(vec![
|
||||
nqr.pow_vartime(vec![
|
||||
0x107db680942de533,
|
||||
0x6262b24d2052393b,
|
||||
0x6136df824159ebc,
|
||||
@ -1166,8 +1170,7 @@ fn test_frob_coeffs() {
|
||||
|
||||
#[test]
|
||||
fn test_neg_one() {
|
||||
let mut o = Fq::one();
|
||||
o.negate();
|
||||
let o = Fq::one().neg();
|
||||
|
||||
assert_eq!(NEGATIVE_ONE, o);
|
||||
}
|
||||
@ -1928,7 +1931,7 @@ fn test_fq_mul_assign() {
|
||||
|
||||
#[test]
|
||||
fn test_fq_squaring() {
|
||||
let mut a = Fq(FqRepr([
|
||||
let a = Fq(FqRepr([
|
||||
0xffffffffffffffff,
|
||||
0xffffffffffffffff,
|
||||
0xffffffffffffffff,
|
||||
@ -1937,9 +1940,8 @@ fn test_fq_squaring() {
|
||||
0x19ffffffffffffff,
|
||||
]));
|
||||
assert!(a.is_valid());
|
||||
a.square();
|
||||
assert_eq!(
|
||||
a,
|
||||
a.square(),
|
||||
Fq::from_repr(FqRepr([
|
||||
0x1cfb28fe7dfbbb86,
|
||||
0x24cbe1731577a59,
|
||||
@ -1959,20 +1961,13 @@ fn test_fq_squaring() {
|
||||
for _ in 0..1000000 {
|
||||
// Ensure that (a * a) = a^2
|
||||
let a = Fq::random(&mut rng);
|
||||
|
||||
let mut tmp = a;
|
||||
tmp.square();
|
||||
|
||||
let mut tmp2 = a;
|
||||
tmp2.mul_assign(&a);
|
||||
|
||||
assert_eq!(tmp, tmp2);
|
||||
assert_eq!(a.square(), a * a);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fq_inverse() {
|
||||
assert!(Fq::zero().inverse().is_none());
|
||||
fn test_fq_invert() {
|
||||
assert!(bool::from(Fq::zero().invert().is_none()));
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
@ -1984,7 +1979,7 @@ fn test_fq_inverse() {
|
||||
for _ in 0..1000 {
|
||||
// Ensure that a * a^-1 = 1
|
||||
let mut a = Fq::random(&mut rng);
|
||||
let ainv = a.inverse().unwrap();
|
||||
let ainv = a.invert().unwrap();
|
||||
a.mul_assign(&ainv);
|
||||
assert_eq!(a, one);
|
||||
}
|
||||
@ -1999,19 +1994,15 @@ fn test_fq_double() {
|
||||
|
||||
for _ in 0..1000 {
|
||||
// Ensure doubling a is equivalent to adding a to itself.
|
||||
let mut a = Fq::random(&mut rng);
|
||||
let mut b = a;
|
||||
b.add_assign(&a);
|
||||
a.double();
|
||||
assert_eq!(a, b);
|
||||
let a = Fq::random(&mut rng);
|
||||
assert_eq!(a.double(), a + a);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fq_negate() {
|
||||
fn test_fq_neg() {
|
||||
{
|
||||
let mut a = Fq::zero();
|
||||
a.negate();
|
||||
let a = Fq::zero().neg();
|
||||
|
||||
assert!(a.is_zero());
|
||||
}
|
||||
@ -2024,8 +2015,7 @@ fn test_fq_negate() {
|
||||
for _ in 0..1000 {
|
||||
// Ensure (a - (-a)) = 0.
|
||||
let mut a = Fq::random(&mut rng);
|
||||
let mut b = a;
|
||||
b.negate();
|
||||
let b = a.neg();
|
||||
a.add_assign(&b);
|
||||
|
||||
assert!(a.is_zero());
|
||||
@ -2043,7 +2033,7 @@ fn test_fq_pow() {
|
||||
// Exponentiate by various small numbers and ensure it consists with repeated
|
||||
// multiplication.
|
||||
let a = Fq::random(&mut rng);
|
||||
let target = a.pow(&[i]);
|
||||
let target = a.pow_vartime(&[i]);
|
||||
let mut c = Fq::one();
|
||||
for _ in 0..i {
|
||||
c.mul_assign(&a);
|
||||
@ -2055,7 +2045,7 @@ fn test_fq_pow() {
|
||||
// Exponentiating by the modulus should have no effect in a prime field.
|
||||
let a = Fq::random(&mut rng);
|
||||
|
||||
assert_eq!(a, a.pow(Fq::char()));
|
||||
assert_eq!(a, a.pow_vartime(Fq::char()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -2073,10 +2063,8 @@ fn test_fq_sqrt() {
|
||||
for _ in 0..1000 {
|
||||
// Ensure sqrt(a^2) = a or -a
|
||||
let a = Fq::random(&mut rng);
|
||||
let mut nega = a;
|
||||
nega.negate();
|
||||
let mut b = a;
|
||||
b.square();
|
||||
let nega = a.neg();
|
||||
let b = a.square();
|
||||
|
||||
let b = b.sqrt().unwrap();
|
||||
|
||||
@ -2087,10 +2075,9 @@ fn test_fq_sqrt() {
|
||||
// Ensure sqrt(a)^2 = a for random a
|
||||
let a = Fq::random(&mut rng);
|
||||
|
||||
if let Some(mut tmp) = a.sqrt() {
|
||||
tmp.square();
|
||||
|
||||
assert_eq!(a, tmp);
|
||||
let tmp = a.sqrt();
|
||||
if tmp.is_some().into() {
|
||||
assert_eq!(a, tmp.unwrap().square());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2209,7 +2196,7 @@ fn test_fq_root_of_unity() {
|
||||
Fq::from_repr(FqRepr::from(2)).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
Fq::multiplicative_generator().pow([
|
||||
Fq::multiplicative_generator().pow_vartime([
|
||||
0xdcff7fffffffd555,
|
||||
0xf55ffff58a9ffff,
|
||||
0xb39869507b587b12,
|
||||
@ -2219,8 +2206,8 @@ fn test_fq_root_of_unity() {
|
||||
]),
|
||||
Fq::root_of_unity()
|
||||
);
|
||||
assert_eq!(Fq::root_of_unity().pow([1 << Fq::S]), Fq::one());
|
||||
assert!(Fq::multiplicative_generator().sqrt().is_none());
|
||||
assert_eq!(Fq::root_of_unity().pow_vartime([1 << Fq::S]), Fq::one());
|
||||
assert!(bool::from(Fq::multiplicative_generator().sqrt().is_none()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -2246,40 +2233,3 @@ fn test_fq_ordering() {
|
||||
fn fq_repr_tests() {
|
||||
crate::tests::repr::random_repr_tests::<Fq>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fq_legendre() {
|
||||
use ff::LegendreSymbol::*;
|
||||
use ff::SqrtField;
|
||||
|
||||
assert_eq!(QuadraticResidue, Fq::one().legendre());
|
||||
assert_eq!(Zero, Fq::zero().legendre());
|
||||
|
||||
assert_eq!(
|
||||
QuadraticNonResidue,
|
||||
Fq::from_repr(FqRepr::from(2)).unwrap().legendre()
|
||||
);
|
||||
assert_eq!(
|
||||
QuadraticResidue,
|
||||
Fq::from_repr(FqRepr::from(4)).unwrap().legendre()
|
||||
);
|
||||
|
||||
let e = FqRepr([
|
||||
0x52a112f249778642,
|
||||
0xd0bedb989b7991f,
|
||||
0xdad3b6681aa63c05,
|
||||
0xf2efc0bb4721b283,
|
||||
0x6057a98f18c24733,
|
||||
0x1022c2fd122889e4,
|
||||
]);
|
||||
assert_eq!(QuadraticNonResidue, Fq::from_repr(e).unwrap().legendre());
|
||||
let e = FqRepr([
|
||||
0x6dae594e53a96c74,
|
||||
0x19b16ca9ba64b37b,
|
||||
0x5c764661a59bfc68,
|
||||
0xaa346e9b31c60a,
|
||||
0x346059f9d87a9fa9,
|
||||
0x1d61ac6bfd5c88b,
|
||||
]);
|
||||
assert_eq!(QuadraticResidue, Fq::from_repr(e).unwrap().legendre());
|
||||
}
|
||||
|
@ -3,9 +3,11 @@ use super::fq2::Fq2;
|
||||
use super::fq6::Fq6;
|
||||
use ff::Field;
|
||||
use rand_core::RngCore;
|
||||
use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
||||
use subtle::{Choice, ConditionallySelectable, CtOption};
|
||||
|
||||
/// An element of Fq12, represented by c0 + c1 * w.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct Fq12 {
|
||||
pub c0: Fq6,
|
||||
pub c1: Fq6,
|
||||
@ -19,7 +21,7 @@ impl ::std::fmt::Display for Fq12 {
|
||||
|
||||
impl Fq12 {
|
||||
pub fn conjugate(&mut self) {
|
||||
self.c1.negate();
|
||||
self.c1 = self.c1.neg();
|
||||
}
|
||||
|
||||
pub fn mul_by_014(&mut self, c0: &Fq2, c1: &Fq2, c4: &Fq2) {
|
||||
@ -39,6 +41,132 @@ impl Fq12 {
|
||||
}
|
||||
}
|
||||
|
||||
impl ConditionallySelectable for Fq12 {
|
||||
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
|
||||
Fq12 {
|
||||
c0: Fq6::conditional_select(&a.c0, &b.c0, choice),
|
||||
c1: Fq6::conditional_select(&a.c1, &b.c1, choice),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for Fq12 {
|
||||
type Output = Self;
|
||||
|
||||
fn neg(self) -> Self {
|
||||
Fq12 {
|
||||
c0: self.c0.neg(),
|
||||
c1: self.c1.neg(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> Add<&'r Fq12> for Fq12 {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, other: &Self) -> Self {
|
||||
Fq12 {
|
||||
c0: self.c0 + other.c0,
|
||||
c1: self.c1 + other.c1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Fq12 {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, other: Self) -> Self {
|
||||
self.add(&other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> AddAssign<&'r Fq12> for Fq12 {
|
||||
fn add_assign(&mut self, other: &'r Self) {
|
||||
self.c0.add_assign(&other.c0);
|
||||
self.c1.add_assign(&other.c1);
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign for Fq12 {
|
||||
fn add_assign(&mut self, other: Self) {
|
||||
self.add_assign(&other);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> Sub<&'r Fq12> for Fq12 {
|
||||
type Output = Self;
|
||||
|
||||
fn sub(self, other: &Self) -> Self {
|
||||
Fq12 {
|
||||
c0: self.c0 - other.c0,
|
||||
c1: self.c1 - other.c1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for Fq12 {
|
||||
type Output = Self;
|
||||
|
||||
fn sub(self, other: Self) -> Self {
|
||||
self.sub(&other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> SubAssign<&'r Fq12> for Fq12 {
|
||||
fn sub_assign(&mut self, other: &'r Self) {
|
||||
self.c0.sub_assign(&other.c0);
|
||||
self.c1.sub_assign(&other.c1);
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign for Fq12 {
|
||||
fn sub_assign(&mut self, other: Self) {
|
||||
self.sub_assign(&other);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> Mul<&'r Fq12> for Fq12 {
|
||||
type Output = Self;
|
||||
|
||||
fn mul(self, other: &Self) -> Self {
|
||||
let mut ret = self;
|
||||
ret.mul_assign(other);
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul for Fq12 {
|
||||
type Output = Self;
|
||||
|
||||
fn mul(self, other: Self) -> Self {
|
||||
self.mul(&other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> MulAssign<&'r Fq12> for Fq12 {
|
||||
fn mul_assign(&mut self, other: &Self) {
|
||||
let mut aa = self.c0;
|
||||
aa.mul_assign(&other.c0);
|
||||
let mut bb = self.c1;
|
||||
bb.mul_assign(&other.c1);
|
||||
let mut o = other.c0;
|
||||
o.add_assign(&other.c1);
|
||||
self.c1.add_assign(&self.c0);
|
||||
self.c1.mul_assign(&o);
|
||||
self.c1.sub_assign(&aa);
|
||||
self.c1.sub_assign(&bb);
|
||||
self.c0 = bb;
|
||||
self.c0.mul_by_nonresidue();
|
||||
self.c0.add_assign(&aa);
|
||||
}
|
||||
}
|
||||
|
||||
impl MulAssign for Fq12 {
|
||||
fn mul_assign(&mut self, other: Self) {
|
||||
self.mul_assign(&other);
|
||||
}
|
||||
}
|
||||
|
||||
impl Field for Fq12 {
|
||||
fn random<R: RngCore + ?std::marker::Sized>(rng: &mut R) -> Self {
|
||||
Fq12 {
|
||||
@ -65,24 +193,11 @@ impl Field for Fq12 {
|
||||
self.c0.is_zero() && self.c1.is_zero()
|
||||
}
|
||||
|
||||
fn double(&mut self) {
|
||||
self.c0.double();
|
||||
self.c1.double();
|
||||
}
|
||||
|
||||
fn negate(&mut self) {
|
||||
self.c0.negate();
|
||||
self.c1.negate();
|
||||
}
|
||||
|
||||
fn add_assign(&mut self, other: &Self) {
|
||||
self.c0.add_assign(&other.c0);
|
||||
self.c1.add_assign(&other.c1);
|
||||
}
|
||||
|
||||
fn sub_assign(&mut self, other: &Self) {
|
||||
self.c0.sub_assign(&other.c0);
|
||||
self.c1.sub_assign(&other.c1);
|
||||
fn double(&self) -> Self {
|
||||
Fq12 {
|
||||
c0: self.c0.double(),
|
||||
c1: self.c1.double(),
|
||||
}
|
||||
}
|
||||
|
||||
fn frobenius_map(&mut self, power: usize) {
|
||||
@ -94,7 +209,7 @@ impl Field for Fq12 {
|
||||
self.c1.c2.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]);
|
||||
}
|
||||
|
||||
fn square(&mut self) {
|
||||
fn square(&self) -> Self {
|
||||
let mut ab = self.c0;
|
||||
ab.mul_assign(&self.c1);
|
||||
let mut c0c1 = self.c0;
|
||||
@ -104,44 +219,22 @@ impl Field for Fq12 {
|
||||
c0.add_assign(&self.c0);
|
||||
c0.mul_assign(&c0c1);
|
||||
c0.sub_assign(&ab);
|
||||
self.c1 = ab;
|
||||
self.c1.add_assign(&ab);
|
||||
let mut c1 = ab;
|
||||
c1.add_assign(&ab);
|
||||
ab.mul_by_nonresidue();
|
||||
c0.sub_assign(&ab);
|
||||
self.c0 = c0;
|
||||
Fq12 { c0, c1 }
|
||||
}
|
||||
|
||||
fn mul_assign(&mut self, other: &Self) {
|
||||
let mut aa = self.c0;
|
||||
aa.mul_assign(&other.c0);
|
||||
let mut bb = self.c1;
|
||||
bb.mul_assign(&other.c1);
|
||||
let mut o = other.c0;
|
||||
o.add_assign(&other.c1);
|
||||
self.c1.add_assign(&self.c0);
|
||||
self.c1.mul_assign(&o);
|
||||
self.c1.sub_assign(&aa);
|
||||
self.c1.sub_assign(&bb);
|
||||
self.c0 = bb;
|
||||
self.c0.mul_by_nonresidue();
|
||||
self.c0.add_assign(&aa);
|
||||
}
|
||||
|
||||
fn inverse(&self) -> Option<Self> {
|
||||
let mut c0s = self.c0;
|
||||
c0s.square();
|
||||
let mut c1s = self.c1;
|
||||
c1s.square();
|
||||
fn invert(&self) -> CtOption<Self> {
|
||||
let mut c0s = self.c0.square();
|
||||
let mut c1s = self.c1.square();
|
||||
c1s.mul_by_nonresidue();
|
||||
c0s.sub_assign(&c1s);
|
||||
|
||||
c0s.inverse().map(|t| {
|
||||
let mut tmp = Fq12 { c0: t, c1: t };
|
||||
tmp.c0.mul_assign(&self.c0);
|
||||
tmp.c1.mul_assign(&self.c1);
|
||||
tmp.c1.negate();
|
||||
|
||||
tmp
|
||||
c0s.invert().map(|t| Fq12 {
|
||||
c0: t.mul(&self.c0),
|
||||
c1: t.mul(&self.c1).neg(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user