def __init__(self, peer_key: x25519.X25519PublicKey, my_private_key: x25519.X25519PrivateKey = None, min_peer_tag_len: int = MAX_TAG_LEN): """Initialize Glome class. Performs the handshake and generates keys. Args: peer_key: Your peer's public key. my_private_key: Your private key. min_peer_tag_len: Desired length (in bytes) for the tag. Must be an integer in range 1-32. Raises: ValueError: Raised whenever min_peer_tag_len is not in range 1-32. ExchangeError: Raised whenever null shared secret is derived from user/peer key pair. """ if my_private_key is None: my_private_key, my_public_key = generate_keys() else: my_public_key = my_private_key.public_key() if not Glome.MIN_TAG_LEN < min_peer_tag_len <= Glome.MAX_TAG_LEN: raise ValueError('min_peer_tag_len must be in range {}-{}'.format( Glome.MIN_TAG_LEN, Glome.MAX_TAG_LEN)) try: shared_secret = my_private_key.exchange(peer_key) except ValueError as value_error: raise ExchangeError( 'Failed to deduce shared secret') from value_error self._send_key = shared_secret + _public_key_encode( peer_key) + _public_key_encode(my_public_key) self._receive_key = shared_secret + _public_key_encode( my_public_key) + _public_key_encode(peer_key) self._peer_key = peer_key self._my_keys = KeyPair(my_private_key, my_public_key) self._min_peer_tag_len = min_peer_tag_len
def _decrypt(cls, fobj: io.BufferedIOBase, input_size: typing.Optional[int], privkey: x25519.X25519PrivateKey, chunk_size: int) -> typing.Iterable[bytes]: if input_size is None: input_size = cls._get_size_till_eof(fobj) epubkey = x25519.X25519PublicKey.from_public_bytes( cls._read_exact(fobj, cls._PUBKEY_SIZE_BYTES)) input_size -= cls._PUBKEY_SIZE_BYTES shared_secret = privkey.exchange(epubkey) iv = cls._read_exact(fobj, cls._IV_SIZE_BYTES) input_size -= cls._IV_SIZE_BYTES cipher_key = cls._derive_keys(shared_secret, iv) cipher_iv = iv[:cls._CIPHER_IV_SIZE_BYTES] decryptor = Cipher(algorithm=cls._CIPHER(cipher_key), mode=GCM(cipher_iv), backend=cls._CRYPTO_BACKEND).decryptor() if input_size < cls._AUTH_TAG_SIZE_BYTES: raise RuntimeError('input_size is too short') bufmv = memoryview(bytearray(chunk_size)) auth_tag = b'' while input_size > 0: bytes_read = fobj.readinto(bufmv) if bytes_read == 0: break input_size -= bytes_read if input_size <= cls._AUTH_TAG_SIZE_BYTES: auth_tag_part_len = cls._AUTH_TAG_SIZE_BYTES - input_size auth_tag += bufmv[bytes_read - auth_tag_part_len:bytes_read] auth_tag += fobj.read() yield decryptor.update(bufmv[:bytes_read - auth_tag_part_len]) else: yield decryptor.update(bufmv[:bytes_read]) yield decryptor.finalize_with_tag(auth_tag)