1 /**
2 * ASN.1 OID
3 * 
4 * Copyright:
5 * (C) 1999-2007 Jack Lloyd
6 * (C) 2014-2015 Etienne Cimon
7 *
8 * License:
9 * Botan is released under the Simplified BSD License (see LICENSE.md)
10 */
11 module botan.asn1.asn1_oid;
12 
13 import botan.constants;
14 public import botan.asn1.asn1_obj;
15 import botan.asn1.der_enc;
16 import botan.asn1.ber_dec;
17 import botan.utils.bit_ops;
18 import botan.utils.parsing;
19 import std.array;
20 import botan.utils.types;
21 
22 alias OID = RefCounted!(OIDImpl);
23 
24 /**
25 * This class represents ASN.1 object identifiers.
26 */
27 final class OIDImpl : ASN1Object
28 {
29 public:
30 
31     /*
32     * DER encode an OBJECT IDENTIFIER
33     */
34     override void encodeInto(ref DEREncoder der) const
35     {
36         if (m_id.length < 2)
37             throw new InvalidArgument("encodeInto: OID is invalid");
38         
39         Vector!ubyte encoding;
40         encoding.pushBack(cast(ubyte) (40 * m_id[0] + m_id[1]));
41         
42         foreach (size_t i; 2 .. m_id.length)
43         {
44             if (m_id[i] == 0)
45                 encoding.pushBack(cast(ubyte)0);
46             else
47             {
48                 size_t blocks = highBit(m_id[i]) + 6;
49                 blocks = (blocks - (blocks % 7)) / 7;
50                 
51                 foreach (size_t j; 0 .. (blocks - 1))
52                     encoding.pushBack(cast(ubyte) (0x80 | ((m_id[i] >> 7*(blocks-j-1)) & 0x7F)));
53                 encoding.pushBack(cast(ubyte) (m_id[i] & 0x7F));
54             }
55         }
56         der.addObject(ASN1Tag.OBJECT_ID, ASN1Tag.UNIVERSAL, encoding);
57     }
58 
59 
60     /*
61     * Decode a BER encoded OBJECT IDENTIFIER
62     */
63     override void decodeFrom(ref BERDecoder decoder)
64     {
65         BERObject obj = decoder.getNextObject();
66         if (obj.type_tag != ASN1Tag.OBJECT_ID || obj.class_tag != ASN1Tag.UNIVERSAL)
67             throw new BERBadTag("Error decoding OID, unknown tag", obj.type_tag, obj.class_tag);
68         if (obj.value.length < 2)
69             throw new BERDecodingError("OID encoding is too short");
70         clear();
71         m_id.pushBack(obj.value[0] / 40);
72         m_id.pushBack(obj.value[0] % 40);
73         
74         size_t i = 0;
75         while (i != obj.value.length - 1)
76         {
77             uint component = 0;
78             while (i != obj.value.length - 1)
79             {
80                 ++i;
81                 
82                 if (component >> (32-7))
83                     throw new DecodingError("OID component overflow");
84                 
85                 component = (component << 7) + (obj.value[i] & 0x7F);
86                 
87                 if (!(obj.value[i] & 0x80))
88                     break;
89             }
90             m_id.pushBack(component);
91         }
92         //import botan.asn1.oids : OIDS;
93         //assert(OIDS.lookup(OID(this)) !is null, "Invalid OID: " ~ m_id[].to!string);
94     }
95 
96 
97     /**
98     * Find out whether this OID is empty
99     * Returns: true is no OID value is set
100     */
101     @property bool empty() const { return m_id.length == 0; }
102 
103     /**
104     * Get this OID as list (vector) of its components.
105     * Returns: vector representing this OID
106     */
107     ref const(Vector!uint) getId() const { return m_id; }
108 
109     /**
110     * Get this OID as a string
111     * Returns: string representing this OID
112     */
113     override string toString() const
114     {
115 		auto ret = toVector();
116 		return ret[].idup;
117     }
118 
119     Vector!char toVector() const {
120         Vector!char oid_str;
121         foreach (size_t i; 0 .. m_id.length)
122         {
123             oid_str ~= to!(char[])(m_id[i]);
124             if (i != m_id.length - 1)
125                 oid_str ~= '.';
126         }
127         return oid_str.move();
128     }
129 
130     /**
131     * Compare two OIDs.
132     * Returns: true if they are equal, false otherwise
133     */
134     bool opEquals(in OIDImpl oid) const
135     {
136         if ((!oid || oid.m_id.length == 0) && m_id.length == 0) return true;
137         else if (!oid || oid.m_id.length == 0) return false;
138         else if (m_id.length == 0) return false;
139         
140         if (m_id.length != oid.m_id.length)
141             return false;
142         
143         if (m_id != oid.m_id) return false;
144         return true;
145     }
146 
147     /**
148     * Reset this instance to an empty OID.
149     */
150     void clear()
151     {
152         m_id.clear();
153     }
154 
155     /**
156     * Append another component onto the OID.
157     * 
158     * Params:
159     *  oid = the OID to add the new component to
160     *  component = the new component to add
161     */
162     OID opBinary(string op)(in OID oid, uint component)
163         if (op == "+")
164     {
165         OID new_oid = OID(oid);
166         new_oid ~= component;
167         return new_oid;
168     }
169     
170     /**
171     * Compare two OIDs.
172     * 
173     * Params:
174     *  b = the second OID
175     * 
176     * Returns: true if a is not equal to b
177     */
178     int opCmp(in OID b) const
179     {
180         if (this == *b) return 0;
181         else return -1;
182     }
183     
184     /**
185     * Compare two OIDs.
186     * 
187     * Params:
188     *  b = the second OID
189     * 
190     * Returns: true if a is lexicographically smaller than b
191     */
192     bool opBinary(string op)(in OID b)
193         if (op == "<")
194     {
195         const Vector!uint* oid1 = &getId();
196         const Vector!uint* oid2 = &b.getId();
197         
198         if (oid1.length < oid2.length)
199             return true;
200         if (oid1.length > oid2.length)
201             return false;
202         foreach (const i, const oid; (*oid1)[])
203         {
204             if (oid < (*oid2)[i])
205                 return true;
206             if (oid > (*oid2)[i])
207                 return false;
208         }
209         return false;
210     }
211 
212 
213     /**
214     * Add a component to this OID.
215     * 
216     * Params:
217     *  new_comp = the new component to add to the end of this OID
218     * 
219     * Returns: reference to this
220     */
221     void opOpAssign(string op)(uint new_comp)
222         if (op == "~") 
223     {
224         m_id.pushBack(new_comp);
225     }
226 
227     /**
228     * Construct an OID from a string.
229     * 
230     * Params:
231     *  oid_str = a string in the form "a.b.c" etc., where a,b,c are numbers
232     */
233     this(in string oid_str = "")
234     {
235         if (oid_str == "")
236             return;
237         //logTrace("Loading ", oid_str);
238         try
239         {
240             m_id = parseAsn1Oid(oid_str);
241         }
242         catch (Exception e)
243         {
244 
245             logError("parseAsn1Oid failure with '" ~ oid_str ~ "': " ~ e.toString());
246 			throw new InvalidOID(oid_str);
247         }
248         
249         if (m_id.length < 2 || m_id[0] > 2) {
250            // logTrace("Got m_id: ", m_id[]);
251             throw new InvalidOID(oid_str);
252         }
253         if ((m_id[0] == 0 || m_id[0] == 1) && m_id[1] > 39)
254             throw new InvalidOID(oid_str);
255     }
256 
257     this(const ref OID other)
258     {
259         m_id = other.m_id.clone;
260     }
261 
262     this(const OIDImpl other)
263     {
264         m_id = other.m_id.clone;
265     }
266 
267     @property OID clone() const {
268         OID oid = OID();
269         oid.m_id = m_id.clone;
270         return oid;
271     }
272 private:
273     Vector!uint m_id;
274 }