Coverage Report

Created: 2025-04-24 07:09

/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
3.16k
void oid_valid_check(std::span<const uint32_t> oid) {
27
3.16k
   BOTAN_ARG_CHECK(oid.size() >= 2, "OID too short to be valid");
28
3.16k
   BOTAN_ARG_CHECK(oid[0] <= 2, "OID root out of range");
29
3.16k
   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
3.16k
   BOTAN_ARG_CHECK(oid[1] <= 0xFFFFFFAF, "OID second arc too large");
33
3.16k
}
34
35
// returns empty on invalid
36
16
std::vector<uint32_t> parse_oid_str(std::string_view oid) {
37
16
   try {
38
16
      std::string elem;
39
16
      std::vector<uint32_t> oid_elems;
40
41
184
      for(char c : oid) {
42
184
         if(c == '.') {
43
64
            if(elem.empty()) {
44
0
               return std::vector<uint32_t>();
45
0
            }
46
64
            oid_elems.push_back(to_u32bit(elem));
47
64
            elem.clear();
48
120
         } else {
49
120
            elem += c;
50
120
         }
51
184
      }
52
53
16
      if(!elem.empty()) {
54
16
         oid_elems.push_back(to_u32bit(elem));
55
16
      }
56
57
16
      return oid_elems;
58
16
   } catch(Invalid_Argument&) {
59
      // thrown by to_u32bit
60
0
      return std::vector<uint32_t>();
61
0
   }
62
16
}
63
64
}  // namespace
65
66
//static
67
16
void OID::register_oid(const OID& oid, std::string_view name) {
68
16
   OID_Map::global_registry().add_oid(oid, name);
69
16
}
70
71
//static
72
0
std::optional<OID> OID::from_name(std::string_view name) {
73
0
   if(name.empty()) {
74
0
      throw Invalid_Argument("OID::from_name argument must be non-empty");
75
0
   }
76
77
0
   OID o = OID_Map::global_registry().str2oid(name);
78
0
   if(o.has_value()) {
79
0
      return std::optional(o);
80
0
   }
81
82
0
   return std::nullopt;
83
0
}
84
85
//static
86
0
OID OID::from_string(std::string_view str) {
87
0
   if(str.empty()) {
88
0
      throw Invalid_Argument("OID::from_string argument must be non-empty");
89
0
   }
90
91
0
   OID o = OID_Map::global_registry().str2oid(str);
92
0
   if(o.has_value()) {
93
0
      return o;
94
0
   }
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
480
OID::OID(std::initializer_list<uint32_t> init) : m_id(init) {
105
480
   oid_valid_check(m_id);
106
480
}
107
108
2.66k
OID::OID(std::vector<uint32_t>&& init) : m_id(std::move(init)) {
109
2.66k
   oid_valid_check(m_id);
110
2.66k
}
111
112
/*
113
* ASN.1 OID Constructor
114
*/
115
16
OID::OID(std::string_view oid_str) {
116
16
   if(!oid_str.empty()) {
117
16
      m_id = parse_oid_str(oid_str);
118
16
      oid_valid_check(m_id);
119
16
   }
120
16
}
121
122
/*
123
* Return this OID as a string
124
*/
125
0
std::string OID::to_string() const {
126
0
   std::ostringstream out;
127
128
0
   for(size_t i = 0; i != m_id.size(); ++i) {
129
      // avoid locale issues with integer formatting
130
0
      out << std::to_string(m_id[i]);
131
0
      if(i != m_id.size() - 1) {
132
0
         out << ".";
133
0
      }
134
0
   }
135
136
0
   return out.str();
137
0
}
138
139
0
std::string OID::to_formatted_string() const {
140
0
   std::string s = this->human_name_or_empty();
141
0
   if(!s.empty()) {
142
0
      return s;
143
0
   }
144
0
   return this->to_string();
145
0
}
146
147
16
std::string OID::human_name_or_empty() const {
148
16
   return OID_Map::global_registry().oid2str(*this);
149
16
}
150
151
0
bool OID::registered_oid() const {
152
0
   return !human_name_or_empty().empty();
153
0
}
154
155
1.35k
size_t OID::hash_code() const {
156
1.35k
   constexpr uint64_t mod = 0xffffffffffffffc5;
157
1.35k
   uint64_t hash = 0;
158
10.7k
   for(auto id : m_id) {
159
10.7k
      hash = (hash * 257 + id) % mod;
160
10.7k
   }
161
1.35k
   return static_cast<size_t>(hash);
162
1.35k
}
163
164
/*
165
* Compare two OIDs
166
*/
167
0
bool operator<(const OID& a, const OID& b) {
168
0
   const std::vector<uint32_t>& oid1 = a.get_components();
169
0
   const std::vector<uint32_t>& oid2 = b.get_components();
170
171
0
   return std::lexicographical_compare(oid1.begin(), oid1.end(), oid2.begin(), oid2.end());
172
0
}
173
174
/*
175
* DER encode an OBJECT IDENTIFIER
176
*/
177
16
void OID::encode_into(DER_Encoder& der) const {
178
16
   if(m_id.size() < 2) {
179
0
      throw Invalid_Argument("OID::encode_into: OID is invalid");
180
0
   }
181
182
64
   auto append = [](std::vector<uint8_t>& encoding, uint32_t z) {
183
64
      if(z <= 0x7F) {
184
48
         encoding.push_back(static_cast<uint8_t>(z));
185
48
      } else {
186
16
         size_t z7 = (high_bit(z) + 7 - 1) / 7;
187
188
48
         for(size_t j = 0; j != z7; ++j) {
189
32
            uint8_t zp = static_cast<uint8_t>(z >> (7 * (z7 - j - 1)) & 0x7F);
190
191
32
            if(j != z7 - 1) {
192
16
               zp |= 0x80;
193
16
            }
194
195
32
            encoding.push_back(zp);
196
32
         }
197
16
      }
198
64
   };
199
200
16
   std::vector<uint8_t> encoding;
201
202
   // We know 40 * root can't overflow because root is between 0 and 2
203
16
   auto first = BOTAN_ASSERT_IS_SOME(checked_add(40 * m_id[0], m_id[1]));
204
205
16
   append(encoding, first);
206
207
64
   for(size_t i = 2; i != m_id.size(); ++i) {
208
48
      append(encoding, m_id[i]);
209
48
   }
210
16
   der.add_object(ASN1_Type::ObjectId, ASN1_Class::Universal, encoding);
211
16
}
212
213
/*
214
* Decode a BER encoded OBJECT IDENTIFIER
215
*/
216
0
void OID::decode_from(BER_Decoder& decoder) {
217
0
   BER_Object obj = decoder.get_next_object();
218
0
   if(obj.tagging() != (ASN1_Class::Universal | ASN1_Type::ObjectId)) {
219
0
      throw BER_Bad_Tag("Error decoding OID, unknown tag", obj.tagging());
220
0
   }
221
222
0
   if(obj.length() == 0) {
223
0
      throw BER_Decoding_Error("OID encoding is too short");
224
0
   }
225
226
0
   auto consume = [](BufferSlicer& data) -> uint32_t {
227
0
      BOTAN_ASSERT_NOMSG(!data.empty());
228
0
      uint32_t b = data.take_byte();
229
230
0
      if(b > 0x7F) {
231
0
         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
0
         if(b == 0) {
237
0
            throw Decoding_Error("Leading zero byte in multibyte OID encoding");
238
0
         }
239
240
0
         while(true) {
241
0
            if(data.empty()) {
242
0
               throw Decoding_Error("Truncated OID value");
243
0
            }
244
245
0
            const uint8_t next = data.take_byte();
246
0
            const bool more = (next & 0x80);
247
0
            const uint8_t value = next & 0x7F;
248
249
0
            if((b >> (32 - 7)) != 0) {
250
0
               throw Decoding_Error("OID component overflow");
251
0
            }
252
253
0
            b = (b << 7) | value;
254
255
0
            if(!more) {
256
0
               break;
257
0
            }
258
0
         }
259
0
      }
260
261
0
      return b;
262
0
   };
263
264
0
   BufferSlicer data(obj.data());
265
0
   std::vector<uint32_t> parts;
266
0
   while(!data.empty()) {
267
0
      const uint32_t comp = consume(data);
268
269
0
      if(parts.empty()) {
270
         // divide into root and second arc
271
272
0
         const uint32_t root_arc = [](uint32_t b0) -> uint32_t {
273
0
            if(b0 < 40) {
274
0
               return 0;
275
0
            } else if(b0 < 80) {
276
0
               return 1;
277
0
            } else {
278
0
               return 2;
279
0
            }
280
0
         }(comp);
281
282
0
         parts.push_back(root_arc);
283
0
         BOTAN_ASSERT_NOMSG(comp >= 40 * root_arc);
284
0
         parts.push_back(comp - 40 * root_arc);
285
0
      } else {
286
0
         parts.push_back(comp);
287
0
      }
288
0
   }
289
290
0
   m_id = parts;
291
0
}
292
293
}  // namespace Botan