From d70f68a6d25433372cee9740bc5c337c59c1b583 Mon Sep 17 00:00:00 2001 From: xvzcf Date: Fri, 18 Aug 2023 15:21:27 -0400 Subject: [PATCH 1/6] Use montgomery reduction for NTT. --- src/kem/kyber768/arithmetic.rs | 13 +++++++++++++ src/kem/kyber768/ntt.rs | 23 +++++++++++++++++++++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/kem/kyber768/arithmetic.rs b/src/kem/kyber768/arithmetic.rs index 4c131c46f..1ee6f6d31 100644 --- a/src/kem/kyber768/arithmetic.rs +++ b/src/kem/kyber768/arithmetic.rs @@ -15,6 +15,19 @@ pub(crate) fn barrett_reduce(value: i16) -> KyberFieldElement { value - (quotient * FIELD_MODULUS) } +const MONTGOMERY_SHIFT : i64 = 16; +const MONTGOMERY_R : i64 = 1i64 << MONTGOMERY_SHIFT; +const INVERSE_OF_MODULUS_MOD_R : i64 = -3327; // FIELD_MODULUS^{-1} mod MONTGOMERY_R + +pub(crate) fn montgomery_reduce(value : i32) -> KyberFieldElement { + let t : i64 = i64::from(value) * INVERSE_OF_MODULUS_MOD_R; + let t : i32 = (t & (MONTGOMERY_R - 1)) as i32; + + let t = value - (t * i32::from(FIELD_MODULUS)); + + (t >> MONTGOMERY_SHIFT) as i16 +} + pub(crate) fn fe_mul(lhs: KyberFieldElement, rhs: KyberFieldElement) -> KyberFieldElement { // TODO: This will shortly be replaced by an implementation of // montgomery reduction. diff --git a/src/kem/kyber768/ntt.rs b/src/kem/kyber768/ntt.rs index c45b78c80..3670dd25a 100644 --- a/src/kem/kyber768/ntt.rs +++ b/src/kem/kyber768/ntt.rs @@ -7,7 +7,7 @@ use self::kyber_polynomial_ring_element_mod::ntt_multiply; pub(crate) mod kyber_polynomial_ring_element_mod { use crate::kem::kyber768::{ - arithmetic::{barrett_reduce, fe_mul, KyberFieldElement, KyberPolynomialRingElement}, + arithmetic::{montgomery_reduce, barrett_reduce, fe_mul, KyberFieldElement, KyberPolynomialRingElement}, parameters::COEFFICIENTS_IN_RING_ELEMENT, }; @@ -23,6 +23,25 @@ pub(crate) mod kyber_polynomial_ring_element_mod { 1212, -1455, 1029, -1219, -394, 885, -1175, ]; + const ZETAS_MONTGOMERY_DOMAIN: [i32; 128] = [ + -1044, -758, -359, -1517, 1493, 1422, 287, 202, + -171, 622, 1577, 182, 962, -1202, -1474, 1468, + 573, -1325, 264, 383, -829, 1458, -1602, -130, + -681, 1017, 732, 608, -1542, 411, -205, -1571, + 1223, 652, -552, 1015, -1293, 1491, -282, -1544, + 516, -8, -320, -666, -1618, -1162, 126, 1469, + -853, -90, -271, 830, 107, -1421, -247, -951, + -398, 961, -1508, -725, 448, -1065, 677, -1275, + -1103, 430, 555, 843, -1251, 871, 1550, 105, + 422, 587, 177, -235, -291, -460, 1574, 1653, + -246, 778, 1159, -147, -777, 1483, -602, 1119, + -1590, 644, -872, 349, 418, 329, -156, -75, + 817, 1097, 603, 610, 1322, -1285, -1465, 384, + -1215, -136, 1218, -1335, -874, 220, -1187, -1659, + -1185, -1530, -1278, 794, -1510, -854, -870, 478, + -108, -308, 996, 991, 958, -1460, 1522, 1628 + ]; + const MOD_ROOTS: [i16; 128] = [ 17, -17, -568, 568, 583, -583, -680, 680, 1637, -1637, 723, -723, -1041, 1041, 1100, -1100, 1409, -1409, -667, 667, -48, 48, 233, -233, 756, -756, -1173, 1173, -314, 314, -279, 279, @@ -44,7 +63,7 @@ pub(crate) mod kyber_polynomial_ring_element_mod { zeta_i += 1; for j in offset..offset + layer { - let t = fe_mul(re[j + layer], ZETAS[zeta_i]); + let t = montgomery_reduce(i32::from(re[j + layer]) * ZETAS_MONTGOMERY_DOMAIN[zeta_i]); re[j + layer] = re[j] - t; re[j] += t; } From 21dd711237b629fd7ccc56b73a48da236d9ba37e Mon Sep 17 00:00:00 2001 From: xvzcf Date: Mon, 21 Aug 2023 10:18:01 -0400 Subject: [PATCH 2/6] cargo fmt --- src/kem/kyber768/arithmetic.rs | 12 ++++++------ src/kem/kyber768/ntt.rs | 34 ++++++++++++++++------------------ 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/src/kem/kyber768/arithmetic.rs b/src/kem/kyber768/arithmetic.rs index 1ee6f6d31..151558705 100644 --- a/src/kem/kyber768/arithmetic.rs +++ b/src/kem/kyber768/arithmetic.rs @@ -15,13 +15,13 @@ pub(crate) fn barrett_reduce(value: i16) -> KyberFieldElement { value - (quotient * FIELD_MODULUS) } -const MONTGOMERY_SHIFT : i64 = 16; -const MONTGOMERY_R : i64 = 1i64 << MONTGOMERY_SHIFT; -const INVERSE_OF_MODULUS_MOD_R : i64 = -3327; // FIELD_MODULUS^{-1} mod MONTGOMERY_R +const MONTGOMERY_SHIFT: i64 = 16; +const MONTGOMERY_R: i64 = 1i64 << MONTGOMERY_SHIFT; +const INVERSE_OF_MODULUS_MOD_R: i64 = -3327; // FIELD_MODULUS^{-1} mod MONTGOMERY_R -pub(crate) fn montgomery_reduce(value : i32) -> KyberFieldElement { - let t : i64 = i64::from(value) * INVERSE_OF_MODULUS_MOD_R; - let t : i32 = (t & (MONTGOMERY_R - 1)) as i32; +pub(crate) fn montgomery_reduce(value: i32) -> KyberFieldElement { + let t: i64 = i64::from(value) * INVERSE_OF_MODULUS_MOD_R; + let t: i32 = (t & (MONTGOMERY_R - 1)) as i32; let t = value - (t * i32::from(FIELD_MODULUS)); diff --git a/src/kem/kyber768/ntt.rs b/src/kem/kyber768/ntt.rs index 3670dd25a..3c6eb479a 100644 --- a/src/kem/kyber768/ntt.rs +++ b/src/kem/kyber768/ntt.rs @@ -7,7 +7,10 @@ use self::kyber_polynomial_ring_element_mod::ntt_multiply; pub(crate) mod kyber_polynomial_ring_element_mod { use crate::kem::kyber768::{ - arithmetic::{montgomery_reduce, barrett_reduce, fe_mul, KyberFieldElement, KyberPolynomialRingElement}, + arithmetic::{ + barrett_reduce, fe_mul, montgomery_reduce, KyberFieldElement, + KyberPolynomialRingElement, + }, parameters::COEFFICIENTS_IN_RING_ELEMENT, }; @@ -24,22 +27,15 @@ pub(crate) mod kyber_polynomial_ring_element_mod { ]; const ZETAS_MONTGOMERY_DOMAIN: [i32; 128] = [ - -1044, -758, -359, -1517, 1493, 1422, 287, 202, - -171, 622, 1577, 182, 962, -1202, -1474, 1468, - 573, -1325, 264, 383, -829, 1458, -1602, -130, - -681, 1017, 732, 608, -1542, 411, -205, -1571, - 1223, 652, -552, 1015, -1293, 1491, -282, -1544, - 516, -8, -320, -666, -1618, -1162, 126, 1469, - -853, -90, -271, 830, 107, -1421, -247, -951, - -398, 961, -1508, -725, 448, -1065, 677, -1275, - -1103, 430, 555, 843, -1251, 871, 1550, 105, - 422, 587, 177, -235, -291, -460, 1574, 1653, - -246, 778, 1159, -147, -777, 1483, -602, 1119, - -1590, 644, -872, 349, 418, 329, -156, -75, - 817, 1097, 603, 610, 1322, -1285, -1465, 384, - -1215, -136, 1218, -1335, -874, 220, -1187, -1659, - -1185, -1530, -1278, 794, -1510, -854, -870, 478, - -108, -308, 996, 991, 958, -1460, 1522, 1628 + -1044, -758, -359, -1517, 1493, 1422, 287, 202, -171, 622, 1577, 182, 962, -1202, -1474, + 1468, 573, -1325, 264, 383, -829, 1458, -1602, -130, -681, 1017, 732, 608, -1542, 411, + -205, -1571, 1223, 652, -552, 1015, -1293, 1491, -282, -1544, 516, -8, -320, -666, -1618, + -1162, 126, 1469, -853, -90, -271, 830, 107, -1421, -247, -951, -398, 961, -1508, -725, + 448, -1065, 677, -1275, -1103, 430, 555, 843, -1251, 871, 1550, 105, 422, 587, 177, -235, + -291, -460, 1574, 1653, -246, 778, 1159, -147, -777, 1483, -602, 1119, -1590, 644, -872, + 349, 418, 329, -156, -75, 817, 1097, 603, 610, 1322, -1285, -1465, 384, -1215, -136, 1218, + -1335, -874, 220, -1187, -1659, -1185, -1530, -1278, 794, -1510, -854, -870, 478, -108, + -308, 996, 991, 958, -1460, 1522, 1628, ]; const MOD_ROOTS: [i16; 128] = [ @@ -63,7 +59,9 @@ pub(crate) mod kyber_polynomial_ring_element_mod { zeta_i += 1; for j in offset..offset + layer { - let t = montgomery_reduce(i32::from(re[j + layer]) * ZETAS_MONTGOMERY_DOMAIN[zeta_i]); + let t = montgomery_reduce( + i32::from(re[j + layer]) * ZETAS_MONTGOMERY_DOMAIN[zeta_i], + ); re[j + layer] = re[j] - t; re[j] += t; } From bcaa69e959da785c7730112f8fcd65814e57e08d Mon Sep 17 00:00:00 2001 From: xvzcf Date: Mon, 21 Aug 2023 10:40:40 -0400 Subject: [PATCH 3/6] Comments and slight refactoring. --- src/kem/kyber768/ind_cpa.rs | 6 ++++-- src/kem/kyber768/serialize.rs | 6 ++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/kem/kyber768/ind_cpa.rs b/src/kem/kyber768/ind_cpa.rs index 4fcef310b..556ddfd19 100644 --- a/src/kem/kyber768/ind_cpa.rs +++ b/src/kem/kyber768/ind_cpa.rs @@ -180,7 +180,9 @@ pub(crate) fn generate_keypair( )) } -fn encode_and_compress_u(input: [KyberPolynomialRingElement; RANK]) -> [u8; VECTOR_U_ENCODED_SIZE] { +fn compress_then_encode_u( + input: [KyberPolynomialRingElement; RANK], +) -> [u8; VECTOR_U_ENCODED_SIZE] { let mut out = [0u8; VECTOR_U_ENCODED_SIZE]; for (i, re) in input.into_iter().enumerate() { out[i * BYTES_PER_ENCODED_ELEMENT_OF_U..(i + 1) * BYTES_PER_ENCODED_ELEMENT_OF_U] @@ -258,7 +260,7 @@ pub(crate) fn encrypt( + decompress(message_as_ring_element, 1); // c_1 := Encode_{du}(Compress_q(u,d_u)) - let c1 = encode_and_compress_u(u); + let c1 = compress_then_encode_u(u); // c_2 := Encode_{dv}(Compress_q(v,d_v)) let c2 = serialize_little_endian_4(compress(v, VECTOR_V_COMPRESSION_FACTOR)); diff --git a/src/kem/kyber768/serialize.rs b/src/kem/kyber768/serialize.rs index 576374864..1affe1985 100644 --- a/src/kem/kyber768/serialize.rs +++ b/src/kem/kyber768/serialize.rs @@ -32,6 +32,12 @@ use crate::kem::kyber768::{ /// /// Otherwise `deserialize_little_endian` is not injective and therefore has /// no left inverse. +/// +/// N.B.: All the `serialize_little_endian_{n}` functions work on the canonical +/// unsigned representative of each coefficient in the polynomial ring. +/// Only `serialize_little_endian_12` actually performs this conversion in the +/// function itself; the rest don't since they are called only after `compress_q` +/// is called, and `compress_q` also performs this conversion. pub fn serialize_little_endian_1( re: KyberPolynomialRingElement, From c70266bfb6e46846ac20990ecefeaf5690b0b48b Mon Sep 17 00:00:00 2001 From: xvzcf Date: Mon, 21 Aug 2023 12:13:51 -0400 Subject: [PATCH 4/6] Key generation and decryption now use montgomery multiplication only. --- src/kem/kyber768/arithmetic.rs | 5 +++ src/kem/kyber768/ntt.rs | 63 +++++++++++++++------------------- 2 files changed, 33 insertions(+), 35 deletions(-) diff --git a/src/kem/kyber768/arithmetic.rs b/src/kem/kyber768/arithmetic.rs index 151558705..0d17fe4b1 100644 --- a/src/kem/kyber768/arithmetic.rs +++ b/src/kem/kyber768/arithmetic.rs @@ -28,6 +28,11 @@ pub(crate) fn montgomery_reduce(value: i32) -> KyberFieldElement { (t >> MONTGOMERY_SHIFT) as i16 } +const MONTGOMERY_F : i32 = 1353; // 2^32 mod 3329 +pub(crate) fn from_montgomery_domain(value: i32) -> KyberFieldElement { + montgomery_reduce(MONTGOMERY_F * value) +} + pub(crate) fn fe_mul(lhs: KyberFieldElement, rhs: KyberFieldElement) -> KyberFieldElement { // TODO: This will shortly be replaced by an implementation of // montgomery reduction. diff --git a/src/kem/kyber768/ntt.rs b/src/kem/kyber768/ntt.rs index 3c6eb479a..98b0d16cc 100644 --- a/src/kem/kyber768/ntt.rs +++ b/src/kem/kyber768/ntt.rs @@ -1,5 +1,5 @@ use crate::kem::kyber768::{ - arithmetic::{barrett_reduce, KyberPolynomialRingElement}, + arithmetic::{barrett_reduce, from_montgomery_domain, KyberPolynomialRingElement}, parameters::RANK, }; @@ -38,18 +38,6 @@ pub(crate) mod kyber_polynomial_ring_element_mod { -308, 996, 991, 958, -1460, 1522, 1628, ]; - const MOD_ROOTS: [i16; 128] = [ - 17, -17, -568, 568, 583, -583, -680, 680, 1637, -1637, 723, -723, -1041, 1041, 1100, -1100, - 1409, -1409, -667, 667, -48, 48, 233, -233, 756, -756, -1173, 1173, -314, 314, -279, 279, - -1626, 1626, 1651, -1651, -540, 540, -1540, 1540, -1482, 1482, 952, -952, 1461, -1461, - -642, 642, 939, -939, -1021, 1021, -892, 892, -941, 941, 733, -733, -992, 992, 268, -268, - 641, -641, 1584, -1584, -1031, 1031, -1292, 1292, -109, 109, 375, -375, -780, 780, -1239, - 1239, 1645, -1645, 1063, -1063, 319, -319, -556, 556, 757, -757, -1230, 1230, 561, -561, - -863, 863, -735, 735, -525, 525, 1092, -1092, 403, -403, 1026, -1026, 1143, -1143, -1179, - 1179, -554, 554, 886, -886, -1607, 1607, 1212, -1212, -1455, 1455, 1029, -1029, -1219, - 1219, -394, 394, 885, -885, -1175, 1175, - ]; - const NTT_LAYERS: [usize; 7] = [2, 4, 8, 16, 32, 64, 128]; pub fn ntt_representation(mut re: KyberPolynomialRingElement) -> KyberPolynomialRingElement { @@ -96,11 +84,17 @@ pub(crate) mod kyber_polynomial_ring_element_mod { fn ntt_multiply_binomials( (a0, a1): (KyberFieldElement, KyberFieldElement), (b0, b1): (KyberFieldElement, KyberFieldElement), - zeta: i16, + zeta: i32, ) -> (KyberFieldElement, KyberFieldElement) { + let a0 = i32::from(a0); + let a1 = i32::from(a1); + + let b0 = i32::from(b0); + let b1 = i32::from(b1); + ( - fe_mul(a0, b0) + fe_mul(fe_mul(a1, b1), zeta), - fe_mul(a0, b1) + fe_mul(a1, b0), + montgomery_reduce(a0 * b0) + montgomery_reduce((montgomery_reduce(a1 * b1) as i32) * zeta), + montgomery_reduce(a0 * b1) + montgomery_reduce(a1 * b0), ) } @@ -114,7 +108,7 @@ pub(crate) mod kyber_polynomial_ring_element_mod { let product = ntt_multiply_binomials( (left[i], left[i + 1]), (right[i], right[i + 1]), - MOD_ROOTS[i / 2], + ZETAS_MONTGOMERY_DOMAIN[64 + (i/4)], ); out[i] = product.0; out[i + 1] = product.1; @@ -122,17 +116,31 @@ pub(crate) mod kyber_polynomial_ring_element_mod { let product = ntt_multiply_binomials( (left[i + 2], left[i + 3]), (right[i + 2], right[i + 3]), - MOD_ROOTS[(i + 2) / 2], + -ZETAS_MONTGOMERY_DOMAIN[64 + (i/4)], ); out[i + 2] = product.0; out[i + 3] = product.1; } - out.coefficients = out.coefficients.map(barrett_reduce); out } } +pub(crate) fn multiply_row_by_column( + row_vector: &[KyberPolynomialRingElement; RANK], + column_vector: &[KyberPolynomialRingElement; RANK], +) -> KyberPolynomialRingElement { + let mut result = KyberPolynomialRingElement::ZERO; + + for (row_element, column_element) in row_vector.iter().zip(column_vector.iter()) { + result = result + ntt_multiply(row_element, column_element); + } + + result.coefficients = result.coefficients.map(|coefficient| from_montgomery_domain(coefficient as i32)).map(barrett_reduce); + + result +} + pub(crate) fn multiply_matrix_by_column( matrix: &[[KyberPolynomialRingElement; RANK]; RANK], vector: &[KyberPolynomialRingElement; RANK], @@ -144,23 +152,8 @@ pub(crate) fn multiply_matrix_by_column( let product = ntt_multiply(matrix_element, &vector[j]); result[i] = result[i] + product; } - result[i].coefficients = result[i].coefficients.map(barrett_reduce); - } - - result -} - -pub(crate) fn multiply_row_by_column( - row_vector: &[KyberPolynomialRingElement; RANK], - column_vector: &[KyberPolynomialRingElement; RANK], -) -> KyberPolynomialRingElement { - let mut result = KyberPolynomialRingElement::ZERO; - - for (row_element, column_element) in row_vector.iter().zip(column_vector.iter()) { - result = result + ntt_multiply(row_element, column_element); + result[i].coefficients = result[i].coefficients.map(|coefficient| from_montgomery_domain(coefficient as i32)).map(barrett_reduce); } - result.coefficients = result.coefficients.map(barrett_reduce); - result } From ac72615496d8e55615b05fefbbef9228f14199db Mon Sep 17 00:00:00 2001 From: xvzcf Date: Tue, 22 Aug 2023 10:51:56 -0400 Subject: [PATCH 5/6] Use montgomery_multiplication everywhere. --- src/kem/kyber768/arithmetic.rs | 16 -------------- src/kem/kyber768/ntt.rs | 39 ++++++++++++++-------------------- 2 files changed, 16 insertions(+), 39 deletions(-) diff --git a/src/kem/kyber768/arithmetic.rs b/src/kem/kyber768/arithmetic.rs index 0d17fe4b1..b0a0bfd65 100644 --- a/src/kem/kyber768/arithmetic.rs +++ b/src/kem/kyber768/arithmetic.rs @@ -33,22 +33,6 @@ pub(crate) fn from_montgomery_domain(value: i32) -> KyberFieldElement { montgomery_reduce(MONTGOMERY_F * value) } -pub(crate) fn fe_mul(lhs: KyberFieldElement, rhs: KyberFieldElement) -> KyberFieldElement { - // TODO: This will shortly be replaced by an implementation of - // montgomery reduction. - let product: i32 = i32::from(lhs) * i32::from(rhs); - - let reduced = (product % i32::from(FIELD_MODULUS)) as i16; - - if reduced > FIELD_MODULUS / 2 { - reduced - FIELD_MODULUS - } else if reduced < -FIELD_MODULUS / 2 { - reduced + FIELD_MODULUS - } else { - reduced - } -} - #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct KyberPolynomialRingElement { pub(crate) coefficients: [KyberFieldElement; COEFFICIENTS_IN_RING_ELEMENT], diff --git a/src/kem/kyber768/ntt.rs b/src/kem/kyber768/ntt.rs index 98b0d16cc..9018756ca 100644 --- a/src/kem/kyber768/ntt.rs +++ b/src/kem/kyber768/ntt.rs @@ -8,24 +8,12 @@ use self::kyber_polynomial_ring_element_mod::ntt_multiply; pub(crate) mod kyber_polynomial_ring_element_mod { use crate::kem::kyber768::{ arithmetic::{ - barrett_reduce, fe_mul, montgomery_reduce, KyberFieldElement, + barrett_reduce, montgomery_reduce, KyberFieldElement, KyberPolynomialRingElement, }, parameters::COEFFICIENTS_IN_RING_ELEMENT, }; - const ZETAS: [i16; 128] = [ - 1, -1600, -749, -40, -687, 630, -1432, 848, 1062, -1410, 193, 797, -543, -69, 569, -1583, - 296, -882, 1339, 1476, -283, 56, -1089, 1333, 1426, -1235, 535, -447, -936, -450, -1355, - 821, 289, 331, -76, -1573, 1197, -1025, -1052, -1274, 650, -1352, -816, 632, -464, 33, - 1320, -1414, -1010, 1435, 807, 452, 1438, -461, 1534, -927, -682, -712, 1481, 648, -855, - -219, 1227, 910, 17, -568, 583, -680, 1637, 723, -1041, 1100, 1409, -667, -48, 233, 756, - -1173, -314, -279, -1626, 1651, -540, -1540, -1482, 952, 1461, -642, 939, -1021, -892, - -941, 733, -992, 268, 641, 1584, -1031, -1292, -109, 375, -780, -1239, 1645, 1063, 319, - -556, 757, -1230, 561, -863, -735, -525, 1092, 403, 1026, 1143, -1179, -554, 886, -1607, - 1212, -1455, 1029, -1219, -394, 885, -1175, - ]; - const ZETAS_MONTGOMERY_DOMAIN: [i32; 128] = [ -1044, -758, -359, -1517, 1493, 1422, 287, 202, -171, 622, 1577, 182, 962, -1202, -1474, 1468, 573, -1325, 264, 383, -829, 1458, -1602, -130, -681, 1017, 732, 608, -1542, 411, @@ -61,8 +49,6 @@ pub(crate) mod kyber_polynomial_ring_element_mod { } pub fn invert_ntt(mut re: KyberPolynomialRingElement) -> KyberPolynomialRingElement { - let inverse_of_2: i16 = -1664; - let mut zeta_i = COEFFICIENTS_IN_RING_ELEMENT / 2; for layer in NTT_LAYERS { @@ -70,13 +56,20 @@ pub(crate) mod kyber_polynomial_ring_element_mod { zeta_i -= 1; for j in offset..offset + layer { - let a_minus_b = re[j + layer] - re[j]; - re[j] = fe_mul(re[j] + re[j + layer], inverse_of_2); - re[j + layer] = fe_mul(fe_mul(a_minus_b, ZETAS[zeta_i]), inverse_of_2); + let a_minus_b = (re[j + layer] - re[j]) as i32; + + // Instead of dividing by 2 here, we just divide by + // 2^7 at once in the end. + re[j] = barrett_reduce(re[j] + re[j + layer]); + re[j + layer] = montgomery_reduce(a_minus_b * ZETAS_MONTGOMERY_DOMAIN[zeta_i]); } } } - re.coefficients = re.coefficients.map(barrett_reduce); + + // We first convert (2^7)^-1 = (128)^-1 into the montgomery domain by + // computing (2^7)^-1 * 2^16 mod 3329. This is equal to 512 = 2^9, and + // so we can just left-shift by 9 bits. + re.coefficients = re.coefficients.map(|coefficient| montgomery_reduce((coefficient as i32) << 9)); re } @@ -86,11 +79,11 @@ pub(crate) mod kyber_polynomial_ring_element_mod { (b0, b1): (KyberFieldElement, KyberFieldElement), zeta: i32, ) -> (KyberFieldElement, KyberFieldElement) { - let a0 = i32::from(a0); - let a1 = i32::from(a1); + let a0 = a0 as i32; + let a1 = a1 as i32; - let b0 = i32::from(b0); - let b1 = i32::from(b1); + let b0 = b0 as i32; + let b1 = b1 as i32; ( montgomery_reduce(a0 * b0) + montgomery_reduce((montgomery_reduce(a1 * b1) as i32) * zeta), From 598875d40c12e4b455b5d42c11cfe5ad0c6e0788 Mon Sep 17 00:00:00 2001 From: xvzcf Date: Tue, 22 Aug 2023 13:10:33 -0400 Subject: [PATCH 6/6] Comments and formatting. --- src/kem/kyber768/arithmetic.rs | 9 ++++++--- src/kem/kyber768/ntt.rs | 31 ++++++++++++++++++++++--------- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/src/kem/kyber768/arithmetic.rs b/src/kem/kyber768/arithmetic.rs index b0a0bfd65..ed3bbc3ab 100644 --- a/src/kem/kyber768/arithmetic.rs +++ b/src/kem/kyber768/arithmetic.rs @@ -28,9 +28,12 @@ pub(crate) fn montgomery_reduce(value: i32) -> KyberFieldElement { (t >> MONTGOMERY_SHIFT) as i16 } -const MONTGOMERY_F : i32 = 1353; // 2^32 mod 3329 -pub(crate) fn from_montgomery_domain(value: i32) -> KyberFieldElement { - montgomery_reduce(MONTGOMERY_F * value) +// Given a |value|, return |value|*R mod q. Notice that montgomery_reduce +// returns a value aR^{-1} mod q, and so montgomery_reduce(|value| * R^2) +// returns |value| * R^2 & R^{-1} mod q = |value| * R mod q. +pub(crate) fn to_montgomery_domain(value: i32) -> KyberFieldElement { + // R^2 mod q = (2^16)^2 mod 3329 = 1353 + montgomery_reduce(1353 * value) } #[derive(Debug, Clone, Copy, PartialEq, Eq)] diff --git a/src/kem/kyber768/ntt.rs b/src/kem/kyber768/ntt.rs index 9018756ca..c4c3e7a3a 100644 --- a/src/kem/kyber768/ntt.rs +++ b/src/kem/kyber768/ntt.rs @@ -1,5 +1,5 @@ use crate::kem::kyber768::{ - arithmetic::{barrett_reduce, from_montgomery_domain, KyberPolynomialRingElement}, + arithmetic::{barrett_reduce, to_montgomery_domain, KyberPolynomialRingElement}, parameters::RANK, }; @@ -8,8 +8,7 @@ use self::kyber_polynomial_ring_element_mod::ntt_multiply; pub(crate) mod kyber_polynomial_ring_element_mod { use crate::kem::kyber768::{ arithmetic::{ - barrett_reduce, montgomery_reduce, KyberFieldElement, - KyberPolynomialRingElement, + barrett_reduce, montgomery_reduce, KyberFieldElement, KyberPolynomialRingElement, }, parameters::COEFFICIENTS_IN_RING_ELEMENT, }; @@ -69,7 +68,9 @@ pub(crate) mod kyber_polynomial_ring_element_mod { // We first convert (2^7)^-1 = (128)^-1 into the montgomery domain by // computing (2^7)^-1 * 2^16 mod 3329. This is equal to 512 = 2^9, and // so we can just left-shift by 9 bits. - re.coefficients = re.coefficients.map(|coefficient| montgomery_reduce((coefficient as i32) << 9)); + re.coefficients = re + .coefficients + .map(|coefficient| montgomery_reduce((coefficient as i32) << 9)); re } @@ -86,7 +87,8 @@ pub(crate) mod kyber_polynomial_ring_element_mod { let b1 = b1 as i32; ( - montgomery_reduce(a0 * b0) + montgomery_reduce((montgomery_reduce(a1 * b1) as i32) * zeta), + montgomery_reduce(a0 * b0) + + montgomery_reduce((montgomery_reduce(a1 * b1) as i32) * zeta), montgomery_reduce(a0 * b1) + montgomery_reduce(a1 * b0), ) } @@ -101,7 +103,7 @@ pub(crate) mod kyber_polynomial_ring_element_mod { let product = ntt_multiply_binomials( (left[i], left[i + 1]), (right[i], right[i + 1]), - ZETAS_MONTGOMERY_DOMAIN[64 + (i/4)], + ZETAS_MONTGOMERY_DOMAIN[64 + (i / 4)], ); out[i] = product.0; out[i + 1] = product.1; @@ -109,7 +111,7 @@ pub(crate) mod kyber_polynomial_ring_element_mod { let product = ntt_multiply_binomials( (left[i + 2], left[i + 3]), (right[i + 2], right[i + 3]), - -ZETAS_MONTGOMERY_DOMAIN[64 + (i/4)], + -ZETAS_MONTGOMERY_DOMAIN[64 + (i / 4)], ); out[i + 2] = product.0; out[i + 3] = product.1; @@ -129,7 +131,12 @@ pub(crate) fn multiply_row_by_column( result = result + ntt_multiply(row_element, column_element); } - result.coefficients = result.coefficients.map(|coefficient| from_montgomery_domain(coefficient as i32)).map(barrett_reduce); + // The coefficients of the form aR^{-1} mod q, which means + // calling to_montgomery_domain() on them should return a mod q. + result.coefficients = result + .coefficients + .map(|coefficient| to_montgomery_domain(coefficient as i32)) + .map(barrett_reduce); result } @@ -145,7 +152,13 @@ pub(crate) fn multiply_matrix_by_column( let product = ntt_multiply(matrix_element, &vector[j]); result[i] = result[i] + product; } - result[i].coefficients = result[i].coefficients.map(|coefficient| from_montgomery_domain(coefficient as i32)).map(barrett_reduce); + + // The coefficients of the form aR^{-1} mod q, which means + // calling to_montgomery_domain() on them should return a mod q. + result[i].coefficients = result[i] + .coefficients + .map(|coefficient| to_montgomery_domain(coefficient as i32)) + .map(barrett_reduce); } result