def test_lib_crypto_ecc_exchange(self): spvk1 = s_ecc.PriKey.generate() spbk1 = spvk1.public() spvk2 = s_ecc.PriKey.generate() spbk2 = spvk2.public() k1 = (spvk1.exchange(spbk2)) k2 = (spvk2.exchange(spbk1)) self.eq(k1, k2) # Curves must be the same _pkd = c_ec.generate_private_key( c_ec.SECP192R1(), # We don't use this curve default_backend()) prkdiff = s_ecc.PriKey(_pkd) pbkdiff = prkdiff.public() self.raises(BadEccExchange, spvk1.exchange, pbkdiff) self.raises(BadEccExchange, prkdiff.exchange, spbk1) # Do a demonstrative ephemeral exchange epvk1 = s_ecc.PriKey.generate() epbk1 = epvk1.public() epvk2 = s_ecc.PriKey.generate() epbk2 = epvk2.public() # assume epbk2 is sent to the owner of pvk1 z1e = epvk1.exchange(epbk2) z1s = spvk1.exchange(spbk2) z1 = z1e + z1s # assume epbk1 is sent to the owner of pvk2 z2e = epvk2.exchange(epbk1) z2s = spvk2.exchange(spbk1) z2 = z2e + z2s self.eq(z1, z2) # run through kdf kdf1 = c_hkdf.HKDF(c_hashes.SHA256(), length=64, salt=None, info=b'test', backend=default_backend()) k1 = kdf1.derive(z1) k1tx, k1rx = k1[32:], k1[:32] kdf2 = c_hkdf.HKDF(c_hashes.SHA256(), length=64, salt=None, info=b'test', backend=default_backend()) k2 = kdf2.derive(z2) k2rx, k2tx = k2[32:], k2[:32] self.eq(k1tx, k2rx) self.eq(k1rx, k2tx) self.ne(k1tx, k2tx)
def verify_encrypted_message(message, ephemeral_public_key_bytes, priv_keys): ephemeral_public_key = ec.EllipticCurvePublicKey.from_encoded_point( ec.SECP256R1(), ephemeral_public_key_bytes) for k in priv_keys: sharedKey = k.exchange(ec.ECDH(), ephemeral_public_key) kdf = hkdf.HKDF( algorithm=hashes.SHA256(), length=512 // 8, salt=b"\0", info=b"Google", backend=backend, ) sharedKey = kdf.derive(ephemeral_public_key_bytes + sharedKey) symmetricEncryptionKey = sharedKey[:256 // 8] macKey = sharedKey[256 // 8:] encryptedMessage = base64.b64decode(message["encryptedMessage"]) h = hmac.HMAC(macKey, hashes.SHA256(), backend) h.update(encryptedMessage) try: h.verify(base64.b64decode(message["tag"])) except cryptography.exceptions.InvalidSignature: continue cipher = Cipher(algorithms.AES(symmetricEncryptionKey), modes.CTR(b"\0" * 16), backend) decryptor = cipher.decryptor() message = decryptor.update(encryptedMessage) + decryptor.finalize() return json.loads(message) raise GPayError("no private key worked")
def _ratchet(secret, proxy, salt_ghid): ''' Ratchets a key using HKDF-SHA512, using the associated address as salt. For dynamic files, this should be the previous frame ghid (not the dynamic ghid). Note: this ratchet is bound to a particular dynamic address. The ratchet algorithm is: new_key = HKDF-SHA512( IKM = old_secret, (secret IV/nonce | secret key) salt = old_frame_ghid, (entire 65 bytes) info = dynamic_ghid, (entire 65 bytes) new_key_length = len(IV/nonce) + len(key), num_keys = 1, ) ''' cls = type(secret) cipher = secret.cipher version = secret.version len_seed = len(secret.seed) len_key = len(secret.key) source = bytes(secret.seed + secret.key) instance = hkdf.HKDF(algorithm=hashes.SHA512(), length=len_seed + len_key, salt=bytes(salt_ghid), info=bytes(proxy), backend=CRYPTO_BACKEND) ratcheted = instance.derive(source) return cls(cipher=cipher, version=version, key=ratcheted[:len_key], seed=ratcheted[len_key:])
def _v1_derive_key(k, n, info): h = hkdf.HKDF( algorithm=hashes.SHA384(), length=32, salt=n[:16], info=info, backend=backends.default_backend(), ) return h.derive(k)
def stretch_key(self, key, salt): kdf = hkdf.HKDF( algorithm=hashes.SHA256(), length=self.key_size / 8, salt=salt, info=None, backend=backends.default_backend(), ) return kdf.derive(key)
def _hkdf(self, base_key, info_prefix): local_hkdf = hkdf.HKDF( algorithm=hashes.SHA384(), length=32, salt=self.table.encode(), info=info_prefix + self.field.encode(), backend=openssl_backend, ) output = local_hkdf.derive(base_key) return output
def kdf_rfc5869_derive(secret_input_bytes, output_len, m_expand=M_EXPAND_NTOR, t_key=T_KEY_NTOR): ''' Return output_len bytes generated from secret_input_bytes using RFC5869 with HKDF-SHA256. There is no equivalent verification function, as only the nonce part of the KDF result is verified directly. See https://gitweb.torproject.org/torspec.git/tree/tor-spec.txt#n1026 ''' hkdf_sha256 = hkdf.HKDF(algorithm=hashes.SHA256(), length=output_len, info=bytes(m_expand), salt=bytes(t_key), backend=backends.default_backend()) output_bytes = hkdf_sha256.derive(bytes(secret_input_bytes)) assert len(output_bytes) == output_len return bytearray(output_bytes)
def _derive_shared(self, partner): ''' Derive a shared secret with the partner. ''' # Call the donna25519 exchange method and return bytes ecdh = self._exchange_key.do_exchange(partner._exchange_key) # Get both of our addresses and then the bitwise XOR of them both my_hash = self.ghid.address their_hash = partner.ghid.address salt = bytes([a ^ b for a, b in zip(my_hash, their_hash)]) instance = hkdf.HKDF(algorithm=hashes.SHA512(), length=hashes.SHA512.digest_size, salt=salt, info=b'', backend=CRYPTO_BACKEND) key = instance.derive(ecdh) # Might as well do this immediately, not that it really adds anything del ecdh, my_hash, their_hash, salt return key
def doECDHE(statprv_u, statpub_v, ephmprv_u, ephmpub_v, length=64, salt=None, info=None): ''' Perform one side of an Ecliptic Curve Diffie Hellman Ephemeral key exchange. Args: statprv_u (PriKey): Static Private Key for U statpub_v (PubKey: Static Public Key for V ephmprv_u (PriKey): Ephemeral Private Key for U ephmpub_v (PubKey): Ephemeral Public Key for V length (int): Number of bytes to return salt (bytes): Salt to use when computing the key. info (bytes): Additional information to use when computing the key. Notes: This makes no assumption about the reuse of the Ephemeral keys passed to the function. It is the caller's responsibility to destroy the keys after they are used for doing key generation. This implementation is the dhHybrid1 scheme described in NIST 800-56A Revision 2. Returns: bytes: The derived key. ''' zs = statprv_u.exchange(statpub_v) ze = ephmprv_u.exchange(ephmpub_v) z = ze + zs kdf = c_hkdf.HKDF(c_hashes.SHA256(), length=length, salt=salt, info=info, backend=default_backend()) k = kdf.derive(z) return k
def generate_shared_key(my_private, peer_public, salt: bytes = b""): """Derive the shared key for file encryption.""" # Generate or from db if salt == b"": salt = os.urandom(16) # Project public key # peer_public_bytes = bytes.fromhex(peer_public) # loaded_peer_public = x25519.X25519PublicKey.from_public_bytes(peer_public_bytes) # Generate shared key and derive encryption key with salt shared_key = (my_private).exchange(peer_public_key=peer_public) derived_shared_key = hkdf.HKDF( algorithm=hashes.SHA256(), length=32, salt=salt, info=b"", backend=backends.default_backend(), ).derive(shared_key) LOG.debug(f"Salt: {salt}") return derived_shared_key, salt.hex().upper()
def keyderivation(private, peer, salt, info): private = x25519.X25519PrivateKey._from_private_bytes(private) public = x25519.X25519PublicKey.from_public_bytes(peer) shared = private.exchange(public) return hkdf.HKDF(hashes.BLAKE2b(64), 32, salt, info, default_backend()).derive(shared)
def initialize_session_key(self, address, actions): # asks for a challenge sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) message = "{\"type\" : \"ask_challenge\"," + "\n" message += "\"username\" : \"" + self.username + "\"" + "\n" message += "}" sock.sendto(base64.b64encode(message.encode("utf-8")), address) # receives the challenge data, server = sock.recvfrom(utils_app.SOCKET_BYTES) decoded_message = base64.b64decode(data) sock.close() message = json.loads(decoded_message, strict=False) challenge = message["challenge"] # Our parameters parameters = dh.generate_parameters(generator=5, key_size=utils_app.DH_KEY_SIZE, backend=default_backend()) # Our private key and public key from DH private_key = parameters.generate_private_key() public_key = private_key.public_key() pickled_params = codecs.encode( pickle.dumps( parameters.parameter_bytes(encoding=Encoding.PEM, format=ParameterFormat.PKCS3)), "base64").decode() message = "{\"type\" : \"session_key\"," + "\n" message += "\"params\" : \"" + pickled_params + "\"," + "\n" message += "\"username\" : \"" + self.username + "\"," + "\n" message += "\"pk\" : \"" + public_key.public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo).decode( 'utf-8') + "\",\n" message += "\"public\" : \"" + self.public_key.public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo).decode( 'utf-8') + "\"," inner_message = actions.trust_server(self, challenge=challenge) enc_json_message = None server_pub = None if address == utils_app.AM_ADDRESS: server_pub = self.server_public_key_manager else: server_pub = self.server_public_key_repository enc_json_message = utilities.encrypt_message_complete( base64.b64encode(inner_message.encode("utf-8")), "", server_pub) key = enc_json_message[0] iv = enc_json_message[2] data = enc_json_message[1] rsa_kg = RSAKGen() random_key = Fernet.generate_key() hmac = HMAC_Conf.integrity_control(data.encode(), random_key) random_key = rsa_kg.cipher_public_key(server_pub, random_key) sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) message += "\"message\" : \"" + data + "\",\n" message += "\"Key\" : \"" + str(base64.b64encode(key), 'utf-8') + "\",\n" message += "\"hmac\" : \"" + str(base64.b64encode(hmac), 'utf-8') + "\", \n" message += "\"random_key\" : \"" + str(base64.b64encode(random_key), 'utf-8') + "\", \n" message += "\"iv\" : \"" + str(base64.b64encode(iv), 'utf-8') + "\"\n" message += "}" sock.sendto(base64.b64encode(message.encode("utf-8")), address) #print(message) ##### Sent the parameters with all the information ##### ########################################################################################################### # Receive response data, server = sock.recvfrom(utils_app.SOCKET_BYTES) decoded_data = base64.b64decode(data).decode() json_message = json.loads(decoded_data, strict=False) if json_message["type"] == "No valid signature": print("No valid signature") sys.exit(0) elif json_message["type"] == "No valid certificate": print("No valid signature") sys.exit(0) # 'type', 'pk' -> public dh_key , 'info' -> handshake data, 'server_key' peer_pk = serialization.load_pem_public_key( json_message["pk"].encode('utf-8'), default_backend()) shared_secret = private_key.exchange(peer_pk) derived_key = hkdf.HKDF(algorithm=hashes.SHA256(), length=utils_app.DH_HKDF_KEY, salt=None, info=json_message["info"].encode('utf-8'), backend=default_backend())\ .derive(shared_secret) if address == utils_app.AM_ADDRESS: self.session_key_manager = derived_key else: self.session_key_repository = derived_key ### Recieved the key from the server and created the public key #### ############################################################################################################ sock.close()