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

randombit / botan / 26076563351

18 May 2026 04:08PM UTC coverage: 89.334% (+0.02%) from 89.316%
26076563351

push

github

web-flow
Merge pull request #5598 from randombit/jack/x509-nameconstraints-uri-email

Add URI and email name constraint processing

108478 of 121430 relevant lines covered (89.33%)

11163868.82 hits per line

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

97.35
/src/tests/test_alt_name.cpp
1
/*
2
* (C) 2024 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_X509_CERTIFICATES)
10
   #include <botan/ber_dec.h>
11
   #include <botan/der_enc.h>
12
   #include <botan/pkix_types.h>
13
#endif
14

15
namespace Botan_Tests {
16

17
namespace {
18

19
#if defined(BOTAN_HAS_X509_CERTIFICATES)
20
class X509_Alt_Name_Tests final : public Test {
1✔
21
   public:
22
      std::vector<Test::Result> run() override {
1✔
23
         Test::Result result("X509 AlternativeName tests");
1✔
24

25
         const std::vector<std::string> uri_names = {
1✔
26
            "https://example.com", "https://example.org", "https://sub.example.net"};
1✔
27

28
         const std::vector<std::string> dns_names = {
1✔
29
            "dns1.example.com",
30
            "dns2.example.org",
31
            "*.wildcard.example.com",
32
         };
1✔
33

34
         const std::vector<std::string> email_names = {
1✔
35
            "test@example.org",
36
            "admin@example.com",
37
            "root@example.net",
38
         };
1✔
39

40
         const std::vector<uint32_t> ipv4_names = {
1✔
41
            0xC0A80101,
42
            0xC0A80102,
43
         };
1✔
44

45
         const std::vector<Botan::IPv6Address> ipv6_names = {
1✔
46
            Botan::IPv6Address({0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01}),
47
            Botan::IPv6Address({0x26, 0x06, 0x47, 0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01}),
48
         };
1✔
49

50
         Botan::AlternativeName alt_name;
1✔
51
         for(const auto& uri : uri_names) {
4✔
52
            alt_name.add_uri(uri);
3✔
53
         }
54
         for(const auto& dns : dns_names) {
4✔
55
            alt_name.add_dns(dns);
3✔
56
         }
57
         for(const auto ipv4 : ipv4_names) {
3✔
58
            alt_name.add_ipv4_address(ipv4);
2✔
59
         }
60
         for(const auto& ipv6 : ipv6_names) {
3✔
61
            alt_name.add_ipv6_address(ipv6);
2✔
62
         }
63
         for(const auto& email : email_names) {
4✔
64
            alt_name.add_email(email);
3✔
65
         }
66

67
         alt_name.add_other_name(Botan::OID{1, 3, 6, 1, 4, 1, 25258, 10000, 1}, Botan::ASN1_String("foof"));
1✔
68
         alt_name.add_other_name(Botan::OID{1, 3, 6, 1, 4, 1, 25258, 10000, 2}, Botan::ASN1_String("yow"));
1✔
69

70
         // Raw OtherName whose inner value is a SEQUENCE (i.e. not an ASN1_String).
71
         const Botan::OID raw_other_oid{1, 3, 6, 1, 4, 1, 25258, 10000, 3};
1✔
72
         const std::vector<uint8_t> raw_other_value = {0x30, 0x03, 0x02, 0x01, 0x2A};
1✔
73
         alt_name.add_other_name_value(raw_other_oid, raw_other_value);
1✔
74

75
         alt_name.add_registered_id(Botan::OID{1, 3, 6, 1, 4, 1, 25258, 10001, 1});
1✔
76
         alt_name.add_registered_id(Botan::OID{1, 3, 6, 1, 4, 1, 25258, 10001, 2});
1✔
77

78
         Botan::X509_DN bonus_dn1;
1✔
79
         bonus_dn1.add_attribute("X520.CommonName", "cn1");
1✔
80
         alt_name.add_dn(bonus_dn1);
1✔
81

82
         Botan::X509_DN bonus_dn2;
1✔
83
         bonus_dn2.add_attribute("X520.CommonName", "cn2");
1✔
84
         alt_name.add_dn(bonus_dn2);
1✔
85

86
         std::vector<uint8_t> der;
1✔
87
         Botan::DER_Encoder enc(der);
1✔
88
         enc.encode(alt_name);
1✔
89

90
         Botan::AlternativeName recoded;
1✔
91
         Botan::BER_Decoder dec(der);
1✔
92
         dec.decode(recoded);
1✔
93

94
         result.test_sz_eq("Expected number of domains", recoded.dns_names().size(), dns_names.size());
1✔
95
         for(const auto& name : dns_names) {
4✔
96
            // SAN dnsName entries can be wildcards, so use from_san_string.
97
            auto parsed = Botan::DNSName::from_san_string(name);
3✔
98
            result.test_is_true("DNS name parses: " + name, parsed.has_value());
3✔
99
            if(parsed.has_value()) {
3✔
100
               result.test_is_true("Has expected DNS name: " + name, recoded.dns_names().contains(*parsed));
6✔
101
            }
102
         }
3✔
103

104
         result.test_sz_eq("Expected number of URIs", recoded.uri_names().size(), uri_names.size());
1✔
105
         for(const auto& name : uri_names) {
4✔
106
            auto parsed = Botan::URI::parse(name);
3✔
107
            result.test_is_true("URI parses: " + name, parsed.has_value());
3✔
108
            if(parsed.has_value()) {
3✔
109
               result.test_is_true("Has expected URI name: " + name, recoded.uri_names().contains(*parsed));
6✔
110
            }
111
         }
3✔
112

113
         result.test_sz_eq("Expected number of email", recoded.email_addresses().size(), email_names.size());
1✔
114
         for(const auto& name : email_names) {
4✔
115
            auto parsed = Botan::EmailAddress::from_string(name);
3✔
116
            result.test_is_true("Email name parses: " + name, parsed.has_value());
3✔
117
            if(parsed.has_value()) {
3✔
118
               result.test_is_true("Has expected email name: " + name, recoded.email_addresses().contains(*parsed));
6✔
119
            }
120
         }
3✔
121

122
         result.test_sz_eq("Expected number of IPv4", recoded.ipv4_address().size(), ipv4_names.size());
1✔
123
         for(const auto ipv4 : ipv4_names) {
3✔
124
            result.test_is_true("Has expected IPv4 name", recoded.ipv4_address().contains(ipv4));
4✔
125
         }
126

127
         result.test_sz_eq("Expected number of IPv6", recoded.ipv6_address().size(), ipv6_names.size());
1✔
128
         for(const auto& ipv6 : ipv6_names) {
3✔
129
            result.test_is_true("Has expected IPv6 name", recoded.ipv6_address().contains(ipv6));
2✔
130
         }
131

132
         result.test_sz_eq("Expected number of DNs", recoded.directory_names().size(), 2);
1✔
133
         result.test_sz_eq("Expected number of Othernames", recoded.other_names().size(), 2);
1✔
134
         result.test_sz_eq("Expected number of OtherName values", recoded.other_name_values().size(), 3);
1✔
135
         result.test_sz_eq("Expected number of registeredIDs", recoded.registered_ids().size(), 2);
1✔
136

137
         // The raw-bytes OtherName roundtripped verbatim.
138
         const auto& on_set = recoded.other_name_values();
1✔
139
         auto raw_match = on_set.end();
1✔
140
         for(auto it = on_set.begin(); it != on_set.end(); ++it) {
3✔
141
            if(it->oid() == raw_other_oid) {
3✔
142
               raw_match = it;
143
               break;
144
            }
145
         }
146
         result.test_is_true("raw OtherName preserved", raw_match != on_set.end());
1✔
147
         if(raw_match != on_set.end()) {
1✔
148
            result.test_bin_eq("raw OtherName value bytes match", raw_match->value(), raw_other_value);
1✔
149
         }
150

151
         return {result};
3✔
152
      }
6✔
153
};
154

155
BOTAN_REGISTER_TEST("x509", "x509_alt_name", X509_Alt_Name_Tests);
156

157
class X509_Alt_Name_SmtpUtf8_Wire_Type_Test final : public Test {
1✔
158
   public:
159
      std::vector<Test::Result> run() override {
1✔
160
         Test::Result result("X509 AlternativeName SmtpUTF8Mailbox wire-type guard");
1✔
161

162
         std::vector<uint8_t> ia5_inner;
1✔
163
         Botan::DER_Encoder(ia5_inner).encode(Botan::ASN1_String("alice@evil.com", Botan::ASN1_Type::Ia5String));
2✔
164

165
         Botan::AlternativeName crafted;
1✔
166
         crafted.add_other_name_value(Botan::OID::from_string("PKIX.SmtpUTF8Mailbox"), ia5_inner);
1✔
167

168
         std::vector<uint8_t> der;
1✔
169
         Botan::DER_Encoder(der).encode(crafted);
1✔
170

171
         Botan::AlternativeName recoded;
1✔
172
         Botan::BER_Decoder dec(der);
1✔
173
         result.test_throws<Botan::Decoding_Error>("SmtpUTF8Mailbox with non-UTF8String inner is rejected",
1✔
174
                                                   [&] { dec.decode(recoded); });
2✔
175

176
         // Check that the valid type is accepted
177
         std::vector<uint8_t> utf8_inner;
1✔
178
         Botan::DER_Encoder(utf8_inner).encode(Botan::ASN1_String("alicé@example.com", Botan::ASN1_Type::Utf8String));
2✔
179

180
         Botan::AlternativeName ok;
1✔
181
         ok.add_other_name_value(Botan::OID::from_string("PKIX.SmtpUTF8Mailbox"), utf8_inner);
1✔
182

183
         std::vector<uint8_t> ok_der;
1✔
184
         Botan::DER_Encoder(ok_der).encode(ok);
1✔
185

186
         Botan::AlternativeName ok_recoded;
1✔
187
         Botan::BER_Decoder ok_dec(ok_der);
1✔
188
         try {
1✔
189
            ok_dec.decode(ok_recoded);
1✔
190
            result.test_sz_eq(
1✔
191
               "UTF8String inner surfaces in smtp_utf8_mailboxes", ok_recoded.smtp_utf8_mailboxes().size(), 1);
1✔
192
         } catch(const std::exception& e) {
×
193
            result.test_failure(std::string("UTF8String inner round-trip threw: ") + e.what());
×
194
         }
×
195

196
         return {result};
3✔
197
      }
2✔
198
};
199

200
BOTAN_REGISTER_TEST("x509", "x509_alt_name_smtputf8_wire_type", X509_Alt_Name_SmtpUtf8_Wire_Type_Test);
201

202
#endif
203

204
}  // namespace
205

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