def blake2b(self, data, addr, server=False): # ECDH_AED_accept = None addrStr = sts_utility.addrToString(addr) proposeResp = sts_utility.deconstructPropose(data) # print(STS.STSConnectionStates) # if addrStr in STS.STSConnectionStates.keys(): myECDHPK, myECDHSK = STS.STSConnectionStates[addrStr]['keys'] # TODO verification scalarmult_q = pysodium.crypto_scalarmult_curve25519( myECDHSK, proposeResp['K']) genericHash = pysodium.crypto_generichash_init(outlen=64) genericHash = pysodium.crypto_generichash_update( genericHash, scalarmult_q) if not server: genericHash = pysodium.crypto_generichash_update( genericHash, myECDHPK) genericHash = pysodium.crypto_generichash_update( genericHash, proposeResp['K']) else: genericHash = pysodium.crypto_generichash_update( genericHash, proposeResp['K']) genericHash = pysodium.crypto_generichash_update( genericHash, myECDHPK) genericHash = pysodium.crypto_generichash_final(genericHash, outlen=64) STS.STSConnectionStates[addrStr]['session_key'] = genericHash # STS.STSConnectionStates[addrStr]['phase'] = 1 STS.STSConnectionStates[addrStr]['time'] = int(time.time())
def constructSTSResponse(self, mType, addr, exchangeData): data = None addrStr = sts_utility.addrToString(addr) if addrStr in STS.STSConnectionStates.keys(): if STS.STSConnectionStates[addrStr][ 'cSuite'] == 1 or STS.STSConnectionStates[addrStr][ 'cSuite'] == 4: nonce = pysodium.randombytes( pysodium.crypto_aead_chacha20poly1305_ietf_NONCEBYTES) m = struct.pack('>B', mType) + struct.pack('>I', len(nonce)) # T = struct.pack('>Q', int(time.time())) encrypData = pysodium.crypto_aead_chacha20poly1305_ietf_encrypt( exchangeData, m, nonce, STS.STSConnectionStates[addrStr]['session_key'][:32]) data = m + nonce + encrypData elif STS.STSConnectionStates[addrStr][ 'cSuite'] == 2 or STS.STSConnectionStates[addrStr][ 'cSuite'] == 5: nonce = pysodium.randombytes( pysodium.crypto_aead_chacha20poly1305_NONCEBYTES) m = struct.pack('>B', mType) + struct.pack('>I', len(nonce)) # T = struct.pack('>Q', int(time.time())) encrypData = pysodium.crypto_aead_chacha20poly1305_encrypt( exchangeData, m, nonce, STS.STSConnectionStates[addrStr]['session_key'][:32]) data = m + nonce + encrypData # print(binascii.hexlify(T)) # print(binascii.hexlify(pysodium.crypto_aead_chacha20poly1305_ietf_decrypt(encrypData, m, nonce, myTX[:32]))) return data
def send(self, addr, cSuite, nodeData): try: addrStr = sts_utility.addrToString(addr) if addrStr in STS.STSConnectionStates.keys(): if STS.STSConnectionStates[addrStr]['phase'] == 2: print('send data with the existing session') ECDH_DATA_EX = self.constructSTSResponse(2, addr, nodeData) self.sock.sendto(ECDH_DATA_EX, addr) # self.receive() else: self.terminate(addr) else: print('send data with the new session') # myCert, myCertStatus = sts_utility.decodeMyCertificate(self.sock, self.endpoint_address) # myEncrypKey, mySignKey = sts_utility.decodeSecretKey() if cSuite in [1, 2, 6]: # print('sending ECDH') myECDHPK, myECDHSK = pysodium.crypto_kx_keypair() propose = self.generateProposeECDH(cSuite, myECDHPK, self.mySignKey, self.myCert, self.myCertStatus) STS.STSConnectionStates[addrStr] = { 'session_key': None, 'phase': 0, 'init': True, 'data': nodeData, 'keys': (myECDHPK, myECDHSK), 'cSuite': cSuite, 'time': int(time.time()) } print('sending PROPOSE') sts_utility.decodeSTSMessage(propose) self.sock.sendto(propose, addr) elif cSuite in [3, 4, 5]: propose = self.generateProposeECDH(cSuite, (0).to_bytes( 32, byteorder='big'), self.mySignKey, self.myCert, self.myCertStatus) STS.STSConnectionStates[addrStr] = { 'session_key': None, 'phase': 0, 'init': True, 'data': nodeData, 'keys': None, 'cSuite': cSuite, 'time': int(time.time()) } print('sending PROPOSE') sts_utility.decodeSTSMessage(propose) self.sock.sendto(propose, addr) # while not STS.STSConnectionStates[addrStr]['phase'] == 2: # self.receive() # ECDH_DATA_EX = self.constructSTSResponse(2, addr, nodeData) # self.sock.sendto(ECDH_DATA_EX, addr) # self.receive() except: return None
def send(self, addr, cSuite, nodeData): try: addrStr = sts_utility.addrToString(addr) if addrStr in STS.STSConnectionStates.keys(): if STS.STSConnectionStates[addrStr]['phase'] == 2: print('send data with the existing session') ECDH_DATA_EX = self.constructSTSResponse(2, addr, nodeData) self.sock.sendto(ECDH_DATA_EX, addr) # self.receive() else: self.terminate(addr) else: print('send data with the new session') if cSuite in [1, 2]: myECDHPK, myECDHSK = pysodium.crypto_kx_keypair() propose = self.generateProposeECDH(cSuite, myECDHPK, self.mySignKey, self.myCert, self.myCertStatus) STS.STSConnectionStates[addrStr] = { 'session_key': None, 'phase': 0, 'init': True, 'data': nodeData, 'keys': (myECDHPK, myECDHSK), 'cSuite': cSuite, 'time': int(time.time()) } self.sock.sendto(propose, addr) elif cSuite in [3, 4, 5]: propose = self.generateProposeECDH(3, (0).to_bytes( 32, byteorder='big'), self.mySignKey, self.myCert, self.myCertStatus) STS.STSConnectionStates[addrStr] = { 'session_key': None, 'phase': 0, 'init': True, 'data': nodeData, 'keys': None, 'cSuite': cSuite, 'time': int(time.time()) } self.sock.sendto(propose, addr) except: return None
def terminate(self, addr): print('=== SEND TERMINATE ===', addr) addrStr = sts_utility.addrToString(addr) if addrStr not in STS.STSConnectionStates.keys(): terminate = struct.pack( '>BI', 3, len((0).to_bytes( 8, byteorder='big'))) + (0).to_bytes(8, byteorder='big') self.sock.sendto(terminate, addr) elif STS.STSConnectionStates[addrStr]['session_key'] is not None: terminate = self.constructSTSResponse( 3, addr, sts_utility.timestampPacked()) self.sock.sendto(terminate, addr) STS.STSConnectionStates.pop(addrStr) else: terminate = struct.pack( '>BI', 3, len((0).to_bytes( 8, byteorder='big'))) + (0).to_bytes(8, byteorder='big') self.sock.sendto(terminate, addr) STS.STSConnectionStates.pop(addrStr)
def deconstructSTSResponse(self, data, addr): # TODO verification # m = None decrypData = None addrStr = sts_utility.addrToString(addr) if addrStr in STS.STSConnectionStates.keys(): cp = 0 m = struct.pack('>B', data[cp]) # if data[cp] == 2: cp += 1 nonce_length = data[cp:cp + 4] length = struct.unpack('>I', nonce_length)[0] m += nonce_length cp += 4 nonce = data[cp:cp + length] cp += length encrypData = data[cp:] if STS.STSConnectionStates[addrStr][ 'cSuite'] == 1 or STS.STSConnectionStates[addrStr][ 'cSuite'] == 4: try: decrypData = pysodium.crypto_aead_chacha20poly1305_ietf_decrypt( encrypData, m, nonce, STS.STSConnectionStates[addrStr]['session_key'][:32]) except: decrypData = None # TODO suites 2 or 5 elif STS.STSConnectionStates[addrStr][ 'cSuite'] == 2 or STS.STSConnectionStates[addrStr][ 'cSuite'] == 5: try: decrypData = pysodium.crypto_aead_chacha20poly1305_decrypt( encrypData, m, nonce, STS.STSConnectionStates[addrStr]['session_key'][:32]) except: decrypData = None return decrypData
def sendBlake2bResp(self, data, addr, cSuite): addrStr = sts_utility.addrToString(addr) # myCert, myCertStatus = sts_utility.decodeMyCertificate(self.sock, self.endpoint_address) # myEncrypKey, mySignKey = sts_utility.decodeSecretKey() if cSuite in [1, 2]: # print('sending ECDH') myECDHPK, myECDHSK = pysodium.crypto_kx_keypair() propose = self.generateProposeECDH(cSuite, myECDHPK, self.mySignKey, self.myCert, self.myCertStatus) # STS.STSConnectionStates[addrStr] = {'session_key': None, 'phase': 0, 'init': True, # 'keys': (myECDHPK, myECDHSK), # 'cSuite': cSuite, 'time': int(time.time())} STS.STSConnectionStates[addrStr]['keys'] = (myECDHPK, myECDHSK) self.blake2b(data, addr, server=True) self.sock.sendto(propose, addr) elif cSuite == 0: myECDHPK, myECDHSK = pysodium.crypto_kx_keypair() STS.STSConnectionStates[addrStr]['keys'] = (myECDHPK, myECDHSK) propose = self.generateProposeECDH(1, myECDHPK, self.mySignKey, self.myCert, self.myCertStatus) self.sock.sendto(propose, addr)
def receive(self): # print(STS.STSConnectionStates) data, addr = self.sock.recvfrom(1500) print('received STS message') sts_utility.decodeSTSMessage(data) # TODO Clean STSConnectionStates # TODO VALIDATE SUITE DOWNGRADE addrStr = sts_utility.addrToString(addr) # Received propose # Reply propose for actual suite when 0/3 used try: if addr == self.endpoint_address: self.setCertificateStatus(data) elif data[0] == 0: # propose received as server # print(self.verifySTSResponse(data, addr)) # CLIENT PROPOSE / MULTIPLE PROPOSE if addrStr in STS.STSConnectionStates.keys(): if self.verifyPropose(data, addr): if STS.STSConnectionStates[addrStr]['phase'] != 0: # pprint.pprint(STS.STSConnectionStates) self.terminate(addr) elif data[1] < STS.STSConnectionStates[addrStr][ 'cSuite']: self.terminate(addr) elif STS.STSConnectionStates[addrStr]['phase'] == 0: # TODO UPGRADE SUITE IS VALID, NEED TO FIX BELOW # if STS.STSConnectionStates[addrStr]['cSuite'] in [1, 2] and data[1] in [3, 4, 5]: # self.terminate(addr) if STS.STSConnectionStates[addrStr]['init']: if STS.STSConnectionStates[addrStr][ 'cSuite'] in [1, 2]: # LOWER SUITE CHECKED BEFORE if STS.STSConnectionStates[addrStr][ 'cSuite'] != data[1]: data = STS.STSConnectionStates[ addrStr]['data'] STS.STSConnectionStates.pop(addrStr) self.send(addr, data[1], data) return None, None elif STS.STSConnectionStates[addrStr][ 'cSuite'] == 3 and data[1] == 3: self.terminate(addr) else: if STS.STSConnectionStates[addrStr][ 'cSuite'] in [1, 2] and data[1] in [ 3, 4, 5 ]: self.terminate(addr) # if STS.STSConnectionStates[addrStr]['cSuite'] in [1 ,2]: # if STS.STSConnectionStates[addrStr]['cSuite'] != data[1]: # self.terminate(addr) else: STS.STSConnectionStates[addrStr]['cSuite'] = data[ 1] STS.STSConnectionStates[addrStr]['time'] = int( time.time()) else: print('=== VERIFICATION FAILED ===', addr) self.terminate(addr) # SERVER RECEIVE else: if self.verifyPropose(data, addr): # if data[1] == 0: # TODO PROPOSE EXPECTED SUITE if addrStr not in STS.STSConnectionStates.keys(): STS.STSConnectionStates[addrStr] = { 'session_key': None, 'phase': data[0], 'init': False, 'keys': None, 'cSuite': data[1], 'time': int(time.time()) } else: print('=== VERIFICATION FAILED ===', addr) self.terminate(addr) self.negotiate(data, addr) elif data[0] == 1: if addrStr not in STS.STSConnectionStates.keys(): self.terminate(addr) elif not self.deconstructSTSResponse(data, addr): self.terminate(addr) else: if STS.STSConnectionStates[addrStr]['init']: if STS.STSConnectionStates[addrStr]['phase'] == 1: STS.STSConnectionStates[addrStr]['phase'] = 2 ECDH_DATA_EX = self.constructSTSResponse( 2, addr, STS.STSConnectionStates[addrStr]['data']) self.sock.sendto(ECDH_DATA_EX, addr) else: self.terminate(addr) else: if STS.STSConnectionStates[addrStr][ 'phase'] == 0 and STS.STSConnectionStates[ addrStr]['cSuite'] in [1, 2]: STS.STSConnectionStates[addrStr]['phase'] = 1 self.negotiate(data, addr) elif STS.STSConnectionStates[addrStr][ 'phase'] == 0 and STS.STSConnectionStates[ addrStr]['cSuite'] in [4, 5]: STS.STSConnectionStates[addrStr]['phase'] = 1 self.negotiate(data, addr) else: self.terminate(addr) elif data[0] == 2: # TODO VALID DATA EXCHANGE if addrStr not in STS.STSConnectionStates.keys(): self.terminate(addr) elif not self.deconstructSTSResponse(data, addr): self.terminate(addr) else: if addrStr in STS.STSConnectionStates.keys(): if STS.STSConnectionStates[addrStr]['init']: if STS.STSConnectionStates[addrStr]['phase'] == 2: data_ex_resp = self.deconstructSTSResponse( data, addr) sts_utility.decodeDHTMessage(data_ex_resp) return data_ex_resp, addr else: self.terminate(addr) else: if STS.STSConnectionStates[addrStr]['phase'] == 2: date_ex_resp = self.deconstructSTSResponse( data, addr) response = packet.pingResponse(date_ex_resp) ECDH_DATA_EX = self.constructSTSResponse( 2, addr, response) self.sock.sendto(ECDH_DATA_EX, addr) else: self.terminate(addr) elif data[0] == 3: print('=== TERMINATE RECEIVED ===', addr) sts_utility.decodeSTSMessage(data) if addrStr in STS.STSConnectionStates.keys(): STS.STSConnectionStates.pop(addrStr) except: self.terminate(addr) return None, None return None, None
def negotiate(self, data, addr): addrStr = sts_utility.addrToString(addr) if addrStr in STS.STSConnectionStates.keys(): # SERVER LOGIC if not STS.STSConnectionStates[addrStr]['init']: if STS.STSConnectionStates[addrStr]['cSuite'] in [ 0, 1, 2 ] and STS.STSConnectionStates[addrStr]['phase'] == 0: self.sendBlake2bResp( data, addr, STS.STSConnectionStates[addrStr]['cSuite']) elif STS.STSConnectionStates[addrStr]['cSuite'] in [ 1, 2 ] and STS.STSConnectionStates[addrStr]['phase'] == 1: # TODO verify accept # ACCEPT Verify ECDH_AED_accept = self.constructSTSResponse( 1, addr, sts_utility.timestampPacked()) sts_utility.decodeSTSMessage(ECDH_AED_accept) STS.STSConnectionStates[addrStr]['phase'] = 2 self.sock.sendto(ECDH_AED_accept, addr) # self.receive() elif STS.STSConnectionStates[addrStr]['cSuite'] in [ 3, 4, 5 ] and STS.STSConnectionStates[addrStr]['phase'] == 0: # print('=== GEN PROPOSE SALSA 4 ===') # print(binascii.hexlify(proposeResp['C']['K_E'])) salsaMsg = self.salsa20poly1305(data, addr, server=True) if not salsaMsg: self.terminate(addr) else: sts_utility.decodeSTSMessage(salsaMsg) # print(mySalsaSession) self.sock.sendto(salsaMsg, addr) # self.receive() elif STS.STSConnectionStates[addrStr]['cSuite'] in [ 4, 5 ] and STS.STSConnectionStates[addrStr]['phase'] == 1: STS.STSConnectionStates[addrStr]['phase'] = 2 SALSA_AED_accept = self.constructSTSResponse( 1, addr, sts_utility.timestampPacked()) sts_utility.decodeSTSMessage(SALSA_AED_accept) self.sock.sendto(SALSA_AED_accept, addr) # self.receive() else: self.terminate(addr) # CLIENT LOGIC else: if STS.STSConnectionStates[addrStr]['cSuite'] in [ 1, 2 ] and STS.STSConnectionStates[addrStr]['phase'] == 0: if STS.STSConnectionStates[addrStr]['session_key'] is None: self.blake2b(data, addr) ECDH_AED_accept = self.constructSTSResponse( 1, addr, sts_utility.timestampPacked()) print('Sending Accept') sts_utility.decodeSTSMessage(ECDH_AED_accept) STS.STSConnectionStates[addrStr]['phase'] = 1 self.sock.sendto(ECDH_AED_accept, addr) # self.receive() elif STS.STSConnectionStates[addrStr]['cSuite'] in [3, 4, 5]: # print('=== GEN PROPOSE SALSA 4 ===') # print(binascii.hexlify(proposeResp['C']['K_E'])) salsaMsg = self.salsa20poly1305(data, addr) sts_utility.decodeSTSMessage(salsaMsg) # print(mySalsaSession) self.sock.sendto(salsaMsg, addr) # self.receive() else: self.terminate(addr)
def salsa20poly1305(self, data, addr, server=False): salsaMsg = None addrStr = sts_utility.addrToString(addr) # myCert, myCertStatus = sts_utility.decodeMyCertificate(self.sock, self.endpoint_address) # myEncrypKey, mySignKey = sts_utility.decodeSecretKey() if not server: if STS.STSConnectionStates[addrStr]['cSuite'] == 3: mySalsaSession = pysodium.randombytes(32) proposeResp = sts_utility.deconstructPropose(data) salsaMsg = self.genProposeSalsa(proposeResp['P'], proposeResp['C']['K_E'], mySalsaSession, self.myEncrypKey, self.mySignKey, self.myCert, self.myCertStatus) STS.STSConnectionStates[addrStr][ 'session_key'] = mySalsaSession STS.STSConnectionStates[addrStr]['cSuite'] = proposeResp['P'] STS.STSConnectionStates[addrStr]['time'] = int(time.time()) elif STS.STSConnectionStates[addrStr]['cSuite'] in [4, 5]: STS.STSConnectionStates[addrStr]['phase'] = 1 salsaMsg = self.constructSTSResponse( 1, addr, sts_utility.timestampPacked()) else: proposeReq = sts_utility.deconstructPropose(data) if STS.STSConnectionStates[addrStr][ 'cSuite'] == 3 and STS.STSConnectionStates[addrStr][ 'phase'] == 0: # mySalsaSession = pysodium.randombytes(32) salsaMsg = self.genProposeSalsa(4, proposeReq['C']['K_E'], (0).to_bytes(32, byteorder='big'), self.myEncrypKey, self.mySignKey, self.myCert, self.myCertStatus) STS.STSConnectionStates[addrStr]['session_key'] = (0).to_bytes( 32, byteorder='big') STS.STSConnectionStates[addrStr]['cSuite'] = 4 STS.STSConnectionStates[addrStr]['time'] = int(time.time()) elif STS.STSConnectionStates[addrStr]['cSuite'] in [ 4, 5 ] and STS.STSConnectionStates[addrStr]['phase'] == 0: session_key = self.decryptSalsaSession(proposeReq, self.myEncrypKey) if not session_key: salsaMsg = self.genProposeSalsa( STS.STSConnectionStates[addrStr]['cSuite'], proposeReq['C']['K_E'], (0).to_bytes(32, byteorder='big'), self.myEncrypKey, self.mySignKey, self.myCert, self.myCertStatus) STS.STSConnectionStates[addrStr]['session_key'] = ( 0).to_bytes(32, byteorder='big') STS.STSConnectionStates[addrStr][ 'cSuite'] = STS.STSConnectionStates[addrStr]['cSuite'] STS.STSConnectionStates[addrStr]['time'] = int(time.time()) else: STS.STSConnectionStates[addrStr][ 'session_key'] = session_key # STS.STSConnectionStates[addrStr]['phase'] = 1 salsaMsg = self.genProposeSalsa( 4, proposeReq['C']['K_E'], (0).to_bytes(32, byteorder='big'), self.myEncrypKey, self.mySignKey, self.myCert, self.myCertStatus) STS.STSConnectionStates[addrStr]['cSuite'] = proposeReq[ 'P'] # STS.STSConnectionStates[addrStr]['phase'] = 1 STS.STSConnectionStates[addrStr]['time'] = int(time.time()) # elif STS.STSConnectionStates[addrStr]['cSuite'] in [4, 5] and STS.STSConnectionStates[addrStr][ # 'phase'] == 1: # STS.STSConnectionStates[addrStr]['phase'] = 2 # salsaMsg = self.constructSTSResponse(1, addr, sts_utility.timestampPacked()) # salsaMsg = self.constructSTSResponse(1, addr, sts_utility.timestampPacked()) return salsaMsg