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.dup; 260 } 261 262 this(const OIDImpl other) 263 { 264 m_id = other.m_id.dup; 265 } 266 267 @property OID dup() const { 268 OID oid = OID(); 269 oid.m_id = m_id.dup; 270 return oid; 271 } 272 private: 273 Vector!uint m_id; 274 }