• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 11428340676

20 Oct 2024 05:39PM UTC coverage: 91.134%. Remained the same
11428340676

Pull #4397

github

web-flow
Merge d008c3e10 into 6babd8226
Pull Request #4397: Reduce risk of compiler value range propogation in FrodoKEM sampling

91050 of 99908 relevant lines covered (91.13%)

9314704.14 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.3
/src/lib/pubkey/frodokem/frodokem_common/frodo_matrix.cpp
1
/*
2
 * FrodoKEM matrix logic
3
 * Based on the MIT licensed reference implementation by the designers
4
 * (https://github.com/microsoft/PQCrypto-LWEKE/tree/master/src)
5
 *
6
 * The Fellowship of the FrodoKEM:
7
 * (C) 2023 Jack Lloyd
8
 *     2023 René Meusel, Amos Treiber - Rohde & Schwarz Cybersecurity
9
 *
10
 * Botan is released under the Simplified BSD License (see license.txt)
11
 */
12

13
#include <botan/internal/frodo_matrix.h>
14

15
#include <botan/assert.h>
16
#include <botan/frodokem.h>
17
#include <botan/hex.h>
18
#include <botan/mem_ops.h>
19
#include <botan/xof.h>
20
#include <botan/internal/bit_ops.h>
21
#include <botan/internal/frodo_constants.h>
22
#include <botan/internal/loadstor.h>
23
#include <botan/internal/stl_util.h>
24

25
#if defined(BOTAN_HAS_FRODOKEM_AES)
26
   #include <botan/internal/frodo_aes_generator.h>
27
#endif
28

29
#if defined(BOTAN_HAS_FRODOKEM_SHAKE)
30
   #include <botan/internal/frodo_shake_generator.h>
31
#endif
32

33
#include <array>
34
#include <cmath>
35
#include <cstdint>
36
#include <memory>
37
#include <span>
38
#include <utility>
39
#include <vector>
40

41
namespace Botan {
42

43
namespace {
44

45
secure_vector<uint16_t> make_elements_vector(const FrodoMatrix::Dimensions& dimensions) {
7,749✔
46
   return secure_vector<uint16_t>(static_cast<size_t>(std::get<0>(dimensions)) * std::get<1>(dimensions));
2,106✔
47
}
48

49
std::function<void(std::span<uint8_t> out, uint16_t i)> make_row_generator(const FrodoKEMConstants& constants,
1,050✔
50
                                                                           StrongSpan<const FrodoSeedA> seed_a) {
51
#if defined(BOTAN_HAS_FRODOKEM_AES)
52
   if(constants.mode().is_aes()) {
1,050✔
53
      return create_aes_row_generator(constants, seed_a);
1,048✔
54
   }
55
#endif
56

57
#if defined(BOTAN_HAS_FRODOKEM_SHAKE)
58
   if(constants.mode().is_shake()) {
526✔
59
      return create_shake_row_generator(constants, seed_a);
526✔
60
   }
61
#endif
62

63
   // If we don't have AES in this build, the instantiation of the FrodoKEM instance
64
   // is blocked upstream already. Hence, assert is save here.
65
   BOTAN_ASSERT_UNREACHABLE();
×
66
}
67

68
}  // namespace
69

70
FrodoMatrix FrodoMatrix::sample(const FrodoKEMConstants& constants,
2,796✔
71
                                const Dimensions& dimensions,
72
                                StrongSpan<const FrodoSampleR> r) {
73
   BOTAN_ASSERT_NOMSG(r.size() % 2 == 0);
2,796✔
74
   const auto n = r.size() / 2;
2,796✔
75

76
   auto elements = make_elements_vector(dimensions);
2,796✔
77
   BOTAN_ASSERT_NOMSG(n == elements.size());
2,796✔
78

79
   load_le<uint16_t>(elements.data(), r.data(), n);
2,796✔
80

81
   for(auto& elem : elements) {
16,571,884✔
82
      const auto prnd = CT::value_barrier(static_cast<uint16_t>(elem >> 1));  // Drop the least significant bit
16,569,088✔
83
      const auto sign = CT::Mask<uint16_t>::expand_bit(elem, 0);              // Pick the least significant bit
16,569,088✔
84

85
      uint32_t sample = 0;  // Avoid integral promotion
16,569,088✔
86

87
      // No need to compare with the last value.
88
      for(size_t j = 0; j < constants.cdf_table_len() - 1; ++j) {
159,870,208✔
89
         // Constant time comparison: 1 if CDF_TABLE[j] < s, 0 otherwise.
90
         sample += CT::Mask<uint16_t>::is_lt(constants.cdf_table_at(j), prnd).if_set_return(1);
143,301,120✔
91
      }
92
      // Assuming that sign is either 0 or 1, flips sample iff sign = 1
93
      const uint16_t sample_u16 = static_cast<uint16_t>(sample);
16,569,088✔
94

95
      elem = sign.select(~sample_u16 + 1, sample_u16);
16,569,088✔
96
   }
97

98
   return FrodoMatrix(dimensions, std::move(elements));
2,796✔
99
}
2,796✔
100

101
std::function<FrodoMatrix(const FrodoMatrix::Dimensions& dimensions)> FrodoMatrix::make_sample_generator(
1,062✔
102
   const FrodoKEMConstants& constants, Botan::XOF& shake) {
103
   return [&constants, &shake](const FrodoMatrix::Dimensions& dimensions) mutable {
3,858✔
104
      return sample(constants,
2,796✔
105
                    dimensions,
106
                    shake.output<FrodoSampleR>(sizeof(uint16_t) * std::get<0>(dimensions) * std::get<1>(dimensions)));
5,592✔
107
   };
1,062✔
108
}
109

110
FrodoMatrix::FrodoMatrix(Dimensions dims) :
×
111
      m_dim1(std::get<0>(dims)), m_dim2(std::get<1>(dims)), m_elements(make_elements_vector(dims)) {}
×
112

113
FrodoMatrix FrodoMatrix::mul_add_as_plus_e(const FrodoKEMConstants& constants,
354✔
114
                                           const FrodoMatrix& s,
115
                                           const FrodoMatrix& e,
116
                                           StrongSpan<const FrodoSeedA> seed_a) {
117
   BOTAN_ASSERT(std::get<0>(e.dimensions()) == std::get<1>(s.dimensions()) &&
354✔
118
                   std::get<1>(e.dimensions()) == std::get<0>(s.dimensions()),
119
                "FrodoMatrix dimension mismatch of E and S");
120
   BOTAN_ASSERT(std::get<0>(e.dimensions()) == constants.n() && std::get<1>(e.dimensions()) == constants.n_bar(),
354✔
121
                "FrodoMatrix dimension mismatch of new matrix dimensions and E");
122

123
   auto elements = make_elements_vector(e.dimensions());
354✔
124
   auto row_generator = make_row_generator(constants, seed_a);
354✔
125

126
   /*
127
   We perform 4 invocations of SHAKE128 per iteration to obtain n 16-bit values per invocation.
128
   a_row_data contains the 16-bit values of the current 4 rows. a_row_data_bytes represents the corresponding bytes.
129
   */
130
   std::vector<uint16_t> a_row_data(4 * constants.n(), 0);
354✔
131
   // TODO: maybe use std::as_bytes() instead
132
   //       (take extra care, as it produces a std::span<std::byte>)
133
   std::span<uint8_t> a_row_data_bytes(reinterpret_cast<uint8_t*>(a_row_data.data()),
354✔
134
                                       sizeof(uint16_t) * a_row_data.size());
354✔
135

136
   for(size_t i = 0; i < constants.n(); i += 4) {
86,870✔
137
      auto a_row = BufferStuffer(a_row_data_bytes);
86,516✔
138

139
      // Do 4 invocations to fill 4 rows
140
      row_generator(a_row.next(constants.n() * sizeof(uint16_t)), static_cast<uint16_t>(i + 0));
86,516✔
141
      row_generator(a_row.next(constants.n() * sizeof(uint16_t)), static_cast<uint16_t>(i + 1));
86,516✔
142
      row_generator(a_row.next(constants.n() * sizeof(uint16_t)), static_cast<uint16_t>(i + 2));
86,516✔
143
      row_generator(a_row.next(constants.n() * sizeof(uint16_t)), static_cast<uint16_t>(i + 3));
86,516✔
144

145
      // Use generated bytes to fill 16-bit data
146
      load_le<uint16_t>(a_row_data.data(), a_row_data_bytes.data(), 4 * constants.n());
86,516✔
147

148
      for(size_t k = 0; k < constants.n_bar(); ++k) {
778,644✔
149
         std::array<uint16_t, 4> sum = {0};
150
         for(size_t j = 0; j < constants.n(); ++j) {  // Matrix-vector multiplication
734,957,984✔
151
            // Note: we use uint32_t for `sp` to avoid an integral promotion to `int`
152
            //       when multiplying `sp` with other row values. Otherwise we might
153
            //       suffer from undefined behaviour due to a signed integer overflow.
154
            // See:  https://learn.microsoft.com/en-us/cpp/cpp/standard-conversions#integral-promotions
155
            const uint32_t sp = s.elements_at(k * constants.n() + j);
734,265,856✔
156

157
            // Go through four lines with same sp
158
            sum.at(0) += static_cast<uint16_t>(a_row_data.at(0 * constants.n() + j) * sp);
734,265,856✔
159
            sum.at(1) += static_cast<uint16_t>(a_row_data.at(1 * constants.n() + j) * sp);
734,265,856✔
160
            sum.at(2) += static_cast<uint16_t>(a_row_data.at(2 * constants.n() + j) * sp);
734,265,856✔
161
            sum.at(3) += static_cast<uint16_t>(a_row_data.at(3 * constants.n() + j) * sp);
734,265,856✔
162
         }
163
         elements.at((i + 0) * constants.n_bar() + k) = e.elements_at((i + 0) * constants.n_bar() + k) + sum.at(0);
692,128✔
164
         elements.at((i + 3) * constants.n_bar() + k) = e.elements_at((i + 3) * constants.n_bar() + k) + sum.at(3);
692,128✔
165
         elements.at((i + 2) * constants.n_bar() + k) = e.elements_at((i + 2) * constants.n_bar() + k) + sum.at(2);
692,128✔
166
         elements.at((i + 1) * constants.n_bar() + k) = e.elements_at((i + 1) * constants.n_bar() + k) + sum.at(1);
692,128✔
167
      }
168
   }
169

170
   return FrodoMatrix(e.dimensions(), std::move(elements));
708✔
171
}
708✔
172

173
FrodoMatrix FrodoMatrix::mul_add_sa_plus_e(const FrodoKEMConstants& constants,
696✔
174
                                           const FrodoMatrix& s,
175
                                           const FrodoMatrix& e,
176
                                           StrongSpan<const FrodoSeedA> seed_a) {
177
   BOTAN_ASSERT(std::get<0>(e.dimensions()) == std::get<0>(s.dimensions()) &&
696✔
178
                   std::get<1>(e.dimensions()) == std::get<1>(s.dimensions()),
179
                "FrodoMatrix dimension mismatch of E and S");
180
   BOTAN_ASSERT(std::get<0>(e.dimensions()) == constants.n_bar() && std::get<1>(e.dimensions()) == constants.n(),
696✔
181
                "FrodoMatrix dimension mismatch of new matrix dimensions and E");
182

183
   auto elements = e.m_elements;
696✔
184
   auto row_generator = make_row_generator(constants, seed_a);
696✔
185

186
   /*
187
   We perform 8 invocations of SHAKE128 per iteration to obtain n 16-bit values per invocation.
188
   a_row_data contains the 16-bit values of the current 8 rows. a_row_data_bytes represents the corresponding bytes.
189
   */
190
   std::vector<uint16_t> a_row_data(8 * constants.n(), 0);
696✔
191
   // TODO: maybe use std::as_bytes()
192
   std::span<uint8_t> a_row_data_bytes(reinterpret_cast<uint8_t*>(a_row_data.data()),
696✔
193
                                       sizeof(uint16_t) * a_row_data.size());
696✔
194

195
   // Start matrix multiplication
196
   for(size_t i = 0; i < constants.n(); i += 8) {
86,536✔
197
      auto a_row = BufferStuffer(a_row_data_bytes);
85,840✔
198

199
      // Do 8 invocations to fill 8 rows
200
      row_generator(a_row.next(sizeof(uint16_t) * constants.n()), static_cast<uint16_t>(i + 0));
85,840✔
201
      row_generator(a_row.next(sizeof(uint16_t) * constants.n()), static_cast<uint16_t>(i + 1));
85,840✔
202
      row_generator(a_row.next(sizeof(uint16_t) * constants.n()), static_cast<uint16_t>(i + 2));
85,840✔
203
      row_generator(a_row.next(sizeof(uint16_t) * constants.n()), static_cast<uint16_t>(i + 3));
85,840✔
204
      row_generator(a_row.next(sizeof(uint16_t) * constants.n()), static_cast<uint16_t>(i + 4));
85,840✔
205
      row_generator(a_row.next(sizeof(uint16_t) * constants.n()), static_cast<uint16_t>(i + 5));
85,840✔
206
      row_generator(a_row.next(sizeof(uint16_t) * constants.n()), static_cast<uint16_t>(i + 6));
85,840✔
207
      row_generator(a_row.next(sizeof(uint16_t) * constants.n()), static_cast<uint16_t>(i + 7));
85,840✔
208

209
      // Use generated bytes to fill 16-bit data
210
      load_le<uint16_t>(a_row_data.data(), a_row_data_bytes.data(), 8 * constants.n());
85,840✔
211

212
      for(size_t j = 0; j < constants.n_bar(); ++j) {
772,560✔
213
         uint16_t sum = 0;
6,180,480✔
214
         std::array<uint32_t /* to avoid integral promotion */, 8> sp;
215
         for(size_t p = 0; p < 8; ++p) {
6,180,480✔
216
            sp[p] = s.elements_at(j * constants.n() + i + p);
5,493,760✔
217
         }
218
         for(size_t q = 0; q < constants.n(); ++q) {
735,781,504✔
219
            sum = elements.at(j * constants.n() + q);
735,094,784✔
220
            for(size_t p = 0; p < 8; ++p) {
2,147,483,647✔
221
               sum += static_cast<uint16_t>(sp[p] * a_row_data.at(p * constants.n() + q));
2,147,483,647✔
222
            }
223
            elements.at(j * constants.n() + q) = sum;
735,094,784✔
224
         }
225
      }
226
   }
227

228
   return FrodoMatrix(e.dimensions(), std::move(elements));
1,392✔
229
}
1,392✔
230

231
FrodoMatrix FrodoMatrix::mul_add_sb_plus_e(const FrodoKEMConstants& constants,
696✔
232
                                           const FrodoMatrix& b,
233
                                           const FrodoMatrix& s,
234
                                           const FrodoMatrix& e) {
235
   BOTAN_ASSERT(std::get<0>(b.dimensions()) == std::get<1>(s.dimensions()) &&
696✔
236
                   std::get<1>(b.dimensions()) == std::get<0>(s.dimensions()),
237
                "FrodoMatrix dimension mismatch of B and S");
238
   BOTAN_ASSERT(std::get<0>(b.dimensions()) == constants.n() && std::get<1>(b.dimensions()) == constants.n_bar(),
696✔
239
                "FrodoMatrix dimension mismatch of B");
240
   BOTAN_ASSERT(std::get<0>(e.dimensions()) == constants.n_bar() && std::get<1>(e.dimensions()) == constants.n_bar(),
696✔
241
                "FrodoMatrix dimension mismatch of E");
242

243
   auto elements = make_elements_vector(e.dimensions());
696✔
244

245
   for(size_t k = 0; k < constants.n_bar(); ++k) {
6,264✔
246
      for(size_t i = 0; i < constants.n_bar(); ++i) {
50,112✔
247
         elements.at(k * constants.n_bar() + i) = e.elements_at(k * constants.n_bar() + i);
44,544✔
248
         for(size_t j = 0; j < constants.n(); ++j) {
43,994,624✔
249
            elements.at(k * constants.n_bar() + i) += static_cast<uint16_t>(
43,950,080✔
250
               static_cast<uint32_t /* to avoid integral promotion */>(s.elements_at(k * constants.n() + j)) *
43,950,080✔
251
               b.elements_at(j * constants.n_bar() + i));
43,950,080✔
252
         }
253
      }
254
   }
255

256
   return FrodoMatrix(e.dimensions(), std::move(elements));
696✔
257
}
696✔
258

259
FrodoMatrix FrodoMatrix::encode(const FrodoKEMConstants& constants, StrongSpan<const FrodoPlaintext> in) {
696✔
260
   const uint64_t mask = (uint64_t(1) << constants.b()) - 1;
696✔
261

262
   const auto dimensions = std::make_tuple<size_t, size_t>(constants.n_bar(), constants.n_bar());
696✔
263
   auto elements = make_elements_vector(dimensions);
696✔
264

265
   BOTAN_ASSERT_NOMSG(in.size() * 8 == constants.n_bar() * constants.n_bar() * constants.b());
696✔
266

267
   size_t pos = 0;
268
   for(size_t i = 0; i < (constants.n_bar() * constants.n_bar()) / 8; ++i) {
6,264✔
269
      uint64_t temp = 0;
270
      for(size_t j = 0; j < constants.b(); ++j) {
22,272✔
271
         temp |= static_cast<uint64_t /* avoiding integral promotion */>(in[i * constants.b() + j]) << (8 * j);
16,704✔
272
      }
273
      for(size_t j = 0; j < 8; ++j) {
50,112✔
274
         elements.at(pos++) = static_cast<uint16_t>((temp & mask) << (constants.d() - constants.b()));  // k*2^(D-B)
44,544✔
275
         temp >>= constants.b();
44,544✔
276
      }
277
   }
278

279
   return FrodoMatrix(dimensions, std::move(elements));
696✔
280
}
696✔
281

282
FrodoMatrix FrodoMatrix::add(const FrodoKEMConstants& constants, const FrodoMatrix& a, const FrodoMatrix& b) {
696✔
283
   // Addition is defined for n_bar x n_bar matrices only
284
   BOTAN_ASSERT_NOMSG(a.dimensions() == b.dimensions());
696✔
285
   BOTAN_ASSERT_NOMSG(std::get<0>(a.dimensions()) == constants.n_bar() &&
696✔
286
                      std::get<1>(a.dimensions()) == constants.n_bar());
287

288
   auto elements = make_elements_vector(a.dimensions());
696✔
289

290
   for(size_t i = 0; i < constants.n_bar() * constants.n_bar(); ++i) {
45,240✔
291
      elements.at(i) = a.elements_at(i) + b.elements_at(i);
44,544✔
292
   }
293

294
   return FrodoMatrix(a.dimensions(), std::move(elements));
696✔
295
}
696✔
296

297
FrodoMatrix FrodoMatrix::sub(const FrodoKEMConstants& constants, const FrodoMatrix& a, const FrodoMatrix& b) {
360✔
298
   // Subtraction is defined for n_bar x n_bar matrices only
299
   BOTAN_ASSERT_NOMSG(a.dimensions() == b.dimensions());
360✔
300
   BOTAN_ASSERT_NOMSG(std::get<0>(a.dimensions()) == constants.n_bar() &&
360✔
301
                      std::get<1>(a.dimensions()) == constants.n_bar());
302

303
   auto elements = make_elements_vector(a.dimensions());
360✔
304

305
   for(size_t i = 0; i < constants.n_bar() * constants.n_bar(); ++i) {
23,400✔
306
      elements.at(i) = a.elements_at(i) - b.elements_at(i);
23,040✔
307
   }
308

309
   return FrodoMatrix(a.dimensions(), std::move(elements));
360✔
310
}
360✔
311

312
CT::Mask<uint8_t> FrodoMatrix::constant_time_compare(const FrodoMatrix& other) const {
720✔
313
   BOTAN_ASSERT_NOMSG(dimensions() == other.dimensions());
720✔
314
   // TODO: Possibly use range-based comparison after #3715 is merged
315
   return CT::is_equal(reinterpret_cast<const uint8_t*>(m_elements.data()),
1,440✔
316
                       reinterpret_cast<const uint8_t*>(other.m_elements.data()),
720✔
317
                       sizeof(decltype(m_elements)::value_type) * m_elements.size());
720✔
318
}
319

320
FrodoMatrix FrodoMatrix::mul_bs(const FrodoKEMConstants& constants, const FrodoMatrix& b, const FrodoMatrix& s) {
360✔
321
   Dimensions dimensions = {constants.n_bar(), constants.n_bar()};
360✔
322
   auto elements = make_elements_vector(dimensions);
360✔
323

324
   for(size_t i = 0; i < constants.n_bar(); ++i) {
3,240✔
325
      for(size_t j = 0; j < constants.n_bar(); ++j) {
25,920✔
326
         auto& current = elements.at(i * constants.n_bar() + j);
23,040✔
327
         current = 0;
23,040✔
328
         for(size_t k = 0; k < constants.n(); ++k) {
22,755,840✔
329
            // Explicitly store the values in 32-bit variables to avoid integral promotion
330
            const uint32_t b_ink = b.elements_at(i * constants.n() + k);
22,732,800✔
331

332
            // Since the input is s^T, we multiply the i-th row of b with the j-th row of s^t
333
            const uint32_t s_ink = s.elements_at(j * constants.n() + k);
22,732,800✔
334

335
            current += static_cast<uint16_t>(b_ink * s_ink);
22,732,800✔
336
         }
337
      }
338
   }
339

340
   return FrodoMatrix(dimensions, std::move(elements));
360✔
341
}
360✔
342

343
void FrodoMatrix::pack(const FrodoKEMConstants& constants, StrongSpan<FrodoPackedMatrix> out) const {
3,537✔
344
   const size_t outlen = packed_size(constants);
3,537✔
345
   BOTAN_ASSERT_NOMSG(out.size() == outlen);
3,537✔
346

347
   size_t i = 0;      // whole bytes already filled in
3,537✔
348
   size_t j = 0;      // whole uint16_t already copied
3,537✔
349
   uint16_t w = 0;    // the leftover, not yet copied
3,537✔
350
   uint8_t bits = 0;  // the number of lsb in w
3,537✔
351

352
   while(i < outlen && (j < element_count() || ((j == element_count()) && (bits > 0)))) {
48,994,129✔
353
      /*
354
      in: |        |        |********|********|
355
                            ^
356
                            j
357
      w : |   ****|
358
              ^
359
             bits
360
      out:|**|**|**|**|**|**|**|**|* |
361
                                  ^^
362
                                  ib
363
      */
364
      uint8_t b = 0;  // bits in out[i] already filled in
365
      while(b < 8) {
103,005,056✔
366
         const uint8_t nbits = std::min(static_cast<uint8_t>(8 - b), bits);
54,018,001✔
367
         const uint16_t mask = static_cast<uint16_t>(1 << nbits) - 1;
54,018,001✔
368
         const auto t = static_cast<uint8_t>((w >> (bits - nbits)) & mask);  // the bits to copy from w to out
54,018,001✔
369
         out[i] = out[i] + static_cast<uint8_t>(t << (8 - b - nbits));
54,018,001✔
370
         b += nbits;
54,018,001✔
371
         bits -= nbits;
54,018,001✔
372

373
         if(bits == 0) {
54,018,001✔
374
            if(j < element_count()) {
24,857,681✔
375
               w = m_elements.at(j);
24,854,144✔
376
               bits = static_cast<uint8_t>(constants.d());
24,854,144✔
377
               j++;
24,854,144✔
378
            } else {
379
               break;  // the input vector is exhausted
380
            }
381
         }
382
      }
383
      if(b == 8) {  // out[i] is filled in
48,990,592✔
384
         i++;
48,990,592✔
385
      }
386
   }
387
}
3,537✔
388

389
FrodoSerializedMatrix FrodoMatrix::serialize() const {
770✔
390
   FrodoSerializedMatrix out(2 * m_elements.size());
770✔
391

392
   for(unsigned int i = 0; i < m_elements.size(); ++i) {
5,901,570✔
393
      store_le(m_elements.at(i), out.data() + 2 * i);
5,900,800✔
394
   }
395

396
   return out;
770✔
397
}
398

399
FrodoPlaintext FrodoMatrix::decode(const FrodoKEMConstants& constants) const {
360✔
400
   const size_t nwords = (constants.n_bar() * constants.n_bar()) / 8;
360✔
401
   const uint16_t maskex = static_cast<uint16_t>(1 << constants.b()) - 1;
360✔
402
   const uint16_t maskq = static_cast<uint16_t>(1 << constants.d()) - 1;
360✔
403

404
   FrodoPlaintext out(nwords * constants.b());
360✔
405

406
   size_t index = 0;
360✔
407
   for(size_t i = 0; i < nwords; i++) {
3,240✔
408
      uint64_t templong = 0;
409
      for(size_t j = 0; j < 8; j++) {
25,920✔
410
         const auto temp =
23,040✔
411
            static_cast<uint16_t>(((m_elements.at(index) & maskq) + (1 << (constants.d() - constants.b() - 1))) >>
23,040✔
412
                                  (constants.d() - constants.b()));
23,040✔
413
         templong |= static_cast<uint64_t>(temp & maskex) << (constants.b() * j);
23,040✔
414
         index++;
23,040✔
415
      }
416
      for(size_t j = 0; j < constants.b(); j++) {
11,520✔
417
         out[i * constants.b() + j] = (templong >> (8 * j)) & 0xFF;
8,640✔
418
      }
419
   }
420

421
   return out;
360✔
422
}
×
423

424
FrodoMatrix FrodoMatrix::unpack(const FrodoKEMConstants& constants,
1,430✔
425
                                const Dimensions& dimensions,
426
                                StrongSpan<const FrodoPackedMatrix> packed_bytes) {
427
   const uint8_t lsb = static_cast<uint8_t>(constants.d());
1,430✔
428
   const size_t inlen = packed_bytes.size();
1,430✔
429
   const size_t outlen = static_cast<size_t>(std::get<0>(dimensions)) * std::get<1>(dimensions);
1,430✔
430

431
   BOTAN_ASSERT_NOMSG(inlen == ceil_tobytes(outlen * lsb));
1,430✔
432

433
   auto elements = make_elements_vector(dimensions);
1,430✔
434

435
   size_t i = 0;      // whole uint16_t already filled in
1,430✔
436
   size_t j = 0;      // whole bytes already copied
1,430✔
437
   uint8_t w = 0;     // the leftover, not yet copied
1,430✔
438
   uint8_t bits = 0;  // the number of lsb bits of w
1,430✔
439

440
   while(i < outlen && (j < inlen || ((j == inlen) && (bits > 0)))) {
8,361,878✔
441
      /*
442
      in: |  |  |  |  |  |  |**|**|...
443
                            ^
444
                            j
445
      w : | *|
446
            ^
447
            bits
448
      out:|   *****|   *****|   ***  |        |...
449
                            ^   ^
450
                            i   b
451
      */
452
      uint8_t b = 0;  // bits in out[i] already filled in
453
      while(b < lsb) {
26,507,904✔
454
         const uint8_t nbits = std::min(static_cast<uint8_t>(lsb - b), bits);
18,148,886✔
455
         const uint16_t mask = static_cast<uint16_t>(1 << nbits) - 1;
18,148,886✔
456
         uint8_t t = (w >> (bits - nbits)) & mask;  // the bits to copy from w to out
18,148,886✔
457

458
         elements.at(i) = elements.at(i) + static_cast<uint16_t>(t << (lsb - b - nbits));
18,148,886✔
459
         b += nbits;
18,148,886✔
460
         bits -= nbits;
18,148,886✔
461
         w &= static_cast<uint8_t>(~(mask << bits));  // not strictly necessary; mostly for debugging
18,148,886✔
462

463
         if(bits == 0) {
18,148,886✔
464
            if(j < inlen) {
16,484,566✔
465
               w = packed_bytes[j];
16,483,136✔
466
               bits = 8;
16,483,136✔
467
               j++;
16,483,136✔
468
            } else {
469
               break;  // the input vector is exhausted
470
            }
471
         }
472
      }
473
      if(b == lsb) {  // out[i] is filled in
8,360,448✔
474
         i++;
8,360,448✔
475
      }
476
   }
477

478
   return FrodoMatrix(dimensions, std::move(elements));
1,430✔
479
}
1,430✔
480

481
FrodoMatrix FrodoMatrix::deserialize(const Dimensions& dimensions, StrongSpan<const FrodoSerializedMatrix> bytes) {
361✔
482
   auto elements = make_elements_vector(dimensions);
361✔
483
   BOTAN_ASSERT_NOMSG(elements.size() * 2 == bytes.size());
361✔
484
   load_le<uint16_t>(elements.data(), bytes.data(), elements.size());
361✔
485
   return FrodoMatrix(dimensions, std::move(elements));
361✔
486
}
361✔
487

488
void FrodoMatrix::reduce(const FrodoKEMConstants& constants) {
720✔
489
   // Reduction is inherent if D is 16, because we use uint16_t in m_elements
490
   if(constants.d() < sizeof(decltype(m_elements)::value_type) * 8) {
720✔
491
      const uint16_t mask = static_cast<uint16_t>(1 << constants.d()) - 1;
240✔
492
      for(auto& elem : m_elements) {
622,320✔
493
         elem = elem & mask;  // mod q
622,080✔
494
      }
495
   }
496
}
720✔
497

498
}  // namespace Botan
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc