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 dup() { 127 return BERObject(type_tag, class_tag, value.dup()); 128 } 129 130 ASN1Tag type_tag, class_tag; 131 SecureVector!ubyte value; 132 } 133 134 /** 135 * General BER Decoding Error Exception 136 */ 137 class BERDecodingError : DecodingError 138 { 139 this(in string str) { 140 super("BER: " ~ str); 141 } 142 } 143 144 /** 145 * Exception For Incorrect BER Taggings 146 */ 147 class BERBadTag : BERDecodingError 148 { 149 150 /* 151 * BER Decoding Exceptions 152 */ 153 this(in string str, ASN1Tag tag) { 154 super(str ~ ": " ~ to!string(tag)); 155 } 156 157 /* 158 * BER Decoding Exceptions 159 */ 160 this(in string str, ASN1Tag tag1, ASN1Tag tag2) { 161 super(str ~ ": " ~ to!string(tag1) ~ "/" ~ to!string(tag2)); 162 } 163 } 164 165 /** 166 * Put some arbitrary bytes into a ASN1Tag.SEQUENCE 167 */ 168 Vector!ubyte putInSequence(ALLOC)(auto const ref Vector!(ubyte, ALLOC) contents) 169 { 170 return DEREncoder() 171 .startCons(ASN1Tag.SEQUENCE) 172 .rawBytes(contents) 173 .endCons() 174 .getContentsUnlocked(); 175 } 176 177 /// ditto 178 Vector!ubyte putInSequence(ALLOC)(auto const ref RefCounted!(Vector!(ubyte, ALLOC), ALLOC) contents) 179 { 180 return DEREncoder() 181 .startCons(ASN1Tag.SEQUENCE) 182 .rawBytes(contents) 183 .endCons() 184 .getContentsUnlocked(); 185 } 186 187 /** 188 * Heuristics tests; is this object possibly BER? 189 * Params: 190 * source = a data source that will be peeked at but not modified 191 */ 192 bool maybeBER(DataSource source) 193 { 194 ubyte first_byte; 195 if (!source.peekByte(first_byte)) 196 throw new StreamIOError("maybeBER: Source was empty"); 197 198 if (first_byte == (ASN1Tag.SEQUENCE | ASN1Tag.CONSTRUCTED)) 199 return true; 200 return false; 201 }