Package tlslite :: Module recordlayer
[hide private]
[frames] | no frames]

Source Code for Module tlslite.recordlayer

   1  # Copyright (c) 2014, Hubert Kario 
   2  # 
   3  # See the LICENSE file for legal information regarding use of this file. 
   4   
   5  """Implementation of the TLS Record Layer protocol""" 
   6   
   7  import socket 
   8  import errno 
   9  from .utils import tlshashlib as hashlib 
  10  from .constants import ContentType, CipherSuite 
  11  from .messages import RecordHeader3, RecordHeader2, Message 
  12  from .utils.cipherfactory import createAESGCM, createAES, createRC4, \ 
  13          createTripleDES, createCHACHA20 
  14  from .utils.codec import Parser, Writer 
  15  from .utils.compat import compatHMAC 
  16  from .utils.cryptomath import getRandomBytes, MD5 
  17  from .utils.constanttime import ct_compare_digest, ct_check_cbc_mac_and_pad 
  18  from .errors import TLSRecordOverflow, TLSIllegalParameterException,\ 
  19          TLSAbruptCloseError, TLSDecryptionFailed, TLSBadRecordMAC 
  20  from .mathtls import createMAC_SSL, createHMAC, PRF_SSL, PRF, PRF_1_2, \ 
  21          PRF_1_2_SHA384 
22 23 -class RecordSocket(object):
24 25 """Socket wrapper for reading and writing TLS Records""" 26
27 - def __init__(self, sock):
28 """ 29 Assign socket to wrapper 30 31 @type sock: socket.socket 32 """ 33 self.sock = sock 34 self.version = (0, 0)
35
36 - def _sockSendAll(self, data):
37 """ 38 Send all data through socket 39 40 @type data: bytearray 41 @param data: data to send 42 @raise socket.error: when write to socket failed 43 """ 44 while 1: 45 try: 46 bytesSent = self.sock.send(data) 47 except socket.error as why: 48 if why.args[0] in (errno.EWOULDBLOCK, errno.EAGAIN): 49 yield 1 50 continue 51 raise 52 53 if bytesSent == len(data): 54 return 55 data = data[bytesSent:] 56 yield 1
57
58 - def send(self, msg, padding=0):
59 """ 60 Send the message through socket. 61 62 @type msg: bytearray 63 @param msg: TLS message to send 64 @type padding: int 65 @param padding: amount of padding to specify for SSLv2 66 @raise socket.error: when write to socket failed 67 """ 68 data = msg.write() 69 70 if self.version in ((2, 0), (0, 2)): 71 header = RecordHeader2().create(len(data), 72 padding) 73 else: 74 header = RecordHeader3().create(self.version, 75 msg.contentType, 76 len(data)) 77 78 data = header.write() + data 79 80 for result in self._sockSendAll(data): 81 yield result
82
83 - def _sockRecvAll(self, length):
84 """ 85 Read exactly the amount of bytes specified in L{length} from raw socket. 86 87 @rtype: generator 88 @return: generator that will return 0 or 1 in case the socket is non 89 blocking and would block and bytearray in case the read finished 90 @raise TLSAbruptCloseError: when the socket closed 91 """ 92 buf = bytearray(0) 93 94 if length == 0: 95 yield buf 96 97 while True: 98 try: 99 socketBytes = self.sock.recv(length - len(buf)) 100 except socket.error as why: 101 if why.args[0] in (errno.EWOULDBLOCK, errno.EAGAIN): 102 yield 0 103 continue 104 else: 105 raise 106 107 #if the connection closed, raise socket error 108 if len(socketBytes) == 0: 109 raise TLSAbruptCloseError() 110 111 buf += bytearray(socketBytes) 112 if len(buf) == length: 113 yield buf
114
115 - def _recvHeader(self):
116 """Read a single record header from socket""" 117 #Read the next record header 118 buf = bytearray(0) 119 ssl2 = False 120 121 result = None 122 for result in self._sockRecvAll(1): 123 if result in (0, 1): 124 yield result 125 else: break 126 assert result is not None 127 128 buf += result 129 130 if buf[0] in ContentType.all: 131 ssl2 = False 132 # SSLv3 record layer header is 5 bytes long, we already read 1 133 result = None 134 for result in self._sockRecvAll(4): 135 if result in (0, 1): 136 yield result 137 else: break 138 assert result is not None 139 buf += result 140 else: 141 # if header has no pading the header is 2 bytes long, 3 otherwise 142 # at the same time we already read 1 byte 143 ssl2 = True 144 if buf[0] & 0x80: 145 readLen = 1 146 else: 147 readLen = 2 148 result = None 149 for result in self._sockRecvAll(readLen): 150 if result in (0, 1): 151 yield result 152 else: break 153 assert result is not None 154 buf += result 155 156 157 #Parse the record header 158 if ssl2: 159 record = RecordHeader2().parse(Parser(buf)) 160 # padding can't be longer than overall length and if it is present 161 # the overall size must be a multiple of cipher block size 162 if ((record.padding > record.length) or 163 (record.padding and record.length % 8)): 164 raise TLSIllegalParameterException(\ 165 "Malformed record layer header") 166 else: 167 record = RecordHeader3().parse(Parser(buf)) 168 169 yield record
170
171 - def recv(self):
172 """ 173 Read a single record from socket, handle SSLv2 and SSLv3 record layer 174 175 @rtype: generator 176 @return: generator that returns 0 or 1 in case the read would be 177 blocking or a tuple containing record header (object) and record 178 data (bytearray) read from socket 179 @raise socket.error: In case of network error 180 @raise TLSAbruptCloseError: When the socket was closed on the other 181 side in middle of record receiving 182 @raise TLSRecordOverflow: When the received record was longer than 183 allowed by TLS 184 @raise TLSIllegalParameterException: When the record header was 185 malformed 186 """ 187 record = None 188 for record in self._recvHeader(): 189 if record in (0, 1): 190 yield record 191 else: break 192 assert record is not None 193 194 #Check the record header fields 195 # 18432 = 2**14 (basic record size limit) + 1024 (maximum compression 196 # overhead) + 1024 (maximum encryption overhead) 197 if record.length > 18432: 198 raise TLSRecordOverflow() 199 200 #Read the record contents 201 buf = bytearray(0) 202 203 result = None 204 for result in self._sockRecvAll(record.length): 205 if result in (0, 1): 206 yield result 207 else: break 208 assert result is not None 209 210 buf += result 211 212 yield (record, buf)
213
214 -class ConnectionState(object):
215 216 """Preserve the connection state for reading and writing data to records""" 217
218 - def __init__(self):
219 """Create an instance with empty encryption and MACing contexts""" 220 self.macContext = None 221 self.encContext = None 222 self.fixedNonce = None 223 self.seqnum = 0
224
225 - def getSeqNumBytes(self):
226 """Return encoded sequence number and increment it.""" 227 writer = Writer() 228 writer.add(self.seqnum, 8) 229 self.seqnum += 1 230 return writer.bytes
231
232 -class RecordLayer(object):
233 234 """ 235 Implementation of TLS record layer protocol 236 237 @ivar version: the TLS version to use (tuple encoded as on the wire) 238 @ivar sock: underlying socket 239 @ivar client: whether the connection should use encryption 240 @ivar encryptThenMAC: use the encrypt-then-MAC mechanism for record 241 integrity 242 @ivar handshake_finished: used in SSL2, True if handshake protocol is over 243 """ 244
245 - def __init__(self, sock):
246 self.sock = sock 247 self._recordSocket = RecordSocket(sock) 248 self._version = (0, 0) 249 250 self.client = True 251 252 self._writeState = ConnectionState() 253 self._readState = ConnectionState() 254 self._pendingWriteState = ConnectionState() 255 self._pendingReadState = ConnectionState() 256 self.fixedIVBlock = None 257 258 self.encryptThenMAC = False 259 260 self.handshake_finished = False
261 262 @property
263 - def blockSize(self):
264 """Return the size of block used by current symmetric cipher (R/O)""" 265 return self._writeState.encContext.block_size
266 267 @property
268 - def version(self):
269 """Return the TLS version used by record layer""" 270 return self._version
271 272 @version.setter
273 - def version(self, val):
274 """Set the TLS version used by record layer""" 275 self._version = val 276 self._recordSocket.version = val
277
278 - def getCipherName(self):
279 """ 280 Return the name of the bulk cipher used by this connection 281 282 @rtype: str 283 @return: The name of the cipher, like 'aes128', 'rc4', etc. 284 """ 285 if self._writeState.encContext is None: 286 return None 287 return self._writeState.encContext.name
288
289 - def getCipherImplementation(self):
290 """ 291 Return the name of the implementation used for the connection 292 293 'python' for tlslite internal implementation, 'openssl' for M2crypto 294 and 'pycrypto' for pycrypto 295 @rtype: str 296 @return: Name of cipher implementation used, None if not initialised 297 """ 298 if self._writeState.encContext is None: 299 return None 300 return self._writeState.encContext.implementation
301
302 - def shutdown(self):
303 """Clear read and write states""" 304 self._writeState = ConnectionState() 305 self._readState = ConnectionState() 306 self._pendingWriteState = ConnectionState() 307 self._pendingReadState = ConnectionState()
308
309 - def isCBCMode(self):
310 """Returns true if cipher uses CBC mode""" 311 if self._writeState and self._writeState.encContext and \ 312 self._writeState.encContext.isBlockCipher: 313 return True 314 else: 315 return False
316 # 317 # sending messages 318 # 319
320 - def addPadding(self, data):
321 """Add padding to data so that it is multiple of block size""" 322 currentLength = len(data) 323 blockLength = self.blockSize 324 paddingLength = blockLength - 1 - (currentLength % blockLength) 325 326 paddingBytes = bytearray([paddingLength] * (paddingLength+1)) 327 data += paddingBytes 328 return data
329
330 - def calculateMAC(self, mac, seqnumBytes, contentType, data):
331 """Calculate the SSL/TLS version of a MAC""" 332 mac.update(compatHMAC(seqnumBytes)) 333 mac.update(compatHMAC(bytearray([contentType]))) 334 assert self.version in ((3, 0), (3, 1), (3, 2), (3, 3)) 335 if self.version != (3, 0): 336 mac.update(compatHMAC(bytearray([self.version[0]]))) 337 mac.update(compatHMAC(bytearray([self.version[1]]))) 338 mac.update(compatHMAC(bytearray([len(data)//256]))) 339 mac.update(compatHMAC(bytearray([len(data)%256]))) 340 mac.update(compatHMAC(data)) 341 return bytearray(mac.digest())
342
343 - def _macThenEncrypt(self, data, contentType):
344 """MAC, pad then encrypt data""" 345 if self._writeState.macContext: 346 seqnumBytes = self._writeState.getSeqNumBytes() 347 mac = self._writeState.macContext.copy() 348 macBytes = self.calculateMAC(mac, seqnumBytes, contentType, data) 349 data += macBytes 350 351 #Encrypt for Block or Stream Cipher 352 if self._writeState.encContext: 353 #Add padding (for Block Cipher): 354 if self._writeState.encContext.isBlockCipher: 355 356 #Add TLS 1.1 fixed block 357 if self.version >= (3, 2): 358 data = self.fixedIVBlock + data 359 360 data = self.addPadding(data) 361 362 #Encrypt 363 data = self._writeState.encContext.encrypt(data) 364 365 return data
366
367 - def _encryptThenMAC(self, buf, contentType):
368 """Pad, encrypt and then MAC the data""" 369 if self._writeState.encContext: 370 # add IV for TLS1.1+ 371 if self.version >= (3, 2): 372 buf = self.fixedIVBlock + buf 373 374 buf = self.addPadding(buf) 375 376 buf = self._writeState.encContext.encrypt(buf) 377 378 # add MAC 379 if self._writeState.macContext: 380 seqnumBytes = self._writeState.getSeqNumBytes() 381 mac = self._writeState.macContext.copy() 382 383 # append MAC 384 macBytes = self.calculateMAC(mac, seqnumBytes, contentType, buf) 385 buf += macBytes 386 387 return buf
388 389 @staticmethod
390 - def _getNonce(state, seqnum):
391 """Calculate a nonce for a given enc/dec context""" 392 # ChaCha is using the draft-TLS1.3-like nonce derivation 393 if state.encContext.name == "chacha20-poly1305" and \ 394 len(state.fixedNonce) == 12: 395 # 4 byte nonce is used by the draft cipher 396 nonce = bytearray(i ^ j for i, j in zip(bytearray(4) + seqnum, 397 state.fixedNonce)) 398 else: 399 nonce = state.fixedNonce + seqnum 400 return nonce
401 402
403 - def _encryptThenSeal(self, buf, contentType):
404 """Encrypt with AEAD cipher""" 405 #Assemble the authenticated data. 406 seqNumBytes = self._writeState.getSeqNumBytes() 407 authData = seqNumBytes + bytearray([contentType, 408 self.version[0], 409 self.version[1], 410 len(buf)//256, 411 len(buf)%256]) 412 413 nonce = self._getNonce(self._writeState, seqNumBytes) 414 415 assert len(nonce) == self._writeState.encContext.nonceLength 416 417 buf = self._writeState.encContext.seal(nonce, buf, authData) 418 419 #AES-GCM, has an explicit variable nonce. 420 if "aes" in self._writeState.encContext.name: 421 buf = seqNumBytes + buf 422 423 return buf
424
425 - def _ssl2Encrypt(self, data):
426 """Encrypt in SSL2 mode""" 427 # in SSLv2 sequence numbers are incremented for plaintext records too 428 seqnumBytes = self._writeState.getSeqNumBytes() 429 430 if (self._writeState.encContext and 431 self._writeState.encContext.isBlockCipher): 432 plaintext_len = len(data) 433 data = self.addPadding(data) 434 padding = len(data) - plaintext_len 435 else: 436 padding = 0 437 438 if self._writeState.macContext: 439 mac = self._writeState.macContext.copy() 440 mac.update(compatHMAC(data)) 441 mac.update(compatHMAC(seqnumBytes[-4:])) 442 443 data = bytearray(mac.digest()) + data 444 445 if self._writeState.encContext: 446 data = self._writeState.encContext.encrypt(data) 447 448 return data, padding
449
450 - def sendRecord(self, msg):
451 """ 452 Encrypt, MAC and send arbitrary message as-is through socket. 453 454 Note that if the message was not fragmented to below 2**14 bytes 455 it will be rejected by the other connection side. 456 457 @param msg: TLS message to send 458 @type msg: ApplicationData, HandshakeMessage, etc. 459 """ 460 data = msg.write() 461 contentType = msg.contentType 462 463 padding = 0 464 if self.version in ((0, 2), (2, 0)): 465 data, padding = self._ssl2Encrypt(data) 466 elif self._writeState and \ 467 self._writeState.encContext and \ 468 self._writeState.encContext.isAEAD: 469 data = self._encryptThenSeal(data, contentType) 470 elif self.encryptThenMAC: 471 data = self._encryptThenMAC(data, contentType) 472 else: 473 data = self._macThenEncrypt(data, contentType) 474 475 encryptedMessage = Message(contentType, data) 476 477 for result in self._recordSocket.send(encryptedMessage, padding): 478 yield result
479 480 # 481 # receiving messages 482 # 483
484 - def _decryptStreamThenMAC(self, recordType, data):
485 """Decrypt a stream cipher and check MAC""" 486 if self._readState.encContext: 487 assert self.version in ((3, 0), (3, 1), (3, 2), (3, 3)) 488 489 data = self._readState.encContext.decrypt(data) 490 491 if self._readState.macContext: 492 #Check MAC 493 macGood = True 494 macLength = self._readState.macContext.digest_size 495 endLength = macLength 496 if endLength > len(data): 497 macGood = False 498 else: 499 #Read MAC 500 startIndex = len(data) - endLength 501 endIndex = startIndex + macLength 502 checkBytes = data[startIndex : endIndex] 503 504 #Calculate MAC 505 seqnumBytes = self._readState.getSeqNumBytes() 506 data = data[:-endLength] 507 mac = self._readState.macContext.copy() 508 macBytes = self.calculateMAC(mac, seqnumBytes, recordType, 509 data) 510 511 #Compare MACs 512 if not ct_compare_digest(macBytes, checkBytes): 513 macGood = False 514 515 if not macGood: 516 raise TLSBadRecordMAC() 517 518 return data
519 520
521 - def _decryptThenMAC(self, recordType, data):
522 """Decrypt data, check padding and MAC""" 523 if self._readState.encContext: 524 assert self.version in ((3, 0), (3, 1), (3, 2), (3, 3)) 525 assert self._readState.encContext.isBlockCipher 526 assert self._readState.macContext 527 528 # 529 # decrypt the record 530 # 531 blockLength = self._readState.encContext.block_size 532 if len(data) % blockLength != 0: 533 raise TLSDecryptionFailed() 534 data = self._readState.encContext.decrypt(data) 535 if self.version >= (3, 2): #For TLS 1.1, remove explicit IV 536 data = data[self._readState.encContext.block_size : ] 537 538 # 539 # check padding and MAC 540 # 541 seqnumBytes = self._readState.getSeqNumBytes() 542 543 if not ct_check_cbc_mac_and_pad(data, 544 self._readState.macContext, 545 seqnumBytes, 546 recordType, 547 self.version): 548 raise TLSBadRecordMAC() 549 550 # 551 # strip padding and MAC 552 # 553 554 endLength = data[-1] + 1 + self._readState.macContext.digest_size 555 556 data = data[:-endLength] 557 558 return data
559
560 - def _macThenDecrypt(self, recordType, buf):
561 """ 562 Check MAC of data, then decrypt and remove padding 563 564 @raise TLSBadRecordMAC: when the mac value is invalid 565 @raise TLSDecryptionFailed: when the data to decrypt has invalid size 566 """ 567 if self._readState.macContext: 568 macLength = self._readState.macContext.digest_size 569 if len(buf) < macLength: 570 raise TLSBadRecordMAC("Truncated data") 571 572 checkBytes = buf[-macLength:] 573 buf = buf[:-macLength] 574 575 seqnumBytes = self._readState.getSeqNumBytes() 576 mac = self._readState.macContext.copy() 577 578 macBytes = self.calculateMAC(mac, seqnumBytes, recordType, buf) 579 580 if not ct_compare_digest(macBytes, checkBytes): 581 raise TLSBadRecordMAC("MAC mismatch") 582 583 if self._readState.encContext: 584 blockLength = self._readState.encContext.block_size 585 if len(buf) % blockLength != 0: 586 raise TLSDecryptionFailed("data length not multiple of "\ 587 "block size") 588 589 buf = self._readState.encContext.decrypt(buf) 590 591 # remove explicit IV 592 if self.version >= (3, 2): 593 buf = buf[blockLength:] 594 595 if len(buf) == 0: 596 raise TLSBadRecordMAC("No data left after IV removal") 597 598 # check padding 599 paddingLength = buf[-1] 600 if paddingLength + 1 > len(buf): 601 raise TLSBadRecordMAC("Invalid padding length") 602 603 paddingGood = True 604 totalPaddingLength = paddingLength+1 605 if self.version != (3, 0): 606 paddingBytes = buf[-totalPaddingLength:-1] 607 for byte in paddingBytes: 608 if byte != paddingLength: 609 paddingGood = False 610 611 if not paddingGood: 612 raise TLSBadRecordMAC("Invalid padding byte values") 613 614 # remove padding 615 buf = buf[:-totalPaddingLength] 616 617 return buf
618
619 - def _decryptAndUnseal(self, recordType, buf):
620 """Decrypt AEAD encrypted data""" 621 seqnumBytes = self._readState.getSeqNumBytes() 622 #AES-GCM, has an explicit variable nonce. 623 if "aes" in self._readState.encContext.name: 624 explicitNonceLength = 8 625 if explicitNonceLength > len(buf): 626 #Publicly invalid. 627 raise TLSBadRecordMAC("Truncated nonce") 628 nonce = self._readState.fixedNonce + buf[:explicitNonceLength] 629 buf = buf[8:] 630 else: 631 nonce = self._getNonce(self._readState, seqnumBytes) 632 633 if self._readState.encContext.tagLength > len(buf): 634 #Publicly invalid. 635 raise TLSBadRecordMAC("Truncated tag") 636 637 plaintextLen = len(buf) - self._readState.encContext.tagLength 638 authData = seqnumBytes + bytearray([recordType, self.version[0], 639 self.version[1], 640 plaintextLen//256, 641 plaintextLen%256]) 642 643 buf = self._readState.encContext.open(nonce, buf, authData) 644 if buf is None: 645 raise TLSBadRecordMAC("Invalid tag, decryption failure") 646 return buf
647
648 - def _decryptSSL2(self, data, padding):
649 """Decrypt SSL2 encrypted data""" 650 # sequence numbers are incremented for plaintext records too 651 seqnumBytes = self._readState.getSeqNumBytes() 652 653 # 654 # decrypt 655 # 656 if self._readState.encContext: 657 if self._readState.encContext.isBlockCipher: 658 blockLength = self._readState.encContext.block_size 659 if len(data) % blockLength: 660 raise TLSDecryptionFailed() 661 data = self._readState.encContext.decrypt(data) 662 663 # 664 # strip and check MAC 665 # 666 if self._readState.macContext: 667 macBytes = data[:16] 668 data = data[16:] 669 670 mac = self._readState.macContext.copy() 671 mac.update(compatHMAC(data)) 672 mac.update(compatHMAC(seqnumBytes[-4:])) 673 calcMac = bytearray(mac.digest()) 674 if macBytes != calcMac: 675 raise TLSBadRecordMAC() 676 677 # 678 # strip padding 679 # 680 if padding: 681 data = data[:-padding] 682 return data
683
684 - def recvRecord(self):
685 """ 686 Read, decrypt and check integrity of a single record 687 688 @rtype: tuple 689 @return: message header and decrypted message payload 690 @raise TLSDecryptionFailed: when decryption of data failed 691 @raise TLSBadRecordMAC: when record has bad MAC or padding 692 @raise socket.error: when reading from socket was unsuccessful 693 """ 694 result = None 695 for result in self._recordSocket.recv(): 696 if result in (0, 1): 697 yield result 698 else: break 699 assert result is not None 700 701 (header, data) = result 702 703 if isinstance(header, RecordHeader2): 704 data = self._decryptSSL2(data, header.padding) 705 if self.handshake_finished: 706 header.type = ContentType.application_data 707 elif self._readState and \ 708 self._readState.encContext and \ 709 self._readState.encContext.isAEAD: 710 data = self._decryptAndUnseal(header.type, data) 711 elif self.encryptThenMAC: 712 data = self._macThenDecrypt(header.type, data) 713 elif self._readState and \ 714 self._readState.encContext and \ 715 self._readState.encContext.isBlockCipher: 716 data = self._decryptThenMAC(header.type, data) 717 else: 718 data = self._decryptStreamThenMAC(header.type, data) 719 720 # RFC 5246, section 6.2.1 721 if len(data) > 2**14: 722 raise TLSRecordOverflow() 723 724 yield (header, Parser(data))
725 726 # 727 # cryptography state methods 728 # 729
730 - def changeWriteState(self):
731 """ 732 Change the cipher state to the pending one for write operations. 733 734 This should be done only once after a call to L{calcPendingStates} was 735 performed and directly after sending a L{ChangeCipherSpec} message. 736 """ 737 if self.version in ((0, 2), (2, 0)): 738 # in SSLv2 sequence numbers carry over from plaintext to encrypted 739 # context 740 self._pendingWriteState.seqnum = self._writeState.seqnum 741 self._writeState = self._pendingWriteState 742 self._pendingWriteState = ConnectionState()
743
744 - def changeReadState(self):
745 """ 746 Change the cipher state to the pending one for read operations. 747 748 This should be done only once after a call to L{calcPendingStates} was 749 performed and directly after receiving a L{ChangeCipherSpec} message. 750 """ 751 if self.version in ((0, 2), (2, 0)): 752 # in SSLv2 sequence numbers carry over from plaintext to encrypted 753 # context 754 self._pendingReadState.seqnum = self._readState.seqnum 755 self._readState = self._pendingReadState 756 self._pendingReadState = ConnectionState()
757 758 @staticmethod
759 - def _getCipherSettings(cipherSuite):
760 """Get the settings for cipher suite used""" 761 if cipherSuite in CipherSuite.aes256GcmSuites: 762 keyLength = 32 763 ivLength = 4 764 createCipherFunc = createAESGCM 765 elif cipherSuite in CipherSuite.aes128GcmSuites: 766 keyLength = 16 767 ivLength = 4 768 createCipherFunc = createAESGCM 769 elif cipherSuite in CipherSuite.chacha20Suites: 770 keyLength = 32 771 ivLength = 12 772 createCipherFunc = createCHACHA20 773 elif cipherSuite in CipherSuite.chacha20draft00Suites: 774 keyLength = 32 775 ivLength = 4 776 createCipherFunc = createCHACHA20 777 elif cipherSuite in CipherSuite.aes128Suites: 778 keyLength = 16 779 ivLength = 16 780 createCipherFunc = createAES 781 elif cipherSuite in CipherSuite.aes256Suites: 782 keyLength = 32 783 ivLength = 16 784 createCipherFunc = createAES 785 elif cipherSuite in CipherSuite.rc4Suites: 786 keyLength = 16 787 ivLength = 0 788 createCipherFunc = createRC4 789 elif cipherSuite in CipherSuite.tripleDESSuites: 790 keyLength = 24 791 ivLength = 8 792 createCipherFunc = createTripleDES 793 elif cipherSuite in CipherSuite.nullSuites: 794 keyLength = 0 795 ivLength = 0 796 createCipherFunc = None 797 else: 798 raise AssertionError() 799 800 return (keyLength, ivLength, createCipherFunc)
801 802 @staticmethod
803 - def _getMacSettings(cipherSuite):
804 """Get settings for HMAC used""" 805 if cipherSuite in CipherSuite.aeadSuites: 806 macLength = 0 807 digestmod = None 808 elif cipherSuite in CipherSuite.shaSuites: 809 macLength = 20 810 digestmod = hashlib.sha1 811 elif cipherSuite in CipherSuite.sha256Suites: 812 macLength = 32 813 digestmod = hashlib.sha256 814 elif cipherSuite in CipherSuite.sha384Suites: 815 macLength = 48 816 digestmod = hashlib.sha384 817 elif cipherSuite in CipherSuite.md5Suites: 818 macLength = 16 819 digestmod = hashlib.md5 820 else: 821 raise AssertionError() 822 823 return macLength, digestmod
824 825 @staticmethod
826 - def _getHMACMethod(version):
827 """Get the HMAC method""" 828 assert version in ((3, 0), (3, 1), (3, 2), (3, 3)) 829 if version == (3, 0): 830 createMACFunc = createMAC_SSL 831 elif version in ((3, 1), (3, 2), (3, 3)): 832 createMACFunc = createHMAC 833 834 return createMACFunc
835
836 - def _calcKeyBlock(self, cipherSuite, masterSecret, clientRandom, 837 serverRandom, outputLength):
838 """Calculate the overall key to slice up""" 839 if self.version == (3, 0): 840 keyBlock = PRF_SSL(masterSecret, 841 serverRandom + clientRandom, 842 outputLength) 843 elif self.version in ((3, 1), (3, 2)): 844 keyBlock = PRF(masterSecret, 845 b"key expansion", 846 serverRandom + clientRandom, 847 outputLength) 848 elif self.version == (3, 3): 849 if cipherSuite in CipherSuite.sha384PrfSuites: 850 keyBlock = PRF_1_2_SHA384(masterSecret, 851 b"key expansion", 852 serverRandom + clientRandom, 853 outputLength) 854 else: 855 keyBlock = PRF_1_2(masterSecret, 856 b"key expansion", 857 serverRandom + clientRandom, 858 outputLength) 859 else: 860 raise AssertionError() 861 862 return keyBlock
863
864 - def calcSSL2PendingStates(self, cipherSuite, masterSecret, clientRandom, 865 serverRandom, implementations):
866 """ 867 Create the keys for encryption and decryption in SSLv2 868 869 While we could reuse calcPendingStates(), we need to provide the 870 key-arg data for the server that needs to be passed up to handshake 871 protocol. 872 """ 873 if cipherSuite in CipherSuite.ssl2_128Key: 874 key_length = 16 875 elif cipherSuite in CipherSuite.ssl2_192Key: 876 key_length = 24 877 elif cipherSuite in CipherSuite.ssl2_64Key: 878 key_length = 8 879 else: 880 raise ValueError("Unknown cipher specified") 881 882 key_material = bytearray(key_length * 2) 883 md5_output_size = 16 884 for i, pos in enumerate(range(0, key_length * 2, md5_output_size)): 885 key_material[pos:pos+md5_output_size] = MD5(\ 886 masterSecret + 887 bytearray(str(i), "ascii") + 888 clientRandom + serverRandom) 889 890 serverWriteKey = key_material[:key_length] 891 clientWriteKey = key_material[key_length:] 892 893 # specification draft says that DES key should not use the 894 # incrementing label but all implementations use it anyway 895 #elif cipherSuite in CipherSuite.ssl2_64Key: 896 # key_material = MD5(masterSecret + clientRandom + serverRandom) 897 # serverWriteKey = key_material[0:8] 898 # clientWriteKey = key_material[8:16] 899 900 # RC4 cannot use initialisation vector 901 if cipherSuite not in CipherSuite.ssl2rc4: 902 iv = getRandomBytes(8) 903 else: 904 iv = bytearray(0) 905 906 clientPendingState = ConnectionState() 907 serverPendingState = ConnectionState() 908 909 # MAC 910 clientPendingState.macContext = hashlib.md5() 911 clientPendingState.macContext.update(compatHMAC(clientWriteKey)) 912 serverPendingState.macContext = hashlib.md5() 913 serverPendingState.macContext.update(compatHMAC(serverWriteKey)) 914 915 # ciphers 916 if cipherSuite in CipherSuite.ssl2rc4: 917 cipherMethod = createRC4 918 elif cipherSuite in CipherSuite.ssl2_3des: 919 cipherMethod = createTripleDES 920 else: 921 raise NotImplementedError("Unknown cipher") 922 923 clientPendingState.encContext = cipherMethod(clientWriteKey, iv, 924 implementations) 925 serverPendingState.encContext = cipherMethod(serverWriteKey, iv, 926 implementations) 927 928 # Assign new connection states to pending states 929 if self.client: 930 self._pendingWriteState = clientPendingState 931 self._pendingReadState = serverPendingState 932 else: 933 self._pendingWriteState = serverPendingState 934 self._pendingReadState = clientPendingState 935 936 return iv
937
938 - def calcPendingStates(self, cipherSuite, masterSecret, clientRandom, 939 serverRandom, implementations):
940 """Create pending states for encryption and decryption.""" 941 keyLength, ivLength, createCipherFunc = \ 942 self._getCipherSettings(cipherSuite) 943 944 macLength, digestmod = self._getMacSettings(cipherSuite) 945 946 if not digestmod: 947 createMACFunc = None 948 else: 949 createMACFunc = self._getHMACMethod(self.version) 950 951 outputLength = (macLength*2) + (keyLength*2) + (ivLength*2) 952 953 #Calculate Keying Material from Master Secret 954 keyBlock = self._calcKeyBlock(cipherSuite, masterSecret, clientRandom, 955 serverRandom, outputLength) 956 957 #Slice up Keying Material 958 clientPendingState = ConnectionState() 959 serverPendingState = ConnectionState() 960 parser = Parser(keyBlock) 961 clientMACBlock = parser.getFixBytes(macLength) 962 serverMACBlock = parser.getFixBytes(macLength) 963 clientKeyBlock = parser.getFixBytes(keyLength) 964 serverKeyBlock = parser.getFixBytes(keyLength) 965 clientIVBlock = parser.getFixBytes(ivLength) 966 serverIVBlock = parser.getFixBytes(ivLength) 967 968 if digestmod: 969 # Legacy cipher 970 clientPendingState.macContext = createMACFunc( 971 compatHMAC(clientMACBlock), digestmod=digestmod) 972 serverPendingState.macContext = createMACFunc( 973 compatHMAC(serverMACBlock), digestmod=digestmod) 974 if createCipherFunc is not None: 975 clientPendingState.encContext = \ 976 createCipherFunc(clientKeyBlock, 977 clientIVBlock, 978 implementations) 979 serverPendingState.encContext = \ 980 createCipherFunc(serverKeyBlock, 981 serverIVBlock, 982 implementations) 983 else: 984 # AEAD 985 clientPendingState.macContext = None 986 serverPendingState.macContext = None 987 clientPendingState.encContext = createCipherFunc(clientKeyBlock, 988 implementations) 989 serverPendingState.encContext = createCipherFunc(serverKeyBlock, 990 implementations) 991 clientPendingState.fixedNonce = clientIVBlock 992 serverPendingState.fixedNonce = serverIVBlock 993 994 #Assign new connection states to pending states 995 if self.client: 996 self._pendingWriteState = clientPendingState 997 self._pendingReadState = serverPendingState 998 else: 999 self._pendingWriteState = serverPendingState 1000 self._pendingReadState = clientPendingState 1001 1002 if self.version >= (3, 2) and ivLength: 1003 #Choose fixedIVBlock for TLS 1.1 (this is encrypted with the CBC 1004 #residue to create the IV for each sent block) 1005 self.fixedIVBlock = getRandomBytes(ivLength)
1006