feat: Falcon 512 signature
This commit is contained in:
parent
b1dbcee21d
commit
8078021aff
28 changed files with 2263 additions and 114 deletions
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
|
@ -19,6 +19,8 @@ jobs:
|
||||||
args: [--no-default-features --target wasm32-unknown-unknown]
|
args: [--no-default-features --target wasm32-unknown-unknown]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@main
|
- uses: actions/checkout@main
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
- name: Install rust
|
- name: Install rust
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
|
@ -42,6 +44,8 @@ jobs:
|
||||||
features: ["--features default,std,serde", --no-default-features]
|
features: ["--features default,std,serde", --no-default-features]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@main
|
- uses: actions/checkout@main
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
- name: Install rust
|
- name: Install rust
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
|
@ -62,6 +66,8 @@ jobs:
|
||||||
features: ["--features default,std,serde", --no-default-features]
|
features: ["--features default,std,serde", --no-default-features]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@main
|
- uses: actions/checkout@main
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
- name: Install minimal nightly with clippy
|
- name: Install minimal nightly with clippy
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
|
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "PQClean"]
|
||||||
|
path = PQClean
|
||||||
|
url = https://github.com/PQClean/PQClean.git
|
12
Cargo.toml
12
Cargo.toml
|
@ -17,7 +17,7 @@ name = "miden-crypto"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
bench = false
|
bench = false
|
||||||
doctest = false
|
doctest = false
|
||||||
required-features = ["std"]
|
required-features = ["executable"]
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "hash"
|
name = "hash"
|
||||||
|
@ -34,12 +34,15 @@ harness = false
|
||||||
[features]
|
[features]
|
||||||
arch-arm64-sve = ["dep:cc"]
|
arch-arm64-sve = ["dep:cc"]
|
||||||
default = ["blake3/default", "std", "winter_crypto/default", "winter_math/default", "winter_utils/default"]
|
default = ["blake3/default", "std", "winter_crypto/default", "winter_math/default", "winter_utils/default"]
|
||||||
std = ["blake3/std", "winter_crypto/std", "winter_math/std", "winter_utils/std", "rand_utils"]
|
executable = ["dep:clap", "dep:rand_utils", "std"]
|
||||||
|
std = ["blake3/std", "dep:cc", "dep:libc", "dep:rand", "winter_crypto/std", "winter_math/std", "winter_utils/std"]
|
||||||
serde = ["winter_math/serde", "dep:serde", "serde/alloc"]
|
serde = ["winter_math/serde", "dep:serde", "serde/alloc"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
blake3 = { version = "1.4", default-features = false }
|
blake3 = { version = "1.4", default-features = false }
|
||||||
clap = { version = "4.3.21", features = ["derive"] }
|
clap = { version = "4.3", features = ["derive"], optional = true}
|
||||||
|
libc = { version = "0.2", optional = true, default-features = false }
|
||||||
|
rand = { version = "0.8", optional = true, default-features = false }
|
||||||
winter_crypto = { version = "0.6", package = "winter-crypto", default-features = false }
|
winter_crypto = { version = "0.6", package = "winter-crypto", default-features = false }
|
||||||
winter_math = { version = "0.6", package = "winter-math", default-features = false }
|
winter_math = { version = "0.6", package = "winter-math", default-features = false }
|
||||||
winter_utils = { version = "0.6", package = "winter-utils", default-features = false }
|
winter_utils = { version = "0.6", package = "winter-utils", default-features = false }
|
||||||
|
@ -52,4 +55,5 @@ proptest = "1.1.0"
|
||||||
rand_utils = { version = "0.6", package = "winter-rand-utils" }
|
rand_utils = { version = "0.6", package = "winter-rand-utils" }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
cc = { version = "1.0.79", optional = true }
|
cc = { version = "1.0", features = ["parallel"], optional = true }
|
||||||
|
glob = "*"
|
||||||
|
|
1
PQClean
Submodule
1
PQClean
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit c3abebf4ab1ff516ffa71e6337f06d898952c299
|
24
build.rs
24
build.rs
|
@ -1,8 +1,32 @@
|
||||||
fn main() {
|
fn main() {
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
compile_rpo_falcon();
|
||||||
|
|
||||||
#[cfg(feature = "arch-arm64-sve")]
|
#[cfg(feature = "arch-arm64-sve")]
|
||||||
compile_arch_arm64_sve();
|
compile_arch_arm64_sve();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
fn compile_rpo_falcon() {
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
let target_dir: PathBuf = ["PQClean", "crypto_sign", "falcon-512", "clean"].iter().collect();
|
||||||
|
let common_dir: PathBuf = ["PQClean", "common"].iter().collect();
|
||||||
|
let rpo_dir: PathBuf = ["src", "dsa", "rpo_falcon512", "falcon_c"].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();
|
||||||
|
let rpo_files = glob::glob(rpo_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()))
|
||||||
|
.files(rpo_files.into_iter().map(|p| p.unwrap().to_string_lossy().into_owned()))
|
||||||
|
.compile("falcon-512_clean");
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "arch-arm64-sve")]
|
#[cfg(feature = "arch-arm64-sve")]
|
||||||
fn compile_arch_arm64_sve() {
|
fn compile_arch_arm64_sve() {
|
||||||
println!("cargo:rerun-if-changed=arch/arm64-sve/rpo/library.c");
|
println!("cargo:rerun-if-changed=arch/arm64-sve/rpo/library.c");
|
||||||
|
|
|
@ -16,5 +16,6 @@ newline_style = "Unix"
|
||||||
#normalize_doc_attributes = true
|
#normalize_doc_attributes = true
|
||||||
#reorder_impl_items = true
|
#reorder_impl_items = true
|
||||||
single_line_if_else_max_width = 60
|
single_line_if_else_max_width = 60
|
||||||
|
struct_lit_width = 40
|
||||||
use_field_init_shorthand = true
|
use_field_init_shorthand = true
|
||||||
use_try_shorthand = true
|
use_try_shorthand = true
|
||||||
|
|
1
src/dsa/mod.rs
Normal file
1
src/dsa/mod.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pub mod rpo_falcon512;
|
55
src/dsa/rpo_falcon512/error.rs
Normal file
55
src/dsa/rpo_falcon512/error.rs
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
use super::{LOG_N, MODULUS, PK_LEN};
|
||||||
|
use core::fmt;
|
||||||
|
|
||||||
|
#[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 {}
|
66
src/dsa/rpo_falcon512/falcon_c/api_rpo.h
Normal file
66
src/dsa/rpo_falcon512/falcon_c/api_rpo.h
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#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);
|
387
src/dsa/rpo_falcon512/falcon_c/falcon_rpo.c
Normal file
387
src/dsa/rpo_falcon512/falcon_c/falcon_rpo.c
Normal file
|
@ -0,0 +1,387 @@
|
||||||
|
/*
|
||||||
|
* Wrapper for implementing the PQClean API.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include "randombytes.h"
|
||||||
|
#include "api_rpo.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 api_rpo.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 api_rpo.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 api_rpo.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);
|
||||||
|
}
|
582
src/dsa/rpo_falcon512/falcon_c/rpo.c
Normal file
582
src/dsa/rpo_falcon512/falcon_c/rpo.c
Normal file
|
@ -0,0 +1,582 @@
|
||||||
|
/*
|
||||||
|
* RPO implementation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/* ================================================================================================
|
||||||
|
* Modular Arithmetic
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define P 0xFFFFFFFF00000001
|
||||||
|
#define M 12289
|
||||||
|
|
||||||
|
// From https://github.com/ncw/iprime/blob/master/mod_math_noasm.go
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t sub_mod_p(uint64_t a, uint64_t b)
|
||||||
|
{
|
||||||
|
uint64_t r = a - b;
|
||||||
|
if (a < b)
|
||||||
|
r += P;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
|
||||||
|
static const uint64_t STATE_WIDTH = 12;
|
||||||
|
static const uint64_t NUM_ROUNDS = 7;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MDS matrix
|
||||||
|
*/
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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--;
|
||||||
|
}
|
||||||
|
}
|
83
src/dsa/rpo_falcon512/falcon_c/rpo.h
Normal file
83
src/dsa/rpo_falcon512/falcon_c/rpo.h
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* ================================================================================================
|
||||||
|
* 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);
|
189
src/dsa/rpo_falcon512/ffi.rs
Normal file
189
src/dsa/rpo_falcon512/ffi.rs
Normal file
|
@ -0,0 +1,189 @@
|
||||||
|
use libc::c_int;
|
||||||
|
|
||||||
|
// C IMPLEMENTATION INTERFACE
|
||||||
|
// ================================================================================================
|
||||||
|
|
||||||
|
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 ::std::os::raw::c_void,
|
||||||
|
len: libc::size_t,
|
||||||
|
);
|
||||||
|
|
||||||
|
#[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 super::*;
|
||||||
|
use crate::dsa::rpo_falcon512::{NONCE_LEN, PK_LEN, SIG_LEN, SK_LEN};
|
||||||
|
use rand::Rng;
|
||||||
|
|
||||||
|
#[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] =
|
||||||
|
(0..NONCE_LEN).map(|_| rng.gen()).collect::<Vec<u8>>().try_into().unwrap();
|
||||||
|
|
||||||
|
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 = rng.gen::<u16>() as usize;
|
||||||
|
let msg: Vec<u8> = (0..mlen).map(|_| rng.gen()).collect();
|
||||||
|
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()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
227
src/dsa/rpo_falcon512/keys.rs
Normal file
227
src/dsa/rpo_falcon512/keys.rs
Normal file
|
@ -0,0 +1,227 @@
|
||||||
|
use super::{
|
||||||
|
ByteReader, ByteWriter, Deserializable, DeserializationError, FalconError, Polynomial,
|
||||||
|
PublicKeyBytes, Rpo256, SecretKeyBytes, Serializable, Signature, Word,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use super::{ffi, NonceBytes, StarkField, NONCE_LEN, PK_LEN, SIG_LEN, SK_LEN};
|
||||||
|
|
||||||
|
// 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<Self, FalconError> {
|
||||||
|
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<PublicKey> 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<Self, FalconError> {
|
||||||
|
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<Self, FalconError> {
|
||||||
|
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<Signature, FalconError> {
|
||||||
|
let msg = message.iter().flat_map(|e| e.as_int().to_le_bytes()).collect::<Vec<_>>();
|
||||||
|
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 })
|
||||||
|
} else {
|
||||||
|
Err(FalconError::SigGenerationFailed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SERIALIZATION / DESERIALIZATION
|
||||||
|
// ================================================================================================
|
||||||
|
|
||||||
|
impl Serializable for KeyPair {
|
||||||
|
fn write_into<W: ByteWriter>(&self, target: &mut W) {
|
||||||
|
target.write_bytes(&self.public_key);
|
||||||
|
target.write_bytes(&self.secret_key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deserializable for KeyPair {
|
||||||
|
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
|
||||||
|
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 super::{super::Felt, KeyPair, NonceBytes, Word};
|
||||||
|
use rand_utils::{rand_array, rand_vector};
|
||||||
|
|
||||||
|
#[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::<Felt>(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::<Felt>(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::<Felt>(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::<Felt>(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()))
|
||||||
|
}
|
||||||
|
}
|
60
src/dsa/rpo_falcon512/mod.rs
Normal file
60
src/dsa/rpo_falcon512/mod.rs
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
use crate::{
|
||||||
|
hash::rpo::Rpo256,
|
||||||
|
utils::{
|
||||||
|
collections::Vec, ByteReader, ByteWriter, Deserializable, DeserializationError,
|
||||||
|
Serializable,
|
||||||
|
},
|
||||||
|
Felt, StarkField, Word, ZERO,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
mod ffi;
|
||||||
|
|
||||||
|
mod error;
|
||||||
|
mod keys;
|
||||||
|
mod polynomial;
|
||||||
|
mod signature;
|
||||||
|
|
||||||
|
pub use error::FalconError;
|
||||||
|
pub use keys::{KeyPair, PublicKey};
|
||||||
|
pub use polynomial::Polynomial;
|
||||||
|
pub use signature::Signature;
|
||||||
|
|
||||||
|
// CONSTANTS
|
||||||
|
// ================================================================================================
|
||||||
|
|
||||||
|
// The Falcon modulus.
|
||||||
|
const MODULUS: u16 = 12289;
|
||||||
|
const MODULUS_MINUS_1_OVER_TWO: u16 = 6144;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
/// Length of nonce used for key-pair generation.
|
||||||
|
const NONCE_LEN: usize = 40;
|
||||||
|
|
||||||
|
/// Number of filed elements used to encode a nonce.
|
||||||
|
const NONCE_ELEMENTS: usize = 8;
|
||||||
|
|
||||||
|
/// Public key length as a u8 vector.
|
||||||
|
const PK_LEN: usize = 897;
|
||||||
|
|
||||||
|
/// Secret key length as a u8 vector.
|
||||||
|
const SK_LEN: usize = 1281;
|
||||||
|
|
||||||
|
/// Signature length as a u8 vector.
|
||||||
|
const SIG_LEN: usize = 626;
|
||||||
|
|
||||||
|
/// Bound on the squared-norm of the signature.
|
||||||
|
const SIG_L2_BOUND: u64 = 34034726;
|
||||||
|
|
||||||
|
// 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];
|
277
src/dsa/rpo_falcon512/polynomial.rs
Normal file
277
src/dsa/rpo_falcon512/polynomial.rs
Normal file
|
@ -0,0 +1,277 @@
|
||||||
|
use super::{FalconError, Felt, Vec, LOG_N, MODULUS, MODULUS_MINUS_1_OVER_TWO, N, PK_LEN};
|
||||||
|
use core::ops::{Add, Mul, Sub};
|
||||||
|
|
||||||
|
// 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<Self, FalconError> {
|
||||||
|
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<Self, FalconError> {
|
||||||
|
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<Felt> {
|
||||||
|
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) -> <Self as Mul<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) -> <Self as Add<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) -> <Self as Add<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))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
262
src/dsa/rpo_falcon512/signature.rs
Normal file
262
src/dsa/rpo_falcon512/signature.rs
Normal file
|
@ -0,0 +1,262 @@
|
||||||
|
use super::{
|
||||||
|
ByteReader, ByteWriter, Deserializable, DeserializationError, NonceBytes, NonceElements,
|
||||||
|
Polynomial, PublicKeyBytes, Rpo256, Serializable, SignatureBytes, StarkField, Word, MODULUS, N,
|
||||||
|
SIG_L2_BOUND, ZERO,
|
||||||
|
};
|
||||||
|
use crate::utils::string::ToString;
|
||||||
|
|
||||||
|
// 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:
|
||||||
|
/// - 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:
|
||||||
|
/// 1. s1 = c - s2 * h
|
||||||
|
/// 2. |s1|^2 + |s2|^2 <= SIG_L2_BOUND
|
||||||
|
///
|
||||||
|
/// where |.| is the norm.
|
||||||
|
///
|
||||||
|
/// [Signature] also includes the extended public key which 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.
|
||||||
|
///
|
||||||
|
/// The actual 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.
|
||||||
|
/// 2. 40 bytes for the nonce.
|
||||||
|
/// 3. 625 bytes encoding the `s2` polynomial above.
|
||||||
|
///
|
||||||
|
/// The total size of the signature (including the extended public key) is 1563 bytes.
|
||||||
|
pub struct Signature {
|
||||||
|
pub(super) pk: PublicKeyBytes,
|
||||||
|
pub(super) sig: SignatureBytes,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Signature {
|
||||||
|
// PUBLIC ACCESSORS
|
||||||
|
// --------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// Returns the public key polynomial h.
|
||||||
|
pub fn pub_key_poly(&self) -> Polynomial {
|
||||||
|
// TODO: memoize
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the polynomial representation of the signature in Z_p[x]/(phi).
|
||||||
|
pub fn sig_poly(&self) -> Polynomial {
|
||||||
|
// TODO: memoize
|
||||||
|
// 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")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
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();
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SERIALIZATION / DESERIALIZATION
|
||||||
|
// ================================================================================================
|
||||||
|
|
||||||
|
impl Serializable for Signature {
|
||||||
|
fn write_into<W: ByteWriter>(&self, target: &mut W) {
|
||||||
|
target.write_bytes(&self.pk);
|
||||||
|
target.write_bytes(&self.sig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deserializable for Signature {
|
||||||
|
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
|
||||||
|
let pk: PublicKeyBytes = source.read_array()?;
|
||||||
|
let sig: SignatureBytes = source.read_array()?;
|
||||||
|
|
||||||
|
// make sure public key and signature can be decoded correctly
|
||||||
|
Polynomial::from_pub_key(&pk)
|
||||||
|
.map_err(|err| DeserializationError::InvalidValue(err.to_string()))?;
|
||||||
|
Polynomial::from_signature(&sig[41..])
|
||||||
|
.map_err(|err| DeserializationError::InvalidValue(err.to_string()))?;
|
||||||
|
|
||||||
|
Ok(Self { pk, sig })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HELPER FUNCTIONS
|
||||||
|
// ================================================================================================
|
||||||
|
|
||||||
|
/// 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];
|
||||||
|
|
||||||
|
// absorb the nonce into the state
|
||||||
|
for (&n, s) in nonce.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 = [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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// using the raw constructor is OK here because we reduce all coefficients by the modulus above
|
||||||
|
unsafe { Polynomial::new(res) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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);
|
||||||
|
result[i] = u64::from_le_bytes(buffer).into();
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
// TESTS
|
||||||
|
// ================================================================================================
|
||||||
|
|
||||||
|
#[cfg(all(test, feature = "std"))]
|
||||||
|
mod tests {
|
||||||
|
use super::{
|
||||||
|
super::{ffi::*, Felt},
|
||||||
|
*,
|
||||||
|
};
|
||||||
|
use libc::c_void;
|
||||||
|
use rand_utils::rand_vector;
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
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(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finalizes the RPO state to prepare for squeezing.
|
||||||
|
pub fn finalize(&mut self) {
|
||||||
|
unsafe { rpo128_finalize(self as *mut Rpo128Context) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hash_to_point() {
|
||||||
|
// Create a random message and transform it into a u8 vector
|
||||||
|
let msg_felts: Word = rand_vector::<Felt>(4).try_into().unwrap();
|
||||||
|
let msg_bytes = msg_felts.iter().flat_map(|e| e.as_int().to_le_bytes()).collect::<Vec<_>>();
|
||||||
|
|
||||||
|
// Create a nonce i.e. a [u8; 40] array and pack into a [Felt; 8] array.
|
||||||
|
let nonce: [u8; 40] = rand_vector::<u8>(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];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the RPO state
|
||||||
|
let mut rng = Rpo128Context::init();
|
||||||
|
|
||||||
|
// Absorb the nonce and message into the RPO state
|
||||||
|
rng.absorb(&buffer);
|
||||||
|
rng.absorb(&msg_bytes);
|
||||||
|
rng.finalize();
|
||||||
|
|
||||||
|
// Generate the coefficients of the hash-to-point polynomial.
|
||||||
|
let mut res: [u16; N] = [0; N];
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
PQCLEAN_FALCON512_CLEAN_hash_to_point_rpo(
|
||||||
|
&mut rng as *mut Rpo128Context,
|
||||||
|
res.as_mut_ptr(),
|
||||||
|
9,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the coefficients are correct
|
||||||
|
let nonce = decode_nonce(&nonce);
|
||||||
|
assert_eq!(res, hash_to_point(msg_felts, &nonce).inner());
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,7 @@
|
||||||
#[cfg_attr(test, macro_use)]
|
#[cfg_attr(test, macro_use)]
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
|
pub mod dsa;
|
||||||
pub mod hash;
|
pub mod hash;
|
||||||
pub mod merkle;
|
pub mod merkle;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
|
@ -371,21 +371,9 @@ mod tests {
|
||||||
|
|
||||||
let nodes: Vec<InnerNodeInfo> = tree.inner_nodes().collect();
|
let nodes: Vec<InnerNodeInfo> = tree.inner_nodes().collect();
|
||||||
let expected = vec![
|
let expected = vec![
|
||||||
InnerNodeInfo {
|
InnerNodeInfo { value: root, left: l1n0, right: l1n1 },
|
||||||
value: root,
|
InnerNodeInfo { value: l1n0, left: l2n0, right: l2n1 },
|
||||||
left: l1n0,
|
InnerNodeInfo { value: l1n1, left: l2n2, right: l2n3 },
|
||||||
right: l1n1,
|
|
||||||
},
|
|
||||||
InnerNodeInfo {
|
|
||||||
value: l1n0,
|
|
||||||
left: l2n0,
|
|
||||||
right: l2n1,
|
|
||||||
},
|
|
||||||
InnerNodeInfo {
|
|
||||||
value: l1n1,
|
|
||||||
left: l2n2,
|
|
||||||
right: l2n3,
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
assert_eq!(nodes, expected);
|
assert_eq!(nodes, expected);
|
||||||
|
|
||||||
|
|
|
@ -71,10 +71,7 @@ impl Mmr {
|
||||||
|
|
||||||
/// Constructor for an empty `Mmr`.
|
/// Constructor for an empty `Mmr`.
|
||||||
pub fn new() -> Mmr {
|
pub fn new() -> Mmr {
|
||||||
Mmr {
|
Mmr { forest: 0, nodes: Vec::new() }
|
||||||
forest: 0,
|
|
||||||
nodes: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ACCESSORS
|
// ACCESSORS
|
||||||
|
@ -188,10 +185,7 @@ impl Mmr {
|
||||||
.map(|offset| self.nodes[offset - 1])
|
.map(|offset| self.nodes[offset - 1])
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
MmrPeaks {
|
MmrPeaks { num_leaves: self.forest, peaks }
|
||||||
num_leaves: self.forest,
|
|
||||||
peaks,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An iterator over inner nodes in the MMR. The order of iteration is unspecified.
|
/// An iterator over inner nodes in the MMR. The order of iteration is unspecified.
|
||||||
|
|
|
@ -380,11 +380,7 @@ fn test_mmr_inner_nodes() {
|
||||||
left: LEAVES[2],
|
left: LEAVES[2],
|
||||||
right: LEAVES[3],
|
right: LEAVES[3],
|
||||||
},
|
},
|
||||||
InnerNodeInfo {
|
InnerNodeInfo { value: h0123, left: h01, right: h23 },
|
||||||
value: h0123,
|
|
||||||
left: h01,
|
|
||||||
right: h23,
|
|
||||||
},
|
|
||||||
InnerNodeInfo {
|
InnerNodeInfo {
|
||||||
value: h45,
|
value: h45,
|
||||||
left: LEAVES[4],
|
left: LEAVES[4],
|
||||||
|
|
|
@ -158,11 +158,7 @@ impl PartialMerkleTree {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(PartialMerkleTree {
|
Ok(PartialMerkleTree { max_depth, nodes, leaves })
|
||||||
max_depth,
|
|
||||||
nodes,
|
|
||||||
leaves,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PUBLIC ACCESSORS
|
// PUBLIC ACCESSORS
|
||||||
|
|
|
@ -137,11 +137,7 @@ impl<'a> Iterator for InnerNodeIterator<'a> {
|
||||||
self.value = Rpo256::merge(&[left, right]);
|
self.value = Rpo256::merge(&[left, right]);
|
||||||
self.index.move_up();
|
self.index.move_up();
|
||||||
|
|
||||||
Some(InnerNodeInfo {
|
Some(InnerNodeInfo { value: self.value, left, right })
|
||||||
value: self.value,
|
|
||||||
left,
|
|
||||||
right,
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -163,10 +159,7 @@ pub struct ValuePath {
|
||||||
impl ValuePath {
|
impl ValuePath {
|
||||||
/// Returns a new [ValuePath] instantiated from the specified value and path.
|
/// Returns a new [ValuePath] instantiated from the specified value and path.
|
||||||
pub fn new(value: RpoDigest, path: Vec<RpoDigest>) -> Self {
|
pub fn new(value: RpoDigest, path: Vec<RpoDigest>) -> Self {
|
||||||
Self {
|
Self { value, path: MerklePath::new(path) }
|
||||||
value,
|
|
||||||
path: MerklePath::new(path),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -249,10 +249,7 @@ impl SimpleSmt {
|
||||||
fn get_branch_node(&self, index: &NodeIndex) -> BranchNode {
|
fn get_branch_node(&self, index: &NodeIndex) -> BranchNode {
|
||||||
self.branches.get(index).cloned().unwrap_or_else(|| {
|
self.branches.get(index).cloned().unwrap_or_else(|| {
|
||||||
let node = self.empty_hashes[index.depth() as usize + 1];
|
let node = self.empty_hashes[index.depth() as usize + 1];
|
||||||
BranchNode {
|
BranchNode { left: node, right: node }
|
||||||
left: node,
|
|
||||||
right: node,
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -123,21 +123,9 @@ fn test_inner_node_iterator() -> Result<(), MerkleError> {
|
||||||
|
|
||||||
let nodes: Vec<InnerNodeInfo> = tree.inner_nodes().collect();
|
let nodes: Vec<InnerNodeInfo> = tree.inner_nodes().collect();
|
||||||
let expected = vec![
|
let expected = vec![
|
||||||
InnerNodeInfo {
|
InnerNodeInfo { value: root, left: l1n0, right: l1n1 },
|
||||||
value: root,
|
InnerNodeInfo { value: l1n0, left: l2n0, right: l2n1 },
|
||||||
left: l1n0,
|
InnerNodeInfo { value: l1n1, left: l2n2, right: l2n3 },
|
||||||
right: l1n1,
|
|
||||||
},
|
|
||||||
InnerNodeInfo {
|
|
||||||
value: l1n0,
|
|
||||||
left: l2n0,
|
|
||||||
right: l2n1,
|
|
||||||
},
|
|
||||||
InnerNodeInfo {
|
|
||||||
value: l1n1,
|
|
||||||
left: l2n2,
|
|
||||||
right: l2n3,
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
assert_eq!(nodes, expected);
|
assert_eq!(nodes, expected);
|
||||||
|
|
||||||
|
|
|
@ -326,11 +326,9 @@ impl<T: KvMap<RpoDigest, StoreNode>> MerkleStore<T> {
|
||||||
|
|
||||||
/// Iterator over the inner nodes of the [MerkleStore].
|
/// Iterator over the inner nodes of the [MerkleStore].
|
||||||
pub fn inner_nodes(&self) -> impl Iterator<Item = InnerNodeInfo> + '_ {
|
pub fn inner_nodes(&self) -> impl Iterator<Item = InnerNodeInfo> + '_ {
|
||||||
self.nodes.iter().map(|(r, n)| InnerNodeInfo {
|
self.nodes
|
||||||
value: *r,
|
.iter()
|
||||||
left: n.left,
|
.map(|(r, n)| InnerNodeInfo { value: *r, left: n.left, right: n.right })
|
||||||
right: n.right,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterator over the non-empty leaves of the Merkle tree associated with the specified `root`
|
/// Iterator over the non-empty leaves of the Merkle tree associated with the specified `root`
|
||||||
|
@ -450,13 +448,7 @@ impl<T: KvMap<RpoDigest, StoreNode>> MerkleStore<T> {
|
||||||
right_root: RpoDigest,
|
right_root: RpoDigest,
|
||||||
) -> Result<RpoDigest, MerkleError> {
|
) -> Result<RpoDigest, MerkleError> {
|
||||||
let parent = Rpo256::merge(&[left_root, right_root]);
|
let parent = Rpo256::merge(&[left_root, right_root]);
|
||||||
self.nodes.insert(
|
self.nodes.insert(parent, StoreNode { left: left_root, right: right_root });
|
||||||
parent,
|
|
||||||
StoreNode {
|
|
||||||
left: left_root,
|
|
||||||
right: right_root,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(parent)
|
Ok(parent)
|
||||||
}
|
}
|
||||||
|
@ -551,15 +543,10 @@ impl<T: KvMap<RpoDigest, StoreNode>> FromIterator<(RpoDigest, StoreNode)> for Me
|
||||||
// ================================================================================================
|
// ================================================================================================
|
||||||
impl<T: KvMap<RpoDigest, StoreNode>> Extend<InnerNodeInfo> for MerkleStore<T> {
|
impl<T: KvMap<RpoDigest, StoreNode>> Extend<InnerNodeInfo> for MerkleStore<T> {
|
||||||
fn extend<I: IntoIterator<Item = InnerNodeInfo>>(&mut self, iter: I) {
|
fn extend<I: IntoIterator<Item = InnerNodeInfo>>(&mut self, iter: I) {
|
||||||
self.nodes.extend(iter.into_iter().map(|info| {
|
self.nodes.extend(
|
||||||
(
|
iter.into_iter()
|
||||||
info.value,
|
.map(|info| (info.value, StoreNode { left: info.left, right: info.right })),
|
||||||
StoreNode {
|
);
|
||||||
left: info.left,
|
|
||||||
right: info.right,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -646,17 +633,12 @@ impl<T: KvMap<RpoDigest, StoreNode>> Deserializable for MerkleStore<T> {
|
||||||
/// Creates empty hashes for all the subtrees of a tree with a max depth of 255.
|
/// Creates empty hashes for all the subtrees of a tree with a max depth of 255.
|
||||||
fn empty_hashes() -> impl IntoIterator<Item = (RpoDigest, StoreNode)> {
|
fn empty_hashes() -> impl IntoIterator<Item = (RpoDigest, StoreNode)> {
|
||||||
let subtrees = EmptySubtreeRoots::empty_hashes(255);
|
let subtrees = EmptySubtreeRoots::empty_hashes(255);
|
||||||
subtrees.iter().rev().copied().zip(subtrees.iter().rev().skip(1).copied()).map(
|
subtrees
|
||||||
|(child, parent)| {
|
.iter()
|
||||||
(
|
.rev()
|
||||||
parent,
|
.copied()
|
||||||
StoreNode {
|
.zip(subtrees.iter().rev().skip(1).copied())
|
||||||
left: child,
|
.map(|(child, parent)| (parent, StoreNode { left: child, right: child }))
|
||||||
right: child,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Consumes an iterator of [InnerNodeInfo] and returns an iterator of `(value, node)` tuples
|
/// Consumes an iterator of [InnerNodeInfo] and returns an iterator of `(value, node)` tuples
|
||||||
|
@ -666,14 +648,6 @@ fn combine_nodes_with_empty_hashes(
|
||||||
) -> impl Iterator<Item = (RpoDigest, StoreNode)> {
|
) -> impl Iterator<Item = (RpoDigest, StoreNode)> {
|
||||||
nodes
|
nodes
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|info| {
|
.map(|info| (info.value, StoreNode { left: info.left, right: info.right }))
|
||||||
(
|
|
||||||
info.value,
|
|
||||||
StoreNode {
|
|
||||||
left: info.left,
|
|
||||||
right: info.right,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.chain(empty_hashes())
|
.chain(empty_hashes())
|
||||||
}
|
}
|
||||||
|
|
|
@ -213,10 +213,7 @@ impl StoreEntry {
|
||||||
|
|
||||||
/// Returns an iterator over all key-value pairs in this entry.
|
/// Returns an iterator over all key-value pairs in this entry.
|
||||||
pub fn iter(&self) -> impl Iterator<Item = &(RpoDigest, Word)> {
|
pub fn iter(&self) -> impl Iterator<Item = &(RpoDigest, Word)> {
|
||||||
EntryIterator {
|
EntryIterator { entry: self, pos: 0 }
|
||||||
entry: self,
|
|
||||||
pos: 0,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// STATE MUTATORS
|
// STATE MUTATORS
|
||||||
|
|
|
@ -83,10 +83,7 @@ impl std::error::Error for HexParseError {}
|
||||||
pub fn hex_to_bytes<const N: usize>(value: &str) -> Result<[u8; N], HexParseError> {
|
pub fn hex_to_bytes<const N: usize>(value: &str) -> Result<[u8; N], HexParseError> {
|
||||||
let expected: usize = (N * 2) + 2;
|
let expected: usize = (N * 2) + 2;
|
||||||
if value.len() != expected {
|
if value.len() != expected {
|
||||||
return Err(HexParseError::InvalidLength {
|
return Err(HexParseError::InvalidLength { expected, got: value.len() });
|
||||||
expected,
|
|
||||||
got: value.len(),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !value.starts_with("0x") {
|
if !value.starts_with("0x") {
|
||||||
|
|
Loading…
Add table
Reference in a new issue