Skip to content

Commit

Permalink
Merge branch 'jonas/xyber768' into jonas/restore-verified-mlkem
Browse files Browse the repository at this point in the history
  • Loading branch information
jschneider-bensch authored Jun 21, 2024
2 parents cb11230 + 66dca4d commit 688b7fb
Show file tree
Hide file tree
Showing 25 changed files with 2,121 additions and 836 deletions.
183 changes: 160 additions & 23 deletions libcrux-ml-dsa/src/arithmetic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,74 @@ impl PolynomialRingElement {
// FIXME: hax issue, 256 is COEFFICIENTS_IN_RING_ELEMENT
coefficients: [0i32; 256],
};

#[inline(always)]
pub(crate) fn add(&self, rhs: &Self) -> Self {
let mut sum = Self::ZERO;

for i in 0..rhs.coefficients.len() {
sum.coefficients[i] = self.coefficients[i] + rhs.coefficients[i];
}

sum
}

#[inline(always)]
pub(crate) fn sub(&self, rhs: &Self) -> Self {
let mut difference = Self::ZERO;

for i in 0..rhs.coefficients.len() {
difference.coefficients[i] = self.coefficients[i] - rhs.coefficients[i];
}

difference
}

#[inline(always)]
pub(crate) fn infinity_norm_exceeds(&self, value: i32) -> bool {
if value > (FIELD_MODULUS - 1) / 8 {
return true;
}

let mut exceeds = false;

// It is ok to leak which coefficient violates the bound since
// the probability for each coefficient is independent of secret
// data but we must not leak the sign of the centralized representative.
//
// TODO: We can break out of this loop early if need be, but the most
// straightforward way to do so (returning false) will not go through hax;
// revisit if performance is impacted.
for coefficient in self.coefficients.iter() {
// Normalize the coefficient
let sign = coefficient >> 31;
let normalized = coefficient - (sign & (2 * coefficient));

exceeds |= normalized >= value;
}

exceeds
}
}

pub(crate) fn add_to_ring_element(
mut lhs: PolynomialRingElement,
rhs: &PolynomialRingElement,
) -> PolynomialRingElement {
for i in 0..lhs.coefficients.len() {
lhs.coefficients[i] += rhs.coefficients[i];
#[inline(always)]
pub(crate) fn vector_infinity_norm_exceeds<const DIMENSION: usize>(
vector: [PolynomialRingElement; DIMENSION],
value: i32,
) -> bool {
let mut exceeds = false;

// TODO: We can break out of this loop early if need be, but the most
// straightforward way to do so (returning false) will not go through hax;
// revisit if performance is impacted.
for i in 0..DIMENSION {
exceeds |= vector[i].infinity_norm_exceeds(value);
}

lhs
exceeds
}

#[inline(always)]
pub(crate) fn get_n_least_significant_bits(n: u8, value: u64) -> u64 {
value & ((1 << n) - 1)
}
Expand All @@ -43,6 +98,7 @@ pub(crate) type FieldElementTimesMontgomeryR = i32;
const MONTGOMERY_SHIFT: u8 = 32;
const INVERSE_OF_MODULUS_MOD_MONTGOMERY_R: u64 = 58_728_449; // FIELD_MODULUS^{-1} mod 2^32

#[inline(always)]
pub(crate) fn montgomery_reduce(value: i64) -> MontgomeryFieldElement {
let t = get_n_least_significant_bits(MONTGOMERY_SHIFT, value as u64)
* INVERSE_OF_MODULUS_MOD_MONTGOMERY_R;
Expand Down Expand Up @@ -72,7 +128,8 @@ pub(crate) fn montgomery_multiply_fe_by_fer(
//
// We assume the input t is in the signed representative range and convert it
// to the standard unsigned range.
pub(crate) fn power2round(t: i32) -> (i32, i32) {
#[inline(always)]
fn power2round(t: i32) -> (i32, i32) {
debug_assert!(t > -FIELD_MODULUS && t < FIELD_MODULUS, "t is {}", t);

// Convert the signed representative to the standard unsigned one.
Expand All @@ -89,11 +146,32 @@ pub(crate) fn power2round(t: i32) -> (i32, i32) {
(t0, t1)
}

pub(crate) fn t0_to_unsigned_representative(t0: i32) -> i32 {
(1 << (BITS_IN_LOWER_PART_OF_T - 1)) - t0
#[inline(always)]
pub(crate) fn power2round_vector<const DIMENSION: usize>(
t: [PolynomialRingElement; DIMENSION],
) -> (
[PolynomialRingElement; DIMENSION],
[PolynomialRingElement; DIMENSION],
) {
let mut vector_t0 = [PolynomialRingElement::ZERO; DIMENSION];
let mut vector_t1 = [PolynomialRingElement::ZERO; DIMENSION];

for i in 0..DIMENSION {
for (j, coefficient) in t[i].coefficients.into_iter().enumerate() {
let (c0, c1) = power2round(coefficient);

vector_t0[i].coefficients[j] = c0;
vector_t1[i].coefficients[j] = c1;
}
}

(vector_t0, vector_t1)
}

// Splits 0 ≤ r < q into r₀ and r₁ such that:
// Take a representative -q < r < q and convert it
// to the standard unsigned one in the interval [0, q).
//
// Splits this representative r into r₀ and r₁ such that:
//
// - r = r₁*α + r₀
// - -α/2 < r₀ ≤ α/2
Expand All @@ -104,21 +182,35 @@ pub(crate) fn t0_to_unsigned_representative(t0: i32) -> i32 {
// - α/2 ≤ r₀ < 0.
//
// Note that 0 ≤ r₁ < (q-1)/α.
pub(crate) fn decompose<const ALPHA: i32>(r: i32) -> (i32, i32) {
#[inline(always)]
fn decompose<const GAMMA2: i32>(r: i32) -> (i32, i32) {
debug_assert!(
r > -FIELD_MODULUS && r < FIELD_MODULUS,
"the representative is {}",
r
);

// Convert the signed representative to the standard unsigned one.
let r = r + ((r >> 31) & FIELD_MODULUS);

let alpha = GAMMA2 * 2;

let r1 = {
// Compute ⌈r / 128⌉
let ceil_of_r_by_128 = (r + 127) >> 7;

match ALPHA {
match alpha {
190_464 => {
// 1488/2²⁴ is an approximation of 1/1488
// We approximate 1 / 1488 as:
// ⌊2²⁴ / 1488⌋ / 2²⁴ = 11,275 / 2²⁴
let result = ((ceil_of_r_by_128 * 11_275) + (1 << 23)) >> 24;

// For the corner-case a₁ = (q-1)/α = 44, we have to set a₁=0.
(result ^ (43 - result) >> 31) & result
}
523_776 => {
// 1025/2²² is an approximation of 1/4092
// We approximate 1 / 4092 as:
// ⌊2²² / 4092⌋ / 2²² = 1025 / 2²²
let result = (ceil_of_r_by_128 * 1025 + (1 << 21)) >> 22;

// For the corner-case a₁ = (q-1)/α = 16, we have to set a₁=0.
Expand All @@ -128,7 +220,7 @@ pub(crate) fn decompose<const ALPHA: i32>(r: i32) -> (i32, i32) {
}
};

let mut r0 = r - (r1 * ALPHA);
let mut r0 = r - (r1 * alpha);

// In the corner-case, when we set a₁=0, we will incorrectly
// have a₀ > (q-1)/2 and we'll need to subtract q. As we
Expand All @@ -138,10 +230,55 @@ pub(crate) fn decompose<const ALPHA: i32>(r: i32) -> (i32, i32) {
(r0, r1)
}

pub(crate) fn make_hint<const GAMMA2: i32>(low: i32, high: i32) -> bool {
#[inline(always)]
pub(crate) fn decompose_vector<const DIMENSION: usize, const GAMMA2: i32>(
t: [PolynomialRingElement; DIMENSION],
) -> (
[PolynomialRingElement; DIMENSION],
[PolynomialRingElement; DIMENSION],
) {
let mut vector_low = [PolynomialRingElement::ZERO; DIMENSION];
let mut vector_high = [PolynomialRingElement::ZERO; DIMENSION];

for i in 0..DIMENSION {
for (j, coefficient) in t[i].coefficients.into_iter().enumerate() {
let (low, high) = decompose::<GAMMA2>(coefficient);

vector_low[i].coefficients[j] = low;
vector_high[i].coefficients[j] = high;
}
}

(vector_low, vector_high)
}

#[inline(always)]
fn make_hint<const GAMMA2: i32>(low: i32, high: i32) -> bool {
(low > GAMMA2) || (low < -GAMMA2) || (low == -GAMMA2 && high != 0)
}

#[inline(always)]
pub(crate) fn make_hint_vector<const DIMENSION: usize, const GAMMA2: i32>(
low: [PolynomialRingElement; DIMENSION],
high: [PolynomialRingElement; DIMENSION],
) -> ([[bool; COEFFICIENTS_IN_RING_ELEMENT]; DIMENSION], usize) {
let mut hint_vector = [[false; COEFFICIENTS_IN_RING_ELEMENT]; DIMENSION];
let mut hints_of_one = 0;

for i in 0..DIMENSION {
for j in 0..COEFFICIENTS_IN_RING_ELEMENT {
hint_vector[i][j] =
make_hint::<GAMMA2>(low[i].coefficients[j], high[i].coefficients[j]);

// From https://doc.rust-lang.org/std/primitive.bool.html:
// "If you cast a bool into an integer, true will be 1 and false will be 0."
hints_of_one += hint_vector[i][j] as usize;
}
}

(hint_vector, hints_of_one)
}

#[cfg(test)]
mod tests {
use super::*;
Expand All @@ -164,12 +301,12 @@ mod tests {

#[test]
fn test_decompose() {
assert_eq!(decompose::<190_464>(3574899), (-43917, 19));
assert_eq!(decompose::<190_464>(7368323), (-59773, 39));
assert_eq!(decompose::<190_464>(3640854), (22038, 19));
assert_eq!(decompose::<95_232>(3574899), (-43917, 19));
assert_eq!(decompose::<95_232>(7368323), (-59773, 39));
assert_eq!(decompose::<95_232>(3640854), (22038, 19));

assert_eq!(decompose::<523_776>(563751), (39975, 1));
assert_eq!(decompose::<523_776>(6645076), (-164012, 13));
assert_eq!(decompose::<523_776>(7806985), (-49655, 15));
assert_eq!(decompose::<261_888>(563751), (39975, 1));
assert_eq!(decompose::<261_888>(6645076), (-164012, 13));
assert_eq!(decompose::<261_888>(7806985), (-49655, 15));
}
}
12 changes: 10 additions & 2 deletions libcrux-ml-dsa/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,23 @@ pub(crate) const COEFFICIENTS_IN_RING_ELEMENT: usize = 256;
pub(crate) const FIELD_MODULUS_MINUS_ONE_BIT_LENGTH: usize = 23;

pub(crate) const BITS_IN_LOWER_PART_OF_T: usize = 13;
pub(crate) const BYTES_FOR_RING_ELEMENT_OF_T0S: usize =
pub(crate) const RING_ELEMENT_OF_T0S_SIZE: usize =
(BITS_IN_LOWER_PART_OF_T * COEFFICIENTS_IN_RING_ELEMENT) / 8;

pub(crate) const BITS_IN_UPPER_PART_OF_T: usize =
FIELD_MODULUS_MINUS_ONE_BIT_LENGTH - BITS_IN_LOWER_PART_OF_T;
pub(crate) const BYTES_FOR_RING_ELEMENT_OF_T1S: usize =
pub(crate) const RING_ELEMENT_OF_T1S_SIZE: usize =
(BITS_IN_UPPER_PART_OF_T * COEFFICIENTS_IN_RING_ELEMENT) / 8;

pub(crate) const SEED_FOR_A_SIZE: usize = 32;
pub(crate) const SEED_FOR_ERROR_VECTORS_SIZE: usize = 64;
pub(crate) const BYTES_FOR_VERIFICATION_KEY_HASH: usize = 64;
pub(crate) const SEED_FOR_SIGNING_SIZE: usize = 32;

pub(crate) const KEY_GENERATION_RANDOMNESS_SIZE: usize = 32;
pub(crate) const SIGNING_RANDOMNESS_SIZE: usize = 32;

pub(crate) const MESSAGE_REPRESENTATIVE_SIZE: usize = 64;
pub(crate) const MASK_SEED_SIZE: usize = 64;

pub(crate) const VERIFIER_CHALLENGE_SEED_SIZE: usize = 32;
5 changes: 5 additions & 0 deletions libcrux-ml-dsa/src/encoding.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pub(crate) mod commitment;
pub(crate) mod error;
pub(crate) mod gamma1;
pub(crate) mod t0;
pub(crate) mod t1;
Loading

0 comments on commit 688b7fb

Please sign in to comment.