diff --git a/.gitmodules b/.gitmodules index 88ae99fac..e69de29bb 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 908a8e744..8e7f98555 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## 0.9.0 (TBD) -* [BREAKING] Removed deprecated re-exports from liballoc/libstd. +* [BREAKING] Removed deprecated re-exports from liballoc/libstd (#290). +* [BREAKING] Refactored RpoFalcon512 signature to work with pure Rust (#285). # 0.8.4 (2024-03-17) diff --git a/Cargo.toml b/Cargo.toml index d4d955c3f..971885d67 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,33 +33,40 @@ 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" } +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/PQClean b/PQClean deleted file mode 160000 index c3abebf4a..000000000 --- a/PQClean +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c3abebf4ab1ff516ffa71e6337f06d898952c299 diff --git a/README.md b/README.md index 03be9019b..c7d56613a 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,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: diff --git a/build.rs b/build.rs index 2e713593a..f2e8a1c79 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/src/dsa/rpo_falcon512/error.rs b/src/dsa/rpo_falcon512/error.rs deleted file mode 100644 index 3f5e6e91d..000000000 --- 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 cd7bed561..000000000 --- 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 bdcc3ec73..000000000 --- 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 4879c1288..000000000 --- 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 d9038af8f..000000000 --- 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 de3f2bec8..000000000 --- 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 000000000..292a215cc --- /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 2ad1b4e73..000000000 --- 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 000000000..73d1ca1b2 --- /dev/null +++ b/src/dsa/rpo_falcon512/keys/mod.rs @@ -0,0 +1,51 @@ +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, rand::RpoRandomCoin, Word, ONE, ZERO}; + use winter_math::FieldElement; + use winter_utils::{Deserializable, Serializable}; + + #[test] + fn test_falcon_verification() { + let seed = [ZERO; 4]; + let mut rng = RpoRandomCoin::new(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 000000000..8b172d20e --- /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 000000000..cfcff5680 --- /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]. + 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 000000000..6cdf858a9 --- /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 000000000..10e6bfed5 --- /dev/null +++ b/src/dsa/rpo_falcon512/math/fft.rs @@ -0,0 +1,1927 @@ +use super::{field::FalconFelt, polynomial::Polynomial, Inverse}; +use alloc::vec::Vec; +use core::{ + f64::consts::PI, + ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}, +}; +use num::{One, Zero}; +use num_complex::Complex64; + +#[cfg(not(feature = "std"))] +use num::Float; + +/// Implements Cyclotomic FFT without bitreversing the outputs, and using precomputed powers of the +/// 2n-th primitive root of unity. +pub trait FastFft: Sized + Clone { + type Field: Add + Mul + AddAssign + MulAssign + Neg + Sub + SubAssign; + fn fft_inplace(&mut self); + fn fft(&self) -> Self { + let mut a = self.clone(); + a.fft_inplace(); + a + } + + fn merge_fft(a: &Self, b: &Self) -> Self; + fn split_fft(&self) -> (Self, Self); + + fn ifft_inplace(&mut self); + fn ifft(&self) -> Self { + let mut a = self.clone(); + a.ifft_inplace(); + a + } +} + +pub trait CyclotomicFourier +where + Self: Sized + + Copy + + One + + Zero + + Add + + Sub + + Mul + + MulAssign + + Inverse, +{ + /// Gets the inverse of 2^n. + #[allow(dead_code)] + fn power_of_two_inverse(n: usize) -> Self { + let mut a = Self::one() + Self::one(); + for _ in 0..n { + a *= a; + } + Self::inverse_or_zero(a) + } + + /// Gets a primitive nth (with n a power of 2) root of unity. + #[allow(dead_code)] + fn primitive_root_of_unity(n: usize) -> Self; + + /// Computes the integer whose n-bit binary expansion is the reverse of that of the argument. + fn bitreverse_index(arg: usize, n: usize) -> usize { + assert!(n > 0); + assert_eq!(n & (n - 1), 0); + let mut rev = 0; + let mut m = n >> 1; + let mut k = 1; + while m > 0 { + rev |= (((arg & m) != 0) as usize) * k; + k <<= 1; + m >>= 1; + } + rev + } + + /// Computes the first n powers of the 2nth root of unity, and put them in bit-reversed order. + #[allow(dead_code)] + fn bitreversed_powers(n: usize) -> Vec { + let psi = Self::primitive_root_of_unity(2 * n); + let mut array = vec![Self::zero(); n]; + let mut alpha = Self::one(); + for a in array.iter_mut() { + *a = alpha; + alpha *= psi; + } + Self::bitreverse_array(&mut array); + array + } + + /// Computes the first n powers of the 2nth root of unity, invert them, and put them in + /// bit-reversed order. + #[allow(dead_code)] + fn bitreversed_powers_inverse(n: usize) -> Vec { + let psi = Self::primitive_root_of_unity(2 * n).inverse_or_zero(); + let mut array = vec![Self::zero(); n]; + let mut alpha = Self::one(); + for a in array.iter_mut() { + *a = alpha; + alpha *= psi; + } + Self::bitreverse_array(&mut array); + array + } + + /// Reorders the given elements in the array by reversing the binary expansions of their indices. + fn bitreverse_array(array: &mut [T]) { + let n = array.len(); + for i in 0..n { + let j = Self::bitreverse_index(i, n); + if i < j { + array.swap(i, j); + } + } + } + + /// Computes the evaluations of the polynomial on the roots of the polynomial X^n + 1 using a + /// fast Fourier transform. Algorithm 1 from https://eprint.iacr.org/2016/504.pdf. + /// + /// Arguments: + /// + /// - a : &mut [Self] + /// (a reference to) a mutable array of field elements which is to + /// be transformed under the FFT. The transformation happens in- + /// place. + /// + /// - psi_rev: &[Self] + /// (a reference to) an array of powers of psi, from 0 to n-1, + /// but ordered by bit-reversed index. Here psi is a primitive root + /// of order 2n. You can use + /// `Self::bitreversed_powers(psi, n)` for this purpose, but this + /// trait implementation is not const. For the performance benefit + /// you want a precompiled array, which you can get if you can get + /// by implementing the same method and marking it "const". + fn fft(a: &mut [Self], psi_rev: &[Self]) { + let n = a.len(); + let mut t = n; + let mut m = 1; + while m < n { + t >>= 1; + for i in 0..m { + let j1 = 2 * i * t; + let j2 = j1 + t - 1; + let s = psi_rev[m + i]; + for j in j1..=j2 { + let u = a[j]; + let v = a[j + t] * s; + a[j] = u + v; + a[j + t] = u - v; + } + } + m <<= 1; + } + } + + /// Computes the coefficients of the polynomial with the given evaluations on the roots of + /// X^n + 1 using an inverse fast Fourier transform. + /// Algorithm 2 from https://eprint.iacr.org/2016/504.pdf. + /// + /// Arguments: + /// + /// - a : &mut [Self] + /// (a reference to) a mutable array of field elements which is to + /// be transformed under the IFFT. The transformation happens in- + /// place. + /// + /// - psi_inv_rev: &[Self] + /// (a reference to) an array of powers of psi^-1, from 0 to n-1, + /// but ordered by bit-reversed index. Here psi is a primitive root of + /// order 2n. You can use + /// `Self::bitreversed_powers(Self::inverse_or_zero(psi), n)` for + /// this purpose, but this trait implementation is not const. For + /// the performance benefit you want a precompiled array, which you + /// can get if you can get by implementing the same methods and marking + /// them "const". + fn ifft(a: &mut [Self], psi_inv_rev: &[Self], ninv: Self) { + let n = a.len(); + let mut t = 1; + let mut m = n; + while m > 1 { + let h = m / 2; + let mut j1 = 0; + for i in 0..h { + let j2 = j1 + t - 1; + let s = psi_inv_rev[h + i]; + for j in j1..=j2 { + let u = a[j]; + let v = a[j + t]; + a[j] = u + v; + a[j + t] = (u - v) * s; + } + j1 += 2 * t; + } + t <<= 1; + m >>= 1; + } + for ai in a.iter_mut() { + *ai *= ninv; + } + } + + fn split_fft(f: &[Self], psi_inv_rev: &[Self]) -> (Vec, Vec) { + let n_over_2 = f.len() / 2; + let mut f0 = vec![Self::zero(); n_over_2]; + let mut f1 = vec![Self::zero(); n_over_2]; + let two_inv = (Self::one() + Self::one()).inverse_or_zero(); + for i in 0..n_over_2 { + let two_i = i * 2; + let two_zeta_inv = two_inv * psi_inv_rev[n_over_2 + i]; + f0[i] = two_inv * (f[two_i] + f[two_i + 1]); + f1[i] = two_zeta_inv * (f[two_i] - f[two_i + 1]); + } + (f0, f1) + } + + fn merge_fft(f0: &[Self], f1: &[Self], psi_rev: &[Self]) -> Vec { + let n_over_2 = f0.len(); + let n = 2 * n_over_2; + let mut f = vec![Self::zero(); n]; + for i in 0..n_over_2 { + let two_i = i * 2; + f[two_i] = f0[i] + psi_rev[n_over_2 + i] * f1[i]; + f[two_i + 1] = f0[i] - psi_rev[n_over_2 + i] * f1[i]; + } + f + } +} + +impl CyclotomicFourier for Complex64 { + fn primitive_root_of_unity(n: usize) -> Self { + let angle = 2. * PI / (n as f64); + Complex64::new(f64::cos(angle), f64::sin(angle)) + } + + /// Custom implementation of CyclotomicFourier::bitreversed_powers for + /// better precision. + fn bitreversed_powers(n: usize) -> Vec { + let mut array = vec![Self::zero(); n]; + let half_circle = PI; + for (i, a) in array.iter_mut().enumerate() { + let angle = (i as f64) * half_circle / (n as f64); + *a = Self::new(f64::cos(angle), f64::sin(angle)); + } + Self::bitreverse_array(&mut array); + array + } + + /// Custom implementation of CyclotomicFourier::bitreversed_powers_inverse + /// for better precision. + fn bitreversed_powers_inverse(n: usize) -> Vec { + let mut array = vec![Self::zero(); n]; + let half_circle = PI; + for (i, a) in array.iter_mut().enumerate() { + let angle = (i as f64) * half_circle / (n as f64); + *a = Self::new(f64::cos(angle), -f64::sin(angle)); + } + Self::bitreverse_array(&mut array); + array + } +} + +impl FastFft for Polynomial { + type Field = Complex64; + fn fft_inplace(&mut self) { + let n = self.coefficients.len(); + debug_assert!( + (1..=512).contains(&n), + "unsupported: n = {n} not a power of 2 or larger than 512" + ); + Complex64::fft(&mut self.coefficients, &COMPLEX_BITREVERSED_POWERS); + } + + fn ifft_inplace(&mut self) { + let n = self.coefficients.len(); + debug_assert!( + (1..=512).contains(&n), + "unsupported: n = {n} not a power of 2 or larger than 512" + ); + let psi_inv_rev: Vec = + COMPLEX_BITREVERSED_POWERS.iter().map(|c| Complex64::new(c.re, -c.im)).collect(); + let ninv = Complex64::new(1.0 / (n as f64), 0.0); + Complex64::ifft(&mut self.coefficients, &psi_inv_rev, ninv); + } + + fn merge_fft(a: &Self, b: &Self) -> Self { + let n = a.coefficients.len(); + debug_assert!( + (1..=512).contains(&n), + "unsupported: n = {n} not a power of 2 or larger than 512" + ); + Self { + coefficients: Self::Field::merge_fft( + &a.coefficients, + &b.coefficients, + &COMPLEX_BITREVERSED_POWERS, + ), + } + } + + fn split_fft(&self) -> (Self, Self) { + let n = self.coefficients.len(); + debug_assert!( + (1..=512).contains(&n), + "unsupported: n = {n} not a power of 2 or larger than 512" + ); + let psi_inv_rev: Vec = + COMPLEX_BITREVERSED_POWERS.iter().map(|c| Complex64::new(c.re, -c.im)).collect(); + let (a, b) = Self::Field::split_fft(&self.coefficients, &psi_inv_rev); + (Self { coefficients: a }, Self { coefficients: b }) + } +} + +impl FastFft for Polynomial { + type Field = FalconFelt; + + fn fft_inplace(&mut self) { + let n = self.coefficients.len(); + debug_assert!( + (1..=512).contains(&n), + "unsupported: n = {n} not a power of 2 or larger than 512" + ); + FalconFelt::fft(&mut self.coefficients, &FELT_BITREVERSED_POWERS); + } + + fn ifft_inplace(&mut self) { + let n = self.coefficients.len(); + debug_assert!( + (1..=512).contains(&n), + "unsupported: n = {n} not a power of 2 or larger than 512" + ); + let ninv = match n { + 1 => FELT_NINV_1, + 2 => FELT_NINV_2, + 4 => FELT_NINV_4, + 8 => FELT_NINV_8, + 16 => FELT_NINV_16, + 32 => FELT_NINV_32, + 64 => FELT_NINV_64, + 128 => FELT_NINV_128, + 256 => FELT_NINV_256, + 512 => FELT_NINV_512, + _ => unreachable!("vector length is not power of 2 or larger than 512"), + }; + FalconFelt::ifft(&mut self.coefficients, &FELT_BITREVERSED_POWERS_INVERSE, ninv); + } + + fn merge_fft(a: &Self, b: &Self) -> Self { + let n = a.coefficients.len(); + debug_assert!( + (1..=512).contains(&n), + "unsupported: n = {n} not a power of 2 or larger than 512" + ); + Self { + coefficients: Self::Field::merge_fft( + &a.coefficients, + &b.coefficients, + &FELT_BITREVERSED_POWERS, + ), + } + } + + fn split_fft(&self) -> (Self, Self) { + let n = self.coefficients.len(); + debug_assert!( + (1..=512).contains(&n), + "unsupported: n = {n} not a power of 2 or larger than 512" + ); + let (a, b) = Self::Field::split_fft(&self.coefficients, &FELT_BITREVERSED_POWERS_INVERSE); + (Self { coefficients: a }, Self { coefficients: b }) + } +} + +#[allow(clippy::approx_constant)] +const COMPLEX_BITREVERSED_POWERS: [Complex64; 512] = [ + Complex64::new(1.0, 0.0), + Complex64::new(0.00000000000000006123233995736766, 1.0), + Complex64::new(0.7071067811865476, 0.7071067811865475), + Complex64::new(-0.7071067811865475, 0.7071067811865476), + Complex64::new(0.9238795325112867, 0.3826834323650898), + Complex64::new(-0.3826834323650897, 0.9238795325112867), + Complex64::new(0.38268343236508984, 0.9238795325112867), + Complex64::new(-0.9238795325112867, 0.3826834323650899), + Complex64::new(0.9807852804032304, 0.19509032201612825), + Complex64::new(-0.1950903220161282, 0.9807852804032304), + Complex64::new(0.5555702330196023, 0.8314696123025452), + Complex64::new(-0.8314696123025453, 0.5555702330196022), + Complex64::new(0.8314696123025452, 0.5555702330196022), + Complex64::new(-0.555570233019602, 0.8314696123025455), + Complex64::new(0.19509032201612833, 0.9807852804032304), + Complex64::new(-0.9807852804032304, 0.1950903220161286), + Complex64::new(0.9951847266721969, 0.0980171403295606), + Complex64::new(-0.09801714032956065, 0.9951847266721969), + Complex64::new(0.6343932841636455, 0.773010453362737), + Complex64::new(-0.773010453362737, 0.6343932841636455), + Complex64::new(0.881921264348355, 0.47139673682599764), + Complex64::new(-0.4713967368259977, 0.881921264348355), + Complex64::new(0.29028467725446233, 0.9569403357322089), + Complex64::new(-0.9569403357322088, 0.2902846772544624), + Complex64::new(0.9569403357322088, 0.29028467725446233), + Complex64::new(-0.29028467725446216, 0.9569403357322089), + Complex64::new(0.4713967368259978, 0.8819212643483549), + Complex64::new(-0.8819212643483549, 0.47139673682599786), + Complex64::new(0.773010453362737, 0.6343932841636455), + Complex64::new(-0.6343932841636454, 0.7730104533627371), + Complex64::new(0.09801714032956077, 0.9951847266721968), + Complex64::new(-0.9951847266721968, 0.09801714032956083), + Complex64::new(0.9987954562051724, 0.049067674327418015), + Complex64::new(-0.04906767432741801, 0.9987954562051724), + Complex64::new(0.6715589548470183, 0.7409511253549591), + Complex64::new(-0.7409511253549589, 0.6715589548470186), + Complex64::new(0.9039892931234433, 0.4275550934302821), + Complex64::new(-0.42755509343028186, 0.9039892931234434), + Complex64::new(0.33688985339222005, 0.9415440651830208), + Complex64::new(-0.9415440651830207, 0.33688985339222033), + Complex64::new(0.970031253194544, 0.24298017990326387), + Complex64::new(-0.24298017990326387, 0.970031253194544), + Complex64::new(0.5141027441932217, 0.8577286100002721), + Complex64::new(-0.857728610000272, 0.5141027441932218), + Complex64::new(0.8032075314806449, 0.5956993044924334), + Complex64::new(-0.5956993044924334, 0.8032075314806449), + Complex64::new(0.14673047445536175, 0.989176509964781), + Complex64::new(-0.989176509964781, 0.1467304744553618), + Complex64::new(0.989176509964781, 0.14673047445536175), + Complex64::new(-0.14673047445536164, 0.989176509964781), + Complex64::new(0.5956993044924335, 0.8032075314806448), + Complex64::new(-0.8032075314806448, 0.5956993044924335), + Complex64::new(0.8577286100002721, 0.5141027441932217), + Complex64::new(-0.5141027441932217, 0.8577286100002721), + Complex64::new(0.24298017990326398, 0.970031253194544), + Complex64::new(-0.970031253194544, 0.24298017990326407), + Complex64::new(0.9415440651830208, 0.33688985339222005), + Complex64::new(-0.33688985339221994, 0.9415440651830208), + Complex64::new(0.4275550934302822, 0.9039892931234433), + Complex64::new(-0.9039892931234433, 0.42755509343028203), + Complex64::new(0.7409511253549591, 0.6715589548470183), + Complex64::new(-0.6715589548470184, 0.740951125354959), + Complex64::new(0.049067674327418126, 0.9987954562051724), + Complex64::new(-0.9987954562051724, 0.049067674327417966), + Complex64::new(0.9996988186962042, 0.024541228522912288), + Complex64::new(-0.024541228522912142, 0.9996988186962042), + Complex64::new(0.6895405447370669, 0.7242470829514669), + Complex64::new(-0.7242470829514668, 0.689540544737067), + Complex64::new(0.9142097557035307, 0.40524131400498986), + Complex64::new(-0.40524131400498975, 0.9142097557035307), + Complex64::new(0.3598950365349883, 0.9329927988347388), + Complex64::new(-0.9329927988347388, 0.35989503653498833), + Complex64::new(0.9757021300385286, 0.2191012401568698), + Complex64::new(-0.21910124015686966, 0.9757021300385286), + Complex64::new(0.5349976198870973, 0.844853565249707), + Complex64::new(-0.8448535652497071, 0.5349976198870972), + Complex64::new(0.8175848131515837, 0.5758081914178453), + Complex64::new(-0.5758081914178453, 0.8175848131515837), + Complex64::new(0.17096188876030136, 0.9852776423889412), + Complex64::new(-0.9852776423889412, 0.17096188876030122), + Complex64::new(0.99247953459871, 0.1224106751992162), + Complex64::new(-0.12241067519921615, 0.99247953459871), + Complex64::new(0.6152315905806268, 0.7883464276266062), + Complex64::new(-0.7883464276266062, 0.6152315905806269), + Complex64::new(0.8700869911087115, 0.49289819222978404), + Complex64::new(-0.492898192229784, 0.8700869911087115), + Complex64::new(0.2667127574748984, 0.9637760657954398), + Complex64::new(-0.9637760657954398, 0.2667127574748985), + Complex64::new(0.9495281805930367, 0.3136817403988915), + Complex64::new(-0.3136817403988914, 0.9495281805930367), + Complex64::new(0.4496113296546066, 0.8932243011955153), + Complex64::new(-0.8932243011955152, 0.4496113296546069), + Complex64::new(0.7572088465064846, 0.6531728429537768), + Complex64::new(-0.6531728429537765, 0.7572088465064847), + Complex64::new(0.07356456359966745, 0.9972904566786902), + Complex64::new(-0.9972904566786902, 0.07356456359966773), + Complex64::new(0.9972904566786902, 0.07356456359966743), + Complex64::new(-0.07356456359966733, 0.9972904566786902), + Complex64::new(0.6531728429537768, 0.7572088465064845), + Complex64::new(-0.7572088465064846, 0.6531728429537766), + Complex64::new(0.8932243011955153, 0.44961132965460654), + Complex64::new(-0.4496113296546067, 0.8932243011955152), + Complex64::new(0.3136817403988916, 0.9495281805930367), + Complex64::new(-0.9495281805930367, 0.3136817403988914), + Complex64::new(0.9637760657954398, 0.26671275747489837), + Complex64::new(-0.2667127574748983, 0.9637760657954398), + Complex64::new(0.4928981922297841, 0.8700869911087113), + Complex64::new(-0.8700869911087113, 0.49289819222978415), + Complex64::new(0.7883464276266063, 0.6152315905806268), + Complex64::new(-0.6152315905806267, 0.7883464276266063), + Complex64::new(0.12241067519921628, 0.99247953459871), + Complex64::new(-0.99247953459871, 0.12241067519921635), + Complex64::new(0.9852776423889412, 0.17096188876030122), + Complex64::new(-0.17096188876030124, 0.9852776423889412), + Complex64::new(0.5758081914178453, 0.8175848131515837), + Complex64::new(-0.8175848131515836, 0.5758081914178454), + Complex64::new(0.8448535652497071, 0.5349976198870972), + Complex64::new(-0.534997619887097, 0.8448535652497072), + Complex64::new(0.21910124015686977, 0.9757021300385286), + Complex64::new(-0.9757021300385285, 0.21910124015687005), + Complex64::new(0.932992798834739, 0.3598950365349881), + Complex64::new(-0.35989503653498817, 0.9329927988347388), + Complex64::new(0.40524131400498986, 0.9142097557035307), + Complex64::new(-0.9142097557035307, 0.4052413140049899), + Complex64::new(0.724247082951467, 0.6895405447370668), + Complex64::new(-0.6895405447370669, 0.7242470829514669), + Complex64::new(0.024541228522912264, 0.9996988186962042), + Complex64::new(-0.9996988186962042, 0.024541228522912326), + Complex64::new(0.9999247018391445, 0.012271538285719925), + Complex64::new(-0.012271538285719823, 0.9999247018391445), + Complex64::new(0.6983762494089729, 0.7157308252838186), + Complex64::new(-0.7157308252838186, 0.6983762494089729), + Complex64::new(0.9191138516900578, 0.3939920400610481), + Complex64::new(-0.393992040061048, 0.9191138516900578), + Complex64::new(0.3713171939518376, 0.9285060804732155), + Complex64::new(-0.9285060804732155, 0.3713171939518377), + Complex64::new(0.9783173707196277, 0.20711137619221856), + Complex64::new(-0.20711137619221845, 0.9783173707196277), + Complex64::new(0.5453249884220465, 0.838224705554838), + Complex64::new(-0.8382247055548381, 0.5453249884220464), + Complex64::new(0.8245893027850253, 0.5657318107836131), + Complex64::new(-0.5657318107836132, 0.8245893027850252), + Complex64::new(0.18303988795514106, 0.9831054874312163), + Complex64::new(-0.9831054874312163, 0.1830398879551409), + Complex64::new(0.9939069700023561, 0.11022220729388306), + Complex64::new(-0.11022220729388306, 0.9939069700023561), + Complex64::new(0.6248594881423865, 0.7807372285720944), + Complex64::new(-0.7807372285720945, 0.6248594881423863), + Complex64::new(0.8760700941954066, 0.4821837720791227), + Complex64::new(-0.4821837720791227, 0.8760700941954066), + Complex64::new(0.27851968938505306, 0.9604305194155658), + Complex64::new(-0.9604305194155658, 0.27851968938505317), + Complex64::new(0.9533060403541939, 0.3020059493192281), + Complex64::new(-0.3020059493192281, 0.9533060403541939), + Complex64::new(0.46053871095824, 0.8876396204028539), + Complex64::new(-0.8876396204028538, 0.4605387109582402), + Complex64::new(0.765167265622459, 0.6438315428897914), + Complex64::new(-0.6438315428897913, 0.7651672656224591), + Complex64::new(0.08579731234443988, 0.996312612182778), + Complex64::new(-0.996312612182778, 0.08579731234444016), + Complex64::new(0.9981181129001492, 0.06132073630220858), + Complex64::new(-0.06132073630220853, 0.9981181129001492), + Complex64::new(0.6624157775901718, 0.7491363945234593), + Complex64::new(-0.7491363945234591, 0.662415777590172), + Complex64::new(0.8986744656939538, 0.43861623853852766), + Complex64::new(-0.4386162385385274, 0.8986744656939539), + Complex64::new(0.325310292162263, 0.9456073253805213), + Complex64::new(-0.9456073253805212, 0.32531029216226326), + Complex64::new(0.9669764710448521, 0.25486565960451457), + Complex64::new(-0.2548656596045145, 0.9669764710448521), + Complex64::new(0.5035383837257176, 0.8639728561215867), + Complex64::new(-0.8639728561215867, 0.5035383837257177), + Complex64::new(0.7958369046088836, 0.6055110414043255), + Complex64::new(-0.6055110414043254, 0.7958369046088836), + Complex64::new(0.13458070850712622, 0.99090263542778), + Complex64::new(-0.99090263542778, 0.13458070850712628), + Complex64::new(0.9873014181578584, 0.15885814333386145), + Complex64::new(-0.15885814333386128, 0.9873014181578584), + Complex64::new(0.5857978574564389, 0.8104571982525948), + Complex64::new(-0.8104571982525947, 0.585797857456439), + Complex64::new(0.8513551931052652, 0.524589682678469), + Complex64::new(-0.5245896826784687, 0.8513551931052652), + Complex64::new(0.23105810828067128, 0.9729399522055601), + Complex64::new(-0.9729399522055601, 0.23105810828067133), + Complex64::new(0.937339011912575, 0.34841868024943456), + Complex64::new(-0.3484186802494344, 0.937339011912575), + Complex64::new(0.4164295600976373, 0.9091679830905223), + Complex64::new(-0.9091679830905224, 0.41642956009763715), + Complex64::new(0.7326542716724128, 0.680600997795453), + Complex64::new(-0.680600997795453, 0.7326542716724128), + Complex64::new(0.03680722294135899, 0.9993223845883495), + Complex64::new(-0.9993223845883495, 0.03680722294135883), + Complex64::new(0.9993223845883495, 0.03680722294135883), + Complex64::new(-0.036807222941358866, 0.9993223845883495), + Complex64::new(0.6806009977954531, 0.7326542716724128), + Complex64::new(-0.7326542716724127, 0.6806009977954532), + Complex64::new(0.9091679830905224, 0.41642956009763715), + Complex64::new(-0.416429560097637, 0.9091679830905225), + Complex64::new(0.3484186802494345, 0.937339011912575), + Complex64::new(-0.9373390119125748, 0.3484186802494348), + Complex64::new(0.9729399522055602, 0.2310581082806711), + Complex64::new(-0.23105810828067114, 0.9729399522055602), + Complex64::new(0.5245896826784688, 0.8513551931052652), + Complex64::new(-0.8513551931052652, 0.524589682678469), + Complex64::new(0.8104571982525948, 0.5857978574564389), + Complex64::new(-0.5857978574564389, 0.8104571982525948), + Complex64::new(0.1588581433338614, 0.9873014181578584), + Complex64::new(-0.9873014181578584, 0.15885814333386147), + Complex64::new(0.99090263542778, 0.13458070850712617), + Complex64::new(-0.1345807085071261, 0.99090263542778), + Complex64::new(0.6055110414043255, 0.7958369046088835), + Complex64::new(-0.7958369046088835, 0.6055110414043257), + Complex64::new(0.8639728561215868, 0.5035383837257176), + Complex64::new(-0.5035383837257175, 0.8639728561215868), + Complex64::new(0.2548656596045146, 0.9669764710448521), + Complex64::new(-0.9669764710448521, 0.2548656596045147), + Complex64::new(0.9456073253805213, 0.3253102921622629), + Complex64::new(-0.32531029216226287, 0.9456073253805214), + Complex64::new(0.4386162385385277, 0.8986744656939538), + Complex64::new(-0.8986744656939539, 0.43861623853852755), + Complex64::new(0.7491363945234594, 0.6624157775901718), + Complex64::new(-0.6624157775901719, 0.7491363945234593), + Complex64::new(0.06132073630220865, 0.9981181129001492), + Complex64::new(-0.9981181129001492, 0.06132073630220849), + Complex64::new(0.996312612182778, 0.0857973123444399), + Complex64::new(-0.08579731234443976, 0.996312612182778), + Complex64::new(0.6438315428897915, 0.765167265622459), + Complex64::new(-0.765167265622459, 0.6438315428897914), + Complex64::new(0.8876396204028539, 0.46053871095824), + Complex64::new(-0.46053871095824006, 0.8876396204028539), + Complex64::new(0.3020059493192282, 0.9533060403541938), + Complex64::new(-0.9533060403541939, 0.30200594931922803), + Complex64::new(0.9604305194155658, 0.27851968938505306), + Complex64::new(-0.27851968938505295, 0.9604305194155659), + Complex64::new(0.48218377207912283, 0.8760700941954066), + Complex64::new(-0.8760700941954065, 0.4821837720791229), + Complex64::new(0.7807372285720945, 0.6248594881423863), + Complex64::new(-0.6248594881423862, 0.7807372285720946), + Complex64::new(0.11022220729388318, 0.9939069700023561), + Complex64::new(-0.9939069700023561, 0.11022220729388324), + Complex64::new(0.9831054874312163, 0.18303988795514095), + Complex64::new(-0.18303988795514092, 0.9831054874312163), + Complex64::new(0.5657318107836132, 0.8245893027850253), + Complex64::new(-0.8245893027850251, 0.5657318107836135), + Complex64::new(0.8382247055548381, 0.5453249884220465), + Complex64::new(-0.5453249884220462, 0.8382247055548382), + Complex64::new(0.20711137619221856, 0.9783173707196277), + Complex64::new(-0.9783173707196275, 0.20711137619221884), + Complex64::new(0.9285060804732156, 0.37131719395183754), + Complex64::new(-0.3713171939518375, 0.9285060804732156), + Complex64::new(0.3939920400610481, 0.9191138516900578), + Complex64::new(-0.9191138516900578, 0.39399204006104815), + Complex64::new(0.7157308252838186, 0.6983762494089729), + Complex64::new(-0.6983762494089728, 0.7157308252838187), + Complex64::new(0.012271538285719944, 0.9999247018391445), + Complex64::new(-0.9999247018391445, 0.012271538285720007), + Complex64::new(0.9999811752826011, 0.006135884649154475), + Complex64::new(-0.006135884649154393, 0.9999811752826011), + Complex64::new(0.7027547444572253, 0.7114321957452164), + Complex64::new(-0.7114321957452165, 0.7027547444572252), + Complex64::new(0.921514039342042, 0.38834504669882625), + Complex64::new(-0.3883450466988262, 0.921514039342042), + Complex64::new(0.3770074102164183, 0.9262102421383113), + Complex64::new(-0.9262102421383114, 0.37700741021641815), + Complex64::new(0.9795697656854405, 0.2011046348420919), + Complex64::new(-0.20110463484209182, 0.9795697656854405), + Complex64::new(0.5504579729366048, 0.83486287498638), + Complex64::new(-0.83486287498638, 0.5504579729366049), + Complex64::new(0.8280450452577558, 0.560661576197336), + Complex64::new(-0.5606615761973359, 0.8280450452577558), + Complex64::new(0.18906866414980628, 0.9819638691095552), + Complex64::new(-0.9819638691095552, 0.18906866414980636), + Complex64::new(0.9945645707342554, 0.10412163387205459), + Complex64::new(-0.1041216338720546, 0.9945645707342554), + Complex64::new(0.6296382389149271, 0.7768884656732324), + Complex64::new(-0.7768884656732323, 0.6296382389149272), + Complex64::new(0.8790122264286335, 0.4767992300633221), + Complex64::new(-0.4767992300633219, 0.8790122264286335), + Complex64::new(0.2844075372112718, 0.9587034748958716), + Complex64::new(-0.9587034748958715, 0.2844075372112721), + Complex64::new(0.9551411683057708, 0.2961508882436238), + Complex64::new(-0.29615088824362384, 0.9551411683057707), + Complex64::new(0.4659764957679661, 0.8847970984309378), + Complex64::new(-0.8847970984309378, 0.4659764957679662), + Complex64::new(0.7691033376455797, 0.6391244448637757), + Complex64::new(-0.6391244448637757, 0.7691033376455796), + Complex64::new(0.0919089564971327, 0.9957674144676598), + Complex64::new(-0.9957674144676598, 0.09190895649713275), + Complex64::new(0.9984755805732948, 0.055195244349689934), + Complex64::new(-0.05519524434968991, 0.9984755805732948), + Complex64::new(0.6669999223036375, 0.745057785441466), + Complex64::new(-0.745057785441466, 0.6669999223036376), + Complex64::new(0.901348847046022, 0.43309381885315196), + Complex64::new(-0.4330938188531519, 0.901348847046022), + Complex64::new(0.33110630575987643, 0.9435934581619604), + Complex64::new(-0.9435934581619604, 0.3311063057598765), + Complex64::new(0.9685220942744174, 0.24892760574572015), + Complex64::new(-0.24892760574572012, 0.9685220942744174), + Complex64::new(0.508830142543107, 0.8608669386377673), + Complex64::new(-0.8608669386377671, 0.5088301425431073), + Complex64::new(0.799537269107905, 0.600616479383869), + Complex64::new(-0.6006164793838688, 0.7995372691079052), + Complex64::new(0.14065823933284924, 0.9900582102622971), + Complex64::new(-0.990058210262297, 0.14065823933284954), + Complex64::new(0.9882575677307495, 0.15279718525844344), + Complex64::new(-0.1527971852584433, 0.9882575677307495), + Complex64::new(0.5907597018588743, 0.8068475535437992), + Complex64::new(-0.8068475535437993, 0.5907597018588742), + Complex64::new(0.8545579883654005, 0.5193559901655896), + Complex64::new(-0.5193559901655896, 0.8545579883654005), + Complex64::new(0.23702360599436734, 0.9715038909862518), + Complex64::new(-0.9715038909862518, 0.23702360599436717), + Complex64::new(0.9394592236021899, 0.3426607173119944), + Complex64::new(-0.34266071731199427, 0.9394592236021899), + Complex64::new(0.4220002707997998, 0.9065957045149153), + Complex64::new(-0.9065957045149153, 0.42200027079979985), + Complex64::new(0.7368165688773699, 0.6760927035753159), + Complex64::new(-0.6760927035753158, 0.73681656887737), + Complex64::new(0.04293825693494096, 0.9990777277526454), + Complex64::new(-0.9990777277526454, 0.04293825693494102), + Complex64::new(0.9995294175010931, 0.030674803176636626), + Complex64::new(-0.03067480317663646, 0.9995294175010931), + Complex64::new(0.6850836677727004, 0.7284643904482252), + Complex64::new(-0.7284643904482252, 0.6850836677727004), + Complex64::new(0.9117060320054299, 0.4108431710579039), + Complex64::new(-0.4108431710579038, 0.9117060320054299), + Complex64::new(0.3541635254204905, 0.9351835099389475), + Complex64::new(-0.9351835099389476, 0.3541635254204904), + Complex64::new(0.9743393827855759, 0.22508391135979283), + Complex64::new(-0.22508391135979267, 0.9743393827855759), + Complex64::new(0.5298036246862948, 0.8481203448032971), + Complex64::new(-0.8481203448032971, 0.5298036246862948), + Complex64::new(0.8140363297059484, 0.5808139580957645), + Complex64::new(-0.5808139580957644, 0.8140363297059485), + Complex64::new(0.1649131204899701, 0.9863080972445987), + Complex64::new(-0.9863080972445986, 0.16491312048997014), + Complex64::new(0.9917097536690995, 0.12849811079379317), + Complex64::new(-0.1284981107937931, 0.9917097536690995), + Complex64::new(0.6103828062763095, 0.7921065773002124), + Complex64::new(-0.7921065773002122, 0.6103828062763097), + Complex64::new(0.8670462455156926, 0.49822766697278187), + Complex64::new(-0.4982276669727816, 0.8670462455156928), + Complex64::new(0.26079411791527557, 0.9653944416976894), + Complex64::new(-0.9653944416976893, 0.26079411791527585), + Complex64::new(0.9475855910177411, 0.3195020308160157), + Complex64::new(-0.31950203081601564, 0.9475855910177412), + Complex64::new(0.44412214457042926, 0.8959662497561851), + Complex64::new(-0.8959662497561851, 0.4441221445704293), + Complex64::new(0.7531867990436125, 0.6578066932970786), + Complex64::new(-0.6578066932970786, 0.7531867990436125), + Complex64::new(0.0674439195636641, 0.9977230666441916), + Complex64::new(-0.9977230666441916, 0.06744391956366418), + Complex64::new(0.9968202992911657, 0.07968243797143013), + Complex64::new(-0.07968243797143001, 0.9968202992911658), + Complex64::new(0.6485144010221126, 0.7612023854842618), + Complex64::new(-0.7612023854842617, 0.6485144010221126), + Complex64::new(0.8904487232447579, 0.45508358712634384), + Complex64::new(-0.4550835871263437, 0.890448723244758), + Complex64::new(0.307849640041535, 0.9514350209690083), + Complex64::new(-0.9514350209690083, 0.30784964004153503), + Complex64::new(0.9621214042690416, 0.272621355449949), + Complex64::new(-0.27262135544994887, 0.9621214042690416), + Complex64::new(0.48755016014843605, 0.8730949784182901), + Complex64::new(-0.8730949784182901, 0.4875501601484359), + Complex64::new(0.7845565971555752, 0.6200572117632891), + Complex64::new(-0.6200572117632892, 0.7845565971555751), + Complex64::new(0.11631863091190488, 0.9932119492347945), + Complex64::new(-0.9932119492347945, 0.11631863091190471), + Complex64::new(0.984210092386929, 0.17700422041214875), + Complex64::new(-0.17700422041214875, 0.984210092386929), + Complex64::new(0.5707807458869674, 0.8211025149911046), + Complex64::new(-0.8211025149911046, 0.5707807458869673), + Complex64::new(0.8415549774368984, 0.5401714727298929), + Complex64::new(-0.5401714727298929, 0.8415549774368984), + Complex64::new(0.21311031991609136, 0.9770281426577544), + Complex64::new(-0.9770281426577544, 0.21311031991609142), + Complex64::new(0.9307669610789837, 0.36561299780477385), + Complex64::new(-0.36561299780477385, 0.9307669610789837), + Complex64::new(0.3996241998456468, 0.9166790599210427), + Complex64::new(-0.9166790599210426, 0.39962419984564707), + Complex64::new(0.7200025079613817, 0.693971460889654), + Complex64::new(-0.6939714608896538, 0.7200025079613818), + Complex64::new(0.01840672990580482, 0.9998305817958234), + Complex64::new(-0.9998305817958234, 0.0184067299058051), + Complex64::new(0.9998305817958234, 0.01840672990580482), + Complex64::new(-0.018406729905804695, 0.9998305817958234), + Complex64::new(0.693971460889654, 0.7200025079613817), + Complex64::new(-0.7200025079613817, 0.693971460889654), + Complex64::new(0.9166790599210427, 0.3996241998456468), + Complex64::new(-0.3996241998456467, 0.9166790599210427), + Complex64::new(0.36561299780477396, 0.9307669610789837), + Complex64::new(-0.9307669610789837, 0.3656129978047738), + Complex64::new(0.9770281426577544, 0.21311031991609136), + Complex64::new(-0.21311031991609125, 0.9770281426577544), + Complex64::new(0.540171472729893, 0.8415549774368983), + Complex64::new(-0.8415549774368983, 0.540171472729893), + Complex64::new(0.8211025149911046, 0.5707807458869673), + Complex64::new(-0.5707807458869671, 0.8211025149911048), + Complex64::new(0.17700422041214886, 0.984210092386929), + Complex64::new(-0.984210092386929, 0.17700422041214894), + Complex64::new(0.9932119492347945, 0.11631863091190475), + Complex64::new(-0.11631863091190475, 0.9932119492347945), + Complex64::new(0.6200572117632892, 0.7845565971555752), + Complex64::new(-0.784556597155575, 0.6200572117632894), + Complex64::new(0.8730949784182901, 0.487550160148436), + Complex64::new(-0.4875501601484357, 0.8730949784182902), + Complex64::new(0.272621355449949, 0.9621214042690416), + Complex64::new(-0.9621214042690415, 0.27262135544994925), + Complex64::new(0.9514350209690083, 0.30784964004153487), + Complex64::new(-0.30784964004153487, 0.9514350209690083), + Complex64::new(0.45508358712634384, 0.8904487232447579), + Complex64::new(-0.8904487232447579, 0.4550835871263439), + Complex64::new(0.7612023854842618, 0.6485144010221124), + Complex64::new(-0.6485144010221124, 0.7612023854842619), + Complex64::new(0.07968243797143013, 0.9968202992911657), + Complex64::new(-0.9968202992911657, 0.0796824379714302), + Complex64::new(0.9977230666441916, 0.06744391956366405), + Complex64::new(-0.06744391956366398, 0.9977230666441916), + Complex64::new(0.6578066932970786, 0.7531867990436124), + Complex64::new(-0.7531867990436124, 0.6578066932970787), + Complex64::new(0.8959662497561852, 0.4441221445704292), + Complex64::new(-0.44412214457042914, 0.8959662497561852), + Complex64::new(0.31950203081601575, 0.9475855910177411), + Complex64::new(-0.9475855910177411, 0.3195020308160158), + Complex64::new(0.9653944416976894, 0.2607941179152755), + Complex64::new(-0.26079411791527546, 0.9653944416976894), + Complex64::new(0.49822766697278187, 0.8670462455156926), + Complex64::new(-0.8670462455156928, 0.49822766697278176), + Complex64::new(0.7921065773002124, 0.6103828062763095), + Complex64::new(-0.6103828062763096, 0.7921065773002123), + Complex64::new(0.12849811079379322, 0.9917097536690995), + Complex64::new(-0.9917097536690995, 0.12849811079379309), + Complex64::new(0.9863080972445987, 0.16491312048996992), + Complex64::new(-0.16491312048996995, 0.9863080972445987), + Complex64::new(0.5808139580957645, 0.8140363297059483), + Complex64::new(-0.8140363297059484, 0.5808139580957645), + Complex64::new(0.8481203448032972, 0.5298036246862946), + Complex64::new(-0.5298036246862947, 0.8481203448032972), + Complex64::new(0.22508391135979278, 0.9743393827855759), + Complex64::new(-0.9743393827855759, 0.22508391135979283), + Complex64::new(0.9351835099389476, 0.35416352542049034), + Complex64::new(-0.3541635254204904, 0.9351835099389476), + Complex64::new(0.4108431710579039, 0.9117060320054299), + Complex64::new(-0.9117060320054298, 0.41084317105790413), + Complex64::new(0.7284643904482252, 0.6850836677727004), + Complex64::new(-0.6850836677727002, 0.7284643904482253), + Complex64::new(0.03067480317663658, 0.9995294175010931), + Complex64::new(-0.9995294175010931, 0.030674803176636865), + Complex64::new(0.9990777277526454, 0.04293825693494082), + Complex64::new(-0.042938256934940834, 0.9990777277526454), + Complex64::new(0.676092703575316, 0.7368165688773698), + Complex64::new(-0.7368165688773699, 0.6760927035753159), + Complex64::new(0.9065957045149153, 0.4220002707997997), + Complex64::new(-0.4220002707997997, 0.9065957045149153), + Complex64::new(0.3426607173119944, 0.9394592236021899), + Complex64::new(-0.9394592236021899, 0.34266071731199443), + Complex64::new(0.9715038909862518, 0.2370236059943672), + Complex64::new(-0.23702360599436723, 0.9715038909862518), + Complex64::new(0.5193559901655895, 0.8545579883654005), + Complex64::new(-0.8545579883654004, 0.5193559901655898), + Complex64::new(0.8068475535437993, 0.5907597018588742), + Complex64::new(-0.590759701858874, 0.8068475535437994), + Complex64::new(0.1527971852584434, 0.9882575677307495), + Complex64::new(-0.9882575677307495, 0.15279718525844369), + Complex64::new(0.9900582102622971, 0.1406582393328492), + Complex64::new(-0.14065823933284913, 0.9900582102622971), + Complex64::new(0.600616479383869, 0.799537269107905), + Complex64::new(-0.7995372691079051, 0.6006164793838689), + Complex64::new(0.8608669386377673, 0.508830142543107), + Complex64::new(-0.5088301425431071, 0.8608669386377672), + Complex64::new(0.24892760574572026, 0.9685220942744173), + Complex64::new(-0.9685220942744174, 0.2489276057457201), + Complex64::new(0.9435934581619604, 0.33110630575987643), + Complex64::new(-0.3311063057598763, 0.9435934581619604), + Complex64::new(0.433093818853152, 0.901348847046022), + Complex64::new(-0.9013488470460219, 0.43309381885315207), + Complex64::new(0.7450577854414661, 0.6669999223036375), + Complex64::new(-0.6669999223036374, 0.7450577854414661), + Complex64::new(0.05519524434969003, 0.9984755805732948), + Complex64::new(-0.9984755805732948, 0.055195244349690094), + Complex64::new(0.9957674144676598, 0.09190895649713272), + Complex64::new(-0.09190895649713257, 0.9957674144676598), + Complex64::new(0.6391244448637757, 0.7691033376455796), + Complex64::new(-0.7691033376455795, 0.6391244448637758), + Complex64::new(0.8847970984309378, 0.4659764957679662), + Complex64::new(-0.465976495767966, 0.8847970984309379), + Complex64::new(0.29615088824362396, 0.9551411683057707), + Complex64::new(-0.9551411683057707, 0.296150888243624), + Complex64::new(0.9587034748958716, 0.2844075372112719), + Complex64::new(-0.2844075372112717, 0.9587034748958716), + Complex64::new(0.47679923006332225, 0.8790122264286334), + Complex64::new(-0.8790122264286335, 0.4767992300633221), + Complex64::new(0.7768884656732324, 0.629638238914927), + Complex64::new(-0.6296382389149271, 0.7768884656732324), + Complex64::new(0.10412163387205473, 0.9945645707342554), + Complex64::new(-0.9945645707342554, 0.10412163387205457), + Complex64::new(0.9819638691095552, 0.1890686641498062), + Complex64::new(-0.18906866414980616, 0.9819638691095552), + Complex64::new(0.560661576197336, 0.8280450452577558), + Complex64::new(-0.8280450452577557, 0.5606615761973361), + Complex64::new(0.83486287498638, 0.5504579729366048), + Complex64::new(-0.5504579729366047, 0.8348628749863801), + Complex64::new(0.20110463484209196, 0.9795697656854405), + Complex64::new(-0.9795697656854405, 0.201104634842092), + Complex64::new(0.9262102421383114, 0.37700741021641826), + Complex64::new(-0.3770074102164182, 0.9262102421383114), + Complex64::new(0.3883450466988263, 0.9215140393420419), + Complex64::new(-0.9215140393420418, 0.3883450466988266), + Complex64::new(0.7114321957452164, 0.7027547444572253), + Complex64::new(-0.7027547444572251, 0.7114321957452167), + Complex64::new(0.006135884649154515, 0.9999811752826011), + Complex64::new(-0.9999811752826011, 0.006135884649154799), +]; + +const FELT_BITREVERSED_POWERS: [FalconFelt; 512] = [ + FalconFelt::new(1), + FalconFelt::new(1479), + FalconFelt::new(8246), + FalconFelt::new(5146), + FalconFelt::new(4134), + FalconFelt::new(6553), + FalconFelt::new(11567), + FalconFelt::new(1305), + FalconFelt::new(5860), + FalconFelt::new(3195), + FalconFelt::new(1212), + FalconFelt::new(10643), + FalconFelt::new(3621), + FalconFelt::new(9744), + FalconFelt::new(8785), + FalconFelt::new(3542), + FalconFelt::new(7311), + FalconFelt::new(10938), + FalconFelt::new(8961), + FalconFelt::new(5777), + FalconFelt::new(5023), + FalconFelt::new(6461), + FalconFelt::new(5728), + FalconFelt::new(4591), + FalconFelt::new(3006), + FalconFelt::new(9545), + FalconFelt::new(563), + FalconFelt::new(9314), + FalconFelt::new(2625), + FalconFelt::new(11340), + FalconFelt::new(4821), + FalconFelt::new(2639), + FalconFelt::new(12149), + FalconFelt::new(1853), + FalconFelt::new(726), + FalconFelt::new(4611), + FalconFelt::new(11112), + FalconFelt::new(4255), + FalconFelt::new(2768), + FalconFelt::new(1635), + FalconFelt::new(2963), + FalconFelt::new(7393), + FalconFelt::new(2366), + FalconFelt::new(9238), + FalconFelt::new(9198), + FalconFelt::new(12208), + FalconFelt::new(11289), + FalconFelt::new(7969), + FalconFelt::new(8736), + FalconFelt::new(4805), + FalconFelt::new(11227), + FalconFelt::new(2294), + FalconFelt::new(9542), + FalconFelt::new(4846), + FalconFelt::new(9154), + FalconFelt::new(8577), + FalconFelt::new(9275), + FalconFelt::new(3201), + FalconFelt::new(7203), + FalconFelt::new(10963), + FalconFelt::new(1170), + FalconFelt::new(9970), + FalconFelt::new(955), + FalconFelt::new(11499), + FalconFelt::new(8340), + FalconFelt::new(8993), + FalconFelt::new(2396), + FalconFelt::new(4452), + FalconFelt::new(6915), + FalconFelt::new(2837), + FalconFelt::new(130), + FalconFelt::new(7935), + FalconFelt::new(11336), + FalconFelt::new(3748), + FalconFelt::new(6522), + FalconFelt::new(11462), + FalconFelt::new(5067), + FalconFelt::new(10092), + FalconFelt::new(12171), + FalconFelt::new(9813), + FalconFelt::new(8011), + FalconFelt::new(1673), + FalconFelt::new(5331), + FalconFelt::new(7300), + FalconFelt::new(10908), + FalconFelt::new(9764), + FalconFelt::new(4177), + FalconFelt::new(8705), + FalconFelt::new(480), + FalconFelt::new(9447), + FalconFelt::new(1022), + FalconFelt::new(12280), + FalconFelt::new(5791), + FalconFelt::new(11745), + FalconFelt::new(9821), + FalconFelt::new(11950), + FalconFelt::new(12144), + FalconFelt::new(6747), + FalconFelt::new(8652), + FalconFelt::new(3459), + FalconFelt::new(2731), + FalconFelt::new(8357), + FalconFelt::new(6378), + FalconFelt::new(7399), + FalconFelt::new(10530), + FalconFelt::new(3707), + FalconFelt::new(8595), + FalconFelt::new(5179), + FalconFelt::new(3382), + FalconFelt::new(355), + FalconFelt::new(4231), + FalconFelt::new(2548), + FalconFelt::new(9048), + FalconFelt::new(11560), + FalconFelt::new(3289), + FalconFelt::new(10276), + FalconFelt::new(9005), + FalconFelt::new(9408), + FalconFelt::new(5092), + FalconFelt::new(10200), + FalconFelt::new(6534), + FalconFelt::new(4632), + FalconFelt::new(4388), + FalconFelt::new(1260), + FalconFelt::new(334), + FalconFelt::new(2426), + FalconFelt::new(1428), + FalconFelt::new(10593), + FalconFelt::new(3400), + FalconFelt::new(2399), + FalconFelt::new(5191), + FalconFelt::new(9153), + FalconFelt::new(9273), + FalconFelt::new(243), + FalconFelt::new(3000), + FalconFelt::new(671), + FalconFelt::new(3531), + FalconFelt::new(11813), + FalconFelt::new(3985), + FalconFelt::new(7384), + FalconFelt::new(10111), + FalconFelt::new(10745), + FalconFelt::new(6730), + FalconFelt::new(11869), + FalconFelt::new(9042), + FalconFelt::new(2686), + FalconFelt::new(2969), + FalconFelt::new(3978), + FalconFelt::new(8779), + FalconFelt::new(6957), + FalconFelt::new(9424), + FalconFelt::new(2370), + FalconFelt::new(8241), + FalconFelt::new(10040), + FalconFelt::new(9405), + FalconFelt::new(11136), + FalconFelt::new(3186), + FalconFelt::new(5407), + FalconFelt::new(10163), + FalconFelt::new(1630), + FalconFelt::new(3271), + FalconFelt::new(8232), + FalconFelt::new(10600), + FalconFelt::new(8925), + FalconFelt::new(4414), + FalconFelt::new(2847), + FalconFelt::new(10115), + FalconFelt::new(4372), + FalconFelt::new(9509), + FalconFelt::new(5195), + FalconFelt::new(7394), + FalconFelt::new(10805), + FalconFelt::new(9984), + FalconFelt::new(7247), + FalconFelt::new(4053), + FalconFelt::new(9644), + FalconFelt::new(12176), + FalconFelt::new(4919), + FalconFelt::new(2166), + FalconFelt::new(8374), + FalconFelt::new(12129), + FalconFelt::new(9140), + FalconFelt::new(7852), + FalconFelt::new(3), + FalconFelt::new(1426), + FalconFelt::new(7635), + FalconFelt::new(10512), + FalconFelt::new(1663), + FalconFelt::new(8653), + FalconFelt::new(4938), + FalconFelt::new(2704), + FalconFelt::new(5291), + FalconFelt::new(5277), + FalconFelt::new(1168), + FalconFelt::new(11082), + FalconFelt::new(9041), + FalconFelt::new(2143), + FalconFelt::new(11224), + FalconFelt::new(11885), + FalconFelt::new(4645), + FalconFelt::new(4096), + FalconFelt::new(11796), + FalconFelt::new(5444), + FalconFelt::new(2381), + FalconFelt::new(10911), + FalconFelt::new(1912), + FalconFelt::new(4337), + FalconFelt::new(11854), + FalconFelt::new(4976), + FalconFelt::new(10682), + FalconFelt::new(11414), + FalconFelt::new(8509), + FalconFelt::new(11287), + FalconFelt::new(5011), + FalconFelt::new(8005), + FalconFelt::new(5088), + FalconFelt::new(9852), + FalconFelt::new(8643), + FalconFelt::new(9302), + FalconFelt::new(6267), + FalconFelt::new(2422), + FalconFelt::new(6039), + FalconFelt::new(2187), + FalconFelt::new(2566), + FalconFelt::new(10849), + FalconFelt::new(8526), + FalconFelt::new(9223), + FalconFelt::new(27), + FalconFelt::new(7205), + FalconFelt::new(1632), + FalconFelt::new(7404), + FalconFelt::new(1017), + FalconFelt::new(4143), + FalconFelt::new(7575), + FalconFelt::new(12047), + FalconFelt::new(10752), + FalconFelt::new(8585), + FalconFelt::new(2678), + FalconFelt::new(7270), + FalconFelt::new(11744), + FalconFelt::new(3833), + FalconFelt::new(3778), + FalconFelt::new(11899), + FalconFelt::new(773), + FalconFelt::new(5101), + FalconFelt::new(11222), + FalconFelt::new(9888), + FalconFelt::new(442), + FalconFelt::new(9377), + FalconFelt::new(6591), + FalconFelt::new(354), + FalconFelt::new(7428), + FalconFelt::new(5012), + FalconFelt::new(2481), + FalconFelt::new(1045), + FalconFelt::new(9430), + FalconFelt::new(10302), + FalconFelt::new(10587), + FalconFelt::new(8724), + FalconFelt::new(11635), + FalconFelt::new(7083), + FalconFelt::new(5529), + FalconFelt::new(9090), + FalconFelt::new(12233), + FalconFelt::new(6152), + FalconFelt::new(4948), + FalconFelt::new(400), + FalconFelt::new(1728), + FalconFelt::new(6427), + FalconFelt::new(6136), + FalconFelt::new(6874), + FalconFelt::new(3643), + FalconFelt::new(10930), + FalconFelt::new(5435), + FalconFelt::new(1254), + FalconFelt::new(11316), + FalconFelt::new(10256), + FalconFelt::new(3998), + FalconFelt::new(10367), + FalconFelt::new(8410), + FalconFelt::new(11821), + FalconFelt::new(8301), + FalconFelt::new(11907), + FalconFelt::new(316), + FalconFelt::new(6950), + FalconFelt::new(5446), + FalconFelt::new(6093), + FalconFelt::new(3710), + FalconFelt::new(7822), + FalconFelt::new(4789), + FalconFelt::new(7540), + FalconFelt::new(5537), + FalconFelt::new(3789), + FalconFelt::new(147), + FalconFelt::new(5456), + FalconFelt::new(7840), + FalconFelt::new(11239), + FalconFelt::new(7753), + FalconFelt::new(5445), + FalconFelt::new(3860), + FalconFelt::new(9606), + FalconFelt::new(1190), + FalconFelt::new(8471), + FalconFelt::new(6118), + FalconFelt::new(5925), + FalconFelt::new(1018), + FalconFelt::new(8775), + FalconFelt::new(1041), + FalconFelt::new(1973), + FalconFelt::new(5574), + FalconFelt::new(11011), + FalconFelt::new(2344), + FalconFelt::new(4075), + FalconFelt::new(5315), + FalconFelt::new(4324), + FalconFelt::new(4916), + FalconFelt::new(10120), + FalconFelt::new(11767), + FalconFelt::new(7210), + FalconFelt::new(9027), + FalconFelt::new(6281), + FalconFelt::new(11404), + FalconFelt::new(7280), + FalconFelt::new(1956), + FalconFelt::new(11286), + FalconFelt::new(3532), + FalconFelt::new(12048), + FalconFelt::new(12231), + FalconFelt::new(1105), + FalconFelt::new(12147), + FalconFelt::new(5681), + FalconFelt::new(8812), + FalconFelt::new(8851), + FalconFelt::new(2844), + FalconFelt::new(975), + FalconFelt::new(4212), + FalconFelt::new(8687), + FalconFelt::new(6068), + FalconFelt::new(421), + FalconFelt::new(8209), + FalconFelt::new(3600), + FalconFelt::new(3263), + FalconFelt::new(7665), + FalconFelt::new(6077), + FalconFelt::new(4782), + FalconFelt::new(6403), + FalconFelt::new(9260), + FalconFelt::new(5594), + FalconFelt::new(8076), + FalconFelt::new(11785), + FalconFelt::new(605), + FalconFelt::new(9987), + FalconFelt::new(5468), + FalconFelt::new(1010), + FalconFelt::new(787), + FalconFelt::new(8807), + FalconFelt::new(5241), + FalconFelt::new(9369), + FalconFelt::new(9162), + FalconFelt::new(8120), + FalconFelt::new(5057), + FalconFelt::new(7591), + FalconFelt::new(3445), + FalconFelt::new(7509), + FalconFelt::new(2049), + FalconFelt::new(7377), + FalconFelt::new(10968), + FalconFelt::new(192), + FalconFelt::new(431), + FalconFelt::new(10710), + FalconFelt::new(2505), + FalconFelt::new(5906), + FalconFelt::new(12138), + FalconFelt::new(10162), + FalconFelt::new(8332), + FalconFelt::new(9450), + FalconFelt::new(6415), + FalconFelt::new(677), + FalconFelt::new(6234), + FalconFelt::new(3336), + FalconFelt::new(12237), + FalconFelt::new(9115), + FalconFelt::new(1323), + FalconFelt::new(2766), + FalconFelt::new(3150), + FalconFelt::new(1319), + FalconFelt::new(8243), + FalconFelt::new(709), + FalconFelt::new(8049), + FalconFelt::new(8719), + FalconFelt::new(11454), + FalconFelt::new(6224), + FalconFelt::new(922), + FalconFelt::new(11848), + FalconFelt::new(8210), + FalconFelt::new(1058), + FalconFelt::new(1958), + FalconFelt::new(7967), + FalconFelt::new(10211), + FalconFelt::new(11177), + FalconFelt::new(64), + FalconFelt::new(8633), + FalconFelt::new(11606), + FalconFelt::new(9830), + FalconFelt::new(6507), + FalconFelt::new(1566), + FalconFelt::new(2948), + FalconFelt::new(9786), + FalconFelt::new(6370), + FalconFelt::new(7856), + FalconFelt::new(3834), + FalconFelt::new(5257), + FalconFelt::new(10542), + FalconFelt::new(9166), + FalconFelt::new(9235), + FalconFelt::new(5486), + FalconFelt::new(1404), + FalconFelt::new(11964), + FalconFelt::new(1146), + FalconFelt::new(11341), + FalconFelt::new(3728), + FalconFelt::new(8240), + FalconFelt::new(6299), + FalconFelt::new(1159), + FalconFelt::new(6099), + FalconFelt::new(295), + FalconFelt::new(5766), + FalconFelt::new(11637), + FalconFelt::new(8527), + FalconFelt::new(2919), + FalconFelt::new(8273), + FalconFelt::new(8212), + FalconFelt::new(3329), + FalconFelt::new(7991), + FalconFelt::new(9597), + FalconFelt::new(168), + FalconFelt::new(10695), + FalconFelt::new(1962), + FalconFelt::new(5106), + FalconFelt::new(6328), + FalconFelt::new(5297), + FalconFelt::new(6170), + FalconFelt::new(3956), + FalconFelt::new(1360), + FalconFelt::new(11089), + FalconFelt::new(7105), + FalconFelt::new(9734), + FalconFelt::new(6167), + FalconFelt::new(9407), + FalconFelt::new(1805), + FalconFelt::new(1954), + FalconFelt::new(2051), + FalconFelt::new(6142), + FalconFelt::new(2447), + FalconFelt::new(3963), + FalconFelt::new(11713), + FalconFelt::new(8855), + FalconFelt::new(8760), + FalconFelt::new(9381), + FalconFelt::new(218), + FalconFelt::new(9928), + FalconFelt::new(10446), + FalconFelt::new(9259), + FalconFelt::new(4115), + FalconFelt::new(5333), + FalconFelt::new(10258), + FalconFelt::new(5876), + FalconFelt::new(2281), + FalconFelt::new(156), + FalconFelt::new(9522), + FalconFelt::new(8320), + FalconFelt::new(3991), + FalconFelt::new(453), + FalconFelt::new(6381), + FalconFelt::new(11871), + FalconFelt::new(8517), + FalconFelt::new(4774), + FalconFelt::new(6860), + FalconFelt::new(4737), + FalconFelt::new(1293), + FalconFelt::new(10232), + FalconFelt::new(5369), + FalconFelt::new(9087), + FalconFelt::new(7796), + FalconFelt::new(350), + FalconFelt::new(1512), + FalconFelt::new(10474), + FalconFelt::new(6906), + FalconFelt::new(1489), + FalconFelt::new(2500), + FalconFelt::new(1583), + FalconFelt::new(6347), + FalconFelt::new(11026), + FalconFelt::new(12240), + FalconFelt::new(6374), + FalconFelt::new(1483), + FalconFelt::new(3009), + FalconFelt::new(1693), + FalconFelt::new(723), + FalconFelt::new(174), + FalconFelt::new(2738), + FalconFelt::new(6421), + FalconFelt::new(2655), + FalconFelt::new(6554), + FalconFelt::new(10314), + FalconFelt::new(3757), + FalconFelt::new(9364), + FalconFelt::new(11942), + FalconFelt::new(7535), + FalconFelt::new(10431), + FalconFelt::new(426), + FalconFelt::new(3315), +]; + +const FELT_BITREVERSED_POWERS_INVERSE: [FalconFelt; 512] = [ + FalconFelt::new(1), + FalconFelt::new(10810), + FalconFelt::new(7143), + FalconFelt::new(4043), + FalconFelt::new(10984), + FalconFelt::new(722), + FalconFelt::new(5736), + FalconFelt::new(8155), + FalconFelt::new(8747), + FalconFelt::new(3504), + FalconFelt::new(2545), + FalconFelt::new(8668), + FalconFelt::new(1646), + FalconFelt::new(11077), + FalconFelt::new(9094), + FalconFelt::new(6429), + FalconFelt::new(9650), + FalconFelt::new(7468), + FalconFelt::new(949), + FalconFelt::new(9664), + FalconFelt::new(2975), + FalconFelt::new(11726), + FalconFelt::new(2744), + FalconFelt::new(9283), + FalconFelt::new(7698), + FalconFelt::new(6561), + FalconFelt::new(5828), + FalconFelt::new(7266), + FalconFelt::new(6512), + FalconFelt::new(3328), + FalconFelt::new(1351), + FalconFelt::new(4978), + FalconFelt::new(790), + FalconFelt::new(11334), + FalconFelt::new(2319), + FalconFelt::new(11119), + FalconFelt::new(1326), + FalconFelt::new(5086), + FalconFelt::new(9088), + FalconFelt::new(3014), + FalconFelt::new(3712), + FalconFelt::new(3135), + FalconFelt::new(7443), + FalconFelt::new(2747), + FalconFelt::new(9995), + FalconFelt::new(1062), + FalconFelt::new(7484), + FalconFelt::new(3553), + FalconFelt::new(4320), + FalconFelt::new(1000), + FalconFelt::new(81), + FalconFelt::new(3091), + FalconFelt::new(3051), + FalconFelt::new(9923), + FalconFelt::new(4896), + FalconFelt::new(9326), + FalconFelt::new(10654), + FalconFelt::new(9521), + FalconFelt::new(8034), + FalconFelt::new(1177), + FalconFelt::new(7678), + FalconFelt::new(11563), + FalconFelt::new(10436), + FalconFelt::new(140), + FalconFelt::new(1696), + FalconFelt::new(10861), + FalconFelt::new(9863), + FalconFelt::new(11955), + FalconFelt::new(11029), + FalconFelt::new(7901), + FalconFelt::new(7657), + FalconFelt::new(5755), + FalconFelt::new(2089), + FalconFelt::new(7197), + FalconFelt::new(2881), + FalconFelt::new(3284), + FalconFelt::new(2013), + FalconFelt::new(9000), + FalconFelt::new(729), + FalconFelt::new(3241), + FalconFelt::new(9741), + FalconFelt::new(8058), + FalconFelt::new(11934), + FalconFelt::new(8907), + FalconFelt::new(7110), + FalconFelt::new(3694), + FalconFelt::new(8582), + FalconFelt::new(1759), + FalconFelt::new(4890), + FalconFelt::new(5911), + FalconFelt::new(3932), + FalconFelt::new(9558), + FalconFelt::new(8830), + FalconFelt::new(3637), + FalconFelt::new(5542), + FalconFelt::new(145), + FalconFelt::new(339), + FalconFelt::new(2468), + FalconFelt::new(544), + FalconFelt::new(6498), + FalconFelt::new(9), + FalconFelt::new(11267), + FalconFelt::new(2842), + FalconFelt::new(11809), + FalconFelt::new(3584), + FalconFelt::new(8112), + FalconFelt::new(2525), + FalconFelt::new(1381), + FalconFelt::new(4989), + FalconFelt::new(6958), + FalconFelt::new(10616), + FalconFelt::new(4278), + FalconFelt::new(2476), + FalconFelt::new(118), + FalconFelt::new(2197), + FalconFelt::new(7222), + FalconFelt::new(827), + FalconFelt::new(5767), + FalconFelt::new(8541), + FalconFelt::new(953), + FalconFelt::new(4354), + FalconFelt::new(12159), + FalconFelt::new(9452), + FalconFelt::new(5374), + FalconFelt::new(7837), + FalconFelt::new(9893), + FalconFelt::new(3296), + FalconFelt::new(3949), + FalconFelt::new(2859), + FalconFelt::new(11244), + FalconFelt::new(9808), + FalconFelt::new(7277), + FalconFelt::new(4861), + FalconFelt::new(11935), + FalconFelt::new(5698), + FalconFelt::new(2912), + FalconFelt::new(11847), + FalconFelt::new(2401), + FalconFelt::new(1067), + FalconFelt::new(7188), + FalconFelt::new(11516), + FalconFelt::new(390), + FalconFelt::new(8511), + FalconFelt::new(8456), + FalconFelt::new(545), + FalconFelt::new(5019), + FalconFelt::new(9611), + FalconFelt::new(3704), + FalconFelt::new(1537), + FalconFelt::new(242), + FalconFelt::new(4714), + FalconFelt::new(8146), + FalconFelt::new(11272), + FalconFelt::new(4885), + FalconFelt::new(10657), + FalconFelt::new(5084), + FalconFelt::new(12262), + FalconFelt::new(3066), + FalconFelt::new(3763), + FalconFelt::new(1440), + FalconFelt::new(9723), + FalconFelt::new(10102), + FalconFelt::new(6250), + FalconFelt::new(9867), + FalconFelt::new(6022), + FalconFelt::new(2987), + FalconFelt::new(3646), + FalconFelt::new(2437), + FalconFelt::new(7201), + FalconFelt::new(4284), + FalconFelt::new(7278), + FalconFelt::new(1002), + FalconFelt::new(3780), + FalconFelt::new(875), + FalconFelt::new(1607), + FalconFelt::new(7313), + FalconFelt::new(435), + FalconFelt::new(7952), + FalconFelt::new(10377), + FalconFelt::new(1378), + FalconFelt::new(9908), + FalconFelt::new(6845), + FalconFelt::new(493), + FalconFelt::new(8193), + FalconFelt::new(7644), + FalconFelt::new(404), + FalconFelt::new(1065), + FalconFelt::new(10146), + FalconFelt::new(3248), + FalconFelt::new(1207), + FalconFelt::new(11121), + FalconFelt::new(7012), + FalconFelt::new(6998), + FalconFelt::new(9585), + FalconFelt::new(7351), + FalconFelt::new(3636), + FalconFelt::new(10626), + FalconFelt::new(1777), + FalconFelt::new(4654), + FalconFelt::new(10863), + FalconFelt::new(12286), + FalconFelt::new(4437), + FalconFelt::new(3149), + FalconFelt::new(160), + FalconFelt::new(3915), + FalconFelt::new(10123), + FalconFelt::new(7370), + FalconFelt::new(113), + FalconFelt::new(2645), + FalconFelt::new(8236), + FalconFelt::new(5042), + FalconFelt::new(2305), + FalconFelt::new(1484), + FalconFelt::new(4895), + FalconFelt::new(7094), + FalconFelt::new(2780), + FalconFelt::new(7917), + FalconFelt::new(2174), + FalconFelt::new(9442), + FalconFelt::new(7875), + FalconFelt::new(3364), + FalconFelt::new(1689), + FalconFelt::new(4057), + FalconFelt::new(9018), + FalconFelt::new(10659), + FalconFelt::new(2126), + FalconFelt::new(6882), + FalconFelt::new(9103), + FalconFelt::new(1153), + FalconFelt::new(2884), + FalconFelt::new(2249), + FalconFelt::new(4048), + FalconFelt::new(9919), + FalconFelt::new(2865), + FalconFelt::new(5332), + FalconFelt::new(3510), + FalconFelt::new(8311), + FalconFelt::new(9320), + FalconFelt::new(9603), + FalconFelt::new(3247), + FalconFelt::new(420), + FalconFelt::new(5559), + FalconFelt::new(1544), + FalconFelt::new(2178), + FalconFelt::new(4905), + FalconFelt::new(8304), + FalconFelt::new(476), + FalconFelt::new(8758), + FalconFelt::new(11618), + FalconFelt::new(9289), + FalconFelt::new(12046), + FalconFelt::new(3016), + FalconFelt::new(3136), + FalconFelt::new(7098), + FalconFelt::new(9890), + FalconFelt::new(8889), + FalconFelt::new(8974), + FalconFelt::new(11863), + FalconFelt::new(1858), + FalconFelt::new(4754), + FalconFelt::new(347), + FalconFelt::new(2925), + FalconFelt::new(8532), + FalconFelt::new(1975), + FalconFelt::new(5735), + FalconFelt::new(9634), + FalconFelt::new(5868), + FalconFelt::new(9551), + FalconFelt::new(12115), + FalconFelt::new(11566), + FalconFelt::new(10596), + FalconFelt::new(9280), + FalconFelt::new(10806), + FalconFelt::new(5915), + FalconFelt::new(49), + FalconFelt::new(1263), + FalconFelt::new(5942), + FalconFelt::new(10706), + FalconFelt::new(9789), + FalconFelt::new(10800), + FalconFelt::new(5383), + FalconFelt::new(1815), + FalconFelt::new(10777), + FalconFelt::new(11939), + FalconFelt::new(4493), + FalconFelt::new(3202), + FalconFelt::new(6920), + FalconFelt::new(2057), + FalconFelt::new(10996), + FalconFelt::new(7552), + FalconFelt::new(5429), + FalconFelt::new(7515), + FalconFelt::new(3772), + FalconFelt::new(418), + FalconFelt::new(5908), + FalconFelt::new(11836), + FalconFelt::new(8298), + FalconFelt::new(3969), + FalconFelt::new(2767), + FalconFelt::new(12133), + FalconFelt::new(10008), + FalconFelt::new(6413), + FalconFelt::new(2031), + FalconFelt::new(6956), + FalconFelt::new(8174), + FalconFelt::new(3030), + FalconFelt::new(1843), + FalconFelt::new(2361), + FalconFelt::new(12071), + FalconFelt::new(2908), + FalconFelt::new(3529), + FalconFelt::new(3434), + FalconFelt::new(576), + FalconFelt::new(8326), + FalconFelt::new(9842), + FalconFelt::new(6147), + FalconFelt::new(10238), + FalconFelt::new(10335), + FalconFelt::new(10484), + FalconFelt::new(2882), + FalconFelt::new(6122), + FalconFelt::new(2555), + FalconFelt::new(5184), + FalconFelt::new(1200), + FalconFelt::new(10929), + FalconFelt::new(8333), + FalconFelt::new(6119), + FalconFelt::new(6992), + FalconFelt::new(5961), + FalconFelt::new(7183), + FalconFelt::new(10327), + FalconFelt::new(1594), + FalconFelt::new(12121), + FalconFelt::new(2692), + FalconFelt::new(4298), + FalconFelt::new(8960), + FalconFelt::new(4077), + FalconFelt::new(4016), + FalconFelt::new(9370), + FalconFelt::new(3762), + FalconFelt::new(652), + FalconFelt::new(6523), + FalconFelt::new(11994), + FalconFelt::new(6190), + FalconFelt::new(11130), + FalconFelt::new(5990), + FalconFelt::new(4049), + FalconFelt::new(8561), + FalconFelt::new(948), + FalconFelt::new(11143), + FalconFelt::new(325), + FalconFelt::new(10885), + FalconFelt::new(6803), + FalconFelt::new(3054), + FalconFelt::new(3123), + FalconFelt::new(1747), + FalconFelt::new(7032), + FalconFelt::new(8455), + FalconFelt::new(4433), + FalconFelt::new(5919), + FalconFelt::new(2503), + FalconFelt::new(9341), + FalconFelt::new(10723), + FalconFelt::new(5782), + FalconFelt::new(2459), + FalconFelt::new(683), + FalconFelt::new(3656), + FalconFelt::new(12225), + FalconFelt::new(1112), + FalconFelt::new(2078), + FalconFelt::new(4322), + FalconFelt::new(10331), + FalconFelt::new(11231), + FalconFelt::new(4079), + FalconFelt::new(441), + FalconFelt::new(11367), + FalconFelt::new(6065), + FalconFelt::new(835), + FalconFelt::new(3570), + FalconFelt::new(4240), + FalconFelt::new(11580), + FalconFelt::new(4046), + FalconFelt::new(10970), + FalconFelt::new(9139), + FalconFelt::new(9523), + FalconFelt::new(10966), + FalconFelt::new(3174), + FalconFelt::new(52), + FalconFelt::new(8953), + FalconFelt::new(6055), + FalconFelt::new(11612), + FalconFelt::new(5874), + FalconFelt::new(2839), + FalconFelt::new(3957), + FalconFelt::new(2127), + FalconFelt::new(151), + FalconFelt::new(6383), + FalconFelt::new(9784), + FalconFelt::new(1579), + FalconFelt::new(11858), + FalconFelt::new(12097), + FalconFelt::new(1321), + FalconFelt::new(4912), + FalconFelt::new(10240), + FalconFelt::new(4780), + FalconFelt::new(8844), + FalconFelt::new(4698), + FalconFelt::new(7232), + FalconFelt::new(4169), + FalconFelt::new(3127), + FalconFelt::new(2920), + FalconFelt::new(7048), + FalconFelt::new(3482), + FalconFelt::new(11502), + FalconFelt::new(11279), + FalconFelt::new(6821), + FalconFelt::new(2302), + FalconFelt::new(11684), + FalconFelt::new(504), + FalconFelt::new(4213), + FalconFelt::new(6695), + FalconFelt::new(3029), + FalconFelt::new(5886), + FalconFelt::new(7507), + FalconFelt::new(6212), + FalconFelt::new(4624), + FalconFelt::new(9026), + FalconFelt::new(8689), + FalconFelt::new(4080), + FalconFelt::new(11868), + FalconFelt::new(6221), + FalconFelt::new(3602), + FalconFelt::new(8077), + FalconFelt::new(11314), + FalconFelt::new(9445), + FalconFelt::new(3438), + FalconFelt::new(3477), + FalconFelt::new(6608), + FalconFelt::new(142), + FalconFelt::new(11184), + FalconFelt::new(58), + FalconFelt::new(241), + FalconFelt::new(8757), + FalconFelt::new(1003), + FalconFelt::new(10333), + FalconFelt::new(5009), + FalconFelt::new(885), + FalconFelt::new(6008), + FalconFelt::new(3262), + FalconFelt::new(5079), + FalconFelt::new(522), + FalconFelt::new(2169), + FalconFelt::new(7373), + FalconFelt::new(7965), + FalconFelt::new(6974), + FalconFelt::new(8214), + FalconFelt::new(9945), + FalconFelt::new(1278), + FalconFelt::new(6715), + FalconFelt::new(10316), + FalconFelt::new(11248), + FalconFelt::new(3514), + FalconFelt::new(11271), + FalconFelt::new(6364), + FalconFelt::new(6171), + FalconFelt::new(3818), + FalconFelt::new(11099), + FalconFelt::new(2683), + FalconFelt::new(8429), + FalconFelt::new(6844), + FalconFelt::new(4536), + FalconFelt::new(1050), + FalconFelt::new(4449), + FalconFelt::new(6833), + FalconFelt::new(12142), + FalconFelt::new(8500), + FalconFelt::new(6752), + FalconFelt::new(4749), + FalconFelt::new(7500), + FalconFelt::new(4467), + FalconFelt::new(8579), + FalconFelt::new(6196), + FalconFelt::new(6843), + FalconFelt::new(5339), + FalconFelt::new(11973), + FalconFelt::new(382), + FalconFelt::new(3988), + FalconFelt::new(468), + FalconFelt::new(3879), + FalconFelt::new(1922), + FalconFelt::new(8291), + FalconFelt::new(2033), + FalconFelt::new(973), + FalconFelt::new(11035), + FalconFelt::new(6854), + FalconFelt::new(1359), + FalconFelt::new(8646), + FalconFelt::new(5415), + FalconFelt::new(6153), + FalconFelt::new(5862), + FalconFelt::new(10561), + FalconFelt::new(11889), + FalconFelt::new(7341), + FalconFelt::new(6137), + FalconFelt::new(56), + FalconFelt::new(3199), + FalconFelt::new(6760), + FalconFelt::new(5206), + FalconFelt::new(654), + FalconFelt::new(3565), + FalconFelt::new(1702), + FalconFelt::new(1987), +]; + +const FELT_NINV_1: FalconFelt = FalconFelt::new(1); +const FELT_NINV_2: FalconFelt = FalconFelt::new(6145); +const FELT_NINV_4: FalconFelt = FalconFelt::new(9217); +const FELT_NINV_8: FalconFelt = FalconFelt::new(10753); +const FELT_NINV_16: FalconFelt = FalconFelt::new(11521); +const FELT_NINV_32: FalconFelt = FalconFelt::new(11905); +const FELT_NINV_64: FalconFelt = FalconFelt::new(12097); +const FELT_NINV_128: FalconFelt = FalconFelt::new(12193); +const FELT_NINV_256: FalconFelt = FalconFelt::new(12241); +const FELT_NINV_512: FalconFelt = FalconFelt::new(12265); diff --git a/src/dsa/rpo_falcon512/math/field.rs b/src/dsa/rpo_falcon512/math/field.rs new file mode 100644 index 000000000..c05b6a74b --- /dev/null +++ b/src/dsa/rpo_falcon512/math/field.rs @@ -0,0 +1,172 @@ +use super::{fft::CyclotomicFourier, Inverse, MODULUS}; +use alloc::string::String; +use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; +use num::{One, Zero}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub struct FalconFelt(u32); + +impl FalconFelt { + pub const fn new(value: i16) -> Self { + let gtz_bool = value >= 0; + let gtz_int = gtz_bool as i16; + let gtz_sign = gtz_int - ((!gtz_bool) as i16); + let reduced = gtz_sign * (gtz_sign * value) % MODULUS; + let canonical_representative = (reduced + MODULUS * (1 - gtz_int)) as u32; + FalconFelt(canonical_representative) + } + + pub const fn value(&self) -> i16 { + self.0 as i16 + } + + pub fn balanced_value(&self) -> i16 { + let value = self.value(); + let g = (value > ((MODULUS) / 2)) as i16; + value - (MODULUS) * g + } + + pub const fn multiply(&self, other: Self) -> Self { + FalconFelt((self.0 * other.0) % MODULUS as u32) + } +} + +impl Add for FalconFelt { + type Output = Self; + + #[allow(clippy::suspicious_arithmetic_impl)] + fn add(self, rhs: Self) -> Self::Output { + let (s, _) = self.0.overflowing_add(rhs.0); + let (d, n) = s.overflowing_sub(MODULUS as u32); + let (r, _) = d.overflowing_add(MODULUS as u32 * (n as u32)); + FalconFelt(r) + } +} + +impl AddAssign for FalconFelt { + fn add_assign(&mut self, rhs: Self) { + *self = *self + rhs; + } +} + +impl Sub for FalconFelt { + type Output = Self; + + fn sub(self, rhs: Self) -> Self::Output { + self + -rhs + } +} + +impl SubAssign for FalconFelt { + fn sub_assign(&mut self, rhs: Self) { + *self = *self - rhs; + } +} + +impl Neg for FalconFelt { + type Output = FalconFelt; + + fn neg(self) -> Self::Output { + let is_nonzero = self.0 != 0; + let r = MODULUS as u32 - self.0; + FalconFelt(r * (is_nonzero as u32)) + } +} + +impl Mul for FalconFelt { + fn mul(self, rhs: Self) -> Self::Output { + FalconFelt((self.0 * rhs.0) % MODULUS as u32) + } + + type Output = Self; +} + +impl MulAssign for FalconFelt { + fn mul_assign(&mut self, rhs: Self) { + *self = *self * rhs; + } +} + +impl Div for FalconFelt { + type Output = FalconFelt; + + #[allow(clippy::suspicious_arithmetic_impl)] + fn div(self, rhs: Self) -> Self::Output { + self * rhs.inverse_or_zero() + } +} + +impl DivAssign for FalconFelt { + fn div_assign(&mut self, rhs: Self) { + *self = *self / rhs + } +} + +impl Zero for FalconFelt { + fn zero() -> Self { + FalconFelt::new(0) + } + + fn is_zero(&self) -> bool { + self.0 == 0 + } +} + +impl One for FalconFelt { + fn one() -> Self { + FalconFelt::new(1) + } +} + +impl Inverse for FalconFelt { + fn inverse_or_zero(self) -> Self { + // q-2 = 0b10 11 11 11 11 11 11 + let two = self.multiply(self); + let three = two.multiply(self); + let six = three.multiply(three); + let twelve = six.multiply(six); + let fifteen = twelve.multiply(three); + let thirty = fifteen.multiply(fifteen); + let sixty = thirty.multiply(thirty); + let sixty_three = sixty.multiply(three); + + let sixty_three_sq = sixty_three.multiply(sixty_three); + let sixty_three_qu = sixty_three_sq.multiply(sixty_three_sq); + let sixty_three_oc = sixty_three_qu.multiply(sixty_three_qu); + let sixty_three_hx = sixty_three_oc.multiply(sixty_three_oc); + let sixty_three_tt = sixty_three_hx.multiply(sixty_three_hx); + let sixty_three_sf = sixty_three_tt.multiply(sixty_three_tt); + + let all_ones = sixty_three_sf.multiply(sixty_three); + let two_e_twelve = all_ones.multiply(self); + let two_e_thirteen = two_e_twelve.multiply(two_e_twelve); + + two_e_thirteen.multiply(all_ones) + } +} + +impl CyclotomicFourier for FalconFelt { + fn primitive_root_of_unity(n: usize) -> Self { + let log2n = n.ilog2(); + assert!(log2n <= 12); + // and 1331 is a twelfth root of unity + let mut a = FalconFelt::new(1331); + let num_squarings = 12 - n.ilog2(); + for _ in 0..num_squarings { + a *= a; + } + a + } +} + +impl TryFrom for FalconFelt { + type Error = String; + + fn try_from(value: u32) -> Result { + if value >= MODULUS as u32 { + Err(format!("value {value} is greater than or equal to the field modulus {MODULUS}")) + } else { + Ok(FalconFelt::new(value as i16)) + } + } +} diff --git a/src/dsa/rpo_falcon512/math/mod.rs b/src/dsa/rpo_falcon512/math/mod.rs new file mode 100644 index 000000000..8d0ed17f5 --- /dev/null +++ b/src/dsa/rpo_falcon512/math/mod.rs @@ -0,0 +1,320 @@ +//! Contains different structs and methods related to the Falcon DSA. +//! +//! It uses and acknowledges the work in: +//! +//! 1. The [reference](https://falcon-sign.info/impl/README.txt.html) implementation by Thomas Pornin. +//! 2. The [Rust](https://github.com/aszepieniec/falcon-rust) implementation by Alan Szepieniec. +use super::MODULUS; +use alloc::{string::String, vec::Vec}; +use core::ops::MulAssign; +use num::{BigInt, FromPrimitive, One, Zero}; +use num_complex::Complex64; +use rand::Rng; + +#[cfg(not(feature = "std"))] +use num::Float; + +mod fft; +pub use fft::{CyclotomicFourier, FastFft}; + +mod field; +pub use field::FalconFelt; + +mod ffsampling; +pub use ffsampling::{ffldl, ffsampling, gram, normalize_tree, LdlTree}; + +mod samplerz; +use self::samplerz::sampler_z; + +mod polynomial; +pub use polynomial::Polynomial; + +pub trait Inverse: Copy + Zero + MulAssign + One { + /// Gets the inverse of a, or zero if it is zero. + fn inverse_or_zero(self) -> Self; + + /// Gets the inverses of a batch of elements, and skip over any that are zero. + fn batch_inverse_or_zero(batch: &[Self]) -> Vec { + let mut acc = Self::one(); + let mut rp: Vec = Vec::with_capacity(batch.len()); + for batch_item in batch { + if !batch_item.is_zero() { + rp.push(acc); + acc = *batch_item * acc; + } else { + rp.push(Self::zero()); + } + } + let mut inv = Self::inverse_or_zero(acc); + for i in (0..batch.len()).rev() { + if !batch[i].is_zero() { + rp[i] *= inv; + inv *= batch[i]; + } + } + rp + } +} + +impl Inverse for Complex64 { + fn inverse_or_zero(self) -> Self { + let modulus = self.re * self.re + self.im * self.im; + Complex64::new(self.re / modulus, -self.im / modulus) + } + fn batch_inverse_or_zero(batch: &[Self]) -> Vec { + batch.iter().map(|&c| Complex64::new(1.0, 0.0) / c).collect() + } +} + +impl Inverse for f64 { + fn inverse_or_zero(self) -> Self { + 1.0 / self + } + fn batch_inverse_or_zero(batch: &[Self]) -> Vec { + batch.iter().map(|&c| 1.0 / c).collect() + } +} + +/// Samples 4 small polynomials f, g, F, G such that f * G - g * F = q mod (X^n + 1). +/// Algorithm 5 (NTRUgen) of the documentation [1, p.34]. +/// +/// [1]: https://falcon-sign.info/falcon.pdf +pub(crate) fn ntru_gen(n: usize, rng: &mut R) -> [Polynomial; 4] { + loop { + let f = gen_poly(n, rng); + let g = gen_poly(n, rng); + let f_ntt = f.map(|&i| FalconFelt::new(i)).fft(); + if f_ntt.coefficients.iter().any(|e| e.is_zero()) { + continue; + } + let gamma = gram_schmidt_norm_squared(&f, &g); + if gamma > 1.3689f64 * (MODULUS as f64) { + continue; + } + + if let Some((capital_f, capital_g)) = + ntru_solve(&f.map(|&i| i.into()), &g.map(|&i| i.into())) + { + return [ + f, + g, + capital_f.map(|i| i.try_into().unwrap()), + capital_g.map(|i| i.try_into().unwrap()), + ]; + } + } +} + +/// Solves the NTRU equation. Given f, g in ZZ[X], find F, G in ZZ[X] such that: +/// +/// f G - g F = q mod (X^n + 1) +/// +/// Algorithm 6 of the specification [1, p.35]. +/// +/// [1]: https://falcon-sign.info/falcon.pdf +fn ntru_solve( + f: &Polynomial, + g: &Polynomial, +) -> Option<(Polynomial, Polynomial)> { + let n = f.coefficients.len(); + if n == 1 { + let (gcd, u, v) = xgcd(&f.coefficients[0], &g.coefficients[0]); + if gcd != BigInt::one() { + return None; + } + return Some(( + (Polynomial::new(vec![-v * BigInt::from_u32(MODULUS as u32).unwrap()])), + Polynomial::new(vec![u * BigInt::from_u32(MODULUS as u32).unwrap()]), + )); + } + + let f_prime = f.field_norm(); + let g_prime = g.field_norm(); + + let (capital_f_prime, capital_g_prime) = ntru_solve(&f_prime, &g_prime)?; + let capital_f_prime_xsq = capital_f_prime.lift_next_cyclotomic(); + let capital_g_prime_xsq = capital_g_prime.lift_next_cyclotomic(); + + let f_minx = f.galois_adjoint(); + let g_minx = g.galois_adjoint(); + + let mut capital_f = (capital_f_prime_xsq.karatsuba(&g_minx)).reduce_by_cyclotomic(n); + let mut capital_g = (capital_g_prime_xsq.karatsuba(&f_minx)).reduce_by_cyclotomic(n); + + match babai_reduce(f, g, &mut capital_f, &mut capital_g) { + Ok(_) => Some((capital_f, capital_g)), + Err(_e) => { + #[cfg(test)] + { + panic!("{}", _e); + } + #[cfg(not(test))] + { + None + } + } + } +} + +/// Generates a polynomial of degree at most n-1 whose coefficients are distributed according +/// to a discrete Gaussian with mu = 0 and sigma = 1.17 * sqrt(Q / (2n)). +fn gen_poly(n: usize, rng: &mut R) -> Polynomial { + let mu = 0.0; + let sigma_star = 1.43300980528773; + Polynomial { + coefficients: (0..4096) + .map(|_| sampler_z(mu, sigma_star, sigma_star - 0.001, rng)) + .collect::>() + .chunks(4096 / n) + .map(|ch| ch.iter().sum()) + .collect(), + } +} + +/// Computes the Gram-Schmidt norm of B = [[g, -f], [G, -F]] from f and g. +/// Corresponds to line 9 in algorithm 5 of the spec [1, p.34] +/// +/// [1]: https://falcon-sign.info/falcon.pdf +fn gram_schmidt_norm_squared(f: &Polynomial, g: &Polynomial) -> f64 { + let n = f.coefficients.len(); + let norm_f_squared = f.l2_norm_squared(); + let norm_g_squared = g.l2_norm_squared(); + let gamma1 = norm_f_squared + norm_g_squared; + + let f_fft = f.map(|i| Complex64::new(*i as f64, 0.0)).fft(); + let g_fft = g.map(|i| Complex64::new(*i as f64, 0.0)).fft(); + let f_adj_fft = f_fft.map(|c| c.conj()); + let g_adj_fft = g_fft.map(|c| c.conj()); + let ffgg_fft = f_fft.hadamard_mul(&f_adj_fft) + g_fft.hadamard_mul(&g_adj_fft); + let ffgg_fft_inverse = ffgg_fft.hadamard_inv(); + let qf_over_ffgg_fft = f_adj_fft.map(|c| c * (MODULUS as f64)).hadamard_mul(&ffgg_fft_inverse); + let qg_over_ffgg_fft = g_adj_fft.map(|c| c * (MODULUS as f64)).hadamard_mul(&ffgg_fft_inverse); + let norm_f_over_ffgg_squared = + qf_over_ffgg_fft.coefficients.iter().map(|c| (c * c.conj()).re).sum::() / (n as f64); + let norm_g_over_ffgg_squared = + qg_over_ffgg_fft.coefficients.iter().map(|c| (c * c.conj()).re).sum::() / (n as f64); + + let gamma2 = norm_f_over_ffgg_squared + norm_g_over_ffgg_squared; + + f64::max(gamma1, gamma2) +} + +/// Reduces the vector (F,G) relative to (f,g). This method follows the python implementation [1]. +/// Note that this algorithm can end up in an infinite loop. (It's one of the things the author +/// would like to fix.) When this happens, control returns an error (hence the return type) and +/// generates another keypair with fresh randomness. +/// +/// Algorithm 7 in the spec [2, p.35] +/// +/// [1]: https://github.com/tprest/falcon.py +/// +/// [2]: https://falcon-sign.info/falcon.pdf +fn babai_reduce( + f: &Polynomial, + g: &Polynomial, + capital_f: &mut Polynomial, + capital_g: &mut Polynomial, +) -> Result<(), String> { + let bitsize = |bi: &BigInt| (bi.bits() + 7) & (u64::MAX ^ 7); + let n = f.coefficients.len(); + let size = [ + f.map(bitsize).fold(0, |a, &b| u64::max(a, b)), + g.map(bitsize).fold(0, |a, &b| u64::max(a, b)), + 53, + ] + .into_iter() + .max() + .unwrap(); + let shift = (size as i64) - 53; + let f_adjusted = f + .map(|bi| Complex64::new(i64::try_from(bi >> shift).unwrap() as f64, 0.0)) + .fft(); + let g_adjusted = g + .map(|bi| Complex64::new(i64::try_from(bi >> shift).unwrap() as f64, 0.0)) + .fft(); + + let f_star_adjusted = f_adjusted.map(|c| c.conj()); + let g_star_adjusted = g_adjusted.map(|c| c.conj()); + let denominator_fft = + f_adjusted.hadamard_mul(&f_star_adjusted) + g_adjusted.hadamard_mul(&g_star_adjusted); + + let mut counter = 0; + loop { + let capital_size = [ + capital_f.map(bitsize).fold(0, |a, &b| u64::max(a, b)), + capital_g.map(bitsize).fold(0, |a, &b| u64::max(a, b)), + 53, + ] + .into_iter() + .max() + .unwrap(); + + if capital_size < size { + break; + } + let capital_shift = (capital_size as i64) - 53; + let capital_f_adjusted = capital_f + .map(|bi| Complex64::new(i64::try_from(bi >> capital_shift).unwrap() as f64, 0.0)) + .fft(); + let capital_g_adjusted = capital_g + .map(|bi| Complex64::new(i64::try_from(bi >> capital_shift).unwrap() as f64, 0.0)) + .fft(); + + let numerator = capital_f_adjusted.hadamard_mul(&f_star_adjusted) + + capital_g_adjusted.hadamard_mul(&g_star_adjusted); + let quotient = numerator.hadamard_div(&denominator_fft).ifft(); + + let k = quotient.map(|f| Into::::into(f.re.round() as i64)); + + if k.is_zero() { + break; + } + let kf = (k.clone().karatsuba(f)) + .reduce_by_cyclotomic(n) + .map(|bi| bi << (capital_size - size)); + let kg = (k.clone().karatsuba(g)) + .reduce_by_cyclotomic(n) + .map(|bi| bi << (capital_size - size)); + *capital_f -= kf; + *capital_g -= kg; + + counter += 1; + if counter > 1000 { + // If we get here, that means that (with high likelihood) we are in an + // infinite loop. We know it happens from time to time -- seldomly, but it + // does. It would be nice to fix that! But in order to fix it we need to be + // able to reproduce it, and for that we need test vectors. So print them + // and hope that one day they circle back to the implementor. + return Err(format!("Encountered infinite loop in babai_reduce of falcon-rust.\n\\ + Please help the developer(s) fix it! You can do this by sending them the inputs to the function that caused the behavior:\n\\ + f: {:?}\n\\ + g: {:?}\n\\ + capital_f: {:?}\n\\ + capital_g: {:?}\n", f.coefficients, g.coefficients, capital_f.coefficients, capital_g.coefficients)); + } + } + Ok(()) +} + +/// Extended Euclidean algorithm for computing the greatest common divisor (g) and +/// Bézout coefficients (u, v) for the relation +/// +/// $$ u a + v b = g . $$ +/// +/// Implementation adapted from Wikipedia [1]. +/// +/// [1]: https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm#Pseudocode +fn xgcd(a: &BigInt, b: &BigInt) -> (BigInt, BigInt, BigInt) { + let (mut old_r, mut r) = (a.clone(), b.clone()); + let (mut old_s, mut s) = (BigInt::one(), BigInt::zero()); + let (mut old_t, mut t) = (BigInt::zero(), BigInt::one()); + + while r != BigInt::zero() { + let quotient = old_r.clone() / r.clone(); + (old_r, r) = (r.clone(), old_r.clone() - quotient.clone() * r); + (old_s, s) = (s.clone(), old_s.clone() - quotient.clone() * s); + (old_t, t) = (t.clone(), old_t.clone() - quotient * t); + } + + (old_r, old_s, old_t) +} diff --git a/src/dsa/rpo_falcon512/math/polynomial.rs b/src/dsa/rpo_falcon512/math/polynomial.rs new file mode 100644 index 000000000..8b5030508 --- /dev/null +++ b/src/dsa/rpo_falcon512/math/polynomial.rs @@ -0,0 +1,616 @@ +use super::{field::FalconFelt, Inverse}; +use crate::dsa::rpo_falcon512::{MODULUS, N}; +use crate::Felt; +use alloc::vec::Vec; +use core::default::Default; +use core::fmt::Debug; +use core::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub, SubAssign}; +use num::{One, Zero}; + +#[derive(Debug, Clone, Default)] +pub struct Polynomial { + pub coefficients: Vec, +} + +impl Polynomial +where + F: Clone, +{ + pub fn new(coefficients: Vec) -> Self { + Self { coefficients } + } +} + +impl< + F: Mul + Sub + AddAssign + Zero + Div + Clone + Inverse, + > Polynomial +{ + pub fn hadamard_mul(&self, other: &Self) -> Self { + Polynomial::new( + self.coefficients + .iter() + .zip(other.coefficients.iter()) + .map(|(a, b)| *a * *b) + .collect(), + ) + } + pub fn hadamard_div(&self, other: &Self) -> Self { + let other_coefficients_inverse = F::batch_inverse_or_zero(&other.coefficients); + Polynomial::new( + self.coefficients + .iter() + .zip(other_coefficients_inverse.iter()) + .map(|(a, b)| *a * *b) + .collect(), + ) + } + + pub fn hadamard_inv(&self) -> Self { + let coefficients_inverse = F::batch_inverse_or_zero(&self.coefficients); + Polynomial::new(coefficients_inverse) + } +} + +impl Polynomial { + pub fn degree(&self) -> Option { + if self.coefficients.is_empty() { + return None; + } + let mut max_index = self.coefficients.len() - 1; + while self.coefficients[max_index] == F::zero() { + if let Some(new_index) = max_index.checked_sub(1) { + max_index = new_index; + } else { + return None; + } + } + Some(max_index) + } + + pub fn lc(&self) -> F { + match self.degree() { + Some(non_negative_degree) => self.coefficients[non_negative_degree].clone(), + None => F::zero(), + } + } +} + +/// The following implementations are specific to cyclotomic polynomial rings, +/// i.e., F[ X ] / , and are used extensively in Falcon. +impl< + F: One + + Zero + + Clone + + Neg + + MulAssign + + AddAssign + + Div + + Sub + + PartialEq, + > Polynomial +{ + /// Reduce the polynomial by X^n + 1. + pub fn reduce_by_cyclotomic(&self, n: usize) -> Self { + let mut coefficients = vec![F::zero(); n]; + let mut sign = -F::one(); + for (i, c) in self.coefficients.iter().cloned().enumerate() { + if i % n == 0 { + sign *= -F::one(); + } + coefficients[i % n] += sign.clone() * c; + } + Polynomial::new(coefficients) + } + + /// Computes the field norm of the polynomial as an element of the cyclotomic ring + /// F[ X ] / relative to one of half the size, i.e., F[ X ] / . + /// + /// Corresponds to formula 3.25 in the spec [1, p.30]. + /// + /// [1]: https://falcon-sign.info/falcon.pdf + pub fn field_norm(&self) -> Self { + let n = self.coefficients.len(); + let mut f0_coefficients = vec![F::zero(); n / 2]; + let mut f1_coefficients = vec![F::zero(); n / 2]; + for i in 0..n / 2 { + f0_coefficients[i] = self.coefficients[2 * i].clone(); + f1_coefficients[i] = self.coefficients[2 * i + 1].clone(); + } + let f0 = Polynomial::new(f0_coefficients); + let f1 = Polynomial::new(f1_coefficients); + let f0_squared = (f0.clone() * f0).reduce_by_cyclotomic(n / 2); + let f1_squared = (f1.clone() * f1).reduce_by_cyclotomic(n / 2); + let x = Polynomial::new(vec![F::zero(), F::one()]); + f0_squared - (x * f1_squared).reduce_by_cyclotomic(n / 2) + } + + /// Lifts an element from a cyclotomic polynomial ring to one of double the size. + pub fn lift_next_cyclotomic(&self) -> Self { + let n = self.coefficients.len(); + let mut coefficients = vec![F::zero(); n * 2]; + for i in 0..n { + coefficients[2 * i] = self.coefficients[i].clone(); + } + Self::new(coefficients) + } + + /// Computes the galois adjoint of the polynomial in the cyclotomic ring F[ X ] / < X^n + 1 > , + /// which corresponds to f(x^2). + pub fn galois_adjoint(&self) -> Self { + Self::new( + self.coefficients + .iter() + .enumerate() + .map(|(i, c)| if i % 2 == 0 { c.clone() } else { c.clone().neg() }) + .collect(), + ) + } +} + +impl> Polynomial { + pub(crate) fn l2_norm_squared(&self) -> f64 { + self.coefficients + .iter() + .map(|i| Into::::into(i.clone())) + .map(|i| i * i) + .sum::() + } +} + +impl PartialEq for Polynomial +where + F: Zero + PartialEq + Clone + AddAssign, +{ + fn eq(&self, other: &Self) -> bool { + if self.is_zero() && other.is_zero() { + true + } else if self.is_zero() || other.is_zero() { + false + } else { + let self_degree = self.degree().unwrap(); + let other_degree = other.degree().unwrap(); + self.coefficients[0..=self_degree] == other.coefficients[0..=other_degree] + } + } +} + +impl Eq for Polynomial where F: Zero + PartialEq + Clone + AddAssign {} + +impl Add for &Polynomial +where + F: Add + AddAssign + Clone, +{ + type Output = Polynomial; + + fn add(self, rhs: Self) -> Self::Output { + let coefficients = if self.coefficients.len() >= rhs.coefficients.len() { + let mut coefficients = self.coefficients.clone(); + for (i, c) in rhs.coefficients.iter().enumerate() { + coefficients[i] += c.clone(); + } + coefficients + } else { + let mut coefficients = rhs.coefficients.clone(); + for (i, c) in self.coefficients.iter().enumerate() { + coefficients[i] += c.clone(); + } + coefficients + }; + Self::Output { coefficients } + } +} + +impl Add for Polynomial +where + F: Add + AddAssign + Clone, +{ + type Output = Polynomial; + fn add(self, rhs: Self) -> Self::Output { + let coefficients = if self.coefficients.len() >= rhs.coefficients.len() { + let mut coefficients = self.coefficients.clone(); + for (i, c) in rhs.coefficients.into_iter().enumerate() { + coefficients[i] += c; + } + coefficients + } else { + let mut coefficients = rhs.coefficients.clone(); + for (i, c) in self.coefficients.into_iter().enumerate() { + coefficients[i] += c; + } + coefficients + }; + Self::Output { coefficients } + } +} + +impl AddAssign for Polynomial +where + F: Add + AddAssign + Clone, +{ + fn add_assign(&mut self, rhs: Self) { + if self.coefficients.len() >= rhs.coefficients.len() { + for (i, c) in rhs.coefficients.into_iter().enumerate() { + self.coefficients[i] += c; + } + } else { + let mut coefficients = rhs.coefficients.clone(); + for (i, c) in self.coefficients.iter().enumerate() { + coefficients[i] += c.clone(); + } + self.coefficients = coefficients; + } + } +} + +impl Sub for &Polynomial +where + F: Sub + Clone + Neg + Add + AddAssign, +{ + type Output = Polynomial; + + fn sub(self, rhs: Self) -> Self::Output { + self + &(-rhs) + } +} + +impl Sub for Polynomial +where + F: Sub + Clone + Neg + Add + AddAssign, +{ + type Output = Polynomial; + + fn sub(self, rhs: Self) -> Self::Output { + self + (-rhs) + } +} + +impl SubAssign for Polynomial +where + F: Add + Neg + AddAssign + Clone + Sub, +{ + fn sub_assign(&mut self, rhs: Self) { + self.coefficients = self.clone().sub(rhs).coefficients; + } +} + +impl + Clone> Neg for &Polynomial { + type Output = Polynomial; + + fn neg(self) -> Self::Output { + Self::Output { + coefficients: self.coefficients.iter().cloned().map(|a| -a).collect(), + } + } +} + +impl + Clone> Neg for Polynomial { + type Output = Self; + + fn neg(self) -> Self::Output { + Self::Output { + coefficients: self.coefficients.iter().cloned().map(|a| -a).collect(), + } + } +} + +impl Mul for &Polynomial +where + F: Add + AddAssign + Mul + Sub + Zero + PartialEq + Clone, +{ + type Output = Polynomial; + + fn mul(self, other: Self) -> Self::Output { + if self.is_zero() || other.is_zero() { + return Polynomial::::zero(); + } + let mut coefficients = + vec![F::zero(); self.coefficients.len() + other.coefficients.len() - 1]; + for i in 0..self.coefficients.len() { + for j in 0..other.coefficients.len() { + coefficients[i + j] += self.coefficients[i].clone() * other.coefficients[j].clone(); + } + } + Polynomial { coefficients } + } +} + +impl Mul for Polynomial +where + F: Add + AddAssign + Mul + Zero + PartialEq + Clone, +{ + type Output = Self; + + fn mul(self, other: Self) -> Self::Output { + if self.is_zero() || other.is_zero() { + return Self::zero(); + } + let mut coefficients = + vec![F::zero(); self.coefficients.len() + other.coefficients.len() - 1]; + for i in 0..self.coefficients.len() { + for j in 0..other.coefficients.len() { + coefficients[i + j] += self.coefficients[i].clone() * other.coefficients[j].clone(); + } + } + Self { coefficients } + } +} + +impl + Zero + Clone> Mul for &Polynomial { + type Output = Polynomial; + + fn mul(self, other: F) -> Self::Output { + Polynomial { + coefficients: self.coefficients.iter().cloned().map(|i| i * other.clone()).collect(), + } + } +} + +impl + Zero + Clone> Mul for Polynomial { + type Output = Polynomial; + + fn mul(self, other: F) -> Self::Output { + Polynomial { + coefficients: self.coefficients.iter().cloned().map(|i| i * other.clone()).collect(), + } + } +} + +impl + Sub + AddAssign + Zero + Div + Clone> + Polynomial +{ + /// Multiply two polynomials using Karatsuba's divide-and-conquer algorithm. + pub fn karatsuba(&self, other: &Self) -> Self { + Polynomial::new(vector_karatsuba(&self.coefficients, &other.coefficients)) + } +} + +impl One for Polynomial +where + F: Clone + One + PartialEq + Zero + AddAssign, +{ + fn one() -> Self { + Self { coefficients: vec![F::one()] } + } +} + +impl Zero for Polynomial +where + F: Zero + PartialEq + Clone + AddAssign, +{ + fn zero() -> Self { + Self { coefficients: vec![] } + } + + fn is_zero(&self) -> bool { + self.degree().is_none() + } +} + +impl Polynomial { + pub fn shift(&self, shamt: usize) -> Self { + Self { + coefficients: [vec![F::zero(); shamt], self.coefficients.clone()].concat(), + } + } + + pub fn constant(f: F) -> Self { + Self { coefficients: vec![f] } + } + + pub fn map G>(&self, closure: C) -> Polynomial { + Polynomial::::new(self.coefficients.iter().map(closure).collect()) + } + + pub fn fold G + Clone>(&self, mut initial_value: G, closure: C) -> G { + for c in self.coefficients.iter() { + initial_value = (closure.clone())(initial_value, c); + } + initial_value + } +} + +impl Div> for Polynomial +where + F: Zero + + One + + PartialEq + + AddAssign + + Clone + + Mul + + MulAssign + + Div + + Neg + + Sub, +{ + type Output = Polynomial; + + fn div(self, denominator: Self) -> Self::Output { + if denominator.is_zero() { + panic!(); + } + if self.is_zero() { + Self::zero(); + } + let mut remainder = self.clone(); + let mut quotient = Polynomial::::zero(); + while remainder.degree().unwrap() >= denominator.degree().unwrap() { + let shift = remainder.degree().unwrap() - denominator.degree().unwrap(); + let quotient_coefficient = remainder.lc() / denominator.lc(); + let monomial = Self::constant(quotient_coefficient).shift(shift); + quotient += monomial.clone(); + remainder -= monomial * denominator.clone(); + if remainder.is_zero() { + break; + } + } + quotient + } +} + +fn vector_karatsuba< + F: Zero + AddAssign + Mul + Sub + Div + Clone, +>( + left: &[F], + right: &[F], +) -> Vec { + let n = left.len(); + if n <= 8 { + let mut product = vec![F::zero(); left.len() + right.len() - 1]; + for (i, l) in left.iter().enumerate() { + for (j, r) in right.iter().enumerate() { + product[i + j] += l.clone() * r.clone(); + } + } + return product; + } + let n_over_2 = n / 2; + let mut product = vec![F::zero(); 2 * n - 1]; + let left_lo = &left[0..n_over_2]; + let right_lo = &right[0..n_over_2]; + let left_hi = &left[n_over_2..]; + let right_hi = &right[n_over_2..]; + let left_sum: Vec = + left_lo.iter().zip(left_hi).map(|(a, b)| a.clone() + b.clone()).collect(); + let right_sum: Vec = + right_lo.iter().zip(right_hi).map(|(a, b)| a.clone() + b.clone()).collect(); + + let prod_lo = vector_karatsuba(left_lo, right_lo); + let prod_hi = vector_karatsuba(left_hi, right_hi); + let prod_mid: Vec = vector_karatsuba(&left_sum, &right_sum) + .iter() + .zip(prod_lo.iter().zip(prod_hi.iter())) + .map(|(s, (l, h))| s.clone() - (l.clone() + h.clone())) + .collect(); + + for (i, l) in prod_lo.into_iter().enumerate() { + product[i] = l; + } + for (i, m) in prod_mid.into_iter().enumerate() { + product[i + n_over_2] += m; + } + for (i, h) in prod_hi.into_iter().enumerate() { + product[i + n] += h + } + product +} + +impl From> for Polynomial { + fn from(item: Polynomial) -> Self { + let res: Vec = + item.coefficients.iter().map(|a| Felt::from(a.value() as u16)).collect(); + Polynomial::new(res) + } +} + +impl From<&Polynomial> for Polynomial { + fn from(item: &Polynomial) -> Self { + let res: Vec = + item.coefficients.iter().map(|a| Felt::from(a.value() as u16)).collect(); + Polynomial::new(res) + } +} + +impl From> for Polynomial { + fn from(item: Polynomial) -> Self { + let res: Vec = item.coefficients.iter().map(|&a| FalconFelt::new(a)).collect(); + Polynomial::new(res) + } +} + +impl From<&Polynomial> for Polynomial { + fn from(item: &Polynomial) -> Self { + let res: Vec = item.coefficients.iter().map(|&a| FalconFelt::new(a)).collect(); + Polynomial::new(res) + } +} + +impl From> for Polynomial { + fn from(item: Vec) -> Self { + let res: Vec = item.iter().map(|&a| FalconFelt::new(a)).collect(); + Polynomial::new(res) + } +} + +impl From<&Vec> for Polynomial { + fn from(item: &Vec) -> Self { + let res: Vec = item.iter().map(|&a| FalconFelt::new(a)).collect(); + Polynomial::new(res) + } +} + +impl Polynomial { + pub fn norm_squared(&self) -> u64 { + self.coefficients + .iter() + .map(|&i| i.balanced_value() as i64) + .map(|i| (i * i) as u64) + .sum::() + } + + // PUBLIC ACCESSORS + // -------------------------------------------------------------------------------------------- + + /// Returns the coefficients of this polynomial as field elements. + pub fn to_elements(&self) -> Vec { + self.coefficients.iter().map(|&a| Felt::from(a.value() as u16)).collect() + } + + // POLYNOMIAL OPERATIONS + // -------------------------------------------------------------------------------------------- + + /// Multiplies two polynomials over Z_p\[x\] without reducing modulo p. Given that the degrees + /// of the input polynomials are less than 512 and their coefficients are less than the modulus + /// q equal to 12289, the resulting product polynomial is guaranteed to have coefficients less + /// than the Miden prime. + /// + /// Note that this multiplication is not over Z_p\[x\]/(phi). + pub fn mul_modulo_p(a: &Self, b: &Self) -> [u64; 1024] { + let mut c = [0; 2 * N]; + for i in 0..N { + for j in 0..N { + c[i + j] += a.coefficients[i].value() as u64 * b.coefficients[j].value() as u64; + } + } + + c + } + + /// Reduces a polynomial, that is the product of two polynomials over Z_p\[x\], modulo + /// the irreducible polynomial phi. This results in an element in Z_p\[x\]/(phi). + pub fn reduce_negacyclic(a: &[u64; 1024]) -> Self { + let mut c = [FalconFelt::zero(); N]; + let modulus = MODULUS as u16; + for i in 0..N { + let ai = a[N + i] % modulus as u64; + let neg_ai = (modulus - ai as u16) % modulus; + + let bi = (a[i] % modulus as u64) as u16; + c[i] = FalconFelt::new(((neg_ai + bi) % modulus) as i16); + } + + Self::new(c.to_vec()) + } +} + +// TESTS +// ================================================================================================ + +#[cfg(test)] +mod tests { + use super::{FalconFelt, Polynomial, N}; + + #[test] + fn test_negacyclic_reduction() { + let coef1: [u8; N] = rand_utils::rand_array(); + let coef2: [u8; N] = rand_utils::rand_array(); + + let poly1 = Polynomial::new(coef1.iter().map(|&a| FalconFelt::new(a as i16)).collect()); + let poly2 = Polynomial::new(coef2.iter().map(|&a| FalconFelt::new(a as i16)).collect()); + let prod = poly1.clone() * poly2.clone(); + + assert_eq!( + prod.reduce_by_cyclotomic(N), + Polynomial::reduce_negacyclic(&Polynomial::mul_modulo_p(&poly1, &poly2)) + ); + } +} diff --git a/src/dsa/rpo_falcon512/math/samplerz.rs b/src/dsa/rpo_falcon512/math/samplerz.rs new file mode 100644 index 000000000..af4057f67 --- /dev/null +++ b/src/dsa/rpo_falcon512/math/samplerz.rs @@ -0,0 +1,298 @@ +use core::f64::consts::LN_2; +use rand::Rng; + +#[cfg(not(feature = "std"))] +use num::Float; + +/// Samples an integer from {0, ..., 18} according to the distribution χ, which is close to +/// the half-Gaussian distribution on the natural numbers with mean 0 and standard deviation +/// equal to sigma_max. +fn base_sampler(bytes: [u8; 9]) -> i16 { + const RCDT: [u128; 18] = [ + 3024686241123004913666, + 1564742784480091954050, + 636254429462080897535, + 199560484645026482916, + 47667343854657281903, + 8595902006365044063, + 1163297957344668388, + 117656387352093658, + 8867391802663976, + 496969357462633, + 20680885154299, + 638331848991, + 14602316184, + 247426747, + 3104126, + 28824, + 198, + 1, + ]; + let u = u128::from_be_bytes([vec![0u8; 7], bytes.to_vec()].concat().try_into().unwrap()); + RCDT.into_iter().filter(|r| u < *r).count() as i16 +} + +/// Computes an integer approximation of 2^63 * ccs * exp(-x). +fn approx_exp(x: f64, ccs: f64) -> u64 { + // The constants C are used to approximate exp(-x); these + // constants are taken from FACCT (up to a scaling factor + // of 2^63): + // https://eprint.iacr.org/2018/1234 + // https://github.com/raykzhao/gaussian + const C: [u64; 13] = [ + 0x00000004741183A3u64, + 0x00000036548CFC06u64, + 0x0000024FDCBF140Au64, + 0x0000171D939DE045u64, + 0x0000D00CF58F6F84u64, + 0x000680681CF796E3u64, + 0x002D82D8305B0FEAu64, + 0x011111110E066FD0u64, + 0x0555555555070F00u64, + 0x155555555581FF00u64, + 0x400000000002B400u64, + 0x7FFFFFFFFFFF4800u64, + 0x8000000000000000u64, + ]; + + let mut z: u64; + let mut y: u64; + let twoe63 = 1u64 << 63; + + y = C[0]; + z = f64::floor(x * (twoe63 as f64)) as u64; + for cu in C.iter().skip(1) { + let zy = (z as u128) * (y as u128); + y = cu - ((zy >> 63) as u64); + } + + z = f64::floor((twoe63 as f64) * ccs) as u64; + + (((z as u128) * (y as u128)) >> 63) as u64 +} + +/// A random bool that is true with probability ≈ ccs · exp(-x). +fn ber_exp(x: f64, ccs: f64, random_bytes: [u8; 7]) -> bool { + // 0.69314718055994530941 = ln(2) + let s = f64::floor(x / LN_2) as usize; + let r = x - LN_2 * (s as f64); + let shamt = usize::min(s, 63); + let z = ((((approx_exp(r, ccs) as u128) << 1) - 1) >> shamt) as u64; + let mut w = 0i16; + for (index, i) in (0..64).step_by(8).rev().enumerate() { + let byte = random_bytes[index]; + w = (byte as i16) - (((z >> i) & 0xff) as i16); + if w != 0 { + break; + } + } + w < 0 +} + +/// Samples an integer from the Gaussian distribution with given mean (mu) and standard deviation +/// (sigma). +pub(crate) fn sampler_z(mu: f64, sigma: f64, sigma_min: f64, rng: &mut R) -> i16 { + const SIGMA_MAX: f64 = 1.8205; + const INV_2SIGMA_MAX_SQ: f64 = 1f64 / (2f64 * SIGMA_MAX * SIGMA_MAX); + let isigma = 1f64 / sigma; + let dss = 0.5f64 * isigma * isigma; + let s = f64::floor(mu); + let r = mu - s; + let ccs = sigma_min * isigma; + loop { + let z0 = base_sampler(rng.gen()); + let random_byte: u8 = rng.gen(); + let b = (random_byte & 1) as i16; + let z = b + ((b << 1) - 1) * z0; + let zf_min_r = (z as f64) - r; + // x = ((z-r)^2)/(2*sigma^2) - ((z-b)^2)/(2*sigma0^2) + let x = zf_min_r * zf_min_r * dss - (z0 * z0) as f64 * INV_2SIGMA_MAX_SQ; + if ber_exp(x, ccs, rng.gen()) { + return z + (s as i16); + } + } +} + +#[cfg(all(test, feature = "std"))] +mod test { + use alloc::vec::Vec; + use rand::RngCore; + use std::{thread::sleep, time::Duration}; + + use super::{approx_exp, ber_exp, sampler_z}; + + /// RNG used only for testing purposes, whereby the produced + /// string of random bytes is equal to the one it is initialized + /// with. Whatever you do, do not use this RNG in production. + struct UnsafeBufferRng { + buffer: Vec, + index: usize, + } + + impl UnsafeBufferRng { + fn new(buffer: &[u8]) -> Self { + Self { buffer: buffer.to_vec(), index: 0 } + } + + fn next(&mut self) -> u8 { + if self.buffer.len() <= self.index { + // panic!("Ran out of buffer."); + sleep(Duration::from_millis(10)); + 0u8 + } else { + let return_value = self.buffer[self.index]; + self.index += 1; + return_value + } + } + } + + impl RngCore for UnsafeBufferRng { + fn next_u32(&mut self) -> u32 { + // let bytes: [u8; 4] = (0..4) + // .map(|_| self.next()) + // .collect_vec() + // .try_into() + // .unwrap(); + // u32::from_be_bytes(bytes) + u32::from_le_bytes([self.next(), 0, 0, 0]) + } + + fn next_u64(&mut self) -> u64 { + // let bytes: [u8; 8] = (0..8) + // .map(|_| self.next()) + // .collect_vec() + // .try_into() + // .unwrap(); + // u64::from_be_bytes(bytes) + u64::from_le_bytes([self.next(), 0, 0, 0, 0, 0, 0, 0]) + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + for d in dest.iter_mut() { + *d = self.next(); + } + } + + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> { + for d in dest.iter_mut() { + *d = self.next(); + } + Ok(()) + } + } + + #[test] + fn test_unsafe_buffer_rng() { + let seed_bytes = hex::decode("7FFECD162AE2").unwrap(); + let mut rng = UnsafeBufferRng::new(&seed_bytes); + let generated_bytes: Vec = (0..seed_bytes.len()).map(|_| rng.next()).collect(); + assert_eq!(seed_bytes, generated_bytes); + } + + #[test] + fn test_approx_exp() { + let precision = 1u64 << 14; + // known answers were generated with the following sage script: + //```sage + // num_samples = 10 + // precision = 200 + // R = Reals(precision) + // + // print(f"let kats : [(f64, f64, u64);{num_samples}] = [") + // for i in range(num_samples): + // x = RDF.random_element(0.0, 0.693147180559945) + // ccs = RDF.random_element(0.0, 1.0) + // res = round(2^63 * R(ccs) * exp(R(-x))) + // print(f"({x}, {ccs}, {res}),") + // print("];") + // ``` + let kats: [(f64, f64, u64); 10] = [ + (0.2314993926072656, 0.8148006314615972, 5962140072160879737), + (0.2648875572812225, 0.12769669655309035, 903712282351034505), + (0.11251957513682391, 0.9264611470305881, 7635725498677341553), + (0.04353439307256617, 0.5306497137523327, 4685877322232397936), + (0.41834495299784347, 0.879438856118578, 5338392138535350986), + (0.32579398973228557, 0.16513412873289002, 1099603299296456803), + (0.5939508073919817, 0.029776019144967303, 151637565622779016), + (0.2932367999399056, 0.37123847662857923, 2553827649386670452), + (0.5005699297417507, 0.31447208863888976, 1758235618083658825), + (0.4876437338498085, 0.6159515298936868, 3488632981903743976), + ]; + for (x, ccs, answer) in kats { + let difference = (answer as i128) - (approx_exp(x, ccs) as i128); + assert!( + (difference * difference) as u64 <= precision * precision, + "answer: {answer} versus approximation: {}\ndifference: {} whereas precision: {}", + approx_exp(x, ccs), + difference, + precision + ); + } + } + + #[test] + fn test_ber_exp() { + let kats = [ + ( + 1.268_314_048_020_498_4, + 0.749_990_853_267_664_9, + hex::decode("ea000000000000").unwrap(), + false, + ), + ( + 0.001_563_917_959_143_409_6, + 0.749_990_853_267_664_9, + hex::decode("6c000000000000").unwrap(), + true, + ), + ( + 0.017_921_215_753_999_235, + 0.749_990_853_267_664_9, + hex::decode("c2000000000000").unwrap(), + false, + ), + ( + 0.776_117_648_844_980_6, + 0.751_181_554_542_520_8, + hex::decode("58000000000000").unwrap(), + true, + ), + ]; + for (x, ccs, bytes, answer) in kats { + assert_eq!(answer, ber_exp(x, ccs, bytes.try_into().unwrap())); + } + } + + #[test] + fn test_sampler_z() { + let sigma_min = 1.277833697; + // known answers from the doc, table 3.2, page 44 + // https://falcon-sign.info/falcon.pdf + // The zeros were added to account for dropped bytes. + let kats = [ + (-91.90471153063714,1.7037990414754918,hex::decode("0fc5442ff043d66e91d1ea000000000000cac64ea5450a22941edc6c").unwrap(),-92), + (-8.322564895434937,1.7037990414754918,hex::decode("f4da0f8d8444d1a77265c2000000000000ef6f98bbbb4bee7db8d9b3").unwrap(),-8), + (-19.096516109216804,1.7035823083824078,hex::decode("db47f6d7fb9b19f25c36d6000000000000b9334d477a8bc0be68145d").unwrap(),-20), + (-11.335543982423326, 1.7035823083824078, hex::decode("ae41b4f5209665c74d00dc000000000000c1a8168a7bb516b3190cb42c1ded26cd52000000000000aed770eca7dd334e0547bcc3c163ce0b").unwrap(), -12), + (7.9386734193997555, 1.6984647769450156, hex::decode("31054166c1012780c603ae0000000000009b833cec73f2f41ca5807c000000000000c89c92158834632f9b1555").unwrap(), 8), + (-28.990850086867255, 1.6984647769450156, hex::decode("737e9d68a50a06dbbc6477").unwrap(), -30), + (-9.071257914091655, 1.6980782114808988, hex::decode("a98ddd14bf0bf22061d632").unwrap(), -10), + (-43.88754568839566, 1.6980782114808988, hex::decode("3cbf6818a68f7ab9991514").unwrap(), -41), + (-58.17435547946095,1.7010983419195522,hex::decode("6f8633f5bfa5d26848668e0000000000003d5ddd46958e97630410587c").unwrap(),-61), + (-43.58664906684732, 1.7010983419195522, hex::decode("272bc6c25f5c5ee53f83c40000000000003a361fbc7cc91dc783e20a").unwrap(), -46), + (-34.70565203313315, 1.7009387219711465, hex::decode("45443c59574c2c3b07e2e1000000000000d9071e6d133dbe32754b0a").unwrap(), -34), + (-44.36009577368896, 1.7009387219711465, hex::decode("6ac116ed60c258e2cbaeab000000000000728c4823e6da36e18d08da0000000000005d0cc104e21cc7fd1f5ca8000000000000d9dbb675266c928448059e").unwrap(), -44), + (-21.783037079346236, 1.6958406126012802, hex::decode("68163bc1e2cbf3e18e7426").unwrap(), -23), + (-39.68827784633828, 1.6958406126012802, hex::decode("d6a1b51d76222a705a0259").unwrap(), -40), + (-18.488607061056847, 1.6955259305261838, hex::decode("f0523bfaa8a394bf4ea5c10000000000000f842366fde286d6a30803").unwrap(), -22), + (-48.39610939101591, 1.6955259305261838, hex::decode("87bd87e63374cee62127fc0000000000006931104aab64f136a0485b").unwrap(), -50), + ]; + for (mu, sigma, random_bytes, answer) in kats { + assert_eq!( + sampler_z(mu, sigma, sigma_min, &mut UnsafeBufferRng::new(&random_bytes)), + answer + ); + } + } +} diff --git a/src/dsa/rpo_falcon512/mod.rs b/src/dsa/rpo_falcon512/mod.rs index 35ac61a98..85b030c6b 100644 --- a/src/dsa/rpo_falcon512/mod.rs +++ b/src/dsa/rpo_falcon512/mod.rs @@ -4,33 +4,31 @@ use crate::{ Felt, Word, ZERO, }; -#[cfg(feature = "std")] -mod ffi; - -mod error; +mod hash_to_point; mod keys; -mod polynomial; +mod math; mod signature; -pub use error::FalconError; -pub use keys::{KeyPair, PublicKey}; -pub use polynomial::Polynomial; +use self::math::Polynomial; +pub use keys::{PublicKey, SecretKey}; pub use signature::Signature; // CONSTANTS // ================================================================================================ -// The Falcon modulus. -const MODULUS: u16 = 12289; -const MODULUS_MINUS_1_OVER_TWO: u16 = 6144; +// The Falcon modulus p. +const MODULUS: i16 = 12289; + +// Number of bits needed to encode an element in the Falcon field. +const FALCON_ENCODING_BITS: u32 = 14; // The Falcon parameters for Falcon-512. This is the degree of the polynomial `phi := x^N + 1` // defining the ring Z_p[x]/(phi). const N: usize = 512; -const LOG_N: usize = 9; +const LOG_N: u8 = 9; /// Length of nonce used for key-pair generation. -const NONCE_LEN: usize = 40; +const SIG_NONCE_LEN: usize = 40; /// Number of filed elements used to encode a nonce. const NONCE_ELEMENTS: usize = 8; @@ -42,16 +40,64 @@ pub const PK_LEN: usize = 897; pub const SK_LEN: usize = 1281; /// Signature length as a u8 vector. -const SIG_LEN: usize = 626; +const SIG_POLY_BYTE_LEN: usize = 625; /// Bound on the squared-norm of the signature. const SIG_L2_BOUND: u64 = 34034726; +/// Standard deviation of the Gaussian over the lattice. +const SIGMA: f64 = 165.7366171829776; + // TYPE ALIASES // ================================================================================================ -type SignatureBytes = [u8; NONCE_LEN + SIG_LEN]; -type PublicKeyBytes = [u8; PK_LEN]; -type SecretKeyBytes = [u8; SK_LEN]; -type NonceBytes = [u8; NONCE_LEN]; -type NonceElements = [Felt; NONCE_ELEMENTS]; +type ShortLatticeBasis = [Polynomial; 4]; + +// NONCE +// ================================================================================================ + +/// Nonce of the Falcon signature. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Nonce([u8; SIG_NONCE_LEN]); + +impl Nonce { + /// Returns a new [Nonce] instantiated from the provided bytes. + pub fn new(bytes: [u8; SIG_NONCE_LEN]) -> Self { + Self(bytes) + } + + /// Returns the underlying bytes of this nonce. + pub fn as_bytes(&self) -> &[u8; SIG_NONCE_LEN] { + &self.0 + } + + /// Converts byte representation of the nonce into field element representation. + /// + /// Nonce bytes are converted to field elements by taking consecutive 5 byte chunks + /// of the nonce and interpreting them as field elements. + pub fn to_elements(&self) -> [Felt; NONCE_ELEMENTS] { + let mut buffer = [0_u8; 8]; + let mut result = [ZERO; 8]; + for (i, bytes) in self.0.chunks(5).enumerate() { + buffer[..5].copy_from_slice(bytes); + // we can safely (without overflow) create a new Felt from u64 value here since this + // value contains at most 5 bytes + result[i] = Felt::new(u64::from_le_bytes(buffer)); + } + + result + } +} + +impl Serializable for &Nonce { + fn write_into(&self, target: &mut W) { + target.write_bytes(&self.0) + } +} + +impl Deserializable for Nonce { + fn read_from(source: &mut R) -> Result { + let bytes = source.read()?; + Ok(Self(bytes)) + } +} diff --git a/src/dsa/rpo_falcon512/polynomial.rs b/src/dsa/rpo_falcon512/polynomial.rs deleted file mode 100644 index a00787fb6..000000000 --- a/src/dsa/rpo_falcon512/polynomial.rs +++ /dev/null @@ -1,279 +0,0 @@ -use alloc::vec::Vec; -use core::ops::{Add, Mul, Sub}; - -use super::{FalconError, Felt, LOG_N, MODULUS, MODULUS_MINUS_1_OVER_TWO, N, PK_LEN}; - -// FALCON POLYNOMIAL -// ================================================================================================ - -/// A polynomial over Z_p\[x\]/(phi) where phi := x^512 + 1 -#[derive(Debug, Copy, Clone, PartialEq)] -pub struct Polynomial([u16; N]); - -impl Polynomial { - // CONSTRUCTORS - // -------------------------------------------------------------------------------------------- - - /// Constructs a new polynomial from a list of coefficients. - /// - /// # Safety - /// This constructor validates that the coefficients are in the valid range only in debug mode. - pub unsafe fn new(data: [u16; N]) -> Self { - for value in data { - debug_assert!(value < MODULUS); - } - - Self(data) - } - - /// Decodes raw bytes representing a public key into a polynomial in Z_p\[x\]/(phi). - /// - /// # Errors - /// Returns an error if: - /// - The provided input is not exactly 897 bytes long. - /// - The first byte of the input is not equal to log2(512) i.e., 9. - /// - Any of the coefficients encoded in the provided input is greater than or equal to the - /// Falcon field modulus. - pub fn from_pub_key(input: &[u8]) -> Result { - if input.len() != PK_LEN { - return Err(FalconError::PubKeyDecodingInvalidLength(input.len())); - } - - if input[0] != LOG_N as u8 { - return Err(FalconError::PubKeyDecodingInvalidTag(input[0])); - } - - let mut acc = 0_u32; - let mut acc_len = 0; - - let mut output = [0_u16; N]; - let mut output_idx = 0; - - for &byte in input.iter().skip(1) { - acc = (acc << 8) | (byte as u32); - acc_len += 8; - - if acc_len >= 14 { - acc_len -= 14; - let w = (acc >> acc_len) & 0x3FFF; - if w >= MODULUS as u32 { - return Err(FalconError::PubKeyDecodingInvalidCoefficient(w)); - } - output[output_idx] = w as u16; - output_idx += 1; - } - } - - if (acc & ((1u32 << acc_len) - 1)) == 0 { - Ok(Self(output)) - } else { - Err(FalconError::PubKeyDecodingExtraData) - } - } - - /// Decodes the signature into the coefficients of a polynomial in Z_p\[x\]/(phi). It assumes - /// that the signature has been encoded using the uncompressed format. - /// - /// # Errors - /// Returns an error if: - /// - The signature has been encoded using a different algorithm than the reference compressed - /// encoding algorithm. - /// - The encoded signature polynomial is in Z_p\[x\]/(phi') where phi' = x^N' + 1 and N' != 512. - /// - While decoding the high bits of a coefficient, the current accumulated value of its - /// high bits is larger than 2048. - /// - The decoded coefficient is -0. - /// - The remaining unused bits in the last byte of `input` are non-zero. - pub fn from_signature(input: &[u8]) -> Result { - let (encoding, log_n) = (input[0] >> 4, input[0] & 0b00001111); - - if encoding != 0b0011 { - return Err(FalconError::SigDecodingIncorrectEncodingAlgorithm); - } - if log_n != 0b1001 { - return Err(FalconError::SigDecodingNotSupportedDegree(log_n)); - } - - let input = &input[41..]; - let mut input_idx = 0; - let mut acc = 0u32; - let mut acc_len = 0; - let mut output = [0_u16; N]; - - for e in output.iter_mut() { - acc = (acc << 8) | (input[input_idx] as u32); - input_idx += 1; - let b = acc >> acc_len; - let s = b & 128; - let mut m = b & 127; - - loop { - if acc_len == 0 { - acc = (acc << 8) | (input[input_idx] as u32); - input_idx += 1; - acc_len = 8; - } - acc_len -= 1; - if ((acc >> acc_len) & 1) != 0 { - break; - } - m += 128; - if m >= 2048 { - return Err(FalconError::SigDecodingTooBigHighBits(m)); - } - } - if s != 0 && m == 0 { - return Err(FalconError::SigDecodingMinusZero); - } - - *e = if s != 0 { (MODULUS as u32 - m) as u16 } else { m as u16 }; - } - - if (acc & ((1 << acc_len) - 1)) != 0 { - return Err(FalconError::SigDecodingNonZeroUnusedBitsLastByte); - } - - Ok(Self(output)) - } - - // PUBLIC ACCESSORS - // -------------------------------------------------------------------------------------------- - - /// Returns the coefficients of this polynomial as integers. - pub fn inner(&self) -> [u16; N] { - self.0 - } - - /// Returns the coefficients of this polynomial as field elements. - pub fn to_elements(&self) -> Vec { - self.0.iter().map(|&a| Felt::from(a)).collect() - } - - // POLYNOMIAL OPERATIONS - // -------------------------------------------------------------------------------------------- - - /// Multiplies two polynomials over Z_p\[x\] without reducing modulo p. Given that the degrees - /// of the input polynomials are less than 512 and their coefficients are less than the modulus - /// q equal to 12289, the resulting product polynomial is guaranteed to have coefficients less - /// than the Miden prime. - /// - /// Note that this multiplication is not over Z_p\[x\]/(phi). - pub fn mul_modulo_p(a: &Self, b: &Self) -> [u64; 1024] { - let mut c = [0; 2 * N]; - for i in 0..N { - for j in 0..N { - c[i + j] += a.0[i] as u64 * b.0[j] as u64; - } - } - - c - } - - /// Reduces a polynomial, that is the product of two polynomials over Z_p\[x\], modulo - /// the irreducible polynomial phi. This results in an element in Z_p\[x\]/(phi). - pub fn reduce_negacyclic(a: &[u64; 1024]) -> Self { - let mut c = [0; N]; - for i in 0..N { - let ai = a[N + i] % MODULUS as u64; - let neg_ai = (MODULUS - ai as u16) % MODULUS; - - let bi = (a[i] % MODULUS as u64) as u16; - c[i] = (neg_ai + bi) % MODULUS; - } - - Self(c) - } - - /// Computes the norm squared of a polynomial in Z_p\[x\]/(phi) after normalizing its - /// coefficients to be in the interval (-p/2, p/2]. - pub fn sq_norm(&self) -> u64 { - let mut res = 0; - for e in self.0 { - if e > MODULUS_MINUS_1_OVER_TWO { - res += (MODULUS - e) as u64 * (MODULUS - e) as u64 - } else { - res += e as u64 * e as u64 - } - } - res - } -} - -// Returns a polynomial representing the zero polynomial i.e. default element. -impl Default for Polynomial { - fn default() -> Self { - Self([0_u16; N]) - } -} - -/// Multiplication over Z_p\[x\]/(phi) -impl Mul for Polynomial { - type Output = Self; - - fn mul(self, other: Self) -> >::Output { - let mut result = [0_u16; N]; - for j in 0..N { - for k in 0..N { - let i = (j + k) % N; - let a = self.0[j] as usize; - let b = other.0[k] as usize; - let q = MODULUS as usize; - let mut prod = a * b % q; - if (N - 1) < (j + k) { - prod = (q - prod) % q; - } - result[i] = ((result[i] as usize + prod) % q) as u16; - } - } - - Polynomial(result) - } -} - -/// Addition over Z_p\[x\]/(phi) -impl Add for Polynomial { - type Output = Self; - - fn add(self, other: Self) -> >::Output { - let mut res = self; - res.0.iter_mut().zip(other.0.iter()).for_each(|(x, y)| *x = (*x + *y) % MODULUS); - - res - } -} - -/// Subtraction over Z_p\[x\]/(phi) -impl Sub for Polynomial { - type Output = Self; - - fn sub(self, other: Self) -> >::Output { - let mut res = self; - res.0 - .iter_mut() - .zip(other.0.iter()) - .for_each(|(x, y)| *x = (*x + MODULUS - *y) % MODULUS); - - res - } -} - -// TESTS -// ================================================================================================ - -#[cfg(test)] -mod tests { - use super::{Polynomial, N}; - - #[test] - fn test_negacyclic_reduction() { - let coef1: [u16; N] = rand_utils::rand_array(); - let coef2: [u16; N] = rand_utils::rand_array(); - - let poly1 = Polynomial(coef1); - let poly2 = Polynomial(coef2); - - assert_eq!( - poly1 * poly2, - Polynomial::reduce_negacyclic(&Polynomial::mul_modulo_p(&poly1, &poly2)) - ); - } -} diff --git a/src/dsa/rpo_falcon512/signature.rs b/src/dsa/rpo_falcon512/signature.rs index c91f786fb..3cea5c6eb 100644 --- a/src/dsa/rpo_falcon512/signature.rs +++ b/src/dsa/rpo_falcon512/signature.rs @@ -1,289 +1,372 @@ -use alloc::string::ToString; -use core::cell::OnceCell; +use alloc::{string::ToString, vec::Vec}; +use core::ops::Deref; use super::{ - ByteReader, ByteWriter, Deserializable, DeserializationError, Felt, NonceBytes, NonceElements, - Polynomial, PublicKeyBytes, Rpo256, Serializable, SignatureBytes, Word, MODULUS, N, - SIG_L2_BOUND, ZERO, + hash_to_point::hash_to_point_rpo256, + keys::PubKeyPoly, + math::{FalconFelt, FastFft, Polynomial}, + ByteReader, ByteWriter, Deserializable, DeserializationError, Felt, Nonce, Rpo256, + Serializable, Word, LOG_N, MODULUS, N, SIG_L2_BOUND, SIG_POLY_BYTE_LEN, }; +use num::Zero; // FALCON SIGNATURE // ================================================================================================ /// An RPO Falcon512 signature over a message. /// -/// The signature is a pair of polynomials (s1, s2) in (Z_p\[x\]/(phi))^2, where: +/// The signature is a pair of polynomials (s1, s2) in (Z_p\[x\]/(phi))^2 a nonce `r`, and a public +/// key polynomial `h` where: /// - p := 12289 /// - phi := x^512 + 1 -/// - s1 = c - s2 * h -/// - h is a polynomial representing the public key and c is a polynomial that is the hash-to-point -/// of the message being signed. /// -/// The signature verifies if and only if: +/// The signature verifies against a public key `pk` if and only if: /// 1. s1 = c - s2 * h /// 2. |s1|^2 + |s2|^2 <= SIG_L2_BOUND /// -/// where |.| is the norm. +/// where |.| is the norm and: +/// - c = HashToPoint(r || message) +/// - pk = Rpo256::hash(h) /// -/// [Signature] also includes the extended public key which is serialized as: +/// Here h is a polynomial representing the public key and pk is its digest using the Rpo256 hash +/// function. c is a polynomial that is the hash-to-point of the message being signed. +/// +/// The polynomial h is serialized as: /// 1. 1 byte representing the log2(512) i.e., 9. -/// 2. 896 bytes for the public key. This is decoded into the `h` polynomial above. +/// 2. 896 bytes for the public key itself. /// -/// The actual signature is serialized as: +/// The signature is serialized as: /// 1. A header byte specifying the algorithm used to encode the coefficients of the `s2` polynomial -/// together with the degree of the irreducible polynomial phi. -/// The general format of this byte is 0b0cc1nnnn where: -/// a. cc is either 01 when the compressed encoding algorithm is used and 10 when the -/// uncompressed algorithm is used. -/// b. nnnn is log2(N) where N is the degree of the irreducible polynomial phi. -/// The current implementation works always with cc equal to 0b01 and nnnn equal to 0b1001 and -/// thus the header byte is always equal to 0b00111001. +/// together with the degree of the irreducible polynomial phi. For RPO Falcon512, the header +/// byte is set to `10111001` which differentiates it from the standardized instantiation of +/// the Falcon signature. /// 2. 40 bytes for the nonce. -/// 3. 625 bytes encoding the `s2` polynomial above. +/// 4. 625 bytes encoding the `s2` polynomial above. /// -/// The total size of the signature (including the extended public key) is 1563 bytes. -#[derive(Debug, Clone)] +/// The total size of the signature is (including the extended public key) is 1563 bytes. +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Signature { - pub(super) pk: PublicKeyBytes, - pub(super) sig: SignatureBytes, - - // Cached polynomial decoding for public key and signatures - pub(super) pk_polynomial: OnceCell, - pub(super) sig_polynomial: OnceCell, + header: SignatureHeader, + nonce: Nonce, + s2: SignaturePoly, + h: PubKeyPoly, } impl Signature { + // CONSTRUCTOR + // -------------------------------------------------------------------------------------------- + pub fn new(nonce: Nonce, h: PubKeyPoly, s2: SignaturePoly) -> Signature { + Self { + header: SignatureHeader::default(), + nonce, + s2, + h, + } + } + // PUBLIC ACCESSORS // -------------------------------------------------------------------------------------------- /// Returns the public key polynomial h. - pub fn pub_key_poly(&self) -> Polynomial { - *self.pk_polynomial.get_or_init(|| { - // we assume that the signature was constructed with a valid public key, and thus - // expect() is OK here. - Polynomial::from_pub_key(&self.pk).expect("invalid public key") - }) - } - - /// Returns the nonce component of the signature represented as field elements. - /// - /// Nonce bytes are converted to field elements by taking consecutive 5 byte chunks - /// of the nonce and interpreting them as field elements. - pub fn nonce(&self) -> NonceElements { - // we assume that the signature was constructed with a valid signature, and thus - // expect() is OK here. - let nonce = self.sig[1..41].try_into().expect("invalid signature"); - decode_nonce(nonce) + pub fn pk_poly(&self) -> &PubKeyPoly { + &self.h } // Returns the polynomial representation of the signature in Z_p[x]/(phi). - pub fn sig_poly(&self) -> Polynomial { - *self.sig_polynomial.get_or_init(|| { - // we assume that the signature was constructed with a valid signature, and thus - // expect() is OK here. - Polynomial::from_signature(&self.sig).expect("invalid signature") - }) + pub fn sig_poly(&self) -> &Polynomial { + &self.s2 } - // HASH-TO-POINT - // -------------------------------------------------------------------------------------------- - - /// Returns a polynomial in Z_p\[x\]/(phi) representing the hash of the provided message. - pub fn hash_to_point(&self, message: Word) -> Polynomial { - hash_to_point(message, &self.nonce()) + /// Returns the nonce component of the signature. + pub fn nonce(&self) -> &Nonce { + &self.nonce } // SIGNATURE VERIFICATION // -------------------------------------------------------------------------------------------- + /// Returns true if this signature is a valid signature for the specified message generated - /// against key pair matching the specified public key commitment. + /// against the secret key matching the specified public key commitment. pub fn verify(&self, message: Word, pubkey_com: Word) -> bool { - // Make sure the expanded public key matches the provided public key commitment - let h = self.pub_key_poly(); - let h_digest: Word = Rpo256::hash_elements(&h.to_elements()).into(); + // compute the hash of the public key polynomial + let h_felt: Polynomial = (&**self.pk_poly()).into(); + let h_digest: Word = Rpo256::hash_elements(&h_felt.coefficients).into(); if h_digest != pubkey_com { return false; } - // Make sure the signature is valid - let s2 = self.sig_poly(); - let c = self.hash_to_point(message); - - let s1 = c - s2 * h; - - let sq_norm = s1.sq_norm() + s2.sq_norm(); - sq_norm <= SIG_L2_BOUND + let c = hash_to_point_rpo256(message, &self.nonce); + h_digest == pubkey_com && verify_helper(&c, &self.s2, self.pk_poly()) } } -// SERIALIZATION / DESERIALIZATION -// ================================================================================================ - impl Serializable for Signature { fn write_into(&self, target: &mut W) { - target.write_bytes(&self.pk); - target.write_bytes(&self.sig); + target.write(&self.header); + target.write(&self.nonce); + target.write(&self.s2); + target.write(&self.h); } } impl Deserializable for Signature { fn read_from(source: &mut R) -> Result { - let pk: PublicKeyBytes = source.read_array()?; - let sig: SignatureBytes = source.read_array()?; - - // make sure public key and signature can be decoded correctly - let pk_polynomial = Polynomial::from_pub_key(&pk) - .map_err(|err| DeserializationError::InvalidValue(err.to_string()))? - .into(); - let sig_polynomial = Polynomial::from_signature(&sig) - .map_err(|err| DeserializationError::InvalidValue(err.to_string()))? - .into(); - - Ok(Self { pk, sig, pk_polynomial, sig_polynomial }) + let header = source.read()?; + let nonce = source.read()?; + let s2 = source.read()?; + let h = source.read()?; + + Ok(Self { header, nonce, s2, h }) } } -// HELPER FUNCTIONS +// SIGNATURE HEADER // ================================================================================================ -/// Returns a polynomial in Z_p[x]/(phi) representing the hash of the provided message and -/// nonce. -fn hash_to_point(message: Word, nonce: &NonceElements) -> Polynomial { - let mut state = [ZERO; Rpo256::STATE_WIDTH]; +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SignatureHeader(u8); - // absorb the nonce into the state - for (&n, s) in nonce.iter().zip(state[Rpo256::RATE_RANGE].iter_mut()) { - *s = n; +impl Default for SignatureHeader { + /// According to section 3.11.3 in the specification [1], the signature header has the format + /// `0cc1nnnn` where: + /// + /// 1. `cc` signifies the encoding method. `01` denotes using the compression encoding method + /// and `10` denotes encoding using the uncompressed method. + /// 2. `nnnn` encodes `LOG_N`. + /// + /// For RPO Falcon 512 we use compression encoding and N = 512. Moreover, to differentiate the + /// RPO Falcon variant from the reference variant using SHAKE256, we flip the first bit in the + /// header. Thus, for RPO Falcon 512 the header is `10111001` + /// + /// [1]: https://falcon-sign.info/falcon.pdf + fn default() -> Self { + Self(0b1011_1001) } - Rpo256::apply_permutation(&mut state); +} - // absorb message into the state - for (&m, s) in message.iter().zip(state[Rpo256::RATE_RANGE].iter_mut()) { - *s = m; +impl Serializable for &SignatureHeader { + fn write_into(&self, target: &mut W) { + target.write_u8(self.0) } +} - // squeeze the coefficients of the polynomial - let mut i = 0; - let mut res = [0_u16; N]; - for _ in 0..64 { - Rpo256::apply_permutation(&mut state); - for a in &state[Rpo256::RATE_RANGE] { - res[i] = (a.as_int() % MODULUS as u64) as u16; - i += 1; +impl Deserializable for SignatureHeader { + fn read_from(source: &mut R) -> Result { + let header = source.read_u8()?; + let (encoding, log_n) = (header >> 4, header & 0b00001111); + if encoding != 0b1011 { + return Err(DeserializationError::InvalidValue( + "Failed to decode signature: not supported encoding algorithm".to_string(), + )); + } + + if log_n != LOG_N { + return Err(DeserializationError::InvalidValue( + format!("Failed to decode signature: only supported irreducible polynomial degree is 512, 2^{log_n} was provided") + )); } + + Ok(Self(header)) } +} + +// SIGNATURE POLYNOMIAL +// ================================================================================================ + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SignaturePoly(pub Polynomial); + +impl Deref for SignaturePoly { + type Target = Polynomial; - // using the raw constructor is OK here because we reduce all coefficients by the modulus above - unsafe { Polynomial::new(res) } + fn deref(&self) -> &Self::Target { + &self.0 + } } -/// Converts byte representation of the nonce into field element representation. -fn decode_nonce(nonce: &NonceBytes) -> NonceElements { - let mut buffer = [0_u8; 8]; - let mut result = [ZERO; 8]; - for (i, bytes) in nonce.chunks(5).enumerate() { - buffer[..5].copy_from_slice(bytes); - // we can safely (without overflow) create a new Felt from u64 value here since this value - // contains at most 5 bytes - result[i] = Felt::new(u64::from_le_bytes(buffer)); +impl From> for SignaturePoly { + fn from(pk_poly: Polynomial) -> Self { + Self(pk_poly) } +} - result +impl TryFrom<&[i16; N]> for SignaturePoly { + type Error = (); + + fn try_from(coefficients: &[i16; N]) -> Result { + if are_coefficients_valid(coefficients) { + Ok(Self(coefficients.to_vec().into())) + } else { + Err(()) + } + } } -// TESTS -// ================================================================================================ +impl Serializable for &SignaturePoly { + fn write_into(&self, target: &mut W) { + let sig_coeff: Vec = self.0.coefficients.iter().map(|a| a.balanced_value()).collect(); + let mut sk_bytes = vec![0_u8; SIG_POLY_BYTE_LEN]; + + let mut acc = 0; + let mut acc_len = 0; + let mut v = 0; + let mut t; + let mut w; + + // For each coefficient of x: + // - the sign is encoded on 1 bit + // - the 7 lower bits are encoded naively (binary) + // - the high bits are encoded in unary encoding + // + // Algorithm 17 p. 47 of the specification [1]. + // + // [1]: https://falcon-sign.info/falcon.pdf + for &c in sig_coeff.iter() { + acc <<= 1; + t = c; + + if t < 0 { + t = -t; + acc |= 1; + } + w = t as u16; -#[cfg(all(test, feature = "std"))] -mod tests { - use core::ffi::c_void; - use rand_utils::rand_vector; - - use super::{ - super::{ffi::*, KeyPair}, - *, - }; - - // Wrappers for unsafe functions - impl Rpo128Context { - /// Initializes the RPO state. - pub fn init() -> Self { - let mut ctx = Rpo128Context { content: [0u64; 13] }; - unsafe { - rpo128_init(&mut ctx as *mut Rpo128Context); + acc <<= 7; + let mask = 127_u32; + acc |= (w as u32) & mask; + w >>= 7; + + acc_len += 8; + + acc <<= w + 1; + acc |= 1; + acc_len += w + 1; + + while acc_len >= 8 { + acc_len -= 8; + + sk_bytes[v] = (acc >> acc_len) as u8; + v += 1; } - ctx } - /// Absorbs data into the RPO state. - pub fn absorb(&mut self, data: &[u8]) { - unsafe { - rpo128_absorb( - self as *mut Rpo128Context, - data.as_ptr() as *const c_void, - data.len(), - ) + if acc_len > 0 { + sk_bytes[v] = (acc << (8 - acc_len)) as u8; + } + target.write_bytes(&sk_bytes); + } +} + +impl Deserializable for SignaturePoly { + fn read_from(source: &mut R) -> Result { + let input = source.read_array::()?; + + let mut input_idx = 0; + let mut acc = 0u32; + let mut acc_len = 0; + let mut coefficients = [FalconFelt::zero(); N]; + + // Algorithm 18 p. 48 of the specification [1]. + // + // [1]: https://falcon-sign.info/falcon.pdf + for c in coefficients.iter_mut() { + acc = (acc << 8) | (input[input_idx] as u32); + input_idx += 1; + let b = acc >> acc_len; + let s = b & 128; + let mut m = b & 127; + + loop { + if acc_len == 0 { + acc = (acc << 8) | (input[input_idx] as u32); + input_idx += 1; + acc_len = 8; + } + acc_len -= 1; + if ((acc >> acc_len) & 1) != 0 { + break; + } + m += 128; + if m >= 2048 { + return Err(DeserializationError::InvalidValue( + "Failed to decode signature: high bits {m} exceed 2048".to_string(), + )); + } + } + if s != 0 && m == 0 { + return Err(DeserializationError::InvalidValue( + "Failed to decode signature: -0 is forbidden".to_string(), + )); } + + let felt = if s != 0 { (MODULUS as u32 - m) as u16 } else { m as u16 }; + *c = FalconFelt::new(felt as i16); } - /// Finalizes the RPO state to prepare for squeezing. - pub fn finalize(&mut self) { - unsafe { rpo128_finalize(self as *mut Rpo128Context) } + if (acc & ((1 << acc_len) - 1)) != 0 { + return Err(DeserializationError::InvalidValue( + "Failed to decode signature: Non-zero unused bits in the last byte".to_string(), + )); } + Ok(Polynomial::new(coefficients.to_vec()).into()) } +} - #[test] - fn test_hash_to_point() { - // Create a random message and transform it into a u8 vector - let msg_felts: Word = rand_vector::(4).try_into().unwrap(); - let msg_bytes = msg_felts - .iter() - .flat_map(|e| e.as_int().to_le_bytes()) - .collect::>(); - - // Create a nonce i.e. a [u8; 40] array and pack into a [Felt; 8] array. - let nonce: [u8; 40] = rand_vector::(40).try_into().unwrap(); - - let mut buffer = [0_u8; 64]; - for i in 0..8 { - buffer[8 * i] = nonce[5 * i]; - buffer[8 * i + 1] = nonce[5 * i + 1]; - buffer[8 * i + 2] = nonce[5 * i + 2]; - buffer[8 * i + 3] = nonce[5 * i + 3]; - buffer[8 * i + 4] = nonce[5 * i + 4]; - } +// HELPER FUNCTIONS +// ================================================================================================ - // Initialize the RPO state - let mut rng = Rpo128Context::init(); +/// Takes the hash-to-point polynomial `c` of a message, the signature polynomial over +/// the message `s2` and a public key polynomial and returns `true` is the signature is a valid +/// signature for the given parameters, otherwise it returns `false`. +fn verify_helper(c: &Polynomial, s2: &SignaturePoly, h: &PubKeyPoly) -> bool { + let h_fft = h.fft(); + let s2_fft = s2.fft(); + let c_fft = c.fft(); - // Absorb the nonce and message into the RPO state - rng.absorb(&buffer); - rng.absorb(&msg_bytes); - rng.finalize(); + // compute the signature polynomial s1 using s1 = c - s2 * h + let s1_fft = c_fft - s2_fft.hadamard_mul(&h_fft); + let s1 = s1_fft.ifft(); - // Generate the coefficients of the hash-to-point polynomial. - let mut res: [u16; N] = [0; N]; + // compute the norm squared of (s1, s2) + let length_squared_s1 = s1.norm_squared(); + let length_squared_s2 = s2.norm_squared(); + let length_squared = length_squared_s1 + length_squared_s2; - unsafe { - PQCLEAN_FALCON512_CLEAN_hash_to_point_rpo( - &mut rng as *mut Rpo128Context, - res.as_mut_ptr(), - 9, - ); - } + length_squared < SIG_L2_BOUND +} + +/// Checks whether a set of coefficients is a valid one for a signature polynomial. +fn are_coefficients_valid(x: &[i16]) -> bool { + if x.len() != N { + return false; + } - // Check that the coefficients are correct - let nonce = decode_nonce(&nonce); - assert_eq!(res, hash_to_point(msg_felts, &nonce).inner()); + for &c in x { + if !(-2047..=2047).contains(&c) { + return false; + } } + true +} + +// TESTS +// ================================================================================================ + +#[cfg(test)] +mod tests { + use super::{super::SecretKey, *}; + use crate::{rand::RpoRandomCoin, ZERO}; + #[test] fn test_serialization_round_trip() { - let key = KeyPair::new().unwrap(); - let signature = key.sign(Word::default()).unwrap(); + let seed = [ZERO; 4]; + let mut rng = RpoRandomCoin::new(seed); + + let sk = SecretKey::with_rng(&mut rng); + let signature = sk.sign_with_rng(Word::default(), &mut rng); let serialized = signature.to_bytes(); let deserialized = Signature::read_from_bytes(&serialized).unwrap(); assert_eq!(signature.sig_poly(), deserialized.sig_poly()); - assert_eq!(signature.pub_key_poly(), deserialized.pub_key_poly()); } } diff --git a/src/rand/rpo.rs b/src/rand/rpo.rs index e5d805162..7ac94493d 100644 --- a/src/rand/rpo.rs +++ b/src/rand/rpo.rs @@ -4,6 +4,8 @@ use crate::{ utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}, }; use alloc::{string::ToString, vec::Vec}; +use rand::RngCore; +use rand_core::impls; // CONSTANTS // ================================================================================================ @@ -183,6 +185,28 @@ impl FeltRng for RpoRandomCoin { } } +// RNGCORE IMPLEMENTATION +// ------------------------------------------------------------------------------------------------ + +impl RngCore for RpoRandomCoin { + fn next_u32(&mut self) -> u32 { + self.draw_basefield().as_int() as u32 + } + + fn next_u64(&mut self) -> u64 { + impls::next_u64_via_u32(self) + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + impls::fill_bytes_via_next(self, dest) + } + + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> { + self.fill_bytes(dest); + Ok(()) + } +} + // SERIALIZATION // ------------------------------------------------------------------------------------------------