Tracking PR for v0.9.0 release (#278)

* chore: update crate version to v0.9.0
* chore: remove deprecated re-exports
* chore: remove Box re-export
* feat: implement pure-Rust keygen and signing for RpoFalcon512 (#285)
* feat: add reproducible builds (#296)
* fix: address a few issues for migrating Miden VM  (#298)
* feat: add RngCore supertrait for FeltRng (#299)

---------

Co-authored-by: Al-Kindi-0 <82364884+Al-Kindi-0@users.noreply.github.com>
Co-authored-by: Paul-Henry Kajfasz <42912740+phklive@users.noreply.github.com>
This commit is contained in:
Bobbin Threadbare
2024-03-24 08:38:08 -07:00
committed by GitHub
parent 2be17b74fb
commit 5a2e917dd5
39 changed files with 5964 additions and 2334 deletions

View File

@@ -1,56 +0,0 @@
use core::fmt;
use super::{LOG_N, MODULUS, PK_LEN};
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum FalconError {
KeyGenerationFailed,
PubKeyDecodingExtraData,
PubKeyDecodingInvalidCoefficient(u32),
PubKeyDecodingInvalidLength(usize),
PubKeyDecodingInvalidTag(u8),
SigDecodingTooBigHighBits(u32),
SigDecodingInvalidRemainder,
SigDecodingNonZeroUnusedBitsLastByte,
SigDecodingMinusZero,
SigDecodingIncorrectEncodingAlgorithm,
SigDecodingNotSupportedDegree(u8),
SigGenerationFailed,
}
impl fmt::Display for FalconError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use FalconError::*;
match self {
KeyGenerationFailed => write!(f, "Failed to generate a private-public key pair"),
PubKeyDecodingExtraData => {
write!(f, "Failed to decode public key: input not fully consumed")
}
PubKeyDecodingInvalidCoefficient(val) => {
write!(f, "Failed to decode public key: coefficient {val} is greater than or equal to the field modulus {MODULUS}")
}
PubKeyDecodingInvalidLength(len) => {
write!(f, "Failed to decode public key: expected {PK_LEN} bytes but received {len}")
}
PubKeyDecodingInvalidTag(byte) => {
write!(f, "Failed to decode public key: expected the first byte to be {LOG_N} but was {byte}")
}
SigDecodingTooBigHighBits(m) => {
write!(f, "Failed to decode signature: high bits {m} exceed 2048")
}
SigDecodingInvalidRemainder => {
write!(f, "Failed to decode signature: incorrect remaining data")
}
SigDecodingNonZeroUnusedBitsLastByte => {
write!(f, "Failed to decode signature: Non-zero unused bits in the last byte")
}
SigDecodingMinusZero => write!(f, "Failed to decode signature: -0 is forbidden"),
SigDecodingIncorrectEncodingAlgorithm => write!(f, "Failed to decode signature: not supported encoding algorithm"),
SigDecodingNotSupportedDegree(log_n) => write!(f, "Failed to decode signature: only supported irreducible polynomial degree is 512, 2^{log_n} was provided"),
SigGenerationFailed => write!(f, "Failed to generate a signature"),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for FalconError {}

View File

@@ -1,402 +0,0 @@
/*
* Wrapper for implementing the PQClean API.
*/
#include <string.h>
#include "randombytes.h"
#include "falcon.h"
#include "inner.h"
#include "rpo.h"
#define NONCELEN 40
/*
* Encoding formats (nnnn = log of degree, 9 for Falcon-512, 10 for Falcon-1024)
*
* private key:
* header byte: 0101nnnn
* private f (6 or 5 bits by element, depending on degree)
* private g (6 or 5 bits by element, depending on degree)
* private F (8 bits by element)
*
* public key:
* header byte: 0000nnnn
* public h (14 bits by element)
*
* signature:
* header byte: 0011nnnn
* nonce 40 bytes
* value (12 bits by element)
*
* message + signature:
* signature length (2 bytes, big-endian)
* nonce 40 bytes
* message
* header byte: 0010nnnn
* value (12 bits by element)
* (signature length is 1+len(value), not counting the nonce)
*/
/* see falcon.h */
int PQCLEAN_FALCON512_CLEAN_crypto_sign_keypair_from_seed_rpo(
uint8_t *pk,
uint8_t *sk,
unsigned char *seed
) {
union
{
uint8_t b[FALCON_KEYGEN_TEMP_9];
uint64_t dummy_u64;
fpr dummy_fpr;
} tmp;
int8_t f[512], g[512], F[512];
uint16_t h[512];
inner_shake256_context rng;
size_t u, v;
/*
* Generate key pair.
*/
inner_shake256_init(&rng);
inner_shake256_inject(&rng, seed, sizeof seed);
inner_shake256_flip(&rng);
PQCLEAN_FALCON512_CLEAN_keygen(&rng, f, g, F, NULL, h, 9, tmp.b);
inner_shake256_ctx_release(&rng);
/*
* Encode private key.
*/
sk[0] = 0x50 + 9;
u = 1;
v = PQCLEAN_FALCON512_CLEAN_trim_i8_encode(
sk + u, PQCLEAN_FALCON512_CLEAN_CRYPTO_SECRETKEYBYTES - u,
f, 9, PQCLEAN_FALCON512_CLEAN_max_fg_bits[9]);
if (v == 0)
{
return -1;
}
u += v;
v = PQCLEAN_FALCON512_CLEAN_trim_i8_encode(
sk + u, PQCLEAN_FALCON512_CLEAN_CRYPTO_SECRETKEYBYTES - u,
g, 9, PQCLEAN_FALCON512_CLEAN_max_fg_bits[9]);
if (v == 0)
{
return -1;
}
u += v;
v = PQCLEAN_FALCON512_CLEAN_trim_i8_encode(
sk + u, PQCLEAN_FALCON512_CLEAN_CRYPTO_SECRETKEYBYTES - u,
F, 9, PQCLEAN_FALCON512_CLEAN_max_FG_bits[9]);
if (v == 0)
{
return -1;
}
u += v;
if (u != PQCLEAN_FALCON512_CLEAN_CRYPTO_SECRETKEYBYTES)
{
return -1;
}
/*
* Encode public key.
*/
pk[0] = 0x00 + 9;
v = PQCLEAN_FALCON512_CLEAN_modq_encode(
pk + 1, PQCLEAN_FALCON512_CLEAN_CRYPTO_PUBLICKEYBYTES - 1,
h, 9);
if (v != PQCLEAN_FALCON512_CLEAN_CRYPTO_PUBLICKEYBYTES - 1)
{
return -1;
}
return 0;
}
int PQCLEAN_FALCON512_CLEAN_crypto_sign_keypair_rpo(
uint8_t *pk,
uint8_t *sk
) {
unsigned char seed[48];
/*
* Generate a random seed.
*/
randombytes(seed, sizeof seed);
return PQCLEAN_FALCON512_CLEAN_crypto_sign_keypair_from_seed_rpo(pk, sk, seed);
}
/*
* Compute the signature. nonce[] receives the nonce and must have length
* NONCELEN bytes. sigbuf[] receives the signature value (without nonce
* or header byte), with *sigbuflen providing the maximum value length and
* receiving the actual value length.
*
* If a signature could be computed but not encoded because it would
* exceed the output buffer size, then a new signature is computed. If
* the provided buffer size is too low, this could loop indefinitely, so
* the caller must provide a size that can accommodate signatures with a
* large enough probability.
*
* Return value: 0 on success, -1 on error.
*/
static int do_sign(
uint8_t *nonce,
uint8_t *sigbuf,
size_t *sigbuflen,
const uint8_t *m,
size_t mlen,
const uint8_t *sk
) {
union
{
uint8_t b[72 * 512];
uint64_t dummy_u64;
fpr dummy_fpr;
} tmp;
int8_t f[512], g[512], F[512], G[512];
struct
{
int16_t sig[512];
uint16_t hm[512];
} r;
unsigned char seed[48];
inner_shake256_context sc;
rpo128_context rc;
size_t u, v;
/*
* Decode the private key.
*/
if (sk[0] != 0x50 + 9)
{
return -1;
}
u = 1;
v = PQCLEAN_FALCON512_CLEAN_trim_i8_decode(
f, 9, PQCLEAN_FALCON512_CLEAN_max_fg_bits[9],
sk + u, PQCLEAN_FALCON512_CLEAN_CRYPTO_SECRETKEYBYTES - u);
if (v == 0)
{
return -1;
}
u += v;
v = PQCLEAN_FALCON512_CLEAN_trim_i8_decode(
g, 9, PQCLEAN_FALCON512_CLEAN_max_fg_bits[9],
sk + u, PQCLEAN_FALCON512_CLEAN_CRYPTO_SECRETKEYBYTES - u);
if (v == 0)
{
return -1;
}
u += v;
v = PQCLEAN_FALCON512_CLEAN_trim_i8_decode(
F, 9, PQCLEAN_FALCON512_CLEAN_max_FG_bits[9],
sk + u, PQCLEAN_FALCON512_CLEAN_CRYPTO_SECRETKEYBYTES - u);
if (v == 0)
{
return -1;
}
u += v;
if (u != PQCLEAN_FALCON512_CLEAN_CRYPTO_SECRETKEYBYTES)
{
return -1;
}
if (!PQCLEAN_FALCON512_CLEAN_complete_private(G, f, g, F, 9, tmp.b))
{
return -1;
}
/*
* Create a random nonce (40 bytes).
*/
randombytes(nonce, NONCELEN);
/* ==== Start: Deviation from the reference implementation ================================= */
// Transform the nonce into 8 chunks each of size 5 bytes. We do this in order to be sure that
// the conversion to field elements succeeds
uint8_t buffer[64];
memset(buffer, 0, 64);
for (size_t i = 0; i < 8; i++)
{
buffer[8 * i] = nonce[5 * i];
buffer[8 * i + 1] = nonce[5 * i + 1];
buffer[8 * i + 2] = nonce[5 * i + 2];
buffer[8 * i + 3] = nonce[5 * i + 3];
buffer[8 * i + 4] = nonce[5 * i + 4];
}
/*
* Hash message nonce + message into a vector.
*/
rpo128_init(&rc);
rpo128_absorb(&rc, buffer, NONCELEN + 24);
rpo128_absorb(&rc, m, mlen);
rpo128_finalize(&rc);
PQCLEAN_FALCON512_CLEAN_hash_to_point_rpo(&rc, r.hm, 9);
rpo128_release(&rc);
/* ==== End: Deviation from the reference implementation =================================== */
/*
* Initialize a RNG.
*/
randombytes(seed, sizeof seed);
inner_shake256_init(&sc);
inner_shake256_inject(&sc, seed, sizeof seed);
inner_shake256_flip(&sc);
/*
* Compute and return the signature. This loops until a signature
* value is found that fits in the provided buffer.
*/
for (;;)
{
PQCLEAN_FALCON512_CLEAN_sign_dyn(r.sig, &sc, f, g, F, G, r.hm, 9, tmp.b);
v = PQCLEAN_FALCON512_CLEAN_comp_encode(sigbuf, *sigbuflen, r.sig, 9);
if (v != 0)
{
inner_shake256_ctx_release(&sc);
*sigbuflen = v;
return 0;
}
}
}
/*
* Verify a signature. The nonce has size NONCELEN bytes. sigbuf[]
* (of size sigbuflen) contains the signature value, not including the
* header byte or nonce. Return value is 0 on success, -1 on error.
*/
static int do_verify(
const uint8_t *nonce,
const uint8_t *sigbuf,
size_t sigbuflen,
const uint8_t *m,
size_t mlen,
const uint8_t *pk
) {
union
{
uint8_t b[2 * 512];
uint64_t dummy_u64;
fpr dummy_fpr;
} tmp;
uint16_t h[512], hm[512];
int16_t sig[512];
rpo128_context rc;
/*
* Decode public key.
*/
if (pk[0] != 0x00 + 9)
{
return -1;
}
if (PQCLEAN_FALCON512_CLEAN_modq_decode(h, 9,
pk + 1, PQCLEAN_FALCON512_CLEAN_CRYPTO_PUBLICKEYBYTES - 1)
!= PQCLEAN_FALCON512_CLEAN_CRYPTO_PUBLICKEYBYTES - 1)
{
return -1;
}
PQCLEAN_FALCON512_CLEAN_to_ntt_monty(h, 9);
/*
* Decode signature.
*/
if (sigbuflen == 0)
{
return -1;
}
if (PQCLEAN_FALCON512_CLEAN_comp_decode(sig, 9, sigbuf, sigbuflen) != sigbuflen)
{
return -1;
}
/* ==== Start: Deviation from the reference implementation ================================= */
/*
* Hash nonce + message into a vector.
*/
// Transform the nonce into 8 chunks each of size 5 bytes. We do this in order to be sure that
// the conversion to field elements succeeds
uint8_t buffer[64];
memset(buffer, 0, 64);
for (size_t i = 0; i < 8; i++)
{
buffer[8 * i] = nonce[5 * i];
buffer[8 * i + 1] = nonce[5 * i + 1];
buffer[8 * i + 2] = nonce[5 * i + 2];
buffer[8 * i + 3] = nonce[5 * i + 3];
buffer[8 * i + 4] = nonce[5 * i + 4];
}
rpo128_init(&rc);
rpo128_absorb(&rc, buffer, NONCELEN + 24);
rpo128_absorb(&rc, m, mlen);
rpo128_finalize(&rc);
PQCLEAN_FALCON512_CLEAN_hash_to_point_rpo(&rc, hm, 9);
rpo128_release(&rc);
/* === End: Deviation from the reference implementation ==================================== */
/*
* Verify signature.
*/
if (!PQCLEAN_FALCON512_CLEAN_verify_raw(hm, sig, h, 9, tmp.b))
{
return -1;
}
return 0;
}
/* see falcon.h */
int PQCLEAN_FALCON512_CLEAN_crypto_sign_signature_rpo(
uint8_t *sig,
size_t *siglen,
const uint8_t *m,
size_t mlen,
const uint8_t *sk
) {
/*
* The PQCLEAN_FALCON512_CLEAN_CRYPTO_BYTES constant is used for
* the signed message object (as produced by crypto_sign())
* and includes a two-byte length value, so we take care here
* to only generate signatures that are two bytes shorter than
* the maximum. This is done to ensure that crypto_sign()
* and crypto_sign_signature() produce the exact same signature
* value, if used on the same message, with the same private key,
* and using the same output from randombytes() (this is for
* reproducibility of tests).
*/
size_t vlen;
vlen = PQCLEAN_FALCON512_CLEAN_CRYPTO_BYTES - NONCELEN - 3;
if (do_sign(sig + 1, sig + 1 + NONCELEN, &vlen, m, mlen, sk) < 0)
{
return -1;
}
sig[0] = 0x30 + 9;
*siglen = 1 + NONCELEN + vlen;
return 0;
}
/* see falcon.h */
int PQCLEAN_FALCON512_CLEAN_crypto_sign_verify_rpo(
const uint8_t *sig,
size_t siglen,
const uint8_t *m,
size_t mlen,
const uint8_t *pk
) {
if (siglen < 1 + NONCELEN)
{
return -1;
}
if (sig[0] != 0x30 + 9)
{
return -1;
}
return do_verify(sig + 1, sig + 1 + NONCELEN, siglen - 1 - NONCELEN, m, mlen, pk);
}

View File

@@ -1,66 +0,0 @@
#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);

View File

@@ -1,582 +0,0 @@
/*
* 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
static uint64_t add_mod_p(uint64_t a, uint64_t b)
{
a = P - a;
uint64_t res = b - a;
if (b < a)
res += P;
return res;
}
static uint64_t sub_mod_p(uint64_t a, uint64_t b)
{
uint64_t r = a - b;
if (a < b)
r += P;
return r;
}
static uint64_t reduce_mod_p(uint64_t b, uint64_t a)
{
uint32_t d = b >> 32,
c = b;
if (a >= P)
a -= P;
a = sub_mod_p(a, c);
a = sub_mod_p(a, d);
a = add_mod_p(a, ((uint64_t)c) << 32);
return a;
}
static uint64_t mult_mod_p(uint64_t x, uint64_t y)
{
uint32_t a = x,
b = x >> 32,
c = y,
d = y >> 32;
/* first synthesize the product using 32*32 -> 64 bit multiplies */
x = b * (uint64_t)c; /* b*c */
y = a * (uint64_t)d; /* a*d */
uint64_t e = a * (uint64_t)c, /* a*c */
f = b * (uint64_t)d, /* b*d */
t;
x += y; /* b*c + a*d */
/* carry? */
if (x < y)
f += 1LL << 32; /* carry into upper 32 bits - can't overflow */
t = x << 32;
e += t; /* a*c + LSW(b*c + a*d) */
/* carry? */
if (e < t)
f += 1; /* carry into upper 64 bits - can't overflow*/
t = x >> 32;
f += t; /* b*d + MSW(b*c + a*d) */
/* can't overflow */
/* now reduce: (b*d + MSW(b*c + a*d), a*c + LSW(b*c + a*d)) */
return reduce_mod_p(f, e);
}
/* ================================================================================================
* RPO128 Permutation
*/
#define STATE_WIDTH 12
#define NUM_ROUNDS 7
/*
* MDS matrix
*/
static const uint64_t MDS[12][12] = {
{ 7, 23, 8, 26, 13, 10, 9, 7, 6, 22, 21, 8 },
{ 8, 7, 23, 8, 26, 13, 10, 9, 7, 6, 22, 21 },
{ 21, 8, 7, 23, 8, 26, 13, 10, 9, 7, 6, 22 },
{ 22, 21, 8, 7, 23, 8, 26, 13, 10, 9, 7, 6 },
{ 6, 22, 21, 8, 7, 23, 8, 26, 13, 10, 9, 7 },
{ 7, 6, 22, 21, 8, 7, 23, 8, 26, 13, 10, 9 },
{ 9, 7, 6, 22, 21, 8, 7, 23, 8, 26, 13, 10 },
{ 10, 9, 7, 6, 22, 21, 8, 7, 23, 8, 26, 13 },
{ 13, 10, 9, 7, 6, 22, 21, 8, 7, 23, 8, 26 },
{ 26, 13, 10, 9, 7, 6, 22, 21, 8, 7, 23, 8 },
{ 8, 26, 13, 10, 9, 7, 6, 22, 21, 8, 7, 23 },
{ 23, 8, 26, 13, 10, 9, 7, 6, 22, 21, 8, 7 },
};
/*
* Round constants.
*/
static const uint64_t ARK1[7][12] = {
{
5789762306288267392ULL,
6522564764413701783ULL,
17809893479458208203ULL,
107145243989736508ULL,
6388978042437517382ULL,
15844067734406016715ULL,
9975000513555218239ULL,
3344984123768313364ULL,
9959189626657347191ULL,
12960773468763563665ULL,
9602914297752488475ULL,
16657542370200465908ULL,
},
{
12987190162843096997ULL,
653957632802705281ULL,
4441654670647621225ULL,
4038207883745915761ULL,
5613464648874830118ULL,
13222989726778338773ULL,
3037761201230264149ULL,
16683759727265180203ULL,
8337364536491240715ULL,
3227397518293416448ULL,
8110510111539674682ULL,
2872078294163232137ULL,
},
{
18072785500942327487ULL,
6200974112677013481ULL,
17682092219085884187ULL,
10599526828986756440ULL,
975003873302957338ULL,
8264241093196931281ULL,
10065763900435475170ULL,
2181131744534710197ULL,
6317303992309418647ULL,
1401440938888741532ULL,
8884468225181997494ULL,
13066900325715521532ULL,
},
{
5674685213610121970ULL,
5759084860419474071ULL,
13943282657648897737ULL,
1352748651966375394ULL,
17110913224029905221ULL,
1003883795902368422ULL,
4141870621881018291ULL,
8121410972417424656ULL,
14300518605864919529ULL,
13712227150607670181ULL,
17021852944633065291ULL,
6252096473787587650ULL,
},
{
4887609836208846458ULL,
3027115137917284492ULL,
9595098600469470675ULL,
10528569829048484079ULL,
7864689113198939815ULL,
17533723827845969040ULL,
5781638039037710951ULL,
17024078752430719006ULL,
109659393484013511ULL,
7158933660534805869ULL,
2955076958026921730ULL,
7433723648458773977ULL,
},
{
16308865189192447297ULL,
11977192855656444890ULL,
12532242556065780287ULL,
14594890931430968898ULL,
7291784239689209784ULL,
5514718540551361949ULL,
10025733853830934803ULL,
7293794580341021693ULL,
6728552937464861756ULL,
6332385040983343262ULL,
13277683694236792804ULL,
2600778905124452676ULL,
},
{
7123075680859040534ULL,
1034205548717903090ULL,
7717824418247931797ULL,
3019070937878604058ULL,
11403792746066867460ULL,
10280580802233112374ULL,
337153209462421218ULL,
13333398568519923717ULL,
3596153696935337464ULL,
8104208463525993784ULL,
14345062289456085693ULL,
17036731477169661256ULL,
}};
const uint64_t ARK2[7][12] = {
{
6077062762357204287ULL,
15277620170502011191ULL,
5358738125714196705ULL,
14233283787297595718ULL,
13792579614346651365ULL,
11614812331536767105ULL,
14871063686742261166ULL,
10148237148793043499ULL,
4457428952329675767ULL,
15590786458219172475ULL,
10063319113072092615ULL,
14200078843431360086ULL,
},
{
6202948458916099932ULL,
17690140365333231091ULL,
3595001575307484651ULL,
373995945117666487ULL,
1235734395091296013ULL,
14172757457833931602ULL,
707573103686350224ULL,
15453217512188187135ULL,
219777875004506018ULL,
17876696346199469008ULL,
17731621626449383378ULL,
2897136237748376248ULL,
},
{
8023374565629191455ULL,
15013690343205953430ULL,
4485500052507912973ULL,
12489737547229155153ULL,
9500452585969030576ULL,
2054001340201038870ULL,
12420704059284934186ULL,
355990932618543755ULL,
9071225051243523860ULL,
12766199826003448536ULL,
9045979173463556963ULL,
12934431667190679898ULL,
},
{
18389244934624494276ULL,
16731736864863925227ULL,
4440209734760478192ULL,
17208448209698888938ULL,
8739495587021565984ULL,
17000774922218161967ULL,
13533282547195532087ULL,
525402848358706231ULL,
16987541523062161972ULL,
5466806524462797102ULL,
14512769585918244983ULL,
10973956031244051118ULL,
},
{
6982293561042362913ULL,
14065426295947720331ULL,
16451845770444974180ULL,
7139138592091306727ULL,
9012006439959783127ULL,
14619614108529063361ULL,
1394813199588124371ULL,
4635111139507788575ULL,
16217473952264203365ULL,
10782018226466330683ULL,
6844229992533662050ULL,
7446486531695178711ULL,
},
{
3736792340494631448ULL,
577852220195055341ULL,
6689998335515779805ULL,
13886063479078013492ULL,
14358505101923202168ULL,
7744142531772274164ULL,
16135070735728404443ULL,
12290902521256031137ULL,
12059913662657709804ULL,
16456018495793751911ULL,
4571485474751953524ULL,
17200392109565783176ULL,
},
{
17130398059294018733ULL,
519782857322261988ULL,
9625384390925085478ULL,
1664893052631119222ULL,
7629576092524553570ULL,
3485239601103661425ULL,
9755891797164033838ULL,
15218148195153269027ULL,
16460604813734957368ULL,
9643968136937729763ULL,
3611348709641382851ULL,
18256379591337759196ULL,
},
};
static void apply_sbox(uint64_t *const state)
{
for (uint64_t i = 0; i < STATE_WIDTH; i++)
{
uint64_t t2 = mult_mod_p(*(state + i), *(state + i));
uint64_t t4 = mult_mod_p(t2, t2);
*(state + i) = mult_mod_p(*(state + i), mult_mod_p(t2, t4));
}
}
static void apply_mds(uint64_t *state)
{
uint64_t res[STATE_WIDTH];
for (uint64_t i = 0; i < STATE_WIDTH; i++)
{
res[i] = 0;
}
for (uint64_t i = 0; i < STATE_WIDTH; i++)
{
for (uint64_t j = 0; j < STATE_WIDTH; j++)
{
res[i] = add_mod_p(res[i], mult_mod_p(MDS[i][j], *(state + j)));
}
}
for (uint64_t i = 0; i < STATE_WIDTH; i++)
{
*(state + i) = res[i];
}
}
static void apply_constants(uint64_t *const state, const uint64_t *ark)
{
for (uint64_t i = 0; i < STATE_WIDTH; i++)
{
*(state + i) = add_mod_p(*(state + i), *(ark + i));
}
}
static void exp_acc(const uint64_t m, const uint64_t *base, const uint64_t *tail, uint64_t *const res)
{
for (uint64_t i = 0; i < m; i++)
{
for (uint64_t j = 0; j < STATE_WIDTH; j++)
{
if (i == 0)
{
*(res + j) = mult_mod_p(*(base + j), *(base + j));
}
else
{
*(res + j) = mult_mod_p(*(res + j), *(res + j));
}
}
}
for (uint64_t i = 0; i < STATE_WIDTH; i++)
{
*(res + i) = mult_mod_p(*(res + i), *(tail + i));
}
}
static void apply_inv_sbox(uint64_t *const state)
{
uint64_t t1[STATE_WIDTH];
for (uint64_t i = 0; i < STATE_WIDTH; i++)
{
t1[i] = 0;
}
for (uint64_t i = 0; i < STATE_WIDTH; i++)
{
t1[i] = mult_mod_p(*(state + i), *(state + i));
}
uint64_t t2[STATE_WIDTH];
for (uint64_t i = 0; i < STATE_WIDTH; i++)
{
t2[i] = 0;
}
for (uint64_t i = 0; i < STATE_WIDTH; i++)
{
t2[i] = mult_mod_p(t1[i], t1[i]);
}
uint64_t t3[STATE_WIDTH];
for (uint64_t i = 0; i < STATE_WIDTH; i++)
{
t3[i] = 0;
}
exp_acc(3, t2, t2, t3);
uint64_t t4[STATE_WIDTH];
for (uint64_t i = 0; i < STATE_WIDTH; i++)
{
t4[i] = 0;
}
exp_acc(6, t3, t3, t4);
uint64_t tmp[STATE_WIDTH];
for (uint64_t i = 0; i < STATE_WIDTH; i++)
{
tmp[i] = 0;
}
exp_acc(12, t4, t4, tmp);
uint64_t t5[STATE_WIDTH];
for (uint64_t i = 0; i < STATE_WIDTH; i++)
{
t5[i] = 0;
}
exp_acc(6, tmp, t3, t5);
uint64_t t6[STATE_WIDTH];
for (uint64_t i = 0; i < STATE_WIDTH; i++)
{
t6[i] = 0;
}
exp_acc(31, t5, t5, t6);
for (uint64_t i = 0; i < STATE_WIDTH; i++)
{
uint64_t a = mult_mod_p(mult_mod_p(t6[i], t6[i]), t5[i]);
a = mult_mod_p(a, a);
a = mult_mod_p(a, a);
uint64_t b = mult_mod_p(mult_mod_p(t1[i], t2[i]), *(state + i));
*(state + i) = mult_mod_p(a, b);
}
}
static void apply_round(uint64_t *const state, const uint64_t round)
{
apply_mds(state);
apply_constants(state, ARK1[round]);
apply_sbox(state);
apply_mds(state);
apply_constants(state, ARK2[round]);
apply_inv_sbox(state);
}
static void apply_permutation(uint64_t *state)
{
for (uint64_t i = 0; i < NUM_ROUNDS; i++)
{
apply_round(state, i);
}
}
/* ================================================================================================
* RPO128 implementation. This is supposed to substitute SHAKE256 in the hash-to-point algorithm.
*/
#include "rpo.h"
void rpo128_init(rpo128_context *rc)
{
rc->dptr = 32;
memset(rc->st.A, 0, sizeof rc->st.A);
}
void rpo128_absorb(rpo128_context *rc, const uint8_t *in, size_t len)
{
size_t dptr;
dptr = (size_t)rc->dptr;
while (len > 0)
{
size_t clen, u;
/* 136 * 8 = 1088 bit for the rate portion in the case of SHAKE256
* For RPO, this is 64 * 8 = 512 bits
* The capacity for SHAKE256 is at the end while for RPO128 it is at the beginning
*/
clen = 96 - dptr;
if (clen > len)
{
clen = len;
}
for (u = 0; u < clen; u++)
{
rc->st.dbuf[dptr + u] = in[u];
}
dptr += clen;
in += clen;
len -= clen;
if (dptr == 96)
{
apply_permutation(rc->st.A);
dptr = 32;
}
}
rc->dptr = dptr;
}
void rpo128_finalize(rpo128_context *rc)
{
// Set dptr to the end of the buffer, so that first call to extract will call the permutation.
rc->dptr = 96;
}
void rpo128_squeeze(rpo128_context *rc, uint8_t *out, size_t len)
{
size_t dptr;
dptr = (size_t)rc->dptr;
while (len > 0)
{
size_t clen;
if (dptr == 96)
{
apply_permutation(rc->st.A);
dptr = 32;
}
clen = 96 - dptr;
if (clen > len)
{
clen = len;
}
len -= clen;
memcpy(out, rc->st.dbuf + dptr, clen);
dptr += clen;
out += clen;
}
rc->dptr = dptr;
}
void rpo128_release(rpo128_context *rc)
{
memset(rc->st.A, 0, sizeof rc->st.A);
rc->dptr = 32;
}
/* ================================================================================================
* Hash-to-Point algorithm implementation based on RPO128
*/
void PQCLEAN_FALCON512_CLEAN_hash_to_point_rpo(rpo128_context *rc, uint16_t *x, unsigned logn)
{
/*
* This implementation avoids the rejection sampling step needed in the
* per-the-spec implementation. It uses a remark in https://falcon-sign.info/falcon.pdf
* page 31, which argues that the current variant is secure for the parameters set by NIST.
* Avoiding the rejection-sampling step leads to an implementation that is constant-time.
* TODO: Check that the current implementation is indeed constant-time.
*/
size_t n;
n = (size_t)1 << logn;
while (n > 0)
{
uint8_t buf[8];
uint64_t w;
rpo128_squeeze(rc, (void *)buf, sizeof buf);
w = ((uint64_t)(buf[7]) << 56) |
((uint64_t)(buf[6]) << 48) |
((uint64_t)(buf[5]) << 40) |
((uint64_t)(buf[4]) << 32) |
((uint64_t)(buf[3]) << 24) |
((uint64_t)(buf[2]) << 16) |
((uint64_t)(buf[1]) << 8) |
((uint64_t)(buf[0]));
w %= M;
*x++ = (uint16_t)w;
n--;
}
}

View File

@@ -1,83 +0,0 @@
#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);

View File

@@ -1,194 +0,0 @@
use core::ffi::c_int;
// C IMPLEMENTATION INTERFACE
// ================================================================================================
#[link(name = "rpo_falcon512", kind = "static")]
extern "C" {
/// Generate a new key pair. Public key goes into pk[], private key in sk[].
/// Key sizes are exact (in bytes):
/// - public (pk): 897
/// - private (sk): 1281
///
/// Return value: 0 on success, -1 on error.
pub fn PQCLEAN_FALCON512_CLEAN_crypto_sign_keypair_rpo(pk: *mut u8, sk: *mut u8) -> c_int;
/// Generate a new key pair from seed. Public key goes into pk[], private key in sk[].
/// Key sizes are exact (in bytes):
/// - public (pk): 897
/// - private (sk): 1281
///
/// Return value: 0 on success, -1 on error.
pub fn PQCLEAN_FALCON512_CLEAN_crypto_sign_keypair_from_seed_rpo(
pk: *mut u8,
sk: *mut u8,
seed: *const u8,
) -> c_int;
/// Compute a signature on a provided message (m, mlen), with a given private key (sk).
/// Signature is written in sig[], with length written into *siglen. Signature length is
/// variable; maximum signature length (in bytes) is 666.
///
/// sig[], m[] and sk[] may overlap each other arbitrarily.
///
/// Return value: 0 on success, -1 on error.
pub fn PQCLEAN_FALCON512_CLEAN_crypto_sign_signature_rpo(
sig: *mut u8,
siglen: *mut usize,
m: *const u8,
mlen: usize,
sk: *const u8,
) -> c_int;
// TEST HELPERS
// --------------------------------------------------------------------------------------------
/// Verify a signature (sig, siglen) on a message (m, mlen) with a given public key (pk).
///
/// sig[], m[] and pk[] may overlap each other arbitrarily.
///
/// Return value: 0 on success, -1 on error.
#[cfg(test)]
pub fn PQCLEAN_FALCON512_CLEAN_crypto_sign_verify_rpo(
sig: *const u8,
siglen: usize,
m: *const u8,
mlen: usize,
pk: *const u8,
) -> c_int;
/// Hash-to-Point algorithm.
///
/// This function generates a point in Z_q[x]/(phi) from a given message.
///
/// It takes a finalized rpo128_context as input and it generates the coefficients of the polynomial
/// representing the point. The coefficients are stored in the array 'x'. The number of coefficients
/// is given by 'logn', which must in our case is 512.
#[cfg(test)]
pub fn PQCLEAN_FALCON512_CLEAN_hash_to_point_rpo(
rc: *mut Rpo128Context,
x: *mut u16,
logn: usize,
);
#[cfg(test)]
pub fn rpo128_init(sc: *mut Rpo128Context);
#[cfg(test)]
pub fn rpo128_absorb(
sc: *mut Rpo128Context,
data: *const core::ffi::c_void,
// TODO: When #![feature(c_size_t)] stabilizes, switch this to `core::ffi::size_t` to be
// more accurate. Currently, however, all Rust targets as of this writing are such that
// `core::ffi::size_t` and `usize` are the same size.
len: usize,
);
#[cfg(test)]
pub fn rpo128_finalize(sc: *mut Rpo128Context);
}
#[repr(C)]
#[cfg(test)]
pub struct Rpo128Context {
pub content: [u64; 13usize],
}
// TESTS
// ================================================================================================
#[cfg(all(test, feature = "std"))]
mod tests {
use alloc::vec::Vec;
use rand_utils::{rand_array, rand_value, rand_vector};
use super::*;
use crate::dsa::rpo_falcon512::{NONCE_LEN, PK_LEN, SIG_LEN, SK_LEN};
#[test]
fn falcon_ffi() {
unsafe {
//let mut rng = rand::thread_rng();
// --- generate a key pair from a seed ----------------------------
let mut pk = [0u8; PK_LEN];
let mut sk = [0u8; SK_LEN];
let seed: [u8; NONCE_LEN] = rand_array();
assert_eq!(
0,
PQCLEAN_FALCON512_CLEAN_crypto_sign_keypair_from_seed_rpo(
pk.as_mut_ptr(),
sk.as_mut_ptr(),
seed.as_ptr()
)
);
// --- sign a message and make sure it verifies -------------------
let mlen: usize = rand_value::<u16>() as usize;
let msg: Vec<u8> = rand_vector(mlen);
let mut detached_sig = [0u8; NONCE_LEN + SIG_LEN];
let mut siglen = 0;
assert_eq!(
0,
PQCLEAN_FALCON512_CLEAN_crypto_sign_signature_rpo(
detached_sig.as_mut_ptr(),
&mut siglen as *mut usize,
msg.as_ptr(),
msg.len(),
sk.as_ptr()
)
);
assert_eq!(
0,
PQCLEAN_FALCON512_CLEAN_crypto_sign_verify_rpo(
detached_sig.as_ptr(),
siglen,
msg.as_ptr(),
msg.len(),
pk.as_ptr()
)
);
// --- check verification of different signature ------------------
assert_eq!(
-1,
PQCLEAN_FALCON512_CLEAN_crypto_sign_verify_rpo(
detached_sig.as_ptr(),
siglen,
msg.as_ptr(),
msg.len() - 1,
pk.as_ptr()
)
);
// --- check verification against a different pub key -------------
let mut pk_alt = [0u8; PK_LEN];
let mut sk_alt = [0u8; SK_LEN];
assert_eq!(
0,
PQCLEAN_FALCON512_CLEAN_crypto_sign_keypair_rpo(
pk_alt.as_mut_ptr(),
sk_alt.as_mut_ptr()
)
);
assert_eq!(
-1,
PQCLEAN_FALCON512_CLEAN_crypto_sign_verify_rpo(
detached_sig.as_ptr(),
siglen,
msg.as_ptr(),
msg.len(),
pk_alt.as_ptr()
)
);
}
}
}

View File

@@ -0,0 +1,68 @@
use super::{math::FalconFelt, Nonce, Polynomial, Rpo256, Word, MODULUS, N, ZERO};
use alloc::vec::Vec;
use num::Zero;
// HASH-TO-POINT FUNCTIONS
// ================================================================================================
/// Returns a polynomial in Z_p[x]/(phi) representing the hash of the provided message and
/// nonce using RPO256.
pub fn hash_to_point_rpo256(message: Word, nonce: &Nonce) -> Polynomial<FalconFelt> {
let mut state = [ZERO; Rpo256::STATE_WIDTH];
// absorb the nonce into the state
let nonce_elements = nonce.to_elements();
for (&n, s) in nonce_elements.iter().zip(state[Rpo256::RATE_RANGE].iter_mut()) {
*s = n;
}
Rpo256::apply_permutation(&mut state);
// absorb message into the state
for (&m, s) in message.iter().zip(state[Rpo256::RATE_RANGE].iter_mut()) {
*s = m;
}
// squeeze the coefficients of the polynomial
let mut i = 0;
let mut res = [FalconFelt::zero(); N];
for _ in 0..64 {
Rpo256::apply_permutation(&mut state);
for a in &state[Rpo256::RATE_RANGE] {
res[i] = FalconFelt::new((a.as_int() % MODULUS as u64) as i16);
i += 1;
}
}
Polynomial::new(res.to_vec())
}
/// Returns a polynomial in Z_p[x]/(phi) representing the hash of the provided message and
/// nonce using SHAKE256. This is the hash-to-point algorithm used in the reference implementation.
#[allow(dead_code)]
pub fn hash_to_point_shake256(message: &[u8], nonce: &Nonce) -> Polynomial<FalconFelt> {
use sha3::{
digest::{ExtendableOutput, Update, XofReader},
Shake256,
};
let mut data = vec![];
data.extend_from_slice(nonce.as_bytes());
data.extend_from_slice(message);
const K: u32 = (1u32 << 16) / MODULUS as u32;
let mut hasher = Shake256::default();
hasher.update(&data);
let mut reader = hasher.finalize_xof();
let mut coefficients: Vec<FalconFelt> = Vec::with_capacity(N);
while coefficients.len() != N {
let mut randomness = [0u8; 2];
reader.read(&mut randomness);
let t = ((randomness[0] as u32) << 8) | (randomness[1] as u32);
if t < K * MODULUS as u32 {
coefficients.push(FalconFelt::new((t % MODULUS as u32) as i16));
}
}
Polynomial { coefficients }
}

View File

@@ -1,235 +0,0 @@
use super::{
ByteReader, ByteWriter, Deserializable, DeserializationError, FalconError, Polynomial,
PublicKeyBytes, Rpo256, SecretKeyBytes, Serializable, Signature, Word,
};
#[cfg(feature = "std")]
use {
super::{ffi, NonceBytes, NONCE_LEN, PK_LEN, SIG_LEN, SK_LEN},
alloc::vec::Vec,
};
// PUBLIC KEY
// ================================================================================================
/// A public key for verifying signatures.
///
/// The public key is a [Word] (i.e., 4 field elements) that is the hash of the coefficients of
/// the polynomial representing the raw bytes of the expanded public key.
///
/// For Falcon-512, the first byte of the expanded public key is always equal to log2(512) i.e., 9.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct PublicKey(Word);
impl PublicKey {
/// Returns a new [PublicKey] which is a commitment to the provided expanded public key.
///
/// # Errors
/// Returns an error if the decoding of the public key fails.
pub fn new(pk: PublicKeyBytes) -> Result<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,
pk_polynomial: Default::default(),
sig_polynomial: Default::default(),
})
} 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 rand_utils::{rand_array, rand_vector};
use super::{super::Felt, KeyPair, NonceBytes, Word};
#[test]
fn test_falcon_verification() {
// generate random keys
let keys = KeyPair::new().unwrap();
let pk = keys.public_key();
// sign a random message
let message: Word = rand_vector::<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()))
}
}

View File

@@ -0,0 +1,53 @@
use super::{
math::{FalconFelt, Polynomial},
ByteReader, ByteWriter, Deserializable, DeserializationError, Felt, Serializable, Signature,
Word,
};
mod public_key;
pub use public_key::{PubKeyPoly, PublicKey};
mod secret_key;
pub use secret_key::SecretKey;
// TESTS
// ================================================================================================
#[cfg(test)]
mod tests {
use crate::{dsa::rpo_falcon512::SecretKey, Word, ONE};
use rand::SeedableRng;
use rand_chacha::ChaCha20Rng;
use winter_math::FieldElement;
use winter_utils::{Deserializable, Serializable};
#[test]
fn test_falcon_verification() {
let seed = [0_u8; 32];
let mut rng = ChaCha20Rng::from_seed(seed);
// generate random keys
let sk = SecretKey::with_rng(&mut rng);
let pk = sk.public_key();
// test secret key serialization/deserialization
let mut buffer = vec![];
sk.write_into(&mut buffer);
let sk = SecretKey::read_from_bytes(&buffer).unwrap();
// sign a random message
let message: Word = [ONE; 4];
let signature = sk.sign_with_rng(message, &mut rng);
// make sure the signature verifies correctly
assert!(pk.verify(message, &signature));
// a signature should not verify against a wrong message
let message2: Word = [ONE.double(); 4];
assert!(!pk.verify(message2, &signature));
// a signature should not verify against a wrong public key
let sk2 = SecretKey::with_rng(&mut rng);
assert!(!sk2.public_key().verify(message, &signature))
}
}

View File

@@ -0,0 +1,138 @@
use crate::dsa::rpo_falcon512::FALCON_ENCODING_BITS;
use super::{
super::{Rpo256, LOG_N, N, PK_LEN},
ByteReader, ByteWriter, Deserializable, DeserializationError, FalconFelt, Felt, Polynomial,
Serializable, Signature, Word,
};
use alloc::string::ToString;
use core::ops::Deref;
use num::Zero;
// PUBLIC KEY
// ================================================================================================
/// A public key for verifying signatures.
///
/// The public key is a [Word] (i.e., 4 field elements) that is the hash of the coefficients of
/// the polynomial representing the raw bytes of the expanded public key. The hash is computed
/// using Rpo256.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PublicKey(Word);
impl PublicKey {
/// Returns a new [PublicKey] which is a commitment to the provided expanded public key.
pub fn new(pub_key: Word) -> Self {
Self(pub_key)
}
/// Verifies the provided signature against provided message and this public key.
pub fn verify(&self, message: Word, signature: &Signature) -> bool {
signature.verify(message, self.0)
}
}
impl From<PubKeyPoly> for PublicKey {
fn from(pk_poly: PubKeyPoly) -> Self {
let pk_felts: Polynomial<Felt> = pk_poly.0.into();
let pk_digest = Rpo256::hash_elements(&pk_felts.coefficients).into();
Self(pk_digest)
}
}
impl From<PublicKey> for Word {
fn from(key: PublicKey) -> Self {
key.0
}
}
// PUBLIC KEY POLYNOMIAL
// ================================================================================================
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PubKeyPoly(pub Polynomial<FalconFelt>);
impl Deref for PubKeyPoly {
type Target = Polynomial<FalconFelt>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<Polynomial<FalconFelt>> for PubKeyPoly {
fn from(pk_poly: Polynomial<FalconFelt>) -> Self {
Self(pk_poly)
}
}
impl Serializable for &PubKeyPoly {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
let mut buf = [0_u8; PK_LEN];
buf[0] = LOG_N;
let mut acc = 0_u32;
let mut acc_len: u32 = 0;
let mut input_pos = 1;
for c in self.0.coefficients.iter() {
let c = c.value();
acc = (acc << FALCON_ENCODING_BITS) | c as u32;
acc_len += FALCON_ENCODING_BITS;
while acc_len >= 8 {
acc_len -= 8;
buf[input_pos] = (acc >> acc_len) as u8;
input_pos += 1;
}
}
if acc_len > 0 {
buf[input_pos] = (acc >> (8 - acc_len)) as u8;
}
target.write(buf);
}
}
impl Deserializable for PubKeyPoly {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let buf = source.read_array::<PK_LEN>()?;
if buf[0] != LOG_N {
return Err(DeserializationError::InvalidValue(format!(
"Failed to decode public key: expected the first byte to be {LOG_N} but was {}",
buf[0]
)));
}
let mut acc = 0_u32;
let mut acc_len = 0;
let mut output = [FalconFelt::zero(); N];
let mut output_idx = 0;
for &byte in buf.iter().skip(1) {
acc = (acc << 8) | (byte as u32);
acc_len += 8;
if acc_len >= FALCON_ENCODING_BITS {
acc_len -= FALCON_ENCODING_BITS;
let w = (acc >> acc_len) & 0x3FFF;
let element = w.try_into().map_err(|err| {
DeserializationError::InvalidValue(format!(
"Failed to decode public key: {err}"
))
})?;
output[output_idx] = element;
output_idx += 1;
}
}
if (acc & ((1u32 << acc_len) - 1)) == 0 {
Ok(Polynomial::new(output.to_vec()).into())
} else {
Err(DeserializationError::InvalidValue(
"Failed to decode public key: input not fully consumed".to_string(),
))
}
}
}

View File

@@ -0,0 +1,386 @@
use super::{
super::{
math::{ffldl, ffsampling, gram, normalize_tree, FalconFelt, FastFft, LdlTree, Polynomial},
signature::SignaturePoly,
ByteReader, ByteWriter, Deserializable, DeserializationError, Nonce, Serializable,
ShortLatticeBasis, Signature, Word, MODULUS, N, SIGMA, SIG_L2_BOUND,
},
PubKeyPoly, PublicKey,
};
use crate::dsa::rpo_falcon512::{
hash_to_point::hash_to_point_rpo256, math::ntru_gen, SIG_NONCE_LEN, SK_LEN,
};
use alloc::{string::ToString, vec::Vec};
use num::Complex;
use num_complex::Complex64;
use rand::Rng;
#[cfg(not(feature = "std"))]
use num::Float;
// CONSTANTS
// ================================================================================================
const WIDTH_BIG_POLY_COEFFICIENT: usize = 8;
const WIDTH_SMALL_POLY_COEFFICIENT: usize = 6;
// SECRET KEY
// ================================================================================================
/// The secret key is a quadruple [[g, -f], [G, -F]] of polynomials with integer coefficients. Each
/// polynomial is of degree at most N = 512 and computations with these polynomials is done modulo
/// the monic irreducible polynomial ϕ = x^N + 1. The secret key is a basis for a lattice and has
/// the property of being short with respect to a certain norm and an upper bound appropriate for
/// a given security parameter. The public key on the other hand is another basis for the same
/// lattice and can be described by a single polynomial h with integer coefficients modulo ϕ.
/// The two keys are related by the following relation:
///
/// 1. h = g /f [mod ϕ][mod p]
/// 2. f.G - g.F = p [mod ϕ]
///
/// where p = 12289 is the Falcon prime. Equation 2 is called the NTRU equation.
/// The secret key is generated by first sampling a random pair (f, g) of polynomials using
/// an appropriate distribution that yields short but not too short polynomials with integer
/// coefficients modulo ϕ. The NTRU equation is then used to find a matching pair (F, G).
/// The public key is then derived from the secret key using equation 1.
///
/// To allow for fast signature generation, the secret key is pre-processed into a more suitable
/// form, called the LDL tree, and this allows for fast sampling of short vectors in the lattice
/// using Fast Fourier sampling during signature generation (ffSampling algorithm 11 in [1]).
///
/// [1]: https://falcon-sign.info/falcon.pdf
#[derive(Debug, Clone)]
pub struct SecretKey {
secret_key: ShortLatticeBasis,
tree: LdlTree,
}
#[allow(clippy::new_without_default)]
impl SecretKey {
// CONSTRUCTORS
// --------------------------------------------------------------------------------------------
/// Generates a secret key from OS-provided randomness.
#[cfg(feature = "std")]
pub fn new() -> Self {
use rand::{rngs::StdRng, SeedableRng};
let mut rng = StdRng::from_entropy();
Self::with_rng(&mut rng)
}
/// Generates a secret_key using the provided random number generator `Rng`.
pub fn with_rng<R: Rng>(rng: &mut R) -> Self {
let basis = ntru_gen(N, rng);
Self::from_short_lattice_basis(basis)
}
/// Given a short basis [[g, -f], [G, -F]], computes the normalized LDL tree i.e., Falcon tree.
fn from_short_lattice_basis(basis: ShortLatticeBasis) -> SecretKey {
// FFT each polynomial of the short basis.
let basis_fft = to_complex_fft(&basis);
// compute the Gram matrix.
let gram_fft = gram(basis_fft);
// construct the LDL tree of the Gram matrix.
let mut tree = ffldl(gram_fft);
// normalize the leaves of the LDL tree.
normalize_tree(&mut tree, SIGMA);
Self { secret_key: basis, tree }
}
// PUBLIC ACCESSORS
// --------------------------------------------------------------------------------------------
/// Returns the polynomials of the short lattice basis of this secret key.
pub fn short_lattice_basis(&self) -> &ShortLatticeBasis {
&self.secret_key
}
/// Returns the public key corresponding to this secret key.
pub fn public_key(&self) -> PublicKey {
self.compute_pub_key_poly().into()
}
/// Returns the LDL tree associated to this secret key.
pub fn tree(&self) -> &LdlTree {
&self.tree
}
// SIGNATURE GENERATION
// --------------------------------------------------------------------------------------------
/// Signs a message with this secret key.
#[cfg(feature = "std")]
pub fn sign(&self, message: Word) -> Signature {
use rand::{rngs::StdRng, SeedableRng};
let mut rng = StdRng::from_entropy();
self.sign_with_rng(message, &mut rng)
}
/// Signs a message with the secret key relying on the provided randomness generator.
pub fn sign_with_rng<R: Rng>(&self, message: Word, rng: &mut R) -> Signature {
let mut nonce_bytes = [0u8; SIG_NONCE_LEN];
rng.fill_bytes(&mut nonce_bytes);
let nonce = Nonce::new(nonce_bytes);
let h = self.compute_pub_key_poly();
let c = hash_to_point_rpo256(message, &nonce);
let s2 = self.sign_helper(c, rng);
Signature::new(nonce, h, s2)
}
// HELPER METHODS
// --------------------------------------------------------------------------------------------
/// Derives the public key corresponding to this secret key using h = g /f [mod ϕ][mod p].
pub fn compute_pub_key_poly(&self) -> PubKeyPoly {
let g: Polynomial<FalconFelt> = self.secret_key[0].clone().into();
let g_fft = g.fft();
let minus_f: Polynomial<FalconFelt> = self.secret_key[1].clone().into();
let f = -minus_f;
let f_fft = f.fft();
let h_fft = g_fft.hadamard_div(&f_fft);
h_fft.ifft().into()
}
/// Signs a message polynomial with the secret key.
///
/// Takes a randomness generator implementing `Rng` and message polynomial representing `c`
/// the hash-to-point of the message to be signed. It outputs a signature polynomial `s2`.
fn sign_helper<R: Rng>(&self, c: Polynomial<FalconFelt>, rng: &mut R) -> SignaturePoly {
let one_over_q = 1.0 / (MODULUS as f64);
let c_over_q_fft = c.map(|cc| Complex::new(one_over_q * cc.value() as f64, 0.0)).fft();
// B = [[FFT(g), -FFT(f)], [FFT(G), -FFT(F)]]
let [g_fft, minus_f_fft, big_g_fft, minus_big_f_fft] = to_complex_fft(&self.secret_key);
let t0 = c_over_q_fft.hadamard_mul(&minus_big_f_fft);
let t1 = -c_over_q_fft.hadamard_mul(&minus_f_fft);
loop {
let bold_s = loop {
let z = ffsampling(&(t0.clone(), t1.clone()), &self.tree, rng);
let t0_min_z0 = t0.clone() - z.0;
let t1_min_z1 = t1.clone() - z.1;
// s = (t-z) * B
let s0 = t0_min_z0.hadamard_mul(&g_fft) + t1_min_z1.hadamard_mul(&big_g_fft);
let s1 =
t0_min_z0.hadamard_mul(&minus_f_fft) + t1_min_z1.hadamard_mul(&minus_big_f_fft);
// compute the norm of (s0||s1) and note that they are in FFT representation
let length_squared: f64 =
(s0.coefficients.iter().map(|a| (a * a.conj()).re).sum::<f64>()
+ s1.coefficients.iter().map(|a| (a * a.conj()).re).sum::<f64>())
/ (N as f64);
if length_squared > (SIG_L2_BOUND as f64) {
continue;
}
break [-s0, s1];
};
let s2 = bold_s[1].ifft();
let s2_coef: [i16; N] = s2
.coefficients
.iter()
.map(|a| a.re.round() as i16)
.collect::<Vec<i16>>()
.try_into()
.expect("The number of coefficients should be equal to N");
if let Ok(s2) = SignaturePoly::try_from(&s2_coef) {
return s2;
}
}
}
}
// SERIALIZATION / DESERIALIZATION
// ================================================================================================
impl Serializable for SecretKey {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
let basis = &self.secret_key;
// header
let n = basis[0].coefficients.len();
let l = n.checked_ilog2().unwrap() as u8;
let header: u8 = (5 << 4) | l;
let f = &basis[1];
let g = &basis[0];
let capital_f = &basis[3];
let mut buffer = Vec::with_capacity(1281);
buffer.push(header);
let f_i8: Vec<i8> = f.coefficients.iter().map(|&a| -a as i8).collect();
let f_i8_encoded = encode_i8(&f_i8, WIDTH_SMALL_POLY_COEFFICIENT).unwrap();
buffer.extend_from_slice(&f_i8_encoded);
let g_i8: Vec<i8> = g.coefficients.iter().map(|&a| a as i8).collect();
let g_i8_encoded = encode_i8(&g_i8, WIDTH_SMALL_POLY_COEFFICIENT).unwrap();
buffer.extend_from_slice(&g_i8_encoded);
let big_f_i8: Vec<i8> = capital_f.coefficients.iter().map(|&a| -a as i8).collect();
let big_f_i8_encoded = encode_i8(&big_f_i8, WIDTH_BIG_POLY_COEFFICIENT).unwrap();
buffer.extend_from_slice(&big_f_i8_encoded);
target.write_bytes(&buffer);
}
}
impl Deserializable for SecretKey {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let byte_vector: [u8; SK_LEN] = source.read_array()?;
// check length
if byte_vector.len() < 2 {
return Err(DeserializationError::InvalidValue("Invalid encoding length: Failed to decode as length is different from the one expected".to_string()));
}
// read fields
let header = byte_vector[0];
// check fixed bits in header
if (header >> 4) != 5 {
return Err(DeserializationError::InvalidValue("Invalid header format".to_string()));
}
// check log n
let logn = (header & 15) as usize;
let n = 1 << logn;
// match against const variant generic parameter
if n != N {
return Err(DeserializationError::InvalidValue(
"Unsupported Falcon DSA variant".to_string(),
));
}
if byte_vector.len() != SK_LEN {
return Err(DeserializationError::InvalidValue("Invalid encoding length: Failed to decode as length is different from the one expected".to_string()));
}
let chunk_size_f = ((n * WIDTH_SMALL_POLY_COEFFICIENT) + 7) >> 3;
let chunk_size_g = ((n * WIDTH_SMALL_POLY_COEFFICIENT) + 7) >> 3;
let chunk_size_big_f = ((n * WIDTH_BIG_POLY_COEFFICIENT) + 7) >> 3;
let f = decode_i8(&byte_vector[1..chunk_size_f + 1], WIDTH_SMALL_POLY_COEFFICIENT).unwrap();
let g = decode_i8(
&byte_vector[chunk_size_f + 1..(chunk_size_f + chunk_size_g + 1)],
WIDTH_SMALL_POLY_COEFFICIENT,
)
.unwrap();
let big_f = decode_i8(
&byte_vector[(chunk_size_f + chunk_size_g + 1)
..(chunk_size_f + chunk_size_g + chunk_size_big_f + 1)],
WIDTH_BIG_POLY_COEFFICIENT,
)
.unwrap();
let f = Polynomial::new(f.iter().map(|&c| FalconFelt::new(c.into())).collect());
let g = Polynomial::new(g.iter().map(|&c| FalconFelt::new(c.into())).collect());
let big_f = Polynomial::new(big_f.iter().map(|&c| FalconFelt::new(c.into())).collect());
// big_g * f - g * big_f = p (mod X^n + 1)
let big_g = g.fft().hadamard_div(&f.fft()).hadamard_mul(&big_f.fft()).ifft();
let basis = [
g.map(|f| f.balanced_value()),
-f.map(|f| f.balanced_value()),
big_g.map(|f| f.balanced_value()),
-big_f.map(|f| f.balanced_value()),
];
Ok(Self::from_short_lattice_basis(basis))
}
}
// HELPER FUNCTIONS
// ================================================================================================
/// Computes the complex FFT of the secret key polynomials.
fn to_complex_fft(basis: &[Polynomial<i16>; 4]) -> [Polynomial<Complex<f64>>; 4] {
let [g, f, big_g, big_f] = basis.clone();
let g_fft = g.map(|cc| Complex64::new(*cc as f64, 0.0)).fft();
let minus_f_fft = f.map(|cc| -Complex64::new(*cc as f64, 0.0)).fft();
let big_g_fft = big_g.map(|cc| Complex64::new(*cc as f64, 0.0)).fft();
let minus_big_f_fft = big_f.map(|cc| -Complex64::new(*cc as f64, 0.0)).fft();
[g_fft, minus_f_fft, big_g_fft, minus_big_f_fft]
}
/// Encodes a sequence of signed integers such that each integer x satisfies |x| < 2^(bits-1)
/// for a given parameter bits. bits can take either the value 6 or 8.
pub fn encode_i8(x: &[i8], bits: usize) -> Option<Vec<u8>> {
let maxv = (1 << (bits - 1)) - 1_usize;
let maxv = maxv as i8;
let minv = -maxv;
for &c in x {
if c > maxv || c < minv {
return None;
}
}
let out_len = ((N * bits) + 7) >> 3;
let mut buf = vec![0_u8; out_len];
let mut acc = 0_u32;
let mut acc_len = 0;
let mask = ((1_u16 << bits) - 1) as u8;
let mut input_pos = 0;
for &c in x {
acc = (acc << bits) | (c as u8 & mask) as u32;
acc_len += bits;
while acc_len >= 8 {
acc_len -= 8;
buf[input_pos] = (acc >> acc_len) as u8;
input_pos += 1;
}
}
if acc_len > 0 {
buf[input_pos] = (acc >> (8 - acc_len)) as u8;
}
Some(buf)
}
/// Decodes a sequence of bytes into a sequence of signed integers such that each integer x
/// satisfies |x| < 2^(bits-1) for a given parameter bits. bits can take either the value 6 or 8.
pub fn decode_i8(buf: &[u8], bits: usize) -> Option<Vec<i8>> {
let mut x = [0_i8; N];
let mut i = 0;
let mut j = 0;
let mut acc = 0_u32;
let mut acc_len = 0;
let mask = (1_u32 << bits) - 1;
let a = (1 << bits) as u8;
let b = ((1 << (bits - 1)) - 1) as u8;
while i < N {
acc = (acc << 8) | (buf[j] as u32);
j += 1;
acc_len += 8;
while acc_len >= bits && i < N {
acc_len -= bits;
let w = (acc >> acc_len) & mask;
let w = w as u8;
let z = if w > b { w as i8 - a as i8 } else { w as i8 };
x[i] = z;
i += 1;
}
}
if (acc & ((1u32 << acc_len) - 1)) == 0 {
Some(x.to_vec())
} else {
None
}
}

View File

@@ -0,0 +1,123 @@
use super::{fft::FastFft, polynomial::Polynomial, samplerz::sampler_z};
use alloc::boxed::Box;
use num::{One, Zero};
use num_complex::{Complex, Complex64};
use rand::Rng;
#[cfg(not(feature = "std"))]
use num::Float;
const SIGMIN: f64 = 1.2778336969128337;
/// Computes the Gram matrix. The argument must be a 2x2 matrix
/// whose elements are equal-length vectors of complex numbers,
/// representing polynomials in FFT domain.
pub fn gram(b: [Polynomial<Complex64>; 4]) -> [Polynomial<Complex64>; 4] {
const N: usize = 2;
let mut g: [Polynomial<Complex<f64>>; 4] =
[Polynomial::zero(), Polynomial::zero(), Polynomial::zero(), Polynomial::zero()];
for i in 0..N {
for j in 0..N {
for k in 0..N {
g[N * i + j] = g[N * i + j].clone()
+ b[N * i + k].hadamard_mul(&b[N * j + k].map(|c| c.conj()));
}
}
}
g
}
/// Computes the LDL decomposition of a 2x2 matrix G such that
/// L D L* = G
/// where D is diagonal, and L is lower-triangular. The elements of the matrices are in FFT domain.
pub fn ldl(
g: [Polynomial<Complex64>; 4],
) -> ([Polynomial<Complex64>; 4], [Polynomial<Complex64>; 4]) {
let zero = Polynomial::<Complex64>::one();
let one = Polynomial::<Complex64>::zero();
let l10 = g[2].hadamard_div(&g[0]);
let bc = l10.map(|c| c * c.conj());
let abc = g[0].hadamard_mul(&bc);
let d11 = g[3].clone() - abc;
let l = [one.clone(), zero.clone(), l10.clone(), one];
let d = [g[0].clone(), zero.clone(), zero, d11];
(l, d)
}
#[derive(Debug, Clone)]
pub enum LdlTree {
Branch(Polynomial<Complex64>, Box<LdlTree>, Box<LdlTree>),
Leaf([Complex64; 2]),
}
/// Computes the LDL Tree of G. Corresponds to Algorithm 9 of the specification [1, p.37].
/// The argument is a 2x2 matrix of polynomials, given in FFT form.
/// [1]: https://falcon-sign.info/falcon.pdf
pub fn ffldl(gram_matrix: [Polynomial<Complex64>; 4]) -> LdlTree {
let n = gram_matrix[0].coefficients.len();
let (l, d) = ldl(gram_matrix);
if n > 2 {
let (d00, d01) = d[0].split_fft();
let (d10, d11) = d[3].split_fft();
let g0 = [d00.clone(), d01.clone(), d01.map(|c| c.conj()), d00];
let g1 = [d10.clone(), d11.clone(), d11.map(|c| c.conj()), d10];
LdlTree::Branch(l[2].clone(), Box::new(ffldl(g0)), Box::new(ffldl(g1)))
} else {
LdlTree::Branch(
l[2].clone(),
Box::new(LdlTree::Leaf(d[0].clone().coefficients.try_into().unwrap())),
Box::new(LdlTree::Leaf(d[3].clone().coefficients.try_into().unwrap())),
)
}
}
/// Normalizes the leaves of an LDL tree using a given normalization value `sigma`.
pub fn normalize_tree(tree: &mut LdlTree, sigma: f64) {
match tree {
LdlTree::Branch(_ell, left, right) => {
normalize_tree(left, sigma);
normalize_tree(right, sigma);
}
LdlTree::Leaf(vector) => {
vector[0] = Complex::new(sigma / vector[0].re.sqrt(), 0.0);
vector[1] = Complex64::zero();
}
}
}
/// Samples short polynomials using a Falcon tree. Algorithm 11 from the spec [1, p.40].
///
/// [1]: https://falcon-sign.info/falcon.pdf
pub fn ffsampling<R: Rng>(
t: &(Polynomial<Complex64>, Polynomial<Complex64>),
tree: &LdlTree,
mut rng: &mut R,
) -> (Polynomial<Complex64>, Polynomial<Complex64>) {
match tree {
LdlTree::Branch(ell, left, right) => {
let bold_t1 = t.1.split_fft();
let bold_z1 = ffsampling(&bold_t1, right, rng);
let z1 = Polynomial::<Complex64>::merge_fft(&bold_z1.0, &bold_z1.1);
// t0' = t0 + (t1 - z1) * l
let t0_prime = t.0.clone() + (t.1.clone() - z1.clone()).hadamard_mul(ell);
let bold_t0 = t0_prime.split_fft();
let bold_z0 = ffsampling(&bold_t0, left, rng);
let z0 = Polynomial::<Complex64>::merge_fft(&bold_z0.0, &bold_z0.1);
(z0, z1)
}
LdlTree::Leaf(value) => {
let z0 = sampler_z(t.0.coefficients[0].re, value[0].re, SIGMIN, &mut rng);
let z1 = sampler_z(t.1.coefficients[0].re, value[0].re, SIGMIN, &mut rng);
(
Polynomial::new(vec![Complex64::new(z0 as f64, 0.0)]),
Polynomial::new(vec![Complex64::new(z1 as f64, 0.0)]),
)
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,172 @@
use super::{fft::CyclotomicFourier, Inverse, MODULUS};
use alloc::string::String;
use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use num::{One, Zero};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct FalconFelt(u32);
impl FalconFelt {
pub const fn new(value: i16) -> Self {
let gtz_bool = value >= 0;
let gtz_int = gtz_bool as i16;
let gtz_sign = gtz_int - ((!gtz_bool) as i16);
let reduced = gtz_sign * (gtz_sign * value) % MODULUS;
let canonical_representative = (reduced + MODULUS * (1 - gtz_int)) as u32;
FalconFelt(canonical_representative)
}
pub const fn value(&self) -> i16 {
self.0 as i16
}
pub fn balanced_value(&self) -> i16 {
let value = self.value();
let g = (value > ((MODULUS) / 2)) as i16;
value - (MODULUS) * g
}
pub const fn multiply(&self, other: Self) -> Self {
FalconFelt((self.0 * other.0) % MODULUS as u32)
}
}
impl Add for FalconFelt {
type Output = Self;
#[allow(clippy::suspicious_arithmetic_impl)]
fn add(self, rhs: Self) -> Self::Output {
let (s, _) = self.0.overflowing_add(rhs.0);
let (d, n) = s.overflowing_sub(MODULUS as u32);
let (r, _) = d.overflowing_add(MODULUS as u32 * (n as u32));
FalconFelt(r)
}
}
impl AddAssign for FalconFelt {
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs;
}
}
impl Sub for FalconFelt {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
self + -rhs
}
}
impl SubAssign for FalconFelt {
fn sub_assign(&mut self, rhs: Self) {
*self = *self - rhs;
}
}
impl Neg for FalconFelt {
type Output = FalconFelt;
fn neg(self) -> Self::Output {
let is_nonzero = self.0 != 0;
let r = MODULUS as u32 - self.0;
FalconFelt(r * (is_nonzero as u32))
}
}
impl Mul for FalconFelt {
fn mul(self, rhs: Self) -> Self::Output {
FalconFelt((self.0 * rhs.0) % MODULUS as u32)
}
type Output = Self;
}
impl MulAssign for FalconFelt {
fn mul_assign(&mut self, rhs: Self) {
*self = *self * rhs;
}
}
impl Div for FalconFelt {
type Output = FalconFelt;
#[allow(clippy::suspicious_arithmetic_impl)]
fn div(self, rhs: Self) -> Self::Output {
self * rhs.inverse_or_zero()
}
}
impl DivAssign for FalconFelt {
fn div_assign(&mut self, rhs: Self) {
*self = *self / rhs
}
}
impl Zero for FalconFelt {
fn zero() -> Self {
FalconFelt::new(0)
}
fn is_zero(&self) -> bool {
self.0 == 0
}
}
impl One for FalconFelt {
fn one() -> Self {
FalconFelt::new(1)
}
}
impl Inverse for FalconFelt {
fn inverse_or_zero(self) -> Self {
// q-2 = 0b10 11 11 11 11 11 11
let two = self.multiply(self);
let three = two.multiply(self);
let six = three.multiply(three);
let twelve = six.multiply(six);
let fifteen = twelve.multiply(three);
let thirty = fifteen.multiply(fifteen);
let sixty = thirty.multiply(thirty);
let sixty_three = sixty.multiply(three);
let sixty_three_sq = sixty_three.multiply(sixty_three);
let sixty_three_qu = sixty_three_sq.multiply(sixty_three_sq);
let sixty_three_oc = sixty_three_qu.multiply(sixty_three_qu);
let sixty_three_hx = sixty_three_oc.multiply(sixty_three_oc);
let sixty_three_tt = sixty_three_hx.multiply(sixty_three_hx);
let sixty_three_sf = sixty_three_tt.multiply(sixty_three_tt);
let all_ones = sixty_three_sf.multiply(sixty_three);
let two_e_twelve = all_ones.multiply(self);
let two_e_thirteen = two_e_twelve.multiply(two_e_twelve);
two_e_thirteen.multiply(all_ones)
}
}
impl CyclotomicFourier for FalconFelt {
fn primitive_root_of_unity(n: usize) -> Self {
let log2n = n.ilog2();
assert!(log2n <= 12);
// and 1331 is a twelfth root of unity
let mut a = FalconFelt::new(1331);
let num_squarings = 12 - n.ilog2();
for _ in 0..num_squarings {
a *= a;
}
a
}
}
impl TryFrom<u32> for FalconFelt {
type Error = String;
fn try_from(value: u32) -> Result<Self, Self::Error> {
if value >= MODULUS as u32 {
Err(format!("value {value} is greater than or equal to the field modulus {MODULUS}"))
} else {
Ok(FalconFelt::new(value as i16))
}
}
}

View File

@@ -0,0 +1,320 @@
//! Contains different structs and methods related to the Falcon DSA.
//!
//! It uses and acknowledges the work in:
//!
//! 1. The [reference](https://falcon-sign.info/impl/README.txt.html) implementation by Thomas Pornin.
//! 2. The [Rust](https://github.com/aszepieniec/falcon-rust) implementation by Alan Szepieniec.
use super::MODULUS;
use alloc::{string::String, vec::Vec};
use core::ops::MulAssign;
use num::{BigInt, FromPrimitive, One, Zero};
use num_complex::Complex64;
use rand::Rng;
#[cfg(not(feature = "std"))]
use num::Float;
mod fft;
pub use fft::{CyclotomicFourier, FastFft};
mod field;
pub use field::FalconFelt;
mod ffsampling;
pub use ffsampling::{ffldl, ffsampling, gram, normalize_tree, LdlTree};
mod samplerz;
use self::samplerz::sampler_z;
mod polynomial;
pub use polynomial::Polynomial;
pub trait Inverse: Copy + Zero + MulAssign + One {
/// Gets the inverse of a, or zero if it is zero.
fn inverse_or_zero(self) -> Self;
/// Gets the inverses of a batch of elements, and skip over any that are zero.
fn batch_inverse_or_zero(batch: &[Self]) -> Vec<Self> {
let mut acc = Self::one();
let mut rp: Vec<Self> = Vec::with_capacity(batch.len());
for batch_item in batch {
if !batch_item.is_zero() {
rp.push(acc);
acc = *batch_item * acc;
} else {
rp.push(Self::zero());
}
}
let mut inv = Self::inverse_or_zero(acc);
for i in (0..batch.len()).rev() {
if !batch[i].is_zero() {
rp[i] *= inv;
inv *= batch[i];
}
}
rp
}
}
impl Inverse for Complex64 {
fn inverse_or_zero(self) -> Self {
let modulus = self.re * self.re + self.im * self.im;
Complex64::new(self.re / modulus, -self.im / modulus)
}
fn batch_inverse_or_zero(batch: &[Self]) -> Vec<Self> {
batch.iter().map(|&c| Complex64::new(1.0, 0.0) / c).collect()
}
}
impl Inverse for f64 {
fn inverse_or_zero(self) -> Self {
1.0 / self
}
fn batch_inverse_or_zero(batch: &[Self]) -> Vec<Self> {
batch.iter().map(|&c| 1.0 / c).collect()
}
}
/// Samples 4 small polynomials f, g, F, G such that f * G - g * F = q mod (X^n + 1).
/// Algorithm 5 (NTRUgen) of the documentation [1, p.34].
///
/// [1]: https://falcon-sign.info/falcon.pdf
pub(crate) fn ntru_gen<R: Rng>(n: usize, rng: &mut R) -> [Polynomial<i16>; 4] {
loop {
let f = gen_poly(n, rng);
let g = gen_poly(n, rng);
let f_ntt = f.map(|&i| FalconFelt::new(i)).fft();
if f_ntt.coefficients.iter().any(|e| e.is_zero()) {
continue;
}
let gamma = gram_schmidt_norm_squared(&f, &g);
if gamma > 1.3689f64 * (MODULUS as f64) {
continue;
}
if let Some((capital_f, capital_g)) =
ntru_solve(&f.map(|&i| i.into()), &g.map(|&i| i.into()))
{
return [
f,
g,
capital_f.map(|i| i.try_into().unwrap()),
capital_g.map(|i| i.try_into().unwrap()),
];
}
}
}
/// Solves the NTRU equation. Given f, g in ZZ[X], find F, G in ZZ[X] such that:
///
/// f G - g F = q mod (X^n + 1)
///
/// Algorithm 6 of the specification [1, p.35].
///
/// [1]: https://falcon-sign.info/falcon.pdf
fn ntru_solve(
f: &Polynomial<BigInt>,
g: &Polynomial<BigInt>,
) -> Option<(Polynomial<BigInt>, Polynomial<BigInt>)> {
let n = f.coefficients.len();
if n == 1 {
let (gcd, u, v) = xgcd(&f.coefficients[0], &g.coefficients[0]);
if gcd != BigInt::one() {
return None;
}
return Some((
(Polynomial::new(vec![-v * BigInt::from_u32(MODULUS as u32).unwrap()])),
Polynomial::new(vec![u * BigInt::from_u32(MODULUS as u32).unwrap()]),
));
}
let f_prime = f.field_norm();
let g_prime = g.field_norm();
let (capital_f_prime, capital_g_prime) = ntru_solve(&f_prime, &g_prime)?;
let capital_f_prime_xsq = capital_f_prime.lift_next_cyclotomic();
let capital_g_prime_xsq = capital_g_prime.lift_next_cyclotomic();
let f_minx = f.galois_adjoint();
let g_minx = g.galois_adjoint();
let mut capital_f = (capital_f_prime_xsq.karatsuba(&g_minx)).reduce_by_cyclotomic(n);
let mut capital_g = (capital_g_prime_xsq.karatsuba(&f_minx)).reduce_by_cyclotomic(n);
match babai_reduce(f, g, &mut capital_f, &mut capital_g) {
Ok(_) => Some((capital_f, capital_g)),
Err(_e) => {
#[cfg(test)]
{
panic!("{}", _e);
}
#[cfg(not(test))]
{
None
}
}
}
}
/// Generates a polynomial of degree at most n-1 whose coefficients are distributed according
/// to a discrete Gaussian with mu = 0 and sigma = 1.17 * sqrt(Q / (2n)).
fn gen_poly<R: Rng>(n: usize, rng: &mut R) -> Polynomial<i16> {
let mu = 0.0;
let sigma_star = 1.43300980528773;
Polynomial {
coefficients: (0..4096)
.map(|_| sampler_z(mu, sigma_star, sigma_star - 0.001, rng))
.collect::<Vec<i16>>()
.chunks(4096 / n)
.map(|ch| ch.iter().sum())
.collect(),
}
}
/// Computes the Gram-Schmidt norm of B = [[g, -f], [G, -F]] from f and g.
/// Corresponds to line 9 in algorithm 5 of the spec [1, p.34]
///
/// [1]: https://falcon-sign.info/falcon.pdf
fn gram_schmidt_norm_squared(f: &Polynomial<i16>, g: &Polynomial<i16>) -> f64 {
let n = f.coefficients.len();
let norm_f_squared = f.l2_norm_squared();
let norm_g_squared = g.l2_norm_squared();
let gamma1 = norm_f_squared + norm_g_squared;
let f_fft = f.map(|i| Complex64::new(*i as f64, 0.0)).fft();
let g_fft = g.map(|i| Complex64::new(*i as f64, 0.0)).fft();
let f_adj_fft = f_fft.map(|c| c.conj());
let g_adj_fft = g_fft.map(|c| c.conj());
let ffgg_fft = f_fft.hadamard_mul(&f_adj_fft) + g_fft.hadamard_mul(&g_adj_fft);
let ffgg_fft_inverse = ffgg_fft.hadamard_inv();
let qf_over_ffgg_fft = f_adj_fft.map(|c| c * (MODULUS as f64)).hadamard_mul(&ffgg_fft_inverse);
let qg_over_ffgg_fft = g_adj_fft.map(|c| c * (MODULUS as f64)).hadamard_mul(&ffgg_fft_inverse);
let norm_f_over_ffgg_squared =
qf_over_ffgg_fft.coefficients.iter().map(|c| (c * c.conj()).re).sum::<f64>() / (n as f64);
let norm_g_over_ffgg_squared =
qg_over_ffgg_fft.coefficients.iter().map(|c| (c * c.conj()).re).sum::<f64>() / (n as f64);
let gamma2 = norm_f_over_ffgg_squared + norm_g_over_ffgg_squared;
f64::max(gamma1, gamma2)
}
/// Reduces the vector (F,G) relative to (f,g). This method follows the python implementation [1].
/// Note that this algorithm can end up in an infinite loop. (It's one of the things the author
/// would like to fix.) When this happens, control returns an error (hence the return type) and
/// generates another keypair with fresh randomness.
///
/// Algorithm 7 in the spec [2, p.35]
///
/// [1]: https://github.com/tprest/falcon.py
///
/// [2]: https://falcon-sign.info/falcon.pdf
fn babai_reduce(
f: &Polynomial<BigInt>,
g: &Polynomial<BigInt>,
capital_f: &mut Polynomial<BigInt>,
capital_g: &mut Polynomial<BigInt>,
) -> Result<(), String> {
let bitsize = |bi: &BigInt| (bi.bits() + 7) & (u64::MAX ^ 7);
let n = f.coefficients.len();
let size = [
f.map(bitsize).fold(0, |a, &b| u64::max(a, b)),
g.map(bitsize).fold(0, |a, &b| u64::max(a, b)),
53,
]
.into_iter()
.max()
.unwrap();
let shift = (size as i64) - 53;
let f_adjusted = f
.map(|bi| Complex64::new(i64::try_from(bi >> shift).unwrap() as f64, 0.0))
.fft();
let g_adjusted = g
.map(|bi| Complex64::new(i64::try_from(bi >> shift).unwrap() as f64, 0.0))
.fft();
let f_star_adjusted = f_adjusted.map(|c| c.conj());
let g_star_adjusted = g_adjusted.map(|c| c.conj());
let denominator_fft =
f_adjusted.hadamard_mul(&f_star_adjusted) + g_adjusted.hadamard_mul(&g_star_adjusted);
let mut counter = 0;
loop {
let capital_size = [
capital_f.map(bitsize).fold(0, |a, &b| u64::max(a, b)),
capital_g.map(bitsize).fold(0, |a, &b| u64::max(a, b)),
53,
]
.into_iter()
.max()
.unwrap();
if capital_size < size {
break;
}
let capital_shift = (capital_size as i64) - 53;
let capital_f_adjusted = capital_f
.map(|bi| Complex64::new(i64::try_from(bi >> capital_shift).unwrap() as f64, 0.0))
.fft();
let capital_g_adjusted = capital_g
.map(|bi| Complex64::new(i64::try_from(bi >> capital_shift).unwrap() as f64, 0.0))
.fft();
let numerator = capital_f_adjusted.hadamard_mul(&f_star_adjusted)
+ capital_g_adjusted.hadamard_mul(&g_star_adjusted);
let quotient = numerator.hadamard_div(&denominator_fft).ifft();
let k = quotient.map(|f| Into::<BigInt>::into(f.re.round() as i64));
if k.is_zero() {
break;
}
let kf = (k.clone().karatsuba(f))
.reduce_by_cyclotomic(n)
.map(|bi| bi << (capital_size - size));
let kg = (k.clone().karatsuba(g))
.reduce_by_cyclotomic(n)
.map(|bi| bi << (capital_size - size));
*capital_f -= kf;
*capital_g -= kg;
counter += 1;
if counter > 1000 {
// If we get here, that means that (with high likelihood) we are in an
// infinite loop. We know it happens from time to time -- seldomly, but it
// does. It would be nice to fix that! But in order to fix it we need to be
// able to reproduce it, and for that we need test vectors. So print them
// and hope that one day they circle back to the implementor.
return Err(format!("Encountered infinite loop in babai_reduce of falcon-rust.\n\\
Please help the developer(s) fix it! You can do this by sending them the inputs to the function that caused the behavior:\n\\
f: {:?}\n\\
g: {:?}\n\\
capital_f: {:?}\n\\
capital_g: {:?}\n", f.coefficients, g.coefficients, capital_f.coefficients, capital_g.coefficients));
}
}
Ok(())
}
/// Extended Euclidean algorithm for computing the greatest common divisor (g) and
/// Bézout coefficients (u, v) for the relation
///
/// $$ u a + v b = g . $$
///
/// Implementation adapted from Wikipedia [1].
///
/// [1]: https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm#Pseudocode
fn xgcd(a: &BigInt, b: &BigInt) -> (BigInt, BigInt, BigInt) {
let (mut old_r, mut r) = (a.clone(), b.clone());
let (mut old_s, mut s) = (BigInt::one(), BigInt::zero());
let (mut old_t, mut t) = (BigInt::zero(), BigInt::one());
while r != BigInt::zero() {
let quotient = old_r.clone() / r.clone();
(old_r, r) = (r.clone(), old_r.clone() - quotient.clone() * r);
(old_s, s) = (s.clone(), old_s.clone() - quotient.clone() * s);
(old_t, t) = (t.clone(), old_t.clone() - quotient * t);
}
(old_r, old_s, old_t)
}

View File

@@ -0,0 +1,616 @@
use super::{field::FalconFelt, Inverse};
use crate::dsa::rpo_falcon512::{MODULUS, N};
use crate::Felt;
use alloc::vec::Vec;
use core::default::Default;
use core::fmt::Debug;
use core::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub, SubAssign};
use num::{One, Zero};
#[derive(Debug, Clone, Default)]
pub struct Polynomial<F> {
pub coefficients: Vec<F>,
}
impl<F> Polynomial<F>
where
F: Clone,
{
pub fn new(coefficients: Vec<F>) -> Self {
Self { coefficients }
}
}
impl<
F: Mul<Output = F> + Sub<Output = F> + AddAssign + Zero + Div<Output = F> + Clone + Inverse,
> Polynomial<F>
{
pub fn hadamard_mul(&self, other: &Self) -> Self {
Polynomial::new(
self.coefficients
.iter()
.zip(other.coefficients.iter())
.map(|(a, b)| *a * *b)
.collect(),
)
}
pub fn hadamard_div(&self, other: &Self) -> Self {
let other_coefficients_inverse = F::batch_inverse_or_zero(&other.coefficients);
Polynomial::new(
self.coefficients
.iter()
.zip(other_coefficients_inverse.iter())
.map(|(a, b)| *a * *b)
.collect(),
)
}
pub fn hadamard_inv(&self) -> Self {
let coefficients_inverse = F::batch_inverse_or_zero(&self.coefficients);
Polynomial::new(coefficients_inverse)
}
}
impl<F: Zero + PartialEq + Clone> Polynomial<F> {
pub fn degree(&self) -> Option<usize> {
if self.coefficients.is_empty() {
return None;
}
let mut max_index = self.coefficients.len() - 1;
while self.coefficients[max_index] == F::zero() {
if let Some(new_index) = max_index.checked_sub(1) {
max_index = new_index;
} else {
return None;
}
}
Some(max_index)
}
pub fn lc(&self) -> F {
match self.degree() {
Some(non_negative_degree) => self.coefficients[non_negative_degree].clone(),
None => F::zero(),
}
}
}
/// The following implementations are specific to cyclotomic polynomial rings,
/// i.e., F\[ X \] / <X^n + 1>, and are used extensively in Falcon.
impl<
F: One
+ Zero
+ Clone
+ Neg<Output = F>
+ MulAssign
+ AddAssign
+ Div<Output = F>
+ Sub<Output = F>
+ PartialEq,
> Polynomial<F>
{
/// Reduce the polynomial by X^n + 1.
pub fn reduce_by_cyclotomic(&self, n: usize) -> Self {
let mut coefficients = vec![F::zero(); n];
let mut sign = -F::one();
for (i, c) in self.coefficients.iter().cloned().enumerate() {
if i % n == 0 {
sign *= -F::one();
}
coefficients[i % n] += sign.clone() * c;
}
Polynomial::new(coefficients)
}
/// Computes the field norm of the polynomial as an element of the cyclotomic ring
/// F\[ X \] / <X^n + 1 > relative to one of half the size, i.e., F\[ X \] / <X^(n/2) + 1> .
///
/// Corresponds to formula 3.25 in the spec [1, p.30].
///
/// [1]: https://falcon-sign.info/falcon.pdf
pub fn field_norm(&self) -> Self {
let n = self.coefficients.len();
let mut f0_coefficients = vec![F::zero(); n / 2];
let mut f1_coefficients = vec![F::zero(); n / 2];
for i in 0..n / 2 {
f0_coefficients[i] = self.coefficients[2 * i].clone();
f1_coefficients[i] = self.coefficients[2 * i + 1].clone();
}
let f0 = Polynomial::new(f0_coefficients);
let f1 = Polynomial::new(f1_coefficients);
let f0_squared = (f0.clone() * f0).reduce_by_cyclotomic(n / 2);
let f1_squared = (f1.clone() * f1).reduce_by_cyclotomic(n / 2);
let x = Polynomial::new(vec![F::zero(), F::one()]);
f0_squared - (x * f1_squared).reduce_by_cyclotomic(n / 2)
}
/// Lifts an element from a cyclotomic polynomial ring to one of double the size.
pub fn lift_next_cyclotomic(&self) -> Self {
let n = self.coefficients.len();
let mut coefficients = vec![F::zero(); n * 2];
for i in 0..n {
coefficients[2 * i] = self.coefficients[i].clone();
}
Self::new(coefficients)
}
/// Computes the galois adjoint of the polynomial in the cyclotomic ring F\[ X \] / < X^n + 1 > ,
/// which corresponds to f(x^2).
pub fn galois_adjoint(&self) -> Self {
Self::new(
self.coefficients
.iter()
.enumerate()
.map(|(i, c)| if i % 2 == 0 { c.clone() } else { c.clone().neg() })
.collect(),
)
}
}
impl<F: Clone + Into<f64>> Polynomial<F> {
pub(crate) fn l2_norm_squared(&self) -> f64 {
self.coefficients
.iter()
.map(|i| Into::<f64>::into(i.clone()))
.map(|i| i * i)
.sum::<f64>()
}
}
impl<F> PartialEq for Polynomial<F>
where
F: Zero + PartialEq + Clone + AddAssign,
{
fn eq(&self, other: &Self) -> bool {
if self.is_zero() && other.is_zero() {
true
} else if self.is_zero() || other.is_zero() {
false
} else {
let self_degree = self.degree().unwrap();
let other_degree = other.degree().unwrap();
self.coefficients[0..=self_degree] == other.coefficients[0..=other_degree]
}
}
}
impl<F> Eq for Polynomial<F> where F: Zero + PartialEq + Clone + AddAssign {}
impl<F> Add for &Polynomial<F>
where
F: Add<Output = F> + AddAssign + Clone,
{
type Output = Polynomial<F>;
fn add(self, rhs: Self) -> Self::Output {
let coefficients = if self.coefficients.len() >= rhs.coefficients.len() {
let mut coefficients = self.coefficients.clone();
for (i, c) in rhs.coefficients.iter().enumerate() {
coefficients[i] += c.clone();
}
coefficients
} else {
let mut coefficients = rhs.coefficients.clone();
for (i, c) in self.coefficients.iter().enumerate() {
coefficients[i] += c.clone();
}
coefficients
};
Self::Output { coefficients }
}
}
impl<F> Add for Polynomial<F>
where
F: Add<Output = F> + AddAssign + Clone,
{
type Output = Polynomial<F>;
fn add(self, rhs: Self) -> Self::Output {
let coefficients = if self.coefficients.len() >= rhs.coefficients.len() {
let mut coefficients = self.coefficients.clone();
for (i, c) in rhs.coefficients.into_iter().enumerate() {
coefficients[i] += c;
}
coefficients
} else {
let mut coefficients = rhs.coefficients.clone();
for (i, c) in self.coefficients.into_iter().enumerate() {
coefficients[i] += c;
}
coefficients
};
Self::Output { coefficients }
}
}
impl<F> AddAssign for Polynomial<F>
where
F: Add<Output = F> + AddAssign + Clone,
{
fn add_assign(&mut self, rhs: Self) {
if self.coefficients.len() >= rhs.coefficients.len() {
for (i, c) in rhs.coefficients.into_iter().enumerate() {
self.coefficients[i] += c;
}
} else {
let mut coefficients = rhs.coefficients.clone();
for (i, c) in self.coefficients.iter().enumerate() {
coefficients[i] += c.clone();
}
self.coefficients = coefficients;
}
}
}
impl<F> Sub for &Polynomial<F>
where
F: Sub<Output = F> + Clone + Neg<Output = F> + Add<Output = F> + AddAssign,
{
type Output = Polynomial<F>;
fn sub(self, rhs: Self) -> Self::Output {
self + &(-rhs)
}
}
impl<F> Sub for Polynomial<F>
where
F: Sub<Output = F> + Clone + Neg<Output = F> + Add<Output = F> + AddAssign,
{
type Output = Polynomial<F>;
fn sub(self, rhs: Self) -> Self::Output {
self + (-rhs)
}
}
impl<F> SubAssign for Polynomial<F>
where
F: Add<Output = F> + Neg<Output = F> + AddAssign + Clone + Sub<Output = F>,
{
fn sub_assign(&mut self, rhs: Self) {
self.coefficients = self.clone().sub(rhs).coefficients;
}
}
impl<F: Neg<Output = F> + Clone> Neg for &Polynomial<F> {
type Output = Polynomial<F>;
fn neg(self) -> Self::Output {
Self::Output {
coefficients: self.coefficients.iter().cloned().map(|a| -a).collect(),
}
}
}
impl<F: Neg<Output = F> + Clone> Neg for Polynomial<F> {
type Output = Self;
fn neg(self) -> Self::Output {
Self::Output {
coefficients: self.coefficients.iter().cloned().map(|a| -a).collect(),
}
}
}
impl<F> Mul for &Polynomial<F>
where
F: Add + AddAssign + Mul<Output = F> + Sub<Output = F> + Zero + PartialEq + Clone,
{
type Output = Polynomial<F>;
fn mul(self, other: Self) -> Self::Output {
if self.is_zero() || other.is_zero() {
return Polynomial::<F>::zero();
}
let mut coefficients =
vec![F::zero(); self.coefficients.len() + other.coefficients.len() - 1];
for i in 0..self.coefficients.len() {
for j in 0..other.coefficients.len() {
coefficients[i + j] += self.coefficients[i].clone() * other.coefficients[j].clone();
}
}
Polynomial { coefficients }
}
}
impl<F> Mul for Polynomial<F>
where
F: Add + AddAssign + Mul<Output = F> + Zero + PartialEq + Clone,
{
type Output = Self;
fn mul(self, other: Self) -> Self::Output {
if self.is_zero() || other.is_zero() {
return Self::zero();
}
let mut coefficients =
vec![F::zero(); self.coefficients.len() + other.coefficients.len() - 1];
for i in 0..self.coefficients.len() {
for j in 0..other.coefficients.len() {
coefficients[i + j] += self.coefficients[i].clone() * other.coefficients[j].clone();
}
}
Self { coefficients }
}
}
impl<F: Add + Mul<Output = F> + Zero + Clone> Mul<F> for &Polynomial<F> {
type Output = Polynomial<F>;
fn mul(self, other: F) -> Self::Output {
Polynomial {
coefficients: self.coefficients.iter().cloned().map(|i| i * other.clone()).collect(),
}
}
}
impl<F: Add + Mul<Output = F> + Zero + Clone> Mul<F> for Polynomial<F> {
type Output = Polynomial<F>;
fn mul(self, other: F) -> Self::Output {
Polynomial {
coefficients: self.coefficients.iter().cloned().map(|i| i * other.clone()).collect(),
}
}
}
impl<F: Mul<Output = F> + Sub<Output = F> + AddAssign + Zero + Div<Output = F> + Clone>
Polynomial<F>
{
/// Multiply two polynomials using Karatsuba's divide-and-conquer algorithm.
pub fn karatsuba(&self, other: &Self) -> Self {
Polynomial::new(vector_karatsuba(&self.coefficients, &other.coefficients))
}
}
impl<F> One for Polynomial<F>
where
F: Clone + One + PartialEq + Zero + AddAssign,
{
fn one() -> Self {
Self { coefficients: vec![F::one()] }
}
}
impl<F> Zero for Polynomial<F>
where
F: Zero + PartialEq + Clone + AddAssign,
{
fn zero() -> Self {
Self { coefficients: vec![] }
}
fn is_zero(&self) -> bool {
self.degree().is_none()
}
}
impl<F: Zero + Clone> Polynomial<F> {
pub fn shift(&self, shamt: usize) -> Self {
Self {
coefficients: [vec![F::zero(); shamt], self.coefficients.clone()].concat(),
}
}
pub fn constant(f: F) -> Self {
Self { coefficients: vec![f] }
}
pub fn map<G: Clone, C: FnMut(&F) -> G>(&self, closure: C) -> Polynomial<G> {
Polynomial::<G>::new(self.coefficients.iter().map(closure).collect())
}
pub fn fold<G, C: FnMut(G, &F) -> G + Clone>(&self, mut initial_value: G, closure: C) -> G {
for c in self.coefficients.iter() {
initial_value = (closure.clone())(initial_value, c);
}
initial_value
}
}
impl<F> Div<Polynomial<F>> for Polynomial<F>
where
F: Zero
+ One
+ PartialEq
+ AddAssign
+ Clone
+ Mul<Output = F>
+ MulAssign
+ Div<Output = F>
+ Neg<Output = F>
+ Sub<Output = F>,
{
type Output = Polynomial<F>;
fn div(self, denominator: Self) -> Self::Output {
if denominator.is_zero() {
panic!();
}
if self.is_zero() {
Self::zero();
}
let mut remainder = self.clone();
let mut quotient = Polynomial::<F>::zero();
while remainder.degree().unwrap() >= denominator.degree().unwrap() {
let shift = remainder.degree().unwrap() - denominator.degree().unwrap();
let quotient_coefficient = remainder.lc() / denominator.lc();
let monomial = Self::constant(quotient_coefficient).shift(shift);
quotient += monomial.clone();
remainder -= monomial * denominator.clone();
if remainder.is_zero() {
break;
}
}
quotient
}
}
fn vector_karatsuba<
F: Zero + AddAssign + Mul<Output = F> + Sub<Output = F> + Div<Output = F> + Clone,
>(
left: &[F],
right: &[F],
) -> Vec<F> {
let n = left.len();
if n <= 8 {
let mut product = vec![F::zero(); left.len() + right.len() - 1];
for (i, l) in left.iter().enumerate() {
for (j, r) in right.iter().enumerate() {
product[i + j] += l.clone() * r.clone();
}
}
return product;
}
let n_over_2 = n / 2;
let mut product = vec![F::zero(); 2 * n - 1];
let left_lo = &left[0..n_over_2];
let right_lo = &right[0..n_over_2];
let left_hi = &left[n_over_2..];
let right_hi = &right[n_over_2..];
let left_sum: Vec<F> =
left_lo.iter().zip(left_hi).map(|(a, b)| a.clone() + b.clone()).collect();
let right_sum: Vec<F> =
right_lo.iter().zip(right_hi).map(|(a, b)| a.clone() + b.clone()).collect();
let prod_lo = vector_karatsuba(left_lo, right_lo);
let prod_hi = vector_karatsuba(left_hi, right_hi);
let prod_mid: Vec<F> = vector_karatsuba(&left_sum, &right_sum)
.iter()
.zip(prod_lo.iter().zip(prod_hi.iter()))
.map(|(s, (l, h))| s.clone() - (l.clone() + h.clone()))
.collect();
for (i, l) in prod_lo.into_iter().enumerate() {
product[i] = l;
}
for (i, m) in prod_mid.into_iter().enumerate() {
product[i + n_over_2] += m;
}
for (i, h) in prod_hi.into_iter().enumerate() {
product[i + n] += h
}
product
}
impl From<Polynomial<FalconFelt>> for Polynomial<Felt> {
fn from(item: Polynomial<FalconFelt>) -> Self {
let res: Vec<Felt> =
item.coefficients.iter().map(|a| Felt::from(a.value() as u16)).collect();
Polynomial::new(res)
}
}
impl From<&Polynomial<FalconFelt>> for Polynomial<Felt> {
fn from(item: &Polynomial<FalconFelt>) -> Self {
let res: Vec<Felt> =
item.coefficients.iter().map(|a| Felt::from(a.value() as u16)).collect();
Polynomial::new(res)
}
}
impl From<Polynomial<i16>> for Polynomial<FalconFelt> {
fn from(item: Polynomial<i16>) -> Self {
let res: Vec<FalconFelt> = item.coefficients.iter().map(|&a| FalconFelt::new(a)).collect();
Polynomial::new(res)
}
}
impl From<&Polynomial<i16>> for Polynomial<FalconFelt> {
fn from(item: &Polynomial<i16>) -> Self {
let res: Vec<FalconFelt> = item.coefficients.iter().map(|&a| FalconFelt::new(a)).collect();
Polynomial::new(res)
}
}
impl From<Vec<i16>> for Polynomial<FalconFelt> {
fn from(item: Vec<i16>) -> Self {
let res: Vec<FalconFelt> = item.iter().map(|&a| FalconFelt::new(a)).collect();
Polynomial::new(res)
}
}
impl From<&Vec<i16>> for Polynomial<FalconFelt> {
fn from(item: &Vec<i16>) -> Self {
let res: Vec<FalconFelt> = item.iter().map(|&a| FalconFelt::new(a)).collect();
Polynomial::new(res)
}
}
impl Polynomial<FalconFelt> {
pub fn norm_squared(&self) -> u64 {
self.coefficients
.iter()
.map(|&i| i.balanced_value() as i64)
.map(|i| (i * i) as u64)
.sum::<u64>()
}
// PUBLIC ACCESSORS
// --------------------------------------------------------------------------------------------
/// Returns the coefficients of this polynomial as field elements.
pub fn to_elements(&self) -> Vec<Felt> {
self.coefficients.iter().map(|&a| Felt::from(a.value() as u16)).collect()
}
// POLYNOMIAL OPERATIONS
// --------------------------------------------------------------------------------------------
/// Multiplies two polynomials over Z_p\[x\] without reducing modulo p. Given that the degrees
/// of the input polynomials are less than 512 and their coefficients are less than the modulus
/// q equal to 12289, the resulting product polynomial is guaranteed to have coefficients less
/// than the Miden prime.
///
/// Note that this multiplication is not over Z_p\[x\]/(phi).
pub fn mul_modulo_p(a: &Self, b: &Self) -> [u64; 1024] {
let mut c = [0; 2 * N];
for i in 0..N {
for j in 0..N {
c[i + j] += a.coefficients[i].value() as u64 * b.coefficients[j].value() as u64;
}
}
c
}
/// Reduces a polynomial, that is the product of two polynomials over Z_p\[x\], modulo
/// the irreducible polynomial phi. This results in an element in Z_p\[x\]/(phi).
pub fn reduce_negacyclic(a: &[u64; 1024]) -> Self {
let mut c = [FalconFelt::zero(); N];
let modulus = MODULUS as u16;
for i in 0..N {
let ai = a[N + i] % modulus as u64;
let neg_ai = (modulus - ai as u16) % modulus;
let bi = (a[i] % modulus as u64) as u16;
c[i] = FalconFelt::new(((neg_ai + bi) % modulus) as i16);
}
Self::new(c.to_vec())
}
}
// TESTS
// ================================================================================================
#[cfg(test)]
mod tests {
use super::{FalconFelt, Polynomial, N};
#[test]
fn test_negacyclic_reduction() {
let coef1: [u8; N] = rand_utils::rand_array();
let coef2: [u8; N] = rand_utils::rand_array();
let poly1 = Polynomial::new(coef1.iter().map(|&a| FalconFelt::new(a as i16)).collect());
let poly2 = Polynomial::new(coef2.iter().map(|&a| FalconFelt::new(a as i16)).collect());
let prod = poly1.clone() * poly2.clone();
assert_eq!(
prod.reduce_by_cyclotomic(N),
Polynomial::reduce_negacyclic(&Polynomial::mul_modulo_p(&poly1, &poly2))
);
}
}

View File

@@ -0,0 +1,298 @@
use core::f64::consts::LN_2;
use rand::Rng;
#[cfg(not(feature = "std"))]
use num::Float;
/// Samples an integer from {0, ..., 18} according to the distribution χ, which is close to
/// the half-Gaussian distribution on the natural numbers with mean 0 and standard deviation
/// equal to sigma_max.
fn base_sampler(bytes: [u8; 9]) -> i16 {
const RCDT: [u128; 18] = [
3024686241123004913666,
1564742784480091954050,
636254429462080897535,
199560484645026482916,
47667343854657281903,
8595902006365044063,
1163297957344668388,
117656387352093658,
8867391802663976,
496969357462633,
20680885154299,
638331848991,
14602316184,
247426747,
3104126,
28824,
198,
1,
];
let u = u128::from_be_bytes([vec![0u8; 7], bytes.to_vec()].concat().try_into().unwrap());
RCDT.into_iter().filter(|r| u < *r).count() as i16
}
/// Computes an integer approximation of 2^63 * ccs * exp(-x).
fn approx_exp(x: f64, ccs: f64) -> u64 {
// The constants C are used to approximate exp(-x); these
// constants are taken from FACCT (up to a scaling factor
// of 2^63):
// https://eprint.iacr.org/2018/1234
// https://github.com/raykzhao/gaussian
const C: [u64; 13] = [
0x00000004741183A3u64,
0x00000036548CFC06u64,
0x0000024FDCBF140Au64,
0x0000171D939DE045u64,
0x0000D00CF58F6F84u64,
0x000680681CF796E3u64,
0x002D82D8305B0FEAu64,
0x011111110E066FD0u64,
0x0555555555070F00u64,
0x155555555581FF00u64,
0x400000000002B400u64,
0x7FFFFFFFFFFF4800u64,
0x8000000000000000u64,
];
let mut z: u64;
let mut y: u64;
let twoe63 = 1u64 << 63;
y = C[0];
z = f64::floor(x * (twoe63 as f64)) as u64;
for cu in C.iter().skip(1) {
let zy = (z as u128) * (y as u128);
y = cu - ((zy >> 63) as u64);
}
z = f64::floor((twoe63 as f64) * ccs) as u64;
(((z as u128) * (y as u128)) >> 63) as u64
}
/// A random bool that is true with probability ≈ ccs · exp(-x).
fn ber_exp(x: f64, ccs: f64, random_bytes: [u8; 7]) -> bool {
// 0.69314718055994530941 = ln(2)
let s = f64::floor(x / LN_2) as usize;
let r = x - LN_2 * (s as f64);
let shamt = usize::min(s, 63);
let z = ((((approx_exp(r, ccs) as u128) << 1) - 1) >> shamt) as u64;
let mut w = 0i16;
for (index, i) in (0..64).step_by(8).rev().enumerate() {
let byte = random_bytes[index];
w = (byte as i16) - (((z >> i) & 0xff) as i16);
if w != 0 {
break;
}
}
w < 0
}
/// Samples an integer from the Gaussian distribution with given mean (mu) and standard deviation
/// (sigma).
pub(crate) fn sampler_z<R: Rng>(mu: f64, sigma: f64, sigma_min: f64, rng: &mut R) -> i16 {
const SIGMA_MAX: f64 = 1.8205;
const INV_2SIGMA_MAX_SQ: f64 = 1f64 / (2f64 * SIGMA_MAX * SIGMA_MAX);
let isigma = 1f64 / sigma;
let dss = 0.5f64 * isigma * isigma;
let s = f64::floor(mu);
let r = mu - s;
let ccs = sigma_min * isigma;
loop {
let z0 = base_sampler(rng.gen());
let random_byte: u8 = rng.gen();
let b = (random_byte & 1) as i16;
let z = b + ((b << 1) - 1) * z0;
let zf_min_r = (z as f64) - r;
// x = ((z-r)^2)/(2*sigma^2) - ((z-b)^2)/(2*sigma0^2)
let x = zf_min_r * zf_min_r * dss - (z0 * z0) as f64 * INV_2SIGMA_MAX_SQ;
if ber_exp(x, ccs, rng.gen()) {
return z + (s as i16);
}
}
}
#[cfg(all(test, feature = "std"))]
mod test {
use alloc::vec::Vec;
use rand::RngCore;
use std::{thread::sleep, time::Duration};
use super::{approx_exp, ber_exp, sampler_z};
/// RNG used only for testing purposes, whereby the produced
/// string of random bytes is equal to the one it is initialized
/// with. Whatever you do, do not use this RNG in production.
struct UnsafeBufferRng {
buffer: Vec<u8>,
index: usize,
}
impl UnsafeBufferRng {
fn new(buffer: &[u8]) -> Self {
Self { buffer: buffer.to_vec(), index: 0 }
}
fn next(&mut self) -> u8 {
if self.buffer.len() <= self.index {
// panic!("Ran out of buffer.");
sleep(Duration::from_millis(10));
0u8
} else {
let return_value = self.buffer[self.index];
self.index += 1;
return_value
}
}
}
impl RngCore for UnsafeBufferRng {
fn next_u32(&mut self) -> u32 {
// let bytes: [u8; 4] = (0..4)
// .map(|_| self.next())
// .collect_vec()
// .try_into()
// .unwrap();
// u32::from_be_bytes(bytes)
u32::from_le_bytes([self.next(), 0, 0, 0])
}
fn next_u64(&mut self) -> u64 {
// let bytes: [u8; 8] = (0..8)
// .map(|_| self.next())
// .collect_vec()
// .try_into()
// .unwrap();
// u64::from_be_bytes(bytes)
u64::from_le_bytes([self.next(), 0, 0, 0, 0, 0, 0, 0])
}
fn fill_bytes(&mut self, dest: &mut [u8]) {
for d in dest.iter_mut() {
*d = self.next();
}
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> {
for d in dest.iter_mut() {
*d = self.next();
}
Ok(())
}
}
#[test]
fn test_unsafe_buffer_rng() {
let seed_bytes = hex::decode("7FFECD162AE2").unwrap();
let mut rng = UnsafeBufferRng::new(&seed_bytes);
let generated_bytes: Vec<u8> = (0..seed_bytes.len()).map(|_| rng.next()).collect();
assert_eq!(seed_bytes, generated_bytes);
}
#[test]
fn test_approx_exp() {
let precision = 1u64 << 14;
// known answers were generated with the following sage script:
//```sage
// num_samples = 10
// precision = 200
// R = Reals(precision)
//
// print(f"let kats : [(f64, f64, u64);{num_samples}] = [")
// for i in range(num_samples):
// x = RDF.random_element(0.0, 0.693147180559945)
// ccs = RDF.random_element(0.0, 1.0)
// res = round(2^63 * R(ccs) * exp(R(-x)))
// print(f"({x}, {ccs}, {res}),")
// print("];")
// ```
let kats: [(f64, f64, u64); 10] = [
(0.2314993926072656, 0.8148006314615972, 5962140072160879737),
(0.2648875572812225, 0.12769669655309035, 903712282351034505),
(0.11251957513682391, 0.9264611470305881, 7635725498677341553),
(0.04353439307256617, 0.5306497137523327, 4685877322232397936),
(0.41834495299784347, 0.879438856118578, 5338392138535350986),
(0.32579398973228557, 0.16513412873289002, 1099603299296456803),
(0.5939508073919817, 0.029776019144967303, 151637565622779016),
(0.2932367999399056, 0.37123847662857923, 2553827649386670452),
(0.5005699297417507, 0.31447208863888976, 1758235618083658825),
(0.4876437338498085, 0.6159515298936868, 3488632981903743976),
];
for (x, ccs, answer) in kats {
let difference = (answer as i128) - (approx_exp(x, ccs) as i128);
assert!(
(difference * difference) as u64 <= precision * precision,
"answer: {answer} versus approximation: {}\ndifference: {} whereas precision: {}",
approx_exp(x, ccs),
difference,
precision
);
}
}
#[test]
fn test_ber_exp() {
let kats = [
(
1.268_314_048_020_498_4,
0.749_990_853_267_664_9,
hex::decode("ea000000000000").unwrap(),
false,
),
(
0.001_563_917_959_143_409_6,
0.749_990_853_267_664_9,
hex::decode("6c000000000000").unwrap(),
true,
),
(
0.017_921_215_753_999_235,
0.749_990_853_267_664_9,
hex::decode("c2000000000000").unwrap(),
false,
),
(
0.776_117_648_844_980_6,
0.751_181_554_542_520_8,
hex::decode("58000000000000").unwrap(),
true,
),
];
for (x, ccs, bytes, answer) in kats {
assert_eq!(answer, ber_exp(x, ccs, bytes.try_into().unwrap()));
}
}
#[test]
fn test_sampler_z() {
let sigma_min = 1.277833697;
// known answers from the doc, table 3.2, page 44
// https://falcon-sign.info/falcon.pdf
// The zeros were added to account for dropped bytes.
let kats = [
(-91.90471153063714,1.7037990414754918,hex::decode("0fc5442ff043d66e91d1ea000000000000cac64ea5450a22941edc6c").unwrap(),-92),
(-8.322564895434937,1.7037990414754918,hex::decode("f4da0f8d8444d1a77265c2000000000000ef6f98bbbb4bee7db8d9b3").unwrap(),-8),
(-19.096516109216804,1.7035823083824078,hex::decode("db47f6d7fb9b19f25c36d6000000000000b9334d477a8bc0be68145d").unwrap(),-20),
(-11.335543982423326, 1.7035823083824078, hex::decode("ae41b4f5209665c74d00dc000000000000c1a8168a7bb516b3190cb42c1ded26cd52000000000000aed770eca7dd334e0547bcc3c163ce0b").unwrap(), -12),
(7.9386734193997555, 1.6984647769450156, hex::decode("31054166c1012780c603ae0000000000009b833cec73f2f41ca5807c000000000000c89c92158834632f9b1555").unwrap(), 8),
(-28.990850086867255, 1.6984647769450156, hex::decode("737e9d68a50a06dbbc6477").unwrap(), -30),
(-9.071257914091655, 1.6980782114808988, hex::decode("a98ddd14bf0bf22061d632").unwrap(), -10),
(-43.88754568839566, 1.6980782114808988, hex::decode("3cbf6818a68f7ab9991514").unwrap(), -41),
(-58.17435547946095,1.7010983419195522,hex::decode("6f8633f5bfa5d26848668e0000000000003d5ddd46958e97630410587c").unwrap(),-61),
(-43.58664906684732, 1.7010983419195522, hex::decode("272bc6c25f5c5ee53f83c40000000000003a361fbc7cc91dc783e20a").unwrap(), -46),
(-34.70565203313315, 1.7009387219711465, hex::decode("45443c59574c2c3b07e2e1000000000000d9071e6d133dbe32754b0a").unwrap(), -34),
(-44.36009577368896, 1.7009387219711465, hex::decode("6ac116ed60c258e2cbaeab000000000000728c4823e6da36e18d08da0000000000005d0cc104e21cc7fd1f5ca8000000000000d9dbb675266c928448059e").unwrap(), -44),
(-21.783037079346236, 1.6958406126012802, hex::decode("68163bc1e2cbf3e18e7426").unwrap(), -23),
(-39.68827784633828, 1.6958406126012802, hex::decode("d6a1b51d76222a705a0259").unwrap(), -40),
(-18.488607061056847, 1.6955259305261838, hex::decode("f0523bfaa8a394bf4ea5c10000000000000f842366fde286d6a30803").unwrap(), -22),
(-48.39610939101591, 1.6955259305261838, hex::decode("87bd87e63374cee62127fc0000000000006931104aab64f136a0485b").unwrap(), -50),
];
for (mu, sigma, random_bytes, answer) in kats {
assert_eq!(
sampler_z(mu, sigma, sigma_min, &mut UnsafeBufferRng::new(&random_bytes)),
answer
);
}
}
}

View File

@@ -4,33 +4,31 @@ use crate::{
Felt, Word, ZERO,
};
#[cfg(feature = "std")]
mod ffi;
mod error;
mod hash_to_point;
mod keys;
mod polynomial;
mod math;
mod signature;
pub use error::FalconError;
pub use keys::{KeyPair, PublicKey};
pub use polynomial::Polynomial;
pub use signature::Signature;
pub use self::keys::{PubKeyPoly, PublicKey, SecretKey};
pub use self::math::Polynomial;
pub use self::signature::{Signature, SignatureHeader, SignaturePoly};
// CONSTANTS
// ================================================================================================
// The Falcon modulus.
const MODULUS: u16 = 12289;
const MODULUS_MINUS_1_OVER_TWO: u16 = 6144;
// The Falcon modulus p.
const MODULUS: i16 = 12289;
// Number of bits needed to encode an element in the Falcon field.
const FALCON_ENCODING_BITS: u32 = 14;
// The Falcon parameters for Falcon-512. This is the degree of the polynomial `phi := x^N + 1`
// defining the ring Z_p[x]/(phi).
const N: usize = 512;
const LOG_N: usize = 9;
const LOG_N: u8 = 9;
/// Length of nonce used for key-pair generation.
const NONCE_LEN: usize = 40;
const SIG_NONCE_LEN: usize = 40;
/// Number of filed elements used to encode a nonce.
const NONCE_ELEMENTS: usize = 8;
@@ -42,16 +40,64 @@ pub const PK_LEN: usize = 897;
pub const SK_LEN: usize = 1281;
/// Signature length as a u8 vector.
const SIG_LEN: usize = 626;
const SIG_POLY_BYTE_LEN: usize = 625;
/// Bound on the squared-norm of the signature.
const SIG_L2_BOUND: u64 = 34034726;
/// Standard deviation of the Gaussian over the lattice.
const SIGMA: f64 = 165.7366171829776;
// TYPE ALIASES
// ================================================================================================
type SignatureBytes = [u8; NONCE_LEN + SIG_LEN];
type PublicKeyBytes = [u8; PK_LEN];
type SecretKeyBytes = [u8; SK_LEN];
type NonceBytes = [u8; NONCE_LEN];
type NonceElements = [Felt; NONCE_ELEMENTS];
type ShortLatticeBasis = [Polynomial<i16>; 4];
// NONCE
// ================================================================================================
/// Nonce of the Falcon signature.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Nonce([u8; SIG_NONCE_LEN]);
impl Nonce {
/// Returns a new [Nonce] instantiated from the provided bytes.
pub fn new(bytes: [u8; SIG_NONCE_LEN]) -> Self {
Self(bytes)
}
/// Returns the underlying bytes of this nonce.
pub fn as_bytes(&self) -> &[u8; SIG_NONCE_LEN] {
&self.0
}
/// Converts byte representation of the nonce into field element representation.
///
/// Nonce bytes are converted to field elements by taking consecutive 5 byte chunks
/// of the nonce and interpreting them as field elements.
pub fn to_elements(&self) -> [Felt; NONCE_ELEMENTS] {
let mut buffer = [0_u8; 8];
let mut result = [ZERO; 8];
for (i, bytes) in self.0.chunks(5).enumerate() {
buffer[..5].copy_from_slice(bytes);
// we can safely (without overflow) create a new Felt from u64 value here since this
// value contains at most 5 bytes
result[i] = Felt::new(u64::from_le_bytes(buffer));
}
result
}
}
impl Serializable for &Nonce {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
target.write_bytes(&self.0)
}
}
impl Deserializable for Nonce {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let bytes = source.read()?;
Ok(Self(bytes))
}
}

View File

@@ -1,279 +0,0 @@
use alloc::vec::Vec;
use core::ops::{Add, Mul, Sub};
use super::{FalconError, Felt, LOG_N, MODULUS, MODULUS_MINUS_1_OVER_TWO, N, PK_LEN};
// FALCON POLYNOMIAL
// ================================================================================================
/// A polynomial over Z_p\[x\]/(phi) where phi := x^512 + 1
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Polynomial([u16; N]);
impl Polynomial {
// CONSTRUCTORS
// --------------------------------------------------------------------------------------------
/// Constructs a new polynomial from a list of coefficients.
///
/// # Safety
/// This constructor validates that the coefficients are in the valid range only in debug mode.
pub unsafe fn new(data: [u16; N]) -> Self {
for value in data {
debug_assert!(value < MODULUS);
}
Self(data)
}
/// Decodes raw bytes representing a public key into a polynomial in Z_p\[x\]/(phi).
///
/// # Errors
/// Returns an error if:
/// - The provided input is not exactly 897 bytes long.
/// - The first byte of the input is not equal to log2(512) i.e., 9.
/// - Any of the coefficients encoded in the provided input is greater than or equal to the
/// Falcon field modulus.
pub fn from_pub_key(input: &[u8]) -> Result<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))
);
}
}

View File

@@ -1,289 +1,373 @@
use alloc::string::ToString;
use core::cell::OnceCell;
use alloc::{string::ToString, vec::Vec};
use core::ops::Deref;
use super::{
ByteReader, ByteWriter, Deserializable, DeserializationError, Felt, NonceBytes, NonceElements,
Polynomial, PublicKeyBytes, Rpo256, Serializable, SignatureBytes, Word, MODULUS, N,
SIG_L2_BOUND, ZERO,
hash_to_point::hash_to_point_rpo256,
keys::PubKeyPoly,
math::{FalconFelt, FastFft, Polynomial},
ByteReader, ByteWriter, Deserializable, DeserializationError, Felt, Nonce, Rpo256,
Serializable, Word, LOG_N, MODULUS, N, SIG_L2_BOUND, SIG_POLY_BYTE_LEN,
};
use num::Zero;
// FALCON SIGNATURE
// ================================================================================================
/// An RPO Falcon512 signature over a message.
///
/// The signature is a pair of polynomials (s1, s2) in (Z_p\[x\]/(phi))^2, where:
/// The signature is a pair of polynomials (s1, s2) in (Z_p\[x\]/(phi))^2 a nonce `r`, and a public
/// key polynomial `h` where:
/// - p := 12289
/// - phi := x^512 + 1
/// - s1 = c - s2 * h
/// - h is a polynomial representing the public key and c is a polynomial that is the hash-to-point
/// of the message being signed.
///
/// The signature verifies if and only if:
/// The signature verifies against a public key `pk` if and only if:
/// 1. s1 = c - s2 * h
/// 2. |s1|^2 + |s2|^2 <= SIG_L2_BOUND
///
/// where |.| is the norm.
/// where |.| is the norm and:
/// - c = HashToPoint(r || message)
/// - pk = Rpo256::hash(h)
///
/// [Signature] also includes the extended public key which is serialized as:
/// Here h is a polynomial representing the public key and pk is its digest using the Rpo256 hash
/// function. c is a polynomial that is the hash-to-point of the message being signed.
///
/// The polynomial h is serialized as:
/// 1. 1 byte representing the log2(512) i.e., 9.
/// 2. 896 bytes for the public key. This is decoded into the `h` polynomial above.
/// 2. 896 bytes for the public key itself.
///
/// The actual signature is serialized as:
/// The signature is serialized as:
/// 1. A header byte specifying the algorithm used to encode the coefficients of the `s2` polynomial
/// together with the degree of the irreducible polynomial phi.
/// The general format of this byte is 0b0cc1nnnn where:
/// a. cc is either 01 when the compressed encoding algorithm is used and 10 when the
/// uncompressed algorithm is used.
/// b. nnnn is log2(N) where N is the degree of the irreducible polynomial phi.
/// The current implementation works always with cc equal to 0b01 and nnnn equal to 0b1001 and
/// thus the header byte is always equal to 0b00111001.
/// together with the degree of the irreducible polynomial phi. For RPO Falcon512, the header
/// byte is set to `10111001` which differentiates it from the standardized instantiation of
/// the Falcon signature.
/// 2. 40 bytes for the nonce.
/// 3. 625 bytes encoding the `s2` polynomial above.
/// 4. 625 bytes encoding the `s2` polynomial above.
///
/// The total size of the signature (including the extended public key) is 1563 bytes.
#[derive(Debug, Clone)]
/// The total size of the signature is (including the extended public key) is 1563 bytes.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Signature {
pub(super) pk: PublicKeyBytes,
pub(super) sig: SignatureBytes,
// Cached polynomial decoding for public key and signatures
pub(super) pk_polynomial: OnceCell<Polynomial>,
pub(super) sig_polynomial: OnceCell<Polynomial>,
header: SignatureHeader,
nonce: Nonce,
s2: SignaturePoly,
h: PubKeyPoly,
}
impl Signature {
// CONSTRUCTOR
// --------------------------------------------------------------------------------------------
pub fn new(nonce: Nonce, h: PubKeyPoly, s2: SignaturePoly) -> Signature {
Self {
header: SignatureHeader::default(),
nonce,
s2,
h,
}
}
// PUBLIC ACCESSORS
// --------------------------------------------------------------------------------------------
/// Returns the public key polynomial h.
pub fn pub_key_poly(&self) -> Polynomial {
*self.pk_polynomial.get_or_init(|| {
// we assume that the signature was constructed with a valid public key, and thus
// expect() is OK here.
Polynomial::from_pub_key(&self.pk).expect("invalid public key")
})
}
/// Returns the nonce component of the signature represented as field elements.
///
/// Nonce bytes are converted to field elements by taking consecutive 5 byte chunks
/// of the nonce and interpreting them as field elements.
pub fn nonce(&self) -> NonceElements {
// we assume that the signature was constructed with a valid signature, and thus
// expect() is OK here.
let nonce = self.sig[1..41].try_into().expect("invalid signature");
decode_nonce(nonce)
pub fn pk_poly(&self) -> &PubKeyPoly {
&self.h
}
// Returns the polynomial representation of the signature in Z_p[x]/(phi).
pub fn sig_poly(&self) -> Polynomial {
*self.sig_polynomial.get_or_init(|| {
// we assume that the signature was constructed with a valid signature, and thus
// expect() is OK here.
Polynomial::from_signature(&self.sig).expect("invalid signature")
})
pub fn sig_poly(&self) -> &Polynomial<FalconFelt> {
&self.s2
}
// HASH-TO-POINT
// --------------------------------------------------------------------------------------------
/// Returns a polynomial in Z_p\[x\]/(phi) representing the hash of the provided message.
pub fn hash_to_point(&self, message: Word) -> Polynomial {
hash_to_point(message, &self.nonce())
/// Returns the nonce component of the signature.
pub fn nonce(&self) -> &Nonce {
&self.nonce
}
// SIGNATURE VERIFICATION
// --------------------------------------------------------------------------------------------
/// Returns true if this signature is a valid signature for the specified message generated
/// against key pair matching the specified public key commitment.
/// against the secret key matching the specified public key commitment.
pub fn verify(&self, message: Word, pubkey_com: Word) -> bool {
// Make sure the expanded public key matches the provided public key commitment
let h = self.pub_key_poly();
let h_digest: Word = Rpo256::hash_elements(&h.to_elements()).into();
// compute the hash of the public key polynomial
let h_felt: Polynomial<Felt> = (&**self.pk_poly()).into();
let h_digest: Word = Rpo256::hash_elements(&h_felt.coefficients).into();
if h_digest != pubkey_com {
return false;
}
// Make sure the signature is valid
let s2 = self.sig_poly();
let c = self.hash_to_point(message);
let s1 = c - s2 * h;
let sq_norm = s1.sq_norm() + s2.sq_norm();
sq_norm <= SIG_L2_BOUND
let c = hash_to_point_rpo256(message, &self.nonce);
h_digest == pubkey_com && verify_helper(&c, &self.s2, self.pk_poly())
}
}
// SERIALIZATION / DESERIALIZATION
// ================================================================================================
impl Serializable for Signature {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
target.write_bytes(&self.pk);
target.write_bytes(&self.sig);
target.write(&self.header);
target.write(&self.nonce);
target.write(&self.s2);
target.write(&self.h);
}
}
impl Deserializable for Signature {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let pk: PublicKeyBytes = source.read_array()?;
let sig: SignatureBytes = source.read_array()?;
let header = source.read()?;
let nonce = source.read()?;
let s2 = source.read()?;
let h = source.read()?;
// make sure public key and signature can be decoded correctly
let pk_polynomial = Polynomial::from_pub_key(&pk)
.map_err(|err| DeserializationError::InvalidValue(err.to_string()))?
.into();
let sig_polynomial = Polynomial::from_signature(&sig)
.map_err(|err| DeserializationError::InvalidValue(err.to_string()))?
.into();
Ok(Self { header, nonce, s2, h })
}
}
Ok(Self { pk, sig, pk_polynomial, sig_polynomial })
// SIGNATURE HEADER
// ================================================================================================
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SignatureHeader(u8);
impl Default for SignatureHeader {
/// According to section 3.11.3 in the specification [1], the signature header has the format
/// `0cc1nnnn` where:
///
/// 1. `cc` signifies the encoding method. `01` denotes using the compression encoding method
/// and `10` denotes encoding using the uncompressed method.
/// 2. `nnnn` encodes `LOG_N`.
///
/// For RPO Falcon 512 we use compression encoding and N = 512. Moreover, to differentiate the
/// RPO Falcon variant from the reference variant using SHAKE256, we flip the first bit in the
/// header. Thus, for RPO Falcon 512 the header is `10111001`
///
/// [1]: https://falcon-sign.info/falcon.pdf
fn default() -> Self {
Self(0b1011_1001)
}
}
impl Serializable for &SignatureHeader {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
target.write_u8(self.0)
}
}
impl Deserializable for SignatureHeader {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let header = source.read_u8()?;
let (encoding, log_n) = (header >> 4, header & 0b00001111);
if encoding != 0b1011 {
return Err(DeserializationError::InvalidValue(
"Failed to decode signature: not supported encoding algorithm".to_string(),
));
}
if log_n != LOG_N {
return Err(DeserializationError::InvalidValue(
format!("Failed to decode signature: only supported irreducible polynomial degree is 512, 2^{log_n} was provided")
));
}
Ok(Self(header))
}
}
// SIGNATURE POLYNOMIAL
// ================================================================================================
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SignaturePoly(pub Polynomial<FalconFelt>);
impl Deref for SignaturePoly {
type Target = Polynomial<FalconFelt>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<Polynomial<FalconFelt>> for SignaturePoly {
fn from(pk_poly: Polynomial<FalconFelt>) -> Self {
Self(pk_poly)
}
}
impl TryFrom<&[i16; N]> for SignaturePoly {
type Error = ();
fn try_from(coefficients: &[i16; N]) -> Result<Self, Self::Error> {
if are_coefficients_valid(coefficients) {
Ok(Self(coefficients.to_vec().into()))
} else {
Err(())
}
}
}
impl Serializable for &SignaturePoly {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
let sig_coeff: Vec<i16> = self.0.coefficients.iter().map(|a| a.balanced_value()).collect();
let mut sk_bytes = vec![0_u8; SIG_POLY_BYTE_LEN];
let mut acc = 0;
let mut acc_len = 0;
let mut v = 0;
let mut t;
let mut w;
// For each coefficient of x:
// - the sign is encoded on 1 bit
// - the 7 lower bits are encoded naively (binary)
// - the high bits are encoded in unary encoding
//
// Algorithm 17 p. 47 of the specification [1].
//
// [1]: https://falcon-sign.info/falcon.pdf
for &c in sig_coeff.iter() {
acc <<= 1;
t = c;
if t < 0 {
t = -t;
acc |= 1;
}
w = t as u16;
acc <<= 7;
let mask = 127_u32;
acc |= (w as u32) & mask;
w >>= 7;
acc_len += 8;
acc <<= w + 1;
acc |= 1;
acc_len += w + 1;
while acc_len >= 8 {
acc_len -= 8;
sk_bytes[v] = (acc >> acc_len) as u8;
v += 1;
}
}
if acc_len > 0 {
sk_bytes[v] = (acc << (8 - acc_len)) as u8;
}
target.write_bytes(&sk_bytes);
}
}
impl Deserializable for SignaturePoly {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let input = source.read_array::<SIG_POLY_BYTE_LEN>()?;
let mut input_idx = 0;
let mut acc = 0u32;
let mut acc_len = 0;
let mut coefficients = [FalconFelt::zero(); N];
// Algorithm 18 p. 48 of the specification [1].
//
// [1]: https://falcon-sign.info/falcon.pdf
for c in coefficients.iter_mut() {
acc = (acc << 8) | (input[input_idx] as u32);
input_idx += 1;
let b = acc >> acc_len;
let s = b & 128;
let mut m = b & 127;
loop {
if acc_len == 0 {
acc = (acc << 8) | (input[input_idx] as u32);
input_idx += 1;
acc_len = 8;
}
acc_len -= 1;
if ((acc >> acc_len) & 1) != 0 {
break;
}
m += 128;
if m >= 2048 {
return Err(DeserializationError::InvalidValue(
"Failed to decode signature: high bits {m} exceed 2048".to_string(),
));
}
}
if s != 0 && m == 0 {
return Err(DeserializationError::InvalidValue(
"Failed to decode signature: -0 is forbidden".to_string(),
));
}
let felt = if s != 0 { (MODULUS as u32 - m) as u16 } else { m as u16 };
*c = FalconFelt::new(felt as i16);
}
if (acc & ((1 << acc_len) - 1)) != 0 {
return Err(DeserializationError::InvalidValue(
"Failed to decode signature: Non-zero unused bits in the last byte".to_string(),
));
}
Ok(Polynomial::new(coefficients.to_vec()).into())
}
}
// 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];
/// Takes the hash-to-point polynomial `c` of a message, the signature polynomial over
/// the message `s2` and a public key polynomial and returns `true` is the signature is a valid
/// signature for the given parameters, otherwise it returns `false`.
fn verify_helper(c: &Polynomial<FalconFelt>, s2: &SignaturePoly, h: &PubKeyPoly) -> bool {
let h_fft = h.fft();
let s2_fft = s2.fft();
let c_fft = c.fft();
// 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);
// compute the signature polynomial s1 using s1 = c - s2 * h
let s1_fft = c_fft - s2_fft.hadamard_mul(&h_fft);
let s1 = s1_fft.ifft();
// absorb message into the state
for (&m, s) in message.iter().zip(state[Rpo256::RATE_RANGE].iter_mut()) {
*s = m;
// compute the norm squared of (s1, s2)
let length_squared_s1 = s1.norm_squared();
let length_squared_s2 = s2.norm_squared();
let length_squared = length_squared_s1 + length_squared_s2;
length_squared < SIG_L2_BOUND
}
/// Checks whether a set of coefficients is a valid one for a signature polynomial.
fn are_coefficients_valid(x: &[i16]) -> bool {
if x.len() != N {
return false;
}
// 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;
for &c in x {
if !(-2047..=2047).contains(&c) {
return false;
}
}
// 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);
// we can safely (without overflow) create a new Felt from u64 value here since this value
// contains at most 5 bytes
result[i] = Felt::new(u64::from_le_bytes(buffer));
}
result
true
}
// TESTS
// ================================================================================================
#[cfg(all(test, feature = "std"))]
#[cfg(test)]
mod tests {
use core::ffi::c_void;
use rand_utils::rand_vector;
use super::{
super::{ffi::*, KeyPair},
*,
};
// Wrappers for unsafe functions
impl Rpo128Context {
/// Initializes the RPO state.
pub fn init() -> Self {
let mut ctx = Rpo128Context { content: [0u64; 13] };
unsafe {
rpo128_init(&mut ctx as *mut Rpo128Context);
}
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::<alloc::vec::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());
}
use super::{super::SecretKey, *};
use rand::SeedableRng;
use rand_chacha::ChaCha20Rng;
#[test]
fn test_serialization_round_trip() {
let key = KeyPair::new().unwrap();
let signature = key.sign(Word::default()).unwrap();
let seed = [0_u8; 32];
let mut rng = ChaCha20Rng::from_seed(seed);
let sk = SecretKey::with_rng(&mut rng);
let signature = sk.sign_with_rng(Word::default(), &mut rng);
let serialized = signature.to_bytes();
let deserialized = Signature::read_from_bytes(&serialized).unwrap();
assert_eq!(signature.sig_poly(), deserialized.sig_poly());
assert_eq!(signature.pub_key_poly(), deserialized.pub_key_poly());
}
}