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