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

randombit / botan / 27052752106

06 Jun 2026 12:06AM UTC coverage: 89.396% (+0.002%) from 89.394%
27052752106

push

github

web-flow
Merge pull request #5649 from randombit/jack/small-hardenings

Various small hardenings and improve input validation

110625 of 123747 relevant lines covered (89.4%)

11055225.92 hits per line

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

97.04
/src/tests/test_x509_dn.cpp
1
/*
2
* (C) 2017 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/hex.h>
12
   #include <botan/pkix_types.h>
13
   #include <sstream>
14
#endif
15

16
namespace Botan_Tests {
17

18
namespace {
19

20
#if defined(BOTAN_HAS_X509_CERTIFICATES)
21
class X509_DN_Comparisons_Tests final : public Text_Based_Test {
×
22
   public:
23
      X509_DN_Comparisons_Tests() : Text_Based_Test("x509_dn.vec", "DN1,DN2") {}
2✔
24

25
      Test::Result run_one_test(const std::string& type, const VarMap& vars) override {
10✔
26
         const std::vector<uint8_t> dn_bits1 = vars.get_req_bin("DN1");
10✔
27
         const std::vector<uint8_t> dn_bits2 = vars.get_req_bin("DN2");
10✔
28

29
         const bool dn_same = (type == "Equal");
10✔
30

31
         Test::Result result("X509_DN comparisons");
10✔
32
         try {
10✔
33
            Botan::X509_DN dn1;
10✔
34
            Botan::BER_Decoder bd1(dn_bits1);
10✔
35
            dn1.decode_from(bd1);
10✔
36

37
            Botan::X509_DN dn2;
10✔
38
            Botan::BER_Decoder bd2(dn_bits2);
10✔
39
            dn2.decode_from(bd2);
10✔
40

41
            const bool compared_same = (dn1 == dn2);
10✔
42
            result.test_bool_eq("Comparison matches expected", dn_same, compared_same);
10✔
43

44
            const bool lt1 = (dn1 < dn2);
10✔
45
            const bool lt2 = (dn2 < dn1);
10✔
46

47
            if(dn_same) {
10✔
48
               result.test_is_false("same means neither is less than", lt1);
6✔
49
               result.test_is_false("same means neither is less than", lt2);
6✔
50
            } else {
51
               result.test_is_true("different means one is less than", lt1 || lt2);
4✔
52
               result.test_is_false("different means only one is less than", lt1 && lt2);
4✔
53
            }
54
         } catch(Botan::Exception& e) {
10✔
55
            result.test_failure(e.what());
×
56
         }
×
57

58
         return result;
10✔
59
      }
10✔
60
};
61

62
BOTAN_REGISTER_TEST("x509", "x509_dn_cmp", X509_DN_Comparisons_Tests);
63

64
class X509_DN_String_Tests final : public Test {
1✔
65
   public:
66
      std::vector<Test::Result> run() override {
1✔
67
         std::vector<Test::Result> results;
1✔
68
         results.push_back(test_single_ava_round_trip());
2✔
69
         results.push_back(test_multi_ava_rdn_emits_plus());
2✔
70
         results.push_back(test_multi_ava_rdn_round_trip());
2✔
71
         results.push_back(test_parse_multi_ava_rdn());
2✔
72
         results.push_back(test_mixed_single_and_multi_ava_round_trip());
2✔
73
         results.push_back(test_quoted_plus_in_value_not_split());
2✔
74
         results.push_back(test_decode_failure_leaves_dn_unchanged());
2✔
75
         return results;
1✔
76
      }
×
77

78
   private:
79
      static Botan::X509_DN parse(std::string_view s) {
6✔
80
         Botan::X509_DN dn;
6✔
81
         std::istringstream iss{std::string(s)};
12✔
82
         iss >> dn;
6✔
83
         return dn;
6✔
84
      }
6✔
85

86
      static std::string format(const Botan::X509_DN& dn) {
7✔
87
         std::ostringstream oss;
7✔
88
         oss << dn;
7✔
89
         return oss.str();
14✔
90
      }
7✔
91

92
      static Test::Result test_single_ava_round_trip() {
1✔
93
         Test::Result result("X509_DN string round-trip (single-AVA RDNs)");
1✔
94
         Botan::X509_DN dn;
1✔
95
         dn.add_attribute("X520.CommonName", "Alice");
1✔
96
         dn.add_attribute("X520.Organization", "Example");
1✔
97

98
         const std::string s = format(dn);
1✔
99
         result.test_str_eq("expected serialization", s, R"(CN="Alice",O="Example")");
1✔
100

101
         const Botan::X509_DN parsed = parse(s);
1✔
102
         result.test_sz_eq("two RDNs", parsed.count(), size_t(2));
1✔
103
         result.test_is_true("parses back to equal DN", parsed == dn);
1✔
104
         return result;
1✔
105
      }
1✔
106

107
      static Test::Result test_multi_ava_rdn_emits_plus() {
1✔
108
         Test::Result result("X509_DN string output uses '+' within RDN");
1✔
109
         Botan::X509_DN dn;
1✔
110
         dn.add_rdn({{Botan::OID::from_string("X520.CommonName"), Botan::ASN1_String("Alice")},
5✔
111
                     {Botan::OID::from_string("X520.Organization"), Botan::ASN1_String("Example")}});
2✔
112

113
         const std::string s = format(dn);
1✔
114
         result.test_str_eq("multi-AVA RDN uses '+' separator", s, R"(CN="Alice"+O="Example")");
1✔
115
         return result;
2✔
116
      }
4✔
117

118
      static Test::Result test_multi_ava_rdn_round_trip() {
1✔
119
         Test::Result result("X509_DN string round-trip (multi-AVA RDN)");
1✔
120
         Botan::X509_DN dn;
1✔
121
         dn.add_rdn({{Botan::OID::from_string("X520.CommonName"), Botan::ASN1_String("Alice")},
5✔
122
                     {Botan::OID::from_string("X520.Organization"), Botan::ASN1_String("Example")}});
2✔
123

124
         const std::string s = format(dn);
1✔
125
         const Botan::X509_DN parsed = parse(s);
1✔
126

127
         result.test_sz_eq("one RDN", parsed.count(), size_t(1));
1✔
128
         result.test_sz_eq("two AVAs in RDN", parsed.rdns().at(0).size(), size_t(2));
1✔
129
         result.test_is_true("parses back to equal DN", parsed == dn);
1✔
130
         result.test_str_eq("re-emits identical string", format(parsed), s);
1✔
131
         return result;
1✔
132
      }
4✔
133

134
      static Test::Result test_parse_multi_ava_rdn() {
1✔
135
         Test::Result result("X509_DN parses '+'-separated AVAs into one RDN");
1✔
136
         const Botan::X509_DN parsed = parse(R"(CN="Alice"+O="Example")");
1✔
137
         result.test_sz_eq("one RDN", parsed.count(), size_t(1));
1✔
138
         result.test_sz_eq("two AVAs in that RDN", parsed.rdns().at(0).size(), size_t(2));
1✔
139

140
         // ',' continues to act as the RDN separator.
141
         const Botan::X509_DN comma = parse(R"(CN="Alice",O="Example")");
1✔
142
         result.test_sz_eq("',' yields two RDNs", comma.count(), size_t(2));
1✔
143
         result.test_sz_eq("each RDN has one AVA", comma.rdns().at(0).size(), size_t(1));
1✔
144
         result.test_is_false("two distinct groupings", parsed == comma);
1✔
145
         return result;
1✔
146
      }
1✔
147

148
      static Test::Result test_mixed_single_and_multi_ava_round_trip() {
1✔
149
         Test::Result result("X509_DN string round-trip (mixed RDNs)");
1✔
150
         Botan::X509_DN dn;
1✔
151
         dn.add_attribute("X520.Country", "US");
1✔
152
         dn.add_rdn({{Botan::OID::from_string("X520.CommonName"), Botan::ASN1_String("Alice")},
5✔
153
                     {Botan::OID::from_string("X520.Organization"), Botan::ASN1_String("Example")}});
2✔
154
         dn.add_attribute("X520.OrganizationalUnit", "Eng");
1✔
155

156
         const std::string s = format(dn);
1✔
157
         result.test_str_eq("mixed RDN format", s, R"(C="US",CN="Alice"+O="Example",OU="Eng")");
1✔
158

159
         const Botan::X509_DN parsed = parse(s);
1✔
160
         result.test_sz_eq("three RDNs", parsed.count(), size_t(3));
1✔
161
         result.test_sz_eq("first is single AVA", parsed.rdns().at(0).size(), size_t(1));
1✔
162
         result.test_sz_eq("second is multi-AVA", parsed.rdns().at(1).size(), size_t(2));
1✔
163
         result.test_sz_eq("third is single AVA", parsed.rdns().at(2).size(), size_t(1));
1✔
164
         result.test_is_true("round-trips equal", parsed == dn);
1✔
165
         return result;
1✔
166
      }
4✔
167

168
      static Test::Result test_quoted_plus_in_value_not_split() {
1✔
169
         Test::Result result("X509_DN parser treats '+' inside quotes as data");
1✔
170
         const Botan::X509_DN parsed = parse(R"(CN="A+B")");
1✔
171
         result.test_sz_eq("one RDN", parsed.count(), size_t(1));
1✔
172
         result.test_sz_eq("one AVA", parsed.rdns().at(0).size(), size_t(1));
1✔
173
         result.test_str_eq("value preserved", parsed.get_first_attribute("CN"), "A+B");
1✔
174
         return result;
1✔
175
      }
1✔
176

177
      static Test::Result test_decode_failure_leaves_dn_unchanged() {
1✔
178
         Test::Result result("X509_DN decode failure leaves DN unchanged");
1✔
179

180
         Botan::X509_DN dn;
1✔
181
         dn.add_attribute("X520.CommonName", "Original");
1✔
182
         const Botan::X509_DN original = dn;
1✔
183

184
         const auto invalid_dn = Botan::hex_decode("3010310C300A06035504030C034261643100");
1✔
185
         result.test_throws("invalid empty RDN rejected", [&] {
1✔
186
            Botan::BER_Decoder bd(invalid_dn);
1✔
187
            dn.decode_from(bd);
1✔
188
         });
1✔
189

190
         result.test_str_eq("string form unchanged", format(dn), format(original));
1✔
191
         result.test_is_true("DN comparison unchanged", dn == original);
1✔
192
         return result;
1✔
193
      }
1✔
194
};
195

196
BOTAN_REGISTER_TEST("x509", "x509_dn_string", X509_DN_String_Tests);
197
#endif
198

199
}  // namespace
200

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