def decrypt_html(pms2, tlsn_session): '''Receive correct server mac key and then decrypt server response (html), (includes authentication of response). Submit resulting html for browser for display (optionally render by stripping http headers).''' print("\nStarting decryption of content, may take a few seconds...") try: tlsn_session.auditor_secret = pms2[:tlsn_session.n_auditor_entropy] tlsn_session.set_auditor_secret() tlsn_session.set_master_secret_half( ) #without arguments sets the whole MS tlsn_session.do_key_expansion( ) #also resets encryption connection state except shared.TLSNSSLError: shared.ssl_dump(tlsn_session) raise #either using slowAES or a RC4 ciphersuite try: plaintext, bad_mac = tlsn_session.process_server_app_data_records() # print(plaintext) except shared.TLSNSSLError: shared.ssl_dump(tlsn_session) raise if bad_mac: return False, "ERROR! Audit not valid! Plaintext is not authenticated." # if http data chunked, dechunk plaintext = shared.dechunk_http(plaintext) if global_use_gzip: plaintext = shared.gunzip_http(plaintext) return True, plaintext
def prepare_pms(tlsn_session, session_id): n = shared.bi2ba(tlsn_session.server_modulus) rs_choice = random.choice(shared.reliable_sites.keys()) print("ramdom choice:", rs_choice) for i in range(10): #keep trying until reliable site check succeeds try: pms_session = shared.TLSNClientSession( rs_choice, shared.reliable_sites[rs_choice][0], ccs=53, tlsver=global_tlsver) if not pms_session: raise Exception( "Client session construction failed in prepare_pms") tls_sock = shared.create_sock(pms_session.server_name, pms_session.ssl_port) pms_session.start_handshake(tls_sock) # 1# message exchange with auditor reply = send_and_recv( 'rcr_rsr_rsname_n', pms_session.client_random + pms_session.server_random + rs_choice[:5] + n, session_id) if reply[0] != 'success': raise Exception( 'Failed to receive a reply for rcr_rsr_rsname_n:') if not reply[1] == 'rrsapms_rhmac_rsapms': raise Exception('bad reply. Expected rrsapms_rhmac_rsapms:') reply_data = reply[2] rrsapms2 = reply_data[:256] pms_session.p_auditor = reply_data[256:304] rsapms2 = reply_data[304:] response = pms_session.complete_handshake(tls_sock, rrsapms2) tls_sock.close() if not response: print("PMS trial failed") continue #judge success/fail based on whether a properly encoded #Change Cipher Spec record is returned by the server (we could #also check the server finished, but it isn't necessary) if not response.count( shared.TLSRecord(shared.chcis, f='\x01', tlsver=global_tlsver).serialized): print("PMS trial failed, retrying. (", binascii.hexlify(response), ")") continue tlsn_session.auditee_secret = pms_session.auditee_secret tlsn_session.auditee_padding_secret = pms_session.auditee_padding_secret tlsn_session.enc_second_half_pms = shared.ba2int(rsapms2) tlsn_session.set_enc_first_half_pms() tlsn_session.set_encrypted_pms() return except shared.TLSNSSLError: shared.ssl_dump(pms_session, fn='preparepms_ssldump') shared.ssl_dump(tlsn_session) raise raise Exception ('Could not prepare PMS with ', rs_choice, ' after 10 tries. Please '+\ 'double check that you are using a valid public key modulus for this site; '+\ 'it may have expired.')
def make_tlsn_request(headers, tlsn_session, tls_sock): '''Send TLS request including http headers and receive server response.''' try: tlsn_session.build_request(tls_sock, headers) response = shared.recv_socket( tls_sock) #not handshake flag means we wait on timeout if not response: raise Exception( "Received no response to request, cannot continue audit.") tlsn_session.store_server_app_data_records(response) except shared.TLSNSSLError: shared.ssl_dump(tlsn_session) raise tls_sock.close() #we return the full record set, not only the response to our request return tlsn_session.unexpected_server_app_data_raw + response
def start_generate(server_name, headers, server_modulus): global global_tlsver global global_use_gzip global global_use_slowaes print("server_name:", server_name) session_id = ''.join( random.choice(string.ascii_lowercase + string.digits) for x in range(10)) tlsn_session = shared.TLSNClientSession(server_name, tlsver=global_tlsver) tlsn_session.server_modulus = shared.ba2int(server_modulus) tlsn_session.server_mod_length = shared.bi2ba(len(server_modulus)) print('Preparing encrypted pre-master secret') prepare_pms(tlsn_session, session_id) for i in range(10): try: print('Performing handshake with server') tls_sock = shared.create_sock(tlsn_session.server_name, tlsn_session.ssl_port) tlsn_session.start_handshake(tls_sock) retval = negotiate_crippled_secrets(tlsn_session, tls_sock, session_id) if not retval == 'success': raise shared.TLSNSSLError('Failed to negotiate secrets: ' + retval) print('Getting data from server') # response = make_tlsn_request(headers, tlsn_session, tls_sock) #prefix response with number of to-be-ignored records, #note: more than 256 unexpected records will cause a failure of audit. Just as well! response = shared.bi2ba( tlsn_session.unexpected_server_app_data_count, fixed=1) + response break except shared.TLSNSSLError: shared.ssl_dump(tlsn_session) raise except Exception as e: print( 'Exception caught while getting data from server, retrying...', e) if i == 9: raise Exception('Notarization failed') continue # commit its own hash, get auditor's secrets ok, commit_hash, pms2, signature, audit_time = commit_session( tlsn_session, response, session_id) if not ok: return ok, "commit hash to auditor failed" # no need to verify signatures msg = sha256(commit_hash + pms2 + shared.bi2ba(tlsn_session.server_modulus) + audit_time).digest() oracle_ba_modulus = binascii.unhexlify(oracle_modulus) oracle_int_modulus = shared.ba2int(oracle_ba_modulus) print(msg) print(signature) if not shared.verify_signature(msg, signature, oracle_int_modulus): raise Exception("Notarization FAILED, notary signature invalid.") ok, result = decrypt_html(pms2, tlsn_session) if not ok: return ok, result html = result print('Verified OK') host_line = "host: " + server_name + "\r\n" readable = host_line b = """""" "" b = """<meta[ ]+http-equiv=["']?content-type["']?[ ]+content=["']?text/html;[ ]*charset=([0-9-a-zA-Z]+)["']?""" B = re.compile(b, re.IGNORECASE) encodings = B.search(html) # print("encoding:") if encodings != None: encoding = encodings.group(1) if encoding.lower() == 'gb231' or encoding.lower() == 'gb2312': html = html.decode('gb18030') # print(html) else: html = html.decode(encoding) else: html.decode('utf-8') print("html len:", len(html)) html = "response:\r\n" + html + '\r\n-----PROOF BINARY DATA-----\r\n' # print(html) # readable += html.decode().encode('utf-8') readable += html # chosen_cipher_suite audit_data = shared.bi2ba(tlsn_session.chosen_cipher_suite, fixed=2) # 2 bytes # client_random, server_random audit_data += tlsn_session.client_random + tlsn_session.server_random # 64 bytes # pre_master_key, two parts, each generated by auditee or auditor audit_data += tlsn_session.pms1 + pms2 #48 bytes # length of server_cert, sent by TLS Server, why fix 3 ? audit_data += shared.bi2ba(len(tlsn_session.server_certificate.certs), fixed=3) # server_certs, it's very hard to verify these babies on-chain. audit_data += tlsn_session.server_certificate.certs # tls version audit_data += tlsn_session.tlsver #2 bytes # proposed tls version in ClientHello? # audit_data += tlsn_session.initial_tlsver #2 bytes # length of response audit_data += shared.bi2ba(len(response), fixed=8) #8 bytes # response data audit_data += response #note that it includes unexpected pre-request app data, 10s of kB # IV_after_finished used by RC4, necessory to authenticate response IV = tlsn_session.IV_after_finished if tlsn_session.chosen_cipher_suite in [47,53] \ else shared.rc4_state_to_bytearray(tlsn_session.IV_after_finished) # length of IV, should be before IV ? audit_data += shared.bi2ba(len(IV), fixed=2) #2 bytes # IV audit_data += IV #16 bytes or 258 bytes for RC4. # length of auditor's public key, in modulus formate audit_data += shared.bi2ba(len(oracle_ba_modulus), fixed=2) # auditor's signature audit_data += signature #512 bytes RSA PKCS 1 v1.5 padding # hash that auditee commits to auditor audit_data += commit_hash #32 bytes sha256 hash # auditor's public key audit_data += oracle_ba_modulus # timestamp used to make signature unique audit_data += audit_time # audit_data = bytearray(readable.encode('utf-8')) + audit_data date = time_str = time.strftime('%d-%b-%Y-%H-%M-%S-', time.gmtime()) filename = date + session_id + ".pgsg" # print("proof file name:",filename) proofpath = join(PROOF_DIR, filename) with open(proofpath, "w") as f: f.write(readable) f.write(audit_data) return True, filename