Skip to content

Commit

Permalink
Laying groundwork for signing.
Browse files Browse the repository at this point in the history
  • Loading branch information
xvzcf committed Jun 11, 2024
1 parent 382c403 commit d170ecf
Show file tree
Hide file tree
Showing 6 changed files with 212 additions and 26 deletions.
84 changes: 66 additions & 18 deletions libcrux-ml-dsa/src/arithmetic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,37 +72,74 @@ 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.
//
// This approach has been taken from:
// https://github.com/cloudflare/circl/blob/main/sign/dilithium/internal/common/field.go#L35
pub(crate) 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.
let t = t + ((t >> 31) & FIELD_MODULUS);

// Compute t mod 2ᵈ
// t0 is now one of 0, 1, ..., 2ᵈ⁻¹-1, 2ᵈ⁻¹, 2ᵈ⁻¹+1, ..., 2ᵈ-1
let mut t0 = t & ((1 << BITS_IN_LOWER_PART_OF_T) - 1);
// t0 = t - (2^{BITS_IN_LOWER_PART_OF_T} * t1)
// t1 = ⌊(t - 1)/2^{BITS_IN_LOWER_PART_OF_T} + 1/2⌋
//
// See Lemma 10 of the implementation notes document for more information
// on what these compute.
let t1 = (t - 1 + (1 << (BITS_IN_LOWER_PART_OF_T - 1))) >> BITS_IN_LOWER_PART_OF_T;
let t0 = t - (t1 << BITS_IN_LOWER_PART_OF_T);

// now t0 is -2ᵈ⁻¹-1, -2ᵈ⁻¹, ..., -2, -1, 0, ..., 2ᵈ⁻¹-2
t0 -= (1 << (BITS_IN_LOWER_PART_OF_T - 1)) + 1;
(t0, t1)
}

// Next, we add 2ᴰ to those t0 that are negative
// now a0 is 2ᵈ⁻¹-1, 2ᵈ⁻¹, ..., 2ᵈ-2, 2ᵈ-1, 0, ..., 2ᵈ⁻¹-2
t0 += (t0 >> 31) & (1 << BITS_IN_LOWER_PART_OF_T);
pub(crate) fn t0_to_unsigned_representative(t0: i32) -> i32 {
(1 << (BITS_IN_LOWER_PART_OF_T - 1)) - t0
}

// Splits 0 ≤ r < q into r₀ and r₁ such that:
//
// - r = r₁*α + r₀
// - -α/2 < r₀ ≤ α/2
//
// except when r₁ = (q-1)/α; in this case:
//
// - r₁ is set to 0 is taken
// - α/2 ≤ r₀ < 0.
//
// Note that 0 ≤ r₁ < (q-1)/α.
pub(crate) fn decompose<const ALPHA: i32>(r: i32) -> (i32, i32) {
let r1 = {
// Compute ⌈r / 128⌉
let ceil_of_r_by_128 = (r + 127) >> 7;

match ALPHA {
190_464 => {
// 1488/2²⁴ is an approximation of 1/1488
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
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.
result & 15
}
_ => unreachable!(),
}
};

// now t0 is 0, 1, 2, ..., 2ᵈ⁻¹-1, 2ᵈ⁻¹-1, -2ᵈ⁻¹-1, ...
// which is what we want.
t0 -= (1 << (BITS_IN_LOWER_PART_OF_T - 1)) - 1;
let mut r0 = r - (r1 * ALPHA);

let t1 = (t - t0) >> BITS_IN_LOWER_PART_OF_T;
// 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
// return a₀ + q, that comes down to adding q if a₀ < (q-1)/2.
r0 -= (((FIELD_MODULUS - 1) / 2 - r0) >> 31) & FIELD_MODULUS;

(t0, t1)
(r0, r1)
}

pub(crate) fn t0_to_unsigned_representative(t0: i32) -> i32 {
(1 << (BITS_IN_LOWER_PART_OF_T - 1)) - t0
pub(crate) fn make_hint<const GAMMA2: i32>(low: i32, high: i32) -> bool {
(low > GAMMA2) || (low < -GAMMA2) || (low == -GAMMA2 && high != 0)
}

#[cfg(test)]
Expand All @@ -124,4 +161,15 @@ mod tests {
assert_eq!(power2round(-1568816), (4049, 831));
assert_eq!(power2round(-4022142), (131, 532));
}

#[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::<523_776>(563751), (39975, 1));
assert_eq!(decompose::<523_776>(6645076), (-164012, 13));
assert_eq!(decompose::<523_776>(7806985), (-49655, 15));
}
}
12 changes: 6 additions & 6 deletions libcrux-ml-dsa/src/deserialize.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::arithmetic::PolynomialRingElement;

#[inline(always)]
fn deserialize_to_mask_when_gamma_is_2_pow_17(serialized: &[u8]) -> PolynomialRingElement {
fn deserialize_to_mask_when_gamma1_is_2_pow_17(serialized: &[u8]) -> PolynomialRingElement {
const GAMMA1: i32 = 1 << 17;
const GAMMA1_TIMES_2_BITMASK: i32 = (GAMMA1 << 1) - 1;

Expand Down Expand Up @@ -38,7 +38,7 @@ fn deserialize_to_mask_when_gamma_is_2_pow_17(serialized: &[u8]) -> PolynomialRi
}

#[inline(always)]
fn deserialize_to_mask_when_gamma_is_2_pow_19(serialized: &[u8]) -> PolynomialRingElement {
fn deserialize_to_mask_when_gamma1_is_2_pow_19(serialized: &[u8]) -> PolynomialRingElement {
const GAMMA1: i32 = 1 << 19;
const GAMMA1_TIMES_2_BITMASK: i32 = (GAMMA1 << 1) - 1;

Expand Down Expand Up @@ -66,8 +66,8 @@ pub(crate) fn deserialize_to_mask_ring_element<const GAMMA1_EXPONENT: usize>(
serialized: &[u8],
) -> PolynomialRingElement {
match GAMMA1_EXPONENT {
17 => deserialize_to_mask_when_gamma_is_2_pow_17(serialized),
19 => deserialize_to_mask_when_gamma_is_2_pow_19(serialized),
17 => deserialize_to_mask_when_gamma1_is_2_pow_17(serialized),
19 => deserialize_to_mask_when_gamma1_is_2_pow_19(serialized),
_ => unreachable!(),
}
}
Expand All @@ -77,7 +77,7 @@ mod tests {
use super::*;

#[test]
fn test_deserialize_to_mask_when_gamma_is_2_pow_17() {
fn test_deserialize_to_mask_when_gamma1_is_2_pow_17() {
let bytes = [
198, 32, 33, 79, 53, 132, 46, 198, 17, 233, 84, 94, 175, 136, 13, 127, 137, 254, 113,
82, 68, 239, 94, 176, 179, 22, 102, 177, 253, 142, 176, 250, 96, 201, 11, 213, 230, 41,
Expand Down Expand Up @@ -146,7 +146,7 @@ mod tests {
}

#[test]
fn test_deserialize_to_mask_when_gamma_is_2_pow_19() {
fn test_deserialize_to_mask_when_gamma1_is_2_pow_19() {
let bytes: [u8; 640] = [
253, 11, 216, 60, 251, 71, 79, 187, 242, 250, 209, 44, 72, 206, 98, 3, 22, 91, 184, 22,
197, 50, 249, 184, 253, 104, 8, 3, 9, 116, 147, 157, 110, 167, 67, 218, 30, 79, 58, 12,
Expand Down
2 changes: 2 additions & 0 deletions libcrux-ml-dsa/src/ml_dsa_65.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ const BYTES_FOR_ERROR_RING_ELEMENT: usize =
// GAMMA - v. This can be done in 20 bits when GAMMA is 2^{19}.
const BITS_PER_MASK_COEFFICIENT: usize = 20;

const MAX_NUMBER_OF_ONES_IN_HINT: usize = 55;

const BYTES_PER_MASK_RING_ELEMENT: usize =
(BITS_PER_MASK_COEFFICIENT * COEFFICIENTS_IN_RING_ELEMENT) / 8;

Expand Down
26 changes: 26 additions & 0 deletions libcrux-ml-dsa/src/ml_dsa_generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,29 @@ pub(crate) fn generate_key_pair<

(signing_key_serialized, verification_key_serialized)
}

#[allow(non_snake_case)]
pub(crate) fn sign<
const ROWS_IN_A: usize,
const COLUMNS_IN_A: usize,
const BYTES_FOR_ERROR_RING_ELEMENT: usize,
const SIGNING_KEY_SIZE: usize,
const SIGNATURE_SIZE: usize,
>(
signing_key: [u8; SIGNING_KEY_SIZE],
message: &[u8],
randomness: [u8; 32],
) -> [u8; SIGNATURE_SIZE] {
let (rho, remaining_signing_key) = signing_key.split_at(SEED_FOR_A_SIZE);
let (seed_for_signing, remaining_signing_key) =
remaining_signing_key.split_at(SEED_FOR_SIGNING_SIZE);
let (verification_key_hash, remaining_signing_key) =
remaining_signing_key.split_at(BYTES_FOR_VERIFICATION_KEY_HASH);

let (s1_serialized, remaining_signing_key) =
remaining_signing_key.split_at(BYTES_FOR_ERROR_RING_ELEMENT);
let (s2_serializd, t0_serialized) =
remaining_signing_key.split_at(BYTES_FOR_ERROR_RING_ELEMENT);

todo!();
}
6 changes: 4 additions & 2 deletions libcrux-ml-dsa/src/sample.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ pub(crate) fn sample_challenge_ring_element<const TAU: usize>(
seed: [u8; 32],
) -> PolynomialRingElement {
// TODO: Use incremental API to squeeze one block at a time.
let mut randomness = H::<544>(&seed).into_iter();
let mut randomness = H::<136>(&seed).into_iter();

let mut signs: u64 = 0;
for i in 0..8 {
Expand All @@ -175,11 +175,13 @@ pub(crate) fn sample_challenge_ring_element<const TAU: usize>(
let mut out = PolynomialRingElement::ZERO;

for index in (out.coefficients.len() - TAU)..out.coefficients.len() {
// TODO: Rewrite this without using `break`. It's doable, just probably
// not as nice.
let sample_at = loop {
let i = match randomness.next() {
Some(byte) => byte as usize,

// TODO: We need to re-sample here instead of panicking.
// TODO: We need to incrementally sample here instead of panicking.
None => panic!("Insufficient randomness to sample challenge ring element."),
};

Expand Down
108 changes: 108 additions & 0 deletions libcrux-ml-dsa/src/serialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,48 @@ pub(crate) fn serialize_error_ring_element<const ETA: usize, const BYTES_FOR_OUT
}
}

pub(crate) fn serialize_ring_element_w1<const GAMMA2: usize, const BYTES_FOR_OUTPUT: usize>(
re: PolynomialRingElement,
) -> [u8; BYTES_FOR_OUTPUT] {
let mut out = [0u8; BYTES_FOR_OUTPUT];

match GAMMA2 {
// 95,232 = (FIELD_MODULUS - 1) / 88
95_232 => {
// w1 has coefficients in [0,43] => each coefficient occupies
// 6 bits.
for (i, coefficients) in re.coefficients.chunks_exact(4).enumerate() {
let coefficient0 = coefficients[0] as u8;
let coefficient1 = coefficients[1] as u8;
let coefficient2 = coefficients[2] as u8;
let coefficient3 = coefficients[3] as u8;

out[3 * i + 0] = (coefficient1 << 6) | coefficient0;
out[3 * i + 1] = (coefficient2 << 4) | coefficient1 >> 2;
out[3 * i + 2] = (coefficient3 << 2) | coefficient2 >> 4;
}

out
}

// 261,888 = (FIELD_MODULUS - 1) / 32
261_888 => {
// w1 has coefficients in [0,15] => each coefficient occupies
// 4 bits.
for (i, coefficients) in re.coefficients.chunks_exact(2).enumerate() {
let coefficient0 = coefficients[0] as u8;
let coefficient1 = coefficients[1] as u8;

out[i] = (coefficient1 << 4) | coefficient0;
}

out
}

_ => unreachable!(),
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -233,4 +275,70 @@ mod tests {

assert_eq!(serialize_ring_element_of_t0s(re), expected_re_serialized);
}

#[test]
fn test_serialize_ring_element_w1() {
// Test serialization when GAMMA2 = 95_232
let re = PolynomialRingElement {
coefficients: [
42, 38, 3, 37, 37, 40, 2, 36, 11, 43, 37, 40, 1, 39, 20, 41, 38, 24, 41, 32, 7, 10,
21, 21, 25, 11, 21, 22, 33, 43, 8, 11, 20, 23, 24, 30, 22, 42, 11, 37, 31, 39, 9,
22, 27, 14, 39, 11, 3, 17, 25, 17, 17, 20, 32, 43, 17, 20, 23, 2, 38, 19, 16, 14,
38, 34, 35, 8, 39, 12, 9, 4, 4, 1, 21, 37, 22, 10, 20, 3, 36, 1, 42, 39, 18, 17, 3,
1, 38, 1, 5, 20, 0, 21, 39, 20, 10, 42, 10, 26, 6, 22, 12, 1, 20, 1, 43, 37, 33,
37, 6, 24, 32, 8, 42, 2, 32, 16, 13, 3, 33, 2, 0, 29, 4, 3, 23, 36, 6, 42, 1, 37,
7, 3, 12, 36, 19, 41, 42, 20, 36, 12, 11, 39, 23, 35, 29, 9, 31, 11, 19, 11, 14, 1,
32, 5, 6, 31, 4, 30, 8, 24, 22, 39, 8, 10, 26, 11, 25, 10, 36, 17, 43, 25, 20, 2,
37, 11, 21, 4, 24, 25, 5, 26, 29, 39, 3, 10, 8, 15, 40, 28, 26, 4, 30, 42, 14, 17,
41, 27, 8, 19, 19, 0, 3, 5, 41, 34, 39, 14, 1, 39, 9, 10, 41, 12, 24, 16, 2, 5, 33,
27, 27, 32, 4, 3, 9, 5, 37, 40, 38, 43, 32, 27, 34, 27, 15, 24, 4, 2, 42, 15, 9, 3,
17, 35, 0, 22, 43, 13, 15, 6, 38, 10, 20, 37,
],
};

let serialized = [
170, 57, 148, 37, 42, 144, 203, 90, 162, 193, 73, 165, 38, 150, 130, 135, 82, 85, 217,
82, 89, 225, 138, 44, 212, 133, 121, 150, 186, 148, 223, 153, 88, 155, 115, 46, 67,
148, 69, 17, 5, 174, 17, 117, 9, 230, 4, 57, 166, 56, 34, 39, 147, 16, 68, 80, 149,
150, 66, 13, 100, 160, 158, 82, 52, 4, 102, 80, 80, 64, 117, 82, 138, 170, 104, 134,
197, 4, 84, 176, 150, 97, 105, 96, 32, 162, 10, 32, 212, 12, 161, 0, 116, 196, 112,
145, 134, 26, 148, 199, 192, 144, 83, 170, 82, 36, 179, 156, 215, 216, 37, 223, 50, 45,
78, 0, 22, 198, 71, 120, 8, 102, 157, 136, 162, 45, 153, 66, 70, 107, 70, 9, 229, 82,
17, 88, 86, 104, 221, 57, 40, 200, 131, 114, 26, 225, 169, 78, 148, 110, 200, 52, 1,
67, 145, 138, 167, 19, 156, 137, 146, 50, 24, 36, 20, 225, 182, 129, 196, 144, 20, 37,
106, 174, 224, 38, 110, 15, 70, 8, 234, 147, 12, 209, 8, 88, 107, 243, 24, 166, 66,
149,
];

assert_eq!(serialize_ring_element_w1::<95_232, 192>(re), serialized);

// Test serialization when GAMMA2 = 261,888
let re = PolynomialRingElement {
coefficients: [
2, 4, 8, 3, 14, 3, 10, 7, 4, 15, 13, 3, 1, 2, 9, 12, 8, 11, 12, 4, 7, 14, 9, 4, 4,
2, 5, 15, 14, 11, 6, 11, 10, 13, 3, 13, 9, 15, 10, 8, 14, 4, 8, 11, 11, 10, 13, 8,
4, 9, 3, 8, 8, 3, 4, 5, 14, 9, 13, 12, 0, 4, 4, 2, 9, 11, 7, 11, 9, 14, 1, 7, 13,
12, 0, 15, 14, 8, 6, 15, 15, 7, 11, 1, 11, 2, 4, 11, 10, 3, 15, 6, 7, 3, 1, 12, 0,
15, 7, 13, 13, 1, 9, 14, 3, 5, 0, 8, 5, 7, 5, 8, 10, 13, 13, 11, 11, 13, 1, 4, 10,
14, 15, 14, 12, 6, 13, 1, 7, 7, 15, 4, 2, 5, 6, 2, 7, 14, 2, 2, 4, 11, 7, 1, 5, 8,
9, 5, 4, 13, 8, 8, 13, 13, 15, 5, 6, 11, 11, 4, 13, 7, 11, 15, 15, 3, 12, 4, 12,
14, 2, 6, 9, 10, 6, 13, 15, 12, 11, 12, 2, 7, 6, 9, 9, 5, 6, 3, 4, 2, 8, 3, 10, 2,
8, 1, 13, 10, 12, 8, 14, 0, 5, 12, 5, 3, 7, 15, 12, 13, 3, 4, 10, 1, 13, 3, 9, 6,
10, 13, 4, 4, 2, 9, 0, 4, 5, 7, 14, 11, 2, 6, 3, 11, 6, 2, 0, 5, 8, 5, 9, 5, 9, 0,
2, 2, 3, 15, 0, 8, 11, 13, 2, 6, 11, 0,
],
};

let serialized = [
66, 56, 62, 122, 244, 61, 33, 201, 184, 76, 231, 73, 36, 245, 190, 182, 218, 211, 249,
138, 78, 184, 171, 141, 148, 131, 56, 84, 158, 205, 64, 36, 185, 183, 233, 113, 205,
240, 142, 246, 127, 27, 43, 180, 58, 111, 55, 193, 240, 215, 29, 233, 83, 128, 117,
133, 218, 189, 219, 65, 234, 239, 108, 29, 119, 79, 82, 38, 231, 34, 180, 23, 133, 89,
212, 136, 221, 95, 182, 75, 125, 251, 63, 76, 236, 98, 169, 214, 207, 203, 114, 150,
89, 54, 36, 56, 42, 24, 173, 140, 14, 197, 53, 247, 220, 67, 26, 61, 105, 218, 68, 146,
64, 117, 190, 98, 179, 38, 80, 88, 89, 9, 34, 243, 128, 219, 98, 11,
];

assert_eq!(serialize_ring_element_w1::<261_888, 128>(re), serialized);
}
}

0 comments on commit d170ecf

Please sign in to comment.