def test_keying_material_serialization(): umbral_keying_material = UmbralKeyingMaterial() encoded_key = umbral_keying_material.to_bytes() decoded_key = UmbralKeyingMaterial.from_bytes(encoded_key) assert umbral_keying_material.keying_material == decoded_key.keying_material
def __init__(self, keying_material: Optional[bytes] = None, password: Optional[bytes] = None) -> None: if keying_material is None: self.__umbral_keying_material = UmbralKeyingMaterial() else: self.__umbral_keying_material = UmbralKeyingMaterial.from_bytes(key_bytes=keying_material, password=password)
def test_keying_material_serialization(): umbral_keying_material = UmbralKeyingMaterial() encoded_keying_material = umbral_keying_material.to_bytes() decoded_keying_material = UmbralKeyingMaterial.from_bytes(encoded_keying_material) label = os.urandom(32) privkey_bytes = umbral_keying_material.derive_privkey_by_label(label).to_bytes() assert privkey_bytes == decoded_keying_material.derive_privkey_by_label(label).to_bytes()
def test_keying_material_serialization_with_encryption(): umbral_keying_material = UmbralKeyingMaterial() insecure_cost = 15 # This is deliberately insecure, just to make the tests faster encoded_key = umbral_keying_material.to_bytes(password=b'test', _scrypt_cost=insecure_cost) decoded_key = UmbralKeyingMaterial.from_bytes(encoded_key, password=b'test', _scrypt_cost=insecure_cost) assert umbral_keying_material.keying_material == decoded_key.keying_material
def test_keying_material_serialization_with_encryption(): umbral_keying_material = UmbralKeyingMaterial() insecure_cost = 15 # This is deliberately insecure, just to make the tests faster encoded_keying_material = umbral_keying_material.to_bytes(password=b'test', _scrypt_cost=insecure_cost) decoded_keying_material = UmbralKeyingMaterial.from_bytes(encoded_keying_material, password=b'test', _scrypt_cost=insecure_cost) label = os.urandom(32) privkey_bytes = umbral_keying_material.derive_privkey_by_label(label).to_bytes() assert privkey_bytes == decoded_keying_material.derive_privkey_by_label(label).to_bytes()
def create_policy(self, label: bytes, alice_privkey: UmbralPrivateKey, bob_pubkey: UmbralPublicKey, policy_expiration, m: int, n: int): """ Create a Policy with Alice granting Bob access to `label` DataSource :param label: A label to represent the policies data :param alice_privkey: Alice's private key :param bob_pubkey: Bob's public key :param policy_expiration: Datetime of policy expiration duration :param m: Minimum number of KFrags needed to rebuild ciphertext :param n: Total number of rekey shares to generate :return: The policy granted to Bob """ # This is not how this should be implemented, but I am still figuring out # the keying material and why it is randomly generated when a character is # initialized, instead of being derived from the keys like the other powers # or explained how it should be stored. d = DelegatingPower() d.umbral_keying_material = UmbralKeyingMaterial.from_bytes( alice_privkey.to_bytes() + alice_privkey.get_pubkey().to_bytes()) # Initialize Alice ALICE = Alice( crypto_power_ups=[ SigningPower(keypair=SigningKeypair(alice_privkey)), EncryptingPower(keypair=EncryptingKeypair(alice_privkey)), # DelegatingPower d ], network_middleware=RestMiddleware(), known_nodes=(self.ursula, ), federated_only=True, always_be_learning=True) # Initialize Bob BOB = Bob(crypto_power_ups=[ SigningPower(pubkey=bob_pubkey), EncryptingPower(pubkey=bob_pubkey) ], known_nodes=(self.ursula, ), federated_only=True, always_be_learning=True) # Alice grants a policy for Bob policy = ALICE.grant(BOB, label, m=m, n=n, expiration=policy_expiration) return policy
def test_derive_key_from_label(): umbral_keying_material = UmbralKeyingMaterial() label = b"my_healthcare_information" priv_key1 = umbral_keying_material.derive_privkey_by_label(label) assert type(priv_key1) == UmbralPrivateKey pub_key1 = priv_key1.get_pubkey() assert type(pub_key1) == UmbralPublicKey # Check that key derivation is reproducible priv_key2 = umbral_keying_material.derive_privkey_by_label(label) pub_key2 = priv_key2.get_pubkey() assert priv_key1.bn_key == priv_key2.bn_key assert pub_key1 == pub_key2 # A salt can be used too, but of course it affects the derived key salt = b"optional, randomly generated salt" priv_key3 = umbral_keying_material.derive_privkey_by_label(label, salt=salt) assert priv_key3.bn_key != priv_key1.bn_key # Different labels on the same master secret create different keys label = b"my_tax_information" priv_key4 = umbral_keying_material.derive_privkey_by_label(label) pub_key4 = priv_key4.get_pubkey() assert priv_key1.bn_key != priv_key4.bn_key
class DelegatingPower(DerivedKeyBasedPower): def __init__(self, keying_material: Optional[bytes] = None, password: Optional[bytes] = None) -> None: if keying_material is None: self.__umbral_keying_material = UmbralKeyingMaterial() else: self.__umbral_keying_material = UmbralKeyingMaterial.from_bytes(key_bytes=keying_material, password=password) def _get_privkey_from_label(self, label): return self.__umbral_keying_material.derive_privkey_by_label(label) def get_pubkey_from_label(self, label): return self._get_privkey_from_label(label).get_pubkey() def generate_kfrags(self, bob_pubkey_enc, signer, label: bytes, m: int, n: int ) -> Tuple[UmbralPublicKey, List]: """ Generates re-encryption key frags ("KFrags") and returns them. These KFrags can be used by Ursula to re-encrypt a Capsule for Bob so that he can activate the Capsule. :param bob_pubkey_enc: Bob's public key :param m: Minimum number of KFrags needed to rebuild ciphertext :param n: Total number of KFrags to generate """ # TODO: salt? #265 __private_key = self._get_privkey_from_label(label) kfrags = pre.generate_kfrags(delegating_privkey=__private_key, receiving_pubkey=bob_pubkey_enc, threshold=m, N=n, signer=signer, sign_delegating_key=False, sign_receiving_key=False, ) return __private_key.get_pubkey(), kfrags def get_decrypting_power_from_label(self, label): label_privkey = self._get_privkey_from_label(label) label_keypair = keypairs.DecryptingKeypair(private_key=label_privkey) decrypting_power = DecryptingPower(keypair=label_keypair) return decrypting_power
class DelegatingPower(DerivedKeyBasedPower): def __init__(self): self.umbral_keying_material = UmbralKeyingMaterial() def generate_kfrags(self, bob_pubkey_enc, signer, label, m, n) -> Union[UmbralPublicKey, List]: """ Generates re-encryption key frags ("KFrags") and returns them. These KFrags can be used by Ursula to re-encrypt a Capsule for Bob so that he can activate the Capsule. :param bob_pubkey_enc: Bob's public key :param m: Minimum number of KFrags needed to rebuild ciphertext :param n: Total number of rekey shares to generate """ # TODO: salt? #265 __private_key = self.umbral_keying_material.derive_privkey_by_label(label) kfrags = pre.split_rekey(__private_key, signer, bob_pubkey_enc, m, n) return __private_key.get_pubkey(), kfrags
def generate( cls, passphrase: str, encrypting: bool = True, wallet: bool = True, tls: bool = True, host: str = None, curve: EllipticCurve = None, keyring_root: str = None, ) -> 'NucypherKeyring': """ Generates new encrypting, signing, and wallet keys encrypted with the passphrase, respectively saving keyfiles on the local filesystem from *default* paths, returning the corresponding Keyring instance. """ failures = cls.validate_passphrase(passphrase) if failures: raise cls.InvalidPassphrase( ", ".join(failures) ) # TODO: Ensure this scope is seperable from the scope containing the passphrase if not any((wallet, encrypting, tls)): raise ValueError( 'Either "encrypting", "wallet", or "tls" must be True ' 'to generate new keys, or set "no_keys" to True to skip generation.' ) _base_filepaths = cls._generate_base_filepaths( keyring_root=keyring_root) _public_key_dir = _base_filepaths['public_key_dir'] _private_key_dir = _base_filepaths['private_key_dir'] # Create the key directories with default paths. Raises OSError if dirs exist # if exists_ok and not os.path.isdir(_public_key_dir): os.mkdir(_public_key_dir, mode=0o744) # public dir # if exists_ok and not os.path.isdir(_private_key_dir): os.mkdir(_private_key_dir, mode=0o700) # private dir # # Generate New Keypairs # keyring_args = dict() if wallet is True: new_address, new_wallet = _generate_wallet(passphrase) new_wallet_path = os.path.join( _private_key_dir, 'wallet-{}.json'.format(new_address)) saved_wallet_path = _write_private_keyfile(new_wallet_path, json.dumps(new_wallet), serializer=None) keyring_args.update(wallet_path=saved_wallet_path) account = new_address if encrypting is True: signing_private_key, signing_public_key = _generate_signing_keys() if not wallet: uncompressed_bytes = signing_public_key.to_bytes( is_compressed=False) without_prefix = uncompressed_bytes[1:] verifying_key_as_eth_key = EthKeyAPI.PublicKey(without_prefix) account = verifying_key_as_eth_key.to_checksum_address() __key_filepaths = cls._generate_key_filepaths( account=account, private_key_dir=_private_key_dir, public_key_dir=_public_key_dir) if encrypting is True: encrypting_private_key, encrypting_public_key = _generate_encryption_keys( ) delegating_keying_material = UmbralKeyingMaterial().to_bytes() # Derive Wrapping Keys passphrase_salt, encrypting_salt, signing_salt, delegating_salt = ( os.urandom(32) for _ in range(4)) derived_key_material = _derive_key_material_from_passphrase( salt=passphrase_salt, passphrase=passphrase) encrypting_wrap_key = _derive_wrapping_key_from_key_material( salt=encrypting_salt, key_material=derived_key_material) signature_wrap_key = _derive_wrapping_key_from_key_material( salt=signing_salt, key_material=derived_key_material) delegating_wrap_key = _derive_wrapping_key_from_key_material( salt=delegating_salt, key_material=derived_key_material) # TODO: Deprecate _encrypt_umbral_key with new pyumbral release # Encapsulate Private Keys encrypting_key_data = _encrypt_umbral_key( umbral_key=encrypting_private_key, wrapping_key=encrypting_wrap_key) signing_key_data = _encrypt_umbral_key( umbral_key=signing_private_key, wrapping_key=signature_wrap_key) delegating_key_data = bytes( SecretBox(delegating_wrap_key).encrypt( delegating_keying_material)) # Assemble Private Keys encrypting_key_metadata = _assemble_key_data( key_data=encrypting_key_data, master_salt=passphrase_salt, wrap_salt=encrypting_salt) signing_key_metadata = _assemble_key_data( key_data=signing_key_data, master_salt=passphrase_salt, wrap_salt=signing_salt) delegating_key_metadata = _assemble_key_data( key_data=delegating_key_data, master_salt=passphrase_salt, wrap_salt=delegating_salt) # Write Private Keys rootkey_path = _write_private_keyfile( keypath=__key_filepaths['root'], key_data=encrypting_key_metadata, serializer=cls._private_key_serializer) sigkey_path = _write_private_keyfile( keypath=__key_filepaths['signing'], key_data=signing_key_metadata, serializer=cls._private_key_serializer) delegating_key_path = _write_private_keyfile( keypath=__key_filepaths['delegating'], key_data=delegating_key_metadata, serializer=cls._private_key_serializer) # Write Public Keys root_keypath = _write_public_keyfile( __key_filepaths['root_pub'], encrypting_public_key.to_bytes()) signing_keypath = _write_public_keyfile( __key_filepaths['signing_pub'], signing_public_key.to_bytes()) # Commit keyring_args.update( keyring_root=keyring_root or cls.__default_keyring_root, root_key_path=rootkey_path, pub_root_key_path=root_keypath, signing_key_path=sigkey_path, pub_signing_key_path=signing_keypath, delegating_key_path=delegating_key_path, ) if tls is True: if not all((host, curve)): raise ValueError( "Host and curve are required to make a new keyring TLS certificate" ) private_key, cert = _generate_tls_keys(host, curve) def __serialize_pem(pk): return pk.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.TraditionalOpenSSL, encryption_algorithm=serialization.BestAvailableEncryption( password=derived_key_material)) tls_key_path = _write_private_keyfile( keypath=__key_filepaths['tls'], key_data=__serialize_pem(pk=private_key), serializer=None) certificate_filepath = _write_tls_certificate( full_filepath=__key_filepaths['tls_certificate'], certificate=cert) keyring_args.update(tls_certificate_path=certificate_filepath, tls_key_path=tls_key_path) keyring_instance = cls(account=account, **keyring_args) return keyring_instance
def __init__(self): self.umbral_keying_material = UmbralKeyingMaterial()
def generate( cls, password: str, encrypting: bool, wallet: bool, rest: bool, host: str = None, curve: EllipticCurve = None, keyring_root: str = None, ) -> 'NucypherKeyring': """ Generates new encrypting, signing, and wallet keys encrypted with the password, respectively saving keyfiles on the local filesystem from *default* paths, returning the corresponding Keyring instance. """ failures = cls.validate_password(password) if failures: raise cls.AuthenticationFailed( ", ".join(failures) ) # TODO: Ensure this scope is seperable from the scope containing the password if not any((wallet, encrypting, rest)): raise ValueError( 'Either "encrypting", "wallet", or "tls" must be True ' 'to generate new keys, or set "no_keys" to True to skip generation.' ) if curve is None: curve = cls.__DEFAULT_TLS_CURVE _base_filepaths = cls._generate_base_filepaths( keyring_root=keyring_root) _public_key_dir = _base_filepaths['public_key_dir'] _private_key_dir = _base_filepaths['private_key_dir'] # Write to disk os.mkdir(_public_key_dir, mode=0o744) # public dir os.mkdir(_private_key_dir, mode=0o700) # private dir # # Generate New Keypairs # keyring_args = dict() if wallet is True: new_address, new_wallet = _generate_wallet(password) new_wallet_path = os.path.join( _private_key_dir, 'wallet-{}.json'.format(new_address)) with open(new_wallet_path, 'w') as wallet: # TODO: is this pub or private? wallet.write(json.dumps(new_wallet)) keyring_args.update(wallet_path=new_wallet_path) if encrypting is True: signing_private_key, signing_public_key = _generate_signing_keys() if not wallet: uncompressed_bytes = signing_public_key.to_bytes( is_compressed=False) without_prefix = uncompressed_bytes[1:] verifying_key_as_eth_key = EthKeyAPI.PublicKey(without_prefix) new_address = verifying_key_as_eth_key.to_checksum_address() __key_filepaths = cls._generate_key_filepaths( account=new_address, private_key_dir=_private_key_dir, public_key_dir=_public_key_dir) if encrypting is True: encrypting_private_key, encrypting_public_key = _generate_encryption_keys( ) delegating_keying_material = UmbralKeyingMaterial().to_bytes() # Derive Wrapping Keys password_salt, encrypting_salt, signing_salt, delegating_salt = ( os.urandom(32) for _ in range(4)) cls.log.info("About to derive key from password.") derived_key_material = derive_key_from_password( salt=password_salt, password=password.encode()) encrypting_wrap_key = _derive_wrapping_key_from_key_material( salt=encrypting_salt, key_material=derived_key_material) signature_wrap_key = _derive_wrapping_key_from_key_material( salt=signing_salt, key_material=derived_key_material) delegating_wrap_key = _derive_wrapping_key_from_key_material( salt=delegating_salt, key_material=derived_key_material) # Encapsulate Private Keys encrypting_key_data = encrypting_private_key.to_bytes( wrapping_key=encrypting_wrap_key) signing_key_data = signing_private_key.to_bytes( wrapping_key=signature_wrap_key) delegating_key_data = bytes( SecretBox(delegating_wrap_key).encrypt( delegating_keying_material)) # Assemble Private Keys encrypting_key_metadata = _assemble_key_data( key_data=encrypting_key_data, master_salt=password_salt, wrap_salt=encrypting_salt) signing_key_metadata = _assemble_key_data( key_data=signing_key_data, master_salt=password_salt, wrap_salt=signing_salt) delegating_key_metadata = _assemble_key_data( key_data=delegating_key_data, master_salt=password_salt, wrap_salt=delegating_salt) # Write Private Keys rootkey_path = _write_private_keyfile( keypath=__key_filepaths['root'], key_data=encrypting_key_metadata, serializer=cls._private_key_serializer) sigkey_path = _write_private_keyfile( keypath=__key_filepaths['signing'], key_data=signing_key_metadata, serializer=cls._private_key_serializer) delegating_key_path = _write_private_keyfile( keypath=__key_filepaths['delegating'], key_data=delegating_key_metadata, serializer=cls._private_key_serializer) # Write Public Keys root_keypath = _write_public_keyfile( __key_filepaths['root_pub'], encrypting_public_key.to_bytes()) signing_keypath = _write_public_keyfile( __key_filepaths['signing_pub'], signing_public_key.to_bytes()) # Commit keyring_args.update( keyring_root=keyring_root or cls.__default_keyring_root, root_key_path=rootkey_path, pub_root_key_path=root_keypath, signing_key_path=sigkey_path, pub_signing_key_path=signing_keypath, delegating_key_path=delegating_key_path, ) if rest is True: if not all( (host, curve, new_address) ): # TODO: Do we want to allow showing up with an old wallet and generating a new cert? Probably. raise ValueError( "host, checksum_address and curve are required to make a new keyring TLS certificate. Got {}, {}" .format(host, curve)) private_key, cert = _generate_tls_keys( host=host, checksum_address=new_address, curve=curve) def __serialize_pem(pk): return pk.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.TraditionalOpenSSL, encryption_algorithm=serialization.BestAvailableEncryption( password=derived_key_material)) tls_key_path = _write_private_keyfile( keypath=__key_filepaths['tls'], key_data=__serialize_pem(pk=private_key), serializer=None) certificate_filepath = _write_tls_certificate( full_filepath=__key_filepaths['tls_certificate'], certificate=cert) keyring_args.update(tls_certificate_path=certificate_filepath, tls_key_path=tls_key_path) keyring_instance = cls(account=new_address, **keyring_args) return keyring_instance
def generate(cls, checksum_address: str, password: str, encrypting: bool = True, rest: bool = False, host: str = None, curve: EllipticCurve = None, keyring_root: str = None, force: bool = False, ) -> 'NucypherKeyring': """ Generates new encrypting, signing, and wallet keys encrypted with the password, respectively saving keyfiles on the local filesystem from *default* paths, returning the corresponding Keyring instance. """ keyring_root = keyring_root or cls._default_keyring_root failures = cls.validate_password(password) if failures: raise cls.AuthenticationFailed(", ".join(failures)) # TODO: Ensure this scope is seperable from the scope containing the password if not any((encrypting, rest)): raise ValueError('Either "encrypting", "wallet", or "tls" must be True ' 'to generate new keys, or set "no_keys" to True to skip generation.') if curve is None: curve = cls.__DEFAULT_TLS_CURVE _base_filepaths = cls._generate_base_filepaths(keyring_root=keyring_root) _public_key_dir = _base_filepaths['public_key_dir'] _private_key_dir = _base_filepaths['private_key_dir'] # # Generate New Keypairs # keyring_args = dict() if checksum_address is not FEDERATED_ADDRESS: # Addresses read from some node keyrings (clients) are *not* returned in checksum format. checksum_address = to_checksum_address(checksum_address) if encrypting is True: signing_private_key, signing_public_key = _generate_signing_keys() if checksum_address is FEDERATED_ADDRESS: uncompressed_bytes = signing_public_key.to_bytes(is_compressed=False) without_prefix = uncompressed_bytes[1:] verifying_key_as_eth_key = EthKeyAPI.PublicKey(without_prefix) checksum_address = verifying_key_as_eth_key.to_checksum_address() else: # TODO: Consider a "Repair" mode here # signing_private_key, signing_public_key = ... pass if not checksum_address: raise ValueError("Checksum address must be provided for non-federated keyring generation") __key_filepaths = cls._generate_key_filepaths(account=checksum_address, private_key_dir=_private_key_dir, public_key_dir=_public_key_dir) if encrypting is True: encrypting_private_key, encrypting_public_key = _generate_encryption_keys() delegating_keying_material = UmbralKeyingMaterial().to_bytes() # Derive Wrapping Keys password_salt, encrypting_salt, signing_salt, delegating_salt = (os.urandom(32) for _ in range(4)) cls.log.info("About to derive key from password.") derived_key_material = derive_key_from_password(salt=password_salt, password=password.encode()) encrypting_wrap_key = _derive_wrapping_key_from_key_material(salt=encrypting_salt, key_material=derived_key_material) signature_wrap_key = _derive_wrapping_key_from_key_material(salt=signing_salt, key_material=derived_key_material) delegating_wrap_key = _derive_wrapping_key_from_key_material(salt=delegating_salt, key_material=derived_key_material) # Encapsulate Private Keys encrypting_key_data = encrypting_private_key.to_bytes(wrapping_key=encrypting_wrap_key) signing_key_data = signing_private_key.to_bytes(wrapping_key=signature_wrap_key) delegating_key_data = bytes(SecretBox(delegating_wrap_key).encrypt(delegating_keying_material)) # Assemble Private Keys encrypting_key_metadata = _assemble_key_data(key_data=encrypting_key_data, master_salt=password_salt, wrap_salt=encrypting_salt) signing_key_metadata = _assemble_key_data(key_data=signing_key_data, master_salt=password_salt, wrap_salt=signing_salt) delegating_key_metadata = _assemble_key_data(key_data=delegating_key_data, master_salt=password_salt, wrap_salt=delegating_salt) # # Write Keys # # Create base paths if the do not exist. os.makedirs(abspath(keyring_root), exist_ok=True, mode=0o700) if not os.path.isdir(_public_key_dir): os.mkdir(_public_key_dir, mode=0o744) # public dir if not os.path.isdir(_private_key_dir): os.mkdir(_private_key_dir, mode=0o700) # private dir try: rootkey_path = _write_private_keyfile(keypath=__key_filepaths['root'], key_data=encrypting_key_metadata, serializer=cls._private_key_serializer) sigkey_path = _write_private_keyfile(keypath=__key_filepaths['signing'], key_data=signing_key_metadata, serializer=cls._private_key_serializer) delegating_key_path = _write_private_keyfile(keypath=__key_filepaths['delegating'], key_data=delegating_key_metadata, serializer=cls._private_key_serializer) # Write Public Keys root_keypath = _write_public_keyfile(__key_filepaths['root_pub'], encrypting_public_key.to_bytes()) signing_keypath = _write_public_keyfile(__key_filepaths['signing_pub'], signing_public_key.to_bytes()) except (PrivateKeyExistsError, FileExistsError): if not force: raise ExistingKeyringError(f"There is an existing keyring for address '{checksum_address}'") else: # Commit keyring_args.update( keyring_root=keyring_root, root_key_path=rootkey_path, pub_root_key_path=root_keypath, signing_key_path=sigkey_path, pub_signing_key_path=signing_keypath, delegating_key_path=delegating_key_path, ) if rest is True: if not all((host, curve, checksum_address)): # TODO: Do we want to allow showing up with an old wallet and generating a new cert? Probably. raise ValueError("host, checksum_address and curve are required to make a new keyring TLS certificate. Got {}, {}".format(host, curve)) private_key, cert = _generate_tls_keys(host=host, checksum_address=checksum_address, curve=curve) def __serialize_pem(pk): return pk.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.TraditionalOpenSSL, encryption_algorithm=serialization.BestAvailableEncryption(password=derived_key_material) ) tls_key_path = _write_private_keyfile(keypath=__key_filepaths['tls'], key_data=__serialize_pem(pk=private_key), serializer=None) certificate_filepath = _write_tls_certificate(full_filepath=__key_filepaths['tls_certificate'], certificate=cert) keyring_args.update(tls_certificate_path=certificate_filepath, tls_key_path=tls_key_path) keyring_instance = cls(account=checksum_address, **keyring_args) return keyring_instance