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