Coverage Report

Created: 2025-08-28 06:21

/src/Botan-3.4.0/src/lib/asn1/asn1_oid.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
* ASN.1 OID
3
* (C) 1999-2007 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/oid_map.h>
15
#include <botan/internal/parsing.h>
16
#include <algorithm>
17
#include <sstream>
18
19
namespace Botan {
20
21
namespace {
22
23
// returns empty on invalid
24
0
std::vector<uint32_t> parse_oid_str(std::string_view oid) {
25
0
   try {
26
0
      std::string elem;
27
0
      std::vector<uint32_t> oid_elems;
28
29
0
      for(char c : oid) {
30
0
         if(c == '.') {
31
0
            if(elem.empty()) {
32
0
               return std::vector<uint32_t>();
33
0
            }
34
0
            oid_elems.push_back(to_u32bit(elem));
35
0
            elem.clear();
36
0
         } else {
37
0
            elem += c;
38
0
         }
39
0
      }
40
41
0
      if(!elem.empty()) {
42
0
         oid_elems.push_back(to_u32bit(elem));
43
0
      }
44
45
0
      return oid_elems;
46
0
   } catch(Invalid_Argument&)  // thrown by to_u32bit
47
0
   {
48
0
      return std::vector<uint32_t>();
49
0
   }
50
0
}
51
52
}  // namespace
53
54
//static
55
0
void OID::register_oid(const OID& oid, std::string_view name) {
56
0
   OID_Map::global_registry().add_oid(oid, name);
57
0
}
58
59
//static
60
0
std::optional<OID> OID::from_name(std::string_view name) {
61
0
   if(name.empty()) {
62
0
      throw Invalid_Argument("OID::from_name argument must be non-empty");
63
0
   }
64
65
0
   OID o = OID_Map::global_registry().str2oid(name);
66
0
   if(o.has_value()) {
67
0
      return std::optional(o);
68
0
   }
69
70
0
   return std::nullopt;
71
0
}
72
73
//static
74
66.4k
OID OID::from_string(std::string_view str) {
75
66.4k
   if(str.empty()) {
76
0
      throw Invalid_Argument("OID::from_string argument must be non-empty");
77
0
   }
78
79
66.4k
   OID o = OID_Map::global_registry().str2oid(str);
80
66.4k
   if(o.has_value()) {
81
66.4k
      return o;
82
66.4k
   }
83
84
0
   std::vector<uint32_t> raw = parse_oid_str(str);
85
86
0
   if(!raw.empty()) {
87
0
      return OID(std::move(raw));
88
0
   }
89
90
0
   throw Lookup_Error(fmt("No OID associated with name '{}'", str));
91
0
}
92
93
/*
94
* ASN.1 OID Constructor
95
*/
96
0
OID::OID(std::string_view oid_str) {
97
0
   if(!oid_str.empty()) {
98
0
      m_id = parse_oid_str(oid_str);
99
0
      if(m_id.size() < 2 || m_id[0] > 2 || (m_id[0] < 2 && m_id[1] > 39)) {
100
0
         throw Decoding_Error(fmt("Invalid OID '{}'", oid_str));
101
0
      }
102
0
   }
103
0
}
104
105
/*
106
* Return this OID as a string
107
*/
108
0
std::string OID::to_string() const {
109
0
   std::ostringstream out;
110
0
   out << (*this);
111
0
   return out.str();
112
0
}
113
114
0
std::string OID::to_formatted_string() const {
115
0
   std::string s = this->human_name_or_empty();
116
0
   if(!s.empty()) {
117
0
      return s;
118
0
   }
119
0
   return this->to_string();
120
0
}
121
122
0
std::string OID::human_name_or_empty() const {
123
0
   return OID_Map::global_registry().oid2str(*this);
124
0
}
125
126
0
bool OID::registered_oid() const {
127
0
   return !human_name_or_empty().empty();
128
0
}
129
130
/*
131
* Compare two OIDs
132
*/
133
0
bool operator<(const OID& a, const OID& b) {
134
0
   const std::vector<uint32_t>& oid1 = a.get_components();
135
0
   const std::vector<uint32_t>& oid2 = b.get_components();
136
137
0
   return std::lexicographical_compare(oid1.begin(), oid1.end(), oid2.begin(), oid2.end());
138
0
}
139
140
0
std::ostream& operator<<(std::ostream& out, const OID& oid) {
141
0
   const auto& val = oid.get_components();
142
143
0
   for(size_t i = 0; i != val.size(); ++i) {
144
      // avoid locale issues with integer formatting
145
0
      out << std::to_string(val[i]);
146
0
      if(i != val.size() - 1) {
147
0
         out << ".";
148
0
      }
149
0
   }
150
151
0
   return out;
152
0
}
153
154
/*
155
* DER encode an OBJECT IDENTIFIER
156
*/
157
0
void OID::encode_into(DER_Encoder& der) const {
158
0
   if(m_id.size() < 2) {
159
0
      throw Invalid_Argument("OID::encode_into: OID is invalid");
160
0
   }
161
162
0
   std::vector<uint8_t> encoding;
163
164
0
   if(m_id[0] > 2 || m_id[1] >= 40) {
165
0
      throw Encoding_Error("Invalid OID prefix, cannot encode");
166
0
   }
167
168
0
   encoding.push_back(static_cast<uint8_t>(40 * m_id[0] + m_id[1]));
169
170
0
   for(size_t i = 2; i != m_id.size(); ++i) {
171
0
      if(m_id[i] == 0) {
172
0
         encoding.push_back(0);
173
0
      } else {
174
0
         size_t blocks = high_bit(m_id[i]) + 6;
175
0
         blocks = (blocks - (blocks % 7)) / 7;
176
177
0
         BOTAN_ASSERT(blocks > 0, "Math works");
178
179
0
         for(size_t j = 0; j != blocks - 1; ++j) {
180
0
            encoding.push_back(0x80 | ((m_id[i] >> 7 * (blocks - j - 1)) & 0x7F));
181
0
         }
182
0
         encoding.push_back(m_id[i] & 0x7F);
183
0
      }
184
0
   }
185
0
   der.add_object(ASN1_Type::ObjectId, ASN1_Class::Universal, encoding);
186
0
}
187
188
/*
189
* Decode a BER encoded OBJECT IDENTIFIER
190
*/
191
0
void OID::decode_from(BER_Decoder& decoder) {
192
0
   BER_Object obj = decoder.get_next_object();
193
0
   if(obj.tagging() != (ASN1_Class::Universal | ASN1_Type::ObjectId)) {
194
0
      throw BER_Bad_Tag("Error decoding OID, unknown tag", obj.tagging());
195
0
   }
196
197
0
   const size_t length = obj.length();
198
0
   const uint8_t* bits = obj.bits();
199
200
0
   if(length < 2 && !(length == 1 && bits[0] == 0)) {
201
0
      throw BER_Decoding_Error("OID encoding is too short");
202
0
   }
203
204
0
   m_id.clear();
205
0
   m_id.push_back(bits[0] / 40);
206
0
   m_id.push_back(bits[0] % 40);
207
208
0
   size_t i = 0;
209
0
   while(i != length - 1) {
210
0
      uint32_t component = 0;
211
0
      while(i != length - 1) {
212
0
         ++i;
213
214
0
         if(component >> (32 - 7)) {
215
0
            throw Decoding_Error("OID component overflow");
216
0
         }
217
218
0
         component = (component << 7) + (bits[i] & 0x7F);
219
220
0
         if(!(bits[i] & 0x80)) {
221
0
            break;
222
0
         }
223
0
      }
224
0
      m_id.push_back(component);
225
0
   }
226
0
}
227
228
}  // namespace Botan