def registerAuditeeThread():
    global auditee_nick
    global google_modulus
    global google_exponent

    with open(os.path.join(current_sessiondir, 'mypubkey'), 'r') as f: my_pubkey_pem =f.read()
    myPublicKey = rsa.PublicKey.load_pkcs1(my_pubkey_pem)
    myModulus = shared.bigint_to_bytearray(myPublicKey.n)[:10]
    bIsAuditeeRegistered = False

    while not (bIsAuditeeRegistered or bTerminateAllThreads):
        msg = shared.receive_single_msg((':google_pubkey:',':client_hello:'))
        if not msg: continue
        if msg[0].startswith(':google_pubkey:') and auditee_nick != '': #we already got the first client_hello part
            try:
                b64_google_pubkey = msg[0][len(':google_pubkey:'):]
                google_pubkey =  base64.b64decode(b64_google_pubkey)
                google_modulus_byte = google_pubkey[:256]
                google_exponent_byte = google_pubkey[256:]
                google_modulus = int(google_modulus_byte.encode('hex'),16)
                google_exponent = int(google_exponent_byte.encode('hex'),16)
                print ('Auditee successfully verified')
                bIsAuditeeRegistered = True
                break
            except:
                print ('Error while processing google pubkey')
                auditee_nick=''#erase the nick so that the auditee could try registering again
                continue
        #if we got here, it must be a client hello
        b64_hello = msg[0][len(':client_hello:'):]
        try:
            hello = base64.b64decode(b64_hello)
            modulus = hello[:10] #this is the first 10 bytes of modulus of auditor's pubkey
            sig = hello[10:] #this is a sig for 'client_hello'. The auditor is expected to have received auditee's pubkey via other channels
            if modulus != myModulus : continue
            rsa.verify('client_hello', sig, auditeePublicKey)
            #we get here if there was no exception
            auditee_nick = msg[1]
        except:
            print ('Verification of a hello message failed')
            continue

    if not bIsAuditeeRegistered:
        return ('failure',)
    #else send back a hello message
    signed_hello = rsa.sign('server_hello', myPrivateKey, 'SHA-1')
    b64_signed_hello = base64.b64encode(signed_hello)

    #send twice because it was observed that the msg would not appear on the chan
    for x in range(2):
        shared.send_raw(':'+auditee_nick + ' server_hello:'+b64_signed_hello)
        time.sleep(2)

    progressQueue.put(time.strftime('%H:%M:%S', time.localtime()) + ': Auditee has been authorized. Awaiting data...')
    thread = threading.Thread(target= receivingThread)
    thread.daemon = True
    thread.start()
    thread = threading.Thread(target= process_messages)
    thread.daemon = True
    thread.start()
def get_recent_keys():
    global myPrivateKey
    global auditeePublicKey
    #this is the very first command that we expect in a new session.
    #If this is the very first time tlsnotary is run, there will be no saved keys
    #otherwise we load up the saved keys which the user can override with new keys if need be
    my_pubkey_export = auditee_pubkey_export = ''
    if os.path.exists(os.path.join(datadir, 'recentkeys')):
        if os.path.exists(os.path.join(datadir, 'recentkeys', 'myprivkey')) and os.path.exists(os.path.join(datadir, 'recentkeys', 'mypubkey')):
            with open(os.path.join(datadir, 'recentkeys', 'myprivkey'), 'r') as f: my_privkey_pem = f.read()
            with open(os.path.join(datadir, 'recentkeys', 'mypubkey'), 'r') as f: my_pubkey_pem = f.read()
            with open(os.path.join(current_sessiondir, 'myprivkey'), 'w') as f: f.write(my_privkey_pem)
            with open(os.path.join(current_sessiondir, 'mypubkey'), 'w') as f: f.write(my_pubkey_pem)
            myPrivateKey = rsa.PrivateKey.load_pkcs1(my_privkey_pem)
            my_pubkey = rsa.PublicKey.load_pkcs1(my_pubkey_pem)
            my_pubkey_export = base64.b64encode(shared.bigint_to_bytearray(my_pubkey.n))
        if os.path.exists(os.path.join(datadir, 'recentkeys', 'auditeepubkey')):
            with open(os.path.join(datadir, 'recentkeys', 'auditeepubkey'), 'r') as f: auditee_pubkey_pem = f.read()
            with open(os.path.join(current_sessiondir, 'auditorpubkey'), 'w') as f: f.write(auditee_pubkey_pem)
            auditeePublicKey = rsa.PublicKey.load_pkcs1(auditee_pubkey_pem)
            auditee_pubkey = rsa.PublicKey.load_pkcs1(auditee_pubkey_pem)
            auditee_pubkey_export = base64.b64encode(shared.bigint_to_bytearray(auditee_pubkey.n))
    return my_pubkey_export, auditee_pubkey_export
def new_keypair():
    global myPrivateKey
    pubkey, privkey = rsa.newkeys(1024)
    myPrivateKey = privkey
    my_pubkey_pem = pubkey.save_pkcs1()
    my_privkey_pem = privkey.save_pkcs1()
    #------------------------------------------
    with open(os.path.join(current_sessiondir, 'myprivkey'), 'w') as f: f.write(my_privkey_pem)
    with open(os.path.join(current_sessiondir, 'mypubkey'), 'w') as f: f.write(my_pubkey_pem)
    #also save the keys as recent, so that they could be reused in the next session
    if not os.path.exists(os.path.join(datadir, 'recentkeys')): os.makedirs(os.path.join(datadir, 'recentkeys'))
    with open(os.path.join(datadir, 'recentkeys' , 'myprivkey'), 'w') as f: f.write(my_privkey_pem)
    with open(os.path.join(datadir, 'recentkeys', 'mypubkey'), 'w') as f: f.write(my_pubkey_pem)
    my_pubkey = rsa.PublicKey.load_pkcs1(my_pubkey_pem)
    my_pubkey_export = base64.b64encode(shared.bigint_to_bytearray(my_pubkey.n))
    return my_pubkey_export
def process_messages():
    while True:
        try: msg = recvQueue.get(block=True, timeout=1)
        except: continue
        if msg.startswith('gcr_gsr:'):
            b64_gcr_gsr = msg[len('gcr_gsr:'):]
            try: gcr_gsr = base64.b64decode(b64_gcr_gsr)
            except: raise Exception ('base64 decode error in cr_gcr_gsr')
            google_cr = gcr_gsr[:32]
            google_sr = gcr_gsr[32:64]
            #second half of pre-master secret
            PMS2 =  os.urandom(secretbytes_amount) + ('\x00' * (24-secretbytes_amount-1)) + '\x01'
            RSA_PMS_google_int = pow( int(('\x01'+('\x00'*25)+PMS2).encode('hex'),16), google_exponent, google_modulus )
            grsapms = shared.bigint_to_bytearray(RSA_PMS_google_int)
            #-------------------BEGIN get sha1hmac for google
            label = "master secret"
            seed = google_cr + google_sr        
            sha1A1 = hmac.new(PMS2, label+seed, hashlib.sha1).digest()
            sha1A2 = hmac.new(PMS2, sha1A1, hashlib.sha1).digest()
            sha1A3 = hmac.new(PMS2, sha1A2, hashlib.sha1).digest()          
            sha1hmac1 = hmac.new(PMS2, sha1A1 + label + seed, hashlib.sha1).digest()
            sha1hmac2 = hmac.new(PMS2, sha1A2 + label + seed, hashlib.sha1).digest()
            sha1hmac3 = hmac.new(PMS2, sha1A3 + label + seed, hashlib.sha1).digest()
            ghmac = (sha1hmac1+sha1hmac2+sha1hmac3)[:48]
            #-------------------END get sha1hmac for google            
            b64_grsapms_ghmac = base64.b64encode(grsapms+ghmac)
            send_message('grsapms_ghmac:'+ b64_grsapms_ghmac)
            continue
         #---------------------------------------------------------------------#
        elif msg.startswith('cr_sr_hmac_n_e:'): 
            progressQueue.put(time.strftime('%H:%M:%S', time.localtime()) + ': Processing data from the auditee.')
            b64_cr_sr_hmac_n_e = msg[len('cr_sr_hmac_n_e:'):]
            try: cr_sr_hmac_n_e = base64.b64decode(b64_cr_sr_hmac_n_e)
            except: raise Exception ('base64 decode error in cr_sr_hmac_n_e')
            cipher_suite_int = int(cr_sr_hmac_n_e[:1].encode('hex'), 16)
            if cipher_suite_int == 4: cipher_suite = 'RC4MD5'
            elif cipher_suite_int == 5: cipher_suite = 'RC4SHA'
            elif cipher_suite_int == 47: cipher_suite = 'AES128'
            elif cipher_suite_int == 53: cipher_suite = 'AES256'
            else: raise Exception ('invalid cipher sute')
            cr = cr_sr_hmac_n_e[1:33]
            sr = cr_sr_hmac_n_e[33:65]
            md5hmac1_for_MS=cr_sr_hmac_n_e[65:89] #half of MS's 48 bytes
            n_len = cr_sr_hmac_n_e[89:91]
            n_len_int = int(n_len.encode('hex'),16)
            n = cr_sr_hmac_n_e[91:91+n_len_int]
            e = cr_sr_hmac_n_e[91+n_len_int:91+n_len_int+3]
            n_int = int(n.encode('hex'),16)
            e_int = int(e.encode('hex'),16)                        
            #RSA encryption without padding: ciphertext = plaintext^e mod n
            RSA_PMS2_int = pow( int(('\x01'+('\x00'*25)+PMS2).encode('hex'),16), e_int, n_int )
            #get my sha1hmac to xor with auditee's md5hmac and get MS first half
            label = "master secret"
            seed = cr + sr        
            sha1A1 = hmac.new(PMS2, label+seed, hashlib.sha1).digest()
            sha1A2 = hmac.new(PMS2, sha1A1, hashlib.sha1).digest()
            sha1A3 = hmac.new(PMS2, sha1A2, hashlib.sha1).digest()            
            sha1hmac1 = hmac.new(PMS2, sha1A1 + label + seed, hashlib.sha1).digest()
            sha1hmac2 = hmac.new(PMS2, sha1A2 + label + seed, hashlib.sha1).digest()
            sha1hmac3 = hmac.new(PMS2, sha1A3 + label + seed, hashlib.sha1).digest()
            sha1hmac = (sha1hmac1+sha1hmac2+sha1hmac3)[:48]
            sha1hmac1_for_MS = sha1hmac[:24]
            sha1hmac2_for_MS = sha1hmac[24:48]
            MS1 = shared.xor(md5hmac1_for_MS, sha1hmac1_for_MS)
            #master secret key expansion
            #see RFC2246 6.3. Key calculation & 5. HMAC and the pseudorandom function
            #The amount of key material for each ciphersuite:
            #AES256-CBC-SHA: mac key 20*2, encryption key 32*2, IV 16*2 == 136bytes
            #AES128-CBC-SHA: mac key 20*2, encryption key 16*2, IV 16*2 == 104bytes
            #RC4128_MD5: mac key 16*2, encryption key 16*2 == 64 bytes
            #RC4128_SHA: mac key 20*2, encryption key 16*2 == 72bytes
            #Regardless of theciphersuite, we generate the max key material we'd ever need which is 136 bytes
            label = "key expansion"
            seed = sr + cr
            #this is not optimized in a loop on purpose. I want people to see exactly what is going on
            md5A1 = hmac.new(MS1, label+seed, hashlib.md5).digest()
            md5A2 = hmac.new(MS1, md5A1, hashlib.md5).digest()
            md5A3 = hmac.new(MS1, md5A2, hashlib.md5).digest()
            md5A4 = hmac.new(MS1, md5A3, hashlib.md5).digest()
            md5A5 = hmac.new(MS1, md5A4, hashlib.md5).digest()
            md5A6 = hmac.new(MS1, md5A5, hashlib.md5).digest()
            md5A7 = hmac.new(MS1, md5A6, hashlib.md5).digest()
            md5A8 = hmac.new(MS1, md5A7, hashlib.md5).digest()
            md5A9 = hmac.new(MS1, md5A8, hashlib.md5).digest()           
            md5hmac1 = hmac.new(MS1, md5A1 + label + seed, hashlib.md5).digest()
            md5hmac2 = hmac.new(MS1, md5A2 + label + seed, hashlib.md5).digest()
            md5hmac3 = hmac.new(MS1, md5A3 + label + seed, hashlib.md5).digest()
            md5hmac4 = hmac.new(MS1, md5A4 + label + seed, hashlib.md5).digest()
            md5hmac5 = hmac.new(MS1, md5A5 + label + seed, hashlib.md5).digest()
            md5hmac6 = hmac.new(MS1, md5A6 + label + seed, hashlib.md5).digest()
            md5hmac7 = hmac.new(MS1, md5A7 + label + seed, hashlib.md5).digest()
            md5hmac8 = hmac.new(MS1, md5A8 + label + seed, hashlib.md5).digest()
            md5hmac9 = hmac.new(MS1, md5A9 + label + seed, hashlib.md5).digest()
            md5hmac = (md5hmac1+md5hmac2+md5hmac3+md5hmac4+md5hmac5+md5hmac6+md5hmac7+md5hmac8+md5hmac9)
            #fill the place of server MAC with zeroes
            if cipher_suite == 'AES256': 
                md5hmac_for_ek = md5hmac[:20] + bytearray(os.urandom(20)) + md5hmac[40:136]
            elif cipher_suite == 'AES128':
                md5hmac_for_ek = md5hmac[:20] + bytearray(os.urandom(20)) + md5hmac[40:104]
            elif cipher_suite == 'RC4SHA':
                md5hmac_for_ek = md5hmac[:20] + bytearray(os.urandom(20)) + md5hmac[40:72]
            elif cipher_suite == 'RC4MD5': 
                md5hmac_for_ek = md5hmac[:16] + bytearray(os.urandom(16)) + md5hmac[32:64]     
            rsapms_hmacms_hmacek = shared.bigint_to_bytearray(RSA_PMS2_int)+sha1hmac2_for_MS+md5hmac_for_ek
            b64_rsapms_hmacms_hmacek = base64.b64encode(rsapms_hmacms_hmacek)
            send_message('rsapms_hmacms_hmacek:'+ b64_rsapms_hmacms_hmacek)
            continue
        #---------------------------------------------------------------------#
        elif msg.startswith('verify_md5sha:'):
            b64_md5sha = msg[len('verify_md5sha:') : ]
            try: md5sha = base64.b64decode(b64_md5sha)
            except: raise Exception ('base64 decode error in verify_md5sha')
            md5 = md5sha[:16] #md5 hash is 16bytes
            sha = md5sha[16:]   #sha hash is 20 bytes          
            #calculate verify_data for Finished message
            #see RFC2246 7.4.9. Finished & 5. HMAC and the pseudorandom function
            label = "client finished"
            seed = md5 + sha          
            md5A1 = hmac.new(MS1, label+seed, hashlib.md5).digest()
            md5hmac1 = hmac.new(MS1, md5A1 + label + seed, hashlib.md5).digest()
            b64_verify_hmac = base64.b64encode(md5hmac1)
            send_message('verify_hmac:'+b64_verify_hmac)
            continue
        #------------------------------------------------------------------------------------------------------#    
        elif msg.startswith('commit_hash:'):
            b64_commit_hash = msg[len('commit_hash:'):]
            try: commit_hash = base64.b64decode(b64_commit_hash)
            except: raise Exception ('base64 decode error in commit_hash')
            trace_hash = commit_hash[:32]
            md5hmac_hash = commit_hash[32:64]
            commit_dir = os.path.join(current_sessiondir, 'commit')
            if not os.path.exists(commit_dir): os.makedirs(commit_dir)
            #file names are assigned sequentially hash1, hash2 etc.
            #The auditee must provide tracefiles trace1, trace2 corresponding to these sequence numbers
            commdir_list = os.listdir(commit_dir)
            #get last seqno
            seqnos = [int(one_trace[len('tracehash'):]) for one_trace 
                      in commdir_list if one_trace.startswith('tracehash')]
            last_seqno = max([0] + seqnos) #avoid throwing by feeding at least one value 0
            my_seqno = last_seqno+1
            trace_hash_path = os.path.join(commit_dir, 'tracehash'+str(my_seqno))
            n_hexlified = binascii.hexlify(n)
            n_write = " ".join(n_hexlified[i:i+2] for i in range(0, len(n_hexlified), 2)) #pubkey in the format 09 56 23 ....
            pubkey_path = os.path.join(commit_dir, 'pubkey'+str(my_seqno))
            trace_hash_path = os.path.join(commit_dir, 'tracehash'+str(my_seqno))
            md5hmac_hash_path =  os.path.join(commit_dir, 'md5hmac_hash'+str(my_seqno))
            with open(pubkey_path, 'wb') as f: f.write(n_write)            
            with open(trace_hash_path, 'wb') as f: f.write(trace_hash)
            with open(md5hmac_hash_path, 'wb') as f: f.write(md5hmac_hash)
            sha1hmac_path = os.path.join(commit_dir, 'sha1hmac'+str(my_seqno))
            with open(sha1hmac_path, 'wb') as f: f.write(sha1hmac)
            cr_path = os.path.join(commit_dir, 'cr'+str(my_seqno))
            with open(cr_path, 'wb') as f: f.write(cr)
            b64_sha1hmac = base64.b64encode(sha1hmac) 
            send_message('sha1hmac_for_MS:'+b64_sha1hmac)
            continue  
        #---------------------------------------------------------------------#
        elif msg.startswith('link:'):
            b64_link = msg[len('link:'):]
            try: link = base64.b64decode(b64_link)
            except: raise Exception ('base64 decode error in link')
            time.sleep(1) #just in case the upload server needs some time to prepare the file
            req = urllib2.Request(link)
            resp = urllib2.urlopen(req)
            linkdata = resp.read()
            with open(os.path.join(current_sessiondir, 'auditeetrace.zip'), 'wb') as f : f.write(linkdata)
            zipf = zipfile.ZipFile(os.path.join(current_sessiondir, 'auditeetrace.zip'), 'r')
            auditeetrace_dir = os.path.join(current_sessiondir, 'auditeetrace')
            zipf.extractall(auditeetrace_dir)
            response = 'success' #unless overridden by a failure in sanity check
            #sanity: all trace names must be unique and their hashes must correspond to the
            #hashes which the auditee committed to earlier
            adir_list = os.listdir(auditeetrace_dir)
            seqnos = []
            for one_trace in adir_list:
                if not one_trace.startswith('trace'): continue
                try: this_seqno = int(one_trace[len('trace'):])
                except: raise Exception ('WARNING: Could not cast trace\'s tail to int')
                if this_seqno in seqnos: 
                    raise Exception ('WARNING: multiple tracefiles names detected')
                saved_hash_path = os.path.join(commit_dir, 'tracehash'+str(this_seqno))
                if not os.path.exists(saved_hash_path): 
                    raise Exception ('WARNING: Auditee gave a trace number which doesn\'t have a committed hash')
                with open(saved_hash_path, 'rb') as f: saved_hash = f.read()
                with open(os.path.join(auditeetrace_dir, one_trace), 'rb') as f: tracedata = f.read()
                trace_hash = hashlib.sha256(tracedata).digest()
                if not saved_hash == trace_hash: 
                    raise Exception ('WARNING: Trace\'s hash doesn\'t match the hash committed to')
                md5hmac_path = os.path.join(auditeetrace_dir, 'md5hmac'+str(this_seqno))
                if not os.path.exists(md5hmac_path):
                    raise Exception ('WARNING: Could not find md5hmac in auditeetrace')
                with open(md5hmac_path, 'rb') as f: md5hmac_data = f.read()
                md5hmac_hash = hashlib.sha256(md5hmac_data).digest()
                with open(os.path.join(commit_dir, 'md5hmac_hash'+str(this_seqno)), 'rb') as f: commited_md5hmac_hash = f.read()
                if not md5hmac_hash == commited_md5hmac_hash:
                    raise Exception ('WARNING: mismatch in committed md5hmac hashes')
                domain_path = os.path.join(auditeetrace_dir, 'domain'+str(this_seqno))
                if not os.path.exists(domain_path):
                    raise Exception ('WARNING: Could not find domain in auditeetrace')                
                #elif no errors
                seqnos.append(this_seqno)
                continue
            send_message('response:'+response)
            if response == 'success':
                progressQueue.put(time.strftime('%H:%M:%S', time.localtime()) + ': The auditee has successfully finished the audit session')
            else:
                progressQueue.put(time.strftime('%H:%M:%S', time.localtime()) + ': WARNING!!! The auditee FAILED the audit session')
            progressQueue.put(time.strftime('%H:%M:%S', time.localtime()) + ': Decrypting  auditee\'s data')
            #decrypt  the tracefiles
            decr_dir = os.path.join(current_sessiondir, 'decrypted')
            os.makedirs(decr_dir)
            for one_trace in adir_list:
                if not one_trace.startswith('trace'): continue
                seqno = one_trace[len('trace'):]
                with open(os.path.join(auditeetrace_dir, 'md5hmac'+seqno), 'rb') as f: md5hmac = f.read()
                with open(os.path.join(commit_dir, 'sha1hmac'+seqno), 'rb') as f: sha1hmac = f.read()
                with open(os.path.join(commit_dir, 'cr'+seqno), 'rb') as f: cr = f.read()
                ms = shared.xor(md5hmac, sha1hmac)
                sslkeylog = os.path.join(decr_dir, 'sslkeylog'+seqno)
                ssldebuglog = os.path.join(decr_dir, 'ssldebuglog'+seqno)
                cr_hexl = binascii.hexlify(cr)
                ms_hexl = binascii.hexlify(ms)
                with open(sslkeylog, 'wb') as f: 
                    f.write('CLIENT_RANDOM ' + cr_hexl + ' ' + ms_hexl + '\n')
                try: output = subprocess.check_output([tshark_exepath, '-r', 
                                                    os.path.join(auditeetrace_dir, one_trace),
                                                     '-Y', 'ssl and http.content_type contains html', 
                                                     '-o', 'http.ssl.port:1025-65535', 
                                                     '-o', 'ssl.keylog_file:'+ sslkeylog,
                                                     '-o', 'ssl.ignore_ssl_mac_failed:False',
                                                     '-o', 'ssl.debug_file:' + ssldebuglog,
                                                     '-x'])
                except: #maybe an old tshark version, Replace -Y with -R
                    try: output = subprocess.check_output([tshark_exepath, '-r',
                                                           os.path.join(auditeetrace_dir, one_trace),
                                                          '-R', 'ssl and http.content_type contains html',
                                                          '-o', 'http.ssl.port:1025-65535', 
                                                          '-o', 'ssl.keylog_file:'+ sslkeylog,
                                                          '-o', 'ssl.ignore_ssl_mac_failed:False',
                                                          '-o', 'ssl.debug_file:' + ssldebuglog,
                                                          '-x'])
                    except: raise Exception ('Could not launch tshark')
                if output == '': raise Exception ("Failed to find HTML in escrowtrace")
                with open(ssldebuglog, 'rb') as f: debugdata = f.read()
                if debugdata.count('mac failed') > 0:
                    raise Exception('Mac check failed in tracefile')
                #output may contain multiple frames with HTML, we examine them one-by-one
                separator = re.compile('Frame ' + re.escape('(') + '[0-9]{2,7} bytes' + re.escape(')') + ':')
                #ignore the first split element which is always an empty string
                frames = re.split(separator, output)[1:]    
                html_paths = ''
                for index,oneframe in enumerate(frames):
                    html = shared.get_html_from_asciidump(oneframe)
                    path = os.path.join(decr_dir, 'html-'+seqno+'-'+str(index))
                    with open(path, 'wb') as f: f.write(html)
                #also create a file where the auditor can see the domain and pubkey
                with open (os.path.join(auditeetrace_dir, 'domain'+seqno), 'rb') as f: domain_data = f.read()
                with open (os.path.join(commit_dir, 'pubkey'+seqno), 'rb') as f: pubkey_data = f.read()
                write_data = domain_data + '\n\n' + 'The auditee claims that the server above presented the public key below\n' + 'Open the server address in your browser and check that the public key matches\n' + 'This step is mandatory to ascertain that the auditee hasn\'t tampered with the audit data\n' + pubkey_data
                with open(os.path.join(decr_dir, 'domain'+seqno), 'wb') as f: f.write(write_data)               
            progressQueue.put(time.strftime('%H:%M:%S', time.localtime()) + ': All decrypted HTML can be found in ' + decr_dir)
            progressQueue.put(time.strftime('%H:%M:%S', time.localtime()) + ': You may now close the browser.')
            continue