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