diff --git a/libcrux-ml-dsa/src/arithmetic.rs b/libcrux-ml-dsa/src/arithmetic.rs index 6263884e2..661472d18 100644 --- a/libcrux-ml-dsa/src/arithmetic.rs +++ b/libcrux-ml-dsa/src/arithmetic.rs @@ -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(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(low: i32, high: i32) -> bool { + (low > GAMMA2) || (low < -GAMMA2) || (low == -GAMMA2 && high != 0) } #[cfg(test)] @@ -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)); + } } diff --git a/libcrux-ml-dsa/src/deserialize.rs b/libcrux-ml-dsa/src/deserialize.rs new file mode 100644 index 000000000..e15f8d625 --- /dev/null +++ b/libcrux-ml-dsa/src/deserialize.rs @@ -0,0 +1,221 @@ +use crate::arithmetic::PolynomialRingElement; + +#[inline(always)] +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; + + let mut re = PolynomialRingElement::ZERO; + + for (i, bytes) in serialized.chunks_exact(9).enumerate() { + re.coefficients[4 * i + 0] = bytes[0] as i32; + re.coefficients[4 * i + 0] |= (bytes[1] as i32) << 8; + re.coefficients[4 * i + 0] |= (bytes[2] as i32) << 16; + re.coefficients[4 * i + 0] &= GAMMA1_TIMES_2_BITMASK; + + re.coefficients[4 * i + 1] = (bytes[2] as i32) >> 2; + re.coefficients[4 * i + 1] |= (bytes[3] as i32) << 6; + re.coefficients[4 * i + 1] |= (bytes[4] as i32) << 14; + re.coefficients[4 * i + 1] &= GAMMA1_TIMES_2_BITMASK; + + re.coefficients[4 * i + 2] = (bytes[4] as i32) >> 4; + re.coefficients[4 * i + 2] |= (bytes[5] as i32) << 4; + re.coefficients[4 * i + 2] |= (bytes[6] as i32) << 12; + re.coefficients[4 * i + 2] &= GAMMA1_TIMES_2_BITMASK; + + re.coefficients[4 * i + 3] = (bytes[6] as i32) >> 6; + re.coefficients[4 * i + 3] |= (bytes[7] as i32) << 2; + re.coefficients[4 * i + 3] |= (bytes[8] as i32) << 10; + re.coefficients[4 * i + 3] &= GAMMA1_TIMES_2_BITMASK; + + re.coefficients[4 * i + 0] = GAMMA1 - re.coefficients[4 * i + 0]; + re.coefficients[4 * i + 1] = GAMMA1 - re.coefficients[4 * i + 1]; + re.coefficients[4 * i + 2] = GAMMA1 - re.coefficients[4 * i + 2]; + re.coefficients[4 * i + 3] = GAMMA1 - re.coefficients[4 * i + 3]; + } + + re +} + +#[inline(always)] +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; + + let mut re = PolynomialRingElement::ZERO; + + for (i, bytes) in serialized.chunks_exact(5).enumerate() { + re.coefficients[2 * i + 0] = bytes[0] as i32; + re.coefficients[2 * i + 0] |= (bytes[1] as i32) << 8; + re.coefficients[2 * i + 0] |= (bytes[2] as i32) << 16; + re.coefficients[2 * i + 0] &= GAMMA1_TIMES_2_BITMASK; + + re.coefficients[2 * i + 1] = (bytes[2] as i32) >> 4; + re.coefficients[2 * i + 1] |= (bytes[3] as i32) << 4; + re.coefficients[2 * i + 1] |= (bytes[4] as i32) << 12; + + re.coefficients[2 * i + 0] = GAMMA1 - re.coefficients[2 * i + 0]; + re.coefficients[2 * i + 1] = GAMMA1 - re.coefficients[2 * i + 1]; + } + + re +} + +#[inline(always)] +pub(crate) fn deserialize_to_mask_ring_element( + serialized: &[u8], +) -> PolynomialRingElement { + match GAMMA1_EXPONENT { + 17 => deserialize_to_mask_when_gamma1_is_2_pow_17(serialized), + 19 => deserialize_to_mask_when_gamma1_is_2_pow_19(serialized), + _ => unreachable!(), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + 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, + 207, 14, 252, 247, 44, 197, 61, 57, 233, 239, 7, 173, 48, 253, 53, 43, 107, 174, 112, + 33, 144, 137, 117, 234, 75, 181, 150, 72, 158, 193, 130, 225, 136, 17, 65, 227, 146, + 207, 208, 228, 176, 164, 158, 62, 142, 193, 250, 109, 210, 52, 182, 254, 148, 179, 247, + 164, 167, 177, 209, 148, 189, 86, 221, 208, 92, 28, 51, 228, 176, 249, 12, 142, 124, + 187, 37, 164, 131, 203, 222, 228, 211, 250, 222, 114, 123, 183, 44, 125, 14, 97, 12, + 64, 154, 168, 11, 96, 112, 93, 225, 58, 8, 110, 164, 69, 246, 67, 102, 109, 227, 41, + 170, 254, 3, 137, 7, 0, 7, 12, 89, 123, 133, 155, 52, 14, 80, 211, 96, 229, 7, 100, + 208, 68, 109, 222, 122, 84, 80, 163, 240, 121, 126, 235, 54, 72, 124, 163, 195, 30, + 207, 194, 158, 106, 46, 181, 90, 251, 232, 201, 121, 115, 110, 225, 245, 38, 111, 109, + 248, 202, 9, 161, 220, 240, 202, 155, 72, 236, 80, 97, 168, 67, 128, 160, 37, 56, 211, + 167, 71, 73, 215, 92, 101, 148, 227, 207, 180, 155, 233, 42, 144, 30, 28, 236, 184, 13, + 133, 206, 47, 170, 205, 59, 29, 209, 245, 226, 66, 69, 144, 146, 168, 83, 66, 233, 193, + 59, 79, 41, 167, 246, 246, 95, 161, 50, 105, 109, 255, 137, 188, 210, 189, 142, 91, 73, + 139, 24, 228, 30, 36, 133, 202, 123, 206, 244, 9, 229, 227, 255, 94, 198, 149, 5, 193, + 37, 72, 129, 16, 205, 245, 177, 242, 241, 120, 66, 137, 39, 20, 111, 197, 64, 89, 9, + 238, 114, 4, 212, 146, 75, 206, 58, 232, 33, 231, 186, 90, 202, 95, 49, 233, 209, 177, + 195, 88, 253, 80, 103, 112, 163, 245, 31, 6, 78, 119, 131, 17, 240, 77, 210, 72, 61, 1, + 104, 110, 70, 49, 83, 172, 187, 39, 53, 235, 40, 4, 19, 170, 110, 153, 249, 52, 6, 122, + 225, 235, 40, 196, 149, 80, 184, 69, 148, 61, 158, 255, 145, 72, 129, 51, 16, 2, 196, + 156, 146, 128, 107, 76, 122, 194, 42, 99, 240, 12, 213, 57, 55, 232, 145, 61, 45, 160, + 136, 168, 6, 128, 210, 250, 114, 174, 126, 230, 8, 228, 207, 143, 146, 135, 161, 201, + 156, 182, 241, 219, 217, 69, 27, 35, 156, 194, 91, 192, 115, 201, 22, 197, 145, 240, + 132, 11, 206, 138, 128, 139, 222, 212, 13, 212, 22, 92, 232, 216, 16, 69, 109, 55, 230, + 217, 210, 95, 192, 242, 193, 250, 91, 217, 140, 11, 98, 111, 8, 117, 91, 212, 63, 24, + 93, 161, 48, 241, 55, 11, 5, 171, 199, 212, 161, 230, 156, 205, 103, 171, 11, 226, 125, + 213, 216, 76, 17, 229, 56, 203, 30, 43, 39, 212, 232, 96, 198, 217, 109, 81, 125, 22, + 180, 195, 249, 20, 18, 57, 153, 132, 63, 206, 40, 137, 240, 149, 189, 220, 75, 227, + 142, 178, 214, 215, 101, 232, 204, 94, 189, 109, 236, 173, 200, 39, 114, 203, 136, 152, + 114, 173, 199, 38, 195, 46, 224, 68, 92, 129, 184, 157, 35, + ]; + + let expected_coefficients = [ + 57146, 44088, -59459, 112872, -21737, -11223, -127192, -129573, 109967, -113617, + -80645, 26534, -64945, -44067, 92657, -87087, -76262, -66483, -53119, 67820, -125241, + -82427, -119562, 86825, 86421, 128932, -88217, 53335, 92491, 104558, -6188, 113117, + -58177, 117788, -69197, -31378, 29122, -97968, -85286, -129752, -111508, 5827, 58598, + -63059, 74410, -71476, -17201, -124611, 94708, 37153, 116158, -97070, -54244, 84034, + -96183, 2894, 106226, -36867, 83319, 16000, -57693, -98830, 107962, 61479, -93542, + -35448, 114710, 123356, 129280, -54851, 18345, 116526, 76976, 1704, 63936, 19181, + 99618, 76779, -106250, -110073, 112586, 71457, 69140, -31499, 53654, -54957, 90481, + 12825, 7826, -117181, -100054, 121045, 74591, -62140, -50313, 31421, 113752, 38880, + 52350, 57697, 75959, 59049, 65991, -28371, 120087, -67492, -102081, -5174, -12238, + -62314, 60973, -101335, 113342, -9380, 121542, -67493, 45253, 22070, 145, 79227, + -93545, -74367, -122155, 37318, 95415, -112902, 110015, 4310, 2866, 67262, 4098, + -22297, 16123, 110071, 77560, -51159, 69134, -20638, 48520, -71100, 42688, 83070, + 49081, 53685, 116018, 14214, 21586, 32983, 5839, 70540, -120204, 25277, 23696, 30723, + -95456, 113139, -19952, -86580, -32787, 58951, 109775, 4373, -45906, 126813, -43539, + -26203, 105649, -99816, 120597, 121487, 107643, 68015, 98, 110044, 64712, -69640, + 93540, -72416, 120924, 29525, 62224, 12683, 57725, 84746, 96096, 130646, -109864, + -47563, 72066, -129282, 55044, -34334, -40137, -64621, 107107, 95123, -115356, 69610, + 37737, -18196, -99568, -45954, 83960, -86906, -54285, -5893, 62066, 19180, 6601, + -128182, -76805, -125703, 75429, 97565, 96522, 37420, 114732, 108730, -70410, 119585, + -109317, 101071, 12694, 24778, -2987, 41096, 78451, -103493, -52024, 13625, -36162, + -72067, 37415, 24748, 115903, 109593, 50926, -123174, -36067, -115236, 82539, 77065, + -76014, -89946, 71579, -87987, -50907, -74423, -94759, -8754, -55081, 91362, 119101, + -69944, -100373, 94602, + ]; + + assert_eq!( + deserialize_to_mask_ring_element::<17>(&bytes).coefficients, + expected_coefficients + ); + } + + #[test] + 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, + 2, 72, 239, 104, 6, 162, 57, 195, 128, 18, 93, 245, 0, 132, 218, 172, 178, 214, 243, + 53, 171, 128, 90, 13, 126, 226, 148, 153, 238, 106, 146, 46, 220, 184, 140, 28, 167, + 18, 38, 212, 17, 6, 136, 251, 94, 47, 164, 196, 66, 120, 204, 45, 111, 37, 45, 51, 38, + 109, 32, 32, 144, 122, 13, 52, 144, 108, 75, 152, 73, 164, 139, 91, 26, 37, 76, 237, + 211, 47, 124, 0, 210, 175, 145, 149, 28, 19, 81, 38, 3, 121, 106, 191, 144, 129, 93, + 118, 202, 8, 163, 27, 182, 42, 148, 249, 166, 67, 198, 69, 164, 49, 157, 40, 230, 39, + 126, 108, 93, 96, 211, 185, 61, 99, 30, 83, 183, 241, 30, 16, 91, 76, 200, 55, 228, 22, + 33, 142, 114, 240, 217, 138, 155, 223, 136, 77, 216, 181, 102, 56, 218, 49, 91, 223, + 67, 68, 190, 216, 214, 180, 230, 199, 165, 17, 171, 151, 156, 33, 125, 248, 0, 56, 104, + 184, 150, 91, 83, 138, 61, 162, 255, 96, 168, 189, 86, 60, 76, 36, 163, 207, 76, 227, + 76, 180, 145, 125, 229, 251, 212, 77, 115, 88, 177, 134, 20, 122, 27, 211, 207, 254, + 233, 226, 31, 112, 10, 181, 117, 97, 56, 188, 176, 229, 156, 140, 97, 31, 64, 139, 249, + 217, 172, 100, 70, 121, 130, 94, 182, 245, 239, 138, 4, 65, 64, 228, 118, 200, 128, 94, + 143, 59, 53, 12, 185, 209, 191, 52, 91, 170, 161, 200, 12, 223, 221, 54, 151, 218, 3, + 156, 49, 176, 78, 50, 117, 16, 36, 179, 203, 91, 222, 181, 53, 151, 211, 229, 22, 49, + 247, 223, 195, 241, 1, 44, 157, 56, 48, 158, 25, 246, 231, 54, 106, 197, 107, 199, 252, + 60, 182, 216, 27, 129, 32, 149, 8, 239, 44, 176, 119, 104, 207, 77, 206, 150, 220, 18, + 172, 54, 140, 37, 235, 243, 23, 220, 149, 241, 197, 149, 240, 41, 223, 179, 98, 188, + 135, 231, 56, 176, 102, 173, 39, 46, 236, 79, 177, 224, 17, 164, 88, 227, 108, 214, + 234, 106, 253, 242, 27, 120, 44, 44, 63, 117, 135, 97, 90, 239, 81, 138, 112, 203, 188, + 13, 239, 224, 37, 53, 1, 27, 33, 26, 213, 36, 129, 146, 254, 82, 106, 111, 179, 25, + 199, 217, 243, 188, 250, 141, 136, 148, 154, 241, 152, 195, 225, 82, 174, 149, 124, + 237, 3, 81, 218, 90, 157, 6, 243, 34, 62, 141, 211, 164, 2, 103, 45, 46, 253, 115, 244, + 216, 191, 245, 177, 121, 216, 86, 131, 66, 63, 18, 167, 41, 199, 241, 195, 117, 168, + 134, 193, 73, 201, 83, 197, 85, 147, 217, 45, 162, 18, 203, 166, 95, 166, 159, 8, 1, + 110, 125, 113, 228, 180, 78, 194, 174, 60, 172, 124, 151, 23, 202, 247, 189, 206, 204, + 101, 51, 35, 8, 196, 85, 237, 64, 222, 81, 143, 182, 205, 105, 110, 173, 197, 239, 196, + 5, 108, 128, 248, 191, 247, 43, 25, 180, 246, 154, 125, 142, 227, 246, 17, 2, 207, 193, + 89, 244, 159, 82, 218, 117, 78, 191, 40, 49, 154, 160, 83, 246, 93, 94, 52, 85, 45, + 140, 99, 40, 23, 179, 141, 10, 143, 62, 176, 84, 19, 94, 79, 72, 58, 138, 7, 87, 196, + 2, 87, 0, 191, 226, 2, 224, 187, 150, 199, 217, 211, 51, 114, 228, 71, 54, 23, 17, 9, + 212, 195, 125, 236, 213, 254, 189, 203, 232, 161, 50, 81, 174, 129, 117, + ]; + + let expected_coefficients = [ + -3069, -504781, -216903, -503595, -11473, 119580, -202243, 431227, -78533, -514959, + 325528, 49008, -433555, 247178, -466650, 474204, -477186, 498034, 312926, 448500, + 461475, -370752, 85332, 303299, -164011, 7979, -103650, 86295, -274066, -52109, 350436, + -344673, -1553, 135240, 220113, 31700, -470476, 339370, -337459, 392698, -359056, + -66368, -19308, -148633, -154507, 212399, -513005, 522302, 413742, 407207, 110317, + 28622, 475286, 141287, -51830, 411088, 251210, -159641, 145853, 320956, 120675, 7554, + 500372, -236854, -418621, -226609, 516367, 211535, 247864, 388754, 494962, -44447, + -57243, -361688, -26293, 320093, 270501, -255044, 207144, -294507, -201125, -117114, + -32033, 294897, 83864, 182855, 377462, 126982, 82520, 212027, -500516, -406732, 412596, + -415705, -382203, 161996, 227663, 411743, -446419, -405151, -159775, 42160, -276577, + -416523, 422756, 261642, -129419, 111923, 362170, -222696, -192501, 257976, 72640, + -3207, -233310, 474285, -512441, 150709, -41386, -389324, 51491, 508503, 511588, + 318229, 257931, -310066, 139685, -95067, 72237, -488209, 408609, 344033, 509795, + 419357, 71690, -284323, -313195, -222159, 451624, -86536, -323336, 34046, -380776, + -93412, -266972, -50026, 267483, -377215, 134763, -461148, 270551, -247339, -59271, + 103677, -403373, 196926, 401231, 161215, 103197, 86355, -258813, 342143, 180436, + 124809, 397478, 63323, -376011, -397040, 445147, 388688, 207590, -75794, -152318, + -210678, -116505, -249661, -36346, -108872, 288527, 184804, -300462, 508201, -186961, + 497195, -402163, -342227, 64860, 335146, 232451, -261519, -111093, 168569, -475779, + -160035, 407767, 41921, 424280, -300188, 146093, -366901, 351699, -158897, -501343, + 520055, 426642, -216647, -442958, -181194, 26756, -490657, -315069, 313764, 260061, + -447836, 401856, -223477, -420301, -285398, 146193, -1728, 16392, 421185, -194228, + -59353, 395549, -323617, 239167, 185857, -423386, 357388, 484815, -484666, 237987, + 338605, -25484, -209266, -461453, -197608, -398164, 228107, 30150, -279920, 502014, + -404464, -253954, -293227, 273447, -411427, 51641, 487151, -377812, -351943, -245246, + -138892, -414002, 42982, + ]; + + assert_eq!( + deserialize_to_mask_ring_element::<19>(&bytes).coefficients, + expected_coefficients + ); + } +} diff --git a/libcrux-ml-dsa/src/lib.rs b/libcrux-ml-dsa/src/lib.rs index 7f7fe2db9..32e111662 100644 --- a/libcrux-ml-dsa/src/lib.rs +++ b/libcrux-ml-dsa/src/lib.rs @@ -1,5 +1,6 @@ mod arithmetic; mod constants; +mod deserialize; mod hash_functions; mod matrix; mod ml_dsa_generic; diff --git a/libcrux-ml-dsa/src/matrix.rs b/libcrux-ml-dsa/src/matrix.rs index 33451c0fe..16d5a8fe5 100644 --- a/libcrux-ml-dsa/src/matrix.rs +++ b/libcrux-ml-dsa/src/matrix.rs @@ -1,7 +1,7 @@ use crate::{ arithmetic::{add_to_ring_element, power2round, PolynomialRingElement}, ntt::{invert_ntt_montgomery, ntt, ntt_multiply_montgomery}, - sample::{sample_error_ring_element_uniform, sample_ring_element_uniform}, + sample::{sample_error_ring_element, sample_mask_ring_element, sample_ring_element_uniform}, }; pub(crate) fn power2round_vector( @@ -36,7 +36,25 @@ pub(crate) fn sample_error_vector( seed[65] = (*domain_separator >> 8) as u8; *domain_separator += 1; - error[i] = sample_error_ring_element_uniform::(seed); + error[i] = sample_error_ring_element::(seed); + } + + error +} + +#[inline(always)] +pub(crate) fn sample_mask_vector( + mut seed: [u8; 66], + signing_attempt_counter: u16, +) -> [PolynomialRingElement; COLUMNS_IN_A] { + let mut error = [PolynomialRingElement::ZERO; COLUMNS_IN_A]; + + for i in 0..COLUMNS_IN_A { + let domain_separator = signing_attempt_counter + (i as u16); + seed[64] = domain_separator as u8; + seed[65] = (domain_separator >> 8) as u8; + + error[i] = sample_mask_ring_element::(seed); } error diff --git a/libcrux-ml-dsa/src/ml_dsa_44.rs b/libcrux-ml-dsa/src/ml_dsa_44.rs index b251b4eb0..70096d8cc 100644 --- a/libcrux-ml-dsa/src/ml_dsa_44.rs +++ b/libcrux-ml-dsa/src/ml_dsa_44.rs @@ -6,10 +6,10 @@ const ROWS_IN_A: usize = 4; const COLUMNS_IN_A: usize = 4; const ETA: usize = 2; -const TWO_TIMES_ETA_BIT_SIZE: usize = 3; // ⌊log_2(2 * 2)⌋ + 1 +const BITS_PER_ERROR_COEFFICIENT: usize = 3; const BYTES_FOR_ERROR_RING_ELEMENT: usize = - (TWO_TIMES_ETA_BIT_SIZE * COEFFICIENTS_IN_RING_ELEMENT) / 8; + (BITS_PER_ERROR_COEFFICIENT * COEFFICIENTS_IN_RING_ELEMENT) / 8; const VERIFICATION_KEY_SIZE: usize = SEED_FOR_A_SIZE + (COEFFICIENTS_IN_RING_ELEMENT diff --git a/libcrux-ml-dsa/src/ml_dsa_65.rs b/libcrux-ml-dsa/src/ml_dsa_65.rs index 60bc95dfd..9f0049e04 100644 --- a/libcrux-ml-dsa/src/ml_dsa_65.rs +++ b/libcrux-ml-dsa/src/ml_dsa_65.rs @@ -6,10 +6,25 @@ const ROWS_IN_A: usize = 6; const COLUMNS_IN_A: usize = 5; const ETA: usize = 4; -const TWO_TIMES_ETA_BIT_SIZE: usize = 4; // ⌊log_2(2 * 4)⌋ + 1 +const GAMMA1_EXPONENT: usize = 19; + +// To sample a value in the interval [-ETA, ETA], we can sample a value (say 'v') +// in the interval [0, 2 * ETA] and then compute ETA - v. This can be done in +// 4 bits when ETA is 4. +const BITS_PER_ERROR_COEFFICIENT: usize = 4; const BYTES_FOR_ERROR_RING_ELEMENT: usize = - (TWO_TIMES_ETA_BIT_SIZE * COEFFICIENTS_IN_RING_ELEMENT) / 8; + (BITS_PER_ERROR_COEFFICIENT * COEFFICIENTS_IN_RING_ELEMENT) / 8; + +// To sample a value in the interval [-(GAMMA - 1), GAMMA], we can sample a +// value (say 'v') in the interval [0, (2 * GAMMA) - 1] and then compute +// 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; const VERIFICATION_KEY_SIZE: usize = SEED_FOR_A_SIZE + (COEFFICIENTS_IN_RING_ELEMENT diff --git a/libcrux-ml-dsa/src/ml_dsa_87.rs b/libcrux-ml-dsa/src/ml_dsa_87.rs index 1778ca333..34b9e63fc 100644 --- a/libcrux-ml-dsa/src/ml_dsa_87.rs +++ b/libcrux-ml-dsa/src/ml_dsa_87.rs @@ -6,10 +6,10 @@ const ROWS_IN_A: usize = 8; const COLUMNS_IN_A: usize = 7; const ETA: usize = 2; -const TWO_TIMES_ETA_BIT_SIZE: usize = 3; // ⌊log_2(2 * 2)⌋ + 1 +const BITS_PER_ERROR_COEFFICIENT: usize = 3; const BYTES_FOR_ERROR_RING_ELEMENT: usize = - (TWO_TIMES_ETA_BIT_SIZE * COEFFICIENTS_IN_RING_ELEMENT) / 8; + (BITS_PER_ERROR_COEFFICIENT * COEFFICIENTS_IN_RING_ELEMENT) / 8; const VERIFICATION_KEY_SIZE: usize = SEED_FOR_A_SIZE + (COEFFICIENTS_IN_RING_ELEMENT diff --git a/libcrux-ml-dsa/src/ml_dsa_generic.rs b/libcrux-ml-dsa/src/ml_dsa_generic.rs index 14bf832e1..04f63e328 100644 --- a/libcrux-ml-dsa/src/ml_dsa_generic.rs +++ b/libcrux-ml-dsa/src/ml_dsa_generic.rs @@ -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!(); +} diff --git a/libcrux-ml-dsa/src/notes.md b/libcrux-ml-dsa/src/notes.md new file mode 100644 index 000000000..5d4f5f5d0 --- /dev/null +++ b/libcrux-ml-dsa/src/notes.md @@ -0,0 +1,4 @@ +The constants and arrays used in the unit tests in this directory have been +generated by printing the inputs and outputs produced by the corresponding +functions from the `ref` version under the `standard` branch of the +[PQ-Crystals implementation](https://github.com/pq-crystals/dilithium) of ML-DSA. diff --git a/libcrux-ml-dsa/src/sample.rs b/libcrux-ml-dsa/src/sample.rs index 579b19e87..44d72057b 100644 --- a/libcrux-ml-dsa/src/sample.rs +++ b/libcrux-ml-dsa/src/sample.rs @@ -1,6 +1,7 @@ use crate::{ arithmetic::PolynomialRingElement, constants::{COEFFICIENTS_IN_RING_ELEMENT, FIELD_MODULUS}, + deserialize::deserialize_to_mask_ring_element, hash_functions::{H, H_128}, }; @@ -133,9 +134,7 @@ pub(crate) fn rejection_sample_less_than_eta( } #[allow(non_snake_case)] -pub(crate) fn sample_error_ring_element_uniform( - seed: [u8; 66], -) -> PolynomialRingElement { +pub(crate) fn sample_error_ring_element(seed: [u8; 66]) -> PolynomialRingElement { // TODO: Use incremental API to squeeze one block at a time. let randomness = H::<272>(&seed); @@ -152,6 +151,53 @@ pub(crate) fn sample_error_ring_element_uniform( out } +pub(crate) fn sample_mask_ring_element( + seed: [u8; 66], +) -> PolynomialRingElement { + match GAMMA1_EXPONENT { + 17 => deserialize_to_mask_ring_element::(&H::<576>(&seed)), + 19 => deserialize_to_mask_ring_element::(&H::<640>(&seed)), + _ => unreachable!(), + } +} + +pub(crate) fn sample_challenge_ring_element( + seed: [u8; 32], +) -> PolynomialRingElement { + // TODO: Use incremental API to squeeze one block at a time. + let mut randomness = H::<136>(&seed).into_iter(); + + let mut signs: u64 = 0; + for i in 0..8 { + signs |= (randomness.next().unwrap() as u64) << (8 * i); + } + + 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 incrementally sample here instead of panicking. + None => panic!("Insufficient randomness to sample challenge ring element."), + }; + + if i <= index { + break i; + } + }; + + out.coefficients[index] = out.coefficients[sample_at]; + out.coefficients[sample_at] = 1 - 2 * ((signs & 1) as i32); + signs >>= 1; + } + + out +} + #[cfg(test)] mod tests { use super::*; @@ -225,7 +271,7 @@ mod tests { ]; assert_eq!( - sample_error_ring_element_uniform::<2>(seed).coefficients, + sample_error_ring_element::<2>(seed).coefficients, expected_coefficients ); } @@ -254,7 +300,85 @@ mod tests { ]; assert_eq!( - sample_error_ring_element_uniform::<4>(seed).coefficients, + sample_error_ring_element::<4>(seed).coefficients, + expected_coefficients + ); + } + + #[test] + fn test_sample_challenge_ring_element_when_tau_is_39() { + let seed: [u8; 32] = [ + 3, 9, 159, 119, 236, 6, 207, 7, 103, 108, 187, 137, 222, 35, 37, 30, 79, 224, 204, 186, + 41, 38, 148, 188, 201, 50, 105, 155, 129, 217, 124, 57, + ]; + + let expected_coefficients: [i32; COEFFICIENTS_IN_RING_ELEMENT] = [ + 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, -1, + -1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, -1, + -1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, -1, 0, 0, 0, 0, 0, -1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 1, 0, 0, -1, 0, 0, -1, 1, 0, 0, 1, + 0, 0, 0, 1, 0, 0, -1, 0, 1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, + 0, + ]; + + assert_eq!( + sample_challenge_ring_element::<39>(seed).coefficients, + expected_coefficients + ); + } + + #[test] + fn test_sample_challenge_ring_element_when_tau_is_49() { + let seed: [u8; 32] = [ + 147, 7, 165, 152, 200, 20, 4, 38, 107, 110, 111, 176, 108, 84, 109, 201, 232, 125, 52, + 83, 160, 120, 106, 44, 76, 41, 76, 144, 8, 184, 4, 74, + ]; + + let expected_coefficients: [i32; COEFFICIENTS_IN_RING_ELEMENT] = [ + 0, 0, 0, 1, 0, 0, 0, -1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -1, -1, 0, + 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, + -1, 0, 0, 1, 0, 0, 0, 0, 0, 0, -1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, -1, 0, 0, -1, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -1, 0, 0, 1, 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, -1, 0, -1, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, + -1, 0, -1, 0, 0, -1, 0, 0, -1, 0, -1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 1, 0, + 0, -1, 0, 0, 0, + ]; + + assert_eq!( + sample_challenge_ring_element::<49>(seed).coefficients, + expected_coefficients + ); + } + + #[test] + fn test_sample_challenge_ring_element_when_tau_is_60() { + let seed: [u8; 32] = [ + 188, 193, 17, 175, 172, 179, 13, 23, 90, 238, 237, 230, 143, 113, 24, 65, 250, 86, 234, + 229, 251, 57, 199, 158, 9, 4, 102, 249, 11, 68, 140, 107, + ]; + + let expected_coefficients: [i32; COEFFICIENTS_IN_RING_ELEMENT] = [ + 0, 0, 0, 0, -1, 0, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, 0, -1, 0, 0, -1, + 0, 0, 0, 0, 0, -1, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, -1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, -1, 0, 1, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 1, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, + 0, 1, 0, -1, 1, 0, 0, 0, 0, 0, 1, 1, -1, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 1, 0, 0, 1, 1, -1, 0, + 0, 0, 0, 1, -1, 0, + ]; + + assert_eq!( + sample_challenge_ring_element::<60>(seed).coefficients, expected_coefficients ); } diff --git a/libcrux-ml-dsa/src/serialize.rs b/libcrux-ml-dsa/src/serialize.rs index 7354b1ba4..5b5f07260 100644 --- a/libcrux-ml-dsa/src/serialize.rs +++ b/libcrux-ml-dsa/src/serialize.rs @@ -126,6 +126,48 @@ pub(crate) fn serialize_error_ring_element( + 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::*; @@ -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); + } }