Botan 3.13.0
Crypto and TLS for C&
alt_name.cpp
Go to the documentation of this file.
1/*
2* (C) 2024 Jack Lloyd
3*
4* Botan is released under the Simplified BSD License (see license.txt)
5*/
6
7#include <botan/pkix_types.h>
8
9#include <botan/ber_dec.h>
10#include <botan/der_enc.h>
11#include <botan/internal/fmt.h>
12#include <botan/internal/int_utils.h>
13#include <botan/internal/loadstor.h>
14
15namespace Botan {
16
17void AlternativeName::add_uri(std::string_view uri) {
18 if(uri.empty()) {
19 return;
20 }
21 if(auto parsed = URI::from_string(uri)) {
22 add_uri(std::move(*parsed));
23 } else {
24 throw Decoding_Error("Invalid URI in SubjectAlternativeName");
25 }
26}
27
29 m_uri.insert(std::move(uri));
30}
31
32std::set<std::string> AlternativeName::uris() const {
33 std::set<std::string> out;
34 for(const auto& uri : m_uri) {
35 out.insert(uri.original_input());
36 }
37 return out;
38}
39
40void AlternativeName::add_email(std::string_view addr) {
41 if(addr.empty()) {
42 return;
43 }
44 if(auto parsed = EmailAddress::from_string(addr)) {
45 add_email(std::move(*parsed));
46 } else {
47 throw Decoding_Error("Invalid email address in SubjectAlternativeName");
48 }
49}
50
52 m_email.insert(std::move(addr));
53}
54
55std::set<std::string> AlternativeName::email() const {
56 std::set<std::string> out;
57 for(const auto& addr : m_email) {
58 out.insert(addr.to_string());
59 }
60 return out;
61}
62
63void AlternativeName::add_dns(std::string_view dns) {
64 if(dns.empty()) {
65 return;
66 }
67 if(auto parsed = DNSName::from_san_string(dns)) {
68 add_dns(std::move(*parsed));
69 } else {
70 throw Decoding_Error("Invalid DNS name in SubjectAlternativeName");
71 }
72}
73
75 m_dns.insert(std::move(dns));
76}
77
78std::set<std::string> AlternativeName::dns() const {
79 std::set<std::string> out;
80 for(const auto& name : m_dns) {
81 out.insert(name.to_string());
82 }
83 return out;
84}
85
86void AlternativeName::add_other_name(const OID& oid, const ASN1_String& value) {
87 m_othernames.insert(std::make_pair(oid, value));
88 std::vector<uint8_t> raw;
89 DER_Encoder(raw).encode(value);
90 m_other_name_values.insert(OtherNameValue(oid, std::move(raw)));
91}
92
93void AlternativeName::add_other_name_value(const OID& oid, std::span<const uint8_t> value) {
94 m_other_name_values.insert(OtherNameValue(oid, value));
95}
96
98 m_registered_ids.insert(oid);
99}
100
102 m_dn_names.insert(dn);
103}
104
106 m_ipv4_addrs.insert(ip);
107}
108
110 m_ipv6_addrs.insert(ip);
111}
112
114 const auto sum = checked_add(m_dns.size(),
115 m_uri.size(),
116 m_email.size(),
117 m_ipv4_addrs.size(),
118 m_ipv6_addrs.size(),
119 m_dn_names.size(),
120 m_other_name_values.size(),
121 m_registered_ids.size());
122
123 BOTAN_ASSERT_NOMSG(sum.has_value());
124 return sum.value();
125}
126
128 return this->count() > 0;
129}
130
132 return this->count() == 0;
133}
134
136 der.start_sequence();
137
138 /*
139 GeneralName ::= CHOICE {
140 otherName [0] OtherName,
141 rfc822Name [1] IA5String,
142 dNSName [2] IA5String,
143 x400Address [3] ORAddress,
144 directoryName [4] Name,
145 ediPartyName [5] EDIPartyName,
146 uniformResourceIdentifier [6] IA5String,
147 iPAddress [7] OCTET STRING,
148 registeredID [8] OBJECT IDENTIFIER }
149 */
150
151 for(const auto& othername : m_other_name_values) {
152 der.start_explicit(0)
153 .encode(othername.oid())
155 .raw_bytes(othername.value())
156 .end_explicit()
157 .end_explicit();
158 }
159
160 for(const auto& addr : m_email) {
161 const ASN1_String str(addr.to_string(), ASN1_Type::Ia5String);
163 }
164
165 for(const auto& name : m_dns) {
166 const ASN1_String str(name.to_string(), ASN1_Type::Ia5String);
168 }
169
170 for(const auto& name : m_dn_names) {
171 der.add_object(ASN1_Type(4), ASN1_Class::ExplicitContextSpecific, name.DER_encode());
172 }
173
174 for(const auto& name : m_uri) {
175 const ASN1_String str(name.original_input(), ASN1_Type::Ia5String);
177 }
178
179 for(const auto& ip : m_ipv4_addrs) {
180 // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange)
181 der.add_object(ASN1_Type(7), ASN1_Class::ContextSpecific, ip.to_bytes());
182 }
183
184 for(const auto& ip : m_ipv6_addrs) {
185 // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange)
186 der.add_object(ASN1_Type(7), ASN1_Class::ContextSpecific, ip.address().data(), ip.address().size());
187 }
188
189 for(const auto& reg_id : m_registered_ids) {
190 // [8] registeredID is IMPLICIT OBJECT IDENTIFIER.
191 // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange)
193 }
194
195 der.end_cons();
196}
197
199 BER_Decoder names = source.start_sequence();
200
201 while(names.more_items()) {
202 const BER_Object obj = names.get_next_object();
203
205 BER_Decoder othername(obj, names.limits());
206
207 OID oid;
208 othername.decode(oid);
209 const BER_Object othername_value_outer = othername.get_next_object();
210 othername.verify_end();
211
212 if(!othername_value_outer.is_a(0, ASN1_Class::ExplicitContextSpecific)) {
213 throw Decoding_Error("Invalid tags on otherName value");
214 }
215
216 BER_Decoder othername_value_inner(othername_value_outer, names.limits());
217
218 const BER_Object value = othername_value_inner.get_next_object();
219 othername_value_inner.verify_end();
220
221 // Capture the inner ANY value verbatim so applications can retrieve
222 // it regardless of its ASN.1 form.
223 std::vector<uint8_t> raw_value;
224 DER_Encoder(raw_value).add_object(value.type_tag(), value.class_tag(), value.data());
225 m_other_name_values.insert(OtherNameValue{oid, std::move(raw_value)});
226
227 // Populate old string view for compatibility
229 try {
230 m_othernames.insert(std::make_pair(oid, ASN1_String(ASN1::to_string(value), value.type())));
231 } catch(const Invalid_Argument&) { // NOLINT(*-empty-catch)
232 }
233 }
234
235 if(oid == OID::from_string("PKIX.SmtpUTF8Mailbox")) {
237 throw Decoding_Error("SmtpUTF8Mailbox otherName must contain a UTF8String");
238 }
239 auto parsed_mailbox = SmtpUtf8Mailbox::from_string(ASN1::to_string(value));
240 if(!parsed_mailbox.has_value()) {
241 throw Decoding_Error("Invalid SmtpUTF8Mailbox encoding");
242 }
243 m_smtp_utf8_mailboxes.insert(std::move(*parsed_mailbox));
244 }
245 } else if(obj.is_a(1, ASN1_Class::ContextSpecific)) {
247 } else if(obj.is_a(2, ASN1_Class::ContextSpecific)) {
250 // x400Address not supported but it is a SEQUENCE so we know it cannot be empty
251 if(obj.length() == 0) {
252 throw Decoding_Error("Invalid x400Address field");
253 }
255 BER_Decoder dec(obj, names.limits());
256 X509_DN dn;
257 dec.decode(dn).verify_end();
258 this->add_dn(dn);
260 // ediPartyName not supported but it is a SEQUENCE so we know it cannot be empty
261 if(obj.length() == 0) {
262 throw Decoding_Error("Invalid ediPartyName field");
263 }
264 } else if(obj.is_a(6, ASN1_Class::ContextSpecific)) {
265 this->add_uri(ASN1::to_string(obj));
266 } else if(obj.is_a(7, ASN1_Class::ContextSpecific)) {
267 if(obj.length() == 4) {
268 const uint32_t ip = load_be<uint32_t>(obj.bits(), 0);
269 this->add_ipv4_address(ip);
270 } else if(obj.length() == 16) {
271 const IPv6Address ip(std::span<const uint8_t, 16>{obj.bits(), 16});
272 this->add_ipv6_address(ip);
273 } else {
274 throw Decoding_Error("Invalid IP constraint neither IPv4 or IPv6");
275 }
276 } else if(obj.is_a(8, ASN1_Class::ContextSpecific)) {
277 // [8] registeredID is IMPLICIT OBJECT IDENTIFIER.
278 OID oid;
280 this->add_registered_id(oid);
281 } else {
282 throw Decoding_Error(fmt("Unknown GeneralName tag {}/class {}",
283 static_cast<uint32_t>(obj.type_tag()),
284 static_cast<uint32_t>(obj.class_tag())));
285 }
286 }
287}
288
289} // namespace Botan
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
const std::string & value() const
Definition asn1_obj.h:590
static bool is_string_type(ASN1_Type tag)
Definition asn1_str.cpp:131
An "OtherName" GeneralName entry: type-id OID and the inner ANY value as raw BER.
Definition pkix_types.h:309
void add_dns(std::string_view dns)
Add a DNS name to this AlternativeName, parsing and validating the input.
Definition alt_name.cpp:63
std::set< std::string > dns() const
Definition alt_name.cpp:78
void add_ipv4_address(uint32_t ipv4)
Add an IP address to this alternative name.
Definition pkix_types.h:374
void add_email(std::string_view addr)
Add an email address to this AlternativeName, parsing and validating the input.
Definition alt_name.cpp:40
size_t count() const
Definition alt_name.cpp:113
void encode_into(DER_Encoder &to) const override
Definition alt_name.cpp:135
void add_uri(std::string_view uri)
Add a URI to this AlternativeName, parsing and validating the input.
Definition alt_name.cpp:17
void add_registered_id(const OID &oid)
Add a registeredID (RFC 5280 [8]).
Definition alt_name.cpp:97
void add_ipv6_address(const IPv6Address &ipv6)
Add an IPv6 address to this alternative name.
Definition alt_name.cpp:109
bool has_items() const
Return true if this has any names set.
Definition alt_name.cpp:127
void add_other_name_value(const OID &oid, std::span< const uint8_t > value)
Definition alt_name.cpp:93
void add_other_name(const OID &oid, const ASN1_String &value)
Add an "OtherName" identified by object identifier to this AlternativeName.
Definition alt_name.cpp:86
std::set< std::string > uris() const
Definition alt_name.cpp:32
std::set< std::string > email() const
Definition alt_name.cpp:55
void add_dn(const X509_DN &dn)
Add a directory name to this AlternativeName.
Definition alt_name.cpp:101
void decode_from(BER_Decoder &from) override
Definition alt_name.cpp:198
bool is_empty() const
Return true if this alternative name is empty (zero names).
Definition alt_name.cpp:131
BER_Object get_next_object()
Definition ber_dec.cpp:516
BER_Decoder & decode(bool &out)
Definition ber_dec.h:358
bool more_items() const
Definition ber_dec.cpp:461
Limits limits() const
Definition ber_dec.h:197
BER_Decoder & verify_end()
Definition ber_dec.cpp:471
BER_Decoder start_sequence()
Definition ber_dec.h:275
BER_Decoder & decode_implicit(BER_Object obj, T &out, ASN1_Type real_type, ASN1_Class real_class)
Definition ber_dec.h:640
size_t length() const
Definition asn1_obj.h:303
const uint8_t * bits() const
Definition asn1_obj.h:298
bool is_a(ASN1_Type type_tag, ASN1_Class class_tag) const
Definition asn1_obj.cpp:97
ASN1_Type type_tag() const
Definition asn1_obj.h:277
std::span< const uint8_t > data() const
Definition asn1_obj.h:308
ASN1_Type type() const
Definition asn1_obj.h:287
ASN1_Class get_class() const
Definition asn1_obj.h:292
ASN1_Class class_tag() const
Definition asn1_obj.h:282
DER_Encoder & add_object(ASN1_Type type_tag, ASN1_Class class_tag, const uint8_t rep[], size_t length)
Definition der_enc.cpp:285
DER_Encoder & encode_implicit(const T &value, ASN1_Type type_tag, ASN1_Class class_tag=ASN1_Class::ContextSpecific)
Definition der_enc.h:461
DER_Encoder & end_explicit()
Definition der_enc.cpp:230
DER_Encoder & start_explicit(uint16_t type_tag)
Definition der_enc.cpp:223
DER_Encoder & start_sequence()
Definition der_enc.h:86
DER_Encoder & raw_bytes(const uint8_t val[], size_t len)
Definition der_enc.cpp:237
DER_Encoder & end_cons()
Definition der_enc.cpp:208
DER_Encoder & encode(bool b)
Definition der_enc.cpp:313
static std::optional< DNSName > from_san_string(std::string_view name)
Definition dns_name.cpp:149
static std::optional< EmailAddress > from_string(std::string_view addr)
Definition email.cpp:78
static OID from_string(std::string_view str)
Definition asn1_oid.cpp:80
static std::optional< SmtpUtf8Mailbox > from_string(std::string_view addr)
Definition email.cpp:99
static std::optional< URI > from_string(std::string_view raw)
Definition uri.cpp:164
std::string to_string(const BER_Object &obj)
Definition asn1_obj.cpp:224
constexpr std::optional< T > checked_add(T a, T b)
Definition int_utils.h:19
std::string fmt(std::string_view format, const T &... args)
Definition fmt.h:53
ASN1_Type
Definition asn1_obj.h:47
constexpr auto load_be(ParamTs &&... params)
Definition loadstor.h:504