Skip to content

Commit 23fc31e

Browse files
authored
Merge pull request #79 from itzmeanjan/bump-subtle-dep
Use constant-time operations using `subtle` itself
2 parents 993a5dd + cd7826b commit 23fc31e

4 files changed

Lines changed: 24 additions & 69 deletions

File tree

‎.clang-tidy‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ Checks: '
2626
-modernize-use-trailing-return-type,
2727
-modernize-use-auto,
2828
-modernize-use-ranges,
29+
-portability-avoid-pragma-once,
2930
-readability-magic-numbers
3031
'
3132
CheckOptions:

‎include/ml_kem/internals/k_pke.hpp‎

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "ml_kem/internals/utility/params.hpp"
99
#include "ml_kem/internals/utility/utils.hpp"
1010
#include "sha3/sha3_512.hpp"
11+
#include "subtle.hpp"
1112
#include <algorithm>
1213
#include <array>
1314
#include <cstddef>
@@ -71,9 +72,9 @@ keygen(std::span<const uint8_t, 32> d,
7172
std::copy(rho.begin(), rho.end(), rho_in_pubkey.begin());
7273
ml_kem_utils::poly_vec_encode<k, 12>(s, seckey);
7374

74-
ml_kem_utils::secure_zeroize(g_out);
75-
ml_kem_utils::secure_zeroize(s);
76-
ml_kem_utils::secure_zeroize(e);
75+
subtle::ct_zeroize(g_out_span);
76+
subtle::ct_zeroize(std::span(s));
77+
subtle::ct_zeroize(std::span(e));
7778
}
7879

7980
// Given a *valid* K-PKE public key, 32 -bytes message ( to be encrypted ) and 32 -bytes random coin
@@ -102,7 +103,7 @@ encrypt(std::span<const uint8_t, ml_kem_utils::get_pke_public_key_len(k)> pubkey
102103
ml_kem_utils::poly_vec_encode<k, 12>(t_prime, encoded_tprime);
103104

104105
using encoded_pkey_t = std::span<const uint8_t, encoded_t_prime_in_pubkey.size()>;
105-
const auto are_equal = ml_kem_utils::ct_memcmp(encoded_pkey_t(encoded_t_prime_in_pubkey), encoded_pkey_t(encoded_tprime));
106+
const auto are_equal = subtle::ct_memcmp<uint8_t, uint32_t>(encoded_pkey_t(encoded_t_prime_in_pubkey), encoded_pkey_t(encoded_tprime));
106107
if (are_equal == 0U) {
107108
// Got an invalid public key
108109
return false;
@@ -153,10 +154,10 @@ encrypt(std::span<const uint8_t, ml_kem_utils::get_pke_public_key_len(k)> pubkey
153154
ml_kem_utils::poly_compress<dv>(v);
154155
ml_kem_utils::encode<dv>(v, poly_v_in_ctxt);
155156

156-
ml_kem_utils::secure_zeroize(r);
157-
ml_kem_utils::secure_zeroize(e1);
158-
ml_kem_utils::secure_zeroize(e2);
159-
ml_kem_utils::secure_zeroize(m);
157+
subtle::ct_zeroize(std::span(r));
158+
subtle::ct_zeroize(std::span(e1));
159+
subtle::ct_zeroize(std::span(e2));
160+
subtle::ct_zeroize(std::span(m));
160161

161162
return true;
162163
}
@@ -199,7 +200,7 @@ decrypt(std::span<const uint8_t, ml_kem_utils::get_pke_secret_key_len(k)> seckey
199200
ml_kem_utils::poly_compress<1>(v);
200201
ml_kem_utils::encode<1>(v, ptxt);
201202

202-
ml_kem_utils::secure_zeroize(s_prime);
203+
subtle::ct_zeroize(std::span(s_prime));
203204
}
204205

205206
}

‎include/ml_kem/internals/ml_kem.hpp‎

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "sha3/sha3_256.hpp"
66
#include "sha3/sha3_512.hpp"
77
#include "sha3/shake256.hpp"
8+
#include "subtle.hpp"
89
#include <algorithm>
910
#include <array>
1011
#include <cstddef>
@@ -88,15 +89,15 @@ encapsulate(std::span<const uint8_t, 32> m,
8889
const auto has_mod_check_passed = k_pke::encrypt<k, eta1, eta2, du, dv>(pubkey, m, g_out_span1, cipher);
8990
if (!has_mod_check_passed) {
9091
// Got an invalid public key
91-
ml_kem_utils::secure_zeroize(g_in);
92-
ml_kem_utils::secure_zeroize(g_out);
92+
subtle::ct_zeroize(g_in_span);
93+
subtle::ct_zeroize(g_out_span);
9394
return has_mod_check_passed;
9495
}
9596

9697
std::copy(g_out_span0.begin(), g_out_span0.end(), shared_secret.begin());
9798

98-
ml_kem_utils::secure_zeroize(g_in);
99-
ml_kem_utils::secure_zeroize(g_out);
99+
subtle::ct_zeroize(g_in_span);
100+
subtle::ct_zeroize(g_out_span);
100101
return true;
101102
}
102103

@@ -158,14 +159,14 @@ decapsulate(std::span<const uint8_t, ml_kem_utils::get_kem_secret_key_len(k)> se
158159
(void)k_pke::encrypt<k, eta1, eta2, du, dv>(pubkey, g_in_span0, g_out_span1, c_prime);
159160

160161
// line 9-12 of algorithm 17, in constant-time
161-
using kdf_t = std::span<const uint8_t, shared_secret.size()>;
162-
const uint32_t cond = ml_kem_utils::ct_memcmp(cipher, std::span<const uint8_t, ctlen>(c_prime));
163-
ml_kem_utils::ct_cond_memcpy(cond, shared_secret, kdf_t(g_out_span0), kdf_t(j_out));
164-
165-
ml_kem_utils::secure_zeroize(g_in);
166-
ml_kem_utils::secure_zeroize(g_out);
167-
ml_kem_utils::secure_zeroize(j_out);
168-
ml_kem_utils::secure_zeroize(c_prime);
162+
std::copy(j_out.begin(), j_out.end(), shared_secret.begin());
163+
const uint32_t cond = subtle::ct_memcmp<uint8_t, uint32_t>(cipher, std::span<const uint8_t, ctlen>(c_prime));
164+
subtle::ct_conditional_memcpy<uint32_t, uint8_t, 32>(cond, shared_secret, g_out_span0);
165+
166+
subtle::ct_zeroize(g_in_span);
167+
subtle::ct_zeroize(g_out_span);
168+
subtle::ct_zeroize(std::span(j_out));
169+
subtle::ct_zeroize(std::span(c_prime));
169170
}
170171

171172
}

‎include/ml_kem/internals/utility/utils.hpp‎

Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,9 @@
11
#pragma once
22
#include "ml_kem/internals/utility/force_inline.hpp"
3-
#include "subtle.hpp"
4-
#include <array>
53
#include <cstddef>
6-
#include <cstdint>
7-
#include <span>
8-
#include <type_traits>
94

105
namespace ml_kem_utils {
116

12-
// Securely zeroizes a std::array, preventing the compiler from optimizing away the operation.
13-
// At compile-time: a plain fill suffices -- there is no real memory to protect.
14-
// At runtime: the fill is followed by a compiler barrier (empty asm with "memory" clobber)
15-
// that forces the compiler to treat the array's memory as externally observable, preventing
16-
// dead store elimination. The barrier generates zero instructions.
17-
template<typename T, size_t N>
18-
forceinline constexpr void
19-
secure_zeroize(std::array<T, N>& arr)
20-
{
21-
arr.fill(T{});
22-
if (!std::is_constant_evaluated()) {
23-
asm volatile("" : : "r"(arr.data()) : "memory"); // NOLINT(hicpp-no-assembler)
24-
}
25-
}
26-
27-
// Given two byte arrays of equal length, this routine can be used for comparing them in constant-time,
28-
// producing truth value (0xffffffff) in case of equality, otherwise it returns false value (0x00000000).
29-
template<size_t n>
30-
forceinline constexpr uint32_t
31-
ct_memcmp(std::span<const uint8_t, n> bytes0, std::span<const uint8_t, n> bytes1)
32-
{
33-
uint32_t flag = -1U;
34-
for (size_t i = 0; i < n; i++) {
35-
flag &= subtle::ct_eq<uint8_t, uint32_t>(bytes0[i], bytes1[i]);
36-
}
37-
38-
return flag;
39-
}
40-
41-
// Given a branch value, taking either 0x00000000 (false value) or 0xffffffff (truth value), this routine can be used for conditionally
42-
// copying bytes from either `source0` byte array (in case branch holds truth value) or `source1` byte array (if branch holds false value)
43-
// to `sink` byte array, all in constant-time.
44-
//
45-
// In simple words, `sink = cond ? source0 ? source1`
46-
template<size_t n>
47-
forceinline constexpr void
48-
ct_cond_memcpy(const uint32_t cond, std::span<uint8_t, n> sink, std::span<const uint8_t, n> source0, std::span<const uint8_t, n> source1)
49-
{
50-
for (size_t i = 0; i < n; i++) {
51-
sink[i] = subtle::ct_select(cond, source0[i], source1[i]);
52-
}
53-
}
54-
557
// Returns compile-time computable K-PKE public key byte length.
568
forceinline constexpr size_t
579
get_pke_public_key_len(const size_t k)

0 commit comments

Comments
 (0)