From 5a2e917dd5a243301745a3d1ee3c450c71ffbcbd Mon Sep 17 00:00:00 2001 From: Bobbin Threadbare <43513081+bobbinth@users.noreply.github.com> Date: Sun, 24 Mar 2024 08:38:08 -0700 Subject: [PATCH] Tracking PR for v0.9.0 release (#278) * chore: update crate version to v0.9.0 * chore: remove deprecated re-exports * chore: remove Box re-export * feat: implement pure-Rust keygen and signing for RpoFalcon512 (#285) * feat: add reproducible builds (#296) * fix: address a few issues for migrating Miden VM (#298) * feat: add RngCore supertrait for FeltRng (#299) --------- Co-authored-by: Al-Kindi-0 <82364884+Al-Kindi-0@users.noreply.github.com> Co-authored-by: Paul-Henry Kajfasz <42912740+phklive@users.noreply.github.com> --- .github/workflows/ci.yml | 133 -- .github/workflows/doc.yml | 31 + .github/workflows/lint.yml | 66 + .github/workflows/no-std.yml | 32 + .github/workflows/test.yml | 34 + .gitignore | 4 - .gitmodules | 3 - CHANGELOG.md | 6 + Cargo.lock | 1164 +++++++++++++ Cargo.toml | 44 +- Makefile.toml | 86 + PQClean | 1 - README.md | 24 +- build.rs | 31 - rust-toolchain | 1 + scripts/check-rust-version.sh | 13 + src/dsa/rpo_falcon512/error.rs | 56 - src/dsa/rpo_falcon512/falcon_c/falcon.c | 402 ----- src/dsa/rpo_falcon512/falcon_c/falcon.h | 66 - src/dsa/rpo_falcon512/falcon_c/rpo.c | 582 ------- src/dsa/rpo_falcon512/falcon_c/rpo.h | 83 - src/dsa/rpo_falcon512/ffi.rs | 194 --- src/dsa/rpo_falcon512/hash_to_point.rs | 68 + src/dsa/rpo_falcon512/keys.rs | 235 --- src/dsa/rpo_falcon512/keys/mod.rs | 53 + src/dsa/rpo_falcon512/keys/public_key.rs | 138 ++ src/dsa/rpo_falcon512/keys/secret_key.rs | 386 +++++ src/dsa/rpo_falcon512/math/ffsampling.rs | 123 ++ src/dsa/rpo_falcon512/math/fft.rs | 1927 ++++++++++++++++++++++ src/dsa/rpo_falcon512/math/field.rs | 172 ++ src/dsa/rpo_falcon512/math/mod.rs | 320 ++++ src/dsa/rpo_falcon512/math/polynomial.rs | 616 +++++++ src/dsa/rpo_falcon512/math/samplerz.rs | 298 ++++ src/dsa/rpo_falcon512/mod.rs | 86 +- src/dsa/rpo_falcon512/polynomial.rs | 279 ---- src/dsa/rpo_falcon512/signature.rs | 468 +++--- src/rand/mod.rs | 3 +- src/rand/rpo.rs | 34 +- src/utils/mod.rs | 8 +- 39 files changed, 5950 insertions(+), 2320 deletions(-) delete mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/doc.yml create mode 100644 .github/workflows/lint.yml create mode 100644 .github/workflows/no-std.yml create mode 100644 .github/workflows/test.yml create mode 100644 Cargo.lock create mode 100644 Makefile.toml delete mode 160000 PQClean create mode 100644 rust-toolchain create mode 100755 scripts/check-rust-version.sh delete mode 100644 src/dsa/rpo_falcon512/error.rs delete mode 100644 src/dsa/rpo_falcon512/falcon_c/falcon.c delete mode 100644 src/dsa/rpo_falcon512/falcon_c/falcon.h delete mode 100644 src/dsa/rpo_falcon512/falcon_c/rpo.c delete mode 100644 src/dsa/rpo_falcon512/falcon_c/rpo.h delete mode 100644 src/dsa/rpo_falcon512/ffi.rs create mode 100644 src/dsa/rpo_falcon512/hash_to_point.rs delete mode 100644 src/dsa/rpo_falcon512/keys.rs create mode 100644 src/dsa/rpo_falcon512/keys/mod.rs create mode 100644 src/dsa/rpo_falcon512/keys/public_key.rs create mode 100644 src/dsa/rpo_falcon512/keys/secret_key.rs create mode 100644 src/dsa/rpo_falcon512/math/ffsampling.rs create mode 100644 src/dsa/rpo_falcon512/math/fft.rs create mode 100644 src/dsa/rpo_falcon512/math/field.rs create mode 100644 src/dsa/rpo_falcon512/math/mod.rs create mode 100644 src/dsa/rpo_falcon512/math/polynomial.rs create mode 100644 src/dsa/rpo_falcon512/math/samplerz.rs delete mode 100644 src/dsa/rpo_falcon512/polynomial.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 1313d644..00000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,133 +0,0 @@ -name: CI -on: - push: - branches: - - main - pull_request: - types: [opened, reopened, synchronize] - -jobs: - rustfmt: - name: rustfmt ${{matrix.toolchain}} on ${{matrix.os}} - runs-on: ${{matrix.os}}-latest - strategy: - fail-fast: false - matrix: - toolchain: [nightly] - os: [ubuntu] - steps: - - uses: actions/checkout@v4 - - name: Install minimal Rust with rustfmt - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: ${{matrix.toolchain}} - components: rustfmt - override: true - - name: fmt - uses: actions-rs/cargo@v1 - with: - command: fmt - args: --all -- --check - - clippy: - name: clippy ${{matrix.toolchain}} on ${{matrix.os}} - runs-on: ${{matrix.os}}-latest - strategy: - fail-fast: false - matrix: - toolchain: [nightly] - os: [ubuntu] - steps: - - uses: actions/checkout@v4 - with: - submodules: recursive - - name: Install minimal Rust with clippy - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: ${{matrix.toolchain}} - components: clippy - override: true - - name: Clippy - uses: actions-rs/cargo@v1 - with: - command: clippy - args: --all-targets -- -D clippy::all -D warnings - - name: Clippy all features - uses: actions-rs/cargo@v1 - with: - command: clippy - args: --all-targets --all-features -- -D clippy::all -D warnings - - test: - name: test ${{matrix.toolchain}} on ${{matrix.os}} with ${{matrix.features}} - runs-on: ${{matrix.os}}-latest - strategy: - fail-fast: false - matrix: - toolchain: [stable, nightly] - os: [ubuntu] - features: ["--features default,serde", --no-default-features] - timeout-minutes: 30 - steps: - - uses: actions/checkout@v4 - with: - submodules: recursive - - name: Install rust - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{matrix.toolchain}} - override: true - - name: Test - uses: actions-rs/cargo@v1 - with: - command: test - args: ${{matrix.features}} - - no-std: - name: build ${{matrix.toolchain}} no-std for wasm32-unknown-unknown - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - toolchain: [stable, nightly] - steps: - - uses: actions/checkout@v4 - with: - submodules: recursive - - name: Install rust - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{matrix.toolchain}} - override: true - - run: rustup target add wasm32-unknown-unknown - - name: Build - uses: actions-rs/cargo@v1 - with: - command: build - args: --no-default-features --target wasm32-unknown-unknown - - docs: - name: Verify the docs on ${{matrix.toolchain}} - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - toolchain: [stable] - steps: - - uses: actions/checkout@v4 - with: - submodules: recursive - - name: Install rust - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{matrix.toolchain}} - override: true - - name: Check docs - uses: actions-rs/cargo@v1 - env: - RUSTDOCFLAGS: -D warnings - with: - command: doc - args: --verbose --all-features --keep-going diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml new file mode 100644 index 00000000..a192a018 --- /dev/null +++ b/.github/workflows/doc.yml @@ -0,0 +1,31 @@ +# Runs documentation related jobs. + +name: doc + +on: + push: + branches: + - main + pull_request: + types: [opened, reopened, synchronize] + +jobs: + docs: + name: Verify the docs on ${{matrix.toolchain}} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + toolchain: [stable] + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + - name: Install rust + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{matrix.toolchain}} + override: true + - uses: davidB/rust-cargo-make@v1 + - name: cargo make - doc + run: cargo make doc diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 00000000..97c32fd0 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,66 @@ +# Runs linting related jobs. + +name: lint + +on: + push: + branches: + - main + pull_request: + types: [opened, reopened, synchronize] + +jobs: + version: + name: check rust version consistency + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + profile: minimal + override: true + - name: check rust versions + run: ./scripts/check-rust-version.sh + + rustfmt: + name: rustfmt ${{matrix.toolchain}} on ${{matrix.os}} + runs-on: ${{matrix.os}}-latest + strategy: + fail-fast: false + matrix: + toolchain: [nightly] + os: [ubuntu] + steps: + - uses: actions/checkout@v4 + - name: Install minimal Rust with rustfmt + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{matrix.toolchain}} + components: rustfmt + override: true + - uses: davidB/rust-cargo-make@v1 + - name: cargo make - format-check + run: cargo make format-check + + clippy: + name: clippy ${{matrix.toolchain}} on ${{matrix.os}} + runs-on: ${{matrix.os}}-latest + strategy: + fail-fast: false + matrix: + toolchain: [stable] + os: [ubuntu] + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + - name: Install minimal Rust with clippy + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{matrix.toolchain}} + components: clippy + override: true + - uses: davidB/rust-cargo-make@v1 + - name: cargo make - clippy + run: cargo make clippy diff --git a/.github/workflows/no-std.yml b/.github/workflows/no-std.yml new file mode 100644 index 00000000..7a243d6d --- /dev/null +++ b/.github/workflows/no-std.yml @@ -0,0 +1,32 @@ +# Runs no-std related jobs. + +name: no-std + +on: + push: + branches: + - main + pull_request: + types: [opened, reopened, synchronize] + +jobs: + no-std: + name: build ${{matrix.toolchain}} no-std for wasm32-unknown-unknown + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + toolchain: [stable, nightly] + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + - name: Install rust + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{matrix.toolchain}} + override: true + - run: rustup target add wasm32-unknown-unknown + - uses: davidB/rust-cargo-make@v1 + - name: cargo make - build-no-std + run: cargo make build-no-std diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..6fb62526 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,34 @@ +# Runs testing related jobs + +name: test + +on: + push: + branches: + - main + pull_request: + types: [opened, reopened, synchronize] + +jobs: + test: + name: test ${{matrix.toolchain}} on ${{matrix.os}} with ${{matrix.features}} + runs-on: ${{matrix.os}}-latest + strategy: + fail-fast: false + matrix: + toolchain: [stable, nightly] + os: [ubuntu] + features: ["test", "test-no-default-features"] + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + - name: Install rust + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{matrix.toolchain}} + override: true + - uses: davidB/rust-cargo-make@v1 + - name: cargo make - test + run: cargo make ${{matrix.features}} diff --git a/.gitignore b/.gitignore index d4a844e3..c7b497e3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,10 +2,6 @@ # will have compiled files and executables /target/ -# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries -# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html -Cargo.lock - # These are backup files generated by rustfmt **/*.rs.bk diff --git a/.gitmodules b/.gitmodules index 88ae99fa..e69de29b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "PQClean"] - path = PQClean - url = https://github.com/PQClean/PQClean.git diff --git a/CHANGELOG.md b/CHANGELOG.md index c60752c7..6850ca97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.9.0 (TBD) + +* [BREAKING] Removed deprecated re-exports from liballoc/libstd (#290). +* [BREAKING] Refactored RpoFalcon512 signature to work with pure Rust (#285). +* Added `RngCore` as supertrait for `FeltRng` (#299). + # 0.8.4 (2024-03-17) * Re-added unintentionally removed re-exported liballoc macros (`vec` and `format` macros). diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 00000000..4b5b02cd --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1164 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstream" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "blake3" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30cca6d3674597c30ddf2c587bf8d9d65c9a84d2326d941cc79c9842dfe0ef52" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cc" +version = "1.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" +dependencies = [ + "jobserver", + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "4.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "949626d00e063efc93b6dca932419ceb5432f99769911c0b995f7e884c778813" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90239a040c80f5e14809ca132ddc4176ab33d5e17e49691793296e3fcb34d72f" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "constant_time_eq" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "is-terminal", + "itertools", + "num-traits", + "once_cell", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +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.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "either" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[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 = "getrandom" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "half" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5eceaaeec696539ddaf7b333340f1af35a5aa87ae3e4f3ead0532f72affab2e" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[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" + +[[package]] +name = "is-terminal" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys", +] + +[[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.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "jobserver" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[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.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "miden-crypto" +version = "0.9.0" +dependencies = [ + "blake3", + "cc", + "clap", + "criterion", + "getrandom", + "glob", + "hex", + "num", + "num-complex", + "proptest", + "rand", + "rand_chacha", + "rand_core", + "seq-macro", + "serde", + "sha3", + "winter-crypto", + "winter-math", + "winter-rand-utils", + "winter-utils", +] + +[[package]] +name = "num" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" +dependencies = [ + "num-traits", +] + +[[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.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + +[[package]] +name = "plotters" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" + +[[package]] +name = "plotters-svg" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf" +dependencies = [ + "bit-set", + "bit-vec", + "bitflags", + "lazy_static", + "num-traits", + "rand", + "rand_chacha", + "rand_xorshift", + "regex-syntax", + "rusty-fork", + "tempfile", + "unarray", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[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", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rayon" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4963ed1bc86e4f3ee217022bd855b297cef07fb9eac5dfa1f788b220b49b3bd" +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 = "regex" +version = "1.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "rustix" +version = "0.38.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "rusty-fork" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + +[[package]] +name = "ryu" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "seq-macro" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4" + +[[package]] +name = "serde" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "strsim" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" + +[[package]] +name = "syn" +version = "2.0.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7383cd0e49fff4b6b90ca5670bfd3e9d6a733b3f90c686605aa7eec8c4996032" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" + +[[package]] +name = "winter-crypto" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aea508aa819e934c837f24bb706e69d890b9be2db82da39cde887e6f0a37246" +dependencies = [ + "blake3", + "sha3", + "winter-math", + "winter-utils", +] + +[[package]] +name = "winter-math" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c91111b368b08c5a76009514e9b6d26af41fbb28604ea77a249282323b64d5" +dependencies = [ + "serde", + "winter-utils", +] + +[[package]] +name = "winter-rand-utils" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b19ce50e688442052e957a69d72b8057d72ae8f03a7aea7c2538e11c76b2583" +dependencies = [ + "rand", + "winter-utils", +] + +[[package]] +name = "winter-utils" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab6efccf6efa6fd0a80784f3894bc372ada67cc30d9c017fc907d4c0cdce86e7" diff --git a/Cargo.toml b/Cargo.toml index 42c3375f..efe235d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "miden-crypto" -version = "0.8.4" +version = "0.9.0" description = "Miden Cryptographic primitives" authors = ["miden contributors"] readme = "README.md" license = "MIT" repository = "https://github.com/0xPolygonMiden/crypto" -documentation = "https://docs.rs/miden-crypto/0.8.4" +documentation = "https://docs.rs/miden-crypto/0.9.0" categories = ["cryptography", "no-std"] keywords = ["miden", "crypto", "hash", "merkle"] edition = "2021" @@ -33,33 +33,41 @@ harness = false [features] default = ["std"] -executable = ["dep:clap", "dep:rand_utils", "std"] -serde = ["dep:serde", "serde?/alloc", "winter_math/serde"] +executable = ["dep:clap", "dep:rand-utils", "std"] +serde = ["dep:serde", "serde?/alloc", "winter-math/serde"] std = [ "blake3/std", "dep:cc", - "winter_crypto/std", - "winter_math/std", - "winter_utils/std", + "rand/std", + "rand/std_rng", + "winter-crypto/std", + "winter-math/std", + "winter-utils/std", ] [dependencies] blake3 = { version = "1.5", default-features = false } -clap = { version = "4.5", features = ["derive"], optional = true } -rand_utils = { version = "0.8", package = "winter-rand-utils", optional = true } -serde = { version = "1.0", features = [ - "derive", -], default-features = false, optional = true } -winter_crypto = { version = "0.8", package = "winter-crypto", default-features = false } -winter_math = { version = "0.8", package = "winter-math", default-features = false } -winter_utils = { version = "0.8", package = "winter-utils", default-features = false } +clap = { version = "4.5", optional = true, features = ["derive"] } +num = { version = "0.4", default-features = false, features = ["alloc", "libm"] } +num-complex = { version = "0.4.4", default-features = false } +rand = { version = "0.8", default-features = false } +rand_core = { version = "0.6", default-features = false } +rand-utils = { version = "0.8", package = "winter-rand-utils", optional = true } +serde = { version = "1.0", default-features = false, optional = true, features = ["derive"] } +sha3 = { version = "0.10", default-features = false } +winter-crypto = { version = "0.8", default-features = false } +winter-math = { version = "0.8", default-features = false } +winter-utils = { version = "0.8", default-features = false } [dev-dependencies] -seq-macro = { version = "0.3" } criterion = { version = "0.5", features = ["html_reports"] } +getrandom = { version = "0.2", features = ["js"] } +hex = { version = "0.4", default-features = false, features = ["alloc"] } proptest = "1.4" -rand_utils = { version = "0.8", package = "winter-rand-utils" } +rand_chacha = { version = "0.3", default-features = false } +rand-utils = { version = "0.8", package = "winter-rand-utils" } +seq-macro = { version = "0.3" } [build-dependencies] -cc = { version = "1.0", features = ["parallel"], optional = true } +cc = { version = "1.0", optional = true, features = ["parallel"] } glob = "0.3" diff --git a/Makefile.toml b/Makefile.toml new file mode 100644 index 00000000..f5c185cf --- /dev/null +++ b/Makefile.toml @@ -0,0 +1,86 @@ +# Cargo Makefile + +# -- linting -------------------------------------------------------------------------------------- +[tasks.format] +toolchain = "nightly" +command = "cargo" +args = ["fmt", "--all"] + +[tasks.format-check] +toolchain = "nightly" +command = "cargo" +args = ["fmt", "--all", "--", "--check"] + +[tasks.clippy-default] +command = "cargo" +args = ["clippy","--workspace", "--all-targets", "--", "-D", "clippy::all", "-D", "warnings"] + +[tasks.clippy-all-features] +command = "cargo" +args = ["clippy","--workspace", "--all-targets", "--all-features", "--", "-D", "clippy::all", "-D", "warnings"] + +[tasks.clippy] +dependencies = [ + "clippy-default", + "clippy-all-features" +] + +[tasks.fix] +description = "Runs Fix" +command = "cargo" +toolchain = "nightly" +args = ["fix", "--allow-staged", "--allow-dirty", "--all-targets", "--all-features"] + +[tasks.lint] +description = "Runs all linting tasks (Clippy, fixing, formatting)" +run_task = { name = ["format", "format-check", "clippy", "docs"] } + +# --- docs ---------------------------------------------------------------------------------------- +[tasks.doc] +env = { "RUSTDOCFLAGS" = "-D warnings" } +command = "cargo" +args = ["doc", "--all-features", "--keep-going", "--release"] + +# --- testing ------------------------------------------------------------------------------------- +[tasks.test] +description = "Run tests with default features" +env = { "RUSTFLAGS" = "-C debug-assertions -C overflow-checks -C debuginfo=2" } +workspace = false +command = "cargo" +args = ["test", "--release"] + +[tasks.test-no-default-features] +description = "Run tests with no-default-features" +env = { "RUSTFLAGS" = "-C debug-assertions -C overflow-checks -C debuginfo=2" } +workspace = false +command = "cargo" +args = ["test", "--release", "--no-default-features"] + +[tasks.test-all] +description = "Run all tests" +workspace = false +run_task = { name = ["test", "test-no-default-features"], parallel = true } + +# --- building ------------------------------------------------------------------------------------ +[tasks.build] +description = "Build in release mode" +command = "cargo" +args = ["build", "--release"] + +[tasks.build-no-std] +description = "Build using no-std" +command = "cargo" +args = ["build", "--release", "--no-default-features", "--target", "wasm32-unknown-unknown"] + +[tasks.build-avx2] +description = "Build using AVX2 acceleration" +env = { "RUSTFLAGS" = "-C target-feature=+avx2" } +command = "cargo" +args = ["build", "--release"] + +[tasks.build-sve] +description = "Build with SVE acceleration" +env = { "RUSTFLAGS" = "-C target-feature=+sve" } +command = "cargo" +args = ["build", "--release"] + diff --git a/PQClean b/PQClean deleted file mode 160000 index c3abebf4..00000000 --- a/PQClean +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c3abebf4ab1ff516ffa71e6337f06d898952c299 diff --git a/README.md b/README.md index 03be9019..d82af815 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,10 @@ # Miden Crypto - - - - + +[![LICENSE](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/0xPolygonMiden/crypto/blob/main/LICENSE) +[![test](https://github.com/0xPolygonMiden/crypto/actions/workflows/test.yml/badge.svg)](https://github.com/0xPolygonMiden/crypto/actions/workflows/test.yml) +[![no-std](https://github.com/0xPolygonMiden/crypto/actions/workflows/no-std.yml/badge.svg)](https://github.com/0xPolygonMiden/crypto/actions/workflows/no-std.yml) +[![RUST_VERSION](https://img.shields.io/badge/rustc-1.75+-lightgray.svg)]() +[![CRATE](https://img.shields.io/crates/v/miden-crypto)](https://crates.io/crates/miden-crypto) This crate contains cryptographic primitives used in Polygon Miden. @@ -33,7 +35,7 @@ The module also contains additional supporting components such as `NodeIndex`, ` * `RPO Falcon512`: a variant of the [Falcon](https://falcon-sign.info/) signature scheme. This variant differs from the standard in that instead of using SHAKE256 hash function in the *hash-to-point* algorithm we use RPO256. This makes the signature more efficient to verify in Miden VM. -For the above signatures, key generation and signing is available only in the `std` context (see [crate features](#crate-features) below), while signature verification is available in `no_std` context as well. +For the above signatures, key generation, signing, and signature verification are available for both `std` and `no_std` contexts (see [crate features](#crate-features) below). However, in `no_std` context, the user is responsible for supplying the key generation and signing procedures with a random number generator. ## Pseudo-Random Element Generator [Pseudo random element generator module](./src/rand/) provides a set of traits and data structures that facilitate generating pseudo-random elements in the context of Miden VM and Miden rollup. The module currently includes: @@ -54,26 +56,26 @@ To compile with `no_std`, disable default features via `--no-default-features` f ### AVX2 acceleration On platforms with [AVX2](https://en.wikipedia.org/wiki/Advanced_Vector_Extensions) support, RPO and RPX hash function can be accelerated by using the vector processing unit. To enable AVX2 acceleration, the code needs to be compiled with the `avx2` target feature enabled. For example: ```shell -RUSTFLAGS="-C target-feature=+avx2" cargo build --release +cargo make build-avx2 ``` ### SVE acceleration On platforms with [SVE](https://en.wikipedia.org/wiki/AArch64#Scalable_Vector_Extension_(SVE)) support, RPO and RPX hash function can be accelerated by using the vector processing unit. To enable SVE acceleration, the code needs to be compiled with the `sve` target feature enabled. For example: ```shell - RUSTFLAGS="-C target-feature=+sve" cargo build --release +cargo make build-sve ``` ## Testing -You can use cargo defaults to test the library: +The best way to test the library is using our `Makefile.toml` and [cargo-make](https://github.com/sagiegurari/cargo-make), this will enable you to use our pre-defined optimized testing commands: ```shell -cargo test +cargo make test-all ``` -However, some of the functions are heavy and might take a while for the tests to complete. In order to test in release mode, we have to replicate the test conditions of the development mode so all debug assertions can be verified. +For example, some of the functions are heavy and might take a while for the tests to complete if using simply `cargo test`. In order to test in release and optimized mode, we have to replicate the test conditions of the development mode so all debug assertions can be verified. -We do that by enabling some special [flags](https://doc.rust-lang.org/cargo/reference/profiles.html) for the compilation. +We do that by enabling some special [flags](https://doc.rust-lang.org/cargo/reference/profiles.html) for the compilation (which we have set as a default in our [Makefile.toml](Makefile.toml)): ```shell RUSTFLAGS="-C debug-assertions -C overflow-checks -C debuginfo=2" cargo test --release diff --git a/build.rs b/build.rs index 2e713593..f2e8a1c7 100644 --- a/build.rs +++ b/build.rs @@ -1,39 +1,8 @@ fn main() { - #[cfg(feature = "std")] - compile_rpo_falcon(); - #[cfg(target_feature = "sve")] compile_arch_arm64_sve(); } -#[cfg(feature = "std")] -fn compile_rpo_falcon() { - use std::path::PathBuf; - - const RPO_FALCON_PATH: &str = "src/dsa/rpo_falcon512/falcon_c"; - - println!("cargo:rerun-if-changed={RPO_FALCON_PATH}/falcon.h"); - println!("cargo:rerun-if-changed={RPO_FALCON_PATH}/falcon.c"); - println!("cargo:rerun-if-changed={RPO_FALCON_PATH}/rpo.h"); - println!("cargo:rerun-if-changed={RPO_FALCON_PATH}/rpo.c"); - - let target_dir: PathBuf = ["PQClean", "crypto_sign", "falcon-512", "clean"].iter().collect(); - let common_dir: PathBuf = ["PQClean", "common"].iter().collect(); - - let scheme_files = glob::glob(target_dir.join("*.c").to_str().unwrap()).unwrap(); - let common_files = glob::glob(common_dir.join("*.c").to_str().unwrap()).unwrap(); - - cc::Build::new() - .include(&common_dir) - .include(target_dir) - .files(scheme_files.into_iter().map(|p| p.unwrap().to_string_lossy().into_owned())) - .files(common_files.into_iter().map(|p| p.unwrap().to_string_lossy().into_owned())) - .file(format!("{RPO_FALCON_PATH}/falcon.c")) - .file(format!("{RPO_FALCON_PATH}/rpo.c")) - .flag("-O3") - .compile("rpo_falcon512"); -} - #[cfg(target_feature = "sve")] fn compile_arch_arm64_sve() { const RPO_SVE_PATH: &str = "arch/arm64-sve/rpo"; diff --git a/rust-toolchain b/rust-toolchain new file mode 100644 index 00000000..07cde984 --- /dev/null +++ b/rust-toolchain @@ -0,0 +1 @@ +1.75 diff --git a/scripts/check-rust-version.sh b/scripts/check-rust-version.sh new file mode 100755 index 00000000..f84634cc --- /dev/null +++ b/scripts/check-rust-version.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# Check rust-toolchain file +TOOLCHAIN_VERSION=$(cat rust-toolchain) + +# Check workspace Cargo.toml file +CARGO_VERSION=$(cat Cargo.toml | grep "rust-version" | cut -d '"' -f 2) +if [ "$CARGO_VERSION" != "$TOOLCHAIN_VERSION" ]; then + echo "Mismatch in Cargo.toml: Expected $TOOLCHAIN_VERSION, found $CARGO_VERSION" + exit 1 +fi + +echo "Rust versions match ✅" diff --git a/src/dsa/rpo_falcon512/error.rs b/src/dsa/rpo_falcon512/error.rs deleted file mode 100644 index 3f5e6e91..00000000 --- a/src/dsa/rpo_falcon512/error.rs +++ /dev/null @@ -1,56 +0,0 @@ -use core::fmt; - -use super::{LOG_N, MODULUS, PK_LEN}; - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum FalconError { - KeyGenerationFailed, - PubKeyDecodingExtraData, - PubKeyDecodingInvalidCoefficient(u32), - PubKeyDecodingInvalidLength(usize), - PubKeyDecodingInvalidTag(u8), - SigDecodingTooBigHighBits(u32), - SigDecodingInvalidRemainder, - SigDecodingNonZeroUnusedBitsLastByte, - SigDecodingMinusZero, - SigDecodingIncorrectEncodingAlgorithm, - SigDecodingNotSupportedDegree(u8), - SigGenerationFailed, -} - -impl fmt::Display for FalconError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use FalconError::*; - match self { - KeyGenerationFailed => write!(f, "Failed to generate a private-public key pair"), - PubKeyDecodingExtraData => { - write!(f, "Failed to decode public key: input not fully consumed") - } - PubKeyDecodingInvalidCoefficient(val) => { - write!(f, "Failed to decode public key: coefficient {val} is greater than or equal to the field modulus {MODULUS}") - } - PubKeyDecodingInvalidLength(len) => { - write!(f, "Failed to decode public key: expected {PK_LEN} bytes but received {len}") - } - PubKeyDecodingInvalidTag(byte) => { - write!(f, "Failed to decode public key: expected the first byte to be {LOG_N} but was {byte}") - } - SigDecodingTooBigHighBits(m) => { - write!(f, "Failed to decode signature: high bits {m} exceed 2048") - } - SigDecodingInvalidRemainder => { - write!(f, "Failed to decode signature: incorrect remaining data") - } - SigDecodingNonZeroUnusedBitsLastByte => { - write!(f, "Failed to decode signature: Non-zero unused bits in the last byte") - } - SigDecodingMinusZero => write!(f, "Failed to decode signature: -0 is forbidden"), - SigDecodingIncorrectEncodingAlgorithm => write!(f, "Failed to decode signature: not supported encoding algorithm"), - SigDecodingNotSupportedDegree(log_n) => write!(f, "Failed to decode signature: only supported irreducible polynomial degree is 512, 2^{log_n} was provided"), - SigGenerationFailed => write!(f, "Failed to generate a signature"), - } - } -} - -#[cfg(feature = "std")] -impl std::error::Error for FalconError {} diff --git a/src/dsa/rpo_falcon512/falcon_c/falcon.c b/src/dsa/rpo_falcon512/falcon_c/falcon.c deleted file mode 100644 index cd7bed56..00000000 --- a/src/dsa/rpo_falcon512/falcon_c/falcon.c +++ /dev/null @@ -1,402 +0,0 @@ -/* - * Wrapper for implementing the PQClean API. - */ - -#include -#include "randombytes.h" -#include "falcon.h" -#include "inner.h" -#include "rpo.h" - -#define NONCELEN 40 - -/* - * Encoding formats (nnnn = log of degree, 9 for Falcon-512, 10 for Falcon-1024) - * - * private key: - * header byte: 0101nnnn - * private f (6 or 5 bits by element, depending on degree) - * private g (6 or 5 bits by element, depending on degree) - * private F (8 bits by element) - * - * public key: - * header byte: 0000nnnn - * public h (14 bits by element) - * - * signature: - * header byte: 0011nnnn - * nonce 40 bytes - * value (12 bits by element) - * - * message + signature: - * signature length (2 bytes, big-endian) - * nonce 40 bytes - * message - * header byte: 0010nnnn - * value (12 bits by element) - * (signature length is 1+len(value), not counting the nonce) - */ - -/* see falcon.h */ -int PQCLEAN_FALCON512_CLEAN_crypto_sign_keypair_from_seed_rpo( - uint8_t *pk, - uint8_t *sk, - unsigned char *seed -) { - union - { - uint8_t b[FALCON_KEYGEN_TEMP_9]; - uint64_t dummy_u64; - fpr dummy_fpr; - } tmp; - int8_t f[512], g[512], F[512]; - uint16_t h[512]; - inner_shake256_context rng; - size_t u, v; - - /* - * Generate key pair. - */ - inner_shake256_init(&rng); - inner_shake256_inject(&rng, seed, sizeof seed); - inner_shake256_flip(&rng); - PQCLEAN_FALCON512_CLEAN_keygen(&rng, f, g, F, NULL, h, 9, tmp.b); - inner_shake256_ctx_release(&rng); - - /* - * Encode private key. - */ - sk[0] = 0x50 + 9; - u = 1; - v = PQCLEAN_FALCON512_CLEAN_trim_i8_encode( - sk + u, PQCLEAN_FALCON512_CLEAN_CRYPTO_SECRETKEYBYTES - u, - f, 9, PQCLEAN_FALCON512_CLEAN_max_fg_bits[9]); - if (v == 0) - { - return -1; - } - u += v; - v = PQCLEAN_FALCON512_CLEAN_trim_i8_encode( - sk + u, PQCLEAN_FALCON512_CLEAN_CRYPTO_SECRETKEYBYTES - u, - g, 9, PQCLEAN_FALCON512_CLEAN_max_fg_bits[9]); - if (v == 0) - { - return -1; - } - u += v; - v = PQCLEAN_FALCON512_CLEAN_trim_i8_encode( - sk + u, PQCLEAN_FALCON512_CLEAN_CRYPTO_SECRETKEYBYTES - u, - F, 9, PQCLEAN_FALCON512_CLEAN_max_FG_bits[9]); - if (v == 0) - { - return -1; - } - u += v; - if (u != PQCLEAN_FALCON512_CLEAN_CRYPTO_SECRETKEYBYTES) - { - return -1; - } - - /* - * Encode public key. - */ - pk[0] = 0x00 + 9; - v = PQCLEAN_FALCON512_CLEAN_modq_encode( - pk + 1, PQCLEAN_FALCON512_CLEAN_CRYPTO_PUBLICKEYBYTES - 1, - h, 9); - if (v != PQCLEAN_FALCON512_CLEAN_CRYPTO_PUBLICKEYBYTES - 1) - { - return -1; - } - - return 0; -} - -int PQCLEAN_FALCON512_CLEAN_crypto_sign_keypair_rpo( - uint8_t *pk, - uint8_t *sk -) { - unsigned char seed[48]; - - /* - * Generate a random seed. - */ - randombytes(seed, sizeof seed); - - return PQCLEAN_FALCON512_CLEAN_crypto_sign_keypair_from_seed_rpo(pk, sk, seed); -} - -/* - * Compute the signature. nonce[] receives the nonce and must have length - * NONCELEN bytes. sigbuf[] receives the signature value (without nonce - * or header byte), with *sigbuflen providing the maximum value length and - * receiving the actual value length. - * - * If a signature could be computed but not encoded because it would - * exceed the output buffer size, then a new signature is computed. If - * the provided buffer size is too low, this could loop indefinitely, so - * the caller must provide a size that can accommodate signatures with a - * large enough probability. - * - * Return value: 0 on success, -1 on error. - */ -static int do_sign( - uint8_t *nonce, - uint8_t *sigbuf, - size_t *sigbuflen, - const uint8_t *m, - size_t mlen, - const uint8_t *sk -) { - union - { - uint8_t b[72 * 512]; - uint64_t dummy_u64; - fpr dummy_fpr; - } tmp; - int8_t f[512], g[512], F[512], G[512]; - struct - { - int16_t sig[512]; - uint16_t hm[512]; - } r; - unsigned char seed[48]; - inner_shake256_context sc; - rpo128_context rc; - size_t u, v; - - /* - * Decode the private key. - */ - if (sk[0] != 0x50 + 9) - { - return -1; - } - u = 1; - v = PQCLEAN_FALCON512_CLEAN_trim_i8_decode( - f, 9, PQCLEAN_FALCON512_CLEAN_max_fg_bits[9], - sk + u, PQCLEAN_FALCON512_CLEAN_CRYPTO_SECRETKEYBYTES - u); - if (v == 0) - { - return -1; - } - u += v; - v = PQCLEAN_FALCON512_CLEAN_trim_i8_decode( - g, 9, PQCLEAN_FALCON512_CLEAN_max_fg_bits[9], - sk + u, PQCLEAN_FALCON512_CLEAN_CRYPTO_SECRETKEYBYTES - u); - if (v == 0) - { - return -1; - } - u += v; - v = PQCLEAN_FALCON512_CLEAN_trim_i8_decode( - F, 9, PQCLEAN_FALCON512_CLEAN_max_FG_bits[9], - sk + u, PQCLEAN_FALCON512_CLEAN_CRYPTO_SECRETKEYBYTES - u); - if (v == 0) - { - return -1; - } - u += v; - if (u != PQCLEAN_FALCON512_CLEAN_CRYPTO_SECRETKEYBYTES) - { - return -1; - } - if (!PQCLEAN_FALCON512_CLEAN_complete_private(G, f, g, F, 9, tmp.b)) - { - return -1; - } - - /* - * Create a random nonce (40 bytes). - */ - randombytes(nonce, NONCELEN); - - /* ==== Start: Deviation from the reference implementation ================================= */ - - // Transform the nonce into 8 chunks each of size 5 bytes. We do this in order to be sure that - // the conversion to field elements succeeds - uint8_t buffer[64]; - memset(buffer, 0, 64); - for (size_t i = 0; i < 8; i++) - { - buffer[8 * i] = nonce[5 * i]; - buffer[8 * i + 1] = nonce[5 * i + 1]; - buffer[8 * i + 2] = nonce[5 * i + 2]; - buffer[8 * i + 3] = nonce[5 * i + 3]; - buffer[8 * i + 4] = nonce[5 * i + 4]; - } - - /* - * Hash message nonce + message into a vector. - */ - rpo128_init(&rc); - rpo128_absorb(&rc, buffer, NONCELEN + 24); - rpo128_absorb(&rc, m, mlen); - rpo128_finalize(&rc); - PQCLEAN_FALCON512_CLEAN_hash_to_point_rpo(&rc, r.hm, 9); - rpo128_release(&rc); - - /* ==== End: Deviation from the reference implementation =================================== */ - - /* - * Initialize a RNG. - */ - randombytes(seed, sizeof seed); - inner_shake256_init(&sc); - inner_shake256_inject(&sc, seed, sizeof seed); - inner_shake256_flip(&sc); - - /* - * Compute and return the signature. This loops until a signature - * value is found that fits in the provided buffer. - */ - for (;;) - { - PQCLEAN_FALCON512_CLEAN_sign_dyn(r.sig, &sc, f, g, F, G, r.hm, 9, tmp.b); - v = PQCLEAN_FALCON512_CLEAN_comp_encode(sigbuf, *sigbuflen, r.sig, 9); - if (v != 0) - { - inner_shake256_ctx_release(&sc); - *sigbuflen = v; - return 0; - } - } -} - -/* - * Verify a signature. The nonce has size NONCELEN bytes. sigbuf[] - * (of size sigbuflen) contains the signature value, not including the - * header byte or nonce. Return value is 0 on success, -1 on error. - */ -static int do_verify( - const uint8_t *nonce, - const uint8_t *sigbuf, - size_t sigbuflen, - const uint8_t *m, - size_t mlen, - const uint8_t *pk -) { - union - { - uint8_t b[2 * 512]; - uint64_t dummy_u64; - fpr dummy_fpr; - } tmp; - uint16_t h[512], hm[512]; - int16_t sig[512]; - rpo128_context rc; - - /* - * Decode public key. - */ - if (pk[0] != 0x00 + 9) - { - return -1; - } - if (PQCLEAN_FALCON512_CLEAN_modq_decode(h, 9, - pk + 1, PQCLEAN_FALCON512_CLEAN_CRYPTO_PUBLICKEYBYTES - 1) - != PQCLEAN_FALCON512_CLEAN_CRYPTO_PUBLICKEYBYTES - 1) - { - return -1; - } - PQCLEAN_FALCON512_CLEAN_to_ntt_monty(h, 9); - - /* - * Decode signature. - */ - if (sigbuflen == 0) - { - return -1; - } - if (PQCLEAN_FALCON512_CLEAN_comp_decode(sig, 9, sigbuf, sigbuflen) != sigbuflen) - { - return -1; - } - - /* ==== Start: Deviation from the reference implementation ================================= */ - - /* - * Hash nonce + message into a vector. - */ - - // Transform the nonce into 8 chunks each of size 5 bytes. We do this in order to be sure that - // the conversion to field elements succeeds - uint8_t buffer[64]; - memset(buffer, 0, 64); - for (size_t i = 0; i < 8; i++) - { - buffer[8 * i] = nonce[5 * i]; - buffer[8 * i + 1] = nonce[5 * i + 1]; - buffer[8 * i + 2] = nonce[5 * i + 2]; - buffer[8 * i + 3] = nonce[5 * i + 3]; - buffer[8 * i + 4] = nonce[5 * i + 4]; - } - - rpo128_init(&rc); - rpo128_absorb(&rc, buffer, NONCELEN + 24); - rpo128_absorb(&rc, m, mlen); - rpo128_finalize(&rc); - PQCLEAN_FALCON512_CLEAN_hash_to_point_rpo(&rc, hm, 9); - rpo128_release(&rc); - - /* === End: Deviation from the reference implementation ==================================== */ - - /* - * Verify signature. - */ - if (!PQCLEAN_FALCON512_CLEAN_verify_raw(hm, sig, h, 9, tmp.b)) - { - return -1; - } - return 0; -} - -/* see falcon.h */ -int PQCLEAN_FALCON512_CLEAN_crypto_sign_signature_rpo( - uint8_t *sig, - size_t *siglen, - const uint8_t *m, - size_t mlen, - const uint8_t *sk -) { - /* - * The PQCLEAN_FALCON512_CLEAN_CRYPTO_BYTES constant is used for - * the signed message object (as produced by crypto_sign()) - * and includes a two-byte length value, so we take care here - * to only generate signatures that are two bytes shorter than - * the maximum. This is done to ensure that crypto_sign() - * and crypto_sign_signature() produce the exact same signature - * value, if used on the same message, with the same private key, - * and using the same output from randombytes() (this is for - * reproducibility of tests). - */ - size_t vlen; - - vlen = PQCLEAN_FALCON512_CLEAN_CRYPTO_BYTES - NONCELEN - 3; - if (do_sign(sig + 1, sig + 1 + NONCELEN, &vlen, m, mlen, sk) < 0) - { - return -1; - } - sig[0] = 0x30 + 9; - *siglen = 1 + NONCELEN + vlen; - return 0; -} - -/* see falcon.h */ -int PQCLEAN_FALCON512_CLEAN_crypto_sign_verify_rpo( - const uint8_t *sig, - size_t siglen, - const uint8_t *m, - size_t mlen, - const uint8_t *pk -) { - if (siglen < 1 + NONCELEN) - { - return -1; - } - if (sig[0] != 0x30 + 9) - { - return -1; - } - return do_verify(sig + 1, sig + 1 + NONCELEN, siglen - 1 - NONCELEN, m, mlen, pk); -} diff --git a/src/dsa/rpo_falcon512/falcon_c/falcon.h b/src/dsa/rpo_falcon512/falcon_c/falcon.h deleted file mode 100644 index bdcc3ec7..00000000 --- a/src/dsa/rpo_falcon512/falcon_c/falcon.h +++ /dev/null @@ -1,66 +0,0 @@ -#include -#include - -#define PQCLEAN_FALCON512_CLEAN_CRYPTO_SECRETKEYBYTES 1281 -#define PQCLEAN_FALCON512_CLEAN_CRYPTO_PUBLICKEYBYTES 897 -#define PQCLEAN_FALCON512_CLEAN_CRYPTO_BYTES 666 - -/* - * Generate a new key pair. Public key goes into pk[], private key in sk[]. - * Key sizes are exact (in bytes): - * public (pk): PQCLEAN_FALCON512_CLEAN_CRYPTO_PUBLICKEYBYTES - * private (sk): PQCLEAN_FALCON512_CLEAN_CRYPTO_SECRETKEYBYTES - * - * Return value: 0 on success, -1 on error. - * - * Note: This implementation follows the reference implementation in PQClean - * https://github.com/PQClean/PQClean/tree/master/crypto_sign/falcon-512 - * verbatim except for the sections that are marked otherwise. - */ -int PQCLEAN_FALCON512_CLEAN_crypto_sign_keypair_rpo( - uint8_t *pk, uint8_t *sk); - -/* - * Generate a new key pair from seed. Public key goes into pk[], private key in sk[]. - * Key sizes are exact (in bytes): - * public (pk): PQCLEAN_FALCON512_CLEAN_CRYPTO_PUBLICKEYBYTES - * private (sk): PQCLEAN_FALCON512_CLEAN_CRYPTO_SECRETKEYBYTES - * - * Return value: 0 on success, -1 on error. - */ -int PQCLEAN_FALCON512_CLEAN_crypto_sign_keypair_from_seed_rpo( - uint8_t *pk, uint8_t *sk, unsigned char *seed); - -/* - * Compute a signature on a provided message (m, mlen), with a given - * private key (sk). Signature is written in sig[], with length written - * into *siglen. Signature length is variable; maximum signature length - * (in bytes) is PQCLEAN_FALCON512_CLEAN_CRYPTO_BYTES. - * - * sig[], m[] and sk[] may overlap each other arbitrarily. - * - * Return value: 0 on success, -1 on error. - * - * Note: This implementation follows the reference implementation in PQClean - * https://github.com/PQClean/PQClean/tree/master/crypto_sign/falcon-512 - * verbatim except for the sections that are marked otherwise. - */ -int PQCLEAN_FALCON512_CLEAN_crypto_sign_signature_rpo( - uint8_t *sig, size_t *siglen, - const uint8_t *m, size_t mlen, const uint8_t *sk); - -/* - * Verify a signature (sig, siglen) on a message (m, mlen) with a given - * public key (pk). - * - * sig[], m[] and pk[] may overlap each other arbitrarily. - * - * Return value: 0 on success, -1 on error. - * - * Note: This implementation follows the reference implementation in PQClean - * https://github.com/PQClean/PQClean/tree/master/crypto_sign/falcon-512 - * verbatim except for the sections that are marked otherwise. - */ -int PQCLEAN_FALCON512_CLEAN_crypto_sign_verify_rpo( - const uint8_t *sig, size_t siglen, - const uint8_t *m, size_t mlen, const uint8_t *pk); diff --git a/src/dsa/rpo_falcon512/falcon_c/rpo.c b/src/dsa/rpo_falcon512/falcon_c/rpo.c deleted file mode 100644 index 4879c128..00000000 --- a/src/dsa/rpo_falcon512/falcon_c/rpo.c +++ /dev/null @@ -1,582 +0,0 @@ -/* - * RPO implementation. - */ - -#include -#include -#include - -/* ================================================================================================ - * Modular Arithmetic - */ - -#define P 0xFFFFFFFF00000001 -#define M 12289 - -// From https://github.com/ncw/iprime/blob/master/mod_math_noasm.go -static uint64_t add_mod_p(uint64_t a, uint64_t b) -{ - a = P - a; - uint64_t res = b - a; - if (b < a) - res += P; - return res; -} - -static uint64_t sub_mod_p(uint64_t a, uint64_t b) -{ - uint64_t r = a - b; - if (a < b) - r += P; - return r; -} - -static uint64_t reduce_mod_p(uint64_t b, uint64_t a) -{ - uint32_t d = b >> 32, - c = b; - if (a >= P) - a -= P; - a = sub_mod_p(a, c); - a = sub_mod_p(a, d); - a = add_mod_p(a, ((uint64_t)c) << 32); - return a; -} - -static uint64_t mult_mod_p(uint64_t x, uint64_t y) -{ - uint32_t a = x, - b = x >> 32, - c = y, - d = y >> 32; - - /* first synthesize the product using 32*32 -> 64 bit multiplies */ - x = b * (uint64_t)c; /* b*c */ - y = a * (uint64_t)d; /* a*d */ - uint64_t e = a * (uint64_t)c, /* a*c */ - f = b * (uint64_t)d, /* b*d */ - t; - - x += y; /* b*c + a*d */ - /* carry? */ - if (x < y) - f += 1LL << 32; /* carry into upper 32 bits - can't overflow */ - - t = x << 32; - e += t; /* a*c + LSW(b*c + a*d) */ - /* carry? */ - if (e < t) - f += 1; /* carry into upper 64 bits - can't overflow*/ - t = x >> 32; - f += t; /* b*d + MSW(b*c + a*d) */ - /* can't overflow */ - - /* now reduce: (b*d + MSW(b*c + a*d), a*c + LSW(b*c + a*d)) */ - return reduce_mod_p(f, e); -} - -/* ================================================================================================ - * RPO128 Permutation - */ - -#define STATE_WIDTH 12 -#define NUM_ROUNDS 7 - -/* - * MDS matrix - */ -static const uint64_t MDS[12][12] = { - { 7, 23, 8, 26, 13, 10, 9, 7, 6, 22, 21, 8 }, - { 8, 7, 23, 8, 26, 13, 10, 9, 7, 6, 22, 21 }, - { 21, 8, 7, 23, 8, 26, 13, 10, 9, 7, 6, 22 }, - { 22, 21, 8, 7, 23, 8, 26, 13, 10, 9, 7, 6 }, - { 6, 22, 21, 8, 7, 23, 8, 26, 13, 10, 9, 7 }, - { 7, 6, 22, 21, 8, 7, 23, 8, 26, 13, 10, 9 }, - { 9, 7, 6, 22, 21, 8, 7, 23, 8, 26, 13, 10 }, - { 10, 9, 7, 6, 22, 21, 8, 7, 23, 8, 26, 13 }, - { 13, 10, 9, 7, 6, 22, 21, 8, 7, 23, 8, 26 }, - { 26, 13, 10, 9, 7, 6, 22, 21, 8, 7, 23, 8 }, - { 8, 26, 13, 10, 9, 7, 6, 22, 21, 8, 7, 23 }, - { 23, 8, 26, 13, 10, 9, 7, 6, 22, 21, 8, 7 }, -}; - -/* - * Round constants. - */ -static const uint64_t ARK1[7][12] = { - { - 5789762306288267392ULL, - 6522564764413701783ULL, - 17809893479458208203ULL, - 107145243989736508ULL, - 6388978042437517382ULL, - 15844067734406016715ULL, - 9975000513555218239ULL, - 3344984123768313364ULL, - 9959189626657347191ULL, - 12960773468763563665ULL, - 9602914297752488475ULL, - 16657542370200465908ULL, - }, - { - 12987190162843096997ULL, - 653957632802705281ULL, - 4441654670647621225ULL, - 4038207883745915761ULL, - 5613464648874830118ULL, - 13222989726778338773ULL, - 3037761201230264149ULL, - 16683759727265180203ULL, - 8337364536491240715ULL, - 3227397518293416448ULL, - 8110510111539674682ULL, - 2872078294163232137ULL, - }, - { - 18072785500942327487ULL, - 6200974112677013481ULL, - 17682092219085884187ULL, - 10599526828986756440ULL, - 975003873302957338ULL, - 8264241093196931281ULL, - 10065763900435475170ULL, - 2181131744534710197ULL, - 6317303992309418647ULL, - 1401440938888741532ULL, - 8884468225181997494ULL, - 13066900325715521532ULL, - }, - { - 5674685213610121970ULL, - 5759084860419474071ULL, - 13943282657648897737ULL, - 1352748651966375394ULL, - 17110913224029905221ULL, - 1003883795902368422ULL, - 4141870621881018291ULL, - 8121410972417424656ULL, - 14300518605864919529ULL, - 13712227150607670181ULL, - 17021852944633065291ULL, - 6252096473787587650ULL, - }, - { - 4887609836208846458ULL, - 3027115137917284492ULL, - 9595098600469470675ULL, - 10528569829048484079ULL, - 7864689113198939815ULL, - 17533723827845969040ULL, - 5781638039037710951ULL, - 17024078752430719006ULL, - 109659393484013511ULL, - 7158933660534805869ULL, - 2955076958026921730ULL, - 7433723648458773977ULL, - }, - { - 16308865189192447297ULL, - 11977192855656444890ULL, - 12532242556065780287ULL, - 14594890931430968898ULL, - 7291784239689209784ULL, - 5514718540551361949ULL, - 10025733853830934803ULL, - 7293794580341021693ULL, - 6728552937464861756ULL, - 6332385040983343262ULL, - 13277683694236792804ULL, - 2600778905124452676ULL, - }, - { - 7123075680859040534ULL, - 1034205548717903090ULL, - 7717824418247931797ULL, - 3019070937878604058ULL, - 11403792746066867460ULL, - 10280580802233112374ULL, - 337153209462421218ULL, - 13333398568519923717ULL, - 3596153696935337464ULL, - 8104208463525993784ULL, - 14345062289456085693ULL, - 17036731477169661256ULL, - }}; - -const uint64_t ARK2[7][12] = { - { - 6077062762357204287ULL, - 15277620170502011191ULL, - 5358738125714196705ULL, - 14233283787297595718ULL, - 13792579614346651365ULL, - 11614812331536767105ULL, - 14871063686742261166ULL, - 10148237148793043499ULL, - 4457428952329675767ULL, - 15590786458219172475ULL, - 10063319113072092615ULL, - 14200078843431360086ULL, - }, - { - 6202948458916099932ULL, - 17690140365333231091ULL, - 3595001575307484651ULL, - 373995945117666487ULL, - 1235734395091296013ULL, - 14172757457833931602ULL, - 707573103686350224ULL, - 15453217512188187135ULL, - 219777875004506018ULL, - 17876696346199469008ULL, - 17731621626449383378ULL, - 2897136237748376248ULL, - }, - { - 8023374565629191455ULL, - 15013690343205953430ULL, - 4485500052507912973ULL, - 12489737547229155153ULL, - 9500452585969030576ULL, - 2054001340201038870ULL, - 12420704059284934186ULL, - 355990932618543755ULL, - 9071225051243523860ULL, - 12766199826003448536ULL, - 9045979173463556963ULL, - 12934431667190679898ULL, - }, - { - 18389244934624494276ULL, - 16731736864863925227ULL, - 4440209734760478192ULL, - 17208448209698888938ULL, - 8739495587021565984ULL, - 17000774922218161967ULL, - 13533282547195532087ULL, - 525402848358706231ULL, - 16987541523062161972ULL, - 5466806524462797102ULL, - 14512769585918244983ULL, - 10973956031244051118ULL, - }, - { - 6982293561042362913ULL, - 14065426295947720331ULL, - 16451845770444974180ULL, - 7139138592091306727ULL, - 9012006439959783127ULL, - 14619614108529063361ULL, - 1394813199588124371ULL, - 4635111139507788575ULL, - 16217473952264203365ULL, - 10782018226466330683ULL, - 6844229992533662050ULL, - 7446486531695178711ULL, - }, - { - 3736792340494631448ULL, - 577852220195055341ULL, - 6689998335515779805ULL, - 13886063479078013492ULL, - 14358505101923202168ULL, - 7744142531772274164ULL, - 16135070735728404443ULL, - 12290902521256031137ULL, - 12059913662657709804ULL, - 16456018495793751911ULL, - 4571485474751953524ULL, - 17200392109565783176ULL, - }, - { - 17130398059294018733ULL, - 519782857322261988ULL, - 9625384390925085478ULL, - 1664893052631119222ULL, - 7629576092524553570ULL, - 3485239601103661425ULL, - 9755891797164033838ULL, - 15218148195153269027ULL, - 16460604813734957368ULL, - 9643968136937729763ULL, - 3611348709641382851ULL, - 18256379591337759196ULL, - }, -}; - -static void apply_sbox(uint64_t *const state) -{ - for (uint64_t i = 0; i < STATE_WIDTH; i++) - { - uint64_t t2 = mult_mod_p(*(state + i), *(state + i)); - uint64_t t4 = mult_mod_p(t2, t2); - - *(state + i) = mult_mod_p(*(state + i), mult_mod_p(t2, t4)); - } -} - -static void apply_mds(uint64_t *state) -{ - uint64_t res[STATE_WIDTH]; - for (uint64_t i = 0; i < STATE_WIDTH; i++) - { - res[i] = 0; - } - for (uint64_t i = 0; i < STATE_WIDTH; i++) - { - for (uint64_t j = 0; j < STATE_WIDTH; j++) - { - res[i] = add_mod_p(res[i], mult_mod_p(MDS[i][j], *(state + j))); - } - } - - for (uint64_t i = 0; i < STATE_WIDTH; i++) - { - *(state + i) = res[i]; - } -} - -static void apply_constants(uint64_t *const state, const uint64_t *ark) -{ - for (uint64_t i = 0; i < STATE_WIDTH; i++) - { - *(state + i) = add_mod_p(*(state + i), *(ark + i)); - } -} - -static void exp_acc(const uint64_t m, const uint64_t *base, const uint64_t *tail, uint64_t *const res) -{ - for (uint64_t i = 0; i < m; i++) - { - for (uint64_t j = 0; j < STATE_WIDTH; j++) - { - if (i == 0) - { - *(res + j) = mult_mod_p(*(base + j), *(base + j)); - } - else - { - *(res + j) = mult_mod_p(*(res + j), *(res + j)); - } - } - } - - for (uint64_t i = 0; i < STATE_WIDTH; i++) - { - *(res + i) = mult_mod_p(*(res + i), *(tail + i)); - } -} - -static void apply_inv_sbox(uint64_t *const state) -{ - uint64_t t1[STATE_WIDTH]; - for (uint64_t i = 0; i < STATE_WIDTH; i++) - { - t1[i] = 0; - } - for (uint64_t i = 0; i < STATE_WIDTH; i++) - { - t1[i] = mult_mod_p(*(state + i), *(state + i)); - } - - uint64_t t2[STATE_WIDTH]; - for (uint64_t i = 0; i < STATE_WIDTH; i++) - { - t2[i] = 0; - } - for (uint64_t i = 0; i < STATE_WIDTH; i++) - { - t2[i] = mult_mod_p(t1[i], t1[i]); - } - - uint64_t t3[STATE_WIDTH]; - for (uint64_t i = 0; i < STATE_WIDTH; i++) - { - t3[i] = 0; - } - exp_acc(3, t2, t2, t3); - - uint64_t t4[STATE_WIDTH]; - for (uint64_t i = 0; i < STATE_WIDTH; i++) - { - t4[i] = 0; - } - exp_acc(6, t3, t3, t4); - - uint64_t tmp[STATE_WIDTH]; - for (uint64_t i = 0; i < STATE_WIDTH; i++) - { - tmp[i] = 0; - } - exp_acc(12, t4, t4, tmp); - - uint64_t t5[STATE_WIDTH]; - for (uint64_t i = 0; i < STATE_WIDTH; i++) - { - t5[i] = 0; - } - exp_acc(6, tmp, t3, t5); - - uint64_t t6[STATE_WIDTH]; - for (uint64_t i = 0; i < STATE_WIDTH; i++) - { - t6[i] = 0; - } - exp_acc(31, t5, t5, t6); - - for (uint64_t i = 0; i < STATE_WIDTH; i++) - { - uint64_t a = mult_mod_p(mult_mod_p(t6[i], t6[i]), t5[i]); - a = mult_mod_p(a, a); - a = mult_mod_p(a, a); - uint64_t b = mult_mod_p(mult_mod_p(t1[i], t2[i]), *(state + i)); - - *(state + i) = mult_mod_p(a, b); - } -} - -static void apply_round(uint64_t *const state, const uint64_t round) -{ - apply_mds(state); - apply_constants(state, ARK1[round]); - apply_sbox(state); - - apply_mds(state); - apply_constants(state, ARK2[round]); - apply_inv_sbox(state); -} - -static void apply_permutation(uint64_t *state) -{ - for (uint64_t i = 0; i < NUM_ROUNDS; i++) - { - apply_round(state, i); - } -} - -/* ================================================================================================ - * RPO128 implementation. This is supposed to substitute SHAKE256 in the hash-to-point algorithm. - */ - -#include "rpo.h" - -void rpo128_init(rpo128_context *rc) -{ - rc->dptr = 32; - - memset(rc->st.A, 0, sizeof rc->st.A); -} - -void rpo128_absorb(rpo128_context *rc, const uint8_t *in, size_t len) -{ - size_t dptr; - - dptr = (size_t)rc->dptr; - while (len > 0) - { - size_t clen, u; - - /* 136 * 8 = 1088 bit for the rate portion in the case of SHAKE256 - * For RPO, this is 64 * 8 = 512 bits - * The capacity for SHAKE256 is at the end while for RPO128 it is at the beginning - */ - clen = 96 - dptr; - if (clen > len) - { - clen = len; - } - - for (u = 0; u < clen; u++) - { - rc->st.dbuf[dptr + u] = in[u]; - } - - dptr += clen; - in += clen; - len -= clen; - if (dptr == 96) - { - apply_permutation(rc->st.A); - dptr = 32; - } - } - rc->dptr = dptr; -} - -void rpo128_finalize(rpo128_context *rc) -{ - // Set dptr to the end of the buffer, so that first call to extract will call the permutation. - rc->dptr = 96; -} - -void rpo128_squeeze(rpo128_context *rc, uint8_t *out, size_t len) -{ - size_t dptr; - - dptr = (size_t)rc->dptr; - while (len > 0) - { - size_t clen; - - if (dptr == 96) - { - apply_permutation(rc->st.A); - dptr = 32; - } - clen = 96 - dptr; - if (clen > len) - { - clen = len; - } - len -= clen; - - memcpy(out, rc->st.dbuf + dptr, clen); - dptr += clen; - out += clen; - } - rc->dptr = dptr; -} - -void rpo128_release(rpo128_context *rc) -{ - memset(rc->st.A, 0, sizeof rc->st.A); - rc->dptr = 32; -} - -/* ================================================================================================ - * Hash-to-Point algorithm implementation based on RPO128 - */ - -void PQCLEAN_FALCON512_CLEAN_hash_to_point_rpo(rpo128_context *rc, uint16_t *x, unsigned logn) -{ - /* - * This implementation avoids the rejection sampling step needed in the - * per-the-spec implementation. It uses a remark in https://falcon-sign.info/falcon.pdf - * page 31, which argues that the current variant is secure for the parameters set by NIST. - * Avoiding the rejection-sampling step leads to an implementation that is constant-time. - * TODO: Check that the current implementation is indeed constant-time. - */ - size_t n; - - n = (size_t)1 << logn; - while (n > 0) - { - uint8_t buf[8]; - uint64_t w; - - rpo128_squeeze(rc, (void *)buf, sizeof buf); - w = ((uint64_t)(buf[7]) << 56) | - ((uint64_t)(buf[6]) << 48) | - ((uint64_t)(buf[5]) << 40) | - ((uint64_t)(buf[4]) << 32) | - ((uint64_t)(buf[3]) << 24) | - ((uint64_t)(buf[2]) << 16) | - ((uint64_t)(buf[1]) << 8) | - ((uint64_t)(buf[0])); - - w %= M; - - *x++ = (uint16_t)w; - n--; - } -} diff --git a/src/dsa/rpo_falcon512/falcon_c/rpo.h b/src/dsa/rpo_falcon512/falcon_c/rpo.h deleted file mode 100644 index d9038af8..00000000 --- a/src/dsa/rpo_falcon512/falcon_c/rpo.h +++ /dev/null @@ -1,83 +0,0 @@ -#include -#include - -/* ================================================================================================ - * RPO hashing algorithm related structs and methods. - */ - -/* - * RPO128 context. - * - * This structure is used by the hashing API. It is composed of an internal state that can be - * viewed as either: - * 1. 12 field elements in the Miden VM. - * 2. 96 bytes. - * - * The first view is used for the internal state in the context of the RPO hashing algorithm. The - * second view is used for the buffer used to absorb the data to be hashed. - * - * The pointer to the buffer is updated as the data is absorbed. - * - * 'rpo128_context' must be initialized with rpo128_init() before first use. - */ -typedef struct -{ - union - { - uint64_t A[12]; - uint8_t dbuf[96]; - } st; - uint64_t dptr; -} rpo128_context; - -/* - * Initializes an RPO state - */ -void rpo128_init(rpo128_context *rc); - -/* - * Absorbs an array of bytes of length 'len' into the state. - */ -void rpo128_absorb(rpo128_context *rc, const uint8_t *in, size_t len); - -/* - * Squeezes an array of bytes of length 'len' from the state. - */ -void rpo128_squeeze(rpo128_context *rc, uint8_t *out, size_t len); - -/* - * Finalizes the state in preparation for squeezing. - * - * This function should be called after all the data has been absorbed. - * - * Note that the current implementation does not perform any sort of padding for domain separation - * purposes. The reason being that, for our purposes, we always perform the following sequence: - * 1. Absorb a Nonce (which is always 40 bytes packed as 8 field elements). - * 2. Absorb the message (which is always 4 field elements). - * 3. Call finalize. - * 4. Squeeze the output. - * 5. Call release. - */ -void rpo128_finalize(rpo128_context *rc); - -/* - * Releases the state. - * - * This function should be called after the squeeze operation is finished. - */ -void rpo128_release(rpo128_context *rc); - -/* ================================================================================================ - * Hash-to-Point algorithm for signature generation and signature verification. - */ - -/* - * Hash-to-Point algorithm. - * - * This function generates a point in Z_q[x]/(phi) from a given message. - * - * It takes a finalized rpo128_context as input and it generates the coefficients of the polynomial - * representing the point. The coefficients are stored in the array 'x'. The number of coefficients - * is given by 'logn', which must in our case is 512. - */ -void PQCLEAN_FALCON512_CLEAN_hash_to_point_rpo(rpo128_context *rc, uint16_t *x, unsigned logn); diff --git a/src/dsa/rpo_falcon512/ffi.rs b/src/dsa/rpo_falcon512/ffi.rs deleted file mode 100644 index de3f2bec..00000000 --- a/src/dsa/rpo_falcon512/ffi.rs +++ /dev/null @@ -1,194 +0,0 @@ -use core::ffi::c_int; - -// C IMPLEMENTATION INTERFACE -// ================================================================================================ - -#[link(name = "rpo_falcon512", kind = "static")] -extern "C" { - /// Generate a new key pair. Public key goes into pk[], private key in sk[]. - /// Key sizes are exact (in bytes): - /// - public (pk): 897 - /// - private (sk): 1281 - /// - /// Return value: 0 on success, -1 on error. - pub fn PQCLEAN_FALCON512_CLEAN_crypto_sign_keypair_rpo(pk: *mut u8, sk: *mut u8) -> c_int; - - /// Generate a new key pair from seed. Public key goes into pk[], private key in sk[]. - /// Key sizes are exact (in bytes): - /// - public (pk): 897 - /// - private (sk): 1281 - /// - /// Return value: 0 on success, -1 on error. - pub fn PQCLEAN_FALCON512_CLEAN_crypto_sign_keypair_from_seed_rpo( - pk: *mut u8, - sk: *mut u8, - seed: *const u8, - ) -> c_int; - - /// Compute a signature on a provided message (m, mlen), with a given private key (sk). - /// Signature is written in sig[], with length written into *siglen. Signature length is - /// variable; maximum signature length (in bytes) is 666. - /// - /// sig[], m[] and sk[] may overlap each other arbitrarily. - /// - /// Return value: 0 on success, -1 on error. - pub fn PQCLEAN_FALCON512_CLEAN_crypto_sign_signature_rpo( - sig: *mut u8, - siglen: *mut usize, - m: *const u8, - mlen: usize, - sk: *const u8, - ) -> c_int; - - // TEST HELPERS - // -------------------------------------------------------------------------------------------- - - /// Verify a signature (sig, siglen) on a message (m, mlen) with a given public key (pk). - /// - /// sig[], m[] and pk[] may overlap each other arbitrarily. - /// - /// Return value: 0 on success, -1 on error. - #[cfg(test)] - pub fn PQCLEAN_FALCON512_CLEAN_crypto_sign_verify_rpo( - sig: *const u8, - siglen: usize, - m: *const u8, - mlen: usize, - pk: *const u8, - ) -> c_int; - - /// Hash-to-Point algorithm. - /// - /// This function generates a point in Z_q[x]/(phi) from a given message. - /// - /// It takes a finalized rpo128_context as input and it generates the coefficients of the polynomial - /// representing the point. The coefficients are stored in the array 'x'. The number of coefficients - /// is given by 'logn', which must in our case is 512. - #[cfg(test)] - pub fn PQCLEAN_FALCON512_CLEAN_hash_to_point_rpo( - rc: *mut Rpo128Context, - x: *mut u16, - logn: usize, - ); - - #[cfg(test)] - pub fn rpo128_init(sc: *mut Rpo128Context); - - #[cfg(test)] - pub fn rpo128_absorb( - sc: *mut Rpo128Context, - data: *const core::ffi::c_void, - // TODO: When #![feature(c_size_t)] stabilizes, switch this to `core::ffi::size_t` to be - // more accurate. Currently, however, all Rust targets as of this writing are such that - // `core::ffi::size_t` and `usize` are the same size. - len: usize, - ); - - #[cfg(test)] - pub fn rpo128_finalize(sc: *mut Rpo128Context); -} - -#[repr(C)] -#[cfg(test)] -pub struct Rpo128Context { - pub content: [u64; 13usize], -} - -// TESTS -// ================================================================================================ - -#[cfg(all(test, feature = "std"))] -mod tests { - use alloc::vec::Vec; - use rand_utils::{rand_array, rand_value, rand_vector}; - - use super::*; - use crate::dsa::rpo_falcon512::{NONCE_LEN, PK_LEN, SIG_LEN, SK_LEN}; - - #[test] - fn falcon_ffi() { - unsafe { - //let mut rng = rand::thread_rng(); - - // --- generate a key pair from a seed ---------------------------- - - let mut pk = [0u8; PK_LEN]; - let mut sk = [0u8; SK_LEN]; - let seed: [u8; NONCE_LEN] = rand_array(); - - assert_eq!( - 0, - PQCLEAN_FALCON512_CLEAN_crypto_sign_keypair_from_seed_rpo( - pk.as_mut_ptr(), - sk.as_mut_ptr(), - seed.as_ptr() - ) - ); - - // --- sign a message and make sure it verifies ------------------- - - let mlen: usize = rand_value::() as usize; - let msg: Vec = rand_vector(mlen); - let mut detached_sig = [0u8; NONCE_LEN + SIG_LEN]; - let mut siglen = 0; - - assert_eq!( - 0, - PQCLEAN_FALCON512_CLEAN_crypto_sign_signature_rpo( - detached_sig.as_mut_ptr(), - &mut siglen as *mut usize, - msg.as_ptr(), - msg.len(), - sk.as_ptr() - ) - ); - - assert_eq!( - 0, - PQCLEAN_FALCON512_CLEAN_crypto_sign_verify_rpo( - detached_sig.as_ptr(), - siglen, - msg.as_ptr(), - msg.len(), - pk.as_ptr() - ) - ); - - // --- check verification of different signature ------------------ - - assert_eq!( - -1, - PQCLEAN_FALCON512_CLEAN_crypto_sign_verify_rpo( - detached_sig.as_ptr(), - siglen, - msg.as_ptr(), - msg.len() - 1, - pk.as_ptr() - ) - ); - - // --- check verification against a different pub key ------------- - - let mut pk_alt = [0u8; PK_LEN]; - let mut sk_alt = [0u8; SK_LEN]; - assert_eq!( - 0, - PQCLEAN_FALCON512_CLEAN_crypto_sign_keypair_rpo( - pk_alt.as_mut_ptr(), - sk_alt.as_mut_ptr() - ) - ); - - assert_eq!( - -1, - PQCLEAN_FALCON512_CLEAN_crypto_sign_verify_rpo( - detached_sig.as_ptr(), - siglen, - msg.as_ptr(), - msg.len(), - pk_alt.as_ptr() - ) - ); - } - } -} diff --git a/src/dsa/rpo_falcon512/hash_to_point.rs b/src/dsa/rpo_falcon512/hash_to_point.rs new file mode 100644 index 00000000..292a215c --- /dev/null +++ b/src/dsa/rpo_falcon512/hash_to_point.rs @@ -0,0 +1,68 @@ +use super::{math::FalconFelt, Nonce, Polynomial, Rpo256, Word, MODULUS, N, ZERO}; +use alloc::vec::Vec; +use num::Zero; + +// HASH-TO-POINT FUNCTIONS +// ================================================================================================ + +/// Returns a polynomial in Z_p[x]/(phi) representing the hash of the provided message and +/// nonce using RPO256. +pub fn hash_to_point_rpo256(message: Word, nonce: &Nonce) -> Polynomial { + let mut state = [ZERO; Rpo256::STATE_WIDTH]; + + // absorb the nonce into the state + let nonce_elements = nonce.to_elements(); + for (&n, s) in nonce_elements.iter().zip(state[Rpo256::RATE_RANGE].iter_mut()) { + *s = n; + } + Rpo256::apply_permutation(&mut state); + + // absorb message into the state + for (&m, s) in message.iter().zip(state[Rpo256::RATE_RANGE].iter_mut()) { + *s = m; + } + + // squeeze the coefficients of the polynomial + let mut i = 0; + let mut res = [FalconFelt::zero(); N]; + for _ in 0..64 { + Rpo256::apply_permutation(&mut state); + for a in &state[Rpo256::RATE_RANGE] { + res[i] = FalconFelt::new((a.as_int() % MODULUS as u64) as i16); + i += 1; + } + } + + Polynomial::new(res.to_vec()) +} + +/// Returns a polynomial in Z_p[x]/(phi) representing the hash of the provided message and +/// nonce using SHAKE256. This is the hash-to-point algorithm used in the reference implementation. +#[allow(dead_code)] +pub fn hash_to_point_shake256(message: &[u8], nonce: &Nonce) -> Polynomial { + use sha3::{ + digest::{ExtendableOutput, Update, XofReader}, + Shake256, + }; + + let mut data = vec![]; + data.extend_from_slice(nonce.as_bytes()); + data.extend_from_slice(message); + const K: u32 = (1u32 << 16) / MODULUS as u32; + + let mut hasher = Shake256::default(); + hasher.update(&data); + let mut reader = hasher.finalize_xof(); + + let mut coefficients: Vec = Vec::with_capacity(N); + while coefficients.len() != N { + let mut randomness = [0u8; 2]; + reader.read(&mut randomness); + let t = ((randomness[0] as u32) << 8) | (randomness[1] as u32); + if t < K * MODULUS as u32 { + coefficients.push(FalconFelt::new((t % MODULUS as u32) as i16)); + } + } + + Polynomial { coefficients } +} diff --git a/src/dsa/rpo_falcon512/keys.rs b/src/dsa/rpo_falcon512/keys.rs deleted file mode 100644 index 2ad1b4e7..00000000 --- a/src/dsa/rpo_falcon512/keys.rs +++ /dev/null @@ -1,235 +0,0 @@ -use super::{ - ByteReader, ByteWriter, Deserializable, DeserializationError, FalconError, Polynomial, - PublicKeyBytes, Rpo256, SecretKeyBytes, Serializable, Signature, Word, -}; -#[cfg(feature = "std")] -use { - super::{ffi, NonceBytes, NONCE_LEN, PK_LEN, SIG_LEN, SK_LEN}, - alloc::vec::Vec, -}; - -// PUBLIC KEY -// ================================================================================================ - -/// A public key for verifying signatures. -/// -/// The public key is a [Word] (i.e., 4 field elements) that is the hash of the coefficients of -/// the polynomial representing the raw bytes of the expanded public key. -/// -/// For Falcon-512, the first byte of the expanded public key is always equal to log2(512) i.e., 9. -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct PublicKey(Word); - -impl PublicKey { - /// Returns a new [PublicKey] which is a commitment to the provided expanded public key. - /// - /// # Errors - /// Returns an error if the decoding of the public key fails. - pub fn new(pk: PublicKeyBytes) -> Result { - let h = Polynomial::from_pub_key(&pk)?; - let pk_felts = h.to_elements(); - let pk_digest = Rpo256::hash_elements(&pk_felts).into(); - Ok(Self(pk_digest)) - } - - /// Verifies the provided signature against provided message and this public key. - pub fn verify(&self, message: Word, signature: &Signature) -> bool { - signature.verify(message, self.0) - } -} - -impl From for Word { - fn from(key: PublicKey) -> Self { - key.0 - } -} - -// KEY PAIR -// ================================================================================================ - -/// A key pair (public and secret keys) for signing messages. -/// -/// The secret key is a byte array of length [PK_LEN]. -/// The public key is a byte array of length [SK_LEN]. -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct KeyPair { - public_key: PublicKeyBytes, - secret_key: SecretKeyBytes, -} - -#[allow(clippy::new_without_default)] -impl KeyPair { - // CONSTRUCTORS - // -------------------------------------------------------------------------------------------- - - /// Generates a (public_key, secret_key) key pair from OS-provided randomness. - /// - /// # Errors - /// Returns an error if key generation fails. - #[cfg(feature = "std")] - pub fn new() -> Result { - let mut public_key = [0u8; PK_LEN]; - let mut secret_key = [0u8; SK_LEN]; - - let res = unsafe { - ffi::PQCLEAN_FALCON512_CLEAN_crypto_sign_keypair_rpo( - public_key.as_mut_ptr(), - secret_key.as_mut_ptr(), - ) - }; - - if res == 0 { - Ok(Self { public_key, secret_key }) - } else { - Err(FalconError::KeyGenerationFailed) - } - } - - /// Generates a (public_key, secret_key) key pair from the provided seed. - /// - /// # Errors - /// Returns an error if key generation fails. - #[cfg(feature = "std")] - pub fn from_seed(seed: &NonceBytes) -> Result { - let mut public_key = [0u8; PK_LEN]; - let mut secret_key = [0u8; SK_LEN]; - - let res = unsafe { - ffi::PQCLEAN_FALCON512_CLEAN_crypto_sign_keypair_from_seed_rpo( - public_key.as_mut_ptr(), - secret_key.as_mut_ptr(), - seed.as_ptr(), - ) - }; - - if res == 0 { - Ok(Self { public_key, secret_key }) - } else { - Err(FalconError::KeyGenerationFailed) - } - } - - // PUBLIC ACCESSORS - // -------------------------------------------------------------------------------------------- - - /// Returns the public key corresponding to this key pair. - pub fn public_key(&self) -> PublicKey { - // TODO: memoize public key commitment as computing it requires quite a bit of hashing. - // expect() is fine here because we assume that the key pair was constructed correctly. - PublicKey::new(self.public_key).expect("invalid key pair") - } - - /// Returns the expanded public key corresponding to this key pair. - pub fn expanded_public_key(&self) -> PublicKeyBytes { - self.public_key - } - - // SIGNATURE GENERATION - // -------------------------------------------------------------------------------------------- - - /// Signs a message with a secret key and a seed. - /// - /// # Errors - /// Returns an error of signature generation fails. - #[cfg(feature = "std")] - pub fn sign(&self, message: Word) -> Result { - let msg = message.iter().flat_map(|e| e.as_int().to_le_bytes()).collect::>(); - let msg_len = msg.len(); - let mut sig = [0_u8; SIG_LEN + NONCE_LEN]; - let mut sig_len: usize = 0; - - let res = unsafe { - ffi::PQCLEAN_FALCON512_CLEAN_crypto_sign_signature_rpo( - sig.as_mut_ptr(), - &mut sig_len as *mut usize, - msg.as_ptr(), - msg_len, - self.secret_key.as_ptr(), - ) - }; - - if res == 0 { - Ok(Signature { - sig, - pk: self.public_key, - pk_polynomial: Default::default(), - sig_polynomial: Default::default(), - }) - } else { - Err(FalconError::SigGenerationFailed) - } - } -} - -// SERIALIZATION / DESERIALIZATION -// ================================================================================================ - -impl Serializable for KeyPair { - fn write_into(&self, target: &mut W) { - target.write_bytes(&self.public_key); - target.write_bytes(&self.secret_key); - } -} - -impl Deserializable for KeyPair { - fn read_from(source: &mut R) -> Result { - let public_key: PublicKeyBytes = source.read_array()?; - let secret_key: SecretKeyBytes = source.read_array()?; - Ok(Self { public_key, secret_key }) - } -} - -// TESTS -// ================================================================================================ - -#[cfg(all(test, feature = "std"))] -mod tests { - use rand_utils::{rand_array, rand_vector}; - - use super::{super::Felt, KeyPair, NonceBytes, Word}; - - #[test] - fn test_falcon_verification() { - // generate random keys - let keys = KeyPair::new().unwrap(); - let pk = keys.public_key(); - - // sign a random message - let message: Word = rand_vector::(4).try_into().expect("Should not fail."); - let signature = keys.sign(message); - - // make sure the signature verifies correctly - assert!(pk.verify(message, signature.as_ref().unwrap())); - - // a signature should not verify against a wrong message - let message2: Word = rand_vector::(4).try_into().expect("Should not fail."); - assert!(!pk.verify(message2, signature.as_ref().unwrap())); - - // a signature should not verify against a wrong public key - let keys2 = KeyPair::new().unwrap(); - assert!(!keys2.public_key().verify(message, signature.as_ref().unwrap())) - } - - #[test] - fn test_falcon_verification_from_seed() { - // generate keys from a random seed - let seed: NonceBytes = rand_array(); - let keys = KeyPair::from_seed(&seed).unwrap(); - let pk = keys.public_key(); - - // sign a random message - let message: Word = rand_vector::(4).try_into().expect("Should not fail."); - let signature = keys.sign(message); - - // make sure the signature verifies correctly - assert!(pk.verify(message, signature.as_ref().unwrap())); - - // a signature should not verify against a wrong message - let message2: Word = rand_vector::(4).try_into().expect("Should not fail."); - assert!(!pk.verify(message2, signature.as_ref().unwrap())); - - // a signature should not verify against a wrong public key - let keys2 = KeyPair::new().unwrap(); - assert!(!keys2.public_key().verify(message, signature.as_ref().unwrap())) - } -} diff --git a/src/dsa/rpo_falcon512/keys/mod.rs b/src/dsa/rpo_falcon512/keys/mod.rs new file mode 100644 index 00000000..240ff6a3 --- /dev/null +++ b/src/dsa/rpo_falcon512/keys/mod.rs @@ -0,0 +1,53 @@ +use super::{ + math::{FalconFelt, Polynomial}, + ByteReader, ByteWriter, Deserializable, DeserializationError, Felt, Serializable, Signature, + Word, +}; + +mod public_key; +pub use public_key::{PubKeyPoly, PublicKey}; + +mod secret_key; +pub use secret_key::SecretKey; + +// TESTS +// ================================================================================================ + +#[cfg(test)] +mod tests { + use crate::{dsa::rpo_falcon512::SecretKey, Word, ONE}; + use rand::SeedableRng; + use rand_chacha::ChaCha20Rng; + use winter_math::FieldElement; + use winter_utils::{Deserializable, Serializable}; + + #[test] + fn test_falcon_verification() { + let seed = [0_u8; 32]; + let mut rng = ChaCha20Rng::from_seed(seed); + + // generate random keys + let sk = SecretKey::with_rng(&mut rng); + let pk = sk.public_key(); + + // test secret key serialization/deserialization + let mut buffer = vec![]; + sk.write_into(&mut buffer); + let sk = SecretKey::read_from_bytes(&buffer).unwrap(); + + // sign a random message + let message: Word = [ONE; 4]; + let signature = sk.sign_with_rng(message, &mut rng); + + // make sure the signature verifies correctly + assert!(pk.verify(message, &signature)); + + // a signature should not verify against a wrong message + let message2: Word = [ONE.double(); 4]; + assert!(!pk.verify(message2, &signature)); + + // a signature should not verify against a wrong public key + let sk2 = SecretKey::with_rng(&mut rng); + assert!(!sk2.public_key().verify(message, &signature)) + } +} diff --git a/src/dsa/rpo_falcon512/keys/public_key.rs b/src/dsa/rpo_falcon512/keys/public_key.rs new file mode 100644 index 00000000..8b172d20 --- /dev/null +++ b/src/dsa/rpo_falcon512/keys/public_key.rs @@ -0,0 +1,138 @@ +use crate::dsa::rpo_falcon512::FALCON_ENCODING_BITS; + +use super::{ + super::{Rpo256, LOG_N, N, PK_LEN}, + ByteReader, ByteWriter, Deserializable, DeserializationError, FalconFelt, Felt, Polynomial, + Serializable, Signature, Word, +}; +use alloc::string::ToString; +use core::ops::Deref; +use num::Zero; + +// PUBLIC KEY +// ================================================================================================ + +/// A public key for verifying signatures. +/// +/// The public key is a [Word] (i.e., 4 field elements) that is the hash of the coefficients of +/// the polynomial representing the raw bytes of the expanded public key. The hash is computed +/// using Rpo256. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct PublicKey(Word); + +impl PublicKey { + /// Returns a new [PublicKey] which is a commitment to the provided expanded public key. + pub fn new(pub_key: Word) -> Self { + Self(pub_key) + } + + /// Verifies the provided signature against provided message and this public key. + pub fn verify(&self, message: Word, signature: &Signature) -> bool { + signature.verify(message, self.0) + } +} + +impl From for PublicKey { + fn from(pk_poly: PubKeyPoly) -> Self { + let pk_felts: Polynomial = pk_poly.0.into(); + let pk_digest = Rpo256::hash_elements(&pk_felts.coefficients).into(); + Self(pk_digest) + } +} + +impl From for Word { + fn from(key: PublicKey) -> Self { + key.0 + } +} + +// PUBLIC KEY POLYNOMIAL +// ================================================================================================ + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct PubKeyPoly(pub Polynomial); + +impl Deref for PubKeyPoly { + type Target = Polynomial; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From> for PubKeyPoly { + fn from(pk_poly: Polynomial) -> Self { + Self(pk_poly) + } +} + +impl Serializable for &PubKeyPoly { + fn write_into(&self, target: &mut W) { + let mut buf = [0_u8; PK_LEN]; + buf[0] = LOG_N; + + let mut acc = 0_u32; + let mut acc_len: u32 = 0; + + let mut input_pos = 1; + for c in self.0.coefficients.iter() { + let c = c.value(); + acc = (acc << FALCON_ENCODING_BITS) | c as u32; + acc_len += FALCON_ENCODING_BITS; + while acc_len >= 8 { + acc_len -= 8; + buf[input_pos] = (acc >> acc_len) as u8; + input_pos += 1; + } + } + if acc_len > 0 { + buf[input_pos] = (acc >> (8 - acc_len)) as u8; + } + + target.write(buf); + } +} + +impl Deserializable for PubKeyPoly { + fn read_from(source: &mut R) -> Result { + let buf = source.read_array::()?; + + if buf[0] != LOG_N { + return Err(DeserializationError::InvalidValue(format!( + "Failed to decode public key: expected the first byte to be {LOG_N} but was {}", + buf[0] + ))); + } + + let mut acc = 0_u32; + let mut acc_len = 0; + + let mut output = [FalconFelt::zero(); N]; + let mut output_idx = 0; + + for &byte in buf.iter().skip(1) { + acc = (acc << 8) | (byte as u32); + acc_len += 8; + + if acc_len >= FALCON_ENCODING_BITS { + acc_len -= FALCON_ENCODING_BITS; + let w = (acc >> acc_len) & 0x3FFF; + let element = w.try_into().map_err(|err| { + DeserializationError::InvalidValue(format!( + "Failed to decode public key: {err}" + )) + })?; + output[output_idx] = element; + output_idx += 1; + } + } + + if (acc & ((1u32 << acc_len) - 1)) == 0 { + Ok(Polynomial::new(output.to_vec()).into()) + } else { + Err(DeserializationError::InvalidValue( + "Failed to decode public key: input not fully consumed".to_string(), + )) + } + } +} diff --git a/src/dsa/rpo_falcon512/keys/secret_key.rs b/src/dsa/rpo_falcon512/keys/secret_key.rs new file mode 100644 index 00000000..4a099ff2 --- /dev/null +++ b/src/dsa/rpo_falcon512/keys/secret_key.rs @@ -0,0 +1,386 @@ +use super::{ + super::{ + math::{ffldl, ffsampling, gram, normalize_tree, FalconFelt, FastFft, LdlTree, Polynomial}, + signature::SignaturePoly, + ByteReader, ByteWriter, Deserializable, DeserializationError, Nonce, Serializable, + ShortLatticeBasis, Signature, Word, MODULUS, N, SIGMA, SIG_L2_BOUND, + }, + PubKeyPoly, PublicKey, +}; +use crate::dsa::rpo_falcon512::{ + hash_to_point::hash_to_point_rpo256, math::ntru_gen, SIG_NONCE_LEN, SK_LEN, +}; +use alloc::{string::ToString, vec::Vec}; +use num::Complex; +use num_complex::Complex64; +use rand::Rng; + +#[cfg(not(feature = "std"))] +use num::Float; + +// CONSTANTS +// ================================================================================================ + +const WIDTH_BIG_POLY_COEFFICIENT: usize = 8; +const WIDTH_SMALL_POLY_COEFFICIENT: usize = 6; + +// SECRET KEY +// ================================================================================================ + +/// The secret key is a quadruple [[g, -f], [G, -F]] of polynomials with integer coefficients. Each +/// polynomial is of degree at most N = 512 and computations with these polynomials is done modulo +/// the monic irreducible polynomial ϕ = x^N + 1. The secret key is a basis for a lattice and has +/// the property of being short with respect to a certain norm and an upper bound appropriate for +/// a given security parameter. The public key on the other hand is another basis for the same +/// lattice and can be described by a single polynomial h with integer coefficients modulo ϕ. +/// The two keys are related by the following relation: +/// +/// 1. h = g /f [mod ϕ][mod p] +/// 2. f.G - g.F = p [mod ϕ] +/// +/// where p = 12289 is the Falcon prime. Equation 2 is called the NTRU equation. +/// The secret key is generated by first sampling a random pair (f, g) of polynomials using +/// an appropriate distribution that yields short but not too short polynomials with integer +/// coefficients modulo ϕ. The NTRU equation is then used to find a matching pair (F, G). +/// The public key is then derived from the secret key using equation 1. +/// +/// To allow for fast signature generation, the secret key is pre-processed into a more suitable +/// form, called the LDL tree, and this allows for fast sampling of short vectors in the lattice +/// using Fast Fourier sampling during signature generation (ffSampling algorithm 11 in [1]). +/// +/// [1]: https://falcon-sign.info/falcon.pdf +#[derive(Debug, Clone)] +pub struct SecretKey { + secret_key: ShortLatticeBasis, + tree: LdlTree, +} + +#[allow(clippy::new_without_default)] +impl SecretKey { + // CONSTRUCTORS + // -------------------------------------------------------------------------------------------- + + /// Generates a secret key from OS-provided randomness. + #[cfg(feature = "std")] + pub fn new() -> Self { + use rand::{rngs::StdRng, SeedableRng}; + + let mut rng = StdRng::from_entropy(); + Self::with_rng(&mut rng) + } + + /// Generates a secret_key using the provided random number generator `Rng`. + pub fn with_rng(rng: &mut R) -> Self { + let basis = ntru_gen(N, rng); + Self::from_short_lattice_basis(basis) + } + + /// Given a short basis [[g, -f], [G, -F]], computes the normalized LDL tree i.e., Falcon tree. + fn from_short_lattice_basis(basis: ShortLatticeBasis) -> SecretKey { + // FFT each polynomial of the short basis. + let basis_fft = to_complex_fft(&basis); + // compute the Gram matrix. + let gram_fft = gram(basis_fft); + // construct the LDL tree of the Gram matrix. + let mut tree = ffldl(gram_fft); + // normalize the leaves of the LDL tree. + normalize_tree(&mut tree, SIGMA); + Self { secret_key: basis, tree } + } + + // PUBLIC ACCESSORS + // -------------------------------------------------------------------------------------------- + + /// Returns the polynomials of the short lattice basis of this secret key. + pub fn short_lattice_basis(&self) -> &ShortLatticeBasis { + &self.secret_key + } + + /// Returns the public key corresponding to this secret key. + pub fn public_key(&self) -> PublicKey { + self.compute_pub_key_poly().into() + } + + /// Returns the LDL tree associated to this secret key. + pub fn tree(&self) -> &LdlTree { + &self.tree + } + + // SIGNATURE GENERATION + // -------------------------------------------------------------------------------------------- + + /// Signs a message with this secret key. + #[cfg(feature = "std")] + pub fn sign(&self, message: Word) -> Signature { + use rand::{rngs::StdRng, SeedableRng}; + + let mut rng = StdRng::from_entropy(); + self.sign_with_rng(message, &mut rng) + } + + /// Signs a message with the secret key relying on the provided randomness generator. + pub fn sign_with_rng(&self, message: Word, rng: &mut R) -> Signature { + let mut nonce_bytes = [0u8; SIG_NONCE_LEN]; + rng.fill_bytes(&mut nonce_bytes); + let nonce = Nonce::new(nonce_bytes); + + let h = self.compute_pub_key_poly(); + let c = hash_to_point_rpo256(message, &nonce); + let s2 = self.sign_helper(c, rng); + + Signature::new(nonce, h, s2) + } + + // HELPER METHODS + // -------------------------------------------------------------------------------------------- + + /// Derives the public key corresponding to this secret key using h = g /f [mod ϕ][mod p]. + pub fn compute_pub_key_poly(&self) -> PubKeyPoly { + let g: Polynomial = self.secret_key[0].clone().into(); + let g_fft = g.fft(); + let minus_f: Polynomial = self.secret_key[1].clone().into(); + let f = -minus_f; + let f_fft = f.fft(); + let h_fft = g_fft.hadamard_div(&f_fft); + h_fft.ifft().into() + } + + /// Signs a message polynomial with the secret key. + /// + /// Takes a randomness generator implementing `Rng` and message polynomial representing `c` + /// the hash-to-point of the message to be signed. It outputs a signature polynomial `s2`. + fn sign_helper(&self, c: Polynomial, rng: &mut R) -> SignaturePoly { + let one_over_q = 1.0 / (MODULUS as f64); + let c_over_q_fft = c.map(|cc| Complex::new(one_over_q * cc.value() as f64, 0.0)).fft(); + + // B = [[FFT(g), -FFT(f)], [FFT(G), -FFT(F)]] + let [g_fft, minus_f_fft, big_g_fft, minus_big_f_fft] = to_complex_fft(&self.secret_key); + let t0 = c_over_q_fft.hadamard_mul(&minus_big_f_fft); + let t1 = -c_over_q_fft.hadamard_mul(&minus_f_fft); + + loop { + let bold_s = loop { + let z = ffsampling(&(t0.clone(), t1.clone()), &self.tree, rng); + let t0_min_z0 = t0.clone() - z.0; + let t1_min_z1 = t1.clone() - z.1; + + // s = (t-z) * B + let s0 = t0_min_z0.hadamard_mul(&g_fft) + t1_min_z1.hadamard_mul(&big_g_fft); + let s1 = + t0_min_z0.hadamard_mul(&minus_f_fft) + t1_min_z1.hadamard_mul(&minus_big_f_fft); + + // compute the norm of (s0||s1) and note that they are in FFT representation + let length_squared: f64 = + (s0.coefficients.iter().map(|a| (a * a.conj()).re).sum::() + + s1.coefficients.iter().map(|a| (a * a.conj()).re).sum::()) + / (N as f64); + + if length_squared > (SIG_L2_BOUND as f64) { + continue; + } + + break [-s0, s1]; + }; + + let s2 = bold_s[1].ifft(); + let s2_coef: [i16; N] = s2 + .coefficients + .iter() + .map(|a| a.re.round() as i16) + .collect::>() + .try_into() + .expect("The number of coefficients should be equal to N"); + + if let Ok(s2) = SignaturePoly::try_from(&s2_coef) { + return s2; + } + } + } +} + +// SERIALIZATION / DESERIALIZATION +// ================================================================================================ + +impl Serializable for SecretKey { + fn write_into(&self, target: &mut W) { + let basis = &self.secret_key; + + // header + let n = basis[0].coefficients.len(); + let l = n.checked_ilog2().unwrap() as u8; + let header: u8 = (5 << 4) | l; + + let f = &basis[1]; + let g = &basis[0]; + let capital_f = &basis[3]; + + let mut buffer = Vec::with_capacity(1281); + buffer.push(header); + + let f_i8: Vec = f.coefficients.iter().map(|&a| -a as i8).collect(); + let f_i8_encoded = encode_i8(&f_i8, WIDTH_SMALL_POLY_COEFFICIENT).unwrap(); + buffer.extend_from_slice(&f_i8_encoded); + + let g_i8: Vec = g.coefficients.iter().map(|&a| a as i8).collect(); + let g_i8_encoded = encode_i8(&g_i8, WIDTH_SMALL_POLY_COEFFICIENT).unwrap(); + buffer.extend_from_slice(&g_i8_encoded); + + let big_f_i8: Vec = capital_f.coefficients.iter().map(|&a| -a as i8).collect(); + let big_f_i8_encoded = encode_i8(&big_f_i8, WIDTH_BIG_POLY_COEFFICIENT).unwrap(); + buffer.extend_from_slice(&big_f_i8_encoded); + target.write_bytes(&buffer); + } +} + +impl Deserializable for SecretKey { + fn read_from(source: &mut R) -> Result { + let byte_vector: [u8; SK_LEN] = source.read_array()?; + + // check length + if byte_vector.len() < 2 { + return Err(DeserializationError::InvalidValue("Invalid encoding length: Failed to decode as length is different from the one expected".to_string())); + } + + // read fields + let header = byte_vector[0]; + + // check fixed bits in header + if (header >> 4) != 5 { + return Err(DeserializationError::InvalidValue("Invalid header format".to_string())); + } + + // check log n + let logn = (header & 15) as usize; + let n = 1 << logn; + + // match against const variant generic parameter + if n != N { + return Err(DeserializationError::InvalidValue( + "Unsupported Falcon DSA variant".to_string(), + )); + } + + if byte_vector.len() != SK_LEN { + return Err(DeserializationError::InvalidValue("Invalid encoding length: Failed to decode as length is different from the one expected".to_string())); + } + + let chunk_size_f = ((n * WIDTH_SMALL_POLY_COEFFICIENT) + 7) >> 3; + let chunk_size_g = ((n * WIDTH_SMALL_POLY_COEFFICIENT) + 7) >> 3; + let chunk_size_big_f = ((n * WIDTH_BIG_POLY_COEFFICIENT) + 7) >> 3; + + let f = decode_i8(&byte_vector[1..chunk_size_f + 1], WIDTH_SMALL_POLY_COEFFICIENT).unwrap(); + let g = decode_i8( + &byte_vector[chunk_size_f + 1..(chunk_size_f + chunk_size_g + 1)], + WIDTH_SMALL_POLY_COEFFICIENT, + ) + .unwrap(); + let big_f = decode_i8( + &byte_vector[(chunk_size_f + chunk_size_g + 1) + ..(chunk_size_f + chunk_size_g + chunk_size_big_f + 1)], + WIDTH_BIG_POLY_COEFFICIENT, + ) + .unwrap(); + + let f = Polynomial::new(f.iter().map(|&c| FalconFelt::new(c.into())).collect()); + let g = Polynomial::new(g.iter().map(|&c| FalconFelt::new(c.into())).collect()); + let big_f = Polynomial::new(big_f.iter().map(|&c| FalconFelt::new(c.into())).collect()); + + // big_g * f - g * big_f = p (mod X^n + 1) + let big_g = g.fft().hadamard_div(&f.fft()).hadamard_mul(&big_f.fft()).ifft(); + let basis = [ + g.map(|f| f.balanced_value()), + -f.map(|f| f.balanced_value()), + big_g.map(|f| f.balanced_value()), + -big_f.map(|f| f.balanced_value()), + ]; + Ok(Self::from_short_lattice_basis(basis)) + } +} + +// HELPER FUNCTIONS +// ================================================================================================ + +/// Computes the complex FFT of the secret key polynomials. +fn to_complex_fft(basis: &[Polynomial; 4]) -> [Polynomial>; 4] { + let [g, f, big_g, big_f] = basis.clone(); + let g_fft = g.map(|cc| Complex64::new(*cc as f64, 0.0)).fft(); + let minus_f_fft = f.map(|cc| -Complex64::new(*cc as f64, 0.0)).fft(); + let big_g_fft = big_g.map(|cc| Complex64::new(*cc as f64, 0.0)).fft(); + let minus_big_f_fft = big_f.map(|cc| -Complex64::new(*cc as f64, 0.0)).fft(); + [g_fft, minus_f_fft, big_g_fft, minus_big_f_fft] +} + +/// Encodes a sequence of signed integers such that each integer x satisfies |x| < 2^(bits-1) +/// for a given parameter bits. bits can take either the value 6 or 8. +pub fn encode_i8(x: &[i8], bits: usize) -> Option> { + let maxv = (1 << (bits - 1)) - 1_usize; + let maxv = maxv as i8; + let minv = -maxv; + + for &c in x { + if c > maxv || c < minv { + return None; + } + } + + let out_len = ((N * bits) + 7) >> 3; + let mut buf = vec![0_u8; out_len]; + + let mut acc = 0_u32; + let mut acc_len = 0; + let mask = ((1_u16 << bits) - 1) as u8; + + let mut input_pos = 0; + for &c in x { + acc = (acc << bits) | (c as u8 & mask) as u32; + acc_len += bits; + while acc_len >= 8 { + acc_len -= 8; + buf[input_pos] = (acc >> acc_len) as u8; + input_pos += 1; + } + } + if acc_len > 0 { + buf[input_pos] = (acc >> (8 - acc_len)) as u8; + } + + Some(buf) +} + +/// Decodes a sequence of bytes into a sequence of signed integers such that each integer x +/// satisfies |x| < 2^(bits-1) for a given parameter bits. bits can take either the value 6 or 8. +pub fn decode_i8(buf: &[u8], bits: usize) -> Option> { + let mut x = [0_i8; N]; + + let mut i = 0; + let mut j = 0; + let mut acc = 0_u32; + let mut acc_len = 0; + let mask = (1_u32 << bits) - 1; + let a = (1 << bits) as u8; + let b = ((1 << (bits - 1)) - 1) as u8; + + while i < N { + acc = (acc << 8) | (buf[j] as u32); + j += 1; + acc_len += 8; + + while acc_len >= bits && i < N { + acc_len -= bits; + let w = (acc >> acc_len) & mask; + + let w = w as u8; + + let z = if w > b { w as i8 - a as i8 } else { w as i8 }; + + x[i] = z; + i += 1; + } + } + + if (acc & ((1u32 << acc_len) - 1)) == 0 { + Some(x.to_vec()) + } else { + None + } +} diff --git a/src/dsa/rpo_falcon512/math/ffsampling.rs b/src/dsa/rpo_falcon512/math/ffsampling.rs new file mode 100644 index 00000000..6cdf858a --- /dev/null +++ b/src/dsa/rpo_falcon512/math/ffsampling.rs @@ -0,0 +1,123 @@ +use super::{fft::FastFft, polynomial::Polynomial, samplerz::sampler_z}; +use alloc::boxed::Box; +use num::{One, Zero}; +use num_complex::{Complex, Complex64}; +use rand::Rng; + +#[cfg(not(feature = "std"))] +use num::Float; + +const SIGMIN: f64 = 1.2778336969128337; + +/// Computes the Gram matrix. The argument must be a 2x2 matrix +/// whose elements are equal-length vectors of complex numbers, +/// representing polynomials in FFT domain. +pub fn gram(b: [Polynomial; 4]) -> [Polynomial; 4] { + const N: usize = 2; + let mut g: [Polynomial>; 4] = + [Polynomial::zero(), Polynomial::zero(), Polynomial::zero(), Polynomial::zero()]; + for i in 0..N { + for j in 0..N { + for k in 0..N { + g[N * i + j] = g[N * i + j].clone() + + b[N * i + k].hadamard_mul(&b[N * j + k].map(|c| c.conj())); + } + } + } + g +} + +/// Computes the LDL decomposition of a 2x2 matrix G such that +/// L D L* = G +/// where D is diagonal, and L is lower-triangular. The elements of the matrices are in FFT domain. +pub fn ldl( + g: [Polynomial; 4], +) -> ([Polynomial; 4], [Polynomial; 4]) { + let zero = Polynomial::::one(); + let one = Polynomial::::zero(); + + let l10 = g[2].hadamard_div(&g[0]); + let bc = l10.map(|c| c * c.conj()); + let abc = g[0].hadamard_mul(&bc); + let d11 = g[3].clone() - abc; + + let l = [one.clone(), zero.clone(), l10.clone(), one]; + let d = [g[0].clone(), zero.clone(), zero, d11]; + (l, d) +} + +#[derive(Debug, Clone)] +pub enum LdlTree { + Branch(Polynomial, Box, Box), + Leaf([Complex64; 2]), +} + +/// Computes the LDL Tree of G. Corresponds to Algorithm 9 of the specification [1, p.37]. +/// The argument is a 2x2 matrix of polynomials, given in FFT form. +/// [1]: https://falcon-sign.info/falcon.pdf +pub fn ffldl(gram_matrix: [Polynomial; 4]) -> LdlTree { + let n = gram_matrix[0].coefficients.len(); + let (l, d) = ldl(gram_matrix); + + if n > 2 { + let (d00, d01) = d[0].split_fft(); + let (d10, d11) = d[3].split_fft(); + let g0 = [d00.clone(), d01.clone(), d01.map(|c| c.conj()), d00]; + let g1 = [d10.clone(), d11.clone(), d11.map(|c| c.conj()), d10]; + LdlTree::Branch(l[2].clone(), Box::new(ffldl(g0)), Box::new(ffldl(g1))) + } else { + LdlTree::Branch( + l[2].clone(), + Box::new(LdlTree::Leaf(d[0].clone().coefficients.try_into().unwrap())), + Box::new(LdlTree::Leaf(d[3].clone().coefficients.try_into().unwrap())), + ) + } +} + +/// Normalizes the leaves of an LDL tree using a given normalization value `sigma`. +pub fn normalize_tree(tree: &mut LdlTree, sigma: f64) { + match tree { + LdlTree::Branch(_ell, left, right) => { + normalize_tree(left, sigma); + normalize_tree(right, sigma); + } + LdlTree::Leaf(vector) => { + vector[0] = Complex::new(sigma / vector[0].re.sqrt(), 0.0); + vector[1] = Complex64::zero(); + } + } +} + +/// Samples short polynomials using a Falcon tree. Algorithm 11 from the spec [1, p.40]. +/// +/// [1]: https://falcon-sign.info/falcon.pdf +pub fn ffsampling( + t: &(Polynomial, Polynomial), + tree: &LdlTree, + mut rng: &mut R, +) -> (Polynomial, Polynomial) { + match tree { + LdlTree::Branch(ell, left, right) => { + let bold_t1 = t.1.split_fft(); + let bold_z1 = ffsampling(&bold_t1, right, rng); + let z1 = Polynomial::::merge_fft(&bold_z1.0, &bold_z1.1); + + // t0' = t0 + (t1 - z1) * l + let t0_prime = t.0.clone() + (t.1.clone() - z1.clone()).hadamard_mul(ell); + + let bold_t0 = t0_prime.split_fft(); + let bold_z0 = ffsampling(&bold_t0, left, rng); + let z0 = Polynomial::::merge_fft(&bold_z0.0, &bold_z0.1); + + (z0, z1) + } + LdlTree::Leaf(value) => { + let z0 = sampler_z(t.0.coefficients[0].re, value[0].re, SIGMIN, &mut rng); + let z1 = sampler_z(t.1.coefficients[0].re, value[0].re, SIGMIN, &mut rng); + ( + Polynomial::new(vec![Complex64::new(z0 as f64, 0.0)]), + Polynomial::new(vec![Complex64::new(z1 as f64, 0.0)]), + ) + } + } +} diff --git a/src/dsa/rpo_falcon512/math/fft.rs b/src/dsa/rpo_falcon512/math/fft.rs new file mode 100644 index 00000000..10e6bfed --- /dev/null +++ b/src/dsa/rpo_falcon512/math/fft.rs @@ -0,0 +1,1927 @@ +use super::{field::FalconFelt, polynomial::Polynomial, Inverse}; +use alloc::vec::Vec; +use core::{ + f64::consts::PI, + ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}, +}; +use num::{One, Zero}; +use num_complex::Complex64; + +#[cfg(not(feature = "std"))] +use num::Float; + +/// Implements Cyclotomic FFT without bitreversing the outputs, and using precomputed powers of the +/// 2n-th primitive root of unity. +pub trait FastFft: Sized + Clone { + type Field: Add + Mul + AddAssign + MulAssign + Neg + Sub + SubAssign; + fn fft_inplace(&mut self); + fn fft(&self) -> Self { + let mut a = self.clone(); + a.fft_inplace(); + a + } + + fn merge_fft(a: &Self, b: &Self) -> Self; + fn split_fft(&self) -> (Self, Self); + + fn ifft_inplace(&mut self); + fn ifft(&self) -> Self { + let mut a = self.clone(); + a.ifft_inplace(); + a + } +} + +pub trait CyclotomicFourier +where + Self: Sized + + Copy + + One + + Zero + + Add + + Sub + + Mul + + MulAssign + + Inverse, +{ + /// Gets the inverse of 2^n. + #[allow(dead_code)] + fn power_of_two_inverse(n: usize) -> Self { + let mut a = Self::one() + Self::one(); + for _ in 0..n { + a *= a; + } + Self::inverse_or_zero(a) + } + + /// Gets a primitive nth (with n a power of 2) root of unity. + #[allow(dead_code)] + fn primitive_root_of_unity(n: usize) -> Self; + + /// Computes the integer whose n-bit binary expansion is the reverse of that of the argument. + fn bitreverse_index(arg: usize, n: usize) -> usize { + assert!(n > 0); + assert_eq!(n & (n - 1), 0); + let mut rev = 0; + let mut m = n >> 1; + let mut k = 1; + while m > 0 { + rev |= (((arg & m) != 0) as usize) * k; + k <<= 1; + m >>= 1; + } + rev + } + + /// Computes the first n powers of the 2nth root of unity, and put them in bit-reversed order. + #[allow(dead_code)] + fn bitreversed_powers(n: usize) -> Vec { + let psi = Self::primitive_root_of_unity(2 * n); + let mut array = vec![Self::zero(); n]; + let mut alpha = Self::one(); + for a in array.iter_mut() { + *a = alpha; + alpha *= psi; + } + Self::bitreverse_array(&mut array); + array + } + + /// Computes the first n powers of the 2nth root of unity, invert them, and put them in + /// bit-reversed order. + #[allow(dead_code)] + fn bitreversed_powers_inverse(n: usize) -> Vec { + let psi = Self::primitive_root_of_unity(2 * n).inverse_or_zero(); + let mut array = vec![Self::zero(); n]; + let mut alpha = Self::one(); + for a in array.iter_mut() { + *a = alpha; + alpha *= psi; + } + Self::bitreverse_array(&mut array); + array + } + + /// Reorders the given elements in the array by reversing the binary expansions of their indices. + fn bitreverse_array(array: &mut [T]) { + let n = array.len(); + for i in 0..n { + let j = Self::bitreverse_index(i, n); + if i < j { + array.swap(i, j); + } + } + } + + /// Computes the evaluations of the polynomial on the roots of the polynomial X^n + 1 using a + /// fast Fourier transform. Algorithm 1 from https://eprint.iacr.org/2016/504.pdf. + /// + /// Arguments: + /// + /// - a : &mut [Self] + /// (a reference to) a mutable array of field elements which is to + /// be transformed under the FFT. The transformation happens in- + /// place. + /// + /// - psi_rev: &[Self] + /// (a reference to) an array of powers of psi, from 0 to n-1, + /// but ordered by bit-reversed index. Here psi is a primitive root + /// of order 2n. You can use + /// `Self::bitreversed_powers(psi, n)` for this purpose, but this + /// trait implementation is not const. For the performance benefit + /// you want a precompiled array, which you can get if you can get + /// by implementing the same method and marking it "const". + fn fft(a: &mut [Self], psi_rev: &[Self]) { + let n = a.len(); + let mut t = n; + let mut m = 1; + while m < n { + t >>= 1; + for i in 0..m { + let j1 = 2 * i * t; + let j2 = j1 + t - 1; + let s = psi_rev[m + i]; + for j in j1..=j2 { + let u = a[j]; + let v = a[j + t] * s; + a[j] = u + v; + a[j + t] = u - v; + } + } + m <<= 1; + } + } + + /// Computes the coefficients of the polynomial with the given evaluations on the roots of + /// X^n + 1 using an inverse fast Fourier transform. + /// Algorithm 2 from https://eprint.iacr.org/2016/504.pdf. + /// + /// Arguments: + /// + /// - a : &mut [Self] + /// (a reference to) a mutable array of field elements which is to + /// be transformed under the IFFT. The transformation happens in- + /// place. + /// + /// - psi_inv_rev: &[Self] + /// (a reference to) an array of powers of psi^-1, from 0 to n-1, + /// but ordered by bit-reversed index. Here psi is a primitive root of + /// order 2n. You can use + /// `Self::bitreversed_powers(Self::inverse_or_zero(psi), n)` for + /// this purpose, but this trait implementation is not const. For + /// the performance benefit you want a precompiled array, which you + /// can get if you can get by implementing the same methods and marking + /// them "const". + fn ifft(a: &mut [Self], psi_inv_rev: &[Self], ninv: Self) { + let n = a.len(); + let mut t = 1; + let mut m = n; + while m > 1 { + let h = m / 2; + let mut j1 = 0; + for i in 0..h { + let j2 = j1 + t - 1; + let s = psi_inv_rev[h + i]; + for j in j1..=j2 { + let u = a[j]; + let v = a[j + t]; + a[j] = u + v; + a[j + t] = (u - v) * s; + } + j1 += 2 * t; + } + t <<= 1; + m >>= 1; + } + for ai in a.iter_mut() { + *ai *= ninv; + } + } + + fn split_fft(f: &[Self], psi_inv_rev: &[Self]) -> (Vec, Vec) { + let n_over_2 = f.len() / 2; + let mut f0 = vec![Self::zero(); n_over_2]; + let mut f1 = vec![Self::zero(); n_over_2]; + let two_inv = (Self::one() + Self::one()).inverse_or_zero(); + for i in 0..n_over_2 { + let two_i = i * 2; + let two_zeta_inv = two_inv * psi_inv_rev[n_over_2 + i]; + f0[i] = two_inv * (f[two_i] + f[two_i + 1]); + f1[i] = two_zeta_inv * (f[two_i] - f[two_i + 1]); + } + (f0, f1) + } + + fn merge_fft(f0: &[Self], f1: &[Self], psi_rev: &[Self]) -> Vec { + let n_over_2 = f0.len(); + let n = 2 * n_over_2; + let mut f = vec![Self::zero(); n]; + for i in 0..n_over_2 { + let two_i = i * 2; + f[two_i] = f0[i] + psi_rev[n_over_2 + i] * f1[i]; + f[two_i + 1] = f0[i] - psi_rev[n_over_2 + i] * f1[i]; + } + f + } +} + +impl CyclotomicFourier for Complex64 { + fn primitive_root_of_unity(n: usize) -> Self { + let angle = 2. * PI / (n as f64); + Complex64::new(f64::cos(angle), f64::sin(angle)) + } + + /// Custom implementation of CyclotomicFourier::bitreversed_powers for + /// better precision. + fn bitreversed_powers(n: usize) -> Vec { + let mut array = vec![Self::zero(); n]; + let half_circle = PI; + for (i, a) in array.iter_mut().enumerate() { + let angle = (i as f64) * half_circle / (n as f64); + *a = Self::new(f64::cos(angle), f64::sin(angle)); + } + Self::bitreverse_array(&mut array); + array + } + + /// Custom implementation of CyclotomicFourier::bitreversed_powers_inverse + /// for better precision. + fn bitreversed_powers_inverse(n: usize) -> Vec { + let mut array = vec![Self::zero(); n]; + let half_circle = PI; + for (i, a) in array.iter_mut().enumerate() { + let angle = (i as f64) * half_circle / (n as f64); + *a = Self::new(f64::cos(angle), -f64::sin(angle)); + } + Self::bitreverse_array(&mut array); + array + } +} + +impl FastFft for Polynomial { + type Field = Complex64; + fn fft_inplace(&mut self) { + let n = self.coefficients.len(); + debug_assert!( + (1..=512).contains(&n), + "unsupported: n = {n} not a power of 2 or larger than 512" + ); + Complex64::fft(&mut self.coefficients, &COMPLEX_BITREVERSED_POWERS); + } + + fn ifft_inplace(&mut self) { + let n = self.coefficients.len(); + debug_assert!( + (1..=512).contains(&n), + "unsupported: n = {n} not a power of 2 or larger than 512" + ); + let psi_inv_rev: Vec = + COMPLEX_BITREVERSED_POWERS.iter().map(|c| Complex64::new(c.re, -c.im)).collect(); + let ninv = Complex64::new(1.0 / (n as f64), 0.0); + Complex64::ifft(&mut self.coefficients, &psi_inv_rev, ninv); + } + + fn merge_fft(a: &Self, b: &Self) -> Self { + let n = a.coefficients.len(); + debug_assert!( + (1..=512).contains(&n), + "unsupported: n = {n} not a power of 2 or larger than 512" + ); + Self { + coefficients: Self::Field::merge_fft( + &a.coefficients, + &b.coefficients, + &COMPLEX_BITREVERSED_POWERS, + ), + } + } + + fn split_fft(&self) -> (Self, Self) { + let n = self.coefficients.len(); + debug_assert!( + (1..=512).contains(&n), + "unsupported: n = {n} not a power of 2 or larger than 512" + ); + let psi_inv_rev: Vec = + COMPLEX_BITREVERSED_POWERS.iter().map(|c| Complex64::new(c.re, -c.im)).collect(); + let (a, b) = Self::Field::split_fft(&self.coefficients, &psi_inv_rev); + (Self { coefficients: a }, Self { coefficients: b }) + } +} + +impl FastFft for Polynomial { + type Field = FalconFelt; + + fn fft_inplace(&mut self) { + let n = self.coefficients.len(); + debug_assert!( + (1..=512).contains(&n), + "unsupported: n = {n} not a power of 2 or larger than 512" + ); + FalconFelt::fft(&mut self.coefficients, &FELT_BITREVERSED_POWERS); + } + + fn ifft_inplace(&mut self) { + let n = self.coefficients.len(); + debug_assert!( + (1..=512).contains(&n), + "unsupported: n = {n} not a power of 2 or larger than 512" + ); + let ninv = match n { + 1 => FELT_NINV_1, + 2 => FELT_NINV_2, + 4 => FELT_NINV_4, + 8 => FELT_NINV_8, + 16 => FELT_NINV_16, + 32 => FELT_NINV_32, + 64 => FELT_NINV_64, + 128 => FELT_NINV_128, + 256 => FELT_NINV_256, + 512 => FELT_NINV_512, + _ => unreachable!("vector length is not power of 2 or larger than 512"), + }; + FalconFelt::ifft(&mut self.coefficients, &FELT_BITREVERSED_POWERS_INVERSE, ninv); + } + + fn merge_fft(a: &Self, b: &Self) -> Self { + let n = a.coefficients.len(); + debug_assert!( + (1..=512).contains(&n), + "unsupported: n = {n} not a power of 2 or larger than 512" + ); + Self { + coefficients: Self::Field::merge_fft( + &a.coefficients, + &b.coefficients, + &FELT_BITREVERSED_POWERS, + ), + } + } + + fn split_fft(&self) -> (Self, Self) { + let n = self.coefficients.len(); + debug_assert!( + (1..=512).contains(&n), + "unsupported: n = {n} not a power of 2 or larger than 512" + ); + let (a, b) = Self::Field::split_fft(&self.coefficients, &FELT_BITREVERSED_POWERS_INVERSE); + (Self { coefficients: a }, Self { coefficients: b }) + } +} + +#[allow(clippy::approx_constant)] +const COMPLEX_BITREVERSED_POWERS: [Complex64; 512] = [ + Complex64::new(1.0, 0.0), + Complex64::new(0.00000000000000006123233995736766, 1.0), + Complex64::new(0.7071067811865476, 0.7071067811865475), + Complex64::new(-0.7071067811865475, 0.7071067811865476), + Complex64::new(0.9238795325112867, 0.3826834323650898), + Complex64::new(-0.3826834323650897, 0.9238795325112867), + Complex64::new(0.38268343236508984, 0.9238795325112867), + Complex64::new(-0.9238795325112867, 0.3826834323650899), + Complex64::new(0.9807852804032304, 0.19509032201612825), + Complex64::new(-0.1950903220161282, 0.9807852804032304), + Complex64::new(0.5555702330196023, 0.8314696123025452), + Complex64::new(-0.8314696123025453, 0.5555702330196022), + Complex64::new(0.8314696123025452, 0.5555702330196022), + Complex64::new(-0.555570233019602, 0.8314696123025455), + Complex64::new(0.19509032201612833, 0.9807852804032304), + Complex64::new(-0.9807852804032304, 0.1950903220161286), + Complex64::new(0.9951847266721969, 0.0980171403295606), + Complex64::new(-0.09801714032956065, 0.9951847266721969), + Complex64::new(0.6343932841636455, 0.773010453362737), + Complex64::new(-0.773010453362737, 0.6343932841636455), + Complex64::new(0.881921264348355, 0.47139673682599764), + Complex64::new(-0.4713967368259977, 0.881921264348355), + Complex64::new(0.29028467725446233, 0.9569403357322089), + Complex64::new(-0.9569403357322088, 0.2902846772544624), + Complex64::new(0.9569403357322088, 0.29028467725446233), + Complex64::new(-0.29028467725446216, 0.9569403357322089), + Complex64::new(0.4713967368259978, 0.8819212643483549), + Complex64::new(-0.8819212643483549, 0.47139673682599786), + Complex64::new(0.773010453362737, 0.6343932841636455), + Complex64::new(-0.6343932841636454, 0.7730104533627371), + Complex64::new(0.09801714032956077, 0.9951847266721968), + Complex64::new(-0.9951847266721968, 0.09801714032956083), + Complex64::new(0.9987954562051724, 0.049067674327418015), + Complex64::new(-0.04906767432741801, 0.9987954562051724), + Complex64::new(0.6715589548470183, 0.7409511253549591), + Complex64::new(-0.7409511253549589, 0.6715589548470186), + Complex64::new(0.9039892931234433, 0.4275550934302821), + Complex64::new(-0.42755509343028186, 0.9039892931234434), + Complex64::new(0.33688985339222005, 0.9415440651830208), + Complex64::new(-0.9415440651830207, 0.33688985339222033), + Complex64::new(0.970031253194544, 0.24298017990326387), + Complex64::new(-0.24298017990326387, 0.970031253194544), + Complex64::new(0.5141027441932217, 0.8577286100002721), + Complex64::new(-0.857728610000272, 0.5141027441932218), + Complex64::new(0.8032075314806449, 0.5956993044924334), + Complex64::new(-0.5956993044924334, 0.8032075314806449), + Complex64::new(0.14673047445536175, 0.989176509964781), + Complex64::new(-0.989176509964781, 0.1467304744553618), + Complex64::new(0.989176509964781, 0.14673047445536175), + Complex64::new(-0.14673047445536164, 0.989176509964781), + Complex64::new(0.5956993044924335, 0.8032075314806448), + Complex64::new(-0.8032075314806448, 0.5956993044924335), + Complex64::new(0.8577286100002721, 0.5141027441932217), + Complex64::new(-0.5141027441932217, 0.8577286100002721), + Complex64::new(0.24298017990326398, 0.970031253194544), + Complex64::new(-0.970031253194544, 0.24298017990326407), + Complex64::new(0.9415440651830208, 0.33688985339222005), + Complex64::new(-0.33688985339221994, 0.9415440651830208), + Complex64::new(0.4275550934302822, 0.9039892931234433), + Complex64::new(-0.9039892931234433, 0.42755509343028203), + Complex64::new(0.7409511253549591, 0.6715589548470183), + Complex64::new(-0.6715589548470184, 0.740951125354959), + Complex64::new(0.049067674327418126, 0.9987954562051724), + Complex64::new(-0.9987954562051724, 0.049067674327417966), + Complex64::new(0.9996988186962042, 0.024541228522912288), + Complex64::new(-0.024541228522912142, 0.9996988186962042), + Complex64::new(0.6895405447370669, 0.7242470829514669), + Complex64::new(-0.7242470829514668, 0.689540544737067), + Complex64::new(0.9142097557035307, 0.40524131400498986), + Complex64::new(-0.40524131400498975, 0.9142097557035307), + Complex64::new(0.3598950365349883, 0.9329927988347388), + Complex64::new(-0.9329927988347388, 0.35989503653498833), + Complex64::new(0.9757021300385286, 0.2191012401568698), + Complex64::new(-0.21910124015686966, 0.9757021300385286), + Complex64::new(0.5349976198870973, 0.844853565249707), + Complex64::new(-0.8448535652497071, 0.5349976198870972), + Complex64::new(0.8175848131515837, 0.5758081914178453), + Complex64::new(-0.5758081914178453, 0.8175848131515837), + Complex64::new(0.17096188876030136, 0.9852776423889412), + Complex64::new(-0.9852776423889412, 0.17096188876030122), + Complex64::new(0.99247953459871, 0.1224106751992162), + Complex64::new(-0.12241067519921615, 0.99247953459871), + Complex64::new(0.6152315905806268, 0.7883464276266062), + Complex64::new(-0.7883464276266062, 0.6152315905806269), + Complex64::new(0.8700869911087115, 0.49289819222978404), + Complex64::new(-0.492898192229784, 0.8700869911087115), + Complex64::new(0.2667127574748984, 0.9637760657954398), + Complex64::new(-0.9637760657954398, 0.2667127574748985), + Complex64::new(0.9495281805930367, 0.3136817403988915), + Complex64::new(-0.3136817403988914, 0.9495281805930367), + Complex64::new(0.4496113296546066, 0.8932243011955153), + Complex64::new(-0.8932243011955152, 0.4496113296546069), + Complex64::new(0.7572088465064846, 0.6531728429537768), + Complex64::new(-0.6531728429537765, 0.7572088465064847), + Complex64::new(0.07356456359966745, 0.9972904566786902), + Complex64::new(-0.9972904566786902, 0.07356456359966773), + Complex64::new(0.9972904566786902, 0.07356456359966743), + Complex64::new(-0.07356456359966733, 0.9972904566786902), + Complex64::new(0.6531728429537768, 0.7572088465064845), + Complex64::new(-0.7572088465064846, 0.6531728429537766), + Complex64::new(0.8932243011955153, 0.44961132965460654), + Complex64::new(-0.4496113296546067, 0.8932243011955152), + Complex64::new(0.3136817403988916, 0.9495281805930367), + Complex64::new(-0.9495281805930367, 0.3136817403988914), + Complex64::new(0.9637760657954398, 0.26671275747489837), + Complex64::new(-0.2667127574748983, 0.9637760657954398), + Complex64::new(0.4928981922297841, 0.8700869911087113), + Complex64::new(-0.8700869911087113, 0.49289819222978415), + Complex64::new(0.7883464276266063, 0.6152315905806268), + Complex64::new(-0.6152315905806267, 0.7883464276266063), + Complex64::new(0.12241067519921628, 0.99247953459871), + Complex64::new(-0.99247953459871, 0.12241067519921635), + Complex64::new(0.9852776423889412, 0.17096188876030122), + Complex64::new(-0.17096188876030124, 0.9852776423889412), + Complex64::new(0.5758081914178453, 0.8175848131515837), + Complex64::new(-0.8175848131515836, 0.5758081914178454), + Complex64::new(0.8448535652497071, 0.5349976198870972), + Complex64::new(-0.534997619887097, 0.8448535652497072), + Complex64::new(0.21910124015686977, 0.9757021300385286), + Complex64::new(-0.9757021300385285, 0.21910124015687005), + Complex64::new(0.932992798834739, 0.3598950365349881), + Complex64::new(-0.35989503653498817, 0.9329927988347388), + Complex64::new(0.40524131400498986, 0.9142097557035307), + Complex64::new(-0.9142097557035307, 0.4052413140049899), + Complex64::new(0.724247082951467, 0.6895405447370668), + Complex64::new(-0.6895405447370669, 0.7242470829514669), + Complex64::new(0.024541228522912264, 0.9996988186962042), + Complex64::new(-0.9996988186962042, 0.024541228522912326), + Complex64::new(0.9999247018391445, 0.012271538285719925), + Complex64::new(-0.012271538285719823, 0.9999247018391445), + Complex64::new(0.6983762494089729, 0.7157308252838186), + Complex64::new(-0.7157308252838186, 0.6983762494089729), + Complex64::new(0.9191138516900578, 0.3939920400610481), + Complex64::new(-0.393992040061048, 0.9191138516900578), + Complex64::new(0.3713171939518376, 0.9285060804732155), + Complex64::new(-0.9285060804732155, 0.3713171939518377), + Complex64::new(0.9783173707196277, 0.20711137619221856), + Complex64::new(-0.20711137619221845, 0.9783173707196277), + Complex64::new(0.5453249884220465, 0.838224705554838), + Complex64::new(-0.8382247055548381, 0.5453249884220464), + Complex64::new(0.8245893027850253, 0.5657318107836131), + Complex64::new(-0.5657318107836132, 0.8245893027850252), + Complex64::new(0.18303988795514106, 0.9831054874312163), + Complex64::new(-0.9831054874312163, 0.1830398879551409), + Complex64::new(0.9939069700023561, 0.11022220729388306), + Complex64::new(-0.11022220729388306, 0.9939069700023561), + Complex64::new(0.6248594881423865, 0.7807372285720944), + Complex64::new(-0.7807372285720945, 0.6248594881423863), + Complex64::new(0.8760700941954066, 0.4821837720791227), + Complex64::new(-0.4821837720791227, 0.8760700941954066), + Complex64::new(0.27851968938505306, 0.9604305194155658), + Complex64::new(-0.9604305194155658, 0.27851968938505317), + Complex64::new(0.9533060403541939, 0.3020059493192281), + Complex64::new(-0.3020059493192281, 0.9533060403541939), + Complex64::new(0.46053871095824, 0.8876396204028539), + Complex64::new(-0.8876396204028538, 0.4605387109582402), + Complex64::new(0.765167265622459, 0.6438315428897914), + Complex64::new(-0.6438315428897913, 0.7651672656224591), + Complex64::new(0.08579731234443988, 0.996312612182778), + Complex64::new(-0.996312612182778, 0.08579731234444016), + Complex64::new(0.9981181129001492, 0.06132073630220858), + Complex64::new(-0.06132073630220853, 0.9981181129001492), + Complex64::new(0.6624157775901718, 0.7491363945234593), + Complex64::new(-0.7491363945234591, 0.662415777590172), + Complex64::new(0.8986744656939538, 0.43861623853852766), + Complex64::new(-0.4386162385385274, 0.8986744656939539), + Complex64::new(0.325310292162263, 0.9456073253805213), + Complex64::new(-0.9456073253805212, 0.32531029216226326), + Complex64::new(0.9669764710448521, 0.25486565960451457), + Complex64::new(-0.2548656596045145, 0.9669764710448521), + Complex64::new(0.5035383837257176, 0.8639728561215867), + Complex64::new(-0.8639728561215867, 0.5035383837257177), + Complex64::new(0.7958369046088836, 0.6055110414043255), + Complex64::new(-0.6055110414043254, 0.7958369046088836), + Complex64::new(0.13458070850712622, 0.99090263542778), + Complex64::new(-0.99090263542778, 0.13458070850712628), + Complex64::new(0.9873014181578584, 0.15885814333386145), + Complex64::new(-0.15885814333386128, 0.9873014181578584), + Complex64::new(0.5857978574564389, 0.8104571982525948), + Complex64::new(-0.8104571982525947, 0.585797857456439), + Complex64::new(0.8513551931052652, 0.524589682678469), + Complex64::new(-0.5245896826784687, 0.8513551931052652), + Complex64::new(0.23105810828067128, 0.9729399522055601), + Complex64::new(-0.9729399522055601, 0.23105810828067133), + Complex64::new(0.937339011912575, 0.34841868024943456), + Complex64::new(-0.3484186802494344, 0.937339011912575), + Complex64::new(0.4164295600976373, 0.9091679830905223), + Complex64::new(-0.9091679830905224, 0.41642956009763715), + Complex64::new(0.7326542716724128, 0.680600997795453), + Complex64::new(-0.680600997795453, 0.7326542716724128), + Complex64::new(0.03680722294135899, 0.9993223845883495), + Complex64::new(-0.9993223845883495, 0.03680722294135883), + Complex64::new(0.9993223845883495, 0.03680722294135883), + Complex64::new(-0.036807222941358866, 0.9993223845883495), + Complex64::new(0.6806009977954531, 0.7326542716724128), + Complex64::new(-0.7326542716724127, 0.6806009977954532), + Complex64::new(0.9091679830905224, 0.41642956009763715), + Complex64::new(-0.416429560097637, 0.9091679830905225), + Complex64::new(0.3484186802494345, 0.937339011912575), + Complex64::new(-0.9373390119125748, 0.3484186802494348), + Complex64::new(0.9729399522055602, 0.2310581082806711), + Complex64::new(-0.23105810828067114, 0.9729399522055602), + Complex64::new(0.5245896826784688, 0.8513551931052652), + Complex64::new(-0.8513551931052652, 0.524589682678469), + Complex64::new(0.8104571982525948, 0.5857978574564389), + Complex64::new(-0.5857978574564389, 0.8104571982525948), + Complex64::new(0.1588581433338614, 0.9873014181578584), + Complex64::new(-0.9873014181578584, 0.15885814333386147), + Complex64::new(0.99090263542778, 0.13458070850712617), + Complex64::new(-0.1345807085071261, 0.99090263542778), + Complex64::new(0.6055110414043255, 0.7958369046088835), + Complex64::new(-0.7958369046088835, 0.6055110414043257), + Complex64::new(0.8639728561215868, 0.5035383837257176), + Complex64::new(-0.5035383837257175, 0.8639728561215868), + Complex64::new(0.2548656596045146, 0.9669764710448521), + Complex64::new(-0.9669764710448521, 0.2548656596045147), + Complex64::new(0.9456073253805213, 0.3253102921622629), + Complex64::new(-0.32531029216226287, 0.9456073253805214), + Complex64::new(0.4386162385385277, 0.8986744656939538), + Complex64::new(-0.8986744656939539, 0.43861623853852755), + Complex64::new(0.7491363945234594, 0.6624157775901718), + Complex64::new(-0.6624157775901719, 0.7491363945234593), + Complex64::new(0.06132073630220865, 0.9981181129001492), + Complex64::new(-0.9981181129001492, 0.06132073630220849), + Complex64::new(0.996312612182778, 0.0857973123444399), + Complex64::new(-0.08579731234443976, 0.996312612182778), + Complex64::new(0.6438315428897915, 0.765167265622459), + Complex64::new(-0.765167265622459, 0.6438315428897914), + Complex64::new(0.8876396204028539, 0.46053871095824), + Complex64::new(-0.46053871095824006, 0.8876396204028539), + Complex64::new(0.3020059493192282, 0.9533060403541938), + Complex64::new(-0.9533060403541939, 0.30200594931922803), + Complex64::new(0.9604305194155658, 0.27851968938505306), + Complex64::new(-0.27851968938505295, 0.9604305194155659), + Complex64::new(0.48218377207912283, 0.8760700941954066), + Complex64::new(-0.8760700941954065, 0.4821837720791229), + Complex64::new(0.7807372285720945, 0.6248594881423863), + Complex64::new(-0.6248594881423862, 0.7807372285720946), + Complex64::new(0.11022220729388318, 0.9939069700023561), + Complex64::new(-0.9939069700023561, 0.11022220729388324), + Complex64::new(0.9831054874312163, 0.18303988795514095), + Complex64::new(-0.18303988795514092, 0.9831054874312163), + Complex64::new(0.5657318107836132, 0.8245893027850253), + Complex64::new(-0.8245893027850251, 0.5657318107836135), + Complex64::new(0.8382247055548381, 0.5453249884220465), + Complex64::new(-0.5453249884220462, 0.8382247055548382), + Complex64::new(0.20711137619221856, 0.9783173707196277), + Complex64::new(-0.9783173707196275, 0.20711137619221884), + Complex64::new(0.9285060804732156, 0.37131719395183754), + Complex64::new(-0.3713171939518375, 0.9285060804732156), + Complex64::new(0.3939920400610481, 0.9191138516900578), + Complex64::new(-0.9191138516900578, 0.39399204006104815), + Complex64::new(0.7157308252838186, 0.6983762494089729), + Complex64::new(-0.6983762494089728, 0.7157308252838187), + Complex64::new(0.012271538285719944, 0.9999247018391445), + Complex64::new(-0.9999247018391445, 0.012271538285720007), + Complex64::new(0.9999811752826011, 0.006135884649154475), + Complex64::new(-0.006135884649154393, 0.9999811752826011), + Complex64::new(0.7027547444572253, 0.7114321957452164), + Complex64::new(-0.7114321957452165, 0.7027547444572252), + Complex64::new(0.921514039342042, 0.38834504669882625), + Complex64::new(-0.3883450466988262, 0.921514039342042), + Complex64::new(0.3770074102164183, 0.9262102421383113), + Complex64::new(-0.9262102421383114, 0.37700741021641815), + Complex64::new(0.9795697656854405, 0.2011046348420919), + Complex64::new(-0.20110463484209182, 0.9795697656854405), + Complex64::new(0.5504579729366048, 0.83486287498638), + Complex64::new(-0.83486287498638, 0.5504579729366049), + Complex64::new(0.8280450452577558, 0.560661576197336), + Complex64::new(-0.5606615761973359, 0.8280450452577558), + Complex64::new(0.18906866414980628, 0.9819638691095552), + Complex64::new(-0.9819638691095552, 0.18906866414980636), + Complex64::new(0.9945645707342554, 0.10412163387205459), + Complex64::new(-0.1041216338720546, 0.9945645707342554), + Complex64::new(0.6296382389149271, 0.7768884656732324), + Complex64::new(-0.7768884656732323, 0.6296382389149272), + Complex64::new(0.8790122264286335, 0.4767992300633221), + Complex64::new(-0.4767992300633219, 0.8790122264286335), + Complex64::new(0.2844075372112718, 0.9587034748958716), + Complex64::new(-0.9587034748958715, 0.2844075372112721), + Complex64::new(0.9551411683057708, 0.2961508882436238), + Complex64::new(-0.29615088824362384, 0.9551411683057707), + Complex64::new(0.4659764957679661, 0.8847970984309378), + Complex64::new(-0.8847970984309378, 0.4659764957679662), + Complex64::new(0.7691033376455797, 0.6391244448637757), + Complex64::new(-0.6391244448637757, 0.7691033376455796), + Complex64::new(0.0919089564971327, 0.9957674144676598), + Complex64::new(-0.9957674144676598, 0.09190895649713275), + Complex64::new(0.9984755805732948, 0.055195244349689934), + Complex64::new(-0.05519524434968991, 0.9984755805732948), + Complex64::new(0.6669999223036375, 0.745057785441466), + Complex64::new(-0.745057785441466, 0.6669999223036376), + Complex64::new(0.901348847046022, 0.43309381885315196), + Complex64::new(-0.4330938188531519, 0.901348847046022), + Complex64::new(0.33110630575987643, 0.9435934581619604), + Complex64::new(-0.9435934581619604, 0.3311063057598765), + Complex64::new(0.9685220942744174, 0.24892760574572015), + Complex64::new(-0.24892760574572012, 0.9685220942744174), + Complex64::new(0.508830142543107, 0.8608669386377673), + Complex64::new(-0.8608669386377671, 0.5088301425431073), + Complex64::new(0.799537269107905, 0.600616479383869), + Complex64::new(-0.6006164793838688, 0.7995372691079052), + Complex64::new(0.14065823933284924, 0.9900582102622971), + Complex64::new(-0.990058210262297, 0.14065823933284954), + Complex64::new(0.9882575677307495, 0.15279718525844344), + Complex64::new(-0.1527971852584433, 0.9882575677307495), + Complex64::new(0.5907597018588743, 0.8068475535437992), + Complex64::new(-0.8068475535437993, 0.5907597018588742), + Complex64::new(0.8545579883654005, 0.5193559901655896), + Complex64::new(-0.5193559901655896, 0.8545579883654005), + Complex64::new(0.23702360599436734, 0.9715038909862518), + Complex64::new(-0.9715038909862518, 0.23702360599436717), + Complex64::new(0.9394592236021899, 0.3426607173119944), + Complex64::new(-0.34266071731199427, 0.9394592236021899), + Complex64::new(0.4220002707997998, 0.9065957045149153), + Complex64::new(-0.9065957045149153, 0.42200027079979985), + Complex64::new(0.7368165688773699, 0.6760927035753159), + Complex64::new(-0.6760927035753158, 0.73681656887737), + Complex64::new(0.04293825693494096, 0.9990777277526454), + Complex64::new(-0.9990777277526454, 0.04293825693494102), + Complex64::new(0.9995294175010931, 0.030674803176636626), + Complex64::new(-0.03067480317663646, 0.9995294175010931), + Complex64::new(0.6850836677727004, 0.7284643904482252), + Complex64::new(-0.7284643904482252, 0.6850836677727004), + Complex64::new(0.9117060320054299, 0.4108431710579039), + Complex64::new(-0.4108431710579038, 0.9117060320054299), + Complex64::new(0.3541635254204905, 0.9351835099389475), + Complex64::new(-0.9351835099389476, 0.3541635254204904), + Complex64::new(0.9743393827855759, 0.22508391135979283), + Complex64::new(-0.22508391135979267, 0.9743393827855759), + Complex64::new(0.5298036246862948, 0.8481203448032971), + Complex64::new(-0.8481203448032971, 0.5298036246862948), + Complex64::new(0.8140363297059484, 0.5808139580957645), + Complex64::new(-0.5808139580957644, 0.8140363297059485), + Complex64::new(0.1649131204899701, 0.9863080972445987), + Complex64::new(-0.9863080972445986, 0.16491312048997014), + Complex64::new(0.9917097536690995, 0.12849811079379317), + Complex64::new(-0.1284981107937931, 0.9917097536690995), + Complex64::new(0.6103828062763095, 0.7921065773002124), + Complex64::new(-0.7921065773002122, 0.6103828062763097), + Complex64::new(0.8670462455156926, 0.49822766697278187), + Complex64::new(-0.4982276669727816, 0.8670462455156928), + Complex64::new(0.26079411791527557, 0.9653944416976894), + Complex64::new(-0.9653944416976893, 0.26079411791527585), + Complex64::new(0.9475855910177411, 0.3195020308160157), + Complex64::new(-0.31950203081601564, 0.9475855910177412), + Complex64::new(0.44412214457042926, 0.8959662497561851), + Complex64::new(-0.8959662497561851, 0.4441221445704293), + Complex64::new(0.7531867990436125, 0.6578066932970786), + Complex64::new(-0.6578066932970786, 0.7531867990436125), + Complex64::new(0.0674439195636641, 0.9977230666441916), + Complex64::new(-0.9977230666441916, 0.06744391956366418), + Complex64::new(0.9968202992911657, 0.07968243797143013), + Complex64::new(-0.07968243797143001, 0.9968202992911658), + Complex64::new(0.6485144010221126, 0.7612023854842618), + Complex64::new(-0.7612023854842617, 0.6485144010221126), + Complex64::new(0.8904487232447579, 0.45508358712634384), + Complex64::new(-0.4550835871263437, 0.890448723244758), + Complex64::new(0.307849640041535, 0.9514350209690083), + Complex64::new(-0.9514350209690083, 0.30784964004153503), + Complex64::new(0.9621214042690416, 0.272621355449949), + Complex64::new(-0.27262135544994887, 0.9621214042690416), + Complex64::new(0.48755016014843605, 0.8730949784182901), + Complex64::new(-0.8730949784182901, 0.4875501601484359), + Complex64::new(0.7845565971555752, 0.6200572117632891), + Complex64::new(-0.6200572117632892, 0.7845565971555751), + Complex64::new(0.11631863091190488, 0.9932119492347945), + Complex64::new(-0.9932119492347945, 0.11631863091190471), + Complex64::new(0.984210092386929, 0.17700422041214875), + Complex64::new(-0.17700422041214875, 0.984210092386929), + Complex64::new(0.5707807458869674, 0.8211025149911046), + Complex64::new(-0.8211025149911046, 0.5707807458869673), + Complex64::new(0.8415549774368984, 0.5401714727298929), + Complex64::new(-0.5401714727298929, 0.8415549774368984), + Complex64::new(0.21311031991609136, 0.9770281426577544), + Complex64::new(-0.9770281426577544, 0.21311031991609142), + Complex64::new(0.9307669610789837, 0.36561299780477385), + Complex64::new(-0.36561299780477385, 0.9307669610789837), + Complex64::new(0.3996241998456468, 0.9166790599210427), + Complex64::new(-0.9166790599210426, 0.39962419984564707), + Complex64::new(0.7200025079613817, 0.693971460889654), + Complex64::new(-0.6939714608896538, 0.7200025079613818), + Complex64::new(0.01840672990580482, 0.9998305817958234), + Complex64::new(-0.9998305817958234, 0.0184067299058051), + Complex64::new(0.9998305817958234, 0.01840672990580482), + Complex64::new(-0.018406729905804695, 0.9998305817958234), + Complex64::new(0.693971460889654, 0.7200025079613817), + Complex64::new(-0.7200025079613817, 0.693971460889654), + Complex64::new(0.9166790599210427, 0.3996241998456468), + Complex64::new(-0.3996241998456467, 0.9166790599210427), + Complex64::new(0.36561299780477396, 0.9307669610789837), + Complex64::new(-0.9307669610789837, 0.3656129978047738), + Complex64::new(0.9770281426577544, 0.21311031991609136), + Complex64::new(-0.21311031991609125, 0.9770281426577544), + Complex64::new(0.540171472729893, 0.8415549774368983), + Complex64::new(-0.8415549774368983, 0.540171472729893), + Complex64::new(0.8211025149911046, 0.5707807458869673), + Complex64::new(-0.5707807458869671, 0.8211025149911048), + Complex64::new(0.17700422041214886, 0.984210092386929), + Complex64::new(-0.984210092386929, 0.17700422041214894), + Complex64::new(0.9932119492347945, 0.11631863091190475), + Complex64::new(-0.11631863091190475, 0.9932119492347945), + Complex64::new(0.6200572117632892, 0.7845565971555752), + Complex64::new(-0.784556597155575, 0.6200572117632894), + Complex64::new(0.8730949784182901, 0.487550160148436), + Complex64::new(-0.4875501601484357, 0.8730949784182902), + Complex64::new(0.272621355449949, 0.9621214042690416), + Complex64::new(-0.9621214042690415, 0.27262135544994925), + Complex64::new(0.9514350209690083, 0.30784964004153487), + Complex64::new(-0.30784964004153487, 0.9514350209690083), + Complex64::new(0.45508358712634384, 0.8904487232447579), + Complex64::new(-0.8904487232447579, 0.4550835871263439), + Complex64::new(0.7612023854842618, 0.6485144010221124), + Complex64::new(-0.6485144010221124, 0.7612023854842619), + Complex64::new(0.07968243797143013, 0.9968202992911657), + Complex64::new(-0.9968202992911657, 0.0796824379714302), + Complex64::new(0.9977230666441916, 0.06744391956366405), + Complex64::new(-0.06744391956366398, 0.9977230666441916), + Complex64::new(0.6578066932970786, 0.7531867990436124), + Complex64::new(-0.7531867990436124, 0.6578066932970787), + Complex64::new(0.8959662497561852, 0.4441221445704292), + Complex64::new(-0.44412214457042914, 0.8959662497561852), + Complex64::new(0.31950203081601575, 0.9475855910177411), + Complex64::new(-0.9475855910177411, 0.3195020308160158), + Complex64::new(0.9653944416976894, 0.2607941179152755), + Complex64::new(-0.26079411791527546, 0.9653944416976894), + Complex64::new(0.49822766697278187, 0.8670462455156926), + Complex64::new(-0.8670462455156928, 0.49822766697278176), + Complex64::new(0.7921065773002124, 0.6103828062763095), + Complex64::new(-0.6103828062763096, 0.7921065773002123), + Complex64::new(0.12849811079379322, 0.9917097536690995), + Complex64::new(-0.9917097536690995, 0.12849811079379309), + Complex64::new(0.9863080972445987, 0.16491312048996992), + Complex64::new(-0.16491312048996995, 0.9863080972445987), + Complex64::new(0.5808139580957645, 0.8140363297059483), + Complex64::new(-0.8140363297059484, 0.5808139580957645), + Complex64::new(0.8481203448032972, 0.5298036246862946), + Complex64::new(-0.5298036246862947, 0.8481203448032972), + Complex64::new(0.22508391135979278, 0.9743393827855759), + Complex64::new(-0.9743393827855759, 0.22508391135979283), + Complex64::new(0.9351835099389476, 0.35416352542049034), + Complex64::new(-0.3541635254204904, 0.9351835099389476), + Complex64::new(0.4108431710579039, 0.9117060320054299), + Complex64::new(-0.9117060320054298, 0.41084317105790413), + Complex64::new(0.7284643904482252, 0.6850836677727004), + Complex64::new(-0.6850836677727002, 0.7284643904482253), + Complex64::new(0.03067480317663658, 0.9995294175010931), + Complex64::new(-0.9995294175010931, 0.030674803176636865), + Complex64::new(0.9990777277526454, 0.04293825693494082), + Complex64::new(-0.042938256934940834, 0.9990777277526454), + Complex64::new(0.676092703575316, 0.7368165688773698), + Complex64::new(-0.7368165688773699, 0.6760927035753159), + Complex64::new(0.9065957045149153, 0.4220002707997997), + Complex64::new(-0.4220002707997997, 0.9065957045149153), + Complex64::new(0.3426607173119944, 0.9394592236021899), + Complex64::new(-0.9394592236021899, 0.34266071731199443), + Complex64::new(0.9715038909862518, 0.2370236059943672), + Complex64::new(-0.23702360599436723, 0.9715038909862518), + Complex64::new(0.5193559901655895, 0.8545579883654005), + Complex64::new(-0.8545579883654004, 0.5193559901655898), + Complex64::new(0.8068475535437993, 0.5907597018588742), + Complex64::new(-0.590759701858874, 0.8068475535437994), + Complex64::new(0.1527971852584434, 0.9882575677307495), + Complex64::new(-0.9882575677307495, 0.15279718525844369), + Complex64::new(0.9900582102622971, 0.1406582393328492), + Complex64::new(-0.14065823933284913, 0.9900582102622971), + Complex64::new(0.600616479383869, 0.799537269107905), + Complex64::new(-0.7995372691079051, 0.6006164793838689), + Complex64::new(0.8608669386377673, 0.508830142543107), + Complex64::new(-0.5088301425431071, 0.8608669386377672), + Complex64::new(0.24892760574572026, 0.9685220942744173), + Complex64::new(-0.9685220942744174, 0.2489276057457201), + Complex64::new(0.9435934581619604, 0.33110630575987643), + Complex64::new(-0.3311063057598763, 0.9435934581619604), + Complex64::new(0.433093818853152, 0.901348847046022), + Complex64::new(-0.9013488470460219, 0.43309381885315207), + Complex64::new(0.7450577854414661, 0.6669999223036375), + Complex64::new(-0.6669999223036374, 0.7450577854414661), + Complex64::new(0.05519524434969003, 0.9984755805732948), + Complex64::new(-0.9984755805732948, 0.055195244349690094), + Complex64::new(0.9957674144676598, 0.09190895649713272), + Complex64::new(-0.09190895649713257, 0.9957674144676598), + Complex64::new(0.6391244448637757, 0.7691033376455796), + Complex64::new(-0.7691033376455795, 0.6391244448637758), + Complex64::new(0.8847970984309378, 0.4659764957679662), + Complex64::new(-0.465976495767966, 0.8847970984309379), + Complex64::new(0.29615088824362396, 0.9551411683057707), + Complex64::new(-0.9551411683057707, 0.296150888243624), + Complex64::new(0.9587034748958716, 0.2844075372112719), + Complex64::new(-0.2844075372112717, 0.9587034748958716), + Complex64::new(0.47679923006332225, 0.8790122264286334), + Complex64::new(-0.8790122264286335, 0.4767992300633221), + Complex64::new(0.7768884656732324, 0.629638238914927), + Complex64::new(-0.6296382389149271, 0.7768884656732324), + Complex64::new(0.10412163387205473, 0.9945645707342554), + Complex64::new(-0.9945645707342554, 0.10412163387205457), + Complex64::new(0.9819638691095552, 0.1890686641498062), + Complex64::new(-0.18906866414980616, 0.9819638691095552), + Complex64::new(0.560661576197336, 0.8280450452577558), + Complex64::new(-0.8280450452577557, 0.5606615761973361), + Complex64::new(0.83486287498638, 0.5504579729366048), + Complex64::new(-0.5504579729366047, 0.8348628749863801), + Complex64::new(0.20110463484209196, 0.9795697656854405), + Complex64::new(-0.9795697656854405, 0.201104634842092), + Complex64::new(0.9262102421383114, 0.37700741021641826), + Complex64::new(-0.3770074102164182, 0.9262102421383114), + Complex64::new(0.3883450466988263, 0.9215140393420419), + Complex64::new(-0.9215140393420418, 0.3883450466988266), + Complex64::new(0.7114321957452164, 0.7027547444572253), + Complex64::new(-0.7027547444572251, 0.7114321957452167), + Complex64::new(0.006135884649154515, 0.9999811752826011), + Complex64::new(-0.9999811752826011, 0.006135884649154799), +]; + +const FELT_BITREVERSED_POWERS: [FalconFelt; 512] = [ + FalconFelt::new(1), + FalconFelt::new(1479), + FalconFelt::new(8246), + FalconFelt::new(5146), + FalconFelt::new(4134), + FalconFelt::new(6553), + FalconFelt::new(11567), + FalconFelt::new(1305), + FalconFelt::new(5860), + FalconFelt::new(3195), + FalconFelt::new(1212), + FalconFelt::new(10643), + FalconFelt::new(3621), + FalconFelt::new(9744), + FalconFelt::new(8785), + FalconFelt::new(3542), + FalconFelt::new(7311), + FalconFelt::new(10938), + FalconFelt::new(8961), + FalconFelt::new(5777), + FalconFelt::new(5023), + FalconFelt::new(6461), + FalconFelt::new(5728), + FalconFelt::new(4591), + FalconFelt::new(3006), + FalconFelt::new(9545), + FalconFelt::new(563), + FalconFelt::new(9314), + FalconFelt::new(2625), + FalconFelt::new(11340), + FalconFelt::new(4821), + FalconFelt::new(2639), + FalconFelt::new(12149), + FalconFelt::new(1853), + FalconFelt::new(726), + FalconFelt::new(4611), + FalconFelt::new(11112), + FalconFelt::new(4255), + FalconFelt::new(2768), + FalconFelt::new(1635), + FalconFelt::new(2963), + FalconFelt::new(7393), + FalconFelt::new(2366), + FalconFelt::new(9238), + FalconFelt::new(9198), + FalconFelt::new(12208), + FalconFelt::new(11289), + FalconFelt::new(7969), + FalconFelt::new(8736), + FalconFelt::new(4805), + FalconFelt::new(11227), + FalconFelt::new(2294), + FalconFelt::new(9542), + FalconFelt::new(4846), + FalconFelt::new(9154), + FalconFelt::new(8577), + FalconFelt::new(9275), + FalconFelt::new(3201), + FalconFelt::new(7203), + FalconFelt::new(10963), + FalconFelt::new(1170), + FalconFelt::new(9970), + FalconFelt::new(955), + FalconFelt::new(11499), + FalconFelt::new(8340), + FalconFelt::new(8993), + FalconFelt::new(2396), + FalconFelt::new(4452), + FalconFelt::new(6915), + FalconFelt::new(2837), + FalconFelt::new(130), + FalconFelt::new(7935), + FalconFelt::new(11336), + FalconFelt::new(3748), + FalconFelt::new(6522), + FalconFelt::new(11462), + FalconFelt::new(5067), + FalconFelt::new(10092), + FalconFelt::new(12171), + FalconFelt::new(9813), + FalconFelt::new(8011), + FalconFelt::new(1673), + FalconFelt::new(5331), + FalconFelt::new(7300), + FalconFelt::new(10908), + FalconFelt::new(9764), + FalconFelt::new(4177), + FalconFelt::new(8705), + FalconFelt::new(480), + FalconFelt::new(9447), + FalconFelt::new(1022), + FalconFelt::new(12280), + FalconFelt::new(5791), + FalconFelt::new(11745), + FalconFelt::new(9821), + FalconFelt::new(11950), + FalconFelt::new(12144), + FalconFelt::new(6747), + FalconFelt::new(8652), + FalconFelt::new(3459), + FalconFelt::new(2731), + FalconFelt::new(8357), + FalconFelt::new(6378), + FalconFelt::new(7399), + FalconFelt::new(10530), + FalconFelt::new(3707), + FalconFelt::new(8595), + FalconFelt::new(5179), + FalconFelt::new(3382), + FalconFelt::new(355), + FalconFelt::new(4231), + FalconFelt::new(2548), + FalconFelt::new(9048), + FalconFelt::new(11560), + FalconFelt::new(3289), + FalconFelt::new(10276), + FalconFelt::new(9005), + FalconFelt::new(9408), + FalconFelt::new(5092), + FalconFelt::new(10200), + FalconFelt::new(6534), + FalconFelt::new(4632), + FalconFelt::new(4388), + FalconFelt::new(1260), + FalconFelt::new(334), + FalconFelt::new(2426), + FalconFelt::new(1428), + FalconFelt::new(10593), + FalconFelt::new(3400), + FalconFelt::new(2399), + FalconFelt::new(5191), + FalconFelt::new(9153), + FalconFelt::new(9273), + FalconFelt::new(243), + FalconFelt::new(3000), + FalconFelt::new(671), + FalconFelt::new(3531), + FalconFelt::new(11813), + FalconFelt::new(3985), + FalconFelt::new(7384), + FalconFelt::new(10111), + FalconFelt::new(10745), + FalconFelt::new(6730), + FalconFelt::new(11869), + FalconFelt::new(9042), + FalconFelt::new(2686), + FalconFelt::new(2969), + FalconFelt::new(3978), + FalconFelt::new(8779), + FalconFelt::new(6957), + FalconFelt::new(9424), + FalconFelt::new(2370), + FalconFelt::new(8241), + FalconFelt::new(10040), + FalconFelt::new(9405), + FalconFelt::new(11136), + FalconFelt::new(3186), + FalconFelt::new(5407), + FalconFelt::new(10163), + FalconFelt::new(1630), + FalconFelt::new(3271), + FalconFelt::new(8232), + FalconFelt::new(10600), + FalconFelt::new(8925), + FalconFelt::new(4414), + FalconFelt::new(2847), + FalconFelt::new(10115), + FalconFelt::new(4372), + FalconFelt::new(9509), + FalconFelt::new(5195), + FalconFelt::new(7394), + FalconFelt::new(10805), + FalconFelt::new(9984), + FalconFelt::new(7247), + FalconFelt::new(4053), + FalconFelt::new(9644), + FalconFelt::new(12176), + FalconFelt::new(4919), + FalconFelt::new(2166), + FalconFelt::new(8374), + FalconFelt::new(12129), + FalconFelt::new(9140), + FalconFelt::new(7852), + FalconFelt::new(3), + FalconFelt::new(1426), + FalconFelt::new(7635), + FalconFelt::new(10512), + FalconFelt::new(1663), + FalconFelt::new(8653), + FalconFelt::new(4938), + FalconFelt::new(2704), + FalconFelt::new(5291), + FalconFelt::new(5277), + FalconFelt::new(1168), + FalconFelt::new(11082), + FalconFelt::new(9041), + FalconFelt::new(2143), + FalconFelt::new(11224), + FalconFelt::new(11885), + FalconFelt::new(4645), + FalconFelt::new(4096), + FalconFelt::new(11796), + FalconFelt::new(5444), + FalconFelt::new(2381), + FalconFelt::new(10911), + FalconFelt::new(1912), + FalconFelt::new(4337), + FalconFelt::new(11854), + FalconFelt::new(4976), + FalconFelt::new(10682), + FalconFelt::new(11414), + FalconFelt::new(8509), + FalconFelt::new(11287), + FalconFelt::new(5011), + FalconFelt::new(8005), + FalconFelt::new(5088), + FalconFelt::new(9852), + FalconFelt::new(8643), + FalconFelt::new(9302), + FalconFelt::new(6267), + FalconFelt::new(2422), + FalconFelt::new(6039), + FalconFelt::new(2187), + FalconFelt::new(2566), + FalconFelt::new(10849), + FalconFelt::new(8526), + FalconFelt::new(9223), + FalconFelt::new(27), + FalconFelt::new(7205), + FalconFelt::new(1632), + FalconFelt::new(7404), + FalconFelt::new(1017), + FalconFelt::new(4143), + FalconFelt::new(7575), + FalconFelt::new(12047), + FalconFelt::new(10752), + FalconFelt::new(8585), + FalconFelt::new(2678), + FalconFelt::new(7270), + FalconFelt::new(11744), + FalconFelt::new(3833), + FalconFelt::new(3778), + FalconFelt::new(11899), + FalconFelt::new(773), + FalconFelt::new(5101), + FalconFelt::new(11222), + FalconFelt::new(9888), + FalconFelt::new(442), + FalconFelt::new(9377), + FalconFelt::new(6591), + FalconFelt::new(354), + FalconFelt::new(7428), + FalconFelt::new(5012), + FalconFelt::new(2481), + FalconFelt::new(1045), + FalconFelt::new(9430), + FalconFelt::new(10302), + FalconFelt::new(10587), + FalconFelt::new(8724), + FalconFelt::new(11635), + FalconFelt::new(7083), + FalconFelt::new(5529), + FalconFelt::new(9090), + FalconFelt::new(12233), + FalconFelt::new(6152), + FalconFelt::new(4948), + FalconFelt::new(400), + FalconFelt::new(1728), + FalconFelt::new(6427), + FalconFelt::new(6136), + FalconFelt::new(6874), + FalconFelt::new(3643), + FalconFelt::new(10930), + FalconFelt::new(5435), + FalconFelt::new(1254), + FalconFelt::new(11316), + FalconFelt::new(10256), + FalconFelt::new(3998), + FalconFelt::new(10367), + FalconFelt::new(8410), + FalconFelt::new(11821), + FalconFelt::new(8301), + FalconFelt::new(11907), + FalconFelt::new(316), + FalconFelt::new(6950), + FalconFelt::new(5446), + FalconFelt::new(6093), + FalconFelt::new(3710), + FalconFelt::new(7822), + FalconFelt::new(4789), + FalconFelt::new(7540), + FalconFelt::new(5537), + FalconFelt::new(3789), + FalconFelt::new(147), + FalconFelt::new(5456), + FalconFelt::new(7840), + FalconFelt::new(11239), + FalconFelt::new(7753), + FalconFelt::new(5445), + FalconFelt::new(3860), + FalconFelt::new(9606), + FalconFelt::new(1190), + FalconFelt::new(8471), + FalconFelt::new(6118), + FalconFelt::new(5925), + FalconFelt::new(1018), + FalconFelt::new(8775), + FalconFelt::new(1041), + FalconFelt::new(1973), + FalconFelt::new(5574), + FalconFelt::new(11011), + FalconFelt::new(2344), + FalconFelt::new(4075), + FalconFelt::new(5315), + FalconFelt::new(4324), + FalconFelt::new(4916), + FalconFelt::new(10120), + FalconFelt::new(11767), + FalconFelt::new(7210), + FalconFelt::new(9027), + FalconFelt::new(6281), + FalconFelt::new(11404), + FalconFelt::new(7280), + FalconFelt::new(1956), + FalconFelt::new(11286), + FalconFelt::new(3532), + FalconFelt::new(12048), + FalconFelt::new(12231), + FalconFelt::new(1105), + FalconFelt::new(12147), + FalconFelt::new(5681), + FalconFelt::new(8812), + FalconFelt::new(8851), + FalconFelt::new(2844), + FalconFelt::new(975), + FalconFelt::new(4212), + FalconFelt::new(8687), + FalconFelt::new(6068), + FalconFelt::new(421), + FalconFelt::new(8209), + FalconFelt::new(3600), + FalconFelt::new(3263), + FalconFelt::new(7665), + FalconFelt::new(6077), + FalconFelt::new(4782), + FalconFelt::new(6403), + FalconFelt::new(9260), + FalconFelt::new(5594), + FalconFelt::new(8076), + FalconFelt::new(11785), + FalconFelt::new(605), + FalconFelt::new(9987), + FalconFelt::new(5468), + FalconFelt::new(1010), + FalconFelt::new(787), + FalconFelt::new(8807), + FalconFelt::new(5241), + FalconFelt::new(9369), + FalconFelt::new(9162), + FalconFelt::new(8120), + FalconFelt::new(5057), + FalconFelt::new(7591), + FalconFelt::new(3445), + FalconFelt::new(7509), + FalconFelt::new(2049), + FalconFelt::new(7377), + FalconFelt::new(10968), + FalconFelt::new(192), + FalconFelt::new(431), + FalconFelt::new(10710), + FalconFelt::new(2505), + FalconFelt::new(5906), + FalconFelt::new(12138), + FalconFelt::new(10162), + FalconFelt::new(8332), + FalconFelt::new(9450), + FalconFelt::new(6415), + FalconFelt::new(677), + FalconFelt::new(6234), + FalconFelt::new(3336), + FalconFelt::new(12237), + FalconFelt::new(9115), + FalconFelt::new(1323), + FalconFelt::new(2766), + FalconFelt::new(3150), + FalconFelt::new(1319), + FalconFelt::new(8243), + FalconFelt::new(709), + FalconFelt::new(8049), + FalconFelt::new(8719), + FalconFelt::new(11454), + FalconFelt::new(6224), + FalconFelt::new(922), + FalconFelt::new(11848), + FalconFelt::new(8210), + FalconFelt::new(1058), + FalconFelt::new(1958), + FalconFelt::new(7967), + FalconFelt::new(10211), + FalconFelt::new(11177), + FalconFelt::new(64), + FalconFelt::new(8633), + FalconFelt::new(11606), + FalconFelt::new(9830), + FalconFelt::new(6507), + FalconFelt::new(1566), + FalconFelt::new(2948), + FalconFelt::new(9786), + FalconFelt::new(6370), + FalconFelt::new(7856), + FalconFelt::new(3834), + FalconFelt::new(5257), + FalconFelt::new(10542), + FalconFelt::new(9166), + FalconFelt::new(9235), + FalconFelt::new(5486), + FalconFelt::new(1404), + FalconFelt::new(11964), + FalconFelt::new(1146), + FalconFelt::new(11341), + FalconFelt::new(3728), + FalconFelt::new(8240), + FalconFelt::new(6299), + FalconFelt::new(1159), + FalconFelt::new(6099), + FalconFelt::new(295), + FalconFelt::new(5766), + FalconFelt::new(11637), + FalconFelt::new(8527), + FalconFelt::new(2919), + FalconFelt::new(8273), + FalconFelt::new(8212), + FalconFelt::new(3329), + FalconFelt::new(7991), + FalconFelt::new(9597), + FalconFelt::new(168), + FalconFelt::new(10695), + FalconFelt::new(1962), + FalconFelt::new(5106), + FalconFelt::new(6328), + FalconFelt::new(5297), + FalconFelt::new(6170), + FalconFelt::new(3956), + FalconFelt::new(1360), + FalconFelt::new(11089), + FalconFelt::new(7105), + FalconFelt::new(9734), + FalconFelt::new(6167), + FalconFelt::new(9407), + FalconFelt::new(1805), + FalconFelt::new(1954), + FalconFelt::new(2051), + FalconFelt::new(6142), + FalconFelt::new(2447), + FalconFelt::new(3963), + FalconFelt::new(11713), + FalconFelt::new(8855), + FalconFelt::new(8760), + FalconFelt::new(9381), + FalconFelt::new(218), + FalconFelt::new(9928), + FalconFelt::new(10446), + FalconFelt::new(9259), + FalconFelt::new(4115), + FalconFelt::new(5333), + FalconFelt::new(10258), + FalconFelt::new(5876), + FalconFelt::new(2281), + FalconFelt::new(156), + FalconFelt::new(9522), + FalconFelt::new(8320), + FalconFelt::new(3991), + FalconFelt::new(453), + FalconFelt::new(6381), + FalconFelt::new(11871), + FalconFelt::new(8517), + FalconFelt::new(4774), + FalconFelt::new(6860), + FalconFelt::new(4737), + FalconFelt::new(1293), + FalconFelt::new(10232), + FalconFelt::new(5369), + FalconFelt::new(9087), + FalconFelt::new(7796), + FalconFelt::new(350), + FalconFelt::new(1512), + FalconFelt::new(10474), + FalconFelt::new(6906), + FalconFelt::new(1489), + FalconFelt::new(2500), + FalconFelt::new(1583), + FalconFelt::new(6347), + FalconFelt::new(11026), + FalconFelt::new(12240), + FalconFelt::new(6374), + FalconFelt::new(1483), + FalconFelt::new(3009), + FalconFelt::new(1693), + FalconFelt::new(723), + FalconFelt::new(174), + FalconFelt::new(2738), + FalconFelt::new(6421), + FalconFelt::new(2655), + FalconFelt::new(6554), + FalconFelt::new(10314), + FalconFelt::new(3757), + FalconFelt::new(9364), + FalconFelt::new(11942), + FalconFelt::new(7535), + FalconFelt::new(10431), + FalconFelt::new(426), + FalconFelt::new(3315), +]; + +const FELT_BITREVERSED_POWERS_INVERSE: [FalconFelt; 512] = [ + FalconFelt::new(1), + FalconFelt::new(10810), + FalconFelt::new(7143), + FalconFelt::new(4043), + FalconFelt::new(10984), + FalconFelt::new(722), + FalconFelt::new(5736), + FalconFelt::new(8155), + FalconFelt::new(8747), + FalconFelt::new(3504), + FalconFelt::new(2545), + FalconFelt::new(8668), + FalconFelt::new(1646), + FalconFelt::new(11077), + FalconFelt::new(9094), + FalconFelt::new(6429), + FalconFelt::new(9650), + FalconFelt::new(7468), + FalconFelt::new(949), + FalconFelt::new(9664), + FalconFelt::new(2975), + FalconFelt::new(11726), + FalconFelt::new(2744), + FalconFelt::new(9283), + FalconFelt::new(7698), + FalconFelt::new(6561), + FalconFelt::new(5828), + FalconFelt::new(7266), + FalconFelt::new(6512), + FalconFelt::new(3328), + FalconFelt::new(1351), + FalconFelt::new(4978), + FalconFelt::new(790), + FalconFelt::new(11334), + FalconFelt::new(2319), + FalconFelt::new(11119), + FalconFelt::new(1326), + FalconFelt::new(5086), + FalconFelt::new(9088), + FalconFelt::new(3014), + FalconFelt::new(3712), + FalconFelt::new(3135), + FalconFelt::new(7443), + FalconFelt::new(2747), + FalconFelt::new(9995), + FalconFelt::new(1062), + FalconFelt::new(7484), + FalconFelt::new(3553), + FalconFelt::new(4320), + FalconFelt::new(1000), + FalconFelt::new(81), + FalconFelt::new(3091), + FalconFelt::new(3051), + FalconFelt::new(9923), + FalconFelt::new(4896), + FalconFelt::new(9326), + FalconFelt::new(10654), + FalconFelt::new(9521), + FalconFelt::new(8034), + FalconFelt::new(1177), + FalconFelt::new(7678), + FalconFelt::new(11563), + FalconFelt::new(10436), + FalconFelt::new(140), + FalconFelt::new(1696), + FalconFelt::new(10861), + FalconFelt::new(9863), + FalconFelt::new(11955), + FalconFelt::new(11029), + FalconFelt::new(7901), + FalconFelt::new(7657), + FalconFelt::new(5755), + FalconFelt::new(2089), + FalconFelt::new(7197), + FalconFelt::new(2881), + FalconFelt::new(3284), + FalconFelt::new(2013), + FalconFelt::new(9000), + FalconFelt::new(729), + FalconFelt::new(3241), + FalconFelt::new(9741), + FalconFelt::new(8058), + FalconFelt::new(11934), + FalconFelt::new(8907), + FalconFelt::new(7110), + FalconFelt::new(3694), + FalconFelt::new(8582), + FalconFelt::new(1759), + FalconFelt::new(4890), + FalconFelt::new(5911), + FalconFelt::new(3932), + FalconFelt::new(9558), + FalconFelt::new(8830), + FalconFelt::new(3637), + FalconFelt::new(5542), + FalconFelt::new(145), + FalconFelt::new(339), + FalconFelt::new(2468), + FalconFelt::new(544), + FalconFelt::new(6498), + FalconFelt::new(9), + FalconFelt::new(11267), + FalconFelt::new(2842), + FalconFelt::new(11809), + FalconFelt::new(3584), + FalconFelt::new(8112), + FalconFelt::new(2525), + FalconFelt::new(1381), + FalconFelt::new(4989), + FalconFelt::new(6958), + FalconFelt::new(10616), + FalconFelt::new(4278), + FalconFelt::new(2476), + FalconFelt::new(118), + FalconFelt::new(2197), + FalconFelt::new(7222), + FalconFelt::new(827), + FalconFelt::new(5767), + FalconFelt::new(8541), + FalconFelt::new(953), + FalconFelt::new(4354), + FalconFelt::new(12159), + FalconFelt::new(9452), + FalconFelt::new(5374), + FalconFelt::new(7837), + FalconFelt::new(9893), + FalconFelt::new(3296), + FalconFelt::new(3949), + FalconFelt::new(2859), + FalconFelt::new(11244), + FalconFelt::new(9808), + FalconFelt::new(7277), + FalconFelt::new(4861), + FalconFelt::new(11935), + FalconFelt::new(5698), + FalconFelt::new(2912), + FalconFelt::new(11847), + FalconFelt::new(2401), + FalconFelt::new(1067), + FalconFelt::new(7188), + FalconFelt::new(11516), + FalconFelt::new(390), + FalconFelt::new(8511), + FalconFelt::new(8456), + FalconFelt::new(545), + FalconFelt::new(5019), + FalconFelt::new(9611), + FalconFelt::new(3704), + FalconFelt::new(1537), + FalconFelt::new(242), + FalconFelt::new(4714), + FalconFelt::new(8146), + FalconFelt::new(11272), + FalconFelt::new(4885), + FalconFelt::new(10657), + FalconFelt::new(5084), + FalconFelt::new(12262), + FalconFelt::new(3066), + FalconFelt::new(3763), + FalconFelt::new(1440), + FalconFelt::new(9723), + FalconFelt::new(10102), + FalconFelt::new(6250), + FalconFelt::new(9867), + FalconFelt::new(6022), + FalconFelt::new(2987), + FalconFelt::new(3646), + FalconFelt::new(2437), + FalconFelt::new(7201), + FalconFelt::new(4284), + FalconFelt::new(7278), + FalconFelt::new(1002), + FalconFelt::new(3780), + FalconFelt::new(875), + FalconFelt::new(1607), + FalconFelt::new(7313), + FalconFelt::new(435), + FalconFelt::new(7952), + FalconFelt::new(10377), + FalconFelt::new(1378), + FalconFelt::new(9908), + FalconFelt::new(6845), + FalconFelt::new(493), + FalconFelt::new(8193), + FalconFelt::new(7644), + FalconFelt::new(404), + FalconFelt::new(1065), + FalconFelt::new(10146), + FalconFelt::new(3248), + FalconFelt::new(1207), + FalconFelt::new(11121), + FalconFelt::new(7012), + FalconFelt::new(6998), + FalconFelt::new(9585), + FalconFelt::new(7351), + FalconFelt::new(3636), + FalconFelt::new(10626), + FalconFelt::new(1777), + FalconFelt::new(4654), + FalconFelt::new(10863), + FalconFelt::new(12286), + FalconFelt::new(4437), + FalconFelt::new(3149), + FalconFelt::new(160), + FalconFelt::new(3915), + FalconFelt::new(10123), + FalconFelt::new(7370), + FalconFelt::new(113), + FalconFelt::new(2645), + FalconFelt::new(8236), + FalconFelt::new(5042), + FalconFelt::new(2305), + FalconFelt::new(1484), + FalconFelt::new(4895), + FalconFelt::new(7094), + FalconFelt::new(2780), + FalconFelt::new(7917), + FalconFelt::new(2174), + FalconFelt::new(9442), + FalconFelt::new(7875), + FalconFelt::new(3364), + FalconFelt::new(1689), + FalconFelt::new(4057), + FalconFelt::new(9018), + FalconFelt::new(10659), + FalconFelt::new(2126), + FalconFelt::new(6882), + FalconFelt::new(9103), + FalconFelt::new(1153), + FalconFelt::new(2884), + FalconFelt::new(2249), + FalconFelt::new(4048), + FalconFelt::new(9919), + FalconFelt::new(2865), + FalconFelt::new(5332), + FalconFelt::new(3510), + FalconFelt::new(8311), + FalconFelt::new(9320), + FalconFelt::new(9603), + FalconFelt::new(3247), + FalconFelt::new(420), + FalconFelt::new(5559), + FalconFelt::new(1544), + FalconFelt::new(2178), + FalconFelt::new(4905), + FalconFelt::new(8304), + FalconFelt::new(476), + FalconFelt::new(8758), + FalconFelt::new(11618), + FalconFelt::new(9289), + FalconFelt::new(12046), + FalconFelt::new(3016), + FalconFelt::new(3136), + FalconFelt::new(7098), + FalconFelt::new(9890), + FalconFelt::new(8889), + FalconFelt::new(8974), + FalconFelt::new(11863), + FalconFelt::new(1858), + FalconFelt::new(4754), + FalconFelt::new(347), + FalconFelt::new(2925), + FalconFelt::new(8532), + FalconFelt::new(1975), + FalconFelt::new(5735), + FalconFelt::new(9634), + FalconFelt::new(5868), + FalconFelt::new(9551), + FalconFelt::new(12115), + FalconFelt::new(11566), + FalconFelt::new(10596), + FalconFelt::new(9280), + FalconFelt::new(10806), + FalconFelt::new(5915), + FalconFelt::new(49), + FalconFelt::new(1263), + FalconFelt::new(5942), + FalconFelt::new(10706), + FalconFelt::new(9789), + FalconFelt::new(10800), + FalconFelt::new(5383), + FalconFelt::new(1815), + FalconFelt::new(10777), + FalconFelt::new(11939), + FalconFelt::new(4493), + FalconFelt::new(3202), + FalconFelt::new(6920), + FalconFelt::new(2057), + FalconFelt::new(10996), + FalconFelt::new(7552), + FalconFelt::new(5429), + FalconFelt::new(7515), + FalconFelt::new(3772), + FalconFelt::new(418), + FalconFelt::new(5908), + FalconFelt::new(11836), + FalconFelt::new(8298), + FalconFelt::new(3969), + FalconFelt::new(2767), + FalconFelt::new(12133), + FalconFelt::new(10008), + FalconFelt::new(6413), + FalconFelt::new(2031), + FalconFelt::new(6956), + FalconFelt::new(8174), + FalconFelt::new(3030), + FalconFelt::new(1843), + FalconFelt::new(2361), + FalconFelt::new(12071), + FalconFelt::new(2908), + FalconFelt::new(3529), + FalconFelt::new(3434), + FalconFelt::new(576), + FalconFelt::new(8326), + FalconFelt::new(9842), + FalconFelt::new(6147), + FalconFelt::new(10238), + FalconFelt::new(10335), + FalconFelt::new(10484), + FalconFelt::new(2882), + FalconFelt::new(6122), + FalconFelt::new(2555), + FalconFelt::new(5184), + FalconFelt::new(1200), + FalconFelt::new(10929), + FalconFelt::new(8333), + FalconFelt::new(6119), + FalconFelt::new(6992), + FalconFelt::new(5961), + FalconFelt::new(7183), + FalconFelt::new(10327), + FalconFelt::new(1594), + FalconFelt::new(12121), + FalconFelt::new(2692), + FalconFelt::new(4298), + FalconFelt::new(8960), + FalconFelt::new(4077), + FalconFelt::new(4016), + FalconFelt::new(9370), + FalconFelt::new(3762), + FalconFelt::new(652), + FalconFelt::new(6523), + FalconFelt::new(11994), + FalconFelt::new(6190), + FalconFelt::new(11130), + FalconFelt::new(5990), + FalconFelt::new(4049), + FalconFelt::new(8561), + FalconFelt::new(948), + FalconFelt::new(11143), + FalconFelt::new(325), + FalconFelt::new(10885), + FalconFelt::new(6803), + FalconFelt::new(3054), + FalconFelt::new(3123), + FalconFelt::new(1747), + FalconFelt::new(7032), + FalconFelt::new(8455), + FalconFelt::new(4433), + FalconFelt::new(5919), + FalconFelt::new(2503), + FalconFelt::new(9341), + FalconFelt::new(10723), + FalconFelt::new(5782), + FalconFelt::new(2459), + FalconFelt::new(683), + FalconFelt::new(3656), + FalconFelt::new(12225), + FalconFelt::new(1112), + FalconFelt::new(2078), + FalconFelt::new(4322), + FalconFelt::new(10331), + FalconFelt::new(11231), + FalconFelt::new(4079), + FalconFelt::new(441), + FalconFelt::new(11367), + FalconFelt::new(6065), + FalconFelt::new(835), + FalconFelt::new(3570), + FalconFelt::new(4240), + FalconFelt::new(11580), + FalconFelt::new(4046), + FalconFelt::new(10970), + FalconFelt::new(9139), + FalconFelt::new(9523), + FalconFelt::new(10966), + FalconFelt::new(3174), + FalconFelt::new(52), + FalconFelt::new(8953), + FalconFelt::new(6055), + FalconFelt::new(11612), + FalconFelt::new(5874), + FalconFelt::new(2839), + FalconFelt::new(3957), + FalconFelt::new(2127), + FalconFelt::new(151), + FalconFelt::new(6383), + FalconFelt::new(9784), + FalconFelt::new(1579), + FalconFelt::new(11858), + FalconFelt::new(12097), + FalconFelt::new(1321), + FalconFelt::new(4912), + FalconFelt::new(10240), + FalconFelt::new(4780), + FalconFelt::new(8844), + FalconFelt::new(4698), + FalconFelt::new(7232), + FalconFelt::new(4169), + FalconFelt::new(3127), + FalconFelt::new(2920), + FalconFelt::new(7048), + FalconFelt::new(3482), + FalconFelt::new(11502), + FalconFelt::new(11279), + FalconFelt::new(6821), + FalconFelt::new(2302), + FalconFelt::new(11684), + FalconFelt::new(504), + FalconFelt::new(4213), + FalconFelt::new(6695), + FalconFelt::new(3029), + FalconFelt::new(5886), + FalconFelt::new(7507), + FalconFelt::new(6212), + FalconFelt::new(4624), + FalconFelt::new(9026), + FalconFelt::new(8689), + FalconFelt::new(4080), + FalconFelt::new(11868), + FalconFelt::new(6221), + FalconFelt::new(3602), + FalconFelt::new(8077), + FalconFelt::new(11314), + FalconFelt::new(9445), + FalconFelt::new(3438), + FalconFelt::new(3477), + FalconFelt::new(6608), + FalconFelt::new(142), + FalconFelt::new(11184), + FalconFelt::new(58), + FalconFelt::new(241), + FalconFelt::new(8757), + FalconFelt::new(1003), + FalconFelt::new(10333), + FalconFelt::new(5009), + FalconFelt::new(885), + FalconFelt::new(6008), + FalconFelt::new(3262), + FalconFelt::new(5079), + FalconFelt::new(522), + FalconFelt::new(2169), + FalconFelt::new(7373), + FalconFelt::new(7965), + FalconFelt::new(6974), + FalconFelt::new(8214), + FalconFelt::new(9945), + FalconFelt::new(1278), + FalconFelt::new(6715), + FalconFelt::new(10316), + FalconFelt::new(11248), + FalconFelt::new(3514), + FalconFelt::new(11271), + FalconFelt::new(6364), + FalconFelt::new(6171), + FalconFelt::new(3818), + FalconFelt::new(11099), + FalconFelt::new(2683), + FalconFelt::new(8429), + FalconFelt::new(6844), + FalconFelt::new(4536), + FalconFelt::new(1050), + FalconFelt::new(4449), + FalconFelt::new(6833), + FalconFelt::new(12142), + FalconFelt::new(8500), + FalconFelt::new(6752), + FalconFelt::new(4749), + FalconFelt::new(7500), + FalconFelt::new(4467), + FalconFelt::new(8579), + FalconFelt::new(6196), + FalconFelt::new(6843), + FalconFelt::new(5339), + FalconFelt::new(11973), + FalconFelt::new(382), + FalconFelt::new(3988), + FalconFelt::new(468), + FalconFelt::new(3879), + FalconFelt::new(1922), + FalconFelt::new(8291), + FalconFelt::new(2033), + FalconFelt::new(973), + FalconFelt::new(11035), + FalconFelt::new(6854), + FalconFelt::new(1359), + FalconFelt::new(8646), + FalconFelt::new(5415), + FalconFelt::new(6153), + FalconFelt::new(5862), + FalconFelt::new(10561), + FalconFelt::new(11889), + FalconFelt::new(7341), + FalconFelt::new(6137), + FalconFelt::new(56), + FalconFelt::new(3199), + FalconFelt::new(6760), + FalconFelt::new(5206), + FalconFelt::new(654), + FalconFelt::new(3565), + FalconFelt::new(1702), + FalconFelt::new(1987), +]; + +const FELT_NINV_1: FalconFelt = FalconFelt::new(1); +const FELT_NINV_2: FalconFelt = FalconFelt::new(6145); +const FELT_NINV_4: FalconFelt = FalconFelt::new(9217); +const FELT_NINV_8: FalconFelt = FalconFelt::new(10753); +const FELT_NINV_16: FalconFelt = FalconFelt::new(11521); +const FELT_NINV_32: FalconFelt = FalconFelt::new(11905); +const FELT_NINV_64: FalconFelt = FalconFelt::new(12097); +const FELT_NINV_128: FalconFelt = FalconFelt::new(12193); +const FELT_NINV_256: FalconFelt = FalconFelt::new(12241); +const FELT_NINV_512: FalconFelt = FalconFelt::new(12265); diff --git a/src/dsa/rpo_falcon512/math/field.rs b/src/dsa/rpo_falcon512/math/field.rs new file mode 100644 index 00000000..c05b6a74 --- /dev/null +++ b/src/dsa/rpo_falcon512/math/field.rs @@ -0,0 +1,172 @@ +use super::{fft::CyclotomicFourier, Inverse, MODULUS}; +use alloc::string::String; +use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; +use num::{One, Zero}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub struct FalconFelt(u32); + +impl FalconFelt { + pub const fn new(value: i16) -> Self { + let gtz_bool = value >= 0; + let gtz_int = gtz_bool as i16; + let gtz_sign = gtz_int - ((!gtz_bool) as i16); + let reduced = gtz_sign * (gtz_sign * value) % MODULUS; + let canonical_representative = (reduced + MODULUS * (1 - gtz_int)) as u32; + FalconFelt(canonical_representative) + } + + pub const fn value(&self) -> i16 { + self.0 as i16 + } + + pub fn balanced_value(&self) -> i16 { + let value = self.value(); + let g = (value > ((MODULUS) / 2)) as i16; + value - (MODULUS) * g + } + + pub const fn multiply(&self, other: Self) -> Self { + FalconFelt((self.0 * other.0) % MODULUS as u32) + } +} + +impl Add for FalconFelt { + type Output = Self; + + #[allow(clippy::suspicious_arithmetic_impl)] + fn add(self, rhs: Self) -> Self::Output { + let (s, _) = self.0.overflowing_add(rhs.0); + let (d, n) = s.overflowing_sub(MODULUS as u32); + let (r, _) = d.overflowing_add(MODULUS as u32 * (n as u32)); + FalconFelt(r) + } +} + +impl AddAssign for FalconFelt { + fn add_assign(&mut self, rhs: Self) { + *self = *self + rhs; + } +} + +impl Sub for FalconFelt { + type Output = Self; + + fn sub(self, rhs: Self) -> Self::Output { + self + -rhs + } +} + +impl SubAssign for FalconFelt { + fn sub_assign(&mut self, rhs: Self) { + *self = *self - rhs; + } +} + +impl Neg for FalconFelt { + type Output = FalconFelt; + + fn neg(self) -> Self::Output { + let is_nonzero = self.0 != 0; + let r = MODULUS as u32 - self.0; + FalconFelt(r * (is_nonzero as u32)) + } +} + +impl Mul for FalconFelt { + fn mul(self, rhs: Self) -> Self::Output { + FalconFelt((self.0 * rhs.0) % MODULUS as u32) + } + + type Output = Self; +} + +impl MulAssign for FalconFelt { + fn mul_assign(&mut self, rhs: Self) { + *self = *self * rhs; + } +} + +impl Div for FalconFelt { + type Output = FalconFelt; + + #[allow(clippy::suspicious_arithmetic_impl)] + fn div(self, rhs: Self) -> Self::Output { + self * rhs.inverse_or_zero() + } +} + +impl DivAssign for FalconFelt { + fn div_assign(&mut self, rhs: Self) { + *self = *self / rhs + } +} + +impl Zero for FalconFelt { + fn zero() -> Self { + FalconFelt::new(0) + } + + fn is_zero(&self) -> bool { + self.0 == 0 + } +} + +impl One for FalconFelt { + fn one() -> Self { + FalconFelt::new(1) + } +} + +impl Inverse for FalconFelt { + fn inverse_or_zero(self) -> Self { + // q-2 = 0b10 11 11 11 11 11 11 + let two = self.multiply(self); + let three = two.multiply(self); + let six = three.multiply(three); + let twelve = six.multiply(six); + let fifteen = twelve.multiply(three); + let thirty = fifteen.multiply(fifteen); + let sixty = thirty.multiply(thirty); + let sixty_three = sixty.multiply(three); + + let sixty_three_sq = sixty_three.multiply(sixty_three); + let sixty_three_qu = sixty_three_sq.multiply(sixty_three_sq); + let sixty_three_oc = sixty_three_qu.multiply(sixty_three_qu); + let sixty_three_hx = sixty_three_oc.multiply(sixty_three_oc); + let sixty_three_tt = sixty_three_hx.multiply(sixty_three_hx); + let sixty_three_sf = sixty_three_tt.multiply(sixty_three_tt); + + let all_ones = sixty_three_sf.multiply(sixty_three); + let two_e_twelve = all_ones.multiply(self); + let two_e_thirteen = two_e_twelve.multiply(two_e_twelve); + + two_e_thirteen.multiply(all_ones) + } +} + +impl CyclotomicFourier for FalconFelt { + fn primitive_root_of_unity(n: usize) -> Self { + let log2n = n.ilog2(); + assert!(log2n <= 12); + // and 1331 is a twelfth root of unity + let mut a = FalconFelt::new(1331); + let num_squarings = 12 - n.ilog2(); + for _ in 0..num_squarings { + a *= a; + } + a + } +} + +impl TryFrom for FalconFelt { + type Error = String; + + fn try_from(value: u32) -> Result { + if value >= MODULUS as u32 { + Err(format!("value {value} is greater than or equal to the field modulus {MODULUS}")) + } else { + Ok(FalconFelt::new(value as i16)) + } + } +} diff --git a/src/dsa/rpo_falcon512/math/mod.rs b/src/dsa/rpo_falcon512/math/mod.rs new file mode 100644 index 00000000..8d0ed17f --- /dev/null +++ b/src/dsa/rpo_falcon512/math/mod.rs @@ -0,0 +1,320 @@ +//! Contains different structs and methods related to the Falcon DSA. +//! +//! It uses and acknowledges the work in: +//! +//! 1. The [reference](https://falcon-sign.info/impl/README.txt.html) implementation by Thomas Pornin. +//! 2. The [Rust](https://github.com/aszepieniec/falcon-rust) implementation by Alan Szepieniec. +use super::MODULUS; +use alloc::{string::String, vec::Vec}; +use core::ops::MulAssign; +use num::{BigInt, FromPrimitive, One, Zero}; +use num_complex::Complex64; +use rand::Rng; + +#[cfg(not(feature = "std"))] +use num::Float; + +mod fft; +pub use fft::{CyclotomicFourier, FastFft}; + +mod field; +pub use field::FalconFelt; + +mod ffsampling; +pub use ffsampling::{ffldl, ffsampling, gram, normalize_tree, LdlTree}; + +mod samplerz; +use self::samplerz::sampler_z; + +mod polynomial; +pub use polynomial::Polynomial; + +pub trait Inverse: Copy + Zero + MulAssign + One { + /// Gets the inverse of a, or zero if it is zero. + fn inverse_or_zero(self) -> Self; + + /// Gets the inverses of a batch of elements, and skip over any that are zero. + fn batch_inverse_or_zero(batch: &[Self]) -> Vec { + let mut acc = Self::one(); + let mut rp: Vec = Vec::with_capacity(batch.len()); + for batch_item in batch { + if !batch_item.is_zero() { + rp.push(acc); + acc = *batch_item * acc; + } else { + rp.push(Self::zero()); + } + } + let mut inv = Self::inverse_or_zero(acc); + for i in (0..batch.len()).rev() { + if !batch[i].is_zero() { + rp[i] *= inv; + inv *= batch[i]; + } + } + rp + } +} + +impl Inverse for Complex64 { + fn inverse_or_zero(self) -> Self { + let modulus = self.re * self.re + self.im * self.im; + Complex64::new(self.re / modulus, -self.im / modulus) + } + fn batch_inverse_or_zero(batch: &[Self]) -> Vec { + batch.iter().map(|&c| Complex64::new(1.0, 0.0) / c).collect() + } +} + +impl Inverse for f64 { + fn inverse_or_zero(self) -> Self { + 1.0 / self + } + fn batch_inverse_or_zero(batch: &[Self]) -> Vec { + batch.iter().map(|&c| 1.0 / c).collect() + } +} + +/// Samples 4 small polynomials f, g, F, G such that f * G - g * F = q mod (X^n + 1). +/// Algorithm 5 (NTRUgen) of the documentation [1, p.34]. +/// +/// [1]: https://falcon-sign.info/falcon.pdf +pub(crate) fn ntru_gen(n: usize, rng: &mut R) -> [Polynomial; 4] { + loop { + let f = gen_poly(n, rng); + let g = gen_poly(n, rng); + let f_ntt = f.map(|&i| FalconFelt::new(i)).fft(); + if f_ntt.coefficients.iter().any(|e| e.is_zero()) { + continue; + } + let gamma = gram_schmidt_norm_squared(&f, &g); + if gamma > 1.3689f64 * (MODULUS as f64) { + continue; + } + + if let Some((capital_f, capital_g)) = + ntru_solve(&f.map(|&i| i.into()), &g.map(|&i| i.into())) + { + return [ + f, + g, + capital_f.map(|i| i.try_into().unwrap()), + capital_g.map(|i| i.try_into().unwrap()), + ]; + } + } +} + +/// Solves the NTRU equation. Given f, g in ZZ[X], find F, G in ZZ[X] such that: +/// +/// f G - g F = q mod (X^n + 1) +/// +/// Algorithm 6 of the specification [1, p.35]. +/// +/// [1]: https://falcon-sign.info/falcon.pdf +fn ntru_solve( + f: &Polynomial, + g: &Polynomial, +) -> Option<(Polynomial, Polynomial)> { + let n = f.coefficients.len(); + if n == 1 { + let (gcd, u, v) = xgcd(&f.coefficients[0], &g.coefficients[0]); + if gcd != BigInt::one() { + return None; + } + return Some(( + (Polynomial::new(vec![-v * BigInt::from_u32(MODULUS as u32).unwrap()])), + Polynomial::new(vec![u * BigInt::from_u32(MODULUS as u32).unwrap()]), + )); + } + + let f_prime = f.field_norm(); + let g_prime = g.field_norm(); + + let (capital_f_prime, capital_g_prime) = ntru_solve(&f_prime, &g_prime)?; + let capital_f_prime_xsq = capital_f_prime.lift_next_cyclotomic(); + let capital_g_prime_xsq = capital_g_prime.lift_next_cyclotomic(); + + let f_minx = f.galois_adjoint(); + let g_minx = g.galois_adjoint(); + + let mut capital_f = (capital_f_prime_xsq.karatsuba(&g_minx)).reduce_by_cyclotomic(n); + let mut capital_g = (capital_g_prime_xsq.karatsuba(&f_minx)).reduce_by_cyclotomic(n); + + match babai_reduce(f, g, &mut capital_f, &mut capital_g) { + Ok(_) => Some((capital_f, capital_g)), + Err(_e) => { + #[cfg(test)] + { + panic!("{}", _e); + } + #[cfg(not(test))] + { + None + } + } + } +} + +/// Generates a polynomial of degree at most n-1 whose coefficients are distributed according +/// to a discrete Gaussian with mu = 0 and sigma = 1.17 * sqrt(Q / (2n)). +fn gen_poly(n: usize, rng: &mut R) -> Polynomial { + let mu = 0.0; + let sigma_star = 1.43300980528773; + Polynomial { + coefficients: (0..4096) + .map(|_| sampler_z(mu, sigma_star, sigma_star - 0.001, rng)) + .collect::>() + .chunks(4096 / n) + .map(|ch| ch.iter().sum()) + .collect(), + } +} + +/// Computes the Gram-Schmidt norm of B = [[g, -f], [G, -F]] from f and g. +/// Corresponds to line 9 in algorithm 5 of the spec [1, p.34] +/// +/// [1]: https://falcon-sign.info/falcon.pdf +fn gram_schmidt_norm_squared(f: &Polynomial, g: &Polynomial) -> f64 { + let n = f.coefficients.len(); + let norm_f_squared = f.l2_norm_squared(); + let norm_g_squared = g.l2_norm_squared(); + let gamma1 = norm_f_squared + norm_g_squared; + + let f_fft = f.map(|i| Complex64::new(*i as f64, 0.0)).fft(); + let g_fft = g.map(|i| Complex64::new(*i as f64, 0.0)).fft(); + let f_adj_fft = f_fft.map(|c| c.conj()); + let g_adj_fft = g_fft.map(|c| c.conj()); + let ffgg_fft = f_fft.hadamard_mul(&f_adj_fft) + g_fft.hadamard_mul(&g_adj_fft); + let ffgg_fft_inverse = ffgg_fft.hadamard_inv(); + let qf_over_ffgg_fft = f_adj_fft.map(|c| c * (MODULUS as f64)).hadamard_mul(&ffgg_fft_inverse); + let qg_over_ffgg_fft = g_adj_fft.map(|c| c * (MODULUS as f64)).hadamard_mul(&ffgg_fft_inverse); + let norm_f_over_ffgg_squared = + qf_over_ffgg_fft.coefficients.iter().map(|c| (c * c.conj()).re).sum::() / (n as f64); + let norm_g_over_ffgg_squared = + qg_over_ffgg_fft.coefficients.iter().map(|c| (c * c.conj()).re).sum::() / (n as f64); + + let gamma2 = norm_f_over_ffgg_squared + norm_g_over_ffgg_squared; + + f64::max(gamma1, gamma2) +} + +/// Reduces the vector (F,G) relative to (f,g). This method follows the python implementation [1]. +/// Note that this algorithm can end up in an infinite loop. (It's one of the things the author +/// would like to fix.) When this happens, control returns an error (hence the return type) and +/// generates another keypair with fresh randomness. +/// +/// Algorithm 7 in the spec [2, p.35] +/// +/// [1]: https://github.com/tprest/falcon.py +/// +/// [2]: https://falcon-sign.info/falcon.pdf +fn babai_reduce( + f: &Polynomial, + g: &Polynomial, + capital_f: &mut Polynomial, + capital_g: &mut Polynomial, +) -> Result<(), String> { + let bitsize = |bi: &BigInt| (bi.bits() + 7) & (u64::MAX ^ 7); + let n = f.coefficients.len(); + let size = [ + f.map(bitsize).fold(0, |a, &b| u64::max(a, b)), + g.map(bitsize).fold(0, |a, &b| u64::max(a, b)), + 53, + ] + .into_iter() + .max() + .unwrap(); + let shift = (size as i64) - 53; + let f_adjusted = f + .map(|bi| Complex64::new(i64::try_from(bi >> shift).unwrap() as f64, 0.0)) + .fft(); + let g_adjusted = g + .map(|bi| Complex64::new(i64::try_from(bi >> shift).unwrap() as f64, 0.0)) + .fft(); + + let f_star_adjusted = f_adjusted.map(|c| c.conj()); + let g_star_adjusted = g_adjusted.map(|c| c.conj()); + let denominator_fft = + f_adjusted.hadamard_mul(&f_star_adjusted) + g_adjusted.hadamard_mul(&g_star_adjusted); + + let mut counter = 0; + loop { + let capital_size = [ + capital_f.map(bitsize).fold(0, |a, &b| u64::max(a, b)), + capital_g.map(bitsize).fold(0, |a, &b| u64::max(a, b)), + 53, + ] + .into_iter() + .max() + .unwrap(); + + if capital_size < size { + break; + } + let capital_shift = (capital_size as i64) - 53; + let capital_f_adjusted = capital_f + .map(|bi| Complex64::new(i64::try_from(bi >> capital_shift).unwrap() as f64, 0.0)) + .fft(); + let capital_g_adjusted = capital_g + .map(|bi| Complex64::new(i64::try_from(bi >> capital_shift).unwrap() as f64, 0.0)) + .fft(); + + let numerator = capital_f_adjusted.hadamard_mul(&f_star_adjusted) + + capital_g_adjusted.hadamard_mul(&g_star_adjusted); + let quotient = numerator.hadamard_div(&denominator_fft).ifft(); + + let k = quotient.map(|f| Into::::into(f.re.round() as i64)); + + if k.is_zero() { + break; + } + let kf = (k.clone().karatsuba(f)) + .reduce_by_cyclotomic(n) + .map(|bi| bi << (capital_size - size)); + let kg = (k.clone().karatsuba(g)) + .reduce_by_cyclotomic(n) + .map(|bi| bi << (capital_size - size)); + *capital_f -= kf; + *capital_g -= kg; + + counter += 1; + if counter > 1000 { + // If we get here, that means that (with high likelihood) we are in an + // infinite loop. We know it happens from time to time -- seldomly, but it + // does. It would be nice to fix that! But in order to fix it we need to be + // able to reproduce it, and for that we need test vectors. So print them + // and hope that one day they circle back to the implementor. + return Err(format!("Encountered infinite loop in babai_reduce of falcon-rust.\n\\ + Please help the developer(s) fix it! You can do this by sending them the inputs to the function that caused the behavior:\n\\ + f: {:?}\n\\ + g: {:?}\n\\ + capital_f: {:?}\n\\ + capital_g: {:?}\n", f.coefficients, g.coefficients, capital_f.coefficients, capital_g.coefficients)); + } + } + Ok(()) +} + +/// Extended Euclidean algorithm for computing the greatest common divisor (g) and +/// Bézout coefficients (u, v) for the relation +/// +/// $$ u a + v b = g . $$ +/// +/// Implementation adapted from Wikipedia [1]. +/// +/// [1]: https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm#Pseudocode +fn xgcd(a: &BigInt, b: &BigInt) -> (BigInt, BigInt, BigInt) { + let (mut old_r, mut r) = (a.clone(), b.clone()); + let (mut old_s, mut s) = (BigInt::one(), BigInt::zero()); + let (mut old_t, mut t) = (BigInt::zero(), BigInt::one()); + + while r != BigInt::zero() { + let quotient = old_r.clone() / r.clone(); + (old_r, r) = (r.clone(), old_r.clone() - quotient.clone() * r); + (old_s, s) = (s.clone(), old_s.clone() - quotient.clone() * s); + (old_t, t) = (t.clone(), old_t.clone() - quotient * t); + } + + (old_r, old_s, old_t) +} diff --git a/src/dsa/rpo_falcon512/math/polynomial.rs b/src/dsa/rpo_falcon512/math/polynomial.rs new file mode 100644 index 00000000..ac8bedd4 --- /dev/null +++ b/src/dsa/rpo_falcon512/math/polynomial.rs @@ -0,0 +1,616 @@ +use super::{field::FalconFelt, Inverse}; +use crate::dsa::rpo_falcon512::{MODULUS, N}; +use crate::Felt; +use alloc::vec::Vec; +use core::default::Default; +use core::fmt::Debug; +use core::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub, SubAssign}; +use num::{One, Zero}; + +#[derive(Debug, Clone, Default)] +pub struct Polynomial { + pub coefficients: Vec, +} + +impl Polynomial +where + F: Clone, +{ + pub fn new(coefficients: Vec) -> Self { + Self { coefficients } + } +} + +impl< + F: Mul + Sub + AddAssign + Zero + Div + Clone + Inverse, + > Polynomial +{ + pub fn hadamard_mul(&self, other: &Self) -> Self { + Polynomial::new( + self.coefficients + .iter() + .zip(other.coefficients.iter()) + .map(|(a, b)| *a * *b) + .collect(), + ) + } + pub fn hadamard_div(&self, other: &Self) -> Self { + let other_coefficients_inverse = F::batch_inverse_or_zero(&other.coefficients); + Polynomial::new( + self.coefficients + .iter() + .zip(other_coefficients_inverse.iter()) + .map(|(a, b)| *a * *b) + .collect(), + ) + } + + pub fn hadamard_inv(&self) -> Self { + let coefficients_inverse = F::batch_inverse_or_zero(&self.coefficients); + Polynomial::new(coefficients_inverse) + } +} + +impl Polynomial { + pub fn degree(&self) -> Option { + if self.coefficients.is_empty() { + return None; + } + let mut max_index = self.coefficients.len() - 1; + while self.coefficients[max_index] == F::zero() { + if let Some(new_index) = max_index.checked_sub(1) { + max_index = new_index; + } else { + return None; + } + } + Some(max_index) + } + + pub fn lc(&self) -> F { + match self.degree() { + Some(non_negative_degree) => self.coefficients[non_negative_degree].clone(), + None => F::zero(), + } + } +} + +/// The following implementations are specific to cyclotomic polynomial rings, +/// i.e., F\[ X \] / , and are used extensively in Falcon. +impl< + F: One + + Zero + + Clone + + Neg + + MulAssign + + AddAssign + + Div + + Sub + + PartialEq, + > Polynomial +{ + /// Reduce the polynomial by X^n + 1. + pub fn reduce_by_cyclotomic(&self, n: usize) -> Self { + let mut coefficients = vec![F::zero(); n]; + let mut sign = -F::one(); + for (i, c) in self.coefficients.iter().cloned().enumerate() { + if i % n == 0 { + sign *= -F::one(); + } + coefficients[i % n] += sign.clone() * c; + } + Polynomial::new(coefficients) + } + + /// Computes the field norm of the polynomial as an element of the cyclotomic ring + /// F\[ X \] / relative to one of half the size, i.e., F\[ X \] / . + /// + /// Corresponds to formula 3.25 in the spec [1, p.30]. + /// + /// [1]: https://falcon-sign.info/falcon.pdf + pub fn field_norm(&self) -> Self { + let n = self.coefficients.len(); + let mut f0_coefficients = vec![F::zero(); n / 2]; + let mut f1_coefficients = vec![F::zero(); n / 2]; + for i in 0..n / 2 { + f0_coefficients[i] = self.coefficients[2 * i].clone(); + f1_coefficients[i] = self.coefficients[2 * i + 1].clone(); + } + let f0 = Polynomial::new(f0_coefficients); + let f1 = Polynomial::new(f1_coefficients); + let f0_squared = (f0.clone() * f0).reduce_by_cyclotomic(n / 2); + let f1_squared = (f1.clone() * f1).reduce_by_cyclotomic(n / 2); + let x = Polynomial::new(vec![F::zero(), F::one()]); + f0_squared - (x * f1_squared).reduce_by_cyclotomic(n / 2) + } + + /// Lifts an element from a cyclotomic polynomial ring to one of double the size. + pub fn lift_next_cyclotomic(&self) -> Self { + let n = self.coefficients.len(); + let mut coefficients = vec![F::zero(); n * 2]; + for i in 0..n { + coefficients[2 * i] = self.coefficients[i].clone(); + } + Self::new(coefficients) + } + + /// Computes the galois adjoint of the polynomial in the cyclotomic ring F\[ X \] / < X^n + 1 > , + /// which corresponds to f(x^2). + pub fn galois_adjoint(&self) -> Self { + Self::new( + self.coefficients + .iter() + .enumerate() + .map(|(i, c)| if i % 2 == 0 { c.clone() } else { c.clone().neg() }) + .collect(), + ) + } +} + +impl> Polynomial { + pub(crate) fn l2_norm_squared(&self) -> f64 { + self.coefficients + .iter() + .map(|i| Into::::into(i.clone())) + .map(|i| i * i) + .sum::() + } +} + +impl PartialEq for Polynomial +where + F: Zero + PartialEq + Clone + AddAssign, +{ + fn eq(&self, other: &Self) -> bool { + if self.is_zero() && other.is_zero() { + true + } else if self.is_zero() || other.is_zero() { + false + } else { + let self_degree = self.degree().unwrap(); + let other_degree = other.degree().unwrap(); + self.coefficients[0..=self_degree] == other.coefficients[0..=other_degree] + } + } +} + +impl Eq for Polynomial where F: Zero + PartialEq + Clone + AddAssign {} + +impl Add for &Polynomial +where + F: Add + AddAssign + Clone, +{ + type Output = Polynomial; + + fn add(self, rhs: Self) -> Self::Output { + let coefficients = if self.coefficients.len() >= rhs.coefficients.len() { + let mut coefficients = self.coefficients.clone(); + for (i, c) in rhs.coefficients.iter().enumerate() { + coefficients[i] += c.clone(); + } + coefficients + } else { + let mut coefficients = rhs.coefficients.clone(); + for (i, c) in self.coefficients.iter().enumerate() { + coefficients[i] += c.clone(); + } + coefficients + }; + Self::Output { coefficients } + } +} + +impl Add for Polynomial +where + F: Add + AddAssign + Clone, +{ + type Output = Polynomial; + fn add(self, rhs: Self) -> Self::Output { + let coefficients = if self.coefficients.len() >= rhs.coefficients.len() { + let mut coefficients = self.coefficients.clone(); + for (i, c) in rhs.coefficients.into_iter().enumerate() { + coefficients[i] += c; + } + coefficients + } else { + let mut coefficients = rhs.coefficients.clone(); + for (i, c) in self.coefficients.into_iter().enumerate() { + coefficients[i] += c; + } + coefficients + }; + Self::Output { coefficients } + } +} + +impl AddAssign for Polynomial +where + F: Add + AddAssign + Clone, +{ + fn add_assign(&mut self, rhs: Self) { + if self.coefficients.len() >= rhs.coefficients.len() { + for (i, c) in rhs.coefficients.into_iter().enumerate() { + self.coefficients[i] += c; + } + } else { + let mut coefficients = rhs.coefficients.clone(); + for (i, c) in self.coefficients.iter().enumerate() { + coefficients[i] += c.clone(); + } + self.coefficients = coefficients; + } + } +} + +impl Sub for &Polynomial +where + F: Sub + Clone + Neg + Add + AddAssign, +{ + type Output = Polynomial; + + fn sub(self, rhs: Self) -> Self::Output { + self + &(-rhs) + } +} + +impl Sub for Polynomial +where + F: Sub + Clone + Neg + Add + AddAssign, +{ + type Output = Polynomial; + + fn sub(self, rhs: Self) -> Self::Output { + self + (-rhs) + } +} + +impl SubAssign for Polynomial +where + F: Add + Neg + AddAssign + Clone + Sub, +{ + fn sub_assign(&mut self, rhs: Self) { + self.coefficients = self.clone().sub(rhs).coefficients; + } +} + +impl + Clone> Neg for &Polynomial { + type Output = Polynomial; + + fn neg(self) -> Self::Output { + Self::Output { + coefficients: self.coefficients.iter().cloned().map(|a| -a).collect(), + } + } +} + +impl + Clone> Neg for Polynomial { + type Output = Self; + + fn neg(self) -> Self::Output { + Self::Output { + coefficients: self.coefficients.iter().cloned().map(|a| -a).collect(), + } + } +} + +impl Mul for &Polynomial +where + F: Add + AddAssign + Mul + Sub + Zero + PartialEq + Clone, +{ + type Output = Polynomial; + + fn mul(self, other: Self) -> Self::Output { + if self.is_zero() || other.is_zero() { + return Polynomial::::zero(); + } + let mut coefficients = + vec![F::zero(); self.coefficients.len() + other.coefficients.len() - 1]; + for i in 0..self.coefficients.len() { + for j in 0..other.coefficients.len() { + coefficients[i + j] += self.coefficients[i].clone() * other.coefficients[j].clone(); + } + } + Polynomial { coefficients } + } +} + +impl Mul for Polynomial +where + F: Add + AddAssign + Mul + Zero + PartialEq + Clone, +{ + type Output = Self; + + fn mul(self, other: Self) -> Self::Output { + if self.is_zero() || other.is_zero() { + return Self::zero(); + } + let mut coefficients = + vec![F::zero(); self.coefficients.len() + other.coefficients.len() - 1]; + for i in 0..self.coefficients.len() { + for j in 0..other.coefficients.len() { + coefficients[i + j] += self.coefficients[i].clone() * other.coefficients[j].clone(); + } + } + Self { coefficients } + } +} + +impl + Zero + Clone> Mul for &Polynomial { + type Output = Polynomial; + + fn mul(self, other: F) -> Self::Output { + Polynomial { + coefficients: self.coefficients.iter().cloned().map(|i| i * other.clone()).collect(), + } + } +} + +impl + Zero + Clone> Mul for Polynomial { + type Output = Polynomial; + + fn mul(self, other: F) -> Self::Output { + Polynomial { + coefficients: self.coefficients.iter().cloned().map(|i| i * other.clone()).collect(), + } + } +} + +impl + Sub + AddAssign + Zero + Div + Clone> + Polynomial +{ + /// Multiply two polynomials using Karatsuba's divide-and-conquer algorithm. + pub fn karatsuba(&self, other: &Self) -> Self { + Polynomial::new(vector_karatsuba(&self.coefficients, &other.coefficients)) + } +} + +impl One for Polynomial +where + F: Clone + One + PartialEq + Zero + AddAssign, +{ + fn one() -> Self { + Self { coefficients: vec![F::one()] } + } +} + +impl Zero for Polynomial +where + F: Zero + PartialEq + Clone + AddAssign, +{ + fn zero() -> Self { + Self { coefficients: vec![] } + } + + fn is_zero(&self) -> bool { + self.degree().is_none() + } +} + +impl Polynomial { + pub fn shift(&self, shamt: usize) -> Self { + Self { + coefficients: [vec![F::zero(); shamt], self.coefficients.clone()].concat(), + } + } + + pub fn constant(f: F) -> Self { + Self { coefficients: vec![f] } + } + + pub fn map G>(&self, closure: C) -> Polynomial { + Polynomial::::new(self.coefficients.iter().map(closure).collect()) + } + + pub fn fold G + Clone>(&self, mut initial_value: G, closure: C) -> G { + for c in self.coefficients.iter() { + initial_value = (closure.clone())(initial_value, c); + } + initial_value + } +} + +impl Div> for Polynomial +where + F: Zero + + One + + PartialEq + + AddAssign + + Clone + + Mul + + MulAssign + + Div + + Neg + + Sub, +{ + type Output = Polynomial; + + fn div(self, denominator: Self) -> Self::Output { + if denominator.is_zero() { + panic!(); + } + if self.is_zero() { + Self::zero(); + } + let mut remainder = self.clone(); + let mut quotient = Polynomial::::zero(); + while remainder.degree().unwrap() >= denominator.degree().unwrap() { + let shift = remainder.degree().unwrap() - denominator.degree().unwrap(); + let quotient_coefficient = remainder.lc() / denominator.lc(); + let monomial = Self::constant(quotient_coefficient).shift(shift); + quotient += monomial.clone(); + remainder -= monomial * denominator.clone(); + if remainder.is_zero() { + break; + } + } + quotient + } +} + +fn vector_karatsuba< + F: Zero + AddAssign + Mul + Sub + Div + Clone, +>( + left: &[F], + right: &[F], +) -> Vec { + let n = left.len(); + if n <= 8 { + let mut product = vec![F::zero(); left.len() + right.len() - 1]; + for (i, l) in left.iter().enumerate() { + for (j, r) in right.iter().enumerate() { + product[i + j] += l.clone() * r.clone(); + } + } + return product; + } + let n_over_2 = n / 2; + let mut product = vec![F::zero(); 2 * n - 1]; + let left_lo = &left[0..n_over_2]; + let right_lo = &right[0..n_over_2]; + let left_hi = &left[n_over_2..]; + let right_hi = &right[n_over_2..]; + let left_sum: Vec = + left_lo.iter().zip(left_hi).map(|(a, b)| a.clone() + b.clone()).collect(); + let right_sum: Vec = + right_lo.iter().zip(right_hi).map(|(a, b)| a.clone() + b.clone()).collect(); + + let prod_lo = vector_karatsuba(left_lo, right_lo); + let prod_hi = vector_karatsuba(left_hi, right_hi); + let prod_mid: Vec = vector_karatsuba(&left_sum, &right_sum) + .iter() + .zip(prod_lo.iter().zip(prod_hi.iter())) + .map(|(s, (l, h))| s.clone() - (l.clone() + h.clone())) + .collect(); + + for (i, l) in prod_lo.into_iter().enumerate() { + product[i] = l; + } + for (i, m) in prod_mid.into_iter().enumerate() { + product[i + n_over_2] += m; + } + for (i, h) in prod_hi.into_iter().enumerate() { + product[i + n] += h + } + product +} + +impl From> for Polynomial { + fn from(item: Polynomial) -> Self { + let res: Vec = + item.coefficients.iter().map(|a| Felt::from(a.value() as u16)).collect(); + Polynomial::new(res) + } +} + +impl From<&Polynomial> for Polynomial { + fn from(item: &Polynomial) -> Self { + let res: Vec = + item.coefficients.iter().map(|a| Felt::from(a.value() as u16)).collect(); + Polynomial::new(res) + } +} + +impl From> for Polynomial { + fn from(item: Polynomial) -> Self { + let res: Vec = item.coefficients.iter().map(|&a| FalconFelt::new(a)).collect(); + Polynomial::new(res) + } +} + +impl From<&Polynomial> for Polynomial { + fn from(item: &Polynomial) -> Self { + let res: Vec = item.coefficients.iter().map(|&a| FalconFelt::new(a)).collect(); + Polynomial::new(res) + } +} + +impl From> for Polynomial { + fn from(item: Vec) -> Self { + let res: Vec = item.iter().map(|&a| FalconFelt::new(a)).collect(); + Polynomial::new(res) + } +} + +impl From<&Vec> for Polynomial { + fn from(item: &Vec) -> Self { + let res: Vec = item.iter().map(|&a| FalconFelt::new(a)).collect(); + Polynomial::new(res) + } +} + +impl Polynomial { + pub fn norm_squared(&self) -> u64 { + self.coefficients + .iter() + .map(|&i| i.balanced_value() as i64) + .map(|i| (i * i) as u64) + .sum::() + } + + // PUBLIC ACCESSORS + // -------------------------------------------------------------------------------------------- + + /// Returns the coefficients of this polynomial as field elements. + pub fn to_elements(&self) -> Vec { + self.coefficients.iter().map(|&a| Felt::from(a.value() as u16)).collect() + } + + // POLYNOMIAL OPERATIONS + // -------------------------------------------------------------------------------------------- + + /// Multiplies two polynomials over Z_p\[x\] without reducing modulo p. Given that the degrees + /// of the input polynomials are less than 512 and their coefficients are less than the modulus + /// q equal to 12289, the resulting product polynomial is guaranteed to have coefficients less + /// than the Miden prime. + /// + /// Note that this multiplication is not over Z_p\[x\]/(phi). + pub fn mul_modulo_p(a: &Self, b: &Self) -> [u64; 1024] { + let mut c = [0; 2 * N]; + for i in 0..N { + for j in 0..N { + c[i + j] += a.coefficients[i].value() as u64 * b.coefficients[j].value() as u64; + } + } + + c + } + + /// Reduces a polynomial, that is the product of two polynomials over Z_p\[x\], modulo + /// the irreducible polynomial phi. This results in an element in Z_p\[x\]/(phi). + pub fn reduce_negacyclic(a: &[u64; 1024]) -> Self { + let mut c = [FalconFelt::zero(); N]; + let modulus = MODULUS as u16; + for i in 0..N { + let ai = a[N + i] % modulus as u64; + let neg_ai = (modulus - ai as u16) % modulus; + + let bi = (a[i] % modulus as u64) as u16; + c[i] = FalconFelt::new(((neg_ai + bi) % modulus) as i16); + } + + Self::new(c.to_vec()) + } +} + +// TESTS +// ================================================================================================ + +#[cfg(test)] +mod tests { + use super::{FalconFelt, Polynomial, N}; + + #[test] + fn test_negacyclic_reduction() { + let coef1: [u8; N] = rand_utils::rand_array(); + let coef2: [u8; N] = rand_utils::rand_array(); + + let poly1 = Polynomial::new(coef1.iter().map(|&a| FalconFelt::new(a as i16)).collect()); + let poly2 = Polynomial::new(coef2.iter().map(|&a| FalconFelt::new(a as i16)).collect()); + let prod = poly1.clone() * poly2.clone(); + + assert_eq!( + prod.reduce_by_cyclotomic(N), + Polynomial::reduce_negacyclic(&Polynomial::mul_modulo_p(&poly1, &poly2)) + ); + } +} diff --git a/src/dsa/rpo_falcon512/math/samplerz.rs b/src/dsa/rpo_falcon512/math/samplerz.rs new file mode 100644 index 00000000..af4057f6 --- /dev/null +++ b/src/dsa/rpo_falcon512/math/samplerz.rs @@ -0,0 +1,298 @@ +use core::f64::consts::LN_2; +use rand::Rng; + +#[cfg(not(feature = "std"))] +use num::Float; + +/// Samples an integer from {0, ..., 18} according to the distribution χ, which is close to +/// the half-Gaussian distribution on the natural numbers with mean 0 and standard deviation +/// equal to sigma_max. +fn base_sampler(bytes: [u8; 9]) -> i16 { + const RCDT: [u128; 18] = [ + 3024686241123004913666, + 1564742784480091954050, + 636254429462080897535, + 199560484645026482916, + 47667343854657281903, + 8595902006365044063, + 1163297957344668388, + 117656387352093658, + 8867391802663976, + 496969357462633, + 20680885154299, + 638331848991, + 14602316184, + 247426747, + 3104126, + 28824, + 198, + 1, + ]; + let u = u128::from_be_bytes([vec![0u8; 7], bytes.to_vec()].concat().try_into().unwrap()); + RCDT.into_iter().filter(|r| u < *r).count() as i16 +} + +/// Computes an integer approximation of 2^63 * ccs * exp(-x). +fn approx_exp(x: f64, ccs: f64) -> u64 { + // The constants C are used to approximate exp(-x); these + // constants are taken from FACCT (up to a scaling factor + // of 2^63): + // https://eprint.iacr.org/2018/1234 + // https://github.com/raykzhao/gaussian + const C: [u64; 13] = [ + 0x00000004741183A3u64, + 0x00000036548CFC06u64, + 0x0000024FDCBF140Au64, + 0x0000171D939DE045u64, + 0x0000D00CF58F6F84u64, + 0x000680681CF796E3u64, + 0x002D82D8305B0FEAu64, + 0x011111110E066FD0u64, + 0x0555555555070F00u64, + 0x155555555581FF00u64, + 0x400000000002B400u64, + 0x7FFFFFFFFFFF4800u64, + 0x8000000000000000u64, + ]; + + let mut z: u64; + let mut y: u64; + let twoe63 = 1u64 << 63; + + y = C[0]; + z = f64::floor(x * (twoe63 as f64)) as u64; + for cu in C.iter().skip(1) { + let zy = (z as u128) * (y as u128); + y = cu - ((zy >> 63) as u64); + } + + z = f64::floor((twoe63 as f64) * ccs) as u64; + + (((z as u128) * (y as u128)) >> 63) as u64 +} + +/// A random bool that is true with probability ≈ ccs · exp(-x). +fn ber_exp(x: f64, ccs: f64, random_bytes: [u8; 7]) -> bool { + // 0.69314718055994530941 = ln(2) + let s = f64::floor(x / LN_2) as usize; + let r = x - LN_2 * (s as f64); + let shamt = usize::min(s, 63); + let z = ((((approx_exp(r, ccs) as u128) << 1) - 1) >> shamt) as u64; + let mut w = 0i16; + for (index, i) in (0..64).step_by(8).rev().enumerate() { + let byte = random_bytes[index]; + w = (byte as i16) - (((z >> i) & 0xff) as i16); + if w != 0 { + break; + } + } + w < 0 +} + +/// Samples an integer from the Gaussian distribution with given mean (mu) and standard deviation +/// (sigma). +pub(crate) fn sampler_z(mu: f64, sigma: f64, sigma_min: f64, rng: &mut R) -> i16 { + const SIGMA_MAX: f64 = 1.8205; + const INV_2SIGMA_MAX_SQ: f64 = 1f64 / (2f64 * SIGMA_MAX * SIGMA_MAX); + let isigma = 1f64 / sigma; + let dss = 0.5f64 * isigma * isigma; + let s = f64::floor(mu); + let r = mu - s; + let ccs = sigma_min * isigma; + loop { + let z0 = base_sampler(rng.gen()); + let random_byte: u8 = rng.gen(); + let b = (random_byte & 1) as i16; + let z = b + ((b << 1) - 1) * z0; + let zf_min_r = (z as f64) - r; + // x = ((z-r)^2)/(2*sigma^2) - ((z-b)^2)/(2*sigma0^2) + let x = zf_min_r * zf_min_r * dss - (z0 * z0) as f64 * INV_2SIGMA_MAX_SQ; + if ber_exp(x, ccs, rng.gen()) { + return z + (s as i16); + } + } +} + +#[cfg(all(test, feature = "std"))] +mod test { + use alloc::vec::Vec; + use rand::RngCore; + use std::{thread::sleep, time::Duration}; + + use super::{approx_exp, ber_exp, sampler_z}; + + /// RNG used only for testing purposes, whereby the produced + /// string of random bytes is equal to the one it is initialized + /// with. Whatever you do, do not use this RNG in production. + struct UnsafeBufferRng { + buffer: Vec, + index: usize, + } + + impl UnsafeBufferRng { + fn new(buffer: &[u8]) -> Self { + Self { buffer: buffer.to_vec(), index: 0 } + } + + fn next(&mut self) -> u8 { + if self.buffer.len() <= self.index { + // panic!("Ran out of buffer."); + sleep(Duration::from_millis(10)); + 0u8 + } else { + let return_value = self.buffer[self.index]; + self.index += 1; + return_value + } + } + } + + impl RngCore for UnsafeBufferRng { + fn next_u32(&mut self) -> u32 { + // let bytes: [u8; 4] = (0..4) + // .map(|_| self.next()) + // .collect_vec() + // .try_into() + // .unwrap(); + // u32::from_be_bytes(bytes) + u32::from_le_bytes([self.next(), 0, 0, 0]) + } + + fn next_u64(&mut self) -> u64 { + // let bytes: [u8; 8] = (0..8) + // .map(|_| self.next()) + // .collect_vec() + // .try_into() + // .unwrap(); + // u64::from_be_bytes(bytes) + u64::from_le_bytes([self.next(), 0, 0, 0, 0, 0, 0, 0]) + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + for d in dest.iter_mut() { + *d = self.next(); + } + } + + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> { + for d in dest.iter_mut() { + *d = self.next(); + } + Ok(()) + } + } + + #[test] + fn test_unsafe_buffer_rng() { + let seed_bytes = hex::decode("7FFECD162AE2").unwrap(); + let mut rng = UnsafeBufferRng::new(&seed_bytes); + let generated_bytes: Vec = (0..seed_bytes.len()).map(|_| rng.next()).collect(); + assert_eq!(seed_bytes, generated_bytes); + } + + #[test] + fn test_approx_exp() { + let precision = 1u64 << 14; + // known answers were generated with the following sage script: + //```sage + // num_samples = 10 + // precision = 200 + // R = Reals(precision) + // + // print(f"let kats : [(f64, f64, u64);{num_samples}] = [") + // for i in range(num_samples): + // x = RDF.random_element(0.0, 0.693147180559945) + // ccs = RDF.random_element(0.0, 1.0) + // res = round(2^63 * R(ccs) * exp(R(-x))) + // print(f"({x}, {ccs}, {res}),") + // print("];") + // ``` + let kats: [(f64, f64, u64); 10] = [ + (0.2314993926072656, 0.8148006314615972, 5962140072160879737), + (0.2648875572812225, 0.12769669655309035, 903712282351034505), + (0.11251957513682391, 0.9264611470305881, 7635725498677341553), + (0.04353439307256617, 0.5306497137523327, 4685877322232397936), + (0.41834495299784347, 0.879438856118578, 5338392138535350986), + (0.32579398973228557, 0.16513412873289002, 1099603299296456803), + (0.5939508073919817, 0.029776019144967303, 151637565622779016), + (0.2932367999399056, 0.37123847662857923, 2553827649386670452), + (0.5005699297417507, 0.31447208863888976, 1758235618083658825), + (0.4876437338498085, 0.6159515298936868, 3488632981903743976), + ]; + for (x, ccs, answer) in kats { + let difference = (answer as i128) - (approx_exp(x, ccs) as i128); + assert!( + (difference * difference) as u64 <= precision * precision, + "answer: {answer} versus approximation: {}\ndifference: {} whereas precision: {}", + approx_exp(x, ccs), + difference, + precision + ); + } + } + + #[test] + fn test_ber_exp() { + let kats = [ + ( + 1.268_314_048_020_498_4, + 0.749_990_853_267_664_9, + hex::decode("ea000000000000").unwrap(), + false, + ), + ( + 0.001_563_917_959_143_409_6, + 0.749_990_853_267_664_9, + hex::decode("6c000000000000").unwrap(), + true, + ), + ( + 0.017_921_215_753_999_235, + 0.749_990_853_267_664_9, + hex::decode("c2000000000000").unwrap(), + false, + ), + ( + 0.776_117_648_844_980_6, + 0.751_181_554_542_520_8, + hex::decode("58000000000000").unwrap(), + true, + ), + ]; + for (x, ccs, bytes, answer) in kats { + assert_eq!(answer, ber_exp(x, ccs, bytes.try_into().unwrap())); + } + } + + #[test] + fn test_sampler_z() { + let sigma_min = 1.277833697; + // known answers from the doc, table 3.2, page 44 + // https://falcon-sign.info/falcon.pdf + // The zeros were added to account for dropped bytes. + let kats = [ + (-91.90471153063714,1.7037990414754918,hex::decode("0fc5442ff043d66e91d1ea000000000000cac64ea5450a22941edc6c").unwrap(),-92), + (-8.322564895434937,1.7037990414754918,hex::decode("f4da0f8d8444d1a77265c2000000000000ef6f98bbbb4bee7db8d9b3").unwrap(),-8), + (-19.096516109216804,1.7035823083824078,hex::decode("db47f6d7fb9b19f25c36d6000000000000b9334d477a8bc0be68145d").unwrap(),-20), + (-11.335543982423326, 1.7035823083824078, hex::decode("ae41b4f5209665c74d00dc000000000000c1a8168a7bb516b3190cb42c1ded26cd52000000000000aed770eca7dd334e0547bcc3c163ce0b").unwrap(), -12), + (7.9386734193997555, 1.6984647769450156, hex::decode("31054166c1012780c603ae0000000000009b833cec73f2f41ca5807c000000000000c89c92158834632f9b1555").unwrap(), 8), + (-28.990850086867255, 1.6984647769450156, hex::decode("737e9d68a50a06dbbc6477").unwrap(), -30), + (-9.071257914091655, 1.6980782114808988, hex::decode("a98ddd14bf0bf22061d632").unwrap(), -10), + (-43.88754568839566, 1.6980782114808988, hex::decode("3cbf6818a68f7ab9991514").unwrap(), -41), + (-58.17435547946095,1.7010983419195522,hex::decode("6f8633f5bfa5d26848668e0000000000003d5ddd46958e97630410587c").unwrap(),-61), + (-43.58664906684732, 1.7010983419195522, hex::decode("272bc6c25f5c5ee53f83c40000000000003a361fbc7cc91dc783e20a").unwrap(), -46), + (-34.70565203313315, 1.7009387219711465, hex::decode("45443c59574c2c3b07e2e1000000000000d9071e6d133dbe32754b0a").unwrap(), -34), + (-44.36009577368896, 1.7009387219711465, hex::decode("6ac116ed60c258e2cbaeab000000000000728c4823e6da36e18d08da0000000000005d0cc104e21cc7fd1f5ca8000000000000d9dbb675266c928448059e").unwrap(), -44), + (-21.783037079346236, 1.6958406126012802, hex::decode("68163bc1e2cbf3e18e7426").unwrap(), -23), + (-39.68827784633828, 1.6958406126012802, hex::decode("d6a1b51d76222a705a0259").unwrap(), -40), + (-18.488607061056847, 1.6955259305261838, hex::decode("f0523bfaa8a394bf4ea5c10000000000000f842366fde286d6a30803").unwrap(), -22), + (-48.39610939101591, 1.6955259305261838, hex::decode("87bd87e63374cee62127fc0000000000006931104aab64f136a0485b").unwrap(), -50), + ]; + for (mu, sigma, random_bytes, answer) in kats { + assert_eq!( + sampler_z(mu, sigma, sigma_min, &mut UnsafeBufferRng::new(&random_bytes)), + answer + ); + } + } +} diff --git a/src/dsa/rpo_falcon512/mod.rs b/src/dsa/rpo_falcon512/mod.rs index 35ac61a9..8d9c1e6f 100644 --- a/src/dsa/rpo_falcon512/mod.rs +++ b/src/dsa/rpo_falcon512/mod.rs @@ -4,33 +4,31 @@ use crate::{ Felt, Word, ZERO, }; -#[cfg(feature = "std")] -mod ffi; - -mod error; +mod hash_to_point; mod keys; -mod polynomial; +mod math; mod signature; -pub use error::FalconError; -pub use keys::{KeyPair, PublicKey}; -pub use polynomial::Polynomial; -pub use signature::Signature; +pub use self::keys::{PubKeyPoly, PublicKey, SecretKey}; +pub use self::math::Polynomial; +pub use self::signature::{Signature, SignatureHeader, SignaturePoly}; // CONSTANTS // ================================================================================================ -// The Falcon modulus. -const MODULUS: u16 = 12289; -const MODULUS_MINUS_1_OVER_TWO: u16 = 6144; +// The Falcon modulus p. +const MODULUS: i16 = 12289; + +// Number of bits needed to encode an element in the Falcon field. +const FALCON_ENCODING_BITS: u32 = 14; // The Falcon parameters for Falcon-512. This is the degree of the polynomial `phi := x^N + 1` // defining the ring Z_p[x]/(phi). const N: usize = 512; -const LOG_N: usize = 9; +const LOG_N: u8 = 9; /// Length of nonce used for key-pair generation. -const NONCE_LEN: usize = 40; +const SIG_NONCE_LEN: usize = 40; /// Number of filed elements used to encode a nonce. const NONCE_ELEMENTS: usize = 8; @@ -42,16 +40,64 @@ pub const PK_LEN: usize = 897; pub const SK_LEN: usize = 1281; /// Signature length as a u8 vector. -const SIG_LEN: usize = 626; +const SIG_POLY_BYTE_LEN: usize = 625; /// Bound on the squared-norm of the signature. const SIG_L2_BOUND: u64 = 34034726; +/// Standard deviation of the Gaussian over the lattice. +const SIGMA: f64 = 165.7366171829776; + // TYPE ALIASES // ================================================================================================ -type SignatureBytes = [u8; NONCE_LEN + SIG_LEN]; -type PublicKeyBytes = [u8; PK_LEN]; -type SecretKeyBytes = [u8; SK_LEN]; -type NonceBytes = [u8; NONCE_LEN]; -type NonceElements = [Felt; NONCE_ELEMENTS]; +type ShortLatticeBasis = [Polynomial; 4]; + +// NONCE +// ================================================================================================ + +/// Nonce of the Falcon signature. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Nonce([u8; SIG_NONCE_LEN]); + +impl Nonce { + /// Returns a new [Nonce] instantiated from the provided bytes. + pub fn new(bytes: [u8; SIG_NONCE_LEN]) -> Self { + Self(bytes) + } + + /// Returns the underlying bytes of this nonce. + pub fn as_bytes(&self) -> &[u8; SIG_NONCE_LEN] { + &self.0 + } + + /// Converts byte representation of the nonce into field element representation. + /// + /// Nonce bytes are converted to field elements by taking consecutive 5 byte chunks + /// of the nonce and interpreting them as field elements. + pub fn to_elements(&self) -> [Felt; NONCE_ELEMENTS] { + let mut buffer = [0_u8; 8]; + let mut result = [ZERO; 8]; + for (i, bytes) in self.0.chunks(5).enumerate() { + buffer[..5].copy_from_slice(bytes); + // we can safely (without overflow) create a new Felt from u64 value here since this + // value contains at most 5 bytes + result[i] = Felt::new(u64::from_le_bytes(buffer)); + } + + result + } +} + +impl Serializable for &Nonce { + fn write_into(&self, target: &mut W) { + target.write_bytes(&self.0) + } +} + +impl Deserializable for Nonce { + fn read_from(source: &mut R) -> Result { + let bytes = source.read()?; + Ok(Self(bytes)) + } +} diff --git a/src/dsa/rpo_falcon512/polynomial.rs b/src/dsa/rpo_falcon512/polynomial.rs deleted file mode 100644 index a00787fb..00000000 --- a/src/dsa/rpo_falcon512/polynomial.rs +++ /dev/null @@ -1,279 +0,0 @@ -use alloc::vec::Vec; -use core::ops::{Add, Mul, Sub}; - -use super::{FalconError, Felt, LOG_N, MODULUS, MODULUS_MINUS_1_OVER_TWO, N, PK_LEN}; - -// FALCON POLYNOMIAL -// ================================================================================================ - -/// A polynomial over Z_p\[x\]/(phi) where phi := x^512 + 1 -#[derive(Debug, Copy, Clone, PartialEq)] -pub struct Polynomial([u16; N]); - -impl Polynomial { - // CONSTRUCTORS - // -------------------------------------------------------------------------------------------- - - /// Constructs a new polynomial from a list of coefficients. - /// - /// # Safety - /// This constructor validates that the coefficients are in the valid range only in debug mode. - pub unsafe fn new(data: [u16; N]) -> Self { - for value in data { - debug_assert!(value < MODULUS); - } - - Self(data) - } - - /// Decodes raw bytes representing a public key into a polynomial in Z_p\[x\]/(phi). - /// - /// # Errors - /// Returns an error if: - /// - The provided input is not exactly 897 bytes long. - /// - The first byte of the input is not equal to log2(512) i.e., 9. - /// - Any of the coefficients encoded in the provided input is greater than or equal to the - /// Falcon field modulus. - pub fn from_pub_key(input: &[u8]) -> Result { - if input.len() != PK_LEN { - return Err(FalconError::PubKeyDecodingInvalidLength(input.len())); - } - - if input[0] != LOG_N as u8 { - return Err(FalconError::PubKeyDecodingInvalidTag(input[0])); - } - - let mut acc = 0_u32; - let mut acc_len = 0; - - let mut output = [0_u16; N]; - let mut output_idx = 0; - - for &byte in input.iter().skip(1) { - acc = (acc << 8) | (byte as u32); - acc_len += 8; - - if acc_len >= 14 { - acc_len -= 14; - let w = (acc >> acc_len) & 0x3FFF; - if w >= MODULUS as u32 { - return Err(FalconError::PubKeyDecodingInvalidCoefficient(w)); - } - output[output_idx] = w as u16; - output_idx += 1; - } - } - - if (acc & ((1u32 << acc_len) - 1)) == 0 { - Ok(Self(output)) - } else { - Err(FalconError::PubKeyDecodingExtraData) - } - } - - /// Decodes the signature into the coefficients of a polynomial in Z_p\[x\]/(phi). It assumes - /// that the signature has been encoded using the uncompressed format. - /// - /// # Errors - /// Returns an error if: - /// - The signature has been encoded using a different algorithm than the reference compressed - /// encoding algorithm. - /// - The encoded signature polynomial is in Z_p\[x\]/(phi') where phi' = x^N' + 1 and N' != 512. - /// - While decoding the high bits of a coefficient, the current accumulated value of its - /// high bits is larger than 2048. - /// - The decoded coefficient is -0. - /// - The remaining unused bits in the last byte of `input` are non-zero. - pub fn from_signature(input: &[u8]) -> Result { - let (encoding, log_n) = (input[0] >> 4, input[0] & 0b00001111); - - if encoding != 0b0011 { - return Err(FalconError::SigDecodingIncorrectEncodingAlgorithm); - } - if log_n != 0b1001 { - return Err(FalconError::SigDecodingNotSupportedDegree(log_n)); - } - - let input = &input[41..]; - let mut input_idx = 0; - let mut acc = 0u32; - let mut acc_len = 0; - let mut output = [0_u16; N]; - - for e in output.iter_mut() { - acc = (acc << 8) | (input[input_idx] as u32); - input_idx += 1; - let b = acc >> acc_len; - let s = b & 128; - let mut m = b & 127; - - loop { - if acc_len == 0 { - acc = (acc << 8) | (input[input_idx] as u32); - input_idx += 1; - acc_len = 8; - } - acc_len -= 1; - if ((acc >> acc_len) & 1) != 0 { - break; - } - m += 128; - if m >= 2048 { - return Err(FalconError::SigDecodingTooBigHighBits(m)); - } - } - if s != 0 && m == 0 { - return Err(FalconError::SigDecodingMinusZero); - } - - *e = if s != 0 { (MODULUS as u32 - m) as u16 } else { m as u16 }; - } - - if (acc & ((1 << acc_len) - 1)) != 0 { - return Err(FalconError::SigDecodingNonZeroUnusedBitsLastByte); - } - - Ok(Self(output)) - } - - // PUBLIC ACCESSORS - // -------------------------------------------------------------------------------------------- - - /// Returns the coefficients of this polynomial as integers. - pub fn inner(&self) -> [u16; N] { - self.0 - } - - /// Returns the coefficients of this polynomial as field elements. - pub fn to_elements(&self) -> Vec { - self.0.iter().map(|&a| Felt::from(a)).collect() - } - - // POLYNOMIAL OPERATIONS - // -------------------------------------------------------------------------------------------- - - /// Multiplies two polynomials over Z_p\[x\] without reducing modulo p. Given that the degrees - /// of the input polynomials are less than 512 and their coefficients are less than the modulus - /// q equal to 12289, the resulting product polynomial is guaranteed to have coefficients less - /// than the Miden prime. - /// - /// Note that this multiplication is not over Z_p\[x\]/(phi). - pub fn mul_modulo_p(a: &Self, b: &Self) -> [u64; 1024] { - let mut c = [0; 2 * N]; - for i in 0..N { - for j in 0..N { - c[i + j] += a.0[i] as u64 * b.0[j] as u64; - } - } - - c - } - - /// Reduces a polynomial, that is the product of two polynomials over Z_p\[x\], modulo - /// the irreducible polynomial phi. This results in an element in Z_p\[x\]/(phi). - pub fn reduce_negacyclic(a: &[u64; 1024]) -> Self { - let mut c = [0; N]; - for i in 0..N { - let ai = a[N + i] % MODULUS as u64; - let neg_ai = (MODULUS - ai as u16) % MODULUS; - - let bi = (a[i] % MODULUS as u64) as u16; - c[i] = (neg_ai + bi) % MODULUS; - } - - Self(c) - } - - /// Computes the norm squared of a polynomial in Z_p\[x\]/(phi) after normalizing its - /// coefficients to be in the interval (-p/2, p/2]. - pub fn sq_norm(&self) -> u64 { - let mut res = 0; - for e in self.0 { - if e > MODULUS_MINUS_1_OVER_TWO { - res += (MODULUS - e) as u64 * (MODULUS - e) as u64 - } else { - res += e as u64 * e as u64 - } - } - res - } -} - -// Returns a polynomial representing the zero polynomial i.e. default element. -impl Default for Polynomial { - fn default() -> Self { - Self([0_u16; N]) - } -} - -/// Multiplication over Z_p\[x\]/(phi) -impl Mul for Polynomial { - type Output = Self; - - fn mul(self, other: Self) -> >::Output { - let mut result = [0_u16; N]; - for j in 0..N { - for k in 0..N { - let i = (j + k) % N; - let a = self.0[j] as usize; - let b = other.0[k] as usize; - let q = MODULUS as usize; - let mut prod = a * b % q; - if (N - 1) < (j + k) { - prod = (q - prod) % q; - } - result[i] = ((result[i] as usize + prod) % q) as u16; - } - } - - Polynomial(result) - } -} - -/// Addition over Z_p\[x\]/(phi) -impl Add for Polynomial { - type Output = Self; - - fn add(self, other: Self) -> >::Output { - let mut res = self; - res.0.iter_mut().zip(other.0.iter()).for_each(|(x, y)| *x = (*x + *y) % MODULUS); - - res - } -} - -/// Subtraction over Z_p\[x\]/(phi) -impl Sub for Polynomial { - type Output = Self; - - fn sub(self, other: Self) -> >::Output { - let mut res = self; - res.0 - .iter_mut() - .zip(other.0.iter()) - .for_each(|(x, y)| *x = (*x + MODULUS - *y) % MODULUS); - - res - } -} - -// TESTS -// ================================================================================================ - -#[cfg(test)] -mod tests { - use super::{Polynomial, N}; - - #[test] - fn test_negacyclic_reduction() { - let coef1: [u16; N] = rand_utils::rand_array(); - let coef2: [u16; N] = rand_utils::rand_array(); - - let poly1 = Polynomial(coef1); - let poly2 = Polynomial(coef2); - - assert_eq!( - poly1 * poly2, - Polynomial::reduce_negacyclic(&Polynomial::mul_modulo_p(&poly1, &poly2)) - ); - } -} diff --git a/src/dsa/rpo_falcon512/signature.rs b/src/dsa/rpo_falcon512/signature.rs index c91f786f..035776de 100644 --- a/src/dsa/rpo_falcon512/signature.rs +++ b/src/dsa/rpo_falcon512/signature.rs @@ -1,289 +1,373 @@ -use alloc::string::ToString; -use core::cell::OnceCell; +use alloc::{string::ToString, vec::Vec}; +use core::ops::Deref; use super::{ - ByteReader, ByteWriter, Deserializable, DeserializationError, Felt, NonceBytes, NonceElements, - Polynomial, PublicKeyBytes, Rpo256, Serializable, SignatureBytes, Word, MODULUS, N, - SIG_L2_BOUND, ZERO, + hash_to_point::hash_to_point_rpo256, + keys::PubKeyPoly, + math::{FalconFelt, FastFft, Polynomial}, + ByteReader, ByteWriter, Deserializable, DeserializationError, Felt, Nonce, Rpo256, + Serializable, Word, LOG_N, MODULUS, N, SIG_L2_BOUND, SIG_POLY_BYTE_LEN, }; +use num::Zero; // FALCON SIGNATURE // ================================================================================================ /// An RPO Falcon512 signature over a message. /// -/// The signature is a pair of polynomials (s1, s2) in (Z_p\[x\]/(phi))^2, where: +/// The signature is a pair of polynomials (s1, s2) in (Z_p\[x\]/(phi))^2 a nonce `r`, and a public +/// key polynomial `h` where: /// - p := 12289 /// - phi := x^512 + 1 -/// - s1 = c - s2 * h -/// - h is a polynomial representing the public key and c is a polynomial that is the hash-to-point -/// of the message being signed. /// -/// The signature verifies if and only if: +/// The signature verifies against a public key `pk` if and only if: /// 1. s1 = c - s2 * h /// 2. |s1|^2 + |s2|^2 <= SIG_L2_BOUND /// -/// where |.| is the norm. +/// where |.| is the norm and: +/// - c = HashToPoint(r || message) +/// - pk = Rpo256::hash(h) /// -/// [Signature] also includes the extended public key which is serialized as: +/// Here h is a polynomial representing the public key and pk is its digest using the Rpo256 hash +/// function. c is a polynomial that is the hash-to-point of the message being signed. +/// +/// The polynomial h is serialized as: /// 1. 1 byte representing the log2(512) i.e., 9. -/// 2. 896 bytes for the public key. This is decoded into the `h` polynomial above. +/// 2. 896 bytes for the public key itself. /// -/// The actual signature is serialized as: +/// The signature is serialized as: /// 1. A header byte specifying the algorithm used to encode the coefficients of the `s2` polynomial -/// together with the degree of the irreducible polynomial phi. -/// The general format of this byte is 0b0cc1nnnn where: -/// a. cc is either 01 when the compressed encoding algorithm is used and 10 when the -/// uncompressed algorithm is used. -/// b. nnnn is log2(N) where N is the degree of the irreducible polynomial phi. -/// The current implementation works always with cc equal to 0b01 and nnnn equal to 0b1001 and -/// thus the header byte is always equal to 0b00111001. +/// together with the degree of the irreducible polynomial phi. For RPO Falcon512, the header +/// byte is set to `10111001` which differentiates it from the standardized instantiation of +/// the Falcon signature. /// 2. 40 bytes for the nonce. -/// 3. 625 bytes encoding the `s2` polynomial above. +/// 4. 625 bytes encoding the `s2` polynomial above. /// -/// The total size of the signature (including the extended public key) is 1563 bytes. -#[derive(Debug, Clone)] +/// The total size of the signature is (including the extended public key) is 1563 bytes. +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Signature { - pub(super) pk: PublicKeyBytes, - pub(super) sig: SignatureBytes, - - // Cached polynomial decoding for public key and signatures - pub(super) pk_polynomial: OnceCell, - pub(super) sig_polynomial: OnceCell, + header: SignatureHeader, + nonce: Nonce, + s2: SignaturePoly, + h: PubKeyPoly, } impl Signature { + // CONSTRUCTOR + // -------------------------------------------------------------------------------------------- + pub fn new(nonce: Nonce, h: PubKeyPoly, s2: SignaturePoly) -> Signature { + Self { + header: SignatureHeader::default(), + nonce, + s2, + h, + } + } + // PUBLIC ACCESSORS // -------------------------------------------------------------------------------------------- /// Returns the public key polynomial h. - pub fn pub_key_poly(&self) -> Polynomial { - *self.pk_polynomial.get_or_init(|| { - // we assume that the signature was constructed with a valid public key, and thus - // expect() is OK here. - Polynomial::from_pub_key(&self.pk).expect("invalid public key") - }) - } - - /// Returns the nonce component of the signature represented as field elements. - /// - /// Nonce bytes are converted to field elements by taking consecutive 5 byte chunks - /// of the nonce and interpreting them as field elements. - pub fn nonce(&self) -> NonceElements { - // we assume that the signature was constructed with a valid signature, and thus - // expect() is OK here. - let nonce = self.sig[1..41].try_into().expect("invalid signature"); - decode_nonce(nonce) + pub fn pk_poly(&self) -> &PubKeyPoly { + &self.h } // Returns the polynomial representation of the signature in Z_p[x]/(phi). - pub fn sig_poly(&self) -> Polynomial { - *self.sig_polynomial.get_or_init(|| { - // we assume that the signature was constructed with a valid signature, and thus - // expect() is OK here. - Polynomial::from_signature(&self.sig).expect("invalid signature") - }) + pub fn sig_poly(&self) -> &Polynomial { + &self.s2 } - // HASH-TO-POINT - // -------------------------------------------------------------------------------------------- - - /// Returns a polynomial in Z_p\[x\]/(phi) representing the hash of the provided message. - pub fn hash_to_point(&self, message: Word) -> Polynomial { - hash_to_point(message, &self.nonce()) + /// Returns the nonce component of the signature. + pub fn nonce(&self) -> &Nonce { + &self.nonce } // SIGNATURE VERIFICATION // -------------------------------------------------------------------------------------------- + /// Returns true if this signature is a valid signature for the specified message generated - /// against key pair matching the specified public key commitment. + /// against the secret key matching the specified public key commitment. pub fn verify(&self, message: Word, pubkey_com: Word) -> bool { - // Make sure the expanded public key matches the provided public key commitment - let h = self.pub_key_poly(); - let h_digest: Word = Rpo256::hash_elements(&h.to_elements()).into(); + // compute the hash of the public key polynomial + let h_felt: Polynomial = (&**self.pk_poly()).into(); + let h_digest: Word = Rpo256::hash_elements(&h_felt.coefficients).into(); if h_digest != pubkey_com { return false; } - // Make sure the signature is valid - let s2 = self.sig_poly(); - let c = self.hash_to_point(message); - - let s1 = c - s2 * h; - - let sq_norm = s1.sq_norm() + s2.sq_norm(); - sq_norm <= SIG_L2_BOUND + let c = hash_to_point_rpo256(message, &self.nonce); + h_digest == pubkey_com && verify_helper(&c, &self.s2, self.pk_poly()) } } -// SERIALIZATION / DESERIALIZATION -// ================================================================================================ - impl Serializable for Signature { fn write_into(&self, target: &mut W) { - target.write_bytes(&self.pk); - target.write_bytes(&self.sig); + target.write(&self.header); + target.write(&self.nonce); + target.write(&self.s2); + target.write(&self.h); } } impl Deserializable for Signature { fn read_from(source: &mut R) -> Result { - let pk: PublicKeyBytes = source.read_array()?; - let sig: SignatureBytes = source.read_array()?; - - // make sure public key and signature can be decoded correctly - let pk_polynomial = Polynomial::from_pub_key(&pk) - .map_err(|err| DeserializationError::InvalidValue(err.to_string()))? - .into(); - let sig_polynomial = Polynomial::from_signature(&sig) - .map_err(|err| DeserializationError::InvalidValue(err.to_string()))? - .into(); - - Ok(Self { pk, sig, pk_polynomial, sig_polynomial }) + let header = source.read()?; + let nonce = source.read()?; + let s2 = source.read()?; + let h = source.read()?; + + Ok(Self { header, nonce, s2, h }) } } -// HELPER FUNCTIONS +// SIGNATURE HEADER // ================================================================================================ -/// Returns a polynomial in Z_p[x]/(phi) representing the hash of the provided message and -/// nonce. -fn hash_to_point(message: Word, nonce: &NonceElements) -> Polynomial { - let mut state = [ZERO; Rpo256::STATE_WIDTH]; +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SignatureHeader(u8); - // absorb the nonce into the state - for (&n, s) in nonce.iter().zip(state[Rpo256::RATE_RANGE].iter_mut()) { - *s = n; +impl Default for SignatureHeader { + /// According to section 3.11.3 in the specification [1], the signature header has the format + /// `0cc1nnnn` where: + /// + /// 1. `cc` signifies the encoding method. `01` denotes using the compression encoding method + /// and `10` denotes encoding using the uncompressed method. + /// 2. `nnnn` encodes `LOG_N`. + /// + /// For RPO Falcon 512 we use compression encoding and N = 512. Moreover, to differentiate the + /// RPO Falcon variant from the reference variant using SHAKE256, we flip the first bit in the + /// header. Thus, for RPO Falcon 512 the header is `10111001` + /// + /// [1]: https://falcon-sign.info/falcon.pdf + fn default() -> Self { + Self(0b1011_1001) } - Rpo256::apply_permutation(&mut state); +} - // absorb message into the state - for (&m, s) in message.iter().zip(state[Rpo256::RATE_RANGE].iter_mut()) { - *s = m; +impl Serializable for &SignatureHeader { + fn write_into(&self, target: &mut W) { + target.write_u8(self.0) } +} - // squeeze the coefficients of the polynomial - let mut i = 0; - let mut res = [0_u16; N]; - for _ in 0..64 { - Rpo256::apply_permutation(&mut state); - for a in &state[Rpo256::RATE_RANGE] { - res[i] = (a.as_int() % MODULUS as u64) as u16; - i += 1; +impl Deserializable for SignatureHeader { + fn read_from(source: &mut R) -> Result { + let header = source.read_u8()?; + let (encoding, log_n) = (header >> 4, header & 0b00001111); + if encoding != 0b1011 { + return Err(DeserializationError::InvalidValue( + "Failed to decode signature: not supported encoding algorithm".to_string(), + )); + } + + if log_n != LOG_N { + return Err(DeserializationError::InvalidValue( + format!("Failed to decode signature: only supported irreducible polynomial degree is 512, 2^{log_n} was provided") + )); } + + Ok(Self(header)) } +} + +// SIGNATURE POLYNOMIAL +// ================================================================================================ + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SignaturePoly(pub Polynomial); + +impl Deref for SignaturePoly { + type Target = Polynomial; - // using the raw constructor is OK here because we reduce all coefficients by the modulus above - unsafe { Polynomial::new(res) } + fn deref(&self) -> &Self::Target { + &self.0 + } } -/// Converts byte representation of the nonce into field element representation. -fn decode_nonce(nonce: &NonceBytes) -> NonceElements { - let mut buffer = [0_u8; 8]; - let mut result = [ZERO; 8]; - for (i, bytes) in nonce.chunks(5).enumerate() { - buffer[..5].copy_from_slice(bytes); - // we can safely (without overflow) create a new Felt from u64 value here since this value - // contains at most 5 bytes - result[i] = Felt::new(u64::from_le_bytes(buffer)); +impl From> for SignaturePoly { + fn from(pk_poly: Polynomial) -> Self { + Self(pk_poly) } +} - result +impl TryFrom<&[i16; N]> for SignaturePoly { + type Error = (); + + fn try_from(coefficients: &[i16; N]) -> Result { + if are_coefficients_valid(coefficients) { + Ok(Self(coefficients.to_vec().into())) + } else { + Err(()) + } + } } -// TESTS -// ================================================================================================ +impl Serializable for &SignaturePoly { + fn write_into(&self, target: &mut W) { + let sig_coeff: Vec = self.0.coefficients.iter().map(|a| a.balanced_value()).collect(); + let mut sk_bytes = vec![0_u8; SIG_POLY_BYTE_LEN]; + + let mut acc = 0; + let mut acc_len = 0; + let mut v = 0; + let mut t; + let mut w; + + // For each coefficient of x: + // - the sign is encoded on 1 bit + // - the 7 lower bits are encoded naively (binary) + // - the high bits are encoded in unary encoding + // + // Algorithm 17 p. 47 of the specification [1]. + // + // [1]: https://falcon-sign.info/falcon.pdf + for &c in sig_coeff.iter() { + acc <<= 1; + t = c; + + if t < 0 { + t = -t; + acc |= 1; + } + w = t as u16; -#[cfg(all(test, feature = "std"))] -mod tests { - use core::ffi::c_void; - use rand_utils::rand_vector; - - use super::{ - super::{ffi::*, KeyPair}, - *, - }; - - // Wrappers for unsafe functions - impl Rpo128Context { - /// Initializes the RPO state. - pub fn init() -> Self { - let mut ctx = Rpo128Context { content: [0u64; 13] }; - unsafe { - rpo128_init(&mut ctx as *mut Rpo128Context); + acc <<= 7; + let mask = 127_u32; + acc |= (w as u32) & mask; + w >>= 7; + + acc_len += 8; + + acc <<= w + 1; + acc |= 1; + acc_len += w + 1; + + while acc_len >= 8 { + acc_len -= 8; + + sk_bytes[v] = (acc >> acc_len) as u8; + v += 1; } - ctx } - /// Absorbs data into the RPO state. - pub fn absorb(&mut self, data: &[u8]) { - unsafe { - rpo128_absorb( - self as *mut Rpo128Context, - data.as_ptr() as *const c_void, - data.len(), - ) + if acc_len > 0 { + sk_bytes[v] = (acc << (8 - acc_len)) as u8; + } + target.write_bytes(&sk_bytes); + } +} + +impl Deserializable for SignaturePoly { + fn read_from(source: &mut R) -> Result { + let input = source.read_array::()?; + + let mut input_idx = 0; + let mut acc = 0u32; + let mut acc_len = 0; + let mut coefficients = [FalconFelt::zero(); N]; + + // Algorithm 18 p. 48 of the specification [1]. + // + // [1]: https://falcon-sign.info/falcon.pdf + for c in coefficients.iter_mut() { + acc = (acc << 8) | (input[input_idx] as u32); + input_idx += 1; + let b = acc >> acc_len; + let s = b & 128; + let mut m = b & 127; + + loop { + if acc_len == 0 { + acc = (acc << 8) | (input[input_idx] as u32); + input_idx += 1; + acc_len = 8; + } + acc_len -= 1; + if ((acc >> acc_len) & 1) != 0 { + break; + } + m += 128; + if m >= 2048 { + return Err(DeserializationError::InvalidValue( + "Failed to decode signature: high bits {m} exceed 2048".to_string(), + )); + } + } + if s != 0 && m == 0 { + return Err(DeserializationError::InvalidValue( + "Failed to decode signature: -0 is forbidden".to_string(), + )); } + + let felt = if s != 0 { (MODULUS as u32 - m) as u16 } else { m as u16 }; + *c = FalconFelt::new(felt as i16); } - /// Finalizes the RPO state to prepare for squeezing. - pub fn finalize(&mut self) { - unsafe { rpo128_finalize(self as *mut Rpo128Context) } + if (acc & ((1 << acc_len) - 1)) != 0 { + return Err(DeserializationError::InvalidValue( + "Failed to decode signature: Non-zero unused bits in the last byte".to_string(), + )); } + Ok(Polynomial::new(coefficients.to_vec()).into()) } +} - #[test] - fn test_hash_to_point() { - // Create a random message and transform it into a u8 vector - let msg_felts: Word = rand_vector::(4).try_into().unwrap(); - let msg_bytes = msg_felts - .iter() - .flat_map(|e| e.as_int().to_le_bytes()) - .collect::>(); - - // Create a nonce i.e. a [u8; 40] array and pack into a [Felt; 8] array. - let nonce: [u8; 40] = rand_vector::(40).try_into().unwrap(); - - let mut buffer = [0_u8; 64]; - for i in 0..8 { - buffer[8 * i] = nonce[5 * i]; - buffer[8 * i + 1] = nonce[5 * i + 1]; - buffer[8 * i + 2] = nonce[5 * i + 2]; - buffer[8 * i + 3] = nonce[5 * i + 3]; - buffer[8 * i + 4] = nonce[5 * i + 4]; - } +// HELPER FUNCTIONS +// ================================================================================================ - // Initialize the RPO state - let mut rng = Rpo128Context::init(); +/// Takes the hash-to-point polynomial `c` of a message, the signature polynomial over +/// the message `s2` and a public key polynomial and returns `true` is the signature is a valid +/// signature for the given parameters, otherwise it returns `false`. +fn verify_helper(c: &Polynomial, s2: &SignaturePoly, h: &PubKeyPoly) -> bool { + let h_fft = h.fft(); + let s2_fft = s2.fft(); + let c_fft = c.fft(); - // Absorb the nonce and message into the RPO state - rng.absorb(&buffer); - rng.absorb(&msg_bytes); - rng.finalize(); + // compute the signature polynomial s1 using s1 = c - s2 * h + let s1_fft = c_fft - s2_fft.hadamard_mul(&h_fft); + let s1 = s1_fft.ifft(); - // Generate the coefficients of the hash-to-point polynomial. - let mut res: [u16; N] = [0; N]; + // compute the norm squared of (s1, s2) + let length_squared_s1 = s1.norm_squared(); + let length_squared_s2 = s2.norm_squared(); + let length_squared = length_squared_s1 + length_squared_s2; - unsafe { - PQCLEAN_FALCON512_CLEAN_hash_to_point_rpo( - &mut rng as *mut Rpo128Context, - res.as_mut_ptr(), - 9, - ); - } + length_squared < SIG_L2_BOUND +} + +/// Checks whether a set of coefficients is a valid one for a signature polynomial. +fn are_coefficients_valid(x: &[i16]) -> bool { + if x.len() != N { + return false; + } - // Check that the coefficients are correct - let nonce = decode_nonce(&nonce); - assert_eq!(res, hash_to_point(msg_felts, &nonce).inner()); + for &c in x { + if !(-2047..=2047).contains(&c) { + return false; + } } + true +} + +// TESTS +// ================================================================================================ + +#[cfg(test)] +mod tests { + use super::{super::SecretKey, *}; + use rand::SeedableRng; + use rand_chacha::ChaCha20Rng; + #[test] fn test_serialization_round_trip() { - let key = KeyPair::new().unwrap(); - let signature = key.sign(Word::default()).unwrap(); + let seed = [0_u8; 32]; + let mut rng = ChaCha20Rng::from_seed(seed); + + let sk = SecretKey::with_rng(&mut rng); + let signature = sk.sign_with_rng(Word::default(), &mut rng); let serialized = signature.to_bytes(); let deserialized = Signature::read_from_bytes(&serialized).unwrap(); assert_eq!(signature.sig_poly(), deserialized.sig_poly()); - assert_eq!(signature.pub_key_poly(), deserialized.pub_key_poly()); } } diff --git a/src/rand/mod.rs b/src/rand/mod.rs index 9e0f3a9a..cc846056 100644 --- a/src/rand/mod.rs +++ b/src/rand/mod.rs @@ -1,5 +1,6 @@ //! Pseudo-random element generation. +use rand::RngCore; pub use winter_crypto::{DefaultRandomCoin as WinterRandomCoin, RandomCoin, RandomCoinError}; pub use winter_utils::Randomizable; @@ -11,7 +12,7 @@ pub use rpo::RpoRandomCoin; /// Pseudo-random element generator. /// /// An instance can be used to draw, uniformly at random, base field elements as well as [Word]s. -pub trait FeltRng { +pub trait FeltRng: RngCore { /// Draw, uniformly at random, a base field element. fn draw_element(&mut self) -> Felt; diff --git a/src/rand/rpo.rs b/src/rand/rpo.rs index e5d80516..ceca2da6 100644 --- a/src/rand/rpo.rs +++ b/src/rand/rpo.rs @@ -1,9 +1,10 @@ -use super::{Felt, FeltRng, FieldElement, RandomCoin, RandomCoinError, Word, ZERO}; +use super::{Felt, FeltRng, FieldElement, RandomCoin, RandomCoinError, RngCore, Word, ZERO}; use crate::{ hash::rpo::{Rpo256, RpoDigest}, utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}, }; use alloc::{string::ToString, vec::Vec}; +use rand_core::impls; // CONSTANTS // ================================================================================================ @@ -20,9 +21,9 @@ const HALF_RATE_WIDTH: usize = (Rpo256::RATE_RANGE.end - Rpo256::RATE_RANGE.star /// /// The simplification is related to the following facts: /// 1. A call to the reseed method implies one and only one call to the permutation function. -/// This is possible because in our case we never reseed with more than 4 field elements. +/// This is possible because in our case we never reseed with more than 4 field elements. /// 2. As a result of the previous point, we don't make use of an input buffer to accumulate seed -/// material. +/// material. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct RpoRandomCoin { state: [Felt; STATE_WIDTH], @@ -61,6 +62,11 @@ impl RpoRandomCoin { (self.state, self.current) } + /// Fills `dest` with random data. + pub fn fill_bytes(&mut self, dest: &mut [u8]) { + ::fill_bytes(self, dest) + } + fn draw_basefield(&mut self) -> Felt { if self.current == RATE_END { Rpo256::apply_permutation(&mut self.state); @@ -183,6 +189,28 @@ impl FeltRng for RpoRandomCoin { } } +// RNGCORE IMPLEMENTATION +// ------------------------------------------------------------------------------------------------ + +impl RngCore for RpoRandomCoin { + fn next_u32(&mut self) -> u32 { + self.draw_basefield().as_int() as u32 + } + + fn next_u64(&mut self) -> u64 { + impls::next_u64_via_u32(self) + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + impls::fill_bytes_via_next(self, dest) + } + + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> { + self.fill_bytes(dest); + Ok(()) + } +} + // SERIALIZATION // ------------------------------------------------------------------------------------------------ diff --git a/src/utils/mod.rs b/src/utils/mod.rs index df2a1cff..14e55645 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -10,17 +10,13 @@ mod kv_map; // RE-EXPORTS // ================================================================================================ -#[deprecated(since = "0.8.3", note = "You should prefer to import from `alloc::*`")] -pub use alloc::{format, vec}; - pub use winter_utils::{ - boxed, string, uninit_vector, Box, ByteReader, ByteWriter, Deserializable, - DeserializationError, Serializable, SliceReader, + uninit_vector, ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable, + SliceReader, }; pub mod collections { pub use super::kv_map::*; - pub use winter_utils::collections::*; } // UTILITY FUNCTIONS