1# coding: utf-8 2 3""" 4Functions for parsing and dumping using the ASN.1 DER encoding. Exports the 5following items: 6 7 - emit() 8 - parse() 9 - peek() 10 11Other type classes are defined that help compose the types listed above. 12""" 13 14from __future__ import unicode_literals, division, absolute_import, print_function 15 16import sys 17 18from ._types import byte_cls, chr_cls, type_name 19from .util import int_from_bytes, int_to_bytes 20 21_PY2 = sys.version_info <= (3,) 22_INSUFFICIENT_DATA_MESSAGE = 'Insufficient data - %s bytes requested but only %s available' 23_MAX_DEPTH = 10 24 25 26def emit(class_, method, tag, contents): 27 """ 28 Constructs a byte string of an ASN.1 DER-encoded value 29 30 This is typically not useful. Instead, use one of the standard classes from 31 asn1crypto.core, or construct a new class with specific fields, and call the 32 .dump() method. 33 34 :param class_: 35 An integer ASN.1 class value: 0 (universal), 1 (application), 36 2 (context), 3 (private) 37 38 :param method: 39 An integer ASN.1 method value: 0 (primitive), 1 (constructed) 40 41 :param tag: 42 An integer ASN.1 tag value 43 44 :param contents: 45 A byte string of the encoded byte contents 46 47 :return: 48 A byte string of the ASN.1 DER value (header and contents) 49 """ 50 51 if not isinstance(class_, int): 52 raise TypeError('class_ must be an integer, not %s' % type_name(class_)) 53 54 if class_ < 0 or class_ > 3: 55 raise ValueError('class_ must be one of 0, 1, 2 or 3, not %s' % class_) 56 57 if not isinstance(method, int): 58 raise TypeError('method must be an integer, not %s' % type_name(method)) 59 60 if method < 0 or method > 1: 61 raise ValueError('method must be 0 or 1, not %s' % method) 62 63 if not isinstance(tag, int): 64 raise TypeError('tag must be an integer, not %s' % type_name(tag)) 65 66 if tag < 0: 67 raise ValueError('tag must be greater than zero, not %s' % tag) 68 69 if not isinstance(contents, byte_cls): 70 raise TypeError('contents must be a byte string, not %s' % type_name(contents)) 71 72 return _dump_header(class_, method, tag, contents) + contents 73 74 75def parse(contents, strict=False): 76 """ 77 Parses a byte string of ASN.1 BER/DER-encoded data. 78 79 This is typically not useful. Instead, use one of the standard classes from 80 asn1crypto.core, or construct a new class with specific fields, and call the 81 .load() class method. 82 83 :param contents: 84 A byte string of BER/DER-encoded data 85 86 :param strict: 87 A boolean indicating if trailing data should be forbidden - if so, a 88 ValueError will be raised when trailing data exists 89 90 :raises: 91 ValueError - when the contents do not contain an ASN.1 header or are truncated in some way 92 TypeError - when contents is not a byte string 93 94 :return: 95 A 6-element tuple: 96 - 0: integer class (0 to 3) 97 - 1: integer method 98 - 2: integer tag 99 - 3: byte string header 100 - 4: byte string content 101 - 5: byte string trailer 102 """ 103 104 if not isinstance(contents, byte_cls): 105 raise TypeError('contents must be a byte string, not %s' % type_name(contents)) 106 107 contents_len = len(contents) 108 info, consumed = _parse(contents, contents_len) 109 if strict and consumed != contents_len: 110 raise ValueError('Extra data - %d bytes of trailing data were provided' % (contents_len - consumed)) 111 return info 112 113 114def peek(contents): 115 """ 116 Parses a byte string of ASN.1 BER/DER-encoded data to find the length 117 118 This is typically used to look into an encoded value to see how long the 119 next chunk of ASN.1-encoded data is. Primarily it is useful when a 120 value is a concatenation of multiple values. 121 122 :param contents: 123 A byte string of BER/DER-encoded data 124 125 :raises: 126 ValueError - when the contents do not contain an ASN.1 header or are truncated in some way 127 TypeError - when contents is not a byte string 128 129 :return: 130 An integer with the number of bytes occupied by the ASN.1 value 131 """ 132 133 if not isinstance(contents, byte_cls): 134 raise TypeError('contents must be a byte string, not %s' % type_name(contents)) 135 136 info, consumed = _parse(contents, len(contents)) 137 return consumed 138 139 140def _parse(encoded_data, data_len, pointer=0, lengths_only=False, depth=0): 141 """ 142 Parses a byte string into component parts 143 144 :param encoded_data: 145 A byte string that contains BER-encoded data 146 147 :param data_len: 148 The integer length of the encoded data 149 150 :param pointer: 151 The index in the byte string to parse from 152 153 :param lengths_only: 154 A boolean to cause the call to return a 2-element tuple of the integer 155 number of bytes in the header and the integer number of bytes in the 156 contents. Internal use only. 157 158 :param depth: 159 The recursion depth when evaluating indefinite-length encoding. 160 161 :return: 162 A 2-element tuple: 163 - 0: A tuple of (class_, method, tag, header, content, trailer) 164 - 1: An integer indicating how many bytes were consumed 165 """ 166 167 if depth > _MAX_DEPTH: 168 raise ValueError('Indefinite-length recursion limit exceeded') 169 170 start = pointer 171 172 if data_len < pointer + 1: 173 raise ValueError(_INSUFFICIENT_DATA_MESSAGE % (1, data_len - pointer)) 174 first_octet = ord(encoded_data[pointer]) if _PY2 else encoded_data[pointer] 175 176 pointer += 1 177 178 tag = first_octet & 31 179 constructed = (first_octet >> 5) & 1 180 # Base 128 length using 8th bit as continuation indicator 181 if tag == 31: 182 tag = 0 183 while True: 184 if data_len < pointer + 1: 185 raise ValueError(_INSUFFICIENT_DATA_MESSAGE % (1, data_len - pointer)) 186 num = ord(encoded_data[pointer]) if _PY2 else encoded_data[pointer] 187 pointer += 1 188 if num == 0x80 and tag == 0: 189 raise ValueError('Non-minimal tag encoding') 190 tag *= 128 191 tag += num & 127 192 if num >> 7 == 0: 193 break 194 if tag < 31: 195 raise ValueError('Non-minimal tag encoding') 196 197 if data_len < pointer + 1: 198 raise ValueError(_INSUFFICIENT_DATA_MESSAGE % (1, data_len - pointer)) 199 length_octet = ord(encoded_data[pointer]) if _PY2 else encoded_data[pointer] 200 pointer += 1 201 trailer = b'' 202 203 if length_octet >> 7 == 0: 204 contents_end = pointer + (length_octet & 127) 205 206 else: 207 length_octets = length_octet & 127 208 if length_octets: 209 if data_len < pointer + length_octets: 210 raise ValueError(_INSUFFICIENT_DATA_MESSAGE % (length_octets, data_len - pointer)) 211 pointer += length_octets 212 contents_end = pointer + int_from_bytes(encoded_data[pointer - length_octets:pointer], signed=False) 213 214 else: 215 # To properly parse indefinite length values, we need to scan forward 216 # parsing headers until we find a value with a length of zero. If we 217 # just scanned looking for \x00\x00, nested indefinite length values 218 # would not work. 219 if not constructed: 220 raise ValueError('Indefinite-length element must be constructed') 221 contents_end = pointer 222 while data_len < contents_end + 2 or encoded_data[contents_end:contents_end+2] != b'\x00\x00': 223 _, contents_end = _parse(encoded_data, data_len, contents_end, lengths_only=True, depth=depth+1) 224 contents_end += 2 225 trailer = b'\x00\x00' 226 227 if contents_end > data_len: 228 raise ValueError(_INSUFFICIENT_DATA_MESSAGE % (contents_end - pointer, data_len - pointer)) 229 230 if lengths_only: 231 return (pointer, contents_end) 232 233 return ( 234 ( 235 first_octet >> 6, 236 constructed, 237 tag, 238 encoded_data[start:pointer], 239 encoded_data[pointer:contents_end-len(trailer)], 240 trailer 241 ), 242 contents_end 243 ) 244 245 246def _dump_header(class_, method, tag, contents): 247 """ 248 Constructs the header bytes for an ASN.1 object 249 250 :param class_: 251 An integer ASN.1 class value: 0 (universal), 1 (application), 252 2 (context), 3 (private) 253 254 :param method: 255 An integer ASN.1 method value: 0 (primitive), 1 (constructed) 256 257 :param tag: 258 An integer ASN.1 tag value 259 260 :param contents: 261 A byte string of the encoded byte contents 262 263 :return: 264 A byte string of the ASN.1 DER header 265 """ 266 267 header = b'' 268 269 id_num = 0 270 id_num |= class_ << 6 271 id_num |= method << 5 272 273 if tag >= 31: 274 cont_bit = 0 275 while tag > 0: 276 header = chr_cls(cont_bit | (tag & 0x7f)) + header 277 if not cont_bit: 278 cont_bit = 0x80 279 tag = tag >> 7 280 header = chr_cls(id_num | 31) + header 281 else: 282 header += chr_cls(id_num | tag) 283 284 length = len(contents) 285 if length <= 127: 286 header += chr_cls(length) 287 else: 288 length_bytes = int_to_bytes(length) 289 header += chr_cls(0x80 | len(length_bytes)) 290 header += length_bytes 291 292 return header 293