def fill_missing(self): s = self.tls_session s.client_kx_privkey = _tls_named_groups_generate( s.client_kx_ecdh_params ) # ecdh_Yc follows ECPoint.point format as defined in # https://tools.ietf.org/html/rfc8422#section-5.4 pubkey = s.client_kx_privkey.public_key() if isinstance(pubkey, (x25519.X25519PublicKey, x448.X448PublicKey)): self.ecdh_Yc = pubkey.public_bytes( serialization.Encoding.Raw, serialization.PublicFormat.Raw ) if s.client_kx_privkey and s.server_kx_pubkey: pms = s.client_kx_privkey.exchange(s.server_kx_pubkey) else: # uncompressed format of an elliptic curve point x = pubkey.public_numbers().x y = pubkey.public_numbers().y self.ecdh_Yc = (b"\x04" + pkcs_i2osp(x, pubkey.key_size // 8) + pkcs_i2osp(y, pubkey.key_size // 8)) if s.client_kx_privkey and s.server_kx_pubkey: pms = s.client_kx_privkey.exchange(ec.ECDH(), s.server_kx_pubkey) if s.client_kx_privkey and s.server_kx_pubkey: s.pre_master_secret = pms if not s.extms or s.session_hash: s.compute_ms_and_derive_keys()
def _tls_auth_encrypt(self, s): """ Return the TLSCiphertext.encrypted_record for AEAD ciphers. """ wcs = self.tls_session.wcs write_seq_num = struct.pack("!Q", wcs.seq_num) wcs.seq_num += 1 add_data = (pkcs_i2osp(self.type, 1) + pkcs_i2osp(self.version, 2) + pkcs_i2osp(len(s) + wcs.cipher.tag_len, 2)) return wcs.cipher.auth_encrypt(s, add_data, write_seq_num)
def _tls_auth_encrypt(self, s): """ Return the TLSCiphertext.fragment for AEAD ciphers, i.e. the whole GenericAEADCipher. Also, the additional data is computed right here. """ write_seq_num = struct.pack("!Q", self.tls_session.wcs.seq_num) self.tls_session.wcs.seq_num += 1 add_data = (write_seq_num + pkcs_i2osp(self.type, 1) + pkcs_i2osp(self.version, 2) + pkcs_i2osp(len(s), 2)) return self.tls_session.wcs.cipher.auth_encrypt( s, add_data, write_seq_num)
def _tls_auth_encrypt(self, s): """ Return the TLSCiphertext.fragment for AEAD ciphers, i.e. the whole GenericAEADCipher. Also, the additional data is computed right here. """ write_seq_num = struct.pack("!Q", self.tls_session.wcs.seq_num) self.tls_session.wcs.seq_num += 1 add_data = (write_seq_num + pkcs_i2osp(self.type, 1) + pkcs_i2osp(self.version, 2) + pkcs_i2osp(len(s), 2)) return self.tls_session.wcs.cipher.auth_encrypt(s, add_data, write_seq_num)
def fill_missing(self): s = self.tls_session s.client_kx_privkey = _tls_named_groups_generate( s.client_kx_ecdh_params) pubkey = s.client_kx_privkey.public_key() x = pubkey.public_numbers().x y = pubkey.public_numbers().y self.ecdh_Yc = (b"\x04" + pkcs_i2osp(x, pubkey.key_size // 8) + pkcs_i2osp(y, pubkey.key_size // 8)) if s.client_kx_privkey and s.server_kx_pubkey: pms = s.client_kx_privkey.exchange(ec.ECDH(), s.server_kx_pubkey) s.pre_master_secret = pms s.compute_ms_and_derive_keys()
def encode_point(point, point_format=0): """ Return a string representation of the Point p, according to point_format. """ pLen = len(binrepr(point.curve().p())) x = pkcs_i2osp(point.x(), math.ceil(pLen / 8)) y = pkcs_i2osp(point.y(), math.ceil(pLen / 8)) if point_format == 0: frmt = '\x04' elif point_format == 1: frmt = chr(2 + y % 2) y = '' else: raise Exception("No support for point_format %d" % point_format) return frmt + x + y
def encode_point(point, point_format=0): """ Return a string representation of the Point p, according to point_format. """ pLen = len(binrepr(point.curve().p())) x = pkcs_i2osp(point.x(), math.ceil(pLen/8)) y = pkcs_i2osp(point.y(), math.ceil(pLen/8)) if point_format == 0: frmt = '\x04' elif point_format == 1: frmt = chr(2 + y%2) y = '' else: raise Exception("No support for point_format %d" % point_format) return frmt + x + y
def fill_missing(self): s = self.tls_session params = s.client_kx_ecdh_params s.client_kx_privkey = ec.generate_private_key(params, default_backend()) pubkey = s.client_kx_privkey.public_key() x = pubkey.public_numbers().x y = pubkey.public_numbers().y self.ecdh_Yc = (b"\x04" + pkcs_i2osp(x, params.key_size / 8) + pkcs_i2osp(y, params.key_size / 8)) if s.client_kx_privkey and s.server_kx_pubkey: pms = s.client_kx_privkey.exchange(ec.ECDH(), s.server_kx_pubkey) s.pre_master_secret = pms s.compute_ms_and_derive_keys()
def fill_missing(self): k = PrivKeyRSA() k.fill_and_store(modulusLen=512) self.tls_session.server_tmp_rsa_key = k pubNum = k.pubkey.public_numbers() if not self.rsamod: self.rsamod = pkcs_i2osp(pubNum.n, k.pubkey.key_size // 8) if self.rsamodlen is None: self.rsamodlen = len(self.rsamod) rsaexplen = math.ceil(math.log(pubNum.e) / math.log(2) / 8.) if not self.rsaexp: self.rsaexp = pkcs_i2osp(pubNum.e, rsaexplen) if self.rsaexplen is None: self.rsaexplen = len(self.rsaexp)
def auth_encrypt(self, P, A, seq_num=None): """ Encrypt the data then prepend the explicit part of the nonce. The authentication tag is directly appended with the most recent crypto API. Additional data may be authenticated without encryption (as A). The 'seq_num' should never be used here, it is only a safeguard needed because one cipher (ChaCha20Poly1305) using TLS 1.2 logic in record.py actually is a _AEADCipher_TLS13 (even though others are not). """ if False in six.itervalues(self.ready): raise CipherError(P, A) if hasattr(self, "pc_cls"): self._cipher.mode._initialization_vector = self._get_nonce() self._cipher.mode._tag = None encryptor = self._cipher.encryptor() encryptor.authenticate_additional_data(A) res = encryptor.update(P) + encryptor.finalize() res += encryptor.tag else: if isinstance(self._cipher, AESCCM): res = self._cipher.encrypt(self._get_nonce(), P, A, tag_length=self.tag_len) else: res = self._cipher.encrypt(self._get_nonce(), P, A) nonce_explicit = pkcs_i2osp(self.nonce_explicit, self.nonce_explicit_len) self._update_nonce_explicit() return nonce_explicit + res
def __init__(self, key=None, salt=None, nonce_explicit=None): """ 'key' and 'salt' are to be provided as strings, whereas the internal 'nonce_explicit' is an integer (it is simpler for incrementation). """ self.ready = {"key": True, "salt": True, "nonce_explicit": True} if key is None: self.ready["key"] = False key = b"\0" * self.key_len if salt is None: self.ready["salt"] = False salt = b"\0" * self.salt_len if nonce_explicit is None: self.ready["nonce_explicit"] = False nonce_explicit = 0 if isinstance(nonce_explicit, str): nonce_explicit = pkcs_os2ip(nonce_explicit) # we use super() in order to avoid any deadlock with __setattr__ super(_AEADCipher, self).__setattr__("key", key) super(_AEADCipher, self).__setattr__("salt", salt) super(_AEADCipher, self).__setattr__("nonce_explicit", nonce_explicit) iv = salt + pkcs_i2osp(nonce_explicit, self.nonce_explicit_len) self._cipher = Cipher(self.pc_cls(key), self.pc_cls_mode(iv), backend=default_backend())
def fill_missing(self): k = PrivKeyRSA() k.fill_and_store(modulusLen=512) self.tls_session.server_tmp_rsa_key = k pubNum = k.pubkey.public_numbers() if not self.rsamod: self.rsamod = pkcs_i2osp(pubNum.n, k.pubkey.key_size // 8) if self.rsamodlen is None: self.rsamodlen = len(self.rsamod) rsaexplen = math.ceil(math.log(pubNum.e) / math.log(2) / 8.) if not self.rsaexp: self.rsaexp = pkcs_i2osp(pubNum.e, rsaexplen) if self.rsaexplen is None: self.rsaexplen = len(self.rsaexp)
def auth_encrypt(self, P, A, seq_num=None): """ Encrypt the data then prepend the explicit part of the nonce. The authentication tag is directly appended with the most recent crypto API. Additional data may be authenticated without encryption (as A). The 'seq_num' should never be used here, it is only a safeguard needed because one cipher (ChaCha20Poly1305) using TLS 1.2 logic in record.py actually is a _AEADCipher_TLS13 (even though others are not). """ if False in six.itervalues(self.ready): raise CipherError(P, A) if hasattr(self, "pc_cls"): self._cipher.mode._initialization_vector = self._get_nonce() self._cipher.mode._tag = None encryptor = self._cipher.encryptor() encryptor.authenticate_additional_data(A) res = encryptor.update(P) + encryptor.finalize() res += encryptor.tag else: if isinstance(self._cipher, AESCCM): res = self._cipher.encrypt(self._get_nonce(), P, A, tag_length=self.tag_len) else: res = self._cipher.encrypt(self._get_nonce(), P, A) nonce_explicit = pkcs_i2osp(self.nonce_explicit, self.nonce_explicit_len) self._update_nonce_explicit() return nonce_explicit + res
def fill_missing(self): s = self.tls_session params = s.client_kx_ecdh_params s.client_kx_privkey = ec.generate_private_key(params, default_backend()) pubkey = s.client_kx_privkey.public_key() x = pubkey.public_numbers().x y = pubkey.public_numbers().y self.ecdh_Yc = (b"\x04" + pkcs_i2osp(x, params.key_size // 8) + pkcs_i2osp(y, params.key_size // 8)) if s.client_kx_privkey and s.server_kx_pubkey: pms = s.client_kx_privkey.exchange(ec.ECDH(), s.server_kx_pubkey) s.pre_master_secret = pms s.compute_ms_and_derive_keys()
def post_build(self, pkt, pay): if not self.ecdh_Yc: try: self.fill_missing() except ImportError: pass if self.ecdh_Yclen is None: self.ecdh_Yclen = len(self.ecdh_Yc) return pkcs_i2osp(self.ecdh_Yclen, 1) + self.ecdh_Yc + pay
def post_build(self, pkt, pay): if not self.ecdh_Yc: try: self.fill_missing() except ImportError: pass if self.ecdh_Yclen is None: self.ecdh_Yclen = len(self.ecdh_Yc) return pkcs_i2osp(self.ecdh_Yclen, 1) + self.ecdh_Yc + pay
def __setattr__(self, name, val): if name == "key": if self._cipher is not None: self._cipher.algorithm.key = val self.ready["key"] = True elif name == "salt": iv = val + pkcs_i2osp(self.nonce_explicit, self.nonce_explicit_len) if self._cipher is not None: self._cipher.mode._initialization_vector = iv self.ready["salt"] = True elif name == "nonce_explicit": if isinstance(val, str): val = pkcs_os2ip(val) iv = self.salt + pkcs_i2osp(val, self.nonce_explicit_len) if self._cipher is not None: self._cipher.mode._initialization_vector = iv self.ready["nonce_explicit"] = True super(_AEADCipher, self).__setattr__(name, val)
def post_build(self, pkt, pay): s = self.tls_session if self.dh_Yc == "": params = s.client_kx_ffdh_params s.client_kx_privkey = params.generate_private_key() pubkey = s.client_kx_privkey.public_key() y = pubkey.public_numbers().y self.dh_Yc = pkcs_i2osp(y, pubkey.key_size / 8) # else, we assume that the user wrote the client_kx_privkey by himself if self.dh_Yclen is None: self.dh_Yclen = len(self.dh_Yc) if s.client_kx_privkey and s.server_kx_pubkey: pms = s.client_kx_privkey.exchange(s.server_kx_pubkey) s.pre_master_secret = pms s.compute_ms_and_derive_keys() return pkcs_i2osp(self.dh_Yclen, 2) + self.dh_Yc + pay
def post_build(self, pkt, pay): s = self.tls_session if self.dh_Yc == "": params = s.client_kx_ffdh_params s.client_kx_privkey = params.generate_private_key() pubkey = s.client_kx_privkey.public_key() y = pubkey.public_numbers().y self.dh_Yc = pkcs_i2osp(y, pubkey.key_size/8) # else, we assume that the user wrote the client_kx_privkey by himself if self.dh_Yclen is None: self.dh_Yclen = len(self.dh_Yc) if s.client_kx_privkey and s.server_kx_pubkey: pms = s.client_kx_privkey.exchange(s.server_kx_pubkey) s.pre_master_secret = pms s.compute_ms_and_derive_keys() return pkcs_i2osp(self.dh_Yclen, 2) + self.dh_Yc + pay
def fill_missing(self): s = self.tls_session s.client_kx_privkey = s.client_kx_ffdh_params.generate_private_key() pubkey = s.client_kx_privkey.public_key() y = pubkey.public_numbers().y self.dh_Yc = pkcs_i2osp(y, pubkey.key_size // 8) if s.client_kx_privkey and s.server_kx_pubkey: pms = s.client_kx_privkey.exchange(s.server_kx_pubkey) s.pre_master_secret = pms s.compute_ms_and_derive_keys()
def fill_missing(self): s = self.tls_session params = s.client_kx_ffdh_params s.client_kx_privkey = params.generate_private_key() pubkey = s.client_kx_privkey.public_key() y = pubkey.public_numbers().y self.dh_Yc = pkcs_i2osp(y, pubkey.key_size // 8) if s.client_kx_privkey and s.server_kx_pubkey: pms = s.client_kx_privkey.exchange(s.server_kx_pubkey) s.pre_master_secret = pms s.compute_ms_and_derive_keys()
def fill_missing(self): ext_k = rsa.generate_private_key(public_exponent=0x10001, key_size=512, backend=default_backend()) pem_k = ext_k.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.TraditionalOpenSSL, encryption_algorithm=serialization.NoEncryption()) k = PrivKeyRSA(pem_k) self.tls_session.server_tmp_rsa_key = k pubNum = k.pubkey.public_numbers() if self.rsamod is "": self.rsamod = pkcs_i2osp(pubNum.n, k.pubkey.key_size/8) if self.rsamodlen is None: self.rsamodlen = len(self.rsamod) rsaexplen = math.ceil(math.log(pubNum.e)/math.log(2)/8.) if self.rsaexp is "": self.rsaexp = pkcs_i2osp(pubNum.e, rsaexplen) if self.rsaexplen is None: self.rsaexplen = len(self.rsaexp)
def _tls_auth_decrypt(self, s): """ Provided with the record header and AEAD-ciphered data, return the sliced and clear tuple (TLSInnerPlaintext, tag). Note that we still return the slicing of the original input in case of decryption failure. Also, if the integrity check fails, a warning will be issued, but we still return the sliced (unauthenticated) plaintext. """ rcs = self.tls_session.rcs read_seq_num = struct.pack("!Q", rcs.seq_num) rcs.seq_num += 1 add_data = (pkcs_i2osp(self.type, 1) + pkcs_i2osp(self.version, 2) + pkcs_i2osp(len(s), 2)) try: return rcs.cipher.auth_decrypt(add_data, s, read_seq_num) except CipherError as e: return e.args except AEADTagError as e: pkt_info = self.firstlayer().summary() log_runtime.info("TLS: record integrity check failed [%s]", pkt_info) # noqa: E501 return e.args
def fill_missing(self): ext_k = rsa.generate_private_key(public_exponent=0x10001, key_size=512, backend=default_backend()) pem_k = ext_k.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.TraditionalOpenSSL, encryption_algorithm=serialization.NoEncryption()) k = PrivKeyRSA(pem_k) self.tls_session.server_tmp_rsa_key = k pubNum = k.pubkey.public_numbers() if self.rsamod is "": self.rsamod = pkcs_i2osp(pubNum.n, k.pubkey.key_size / 8) if self.rsamodlen is None: self.rsamodlen = len(self.rsamod) rsaexplen = math.ceil(math.log(pubNum.e) / math.log(2) / 8.) if self.rsaexp is "": self.rsaexp = pkcs_i2osp(pubNum.e, rsaexplen) if self.rsaexplen is None: self.rsaexplen = len(self.rsaexp)
def post_build(self, pkt, pay): s = self.tls_session if self.ecdh_Yc == "": params = s.client_kx_ecdh_params s.client_kx_privkey = ec.generate_private_key( params, default_backend()) pubkey = s.client_kx_privkey.public_key() x = pubkey.public_numbers().x y = pubkey.public_numbers().y self.ecdh_Yc = (b"\x04" + pkcs_i2osp(x, params.key_size / 8) + pkcs_i2osp(y, params.key_size / 8)) # else, we assume that the user wrote the client_kx_privkey by himself if self.ecdh_Yclen is None: self.ecdh_Yclen = len(self.ecdh_Yc) if s.client_kx_privkey and s.server_kx_pubkey: pms = s.client_kx_privkey.exchange(ec.ECDH(), s.server_kx_pubkey) s.pre_master_secret = pms s.compute_ms_and_derive_keys() return pkcs_i2osp(self.ecdh_Yclen, 1) + self.ecdh_Yc + pay
def fill_missing(self): """ We do not want TLSServerKeyExchange.build() to overload and recompute things every time it is called. This method can be called specifically to have things filled in a smart fashion. Note that we do not expect default_params.g to be more than 0xff. """ s = self.tls_session default_params = _ffdh_groups['modp2048'][0].parameter_numbers() default_mLen = _ffdh_groups['modp2048'][1] if not self.dh_p: self.dh_p = pkcs_i2osp(default_params.p, default_mLen // 8) if self.dh_plen is None: self.dh_plen = len(self.dh_p) if not self.dh_g: self.dh_g = pkcs_i2osp(default_params.g, 1) if self.dh_glen is None: self.dh_glen = 1 p = pkcs_os2ip(self.dh_p) g = pkcs_os2ip(self.dh_g) real_params = dh.DHParameterNumbers(p, g).parameters(default_backend()) if not self.dh_Ys: s.server_kx_privkey = real_params.generate_private_key() pubkey = s.server_kx_privkey.public_key() y = pubkey.public_numbers().y self.dh_Ys = pkcs_i2osp(y, pubkey.key_size // 8) # else, we assume that the user wrote the server_kx_privkey by himself if self.dh_Yslen is None: self.dh_Yslen = len(self.dh_Ys) if not s.client_kx_ffdh_params: s.client_kx_ffdh_params = real_params
def fill_missing(self): """ We do not want TLSServerKeyExchange.build() to overload and recompute things everytime it is called. This method can be called specifically to have things filled in a smart fashion. Note that we do not expect default_params.g to be more than 0xff. """ s = self.tls_session default_params = _ffdh_groups['modp2048'][0].parameter_numbers() default_mLen = _ffdh_groups['modp2048'][1] if not self.dh_p: self.dh_p = pkcs_i2osp(default_params.p, default_mLen // 8) if self.dh_plen is None: self.dh_plen = len(self.dh_p) if not self.dh_g: self.dh_g = pkcs_i2osp(default_params.g, 1) if self.dh_glen is None: self.dh_glen = 1 p = pkcs_os2ip(self.dh_p) g = pkcs_os2ip(self.dh_g) real_params = dh.DHParameterNumbers(p, g).parameters(default_backend()) if not self.dh_Ys: s.server_kx_privkey = real_params.generate_private_key() pubkey = s.server_kx_privkey.public_key() y = pubkey.public_numbers().y self.dh_Ys = pkcs_i2osp(y, pubkey.key_size // 8) # else, we assume that the user wrote the server_kx_privkey by himself if self.dh_Yslen is None: self.dh_Yslen = len(self.dh_Ys) if not s.client_kx_ffdh_params: s.client_kx_ffdh_params = real_params
def post_build(self, pkt, pay): s = self.tls_session if self.ecdh_Yc == "": params = s.client_kx_ecdh_params s.client_kx_privkey = ec.generate_private_key(params, default_backend()) pubkey = s.client_kx_privkey.public_key() x = pubkey.public_numbers().x y = pubkey.public_numbers().y self.ecdh_Yc = (b"\x04" + pkcs_i2osp(x, params.key_size/8) + pkcs_i2osp(y, params.key_size/8)) # else, we assume that the user wrote the client_kx_privkey by himself if self.ecdh_Yclen is None: self.ecdh_Yclen = len(self.ecdh_Yc) if s.client_kx_privkey and s.server_kx_pubkey: pms = s.client_kx_privkey.exchange(ec.ECDH(), s.server_kx_pubkey) s.pre_master_secret = pms s.compute_ms_and_derive_keys() return pkcs_i2osp(self.ecdh_Yclen, 1) + self.ecdh_Yc + pay
def fill_missing(self): s = self.tls_session s.client_kx_privkey = s.client_kx_ffdh_params.generate_private_key() pubkey = s.client_kx_privkey.public_key() y = pubkey.public_numbers().y self.dh_Yc = pkcs_i2osp(y, pubkey.key_size // 8) if s.client_kx_privkey and s.server_kx_pubkey: pms = s.client_kx_privkey.exchange(s.server_kx_pubkey) s.pre_master_secret = pms if not s.extms or s.session_hash: # If extms is set (extended master secret), the key will # need the session hash to be computed. This is provided # by the TLSClientKeyExchange. Same in all occurrences s.compute_ms_and_derive_keys()
def auth_encrypt(self, P, A): """ Encrypt the data, prepend the explicit part of the nonce, and append the computed authentication code. Additional data may be authenticated without encryption (as A). Note that the cipher's authentication tag must be None when encrypting. """ if False in self.ready.itervalues(): raise CipherError, (P, A) self._cipher.mode._tag = None encryptor = self._cipher.encryptor() encryptor.authenticate_additional_data(A) res = encryptor.update(P) + encryptor.finalize() res += encryptor.tag nonce_explicit = pkcs_i2osp(self.nonce_explicit, self.nonce_explicit_len) self._update_nonce() return nonce_explicit + res
def _get_nonce(self): return (self.fixed_iv + pkcs_i2osp(self.nonce_explicit, self.nonce_explicit_len))
def _get_nonce(self): return (self.fixed_iv + pkcs_i2osp(self.nonce_explicit, self.nonce_explicit_len))
def decryptTLSStream(self): """ Generator function that decrypts an RDP stream that uses TLS. """ tpktParser = TPKTParser() tls = None clientRandom = None serverRandom = None currentTimeStamp = None reconstructingRecord = False savedRecord = None savedPayload = b"" tlsKeyGenerated = False for packet in self.packets: ip = packet.getlayer(IP) tcp = packet.getlayer(TCP) if len(tcp.payload) == 0 or tcp.flags & TCPFlags.PSH == 0: continue currentTimeStamp = packet.time currentSrc = ip.src currentDst = ip.dst # The first couple messages don't use TLS. Check if it's one of those messages and output it as is. if hasattr(tcp, "load") and tpktParser.isTPKTPDU(tcp.load): yield PCAPStream.output(tcp.load, currentTimeStamp, ip.src, ip.dst) continue # Create the TLS session context. if not tls: tls = tlsSession( ipsrc=ip.src, ipdst=ip.dst, sport=tcp.sport, dport=tcp.dport, connection_end="server", ) if tls.ipsrc != ip.src: tls = tls.mirror() # Pass every TLS message through our own custom session so the state is kept properly record = packet[TLS] record = TLS(bytes(record), tls_session=tls) for msg in record.msg: if isinstance(msg, TLSClientHello): clientRandom = pkcs_i2osp(msg.gmt_unix_time, 4) + msg.random_bytes elif isinstance(msg, TLSServerHello): serverRandom = pkcs_i2osp(msg.gmt_unix_time, 4) + msg.random_bytes elif isinstance(msg, TLSNewSessionTicket): # Session established, set master secret. tls.rcs.derive_keys( client_random=clientRandom, server_random=serverRandom, master_secret=self.masterSecret, ) tls.wcs.derive_keys( client_random=clientRandom, server_random=serverRandom, master_secret=self.masterSecret, ) tlsKeyGenerated = True elif isinstance(msg, TLSApplicationData): yield PCAPStream.output(msg.data, currentTimeStamp, ip.src, ip.dst)