Skip to content

Commit 0e8d329

Browse files
committed
Fix: Kyberslash side-channel in compress functions
Replace the divisions by KyberConstants::Q with a divisionless alternative.
1 parent 541a29f commit 0e8d329

File tree

1 file changed

+20
-17
lines changed

1 file changed

+20
-17
lines changed

src/lib/pubkey/kyber/kyber_common/kyber.cpp

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,20 @@ KyberMode::Mode kyber_mode_from_string(std::string_view str) {
7171
throw Invalid_Argument(fmt("'{}' is not a valid Kyber mode name", str));
7272
}
7373

74+
/**
75+
* Constant time implementation for computing an unsigned integer division
76+
* with KyberConstants::Q = 3329.
77+
*
78+
* It enforces the optimization of various compilers,
79+
* replacing the division operation with multiplication and shifts.
80+
*
81+
* @returns (a / KyberConstants::Q)
82+
*/
83+
uint16_t ct_int_div_kyber_q(uint32_t a) {
84+
const uint64_t tmp = (static_cast<uint64_t>(a) * 989558401UL) >> 32;
85+
return static_cast<uint16_t>((tmp + ((a - tmp) >> 1)) >> 11);
86+
}
87+
7488
} // namespace
7589

7690
KyberMode::KyberMode(Mode mode) : m_mode(mode) {}
@@ -399,21 +413,12 @@ class Polynomial {
399413

400414
this->csubq();
401415

402-
auto compress = [](uint32_t t) {
403-
// (t << 1) + ((KyberConstants::Q / 2) / KyberConstants::Q) & 1
404-
// Note that magic numbers assume that ::Q = 3329
405-
t <<= 1;
406-
t += 1665;
407-
t *= 80635;
408-
t >>= 28;
409-
t &= 1;
410-
return static_cast<uint8_t>(t);
411-
};
412-
413416
for(size_t i = 0; i < size() / 8; ++i) {
414417
result[i] = 0;
415418
for(size_t j = 0; j < 8; ++j) {
416-
result[i] |= compress(this->m_coeffs[8 * i + j]) << j;
419+
const uint16_t t =
420+
ct_int_div_kyber_q((static_cast<uint16_t>(this->m_coeffs[8 * i + j]) << 1) + KyberConstants::Q / 2);
421+
result[i] |= (t & 1) << j;
417422
}
418423
}
419424

@@ -564,7 +569,7 @@ class Polynomial {
564569
*/
565570
static int16_t barrett_reduce(int16_t a) {
566571
int16_t t;
567-
const int16_t v = ((1U << 26) + KyberConstants::Q / 2) / KyberConstants::Q;
572+
constexpr int16_t v = ((1U << 26) + KyberConstants::Q / 2) / KyberConstants::Q;
568573

569574
t = static_cast<int32_t>(v) * a >> 26;
570575
t *= KyberConstants::Q;
@@ -865,8 +870,7 @@ class Ciphertext {
865870
size_t offset = 0;
866871
for(size_t i = 0; i < p.size() / 8; ++i) {
867872
for(size_t j = 0; j < 8; ++j) {
868-
t[j] =
869-
(((static_cast<uint16_t>(p[8 * i + j]) << 4) + KyberConstants::Q / 2) / KyberConstants::Q) & 15;
873+
t[j] = ct_int_div_kyber_q((static_cast<uint16_t>(p[8 * i + j]) << 4) + KyberConstants::Q / 2) & 15;
870874
}
871875

872876
r[0 + offset] = t[0] | (t[1] << 4);
@@ -879,8 +883,7 @@ class Ciphertext {
879883
size_t offset = 0;
880884
for(size_t i = 0; i < p.size() / 8; ++i) {
881885
for(size_t j = 0; j < 8; ++j) {
882-
t[j] =
883-
(((static_cast<uint32_t>(p[8 * i + j]) << 5) + KyberConstants::Q / 2) / KyberConstants::Q) & 31;
886+
t[j] = ct_int_div_kyber_q((static_cast<uint32_t>(p[8 * i + j]) << 5) + KyberConstants::Q / 2) & 31;
884887
}
885888

886889
r[0 + offset] = (t[0] >> 0) | (t[1] << 5);

0 commit comments

Comments
 (0)