Botan 3.13.0
Crypto and TLS for C&
asn1_oid.cpp
Go to the documentation of this file.
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/buffer_slicer.h>
14#include <botan/internal/fmt.h>
15#include <botan/internal/int_utils.h>
16#include <botan/internal/oid_map.h>
17#include <botan/internal/parsing.h>
18#include <algorithm>
19#include <span>
20#include <sstream>
21
22namespace Botan {
23
24namespace {
25
26void oid_valid_check(std::span<const uint32_t> oid) {
27 BOTAN_ARG_CHECK(oid.size() >= 2, "OID too short to be valid");
28 BOTAN_ARG_CHECK(oid[0] <= 2, "OID root out of range");
29 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 BOTAN_ARG_CHECK(oid[1] <= 0xFFFFFFAF, "OID second arc too large");
33}
34
35// returns nullopt on invalid
36std::optional<std::vector<uint32_t>> parse_oid_str(std::string_view oid) {
37 std::vector<uint32_t> oid_elems;
38
39 for(;;) {
40 const size_t dot = oid.find('.');
41
42 if(const auto elem = parse_u32(oid.substr(0, dot))) {
43 oid_elems.push_back(*elem);
44 } else {
45 return {};
46 }
47
48 // No more dots implies we just read the last group
49 if(dot == std::string_view::npos) {
50 break;
51 }
52 oid = oid.substr(dot + 1);
53 }
54
55 return oid_elems;
56}
57
58} // namespace
59
60//static
61void OID::register_oid(const OID& oid, std::string_view name) {
63}
64
65//static
66std::optional<OID> OID::from_name(std::string_view name) {
67 if(name.empty()) {
68 throw Invalid_Argument("OID::from_name argument must be non-empty");
69 }
70
72 if(o.has_value()) {
73 return std::optional(o);
74 }
75
76 return std::nullopt;
77}
78
79//static
80OID OID::from_string(std::string_view str) {
81 if(str.empty()) {
82 throw Invalid_Argument("OID::from_string argument must be non-empty");
83 }
84
86 if(o.has_value()) {
87 return o;
88 }
89
90 // Try to parse as a dotted decimal
91 try {
92 return OID(str);
93 } catch(...) {}
94
95 throw Lookup_Error(fmt("No OID associated with name '{}'", str));
96}
97
98OID::OID(std::initializer_list<uint32_t> init) : m_id(init) {
99 oid_valid_check(m_id);
100}
101
102OID::OID(std::vector<uint32_t>&& init) : m_id(std::move(init)) {
103 oid_valid_check(m_id);
104}
105
106/*
107* ASN.1 OID Constructor
108*/
109OID::OID(std::string_view oid_str) {
110 if(!oid_str.empty()) {
111 if(auto parsed = parse_oid_str(oid_str)) {
112 m_id = std::move(*parsed);
113 oid_valid_check(m_id);
114 } else {
115 throw Invalid_Argument(fmt("Could not parse '{}' as an OID", oid_str));
116 }
117 }
118}
119
120/*
121* Return this OID as a string
122*/
123std::string OID::to_string() const {
124 std::ostringstream out;
125
126 for(size_t i = 0; i != m_id.size(); ++i) {
127 // avoid locale issues with integer formatting
128 out << std::to_string(m_id[i]);
129 if(i != m_id.size() - 1) {
130 out << ".";
131 }
132 }
133
134 return out.str();
135}
136
137std::string OID::to_formatted_string() const {
138 if(auto name = this->registered_name()) {
139 return *name;
140 } else {
141 return this->to_string();
142 }
143}
144
145std::string OID::human_name_or_empty() const {
146 return this->registered_name().value_or("");
147}
148
149std::optional<std::string> OID::registered_name() const {
150 return OID_Map::global_registry().oid2str(*this);
151}
152
154 return this->registered_name().has_value();
155}
156
157bool OID::matches(std::initializer_list<uint32_t> other) const {
158 // TODO: once all target compilers support it, use std::ranges::equal
159 return std::equal(m_id.begin(), m_id.end(), other.begin(), other.end());
160}
161
162uint64_t OID::hash_code() const {
163 // If this is changed also update gen_oids.py to match
164 uint64_t hash = 0x621F302327D9A49A;
165 for(auto id : m_id) {
166 hash *= 193;
167 hash += id;
168 }
169 return hash;
170}
171
172/*
173* Compare two OIDs
174*/
175bool operator<(const OID& a, const OID& b) {
176 const std::vector<uint32_t>& oid1 = a.get_components();
177 const std::vector<uint32_t>& oid2 = b.get_components();
178
179 return std::lexicographical_compare(oid1.begin(), oid1.end(), oid2.begin(), oid2.end());
180}
181
182/*
183* DER encode an OBJECT IDENTIFIER
184*/
186 if(m_id.size() < 2) {
187 throw Invalid_Argument("OID::encode_into: OID is invalid");
188 }
189
190 auto append = [](std::vector<uint8_t>& encoding, uint32_t z) {
191 if(z <= 0x7F) {
192 encoding.push_back(static_cast<uint8_t>(z));
193 } else {
194 const size_t z7 = (high_bit(z) + 7 - 1) / 7;
195
196 for(size_t j = 0; j != z7; ++j) {
197 uint8_t zp = static_cast<uint8_t>(z >> (7 * (z7 - j - 1)) & 0x7F);
198
199 if(j != z7 - 1) {
200 zp |= 0x80;
201 }
202
203 encoding.push_back(zp);
204 }
205 }
206 };
207
208 std::vector<uint8_t> encoding;
209
210 // We know 40 * root can't overflow because root is between 0 and 2
211 auto first = checked_add(40 * m_id[0], m_id[1]);
212 BOTAN_ASSERT_NOMSG(first.has_value());
213
214 append(encoding, *first);
215
216 for(size_t i = 2; i != m_id.size(); ++i) {
217 append(encoding, m_id[i]);
218 }
220}
221
222/*
223* Decode a BER encoded OBJECT IDENTIFIER
224*/
226 const BER_Object obj = decoder.get_next_object();
228 throw BER_Bad_Tag("Error decoding OID, unknown tag", obj.tagging());
229 }
230
231 if(obj.length() == 0) {
232 throw BER_Decoding_Error("OID encoding is too short");
233 }
234
235 auto consume = [](BufferSlicer& data) -> uint32_t {
236 BOTAN_ASSERT_NOMSG(!data.empty());
237 uint32_t b = data.take_byte();
238
239 if(b > 0x7F) {
240 b &= 0x7F;
241
242 // Even BER requires that the OID have minimal length, ie that
243 // the first byte of a multibyte encoding cannot be zero
244 // See X.690 section 8.19.2
245 if(b == 0) {
246 throw Decoding_Error("Leading zero byte in multibyte OID encoding");
247 }
248
249 while(true) {
250 if(data.empty()) {
251 throw Decoding_Error("Truncated OID value");
252 }
253
254 const uint8_t next = data.take_byte();
255 const bool more = (next & 0x80) == 0x80;
256 const uint8_t value = next & 0x7F;
257
258 if((b >> (32 - 7)) != 0) {
259 throw Decoding_Error("OID component overflow");
260 }
261
262 b = (b << 7) | value;
263
264 if(!more) {
265 break;
266 }
267 }
268 }
269
270 return b;
271 };
272
273 BufferSlicer data(obj.data());
274 std::vector<uint32_t> parts;
275 while(!data.empty()) {
276 const uint32_t comp = consume(data);
277
278 if(parts.empty()) {
279 // divide into root and second arc
280
281 const uint32_t root_arc = [](uint32_t b0) -> uint32_t {
282 if(b0 < 40) {
283 return 0;
284 } else if(b0 < 80) {
285 return 1;
286 } else {
287 return 2;
288 }
289 }(comp);
290
291 parts.push_back(root_arc);
292 BOTAN_ASSERT_NOMSG(comp >= 40 * root_arc);
293 parts.push_back(comp - 40 * root_arc);
294 } else {
295 parts.push_back(comp);
296 }
297 }
298
299 m_id = parts;
300}
301
302std::ostream& operator<<(std::ostream& out, const OID& oid) {
303 out << oid.to_string();
304 return out;
305}
306
307} // namespace Botan
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:33
BER_Object get_next_object()
Definition ber_dec.cpp:516
size_t length() const
Definition asn1_obj.h:303
uint32_t tagging() const
Definition asn1_obj.h:272
std::span< const uint8_t > data() const
Definition asn1_obj.h:308
DER_Encoder & add_object(ASN1_Type type_tag, ASN1_Class class_tag, const uint8_t rep[], size_t length)
Definition der_enc.cpp:285
void add_oid(const OID &oid, std::string_view str)
Definition oid_map.cpp:21
static OID_Map & global_registry()
Definition oid_map.cpp:16
std::optional< std::string > oid2str(const OID &oid)
Definition oid_map.cpp:73
OID str2oid(std::string_view str)
Definition oid_map.cpp:88
std::optional< std::string > registered_name() const
Definition asn1_oid.cpp:149
std::string to_formatted_string() const
Definition asn1_oid.cpp:137
bool registered_oid() const
Definition asn1_oid.cpp:153
uint64_t hash_code() const
Definition asn1_oid.cpp:162
bool matches(std::initializer_list< uint32_t > other) const
Definition asn1_oid.cpp:157
const std::vector< uint32_t > & get_components() const
Definition asn1_obj.h:530
static std::optional< OID > from_name(std::string_view name)
Definition asn1_oid.cpp:66
static void register_oid(const OID &oid, std::string_view name)
Definition asn1_oid.cpp:61
void decode_from(BER_Decoder &from) override
Definition asn1_oid.cpp:225
std::string human_name_or_empty() const
Definition asn1_oid.cpp:145
OID()=default
bool has_value() const
Definition asn1_obj.h:474
void encode_into(DER_Encoder &to) const override
Definition asn1_oid.cpp:185
std::string to_string() const
Definition asn1_oid.cpp:123
static OID from_string(std::string_view str)
Definition asn1_oid.cpp:80
constexpr std::optional< T > checked_add(T a, T b)
Definition int_utils.h:19
std::string fmt(std::string_view format, const T &... args)
Definition fmt.h:53
bool operator<(const OID &a, const OID &b)
Definition asn1_oid.cpp:175
std::ostream & operator<<(std::ostream &out, const OID &oid)
Definition asn1_oid.cpp:302
std::optional< uint32_t > parse_u32(std::string_view input, bool require_canonical)
Definition parsing.cpp:64
BOTAN_FORCE_INLINE constexpr size_t high_bit(T n)
Definition bit_ops.h:73