def prompt_import_ed25519_privatekey_from_file(filepath): """Tries to load an Ed25519 private key without password. If a CryptoError occurs, prompts the user for a password and tries to load the key again. """ password = None try: import_ed25519_privatekey_from_file(filepath, password) except securesystemslib.exceptions.CryptoError: password = prompt_password() return import_ed25519_privatekey_from_file(filepath, password)
def test_create_and_import_encrypted_ed25519_no_password(self): """Try import encrypted ed25519 key without or wrong pw, raises exception. """ name = "key8" password = "******" generate_and_write_ed25519_keypair(name, password) with self.assertRaises(securesystemslib.exceptions.CryptoError): import_ed25519_privatekey_from_file(name) with self.assertRaises(securesystemslib.exceptions.CryptoError): import_ed25519_privatekey_from_file(name, "wrong-password")
def setUpClass(cls): # Create a temporary directory to store the repository, metadata, and # target files. 'temporary_directory' must be deleted in # TearDownClass() so that temporary files are always removed, even when # exceptions occur. cls.temporary_directory = tempfile.mkdtemp(dir=os.getcwd()) test_repo_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), 'repository_data') cls.repo_dir = os.path.join(cls.temporary_directory, 'repository') shutil.copytree( os.path.join(test_repo_data, 'repository'), cls.repo_dir) cls.keystore_dir = os.path.join(cls.temporary_directory, 'keystore') shutil.copytree( os.path.join(test_repo_data, 'keystore'), cls.keystore_dir) # Load keys into memory cls.keystore = {} for role in ['delegation', 'snapshot', 'targets', 'timestamp']: cls.keystore[role] = { 'private': import_ed25519_privatekey_from_file( os.path.join(cls.keystore_dir, role + '_key'), password="******"), 'public': import_ed25519_publickey_from_file( os.path.join(cls.keystore_dir, role + '_key.pub')) }
def test_generate_and_write_ed25519_keypair(self): # Test normal case. temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory) test_keypath = os.path.join(temporary_directory, 'ed25519_key') interface.generate_and_write_ed25519_keypair(test_keypath, password='******') self.assertTrue(os.path.exists(test_keypath)) self.assertTrue(os.path.exists(test_keypath + '.pub')) # Ensure the generated key files are importable. imported_pubkey = \ interface.import_ed25519_publickey_from_file(test_keypath + '.pub') self.assertTrue(securesystemslib.formats.ED25519KEY_SCHEMA.matches(imported_pubkey)) imported_privkey = \ interface.import_ed25519_privatekey_from_file(test_keypath, 'pw') self.assertTrue(securesystemslib.formats.ED25519KEY_SCHEMA.matches(imported_privkey)) # Test improperly formatted arguments. self.assertRaises(securesystemslib.exceptions.FormatError, interface.generate_and_write_ed25519_keypair, 3, password='******') self.assertRaises(securesystemslib.exceptions.FormatError, interface.generate_and_write_rsa_keypair, test_keypath, password=3)
def setUpClass(cls) -> None: # Create a temporary directory to store the repository, metadata, and # target files. 'temporary_directory' must be deleted in # TearDownClass() so that temporary files are always removed, even when # exceptions occur. cls.temporary_directory = tempfile.mkdtemp(dir=os.getcwd()) test_repo_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "repository_data") cls.repo_dir = os.path.join(cls.temporary_directory, "repository") shutil.copytree(os.path.join(test_repo_data, "repository"), cls.repo_dir) cls.keystore_dir = os.path.join(cls.temporary_directory, "keystore") shutil.copytree(os.path.join(test_repo_data, "keystore"), cls.keystore_dir) # Load keys into memory cls.keystore = {} for role in [ "delegation", Snapshot.type, Targets.type, Timestamp.type ]: cls.keystore[role] = import_ed25519_privatekey_from_file( os.path.join(cls.keystore_dir, role + "_key"), password="******", )
def setUpClass(cls): cls.repo_dir = os.path.join(os.getcwd(), 'repository_data', 'repository', 'metadata') cls.metadata = {} for md in [ "root", "timestamp", "snapshot", "targets", "role1", "role2" ]: with open(os.path.join(cls.repo_dir, f"{md}.json"), "rb") as f: cls.metadata[md] = f.read() keystore_dir = os.path.join(os.getcwd(), 'repository_data', 'keystore') cls.keystore = {} root_key_dict = import_rsa_privatekey_from_file(os.path.join( keystore_dir, "root" + '_key'), password="******") cls.keystore["root"] = SSlibSigner(root_key_dict) for role in ["delegation", "snapshot", "targets", "timestamp"]: key_dict = import_ed25519_privatekey_from_file(os.path.join( keystore_dir, role + '_key'), password="******") cls.keystore[role] = SSlibSigner(key_dict) def hashes_length_modifier(timestamp: Timestamp) -> None: timestamp.meta["snapshot.json"].hashes = None timestamp.meta["snapshot.json"].length = None cls.metadata["timestamp"] = cls.modify_metadata( cls, "timestamp", hashes_length_modifier)
def test_import_ed25519_privatekey_from_file(self): # Test normal case. # Generate ed25519 keys that can be imported. scheme = 'ed25519' temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory) ed25519_keypath = os.path.join(temporary_directory, 'ed25519_key') interface.generate_and_write_ed25519_keypair(ed25519_keypath, password='******') imported_ed25519_key = \ interface.import_ed25519_privatekey_from_file(ed25519_keypath, 'pw') self.assertTrue(securesystemslib.formats.ED25519KEY_SCHEMA.matches(imported_ed25519_key)) # Test improperly formatted argument. self.assertRaises(securesystemslib.exceptions.FormatError, interface.import_ed25519_privatekey_from_file, 3, 'pw') # Test invalid argument. # Non-existent key file. nonexistent_keypath = os.path.join(temporary_directory, 'nonexistent_keypath') self.assertRaises(IOError, interface.import_ed25519_privatekey_from_file, nonexistent_keypath, 'pw') # Invalid key file argument. invalid_keyfile = os.path.join(temporary_directory, 'invalid_keyfile') with open(invalid_keyfile, 'wb') as file_object: file_object.write(b'bad keyfile') self.assertRaises(securesystemslib.exceptions.Error, interface.import_ed25519_privatekey_from_file, invalid_keyfile, 'pw') # Invalid private key imported (contains unexpected keytype.) imported_ed25519_key['keytype'] = 'invalid_keytype' # Use 'pyca_crypto_keys.py' to bypass the key format validation performed # by 'keys.py'. salt, iterations, derived_key = \ securesystemslib.pyca_crypto_keys._generate_derived_key('pw') # Store the derived key info in a dictionary, the object expected # by the non-public _encrypt() routine. derived_key_information = {'salt': salt, 'iterations': iterations, 'derived_key': derived_key} # Convert the key object to json string format and encrypt it with the # derived key. encrypted_key = \ securesystemslib.pyca_crypto_keys._encrypt(json.dumps(imported_ed25519_key), derived_key_information) with open(ed25519_keypath, 'wb') as file_object: file_object.write(encrypted_key.encode('utf-8')) self.assertRaises(securesystemslib.exceptions.FormatError, interface.import_ed25519_privatekey_from_file, ed25519_keypath, 'pw')
def test_create_and_import_ed25519(self): """Create ed25519 key and import private and public key separately. """ name = "key6" generate_and_write_ed25519_keypair(name) private_key = import_ed25519_privatekey_from_file(name) public_key = import_ed25519_publickey_from_file(name + ".pub") securesystemslib.formats.KEY_SCHEMA.check_match(private_key) self.assertTrue(private_key["keyval"].get("private")) self.assertTrue( securesystemslib.formats.PUBLIC_KEY_SCHEMA.matches(public_key))
def read_keypair(functionary: str, keypath: Keypath, i: int = 1, n: int = 1, passphrase: Optional[str] = None) -> Keypair: assert isinstance(keypath, Keypath) private_keypath = keypath.private private_key_obj = import_ed25519_privatekey_from_file(keypath.private, password=passphrase) private_key = Key(private_keypath, private_key_obj) # and its corresponding public key. public_keypath = keypath.public public_key_obj = import_ed25519_publickey_from_file(keypath.public) public_key = Key(public_keypath, public_key_obj) return Keypair(private_key, public_key)
def setUpClass(self): """Creates and changes into temporary directory. Copies demo files to temp dir... - owner/functionary key pairs - *.link metadata files - layout template (not signed, no expiration date) - final product ...and dumps layout for test scenario """ # Backup original cwd self.working_dir = os.getcwd() # Find demo files demo_files = os.path.join(os.path.dirname(os.path.realpath(__file__)), "demo_files") scripts_directory = os.path.join( os.path.dirname(os.path.realpath(__file__)), "scripts") # Create and change into temporary directory self.test_dir = os.path.realpath(tempfile.mkdtemp()) os.chdir(self.test_dir) # Copy demo files to temp dir for file in os.listdir(demo_files): shutil.copy(os.path.join(demo_files, file), self.test_dir) shutil.copytree(scripts_directory, 'scripts') # Load layout template layout_template = Metablock.load("demo.layout.template") # Store layout paths to be used in tests self.layout_double_signed_path = "double-signed.layout" # Import layout signing keys alice = import_rsa_key_from_file("alice") danny = import_ed25519_privatekey_from_file("danny") self.alice_path = "alice.pub" self.danny_path = "danny.pub" # dump a double signed layout layout_template.sign(alice) layout_template.sign(danny) layout_template.dump(self.layout_double_signed_path)
def setUpClass(cls) -> None: cls.repo_dir = os.path.join(utils.TESTS_DIR, "repository_data", "repository", "metadata") cls.metadata = {} for md in [ Root.type, Timestamp.type, Snapshot.type, Targets.type, "role1", "role2", ]: with open(os.path.join(cls.repo_dir, f"{md}.json"), "rb") as f: cls.metadata[md] = f.read() keystore_dir = os.path.join(utils.TESTS_DIR, "repository_data", "keystore") cls.keystore = {} root_key_dict = import_rsa_privatekey_from_file(os.path.join( keystore_dir, Root.type + "_key"), password="******") cls.keystore[Root.type] = SSlibSigner(root_key_dict) for role in [ "delegation", Snapshot.type, Targets.type, Timestamp.type ]: key_dict = import_ed25519_privatekey_from_file(os.path.join( keystore_dir, role + "_key"), password="******") cls.keystore[role] = SSlibSigner(key_dict) def hashes_length_modifier(timestamp: Timestamp) -> None: timestamp.snapshot_meta.hashes = None timestamp.snapshot_meta.length = None cls.metadata[Timestamp.type] = cls.modify_metadata( Timestamp.type, hashes_length_modifier)
def test_ed25519(self): """Test ed25519 key _generation and import interface functions. """ # TEST: Generate default keys and import # Assert location and format fn_default = "default" fn_default_ret = _generate_and_write_ed25519_keypair( filepath=fn_default) pub = import_ed25519_publickey_from_file(fn_default + ".pub") priv = import_ed25519_privatekey_from_file(fn_default) self.assertEqual(fn_default, fn_default_ret) self.assertTrue(ED25519KEY_SCHEMA.matches(pub)) self.assertTrue(PUBLIC_KEY_SCHEMA.matches(pub)) self.assertTrue(ED25519KEY_SCHEMA.matches(priv)) # NOTE: There is no private key schema, at least check it has a value self.assertTrue(priv["keyval"]["private"]) # TEST: Generate unencrypted keys with empty prompt # Assert importable with empty prompt password and without password fn_empty_prompt = "empty_prompt" with mock.patch("securesystemslib.interface.get_password", return_value=""): _generate_and_write_ed25519_keypair(filepath=fn_empty_prompt) import_ed25519_privatekey_from_file(fn_empty_prompt, prompt=True) import_ed25519_privatekey_from_file(fn_empty_prompt) # TEST: Generate keys with auto-filename, i.e. keyid # Assert filename is keyid fn_keyid = _generate_and_write_ed25519_keypair() pub = import_ed25519_publickey_from_file(fn_keyid + ".pub") priv = import_ed25519_privatekey_from_file(fn_keyid) self.assertTrue( os.path.basename(fn_keyid) == pub["keyid"] == priv["keyid"]) # TEST: Generate two keypairs with encrypted private keys using ... pw = "pw" fn_encrypted = "encrypted" fn_prompt = "prompt" # ... a passed pw ... _generate_and_write_ed25519_keypair(filepath=fn_encrypted, password=pw) with mock.patch("securesystemslib.interface.get_password", return_value=pw): # ... and a prompted pw. _generate_and_write_ed25519_keypair(filepath=fn_prompt, prompt=True) # Assert that both private keys are importable using the prompted pw ... import_ed25519_privatekey_from_file(fn_prompt, prompt=True) import_ed25519_privatekey_from_file(fn_encrypted, prompt=True) # ... and the passed pw. import_ed25519_privatekey_from_file(fn_prompt, password=pw) import_ed25519_privatekey_from_file(fn_encrypted, password=pw) # TEST: Import existing keys with encrypted private key (test regression) # Assert format pub = import_ed25519_publickey_from_file(self.path_ed25519 + ".pub") priv = import_ed25519_privatekey_from_file(self.path_ed25519, "password") self.assertTrue(PUBLIC_KEY_SCHEMA.matches(pub)) self.assertTrue(ED25519KEY_SCHEMA.matches(pub)) self.assertTrue(ED25519KEY_SCHEMA.matches(priv)) # NOTE: There is no private key schema, at least check it has a value self.assertTrue(priv["keyval"]["private"]) # TEST: Unexpected behavior # FIXME: Should 'import_ed25519_publickey_from_file' be able to import a # a non-encrypted ed25519 private key? I think it should not, but it is: priv = import_ed25519_publickey_from_file(fn_default) self.assertTrue(ED25519KEY_SCHEMA.matches(priv)) self.assertTrue(priv["keyval"]["private"]) # FIXME: Should 'import_ed25519_privatekey_from_file' be able to import a # an ed25519 public key? I think it should not, but it is: pub = import_ed25519_privatekey_from_file(fn_default + ".pub") self.assertTrue(PUBLIC_KEY_SCHEMA.matches(pub)) # TEST: Generation errors for idx, (kwargs, err_msg) in enumerate([ # Error on empty password ({ "password": "" }, "encryption password must be 1 or more characters long"), # Error on 'password' and 'prompt=True' ({ "password": pw, "prompt": True }, "passing 'password' and 'prompt=True' is not allowed") ]): with self.assertRaises(ValueError, msg="(row {})".format(idx)) as ctx: _generate_and_write_ed25519_keypair(**kwargs) self.assertEqual( err_msg, str(ctx.exception), "expected: '{}' got: '{}' (row {})".format( err_msg, ctx.exception, idx)) # Error on bad argument format for idx, kwargs in enumerate([ { "filepath": 123456 }, # Not a string { "password": 123456 }, # Not a string { "prompt": "not-a-bool" } ]): with self.assertRaises(FormatError, msg="(row {})".format(idx)): _generate_and_write_ed25519_keypair(**kwargs) # TEST: Import errors # Error on public key import... for idx, (fn, err_msg) in enumerate([ # Error on invalid json (custom key format) (fn_encrypted, "Cannot deserialize to a Python object"), # Error on invalid custom key format (self.path_no_key, "Missing key"), # Error on invalid key type (self.path_ecdsa + ".pub", "Invalid key type loaded") ]): with self.assertRaises(Error, msg="(row {})".format(idx)) as ctx: import_ed25519_publickey_from_file(fn) self.assertTrue( err_msg in str(ctx.exception), "expected: '{}' got: '{}' (row {})".format( err_msg, ctx.exception, idx)) # Error on private key import... for idx, (args, kwargs, err, err_msg) in enumerate([ # Error on not an ed25519 private key ([self.path_ecdsa], {}, CryptoError, "Malformed Ed25519 key JSON, possibly due to encryption, " "but no password provided?"), # Error on not encrypted ([fn_default], { "password": pw }, CryptoError, "Invalid encrypted file."), # Error on encrypted but no pw ([fn_encrypted], {}, CryptoError, "Malformed Ed25519 key JSON, possibly due to encryption, " "but no password provided?"), # Error on encrypted but empty pw ([fn_encrypted], { "password": "" }, CryptoError, "Decryption failed."), # Error on encrypted but bad pw passed ([fn_encrypted], { "password": "******" }, CryptoError, "Decryption failed."), # Error on pw and prompt ([fn_default], { "password": pw, "prompt": True }, ValueError, "passing 'password' and 'prompt=True' is not allowed") ]): with self.assertRaises(err, msg="(row {})".format(idx)) as ctx: import_ed25519_privatekey_from_file(*args, **kwargs) self.assertTrue( err_msg in str(ctx.exception), "expected: '{}' got: '{}' (row {})".format( err_msg, ctx.exception, idx)) # Error on encrypted but bad pw prompted err_msg = ("Malformed Ed25519 key JSON, possibly due to encryption, " "but no password provided?") with self.assertRaises(CryptoError) as ctx, mock.patch( "securesystemslib.interface.get_password", return_value="bad_pw"): import_ed25519_privatekey_from_file(fn_encrypted) self.assertTrue( err_msg in str(ctx.exception), "expected: '{}' got: '{}'".format(err_msg, ctx.exception)) # Error on bad path format with self.assertRaises(FormatError): import_ed25519_publickey_from_file(123456) with self.assertRaises(FormatError): import_ed25519_privatekey_from_file(123456) # Error on bad password format with self.assertRaises(FormatError): import_ed25519_privatekey_from_file(fn_default, password=123456) # Error on bad prompt format with self.assertRaises(FormatError): import_ed25519_privatekey_from_file(fn_default, prompt="not-a-bool")
def test_generate_and_write_ed25519_keypair(self): # Test normal case. temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory) test_keypath = os.path.join(temporary_directory, 'ed25519_key') test_keypath_unencrypted = os.path.join(temporary_directory, 'ed25519_key_unencrypted') returned_path = interface.generate_and_write_ed25519_keypair( test_keypath, password='******') self.assertTrue(os.path.exists(test_keypath)) self.assertTrue(os.path.exists(test_keypath + '.pub')) self.assertEqual(returned_path, test_keypath) # If an empty string is given for 'password', the private key file # is written to disk unencrypted. interface.generate_and_write_ed25519_keypair(test_keypath_unencrypted, password='') self.assertTrue(os.path.exists(test_keypath_unencrypted)) self.assertTrue(os.path.exists(test_keypath_unencrypted + '.pub')) # Ensure the generated key files are importable. imported_pubkey = \ interface.import_ed25519_publickey_from_file(test_keypath + '.pub') self.assertTrue(securesystemslib.formats.ED25519KEY_SCHEMA\ .matches(imported_pubkey)) imported_privkey = \ interface.import_ed25519_privatekey_from_file(test_keypath, 'pw') self.assertTrue(securesystemslib.formats.ED25519KEY_SCHEMA\ .matches(imported_privkey)) # Fail importing encrypted key passing password and prompt with self.assertRaises(ValueError): interface.import_ed25519_privatekey_from_file(test_keypath, password='******', prompt=True) # Fail importing encrypted key passing an empty string for passwd with self.assertRaises(ValueError): interface.import_ed25519_privatekey_from_file(test_keypath, password='') # Try to import the unencrypted key file, by not passing a password imported_privkey = \ interface.import_ed25519_privatekey_from_file(test_keypath_unencrypted) self.assertTrue(securesystemslib.formats.ED25519KEY_SCHEMA.\ matches(imported_privkey)) # Try to import the unencrypted key file, by entering an empty password with mock.patch('securesystemslib.interface.get_password', return_value=''): imported_privkey = \ interface.import_ed25519_privatekey_from_file(test_keypath_unencrypted, prompt=True) self.assertTrue( securesystemslib.formats.ED25519KEY_SCHEMA.matches(imported_privkey)) # Fail importing unencrypted key passing a password with self.assertRaises(securesystemslib.exceptions.CryptoError): interface.import_ed25519_privatekey_from_file(test_keypath_unencrypted, 'pw') # Fail importing encrypted key passing no password with self.assertRaises(securesystemslib.exceptions.CryptoError): interface.import_ed25519_privatekey_from_file(test_keypath) # Test for a default filepath. If 'filepath' is not given, the key's # KEYID is used as the filename. The key is saved to the current working # directory. default_keypath = interface.generate_and_write_ed25519_keypair(password='******') self.assertTrue(os.path.exists(default_keypath)) self.assertTrue(os.path.exists(default_keypath + '.pub')) written_key = interface.import_ed25519_publickey_from_file(default_keypath + '.pub') self.assertEqual(written_key['keyid'], os.path.basename(default_keypath)) os.remove(default_keypath) os.remove(default_keypath + '.pub') # Test improperly formatted arguments. self.assertRaises(securesystemslib.exceptions.FormatError, interface.generate_and_write_ed25519_keypair, 3, password='******') self.assertRaises(securesystemslib.exceptions.FormatError, interface.generate_and_write_rsa_keypair, test_keypath, password=3)
def get_private_key(): init_keys() return import_ed25519_privatekey_from_file(KEY_NAME, password=PASSWORD)