Coverage Report

Created: 2025-04-11 06:34

/src/botan/src/lib/x509/x509_obj.cpp
Line
Count
Source (jump to first uncovered line)
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/assert.h>
11
#include <botan/ber_dec.h>
12
#include <botan/der_enc.h>
13
#include <botan/pem.h>
14
#include <botan/pubkey.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
30.9k
void X509_Object::load_data(DataSource& in) {
25
30.9k
   try {
26
30.9k
      if(ASN1::maybe_BER(in) && !PEM_Code::matches(in)) {
27
19.9k
         BER_Decoder dec(in);
28
19.9k
         decode_from(dec);
29
19.9k
      } else {
30
10.9k
         std::string got_label;
31
10.9k
         DataSource_Memory ber(PEM_Code::decode(in, got_label));
32
33
10.9k
         if(got_label != PEM_label()) {
34
396
            bool is_alternate = false;
35
396
            for(std::string_view alt_label : alternate_PEM_labels()) {
36
396
               if(got_label == alt_label) {
37
3
                  is_alternate = true;
38
3
                  break;
39
3
               }
40
396
            }
41
42
396
            if(!is_alternate) {
43
393
               throw Decoding_Error("Unexpected PEM label for " + PEM_label() + " of " + got_label);
44
393
            }
45
396
         }
46
47
10.5k
         BER_Decoder dec(ber);
48
10.5k
         decode_from(dec);
49
10.5k
      }
50
30.9k
   } catch(Decoding_Error& e) {
51
13.0k
      throw Decoding_Error(PEM_label() + " decoding", e);
52
13.0k
   }
53
30.9k
}
54
55
17.3k
void X509_Object::encode_into(DER_Encoder& to) const {
56
17.3k
   to.start_sequence()
57
17.3k
      .start_sequence()
58
17.3k
      .raw_bytes(signed_body())
59
17.3k
      .end_cons()
60
17.3k
      .encode(signature_algorithm())
61
17.3k
      .encode(signature(), ASN1_Type::BitString)
62
17.3k
      .end_cons();
63
17.3k
}
64
65
/*
66
* Read a BER encoded X.509 object
67
*/
68
30.7k
void X509_Object::decode_from(BER_Decoder& from) {
69
30.7k
   from.start_sequence()
70
30.7k
      .start_sequence()
71
30.7k
      .raw_bytes(m_tbs_bits)
72
30.7k
      .end_cons()
73
30.7k
      .decode(m_sig_algo)
74
30.7k
      .decode(m_sig, ASN1_Type::BitString)
75
30.7k
      .end_cons();
76
77
30.7k
   force_decode();
78
30.7k
}
79
80
/*
81
* Return a PEM encoded X.509 object
82
*/
83
0
std::string X509_Object::PEM_encode() const {
84
0
   return PEM_Code::encode(BER_encode(), PEM_label());
85
0
}
86
87
/*
88
* Return the TBS data
89
*/
90
3.97k
std::vector<uint8_t> X509_Object::tbs_data() const {
91
3.97k
   return ASN1::put_in_sequence(m_tbs_bits);
92
3.97k
}
93
94
/*
95
* Check the signature on an object
96
*/
97
0
bool X509_Object::check_signature(const Public_Key& pub_key) const {
98
0
   const auto result = this->verify_signature(pub_key);
99
0
   return (result.first == Certificate_Status_Code::VERIFIED);
100
0
}
101
102
4.42k
std::pair<Certificate_Status_Code, std::string> X509_Object::verify_signature(const Public_Key& pub_key) const {
103
4.42k
   try {
104
4.42k
      PK_Verifier verifier(pub_key, signature_algorithm());
105
4.42k
      const bool valid = verifier.verify_message(tbs_data(), signature());
106
107
4.42k
      if(valid) {
108
56
         return std::make_pair(Certificate_Status_Code::VERIFIED, verifier.hash_function());
109
4.37k
      } else {
110
4.37k
         return std::make_pair(Certificate_Status_Code::SIGNATURE_ERROR, "");
111
4.37k
      }
112
4.42k
   } catch(Decoding_Error&) {
113
292
      return std::make_pair(Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS, "");
114
292
   } catch(Algorithm_Not_Found&) {
115
127
      return std::make_pair(Certificate_Status_Code::SIGNATURE_ALGO_UNKNOWN, "");
116
127
   } catch(...) {
117
      // This shouldn't happen, fallback to generic signature error
118
30
      return std::make_pair(Certificate_Status_Code::SIGNATURE_ERROR, "");
119
30
   }
120
4.42k
}
121
122
/*
123
* Apply the X.509 SIGNED macro
124
*/
125
std::vector<uint8_t> X509_Object::make_signed(PK_Signer& signer,
126
                                              RandomNumberGenerator& rng,
127
                                              const AlgorithmIdentifier& algo,
128
0
                                              const secure_vector<uint8_t>& tbs_bits) {
129
0
   const std::vector<uint8_t> signature = signer.sign_message(tbs_bits, rng);
130
131
0
   std::vector<uint8_t> output;
132
0
   DER_Encoder(output)
133
0
      .start_sequence()
134
0
      .raw_bytes(tbs_bits)
135
0
      .encode(algo)
136
0
      .encode(signature, ASN1_Type::BitString)
137
0
      .end_cons();
138
139
0
   return output;
140
0
}
141
142
namespace {
143
144
std::string x509_signature_padding_for(const std::string& algo_name,
145
                                       std::string_view hash_fn,
146
0
                                       std::string_view user_specified_padding) {
147
0
   if(algo_name == "DSA" || algo_name == "ECDSA" || algo_name == "ECGDSA" || algo_name == "ECKCDSA" ||
148
0
      algo_name == "GOST-34.10" || algo_name == "GOST-34.10-2012-256" || algo_name == "GOST-34.10-2012-512") {
149
0
      BOTAN_ARG_CHECK(user_specified_padding.empty() || user_specified_padding == "EMSA1",
150
0
                      "Invalid padding scheme for DSA-like scheme");
151
152
0
      return hash_fn.empty() ? "SHA-256" : std::string(hash_fn);
153
0
   } else if(algo_name == "RSA") {
154
      // set to PKCSv1.5 for compatibility reasons, originally it was the only option
155
156
0
      if(user_specified_padding.empty()) {
157
0
         if(hash_fn.empty()) {
158
0
            return "PKCS1v15(SHA-256)";
159
0
         } else {
160
0
            return fmt("PKCS1v15({})", hash_fn);
161
0
         }
162
0
      } else {
163
0
         if(hash_fn.empty()) {
164
0
            return fmt("{}(SHA-256)", user_specified_padding);
165
0
         } else {
166
0
            return fmt("{}({})", user_specified_padding, hash_fn);
167
0
         }
168
0
      }
169
0
   } else if(algo_name == "Ed25519" || algo_name == "Ed448") {
170
0
      return user_specified_padding.empty() ? "Pure" : std::string(user_specified_padding);
171
0
   } else if(algo_name.starts_with("Dilithium-") || algo_name == "ML-DSA") {
172
0
      return user_specified_padding.empty() ? "Randomized" : std::string(user_specified_padding);
173
0
   } else if(algo_name == "XMSS" || algo_name == "HSS-LMS" || algo_name == "SLH-DSA") {
174
      // These algorithms do not take any padding, but if the user insists, we pass it along
175
0
      return std::string(user_specified_padding);
176
0
   } else {
177
0
      throw Invalid_Argument("Unknown X.509 signing key type: " + algo_name);
178
0
   }
179
0
}
180
181
std::string format_padding_error_message(std::string_view key_name,
182
                                         std::string_view signer_hash_fn,
183
                                         std::string_view user_hash_fn,
184
                                         std::string_view chosen_padding,
185
0
                                         std::string_view user_specified_padding) {
186
0
   std::ostringstream oss;
187
188
0
   oss << "Specified hash function " << user_hash_fn << " is incompatible with " << key_name;
189
190
0
   if(!signer_hash_fn.empty()) {
191
0
      oss << " chose hash function " << signer_hash_fn;
192
0
   }
193
194
0
   if(!chosen_padding.empty()) {
195
0
      oss << " chose padding " << chosen_padding;
196
0
   }
197
0
   if(!user_specified_padding.empty()) {
198
0
      oss << " with user specified padding " << user_specified_padding;
199
0
   }
200
201
0
   return oss.str();
202
0
}
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,
210
                                                          RandomNumberGenerator& rng,
211
                                                          std::string_view hash_fn,
212
0
                                                          std::string_view user_specified_padding) {
213
0
   const Signature_Format format = key._default_x509_signature_format();
214
215
0
   if(!user_specified_padding.empty()) {
216
0
      try {
217
0
         auto pk_signer = std::make_unique<PK_Signer>(key, rng, user_specified_padding, format);
218
0
         if(!hash_fn.empty() && pk_signer->hash_function() != hash_fn) {
219
0
            throw Invalid_Argument(format_padding_error_message(
220
0
               key.algo_name(), pk_signer->hash_function(), hash_fn, "", user_specified_padding));
221
0
         }
222
0
         return pk_signer;
223
0
      } catch(Lookup_Error&) {}
224
0
   }
225
226
0
   const std::string padding = x509_signature_padding_for(key.algo_name(), hash_fn, user_specified_padding);
227
228
0
   try {
229
0
      auto pk_signer = std::make_unique<PK_Signer>(key, rng, padding, format);
230
0
      if(!hash_fn.empty() && pk_signer->hash_function() != hash_fn) {
231
0
         throw Invalid_Argument(format_padding_error_message(
232
0
            key.algo_name(), pk_signer->hash_function(), hash_fn, padding, user_specified_padding));
233
0
      }
234
0
      return pk_signer;
235
0
   } catch(Not_Implemented&) {
236
0
      throw Invalid_Argument("Signatures using " + key.algo_name() + "/" + padding + " are not supported");
237
0
   }
238
0
}
239
240
}  // namespace Botan