def __init__(self): print("\nInitializing server...") # Load parameters with open('parameters', 'rb') as f: self.parameters = serialization.load_pem_parameters( f.read().strip()) # Load media files self.MEDIA = dict() print("\nLoading media...") for _, c in CATALOG.items(): self.MEDIA[c['file_name']] = self.getFile( os.path.join(CATALOG_BASE, c['file_name'])).encode('latin') # Load private key fp = open(FILEPRIVATEKEY, 'rb') self.private_key = serialization.load_pem_private_key(fp.read(), password=None) fp.close() # Load certificate fc = open(FILECERTIFICATE, "rb") self.cert = PKI.getCertFromString(fc.read(), pem=True) fc.close() # Initialize session dictionary self.sessions = {} # Initialize pki self.pki = PKI() print("\nServer has been started!")
def DHexchange(connection): serial_parameters = connection.recv( 1024 ) #primeste obiectul parametrii serializat de unde genereaa cheia publica si privata DH parameters = serialization.load_pem_parameters( serial_parameters, backend=default_backend()) #dezerializez obiectul parametrii serv_private_key = parameters.generate_private_key( ) #generaz cheia privata DH serv_public_key = serv_private_key.public_key() #generez cheia publica DH serial_serv_public_key = serv_public_key.public_bytes( Encoding.PEM, PublicFormat.SubjectPublicKeyInfo) #serializez cheia publica connection.send(serial_serv_public_key) #trimit cheia publica serial_client_public_key = connection.recv( 1024) #primesc cheia publica client serializata client_public_key = serialization.load_pem_public_key( serial_client_public_key, backend=default_backend()) #deserializez cheiaa shared_key = serv_private_key.exchange( client_public_key) #compun cheia comuna DH key = HKDF(algorithm=hashes.SHA256(), length=32, salt=None, info=b'handshake data', backend=default_backend()).derive( shared_key) #generezo o cheie noua comuna din cheia DH return shared_key, key
def process_diffie_parameters(self, message: str) -> bool: logger.debug("Process Diffie-Hellman Parameters: {}".format(message)) if self.state == STATE_TO_CIPHER: data = base64.b64decode(message.get('data', "").encode()) self.diffie_parameters = load_pem_parameters( data, backend=default_backend()) self.state = STATE_DIFFIE_PARAMETERS else: logger.warning("Invalid state. Discarding") return False try: data = message.get('data', None) if data is None: logger.debug("Invalid message. No data found") return False bdata = base64.b64decode(message['data']) except: logger.exception( "Could not decode base64 content from message.data") return False self._send({'type': 'OK'}) return True
def __init__(self): # This example DH server uses a known safe prime, or can generate its own if the PEM file is not available. # The safe prime used here is ffdhe2048, described here: # https://tools.ietf.org/html/rfc7919#appendix-A.1 # There is nothing strictly wrong with generating your own prime, but this one is well tested. import os.path pem_path = os.path.join(os.path.dirname(__file__), 'dh_params.pem') if not os.path.isfile(pem_path): # No PEM file available, generate a new prime of 2048 bits. parameters = dh.generate_parameters(generator=2, key_size=2048, backend=default_backend()) s = parameters.parameter_bytes(Encoding.PEM, ParameterFormat.PKCS3) with open('dh_params.pem', 'wb') as outfile: outfile.write(s) else: # Load PEM file - a standard format for cryptographic keys and numbers. from cryptography.hazmat.primitives.serialization import load_pem_parameters with open(pem_path, 'r') as f: pem_data = f.read().encode("UTF-8") parameters = load_pem_parameters(data=pem_data, backend=default_backend()) self.parameters = parameters self.shared_key = None
def deserializeParameters(string, bc=backend): """ Takes a string (some parameters), loading those performing a deserialize operation The encoding type was PEM """ if type(string) == str: string = string.encode('utf8') return serialization.load_pem_parameters(string, backend=bc)
def create_session_key_server(self, message_json, address): # decode the params parameters = pickle.loads( codecs.decode(message_json["params"].encode(), "base64")) par = load_pem_parameters(parameters, backend=default_backend()) # Get the server public key manager_pub = message_json["public"].encode() save_server_key_client(self._server_path, manager_pub, "/manager_server.pem") # Load the key r = RSAKGen() self.auction_repository.manager_public = r.load_public_key( self._server_path, "manager_server.pem") # Generate our public/private key private_key = par.generate_private_key() public_key = private_key.public_key() # Get the public key from the user peer_public_key_bytes = message_json["pk"].encode() peer_public_key = serialization.load_pem_public_key( peer_public_key_bytes, default_backend()) shared_key = private_key.exchange(peer_public_key) calendar_date = str(datetime.datetime.now()) derived_key = HKDF(algorithm=hashes.SHA256(), length=DH_HKDF_KEY, salt=None, info=calendar_date.encode(), backend=default_backend()).derive(shared_key) # Construct the message with the keys message = "{ \"type\" : \"session_server\" ,\n" # Now send our DH public key to the client pk_dh = public_key.public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo).decode( 'utf-8') message += "\"pk\" : \"" + pk_dh + "\" ,\n" message += "\"info\" : \"" + calendar_date + "\",\n" message += "\"server_key\" : \"" + self.auction_repository.public_key.public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo).decode( 'utf-8') + "\"" message += "}" # Set the sessionKey as the bytes of the derived_key self.auction_repository.session_key_server = derived_key return base64.b64encode(message.encode('utf-8'))
def __generate_parameters(self): param_file = 'dh.pem' if not os.path.isfile(param_file): self.__parameters = dh.generate_parameters( generator=2, key_size=2048, backend=default_backend()) dh_pem = self.__parameters.parameter_bytes(Encoding.PEM, ParameterFormat.PKCS3) with open(param_file, 'wb') as output: output.write(dh_pem) else: with open(param_file, 'rb') as binary_file: pem_data = binary_file.read() self.__parameters = load_pem_parameters( pem_data, default_backend()) self.__private_key = self.__parameters.generate_private_key() self.__full_key = None
def load_params(bmessage): return load_pem_parameters(bmessage, backend=default_backend())
def main(uuid_c): print("|--------------------------------------|") print("| SECURE MEDIA CLIENT |") print("|--------------------------------------|\n") # Get a list of media files print("Contacting Server") CLIENT_CIPHERSUITS = ["AES256_CBC_SHA256", "AES256_CBC_SHA512", "AES256_GCM_SHA256", "AES256_GCM_SHA512", "ChaCha20_None_SHA256", "ChaCha20_None_SHA512"] lib ='/usr/local/lib/libpteidpkcs11.so' pkcs11 = PyKCS11.PyKCS11Lib() pkcs11.load(lib) slots = pkcs11.getSlotList() slot = slots[0] session = pkcs11.openSession(slot) obj = session.findObjects([(PyKCS11.CKA_CLASS, PyKCS11.CKO_CERTIFICATE), (PyKCS11.CKA_LABEL, 'CITIZEN AUTHENTICATION CERTIFICATE')])[0] all_atributes = [PyKCS11.CKA_VALUE] attributes = session.getAttributeValue(obj, all_atributes)[0] cert = x509.load_der_x509_certificate(bytes(attributes)) cc_cert_pem = cert.public_bytes(encoding=serialization.Encoding.PEM) cc_private_key = session.findObjects([( PyKCS11.CKA_CLASS, PyKCS11.CKO_PRIVATE_KEY), (PyKCS11.CKA_LABEL,'CITIZEN AUTHENTICATION KEY')])[0] mechanism = PyKCS11.Mechanism(PyKCS11.CKM_SHA1_RSA_PKCS, None) data = {"uuid": uuid_c, "client_ciphersuits": CLIENT_CIPHERSUITS, "cc_cert": cc_cert_pem.decode('latin')} data = json.dumps(data) signature = bytes(session.sign(cc_private_key, data, mechanism)) payload = {"data": data, "signature": base64.b64encode(signature).decode('latin')} req = requests.get(f'{SERVER_URL}/api/protocols', data= json.dumps(payload)) req = req.json() data_signed = json.loads(req["data"]) algorithms_modes_digests = data_signed["ciphersuit"].split("_") algorithm = algorithms_modes_digests[0] mode = algorithms_modes_digests[1] digest_c = algorithms_modes_digests[2] signature = base64.b64decode(req["signature"].encode()) with open("Certification_Authority.crt", "rb") as CA_cert_file: CA_cert = x509.load_pem_x509_certificate(CA_cert_file.read()) CA_public_key = CA_cert.public_key() server_cert = x509.load_pem_x509_certificate(data_signed["server_cert"].encode()) server_public_key_rsa = server_cert.public_key() #Verificar o certificado CA_public_key.verify( server_cert.signature, server_cert.tbs_certificate_bytes, paddingAsymetric.PKCS1v15(), server_cert.signature_hash_algorithm, ) #verificar assinatura if digest_c == "SHA256": server_public_key_rsa.verify( signature, req["data"].encode(), paddingAsymetric.PSS( mgf=paddingAsymetric.MGF1(hashes.SHA256()), salt_length=paddingAsymetric.PSS.MAX_LENGTH ), hashes.SHA256() ) elif digest_c == "SHA512": server_public_key_rsa.verify( signature, req["data"].encode(), paddingAsymetric.PSS( mgf=paddingAsymetric.MGF1(hashes.SHA512()), salt_length=paddingAsymetric.PSS.MAX_LENGTH ), hashes.SHA512() ) else: print("Erro") sys.exit(0) #Certificados do client e private key with open("Client_Certificate.pem", "rb") as key_file: client_cert_private_key = serialization.load_pem_private_key( key_file.read(), password=None, ) with open("Client_Certificate.crt", "rb") as cert_file: client_cert = cert_file.read() data = json.dumps({ "uuid_c": uuid_c, "client_cert": client_cert.decode('latin') }) signature = client_sign(digest_c, data) payload = { "data": data, "signature": base64.b64encode(signature).decode('latin')} req = requests.get(f'{SERVER_URL}/api/key', data=json.dumps(payload)) req = req.json() signature = base64.b64decode(req["signature"].encode()) message = req["message"].encode() req = json.loads(message) #Verificar o certificado CA_public_key.verify( server_cert.signature, server_cert.tbs_certificate_bytes, paddingAsymetric.PKCS1v15(), server_cert.signature_hash_algorithm, ) #Verificar a assinatura if digest_c == "SHA256": server_public_key_rsa.verify( signature, message, paddingAsymetric.PSS( mgf=paddingAsymetric.MGF1(hashes.SHA256()), salt_length=paddingAsymetric.PSS.MAX_LENGTH ), hashes.SHA256() ) elif digest_c == "SHA512": server_public_key_rsa.verify( signature, message, paddingAsymetric.PSS( mgf=paddingAsymetric.MGF1(hashes.SHA512()), salt_length=paddingAsymetric.PSS.MAX_LENGTH ), hashes.SHA512() ) else: print("Erro") sys.exit(0) parameters_pem = req["parameters"].encode() server_pub_key_pem = req["server_pub_key"].encode() server_pub_key = load_pem_public_key(server_pub_key_pem) parameters = load_pem_parameters(parameters_pem) client_private_key = parameters.generate_private_key() client_pub_key_pem = client_private_key.public_key().public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo ) client_shared_key = client_private_key.exchange(server_pub_key) if digest_c == "SHA256": shared_key_derived = HKDF( algorithm=hashes.SHA256(), length=32, salt=None, info=b'handshake data', ).derive(client_shared_key) elif digest_c == "SHA512": shared_key_derived = HKDF( algorithm=hashes.SHA512(), length=32, salt=None, info=b'handshake data', ).derive(client_shared_key) else: print("Erro ao derivar a shared key") sys.exit(0) data = {"uuid": uuid_c, "client_pub_key": client_pub_key_pem.decode('utf-8')} data = json.dumps(data) signature = client_sign(digest_c, data) payload = {"data": data, "signature": base64.b64encode(signature).decode('latin') } req = requests.post(url=f'{SERVER_URL}/api/shared_key', data=json.dumps(payload)) req = requests.get(f'{SERVER_URL}/api/list') if req.status_code == 200: print("Got Server List") media_list = req.json() # Present a simple selection menu idx = 0 print("MEDIA CATALOG\n") for item in media_list: print(f'{idx} - {media_list[idx]["name"]}') print("----") while True: selection = input("Select a media file number (q to quit): ") if selection.strip() == 'q': sys.exit(0) if not selection.isdigit(): continue selection = int(selection) if 0 <= selection < len(media_list): break # Example: Download first file media_item = media_list[selection] print(f"Playing {media_item['name']}") # Detect if we are running on Windows or Linux # You need to have ffplay or ffplay.exe in the current folder # In alternative, provide the full path to the executable if os.name == 'nt': proc = subprocess.Popen(['ffplay.exe', '-i', '-'], stdin=subprocess.PIPE) else: proc = subprocess.Popen(['ffplay', '-i', '-'], stdin=subprocess.PIPE) # Get data from server and send it to the ffplay stdin through a pipe for chunk in range(media_item['chunks'] + 1): if algorithm == "AES256": if mode == "CBC": media_id, iv = encrypt_AES(shared_key_derived, media_item["id"].encode(), "CBC") iv = base64.b64encode(iv).decode('latin') chunk, iv2 = encrypt_AES(shared_key_derived, str(chunk).encode(), "CBC") iv2 = base64.b64encode(iv2).decode('latin') info = json.dumps({"uuid": uuid_c, "iv": iv, "iv2": iv2}) elif mode == "GCM": media_id, iv, tag1 = encrypt_AES(shared_key_derived, media_item["id"].encode(), "GCM") iv = base64.b64encode(iv).decode('latin') tag1 = base64.b64encode(tag1).decode('latin') chunk, iv2, tag2 = encrypt_AES(shared_key_derived, str(chunk).encode(), "GCM") iv2 = base64.b64encode(iv2).decode('latin') tag2 = base64.b64encode(tag2).decode('latin') info = json.dumps({"uuid": uuid_c, "iv": iv, "iv2": iv2, "tag1": tag1, "tag2": tag2}) elif algorithm == "ChaCha20": media_id, nonce = encrypt_ChaCha20(shared_key_derived, media_item["id"].encode()) nonce = base64.b64encode(nonce).decode('latin') chunk, nonce2 = encrypt_ChaCha20(shared_key_derived, str(chunk).encode()) nonce2 = base64.b64encode(nonce2).decode('latin') info = json.dumps({"uuid": uuid_c, "nonce": nonce, "nonce_chunk": nonce2}) else: print("erro") sys.exit(0) media_id = base64.urlsafe_b64encode(media_id).decode('latin') chunk = base64.urlsafe_b64encode(chunk).decode('latin') signature = client_sign(digest_c, info) payload = { "data": info, "signature": base64.b64encode(signature).decode('latin') } req = requests.get(f'{SERVER_URL}/api/download?id={media_id}&chunk={chunk}', data=json.dumps(payload)) req = req.json() signature = base64.b64decode(req["signature"].encode()) #verificar assinatura if digest_c == "SHA256": server_public_key_rsa.verify( signature, req["data"].encode(), paddingAsymetric.PSS( mgf=paddingAsymetric.MGF1(hashes.SHA256()), salt_length=paddingAsymetric.PSS.MAX_LENGTH ), hashes.SHA256() ) elif digest_c == "SHA512": server_public_key_rsa.verify( signature, req["data"].encode(), paddingAsymetric.PSS( mgf=paddingAsymetric.MGF1(hashes.SHA512()), salt_length=paddingAsymetric.PSS.MAX_LENGTH ), hashes.SHA512() ) else: print("Erro") sys.exit(0) req = json.loads(req["data"]) if algorithm == "AES256": try: data_encrypted = req["data"].encode() data_encrypted = base64.b64decode(data_encrypted) iv = req["iv"].encode() iv = base64.b64decode(iv) MAC = req["MAC"].encode() MAC = base64.b64decode(MAC) salt = req["salt"].encode() salt = base64.b64decode(salt) except: print(req["error"]) proc.kill() break return 0 if digest_c == "SHA256": kdf = PBKDF2HMAC( algorithm=hashes.SHA256(), length=32, salt=salt, iterations=100000, ) key = kdf.derive(shared_key_derived) elif digest_c == "SHA512": kdf = PBKDF2HMAC( algorithm=hashes.SHA512(), length=32, salt=salt, iterations=100000, ) key = kdf.derive(shared_key_derived) else: print("Erro") sys.exit(0) c = cmac.CMAC(algorithms.AES(key)) c.update(data_encrypted) c.verify(MAC) if mode == "CBC": data = decrypt_AES(key, iv, data_encrypted, "CBC") elif mode == "GCM": tag = req["tag"].encode() tag = base64.b64decode(tag) data = decrypt_AES(key, iv, data_encrypted, "GCM", tag) info = json.loads(data.decode('latin')) data = info["data"] data = binascii.a2b_base64(data) elif algorithm == "ChaCha20": #CHACHA20 Funciona try: nonce = req["nonce"].encode() nonce = base64.b64decode(nonce) data_encrypted = req["data"].encode() data_encrypted = base64.b64decode(data_encrypted) MAC = req["MAC"].encode() MAC = base64.b64decode(MAC) salt = req["salt"].encode() salt = base64.b64decode(salt) except: print(req["error"]) proc.kill() break return 0 if digest_c == "SHA256": kdf = PBKDF2HMAC( algorithm=hashes.SHA256(), length=32, salt=salt, iterations=100000, ) key = kdf.derive(shared_key_derived) h = hmac.HMAC(key, hashes.SHA256()) h.update(data_encrypted) h.verify(MAC) elif digest_c == "SHA512": kdf = PBKDF2HMAC( algorithm=hashes.SHA512(), length=32, salt=salt, iterations=100000, ) key = kdf.derive(shared_key_derived) h = hmac.HMAC(key, hashes.SHA512()) h.update(data_encrypted) h.verify(MAC) else: print("ERRO") sys.exit(0) data = decrypt_ChaCha20(key, nonce, data_encrypted) info = json.loads(data.decode('latin')) data = info["data"] data = binascii.a2b_base64(data) else: print("Erro") sys.exit(0) try: proc.stdin.write(data) except: break
def create_session_key_user_server(self, message_json): rsa_kg = RSAKGen() username = message_json["username"] random_key = rsa_kg.decipher_with_private_key( self.auction_repository.private_key, base64.b64decode(message_json["random_key"])) # VERIFIES THE message integrity if not HMAC_Conf.verify_function("message", message_json, random_key): return base64.b64encode( "{ \"type\" : \"Tempered data\"}".encode('utf-8')) # Decrypts the message data = decrypt_data("", message_json["message"], base64.b64decode(message_json["iv"]), base64.b64decode(message_json["Key"]), self.auction_repository.private_key) # Loads the messsage to json internal_json = json.loads(data, strict="False") rsa_signature = internal_json["rsa_signature"] certificate = base64.b64decode( internal_json["certificate"] ) #PENSO QUE TENS DE FAZER BASE64 DECODE digital_signature = base64.b64decode( internal_json["digital_signature"]) citizen = CitizenCard() certificate = x509.load_pem_x509_certificate(certificate, default_backend()) if not citizen.check_signature( certificate, digital_signature, self.auction_repository.clients_challenge[base64.b64decode( message_json["username"])].encode()): return base64.b64encode( "{ \"type\" : \"No valid signature\"}".encode('utf-8')) if not citizen.validate_certificate(certificate): return base64.b64encode( "{ \"type\" : \"No valid certificate\"}".encode('utf-8')) self.auction_repository.clients_challenge.pop( base64.b64decode(message_json["username"])) # Get the parameters parameters = pickle.loads( codecs.decode(message_json["params"].encode(), "base64")) par = load_pem_parameters(parameters, backend=default_backend()) # Generate our DH public/private key private_key = par.generate_private_key() public_key = private_key.public_key() # Get the public key bytes from the user peer_public_key_bytes = message_json["pk"].encode() peer_public_key = serialization.load_pem_public_key( peer_public_key_bytes, default_backend()) calendar_date = str(datetime.datetime.now()) shared_key = private_key.exchange(peer_public_key) derived_key = HKDF(algorithm=hashes.SHA256(), length=DH_HKDF_KEY, salt=None, info=calendar_date.encode("utf-8"), backend=default_backend()).derive(shared_key) # Construct the message with the keys message = "{ \"type\" : \"session\" ,\n" # Now send our DH public key to the client pk_dh = public_key.public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo).decode( 'utf-8') message += "\"pk\" : \"" + pk_dh + "\" ,\n" message += "\"info\" : \"" + calendar_date + "\",\n" message += "\"server_key\" : \"" + self.auction_repository.public_key.public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo).decode( 'utf-8') + "\"" message += "}" # Get the username and set the session key self.auction_repository.session_key_clients[ message_json["username"]] = derived_key # Get the public key from the user key from the user _dir = os.getcwd() + "/Clients/" + message_json["username"] if not check_directory(_dir): if not check_directory(os.getcwd() + "/Clients"): os.mkdir(os.getcwd() + "/Clients") os.mkdir(_dir) with open(_dir + "/" + PK_NAME, "wb") as file: file.write(message_json["public"].encode("utf-8")) return base64.b64encode(message.encode('utf-8'))
def test_wrong_parameters_format(self, backend): param_data = b"---- NOT A KEY ----\n" with pytest.raises(ValueError): load_pem_parameters(param_data, backend)
def on_message(client, userdata, msg): global parameters global hmac_key global b_public_key_number global a_private_key global a_public_key global a_private_key_ecdh global a_public_key_ecdh global key_fernet global f_key global a_key if (msg.topic == (name + "/to")): # Receive params if (str(msg.payload.decode()).split(":")[0] == "param"): b_pem = str(msg.payload.decode()).split(":")[1] parameters = load_pem_parameters(bytes(b_pem, 'ascii'), backend=default_backend()) # Calculate keys from params a_private_key = parameters.generate_private_key() a_public_key = a_private_key.public_key() client.publish(name + "/from", "public:" + str(a_public_key.public_numbers().y)) # Receive public key elif (str(msg.payload.decode()).split(":")[0] == "public"): print("Public key received from platform.") # DH if (asymmetric_mode == 0): b_public_key_number = int( str(msg.payload.decode()).split(":")[1]) peer_public_numbers = dh.DHPublicNumbers( b_public_key_number, parameters.parameter_numbers()) b_public_key = peer_public_numbers.public_key( default_backend()) # Calculate shared key a_shared_key = a_private_key.exchange(b_public_key) # ECDH else: # Generate private and public key ECDH a_private_key_ecdh = ec.generate_private_key(ec.SECP384R1()) a_public_key_ecdh = a_private_key_ecdh.public_key() b_public_key_number = str(msg.payload.decode()).split(":")[1] b_public_key = load_pem_public_key( b_public_key_number.encode()) client.publish( name + "/from", "public:" + a_public_key_ecdh.public_bytes( encoding=Encoding.PEM, format=PublicFormat.SubjectPublicKeyInfo).decode()) # Calculate shared key a_shared_key = a_private_key_ecdh.exchange( ec.ECDH(), b_public_key) print("Shared key calculated.") # Calculate HMAC def hebra(): global hmac_key # If device has just 'input', write HMAC key here if (mode == 0): hmac_key = str( input( "Introduce la clave que aparece en la plataforma: " )) # If device has 'output' or nothing, write HMAC key on web page else: hmac_key = str(os.urandom(2).hex()) print("HMAC KEY: " + hmac_key) # Calcula HMAC (DH or ECDH) if (asymmetric_mode == 0): h = hmac.new( bytes(hmac_key, 'ascii'), bytes(str(a_public_key.public_numbers().y), 'ascii'), hashlib.sha256) else: h = hmac.new( bytes(hmac_key, 'ascii'), a_public_key_ecdh.public_bytes( encoding=Encoding.PEM, format=PublicFormat.SubjectPublicKeyInfo), hashlib.sha256) # Send to platform HMAC client.publish(name + "/from", "hmac:" + str(h.hexdigest())) if (hmac_key is None): # New thread because 'input' is blocking threading.Thread(target=hebra).start() # Calculate FERNET key using HASH derived_key_fernet = HKDF( algorithm=hashes.SHA256(), length=32, salt=None, info=b'handshake data').derive(a_shared_key) key_fernet = base64.urlsafe_b64encode(derived_key_fernet) f_key = Fernet(key_fernet) # Calculate AEAD key using HASH derived_key_aead = HKDF( algorithm=hashes.SHA256(), length=24, salt=None, info=b'handshake data').derive(a_shared_key) key_aead = base64.urlsafe_b64encode(derived_key_aead) a_key = aead.AESGCM(key_aead)