From 9842b5d096a1f85ff27bc34df0dce853badf4f63 Mon Sep 17 00:00:00 2001 From: Kieran Prasch Date: Mon, 17 Sep 2018 14:10:48 -0700 Subject: [PATCH] Modify character classes to use runtime files and directories: certificates and metadata --- nucypher/blockchain/eth/actors.py | 2 +- nucypher/characters/base.py | 11 ++++++--- nucypher/characters/lawful.py | 22 ++++++++++++----- nucypher/crypto/api.py | 19 +++++++-------- nucypher/keystore/keypairs.py | 39 ++++++++++++++++--------------- nucypher/network/middleware.py | 6 ++--- 6 files changed, 55 insertions(+), 44 deletions(-) diff --git a/nucypher/blockchain/eth/actors.py b/nucypher/blockchain/eth/actors.py index f97737fa3..43ea8601d 100644 --- a/nucypher/blockchain/eth/actors.py +++ b/nucypher/blockchain/eth/actors.py @@ -45,7 +45,7 @@ class NucypherTokenActor: self.checksum_public_address = checksum_address if registry_filepath is not None: - EthereumContractRegistry.from_config(registry_filepath=registry_filepath) + EthereumContractRegistry(registry_filepath=registry_filepath) self.token_agent = token_agent if token_agent is not None else NucypherTokenAgent() self._transaction_cache = list() # track transactions transmitted diff --git a/nucypher/characters/base.py b/nucypher/characters/base.py index 6a08f5a07..008a3210f 100644 --- a/nucypher/characters/base.py +++ b/nucypher/characters/base.py @@ -304,8 +304,9 @@ class Character(Teacher): def __init__(self, is_me: bool = True, - config_root=DEFAULT_CONFIG_ROOT, network_middleware: RestMiddleware = None, + known_certificates_dir: str = None, + known_metadata_dir: str = None, crypto_power: CryptoPower = None, crypto_power_ups: List[CryptoPowerUp] = None, federated_only: bool = False, @@ -337,8 +338,9 @@ class Character(Teacher): """ super().__init__(*args, **kwargs) - self.config_root = config_root self.federated_only = federated_only # type: bool + self.known_certificates_dir = known_certificates_dir + self.known_metadata_dir = known_metadata_dir # # Power-ups and Powers @@ -487,9 +489,12 @@ class Character(Teacher): unresponsive_nodes = set() try: + + # FIXME response = self.network_middleware.get_nodes_via_rest(rest_url, nodes_i_need=self._node_ids_to_learn_about_immediately, - announce_nodes=announce_nodes) + announce_nodes=announce_nodes, + certificate_path=current_teacher.certificate_filepath) except requests.exceptions.ConnectionError: unresponsive_nodes.add(current_teacher) teacher_rest_info = current_teacher.rest_information()[0] diff --git a/nucypher/characters/lawful.py b/nucypher/characters/lawful.py index bc35bcf85..62eb44d83 100644 --- a/nucypher/characters/lawful.py +++ b/nucypher/characters/lawful.py @@ -405,7 +405,7 @@ class Ursula(Character, VerifiableNode, Miner): rest_host, rest_port, certificate=None, - certificate_dir=None, + certificate_filepath: str = None, db_name=None, is_me=True, interface_signature=None, @@ -413,6 +413,7 @@ class Ursula(Character, VerifiableNode, Miner): # Blockchain miner_agent=None, checksum_address: str = None, + registry_filepath: str = None, # Character abort_on_learning_error: bool = False, @@ -443,7 +444,8 @@ class Ursula(Character, VerifiableNode, Miner): Miner.__init__(self, is_me=is_me, miner_agent=miner_agent, - checksum_address=checksum_address) + checksum_address=checksum_address, + registry_filepath=registry_filepath) blockchain_power = BlockchainPower(blockchain=self.blockchain, account=self.checksum_public_address) self._crypto_power.consume_power_up(blockchain_power) @@ -489,26 +491,34 @@ class Ursula(Character, VerifiableNode, Miner): curve=tls_curve, host=rest_host, certificate=certificate, - certificate_dir=certificate_dir) + certificate_dir=self.known_certificates_dir) + tls_hosting_power = TLSHostingPower(rest_server=rest_server, keypair=tls_hosting_keypair) + else: # Unless the caller passed a crypto power, we'll make our own TLSHostingPower for this stranger. rest_server = ProxyRESTServer( rest_host=rest_host, rest_port=rest_port, ) - if certificate: - tls_hosting_power = TLSHostingPower(rest_server=rest_server, certificate=certificate) + if certificate or certificate_filepath: + tls_hosting_power = TLSHostingPower(rest_server=rest_server, + certificate_filepath=certificate_filepath, + certificate=certificate) else: tls_hosting_keypair = HostingKeypair( common_name=self.checksum_public_address, curve=tls_curve, host=rest_host, - certificate_dir=certificate_dir) + certificate_filepath=certificate_filepath, + certificate_dir=self.known_certificates_dir) + tls_hosting_power = TLSHostingPower(rest_server=rest_server, keypair=tls_hosting_keypair) + self._crypto_power.consume_power_up(tls_hosting_power) # Make this work for not me for certificate to work + else: self.log.info("Not adhering rest_server; we'll use the one on crypto_power..") diff --git a/nucypher/crypto/api.py b/nucypher/crypto/api.py index a96dee57f..57a3104e4 100644 --- a/nucypher/crypto/api.py +++ b/nucypher/crypto/api.py @@ -117,17 +117,13 @@ def ecdsa_verify( def _save_tls_certificate(certificate: Certificate, directory: str, common_name: str = None, - is_me: bool = False, force: bool = True, ) -> str: - if is_me is False and not common_name: - raise NucypherConfigurationError('A common name must be passed to save another node\'s certificate.') - - certificate_filepath = os.path.join(directory, '{}.pem'.format(common_name[:6])) + certificate_filepath = os.path.join(directory, '{}.pem'.format(common_name[2:8])) if force is False and os.path.isfile(certificate_filepath): - raise NucypherConfigurationError('A TLS certificate already exists at {}.'.format(certificate_filepath)) + raise FileExistsError('A TLS certificate already exists at {}.'.format(certificate_filepath)) with open(certificate_filepath, 'wb') as certificate_file: public_pem_bytes = certificate.public_bytes(Encoding.PEM) @@ -146,9 +142,10 @@ def load_tls_certificate(filepath): def generate_self_signed_certificate(common_name, curve, host, + certificate_dir, private_key=None, days_valid=365, - certificate_dir=None): + ): if not private_key: private_key = ec.generate_private_key(curve, default_backend()) @@ -169,10 +166,10 @@ def generate_self_signed_certificate(common_name, cert = cert.add_extension(x509.SubjectAlternativeName([x509.DNSName(host)]), critical=False) cert = cert.sign(private_key, hashes.SHA512(), default_backend()) - if certificate_dir: - tls_certificate_filepath = _save_tls_certificate(cert, directory=certificate_dir, common_name=common_name) - else: - tls_certificate_filepath = constants.CERTIFICATE_NOT_SAVED + # if certificate_dir: + tls_certificate_filepath = _save_tls_certificate(cert, directory=certificate_dir, common_name=common_name) + # else: + # tls_certificate_filepath = constants.CERTIFICATE_NOT_SAVED return cert, private_key, tls_certificate_filepath diff --git a/nucypher/keystore/keypairs.py b/nucypher/keystore/keypairs.py index c61cb184a..50c9b1043 100644 --- a/nucypher/keystore/keypairs.py +++ b/nucypher/keystore/keypairs.py @@ -13,7 +13,7 @@ from umbral.keys import UmbralPrivateKey, UmbralPublicKey from umbral.signing import Signature, Signer from nucypher.crypto import api as API -from nucypher.crypto.api import generate_self_signed_certificate, _save_tls_certificate +from nucypher.crypto.api import generate_self_signed_certificate, _save_tls_certificate, load_tls_certificate from nucypher.crypto.kits import MessageKit from nucypher.crypto.signing import SignatureStamp, StrangerStamp @@ -132,46 +132,47 @@ class HostingKeypair(Keypair): common_name=None, host=None, private_key: Union[UmbralPrivateKey, UmbralPublicKey] = None, - certificate=None, curve=None, - generate_keys_if_needed=True, + certificate=None, + certificate_filepath: str = None, certificate_dir=None, ) -> None: self.curve = curve or self._DEFAULT_CURVE - self.certificate_filepath = None if private_key: super().__init__(private_key=private_key) elif certificate: - self.certificate = certificate super().__init__(public_key=certificate.public_key()) - elif generate_keys_if_needed: + elif certificate_filepath: + certificate = load_tls_certificate(filepath=certificate_filepath) + + elif generate_certificate: + if not all((common_name, host)): message = "If you don't supply the certificate, one will be generated for you." \ "But for that, you need to pass both host and common_name.." raise TypeError(message) - self.certificate, private_key, self.certificate_filepath = generate_self_signed_certificate(common_name=common_name, - private_key=private_key, - curve=self.curve, - host=host, - ) + + certificate, private_key, certificate_filepath = generate_self_signed_certificate(common_name=common_name, + private_key=private_key, + curve=self.curve, + host=host, + certificate_dir=certificate_dir) + super().__init__(private_key=private_key) else: raise TypeError("You didn't provide a cert, but also told us not to generate keys. Not sure what to do.") - if certificate_dir: - self.certificate_filepath = _save_tls_certificate(self.certificate, - directory=certificate_dir, - common_name=common_name, - is_me=False, - force=False, - ) + self.certificate = certificate + self.certificate_filepath = certificate_filepath + self.certificate_dir = certificate_dir def generate_self_signed_cert(self, common_name): cryptography_key = self._privkey.to_cryptography_privkey() - return generate_self_signed_certificate(common_name, default_curve(), cryptography_key) + return generate_self_signed_certificate(common_name, default_curve(), + cryptography_key, certificate_dir=self.certificate_dir) def get_deployer(self, rest_app, port): return HendrixDeployTLS("start", diff --git a/nucypher/network/middleware.py b/nucypher/network/middleware.py index 838957431..61c26eeb2 100644 --- a/nucypher/network/middleware.py +++ b/nucypher/network/middleware.py @@ -9,20 +9,18 @@ from umbral.fragments import CapsuleFrag class RestMiddleware: def consider_arrangement(self, arrangement, certificate_path): - certificate = load_tls_certificate(filepath=certificate_path) node = arrangement.ursula port = node.rest_interface.port address = node.rest_interface.host - response = requests.post("https://{}:{}/consider_arrangement".format(address, port), bytes(arrangement), verify=certificate) + response = requests.post("https://{}:{}/consider_arrangement".format(address, port), bytes(arrangement), verify=certificate_path) if not response.status_code == 200: raise RuntimeError("Bad response: {}".format(response.content)) return response def enact_policy(self, ursula, id, payload, certificate_path): - certificate = load_tls_certificate(filepath=certificate_path) port = ursula.rest_interface.port address = ursula.rest_interface.host - response = requests.post('https://{}:{}/kFrag/{}'.format(address, port, id.hex()), payload, verify=certificate) + response = requests.post('https://{}:{}/kFrag/{}'.format(address, port, id.hex()), payload, verify=certificate_path) if not response.status_code == 200: raise RuntimeError("Bad response: {}".format(response.content)) return True, ursula.stamp.as_umbral_pubkey()