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() xcomp = capsule._point_noninteractive d = CurveBN.hash_to_bn(xcomp, pub_key, priv_key * xcomp, 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_to_bn(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 split_rekey(priv_a: Union[UmbralPrivateKey, CurveBN], pub_b: 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(pub_b, UmbralPublicKey): pub_b = pub_b.point_key g = params.g pub_a = priv_a * g x = CurveBN.gen_rand(params.curve) xcomp = x * g d = CurveBN.hash_to_bn(xcomp, pub_b, pub_b * 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 * pub_b 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() kfrags = [] for _ in range(N): id_kfrag = CurveBN.gen_rand(params.curve) share_x = CurveBN.hash_to_bn(id_kfrag, hashed_dh_tuple, params=params) rk = poly_eval(coeffs, share_x) u1 = rk * u y = CurveBN.gen_rand(params.curve) z1 = CurveBN.hash_to_bn(y * g, id_kfrag, pub_a, pub_b, u1, xcomp, 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 _verify_correctness(capsule: Capsule, cfrag: CapsuleFrag, pub_a: Point, pub_b: Point, params: UmbralParameters=None) -> bool: proof = cfrag.proof params = params if params is not None else default_params() e = capsule._point_e v = capsule._point_v e1 = cfrag._point_e1 v1 = cfrag._point_v1 xcomp = cfrag._point_noninteractive kfrag_id = cfrag._bn_kfrag_id e2 = proof._point_e2 v2 = proof._point_v2 g = params.g u = params.u u1 = proof._point_kfrag_commitment u2 = proof._point_kfrag_pok z1 = proof._bn_kfrag_sig1 z2 = proof._bn_kfrag_sig2 z3 = proof._bn_sig g_y = (z2 * g) + (z1 * pub_a) hash_input = [e, e1, e2, v, v1, v2, u, u1, u2] if proof.metadata is not None: hash_input.append(proof.metadata) h = CurveBN.hash_to_bn(*hash_input, params=params) signature_input = [g_y, kfrag_id, pub_a, pub_b, u1, xcomp] kfrag_signature1 = CurveBN.hash_to_bn(*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 _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_to_bn(id_0, hashed_dh_tuple, params=params) if len(self._attached_cfrags) > 1: xs = [CurveBN.hash_to_bn(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_to_bn(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_to_bn(e, v, params=params) return s * params.g == v + (h * e)
def _prove_correctness(cfrag: CapsuleFrag, kfrag: KFrag, capsule: Capsule, metadata: bytes=None, params: UmbralParameters=None ) -> CorrectnessProof: params = params if params is not None else default_params() e1 = cfrag._point_e1 v1 = cfrag._point_v1 e = capsule._point_e v = capsule._point_v u = params.u u1 = kfrag._point_commitment rk = kfrag._bn_key t = CurveBN.gen_rand(params.curve) 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_to_bn(*hash_input, params=params) z3 = t + h * rk cfrag.proof = CorrectnessProof(point_e2=e2, point_v2=v2, point_kfrag_commitment=u1, point_kfrag_pok=u2, bn_kfrag_sig1=kfrag._bn_sig1, bn_kfrag_sig2=kfrag._bn_sig2, bn_sig=z3, metadata=metadata) # Check correctness of original ciphertext (check nº 2) at the end # to avoid timing oracles if not capsule.verify(params): 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_to_bn(key_material, params=params) return UmbralPrivateKey(bn_key, params)
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_to_bn(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 verify(self, pub_a, pub_b, params: "UmbralParameters" = None): params = params if params is not None else default_params() u = params.u u1 = self._point_commitment z1 = self._bn_sig1 z2 = self._bn_sig2 x = self._point_noninteractive key = self._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 * pub_a) kfrag_components = [g_y, self._bn_id, pub_a, pub_b, u1, x] valid_kfrag_signature = z1 == CurveBN.hash_to_bn(*kfrag_components, params=params) return correct_commitment & valid_kfrag_signature