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 expected_bytes_length(cls, curve: Optional[EllipticCurve] = None): """ Returns the size (in bytes) of a CorrectnessProof without the metadata. If no curve is given, it will use the default curve. """ curve = curve if curve is not None else default_curve() bn_size = CurveBN.expected_bytes_length(curve=curve) point_size = Point.expected_bytes_length(curve=curve) return (bn_size * 3) + (point_size * 4)
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) result = s * g == v + (h * e) # type: bool return result
def expected_bytes_length(cls, curve: Optional[Curve] = None) -> int: """ Returns the size (in bytes) of a Capsule given the curve. If no curve is provided, it will use the default curve. """ curve = curve if curve is not None else default_curve() bn_size = CurveBN.expected_bytes_length(curve) point_size = Point.expected_bytes_length(curve) return (bn_size * 1) + (point_size * 2)
def get_size(cls, curve: ec.EllipticCurve = None): """ Returns the size (in bytes) of a KFrag given the curve. If no curve is provided, it will use the default curve. """ curve = curve if curve is not None else default_curve() bn_size = CurveBN.get_size(curve) point_size = Point.get_size(curve) return (bn_size * 4) + (point_size * 2)
def get_size(cls, curve: ec.EllipticCurve = None): """ Returns the size (in bytes) of a CorrectnessProof without the metadata. If no curve is given, it will use the default curve. """ curve = curve if curve is not None else default_curve() bn_size = CurveBN.get_size(curve=curve) point_size = Point.get_size(curve=curve) return (bn_size * 3) + (point_size * 4)
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.append(metadata) h = CurveBN.hash(*hash_input, params=params) z1 = kfrag._bn_sig1 z2 = kfrag._bn_sig2 z3 = t + h * rk ######## cfrag.attach_proof(e2, v2, u1, u2, z1, z2, z3, 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 gen_key( cls, params: Optional[UmbralParameters] = None) -> 'UmbralPrivateKey': """ Generates a private key and returns it. """ if params is None: params = default_params() bn_key = CurveBN.gen_rand(params.curve) return cls(bn_key, params)
def test_bad_capsule_fails_reencryption(alices_keys): priv_key_alice, pub_key_alice = alices_keys kfrags = pre.split_rekey(priv_key_alice, pub_key_alice, 1, 2) bollocks_capsule = Capsule(point_e=Point.gen_rand(), point_v=Point.gen_rand(), bn_sig=CurveBN.gen_rand()) with pytest.raises(Capsule.NotValid): pre.reencrypt(kfrags[0], bollocks_capsule)
def test_bad_capsule_fails_reencryption(kfrags): params = default_params() bollocks_capsule = Capsule(params, point_e=Point.gen_rand(), point_v=Point.gen_rand(), bn_sig=CurveBN.gen_rand()) for kfrag in kfrags: with pytest.raises(Capsule.NotValid): pre.reencrypt(kfrag, bollocks_capsule)
def get_size(cls, curve: ec.EllipticCurve = None): """ Returns the size (in bytes) of a CapsuleFrag given the curve without the CorrectnessProof. If no curve is provided, it will use the default curve. """ curve = curve if curve is not None else default_curve() bn_size = CurveBN.get_size(curve) point_size = Point.get_size(curve) return (bn_size * 1) + (point_size * 3)
def from_bytes(cls, data: bytes, curve: ec.EllipticCurve = None): """ Instantiate a KFrag object from the serialized data. """ curve = curve if curve is not None else default_curve() bn_size = CurveBN.get_size(curve) point_size = Point.get_size(curve) data = BytesIO(data) # CurveBNs are the keysize in bytes, Points are compressed and the # keysize + 1 bytes long. id = data.read(bn_size) key = CurveBN.from_bytes(data.read(bn_size), curve) ni = Point.from_bytes(data.read(point_size), curve) commitment = Point.from_bytes(data.read(point_size), curve) xcoord = Point.from_bytes(data.read(point_size), curve) sig1 = CurveBN.from_bytes(data.read(bn_size), curve) sig2 = CurveBN.from_bytes(data.read(bn_size), curve) return cls(id, key, ni, commitment, xcoord, sig1, sig2)
def lambda_coeff(id_i: CurveBN, selected_ids: List[CurveBN]) -> CurveBN: ids = [x for x in selected_ids if x != id_i] if not ids: return CurveBN.from_int(1, id_i.curve) result = ids[0] / (ids[0] - id_i) for id_j in ids[1:]: result = result * id_j / (id_j - id_i) return result
def test_capsule_equality(): params = default_params() one_capsule = Capsule(params, point_e=Point.gen_rand(), point_v=Point.gen_rand(), bn_sig=CurveBN.gen_rand()) another_capsule = Capsule(params, point_e=Point.gen_rand(), point_v=Point.gen_rand(), bn_sig=CurveBN.gen_rand()) assert one_capsule != another_capsule activated_capsule = Capsule(params, point_e_prime=Point.gen_rand(), point_v_prime=Point.gen_rand(), point_noninteractive=Point.gen_rand()) assert activated_capsule != one_capsule
def from_bytes(cls, signature_as_bytes: bytes, der_encoded: bool = False, curve: Optional[Curve] = None) -> 'Signature': curve = curve if curve is not None else default_curve() if der_encoded: r, s = decode_dss_signature(signature_as_bytes) else: expected_len = cls.expected_bytes_length(curve) if not len(signature_as_bytes) == expected_len: raise ValueError( "Looking for exactly {} bytes if you call from_bytes \ with der_encoded=False and curve={}.".format( expected_len, curve)) else: r = int.from_bytes(signature_as_bytes[:(expected_len // 2)], "big") s = int.from_bytes(signature_as_bytes[(expected_len // 2):], "big") return cls(CurveBN.from_int(r, curve), CurveBN.from_int(s, curve))
def expected_bytes_length(cls, curve: Optional[EllipticCurve] = None) -> int: """ Returns the size (in bytes) of a CapsuleFrag given the curve without the CorrectnessProof. If no curve is provided, it will use the default curve. """ curve = curve if curve is not None else default_curve() bn_size = CurveBN.expected_bytes_length(curve) point_size = Point.expected_bytes_length(curve) return (bn_size * 1) + (point_size * 4)
def prove_cfrag_correctness(cfrag: 'CapsuleFrag', kfrag: 'KFrag', capsule: 'Capsule', metadata: Optional[bytes] = None ) -> 'CorrectnessProof': params = capsule._umbral_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 _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 _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 test_cannot_create_capsule_from_bogus_material(alices_keys): params = alices_keys[0].params with pytest.raises(TypeError): _capsule_of_questionable_parentage = Capsule(params, point_e=Point.gen_rand(), point_v=42, bn_sig=CurveBN.gen_rand()) with pytest.raises(TypeError): _capsule_of_questionable_parentage = Capsule(params, point_e=Point.gen_rand(), point_v=Point.gen_rand(), bn_sig=42)
def test_invalid_deserialization(curve): size_in_bytes = CurveBN.expected_bytes_length(curve) # All-zeros bytestring are invalid (i.e., 0 < bn < order of the curve) zero_bytes = bytes(size_in_bytes) with pytest.raises(ValueError): _bn = CurveBN.from_bytes(zero_bytes, curve) # All-ones bytestring is invalid too (since it's greater than order) lots_of_ones = 2**(8*size_in_bytes) - 1 lots_of_ones = lots_of_ones.to_bytes(size_in_bytes, 'big') with pytest.raises(ValueError): _bn = CurveBN.from_bytes(lots_of_ones, curve) # Serialization of `order` is invalid since it's not strictly lower than # the order of the curve order = default_backend()._bn_to_int(curve.order) with pytest.raises(ValueError): _bn = CurveBN.from_bytes(order.to_bytes(size_in_bytes, 'big'), curve) # On the other hand, serialization of `order - 1` is valid order -= 1 _bn = CurveBN.from_bytes(order.to_bytes(size_in_bytes, 'big'), curve)
def from_bytes(cls, data: bytes, curve: ec.EllipticCurve=None): """ Instantiate CorrectnessProof from serialized data. """ curve = curve if curve is not None else default_curve() bn_size = CurveBN.get_size(curve) point_size = Point.get_size(curve) data = BytesIO(data) # CurveBNs are the keysize in bytes, Points are compressed and the # keysize + 1 bytes long. e2 = Point.from_bytes(data.read(point_size), curve) v2 = Point.from_bytes(data.read(point_size), curve) kfrag_commitment = Point.from_bytes(data.read(point_size), curve) kfrag_pok = Point.from_bytes(data.read(point_size), curve) kfrag_sig1 = CurveBN.from_bytes(data.read(bn_size), curve) kfrag_sig2 = CurveBN.from_bytes(data.read(bn_size), curve) sig = CurveBN.from_bytes(data.read(bn_size), curve) metadata = data.read() or None return cls(e2, v2, kfrag_commitment, kfrag_pok, kfrag_sig1, kfrag_sig2, sig, metadata=metadata)
def get_order_from_curve(cls, curve: ec.EllipticCurve=None): """ Returns the order from the given curve as a CurveBN. """ curve = curve if curve is not None else default_curve() try: curve_nid = backend._elliptic_curve_to_nid(curve) except AttributeError: # Presume that the user passed in the curve_nid curve_nid = curve group = openssl._get_ec_group_by_curve_nid(curve_nid) order = openssl._get_ec_order_by_curve_nid(curve_nid) return CurveBN(order, curve_nid, group, order)
def test_bad_capsule_fails_reencryption(alices_keys, bobs_keys): delegating_privkey, _signing_privkey = alices_keys signer_alice = Signer(_signing_privkey) _receiving_privkey, receiving_pubkey = bobs_keys kfrags = pre.split_rekey(delegating_privkey, signer_alice, receiving_pubkey, 1, 2) bollocks_capsule = Capsule(point_e=Point.gen_rand(), point_v=Point.gen_rand(), bn_sig=CurveBN.gen_rand()) with pytest.raises(Capsule.NotValid): pre.reencrypt(kfrags[0], bollocks_capsule)
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 hash_to_curvebn(*hash_input, params=params) == expected
def expected_bytes_length(cls, curve: ec.EllipticCurve = None, activated=False): """ Returns the size (in bytes) of a Capsule given the curve. If no curve is provided, it will use the default curve. """ curve = curve if curve is not None else default_curve() bn_size = CurveBN.expected_bytes_length(curve) point_size = Point.expected_bytes_length(curve) if not activated: return (bn_size * 1) + (point_size * 2) else: return (bn_size * 1) + (point_size * 5)
def prove_correctness(self, capsule, kfrag, metadata: Optional[bytes] = 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 `verify_correctness`. #### e = capsule.point_e v = capsule.point_v e1 = self.point_e1 v1 = self.point_v1 u = params.u u1 = kfrag.point_commitment e2 = t * e # type: Any v2 = t * v # type: Any u2 = t * u # type: Any hash_input = [e, e1, e2, v, v1, v2, u, u1, u2] if metadata is not None: hash_input.append(metadata) h = hash_to_curvebn(*hash_input, params=params, hash_class=ExtendedKeccak) ######## z3 = t + h * rk self.attach_proof(e2, v2, u1, u2, metadata=metadata, z3=z3, kfrag_signature=kfrag.signature_for_bob)
def test_capsule_creation(alices_keys): with pytest.raises(TypeError): rare_capsule = Capsule() # Alice cannot make a capsule this way. # Some users may create capsules their own way. custom_capsule = Capsule(point_e=Point.gen_rand(), point_v=Point.gen_rand(), bn_sig=CurveBN.gen_rand()) assert isinstance(custom_capsule, Capsule) # Typical Alice, constructing a typical capsule _, alices_public_key = alices_keys plaintext = b'peace at dawn' ciphertext, typical_capsule = pre.encrypt(alices_public_key, plaintext) assert isinstance(typical_capsule, Capsule)
def _mock_ursula_reencrypts(ursula, corrupt_cfrag: bool = False): delegating_privkey = UmbralPrivateKey.gen_key() _symmetric_key, capsule = pre._encapsulate(delegating_privkey.get_pubkey()) signing_privkey = UmbralPrivateKey.gen_key() signing_pubkey = signing_privkey.get_pubkey() signer = Signer(signing_privkey) priv_key_bob = UmbralPrivateKey.gen_key() pub_key_bob = priv_key_bob.get_pubkey() kfrags = pre.generate_kfrags(delegating_privkey=delegating_privkey, signer=signer, receiving_pubkey=pub_key_bob, threshold=2, N=4, sign_delegating_key=False, sign_receiving_key=False) capsule.set_correctness_keys(delegating_privkey.get_pubkey(), pub_key_bob, signing_pubkey) ursula_pubkey = ursula.stamp.as_umbral_pubkey() alice_address = canonical_address_from_umbral_key(signing_pubkey) blockhash = bytes(32) specification = b''.join((bytes(capsule), bytes(ursula_pubkey), bytes(ursula.decentralized_identity_evidence), alice_address, blockhash)) bobs_signer = Signer(priv_key_bob) task_signature = bytes(bobs_signer(specification)) metadata = bytes(ursula.stamp(task_signature)) cfrag = pre.reencrypt(kfrags[0], capsule, metadata=metadata) if corrupt_cfrag: cfrag.proof.bn_sig = CurveBN.gen_rand(capsule.params.curve) cfrag_signature = bytes(ursula.stamp(bytes(cfrag))) bob = Bob.from_public_keys(verifying_key=pub_key_bob) task = WorkOrder.Task(capsule, task_signature, cfrag, cfrag_signature) work_order = WorkOrder(bob, None, alice_address, [task], None, ursula, blockhash) evidence = IndisputableEvidence(task, work_order) return evidence
def gen_rand(cls, curve: Optional[Curve] = None) -> 'Point': """ Returns a Point object with a cryptographically secure EC_POINT based on the provided curve. """ curve = curve if curve is not None else default_curve() rand_point = openssl._get_new_EC_POINT(curve) rand_bn = CurveBN.gen_rand(curve).bignum with backend._tmp_bn_ctx() as bn_ctx: res = backend._lib.EC_POINT_mul(curve.ec_group, rand_point, backend._ffi.NULL, curve.generator, rand_bn, bn_ctx) backend.openssl_assert(res == 1) return cls(rand_point, curve)