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

randombit / botan / 16581714815

28 Jul 2025 10:25PM UTC coverage: 90.475% (-0.2%) from 90.685%
16581714815

Pull #5021

github

web-flow
Merge 072983077 into 1eacc5b05
Pull Request #5021: Add PK_Signature_Options

99366 of 109827 relevant lines covered (90.48%)

12349417.34 hits per line

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

92.49
/src/tests/test_rsa.cpp
1
/*
2
* (C) 2014,2015 Jack Lloyd
3
*
4
* Botan is released under the Simplified BSD License (see license.txt)
5
*/
6

7
#include "test_rng.h"
8
#include "tests.h"
9

10
#if defined(BOTAN_HAS_RSA)
11
   #include "test_pubkey.h"
12
   #include <botan/rsa.h>
13
   #include <botan/internal/blinding.h>
14
   #include <botan/internal/fmt.h>
15
   #include <botan/internal/pk_options.h>
16
#endif
17

18
namespace Botan_Tests {
19

20
namespace {
21

22
#if defined(BOTAN_HAS_RSA)
23

24
std::unique_ptr<Botan::Private_Key> load_rsa_private_key(const VarMap& vars) {
563✔
25
   const BigInt p = vars.get_req_bn("P");
563✔
26
   const BigInt q = vars.get_req_bn("Q");
563✔
27
   const BigInt e = vars.get_req_bn("E");
563✔
28

29
   return std::make_unique<Botan::RSA_PrivateKey>(p, q, e);
1,126✔
30
}
563✔
31

32
std::unique_ptr<Botan::Public_Key> load_rsa_public_key(const VarMap& vars) {
574✔
33
   const BigInt n = vars.get_req_bn("N");
574✔
34
   const BigInt e = vars.get_req_bn("E");
574✔
35

36
   return std::make_unique<Botan::RSA_PublicKey>(n, e);
1,722✔
37
}
574✔
38

39
class RSA_ES_KAT_Tests final : public PK_Encryption_Decryption_Test {
40
   public:
41
      RSA_ES_KAT_Tests() : PK_Encryption_Decryption_Test("RSA", "pubkey/rsaes.vec", "E,P,Q,Msg,Ciphertext", "Nonce") {}
2✔
42

43
      std::unique_ptr<Botan::Private_Key> load_private_key(const VarMap& vars) override {
188✔
44
         return load_rsa_private_key(vars);
188✔
45
      }
46
};
47

48
class RSA_Decryption_KAT_Tests final : public PK_Decryption_Test {
49
   public:
50
      RSA_Decryption_KAT_Tests() : PK_Decryption_Test("RSA", "pubkey/rsa_decrypt.vec", "E,P,Q,Ciphertext,Msg") {}
2✔
51

52
      bool clear_between_callbacks() const override { return false; }
25✔
53

54
      std::unique_ptr<Botan::Private_Key> load_private_key(const VarMap& vars) override {
25✔
55
         return load_rsa_private_key(vars);
25✔
56
      }
57
};
58

59
class RSA_KEM_Tests final : public PK_KEM_Test {
60
   public:
61
      RSA_KEM_Tests() : PK_KEM_Test("RSA", "pubkey/rsa_kem.vec", "E,P,Q,R,C0,KDF,K") {}
2✔
62

63
      std::unique_ptr<Botan::Private_Key> load_private_key(const VarMap& vars) override {
10✔
64
         return load_rsa_private_key(vars);
10✔
65
      }
66
};
67

68
class RSA_Signature_KAT_Tests final : public PK_Signature_Generation_Test {
69
   public:
70
      RSA_Signature_KAT_Tests() :
1✔
71
            PK_Signature_Generation_Test("RSA", "pubkey/rsa_sig.vec", "E,P,Q,Msg,Signature", "Nonce") {}
2✔
72

73
      std::string default_padding(const VarMap& /*unused*/) const override { return "Raw"; }
×
74

75
      std::unique_ptr<Botan::Private_Key> load_private_key(const VarMap& vars) override {
172✔
76
         return load_rsa_private_key(vars);
172✔
77
      }
78
};
79

80
class RSA_PSS_KAT_Tests final : public PK_Signature_Generation_Test {
81
   public:
82
      RSA_PSS_KAT_Tests() :
1✔
83
            PK_Signature_Generation_Test("RSA", "pubkey/rsa_pss.vec", "P,Q,E,Hash,Nonce,Msg,Signature", "") {}
2✔
84

85
      std::string default_padding(const VarMap& vars) const override {
88✔
86
         const std::string hash_name = vars.get_req_str("Hash");
88✔
87
         const size_t salt_size = vars.get_req_bin("Nonce").size();
176✔
88
         return Botan::fmt("PSS({},MGF1,{})", hash_name, salt_size);
88✔
89
      }
88✔
90

91
      bool clear_between_callbacks() const override { return false; }
88✔
92

93
      std::unique_ptr<Botan::Private_Key> load_private_key(const VarMap& vars) override {
88✔
94
         return load_rsa_private_key(vars);
88✔
95
      }
96
};
97

98
class RSA_PSS_Raw_KAT_Tests final : public PK_Signature_Generation_Test {
99
   public:
100
      RSA_PSS_Raw_KAT_Tests() :
1✔
101
            PK_Signature_Generation_Test("RSA", "pubkey/rsa_pss_raw.vec", "P,Q,E,Hash,Nonce,Msg,Signature", "") {}
2✔
102

103
      std::string default_padding(const VarMap& vars) const override {
80✔
104
         const std::string hash_name = vars.get_req_str("Hash");
80✔
105
         const size_t salt_size = vars.get_req_bin("Nonce").size();
160✔
106
         return Botan::fmt("PSS_Raw({},MGF1,{})", hash_name, salt_size);
80✔
107
      }
80✔
108

109
      bool clear_between_callbacks() const override { return false; }
80✔
110

111
      std::unique_ptr<Botan::Private_Key> load_private_key(const VarMap& vars) override {
80✔
112
         return load_rsa_private_key(vars);
80✔
113
      }
114
};
115

116
class RSA_Signature_Verify_Tests final : public PK_Signature_Verification_Test {
117
   public:
118
      RSA_Signature_Verify_Tests() :
1✔
119
            PK_Signature_Verification_Test("RSA", "pubkey/rsa_verify.vec", "E,N,Msg,Signature") {}
2✔
120

121
      std::string default_padding(const VarMap& /*unused*/) const override { return "Raw"; }
×
122

123
      std::unique_ptr<Botan::Public_Key> load_public_key(const VarMap& vars) override {
208✔
124
         return load_rsa_public_key(vars);
208✔
125
      }
126
};
127

128
class RSA_Signature_Verify_Invalid_Tests final : public PK_Signature_NonVerification_Test {
129
   public:
130
      RSA_Signature_Verify_Invalid_Tests() :
1✔
131
            PK_Signature_NonVerification_Test("RSA", "pubkey/rsa_invalid.vec", "E,N,Msg,InvalidSignature") {}
2✔
132

133
      std::string default_padding(const VarMap& /*unused*/) const override { return "Raw"; }
×
134

135
      std::unique_ptr<Botan::Public_Key> load_public_key(const VarMap& vars) override {
366✔
136
         return load_rsa_public_key(vars);
366✔
137
      }
138
};
139

140
class RSA_Keygen_Tests final : public PK_Key_Generation_Test {
×
141
   public:
142
      std::vector<std::string> keygen_params() const override { return {"1024", "1280"}; }
1✔
143

144
      std::unique_ptr<Botan::Public_Key> public_key_from_raw(std::string_view /* keygen_params */,
×
145
                                                             std::string_view /* provider */,
146
                                                             std::span<const uint8_t> /* raw_pk */) const override {
147
         // RSA does not implement raw public key encoding
148
         return nullptr;
×
149
      }
150

151
      std::string algo_name() const override { return "RSA"; }
2✔
152
};
153

154
class RSA_Keygen_Stability_Tests final : public PK_Key_Generation_Stability_Test {
155
   public:
156
      RSA_Keygen_Stability_Tests() : PK_Key_Generation_Stability_Test("RSA", "pubkey/rsa_keygen.vec") {}
2✔
157
};
158

159
class RSA_Keygen_Bad_RNG_Test final : public Test {
×
160
   public:
161
      std::vector<Test::Result> run() override {
1✔
162
         Test::Result result("RSA keygen with bad RNG");
1✔
163

164
         /*
165
         We don't need to count requests here; actually this test
166
         is relying on the fact that the Request_Counting_RNG outputs
167
         repeating 808080...
168
         */
169
         Request_Counting_RNG rng;
1✔
170

171
         try {
1✔
172
            Botan::RSA_PrivateKey rsa(rng, 1024);
1✔
173
            result.test_failure("Generated a key with a bad RNG");
×
174
         } catch(Botan::Internal_Error& e) {
1✔
175
            result.test_success("Key generation with bad RNG failed");
1✔
176
            result.test_eq("Expected message", e.what(), "Internal error: RNG failure during RSA key generation");
1✔
177
         }
1✔
178

179
         return {result};
2✔
180
      }
2✔
181
};
182

183
class RSA_Blinding_Tests final : public Test {
×
184
   public:
185
      std::vector<Test::Result> run() override {
1✔
186
         Test::Result result("RSA blinding");
1✔
187

188
         /* This test makes only sense with the base provider, else skip it. */
189
         if(provider_filter({"base"}).empty()) {
1✔
190
            result.note_missing("base provider");
×
191
            return std::vector<Test::Result>{result};
×
192
         }
193

194
   #if defined(BOTAN_HAS_EMSA_RAW) || defined(BOTAN_HAS_EME_RAW)
195
         Botan::RSA_PrivateKey rsa(this->rng(), 1024);
1✔
196
         Botan::Null_RNG null_rng;
1✔
197
   #endif
198

199
   #if defined(BOTAN_HAS_EMSA_RAW)
200

201
         /*
202
         * The blinder chooses a new starting point Blinder::ReinitInterval
203
         * so sign several times that with a single key.
204
         *
205
         * Very small values (padding/hashing disabled, only low byte set on input)
206
         * are used as an additional test on the blinders.
207
         */
208

209
         // don't try this at home
210
         Botan::PK_Signer signer(rsa, this->rng(), Botan::PK_Signature_Options().with_hash("Raw"));
1✔
211
         Botan::PK_Verifier verifier(rsa, Botan::PK_Signature_Options().with_hash("Raw"));
1✔
212

213
         for(size_t i = 1; i <= Botan::Blinder::ReinitInterval * 6; ++i) {
385✔
214
            std::vector<uint8_t> input(16);
384✔
215
            input[input.size() - 1] = static_cast<uint8_t>(i | 1);
384✔
216

217
            signer.update(input);
384✔
218

219
            // assert RNG is not called in this situation
220
            std::vector<uint8_t> signature = signer.signature(null_rng);
384✔
221

222
            result.test_eq("Signature verifies", verifier.verify_message(input, signature), true);
768✔
223
         }
768✔
224
   #endif
225

226
   #if defined(BOTAN_HAS_EME_RAW)
227

228
         /*
229
         * The blinder chooses a new starting point Blinder::ReinitInterval
230
         * so decrypt several times that with a single key.
231
         *
232
         * Very small values (padding/hashing disabled, only low byte set on input)
233
         * are used as an additional test on the blinders.
234
         */
235

236
         Botan::PK_Encryptor_EME encryptor(rsa, this->rng(), "Raw", "base");  // don't try this at home
1✔
237

238
         /*
239
         Test blinding reinit interval
240

241
         Seed Fixed_Output_RNG only with enough bytes for the initial
242
         blinder initialization plus the exponent blinding bits which
243
         is 2*64 bits per operation.
244
         */
245
         const size_t rng_bytes = rsa.get_n().bytes() + (2 * 8 * Botan::Blinder::ReinitInterval);
1✔
246

247
         Fixed_Output_RNG fixed_rng(this->rng(), rng_bytes);
1✔
248
         Botan::PK_Decryptor_EME decryptor(rsa, fixed_rng, "Raw", "base");
1✔
249

250
         for(size_t i = 1; i <= Botan::Blinder::ReinitInterval; ++i) {
65✔
251
            std::vector<uint8_t> input(16);
64✔
252
            input[input.size() - 1] = static_cast<uint8_t>(i);
64✔
253

254
            std::vector<uint8_t> ciphertext = encryptor.encrypt(input, null_rng);
64✔
255

256
            std::vector<uint8_t> plaintext = Botan::unlock(decryptor.decrypt(ciphertext));
64✔
257
            plaintext.insert(plaintext.begin(), input.size() - 1, 0);
64✔
258

259
            result.test_eq("Successful decryption", plaintext, input);
128✔
260
         }
192✔
261

262
         result.test_eq("RNG is no longer seeded", fixed_rng.is_seeded(), false);
1✔
263

264
         // one more decryption should trigger a blinder reinitialization
265
         result.test_throws("RSA blinding reinit",
2✔
266
                            "Test error Fixed output RNG ran out of bytes, test bug?",
267
                            [&decryptor, &encryptor, &null_rng]() {
1✔
268
                               std::vector<uint8_t> ciphertext =
1✔
269
                                  encryptor.encrypt(std::vector<uint8_t>(16, 5), null_rng);
1✔
270
                               decryptor.decrypt(ciphertext);
1✔
271
                            });
×
272

273
   #endif
274

275
         return std::vector<Test::Result>{result};
2✔
276
      }
2✔
277
};
278

279
class RSA_DecryptOrRandom_Tests : public Test {
×
280
   public:
281
      std::vector<Test::Result> run() override {
1✔
282
         const std::vector<std::string> padding_schemes = {
1✔
283
   #if defined(BOTAN_HAS_EME_PKCS1)
284
            "PKCS1v15",
285
   #endif
286
   #if defined(BOTAN_HAS_EME_OAEP)
287
            "OAEP(SHA-256)",
288
   #endif
289
         };
1✔
290

291
         constexpr size_t bits = 1024;
1✔
292

293
         auto private_key = Botan::RSA_PrivateKey(rng(), bits);
1✔
294

295
         std::vector<Test::Result> results;
1✔
296
         for(const auto& padding : padding_schemes) {
3✔
297
            Test::Result result("RSA decrypt_or_random " + padding);
2✔
298
            test_decrypt_or_random(result, padding, private_key, rng());
2✔
299
            results.push_back(result);
2✔
300
         }
2✔
301
         return results;
1✔
302
      }
1✔
303

304
   private:
305
      static void test_decrypt_or_random(Test::Result& result,
2✔
306
                                         std::string_view padding,
307
                                         Botan::Private_Key& private_key,
308
                                         Botan::RandomNumberGenerator& rng) {
309
         constexpr size_t trials = 100;
2✔
310
         constexpr size_t pt_len = 32;
2✔
311

312
         auto public_key = private_key.public_key();
2✔
313
         const auto msg = rng.random_vec(pt_len);
2✔
314

315
         Botan::PK_Encryptor_EME enc(*public_key, rng, padding);
2✔
316
         const auto ctext = enc.encrypt(msg, rng);
2✔
317

318
         Botan::PK_Decryptor_EME dec(private_key, rng, padding);
2✔
319

320
         const BigInt modulus = public_key->get_int_field("n");
2✔
321

322
         for(size_t i = 0; i != trials; ++i) {
202✔
323
            auto bad_ctext = (BigInt::from_bytes(mutate_vec(ctext, rng, false, 0)) % modulus).serialize();
400✔
324

325
            auto rec = dec.decrypt_or_random(bad_ctext.data(), bad_ctext.size(), pt_len, rng);
200✔
326

327
            result.test_eq("Returns a ciphertext of expected length", rec.size(), pt_len);
400✔
328
         }
400✔
329

330
         // Test decrypt_or_random with content check happy path
331
         for(size_t i = 1; i != pt_len; ++i) {
64✔
332
            const size_t req_bytes = i;
62✔
333

334
            std::vector<uint8_t> required_contents(req_bytes);
62✔
335
            std::vector<uint8_t> required_offsets(req_bytes);
62✔
336

337
            for(size_t j = 0; j != req_bytes; ++j) {
1,054✔
338
               uint8_t idx = rng.next_byte() % pt_len;
992✔
339
               required_contents[j] = msg[idx];
992✔
340
               required_offsets[j] = idx;
992✔
341
            }
342

343
            auto rec = dec.decrypt_or_random(
62✔
344
               ctext.data(), ctext.size(), pt_len, rng, required_contents.data(), required_offsets.data(), req_bytes);
62✔
345

346
            result.test_eq("Returned the expected message", rec, msg);
124✔
347
         }
186✔
348

349
         // Test decrypt_or_random with content check error path
350
         for(size_t i = 1; i != pt_len; ++i) {
64✔
351
            const size_t req_bytes = i;
62✔
352

353
            std::vector<uint8_t> required_contents(req_bytes);
62✔
354
            std::vector<uint8_t> required_offsets(req_bytes);
62✔
355

356
            size_t corrupted = Test::random_index(rng, req_bytes);
62✔
357
            uint8_t corruption = rng.next_nonzero_byte();
62✔
358

359
            for(size_t j = 0; j != req_bytes; ++j) {
1,054✔
360
               uint8_t idx = rng.next_byte() % pt_len;
992✔
361
               required_offsets[j] = idx;
992✔
362

363
               if(idx == corrupted) {
992✔
364
                  required_contents[j] = msg[idx] ^ corruption;
24✔
365
               } else {
366
                  required_contents[j] = msg[idx];
968✔
367
               }
368
            }
369

370
            auto rec = dec.decrypt_or_random(
62✔
371
               ctext.data(), ctext.size(), pt_len, rng, required_contents.data(), required_offsets.data(), req_bytes);
62✔
372

373
            result.test_ne("Returned random message", rec, ctext);
124✔
374

375
            for(size_t j = 0; j != req_bytes; ++j) {
1,054✔
376
               result.confirm("Random message satisfies stated content requirements",
1,984✔
377
                              rec[required_offsets[j]] == required_contents[j]);
992✔
378
            }
379
         }
186✔
380
      }
8✔
381
};
382

383
BOTAN_REGISTER_TEST("pubkey", "rsa_encrypt", RSA_ES_KAT_Tests);
384
BOTAN_REGISTER_TEST("pubkey", "rsa_decrypt", RSA_Decryption_KAT_Tests);
385
BOTAN_REGISTER_TEST("pubkey", "rsa_sign", RSA_Signature_KAT_Tests);
386
BOTAN_REGISTER_TEST("pubkey", "rsa_pss", RSA_PSS_KAT_Tests);
387
BOTAN_REGISTER_TEST("pubkey", "rsa_pss_raw", RSA_PSS_Raw_KAT_Tests);
388
BOTAN_REGISTER_TEST("pubkey", "rsa_verify", RSA_Signature_Verify_Tests);
389
BOTAN_REGISTER_TEST("pubkey", "rsa_verify_invalid", RSA_Signature_Verify_Invalid_Tests);
390
BOTAN_REGISTER_TEST("pubkey", "rsa_kem", RSA_KEM_Tests);
391
BOTAN_REGISTER_TEST("pubkey", "rsa_keygen", RSA_Keygen_Tests);
392
BOTAN_REGISTER_TEST("pubkey", "rsa_keygen_stability", RSA_Keygen_Stability_Tests);
393
BOTAN_REGISTER_TEST("pubkey", "rsa_keygen_badrng", RSA_Keygen_Bad_RNG_Test);
394
BOTAN_REGISTER_TEST("pubkey", "rsa_blinding", RSA_Blinding_Tests);
395
BOTAN_REGISTER_TEST("pubkey", "rsa_decrypt_or_random", RSA_DecryptOrRandom_Tests);
396

397
#endif
398

399
}  // namespace
400

401
}  // 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