Coverage Report

Created: 2025-04-11 06:34

/src/botan/src/lib/asn1/asn1_oid.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
* ASN.1 OID
3
* (C) 1999-2007,2024 Jack Lloyd
4
*
5
* Botan is released under the Simplified BSD License (see license.txt)
6
*/
7
8
#include <botan/asn1_obj.h>
9
10
#include <botan/ber_dec.h>
11
#include <botan/der_enc.h>
12
#include <botan/internal/bit_ops.h>
13
#include <botan/internal/fmt.h>
14
#include <botan/internal/int_utils.h>
15
#include <botan/internal/oid_map.h>
16
#include <botan/internal/parsing.h>
17
#include <botan/internal/stl_util.h>
18
#include <algorithm>
19
#include <span>
20
#include <sstream>
21
22
namespace Botan {
23
24
namespace {
25
26
243k
void oid_valid_check(std::span<const uint32_t> oid) {
27
243k
   BOTAN_ARG_CHECK(oid.size() >= 2, "OID too short to be valid");
28
243k
   BOTAN_ARG_CHECK(oid[0] <= 2, "OID root out of range");
29
243k
   BOTAN_ARG_CHECK(oid[1] <= 39 || oid[0] == 2, "OID second arc too large");
30
   // This last is a limitation of using 32 bit integers when decoding
31
   // not a limitation of ASN.1 object identifiers in general
32
243k
   BOTAN_ARG_CHECK(oid[1] <= 0xFFFFFFAF, "OID second arc too large");
33
243k
}
34
35
// returns empty on invalid
36
0
std::vector<uint32_t> parse_oid_str(std::string_view oid) {
37
0
   try {
38
0
      std::string elem;
39
0
      std::vector<uint32_t> oid_elems;
40
41
0
      for(char c : oid) {
42
0
         if(c == '.') {
43
0
            if(elem.empty()) {
44
0
               return std::vector<uint32_t>();
45
0
            }
46
0
            oid_elems.push_back(to_u32bit(elem));
47
0
            elem.clear();
48
0
         } else {
49
0
            elem += c;
50
0
         }
51
0
      }
52
53
0
      if(!elem.empty()) {
54
0
         oid_elems.push_back(to_u32bit(elem));
55
0
      }
56
57
0
      return oid_elems;
58
0
   } catch(Invalid_Argument&) {
59
      // thrown by to_u32bit
60
0
      return std::vector<uint32_t>();
61
0
   }
62
0
}
63
64
}  // namespace
65
66
//static
67
0
void OID::register_oid(const OID& oid, std::string_view name) {
68
0
   OID_Map::global_registry().add_oid(oid, name);
69
0
}
70
71
//static
72
15.1k
std::optional<OID> OID::from_name(std::string_view name) {
73
15.1k
   if(name.empty()) {
74
0
      throw Invalid_Argument("OID::from_name argument must be non-empty");
75
0
   }
76
77
15.1k
   OID o = OID_Map::global_registry().str2oid(name);
78
15.1k
   if(o.has_value()) {
79
15.1k
      return std::optional(o);
80
15.1k
   }
81
82
0
   return std::nullopt;
83
15.1k
}
84
85
//static
86
132k
OID OID::from_string(std::string_view str) {
87
132k
   if(str.empty()) {
88
0
      throw Invalid_Argument("OID::from_string argument must be non-empty");
89
0
   }
90
91
132k
   OID o = OID_Map::global_registry().str2oid(str);
92
132k
   if(o.has_value()) {
93
132k
      return o;
94
132k
   }
95
96
   // Try to parse as a dotted decimal
97
0
   try {
98
0
      return OID(str);
99
0
   } catch(...) {}
100
101
0
   throw Lookup_Error(fmt("No OID associated with name '{}'", str));
102
0
}
103
104
234k
OID::OID(std::initializer_list<uint32_t> init) : m_id(init) {
105
234k
   oid_valid_check(m_id);
106
234k
}
107
108
8.65k
OID::OID(std::vector<uint32_t>&& init) : m_id(std::move(init)) {
109
8.65k
   oid_valid_check(m_id);
110
8.65k
}
111
112
/*
113
* ASN.1 OID Constructor
114
*/
115
0
OID::OID(std::string_view oid_str) {
116
0
   if(!oid_str.empty()) {
117
0
      m_id = parse_oid_str(oid_str);
118
0
      oid_valid_check(m_id);
119
0
   }
120
0
}
121
122
/*
123
* Return this OID as a string
124
*/
125
11.1k
std::string OID::to_string() const {
126
11.1k
   std::ostringstream out;
127
128
262k
   for(size_t i = 0; i != m_id.size(); ++i) {
129
      // avoid locale issues with integer formatting
130
250k
      out << std::to_string(m_id[i]);
131
250k
      if(i != m_id.size() - 1) {
132
239k
         out << ".";
133
239k
      }
134
250k
   }
135
136
11.1k
   return out.str();
137
11.1k
}
138
139
30.2k
std::string OID::to_formatted_string() const {
140
30.2k
   std::string s = this->human_name_or_empty();
141
30.2k
   if(!s.empty()) {
142
23.6k
      return s;
143
23.6k
   }
144
6.56k
   return this->to_string();
145
30.2k
}
146
147
47.3k
std::string OID::human_name_or_empty() const {
148
47.3k
   return OID_Map::global_registry().oid2str(*this);
149
47.3k
}
150
151
674
bool OID::registered_oid() const {
152
674
   return !human_name_or_empty().empty();
153
674
}
154
155
51.5k
size_t OID::hash_code() const {
156
51.5k
   constexpr uint64_t mod = 0xffffffffffffffc5;
157
51.5k
   uint64_t hash = 0;
158
501k
   for(auto id : m_id) {
159
501k
      hash = (hash * 257 + id) % mod;
160
501k
   }
161
51.5k
   return static_cast<size_t>(hash);
162
51.5k
}
163
164
/*
165
* Compare two OIDs
166
*/
167
1.31M
bool operator<(const OID& a, const OID& b) {
168
1.31M
   const std::vector<uint32_t>& oid1 = a.get_components();
169
1.31M
   const std::vector<uint32_t>& oid2 = b.get_components();
170
171
1.31M
   return std::lexicographical_compare(oid1.begin(), oid1.end(), oid2.begin(), oid2.end());
172
1.31M
}
173
174
/*
175
* DER encode an OBJECT IDENTIFIER
176
*/
177
122k
void OID::encode_into(DER_Encoder& der) const {
178
122k
   if(m_id.size() < 2) {
179
0
      throw Invalid_Argument("OID::encode_into: OID is invalid");
180
0
   }
181
182
870k
   auto append = [](std::vector<uint8_t>& encoding, uint32_t z) {
183
870k
      if(z <= 0x7F) {
184
698k
         encoding.push_back(static_cast<uint8_t>(z));
185
698k
      } else {
186
172k
         size_t z7 = (high_bit(z) + 7 - 1) / 7;
187
188
557k
         for(size_t j = 0; j != z7; ++j) {
189
385k
            uint8_t zp = static_cast<uint8_t>(z >> (7 * (z7 - j - 1)) & 0x7F);
190
191
385k
            if(j != z7 - 1) {
192
213k
               zp |= 0x80;
193
213k
            }
194
195
385k
            encoding.push_back(zp);
196
385k
         }
197
172k
      }
198
870k
   };
199
200
122k
   std::vector<uint8_t> encoding;
201
202
   // We know 40 * root can't overflow because root is between 0 and 2
203
122k
   auto first = BOTAN_ASSERT_IS_SOME(checked_add(40 * m_id[0], m_id[1]));
204
205
122k
   append(encoding, first);
206
207
870k
   for(size_t i = 2; i != m_id.size(); ++i) {
208
747k
      append(encoding, m_id[i]);
209
747k
   }
210
122k
   der.add_object(ASN1_Type::ObjectId, ASN1_Class::Universal, encoding);
211
122k
}
212
213
/*
214
* Decode a BER encoded OBJECT IDENTIFIER
215
*/
216
360k
void OID::decode_from(BER_Decoder& decoder) {
217
360k
   BER_Object obj = decoder.get_next_object();
218
360k
   if(obj.tagging() != (ASN1_Class::Universal | ASN1_Type::ObjectId)) {
219
1.64k
      throw BER_Bad_Tag("Error decoding OID, unknown tag", obj.tagging());
220
1.64k
   }
221
222
358k
   if(obj.length() == 0) {
223
333
      throw BER_Decoding_Error("OID encoding is too short");
224
333
   }
225
226
1.93M
   auto consume = [](BufferSlicer& data) -> uint32_t {
227
1.93M
      BOTAN_ASSERT_NOMSG(!data.empty());
228
1.93M
      uint32_t b = data.take_byte();
229
230
1.93M
      if(b > 0x7F) {
231
224k
         b &= 0x7F;
232
233
         // Even BER requires that the OID have minimal length, ie that
234
         // the first byte of a multibyte encoding cannot be zero
235
         // See X.690 section 8.19.2
236
224k
         if(b == 0) {
237
938
            throw Decoding_Error("Leading zero byte in multibyte OID encoding");
238
938
         }
239
240
278k
         while(true) {
241
278k
            if(data.empty()) {
242
724
               throw Decoding_Error("Truncated OID value");
243
724
            }
244
245
277k
            const uint8_t next = data.take_byte();
246
277k
            const bool more = (next & 0x80);
247
277k
            const uint8_t value = next & 0x7F;
248
249
277k
            if((b >> (32 - 7)) != 0) {
250
511
               throw Decoding_Error("OID component overflow");
251
511
            }
252
253
276k
            b = (b << 7) | value;
254
255
276k
            if(!more) {
256
222k
               break;
257
222k
            }
258
276k
         }
259
223k
      }
260
261
1.93M
      return b;
262
1.93M
   };
263
264
358k
   BufferSlicer data(obj.data());
265
358k
   std::vector<uint32_t> parts;
266
2.29M
   while(!data.empty()) {
267
1.93M
      const uint32_t comp = consume(data);
268
269
1.93M
      if(parts.empty()) {
270
         // divide into root and second arc
271
272
356k
         const uint32_t root_arc = [](uint32_t b0) -> uint32_t {
273
356k
            if(b0 < 40) {
274
58.2k
               return 0;
275
298k
            } else if(b0 < 80) {
276
112k
               return 1;
277
186k
            } else {
278
186k
               return 2;
279
186k
            }
280
356k
         }(comp);
281
282
356k
         parts.push_back(root_arc);
283
356k
         BOTAN_ASSERT_NOMSG(comp >= 40 * root_arc);
284
356k
         parts.push_back(comp - 40 * root_arc);
285
1.58M
      } else {
286
1.58M
         parts.push_back(comp);
287
1.58M
      }
288
1.93M
   }
289
290
358k
   m_id = parts;
291
358k
}
292
293
}  // namespace Botan