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

randombit / botan / 22017538080

14 Feb 2026 12:43PM UTC coverage: 90.069% (+0.004%) from 90.065%
22017538080

Pull #5328

github

web-flow
Merge 164f4ff86 into 5f60b6bbe
Pull Request #5328: Change Test::Result integer and bool predicates to be specifically named

102249 of 113523 relevant lines covered (90.07%)

11529820.5 hits per line

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

97.26
/src/tests/test_frodokem.cpp
1
/*
2
 * Tests for FrodoKEM ("You SHALL Pass")
3
 * - KAT tests using the KAT vectors from
4
 *   https://github.com/microsoft/PQCrypto-LWEKE/tree/master
5
 *
6
 * (C) 2023 Jack Lloyd
7
 * (C) 2023 René Meusel and Amos Treiber, Rohde & Schwarz Cybersecurity
8
 *
9
 * Botan is released under the Simplified BSD License (see license.txt)
10
 */
11

12
#include "tests.h"
13

14
#include <memory>
15

16
#if defined(BOTAN_HAS_FRODOKEM)
17
   #include "test_pubkey.h"
18
   #include "test_pubkey_pqc.h"
19
   #include "test_rng.h"
20

21
   #include <botan/frodokem.h>
22
   #include <botan/pubkey.h>
23
   #include <botan/xof.h>
24
   #include <botan/internal/frodo_constants.h>
25
#endif
26

27
namespace Botan_Tests {
28

29
#if defined(BOTAN_HAS_FRODOKEM)
30

31
namespace {
32
class Frodo_KAT_Tests final : public PK_PQC_KEM_KAT_Test {
33
   public:
34
      Frodo_KAT_Tests() : PK_PQC_KEM_KAT_Test("FrodoKEM", "pubkey/frodokem_kat.vec") {}
2✔
35

36
   private:
37
      Botan::FrodoKEMMode get_mode(const std::string& mode) const { return Botan::FrodoKEMMode(mode); }
1,800✔
38

39
      bool is_available(const std::string& mode) const final { return get_mode(mode).is_available(); }
300✔
40

41
      std::vector<uint8_t> map_value(const std::string& /*params*/,
1,200✔
42
                                     std::span<const uint8_t> value,
43
                                     VarType var_type) const final {
44
         if(var_type == VarType::SharedSecret) {
1,200✔
45
            return {value.begin(), value.end()};
300✔
46
         }
47
         auto xof = Botan::XOF::create_or_throw("SHAKE-256");
900✔
48
         xof->update(value);
900✔
49
         return xof->output<std::vector<uint8_t>>(16);
900✔
50
      }
900✔
51

52
      Fixed_Output_RNG rng_for_keygen(const std::string& mode, Botan::RandomNumberGenerator& rng) const final {
300✔
53
         const Botan::FrodoKEMConstants constants(get_mode(mode));
300✔
54
         return Fixed_Output_RNG(rng, constants.len_sec_bytes() + constants.len_se_bytes() + constants.len_a_bytes());
600✔
55
      }
300✔
56

57
      Fixed_Output_RNG rng_for_encapsulation(const std::string& mode, Botan::RandomNumberGenerator& rng) const final {
300✔
58
         const Botan::FrodoKEMConstants constants(get_mode(mode));
300✔
59
         return Fixed_Output_RNG(rng, constants.len_sec_bytes() + constants.len_salt_bytes());
600✔
60
      }
300✔
61
};
62

63
std::vector<Test::Result> test_frodo_roundtrips() {
1✔
64
   auto rng = Test::new_rng("frodokem_roundtrip");
1✔
65

66
   auto modes = std::vector{Botan::FrodoKEMMode::eFrodoKEM1344_SHAKE,
1✔
67
                            Botan::FrodoKEMMode::eFrodoKEM976_SHAKE,
68
                            Botan::FrodoKEMMode::eFrodoKEM640_SHAKE,
69
                            Botan::FrodoKEMMode::FrodoKEM1344_SHAKE,
70
                            Botan::FrodoKEMMode::FrodoKEM976_SHAKE,
71
                            Botan::FrodoKEMMode::FrodoKEM640_SHAKE,
72
                            Botan::FrodoKEMMode::eFrodoKEM1344_AES,
73
                            Botan::FrodoKEMMode::eFrodoKEM976_AES,
74
                            Botan::FrodoKEMMode::eFrodoKEM640_AES,
75
                            Botan::FrodoKEMMode::FrodoKEM1344_AES,
76
                            Botan::FrodoKEMMode::FrodoKEM976_AES,
77
                            Botan::FrodoKEMMode::FrodoKEM640_AES};
1✔
78

79
   auto get_decryption_error_value = [](Botan::FrodoKEMConstants& constants,
25✔
80
                                        std::span<const uint8_t> encaps_value,
81
                                        const Botan::FrodoKEM_PrivateKey& sk) {
82
      // Extracts the `S` value from the encoded private key
83
      auto& shake = constants.SHAKE_XOF();
24✔
84
      const auto sk_bytes = sk.raw_private_key_bits();
24✔
85
      auto sk_s = std::span<const uint8_t>(sk_bytes.data(), constants.len_sec_bytes());
24✔
86
      shake.update(encaps_value);
24✔
87
      shake.update(sk_s);
24✔
88
      return shake.output(constants.len_sec_bytes());
24✔
89
   };
24✔
90

91
   std::vector<Test::Result> results;
1✔
92
   for(auto mode : modes) {
13✔
93
      const Botan::FrodoKEMMode m(mode);
12✔
94
      if(!m.is_available()) {
12✔
95
         continue;
×
96
      }
97
      Botan::FrodoKEMConstants constants(mode);
12✔
98
      Test::Result& result = results.emplace_back("FrodoKEM roundtrip: " + m.to_string());
24✔
99

100
      const Botan::FrodoKEM_PrivateKey sk1(*rng, mode);
12✔
101
      const Botan::FrodoKEM_PublicKey pk1(sk1.public_key_bits(), mode);
12✔
102

103
      // Happy case
104
      Botan::PK_KEM_Encryptor enc1(pk1, "Raw");
12✔
105
      const auto enc_res = enc1.encrypt(*rng, 0 /* no KDF */);
12✔
106

107
      result.test_sz_eq("length of shared secret", enc_res.shared_key().size(), enc1.shared_key_length(0));
12✔
108
      result.test_sz_eq(
12✔
109
         "length of ciphertext", enc_res.encapsulated_shared_key().size(), enc1.encapsulated_key_length());
12✔
110

111
      Botan::PK_KEM_Decryptor dec1(sk1, *rng, "Raw");
12✔
112
      auto ss = dec1.decrypt(enc_res.encapsulated_shared_key(), 0 /* no KDF */);
12✔
113

114
      result.test_eq("shared secrets match", ss, enc_res.shared_key());
12✔
115
      result.test_sz_eq("length of shared secret (decaps)", ss.size(), dec1.shared_key_length(0));
12✔
116

117
      // Decryption failures ("All right then, keep your secrets.")
118
      const Botan::FrodoKEM_PrivateKey sk2(*rng, mode);
12✔
119

120
      // Decryption failure: mismatching private key
121
      Botan::PK_KEM_Decryptor dec2(sk2, *rng, "Raw");
12✔
122
      auto ss_mismatch = dec2.decrypt(enc_res.encapsulated_shared_key(), 0 /* no KDF */);
12✔
123
      result.test_eq("decryption failure sk",
12✔
124
                     ss_mismatch,
125
                     get_decryption_error_value(constants, enc_res.encapsulated_shared_key(), sk2));
12✔
126

127
      // Decryption failure: bitflip in encapsulated shared value
128
      const auto mutated_encaps_value = Test::mutate_vec(enc_res.encapsulated_shared_key(), *rng);
12✔
129
      ss_mismatch = dec2.decrypt(mutated_encaps_value, 0 /* no KDF */);
24✔
130
      result.test_eq(
12✔
131
         "decryption failure bitflip", ss_mismatch, get_decryption_error_value(constants, mutated_encaps_value, sk2));
12✔
132

133
      // Decryption failure: malformed encapsulation value
134
      result.test_throws(
24✔
135
         "malformed encapsulation value", "FrodoKEM ciphertext does not have the correct byte count", [&] {
12✔
136
            auto short_encaps_value = enc_res.encapsulated_shared_key();
12✔
137
            short_encaps_value.pop_back();
12✔
138
            dec1.decrypt(short_encaps_value, 0);
12✔
139
         });
×
140
   }
36✔
141

142
   return results;
1✔
143
}
2✔
144

145
class Frodo_Keygen_Tests final : public PK_Key_Generation_Test {
1✔
146
   public:
147
      std::vector<std::string> keygen_params() const override {
1✔
148
         return {
1✔
149
   #if defined(BOTAN_HAS_FRODOKEM_SHAKE)
150
            "FrodoKEM-640-SHAKE", "FrodoKEM-976-SHAKE", "eFrodoKEM-640-SHAKE", "eFrodoKEM-976-SHAKE",
151
   #endif
152
   #if defined(BOTAN_HAS_FRODOKEM_AES)
153
               "FrodoKEM-640-AES", "FrodoKEM-976-AES", "eFrodoKEM-640-AES", "eFrodoKEM-976-AES",
154
   #endif
155
         };
1✔
156
      }
157

158
      std::string algo_name() const override { return "FrodoKEM"; }
8✔
159

160
      std::unique_ptr<Botan::Public_Key> public_key_from_raw(std::string_view keygen_params,
8✔
161
                                                             std::string_view /* provider */,
162
                                                             std::span<const uint8_t> raw_pk) const override {
163
         return std::make_unique<Botan::FrodoKEM_PublicKey>(raw_pk, Botan::FrodoKEMMode(keygen_params));
8✔
164
      }
165
};
166

167
}  // namespace
168

169
BOTAN_REGISTER_TEST("frodokem", "frodo_kat_tests", Frodo_KAT_Tests);
170
BOTAN_REGISTER_TEST_FN("frodokem", "frodo_roundtrips", test_frodo_roundtrips);
171
BOTAN_REGISTER_TEST("frodokem", "frodo_keygen", Frodo_Keygen_Tests);
172

173
#endif
174

175
}  // namespace Botan_Tests
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

© 2026 Coveralls, Inc