def enc(self, plain: Union[int, AddressValue], my_addr: AddressValue, target_addr: AddressValue) -> Tuple[CipherValue, Optional[RandomnessValue]]: """ Encrypt plain for receiver with target_addr. :param plain: plain text to encrypt :param my_addr: address of the sender who encrypts :param target_addr: address of the receiver for whom to encrypt :return: if symmetric -> (iv_cipher, None), if asymmetric (cipher, randomness which was used to encrypt plain) """ if isinstance(plain, AddressValue): plain = int.from_bytes(plain.val, byteorder='big') assert not isinstance(plain, Value), f"Tried to encrypt value of type {type(plain).__name__}" assert isinstance(my_addr, AddressValue) and isinstance(target_addr, AddressValue) assert int(plain) < bn128_scalar_field, f"Integer overflow, plaintext is >= field prime" zk_print(f'Encrypting value {plain} for destination "{target_addr}"', verbosity_level=2) sk = self.keystore.sk(my_addr).val raw_pk = self.keystore.getPk(target_addr) if self.params.is_symmetric_cipher(): assert len(raw_pk) == 1 pk = raw_pk[0] else: pk = self.deserialize_pk(raw_pk[:]) while True: # Retry until cipher text is not 0 cipher, rnd = self._enc(int(plain), sk, pk) cipher = CipherValue(cipher, params=self.params) rnd = RandomnessValue(rnd, params=self.params) if rnd is not None else None if cipher != CipherValue(params=self.params): break return cipher, rnd
def test_homomorphic_add_zero(self): eg = ElgamalCrypto(None) cipher1 = CipherValue([ 17990166387038654353532224054392704246273066434684370089496246721960255371329, 15866190370882469414665095798958204707796441173247149326160843221134574846694, 13578016172019942326633412365679613147103709674318008979748420035774874659858, 15995926508900361671313404296634773295236345482179714831868518062689263430374 ]) cipher2 = CipherValue([0, 0, 0, 0]) res = eg.do_op('+', None, cipher1, cipher2) self.assertEqual(CipherValue(res), cipher1)
def __get_decrypted_retval(self, raw_value, is_cipher, crypto_params_name, constructor): return self.dec(CipherValue(raw_value, params=CryptoParams(crypto_params_name)), constructor, crypto_backend=crypto_params_name )[0] if is_cipher else constructor(raw_value)
def do_rerand(self, arg: CipherValue, public_key: List[int]) -> Tuple[List[int], List[int]]: # homomorphically add encryption of zero to re-randomize r = randrange(babyjubjub.CURVE_ORDER) enc_zero = CipherValue(self._enc_with_rand(0, r, public_key), params=arg.params) return self.do_op('+', public_key, arg, enc_zero), [r]
def do_rerand(self, arg: CipherValue, crypto_backend: str, target_addr: AddressValue, data: Dict, rnd_key: str): """ Re-randomizes arg using fresh randomness, which is stored in data[rnd_key] (side-effect!) """ params = CryptoParams(crypto_backend) pk = self.__keystore[params.crypto_name].getPk(target_addr) crypto_inst = self.__crypto[params.crypto_name] assert isinstance(crypto_inst, ZkayHomomorphicCryptoInterface) result, rand = crypto_inst.do_rerand(arg, pk[:]) data[rnd_key] = RandomnessValue(rand, params=params) # store randomness return CipherValue(result, params=params)
def test_homomorphic_add(self): eg = ElgamalCrypto(None) cipher1 = CipherValue([ 17990166387038654353532224054392704246273066434684370089496246721960255371329, 15866190370882469414665095798958204707796441173247149326160843221134574846694, 13578016172019942326633412365679613147103709674318008979748420035774874659858, 15995926508900361671313404296634773295236345482179714831868518062689263430374 ]) cipher2 = CipherValue([ 20000451794290380375914691798920385097103434955980148521154607378788339649411, 3379688933589504078077257631396507733503572474143535438012650064116108361323, 19394553866420759826901398082663942344084257999221733532877406304105119931558, 20583024216337563044477284173241746163084488704258522180236559083511927239523 ]) res = eg.do_op('+', None, cipher1, cipher2) expected = [ 18885199402227818148211810144232318738102042906622969713112212912459159846007, 11125071952177567933017599368067887482603292954302203070407920687516147981132, 20036470080915178878390944667725801469044803295396841663384258912114611255016, 18986185709423663075397883577572338596028661172318034324882291197251276265727 ] self.assertEqual(res, expected)
def deserialize( operand: Union[CipherValue, int] ) -> Union[Tuple[babyjubjub.Point, babyjubjub.Point], int]: if isinstance(operand, CipherValue): # if ciphertext is 0, return (Point.ZERO, Point.ZERO) == Enc(0, 0) if operand == CipherValue([0] * 4, params=operand.params): return babyjubjub.Point.ZERO, babyjubjub.Point.ZERO else: c1 = babyjubjub.Point(babyjubjub.Fq(operand[0]), babyjubjub.Fq(operand[1])) c2 = babyjubjub.Point(babyjubjub.Fq(operand[2]), babyjubjub.Fq(operand[3])) return c1, c2 else: return operand
def do_homomorphic_op(self, op: str, crypto_backend: str, target_addr: AddressValue, *args: Union[CipherValue, int]): params = CryptoParams(crypto_backend) pk = self.__keystore[params.crypto_name].getPk(target_addr) for arg in args: if isinstance(arg, CipherValue ) and params.crypto_name != arg.params.crypto_name: raise ValueError( 'CipherValues from different crypto backends used in homomorphic operation' ) crypto_inst = self.__crypto[params.crypto_name] assert isinstance(crypto_inst, ZkayHomomorphicCryptoInterface) result = crypto_inst.do_op(op, pk[:], *args) return CipherValue(result, params=params)
def dec(self, cipher: CipherValue, my_addr: AddressValue) -> Tuple[int, Optional[RandomnessValue]]: """ Decrypt cipher encrypted for my_addr. :param cipher: encrypted value :param my_addr: cipher is encrypted for this address :return: if symmetric -> (plain, None), if asymmetric (plain, randomness which was used to encrypt plain) """ assert isinstance(cipher, CipherValue), f"Tried to decrypt value of type {type(cipher).__name__}" assert isinstance(my_addr, AddressValue) zk_print(f'Decrypting value {cipher} for {my_addr}', verbosity_level=2) if cipher == CipherValue(params=self.params): # Ciphertext is all zeros, i.e. uninitialized -> zero return 0, (None if self.params.is_symmetric_cipher() else RandomnessValue(params=self.params)) else: sk = self.keystore.sk(my_addr) plain, rnd = self._dec(cipher[:], sk.val) return plain, (None if rnd is None else RandomnessValue(rnd, params=self.params))
def __get(self, key: Union[str, Tuple], cache: bool): if not isinstance(key, Tuple): key = (key, ) var, indices = key[0], key[1:] loc = var + ''.join(f'[{k}]' for k in key[1:]) # Retrieve from state scope if cache and loc in self.__state: return self.__state[loc] else: is_cipher, constr = self.__constructors[var] try: if is_cipher: val = CipherValue( self.api._req_state_var(var, *indices, count=cfg.cipher_len)) else: val = constr(self.api._req_state_var(var, *indices)) except BlockChainError: raise KeyError(key) if cache: self.__state[loc] = val return val
def __get_decrypted_retval(self, raw_value, is_cipher, constructor): return self.dec( CipherValue(raw_value), constructor)[0] if is_cipher else constructor(raw_value)