def _decapsulate_reencrypted(receiving_privkey: UmbralPrivateKey, capsule: Capsule, key_length: int = DEM_KEYSIZE) -> bytes: """Derive the same symmetric key""" params = capsule._umbral_params pub_key = receiving_privkey.get_pubkey().point_key priv_key = receiving_privkey.bn_key ni = capsule._point_noninteractive d = CurveBN.hash(ni, pub_key, priv_key * ni, params=params) e_prime = capsule._point_e_prime v_prime = capsule._point_v_prime shared_key = d * (e_prime + v_prime) key = kdf(shared_key, key_length) e = capsule._point_e v = capsule._point_v s = capsule._bn_sig h = CurveBN.hash(e, v, params=params) inv_d = ~d orig_pub_key = capsule.get_correctness_keys()['delegating'].point_key if not (s * inv_d) * orig_pub_key == (h * e_prime) + v_prime: raise GenericUmbralError() return key
def split_rekey(priv_a: Union[UmbralPrivateKey, CurveBN], pubkey_b_point: Union[UmbralPublicKey, Point], threshold: int, N: int, params: UmbralParameters=None) -> List[KFrag]: """ Creates a re-encryption key from Alice to Bob and splits it in KFrags, using Shamir's Secret Sharing. Requires a threshold number of KFrags out of N to guarantee correctness of re-encryption. Returns a list of KFrags. """ params = params if params is not None else default_params() if isinstance(priv_a, UmbralPrivateKey): priv_a = priv_a.bn_key if isinstance(pubkey_b_point, UmbralPublicKey): pubkey_b_point = pubkey_b_point.point_key g = params.g pubkey_a_point = priv_a * g x = CurveBN.gen_rand(params.curve) xcomp = x * g d = CurveBN.hash(xcomp, pubkey_b_point, pubkey_b_point * x, params=params) coeffs = [priv_a * (~d)] coeffs += [CurveBN.gen_rand(params.curve) for _ in range(threshold - 1)] u = params.u g_ab = priv_a * pubkey_b_point blake2b = hashes.Hash(hashes.BLAKE2b(64), backend=backend) blake2b.update(pubkey_a_point.to_bytes()) blake2b.update(pubkey_b_point.to_bytes()) blake2b.update(g_ab.to_bytes()) hashed_dh_tuple = blake2b.finalize() kfrags = [] for _ in range(N): id_kfrag = CurveBN.gen_rand(params.curve) share_x = CurveBN.hash(id_kfrag, hashed_dh_tuple, params=params) rk = poly_eval(coeffs, share_x) u1 = rk * u y = CurveBN.gen_rand(params.curve) signature_input = [y * g, id_kfrag, pubkey_a_point, pubkey_b_point, u1, xcomp] z1 = CurveBN.hash(*signature_input, params=params) z2 = y - priv_a * z1 kfrag = KFrag(bn_id=id_kfrag, bn_key=rk, point_noninteractive=xcomp, point_commitment=u1, bn_sig1=z1, bn_sig2=z2) kfrags.append(kfrag) return kfrags
def _decapsulate_reencrypted(pub_key: Point, priv_key: CurveBN, orig_pub_key: Point, capsule: Capsule, key_length=32, params: UmbralParameters = None) -> bytes: """Derive the same symmetric key""" params = params if params is not None else default_params() ni = capsule._point_noninteractive d = CurveBN.hash(ni, pub_key, priv_key * ni, params=params) e_prime = capsule._point_e_prime v_prime = capsule._point_v_prime shared_key = d * (e_prime + v_prime) key = kdf(shared_key, key_length) e = capsule._point_e v = capsule._point_v s = capsule._bn_sig h = CurveBN.hash(e, v, params=params) inv_d = ~d if not (s * inv_d) * orig_pub_key == (h * e_prime) + v_prime: raise GenericUmbralError() return key
def _decapsulate_reencrypted(receiving_privkey: UmbralPrivateKey, capsule: Capsule, key_length: int = DEM_KEYSIZE) -> bytes: """Derive the same symmetric encapsulated_key""" params = capsule.params pub_key = receiving_privkey.get_pubkey().point_key priv_key = receiving_privkey.bn_key precursor = capsule._attached_cfrags[0]._point_precursor dh_point = priv_key * precursor from constant_sorrow import constants # Combination of CFrags via Shamir's Secret Sharing reconstruction if len(capsule._attached_cfrags) > 1: xs = [ CurveBN.hash(precursor, pub_key, dh_point, bytes(constants.X_COORDINATE), cfrag._kfrag_id, params=params) for cfrag in capsule._attached_cfrags ] e_summands, v_summands = list(), list() for cfrag, x in zip(capsule._attached_cfrags, xs): if precursor != cfrag._point_precursor: raise ValueError("Attached CFrags are not pairwise consistent") lambda_i = lambda_coeff(x, xs) e_summands.append(lambda_i * cfrag._point_e1) v_summands.append(lambda_i * cfrag._point_v1) e_prime = sum(e_summands[1:], e_summands[0]) v_prime = sum(v_summands[1:], v_summands[0]) else: e_prime = capsule._attached_cfrags[0]._point_e1 v_prime = capsule._attached_cfrags[0]._point_v1 # Secret value 'd' allows to make Umbral non-interactive d = CurveBN.hash(precursor, pub_key, dh_point, bytes(constants.NON_INTERACTIVE), params=params) e, v, s = capsule.components() h = CurveBN.hash(e, v, params=params) orig_pub_key = capsule.get_correctness_keys( )['delegating'].point_key # type: ignore if not (s / d) * orig_pub_key == (h * e_prime) + v_prime: raise GenericUmbralError() shared_key = d * (e_prime + v_prime) encapsulated_key = kdf(shared_key, key_length) return encapsulated_key
def _reconstruct_shamirs_secret( self, priv_b: Union[UmbralPrivateKey, CurveBN]) -> None: g = self._umbral_params.g if isinstance(priv_b, UmbralPrivateKey): pub_b = priv_b.get_pubkey() priv_b = priv_b.bn_key else: pub_b = priv_b * g cfrag_0 = self._attached_cfrags[0] id_0 = cfrag_0._kfrag_id ni = cfrag_0._point_noninteractive xcoord = cfrag_0._point_xcoord dh_xcoord = priv_b * xcoord blake2b = hashes.Hash(hashes.BLAKE2b(64), backend=backend) blake2b.update(xcoord.to_bytes()) blake2b.update(pub_b.to_bytes()) blake2b.update(dh_xcoord.to_bytes()) hashed_dh_tuple = blake2b.finalize() if len(self._attached_cfrags) > 1: xs = [ CurveBN.hash(cfrag._kfrag_id, hashed_dh_tuple, params=self._umbral_params) for cfrag in self._attached_cfrags ] x_0 = CurveBN.hash(id_0, hashed_dh_tuple, params=self._umbral_params) lambda_0 = lambda_coeff(x_0, xs) e = lambda_0 * cfrag_0._point_e1 v = lambda_0 * cfrag_0._point_v1 for cfrag in self._attached_cfrags[1:]: if (ni, xcoord) != (cfrag._point_noninteractive, cfrag._point_xcoord): raise ValueError( "Attached CFrags are not pairwise consistent") x_i = CurveBN.hash(cfrag._kfrag_id, hashed_dh_tuple, params=self._umbral_params) lambda_i = lambda_coeff(x_i, xs) e = e + (lambda_i * cfrag._point_e1) v = v + (lambda_i * cfrag._point_v1) else: e = cfrag_0._point_e1 v = cfrag_0._point_v1 self._point_e_prime = e self._point_v_prime = v self._point_noninteractive = ni
def assess_cfrag_correctness(cfrag, capsule: "Capsule", pubkey_a_point, pubkey_b_point, params: UmbralParameters = None): params = params if params is not None else default_params() #### ## Here are the formulaic constituents shared with `prove_cfrag_correctness`. #### e = capsule._point_e v = capsule._point_v e1 = cfrag._point_e1 v1 = cfrag._point_v1 u = params.u u1 = cfrag.proof._point_kfrag_commitment e2 = cfrag.proof._point_e2 v2 = cfrag.proof._point_v2 u2 = cfrag.proof._point_kfrag_pok hash_input = (e, e1, e2, v, v1, v2, u, u1, u2) if cfrag.proof.metadata is not None: hash_input += (cfrag.proof.metadata, ) h = CurveBN.hash(*hash_input, params=params) z1 = cfrag.proof._bn_kfrag_sig1 z2 = cfrag.proof._bn_kfrag_sig2 z3 = cfrag.proof._bn_sig ######## ni = cfrag._point_noninteractive xcoord = cfrag._point_xcoord kfrag_id = cfrag._kfrag_id g = params.g # TODO: change this Schnorr signature for Ed25519 or ECDSA (#97) g_y = (z2 * g) + (z1 * pubkey_a_point) signature_input = (g_y, kfrag_id, pubkey_a_point, pubkey_b_point, u1, ni, xcoord) kfrag_signature1 = CurveBN.hash(*signature_input, params=params) valid_kfrag_signature = z1 == kfrag_signature1 correct_reencryption_of_e = z3 * e == e2 + (h * e1) correct_reencryption_of_v = z3 * v == v2 + (h * v1) correct_rk_commitment = z3 * u == u2 + (h * u1) return valid_kfrag_signature \ & correct_reencryption_of_e \ & correct_reencryption_of_v \ & correct_rk_commitment
def verify_kfrag(kfrag, pubkey_a_point, pubkey_b_point, params: UmbralParameters = None): params = params if params is not None else default_params() u = params.u id = kfrag._id key = kfrag._bn_key u1 = kfrag._point_commitment z1 = kfrag._bn_sig1 z2 = kfrag._bn_sig2 ni = kfrag._point_noninteractive xcoord = kfrag._point_xcoord # We check that the commitment u1 is well-formed correct_commitment = u1 == key * u # We check the Schnorr signature over the kfrag components g_y = (z2 * params.g) + (z1 * pubkey_a_point) signature_input = (g_y, id, pubkey_a_point, pubkey_b_point, u1, ni, xcoord) valid_kfrag_signature = z1 == CurveBN.hash(*signature_input, params=params) return correct_commitment & valid_kfrag_signature
def verify_kfrag(kfrag, pubkey_a_point, pubkey_b_point, params: UmbralParameters = None): params = params if params is not None else default_params() u = params.u u1 = kfrag._point_commitment z1 = kfrag._bn_sig1 z2 = kfrag._bn_sig2 x = kfrag._point_noninteractive key = kfrag._bn_key # We check that the commitment u1 is well-formed correct_commitment = u1 == key * u # We check the Schnorr signature over the kfrag components g_y = (z2 * params.g) + (z1 * pubkey_a_point) kfrag_components = [ g_y, kfrag._bn_id, pubkey_a_point, pubkey_b_point, u1, x ] valid_kfrag_signature = z1 == CurveBN.hash(*kfrag_components, params=params) return correct_commitment & valid_kfrag_signature
def _reconstruct_shamirs_secret(self, pub_a: Union[UmbralPublicKey, Point], priv_b: Union[UmbralPrivateKey, CurveBN], params: UmbralParameters=None) -> None: params = params if params is not None else default_params() if isinstance(priv_b, UmbralPrivateKey): priv_b = priv_b.bn_key if isinstance(pub_a, UmbralPublicKey): pub_a = pub_a.point_key g = params.g pub_b = priv_b * g g_ab = priv_b * pub_a blake2b = hashes.Hash(hashes.BLAKE2b(64), backend=backend) blake2b.update(pub_a.to_bytes()) blake2b.update(pub_b.to_bytes()) blake2b.update(g_ab.to_bytes()) hashed_dh_tuple = blake2b.finalize() cfrag_0 = self._attached_cfrags[0] id_0 = cfrag_0._bn_kfrag_id x_0 = CurveBN.hash(id_0, hashed_dh_tuple, params=params) if len(self._attached_cfrags) > 1: xs = [CurveBN.hash(cfrag._bn_kfrag_id, hashed_dh_tuple, params=params) for cfrag in self._attached_cfrags] lambda_0 = lambda_coeff(x_0, xs) e = lambda_0 * cfrag_0._point_e1 v = lambda_0 * cfrag_0._point_v1 for cfrag in self._attached_cfrags[1:]: x_i = CurveBN.hash(cfrag._bn_kfrag_id, hashed_dh_tuple, params=params) lambda_i = lambda_coeff(x_i, xs) e = e + (lambda_i * cfrag._point_e1) v = v + (lambda_i * cfrag._point_v1) else: e = cfrag_0._point_e1 v = cfrag_0._point_v1 self._point_e_prime = e self._point_v_prime = v self._point_noninteractive = cfrag_0._point_noninteractive
def verify(self, params: UmbralParameters=None) -> bool: params = params if params is not None else default_params() e = self._point_e v = self._point_v s = self._bn_sig h = CurveBN.hash(e, v, params=params) return s * params.g == v + (h * e)
def assess_cfrag_correctness(cfrag, capsule: "Capsule", delegating_point, signing_pubkey, receiving_point, params: UmbralParameters = None): params = params if params is not None else default_params() #### ## Here are the formulaic constituents shared with `prove_cfrag_correctness`. #### e = capsule._point_e v = capsule._point_v e1 = cfrag._point_e1 v1 = cfrag._point_v1 u = params.u try: u1 = cfrag.proof._point_kfrag_commitment e2 = cfrag.proof._point_e2 v2 = cfrag.proof._point_v2 u2 = cfrag.proof._point_kfrag_pok except AttributeError: if cfrag.proof is None: raise cfrag.NoProofProvided else: raise hash_input = (e, e1, e2, v, v1, v2, u, u1, u2) if cfrag.proof.metadata is not None: hash_input += (cfrag.proof.metadata, ) h = CurveBN.hash(*hash_input, params=params) ######## ni = cfrag._point_noninteractive xcoord = cfrag._point_xcoord kfrag_id = cfrag._kfrag_id kfrag_validity_message = bytes().join( bytes(material) for material in (kfrag_id, delegating_point, receiving_point, u1, ni, xcoord)) valid_kfrag_signature = cfrag.proof.kfrag_signature.verify( kfrag_validity_message, signing_pubkey) z3 = cfrag.proof.bn_sig correct_reencryption_of_e = z3 * e == e2 + (h * e1) correct_reencryption_of_v = z3 * v == v2 + (h * v1) correct_rk_commitment = z3 * u == u2 + (h * u1) return valid_kfrag_signature \ & correct_reencryption_of_e \ & correct_reencryption_of_v \ & correct_rk_commitment
def verify(self) -> bool: g = self.params.g e, v, s = self.components() h = CurveBN.hash(e, v, params=self.params) result = s * g == v + (h * e) # type: bool return result
def verify(self) -> bool: g = self._umbral_params.g e = self._point_e v = self._point_v s = self._bn_sig h = CurveBN.hash(e, v, params=self._umbral_params) return s * g == v + (h * e)
def assess_cfrag_correctness(cfrag: 'CapsuleFrag', capsule: 'Capsule') -> bool: correctness_keys = capsule.get_correctness_keys() delegating_pubkey = correctness_keys['delegating'] signing_pubkey = correctness_keys['verifying'] receiving_pubkey = correctness_keys['receiving'] params = capsule.params #### # Here are the formulaic constituents shared with `prove_cfrag_correctness`. #### e = capsule._point_e v = capsule._point_v e1 = cfrag._point_e1 v1 = cfrag._point_v1 u = params.u try: u1 = cfrag.proof._point_kfrag_commitment e2 = cfrag.proof._point_e2 v2 = cfrag.proof._point_v2 u2 = cfrag.proof._point_kfrag_pok except AttributeError: if cfrag.proof is None: raise cfrag.NoProofProvided else: raise hash_input = [e, e1, e2, v, v1, v2, u, u1, u2] if cfrag.proof.metadata is not None: hash_input.append(cfrag.proof.metadata) h = CurveBN.hash(*hash_input, params=params) ######## precursor = cfrag._point_precursor kfrag_id = cfrag._kfrag_id validity_input = (kfrag_id, delegating_pubkey, receiving_pubkey, u1, precursor) kfrag_validity_message = bytes().join(bytes(item) for item in validity_input) valid_kfrag_signature = cfrag.proof.kfrag_signature.verify(kfrag_validity_message, signing_pubkey) z3 = cfrag.proof.bn_sig correct_reencryption_of_e = z3 * e == e2 + (h * e1) correct_reencryption_of_v = z3 * v == v2 + (h * v1) correct_rk_commitment = z3 * u == u2 + (h * u1) return valid_kfrag_signature \ & correct_reencryption_of_e \ & correct_reencryption_of_v \ & correct_rk_commitment
def test_curvebn_hash(): vector_file = os.path.join('vectors', 'vectors_curvebn_hash.json') try: with open(vector_file) as f: vector_suite = json.load(f) except OSError: raise params = default_params() for vector in vector_suite['vectors']: hash_input = [bytes.fromhex(item['bytes']) for item in vector['input']] expected = CurveBN.from_bytes(bytes.fromhex(vector['output'])) assert CurveBN.hash(*hash_input, params=params) == expected
def prove_cfrag_correctness( cfrag: "CapsuleFrag", kfrag: "KFrag", capsule: "Capsule", metadata: bytes = None, params: UmbralParameters = None) -> "CorrectnessProof": params = params if params is not None else default_params() rk = kfrag._bn_key t = CurveBN.gen_rand(params.curve) #### ## Here are the formulaic constituents shared with `assess_cfrag_correctness`. #### e = capsule._point_e v = capsule._point_v e1 = cfrag._point_e1 v1 = cfrag._point_v1 u = params.u u1 = kfrag._point_commitment e2 = t * e v2 = t * v u2 = t * u hash_input = (e, e1, e2, v, v1, v2, u, u1, u2) if metadata is not None: hash_input += (metadata, ) h = CurveBN.hash(*hash_input, params=params) ######## z3 = t + h * rk cfrag.attach_proof(e2, v2, u1, u2, metadata=metadata, z3=z3, kfrag_signature=kfrag.signature) # Check correctness of original ciphertext (check nº 2) at the end # to avoid timing oracles if not capsule.verify(): raise capsule.NotValid("Capsule verification failed.")
def derive_privkey_by_label(self, label: bytes, salt: bytes=None, params: UmbralParameters=None): """ Derives an UmbralPrivateKey using a KDF from this instance of UmbralKeyingMaterial, a label, and an optional salt. """ params = params if params is not None else default_params() key_material = HKDF( algorithm=hashes.BLAKE2b(64), length=64, salt=salt, info=b"NuCypherKMS/KeyDerivation/"+label, backend=default_backend() ).derive(self.keying_material) bn_key = CurveBN.hash(key_material, params=params) return UmbralPrivateKey(bn_key, params)
def _encapsulate(alice_pubkey: UmbralPublicKey, key_length: int = DEM_KEYSIZE) -> Tuple[bytes, Capsule]: """Generates a symmetric key and its associated KEM ciphertext""" params = alice_pubkey.params g = params.g priv_r = CurveBN.gen_rand(params.curve) pub_r = priv_r * g priv_u = CurveBN.gen_rand(params.curve) pub_u = priv_u * g h = CurveBN.hash(pub_r, pub_u, params=params) s = priv_u + (priv_r * h) shared_key = (priv_r + priv_u) * alice_pubkey.point_key # Key to be used for symmetric encryption key = kdf(shared_key, key_length) return key, Capsule(point_e=pub_r, point_v=pub_u, bn_sig=s, params=params)
def prove_cfrag_correctness(cfrag: 'CapsuleFrag', kfrag: 'KFrag', capsule: 'Capsule', metadata: Optional[bytes] = None ) -> None: params = capsule.params # Check correctness of original ciphertext if not capsule.verify(): raise capsule.NotValid("Capsule verification failed.") rk = kfrag._bn_key t = CurveBN.gen_rand(params.curve) #### # Here are the formulaic constituents shared with `assess_cfrag_correctness`. #### e = capsule._point_e v = capsule._point_v e1 = cfrag._point_e1 v1 = cfrag._point_v1 u = params.u u1 = kfrag._point_commitment e2 = t * e v2 = t * v u2 = t * u hash_input = [e, e1, e2, v, v1, v2, u, u1, u2] if metadata is not None: hash_input.append(metadata) h = CurveBN.hash(*hash_input, params=params) ######## z3 = t + h * rk cfrag.attach_proof(e2, v2, u1, u2, metadata=metadata, z3=z3, kfrag_signature=kfrag.signature_for_bob)
def _encapsulate(alice_pub_key: Point, key_length=32, params: UmbralParameters=None) -> Tuple[bytes, Capsule]: """Generates a symmetric key and its associated KEM ciphertext""" params = params if params is not None else default_params() g = params.g priv_r = CurveBN.gen_rand(params.curve) pub_r = priv_r * g priv_u = CurveBN.gen_rand(params.curve) pub_u = priv_u * g h = CurveBN.hash(pub_r, pub_u, params=params) s = priv_u + (priv_r * h) shared_key = (priv_r + priv_u) * alice_pub_key # Key to be used for symmetric encryption key = kdf(shared_key, key_length) return key, Capsule(point_e=pub_r, point_v=pub_u, bn_sig=s)
def test_cant_hash_arbitrary_object_into_bignum(): whatever = object() with pytest.raises(TypeError): CurveBN.hash(whatever)
def assess_cfrag_correctness(cfrag: 'CapsuleFrag', capsule: 'Capsule') -> bool: correctness_keys = capsule.get_correctness_keys() delegating_pubkey = correctness_keys['delegating'] signing_pubkey = correctness_keys['verifying'] receiving_pubkey = correctness_keys['receiving'] if not all((delegating_pubkey, signing_pubkey, receiving_pubkey)): raise TypeError("Need all three keys to verify correctness.") delegating_point = delegating_pubkey.point_key receiving_point = receiving_pubkey.point_key params = capsule._umbral_params #### # Here are the formulaic constituents shared with `prove_cfrag_correctness`. #### e = capsule._point_e v = capsule._point_v e1 = cfrag._point_e1 v1 = cfrag._point_v1 u = params.u try: u1 = cfrag.proof._point_kfrag_commitment e2 = cfrag.proof._point_e2 v2 = cfrag.proof._point_v2 u2 = cfrag.proof._point_kfrag_pok except AttributeError: if cfrag.proof is None: raise cfrag.NoProofProvided else: raise hash_input = (e, e1, e2, v, v1, v2, u, u1, u2) if cfrag.proof.metadata is not None: hash_input += (cfrag.proof.metadata,) h = CurveBN.hash(*hash_input, params=params) ######## ni = cfrag._point_noninteractive xcoord = cfrag._point_xcoord kfrag_id = cfrag._kfrag_id kfrag_validity_message = bytes().join( bytes(material) for material in (kfrag_id, delegating_point, receiving_point, u1, ni, xcoord)) valid_kfrag_signature = cfrag.proof.kfrag_signature.verify(kfrag_validity_message, signing_pubkey) z3 = cfrag.proof.bn_sig correct_reencryption_of_e = z3 * e == e2 + (h * e1) correct_reencryption_of_v = z3 * v == v2 + (h * v1) correct_rk_commitment = z3 * u == u2 + (h * u1) return valid_kfrag_signature \ & correct_reencryption_of_e \ & correct_reencryption_of_v \ & correct_rk_commitment
def split_rekey(delegating_privkey: UmbralPrivateKey, signer: Signer, receiving_pubkey: UmbralPublicKey, threshold: int, N: int) -> List[KFrag]: """ Creates a re-encryption key from Alice to Bob and splits it in KFrags, using Shamir's Secret Sharing. Requires a threshold number of KFrags out of N to guarantee correctness of re-encryption. Returns a list of KFrags. """ if threshold <= 0 or threshold > N: raise ValueError( 'Arguments threshold and N must satisfy 0 < threshold <= N') if delegating_privkey.params != receiving_pubkey.params: raise ValueError("Keys must have the same parameter set.") params = delegating_privkey.params g = params.g pubkey_a_point = delegating_privkey.get_pubkey().point_key privkey_a_bn = delegating_privkey.bn_key pubkey_b_point = receiving_pubkey.point_key # 'ni' stands for 'Non Interactive'. # This point is used as an ephemeral public key in a DH key exchange, # and the resulting shared secret 'd' allows to make Umbral non-interactive priv_ni = CurveBN.gen_rand(params.curve) ni = priv_ni * g d = CurveBN.hash(ni, pubkey_b_point, pubkey_b_point * priv_ni, params=params) coeffs = [privkey_a_bn * (~d)] coeffs += [CurveBN.gen_rand(params.curve) for _ in range(threshold - 1)] u = params.u # 'xcoord' stands for 'X coordinate'. # This point is used as an ephemeral public key in a DH key exchange, # and the resulting shared secret 'dh_xcoord' contributes to prevent # reconstruction of the re-encryption key without Bob's intervention priv_xcoord = CurveBN.gen_rand(params.curve) xcoord = priv_xcoord * g dh_xcoord = priv_xcoord * pubkey_b_point blake2b = hashes.Hash(hashes.BLAKE2b(64), backend=backend) blake2b.update(xcoord.to_bytes()) blake2b.update(pubkey_b_point.to_bytes()) blake2b.update(dh_xcoord.to_bytes()) hashed_dh_tuple = blake2b.finalize() bn_size = CurveBN.expected_bytes_length(params.curve) kfrags = [] for _ in range(N): id = os.urandom(bn_size) share_x = CurveBN.hash(id, hashed_dh_tuple, params=params) rk = poly_eval(coeffs, share_x) u1 = rk * u kfrag_validity_message = bytes().join( bytes(material) for material in (id, pubkey_a_point, pubkey_b_point, u1, ni, xcoord)) signature = signer(kfrag_validity_message) kfrag = KFrag(id=id, bn_key=rk, point_noninteractive=ni, point_commitment=u1, point_xcoord=xcoord, signature=signature) kfrags.append(kfrag) return kfrags
# CurveBN.hash() # ################## # Test vectors for different kinds of inputs (bytes, Points, CurveBNs, etc.) inputs = ( [b''], [b'abc'], [capsule._point_e], [z], [capsule._point_e, z], points, ) vectors = list() for input_to_hash in inputs: bn_output = CurveBN.hash(*input_to_hash, params=params) json_input = [{ 'class': data.__class__.__name__, 'bytes': hexlify(data), } for data in input_to_hash] json_input = {'input': json_input, 'output': hexlify(bn_output)} vectors.append(json_input) vector_suite = { 'name': 'Test vectors for umbral.curvebn.CurveBN.hash()', 'params': 'default', 'vectors': vectors }
def split_rekey(privkey_a_bn: Union[UmbralPrivateKey, CurveBN], signer_a: Signer, pubkey_b_point: Union[UmbralPublicKey, Point], threshold: int, N: int, params: UmbralParameters = None) -> List[KFrag]: """ Creates a re-encryption key from Alice to Bob and splits it in KFrags, using Shamir's Secret Sharing. Requires a threshold number of KFrags out of N to guarantee correctness of re-encryption. Returns a list of KFrags. """ params = params if params is not None else default_params() g = params.g if isinstance(privkey_a_bn, UmbralPrivateKey): pubkey_a_point = privkey_a_bn.get_pubkey().point_key privkey_a_bn = privkey_a_bn.bn_key else: pubkey_a_point = privkey_a_bn * g if isinstance(pubkey_b_point, UmbralPublicKey): pubkey_b_point = pubkey_b_point.point_key # 'ni' stands for 'Non Interactive'. # This point is used as an ephemeral public key in a DH key exchange, # and the resulting shared secret 'd' allows to make Umbral non-interactive priv_ni = CurveBN.gen_rand(params.curve) ni = priv_ni * g d = CurveBN.hash(ni, pubkey_b_point, pubkey_b_point * priv_ni, params=params) coeffs = [privkey_a_bn * (~d)] coeffs += [CurveBN.gen_rand(params.curve) for _ in range(threshold - 1)] u = params.u # 'xcoord' stands for 'X coordinate'. # This point is used as an ephemeral public key in a DH key exchange, # and the resulting shared secret 'dh_xcoord' contributes to prevent # reconstruction of the re-encryption key without Bob's intervention priv_xcoord = CurveBN.gen_rand(params.curve) xcoord = priv_xcoord * g dh_xcoord = priv_xcoord * pubkey_b_point blake2b = hashes.Hash(hashes.BLAKE2b(64), backend=backend) blake2b.update(xcoord.to_bytes()) blake2b.update(pubkey_b_point.to_bytes()) blake2b.update(dh_xcoord.to_bytes()) hashed_dh_tuple = blake2b.finalize() bn_size = CurveBN.expected_bytes_length(params.curve) kfrags = [] for _ in range(N): id = os.urandom(bn_size) share_x = CurveBN.hash(id, hashed_dh_tuple, params=params) rk = poly_eval(coeffs, share_x) u1 = rk * u kfrag_validity_message = bytes().join( bytes(material) for material in (id, pubkey_a_point, pubkey_b_point, u1, ni, xcoord)) signature = signer_a(kfrag_validity_message) kfrag = KFrag(id=id, bn_key=rk, point_noninteractive=ni, point_commitment=u1, point_xcoord=xcoord, signature=signature) kfrags.append(kfrag) return kfrags
def generate_kfrags( delegating_privkey: UmbralPrivateKey, receiving_pubkey: UmbralPublicKey, threshold: int, N: int, signer: Signer, sign_delegating_key: Optional[bool] = True, sign_receiving_key: Optional[bool] = True, ) -> List[KFrag]: """ Creates a re-encryption key from Alice's delegating public key to Bob's receiving public key, and splits it in KFrags, using Shamir's Secret Sharing. Requires a threshold number of KFrags out of N. Returns a list of N KFrags """ if threshold <= 0 or threshold > N: raise ValueError( 'Arguments threshold and N must satisfy 0 < threshold <= N') if delegating_privkey.params != receiving_pubkey.params: raise ValueError("Keys must have the same parameter set.") params = delegating_privkey.params g = params.g delegating_pubkey = delegating_privkey.get_pubkey() bob_pubkey_point = receiving_pubkey.point_key # The precursor point is used as an ephemeral public key in a DH key exchange, # and the resulting shared secret 'dh_point' is used to derive other secret values private_precursor = CurveBN.gen_rand(params.curve) precursor = private_precursor * g # type: Any dh_point = private_precursor * bob_pubkey_point from constant_sorrow import constants # Secret value 'd' allows to make Umbral non-interactive d = CurveBN.hash(precursor, bob_pubkey_point, dh_point, bytes(constants.NON_INTERACTIVE), params=params) # Coefficients of the generating polynomial coefficients = [delegating_privkey.bn_key * (~d)] coefficients += [ CurveBN.gen_rand(params.curve) for _ in range(threshold - 1) ] bn_size = CurveBN.expected_bytes_length(params.curve) kfrags = list() for _ in range(N): kfrag_id = os.urandom(bn_size) # The index of the re-encryption key share (which in Shamir's Secret # Sharing corresponds to x in the tuple (x, f(x)), with f being the # generating polynomial), is used to prevent reconstruction of the # re-encryption key without Bob's intervention share_index = CurveBN.hash(precursor, bob_pubkey_point, dh_point, bytes(constants.X_COORDINATE), kfrag_id, params=params) # The re-encryption key share is the result of evaluating the generating # polynomial for the index value rk = poly_eval(coefficients, share_index) commitment = rk * params.u # type: Any validity_message_for_bob = ( kfrag_id, delegating_pubkey, receiving_pubkey, commitment, precursor, ) # type: Any validity_message_for_bob = bytes().join( bytes(item) for item in validity_message_for_bob) signature_for_bob = signer(validity_message_for_bob) if sign_delegating_key and sign_receiving_key: mode = DELEGATING_AND_RECEIVING elif sign_delegating_key: mode = DELEGATING_ONLY elif sign_receiving_key: mode = RECEIVING_ONLY else: mode = NO_KEY validity_message_for_proxy = [kfrag_id, commitment, precursor, mode] # type: Any if sign_delegating_key: validity_message_for_proxy.append(delegating_pubkey) if sign_receiving_key: validity_message_for_proxy.append(receiving_pubkey) validity_message_for_proxy = bytes().join( bytes(item) for item in validity_message_for_proxy) signature_for_proxy = signer(validity_message_for_proxy) kfrag = KFrag( identifier=kfrag_id, bn_key=rk, point_commitment=commitment, point_precursor=precursor, signature_for_proxy=signature_for_proxy, signature_for_bob=signature_for_bob, keys_in_signature=mode, ) kfrags.append(kfrag) return kfrags