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