1 /** 2 * ASN.1 Internals 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 12 module botan.asn1.asn1_obj; 13 14 import botan.constants; 15 public import botan.asn1.der_enc; 16 public import botan.asn1.ber_dec; 17 public import botan.asn1.alg_id; 18 public import botan.filters.data_src; 19 import botan.utils.parsing; 20 import botan.utils.exceptn; 21 import memutils.vector : SecureVector; 22 import std.conv : to; 23 import botan.utils.types; 24 25 /** 26 * ASN.1 Type and Class Tags 27 */ 28 enum ASN1Tag { 29 UNIVERSAL = 0x00, 30 APPLICATION = 0x40, 31 CONTEXT_SPECIFIC = 0x80, 32 33 CONSTRUCTED = 0x20, 34 35 PRIVATE = ASN1Tag.CONSTRUCTED | ASN1Tag.CONTEXT_SPECIFIC, 36 37 EOC = 0x00, 38 BOOLEAN = 0x01, 39 INTEGER = 0x02, 40 BIT_STRING = 0x03, 41 OCTET_STRING = 0x04, 42 NULL_TAG = 0x05, 43 OBJECT_ID = 0x06, 44 ENUMERATED = 0x0A, 45 SEQUENCE = 0x10, 46 SET = 0x11, 47 48 UTF8_STRING = 0x0C, 49 NUMERIC_STRING = 0x12, 50 PRINTABLE_STRING = 0x13, 51 T61_STRING = 0x14, 52 IA5_STRING = 0x16, 53 VISIBLE_STRING = 0x1A, 54 BMP_STRING = 0x1E, 55 56 UTC_TIME = 0x17, 57 GENERALIZED_TIME = 0x18, 58 59 NO_OBJECT = 0xFF00, 60 DIRECTORY_STRING = 0xFF01 61 } 62 63 /** 64 * Basic ASN.1 Object Interface 65 */ 66 interface ASN1Object 67 { 68 69 public: 70 /** 71 * Encode whatever this object is into to 72 * Params: 73 * to = the $(D DEREncoder) that will be written to 74 */ 75 void encodeInto(ref DEREncoder to) const; 76 77 /** 78 * Decode whatever this object is from from 79 * 80 * Params: 81 * from = the BERDecoder that will be read from 82 */ 83 void decodeFrom(ref BERDecoder from); 84 } 85 86 /** 87 * BER Encoded Object 88 */ 89 struct BERObject 90 { 91 public: 92 /** 93 * Check a type invariant on BER data 94 */ 95 void assertIsA(ASN1Tag type_tag, ASN1Tag class_tag) 96 { 97 if (this.type_tag != type_tag || this.class_tag != class_tag) { 98 throw new BERDecodingError("Tag mismatch when decoding got " ~ 99 to!string(this.type_tag) ~ "/" ~ 100 to!string(this.class_tag) ~ " expected " ~ 101 to!string(type_tag) ~ "/" ~ 102 to!string(class_tag)); 103 } 104 return; 105 } 106 107 /** 108 * Convert a BER object into a string object 109 */ 110 string toString() 111 { 112 return cast(string) value[].idup; 113 } 114 115 void swap(ref BERObject other) { 116 import std.algorithm : swap; 117 swap(type_tag, other.type_tag); 118 swap(class_tag, other.class_tag); 119 value.swap(other.value); 120 } 121 122 BERObject move() { 123 return BERObject(type_tag, class_tag, value.move()); 124 } 125 126 BERObject clone() { 127 return BERObject(type_tag, class_tag, value.clone()); 128 } 129 130 @disable @property BERObject dup(); 131 132 ASN1Tag type_tag, class_tag; 133 SecureVector!ubyte value; 134 } 135 136 /** 137 * General BER Decoding Error Exception 138 */ 139 class BERDecodingError : DecodingError 140 { 141 this(in string str) { 142 super("BER: " ~ str); 143 } 144 } 145 146 /** 147 * Exception For Incorrect BER Taggings 148 */ 149 class BERBadTag : BERDecodingError 150 { 151 152 /* 153 * BER Decoding Exceptions 154 */ 155 this(in string str, ASN1Tag tag) { 156 super(str ~ ": " ~ to!string(tag)); 157 } 158 159 /* 160 * BER Decoding Exceptions 161 */ 162 this(in string str, ASN1Tag tag1, ASN1Tag tag2) { 163 super(str ~ ": " ~ to!string(tag1) ~ "/" ~ to!string(tag2)); 164 } 165 } 166 167 /** 168 * Put some arbitrary bytes into a ASN1Tag.SEQUENCE 169 */ 170 Vector!ubyte putInSequence(ALLOC)(auto const ref Vector!(ubyte, ALLOC) contents) 171 { 172 return DEREncoder() 173 .startCons(ASN1Tag.SEQUENCE) 174 .rawBytes(contents) 175 .endCons() 176 .getContentsUnlocked(); 177 } 178 179 /// ditto 180 Vector!ubyte putInSequence(ALLOC)(auto const ref RefCounted!(Vector!(ubyte, ALLOC), ALLOC) contents) 181 { 182 return DEREncoder() 183 .startCons(ASN1Tag.SEQUENCE) 184 .rawBytes(contents) 185 .endCons() 186 .getContentsUnlocked(); 187 } 188 189 /** 190 * Heuristics tests; is this object possibly BER? 191 * Params: 192 * source = a data source that will be peeked at but not modified 193 */ 194 bool maybeBER(DataSource source) 195 { 196 ubyte first_byte; 197 if (!source.peekByte(first_byte)) 198 throw new StreamIOError("maybeBER: Source was empty"); 199 200 if (first_byte == (ASN1Tag.SEQUENCE | ASN1Tag.CONSTRUCTED)) 201 return true; 202 return false; 203 }