Package tlslite :: Package utils :: Module codec
[hide private]
[frames] | no frames]

Source Code for Module tlslite.utils.codec

  1  # Author: Trevor Perrin 
  2  # See the LICENSE file for legal information regarding use of this file. 
  3   
  4  """Classes for reading/writing binary data (such as TLS records).""" 
  5   
  6  from __future__ import division 
  7   
  8  import sys 
  9  import struct 
 10  from struct import pack 
 11   
 12   
13 -class Writer(object):
14 """Serialisation helper for complex byte-based structures.""" 15
16 - def __init__(self):
17 """Initialise the serializer with no data.""" 18 self.bytes = bytearray(0)
19
20 - def addOne(self, val):
21 """Add a single-byte wide element to buffer, see add().""" 22 self.bytes.append(val)
23 24 if sys.version_info < (2, 7): 25 # struct.pack on Python2.6 does not raise exception if the value 26 # is larger than can fit inside the specified size
27 - def addTwo(self, val):
28 """Add a double-byte wide element to buffer, see add().""" 29 if not 0 <= val <= 0xffff: 30 raise ValueError("Can't represent value in specified length") 31 self.bytes += pack('>H', val)
32
33 - def addThree(self, val):
34 """Add a three-byte wide element to buffer, see add().""" 35 if not 0 <= val <= 0xffffff: 36 raise ValueError("Can't represent value in specified length") 37 self.bytes += pack('>BH', val >> 16, val & 0xffff)
38
39 - def addFour(self, val):
40 """Add a four-byte wide element to buffer, see add().""" 41 if not 0 <= val <= 0xffffffff: 42 raise ValueError("Can't represent value in specified length") 43 self.bytes += pack('>I', val)
44 else:
45 - def addTwo(self, val):
46 """Add a double-byte wide element to buffer, see add().""" 47 try: 48 self.bytes += pack('>H', val) 49 except struct.error: 50 raise ValueError("Can't represent value in specified length")
51
52 - def addThree(self, val):
53 """Add a three-byte wide element to buffer, see add().""" 54 try: 55 self.bytes += pack('>BH', val >> 16, val & 0xffff) 56 except struct.error: 57 raise ValueError("Can't represent value in specified length")
58
59 - def addFour(self, val):
60 """Add a four-byte wide element to buffer, see add().""" 61 try: 62 self.bytes += pack('>I', val) 63 except struct.error: 64 raise ValueError("Can't represent value in specified length")
65 66 if sys.version_info >= (3, 0): 67 # the method is called thousands of times, so it's better to extern 68 # the version info check
69 - def add(self, x, length):
70 """ 71 Add a single positive integer value x, encode it in length bytes 72 73 Encode positive integer x in big-endian format using length bytes, 74 add to the internal buffer. 75 76 @type x: int 77 @param x: value to encode 78 79 @type length: int 80 @param length: number of bytes to use for encoding the value 81 """ 82 try: 83 self.bytes += x.to_bytes(length, 'big') 84 except OverflowError: 85 raise ValueError("Can't represent value in specified length")
86 else: 87 _addMethods = {1: addOne, 2: addTwo, 3: addThree, 4: addFour} 88
89 - def add(self, x, length):
90 """ 91 Add a single positive integer value x, encode it in length bytes 92 93 Encode positive iteger x in big-endian format using length bytes, 94 add to the internal buffer. 95 96 @type x: int 97 @param x: value to encode 98 99 @type length: int 100 @param length: number of bytes to use for encoding the value 101 """ 102 try: 103 self._addMethods[length](self, x) 104 except KeyError: 105 self.bytes += bytearray(length) 106 newIndex = len(self.bytes) - 1 107 for i in range(newIndex, newIndex - length, -1): 108 self.bytes[i] = x & 0xFF 109 x >>= 8 110 if x != 0: 111 raise ValueError("Can't represent value in specified " 112 "length")
113
114 - def addFixSeq(self, seq, length):
115 """ 116 Add a list of items, encode every item in length bytes 117 118 Uses the unbounded iterable seq to produce items, each of 119 which is then encoded to length bytes 120 121 @type seq: iterable of int 122 @param seq: list of positive integers to encode 123 124 @type length: int 125 @param length: number of bytes to which encode every element 126 """ 127 for e in seq: 128 self.add(e, length)
129 130 if sys.version_info < (2, 7): 131 # struct.pack on Python2.6 does not raise exception if the value 132 # is larger than can fit inside the specified size
133 - def _addVarSeqTwo(self, seq):
134 """Helper method for addVarSeq""" 135 if not all(0 <= i <= 0xffff for i in seq): 136 raise ValueError("Can't represent value in specified " 137 "length") 138 self.bytes += pack('>' + 'H' * len(seq), *seq)
139
140 - def addVarSeq(self, seq, length, lengthLength):
141 """ 142 Add a bounded list of same-sized values 143 144 Create a list of specific length with all items being of the same 145 size 146 147 @type seq: list of int 148 @param seq: list of positive integers to encode 149 150 @type length: int 151 @param length: amount of bytes in which to encode every item 152 153 @type lengthLength: int 154 @param lengthLength: amount of bytes in which to encode the overall 155 length of the array 156 """ 157 self.add(len(seq)*length, lengthLength) 158 if length == 1: 159 self.bytes.extend(seq) 160 elif length == 2: 161 self._addVarSeqTwo(seq) 162 else: 163 for i in seq: 164 self.add(i, length)
165 else:
166 - def addVarSeq(self, seq, length, lengthLength):
167 """ 168 Add a bounded list of same-sized values 169 170 Create a list of specific length with all items being of the same 171 size 172 173 @type seq: list of int 174 @param seq: list of positive integers to encode 175 176 @type length: int 177 @param length: amount of bytes in which to encode every item 178 179 @type lengthLength: int 180 @param lengthLength: amount of bytes in which to encode the overall 181 length of the array 182 """ 183 seqLen = len(seq) 184 self.add(seqLen*length, lengthLength) 185 if length == 1: 186 self.bytes.extend(seq) 187 elif length == 2: 188 try: 189 self.bytes += pack('>' + 'H' * seqLen, *seq) 190 except struct.error: 191 raise ValueError("Can't represent value in specified " 192 "length") 193 else: 194 for i in seq: 195 self.add(i, length)
196
197 - def addVarTupleSeq(self, seq, length, lengthLength):
198 """ 199 Add a variable length list of same-sized element tuples. 200 201 Note that all tuples must have the same size. 202 203 Inverse of Parser.getVarTupleList() 204 205 @type seq: enumerable 206 @param seq: list of tuples 207 208 @type length: int 209 @param length: length of single element in tuple 210 211 @type lengthLength: int 212 @param lengthLength: length in bytes of overall length field 213 """ 214 if not seq: 215 self.add(0, lengthLength) 216 else: 217 startPos = len(self.bytes) 218 dataLength = len(seq) * len(seq[0]) * length 219 self.add(dataLength, lengthLength) 220 # since at the time of writing, all the calls encode single byte 221 # elements, and it's very easy to speed up that case, give it 222 # special case 223 if length == 1: 224 for elemTuple in seq: 225 self.bytes.extend(elemTuple) 226 else: 227 for elemTuple in seq: 228 self.addFixSeq(elemTuple, length) 229 if startPos + dataLength + lengthLength != len(self.bytes): 230 raise ValueError("Tuples of different lengths")
231 232
233 -class Parser(object):
234 """ 235 Parser for TLV and LV byte-based encodings. 236 237 Parser that can handle arbitrary byte-based encodings usually employed in 238 Type-Length-Value or Length-Value binary encoding protocols like ASN.1 239 or TLS 240 241 Note: if the raw bytes don't match expected values (like trying to 242 read a 4-byte integer from a 2-byte buffer), most methods will raise a 243 SyntaxError exception. 244 245 TODO: don't use an exception used by language parser to indicate errors 246 in application code. 247 248 @type bytes: bytearray 249 @ivar bytes: data to be interpreted (buffer) 250 251 @type index: int 252 @ivar index: current position in the buffer 253 254 @type lengthCheck: int 255 @ivar lengthCheck: size of struct being parsed 256 257 @type indexCheck: int 258 @ivar indexCheck: position at which the structure begins in buffer 259 """ 260
261 - def __init__(self, bytes):
262 """ 263 Bind raw bytes with parser. 264 265 @type bytes: bytearray 266 @param bytes: bytes to be parsed/interpreted 267 """ 268 self.bytes = bytes 269 self.index = 0 270 self.indexCheck = 0 271 self.lengthCheck = 0
272
273 - def get(self, length):
274 """ 275 Read a single big-endian integer value encoded in 'length' bytes. 276 277 @type length: int 278 @param length: number of bytes in which the value is encoded in 279 280 @rtype: int 281 """ 282 if self.index + length > len(self.bytes): 283 raise SyntaxError() 284 x = 0 285 for _ in range(length): 286 x <<= 8 287 x |= self.bytes[self.index] 288 self.index += 1 289 return x
290
291 - def getFixBytes(self, lengthBytes):
292 """ 293 Read a string of bytes encoded in 'lengthBytes' bytes. 294 295 @type lengthBytes: int 296 @param lengthBytes: number of bytes to return 297 298 @rtype: bytearray 299 """ 300 if self.index + lengthBytes > len(self.bytes): 301 raise SyntaxError() 302 bytes = self.bytes[self.index : self.index+lengthBytes] 303 self.index += lengthBytes 304 return bytes
305
306 - def getVarBytes(self, lengthLength):
307 """ 308 Read a variable length string with a fixed length. 309 310 @type lengthLength: int 311 @param lengthLength: number of bytes in which the length of the string 312 is encoded in 313 314 @rtype: bytearray 315 """ 316 lengthBytes = self.get(lengthLength) 317 return self.getFixBytes(lengthBytes)
318
319 - def getFixList(self, length, lengthList):
320 """ 321 Read a list of static length with same-sized ints. 322 323 @type length: int 324 @param length: size in bytes of a single element in list 325 326 @type lengthList: int 327 @param lengthList: number of elements in list 328 329 @rtype: list of int 330 """ 331 l = [0] * lengthList 332 for x in range(lengthList): 333 l[x] = self.get(length) 334 return l
335
336 - def getVarList(self, length, lengthLength):
337 """ 338 Read a variable length list of same-sized integers. 339 340 @type length: int 341 @param length: size in bytes of a single element 342 343 @type lengthLength: int 344 @param lengthLength: size of the encoded length of the list 345 346 @rtype: list of int 347 """ 348 lengthList = self.get(lengthLength) 349 if lengthList % length != 0: 350 raise SyntaxError() 351 lengthList = lengthList // length 352 l = [0] * lengthList 353 for x in range(lengthList): 354 l[x] = self.get(length) 355 return l
356
357 - def getVarTupleList(self, elemLength, elemNum, lengthLength):
358 """ 359 Read a variable length list of same sized tuples. 360 361 @type elemLength: int 362 @param elemLength: length in bytes of single tuple element 363 364 @type elemNum: int 365 @param elemNum: number of elements in tuple 366 367 @type lengthLength: int 368 @param lengthLength: length in bytes of the list length variable 369 370 @rtype: list of tuple of int 371 """ 372 lengthList = self.get(lengthLength) 373 if lengthList % (elemLength * elemNum) != 0: 374 raise SyntaxError() 375 tupleCount = lengthList // (elemLength * elemNum) 376 tupleList = [] 377 for _ in range(tupleCount): 378 currentTuple = [] 379 for _ in range(elemNum): 380 currentTuple.append(self.get(elemLength)) 381 tupleList.append(tuple(currentTuple)) 382 return tupleList
383
384 - def startLengthCheck(self, lengthLength):
385 """ 386 Read length of struct and start a length check for parsing. 387 388 @type lengthLength: int 389 @param lengthLength: number of bytes in which the length is encoded 390 """ 391 self.lengthCheck = self.get(lengthLength) 392 self.indexCheck = self.index
393
394 - def setLengthCheck(self, length):
395 """ 396 Set length of struct and start a length check for parsing. 397 398 @type length: int 399 @param length: expected size of parsed struct in bytes 400 """ 401 self.lengthCheck = length 402 self.indexCheck = self.index
403
404 - def stopLengthCheck(self):
405 """ 406 Stop struct parsing, verify that no under- or overflow occurred. 407 408 In case the expected length was mismatched with actual length of 409 processed data, raises an exception. 410 """ 411 if (self.index - self.indexCheck) != self.lengthCheck: 412 raise SyntaxError()
413
414 - def atLengthCheck(self):
415 """ 416 Check if there is data in structure left for parsing. 417 418 Returns True if the whole structure was parsed, False if there is 419 some data left. 420 421 Will raise an exception if overflow occured (amount of data read was 422 greater than expected size) 423 """ 424 if (self.index - self.indexCheck) < self.lengthCheck: 425 return False 426 elif (self.index - self.indexCheck) == self.lengthCheck: 427 return True 428 else: 429 raise SyntaxError()
430
431 - def getRemainingLength(self):
432 """Return amount of data remaining in struct being parsed.""" 433 return len(self.bytes) - self.index
434