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

randombit / botan / 28002582618

22 Jun 2026 07:30PM UTC coverage: 89.368% (+0.007%) from 89.361%
28002582618

push

github

web-flow
Merge pull request #5685 from randombit/jack/support-1950-timepoint

Extend calendar_point/ASN1_Time to support timepoint conversions for 1950-1970

111874 of 125184 relevant lines covered (89.37%)

11150060.87 hits per line

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

94.98
/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 "tests.h"
8

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

19
namespace Botan_Tests {
20

21
namespace {
22

23
#if defined(BOTAN_HAS_RSA)
24

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

200
   #if defined(BOTAN_HAS_EMSA_RAW)
201

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

210
         Botan::PK_Signer signer(
1✔
211
            rsa, this->rng(), "Raw", Botan::Signature_Format::Standard, "base");  // don't try this at home
1✔
212
         Botan::PK_Verifier verifier(rsa, "Raw", Botan::Signature_Format::Standard, "base");
1✔
213

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

218
            signer.update(input);
384✔
219

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

223
            result.test_is_true("Signature verifies", verifier.verify_message(input, signature));
384✔
224
         }
384✔
225
   #endif
226

227
   #if defined(BOTAN_HAS_EME_RAW)
228

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

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

239
         /*
240
         Test blinding reinit interval
241

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

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

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

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

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

260
            result.test_bin_eq("Successful decryption", plaintext, input);
64✔
261
         }
64✔
262

263
         result.test_is_false("RNG is no longer seeded", fixed_rng.is_seeded());
1✔
264

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

274
   #endif
275

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

280
class RSA_ISO9796_Roundtrip_Tests final : public Test {
1✔
281
   public:
282
      std::vector<Test::Result> run() override {
1✔
283
         Test::Result result("RSA ISO-9796 sign/verify roundtrip");
1✔
284

285
         try {
1✔
286
            const Botan::RSA_PrivateKey rsa(this->rng(), 1024);
1✔
287

288
            // A leading-zero recovered representative occurs about 1/128 of the
289
            // time, so iterate enough that the roundtrip reliably exercises it.
290
            constexpr size_t iterations = 2000;
1✔
291

292
            for(const std::string padding : {"ISO_9796_DS2(SHA-256)", "ISO_9796_DS3(SHA-256)"}) {
3✔
293
               Botan::PK_Signer signer(rsa, this->rng(), padding);
2✔
294
               Botan::PK_Verifier verifier(rsa, padding);
2✔
295

296
               size_t verified = 0;
2✔
297
               for(size_t i = 0; i != iterations; ++i) {
4,002✔
298
                  const auto msg = rng().random_vec<std::vector<uint8_t>>(i);
4,000✔
299

300
                  const auto sig = signer.sign_message(msg, this->rng());
4,000✔
301
                  if(verifier.verify_message(msg, sig)) {
4,000✔
302
                     verified += 1;
4,000✔
303
                  }
304
               }
4,000✔
305

306
               result.test_sz_eq(padding + " signatures all verify", verified, iterations);
2✔
307
            }
2✔
308

309
            // ISO-9796-2 DS2/DS3 are message-recovery schemes: the verifier
310
            // must split the message at the same capacity the encoder used. A
311
            // modulus whose bit-length is not a multiple of 8 and a message that
312
            // fills the recoverable region exercise a capacity calculation that
313
            // previously differed by one byte between the two sides. Byte-aligned
314
            // moduli (e.g. 1024 above) hide it, so sweep every residue mod 8.
315
            const Botan::BigInt e = Botan::BigInt::from_u64(65537);
1✔
316
            for(size_t mod_bits = 1025; mod_bits <= 1031; ++mod_bits) {
8✔
317
               const size_t p_bits = (mod_bits + 1) / 2;
7✔
318
               const size_t q_bits = mod_bits - p_bits;
7✔
319
               const Botan::BigInt p = Botan::generate_rsa_prime(this->rng(), this->rng(), p_bits, e);
7✔
320
               const Botan::BigInt q = Botan::generate_rsa_prime(this->rng(), this->rng(), q_bits, e);
7✔
321

322
               const Botan::RSA_PrivateKey rsa_unaligned(p, q, e);
7✔
323
               if(!result.test_sz_eq("modulus has expected bit length", rsa_unaligned.key_length(), mod_bits)) {
7✔
324
                  continue;
×
325
               }
326

327
               for(const std::string padding : {"ISO_9796_DS2(SHA-256)", "ISO_9796_DS3(SHA-256)"}) {
21✔
328
                  Botan::PK_Signer signer(rsa_unaligned, this->rng(), padding);
14✔
329
                  Botan::PK_Verifier verifier(rsa_unaligned, padding);
14✔
330

331
                  // Check inputs under, at and above the recoverable capacity
332
                  for(size_t msg_len = 0; msg_len != 1024; ++msg_len) {
14,350✔
333
                     const auto msg = this->rng().random_vec<std::vector<uint8_t>>(msg_len);
14,336✔
334
                     const auto sig = signer.sign_message(msg, this->rng());
14,336✔
335
                     result.test_is_true(
28,672✔
336
                        Botan::fmt(
14,336✔
337
                           "{} verifies recovery message of length {} (modulus {} bits)", padding, msg_len, mod_bits),
338
                        verifier.verify_message(msg, sig));
14,336✔
339
                  }
14,336✔
340
               }
14✔
341
            }
7✔
342
         } catch(const Botan::Lookup_Error& e) {
1✔
343
            result.note_missing(e.what());
×
344
         }
×
345

346
         return {result};
3✔
347
      }
4✔
348
};
349

350
class RSA_DecryptOrRandom_Tests : public Test {
1✔
351
   public:
352
      std::vector<Test::Result> run() override {
1✔
353
         const std::vector<std::string> padding_schemes = {
1✔
354
   #if defined(BOTAN_HAS_EME_PKCS1)
355
            "PKCS1v15",
356
   #endif
357
   #if defined(BOTAN_HAS_EME_OAEP)
358
            "OAEP(SHA-256)",
359
   #endif
360
         };
1✔
361

362
         constexpr size_t bits = 1024;
1✔
363

364
         auto private_key = Botan::RSA_PrivateKey(rng(), bits);
1✔
365

366
         std::vector<Test::Result> results;
1✔
367
         for(const auto& padding : padding_schemes) {
3✔
368
            Test::Result result("RSA decrypt_or_random " + padding);
2✔
369
            test_decrypt_or_random(result, padding, private_key, rng());
2✔
370
            results.push_back(result);
2✔
371
         }
2✔
372
         return results;
1✔
373
      }
1✔
374

375
   private:
376
      static void test_decrypt_or_random(Test::Result& result,
2✔
377
                                         std::string_view padding,
378
                                         Botan::Private_Key& private_key,
379
                                         Botan::RandomNumberGenerator& rng) {
380
         constexpr size_t trials = 100;
2✔
381
         constexpr size_t pt_len = 32;
2✔
382

383
         auto public_key = private_key.public_key();
2✔
384
         const auto msg = rng.random_vec(pt_len);
2✔
385

386
         const Botan::PK_Encryptor_EME enc(*public_key, rng, padding);
2✔
387
         const auto ctext = enc.encrypt(msg, rng);
2✔
388

389
         const Botan::PK_Decryptor_EME dec(private_key, rng, padding);
2✔
390

391
         const BigInt modulus = public_key->get_int_field("n");
2✔
392
         const size_t modulus_bytes = modulus.bytes();
2✔
393

394
         for(size_t i = 0; i != trials; ++i) {
202✔
395
            auto bad_ctext = (BigInt::from_bytes(mutate_vec(ctext, rng, false, 0)) % modulus).serialize(modulus_bytes);
200✔
396

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

399
            result.test_sz_eq("Returns a ciphertext of expected length", rec.size(), pt_len);
200✔
400
         }
200✔
401

402
         // Test decrypt_or_random with content check happy path
403
         for(size_t i = 1; i != pt_len; ++i) {
64✔
404
            const size_t req_bytes = i;
62✔
405

406
            std::vector<uint8_t> required_contents(req_bytes);
62✔
407
            std::vector<uint8_t> required_offsets(req_bytes);
62✔
408

409
            for(size_t j = 0; j != req_bytes; ++j) {
1,054✔
410
               const uint8_t idx = rng.next_byte() % pt_len;
992✔
411
               required_contents[j] = msg[idx];
992✔
412
               required_offsets[j] = idx;
992✔
413
            }
414

415
            auto rec = dec.decrypt_or_random(
62✔
416
               ctext.data(), ctext.size(), pt_len, rng, required_contents.data(), required_offsets.data(), req_bytes);
62✔
417

418
            result.test_bin_eq("Returned the expected message", rec, msg);
62✔
419
         }
62✔
420

421
         // Test decrypt_or_random with content check error path
422
         for(size_t i = 1; i != pt_len; ++i) {
64✔
423
            const size_t req_bytes = i;
62✔
424

425
            std::vector<uint8_t> required_contents(req_bytes);
62✔
426
            std::vector<uint8_t> required_offsets(req_bytes);
62✔
427

428
            const size_t corrupted = Test::random_index(rng, req_bytes);
62✔
429
            const uint8_t corruption = rng.next_nonzero_byte();
62✔
430

431
            for(size_t j = 0; j != req_bytes; ++j) {
1,054✔
432
               const uint8_t idx = rng.next_byte() % pt_len;
992✔
433
               required_offsets[j] = idx;
992✔
434

435
               if(idx == corrupted) {
992✔
436
                  required_contents[j] = msg[idx] ^ corruption;
27✔
437
               } else {
438
                  required_contents[j] = msg[idx];
965✔
439
               }
440
            }
441

442
            auto rec = dec.decrypt_or_random(
62✔
443
               ctext.data(), ctext.size(), pt_len, rng, required_contents.data(), required_offsets.data(), req_bytes);
62✔
444

445
            result.test_bin_ne("Returned random message", rec, ctext);
62✔
446

447
            for(size_t j = 0; j != req_bytes; ++j) {
1,054✔
448
               result.test_is_true("Random message satisfies stated content requirements",
992✔
449
                                   rec[required_offsets[j]] == required_contents[j]);
992✔
450
            }
451
         }
62✔
452
      }
6✔
453
};
454

455
BOTAN_REGISTER_TEST("pubkey", "rsa_encrypt", RSA_ES_KAT_Tests);
456
BOTAN_REGISTER_TEST("pubkey", "rsa_decrypt", RSA_Decryption_KAT_Tests);
457
BOTAN_REGISTER_TEST("pubkey", "rsa_sign", RSA_Signature_KAT_Tests);
458
BOTAN_REGISTER_TEST("pubkey", "rsa_pss", RSA_PSS_KAT_Tests);
459
BOTAN_REGISTER_TEST("pubkey", "rsa_pss_raw", RSA_PSS_Raw_KAT_Tests);
460
BOTAN_REGISTER_TEST("pubkey", "rsa_verify", RSA_Signature_Verify_Tests);
461
BOTAN_REGISTER_TEST("pubkey", "rsa_verify_invalid", RSA_Signature_Verify_Invalid_Tests);
462
BOTAN_REGISTER_TEST("pubkey", "rsa_kem", RSA_KEM_Tests);
463
BOTAN_REGISTER_TEST("pubkey", "rsa_keygen", RSA_Keygen_Tests);
464
BOTAN_REGISTER_TEST("pubkey", "rsa_keygen_stability", RSA_Keygen_Stability_Tests);
465
BOTAN_REGISTER_TEST("pubkey", "rsa_keygen_badrng", RSA_Keygen_Bad_RNG_Test);
466
BOTAN_REGISTER_TEST("pubkey", "rsa_blinding", RSA_Blinding_Tests);
467
BOTAN_REGISTER_TEST("pubkey", "rsa_iso9796_roundtrip", RSA_ISO9796_Roundtrip_Tests);
468
BOTAN_REGISTER_TEST("pubkey", "rsa_decrypt_or_random", RSA_DecryptOrRandom_Tests);
469

470
#endif
471

472
}  // namespace
473

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