diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 07f01aec..52c4274c 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -75,6 +75,12 @@ jobs: cd creds ../clippy_command.sh + - name: Run clippy for /ecdsa-pop + run: | + cd ecdsa-pop + ../clippy_command.sh + + - name: Run clippy for mdl-tools run: | cd circuit_setup/mdl-tools diff --git a/NOTICE.md b/NOTICE.md index 5079cf10..2a26a1aa 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -1868,3 +1868,21 @@ https://github.com/lurk-lab/neptune/blob/main/LICENSE-MIT ### Additional Attributions None +--- + +### Component +halo2curves +https://github.com/privacy-scaling-explorations/halo2curves +Path: forks/halo2curves +Note: forked to support the T256 elliptic curve + +### Open Source License/Copyright Notice +MIT +https://github.com/privacy-scaling-explorations/halo2curves/blob/b6fa078aa36d04d7eb654a1e2f823e3fdf971041/LICENSE-MIT + +Apache +https://github.com/privacy-scaling-explorations/halo2curves/blob/b6fa078aa36d04d7eb654a1e2f823e3fdf971041/LICENSE-APACHE + +### Additional Attributions +None + diff --git a/circuit_setup/mdl-tools/src/bin/prepare-prover-input.rs b/circuit_setup/mdl-tools/src/bin/prepare-prover-input.rs index 05716c95..865ae90a 100644 --- a/circuit_setup/mdl-tools/src/bin/prepare-prover-input.rs +++ b/circuit_setup/mdl-tools/src/bin/prepare-prover-input.rs @@ -438,7 +438,7 @@ fn main() { issuer_auth .verify::(&issuer_pub_key, None, None); if !verification_result.is_success() { - panic!("Error: issuer signature verification failed"); + panic!("Error: issuer signature verification failed: {}", verification_result.into_result().err().unwrap()); } // process the signature diff --git a/clippy_command.sh b/clippy_command.sh index 8110d4bb..897679a9 100755 --- a/clippy_command.sh +++ b/clippy_command.sh @@ -3,6 +3,7 @@ # Invoke clippy with this command to allow some lints RUSTFLAGS="--deny warnings" cargo clippy --release --tests -- \ + --no-deps \ -A clippy::needless_range_loop \ -A clippy::same_item_push \ -A clippy::should_implement_trait \ diff --git a/creds/Cargo.toml b/creds/Cargo.toml index d8db95a9..72f40241 100644 --- a/creds/Cargo.toml +++ b/creds/Cargo.toml @@ -62,6 +62,11 @@ ark-poly-commit = { path = "../forks/ark-poly-commit", default-features = false, ecdsa-pop = {path="../ecdsa-pop"} p256 = { version = "0.13.2", features = ["ecdsa", "pem"]} +# Enable wasm build for getrandom +[target.'cfg(target_arch = "wasm32")'.dependencies] +getrandom_03 = { package = "getrandom", version = "0.3", features = ["wasm_js"] } +getrandom_02 = { package = "getrandom", version = "0.2", features = ["js"] } + [dev-dependencies] criterion = { version = "0.5", features = ["html_reports"] } serial_test = "*" diff --git a/creds/src/wasm_lib.rs b/creds/src/wasm_lib.rs index 828eca94..fb575d22 100644 --- a/creds/src/wasm_lib.rs +++ b/creds/src/wasm_lib.rs @@ -90,8 +90,8 @@ pub fn create_show_proof_wasm( age.expect("Age not valid."), ) } else { - proof_spec.presentation_message = Some(challenge); - create_show_proof(&mut client_state, &range_pk, &io_locations, &proof_spec).unwrap() + proof_spec.presentation_message = Some(challenge.into()); + create_show_proof(&mut client_state, &range_pk, &io_locations, &proof_spec, None).unwrap() }; let show_proof_b64 = write_to_b64url(&show_proof); diff --git a/ecdsa-pop/Cargo.lock b/ecdsa-pop/Cargo.lock deleted file mode 100644 index 6fc522f2..00000000 --- a/ecdsa-pop/Cargo.lock +++ /dev/null @@ -1,1592 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "addchain" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2e69442aa5628ea6951fa33e24efe8313f4321a91bd729fc2f75bdfc858570" -dependencies = [ - "num-bigint 0.3.3", - "num-integer", - "num-traits", -] - -[[package]] -name = "adler2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" - -[[package]] -name = "ahash" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", -] - -[[package]] -name = "ark-bn254" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a22f4561524cd949590d78d7d4c5df8f592430d221f7f3c9497bbafd8972120f" -dependencies = [ - "ark-ec", - "ark-ff", - "ark-std", -] - -[[package]] -name = "ark-ec" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" -dependencies = [ - "ark-ff", - "ark-poly", - "ark-serialize", - "ark-std", - "derivative", - "hashbrown", - "itertools", - "num-traits", - "zeroize", -] - -[[package]] -name = "ark-ff" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" -dependencies = [ - "ark-ff-asm", - "ark-ff-macros", - "ark-serialize", - "ark-std", - "derivative", - "digest 0.10.7", - "itertools", - "num-bigint 0.4.6", - "num-traits", - "paste", - "rustc_version", - "zeroize", -] - -[[package]] -name = "ark-ff-asm" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" -dependencies = [ - "quote", - "syn 1.0.109", -] - -[[package]] -name = "ark-ff-macros" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" -dependencies = [ - "num-bigint 0.4.6", - "num-traits", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "ark-poly" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" -dependencies = [ - "ark-ff", - "ark-serialize", - "ark-std", - "derivative", - "hashbrown", -] - -[[package]] -name = "ark-secp256r1" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3975a01b0a6e3eae0f72ec7ca8598a6620fc72fa5981f6f5cca33b7cd788f633" -dependencies = [ - "ark-ec", - "ark-ff", - "ark-std", -] - -[[package]] -name = "ark-secp256r1" -version = "0.4.0" -source = "git+https://github.com/arkworks-rs/curves#e2d16a27e2cfa9f972ae9772df827a22730011b4" -dependencies = [ - "ark-ec", - "ark-ff", - "ark-std", -] - -[[package]] -name = "ark-serialize" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" -dependencies = [ - "ark-serialize-derive", - "ark-std", - "digest 0.10.7", - "num-bigint 0.4.6", -] - -[[package]] -name = "ark-serialize-derive" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "ark-std" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" -dependencies = [ - "colored", - "num-traits", - "rand 0.8.5", -] - -[[package]] -name = "arrayref" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" - -[[package]] -name = "arrayvec" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" - -[[package]] -name = "autocfg" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" - -[[package]] -name = "az" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" - -[[package]] -name = "bellpepper" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0271a107b5f600ee41bdafbb3c8ddf4afa52983d4b078917d89dbb920116e987" -dependencies = [ - "bellpepper-core", - "byteorder", - "ff", -] - -[[package]] -name = "bellpepper-core" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2c9a1b2f748c59938bc72165ebdf34efffeecee9cfbe0bb7d6b01aea21cd523" -dependencies = [ - "blake2s_simd", - "byteorder", - "ff", - "serde", - "thiserror", -] - -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - -[[package]] -name = "bitflags" -version = "2.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" - -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - -[[package]] -name = "blake2" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" -dependencies = [ - "digest 0.10.7", -] - -[[package]] -name = "blake2b_simd" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06e903a20b159e944f91ec8499fe1e55651480c541ea0a584f5d967c49ad9d99" -dependencies = [ - "arrayref", - "arrayvec", - "constant_time_eq", -] - -[[package]] -name = "blake2s_simd" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e90f7deecfac93095eb874a40febd69427776e24e1bd7f87f33ac62d6f0174df" -dependencies = [ - "arrayref", - "arrayvec", - "constant_time_eq", -] - -[[package]] -name = "block-buffer" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" -dependencies = [ - "block-padding", - "byte-tools", - "byteorder", - "generic-array 0.12.4", -] - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array 0.14.7", -] - -[[package]] -name = "block-padding" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" -dependencies = [ - "byte-tools", -] - -[[package]] -name = "blst" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47c79a94619fade3c0b887670333513a67ac28a6a7e653eb260bf0d4103db38d" -dependencies = [ - "cc", - "glob", - "threadpool", - "zeroize", -] - -[[package]] -name = "blstrs" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a8a8ed6fefbeef4a8c7b460e4110e12c5e22a5b7cf32621aae6ad650c4dcf29" -dependencies = [ - "blst", - "byte-slice-cast", - "ff", - "group", - "pairing", - "rand_core 0.6.4", - "serde", - "subtle", -] - -[[package]] -name = "byte-slice-cast" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" - -[[package]] -name = "byte-tools" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "cc" -version = "1.2.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4ac86a9e5bc1e2b3449ab9d7d3a6a405e3d1bb28d7b9be8614f55846ae3766" -dependencies = [ - "shlex", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "circ_fields" -version = "0.1.0" -dependencies = [ - "ark-ec", - "ark-ff", - "ark-ff-macros", - "ark-secp256r1 0.4.0 (git+https://github.com/arkworks-rs/curves)", - "ark-serialize", - "ark-std", - "datasize", - "ff", - "ff-derive-num", - "ff_derive", - "lazy_static", - "num-traits", - "paste", - "rand 0.8.5", - "rug", - "serde", - "sha2", -] - -[[package]] -name = "colored" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" -dependencies = [ - "lazy_static", - "windows-sys", -] - -[[package]] -name = "constant_time_eq" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" - -[[package]] -name = "cpufeatures" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" -dependencies = [ - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" - -[[package]] -name = "crrl" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca032663238726938d4ca23189575167143599eb219b591270c0d268ca9259b0" -dependencies = [ - "rand_core 0.6.4", - "sha2", - "sha3 0.10.8", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array 0.14.7", - "typenum", -] - -[[package]] -name = "curve25519-dalek" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" -dependencies = [ - "byteorder", - "digest 0.9.0", - "rand_core 0.5.1", - "serde", - "subtle", - "zeroize", -] - -[[package]] -name = "datasize" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e65c07d59e45d77a8bda53458c24a828893a99ac6cdd9c84111e09176ab739a2" -dependencies = [ - "datasize_derive", - "serde", -] - -[[package]] -name = "datasize_derive" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613e4ee15899913285b7612004bbd490abd605be7b11d35afada5902fb6b91d5" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "digest" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" -dependencies = [ - "generic-array 0.12.4", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array 0.14.7", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer 0.10.4", - "crypto-common", - "subtle", -] - -[[package]] -name = "ecdsa-pop" -version = "0.1.0" -dependencies = [ - "ark-bn254", - "ark-ec", - "ark-ff", - "ark-secp256r1 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ark-std", - "bellpepper", - "bellpepper-core", - "bincode", - "ff", - "flate2", - "generic-array 1.2.0", - "halo2curves", - "hex", - "merlin", - "neptune", - "num-bigint 0.4.6", - "num-format", - "num-traits", - "pasta_curves", - "rand 0.9.1", - "serde", - "sha2", - "spartan-t256", - "thiserror", -] - -[[package]] -name = "either" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" - -[[package]] -name = "ff" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" -dependencies = [ - "bitvec", - "byteorder", - "ff_derive", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "ff-derive-num" -version = "0.3.0" -dependencies = [ - "num-traits", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "ff_derive" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f10d12652036b0e99197587c6ba87a8fc3031986499973c030d8b44fcc151b60" -dependencies = [ - "addchain", - "num-bigint 0.3.3", - "num-integer", - "num-traits", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "flate2" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - -[[package]] -name = "generic-array" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" -dependencies = [ - "typenum", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "generic-array" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c8444bc9d71b935156cc0ccab7f622180808af7867b1daae6547d773591703" -dependencies = [ - "typenum", -] - -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" -dependencies = [ - "cfg-if", - "libc", - "r-efi", - "wasi 0.14.2+wasi-0.2.4", -] - -[[package]] -name = "glob" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" - -[[package]] -name = "gmp-mpfr-sys" -version = "1.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c66d61197a68f6323b9afa616cf83d55d69191e1bf364d4eb7d35ae18defe776" -dependencies = [ - "libc", - "windows-sys", -] - -[[package]] -name = "group" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" -dependencies = [ - "ff", - "rand 0.8.5", - "rand_core 0.6.4", - "rand_xorshift", - "subtle", -] - -[[package]] -name = "halo2curves" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b756596082144af6e57105a20403b7b80fe9dccd085700b74fae3af523b74dba" -dependencies = [ - "blake2", - "digest 0.10.7", - "ff", - "group", - "halo2derive", - "hex", - "lazy_static", - "num-bigint 0.4.6", - "num-integer", - "num-traits", - "pairing", - "paste", - "rand 0.8.5", - "rand_core 0.6.4", - "rayon", - "serde", - "serde_arrays", - "sha2", - "static_assertions", - "subtle", - "unroll", -] - -[[package]] -name = "halo2derive" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdb99e7492b4f5ff469d238db464131b86c2eaac814a78715acba369f64d2c76" -dependencies = [ - "num-bigint 0.4.6", - "num-integer", - "num-traits", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "hashbrown" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" -dependencies = [ - "ahash", -] - -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -dependencies = [ - "serde", -] - -[[package]] -name = "hex-literal" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" - -[[package]] -name = "keccak" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" -dependencies = [ - "cpufeatures", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -dependencies = [ - "spin", -] - -[[package]] -name = "libc" -version = "0.2.172" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" - -[[package]] -name = "libm" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" - -[[package]] -name = "merlin" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" -dependencies = [ - "byteorder", - "keccak", - "rand_core 0.6.4", - "zeroize", -] - -[[package]] -name = "miniz_oxide" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" -dependencies = [ - "adler2", -] - -[[package]] -name = "neptune" -version = "13.0.0" -dependencies = [ - "bellpepper", - "bellpepper-core", - "blake2s_simd", - "blstrs", - "byteorder", - "ff", - "generic-array 1.2.0", - "pasta_curves", - "serde", - "trait-set", -] - -[[package]] -name = "num-bigint" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6f7833f2cbf2360a6cfd58cd41a53aa7a90bd4c202f5b1c7dd2ed73c57b2c3" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-bigint" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" -dependencies = [ - "num-integer", - "num-traits", - "rand 0.8.5", -] - -[[package]] -name = "num-bigint-dig" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9bc3e36fd683e004fd59c64a425e0e991616f5a8b617c3b9a933a93c168facc" -dependencies = [ - "byteorder", - "lazy_static", - "libm", - "num-integer", - "num-iter", - "num-traits", - "rand 0.8.5", - "serde", - "smallvec", -] - -[[package]] -name = "num-format" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" -dependencies = [ - "arrayvec", - "itoa", -] - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-iter" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "once_cell" -version = "1.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" - -[[package]] -name = "opaque-debug" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" - -[[package]] -name = "pairing" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fec4625e73cf41ef4bb6846cafa6d44736525f442ba45e407c4a000a13996f" -dependencies = [ - "group", -] - -[[package]] -name = "pasta_curves" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e57598f73cc7e1b2ac63c79c517b31a0877cd7c402cdcaa311b5208de7a095" -dependencies = [ - "blake2b_simd", - "ff", - "group", - "hex", - "lazy_static", - "rand 0.8.5", - "serde", - "static_assertions", - "subtle", -] - -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "proc-macro2" -version = "1.0.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "5.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" - -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" -dependencies = [ - "rand_chacha 0.9.0", - "rand_core 0.9.3", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core 0.9.3", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.16", -] - -[[package]] -name = "rand_core" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" -dependencies = [ - "getrandom 0.3.3", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "rand_xorshift" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" -dependencies = [ - "rand_core 0.6.4", -] - -[[package]] -name = "rayon" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - -[[package]] -name = "rug" -version = "1.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4207e8d668e5b8eb574bda8322088ccd0d7782d3d03c7e8d562e82ed82bdcbc3" -dependencies = [ - "az", - "gmp-mpfr-sys", - "libc", - "libm", - "serde", -] - -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - -[[package]] -name = "semver" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" - -[[package]] -name = "serde" -version = "1.0.219" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_arrays" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38636132857f68ec3d5f3eb121166d2af33cb55174c4d5ff645db6165cbef0fd" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_bytes" -version = "0.11.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8437fd221bde2d4ca316d61b90e337e9e702b3820b87d63caa9ba6c02bd06d96" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_derive" -version = "1.0.219" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "sha2" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.10.7", -] - -[[package]] -name = "sha3" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd26bc0e7a2e3a7c959bc494caf58b72ee0c71d67704e9520f736ca7e4853ecf" -dependencies = [ - "block-buffer 0.7.3", - "byte-tools", - "digest 0.8.1", - "keccak", - "opaque-debug", -] - -[[package]] -name = "sha3" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" -dependencies = [ - "digest 0.10.7", - "keccak", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "smallvec" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" - -[[package]] -name = "spartan-t256" -version = "0.1.0" -dependencies = [ - "ark-ec", - "ark-ff", - "ark-serialize", - "bellpepper", - "bellpepper-core", - "bincode", - "byteorder", - "circ_fields", - "crrl", - "curve25519-dalek", - "digest 0.8.1", - "ff", - "flate2", - "hex-literal", - "itertools", - "lazy_static", - "merlin", - "num-bigint-dig", - "rand 0.7.3", - "rayon", - "rug", - "serde", - "serde_bytes", - "sha3 0.8.2", - "subtle", - "zeroize", -] - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.101" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "threadpool" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" -dependencies = [ - "num_cpus", -] - -[[package]] -name = "trait-set" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b79e2e9c9ab44c6d7c20d5976961b47e8f49ac199154daa514b77cd1ab536625" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "typenum" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" - -[[package]] -name = "unicode-ident" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" - -[[package]] -name = "unroll" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ad948c1cb799b1a70f836077721a92a35ac177d4daddf4c20a633786d4cf618" -dependencies = [ - "quote", - "syn 1.0.109", -] - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasi" -version = "0.14.2+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" -dependencies = [ - "wit-bindgen-rt", -] - -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "wit-bindgen-rt" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" -dependencies = [ - "bitflags", -] - -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - -[[package]] -name = "zerocopy" -version = "0.8.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "zeroize" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] diff --git a/ecdsa-pop/Cargo.toml b/ecdsa-pop/Cargo.toml index 8d756fac..f5417370 100644 --- a/ecdsa-pop/Cargo.toml +++ b/ecdsa-pop/Cargo.toml @@ -14,7 +14,7 @@ ff = { version = "0.13.0", features = ["derive"] } #neptune = { version = "13.0.0", default-features = false } # Poseidon. v13 depends on bellpepper 4.0, but spartan must use 2.0 neptune = {path = './neptune', default-features = false} # Our fork of neptune that works with bellpepper 2.0 generic-array = "1.0.0" -halo2curves = { version = "0.8.0", features =["derive_serde", "asm"]} +halo2curves = { version = "0.8.0", features =["derive_serde"]} merlin = { version = "3.0.0", default-features = false } hex = "0.4.3" sha2 = "0.10.7" @@ -28,6 +28,9 @@ rand = ">=0.8.5" flate2 = { version = "1.0.14" } num-format = { version = "0.4.3" } +# Enable 'asm' feature when NOT a wasm build +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +halo2curves = { version = "0.8.0", features = ["asm"] } [features] print-trace = ["ark-std/print-trace"] diff --git a/ecdsa-pop/src/ecc.rs b/ecdsa-pop/src/ecc.rs index 110cd7be..c702560c 100644 --- a/ecdsa-pop/src/ecc.rs +++ b/ecdsa-pop/src/ecc.rs @@ -44,7 +44,7 @@ where Ok(coords.map_or(Scalar::ZERO, |c| c.1)) })?; let is_infinity = AllocatedNum::alloc(cs.namespace(|| "is_infinity"), || { - Ok(if coords.map_or(true, |c| c.2) { + Ok(if coords.is_none_or(|c| c.2) { Scalar::ONE } else { Scalar::ZERO diff --git a/ecdsa-pop/src/emulated/field_element.rs b/ecdsa-pop/src/emulated/field_element.rs index e65c421f..be76db8c 100644 --- a/ecdsa-pop/src/emulated/field_element.rs +++ b/ecdsa-pop/src/emulated/field_element.rs @@ -483,7 +483,7 @@ where )); } - let new_num_limbs = (allocated_limbs.len() + group_size - 1) / group_size; + let new_num_limbs = allocated_limbs.len().div_ceil(group_size); let mut res = vec![Num::::zero(); new_num_limbs]; for i in 0..new_num_limbs { @@ -771,7 +771,6 @@ where #[cfg(test)] mod tests { use super::*; - use std::u128; use bellpepper::gadgets::multipack::pack_bits; use bellpepper_core::test_cs::TestConstraintSystem; use num_bigint::RandBigInt; @@ -1747,14 +1746,14 @@ mod tests { assert_eq!(cs.num_constraints(), 1226); } - fn allocated_bits_to_emulated_fe(mut cs: CS, a : &Vec) + fn allocated_bits_to_emulated_fe(mut cs: CS, a : &[AllocatedBit]) -> Result, SynthesisError> where CS: ConstraintSystem, F1: PrimeFieldBits, EFP: EmulatedFieldParams { - assert!(a.len() > 0); + assert!(!a.is_empty()); // Make vector of limbs (F1 values used to emulate F2) let mut limbs : Vec> = Vec::with_capacity(16); @@ -1764,7 +1763,7 @@ mod tests { let bpl = EFP::bits_per_limb(); let limb_size = if i == num_limbs - 1 && a.len()%bpl!=0 {a.len()%bpl} else {bpl}; let limb_bits = &a[i*bpl .. i*bpl + limb_size]; - let limb_bits : Vec = limb_bits.into_iter().map(|x| Boolean::from(x.clone())).collect(); + let limb_bits : Vec = limb_bits.iter().map(|x| Boolean::from(x.clone())).collect(); let limb_i = pack_bits(&mut cs.namespace(|| format!("pack limb {}", i)), &limb_bits)?.into(); limbs.push(limb_i); } diff --git a/ecdsa-pop/src/emulated/field_hints.rs b/ecdsa-pop/src/emulated/field_hints.rs index d8b68b9f..20129104 100644 --- a/ecdsa-pop/src/emulated/field_hints.rs +++ b/ecdsa-pop/src/emulated/field_hints.rs @@ -41,6 +41,7 @@ where CS: ConstraintSystem, { // TODO: Check the need for the "+ 1" + #[allow(clippy::manual_div_ceil)] let num_res_limbs = (self.len()*P::bits_per_limb() + self.overflow + 1 - (P::modulus().bits() as usize) // Deduct the modulus bit size + P::bits_per_limb() - 1) / // This term is to round up to next integer diff --git a/ecdsa-pop/src/emulated/field_ops.rs b/ecdsa-pop/src/emulated/field_ops.rs index be600c0c..bf02adf0 100644 --- a/ecdsa-pop/src/emulated/field_ops.rs +++ b/ecdsa-pop/src/emulated/field_ops.rs @@ -798,7 +798,7 @@ where return Ok(()); } - let num_chunks = (self.len() + P::num_limbs() - 1) / P::num_limbs(); + let num_chunks = self.len().div_ceil(P::num_limbs()); let mut chunks: Vec = vec![]; match &self.limbs { diff --git a/ecdsa-pop/src/lib.rs b/ecdsa-pop/src/lib.rs index edb30201..aa586383 100644 --- a/ecdsa-pop/src/lib.rs +++ b/ecdsa-pop/src/lib.rs @@ -171,7 +171,7 @@ struct ECDSACircuitProverInputs { impl ECDSACircuitProverInputs { pub fn new(s: &BigUint, q0: &BigUint, q1: &BigUint, z: &BigUint) -> Self { - Self { s: big_to_ff(&s), q0: big_to_ff(&q0), q1: big_to_ff(&q1), z: big_to_ff(&z) } + Self { s: big_to_ff(s), q0: big_to_ff(q0), q1: big_to_ff(q1), z: big_to_ff(z) } } } @@ -308,9 +308,9 @@ impl ECDSAProofCircuit { pub fn compute_hQ(params: &ECDSAParams, q0: &BigUint, q1: &BigUint, z: &BigUint) -> Vec { let NUM_ABSORBS = 3; let mut poseidon: Poseidon = Poseidon::new(params.constants.clone(), NUM_ABSORBS); - poseidon.absorb(big_to_ff(&q0)); - poseidon.absorb(big_to_ff(&q1)); - poseidon.absorb(big_to_ff(&z)); + poseidon.absorb(big_to_ff(q0)); + poseidon.absorb(big_to_ff(q1)); + poseidon.absorb(big_to_ff(z)); let hQ = poseidon.squeeze_field_element(); // H(q0, q1, z) @@ -337,9 +337,9 @@ impl ECDSAProofCircuit { { // Check that m = q0 + q1*e1 + z*e2 (mod q) // using emulated arithmetic - let q0 : EmulatedFieldElement = allocated_num_to_emulated_fe(&mut cs.namespace(||"convert q0"), &q0)?; - let q1 : EmulatedFieldElement = allocated_num_to_emulated_fe(&mut cs.namespace(||"convert q1"), &q1)?; - let z : EmulatedFieldElement = allocated_num_to_emulated_fe(&mut cs.namespace(||"convert z"), &z)?; + let q0 : EmulatedFieldElement = allocated_num_to_emulated_fe(&mut cs.namespace(||"convert q0"), q0)?; + let q1 : EmulatedFieldElement = allocated_num_to_emulated_fe(&mut cs.namespace(||"convert q1"), q1)?; + let z : EmulatedFieldElement = allocated_num_to_emulated_fe(&mut cs.namespace(||"convert z"), z)?; let tmp = e1.mul(&mut cs.namespace(||"e1*q1"), &q1)?; let tmp2 = e2.mul(&mut cs.namespace(||"e2*z"), &z)?; @@ -349,7 +349,7 @@ impl ECDSAProofCircuit { EmulatedFieldElement::::assert_is_equal( &mut cs.namespace(|| "check equality"), &m_calc, - &m, + m, )?; Ok(()) @@ -457,7 +457,7 @@ impl ECDSAProof { assert!(params.curve == NamedCurve::Secp256r1); // We only support one curve right now let public_inputs = ECDSACircuitPublicInputs::_default(); - let circuit_verifier = ECDSAProofCircuit::new(¶ms, None, &public_inputs); + let circuit_verifier = ECDSAProofCircuit::new(params, None, &public_inputs); let t = start_timer!(|| "Getting R1CS Shape"); let mut cs = ShapeCS::::new(); let _ = circuit_verifier.synthesize(&mut cs.namespace(||"synthesize verifier")); @@ -472,6 +472,7 @@ impl ECDSAProof { } /// Create a proof of an ECDSA signature + #[allow(clippy::too_many_arguments)] pub fn prove(params : &ECDSAParams, qx: &BigUint, qy: &BigUint, // Signer's public key r: &BigUint, s: &BigUint, digest: &[u8], // ECDSA signature on digest @@ -486,12 +487,12 @@ impl ECDSAProof { let (q0, q1) = ECDSAProof::split_public_key_x(qx); - let e1 = BigUint::from_bytes_le(&e1); - let e2 = BigUint::from_bytes_le(&e2); + let e1 = BigUint::from_bytes_le(e1); + let e2 = BigUint::from_bytes_le(e2); let public_inputs = ECDSACircuitPublicInputs::new(&T, &U, hQ, m.clone(), e1, e2); let prover_inputs = ECDSACircuitProverInputs::new(s, &q0, &q1, z); - let circuit_verifier = ECDSAProofCircuit::new(¶ms, None, &public_inputs); + let circuit_verifier = ECDSAProofCircuit::new(params, None, &public_inputs); let t = start_timer!(|| "Getting R1CS Shape"); let mut cs = ShapeCS::::new(); let _ = circuit_verifier.synthesize(&mut cs.namespace(||"synthesize verifier")); @@ -499,7 +500,7 @@ impl ECDSAProof { end_timer!(t); let t = start_timer!(|| "Calculate witness"); - let circuit_prover = ECDSAProofCircuit::new(¶ms, Some(prover_inputs), &public_inputs); + let circuit_prover = ECDSAProofCircuit::new(params, Some(prover_inputs), &public_inputs); let mut cs: SatisfyingAssignment = SatisfyingAssignment::new(); let _ = circuit_prover.clone().synthesize(&mut cs.namespace(||"calculate witness")); let (inst, witness, inputs) = cs.r1cs_instance_and_witness(&shape); @@ -521,7 +522,7 @@ impl ECDSAProof { let t = start_timer!(|| "Checking satisfiability (debugging only)"); let is_sat = inst.is_sat(&witness, &inputs); assert!(is_sat.is_ok()); - assert_eq!(is_sat.unwrap(), true); + assert!(is_sat.unwrap()); end_timer!(t); } @@ -550,6 +551,7 @@ impl ECDSAProof { } /// Verify the proof + #[allow(clippy::too_many_arguments)] pub fn verify(params : &ECDSAParams, Rx: &BigUint, Ry: &BigUint, digest : &[u8], hQ: &[u8], m: &BigUint, e1: &[u8], e2: &[u8], proof : &[u8]) -> bool { assert!(params.curve == NamedCurve::Secp256r1); // We only support one curve right now @@ -557,11 +559,11 @@ impl ECDSAProof { let R = Point{x: Rx.clone(), y: Ry.clone()}; let (T, U) = ECDSACircuitPublicInputs::compute_TU(&R, &hex::encode(digest), ¶ms.curve); - let e1 = BigUint::from_bytes_le(&e1); - let e2 = BigUint::from_bytes_le(&e2); + let e1 = BigUint::from_bytes_le(e1); + let e2 = BigUint::from_bytes_le(e2); let public_inputs = ECDSACircuitPublicInputs::new(&T, &U, hQ, m.clone(), e1, e2); - let circuit_verifier = ECDSAProofCircuit::new(¶ms, None, &public_inputs); + let circuit_verifier = ECDSAProofCircuit::new(params, None, &public_inputs); let t = start_timer!(|| "Getting R1CS Shape"); let mut cs = ShapeCS::::new(); let _ = circuit_verifier.synthesize(&mut cs.namespace(||"synthesize verifier")); @@ -581,7 +583,7 @@ impl ECDSAProof { let t = start_timer!(|| "Decompress proof"); let mut decoder = ZlibDecoder::new(Vec::new()); - match decoder.write_all(&proof) { + match decoder.write_all(proof) { Ok(_) => {}, Err(_) => return_false!("failed writing proof to ZlibDecoder") }; @@ -658,8 +660,8 @@ mod tests { GGA::new(x, y) } fn uint_to_point(x : &BigUint, y: &BigUint) -> GGA { - let x = uint_to_ark::(&x); - let y = uint_to_ark::(&y); + let x = uint_to_ark::(x); + let y = uint_to_ark::(y); GGA::new(x, y) } @@ -684,17 +686,18 @@ mod tests { } fn regular_ECDSA_ver(digest : &str, s: &BigUint, R_x : &BigUint, Q : &Point) -> bool{ - let z = hex_to_ark::(&digest); - let s = uint_to_ark::(&s); + let z = hex_to_ark::(digest); + let s = uint_to_ark::(s); let u1 = z/s; - let r = uint_to_ark::(&R_x); + let r = uint_to_ark::(R_x); let u2 = r/s; let G = hex_to_point( "6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", "4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5"); let Q = uint_to_point(&Q.x, &Q.y); let Rprime = G * u1 + Q * u2; let Rprime = Rprime.into_affine(); - return Rprime.x == uint_to_ark::(&R_x); + + Rprime.x == uint_to_ark::(R_x) } // cargo test --release --features print-trace test_ecdsa_proof -- --nocapture @@ -744,7 +747,7 @@ mod tests { let mut rng = thread_rng(); // Public key Q will be provided as two Pedersen commitments to the high and low // bytes of Q.x - let (q0, q1) = ECDSAProof::split_public_key_x(&Qx); + let (q0, q1) = ECDSAProof::split_public_key_x(Qx); // Setup some commitment bases G and H (just placeholders; the real values defined in Crescent) let G = G1Affine::generator(); @@ -788,7 +791,7 @@ mod tests { let (q0, q1, hQ, m, e1, e2, z) = compute_mock_adapter_values(&Q.x); let params = ECDSAParams::new(NamedCurve::Secp256r1, NamedCurve::Bn254); - let public_inputs = ECDSACircuitPublicInputs::new(&T, &U, &hQ, m, e1, e2); + let public_inputs = ECDSACircuitPublicInputs::new(T, U, &hQ, m, e1, e2); let prover_inputs = ECDSACircuitProverInputs::new(s, &q0, &q1, &z); let circuit_verifier = ECDSAProofCircuit::new(¶ms, None, &public_inputs); let circuit_prover = ECDSAProofCircuit::new(¶ms, Some(prover_inputs), &public_inputs); @@ -823,7 +826,7 @@ mod tests { let t = start_timer!(|| "Checking satisfiability (debugging only)"); let is_sat = inst.is_sat(&witness, &inputs); assert!(is_sat.is_ok()); - assert_eq!(is_sat.unwrap(), true); + assert!(is_sat.unwrap()); end_timer!(t); let t = start_timer!(|| "Producing NIZK public generators"); @@ -887,7 +890,7 @@ mod tests { let valid = ECDSAProof::verify(¶ms, &Rx, &Ry, &digest, &hQ, &m, &e1, &e2, &proof); end_timer!(t); - assert_eq!(valid, true); + assert!(valid); } diff --git a/ecdsa-pop/src/utils.rs b/ecdsa-pop/src/utils.rs index f43180d3..0c7f4f37 100644 --- a/ecdsa-pop/src/utils.rs +++ b/ecdsa-pop/src/utils.rs @@ -447,7 +447,7 @@ pub fn biguint_to_scalar(x : &BigUint) -> Scalar { pub fn mod_inverse(a: &BigUint, p: &BigUint) -> BigUint { let two = BigUint::from(2u8); - a.modpow(&(p - two), &p) + a.modpow(&(p - two), p) } diff --git a/forks/Spartan-t256/Cargo.toml b/forks/Spartan-t256/Cargo.toml index ab7faded..9d4248bb 100644 --- a/forks/Spartan-t256/Cargo.toml +++ b/forks/Spartan-t256/Cargo.toml @@ -12,15 +12,7 @@ keywords = ["zkSNARKs", "cryptography", "proofs"] [dependencies] lazy_static = "1.4" serde_bytes = { version = "0.11.14", default-features = false } -ark-ec = { version = "0.4.2", default-features = false } -ark-ff = { version = "0.4.2", default-features = false } -ark-serialize = { version = "0.4.2", default-features = false } -circ_fields = { path = "circ_fields" } -curve25519-dalek = { version = "3.2.0", features = [ - "serde", - "u64_backend", - "alloc", -], default-features = false } +halo2curves = {path = "../halo2curves", features=["derive_serde"]} merlin = { version = "3.0.0", default-features = false } rand = { version = "0.7.3", features = ["getrandom"], default-features = false } digest = { version = "0.8.1", default-features = false } @@ -29,20 +21,15 @@ byteorder = { version = "1.3.4", default-features = false } rayon = { version = "1.7.0", optional = true } serde = { version = "1.0.106", features = ["derive"], default-features = false } bincode = { version = "1.3.3", default-features = false } -subtle = { version = "2.4", features = ["i128"], default-features = false } -zeroize = { version = "1.5", default-features = false } itertools = { version = "0.10.0", default-features = false } colored = { version = "2.0.0", default-features = false, optional = true } flate2 = { version = "1.0.14" } -hex-literal = { version = "0.3" } -num-bigint-dig = "^0.7" -crrl = "0.9.0" +rand_core = { version = "0.6", default-features = false } -# deps to get the src/bellpepper code working +# deps for src/bellpepper ff = { version = "0.13.0", features = ["derive"] } bellpepper-core = { version="0.2.0", default-features = false } bellpepper = { version="0.2.0", default-features = false } -rug = { version = "1.11", features = ["serde"] } [dev-dependencies] @@ -74,21 +61,16 @@ required-features = ["std"] # required-features = ["std"] [features] -# default = ["std", "simd_backend"] default = ["std"] std = [ - "curve25519-dalek/std", "digest/std", "merlin/std", "rand/std", "sha3/std", "byteorder/std", "serde/std", - "subtle/std", - "zeroize/std", "itertools/use_std", "flate2/rust_backend", ] -# simd_backend = ["curve25519-dalek/simd_backend"] multicore = ["rayon"] profile = ["colored"] diff --git a/forks/Spartan-t256/circ_fields/.gitignore b/forks/Spartan-t256/circ_fields/.gitignore deleted file mode 100644 index f24cfd94..00000000 --- a/forks/Spartan-t256/circ_fields/.gitignore +++ /dev/null @@ -1,13 +0,0 @@ -/target -/pf -/assignment.txt -/params -__pycache__ -/P -/V -/pi -/x -/perf.data* -/.gdb_history -.features.txt -scripts/aby_tests/tests diff --git a/forks/Spartan-t256/circ_fields/Cargo.toml b/forks/Spartan-t256/circ_fields/Cargo.toml deleted file mode 100644 index 19399ccf..00000000 --- a/forks/Spartan-t256/circ_fields/Cargo.toml +++ /dev/null @@ -1,29 +0,0 @@ -[package] -name = "circ_fields" -version = "0.1.0" -authors = ["kwantam "] -edition = "2021" - -[dependencies] -# serde_bytes = { version = "0.11.14", default-features = false } -sha2 = "0.10.8" # needed to implement hash to curve in spartan -ff = { version = "0.13", features = ["derive", "bits"] } -ff_derive = {version = "0.13", features = ["bits"] } -ff-derive-num = {path = "ff-derive-num"} -lazy_static = "1.4" -num-traits = "0.2" -paste = "1.0" -rand = "0.8" -rug = { version = "1.11", features = ["serde"] } -serde = { version = "1.0", features = ["derive", "rc"] } -datasize = { version = "0.2", features = ["detailed"] } -ark-ff = { version = "0.4.2", default-features = false } -ark-ec = { version = "0.4.2", default-features = false} -ark-r1cs-std = { version = "0.4.0", default-features = false, optional = true } -ark-std = { version = "0.4.0", default-features = false } -ark-secp256r1 = {default-features = false, git = "https://github.com/arkworks-rs/curves" } -ark-ff-macros = { version = "0.4.2", default-features = false } -ark-serialize = { version = "0.4.2", default-features = false } - -[dev-dependencies] -rand = "0.8" diff --git a/forks/Spartan-t256/circ_fields/README.md b/forks/Spartan-t256/circ_fields/README.md deleted file mode 100644 index e2a0e7f7..00000000 --- a/forks/Spartan-t256/circ_fields/README.md +++ /dev/null @@ -1,11 +0,0 @@ -This is a fork of circ_fields, modified with support for the T-256 elliptic curve. -Circ: https://github.com/circify/circ - -Pui Yung Anna Woo is the original author of the fork, as part of the sig-pop project: -Efficient Proofs of Possession for Legacy Signatures -2025 IEEE Symposium on Security and Privacy (SP) -Anna Pui Yung Woo, Alex Ozdemir, Chad Sharp, Thomas Pornin, Paul Grubbs -https://www.computer.org/csdl/proceedings-article/sp/2025/223600a080/21B7RmZTW00 - -Greg Zaverucha made further small changes - diff --git a/forks/Spartan-t256/circ_fields/ff-derive-num/.gitignore b/forks/Spartan-t256/circ_fields/ff-derive-num/.gitignore deleted file mode 100644 index 96ef6c0b..00000000 --- a/forks/Spartan-t256/circ_fields/ff-derive-num/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target -Cargo.lock diff --git a/forks/Spartan-t256/circ_fields/ff-derive-num/Cargo.toml b/forks/Spartan-t256/circ_fields/ff-derive-num/Cargo.toml deleted file mode 100644 index 66084f58..00000000 --- a/forks/Spartan-t256/circ_fields/ff-derive-num/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "ff-derive-num" -version = "0.3.0" -edition = "2018" -authors = ["kwantam "] -description = "Procedural macro library to add the Num trait to Field implementations for ff" -homepage = "https://github.com/kwantam/ff-derive-num" -license = "MIT/Apache-2.0" -repository = "https://github.com/kwantam/ff-derive-num" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[lib] -proc-macro = true - -[dependencies] -num-traits = "0.2" -proc-macro2 = "1" -quote = "1" -syn = "1" - -[dev-dependencies] -ff = { version = "0.13", features = ["derive"] } \ No newline at end of file diff --git a/forks/Spartan-t256/circ_fields/ff-derive-num/FORKED.md b/forks/Spartan-t256/circ_fields/ff-derive-num/FORKED.md deleted file mode 100644 index c3edf280..00000000 --- a/forks/Spartan-t256/circ_fields/ff-derive-num/FORKED.md +++ /dev/null @@ -1,3 +0,0 @@ -This is a fork of ff_derive_num, with the code in https://github.com/kwantam/ff-derive-num/pull/1 -This PR was never merged, but is required to use ff_derive_num with version 0.13 of ff -Since bellpepper requires ff verion 0.13, we need to update circ_field as well diff --git a/forks/Spartan-t256/circ_fields/ff-derive-num/LICENSE-mit b/forks/Spartan-t256/circ_fields/ff-derive-num/LICENSE-mit deleted file mode 100644 index 11cd47d4..00000000 --- a/forks/Spartan-t256/circ_fields/ff-derive-num/LICENSE-mit +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2021 Riad S. Wahby - -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. diff --git a/forks/Spartan-t256/circ_fields/ff-derive-num/README.md b/forks/Spartan-t256/circ_fields/ff-derive-num/README.md deleted file mode 100644 index 20222e99..00000000 --- a/forks/Spartan-t256/circ_fields/ff-derive-num/README.md +++ /dev/null @@ -1,32 +0,0 @@ -# ff-derive-num - -This crate provides a derive macro for [num-traits::Num] and associated traits -for [ff::Field] implementations derived with the [ff-derive] crate. - -[num-traits::Num]: https://docs.rs/num-traits -[ff::Field]: https://docs.rs/ff -[ff-derive]: https://docs.rs/ff-derive - -## example - -```rust -use ff::PrimeField; // ff should be used with the "derive" feature! -use ff_derive_num::Num; - -#[derive(PrimeField,Num)] -#[PrimeFieldModulus = "70386805592835581672624750593"] -#[PrimeFieldGenerator = "17"] -#[PrimeFieldReprEndianness = "little"] -pub struct Ft([u64; 2]); -``` - -## license - -Copyright 2021 Riad S. Wahby - -You may choose either the [Apache-2.0] license or the [MIT] license. - -Unless you explicitly state otherwise, any contribution you submit will also be dual-licensed. - -[Apache-2.0]: https://www.apache.org/licenses/LICENSE-2.0 -[MIT]: https://opensource.org/licenses/MIT diff --git a/forks/Spartan-t256/circ_fields/ff-derive-num/src/lib.rs b/forks/Spartan-t256/circ_fields/ff-derive-num/src/lib.rs deleted file mode 100644 index a0b225ab..00000000 --- a/forks/Spartan-t256/circ_fields/ff-derive-num/src/lib.rs +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright 2021 Riad S. Wahby -// -// This file is part of ff-derive-num -// -// Licensed under EITHER: -// -// - The Apache License, Version 2.0 (see LICENSE-apache or -// https://www.apache.org/licenses/LICENSE-2.0). -// -// - The MIT License (see LICENSE-mit or -// https://opensource.org/licenses/MIT). -// -// This file may not be copied, modified, or distributed -// except according to the terms of ONE of these licenses, -// at your discretion. -#![deny(missing_docs)] - -/*! Derive ::num_traits::Num and associated traits for ::ff::Field types derived using ::ff_derive - -# example - -```rust -use ff::PrimeField; // ff should be used with the "derive" feature! -use ff_derive_num::Num; - -#[derive(PrimeField,Num)] -#[PrimeFieldModulus = "70386805592835581672624750593"] -#[PrimeFieldGenerator = "17"] -#[PrimeFieldReprEndianness = "little"] -pub struct Ft([u64; 2]); -``` -*/ - -use quote::quote; -use syn::DeriveInput; - -/// Proc macro for Num derivation -#[proc_macro_derive(Num)] -pub fn num_traits_num(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let ast: DeriveInput = syn::parse(input).unwrap(); - - let ident = ast.ident; - let mut toks = proc_macro2::TokenStream::new(); - toks.extend(quote! { - impl ::num_traits::Num for #ident { - type FromStrRadixErr = ::std::num::ParseIntError; - - fn from_str_radix(s: &str, r: u32)-> Result { - if s.is_empty() { - // hack - return Err(u32::from_str_radix(s, r).err().unwrap()); - } - - if s == "0" { - return Ok(::ZERO); - } - - let mut res = ::ZERO; - let radix = Self::from(r as u64); - let mut first_digit = true; - for c in s.chars() { - match c.to_digit(r) { - Some(c) => { - if first_digit { - if c == 0 { - return Err(u32::from_str_radix("3", 2).err().unwrap()); - } - first_digit = false; - } - - res *= &radix; - res += Self::from(c as u64); - } - None => { - return Err(u32::from_str_radix("3", 2).err().unwrap()); - } - } - } - Ok(res) - } - } - - impl ::num_traits::Zero for #ident { - fn zero() -> Self { - ::ZERO - } - - fn is_zero(&self) -> bool { - bool::from(::is_zero(self)) - } - } - - impl ::num_traits::One for #ident { - fn one() -> Self { - ::ONE - } - - fn is_one(&self) -> bool { - self == &::ONE - } - } - - #[allow(clippy::suspicious_arithmetic_impl)] - impl ::std::ops::Div<#ident> for #ident { - type Output = Self; - - #[must_use] - fn div(self, rhs: Self) -> Self { - use ::ff::Field; - self * ::invert(&rhs).unwrap() - } - } - - #[allow(clippy::suspicious_arithmetic_impl)] - impl ::std::ops::Div<&#ident> for #ident { - type Output = Self; - - fn div(self, rhs: &Self) -> Self { - self * ::invert(rhs).unwrap() - } - } - - impl ::std::ops::Rem<#ident> for #ident { - type Output = Self; - - #[must_use] - fn rem(self, rhs: Self) -> Self { - if bool::from(::is_zero(&self)) { - panic!("divide by zero"); - } - - ::ZERO - } - } - - impl ::std::ops::Rem<&#ident> for #ident { - type Output = Self; - - #[must_use] - fn rem(self, rhs: &Self) -> Self { - if bool::from(::is_zero(&self)) { - panic!("divide by zero"); - } - - ::ZERO - } - } - - impl ::num_traits::ops::mul_add::MulAdd for #ident { - type Output = Self; - - fn mul_add(mut self, a: Self, b: Self) -> Self { - self *= &a; - self += &b; - self - } - } - - impl ::num_traits::ops::mul_add::MulAdd<&#ident, &#ident> for #ident { - type Output = Self; - - fn mul_add(mut self, a: &Self, b: &Self) -> Self { - self *= a; - self += b; - self - } - } - }); - - toks.into() -} diff --git a/forks/Spartan-t256/circ_fields/src/ff_field.rs b/forks/Spartan-t256/circ_fields/src/ff_field.rs deleted file mode 100644 index 2a2fadbe..00000000 --- a/forks/Spartan-t256/circ_fields/src/ff_field.rs +++ /dev/null @@ -1,171 +0,0 @@ -//! ff-based fields for use in CirC - -use paste::paste; - -macro_rules! i64_impl { - ($Trait: ident, $fn: ident, $Ty: ident) => { - impl $Trait for $Ty { - fn $fn(&mut self, other: i64) { - self.$fn($Ty::from(other)); - } - } - }; -} - -macro_rules! def_field { - ($name: ident, $mod: literal, $gen: literal, $limbs: literal) => { - paste! { pub use $name::Ft as [<$name:camel>]; } - paste! { pub use $name::FMOD as [<$name:upper _FMOD>]; } - paste! { pub use $name::FMOD_ARC as [<$name:upper _FMOD_ARC>]; } - pub mod $name { - #![allow(warnings, clippy::derive_hash_xor_eq)] - use datasize::DataSize; - use ff_derive::PrimeField; - use ff_derive_num::Num; - use serde::{Deserialize, Serialize}; - #[derive(PrimeField, Num, Deserialize, Serialize, Hash, DataSize)] - #[PrimeFieldModulus = $mod] - #[PrimeFieldGenerator = $gen] - #[PrimeFieldReprEndianness = "little"] - /// Field definition - pub struct Ft([u64; $limbs]); - - use lazy_static::lazy_static; - use rug::Integer; - use std::sync::Arc; - lazy_static! { - /// Field modulus - pub static ref FMOD: Integer = Integer::from_str_radix($mod, 10).unwrap(); - pub static ref FMOD_ARC: Arc = Arc::new(FMOD.clone()); - } - - use rug::integer::Order; - impl std::convert::From<&Ft> for Integer { - fn from(f: &Ft) -> Self { - use ff::PrimeField; - Self::from_digits(f.to_repr().as_ref(), Order::Lsf) - } - } - - impl std::convert::TryFrom<&Integer> for Ft { - type Error = &'static str; - - fn try_from(i: &Integer) -> Result { - use ff::PrimeField; - let len = 8 * $limbs; - if i.significant_digits::() > len { - return Err("Tried to convert un-reduced Integer to Ft"); - } - let mut bytes = [0u8; 8 * $limbs]; - i.write_digits(&mut bytes, Order::Lsf); - Self::from_repr_vartime(FtRepr(bytes)) - .ok_or("Tried to convert un-reduced Integer to Ft") - } - } - - impl std::convert::From for Ft { - #[track_caller] - fn from(mut i: Integer) -> Self { - use ff::PrimeField; - use rug::ops::RemRoundingAssign; - use std::cmp::Ordering::Less; - - let len = 8 * $limbs; - let mut bytes = [0u8; 8 * $limbs]; - if i.significant_digits::() > len || i.cmp0() == Less { - i.rem_floor_assign(&*FMOD); - } - i.write_digits(&mut bytes, Order::Lsf); - Self::from_repr_vartime(FtRepr(bytes)).unwrap() - } - } - - impl std::convert::From for Ft { - #[track_caller] - fn from(mut i: i64) -> Self { - let u = i.abs_diff(0); - let neg = i < 0; - if neg { - -Ft::from(u) - } else { - Ft::from(u) - } - } - } - - use std::ops::{AddAssign, MulAssign, SubAssign}; - i64_impl! { AddAssign, add_assign, Ft } - i64_impl! { SubAssign, sub_assign, Ft } - i64_impl! { MulAssign, mul_assign, Ft } - - #[cfg(test)] - mod test { - use super::{Ft, FMOD}; - use ff::{Field,PrimeFieldBits}; - use rand::thread_rng; - use rug::Integer; - - #[test] - fn test_ff_roundtrip() { - let mut rng = thread_rng(); - for _ in 0..1024 { - let a0 = Ft::random(&mut rng); - let a1 = Integer::from(&a0); - let a2 = Ft::try_from(&a1).unwrap(); - assert_eq!(a0, a2); - - let ainv0 = a0.invert().unwrap(); - let ainv1 = Integer::from(a1.invert_ref(&*FMOD).unwrap()); - let ainv2 = Ft::try_from(&ainv1).unwrap(); - assert_eq!(ainv0, ainv2); - - let a3 = Ft::from(a1); - assert_eq!(a0, a3); - let ainv3 = Ft::from(ainv1); - assert_eq!(ainv0, ainv3); - } - } - // The PrimeFieldBits trait is only derived when the bits feature is enabled in the ff and ff_derive dependencies. - // This test just checks that the PrimeFieldBits trait is in fact implemented. - #[test] - fn test_prime_field_bits_trait() { - let mut rng = thread_rng(); - let a = Ft::random(&mut rng); - let bits = a.to_le_bits(); - for b in bits { - println!("{:?}",b); - } - } - } - } - }; -} - -def_field!( - f_bls12381, - "52435875175126190479447740508185965837690552500527637822603658699938581184513", - "7", - 4 -); - -def_field!( - f_bn254, - "21888242871839275222246405745257275088548364400416034343698204186575808495617", - "5", - 4 -); - -// def_field!( // this is the group order of the P256 curve (https://neuromancer.sk/std/nist/P-256) -// f_t256, -// "115792089210356248762697446949407573529996955224135760342422259061068512044369", -// "5", -// 5 -// ); - - -def_field!( // this is the base field of the P256 curve (https://neuromancer.sk/std/nist/P-256) - f_t256, - "115792089210356248762697446949407573530086143415290314195533631308867097853951", - "5", - 5 -); \ No newline at end of file diff --git a/forks/Spartan-t256/circ_fields/src/int_field.rs b/forks/Spartan-t256/circ_fields/src/int_field.rs deleted file mode 100644 index 262f7489..00000000 --- a/forks/Spartan-t256/circ_fields/src/int_field.rs +++ /dev/null @@ -1,195 +0,0 @@ -//! Integer-based fields for use in CirC -// based on Alex's original FieldElem impl - -use datasize::DataSize; -use paste::paste; -use rug::{ - ops::{RemRounding, RemRoundingAssign}, - Integer, -}; -use serde::{Deserialize, Serialize}; -use std::fmt::{self, Display, Formatter}; -use std::ops::Deref; -use std::sync::Arc; - -#[derive(PartialEq, Eq, Clone, Debug, PartialOrd, Ord, Hash, Serialize, Deserialize, DataSize)] -pub struct IntField { - #[data_size(with = super::size::estimate_heap_size_integer)] - pub(crate) i: Integer, - m: Arc, -} - -impl Display for IntField { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - if self.i.significant_bits() + 1 < self.m.significant_bits() { - write!(f, "#f{}m{}", self.i, self.m) - } else { - write!(f, "#f-{}m{}", self.m.deref().clone() - &self.i, self.m) - } - } -} - -#[allow(clippy::from_over_into)] -impl Into for IntField { - fn into(self) -> Integer { - self.i - } -} - -#[allow(clippy::from_over_into)] -impl Into for &IntField { - fn into(self) -> Integer { - self.i.clone() - } -} - -impl IntField { - #[track_caller] - #[inline] - /// Check value in-range (debug only) - pub fn check(&self, location: &str) { - debug_assert!( - self.i >= 0, - "Negative field elem: {}\nat {}", - self, - location - ); - debug_assert!( - self.i < *self.m, - "Field elem too big: {}\nat {}", - self, - location - ); - } - - /// Get a ref to the modulus - pub fn modulus(&self) -> &Integer { - &self.m - } - - /// Get an Arc of the modulus - pub fn modulus_arc(&self) -> Arc { - self.m.clone() - } - - /// Invert mod p - pub fn recip(self) -> Self { - let r = Self { - i: self.i.invert(&self.m).expect("no inverse!"), - m: self.m, - }; - r.check("recip"); - r - } - - /// Construct a new IntField - pub fn new(mut i: Integer, m: Arc) -> Self { - if i < 0 || i > *m { - i.rem_floor_assign(&*m); - } - Self { i, m } - } - - /// Update the value - pub fn update_val(&mut self, new_i: Integer) { - self.update(new_i, self.m.clone()); - } - /// Change the modulus - pub fn update_modulus(&mut self, new_modulus: Arc) { - self.update(self.i.clone(), new_modulus); - } - - /// Update the value or the modulus - pub fn update(&mut self, mut new_i: Integer, m: Arc) { - if new_i < 0 || new_i > *m { - new_i.rem_floor_assign(&*m); - } - self.i = new_i; - self.m = m; - } - - /// Check if this value is equal to zero - pub fn is_zero(&self) -> bool { - self.check("is_zero"); - self.i == 0 - } -} - -macro_rules! arith_impl { - ($Trait: ident, $fn: ident) => { - impl $Trait for IntField { - type Output = Self; - fn $fn(self, other: Self) -> Self { - assert_eq!(self.m, other.m); - let r = Self { - i: (self.i.$fn(other.i)).rem_floor(&*self.m), - m: self.m, - }; - r.check(std::stringify!($fn)); - r - } - } - - impl $Trait<&IntField> for IntField { - type Output = Self; - fn $fn(self, other: &Self) -> Self { - assert_eq!(self.m, other.m); - let r = Self { - i: (self.i.$fn(&other.i)).rem_floor(&*self.m), - m: self.m, - }; - r.check(std::stringify!($fn)); - r - } - } - - impl $Trait for &IntField { - type Output = IntField; - fn $fn(self, other: IntField) -> IntField { - assert_eq!(self.m, other.m); - let r = IntField { - i: ((&self.i).$fn(other.i)).rem_floor(&*self.m), - m: other.m, - }; - r.check(std::stringify!($fn)); - r - } - } - - paste! { - impl [<$Trait Assign>]<&IntField> for IntField { - fn [<$fn _assign>](&mut self, other: &IntField) { - assert_eq!(self.m, other.m); - self.i.[<$fn _assign>](&other.i); - self.i.rem_floor_assign(&*self.m); - } - } - } - - paste! { - impl [<$Trait Assign>] for IntField { - fn [<$fn _assign>](&mut self, other: i64) { - self.i.[<$fn _assign>](&other); - self.i.rem_floor_assign(&*self.m); - } - } - } - }; -} - -use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -arith_impl!(Add, add); -arith_impl!(Sub, sub); -arith_impl!(Mul, mul); - -impl Neg for IntField { - type Output = Self; - fn neg(self) -> Self { - let r = Self { - i: (-self.i).rem_floor(&*self.m), - m: self.m, - }; - r.check("neg"); - r - } -} diff --git a/forks/Spartan-t256/circ_fields/src/lib.rs b/forks/Spartan-t256/circ_fields/src/lib.rs deleted file mode 100644 index 4f704746..00000000 --- a/forks/Spartan-t256/circ_fields/src/lib.rs +++ /dev/null @@ -1,926 +0,0 @@ -//! Fields for use in CirC - -#![warn(missing_docs)] - -pub mod ff_field; -mod int_field; -pub mod size; -pub mod t256; -/// Exports for moduli defined in this crate, as ARCs -pub mod moduli { - pub use super::ff_field::{F_BLS12381_FMOD_ARC, F_BN254_FMOD_ARC}; -} - -use ff_field::{FBls12381, FBn254}; -use ff_field::{F_BLS12381_FMOD, F_BN254_FMOD}; -use ff_field::{F_BLS12381_FMOD_ARC, F_BN254_FMOD_ARC}; -use int_field::IntField; - -use datasize::DataSize; -use ff::Field; -use paste::paste; -use rug::Integer; -use serde::{Deserialize, Serialize}; -use std::fmt::{self, Debug, Display, Formatter}; -use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use std::sync::Arc; - -// TODO: rework this using macros? - -/// Field element type -#[derive(PartialEq, Eq, Clone, PartialOrd, Ord, Hash, Serialize, Deserialize, DataSize)] -pub enum FieldT { - /// BLS12-381 scalar field as `ff` - FBls12381, - /// BN-254 scalar field as `ff` - FBn254, - /// Generic field element based on `rug::Integer` - IntField(Arc), -} - -impl Display for FieldT { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - match self { - Self::FBls12381 => write!(f, "FieldT::FBls12381"), - Self::FBn254 => write!(f, "FieldT::FBn254"), - Self::IntField(m) => write!(f, "FieldT::(mod {})", m), - } - } -} - -impl Debug for FieldT { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{}", self) - } -} - -impl From> for FieldT { - fn from(m: Arc) -> Self { - Self::as_ff_opt(m.as_ref()).unwrap_or(Self::IntField(m)) - } -} - -impl From for FieldT { - fn from(m: Integer) -> Self { - Self::as_ff_opt(&m).unwrap_or_else(|| Self::IntField(Arc::new(m))) - } -} - -impl From<&Integer> for FieldT { - fn from(m: &Integer) -> Self { - Self::as_ff_opt(m).unwrap_or_else(|| Self::IntField(Arc::new(m.clone()))) - } -} - -impl FieldT { - // Returns a FieldT if this modulus can be represented as `ff`, `None` otherwise. - fn as_ff_opt(m: &Integer) -> Option { - match m { - m if m == &*F_BLS12381_FMOD => Some(Self::FBls12381), - m if m == &*F_BN254_FMOD => Some(Self::FBn254), - _ => None, - } - } - - /// If this field has an inline tag, get it. - fn inline_tag(&self) -> Option { - match self { - FieldT::FBls12381 => Some(InlineFieldTag::Bls12381), - FieldT::FBn254 => Some(InlineFieldTag::Bn254), - FieldT::IntField(_) => None, - } - } - - /// Field modulus - #[inline] - pub fn modulus(&self) -> &Integer { - match self { - Self::FBls12381 => &F_BLS12381_FMOD, - Self::FBn254 => &F_BN254_FMOD, - Self::IntField(m) => m.as_ref(), - } - } - - /// Field modulus as an ARC - #[inline] - pub fn modulus_arc(&self) -> Arc { - match self { - Self::FBls12381 => F_BLS12381_FMOD_ARC.clone(), - Self::FBn254 => F_BN254_FMOD_ARC.clone(), - Self::IntField(m) => m.clone(), - } - } - - /// Default value for this type - #[inline] - pub fn default_value(&self) -> FieldV { - match self { - Self::FBls12381 => FieldV::from(InlineFieldV(0, InlineFieldTag::Bls12381)), - Self::FBn254 => FieldV::from(InlineFieldV(0, InlineFieldTag::Bn254)), - Self::IntField(_) => self.new_v(0), - } - } - - /// Zero for this type - #[inline] - pub fn zero(&self) -> FieldV { - self.default_value() - } - - /// Random value of this type - #[inline] - pub fn random_v(&self, rng: impl rand::RngCore) -> FieldV { - FieldV::random(self.clone(), rng) - } - - /// New value of this type - #[inline] - pub fn new_v(&self, i: I) -> FieldV - where - Integer: From, - { - FieldV::new_ty_long(i, self.clone()) - } - - /// New value of this type - #[inline] - pub fn new_v_prim(&self, i: I) -> FieldV - where - i64: From, - { - FieldV::new_ty(i, self.clone()) - } -} - -/// Field element value -/// -/// ## Implementation Notes -/// -/// The contents are either: -/// * a pointer to an enum [FullFieldV] or -/// * a i62 with a type-tag -/// -/// The tag can be: -/// * 00: pointer -/// * 01, 10: different fields -/// * 11: invalid (for now) -#[derive(Serialize, Deserialize)] -#[serde(into = "FullFieldV", from = "FullFieldV")] -pub struct FieldV(i64); - -/// Number of bits in [FieldV] used for the tag. -const N_TAG_BITS: u8 = 2; -/// Number of bits in [FieldV] used for the tag. -const N_TAG_BITS_I64: i64 = N_TAG_BITS as i64; -/// Mask that selects the tag bits in a [FieldV]. -const TAG_BITS_MASK: i64 = (1 << N_TAG_BITS_I64) - 1; - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -enum FieldTag { - FullField, - InlineBls12381, - InlineBn254, -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -enum InlineFieldTag { - Bls12381, - Bn254, -} - -impl From for FieldTag { - fn from(value: u8) -> Self { - match value { - 0 => FieldTag::FullField, - 1 => FieldTag::InlineBls12381, - 2 => FieldTag::InlineBn254, - _ => panic!("Invalid field tag {}", value), - } - } -} - -impl From for FieldTag { - fn from(value: InlineFieldTag) -> Self { - match value { - InlineFieldTag::Bls12381 => FieldTag::InlineBls12381, - InlineFieldTag::Bn254 => FieldTag::InlineBn254, - } - } -} - -impl From for InlineFieldTag { - fn from(value: FieldTag) -> Self { - match value { - FieldTag::InlineBls12381 => InlineFieldTag::Bls12381, - FieldTag::InlineBn254 => InlineFieldTag::Bn254, - FieldTag::FullField => panic!("Tag {:?} is not inline", value), - } - } -} - -impl FieldTag { - const fn u8(&self) -> u8 { - match self { - FieldTag::FullField => 0, - FieldTag::InlineBls12381 => 1, - FieldTag::InlineBn254 => 2, - } - } -} - -impl InlineFieldTag { - fn ty(&self) -> FieldT { - match self { - InlineFieldTag::Bls12381 => FieldT::FBls12381, - InlineFieldTag::Bn254 => FieldT::FBn254, - } - } - fn modulus(&self) -> &'static Integer { - match self { - InlineFieldTag::Bls12381 => &F_BLS12381_FMOD, - InlineFieldTag::Bn254 => &F_BN254_FMOD, - } - } - fn matches(&self, v: &FullFieldV) -> bool { - match (self, v) { - (InlineFieldTag::Bls12381, FullFieldV::FBls12381(_)) => true, - (InlineFieldTag::Bn254, FullFieldV::FBn254(_)) => true, - _ => false, - } - } - #[track_caller] - fn assert_matches(&self, v: &FullFieldV) { - assert!(self.matches(v), "Field {:?} does not match {:?}", self, v); - } -} - -struct InlineFieldV(i64, InlineFieldTag); - -impl InlineFieldV { - fn i64_in_range(i: i64) -> bool { - ((i << N_TAG_BITS_I64) >> N_TAG_BITS_I64) == i - } - fn signed_bits(&self) -> u32 { - if self.0 < 0 { - 65 - self.0.leading_ones() - } else { - 65 - self.0.leading_zeros() - } - } - fn repr_as_i64(&self) -> i64 { - debug_assert!(InlineFieldV::i64_in_range(self.0)); - self.0 << N_TAG_BITS_I64 | FieldTag::from(self.1).u8() as i64 - } -} - -impl From for FieldV { - fn from(v: InlineFieldV) -> Self { - FieldV(v.repr_as_i64()) - } -} -impl From for FieldV { - fn from(f: FullFieldV) -> Self { - let this = Self(Box::into_raw(Box::new(f)) as i64); - assert!(this.is_full()); - this - } -} -impl From for FullFieldV { - fn from(mut f: FieldV) -> Self { - if f.is_full() { - f.take_full() - } else { - FullFieldV::from(f.inline()) - } - } -} - -impl FieldV { - fn tag(&self) -> FieldTag { - FieldTag::from((self.0 & TAG_BITS_MASK) as u8) - } - fn inline_i64(&self) -> i64 { - debug_assert_ne!(self.tag(), FieldTag::FullField); - self.0 >> N_TAG_BITS_I64 - } - fn full_mut(&mut self) -> &mut FullFieldV { - assert!(self.is_full()); - let ptr: *mut FullFieldV = self.0 as *mut _; - unsafe { &mut *ptr } - } - fn full_ref(&self) -> &FullFieldV { - assert!(self.is_full()); - let ptr: *const FullFieldV = self.0 as *const _; // converts the internal data of FieldV into a raw pointer to FullFieldV - unsafe { &*ptr } - } - fn full_cow(&self) -> std::borrow::Cow { - if self.is_full() { - std::borrow::Cow::Borrowed(self.full_ref()) - } else { - std::borrow::Cow::Owned(FullFieldV::from(self.inline())) - } - } - fn take_full(&mut self) -> FullFieldV { - assert!(self.is_full()); - unsafe { - let ptr: *mut FullFieldV = self.0 as *mut FullFieldV; - let box_ = Box::from_raw(ptr); - self.0 = InlineFieldV(0, InlineFieldTag::Bls12381).repr_as_i64(); - *box_ - } - } - fn is_full(&self) -> bool { - self.tag() == FieldTag::FullField - } - fn inline(&self) -> InlineFieldV { - debug_assert!(!self.is_full()); - InlineFieldV(self.inline_i64(), self.tag().into()) - } - fn promote(&mut self) -> &mut FullFieldV { - if !self.is_full() { - *self = Self::from(FullFieldV::from(self.inline())); - } - self.full_mut() - } - fn either_ref(&self) -> Result { - if self.is_full() { - Err(self.full_ref()) - } else { - Ok(self.inline()) - } - } - - /// Change the modulus of this field element; newly added to change the underlying curve for the r1cs circuit - pub fn update_modulus(&mut self, new_modulus: Arc) { - let full_element: &mut FullFieldV = self.full_mut(); - - match full_element { - FullFieldV::IntField(ref mut i) => { - i.update_modulus(new_modulus); - // *i = i.clone().mod_floor(new_modulus); - } - _ => {panic!("Not applicable to non-integer field elements");} - } - } - /// Change the value of this field element; newly added to change the underlying curve for the r1cs circuit - pub fn update_val(&mut self, new_val: Integer) { - let full_element: &mut FullFieldV = self.full_mut(); - - match full_element { - FullFieldV::IntField(ref mut i) => { - i.update_val(new_val); - } - _ => {panic!("Not applicable to non-integer field elements");} - } - } - -} - -impl Clone for FieldV { - #[inline] - fn clone(&self) -> Self { - self.either_ref() - .map(|i| i.into()) - .unwrap_or_else(|full| Self::from(full.clone())) - } -} - -impl PartialOrd for FieldV { - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for FieldV { - #[inline] - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.full_cow().cmp(&other.full_cow()) - } -} - -impl PartialEq for FieldV { - #[inline] - fn eq(&self, other: &Self) -> bool { - self.full_cow().eq(&other.full_cow()) - } -} - -impl Eq for FieldV {} - -impl std::hash::Hash for FieldV { - fn hash(&self, state: &mut H) { - self.full_cow().hash(state) - } -} - -impl std::ops::Drop for FieldV { - fn drop(&mut self) { - if self.is_full() { - std::mem::drop(self.take_full()) - } - } -} - -/// Field element value -#[derive(PartialEq, Eq, Clone, PartialOrd, Ord, Hash, Serialize, Deserialize, DataSize)] -pub enum FullFieldV { - /// BLS12-381 scalar field element as `ff` - FBls12381(FBls12381), - /// BN-254 scalar field element as `ff` - FBn254(FBn254), - /// Generic field element based on `rug::Integer` - IntField(IntField), -} - -impl From for FullFieldV { - fn from(value: InlineFieldV) -> Self { - match value.1 { - InlineFieldTag::Bls12381 => FullFieldV::FBls12381(value.0.into()), - InlineFieldTag::Bn254 => FullFieldV::FBn254(value.0.into()), - } - } -} - -impl FullFieldV { - /// Type of this value - #[inline] - fn ty(&self) -> FieldT { - match self { - FullFieldV::FBls12381(_) => FieldT::FBls12381, - FullFieldV::FBn254(_) => FieldT::FBn254, - FullFieldV::IntField(i) => FieldT::IntField(i.modulus_arc()), - } - } - - /// Raise this element to a power. - #[inline] - fn pow(&self, u: u64) -> Self { - match self { - FullFieldV::FBls12381(f) => FullFieldV::FBls12381(f.pow_vartime(&[u])), - FullFieldV::FBn254(f) => FullFieldV::FBn254(f.pow_vartime(&[u])), - FullFieldV::IntField(i) => FullFieldV::IntField(IntField::new( - i.i.clone().pow_mod(&Integer::from(u), i.modulus()).unwrap(), - i.modulus_arc(), - )), - } - } - - /// Get this field element as a signed integer. No particular cut-off is guaranteed. - #[inline] - fn signed_int(&self) -> Integer { - let mut i: Integer = self.into(); - if i.significant_bits() >= self.ty().modulus().significant_bits() - 1 { - i -= self.ty().modulus(); - } - i - } -} - -impl FieldV { - /// Type of this value - #[inline] - pub fn ty(&self) -> FieldT { - match self.either_ref() { - Ok(InlineFieldV(_, t)) => t.ty(), - Err(i) => i.ty(), - } - } - - /// New field element from value and modulus. - /// - /// This function uses `FieldT::from`, which means that if there is - /// an `ff` implementation available it will return that, otherwise - /// it will use a `rug::Integer` implementation. - #[inline] - pub fn new(i: I, m: Arc) -> Self - where - Integer: From, - { - Self::new_ty_long(i, FieldT::from(m)) - } - - /// Check that a value is well formed - #[track_caller] - #[inline] - pub fn check(&self, loc: &str) { - if let Err(FullFieldV::IntField(m)) = self.either_ref() { - m.check(loc); - } - } - - /// Field modulus as `&rug::Integer` - #[inline] - pub fn modulus(&self) -> &Integer { - match self.either_ref() { - Ok(InlineFieldV(_, t)) => t.modulus(), - Err(FullFieldV::FBls12381(_)) => &F_BLS12381_FMOD, - Err(FullFieldV::FBn254(_)) => &F_BN254_FMOD, - Err(FullFieldV::IntField(i)) => i.modulus(), - } - } - - /// Compute the multiplicative inverse (panics on 0) - #[track_caller] - #[inline] - pub fn recip_ref(&self) -> Self { - match &*self.full_cow() { - FullFieldV::FBls12381(pf) => Self::from(FullFieldV::FBls12381(pf.invert().unwrap())), - FullFieldV::FBn254(pf) => Self::from(FullFieldV::FBn254(pf.invert().unwrap())), - FullFieldV::IntField(i) => Self::from(FullFieldV::IntField(i.clone().recip())), - } - } - - /// Ccompute the multiplicative inverse (panics on 0) - #[track_caller] - #[inline] - pub fn recip(self) -> Self { - match &*self.full_cow() { - FullFieldV::FBls12381(pf) => Self::from(FullFieldV::FBls12381(pf.invert().unwrap())), - FullFieldV::FBn254(pf) => Self::from(FullFieldV::FBn254(pf.invert().unwrap())), - FullFieldV::IntField(i) => Self::from(FullFieldV::IntField(i.clone().recip())), - } - } - - /// Get value as an integer - #[inline] - pub fn i(&self) -> Integer { - self.into() - } - - // /// Get value as i64 - // #[inline] - // pub fn i64(&self) -> i64 { - // self.0 - // } - - /// Test if value is zero - #[inline] - pub fn is_zero(&self) -> bool { - match self.either_ref() { - Ok(InlineFieldV(i, _)) => i == 0, - Err(FullFieldV::FBls12381(pf)) => bool::from(pf.is_zero_vartime()), - Err(FullFieldV::FBn254(pf)) => bool::from(pf.is_zero_vartime()), - Err(FullFieldV::IntField(i)) => i.is_zero(), - } - } - - /// Test if value is 1 - #[inline] - pub fn is_one(&self) -> bool { - use num_traits::One; - match self.either_ref() { - Ok(InlineFieldV(i, _)) => i == 1, - Err(FullFieldV::FBls12381(pf)) => bool::from(pf.is_one()), - Err(FullFieldV::FBn254(pf)) => bool::from(pf.is_one()), - Err(FullFieldV::IntField(i)) => i.i == 1, - } - } - - #[inline] - fn new_ty_long(i: I, ty: FieldT) -> Self - where - Integer: From, - { - // TODO: relax interface. - let i = Integer::from(i); - if i.signed_bits() < 64 - N_TAG_BITS as u32 { - if let Some(t) = ty.inline_tag() { - return Self::from(InlineFieldV(i.to_i64_wrapping(), t)); - } - } - Self::from(match ty { - FieldT::FBls12381 => FullFieldV::FBls12381(FBls12381::from(i)), - FieldT::FBn254 => FullFieldV::FBn254(FBn254::from(i)), - FieldT::IntField(m) => FullFieldV::IntField(IntField::new(i, m)), - }) - } - - #[inline] - fn new_ty(i: I, ty: FieldT) -> Self - where - i64: From, - { - let i: i64 = i.into(); - let bits = if i < 0 { - 65 - i.leading_ones() - } else { - 65 - i.leading_zeros() - }; - if bits < 64 - N_TAG_BITS as u32 { - if let Some(t) = ty.inline_tag() { - return Self::from(InlineFieldV(i, t)); - } - } - Self::from(match ty { - FieldT::FBls12381 => FullFieldV::FBls12381(FBls12381::from(i)), - FieldT::FBn254 => FullFieldV::FBn254(FBn254::from(i)), - FieldT::IntField(m) => FullFieldV::IntField(IntField::new(Integer::from(i), m)), - }) - } - - fn random(ty: FieldT, mut rng: impl rand::RngCore) -> Self { - Self::from(match ty { - FieldT::FBls12381 => FullFieldV::FBls12381(FBls12381::random(rng)), - FieldT::FBn254 => FullFieldV::FBn254(FBn254::random(rng)), - FieldT::IntField(m) => { - let mut rug_rng = rug::rand::RandState::new_mersenne_twister(); - rug_rng.seed(&Integer::from(rng.next_u64())); - let i = Integer::from(m.random_below_ref(&mut rug_rng)); - FullFieldV::IntField(IntField::new(i, m)) - } - }) - } - - /// Convert to a different FieldT --- will fail if moduli are not the same! - #[track_caller] - #[inline] - pub fn as_ty_ref(&self, ty: &FieldT) -> Self { - if &self.ty() == ty { - self.clone() - } else if self.modulus() != ty.modulus() { - panic!( - "Incompatible modulus specified: {:#?} vs {:#?}", - self.ty(), - ty, - ); - } else { - ty.new_v(self.i()) - } - } - - /// Get this field element as an SMT-LIB string. - #[inline] - pub fn as_smtlib(&self) -> String { - format!("#f{}m{}", self.signed_int(), self.ty().modulus()) - } - - /// Get this field element as a signed integer. No particular cut-off is guaranteed. - #[inline] - pub fn signed_int(&self) -> Integer { - let mut i = self.i(); - if i.significant_bits() >= self.ty().modulus().significant_bits() - 1 { - i -= self.ty().modulus(); - } - i - } - - /// Raise this element to a power. - #[inline] - pub fn pow(&self, u: u64) -> Self { - if !self.is_full() - && (self.inline().signed_bits() as u64).saturating_mul(u) < 64 - N_TAG_BITS as u64 - { - let InlineFieldV(i, t) = self.inline(); - let i = i.pow(u as u32); - assert!(InlineFieldV::i64_in_range(i)); - Self::from(InlineFieldV(i, t)) - } else { - self.full_cow().pow(u).into() - } - } -} - -impl Display for FieldV { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{}", self.signed_int()) - } -} - -impl Debug for FieldV { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{} (in {})", self.signed_int(), self.ty()) - } -} - -impl Debug for FullFieldV { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{} (in {})", self.signed_int(), self.ty()) - } -} - -impl DataSize for FieldV { - const IS_DYNAMIC: bool = true; - - const STATIC_HEAP_SIZE: usize = 0; - - #[inline] - fn estimate_heap_size(&self) -> usize { - if self.is_full() { - self.full_ref().estimate_heap_size() + std::mem::size_of::() - } else { - 0 - } - } -} - -macro_rules! arith_impl { - ($Trait: ident, $fn: ident) => { - paste! { - impl $Trait for FieldV { - type Output = Self; - fn $fn(mut self, other: Self) -> Self { - self.[<$fn _assign>](&other); - self - } - } - - impl $Trait<&FieldV> for FieldV { - type Output = Self; - fn $fn(mut self, other: &Self) -> Self { - self.[<$fn _assign>](other); - self - } - } - - impl [<$Trait Assign>] for FieldV { - fn [<$fn _assign>](&mut self, other: FieldV) { - self.[<$fn _assign>](&other); - } - } - - impl [<$Trait Assign>]<&FullFieldV> for FullFieldV { - fn [<$fn _assign>](&mut self, other: &FullFieldV) { - match (self, other) { - (Self::FBls12381(f1), Self::FBls12381(f2)) => f1.[<$fn _assign>](f2), - (Self::FBn254(f1), Self::FBn254(f2)) => f1.[<$fn _assign>](f2), - (Self::IntField(i1), Self::IntField(i2)) => i1.[<$fn _assign>](i2), - (s, o) => panic!("Operation [<$Trait Assign>] on {} and {}", s.ty(), o.ty()), - } - } - } - - impl [<$Trait Assign>] for FullFieldV { - fn [<$fn _assign>](&mut self, other: i64) { - match self { - Self::FBls12381(f1) => f1.[<$fn _assign>](other), - Self::FBn254(f1) => f1.[<$fn _assign>](other), - Self::IntField(f1) => f1.[<$fn _assign>](other), - } - } - } - - impl [<$Trait Assign>] for FullFieldV { - fn [<$fn _assign>](&mut self, InlineFieldV(i, t): InlineFieldV) { - t.assert_matches(self); - self.[<$fn _assign>](i) - } - } - - impl [<$Trait Assign>]<&FieldV> for FieldV { - fn [<$fn _assign>](&mut self, other: &FieldV) { - match (self.is_full(), other.is_full()) { - (false, false) => *self = self.inline().$fn(other.inline()), - (false, true) => self.promote().[<$fn _assign>](other.full_ref()), - (true, false) => self.full_mut().[<$fn _assign>](other.inline()), - (true, true) => self.full_mut().[<$fn _assign>](other.full_ref()), - } - } - } - } - } -} - -arith_impl!(Add, add); -arith_impl!(Mul, mul); -arith_impl!(Sub, sub); - -impl Add for InlineFieldV { - type Output = FieldV; - - fn add(self, rhs: InlineFieldV) -> Self::Output { - assert_eq!(self.1, rhs.1); - let x = self.0.add(rhs.0); - if InlineFieldV::i64_in_range(x) { - FieldV::from(InlineFieldV(x, self.1)) - } else { - let mut this = FullFieldV::from(self); - this.add_assign(rhs.0); - FieldV::from(this) - } - } -} - -impl Sub for InlineFieldV { - type Output = FieldV; - - fn sub(self, rhs: InlineFieldV) -> Self::Output { - assert_eq!(self.1, rhs.1); - let x = self.0.sub(rhs.0); - if InlineFieldV::i64_in_range(x) { - FieldV::from(InlineFieldV(x, self.1)) - } else { - let mut this = FullFieldV::from(self); - this.sub_assign(rhs.0); - FieldV::from(this) - } - } -} - -impl Mul for InlineFieldV { - type Output = FieldV; - - fn mul(self, rhs: InlineFieldV) -> Self::Output { - assert_eq!(self.1, rhs.1); - let x = self.0.checked_mul(rhs.0); - if let Some(x) = x { - if InlineFieldV::i64_in_range(x) { - return FieldV::from(InlineFieldV(x, self.1)); - } - } - let mut this = FullFieldV::from(self); - this.mul_assign(rhs.0); - FieldV::from(this) - } -} - -impl Neg for FieldV { - type Output = Self; - fn neg(mut self) -> Self { - if self.is_full() { - match self.full_mut() { - FullFieldV::FBls12381(pf) => Self::from(FullFieldV::FBls12381(pf.clone().neg())), - FullFieldV::FBn254(pf) => Self::from(FullFieldV::FBn254(pf.clone().neg())), - FullFieldV::IntField(i) => Self::from(FullFieldV::IntField(i.clone().neg())), - } - } else { - let InlineFieldV(i, t) = self.inline(); - let ni = -i; - if InlineFieldV::i64_in_range(ni) { - Self::from(InlineFieldV(ni, t)) - } else { - self.promote(); - self.neg() - } - } - } -} - -#[allow(clippy::from_over_into)] -impl Into for FBls12381 { - fn into(self) -> FullFieldV { - FullFieldV::FBls12381(self) - } -} - -#[allow(clippy::from_over_into)] -impl Into for FBn254 { - fn into(self) -> FullFieldV { - FullFieldV::FBn254(self) - } -} - -#[allow(clippy::from_over_into)] -impl Into for IntField { - fn into(self) -> FullFieldV { - FullFieldV::IntField(self) - } -} - -#[allow(clippy::from_over_into)] -impl Into for FullFieldV { - fn into(self) -> Integer { - match self { - FullFieldV::FBls12381(f) => Integer::from(&f), - FullFieldV::FBn254(f) => Integer::from(&f), - FullFieldV::IntField(i) => i.i, - } - } -} - -#[allow(clippy::from_over_into)] -impl Into for &FullFieldV { - fn into(self) -> Integer { - match self { - FullFieldV::FBls12381(f) => Integer::from(f), - FullFieldV::FBn254(f) => Integer::from(f), - FullFieldV::IntField(i) => i.i.clone(), - } - } -} - -#[allow(clippy::from_over_into)] -impl Into for FieldV { - fn into(self) -> Integer { - Into::into(&self) - } -} - -#[allow(clippy::from_over_into)] -impl Into for &FieldV { - fn into(self) -> Integer { - if self.is_full() { // same as self.tag() == FieldTag::FullField - self.full_ref().into() - } else { - let i = self.inline().0; - if i < 0 { - Integer::from(i) + self.modulus() - } else { - Integer::from(i) - } - } - } -} - -#[cfg(test)] -mod test; diff --git a/forks/Spartan-t256/circ_fields/src/size.rs b/forks/Spartan-t256/circ_fields/src/size.rs deleted file mode 100644 index ce632b1e..00000000 --- a/forks/Spartan-t256/circ_fields/src/size.rs +++ /dev/null @@ -1,9 +0,0 @@ -//! Heap Size helpers for use with [datasize]. - -use rug::Integer; - -/// Measure memory footprint of an integer -pub fn estimate_heap_size_integer(i: &Integer) -> usize { - // a guess - i.capacity() / 8 -} diff --git a/forks/Spartan-t256/circ_fields/src/t256/curves/mod.rs b/forks/Spartan-t256/circ_fields/src/t256/curves/mod.rs deleted file mode 100644 index cfd0b67d..00000000 --- a/forks/Spartan-t256/circ_fields/src/t256/curves/mod.rs +++ /dev/null @@ -1,105 +0,0 @@ -//! This module implements t256 curve -use ark_ec::{ - models::CurveConfig, - short_weierstrass::{self as sw, SWCurveConfig}, -}; - -use super::{fq::Fq, fr::Fr, fr::FrConfig}; -// use crate::{fq::Fq, fr::Fr, fr::FrConfig}; -use ark_secp256r1::Config as secp256r1conf; -use ark_secp256r1::Fq as secp256r1Fq; -use ark_secp256r1::FqConfig as secp256FqConfig; -use ark_secp256r1::Fr as secp256r1Fr; -#[allow(unused_imports)] -// This is actually used in the macro below, but rustfmt seems to -// be unable to deduce that... -use ark_secp256r1::FrConfig as secp256FrConfig; -#[warn(unused_imports)] -// use cdls_macros::derive_conversion; -use crate::derive_conversion; -// use ark_ff::{Field, MontConfig, MontFp}; -// use serde_bytes::ByteArray; - -// #[cfg(test)] -// mod tests; -/// Affine -pub type Affine = sw::Affine; -/// Projective -pub type Projective = sw::Projective; -/// Define ScalarBytes -pub type ScalarField = ::ScalarField; - -/// Compressed form of the generator -pub const BASEPOINT_COMPRESSED: [u8; 33] = [3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; -// pub const BASEPOINT_COMPRESSED: ByteArray<33> = ByteArray::new([3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); - -#[derive(Copy, Clone, Default, PartialEq, Eq)] -/// Config -pub struct Config; - -impl CurveConfig for Config { - type BaseField = Fq; - type ScalarField = Fr; - - // We're dealing with prime order curves. - - /// COFACTOR = 1 - const COFACTOR: &'static [u64] = &[0x1]; - - /// COFACTOR_INV = COFACTOR^{-1} mod r = 1 - const COFACTOR_INV: Fr = Fr::ONE; -} - -impl SWCurveConfig for Config { - /// COEFF_A = a4 in the docs, which is a very large string. - const COEFF_A: Fq = - MontFp!("115792089210356248762697446949407573530594504085698471288169790229257723883796"); - - /// COEFF_B = a6 in the docs, which is a very large string. - const COEFF_B: Fq = - MontFp!("81531206846337786915455327229510804132577517753388365729879493166393691077718"); - - /// GENERATOR = (G_GENERATOR_X, G_GENERATOR_Y) - const GENERATOR: Affine = Affine::new_unchecked(G_GENERATOR_X, G_GENERATOR_Y); - -} - -/// G_GENERATOR_X = 3 -pub const G_GENERATOR_X: Fq = MontFp!("3"); - -/// G_GENERATOR_Y = 40902200210088653215032584946694356296222563095503428277299570638400093548589 -pub const G_GENERATOR_Y: Fq = - MontFp!("40902200210088653215032584946694356296222563095503428277299570638400093548589"); - -/// G_GENERATOR_X2 = 5 -pub const G_GENERATOR_X2: Fq = MontFp!("5"); - -/// G_GENERATOR_Y2 = 28281484859698624956664858566852274012236038028101624500031073655422126514829 -pub const G_GENERATOR_Y2: Fq = - MontFp!("28281484859698624956664858566852274012236038028101624500031073655422126514829"); - -/// The x co-ordinate of the other generator for secp256r1. -pub const G_SECP256_O_X: &str = "5"; - -/// The y co-ordinate of the other generator for secp256r1. -pub const G_SECP256_O_Y: &str = - "31468013646237722594854082025316614106172411895747863909393730389177298123724"; - -// Now we instantiate everything else. -derive_conversion!( - Config, - 4, - 128, - secp256r1conf, - G_GENERATOR_X2, - G_GENERATOR_Y2, - Fr, - FrConfig, - secp256r1Fq, - secp256r1Fr, - secp256FqConfig, - secp256FrConfig, - Affine, - "5", - "31468013646237722594854082025316614106172411895747863909393730389177298123724" -); diff --git a/forks/Spartan-t256/circ_fields/src/t256/curves/tests_todelete.rs b/forks/Spartan-t256/circ_fields/src/t256/curves/tests_todelete.rs deleted file mode 100644 index c6cb30dc..00000000 --- a/forks/Spartan-t256/circ_fields/src/t256/curves/tests_todelete.rs +++ /dev/null @@ -1,12 +0,0 @@ -use crate::{Config, Projective}; -use ark_algebra_test_templates::*; -use ark_ec::short_weierstrass::{self as sw}; -use ark_secp256r1::Config as secp256r1conf; -use cdls_macros::test_pedersen; - -// use std::any::type_name; - -type OtherProject = sw::Projective; - -// test_group!(g1; Projective; sw); -test_pedersen!(tp; Config, OtherProject); diff --git a/forks/Spartan-t256/circ_fields/src/t256/fields/fq.rs b/forks/Spartan-t256/circ_fields/src/t256/fields/fq.rs deleted file mode 100644 index b558220f..00000000 --- a/forks/Spartan-t256/circ_fields/src/t256/fields/fq.rs +++ /dev/null @@ -1,11 +0,0 @@ -#![allow(non_local_definitions)] - -use ark_ff::fields::{Fp256, MontBackend, MontConfig}; - -#[derive(MontConfig)] -#[modulus = "115792089210356248762697446949407573530594504085698471288169790229257723883799"] -#[generator = "3"] -/// FqConfig -pub struct FqConfig; -/// Base field -pub type Fq = Fp256>; diff --git a/forks/Spartan-t256/circ_fields/src/t256/fields/fr.rs b/forks/Spartan-t256/circ_fields/src/t256/fields/fr.rs deleted file mode 100644 index 5a056331..00000000 --- a/forks/Spartan-t256/circ_fields/src/t256/fields/fr.rs +++ /dev/null @@ -1,11 +0,0 @@ -#![allow(non_local_definitions)] - -use ark_ff::fields::{Fp256, MontBackend, MontConfig}; - -#[derive(MontConfig)] -#[modulus = "115792089210356248762697446949407573530086143415290314195533631308867097853951"] -#[generator = "1"] -/// FrConfig -pub struct FrConfig; -/// Scalar field -pub type Fr = Fp256>; \ No newline at end of file diff --git a/forks/Spartan-t256/circ_fields/src/t256/fields/mod.rs b/forks/Spartan-t256/circ_fields/src/t256/fields/mod.rs deleted file mode 100644 index c48aa48b..00000000 --- a/forks/Spartan-t256/circ_fields/src/t256/fields/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -/// Definition of parameters for base field of T256 -pub mod fq; -pub use self::fq::*; -/// Definition of parameters for scalar field of T256 -pub mod fr; -pub use self::fr::*; - -#[cfg(test)] -mod tests; diff --git a/forks/Spartan-t256/circ_fields/src/t256/fields/tests.rs b/forks/Spartan-t256/circ_fields/src/t256/fields/tests.rs deleted file mode 100644 index 1b80d291..00000000 --- a/forks/Spartan-t256/circ_fields/src/t256/fields/tests.rs +++ /dev/null @@ -1,5 +0,0 @@ -// use crate::{Fq, Fr}; -// use ark_algebra_test_templates::*; - -// test_field!(fr; Fr; mont_prime_field); -// test_field!(fq; Fq; mont_prime_field); diff --git a/forks/Spartan-t256/circ_fields/src/t256/hash_to_curve/mod.rs b/forks/Spartan-t256/circ_fields/src/t256/hash_to_curve/mod.rs deleted file mode 100644 index f08276b5..00000000 --- a/forks/Spartan-t256/circ_fields/src/t256/hash_to_curve/mod.rs +++ /dev/null @@ -1,38 +0,0 @@ -//! This module implements hash to curve for t256 -use super::{Config, Projective}; -use ark_ec::hashing::curve_maps::swu::{SWUMap, SWUConfig}; -// use ark_ec::{ -// short_weierstrass::{self as sw, SWCurveConfig}, -// }; -use super::{fq::Fq}; -// use ark_ec::hashing::curve_maps::swu::SWUConfig; -use ark_ec::hashing::{map_to_curve_hasher::{MapToCurveBasedHasher}, - HashToCurve}; -use ark_ff::field_hashers::DefaultFieldHasher; -use sha2::Sha256; -use ark_ff::MontFp; -// use ark_ec::{ -// models::CurveConfig, -// short_weierstrass::{self as sw, SWCurveConfig}, -// }; - - -impl SWUConfig for Config { - /// Define parameters for hash to curve as needed in https://github.com/arkworks-rs/algebra/blob/fc3f6614b4b1aa4303a0204daece19679bea04c5/ec/src/hashing/curve_maps/swu.rs - const ZETA: Fq = MontFp!("115792089210356248762697446949407573530594504085698471288169790229257723883798"); // -1 -} - -/// Create a hasher for hash to curve -pub fn create_curvebased_hasher(domain: &[u8]) -> MapToCurveBasedHasher::< - Projective, - DefaultFieldHasher, - SWUMap - > -{ - let hasher = MapToCurveBasedHasher::< - Projective, - DefaultFieldHasher, - SWUMap - >::new(domain).unwrap(); - hasher -} \ No newline at end of file diff --git a/forks/Spartan-t256/circ_fields/src/t256/macros/derive_conversion.rs b/forks/Spartan-t256/circ_fields/src/t256/macros/derive_conversion.rs deleted file mode 100644 index 18a3ccce..00000000 --- a/forks/Spartan-t256/circ_fields/src/t256/macros/derive_conversion.rs +++ /dev/null @@ -1,176 +0,0 @@ -#[macro_export] -#[doc(hidden)] -macro_rules! __derive_conversion { - ($config: ty, $dim: expr, $sec_param: expr, $OtherCurve: ty, $G2_X: ident, $G2_Y: ident, $fr: ty, $fr_config: ty, $other_q: ty, $other_r: ty, $other_q_conf: ty, $other_r_conf: ty, $affine: ty, $GSX: expr, $GSY: expr) => { - // Define the conversion functions for this particular - // mapping. - type OtherBaseField = <$OtherCurve as CurveConfig>::BaseField; - type OtherScalarField = <$OtherCurve as CurveConfig>::ScalarField; - - // macro_rules! StrToOtherFq { - // ($c0:expr) => {{ - // let (is_positive, limbs) = ark_ff_macros::to_sign_and_limbs!($c0); - // <$other_q>::from_sign_and_limbs(is_positive, &limbs) - // }}; - // } - - // macro_rules! StrToFr { - // ($c0:expr) => {{ - // let (is_positive, limbs) = ark_ff_macros::to_sign_and_limbs!($c0); - // <$fr>::from_sign_and_limbs(is_positive, &limbs) - // }}; - // } - - struct FrStruct($fr); - impl FrStruct { - pub fn new(x: $fr) -> FrStruct { - FrStruct(x) - } - - // pub fn as_fr(&self) -> $fr { - // self.0 - // } - } - - impl From> for FrStruct { - fn from(x: BigInt<$dim>) -> Self { - let x_t = <$fr_config>::from_bigint(x).unwrap(); - FrStruct::new(x_t) - } - } - - impl From for BigInt<$dim> { - fn from(val: FrStruct) -> Self { - FrConfig::into_bigint(val.0) - } - } - - struct OtherBase(OtherBaseField); - impl OtherBase { - pub fn new(x: $other_q) -> OtherBase { - OtherBase(x) - } - } - - impl From for BigInt<$dim> { - fn from(x: OtherBase) -> Self { - <$other_q_conf>::into_bigint(x.0) - } - } - - impl From> for OtherBase { - fn from(x: BigInt<$dim>) -> OtherBase { - let x_t = <$other_q_conf>::from_bigint(x).unwrap(); - OtherBase::new(x_t) - } - } - - struct OtherScalar(OtherScalarField); - impl OtherScalar { - pub fn new(x: $other_r) -> OtherScalar { - OtherScalar(x) - } - - // pub fn as_fr(&self) -> OtherScalarField { - // self.0 - // } - } - - impl From for BigInt<$dim> { - fn from(x: OtherScalar) -> Self { - <$other_r_conf>::into_bigint(x.0) - } - } - - impl From> for OtherScalar { - fn from(x: BigInt<$dim>) -> OtherScalar { - let x_t = <$other_r_conf>::from_bigint(x).unwrap(); - OtherScalar::new(x_t) - } - } - - // // Define the Pedersen commitment type. - // impl PedersenConfig for $config { - // type OCurve = $OtherCurve; - - // const SECPARAM: usize = $sec_param; - - // /// GENERATOR2 = (G2_X, G2_Y) - // const GENERATOR2: $affine = <$affine>::new_unchecked($G2_X, $G2_Y); - - // fn from_ob_to_sf(x: OtherBaseField) -> <$config as CurveConfig>::ScalarField { - // let x_t: BigInt<$dim> = x.into(); - // let x_v: FrStruct = FrStruct::from(x_t); - // x_v.as_fr() - // } - - // fn from_ob_to_os(x: OtherBaseField) -> ::ScalarField { - // let x_t: BigInt<$dim> = x.into(); - // OtherScalar::from(x_t).as_fr() - // } - - // fn from_os_to_sf(x: OtherScalarField) -> <$config as CurveConfig>::ScalarField { - // let x_t: BigInt<$dim> = x.into(); - // let x_v: FrStruct = FrStruct::from(x_t); - // x_v.as_fr() - // } - - // fn from_bf_to_sf( - // x: ::BaseField, - // ) -> ::ScalarField { - // let x_t: BigInt<$dim> = x.into(); - // let x_v: FrStruct = FrStruct::from(x_t); - // x_v.as_fr() - // } - - // fn make_single_bit_challenge(v: u8) -> <$config as CurveConfig>::ScalarField { - // match v { - // 0 => Self::CM1, - // 1 => Self::CP1, - // _ => panic!("Invalid bit in make_single_bit_challenge {}", v), - // } - // } - - // fn from_u64_to_sf(x: u64) -> ::ScalarField { - // let x_t = BigInt::<$dim>::from(x); - // let x_v = FrStruct::from(x_t); - // x_v.as_fr() - // } - - // const OGENERATOR2: sw::Affine = - // sw::Affine::::new_unchecked(StrToOtherFq!($GSX), StrToOtherFq!($GSY)); - - // const CM1: Self::ScalarField = StrToFr!("-1"); - // const CP1: Self::ScalarField = StrToFr!("1"); - // } - }; -} - -#[macro_export] -/// derive_conversion -macro_rules! derive_conversion { - ($config: ty, $dim: expr, $sec_param: expr, $OtherCurve: ty, $G2_X: ident, $G2_Y: ident, $fr: ty, $fr_config: ty, $other_q: ty, $other_r: ty, $other_q_conf: ty, $other_r_conf: ty, $affine: ty, $GSX: expr, $GSY: expr) => { - use ark_ff::BigInt; - use ark_ff::{Field, MontConfig, MontFp}; - // use ark_ff_macros::to_sign_and_limbs; - // use pedersen::pedersen_config::PedersenConfig; - - $crate::__derive_conversion!( - $config, - $dim, - $sec_param, - $OtherCurve, - $G2_X, - $G2_Y, - $fr, - $fr_config, - $other_q, - $other_r, - $other_q_conf, - $other_r_conf, - $affine, - $GSX, - $GSY - ); - }; -} diff --git a/forks/Spartan-t256/circ_fields/src/t256/macros/mod.rs b/forks/Spartan-t256/circ_fields/src/t256/macros/mod.rs deleted file mode 100644 index a19ff5a2..00000000 --- a/forks/Spartan-t256/circ_fields/src/t256/macros/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -#[macro_use] -pub mod derive_conversion; -// pub use self::derive_conversion::*; diff --git a/forks/Spartan-t256/circ_fields/src/t256/mod.rs b/forks/Spartan-t256/circ_fields/src/t256/mod.rs deleted file mode 100644 index 3fd67f32..00000000 --- a/forks/Spartan-t256/circ_fields/src/t256/mod.rs +++ /dev/null @@ -1,34 +0,0 @@ -#![forbid(unsafe_code)] -//! From https://github.com/brave-experiments/CDLS/blob/main/t256/src/lib.rs -//! This library implements the 256-bit prime order curve used inside ZKAttest. -//! Note: we use the values here from the ZKAttest implementation: -//! https://github.com/cloudflare/zkp-ecdsa/blob/0af748d23b535a8fffebca34ab51abf37ef1ea13/src/curves/instances.ts#L34, -//! which are different from those that are given in the paper. -//! -//! Curve infomration: -//! * Base field: q = 0xffffffff0000000100000000000000017e72b42b30e7317793135661b1c4b117 -//! * Scalar field: r = 0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff -//! -//! Note that by "base field" we mean "the characteristic of the underlying finite field" and by "scalar field" we mean -//! "the order of the curve". -//! -//! * Curve equation: y^2 = x^3 + a_4*x + a_6, where -//! a_4 = -3 -//! a_6 = 0xb441071b12f4a0366fb552f8e21ed4ac36b06aceeb354224863e60f20219fc56 -//! -//! Or, in decimal, a_4 = -3 -//! a_6 = 81531206846337786915455327229510804132577517753388365729879493166393691077718 - -//#[cfg(feature = "r1cs")] -///// constraints -//pub mod constraints; -pub mod curves; -pub mod hash_to_curve; -mod fields; -mod macros; -/// utils -pub mod utils; - -pub use curves::*; -pub use hash_to_curve::*; -pub use fields::*; diff --git a/forks/Spartan-t256/circ_fields/src/t256/utils/helper.rs b/forks/Spartan-t256/circ_fields/src/t256/utils/helper.rs deleted file mode 100644 index b96c3a0a..00000000 --- a/forks/Spartan-t256/circ_fields/src/t256/utils/helper.rs +++ /dev/null @@ -1,36 +0,0 @@ -// use crate::Config; -use crate::t256::Config; -use ark_ec::{models::CurveConfig}; -use ark_serialize::CanonicalSerialize; -// type SF = ::ScalarField; // scalar field of T256 - -/// Trait for Spartan -pub trait SpartanTrait { - /// Convert to bytes - fn to_bytes(&self) -> [u8; 32]; - // /// Create a zero scalar - // fn zero() -> Self; -} - -impl SpartanTrait for ::ScalarField { - // /// Convert Scalar to bytes - // fn to_bytes(&self) -> [u8; 32] { - // let mut compressed_bytes: Vec = Vec::new(); - // self.serialize_compressed(&mut compressed_bytes).unwrap(); - // assert!(compressed_bytes.len() == 32); - // let mut array_bytes = [0u8; 32]; - // array_bytes.copy_from_slice(&compressed_bytes); - // array_bytes - // } - - /// Convert Scalar to bytes - fn to_bytes(&self) -> [u8; 32] { - let mut array_bytes = [0u8; 32]; - self.serialize_compressed(&mut &mut array_bytes[..]).unwrap(); - array_bytes - } - // /// Create a zero scalar - // fn zero() -> Self { - // Self::from(0) - // } -} \ No newline at end of file diff --git a/forks/Spartan-t256/circ_fields/src/t256/utils/mod.rs b/forks/Spartan-t256/circ_fields/src/t256/utils/mod.rs deleted file mode 100644 index 1f7e19c9..00000000 --- a/forks/Spartan-t256/circ_fields/src/t256/utils/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -/// Helper function for using t256 in spartan -pub mod helper; - - diff --git a/forks/Spartan-t256/circ_fields/src/test.rs b/forks/Spartan-t256/circ_fields/src/test.rs deleted file mode 100644 index 76b2756c..00000000 --- a/forks/Spartan-t256/circ_fields/src/test.rs +++ /dev/null @@ -1,123 +0,0 @@ -use super::*; -use rand::thread_rng; -use rand::Rng; -use rug::ops::RemRounding; - -#[test] -fn inline_signed_bits() { - assert_eq!(InlineFieldV(-2, InlineFieldTag::Bls12381).signed_bits(), 2); - assert_eq!(InlineFieldV(1, InlineFieldTag::Bls12381).signed_bits(), 2); - assert_eq!( - InlineFieldV(i64::MAX, InlineFieldTag::Bls12381).signed_bits(), - 64 - ); - assert_eq!( - InlineFieldV(i64::MIN, InlineFieldTag::Bls12381).signed_bits(), - 64 - ); - assert_eq!( - InlineFieldV(i64::MAX / 2, InlineFieldTag::Bls12381).signed_bits(), - 63 - ); - assert_eq!( - InlineFieldV(i64::MIN / 2, InlineFieldTag::Bls12381).signed_bits(), - 63 - ); - assert_eq!( - InlineFieldV(i64::MAX / 2 + 1, InlineFieldTag::Bls12381).signed_bits(), - 64 - ); - assert_eq!( - InlineFieldV(i64::MIN / 2 - 1, InlineFieldTag::Bls12381).signed_bits(), - 64 - ); -} -use rug::Integer; - -#[test] -fn inline_signed_bits_randomized() { - let mut rng = thread_rng(); - for _ in 0..1024 { - let i: i64 = rng.gen(); - let n_bits = rng.gen_range(0..64); - let i = i % (1 << n_bits); - let big_i = Integer::from(i); - assert_eq!( - InlineFieldV(i, InlineFieldTag::Bls12381).signed_bits(), - big_i.signed_bits(), - "wrong answer on {:b}", - i - ) - } -} - -/// Samples a random integer with up to `max_bits` bits. -/// -/// A number with `i` is chosen with probability proportional to `2^-i`. -fn random_rug_int_exp(rng: &mut impl Rng, max_bits: u32) -> Integer { - let num_bits = rng.gen_range(1u32..max_bits); - let mut rug_rng = rug::rand::RandState::new_mersenne_twister(); - rug_rng.seed(&Integer::from(rng.next_u64())); - Integer::from(Integer::random_bits(num_bits, &mut rug_rng)) -} - -/// Sample a [FieldT] that is: -/// * Bls w/ p = 0.25 -/// * Bn w/ p = 0.25 -/// * An integer field otherwise -/// * with a number of bits sampled uniformly between 1..max_bits -fn sample_field_t(r: &mut impl Rng, max_bits: u32) -> FieldT { - if r.gen_bool(0.5) { - if r.gen_bool(0.5) { - FieldT::FBls12381 - } else { - FieldT::FBn254 - } - } else { - FieldT::IntField(Arc::new(random_rug_int_exp(r, max_bits).next_prime())) - } -} - -/// Sample a [FieldV]. If `ty` is inlinable, the value will be inline with probability 0.5. -fn sample_field_v(ty: &FieldT, r: &mut impl Rng) -> FieldV { - if let Some(t) = ty.inline_tag() { - if r.gen_bool(0.5) { - let num_bits = r.gen_range(0..62); - let i: i64 = r.gen(); - return FieldV::from(InlineFieldV(i % (1 << num_bits as i64), t)); - } - } - ty.new_v(random_rug_int_exp(r, ty.modulus().significant_bits())) -} - -#[test] -fn random() { - let mut rng = thread_rng(); - for _ in 0..1024 { - let f = sample_field_t(&mut rng, 256); - let a = sample_field_v(&f, &mut rng); - let b = sample_field_v(&f, &mut rng); - let a_i = a.i(); - let b_i = b.i(); - - // add - let c = a.clone() + &b; - let c_i = (a_i.clone() + &b_i).rem_floor(f.modulus()); - assert_eq!(c.i(), c_i); - - // sub - let c = a.clone() - &b; - let c_i = (a_i.clone() - &b_i).rem_floor(f.modulus()); - assert_eq!(c.i(), c_i); - - // mul - let c = a.clone() * &b; - let c_i = (a_i.clone() * &b_i).rem_floor(f.modulus()); - assert_eq!(c.i(), c_i); - - // neg - let c = -a.clone(); - let c_i = (-a_i.clone()).rem_floor(f.modulus()); - assert_eq!(c.i(), c_i); - } -} diff --git a/forks/Spartan-t256/src/bellpepper/mod.rs b/forks/Spartan-t256/src/bellpepper/mod.rs index d46dee51..c28dd87a 100644 --- a/forks/Spartan-t256/src/bellpepper/mod.rs +++ b/forks/Spartan-t256/src/bellpepper/mod.rs @@ -24,7 +24,8 @@ mod tests { use merlin::Transcript; use rand::random; - type F = circ_fields::ff_field::f_t256::Ft; + //type F = circ_fields::ff_field::f_t256::Ft; + type F = halo2curves::t256::Fq; fn synthesize_alloc_bit>( cs: &mut CS, @@ -53,7 +54,8 @@ mod tests { #[test] fn test_alloc_bit() { - type F = circ_fields::ff_field::f_t256::Ft; + //type F = circ_fields::ff_field::f_t256::Ft; + type F = halo2curves::t256::Fq; // First create the shape let mut cs = ShapeCS::::new(); let _ = synthesize_alloc_bit(&mut cs); diff --git a/forks/Spartan-t256/src/commitments.rs b/forks/Spartan-t256/src/commitments.rs index 6feb812d..3355963d 100644 --- a/forks/Spartan-t256/src/commitments.rs +++ b/forks/Spartan-t256/src/commitments.rs @@ -1,13 +1,11 @@ -use super::group::{GroupElement, VartimeMultiscalarMul};//, GROUP_BASEPOINT_COMPRESSED}; +use super::group::{GroupElement, VartimeMultiscalarMul}; use super::scalar::Scalar; use digest::XofReader; use digest::{ExtendableOutput, Input}; +use halo2curves::t256::T256; +use halo2curves::CurveExt; use sha3::Shake256; - -use circ_fields::t256::hash_to_curve::create_curvebased_hasher; -use circ_fields::t256::curves::BASEPOINT_COMPRESSED; -use ark_ec::hashing::HashToCurve; -// use circ_fields::t256::Projective; +use crate::group::{GROUP_BASEPOINT_COMPRESSED, AsBytesDev}; use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize)] @@ -21,17 +19,18 @@ impl MultiCommitGens { pub fn new(n: usize, label: &[u8]) -> Self { let mut shake = Shake256::default(); shake.input(label); - // shake.input(GROUP_BASEPOINT_COMPRESSED); - shake.input(BASEPOINT_COMPRESSED); + shake.input(GROUP_BASEPOINT_COMPRESSED.as_bytes()); let mut reader = shake.xof_result(); let mut gens: Vec = Vec::new(); let mut uniform_bytes = [0u8; 64]; - let hasher = create_curvebased_hasher(&[]); + + let hasher = T256::hash_to_curve("domain_prefix"); + for _ in 0..n + 1 { reader.read(&mut uniform_bytes); - let result_affine = hasher.hash(&uniform_bytes).unwrap(); - gens.push(GroupElement::from_affine(result_affine)); + let point = hasher(&uniform_bytes); + gens.push(GroupElement(point)); } MultiCommitGens { diff --git a/forks/Spartan-t256/src/dense_mlpoly.rs b/forks/Spartan-t256/src/dense_mlpoly.rs index c4895fb6..7573ae33 100644 --- a/forks/Spartan-t256/src/dense_mlpoly.rs +++ b/forks/Spartan-t256/src/dense_mlpoly.rs @@ -407,7 +407,8 @@ impl PolyEvalProof { mod tests { use super::super::scalar::ScalarFromPrimitives; use super::*; - use rand::rngs::OsRng; + use rand_core::OsRng; + use ff::Field; fn evaluate_with_LR(Z: &[Scalar], r: &[Scalar]) -> Scalar { let eq = EqPolynomial::new(r.to_vec()); diff --git a/forks/Spartan-t256/src/group.rs b/forks/Spartan-t256/src/group.rs index 8eccecee..b6b0483e 100644 --- a/forks/Spartan-t256/src/group.rs +++ b/forks/Spartan-t256/src/group.rs @@ -1,25 +1,24 @@ use super::errors::ProofVerifyError; -use super::scalar::{Scalar, ScalarBytesFromScalar}; +use super::scalar::Scalar; use core::borrow::Borrow; use core::ops::{Mul, MulAssign, Add, Sub}; -use circ_fields::t256::{Affine, Projective}; -use ark_serialize::CanonicalDeserialize; -use ark_serialize::CanonicalSerialize; -use ark_ec::{Group, CurveGroup}; +use halo2curves::serde::Repr; +use halo2curves::t256::{T256Affine as Affine, T256 as Projective}; +use halo2curves::group::{Curve, GroupEncoding}; use serde_bytes::ByteArray; - use lazy_static::lazy_static; use serde::{Deserialize, Serialize, Serializer, Deserializer}; +use crate::scalar::ScalarBytesFromScalar; -#[derive(Copy, Clone,Debug,PartialEq)] // , Serialize, Deserialize -pub struct GroupElement(circ_fields::t256::Projective); // curve25519_dalek::ristretto::RistrettoPoint; -pub type GroupElementOri = circ_fields::t256::Projective; -pub type CompressedGroup = ByteArray<33>;// [u8; 33]; // curve25519_dalek::ristretto::CompressedRistretto; +#[derive(Copy, Clone,Debug,PartialEq)] +pub struct GroupElement(pub Projective); +pub type GroupElementOri = Projective; +pub type CompressedGroup = ByteArray<33>; lazy_static! { /// Compressed form of the generator - pub static ref GROUP_BASEPOINT_COMPRESSED: CompressedGroup = CompressedGroup::new(circ_fields::t256::curves::BASEPOINT_COMPRESSED); + pub static ref GROUP_BASEPOINT_COMPRESSED: CompressedGroup = CompressedGroup::new(Affine::generator().to_bytes().into()); } // Define an extension trait that offers the as_bytes functionality @@ -29,7 +28,6 @@ pub trait AsBytesDev { impl AsBytesDev for CompressedGroup { fn as_bytes(&self) -> &[u8] { - // &self.into_array() &self[..] } } @@ -45,10 +43,7 @@ impl GroupElement { GroupElement(Projective::from(point)) } pub fn compress(&self) -> CompressedGroup { - let mut compressed_bytes = Vec::new(); - // let mut compressed_bytes = [0u8; 33]; - self.0.serialize_compressed(&mut compressed_bytes).unwrap(); - CompressedGroup::new(compressed_bytes.try_into().unwrap()) //.into_array()) + CompressedGroup::new(self.0.to_bytes().into()) } } @@ -57,9 +52,7 @@ impl Serialize for GroupElement { where S: Serializer, { - let mut bytes = Vec::new(); - self.0.serialize_compressed(&mut bytes).unwrap(); - bytes.serialize(serializer) + self.0.to_bytes().as_ref().serialize(serializer) } } @@ -69,54 +62,31 @@ impl<'de> Deserialize<'de> for GroupElement { // ** to do D: Deserializer<'de>, { let bytes = Vec::::deserialize(deserializer)?; - let point = Projective::deserialize_compressed(&bytes[..]) - .map_err(|e| serde::de::Error::custom(format!("Deserialization error: {:?}", e)))?; - Ok(GroupElement(point)) + let point = Projective::from_bytes(&Repr::from(bytes.as_slice())).into_option().map(GroupElement).ok_or_else(||serde::de::Error::custom("Deserialization error 1"))?; + Ok(point) } } pub trait CompressedGroupExt { type Group; fn unpack(&self) -> Result; - // fn decompress(&self) -> Self::Group; fn decompress(&self) -> Option; } impl CompressedGroupExt for CompressedGroup { - // type Group = circ_fields::t256::Projective; // curve25519_dalek::ristretto::RistrettoPoint; - type Group = GroupElement; // curve25519_dalek::ristretto::RistrettoPoint; + type Group = GroupElement; fn unpack(&self) -> Result { - let result = Projective::deserialize_compressed(&self[..]); - // let result = Self::Group::deserialize_compressed(&self[..]); - if result.is_ok() { - Ok(GroupElement(result.unwrap())) - } else { - Err(ProofVerifyError::DecompressionError(self[1..].try_into().unwrap())) - // let last_32_bytes = &self[1..]; - // let last_32_bytes_array: [u8; 32] = last_32_bytes.try_into() - // .expect("slice with incorrect length"); - // Err(ProofVerifyError::DecompressionError(last_32_bytes.into_array())) - } - // Self::Group::deserialize_compressed(&self[..]).ok_or_else(|| ProofVerifyError::DecompressionError(last_32_bytes_array)) - // self - // .decompress() - // .ok_or_else(|| ProofVerifyError::DecompressionError(self.to_bytes())) + Projective::from_bytes(&Repr::from(self.into_array())).into_option().map(GroupElement).ok_or_else(||ProofVerifyError::DecompressionError([2;32])) } #[inline] fn decompress(&self) -> Option { - // Projective::deserialize_compressed(&self[..]) - // .map_or_else(|_| None, |result| Some(GroupElement(result))) - // Projective::deserialize_compressed(&self[..]) - // .ok().map(|r| GroupElement(r)) - let result = Projective::deserialize_compressed(&self[..]); - // result.ok().map(|r| Self::Group(r)) - result.ok().map(|r| GroupElement(r)) + let result = Projective::from_bytes(&Repr::from(self.into_array())); + result.into_option().map(|r| GroupElement(r)) } } - -impl<'b> MulAssign<&'b Scalar> for GroupElement { // to do: modify Scalar to that it can fit original MulAssign +impl<'b> MulAssign<&'b Scalar> for GroupElement { fn mul_assign(&mut self, scalar: &'b Scalar) { let point = (self as &GroupElement).into(); let result = point * Scalar::decompress_scalar(scalar); @@ -127,7 +97,7 @@ impl<'b> MulAssign<&'b Scalar> for GroupElement { // to do: modify Scalar to tha impl<'a, 'b> Mul<&'b Scalar> for &'a GroupElement { type Output = GroupElement; fn mul(self, scalar: &'b Scalar) -> GroupElement { - GroupElement(self.into() * Scalar::decompress_scalar(scalar)) // right is of type ScalarBytes + GroupElement(self.into() * Scalar::decompress_scalar(scalar)) } } @@ -265,11 +235,7 @@ pub trait VartimeMultiscalarMul { impl VartimeMultiscalarMul for GroupElement { type Scalar = super::scalar::Scalar; - // TODO Borrow the arguments so we don't have to clone them, as it was in the original implementation - // fn vartime_multiscalar_mul(scalars: Vec, points: Vec) -> Self { - // } - // #[inline] // ** to test improve 1ms fn vartime_multiscalar_mul(scalars: I, points: J) -> Self // to do: use msm instead where I: IntoIterator, @@ -278,31 +244,23 @@ impl VartimeMultiscalarMul for GroupElement { J::Item: Borrow, Self: Clone, { - use ark_ec::VariableBaseMSM; - use ark_ff::biginteger::BigInteger256; - let points_affine: Vec = points - .into_iter() - .map(|p| p.borrow().into().into_affine()) - .collect::>(); - let result = GroupElementOri::msm_bigint( - &points_affine, - // &points - // .into_iter() - // .map(|p| p.borrow().into().into_affine()) - // .collect::>(), - &scalars - .into_iter() - .map(|s| s.borrow().to_bigint()) - .collect::>(), - ); - - // let result = GroupElementOri::msm( - // &points_affine, - // &scalars - // .into_iter() - // .map(|s| Scalar::decompress_scalar(s.borrow())) - // .collect::>(), - // ).unwrap(); + use halo2curves::msm::msm_best; + use halo2curves::group::prime::PrimeCurveAffine; + + let points_projective: Vec = points + .into_iter() + .map(|p| p.borrow().0) + .collect(); + let mut points_affine = vec![Affine::identity(); points_projective.len()]; + Projective::batch_normalize(&points_projective, &mut points_affine); + + let scalars_bigint: Vec<_> = scalars + .into_iter() + .map(|s| Scalar::decompress_scalar(s.borrow())) + .collect(); + + let result = msm_best::(&scalars_bigint, &points_affine); + GroupElement(result) } } @@ -310,7 +268,7 @@ impl VartimeMultiscalarMul for GroupElement { #[cfg(test)] mod tests { use super::*; - // use ark_ff::fields::PrimeField; + #[test] fn msm() { // https://github.com/personaelabs/spartan-ecdsa/blob/main/packages/Spartan-secq/src/group.rs for i in 0..5000 { diff --git a/forks/Spartan-t256/src/group25519.rs b/forks/Spartan-t256/src/group25519.rs deleted file mode 100644 index 7cb920bf..00000000 --- a/forks/Spartan-t256/src/group25519.rs +++ /dev/null @@ -1,2603 +0,0 @@ -//! This file implements a group whose order is q = 2^255 - 19. It is -//! internally backed by an elliptic curve of order 2*q. All operations -//! use complete formulas and are constant-time, unless specified explicitly -//! otherwise. - -// Projective/fractional coordinates traditionally use uppercase letters, -// using lowercase only for affine coordinates. -#![allow(non_snake_case)] -#![allow(non_upper_case_globals)] - -use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use core::borrow::Borrow; -use crrl::field::ModInt256; - -// Tentative API expected by the rest of the code. -// pub type GroupElement = Point; -// pub type CompressedGroup = [u8; 32]; - -// Modulus is p = 2^256 - 451024951810263391379330922557034374877 -type GFp = ModInt256<0x20CD9255FD615923u64, 0xACAFC103CD968A25u64, - 0xFFFFFFFFFFFFFFFEu64, 0xFFFFFFFFFFFFFFFFu64>; - -/// A curve point (group element). -#[derive(Clone, Copy, Debug)] -pub struct Point { - // Field is GF(p) with: - // p = 2^256 - 451024951810263391379330922557034374877 - // (p is prime; note that p = 3 mod 4). - // - // Curve equation is y^2 = x*(x^2 + a*x + b) for two specific constants: - // a = 0x83d55b3ef1207cbb74ada704e61adf4dabaed20eae494cc45293fdcefdd1183d - // b = 0x341b58146036cb9911638f4cf4ac3bed671e867f1b14831c1af9cd915591b64c - // We use extended coordinates on the Jacobi quartic curve with - // equation: - // e^2 = (a^2 - 4*b)*u^4 - 2*a*u^2 + 1 - // The map from the base curve is defined as: - // u = x/y - // e = u^2*(x - b/x) - // The curve constants have been chosen so that a^2 - 4*b = -1. - // For the point (0,0) (the neutral in the group, which is the - // unique point of order 2 on the curve), we set u = 0 and e = -1. - // From the curve equation, e = (x^2 - b)/(x^2 + a*x + b), so that - // it is always defined and non-zero, and e = -1 for x = 0; as for u, - // it is the inverse of the slope of the line from (0,0) to the point, - // so the extreme case for (0,0) itself is a vertical tangent, which - // is why we use u = 0. Since addition of (0,0) on the curve becomes - // on the quartic the transform (e,u) -> (-e,-u), we can also map - // the point-at-infinity of the initial curve into (1,0) on the quartic. - // - // In extended coordinates, we have: - // Z != 0 and E != 0 for all points - // e = E/Z - // u = U/Z - // u^2 = T/Z (hence U^2 = T*Z) - E: GFp, - Z: GFp, - U: GFp, - T: GFp, -} - -// The whole curve has order 2*q, with q = 2^255 - 19. The implementation -// defines a group of order q, which is really the quotient of the curve -// by the subgroup {inf,N} (with inf = point-at-infinity and N = unique -// point of order 2). Scalars are integers modulo q. -pub type Scalar = crrl::field::GF25519; - -impl Point { - - /// The group neutral element. - pub const NEUTRAL: Self = Self { - E: GFp::MINUS_ONE, - Z: GFp::ONE, - U: GFp::ZERO, - T: GFp::ZERO, - }; - - /// The curve `a` constant. - const A: GFp = GFp::w64be( - 0x83D55B3EF1207CBB, 0x74ADA704E61ADF4D, - 0xABAED20EAE494CC4, 0x5293FDCEFDD1183D); - - /// a' = -2*a - const Ap: GFp = GFp::w64be( - 0xF85549821DBF0689, 0x16A4B1F633CA4162, - 0x0201DDEA3E9A7AC1, 0x9C73290DFF2081CC); - - /// The curve `b` constant. - const B: GFp = GFp::w64be( - 0x341B58146036CB99, 0x11638F4CF4AC3BED, - 0x671E867F1B14831C, 0x1AF9CD915591B64C); - - /// 16*b - const Bx16: GFp = GFp::w64be( - 0x41B58146036CB991, 0x1638F4CF4AC3BEDA, - 0x6BD924E648849352, 0x4D34221360F75957); - - /// a^2 - const AA: GFp = GFp::w64be( - 0xD06D605180DB2E64, 0x458E3D33D2B0EFB5, - 0x9C7A19FC6C520C70, 0x6BE736455646D92F); - - /* unused - /// b' = a^2 - 4*b - const Bp: GFp = GFp::MINUS_ONE; - */ - - /// The conventional base point (group generator). - /// - /// This point generates the whole group, which as prime order r - /// (integers modulo r are represented by the `Scalar` type). - pub const BASE: Self = Self { - // We use the point with u = 2 (smallest non-zero u that maps to - // a valid group element). - E: GFp::w64be( - 0x80EECF52DB5F5A44, 0xF50CA81BDD9B2756, - 0x1B9488E2D8D6D550, 0x8E570EABE5552C04), - Z: GFp::ONE, - U: GFp::w64be(0, 0, 0, 2), - T: GFp::w64be(0, 0, 0, 4), - }; - - /// Tries to decode a point from bytes. - /// - /// If the source slice has not length exactly 32 bytes, then - /// decoding fails. If the source bytes are not a valid, canonical - /// encoding of a group element, then decoding fails. On success, - /// 0xFFFFFFFF is returned; on failure, 0x00000000 is returned. On - /// failure, this point is set to the neutral. - /// - /// If the source length is exactly 32 bytes, then the decoding - /// outcome (success or failure) should remain hidden from - /// timing-based side channels. - pub fn set_decode(&mut self, buf: &[u8]) -> u32 { - // Check that the input length is correct. - if buf.len() != 32 { - *self = Self::NEUTRAL; - return 0; - } - - // Decode the u coordinate. - let (u, mut r) = GFp::decode32(buf); - - // e^2 = (a^2-4*b)*u^4 - 2*a*u^2 + 1 - let uu = u.square(); - let ee = -uu.square() + uu * Self::Ap + GFp::ONE; - let (e, r2) = ee.sqrt(); - r &= r2; - // GFp::sqrt() already returns the non-negative root, we do - // not have to test the sign of e and adjust. - - // We have the point in affine coordinates, except on failure, - // in which case we have to adjust the values. - self.E = GFp::select(&GFp::MINUS_ONE, &e, r); - self.Z = GFp::ONE; - self.U = GFp::select(&GFp::ZERO, &u, r); - self.T = GFp::select(&GFp::ZERO, &uu, r); - r - } - - /// Tries to decode a point from some bytes. - /// - /// Decoding succeeds only if the source slice has length exactly 32 - /// bytes, and contains the canonical encoding of a valid curve - /// point. Sicne this method returns an `Option`, it - /// inherently leaks (through timing-based side channels) whether - /// decoding succeeded or not; to avoid that, consider using - /// `set_decode()`. The decoded point itself, however, does not leak. - pub fn decode(buf: &[u8]) -> Option { - let mut P = Point::NEUTRAL; - if P.set_decode(buf) != 0 { - Some(P) - } else { - None - } - } - - /// Encodes this point into exactly 32 bytes. - /// - /// Encoding is always canonical. - pub fn encode(self) -> [u8; 32] { - // - Choose the element representant whose e coordinate is - // non-negative. - // - Encode the u coordinate of that point. - let C = GFp::ONE / self.Z; - let mut u = self.U * C; - let sgn = (((self.E * C).encode32()[0] & 1) as u32).wrapping_neg(); - u.set_cond(&-u, sgn); - u.encode32() - } - - /// Creates a point by converting a point in extended affine - /// coordinates (e, u, u^2). - fn from_affine_extended(P: &PointAffineExtended) -> Self { - Self { - E: P.e, - Z: GFp::ONE, - U: P.u, - T: P.t, - } - } - - /// Adds another point (`rhs`) to this point. - fn set_add(&mut self, rhs: &Self) { - let (E1, Z1, U1, T1) = (&self.E, &self.Z, &self.U, &self.T); - let (E2, Z2, U2, T2) = (&rhs.E, &rhs.Z, &rhs.U, &rhs.T); - - // Generic case (8M+3S): - // constants on the dual curve: - // a' = -2*a - // b' = a^2 - 4*b (here: b' = -1) - // e1e2 = E1*E2 - // z1z2 = Z1*Z2 - // u1u2 = U1*U2 - // t1t2 = T1*T2 - // zt = (Z1 + T1)*(Z2 + T2) - z1z2 - t1t2 - // eu = (E1 + U1)*(E2 + U2) - e1e2 - u1u2 - // hd = z1z2 - b'*t1t2 - // E3 = (z1z2 + b'*t1t2)*(e1e2 + a'*u1u2) + 2*b'*u1u2*zt - // Z3 = hd^2 - // T3 = eu^2 - // U3 = ((hd + eu)^2 - Z3 - T3)/2 # Or: U3 = hd*eu - let e1e2 = E1 * E2; - let u1u2 = U1 * U2; - let z1z2 = Z1 * Z2; - let t1t2 = T1 * T2; - let eu = (E1 + U1) * (E2 + U2) - e1e2 - u1u2; - let zt = (Z1 + T1) * (Z2 + T2) - z1z2 - t1t2; - let hd = z1z2 + t1t2; // Z1*Z2 - (a^2 - 4*b)*T1*T2 - let T3 = eu.square(); - let Z3 = hd.square(); - let E3 = (z1z2 - t1t2) * (e1e2 + u1u2 * Self::Ap) - u1u2.mul2() * zt; - let U3 = hd * eu; // faster than: ((hd + eu)^2 - Z3 - T3)/2 - self.E = E3; - self.Z = Z3; - self.U = U3; - self.T = T3; - } - - /// Specialized point addition routine when the other operand is in - /// affine extended coordinates (used in the pregenerated tables for - /// multiples of the base point). - fn set_add_affine_extended(&mut self, rhs: &PointAffineExtended) { - let (E1, Z1, U1, T1) = (&self.E, &self.Z, &self.U, &self.T); - let (e2, u2, t2) = (&rhs.e, &rhs.u, &rhs.t); - - // Generic case (7M+3S): - // constants on the dual curve: - // a' = -2*a - // b' = a^2 - 4*b (here: b' = -1) - // e1e2 = E1*E2 - // u1u2 = U1*U2 - // t1t2 = T1*T2 - // zt = Z1*t2 + T1 - // eu = (E1 + U1)*(E2 + U2) - e1e2 - u1u2 - // hd = Z1 - b'*t1t2 - // E3 = (Z1 + b'*t1t2)*(e1e2 + a'*u1u2) + 2*b'*u1u2*zt - // Z3 = hd^2 - // T3 = eu^2 - // U3 = ((hd + eu)^2 - Z3 - T3)/2 # Or: U3 = hd*eu - let e1e2 = E1 * e2; - let u1u2 = U1 * u2; - let t1t2 = T1 * t2; - let eu = (E1 + U1) * (e2 + u2) - e1e2 - u1u2; - let zt = Z1 * t2 + T1; - let hd = Z1 + t1t2; // Z1*Z2 - (a^2 - 4*b)*T1*T2 - let T3 = eu.square(); - let Z3 = hd.square(); - let E3 = (Z1 - t1t2) * (e1e2 + u1u2 * Self::Ap) - u1u2.mul2() * zt; - let U3 = hd * eu; // faster than: ((hd + eu)^2 - Z3 - T3)/2 - self.E = E3; - self.Z = Z3; - self.U = U3; - self.T = T3; - } - - /// Specialized point subtraction routine when the other operand is in - /// affine extended coordinates (used in the pregenerated tables for - /// multiples of the base point). - fn set_sub_affine_extended(&mut self, rhs: &PointAffineExtended) { - let mrhs = PointAffineExtended { - e: rhs.e, - u: -rhs.u, - t: rhs.t, - }; - self.set_add_affine_extended(&mrhs); - } - - /// Doubles this point (in place). - pub fn set_double(&mut self) { - let (E, Z, U, T) = (&self.E, &self.Z, &self.U, &self.T); - - // Doubling, output in XWJ coordinates (cost: 1M+3S) - // C = U**2 - // X = 16*b*(C**2) - // W = 4*gamma*C - (2*gamma*T + Z)**2 - // J = 2*E*U - let C = U.square(); - let X = C.square() * Self::Bx16; - let W = C.mul2() - (T + Z).square(); - let J = E * U.mul2(); - - // Conversion to EZUT coordinates (cost: 3S) - // Z = W**2 - // T = J**2 - // U = ((W + J)**2 - Z - T)/2 # or: U = W*J - // E = 2*X - Z + a*T - self.Z = W.square(); - self.T = J.square(); - self.U = W * J; - self.E = X.mul2() - self.Z + self.T * Self::A; - } - - /// Doubles this point. - #[inline(always)] - pub fn double(self) -> Self { - let mut r = self; - r.set_double(); - r - } - - /// Doubles this point n times (in place). - pub fn set_xdouble(&mut self, n: u32) { - if n == 0 { - return; - } - - let (E, Z, U, T) = (&self.E, &self.Z, &self.U, &self.T); - - // First doubling, output in XWJ coordinates (cost: 1M+3S) - // C = U**2 - // X = 16*b*(C**2) - // W = 4*gamma*C - (2*gamma*T + Z)**2 - // J = 2*E*U - let C = U.square(); - let mut X = C.square() * Self::Bx16; - let mut W = C.mul2() - (T + Z).square(); - let mut J = E * U.mul2(); - - for _ in 1..n { - // (n-1) doublings in XWJ coordinates. - // C = J**2 - // E = W**2 - // G = (W + J)**2 - C - E # G = 2*W*J - // H = G**2 - // I = (E + 2*gamma*C)**2 - // X2 = b*(H**2) - // W2 = gamma*H - I - // J2 = G*(2*X + a*C - E) # note: uses previous X, not new X2 - let C = J.square(); - let E = W.square(); - let G = (W + J).square() - C - E; // FIXME: or: W * J.mul2() - let H = G.square(); - let I = (E + C).square(); - W = H.half() - I; - J = G * (X.mul2() + C * Self::A - E); - X = H.square() * Self::B; - } - - // Final conversion to EZUT coordinates (cost: 3S) - // Z = W**2 - // T = J**2 - // U = ((W + J)**2 - Z - T)/2 # or: U = W*J - // E = 2*X - Z + a*T - self.Z = W.square(); - self.T = J.square(); - self.U = W * J; - self.E = X.mul2() - self.Z + self.T * Self::A; - } - - /// Doubles this point n times. - #[inline(always)] - pub fn xdouble(self, n: u32) -> Self { - let mut r = self; - r.set_xdouble(n); - r - } - - /// Negates this point (in place). - #[inline(always)] - pub fn set_neg(&mut self) { - self.U.set_neg(); - } - - /// Subtract another point (`rhs`) from this point. - fn set_sub(&mut self, rhs: &Self) { - self.set_add(&-rhs); - } - - /// Multiplies this point by a small integer. - /// - /// This operation is constant-time with regard to the source point, - /// but NOT with regard to the multiplier; the multiplier `n` MUST - /// NOT be secret. - pub fn set_mul_small(&mut self, n: u64) { - if n == 0 { - *self = Self::NEUTRAL; - return; - } - if n == 1 { - return; - } - - let nlen = 64 - n.leading_zeros(); - let T = *self; - let mut ndbl = 0u32; - for i in (0..(nlen - 1)).rev() { - ndbl += 1; - if ((n >> i) & 1) == 0 { - continue; - } - self.set_xdouble(ndbl); - ndbl = 0; - self.set_add(&T); - } - self.set_xdouble(ndbl); - } - - /// Compares two points for equality. - /// - /// Returned value is 0xFFFFFFFF if the two points are equal, - /// 0x00000000 otherwise. - #[inline] - pub fn equals(self, rhs: Self) -> u32 { - // Points are equal if and only if they have the same image through - // isogeny theta1: - // theta1(e, u) = (f, v) - // with f = (a^2 - 4*b)*u^2, and v = u/e - // In the theta1 output, coordinate v of a point uniquely identifies - // the point. Thus, we only need to compare u1/e1 and u2/e2, which - // is equivalent to comparing u1*e2 and u2*e1 (since e1 and e2 are - // never zero). - (self.U * rhs.E).equals(rhs.U * self.E) - } - - /// Tests whether this point is the neutral (identity point on the - /// curve). - /// - /// Returned value is 0xFFFFFFFF for the neutral, 0x00000000 - /// otherwise. - #[inline(always)] - pub fn isneutral(self) -> u32 { - self.U.iszero() - } - - /// Conditionally copies the provided point (`P`) into `self`. - /// - /// - If `ctl` = 0xFFFFFFFF, then the value of `P` is copied into `self`. - /// - /// - If `ctl` = 0x00000000, then the value of `self` is unchanged. - /// - /// `ctl` MUST be equal to 0x00000000 or 0xFFFFFFFF. - #[inline] - pub fn set_cond(&mut self, P: &Self, ctl: u32) { - self.E.set_cond(&P.E, ctl); - self.Z.set_cond(&P.Z, ctl); - self.U.set_cond(&P.U, ctl); - self.T.set_cond(&P.T, ctl); - } - - /// Returns a point equal to `P0` (if `ctl` = 0x00000000) or `P1` (if - /// `ctl` = 0xFFFFFFFF). - /// - /// Value `ctl` MUST be either 0x00000000 or 0xFFFFFFFF. - #[inline(always)] - pub fn select(P0: &Self, P1: &Self, ctl: u32) -> Self { - let mut P = *P0; - P.set_cond(P1, ctl); - P - } - - /// Conditionally negates this point. - /// - /// This point is negated if `ctl` = 0xFFFFFFFF, but kept unchanged - /// if `ctl` = 0x00000000. `ctl` MUST be equal to 0x00000000 or - /// 0xFFFFFFFF. - #[inline] - pub fn set_condneg(&mut self, ctl: u32) { - self.U.set_cond(&-self.U, ctl); - } - - /// Maps a field element into a point. - /// - /// This map output is not uniformly distributed; in general, it should - /// be used only through `hash_to_curve()`, which invokes this map - /// twice. - fn map_to_curve(f: &GFp) -> Self { - // We use the Elligator2 map. - // Let d = -1 (a non-square in GF(p)). We compute: - // c = 1 + d*f^2 = 1 - f^2 - // If c = 0, then the output is the neutral. Otherwise, we - // compute: - // v = -a/c - // z = v*(v^2 + a*v + b) - // If z is a square, then we set x = v; otherwise, x = -v - a. - // At that point, x is correct, we can compute x*(x^2 + a*x + b) - // and extract a square root to get y. From x and y, we can compute - // e and u using: - // e = (x^2 - b)/(x^2 + a*x + b) - // u = x/y - // We do not really care about getting an affine point (in general - // we'll immediately add the resulting point with another map - // output); we can thus avoid divisions here. - - // c = 1 - f^2 - let c = GFp::ONE - f.square(); - - // We now assume that c != 0. - // v = -a/c - // z = v*(v^2 + a*v + b) - // We really compute: - // z*c^4 = -a*c*(a^2 - c*a^2 + b*c^2) - // which is as much a square as z is a square, and does not need - // any division. - let zc4 = -Self::A * c * ((Self::B * c - Self::AA) * c + Self::AA); - - // If z (zc4) is a square, then x = v; otherwise, x = -v - a. - // We really compute x*c, which is then set to either -a or a*(1 - c). - let ls = zc4.legendre(); - let z_is_qr = !((ls >> 1) as u32); - let xc0 = Self::A * (GFp::ONE - c); - let xc1 = -Self::A; - let xc = GFp::select(&xc0, &xc1, z_is_qr); - - // If c != 0, then v != 0. If v = -a, then c = -1, which implies - // that f^2 = 2, which is not possible since 2 is not a square in - // GF(p). Thus, we can never get x = 0, regardless of whether - // z was a square or not. Since the point N = (0,0) is the only - // curve point such that y = 0, this implies that y != 0. - // y^2 = x*(x^2 + a*x + b) - // u = x/y - // = x*y/y^2 - // e = (x^2 - b)/(x^2 + a*x + b) - // = x*(x^2 - b)/y^2 - // t = u^2 - // = x^2/y^2 - // To avoid divisions, we multiply all numerators and denominators - // with c^4. - - // (y*c^2)^2 = c*(x*c)*((x*c)^2 + a*c*(x*c) + b*c^2) - // We then extract yc2 = y*c^2 with a square root. Our sqrt() - // function enforces the choice of a "non-negative" output (the - // least significant bit of the integer output is 0), which is - // the convention we use for this map. - let c2 = c.square(); // c^2 - let xc2 = xc * c; // x*c^2 - let x2c2 = xc.square(); // x^2*c^2 - let y2c4 = xc2 * (xc.square() + Self::A * xc2 + Self::B * c2); - let (yc2, g) = y2c4.sqrt(); // y*c^2 - assert!(g == 0xFFFFFFFF); // sqrt() should never fail here - - // E = c*(x*c)*((x*c)^2 - b*c^2) - // Z = y^2*c^4 - // U = x*y*c^4 = x*c^2*(y*c^2) - // T = (x*c^2)^2 - let mut E = c * xc * (x2c2 - Self::B * c2); - let mut Z = y2c4; - let U = xc2 * yc2; - let T = xc2.square(); - - // If c = 0 then everything is zero here, which is invalid; - // in that case, we must replace E with -1 and Z with 1, to get - // a valid representation of the neutral. - let c_is_zero = c.iszero(); - E.set_cond(&GFp::MINUS_ONE, c_is_zero); - Z.set_cond(&GFp::ONE, c_is_zero); - - Self { E, Z, U, T } - } - - /// Maps a pseudorandom 64-byte input into a point. - /// - /// This function assumes that the provided 64-byte input was - /// obtained from a cryptographically secure hash function (e.g. - /// SHA-512), in which case the whole process represents a secure - /// hash-to-curve mechanism. The output distribution is - /// computationally indistinguishable from uniform over the whole - /// group. Note that the output _may_ be the neutral (with very - /// low probability). - /// - /// The input MUST have length exactly 64 bytes (otherwise, the - /// function panics). - pub fn hash_to_curve(data: &[u8]) -> Self { - assert!(data.len() == 64); - let f1 = GFp::decode_reduce(&data[0..32]); - let f2 = GFp::decode_reduce(&data[32..64]); - Self::map_to_curve(&f1) + Self::map_to_curve(&f2) - } - - /// Recodes a scalar into 52 signed digits. - /// - /// Each digit is in -15..+16, top digit is 0 or 1. - fn recode_scalar(n: &Scalar) -> [i8; 52] { - let mut sd = [0i8; 52]; - let bb = n.encode(); - let mut cc: u32 = 0; // carry from lower digits - let mut i: usize = 0; // index of next source byte - let mut acc: u32 = 0; // buffered bits - let mut acc_len: i32 = 0; // number of buffered bits - for j in 0..52 { - if acc_len < 5 && i < 32 { - acc |= (bb[i] as u32) << acc_len; - acc_len += 8; - i += 1; - } - let d = (acc & 0x1F) + cc; - acc >>= 5; - acc_len -= 5; - let m = 16u32.wrapping_sub(d) >> 8; - sd[j] = (d.wrapping_sub(m & 32)) as i8; - cc = m & 1; - } - sd - } - - /// Lookups a point from a window, with sign handling (constant-time). - fn lookup(win: &[Self; 16], k: i8) -> Self { - // Split k into its sign s (0xFFFFFFFF for negative) and - // absolute value (f). - let s = ((k as i32) >> 8) as u32; - let f = ((k as u32) ^ s).wrapping_sub(s); - let mut P = Self::NEUTRAL; - for i in 0..16 { - // win[i] contains (i+1)*P; we want to keep it if (and only if) - // i+1 == f. - // Values a-b and b-a both have their high bit equal to 0 only - // if a == b. - let j = (i as u32) + 1; - let w = !(f.wrapping_sub(j) | j.wrapping_sub(f)); - let w = ((w as i32) >> 31) as u32; - - P.E.set_cond(&win[i].E, w); - P.Z.set_cond(&win[i].Z, w); - P.U.set_cond(&win[i].U, w); - P.T.set_cond(&win[i].T, w); - } - - // Negate the returned value if needed. - P.U.set_cond(&-P.U, s); - - P - } - - /// Multiplies this point by a scalar (in place). - /// - /// This operation is constant-time with regard to both the points - /// and the scalar value. - pub fn set_mul(&mut self, n: &Scalar) { - // Make a 5-bit window: win[i] contains (i+1)*P - let mut win = [Self::NEUTRAL; 16]; - win[0] = *self; - for i in 1..8 { - let j = 2 * i; - win[j - 1] = win[i - 1].double(); - win[j] = win[j - 1] + win[0]; - } - win[15] = win[7].double(); - - // Recode the scalar into 52 signed digits. - let sd = Self::recode_scalar(n); - - // Process the digits in high-to-low order. - *self = Self::lookup(&win, sd[51]); - for i in (0..51).rev() { - self.set_xdouble(5); - self.set_add(&Self::lookup(&win, sd[i])); - } - } - - /// Lookups a point from a window of points in affine extended - /// coordinates, with sign handling (constant-time). - fn lookup_affine_extended(win: &[PointAffineExtended; 16], k: i8) - -> PointAffineExtended - { - // Split k into its sign s (0xFFFFFFFF for negative) and - // absolute value (f). - let s = ((k as i32) >> 8) as u32; - let f = ((k as u32) ^ s).wrapping_sub(s); - let mut P = PointAffineExtended::NEUTRAL; - for i in 0..16 { - // win[i] contains (i+1)*P; we want to keep it if (and only if) - // i+1 == f. - // Values a-b and b-a both have their high bit equal to 0 only - // if a == b. - let j = (i as u32) + 1; - let w = !(f.wrapping_sub(j) | j.wrapping_sub(f)); - let w = ((w as i32) >> 31) as u32; - - P.e.set_cond(&win[i].e, w); - P.u.set_cond(&win[i].u, w); - P.t.set_cond(&win[i].t, w); - } - - // Negate the returned value if needed. - P.u.set_cond(&-P.u, s); - - P - } - - /// Sets this point by multiplying the conventional generator by the - /// provided scalar. - /// - /// This operation is constant-time. It is faster than using the - /// generic multiplication on `Self::BASE`. - pub fn set_mulgen(&mut self, n: &Scalar) { - // Recode the scalar into 52 signed digits. - let sd = Self::recode_scalar(n); - - // We process four chunks in parallel. Each chunk is 13 digits. - *self = Self::from_affine_extended( - &Self::lookup_affine_extended(&PRECOMP_B, sd[12])); - self.set_add_affine_extended( - &Self::lookup_affine_extended(&PRECOMP_B65, sd[25])); - self.set_add_affine_extended( - &Self::lookup_affine_extended(&PRECOMP_B130, sd[38])); - self.set_add_affine_extended( - &Self::lookup_affine_extended(&PRECOMP_B195, sd[51])); - - // Process the digits in high-to-low order. - for i in (0..12).rev() { - self.set_xdouble(5); - self.set_add_affine_extended( - &Self::lookup_affine_extended(&PRECOMP_B, sd[i])); - self.set_add_affine_extended( - &Self::lookup_affine_extended(&PRECOMP_B65, sd[i + 13])); - self.set_add_affine_extended( - &Self::lookup_affine_extended(&PRECOMP_B130, sd[i + 26])); - self.set_add_affine_extended( - &Self::lookup_affine_extended(&PRECOMP_B195, sd[i + 39])); - } - } - - /// Creates a point by multiplying the conventional generator by the - /// provided scalar. - /// - /// This operation is constant-time. - #[inline] - pub fn mulgen(n: &Scalar) -> Self { - let mut P = Self::NEUTRAL; - P.set_mulgen(n); - P - } - - /// 5-bit wNAF recoding of a scalar; output is a sequence of 256 - /// digits. - /// - /// Non-zero digits have an odd value, between -15 and +15 - /// (inclusive). (The recoding is constant-time, but use of wNAF is - /// inherently non-constant-time.) - fn recode_scalar_NAF(n: &Scalar) -> [i8; 256] { - // We use a branchless algorithm to avoid misprediction - // penalties. - // - // Let x be the current (complete) integer: - // - If x is even, then the next digit is 0. - // - Otherwise, we produce a digit from the low five bits of - // x. If these low bits have value v (odd, 1..31 range): - // - If v <= 15, then the next digit is v. - // - Otherwise, the next digit is v - 32, and we add 32 to x. - // When then subtract v from x (i.e. we clear the low five bits). - // Once the digit has been produced, we divide x by 2 and loop. - // - // Since scalars are modulo q < 2^255, only 256 digits are necessary - // at most. - - let mut sd = [0i8; 256]; - let bb = n.encode(); - let mut x = bb[0] as u32; - for i in 0..256 { - if (i & 7) == 4 && i < 252 { - x += (bb[(i + 4) >> 3] as u32) << 4; - } - let m = (x & 1).wrapping_neg(); // -1 if x is odd, 0 otherwise - let v = x & m & 31; // low 5 bits if x odd, or 0 - let c = (v & 16) << 1; // carry (0 or 32) - let d = v.wrapping_sub(c); // next digit - sd[i] = d as i8; - x = x.wrapping_sub(d) >> 1; - } - sd - } - - /// 5-bit wNAF recoding of a nonnegative integer. - /// - /// 129 digits are produced (array has size 130, extra value is 0). - /// Non-zero digits have an odd value, between -15 and +15 - /// (inclusive). (The recoding is constant-time, but use of wNAF is - /// inherently non-constant-time.) - fn recode_u128_NAF(n: u128) -> [i8; 130] { - // See recode_scalar_NAF() for details. - let mut sd = [0i8; 130]; - let mut y = n; - for i in 0..129 { - let x = y as u32; - let m = (x & 1).wrapping_neg(); // -1 if x is odd, 0 otherwise - let v = x & m & 31; // low 5 bits if x odd, or 0 - let c = (v & 16) << 1; // carry (0 or 32) - sd[i] = v.wrapping_sub(c) as i8; - y = y.wrapping_sub(v as u128).wrapping_add(c as u128) >> 1; - } - sd - } - - /// Given scalars `u` and `v`, sets this point to `u*self + v*B` - /// (with `B` being the conventional generator of the prime order - /// subgroup). - /// - /// This can be used to support Schnorr signature verification. - /// - /// THIS FUNCTION IS NOT CONSTANT-TIME; it shall be used only with - /// public data. - pub fn set_mul_add_mulgen_vartime(&mut self, u: &Scalar, v: &Scalar) { - // Recode the scalars in 5-bit wNAF. - let sdu = Self::recode_scalar_NAF(&u); - let sdv = Self::recode_scalar_NAF(&v); - - // Compute the window for the current point: - // win[i] = (2*i+1)*self (i = 0 to 7) - let mut win = [Self::NEUTRAL; 8]; - let Q = self.double(); - win[0] = *self; - for i in 1..8 { - win[i] = win[i - 1] + Q; - } - - let mut zz = true; - let mut ndbl = 0u32; - for i in (0..256).rev() { - // We have one more doubling to perform. - ndbl += 1; - - // Get next digits. If they are all zeros, then we can loop - // immediately. - let e1 = sdu[i]; - let e2 = sdv[i]; - if ((e1 as u32) | (e2 as u32)) == 0 { - continue; - } - - // Apply accumulated doubles. - if zz { - *self = Self::NEUTRAL; - zz = false; - } else { - self.set_xdouble(ndbl); - } - ndbl = 0u32; - - // Process digits. - if e1 != 0 { - if e1 > 0 { - self.set_add(&win[e1 as usize >> 1]); - } else { - self.set_sub(&win[(-e1) as usize >> 1]); - } - } - if e2 != 0 { - if e2 > 0 { - self.set_add_affine_extended(&PRECOMP_B[e2 as usize - 1]); - } else { - self.set_sub_affine_extended(&PRECOMP_B[(-e2) as usize - 1]); - } - } - } - - if zz { - *self = Self::NEUTRAL; - } else { - if ndbl > 0 { - self.set_xdouble(ndbl); - } - } - } - - /// Given scalars `u` and `v`, returns `u*self + v*B` (with `B` being - /// the conventional generator of the prime order subgroup). - /// - /// This can be used to support Schnorr signature verification. - /// - /// THIS FUNCTION IS NOT CONSTANT-TIME; it shall be used only with - /// public data. - #[inline(always)] - pub fn mul_add_mulgen_vartime(self, u: &Scalar, v: &Scalar) -> Self { - let mut R = self; - R.set_mul_add_mulgen_vartime(u, v); - R - } - - /// Given integer `u` and scalar `v`, sets this point to `u*self + v*B` - /// (with `B` being the conventional generator of the prime order - /// subgroup). - /// - /// This can be used to support Schnorr signature verification. - /// - /// THIS FUNCTION IS NOT CONSTANT-TIME; it shall be used only with - /// public data. - pub fn set_mul128_add_mulgen_vartime(&mut self, u: u128, v: &Scalar) { - // Recode the integer and scalar in 5-bit wNAF. - let sdu = Self::recode_u128_NAF(u); - let sdv = Self::recode_scalar_NAF(&v); - - // Compute the window for the current point: - // win[i] = (2*i+1)*self (i = 0 to 7) - let mut win = [Self::NEUTRAL; 8]; - let Q = self.double(); - win[0] = *self; - for i in 1..8 { - win[i] = win[i - 1] + Q; - } - - let mut zz = true; - let mut ndbl = 0u32; - for i in (0..130).rev() { - // We have one more doubling to perform. - ndbl += 1; - - // Get next digits. If they are all zeros, then we can loop - // immediately. - let e1 = sdu[i]; - let e2 = sdv[i]; - let e3 = if i < 126 { sdv[i + 130] } else { 0 }; - if ((e1 as u32) | (e2 as u32) | (e3 as u32)) == 0 { - continue; - } - - // Apply accumulated doubles. - if zz { - *self = Self::NEUTRAL; - zz = false; - } else { - self.set_xdouble(ndbl); - } - ndbl = 0u32; - - // Process digits. - if e1 != 0 { - if e1 > 0 { - self.set_add(&win[e1 as usize >> 1]); - } else { - self.set_sub(&win[(-e1) as usize >> 1]); - } - } - if e2 != 0 { - if e2 > 0 { - self.set_add_affine_extended(&PRECOMP_B[e2 as usize - 1]); - } else { - self.set_sub_affine_extended(&PRECOMP_B[(-e2) as usize - 1]); - } - } - if e3 != 0 { - if e3 > 0 { - self.set_add_affine_extended(&PRECOMP_B130[e3 as usize - 1]); - } else { - self.set_sub_affine_extended(&PRECOMP_B130[(-e3) as usize - 1]); - } - } - } - - if zz { - *self = Self::NEUTRAL; - } else { - if ndbl > 0 { - self.set_xdouble(ndbl); - } - } - } - - /// Given integer `u` and scalar `v`, returns `u*self + v*B` (with - /// `B` being the conventional generator of the prime order subgroup). - /// - /// This can be used to support Schnorr signature verification. - /// - /// THIS FUNCTION IS NOT CONSTANT-TIME; it shall be used only with - /// public data. - #[inline(always)] - pub fn mul128_add_mulgen_vartime(self, u: u128, v: &Scalar) -> Self { - let mut R = self; - R.set_mul128_add_mulgen_vartime(u, v); - R - } - - // Recodes a scalar into up to 52 signed digits. - // - // The width (w) must be in 5 to 8 range. Number of digits and - // range of each digit depends on the width, and the top digit is - // a special case: - // - // w num min max top-max - // 5 52 -16 +15 1 - // 6 43 -32 +31 8 - // 7 37 -64 +63 8 - // 8 32 -128 +127 128 (+128 is encoded as -128) - // - // Note: for 5 <= w <= 8, the number of digits is floor(260/w). - fn recode_scalar_ext(n: &Scalar, w: i32) -> [i8; 52] { - assert!(w >= 5 && w <= 8); - let mut sd = [0i8; 52]; - let bb = n.encode(); - let mut cc: u32 = 0; // carry from lower digits - let mut i: usize = 0; // index of next source byte - let mut acc: u32 = 0; // buffered bits - let mut acc_len: i32 = 0; // number of buffered bits - let t = (1u32 << (w - 1)) - 1; // threshold for sign/carry - let vm = (1u32 << w) - 1; // chunk mask - let cm = 1u32 << w; // window value - for j in 0..Self::num_recoded_digits(w) { - if acc_len < w && i < 32 { - acc |= (bb[i] as u32) << acc_len; - acc_len += 8; - i += 1; - } - let d = (acc & vm) + cc; - acc >>= w; - acc_len -= w; - let m = t.wrapping_sub(d) >> 8; - sd[j] = (d.wrapping_sub(m & cm)) as i8; - cc = m & 1; - } - sd - } - - // Get the number of digits in a recoded scalar for a given window - // width (5 to 8). - fn num_recoded_digits(w: i32) -> usize { - 260 / (w as usize) - } - - /// Given two lists of scalars `s_i` and points `P_i` (provided as two - /// iterable objects, set this point to `\sum s_i*P_i`. The two lists - /// MUST have the same length. This function applies Pippenger's algorithm. - /// - /// THIS FUNCTION IS NOT CONSTANT-TIME; it shall be used only with - /// public data. - pub fn set_xmuladd_vartime(&mut self, scalars: I, points: J) - where - I: IntoIterator, - I::Item: Borrow, - J: IntoIterator, - J::Item: Borrow, - { - // Collect all points, and convert them all to affine (extended). - // We can use a batch inversion on the Z coordinates. - // We first create the PointAffineExtended structures, with e, u and t - // set to E, U and Z, respectively. - let mut pp: Vec<_> = points.into_iter().map(|p| { - let r = p.borrow(); - PointAffineExtended { e: r.E, u: r.U, t: r.Z } - }).collect(); - let n = pp.len(); - // We now batch-invert all Z coordinates. - let mut zz = Vec::::with_capacity(n); - for i in 0..n { - zz.push(pp[i].t); - } - GFp::batch_invert(&mut zz[..]); - // We propagate back the inverses to fix all affine representations. - for i in 0..n { - pp[i].e *= zz[i]; - pp[i].u *= zz[i]; - pp[i].t = pp[i].u.square(); - } - - // Adjust number of buckets depending on size of input. - // Some rough benchmarking with various numbers of points (n) - // and window size (w) yielded these values (in clock cycles, - // on an Intel i5-8259U at 2.3 GHz, Rust compiler 1.77.2, - // with flag '-C target-cpu=native'): - // n w = 5 w = 6 w = 7 w = 8 - // 100 7027332 7348788 8442832 10325476 - // 200 12130020 11787996 12653408 14876330 - // 300 17295658 16148224 16550730 18735672 - // 400 22474350 20506440 20351398 22192916 - // 500 27588466 24670988 24124692 25596360 - // 600 32701872 29214516 27930690 28930000 - // 700 37887610 33500780 31653746 32276674 - // 800 43099892 37888676 35566980 35663102 - // 900 48257408 42327264 39274256 38969788 - // 1000 53379008 46652806 43145590 42298562 - // 1100 58474250 51066746 46917582 45603456 - // 1200 63713838 55407826 50741862 48967392 - // 1300 68690520 59623172 54393972 52119948 - // 1400 73844728 64030814 58189806 55444314 - // The choice of w below follows these measurements to try to - // get the best performance (on any particular platform, optimal - // thresholds may vary, but these ones should be reasonably good - // everywhere). - let w = if n < 200 { - 5 - } else if n < 400 { - 6 - } else if n < 800 { - 7 - } else { - 8 - }; - - // Do Booth recoding for all provided scalars. We also check that - // the number of scalars matches the number of points. - let ss_booth: Vec<_> = scalars.into_iter() - .map(|s| Self::recode_scalar_ext(s.borrow(), w)).collect(); - assert!(ss_booth.len() == n); - let num_digits = Self::num_recoded_digits(w); - - // Digits are in the -2^(w-1)..+2^(w-1) range (inclusive). By - // adding either P or -P to a bucket, we can work is 2^(w-1) - // buckets (we do not need a bucket for digits of value zero). - let num_buckets = 1usize << (w - 1); - - // Allocate buckets. - let mut qq: Vec<_> = (0..num_buckets).map(|_| Self::NEUTRAL).collect(); - - // Process top digit of all scalars. The top digits are always - // non-negative, so we can convert them to `u8`, which handles the - // special encoding of the top digit when w = 8. - for i in 0..n { - let d = (ss_booth[i][num_digits - 1] as u8) as usize; - if d != 0 { - qq[d - 1].set_add_affine_extended(&pp[i]); - } - } - - // The accumulator ultimately receives the result. - *self = Self::NEUTRAL; - - // Each loop assumes that buckets for digit j have been filled, - // and empties them into the accumulator. If j == 0, the computation - // is finished; otherwise, it fills the buckets for digit j-1. - for j in (0..num_digits).rev() { - // Add buckets with multiplicity, into the accumulator. - // We skip the top empty buckets. - let mut S = Self::NEUTRAL; - let mut sz = true; - for k in (0..num_buckets).rev() { - let bucket_full = qq[k].isneutral() == 0; - if sz { - if !bucket_full { - continue; - } - sz = false; - S = qq[k]; - } else { - if bucket_full { - S += qq[k]; - } - } - *self += S; - } - - // If we have processed the buckets for digit 0 then we are done. - if j == 0 { - break; - } - - // Reset buckets. - for q in &mut qq { - *q = Self::NEUTRAL; - } - - // Fill buckets for digit j-1. This is for non-top digits, hence - // they can be negative, but there is no special encoding - // (maximal non-top digit with w = 8 is +127, not +128). - for i in 0..n { - let d = ss_booth[i][j - 1] as isize; - if d < 0 { - qq[(-d - 1) as usize].set_sub_affine_extended(&pp[i]); - } else if d > 0 { - qq[(d - 1) as usize].set_add_affine_extended(&pp[i]); - } - } - - // Apply the doublings on the accumulator. - if self.isneutral() == 0 { - self.set_xdouble(w as u32); - } - } - } - - /// Given two lists of scalars `s_i` and points `P_i` (provided as two - /// iterable objects, compute and return `\sum s_i*P_i`. The two lists - /// MUST have the same length. This function applies Pippenger's algorithm. - /// - /// THIS FUNCTION IS NOT CONSTANT-TIME; it shall be used only with - /// public data. - #[inline] - pub fn xmuladd_vartime(scalars: I, points: J) -> Self - where - I: IntoIterator, - I::Item: Borrow, - J: IntoIterator, - J::Item: Borrow, - { - let mut P = Self::NEUTRAL; - P.set_xmuladd_vartime(scalars, points); - P - } -} - -impl Add for Point { - type Output = Point; - - #[inline(always)] - fn add(self, other: Point) -> Point { - let mut r = self; - r.set_add(&other); - r - } -} - -impl Add<&Point> for Point { - type Output = Point; - - #[inline(always)] - fn add(self, other: &Point) -> Point { - let mut r = self; - r.set_add(other); - r - } -} - -impl Add for &Point { - type Output = Point; - - #[inline(always)] - fn add(self, other: Point) -> Point { - let mut r = *self; - r.set_add(&other); - r - } -} - -impl Add<&Point> for &Point { - type Output = Point; - - #[inline(always)] - fn add(self, other: &Point) -> Point { - let mut r = *self; - r.set_add(other); - r - } -} - -impl AddAssign for Point { - #[inline(always)] - fn add_assign(&mut self, other: Point) { - self.set_add(&other); - } -} - -impl AddAssign<&Point> for Point { - #[inline(always)] - fn add_assign(&mut self, other: &Point) { - self.set_add(other); - } -} - -impl Mul for Point { - type Output = Point; - - #[inline(always)] - fn mul(self, other: Scalar) -> Point { - let mut r = self; - r.set_mul(&other); - r - } -} - -impl Mul<&Scalar> for Point { - type Output = Point; - - #[inline(always)] - fn mul(self, other: &Scalar) -> Point { - let mut r = self; - r.set_mul(other); - r - } -} - -impl Mul for &Point { - type Output = Point; - - #[inline(always)] - fn mul(self, other: Scalar) -> Point { - let mut r = *self; - r.set_mul(&other); - r - } -} - -impl Mul<&Scalar> for &Point { - type Output = Point; - - #[inline(always)] - fn mul(self, other: &Scalar) -> Point { - let mut r = *self; - r.set_mul(other); - r - } -} - -impl MulAssign for Point { - #[inline(always)] - fn mul_assign(&mut self, other: Scalar) { - self.set_mul(&other); - } -} - -impl MulAssign<&Scalar> for Point { - #[inline(always)] - fn mul_assign(&mut self, other: &Scalar) { - self.set_mul(other); - } -} - -impl Mul for Scalar { - type Output = Point; - - #[inline(always)] - fn mul(self, other: Point) -> Point { - let mut r = other; - r.set_mul(&self); - r - } -} - -impl Mul<&Point> for Scalar { - type Output = Point; - - #[inline(always)] - fn mul(self, other: &Point) -> Point { - let mut r = *other; - r.set_mul(&self); - r - } -} - -impl Mul for &Scalar { - type Output = Point; - - #[inline(always)] - fn mul(self, other: Point) -> Point { - let mut r = other; - r.set_mul(self); - r - } -} - -impl Mul<&Point> for &Scalar { - type Output = Point; - - #[inline(always)] - fn mul(self, other: &Point) -> Point { - let mut r = *other; - r.set_mul(self); - r - } -} - -impl Mul for Point { - type Output = Point; - - #[inline(always)] - fn mul(self, other: u64) -> Point { - let mut r = self; - r.set_mul_small(other); - r - } -} - -impl Mul for &Point { - type Output = Point; - - #[inline(always)] - fn mul(self, other: u64) -> Point { - let mut r = *self; - r.set_mul_small(other); - r - } -} - -impl MulAssign for Point { - #[inline(always)] - fn mul_assign(&mut self, other: u64) { - self.set_mul_small(other); - } -} - -impl Mul for u64 { - type Output = Point; - - #[inline(always)] - fn mul(self, other: Point) -> Point { - let mut r = other; - r.set_mul_small(self); - r - } -} - -impl Mul<&Point> for u64 { - type Output = Point; - - #[inline(always)] - fn mul(self, other: &Point) -> Point { - let mut r = *other; - r.set_mul_small(self); - r - } -} - -impl Neg for Point { - type Output = Point; - - #[inline(always)] - fn neg(self) -> Point { - let mut r = self; - r.set_neg(); - r - } -} - -impl Neg for &Point { - type Output = Point; - - #[inline(always)] - fn neg(self) -> Point { - let mut r = *self; - r.set_neg(); - r - } -} - -impl Sub for Point { - type Output = Point; - - #[inline(always)] - fn sub(self, other: Point) -> Point { - let mut r = self; - r.set_sub(&other); - r - } -} - -impl Sub<&Point> for Point { - type Output = Point; - - #[inline(always)] - fn sub(self, other: &Point) -> Point { - let mut r = self; - r.set_sub(other); - r - } -} - -impl Sub for &Point { - type Output = Point; - - #[inline(always)] - fn sub(self, other: Point) -> Point { - let mut r = *self; - r.set_sub(&other); - r - } -} - -impl Sub<&Point> for &Point { - type Output = Point; - - #[inline(always)] - fn sub(self, other: &Point) -> Point { - let mut r = *self; - r.set_sub(other); - r - } -} - -impl SubAssign for Point { - #[inline(always)] - fn sub_assign(&mut self, other: Point) { - self.set_sub(&other); - } -} - -impl SubAssign<&Point> for Point { - #[inline(always)] - fn sub_assign(&mut self, other: &Point) { - self.set_sub(other); - } -} - -// ======================================================================== - -// We hardcode known multiples of the points B, (2^65)*B, (2^130)*B -// and (2^195)*B, with B being the conventional base point. These are -// used to speed mulgen() operations up. The points are moreover stored -// in a three-coordinate format (e, u, u^2). - -/// A point in affine extended coordinates (e, u, u^2) -#[derive(Clone, Copy, Debug)] -struct PointAffineExtended { - e: GFp, - u: GFp, - t: GFp, -} - -impl PointAffineExtended { - - const NEUTRAL: Self = Self { - e: GFp::MINUS_ONE, - u: GFp::ZERO, - t: GFp::ZERO, - }; -} - -// Points i*B for i = 1 to 16, affine extended format -static PRECOMP_B: [PointAffineExtended; 16] = [ - // B * 1 - PointAffineExtended { - e: GFp::w64be(0x80EECF52DB5F5A44, 0xF50CA81BDD9B2756, - 0x1B9488E2D8D6D550, 0x8E570EABE5552C04), - u: GFp::w64be(0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000002), - t: GFp::w64be(0x0000000000000000, 0x0000000000000000, - 0x0000000000000000, 0x0000000000000004), - }, - // B * 2 - PointAffineExtended { - e: GFp::w64be(0x754980D9046108EA, 0x15CDAFC33D5DB9B5, - 0x775957C59254E127, 0x0AF8C3398540104F), - u: GFp::w64be(0xF0B8C028BD52F9D1, 0xA83941DB536305CC, - 0x37E26FECC4C732E7, 0x2E6AD3BA219E5406), - t: GFp::w64be(0x1C6B77EB358915D2, 0x2DC3086820BA852D, - 0xC23A5957CC3B5498, 0x67E0BB0225486336), - }, - // B * 3 - PointAffineExtended { - e: GFp::w64be(0x8A1463B25FA02609, 0x9C3CC67DA62B8C98, - 0x1D78F1501B162479, 0x0FCEEE45497CF781), - u: GFp::w64be(0x650D23BA765E9826, 0x126587674355C5CB, - 0x8C39C717B9AC446D, 0x4AA95D297E2F3FA9), - t: GFp::w64be(0xECE14A169327CEA9, 0xEA5C096A4A26D01F, - 0xFF44027B42C999E0, 0xAB48CC1137E97C13), - }, - // B * 4 - PointAffineExtended { - e: GFp::w64be(0xB0CF31309427930D, 0xE891E8906D4EAE36, - 0x40D41D37C790C213, 0x6CB6B7A0CD78955C), - u: GFp::w64be(0x31DF1B12ADF47760, 0x729F72451066407B, - 0x0AF08FAEDB327503, 0xC68C3025DEEEBE31), - t: GFp::w64be(0x3C08AD70C911F6B5, 0xF74A0ABDA90F241A, - 0x51A0DA8ADD1465BF, 0x504733A80DD559FB), - }, - // B * 5 - PointAffineExtended { - e: GFp::w64be(0x4EC17DC938A5FF12, 0xE362D9371FC6079C, - 0x8BB601A5439E42FD, 0xEF69698E9EED3C4D), - u: GFp::w64be(0x39971482BCBDC62C, 0xCD216971F266F713, - 0x54A888606AA01EC7, 0xEDE8E349BE6E1079), - t: GFp::w64be(0x1650FC12047F6D96, 0x03211E7C3464B9F7, - 0xFDB3F9E8F6B02C5F, 0x486A3DE93907361F), - }, - // B * 6 - PointAffineExtended { - e: GFp::w64be(0xF3E86AC7C43ECC22, 0x9F1F931DC56C62F6, - 0x700F2A9C4B70BAB9, 0xB7A4CE2FD8809972), - u: GFp::w64be(0x91EA9DA4EB0480C2, 0x5B4EFE92E3C26D9B, - 0xA7F6F98EEF530461, 0x01404AF8DE603845), - t: GFp::w64be(0x4614EE32CA8259D3, 0x338151613D01CB7C, - 0x158BE36B46A1E5D3, 0xA1ECDF10B9488760), - }, - // B * 7 - PointAffineExtended { - e: GFp::w64be(0x019AF0EB4AAD5B59, 0x0CDA218D32E3642A, - 0x05E0AFC56D6D9A49, 0xD2FE5CBF578D49BD), - u: GFp::w64be(0x2CFFC6F434246E0E, 0x57D8E95EAC87EE67, - 0x45C2A4477F6CB9D3, 0x95770F14C1EC4A86), - t: GFp::w64be(0xF4166F23C298A6AB, 0xBBA6ED893C827F6A, - 0x7C851F9A8C600147, 0x72072C91617123DD), - }, - // B * 8 - PointAffineExtended { - e: GFp::w64be(0xED956BAEC757DBF4, 0x624B5F697053D95C, - 0x2E97B4D1B7AF91B6, 0xCDEF1FB327CF40B9), - u: GFp::w64be(0x437D9B4FA9F32A62, 0x9E8043F115C7EF68, - 0xEF8F8810BF60EAA1, 0xE405275896AA3B09), - t: GFp::w64be(0x11BF26E3144B292F, 0x2B918EDCDEFA61B9, - 0x5FBE0F58C93E4B72, 0xE33BE33712C628C8), - }, - // B * 9 - PointAffineExtended { - e: GFp::w64be(0x2550E91DCA558435, 0xB276587D895D8C40, - 0x77E0EF0842A529EB, 0xDFE9D5951BA804F8), - u: GFp::w64be(0x34DD6C2A3DC223D6, 0xDA9DC9150F7356DF, - 0x019172B736198DEA, 0x3C70C864AC19409E), - t: GFp::w64be(0xA6E795C8365566A2, 0xA4231EB3F22D7326, - 0x19BBB1EC29F07CC3, 0x79B401A45E077554), - }, - // B * 10 - PointAffineExtended { - e: GFp::w64be(0x183FC60288F6E2A9, 0xF94BF2956759385F, - 0x24E0859D2FC86C90, 0xC576A9560508BEB3), - u: GFp::w64be(0x36E6B1B822913968, 0x1504D6AAA5496B52, - 0x9973094746C3A53D, 0xBFD92CDA390DD4FF), - t: GFp::w64be(0x080D5F9EA93A99B2, 0x56EB4D3E3CAE126E, - 0x1D766C8F9F77FFC0, 0xC7D70F2334D46781), - }, - // B * 11 - PointAffineExtended { - e: GFp::w64be(0x8DB40DBABE30C99B, 0xE5F8DE92DDF5BA30, - 0x71EF9FFA04075A8B, 0x9853669086F3646A), - u: GFp::w64be(0x3060DB4D15E08DD3, 0xBA02748BAFD454D1, - 0x78D578839676BB1E, 0x447D5F45D12EE375), - t: GFp::w64be(0xFF23A44988D59A7F, 0x33DA97B2C4AA5770, - 0xFB3B00137F344D09, 0xA7372AE901E989DD), - }, - // B * 12 - PointAffineExtended { - e: GFp::w64be(0x6AAE40D1DC240E12, 0x1ACBA6533A29A563, - 0x61CF181ECF1EFC7B, 0x66479C521A492E4B), - u: GFp::w64be(0x628D161C563E2BC2, 0xDE9EE1949959B7EE, - 0x671AB12B4ABF99CF, 0x0572E01B21B5822F), - t: GFp::w64be(0xD9A339F31144E977, 0x92FFCE1EF4A9C0D4, - 0x6636AC3D8004FCA6, 0xCE334CDFF620F40C), - }, - // B * 13 - PointAffineExtended { - e: GFp::w64be(0xC1A4BF9D0E791065, 0xD4D68D0036BEC901, - 0xF87FDB15CD9854AC, 0x55569E55F6616C0E), - u: GFp::w64be(0x77FDD57F73B1B6F0, 0x518CC5655F706CA7, - 0x7AE6FCCB5ABF196A, 0x1B04566BA6E6ED76), - t: GFp::w64be(0x6FD9A41081BBDBF1, 0x950C3B0DFE3AE1B0, - 0xF43F0F977ED7EE4A, 0x82C448A4FB4DBB03), - }, - // B * 14 - PointAffineExtended { - e: GFp::w64be(0xCCEDC1214EA183D5, 0xC7A0945867D5B8F8, - 0xBCC26CE6DEF37D50, 0xFC9B4AE7955B4CFB), - u: GFp::w64be(0x05D7074D65AAD3E9, 0x7A8FFF9B66E2AFE2, - 0x56C2D3CAA9A35734, 0x5E7E0D422F4549EF), - t: GFp::w64be(0xE79742A1F7282845, 0xA1D308DA89880372, - 0x8E3C22921D39E085, 0xA464B859B2256418), - }, - // B * 15 - PointAffineExtended { - e: GFp::w64be(0x7AB5A6CD6BC5662B, 0x81F0C9BD91CCB756, - 0x560CF4BCEAB7EC7F, 0xCC1FEF43A995B681), - u: GFp::w64be(0x099147802F8F5626, 0x03079F42B0F39AA5, - 0x89BFFAA2D4329863, 0xA4E0AC70B7DAE979), - t: GFp::w64be(0x66BA4857120473D8, 0x334C3B5D935D0588, - 0xA65C9765974A4DDD, 0xC23B5C33FE29C40E), - }, - // B * 16 - PointAffineExtended { - e: GFp::w64be(0x259A9592E37D58B2, 0x1DA33E8AA1684A62, - 0x96B8871D013D93E7, 0xB2FA92BA7C8F86CB), - u: GFp::w64be(0xE5E71B2BD33C0EB4, 0x27CBE0177D5A8686, - 0xFDFA4A37605C83BA, 0x2AB3FB8C446B39D3), - t: GFp::w64be(0xD6766C7D0F495FA7, 0x296486F4A999F1C9, - 0x82812CA0086FE921, 0x7BF0DA5E89725140), - }, -]; - -// Points i*(2^65)*B for i = 1 to 16, affine extended format -static PRECOMP_B65: [PointAffineExtended; 16] = [ - // (2^65)*B * 1 - PointAffineExtended { - e: GFp::w64be(0x7EEBDC89D1CBFAA9, 0x2419E626E7B00902, - 0xFB26824C2922A44C, 0xE270C6747BF3C5A2), - u: GFp::w64be(0xC408FDB81A4F07CA, 0xECE24E81F47834E1, - 0x32873D32D8A15A60, 0x54281FF53F9A7980), - t: GFp::w64be(0x58EDF76F72454445, 0x27DE0A601C3D5C48, - 0x72A2B27A29FF9548, 0x5A07C1CF5462C314), - }, - // (2^65)*B * 2 - PointAffineExtended { - e: GFp::w64be(0xEF3491E139FB7B4B, 0x90BF4E205E349549, - 0x57D5D2DCAA865F4C, 0x61CC8B422A74B02D), - u: GFp::w64be(0x3BDA8C2DE3AE48FB, 0x578D915AA024B96E, - 0x9713C9C1C7843843, 0xC0509AA72B32EACB), - t: GFp::w64be(0x5CD1DB045A2B93C4, 0x505994E2803E63ED, - 0x4886091C1F1EF44E, 0x39A01368EBE670BB), - }, - // (2^65)*B * 3 - PointAffineExtended { - e: GFp::w64be(0x4D2DB1E5580CD1C3, 0x58CC706CD7500E81, - 0xBB47E94EE715A8F3, 0xEC852585DA006E45), - u: GFp::w64be(0x112F27873DD78E6C, 0xCBFEE6139D435B49, - 0xE4C90DB183F0EAAA, 0x8DEC9C25F6924BC0), - t: GFp::w64be(0x1C03DE2347C7CA11, 0xA059B4F6AD900072, - 0x80F4258BD17C2A86, 0xA84186EC72E9B221), - }, - // (2^65)*B * 4 - PointAffineExtended { - e: GFp::w64be(0x516055FC2868663C, 0x8C1ABEFA28D12F9D, - 0x9734BD8DFAE732B8, 0xACF0806A0871313C), - u: GFp::w64be(0xF3C823B6D91C09E2, 0x41A2415CD3C1BC80, - 0xADB1A71CF63077CC, 0x015108488556229A), - t: GFp::w64be(0xC7325E92B836904B, 0xA172A8B29DC9F8D3, - 0x4A492144C916FF5E, 0x17B608B15FE0776A), - }, - // (2^65)*B * 5 - PointAffineExtended { - e: GFp::w64be(0xFC53FE544FC97617, 0x7C6E45CB4F8BAF6E, - 0x45F9DAE439A8714B, 0xC31D91C68F16778E), - u: GFp::w64be(0x1EF7A8EC7BF85F92, 0xDAF1DD3F71255277, - 0x9513DF1A0B12D0CD, 0xEAF27DBC1F1DB0A5), - t: GFp::w64be(0x281B2C7C1D93FB30, 0x82EEB2840A6DF5D5, - 0xA52A14600DA39F17, 0xDEFC0A3E8E1432C4), - }, - // (2^65)*B * 6 - PointAffineExtended { - e: GFp::w64be(0x0E62A2BB64878922, 0xA65ABB0CE1F01B00, - 0x716DD76B3EDFF4C8, 0x33396BE7482C0C5A), - u: GFp::w64be(0x5BC81E17258A41D8, 0xE5D58D07D692F0A9, - 0x76C0D3118F6E2399, 0x2782AEE3F95691AA), - t: GFp::w64be(0x58437CA9095317C0, 0xF8C5B1557FAD6FE1, - 0x844E5961FFB83907, 0xC3DAE9566914D1F0), - }, - // (2^65)*B * 7 - PointAffineExtended { - e: GFp::w64be(0x12C110144498FC33, 0xFCC9E289547278CA, - 0x1BE741A0ABF9032D, 0x7C4DDC9F43A4C4DB), - u: GFp::w64be(0xF535ABDD686E49E2, 0x573A47E388781B68, - 0x28FB0867FD72DE01, 0x4047BE745D6564B2), - t: GFp::w64be(0x73326A00C74AC27A, 0xC347396D67A805E9, - 0x37145C9E2A276BA9, 0x45F50EC92D1CD389), - }, - // (2^65)*B * 8 - PointAffineExtended { - e: GFp::w64be(0xE01E1A5273E1CE54, 0x9AE6B77BD00F64FA, - 0x0959AF4A7CF51F22, 0x752A8A999FD09C5C), - u: GFp::w64be(0x1F545DF41EDEADBF, 0x890CB0106EAC6AA7, - 0xF1F8649758FDC39C, 0x1B42B4A56D3F3C23), - t: GFp::w64be(0xA13204B9F3070657, 0xB0BECF5EE24618D9, - 0x2AB188D1269970DE, 0xECAA4D2688AD2D7C), - }, - // (2^65)*B * 9 - PointAffineExtended { - e: GFp::w64be(0x268B4B3C8822F0DB, 0xC57ED7EDA80A7D1D, - 0x3AC4AF7DE0594B90, 0xC7133350BD661C7C), - u: GFp::w64be(0x7162DDA9D57EC5A6, 0x12F555216AC05812, - 0x08ACEEBA66D162A8, 0xC2BB222D7813AA60), - t: GFp::w64be(0x666CA696946037ED, 0xBFBDD07A05BD930B, - 0x64EF435B0A3F9863, 0xCBF020442D500465), - }, - // (2^65)*B * 10 - PointAffineExtended { - e: GFp::w64be(0x221CBF34FB8DD9CD, 0x76D34C6B64D6E23D, - 0xFBF4CDEC92BA2C54, 0x94634AAFACFF3270), - u: GFp::w64be(0xC9255DE3E4400E13, 0x61011A2A33AAEBC3, - 0x3A57D9F4B4C1C8E5, 0x96F63FEFD739E8E5), - t: GFp::w64be(0x5F18BF1D4EADD054, 0x6ADB8FE4D6F68272, - 0x6767096B62C4D2B6, 0xBD3F631857E3087C), - }, - // (2^65)*B * 11 - PointAffineExtended { - e: GFp::w64be(0x1A70CD5054F3F5AE, 0x2C48D260CFAF1DF1, - 0xE6AB4EC3F5AEA380, 0x57737AF96AE62D2A), - u: GFp::w64be(0xEE4AB762003D61F0, 0xD09763BF3DB9D6E5, - 0xD4A624B5584ABAC1, 0xF6EDB0940F72037D), - t: GFp::w64be(0x84781682D93F5398, 0x139F5F92FCBC72AE, - 0xF7713B47F0FC1561, 0xBA0F42D58A82DE36), - }, - // (2^65)*B * 12 - PointAffineExtended { - e: GFp::w64be(0x5BD008669F54BDC9, 0x867835DBF6858321, - 0xDF7A22F47DC8D05A, 0xFCF5CBED236C4B26), - u: GFp::w64be(0x47BBD4FCBBB69BD0, 0x52E03ACF56DEE0FB, - 0xD34361B07D9ADA04, 0x33BD2D271B14BC99), - t: GFp::w64be(0x0E18C9FE204CB892, 0x539108625BE3C633, - 0x3F9848454D249713, 0x7AC63BCBCD5C2FE8), - }, - // (2^65)*B * 13 - PointAffineExtended { - e: GFp::w64be(0x05B06EEF36F58443, 0xCA797231B630DC0D, - 0xB21AFCE239B7D9E7, 0x3157469822032A95), - u: GFp::w64be(0x55BB0E803788C9E6, 0x3254DF6FE4FC5108, - 0xD208CAC0191A85A8, 0x638B82FAE9D24083), - t: GFp::w64be(0x5552926512C8908B, 0xCD7BFD2C5DACA89F, - 0x35D0E3CE5A2F8B88, 0xA501A30E6A223857), - }, - // (2^65)*B * 14 - PointAffineExtended { - e: GFp::w64be(0xEF85B3FC2E675AD2, 0x919F55C3F9CD9C9C, - 0xBEE5520B2B952947, 0xA4825DD177C5CBD0), - u: GFp::w64be(0x1BFCCFE6DEB3A97F, 0x6FB2718D7EF97B9E, - 0x4239E7CEB1C7C81F, 0xF417AA29B2DE781C), - t: GFp::w64be(0xF30C0882C4893D3F, 0x43A4ECA2175718EA, - 0xAB2C434C361553B9, 0x35FDC838E8CC36BC), - }, - // (2^65)*B * 15 - PointAffineExtended { - e: GFp::w64be(0x1DE3F7AE3173F0D7, 0x4934A70A4BBE73F8, - 0x487B05A92496F7E7, 0x82B70E822A17AE76), - u: GFp::w64be(0x8392C57AA97E0EC2, 0xA281466C3C92101A, - 0xB3702F0D63EFFF9C, 0x37DD333C15E66B69), - t: GFp::w64be(0x020D69BD2B642826, 0x6EB4EC849EB44275, - 0xA7A54B372EC3AC21, 0x8B7ABE779B43850B), - }, - // (2^65)*B * 16 - PointAffineExtended { - e: GFp::w64be(0x4D6DFD89EB9168EF, 0xFC5A080B0BB337A8, - 0x712A710587AA5420, 0x53FDD0257A7B7A78), - u: GFp::w64be(0x293663EA0B23A093, 0x8D2F9B2C13A87673, - 0x1D2B5FF52AD3A776, 0xC26EDB8255D3A269), - t: GFp::w64be(0x15CFBE1720B675B9, 0x604B42B307DF0AF0, - 0x7643FD813204FEFA, 0x6F1A749791AEF12D), - }, -]; - -// Points i*(2^130)*B for i = 1 to 16, affine extended format -static PRECOMP_B130: [PointAffineExtended; 16] = [ - // (2^130)*B * 1 - PointAffineExtended { - e: GFp::w64be(0x599C4377D2973838, 0xF3E54EE7A02CECC3, - 0x321225E4D3C4456D, 0xF1942D6DCE7085DB), - u: GFp::w64be(0x37066E03AD437E47, 0x4892FE5F44D39462, - 0x286FB241A7AF021F, 0x7150D6CF2CE6355E), - t: GFp::w64be(0x4612185911E38904, 0x17F41F57645633B4, - 0xC38F94B06631402A, 0x918759F71568E2E7), - }, - // (2^130)*B * 2 - PointAffineExtended { - e: GFp::w64be(0x445408F2E99D15B5, 0x86A3AD9B5ACF58F7, - 0x7567A424725542A4, 0x717B483B0F757F8F), - u: GFp::w64be(0xBAF161489E698CE8, 0x18A143A6EA22BAB1, - 0x1CAA7109B672B1A4, 0xC0621919145BDD1E), - t: GFp::w64be(0xBCFF93FABC843A97, 0xB381CCC83AEB5310, - 0x05E0C47429E73F07, 0x84E549B9CA57B9CA), - }, - // (2^130)*B * 3 - PointAffineExtended { - e: GFp::w64be(0xAF7C6EA0FC0D30A2, 0x5D9BCD4F320C4B87, - 0x4C477E9ECC8F6447, 0x178BBB97F2F2BCC7), - u: GFp::w64be(0xECC2A9207D5B8448, 0x7E4FAFB874FC7085, - 0x8A7C4AD20B3CAA3E, 0x6CF12D9B9B72363F), - t: GFp::w64be(0x435C6028ABF356FA, 0xAA6EB56A649B93F3, - 0x8C4FE6AA93EDCB6E, 0xA716901AE4F580AE), - }, - // (2^130)*B * 4 - PointAffineExtended { - e: GFp::w64be(0x3E8E5D21B8539A19, 0x2550BE0E1ADE1DEC, - 0x3E3CE3A3CCDF3D12, 0xA87BCBCD3817E1D1), - u: GFp::w64be(0xD04B5F05C6FB1512, 0xD679BC7F64A6DFAB, - 0x58EE4B900F24E654, 0x2B4B103608FEDA69), - t: GFp::w64be(0x3941C0B876396D74, 0x174FC9FC62636662, - 0xA72377E9901282AC, 0x9413BC10CAFDE6F9), - }, - // (2^130)*B * 5 - PointAffineExtended { - e: GFp::w64be(0x129D8493A46BF962, 0xC197D15E465EC6A5, - 0xD2CAED56DA8008E8, 0xC1D9F92C1E831264), - u: GFp::w64be(0x09EFA132BBAB6DCE, 0xD482283BCD4DA1AE, - 0x24D49CB871B24534, 0x7BB50533145DB021), - t: GFp::w64be(0xE646C9871B6E2B20, 0x334FBF780148A1D9, - 0x214E0BF123CF4776, 0x20C1B63A76066F2F), - }, - // (2^130)*B * 6 - PointAffineExtended { - e: GFp::w64be(0x00975FA87FFA507C, 0x06FE045808A8ECCE, - 0x103CCCB8C23D5F47, 0x03A169E8F1BFEB6C), - u: GFp::w64be(0xCBDF2D4583E049D8, 0x129D1838ABE4BE9F, - 0xF7A3EC0BF7EA367B, 0x21EA2AE371837CB7), - t: GFp::w64be(0x17630878A37ED39D, 0x753B964379F0696C, - 0x074EF904A130A120, 0xFDCEE6C7C512A495), - }, - // (2^130)*B * 7 - PointAffineExtended { - e: GFp::w64be(0xE49CA1DBE5285EDB, 0xA86360DF816CCF94, - 0x82FE5EC77D7A78F2, 0x8F4CE0230491C607), - u: GFp::w64be(0x05870E9772B6798E, 0xC3651674E664BC33, - 0x48914008FBE7D16A, 0xFD1CC728BED4BEF2), - t: GFp::w64be(0x1B064E7966C46F50, 0xE3107252BDC9A37F, - 0x74B4293E9B116BE2, 0x22428B4E9C3582DA), - }, - // (2^130)*B * 8 - PointAffineExtended { - e: GFp::w64be(0x8D17CC99B691C798, 0xF138272E43AF51B3, - 0x88BC068A2C7577B4, 0x9C0972293FA72FC6), - u: GFp::w64be(0xC7584E6298F900AD, 0x862BD9E6BA32D533, - 0xD826B29AAD0F0900, 0x19C5844E2885C378), - t: GFp::w64be(0x4250016E4DD33706, 0xE4A9F4CC99985391, - 0x441288A875A42E15, 0x3F09EEDCB3B53DEF), - }, - // (2^130)*B * 9 - PointAffineExtended { - e: GFp::w64be(0x640BF748916DDCE6, 0xB2F7BC0D75ABCF59, - 0xAD3156C01169881B, 0x5AD5972B02D52E66), - u: GFp::w64be(0x13DF69066BCC4C1B, 0xF7D30B6E1A392338, - 0x3FD2D4476F607F07, 0x90DD6EC51A170783), - t: GFp::w64be(0x20791F49E12CBB52, 0xFF9B67206781A6C5, - 0x4EDF7AB48F01FE33, 0x29D25110324479E2), - }, - // (2^130)*B * 10 - PointAffineExtended { - e: GFp::w64be(0x0D13CB0B83A4E436, 0x215A090E23E009BA, - 0xB1E7AD6E83BA630F, 0x1076716277FE965A), - u: GFp::w64be(0x8B8F72DEA9CB6892, 0x51468E721EB2F0C7, - 0xED7FCE377222D475, 0x98A114985034DB0F), - t: GFp::w64be(0xCC7888B5D7F7EF38, 0xB1E8BC2FCFCBCCBD, - 0x3B5CBE6565AFC603, 0x4E0AED79FE591565), - }, - // (2^130)*B * 11 - PointAffineExtended { - e: GFp::w64be(0x2C1EB2458B6E338B, 0xB9A07BA5C0E026D5, - 0x8869FC1062CB6E15, 0x24B295671CEA742C), - u: GFp::w64be(0x1A63AFF1EAEA17A2, 0x377A180210DE7B45, - 0xFB5FF47A4311102D, 0x59F5C1F87FCE791C), - t: GFp::w64be(0x9AFB1CAA349EB703, 0x0B44889E03D380C1, - 0xB6684A67AC106FEE, 0x060C6F83F4055B5F), - }, - // (2^130)*B * 12 - PointAffineExtended { - e: GFp::w64be(0x7EA4E5F700925A9C, 0xDD939FD79D89B312, - 0xB1F9181FAEF153B4, 0x9A1176B1DF2DE18B), - u: GFp::w64be(0xCCF7B8C9F78AEF40, 0x5F3C905CEAF1DA81, - 0x6332D28A1772F312, 0xAA27601DCD047263), - t: GFp::w64be(0x0AC9F9EBDA208C1A, 0xED3130BDBC5E98C0, - 0x761D3CE91C6614FA, 0xB27CA46016747DB7), - }, - // (2^130)*B * 13 - PointAffineExtended { - e: GFp::w64be(0x8606E25235257F89, 0xA74113BEFF43C49C, - 0x3C197D7BEF53A193, 0xBB9E9D74F8C48374), - u: GFp::w64be(0xFEBF6136BC0F2898, 0x5950CB04DF58C68C, - 0x6A562AB45E4B0E95, 0xE051AF6018F320D1), - t: GFp::w64be(0x0E2C80FB7A428078, 0x334120BC0DFC1AB8, - 0xEA88C380F1EB599A, 0x24FC6308FEE4A0F8), - }, - // (2^130)*B * 14 - PointAffineExtended { - e: GFp::w64be(0x82EB6B222F622C21, 0xB518819B6F6D4CBE, - 0xE28F8AF2712EE245, 0xA4B264991C0FE6D6), - u: GFp::w64be(0x9CEC0B4941178C7A, 0x333309ADB8AEDB75, - 0x61C189D7300547CF, 0xDAA937E63DB87B29), - t: GFp::w64be(0x6D5F943ECE38CCAD, 0x7446D4E47D7F5FF0, - 0xD05016EDC25F88EA, 0x9C12A02FD92E5065), - }, - // (2^130)*B * 15 - PointAffineExtended { - e: GFp::w64be(0x705C40FAF4E18D73, 0x10F2EC67589314EB, - 0xDB806DDA9FB6B721, 0x86CBC4225CD1FD01), - u: GFp::w64be(0x7A241CA54226F148, 0x97D945AB88735A1F, - 0x1CB387861F1BDEF3, 0x35DAE05979E12A2F), - t: GFp::w64be(0x0FC1714DE936EA68, 0x12C3787FD7DE6B8E, - 0xCAA288E681ED4674, 0xA1CC22F3C9D8CB6D), - }, - // (2^130)*B * 16 - PointAffineExtended { - e: GFp::w64be(0xFFA87BFE9791DEAC, 0x61D4DD21759CD85B, - 0x2FC5DDB17FFA38F1, 0xA423F1C1F35C1764), - u: GFp::w64be(0x6C800341A6E661C9, 0xA0240AC06CE8FBC1, - 0x2F19E1987996463C, 0x3B03D19EFAF0FEE6), - t: GFp::w64be(0xC943E2CAD4B644DA, 0x967C6AE6BC2AF126, - 0x7193EE118EEE4A18, 0x4D95F9D6D1F00E02), - }, -]; - -// Points i*(2^195)*B for i = 1 to 16, affine extended format -static PRECOMP_B195: [PointAffineExtended; 16] = [ - // (2^195)*B * 1 - PointAffineExtended { - e: GFp::w64be(0x6984FA5FD8351EA0, 0x3C4398320B13EF49, - 0xFC59A8F1266F50E9, 0xCF10980DF8CF9EDE), - u: GFp::w64be(0x02CC8D1FDA2B07FB, 0xD20D750B3AE1C420, - 0x923939431B7F9DB9, 0xC1A8F6E289019BA2), - t: GFp::w64be(0x8361F125A136D250, 0x180402D298121834, - 0xAC8E6CB697983873, 0xCD9FDADA4925AEC3), - }, - // (2^195)*B * 2 - PointAffineExtended { - e: GFp::w64be(0x38E076EBA74B437D, 0xC63F5962092C1E46, - 0x1485DE2D7EEE4D3D, 0x4DFD533DC1FF57E0), - u: GFp::w64be(0x32DA524D8BD52738, 0xDE320853BBF83BC9, - 0x559486F8C4A88104, 0xF0FB693DC3574BF3), - t: GFp::w64be(0x5289F57EB56E777C, 0x84DD59578BBF7C8F, - 0x53C3A0CDB00FB77A, 0x7F9757CB8A8C1C17), - }, - // (2^195)*B * 3 - PointAffineExtended { - e: GFp::w64be(0xB98305439BE27E00, 0x04AB543C070A5966, - 0x6EDE5FAE6D30A747, 0x7C295A0301B9DA75), - u: GFp::w64be(0x7D7CFC1BDA86A30B, 0xB46B89EAFF81A28C, - 0x1363263FCB83701C, 0xEFE042A19184F187), - t: GFp::w64be(0xBA4362498B6E714A, 0x754A609AB4C28D21, - 0x2DBF2B8F0E9516D2, 0x6BA8BB6C12337B5B), - }, - // (2^195)*B * 4 - PointAffineExtended { - e: GFp::w64be(0x889772BBC6C40AA7, 0xE381B59F5BF7148F, - 0x04CA1E55CB2DE24A, 0xA91E1CC942CAD3F0), - u: GFp::w64be(0x01716EA549482B46, 0x4C53695136CBAE9C, - 0x690396AB4D863C92, 0xD31E11D887EB0CB5), - t: GFp::w64be(0xDD0C2E561772646A, 0x8CEF18811B8625CB, - 0x297789121EA0FF01, 0x551E1B20F06EDD74), - }, - // (2^195)*B * 5 - PointAffineExtended { - e: GFp::w64be(0x653B33AAFCA82464, 0x9755FD41B62F092E, - 0xB2D670AE39B8E81E, 0x5CFB2084143F8B52), - u: GFp::w64be(0x7BA5B547CC9C6A13, 0xA482B77085AFCAAC, - 0xBB58520E3DF0E2FB, 0xE5D6F4173CD79837), - t: GFp::w64be(0x4563268B3AD3E9BA, 0x23097775D4CACA0F, - 0xC02D734442921CE2, 0xB71E96D50C6B536E), - }, - // (2^195)*B * 6 - PointAffineExtended { - e: GFp::w64be(0x8A79BBEC30894DAC, 0x4D01C101C6FA6AA3, - 0xD174EEFC5FFA4460, 0xA0965CC26E9DDA57), - u: GFp::w64be(0xA4B47D75B3DFC2A4, 0xB4A30A9FE39834F0, - 0x67D9C1776C85A54C, 0x223FEE4A89CF4AAF), - t: GFp::w64be(0x895682286425ADC2, 0x037CABED9C0A4ABF, - 0x06A81F003EC1F2A5, 0x032853CA2951B4ED), - }, - // (2^195)*B * 7 - PointAffineExtended { - e: GFp::w64be(0x0014B50FAB28038B, 0x87FAD7F230F573A8, - 0xEA250773B4DB6EC5, 0xCE4D3232A967558F), - u: GFp::w64be(0xFB1CB99F0CAAB8AC, 0xBA5C81A062866457, - 0x64934F9254624ACB, 0xFD12417A55BC99BC), - t: GFp::w64be(0xE5B2C18A29CF8067, 0xF3DF6B4B3FC252C7, - 0x3E9F6892577B7EDA, 0x51EE4620496AB523), - }, - // (2^195)*B * 8 - PointAffineExtended { - e: GFp::w64be(0x744D3431548799D2, 0xD71FAAD51F8F6083, - 0x1B15F92A5507849F, 0x3A22C32BBCB51589), - u: GFp::w64be(0x24D2F6FB70AE82A6, 0x62F7AA2B7321DEF7, - 0x77DF9C6A5189B43D, 0x196C644C3A7C5AD7), - t: GFp::w64be(0x898A06157FAB9CB6, 0xD0769AC548228A77, - 0xDC2B915A54C01DDD, 0x87ADA3B969455F6E), - }, - // (2^195)*B * 9 - PointAffineExtended { - e: GFp::w64be(0x95C89D58CA789F41, 0x3D4D51FE24E6400C, - 0xDBAB19F15A12049C, 0x4A8B9027455C0BE2), - u: GFp::w64be(0xED7F0BC5959EB2F0, 0x285DEE80EB6DB207, - 0x93D373617509D0BE, 0x693E3ABB506C1C98), - t: GFp::w64be(0x0246C92D5B5C5F3D, 0xA832CE5EEFAF7348, - 0xBB145B64501BE12C, 0x9660D84323548454), - }, - // (2^195)*B * 10 - PointAffineExtended { - e: GFp::w64be(0x2D586E0E6547EBE5, 0xB657DF427E9C997C, - 0x5A38963A20CF007C, 0x73A9DE0FA1326A43), - u: GFp::w64be(0x12F7B0975D2C3A1C, 0x35CE861F4E6BA5F9, - 0x1465902198E5E3B2, 0x25C4F5FC601885B1), - t: GFp::w64be(0x73A07E46C2432EF4, 0xB0693FF0C900FD8C, - 0xFEF97E54B1F20E3E, 0x7D584BB099AC971C), - }, - // (2^195)*B * 11 - PointAffineExtended { - e: GFp::w64be(0xF79F1715358223A8, 0x649FB6D3708C796D, - 0xBBBE86301ED8BB55, 0xB75B0675BC8FC788), - u: GFp::w64be(0x5798ED78E0844447, 0x03E591E48B574455, - 0xFEBA85E2BC751276, 0xC708E8F1FE871265), - t: GFp::w64be(0xD2B33B8694701D00, 0x3AAE74E086F94633, - 0x033DA2C81AC9BB4A, 0x97BE717DB1A31E22), - }, - // (2^195)*B * 12 - PointAffineExtended { - e: GFp::w64be(0x9F1F814B3FE1549F, 0x3C7798245BBD42A8, - 0xA816B52283543620, 0xD8081CA0F0463D37), - u: GFp::w64be(0x322DE3AA00D07711, 0x1E153428374F1AF6, - 0x0D6CD8F01E40F52E, 0x0A415396E1CBEA98), - t: GFp::w64be(0x4C81D23DB00B1815, 0x22D959966A0994F8, - 0xE2967682C3174969, 0xED9E5F3990904AFE), - }, - // (2^195)*B * 13 - PointAffineExtended { - e: GFp::w64be(0x402CE80A875D3C38, 0x4445133534783D6B, - 0xD2F8724DF6F2DB7D, 0xA9CA3F2B02162B06), - u: GFp::w64be(0xAB72FBCC3E718D31, 0xD050203D0B58CF41, - 0xA5F0B57CCBF63E85, 0x015C076F84296789), - t: GFp::w64be(0x20F7153F9489C4C0, 0xA9ADF270E8139E3C, - 0xCA05B0128461403A, 0x274E849036BF75A1), - }, - // (2^195)*B * 14 - PointAffineExtended { - e: GFp::w64be(0xAD97C35EAAECF043, 0xF4A0AD684580E0A7, - 0xD26FAB309D2D467B, 0xD27B13A4271428BB), - u: GFp::w64be(0x683B56B117B47018, 0xA7A03A86A3B17F1D, - 0xBF00911330B3C911, 0x404FEB685E3FACB8), - t: GFp::w64be(0xE301B17564CA9EBF, 0x0A2AFB98632179BB, - 0xAED38A7E52C0393A, 0xB08422ED055CF6C3), - }, - // (2^195)*B * 15 - PointAffineExtended { - e: GFp::w64be(0xC35F4798670C2E9F, 0x959A6E5BBD688678, - 0x8D4222C5BED76FE6, 0xFC3C1A1FA9B742B3), - u: GFp::w64be(0x58C51D0A2D22312D, 0x33AA528283AD3C82, - 0x4F27E67934B55D1C, 0x9F9F9B98D538E40D), - t: GFp::w64be(0x5B5429BDF55E274E, 0x6AD507525585B0A4, - 0x8ADE3EC013E0ED0F, 0xAEC066CA3945EA02), - }, - // (2^195)*B * 16 - PointAffineExtended { - e: GFp::w64be(0xA3AB8FB5AAD2C9CA, 0xD16BA675043D232C, - 0x3C32565F120B2BC9, 0x789DE4775664C2DC), - u: GFp::w64be(0x09608F66D036E2B3, 0xBB35549B10798986, - 0x82D6DEE66D5CCEE5, 0x33A76457A70EB1B4), - t: GFp::w64be(0xE78DE6DD7E96C507, 0x078DC64B62F7176B, - 0x9846BB38449E6CF0, 0x2EEFBC620D80CD86), - }, -]; - -// ======================================================================== - -#[cfg(test)] -mod tests { - - use super::{Point, Scalar, GFp}; - - use sha3::{Sha3_256, Sha3_512, Digest}; - - /* unused - - fn print_gf(name: &str, x: GFp) { - print!("{} = 0x", name); - let bb = x.encode(); - for i in (0..32).rev() { - print!("{:02X}", bb[i]); - } - println!(); - } - - fn print(name: &str, P: Point) { - println!("{}:", name); - print_gf(" E", P.E); - print_gf(" Z", P.Z); - print_gf(" U", P.U); - print_gf(" T", P.T); - } - */ - - static KAT_DECODE_OK: [&str; 21] = [ -"0000000000000000000000000000000000000000000000000000000000000000", -"c6754db6934cc2d29b2e79fbaa307c28b203c09b4da579e7a859efd96f00b9f4", -"8201c50a097fae5f05409b39f3cca443cc61a64be370adf34e163b70dc27224c", -"fe13705fba513fddcbe17413c5a433d335d60f92bbea5dfdf8e22b5d121beb11", -"66063ca155b48d432788d85b6b6b4ba4d06ec5e9300b34d1eded82d8fb2b772a", -"1eb98b682d8032524565a03cbdfd06f1bc8f1fb134a22b909572a6ae4170388e", -"d934a16d351714d7e3473326f105ef8175b20c52558ba5f1f7b215ff34fd95ea", -"b69a35030f2bcca39ed6de56b7904f715df631c52b6104da149308eea0f49948", -"1ddf6cfd9bd0b3cc6de021c87a414c03cd10d035323bb3d937b05d86e7495e3d", -"f92d9f63ca31906e4ce659538a0165be2dbe63a75704c622e4fa6dd3d26a39f8", -"1413f10fdc7d19a68822bb08c0cb647c4a6c19ba2309d7d4e133bdd060fe316f", -"39d0a8abed2cd6f291b86f7c2842a2f4f59a92f8080eb7f7e14c74784718a36b", -"60f093f064a1fd252c0b9daae4c806dc38216bebf850a5643b906ea5880c752b", -"0e6db31ec74c8f31118930563a1d2b1557d9fef6a25cef6a8a721a6895f5f49b", -"d904ab90469e7f6befdb354b773cb2e442f801ae8381305a793cd0a9ad13ada9", -"36dab53b7a197f1a4ea68a09d6cc487a2bf3abb429b3dc6f1744c614aa1fe55c", -"e6ea9da7e62d6da3181ea8642748023ba81ba8e130a7f0e47a83b029466ec167", -"a454b7cbc0a06c4c98c531fe4bb421ac7dd8ff46ab43d504e63cc5118d043f7a", -"7d22bf28fc58e6ce0683d846fa25563a2c47b7c106dcde16a1a209170bc5c305", -"d2b83f15a10d977755b81362ec3f6f148a3b643f659d420fd2c7d99e6ccc67f4", -"bbfa488ec411f838ceebefb9ba9ae5a6b131d150520ce2204abe1901dff682ca", - ]; - - static KAT_DECODE_BAD: [&str; 40] = [ - // These values cannot be decoded (u is out of range). -"235961fd5592cd20258a96cd03c1afacfeffffffffffffffffffffffffffffff", -"a56d04c7aebc3127c5df6157b35329afffffffffffffffffffffffffffffffff", -"90533b37dd53d45ecf00627df5b7faa7ffffffffffffffffffffffffffffffff", -"3218d5b5473afc6984e4554729f22ff4ffffffffffffffffffffffffffffffff", -"dadf0df44a3be475d756794b454658d6feffffffffffffffffffffffffffffff", -"e151f28c8ed00d21d557b76b11daf499ffffffffffffffffffffffffffffffff", -"418385828f72962f6404b0a08029dc7dffffffffffffffffffffffffffffffff", -"47645fabe37b97962befb6d188b508bbffffffffffffffffffffffffffffffff", -"2e49e30b1b03a00105a62a5aa6a519befeffffffffffffffffffffffffffffff", -"65fe372dd725232fd6303e8933b03645ffffffffffffffffffffffffffffffff", -"665eedbf9989bcb4c695cfe8dd996ef9feffffffffffffffffffffffffffffff", -"ef14ec27af905f379b6ef3caa2d6f5e1feffffffffffffffffffffffffffffff", -"d8e89f216567a9743e1f7f68fc913edcffffffffffffffffffffffffffffffff", -"093130c0e322f7e7b7a5bd16719d4cf3ffffffffffffffffffffffffffffffff", -"b2c8f6f4ec0f6638520c772044a5a7d6ffffffffffffffffffffffffffffffff", -"fad808cccdc35eb9fa3b591bfcfa7b6effffffffffffffffffffffffffffffff", -"2259b4e2c61af9c1990359c02205228effffffffffffffffffffffffffffffff", -"20a3bd12a50b672459ffecc0ed2247eaffffffffffffffffffffffffffffffff", -"b03b2e17e596cba7671b3ce5ff4d21ecfeffffffffffffffffffffffffffffff", -"97dd9d4c71381cac42ec06bb034df367ffffffffffffffffffffffffffffffff", - - // These values cannot be decoded (u matches no point). -"3b27b892aea70a9d859859afce365227423d1c04d307c7aefb6dd9a92f7667c1", -"1f8b02d2e5bd3dae8b57948a43ffebdbf9b77818c20503c288e3635bd3140115", -"f638a23bf95e009abd86cbddbe2b481f056ae7fc81aa74956d6db831aa90a679", -"d71ab1700fbe4b800751a1d2c122aed56eb2442ba927859041bb90750c94c965", -"4dfee18430d7ec6933c3b06e9932ab989bb612d60a905df62814c4f4e053e32a", -"00676c87c413dff2bebf80f7a316b1551b60aadcde350efc6a086afc267ea1d5", -"e1b69d9e000ce5b3a3b570df5b977415487750d23a400fee1ba804f31d8a4112", -"fa7886e7de2ba4aa5b8633a5d08d1454eb2296d18ac487ed4cf64b0c3ee9a14e", -"0076832bc64ff67c5c725314d7e5d7dde8b314af54f04ca14f71ca1e4d8a608f", -"115bbce7aebb73d00cb4de7c551a69e393a6cb7afc80d99bc5bcb5cc730c7dfa", -"32d17cb1a307eed90fffb7fbb7c3a65a255105da3ecc153438220747128c7ee7", -"f0fecceb7fb9a43fb723d4bc3cdf7d6a40667b98ce27571ecaef9973abd772cc", -"76c61415e10f9706a192be0dc66a57723bc0d905912ffd00d213246432d5ac06", -"b6f17b5d034b6f58d8fa9d688d08106a31c184899693549cc20cbd471d0f0663", -"7c9f543fb2c6cef6b6df833904ac68f4448cf0eef058c8c6b90b876e34ae88b7", -"dd05138f6cabd98462e1c7f08dc64030d8bfec5ba66f685faf5c432254d73d92", -"e704bbbed5f47ed6395ad19d085a930e044df7d6a84bbc0d62749de14adcfaf0", -"fc5ed469902b7380a9526cb013500f6f3f9659166fabfae7c4edb98dc84b6f82", -"b26866c53049cc7ffdd3b1643c382874318182f7a11151943834883f18aaf5e5", -"ab44f95742cefd7c44a08bbc84483ec731880edfd7f79c2a92bd905da00bbc03", - ]; - - static KAT_ADD: [[&str; 6]; 20] = [ - // Each group of 6 values is encodings of points: - // P1, P2, P1+P2, 2*P1, 2*P1+P2, 2*(P1+P2) - [ -"cbc7ed07ca508e0a3fddc7f13df77c7d6884f6fd927bcc2687aca8a8fefb2a1d", -"843952261fae4a4805e42d0f9552627950ce7aaa376fd4c469760473e95bf930", -"a7d2492c3866dbdb2d17b96aa92e7290b27c837b2541eeae8149f59042e46fb6", -"09d52c8b2bcdd539aa3e48a7313a2644791f67545124bb5b5606d6f1f19657cf", -"7b4aa4a5cfb8f182ef1a393eb84e37dd770d3c32f9bac9eedf157afb43ffcc37", -"9bb2666fe8b7ff6d170d303572dac48a00dbc50fac6d8799443f31d283a2a051", - ], [ -"d1bef9e773dc68484441909e5971a884686b8edee51b26c28111e57af9e5570b", -"b117087f3c772c5a190433ae5c0e0a9698d796fef6213415b743fb2644a98afb", -"7ebffcc3f2f3451b80f494f5200f45144b2e67b2b3e6ecad84e45aa7c05a8b74", -"02fad07bf13c94ed3339467002296fac454869691dadf97d7b394b02baec3a25", -"98ce1e834461222536f4c94b3c2392f9f385399fa3f39b1d41a9b3c834ece64f", -"d2defbda27a919574a971e4f7ec753b3251301dc32cd78fad21c77c7d20d4936", - ], [ -"e079c4fb6fd4374ee86faa5eb417587f769944786b6c8e36553c3d4b8b0ac768", -"6a9b495a5d84873c00ef653636aacda5cc6289af0ebe7c1233922661a6a144e8", -"7371a50013d72f3dc83c89ee85d0c217c03e1fe697dcf7e74cc32c301c526e10", -"dc03aeefef480151fd9caa88cd4920c9411e81f00d3a08594b51721547cdb088", -"97accbb5d7b919ac6ce9305f361bdd8988bec327cb85b96cafd66939a274f73a", -"ec51cf87aee58a5d08e6f338bb04041d1f39ca50b30d24c807ddf700fc52b126", - ], [ -"ea37c1a47e824ef49667fa52056c7156571751f1831b8bb6d9f4ac649e0c6deb", -"101788eb55d2ceb044f976f29f3c4e76c59782d26315babf11708ba51ee05f68", -"9efa6da7e6a0b79fd63b10662717b274a80f609a0630cf467bc9aa60dfed679c", -"8bf94069545a93e8be176288b80e87f557eaa7fe93ad0b18d3ed9348c52b4e3b", -"6a048026f6b645c13c4bfa5fad87950afd032f4cf957df40ffab89c2973996e3", -"46433a0a68095d4da95c368d05e6ede3f4ee405db90565ae09212e1c12cbb205", - ], [ -"3863af3eedd641e9de136269f4d27fcb0697bd2a3ffc07cfc1ce56a4f7b7b48c", -"90966396a34be1c135dd9a40f4ef79fd629dbcf3532b1989095a239f1575cdea", -"d4dbf804f163a7559e0fa32724049286f26cb87428c67f91e345f534254a6d11", -"4069b52edcbba08d2fdc73fa643ff8df07a3073a29d291a71d63185b0f0a5b68", -"f983accbdfb2ffadcba6391e812767f77a57ddcfe776c1daa8d9b6106a180953", -"f9043c2ff98df57b77409898a5c13e609fbcf1ccf6395682324e9c926af2850d", - ], [ -"e25fde1be148cede03e413389d9c3e272677c05c07d8232a721c36c7198e4677", -"a3b5544e8b1045d7e8a8894264b143c2bdba74722471b4aae1a0e84b8756f4c9", -"fa423f9fd12fdf2d2d46d3cadc67142934e5b17eca3394f37c5fc651e5eb70f7", -"698b86fa8570f4cb7f871c11dd51787245498f323f9ad4fff0e4d6f1c19f20f8", -"31411319922530d1f73ca20d2b641b0f08d8ee5cab6be576c0cfd9bc4a2e7596", -"aabcf7cac268d3651b59069cc69c8893089539dc41c899a262937dab5b0e00b5", - ], [ -"45427bea9bad14dee6c8aa2de29f7e11db2f0c2046a37cb60fc3437058eaf6f5", -"f037493aa712cbbc276e36e9500757bf8c01740ab866ea622204bdad37d64477", -"f023750cd0e793a0cee8b06b516a744dc9cce4198af20be7d0965adfe23e5a36", -"d54d8a741a10593098885747bc45920e7f6f6d3fc726b479a1ece7eaa40c0d29", -"c084c31bd90cf873ab98141c9cd1bd52822ff5ecb0ba3ba1cc2b1e9ca865b5f8", -"869b93bf94e4ce7db22a946f76b25c3e8d88c4cd9e72e91472eeec2c9ac1afd0", - ], [ -"3f04b543ecdd86b33ed4bc2d5a3512fad86a62c9eb71b7e2e8c394fd2c383bf8", -"88928f249697dbf12992f30be0f60e148ff206b2ce4ee0cfcb47237f03ce9e7d", -"b293b18365ec537e9b3671f64a91f0d81eae0f715f6f2db439a6544d1a025d15", -"1141d88e5ff114d9d7345d1922e877c211bae8c7e3ce7b5050b41ba26a0ea74f", -"2cc09cfeed5a7b986e01621780dab8a344108396f99bd28755873e6e67131759", -"2986c2c5d9eb473df1a66a17b462d1f0bc08511c658e14cfc915c4ade2a9bca3", - ], [ -"b199a5959c2859c12f9a4a8c3bab636533fe9c0ad4b1ce8256420c12d8f3c78f", -"310d5b48808dc94b09ccaef632228d7d1ada928ef228e393a8db1a1879d19989", -"6901c7e4b94a201dd0424f54c590745e895db35406c07d348d368c4d0c4b30cf", -"7430c11db3c60a7d553af1ceec5f494bc21f3c082b11347f2079559bad733bdc", -"4f51359cc701e97b65a1715c8c57cbaed68e430884fe9c7686e724beab81abbe", -"3dc6cbebe5837ab2bc3e9a73d1a27af98d232242dbeeb99de4fed9bf0b61114d", - ], [ -"728dc259fe491814ab224e98a5289e0438ded7cc4f6d782a4e001132b7d69997", -"4bfc61d53e69127d6b2c80fe5fbbd57f50d87ce3117c285d16fda49ea394bf05", -"2c3e1b0dafe2ef22cfa6afb64a26092c235f48820654ca447dc8796921752db7", -"2826af3e723d22ed66ed6b19f487155eac10345fef20a9c8a65849afe152af8f", -"a719805f30a4963340107606dec2b0ff850b54c75359574d7548c2282b69b5eb", -"b83f737e653ee38bbc3279ad06c83b7ec3821403db31a640d29f0aafb2c2e38c", - ], [ -"e434c88caa65f2e3bb65a2f2abd178ceb247f0036c765f27016a633fa2c91711", -"8b7808d2d4f9c5f1f7f28b6ceaa6468dd8c20dc13b1cc8509dc010a3f3bcfe70", -"6193eca5bc6f027f16069da86da0d365fdd68e00fb5f81fea0306128008aa33b", -"a7dc0b0b46fe17f1159f608aba6c9ded906dc3ca33ade56528f30e9920991b4a", -"754badf9c23b102f2644bc8ddd5d800b44777aa8db1cbd19d0512b7e7009eb8d", -"20e9829f92923b8fb2325b08d8ef0d8b59f0cd6e361eeeb9478e5b2b57a99dd7", - ], [ -"02b22b3151da8fc150e4f9d0b89c4829c54286c4b3a801ea8eebdd14ec41ecec", -"1981106b6318cf1593defe007bc47f46e86e732877c93efebb4438de801710f9", -"c84a17cc275875ff7802a4d15d0bf484bd079459e5c4598a5104cddbde6225f7", -"119f065957a07e12fb2685cef4b6959d92bf459dface90a226f8e799262466ac", -"6440c36958bcd9023087cdff7301524f521894f394f464f3a8192b13fdf74123", -"4c395c65b244ec29ced1e0e18b72be7ea175ff93a02065db1c9a52e04337c4d4", - ], [ -"e4c7cdaa6c7b7020c2254384e01ce2ab8b143d635b9b1ceec18936975f4e060e", -"33e4561730261caecaf5ac4e684e49e587074379b7e295e8806112fd411a106c", -"4065d0dd1ca36d9cc4f7fe393bde1894c2f3d06bef137ed256236718118ec171", -"6c1c3abdcf9eef052f1d3827ccca6bb62b17b4d7a0799e3ed7d45a0ddf462e3a", -"6e241b8d276835313424777b0b2b5b41cb7d13051e6edac701c3574c61a57abe", -"d8fa52c27e684a7d63087ee245f3da074613c2b32f6a1183b45163d38c437f6a", - ], [ -"882ff17bc22345ce60409d7389594b91d630f384a0d794761265239ce6ac803f", -"a968de5fd72e369d64944c35f0c9bdc030f004b2c8a0be8ebddfb792a1c48599", -"25c5d4dacad8e73ab54545907060ccdea9b3b62816cc823ec8c06bed26e8b388", -"b070c2c86a0bf0d8a742dab4c7d18febbf0e83d5eb8d658a58c43eca34479694", -"4ee3e91cc8dad3d081661cdd751d026b074342547aec2c7ef85b5cb5fccfcb88", -"fbadfc3786d272626465fe6aa0b4fdf0681329850cce41483933ef41052c4e0e", - ], [ -"dd9cb182445e867060bd44fa142040893e5b940135e5a4cc6728d2c61f9e9c87", -"af60d54f85064b7ff461dd3b6b608ab7f0906bc554789d20e7f689bf146f79ab", -"3d858f53f06af7e0858c532af6300400449fa71cf1a3ceff678b89c7dffe96dc", -"e5f4bcbcfecf2b7db8ac092bad648149c2aa0126eda77ecdcdc43ade22e21753", -"8df965fbb7cfc3c94df2b2a27e5adc5d3b5fb3e17a272052f5aa9ed99d4a5a82", -"eddf8fdac0a697ca51d1247f561c55e411e42cd4f56e8138186622fa8e62612c", - ], [ -"af5592995a5ae321186c546e6b8b4d894c0c1a4932ba3dbf6f50fe2f9b22043d", -"9dc89917d65547cd09d7ba87fe2c1fead6b7a6a31ab348251d54c229bf4e6349", -"506d313330d144fae7fe18775bedd897c16e15ecf98257695be279e0219366c2", -"04cfeab2f5aa91d6dd297573054fe1287991282928460768aa47678af1a8bf50", -"67156f70eadf9811cbe1d30c9db1e197a3456a2b632e7cc9401410dc666fd9e4", -"3a48ff67182347d60c1aabee2b41d87758e9dca449df62da35a72ed8eb397c61", - ], [ -"b490ea32b433babed7f933320ce490383b88152c455cf22099b5fefa35d0d556", -"fa4fbedcd7db944f34fbd1cf09a6ede9205c8b0ea884ae59c591b691e6ad3a2e", -"7df6cebfea01f1c5a6bf9d9e16c80692a9cf82c56f1b19d238af99483d7bb5b0", -"ef54c1ab699b0b4eb32735d18bd0d5c2e6dd0576ae4da08177847976ec4b2ea8", -"e6759784f7dcd1ff30dd0053c8c92cee98a80e5c1ae24ed875969b666765cafc", -"b9ff1488306a076cb6e8f494d337f5c107e2788eef29c9acc7005431de6adf74", - ], [ -"0031c95fee4ddca241a5c7e86fed719aa4b0101334cb1ada6ea1180d400729d0", -"bac3c18e0b2cc67bdb0a625a230254596c1b36d5571f360052f9af2e896f4e13", -"2e8db44896020250a1f1c5f13c5a28cfc6a847b1b7fc29284e77ba3fc024aac8", -"c3bc96acafc84d64cd59f6a410d13fba216fe64c268956ca604a39ef4080610e", -"3bb57d0760a48303b366b1c3eaa20601f949f0d72bebdf4fd2ebf8b282ffaa3a", -"1d436b1eef1af22b2b05daab1d5250b3ef9e0f770f89c7cd746a626f062bf4d5", - ], [ -"71f05218ed7d3653fb534e75b380ac6f37e1a7c8f7c4a71922590797d47888ce", -"37b5eee95937116dc3342fade6c6fc399a9c6e3e16db4d755757d9c189b6eb93", -"b8b27bff65fb60e4958592eda67c64018be5c8439bd47130b0dfbbd2d6842757", -"5fc7fd92dcfcc3f8c2e3204c04ae8a14259674f1d4defcb1cff7b34fc0e31473", -"e1f9f9629c96b7e43c4c3b81d76d9463c55e69aa043907963d1a3ede6276be43", -"ea5960e644f753cf0a219646361271cfba67075da98af62b6933f8e612cb57a4", - ], [ -"71e4f3589bae4c0130b1dc1d379018b4ec2bd07050be7e5700ba6a813b534f7d", -"9fb3a3849686fe19efef98e05ea9ae65444e7ebfacea188e2cd5668cfbc200b3", -"1c52a0b3f2c63f6311158aee8ca237b59ae3837934044812ed6263cc82485b72", -"362440ae86acfeb8c34eecb0e29d1bbcd6d8c98fd1012d6cefa86feba403b555", -"75bc067ed7beb7ad726b8fce054700226676c0b183ec00b9aae44a67b57b4259", -"097744283f77060e5a8f60d44f6d5921645e2084dbc2c374ae57c1fc9cd3b6e8", - ] - ]; - - #[test] - fn encode_decode() { - for i in 0..KAT_DECODE_OK.len() { - let buf = hex::decode(KAT_DECODE_OK[i]).unwrap(); - let Q = Point::decode(&buf).unwrap(); - assert!(Q.encode()[..] == buf); - } - for i in 0..KAT_DECODE_BAD.len() { - let buf = hex::decode(KAT_DECODE_BAD[i]).unwrap(); - assert!(Point::decode(&buf).is_none()); - } - } - - #[test] - fn base_arith() { - for i in 0..KAT_ADD.len() { - let buf1 = hex::decode(KAT_ADD[i][0]).unwrap(); - let buf2 = hex::decode(KAT_ADD[i][1]).unwrap(); - let buf3 = hex::decode(KAT_ADD[i][2]).unwrap(); - let buf4 = hex::decode(KAT_ADD[i][3]).unwrap(); - let buf5 = hex::decode(KAT_ADD[i][4]).unwrap(); - let buf6 = hex::decode(KAT_ADD[i][5]).unwrap(); - let P1 = Point::decode(&buf1).unwrap(); - let P2 = Point::decode(&buf2).unwrap(); - let P3 = Point::decode(&buf3).unwrap(); - let P4 = Point::decode(&buf4).unwrap(); - let P5 = Point::decode(&buf5).unwrap(); - let P6 = Point::decode(&buf6).unwrap(); - assert!(P1.equals(P1) == 0xFFFFFFFF); - assert!(P2.equals(P2) == 0xFFFFFFFF); - assert!(P3.equals(P3) == 0xFFFFFFFF); - assert!(P4.equals(P4) == 0xFFFFFFFF); - assert!(P5.equals(P5) == 0xFFFFFFFF); - assert!(P6.equals(P6) == 0xFFFFFFFF); - assert!(P1.equals(P2) == 0x00000000); - assert!(P1.equals(P3) == 0x00000000); - assert!(P1.equals(P4) == 0x00000000); - assert!(P1.equals(P5) == 0x00000000); - assert!(P1.equals(P6) == 0x00000000); - let Q3 = P1 + P2; - assert!(Q3.equals(P3) != 0); - assert!(Q3.encode()[..] == buf3); - let Q4 = P1.double(); - assert!(Q4.equals(P4) != 0); - assert!(Q4.encode()[..] == buf4); - let R4 = P1 + P1; - assert!(R4.equals(P4) != 0); - assert!(R4.equals(Q4) != 0); - assert!(R4.encode()[..] == buf4); - let Q5 = P4 + P2; - assert!(Q5.equals(P5) != 0); - assert!(Q5.encode()[..] == buf5); - let R5 = Q4 + P2; - assert!(R5.equals(P5) != 0); - assert!(R5.equals(Q5) != 0); - assert!(R5.encode()[..] == buf5); - let S5 = P1 + Q3; - assert!(S5.equals(P5) != 0); - assert!(S5.equals(Q5) != 0); - assert!(S5.equals(R5) != 0); - assert!(S5.encode()[..] == buf5); - let Q6 = Q3.double(); - assert!(Q6.equals(P6) != 0); - assert!(Q6.encode()[..] == buf6); - let R6 = Q4 + P2.double(); - assert!(R6.equals(P6) != 0); - assert!(R6.equals(Q6) != 0); - assert!(R6.encode()[..] == buf6); - let S6 = R5 + P2; - assert!(S6.equals(P6) != 0); - assert!(S6.equals(Q6) != 0); - assert!(S6.equals(R6) != 0); - assert!(S6.encode()[..] == buf6); - - let mut T = Q6; - for j in 0..10 { - let S = R6.xdouble(j as u32); - assert!(T.equals(S) != 0); - assert!(T.encode() == S.encode()); - T = T.double(); - } - - assert!((R6 + Point::NEUTRAL).encode()[..] == buf6); - } - } - - #[test] - fn mulgen() { - let sbuf = hex::decode("f17dbcbef04a157b7e470b6563940017e5de0bbf30042ef0a86e36f4b8600d14").unwrap(); - let (s, ok) = Scalar::decode32(&sbuf); - assert!(ok == 0xFFFFFFFF); - let rbuf = hex::decode("d79870ba92af7240e4e7a9ca4a96a6411822382b043006c9046fa8d011f79ace").unwrap(); - let R = Point::decode(&rbuf).unwrap(); - let P = Point::BASE * s; - assert!(P.equals(R) == 0xFFFFFFFF); - assert!(P.encode()[..] == rbuf); - let Q = Point::mulgen(&s); - assert!(Q.equals(R) == 0xFFFFFFFF); - assert!(Q.encode()[..] == rbuf); - } - - #[test] - fn mul() { - for i in 0..20 { - // Build pseudorandom s1 and s2 - let v1 = Sha3_256::digest(&((2 * i + 0) as u64).to_le_bytes()); - let v2 = Sha3_256::digest(&((2 * i + 1) as u64).to_le_bytes()); - - let s1 = Scalar::decode_reduce(&v1); - let s2 = Scalar::decode_reduce(&v2); - let s3 = s1 * s2; - let P1 = Point::mulgen(&s1); - let Q1 = s1 * Point::BASE; - assert!(P1.equals(Q1) == 0xFFFFFFFF); - let P2 = Point::mulgen(&s3); - let Q2 = s2 * Q1; - assert!(P2.equals(Q2) == 0xFFFFFFFF); - } - - let mut T = Point::BASE.xdouble(120); - assert!(T.encode()[..] == hex::decode("9811a66139b0779ecdd85726b290a67346aedda26bc1f6fded6da44631fb2149").unwrap()); - for _ in 0..1000 { - let n = Scalar::decode_reduce(&T.encode()); - T *= n; - } - assert!(T.encode()[..] == hex::decode("7c9d21debf079763b76125728bbe6f8d22947ad296a86a92819654ac1b9bd135").unwrap()); - } - - #[test] - fn mul_add_mulgen() { - for i in 0..20 { - // Build pseudorandom A, u and v - let v1 = Sha3_256::digest(&((3 * i + 0) as u64).to_le_bytes()); - let v2 = Sha3_256::digest(&((3 * i + 1) as u64).to_le_bytes()); - let v3 = Sha3_256::digest(&((3 * i + 2) as u64).to_le_bytes()); - let A = Point::mulgen(&Scalar::decode_reduce(&v1)); - let u = Scalar::decode_reduce(&v2); - let v = Scalar::decode_reduce(&v3); - - // Compute u*A + v*B in two different ways; check that they - // match. - let R1 = u * A + Point::mulgen(&v); - let R2 = A.mul_add_mulgen_vartime(&u, &v); - assert!(R1.equals(R2) == 0xFFFFFFFF); - } - } - - #[test] - fn map() { - for i in 0..50 { - let v = Sha3_256::digest(&(i as u64).to_le_bytes()); - let f = GFp::decode_reduce(&v); - let P = Point::map_to_curve(&f); - assert!(P.Z.iszero() == 0); - let iZ = GFp::ONE / P.Z; - let e = P.E * iZ; - let u = P.U * iZ; - let t = P.T * iZ; - assert!(u.square().equals(t) == 0xFFFFFFFF); - assert!(e.square().equals(-u.xsquare(2) + Point::Ap * u.square() + GFp::ONE) == 0xFFFFFFFF); - } - } - - fn test_recode_scalar_ext(s: &Scalar, w: i32) { - let sd = Point::recode_scalar_ext(s, w); - let num = Point::num_recoded_digits(w); - let mut r = Scalar::ZERO; - let md = 1i32 << (w - 1); - for i in (0..num).rev() { - let mut d = sd[i] as i32; - if i == (num - 1) { - if w == 8 && d == -128 { - d = 128; - } - assert!(d >= 0); - assert!(d <= md); - r = Scalar::from_i32(d); - } else { - assert!(d >= -md); - assert!(d < md); - for _ in 0..w { - r = r.mul2(); - } - r += Scalar::from_i32(d); - } - } - assert!(s.equals(r) == 0xFFFFFFFF); - } - - #[test] - fn recode_scalar_ext() { - for w in 5..9 { - test_recode_scalar_ext(&Scalar::ZERO, w); - test_recode_scalar_ext(&Scalar::ONE, w); - test_recode_scalar_ext(&Scalar::MINUS_ONE, w); - for i in 0..100 { - let v = Sha3_256::digest(&(i as u64).to_le_bytes()); - test_recode_scalar_ext(&Scalar::decode_reduce(&v), w); - } - } - } - - // This function tests xmuladd with n pseudorandom points and scalars. - fn test_xmuladd(n: usize) { - // Make n pseudorandom scalars and points. - // We force one scalar to be zero, and one point to be the neutral, - // to exercise these cases. - let mut scalars = Vec::::with_capacity(n); - let mut points = Vec::::with_capacity(n); - let z = (n as u64) << 32; - for i in 0..n { - let y = z + ((i as u64) << 2); - let vs = Sha3_256::digest(&(y + 0).to_le_bytes()); - let vp = Sha3_512::digest(&(y + 1).to_le_bytes()); - if i == 50 { - scalars.push(Scalar::ZERO); - } else { - scalars.push(Scalar::decode_reduce(&vs)); - } - if i == 40 { - points.push(Point::NEUTRAL); - } else { - points.push(Point::hash_to_curve(&vp)); - } - } - - let P = Point::xmuladd_vartime(&scalars, &points); - - let mut Q = Point::NEUTRAL; - for i in 0..n { - Q += scalars[i] * points[i]; - } - assert!(P.equals(Q) == 0xFFFFFFFF); - } - - #[test] - fn xmuladd110() { - test_xmuladd(110); - } - - #[test] - fn xmuladd400() { - test_xmuladd(400); - } - - #[test] - fn xmuladd700() { - test_xmuladd(700); - } - - #[test] - fn xmuladd1000() { - test_xmuladd(1000); - } -} diff --git a/forks/Spartan-t256/src/lib.rs b/forks/Spartan-t256/src/lib.rs index 5ad527ed..685d654b 100644 --- a/forks/Spartan-t256/src/lib.rs +++ b/forks/Spartan-t256/src/lib.rs @@ -3,10 +3,8 @@ // #![deny(missing_docs)] #![allow(clippy::assertions_on_result_states)] -// https://github.com/microsoft/Spartan/tree/master extern crate byteorder; extern crate core; -extern crate curve25519_dalek; extern crate digest; extern crate merlin; extern crate rand; @@ -19,7 +17,6 @@ mod commitments; mod dense_mlpoly; mod errors; mod group; -pub mod group25519; mod math; mod nizk; mod product_tree; diff --git a/forks/Spartan-t256/src/nizk/bullet.rs b/forks/Spartan-t256/src/nizk/bullet.rs index 0c651b6e..337029dc 100644 --- a/forks/Spartan-t256/src/nizk/bullet.rs +++ b/forks/Spartan-t256/src/nizk/bullet.rs @@ -3,6 +3,8 @@ #![allow(non_snake_case)] #![allow(clippy::type_complexity)] #![allow(clippy::too_many_arguments)] +use crate::nizk::ipa_bases::IPABases; + use super::super::errors::ProofVerifyError; use super::super::group::{CompressedGroup, GroupElement, VartimeMultiscalarMul}; use super::super::math::Math; @@ -11,6 +13,7 @@ use super::super::transcript::ProofTranscript; use core::iter; use merlin::Transcript; use serde::{Deserialize, Serialize}; +use ff::Field; use super::super::group::CompressedGroupExt; // added @@ -51,7 +54,7 @@ impl BulletReductionProof { // Create slices G, H, a, b backed by their respective // vectors. This lets us reslice as we compress the lengths // of the vectors in the main loop below. - let mut G = &mut G_vec.to_owned()[..]; + let G = &mut G_vec.to_owned()[..]; let mut a = &mut a_vec.to_owned()[..]; let mut b = &mut b_vec.to_owned()[..]; @@ -71,31 +74,38 @@ impl BulletReductionProof { let mut blinds_iter = blinds_vec.iter(); let mut blind_fin = *blind; + let mut G_bases = IPABases::new(n); + for i in 0..n { + G_bases.defer_init(&[Scalar::one()], &[G[i]]); + } + while n != 1 { n /= 2; let (a_L, a_R) = a.split_at_mut(n); let (b_L, b_R) = b.split_at_mut(n); - let (G_L, G_R) = G.split_at_mut(n); + let (mut G_bases_L, G_bases_R) = G_bases.split_at(n); let c_L = inner_product(a_L, b_R); let c_R = inner_product(a_R, b_L); let (blind_L, blind_R) = blinds_iter.next().unwrap(); + let (scalars, bases) = G_bases_R.get(&a_L); let L = GroupElement::vartime_multiscalar_mul( - a_L + scalars .iter() .chain(iter::once(&c_L)) .chain(iter::once(blind_L)), - G_R.iter().chain(iter::once(Q)).chain(iter::once(H)), + bases.iter().chain(iter::once(Q)).chain(iter::once(H)), ); + let (scalars, bases) = G_bases_L.get(&a_R); let R = GroupElement::vartime_multiscalar_mul( - a_R + scalars .iter() .chain(iter::once(&c_R)) .chain(iter::once(blind_R)), - G_L.iter().chain(iter::once(Q)).chain(iter::once(H)), + bases.iter().chain(iter::once(Q)).chain(iter::once(H)), ); transcript.append_point(b"L", &L.compress()); @@ -107,28 +117,33 @@ impl BulletReductionProof { for i in 0..n { a_L[i] = a_L[i] * u + u_inv * a_R[i]; b_L[i] = b_L[i] * u_inv + u * b_R[i]; - G_L[i] = GroupElement::vartime_multiscalar_mul(&[u_inv, u], &[G_L[i], G_R[i]]); + //G_L[i] = GroupElement::vartime_multiscalar_mul(&[u_inv, u], &[G_L[i], G_R[i]]); + G_bases_L.defer(i, &G_bases_R, &[u_inv, u]); } blind_fin = blind_fin + blind_L * u * u + blind_R * u_inv * u_inv; L_vec.push(L.compress()); R_vec.push(R.compress()); + a = a_L; b = b_L; - G = G_L; + G_bases = G_bases_L; } - let Gamma_hat = - GroupElement::vartime_multiscalar_mul(&[a[0], a[0] * b[0], blind_fin], &[G[0], *Q, *H]); + let (scalars, bases) = G_bases.get(&[Scalar::one()]); + let G0 = GroupElement::vartime_multiscalar_mul(scalars, bases); + let Gamma_hat = + GroupElement::vartime_multiscalar_mul(&[a[0], a[0] * b[0], blind_fin], &[G0, *Q, *H]); + ( BulletReductionProof { L_vec, R_vec }, Gamma_hat, a[0], b[0], - G[0], + G0, blind_fin, ) } @@ -161,7 +176,7 @@ impl BulletReductionProof { // 2. Compute 1/(u_k...u_1) and 1/u_k, ..., 1/u_1 let mut challenges_inv = challenges.clone(); - let allinv = Scalar::batch_invert(&mut challenges_inv); + let allinv = crate::scalar::batch_invert(&mut challenges_inv); // 3. Compute u_i^2 and (1/u_i)^2 for i in 0..lg_n { diff --git a/forks/Spartan-t256/src/nizk/ipa_bases.rs b/forks/Spartan-t256/src/nizk/ipa_bases.rs new file mode 100644 index 00000000..05835c7b --- /dev/null +++ b/forks/Spartan-t256/src/nizk/ipa_bases.rs @@ -0,0 +1,92 @@ + +use crate::group::GroupElement as C; +use crate::scalar::Scalar as F; + + +// Represent each base symbolically, as (bases, scalars), such that base = msm(bases, scalars) +// When we later use the base in an MSM, say as base^x, we compute (bases, x*scalars) and +// add this list of terms (bases[1]^scalars[1], ..., ) as terms in the MSM +#[derive(Clone)] +pub struct IPABases{ + bases: Vec>, + scalars: Vec>, +} + +impl IPABases +{ + pub fn new(n: usize) -> Self { + let bases = Vec::with_capacity(n); + let scalars = Vec::with_capacity(n); + IPABases { bases, scalars} + } + + pub fn defer_init(&mut self, scalars : &[F], bases: &[C]){ + assert!(scalars.len() == bases.len(), "Scalars and bases must have same length"); + self.scalars.push(scalars.to_vec()); + self.bases.push(bases.to_vec()); + } + + // self is the left half + // Update G_L[i] to G_L[i] * scalars[0], (just update the scalars, don't actually compute the scalar mult) + // then append G_R[i] * scalars[1], (again not the scalar mult) + pub fn defer(&mut self, i : usize, bases_R : &Self, scalars : &[F]){ + + assert!(scalars.len() == 2); + + for k in 0..self.scalars[i].len() { + self.scalars[i][k] *= scalars[0]; + } + + for k in 0..bases_R.bases[i].len() { + self.scalars[i].push(bases_R.scalars[i][k] * scalars[1]); + self.bases[i].push(bases_R.bases[i][k]); + } + } + + /// Split the bases at the given index, returning two separate IPABases + /// structures representing the left and right halves + pub fn split_at(&self, n: usize) -> (Self, Self) { + assert!(n <= self.bases.len(), "Split index out of bounds"); + + // Create new instances for left and right parts + let mut left = Self::new(n); + let mut right = Self::new(self.bases.len() - n); + for i in 0..n { + left.bases.push(self.bases[i].clone()); + left.scalars.push(self.scalars[i].clone()); + } + + for i in 0..(self.bases.len() - n) { + right.bases.push(self.bases[n + i].clone()); + right.scalars.push(self.scalars[n + i].clone()); + } + + (left, right) + } + + /// Get the scalars and bases associated with the given values + /// + /// This method takes a slice of scalars and returns a tuple containing: + /// 1. A vector of scalars (combining the input scalars with the internal scalars) + /// 2. A vector of bases (corresponding bases for the scalars) + pub fn get(&self, values: &[F]) -> (Vec, Vec) { + assert!(values.len() == self.bases.len(), "Input length must match bases length"); + + let mut result_scalars = Vec::with_capacity(values.len()); + let mut result_bases = Vec::with_capacity(values.len()); + + for (i, value) in values.iter().enumerate() { + // For each scalar in the input, we multiply it with all stored scalars + // for the corresponding base and add to the result + for j in 0..self.scalars[i].len() { + result_scalars.push(*value * self.scalars[i][j]); + result_bases.push(self.bases[i][j]); + } + } + + (result_scalars, result_bases) + } + + + +} \ No newline at end of file diff --git a/forks/Spartan-t256/src/nizk/mod.rs b/forks/Spartan-t256/src/nizk/mod.rs index 24c9d8ee..28ab18ae 100644 --- a/forks/Spartan-t256/src/nizk/mod.rs +++ b/forks/Spartan-t256/src/nizk/mod.rs @@ -10,8 +10,10 @@ use merlin::Transcript; use serde::{Deserialize, Serialize}; mod bullet; +mod ipa_bases; use bullet::BulletReductionProof; + #[derive(Serialize, Deserialize, Debug)] pub struct KnowledgeProof { alpha: CompressedGroup, @@ -580,7 +582,9 @@ impl DotProductProofLog { #[cfg(test)] mod tests { use super::*; - use rand::rngs::OsRng; + use rand_core::OsRng; + use ff::Field; + #[test] fn check_knowledgeproof() { let mut csprng: OsRng = OsRng; diff --git a/forks/Spartan-t256/src/r1csinstance.rs b/forks/Spartan-t256/src/r1csinstance.rs index 2c6e50e5..f80b4055 100644 --- a/forks/Spartan-t256/src/r1csinstance.rs +++ b/forks/Spartan-t256/src/r1csinstance.rs @@ -12,7 +12,8 @@ use super::sparse_mlpoly::{ use super::timer::Timer; use merlin::Transcript; -use rand::rngs::OsRng; +use rand_core::OsRng; +use ff::Field; use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize)] diff --git a/forks/Spartan-t256/src/r1csproof.rs b/forks/Spartan-t256/src/r1csproof.rs index bbb265d5..feb56f19 100644 --- a/forks/Spartan-t256/src/r1csproof.rs +++ b/forks/Spartan-t256/src/r1csproof.rs @@ -495,7 +495,8 @@ impl R1CSProof { #[cfg(test)] mod tests { use super::*; - use rand::rngs::OsRng; + use rand_core::OsRng; + use ff::Field; fn produce_tiny_r1cs() -> (R1CSInstance, Vec, Vec) { // three constraints over five variables Z1, Z2, Z3, Z4, and Z5 diff --git a/forks/Spartan-t256/src/random.rs b/forks/Spartan-t256/src/random.rs index 2a1a3a2e..b2bda6f3 100644 --- a/forks/Spartan-t256/src/random.rs +++ b/forks/Spartan-t256/src/random.rs @@ -1,7 +1,8 @@ use super::scalar::Scalar; use super::transcript::ProofTranscript; use merlin::Transcript; -use rand::rngs::OsRng; +use rand_core::OsRng; +use ff::Field; pub struct RandomTape { tape: Transcript, diff --git a/forks/Spartan-t256/src/scalar/mod.rs b/forks/Spartan-t256/src/scalar/mod.rs index 8450d38b..9e0b0097 100644 --- a/forks/Spartan-t256/src/scalar/mod.rs +++ b/forks/Spartan-t256/src/scalar/mod.rs @@ -1,12 +1,8 @@ -use circ_fields::t256::ScalarField; -// use ark_serialize::CanonicalDeserialize; -// use ark_serialize::CanonicalSerialize; +use halo2curves::t256::Fq as ScalarField; +use ff::Field; -use ark_ff::PrimeField; -mod scalar; - -pub type Scalar = scalar::Scalar; +pub type Scalar = halo2curves::t256::Fq; pub type ScalarBytes = ScalarField; @@ -39,8 +35,8 @@ pub trait ScalarBytesFromScalar { impl ScalarBytesFromScalar for Scalar { fn decompress_scalar(s: &Scalar) -> ScalarBytes { - // ScalarBytes::deserialize_compressed(&s.to_bytes()[..]).unwrap() - ScalarBytes::from_bigint(s.to_bigint()).unwrap() + let bytes = s.to_bytes(); + ScalarBytes::from_bytes(&bytes).unwrap() } fn decompress_vector(s: &[Scalar]) -> Vec { @@ -50,6 +46,53 @@ impl ScalarBytesFromScalar for Scalar { } } +pub fn batch_invert(inputs: &mut [Scalar]) -> Scalar { + // This code is essentially identical to the FieldElement + // implementation, and is documented there. Unfortunately, + // it's not easy to write it generically, since here we want + // to use `UnpackedScalar`s internally, and `Scalar`s + // externally, but there's no corresponding distinction for + // field elements. + // We also remove the zeroization support since halo2curves + // does not support it + + let n = inputs.len(); + let one = Scalar::one(); + + let scratch_vec = vec![one; n]; + let mut scratch = scratch_vec; + + // Keep an accumulator of all of the previous products + let mut acc = Scalar::one(); + + // Pass through the input vector, recording the previous + // products in the scratch space + for (input, scratch) in inputs.iter().zip(scratch.iter_mut()) { + *scratch = acc; + + acc = acc * input; + } + + // acc is nonzero iff all inputs are nonzero + debug_assert!(acc != Scalar::zero()); + + // Compute the inverse of all products + acc = acc.invert().unwrap(); + + // We need to return the product of all inverses later + let ret = acc; + + // Pass through the vector backwards to compute the inverses + // in place + for (input, scratch) in inputs.iter_mut().rev().zip(scratch.iter().rev()) { + let tmp = &acc * input.clone(); + *input = &acc * scratch; + acc = tmp; + } + + ret +} + #[cfg(test)] mod tests { use super::*; diff --git a/forks/Spartan-t256/src/scalar/scalar.rs b/forks/Spartan-t256/src/scalar/scalar.rs deleted file mode 100644 index 7bca8972..00000000 --- a/forks/Spartan-t256/src/scalar/scalar.rs +++ /dev/null @@ -1,1307 +0,0 @@ -//! This module provides an implementation of the T256's scalar field $\mathbb{F}_q$ -//! where `q = 115792089210356248762697446949407573530086143415290314195533631308867097853951 = 0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff` -//! This module is an adaptation of code from the bls12-381 crate. -//! We modify various constants (MODULUS, R, R2, etc.) to appropriate values for Curve25519 and update tests -//! We borrow the `invert` method from the curve25519-dalek crate. -//! See NOTICE.md for more details -#![allow(clippy::all)] -use core::borrow::Borrow; -use core::convert::TryFrom; -use core::fmt; -use core::iter::{Product, Sum}; -use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use rand::{CryptoRng, RngCore}; -use serde::{Deserialize, Serialize}; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; -use zeroize::Zeroize; - -use hex_literal::hex; -use num_bigint_dig::{BigUint, ModInverse}; - -use ark_ff::biginteger::BigInteger256; -// use crate::util::{adc, mac, sbb}; -/// 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; - } - } - }; -} - -/// Represents an element of the scalar field $\mathbb{F}_q$ of the Curve25519 elliptic -/// curve construction. -// The internal representation of this type is four 64-bit unsigned -// integers in little-endian order. `Scalar` values are always in -// Montgomery form; i.e., Scalar(a) = aR mod q, with R = 2^256. -// Require 5 elements instead of 4 because adding two -#[derive(Clone, Copy, Eq, Serialize, Deserialize)] -// pub struct Scalar(pub(crate) [u64; 4]); -pub struct Scalar(pub(crate) [u64; 5]); - -impl fmt::Debug for Scalar { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // println!("**"); - let tmp = self.to_bytes(); - write!(f, "0x")?; - for &b in tmp.iter().rev() { - write!(f, "{:02x}", b)?; - } - Ok(()) - } -} - -impl From for Scalar { - fn from(val: u64) -> Scalar { - Scalar([val, 0, 0, 0, 0]) * R2 - } -} - -impl ConstantTimeEq for Scalar { - fn ct_eq(&self, other: &Self) -> Choice { - self.0[0].ct_eq(&other.0[0]) - & self.0[1].ct_eq(&other.0[1]) - & self.0[2].ct_eq(&other.0[2]) - & self.0[3].ct_eq(&other.0[3]) - } -} - -impl PartialEq for Scalar { - #[inline] - fn eq(&self, other: &Self) -> bool { - self.ct_eq(other).unwrap_u8() == 1 - } -} - -impl ConditionallySelectable for Scalar { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - Scalar([ - 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), - ]) - } -} - -// /// Constant representing the modulus -// /// q = 2^252 + 27742317777372353535851937790883648493 -// /// 0x1000000000000000 0000000000000000 14def9dea2f79cd6 5812631a5cf5d3ed -// const MODULUS: Scalar = Scalar([ -// 0x5812_631a_5cf5_d3ed, -// 0x14de_f9de_a2f7_9cd6, -// 0x0000_0000_0000_0000, -// 0x1000_0000_0000_0000, -// ]); -/// Constant representing the modulus q = 115792089210356248762697446949407573530086143415290314195533631308867097853951 -const MODULUS: Scalar = Scalar([ - 0xffffffffffffffff, - 0x00000000ffffffff, - 0x0000000000000000, - 0xffffffff00000001, - 0, -]); - -impl<'a> Neg for &'a Scalar { - type Output = Scalar; - - #[inline] - fn neg(self) -> Scalar { - self.neg() - } -} - -impl Neg for Scalar { - type Output = Scalar; - - #[inline] - fn neg(self) -> Scalar { - -&self - } -} - -impl<'a, 'b> Sub<&'b Scalar> for &'a Scalar { - type Output = Scalar; - - #[inline] - fn sub(self, rhs: &'b Scalar) -> Scalar { - self.sub(rhs) - } -} - -impl<'a, 'b> Add<&'b Scalar> for &'a Scalar { - type Output = Scalar; - - #[inline] - fn add(self, rhs: &'b Scalar) -> Scalar { - self.add(rhs) - } -} - -impl<'a, 'b> Mul<&'b Scalar> for &'a Scalar { - type Output = Scalar; - - #[inline] - fn mul(self, rhs: &'b Scalar) -> Scalar { - self.mul(rhs) - } -} - -impl_binops_additive!(Scalar, Scalar); -impl_binops_multiplicative!(Scalar, Scalar); - -/// INV = -(q^{-1} mod 2^64) mod 2^64 -// const INV: u64 = 0xd2b5_1da3_1254_7e1b; -const INV: u64 = 0x1; - -/// R = 2^256 mod q -// const R: Scalar = Scalar([ -// 0xd6ec_3174_8d98_951d, -// 0xc6ef_5bf4_737d_cf70, -// 0xffff_ffff_ffff_fffe, -// 0x0fff_ffff_ffff_ffff, -// ]); -const R: Scalar = Scalar([ - 0x0000000000000001, - 0xffffffff00000000, - 0xffffffffffffffff, - 0x00000000fffffffe, - 0, -]); - -/// R^2 = 2^512 mod q -// const R2: Scalar = Scalar([ -// 0xa406_11e3_449c_0f01, -// 0xd00e_1ba7_6885_9347, -// 0xceec_73d2_17f5_be65, -// 0x0399_411b_7c30_9a3d, -// ]); -const R2: Scalar = Scalar([ - 0x0000000000000003, - 0xfffffffbffffffff, - 0xfffffffffffffffe, - 0x00000004fffffffd, - 0, -]); - -/// R^3 = 2^768 mod q -// const R3: Scalar = Scalar([ -// 0x2a9e_4968_7b83_a2db, -// 0x2783_24e6_aef7_f3ec, -// 0x8065_dc6c_04ec_5b65, -// 0x0e53_0b77_3599_cec7, -// ]); -const R3: Scalar = Scalar([ - 0xfffffffd0000000a, - 0xffffffedfffffff7, - 0x00000005fffffffc, - 0x0000001800000001, - 0, -]); - -impl Default for Scalar { - #[inline] - fn default() -> Self { - Self::zero() - } -} - -impl Product for Scalar -where - T: Borrow, -{ - fn product(iter: I) -> Self - where - I: Iterator, - { - iter.fold(Scalar::one(), |acc, item| acc * item.borrow()) - } -} - -impl Sum for Scalar -where - T: Borrow, -{ - fn sum(iter: I) -> Self - where - I: Iterator, - { - iter.fold(Scalar::zero(), |acc, item| acc + item.borrow()) - } -} - -impl Zeroize for Scalar { - fn zeroize(&mut self) { - // self.0 = [0u64; 4]; - self.0 = [0u64; 5]; - } -} - -impl Scalar { - /// Returns zero, the additive identity. - #[inline] - pub const fn zero() -> Scalar { - Scalar([0, 0, 0, 0, 0]) - } - - /// Returns one, the multiplicative identity. - #[inline] - pub const fn one() -> Scalar { - R - } - - pub fn random(rng: &mut Rng) -> Self { - let mut limbs = [0u64; 8]; - for i in 0..8 { - limbs[i] = rng.next_u64(); - } - Scalar::from_u512(limbs) - } - - /// Doubles this field element. - #[inline] - pub const fn double(&self) -> Scalar { - self.add(self) - } - - /// Attempts to convert a little-endian byte representation of - /// a scalar into a `Scalar`, failing if the input is not canonical. - pub fn from_bytes(bytes: &[u8; 32]) -> CtOption { - let mut tmp = Scalar([0, 0, 0, 0, 0]); - - tmp.0[0] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[..8]).unwrap()); - tmp.0[1] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[8..16]).unwrap()); - tmp.0[2] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[16..24]).unwrap()); - tmp.0[3] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[24..32]).unwrap()); - - // Try to subtract the modulus - let (_, borrow) = sbb(tmp.0[0], MODULUS.0[0], 0); - let (_, borrow) = sbb(tmp.0[1], MODULUS.0[1], borrow); - let (_, borrow) = sbb(tmp.0[2], MODULUS.0[2], borrow); - let (_, borrow) = sbb(tmp.0[3], MODULUS.0[3], borrow); - - // If the element is smaller than MODULUS then the - // subtraction will underflow, producing a borrow value - // of 0xffff...ffff. Otherwise, it'll be zero. - let is_some = (borrow as u8) & 1; - - // Convert to Montgomery form by computing - // (a.R^0 * R^2) / R = a.R - tmp *= &R2; - - CtOption::new(tmp, Choice::from(is_some)) - } - - /// Converts an element of `Scalar` into a byte representation in - /// little-endian byte order. - pub fn to_bytes(&self) -> [u8; 32] { - // Turn into canonical form by computing - // (a.R) / R = a - // let tmp = Scalar::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0); - let tmp = Scalar::montgomery_reduce( - self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], 0, 0, 0, 0, - ); - - let mut res = [0; 32]; - res[..8].copy_from_slice(&tmp.0[0].to_le_bytes()); - res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes()); - res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes()); - res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes()); - - res - } - - /// Converts a 512-bit little endian integer into - /// a `Scalar` by reducing by the modulus. - pub fn from_bytes_wide(bytes: &[u8; 64]) -> Scalar { - Scalar::from_u512([ - u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[..8]).unwrap()), - u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[8..16]).unwrap()), - u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[16..24]).unwrap()), - u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[24..32]).unwrap()), - u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[32..40]).unwrap()), - u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[40..48]).unwrap()), - u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[48..56]).unwrap()), - u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[56..64]).unwrap()), - ]) - } - - fn from_u512(limbs: [u64; 8]) -> Scalar { - // We reduce an arbitrary 512-bit number by decomposing it into two 256-bit digits - // with the higher bits multiplied by 2^256. Thus, we perform two reductions - // - // 1. the lower bits are multiplied by R^2, as normal - // 2. the upper bits are multiplied by R^2 * 2^256 = R^3 - // - // and computing their sum in the field. It remains to see that arbitrary 256-bit - // numbers can be placed into Montgomery form safely using the reduction. The - // reduction works so long as the product is less than R=2^256 multipled by - // the modulus. This holds because for any `c` smaller than the modulus, we have - // that (2^256 - 1)*c is an acceptable product for the reduction. Therefore, the - // reduction always works so long as `c` is in the field; in this case it is either the - // constant `R2` or `R3`. - // let d0 = Scalar([limbs[0], limbs[1], limbs[2], limbs[3]]); - // let d1 = Scalar([limbs[4], limbs[5], limbs[6], limbs[7]]); - let d0 = Scalar([limbs[0], limbs[1], limbs[2], limbs[3], 0]); - let d1 = Scalar([limbs[4], limbs[5], limbs[6], limbs[7], 0]); - // Convert to Montgomery form - d0 * R2 + d1 * R3 - } - - /// Converts from an integer represented in little endian - /// into its (congruent) `Scalar` representation. - pub const fn from_raw(val: [u64; 4]) -> Self { - // (&Scalar(val)).mul(&R2) - (&Scalar([val[0], val[1], val[2], val[3], 0])).mul(&R2) - } - - /// Squares this element. - #[inline] - pub const fn square(&self) -> Scalar { - let (r1, carry) = mac(0, self.0[0], self.0[1], 0); - let (r2, carry) = mac(0, self.0[0], self.0[2], carry); - let (r3, r4) = mac(0, self.0[0], self.0[3], carry); - - let (r3, carry) = mac(r3, self.0[1], self.0[2], 0); - let (r4, r5) = mac(r4, self.0[1], self.0[3], carry); - - let (r5, r6) = mac(r5, self.0[2], self.0[3], 0); - - let r7 = r6 >> 63; - let r6 = (r6 << 1) | (r5 >> 63); - let r5 = (r5 << 1) | (r4 >> 63); - let r4 = (r4 << 1) | (r3 >> 63); - let r3 = (r3 << 1) | (r2 >> 63); - let r2 = (r2 << 1) | (r1 >> 63); - let r1 = r1 << 1; - - let (r0, carry) = mac(0, self.0[0], self.0[0], 0); - let (r1, carry) = adc(0, r1, carry); - let (r2, carry) = mac(r2, self.0[1], self.0[1], carry); - let (r3, carry) = adc(0, r3, carry); - let (r4, carry) = mac(r4, self.0[2], self.0[2], carry); - let (r5, carry) = adc(0, r5, carry); - let (r6, carry) = mac(r6, self.0[3], self.0[3], carry); - let (r7, _) = adc(0, r7, carry); - - // Scalar::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7) - Scalar::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7, 0) - } - - /// Exponentiates `self` by `by`, where `by` is a - /// little-endian order integer exponent. - pub fn pow(&self, by: &[u64; 4]) -> Self { - let mut res = Self::one(); - for e in by.iter().rev() { - for i in (0..64).rev() { - res = res.square(); - let mut tmp = res; - tmp *= self; - res.conditional_assign(&tmp, (((*e >> i) & 0x1) as u8).into()); - } - } - res - } - - /// Exponentiates `self` by `by`, where `by` 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. - pub fn pow_vartime(&self, by: &[u64; 4]) -> 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.mul_assign(self); - } - } - } - res - } - - pub fn invert(&self) -> CtOption { - // adapt from https://github.com/personaelabs/spartan-ecdsa/blob/main/packages/Spartan-secq/src/scalar/scalar.rs#L607 - let val = BigUint::from_bytes_le(&self.to_bytes()); - - let result = val.mod_inverse(&BigUint::from_bytes_be(&hex!( - "ffffffff00000001000000000000000000000000ffffffffffffffffffffffff" - ))); - - if result.is_some() { - let mut result = result.unwrap().to_bytes_le().1.to_vec(); - result.resize(64, 0); - - // let result_bytes: [u8; 64] = result.try_into().unwrap_or_else(|_| { - // panic!("Unexpected length of result vector; expected 32 bytes") - // }); - let result_bytes: [u8; 64] = result.try_into().unwrap(); - let result = Scalar::from_bytes_wide(&result_bytes); - - CtOption::new(result, Choice::from(1)) - } else { - CtOption::new(Scalar::zero(), Choice::from(0)) - } - } - - pub fn batch_invert(inputs: &mut [Scalar]) -> Scalar { - // This code is essentially identical to the FieldElement - // implementation, and is documented there. Unfortunately, - // it's not easy to write it generically, since here we want - // to use `UnpackedScalar`s internally, and `Scalar`s - // externally, but there's no corresponding distinction for - // field elements. - - use zeroize::Zeroizing; - - let n = inputs.len(); - let one = Scalar::one(); - - // Place scratch storage in a Zeroizing wrapper to wipe it when - // we pass out of scope. - let scratch_vec = vec![one; n]; - let mut scratch = Zeroizing::new(scratch_vec); - - // Keep an accumulator of all of the previous products - let mut acc = Scalar::one(); - - // Pass through the input vector, recording the previous - // products in the scratch space - for (input, scratch) in inputs.iter().zip(scratch.iter_mut()) { - *scratch = acc; - - acc = acc * input; - } - - // acc is nonzero iff all inputs are nonzero - debug_assert!(acc != Scalar::zero()); - - // Compute the inverse of all products - acc = acc.invert().unwrap(); - - // We need to return the product of all inverses later - let ret = acc; - - // Pass through the vector backwards to compute the inverses - // in place - for (input, scratch) in inputs.iter_mut().rev().zip(scratch.iter().rev()) { - let tmp = &acc * input.clone(); - *input = &acc * scratch; - acc = tmp; - } - - ret - } - - - #[inline(always)] - const fn montgomery_reduce( - r0: u64, - r1: u64, - r2: u64, - r3: u64, - r4: u64, - r5: u64, - r6: u64, - r7: u64, - r8: u64, - ) -> Self { - // The Montgomery reduction here is based on Algorithm 14.32 in - // Handbook of Applied Cryptography - // . - - let k = r0.wrapping_mul(INV); - let (_, carry) = mac(r0, k, MODULUS.0[0], 0); - let (r1, carry) = mac(r1, k, MODULUS.0[1], carry); - let (r2, carry) = mac(r2, k, MODULUS.0[2], carry); - let (r3, carry) = mac(r3, k, MODULUS.0[3], carry); - // let (r4, carry2) = adc(r4, 0, carry); - let (r4, carry) = mac(r4, k, MODULUS.0[4], carry); - let (r5, carry2) = adc(r5, 0, carry); - - let k = r1.wrapping_mul(INV); - let (_, carry) = mac(r1, k, MODULUS.0[0], 0); - let (r2, carry) = mac(r2, k, MODULUS.0[1], carry); - let (r3, carry) = mac(r3, k, MODULUS.0[2], carry); - let (r4, carry) = mac(r4, k, MODULUS.0[3], carry); - // let (r5, carry2) = adc(r5, carry2, carry); - let (r5, carry) = mac(r5, k, MODULUS.0[4], carry); - let (r6, carry2) = adc(r6, carry2, carry); - - let k = r2.wrapping_mul(INV); - let (_, carry) = mac(r2, k, MODULUS.0[0], 0); - let (r3, carry) = mac(r3, k, MODULUS.0[1], carry); - let (r4, carry) = mac(r4, k, MODULUS.0[2], carry); - let (r5, carry) = mac(r5, k, MODULUS.0[3], carry); - // let (r6, carry2) = adc(r6, carry2, carry); - let (r6, carry) = mac(r6, k, MODULUS.0[4], carry); - let (r7, carry2) = adc(r7, carry2, carry); - - let k = r3.wrapping_mul(INV); - let (_, carry) = mac(r3, k, MODULUS.0[0], 0); - let (r4, carry) = mac(r4, k, MODULUS.0[1], carry); - let (r5, carry) = mac(r5, k, MODULUS.0[2], carry); - let (r6, carry) = mac(r6, k, MODULUS.0[3], carry); - // let (r7, _) = adc(r7, carry2, carry); - let (r7, carry) = mac(r7, k, MODULUS.0[4], carry); - // let (r8, _) = adc(r8, carry2, carry); - let (r8, _) = adc(r8, carry2, carry); - - // Result may be within MODULUS of the correct value - // (&Scalar([r4, r5, r6, r7])).sub(&MODULUS) - (&Scalar([r4, r5, r6, r7, r8])).sub(&MODULUS) - } - - /// Multiplies `rhs` by `self`, returning the result. - #[inline] - pub const fn mul(&self, rhs: &Self) -> Self { - assert!(rhs.0[4] == 0); - assert!(self.0[4] == 0); - // Schoolbook multiplication - - let (r0, carry) = mac(0, self.0[0], rhs.0[0], 0); - let (r1, carry) = mac(0, self.0[0], rhs.0[1], carry); - let (r2, carry) = mac(0, self.0[0], rhs.0[2], carry); - let (r3, r4) = mac(0, self.0[0], rhs.0[3], carry); - // let (r3, carry) = mac(0, self.0[0], rhs.0[3], carry); - // let (r4, r5) = mac(0, self.0[0], rhs.0[4], carry); // 0 + (self.0[0] * rhs.0[4]) + carry = carry - - let (r1, carry) = mac(r1, self.0[1], rhs.0[0], 0); - let (r2, carry) = mac(r2, self.0[1], rhs.0[1], carry); - let (r3, carry) = mac(r3, self.0[1], rhs.0[2], carry); - let (r4, r5) = mac(r4, self.0[1], rhs.0[3], carry); - // let (r4, carry) = mac(r4, self.0[1], rhs.0[3], carry); - // let (r5, r6) = mac(r5, self.0[1], rhs.0[4], carry); - - let (r2, carry) = mac(r2, self.0[2], rhs.0[0], 0); - let (r3, carry) = mac(r3, self.0[2], rhs.0[1], carry); - let (r4, carry) = mac(r4, self.0[2], rhs.0[2], carry); - let (r5, r6) = mac(r5, self.0[2], rhs.0[3], carry); - // let (r5, carry) = mac(r5, self.0[2], rhs.0[3], carry); - // let (r6, r7) = mac(r6, self.0[2], rhs.0[4], carry); - - let (r3, carry) = mac(r3, self.0[3], rhs.0[0], 0); - let (r4, carry) = mac(r4, self.0[3], rhs.0[1], carry); - let (r5, carry) = mac(r5, self.0[3], rhs.0[2], carry); - let (r6, r7) = mac(r6, self.0[3], rhs.0[3], carry); - // let (r6, carry) = mac(r6, self.0[3], rhs.0[3], carry); - // let (r7, r8) = mac(r7, self.0[3], rhs.0[4], carry); - - // let (r4, carry) = mac(r4, self.0[4], rhs.0[0], 0); // r4 + (self.0[4] * rhs.0[0]) + 0 = r4 - // let (r5, carry) = mac(r5, self.0[4], rhs.0[1], carry); - // let (r6, carry) = mac(r6, self.0[4], rhs.0[2], carry); - // let (r7, carry) = mac(r7, self.0[4], rhs.0[3], carry); - // let (r8, _) = mac(r8, self.0[4], rhs.0[4], carry); - - // Scalar::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7, 0) - Scalar::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7, 0) - } - - - /// Subtracts `rhs` from `self`, returning the result. - #[inline] - pub const fn sub(&self, rhs: &Self) -> Self { - let (d0, borrow) = sbb(self.0[0], rhs.0[0], 0); - let (d1, borrow) = sbb(self.0[1], rhs.0[1], borrow); - let (d2, borrow) = sbb(self.0[2], rhs.0[2], borrow); - let (d3, borrow) = sbb(self.0[3], rhs.0[3], borrow); - let (d4, borrow) = sbb(self.0[4], rhs.0[4], borrow); - - // If underflow occurred on the final limb, borrow = 0xfff...fff, otherwise - // borrow = 0x000...000. Thus, we use it as a mask to conditionally add the modulus. - let (d0, carry) = adc(d0, MODULUS.0[0] & borrow, 0); - let (d1, carry) = adc(d1, MODULUS.0[1] & borrow, carry); - let (d2, carry) = adc(d2, MODULUS.0[2] & borrow, carry); - // let (d3, _) = adc(d3, MODULUS.0[3] & borrow, carry); - let (d3, carry) = adc(d3, MODULUS.0[3] & borrow, carry); - let (d4, _) = adc(d4, MODULUS.0[4] & borrow, carry); - - // Scalar([d0, d1, d2, d3]) - Scalar([d0, d1, d2, d3, d4]) - } - - // /// Adds `rhs` to `self`, returning the result. - #[inline] - pub const fn add(&self, rhs: &Self) -> Self { - let (d0, carry) = adc(self.0[0], rhs.0[0], 0); - let (d1, carry) = adc(self.0[1], rhs.0[1], carry); - let (d2, carry) = adc(self.0[2], rhs.0[2], carry); - // let (d3, _) = adc(self.0[3], rhs.0[3], carry); - let (d3, carry) = adc(self.0[3], rhs.0[3], carry); - let (d4, _) = adc(self.0[4], rhs.0[4], carry); - - // Attempt to subtract the modulus, to ensure the value - // is smaller than the modulus. - (&Scalar([d0, d1, d2, d3, d4])).sub(&MODULUS) - } - - - /// Negates `self`. - #[inline] - pub const fn neg(&self) -> Self { - // Subtract `self` from `MODULUS` to negate. Ignore the final - // borrow because it cannot underflow; self is guaranteed to - // be in the field. - let (d0, borrow) = sbb(MODULUS.0[0], self.0[0], 0); - let (d1, borrow) = sbb(MODULUS.0[1], self.0[1], borrow); - let (d2, borrow) = sbb(MODULUS.0[2], self.0[2], borrow); - // let (d3, _) = sbb(MODULUS.0[3], self.0[3], borrow); - let (d3, borrow) = sbb(MODULUS.0[3], self.0[3], borrow); - let (d4, _) = sbb(MODULUS.0[4], self.0[4], borrow); - - // `tmp` could be `MODULUS` if `self` was zero. Create a mask that is - // zero if `self` was zero, and `u64::max_value()` if self was nonzero. - // let mask = (((self.0[0] | self.0[1] | self.0[2] | self.0[3]) == 0) as u64).wrapping_sub(1); - let mask = - (((self.0[0] | self.0[1] | self.0[2] | self.0[3] | self.0[4]) == 0) as u64).wrapping_sub(1); - - // Scalar([d0 & mask, d1 & mask, d2 & mask, d3 & mask]) - Scalar([d0 & mask, d1 & mask, d2 & mask, d3 & mask, d4 & mask]) - } - - #[inline] // ** to test - pub fn to_bigint(&self) -> BigInteger256 { // for vartime_multiscalar_mul - let scalar_reduced = Self::montgomery_reduce( - self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], 0, 0, 0, 0, - ); - BigInteger256::new(scalar_reduced.0[..4].try_into().expect("Length 4 array")) - } -} - -impl<'a> From<&'a Scalar> for [u8; 32] { - fn from(value: &'a Scalar) -> [u8; 32] { - value.to_bytes() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use circ_fields::t256::ScalarField; - use ark_serialize::CanonicalDeserialize; - - #[test] - fn test_inv() { - // Compute -(q^{-1} mod 2^64) mod 2^64 by exponentiating - // by totient(2**64) - 1 - - let mut inv = 1u64; - for _ in 0..63 { - inv = inv.wrapping_mul(inv); - inv = inv.wrapping_mul(MODULUS.0[0]); - } - inv = inv.wrapping_neg(); - - assert_eq!(inv, INV); - } - - #[cfg(feature = "std")] - #[test] - fn test_debug() { - assert_eq!( - format!("{:?}", Scalar::zero()), - "0x0000000000000000000000000000000000000000000000000000000000000000" - ); - assert_eq!( - format!("{:?}", Scalar::one()), - "0x0000000000000000000000000000000000000000000000000000000000000001" - ); - //let R2_format = format!("{:?}", R2); - // println!("In test_debug R2 = {:?}; R2_format {:?}", R2, R2_format); - // assert_eq!( - // format!("{:?}", R2), - // "0x0ffffffffffffffffffffffffffffffec6ef5bf4737dcf70d6ec31748d98951d" - // ); - } - - #[test] - fn test_equality() { - assert_eq!(Scalar::zero(), Scalar::zero()); - assert_eq!(Scalar::one(), Scalar::one()); - assert_eq!(R2, R2); - - assert!(Scalar::zero() != Scalar::one()); - assert!(Scalar::one() != R2); - } - - #[test] - fn test_to_bytes() { - assert_eq!( - Scalar::zero().to_bytes(), - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0 - ] - ); - - assert_eq!( - Scalar::one().to_bytes(), - [ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0 - ] - ); - - let one_bytes = Scalar::one().to_bytes(); - let scalarbytes: ScalarField = ScalarField::deserialize_compressed(&one_bytes[..]).unwrap();// ScalarField::from(1); - assert_eq!( - ScalarField::from(1), - scalarbytes - ); - let number: u64 = 18446744073709551615; - let scalar: Scalar = Scalar::from(number); - assert_eq!( - ScalarField::deserialize_compressed(&scalar.to_bytes()[..]).unwrap(), - ScalarField::from(number) - ); - //let largest_bytes = LARGEST.to_bytes(); - //println!("largest bytes {:?}", largest_bytes); - // println!("test t256 scalar successfully -1 = {:?}", ScalarField::from(-1)); - // assert_eq!( - // R2.to_bytes(), - // [ - // 29, 149, 152, 141, 116, 49, 236, 214, 112, 207, 125, 115, 244, 91, 239, 198, 254, 255, 255, - // 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 15 - // ] - // ); - - // assert_eq!( - // (-&Scalar::one()).to_bytes(), - // [ - // 236, 211, 245, 92, 26, 99, 18, 88, 214, 156, 247, 162, 222, 249, 222, 20, 0, 0, 0, 0, 0, 0, - // 0, 0, 0, 0, 0, 0, 0, 0, 0, 16 - // ] - // ); - } - - #[test] - fn test_from_bytes() { - assert_eq!( - Scalar::from_bytes(&[ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0 - ]) - .unwrap(), - Scalar::zero() - ); - - assert_eq!( - Scalar::from_bytes(&[ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0 - ]) - .unwrap(), - Scalar::one() - ); - - // assert_eq!( - // Scalar::from_bytes(&[ - // 29, 149, 152, 141, 116, 49, 236, 214, 112, 207, 125, 115, 244, 91, 239, 198, 254, 255, 255, - // 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 15 - // ]) - // .unwrap(), - // R2 - // ); - - // // -1 should work - // assert!( - // Scalar::from_bytes(&[ - // 236, 211, 245, 92, 26, 99, 18, 88, 214, 156, 247, 162, 222, 249, 222, 20, 0, 0, 0, 0, 0, 0, - // 0, 0, 0, 0, 0, 0, 0, 0, 0, 16 - // ]) - // .is_some() - // .unwrap_u8() - // == 1 - // ); - - // // modulus is invalid - // assert!( - // Scalar::from_bytes(&[ - // 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, 216, - // 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 - // ]) - // .is_none() - // .unwrap_u8() - // == 1 - // ); - - // // Anything larger than the modulus is invalid - // assert!( - // Scalar::from_bytes(&[ - // 2, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, 216, - // 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 - // ]) - // .is_none() - // .unwrap_u8() - // == 1 - // ); - // assert!( - // Scalar::from_bytes(&[ - // 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, 216, - // 58, 51, 72, 125, 157, 41, 83, 167, 237, 115 - // ]) - // .is_none() - // .unwrap_u8() - // == 1 - // ); - // assert!( - // Scalar::from_bytes(&[ - // 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, 216, - // 57, 51, 72, 125, 157, 41, 83, 167, 237, 116 - // ]) - // .is_none() - // .unwrap_u8() - // == 1 - // ); - } - - #[test] - fn test_from_u512_zero() { - assert_eq!( - Scalar::zero(), - Scalar::from_u512([ - MODULUS.0[0], - MODULUS.0[1], - MODULUS.0[2], - MODULUS.0[3], - 0, - 0, - 0, - 0 - ]) - ); - } - - #[test] - fn test_from_u512_r() { - assert_eq!(R, Scalar::from_u512([1, 0, 0, 0, 0, 0, 0, 0])); - } - - #[test] - fn test_from_u512_r2() { - assert_eq!(R2, Scalar::from_u512([0, 0, 0, 0, 1, 0, 0, 0])); - } - - #[test] - fn test_from_u512_max() { - let max_u64 = 0xffffffffffffffff; - assert_eq!( - R3 - R, - Scalar::from_u512([max_u64, max_u64, max_u64, max_u64, max_u64, max_u64, max_u64, max_u64]) - ); - } - -// #[test] -// fn test_from_bytes_wide_r2() { -// assert_eq!( -// R2, -// Scalar::from_bytes_wide(&[ -// 29, 149, 152, 141, 116, 49, 236, 214, 112, 207, 125, 115, 244, 91, 239, 198, 254, 255, 255, -// 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, -// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -// ]) -// ); -// } - -// #[test] -// fn test_from_bytes_wide_negative_one() { -// assert_eq!( -// -&Scalar::one(), -// Scalar::from_bytes_wide(&[ -// 236, 211, 245, 92, 26, 99, 18, 88, 214, 156, 247, 162, 222, 249, 222, 20, 0, 0, 0, 0, 0, 0, -// 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -// ]) -// ); -// } - - // #[test] - // fn test_from_bytes_wide_maximum() { - // assert_eq!( - // Scalar::from_raw([ - // 0xa40611e3449c0f00, - // 0xd00e1ba768859347, - // 0xceec73d217f5be65, - // 0x0399411b7c309a3d - // ]), - // Scalar::from_bytes_wide(&[0xff; 64]) - // ); - // } - - #[test] - fn test_zero() { - assert_eq!(Scalar::zero(), -&Scalar::zero()); - assert_eq!(Scalar::zero(), Scalar::zero() + Scalar::zero()); - assert_eq!(Scalar::zero(), Scalar::zero() - Scalar::zero()); - assert_eq!(Scalar::zero(), Scalar::zero() * Scalar::zero()); - } - -// const LARGEST: Scalar = Scalar([ -// 0x5812631a5cf5d3ec, -// 0x14def9dea2f79cd6, -// 0x0000000000000000, -// 0x1000000000000000, -// ]); -// MODULUS - 1 - const LARGEST: Scalar = Scalar([ - 0xfffffffffffffffe, - 0x00000000ffffffff, - 0x0000000000000000, - 0xffffffff00000001, - 0, - ]); - - #[test] - fn test_addition() { - let mut tmp = LARGEST; - - tmp += &LARGEST; - - - let target = Scalar([ // LARGEST + LARGEST % MODULUS - 0xfffffffffffffffd, - 0x00000000ffffffff, - 0x0000000000000000, - 0xffffffff00000001, - 0, - ]); - assert_eq!(tmp, target); - let mut tmp = LARGEST; - tmp += &Scalar([1, 0, 0, 0, 0]); - - assert_eq!(tmp, Scalar::zero()); - } - - #[test] - fn test_negation() { - let tmp = -&LARGEST; - - assert_eq!(tmp, Scalar([1, 0, 0, 0, 0])); - - let tmp = -&Scalar::zero(); - assert_eq!(tmp, Scalar::zero()); - let tmp = -&Scalar([1, 0, 0, 0, 0]); - assert_eq!(tmp, LARGEST); - } - - #[test] - fn test_subtraction() { - let mut tmp = LARGEST; - tmp -= &LARGEST; - - assert_eq!(tmp, Scalar::zero()); - - let mut tmp = Scalar::zero(); - tmp -= &LARGEST; - - let mut tmp2 = MODULUS; - tmp2 -= &LARGEST; - - assert_eq!(tmp, tmp2); - } - - #[test] - fn test_multiplication() { - let mut cur = LARGEST; - - for _ in 0..100 { - let mut tmp = cur; - tmp *= &cur; - - let mut tmp2 = Scalar::zero(); - for b in cur - .to_bytes() - .iter() - .rev() - .flat_map(|byte| (0..8).rev().map(move |i| ((byte >> i) & 1u8) == 1u8)) - { - let tmp3 = tmp2; - tmp2.add_assign(&tmp3); - - if b { - tmp2.add_assign(&cur); - } - } - - assert_eq!(tmp, tmp2); - - cur.add_assign(&LARGEST); - } - } - - #[test] - fn test_squaring() { - let mut cur = LARGEST; - - for _ in 0..100 { - let mut tmp = cur; - tmp = tmp.square(); - - let mut tmp2 = Scalar::zero(); - for b in cur - .to_bytes() - .iter() - .rev() - .flat_map(|byte| (0..8).rev().map(move |i| ((byte >> i) & 1u8) == 1u8)) - { - let tmp3 = tmp2; - tmp2.add_assign(&tmp3); - - if b { - tmp2.add_assign(&cur); - } - } - - assert_eq!(tmp, tmp2); - - cur.add_assign(&LARGEST); - } - } - - #[test] - fn test_inversion() { - assert_eq!(Scalar::zero().invert().is_none().unwrap_u8(), 1); - assert_eq!(Scalar::one().invert().unwrap(), Scalar::one()); - assert_eq!((-&Scalar::one()).invert().unwrap(), -&Scalar::one()); - - let a = Scalar::from(123); - let _result = a.invert().unwrap(); - - let mut tmp = R2; - - for _ in 0..100 { - let mut tmp2 = tmp.invert().unwrap(); - tmp2.mul_assign(&tmp); - - assert_eq!(tmp2, Scalar::one()); - - tmp.add_assign(&R2); - } - } - - #[test] - fn test_invert_is_pow() { - let q_minus_2 = [ - 0xfffffffffffffffd, - 0x00000000ffffffff, - 0x0000000000000000, - 0xffffffff00000001, - ]; - - let mut r1 = R; - let mut r2 = R; - let mut r3 = R; - - for _ in 0..100 { - r1 = r1.invert().unwrap(); - r2 = r2.pow_vartime(&q_minus_2); - r3 = r3.pow(&q_minus_2); - - assert_eq!(r1, r2); - assert_eq!(r2, r3); - // Add R so we check something different next time around - r1.add_assign(&R); - r2 = r1; - r3 = r1; - } - } - - // #[test] - // fn test_from_raw() { - // assert_eq!( - // Scalar::from_raw([ - // 0xd6ec31748d98951c, - // 0xc6ef5bf4737dcf70, - // 0xfffffffffffffffe, - // 0x0fffffffffffffff - // ]), - // Scalar::from_raw([0xffffffffffffffff; 4]) - // ); - - // assert_eq!(Scalar::from_raw(MODULUS.0), Scalar::zero()); - - // assert_eq!(Scalar::from_raw([1, 0, 0, 0]), R); - // } - - #[test] - fn test_double() { - let a = Scalar::from_raw([ - 0x1fff3231233ffffd, - 0x4884b7fa00034802, - 0x998c4fefecbc4ff3, - 0x1824b159acc50562, - ]); - - assert_eq!(a.double(), a + a); - } -} \ No newline at end of file diff --git a/forks/Spartan-t256/src/sparse_mlpoly.rs b/forks/Spartan-t256/src/sparse_mlpoly.rs index 1e10f6a3..489872ec 100644 --- a/forks/Spartan-t256/src/sparse_mlpoly.rs +++ b/forks/Spartan-t256/src/sparse_mlpoly.rs @@ -1610,10 +1610,12 @@ mod tests { use super::*; use rand::rngs::OsRng; use rand::RngCore; + use ff::Field; #[test] fn check_sparse_polyeval_proof() { let mut csprng: OsRng = OsRng; - + let mut osrng = rand_core::OsRng; + let num_nz_entries: usize = 256; let num_rows: usize = 256; let num_cols: usize = 256; @@ -1626,7 +1628,7 @@ mod tests { M.push(SparseMatEntry::new( (csprng.next_u64() % (num_rows as u64)) as usize, (csprng.next_u64() % (num_cols as u64)) as usize, - Scalar::random(&mut csprng), + Scalar::random(&mut osrng), )); } @@ -1644,10 +1646,10 @@ mod tests { // evaluation let rx: Vec = (0..num_vars_x) - .map(|_i| Scalar::random(&mut csprng)) + .map(|_i| Scalar::random(&mut osrng)) .collect::>(); let ry: Vec = (0..num_vars_y) - .map(|_i| Scalar::random(&mut csprng)) + .map(|_i| Scalar::random(&mut osrng)) .collect::>(); let eval = SparseMatPolynomial::multi_evaluate(&[&poly_M], &rx, &ry); let evals = vec![eval[0], eval[0], eval[0]]; diff --git a/forks/Spartan-t256/src/transcript.rs b/forks/Spartan-t256/src/transcript.rs index a932c0de..4bcb988d 100644 --- a/forks/Spartan-t256/src/transcript.rs +++ b/forks/Spartan-t256/src/transcript.rs @@ -1,6 +1,7 @@ use super::group::{CompressedGroup, AsBytesDev}; use super::scalar::Scalar; use merlin::Transcript; +use ff::FromUniformBytes; pub trait ProofTranscript { fn append_protocol_name(&mut self, protocol_name: &'static [u8]); @@ -27,7 +28,7 @@ impl ProofTranscript for Transcript { fn challenge_scalar(&mut self, label: &'static [u8]) -> Scalar { let mut buf = [0u8; 64]; self.challenge_bytes(label, &mut buf); - Scalar::from_bytes_wide(&buf) + Scalar::from_uniform_bytes(&buf) } fn challenge_vector(&mut self, label: &'static [u8], len: usize) -> Vec { diff --git a/forks/Spartan-t256/src/unipoly.rs b/forks/Spartan-t256/src/unipoly.rs index dcc39185..958c74e5 100644 --- a/forks/Spartan-t256/src/unipoly.rs +++ b/forks/Spartan-t256/src/unipoly.rs @@ -4,6 +4,7 @@ use super::scalar::{Scalar, ScalarFromPrimitives}; use super::transcript::{AppendToTranscript, ProofTranscript}; use merlin::Transcript; use serde::{Deserialize, Serialize}; +use ff::Field; // ax^2 + bx + c stored as vec![c,b,a] // ax^3 + bx^2 + cx + d stored as vec![d,c,b,a] diff --git a/forks/halo2curves/.config/nextest.toml b/forks/halo2curves/.config/nextest.toml new file mode 100644 index 00000000..d79bd47e --- /dev/null +++ b/forks/halo2curves/.config/nextest.toml @@ -0,0 +1,10 @@ +[profile.ci] +# Print out output for failing tests as soon as they fail, and also at the end +# of the run (for easy scrollability). +failure-output = "immediate-final" +# Show skipped tests in the CI output. +status-level = "skip" +# Do not cancel the test run on the first failure. +fail-fast = false +# Mark tests as slow after 5mins, kill them after 50 +slow-timeout = { period = "300s", terminate-after = 10 } diff --git a/forks/halo2curves/.config/typos.toml b/forks/halo2curves/.config/typos.toml new file mode 100644 index 00000000..db40766b --- /dev/null +++ b/forks/halo2curves/.config/typos.toml @@ -0,0 +1,3 @@ +[default.extend-words] +groth = "groth" +ba = "ba" diff --git a/forks/halo2curves/.gitignore b/forks/halo2curves/.gitignore new file mode 100644 index 00000000..4def0c40 --- /dev/null +++ b/forks/halo2curves/.gitignore @@ -0,0 +1,10 @@ +target +Cargo.lock +**/*.rs.bk +.vscode +**/*.html +.DS_Store + +# script generated source code +src/bn256/fr/table.rs + diff --git a/forks/halo2curves/Cargo.toml b/forks/halo2curves/Cargo.toml new file mode 100644 index 00000000..e7b8f46f --- /dev/null +++ b/forks/halo2curves/Cargo.toml @@ -0,0 +1,89 @@ +[package] +name = "halo2curves" +version = "0.8.0" +authors = ["Privacy Scaling Explorations team"] +license = "MIT/Apache-2.0" +edition = "2021" +repository = "https://github.com/privacy-scaling-explorations/halo2curves" +readme = "README.md" +description = "Elliptic curve implementations and wrappers for halo2 library" +rust-version = "1.63.0" + +[dev-dependencies] +criterion = { version = "0.3", features = ["html_reports"] } +rand_xorshift = "0.3" +ark-std = { version = "0.3" } +bincode = "1.3.3" +serde_json = "1.0.105" +hex = "0.4" +rand_chacha = "0.3.1" +impls = "1" + +# Added to make sure we are able to build the lib in the CI. +# Notice this will never be loaded for someone using this lib as dep. +[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dev-dependencies] +getrandom = { version = "0.2", features = ["js"] } + +[dependencies] +halo2derive = {path = "derive", version="0.1.0"} +subtle = "2.5" +ff = { version = "0.13.0", default-features = false, features = ["std"] } +group = "0.13.0" +pairing = "0.23.0" +static_assertions = "1.1.0" +rand = "0.8" +rand_core = { version = "0.6", default-features = false } +lazy_static = "1.4.0" +num-bigint = "0.4.3" +num-integer = "0.1.46" +num-traits = "0.2" +paste = "1.0.11" +serde = { version = "1.0", default-features = false, optional = true } +serde_arrays = { version = "0.1.0", optional = true } +hex = { version = "0.4", optional = true, default-features = false, features = ["alloc", "serde"] } +rayon = "1.8" +unroll = "0.1.5" +blake2 = "0.10.6" +sha2 = "0.10.8" +digest = "0.10.7" + +[features] +default = ["bits"] +asm = ["halo2derive/asm"] +bits = ["ff/bits"] +bn256-table = [] +derive_serde = ["serde/derive", "serde_arrays", "hex"] +print-trace = ["ark-std/print-trace"] + +[profile.bench] +opt-level = 3 +debug = false +debug-assertions = false +overflow-checks = false +lto = true +incremental = false +codegen-units = 1 + +[[bench]] +name = "field_arith" +harness = false + +[[bench]] +name = "curve" +harness = false + +[[bench]] +name = "hash_to_curve" +harness = false + +[[bench]] +name = "fft" +harness = false + +[[bench]] +name = "msm" +harness = false + +[[bench]] +name = "pairing" +harness = false diff --git a/forks/Spartan-t256/circ_fields/ff-derive-num/LICENSE-apache b/forks/halo2curves/LICENSE-APACHE similarity index 99% rename from forks/Spartan-t256/circ_fields/ff-derive-num/LICENSE-apache rename to forks/halo2curves/LICENSE-APACHE index 1e5006dc..f8e5e5ea 100644 --- a/forks/Spartan-t256/circ_fields/ff-derive-num/LICENSE-apache +++ b/forks/halo2curves/LICENSE-APACHE @@ -198,5 +198,4 @@ 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. - +limitations under the License. \ No newline at end of file diff --git a/forks/halo2curves/LICENSE-MIT b/forks/halo2curves/LICENSE-MIT new file mode 100644 index 00000000..31aa7938 --- /dev/null +++ b/forks/halo2curves/LICENSE-MIT @@ -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. diff --git a/forks/halo2curves/Makefile b/forks/halo2curves/Makefile new file mode 100644 index 00000000..d7fe60a3 --- /dev/null +++ b/forks/halo2curves/Makefile @@ -0,0 +1,24 @@ +doc: + cargo doc --no-deps + +.PHONY: doc + +# Since the master branch is protected, the current workflow is to create a PR with the version changes, +# and once the PR is merged, run the `make VERSION= release` to publish the new crates. +release: +ifndef VERSION + $(error VERSION is not set. Run with `make VERSION= release`) +endif +ifeq (, $(shell cargo --list|grep release)) + $(error "Please, install cargo-release in order to be able to use this rule") +endif + git pull + cargo update + git tag v$(VERSION) + @TRACKING_BRANCH=$$(git rev-parse --abbrev-ref --symbolic-full-name @{u} 2> /dev/null) ;\ + if [ "$$TRACKING_BRANCH" == "" ]; then \ + echo "Error: Current branch does not have an upstream tracking branch." ;\ + exit 1 ;\ + fi ;\ + git push --tags $$(echo $$TRACKING_BRANCH | sed 's!/.*!!') v$(VERSION) + cargo release publish --execute --verbose \ No newline at end of file diff --git a/forks/halo2curves/README-fork b/forks/halo2curves/README-fork new file mode 100644 index 00000000..711d1817 --- /dev/null +++ b/forks/halo2curves/README-fork @@ -0,0 +1,100 @@ + +Fork of halo2curves that adds support for the T256 curve +https://neuromancer.sk/std/other/Tom-256 + +HEAD was b6fa078aa36d04d7eb654a1e2f823e3fdf971041 + +Changes: +- Adds src/t256 +and adds it to lib.rs: + +diff --git a/src/lib.rs b/src/lib.rs +index 35b2bec..2e21ce0 100644 +--- a/src/lib.rs ++++ b/src/lib.rs +@@ -14,6 +14,7 @@ pub mod pluto_eris; + pub mod secp256k1; + pub mod secp256r1; + pub mod secq256k1; ++pub mod t256; + + #[macro_use] + mod derive; + + +To run some MSM benchmarks, apply this patch + ++++ b/benches/msm.rs +@@ -18,7 +18,7 @@ use criterion::{BenchmarkId, Criterion}; + use ff::{Field, PrimeField}; + use group::prime::PrimeCurveAffine; + use halo2curves::{ +- bn256::{Fr as Scalar, G1Affine as Point}, ++ t256::{Fq as Scalar, T256Affine as Point}, + msm::{msm_best, msm_serial}, + }; + use rand_core::{RngCore, SeedableRng}; +@@ -29,8 +29,11 @@ use rayon::{ + }; + + const SAMPLE_SIZE: usize = 10; +-const SINGLECORE_RANGE: [u8; 6] = [3, 8, 10, 12, 14, 16]; +-const MULTICORE_RANGE: [u8; 9] = [3, 8, 10, 12, 14, 16, 18, 20, 22]; ++//const SINGLECORE_RANGE: [u8; 6] = [3, 8, 10, 12, 14, 16]; ++//const SINGLECORE_RANGE: [u8; 3] = [10, 12, 14]; ++const SINGLECORE_RANGE: [u8; 1] = [1]; ++//const MULTICORE_RANGE: [u8; 9] = [3, 8, 10, 12, 14, 16, 18, 20, 22]; ++const MULTICORE_RANGE: [u8; 4] = [10, 11, 13, 14]; + const SEED: [u8; 16] = [ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, + ]; +@@ -127,7 +130,8 @@ fn msm(c: &mut Criterion) { + .max() + .unwrap_or(&16); + let bases = generate_curvepoints(max_k); +- let bits = [1, 8, 16, 32, 64, 128, 256]; ++// let bits = [1, 8, 16, 32, 64, 128, 256]; ++ let bits = [256]; + let coeffs: Vec<_> = bits + .iter() + .map(|b| generate_coefficients(max_k, *b)) +@@ -161,5 +165,37 @@ fn msm(c: &mut Criterion) { + group.finish(); + } + +-criterion_group!(benches, msm); ++fn simple_msm(c: &mut Criterion) { ++ let mut group = c.benchmark_group("simple_msm"); ++ ++ let max_k = 14; ++ ++ let bases = generate_curvepoints(max_k); ++ let scalars = generate_coefficients(max_k, 256); ++ ++ for k in [11, 12, 13, 14] { ++ let id = format!("msm with 2**{k} terms"); ++ group ++ .bench_function(BenchmarkId::new("singlecore", id.clone()), |b| { ++ assert!(k < 64); ++ let n: usize = 1 << k; ++ let mut acc = Point::identity().into(); ++ b.iter(|| msm_serial(&scalars[..n], &bases[..n], &mut acc)); ++ }) ++ .sample_size(10); ++ ++ group ++ .bench_function(BenchmarkId::new("multicore", id), |b| { ++ assert!(k < 64); ++ let n: usize = 1 << k; ++ b.iter(|| { ++ halo2curves::msm::msm_parallel(&scalars[..n], &bases[..n]); ++ }) ++ }) ++ .sample_size(SAMPLE_SIZE); ++ } ++ group.finish(); ++} ++ ++criterion_group!(benches, simple_msm); + criterion_main!(benches); + diff --git a/forks/halo2curves/README.md b/forks/halo2curves/README.md new file mode 100644 index 00000000..bfe3b538 --- /dev/null +++ b/forks/halo2curves/README.md @@ -0,0 +1,62 @@ +# A collection of Elliptic Curves for ZkCrypto traits + +[![crates.io version](https://img.shields.io/crates/v/halo2curves.svg)](https://crates.io/crates/halo2curves) +[![docs.rs availability](https://img.shields.io/docsrs/halo2curves?label=docs.rs)](https://docs.rs/halo2curves) +[![Build status](https://img.shields.io/github/actions/workflow/status/privacy-scaling-explorations/halo2curves/ci.yml?branch=main)](https://github.com/privacy-scaling-explorations/halo2curves/actions) + +This library provides efficient and flexible implementations of various halo2-friendly elliptic curves, originally implementing the BN256 curve with traits from the `zkcrypto` ecosystem, + +* [`zkcrypto/ff`](https://github.com/zkcrypto/ff) +* [`zkcrypto/group`](https://github.com/zkcrypto/group) +* [`zkcrypto/pairing`](https://github.com/zkcrypto/pairing) + +The implementations were originally ported from [matterlabs/pairing](https://github.com/matter-labs/pairing/tree/master/src/bn256) and [zkcrypto/bls12-381](https://github.com/zkcrypto/bls12_381), but have been extended and optimized to cover a broader set of curves and use cases. Since its initial release, the library has expanded to include additional curves, along with the following features: + +* `secp256k1`, `secp256r1`, `pluto`, `eris` and `grumpkin` curves, enhancing its usability across a range of cryptographic protocols. +* Assembly optimizations leading to significantly improved performance. +* Various features related to serialization and deserialization of curve points and field elements. +* Curve-specific optimizations and benchmarking capabilities. + +## Controlling parallelism + +`halo2curves` currently uses [rayon](https://github.com/rayon-rs/rayon) for parallel +computation. + +The `RAYON_NUM_THREADS` environment variable can be used to set the number of +threads. + +When compiling to WASM-targets, notice that since version `1.7`, `rayon` will fallback automatically (with no need to handle features) to require `getrandom` in order to be able to work. +For more info related to WASM-compilation. + +See: [Rayon: Usage with WebAssembly](https://github.com/rayon-rs/rayon#usage-with-webassembly) for more info. + +## Benchmarks + +Benchmarking is supported through the use of Rust's built-in test framework. Benchmarks can be run without assembly optimizations: + +``` +$ cargo test --profile bench test_field -- --nocapture +``` + +or with assembly optimizations: + +``` +$ cargo test --profile bench test_field --features asm -- --nocapture +``` + + +## Additional Features + +1. **Derivation of Serialize/Deserialize**: The library supports Serde's `Serialize` and `Deserialize` traits for field and group elements, making it easier to integrate curve operations into serialization-dependent workflows. + +2. **Hash to Curve**: For the `bn256::G1` and `grumpkin::G1` curves, `hash_to_curve` is implemented, enabling more efficient hash-and-sign signature schemes. + +3. **Lookup Table**: A pre-computed lookup table is available for `bn256::Fr`, accelerating conversion from `u16` to montgomery representation. + +## Structure + +The library's top-level directories are organized as follows: + +* `benches`: Contains benchmarking tests. +* `script`: Contains utility scripts. +* `src`: Contains the source code of the library, further subdivided into modules for each supported curve (`bn256`, `grumpkin`, `secp256k1`, `secp256r1`, `secq256k1`, `pasta`, `pluto`, `eris`) and additional functionalities (`derive`, `tests`). diff --git a/forks/halo2curves/benches/curve.rs b/forks/halo2curves/benches/curve.rs new file mode 100644 index 00000000..cb647651 --- /dev/null +++ b/forks/halo2curves/benches/curve.rs @@ -0,0 +1,73 @@ +//! This benchmarks the basic EC operations. +//! It measures `G1` from the BN256 curve. +//! +//! To run this benchmark: +//! +//! cargo bench --bench curve + +use criterion::{black_box, criterion_group, criterion_main, Criterion, Throughput}; +use ff::Field; +use group::prime::PrimeCurveAffine; +use halo2curves::{bn256::G1, CurveExt}; +use rand::SeedableRng; +use rand_xorshift::XorShiftRng; + +fn bench_curve_ops(c: &mut Criterion, name: &'static str) { + { + let mut rng = XorShiftRng::seed_from_u64(3141519u64); + + // Generate 2 random points. + let mut p1 = G::random(&mut rng); + let p2 = G::random(&mut rng); + p1 += p2; + + let p1_affine = G::AffineExt::from(p1); + + let s = G::ScalarExt::random(&mut rng); + + const N: usize = 1000; + let v: Vec = (0..N).map(|_| p1 + G::random(&mut rng)).collect(); + + let mut q = vec![G::AffineExt::identity(); N]; + + let mut group = c.benchmark_group(format!("{} arithmetic", name)); + + group.significance_level(0.1).sample_size(1000); + group.throughput(Throughput::Elements(1)); + + group.bench_function(&format!("{name} check on curve"), move |b| { + b.iter(|| black_box(p1).is_on_curve()) + }); + group.bench_function(&format!("{name} check equality"), move |b| { + b.iter(|| black_box(p1) == black_box(p1)) + }); + group.bench_function(&format!("{name} to affine"), move |b| { + b.iter(|| G::AffineExt::from(black_box(p1))) + }); + group.bench_function(&format!("{name} doubling"), move |b| { + b.iter(|| black_box(p1).double()) + }); + group.bench_function(&format!("{name} addition"), move |b| { + b.iter(|| black_box(p1).add(&p2)) + }); + group.bench_function(&format!("{name} mixed addition"), move |b| { + b.iter(|| black_box(p2).add(&p1_affine)) + }); + group.bench_function(&format!("{name} scalar multiplication"), move |b| { + b.iter(|| black_box(p1) * black_box(s)) + }); + group.bench_function(&format!("{name} batch to affine n={N}"), move |b| { + b.iter(|| { + G::batch_normalize(black_box(&v), black_box(&mut q)); + }) + }); + group.finish(); + } +} + +fn bench_bn256_ops(c: &mut Criterion) { + bench_curve_ops::(c, "BN256") +} + +criterion_group!(benches, bench_bn256_ops); +criterion_main!(benches); diff --git a/forks/halo2curves/benches/fft.rs b/forks/halo2curves/benches/fft.rs new file mode 100644 index 00000000..8918e1a0 --- /dev/null +++ b/forks/halo2curves/benches/fft.rs @@ -0,0 +1,61 @@ +//! This benchmarks Fast-Fourier Transform (FFT). +//! Since it is over a finite field, it is actually the Number Theoretical +//! Transform (NNT). It uses the `Fr` scalar field from the BN256 curve. +//! +//! To run this benchmark: +//! +//! cargo bench --bench fft +//! +//! Caveat: The multicore benchmark assumes: +//! 1. a multi-core system +//! 2. that the `multicore` feature is enabled. It is by default. + +#[macro_use] +extern crate criterion; + +use std::{ops::Range, time::SystemTime}; + +use criterion::{BenchmarkId, Criterion}; +use group::ff::Field; +use halo2curves::{bn256::Fr as Scalar, fft::best_fft}; +use rand::{RngCore, SeedableRng}; +use rand_xorshift::XorShiftRng; + +const RANGE: Range = 3..19; +const SEED: [u8; 16] = [ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, +]; + +fn generate_data(k: u32, mut rng: impl RngCore) -> Vec { + let n = 1 << k; + let timer = SystemTime::now(); + println!("\n\nGenerating 2^{k} = {n} values..",); + let data: Vec = (0..n).map(|_| Scalar::random(&mut rng)).collect(); + let end = timer.elapsed().unwrap(); + println!( + "Generating 2^{k} = {n} values took: {} sec.\n\n", + end.as_secs() + ); + data +} + +fn fft(c: &mut Criterion) { + let max_k = RANGE.max().unwrap_or(16); + let mut rng = XorShiftRng::from_seed(SEED); + let mut data = generate_data(max_k, &mut rng); + let omega = Scalar::random(&mut rng); + let mut group = c.benchmark_group("fft"); + for k in RANGE { + group.bench_function(BenchmarkId::new("k", k), |b| { + let n = 1 << k; + assert!(n <= data.len()); + b.iter(|| { + best_fft(&mut data[..n], omega, k); + }); + }); + } + group.finish(); +} + +criterion_group!(benches, fft); +criterion_main!(benches); diff --git a/forks/halo2curves/benches/field_arith.rs b/forks/halo2curves/benches/field_arith.rs new file mode 100644 index 00000000..2edcaef2 --- /dev/null +++ b/forks/halo2curves/benches/field_arith.rs @@ -0,0 +1,70 @@ +//! This benchmarks the basic FF operations. +//! It measures the base field `Fq` and scalar field `Fr` from the BN256 curve. +//! +//! To run this benchmark: +//! +//! cargo bench --bench field_arith + +use criterion::{black_box, criterion_group, criterion_main, Criterion, Throughput}; +use halo2curves::{ + bn256::{Fq, Fr}, + ff::Field, + ff_ext::Legendre, +}; +use rand::{RngCore, SeedableRng}; +use rand_xorshift::XorShiftRng; + +const SEED: [u8; 16] = [ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, +]; +fn bench_field_arithmetic(c: &mut Criterion, name: &'static str) { + let mut rng = XorShiftRng::from_seed(SEED); + + let a = ::random(&mut rng); + let b = ::random(&mut rng); + let exp = rng.next_u64(); + + let mut group = c.benchmark_group(format!("{} arithmetic", name)); + + group.significance_level(0.1).sample_size(1000); + group.throughput(Throughput::Elements(1)); + + group.bench_function(format!("{}_add", name), |bencher| { + bencher.iter(|| black_box(&a).add(black_box(&b))) + }); + group.bench_function(format!("{}_double", name), |bencher| { + bencher.iter(|| black_box(&a).double()) + }); + group.bench_function(format!("{}_sub", name), |bencher| { + bencher.iter(|| black_box(&a).sub(black_box(&b))) + }); + group.bench_function(format!("{}_neg", name), |bencher| { + bencher.iter(|| black_box(&a).neg()) + }); + group.bench_function(format!("{}_mul", name), |bencher| { + bencher.iter(|| black_box(&a).mul(black_box(&b))) + }); + group.bench_function(format!("{}_square", name), |bencher| { + bencher.iter(|| black_box(&a).square()) + }); + group.bench_function(format!("{}_pow_vartime", name), |bencher| { + bencher.iter(|| black_box(&a).pow_vartime(black_box(&[exp]))) + }); + group.bench_function(format!("{}_invert", name), |bencher| { + bencher.iter(|| black_box(&a).invert()) + }); + group.bench_function(format!("{}_legendre", name), |bencher| { + bencher.iter(|| black_box(&a).legendre()) + }); + group.finish() +} + +fn bench_bn256_base_field(c: &mut Criterion) { + bench_field_arithmetic::(c, "Fq") +} +fn bench_bn256_scalar_field(c: &mut Criterion) { + bench_field_arithmetic::(c, "Fr") +} + +criterion_group!(benches, bench_bn256_base_field, bench_bn256_scalar_field); +criterion_main!(benches); diff --git a/forks/halo2curves/benches/hash_to_curve.rs b/forks/halo2curves/benches/hash_to_curve.rs new file mode 100644 index 00000000..eda2ac78 --- /dev/null +++ b/forks/halo2curves/benches/hash_to_curve.rs @@ -0,0 +1,45 @@ +//! This benchmarks basic the hash-to-curve algorithm. +//! It measures `G1` from the BN256 curve. +//! +//! To run this benchmark: +//! +//! cargo bench --bench hash_to_curve + +use std::iter; + +use criterion::{black_box, criterion_group, criterion_main, Criterion, Throughput}; +use halo2curves::{bn256::G1, CurveExt}; +use rand::SeedableRng; +use rand_core::RngCore; +use rand_xorshift::XorShiftRng; + +const SEED: [u8; 16] = [ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, +]; +fn hash_to_curve(c: &mut Criterion, name: &'static str) { + { + let hasher = G::hash_to_curve("test"); + let mut rng = XorShiftRng::from_seed(SEED); + let message = iter::repeat_with(|| rng.next_u32().to_be_bytes()) + .take(1024) + .flatten() + .collect::>(); + + let mut group = c.benchmark_group(format!("{} hash-to-curve", name)); + + group.significance_level(0.1).sample_size(100); + group.throughput(Throughput::Elements(1)); + + group.bench_function(&format!("Hash to {name}"), move |b| { + b.iter(|| hasher(black_box(&message))) + }); + group.finish(); + } +} + +fn hash_to_bn256(c: &mut Criterion) { + hash_to_curve::(c, "BN256"); +} + +criterion_group!(benches, hash_to_bn256); +criterion_main!(benches); diff --git a/forks/halo2curves/benches/msm.rs b/forks/halo2curves/benches/msm.rs new file mode 100644 index 00000000..8a98bcb5 --- /dev/null +++ b/forks/halo2curves/benches/msm.rs @@ -0,0 +1,165 @@ +//! This benchmarks Multi Scalar Multiplication (MSM). +//! It measures `G1` from the BN256 curve. +//! +//! To run this benchmark: +//! +//! cargo bench --bench msm +//! +//! Caveat: The multicore benchmark assumes: +//! 1. a multi-core system +//! 2. that the `multicore` feature is enabled. It is by default. + +#[macro_use] +extern crate criterion; + +use std::time::SystemTime; + +use criterion::{BenchmarkId, Criterion}; +use ff::{Field, PrimeField}; +use group::prime::PrimeCurveAffine; +use halo2curves::{ + bn256::{Fr as Scalar, G1Affine as Point}, + msm::{msm_best, msm_serial}, +}; +use rand_core::{RngCore, SeedableRng}; +use rand_xorshift::XorShiftRng; +use rayon::{ + current_thread_index, + prelude::{IntoParallelIterator, ParallelIterator}, +}; + +const SAMPLE_SIZE: usize = 10; +const SINGLECORE_RANGE: [u8; 6] = [3, 8, 10, 12, 14, 16]; +const MULTICORE_RANGE: [u8; 9] = [3, 8, 10, 12, 14, 16, 18, 20, 22]; +const SEED: [u8; 16] = [ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, +]; + +fn generate_curvepoints(k: u8) -> Vec { + let n: u64 = { + assert!(k < 64); + 1 << k + }; + + println!("Generating 2^{k} = {n} curve points..",); + let timer = SystemTime::now(); + let bases = (0..n) + .into_par_iter() + .map_init( + || { + let mut thread_seed = SEED; + let uniq = current_thread_index().unwrap().to_ne_bytes(); + assert!(std::mem::size_of::() == 8); + for i in 0..uniq.len() { + thread_seed[i] += uniq[i]; + thread_seed[i + 8] += uniq[i]; + } + XorShiftRng::from_seed(thread_seed) + }, + |rng, _| Point::random(rng), + ) + .collect(); + let end = timer.elapsed().unwrap(); + println!( + "Generating 2^{k} = {n} curve points took: {} sec.\n\n", + end.as_secs() + ); + bases +} + +fn generate_coefficients(k: u8, bits: usize) -> Vec { + let n: u64 = { + assert!(k < 64); + 1 << k + }; + let max_val: Option = match bits { + 1 => Some(1), + 8 => Some(0xff), + 16 => Some(0xffff), + 32 => Some(0xffff_ffff), + 64 => Some(0xffff_ffff_ffff_ffff), + 128 => Some(0xffff_ffff_ffff_ffff_ffff_ffff_ffff_ffff), + 256 => None, + _ => panic!("unexpected bit size {}", bits), + }; + + println!("Generating 2^{k} = {n} coefficients..",); + let timer = SystemTime::now(); + let coeffs = (0..n) + .into_par_iter() + .map_init( + || { + let mut thread_seed = SEED; + let uniq = current_thread_index().unwrap().to_ne_bytes(); + assert!(std::mem::size_of::() == 8); + for i in 0..uniq.len() { + thread_seed[i] += uniq[i]; + thread_seed[i + 8] += uniq[i]; + } + XorShiftRng::from_seed(thread_seed) + }, + |rng, _| { + if let Some(max_val) = max_val { + let v_lo = rng.next_u64() as u128; + let v_hi = rng.next_u64() as u128; + let mut v = v_lo + (v_hi << 64); + v &= max_val; // Mask the 128bit value to get a lower number of bits + Scalar::from_u128(v) + } else { + Scalar::random(rng) + } + }, + ) + .collect(); + let end = timer.elapsed().unwrap(); + println!( + "Generating 2^{k} = {n} coefficients took: {} sec.\n\n", + end.as_secs() + ); + coeffs +} + +fn msm(c: &mut Criterion) { + let mut group = c.benchmark_group("msm"); + let max_k = *SINGLECORE_RANGE + .iter() + .chain(MULTICORE_RANGE.iter()) + .max() + .unwrap_or(&16); + let bases = generate_curvepoints(max_k); + let bits = [1, 8, 16, 32, 64, 128, 256]; + let coeffs: Vec<_> = bits + .iter() + .map(|b| generate_coefficients(max_k, *b)) + .collect(); + + for (b_index, b) in bits.iter().enumerate() { + for k in SINGLECORE_RANGE { + let id = format!("{b}b_{k}"); + group + .bench_function(BenchmarkId::new("singlecore", id), |b| { + assert!(k < 64); + let n: usize = 1 << k; + let mut acc = Point::identity().into(); + b.iter(|| msm_serial(&coeffs[b_index][..n], &bases[..n], &mut acc)); + }) + .sample_size(10); + } + for k in MULTICORE_RANGE { + let id = format!("{b}b_{k}"); + group + .bench_function(BenchmarkId::new("multicore", id), |b| { + assert!(k < 64); + let n: usize = 1 << k; + b.iter(|| { + msm_best(&coeffs[b_index][..n], &bases[..n]); + }) + }) + .sample_size(SAMPLE_SIZE); + } + } + group.finish(); +} + +criterion_group!(benches, msm); +criterion_main!(benches); diff --git a/forks/halo2curves/benches/pairing.rs b/forks/halo2curves/benches/pairing.rs new file mode 100644 index 00000000..5480d6a0 --- /dev/null +++ b/forks/halo2curves/benches/pairing.rs @@ -0,0 +1,49 @@ +//! Benchmark pairing. +//! It measures the pairing of the BN256 curve. +//! +//! To run this benchmark: +//! +//! cargo bench --bench pairing + +use criterion::{black_box, criterion_group, criterion_main, Criterion, Throughput}; +use ff::Field; +use group::prime::PrimeCurveAffine; +use halo2curves::bn256::Bn256; +use pairing::Engine; +use rand::SeedableRng; +use rand_xorshift::XorShiftRng; + +const SEED: [u8; 16] = [ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, +]; + +fn bench_pairing(c: &mut Criterion, name: &'static str) { + { + let mut rng = XorShiftRng::from_seed(SEED); + let mut group = c.benchmark_group(format!("{} Pairing", name)); + + group.significance_level(0.1).sample_size(100); + group.throughput(Throughput::Elements(1)); + + let a = E::Fr::random(&mut rng); + let b = E::Fr::random(&mut rng); + + let g1 = E::G1Affine::generator(); + let g1_affine = (g1 * a).into(); + + let g2 = E::G2Affine::generator(); + let g2_affine = (g2 * b).into(); + + group.bench_function(&format!("{} pairing", name), move |b| { + b.iter(|| E::pairing(&black_box(g1_affine), &black_box(g2_affine))) + }); + group.finish(); + } +} + +fn bench_bn256_pairing(c: &mut Criterion) { + bench_pairing::(c, "BN256"); +} + +criterion_group!(benches, bench_bn256_pairing); +criterion_main!(benches); diff --git a/forks/halo2curves/build.rs b/forks/halo2curves/build.rs new file mode 100644 index 00000000..ad1aa174 --- /dev/null +++ b/forks/halo2curves/build.rs @@ -0,0 +1,20 @@ +fn main() { + #[cfg(feature = "asm")] + if std::env::consts::ARCH != "x86_64" { + eprintln!("Currently feature `asm` can only be enabled on x86_64 arch."); + std::process::exit(1); + } + #[cfg(feature = "bn256-table")] + { + if std::path::Path::new("src/bn256/fr/table.rs").exists() { + eprintln!("Pre-computed table for BN256 scalar field exists."); + eprintln!("Skip pre-computation\n"); + } else { + eprintln!("Generating pre-computed table for BN256 scalar field\n"); + std::process::Command::new("python3") + .args(["script/bn256.py"]) + .output() + .expect("requires python 3 to build pre-computed table"); + } + } +} diff --git a/forks/halo2curves/derive/Cargo.toml b/forks/halo2curves/derive/Cargo.toml new file mode 100644 index 00000000..a3585dd5 --- /dev/null +++ b/forks/halo2curves/derive/Cargo.toml @@ -0,0 +1,24 @@ +[package] +edition = "2021" +name = "halo2derive" +version = "0.1.0" +authors = ["Privacy Scaling Explorations team"] +license = "MIT/Apache-2.0" +repository = "https://github.com/privacy-scaling-explorations/halo2curves" +description = "Derive macros for halo2curves fields." +rust-version = "1.63.0" + +[lib] +proc-macro = true + +[dependencies] +num-bigint = "0.4" +num-integer = "0.1" +num-traits = "0.2" +proc-macro2 = "1.0" +quote = "1.0" +syn = {version = "1.0", features = ["full"]} + +[features] +default = [] +asm = [] diff --git a/forks/halo2curves/derive/src/field/arith.rs b/forks/halo2curves/derive/src/field/arith.rs new file mode 100644 index 00000000..7ef11c3f --- /dev/null +++ b/forks/halo2curves/derive/src/field/arith.rs @@ -0,0 +1,341 @@ +use proc_macro2::TokenStream; +use quote::{format_ident as fmtid, quote}; + +fn select(cond: bool, this: TokenStream, other: TokenStream) -> TokenStream { + if cond { + this + } else { + other + } +} + +pub(crate) fn impl_arith(field: &syn::Ident, num_limbs: usize, inv: u64) -> TokenStream { + let impl_add = impl_add(field, num_limbs); + let impl_sub = impl_sub(field, num_limbs); + let impl_neg = impl_neg(field, num_limbs); + let impl_mont = impl_mont(field, num_limbs, inv); + let impl_from_mont = impl_from_mont(field, num_limbs, inv); + let impl_mul = impl_mul(field, num_limbs, false); + let impl_square = impl_square(field, num_limbs); + let wide_num_limbs = num_limbs * 2; + quote::quote! { + impl #field { + #[inline(always)] + pub const fn add(&self, rhs: &Self) -> Self { + #impl_add + } + + #[inline] + pub const fn double(&self) -> Self { + self.add(self) + } + + #[inline(always)] + pub const fn sub(&self, rhs: &Self) -> Self { + #impl_sub + } + + #[inline(always)] + pub const fn neg(&self) -> Self { + #impl_neg + } + + #[inline(always)] + pub const fn mul(&self, rhs: &Self) -> Self{ + #impl_mul + } + + #[inline(always)] + pub const fn square(&self) -> Self{ + #impl_square + } + + #[inline(always)] + pub(crate) const fn montgomery_reduce(r: &[u64; #wide_num_limbs]) -> Self { + #impl_mont + } + + #[inline(always)] + pub(crate) const fn from_mont(&self) -> [u64; #num_limbs] { + #impl_from_mont + } + } + } +} + +pub(crate) fn impl_arith_always_const( + field: &syn::Ident, + num_limbs: usize, + inv: u64, +) -> TokenStream { + let impl_sub = impl_sub(field, num_limbs); + let impl_mont = impl_mont(field, num_limbs, inv); + let impl_mul = impl_mul(field, num_limbs, true); + let wide_num_limbs = num_limbs * 2; + quote::quote! { + impl #field { + + #[inline(always)] + pub(crate) const fn sub_const(&self, rhs: &Self) -> Self { + #impl_sub + } + + + #[inline(always)] + pub(crate) const fn mul_const(&self, rhs: &Self) -> Self{ + #impl_mul + } + + #[inline(always)] + pub(crate) const fn montgomery_reduce_const(r: &[u64; #wide_num_limbs]) -> Self { + #impl_mont + } + } + } +} + +fn impl_mul(field: &syn::Ident, num_limbs: usize, constant: bool) -> TokenStream { + let mut gen = quote! { use crate::arithmetic::{adc, sbb, mac}; }; + for i in 0..num_limbs { + for j in 0..num_limbs { + let r_out = fmtid!("r_{}", i + j); + let r_next = fmtid!("r_{}", i + j + 1); + let r_in = select(i == 0, quote! {0}, quote! {#r_out}); + let carry_in = select(j == 0, quote! {0}, quote! {carry}); + let carry_out = select(j == num_limbs - 1, quote! {#r_next}, quote! {carry}); + gen.extend( + quote! { let (#r_out, #carry_out) = mac(#r_in, self.0[#i], rhs.0[#j], #carry_in); }, + ); + } + } + + let r: Vec<_> = (0..num_limbs * 2).map(|i| fmtid!("r_{}", i)).collect(); + let mont_red = if constant { + quote! { #field::montgomery_reduce_const(&[#(#r),*]) } + } else { + quote! { #field::montgomery_reduce(&[#(#r),*]) } + }; + quote! { + #gen + #mont_red + } +} + +fn impl_square(field: &syn::Ident, num_limbs: usize) -> TokenStream { + let mut gen = quote! { use crate::arithmetic::{adc, sbb, mac}; }; + for i in 0..num_limbs - 1 { + let start_index = i * 2 + 1; + for j in 0..num_limbs - i - 1 { + let r_out = fmtid!("r_{}", start_index + j); + let r_in = select(i == 0, quote! {0}, quote! {#r_out}); + let r_next = fmtid!("r_{}", start_index + j + 1); + let carry_in = select(j == 0, quote! {0}, quote! {carry}); + let carry_out = select(j == num_limbs - i - 2, quote! {#r_next}, quote! {carry}); + let j = i + j + 1; + gen.extend(quote! { let (#r_out, #carry_out) = mac(#r_in, self.0[#i], self.0[#j], #carry_in); }); + } + } + + for i in (1..num_limbs * 2).rev() { + let (r_cur, r_next) = (fmtid!("r_{}", i), fmtid!("r_{}", i - 1)); + if i == num_limbs * 2 - 1 { + gen.extend(quote! { let #r_cur = #r_next >> 63; }); + } else if i == 1 { + gen.extend(quote! { let #r_cur = (#r_cur << 1); }); + } else { + gen.extend(quote! { let #r_cur = (#r_cur << 1) | (#r_next >> 63); }); + } + } + + for i in 0..num_limbs { + let index = i * 2; + let r_cur = fmtid!("r_{}", index); + let r_next = fmtid!("r_{}", index + 1); + let r_cur_in = select(i == 0, quote! {0}, quote! {#r_cur}); + let carry_in = select(i == 0, quote! {0}, quote! {carry}); + let carry_out = select(i == num_limbs - 1, quote! {_}, quote! {carry}); + gen.extend(quote! { + let (#r_cur, carry) = mac(#r_cur_in, self.0[#i], self.0[#i], #carry_in); + let (#r_next, #carry_out) = adc(0, #r_next, carry); + }); + } + + let r: Vec<_> = (0..num_limbs * 2).map(|i| fmtid!("r_{}", i)).collect(); + quote! { + #gen + #field::montgomery_reduce(&[#(#r),*]) + } +} + +fn impl_add(field: &syn::Ident, num_limbs: usize) -> TokenStream { + let mut gen = quote! { use crate::arithmetic::{adc, sbb}; }; + + (0..num_limbs).for_each(|i| { + let carry = select(i == 0, quote! {0}, quote! {carry}); + let d_i = fmtid!("d_{}", i); + gen.extend(quote! { let ( #d_i, carry) = adc(self.0[#i], rhs.0[#i], #carry); }); + }); + + // Attempt to subtract the modulus, to ensure the value + // is smaller than the modulus. + (0..num_limbs).for_each(|i| { + let borrow = select(i == 0, quote! {0}, quote! {borrow}); + let d_i = fmtid!("d_{}", i); + gen.extend(quote! { let (#d_i, borrow) = sbb(#d_i, Self::MODULUS_LIMBS[#i], #borrow); }); + }); + gen.extend(quote! {let (_, borrow) = sbb(carry, 0, borrow);}); + + (0..num_limbs).for_each(|i| { + let carry_in = select(i == 0, quote! {0}, quote! {carry}); + let carry_out = select(i == num_limbs - 1, quote! {_}, quote! {carry}); + let d_i = fmtid!("d_{}", i); + gen.extend( + quote! { let (#d_i, #carry_out) = adc(#d_i, Self::MODULUS_LIMBS[#i] & borrow, #carry_in); }, + ); + }); + + let ret: Vec<_> = (0..num_limbs).map(|i| fmtid!("d_{}", i)).collect(); + + quote! { + #gen + #field([#(#ret),*]) + } +} + +fn impl_sub(field: &syn::Ident, num_limbs: usize) -> TokenStream { + let mut gen = quote! { use crate::arithmetic::{adc, sbb}; }; + + (0..num_limbs).for_each(|i| { + let borrow = select(i == 0, quote! {0}, quote! {borrow}); + let d_i = fmtid!("d_{}", i); + gen.extend(quote! { let (#d_i, borrow) = sbb(self.0[#i], rhs.0[#i], #borrow); }); + }); + + (0..num_limbs).for_each(|i| { + let carry_in = select(i == 0, quote! {0}, quote! {carry}); + let carry_out = select(i == num_limbs - 1, quote! {_}, quote! {carry}); + let d_i = fmtid!("d_{}", i); + gen.extend( + quote! { let (#d_i, #carry_out) = adc(#d_i, Self::MODULUS_LIMBS[#i] & borrow, #carry_in); }, + ); + }); + + let ret: Vec<_> = (0..num_limbs).map(|i| fmtid!("d_{}", i)).collect(); + + quote! { + #gen + #field([#(#ret),*]) + } +} + +fn impl_neg(field: &syn::Ident, num_limbs: usize) -> TokenStream { + let mut gen = quote! { use crate::arithmetic::{adc, sbb}; }; + + (0..num_limbs).for_each(|i| { + let borrow_in = select(i == 0, quote! {0}, quote! {borrow}); + let borrow_out = select(i == num_limbs - 1, quote! {_}, quote! {borrow}); + let d_i = fmtid!("d_{}", i); + gen.extend(quote! { let (#d_i, #borrow_out) = sbb(Self::MODULUS_LIMBS[#i], self.0[#i], #borrow_in); }) + }); + + let mask_limbs: Vec<_> = (0..num_limbs) + .map(|i| quote::quote! { self.0[#i] }) + .collect(); + gen.extend(quote! { let mask = (((#(#mask_limbs)|*) == 0) as u64).wrapping_sub(1); }); + + let ret: Vec<_> = (0..num_limbs) + .map(|i| { + let d_i = fmtid!("d_{}", i); + quote! { #d_i & mask } + }) + .collect(); + + quote! { + #gen + #field([#(#ret),*]) + } +} + +fn impl_mont(field: &syn::Ident, num_limbs: usize, inv: u64) -> TokenStream { + let mut gen = quote! { use crate::arithmetic::{adc, sbb, mac}; }; + + for i in 0..num_limbs { + if i == 0 { + gen.extend(quote! { let k = r[0].wrapping_mul(#inv); }); + + for j in 0..num_limbs { + let r_out = fmtid!("r_{}", j); + let r_out = select(j == 0, quote! {_}, quote! {#r_out}); + let carry_in = select(j == 0, quote! {0}, quote! {carry}); + gen.extend(quote! { let (#r_out, carry) = mac(r[#j], k, Self::MODULUS_LIMBS[#j], #carry_in); }); + } + let r_out = fmtid!("r_{}", num_limbs); + gen.extend(quote! { let (#r_out, carry2) = adc(r[#num_limbs], 0, carry); }); + } else { + let r_i = fmtid!("r_{}", i); + gen.extend(quote! { let k = #r_i.wrapping_mul(#inv); }); + + for j in 0..num_limbs { + let r_in = fmtid!("r_{}", j + i); + let r_out = select(j == 0, quote! {_}, quote! {#r_in}); + let carry_in = select(j == 0, quote! {0}, quote! {carry}); + gen.extend(quote! { let (#r_out, carry) = mac(#r_in, k, Self::MODULUS_LIMBS[#j], #carry_in); }); + } + let idx = num_limbs + i; + let r_out = fmtid!("r_{}", idx); + gen.extend(quote! { let (#r_out, carry2) = adc(r[#idx], carry2, carry); }); + } + } + + (0..num_limbs).for_each(|i| { + let borrow = select(i == 0, quote! {0}, quote! {borrow}); + let d_i = fmtid!("d_{}", i); + let r_in = fmtid!("r_{}", num_limbs + i); + gen.extend(quote! { let (#d_i, borrow) = sbb(#r_in, Self::MODULUS_LIMBS[#i], #borrow); }); + }); + + gen.extend(quote! {let (_, borrow) = sbb(carry2, 0, borrow);}); + + (0..num_limbs).for_each(|i| { + let carry_in = select(i == 0, quote! {0}, quote! {carry}); + let carry_out = select(i == num_limbs - 1, quote! {_}, quote! {carry}); + let d_i = fmtid!("d_{}", i); + gen.extend( + quote! { let (#d_i, #carry_out) = adc(#d_i, Self::MODULUS_LIMBS[#i] & borrow, #carry_in); }, + ); + }); + let ret: Vec<_> = (0..num_limbs).map(|i| fmtid!("d_{}", i)).collect(); + + quote! { + #gen + #field([#(#ret),*]) + } +} + +fn impl_from_mont(field: &syn::Ident, num_limbs: usize, inv: u64) -> TokenStream { + let mut gen = quote! { use crate::arithmetic::{adc, sbb, mac}; }; + + for i in 0..num_limbs { + let r_i = fmtid!("r_{}", i); + if i == 0 { + gen.extend(quote! { let k = self.0[0].wrapping_mul(#inv); }); + } else { + gen.extend(quote! { let k = #r_i.wrapping_mul(#inv); }); + } + + for j in 0..num_limbs { + let r_ij = fmtid!("r_{}", (j + i) % num_limbs); + let r_out = select(j == 0, quote! {_}, quote! {#r_ij}); + let r_ij = select(i == 0, quote! {self.0[#j]}, quote! {#r_ij}); + let carry_in = select(j == 0, quote! {0}, quote! {#r_i}); + gen.extend( + quote! { let (#r_out, #r_i) = mac(#r_ij, k, Self::MODULUS_LIMBS[#j], #carry_in); }, + ); + } + } + let ret: Vec<_> = (0..num_limbs).map(|i| fmtid!("r_{}", i)).collect(); + quote! { + #gen + #field([#(#ret),*]).sub(&#field(Self::MODULUS_LIMBS)).0 + } +} diff --git a/forks/halo2curves/derive/src/field/asm/limb4.rs b/forks/halo2curves/derive/src/field/asm/limb4.rs new file mode 100644 index 00000000..8fc071e4 --- /dev/null +++ b/forks/halo2curves/derive/src/field/asm/limb4.rs @@ -0,0 +1,609 @@ +use proc_macro2::TokenStream; + +pub(crate) fn impl_arith(field: &syn::Ident, inv: u64) -> TokenStream { + quote::quote! { + use std::arch::asm; + impl #field { + /// Doubles this field element. + #[inline] + pub fn double(&self) -> #field { + let mut r0: u64; + let mut r1: u64; + let mut r2: u64; + let mut r3: u64; + unsafe { + asm!( + // load a array to former registers + "mov r8, qword ptr [{a_ptr} + 0]", + "mov r9, qword ptr [{a_ptr} + 8]", + "mov r10, qword ptr [{a_ptr} + 16]", + "mov r11, qword ptr [{a_ptr} + 24]", + + // // add a array and b array with carry + "add r8, r8", + "adc r9, r9", + "adc r10, r10", + "adc r11, r11", + + // copy result array to latter registers + "mov r12, r8", + "mov r13, r9", + "mov r14, r10", + "mov r15, r11", + + // mod reduction + "sub r12, qword ptr [{m_ptr} + 0]", + "sbb r13, qword ptr [{m_ptr} + 8]", + "sbb r14, qword ptr [{m_ptr} + 16]", + "sbb r15, qword ptr [{m_ptr} + 24]", + + // if carry copy former registers to out areas + "cmovc r12, r8", + "cmovc r13, r9", + "cmovc r14, r10", + "cmovc r15, r11", + + m_ptr = in(reg) #field::MODULUS_LIMBS.as_ptr(), + a_ptr = in(reg) self.0.as_ptr(), + out("r8") _, + out("r9") _, + out("r10") _, + out("r11") _, + out("r12") r0, + out("r13") r1, + out("r14") r2, + out("r15") r3, + options(pure, readonly, nostack) + ); + } + #field([r0, r1, r2, r3]) + } + + /// Squares this element. + #[inline] + pub fn square(&self) -> #field { + self.mul(self) + } + + #[inline(always)] + pub(crate) fn from_mont(&self) -> [u64; Self::NUM_LIMBS] { + let mut r0: u64; + let mut r1: u64; + let mut r2: u64; + let mut r3: u64; + + unsafe { + asm!( + "mov r8, qword ptr [{a_ptr} + 0]", + "mov r9, qword ptr [{a_ptr} + 8]", + "mov r10, qword ptr [{a_ptr} + 16]", + "mov r11, qword ptr [{a_ptr} + 24]", + "mov r15, {inv}", + "xor r12, r12", + + // i0 + "mov rdx, r8", + "mulx rcx, rdx, r15", + + // j0 + "mulx rcx, rax, qword ptr [{m_ptr} + 0]", + "adox r8, rax", + "adcx r9, rcx", + // j1 + "mulx rcx, rax, qword ptr [{m_ptr} + 8]", + "adox r9, rax", + "adcx r10, rcx", + // j2 + "mulx rcx, rax, qword ptr [{m_ptr} + 16]", + "adox r10, rax", + "adcx r11, rcx", + // j3 + "mulx rcx, rax, qword ptr [{m_ptr} + 24]", + "adox r11, rax", + "adcx r8, rcx", + "adox r8, r12", + + // i1 + "mov rdx, r9", + "mulx rcx, rdx, r15", + + // j0 + "mulx rcx, rax, qword ptr [{m_ptr} + 0]", + "adox r9, rax", + "adcx r10, rcx", + + // j1 + "mulx rcx, rax, qword ptr [{m_ptr} + 8]", + "adox r10, rax", + "adcx r11, rcx", + // j2 + "mulx rcx, rax, qword ptr [{m_ptr} + 16]", + "adox r11, rax", + "adcx r8, rcx", + // j3 + "mulx rcx, rax, qword ptr [{m_ptr} + 24]", + "adox r8, rax", + "adcx r9, rcx", + "adox r9, r12", + + // i2 + "mov rdx, r10", + "mulx rcx, rdx, r15", + + // j0 + "mulx rcx, rax, qword ptr [{m_ptr} + 0]", + "adox r10, rax", + "adcx r11, rcx", + + // j1 + "mulx rcx, rax, qword ptr [{m_ptr} + 8]", + "adox r11, rax", + "adcx r8, rcx", + + // j2 + "mulx rcx, rax, qword ptr [{m_ptr} + 16]", + "adox r8, rax", + "adcx r9, rcx", + + // j3 + "mulx rcx, rax, qword ptr [{m_ptr} + 24]", + "adox r9, rax", + "adcx r10, rcx", + "adox r10, r12", + + // i3 + "mov rdx, r11", + "mulx rcx, rdx, r15", + // j0 + "mulx rcx, rax, qword ptr [{m_ptr} + 0]", + "adox r11, rax", + "adcx r8, rcx", + // j1 + "mulx rcx, rax, qword ptr [{m_ptr} + 8]", + "adox r8, rax", + "adcx r9, rcx", + // j2 + "mulx rcx, rax, qword ptr [{m_ptr} + 16]", + "adox r9, rax", + "adcx r10, rcx", + // j3 + "mulx rcx, rax, qword ptr [{m_ptr} + 24]", + "adox r10, rax", + "adcx r11, rcx", + "adox r11, r12", + + // modular reduction is not required since: + // high(inv * p3) + 2 < p3 + + a_ptr = in(reg) self.0.as_ptr(), + m_ptr = in(reg) #field::MODULUS_LIMBS.as_ptr(), + inv = in(reg) #inv, + + out("rax") _, + out("rcx") _, + out("rdx") _, + out("r8") r0, + out("r9") r1, + out("r10") r2, + out("r11") r3, + out("r12") _, + out("r13") _, + out("r14") _, + out("r15") _, + options(pure, readonly, nostack) + ) + } + [r0, r1, r2, r3] + } + + /// Multiplies `rhs` by `self`, returning the result. + #[inline] + pub fn mul(&self, rhs: &Self) -> #field { + let mut r0: u64; + let mut r1: u64; + let mut r2: u64; + let mut r3: u64; + unsafe { + asm!( + + // Coarsely Integrated Operand Scanning: + // - Analyzing and Comparing Montgomery Multiplication Algorithms + // Cetin Kaya Koc and Tolga Acar and Burton S. Kaliski Jr. + // http://pdfs.semanticscholar.org/5e39/41ff482ec3ee41dc53c3298f0be085c69483.pdf + // + // No-carry optimization + // - https://hackmd.io/@gnark/modular_multiplication + // + // Code generator + // - https://github.com/mratsim/constantine/blob/151f284/constantine/math/arithmetic/assembly/limbs_asm_mul_mont_x86_adx_bmi2.nim#L231-L269 + // + // Assembly generated + // - https://github.com/ethereum/evmone/blob/d006d81/lib/evmmax/mulMont256_spare_bits_asm_adx.S + + // Algorithm + // ----------------------------------------- + // for i=0 to N-1 + // for j=0 to N-1 + // (A,t[j]) := t[j] + a[j]*b[i] + A + // m := t[0]*m0ninv mod W + // C,_ := t[0] + m*M[0] + // for j=1 to N-1 + // (C,t[j-1]) := t[j] + m*M[j] + C + // t[N-1] = C + A + + // Outer loop i = 0 + // Multiplication + "mov rdx, qword ptr [{b_ptr} + 0]", + "mulx r13, r11, qword ptr [{a_ptr} + 0]", + "mulx rax, r15, qword ptr [{a_ptr} + 8]", + "add r13, r15", + "mulx r14, r15, qword ptr [{a_ptr} + 16]", + "adc rax, r15", + // Multiplication. last limb + "mulx r12, r15, qword ptr [{a_ptr} + 24]", + "adc r14, r15", + "adc r12, 0", // accumulate last carries in hi word + + // Reduction + // m = t[0] * m0ninv mod 2^w + "mov rdx, r11", + "imul rdx, {inv}", + "xor r15, r15", + // C,_ := t[0] + m*M[0] + "mulx r15, r10, qword ptr [{m_ptr} + 0]", + "adcx r10, r11", + "mov r11, r15", + "mov r10, 0", + // for j=1 to N-1 + // (C, t[j-1]) := t[j] + m*M[j] + C + "adcx r11, r13", + "mulx r13, r15, qword ptr [{m_ptr} + 8]", + "adox r11, r15", + "adcx r13, rax", + "mulx rax, r15, qword ptr [{m_ptr} + 16]", + "adox r13, r15", + "adcx rax, r14", + "mulx r14, r15, qword ptr [{m_ptr} + 24]", + "adox rax, r15", + // Reduction carry + "adcx r10, r12", + "adox r14, r10", + + // Outer loop i = 1, j in [0, 4) + "mov rdx, qword ptr [{b_ptr} + 8]", + "xor r12, r12", + "mulx r12, r15, qword ptr [{a_ptr} + 0]", + "adox r11, r15", + "adcx r13, r12", + "mulx r12, r15, qword ptr [{a_ptr} + 8]", + "adox r13, r15", + "adcx rax, r12", + "mulx r12, r15, qword ptr [{a_ptr} + 16]", + "adox rax, r15", + "adcx r14, r12", + // Multiplication, last limb + "mulx r12, r15, qword ptr [{a_ptr} + 24]", + "adox r14, r15", + "mov rdx, 0", // accumulate last carries in hi word + "adcx r12, rdx", + "adox r12, rdx", + + // Reduction + // m = t[0] * m0ninv mod 2^w + "mov rdx, r11", + "imul rdx, {inv}", + "xor r15, r15", + // C,_ := t[0] + m*M[0] + "mulx r15, r10, qword ptr [{m_ptr} + 0]", + "adcx r10, r11", + "mov r11, r15", + "mov r10, 0", + // for j=1 to N-1 + // (C, t[j-1]) := t[j] + m*M[j] + C + "adcx r11, r13", + "mulx r13, r15, qword ptr [{m_ptr} + 8]", + "adox r11, r15", + "adcx r13, rax", + "mulx rax, r15, qword ptr [{m_ptr} + 16]", + "adox r13, r15", + "adcx rax, r14", + "mulx r14, r15, qword ptr [{m_ptr} + 24]", + "adox rax, r15", + // Reduction carry + "adcx r10, r12", + "adox r14, r10", + + // Outer loop i = 2, j in [0, 4) + "mov rdx, qword ptr [{b_ptr} + 16]", + "xor r12, r12", + "mulx r12, r15, qword ptr [{a_ptr} + 0]", + "adox r11, r15", + "adcx r13, r12", + "mulx r12, r15, qword ptr [{a_ptr} + 8]", + "adox r13, r15", + "adcx rax, r12", + "mulx r12, r15, qword ptr [{a_ptr} + 16]", + "adox rax, r15", + "adcx r14, r12", + // Multiplication, last limb + "mulx r12, r15, qword ptr [{a_ptr} + 24]", + "adox r14, r15", + "mov rdx, 0", // accumulate last carries in hi word + "adcx r12, rdx", + "adox r12, rdx", + + // Reduction + // m = t[0] * m0ninv mod 2^w + "mov rdx, r11", + "imul rdx, {inv}", + "xor r15, r15", + // C,_ := t[0] + m*M[0] + "mulx r15, r10, qword ptr [{m_ptr} + 0]", + "adcx r10, r11", + "mov r11, r15", + "mov r10, 0", + // for j=1 to N-1 + // (C, t[j-1]) := t[j] + m*M[j] + C + "adcx r11, r13", + "mulx r13, r15, qword ptr [{m_ptr} + 8]", + "adox r11, r15", + "adcx r13, rax", + "mulx rax, r15, qword ptr [{m_ptr} + 16]", + "adox r13, r15", + "adcx rax, r14", + "mulx r14, r15, qword ptr [{m_ptr} + 24]", + "adox rax, r15", + // Reduction carry + "adcx r10, r12", + "adox r14, r10", + + // Outer loop i = 3, j in [0, 4) + "mov rdx, qword ptr [{b_ptr} + 24]", + "xor r12, r12", + "mulx r12, r15, qword ptr [{a_ptr} + 0]", + "adox r11, r15", + "adcx r13, r12", + "mulx r12, r15, qword ptr [{a_ptr} + 8]", + "adox r13, r15", + "adcx rax, r12", + "mulx r12, r15, qword ptr [{a_ptr} + 16]", + "adox rax, r15", + "adcx r14, r12", + // Multiplication, last limb + "mulx r12, r15, qword ptr [{a_ptr} + 24]", + "adox r14, r15", + "mov rdx, 0", // accumulate last carries in hi word + "adcx r12, rdx", + "adox r12, rdx", + + // Reduction + // m = t[0] * m0ninv mod 2^w + "mov rdx, r11", + "imul rdx, {inv}", + "xor r15, r15", + // C,_ := t[0] + m*M[0] + "mulx r15, r10, qword ptr [{m_ptr} + 0]", + "adcx r10, r11", + "mov r11, r15", + "mov r10, 0", + // for j=1 to N-1 + // (C, t[j-1]) := t[j] + m*M[j] + C + "adcx r11, r13", + "mulx r13, r15, qword ptr [{m_ptr} + 8]", + "adox r11, r15", + "adcx r13, rax", + "mulx rax, r15, qword ptr [{m_ptr} + 16]", + "adox r13, r15", + "adcx rax, r14", + "mulx r14, r15, qword ptr [{m_ptr} + 24]", + "adox rax, r15", + // Reduction carry + "adcx r10, r12", + "adox r14, r10", + + // Final subtraction + "mov r12, r11", + "sub r12, qword ptr [{m_ptr} + 0]", + "mov r10, r13", + "sbb r10, qword ptr [{m_ptr} + 8]", + "mov rdx, rax", + "sbb rdx, qword ptr [{m_ptr} + 16]", + "mov r15, r14", + "sbb r15, qword ptr [{m_ptr} + 24]", + + "cmovnc r11, r12", + "cmovnc r13, r10", + "cmovnc rax, rdx", + "cmovnc r14, r15", + + m_ptr = in(reg) #field::MODULUS_LIMBS.as_ptr(), + a_ptr = in(reg) self.0.as_ptr(), + b_ptr = in(reg) rhs.0.as_ptr(), + inv = in(reg) #inv, + out("rax") r2, + out("rdx") _, + out("r10") _, + out("r11") r0, + out("r12") _, + out("r13") r1, + out("r14") r3, + out("r15") _, + options(pure, readonly, nostack) + ) + } + + #field([r0, r1, r2, r3]) + } + + /// Subtracts `rhs` from `self`, returning the result. + #[inline] + pub fn sub(&self, rhs: &Self) -> #field { + let mut r0: u64; + let mut r1: u64; + let mut r2: u64; + let mut r3: u64; + unsafe { + asm!( + // init modulus area + "mov r12, qword ptr [{m_ptr} + 0]", + "mov r13, qword ptr [{m_ptr} + 8]", + "mov r14, qword ptr [{m_ptr} + 16]", + "mov r15, qword ptr [{m_ptr} + 24]", + + // load a array to former registers + "mov r8, qword ptr [{a_ptr} + 0]", + "mov r9, qword ptr [{a_ptr} + 8]", + "mov r10, qword ptr [{a_ptr} + 16]", + "mov r11, qword ptr [{a_ptr} + 24]", + + // sub a array and b array with borrow + "sub r8, qword ptr [{b_ptr} + 0]", + "sbb r9, qword ptr [{b_ptr} + 8]", + "sbb r10, qword ptr [{b_ptr} + 16]", + "sbb r11, qword ptr [{b_ptr} + 24]", + + // Mask: rax contains 0xFFFF if < m or 0x0000 otherwise + "sbb rax, rax", + + // Zero-out the modulus if a-b < m or leave as-is otherwise + "and r12, rax", + "and r13, rax", + "and r14, rax", + "and r15, rax", + + // Add zero if a-b < m or a-b+m otherwise + "add r12, r8", + "adc r13, r9", + "adc r14, r10", + "adc r15, r11", + + m_ptr = in(reg) #field::MODULUS_LIMBS.as_ptr(), + a_ptr = in(reg) self.0.as_ptr(), + b_ptr = in(reg) rhs.0.as_ptr(), + out("rax") _, + out("r8") _, + out("r9") _, + out("r10") _, + out("r11") _, + out("r12") r0, + out("r13") r1, + out("r14") r2, + out("r15") r3, + options(pure, readonly, nostack) + ); + } + #field([r0, r1, r2, r3]) + } + + /// Adds `rhs` to `self`, returning the result. + #[inline] + pub fn add(&self, rhs: &Self) -> #field { + let mut r0: u64; + let mut r1: u64; + let mut r2: u64; + let mut r3: u64; + unsafe { + asm!( + // load a array to former registers + "mov r8, qword ptr [{a_ptr} + 0]", + "mov r9, qword ptr [{a_ptr} + 8]", + "mov r10, qword ptr [{a_ptr} + 16]", + "mov r11, qword ptr [{a_ptr} + 24]", + + // add a array and b array with carry + "add r8, qword ptr [{b_ptr} + 0]", + "adc r9, qword ptr [{b_ptr} + 8]", + "adc r10, qword ptr [{b_ptr} + 16]", + "adc r11, qword ptr [{b_ptr} + 24]", + + // copy result array to latter registers + "mov r12, r8", + "mov r13, r9", + "mov r14, r10", + "mov r15, r11", + + // mod reduction + "sub r12, qword ptr [{m_ptr} + 0]", + "sbb r13, qword ptr [{m_ptr} + 8]", + "sbb r14, qword ptr [{m_ptr} + 16]", + "sbb r15, qword ptr [{m_ptr} + 24]", + + // if carry copy former registers to out areas + "cmovc r12, r8", + "cmovc r13, r9", + "cmovc r14, r10", + "cmovc r15, r11", + + m_ptr = in(reg) #field::MODULUS_LIMBS.as_ptr(), + a_ptr = in(reg) self.0.as_ptr(), + b_ptr = in(reg) rhs.0.as_ptr(), + out("r8") _, + out("r9") _, + out("r10") _, + out("r11") _, + out("r12") r0, + out("r13") r1, + out("r14") r2, + out("r15") r3, + options(pure, readonly, nostack) + ); + } + #field([r0, r1, r2, r3]) + } + + /// Negates `self`. + #[inline] + pub fn neg(&self) -> #field { + let mut r0: u64; + let mut r1: u64; + let mut r2: u64; + let mut r3: u64; + unsafe { + asm!( + // load a array to former registers + "mov r8, qword ptr [{m_ptr} + 0]", + "mov r9, qword ptr [{m_ptr} + 8]", + "mov r10, qword ptr [{m_ptr} + 16]", + "mov r11, qword ptr [{m_ptr} + 24]", + + "sub r8, qword ptr [{a_ptr} + 0]", + "sbb r9, qword ptr [{a_ptr} + 8]", + "sbb r10, qword ptr [{a_ptr} + 16]", + "sbb r11, qword ptr [{a_ptr} + 24]", + + "mov r12, qword ptr [{a_ptr} + 0]", + "mov r13, qword ptr [{a_ptr} + 8]", + "mov r14, qword ptr [{a_ptr} + 16]", + "mov r15, qword ptr [{a_ptr} + 24]", + + "or r12, r13", + "or r14, r15", + "or r12, r14", + + "mov r13, 0xffffffffffffffff", + "cmp r12, 0x0000000000000000", + "cmove r13, r12", + + "and r8, r13", + "and r9, r13", + "and r10, r13", + "and r11, r13", + + a_ptr = in(reg) self.0.as_ptr(), + m_ptr = in(reg) #field::MODULUS_LIMBS.as_ptr(), + out("r8") r0, + out("r9") r1, + out("r10") r2, + out("r11") r3, + out("r12") _, + out("r13") _, + out("r14") _, + out("r15") _, + options(pure, readonly, nostack) + ) + } + #field([r0, r1, r2, r3]) + } + } + } +} diff --git a/forks/halo2curves/derive/src/field/asm/mod.rs b/forks/halo2curves/derive/src/field/asm/mod.rs new file mode 100644 index 00000000..a2900fbc --- /dev/null +++ b/forks/halo2curves/derive/src/field/asm/mod.rs @@ -0,0 +1 @@ +pub(crate) mod limb4; diff --git a/forks/halo2curves/derive/src/field/mod.rs b/forks/halo2curves/derive/src/field/mod.rs new file mode 100644 index 00000000..6ea6fa16 --- /dev/null +++ b/forks/halo2curves/derive/src/field/mod.rs @@ -0,0 +1,613 @@ +mod arith; +#[cfg(feature = "asm")] +mod asm; + +use num_bigint::BigUint; +use num_integer::Integer; +use num_traits::{Num, One}; +use proc_macro::TokenStream; +use proc_macro2::Span; +use quote::quote; +use syn::Token; + +struct FieldConfig { + identifier: String, + field: syn::Ident, + modulus: BigUint, + mul_gen: BigUint, + zeta: BigUint, + endian: String, + from_uniform: Vec, +} + +impl syn::parse::Parse for FieldConfig { + fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { + let identifier: syn::Ident = input.parse()?; + let identifier = identifier.to_string(); + input.parse::()?; + + let field: syn::Ident = input.parse()?; + input.parse::()?; + + let get_big = |is_key: &str| -> Result { + let key: syn::Ident = input.parse()?; + assert_eq!(key.to_string(), is_key); + input.parse::()?; + let n: syn::LitStr = input.parse()?; + let n = BigUint::from_str_radix(&n.value(), 16) + .map_err(|err| syn::Error::new(Span::call_site(), err.to_string()))?; + input.parse::()?; + Ok(n) + }; + + let get_str = |is_key: &str| -> Result { + let key: syn::Ident = input.parse()?; + assert_eq!(key.to_string(), is_key); + input.parse::()?; + let n: syn::LitStr = input.parse()?; + let n = n.value(); + input.parse::()?; + Ok(n) + }; + + let get_usize_list = |is_key: &str| -> Result, syn::Error> { + let key: syn::Ident = input.parse()?; + assert_eq!(key.to_string(), is_key); + input.parse::()?; + + // chatgpt + let content; + syn::bracketed!(content in input); + let punctuated: syn::punctuated::Punctuated = + content.parse_terminated(syn::LitInt::parse)?; + let values = punctuated + .into_iter() + .map(|lit| lit.base10_parse::()) + .collect::, _>>()?; + input.parse::()?; + Ok(values) + }; + + let modulus = get_big("modulus")?; + let mul_gen = get_big("mul_gen")?; + let zeta = get_big("zeta")?; + let from_uniform = get_usize_list("from_uniform")?; + let endian = get_str("endian")?; + assert!(endian == "little" || endian == "big"); + assert!(input.is_empty()); + + Ok(FieldConfig { + identifier, + field, + modulus, + mul_gen, + zeta, + from_uniform, + endian, + }) + } +} + +pub(crate) fn impl_field(input: TokenStream) -> TokenStream { + use crate::utils::{big_to_token, mod_inv}; + let FieldConfig { + identifier, + field, + modulus, + mul_gen, + zeta, + from_uniform, + endian, + } = syn::parse_macro_input!(input as FieldConfig); + let _ = identifier; + + let num_bits = modulus.bits() as u32; + let limb_size = 64; + let num_limbs = ((num_bits - 1) / limb_size + 1) as usize; + let size = num_limbs * 8; + let modulus_limbs = crate::utils::big_to_limbs(&modulus, num_limbs); + let modulus_str = format!("0x{}", modulus.to_str_radix(16)); + let modulus_limbs_ident = quote! {[#(#modulus_limbs,)*]}; + + let modulus_limbs_32 = crate::utils::big_to_limbs_32(&modulus, num_limbs * 2); + let modulus_limbs_32_ident = quote! {[#(#modulus_limbs_32,)*]}; + + let to_token = |e: &BigUint| big_to_token(e, num_limbs); + let half_modulus = (&modulus - 1usize) >> 1; + let half_modulus = to_token(&half_modulus); + + // binary modulus + let t = BigUint::from(1u64) << (num_limbs * limb_size as usize); + // r1 = mont(1) + let r1: BigUint = &t % &modulus; + let mont = |v: &BigUint| (v * &r1) % &modulus; + // r2 = mont(r) + let r2: BigUint = (&r1 * &r1) % &modulus; + // r3 = mont(r^2) + let r3: BigUint = (&r1 * &r1 * &r1) % &modulus; + + let r1 = to_token(&r1); + let r2 = to_token(&r2); + let r3 = to_token(&r3); + + // inv = -(r^{-1} mod 2^64) mod 2^64 + let mut inv64 = 1u64; + for _ in 0..63 { + inv64 = inv64.wrapping_mul(inv64); + inv64 = inv64.wrapping_mul(modulus_limbs[0]); + } + inv64 = inv64.wrapping_neg(); + + let mut by_inverter_constant: usize = 2; + loop { + let t = BigUint::from(1u64) << (62 * by_inverter_constant - 64); + if t > modulus { + break; + } + by_inverter_constant += 1; + } + + let mut jacobi_constant: usize = 1; + loop { + let t = BigUint::from(1u64) << (64 * jacobi_constant - 31); + if t > modulus { + break; + } + jacobi_constant += 1; + } + + let mut s: u32 = 0; + let mut t = &modulus - BigUint::one(); + while t.is_even() { + t >>= 1; + s += 1; + } + + let two_inv = mod_inv(&BigUint::from(2usize), &modulus); + + let sqrt_impl = { + if &modulus % 16u64 == BigUint::from(1u64) { + let tm1o2 = ((&t - 1usize) * &two_inv) % &modulus; + let tm1o2 = big_to_token(&tm1o2, num_limbs); + quote! { + fn sqrt(&self) -> subtle::CtOption { + ff::helpers::sqrt_tonelli_shanks(self, #tm1o2) + } + } + } else if &modulus % 4u64 == BigUint::from(3u64) { + let exp = (&modulus + 1usize) >> 2; + let exp = big_to_token(&exp, num_limbs); + quote! { + fn sqrt(&self) -> subtle::CtOption { + use subtle::ConstantTimeEq; + let t = self.pow(#exp); + subtle::CtOption::new(t, t.square().ct_eq(self)) + } + } + } else { + panic!("unsupported modulus") + } + }; + + let root_of_unity = mul_gen.modpow(&t, &modulus); + let root_of_unity_inv = mod_inv(&root_of_unity, &modulus); + let delta = mul_gen.modpow(&(BigUint::one() << s), &modulus); + + let root_of_unity = to_token(&mont(&root_of_unity)); + let root_of_unity_inv = to_token(&mont(&root_of_unity_inv)); + let two_inv = to_token(&mont(&two_inv)); + let mul_gen = to_token(&mont(&mul_gen)); + let delta = to_token(&mont(&delta)); + let zeta = to_token(&mont(&zeta)); + + let endian = match endian.as_str() { + "little" => { + quote! { LE } + } + "big" => { + quote! { BE } + } + _ => { + unreachable!() + } + }; + + let impl_field = quote! { + #[derive(Clone, Copy, PartialEq, Eq, Hash, Default)] + pub struct #field(pub(crate) [u64; #num_limbs]); + + impl core::fmt::Debug for #field { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + use ff::PrimeField; + let tmp = self.to_repr(); + write!(f, "0x")?; + for &b in tmp.as_ref().iter().rev() { + write!(f, "{:02x}", b)?; + } + Ok(()) + } + } + + impl ConstantTimeEq for #field { + fn ct_eq(&self, other: &Self) -> Choice { + Choice::from( + self.0 + .iter() + .zip(other.0) + .all(|(a, b)| bool::from(a.ct_eq(&b))) as u8, + ) + } + } + + impl ConditionallySelectable for #field { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + let limbs = (0..#num_limbs) + .map(|i| u64::conditional_select(&a.0[i], &b.0[i], choice)) + .collect::>() + .try_into() + .unwrap(); + #field(limbs) + } + } + + impl core::cmp::PartialOrd for #field { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } + } + + impl core::cmp::Ord for #field { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + use ff::PrimeField; + let left = self.to_repr(); + let right = other.to_repr(); + left.as_ref().iter() + .zip(right.as_ref().iter()) + .rev() + .find_map(|(left_byte, right_byte)| match left_byte.cmp(right_byte) { + core::cmp::Ordering::Equal => None, + res => Some(res), + }) + .unwrap_or(core::cmp::Ordering::Equal) + } + } + + impl> ::core::iter::Sum for #field { + fn sum>(iter: I) -> Self { + iter.fold(Self::zero(), |acc, item| acc + item.borrow()) + } + } + + impl> ::core::iter::Product for #field { + fn product>(iter: I) -> Self { + iter.fold(Self::one(), |acc, item| acc * item.borrow()) + } + } + + impl crate::serde::endian::EndianRepr for #field { + const ENDIAN: crate::serde::endian::Endian = crate::serde::endian::Endian::#endian; + + fn to_bytes(&self) -> Vec { + self.to_bytes().to_vec() + } + + fn from_bytes(bytes: &[u8]) -> subtle::CtOption { + #field::from_bytes(bytes[..#field::SIZE].try_into().unwrap()) + } + } + + impl #field { + pub const SIZE: usize = #num_limbs * 8; + pub const NUM_LIMBS: usize = #num_limbs; + pub(crate) const MODULUS_LIMBS: [u64; Self::NUM_LIMBS] = #modulus_limbs_ident; + pub(crate) const MODULUS_LIMBS_32: [u32; Self::NUM_LIMBS*2] = #modulus_limbs_32_ident; + const R: Self = Self(#r1); + const R2: Self = Self(#r2); + const R3: Self = Self(#r3); + + /// Returns zero, the additive identity. + #[inline(always)] + pub const fn zero() -> #field { + #field([0; Self::NUM_LIMBS]) + } + + /// Returns one, the multiplicative identity. + #[inline(always)] + pub const fn one() -> #field { + Self::R + } + + /// Converts from an integer represented in little endian + /// into its (congruent) `$field` representation. + pub const fn from_raw(val: [u64; Self::NUM_LIMBS]) -> Self { + Self(val).mul_const(&Self::R2) + } + + /// Attempts to convert a <#endian>-endian byte representation of + /// a scalar into a `$field`, failing if the input is not canonical. + pub fn from_bytes(bytes: &[u8; Self::SIZE]) -> subtle::CtOption { + use crate::serde::endian::EndianRepr; + let mut el = #field::default(); + #field::ENDIAN.from_bytes(bytes, &mut el.0); + subtle::CtOption::new(el * Self::R2, subtle::Choice::from(Self::is_less_than_modulus(&el.0) as u8)) + } + + + /// Converts an element of `$field` into a byte representation in + /// <#endian>-endian byte order. + pub fn to_bytes(&self) -> [u8; Self::SIZE] { + use crate::serde::endian::EndianRepr; + let el = self.from_mont(); + let mut res = [0; Self::SIZE]; + #field::ENDIAN.to_bytes(&mut res, &el); + res.into() + } + + + // Returns the Jacobi symbol, where the numerator and denominator + // are the element and the characteristic of the field, respectively. + // The Jacobi symbol is applicable to odd moduli + // while the Legendre symbol is applicable to prime moduli. + // They are equivalent for prime moduli. + #[inline(always)] + fn jacobi(&self) -> i64 { + crate::ff_ext::jacobi::jacobi::<#jacobi_constant>(&self.0, &#modulus_limbs_ident) + } + + + #[inline(always)] + pub(crate) fn is_less_than_modulus(limbs: &[u64; Self::NUM_LIMBS]) -> bool { + let borrow = limbs.iter().enumerate().fold(0, |borrow, (i, limb)| { + crate::arithmetic::sbb(*limb, Self::MODULUS_LIMBS[i], borrow).1 + }); + (borrow as u8) & 1 == 1 + } + + /// Returns whether or not this element is strictly lexicographically + /// larger than its negation. + pub fn lexicographically_largest(&self) -> Choice { + const HALF_MODULUS: [u64; #num_limbs]= #half_modulus; + let tmp = self.from_mont(); + let borrow = tmp + .into_iter() + .zip(HALF_MODULUS.into_iter()) + .fold(0, |borrow, (t, m)| crate::arithmetic::sbb(t, m, borrow).1); + !Choice::from((borrow as u8) & 1) + } + } + + impl ff::Field for #field { + const ZERO: Self = Self::zero(); + const ONE: Self = Self::one(); + + fn random(mut rng: impl RngCore) -> Self { + let mut wide = [0u8; Self::SIZE * 2]; + rng.fill_bytes(&mut wide); + <#field as ff::FromUniformBytes<{ #field::SIZE * 2 }>>::from_uniform_bytes(&wide) + } + + #[inline(always)] + #[must_use] + fn double(&self) -> Self { + self.double() + } + + #[inline(always)] + #[must_use] + fn square(&self) -> Self { + self.square() + } + + // Returns the multiplicative inverse of the element. If it is zero, the method fails. + #[inline(always)] + fn invert(&self) -> CtOption { + const BYINVERTOR: crate::ff_ext::inverse::BYInverter<#by_inverter_constant> = + crate::ff_ext::inverse::BYInverter::<#by_inverter_constant>::new(&#modulus_limbs_ident, &#r2); + + if let Some(inverse) = BYINVERTOR.invert::<{ Self::NUM_LIMBS }>(&self.0) { + subtle::CtOption::new(Self(inverse), subtle::Choice::from(1)) + } else { + subtle::CtOption::new(Self::zero(), subtle::Choice::from(0)) + } + } + + #sqrt_impl + + fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) { + ff::helpers::sqrt_ratio_generic(num, div) + } + } + }; + + let impl_prime_field = quote! { + + // TODO use ::core::borrow::Borrow or AsRef + impl From<#field> for crate::serde::Repr<{ #field::SIZE }> { + fn from(value: #field) -> crate::serde::Repr<{ #field::SIZE }> { + use ff::PrimeField; + value.to_repr() + } + } + + impl<'a> From<&'a #field> for crate::serde::Repr<{ #field::SIZE }> { + fn from(value: &'a #field) -> crate::serde::Repr<{ #field::SIZE }> { + use ff::PrimeField; + value.to_repr() + } + } + + impl ff::PrimeField for #field { + const NUM_BITS: u32 = #num_bits; + const CAPACITY: u32 = #num_bits-1; + const TWO_INV :Self = Self(#two_inv); + const MULTIPLICATIVE_GENERATOR: Self = Self(#mul_gen); + const S: u32 = #s; + const ROOT_OF_UNITY: Self = Self(#root_of_unity); + const ROOT_OF_UNITY_INV: Self = Self(#root_of_unity_inv); + const DELTA: Self = Self(#delta); + const MODULUS: &'static str = #modulus_str; + + type Repr = crate::serde::Repr<{ #field::SIZE }>; + + fn from_u128(v: u128) -> Self { + Self::R2 * Self( + [v as u64, (v >> 64) as u64] + .into_iter() + .chain(std::iter::repeat(0)) + .take(Self::NUM_LIMBS) + .collect::>() + .try_into() + .unwrap(), + ) + } + + fn from_repr(repr: Self::Repr) -> subtle::CtOption { + let mut el = #field::default(); + crate::serde::endian::Endian::LE.from_bytes(repr.as_ref(), &mut el.0); + subtle::CtOption::new(el * Self::R2, subtle::Choice::from(Self::is_less_than_modulus(&el.0) as u8)) + } + + fn to_repr(&self) -> Self::Repr { + use crate::serde::endian::Endian; + let el = self.from_mont(); + let mut res = [0; #size]; + crate::serde::endian::Endian::LE.to_bytes(&mut res, &el); + res.into() + } + + fn is_odd(&self) -> Choice { + Choice::from(self.to_repr()[0] & 1) + } + } + }; + + let impl_serde_object = quote! { + impl crate::serde::SerdeObject for #field { + fn from_raw_bytes_unchecked(bytes: &[u8]) -> Self { + debug_assert_eq!(bytes.len(), #size); + + let inner = (0..#num_limbs) + .map(|off| { + u64::from_le_bytes(bytes[off * 8..(off + 1) * 8].try_into().unwrap()) + }) + .collect::>(); + Self(inner.try_into().unwrap()) + } + + fn from_raw_bytes(bytes: &[u8]) -> Option { + if bytes.len() != #size { + return None; + } + let elt = Self::from_raw_bytes_unchecked(bytes); + Self::is_less_than_modulus(&elt.0).then(|| elt) + } + + fn to_raw_bytes(&self) -> Vec { + let mut res = Vec::with_capacity(#num_limbs * 4); + for limb in self.0.iter() { + res.extend_from_slice(&limb.to_le_bytes()); + } + res + } + + fn read_raw_unchecked(reader: &mut R) -> Self { + let inner = [(); #num_limbs].map(|_| { + let mut buf = [0; 8]; + reader.read_exact(&mut buf).unwrap(); + u64::from_le_bytes(buf) + }); + Self(inner) + } + + fn read_raw(reader: &mut R) -> std::io::Result { + let mut inner = [0u64; #num_limbs]; + for limb in inner.iter_mut() { + let mut buf = [0; 8]; + reader.read_exact(&mut buf)?; + *limb = u64::from_le_bytes(buf); + } + let elt = Self(inner); + Self::is_less_than_modulus(&elt.0) + .then(|| elt) + .ok_or_else(|| { + std::io::Error::new( + std::io::ErrorKind::InvalidData, + "input number is not less than field modulus", + ) + }) + } + fn write_raw(&self, writer: &mut W) -> std::io::Result<()> { + for limb in self.0.iter() { + writer.write_all(&limb.to_le_bytes())?; + } + Ok(()) + } + } + }; + + #[cfg(feature = "asm")] + let impl_arith = { + if num_limbs == 4 && num_bits < 256 { + asm::limb4::impl_arith(&field, inv64) + } else { + arith::impl_arith(&field, num_limbs, inv64) + } + }; + #[cfg(not(feature = "asm"))] + let impl_arith = arith::impl_arith(&field, num_limbs, inv64); + + let impl_arith_always_const = arith::impl_arith_always_const(&field, num_limbs, inv64); + + let impl_from_uniform_bytes = from_uniform + .iter() + .map(|input_size| { + assert!(*input_size >= size); + assert!(*input_size <= size*2); + quote! { + impl ff::FromUniformBytes<#input_size> for #field { + fn from_uniform_bytes(bytes: &[u8; #input_size]) -> Self { + let mut wide = [0u8; Self::SIZE * 2]; + wide[..#input_size].copy_from_slice(bytes); + let (a0, a1) = wide.split_at(Self::SIZE); + + let a0: [u64; Self::NUM_LIMBS] = (0..Self::NUM_LIMBS) + .map(|off| u64::from_le_bytes(a0[off * 8..(off + 1) * 8].try_into().unwrap())) + .collect::>() + .try_into() + .unwrap(); + let a0 = #field(a0); + + let a1: [u64; Self::NUM_LIMBS] = (0..Self::NUM_LIMBS) + .map(|off| u64::from_le_bytes(a1[off * 8..(off + 1) * 8].try_into().unwrap())) + .collect::>() + .try_into() + .unwrap(); + let a1 = #field(a1); + + // enforce non assembly impl since asm is likely to be optimized for sparse fields + a0.mul_const(&Self::R2) + a1.mul_const(&Self::R3) + + } + } + } + }) + .collect::(); + + let impl_zeta = quote! { + impl ff::WithSmallOrderMulGroup<3> for #field { + const ZETA: Self = Self(#zeta); + } + }; + + let output = quote! { + #impl_arith + #impl_arith_always_const + #impl_field + #impl_prime_field + #impl_serde_object + #impl_from_uniform_bytes + #impl_zeta + }; + + output.into() +} diff --git a/forks/halo2curves/derive/src/lib.rs b/forks/halo2curves/derive/src/lib.rs new file mode 100644 index 00000000..4cdee00d --- /dev/null +++ b/forks/halo2curves/derive/src/lib.rs @@ -0,0 +1,7 @@ +mod field; +mod utils; + +#[proc_macro] +pub fn impl_field(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + field::impl_field(input) +} diff --git a/forks/halo2curves/derive/src/utils.rs b/forks/halo2curves/derive/src/utils.rs new file mode 100644 index 00000000..59607652 --- /dev/null +++ b/forks/halo2curves/derive/src/utils.rs @@ -0,0 +1,36 @@ +use core::ops::Shl; + +use num_bigint::BigUint; +use num_traits::{One, ToPrimitive}; + +fn decompose(e: &BigUint, number_of_limbs: usize, limb_size: usize) -> Vec { + let mask = &(BigUint::one().shl(limb_size) - 1usize); + (0usize..) + .step_by(limb_size) + .take(number_of_limbs) + .map(|shift| ((e >> shift) & mask)) + .collect::>() +} + +pub(crate) fn big_to_limbs(e: &BigUint, number_of_limbs: usize) -> Vec { + decompose(e, number_of_limbs, 64) + .iter() + .map(|x| x.to_u64().unwrap()) + .collect() +} + +pub(crate) fn big_to_limbs_32(e: &BigUint, number_of_limbs: usize) -> Vec { + decompose(e, number_of_limbs, 32) + .iter() + .map(|x| x.to_u32().unwrap()) + .collect() +} + +pub(crate) fn big_to_token(e: &BigUint, number_of_limbs: usize) -> proc_macro2::TokenStream { + let limbs = big_to_limbs(e, number_of_limbs); + quote::quote! {[#(#limbs,)*]} +} + +pub(crate) fn mod_inv(e: &BigUint, modulus: &BigUint) -> BigUint { + e.modpow(&(modulus - BigUint::from(2u64)), modulus) +} diff --git a/forks/halo2curves/release.toml b/forks/halo2curves/release.toml new file mode 100644 index 00000000..7fe66589 --- /dev/null +++ b/forks/halo2curves/release.toml @@ -0,0 +1 @@ +consolidate-commits = false diff --git a/forks/halo2curves/rust-toolchain b/forks/halo2curves/rust-toolchain new file mode 100644 index 00000000..dc87e8af --- /dev/null +++ b/forks/halo2curves/rust-toolchain @@ -0,0 +1 @@ +1.74.0 diff --git a/forks/halo2curves/rustfmt.toml b/forks/halo2curves/rustfmt.toml new file mode 100644 index 00000000..8509df1d --- /dev/null +++ b/forks/halo2curves/rustfmt.toml @@ -0,0 +1,10 @@ +# It's the default. But here for visibility. +comment_width = 80 +# Makes code docs & examples easier to write. +format_code_in_doc_comments = true +# Group imports by crate. +imports_granularity = "Crate" +# This is a standard almost +wrap_comments = true +# Rustlang core team is migrating to this +group_imports = "StdExternalCrate" diff --git a/forks/halo2curves/script/bn256.py b/forks/halo2curves/script/bn256.py new file mode 100644 index 00000000..a13dce6d --- /dev/null +++ b/forks/halo2curves/script/bn256.py @@ -0,0 +1,46 @@ +# This file generates the montgomery form integers for x in [0, 2^16) \intersect +# BN::ScalarField + +verbose = False + +modulus = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001 +R = 2**256 % modulus +table_size = 1<<16 + +# @input: field element a +# @output: 4 u64 a0, a1, a2, a3 s.t. +# a = a3 * 2^192 + a2 * 2^128 + a1 * 2^64 + a0 +def decompose_field_element(a): + a0 = a % 2**64 + a = a // 2**64 + a1 = a % 2**64 + a = a // 2**64 + a2 = a % 2**64 + a = a // 2**64 + a3 = a + return [a0, a1, a2, a3] + + +# @input: field element a +# @output: a rust format string that encodes +# 4 u64 a0, a1, a2, a3 s.t. +# a = a3 * 2^192 + a2 * 2^128 + a1 * 2^64 + a0 +def format_field_element(a): + [a0, a1, a2, a3] = decompose_field_element(a); + return "Fr([" + hex(a0) + "," + hex(a1) + "," + hex(a2) + "," + hex(a3) + "]),\n" + + +f = open("src/bn256/fr/table.rs", "w") +f.write("//! auto generated file from scripts/bn256.sage, do not modify\n") +f.write("//! see src/bn256/fr.rs for more details\n") +f.write("use super::Fr;\n") +f.write("pub const FR_TABLE: &[Fr] = &[\n") + +for i in range(table_size): + a = (i * R) % modulus + if verbose: + print (i, a, format_field_element(a)) + f.write(format_field_element(a)) + +f.write("\n];") + diff --git a/forks/halo2curves/script/get_current_version.sh b/forks/halo2curves/script/get_current_version.sh new file mode 100755 index 00000000..4c7c850f --- /dev/null +++ b/forks/halo2curves/script/get_current_version.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +# Script requirements: +# - curl +# - jq + +# Fail on first error, on undefined variables, and on failures in pipelines. +set -euo pipefail + +# Verify if required executables are available +for cmd in curl jq git cargo; do + if ! command -v $cmd &> /dev/null + then + echo "Error: $cmd could not be found." + exit 1 + fi +done + +# Go to the repo root directory. +cd "$(git rev-parse --show-toplevel)" + +# Check 1 argument is given +if [ $# -lt 1 ] +then + echo "Usage : $0 " + exit 1 +fi + +# The first argument should be the name of a crate. +CRATE_NAME="$1" + +cargo metadata --format-version 1 | \ + jq --arg crate_name "$CRATE_NAME" --exit-status -r \ + '.packages[] | select(.name == $crate_name) | .version' diff --git a/forks/halo2curves/src/arithmetic.rs b/forks/halo2curves/src/arithmetic.rs new file mode 100644 index 00000000..48c5a32a --- /dev/null +++ b/forks/halo2curves/src/arithmetic.rs @@ -0,0 +1,73 @@ +//! This module provides common utilities, traits and structures for group and +//! field arithmetic. +//! +//! This module is temporary, and the extension traits defined here are expected +//! to be upstreamed into the `ff` and `group` crates after some refactoring. + +use crate::CurveExt; + +pub(crate) struct EndoParameters { + pub(crate) gamma1: [u64; 4], + pub(crate) gamma2: [u64; 4], + pub(crate) b1: [u64; 4], + pub(crate) b2: [u64; 4], +} + +#[allow(dead_code)] +pub trait CurveEndo: CurveExt { + fn decompose_scalar(e: &Self::ScalarExt) -> (u128, bool, u128, bool); +} + +/// Compute a + b + carry, returning the result and the new carry over. +#[inline(always)] +pub(crate) const fn adc(a: u64, b: u64, carry: u64) -> (u64, u64) { + let ret = (a as u128) + (b as u128) + (carry as u128); + (ret as u64, (ret >> 64) as u64) +} + +/// Compute a - (b + borrow), returning the result and the new borrow. +#[inline(always)] +pub(crate) const fn sbb(a: u64, b: u64, borrow: u64) -> (u64, u64) { + let ret = (a as u128).wrapping_sub((b as u128) + ((borrow >> 63) as u128)); + (ret as u64, (ret >> 64) as u64) +} + +/// Compute a + (b * c) + carry, returning the result and the new carry over. +#[inline(always)] +pub(crate) const fn mac(a: u64, b: u64, c: u64, carry: u64) -> (u64, u64) { + let ret = (a as u128) + ((b as u128) * (c as u128)) + (carry as u128); + (ret as u64, (ret >> 64) as u64) +} + +/// Compute a + (b * c), returning the result and the new carry over. +#[inline(always)] +pub(crate) const fn macx(a: u64, b: u64, c: u64) -> (u64, u64) { + let res = (a as u128) + ((b as u128) * (c as u128)); + (res as u64, (res >> 64) as u64) +} + +/// Compute a * b, returning the result. +#[inline(always)] +pub(crate) fn mul_512(a: [u64; 4], b: [u64; 4]) -> [u64; 8] { + let (r0, carry) = macx(0, a[0], b[0]); + let (r1, carry) = macx(carry, a[0], b[1]); + let (r2, carry) = macx(carry, a[0], b[2]); + let (r3, carry_out) = macx(carry, a[0], b[3]); + + let (r1, carry) = macx(r1, a[1], b[0]); + let (r2, carry) = mac(r2, a[1], b[1], carry); + let (r3, carry) = mac(r3, a[1], b[2], carry); + let (r4, carry_out) = mac(carry_out, a[1], b[3], carry); + + let (r2, carry) = macx(r2, a[2], b[0]); + let (r3, carry) = mac(r3, a[2], b[1], carry); + let (r4, carry) = mac(r4, a[2], b[2], carry); + let (r5, carry_out) = mac(carry_out, a[2], b[3], carry); + + let (r3, carry) = macx(r3, a[3], b[0]); + let (r4, carry) = mac(r4, a[3], b[1], carry); + let (r5, carry) = mac(r5, a[3], b[2], carry); + let (r6, carry_out) = mac(carry_out, a[3], b[3], carry); + + [r0, r1, r2, r3, r4, r5, r6, carry_out] +} diff --git a/forks/halo2curves/src/bls12381/engine.rs b/forks/halo2curves/src/bls12381/engine.rs new file mode 100644 index 00000000..90d9a57d --- /dev/null +++ b/forks/halo2curves/src/bls12381/engine.rs @@ -0,0 +1,135 @@ +use core::{ + borrow::Borrow, + iter::Sum, + ops::{Add, Mul, Neg, Sub}, +}; +use std::ops::MulAssign; + +use ff::{Field, PrimeField}; +use group::{prime::PrimeCurveAffine, Group}; +use pairing::{Engine, MillerLoopResult, MultiMillerLoop, PairingCurveAffine}; +use rand::RngCore; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; + +use super::{fq12::Fq12, fq2::Fq2, Fr, G1Affine, G2Affine, BLS_X, G1, G2}; +use crate::ff_ext::{quadratic::QuadSparseMul, ExtField}; + +crate::impl_gt!(Gt, Fq12, Fr); +crate::impl_miller_loop_components!(Bls12381, G1, G1Affine, G2, G2Affine, Fq12, Gt, Fr); + +impl MillerLoopResult for Fq12 { + type Gt = Gt; + + /// Computes the exponentiation to an Fq element to d, + /// where d = (p¹²-1)/r = (p¹²-1)/Φ₁₂(p) ⋅ Φ₁₂(p)/r = + /// = (p⁶-1)(p²+1)(p⁴ - p² +1)/r + /// we use instead + /// d = s ⋅ (p⁶-1)(p²+1)(p⁴ - p² +1)/r + /// where s is the cofactor 3. https://eprint.iacr.org/2020/875.pdf + fn final_exponentiation(&self) -> Gt { + // Reference: [https://github.com/Consensys/gnark-crypto/blob/97d69dcde082a20847e16ba29bce35e9bfde0261/ecc/bls12-381/pairing.go#L52] + #[must_use] + fn exp_by_x(f: Fq12) -> Fq12 { + let mut acc = Fq12::one(); + for (i, b) in BLS_X.into_iter().enumerate() { + (i != 0).then(|| acc.cyclotomic_square()); + (b == 1).then(|| acc *= f); + } + acc.conjugate(); + acc + } + + let mut t0 = *self; + t0.frobenius_map(6); + + Gt(self + .invert() + .map(|mut t1| { + let mut t2 = t0 * t1; + t1 = t2; + t2.frobenius_map(2); + t2 *= t1; + t1 = t2; + t1.cyclotomic_square(); + t1.conjugate(); + let mut t3 = exp_by_x(t2); + let mut t4 = t3; + t4.cyclotomic_square(); + let mut t5 = t1 * t3; + t1 = exp_by_x(t5); + t0 = exp_by_x(t1); + let mut t6 = exp_by_x(t0) * t4; + t4 = exp_by_x(t6); + t5.conjugate(); + t4 *= t5 * t2; + t1 *= t2; + t1.frobenius_map(3); + t2.conjugate(); + t6 *= t2; + t6.frobenius_map(1); + t3 *= t0; + t3.frobenius_map(2); + t3 * t4 * t1 * t6 + }) + .unwrap()) + } +} + +pub fn multi_miller_loop(terms: &[(&G1Affine, &G2Affine)]) -> Fq12 { + let terms = terms + .iter() + .filter_map(|&(p, q)| { + if bool::from(p.is_identity()) || bool::from(q.is_identity()) { + None + } else { + Some((p, q)) + } + }) + .collect::>(); + + let mut f = Fq12::one(); + let mut r = terms.iter().map(|(_, q)| q.to_curve()).collect::>(); + + for (i, x) in BLS_X.iter().map(|&b| b == 1).skip(1).enumerate() { + if i != 0 { + f.square_assign(); + } + + terms.iter().zip(r.iter_mut()).for_each(|((p, _), r)| { + double(&mut f, r, p); + }); + + if x { + for ((p, q), r) in terms.iter().zip(r.iter_mut()) { + add(&mut f, r, q, p); + } + } + } + + f.conjugate(); + f +} + +fn ell(f: &mut Fq12, coeffs: &(Fq2, Fq2, Fq2), p: &G1Affine) { + let mut c0 = coeffs.0; + let mut c1 = coeffs.1; + c0.c0.mul_assign(&p.y); + c0.c1.mul_assign(&p.y); + c1.c0.mul_assign(&p.x); + c1.c1.mul_assign(&p.x); + Fq12::mul_by_014(f, &coeffs.2, &c1, &c0); +} + +#[cfg(test)] +mod test { + use ff::Field; + use group::{prime::PrimeCurveAffine, Curve, Group}; + use pairing::{Engine as _, MillerLoopResult, PairingCurveAffine}; + use rand_core::OsRng; + + use super::{ + super::{Bls12381, Fr, G1, G2}, + multi_miller_loop, Fq12, G1Affine, G2Affine, Gt, + }; + crate::test_pairing!(Bls12381, G1, G1Affine, G2, G2Affine, Fq12, Gt, Fr); +} diff --git a/forks/halo2curves/src/bls12381/fq.rs b/forks/halo2curves/src/bls12381/fq.rs new file mode 100644 index 00000000..2599a92f --- /dev/null +++ b/forks/halo2curves/src/bls12381/fq.rs @@ -0,0 +1,61 @@ +use core::convert::TryInto; + +use halo2derive::impl_field; +use rand::RngCore; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +impl_field!( + bls12381_base, + Fq, + modulus = "1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab", + mul_gen = "2", + zeta = "1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac", + from_uniform = [64, 96], + endian = "big", +); + +crate::extend_field_legendre!(Fq); +crate::impl_binops_calls!(Fq); +crate::impl_binops_additive!(Fq, Fq); +crate::impl_binops_multiplicative!(Fq, Fq); +crate::field_bits!(Fq); +crate::serialize_deserialize_primefield!(Fq); +crate::impl_from_u64!(Fq); +crate::impl_from_bool!(Fq); + +use ff::Field; + +use crate::ff_ext::ExtField; +const NEGATIVE_ONE: Fq = Fq::ZERO.sub_const(&Fq::ONE); +impl ExtField for Fq { + const NON_RESIDUE: Self = NEGATIVE_ONE; + fn mul_by_nonresidue(&self) -> Self { + self.neg() + } + fn frobenius_map(&mut self, _: usize) {} +} + +#[cfg(test)] +mod test { + use super::*; + use crate::{ + arith_test, constants_test, from_uniform_bytes_test, legendre_test, serde_test, test, + }; + + constants_test!(Fq); + + arith_test!(Fq); + legendre_test!(Fq); + test!(arith, Fq, sqrt_test, 1000); + + serde_test!(Fq PrimeFieldBits); + from_uniform_bytes_test!(Fq, 1000, L 64, L 96); + + #[test] + fn test_fq_mul_nonresidue() { + let e = Fq::random(rand_core::OsRng); + let a0 = e.mul_by_nonresidue(); + let a1 = e * Fq::NON_RESIDUE; + assert_eq!(a0, a1); + } +} diff --git a/forks/halo2curves/src/bls12381/fq12.rs b/forks/halo2curves/src/bls12381/fq12.rs new file mode 100644 index 00000000..f059204d --- /dev/null +++ b/forks/halo2curves/src/bls12381/fq12.rs @@ -0,0 +1,297 @@ +use super::{fq::Fq, fq2::Fq2, fq6::Fq6}; +use crate::ff_ext::{ + quadratic::{QuadExtField, QuadExtFieldArith, QuadSparseMul}, + ExtField, +}; + +pub type Fq12 = QuadExtField; + +impl QuadExtFieldArith for Fq12 { + type Base = Fq6; +} + +impl QuadSparseMul for Fq12 { + type Base = Fq2; +} + +impl ExtField for Fq12 { + const NON_RESIDUE: Self = Fq12::zero(); // no needs + + fn frobenius_map(&mut self, power: usize) { + self.c0.frobenius_map(power); + self.c1.frobenius_map(power); + self.c1.c0.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]); + self.c1.c1.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]); + self.c1.c2.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]); + } +} + +crate::impl_binops_additive!(Fq12, Fq12); +crate::impl_binops_multiplicative!(Fq12, Fq12); +crate::impl_binops_calls!(Fq12); +crate::impl_sum_prod!(Fq12); +crate::impl_cyclotomic_square!(Fq2, Fq12); + +pub const FROBENIUS_COEFF_FQ12_C1: [Fq2; 12] = [ + // z = u + 1 + // z ^ ((p ^ 0 - 1) / 6) + Fq2 { + c0: Fq([ + 0x760900000002fffd, + 0xebf4000bc40c0002, + 0x5f48985753c758ba, + 0x77ce585370525745, + 0x5c071a97a256ec6d, + 0x15f65ec3fa80e493, + ]), + c1: Fq([ + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + ]), + }, + // z ^ ((p ^ 1 - 1) / 6) + Fq2 { + c0: Fq([ + 0x07089552b319d465, + 0xc6695f92b50a8313, + 0x97e83cccd117228f, + 0xa35baecab2dc29ee, + 0x1ce393ea5daace4d, + 0x08f2220fb0fb66eb, + ]), + c1: Fq([ + 0xb2f66aad4ce5d646, + 0x5842a06bfc497cec, + 0xcf4895d42599d394, + 0xc11b9cba40a8e8d0, + 0x2e3813cbe5a0de89, + 0x110eefda88847faf, + ]), + }, + // z ^ ((p ^ 2 - 1) / 6) + Fq2 { + c0: Fq([ + 0xecfb361b798dba3a, + 0xc100ddb891865a2c, + 0x0ec08ff1232bda8e, + 0xd5c13cc6f1ca4721, + 0x47222a47bf7b5c04, + 0x0110f184e51c5f59, + ]), + c1: Fq([ + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + ]), + }, + // z ^ ((p ^ 3 - 1) / 6) + Fq2 { + c0: Fq([ + 0x3e2f585da55c9ad1, + 0x4294213d86c18183, + 0x382844c88b623732, + 0x92ad2afd19103e18, + 0x1d794e4fac7cf0b9, + 0x0bd592fc7d825ec8, + ]), + c1: Fq([ + 0x7bcfa7a25aa30fda, + 0xdc17dec12a927e7c, + 0x2f088dd86b4ebef1, + 0xd1ca2087da74d4a7, + 0x2da2596696cebc1d, + 0x0e2b7eedbbfd87d2, + ]), + }, + // z ^ ((p ^ 4 - 1) / 6) + Fq2 { + c0: Fq([ + 0x30f1361b798a64e8, + 0xf3b8ddab7ece5a2a, + 0x16a8ca3ac61577f7, + 0xc26a2ff874fd029b, + 0x3636b76660701c6e, + 0x051ba4ab241b6160, + ]), + c1: Fq([ + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + ]), + }, + // z ^ ((p ^ 5 - 1) / 6) + Fq2 { + c0: Fq([ + 0x3726c30af242c66c, + 0x7c2ac1aad1b6fe70, + 0xa04007fbba4b14a2, + 0xef517c3266341429, + 0x0095ba654ed2226b, + 0x02e370eccc86f7dd, + ]), + c1: Fq([ + 0x82d83cf50dbce43f, + 0xa2813e53df9d018f, + 0xc6f0caa53c65e181, + 0x7525cf528d50fe95, + 0x4a85ed50f4798a6b, + 0x171da0fd6cf8eebd, + ]), + }, + // z ^ ((p ^ 6 - 1) / 6) + Fq2 { + c0: Fq([ + 0x43f5fffffffcaaae, + 0x32b7fff2ed47fffd, + 0x07e83a49a2e99d69, + 0xeca8f3318332bb7a, + 0xef148d1ea0f4c069, + 0x040ab3263eff0206, + ]), + c1: Fq([ + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + ]), + }, + // z ^ ((p ^ 7 - 1) / 6) + Fq2 { + c0: Fq([ + 0xb2f66aad4ce5d646, + 0x5842a06bfc497cec, + 0xcf4895d42599d394, + 0xc11b9cba40a8e8d0, + 0x2e3813cbe5a0de89, + 0x110eefda88847faf, + ]), + c1: Fq([ + 0x07089552b319d465, + 0xc6695f92b50a8313, + 0x97e83cccd117228f, + 0xa35baecab2dc29ee, + 0x1ce393ea5daace4d, + 0x08f2220fb0fb66eb, + ]), + }, + // z ^ ((p ^ 8 - 1) / 6) + Fq2 { + c0: Fq([ + 0xcd03c9e48671f071, + 0x5dab22461fcda5d2, + 0x587042afd3851b95, + 0x8eb60ebe01bacb9e, + 0x03f97d6e83d050d2, + 0x18f0206554638741, + ]), + c1: Fq([ + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + ]), + }, + // z ^ ((p ^ 9 - 1) / 6) + Fq2 { + c0: Fq([ + 0x7bcfa7a25aa30fda, + 0xdc17dec12a927e7c, + 0x2f088dd86b4ebef1, + 0xd1ca2087da74d4a7, + 0x2da2596696cebc1d, + 0x0e2b7eedbbfd87d2, + ]), + c1: Fq([ + 0x3e2f585da55c9ad1, + 0x4294213d86c18183, + 0x382844c88b623732, + 0x92ad2afd19103e18, + 0x1d794e4fac7cf0b9, + 0x0bd592fc7d825ec8, + ]), + }, + // z ^ ((p ^ 10 - 1) / 6) + Fq2 { + c0: Fq([ + 0x890dc9e4867545c3, + 0x2af322533285a5d5, + 0x50880866309b7e2c, + 0xa20d1b8c7e881024, + 0x14e4f04fe2db9068, + 0x14e56d3f1564853a, + ]), + c1: Fq([ + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + ]), + }, + // z ^ ((p ^ 11 - 1) / 6) + Fq2 { + c0: Fq([ + 0x82d83cf50dbce43f, + 0xa2813e53df9d018f, + 0xc6f0caa53c65e181, + 0x7525cf528d50fe95, + 0x4a85ed50f4798a6b, + 0x171da0fd6cf8eebd, + ]), + c1: Fq([ + 0x3726c30af242c66c, + 0x7c2ac1aad1b6fe70, + 0xa04007fbba4b14a2, + 0xef517c3266341429, + 0x0095ba654ed2226b, + 0x02e370eccc86f7dd, + ]), + }, +]; + +#[cfg(test)] +mod test { + macro_rules! test_fq12 { + ($test:ident, $size: expr) => { + paste::paste! { + #[test] + fn [< $test test >]() { + use rand::SeedableRng; + use rand_xorshift::XorShiftRng; + let mut rng = XorShiftRng::from_seed(crate::tests::SEED); + crate::bls12381::fq12::test::$test(&mut rng, $size); + } + } + }; + } + use ff::Field; + use rand::RngCore; + + use super::*; + use crate::{arith_test, frobenius_test, setup_f12_test_funcs, test}; + + arith_test!(Fq12); + // TODO Compile problems with derive_serde feature + // serde_test!(fq12); + + // F12 specific + setup_f12_test_funcs!(Fq12, Fq6, Fq2); + test_fq12!(f12_mul_by_014_, 500); + test_fq12!(f12_mul_by_034_, 500); + frobenius_test!(Fq12, Fq, 8); +} diff --git a/forks/halo2curves/src/bls12381/fq2.rs b/forks/halo2curves/src/bls12381/fq2.rs new file mode 100644 index 00000000..bc322262 --- /dev/null +++ b/forks/halo2curves/src/bls12381/fq2.rs @@ -0,0 +1,98 @@ +use core::convert::TryInto; +use std::cmp::Ordering; + +use subtle::{Choice, CtOption}; + +use super::fq::Fq; +use crate::{ + ff::{Field, FromUniformBytes, PrimeField, WithSmallOrderMulGroup}, + ff_ext::{ + quadratic::{QuadExtField, QuadExtFieldArith, SQRT}, + ExtField, Legendre, + }, +}; + +crate::impl_binops_additive!(Fq2, Fq2); +crate::impl_binops_multiplicative!(Fq2, Fq2); +crate::impl_binops_calls!(Fq2); +crate::impl_sum_prod!(Fq2); +crate::impl_tower2!(Fq, Fq2); +crate::impl_tower2_from_uniform_bytes!(Fq, Fq2, 128); + +pub type Fq2 = QuadExtField; +impl QuadExtFieldArith for Fq2 { + type Base = Fq; + const SQRT: SQRT = SQRT::Algorithm9 { + q_minus_3_over_4: &[ + 0xee7fbfffffffeaaa, + 0x07aaffffac54ffff, + 0xd9cc34a83dac3d89, + 0xd91dd2e13ce144af, + 0x92c6e9ed90d2eb35, + 0x0680447a8e5ff9a6, + ], + q_minus_1_over_2: &[ + 0xdcff7fffffffd555, + 0x0f55ffff58a9ffff, + 0xb39869507b587b12, + 0xb23ba5c279c2895f, + 0x258dd3db21a5d66b, + 0x0d0088f51cbff34d, + ], + }; + + fn square_assign(el: &mut QuadExtField) { + let a = el.c0 + el.c1; + let b = el.c0 - el.c1; + let c = el.c0.double(); + el.c0 = a * b; + el.c1 = c * el.c1; + } +} + +impl ExtField for Fq2 { + const NON_RESIDUE: Self = Fq2::new(Fq::ONE, Fq::ONE); + + fn mul_by_nonresidue(&self) -> Self { + Self { + c0: self.c0 - self.c1, + c1: self.c0 + self.c1, + } + } + + fn frobenius_map(&mut self, power: usize) { + if power % 2 != 0 { + self.conjugate(); + } + } +} + +#[cfg(test)] +mod test { + + use rand_core::RngCore; + + use super::*; + use crate::{ + arith_test, constants_test, f2_test, frobenius_test, legendre_test, serde_test, test, + }; + + constants_test!(Fq2); + + arith_test!(Fq2); + legendre_test!(Fq2); + test!(arith, Fq2, sqrt_test, 1000); + + serde_test!(Fq2); + + f2_test!(Fq2, Fq); + frobenius_test!(Fq2, Fq, 20); + + #[test] + fn test_fq2_mul_nonresidue() { + let e = Fq2::random(rand_core::OsRng); + let a0 = e.mul_by_nonresidue(); + let a1 = e * Fq2::NON_RESIDUE; + assert_eq!(a0, a1); + } +} diff --git a/forks/halo2curves/src/bls12381/fq6.rs b/forks/halo2curves/src/bls12381/fq6.rs new file mode 100644 index 00000000..bf5722c0 --- /dev/null +++ b/forks/halo2curves/src/bls12381/fq6.rs @@ -0,0 +1,312 @@ +use ff::Field; + +use super::{fq::Fq, fq2::Fq2}; +use crate::ff_ext::{ + cubic::{CubicExtField, CubicExtFieldArith, CubicSparseMul}, + ExtField, +}; + +crate::impl_binops_additive!(Fq6, Fq6); +crate::impl_binops_multiplicative!(Fq6, Fq6); +crate::impl_binops_calls!(Fq6); +crate::impl_sum_prod!(Fq6); +pub type Fq6 = CubicExtField; + +impl CubicExtFieldArith for Fq6 { + type Base = Fq2; +} + +impl CubicSparseMul for Fq6 { + type Base = Fq2; +} + +impl ExtField for Fq6 { + const NON_RESIDUE: Self = Fq6::new(Fq2::ZERO, Fq2::ONE, Fq2::ZERO); + + fn frobenius_map(&mut self, power: usize) { + self.c0.frobenius_map(power); + self.c1.frobenius_map(power); + self.c2.frobenius_map(power); + self.c1.mul_assign(&FROBENIUS_COEFF_FQ6_C1[power % 6]); + self.c2.mul_assign(&FROBENIUS_COEFF_FQ6_C2[power % 6]); + } + + fn mul_by_nonresidue(self: &Fq6) -> Fq6 { + let c0 = self.c2.mul_by_nonresidue(); + let c1 = self.c0; + let c2 = self.c1; + Self { c0, c1, c2 } + } +} + +pub const FROBENIUS_COEFF_FQ6_C1: [Fq2; 6] = [ + // z ^ (( p ^ 0 - 1) / 3) + Fq2 { + c0: Fq([ + 0x760900000002fffd, + 0xebf4000bc40c0002, + 0x5f48985753c758ba, + 0x77ce585370525745, + 0x5c071a97a256ec6d, + 0x15f65ec3fa80e493, + ]), + c1: Fq([ + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + ]), + }, + // z ^ (( p ^ 1 - 1) / 3) + Fq2 { + c0: Fq([ + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + ]), + c1: Fq([ + 0xcd03c9e48671f071, + 0x5dab22461fcda5d2, + 0x587042afd3851b95, + 0x8eb60ebe01bacb9e, + 0x03f97d6e83d050d2, + 0x18f0206554638741, + ]), + }, + // z ^ (( p ^ 2 - 1) / 3) + Fq2 { + c0: Fq([ + 0x30f1361b798a64e8, + 0xf3b8ddab7ece5a2a, + 0x16a8ca3ac61577f7, + 0xc26a2ff874fd029b, + 0x3636b76660701c6e, + 0x051ba4ab241b6160, + ]), + c1: Fq([ + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + ]), + }, + // z ^ (( p ^ 3 - 1) / 3) + Fq2 { + c0: Fq([ + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + ]), + c1: Fq([ + 0x760900000002fffd, + 0xebf4000bc40c0002, + 0x5f48985753c758ba, + 0x77ce585370525745, + 0x5c071a97a256ec6d, + 0x15f65ec3fa80e493, + ]), + }, + // z ^ (( p ^ 4 - 1) / 3) + Fq2 { + c0: Fq([ + 0xcd03c9e48671f071, + 0x5dab22461fcda5d2, + 0x587042afd3851b95, + 0x8eb60ebe01bacb9e, + 0x03f97d6e83d050d2, + 0x18f0206554638741, + ]), + c1: Fq([ + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + ]), + }, + // z ^ (( p ^ 5 - 1) / 3) + Fq2 { + c0: Fq([ + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + ]), + c1: Fq([ + 0x30f1361b798a64e8, + 0xf3b8ddab7ece5a2a, + 0x16a8ca3ac61577f7, + 0xc26a2ff874fd029b, + 0x3636b76660701c6e, + 0x051ba4ab241b6160, + ]), + }, +]; + +// z = u + 1 +pub const FROBENIUS_COEFF_FQ6_C2: [Fq2; 6] = [ + // z ^ (( 2 * p ^ 0 - 2) / 3) + Fq2 { + c0: Fq([ + 0x760900000002fffd, + 0xebf4000bc40c0002, + 0x5f48985753c758ba, + 0x77ce585370525745, + 0x5c071a97a256ec6d, + 0x15f65ec3fa80e493, + ]), + c1: Fq([ + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + ]), + }, + // z ^ (( 2 * p ^ 1 - 2) / 3) + Fq2 { + c0: Fq([ + 0x890dc9e4867545c3, + 0x2af322533285a5d5, + 0x50880866309b7e2c, + 0xa20d1b8c7e881024, + 0x14e4f04fe2db9068, + 0x14e56d3f1564853a, + ]), + c1: Fq([ + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + ]), + }, + // z ^ (( 2 * p ^ 2 - 2) / 3) + Fq2 { + c0: Fq([ + 0xcd03c9e48671f071, + 0x5dab22461fcda5d2, + 0x587042afd3851b95, + 0x8eb60ebe01bacb9e, + 0x03f97d6e83d050d2, + 0x18f0206554638741, + ]), + c1: Fq([ + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + ]), + }, + // z ^ (( 2 * p ^ 3 - 2) / 3) + Fq2 { + c0: Fq([ + 0x43f5fffffffcaaae, + 0x32b7fff2ed47fffd, + 0x07e83a49a2e99d69, + 0xeca8f3318332bb7a, + 0xef148d1ea0f4c069, + 0x040ab3263eff0206, + ]), + c1: Fq([ + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + ]), + }, + // z ^ (( 2 * p ^ 4 - 2) / 3) + Fq2 { + c0: Fq([ + 0x30f1361b798a64e8, + 0xf3b8ddab7ece5a2a, + 0x16a8ca3ac61577f7, + 0xc26a2ff874fd029b, + 0x3636b76660701c6e, + 0x051ba4ab241b6160, + ]), + c1: Fq([ + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + ]), + }, + // z ^ (( 2 * p ^ 5 - 2) / 3) + Fq2 { + c0: Fq([ + 0xecfb361b798dba3a, + 0xc100ddb891865a2c, + 0x0ec08ff1232bda8e, + 0xd5c13cc6f1ca4721, + 0x47222a47bf7b5c04, + 0x0110f184e51c5f59, + ]), + c1: Fq([ + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + ]), + }, +]; + +#[cfg(test)] +mod test { + use rand_core::RngCore; + + use super::*; + use crate::{arith_test, frobenius_test, setup_f6_test_funcs, test}; + + macro_rules! test_fq6 { + ($test:ident, $size: expr) => { + paste::paste! { + #[test] + fn [< $test test >]() { + use rand::SeedableRng; + use rand_xorshift::XorShiftRng; + let mut rng = XorShiftRng::from_seed(crate::tests::SEED); + crate::bls12381::fq6::test::$test(&mut rng, $size); + } + } + }; + } + + arith_test!(Fq6); + setup_f6_test_funcs!(Fq6, Fq2); + test_fq6!(f6_mul_nonresidue_, 1000); + test_fq6!(f6_mul_by_1_, 1000); + test_fq6!(f6_mul_by_01_, 1000); + frobenius_test!(Fq6, Fq, 10); + + #[test] + fn test_fq6_mul_nonresidue() { + let e = Fq6::random(rand_core::OsRng); + let a0 = e.mul_by_nonresidue(); + let a1 = e * Fq6::NON_RESIDUE; + assert_eq!(a0, a1); + } +} diff --git a/forks/halo2curves/src/bls12381/fr.rs b/forks/halo2curves/src/bls12381/fr.rs new file mode 100644 index 00000000..28674dfe --- /dev/null +++ b/forks/halo2curves/src/bls12381/fr.rs @@ -0,0 +1,41 @@ +use core::convert::TryInto; + +use halo2derive::impl_field; +use rand::RngCore; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +impl_field!( + bls12381_scalar, + Fr, + modulus = "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001", + mul_gen = "7", + zeta = "ac45a4010001a40200000000ffffffff", + from_uniform = [64], + endian = "little", +); + +crate::extend_field_legendre!(Fr); +crate::impl_binops_calls!(Fr); +crate::impl_binops_additive!(Fr, Fr); +crate::impl_binops_multiplicative!(Fr, Fr); +crate::field_bits!(Fr); +crate::serialize_deserialize_primefield!(Fr); +crate::impl_from_u64!(Fr); +crate::impl_from_bool!(Fr); + +#[cfg(test)] +mod test { + use super::Fr; + use crate::{ + arith_test, constants_test, from_uniform_bytes_test, legendre_test, serde_test, test, + }; + + constants_test!(Fr); + + arith_test!(Fr); + legendre_test!(Fr); + test!(arith, Fr, sqrt_test, 1000); + + serde_test!(Fr PrimeFieldBits); + from_uniform_bytes_test!(Fr, 1000, L 64); +} diff --git a/forks/halo2curves/src/bls12381/g1.rs b/forks/halo2curves/src/bls12381/g1.rs new file mode 100644 index 00000000..119f5851 --- /dev/null +++ b/forks/halo2curves/src/bls12381/g1.rs @@ -0,0 +1,696 @@ +use core::{ + cmp, + iter::Sum, + ops::{Add, Mul, Neg, Sub}, +}; + +use ff::{PrimeField, WithSmallOrderMulGroup}; +use group::{ + cofactor::CofactorGroup, ff::Field, prime::PrimeCurveAffine, Curve, Group, GroupEncoding, +}; +use rand_core::RngCore; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +use super::{fq::Fq, Fr}; +use crate::{ + impl_binops_additive, impl_binops_additive_specify_output, impl_binops_multiplicative, + impl_binops_multiplicative_mixed, new_curve_impl, + serde::{Compressed, CompressedFlagConfig}, + Coordinates, CurveAffine, CurveExt, +}; + +new_curve_impl!( + (pub), + G1, + G1Affine, + Fq, + Fr, + (GENERATOR_X, GENERATOR_Y), + A, + B, + "bls12381_g1", + |domain_prefix| hash_to_curve(domain_prefix, hash_to_curve_suite(b"BLS12381G1_XMD:SHA-256_SSWU_RO_")), + crate::serde::CompressedFlagConfig::ThreeSpare +); + +impl Compressed for G1Compressed { + const CONFIG: CompressedFlagConfig = CompressedFlagConfig::ThreeSpare; + fn sign(c: &G1Affine) -> Choice { + c.y.lexicographically_largest() & !c.is_identity() + } + fn resolve(x: Fq, sign_set: Choice) -> CtOption { + G1Affine::y2(x).sqrt().map(|y| { + let y = Fq::conditional_select(&y, &-y, sign_set ^ y.lexicographically_largest()); + G1Affine { x, y } + }) + } +} + +impl group::cofactor::CofactorGroup for G1 { + type Subgroup = G1; + + fn clear_cofactor(&self) -> Self { + self - self.mul_by_x() + } + + fn into_subgroup(self) -> CtOption { + CtOption::new(self, 1.into()) + } + + fn is_torsion_free(&self) -> Choice { + self.mul_by_x().mul_by_x().neg().endo().ct_eq(self) + } +} + +const GENERATOR_X: Fq = Fq([ + 0x5cb38790fd530c16, + 0x7817fc679976fff5, + 0x154f95c7143ba1c1, + 0xf0ae6acdf3d0e747, + 0xedce6ecc21dbf440, + 0x120177419e0bfb75, +]); + +const GENERATOR_Y: Fq = Fq([ + 0xbaac93d50ce72271, + 0x8c22631a7918fd8e, + 0xdd595f13570725ce, + 0x51ac582950405194, + 0x0e1c8c3fad0059c0, + 0x0bbc3efc5008a26a, +]); + +const A: Fq = Fq::ZERO; +const B: Fq = Fq::from_raw([4, 0, 0, 0, 0, 0]); + +impl G1 { + fn mul_by_x(&self) -> G1 { + let mut acc = G1::identity(); + for (i, b) in super::BLS_X.into_iter().enumerate() { + (i != 0).then(|| acc = acc.double()); + (b == 1).then(|| acc += self); + } + acc.neg() + } +} + +fn hash_to_curve_suite(domain: &[u8]) -> crate::hash_to_curve::Suite { + const SSWU_Z: Fq = Fq::from_raw([11, 0, 0, 0, 0, 0]); + + pub const ISO_A: Fq = Fq([ + 0x2f65_aa0e_9af5_aa51, + 0x8646_4c2d_1e84_16c3, + 0xb85c_e591_b7bd_31e2, + 0x27e1_1c91_b5f2_4e7c, + 0x2837_6eda_6bfc_1835, + 0x1554_55c3_e507_1d85, + ]); + + pub const ISO_B: Fq = Fq([ + 0xfb99_6971_fe22_a1e0, + 0x9aa9_3eb3_5b74_2d6f, + 0x8c47_6013_de99_c5c4, + 0x873e_27c3_a221_e571, + 0xca72_b5e4_5a52_d888, + 0x0682_4061_418a_386b, + ]); + + let iso_map = crate::hash_to_curve::Iso { + a: ISO_A, + b: ISO_B, + map: Box::new(iso_map), + }; + + crate::hash_to_curve::Suite::new(domain, SSWU_Z, crate::hash_to_curve::Method::SSWU(iso_map)) +} + +/// Maps an iso-G1 point to a G1 point. +fn iso_map(x: Fq, y: Fq, z: Fq) -> G1 { + const COEFFS: [&[Fq]; 4] = [&ISO11_XNUM, &ISO11_XDEN, &ISO11_YNUM, &ISO11_YDEN]; + + // xnum, xden, ynum, yden + let mut mapvals = [Fq::zero(); 4]; + + // pre-compute powers of z + let zpows = { + let mut zpows = [Fq::zero(); 15]; + zpows[0] = z; + for idx in 1..zpows.len() { + zpows[idx] = zpows[idx - 1] * z; + } + zpows + }; + + // compute map value by Horner's rule + for idx in 0..4 { + let coeff = COEFFS[idx]; + let clast = coeff.len() - 1; + mapvals[idx] = coeff[clast]; + for jdx in 0..clast { + mapvals[idx] = mapvals[idx] * x + zpows[jdx] * coeff[clast - 1 - jdx]; + } + } + + // x denominator is order 1 less than x numerator, so we need an extra factor of + // z + mapvals[1] *= z; + + // multiply result of Y map by the y-coord, y / z + mapvals[2] *= y; + mapvals[3] *= z; + + G1 { + x: mapvals[0] * mapvals[3], // xnum * yden, + y: mapvals[2] * mapvals[1], // ynum * xden, + z: mapvals[1] * mapvals[3], // xden * yden + } +} + +#[allow(clippy::type_complexity)] +pub(crate) fn hash_to_curve<'a>( + domain_prefix: &'a str, + suite: crate::hash_to_curve::Suite, +) -> Box G1 + 'a> { + Box::new(move |message| suite.hash_to_curve(domain_prefix, message).clear_cofactor()) +} + +#[cfg(test)] +mod test { + use group::UncompressedEncoding; + use rand_core::OsRng; + + use super::*; + use crate::{arithmetic::CurveEndo, serde::SerdeObject, tests::curve::TestH2C}; + crate::curve_testing_suite!(G1); + crate::curve_testing_suite!(G1, "endo_consistency"); + crate::curve_testing_suite!(G1, "endo"); + + #[test] + fn test_cofactor() { + assert!(bool::from( + G1Affine::identity().to_curve().is_torsion_free() + )); + assert!(bool::from( + G1Affine::generator().to_curve().is_torsion_free() + )); + } + + #[test] + fn test_hash_to_curve() { + [ + TestH2C::::new( + b"", + crate::tests::point_from_hex( + "052926add2207b76ca4fa57a8734416c8dc95e24501772c814278700eed6d1e4e8cf62d9c09db0fac349612b759e79a1", + "08ba738453bfed09cb546dbb0783dbb3a5f1f566ed67bb6be0e8c67e2e81a4cc68ee29813bb7994998f3eae0c9c6a265", + ), + ), + TestH2C::::new( + b"abc", + crate::tests::point_from_hex( + "03567bc5ef9c690c2ab2ecdf6a96ef1c139cc0b2f284dca0a9a7943388a49a3aee664ba5379a7655d3c68900be2f6903", + "0b9c15f3fe6e5cf4211f346271d7b01c8f3b28be689c8429c85b67af215533311f0b8dfaaa154fa6b88176c229f2885d", + ), + ), + TestH2C::::new( + b"abcdef0123456789", + crate::tests::point_from_hex( + "11e0b079dea29a68f0383ee94fed1b940995272407e3bb916bbf268c263ddd57a6a27200a784cbc248e84f357ce82d98", + "03a87ae2caf14e8ee52e51fa2ed8eefe80f02457004ba4d486d6aa1f517c0889501dc7413753f9599b099ebcbbd2d709", + ), + ), + TestH2C::::new( + b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", + crate::tests::point_from_hex( + "15f68eaa693b95ccb85215dc65fa81038d69629f70aeee0d0f677cf22285e7bf58d7cb86eefe8f2e9bc3f8cb84fac488", + "1807a1d50c29f430b8cafc4f8638dfeeadf51211e1602a5f184443076715f91bb90a48ba1e370edce6ae1062f5e6dd38", + ), + ), + TestH2C::::new( + b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + crate::tests::point_from_hex( + "082aabae8b7dedb0e78aeb619ad3bfd9277a2f77ba7fad20ef6aabdc6c31d19ba5a6d12283553294c1825c4b3ca2dcfe", + "05b84ae5a942248eea39e1d91030458c40153f3b654ab7872d779ad1e942856a20c438e8d99bc8abfbf74729ce1f7ac8", + ), + ) + ].iter().for_each(|test| { + test.run("QUUX-V01-CS02-with-"); + }); + } +} + +/// Coefficients of the 11-isogeny x map's numerator +const ISO11_XNUM: [Fq; 12] = [ + Fq::from_raw([ + 0x4d18_b6f3_af00_131c, + 0x19fa_2197_93fe_e28c, + 0x3f28_85f1_467f_19ae, + 0x23dc_ea34_f2ff_b304, + 0xd15b_58d2_ffc0_0054, + 0x0913_be20_0a20_bef4, + ]), + Fq::from_raw([ + 0x8989_8538_5cdb_bd8b, + 0x3c79_e43c_c7d9_66aa, + 0x1597_e193_f4cd_233a, + 0x8637_ef1e_4d66_23ad, + 0x11b2_2dee_d20d_827b, + 0x0709_7bc5_9987_84ad, + ]), + Fq::from_raw([ + 0xa542_583a_480b_664b, + 0xfc71_69c0_26e5_68c6, + 0x5ba2_ef31_4ed8_b5a6, + 0x5b54_91c0_5102_f0e7, + 0xdf6e_9970_7d2a_0079, + 0x0784_151e_d760_5524, + ]), + Fq::from_raw([ + 0x494e_2128_70f7_2741, + 0xab9b_e52f_bda4_3021, + 0x26f5_5779_94e3_4c3d, + 0x049d_fee8_2aef_bd60, + 0x65da_dd78_2850_5289, + 0x0e93_d431_ea01_1aeb, + ]), + Fq::from_raw([ + 0x90ee_774b_d6a7_4d45, + 0x7ada_1c8a_41bf_b185, + 0x0f1a_8953_b325_f464, + 0x104c_2421_1be4_805c, + 0x1691_39d3_19ea_7a8f, + 0x09f2_0ead_8e53_2bf6, + ]), + Fq::from_raw([ + 0x6ddd_93e2_f436_26b7, + 0xa548_2c9a_a1cc_d7bd, + 0x1432_4563_1883_f4bd, + 0x2e0a_94cc_f77e_c0db, + 0xb028_2d48_0e56_489f, + 0x18f4_bfcb_b436_8929, + ]), + Fq::from_raw([ + 0x23c5_f0c9_5340_2dfd, + 0x7a43_ff69_58ce_4fe9, + 0x2c39_0d3d_2da5_df63, + 0xd0df_5c98_e1f9_d70f, + 0xffd8_9869_a572_b297, + 0x1277_ffc7_2f25_e8fe, + ]), + Fq::from_raw([ + 0x79f4_f049_0f06_a8a6, + 0x85f8_94a8_8030_fd81, + 0x12da_3054_b18b_6410, + 0xe2a5_7f65_0588_0d65, + 0xbba0_74f2_60e4_00f1, + 0x08b7_6279_f621_d028, + ]), + Fq::from_raw([ + 0xe672_45ba_78d5_b00b, + 0x8456_ba9a_1f18_6475, + 0x7888_bff6_e6b3_3bb4, + 0xe215_85b9_a30f_86cb, + 0x05a6_9cdc_ef55_feee, + 0x09e6_99dd_9adf_a5ac, + ]), + Fq::from_raw([ + 0x0de5_c357_bff5_7107, + 0x0a0d_b4ae_6b1a_10b2, + 0xe256_bb67_b3b3_cd8d, + 0x8ad4_5657_4e9d_b24f, + 0x0443_915f_50fd_4179, + 0x098c_4bf7_de8b_6375, + ]), + Fq::from_raw([ + 0xe6b0_617e_7dd9_29c7, + 0xfe6e_37d4_4253_7375, + 0x1daf_deda_137a_489e, + 0xe4ef_d1ad_3f76_7ceb, + 0x4a51_d866_7f0f_e1cf, + 0x054f_df4b_bf1d_821c, + ]), + Fq::from_raw([ + 0x72db_2a50_658d_767b, + 0x8abf_91fa_a257_b3d5, + 0xe969_d683_3764_ab47, + 0x4641_7014_2a10_09eb, + 0xb14f_01aa_db30_be2f, + 0x18ae_6a85_6f40_715d, + ]), +]; + +/// Coefficients of the 11-isogeny x map's denominator +const ISO11_XDEN: [Fq; 11] = [ + Fq::from_raw([ + 0xb962_a077_fdb0_f945, + 0xa6a9_740f_efda_13a0, + 0xc14d_568c_3ed6_c544, + 0xb43f_c37b_908b_133e, + 0x9c0b_3ac9_2959_9016, + 0x0165_aa6c_93ad_115f, + ]), + Fq::from_raw([ + 0x2327_9a3b_a506_c1d9, + 0x92cf_ca0a_9465_176a, + 0x3b29_4ab1_3755_f0ff, + 0x116d_da1c_5070_ae93, + 0xed45_3092_4cec_2045, + 0x0833_83d6_ed81_f1ce, + ]), + Fq::from_raw([ + 0x9885_c2a6_449f_ecfc, + 0x4a2b_54cc_d377_33f0, + 0x17da_9ffd_8738_c142, + 0xa0fb_a727_32b3_fafd, + 0xff36_4f36_e54b_6812, + 0x0f29_c13c_6605_23e2, + ]), + Fq::from_raw([ + 0xe349_cc11_8278_f041, + 0xd487_228f_2f32_04fb, + 0xc9d3_2584_9ade_5150, + 0x43a9_2bd6_9c15_c2df, + 0x1c2c_7844_bc41_7be4, + 0x1202_5184_f407_440c, + ]), + Fq::from_raw([ + 0x587f_65ae_6acb_057b, + 0x1444_ef32_5140_201f, + 0xfbf9_95e7_1270_da49, + 0xccda_0660_7243_6a42, + 0x7408_904f_0f18_6bb2, + 0x13b9_3c63_edf6_c015, + ]), + Fq::from_raw([ + 0xfb91_8622_cd14_1920, + 0x4a4c_6442_3eca_ddb4, + 0x0beb_2329_27f7_fb26, + 0x30f9_4df6_f83a_3dc2, + 0xaeed_d424_d780_f388, + 0x06cc_402d_d594_bbeb, + ]), + Fq::from_raw([ + 0xd41f_7611_51b2_3f8f, + 0x32a9_2465_4357_19b3, + 0x64f4_36e8_88c6_2cb9, + 0xdf70_a9a1_f757_c6e4, + 0x6933_a38d_5b59_4c81, + 0x0c6f_7f72_37b4_6606, + ]), + Fq::from_raw([ + 0x693c_0874_7876_c8f7, + 0x22c9_850b_f9cf_80f0, + 0x8e90_71da_b950_c124, + 0x89bc_62d6_1c7b_af23, + 0xbc6b_e2d8_dad5_7c23, + 0x1791_6987_aa14_a122, + ]), + Fq::from_raw([ + 0x1be3_ff43_9c13_16fd, + 0x9965_243a_7571_dfa7, + 0xc7f7_f629_62f5_cd81, + 0x32c6_aa9a_f394_361c, + 0xbbc2_ee18_e1c2_27f4, + 0x0c10_2cba_c531_bb34, + ]), + Fq::from_raw([ + 0x9976_14c9_7bac_bf07, + 0x61f8_6372_b991_92c0, + 0x5b8c_95fc_1435_3fc3, + 0xca2b_066c_2a87_492f, + 0x1617_8f5b_bf69_8711, + 0x12a6_dcd7_f0f4_e0e8, + ]), + Fq::from_raw([ + 0x7609_0000_0002_fffd, + 0xebf4_000b_c40c_0002, + 0x5f48_9857_53c7_58ba, + 0x77ce_5853_7052_5745, + 0x5c07_1a97_a256_ec6d, + 0x15f6_5ec3_fa80_e493, + ]), +]; + +/// Coefficients of the 11-isogeny y map's numerator +const ISO11_YNUM: [Fq; 16] = [ + Fq::from_raw([ + 0x2b56_7ff3_e283_7267, + 0x1d4d_9e57_b958_a767, + 0xce02_8fea_04bd_7373, + 0xcc31_a30a_0b6c_d3df, + 0x7d7b_18a6_8269_2693, + 0x0d30_0744_d42a_0310, + ]), + Fq::from_raw([ + 0x99c2_555f_a542_493f, + 0xfe7f_53cc_4874_f878, + 0x5df0_608b_8f97_608a, + 0x14e0_3832_052b_49c8, + 0x7063_26a6_957d_d5a4, + 0x0a8d_add9_c241_4555, + ]), + Fq::from_raw([ + 0x13d9_4292_2a5c_f63a, + 0x357e_33e3_6e26_1e7d, + 0xcf05_a27c_8456_088d, + 0x0000_bd1d_e7ba_50f0, + 0x83d0_c753_2f8c_1fde, + 0x13f7_0bf3_8bbf_2905, + ]), + Fq::from_raw([ + 0x5c57_fd95_bfaf_bdbb, + 0x28a3_59a6_5e54_1707, + 0x3983_ceb4_f636_0b6d, + 0xafe1_9ff6_f97e_6d53, + 0xb346_8f45_5019_2bf7, + 0x0bb6_cde4_9d8b_a257, + ]), + Fq::from_raw([ + 0x590b_62c7_ff8a_513f, + 0x314b_4ce3_72ca_cefd, + 0x6bef_32ce_94b8_a800, + 0x6ddf_84a0_9571_3d5f, + 0x64ea_ce4c_b098_2191, + 0x0386_213c_651b_888d, + ]), + Fq::from_raw([ + 0xa531_0a31_111b_bcdd, + 0xa14a_c0f5_da14_8982, + 0xf9ad_9cc9_5423_d2e9, + 0xaa6e_c095_283e_e4a7, + 0xcf5b_1f02_2e1c_9107, + 0x01fd_df5a_ed88_1793, + ]), + Fq::from_raw([ + 0x65a5_72b0_d7a7_d950, + 0xe25c_2d81_8347_3a19, + 0xc2fc_ebe7_cb87_7dbd, + 0x05b2_d36c_769a_89b0, + 0xba12_961b_e86e_9efb, + 0x07eb_1b29_c1df_de1f, + ]), + Fq::from_raw([ + 0x93e0_9572_f7c4_cd24, + 0x364e_9290_7679_5091, + 0x8569_467e_68af_51b5, + 0xa47d_a894_39f5_340f, + 0xf4fa_9180_82e4_4d64, + 0x0ad5_2ba3_e669_5a79, + ]), + Fq::from_raw([ + 0x9114_2984_4e0d_5f54, + 0xd03f_51a3_516b_b233, + 0x3d58_7e56_4053_6e66, + 0xfa86_d2a3_a9a7_3482, + 0xa90e_d5ad_f1ed_5537, + 0x149c_9c32_6a5e_7393, + ]), + Fq::from_raw([ + 0x462b_beb0_3c12_921a, + 0xdc9a_f5fa_0a27_4a17, + 0x9a55_8ebd_e836_ebed, + 0x649e_f8f1_1a4f_ae46, + 0x8100_e165_2b3c_dc62, + 0x1862_bd62_c291_dacb, + ]), + Fq::from_raw([ + 0x05c9_b8ca_89f1_2c26, + 0x0194_160f_a9b9_ac4f, + 0x6a64_3d5a_6879_fa2c, + 0x1466_5bdd_8846_e19d, + 0xbb1d_0d53_af3f_f6bf, + 0x12c7_e1c3_b289_62e5, + ]), + Fq::from_raw([ + 0xb55e_bf90_0b8a_3e17, + 0xfedc_77ec_1a92_01c4, + 0x1f07_db10_ea1a_4df4, + 0x0dfb_d15d_c41a_594d, + 0x3895_47f2_334a_5391, + 0x0241_9f98_1658_71a4, + ]), + Fq::from_raw([ + 0xb416_af00_0745_fc20, + 0x8e56_3e9d_1ea6_d0f5, + 0x7c76_3e17_763a_0652, + 0x0145_8ef0_159e_bbef, + 0x8346_fe42_1f96_bb13, + 0x0d2d_7b82_9ce3_24d2, + ]), + Fq::from_raw([ + 0x9309_6bb5_38d6_4615, + 0x6f2a_2619_951d_823a, + 0x8f66_b3ea_5951_4fa4, + 0xf563_e637_04f7_092f, + 0x724b_136c_4cf2_d9fa, + 0x0469_59cf_cfd0_bf49, + ]), + Fq::from_raw([ + 0xea74_8d4b_6e40_5346, + 0x91e9_079c_2c02_d58f, + 0x4106_4965_946d_9b59, + 0xa067_31f1_d2bb_e1ee, + 0x07f8_97e2_67a3_3f1b, + 0x1017_2909_1921_0e5f, + ]), + Fq::from_raw([ + 0x872a_a6c1_7d98_5097, + 0xeecc_5316_1264_562a, + 0x07af_e37a_fff5_5002, + 0x5475_9078_e5be_6838, + 0xc4b9_2d15_db8a_cca8, + 0x106d_87d1_b51d_13b9, + ]), +]; + +/// Coefficients of the 11-isogeny y map's denominator +const ISO11_YDEN: [Fq; 16] = [ + Fq::from_raw([ + 0xeb6c_359d_47e5_2b1c, + 0x18ef_5f8a_1063_4d60, + 0xddfa_71a0_889d_5b7e, + 0x723e_71dc_c5fc_1323, + 0x52f4_5700_b70d_5c69, + 0x0a8b_981e_e476_91f1, + ]), + Fq::from_raw([ + 0x616a_3c4f_5535_b9fb, + 0x6f5f_0373_95db_d911, + 0xf25f_4cc5_e35c_65da, + 0x3e50_dffe_a3c6_2658, + 0x6a33_dca5_2356_0776, + 0x0fad_eff7_7b6b_fe3e, + ]), + Fq::from_raw([ + 0x2be9_b66d_f470_059c, + 0x24a2_c159_a3d3_6742, + 0x115d_be7a_d10c_2a37, + 0xb663_4a65_2ee5_884d, + 0x04fe_8bb2_b8d8_1af4, + 0x01c2_a7a2_56fe_9c41, + ]), + Fq::from_raw([ + 0xf27b_f8ef_3b75_a386, + 0x898b_3674_76c9_073f, + 0x2448_2e6b_8c2f_4e5f, + 0xc8e0_bbd6_fe11_0806, + 0x59b0_c17f_7631_448a, + 0x1103_7cd5_8b3d_bfbd, + ]), + Fq::from_raw([ + 0x31c7_912e_a267_eec6, + 0x1dbf_6f1c_5fcd_b700, + 0xd30d_4fe3_ba86_fdb1, + 0x3cae_528f_bee9_a2a4, + 0xb1cc_e69b_6aa9_ad9a, + 0x0443_93bb_632d_94fb, + ]), + Fq::from_raw([ + 0xc66e_f6ef_eeb5_c7e8, + 0x9824_c289_dd72_bb55, + 0x71b1_a4d2_f119_981d, + 0x104f_c1aa_fb09_19cc, + 0x0e49_df01_d942_a628, + 0x096c_3a09_7732_72d4, + ]), + Fq::from_raw([ + 0x9abc_11eb_5fad_eff4, + 0x32dc_a50a_8857_28f0, + 0xfb1f_a372_1569_734c, + 0xc4b7_6271_ea65_06b3, + 0xd466_a755_99ce_728e, + 0x0c81_d464_5f4c_b6ed, + ]), + Fq::from_raw([ + 0x4199_f10e_5b8b_e45b, + 0xda64_e495_b1e8_7930, + 0xcb35_3efe_9b33_e4ff, + 0x9e9e_fb24_aa64_24c6, + 0xf08d_3368_0a23_7465, + 0x0d33_7802_3e4c_7406, + ]), + Fq::from_raw([ + 0x7eb4_ae92_ec74_d3a5, + 0xc341_b4aa_9fac_3497, + 0x5be6_0389_9e90_7687, + 0x03bf_d9cc_a75c_bdeb, + 0x564c_2935_a96b_fa93, + 0x0ef3_c333_71e2_fdb5, + ]), + Fq::from_raw([ + 0x7ee9_1fd4_49f6_ac2e, + 0xe5d5_bd5c_b935_7a30, + 0x773a_8ca5_196b_1380, + 0xd0fd_a172_174e_d023, + 0x6cb9_5e0f_a776_aead, + 0x0d22_d5a4_0cec_7cff, + ]), + Fq::from_raw([ + 0xf727_e092_85fd_8519, + 0xdc9d_55a8_3017_897b, + 0x7549_d8bd_0578_94ae, + 0x1784_1961_3d90_d8f8, + 0xfce9_5ebd_eb5b_490a, + 0x0467_ffae_f23f_c49e, + ]), + Fq::from_raw([ + 0xc176_9e6a_7c38_5f1b, + 0x79bc_930d_eac0_1c03, + 0x5461_c75a_23ed_e3b5, + 0x6e20_829e_5c23_0c45, + 0x828e_0f1e_772a_53cd, + 0x116a_efa7_4912_7bff, + ]), + Fq::from_raw([ + 0x101c_10bf_2744_c10a, + 0xbbf1_8d05_3a6a_3154, + 0xa0ec_f39e_f026_f602, + 0xfc00_9d49_96dc_5153, + 0xb900_0209_d5bd_08d3, + 0x189e_5fe4_470c_d73c, + ]), + Fq::from_raw([ + 0x7ebd_546c_a157_5ed2, + 0xe47d_5a98_1d08_1b55, + 0x57b2_b625_b6d4_ca21, + 0xb0a1_ba04_2285_20cc, + 0x9873_8983_c210_7ff3, + 0x13dd_dbc4_799d_81d6, + ]), + Fq::from_raw([ + 0x0931_9f2e_3983_4935, + 0x039e_952c_bdb0_5c21, + 0x55ba_77a9_a2f7_6493, + 0xfd04_e3df_c608_6467, + 0xfb95_832e_7d78_742e, + 0x0ef9_c24e_ccaf_5e0e, + ]), + Fq::from_raw([ + 0x7609_0000_0002_fffd, + 0xebf4_000b_c40c_0002, + 0x5f48_9857_53c7_58ba, + 0x77ce_5853_7052_5745, + 0x5c07_1a97_a256_ec6d, + 0x15f6_5ec3_fa80_e493, + ]), +]; diff --git a/forks/halo2curves/src/bls12381/g2.rs b/forks/halo2curves/src/bls12381/g2.rs new file mode 100644 index 00000000..3154551f --- /dev/null +++ b/forks/halo2curves/src/bls12381/g2.rs @@ -0,0 +1,643 @@ +use core::{ + cmp, + fmt::Debug, + iter::Sum, + ops::{Add, Mul, Neg, Sub}, +}; + +use rand::RngCore; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +use crate::{ + bls12381::{fq::Fq, fq2::Fq2, fr::Fr}, + ff::{Field, PrimeField, WithSmallOrderMulGroup}, + ff_ext::ExtField, + group::{cofactor::CofactorGroup, prime::PrimeCurveAffine, Curve, Group, GroupEncoding}, + impl_binops_additive, impl_binops_additive_specify_output, impl_binops_multiplicative, + impl_binops_multiplicative_mixed, new_curve_impl, + serde::{Compressed, CompressedFlagConfig}, + Coordinates, CurveAffine, CurveExt, +}; + +const G2_GENERATOR_X: Fq2 = Fq2 { + c0: Fq([ + 0xf5f2_8fa2_0294_0a10, + 0xb3f5_fb26_87b4_961a, + 0xa1a8_93b5_3e2a_e580, + 0x9894_999d_1a3c_aee9, + 0x6f67_b763_1863_366b, + 0x0581_9192_4350_bcd7, + ]), + c1: Fq([ + 0xa5a9_c075_9e23_f606, + 0xaaa0_c59d_bccd_60c3, + 0x3bb1_7e18_e286_7806, + 0x1b1a_b6cc_8541_b367, + 0xc2b6_ed0e_f215_8547, + 0x1192_2a09_7360_edf3, + ]), +}; + +const G2_GENERATOR_Y: Fq2 = Fq2 { + c0: Fq([ + 0x4c73_0af8_6049_4c4a, + 0x597c_fa1f_5e36_9c5a, + 0xe7e6_856c_aa0a_635a, + 0xbbef_b5e9_6e0d_495f, + 0x07d3_a975_f0ef_25a2, + 0x0083_fd8e_7e80_dae5, + ]), + c1: Fq([ + 0xadc0_fc92_df64_b05d, + 0x18aa_270a_2b14_61dc, + 0x86ad_ac6a_3be4_eba0, + 0x7949_5c4e_c93d_a33a, + 0xe717_5850_a43c_caed, + 0x0b2b_c2a1_63de_1bf2, + ]), +}; + +const G2_B: Fq2 = Fq2 { + c0: Fq::from_raw([4, 0, 0, 0, 0, 0]), + c1: Fq::from_raw([4, 0, 0, 0, 0, 0]), +}; + +const G2_A: Fq2 = Fq2::ZERO; + +new_curve_impl!( + (pub), + G2, + G2Affine, + Fq2, + Fr, + (G2_GENERATOR_X, G2_GENERATOR_Y), + G2_A, + G2_B, + "bls12381_g2", + |domain_prefix| hash_to_curve(domain_prefix, hash_to_curve_suite(b"BLS12381G2_XMD:SHA-256_SSWU_RO_")), + crate::serde::CompressedFlagConfig::ThreeSpare + +); + +impl crate::serde::endian::EndianRepr for Fq2 { + const ENDIAN: crate::serde::endian::Endian = Fq::ENDIAN; + + fn to_bytes(&self) -> Vec { + self.to_bytes().to_vec() + } + + fn from_bytes(bytes: &[u8]) -> subtle::CtOption { + Fq2::from_bytes(bytes[..Fq2::SIZE].try_into().unwrap()) + } +} + +impl Compressed for G2Compressed { + const CONFIG: CompressedFlagConfig = CompressedFlagConfig::ThreeSpare; + fn sign(c: &G2Affine) -> Choice { + c.y.lexicographically_largest() & !c.is_identity() + } + fn resolve(x: Fq2, sign_set: Choice) -> CtOption { + G2Affine::y2(x).sqrt().map(|y| { + let y = Fq2::conditional_select(&y, &-y, sign_set ^ y.lexicographically_largest()); + G2Affine { x, y } + }) + } +} + +impl group::cofactor::CofactorGroup for G2 { + type Subgroup = G2; + + /// Clears the cofactor, using [Budroni-Pintore](https://ia.cr/2017/419). + /// This is equivalent to multiplying by $h\_\textrm{eff} = 3(z^2 - 1) \cdot + /// h_2$, where $h_2$ is the cofactor of $\mathbb{G}\_2$ and $z$ is the + /// parameter of BLS12-381. + fn clear_cofactor(&self) -> G2 { + let t1 = self.mul_by_x(); // [x] P + let t2 = self.psi(); // psi(P) + + self.double().psi2() // psi^2(2P) + + (t1 + t2).mul_by_x() // psi^2(2P) + [x^2] P + [x] psi(P) + - t1 // psi^2(2P) + [x^2 - x] P + [x] psi(P) + - t2 // psi^2(2P) + [x^2 - x] P + [x - 1] psi(P) + - self // psi^2(2P) + [x^2 - x - 1] P + [x - 1] psi(P) + } + + fn into_subgroup(self) -> CtOption { + CtOption::new(self, 1.into()) + } + + /// Returns true if this point is free of an $h$-torsion component, and so + /// it exists within the $q$-order subgroup $\mathbb{G}_2$. This should + /// always return true unless an "unchecked" API was used. + fn is_torsion_free(&self) -> Choice { + // Algorithm from Section 4 of https://eprint.iacr.org/2021/1130 + // Updated proof of correctness in https://eprint.iacr.org/2022/352 + // + // Check that psi(P) == [x] P + self.psi().ct_eq(&self.mul_by_x()) + } +} + +impl G2 { + /// Multiply `self` by `crate::BLS_X`, using double and add. + + fn mul_by_x(&self) -> G2 { + let mut acc = G2::identity(); + for (i, b) in super::BLS_X.into_iter().enumerate() { + (i != 0).then(|| acc = acc.double()); + (b == 1).then(|| acc += self); + } + acc.neg() + } + + fn psi(&self) -> G2 { + // 1 / ((u+1) ^ ((q-1)/3)) + let psi_coeff_x = Fq2 { + c0: Fq::zero(), + c1: Fq([ + 0x890dc9e4867545c3, + 0x2af322533285a5d5, + 0x50880866309b7e2c, + 0xa20d1b8c7e881024, + 0x14e4f04fe2db9068, + 0x14e56d3f1564853a, + ]), + }; + // 1 / ((u+1) ^ (p-1)/2) + let psi_coeff_y = Fq2 { + c0: Fq([ + 0x3e2f585da55c9ad1, + 0x4294213d86c18183, + 0x382844c88b623732, + 0x92ad2afd19103e18, + 0x1d794e4fac7cf0b9, + 0x0bd592fc7d825ec8, + ]), + c1: Fq([ + 0x7bcfa7a25aa30fda, + 0xdc17dec12a927e7c, + 0x2f088dd86b4ebef1, + 0xd1ca2087da74d4a7, + 0x2da2596696cebc1d, + 0x0e2b7eedbbfd87d2, + ]), + }; + + // x = frobenius(x)/((u+1)^((p-1)/3)) + let mut x = self.x; + x.frobenius_map(1); + x.mul_assign(&psi_coeff_x); + + // y = frobenius(y)/(u+1)^((p-1)/2) + let mut y = self.y; + y.frobenius_map(1); + y.mul_assign(&psi_coeff_y); + + // z = frobenius(z) + let mut z = self.z; + z.frobenius_map(1); + + G2 { x, y, z } + } + + fn psi2(&self) -> G2 { + // 1 / 2 ^ ((q-1)/3) + let psi2_coeff_x = Fq2 { + c0: Fq([ + 0xcd03c9e48671f071, + 0x5dab22461fcda5d2, + 0x587042afd3851b95, + 0x8eb60ebe01bacb9e, + 0x03f97d6e83d050d2, + 0x18f0206554638741, + ]), + c1: Fq::zero(), + }; + + G2 { + // x = frobenius^2(x)/2^((p-1)/3); note that q^2 is the order of the field. + x: self.x * psi2_coeff_x, + // y = -frobenius^2(y); note that q^2 is the order of the field. + y: self.y.neg(), + // z = z + z: self.z, + } + } +} + +fn hash_to_curve_suite(domain: &[u8]) -> crate::hash_to_curve::Suite { + const SSWU_Z: Fq2 = Fq2 { + c0: Fq([ + 0x87eb_ffff_fff9_555c, + 0x656f_ffe5_da8f_fffa, + 0x0fd0_7493_45d3_3ad2, + 0xd951_e663_0665_76f4, + 0xde29_1a3d_41e9_80d3, + 0x0815_664c_7dfe_040d, + ]), + c1: Fq([ + 0x43f5_ffff_fffc_aaae, + 0x32b7_fff2_ed47_fffd, + 0x07e8_3a49_a2e9_9d69, + 0xeca8_f331_8332_bb7a, + 0xef14_8d1e_a0f4_c069, + 0x040a_b326_3eff_0206, + ]), + }; + + const ISO_A: Fq2 = Fq2 { + c0: Fq::zero(), + c1: Fq([ + 0xe53a_0000_0313_5242, + 0x0108_0c0f_def8_0285, + 0xe788_9edb_e340_f6bd, + 0x0b51_3751_2631_0601, + 0x02d6_9857_17c7_44ab, + 0x1220_b4e9_79ea_5467, + ]), + }; + + const ISO_B: Fq2 = Fq2 { + c0: Fq([ + 0x22ea_0000_0cf8_9db2, + 0x6ec8_32df_7138_0aa4, + 0x6e1b_9440_3db5_a66e, + 0x75bf_3c53_a794_73ba, + 0x3dd3_a569_412c_0a34, + 0x125c_db5e_74dc_4fd1, + ]), + c1: Fq([ + 0x22ea_0000_0cf8_9db2, + 0x6ec8_32df_7138_0aa4, + 0x6e1b_9440_3db5_a66e, + 0x75bf_3c53_a794_73ba, + 0x3dd3_a569_412c_0a34, + 0x125c_db5e_74dc_4fd1, + ]), + }; + let iso_map = crate::hash_to_curve::Iso { + a: ISO_A, + b: ISO_B, + map: Box::new(iso_map), + }; + + crate::hash_to_curve::Suite::new(domain, SSWU_Z, crate::hash_to_curve::Method::SSWU(iso_map)) +} + +/// Maps an iso-G1 point to a G1 point. +fn iso_map(x: Fq2, y: Fq2, z: Fq2) -> G2 { + const COEFFS: [&[Fq2]; 4] = [&ISO3_XNUM, &ISO3_XDEN, &ISO3_YNUM, &ISO3_YDEN]; + + // xnum, xden, ynum, yden + let mut mapvals = [Fq2::ZERO; 4]; + + // compute powers of z + let zsq = z.square(); + let zpows = [z, zsq, zsq * z]; + + // compute map value by Horner's rule + for idx in 0..4 { + let coeff = COEFFS[idx]; + let clast = coeff.len() - 1; + mapvals[idx] = coeff[clast]; + for jdx in 0..clast { + mapvals[idx] = mapvals[idx] * x + zpows[jdx] * coeff[clast - 1 - jdx]; + } + } + + // x denominator is order 1 less than x numerator, so we need an extra factor of + // z + mapvals[1] *= z; + + // multiply result of Y map by the y-coord, y / z + mapvals[2] *= y; + mapvals[3] *= z; + + G2 { + x: mapvals[0] * mapvals[3], // xnum * yden, + y: mapvals[2] * mapvals[1], // ynum * xden, + z: mapvals[1] * mapvals[3], // xden * yden + } +} + +#[allow(clippy::type_complexity)] +pub(crate) fn hash_to_curve<'a>( + domain_prefix: &'a str, + suite: crate::hash_to_curve::Suite, +) -> Box G2 + 'a> { + Box::new(move |message| suite.hash_to_curve(domain_prefix, message).clear_cofactor()) +} + +#[cfg(test)] +mod test { + use group::UncompressedEncoding; + use rand_core::OsRng; + + use super::*; + use crate::{arithmetic::CurveEndo, serde::SerdeObject}; + + crate::curve_testing_suite!(G2); + crate::curve_testing_suite!(G2, "endo_consistency"); + crate::curve_testing_suite!(G2, "endo"); + + #[test] + fn test_cofactor() { + assert!(bool::from( + G2Affine::identity().to_curve().is_torsion_free() + )); + assert!(bool::from( + G2Affine::generator().to_curve().is_torsion_free() + )); + } + + #[test] + fn test_hash_to_curve() { + pub(crate) fn point_from_hex(x0: &str, x1: &str, y0: &str, y1: &str) -> G2Affine { + let x0: Fq = crate::tests::hex_to_field(x0); + let x1: Fq = crate::tests::hex_to_field(x1); + let x = Fq2 { c0: x0, c1: x1 }; + let y0: Fq = crate::tests::hex_to_field(y0); + let y1: Fq = crate::tests::hex_to_field(y1); + let y = Fq2 { c0: y0, c1: y1 }; + G2Affine::from_xy(x, y).unwrap() + } + + struct Test { + msg: &'static [u8], + expect: G2Affine, + } + + impl Test { + fn new(msg: &'static [u8], expect: G2Affine) -> Self { + Self { msg, expect } + } + + fn run(&self, domain_prefix: &str) { + let r0 = G2::hash_to_curve(domain_prefix)(self.msg); + assert_eq!(r0.to_affine(), self.expect); + } + } + + let tests = [ + Test::new( + b"", + point_from_hex( + "0141ebfbdca40eb85b87142e130ab689c673cf60f1a3e98d69335266f30d9b8d4ac44c1038e9dcdd5393faf5c41fb78a", + "05cb8437535e20ecffaef7752baddf98034139c38452458baeefab379ba13dff5bf5dd71b72418717047f5b0f37da03d", + "0503921d7f6a12805e72940b963c0cf3471c7b2a524950ca195d11062ee75ec076daf2d4bc358c4b190c0c98064fdd92", + "12424ac32561493f3fe3c260708a12b7c620e7be00099a974e259ddc7d1f6395c3c811cdd19f1e8dbf3e9ecfdcbab8d6", + ), + ), + Test::new( + b"abc", + point_from_hex( + "02c2d18e033b960562aae3cab37a27ce00d80ccd5ba4b7fe0e7a210245129dbec7780ccc7954725f4168aff2787776e6", + "139cddbccdc5e91b9623efd38c49f81a6f83f175e80b06fc374de9eb4b41dfe4ca3a230ed250fbe3a2acf73a41177fd8", + "1787327b68159716a37440985269cf584bcb1e621d3a7202be6ea05c4cfe244aeb197642555a0645fb87bf7466b2ba48", + "00aa65dae3c8d732d10ecd2c50f8a1baf3001578f71c694e03866e9f3d49ac1e1ce70dd94a733534f106d4cec0eddd16", + ), + ), + Test::new( + b"abcdef0123456789", + point_from_hex( + "121982811d2491fde9ba7ed31ef9ca474f0e1501297f68c298e9f4c0028add35aea8bb83d53c08cfc007c1e005723cd0", + "190d119345b94fbd15497bcba94ecf7db2cbfd1e1fe7da034d26cbba169fb3968288b3fafb265f9ebd380512a71c3f2c", + "05571a0f8d3c08d094576981f4a3b8eda0a8e771fcdcc8ecceaf1356a6acf17574518acb506e435b639353c2e14827c8", + "0bb5e7572275c567462d91807de765611490205a941a5a6af3b1691bfe596c31225d3aabdf15faff860cb4ef17c7c3be", + ), + ), + Test::new( + b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", + point_from_hex( + "19a84dd7248a1066f737cc34502ee5555bd3c19f2ecdb3c7d9e24dc65d4e25e50d83f0f77105e955d78f4762d33c17da", + "0934aba516a52d8ae479939a91998299c76d39cc0c035cd18813bec433f587e2d7a4fef038260eef0cef4d02aae3eb91", + "14f81cd421617428bc3b9fe25afbb751d934a00493524bc4e065635b0555084dd54679df1536101b2c979c0152d09192", + "09bcccfa036b4847c9950780733633f13619994394c23ff0b32fa6b795844f4a0673e20282d07bc69641cee04f5e5662", + ), + ), + Test::new( + b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + point_from_hex( + "01a6ba2f9a11fa5598b2d8ace0fbe0a0eacb65deceb476fbbcb64fd24557c2f4b18ecfc5663e54ae16a84f5ab7f62534", + "11fca2ff525572795a801eed17eb12785887c7b63fb77a42be46ce4a34131d71f7a73e95fee3f812aea3de78b4d01569", + "0b6798718c8aed24bc19cb27f866f1c9effcdbf92397ad6448b5c9db90d2b9da6cbabf48adc1adf59a1a28344e79d57e", + "03a47f8e6d1763ba0cad63d6114c0accbef65707825a511b251a660a9b3994249ae4e63fac38b23da0c398689ee2ab52", + ), + ), + ]; + + tests.iter().for_each(|test| { + test.run("QUUX-V01-CS02-with-"); + }); + } +} + +/// Coefficients of the 3-isogeny x map's numerator +const ISO3_XNUM: [Fq2; 4] = [ + Fq2 { + c0: Fq([ + 0x47f6_71c7_1ce0_5e62, + 0x06dd_5707_1206_393e, + 0x7c80_cd2a_f3fd_71a2, + 0x0481_03ea_9e6c_d062, + 0xc545_16ac_c8d0_37f6, + 0x1380_8f55_0920_ea41, + ]), + c1: Fq([ + 0x47f6_71c7_1ce0_5e62, + 0x06dd_5707_1206_393e, + 0x7c80_cd2a_f3fd_71a2, + 0x0481_03ea_9e6c_d062, + 0xc545_16ac_c8d0_37f6, + 0x1380_8f55_0920_ea41, + ]), + }, + Fq2 { + c0: Fq::zero(), + c1: Fq([ + 0x5fe5_5555_554c_71d0, + 0x873f_ffdd_236a_aaa3, + 0x6a6b_4619_b26e_f918, + 0x21c2_8884_0887_4945, + 0x2836_cda7_028c_abc5, + 0x0ac7_3310_a7fd_5abd, + ]), + }, + Fq2 { + c0: Fq([ + 0x0a0c_5555_5559_71c3, + 0xdb0c_0010_1f9e_aaae, + 0xb1fb_2f94_1d79_7997, + 0xd396_0742_ef41_6e1c, + 0xb700_40e2_c205_56f4, + 0x149d_7861_e581_393b, + ]), + c1: Fq([ + 0xaff2_aaaa_aaa6_38e8, + 0x439f_ffee_91b5_5551, + 0xb535_a30c_d937_7c8c, + 0x90e1_4442_0443_a4a2, + 0x941b_66d3_8146_55e2, + 0x0563_9988_53fe_ad5e, + ]), + }, + Fq2 { + c0: Fq([ + 0x40aa_c71c_71c7_25ed, + 0x1909_5555_7a84_e38e, + 0xd817_050a_8f41_abc3, + 0xd864_85d4_c87f_6fb1, + 0x696e_b479_f885_d059, + 0x198e_1a74_3280_02d2, + ]), + c1: Fq::zero(), + }, +]; + +/// Coefficients of the 3-isogeny x map's denominator +const ISO3_XDEN: [Fq2; 3] = [ + Fq2 { + c0: Fq::zero(), + c1: Fq([ + 0x1f3a_ffff_ff13_ab97, + 0xf25b_fc61_1da3_ff3e, + 0xca37_57cb_3819_b208, + 0x3e64_2736_6f8c_ec18, + 0x0397_7bc8_6095_b089, + 0x04f6_9db1_3f39_a952, + ]), + }, + Fq2 { + c0: Fq([ + 0x4476_0000_0027_552e, + 0xdcb8_009a_4348_0020, + 0x6f7e_e9ce_4a6e_8b59, + 0xb103_30b7_c0a9_5bc6, + 0x6140_b1fc_fb1e_54b7, + 0x0381_be09_7f0b_b4e1, + ]), + c1: Fq([ + 0x7588_ffff_ffd8_557d, + 0x41f3_ff64_6e0b_ffdf, + 0xf7b1_e8d2_ac42_6aca, + 0xb374_1acd_32db_b6f8, + 0xe9da_f5b9_482d_581f, + 0x167f_53e0_ba74_31b8, + ]), + }, + Fq2::one(), +]; + +/// Coefficients of the 3-isogeny y map's numerator +const ISO3_YNUM: [Fq2; 4] = [ + Fq2 { + c0: Fq([ + 0x96d8_f684_bdfc_77be, + 0xb530_e4f4_3b66_d0e2, + 0x184a_88ff_3796_52fd, + 0x57cb_23ec_fae8_04e1, + 0x0fd2_e39e_ada3_eba9, + 0x08c8_055e_31c5_d5c3, + ]), + c1: Fq([ + 0x96d8_f684_bdfc_77be, + 0xb530_e4f4_3b66_d0e2, + 0x184a_88ff_3796_52fd, + 0x57cb_23ec_fae8_04e1, + 0x0fd2_e39e_ada3_eba9, + 0x08c8_055e_31c5_d5c3, + ]), + }, + Fq2 { + c0: Fq::zero(), + c1: Fq([ + 0xbf0a_71c7_1c91_b406, + 0x4d6d_55d2_8b76_38fd, + 0x9d82_f98e_5f20_5aee, + 0xa27a_a27b_1d1a_18d5, + 0x02c3_b2b2_d293_8e86, + 0x0c7d_1342_0b09_807f, + ]), + }, + Fq2 { + c0: Fq([ + 0xd7f9_5555_5553_1c74, + 0x21cf_fff7_48da_aaa8, + 0x5a9a_d186_6c9b_be46, + 0x4870_a221_0221_d251, + 0x4a0d_b369_c0a3_2af1, + 0x02b1_ccc4_29ff_56af, + ]), + c1: Fq([ + 0xe205_aaaa_aaac_8e37, + 0xfcdc_0007_6879_5556, + 0x0c96_011a_8a15_37dd, + 0x1c06_a963_f163_406e, + 0x010d_f44c_82a8_81e6, + 0x174f_4526_0f80_8feb, + ]), + }, + Fq2 { + c0: Fq([ + 0xa470_bda1_2f67_f35c, + 0xc0fe_38e2_3327_b425, + 0xc9d3_d0f2_c6f0_678d, + 0x1c55_c993_5b5a_982e, + 0x27f6_c0e2_f074_6764, + 0x117c_5e6e_28aa_9054, + ]), + c1: Fq::zero(), + }, +]; + +/// Coefficients of the 3-isogeny y map's denominator +const ISO3_YDEN: [Fq2; 4] = [ + Fq2 { + c0: Fq([ + 0x0162_ffff_fa76_5adf, + 0x8f7b_ea48_0083_fb75, + 0x561b_3c22_59e9_3611, + 0x11e1_9fc1_a9c8_75d5, + 0xca71_3efc_0036_7660, + 0x03c6_a03d_41da_1151, + ]), + c1: Fq([ + 0x0162_ffff_fa76_5adf, + 0x8f7b_ea48_0083_fb75, + 0x561b_3c22_59e9_3611, + 0x11e1_9fc1_a9c8_75d5, + 0xca71_3efc_0036_7660, + 0x03c6_a03d_41da_1151, + ]), + }, + Fq2 { + c0: Fq::zero(), + c1: Fq([ + 0x5db0_ffff_fd3b_02c5, + 0xd713_f523_58eb_fdba, + 0x5ea6_0761_a84d_161a, + 0xbb2c_75a3_4ea6_c44a, + 0x0ac6_7359_21c1_119b, + 0x0ee3_d913_bdac_fbf6, + ]), + }, + Fq2 { + c0: Fq([ + 0x66b1_0000_003a_ffc5, + 0xcb14_00e7_64ec_0030, + 0xa73e_5eb5_6fa5_d106, + 0x8984_c913_a0fe_09a9, + 0x11e1_0afb_78ad_7f13, + 0x0542_9d0e_3e91_8f52, + ]), + c1: Fq([ + 0x534d_ffff_ffc4_aae6, + 0x5397_ff17_4c67_ffcf, + 0xbff2_73eb_870b_251d, + 0xdaf2_8271_5287_0915, + 0x393a_9cba_ca9e_2dc3, + 0x14be_74db_faee_5748, + ]), + }, + Fq2::one(), +]; diff --git a/forks/halo2curves/src/bls12381/mod.rs b/forks/halo2curves/src/bls12381/mod.rs new file mode 100644 index 00000000..08577e5b --- /dev/null +++ b/forks/halo2curves/src/bls12381/mod.rs @@ -0,0 +1,37 @@ +mod engine; +mod fq; +mod fq12; +mod fq2; +mod fq6; +mod fr; +mod g1; +mod g2; + +pub use engine::*; +pub use fq::*; +pub use fq12::*; +pub use fq2::*; +pub use fq6::*; +pub use fr::*; +pub use g1::*; +pub use g2::*; + +const BLS_X: [u8; 64] = [ + 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +]; + +const ENDO_PARAMS: EndoParameters = EndoParameters { + // round(b2/n) + gamma2: [0x63f6e522f6cfee30u64, 0x7c6becf1e01faadd, 0x01, 0x0], + // round(-b1/n) + gamma1: [0x02u64, 0x0, 0x0, 0x0], + b1: [0x01u64, 0x0, 0x0, 0x0], + b2: [0x0000000100000000, 0xac45a4010001a402, 0x0, 0x0], +}; + +use ff::{PrimeField, WithSmallOrderMulGroup}; + +use crate::arithmetic::{mul_512, sbb, CurveEndo, EndoParameters}; +crate::endo!(G1, Fr, ENDO_PARAMS); +crate::endo!(G2, Fr, ENDO_PARAMS); diff --git a/forks/halo2curves/src/bn256/curve.rs b/forks/halo2curves/src/bn256/curve.rs new file mode 100644 index 00000000..14c6d9f2 --- /dev/null +++ b/forks/halo2curves/src/bn256/curve.rs @@ -0,0 +1,414 @@ +use core::{ + cmp, + fmt::Debug, + iter::Sum, + ops::{Add, Mul, Neg, Sub}, +}; +use std::convert::TryInto; + +use rand::RngCore; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +use crate::{ + arithmetic::{mul_512, sbb, CurveEndo, EndoParameters}, + bn256::{Fq, Fq2, Fr}, + endo, + ff::{Field, PrimeField, WithSmallOrderMulGroup}, + group::{cofactor::CofactorGroup, prime::PrimeCurveAffine, Curve, Group, GroupEncoding}, + impl_binops_additive, impl_binops_additive_specify_output, impl_binops_multiplicative, + impl_binops_multiplicative_mixed, new_curve_impl, Coordinates, CurveAffine, CurveExt, +}; + +impl crate::serde::endian::EndianRepr for Fq2 { + const ENDIAN: crate::serde::endian::Endian = Fq::ENDIAN; + + fn to_bytes(&self) -> Vec { + self.to_bytes().to_vec() + } + + fn from_bytes(bytes: &[u8]) -> subtle::CtOption { + Fq2::from_bytes(bytes[..Fq2::SIZE].try_into().unwrap()) + } +} + +new_curve_impl!( + (pub), + G1, + G1Affine, + Fq, + Fr, + (G1_GENERATOR_X,G1_GENERATOR_Y), + G1_A, + G1_B, + "bn256_g1", + |domain_prefix| crate::hash_to_curve::hash_to_curve(domain_prefix, G1::default_hash_to_curve_suite()), + crate::serde::CompressedFlagConfig::TwoSpare, + standard_sign +); + +new_curve_impl!( + (pub), + G2, + G2Affine, + Fq2, + Fr, + (G2_GENERATOR_X, G2_GENERATOR_Y), + G2_A, + G2_B, + "bn256_g2", + |domain_prefix| hash_to_curve_g2(domain_prefix), + crate::serde::CompressedFlagConfig::TwoSpare, + standard_sign +); + +#[allow(clippy::type_complexity)] +pub(crate) fn hash_to_curve_g2<'a>(domain_prefix: &'a str) -> Box G2 + 'a> { + let suite = G2::default_hash_to_curve_suite(); + Box::new(move |message| { + let r0 = suite.hash_to_curve(domain_prefix, message); + r0.clear_cofactor() + }) +} + +const G1_GENERATOR_X: Fq = Fq::ONE; +const G1_GENERATOR_Y: Fq = Fq::from_raw([2, 0, 0, 0]); +const G1_A: Fq = Fq::ZERO; +const G1_B: Fq = Fq::from_raw([3, 0, 0, 0]); + +const G2_A: Fq2 = Fq2 { + c0: Fq::ZERO, + c1: Fq::ZERO, +}; + +const G2_B: Fq2 = Fq2 { + c0: Fq::from_raw([ + 0x3267e6dc24a138e5, + 0xb5b4c5e559dbefa3, + 0x81be18991be06ac3, + 0x2b149d40ceb8aaae, + ]), + c1: Fq::from_raw([ + 0xe4a2bd0685c315d2, + 0xa74fa084e52d1852, + 0xcd2cafadeed8fdf4, + 0x009713b03af0fed4, + ]), +}; + +const G2_GENERATOR_X: Fq2 = Fq2 { + c0: Fq::from_raw([ + 0x46debd5cd992f6ed, + 0x674322d4f75edadd, + 0x426a00665e5c4479, + 0x1800deef121f1e76, + ]), + c1: Fq::from_raw([ + 0x97e485b7aef312c2, + 0xf1aa493335a9e712, + 0x7260bfb731fb5d25, + 0x198e9393920d483a, + ]), +}; + +const G2_GENERATOR_Y: Fq2 = Fq2 { + c0: Fq::from_raw([ + 0x4ce6cc0166fa7daa, + 0xe3d1e7690c43d37b, + 0x4aab71808dcb408f, + 0x12c85ea5db8c6deb, + ]), + + c1: Fq::from_raw([ + 0x55acdadcd122975b, + 0xbc4b313370b38ef3, + 0xec9e99ad690c3395, + 0x090689d0585ff075, + ]), +}; + +// Generated using https://github.com/ConsenSys/gnark-crypto/blob/master/ecc/utils.go +// with `bn256::Fr::ZETA` +// See https://github.com/demining/Endomorphism-Secp256k1/blob/main/README.md +// to have more details about the endomorphism. +const ENDO_PARAMS_BN: EndoParameters = EndoParameters { + // round(b2/n) + gamma1: [0xd91d232ec7e0b3d7, 0x2, 0, 0], + // round(-b1/n) + gamma2: [0x5398fd0300ff6565, 0x4ccef014a773d2d2, 0x02, 0], + b1: [0x89d3256894d213e3, 0, 0, 0], + b2: [0x0be4e1541221250b, 0x6f4d8248eeb859fd, 0, 0], +}; + +endo!(G1, Fr, ENDO_PARAMS_BN); + +impl group::cofactor::CofactorGroup for G1 { + type Subgroup = G1; + + fn clear_cofactor(&self) -> Self { + *self + } + + fn into_subgroup(self) -> CtOption { + CtOption::new(self, 1.into()) + } + + fn is_torsion_free(&self) -> Choice { + 1.into() + } +} + +fn exp_by_x(g2: &G2) -> G2 { + let x = super::BN_X; + + (0..62).rev().fold(*g2, |mut acc, i| { + println!("{}", ((x >> i) & 1) == 1); + + acc = acc.double(); + (((x >> i) & 1) == 1).then(|| acc += g2); + acc + }) +} + +fn psi(mut g2: G2) -> G2 { + const U0: Fq = Fq::from_raw([ + 0x99e39557176f553d, + 0xb78cc310c2c3330c, + 0x4c0bec3cf559b143, + 0x2fb347984f7911f7, + ]); + + const U1: Fq = Fq::from_raw([ + 0x1665d51c640fcba2, + 0x32ae2a1d0b7c9dce, + 0x4ba4cc8bd75a0794, + 0x16c9e55061ebae20, + ]); + let u = Fq2::new(U0, U1); + + const V0: Fq = Fq::from_raw([ + 0xdc54014671a0135a, + 0xdbaae0eda9c95998, + 0xdc5ec698b6e2f9b9, + 0x063cf305489af5dc, + ]); + + const V1: Fq = Fq::from_raw([ + 0x82d37f632623b0e3, + 0x21807dc98fa25bd2, + 0x0704b5a7ec796f2b, + 0x07c03cbcac41049a, + ]); + let v = Fq2::new(V0, V1); + + g2.x.conjugate(); + g2.y.conjugate(); + g2.z.conjugate(); + + g2.x *= u; + g2.y *= v; + + g2 +} + +impl CofactorGroup for G2 { + type Subgroup = G2; + + fn clear_cofactor(&self) -> Self { + let u0 = exp_by_x(self); + let u1 = psi(u0.double() + u0); + let u2 = psi(psi(u0)); + let u3 = psi(psi(psi(*self))); + u0 + u1 + u2 + u3 + } + + fn into_subgroup(self) -> CtOption { + unimplemented!(); + } + + fn is_torsion_free(&self) -> Choice { + let u = exp_by_x(self); + let pu = psi(u); + let ppu = psi(pu); + let pppu = psi(ppu); + (ppu + pu + u + self - pppu.double()).is_identity() + } +} + +impl G1 { + const SVDW_Z: Fq = Fq::ONE; + + fn default_hash_to_curve_suite() -> crate::hash_to_curve::Suite { + crate::hash_to_curve::Suite::::new( + b"BN254G1_XMD:SHA-256_SVDW_RO_", + Self::SVDW_Z, + crate::hash_to_curve::Method::SVDW, + ) + } +} + +impl G2 { + const SVDW_Z: Fq2 = Fq2::ONE; + + fn default_hash_to_curve_suite() -> crate::hash_to_curve::Suite { + crate::hash_to_curve::Suite::::new( + b"BN254G2_XMD:SHA-256_SVDW_RO_", + Self::SVDW_Z, + crate::hash_to_curve::Method::SVDW, + ) + } +} + +#[cfg(test)] +mod test { + use group::UncompressedEncoding; + use rand_core::OsRng; + + use super::*; + use crate::{serde::SerdeObject, tests::curve::TestH2C}; + + crate::curve_testing_suite!(G2, "clear_cofactor"); + crate::curve_testing_suite!(G1, G2); + crate::curve_testing_suite!(G1, "endo_consistency"); + crate::curve_testing_suite!( + G1, + "endo", + // Optional `z_other` param. `z_other` is 3-roots of unity, similar to `ZETA`. + // Reference: https://github.com/privacy-scaling-explorations/halo2curves/blob/main/src/bn256/fr.rs#L145-L151 + [ + 0x8b17ea66b99c90dd, + 0x5bfc41088d8daaa7, + 0xb3c4d79d41a91758, + 0x00, + ] + ); + + crate::curve_testing_suite!( + G1, + "constants", + Fq::MODULUS, + G1_A, + G1_B, + G1_GENERATOR_X, + G1_GENERATOR_Y, + Fr::MODULUS + ); + + crate::curve_testing_suite!( + G2, + "constants", + Fq2::MODULUS, + G2_A, + G2_B, + G2_GENERATOR_X, + G2_GENERATOR_Y, + Fr::MODULUS + ); + + #[test] + fn test_hash_to_curve_g1() { + // Test vectors are taken from gnark-crypto/ecc/bn254/hash_vectors_test.go + [ + TestH2C::::new( + b"", + crate::tests::point_from_hex( + "0a976ab906170db1f9638d376514dbf8c42aef256a54bbd48521f20749e59e86", + "02925ead66b9e68bfc309b014398640ab55f6619ab59bc1fab2210ad4c4d53d5", + ), + ), + TestH2C::::new( + b"abc", + crate::tests::point_from_hex( + "23f717bee89b1003957139f193e6be7da1df5f1374b26a4643b0378b5baf53d1", + "04142f826b71ee574452dbc47e05bc3e1a647478403a7ba38b7b93948f4e151d", + ), + ), + TestH2C::::new( + b"abcdef0123456789", + crate::tests::point_from_hex( + "187dbf1c3c89aceceef254d6548d7163fdfa43084145f92c4c91c85c21442d4a", + "0abd99d5b0000910b56058f9cc3b0ab0a22d47cf27615f588924fac1e5c63b4d", + ), + ), + TestH2C::::new( + b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", + crate::tests::point_from_hex( + "00fe2b0743575324fc452d590d217390ad48e5a16cf051bee5c40a2eba233f5c", + "0794211e0cc72d3cbbdf8e4e5cd6e7d7e78d101ff94862caae8acbe63e9fdc78", + ), + ), + TestH2C::::new( + b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + crate::tests::point_from_hex( + "01b05dc540bd79fd0fea4fbb07de08e94fc2e7bd171fe025c479dc212a2173ce", + "1bf028afc00c0f843d113758968f580640541728cfc6d32ced9779aa613cd9b0", + ), + ), + ].iter().for_each(|test| { + test.run("QUUX-V01-CS02-with-"); + }); + } + + #[test] + fn test_hash_to_curve_g2() { + pub(crate) fn point_from_hex(x0: &str, x1: &str, y0: &str, y1: &str) -> G2Affine { + let x0: Fq = crate::tests::hex_to_field(x0); + let x1: Fq = crate::tests::hex_to_field(x1); + let x = Fq2 { c0: x0, c1: x1 }; + let y0: Fq = crate::tests::hex_to_field(y0); + let y1: Fq = crate::tests::hex_to_field(y1); + let y = Fq2 { c0: y0, c1: y1 }; + G2Affine::from_xy(x, y).unwrap() + } + + // Test vectors are taken from gnark-crypto/ecc/bn254/hash_vectors_test.go + [ + TestH2C::::new( + b"", + point_from_hex( + "1192005a0f121921a6d5629946199e4b27ff8ee4d6dd4f9581dc550ade851300", + "1747d950a6f23c16156e2171bce95d1189b04148ad12628869ed21c96a8c9335", + "0498f6bb5ac309a07d9a8b88e6ff4b8de0d5f27a075830e1eb0e68ea318201d8", + "2c9755350ca363ef2cf541005437221c5740086c2e909b71d075152484e845f4", + ), + ), + TestH2C::::new( + b"abc", + point_from_hex( + "16c88b54eec9af86a41569608cd0f60aab43464e52ce7e6e298bf584b94fccd2", + "0b5db3ca7e8ef5edf3a33dfc3242357fbccead98099c3eb564b3d9d13cba4efd", + "1c42ba524cb74db8e2c680449746c028f7bea923f245e69f89256af2d6c5f3ac", + "22d02d2da7f288545ff8789e789902245ab08c6b1d253561eec789ec2c1bd630", + ), + ), + TestH2C::::new( + b"abcdef0123456789", + point_from_hex( + "1435fd84aa43c699230e371f6fea3545ce7e053cbbb06a320296a2b81efddc70", + "2a8a360585b6b05996ef69c3c09b2c6fb17afe2b1e944f07559c53178eabf171", + "2820188dcdc13ffdca31694942418afa1d6dfaaf259d012fab4da52b0f592e38", + "142f08e2441ec431defc24621b73cfe0252d19b243cb55b84bdeb85de039207a", + ), + ), + TestH2C::::new( + b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", + point_from_hex( + "2cffc213fb63d00d923cb22cda5a2904837bb93a2fe6e875c532c51744388341", + "2718ef38d1bc4347f0266c774c8ef4ee5fa7056cc27a4bd7ecf7a888efb95b26", + "232553f728341afa64ce66d00535764557a052e38657594e10074ad28728c584", + "2206ec0a9288f31ed78531c37295df3b56c42a1284443ee9893adb1521779001", + ), + ), + TestH2C::::new( + b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + point_from_hex( + "242a0a159f36f87065e7c5170426012087023165ce47a486e53d6e2845ca625a", + "17f9f6292998cf18ccc155903c1fe6b6465d40c794a3e1ed644a4182ad639f4a", + "2dc5b7b65c9c79e6ef4afab8fbe3083c66d4ce31c78f6621ece17ecc892cf4b3", + "18ef4886c818f01fdf309bc9a46dd904273917f85e74ecd0de62460a68122037", + ), + ), + ].iter().for_each(|test| { + test.run("QUUX-V01-CS02-with-"); + }); + } +} diff --git a/forks/halo2curves/src/bn256/engine.rs b/forks/halo2curves/src/bn256/engine.rs new file mode 100644 index 00000000..fa1c8187 --- /dev/null +++ b/forks/halo2curves/src/bn256/engine.rs @@ -0,0 +1,220 @@ +use core::{ + borrow::Borrow, + iter::Sum, + ops::{Add, Mul, Neg, Sub}, +}; +use std::ops::MulAssign; + +use pairing::{Engine, MillerLoopResult, MultiMillerLoop, PairingCurveAffine}; +use rand_core::RngCore; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; + +use crate::{ + bn256::{curve::*, fq::*, fq12::*, fq2::*, fq6::FROBENIUS_COEFF_FQ6_C1, fr::*}, + ff::PrimeField, + ff_ext::{quadratic::QuadSparseMul, ExtField}, + group::{cofactor::CofactorCurveAffine, Group}, +}; + +crate::impl_gt!(Gt, Fq12, Fr); +crate::impl_miller_loop_components!(Bn256, G1, G1Affine, G2, G2Affine, Fq12, Gt, Fr); + +impl MillerLoopResult for Fq12 { + type Gt = Gt; + + fn final_exponentiation(&self) -> Self::Gt { + fn exp_by_x(f: &mut Fq12) { + let x = super::BN_X; + let mut res = Fq12::one(); + for i in (0..64).rev() { + res.cyclotomic_square(); + if ((x >> i) & 1) == 1 { + res.mul_assign(f); + } + } + *f = res; + } + + let r = *self; + let mut f1 = *self; + f1.conjugate(); + + use ff::Field; + Gt(r.invert() + .map(|mut f2| { + let mut r = f1; + r.mul_assign(&f2); + f2 = r; + r.frobenius_map(2); + r.mul_assign(&f2); + + let mut fp = r; + fp.frobenius_map(1); + + let mut fp2 = r; + fp2.frobenius_map(2); + let mut fp3 = fp2; + fp3.frobenius_map(1); + + let mut fu = r; + exp_by_x(&mut fu); + + let mut fu2 = fu; + exp_by_x(&mut fu2); + + let mut fu3 = fu2; + exp_by_x(&mut fu3); + + let mut y3 = fu; + y3.frobenius_map(1); + + let mut fu2p = fu2; + fu2p.frobenius_map(1); + + let mut fu3p = fu3; + fu3p.frobenius_map(1); + + let mut y2 = fu2; + y2.frobenius_map(2); + + let mut y0 = fp; + y0.mul_assign(&fp2); + y0.mul_assign(&fp3); + + let mut y1 = r; + y1.conjugate(); + + let mut y5 = fu2; + y5.conjugate(); + + y3.conjugate(); + + let mut y4 = fu; + y4.mul_assign(&fu2p); + y4.conjugate(); + + let mut y6 = fu3; + y6.mul_assign(&fu3p); + y6.conjugate(); + + y6.cyclotomic_square(); + y6.mul_assign(&y4); + y6.mul_assign(&y5); + + let mut t1 = y3; + t1.mul_assign(&y5); + t1.mul_assign(&y6); + + y6.mul_assign(&y2); + + t1.cyclotomic_square(); + t1.mul_assign(&y6); + t1.cyclotomic_square(); + + let mut t0 = t1; + t0.mul_assign(&y1); + + t1.mul_assign(&y0); + + t0.cyclotomic_square(); + t0.mul_assign(&t1); + + t0 + }) + .unwrap()) + } +} + +pub fn multi_miller_loop(terms: &[(&G1Affine, &G2Affine)]) -> Fq12 { + let terms = terms + .iter() + .filter_map(|&(p, q)| { + if bool::from(p.is_identity()) || bool::from(q.is_identity()) { + None + } else { + Some((*p, *q)) + } + }) + .collect::>(); + + let mut f = Fq12::one(); + let mut r = terms.iter().map(|(_, q)| q.to_curve()).collect::>(); + + for (i, x) in super::SIX_U_PLUS_2_NAF.iter().rev().skip(1).enumerate() { + (i != 0).then(|| f.square_assign()); + + for ((p, _), r) in terms.iter().zip(r.iter_mut()) { + double(&mut f, r, p); + } + + match x { + &val @ (1 | -1) => { + for ((p, q), r) in terms.iter().zip(r.iter_mut()) { + if val == 1 { + add(&mut f, r, q, p); + } else { + add(&mut f, r, &q.neg(), p); + } + } + } + _ => continue, + } + } + + const XI_TO_Q_MINUS_1_OVER_2: Fq2 = Fq2 { + c0: Fq([ + 0xe4bbdd0c2936b629, + 0xbb30f162e133bacb, + 0x31a9d1b6f9645366, + 0x253570bea500f8dd, + ]), + c1: Fq([ + 0xa1d77ce45ffe77c7, + 0x07affd117826d1db, + 0x6d16bd27bb7edc6b, + 0x2c87200285defecc, + ]), + }; + + for ((p, q), r) in terms.iter().zip(r.iter_mut()) { + let mut q1: G2Affine = *q; + q1.x.conjugate(); + q1.x.mul_assign(&FROBENIUS_COEFF_FQ6_C1[1]); + q1.y.conjugate(); + q1.y.mul_assign(&XI_TO_Q_MINUS_1_OVER_2); + add(&mut f, r, &q1, p); + } + + for ((p, q), r) in terms.iter().zip(r.iter_mut()) { + let mut minusq2: G2Affine = *q; + minusq2.x.mul_assign(&FROBENIUS_COEFF_FQ6_C1[2]); + add(&mut f, r, &minusq2, p); + } + + f +} + +// Final steps of the line function on prepared coefficients +fn ell(f: &mut Fq12, coeffs: &(Fq2, Fq2, Fq2), p: &G1Affine) { + let mut c0 = coeffs.0; + let mut c1 = coeffs.1; + c0.c0.mul_assign(&p.y); + c0.c1.mul_assign(&p.y); + c1.c0.mul_assign(&p.x); + c1.c1.mul_assign(&p.x); + Fq12::mul_by_034(f, &c0, &c1, &coeffs.2); +} + +#[cfg(test)] +mod test { + use ff::Field; + use group::{prime::PrimeCurveAffine, Curve, Group}; + use pairing::{Engine, MillerLoopResult, PairingCurveAffine}; + use rand_core::OsRng; + + use super::{ + super::{Bn256, Fr, G1, G2}, + multi_miller_loop, Fq12, G1Affine, G2Affine, Gt, + }; + crate::test_pairing!(Bn256, G1, G1Affine, G2, G2Affine, Fq12, Gt, Fr); +} diff --git a/forks/halo2curves/src/bn256/fq.rs b/forks/halo2curves/src/bn256/fq.rs new file mode 100644 index 00000000..f234c1a8 --- /dev/null +++ b/forks/halo2curves/src/bn256/fq.rs @@ -0,0 +1,53 @@ +use core::convert::TryInto; + +use halo2derive::impl_field; +use rand::RngCore; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +use crate::ff_ext::ExtField; + +impl_field!( + bn256_base, + Fq, + modulus = "30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47", + mul_gen = "3", + zeta = "30644e72e131a0295e6dd9e7e0acccb0c28f069fbb966e3de4bd44e5607cfd48", + from_uniform = [64, 48], + endian = "little", +); + +crate::extend_field_legendre!(Fq); +crate::impl_binops_calls!(Fq); +crate::impl_binops_additive!(Fq, Fq); +crate::impl_binops_multiplicative!(Fq, Fq); +crate::field_bits!(Fq); +crate::serialize_deserialize_primefield!(Fq); +crate::impl_from_u64!(Fq); +crate::impl_from_bool!(Fq); + +use ff::Field; +const NEGATIVE_ONE: Fq = Fq::ZERO.sub_const(&Fq::ONE); +impl ExtField for Fq { + const NON_RESIDUE: Self = NEGATIVE_ONE; + fn mul_by_nonresidue(&self) -> Self { + self.neg() + } + fn frobenius_map(&mut self, _: usize) {} +} + +#[cfg(test)] +mod test { + use super::Fq; + use crate::{ + arith_test, constants_test, from_uniform_bytes_test, legendre_test, serde_test, test, + }; + + constants_test!(Fq); + + arith_test!(Fq); + legendre_test!(Fq); + test!(arith, Fq, sqrt_test, 1000); + + serde_test!(Fq PrimeFieldBits); + from_uniform_bytes_test!(Fq, 1000, L 64, L 48); +} diff --git a/forks/halo2curves/src/bn256/fq12.rs b/forks/halo2curves/src/bn256/fq12.rs new file mode 100644 index 00000000..3b515a84 --- /dev/null +++ b/forks/halo2curves/src/bn256/fq12.rs @@ -0,0 +1,223 @@ +use super::{fq::Fq, fq2::Fq2, fq6::Fq6}; +use crate::ff_ext::{ + quadratic::{QuadExtField, QuadExtFieldArith, QuadSparseMul}, + ExtField, +}; + +// -GAMMA is a quadratic non-residue in Fp6. Fp12 = Fp6[X] / (X^2 + GAMMA) +// We introduce the variable w such that w^2 = -GAMMA +// GAMMA = -v +/// An element of Fq12, represented by c0 + c1 * w. +pub type Fq12 = QuadExtField; + +impl QuadExtFieldArith for Fq12 { + type Base = Fq6; +} + +impl QuadSparseMul for Fq12 { + type Base = Fq2; +} + +impl ExtField for Fq12 { + const NON_RESIDUE: Self = Fq12::zero(); // no needs + + fn frobenius_map(&mut self, power: usize) { + self.c0.frobenius_map(power); + self.c1.frobenius_map(power); + self.c1.c0.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]); + self.c1.c1.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]); + self.c1.c2.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]); + } +} + +crate::impl_binops_additive!(Fq12, Fq12); +crate::impl_binops_multiplicative!(Fq12, Fq12); +crate::impl_binops_calls!(Fq12); +crate::impl_sum_prod!(Fq12); +crate::impl_cyclotomic_square!(Fq2, Fq12); + +// non_residue^((modulus^i-1)/6) for i=0,...,11 +pub const FROBENIUS_COEFF_FQ12_C1: [Fq2; 12] = [ + // Fq2(u + 9)**(((q^0) - 1) / 6) + // Fq points are represented in Montgomery form with R = 2^256 + Fq2 { + c0: Fq([ + 0xd35d438dc58f0d9d, + 0x0a78eb28f5c70b3d, + 0x666ea36f7879462c, + 0x0e0a77c19a07df2f, + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0]), + }, + // Fq2(u + 9)**(((q^1) - 1) / 6) + Fq2 { + c0: Fq([ + 0xaf9ba69633144907, + 0xca6b1d7387afb78a, + 0x11bded5ef08a2087, + 0x02f34d751a1f3a7c, + ]), + c1: Fq([ + 0xa222ae234c492d72, + 0xd00f02a4565de15b, + 0xdc2ff3a253dfc926, + 0x10a75716b3899551, + ]), + }, + // Fq2(u + 9)**(((q^2) - 1) / 6) + Fq2 { + c0: Fq([ + 0xca8d800500fa1bf2, + 0xf0c5d61468b39769, + 0x0e201271ad0d4418, + 0x04290f65bad856e6, + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0]), + }, + // Fq2(u + 9)**(((q^3) - 1) / 6) + Fq2 { + c0: Fq([ + 0x365316184e46d97d, + 0x0af7129ed4c96d9f, + 0x659da72fca1009b5, + 0x08116d8983a20d23, + ]), + c1: Fq([ + 0xb1df4af7c39c1939, + 0x3d9f02878a73bf7f, + 0x9b2220928caf0ae0, + 0x26684515eff054a6, + ]), + }, + // Fq2(u + 9)**(((q^4) - 1) / 6) + Fq2 { + c0: Fq([ + 0x3350c88e13e80b9c, + 0x7dce557cdb5e56b9, + 0x6001b4b8b615564a, + 0x2682e617020217e0, + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0]), + }, + // Fq2(u + 9)**(((q^5) - 1) / 6) + Fq2 { + c0: Fq([ + 0x86b76f821b329076, + 0x408bf52b4d19b614, + 0x53dfb9d0d985e92d, + 0x051e20146982d2a7, + ]), + c1: Fq([ + 0x0fbc9cd47752ebc7, + 0x6d8fffe33415de24, + 0xbef22cf038cf41b9, + 0x15c0edff3c66bf54, + ]), + }, + // Fq2(u + 9)**(((q^6) - 1) / 6) + Fq2 { + c0: Fq([ + 0x68c3488912edefaa, + 0x8d087f6872aabf4f, + 0x51e1a24709081231, + 0x2259d6b14729c0fa, + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0]), + }, + // Fq2(u + 9)**(((q^7) - 1) / 6) + Fq2 { + c0: Fq([ + 0x8c84e580a568b440, + 0xcd164d1de0c21302, + 0xa692585790f737d5, + 0x2d7100fdc71265ad, + ]), + c1: Fq([ + 0x99fdddf38c33cfd5, + 0xc77267ed1213e931, + 0xdc2052142da18f36, + 0x1fbcf75c2da80ad7, + ]), + }, + // Fq2(u + 9)**(((q^8) - 1) / 6) + Fq2 { + c0: Fq([ + 0x71930c11d782e155, + 0xa6bb947cffbe3323, + 0xaa303344d4741444, + 0x2c3b3f0d26594943, + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0]), + }, + // Fq2(u + 9)**(((q^9) - 1) / 6) + Fq2 { + c0: Fq([ + 0x05cd75fe8a3623ca, + 0x8c8a57f293a85cee, + 0x52b29e86b7714ea8, + 0x2852e0e95d8f9306, + ]), + c1: Fq([ + 0x8a41411f14e0e40e, + 0x59e26809ddfe0b0d, + 0x1d2e2523f4d24d7d, + 0x09fc095cf1414b83, + ]), + }, + // Fq2(u + 9)**(((q^10) - 1) / 6) + Fq2 { + c0: Fq([ + 0x08cfc388c494f1ab, + 0x19b315148d1373d4, + 0x584e90fdcb6c0213, + 0x09e1685bdf2f8849, + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0]), + }, + // Fq2(u + 9)**(((q^11) - 1) / 6) + Fq2 { + c0: Fq([ + 0xb5691c94bd4a6cd1, + 0x56f575661b581478, + 0x64708be5a7fb6f30, + 0x2b462e5e77aecd82, + ]), + c1: Fq([ + 0x2c63ef42612a1180, + 0x29f16aae345bec69, + 0xf95e18c648b216a4, + 0x1aa36073a4cae0d4, + ]), + }, +]; + +#[cfg(test)] +mod test { + + macro_rules! test_fq12 { + ($test:ident, $size: expr) => { + paste::paste! { + #[test] + fn [< $test test >]() { + use rand::SeedableRng; + use rand_xorshift::XorShiftRng; + let mut rng = XorShiftRng::from_seed(crate::tests::SEED); + crate::bn256::fq12::test::$test(&mut rng, $size); + } + } + }; + } + use ff::Field; + use rand::RngCore; + + use super::*; + use crate::{arith_test, frobenius_test, setup_f12_test_funcs, test}; + + arith_test!(Fq12); + + // F12 specific + setup_f12_test_funcs!(Fq12, Fq6, Fq2); + test_fq12!(f12_mul_by_014_, 500); + test_fq12!(f12_mul_by_034_, 500); + frobenius_test!(Fq12, Fq, 8); +} diff --git a/forks/halo2curves/src/bn256/fq2.rs b/forks/halo2curves/src/bn256/fq2.rs new file mode 100644 index 00000000..6201f703 --- /dev/null +++ b/forks/halo2curves/src/bn256/fq2.rs @@ -0,0 +1,100 @@ +use core::convert::TryInto; +use std::cmp::Ordering; + +use subtle::{Choice, CtOption}; + +use super::fq::Fq; +use crate::{ + ff::{Field, FromUniformBytes, PrimeField, WithSmallOrderMulGroup}, + ff_ext::{ + quadratic::{QuadExtField, QuadExtFieldArith, SQRT}, + ExtField, Legendre, + }, +}; + +crate::impl_binops_additive!(Fq2, Fq2); +crate::impl_binops_multiplicative!(Fq2, Fq2); +crate::impl_binops_calls!(Fq2); +crate::impl_sum_prod!(Fq2); +crate::impl_tower2!(Fq, Fq2); +crate::impl_tower2_from_uniform_bytes!(Fq, Fq2, 96); + +pub type Fq2 = QuadExtField; +impl QuadExtFieldArith for Fq2 { + type Base = Fq; + const SQRT: SQRT = SQRT::Algorithm9 { + q_minus_3_over_4: &[ + 0x4f082305b61f3f51, + 0x65e05aa45a1c72a3, + 0x6e14116da0605617, + 0x0c19139cb84c680a, + ], + q_minus_1_over_2: &[ + 0x9e10460b6c3e7ea3, + 0xcbc0b548b438e546, + 0xdc2822db40c0ac2e, + 0x183227397098d014, + ], + }; + + fn square_assign(el: &mut QuadExtField) { + let a = el.c0 + el.c1; + let b = el.c0 - el.c1; + let c = el.c0.double(); + el.c0 = a * b; + el.c1 = c * el.c1; + } +} + +impl ExtField for Fq2 { + const NON_RESIDUE: Self = Fq2::new(Fq::from_raw([9u64, 0, 0, 0]), Fq::ONE); + + fn mul_by_nonresidue(&self) -> Self { + // (xu+y)(u+9) = (9x+y)u+(9y-x) + let t0 = self.c0; + let t1 = self.c1; + // 8*x*i + 8*y + let t = self.double().double().double(); + Self { + // 9*y + c0: t.c0 + t0 - t1, + // (9*x + y) + c1: t.c1 + t0 + t1, + } + } + + fn frobenius_map(&mut self, power: usize) { + if power % 2 != 0 { + self.conjugate(); + } + } +} + +#[cfg(test)] +mod test { + + use rand_core::RngCore; + + use super::*; + use crate::{ + arith_test, constants_test, f2_test, frobenius_test, legendre_test, serde_test, test, + }; + + constants_test!(Fq2); + arith_test!(Fq2); + legendre_test!(Fq2); + test!(arith, Fq2, sqrt_test, 1000); + + serde_test!(Fq2); + + f2_test!(Fq2, Fq); + frobenius_test!(Fq2, Fq, 20); + + #[test] + fn test_fq2_mul_nonresidue() { + let e = Fq2::random(rand_core::OsRng); + let a0 = e.mul_by_nonresidue(); + let a1 = e * Fq2::NON_RESIDUE; + assert_eq!(a0, a1); + } +} diff --git a/forks/halo2curves/src/bn256/fq6.rs b/forks/halo2curves/src/bn256/fq6.rs new file mode 100644 index 00000000..ed3e3371 --- /dev/null +++ b/forks/halo2curves/src/bn256/fq6.rs @@ -0,0 +1,231 @@ +use ff::Field; + +use super::{fq::Fq, fq2::Fq2}; +use crate::ff_ext::{ + cubic::{CubicExtField, CubicExtFieldArith, CubicSparseMul}, + ExtField, +}; + +// -BETA is a cubic non-residue in Fp2. Fp6 = Fp2[X]/(X^3 + BETA) +// We introduce the variable v such that v^3 = -BETA +// BETA = - (u + 9) +// An element of Fq6, represented by c0 + c1 * v + c2 * v^2. +crate::impl_binops_additive!(Fq6, Fq6); +crate::impl_binops_multiplicative!(Fq6, Fq6); +crate::impl_binops_calls!(Fq6); +crate::impl_sum_prod!(Fq6); +pub type Fq6 = CubicExtField; + +impl CubicExtFieldArith for Fq6 { + type Base = Fq2; +} + +impl CubicSparseMul for Fq6 { + type Base = Fq2; +} + +impl ExtField for Fq6 { + const NON_RESIDUE: Self = Fq6::new(Fq2::ZERO, Fq2::ONE, Fq2::ZERO); + + fn frobenius_map(&mut self, power: usize) { + self.c0.frobenius_map(power); + self.c1.frobenius_map(power); + self.c2.frobenius_map(power); + self.c1.mul_assign(&FROBENIUS_COEFF_FQ6_C1[power % 6]); + self.c2.mul_assign(&FROBENIUS_COEFF_FQ6_C2[power % 6]); + } + + fn mul_by_nonresidue(self: &Fq6) -> Fq6 { + let c0 = self.c2.mul_by_nonresidue(); + let c1 = self.c0; + let c2 = self.c1; + Self { c0, c1, c2 } + } +} + +pub const FROBENIUS_COEFF_FQ6_C1: [Fq2; 6] = [ + // Fq2(u + 9)**(((q^0) - 1) / 3) + Fq2 { + c0: Fq([ + 0xd35d438dc58f0d9d, + 0x0a78eb28f5c70b3d, + 0x666ea36f7879462c, + 0x0e0a77c19a07df2f, + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0]), + }, + // Fq2(u + 9)**(((q^1) - 1) / 3) + // taken from go-ethereum and also re-calculated manually + Fq2 { + c0: Fq([ + 0xb5773b104563ab30, + 0x347f91c8a9aa6454, + 0x7a007127242e0991, + 0x1956bcd8118214ec, + ]), + c1: Fq([ + 0x6e849f1ea0aa4757, + 0xaa1c7b6d89f89141, + 0xb6e713cdfae0ca3a, + 0x26694fbb4e82ebc3, + ]), + }, + // Fq2(u + 9)**(((q^2) - 1) / 3) + // this one and other below are recalculated manually + Fq2 { + c0: Fq([ + 0x3350c88e13e80b9c, + 0x7dce557cdb5e56b9, + 0x6001b4b8b615564a, + 0x2682e617020217e0, + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0]), + }, + // Fq2(u + 9)**(((q^3) - 1) / 3) + Fq2 { + c0: Fq([ + 0xc9af22f716ad6bad, + 0xb311782a4aa662b2, + 0x19eeaf64e248c7f4, + 0x20273e77e3439f82, + ]), + c1: Fq([ + 0xacc02860f7ce93ac, + 0x3933d5817ba76b4c, + 0x69e6188b446c8467, + 0x0a46036d4417cc55, + ]), + }, + // Fq2(u + 9)**(((q^4) - 1) / 3) + Fq2 { + c0: Fq([ + 0x71930c11d782e155, + 0xa6bb947cffbe3323, + 0xaa303344d4741444, + 0x2c3b3f0d26594943, + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0]), + }, + // Fq2(u + 9)**(((q^5) - 1) / 3) + Fq2 { + c0: Fq([ + 0xf91aba2654e8e3b1, + 0x4771cb2fdc92ce12, + 0xdcb16ae0fc8bdf35, + 0x274aa195cd9d8be4, + ]), + c1: Fq([ + 0x5cfc50ae18811f8b, + 0x4bb28433cb43988c, + 0x4fd35f13c3b56219, + 0x301949bd2fc8883a, + ]), + }, +]; + +pub const FROBENIUS_COEFF_FQ6_C2: [Fq2; 6] = [ + // Fq2(u + 9)**(((2q^0) - 2) / 3) + Fq2 { + c0: Fq([ + 0xd35d438dc58f0d9d, + 0x0a78eb28f5c70b3d, + 0x666ea36f7879462c, + 0x0e0a77c19a07df2f, + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0]), + }, + // Fq2(u + 9)**(((2q^1) - 2) / 3) + Fq2 { + c0: Fq([ + 0x7361d77f843abe92, + 0xa5bb2bd3273411fb, + 0x9c941f314b3e2399, + 0x15df9cddbb9fd3ec, + ]), + c1: Fq([ + 0x5dddfd154bd8c949, + 0x62cb29a5a4445b60, + 0x37bc870a0c7dd2b9, + 0x24830a9d3171f0fd, + ]), + }, + // Fq2(u + 9)**(((2q^2) - 2) / 3) + Fq2 { + c0: Fq([ + 0x71930c11d782e155, + 0xa6bb947cffbe3323, + 0xaa303344d4741444, + 0x2c3b3f0d26594943, + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0]), + }, + // Fq2(u + 9)**(((2q^3) - 2) / 3) + Fq2 { + c0: Fq([ + 0x448a93a57b6762df, + 0xbfd62df528fdeadf, + 0xd858f5d00e9bd47a, + 0x06b03d4d3476ec58, + ]), + c1: Fq([ + 0x2b19daf4bcc936d1, + 0xa1a54e7a56f4299f, + 0xb533eee05adeaef1, + 0x170c812b84dda0b2, + ]), + }, + // Fq2(u + 9)**(((2q^4) - 2) / 3) + Fq2 { + c0: Fq([ + 0x3350c88e13e80b9c, + 0x7dce557cdb5e56b9, + 0x6001b4b8b615564a, + 0x2682e617020217e0, + ]), + c1: Fq([0x0, 0x0, 0x0, 0x0]), + }, + // Fq2(u + 9)**(((2q^5) - 2) / 3) + Fq2 { + c0: Fq([ + 0x843420f1d8dadbd6, + 0x31f010c9183fcdb2, + 0x436330b527a76049, + 0x13d47447f11adfe4, + ]), + c1: Fq([ + 0xef494023a857fa74, + 0x2a925d02d5ab101a, + 0x83b015829ba62f10, + 0x2539111d0c13aea3, + ]), + }, +]; + +#[cfg(test)] +mod test { + use rand_core::RngCore; + + use super::*; + use crate::{arith_test, frobenius_test, setup_f6_test_funcs, test}; + + macro_rules! test_fq6 { + ($test:ident, $size: expr) => { + paste::paste! { + #[test] + fn [< $test test >]() { + use rand::SeedableRng; + use rand_xorshift::XorShiftRng; + let mut rng = XorShiftRng::from_seed(crate::tests::SEED); + crate::bn256::fq6::test::$test(&mut rng, $size); + } + } + }; + } + + arith_test!(Fq6); + setup_f6_test_funcs!(Fq6, Fq2); + test_fq6!(f6_mul_nonresidue_, 1000); + test_fq6!(f6_mul_by_1_, 1000); + test_fq6!(f6_mul_by_01_, 1000); + frobenius_test!(Fq6, Fq, 10); +} diff --git a/forks/halo2curves/src/bn256/fr.rs b/forks/halo2curves/src/bn256/fr.rs new file mode 100644 index 00000000..c8bd3a0f --- /dev/null +++ b/forks/halo2curves/src/bn256/fr.rs @@ -0,0 +1,62 @@ +use core::convert::TryInto; + +use halo2derive::impl_field; +use rand::RngCore; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +impl_field!( + bn256_scalar, + Fr, + modulus = "30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001", + mul_gen = "7", + zeta = "30644e72e131a029048b6e193fd84104cc37a73fec2bc5e9b8ca0b2d36636f23", + from_uniform = [64, 48], + endian = "little", +); + +crate::extend_field_legendre!(Fr); +crate::impl_binops_calls!(Fr); +crate::impl_binops_additive!(Fr, Fr); +crate::impl_binops_multiplicative!(Fr, Fr); +crate::field_bits!(Fr); +crate::serialize_deserialize_primefield!(Fr); + +#[cfg(feature = "bn256-table")] +pub use table::FR_TABLE; +#[cfg(not(feature = "bn256-table"))] +crate::impl_from_u64!(Fr); +#[cfg(feature = "bn256-table")] +impl From for Fr { + fn from(val: u64) -> Fr { + if val < 65536 { + FR_TABLE[val as usize] + } else { + Self([val, 0, 0, 0]) * Fr::R2 + } + } +} +crate::impl_from_bool!(Fr); + +#[cfg(feature = "bn256-table")] +#[rustfmt::skip] +mod table; +#[cfg(feature = "bn256-table")] +#[cfg(test)] +mod table_tests; + +#[cfg(test)] +mod test { + use super::Fr; + use crate::{ + arith_test, constants_test, from_uniform_bytes_test, legendre_test, serde_test, test, + }; + + constants_test!(Fr); + + arith_test!(Fr); + legendre_test!(Fr); + test!(arith, Fr, sqrt_test, 1000); + + serde_test!(Fr PrimeFieldBits); + from_uniform_bytes_test!(Fr, 1000, L 64, L 48); +} diff --git a/forks/halo2curves/src/bn256/fr/table_tests.rs b/forks/halo2curves/src/bn256/fr/table_tests.rs new file mode 100644 index 00000000..6d086a65 --- /dev/null +++ b/forks/halo2curves/src/bn256/fr/table_tests.rs @@ -0,0 +1,8 @@ +use crate::bn256::{Fr, FR_TABLE}; + +#[test] +fn test_table() { + for (i, e) in FR_TABLE.iter().enumerate() { + assert_eq!(Fr::from(i as u64), *e); + } +} diff --git a/forks/halo2curves/src/bn256/mod.rs b/forks/halo2curves/src/bn256/mod.rs new file mode 100644 index 00000000..4cbbea03 --- /dev/null +++ b/forks/halo2curves/src/bn256/mod.rs @@ -0,0 +1,24 @@ +mod curve; +mod engine; +mod fq; +mod fq12; +mod fq2; +mod fq6; +mod fr; + +pub use curve::*; +pub use engine::*; +pub use fq::*; +pub use fq12::*; +pub use fq2::*; +pub use fq6::*; +pub use fr::*; + +pub const BN_X: u64 = 4965661367192848881; + +// 6U+2 for in NAF form +pub const SIX_U_PLUS_2_NAF: [i8; 65] = [ + 0, 0, 0, 1, 0, 1, 0, -1, 0, 0, 1, -1, 0, 0, 1, 0, 0, 1, 1, 0, -1, 0, 0, 1, 0, -1, 0, 0, 0, 0, + 1, 1, 1, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, -1, 0, 0, 1, 1, 0, 0, -1, 0, 0, 0, 1, 1, 0, -1, 0, + 0, 1, 0, 1, 1, +]; diff --git a/forks/halo2curves/src/curve.rs b/forks/halo2curves/src/curve.rs new file mode 100644 index 00000000..f4b1b8b2 --- /dev/null +++ b/forks/halo2curves/src/curve.rs @@ -0,0 +1,155 @@ +//! This module contains the `Curve`/`CurveAffine` abstractions that allow us to +//! write code that generalizes over a pair of groups. + +use core::ops::{Add, Mul, Sub}; + +use group::prime::{PrimeCurve, PrimeCurveAffine}; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +/// This trait is a common interface for dealing with elements of an elliptic +/// curve group in a "projective" form, where that arithmetic is usually more +/// efficient. +/// +/// Requires the `alloc` feature flag because of `hash_to_curve`. +pub trait CurveExt: + PrimeCurve::AffineExt> + + group::Group::ScalarExt> + + Default + + ConditionallySelectable + + ConstantTimeEq + + From<::Affine> +{ + /// The scalar field of this elliptic curve. + type ScalarExt: ff::WithSmallOrderMulGroup<3>; + /// The base field over which this elliptic curve is constructed. + type Base: ff::WithSmallOrderMulGroup<3>; + /// The affine version of the curve + type AffineExt: CurveAffine::ScalarExt> + + Mul + + for<'r> Mul; + + /// CURVE_ID used for hash-to-curve. + const CURVE_ID: &'static str; + + /// Apply the curve endomorphism by multiplying the x-coordinate + /// by an element of multiplicative order 3. + fn endo(&self) -> Self; + + /// Return the Jacobian coordinates of this point. + fn jacobian_coordinates(&self) -> (Self::Base, Self::Base, Self::Base); + + /// Requests a hasher that accepts messages and returns near-uniformly + /// distributed elements in the group, given domain prefix `domain_prefix`. + /// + /// This method is suitable for use as a random oracle. + #[allow(clippy::type_complexity)] + fn hash_to_curve<'a>(domain_prefix: &'a str) -> Box Self + 'a>; + + /// Returns whether or not this element is on the curve; should + /// always be true unless an "unchecked" API was used. + fn is_on_curve(&self) -> Choice; + + /// Returns the curve constant a. + fn a() -> Self::Base; + + /// Returns the curve constant b. + fn b() -> Self::Base; + + /// Obtains a point given Jacobian coordinates $X : Y : Z$, failing + /// if the coordinates are not on the curve. + fn new_jacobian(x: Self::Base, y: Self::Base, z: Self::Base) -> CtOption; +} +/// This trait is the affine counterpart to `Curve` and is used for +/// serialization, storage in memory, and inspection of $x$ and $y$ coordinates. +/// +/// Requires the `alloc` feature flag because of `hash_to_curve` on +/// [`CurveExt`]. +pub trait CurveAffine: + PrimeCurveAffine< + Scalar = ::ScalarExt, + Curve = ::CurveExt, + > + Default + + Add::Curve> + + Sub::Curve> + + ConditionallySelectable + + ConstantTimeEq + + From<::Curve> +{ + /// The scalar field of this elliptic curve. + type ScalarExt: ff::WithSmallOrderMulGroup<3> + Ord; + /// The base field over which this elliptic curve is constructed. + type Base: ff::WithSmallOrderMulGroup<3> + Ord; + /// The projective form of the curve + type CurveExt: CurveExt::ScalarExt>; + + /// Gets the coordinates of this point. + /// + /// Returns None if this is the identity. + fn coordinates(&self) -> CtOption>; + + /// Obtains a point given $(x, y)$, failing if it is not on the + /// curve. + fn from_xy(x: Self::Base, y: Self::Base) -> CtOption; + + /// Returns whether or not this element is on the curve; should + /// always be true unless an "unchecked" API was used. + fn is_on_curve(&self) -> Choice; + + /// Returns the curve constant $a$. + fn a() -> Self::Base; + + /// Returns the curve constant $b$. + fn b() -> Self::Base; +} + +/// The affine coordinates of a point on an elliptic curve. +#[derive(Clone, Copy, Debug, Default)] +pub struct Coordinates { + pub(crate) x: C::Base, + pub(crate) y: C::Base, +} + +impl Coordinates { + /// Obtains a `Coordinates` value given $(x, y)$, failing if it is not on + /// the curve. + pub fn from_xy(x: C::Base, y: C::Base) -> CtOption { + // We use CurveAffine::from_xy to validate the coordinates. + C::from_xy(x, y).map(|_| Coordinates { x, y }) + } + /// Returns the x-coordinate. + /// + /// Equivalent to `Coordinates::u`. + pub fn x(&self) -> &C::Base { + &self.x + } + + /// Returns the y-coordinate. + /// + /// Equivalent to `Coordinates::v`. + pub fn y(&self) -> &C::Base { + &self.y + } + + /// Returns the u-coordinate. + /// + /// Equivalent to `Coordinates::x`. + pub fn u(&self) -> &C::Base { + &self.x + } + + /// Returns the v-coordinate. + /// + /// Equivalent to `Coordinates::y`. + pub fn v(&self) -> &C::Base { + &self.y + } +} + +impl ConditionallySelectable for Coordinates { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + Coordinates { + x: C::Base::conditional_select(&a.x, &b.x, choice), + y: C::Base::conditional_select(&a.y, &b.y, choice), + } + } +} diff --git a/forks/halo2curves/src/derive/curve.rs b/forks/halo2curves/src/derive/curve.rs new file mode 100644 index 00000000..84f4c56f --- /dev/null +++ b/forks/halo2curves/src/derive/curve.rs @@ -0,0 +1,1190 @@ +#[macro_export] +macro_rules! endo { + ($name:ident, $field:ident, $params:expr) => { + impl CurveEndo for $name { + fn decompose_scalar(k: &$field) -> (u128, bool, u128, bool) { + let to_limbs = |e: &$field| { + let repr = e.to_repr(); + let repr = repr.as_ref(); + let tmp0 = u64::from_le_bytes(repr[0..8].try_into().unwrap()); + let tmp1 = u64::from_le_bytes(repr[8..16].try_into().unwrap()); + let tmp2 = u64::from_le_bytes(repr[16..24].try_into().unwrap()); + let tmp3 = u64::from_le_bytes(repr[24..32].try_into().unwrap()); + [tmp0, tmp1, tmp2, tmp3] + }; + + let get_lower_128 = |e: &$field| { + let e = to_limbs(e); + u128::from(e[0]) | (u128::from(e[1]) << 64) + }; + + let is_neg = |e: &$field| { + let e = to_limbs(e); + let (_, borrow) = sbb(0xffffffffffffffff, e[0], 0); + let (_, borrow) = sbb(0xffffffffffffffff, e[1], borrow); + let (_, borrow) = sbb(0xffffffffffffffff, e[2], borrow); + let (_, borrow) = sbb(0x00, e[3], borrow); + borrow & 1 != 0 + }; + + let input = to_limbs(&k); + let c1 = mul_512($params.gamma2, input); + let c2 = mul_512($params.gamma1, input); + let c1 = [c1[4], c1[5], c1[6], c1[7]]; + let c2 = [c2[4], c2[5], c2[6], c2[7]]; + let q1 = mul_512(c1, $params.b1); + let q2 = mul_512(c2, $params.b2); + let q1 = $field::from_raw([q1[0], q1[1], q1[2], q1[3]]); + let q2 = $field::from_raw([q2[0], q2[1], q2[2], q2[3]]); + let k2 = q2 - q1; + let k1 = k + k2 * $field::ZETA; + let k1_neg = is_neg(&k1); + let k2_neg = is_neg(&k2); + let k1 = if k1_neg { -k1 } else { k1 }; + let k2 = if k2_neg { -k2 } else { k2 }; + + (get_lower_128(&k1), k1_neg, get_lower_128(&k2), k2_neg) + } + } + }; +} + +#[macro_export] +macro_rules! new_curve_impl { + (($($privacy:tt)*), + $name:ident, + $name_affine:ident, + $base:ident, + $scalar:ident, + $generator:expr, + $constant_a:expr, + $constant_b:expr, + $curve_id:literal, + $hash_to_curve:expr, + $flag_config:expr, + standard_sign + ) => { + + paste::paste! { + impl $crate::serde::Compressed<$name_affine> for [<$name:upper Compressed >] { + const CONFIG: $crate::serde::CompressedFlagConfig = $flag_config; + fn sign(c: &$name_affine) -> subtle::Choice { + Choice::from(c.y.to_repr()[0] as u8 & 1) & !c.is_identity() + } + fn resolve(x: $base, sign_set: Choice) -> CtOption<$name_affine> { + $name_affine::y2(x).sqrt().map(|y| { + let y = $base::conditional_select(&y, &-y, sign_set ^ Choice::from(y.to_repr()[0] as u8 & 1)); + $name_affine { x, y } + }) + } + } + } + + + new_curve_impl!(($($privacy)*), $name, $name_affine, $base, $scalar, $generator, $constant_a, $constant_b, $curve_id, $hash_to_curve, $flag_config); + }; + + (($($privacy:tt)*), + $name:ident, + $name_affine:ident, + $base:ident, + $scalar:ident, + $generator:expr, + $constant_a:expr, + $constant_b:expr, + $curve_id:literal, + $hash_to_curve:expr, + $flag_config:expr + ) => { + + + paste::paste! { + const [< $name:upper _COMPRESSED_SIZE >]: usize = $base::SIZE + if $flag_config.has_extra_byte() { 1 } else { 0 }; + + pub type [<$name:upper Compressed >] = $crate::serde::Repr<[< $name:upper _COMPRESSED_SIZE >]>; + pub type [<$name Uncompressed >] = $crate::serde::Repr<{ 2*$base::SIZE }>; + + impl GroupEncoding for $name_affine { + type Repr = [<$name:upper Compressed >]; + + fn from_bytes(bytes: &Self::Repr) -> CtOption { + >::decode(bytes.clone()) + } + + fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { + >::decode(bytes.clone()) + } + + fn to_bytes(&self) -> Self::Repr { + >::encode(&self) + } + } + + impl GroupEncoding for $name { + type Repr = [<$name:upper Compressed >]; + + fn from_bytes(bytes: &Self::Repr) -> CtOption { + >::decode(bytes.clone()).map(Self::from) + } + + fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { + >::decode(bytes.clone()).map(Self::from) + } + + fn to_bytes(&self) -> Self::Repr { + >::encode(&self.to_affine()) + } + } + + impl group::UncompressedEncoding for $name_affine { + type Uncompressed = [<$name Uncompressed >]; + + fn from_uncompressed(bytes: &Self::Uncompressed) -> CtOption { + Self::from_uncompressed_unchecked(bytes).and_then(|p| CtOption::new(p, p.is_on_curve())) + } + + fn from_uncompressed_unchecked(bytes: &Self::Uncompressed) -> CtOption { + let mut repr = [0u8; $base::SIZE]; + + let x = { + repr.copy_from_slice(&bytes[0..$base::SIZE]); + $base::from_bytes(&repr) + }; + + let y = { + repr.copy_from_slice(&bytes[$base::SIZE..2*$base::SIZE]); + $base::from_bytes(&repr) + }; + + x.and_then(|x| { + y.and_then(|y| { + let is_identity = x.is_zero() & y.is_zero(); + let unchecked = Self { x, y }; + subtle::CtOption::new($name_affine::identity(), is_identity) + .or_else(|| subtle::CtOption::new(unchecked, Choice::from(1u8))) + }) + }) + } + + fn to_uncompressed(&self) -> Self::Uncompressed { + let mut res = Self::Uncompressed::default(); + + res[0..$base::SIZE].copy_from_slice( + &$base::conditional_select(&self.x, &$base::zero(), self.is_identity()).to_bytes()[..], + ); + + res[$base::SIZE.. 2*$base::SIZE].copy_from_slice( + &$base::conditional_select(&self.y, &$base::zero(), self.is_identity()).to_bytes()[..], + ); + + res + } + } + } + + /// A macro to help define point serialization using the [`group::GroupEncoding`] trait + /// This assumes both point types ($name, $nameaffine) implement [`group::GroupEncoding`]. + #[cfg(feature = "derive_serde")] + macro_rules! serialize_deserialize_to_from_bytes { + () => { + impl ::serde::Serialize for $name { + fn serialize(&self, serializer: S) -> Result { + let bytes = &self.to_bytes(); + if serializer.is_human_readable() { + ::hex::serde::serialize(&bytes, serializer) + } else { + ::serde_arrays::serialize(bytes.inner(), serializer) + } + } + } + + paste::paste! { + impl<'de> ::serde::Deserialize<'de> for $name { + fn deserialize>( + deserializer: D, + ) -> Result { + use ::serde::de::Error as _; + let bytes = if deserializer.is_human_readable() { + ::hex::serde::deserialize(deserializer)? + } else { + ::serde_arrays::deserialize::<_, u8, [< $name:upper _COMPRESSED_SIZE >]>(deserializer)? + }; + Option::from(Self::from_bytes(&bytes.into())).ok_or_else(|| { + D::Error::custom("deserialized bytes don't encode a valid field element") + }) + } + } + } + + impl ::serde::Serialize for $name_affine { + fn serialize(&self, serializer: S) -> Result { + let bytes = &self.to_bytes(); + if serializer.is_human_readable() { + ::hex::serde::serialize(&bytes, serializer) + } else { + ::serde_arrays::serialize(bytes.inner(), serializer) + } + } + } + + paste::paste! { + impl<'de> ::serde::Deserialize<'de> for $name_affine { + fn deserialize>( + deserializer: D, + ) -> Result { + use ::serde::de::Error as _; + let bytes = if deserializer.is_human_readable() { + ::hex::serde::deserialize(deserializer)? + } else { + ::serde_arrays::deserialize::<_, u8, [< $name:upper _COMPRESSED_SIZE >]>(deserializer)? + }; + Option::from(Self::from_bytes(&bytes.into())).ok_or_else(|| { + D::Error::custom("deserialized bytes don't encode a valid field element") + }) + } + } + } + }; + } + + #[derive(Copy, Clone, Debug)] + $($privacy)* struct $name { + pub x: $base, + pub y: $base, + pub z: $base, + } + + #[derive(Copy, Clone, PartialEq)] + $($privacy)* struct $name_affine { + pub x: $base, + pub y: $base, + } + + #[cfg(feature = "derive_serde")] + serialize_deserialize_to_from_bytes!(); + + + + impl $name { + pub fn generator() -> Self { + let generator = $name_affine::generator(); + Self { + x: generator.x, + y: generator.y, + z: $base::one(), + } + } + + #[inline] + fn curve_constant_3b() -> $base { + lazy_static::lazy_static! { + static ref CONST_3B: $base = $constant_b + $constant_b + $constant_b; + } + *CONST_3B + } + + fn mul_by_3b(input: &$base) -> $base { + if $name::CURVE_ID == "bn256_g1"{ + input.double().double().double() + input + } else { + input * $name::curve_constant_3b() + } + } + } + + impl $name_affine { + pub fn generator() -> Self { + Self { + x: $generator.0, + y: $generator.1, + } + } + + #[inline(always)] + fn y2(x: $base) -> $base { + if $constant_a == $base::ZERO { + let x3 = x.square() * x; + (x3 + $constant_b) + } else { + let x2 = x.square(); + ((x2 + $constant_a) * x + $constant_b) + } + } + + pub fn random(mut rng: impl RngCore) -> Self { + loop { + let x = $base::random(&mut rng); + let ysign = (rng.next_u32() % 2) as u8; + + let y2 = $name_affine::y2(x); + if let Some(y) = Option::<$base>::from(y2.sqrt()) { + let sign = y.to_repr()[0] & 1; + let y = if ysign ^ sign == 0 { y } else { -y }; + + let p = $name_affine { + x, + y, + }; + + + use $crate::group::cofactor::CofactorGroup; + let p = p.to_curve(); + return p.clear_cofactor().to_affine() + } + } + } + } + + + + // Jacobian implementations + + impl<'a> From<&'a $name_affine> for $name { + fn from(p: &'a $name_affine) -> $name { + p.to_curve() + } + } + + impl From<$name_affine> for $name { + fn from(p: $name_affine) -> $name { + p.to_curve() + } + } + + impl Default for $name { + fn default() -> $name { + $name::identity() + } + } + + impl subtle::ConstantTimeEq for $name { + fn ct_eq(&self, other: &Self) -> Choice { + // Is (x, y, z) equal to (x', y, z') when converted to affine? + // => (x/z , y/z) equal to (x'/z' , y'/z') + // => (xz' == x'z) & (yz' == y'z) + + let x1 = self.x * other.z; + let y1 = self.y * other.z; + + let x2 = other.x * self.z; + let y2 = other.y * self.z; + + let self_is_zero = self.is_identity(); + let other_is_zero = other.is_identity(); + + (self_is_zero & other_is_zero) // Both point at infinity + | ((!self_is_zero) & (!other_is_zero) & x1.ct_eq(&x2) & y1.ct_eq(&y2)) + // Neither point at infinity, coordinates are the same + } + + } + + impl subtle::ConditionallySelectable for $name { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + $name { + x: $base::conditional_select(&a.x, &b.x, choice), + y: $base::conditional_select(&a.y, &b.y, choice), + z: $base::conditional_select(&a.z, &b.z, choice), + } + } + } + + impl PartialEq for $name { + fn eq(&self, other: &Self) -> bool { + self.ct_eq(other).into() + } + } + + impl cmp::Eq for $name {} + + impl CurveExt for $name { + + type ScalarExt = $scalar; + type Base = $base; + type AffineExt = $name_affine; + + const CURVE_ID: &'static str = $curve_id; + + fn endo(&self) -> Self { + Self { + x: self.x * Self::Base::ZETA, + y: self.y, + z: self.z, + } + } + + fn jacobian_coordinates(&self) -> ($base, $base, $base) { + // Homogeneous to Jacobian + let x = self.x * self.z; + let y = self.y * self.z.square(); + (x, y, self.z) + } + + + #[allow(clippy::redundant_closure_call)] + fn hash_to_curve<'a>(domain_prefix: &'a str) -> Box Self + 'a> { + $hash_to_curve(domain_prefix) + } + + fn is_on_curve(&self) -> Choice { + if $constant_a == $base::ZERO { + // Check (Y/Z)^2 = (X/Z)^3 + b + // <=> Z Y^2 - X^3 = Z^3 b + + (self.z * self.y.square() - self.x.square() * self.x) + .ct_eq(&(self.z.square() * self.z * $constant_b)) + | self.z.is_zero() + } else { + // Check (Y/Z)^2 = (X/Z)^3 + a(X/Z) + b + // <=> Z Y^2 - X^3 - a(X Z^2) = Z^3 b + + let z2 = self.z.square(); + (self.z * self.y.square() - (self.x.square() + $constant_a * z2) * self.x) + .ct_eq(&(z2 * self.z * $constant_b)) + | self.z.is_zero() + } + } + + fn b() -> Self::Base { + $constant_b + } + + fn a() -> Self::Base { + $constant_a + } + + fn new_jacobian(x: Self::Base, y: Self::Base, z: Self::Base) -> CtOption { + // Jacobian to homogeneous + let z_inv = z.invert().unwrap_or($base::zero()); + let p_x = x * z_inv; + let p_y = y * z_inv.square(); + let p = $name { + x:p_x, + y:$base::conditional_select(&p_y, &$base::one(), z.is_zero()), + z + }; + CtOption::new(p, p.is_on_curve()) + } + } + + impl group::Curve for $name { + + type AffineRepr = $name_affine; + + fn batch_normalize(p: &[Self], q: &mut [Self::AffineRepr]) { + assert_eq!(p.len(), q.len()); + + let mut acc = $base::one(); + for (p, q) in p.iter().zip(q.iter_mut()) { + // We use the `x` field of $name_affine to store the product + // of previous z-coordinates seen. + q.x = acc; + + // We will end up skipping all identities in p + acc = $base::conditional_select(&(acc * p.z), &acc, p.is_identity()); + } + + // This is the inverse, as all z-coordinates are nonzero and the ones + // that are not are skipped. + acc = acc.invert().unwrap(); + + for (p, q) in p.iter().rev().zip(q.iter_mut().rev()) { + let skip = p.is_identity(); + + // Compute tmp = 1/z + let tmp = q.x * acc; + + // Cancel out z-coordinate in denominator of `acc` + acc = $base::conditional_select(&(acc * p.z), &acc, skip); + + q.x = p.x * tmp; + q.y = p.y * tmp; + + *q = $name_affine::conditional_select(&q, &$name_affine::identity(), skip); + } + } + + fn to_affine(&self) -> Self::AffineRepr { + let zinv = self.z.invert().unwrap_or($base::zero()); + let x = self.x * zinv; + let y = self.y * zinv; + let tmp = $name_affine { + x, + y, + }; + $name_affine::conditional_select(&tmp, &$name_affine::identity(), zinv.is_zero()) + } + } + + impl group::Group for $name { + type Scalar = $scalar; + + fn random(mut rng: impl RngCore) -> Self { + $name_affine::random(&mut rng).to_curve() + } + + fn double(&self) -> Self { + if $constant_a == $base::ZERO { + // Algorithm 9, https://eprint.iacr.org/2015/1060.pdf + let t0 = self.y.square(); + let z3 = t0 + t0; + let z3 = z3 + z3; + let z3 = z3 + z3; + let t1 = self.y * self.z; + let t2 = self.z.square(); + let t2 = $name::mul_by_3b(&t2); + let x3 = t2 * z3; + let y3 = t0 + t2; + let z3 = t1 * z3; + let t1 = t2 + t2; + let t2 = t1 + t2; + let t0 = t0 - t2; + let y3 = t0 * y3; + let y3 = x3 + y3; + let t1 = self.x * self.y; + let x3 = t0 * t1; + let x3 = x3 + x3; + + let tmp = $name { + x: x3, + y: y3, + z: z3, + }; + + $name::conditional_select(&tmp, &$name::identity(), self.is_identity()) + } else { + // Algorithm 3, https://eprint.iacr.org/2015/1060.pdf + let t0 = self.x.square(); + let t1 = self.y.square(); + let t2 = self.z.square(); + let t3 = self.x * self.y; + let t3 = t3 + t3; + let z3 = self.x * self.z; + let z3 = z3 + z3; + let x3 = $constant_a * z3; + let y3 = $name::mul_by_3b(&t2); + let y3 = x3 + y3; + let x3 = t1 - y3; + let y3 = t1 + y3; + let y3 = x3 * y3; + let x3 = t3 * x3; + let z3 = $name::mul_by_3b(&z3); + let t2 = $constant_a * t2; + let t3 = t0 - t2; + let t3 = $constant_a * t3; + let t3 = t3 + z3; + let z3 = t0 + t0; + let t0 = z3 + t0; + let t0 = t0 + t2; + let t0 = t0 * t3; + let y3 = y3 + t0; + let t2 = self.y * self.z; + let t2 = t2 + t2; + let t0 = t2 * t3; + let x3 = x3 - t0; + let z3 = t2 * t1; + let z3 = z3 + z3; + let z3 = z3 + z3; + + let tmp = $name { + x: x3, + y: y3, + z: z3, + }; + + $name::conditional_select(&tmp, &$name::identity(), self.is_identity()) + } + } + + fn generator() -> Self { + $name::generator() + } + + fn identity() -> Self { + Self { + x: $base::zero(), + y: $base::one(), + z: $base::zero(), + } + } + + fn is_identity(&self) -> Choice { + self.z.is_zero() + } + } + + impl $crate::serde::SerdeObject for $name { + fn from_raw_bytes_unchecked(bytes: &[u8]) -> Self { + debug_assert_eq!(bytes.len(), 3 * $base::SIZE); + let [x, y, z] = [0, 1, 2] + .map(|i| $base::from_raw_bytes_unchecked(&bytes[i * $base::SIZE..(i + 1) * $base::SIZE])); + Self { x, y, z } + } + fn from_raw_bytes(bytes: &[u8]) -> Option { + if bytes.len() != 3 * $base::SIZE { + return None; + } + let [x, y, z] = + [0, 1, 2].map(|i| $base::from_raw_bytes(&bytes[i * $base::SIZE..(i + 1) * $base::SIZE])); + x.zip(y).zip(z).and_then(|((x, y), z)| { + let res = Self { x, y, z }; + // Check that the point is on the curve. + bool::from(res.is_on_curve()).then(|| res) + }) + } + fn to_raw_bytes(&self) -> Vec { + let mut res = Vec::with_capacity(3 * $base::SIZE); + Self::write_raw(self, &mut res).unwrap(); + res + } + fn read_raw_unchecked(reader: &mut R) -> Self { + let [x, y, z] = [(); 3].map(|_| $base::read_raw_unchecked(reader)); + Self { x, y, z } + } + fn read_raw(reader: &mut R) -> std::io::Result { + let x = $base::read_raw(reader)?; + let y = $base::read_raw(reader)?; + let z = $base::read_raw(reader)?; + Ok(Self { x, y, z }) + } + fn write_raw(&self, writer: &mut W) -> std::io::Result<()> { + self.x.write_raw(writer)?; + self.y.write_raw(writer)?; + self.z.write_raw(writer) + } + } + + impl group::prime::PrimeGroup for $name {} + + impl group::prime::PrimeCurve for $name { + type Affine = $name_affine; + } + + impl group::cofactor::CofactorCurve for $name { + type Affine = $name_affine; + } + + // Affine implementations + + impl std::fmt::Debug for $name_affine { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + if self.is_identity().into() { + write!(f, "Infinity") + } else { + write!(f, "({:?}, {:?})", self.x, self.y) + } + } + } + + impl<'a> From<&'a $name> for $name_affine { + fn from(p: &'a $name) -> $name_affine { + p.to_affine() + } + } + + impl From<$name> for $name_affine { + fn from(p: $name) -> $name_affine { + p.to_affine() + } + } + + impl Default for $name_affine { + fn default() -> $name_affine { + $name_affine::identity() + } + } + + impl subtle::ConstantTimeEq for $name_affine { + fn ct_eq(&self, other: &Self) -> Choice { + let z1 = self.is_identity(); + let z2 = other.is_identity(); + + (z1 & z2) | ((!z1) & (!z2) & (self.x.ct_eq(&other.x)) & (self.y.ct_eq(&other.y))) + } + } + + impl subtle::ConditionallySelectable for $name_affine { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + $name_affine { + x: $base::conditional_select(&a.x, &b.x, choice), + y: $base::conditional_select(&a.y, &b.y, choice), + } + } + } + + impl cmp::Eq for $name_affine {} + + + impl $crate::serde::SerdeObject for $name_affine { + fn from_raw_bytes_unchecked(bytes: &[u8]) -> Self { + debug_assert_eq!(bytes.len(), 2 * $base::SIZE); + let [x, y] = + [0, $base::SIZE].map(|i| $base::from_raw_bytes_unchecked(&bytes[i..i + $base::SIZE])); + Self { x, y } + } + fn from_raw_bytes(bytes: &[u8]) -> Option { + if bytes.len() != 2 * $base::SIZE { + return None; + } + let [x, y] = [0, $base::SIZE].map(|i| $base::from_raw_bytes(&bytes[i..i + $base::SIZE])); + x.zip(y).and_then(|(x, y)| { + let res = Self { x, y }; + // Check that the point is on the curve. + bool::from(res.is_on_curve()).then(|| res) + }) + } + fn to_raw_bytes(&self) -> Vec { + let mut res = Vec::with_capacity(2 * $base::SIZE); + Self::write_raw(self, &mut res).unwrap(); + res + } + fn read_raw_unchecked(reader: &mut R) -> Self { + let [x, y] = [(); 2].map(|_| $base::read_raw_unchecked(reader)); + Self { x, y } + } + fn read_raw(reader: &mut R) -> std::io::Result { + let x = $base::read_raw(reader)?; + let y = $base::read_raw(reader)?; + Ok(Self { x, y }) + } + fn write_raw(&self, writer: &mut W) -> std::io::Result<()> { + self.x.write_raw(writer)?; + self.y.write_raw(writer) + } + } + + impl group::prime::PrimeCurveAffine for $name_affine { + type Curve = $name; + type Scalar = $scalar; + + + fn generator() -> Self { + $name_affine::generator() + } + + fn identity() -> Self { + Self { + x: $base::zero(), + y: $base::zero(), + } + } + + fn is_identity(&self) -> Choice { + self.x.is_zero() & self.y.is_zero() + } + + fn to_curve(&self) -> Self::Curve { + let tmp = $name { + x: self.x, + y: self.y, + z: $base::one(), + }; + $name::conditional_select(&tmp, &$name::identity(), self.is_identity()) + } + } + + impl group::cofactor::CofactorCurveAffine for $name_affine { + type Curve = $name; + type Scalar = $scalar; + + fn identity() -> Self { + ::identity() + } + + fn generator() -> Self { + ::generator() + } + + fn is_identity(&self) -> Choice { + ::is_identity(self) + } + + fn to_curve(&self) -> Self::Curve { + ::to_curve(self) + } + } + + + impl CurveAffine for $name_affine { + type ScalarExt = $scalar; + type Base = $base; + type CurveExt = $name; + + fn is_on_curve(&self) -> Choice { + if $constant_a == $base::ZERO { + // y^2 - x^3 ?= b + (self.y.square() - self.x.square() * self.x).ct_eq(&$constant_b) + | self.is_identity() + } else { + // y^2 - x^3 - ax ?= b + (self.y.square() - (self.x.square() + $constant_a) * self.x).ct_eq(&$constant_b) + | self.is_identity() + } + } + + fn coordinates(&self) -> CtOption> { + Coordinates::from_xy( self.x, self.y ) + } + + fn from_xy(x: Self::Base, y: Self::Base) -> CtOption { + let p = $name_affine { + x, y + }; + CtOption::new(p, p.is_on_curve()) + } + + fn a() -> Self::Base { + $constant_a + } + + fn b() -> Self::Base { + $constant_b + } + } + + + impl_binops_additive!($name, $name); + impl_binops_additive!($name, $name_affine); + impl_binops_additive_specify_output!($name_affine, $name_affine, $name); + impl_binops_additive_specify_output!($name_affine, $name, $name); + impl_binops_multiplicative!($name, $scalar); + impl_binops_multiplicative_mixed!($name_affine, $scalar, $name); + + impl<'a> Neg for &'a $name { + type Output = $name; + + fn neg(self) -> $name { + $name { + x: self.x, + y: -self.y, + z: self.z, + } + } + } + + impl Neg for $name { + type Output = $name; + + fn neg(self) -> $name { + -&self + } + } + + impl Sum for $name + where + T: core::borrow::Borrow<$name>, + { + fn sum(iter: I) -> Self + where + I: Iterator, + { + iter.fold(Self::identity(), |acc, item| acc + item.borrow()) + } + } + + impl<'a, 'b> Add<&'a $name> for &'b $name { + type Output = $name; + + fn add(self, rhs: &'a $name) -> $name { + if $constant_a == $base::ZERO { + // Algorithm 7, https://eprint.iacr.org/2015/1060.pdf + let t0 = self.x * rhs.x; + let t1 = self.y * rhs.y; + let t2 = self.z * rhs.z; + let t3 = self.x + self.y; + let t4 = rhs.x + rhs.y; + let t3 = t3 * t4; + let t4 = t0 + t1; + let t3 = t3 - t4; + let t4 = self.y + self.z; + let x3 = rhs.y + rhs.z; + let t4 = t4 * x3; + let x3 = t1 + t2; + let t4 = t4 - x3; + let x3 = self.x + self.z; + let y3 = rhs.x + rhs.z; + let x3 = x3 * y3; + let y3 = t0 + t2; + let y3 = x3 - y3; + let x3 = t0 + t0; + let t0 = x3 + t0; + let t2 = $name::mul_by_3b(&t2); + let z3 = t1 + t2; + let t1 = t1 - t2; + let y3 = $name::mul_by_3b(&y3); + let x3 = t4 * y3; + let t2 = t3 * t1; + let x3 = t2 - x3; + let y3 = y3 * t0; + let t1 = t1 * z3; + let y3 = t1 + y3; + let t0 = t0 * t3; + let z3 = z3 * t4; + let z3 = z3 + t0; + + $name { + x: x3, + y: y3, + z: z3, + } + } else { + // Algorithm 1, https://eprint.iacr.org/2015/1060.pdf + let t0 = self.x * rhs.x; + let t1 = self.y * rhs.y; + let t2 = self.z * rhs.z; + let t3 = self.x + self.y; + let t4 = rhs.x + rhs.y; + let t3 = t3 * t4; + let t4 = t0 + t1; + let t3 = t3 - t4; + let t4 = self.x + self.z; + let t5 = rhs.x + rhs.z; + let t4 = t4 * t5; + let t5 = t0 + t2; + let t4 = t4 - t5; + let t5 = self.y + self.z; + let x3 = rhs.y + rhs.z; + let t5 = t5 * x3; + let x3 = t1 + t2; + let t5 = t5 - x3; + let z3 = $constant_a * t4; + let x3 = $name::mul_by_3b(&t2); + let z3 = x3 + z3; + let x3 = t1 - z3; + let z3 = t1 + z3; + let y3 = x3 * z3; + let t1 = t0 + t0; + let t1 = t1 + t0; + let t2 = $constant_a * t2; + let t4 = $name::mul_by_3b(&t4); + let t1 = t1 + t2; + let t2 = t0 - t2; + let t2 = $constant_a * t2; + let t4 = t4 + t2; + let t0 = t1 * t4; + let y3 = y3 + t0; + let t0 = t5 * t4; + let x3 = t3 * x3; + let x3 = x3 - t0; + let t0 = t3 * t1; + let z3 = t5 * z3; + let z3 = z3 + t0; + + $name { + x: x3, + y: y3, + z: z3, + } + } + } + } + + impl<'a, 'b> Add<&'a $name_affine> for &'b $name { + type Output = $name; + + // Mixed addition + fn add(self, rhs: &'a $name_affine) -> $name { + if $constant_a == $base::ZERO { + // Algorithm 8, https://eprint.iacr.org/2015/1060.pdf + let t0 = self.x * rhs.x; + let t1 = self.y * rhs.y; + let t3 = rhs.x + rhs.y; + let t4 = self.x + self.y; + let t3 = t3 * t4; + let t4 = t0 + t1; + let t3 = t3 - t4; + let t4 = rhs.y * self.z; + let t4 = t4 + self.y; + let y3 = rhs.x * self.z; + let y3 = y3 + self.x; + let x3 = t0 + t0; + let t0 = x3 + t0; + let t2 = $name::mul_by_3b(&self.z); + let z3 = t1 + t2; + let t1 = t1 - t2; + let y3 = $name::mul_by_3b(&y3); + let x3 = t4 * y3; + let t2 = t3 * t1; + let x3 = t2 - x3; + let y3 = y3 * t0; + let t1 = t1 * z3; + let y3 = t1 + y3; + let t0 = t0 * t3; + let z3 = z3 * t4; + let z3 = z3 + t0; + + let tmp = $name{ + x: x3, + y: y3, + z: z3, + }; + + $name::conditional_select(&tmp, self, rhs.is_identity()) + } else { + // Algorithm 2, https://eprint.iacr.org/2015/1060.pdf + let t0 = self.x * rhs.x; + let t1 = self.y * rhs.y; + let t3 = rhs.x + rhs.y; + let t4 = self.x + self.y; + let t3 = t3 * t4; + let t4 = t0 + t1; + let t3 = t3 - t4; + let t4 = rhs.x * self.z; + let t4 = t4 + self.x; + let t5 = rhs.y * self.z; + let t5 = t5 + self.y; + let z3 = $constant_a * t4; + let x3 = $name::mul_by_3b(&self.z); + let z3 = x3 + z3; + let x3 = t1 - z3; + let z3 = t1 + z3; + let y3 = x3 * z3; + let t1 = t0 + t0; + let t1 = t1 + t0; + let t2 = $constant_a * self.z; + let t4 = $name::mul_by_3b(&t4); + let t1 = t1 + t2; + let t2 = t0 - t2; + let t2 = $constant_a * t2; + let t4 = t4 + t2; + let t0 = t1 * t4; + let y3 = y3 + t0; + let t0 = t5 * t4; + let x3 = t3 * x3; + let x3 = x3 - t0; + let t0 = t3 * t1; + let z3 = t5 * z3; + let z3 = z3 + t0; + + let tmp = $name{ + x: x3, + y: y3, + z: z3, + }; + + $name::conditional_select(&tmp, self, rhs.is_identity()) + } + } + } + + impl<'a, 'b> Sub<&'a $name> for &'b $name { + type Output = $name; + + fn sub(self, other: &'a $name) -> $name { + self + (-other) + } + } + + impl<'a, 'b> Sub<&'a $name_affine> for &'b $name { + type Output = $name; + + fn sub(self, other: &'a $name_affine) -> $name { + self + (-other) + } + } + + + + #[allow(clippy::suspicious_arithmetic_impl)] + impl<'a, 'b> Mul<&'b $scalar> for &'a $name { + type Output = $name; + + // This is a simple double-and-add implementation of point + // multiplication, moving from most significant to least + // significant bit of the scalar. + + fn mul(self, other: &'b $scalar) -> Self::Output { + let mut acc = $name::identity(); + for bit in other + .to_repr() + .as_ref() + .iter() + .rev() + .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) + { + acc = acc.double(); + acc = $name::conditional_select(&acc, &(acc + self), bit); + } + + acc + } + } + + impl<'a> Neg for &'a $name_affine { + type Output = $name_affine; + + fn neg(self) -> $name_affine { + $name_affine { + x: self.x, + y: -self.y, + } + } + } + + impl Neg for $name_affine { + type Output = $name_affine; + + fn neg(self) -> $name_affine { + -&self + } + } + + impl<'a, 'b> Add<&'a $name> for &'b $name_affine { + type Output = $name; + + fn add(self, rhs: &'a $name) -> $name { + rhs + self + } + } + + impl<'a, 'b> Add<&'a $name_affine> for &'b $name_affine { + type Output = $name; + + fn add(self, rhs: &'a $name_affine) -> $name { + rhs.to_curve() + self.to_curve() + } + } + + impl<'a, 'b> Sub<&'a $name_affine> for &'b $name_affine { + type Output = $name; + + fn sub(self, other: &'a $name_affine) -> $name { + self + (-other) + } + } + + impl<'a, 'b> Sub<&'a $name> for &'b $name_affine { + type Output = $name; + + fn sub(self, other: &'a $name) -> $name { + self + (-other) + } + } + + #[allow(clippy::suspicious_arithmetic_impl)] + impl<'a, 'b> Mul<&'b $scalar> for &'a $name_affine { + type Output = $name; + + fn mul(self, other: &'b $scalar) -> Self::Output { + let mut acc = $name::identity(); + + // This is a simple double-and-add implementation of point + // multiplication, moving from most significant to least + // significant bit of the scalar. + + for bit in other + .to_repr() + .as_ref() + .iter() + .rev() + .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) + { + acc = acc.double(); + acc = $name::conditional_select(&acc, &(acc + self), bit); + } + + acc + } + } + }; +} diff --git a/forks/halo2curves/src/derive/field/common.rs b/forks/halo2curves/src/derive/field/common.rs new file mode 100644 index 00000000..95c2ce64 --- /dev/null +++ b/forks/halo2curves/src/derive/field/common.rs @@ -0,0 +1,123 @@ +#[macro_export] +macro_rules! field_bits { + ($field:ident) => { + #[cfg(feature = "bits")] + #[cfg_attr(docsrs, doc(cfg(feature = "bits")))] + impl ff::PrimeFieldBits for $field { + #[cfg(target_pointer_width = "64")] + type ReprBits = [u64; Self::NUM_LIMBS]; + #[cfg(not(target_pointer_width = "64"))] + type ReprBits = [u32; Self::NUM_LIMBS * 2]; + + fn to_le_bits(&self) -> ff::FieldBits { + use ff::PrimeField; + let bytes: [u8; Self::SIZE] = self.to_repr().into(); + + #[cfg(target_pointer_width = "64")] + const STEP: usize = 8; + #[cfg(not(target_pointer_width = "64"))] + const STEP: usize = 4; + + let limbs = (0..Self::NUM_LIMBS * 8 / STEP) + .map(|off| { + #[cfg(target_pointer_width = "64")] + let limb = u64::from_le_bytes( + bytes[off * STEP..(off + 1) * STEP].try_into().unwrap(), + ); + #[cfg(not(target_pointer_width = "64"))] + let limb = u32::from_le_bytes( + bytes[off * STEP..(off + 1) * STEP].try_into().unwrap(), + ); + + limb + }) + .collect::>(); + + ff::FieldBits::new(limbs.try_into().unwrap()) + } + + fn char_le_bits() -> ff::FieldBits { + #[cfg(target_pointer_width = "64")] + let bits = ff::FieldBits::new(Self::MODULUS_LIMBS); + #[cfg(not(target_pointer_width = "64"))] + let bits = ff::FieldBits::new(Self::MODULUS_LIMBS_32); + + bits + } + } + }; +} + +#[macro_export] +macro_rules! impl_from_u64 { + ($field:ident) => { + impl From for $field { + fn from(val: u64) -> $field { + let limbs = std::iter::once(val) + .chain(std::iter::repeat(0)) + .take(Self::NUM_LIMBS) + .collect::>() + .try_into() + .unwrap(); + + $field(limbs) * Self::R2 + } + } + }; +} + +#[macro_export] +macro_rules! impl_from_bool { + ($field:ident) => { + impl From for $field { + fn from(val: bool) -> $field { + let limbs = std::iter::once(u64::from(val)) + .chain(std::iter::repeat(0)) + .take(Self::NUM_LIMBS) + .collect::>() + .try_into() + .unwrap(); + + $field(limbs) * Self::R2 + } + } + }; +} + +/// A macro to help define serialization and deserialization for prime field +/// implementations that use `$field::Repr`` representations. This assumes the +/// concerned type implements PrimeField (for from_repr, to_repr). +#[macro_export] +macro_rules! serialize_deserialize_primefield { + ($field:ident) => { + #[cfg(feature = "derive_serde")] + impl<'de> ::serde::Deserialize<'de> for $field { + fn deserialize>( + deserializer: D, + ) -> Result { + use ::serde::de::Error as _; + let bytes = if deserializer.is_human_readable() { + hex::serde::deserialize(deserializer)? + } else { + ::serde_arrays::deserialize::<_, u8, { $field::SIZE }>(deserializer)? + }; + use ff::PrimeField; + Option::from(Self::from_repr(bytes.into())).ok_or_else(|| { + D::Error::custom("deserialized bytes don't encode a valid field element") + }) + } + } + #[cfg(feature = "derive_serde")] + impl ::serde::Serialize for $field { + fn serialize(&self, serializer: S) -> Result { + use ff::PrimeField; + if serializer.is_human_readable() { + hex::serde::serialize(self.to_repr().as_ref(), serializer) + } else { + let bytes: [u8; $field::SIZE] = self.to_repr().into(); + ::serde_arrays::serialize(&bytes, serializer) + } + } + } + }; +} diff --git a/forks/halo2curves/src/derive/field/mod.rs b/forks/halo2curves/src/derive/field/mod.rs new file mode 100644 index 00000000..a9f66989 --- /dev/null +++ b/forks/halo2curves/src/derive/field/mod.rs @@ -0,0 +1,4 @@ +#[macro_use] +pub mod common; +#[macro_use] +pub mod tower; diff --git a/forks/halo2curves/src/derive/field/tower.rs b/forks/halo2curves/src/derive/field/tower.rs new file mode 100644 index 00000000..9f508fba --- /dev/null +++ b/forks/halo2curves/src/derive/field/tower.rs @@ -0,0 +1,250 @@ +#[macro_export] +macro_rules! impl_tower2 { + ( + $base:ident, + $field:ident + ) => { + impl $field { + pub const SIZE: usize = $base::SIZE * 2; + } + + impl Ord for $field { + #[inline(always)] + fn cmp(&self, other: &$field) -> Ordering { + match self.c1.cmp(&other.c1) { + Ordering::Greater => Ordering::Greater, + Ordering::Less => Ordering::Less, + Ordering::Equal => self.c0.cmp(&other.c0), + } + } + } + + impl PartialOrd for $field { + #[inline(always)] + fn partial_cmp(&self, other: &$field) -> Option { + Some(self.cmp(other)) + } + } + + impl From for $field { + fn from(val: u64) -> Self { + $field { + c0: $base::from(val), + c1: $base::ZERO, + } + } + } + + impl $field { + /// Attempts to convert a little-endian byte representation of + /// a scalar into a `$base`, failing if the input is not canonical. + pub fn from_bytes(bytes: &[u8; $base::SIZE * 2]) -> CtOption<$field> { + let c0 = $base::from_bytes(bytes[0..$base::SIZE].try_into().unwrap()); + let c1 = $base::from_bytes(bytes[$base::SIZE..$base::SIZE * 2].try_into().unwrap()); + CtOption::new( + $field { + c0: c0.unwrap(), + c1: c1.unwrap(), + }, + c0.is_some() & c1.is_some(), + ) + } + + /// Converts an element of `$base` into a byte representation in + /// little-endian byte order. + #[allow(clippy::wrong_self_convention)] + pub fn to_bytes(&self) -> [u8; $base::SIZE * 2] { + let mut res = [0u8; $base::SIZE * 2]; + let c0_bytes = self.c0.to_bytes(); + let c1_bytes = self.c1.to_bytes(); + res[0..$base::SIZE].copy_from_slice(&c0_bytes[..]); + res[$base::SIZE..$base::SIZE * 2].copy_from_slice(&c1_bytes[..]); + res + } + + #[inline] + /// Returns whether or not this element is strictly lexicographically + /// larger than its negation. + 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()) + } + } + + impl WithSmallOrderMulGroup<3> for $field { + const ZETA: Self = $field { + c0: $base::ZETA.mul_const(&$base::ZETA), + c1: $base::ZERO, + }; + } + + impl Legendre for $field { + fn legendre(&self) -> i64 { + self.norm().legendre() + } + } + + impl PrimeField for $field { + type Repr = $crate::serde::Repr<{ $base::SIZE * 2 }>; + + const MODULUS: &'static str = <$base as PrimeField>::MODULUS; + const MULTIPLICATIVE_GENERATOR: Self = $field { + c0: $base::MULTIPLICATIVE_GENERATOR, + c1: $base::ZERO, + }; + const NUM_BITS: u32 = $base::NUM_BITS; + const CAPACITY: u32 = $base::NUM_BITS; + const S: u32 = $base::S; + + // Unused variables (Because this is not a Prime Field). + // These are just used to pass the constants test. + const ROOT_OF_UNITY: Self = $field { + c0: $base::ROOT_OF_UNITY, + c1: $base::ZERO, + }; + const ROOT_OF_UNITY_INV: Self = $field { + c0: $base::ROOT_OF_UNITY_INV, + c1: $base::ZERO, + }; + + const DELTA: Self = $field { + c0: $base::DELTA, + c1: $base::ZERO, + }; + + const TWO_INV: Self = $field { + c0: $base::TWO_INV, + c1: $base::ZERO, + }; + + fn from_repr(repr: Self::Repr) -> CtOption { + let c0: [u8; $base::SIZE] = repr[..$base::SIZE].try_into().unwrap(); + let c0: <$base as PrimeField>::Repr = c0.into(); + let c0 = $base::from_repr(c0); + + let c1: [u8; $base::SIZE] = repr[$base::SIZE..].try_into().unwrap(); + let c1: <$base as PrimeField>::Repr = c1.into(); + let c1 = $base::from_repr(c1); + + CtOption::new($field::new(c0.unwrap(), c1.unwrap()), Choice::from(1)) + } + + fn to_repr(&self) -> Self::Repr { + let mut res = Self::Repr::default(); + let c0 = self.c0.to_repr(); + let c1 = self.c1.to_repr(); + res[0..$base::SIZE].copy_from_slice(&c0.as_ref()[..]); + res[$base::SIZE..$base::SIZE * 2].copy_from_slice(&c1.as_ref()[..]); + res + } + + fn is_odd(&self) -> Choice { + Choice::from(self.to_repr().as_ref()[0] & 1) + } + } + + impl $crate::serde::SerdeObject for $field { + fn from_raw_bytes_unchecked(bytes: &[u8]) -> Self { + debug_assert_eq!(bytes.len(), $base::SIZE * 2); + let [c0, c1] = [0, $base::SIZE] + .map(|i| $base::from_raw_bytes_unchecked(&bytes[i..i + $base::SIZE])); + Self { c0, c1 } + } + fn from_raw_bytes(bytes: &[u8]) -> Option { + if bytes.len() != $base::SIZE * 2 { + return None; + } + let [c0, c1] = + [0, $base::SIZE].map(|i| $base::from_raw_bytes(&bytes[i..i + $base::SIZE])); + c0.zip(c1).map(|(c0, c1)| Self { c0, c1 }) + } + fn to_raw_bytes(&self) -> Vec { + let mut res = Vec::with_capacity($base::SIZE * 2); + for limb in self.c0.0.iter().chain(self.c1.0.iter()) { + res.extend_from_slice(&limb.to_le_bytes()); + } + res + } + fn read_raw_unchecked(reader: &mut R) -> Self { + let [c0, c1] = [(); 2].map(|_| $base::read_raw_unchecked(reader)); + Self { c0, c1 } + } + fn read_raw(reader: &mut R) -> std::io::Result { + let c0 = $base::read_raw(reader)?; + let c1 = $base::read_raw(reader)?; + Ok(Self { c0, c1 }) + } + fn write_raw(&self, writer: &mut W) -> std::io::Result<()> { + self.c0.write_raw(writer)?; + self.c1.write_raw(writer) + } + } + }; +} + +#[macro_export] +macro_rules! impl_tower2_from_uniform_bytes { + ( + $base:ident, + $field:ident, + $size:expr + ) => { + impl FromUniformBytes<{ $size }> for $field { + fn from_uniform_bytes(bytes: &[u8; $size]) -> Self { + assert!($size % 2 == 0); + const SIZE: usize = $size / 2; + let c0: [u8; SIZE] = bytes[SIZE..].try_into().unwrap(); + let c1: [u8; SIZE] = bytes[..SIZE].try_into().unwrap(); + Self::new( + $base::from_uniform_bytes(&c0), + $base::from_uniform_bytes(&c1), + ) + } + } + }; +} + +#[macro_export] +macro_rules! impl_cyclotomic_square { + ( + $tower2:ident, + $tower12:ident + ) => { + impl $tower12 { + pub fn cyclotomic_square(&mut self) { + fn fp4_square(c0: &mut $tower2, c1: &mut $tower2, a0: &$tower2, a1: &$tower2) { + use ff::Field; + let t0 = a0.square(); + let t1 = a1.square(); + *c0 = t1.mul_by_nonresidue() + t0; + *c1 = (a0 + a1).square() - t0 - t1; + } + + let mut t3 = $tower2::zero(); + let mut t4 = $tower2::zero(); + let mut t5 = $tower2::zero(); + let mut t6 = $tower2::zero(); + fp4_square(&mut t3, &mut t4, &self.c0.c0, &self.c1.c1); + + self.c0.c0 = (t3 - self.c0.c0).double() + t3; + self.c1.c1 = (t4 + self.c1.c1).double() + t4; + + fp4_square(&mut t3, &mut t4, &self.c1.c0, &self.c0.c2); + fp4_square(&mut t5, &mut t6, &self.c0.c1, &self.c1.c2); + + self.c0.c1 = (t3 - self.c0.c1).double() + t3; + self.c1.c2 = (t4 + self.c1.c2).double() + t4; + + let t3 = t6.mul_by_nonresidue(); + self.c1.c0 = (t3 + self.c1.c0).double() + t3; + self.c0.c2 = (t5 - self.c0.c2).double() + t5; + } + } + }; +} diff --git a/forks/halo2curves/src/derive/mod.rs b/forks/halo2curves/src/derive/mod.rs new file mode 100644 index 00000000..29535218 --- /dev/null +++ b/forks/halo2curves/src/derive/mod.rs @@ -0,0 +1,233 @@ +#[macro_use] +pub mod curve; +#[macro_use] +pub mod field; +#[macro_use] +pub mod pairing; + +#[macro_export] +macro_rules! impl_binops_calls { + ($field:ident) => { + impl ::core::ops::Neg for $field { + type Output = $field; + + #[inline] + fn neg(self) -> $field { + -&self + } + } + + impl<'a> ::core::ops::Neg for &'a $field { + type Output = $field; + + #[inline] + fn neg(self) -> $field { + self.neg() + } + } + + impl<'a, 'b> ::core::ops::Sub<&'b $field> for &'a $field { + type Output = $field; + + #[inline] + fn sub(self, rhs: &'b $field) -> $field { + self.sub(rhs) + } + } + + impl<'a, 'b> ::core::ops::Add<&'b $field> for &'a $field { + type Output = $field; + + #[inline] + fn add(self, rhs: &'b $field) -> $field { + self.add(rhs) + } + } + + impl<'a, 'b> ::core::ops::Mul<&'b $field> for &'a $field { + type Output = $field; + + #[inline] + fn mul(self, rhs: &'b $field) -> $field { + self.mul(rhs) + } + } + }; +} + +#[macro_export] +macro_rules! impl_add_binop_specify_output { + ($lhs:ident, $rhs:ident, $output:ident) => { + impl<'b> ::core::ops::Add<&'b $rhs> for $lhs { + type Output = $output; + + #[inline] + fn add(self, rhs: &'b $rhs) -> $output { + &self + rhs + } + } + + impl<'a> ::core::ops::Add<$rhs> for &'a $lhs { + type Output = $output; + + #[inline] + fn add(self, rhs: $rhs) -> $output { + self + &rhs + } + } + + impl ::core::ops::Add<$rhs> for $lhs { + type Output = $output; + + #[inline] + fn add(self, rhs: $rhs) -> $output { + &self + &rhs + } + } + }; +} + +#[macro_export] +macro_rules! impl_sub_binop_specify_output { + ($lhs:ident, $rhs:ident, $output:ident) => { + impl<'b> ::core::ops::Sub<&'b $rhs> for $lhs { + type Output = $output; + + #[inline] + fn sub(self, rhs: &'b $rhs) -> $output { + &self - rhs + } + } + + impl<'a> ::core::ops::Sub<$rhs> for &'a $lhs { + type Output = $output; + + #[inline] + fn sub(self, rhs: $rhs) -> $output { + self - &rhs + } + } + + impl ::core::ops::Sub<$rhs> for $lhs { + type Output = $output; + + #[inline] + fn sub(self, rhs: $rhs) -> $output { + &self - &rhs + } + } + }; +} + +#[macro_export] +macro_rules! impl_binops_additive_specify_output { + ($lhs:ident, $rhs:ident, $output:ident) => { + $crate::impl_add_binop_specify_output!($lhs, $rhs, $output); + $crate::impl_sub_binop_specify_output!($lhs, $rhs, $output); + }; +} + +#[macro_export] +macro_rules! impl_binops_multiplicative_mixed { + ($lhs:ident, $rhs:ident, $output:ident) => { + impl<'b> ::core::ops::Mul<&'b $rhs> for $lhs { + type Output = $output; + + #[inline] + fn mul(self, rhs: &'b $rhs) -> $output { + &self * rhs + } + } + + impl<'a> ::core::ops::Mul<$rhs> for &'a $lhs { + type Output = $output; + + #[inline] + fn mul(self, rhs: $rhs) -> $output { + self * &rhs + } + } + + impl ::core::ops::Mul<$rhs> for $lhs { + type Output = $output; + + #[inline] + fn mul(self, rhs: $rhs) -> $output { + &self * &rhs + } + } + }; +} + +#[macro_export] +macro_rules! impl_binops_additive { + ($lhs:ident, $rhs:ident) => { + $crate::impl_binops_additive_specify_output!($lhs, $rhs, $lhs); + + impl ::core::ops::SubAssign<$rhs> for $lhs { + #[inline] + fn sub_assign(&mut self, rhs: $rhs) { + *self = &*self - &rhs; + } + } + + impl ::core::ops::AddAssign<$rhs> for $lhs { + #[inline] + fn add_assign(&mut self, rhs: $rhs) { + *self = &*self + &rhs; + } + } + + impl<'b> ::core::ops::SubAssign<&'b $rhs> for $lhs { + #[inline] + fn sub_assign(&mut self, rhs: &'b $rhs) { + *self = &*self - rhs; + } + } + + impl<'b> ::core::ops::AddAssign<&'b $rhs> for $lhs { + #[inline] + fn add_assign(&mut self, rhs: &'b $rhs) { + *self = &*self + rhs; + } + } + }; +} + +#[macro_export] +macro_rules! impl_binops_multiplicative { + ($lhs:ident, $rhs:ident) => { + $crate::impl_binops_multiplicative_mixed!($lhs, $rhs, $lhs); + + impl ::core::ops::MulAssign<$rhs> for $lhs { + #[inline] + fn mul_assign(&mut self, rhs: $rhs) { + *self = &*self * &rhs; + } + } + + impl<'b> ::core::ops::MulAssign<&'b $rhs> for $lhs { + #[inline] + fn mul_assign(&mut self, rhs: &'b $rhs) { + *self = &*self * rhs; + } + } + }; +} + +#[macro_export] +macro_rules! impl_sum_prod { + ($f:ident) => { + impl> ::core::iter::Sum for $f { + fn sum>(iter: I) -> Self { + iter.fold(Self::zero(), |acc, item| acc + item.borrow()) + } + } + + impl> ::core::iter::Product for $f { + fn product>(iter: I) -> Self { + iter.fold(Self::one(), |acc, item| acc * item.borrow()) + } + } + }; +} diff --git a/forks/halo2curves/src/derive/pairing.rs b/forks/halo2curves/src/derive/pairing.rs new file mode 100644 index 00000000..7f672e59 --- /dev/null +++ b/forks/halo2curves/src/derive/pairing.rs @@ -0,0 +1,267 @@ +#[macro_export] +macro_rules! impl_miller_loop_components { + ( + $engine:ident, + $g1:ident, + $g1affine:ident, + $g2:ident, + $g2affine:ident, + $base:ident, + $target:ident, + $scalar:ident + ) => { + #[derive(Clone, Debug)] + pub struct $engine; + + impl Engine for $engine { + type Fr = $scalar; + type G1 = $g1; + type G1Affine = $g1affine; + type G2 = $g2; + type G2Affine = $g2affine; + type Gt = Gt; + + fn pairing(p: &Self::G1Affine, q: &Self::G2Affine) -> Self::Gt { + $engine::multi_miller_loop(&[(p, q)]).final_exponentiation() + } + } + + impl MultiMillerLoop for $engine { + type G2Prepared = $g2affine; + type Result = $base; + + fn multi_miller_loop(terms: &[(&Self::G1Affine, &Self::G2Prepared)]) -> Self::Result { + multi_miller_loop(terms) + } + } + + impl PairingCurveAffine for $g1affine { + type Pair = $g2affine; + type PairingResult = $target; + + fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult { + $engine::pairing(&self, &other) + } + } + + impl PairingCurveAffine for $g2affine { + type Pair = $g1affine; + type PairingResult = $target; + + fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult { + $engine::pairing(&other, &self) + } + } + + fn double(f: &mut $base, r: &mut $g2, p: &$g1affine) { + use ff::Field; + let t0 = r.x.square(); + let t1 = r.y.square(); + let t2 = t1.square(); + let t3 = (t1 + r.x).square() - t0 - t2; + let t3 = t3 + t3; + let t4 = t0 + t0 + t0; + let t6 = r.x + t4; + let t5 = t4.square(); + let zsquared = r.z.square(); + r.x = t5 - t3 - t3; + r.z = (r.z + r.y).square() - t1 - zsquared; + r.y = (t3 - r.x) * t4; + let t2 = t2 + t2; + let t2 = t2 + t2; + let t2 = t2 + t2; + r.y -= t2; + let t3 = t4 * zsquared; + let t3 = t3 + t3; + let t3 = -t3; + let t6 = t6.square() - t0 - t5; + let t1 = t1 + t1; + let t1 = t1 + t1; + let t6 = t6 - t1; + let t0 = r.z * zsquared; + let t0 = t0 + t0; + + ell(f, &(t0, t3, t6), p); + } + + fn add(f: &mut $base, r: &mut $g2, q: &$g2affine, p: &$g1affine) { + use ff::Field; + 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; + + ell(f, &(t10, t1, t9), p); + } + }; +} + +#[macro_export] +macro_rules! impl_gt { + ( + $target:ident, + $base:ident, + $scalar:ident + ) => { + #[derive(Copy, Clone, Debug, Default)] + pub struct $target(pub(crate) $base); + + impl ConstantTimeEq for $target { + fn ct_eq(&self, other: &Self) -> Choice { + self.0.ct_eq(&other.0) + } + } + + impl ConditionallySelectable for $target { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + $target($base::conditional_select(&a.0, &b.0, choice)) + } + } + + impl Eq for $target {} + impl PartialEq for $target { + #[inline] + fn eq(&self, other: &Self) -> bool { + bool::from(self.ct_eq(other)) + } + } + + impl $target { + /// Returns the group identity, which is $1$. + pub fn identity() -> $target { + $target($base::one()) + } + + /// Doubles this group element. + pub fn double(&self) -> $target { + use ff::Field; + $target(self.0.square()) + } + } + + impl<'a> Neg for &'a $target { + type Output = $target; + + #[inline] + fn neg(self) -> $target { + // The element is unitary, so we just conjugate. + let mut u = self.0; + u.conjugate(); + $target(u) + } + } + + impl Neg for $target { + type Output = $target; + + #[inline] + fn neg(self) -> $target { + -&self + } + } + + impl<'a, 'b> Add<&'b $target> for &'a $target { + type Output = $target; + + #[inline] + #[allow(clippy::suspicious_arithmetic_impl)] + fn add(self, rhs: &'b $target) -> $target { + $target(self.0 * rhs.0) + } + } + + impl<'a, 'b> Sub<&'b $target> for &'a $target { + type Output = $target; + + #[inline] + fn sub(self, rhs: &'b $target) -> $target { + self + (-rhs) + } + } + + #[allow(clippy::suspicious_arithmetic_impl)] + impl<'a, 'b> Mul<&'b $scalar> for &'a $target { + type Output = $target; + + fn mul(self, other: &'b $scalar) -> Self::Output { + let mut acc = $target::identity(); + + for bit in other + .to_repr() + .as_ref() + .iter() + .rev() + .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) + .skip(1) + { + acc = acc.double(); + acc = $target::conditional_select(&acc, &(acc + self), bit); + } + + acc + } + } + + $crate::impl_binops_additive!($target, $target); + $crate::impl_binops_multiplicative!($target, $scalar); + + impl Sum for $target + where + T: Borrow<$target>, + { + fn sum(iter: I) -> Self + where + I: Iterator, + { + iter.fold(Self::identity(), |acc, item| acc + item.borrow()) + } + } + + impl Group for $target { + type Scalar = $scalar; + + fn random(rng: impl RngCore) -> Self { + use ff::Field; + $base::random(rng).final_exponentiation() + } + + fn identity() -> Self { + Self::identity() + } + + fn generator() -> Self { + unimplemented!() + } + + fn is_identity(&self) -> Choice { + self.ct_eq(&Self::identity()) + } + + fn double(&self) -> Self { + self.double() + } + } + }; +} diff --git a/forks/halo2curves/src/ff_ext/cubic.rs b/forks/halo2curves/src/ff_ext/cubic.rs new file mode 100644 index 00000000..671f3250 --- /dev/null +++ b/forks/halo2curves/src/ff_ext/cubic.rs @@ -0,0 +1,255 @@ +use super::ExtField; + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub struct CubicExtField { + pub(crate) c0: F, + pub(crate) c1: F, + pub(crate) c2: F, +} + +pub trait CubicSparseMul { + type Base: ExtField; + + #[must_use] + fn mul_by_1(lhs: &CubicExtField, c1: &Self::Base) -> CubicExtField { + let b_b = lhs.c1 * c1; + + let t1 = (lhs.c1 + lhs.c2) * c1 - b_b; + let t1 = t1.mul_by_nonresidue(); + let t2 = (lhs.c0 + lhs.c1) * c1 - b_b; + + CubicExtField { + c0: t1, + c1: t2, + c2: b_b, + } + } + + #[must_use] + fn mul_by_01( + lhs: &CubicExtField, + c0: &Self::Base, + c1: &Self::Base, + ) -> CubicExtField { + let a_a = lhs.c0 * c0; + let b_b = lhs.c1 * c1; + + let t1 = *c1 * (lhs.c1 + lhs.c2) - b_b; + let t1 = a_a + t1.mul_by_nonresidue(); + let t3 = *c0 * (lhs.c0 + lhs.c2) - a_a + b_b; + let t2 = (*c0 + c1) * (lhs.c0 + lhs.c1) - a_a - b_b; + + CubicExtField { + c0: t1, + c1: t2, + c2: t3, + } + } +} + +pub trait CubicExtFieldArith { + type Base: ExtField; + + fn mul_assign(lhs: &mut CubicExtField, rhs: &CubicExtField) { + let a_a = lhs.c0 * rhs.c0; + let b_b = lhs.c1 * rhs.c1; + let c_c = lhs.c2 * rhs.c2; + + let t1 = (rhs.c1 + rhs.c2) * (lhs.c1 + lhs.c2) - (c_c + b_b); + + let t1 = a_a + t1.mul_by_nonresidue(); + + let t3 = (rhs.c0 + rhs.c2) * (lhs.c0 + lhs.c2) - (a_a - b_b + c_c); + + let t2 = (rhs.c0 + rhs.c1) * (lhs.c0 + lhs.c1) - (a_a + b_b); + let t2 = t2 + c_c.mul_by_nonresidue(); + + lhs.c0 = t1; + lhs.c1 = t2; + lhs.c2 = t3; + } + + fn square_assign(el: &mut CubicExtField) { + use ff::Field; + + let s0 = el.c0.square(); + let s1 = (el.c0 * el.c1).double(); + let s2 = (el.c0 - el.c1 + el.c2).square(); + let s3 = (el.c1 * el.c2).double(); + let s4 = el.c2.square(); + + el.c0 = s3.mul_by_nonresidue() + s0; + el.c1 = s4.mul_by_nonresidue() + s1; + el.c2 = s1 + s2 + s3 - s0 - s4; + } +} + +impl CubicExtField { + #[inline] + pub const fn new(c0: F, c1: F, c2: F) -> Self { + Self { c0, c1, c2 } + } + + #[inline] + pub const fn zero() -> Self { + Self { + c0: F::ZERO, + c1: F::ZERO, + c2: F::ZERO, + } + } + + #[inline] + pub const fn one() -> Self { + Self { + c0: F::ONE, + c1: F::ZERO, + c2: F::ZERO, + } + } + + #[inline] + pub fn c0(&self) -> &F { + &self.c0 + } + + #[inline] + pub fn c1(&self) -> &F { + &self.c1 + } + + #[inline] + pub fn c2(&self) -> &F { + &self.c2 + } + + #[inline] + pub fn double(&self) -> Self { + Self { + c0: self.c0.double(), + c1: self.c1.double(), + c2: self.c2.double(), + } + } + + #[inline] + pub fn add(&self, other: &Self) -> Self { + Self { + c0: self.c0 + other.c0, + c1: self.c1 + other.c1, + c2: self.c2 + other.c2, + } + } + + #[inline] + pub fn sub(&self, other: &Self) -> Self { + Self { + c0: self.c0 - other.c0, + c1: self.c1 - other.c1, + c2: self.c2 - other.c2, + } + } + + #[inline] + pub fn neg(&self) -> Self { + Self { + c0: -self.c0, + c1: -self.c1, + c2: -self.c2, + } + } +} + +impl CubicExtField +where + Self: CubicExtFieldArith, +{ + pub fn mul(&self, rhs: &Self) -> Self { + let mut lhs = *self; + Self::mul_assign(&mut lhs, rhs); + lhs + } + + pub fn mul_assign(&mut self, rhs: &Self) { + ::mul_assign(self, rhs); + } + + pub fn square(el: &Self) -> Self { + let mut el = *el; + Self::square_assign(&mut el); + el + } + + pub fn square_assign(&mut self) { + ::square_assign(self); + } +} + +impl ff::Field for CubicExtField +where + CubicExtField: CubicExtFieldArith + ExtField, /* kind of cyclic being + * `ExtField: Field` but it seems + * alright */ +{ + const ZERO: Self = Self::zero(); + const ONE: Self = Self::one(); + + fn random(mut rng: impl rand_core::RngCore) -> Self { + Self::new( + F::random(&mut rng), + F::random(&mut rng), + F::random(&mut rng), + ) + } + + fn is_zero(&self) -> subtle::Choice { + self.c0.is_zero() & self.c1.is_zero() + } + + fn square(&self) -> Self { + CubicExtField::square(self) + } + + fn double(&self) -> Self { + self.double() + } + + fn sqrt(&self) -> subtle::CtOption { + unimplemented!() + } + + fn sqrt_ratio(_: &Self, _: &Self) -> (subtle::Choice, Self) { + unimplemented!() + } + + fn invert(&self) -> subtle::CtOption { + let c0 = self.c2.mul_by_nonresidue() * self.c1.neg() + self.c0.square(); + let c1 = self.c2.square().mul_by_nonresidue() - (self.c0 * self.c1); + let c2 = self.c1.square() - (self.c0 * self.c2); + + let t = (self.c2 * c1) + (self.c1 * c2); + let t = t.mul_by_nonresidue() + (self.c0 * c0); + + t.invert().map(|t| Self { + c0: t * c0, + c1: t * c1, + c2: t * c2, + }) + } +} + +impl subtle::ConditionallySelectable for CubicExtField { + fn conditional_select(a: &Self, b: &Self, choice: subtle::Choice) -> Self { + CubicExtField { + c0: F::conditional_select(&a.c0, &b.c0, choice), + c1: F::conditional_select(&a.c1, &b.c1, choice), + c2: F::conditional_select(&a.c2, &b.c2, choice), + } + } +} + +impl subtle::ConstantTimeEq for CubicExtField { + fn ct_eq(&self, other: &Self) -> subtle::Choice { + self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1) & self.c2.ct_eq(&other.c2) + } +} diff --git a/forks/halo2curves/src/ff_ext/inverse.rs b/forks/halo2curves/src/ff_ext/inverse.rs new file mode 100644 index 00000000..95a5088a --- /dev/null +++ b/forks/halo2curves/src/ff_ext/inverse.rs @@ -0,0 +1,439 @@ +use core::cmp::PartialEq; +use std::ops::{Add, Mul, Neg, Sub}; + +/// Big signed (B * L)-bit integer type, whose variables store +/// numbers in the two's complement code as arrays of B-bit chunks. +/// The ordering of the chunks in these arrays is little-endian. +/// The arithmetic operations for this type are wrapping ones. +#[derive(Clone)] +struct CInt(pub [u64; L]); + +impl CInt { + /// Mask, in which the B lowest bits are 1 and only they + pub const MASK: u64 = u64::MAX >> (64 - B); + + /// Representation of -1 + pub const MINUS_ONE: Self = Self([Self::MASK; L]); + + /// Representation of 0 + pub const ZERO: Self = Self([0; L]); + + /// Representation of 1 + pub const ONE: Self = { + let mut data = [0; L]; + data[0] = 1; + Self(data) + }; + + /// Returns the result of applying B-bit right + /// arithmetical shift to the current number + pub fn shift(&self) -> Self { + let mut data = [0; L]; + if self.is_negative() { + data[L - 1] = Self::MASK; + } + data[..L - 1].copy_from_slice(&self.0[1..]); + Self(data) + } + + /// Returns the lowest B bits of the current number + pub fn lowest(&self) -> u64 { + self.0[0] + } + + /// Returns "true" iff the current number is negative + pub fn is_negative(&self) -> bool { + self.0[L - 1] > (Self::MASK >> 1) + } +} + +impl PartialEq for CInt { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl Add for &CInt { + type Output = CInt; + fn add(self, other: Self) -> Self::Output { + let (mut data, mut carry) = ([0; L], 0); + for (i, d) in data.iter_mut().enumerate().take(L) { + let sum = self.0[i] + other.0[i] + carry; + *d = sum & CInt::::MASK; + carry = sum >> B; + } + CInt::(data) + } +} + +impl Add<&CInt> for CInt { + type Output = CInt; + fn add(self, other: &Self) -> Self::Output { + &self + other + } +} + +impl Add for CInt { + type Output = CInt; + fn add(self, other: Self) -> Self::Output { + &self + &other + } +} + +impl Sub for &CInt { + type Output = CInt; + fn sub(self, other: Self) -> Self::Output { + // For the two's complement code the additive negation is the result of + // adding 1 to the bitwise inverted argument's representation. Thus, for + // any encoded integers x and y we have x - y = x + !y + 1, where "!" is + // the bitwise inversion and addition is done according to the rules of + // the code. The algorithm below uses this formula and is the modified + // addition algorithm, where the carry flag is initialized with 1 and + // the chunks of the second argument are bitwise inverted + let (mut data, mut carry) = ([0; L], 1); + for (i, d) in data.iter_mut().enumerate().take(L) { + let sum = self.0[i] + (other.0[i] ^ CInt::::MASK) + carry; + *d = sum & CInt::::MASK; + carry = sum >> B; + } + CInt::(data) + } +} + +impl Sub<&CInt> for CInt { + type Output = CInt; + fn sub(self, other: &Self) -> Self::Output { + &self - other + } +} + +impl Sub for CInt { + type Output = CInt; + fn sub(self, other: Self) -> Self::Output { + &self - &other + } +} + +impl Neg for &CInt { + type Output = CInt; + fn neg(self) -> Self::Output { + // For the two's complement code the additive negation is the result + // of adding 1 to the bitwise inverted argument's representation + let (mut data, mut carry) = ([0; L], 1); + for (i, d) in data.iter_mut().enumerate().take(L) { + let sum = (self.0[i] ^ CInt::::MASK) + carry; + *d = sum & CInt::::MASK; + carry = sum >> B; + } + CInt::(data) + } +} + +impl Neg for CInt { + type Output = CInt; + fn neg(self) -> Self::Output { + -&self + } +} + +impl Mul for &CInt { + type Output = CInt; + fn mul(self, other: Self) -> Self::Output { + let mut data = [0; L]; + for i in 0..L { + let mut carry = 0; + for k in 0..(L - i) { + let sum = (data[i + k] as u128) + + (carry as u128) + + (self.0[i] as u128) * (other.0[k] as u128); + data[i + k] = sum as u64 & CInt::::MASK; + carry = (sum >> B) as u64; + } + } + CInt::(data) + } +} + +impl Mul<&CInt> for CInt { + type Output = CInt; + fn mul(self, other: &Self) -> Self::Output { + &self * other + } +} + +impl Mul for CInt { + type Output = CInt; + fn mul(self, other: Self) -> Self::Output { + &self * &other + } +} + +impl Mul for &CInt { + type Output = CInt; + fn mul(self, other: i64) -> Self::Output { + let mut data = [0; L]; + // If the short multiplicand is non-negative, the standard multiplication + // algorithm is performed. Otherwise, the product of the additively negated + // multiplicands is found as follows. Since for the two's complement code + // the additive negation is the result of adding 1 to the bitwise inverted + // argument's representation, for any encoded integers x and y we have + // x * y = (-x) * (-y) = (!x + 1) * (-y) = !x * (-y) + (-y), where "!" is + // the bitwise inversion and arithmetic operations are performed according + // to the rules of the code. If the short multiplicand is negative, the + // algorithm below uses this formula by substituting the short multiplicand + // for y and turns into the modified standard multiplication algorithm, + // where the carry flag is initialized with the additively negated short + // multiplicand and the chunks of the long multiplicand are bitwise inverted + let (other, mut carry, mask) = if other < 0 { + (-other, -other as u64, CInt::::MASK) + } else { + (other, 0, 0) + }; + for (i, d) in data.iter_mut().enumerate().take(L) { + let sum = (carry as u128) + ((self.0[i] ^ mask) as u128) * (other as u128); + *d = sum as u64 & CInt::::MASK; + carry = (sum >> B) as u64; + } + CInt::(data) + } +} + +impl Mul for CInt { + type Output = CInt; + fn mul(self, other: i64) -> Self::Output { + &self * other + } +} + +impl Mul<&CInt> for i64 { + type Output = CInt; + fn mul(self, other: &CInt) -> Self::Output { + other * self + } +} + +impl Mul> for i64 { + type Output = CInt; + fn mul(self, other: CInt) -> Self::Output { + other * self + } +} + +/// Type of the modular multiplicative inverter based on the Bernstein-Yang +/// method. The inverter can be created for a specified modulus M and adjusting +/// parameter A to compute the adjusted multiplicative inverses of positive +/// integers, i.e. for computing (1 / x) * A (mod M) for a positive integer x. +/// +/// The adjusting parameter allows computing the multiplicative inverses in the +/// case of using the Montgomery representation for the input or the expected +/// output. If R is the Montgomery factor, the multiplicative inverses in the +/// appropriate representation can be computed provided that the value of A is +/// chosen as follows: +/// - A = 1, if both the input and the expected output are in the standard form +/// - A = R^2 mod M, if both the input and the expected output are in the +/// Montgomery form +/// - A = R mod M, if either the input or the expected output is in the +/// Montgomery form, +/// but not both of them +/// +/// The public methods of this type receive and return unsigned big integers as +/// arrays of 64-bit chunks, the ordering of which is little-endian. Both the +/// modulus and the integer to be inverted should not exceed 2 ^ (62 * L - 64) +/// +/// For better understanding the implementation, the following resources are +/// recommended: +/// - D. Bernstein, B.-Y. Yang, "Fast constant-time gcd computation and modular +/// inversion", +/// +/// - P. Wuille, "The safegcd implementation in libsecp256k1 explained", +/// +pub struct BYInverter { + /// Modulus + modulus: CInt<62, L>, + + /// Adjusting parameter + adjuster: CInt<62, L>, + + /// Multiplicative inverse of the modulus modulo 2^62 + inverse: i64, +} + +/// Type of the Bernstein-Yang transition matrix multiplied by 2^62 +type Matrix = [[i64; 2]; 2]; + +impl BYInverter { + /// Returns the Bernstein-Yang transition matrix multiplied by 2^62 and the + /// new value of the delta variable for the 62 basic steps of the + /// Bernstein-Yang method, which are to be performed sequentially for + /// specified initial values of f, g and delta + fn jump(f: &CInt<62, L>, g: &CInt<62, L>, mut delta: i64) -> (i64, Matrix) { + let (mut steps, mut f, mut g) = (62, f.lowest() as i64, g.lowest() as i128); + let mut t: Matrix = [[1, 0], [0, 1]]; + + loop { + let zeros = steps.min(g.trailing_zeros() as i64); + (steps, delta, g) = (steps - zeros, delta + zeros, g >> zeros); + t[0] = [t[0][0] << zeros, t[0][1] << zeros]; + + if steps == 0 { + break; + } + if delta > 0 { + (delta, f, g) = (-delta, g as i64, -f as i128); + (t[0], t[1]) = (t[1], [-t[0][0], -t[0][1]]); + } + + // The formula (3 * x) xor 28 = -1 / x (mod 32) for an odd integer x + // in the two's complement code has been derived from the formula + // (3 * x) xor 2 = 1 / x (mod 32) attributed to Peter Montgomery + let mask = (1 << steps.min(1 - delta).min(5)) - 1; + let w = (g as i64).wrapping_mul(f.wrapping_mul(3) ^ 28) & mask; + + t[1] = [t[0][0] * w + t[1][0], t[0][1] * w + t[1][1]]; + g += w as i128 * f as i128; + } + + (delta, t) + } + + /// Returns the updated values of the variables f and g for specified + /// initial ones and Bernstein-Yang transition matrix multiplied by + /// 2^62. The returned vector is "matrix * (f, g)' / 2^62", where "'" is the + /// transpose operator + fn fg(f: CInt<62, L>, g: CInt<62, L>, t: Matrix) -> (CInt<62, L>, CInt<62, L>) { + ( + (t[0][0] * &f + t[0][1] * &g).shift(), + (t[1][0] * &f + t[1][1] * &g).shift(), + ) + } + + /// Returns the updated values of the variables d and e for specified + /// initial ones and Bernstein-Yang transition matrix multiplied by + /// 2^62. The returned vector is congruent modulo M to "matrix * (d, e)' / + /// 2^62 (mod M)", where M is the modulus the inverter was created for + /// and "'" stands for the transpose operator. Both the input and output + /// values lie in the interval (-2 * M, M) + fn de(&self, d: CInt<62, L>, e: CInt<62, L>, t: Matrix) -> (CInt<62, L>, CInt<62, L>) { + let mask = CInt::<62, L>::MASK as i64; + let mut md = t[0][0] * d.is_negative() as i64 + t[0][1] * e.is_negative() as i64; + let mut me = t[1][0] * d.is_negative() as i64 + t[1][1] * e.is_negative() as i64; + + let cd = t[0][0] + .wrapping_mul(d.lowest() as i64) + .wrapping_add(t[0][1].wrapping_mul(e.lowest() as i64)) + & mask; + let ce = t[1][0] + .wrapping_mul(d.lowest() as i64) + .wrapping_add(t[1][1].wrapping_mul(e.lowest() as i64)) + & mask; + + md -= (self.inverse.wrapping_mul(cd).wrapping_add(md)) & mask; + me -= (self.inverse.wrapping_mul(ce).wrapping_add(me)) & mask; + + let cd = t[0][0] * &d + t[0][1] * &e + md * &self.modulus; + let ce = t[1][0] * &d + t[1][1] * &e + me * &self.modulus; + + (cd.shift(), ce.shift()) + } + + /// Returns either "value (mod M)" or "-value (mod M)", where M is the + /// modulus the inverter was created for, depending on "negate", which + /// determines the presence of "-" in the used formula. The input + /// integer lies in the interval (-2 * M, M) + fn norm(&self, mut value: CInt<62, L>, negate: bool) -> CInt<62, L> { + if value.is_negative() { + value = value + &self.modulus; + } + + if negate { + value = -value; + } + + if value.is_negative() { + value = value + &self.modulus; + } + + value + } + + /// Returns a big unsigned integer as an array of O-bit chunks, which is + /// equal modulo 2 ^ (O * S) to the input big unsigned integer stored as + /// an array of I-bit chunks. The ordering of the chunks in these arrays + /// is little-endian + const fn convert(input: &[u64]) -> [u64; S] { + // This function is defined because the method "min" of the usize type is not + // constant + const fn min(a: usize, b: usize) -> usize { + if a > b { + b + } else { + a + } + } + + let (total, mut output, mut bits) = (min(input.len() * I, S * O), [0; S], 0); + + while bits < total { + let (i, o) = (bits % I, bits % O); + output[bits / O] |= (input[bits / I] >> i) << o; + bits += min(I - i, O - o); + } + + let mask = u64::MAX >> (64 - O); + let mut filled = total / O + if total % O > 0 { 1 } else { 0 }; + + while filled > 0 { + filled -= 1; + output[filled] &= mask; + } + + output + } + + /// Returns the multiplicative inverse of the argument modulo 2^62. The + /// implementation is based on the Hurchalla's method for computing the + /// multiplicative inverse modulo a power of two. For better + /// understanding the implementation, the following paper is recommended: + /// J. Hurchalla, "An Improved Integer Multiplicative Inverse (modulo 2^w)", + /// + const fn inv(value: u64) -> i64 { + let x = value.wrapping_mul(3) ^ 2; + let y = 1u64.wrapping_sub(x.wrapping_mul(value)); + let (x, y) = (x.wrapping_mul(y.wrapping_add(1)), y.wrapping_mul(y)); + let (x, y) = (x.wrapping_mul(y.wrapping_add(1)), y.wrapping_mul(y)); + let (x, y) = (x.wrapping_mul(y.wrapping_add(1)), y.wrapping_mul(y)); + (x.wrapping_mul(y.wrapping_add(1)) & CInt::<62, L>::MASK) as i64 + } + + /// Creates the inverter for specified modulus and adjusting parameter + pub const fn new(modulus: &[u64], adjuster: &[u64]) -> Self { + Self { + modulus: CInt::<62, L>(Self::convert::<64, 62, L>(modulus)), + adjuster: CInt::<62, L>(Self::convert::<64, 62, L>(adjuster)), + inverse: Self::inv(modulus[0]), + } + } + + /// Returns either the adjusted modular multiplicative inverse for the + /// argument or None depending on invertibility of the argument, i.e. + /// its coprimality with the modulus + pub fn invert(&self, value: &[u64]) -> Option<[u64; S]> { + let (mut d, mut e) = (CInt::ZERO, self.adjuster.clone()); + let mut g = CInt::<62, L>(Self::convert::<64, 62, L>(value)); + let (mut delta, mut f) = (1, self.modulus.clone()); + let mut matrix; + while g != CInt::ZERO { + (delta, matrix) = Self::jump(&f, &g, delta); + (f, g) = Self::fg(f, g, matrix); + (d, e) = self.de(d, e, matrix); + } + // At this point the absolute value of "f" equals the greatest common divisor + // of the integer to be inverted and the modulus the inverter was created for. + // Thus, if "f" is neither 1 nor -1, then the sought inverse does not exist + let antiunit = f == CInt::MINUS_ONE; + if (f != CInt::ONE) && !antiunit { + return None; + } + Some(Self::convert::<62, 64, S>(&self.norm(d, antiunit).0)) + } +} diff --git a/forks/halo2curves/src/ff_ext/jacobi.rs b/forks/halo2curves/src/ff_ext/jacobi.rs new file mode 100644 index 00000000..113ee6f3 --- /dev/null +++ b/forks/halo2curves/src/ff_ext/jacobi.rs @@ -0,0 +1,427 @@ +use core::cmp::PartialEq; +use std::ops::{Add, Mul, Neg, Shr, Sub}; + +/// Big signed (64 * L)-bit integer type, whose variables store +/// numbers in the two's complement code as arrays of 64-bit chunks. +/// The ordering of the chunks in these arrays is little-endian. +/// The arithmetic operations for this type are wrapping ones +#[derive(Clone)] +pub struct LInt([u64; L]); + +impl LInt { + /// Representation of -1 + pub const MINUS_ONE: Self = Self([u64::MAX; L]); + + /// Representation of 0 + pub const ZERO: Self = Self([0; L]); + + /// Representation of 1 + pub const ONE: Self = { + let mut data = [0; L]; + data[0] = 1; + Self(data) + }; + + /// Returns the number, which is stored as the specified + /// sequence padded with zeros to length L. If the input + /// sequence is longer than L, the method panics + pub fn new(data: &[u64]) -> Self { + let mut number = Self::ZERO; + number.0[..data.len()].copy_from_slice(data); + number + } + + /// Returns "true" iff the current number is negative + #[inline] + pub fn is_negative(&self) -> bool { + self.0[L - 1] > (u64::MAX >> 1) + } + + /// Returns a tuple representing the sum of the first two arguments and the + /// bit described by the third argument. The first element of the tuple + /// is this sum modulo 2^64, the second one indicates whether the sum is + /// no less than 2^64 + #[inline] + fn sum(first: u64, second: u64, carry: bool) -> (u64, bool) { + // The implementation is inspired with the "carrying_add" function from this + // source: + let (second, carry) = second.overflowing_add(carry as u64); + let (first, high) = first.overflowing_add(second); + (first, carry || high) + } + + /// Returns "(low, high)", where "high * 2^64 + low = first * second + carry + /// + summand" + #[inline] + fn prodsum(first: u64, second: u64, summand: u64, carry: u64) -> (u64, u64) { + let all = (first as u128) * (second as u128) + (carry as u128) + (summand as u128); + (all as u64, (all >> u64::BITS) as u64) + } +} + +impl PartialEq for LInt { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl Shr for &LInt { + type Output = LInt; + /// Returns the result of applying the arithmetic right shift to the current + /// number. The specified bit quantity the number is shifted by must lie + /// in {1, 2, ..., 63}. For the quantities outside of the range, the + /// behavior of the method is undefined + fn shr(self, bits: u32) -> Self::Output { + debug_assert!( + (bits > 0) && (bits < 64), + "Cannot shift by 0 or more than 63 bits!" + ); + let (mut data, right) = ([0; L], u64::BITS - bits); + + for (i, d) in data.iter_mut().enumerate().take(L - 1) { + *d = (self.0[i] >> bits) | (self.0[i + 1] << right); + } + data[L - 1] = self.0[L - 1] >> bits; + if self.is_negative() { + data[L - 1] |= u64::MAX << right; + } + LInt::(data) + } +} + +impl Shr for LInt { + type Output = LInt; + fn shr(self, bits: u32) -> Self::Output { + &self >> bits + } +} + +impl Add for &LInt { + type Output = LInt; + fn add(self, other: Self) -> Self::Output { + let (mut data, mut carry) = ([0; L], false); + for (i, d) in data.iter_mut().enumerate().take(L) { + (*d, carry) = Self::Output::sum(self.0[i], other.0[i], carry); + } + LInt::(data) + } +} + +impl Add<&LInt> for LInt { + type Output = LInt; + fn add(self, other: &Self) -> Self::Output { + &self + other + } +} + +impl Add for LInt { + type Output = LInt; + fn add(self, other: Self) -> Self::Output { + &self + &other + } +} + +impl Sub for &LInt { + type Output = LInt; + fn sub(self, other: Self) -> Self::Output { + // For the two's complement code the additive negation is the result of + // adding 1 to the bitwise inverted argument's representation. Thus, for + // any encoded integers x and y we have x - y = x + !y + 1, where "!" is + // the bitwise inversion and addition is done according to the rules of + // the code. The algorithm below uses this formula and is the modified + // addition algorithm, where the carry flag is initialized with "true" + // and the chunks of the second argument are bitwise inverted + let (mut data, mut carry) = ([0; L], true); + for (i, d) in data.iter_mut().enumerate().take(L) { + (*d, carry) = Self::Output::sum(self.0[i], !other.0[i], carry); + } + LInt::(data) + } +} + +impl Sub<&LInt> for LInt { + type Output = LInt; + fn sub(self, other: &Self) -> Self::Output { + &self - other + } +} + +impl Sub for LInt { + type Output = LInt; + fn sub(self, other: Self) -> Self::Output { + &self - &other + } +} + +impl Neg for &LInt { + type Output = LInt; + fn neg(self) -> Self::Output { + // For the two's complement code the additive negation is the result + // of adding 1 to the bitwise inverted argument's representation + let (mut data, mut carry) = ([0; L], true); + for (i, d) in data.iter_mut().enumerate().take(L) { + (*d, carry) = (!self.0[i]).overflowing_add(carry as u64); + } + LInt::(data) + } +} + +impl Neg for LInt { + type Output = LInt; + fn neg(self) -> Self::Output { + -&self + } +} + +impl Mul for &LInt { + type Output = LInt; + fn mul(self, other: Self) -> Self::Output { + let mut data = [0; L]; + for i in 0..L { + let mut carry = 0; + for k in 0..(L - i) { + (data[i + k], carry) = + Self::Output::prodsum(self.0[i], other.0[k], data[i + k], carry); + } + } + LInt::(data) + } +} + +impl Mul<&LInt> for LInt { + type Output = LInt; + fn mul(self, other: &Self) -> Self::Output { + &self * other + } +} + +impl Mul for LInt { + type Output = LInt; + fn mul(self, other: Self) -> Self::Output { + &self * &other + } +} + +impl Mul for &LInt { + type Output = LInt; + fn mul(self, other: i64) -> Self::Output { + let mut data = [0; L]; + // If the short multiplicand is non-negative, the standard multiplication + // algorithm is performed. Otherwise, the product of the additively negated + // multiplicands is found as follows. Since for the two's complement code + // the additive negation is the result of adding 1 to the bitwise inverted + // argument's representation, for any encoded integers x and y we have + // x * y = (-x) * (-y) = (!x + 1) * (-y) = !x * (-y) + (-y), where "!" is + // the bitwise inversion and arithmetic operations are performed according + // to the rules of the code. If the short multiplicand is negative, the + // algorithm below uses this formula by substituting the short multiplicand + // for y and becomes the modified standard multiplication algorithm, where + // the carry variable is being initialized with the additively negated short + // multiplicand and the chunks of the long multiplicand are bitwise inverted + let (other, mut carry, mask) = if other < 0 { + (-other as u64, -other as u64, u64::MAX) + } else { + (other as u64, 0, 0) + }; + for (i, d) in data.iter_mut().enumerate().take(L) { + (*d, carry) = Self::Output::prodsum(self.0[i] ^ mask, other, 0, carry); + } + LInt::(data) + } +} + +impl Mul for LInt { + type Output = LInt; + fn mul(self, other: i64) -> Self::Output { + &self * other + } +} + +impl Mul<&LInt> for i64 { + type Output = LInt; + fn mul(self, other: &LInt) -> Self::Output { + other * self + } +} + +impl Mul> for i64 { + type Output = LInt; + fn mul(self, other: LInt) -> Self::Output { + other * self + } +} + +/// Returns the "approximations" of the arguments and the flag indicating +/// whether both arguments are equal to their "approximations". Both the +/// arguments must be non-negative, and at least one of them must be non-zero. +/// For an incorrect input, the behavior of the function is undefined. These +/// "approximations" are defined in the following way. Let n be the bit length +/// of the largest argument without leading zeros. For n > 64 the +/// "approximation" of the argument, which equals v, is (v div 2 ^ (n - 32)) * 2 +/// ^ 32 + (v mod 2 ^ 32), i.e. it retains the high and low bits of the n-bit +/// representation of v. If n does not exceed 64, an argument +/// and its "approximation" are equal. These "approximations" are defined +/// slightly differently from the ones in the Pornin's method for modular +/// inversion: instead of taking the 33 high and 31 low bits of the n-bit +/// representation of an argument, the 32 high and 32 low bits are taken +fn approximate(x: &LInt, y: &LInt) -> (u64, u64, bool) { + debug_assert!( + !(x.is_negative() || y.is_negative()), + "Both the arguments must be non-negative!" + ); + debug_assert!( + (*x != LInt::ZERO) || (*y != LInt::ZERO), + "At least one argument must be non-zero!" + ); + let mut i = L - 1; + while (x.0[i] == 0) && (y.0[i] == 0) { + i -= 1; + } + if i == 0 { + return (x.0[0], y.0[0], true); + } + let mut h = (x.0[i], y.0[i]); + let z = h.0.leading_zeros().min(h.1.leading_zeros()); + h = (h.0 << z, h.1 << z); + if z > 32 { + h.0 |= x.0[i - 1] >> z; + h.1 |= y.0[i - 1] >> z; + } + let h = (h.0 & u64::MAX << 32, h.1 & u64::MAX << 32); + let l = (x.0[0] & u64::MAX >> 32, y.0[0] & u64::MAX >> 32); + (h.0 | l.0, h.1 | l.1, false) +} + +/// Returns the Jacobi symbol ("n" / "d") multiplied by either 1 or -1. +/// The later multiplicand is -1 iff the second-lowest bit of "t" is 1. +/// The value of "d" must be odd in accordance with the Jacobi symbol +/// definition. For even values of "d", the behavior is not defined. +/// The implementation is based on the binary Euclidean algorithm +fn jacobinary(mut n: u64, mut d: u64, mut t: u64) -> i64 { + debug_assert!(d & 1 > 0, "The second argument must be odd!"); + while n != 0 { + if n & 1 > 0 { + if n < d { + (n, d) = (d, n); + t ^= n & d; + } + n = (n - d) >> 1; + t ^= d ^ d >> 1; + } else { + let z = n.trailing_zeros(); + t ^= (d ^ d >> 1) & (z << 1) as u64; + n >>= z; + } + } + (d == 1) as i64 * (1 - (t & 2) as i64) +} + +/// Returns the Jacobi symbol ("n" / "d") computed by means of the modification +/// of the Pornin's method for modular inversion. The arguments are unsigned +/// big integers in the form of arrays of 64-bit chunks, the ordering of which +/// is little-endian. The value of "d" must be odd in accordance with the Jacobi +/// symbol definition. Both the arguments must be less than 2 ^ (64 * L - 31). +/// For an incorrect input, the behavior of the function is undefined. The +/// method differs from the Pornin's method for modular inversion in absence of +/// the parts, which are not necessary to compute the greatest common divisor of +/// arguments, presence of the parts used to compute the Jacobi symbol, which +/// are based on the properties of the modified Jacobi symbol (x / |y|) +/// described by M. Hamburg, and some original optimizations. Only these +/// differences have been commented; the aforesaid Pornin's method and the used +/// ideas of M. Hamburg were given here: +/// - T. Pornin, "Optimized Binary GCD for Modular Inversion", +/// +/// - M. Hamburg, "Computing the Jacobi symbol using Bernstein-Yang", +/// +pub fn jacobi(n: &[u64], d: &[u64]) -> i64 { + // Instead of the variable "j" taking the values from {-1, 1} and satisfying + // at the end of the outer loop iteration the equation J = "j" * ("n" / |"d"|) + // for the modified Jacobi symbol ("n" / |"d"|) and the sought Jacobi symbol J, + // we store the sign bit of "j" in the second-lowest bit of "t" for optimization + // purposes. This approach was influenced by the paper by M. Hamburg + let (mut n, mut d, mut t) = (LInt::::new(n), LInt::::new(d), 0u64); + debug_assert!(d.0[0] & 1 > 0, "The second argument must be odd!"); + debug_assert!( + n.0[L - 1].leading_zeros().min(d.0[L - 1].leading_zeros()) >= 31, + "Both the arguments must be less than 2 ^ (64 * L - 31)!" + ); + loop { + // The inner loop performs 30 iterations instead of 31 ones in the + // aforementioned Pornin's method, and the "approximations" of "n" and + // "d" retain 32 of the lowest bits instead of 31 in that method. These + // modifications allow the values of the "approximation" variables to be + // equal modulo 8 to the corresponding "precise" variables' values, + // which would have been computed, if the "precise" variables + // had been updated in the inner loop along with the "approximations". This + // equality modulo 8 is used to update the second-lowest bit of "t" in + // accordance with the properties of the modified Jacobi symbol (x / + // |y|). The admissibility of these modifications has been proven using + // the appropriately modified Pornin's theorems + let (mut u, mut v, mut i) = ((1i64, 0i64), (0i64, 1i64), 30); + let (mut a, mut b, precise) = approximate(&n, &d); + // When each "approximation" variable has the same value as the corresponding + // "precise" one, the computation is accomplished using the + // short-arithmetic method of the Jacobi symbol calculation by means of + // the binary Euclidean algorithm. This approach aims at avoiding the + // parts of the final computations, which are related to long arithmetic + if precise { + return jacobinary(a, b, t); + } + while i > 0 { + if a & 1 > 0 { + if a < b { + (a, b, u, v) = (b, a, v, u); + // In both the aforesaid Pornin's method and its modification "n" and "d" + // could not become negative simultaneously even if they were updated after + // each iteration of the inner loop. Also at this point they both have odd + // values. Therefore, the quadratic reciprocity law for the modified Jacobi + // symbol (x / |y|) can be used. According to it, if both x and y are odd + // numbers, among which there is a positive one, then for x = y = 3 (mod 4) + // we have (x / |y|) = -(y / |x|) and for either x or y equal 1 modulo 4 + // the symbols (x / |y|) and (y / |x|) are equal + t ^= a & b; + } + a = (a - b) >> 1; + u = (u.0 - v.0, u.1 - v.1); + v = (v.0 << 1, v.1 << 1); + // The modified Jacobi symbol (2 / |y|) is -1, iff y mod 8 is {3, 5} + t ^= b ^ b >> 1; + i -= 1; + } else { + // Performing the batch of sequential iterations, which divide "a" by 2 + let z = i.min(a.trailing_zeros()); + // The modified Jacobi symbol (2 / |y|) is -1, iff y mod 8 is {3, 5}. However, + // we do not need its value for a batch with an even number of divisions by 2 + t ^= (b ^ b >> 1) & (z << 1) as u64; + v = (v.0 << z, v.1 << z); + a >>= z; + i -= z; + } + } + (n, d) = ((&n * u.0 + &d * u.1) >> 30, (&n * v.0 + &d * v.1) >> 30); + + // This fragment is present to guarantee the correct behavior of the function + // in the case of arguments, whose greatest common divisor is no less than 2^64 + if n == LInt::ZERO { + // In both the aforesaid Pornin's method and its modification the pair of the + // values of "n" and "d" after the divergence point contains a + // positive number and a negative one. Since the value of "n" is 0, + // the divergence point has not been reached by the inner loop this + // time, so there is no need to check whether "d" is equal to -1 + return (d == LInt::ONE) as i64 * (1 - (t & 2) as i64); + } + + if n.is_negative() { + // Since in both the aforesaid Pornin's method and its modification "d" is + // always odd and cannot become negative simultaneously with "n", + // the value of "d" is positive. The modified Jacobi symbol (-1 / + // |y|) for a positive y is -1, iff y mod 4 = 3 + t ^= d.0[0]; + n = -n; + } else if d.is_negative() { + // The modified Jacobi symbols (x / |y|) and (x / |-y|) are equal, so "t" is not + // updated + d = -d; + } + } +} diff --git a/forks/halo2curves/src/ff_ext/mod.rs b/forks/halo2curves/src/ff_ext/mod.rs new file mode 100644 index 00000000..46eff06a --- /dev/null +++ b/forks/halo2curves/src/ff_ext/mod.rs @@ -0,0 +1,43 @@ +pub mod cubic; +pub mod inverse; +pub mod jacobi; +pub mod quadratic; +use subtle::{Choice, ConstantTimeEq}; + +pub trait Legendre { + fn legendre(&self) -> i64; + + #[inline(always)] + fn ct_quadratic_non_residue(&self) -> Choice { + self.legendre().ct_eq(&-1) + } + + #[inline(always)] + fn ct_quadratic_residue(&self) -> Choice { + // The legendre symbol returns 0 for 0 + // and 1 for quadratic residues, + // we consider 0 a square hence quadratic residue. + self.legendre().ct_ne(&-1) + } +} + +#[macro_export] +macro_rules! extend_field_legendre { + ($field:ident ) => { + impl $crate::ff_ext::Legendre for $field { + #[inline(always)] + fn legendre(&self) -> i64 { + self.jacobi() + } + } + }; +} + +pub trait ExtField: ff::Field { + const NON_RESIDUE: Self; + #[must_use] + fn mul_by_nonresidue(&self) -> Self { + Self::NON_RESIDUE * self + } + fn frobenius_map(&mut self, power: usize); +} diff --git a/forks/halo2curves/src/ff_ext/quadratic.rs b/forks/halo2curves/src/ff_ext/quadratic.rs new file mode 100644 index 00000000..59c779c2 --- /dev/null +++ b/forks/halo2curves/src/ff_ext/quadratic.rs @@ -0,0 +1,340 @@ +use ff::Field; +use subtle::{Choice, CtOption}; + +use super::{ + cubic::{CubicExtField, CubicSparseMul}, + ExtField, +}; + +pub trait QuadSparseMul { + type Base: ExtField; + + fn mul_by_014( + lhs: &mut QuadExtField>, + c0: &Self::Base, + c1: &Self::Base, + c4: &Self::Base, + ) where + CubicExtField: CubicSparseMul + ExtField, + { + let aa = CubicExtField::mul_by_01(&lhs.c0, c0, c1); + let bb = CubicExtField::mul_by_1(&lhs.c1, c4); + let t0 = &(lhs.c1 + lhs.c0); + let t1 = *c1 + c4; + lhs.c1 = CubicExtField::mul_by_01(t0, c0, &t1) - (aa + bb); + lhs.c0 = bb.mul_by_nonresidue() + aa; + } + + fn mul_by_034( + lhs: &mut QuadExtField>, + c0: &Self::Base, + c3: &Self::Base, + c4: &Self::Base, + ) where + CubicExtField: CubicSparseMul + ExtField, + { + let t0 = CubicExtField { + c0: lhs.c0.c0 * c0, + c1: lhs.c0.c1 * c0, + c2: lhs.c0.c2 * c0, + }; + let t1 = CubicExtField::mul_by_01(&lhs.c1, c3, c4); + let t2 = lhs.c0 + lhs.c1; + let t3 = *c0 + c3; + lhs.c1 = CubicExtField::mul_by_01(&t2, &t3, c4) - t0 - t1; + lhs.c0 = t0 + t1.mul_by_nonresidue(); + } +} + +// Algorithm 9 of https://eprint.iacr.org/2012/685.pdf +pub fn sqrt_algo9>( + e: &QuadExtField, + q_minus_3_over_4: S, + q_minus_1_over_2: S, +) -> subtle::CtOption> +where + QuadExtField: QuadExtFieldArith + ExtField, +{ + if e.is_zero().into() { + subtle::CtOption::new(QuadExtField::ZERO, subtle::Choice::from(1)) + } else { + let mut a1 = e.pow(q_minus_3_over_4); + + let alpha = a1.square(); + let alpha = alpha * e; + + let mut a0 = alpha; + a0.frobenius_map(1); + let a0 = a0 * alpha; + + let neg1 = QuadExtField:: { + c0: F::ZERO - F::ONE, + c1: F::ZERO, + }; + + if a0 == neg1 { + subtle::CtOption::new(a0, subtle::Choice::from(0)) + } else { + a1.mul_assign(e); + + if alpha == neg1 { + a1.mul_assign(&QuadExtField:: { + c0: F::ZERO, + c1: F::ONE, + }); + } else { + let alpha = alpha + QuadExtField::::ONE; + let alpha = alpha.pow(q_minus_1_over_2); + a1.mul_assign(&alpha); + } + subtle::CtOption::new(a1, subtle::Choice::from(1)) + } + } +} + +// Algorithm 10 of https://eprint.iacr.org/2012/685.pdf +pub fn sqrt_algo10>( + el: &QuadExtField, + precompute_e: &QuadExtField, + precompute_f: &QuadExtField, + q_minus_1_over_4: S, +) -> subtle::CtOption> +where + QuadExtField: QuadExtFieldArith + ExtField, +{ + let b = el.pow_vartime(q_minus_1_over_4); + + let b_2 = b.square(); + let mut b_2_q = b_2; + b_2_q.frobenius_map(1); + + let a0 = b_2_q * b_2; + let neg1 = QuadExtField:: { + c0: F::ZERO - F::ONE, + c1: F::ZERO, + }; + + if a0 == neg1 { + CtOption::new(a0, Choice::from(0)) + } else { + let mut x = b; + x.frobenius_map(1); + if x * b == QuadExtField::ONE { + let x0 = (b_2 * el).c0.sqrt().unwrap(); + x.c0.mul_assign(x0); + x.c1.mul_assign(x0); + CtOption::new(x, Choice::from(1)) + } else { + let x0 = (b_2 * precompute_f * el).sqrt().unwrap(); + x *= x0 * precompute_e; + CtOption::new(x, Choice::from(1)) + } + } +} + +pub enum SQRT { + Algorithm9 { + q_minus_3_over_4: &'static [u64], + q_minus_1_over_2: &'static [u64], + }, + Algorithm10 { + precompute_e: QuadExtField, + precompute_f: QuadExtField, + q_minus_1_over_4: &'static [u64], + }, + Unimplemented, +} + +pub trait QuadExtFieldArith { + type Base: ExtField; + const SQRT: SQRT = SQRT::Unimplemented; + + fn mul_assign(lhs: &mut QuadExtField, rhs: &QuadExtField) { + let v0 = lhs.c0 * rhs.c0; + let v1 = lhs.c1 * rhs.c1; + lhs.c1 = (lhs.c0 + lhs.c1) * (rhs.c0 + rhs.c1) - (v0 + v1); + lhs.c0 = v0 + v1.mul_by_nonresidue(); + } + + fn square_assign(el: &mut QuadExtField) { + let ab = el.c0 * el.c1; + let c0c1 = el.c0 + el.c1; + let c0 = (el.c1.mul_by_nonresidue() + el.c0) * c0c1 - ab; + el.c1 = ab.double(); + el.c0 = c0 - ab.mul_by_nonresidue(); + } +} + +#[cfg(feature = "derive_serde")] +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +#[cfg_attr(feature = "derive_serde", derive(Serialize, Deserialize))] +pub struct QuadExtField { + pub(crate) c0: F, + pub(crate) c1: F, +} + +impl QuadExtField { + #[inline] + pub const fn new(c0: F, c1: F) -> Self { + Self { c0, c1 } + } + + #[inline] + pub const fn zero() -> Self { + Self { + c0: F::ZERO, + c1: F::ZERO, + } + } + + #[inline] + pub const fn one() -> Self { + Self { + c0: F::ONE, + c1: F::ZERO, + } + } + + #[inline] + pub fn c0(&self) -> &F { + &self.c0 + } + + #[inline] + pub fn c1(&self) -> &F { + &self.c1 + } + + #[inline] + pub fn double(&self) -> Self { + Self { + c0: self.c0.double(), + c1: self.c1.double(), + } + } + + #[inline] + pub fn add(&self, other: &Self) -> Self { + Self { + c0: self.c0 + other.c0, + c1: self.c1 + other.c1, + } + } + + #[inline] + pub fn sub(&self, other: &Self) -> Self { + Self { + c0: self.c0 - other.c0, + c1: self.c1 - other.c1, + } + } + + #[inline] + pub fn neg(&self) -> Self { + Self { + c0: -self.c0, + c1: -self.c1, + } + } + + #[inline] + pub fn conjugate(&mut self) { + self.c1 = -self.c1; + } +} + +impl QuadExtField +where + Self: QuadExtFieldArith, +{ + pub fn mul(&self, rhs: &Self) -> Self { + let mut lhs = *self; + Self::mul_assign(&mut lhs, rhs); + lhs + } + + pub fn mul_assign(&mut self, rhs: &Self) { + ::mul_assign(self, rhs); + } + + pub fn square(el: &Self) -> Self { + let mut el = *el; + Self::square_assign(&mut el); + el + } + + pub fn square_assign(&mut self) { + ::square_assign(self); + } + + pub fn norm(&self) -> F { + self.c0.square() - self.c1.square().mul_by_nonresidue() + } +} + +impl Field for QuadExtField +where + QuadExtField: QuadExtFieldArith + ExtField, +{ + const ZERO: Self = Self::zero(); + const ONE: Self = Self::one(); + + fn random(mut rng: impl rand_core::RngCore) -> Self { + Self::new(F::random(&mut rng), F::random(&mut rng)) + } + + fn is_zero(&self) -> subtle::Choice { + self.c0.is_zero() & self.c1.is_zero() + } + + fn square(&self) -> Self { + QuadExtField::square(self) + } + + fn double(&self) -> Self { + self.double() + } + + fn sqrt(&self) -> subtle::CtOption { + match Self::SQRT { + SQRT::Algorithm9 { + q_minus_3_over_4, + q_minus_1_over_2, + } => sqrt_algo9(self, q_minus_3_over_4, q_minus_1_over_2), + SQRT::Algorithm10 { + precompute_e, + precompute_f, + q_minus_1_over_4, + } => sqrt_algo10(self, &precompute_e, &precompute_f, q_minus_1_over_4), + SQRT::Unimplemented => unimplemented!(), + } + } + + fn sqrt_ratio(_: &Self, _: &Self) -> (subtle::Choice, Self) { + unimplemented!() + } + + fn invert(&self) -> subtle::CtOption { + self.norm().invert().map(|t| Self { + c0: self.c0 * t, + c1: self.c1 * -t, + }) + } +} + +impl subtle::ConditionallySelectable for QuadExtField { + fn conditional_select(a: &Self, b: &Self, choice: subtle::Choice) -> Self { + QuadExtField { + c0: F::conditional_select(&a.c0, &b.c0, choice), + c1: F::conditional_select(&a.c1, &b.c1, choice), + } + } +} + +impl subtle::ConstantTimeEq for QuadExtField { + fn ct_eq(&self, other: &Self) -> subtle::Choice { + self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1) + } +} diff --git a/forks/halo2curves/src/fft.rs b/forks/halo2curves/src/fft.rs new file mode 100644 index 00000000..72965255 --- /dev/null +++ b/forks/halo2curves/src/fft.rs @@ -0,0 +1,134 @@ +use ff::Field; +use group::{GroupOpsOwned, ScalarMulOwned}; + +pub use crate::{CurveAffine, CurveExt}; + +/// This represents an element of a group with basic operations that can be +/// performed. This allows an FFT implementation (for example) to operate +/// generically over either a field or elliptic curve group. +pub trait FftGroup: + Copy + Send + Sync + 'static + GroupOpsOwned + ScalarMulOwned +{ +} + +impl FftGroup for T +where + Scalar: Field, + T: Copy + Send + Sync + 'static + GroupOpsOwned + ScalarMulOwned, +{ +} + +/// Performs a radix-$2$ Fast-Fourier Transformation (FFT) on a vector of size +/// $n = 2^k$, when provided `log_n` = $k$ and an element of multiplicative +/// order $n$ called `omega` ($\omega$). The result is that the vector `a`, when +/// interpreted as the coefficients of a polynomial of degree $n - 1$, is +/// transformed into the evaluations of this polynomial at each of the $n$ +/// distinct powers of $\omega$. This transformation is invertible by providing +/// $\omega^{-1}$ in place of $\omega$ and dividing each resulting field element +/// by $n$. +/// +/// This will use multithreading if beneficial. +pub fn best_fft>(a: &mut [G], omega: Scalar, log_n: u32) { + fn bitreverse(mut n: usize, l: usize) -> usize { + let mut r = 0; + for _ in 0..l { + r = (r << 1) | (n & 1); + n >>= 1; + } + r + } + + let threads = rayon::current_num_threads(); + let log_threads = threads.ilog2(); + let n = a.len(); + assert_eq!(n, 1 << log_n); + + for k in 0..n { + let rk = bitreverse(k, log_n as usize); + if k < rk { + a.swap(rk, k); + } + } + + // precompute twiddle factors + let twiddles: Vec<_> = (0..(n / 2)) + .scan(Scalar::ONE, |w, _| { + let tw = *w; + *w *= ω + Some(tw) + }) + .collect(); + + if log_n <= log_threads { + let mut chunk = 2_usize; + let mut twiddle_chunk = n / 2; + for _ in 0..log_n { + a.chunks_mut(chunk).for_each(|coeffs| { + let (left, right) = coeffs.split_at_mut(chunk / 2); + + // case when twiddle factor is one + let (a, left) = left.split_at_mut(1); + let (b, right) = right.split_at_mut(1); + let t = b[0]; + b[0] = a[0]; + a[0] += &t; + b[0] -= &t; + + left.iter_mut() + .zip(right.iter_mut()) + .enumerate() + .for_each(|(i, (a, b))| { + let mut t = *b; + t *= &twiddles[(i + 1) * twiddle_chunk]; + *b = *a; + *a += &t; + *b -= &t; + }); + }); + chunk *= 2; + twiddle_chunk /= 2; + } + } else { + recursive_butterfly_arithmetic(a, n, 1, &twiddles) + } +} + +/// This perform recursive butterfly arithmetic +pub fn recursive_butterfly_arithmetic>( + a: &mut [G], + n: usize, + twiddle_chunk: usize, + twiddles: &[Scalar], +) { + if n == 2 { + let t = a[1]; + a[1] = a[0]; + a[0] += &t; + a[1] -= &t; + } else { + let (left, right) = a.split_at_mut(n / 2); + rayon::join( + || recursive_butterfly_arithmetic(left, n / 2, twiddle_chunk * 2, twiddles), + || recursive_butterfly_arithmetic(right, n / 2, twiddle_chunk * 2, twiddles), + ); + + // case when twiddle factor is one + let (a, left) = left.split_at_mut(1); + let (b, right) = right.split_at_mut(1); + let t = b[0]; + b[0] = a[0]; + a[0] += &t; + b[0] -= &t; + + left.iter_mut() + .zip(right.iter_mut()) + .enumerate() + .for_each(|(i, (a, b))| { + let mut t = *b; + t *= &twiddles[(i + 1) * twiddle_chunk]; + *b = *a; + *a += &t; + *b -= &t; + }); + } +} diff --git a/forks/halo2curves/src/grumpkin/curve.rs b/forks/halo2curves/src/grumpkin/curve.rs new file mode 100644 index 00000000..e950673f --- /dev/null +++ b/forks/halo2curves/src/grumpkin/curve.rs @@ -0,0 +1,114 @@ +use core::{ + cmp, + fmt::Debug, + iter::Sum, + ops::{Add, Mul, Neg, Sub}, +}; + +use rand::RngCore; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +use crate::{ + arithmetic::{mul_512, sbb, CurveEndo, EndoParameters}, + endo, + ff::{Field, PrimeField, WithSmallOrderMulGroup}, + group::{prime::PrimeCurveAffine, Curve, Group, GroupEncoding}, + grumpkin::{Fq, Fr}, + impl_binops_additive, impl_binops_additive_specify_output, impl_binops_multiplicative, + impl_binops_multiplicative_mixed, new_curve_impl, Coordinates, CurveAffine, CurveExt, +}; + +new_curve_impl!( + (pub), + G1, + G1Affine, + Fq, + Fr, + (G1_GENERATOR_X, G1_GENERATOR_Y), + G1_A, + G1_B, + "grumpkin_g1", + |domain_prefix| crate::hash_to_curve::hash_to_curve(domain_prefix, G1::default_hash_to_curve_suite()), + crate::serde::CompressedFlagConfig::TwoSpare, + standard_sign +); + +// Parameters in montgomery form taken from +// https://github.com/AztecProtocol/barretenberg/blob/97ccf76c42db581a8b8f8bfbcffe8ca015a3dd22/cpp/src/barretenberg/ecc/curves/grumpkin/grumpkin.hpp#L14 +const G1_GENERATOR_X: Fq = Fq::one(); +const G1_GENERATOR_Y: Fq = Fq([ + 0x11b2dff1448c41d8, + 0x23d3446f21c77dc3, + 0xaa7b8cf435dfafbb, + 0x14b34cf69dc25d68, +]); +const G1_A: Fq = Fq::zero(); +const G1_B: Fq = Fq([ + 0xdd7056026000005a, + 0x223fa97acb319311, + 0xcc388229877910c0, + 0x034394632b724eaa, +]); + +// Generated using https://github.com/ConsenSys/gnark-crypto/blob/master/ecc/utils.go +// with `bn256::Fq::ZETA` +// See https://github.com/demining/Endomorphism-Secp256k1/blob/main/README.md +// to have more details about the endomorphism. +const ENDO_PARAMS_GRUMPKIN: EndoParameters = EndoParameters { + gamma1: [0xd91d232ec7e0b3d2, 0x2, 0, 0], + gamma2: [0x5398fd0300ff655f, 0x4ccef014a773d2d2, 0x02, 0], + b1: [0x89d3256894d213e2, 0, 0, 0], + b2: [0x0be4e1541221250b, 0x6f4d8248eeb859fd, 0, 0], +}; + +endo!(G1, Fr, ENDO_PARAMS_GRUMPKIN); + +impl group::cofactor::CofactorGroup for G1 { + type Subgroup = G1; + + fn clear_cofactor(&self) -> Self { + *self + } + + fn into_subgroup(self) -> CtOption { + CtOption::new(self, 1.into()) + } + + fn is_torsion_free(&self) -> Choice { + 1.into() + } +} + +impl G1 { + const SVDW_Z: Fq = Fq::ONE; + + fn default_hash_to_curve_suite() -> crate::hash_to_curve::Suite { + crate::hash_to_curve::Suite::::new( + b"GRUMPKIN_XMD:SHA-256_SVDW_RO_", + Self::SVDW_Z, + crate::hash_to_curve::Method::SVDW, + ) + } +} + +#[cfg(test)] +mod test { + use group::UncompressedEncoding; + use rand_core::OsRng; + + use super::*; + use crate::serde::SerdeObject; + crate::curve_testing_suite!(G1); + crate::curve_testing_suite!(G1, "endo_consistency"); + crate::curve_testing_suite!(G1, "endo"); + crate::curve_testing_suite!( + G1, + "constants", + Fq::MODULUS, + G1_A, + G1_B, + G1_GENERATOR_X, + G1_GENERATOR_Y, + Fr::MODULUS + ); +} diff --git a/forks/halo2curves/src/grumpkin/mod.rs b/forks/halo2curves/src/grumpkin/mod.rs new file mode 100644 index 00000000..58a191c6 --- /dev/null +++ b/forks/halo2curves/src/grumpkin/mod.rs @@ -0,0 +1,5 @@ +mod curve; + +pub use curve::*; + +pub use crate::bn256::{Fq as Fr, Fr as Fq}; diff --git a/forks/halo2curves/src/hash_to_curve.rs b/forks/halo2curves/src/hash_to_curve.rs new file mode 100644 index 00000000..add8488f --- /dev/null +++ b/forks/halo2curves/src/hash_to_curve.rs @@ -0,0 +1,513 @@ +#![allow(clippy::op_ref)] + +use digest::{core_api::BlockSizeUser, Digest}; +use ff::{Field, FromUniformBytes, PrimeField}; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +use crate::{ff_ext::Legendre, CurveExt}; + +pub enum Method { + SSWU(Iso), + SVDW, +} + +/// Map the homogeneous coordinates of a point from the isogenous curve to a +/// point in the original curve. +#[allow(clippy::type_complexity)] +pub struct Iso { + pub(crate) a: C::Base, + pub(crate) b: C::Base, + pub(crate) map: Box C>, +} + +pub struct Suite { + domain: Vec, + map_to_curve: Box C>, + _marker: std::marker::PhantomData, +} + +pub(crate) fn expand_message( + domain_prefix: &[u8], + domain: &[u8], + message: &[u8], + out_len: usize, +) -> Vec { + assert!( + domain_prefix.len() + domain.len() < 256, + "long dst is not supported yet" + ); + + let mut h = D::new(); + h.update(vec![0; D::block_size()]); + h.update(message); + h.update([(out_len >> 8) as u8, out_len as u8, 0]); + h.update(domain_prefix); + h.update(domain); + h.update([(domain.len() + domain_prefix.len()) as u8]); + let b_0 = h.finalize(); + + let mut h = D::new(); + h.update(&b_0); + h.update([1]); + h.update(domain_prefix); + h.update(domain); + h.update([(domain.len() + domain_prefix.len()) as u8]); + let mut b_i = h.finalize(); + + let output_size = ::output_size(); + let ell = (out_len + output_size - 1) / output_size; + let mut out = vec![0u8; out_len]; + + for i in 1..ell { + let mut h = D::new(); + b_0.iter() + .zip(b_i.iter()) + .for_each(|(b_0, b_i)| h.update([*b_0 ^ *b_i])); + h.update([1 + i as u8]); + h.update(domain_prefix); + h.update(domain); + h.update([(domain.len() + domain_prefix.len()) as u8]); + + out.iter_mut() + .skip((i - 1) * output_size) + .zip(b_i.iter()) + .for_each(|(out, b_i)| *out = *b_i); + + b_i = h.finalize(); + } + + out.iter_mut() + .skip((ell - 1) * output_size) + .zip(b_i.iter()) + .for_each(|(out, b_i)| *out = *b_i); + + out +} + +#[allow(clippy::type_complexity)] +pub fn hash_to_curve<'a, C, D: Digest + BlockSizeUser + 'a, const L: usize>( + domain_prefix: &'a str, + suite: Suite, +) -> Box C + 'a> +where + C: CurveExt, + C::Base: Legendre, + C::Base: FromUniformBytes, +{ + Box::new(move |message| suite.hash_to_curve(domain_prefix, message)) +} + +impl Suite +where + C::Base: Legendre + FromUniformBytes, +{ + pub(crate) fn new(domain: &[u8], z: C::Base, method: Method) -> Self { + // Check for the target bits of security `k`. Currently, the target security is + // 128 bits. See: + assert!((C::Base::NUM_BITS as usize + 128) / 8 <= L); + + let map_to_curve: Box C> = match method { + Method::SSWU(iso) => { + let Iso { a, b, map } = iso; + Box::new(move |u| { + let (x, y, z) = sswu_map_to_curve::(u, z, a, b); + map(x, y, z) + }) + } + + Method::SVDW => { + let [c1, c2, c3, c4] = svdw_precomputed_constants::(z); + Box::new(move |u| svdw_map_to_curve::(u, c1, c2, c3, c4, z)) + } + }; + + Self { + map_to_curve, + domain: domain.to_vec(), + _marker: std::marker::PhantomData, + } + } + + pub(crate) fn hash_to_field(&self, domain_prefix: &[u8], message: &[u8]) -> (C::Base, C::Base) { + let out = expand_message::(domain_prefix, &self.domain[..], message, L * 2); + + let u0 = { + let mut out = out[0..L].to_vec(); + out.reverse(); + let out: [u8; L] = out.try_into().unwrap(); + C::Base::from_uniform_bytes(&out) + }; + + let u1 = { + let mut out = out[L..L * 2].to_vec(); + out.reverse(); + let out: [u8; L] = out.try_into().unwrap(); + C::Base::from_uniform_bytes(&out) + }; + + (u0, u1) + } + + pub fn hash_to_curve(&self, domain_prefix: &str, message: &[u8]) -> C { + let (u0, u1) = self.hash_to_field(domain_prefix.as_bytes(), message); + (self.map_to_curve)(u0) + (self.map_to_curve)(u1) + } +} + +pub(crate) fn svdw_precomputed_constants(z: C::Base) -> [C::Base; 4] { + let a = C::a(); + let b = C::b(); + let one = C::Base::ONE; + let three = one + one + one; + let four = three + one; + let tmp = three * z.square() + four * a; + + // 1. c1 = g(Z) + let c1 = (z.square() + a) * z + b; + // 2. c2 = -Z / 2 + let c2 = -z * C::Base::TWO_INV; + // 3. c3 = sqrt(-g(Z) * (3 * Z^2 + 4 * A)) # sgn0(c3) MUST equal 0 + let c3 = { + let c3 = (-c1 * tmp).sqrt().unwrap(); + C::Base::conditional_select(&c3, &-c3, c3.is_odd()) + }; + // 4. c4 = -4 * g(Z) / (3 * Z^2 + 4 * A) + let c4 = -four * c1 * tmp.invert().unwrap(); + + [c1, c2, c3, c4] +} + +// Implementation of +#[allow(clippy::too_many_arguments)] +pub(crate) fn sswu_map_to_curve( + u: C::Base, + z: C::Base, + a: C::Base, + b: C::Base, +) -> (C::Base, C::Base, C::Base) +where + C: CurveExt, +{ + // Implement https://datatracker.ietf.org/doc/html/rfc9380#name-sqrt_ratio-for-any-field + // Copied from ff sqrt_ratio_generic substituting F::ROOT_OF_UNITY for input Z + fn sqrt_ratio(num: &F, div: &F, z: &F) -> (Choice, F) { + // General implementation: + // + // a = num * inv0(div) + // = { 0 if div is zero + // { num/div otherwise + // + // b = z * a + // = { 0 if div is zero + // { z*num/div otherwise + + // Since z is non-square, a and b are either both zero (and both square), or + // only one of them is square. We can therefore choose the square root to return + // based on whether a is square, but for the boolean output we need to handle + // the num != 0 && div == 0 case specifically. + + let a = div.invert().unwrap_or(F::ZERO) * num; + let b = a * z; + let sqrt_a = a.sqrt(); + let sqrt_b = b.sqrt(); + + let num_is_zero = num.is_zero(); + let div_is_zero = div.is_zero(); + let is_square = sqrt_a.is_some(); + let is_nonsquare = sqrt_b.is_some(); + assert!(bool::from( + num_is_zero | div_is_zero | (is_square ^ is_nonsquare) + )); + + ( + is_square & (num_is_zero | !div_is_zero), + CtOption::conditional_select(&sqrt_b, &sqrt_a, is_square).unwrap(), + ) + } + + // 1. tv1 = u^2 + let tv1 = u.square(); + // 2. tv1 = Z * tv1 + let tv1 = z * tv1; + // 3. tv2 = tv1^2 + let tv2 = tv1.square(); + // 4. tv2 = tv2 + tv1 + let tv2 = tv2 + tv1; + // 5. tv3 = tv2 + 1 + let tv3 = tv2 + C::Base::ONE; + // 6. tv3 = B * tv3 + let tv3 = b * tv3; + // 7. tv4 = CMOV(Z, -tv2, tv2 != 0) # tv4 = z if tv2 is 0 else tv4 = -tv2 + let tv2_is_not_zero = !tv2.ct_eq(&C::Base::ZERO); + let tv4 = C::Base::conditional_select(&z, &-tv2, tv2_is_not_zero); + // 8. tv4 = A * tv4 + let tv4 = a * tv4; + // 9. tv2 = tv3^2 + let tv2 = tv3.square(); + // 10. tv6 = tv4^2 + let tv6 = tv4.square(); + // 11. tv5 = A * tv6 + let tv5 = a * tv6; + // 12. tv2 = tv2 + tv5 + let tv2 = tv2 + tv5; + // 13. tv2 = tv2 * tv3 + let tv2 = tv2 * tv3; + // 14. tv6 = tv6 * tv4 + let tv6 = tv6 * tv4; + // 15. tv5 = B * tv6 + let tv5 = b * tv6; + // 16. tv2 = tv2 + tv5 + let tv2 = tv2 + tv5; + // 17. x = tv1 * tv3 + let x = tv1 * tv3; + // 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6) + let (is_gx1_square, y1) = sqrt_ratio(&tv2, &tv6, &z); + // 19. y = tv1 * u + let y = tv1 * u; + // 20. y = y * y1 + let y = y * y1; + // 21. x = CMOV(x, tv3, is_gx1_square) + let x = C::Base::conditional_select(&x, &tv3, is_gx1_square); + // 22. y = CMOV(y, y1, is_gx1_square) + let y = C::Base::conditional_select(&y, &y1, is_gx1_square); + // 23. e1 = sgn0(u) == sgn0(y) + let e1 = u.is_odd().ct_eq(&y.is_odd()); + // 24. y = CMOV(-y, y, e1) # Select correct sign of y + let y = C::Base::conditional_select(&-y, &y, e1); + + // In the original algorithm: + // 25. x = x / tv4 + // 26. return (x, y) + // + // we omit instruction 25. and return the result in homogeneous coordinates + // (instead of the previous jacobi). + + (x, y * tv4, tv4) +} + +#[allow(clippy::too_many_arguments)] +pub(crate) fn svdw_map_to_curve( + u: C::Base, + c1: C::Base, + c2: C::Base, + c3: C::Base, + c4: C::Base, + z: C::Base, +) -> C +where + C: CurveExt, + C::Base: Legendre, +{ + let one = C::Base::ONE; + let a = C::a(); + let b = C::b(); + + // 1. tv1 = u^2 + let tv1 = u.square(); + // 2. tv1 = tv1 * c1 + let tv1 = tv1 * c1; + // 3. tv2 = 1 + tv1 + let tv2 = one + tv1; + // 4. tv1 = 1 - tv1 + let tv1 = one - tv1; + // 5. tv3 = tv1 * tv2 + let tv3 = tv1 * tv2; + // 6. tv3 = inv0(tv3) + let tv3 = tv3.invert().unwrap_or(C::Base::ZERO); + // 7. tv4 = u * tv1 + let tv4 = u * tv1; + // 8. tv4 = tv4 * tv3 + let tv4 = tv4 * tv3; + // 9. tv4 = tv4 * c3 + let tv4 = tv4 * c3; + // 10. x1 = c2 - tv4 + let x1 = c2 - tv4; + // 11. gx1 = x1^2 + let gx1 = x1.square(); + // 12. gx1 = gx1 + A + let gx1 = gx1 + a; + // 13. gx1 = gx1 * x1 + let gx1 = gx1 * x1; + // 14. gx1 = gx1 + B + let gx1 = gx1 + b; + // 15. e1 = is_square(gx1) + let e1 = !gx1.ct_quadratic_non_residue(); + // 16. x2 = c2 + tv4 + let x2 = c2 + tv4; + // 17. gx2 = x2^2 + let gx2 = x2.square(); + // 18. gx2 = gx2 + A + let gx2 = gx2 + a; + // 19. gx2 = gx2 * x2 + let gx2 = gx2 * x2; + // 20. gx2 = gx2 + B + let gx2 = gx2 + b; + // 21. e2 = is_square(gx2) AND NOT e1 # Avoid short-circuit logic ops + let e2 = !gx2.ct_quadratic_non_residue() & (!e1); + // 22. x3 = tv2^2 + let x3 = tv2.square(); + // 23. x3 = x3 * tv3 + let x3 = x3 * tv3; + // 24. x3 = x3^2 + let x3 = x3.square(); + // 25. x3 = x3 * c4 + let x3 = x3 * c4; + // 26. x3 = x3 + Z + let x3 = x3 + z; + // 27. x = CMOV(x3, x1, e1) # x = x1 if gx1 is square, else x = x3 + let x = C::Base::conditional_select(&x3, &x1, e1); + // 28. x = CMOV(x, x2, e2) # x = x2 if gx2 is square and gx1 is not + let x = C::Base::conditional_select(&x, &x2, e2); + // 29. gx = x^2 + let gx = x.square(); + // 30. gx = gx + A + let gx = gx + a; + // 31. gx = gx * x + let gx = gx * x; + // 32. gx = gx + B + let gx = gx + b; + // 33. y = sqrt(gx) + let y = gx.sqrt().unwrap(); + // 34. e3 = sgn0(u) == sgn0(y) + let e3 = u.is_odd().ct_eq(&y.is_odd()); + // 35. y = CMOV(-y, y, e3) # Select correct sign of y + let y = C::Base::conditional_select(&-y, &y, e3); + // 36. return (x, y) + C::new_jacobian(x, y, one).unwrap() +} + +#[cfg(test)] +mod test { + + use std::marker::PhantomData; + + use sha2::{Sha256, Sha512}; + + use super::*; + + #[test] + fn test_expand_message() { + // Test vectors are taken from: + // https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-expand_message_xmdsha-256 + + struct Test { + msg: &'static [u8], + expect: Vec, + _marker: PhantomData, + } + + impl Test { + fn new(msg: &'static [u8], expect: &str) -> Self { + Self { + msg, + expect: crate::tests::hex_to_bytes(expect), + _marker: PhantomData, + } + } + + fn run(&self, domain_prefix: &[u8], domain: &[u8]) { + let outlen = self.expect.len(); + let out = expand_message::(domain_prefix, domain, self.msg, outlen); + assert_eq!(out, self.expect); + } + } + [ + // out len 0x20 + Test::::new( + b"", + "68a985b87eb6b46952128911f2a4412bbc302a9d759667f87f7a21d803f07235", + ), + Test::::new( + b"abc", + "d8ccab23b5985ccea865c6c97b6e5b8350e794e603b4b97902f53a8a0d605615", + ), + Test::::new( + b"abcdef0123456789", + "eff31487c770a893cfb36f912fbfcbff40d5661771ca4b2cb4eafe524333f5c1", + ), + Test::::new( + b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", + "b23a1d2b4d97b2ef7785562a7e8bac7eed54ed6e97e29aa51bfe3f12ddad1ff9", + ), + Test::::new( + b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "4623227bcc01293b8c130bf771da8c298dede7383243dc0993d2d94823958c4c", + ), + // out len 0x80 + Test::::new( + b"", + "af84c27ccfd45d41914fdff5df25293e221afc53d8ad2ac06d5e3e29485dadbee0d121587713a3e0dd4d5e69e93eb7cd4f5df4cd103e188cf60cb02edc3edf18eda8576c412b18ffb658e3dd6ec849469b979d444cf7b26911a08e63cf31f9dcc541708d3491184472c2c29bb749d4286b004ceb5ee6b9a7fa5b646c993f0ced", + ), + Test::::new( + b"abc", + "abba86a6129e366fc877aab32fc4ffc70120d8996c88aee2fe4b32d6c7b6437a647e6c3163d40b76a73cf6a5674ef1d890f95b664ee0afa5359a5c4e07985635bbecbac65d747d3d2da7ec2b8221b17b0ca9dc8a1ac1c07ea6a1e60583e2cb00058e77b7b72a298425cd1b941ad4ec65e8afc50303a22c0f99b0509b4c895f40", + ), + Test::::new( + b"abcdef0123456789", + "ef904a29bffc4cf9ee82832451c946ac3c8f8058ae97d8d629831a74c6572bd9ebd0df635cd1f208e2038e760c4994984ce73f0d55ea9f22af83ba4734569d4bc95e18350f740c07eef653cbb9f87910d833751825f0ebefa1abe5420bb52be14cf489b37fe1a72f7de2d10be453b2c9d9eb20c7e3f6edc5a60629178d9478df", + ), + Test::::new( + b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", + "80be107d0884f0d881bb460322f0443d38bd222db8bd0b0a5312a6fedb49c1bbd88fd75d8b9a09486c60123dfa1d73c1cc3169761b17476d3c6b7cbbd727acd0e2c942f4dd96ae3da5de368d26b32286e32de7e5a8cb2949f866a0b80c58116b29fa7fabb3ea7d520ee603e0c25bcaf0b9a5e92ec6a1fe4e0391d1cdbce8c68a", + ), + Test::::new( + b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "546aff5444b5b79aa6148bd81728704c32decb73a3ba76e9e75885cad9def1d06d6792f8a7d12794e90efed817d96920d728896a4510864370c207f99bd4a608ea121700ef01ed879745ee3e4ceef777eda6d9e5e38b90c86ea6fb0b36504ba4a45d22e86f6db5dd43d98a294bebb9125d5b794e9d2a81181066eb954966a487", + ), + + ] + .iter() + .for_each(|test| { + test.run(b"QUUX-V01-CS02-with-expander-",b"SHA256-128"); + }); + + [ + // out len 0x20 + Test::::new( + b"", + "6b9a7312411d92f921c6f68ca0b6380730a1a4d982c507211a90964c394179ba", + ), + Test::::new( + b"abc", + "0da749f12fbe5483eb066a5f595055679b976e93abe9be6f0f6318bce7aca8dc", + ), + Test::::new( + b"abcdef0123456789", + "087e45a86e2939ee8b91100af1583c4938e0f5fc6c9db4b107b83346bc967f58", + ), + Test::::new( + b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", + "7336234ee9983902440f6bc35b348352013becd88938d2afec44311caf8356b3", + ), + Test::::new( + b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "57b5f7e766d5be68a6bfe1768e3c2b7f1228b3e4b3134956dd73a59b954c66f4", + ), + // out len 0x80 + Test::::new( + b"", + "41b037d1734a5f8df225dd8c7de38f851efdb45c372887be655212d07251b921b052b62eaed99b46f72f2ef4cc96bfaf254ebbbec091e1a3b9e4fb5e5b619d2e0c5414800a1d882b62bb5cd1778f098b8eb6cb399d5d9d18f5d5842cf5d13d7eb00a7cff859b605da678b318bd0e65ebff70bec88c753b159a805d2c89c55961", + ), + Test::::new( + b"abc", + "7f1dddd13c08b543f2e2037b14cefb255b44c83cc397c1786d975653e36a6b11bdd7732d8b38adb4a0edc26a0cef4bb45217135456e58fbca1703cd6032cb1347ee720b87972d63fbf232587043ed2901bce7f22610c0419751c065922b488431851041310ad659e4b23520e1772ab29dcdeb2002222a363f0c2b1c972b3efe1", + ), + Test::::new( + b"abcdef0123456789", + "3f721f208e6199fe903545abc26c837ce59ac6fa45733f1baaf0222f8b7acb0424814fcb5eecf6c1d38f06e9d0a6ccfbf85ae612ab8735dfdf9ce84c372a77c8f9e1c1e952c3a61b7567dd0693016af51d2745822663d0c2367e3f4f0bed827feecc2aaf98c949b5ed0d35c3f1023d64ad1407924288d366ea159f46287e61ac", + ), + Test::::new( + b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", + "b799b045a58c8d2b4334cf54b78260b45eec544f9f2fb5bd12fb603eaee70db7317bf807c406e26373922b7b8920fa29142703dd52bdf280084fb7ef69da78afdf80b3586395b433dc66cde048a258e476a561e9deba7060af40adf30c64249ca7ddea79806ee5beb9a1422949471d267b21bc88e688e4014087a0b592b695ed", + ), + Test::::new( + b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "05b0bfef265dcee87654372777b7c44177e2ae4c13a27f103340d9cd11c86cb2426ffcad5bd964080c2aee97f03be1ca18e30a1f14e27bc11ebbd650f305269cc9fb1db08bf90bfc79b42a952b46daf810359e7bc36452684784a64952c343c52e5124cd1f71d474d5197fefc571a92929c9084ffe1112cf5eea5192ebff330b", + ), + ] + .iter() + .for_each(|test| { + test.run(b"QUUX-V01-CS02-with-expander-", b"SHA512-256"); + }); + } +} diff --git a/forks/halo2curves/src/lib.rs b/forks/halo2curves/src/lib.rs new file mode 100644 index 00000000..2e21ce02 --- /dev/null +++ b/forks/halo2curves/src/lib.rs @@ -0,0 +1,29 @@ +mod arithmetic; +mod curve; +pub mod ff_ext; +pub mod fft; +pub mod hash_to_curve; +pub mod msm; +pub mod serde; + +pub mod bls12381; +pub mod bn256; +pub mod grumpkin; +pub mod pasta; +pub mod pluto_eris; +pub mod secp256k1; +pub mod secp256r1; +pub mod secq256k1; +pub mod t256; + +#[macro_use] +mod derive; + +// Re-export to simplify downstream dependencies. +pub use curve::{Coordinates, CurveAffine, CurveExt}; +pub use ff; +pub use group; +pub use pairing; + +#[cfg(test)] +pub mod tests; diff --git a/forks/halo2curves/src/msm.rs b/forks/halo2curves/src/msm.rs new file mode 100644 index 00000000..cbfdf243 --- /dev/null +++ b/forks/halo2curves/src/msm.rs @@ -0,0 +1,636 @@ +use std::ops::Neg; + +use ff::{Field, PrimeField}; +use group::Group; +use rayon::iter::{ + IndexedParallelIterator, IntoParallelRefIterator, IntoParallelRefMutIterator, ParallelIterator, +}; + +use crate::CurveAffine; + +const BATCH_SIZE: usize = 64; + +fn get_booth_index(window_index: usize, window_size: usize, el: &[u8]) -> i32 { + // Booth encoding: + // * step by `window` size + // * slice by size of `window + 1`` + // * each window overlap by 1 bit * append a zero bit to the least significant + // end + // Indexing rule for example window size 3 where we slice by 4 bits: + // `[0, +1, +1, +2, +2, +3, +3, +4, -4, -3, -3 -2, -2, -1, -1, 0]`` + // So we can reduce the bucket size without preprocessing scalars + // and remembering them as in classic signed digit encoding + + let skip_bits = (window_index * window_size).saturating_sub(1); + let skip_bytes = skip_bits / 8; + + // fill into a u32 + let mut v: [u8; 4] = [0; 4]; + for (dst, src) in v.iter_mut().zip(el.iter().skip(skip_bytes)) { + *dst = *src + } + let mut tmp = u32::from_le_bytes(v); + + // pad with one 0 if slicing the least significant window + if window_index == 0 { + tmp <<= 1; + } + + // remove further bits + tmp >>= skip_bits - (skip_bytes * 8); + // apply the booth window + tmp &= (1 << (window_size + 1)) - 1; + + let sign = tmp & (1 << window_size) == 0; + + // div ceil by 2 + tmp = (tmp + 1) >> 1; + + // find the booth action index + if sign { + tmp as i32 + } else { + ((!(tmp - 1) & ((1 << window_size) - 1)) as i32).neg() + } +} + +/// Batch addition. +fn batch_add( + size: usize, + buckets: &mut [BucketAffine], + points: &[SchedulePoint], + bases: &[Affine], +) { + let mut t = vec![C::Base::ZERO; size]; // Stores x2 - x1 + let mut z = vec![C::Base::ZERO; size]; // Stores y2 - y1 + let mut acc = C::Base::ONE; + + for ( + ( + SchedulePoint { + base_idx, + buck_idx, + sign, + }, + t, + ), + z, + ) in points.iter().zip(t.iter_mut()).zip(z.iter_mut()) + { + if buckets[*buck_idx].is_inf() { + // We assume bases[*base_idx] != infinity always. + continue; + } + + if buckets[*buck_idx].x() == bases[*base_idx].x { + // y-coordinate matches: + // 1. y1 == y2 and sign = false or + // 2. y1 != y2 and sign = true + // => ( y1 == y2) xor !sign + // (This uses the fact that x1 == x2 and both points satisfy the curve eq.) + if (buckets[*buck_idx].y() == bases[*base_idx].y) ^ !*sign { + // Doubling + let x_squared = bases[*base_idx].x.square(); + *z = buckets[*buck_idx].y() + buckets[*buck_idx].y(); // 2y + *t = acc * (x_squared + x_squared + x_squared); // acc * 3x^2 + acc *= *z; + continue; + } + // P + (-P) + buckets[*buck_idx].set_inf(); + continue; + } + // Addition + *z = buckets[*buck_idx].x() - bases[*base_idx].x; // x2 - x1 + if *sign { + *t = acc * (buckets[*buck_idx].y() - bases[*base_idx].y); + } else { + *t = acc * (buckets[*buck_idx].y() + bases[*base_idx].y); + } // y2 - y1 + acc *= *z; + } + + acc = acc + .invert() + .expect("Some edge case has not been handled properly"); + + for ( + ( + SchedulePoint { + base_idx, + buck_idx, + sign, + }, + t, + ), + z, + ) in points.iter().zip(t.iter()).zip(z.iter()).rev() + { + if buckets[*buck_idx].is_inf() { + // We assume bases[*base_idx] != infinity always. + continue; + } + let lambda = acc * t; + acc *= z; // update acc + let x = lambda.square() - (buckets[*buck_idx].x() + bases[*base_idx].x); // x_result + if *sign { + buckets[*buck_idx].set_y(&((lambda * (bases[*base_idx].x - x)) - bases[*base_idx].y)); + } else { + buckets[*buck_idx].set_y(&((lambda * (bases[*base_idx].x - x)) + bases[*base_idx].y)); + } // y_result = lambda * (x1 - x_result) - y1 + buckets[*buck_idx].set_x(&x); + } +} + +#[derive(Debug, Clone, Copy)] +struct Affine { + x: C::Base, + y: C::Base, +} + +impl Affine { + fn from(point: &C) -> Self { + let coords = point.coordinates().unwrap(); + + Self { + x: *coords.x(), + y: *coords.y(), + } + } + + fn neg(&self) -> Self { + Self { + x: self.x, + y: -self.y, + } + } + + fn eval(&self) -> C { + C::from_xy(self.x, self.y).unwrap() + } +} + +#[derive(Debug, Clone)] +enum BucketAffine { + None, + Point(Affine), +} + +#[derive(Debug, Clone)] +enum Bucket { + None, + Point(C::Curve), +} + +impl Bucket { + fn add_assign(&mut self, point: &C, sign: bool) { + *self = match *self { + Bucket::None => Bucket::Point({ + if sign { + point.to_curve() + } else { + point.to_curve().neg() + } + }), + Bucket::Point(a) => { + if sign { + Self::Point(a + point) + } else { + Self::Point(a - point) + } + } + } + } + + fn add(&self, other: &BucketAffine) -> C::Curve { + match (self, other) { + (Self::Point(this), BucketAffine::Point(other)) => *this + other.eval(), + (Self::Point(this), BucketAffine::None) => *this, + (Self::None, BucketAffine::Point(other)) => other.eval().to_curve(), + (Self::None, BucketAffine::None) => C::Curve::identity(), + } + } +} + +impl BucketAffine { + fn assign(&mut self, point: &Affine, sign: bool) -> bool { + match *self { + Self::None => { + *self = Self::Point(if sign { *point } else { point.neg() }); + true + } + Self::Point(_) => false, + } + } + + fn x(&self) -> C::Base { + match self { + Self::None => panic!("::x None"), + Self::Point(a) => a.x, + } + } + + fn y(&self) -> C::Base { + match self { + Self::None => panic!("::y None"), + Self::Point(a) => a.y, + } + } + + fn is_inf(&self) -> bool { + match self { + Self::None => true, + Self::Point(_) => false, + } + } + + fn set_x(&mut self, x: &C::Base) { + match self { + Self::None => panic!("::set_x None"), + Self::Point(ref mut a) => a.x = *x, + } + } + + fn set_y(&mut self, y: &C::Base) { + match self { + Self::None => panic!("::set_y None"), + Self::Point(ref mut a) => a.y = *y, + } + } + + fn set_inf(&mut self) { + match self { + Self::None => {} + Self::Point(_) => *self = Self::None, + } + } +} + +struct Schedule { + buckets: Vec>, + set: [SchedulePoint; BATCH_SIZE], + ptr: usize, +} + +#[derive(Debug, Clone, Default)] +struct SchedulePoint { + base_idx: usize, + buck_idx: usize, + sign: bool, +} + +impl SchedulePoint { + fn new(base_idx: usize, buck_idx: usize, sign: bool) -> Self { + Self { + base_idx, + buck_idx, + sign, + } + } +} + +impl Schedule { + fn new(c: usize) -> Self { + let set = (0..BATCH_SIZE) + .map(|_| SchedulePoint::default()) + .collect::>() + .try_into() + .unwrap(); + + Self { + buckets: vec![BucketAffine::None; 1 << (c - 1)], + set, + ptr: 0, + } + } + + fn contains(&self, buck_idx: usize) -> bool { + self.set.iter().any(|sch| sch.buck_idx == buck_idx) + } + + fn execute(&mut self, bases: &[Affine]) { + if self.ptr != 0 { + batch_add(self.ptr, &mut self.buckets, &self.set, bases); + self.ptr = 0; + self.set + .iter_mut() + .for_each(|sch| *sch = SchedulePoint::default()); + } + } + + fn add(&mut self, bases: &[Affine], base_idx: usize, buck_idx: usize, sign: bool) { + if !self.buckets[buck_idx].assign(&bases[base_idx], sign) { + self.set[self.ptr] = SchedulePoint::new(base_idx, buck_idx, sign); + self.ptr += 1; + } + + if self.ptr == self.set.len() { + self.execute(bases); + } + } +} + +/// Performs a multi-scalar multiplication operation. +/// +/// This function will panic if coeffs and bases have a different length. +pub fn msm_serial(coeffs: &[C::Scalar], bases: &[C], acc: &mut C::Curve) { + let coeffs: Vec<_> = coeffs.iter().map(|a| a.to_repr()).collect(); + + let c = if bases.len() < 4 { + 1 + } else if bases.len() < 32 { + 3 + } else { + (f64::from(bases.len() as u32)).ln().ceil() as usize + }; + + let field_byte_size = C::Scalar::NUM_BITS.div_ceil(8u32) as usize; + // OR all coefficients in order to make a mask to figure out the maximum number + // of bytes used among all coefficients. + let mut acc_or = vec![0; field_byte_size]; + for coeff in &coeffs { + for (acc_limb, limb) in acc_or.iter_mut().zip(coeff.as_ref().iter()) { + *acc_limb |= *limb; + } + } + let max_byte_size = field_byte_size + - acc_or + .iter() + .rev() + .position(|v| *v != 0) + .unwrap_or(field_byte_size); + if max_byte_size == 0 { + return; + } + let number_of_windows = max_byte_size * 8_usize / c + 1; + + for current_window in (0..number_of_windows).rev() { + for _ in 0..c { + *acc = acc.double(); + } + + #[derive(Clone, Copy)] + enum Bucket { + None, + Affine(C), + Projective(C::Curve), + } + + impl Bucket { + fn add_assign(&mut self, other: &C) { + *self = match *self { + Bucket::None => Bucket::Affine(*other), + Bucket::Affine(a) => Bucket::Projective(a + *other), + Bucket::Projective(mut a) => { + a += *other; + Bucket::Projective(a) + } + } + } + + fn add(self, mut other: C::Curve) -> C::Curve { + match self { + Bucket::None => other, + Bucket::Affine(a) => { + other += a; + other + } + Bucket::Projective(a) => other + a, + } + } + } + + let mut buckets: Vec> = vec![Bucket::None; 1 << (c - 1)]; + + for (coeff, base) in coeffs.iter().zip(bases.iter()) { + let coeff = get_booth_index(current_window, c, coeff.as_ref()); + if coeff.is_positive() { + buckets[coeff as usize - 1].add_assign(base); + } + if coeff.is_negative() { + buckets[coeff.unsigned_abs() as usize - 1].add_assign(&base.neg()); + } + } + + // Summation by parts + // e.g. 3a + 2b + 1c = a + + // (a) + b + + // ((a) + b) + c + let mut running_sum = C::Curve::identity(); + for exp in buckets.into_iter().rev() { + running_sum = exp.add(running_sum); + *acc += &running_sum; + } + } +} + +/// Performs a multi-scalar multiplication operation. +/// +/// This function will panic if coeffs and bases have a different length. +/// +/// This will use multithreading if beneficial. +pub fn msm_parallel(coeffs: &[C::Scalar], bases: &[C]) -> C::Curve { + assert_eq!(coeffs.len(), bases.len()); + + let num_threads = rayon::current_num_threads(); + if coeffs.len() > num_threads { + let chunk = coeffs.len() / num_threads; + let num_chunks = coeffs.chunks(chunk).len(); + let mut results = vec![C::Curve::identity(); num_chunks]; + rayon::scope(|scope| { + let chunk = coeffs.len() / num_threads; + + for ((coeffs, bases), acc) in coeffs + .chunks(chunk) + .zip(bases.chunks(chunk)) + .zip(results.iter_mut()) + { + scope.spawn(move |_| { + msm_serial(coeffs, bases, acc); + }); + } + }); + results.iter().fold(C::Curve::identity(), |a, b| a + b) + } else { + let mut acc = C::Curve::identity(); + msm_serial(coeffs, bases, &mut acc); + acc + } +} + +/// This function will panic if coeffs and bases have a different length. +/// +/// This will use multithreading if beneficial. +pub fn msm_best(coeffs: &[C::Scalar], bases: &[C]) -> C::Curve { + assert_eq!(coeffs.len(), bases.len()); + +//println!("msm_best coeffs.len(): {} ", coeffs.len()); + + // TODO: consider adjusting it with empirical data? + let c = if bases.len() < 4 { + 1 + } else if bases.len() < 32 { + 3 + } else { + (f64::from(bases.len() as u32)).ln().ceil() as usize + }; + + if c < 10 { + let mut acc = C::Curve::identity(); + msm_serial(coeffs, bases, &mut acc); + return acc; + //return msm_parallel(coeffs, bases); + } + + // coeffs to byte representation + let coeffs: Vec<_> = coeffs.par_iter().map(|a| a.to_repr()).collect(); + // copy bases into `Affine` to skip in on curve check for every access + let bases_local: Vec<_> = bases.par_iter().map(Affine::from).collect(); + + // number of windows + let number_of_windows = C::Scalar::NUM_BITS as usize / c + 1; + // accumumator for each window + let mut acc = vec![C::Curve::identity(); number_of_windows]; + acc.par_iter_mut().enumerate().rev().for_each(|(w, acc)| { + // jacobian buckets for already scheduled points + let mut j_bucks = vec![Bucket::::None; 1 << (c - 1)]; + + // schedular for affine addition + let mut sched = Schedule::new(c); + + for (base_idx, coeff) in coeffs.iter().enumerate() { + let buck_idx = get_booth_index(w, c, coeff.as_ref()); + + if buck_idx != 0 { + // parse bucket index + let sign = buck_idx.is_positive(); + let buck_idx = buck_idx.unsigned_abs() as usize - 1; + + if sched.contains(buck_idx) { + // greedy accumulation + // we use original bases here + j_bucks[buck_idx].add_assign(&bases[base_idx], sign); + } else { + // also flushes the schedule if full + sched.add(&bases_local, base_idx, buck_idx, sign); + } + } + } + + // flush the schedule + sched.execute(&bases_local); + + // summation by parts + // e.g. 3a + 2b + 1c = a + + // (a) + b + + // ((a) + b) + c + let mut running_sum = C::Curve::identity(); + for (j_buck, a_buck) in j_bucks.iter().zip(sched.buckets.iter()).rev() { + running_sum += j_buck.add(a_buck); + *acc += running_sum; + } + + // shift accumulator to the window position + for _ in 0..c * w { + *acc = acc.double(); + } + }); + acc.into_iter().sum::<_>() +} + +#[cfg(test)] +mod test { + use std::ops::Neg; + + use ark_std::{end_timer, start_timer}; + use ff::{Field, PrimeField}; + use group::{Curve, Group}; + use rand_core::OsRng; + + use crate::{ + bn256::{Fr, G1Affine, G1}, + CurveAffine, + }; + + #[test] + fn test_booth_encoding() { + fn mul(scalar: &Fr, point: &G1Affine, window: usize) -> G1Affine { + let u = scalar.to_repr(); + let n = Fr::NUM_BITS as usize / window + 1; + + let table = (0..=1 << (window - 1)) + .map(|i| point * Fr::from(i as u64)) + .collect::>(); + + let mut acc = G1::identity(); + for i in (0..n).rev() { + for _ in 0..window { + acc = acc.double(); + } + + let idx = super::get_booth_index(i, window, u.as_ref()); + + if idx.is_negative() { + acc += table[idx.unsigned_abs() as usize].neg(); + } + if idx.is_positive() { + acc += table[idx.unsigned_abs() as usize]; + } + } + + acc.to_affine() + } + + let (scalars, points): (Vec<_>, Vec<_>) = (0..10) + .map(|_| { + let scalar = Fr::random(OsRng); + let point = G1Affine::random(OsRng); + (scalar, point) + }) + .unzip(); + + for window in 1..10 { + for (scalar, point) in scalars.iter().zip(points.iter()) { + let c0 = mul(scalar, point, window); + let c1 = point * scalar; + assert_eq!(c0, c1.to_affine()); + } + } + } + + fn run_msm_cross(min_k: usize, max_k: usize) { + use rayon::iter::{IntoParallelIterator, ParallelIterator}; + + let points = (0..1 << max_k) + .into_par_iter() + .map(|_| C::Curve::random(OsRng)) + .collect::>(); + let mut affine_points = vec![C::identity(); 1 << max_k]; + C::Curve::batch_normalize(&points[..], &mut affine_points[..]); + let points = affine_points; + + let scalars = (0..1 << max_k) + .into_par_iter() + .map(|_| C::Scalar::random(OsRng)) + .collect::>(); + + for k in min_k..=max_k { + let points = &points[..1 << k]; + let scalars = &scalars[..1 << k]; + + let t0 = start_timer!(|| format!("cyclone indep k={}", k)); + let e0 = super::msm_best(scalars, points); + end_timer!(t0); + + let t1 = start_timer!(|| format!("older k={}", k)); + let e1 = super::msm_parallel(scalars, points); + end_timer!(t1); + assert_eq!(e0, e1); + } + } + + #[test] + fn test_msm_cross() { + run_msm_cross::(14, 18); + } +} diff --git a/forks/halo2curves/src/pasta/curve.rs b/forks/halo2curves/src/pasta/curve.rs new file mode 100644 index 00000000..8dd48b94 --- /dev/null +++ b/forks/halo2curves/src/pasta/curve.rs @@ -0,0 +1,78 @@ +use std::convert::TryInto; + +use ff::{PrimeField, WithSmallOrderMulGroup}; + +use super::{fp::Fp, fq::Fq, Pallas, Vesta}; +use crate::{ + arithmetic::{mul_512, sbb, CurveEndo, EndoParameters}, + endo, +}; + +// Generated using https://github.com/ConsenSys/gnark-crypto/blob/master/ecc/utils.go +// with `pasta_curves::Fp::ZETA` +// See https://github.com/demining/Endomorphism-Secp256k1/blob/main/README.md +// to have more details about the endomorphism. +const ENDO_PARAMS_EQ: EndoParameters = EndoParameters { + // round(b2/n) + gamma1: [0x32c49e4c00000003, 0x279a745902a2654e, 0x1, 0x0], + // round(-b1/n) + gamma2: [0x31f0256800000002, 0x4f34e8b2066389a4, 0x2, 0x0], + b1: [0x8cb1279300000001, 0x49e69d1640a89953, 0x0, 0x0], + b2: [0x0c7c095a00000001, 0x93cd3a2c8198e269, 0x0, 0x0], +}; + +// Generated using https://github.com/ConsenSys/gnark-crypto/blob/master/ecc/utils.go +// with `pasta_curves::Fq::ZETA` +// See https://github.com/demining/Endomorphism-Secp256k1/blob/main/README.md +// to have more details about the endomorphism. +const ENDO_PARAMS_EP: EndoParameters = EndoParameters { + // round(b2/n) + gamma1: [0x32c49e4bffffffff, 0x279a745902a2654e, 0x1, 0x0], + // round(-b1/n) + gamma2: [0x31f0256800000002, 0x4f34e8b2066389a4, 0x2, 0x0], + b1: [0x8cb1279300000000, 0x49e69d1640a89953, 0x0, 0x0], + b2: [0x0c7c095a00000001, 0x93cd3a2c8198e269, 0x0, 0x0], +}; + +endo!(Vesta, Fp, ENDO_PARAMS_EQ); +endo!(Pallas, Fq, ENDO_PARAMS_EP); + +#[cfg(test)] +mod test { + use rand_core::OsRng; + + use super::*; + + #[test] + fn test_endo() { + use ff::Field; + + for _ in 0..100000 { + let k = Fp::random(OsRng); + let (k1, k1_neg, k2, k2_neg) = Vesta::decompose_scalar(&k); + if k1_neg & k2_neg { + assert_eq!(k, -Fp::from_u128(k1) + Fp::ZETA * Fp::from_u128(k2)) + } else if k1_neg { + assert_eq!(k, -Fp::from_u128(k1) - Fp::ZETA * Fp::from_u128(k2)) + } else if k2_neg { + assert_eq!(k, Fp::from_u128(k1) + Fp::ZETA * Fp::from_u128(k2)) + } else { + assert_eq!(k, Fp::from_u128(k1) - Fp::ZETA * Fp::from_u128(k2)) + } + } + + for _ in 0..100000 { + let k = Fp::random(OsRng); + let (k1, k1_neg, k2, k2_neg) = Vesta::decompose_scalar(&k); + if k1_neg & k2_neg { + assert_eq!(k, -Fp::from_u128(k1) + Fp::ZETA * Fp::from_u128(k2)) + } else if k1_neg { + assert_eq!(k, -Fp::from_u128(k1) - Fp::ZETA * Fp::from_u128(k2)) + } else if k2_neg { + assert_eq!(k, Fp::from_u128(k1) + Fp::ZETA * Fp::from_u128(k2)) + } else { + assert_eq!(k, Fp::from_u128(k1) - Fp::ZETA * Fp::from_u128(k2)) + } + } + } +} diff --git a/forks/halo2curves/src/pasta/fp.rs b/forks/halo2curves/src/pasta/fp.rs new file mode 100644 index 00000000..c6cdcf0a --- /dev/null +++ b/forks/halo2curves/src/pasta/fp.rs @@ -0,0 +1,46 @@ +use std::convert::TryInto; + +use halo2derive::impl_field; +use rand::RngCore; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +use crate::{ + extend_field_legendre, field_bits, impl_binops_additive, impl_binops_calls, + impl_binops_multiplicative, impl_from_bool, impl_from_u64, serialize_deserialize_primefield, +}; + +// Fp: Pasta base field and Vesta scalar field. +impl_field!( + pasta_base, + Fp, + modulus = "40000000000000000000000000000000224698fc094cf91b992d30ed00000001", + mul_gen = "5", + zeta = "12ccca834acdba712caad5dc57aab1b01d1f8bd237ad31491dad5ebdfdfe4ab9", + from_uniform = [48, 64], + endian = "little", +); + +extend_field_legendre!(Fp); +impl_binops_calls!(Fp); +impl_binops_additive!(Fp, Fp); +impl_binops_multiplicative!(Fp, Fp); +impl_from_u64!(Fp); +impl_from_bool!(Fp); +field_bits!(Fp); +serialize_deserialize_primefield!(Fp); + +#[cfg(test)] +mod test { + + use super::Fp; + use crate::{ + arith_test, constants_test, from_uniform_bytes_test, legendre_test, serde_test, test, + }; + + constants_test!(Fp); + arith_test!(Fp); + legendre_test!(Fp); + test!(arith, Fp, sqrt_test, 1000); + serde_test!(Fp PrimeFieldBits); + from_uniform_bytes_test!(Fp, 1000, L 64, L 48); +} diff --git a/forks/halo2curves/src/pasta/fq.rs b/forks/halo2curves/src/pasta/fq.rs new file mode 100644 index 00000000..d6384e3c --- /dev/null +++ b/forks/halo2curves/src/pasta/fq.rs @@ -0,0 +1,45 @@ +use std::convert::TryInto; + +use halo2derive::impl_field; +use rand::RngCore; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +use crate::{ + extend_field_legendre, field_bits, impl_binops_additive, impl_binops_calls, + impl_binops_multiplicative, impl_from_bool, impl_from_u64, serialize_deserialize_primefield, +}; + +// Fq: Vesta base field and Pasta scalar field. +impl_field!( + vesta_base, + Fq, + modulus = "40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001", + mul_gen = "5", + zeta = "06819a58283e528e511db4d81cf70f5a0fed467d47c033af2aa9d2e050aa0e4f", + from_uniform = [48, 64], + endian = "little", +); + +extend_field_legendre!(Fq); +impl_binops_calls!(Fq); +impl_binops_additive!(Fq, Fq); +impl_binops_multiplicative!(Fq, Fq); +impl_from_u64!(Fq); +impl_from_bool!(Fq); +field_bits!(Fq); +serialize_deserialize_primefield!(Fq); + +#[cfg(test)] +mod test { + use super::Fq; + use crate::{ + arith_test, constants_test, from_uniform_bytes_test, legendre_test, serde_test, test, + }; + + constants_test!(Fq); + arith_test!(Fq); + legendre_test!(Fq); + test!(arith, Fq, sqrt_test, 1000); + serde_test!(Fq PrimeFieldBits); + from_uniform_bytes_test!(Fq, 1000, L 64, L 48); +} diff --git a/forks/halo2curves/src/pasta/mod.rs b/forks/halo2curves/src/pasta/mod.rs new file mode 100644 index 00000000..54a43573 --- /dev/null +++ b/forks/halo2curves/src/pasta/mod.rs @@ -0,0 +1,10 @@ +mod curve; +mod fp; +mod fq; +mod pallas; +mod vesta; + +pub use fp::*; +pub use fq::*; +pub use pallas::*; +pub use vesta::*; diff --git a/forks/halo2curves/src/pasta/pallas.rs b/forks/halo2curves/src/pasta/pallas.rs new file mode 100644 index 00000000..a251d865 --- /dev/null +++ b/forks/halo2curves/src/pasta/pallas.rs @@ -0,0 +1,91 @@ +use core::{ + cmp, + fmt::Debug, + iter::Sum, + ops::{Add, Mul, Neg, Sub}, +}; + +use ff::{Field, PrimeField, WithSmallOrderMulGroup}; +use rand::RngCore; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +use super::{fp::Fp, fq::Fq}; +use crate::{ + group::{cofactor::CofactorGroup, prime::PrimeCurveAffine, Curve, Group, GroupEncoding}, + impl_binops_additive, impl_binops_additive_specify_output, impl_binops_multiplicative, + impl_binops_multiplicative_mixed, new_curve_impl, Coordinates, CurveAffine, CurveExt, +}; + +new_curve_impl!( + (pub), + Pallas, + PallasAffine, + Fp, + Fq, + (- Fp::ONE, Fp::from_raw([2,0,0,0])), + Fp::ZERO, + Fp::from_raw([5,0,0,0]), + "pasta", + |domain_prefix| crate::hash_to_curve::hash_to_curve(domain_prefix, Pallas::default_hash_to_curve_suite()), + crate::serde::CompressedFlagConfig::SingleSpare, + standard_sign +); + +impl CofactorGroup for Pallas { + type Subgroup = Pallas; + + fn clear_cofactor(&self) -> Self { + *self + } + + fn into_subgroup(self) -> CtOption { + CtOption::new(self, 1.into()) + } + + fn is_torsion_free(&self) -> Choice { + 1.into() + } +} + +impl Pallas { + /// Z = -13 + pub const SVDW_Z: Fp = Fp::from_raw([ + 0x992d30ecfffffff4, + 0x224698fc094cf91b, + 0x0000000000000000, + 0x4000000000000000, + ]); + + fn default_hash_to_curve_suite() -> crate::hash_to_curve::Suite { + crate::hash_to_curve::Suite::::new( + b"pallas:SHA-256_SVDW_RO_", + Self::SVDW_Z, + crate::hash_to_curve::Method::SVDW, + ) + } +} + +#[cfg(test)] +mod test { + + use group::UncompressedEncoding; + use rand_core::OsRng; + + use super::*; + use crate::{curve_testing_suite, serde::SerdeObject}; + + curve_testing_suite!( + Pallas, + "constants", + Fp::MODULUS, + Fp::ZERO, + Fp::from_raw([5, 0, 0, 0]), + -Fp::ONE, + Fp::from_raw([2, 0, 0, 0]), + Fq::MODULUS + ); + + curve_testing_suite!(Pallas); + curve_testing_suite!(Pallas, "endo_consistency"); + curve_testing_suite!(Pallas, "ecdsa_example"); +} diff --git a/forks/halo2curves/src/pasta/vesta.rs b/forks/halo2curves/src/pasta/vesta.rs new file mode 100644 index 00000000..13603eac --- /dev/null +++ b/forks/halo2curves/src/pasta/vesta.rs @@ -0,0 +1,91 @@ +use core::{ + cmp, + fmt::Debug, + iter::Sum, + ops::{Add, Mul, Neg, Sub}, +}; + +use ff::{Field, PrimeField, WithSmallOrderMulGroup}; +use rand::RngCore; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +use super::{fp::Fp, fq::Fq}; +use crate::{ + group::{cofactor::CofactorGroup, prime::PrimeCurveAffine, Curve, Group, GroupEncoding}, + impl_binops_additive, impl_binops_additive_specify_output, impl_binops_multiplicative, + impl_binops_multiplicative_mixed, new_curve_impl, Coordinates, CurveAffine, CurveExt, +}; + +new_curve_impl!( + (pub), + Vesta, + VestaAffine, + Fq, + Fp, + (- Fq::ONE, Fq::from_raw([2,0,0,0])), + Fq::ZERO,// Curve a parameter + Fq::from_raw([5,0,0,0]),// Curve b parameter + "vesta", + |domain_prefix| crate::hash_to_curve::hash_to_curve(domain_prefix, Vesta::default_hash_to_curve_suite()), + crate::serde::CompressedFlagConfig::SingleSpare, + standard_sign +); + +// NOTE: Temporary impl to satisfy macro requirements + +impl CofactorGroup for Vesta { + type Subgroup = Vesta; + + fn clear_cofactor(&self) -> Self { + *self + } + + fn into_subgroup(self) -> CtOption { + CtOption::new(self, 1.into()) + } + + fn is_torsion_free(&self) -> Choice { + 1.into() + } +} + +impl Vesta { + /// Z = -13 + pub const SVDW_Z: Fq = Fq::from_raw([ + 0x8c46eb20fffffff4, + 0x224698fc0994a8dd, + 0x0000000000000000, + 0x4000000000000000, + ]); + fn default_hash_to_curve_suite() -> crate::hash_to_curve::Suite { + crate::hash_to_curve::Suite::::new( + b"vesta:SHA-256_SVDW_RO_", + Self::SVDW_Z, + crate::hash_to_curve::Method::SVDW, + ) + } +} + +#[cfg(test)] +mod test { + use group::UncompressedEncoding; + use rand_core::OsRng; + + use super::*; + use crate::{curve_testing_suite, serde::SerdeObject}; + + curve_testing_suite!( + Vesta, + "constants", + Fq::MODULUS, + Fq::ZERO, + Fq::from_raw([5, 0, 0, 0]), + -Fq::ONE, + Fq::from_raw([2, 0, 0, 0]), + Fp::MODULUS + ); + + curve_testing_suite!(Vesta); + curve_testing_suite!(Vesta, "endo_consistency"); + curve_testing_suite!(Vesta, "ecdsa_example"); +} diff --git a/forks/halo2curves/src/pluto_eris/curve.rs b/forks/halo2curves/src/pluto_eris/curve.rs new file mode 100644 index 00000000..cbe3b7c0 --- /dev/null +++ b/forks/halo2curves/src/pluto_eris/curve.rs @@ -0,0 +1,315 @@ +use core::{ + cmp, + fmt::Debug, + iter::Sum, + ops::{Add, Mul, Neg, Sub}, +}; + +use group::cofactor::CofactorGroup; +use rand::RngCore; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +use super::{fp::Fp, fp2::Fp2, fq::Fq}; +use crate::{ + ff::{Field, PrimeField, WithSmallOrderMulGroup}, + group::{prime::PrimeCurveAffine, Curve, Group as _, GroupEncoding}, + impl_binops_additive, impl_binops_additive_specify_output, impl_binops_multiplicative, + impl_binops_multiplicative_mixed, new_curve_impl, Coordinates, CurveAffine, CurveExt, +}; + +const G1_GENERATOR_X: Fp = Fp::from_raw([ + 0x9ffffcd2ffffffff, + 0xa2a7e8c30006b945, + 0xe4a7a5fe8fadffd6, + 0x443f9a5cda8a6c7b, + 0xa803ca76f439266f, + 0x0130e0000d7f70e4, + 0x2400000000002400, +]); +const G1_GENERATOR_Y: Fp = Fp::from_raw([0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); + +const PLUTO_A: Fp = Fp::ZERO; +const PLUTO_B: Fp = Fp::from_raw([0x39, 0, 0, 0, 0, 0, 0]); + +const ERIS_GENERATOR_X: Fq = Fq::from_raw([ + 0x1ffffcd2ffffffff, + 0x9ca7e85d60050af4, + 0xe4a775fe8e177fd6, + 0x443f9a5c7a8a6c7b, + 0xa803ca76f439266f, + 0x0130e0000d7f70e4, + 0x2400000000002400, +]); +const ERIS_GENERATOR_Y: Fq = Fq::from_raw([0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); + +const ERIS_A: Fq = Fq::ZERO; +const ERIS_B: Fq = Fq::from_raw([0x39, 0, 0, 0, 0, 0, 0]); + +const G2_GENERATOR_X: Fp2 = Fp2 { + // 0x13576c81faf3a13fd815d0e9bd54b845ee935948b84498b27ca972bfb93722e223c9e276a4ebe7559cfc86dd865f07d64f2b5fe6556f9066 + c0: Fp::from_raw([ + 0x4f2b5fe6556f9066, + 0x9cfc86dd865f07d6, + 0x23c9e276a4ebe755, + 0x7ca972bfb93722e2, + 0xee935948b84498b2, + 0xd815d0e9bd54b845, + 0x13576c81faf3a13f, + ]), + + //0x142164cb875db0465e5092f9380f44f555243d011699b7393029f2d201554727aeb383298fdf5847b9b3dff01bbe8d63fe7c781a8fd7bf21 + c1: Fp::from_raw([ + 0xfe7c781a8fd7bf21, + 0xb9b3dff01bbe8d63, + 0xaeb383298fdf5847, + 0x3029f2d201554727, + 0x55243d011699b739, + 0x5e5092f9380f44f5, + 0x142164cb875db046, + ]), +}; +const G2_GENERATOR_Y: Fp2 = Fp2 { + //0x2239f7408ead478c58e88d4df1e7418c42fdbb92e64ba85aa4dc17d7dace3f32eb471c004db774bfe78574aca67b3898cd1b78ad106ab9fe + c0: Fp::from_raw([ + 0xcd1b78ad106ab9fe, + 0xe78574aca67b3898, + 0xeb471c004db774bf, + 0xa4dc17d7dace3f32, + 0x42fdbb92e64ba85a, + 0x58e88d4df1e7418c, + 0x2239f7408ead478c, + ]), + + // 0x1260b04d51136590dbb53dfd7caf450aeca714555bbe4f079ca65d97eb28fc9fc697b4e10bbcd9e0539ef82a731fb88ed49e3c080e6d945d + c1: Fp::from_raw([ + 0xd49e3c080e6d945d, + 0x539ef82a731fb88e, + 0xc697b4e10bbcd9e0, + 0x9ca65d97eb28fc9f, + 0xeca714555bbe4f07, + 0xdbb53dfd7caf450a, + 0x1260b04d51136590, + ]), +}; + +const TRITON_A: Fp2 = Fp2::ZERO; + +// u + 3 +const TRITON_B: Fp2 = Fp2 { + c0: Fp::from_raw([0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), + c1: Fp::ONE, +}; + +impl CofactorGroup for G1 { + type Subgroup = G1; + + fn clear_cofactor(&self) -> Self { + *self + } + + fn into_subgroup(self) -> CtOption { + CtOption::new(self, 1.into()) + } + + fn is_torsion_free(&self) -> Choice { + 1.into() + } +} + +new_curve_impl!( + (pub), + G1, + G1Affine, + Fp, + Fq, + (G1_GENERATOR_X,G1_GENERATOR_Y), + PLUTO_A, + PLUTO_B, + "pluto", + |domain_prefix| crate::hash_to_curve::hash_to_curve(domain_prefix, G1::default_hash_to_curve_suite()), + crate::serde::CompressedFlagConfig::TwoSpare, + standard_sign +); + +impl group::cofactor::CofactorGroup for Eris { + type Subgroup = Eris; + + fn clear_cofactor(&self) -> Self { + *self + } + + fn into_subgroup(self) -> CtOption { + CtOption::new(self, 1.into()) + } + + fn is_torsion_free(&self) -> Choice { + 1.into() + } +} + +impl G1 { + /// Constant Z for the Shallue-van de Woestijne map. + /// Computed using https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-10.html#svdw-z-code + const SVDW_Z: Fp = Fp::ONE; + + fn default_hash_to_curve_suite() -> crate::hash_to_curve::Suite { + crate::hash_to_curve::Suite::::new( + b"pluto_XMD:SHA-256_SVDW_RO_", + Self::SVDW_Z, + crate::hash_to_curve::Method::SVDW, + ) + } +} + +new_curve_impl!( + (pub), + Eris, + ErisAffine, + Fq, + Fp, + (ERIS_GENERATOR_X,ERIS_GENERATOR_Y), + ERIS_A, + ERIS_B, + "eris", + |domain_prefix| crate::hash_to_curve::hash_to_curve(domain_prefix, Eris::default_hash_to_curve_suite()), + crate::serde::CompressedFlagConfig::TwoSpare, + standard_sign +); + +impl CofactorGroup for G2 { + type Subgroup = G2; + + fn clear_cofactor(&self) -> Self { + // cofactor = 2*p - q + //0x24000000000024000130e0000d7f70e4a803ca76f439266f443f9a5d3a8a6c7be4a7d5fe91447fd6a8a7e928a00867971ffffcd300000001 + let e: [u8; 56] = [ + 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x01, 0x30, 0xe0, 0x00, 0x0d, 0x7f, + 0x70, 0xe4, 0xa8, 0x03, 0xca, 0x76, 0xf4, 0x39, 0x26, 0x6f, 0x44, 0x3f, 0x9a, 0x5d, + 0x3a, 0x8a, 0x6c, 0x7b, 0xe4, 0xa7, 0xd5, 0xfe, 0x91, 0x44, 0x7f, 0xd6, 0xa8, 0xa7, + 0xe9, 0x28, 0xa0, 0x08, 0x67, 0x97, 0x1f, 0xff, 0xfc, 0xd3, 0x00, 0x00, 0x00, 0x01, + ]; + + // self * TRITON_COFACTOR + let mut acc = G2::identity(); + for bit in e + .iter() + .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) + .skip(1) + { + acc = acc.double(); + acc = G2::conditional_select(&acc, &(acc + self), bit); + } + acc + } + + fn into_subgroup(self) -> CtOption { + // TODO: Handle the case where the point is already in the subgroup. + CtOption::new(self.clear_cofactor(), 1.into()) + } + + fn is_torsion_free(&self) -> Choice { + // group order = q + let e: [u8; 56] = [ + 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x01, 0x30, 0xe0, 0x00, 0x0d, 0x7f, + 0x70, 0xe4, 0xa8, 0x03, 0xca, 0x76, 0xf4, 0x39, 0x26, 0x6f, 0x44, 0x3f, 0x9a, 0x5c, + 0x7a, 0x8a, 0x6c, 0x7b, 0xe4, 0xa7, 0x75, 0xfe, 0x8e, 0x17, 0x7f, 0xd6, 0x9c, 0xa7, + 0xe8, 0x5d, 0x60, 0x05, 0x0a, 0xf4, 0x1f, 0xff, 0xfc, 0xd3, 0x00, 0x00, 0x00, 0x01, + ]; + // self * GROUP_ORDER; + let mut acc = G2::identity(); + for bit in e + .iter() + .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) + .skip(1) + { + acc = acc.double(); + acc = G2::conditional_select(&acc, &(acc + self), bit); + } + acc.is_identity() + } +} + +impl Eris { + /// Constant Z for the Shallue-van de Woestijne map. + /// Computed using https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-10.html#svdw-z-code + const SVDW_Z: Fq = Fq::ONE; + + fn default_hash_to_curve_suite() -> crate::hash_to_curve::Suite { + crate::hash_to_curve::Suite::::new( + b"eris_XMD:SHA-256_SVDW_RO_", + Self::SVDW_Z, + crate::hash_to_curve::Method::SVDW, + ) + } +} + +impl crate::serde::endian::EndianRepr for Fp2 { + const ENDIAN: crate::serde::endian::Endian = Fq::ENDIAN; + + fn to_bytes(&self) -> Vec { + self.to_bytes().to_vec() + } + + fn from_bytes(bytes: &[u8]) -> subtle::CtOption { + Fp2::from_bytes(bytes[..Fp2::SIZE].try_into().unwrap()) + } +} + +new_curve_impl!( + (pub), + G2, + G2Affine, + Fp2, + Fq, + (G2_GENERATOR_X,G2_GENERATOR_Y), + TRITON_A, + TRITON_B, + "triton", + |_| unimplemented!(), + crate::serde::CompressedFlagConfig::TwoSpare, + standard_sign +); + +#[cfg(test)] +mod test { + use group::UncompressedEncoding; + use rand_core::OsRng; + + use super::*; + use crate::serde::SerdeObject; + + crate::curve_testing_suite!(G2, "clear_cofactor"); + crate::curve_testing_suite!(G1, Eris, G2); + crate::curve_testing_suite!(G1, Eris, "hash_to_curve"); + crate::curve_testing_suite!(G1, Eris, "endo_consistency"); + crate::curve_testing_suite!( + G1, + "constants", + Fp::MODULUS, + PLUTO_A, + PLUTO_B, + G1_GENERATOR_X, + G1_GENERATOR_Y, + Fq::MODULUS + ); + crate::curve_testing_suite!( + Eris, + "constants", + Fq::MODULUS, + ERIS_A, + ERIS_B, + ERIS_GENERATOR_X, + ERIS_GENERATOR_Y, + Fp::MODULUS + ); + crate::curve_testing_suite!( + G2, + "constants", + Fp2::MODULUS, + TRITON_A, + TRITON_B, + G2_GENERATOR_X, + G2_GENERATOR_Y, + Fq::MODULUS + ); +} diff --git a/forks/halo2curves/src/pluto_eris/engine.rs b/forks/halo2curves/src/pluto_eris/engine.rs new file mode 100644 index 00000000..5e78d1d8 --- /dev/null +++ b/forks/halo2curves/src/pluto_eris/engine.rs @@ -0,0 +1,242 @@ +#![allow(clippy::suspicious_arithmetic_impl)] + +use core::{ + borrow::Borrow, + iter::Sum, + ops::{Add, Mul, Neg, Sub}, +}; +use std::ops::MulAssign; + +use ff::Field; +use pairing::{Engine, MillerLoopResult, MultiMillerLoop, PairingCurveAffine}; +use rand_core::RngCore; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; + +use crate::{ + ff::PrimeField, + ff_ext::{quadratic::QuadSparseMul, ExtField}, + group::{cofactor::CofactorCurveAffine, Group}, + pluto_eris::{curve::*, fp::*, fp12::*, fp2::*, fp6::FROBENIUS_COEFF_FP6_C1, fq::Fq}, +}; + +/// Adaptation of Algorithm 1, https://eprint.iacr.org/2013/722.pdf +/// the parameter for the curve Pluto: u = -0x4000000000001000008780000000 +const NEG_PLUTO_U: u128 = 0x4000000000001000008780000000; + +const NEG_SIX_U_PLUS_2_NAF: [i8; 114] = [ + 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, -1, 0, -1, 0, 1, 0, -1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 1, +]; + +crate::impl_gt!(Gt, Fp12, Fq); +crate::impl_miller_loop_components!(Pluto, G1, G1Affine, G2, G2Affine, Fp12, Gt, Fq); + +pub fn multi_miller_loop(terms: &[(&G1Affine, &G2Affine)]) -> Fp12 { + let terms = terms + .iter() + .filter_map(|&(p, q)| { + if bool::from(p.is_identity()) || bool::from(q.is_identity()) { + None + } else { + Some((*p, *q)) + } + }) + .collect::>(); + + let mut f = Fp12::one(); + let mut r = terms.iter().map(|(_, q)| q.to_curve()).collect::>(); + + for (i, x) in NEG_SIX_U_PLUS_2_NAF.iter().rev().skip(1).enumerate() { + (i != 0).then(|| f.square_assign()); + + for ((p, _), r) in terms.iter().zip(r.iter_mut()) { + double(&mut f, r, p); + } + + match x { + &val @ (1 | -1) => { + for ((p, q), r) in terms.iter().zip(r.iter_mut()) { + if val == 1 { + add(&mut f, r, q, p); + } else { + add(&mut f, r, &q.neg(), p); + } + } + } + _ => continue, + } + } + + /// Value of (57/(u + 3))^((p - 1)/2) where u^2 + 5 = 0 in Fp2. + const XI_TO_P_MINUS_1_OVER_2: Fp2 = Fp2 { + c0: Fp::from_raw([ + 0x54cf5ad1c0926216, + 0x186c1f3ce4a46d4e, + 0x9c23800ce9c9452f, + 0x50e0d09ff6d6c08b, + 0x7cf421e4d46f6666, + 0x678664ba4b6d8343, + 0x21cc26d5de0f80f4, + ]), + + c1: Fp::from_raw([ + 0xc0505f4c260e91f4, + 0xe7bbd15f10723657, + 0xb4b3e0c35358097e, + 0x87c56f42a558750d, + 0x4b7211d23f34f0ae, + 0xf6839d29e2f0d250, + 0x16ebe8b2e12a1106, + ]), + }; + + for ((p, q), r) in terms.iter().zip(r.iter_mut()) { + let mut q1: G2Affine = *q; + q1.x.conjugate(); + q1.x.mul_assign(&FROBENIUS_COEFF_FP6_C1[1]); + q1.y.conjugate(); + q1.y.mul_assign(&XI_TO_P_MINUS_1_OVER_2); + add(&mut f, r, &q1.neg(), p); + } + + for ((p, q), r) in terms.iter().zip(r.iter_mut()) { + let mut minusq2: G2Affine = *q; + minusq2.x.mul_assign(&FROBENIUS_COEFF_FP6_C1[2]); + add(&mut f, r, &minusq2.neg(), p); + } + + f +} + +impl MillerLoopResult for Fp12 { + type Gt = Gt; + + fn final_exponentiation(&self) -> Gt { + fn exp_by_x(f: &mut Fp12) { + let x = NEG_PLUTO_U; + let mut res = Fp12::one(); + for i in (0..111).rev() { + res.cyclotomic_square(); + if ((x >> i) & 1) == 1 { + res.mul_assign(f); + } + } + res.conjugate(); + *f = res; + } + + let r = *self; + let mut f1 = *self; + f1.conjugate(); + + Gt(r.invert() + .map(|mut f2| { + let mut r = f1; + r.mul_assign(&f2); + f2 = r; + r.frobenius_map(2); + r.mul_assign(&f2); + + let mut fp = r; + fp.frobenius_map(1); + + let mut fp2 = r; + fp2.frobenius_map(2); + let mut fp3 = fp2; + fp3.frobenius_map(1); + + let mut fu = r; + exp_by_x(&mut fu); + + let mut fu2 = fu; + exp_by_x(&mut fu2); + + let mut fu3 = fu2; + exp_by_x(&mut fu3); + + let mut y3 = fu; + y3.frobenius_map(1); + + let mut fu2p = fu2; + fu2p.frobenius_map(1); + + let mut fu3p = fu3; + fu3p.frobenius_map(1); + + let mut y2 = fu2; + y2.frobenius_map(2); + + let mut y0 = fp; + y0.mul_assign(&fp2); + y0.mul_assign(&fp3); + + let mut y1 = r; + y1.conjugate(); + + let mut y5 = fu2; + y5.conjugate(); + + y3.conjugate(); + + let mut y4 = fu; + y4.mul_assign(&fu2p); + y4.conjugate(); + + let mut y6 = fu3; + y6.mul_assign(&fu3p); + y6.conjugate(); + + y6.cyclotomic_square(); + y6.mul_assign(&y4); + y6.mul_assign(&y5); + + let mut t1 = y3; + t1.mul_assign(&y5); + t1.mul_assign(&y6); + + y6.mul_assign(&y2); + + t1.cyclotomic_square(); + t1.mul_assign(&y6); + t1.cyclotomic_square(); + + let mut t0 = t1; + t0.mul_assign(&y1); + + t1.mul_assign(&y0); + + t0.cyclotomic_square(); + t0.mul_assign(&t1); + + t0 + }) + .unwrap()) + } +} + +// Final steps of the line function on prepared coefficients +fn ell(f: &mut Fp12, coeffs: &(Fp2, Fp2, Fp2), p: &G1Affine) { + let mut c0 = coeffs.0; + let mut c1 = coeffs.1; + c0.c0.mul_assign(&p.y); + c0.c1.mul_assign(&p.y); + c1.c0.mul_assign(&p.x); + c1.c1.mul_assign(&p.x); + Fp12::mul_by_034(f, &c0, &c1, &coeffs.2); +} + +#[cfg(test)] +mod test { + use ff::Field; + use group::{prime::PrimeCurveAffine, Curve, Group}; + use pairing::{Engine, MillerLoopResult, PairingCurveAffine}; + use rand_core::OsRng; + + use super::{ + super::{Fq, Pluto, G1, G2}, + multi_miller_loop, Fp12, G1Affine, G2Affine, Gt, + }; + crate::test_pairing!(Pluto, G1, G1Affine, G2, G2Affine, Fp12, Gt, Fq); +} diff --git a/forks/halo2curves/src/pluto_eris/fp.rs b/forks/halo2curves/src/pluto_eris/fp.rs new file mode 100644 index 00000000..b712e69d --- /dev/null +++ b/forks/halo2curves/src/pluto_eris/fp.rs @@ -0,0 +1,59 @@ +use core::convert::TryInto; + +use halo2derive::impl_field; +use rand::RngCore; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +use crate::ff_ext::ExtField; + +impl_field!( + pluto_eris_fp, + Fp, + modulus = "24000000000024000130e0000d7f70e4a803ca76f439266f443f9a5cda8a6c7be4a7a5fe8fadffd6a2a7e8c30006b9459ffffcd300000001", + mul_gen = "a", + zeta = "480000000000360001c950000d7ee0e4a803c956d01c903d720dc8ad8b38dffaf50c100004c37ffffffe", + from_uniform = [64, 72, 112], + endian = "little", +); + +crate::extend_field_legendre!(Fp); +crate::impl_binops_calls!(Fp); +crate::impl_binops_additive!(Fp, Fp); +crate::impl_binops_multiplicative!(Fp, Fp); +crate::field_bits!(Fp); +crate::serialize_deserialize_primefield!(Fp); +crate::impl_from_u64!(Fp); +crate::impl_from_bool!(Fp); + +impl ExtField for Fp { + const NON_RESIDUE: Self = Fp::from_raw([ + 0x9ffffcd2fffffffc, + 0xa2a7e8c30006b945, + 0xe4a7a5fe8fadffd6, + 0x443f9a5cda8a6c7b, + 0xa803ca76f439266f, + 0x0130e0000d7f70e4, + 0x2400000000002400, + ]); + fn mul_by_nonresidue(&self) -> Self { + (self.double().double() + self).neg() + } + fn frobenius_map(&mut self, _: usize) {} +} + +#[cfg(test)] +mod test { + use super::Fp; + use crate::{ + arith_test, constants_test, from_uniform_bytes_test, legendre_test, serde_test, test, + }; + + constants_test!(Fp); + + arith_test!(Fp); + legendre_test!(Fp); + test!(arith, Fp, sqrt_test, 1000); + + serde_test!(Fp PrimeFieldBits); + from_uniform_bytes_test!(Fp, 1000, L 64, L 72, L 112); +} diff --git a/forks/halo2curves/src/pluto_eris/fp12.rs b/forks/halo2curves/src/pluto_eris/fp12.rs new file mode 100644 index 00000000..7b87f9aa --- /dev/null +++ b/forks/halo2curves/src/pluto_eris/fp12.rs @@ -0,0 +1,283 @@ +use ff::Field; + +use super::{fp::Fp, fp2::Fp2, fp6::Fp6}; +use crate::ff_ext::{ + quadratic::{QuadExtField, QuadExtFieldArith, QuadSparseMul}, + ExtField, +}; + +// -GAMMA is a quadratic non-residue in Fp6. Fp12 = Fp6[X]/(X^2 + GAMMA) +// We introduce the variable w such that w^2 = -GAMMA +// GAMMA = - v +/// An element of Fp12, represented by c0 + c1 * v. +pub type Fp12 = QuadExtField; + +impl QuadExtFieldArith for Fp12 { + type Base = Fp6; +} + +impl QuadSparseMul for Fp12 { + type Base = Fp2; +} + +impl ExtField for Fp12 { + const NON_RESIDUE: Self = Fp12::zero(); // no needs + + fn frobenius_map(&mut self, power: usize) { + self.c0.frobenius_map(power); + self.c1.frobenius_map(power); + self.c1.c0.mul_assign(&FROBENIUS_COEFF_FP12_C1[power % 12]); + self.c1.c1.mul_assign(&FROBENIUS_COEFF_FP12_C1[power % 12]); + self.c1.c2.mul_assign(&FROBENIUS_COEFF_FP12_C1[power % 12]); + } +} + +crate::impl_binops_additive!(Fp12, Fp12); +crate::impl_binops_multiplicative!(Fp12, Fp12); +crate::impl_binops_calls!(Fp12); +crate::impl_sum_prod!(Fp12); +crate::impl_cyclotomic_square!(Fp2, Fp12); + +/// Fp2(v)^((p^i-1)/6) for i=0,...,11 +pub const FROBENIUS_COEFF_FP12_C1: [Fp2; 12] = [ + // Fp2(v)**(((p^0) - 1) / 6) + Fp2::ONE, + // Fp2(v)**(((p^1) - 1) / 6) + Fp2 { + // 0x3c3ad3da8b99cb1df0709dc343113ccd9892dedd51f30695d89c647b90de8f41df055384b9e6cfd4e70648622c750f32ee965dfef2303d3 + c0: Fp::from_raw([ + 0x2ee965dfef2303d3, + 0x4e70648622c750f3, + 0x1df055384b9e6cfd, + 0x5d89c647b90de8f4, + 0xd9892dedd51f3069, + 0xdf0709dc343113cc, + 0x03c3ad3da8b99cb1, + ]), + // 0x149fd9ed2c7affe7aaa3b912182da22dccb29838628f04b6f333d052540294889f03876b2ddb143559f9373f4cf44e6afa0be24ad758a5ff + c1: Fp::from_raw([ + 0xfa0be24ad758a5ff, + 0x59f9373f4cf44e6a, + 0x9f03876b2ddb1435, + 0xf333d05254029488, + 0xccb29838628f04b6, + 0xaaa3b912182da22d, + 0x149fd9ed2c7affe7, + ]), + }, + // Fp2(v)**(((p^2) - 1) / 6) + Fp2 { + // 0x480000000000360001c950000d7ee0e4a803c956d01c903d720dc8ad8b38dffaf50c100004c37fffffff + c0: Fp::from_raw([ + 0x100004c37fffffff, + 0xc8ad8b38dffaf50c, + 0xc956d01c903d720d, + 0x50000d7ee0e4a803, + 0x00000000360001c9, + 0x0000000000004800, + 0x0000000000000000, + ]), + c1: Fp::ZERO, + }, + // Fp2(v)**(((p^3) - 1) / 6) + Fp2 { + // 0x1baee9e044d94d205764b80089c40010af5ca1e56a2a81e6a5d8739325984fc889d390efef216fe4f4af912a897f60a128a3be71be4995ca + c0: Fp::from_raw([ + 0x28a3be71be4995ca, + 0xf4af912a897f60a1, + 0x89d390efef216fe4, + 0xa5d8739325984fc8, + 0xaf5ca1e56a2a81e6, + 0x5764b80089c40010, + 0x1baee9e044d94d20, + ]), + // 0x20d4c11700e832829b26f1795339413be65e47a7716bc8bc07cd6b44b03ef1130b3c35a77291b29d6f45d28e4ef1ecb9678f4479a1151232 + c1: Fp::from_raw([ + 0x678f4479a1151232, + 0x6f45d28e4ef1ecb9, + 0x0b3c35a77291b29d, + 0x07cd6b44b03ef113, + 0xe65e47a7716bc8bc, + 0x9b26f1795339413b, + 0x20d4c11700e83282, + ]), + }, + // Fp2(v)**(((p^4) - 1) / 6) + Fp2 { + // 0x480000000000360001c950000d7ee0e4a803c956d01c903d720dc8ad8b38dffaf50c100004c37ffffffe + c0: Fp::from_raw([ + 0x100004c37ffffffe, + 0xc8ad8b38dffaf50c, + 0xc956d01c903d720d, + 0x50000d7ee0e4a803, + 0x00000000360001c9, + 0x0000000000004800, + 0x0000000000000000, + ]), + c1: Fp::ZERO, + }, + // Fp2(v)**(((p^5) - 1) / 6) + Fp2 { + // 0x17eb3ca29c1fb06e785dae245592ec43d5d373f7950b517d484ead4b6c8a66d46be33bb7a38302e7a63f2ca466b80fadf9ba5891cf2691f7 + c0: Fp::from_raw([ + 0xf9ba5891cf2691f7, + 0xa63f2ca466b80fad, + 0x6be33bb7a38302e7, + 0x484ead4b6c8a66d4, + 0xd5d373f7950b517d, + 0x785dae245592ec43, + 0x17eb3ca29c1fb06e, + ]), + // 0xc34e729d46d329af08338673b0b9f0e19abaf6f0edcc40514999af25c3c5c8a6c38ae3c44b69e68154c9b4f01fd9e4e6d83622ec9bc6c33 + c1: Fp::from_raw([ + 0x6d83622ec9bc6c33, + 0x154c9b4f01fd9e4e, + 0x6c38ae3c44b69e68, + 0x14999af25c3c5c8a, + 0x19abaf6f0edcc405, + 0xf08338673b0b9f0e, + 0x0c34e729d46d329a, + ]), + }, + // Fp2(v)**(((p^6) - 1) / 6) + Fp2 { + // 0x24000000000024000130e0000d7f70e4a803ca76f439266f443f9a5cda8a6c7be4a7a5fe8fadffd6a2a7e8c30006b9459ffffcd300000000 + c0: Fp::from_raw([ + 0x9ffffcd300000000, + 0xa2a7e8c30006b945, + 0xe4a7a5fe8fadffd6, + 0x443f9a5cda8a6c7b, + 0xa803ca76f439266f, + 0x0130e0000d7f70e4, + 0x2400000000002400, + ]), + c1: Fp::ZERO, + }, + // Fp2(v)**(((p^7) - 1) / 6) + Fp2 { + // 0x203c52c25746874e2229d623d94e5d17ce7a9c891f19f605e6b5d415217c8387c6b750c6440f92d95437843cdd3f6852711696f310dcfc2e + c0: Fp::from_raw([ + 0x711696f310dcfc2e, + 0x5437843cdd3f6852, + 0xc6b750c6440f92d9, + 0xe6b5d415217c8387, + 0xce7a9c891f19f605, + 0x2229d623d94e5d17, + 0x203c52c25746874e, + ]), + // 0xf602612d3852418568d26edf551ceb6db51323e91aa21b8510bca0a8687d7f345a41e9361d2eba148aeb183b3126adaa5f41a8828a75a02 + c1: Fp::from_raw([ + 0xa5f41a8828a75a02, + 0x48aeb183b3126ada, + 0x45a41e9361d2eba1, + 0x510bca0a8687d7f3, + 0xdb51323e91aa21b8, + 0x568d26edf551ceb6, + 0x0f602612d3852418, + ]), + }, + // Fp2(v)**(((p^8) - 1) / 6) + Fp2 { + // 0x24000000000024000130e0000d7f28e4a803ca76be3924a5f43f8cddf9a5c4781b50d5e1ff708dc8d9fa5d8a200bc4398ffff80f80000002 + c0: Fp::from_raw([ + 0x8ffff80f80000002, + 0xd9fa5d8a200bc439, + 0x1b50d5e1ff708dc8, + 0xf43f8cddf9a5c478, + 0xa803ca76be3924a5, + 0x0130e0000d7f28e4, + 0x2400000000002400, + ]), + c1: Fp::ZERO, + }, + // Fp2(v)**(((p^9) - 1) / 6) + Fp2 { + // 0x851161fbb26d6dfa9cc27ff83bb70d3f8a728918a0ea4889e6726c9b4f21cb35ad4150ea08c8ff1adf85798768758a4775c3e6141b66a37 + c0: Fp::from_raw([ + 0x775c3e6141b66a37, + 0xadf85798768758a4, + 0x5ad4150ea08c8ff1, + 0x9e6726c9b4f21cb3, + 0xf8a728918a0ea488, + 0xa9cc27ff83bb70d3, + 0x0851161fbb26d6df, + ]), + // 0x32b3ee8ff17f17d6609ee86ba462fa8c1a582cf82cd5db33c722f182a4b7b68d96b70571d1c4d3933621634b114cc8c3870b8595eeaedcf + c1: Fp::from_raw([ + 0x3870b8595eeaedcf, + 0x33621634b114cc8c, + 0xd96b70571d1c4d39, + 0x3c722f182a4b7b68, + 0xc1a582cf82cd5db3, + 0x6609ee86ba462fa8, + 0x032b3ee8ff17f17d, + ]), + }, + // Fp2(v)**(((p^10) - 1) / 6) + Fp2 { + // 0x24000000000024000130e0000d7f28e4a803ca76be3924a5f43f8cddf9a5c4781b50d5e1ff708dc8d9fa5d8a200bc4398ffff80f80000003 + c0: Fp::from_raw([ + 0x8ffff80f80000003, + 0xd9fa5d8a200bc439, + 0x1b50d5e1ff708dc8, + 0xf43f8cddf9a5c478, + 0xa803ca76be3924a5, + 0x0130e0000d7f28e4, + 0x2400000000002400, + ]), + c1: Fp::ZERO, + }, + // Fp2(v)**(((p^11) - 1) / 6) + Fp2 { + // 0xc14c35d63e0739188d331dbb7ec84a0d230567f5f2dd4f1fbf0ed116e0005a778c46a46ec2afceefc68bc1e994ea997a645a44130d96e0a + c0: Fp::from_raw([ + 0xa645a44130d96e0a, + 0xfc68bc1e994ea997, + 0x78c46a46ec2afcee, + 0xfbf0ed116e0005a7, + 0xd230567f5f2dd4f1, + 0x88d331dbb7ec84a0, + 0x0c14c35d63e07391, + ]), + // 0x17cb18d62b92f16510ada798d273d1d68e581b07e55c626a2fa5ff6a7e4e0ff1786ef7c24af7616e8d5b4d73fe091af7327c9aa4364393ce + c1: Fp::from_raw([ + 0x327c9aa4364393ce, + 0x8d5b4d73fe091af7, + 0x786ef7c24af7616e, + 0x2fa5ff6a7e4e0ff1, + 0x8e581b07e55c626a, + 0x10ada798d273d1d6, + 0x17cb18d62b92f165, + ]), + }, +]; + +#[cfg(test)] +mod test { + macro_rules! test_fp12 { + ($test:ident, $size: expr) => { + paste::paste! { + #[test] + fn [< $test test >]() { + use rand::SeedableRng; + use rand_xorshift::XorShiftRng; + let mut rng = XorShiftRng::from_seed(crate::tests::SEED); + crate::pluto_eris::fp12::test::$test(&mut rng, $size); + } + } + }; + } + use ff::Field; + use rand::RngCore; + + use super::*; + use crate::{arith_test, frobenius_test, setup_f12_test_funcs, test}; + + arith_test!(Fp12); + + // F12 specific + setup_f12_test_funcs!(Fp12, Fp6, Fp2); + test_fp12!(f12_mul_by_014_, 500); + test_fp12!(f12_mul_by_034_, 500); + frobenius_test!(Fp12, Fp, 8); +} diff --git a/forks/halo2curves/src/pluto_eris/fp2.rs b/forks/halo2curves/src/pluto_eris/fp2.rs new file mode 100644 index 00000000..d0a661f4 --- /dev/null +++ b/forks/halo2curves/src/pluto_eris/fp2.rs @@ -0,0 +1,151 @@ +use core::convert::TryInto; +use std::cmp::Ordering; + +use subtle::{Choice, CtOption}; + +use super::fp::Fp; +use crate::{ + ff::{Field, FromUniformBytes, PrimeField, WithSmallOrderMulGroup}, + ff_ext::{ + quadratic::{QuadExtField, QuadExtFieldArith, SQRT}, + ExtField, Legendre, + }, +}; + +crate::impl_binops_additive!(Fp2, Fp2); +crate::impl_binops_multiplicative!(Fp2, Fp2); +crate::impl_binops_calls!(Fp2); +crate::impl_sum_prod!(Fp2); +crate::impl_tower2!(Fp, Fp2); +crate::impl_tower2_from_uniform_bytes!(Fp, Fp2, 128); + +pub type Fp2 = QuadExtField; + +impl QuadExtFieldArith for Fp2 { + type Base = Fp; + const SQRT: SQRT = SQRT::Algorithm10 { + precompute_e: Fp2 { + c0: Fp::ZERO, + c1: Fp::from_raw([ + 0x67153f9701e19938, + 0x5d232408689b4c6c, + 0x021848271d63f087, + 0x03b21f15823a404a, + 0x667c70cf991a36e6, + 0x7a82a3d83bc9e63a, + 0x13e275a1fa6a13af, + ]), + }, + precompute_f: Fp2 { + c0: Fp::from_raw([0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), + c1: Fp::ZERO, + }, + q_minus_1_over_4: &[ + 0x67ffff34c0000000, + 0xa8a9fa30c001ae51, + 0xf929e97fa3eb7ff5, + 0xd10fe69736a29b1e, + 0x2a00f29dbd0e499b, + 0x004c3800035fdc39, + 0x0900000000000900, + ], + }; +} + +impl ExtField for Fp2 { + const NON_RESIDUE: Self = Fp2 { + c0: Fp::from_raw([ + 0xddb6da4b5b6db6e8, + 0x833bf7b35b701d98, + 0x3f6072240ebe2483, + 0x73cd928ee056022c, + 0xce4a7f2a7bcb4495, + 0xdbda9924971b3a9a, + 0x0cdb6db6db6dc3b6, + ]), + + c1: Fp::from_raw([ + 0xeb6db62d36db6db3, + 0xb523fb0536dcde8e, + 0x8c6d1148d5a5491b, + 0x457b57ef5366ce1a, + 0x489319197d79f5f3, + 0xb71cc2492776bcc3, + 0x07b6db6db6db756d, + ]), + }; + fn frobenius_map(&mut self, power: usize) { + if power % 2 != 0 { + self.conjugate(); + } + } +} + +#[cfg(test)] +mod test { + + use rand_core::RngCore; + + use super::*; + use crate::{arith_test, constants_test, legendre_test, serde_test, test}; + + constants_test!(Fp2); + + arith_test!(Fp2); + legendre_test!(Fp2); + test!(arith, Fp2, sqrt_test, 1000); + + serde_test!(Fp2); + + crate::f2_test!(Fp2, Fp); + crate::frobenius_test!(Fp2, Fp, 20); + + #[test] + fn test_fp2_squaring() { + // u + 1 + let mut a = Fp2 { + c0: Fp::one(), + c1: Fp::one(), + }; + // (u + 1) ^2 = 1 + u^2 + 2u = -4 + 2u + a.square_assign(); + let minus_4 = -Fp::from(4u64); + assert_eq!( + a, + Fp2 { + c0: minus_4, + c1: Fp::one() + Fp::one(), + } + ); + + // u + let mut a = Fp2 { + c0: Fp::zero(), + c1: Fp::one(), + }; + // u^2 + a.square_assign(); + assert_eq!( + a, + Fp2 { + c0: Fp::NON_RESIDUE, + c1: Fp::zero(), + } + ); + } + + #[test] + fn test_fp2_mul_nonresidue() { + use rand::SeedableRng; + use rand_xorshift::XorShiftRng; + let mut rng = XorShiftRng::from_seed(crate::tests::SEED); + for _ in 0..1000 { + let mut a = Fp2::random(&mut rng); + let mut b = a; + a = a.mul_by_nonresidue(); + b.mul_assign(&Fp2::NON_RESIDUE); + + assert_eq!(a, b); + } + } +} diff --git a/forks/halo2curves/src/pluto_eris/fp6.rs b/forks/halo2curves/src/pluto_eris/fp6.rs new file mode 100644 index 00000000..1baaa2d7 --- /dev/null +++ b/forks/halo2curves/src/pluto_eris/fp6.rs @@ -0,0 +1,289 @@ +use ff::Field; + +use super::{fp::Fp, fp2::Fp2}; +use crate::ff_ext::{ + cubic::{CubicExtField, CubicExtFieldArith, CubicSparseMul}, + ExtField, +}; + +// -BETA is a cubic non-residue in Fp2. Fp6 = Fp2[X]/(X^3 + BETA) +// We introduce the variable v such that v^3 = -BETA +// BETA = - 57/(z+3) +crate::impl_binops_additive!(Fp6, Fp6); +crate::impl_binops_multiplicative!(Fp6, Fp6); +crate::impl_binops_calls!(Fp6); +crate::impl_sum_prod!(Fp6); +pub type Fp6 = CubicExtField; + +impl CubicExtFieldArith for Fp6 { + type Base = Fp2; +} + +impl CubicSparseMul for Fp6 { + type Base = Fp2; +} + +impl ExtField for Fp6 { + const NON_RESIDUE: Self = Fp6::new(Fp2::ZERO, Fp2::ONE, Fp2::ZERO); + + fn frobenius_map(&mut self, power: usize) { + self.c0.frobenius_map(power); + self.c1.frobenius_map(power); + self.c2.frobenius_map(power); + self.c1.mul_assign(&FROBENIUS_COEFF_FP6_C1[power % 6]); + self.c2.mul_assign(&FROBENIUS_COEFF_FP6_C2[power % 6]); + } + + fn mul_by_nonresidue(self: &Fp6) -> Fp6 { + let c0 = self.c2.mul_by_nonresidue(); + let c1 = self.c0; + let c2 = self.c1; + Self { c0, c1, c2 } + } +} + +/// Fp2 coefficients for the efficient computation of Frobenius Endomorphism in +/// Fp6. +pub(crate) const FROBENIUS_COEFF_FP6_C1: [Fp2; 6] = [ + // Fp2(v^3)**(((p^0) - 1) / 3) + Fp2::ONE, + // Fp2(v^3)**(((p^1) - 1) / 3) + Fp2 { + // 0x120de97f024c55bc3bc0d351f4c70da1e3886170077a50986f93678bc921dcd5041bc4bb14cc42dc52e787634eccc335a001825382850d03 + c0: Fp::from_raw([ + 0xa001825382850d03, + 0x52e787634eccc335, + 0x041bc4bb14cc42dc, + 0x6f93678bc921dcd5, + 0xe3886170077a5098, + 0x3bc0d351f4c70da1, + 0x120de97f024c55bc, + ]), + // 0x2096f3f804d973afd82becc2ef081b76132461908eadbe3da1a7f5502b7091965efa1ddf4658080413be1b7cd3c9ea0e2772fea378a9b322 + c1: Fp::from_raw([ + 0x2772fea378a9b322, + 0x13be1b7cd3c9ea0e, + 0x5efa1ddf46580804, + 0xa1a7f5502b709196, + 0x132461908eadbe3d, + 0xd82becc2ef081b76, + 0x2096f3f804d973af, + ]), + }, + // Fp2(v^3)**(((p^2) - 1) / 3) + Fp2 { + // 0x480000000000360001c950000d7ee0e4a803c956d01c903d720dc8ad8b38dffaf50c100004c37ffffffe + c0: Fp::from_raw([ + 0x100004c37ffffffe, + 0xc8ad8b38dffaf50c, + 0xc956d01c903d720d, + 0x50000d7ee0e4a803, + 0x00000000360001c9, + 0x0000000000004800, + 0x0000000000000000, + ]), + c1: Fp::ZERO, + }, + // Fp2(v^3)**(((p^3) - 1) / 3) + Fp2 { + // 0x1f9cd069c59f50a72511749de232911d833b798e78bd98c02913e38315a71c287cd52ae30d09b78a8b43b17b4c3ea938a04518fa783eb497 + c0: Fp::from_raw([ + 0xa04518fa783eb497, + 0x8b43b17b4c3ea938, + 0x7cd52ae30d09b78a, + 0x2913e38315a71c28, + 0x833b798e78bd98c0, + 0x2511749de232911d, + 0x1f9cd069c59f50a7, + ]), + // 0x23affd628747cbaec26943f93dc9eab63f4af36699fe6d74c0aa2122aa7cb689e8faacb3479a973a4a728fcb77b150ee77240d4066e42ac5 + c1: Fp::from_raw([ + 0x77240d4066e42ac5, + 0x4a728fcb77b150ee, + 0xe8faacb3479a973a, + 0xc0aa2122aa7cb689, + 0x3f4af36699fe6d74, + 0xc26943f93dc9eab6, + 0x23affd628747cbae, + ]), + }, + // Fp2(v^3)**(((p^4) - 1) / 3) + Fp2 { + // 0x24000000000024000130e0000d7f28e4a803ca76be3924a5f43f8cddf9a5c4781b50d5e1ff708dc8d9fa5d8a200bc4398ffff80f80000002 + c0: Fp::from_raw([ + 0x8ffff80f80000002, + 0xd9fa5d8a200bc439, + 0x1b50d5e1ff708dc8, + 0xf43f8cddf9a5c478, + 0xa803ca76be3924a5, + 0x0130e0000d7f28e4, + 0x2400000000002400, + ]), + c1: Fp::ZERO, + }, + // Fp2(v^3)**(((p^5) - 1) / 3) + Fp2 { + // 0x165546173814a19ca18f781044054309e943b9ef683a6385efd7e9aad64bdffa485e5c5efd860546672498a76502061cffb95e58053c3e68 + c0: Fp::from_raw([ + 0xffb95e58053c3e68, + 0x672498a76502061c, + 0x485e5c5efd860546, + 0xefd7e9aad64bdffa, + 0xe943b9ef683a6385, + 0xa18f781044054309, + 0x165546173814a19c, + ]), + // 0x3b90ea573df08a167cc8f43ee2cdb9cfd983ff6bfc6212c262d1e46df2790d7815a816a9169606ee71f263db492378ea168edc22072221b + c1: Fp::from_raw([ + 0xa168edc22072221b, + 0xe71f263db492378e, + 0x815a816a9169606e, + 0x262d1e46df2790d7, + 0xfd983ff6bfc6212c, + 0x67cc8f43ee2cdb9c, + 0x03b90ea573df08a1, + ]), + }, +]; + +/// Fp2 coefficients for the efficient computation of Frobenius Endomorphism in +/// Fp6. +pub(crate) const FROBENIUS_COEFF_FP6_C2: [Fp2; 6] = [ + // Fp2(v^3)**(((2p^0) - 2) / 3) + Fp2::ONE, + // Fp2(v^3)**(((2p^1) - 2) / 3) + Fp2 { + // 0x93733692ce3cdcfc34610bac6bd22c4dc590efb038c82998c9549048e7b424cc00e17ffb4a61950d0ec132a7b38f09db0a818e422737f7c + c0: Fp::from_raw([ + 0xb0a818e422737f7c, + 0xd0ec132a7b38f09d, + 0xc00e17ffb4a61950, + 0x8c9549048e7b424c, + 0xdc590efb038c8299, + 0xc34610bac6bd22c4, + 0x093733692ce3cdcf, + ]), + // 0x12cb19daadc92882ba3593aa6f3e6bf426f29bd46039e3036f61d0bd35f39ebecdac3209d9df546061c90b4940d9031c240ce398421dc7dc + c1: Fp::from_raw([ + 0x240ce398421dc7dc, + 0x61c90b4940d9031c, + 0xcdac3209d9df5460, + 0x6f61d0bd35f39ebe, + 0x26f29bd46039e303, + 0xba3593aa6f3e6bf4, + 0x12cb19daadc92882, + ]), + }, + // Fp2(v^3)**(((2p^2) - 2) / 3) + Fp2 { + // 0x24000000000024000130e0000d7f28e4a803ca76be3924a5f43f8cddf9a5c4781b50d5e1ff708dc8d9fa5d8a200bc4398ffff80f80000002 + c0: Fp::from_raw([ + 0x8ffff80f80000002, + 0xd9fa5d8a200bc439, + 0x1b50d5e1ff708dc8, + 0xf43f8cddf9a5c478, + 0xa803ca76be3924a5, + 0x0130e0000d7f28e4, + 0x2400000000002400, + ]), + c1: Fp::ZERO, + }, + // Fp2(v^3)**(((2p^3) - 2) / 3) + Fp2 { + // 0x85cc83a7eeba2ef5f7dd2f9f1405312b2ce0cbc85b8561e1657aaf1e85b82299aa5ace8b26b78d88f57e1c7a87f75556885980d6c8d2186 + c0: Fp::from_raw([ + 0x6885980d6c8d2186, + 0x8f57e1c7a87f7555, + 0x9aa5ace8b26b78d8, + 0x1657aaf1e85b8229, + 0xb2ce0cbc85b8561e, + 0x5f7dd2f9f1405312, + 0x085cc83a7eeba2ef, + ]), + // 0xda3357ee4e6a9836af75e8ec0dbd23e7abc03d404620899ee0ea8b684b9400d58d5ebe487e523680bbe8a0dd9ea1d312bca2a953ab51c9b + c1: Fp::from_raw([ + 0x2bca2a953ab51c9b, + 0x0bbe8a0dd9ea1d31, + 0x58d5ebe487e52368, + 0xee0ea8b684b9400d, + 0x7abc03d404620899, + 0x6af75e8ec0dbd23e, + 0x0da3357ee4e6a983, + ]), + }, + // Fp2(v^3)**(((2p^4) - 2) / 3) + Fp2 { + // 0x480000000000360001c950000d7ee0e4a803c956d01c903d720dc8ad8b38dffaf50c100004c37ffffffe + c0: Fp::from_raw([ + 0x100004c37ffffffe, + 0xc8ad8b38dffaf50c, + 0xc956d01c903d720d, + 0x50000d7ee0e4a803, + 0x00000000360001c9, + 0x0000000000004800, + 0x0000000000000000, + ]), + c1: Fp::ZERO, + }, + // Fp2(v^3)**(((2p^5) - 2) / 3) + Fp2 { + // 0x126c045c5430b340de6cfc4b5581fb0d18dcaebf6af44db7a152a66663b3a80589f3e116289c6dad4263f3d0dc4e535286d24be170ff5eff + c0: Fp::from_raw([ + 0x86d24be170ff5eff, + 0x4263f3d0dc4e5352, + 0x89f3e116289c6dad, + 0xa152a66663b3a805, + 0x18dcaebf6af44db7, + 0xde6cfc4b5581fb0d, + 0x126c045c5430b340, + ]), + // 0x391b0a66d5051f9dc03edc6dd6532b206552ace8f9d3ad1e6cf20e91fdd8dafbe2588102de9880e3520536be54398f85028eea5832d1b8a + c1: Fp::from_raw([ + 0x5028eea5832d1b8a, + 0x3520536be54398f8, + 0xbe2588102de9880e, + 0xe6cf20e91fdd8daf, + 0x06552ace8f9d3ad1, + 0xdc03edc6dd6532b2, + 0x0391b0a66d5051f9, + ]), + }, +]; + +#[cfg(test)] +mod test { + use rand_core::RngCore; + + use super::*; + use crate::{arith_test, frobenius_test, setup_f6_test_funcs, test}; + + macro_rules! test_fp6 { + ($test:ident, $size: expr) => { + paste::paste! { + #[test] + fn [< $test test >]() { + use rand::SeedableRng; + use rand_xorshift::XorShiftRng; + let mut rng = XorShiftRng::from_seed(crate::tests::SEED); + crate::pluto_eris::fp6::test::$test(&mut rng, $size); + } + } + }; + } + + arith_test!(Fp6); + setup_f6_test_funcs!(Fp6, Fp2); + test_fp6!(f6_mul_nonresidue_, 1000); + test_fp6!(f6_mul_by_1_, 1000); + test_fp6!(f6_mul_by_01_, 1000); + frobenius_test!(Fp6, Fp, 10); + + #[test] + fn test_fp6_mul_nonresidue() { + let e = Fp6::random(rand_core::OsRng); + let a0 = e.mul_by_nonresidue(); + let a1 = e * Fp6::NON_RESIDUE; + + assert_eq!(a0, a1); + } +} diff --git a/forks/halo2curves/src/pluto_eris/fq.rs b/forks/halo2curves/src/pluto_eris/fq.rs new file mode 100644 index 00000000..cdddb24f --- /dev/null +++ b/forks/halo2curves/src/pluto_eris/fq.rs @@ -0,0 +1,41 @@ +use core::convert::TryInto; + +use halo2derive::impl_field; +use rand::RngCore; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +impl_field!( + pluto_eris_fq, + Fq, + modulus = "24000000000024000130e0000d7f70e4a803ca76f439266f443f9a5c7a8a6c7be4a775fe8e177fd69ca7e85d60050af41ffffcd300000001", + mul_gen = "7", + zeta = "9000000000006c000392a0001afee1c9500792ae3039253e641ba35817a29ffaf50be000032cfffffffe", + from_uniform = [64, 72, 112], + endian = "little", +); + +crate::extend_field_legendre!(Fq); +crate::impl_binops_calls!(Fq); +crate::impl_binops_additive!(Fq, Fq); +crate::impl_binops_multiplicative!(Fq, Fq); +crate::field_bits!(Fq); +crate::serialize_deserialize_primefield!(Fq); +crate::impl_from_u64!(Fq); +crate::impl_from_bool!(Fq); + +#[cfg(test)] +mod test { + use super::Fq; + use crate::{ + arith_test, constants_test, from_uniform_bytes_test, legendre_test, serde_test, test, + }; + + constants_test!(Fq); + + arith_test!(Fq); + legendre_test!(Fq); + test!(arith, Fq, sqrt_test, 1000); + + serde_test!(Fq PrimeFieldBits); + from_uniform_bytes_test!(Fq, 1000, L 64, L 72, L 112); +} diff --git a/forks/halo2curves/src/pluto_eris/mod.rs b/forks/halo2curves/src/pluto_eris/mod.rs new file mode 100644 index 00000000..b0be4e85 --- /dev/null +++ b/forks/halo2curves/src/pluto_eris/mod.rs @@ -0,0 +1,20 @@ +//! # `Pluto\Eris half-pairing cycle` +//! +//! Implementation of the Pluto / Eris half-pairing cycle of prime order +//! elliptic curves. +//! +//! Supporting evidence: +//! Field constant derivation: +//! Pairing constants derivation: +mod curve; +mod engine; +mod fp; +mod fp12; +mod fp2; +mod fp6; +mod fq; + +pub use curve::*; +pub use engine::*; +pub use fp::*; +pub use fq::*; diff --git a/forks/halo2curves/src/secp256k1/curve.rs b/forks/halo2curves/src/secp256k1/curve.rs new file mode 100644 index 00000000..806a5c27 --- /dev/null +++ b/forks/halo2curves/src/secp256k1/curve.rs @@ -0,0 +1,289 @@ +use core::{ + cmp, + fmt::Debug, + iter::Sum, + ops::{Add, Mul, Neg, Sub}, +}; + +use rand::RngCore; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +use crate::{ + ff::{Field, PrimeField, WithSmallOrderMulGroup}, + group::{prime::PrimeCurveAffine, Curve, Group as _, GroupEncoding}, + impl_binops_additive, impl_binops_additive_specify_output, impl_binops_multiplicative, + impl_binops_multiplicative_mixed, new_curve_impl, + secp256k1::{Fp, Fq}, + Coordinates, CurveAffine, CurveExt, +}; + +impl group::cofactor::CofactorGroup for Secp256k1 { + type Subgroup = Secp256k1; + + fn clear_cofactor(&self) -> Self { + *self + } + + fn into_subgroup(self) -> CtOption { + CtOption::new(self, 1.into()) + } + + fn is_torsion_free(&self) -> Choice { + 1.into() + } +} + +// Reference: https://neuromancer.sk/std/secg/secp256k1 +const SECP_GENERATOR_X: Fp = Fp::from_raw([ + 0x59F2815B16F81798, + 0x029BFCDB2DCE28D9, + 0x55A06295CE870B07, + 0x79BE667EF9DCBBAC, +]); +const SECP_GENERATOR_Y: Fp = Fp::from_raw([ + 0x9C47D08FFB10D4B8, + 0xFD17B448A6855419, + 0x5DA4FBFC0E1108A8, + 0x483ADA7726A3C465, +]); + +const SECP_A: Fp = Fp::from_raw([0, 0, 0, 0]); +const SECP_B: Fp = Fp::from_raw([7, 0, 0, 0]); + +new_curve_impl!( + (pub), + Secp256k1, + Secp256k1Affine, + Fp, + Fq, + (SECP_GENERATOR_X,SECP_GENERATOR_Y), + SECP_A, + SECP_B, + "secp256k1", + |domain_prefix| hash_to_curve(domain_prefix, hash_to_curve_suite(b"secp256k1_XMD:SHA-256_SSWU_RO_")), + crate::serde::CompressedFlagConfig::Extra, + standard_sign +); + +fn hash_to_curve_suite(domain: &[u8]) -> crate::hash_to_curve::Suite { + // Z = -11 (reference: ) + // 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc24 + const SSWU_Z: Fp = Fp::from_raw([ + 0xfffffffefffffc24, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + ]); + + // E': y'^2 = x'^3 + A' * x' + B', where + // A': 0x3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533 + // B': 1771 + // (reference: ) + pub const ISO_SECP_A: Fp = Fp::from_raw([ + 0x405447c01a444533, + 0xe953d363cb6f0e5d, + 0xa08a5558f0f5d272, + 0x3f8731abdd661adc, + ]); + + pub const ISO_SECP_B: Fp = Fp::from_raw([1771, 0, 0, 0]); + + let iso_map = crate::hash_to_curve::Iso { + a: ISO_SECP_A, + b: ISO_SECP_B, + map: Box::new(iso_map), + }; + + crate::hash_to_curve::Suite::new(domain, SSWU_Z, crate::hash_to_curve::Method::SSWU(iso_map)) +} + +#[allow(clippy::type_complexity)] +pub(crate) fn hash_to_curve<'a>( + domain_prefix: &'a str, + suite: crate::hash_to_curve::Suite, +) -> Box Secp256k1 + 'a> { + Box::new(move |message| suite.hash_to_curve(domain_prefix, message)) +} + +/// 3-Isogeny Map for Secp256k1 +/// Reference: +pub(crate) fn iso_map(x: Fp, y: Fp, z: Fp) -> Secp256k1 { + // constants for secp256k1 iso_map computation + const K: [[Fp; 4]; 5] = [ + [Fp::ZERO; 4], + [ + Fp::from_raw([ + 0x8e38e38daaaaa8c7, + 0x38e38e38e38e38e3, + 0xe38e38e38e38e38e, + 0x8e38e38e38e38e38, + ]), + Fp::from_raw([ + 0xdfff1044f17c6581, + 0xd595d2fc0bf63b92, + 0xb9f315cea7fd44c5, + 0x7d3d4c80bc321d5, + ]), + Fp::from_raw([ + 0x4ecbd0b53d9dd262, + 0xe4506144037c4031, + 0xe2a413deca25caec, + 0x534c328d23f234e6, + ]), + Fp::from_raw([ + 0x8e38e38daaaaa88c, + 0x38e38e38e38e38e3, + 0xe38e38e38e38e38e, + 0x8e38e38e38e38e38, + ]), + ], + [ + Fp::from_raw([ + 0x9fe6b745781eb49b, + 0x86cd409542f8487d, + 0x9ca34ccbb7b640dd, + 0xd35771193d94918a, + ]), + Fp::from_raw([ + 0xc52a56612a8c6d14, + 0x06d36b641f5e41bb, + 0xf7c4b2d51b542254, + 0xedadc6f64383dc1d, + ]), + Fp::ZERO, + Fp::ZERO, + ], + [ + Fp::from_raw([ + 0xa12f684b8e38e23c, + 0x2f684bda12f684bd, + 0x684bda12f684bda1, + 0x4bda12f684bda12f, + ]), + Fp::from_raw([ + 0xdffc90fc201d71a3, + 0x647ab046d686da6f, + 0xa9d0a54b12a0a6d5, + 0xc75e0c32d5cb7c0f, + ]), + Fp::from_raw([ + 0xa765e85a9ecee931, + 0x722830a201be2018, + 0x715209ef6512e576, + 0x29a6194691f91a73, + ]), + Fp::from_raw([ + 0x84bda12f38e38d84, + 0xbda12f684bda12f6, + 0xa12f684bda12f684, + 0x2f684bda12f684bd, + ]), + ], + [ + Fp::from_raw([ + 0xfffffffefffff93b, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + ]), + Fp::from_raw([ + 0xdfb425d2685c2573, + 0x9467c1bfc8e8d978, + 0xd5e9e6632722c298, + 0x7a06534bb8bdb49f, + ]), + Fp::from_raw([ + 0xa7bf8192bfd2a76f, + 0x0a3d21162f0d6299, + 0xf3a70c3fa8fe337e, + 0x6484aa716545ca2c, + ]), + Fp::ZERO, + ], + ]; + + let z2 = z.square(); + let z3 = z2 * z; + + // iso_map logic (avoid inversion) in projective coordinates + // reference: + let x_num = ((K[1][3] * x + K[1][2] * z) * x + K[1][1] * z2) * x + K[1][0] * z3; + let x_den = (z * x + K[2][1] * z2) * x + K[2][0] * z3; + + let y_num = (((K[3][3] * x + K[3][2] * z) * x + K[3][1] * z2) * x + K[3][0] * z3) * y; + let y_den = (((x + K[4][2] * z) * x + K[4][1] * z2) * x + K[4][0] * z3) * z; + + let z = x_den * y_den; + let x = x_num * y_den; + let y = y_num * x_den; + + Secp256k1 { x, y, z } +} + +#[cfg(test)] +mod test { + use group::UncompressedEncoding; + use rand_core::OsRng; + + use super::*; + use crate::{serde::SerdeObject, tests::curve::TestH2C}; + + crate::curve_testing_suite!(Secp256k1); + crate::curve_testing_suite!(Secp256k1, "endo_consistency"); + crate::curve_testing_suite!(Secp256k1, "ecdsa_example"); + crate::curve_testing_suite!( + Secp256k1, + "constants", + Fp::MODULUS, + SECP_A, + SECP_B, + SECP_GENERATOR_X, + SECP_GENERATOR_Y, + Fq::MODULUS + ); + + #[test] + fn test_hash_to_curve() { + // Test vectors are taken from + // https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-expand_message_xmdsha-256 + [ + TestH2C::::new( + b"", + crate::tests::point_from_hex( + "c1cae290e291aee617ebaef1be6d73861479c48b841eaba9b7b5852ddfeb1346", + "64fa678e07ae116126f08b022a94af6de15985c996c3a91b64c406a960e51067", + ), + ), + TestH2C::::new( + b"abc", + crate::tests::point_from_hex( + "3377e01eab42db296b512293120c6cee72b6ecf9f9205760bd9ff11fb3cb2c4b", + "7f95890f33efebd1044d382a01b1bee0900fb6116f94688d487c6c7b9c8371f6", + ), + ), + TestH2C::::new( + b"abcdef0123456789", + crate::tests::point_from_hex( + "bac54083f293f1fe08e4a70137260aa90783a5cb84d3f35848b324d0674b0e3a", + "4436476085d4c3c4508b60fcf4389c40176adce756b398bdee27bca19758d828", + ), + ), + TestH2C::::new( + b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", + crate::tests::point_from_hex( + "e2167bc785333a37aa562f021f1e881defb853839babf52a7f72b102e41890e9", + "f2401dd95cc35867ffed4f367cd564763719fbc6a53e969fb8496a1e6685d873", + ), + ), // + TestH2C::::new( + b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + crate::tests::point_from_hex( + "e3c8d35aaaf0b9b647e88a0a0a7ee5d5bed5ad38238152e4e6fd8c1f8cb7c998", + "8446eeb6181bf12f56a9d24e262221cc2f0c4725c7e3803024b5888ee5823aa6", + ), + ), + ].iter().for_each(|test| { + test.run("QUUX-V01-CS02-with-"); + }); + } +} diff --git a/forks/halo2curves/src/secp256k1/fp.rs b/forks/halo2curves/src/secp256k1/fp.rs new file mode 100644 index 00000000..7f621965 --- /dev/null +++ b/forks/halo2curves/src/secp256k1/fp.rs @@ -0,0 +1,39 @@ +use core::convert::TryInto; + +use halo2derive::impl_field; +use rand::RngCore; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +impl_field!( + secp256k1_base, + Fp, + modulus = "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + mul_gen = "3", + zeta = "7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee", + from_uniform = [48, 64], + endian = "little", +); + +crate::extend_field_legendre!(Fp); +crate::impl_binops_calls!(Fp); +crate::impl_binops_additive!(Fp, Fp); +crate::impl_binops_multiplicative!(Fp, Fp); +crate::field_bits!(Fp); +crate::serialize_deserialize_primefield!(Fp); +crate::impl_from_u64!(Fp); +crate::impl_from_bool!(Fp); + +#[cfg(test)] +mod test { + use super::Fp; + use crate::{ + arith_test, constants_test, from_uniform_bytes_test, legendre_test, serde_test, test, + }; + + constants_test!(Fp); + arith_test!(Fp); + legendre_test!(Fp); + test!(arith, Fp, sqrt_test, 1000); + serde_test!(Fp PrimeFieldBits); + from_uniform_bytes_test!(Fp, 1000, L 64, L 48); +} diff --git a/forks/halo2curves/src/secp256k1/fq.rs b/forks/halo2curves/src/secp256k1/fq.rs new file mode 100644 index 00000000..09238482 --- /dev/null +++ b/forks/halo2curves/src/secp256k1/fq.rs @@ -0,0 +1,41 @@ +use core::convert::TryInto; + +use halo2derive::impl_field; +use rand::RngCore; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +impl_field!( + secp256k1_scalar, + Fq, + modulus = "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + mul_gen = "7", + zeta = "5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72", + from_uniform = [48, 64], + endian = "little", +); + +crate::extend_field_legendre!(Fq); +crate::impl_binops_calls!(Fq); +crate::impl_binops_additive!(Fq, Fq); +crate::impl_binops_multiplicative!(Fq, Fq); +crate::field_bits!(Fq); +crate::serialize_deserialize_primefield!(Fq); +crate::impl_from_u64!(Fq); +crate::impl_from_bool!(Fq); + +#[cfg(test)] +mod test { + use super::Fq; + use crate::{ + arith_test, constants_test, from_uniform_bytes_test, legendre_test, serde_test, test, + }; + + constants_test!(Fq); + + arith_test!(Fq); + legendre_test!(Fq); + test!(arith, Fq, sqrt_test, 1000); + + serde_test!(Fq); + from_uniform_bytes_test!(Fq, 1000, L 64, L 48); +} diff --git a/forks/halo2curves/src/secp256k1/mod.rs b/forks/halo2curves/src/secp256k1/mod.rs new file mode 100644 index 00000000..edc3c097 --- /dev/null +++ b/forks/halo2curves/src/secp256k1/mod.rs @@ -0,0 +1,7 @@ +mod curve; +mod fp; +mod fq; + +pub use curve::*; +pub use fp::*; +pub use fq::*; diff --git a/forks/halo2curves/src/secp256r1/curve.rs b/forks/halo2curves/src/secp256r1/curve.rs new file mode 100644 index 00000000..e7e3f1a5 --- /dev/null +++ b/forks/halo2curves/src/secp256r1/curve.rs @@ -0,0 +1,174 @@ +use core::{ + cmp, + fmt::Debug, + iter::Sum, + ops::{Add, Mul, Neg, Sub}, +}; + +use rand::RngCore; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +use crate::{ + ff::{Field, PrimeField, WithSmallOrderMulGroup}, + group::{prime::PrimeCurveAffine, Curve, Group as _, GroupEncoding}, + secp256r1::{Fp, Fq}, + Coordinates, CurveAffine, CurveExt, +}; + +impl group::cofactor::CofactorGroup for Secp256r1 { + type Subgroup = Secp256r1; + + fn clear_cofactor(&self) -> Self { + *self + } + + fn into_subgroup(self) -> CtOption { + CtOption::new(self, 1.into()) + } + + fn is_torsion_free(&self) -> Choice { + 1.into() + } +} + +// Reference: https://neuromancer.sk/std/secg/secp256r1 +const SECP_GENERATOR_X: Fp = Fp::from_raw([ + 0xF4A13945D898C296, + 0x77037D812DEB33A0, + 0xF8BCE6E563A440F2, + 0x6B17D1F2E12C4247, +]); + +const SECP_GENERATOR_Y: Fp = Fp::from_raw([ + 0xCBB6406837BF51F5, + 0x2BCE33576B315ECE, + 0x8EE7EB4A7C0F9E16, + 0x4FE342E2FE1A7F9B, +]); + +const SECP_A: Fp = Fp::from_raw([ + 0xFFFFFFFFFFFFFFFC, + 0x00000000FFFFFFFF, + 0x0000000000000000, + 0xFFFFFFFF00000001, +]); +const SECP_B: Fp = Fp::from_raw([ + 0x3BCE3C3E27D2604B, + 0x651D06B0CC53B0F6, + 0xB3EBBD55769886BC, + 0x5AC635D8AA3A93E7, +]); + +use crate::{ + impl_binops_additive, impl_binops_additive_specify_output, impl_binops_multiplicative, + impl_binops_multiplicative_mixed, new_curve_impl, +}; + +new_curve_impl!( + (pub), + Secp256r1, + Secp256r1Affine, + Fp, + Fq, + (SECP_GENERATOR_X,SECP_GENERATOR_Y), + SECP_A, + SECP_B, + "secp256r1", + |domain_prefix| hash_to_curve(domain_prefix, hash_to_curve_suite(b"P256_XMD:SHA-256_SSWU_RO_")), + crate::serde::CompressedFlagConfig::Extra, + standard_sign +); + +fn hash_to_curve_suite(domain: &[u8]) -> crate::hash_to_curve::Suite { + // Optimal Z with: + // 0xffffffff00000001000000000000000000000000fffffffffffffffffffffff5 + // Z = -10 (reference: ) + const SSWU_Z: Fp = Fp::from_raw([ + 0xfffffffffffffff5, + 0x00000000ffffffff, + 0x0000000000000000, + 0xffffffff00000001, + ]); + + let iso_map = crate::hash_to_curve::Iso { + a: Secp256r1::a(), + b: Secp256r1::b(), + map: Box::new(move |x, y, z| Secp256r1 { x, y, z }), + }; + + crate::hash_to_curve::Suite::new(domain, SSWU_Z, crate::hash_to_curve::Method::SSWU(iso_map)) +} + +#[allow(clippy::type_complexity)] +pub(crate) fn hash_to_curve<'a>( + domain_prefix: &'a str, + suite: crate::hash_to_curve::Suite, +) -> Box Secp256r1 + 'a> { + Box::new(move |message| suite.hash_to_curve(domain_prefix, message)) +} + +#[cfg(test)] +mod test { + use group::UncompressedEncoding; + use rand_core::OsRng; + + use super::*; + use crate::{serde::SerdeObject, tests::curve::TestH2C}; + crate::curve_testing_suite!(Secp256r1); + crate::curve_testing_suite!(Secp256r1, "ecdsa_example"); + crate::curve_testing_suite!( + Secp256r1, + "constants", + Fp::MODULUS, + SECP_A, + SECP_B, + SECP_GENERATOR_X, + SECP_GENERATOR_Y, + Fq::MODULUS + ); + + #[test] + fn test_hash_to_curve() { + // Test vectors are taken from + // https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-p256_xmdsha-256_sswu_ro_ + [ + TestH2C::::new( + b"", + crate::tests::point_from_hex( + "2c15230b26dbc6fc9a37051158c95b79656e17a1a920b11394ca91c44247d3e4", + "8a7a74985cc5c776cdfe4b1f19884970453912e9d31528c060be9ab5c43e8415", + ), + ), + TestH2C::::new( + b"abc", + crate::tests::point_from_hex( + "0bb8b87485551aa43ed54f009230450b492fead5f1cc91658775dac4a3388a0f", + "5c41b3d0731a27a7b14bc0bf0ccded2d8751f83493404c84a88e71ffd424212e", + ), + ), + TestH2C::::new( + b"abcdef0123456789", + crate::tests::point_from_hex( + "65038ac8f2b1def042a5df0b33b1f4eca6bff7cb0f9c6c1526811864e544ed80", + "cad44d40a656e7aff4002a8de287abc8ae0482b5ae825822bb870d6df9b56ca3", + ), + ), + TestH2C::::new( + b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", + crate::tests::point_from_hex( + "4be61ee205094282ba8a2042bcb48d88dfbb609301c49aa8b078533dc65a0b5d", + "98f8df449a072c4721d241a3b1236d3caccba603f916ca680f4539d2bfb3c29e", + ), + ), // + TestH2C::::new( + b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + crate::tests::point_from_hex( + "457ae2981f70ca85d8e24c308b14db22f3e3862c5ea0f652ca38b5e49cd64bc5", + "ecb9f0eadc9aeed232dabc53235368c1394c78de05dd96893eefa62b0f4757dc", + ), + ), + ].iter().for_each(|test| { + test.run("QUUX-V01-CS02-with-"); + }); + } +} diff --git a/forks/halo2curves/src/secp256r1/fp.rs b/forks/halo2curves/src/secp256r1/fp.rs new file mode 100644 index 00000000..6de4542a --- /dev/null +++ b/forks/halo2curves/src/secp256r1/fp.rs @@ -0,0 +1,41 @@ +use core::convert::TryInto; + +use halo2derive::impl_field; +use rand::RngCore; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +impl_field!( + secp256r1_base, + Fp, + modulus = "ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", + mul_gen = "6", + zeta = "4d6ea8928adb86cf62388a8e0ef623312e68c59bdef3e53fd964598eb819acce", + from_uniform = [48, 64], + endian = "little", +); + +crate::extend_field_legendre!(Fp); +crate::impl_binops_calls!(Fp); +crate::impl_binops_additive!(Fp, Fp); +crate::impl_binops_multiplicative!(Fp, Fp); +crate::field_bits!(Fp); +crate::serialize_deserialize_primefield!(Fp); +crate::impl_from_u64!(Fp); +crate::impl_from_bool!(Fp); + +#[cfg(test)] +mod test { + use super::Fp; + use crate::{ + arith_test, constants_test, from_uniform_bytes_test, legendre_test, serde_test, test, + }; + + constants_test!(Fp); + + arith_test!(Fp); + legendre_test!(Fp); + test!(arith, Fp, sqrt_test, 1000); + + serde_test!(Fp PrimeFieldBits); + from_uniform_bytes_test!(Fp, 1000, L 64, L 48); +} diff --git a/forks/halo2curves/src/secp256r1/fq.rs b/forks/halo2curves/src/secp256r1/fq.rs new file mode 100644 index 00000000..2cb9011a --- /dev/null +++ b/forks/halo2curves/src/secp256r1/fq.rs @@ -0,0 +1,41 @@ +use core::convert::TryInto; + +use halo2derive::impl_field; +use rand::RngCore; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +impl_field!( + secp256r1_scalar, + Fq, + modulus = "ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", + mul_gen = "7", + zeta = "52891d43d946a0354e786d0777fd6aef9405335ce9c83e1d7cbf87ff12884e21", + from_uniform = [48, 64], + endian = "little", +); + +crate::extend_field_legendre!(Fq); +crate::impl_binops_calls!(Fq); +crate::impl_binops_additive!(Fq, Fq); +crate::impl_binops_multiplicative!(Fq, Fq); +crate::field_bits!(Fq); +crate::serialize_deserialize_primefield!(Fq); +crate::impl_from_u64!(Fq); +crate::impl_from_bool!(Fq); + +#[cfg(test)] +mod test { + use super::Fq; + use crate::{ + arith_test, constants_test, from_uniform_bytes_test, legendre_test, serde_test, test, + }; + + constants_test!(Fq); + + arith_test!(Fq); + legendre_test!(Fq); + test!(arith, Fq, sqrt_test, 1000); + + serde_test!(Fq PrimeFieldBits); + from_uniform_bytes_test!(Fq, 1000, L 64, L 48); +} diff --git a/forks/halo2curves/src/secp256r1/mod.rs b/forks/halo2curves/src/secp256r1/mod.rs new file mode 100644 index 00000000..edc3c097 --- /dev/null +++ b/forks/halo2curves/src/secp256r1/mod.rs @@ -0,0 +1,7 @@ +mod curve; +mod fp; +mod fq; + +pub use curve::*; +pub use fp::*; +pub use fq::*; diff --git a/forks/halo2curves/src/secq256k1/curve.rs b/forks/halo2curves/src/secq256k1/curve.rs new file mode 100644 index 00000000..63f37c35 --- /dev/null +++ b/forks/halo2curves/src/secq256k1/curve.rs @@ -0,0 +1,100 @@ +use core::{ + cmp, + fmt::Debug, + iter::Sum, + ops::{Add, Mul, Neg, Sub}, +}; + +use rand::RngCore; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +use crate::{ + ff::{Field, PrimeField, WithSmallOrderMulGroup}, + group::{prime::PrimeCurveAffine, Curve, Group, GroupEncoding}, + impl_binops_additive, impl_binops_additive_specify_output, impl_binops_multiplicative, + impl_binops_multiplicative_mixed, new_curve_impl, + secp256k1::{Fp, Fq}, + Coordinates, CurveAffine, CurveExt, +}; + +const SECQ_GENERATOR_X: Fq = Fq::from_raw([ + 0xA24288E37702EDA6, + 0x3134E45A097781A6, + 0xB6B06C87A2CE32E2, + 0x76C39F5585CB160E, +]); + +const SECQ_GENERATOR_Y: Fq = Fq::from_raw([ + 0xA4120DDAD952677F, + 0xD18983D26E8DC055, + 0xDC2D265A8E82A7F7, + 0x3FFC646C7B2918B5, +]); + +const SECQ_A: Fq = Fq::from_raw([0, 0, 0, 0]); +const SECQ_B: Fq = Fq::from_raw([7, 0, 0, 0]); + +new_curve_impl!( + (pub), + Secq256k1, + Secq256k1Affine, + Fq, + Fp, + (SECQ_GENERATOR_X, SECQ_GENERATOR_Y), + SECQ_A, + SECQ_B, + "secq256k1", + |domain_prefix| crate::hash_to_curve::hash_to_curve(domain_prefix, Secq256k1::default_hash_to_curve_suite()), + crate::serde::CompressedFlagConfig::Extra, + standard_sign +); + +impl group::cofactor::CofactorGroup for Secq256k1 { + type Subgroup = Secq256k1; + + fn clear_cofactor(&self) -> Self { + *self + } + + fn into_subgroup(self) -> CtOption { + CtOption::new(self, 1.into()) + } + + fn is_torsion_free(&self) -> Choice { + 1.into() + } +} + +impl Secq256k1 { + const SVDW_Z: Fq = Fq::ONE; + + fn default_hash_to_curve_suite() -> crate::hash_to_curve::Suite { + crate::hash_to_curve::Suite::::new( + b"secq256k1_XMD:SHA-256_SVDW_RO_", + Self::SVDW_Z, + crate::hash_to_curve::Method::SVDW, + ) + } +} + +#[cfg(test)] +mod test { + use group::UncompressedEncoding; + use rand_core::OsRng; + + use super::*; + use crate::serde::SerdeObject; + + crate::curve_testing_suite!(Secq256k1); + crate::curve_testing_suite!(Secq256k1, "endo_consistency"); + crate::curve_testing_suite!( + Secq256k1, + "constants", + Fq::MODULUS, + SECQ_A, + SECQ_B, + SECQ_GENERATOR_X, + SECQ_GENERATOR_Y, + Fp::MODULUS + ); +} diff --git a/forks/halo2curves/src/secq256k1/mod.rs b/forks/halo2curves/src/secq256k1/mod.rs new file mode 100644 index 00000000..cf6e3a91 --- /dev/null +++ b/forks/halo2curves/src/secq256k1/mod.rs @@ -0,0 +1,5 @@ +mod curve; + +pub use curve::*; + +pub use crate::secp256k1::{Fp as Fq, Fq as Fp}; diff --git a/forks/halo2curves/src/serde.rs b/forks/halo2curves/src/serde.rs new file mode 100644 index 00000000..2acd2d80 --- /dev/null +++ b/forks/halo2curves/src/serde.rs @@ -0,0 +1,375 @@ +use std::{ + fmt::Debug, + io::{self, Read, Write}, +}; + +#[cfg(feature = "derive_serde")] +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Copy, Debug)] +#[cfg_attr(feature = "derive_serde", derive(Serialize, Deserialize))] +pub struct Repr( + #[cfg_attr(feature = "derive_serde", serde(with = "serde_arrays"))] [u8; T], +); + +impl Repr { + pub fn inner(&self) -> &[u8; T] { + &self.0 + } +} + +impl From<[u8; T]> for Repr { + fn from(bytes: [u8; T]) -> Self { + Self(bytes) + } +} + +impl<'a, const T: usize> From<&'a [u8]> for Repr { + fn from(bytes: &[u8]) -> Self { + Self(bytes.try_into().unwrap()) + } +} + +impl From> for [u8; T] { + fn from(repr: Repr) -> Self { + repr.0 + } +} + +impl Default for Repr { + fn default() -> Self { + Self([0u8; T]) + } +} + +impl AsMut<[u8]> for Repr { + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0 + } +} + +impl AsRef<[u8]> for Repr { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl std::ops::Index> for Repr { + type Output = [u8]; + + fn index(&self, range: std::ops::Range) -> &Self::Output { + &self.0[range] + } +} + +impl std::ops::Index for Repr { + type Output = u8; + + fn index(&self, index: usize) -> &Self::Output { + &self.0[index] + } +} + +impl std::ops::Index> for Repr { + type Output = [u8]; + + fn index(&self, range: std::ops::RangeTo) -> &Self::Output { + &self.0[range] + } +} + +impl std::ops::Index> for Repr { + type Output = [u8]; + + fn index(&self, range: std::ops::RangeFrom) -> &Self::Output { + &self.0[range] + } +} + +impl std::ops::IndexMut> for Repr { + fn index_mut(&mut self, range: std::ops::Range) -> &mut Self::Output { + &mut self.0[range] + } +} + +/// Trait for converting raw bytes to/from the internal representation of a +/// type. For example, field elements are represented in Montgomery form and +/// serialized/deserialized without Montgomery reduction. +pub trait SerdeObject: Sized { + /// The purpose of unchecked functions is to read the internal memory + /// representation of a type from bytes as quickly as possible. No + /// sanitization checks are performed to ensure the bytes represent a + /// valid object. As such this function should only be used internally + /// as an extension of machine memory. It should not be used to deserialize + /// externally provided data. + fn from_raw_bytes_unchecked(bytes: &[u8]) -> Self; + fn from_raw_bytes(bytes: &[u8]) -> Option; + + fn to_raw_bytes(&self) -> Vec; + + /// The purpose of unchecked functions is to read the internal memory + /// representation of a type from disk as quickly as possible. No + /// sanitization checks are performed to ensure the bytes represent a + /// valid object. This function should only be used internally when some + /// machine state cannot be kept in memory (e.g., between runs) + /// and needs to be reloaded as quickly as possible. + fn read_raw_unchecked(reader: &mut R) -> Self; + fn read_raw(reader: &mut R) -> io::Result; + + fn write_raw(&self, writer: &mut W) -> io::Result<()>; +} + +pub mod endian { + + pub trait EndianRepr: Sized { + const ENDIAN: Endian; + + fn to_bytes(&self) -> Vec; + + fn from_bytes(x: &[u8]) -> subtle::CtOption; + } + + pub enum Endian { + LE, + BE, + } + + impl Endian { + pub fn to_bytes(&self, res: &mut [u8], el: &[u64]) { + match self { + Endian::LE => { + el.iter().enumerate().for_each(|(i, limb)| { + let off = i * 8; + res[off..off + 8].copy_from_slice(&limb.to_le_bytes()); + }); + } + Endian::BE => { + el.iter().rev().enumerate().for_each(|(i, limb)| { + let off = i * 8; + res[off..off + 8].copy_from_slice(&limb.to_be_bytes()); + }); + } + } + } + + pub fn from_bytes(&self, res: &[u8], el: &mut [u64]) { + match self { + Endian::LE => { + el.iter_mut().enumerate().for_each(|(i, limb)| { + let off = i * 8; + *limb = u64::from_le_bytes(res[off..off + 8].try_into().unwrap()); + }); + } + Endian::BE => { + el.iter_mut().rev().enumerate().for_each(|(i, limb)| { + let off = i * 8; + *limb = u64::from_be_bytes(res[off..off + 8].try_into().unwrap()); + }); + } + } + } + } +} + +pub(crate) enum CompressedFlagConfig { + // NOTE: if needed we can add fields for bit positions + + // Secp256k1, Secp256r1 curves should be encoded with + Extra, // sign: 0 identity: 1 + + // Pasta curves should be encoded with + SingleSpare, // sign: 0 + + // BN254 curve should be encoded with + TwoSpare, // sign: 0, identity: 1 + + // BLS12-{381, 377} curves should be encoded with + ThreeSpare, // is_compressed: 0, sign: 1, identity: 2 +} + +impl CompressedFlagConfig { + pub(crate) const fn has_extra_byte(&self) -> bool { + matches!(self, CompressedFlagConfig::Extra) + } +} + +pub(crate) struct Flag {} + +impl Flag { + fn flag(pos: u8) -> u8 { + 1 << 7u8.checked_sub(pos).unwrap() + } + + fn set(pos: u8, value: bool, flag_byte: &mut u8) { + value.then(|| *flag_byte |= Self::flag(pos)); + } + + fn get(pos: u8, flag_byte: &mut u8) -> subtle::Choice { + let flag = Self::flag(pos); + let value = (*flag_byte & flag) != 0; + *flag_byte &= !flag; // clear flag + subtle::Choice::from(value as u8) + } +} + +pub(crate) trait Compressed: + Debug + Copy + Default + AsRef<[u8]> + AsMut<[u8]> + Send + Sync + 'static +where + C::Base: crate::serde::endian::EndianRepr, +{ + const CONFIG: CompressedFlagConfig; + + fn flag_byte(&mut self) -> &mut u8 { + use crate::serde::endian::EndianRepr; + match Self::CONFIG { + // Most sig byte is always the flag byte when extra byte flag is used + CompressedFlagConfig::Extra => self.as_mut().first_mut().unwrap(), + _ => match C::Base::ENDIAN { + // Least sig byte is the flag byte + crate::serde::endian::Endian::LE => self.as_mut().last_mut().unwrap(), + // Most sig byte is the flag byte + crate::serde::endian::Endian::BE => self.as_mut().first_mut().unwrap(), + }, + } + } + + fn sign(y: &C) -> subtle::Choice; + + fn resolve(x: C::Base, sign: subtle::Choice) -> subtle::CtOption; + + fn pos_sign() -> u8 { + match Self::CONFIG { + CompressedFlagConfig::Extra => 0, + CompressedFlagConfig::SingleSpare => 0, + CompressedFlagConfig::TwoSpare => 0, + CompressedFlagConfig::ThreeSpare => 2, + } + } + + fn pos_compressed() -> Option { + match Self::CONFIG { + CompressedFlagConfig::ThreeSpare => Some(0), + _ => None, + } + } + + fn pos_idetity() -> Option { + match Self::CONFIG { + CompressedFlagConfig::Extra => Some(1), + CompressedFlagConfig::SingleSpare => None, + CompressedFlagConfig::TwoSpare => Some(1), + CompressedFlagConfig::ThreeSpare => Some(1), + } + } + + fn set_sign(&mut self, c: &C) { + let sign = bool::from(Self::sign(c)); + let pos = Self::pos_sign(); + Flag::set(pos, sign, self.flag_byte()); + } + + fn set_compressed(&mut self) { + if let Some(pos) = Self::pos_compressed() { + Flag::set(pos, true, self.flag_byte()) + } + } + + fn set_identity(&mut self, c: &C) { + if let Some(pos) = Self::pos_idetity() { + Flag::set(pos, bool::from(c.is_identity()), self.flag_byte()); + }; + } + + fn get_sign(&mut self) -> subtle::Choice { + Flag::get(Self::pos_sign(), self.flag_byte()) + } + + fn get_is_compressed(&mut self) -> Option { + Self::pos_compressed().map(|pos| Flag::get(pos, self.flag_byte())) + } + + fn get_is_identity(&mut self) -> Option { + Self::pos_idetity().map(|pos| Flag::get(pos, self.flag_byte())) + } + + #[allow(dead_code)] + fn set_flags(&mut self, c: &C) { + self.set_identity(c); + self.set_sign(c); + self.set_compressed(); + } + + fn encode(c: &C) -> Self { + use crate::serde::endian::EndianRepr; + let mut this = Self::default(); + let coordinates = c.coordinates().unwrap(); + let x = coordinates.x(); + let x_bytes = x.to_bytes(); + match Self::CONFIG { + CompressedFlagConfig::Extra => { + // Most sig byte is always the flag byte when extra byte flag is used + this.as_mut()[1..1 + x_bytes.len()].copy_from_slice(&x_bytes) + } + _ => this.as_mut()[..x_bytes.len()].copy_from_slice(&x_bytes), + }; + this.set_identity(c); + this.set_sign(c); + this.set_compressed(); + this + } + + fn decode(mut self) -> subtle::CtOption { + let is_compressed = self.get_is_compressed(); + // if is compressed set then it should be set one + let is_valid_0: subtle::Choice = is_compressed.unwrap_or(subtle::Choice::from(1u8)); + + let is_identity = self.get_is_identity(); + + let sign = self.get_sign(); + + // with extra byte config expect it goes to zero after it is read + // otherwise `from_byte` checks if flag or rest unused bytes are zero + let is_valid_1 = match Self::CONFIG { + CompressedFlagConfig::Extra => *self.flag_byte() == 0, + _ => true, + }; + let is_valid_1: subtle::Choice = (is_valid_1 as u8).into(); + + let x = match Self::CONFIG { + CompressedFlagConfig::Extra => { + // Most sig byte is always the flag byte when extra byte flag is used + ::from_bytes(&self.as_ref()[1..]) + } + _ => ::from_bytes(self.as_ref()), + }; + + x.and_then(|x| -> subtle::CtOption { + use ff::Field; + let is_zero = x.is_zero(); + + let (is_valid_2, is_identity) = match is_identity { + // identity flag active + Some(is_identity) => { + // identity flag set: + // * x must be zero + // * sign must not be set + + // identity flag not set: + // * x must not be zero + + let is_valid = (is_identity & is_zero & !sign) ^ (!is_identity & !is_zero); + + (is_valid, is_identity) + } + + // identitity flag inactive + None => (subtle::Choice::from(1u8), is_zero), + }; + + let is_valid = is_valid_0 & is_valid_1 & is_valid_2; + + subtle::CtOption::new(C::identity(), is_valid & is_identity) + .or_else(|| Self::resolve(x, sign).and_then(|c| subtle::CtOption::new(c, is_valid))) + }) + } +} diff --git a/forks/halo2curves/src/t256/curve.rs b/forks/halo2curves/src/t256/curve.rs new file mode 100644 index 00000000..fe8dab39 --- /dev/null +++ b/forks/halo2curves/src/t256/curve.rs @@ -0,0 +1,153 @@ +use core::{ + cmp, + fmt::Debug, + iter::Sum, + ops::{Add, Mul, Neg, Sub}, +}; + +use rand::RngCore; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +use crate::{ + ff::{Field, PrimeField, WithSmallOrderMulGroup}, + group::{prime::PrimeCurveAffine, Curve, Group as _, GroupEncoding}, + t256::{Fp, Fq}, + Coordinates, CurveAffine, CurveExt, +}; + +impl group::cofactor::CofactorGroup for T256 { + type Subgroup = T256; + + fn clear_cofactor(&self) -> Self { + *self + } + + fn into_subgroup(self) -> CtOption { + CtOption::new(self, 1.into()) + } + + fn is_torsion_free(&self) -> Choice { + 1.into() + } +} + + +const T256_GENERATOR_X: Fp = Fp::from_raw([ + 0x0000000000000005, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, +]); +const T256_GENERATOR_Y: Fp = Fp::from_raw([ + 0x5826108A653DE28D, + 0x0ED60B9E33CE397C, + 0x5EFC7B55F6B24FBE, + 0x3E86C0CFEBF2C716, +]); + +const T256_A: Fp = Fp::from_raw([ + 0x93135661B1C4B114, + 0x7E72B42B30E73177, + 0x0000000000000001, + 0xFFFFFFFF00000001, +]); +const T256_B: Fp = Fp::from_raw([ + 0x863E60F20219FC56, + 0x36B06ACEEB354224, + 0x6FB552F8E21ED4AC, + 0xB441071B12F4A036, +]); + +use crate::{ + impl_binops_additive, impl_binops_additive_specify_output, impl_binops_multiplicative, + impl_binops_multiplicative_mixed, new_curve_impl, +}; + +new_curve_impl!( + (pub), + T256, + T256Affine, + Fp, + Fq, + (T256_GENERATOR_X,T256_GENERATOR_Y), + T256_A, + T256_B, + "t256", + |domain_prefix| hash_to_curve(domain_prefix, hash_to_curve_suite(b"T256_XMD:SHA-256_SSWU_RO_")), + crate::serde::CompressedFlagConfig::Extra, + standard_sign +); + +fn hash_to_curve_suite(domain: &[u8]) -> crate::hash_to_curve::Suite { + // Z : + const SSWU_Z: Fp = Fp::from_raw([ + 0x93135661B1C4B116, + 0x7E72B42B30E73177, + 0x0000000000000001, + 0xFFFFFFFF00000001, + ]); + + let iso_map = crate::hash_to_curve::Iso { + a: T256::a(), + b: T256::b(), + map: Box::new(move |x, y, z| T256 { x, y, z }), + }; + + crate::hash_to_curve::Suite::new(domain, SSWU_Z, crate::hash_to_curve::Method::SSWU(iso_map)) +} + +#[allow(clippy::type_complexity)] +pub(crate) fn hash_to_curve<'a>( + domain_prefix: &'a str, + suite: crate::hash_to_curve::Suite, +) -> Box T256 + 'a> { + Box::new(move |message| suite.hash_to_curve(domain_prefix, message)) +} + +#[cfg(test)] +mod test { + use group::UncompressedEncoding; + use rand::{thread_rng, Rng}; + use rand_core::OsRng; + use crate::{curve, msm::{msm_best, msm_serial}}; + + use super::*; + use crate::{serde::SerdeObject, tests::curve::TestH2C}; + crate::curve_testing_suite!(T256); + crate::curve_testing_suite!(T256, "ecdsa_example"); + crate::curve_testing_suite!( + T256, + "constants", + Fp::MODULUS, + T256_A, + T256_B, + T256_GENERATOR_X, + T256_GENERATOR_Y, + Fq::MODULUS + ); + + #[test] // TODO: once we have the real T256 params we'll have to create new test vectors, and maybe some hashtocurve constants + fn test_hash_to_curve() { + + for i in 1..10 { + let r0 = ::CurveExt::hash_to_curve("t256 domain prefix")(format!("Test {}", i).as_bytes()); + assert!(r0.to_affine().is_on_curve().unwrap_u8() == 1); + assert!(r0.to_affine().is_identity().unwrap_u8() == 0); + } + } + + #[test] + fn test_msm() { + let mut rng = thread_rng(); + let mut scalars : Vec = vec![]; + let mut bases : Vec = vec![]; + for _ in 1..(1<<13) { + bases.push(T256Affine::random(&mut rng)); + scalars.push(Fq::random(&mut rng)); + } + let res1 = msm_best(&scalars, &bases); + let res2 = T256Affine::identity(); + msm_serial(&scalars, &bases, &mut res2.into()); + assert!(res1 == res1); + } +} diff --git a/forks/halo2curves/src/t256/fp.rs b/forks/halo2curves/src/t256/fp.rs new file mode 100644 index 00000000..c3fbb6a7 --- /dev/null +++ b/forks/halo2curves/src/t256/fp.rs @@ -0,0 +1,41 @@ +use core::convert::TryInto; + +use halo2derive::impl_field; +use rand::RngCore; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +impl_field!( + t256_base, + Fp, + modulus = "ffffffff0000000100000000000000017e72b42b30e7317793135661b1c4b117", + mul_gen = "6", + zeta = "b6e848ab29fcd1fa4bdd4681fd4839c7bce2889d6b27546299d130ed370be14d", // element of order 3 mod modulus, probably not used with t256 + from_uniform = [48, 64], + endian = "big", +); + +crate::extend_field_legendre!(Fp); +crate::impl_binops_calls!(Fp); +crate::impl_binops_additive!(Fp, Fp); +crate::impl_binops_multiplicative!(Fp, Fp); +crate::field_bits!(Fp); +crate::serialize_deserialize_primefield!(Fp); +crate::impl_from_u64!(Fp); +crate::impl_from_bool!(Fp); + +#[cfg(test)] +mod test { + use super::Fp; + use crate::{ + arith_test, constants_test, from_uniform_bytes_test, legendre_test, serde_test, test, + }; + + constants_test!(Fp); + + arith_test!(Fp); + legendre_test!(Fp); + test!(arith, Fp, sqrt_test, 1000); + + serde_test!(Fp PrimeFieldBits); + from_uniform_bytes_test!(Fp, 1000, L 64, L 48); +} \ No newline at end of file diff --git a/forks/halo2curves/src/t256/fq.rs b/forks/halo2curves/src/t256/fq.rs new file mode 100644 index 00000000..96e5fe5e --- /dev/null +++ b/forks/halo2curves/src/t256/fq.rs @@ -0,0 +1,41 @@ +use core::convert::TryInto; + +use halo2derive::impl_field; +use rand::RngCore; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +impl_field!( + t256_scalar, + Fq, + modulus = "ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", + mul_gen = "6", + zeta = "4d6ea8928adb86cf62388a8e0ef623312e68c59bdef3e53fd964598eb819acce", + from_uniform = [48, 64], + endian = "little", +); + +crate::extend_field_legendre!(Fq); +crate::impl_binops_calls!(Fq); +crate::impl_binops_additive!(Fq, Fq); +crate::impl_binops_multiplicative!(Fq, Fq); +crate::field_bits!(Fq); +crate::serialize_deserialize_primefield!(Fq); +crate::impl_from_u64!(Fq); +crate::impl_from_bool!(Fq); + +#[cfg(test)] +mod test { + use super::Fq; + use crate::{ + arith_test, constants_test, from_uniform_bytes_test, legendre_test, serde_test, test, + }; + + constants_test!(Fq); + + arith_test!(Fq); + legendre_test!(Fq); + test!(arith, Fq, sqrt_test, 1000); + + serde_test!(Fq PrimeFieldBits); + from_uniform_bytes_test!(Fq, 1000, L 64, L 48); +} diff --git a/forks/halo2curves/src/t256/mod.rs b/forks/halo2curves/src/t256/mod.rs new file mode 100644 index 00000000..edc3c097 --- /dev/null +++ b/forks/halo2curves/src/t256/mod.rs @@ -0,0 +1,7 @@ +mod curve; +mod fp; +mod fq; + +pub use curve::*; +pub use fp::*; +pub use fq::*; diff --git a/forks/halo2curves/src/tests/curve.rs b/forks/halo2curves/src/tests/curve.rs new file mode 100644 index 00000000..9d897bf3 --- /dev/null +++ b/forks/halo2curves/src/tests/curve.rs @@ -0,0 +1,556 @@ +#[macro_export] +macro_rules! curve_testing_suite { + ($($curve: ident),*) => { + macro_rules! is_on_curve { + ($c: ident) => { + assert!(bool::from($c::identity().is_on_curve())); + assert!(bool::from($c::generator().is_on_curve())); + + for _ in 0..100 { + let point = $c::random(OsRng); + assert!(bool::from(point.is_on_curve())); + let affine_point: <$c as CurveExt>::AffineExt = point.into(); + assert!(bool::from(affine_point.is_on_curve())); + } + } + } + + macro_rules! equality { + ($c: ident) => { + let a = $c::generator(); + let b = $c::identity(); + + assert!(a == a); + assert!(b == b); + assert!(a != b); + assert!(b != a); + + for _ in 0..100 { + let a = $c::random(OsRng); + let b = $c::random(OsRng); + + assert!(a == a); + assert!(b == b); + assert!(a != b); + assert!(b != a); + + let a: <$c as CurveExt>::AffineExt = a.into(); + let b: <$c as CurveExt>::AffineExt = b.into(); + + assert!(a == a); + assert!(b == b); + assert!(a != b); + assert!(b != a); + } + } + } + + macro_rules! projective_affine_roundtrip { + ($c: ident) => { + let a = $c::generator(); + let b = $c::identity(); + + assert!(bool::from(<$c as CurveExt>::AffineExt::from(a).is_on_curve())); + assert!(!bool::from(<$c as CurveExt>::AffineExt::from(a).is_identity())); + assert!(bool::from(<$c as CurveExt>::AffineExt::from(b).is_on_curve())); + assert!(bool::from(<$c as CurveExt>::AffineExt::from(b).is_identity())); + + let a = <$c as CurveExt>::AffineExt::generator(); + let b = <$c as CurveExt>::AffineExt::identity(); + + assert!(bool::from($c::from(a).is_on_curve())); + assert!(!bool::from($c::from(a).is_identity())); + assert!(bool::from($c::from(b).is_on_curve())); + assert!(bool::from($c::from(b).is_identity())); + } + } + + macro_rules! projective_addition { + ($c: ident) => { + let a = $c::identity(); + let b = $c::identity(); + let c = a + b; + assert!(bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + let c = a - b; + assert!(bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + + let a = $c::identity(); + let a = -a; + assert!(bool::from(a.is_on_curve())); + assert!(bool::from(a.is_identity())); + + let a = $c::random(OsRng); + assert!(a == a + $c::identity()); + assert!(a == $c::identity() + a); + assert!(-a == $c::identity() - a); + + let a = $c::identity(); + let a = a.double(); + assert!(bool::from(c.is_on_curve())); + assert!(bool::from(a.is_identity())); + + let a = $c::generator(); + let a = a.double(); + assert!(bool::from(c.is_on_curve())); + assert_eq!(a, $c::generator() + $c::generator()); + + let a = $c::random(OsRng); + assert!(a.double() - a == a); + + let a = $c::random(OsRng); + let b = $c::random(OsRng); + let c = $c::random(OsRng); + assert!(a + b == b + a); + assert!(a - b == -(b - a)); + assert!(c + (a + b) == a + (c + b)); + assert!((a - b) - c == (a - c) - b); + + let a = $c::generator().double().double(); // 4P + let b = $c::generator().double(); // 2P + let c = a + b; + + let mut d = $c::generator(); + for _ in 0..5 { + d += $c::generator(); + } + + assert!(c == d); + assert!(!bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + assert!(!bool::from(d.is_identity())); + assert!(bool::from(d.is_on_curve())); + } + } + + macro_rules! mixed_addition { + ($c: ident) => { + let a = $c::identity(); + let b = <$c as group::Curve>::AffineRepr::identity(); + let c = a + b; + assert!(bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + let c = a - b; + assert!(bool::from(c.is_identity())); + assert!(bool::from(c.is_on_curve())); + + let a = $c::identity(); + let a = -a; + assert!(bool::from(a.is_on_curve())); + assert!(bool::from(a.is_identity())); + let a = <$c as CurveExt>::AffineExt::identity(); + let a = -a; + assert!(bool::from(a.is_on_curve())); + assert!(bool::from(a.is_identity())); + + let a: <$c as CurveExt>::AffineExt = $c::random(OsRng).into(); + assert!(a.to_curve() == a + <$c as CurveExt>::AffineExt::identity()); + + let a = $c::random(OsRng); + assert!(a.double() - a == a); + + let a = $c::random(OsRng); + let b: <$c as CurveExt>::AffineExt = $c::random(OsRng).into(); + let c0 = a + b; + let c1 = a + $c::from(b); + assert_eq!(c0, c1); + } + } + + macro_rules! multiplication { + ($c: ident) => { + for _ in 1..1000 { + let s1 = <$c as CurveExt>::ScalarExt::random(OsRng); + let s2 = <$c as CurveExt>::ScalarExt::random(OsRng); + + let t0 = $c::identity() * s1; + assert!(bool::from(t0.is_identity())); + + let a = $c::random(OsRng); + let t0 = a * <$c as CurveExt>::ScalarExt::ONE; + assert_eq!(a, t0); + + let t0 = a * <$c as CurveExt>::ScalarExt::ZERO; + assert!(bool::from(t0.is_identity())); + + let t0 = a * s1 + a * s2; + + let s3 = s1 + s2; + let t1 = a * s3; + + assert_eq!(t0, t1); + + let mut t0 = a * s1; + let mut t1 = a * s2; + t0 += t1; + let s3 = s1 + s2; + t1 = a * s3; + assert_eq!(t0, t1); + } + } + } + + macro_rules! batch_normalize { + ($c: ident) => { + let a = $c::generator().double(); + let b = a.double(); + let c = b.double(); + + for a_identity in (0..1).map(|n| n == 1) { + for b_identity in (0..1).map(|n| n == 1) { + for c_identity in (0..1).map(|n| n == 1) { + let mut v = [a, b, c]; + if a_identity { + v[0] = $c::identity() + } + if b_identity { + v[1] = $c::identity() + } + if c_identity { + v[2] = $c::identity() + } + + let mut t = [ + <$c as CurveExt>::AffineExt::identity(), + <$c as CurveExt>::AffineExt::identity(), + <$c as CurveExt>::AffineExt::identity(), + ]; + let expected = [ + <$c as CurveExt>::AffineExt::from(v[0]), + <$c as CurveExt>::AffineExt::from(v[1]), + <$c as CurveExt>::AffineExt::from(v[2]), + ]; + + $c::batch_normalize(&v[..], &mut t[..]); + + assert_eq!(&t[..], &expected[..]); + } + } + } + } + } + + macro_rules! serdes { + ($c: ident) => { + assert!(bool::from( + $c::from_bytes(&$c::identity().to_bytes()) + .unwrap() + .is_identity() + )); + + assert!(bool::from( + <$c as CurveExt>::AffineExt::from_uncompressed(&<$c as CurveExt>::AffineExt::identity().to_uncompressed()) + .unwrap() + .is_identity() + )); + + assert!(bool::from( + <$c as CurveExt>::AffineExt::from_bytes(&<$c as CurveExt>::AffineExt::identity().to_bytes()) + .unwrap() + .is_identity() + )); + + for _ in 0..100 { + let projective_point = $c::random(OsRng); + let affine_point: <$c as CurveExt>::AffineExt = projective_point.into(); + + // Compressed format tests + let projective_repr = projective_point.to_bytes(); + let affine_repr = affine_point.to_bytes(); + + let projective_point_rec = $c::from_bytes(&projective_repr).unwrap(); + let projective_point_rec_unchecked = $c::from_bytes(&projective_repr).unwrap(); + + let affine_point_rec = <$c as CurveExt>::AffineExt::from_bytes(&affine_repr).unwrap(); + let affine_point_rec_unchecked = <$c as CurveExt>::AffineExt::from_bytes(&affine_repr).unwrap(); + + assert_eq!(projective_point, projective_point_rec); + assert_eq!(projective_point, projective_point_rec_unchecked); + assert_eq!(affine_point, affine_point_rec); + assert_eq!(affine_point, affine_point_rec_unchecked); + + // Uncompressed format + let affine_repr = affine_point.to_uncompressed(); + + let affine_point_rec = <$c as CurveExt>::AffineExt::from_uncompressed_unchecked(&affine_repr).unwrap(); + let affine_point_rec_unchecked = <$c as CurveExt>::AffineExt::from_uncompressed_unchecked(&affine_repr).unwrap(); + + assert_eq!(affine_point, affine_point_rec); + assert_eq!(affine_point, affine_point_rec_unchecked); + } + } + } + + // TODO Change name + macro_rules! random_serialization_test { + ($c: ident) => { + for _ in 0..100 { + let projective_point = $c::random(OsRng); + let affine_point: <$c as CurveExt>::AffineExt = projective_point.into(); + + let projective_bytes = projective_point.to_raw_bytes(); + let projective_point_rec = $c::from_raw_bytes(&projective_bytes).unwrap(); + assert_eq!(projective_point, projective_point_rec); + let mut buf = Vec::new(); + projective_point.write_raw(&mut buf).unwrap(); + let projective_point_rec = $c::read_raw(&mut &buf[..]).unwrap(); + assert_eq!(projective_point, projective_point_rec); + + let affine_bytes = affine_point.to_raw_bytes(); + let affine_point_rec = <$c as CurveExt>::AffineExt::from_raw_bytes(&affine_bytes).unwrap(); + assert_eq!(affine_point, affine_point_rec); + let mut buf = Vec::new(); + affine_point.write_raw(&mut buf).unwrap(); + let affine_point_rec = <$c as CurveExt>::AffineExt::read_raw(&mut &buf[..]).unwrap(); + assert_eq!(affine_point, affine_point_rec); + } + } + } + + #[cfg(feature = "derive_serde")] + macro_rules! random_serde_test { + ($c: ident) => { + for _ in 0..100 { + let projective_point = $c::random(OsRng); + let affine_point: <$c as CurveExt>::AffineExt = projective_point.into(); + { + let affine_bytes = bincode::serialize(&affine_point).unwrap(); + let reader = std::io::Cursor::new(affine_bytes); + let affine_point_rec: <$c as CurveExt>::AffineExt = bincode::deserialize_from(reader).unwrap(); + assert_eq!(projective_point.to_affine(), affine_point_rec); + assert_eq!(affine_point, affine_point_rec); + } + { + let affine_json = serde_json::to_string(&affine_point).unwrap(); + let reader = std::io::Cursor::new(affine_json); + let affine_point_rec: <$c as CurveExt>::AffineExt = serde_json::from_reader(reader).unwrap(); + assert_eq!(affine_point, affine_point_rec); + } + { + let projective_bytes = bincode::serialize(&projective_point).unwrap(); + let reader = std::io::Cursor::new(projective_bytes); + let projective_point_rec: $c = bincode::deserialize_from(reader).unwrap(); + assert_eq!(projective_point, projective_point_rec); + } + { + let projective_json = serde_json::to_string(&projective_point).unwrap(); + let reader = std::io::Cursor::new(projective_json); + let projective_point_rec: $c = serde_json::from_reader(reader).unwrap(); + assert_eq!(projective_point, projective_point_rec); + } + } + } + } + + + + #[test] + fn test_curve() { + $( + is_on_curve!($curve); + equality!($curve); + projective_affine_roundtrip!($curve); + projective_addition!($curve); + mixed_addition!($curve); + multiplication!($curve); + batch_normalize!($curve); + serdes!($curve); + )* + } + + #[test] + fn test_serialization() { + $( + random_serialization_test!($curve); + #[cfg(feature = "derive_serde")] + random_serde_test!($curve); + )* + } + }; + + ($($curve: ident),*, "hash_to_curve") => { + macro_rules! hash_to_curve_test { + ($c: ident) => { + let hasher = $c::hash_to_curve("test"); + let mut rng = OsRng; + for _ in 0..1000 { + let message = iter::repeat_with(|| rng.next_u32().to_be_bytes()) + .take(32) + .flatten() + .collect::>(); + assert!(bool::from(hasher(&message).is_on_curve())); + } + } + } + + #[test] + fn test_hash_to_curve() { + use rand_core::{OsRng, RngCore}; + use std::iter; + $( + hash_to_curve_test!($curve); + )* + } + }; + + ($($curve: ident),*, "clear_cofactor") => { + #[test] + fn test_cofactor_clearing() { + use rand_core::OsRng; + $( + for _ in 0..50 { + let point = $curve::random(OsRng); + assert!(bool::from(point.is_on_curve())); + use group::cofactor::CofactorGroup; + assert!(bool::from(point.is_torsion_free())); + } + + )* + } + }; + + ($($curve: ident),*, "endo_consistency") => { + #[test] + fn test_endo_consistency() { + use rand_core::OsRng; + $( + let g = $curve::generator(); + assert_eq!(g * <$curve as CurveExt>::ScalarExt::ZETA, g.endo()); + + for _ in 0..100 { + let g = $curve::random(OsRng); + assert_eq!(g * <$curve as CurveExt>::ScalarExt::ZETA, g.endo()); + } + )* + } + }; + + ($curve: ident, "endo" $(, $z_other_raw: expr)*) => { + #[test] + fn test_endo() { + use rand_core::OsRng; + + let z_impl = <$curve as CurveExt>::ScalarExt::ZETA; + assert_eq!(z_impl * z_impl + z_impl, -<$curve as CurveExt>::ScalarExt::ONE); + $( + let z_other = <$curve as CurveExt>::ScalarExt::from_raw($z_other_raw as [u64; 4]); + assert_eq!(z_other * z_other + z_other, -<$curve as CurveExt>::ScalarExt::ONE); + )* + + for _ in 0..100000 { + let k = <$curve as CurveExt>::ScalarExt::random(OsRng); + let (k1, k1_neg, k2, k2_neg) = $curve::decompose_scalar(&k); + if k1_neg & k2_neg { + assert_eq!(k, -<$curve as CurveExt>::ScalarExt::from_u128(k1) + <$curve as CurveExt>::ScalarExt::ZETA * <$curve as CurveExt>::ScalarExt::from_u128(k2)) + } else if k1_neg { + assert_eq!(k, -<$curve as CurveExt>::ScalarExt::from_u128(k1) - <$curve as CurveExt>::ScalarExt::ZETA * <$curve as CurveExt>::ScalarExt::from_u128(k2)) + } else if k2_neg { + assert_eq!(k, <$curve as CurveExt>::ScalarExt::from_u128(k1) + <$curve as CurveExt>::ScalarExt::ZETA * <$curve as CurveExt>::ScalarExt::from_u128(k2)) + } else { + assert_eq!(k, <$curve as CurveExt>::ScalarExt::from_u128(k1) - <$curve as CurveExt>::ScalarExt::ZETA * <$curve as CurveExt>::ScalarExt::from_u128(k2)) + } + } + } + }; + + ($curve: ident, "ecdsa_example") => { + #[test] + fn ecdsa_example() { + use ff::FromUniformBytes; + use rand_core::OsRng; + + fn mod_n(x: <$curve as CurveExt>::Base) -> <$curve as CurveExt>::ScalarExt { + let mut x_repr = [0u8; 32]; + x_repr.copy_from_slice(x.to_repr().as_ref()); + let mut x_bytes = [0u8; 64]; + x_bytes[..32].copy_from_slice(&x_repr[..]); + <$curve as CurveExt>::ScalarExt::from_uniform_bytes(&x_bytes) + } + + let g = $curve::generator(); + + for _ in 0..1000 { + // Generate a key pair + let sk = <$curve as CurveExt>::ScalarExt::random(OsRng); + let pk = (g * sk).to_affine(); + + // Generate a valid signature + // Suppose `m_hash` is the message hash + let msg_hash = <$curve as CurveExt>::ScalarExt::random(OsRng); + + let (r, s) = { + // Draw arandomness + let k = <$curve as CurveExt>::ScalarExt::random(OsRng); + let k_inv = k.invert().unwrap(); + + // Calculate `r` + let r_point = (g * k).to_affine().coordinates().unwrap(); + let x = r_point.x(); + let r = mod_n(*x); + + // Calculate `s` + let s = k_inv * (msg_hash + (r * sk)); + + (r, s) + }; + + { + // Verify + let s_inv = s.invert().unwrap(); + let u_1 = msg_hash * s_inv; + let u_2 = r * s_inv; + + let v_1 = g * u_1; + let v_2 = pk * u_2; + + let r_point = (v_1 + v_2).to_affine().coordinates().unwrap(); + let x_candidate = r_point.x(); + let r_candidate = mod_n(*x_candidate); + + assert_eq!(r, r_candidate); + } + } + } + }; + + ($curve: ident, "constants", $p: expr, $a: expr, $b: expr, $gen_x: expr, $gen_y: expr, $order: expr) => { + #[test] + #[allow(non_snake_case)] + fn $curve() { + assert!($p == <$curve as CurveExt>::Base::MODULUS); + + let a = $curve::a(); + let b = $curve::b(); + assert!(a == $a); + assert!(b == $b); + + let generator = $curve::generator(); + let generator_affine: <$curve as CurveExt>::AffineExt = generator.into(); + assert!(generator_affine.x == $gen_x); + assert!(generator_affine.y == $gen_y); + + assert!($order == <$curve as CurveExt>::ScalarExt::MODULUS); + } + }; +} + +use group::Curve; + +use crate::{CurveAffine, CurveExt}; + +pub(crate) struct TestH2C { + msg: &'static [u8], + expect: C, +} + +impl TestH2C { + pub(crate) fn new(msg: &'static [u8], expect: C) -> Self { + Self { msg, expect } + } + + pub(crate) fn run(&self, domain_prefix: &str) { + let r0 = C::CurveExt::hash_to_curve(domain_prefix)(self.msg); + assert_eq!(r0.to_affine(), self.expect); + } +} diff --git a/forks/halo2curves/src/tests/field.rs b/forks/halo2curves/src/tests/field.rs new file mode 100644 index 00000000..b67e6676 --- /dev/null +++ b/forks/halo2curves/src/tests/field.rs @@ -0,0 +1,39 @@ +#[cfg(test)] +pub(crate) mod arith; + +#[cfg(test)] +pub(crate) mod constants; + +#[cfg(test)] +#[macro_use] +pub(crate) mod extensions; + +#[cfg(test)] +pub(crate) mod legendre; + +#[cfg(test)] +pub(crate) mod serde; + +#[macro_export] +macro_rules! test { + ($mod: ident, $field:ident, $test:ident, $size:expr) => { + #[test] + fn $test() { + use rand::SeedableRng; + use rand_xorshift::XorShiftRng; + + use super::*; + let mut rng = XorShiftRng::from_seed($crate::tests::SEED); + $crate::tests::field::$mod::$test::<$field>(&mut rng, $size); + } + }; + ($mod: ident, $field:ident, $test:ident) => { + #[test] + fn $test() { + use rand::SeedableRng; + use rand_xorshift::XorShiftRng; + let mut rng = XorShiftRng::from_seed($crate::tests::SEED); + $crate::tests::field::$mod::$test::<$field>(&mut rng); + } + }; +} diff --git a/forks/halo2curves/src/tests/field/arith.rs b/forks/halo2curves/src/tests/field/arith.rs new file mode 100644 index 00000000..09d11af9 --- /dev/null +++ b/forks/halo2curves/src/tests/field/arith.rs @@ -0,0 +1,219 @@ +use ff::Field; +use rand::RngCore; + +pub(crate) fn mul_test(mut rng: impl RngCore, n: usize) { + for _ in 0..n { + let a = F::random(&mut rng); + let b = F::random(&mut rng); + let c = F::random(&mut rng); + + let mut t0 = a; // (a * b) * c + t0.mul_assign(&b); + t0.mul_assign(&c); + + let mut t1 = a; // (a * c) * b + t1.mul_assign(&c); + t1.mul_assign(&b); + + let mut t2 = b; // (b * c) * a + t2.mul_assign(&c); + t2.mul_assign(&a); + + assert_eq!(t0, t1); + assert_eq!(t1, t2); + } +} + +pub(crate) fn add_test(mut rng: impl RngCore, n: usize) { + for _ in 0..n { + let a = F::random(&mut rng); + let b = F::random(&mut rng); + let c = F::random(&mut rng); + + let mut t0 = a; // (a + b) + c + t0.add_assign(&b); + t0.add_assign(&c); + + let mut t1 = a; // (a + c) + b + t1.add_assign(&c); + t1.add_assign(&b); + + let mut t2 = b; // (b + c) + a + t2.add_assign(&c); + t2.add_assign(&a); + + assert_eq!(t0, t1); + assert_eq!(t1, t2); + } +} + +pub(crate) fn sub_test(mut rng: impl RngCore, n: usize) { + for _ in 0..n { + let a = F::random(&mut rng); + let b = F::random(&mut rng); + + let mut t0 = a; // (a - b) + t0.sub_assign(&b); + + let mut t1 = b; // (b - a) + t1.sub_assign(&a); + + let mut t2 = t0; // (a - b) + (b - a) = 0 + t2.add_assign(&t1); + + assert_eq!(t2.is_zero().unwrap_u8(), 1); + } +} + +pub(crate) fn neg_test(mut rng: impl RngCore, n: usize) { + for _ in 0..n { + let a = F::random(&mut rng); + let mut b = a; + b = b.neg(); + b.add_assign(&a); + + assert_eq!(b.is_zero().unwrap_u8(), 1); + } +} + +pub(crate) fn double_test(mut rng: impl RngCore, n: usize) { + for _ in 0..n { + let mut a = F::random(&mut rng); + let mut b = a; + a.add_assign(&b); + b = b.double(); + + assert_eq!(a, b); + } +} + +pub(crate) fn square_test(mut rng: impl RngCore, n: usize) { + for _ in 0..n { + let mut a = F::random(&mut rng); + let mut b = a; + a.mul_assign(&b); + b = b.square(); + + assert_eq!(a, b); + } +} + +pub(crate) fn inv_test(mut rng: impl RngCore, n: usize) { + assert!(bool::from(F::ZERO.invert().is_none())); + + for _ in 0..n { + let mut a = F::random(&mut rng); + let b = a.invert().unwrap(); // probabilistically nonzero + a.mul_assign(&b); + + assert_eq!(a, F::ONE); + } +} + +pub(crate) fn expansion_test(mut rng: impl RngCore, n: usize) { + for _ in 0..n { + // Compare (a + b)(c + d) and (a*c + b*c + a*d + b*d) + + let a = F::random(&mut rng); + let b = F::random(&mut rng); + let c = F::random(&mut rng); + let d = F::random(&mut rng); + + let mut t0 = a; + t0.add_assign(&b); + let mut t1 = c; + t1.add_assign(&d); + t0.mul_assign(&t1); + + let mut t2 = a; + t2.mul_assign(&c); + let mut t3 = b; + t3.mul_assign(&c); + let mut t4 = a; + t4.mul_assign(&d); + let mut t5 = b; + t5.mul_assign(&d); + + t2.add_assign(&t3); + t2.add_assign(&t4); + t2.add_assign(&t5); + + assert_eq!(t0, t2); + } +} + +pub(crate) fn zero_test(mut rng: impl RngCore) { + assert_eq!(F::ZERO.is_zero().unwrap_u8(), 1); + { + let mut z = F::ZERO; + z = z.neg(); + assert_eq!(z.is_zero().unwrap_u8(), 1); + } + + assert!(bool::from(F::ZERO.invert().is_none())); + + // Multiplication by zero + { + let mut a = F::random(&mut rng); + a.mul_assign(&F::ZERO); + assert_eq!(a.is_zero().unwrap_u8(), 1); + } + + // Addition by zero + { + let mut a = F::random(&mut rng); + let copy = a; + a.add_assign(&F::ZERO); + assert_eq!(a, copy); + } +} + +pub(crate) fn one_test(mut rng: impl RngCore) { + assert!(bool::from(F::ONE.invert().is_some())); + + // Multiplication by one + { + let mut a = F::random(&mut rng); + let copy = a; + a.mul_assign(&F::ONE); + assert_eq!(a, copy); + } + + // Addition by one + { + let mut a = F::random(&mut rng); + let copy = a; + a.add_assign(&F::ONE); + assert_eq!(a, copy + F::ONE); + } +} + +#[macro_export] +macro_rules! arith_test { + ($field:ident) => { + test!(arith, $field, mul_test, 1000); + test!(arith, $field, add_test, 1000); + test!(arith, $field, sub_test, 1000); + test!(arith, $field, neg_test, 1000); + test!(arith, $field, double_test, 1000); + test!(arith, $field, square_test, 1000); + test!(arith, $field, inv_test, 1000); + test!(arith, $field, expansion_test, 1000); + test!(arith, $field, one_test); + test!(arith, $field, zero_test); + }; +} + +// This test is autside the `arith_test` macro since it is only +// implemented for prime fields and quadratic extensions. +pub(crate) fn sqrt_test(mut rng: impl RngCore, n: usize) { + for _ in 0..n { + let a = F::random(&mut rng); + let b = a.square(); + + let b = b.sqrt().unwrap(); + let negb = b.neg(); + + assert!(a == b || a == negb); + } +} diff --git a/forks/halo2curves/src/tests/field/constants.rs b/forks/halo2curves/src/tests/field/constants.rs new file mode 100644 index 00000000..bbf7b438 --- /dev/null +++ b/forks/halo2curves/src/tests/field/constants.rs @@ -0,0 +1,32 @@ +use ff::PrimeField; +pub(crate) fn primefield_constants_test() { + assert_eq!(F::ROOT_OF_UNITY_INV, F::ROOT_OF_UNITY.invert().unwrap()); + assert_eq!(F::from(2) * F::TWO_INV, F::ONE); + if F::S != 0 { + assert_eq!(F::ROOT_OF_UNITY.pow_vartime([1 << F::S]), F::ONE); + assert_eq!(F::DELTA, F::MULTIPLICATIVE_GENERATOR.pow([1u64 << F::S])); + } +} + +use ff::WithSmallOrderMulGroup; +pub(crate) fn zeta_test>() { + assert_eq!(F::ZETA * F::ZETA * F::ZETA, F::ONE); + assert_ne!(F::ZETA * F::ZETA, F::ONE); +} + +#[macro_export] +macro_rules! constants_test { + ($field:ident) => { + #[test] + fn primefield_constants_test() { + use super::*; + $crate::tests::field::constants::primefield_constants_test::<$field>(); + } + + #[test] + fn zeta_test() { + use super::*; + $crate::tests::field::constants::zeta_test::<$field>(); + } + }; +} diff --git a/forks/halo2curves/src/tests/field/extensions.rs b/forks/halo2curves/src/tests/field/extensions.rs new file mode 100644 index 00000000..a0079c85 --- /dev/null +++ b/forks/halo2curves/src/tests/field/extensions.rs @@ -0,0 +1,206 @@ +// F2 tests +#[macro_export] +macro_rules! f2_test { + ($ext_field:ident, + $base_field:ident) => { + #[test] + fn f2_ordering_test() { + use ark_std::cmp::Ordering; + let mut a = $ext_field { + c0: $base_field::zero(), + c1: $base_field::zero(), + }; + + let mut b = a; + + assert!(a.cmp(&b) == Ordering::Equal); + b.c0 += &$base_field::one(); + assert!(a.cmp(&b) == Ordering::Less); + a.c0 += &$base_field::one(); + assert!(a.cmp(&b) == Ordering::Equal); + b.c1 += &$base_field::one(); + assert!(a.cmp(&b) == Ordering::Less); + a.c0 += &$base_field::one(); + assert!(a.cmp(&b) == Ordering::Less); + a.c1 += &$base_field::one(); + assert!(a.cmp(&b) == Ordering::Greater); + b.c0 += &$base_field::one(); + assert!(a.cmp(&b) == Ordering::Equal); + } + + #[test] + fn f2_zero_one_test() { + assert_eq!( + $ext_field { + c0: $base_field::zero(), + c1: $base_field::zero(), + }, + $ext_field::ZERO + ); + assert_eq!( + $ext_field { + c0: $base_field::one(), + c1: $base_field::zero(), + }, + $ext_field::ONE + ); + assert_eq!($ext_field::ZERO.is_zero().unwrap_u8(), 1); + assert_eq!($ext_field::ONE.is_zero().unwrap_u8(), 0); + assert_eq!( + $ext_field { + c0: $base_field::zero(), + c1: $base_field::one(), + } + .is_zero() + .unwrap_u8(), + 0 + ); + } + }; +} + +// F6 tests +#[macro_export] +macro_rules! setup_f6_test_funcs { + ($ext_field:ident, + $base_field:ident) => { + fn f6_mul_nonresidue_(mut rng: impl RngCore, n: usize) { + let nqr = $ext_field { + c0: $base_field::zero(), + c1: $base_field::one(), + c2: $base_field::zero(), + }; + + for _ in 0..n { + let mut a = $ext_field::random(&mut rng); + let mut b = a; + a = a.mul_by_nonresidue(); + b.mul_assign(&nqr); + + assert_eq!(a, b); + } + } + + fn f6_mul_by_1_(mut rng: impl RngCore, n: usize) { + for _ in 0..n { + let c1 = $base_field::random(&mut rng); + let mut a = $ext_field::random(&mut rng); + let mut b = a; + + a = <$ext_field as CubicSparseMul>::mul_by_1(&a, &c1); + b.mul_assign(&$ext_field { + c0: $base_field::zero(), + c1, + c2: $base_field::zero(), + }); + + assert_eq!(a, b); + } + } + + fn f6_mul_by_01_(mut rng: impl RngCore, n: usize) { + for _ in 0..n { + let c0 = $base_field::random(&mut rng); + let c1 = $base_field::random(&mut rng); + let mut a = $ext_field::random(&mut rng); + let mut b = a; + + a = <$ext_field as CubicSparseMul>::mul_by_01(&a, &c0, &c1); + b.mul_assign(&$ext_field { + c0, + c1, + c2: $base_field::zero(), + }); + + assert_eq!(a, b); + } + } + }; +} + +// F12 tests +#[macro_export] +macro_rules! setup_f12_test_funcs { + ($ext_field:ident, + $base_field_1:ident, + $base_field_2:ident) => { + fn f12_mul_by_014_(mut rng: impl RngCore, n: usize) { + for _ in 0..n { + let c0 = $base_field_2::random(&mut rng); + let c1 = $base_field_2::random(&mut rng); + let c5 = $base_field_2::random(&mut rng); + let mut a = $ext_field::random(&mut rng); + let mut b = a; + <$ext_field as QuadSparseMul>::mul_by_014(&mut a, &c0, &c1, &c5); + b.mul_assign(&$ext_field { + c0: $base_field_1 { + c0, + c1, + c2: $base_field_2::zero(), + }, + c1: $base_field_1 { + c0: $base_field_2::zero(), + c1: c5, + c2: $base_field_2::zero(), + }, + }); + + assert_eq!(a, b); + } + } + + fn f12_mul_by_034_(mut rng: impl RngCore, n: usize) { + for _ in 0..n { + let c0 = $base_field_2::random(&mut rng); + let c3 = $base_field_2::random(&mut rng); + let c4 = $base_field_2::random(&mut rng); + let mut a = $ext_field::random(&mut rng); + let mut b = a; + + <$ext_field as QuadSparseMul>::mul_by_034(&mut a, &c0, &c3, &c4); + b.mul_assign(&$ext_field { + c0: $base_field_1 { + c0, + c1: $base_field_2::zero(), + c2: $base_field_2::zero(), + }, + c1: $base_field_1 { + c0: c3, + c1: c4, + c2: $base_field_2::zero(), + }, + }); + assert_eq!(a, b); + } + } + }; +} + +#[macro_export] +macro_rules! frobenius_test { + ($field:ident, $base: ident, $size: expr) => { + fn test_frobenius(mut rng: impl RngCore, n: usize) { + for _ in 0..n { + for i in 0..12 { + let mut a = $field::random(&mut rng); + let mut b = a; + + for _ in 0..i { + a = a.pow($base::MODULUS_LIMBS); + } + b.frobenius_map(i); + + assert_eq!(a, b); + } + } + } + + #[test] + fn frobenius_test() { + use rand::SeedableRng; + use rand_xorshift::XorShiftRng; + let mut rng = XorShiftRng::from_seed($crate::tests::SEED); + test_frobenius(&mut rng, $size); + } + }; +} diff --git a/forks/halo2curves/src/tests/field/legendre.rs b/forks/halo2curves/src/tests/field/legendre.rs new file mode 100644 index 00000000..7b60627d --- /dev/null +++ b/forks/halo2curves/src/tests/field/legendre.rs @@ -0,0 +1,33 @@ +use ff::PrimeField; +use rand::RngCore; + +use crate::ff_ext::Legendre; + +pub(crate) fn legendre_symbol_test(mut rng: impl RngCore, n: usize) { + assert_eq!(F::ZERO.legendre(), 0); + for _ in 0..n { + let a = F::random(&mut rng); + if a.legendre() == -1 { + assert!(bool::from(a.sqrt().is_none())); + } + let b = a.square(); + assert_eq!(b.legendre(), 1); + } +} + +pub(crate) fn quadratic_residue_test(mut rng: impl RngCore, n: usize) { + for _ in 0..n { + let elem = F::random(&mut rng); + let is_quad_non_res: bool = elem.ct_quadratic_non_residue().into(); + let is_quad_res_or_zero: bool = elem.ct_quadratic_residue().into(); + assert_eq!(!is_quad_non_res, is_quad_res_or_zero) + } +} + +#[macro_export] +macro_rules! legendre_test { + ($field:ident) => { + test!(legendre, $field, legendre_symbol_test, 1000); + test!(legendre, $field, quadratic_residue_test, 1000); + }; +} diff --git a/forks/halo2curves/src/tests/field/serde.rs b/forks/halo2curves/src/tests/field/serde.rs new file mode 100644 index 00000000..9192b09b --- /dev/null +++ b/forks/halo2curves/src/tests/field/serde.rs @@ -0,0 +1,127 @@ +use ff::{Field, FromUniformBytes, PrimeField}; +use rand::RngCore; + +use crate::serde::SerdeObject; + +// Tests to_repr/ from_repr +pub(crate) fn from_to_repr_test(mut rng: impl RngCore, n: usize) { + // n = 1M + for _ in 0..n { + let a = F::random(&mut rng); + let bytes = a.to_repr(); + let b = F::from_repr(bytes).unwrap(); + assert_eq!(a, b); + } +} + +// Tests to_raw_bytes / from_raw_bytes + read_raw /write_raw +pub(crate) fn from_to_raw_bytes_test(mut rng: impl RngCore, n: usize) { + for _ in 0..n { + let a = F::random(&mut rng); + let bytes = a.to_raw_bytes(); + let b = F::from_raw_bytes(&bytes).unwrap(); + assert_eq!(a, b); + + let mut buf = Vec::new(); + a.write_raw(&mut buf).unwrap(); + let b = F::read_raw(&mut &buf[..]).unwrap(); + assert_eq!(a, b); + } +} + +// Tests derive_serde +#[cfg(feature = "derive_serde")] +pub(crate) fn derive_serde_test(mut rng: impl RngCore, n: usize) +where + for<'de> F: Field + serde::Serialize + serde::Deserialize<'de>, +{ + for _ in 0..n { + // byte serialization + let a = F::random(&mut rng); + let bytes = bincode::serialize(&a).unwrap(); + let reader = std::io::Cursor::new(bytes); + let b = bincode::deserialize_from(reader).unwrap(); + assert_eq!(a, b); + + // json serialization + let json = serde_json::to_string(&a).unwrap(); + let reader = std::io::Cursor::new(json); + let b: F = serde_json::from_reader(reader).unwrap(); + assert_eq!(a, b); + } +} + +#[cfg(feature = "bits")] +pub(crate) fn test_bits(mut rng: impl RngCore, n: usize) { + for _ in 0..n { + let a = F::random(&mut rng); + let bytes = a.to_repr(); + let bits = a.to_le_bits(); + for idx in 0..bits.len() { + assert_eq!(bits[idx], ((bytes.as_ref()[idx / 8] >> (idx % 8)) & 1) == 1); + } + } +} + +#[macro_export] +macro_rules! serde_test { + ($field:ident) => { + test!(serde, $field, from_to_repr_test, 100_000); + test!(serde, $field, from_to_raw_bytes_test, 100_000); + #[cfg(feature = "derive_serde")] + test!(serde, $field, derive_serde_test, 100_000); + }; + + ($field:ident PrimeFieldBits) => { + serde_test!($field); + #[cfg(feature = "bits")] + test!(serde, $field, test_bits, 100_000); + }; +} + +// Out of serde_tests macro, since it needs to be tested for several generic L. +// Tests from_uniform_bytes **for prime fields only**. +pub(crate) fn from_uniform_bytes_test( + mut rng: impl RngCore, + n: usize, +) where + F: FromUniformBytes, +{ + use num_bigint::BigUint; + + let uniform_bytes = [0u8; L]; + assert_eq!(F::from_uniform_bytes(&uniform_bytes), F::ZERO); + + let mut uniform_bytes = [u8::MAX; L]; + + for _ in 0..n { + let e0 = BigUint::from_bytes_le(&uniform_bytes); + let e0: F = crate::tests::big_to_fe(&e0); + + let e1 = F::from_uniform_bytes(&uniform_bytes); + assert_eq!(e0, e1); + + rng.fill_bytes(&mut uniform_bytes[..]); + } +} + +#[macro_export] +macro_rules! from_uniform_bytes_test { + ($field:ident, $size:expr, L $L: expr ) => { + paste::paste! { + #[test] + fn [< from_uniform_bytes_test_ $L>]() { + use rand::SeedableRng; + use rand_xorshift::XorShiftRng; + let mut rng = XorShiftRng::from_seed($crate::tests::SEED); + $crate::tests::field::serde::from_uniform_bytes_test::<$field, $L>(&mut rng, $size); + } + + } + }; + + ($field:ident,$size:expr, L $L:expr, $(L $rest:expr),+ ) => { + from_uniform_bytes_test!( $field, $size, L $L ); + from_uniform_bytes_test! { $field, $size, $(L $rest),+ } + }; +} diff --git a/forks/halo2curves/src/tests/mod.rs b/forks/halo2curves/src/tests/mod.rs new file mode 100644 index 00000000..d365e17d --- /dev/null +++ b/forks/halo2curves/src/tests/mod.rs @@ -0,0 +1,53 @@ +use ff::PrimeField; +use num_bigint::BigUint; + +use crate::CurveAffine; + +pub mod curve; +#[macro_use] +pub mod field; +pub mod pairing; + +// SEED for random tests. +pub(crate) const SEED: [u8; 16] = [ + 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, +]; +// Helper functions for converting between +// Field <> BigInt <> Hex string +pub(crate) fn hex_to_bytes(hex: &str) -> Vec { + let bytes = hex.as_bytes().to_vec(); + bytes + .chunks(2) + .map(|chunk| u8::from_str_radix(std::str::from_utf8(chunk).unwrap(), 16).unwrap()) + .collect() +} + +pub(crate) fn hex_to_field(hex: &str) -> F { + let mut bytes = hex_to_bytes(hex); + bytes.reverse(); + let mut repr = F::Repr::default(); + repr.as_mut()[..bytes.len()].copy_from_slice(&bytes); + F::from_repr(repr).unwrap() +} + +pub(crate) fn point_from_hex(x: &str, y: &str) -> C { + let x = hex_to_field(x); + let y = hex_to_field(y); + C::from_xy(x, y).unwrap() +} + +pub(crate) fn fe_to_big(fe: &F) -> BigUint { + BigUint::from_bytes_le(fe.to_repr().as_ref()) +} + +pub fn big_to_fe(e: &BigUint) -> F { + let e = e % modulus::(); + let bytes = e.to_bytes_le(); + let mut repr = F::Repr::default(); + repr.as_mut()[..bytes.len()].copy_from_slice(&bytes[..]); + F::from_repr(repr).unwrap() +} + +pub(crate) fn modulus() -> BigUint { + fe_to_big(&-F::ONE) + 1usize +} diff --git a/forks/halo2curves/src/tests/pairing.rs b/forks/halo2curves/src/tests/pairing.rs new file mode 100644 index 00000000..d51d0929 --- /dev/null +++ b/forks/halo2curves/src/tests/pairing.rs @@ -0,0 +1,147 @@ +#[macro_export] +macro_rules! test_pairing { + ( + $engine:ident, + $g1:ident, + $g1affine:ident, + $g2:ident, + $g2affine:ident, + $base:ident, + $target:ident, + $scalar:ident + ) => { + #[test] + fn test_miller_loop_identity() { + use ff::Field; + assert_eq!($base::ONE.final_exponentiation(), $target::identity()); + + assert_eq!( + multi_miller_loop(&[(&$g1affine::identity(), &$g2affine::generator().into())]), + $base::one() + ); + assert_eq!( + multi_miller_loop(&[(&$g1affine::generator(), &$g2affine::identity().into())]), + $base::one() + ); + assert_ne!( + multi_miller_loop(&[ + (&$g1affine::generator(), &$g2affine::generator().into()), + (&-$g1affine::generator(), &$g2affine::generator().into()) + ]), + $base::one() + ); + assert_eq!( + multi_miller_loop(&[ + (&$g1affine::generator(), &$g2affine::generator().into()), + (&-$g1affine::generator(), &$g2affine::generator().into()) + ]) + .final_exponentiation(), + $target::identity() + ); + } + + #[test] + fn test_unitary() { + let g = $g1affine::generator(); + let h = $g2affine::generator(); + let p = -$engine::pairing(&g, &h); + let q = $engine::pairing(&g, &-h); + let r = $engine::pairing(&-g, &h); + assert_eq!(p, q); + assert_eq!(q, r); + } + + #[test] + fn test_bilinearity() { + use $crate::ff::Field; + + let a = $scalar::random(OsRng); + let b = $scalar::random(OsRng); + + let g1 = $g1::generator(); + let g2 = $g2::generator(); + + let a1 = g1 * a; + let b2 = g2 * b; + let u0 = $engine::pairing(&a1.into(), &b2.into()); + + let b1 = g1 * b; + let a2 = g2 * a; + let u1 = $engine::pairing(&b1.into(), &a2.into()); + assert_eq!(u0, u1); + + let u1 = $engine::pairing(&g1.into(), &g2.into()) * (a * b); + assert_eq!(u0, u1); + } + + #[test] + pub fn engine_tests() { + for _ in 0..10 { + let a: $g1affine = $g1::random(OsRng).into(); + let b: $g2affine = $g2::random(OsRng).into(); + + assert!(a.pairing_with(&b) == b.pairing_with(&a)); + } + + for _ in 0..1000 { + let z1 = $g1affine::identity(); + let z2 = $g2affine::identity(); + + let a = $g1::random(OsRng).into(); + let b = $g2::random(OsRng).into(); + let c = $g1::random(OsRng).into(); + let d = $g2::random(OsRng).into(); + + assert_eq!( + $base::one(), + multi_miller_loop(&[(&z1, &b)]).final_exponentiation().0, + ); + + assert_eq!( + $base::one(), + multi_miller_loop(&[(&a, &z2)]).final_exponentiation().0, + ); + + assert_eq!( + multi_miller_loop(&[(&z1, &b), (&c, &d)]).final_exponentiation(), + multi_miller_loop(&[(&a, &z2), (&c, &d)]).final_exponentiation(), + ); + + assert_eq!( + multi_miller_loop(&[(&a, &b), (&z1, &d)]).final_exponentiation(), + multi_miller_loop(&[(&a, &b), (&c, &z2)]).final_exponentiation(), + ); + } + } + + #[test] + fn test_pairing_check() { + let n = 10; + let g1 = $g1::generator().to_affine(); + let g2 = $g2::generator().to_affine(); + let scalars = (0..n) + .map(|_| ($scalar::random(OsRng), $scalar::random(OsRng))) + .collect::>(); + let terms = scalars + .iter() + .map(|(a, b)| ((g1 * a).to_affine(), (g2 * b).to_affine())) + .collect::>(); + let mut terms = terms.iter().map(|(a, b)| (a, b)).collect::>(); + let gt = $engine::pairing(&g1, &g2); + let u0 = scalars + .iter() + .fold($target::identity(), |acc, (a, b)| acc + gt * a * b); + let u1 = multi_miller_loop(&terms[..]).final_exponentiation(); + assert_eq!(u1, u0); + + let last = scalars + .iter() + .fold($scalar::ZERO, |acc, (u0, u1)| acc + u0 * u1); + let negg1 = -g1; + let accg2 = (g2 * last).into(); + terms.push((&negg1, &accg2)); + let must_be_one = multi_miller_loop(&terms[..]).final_exponentiation(); + assert_eq!(must_be_one, $target::identity()); + } + }; +} diff --git a/sample/client/setup_client.sh b/sample/client/setup_client.sh index 2a01a5d2..60c068bc 100755 --- a/sample/client/setup_client.sh +++ b/sample/client/setup_client.sh @@ -16,7 +16,7 @@ pushd $CRESCENT_DIR > /dev/null cargo install wasm-pack # Build crescent wasm package -RUSTFLAGS="-A unused-imports -A unused-assignments -A unused-variables" \ +RUSTFLAGS="-A unused-imports -A unused-assignments -A unused-variables --cfg getrandom_backend=\"wasm_js\"" \ wasm-pack build --target web --no-default-features --features wasm || \ echo -e "\n\033[33m[WARNING] wasm-pack build failed. Proceeding without it.\033[0m\n" diff --git a/sample/setup-sample.sh b/sample/setup-sample.sh index 2e46cf03..b6b20bdd 100755 --- a/sample/setup-sample.sh +++ b/sample/setup-sample.sh @@ -27,5 +27,5 @@ cd client npm run build:debug # until we have an issuer to issue mDLs, we use the ones generated in the Crescent lib # the sample expects a hex file, so we convert the binary file to hex -hexdump -v -e '1/1 "%02x"' "../../circuit_setup/inputs/mdl1/mdl.cbor" > "mdl.cbor.hex" +xxd -p -c 0 "../../circuit_setup/inputs/mdl1/mdl.cbor" | tr -d '\n' > mdl.cbor.hex cd ..