123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263 |
- """
- Secure Sockets Layer / Transport Layer Security.
- """
- #
- # Note from April 2011: cde...@gmail.com added code that parses SSL3/TLS messages
- # more in depth.
- #
- # Jul 2012: afleenor@google.com modified and extended SSL support further.
- #
- from pypacker import pypacker, triggerlist
- import logging
- import struct
- # avoid references for performance reasons
- unpack = struct.unpack
- unpack_be_h = struct.Struct(">H").unpack
- logger = logging.getLogger("pypacker")
- # SSLv3/TLS versions
- SSL3_V = 0x0300
- TLS1_V = 0x0301
- TLS11_V = 0x0302
- TLS12_V = 0x0303
- ssl3_versions_str = {
- SSL3_V : "SSL3",
- TLS1_V : "TLS 1.0",
- TLS11_V : "TLS 1.1",
- TLS12_V : "TLS 1.2"
- }
- # Alert levels
- SSL3_AD_WARNING = 1
- SSL3_AD_FATAL = 2
- alert_level_str = {
- SSL3_AD_WARNING : "SSL3_AD_WARNING",
- SSL3_AD_FATAL : "SSL3_AD_FATAL"
- }
- # SSL3 alert descriptions
- SSL3_AD_CLOSE_NOTIFY = 0
- SSL3_AD_UNEXPECTED_MESSAGE = 10 # fatal
- SSL3_AD_BAD_RECORD_MAC = 20 # fatal
- SSL3_AD_DECOMPRESSION_FAILURE = 30 # fatal
- SSL3_AD_HANDSHAKE_FAILURE = 40 # fatal
- SSL3_AD_NO_CERTIFICATE = 41
- SSL3_AD_BAD_CERTIFICATE = 42
- SSL3_AD_UNSUPPORTED_CERTIFICATE = 43
- SSL3_AD_CERTIFICATE_REVOKED = 44
- SSL3_AD_CERTIFICATE_EXPIRED = 45
- SSL3_AD_CERTIFICATE_UNKNOWN = 46
- SSL3_AD_ILLEGAL_PARAMETER = 47 # fatal
- # TLS1 alert descriptions
- TLS1_AD_DECRYPTION_FAILED = 21
- TLS1_AD_RECORD_OVERFLOW = 22
- TLS1_AD_UNKNOWN_CA = 48 # fatal
- TLS1_AD_ACCESS_DENIED = 49 # fatal
- TLS1_AD_DECODE_ERROR = 50 # fatal
- TLS1_AD_DECRYPT_ERROR = 51
- TLS1_AD_EXPORT_RESTRICTION = 60 # fatal
- TLS1_AD_PROTOCOL_VERSION = 70 # fatal
- TLS1_AD_INSUFFICIENT_SECURITY = 71 # fatal
- TLS1_AD_INTERNAL_ERROR = 80 # fatal
- TLS1_AD_USER_CANCELLED = 90
- TLS1_AD_NO_RENEGOTIATION = 100
- # /* codes 110-114 are from RFC3546 */
- TLS1_AD_UNSUPPORTED_EXTENSION = 110
- TLS1_AD_CERTIFICATE_UNOBTAINABLE = 111
- TLS1_AD_UNRECOGNIZED_NAME = 112
- TLS1_AD_BAD_CERTIFICATE_STATUS_RESPONSE = 113
- TLS1_AD_BAD_CERTIFICATE_HASH_VALUE = 114
- TLS1_AD_UNKNOWN_PSK_IDENTITY = 115 # fatal
- # Mapping alert types to strings
- alert_description_str = {
- SSL3_AD_CLOSE_NOTIFY : "SSL3_AD_CLOSE_NOTIFY",
- SSL3_AD_UNEXPECTED_MESSAGE : "SSL3_AD_UNEXPECTED_MESSAGE",
- SSL3_AD_BAD_RECORD_MAC : "SSL3_AD_BAD_RECORD_MAC",
- SSL3_AD_DECOMPRESSION_FAILURE : "SSL3_AD_DECOMPRESSION_FAILURE",
- SSL3_AD_HANDSHAKE_FAILURE : "SSL3_AD_HANDSHAKE_FAILURE",
- SSL3_AD_NO_CERTIFICATE : "SSL3_AD_NO_CERTIFICATE",
- SSL3_AD_BAD_CERTIFICATE : "SSL3_AD_BAD_CERTIFICATE",
- SSL3_AD_UNSUPPORTED_CERTIFICATE : "SSL3_AD_UNSUPPORTED_CERTIFICATE",
- SSL3_AD_CERTIFICATE_REVOKED : "SSL3_AD_CERTIFICATE_REVOKED",
- SSL3_AD_CERTIFICATE_EXPIRED : "SSL3_AD_CERTIFICATE_EXPIRED",
- SSL3_AD_CERTIFICATE_UNKNOWN : "SSL3_AD_CERTIFICATE_UNKNOWN",
- SSL3_AD_ILLEGAL_PARAMETER : "SSL3_AD_ILLEGAL_PARAMETER",
- TLS1_AD_DECRYPTION_FAILED : "TLS1_AD_DECRYPTION_FAILED",
- TLS1_AD_RECORD_OVERFLOW : "TLS1_AD_RECORD_OVERFLOW",
- TLS1_AD_UNKNOWN_CA : "TLS1_AD_UNKNOWN_CA",
- TLS1_AD_ACCESS_DENIED : "TLS1_AD_ACCESS_DENIED",
- TLS1_AD_DECODE_ERROR : "TLS1_AD_DECODE_ERROR",
- TLS1_AD_DECRYPT_ERROR : "TLS1_AD_DECRYPT_ERROR",
- TLS1_AD_EXPORT_RESTRICTION : "TLS1_AD_EXPORT_RESTRICTION",
- TLS1_AD_PROTOCOL_VERSION : "TLS1_AD_PROTOCOL_VERSION",
- TLS1_AD_INSUFFICIENT_SECURITY : "TLS1_AD_INSUFFICIENT_SECURITY",
- TLS1_AD_INTERNAL_ERROR : "TLS1_AD_INTERNAL_ERROR",
- TLS1_AD_USER_CANCELLED : "TLS1_AD_USER_CANCELLED",
- TLS1_AD_NO_RENEGOTIATION : "TLS1_AD_NO_RENEGOTIATION",
- TLS1_AD_UNSUPPORTED_EXTENSION : "TLS1_AD_UNSUPPORTED_EXTENSION",
- TLS1_AD_CERTIFICATE_UNOBTAINABLE : "TLS1_AD_CERTIFICATE_UNOBTAINABLE",
- TLS1_AD_UNRECOGNIZED_NAME : "TLS1_AD_UNRECOGNIZED_NAME",
- TLS1_AD_BAD_CERTIFICATE_STATUS_RESPONSE : "TLS1_AD_BAD_CERTIFICATE_STATUS_RESPONSE",
- TLS1_AD_BAD_CERTIFICATE_HASH_VALUE : "TLS1_AD_BAD_CERTIFICATE_HASH_VALUE",
- TLS1_AD_UNKNOWN_PSK_IDENTITY : "TLS1_AD_UNKNOWN_PSK_IDENTITY"
- }
- # Record types
- RECORD_TLS_CHG_CIPHERSPEC = 20
- RECORD_TLS_ALERT = 21
- RECORD_TLS_HANDSHAKE = 22
- RECORD_TLS_HANDSHAKE_HELLO = 22 * 10
- RECORD_TLS_HANDSHAKE_DATA = 22 * 100
- RECORD_TLS_APPDATA = 23
- # Handshake types
- HNDS_HELLO_REQ = 0
- HNDS_HELLO_CLIENT = 1
- HNDS_HELLO_SERVER = 2
- HNDS_CERTIFICATE = 11
- HNDS_SERVER_KEY_EXCHANGE = 12
- HNDS_CERTIFICATE_REQ = 13
- HNDS_SERVER_HELLO_DONE = 14
- HNDS_CERT_VERIFIY = 15
- HNDS_CLIENT_KEY_EXCHANGE = 16
- HNDS_FINISHED = 20
- class SSL(pypacker.Packet):
- __hdr__ = (
- ("records", None, triggerlist.TriggerList),
- )
- def _dissect(self, buf):
- # logger.debug("parsing SSL")
- # parse all records out of message
- # possible types are Client/Sevrer Hello, Change Cipher Spec etc.
- records = []
- offset = 0
- dlen = len(buf)
- self._fragmented = False
- # records is the only header so it's ok to avoid lazy dissecting
- while offset < dlen:
- record_len = unpack_be_h(buf[offset + 3: offset + 5])[0]
- if offset + record_len > dlen:
- # TODO: handle fragmentation
- # idea: design _dissect() in a way which allows continuation
- # at any given start-point, eg start of a record-entry
- # Later call to a "reassemble([pkt1, pkt2])" can re-use
- # _dissect to add all other records
- logger.warn("possible fragmentation found")
- self._fragmented = True
- # non-parsed data becomes body content
- dlen = offset
- break
- record = Record(buf[offset: offset + 5 + record_len])
- records.append(record)
- offset += len(record)
- # logger.debug("adding records, dlen/offset at end: %d %d" % (dlen, offset))
- self.records.extend(records)
- return dlen
- class Record(pypacker.Packet):
- """
- SSLv3 or TLSv1+ Record layer.
- """
- __hdr__ = (
- ("type", "B", 0),
- ("version", "H", 0),
- ("len", "H", 0),
- )
- def __dissect(self, buf):
- # logger.debug("parsing TLSRecord")
- # TODO: check for different handshages
- self._init_handler(buf[0], buf[5:])
- return 5
- class Extension(pypacker.Packet):
- """
- Handshake protocol extension
- """
- __hdr__ = (
- ("type", "H", 0),
- ("len", "H", 0)
- )
- #
- # Record contents
- #
- class HandshakeHello(pypacker.Packet):
- __hdr__ = (
- ("type", "B", 0),
- # can't use struct here but:
- # int.from_bytes(len, "big")
- ("len", "3s", b"\x00" * 3),
- ("tlsversion", "H", 0x0301),
- ("random", "32s", b"\x00" * 32),
- ("sid_len", "B", 32),
- # variable length
- ("sid", None, b"A" * 32),
- ("ciphersuite", "H", 0x0035),
- ("compression", "B", 0),
- ("ext_len", "H", 0x0000),
- ("extensions", None, triggerlist.TriggerList),
- )
- @staticmethod
- def __parse_extension(buf):
- extensions = []
- offset = 0
- buflen = len(buf)
- while offset < buflen:
- ext_content_len = unpack(buf[offset + 2: offset + 4])
- ext_len = 4 + ext_content_len
- extensions.append(Extension(buf[offset: offset + ext_len]))
- offset += ext_len
- return extensions
- def _dissect(self, buf):
- sid_len = buf[38]
- offset_extlen = 38 + sid_len + 3
- # ext_len = unpack(buf[offset_extlen : offset_extlen+2])
- self._init_triggerlist("extensions", buf[offset_extlen + 2:], self.__parse_extension)
- class HandshakeData(pypacker.Packet):
- pass
- class ChangeCipherSpec(pypacker.Packet):
- pass
- class ApplicationData(pypacker.Packet):
- pass
- pypacker.Packet.load_handler(Record,
- {
- RECORD_TLS_CHG_CIPHERSPEC: ChangeCipherSpec,
- RECORD_TLS_HANDSHAKE_HELLO: HandshakeHello,
- RECORD_TLS_HANDSHAKE_DATA: HandshakeData,
- RECORD_TLS_APPDATA: ApplicationData
- }
- )
|