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

randombit / botan / 11331525401

14 Oct 2024 04:29PM UTC coverage: 91.093% (-0.03%) from 91.12%
11331525401

Pull #4291

github

web-flow
Merge f5ffe99f5 into ed74c9542
Pull Request #4291: PQC: SLH-DSA

90346 of 99180 relevant lines covered (91.09%)

9678761.99 hits per line

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

93.65
/src/lib/x509/x509_obj.cpp
1
/*
2
* X.509 SIGNED Object
3
* (C) 1999-2007,2020 Jack Lloyd
4
*
5
* Botan is released under the Simplified BSD License (see license.txt)
6
*/
7

8
#include <botan/x509_obj.h>
9

10
#include <botan/ber_dec.h>
11
#include <botan/der_enc.h>
12
#include <botan/pem.h>
13
#include <botan/pubkey.h>
14
#include <botan/internal/emsa.h>
15
#include <botan/internal/fmt.h>
16
#include <algorithm>
17
#include <sstream>
18

19
namespace Botan {
20

21
/*
22
* Read a PEM or BER X.509 object
23
*/
24
void X509_Object::load_data(DataSource& in) {
26,836✔
25
   try {
26,836✔
26
      if(ASN1::maybe_BER(in) && !PEM_Code::matches(in)) {
26,836✔
27
         BER_Decoder dec(in);
21,096✔
28
         decode_from(dec);
21,096✔
29
      } else {
21,096✔
30
         std::string got_label;
5,537✔
31
         DataSource_Memory ber(PEM_Code::decode(in, got_label));
5,614✔
32

33
         if(got_label != PEM_label()) {
4,417✔
34
            bool is_alternate = false;
72✔
35
            for(std::string_view alt_label : alternate_PEM_labels()) {
142✔
36
               if(got_label == alt_label) {
74✔
37
                  is_alternate = true;
38
                  break;
39
               }
40
            }
72✔
41

42
            if(!is_alternate) {
72✔
43
               throw Decoding_Error("Unexpected PEM label for " + PEM_label() + " of " + got_label);
350✔
44
            }
45
         }
46

47
         BER_Decoder dec(ber);
4,270✔
48
         decode_from(dec);
4,270✔
49
      }
9,884✔
50
   } catch(Decoding_Error& e) {
7,989✔
51
      throw Decoding_Error(PEM_label() + " decoding", e);
22,236✔
52
   }
7,412✔
53
}
18,644✔
54

55
void X509_Object::encode_into(DER_Encoder& to) const {
20,099✔
56
   to.start_sequence()
20,099✔
57
      .start_sequence()
20,099✔
58
      .raw_bytes(signed_body())
20,099✔
59
      .end_cons()
20,099✔
60
      .encode(signature_algorithm())
20,099✔
61
      .encode(signature(), ASN1_Type::BitString)
20,099✔
62
      .end_cons();
20,099✔
63
}
20,099✔
64

65
/*
66
* Read a BER encoded X.509 object
67
*/
68
void X509_Object::decode_from(BER_Decoder& from) {
25,646✔
69
   from.start_sequence()
50,593✔
70
      .start_sequence()
24,947✔
71
      .raw_bytes(m_tbs_bits)
24,947✔
72
      .end_cons()
24,947✔
73
      .decode(m_sig_algo)
24,947✔
74
      .decode(m_sig, ASN1_Type::BitString)
23,072✔
75
      .end_cons();
23,010✔
76

77
   force_decode();
22,721✔
78
}
18,924✔
79

80
/*
81
* Return a PEM encoded X.509 object
82
*/
83
std::string X509_Object::PEM_encode() const {
181✔
84
   return PEM_Code::encode(BER_encode(), PEM_label());
362✔
85
}
86

87
/*
88
* Return the TBS data
89
*/
90
std::vector<uint8_t> X509_Object::tbs_data() const {
17,081✔
91
   return ASN1::put_in_sequence(m_tbs_bits);
17,081✔
92
}
93

94
/*
95
* Check the signature on an object
96
*/
97
bool X509_Object::check_signature(const Public_Key& pub_key) const {
997✔
98
   const auto result = this->verify_signature(pub_key);
997✔
99
   return (result.first == Certificate_Status_Code::VERIFIED);
997✔
100
}
997✔
101

102
std::pair<Certificate_Status_Code, std::string> X509_Object::verify_signature(const Public_Key& pub_key) const {
17,126✔
103
   try {
17,126✔
104
      PK_Verifier verifier(pub_key, signature_algorithm());
17,126✔
105
      const bool valid = verifier.verify_message(tbs_data(), signature());
17,081✔
106

107
      if(valid) {
17,081✔
108
         return std::make_pair(Certificate_Status_Code::VERIFIED, verifier.hash_function());
16,607✔
109
      } else {
110
         return std::make_pair(Certificate_Status_Code::SIGNATURE_ERROR, "");
474✔
111
      }
112
   } catch(Decoding_Error&) {
17,126✔
113
      return std::make_pair(Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS, "");
44✔
114
   } catch(Algorithm_Not_Found&) {
44✔
115
      return std::make_pair(Certificate_Status_Code::SIGNATURE_ALGO_UNKNOWN, "");
×
116
   } catch(...) {
1✔
117
      // This shouldn't happen, fallback to generic signature error
118
      return std::make_pair(Certificate_Status_Code::SIGNATURE_ERROR, "");
1✔
119
   }
1✔
120
}
121

122
/*
123
* Apply the X.509 SIGNED macro
124
*/
125
std::vector<uint8_t> X509_Object::make_signed(PK_Signer& signer,
563✔
126
                                              RandomNumberGenerator& rng,
127
                                              const AlgorithmIdentifier& algo,
128
                                              const secure_vector<uint8_t>& tbs_bits) {
129
   const std::vector<uint8_t> signature = signer.sign_message(tbs_bits, rng);
563✔
130

131
   std::vector<uint8_t> output;
563✔
132
   DER_Encoder(output)
1,126✔
133
      .start_sequence()
563✔
134
      .raw_bytes(tbs_bits)
563✔
135
      .encode(algo)
563✔
136
      .encode(signature, ASN1_Type::BitString)
563✔
137
      .end_cons();
563✔
138

139
   return output;
563✔
140
}
563✔
141

142
namespace {
143

144
std::string x509_signature_padding_for(const std::string& algo_name,
294✔
145
                                       std::string_view hash_fn,
146
                                       std::string_view user_specified_padding) {
147
   if(algo_name == "DSA" || algo_name == "ECDSA" || algo_name == "ECGDSA" || algo_name == "ECKCDSA" ||
274✔
148
      algo_name == "GOST-34.10" || algo_name == "GOST-34.10-2012-256" || algo_name == "GOST-34.10-2012-512") {
483✔
149
      BOTAN_ARG_CHECK(user_specified_padding.empty() || user_specified_padding == "EMSA1",
125✔
150
                      "Invalid padding scheme for DSA-like scheme");
151

152
      return hash_fn.empty() ? "SHA-256" : std::string(hash_fn);
235✔
153
   } else if(algo_name == "RSA") {
169✔
154
      // set to PKCSv1.5 for compatibility reasons, originally it was the only option
155

156
      if(user_specified_padding.empty()) {
42✔
157
         if(hash_fn.empty()) {
40✔
158
            return "EMSA3(SHA-256)";
6✔
159
         } else {
160
            return fmt("EMSA3({})", hash_fn);
34✔
161
         }
162
      } else {
163
         if(hash_fn.empty()) {
2✔
164
            return fmt("{}(SHA-256)", user_specified_padding);
×
165
         } else {
166
            return fmt("{}({})", user_specified_padding, hash_fn);
2✔
167
         }
168
      }
169
   } else if(algo_name == "Ed25519" || algo_name == "Ed448") {
127✔
170
      return user_specified_padding.empty() ? "Pure" : std::string(user_specified_padding);
40✔
171
   } else if(algo_name.starts_with("Dilithium-")) {
87✔
172
      return user_specified_padding.empty() ? "Randomized" : std::string(user_specified_padding);
27✔
173
   } else if(algo_name == "XMSS" || algo_name == "HSS-LMS" || algo_name == "SLH-DSA") {
60✔
174
      // These algorithms do not take any padding, but if the user insists, we pass it along
175
      return std::string(user_specified_padding);
60✔
176
   } else {
177
      throw Invalid_Argument("Unknown X.509 signing key type: " + algo_name);
×
178
   }
179
}
180

181
std::string format_padding_error_message(std::string_view key_name,
1✔
182
                                         std::string_view signer_hash_fn,
183
                                         std::string_view user_hash_fn,
184
                                         std::string_view chosen_padding,
185
                                         std::string_view user_specified_padding) {
186
   std::ostringstream oss;
1✔
187

188
   oss << "Specified hash function " << user_hash_fn << " is incompatible with " << key_name;
1✔
189

190
   if(!signer_hash_fn.empty()) {
1✔
191
      oss << " chose hash function " << signer_hash_fn;
1✔
192
   }
193

194
   if(!chosen_padding.empty()) {
1✔
195
      oss << " chose padding " << chosen_padding;
×
196
   }
197
   if(!user_specified_padding.empty()) {
1✔
198
      oss << " with user specified padding " << user_specified_padding;
1✔
199
   }
200

201
   return oss.str();
2✔
202
}
1✔
203

204
}  // namespace
205

206
/*
207
* Choose a signing format for the key
208
*/
209
std::unique_ptr<PK_Signer> X509_Object::choose_sig_format(const Private_Key& key,
468✔
210
                                                          RandomNumberGenerator& rng,
211
                                                          std::string_view hash_fn,
212
                                                          std::string_view user_specified_padding) {
213
   const Signature_Format format = key.default_x509_signature_format();
468✔
214

215
   if(!user_specified_padding.empty()) {
468✔
216
      try {
176✔
217
         auto pk_signer = std::make_unique<PK_Signer>(key, rng, user_specified_padding, format);
176✔
218
         if(!hash_fn.empty() && pk_signer->hash_function() != hash_fn) {
522✔
219
            throw Invalid_Argument(format_padding_error_message(
3✔
220
               key.algo_name(), pk_signer->hash_function(), hash_fn, "", user_specified_padding));
3✔
221
         }
222
         return pk_signer;
173✔
223
      } catch(Lookup_Error&) {}
177✔
224
   }
225

226
   const std::string padding = x509_signature_padding_for(key.algo_name(), hash_fn, user_specified_padding);
294✔
227

228
   try {
294✔
229
      auto pk_signer = std::make_unique<PK_Signer>(key, rng, padding, format);
294✔
230
      if(!hash_fn.empty() && pk_signer->hash_function() != hash_fn) {
820✔
231
         throw Invalid_Argument(format_padding_error_message(
×
232
            key.algo_name(), pk_signer->hash_function(), hash_fn, padding, user_specified_padding));
×
233
      }
234
      return pk_signer;
294✔
235
   } catch(Not_Implemented&) {
294✔
236
      throw Invalid_Argument("Signatures using " + key.algo_name() + "/" + padding + " are not supported");
×
237
   }
×
238
}
294✔
239

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

© 2026 Coveralls, Inc