def test_generate_random_points(): for _ in range(10): point = Point.gen_rand() another_point = Point.gen_rand() assert isinstance(point, Point) assert isinstance(another_point, Point) assert point != another_point
def test_cannot_attach_cfrag_without_proof(): """ However, even when properly attaching keys, we can't attach the CFrag if it is unproven. """ params = default_params() capsule = Capsule(params, point_e=Point.gen_rand(), point_v=Point.gen_rand(), bn_sig=CurveBN.gen_rand()) cfrag = CapsuleFrag( point_e1=Point.gen_rand(), point_v1=Point.gen_rand(), kfrag_id=os.urandom(10), point_precursor=Point.gen_rand(), ) key_details = capsule.set_correctness_keys( UmbralPrivateKey.gen_key().get_pubkey(), UmbralPrivateKey.gen_key().get_pubkey(), UmbralPrivateKey.gen_key().get_pubkey()) delegating_details, receiving_details, verifying_details = key_details assert all((delegating_details, receiving_details, verifying_details)) with pytest.raises(cfrag.NoProofProvided): capsule.attach_cfrag(cfrag)
def test_cheating_ursula_sends_garbage(kfrags, prepared_capsule): capsule_alice = prepared_capsule cfrags = [] for i, kfrag in enumerate(kfrags): # Example of potential metadata to describe the re-encryption request metadata_i = "This is an example of metadata for re-encryption request #{}" metadata_i = metadata_i.format(i).encode() cfrag = pre.reencrypt(kfrag, capsule_alice, metadata=metadata_i) cfrags.append(cfrag) # Let's put random garbage in one of the cfrags cfrags[0]._point_e1 = Point.gen_rand() cfrags[0]._point_v1 = Point.gen_rand() # Of course, this CFrag is not valid ... assert not cfrags[0].verify_correctness(capsule_alice) # ... and trying to attach it raises an error. with pytest.raises(pre.UmbralCorrectnessError) as exception_info: capsule_alice.attach_cfrag(cfrags[0]) correctness_error = exception_info.value assert cfrags[0] in correctness_error.offending_cfrags assert len(correctness_error.offending_cfrags) == 1 # The response of cheating Ursula is in cfrags[0], # so the rest of CFrags should be correct: for cfrag_i in cfrags[1:]: assert cfrag_i.verify_correctness(capsule_alice) capsule_alice.attach_cfrag(cfrag_i)
def test_cheating_ursula_sends_garbage(N, M): priv_key_alice = keys.UmbralPrivateKey.gen_key() pub_key_alice = priv_key_alice.get_pubkey() # Bob priv_key_bob = keys.UmbralPrivateKey.gen_key() pub_key_bob = priv_key_bob.get_pubkey() sym_key, capsule_alice = pre._encapsulate(pub_key_alice.point_key) kfrags = pre.split_rekey(priv_key_alice, pub_key_bob, M, N) cfrags, challenges, metadata = [], [], [] for i, kfrag in enumerate(kfrags[:M]): cfrag = pre.reencrypt(kfrag, capsule_alice) # Example of potential metadata to describe the challenge request metadata_i = { 'ursula_id': i, 'timestamp': time.time(), 'capsule': bytes(capsule_alice), 'cfrag': bytes(cfrag) } metadata_i = str(metadata_i).encode() metadata.append(metadata_i) challenge = pre._challenge(kfrag, capsule_alice, cfrag, metadata_i) capsule_alice.attach_cfrag(cfrag) assert pre._check_challenge(capsule_alice, cfrag, challenge, pub_key_alice.point_key, pub_key_bob.point_key, metadata_i) cfrags.append(cfrag) challenges.append(challenge) # Let's put random garbage in one of the cfrags cfrags[0].point_eph_e1 = Point.gen_rand() cfrags[0].point_eph_v1 = Point.gen_rand() capsule_alice._reconstruct_shamirs_secret(pub_key_alice, priv_key_bob) # activate capsule with pytest.raises(pre.GenericUmbralError): sym_key2 = pre._decapsulate_reencrypted(pub_key_bob.point_key, priv_key_bob.bn_key, pub_key_alice.point_key, capsule_alice) assert not pre._check_challenge(capsule_alice, cfrags[0], challenges[0], pub_key_alice.point_key, pub_key_bob.point_key, metadata[0]) # The response of cheating Ursula is in capsules[0], # so the rest of challenges chould be correct: for i, challenge in enumerate(challenges[1:], 1): cfrag = cfrags[i] assert pre._check_challenge(capsule_alice, cfrag, challenge, pub_key_alice.point_key, pub_key_bob.point_key, metadata[i])
def test_bytes_serializers(point_bytes, nid, curve): point_with_nid = Point.from_bytes(point_bytes, curve=nid) # from nid assert isinstance(point_with_nid, Point) point_with_curve = Point.from_bytes(point_bytes, curve=curve) # from curve assert isinstance(point_with_curve, Point) assert point_with_nid == point_with_curve the_same_point_bytes = point_with_curve.to_bytes() assert point_bytes == the_same_point_bytes representations = ( point_bytes, # Compressed representation point_with_curve.to_bytes(is_compressed=False)) # Uncompressed for point_representation in representations: malformed_point_bytes = point_bytes + b'0x' with pytest.raises(ValueError): _ = Point.from_bytes(malformed_point_bytes) malformed_point_bytes = point_bytes[1:] with pytest.raises(ValueError): _ = Point.from_bytes(malformed_point_bytes) malformed_point_bytes = point_bytes[:-1] with pytest.raises(ValueError): _ = Point.from_bytes(malformed_point_bytes)
def test_cheating_ursula_sends_garbage(N, M): priv_key_alice = keys.UmbralPrivateKey.gen_key() pub_key_alice = priv_key_alice.get_pubkey() # Bob priv_key_bob = keys.UmbralPrivateKey.gen_key() pub_key_bob = priv_key_bob.get_pubkey() sym_key, capsule_alice = pre._encapsulate(pub_key_alice.point_key) kfrags = pre.split_rekey(priv_key_alice, pub_key_bob, M, N) cfrags, metadata = [], [] for i, kfrag in enumerate(kfrags[:M]): # Example of potential metadata to describe the re-encryption request metadata_i = "This is an example of metadata for re-encryption request #{}" metadata_i = metadata_i.format(i).encode() cfrag = pre.reencrypt(kfrag, capsule_alice, metadata=metadata_i) capsule_alice.attach_cfrag(cfrag) cfrags.append(cfrag) # Let's put random garbage in one of the cfrags cfrags[0]._point_e1 = Point.gen_rand() cfrags[0]._point_v1 = Point.gen_rand() capsule_alice._reconstruct_shamirs_secret(pub_key_alice, priv_key_bob) # activate capsule with pytest.raises(pre.GenericUmbralError): _unused_key = pre._decapsulate_reencrypted(pub_key_bob.point_key, priv_key_bob.bn_key, pub_key_alice.point_key, capsule_alice) assert not pre._verify_correctness( capsule_alice, cfrags[0], pub_key_alice.point_key, pub_key_bob.point_key, ) # The response of cheating Ursula is in cfrags[0], # so the rest of CFrags chould be correct: for cfrag_i, metadata_i in zip(cfrags[1:], metadata[1:]): assert pre._verify_correctness( capsule_alice, cfrag_i, pub_key_alice.point_key, pub_key_bob.point_key, ) # Alternatively, we can try to open the capsule directly. # We should get an exception with an attached list of incorrect cfrags with pytest.raises(pre.UmbralCorrectnessError) as exception_info: _ = pre._open_capsule(capsule_alice, priv_key_bob, pub_key_alice) correctness_error = exception_info.value assert cfrags[0] in correctness_error.offending_cfrags assert len(correctness_error.offending_cfrags) == 1
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() key_size = get_curve_keysize_bytes(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(key_size + 1), curve) v2 = Point.from_bytes(data.read(key_size + 1), curve) kfrag_commitment = Point.from_bytes(data.read(key_size + 1), curve) kfrag_pok = Point.from_bytes(data.read(key_size + 1), curve) kfrag_sig1 = CurveBN.from_bytes(data.read(key_size), curve) kfrag_sig2 = CurveBN.from_bytes(data.read(key_size), curve) sig = CurveBN.from_bytes(data.read(key_size), curve) metadata = data.read() or None return cls(e2, v2, kfrag_commitment, kfrag_pok, kfrag_sig1, kfrag_sig2, sig, metadata=metadata)
def raw_bytes_from_point(point: Point, only_y_coord=False) -> bytes: uncompressed_point_bytes = point.to_bytes(is_compressed=False) if only_y_coord: y_coord_start = (1 + Point.expected_bytes_length(is_compressed=False)) // 2 return uncompressed_point_bytes[y_coord_start:] else: return uncompressed_point_bytes[1:]
def test_cannot_set_different_keys(): """ Once a key is set on a Capsule, it can't be changed to a different key. """ params = default_params() capsule = Capsule(params, point_e=Point.gen_rand(), point_v=Point.gen_rand(), bn_sig=CurveBN.gen_rand()) capsule.set_correctness_keys( delegating=UmbralPrivateKey.gen_key().get_pubkey(), receiving=UmbralPrivateKey.gen_key().get_pubkey(), verifying=UmbralPrivateKey.gen_key().get_pubkey()) with pytest.raises(ValueError): capsule.set_correctness_keys( delegating=UmbralPrivateKey.gen_key().get_pubkey()) with pytest.raises(ValueError): capsule.set_correctness_keys( receiving=UmbralPrivateKey.gen_key().get_pubkey()) with pytest.raises(ValueError): capsule.set_correctness_keys( verifying=UmbralPrivateKey.gen_key().get_pubkey())
def test_affine(point_affine, nid, curve): point = Point.from_affine(point_affine, curve=nid) # from nid the_same_point = Point.from_affine(point_affine, curve=curve) # from curve instance assert point == the_same_point assert isinstance(point, Point) point_affine2 = point.to_affine() assert point_affine == point_affine2
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 test_bad_capsule_fails_reencryption(alices_keys): priv_key_alice, pub_key_alice = alices_keys k_frags, _unused_vkeys = umbral.split_rekey(priv_key_alice, pub_key_alice, 1, 2) bollocks_capsule = Capsule(point_eph_e=Point.gen_rand(), point_eph_v=Point.gen_rand(), bn_sig=BigNum.gen_rand()) with pytest.raises(Capsule.NotValid): umbral.reencrypt(k_frags[0], bollocks_capsule)
def test_cheating_ursula_sends_garbage(N, M): priv_key_alice = keys.UmbralPrivateKey.gen_key() pub_key_alice = priv_key_alice.get_pubkey() # Bob priv_key_bob = keys.UmbralPrivateKey.gen_key() pub_key_bob = priv_key_bob.get_pubkey() sym_key, capsule_alice = pre._encapsulate(pub_key_alice.point_key) k_frags = pre.split_rekey(priv_key_alice, pub_key_bob, M, N) c_frags, challenges = [], [] for k_frag in k_frags[0:M]: c_frag = pre.reencrypt(k_frag, capsule_alice) challenge = pre.challenge(k_frag, capsule_alice, c_frag) capsule_alice.attach_cfrag(c_frag) assert pre.check_challenge( capsule_alice, c_frag, challenge, pub_key_alice.point_key, pub_key_bob.point_key, ) c_frags.append(c_frag) challenges.append(challenge) # Let's put random garbage in one of the c_frags c_frags[0].point_eph_e1 = Point.gen_rand() c_frags[0].point_eph_v1 = Point.gen_rand() capsule_alice._reconstruct_shamirs_secret() # activate capsule with pytest.raises(pre.GenericUmbralError): sym_key2 = pre.decapsulate_reencrypted(pub_key_bob.point_key, priv_key_bob.bn_key, pub_key_alice.point_key, capsule_alice) assert sym_key2 != sym_key assert not pre.check_challenge(capsule_alice, c_frags[0], challenges[0], pub_key_alice.point_key, pub_key_bob.point_key) # The response of cheating Ursula is in capsules[0], # so the rest of challenges chould be correct: for (c_frag, ch) in zip(c_frags[1:], challenges[1:]): assert pre.check_challenge(capsule_alice, c_frag, ch, pub_key_alice.point_key, pub_key_bob.point_key)
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_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
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 from_bytes(cls, capsule_bytes: bytes, curve: ec.EllipticCurve = None): """ Instantiates a Capsule object from the serialized data. """ 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 len(capsule_bytes) == cls.expected_bytes_length(curve, activated=True): splitter = BytestringSplitter( (Point, point_size), # point_e (Point, point_size), # point_v (CurveBN, bn_size), # bn_sig (Point, point_size), # point_e_prime (Point, point_size), # point_v_prime (Point, point_size) # point_noninteractive ) else: splitter = BytestringSplitter( (Point, point_size), # point_e (Point, point_size), # point_v (CurveBN, bn_size) # bn_sig ) components = splitter(capsule_bytes) return cls(*components)
def from_bytes(cls, data: bytes, curve: Optional[Curve] = None) -> 'CapsuleFrag': """ Instantiates a CapsuleFrag object from the serialized data. """ curve = curve if curve is not None else default_curve() bn_size = CurveBN.expected_bytes_length(curve) point_size = Point.expected_bytes_length(curve) arguments = {'curve': curve} splitter = BytestringSplitter( (Point, point_size, arguments), # point_e1 (Point, point_size, arguments), # point_v1 bn_size, # kfrag_id (Point, point_size, arguments), # point_precursor ) components = splitter(data, return_remainder=True) proof = components.pop() or None components.append( CorrectnessProof.from_bytes(proof, curve) if proof else None) return cls(*components)
def test_serialize_point_at_infinity(): p = Point.gen_rand() point_at_infinity = p - p bytes_point_at_infinity = point_at_infinity.to_bytes() assert bytes_point_at_infinity == b'\x00'
def __init__(self, curve: ec.EllipticCurve): from umbral.point import Point, unsafe_hash_to_point from umbral.utils import get_curve_keysize_bytes self.curve = curve self.g = Point.get_generator_from_curve(self.curve) self.order = Point.get_order_from_curve(self.curve) g_bytes = self.g.to_bytes(is_compressed=True) self.CURVE_MINVAL_SHA512 = (1 << 512) % int(self.order) self.CURVE_KEY_SIZE_BYTES = get_curve_keysize_bytes(self.curve) parameters_seed = b'NuCypherKMS/UmbralParameters/' self.u = unsafe_hash_to_point(g_bytes, self, parameters_seed + b'u')
def from_bytes(data: bytes, curve: ec.EllipticCurve = None): """ Instantiates a CapsuleFrag object from the serialized data. """ curve = curve if curve is not None else default_curve() key_size = get_curve_keysize_bytes(curve) data = BytesIO(data) # BigNums are the keysize in bytes, Points are compressed and the # keysize + 1 bytes long. e1 = Point.from_bytes(data.read(key_size + 1), curve) v1 = Point.from_bytes(data.read(key_size + 1), curve) kfrag_id = BigNum.from_bytes(data.read(key_size), curve) eph_ni = Point.from_bytes(data.read(key_size + 1), curve) return CapsuleFrag(e1, v1, kfrag_id, eph_ni)
def test_generator_point(): """http://www.secg.org/SEC2-Ver-1.0.pdf Section 2.7.1""" g1 = Point.get_generator_from_curve() g_compressed = 0x0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798 g_uncompressed = 0x0479BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8 g_compressed = g_compressed.to_bytes(32 + 1, byteorder='big') g_uncompressed = g_uncompressed.to_bytes(64 + 1, byteorder='big') g2 = Point.from_bytes(g_compressed) assert g1 == g2 g3 = Point.from_bytes(g_uncompressed) assert g1 == g3 assert g2 == g3
def test_bytes_serializers(point_bytes, nid, curve): point_with_nid = Point.from_bytes(point_bytes, curve=nid) # from nid assert isinstance(point_with_nid, Point) point_with_curve = Point.from_bytes(point_bytes, curve=curve) # from curve assert isinstance(point_with_curve, Point) assert point_with_nid == point_with_curve the_same_point_bytes = point_with_curve.to_bytes(is_compressed=False) assert point_bytes == the_same_point_bytes malformed_point_bytes = point_bytes + b'0x' with pytest.raises(ValueError): _ = Point.from_bytes(malformed_point_bytes)
def test_ec_point_operations(testerchain, reencryption_validator): valid_point = Point.gen_rand() x, y = valid_point.to_affine() assert reencryption_validator.functions.is_on_curve(x, y).call() bad_y = y - 1 assert not reencryption_validator.functions.is_on_curve(x, bad_y).call() sign = 2 + (y % 2) assert reencryption_validator.functions.check_compressed_point(sign, x, y).call() bad_sign = 3 - (y % 2) assert not reencryption_validator.functions.check_compressed_point( bad_sign, x, y).call() P = valid_point scalar = CurveBN.gen_rand() Q = scalar * P qx, qy = Q.to_affine() assert reencryption_validator.functions.ecmulVerify( x, y, int(scalar), qx, qy).call() assert not reencryption_validator.functions.ecmulVerify( x, y, int(scalar), x, y).call()
def test_point_not_on_curve(): """ We want to be unable to create a Point that's not on the curve. When we try, we get cryptography.exceptions.InternalError - is that specifically because it isn't on the curve? It seems to be reliably raised in the event of the Point being off the curve. The OpenSSL docs don't explicitly say that they raise an error for this reason: https://www.openssl.org/docs/man1.1.0/crypto/EC_GFp_simple_method.html """ point_on_koblitz256_but_not_P256 = Point.from_bytes(b'\x03%\x98Dk\x88\xe2\x97\xab?\xabZ\xef\xd4' \ b'\x9e\xaa\xc6\xb3\xa4\xa3\x89\xb2\xd7b.\x8f\x16Ci_&\xe0\x7f', curve=SECP256K1) from cryptography.exceptions import InternalError with pytest.raises(InternalError): Point.from_bytes(point_on_koblitz256_but_not_P256.to_bytes(), curve=SECP256R1)
def from_bytes(cls, capsule_bytes: bytes, params: UmbralParameters): """ Instantiates a Capsule object from the serialized data. """ curve = params.curve bn_size = CurveBN.expected_bytes_length(curve) point_size = Point.expected_bytes_length(curve) capsule_bytes_length = len(capsule_bytes) if capsule_bytes_length == cls.expected_bytes_length(curve, activated=True): splitter = BytestringSplitter( (Point, point_size), # point_e (Point, point_size), # point_v (CurveBN, bn_size), # bn_sig (Point, point_size), # point_e_prime (Point, point_size), # point_v_prime (Point, point_size) # point_noninteractive ) elif capsule_bytes_length == cls.expected_bytes_length( curve, activated=False): splitter = BytestringSplitter( (Point, point_size), # point_e (Point, point_size), # point_v (CurveBN, bn_size) # bn_sig ) else: raise ValueError( "Byte string does not have a valid length for a Capsule") components = splitter(capsule_bytes) return cls(params, *components)
def from_bytes(cls, data: bytes, curve: Optional[Curve] = None) -> 'KFrag': """ Instantiate a KFrag object from the serialized data. """ curve = curve if curve is not None else default_curve() bn_size = CurveBN.expected_bytes_length(curve) point_size = Point.expected_bytes_length(curve) signature_size = Signature.expected_bytes_length(curve) arguments = {'curve': curve} splitter = BytestringSplitter( bn_size, # id (CurveBN, bn_size, arguments), # bn_key (Point, point_size, arguments), # point_commitment (Point, point_size, arguments), # point_precursor 1, # keys_in_signature (Signature, signature_size, arguments), # signature_for_proxy (Signature, signature_size, arguments), # signature_for_bob ) components = splitter(data) return cls(identifier=components[0], bn_key=components[1], point_commitment=components[2], point_precursor=components[3], keys_in_signature=components[4], signature_for_proxy=components[5], signature_for_bob=components[6])
def kdf(ecpoint: Point, key_length: int) -> bytes: data = ecpoint.to_bytes(is_compressed=True) return HKDF(algorithm=hashes.BLAKE2b(64), length=key_length, salt=None, info=None, backend=default_backend()).derive(data)
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)