Coverage Report

Created: 2024-11-21 06:38

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