def unlock(self, password: str) -> bool: if self.is_unlocked: return self.is_unlocked key_data = _read_keyfile(keypath=self.__root_keypath, deserializer=self._private_key_serializer) try: derived_key = derive_key_from_password(password=password.encode(), salt=key_data['master_salt']) except CryptoError: raise else: self.__derived_key_material = derived_key return self.is_unlocked
def unlock(self, password: str) -> bool: if self.is_unlocked: return self.is_unlocked key_data = _read_keyfile(keypath=self.__root_keypath, deserializer=self._private_key_serializer) self.log.info("Unlocking keyring.") try: derived_key = derive_key_from_password(password=password.encode(), salt=key_data['master_salt']) except CryptoError: self.log.info("Keyring unlock failed.") raise self.AuthenticationFailed else: self.__derived_key_material = derived_key self.log.info("Finished unlocking.") return self.is_unlocked
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