@ -0,0 +1,3 @@ |
|||
[submodule "PQClean"] |
|||
path = PQClean |
|||
url = https://github.com/PQClean/PQClean.git |
@ -0,0 +1 @@ |
|||
pub mod rpo_falcon512;
|
@ -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 {}
|
@ -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); |
@ -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); |
|||
} |
@ -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--; |
|||
} |
|||
} |
@ -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); |
@ -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()
|
|||
)
|
|||
);
|
|||
}
|
|||
}
|
|||
}
|
@ -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()))
|
|||
}
|
|||
}
|
@ -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];
|
@ -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))
|
|||
);
|
|||
}
|
|||
}
|
@ -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());
|
|||
}
|
|||
}
|