diff --git a/securesystemslib/ecdsa_keys.py b/securesystemslib/ecdsa_keys.py index b843d78f..f233243a 100755 --- a/securesystemslib/ecdsa_keys.py +++ b/securesystemslib/ecdsa_keys.py @@ -59,12 +59,12 @@ import securesystemslib.formats import securesystemslib.exceptions -_SUPPORTED_ECDSA_ALGORITHMS = ['ecdsa-sha2-nistp256'] +_SUPPORTED_ECDSA_SCHEMES = ['ecdsa-sha2-nistp256'] logger = logging.getLogger('securesystemslib_ecdsa_keys') -def generate_public_and_private(algorithm='ecdsa-sha2-nistp256'): +def generate_public_and_private(scheme='ecdsa-sha2-nistp256'): """ Generate a pair of ECDSA public and private keys with one of the supported, @@ -99,7 +99,7 @@ def generate_public_and_private(algorithm='ecdsa-sha2-nistp256'): True - algorithm: + scheme: A string indicating which algorithm to use for the generation of the public and private ECDSA keys. 'ecdsa-sha2-nistp256' is the only currently supported ECDSA algorithm, which is supported by OpenSSH and @@ -126,12 +126,12 @@ def generate_public_and_private(algorithm='ecdsa-sha2-nistp256'): # supported ECDSA algorithms. It must conform to # 'securesystemslib.formats.ECDSAALGORITHMS_SCHEMA'. Raise # 'securesystemslib.exceptions.FormatError' if the check fails. - securesystemslib.formats.ECDSAALGORITHMS_SCHEMA.check_match(algorithm) + securesystemslib.formats.ECDSA_SIG_SCHEMA.check_match(scheme) public_key = None private_key = None - if algorithm == 'ecdsa-sha2-nistp256': + if scheme == 'ecdsa-sha2-nistp256': private_key = ec.generate_private_key(ec.SECP256R1, default_backend()) public_key = private_key.public_key() @@ -139,8 +139,8 @@ def generate_public_and_private(algorithm='ecdsa-sha2-nistp256'): # invalid 'algorithm'. else: #pragma: no cover raise securesystemslib.exceptions.UnsupportedLibraryError('An unsupported' - ' algorithm was specified: ' + repr(algorithm) + '.\n Supported' - ' algorithms: ' + repr(_SUPPORTED_ECDSA_ALGORITHMS)) + ' algorithm was specified: ' + repr(scheme) + '.\n Supported' + ' algorithms: ' + repr(_SUPPORTED_ECDSA_SCHEMES)) private_pem = private_key.private_bytes(encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.TraditionalOpenSSL, @@ -155,18 +155,18 @@ def generate_public_and_private(algorithm='ecdsa-sha2-nistp256'): -def create_signature(public_key, private_key, data): +def create_signature(public_key, private_key, data, scheme='ecdsa-sha2-nistp256'): """ - Return a (signature, method) tuple. + Return a (signature, scheme) tuple. - >>> algorithm = 'ecdsa-sha2-nistp256' - >>> public, private = generate_public_and_private(algorithm) + >>> requested_scheme = 'ecdsa-sha2-nistp256' + >>> public, private = generate_public_and_private(requested_scheme) >>> data = b'The quick brown fox jumps over the lazy dog' - >>> signature, method = create_signature(public, private, data) + >>> signature, scheme = create_signature(public, private, data, requested_scheme) >>> securesystemslib.formats.ECDSASIGNATURE_SCHEMA.matches(signature) True - >>> method == algorithm + >>> requested_scheme == scheme True @@ -179,12 +179,19 @@ def create_signature(public_key, private_key, data): data: Byte data used by create_signature() to generate the signature returned. + scheme: + The signature scheme used to generate the signature. For example: + 'ecesa-sha2-nistp256'. + securesystemslib.exceptions.FormatError, if the arguments are improperly formatted. securesystemslib.exceptions.CryptoError, if a signature cannot be created. + securesystemslib.exceptions.UnsupportedAlgorithmError, if 'scheme' is + an not one of the supported signature schemes. + None. @@ -203,47 +210,56 @@ def create_signature(public_key, private_key, data): # Is 'private_key' properly formatted? securesystemslib.formats.PEMECDSA_SCHEMA.check_match(private_key) - method = 'ecdsa-sha2-nistp256' + # Is 'scheme' properly formatted? + securesystemslib.formats.ECDSA_SIG_SCHEMA.check_match(scheme) - try: - private_key = load_pem_private_key(private_key.encode('utf-8'), - password=None, backend=default_backend()) + # A defensive check for a valid 'scheme'. The check_match() above + # should have already validated it... + if scheme == 'ecdsa-sha2-nistp256': #pragma: no cover + try: + private_key = load_pem_private_key(private_key.encode('utf-8'), + password=None, backend=default_backend()) + + signer = private_key.signer(ec.ECDSA(hashes.SHA256())) + signer.update(data) + signature = signer.finalize() - signer = private_key.signer(ec.ECDSA(hashes.SHA256())) - signer.update(data) - signature = signer.finalize() + except TypeError as e: + raise securesystemslib.exceptions.CryptoError('Could not create' + ' signature: ' + str(e)) - except TypeError as e: - raise securesystemslib.exceptions.CryptoError('Could not create' - ' signature: ' + str(e)) + else: #pragma: no cover + raise securesystemslib.exceptions.UnsupportedAlgorithmError('Unsupported' + ' signature scheme is specified: ' + repr(scheme)) - return signature, method + return signature, scheme -def verify_signature(public_key, method, signature, data): +def verify_signature(public_key, scheme, signature, data): """ - ... + Verify that 'signature' was produced by the private key associated with + 'public_key'. - >>> algorithm = 'ecdsa-sha2-nistp256' - >>> public, private = generate_public_and_private(algorithm) + >>> scheme = 'ecdsa-sha2-nistp256' + >>> public, private = generate_public_and_private(scheme) >>> data = b'The quick brown fox jumps over the lazy dog' - >>> signature, method = create_signature(public, private, data) - >>> verify_signature(public, method, signature, data) + >>> signature, scheme = create_signature(public, private, data, scheme) + >>> verify_signature(public, scheme, signature, data) True - >>> verify_signature(public, method, signature, b'bad data') + >>> verify_signature(public, scheme, signature, b'bad data') False public_key: - The ECDSA public key in PEM format. The publi key is needed to verify + The ECDSA public key in PEM format. The public key is needed to verify 'signature'. - method: - The signature method used to generate 'signature'. For example: + scheme: + The signature scheme used to generate 'signature'. For example: 'ecdsa-sha2-nistp256'. signature: @@ -254,8 +270,11 @@ def verify_signature(public_key, method, signature, data): Byte data that was used by create_signature() to generate 'signature'. - securesystemslib.exceptions.FormatError, if any of the arguments are improperly - formatted. + securesystemslib.exceptions.FormatError, if any of the arguments are + improperly formatted. + + securesystemslib.exceptions.UnsupportedAlgorithmError, if 'scheme' is + not one of the supported signature schemes. None. @@ -268,11 +287,13 @@ def verify_signature(public_key, method, signature, data): # Are the arguments properly formatted? # If not, raise 'securesystemslib.exceptions.FormatError'. securesystemslib.formats.PEMECDSA_SCHEMA.check_match(public_key) - securesystemslib.formats.NAME_SCHEMA.check_match(method) + securesystemslib.formats.ECDSA_SIG_SCHEMA.check_match(scheme) securesystemslib.formats.ECDSASIGNATURE_SCHEMA.check_match(signature) - # Is 'method' one of the supported ECDSA algorithms? - if method in _SUPPORTED_ECDSA_ALGORITHMS: + # Is 'scheme' one of the supported ECDSA signature schemes? A defensive + # check for a valid 'scheme'. The check_match() above should have validated + # it... + if scheme in _SUPPORTED_ECDSA_SCHEMES: #pragma: no cover ecdsa_key = load_pem_public_key(public_key.encode('utf-8'), backend=default_backend()) if not isinstance(ecdsa_key, ec.EllipticCurvePublicKey): @@ -299,10 +320,10 @@ def verify_signature(public_key, method, signature, data): except cryptography.exceptions.InvalidSignature: return False - else: - raise securesystemslib.exceptions.UnknownMethodError('Unsupported signing' - ' method given: ' + repr(method) + '. \nSupported' - ' methods: ' + repr(_SUPPORTED_ECDSA_ALGORITHMS)) + else: #pragma: no cover + raise securesystemslib.exceptions.UnsupportedAlgorithmError('Unsupported' + ' signature scheme is given: ' + repr(scheme) + '. \nSupported' + ' schemes: ' + repr(_SUPPORTED_ECDSA_SCHEMES)) diff --git a/securesystemslib/ed25519_keys.py b/securesystemslib/ed25519_keys.py index 26aab36c..a2b9ba07 100755 --- a/securesystemslib/ed25519_keys.py +++ b/securesystemslib/ed25519_keys.py @@ -108,12 +108,12 @@ import securesystemslib.formats import securesystemslib.exceptions -# Supported ed25519 signing method: 'ed25519'. The pure Python implementation +# Supported ed25519 signing schemes: 'ed25519'. The pure Python implementation # (i.e., ed25519') and PyNaCl (i.e., 'nacl', libsodium + Python bindings) -# modules are currently supported in the creationg of 'ed25519' signatures. +# modules are currently supported in the creation of 'ed25519' signatures. # Previously, a distinction was made between signatures made by the pure Python # implementation and PyNaCl. -_SUPPORTED_ED25519_SIGNING_METHODS = ['ed25519'] +_SUPPORTED_ED25519_SIGNING_SCHEMES = ['ed25519'] def generate_public_and_private(): @@ -175,12 +175,13 @@ def generate_public_and_private(): -def create_signature(public_key, private_key, data): +def create_signature(public_key, private_key, data, scheme): """ - Return a (signature, method) tuple, where the method is 'ed25519' and is - always generated by PyNaCl (i.e., 'nacl'). The signature returned conforms - to 'securesystemslib.formats.ED25519SIGNATURE_SCHEMA', and has the form: + Return a (signature, scheme) tuple, where the signature scheme is 'ed25519' + and is always generated by PyNaCl (i.e., 'nacl'). The signature returned + conforms to 'securesystemslib.formats.ED25519SIGNATURE_SCHEMA', and has the + form: '\xae\xd7\x9f\xaf\x95{bP\x9e\xa8YO Z\x86\x9d...' @@ -188,17 +189,18 @@ def create_signature(public_key, private_key, data): >>> public, private = generate_public_and_private() >>> data = b'The quick brown fox jumps over the lazy dog' - >>> signature, method = \ - create_signature(public, private, data) + >>> scheme = 'ed25519' + >>> signature, scheme = \ + create_signature(public, private, data, scheme) >>> securesystemslib.formats.ED25519SIGNATURE_SCHEMA.matches(signature) True - >>> method == 'ed25519' + >>> scheme == 'ed25519' True - >>> signature, method = \ - create_signature(public, private, data) + >>> signature, scheme = \ + create_signature(public, private, data, scheme) >>> securesystemslib.formats.ED25519SIGNATURE_SCHEMA.matches(signature) True - >>> method == 'ed25519' + >>> scheme == 'ed25519' True @@ -211,8 +213,12 @@ def create_signature(public_key, private_key, data): data: Data object used by create_signature() to generate the signature. + scheme: + The signature scheme used to generate the signature. + - securesystemslib.exceptions.FormatError, if the arguments are improperly formatted. + securesystemslib.exceptions.FormatError, if the arguments are improperly + formatted. securesystemslib.exceptions.CryptoError, if a signature cannot be created. @@ -220,85 +226,95 @@ def create_signature(public_key, private_key, data): nacl.signing.SigningKey.sign() called to generate the actual signature. - A signature dictionary conformat to 'securesystemslib.format.SIGNATURE_SCHEMA'. - ed25519 signatures are 64 bytes, however, the hexlified signature is - stored in the dictionary returned. + A signature dictionary conformat to + 'securesystemslib.format.SIGNATURE_SCHEMA'. ed25519 signatures are 64 + bytes, however, the hexlified signature is stored in the dictionary + returned. """ # Does 'public_key' have the correct format? # This check will ensure 'public_key' conforms to - # 'securesystemslib.formats.ED25519PUBLIC_SCHEMA', which must have length 32 bytes. - # Raise 'securesystemslib.exceptions.FormatError' if the check fails. + # 'securesystemslib.formats.ED25519PUBLIC_SCHEMA', which must have length 32 + # bytes. Raise 'securesystemslib.exceptions.FormatError' if the check fails. securesystemslib.formats.ED25519PUBLIC_SCHEMA.check_match(public_key) # Is 'private_key' properly formatted? securesystemslib.formats.ED25519SEED_SCHEMA.check_match(private_key) + # Is 'scheme' properly formatted? + securesystemslib.formats.ED25519_SIG_SCHEMA.check_match(scheme) + # Signing the 'data' object requires a seed and public key. # nacl.signing.SigningKey.sign() generates the signature. public = public_key private = private_key - method = None signature = None - # The private and public keys have been validated above by 'securesystemslib.formats' and - # should be 32-byte strings. - method = 'ed25519' - try: - nacl_key = nacl.signing.SigningKey(private) - nacl_sig = nacl_key.sign(data) - signature = nacl_sig.signature + # The private and public keys have been validated above by + # 'securesystemslib.formats' and should be 32-byte strings. This is a + # defensive check for a valid 'scheme', which should have already been + # validated in the check_match() above. + if scheme == 'ed25519': #pragma: no cover + try: + nacl_key = nacl.signing.SigningKey(private) + nacl_sig = nacl_key.sign(data) + signature = nacl_sig.signature - except NameError: # pragma: no cover - message = 'The PyNaCl library and/or its dependencies unavailable.' - raise securesystemslib.exceptions.UnsupportedLibraryError(message) + except NameError: # pragma: no cover + message = 'The PyNaCl library and/or its dependencies unavailable.' + raise securesystemslib.exceptions.UnsupportedLibraryError(message) + + except (ValueError, TypeError, nacl.exceptions.CryptoError) as e: + message = 'An "ed25519" signature could not be created with PyNaCl.' + raise securesystemslib.exceptions.CryptoError(message + str(e)) - except (ValueError, TypeError, nacl.exceptions.CryptoError) as e: - message = 'An "ed25519" signature could not be created with PyNaCl.' - raise securesystemslib.exceptions.CryptoError(message + str(e)) + else: #pragma: no cover + raise securesystemslib.exceptions.UnsupportedAlgorithmError('Unsupported' + ' signature scheme is specified: ' + repr(scheme)) - return signature, method + return signature, scheme -def verify_signature(public_key, method, signature, data, use_pynacl=False): +def verify_signature(public_key, scheme, signature, data, use_pynacl=False): """ Determine whether the private key corresponding to 'public_key' produced - 'signature'. verify_signature() will use the public key, the 'method' and + 'signature'. verify_signature() will use the public key, the 'scheme' and 'sig', and 'data' arguments to complete the verification. >>> public, private = generate_public_and_private() >>> data = b'The quick brown fox jumps over the lazy dog' - >>> signature, method = \ - create_signature(public, private, data) - >>> verify_signature(public, method, signature, data, use_pynacl=False) + >>> scheme = 'ed25519' + >>> signature, scheme = \ + create_signature(public, private, data, scheme) + >>> verify_signature(public, scheme, signature, data, use_pynacl=False) True - >>> verify_signature(public, method, signature, data, use_pynacl=True) + >>> verify_signature(public, scheme, signature, data, use_pynacl=True) True >>> bad_data = b'The sly brown fox jumps over the lazy dog' - >>> bad_signature, method = \ - create_signature(public, private, bad_data) - >>> verify_signature(public, method, bad_signature, data, use_pynacl=False) + >>> bad_signature, scheme = \ + create_signature(public, private, bad_data, scheme) + >>> verify_signature(public, scheme, bad_signature, data, use_pynacl=False) False public_key: The public key is a 32-byte string. - method: - 'ed25519' signature method generated by either the pure python + scheme: + 'ed25519' signature scheme used by either the pure python implementation (i.e., ed25519.py) or PyNacl (i.e., 'nacl'). signature: The signature is a 64-byte string. data: - Data object used by securesystemslib.ed25519_keys.create_signature() to generate - 'signature'. 'data' is needed here to verify the signature. + Data object used by securesystemslib.ed25519_keys.create_signature() to + generate 'signature'. 'data' is needed here to verify the signature. use_pynacl: True, if the ed25519 signature should be verified by PyNaCl. False, @@ -306,15 +322,17 @@ def verify_signature(public_key, method, signature, data, use_pynacl=False): of ed25519 (slower). - securesystemslib.exceptions.UnknownMethodError. Raised if the signing method used by - 'signature' is not one supported by securesystemslib.ed25519_keys.create_signature(). + securesystemslib.exceptions.UnsupportedAlgorithmError. Raised if the + signature scheme 'scheme' is not one supported by + securesystemslib.ed25519_keys.create_signature(). - securesystemslib.exceptions.FormatError. Raised if the arguments are improperly formatted. + securesystemslib.exceptions.FormatError. Raised if the arguments are + improperly formatted. - securesystemslib._vendor.ed25519.ed25519.checkvalid() called to do the actual - verification. nacl.signing.VerifyKey.verify() called if 'use_pynacl' is - True. + securesystemslib._vendor.ed25519.ed25519.checkvalid() called to do the + actual verification. nacl.signing.VerifyKey.verify() called if + 'use_pynacl' is True. Boolean. True if the signature is valid, False otherwise. @@ -322,12 +340,12 @@ def verify_signature(public_key, method, signature, data, use_pynacl=False): # Does 'public_key' have the correct format? # This check will ensure 'public_key' conforms to - # 'securesystemslib.formats.ED25519PUBLIC_SCHEMA', which must have length 32 bytes. - # Raise 'securesystemslib.exceptions.FormatError' if the check fails. + # 'securesystemslib.formats.ED25519PUBLIC_SCHEMA', which must have length 32 + # bytes. Raise 'securesystemslib.exceptions.FormatError' if the check fails. securesystemslib.formats.ED25519PUBLIC_SCHEMA.check_match(public_key) - # Is 'method' properly formatted? - securesystemslib.formats.NAME_SCHEMA.check_match(method) + # Is 'scheme' properly formatted? + securesystemslib.formats.ED25519_SIG_SCHEMA.check_match(scheme) # Is 'signature' properly formatted? securesystemslib.formats.ED25519SIGNATURE_SCHEMA.check_match(signature) @@ -335,14 +353,16 @@ def verify_signature(public_key, method, signature, data, use_pynacl=False): # Is 'use_pynacl' properly formatted? securesystemslib.formats.BOOLEAN_SCHEMA.check_match(use_pynacl) - # Verify 'signature'. Before returning the Boolean result, - # ensure 'ed25519' was used as the signing method. - # Raise 'securesystemslib.exceptions.UnsupportedLibraryError' if 'use_pynacl' is True but 'nacl' is - # unavailable. + # Verify 'signature'. Before returning the Boolean result, ensure 'ed25519' + # was used as the signature scheme. Raise + # 'securesystemslib.exceptions.UnsupportedLibraryError' if 'use_pynacl' is + # True but 'nacl' is unavailable. public = public_key valid_signature = False - if method in _SUPPORTED_ED25519_SIGNING_METHODS: + # This is a defensive check for a valid 'scheme', which should have already + # been validated in the check_match() above. + if scheme in _SUPPORTED_ED25519_SIGNING_SCHEMES: #pragma: no cover if use_pynacl: try: nacl_verify_key = nacl.signing.VerifyKey(public) @@ -367,10 +387,10 @@ def verify_signature(public_key, method, signature, data, use_pynacl=False): except Exception as e: pass - else: - message = 'Unsupported ed25519 signing method: '+repr(method)+'.\n'+ \ - 'Supported methods: '+repr(_SUPPORTED_ED25519_SIGNING_METHODS)+'.' - raise securesystemslib.exceptions.UnknownMethodError(message) + else: #pragma: no cover + message = 'Unsupported ed25519 signature scheme: ' + repr(scheme) + '.\n' + \ + 'Supported schemes: ' + repr(_SUPPORTED_ED25519_SIGNING_SCHEMES) + '.' + raise securesystemslib.exceptions.UnsupportedAlgorithmError(message) return valid_signature diff --git a/securesystemslib/formats.py b/securesystemslib/formats.py index bc36a4b8..eb8e506b 100755 --- a/securesystemslib/formats.py +++ b/securesystemslib/formats.py @@ -115,8 +115,9 @@ # A list of KEYID_SCHEMA. KEYIDS_SCHEMA = SCHEMA.ListOf(KEYID_SCHEMA) -# The method used for a generated signature (e.g., 'RSASSA-PSS'). -SIG_METHOD_SCHEMA = SCHEMA.AnyString() +# The signing scheme used by a key to generate a signature (e.g., +# 'rsassa-pss-sha256' is one of the signing schemes for key type 'rsa'). +SIG_SCHEME_SCHEMA = SCHEMA.AnyString() # A relative file path (e.g., 'metadata/root/'). RELPATH_SCHEMA = SCHEMA.AnyString() @@ -183,7 +184,7 @@ RSAKEYBITS_SCHEMA = SCHEMA.Integer(lo=2048) # The supported ECDSA algorithms (ecdsa-sha2-nistp256 is supported by default). -ECDSAALGORITHMS_SCHEMA = SCHEMA.OneOf([SCHEMA.String('ecdsa-sha2-nistp256')]) +ECDSA_SIG_SCHEMA = SCHEMA.OneOf([SCHEMA.String('ecdsa-sha2-nistp256')]) # The number of hashed bins, or the number of delegated roles. See # delegate_hashed_bins() in 'repository_tool.py' for an example. Note: @@ -234,6 +235,7 @@ KEY_SCHEMA = SCHEMA.Object( object_name = 'KEY_SCHEMA', keytype = SCHEMA.AnyString(), + scheme = SIG_SCHEME_SCHEMA, keyval = KEYVAL_SCHEMA, expires = SCHEMA.Optional(ISO8601_DATETIME_SCHEMA)) @@ -253,6 +255,7 @@ ANYKEY_SCHEMA = SCHEMA.Object( object_name = 'ANYKEY_SCHEMA', keytype = KEYTYPE_SCHEMA, + scheme = SIG_SCHEME_SCHEMA, keyid = KEYID_SCHEMA, keyid_hash_algorithms = SCHEMA.Optional(HASHALGORITHMS_SCHEMA), keyval = KEYVAL_SCHEMA, @@ -261,18 +264,26 @@ # A list of TUF key objects. ANYKEYLIST_SCHEMA = SCHEMA.ListOf(ANYKEY_SCHEMA) +# RSA signature schemes. +RSA_SIG_SCHEMA = SCHEMA.OneOf([SCHEMA.String('rsassa-pss-sha256')]) + # An RSA TUF key. RSAKEY_SCHEMA = SCHEMA.Object( object_name = 'RSAKEY_SCHEMA', keytype = SCHEMA.String('rsa'), + scheme = RSA_SIG_SCHEMA, keyid = KEYID_SCHEMA, keyid_hash_algorithms = SCHEMA.Optional(HASHALGORITHMS_SCHEMA), keyval = KEYVAL_SCHEMA) +# ECDSA signature schemes. +ECDSA_SIG_SCHEMA = SCHEMA.OneOf([SCHEMA.String('ecdsa-sha2-nistp256')]) + # An ECDSA TUF key. ECDSAKEY_SCHEMA = SCHEMA.Object( object_name = 'ECDSAKEY_SCHEMA', - keytype = ECDSAALGORITHMS_SCHEMA, + keytype = SCHEMA.String('ecdsa-sha2-nistp256'), + scheme = ECDSA_SIG_SCHEMA, keyid = KEYID_SCHEMA, keyid_hash_algorithms = SCHEMA.Optional(HASHALGORITHMS_SCHEMA), keyval = KEYVAL_SCHEMA) @@ -295,10 +306,15 @@ [SCHEMA.String('general'), SCHEMA.String('ed25519'), SCHEMA.String('rsa'), SCHEMA.String('ecdsa-sha2-nistp256')])) +# Ed25519 signature schemes. The vanilla Ed25519 signature scheme is currently +# supported. +ED25519_SIG_SCHEMA = SCHEMA.OneOf([SCHEMA.String('ed25519')]) + # An ed25519 TUF key. ED25519KEY_SCHEMA = SCHEMA.Object( object_name = 'ED25519KEY_SCHEMA', keytype = SCHEMA.String('ed25519'), + scheme = ED25519_SIG_SCHEMA, keyid = KEYID_SCHEMA, keyid_hash_algorithms = SCHEMA.Optional(HASHALGORITHMS_SCHEMA), keyval = KEYVAL_SCHEMA) @@ -337,18 +353,15 @@ key_schema = RELPATH_SCHEMA, value_schema = FILEINFO_SCHEMA) -# A single signature of an object. Indicates the signature, the ID of the -# signing key, and the signing method. -# I debated making the signature schema not contain the key ID and instead have -# the signatures of a file be a dictionary with the key being the keyid and the -# value being the signature schema without the keyid. That would be under -# the argument that a key should only be able to sign a file once. However, -# one can imagine that maybe a key wants to sign multiple times with different -# signature methods. +# A single signature of an object. Indicates the signature, and the KEYID of +# the signing key. I debated making the signature schema not contain the key +# ID and instead have the signatures of a file be a dictionary with the key +# being the keyid and the value being the signature schema without the keyid. +# That would be under the argument that a key should only be able to sign a +# file once. SIGNATURE_SCHEMA = SCHEMA.Object( object_name = 'SIGNATURE_SCHEMA', keyid = KEYID_SCHEMA, - method = SIG_METHOD_SCHEMA, sig = HEX_SCHEMA) # List of SIGNATURE_SCHEMA. @@ -365,8 +378,7 @@ good_sigs = KEYIDS_SCHEMA, bad_sigs = KEYIDS_SCHEMA, unknown_sigs = KEYIDS_SCHEMA, - untrusted_sigs = KEYIDS_SCHEMA, - unknown_method_sigs = KEYIDS_SCHEMA) + untrusted_sigs = KEYIDS_SCHEMA) # A signable object. Holds the signing role and its associated signatures. SIGNABLE_SCHEMA = SCHEMA.Object( diff --git a/securesystemslib/interface.py b/securesystemslib/interface.py index 0e91762c..2d1d0657 100755 --- a/securesystemslib/interface.py +++ b/securesystemslib/interface.py @@ -199,7 +199,8 @@ def generate_and_write_rsa_keypair(filepath, bits=DEFAULT_RSA_KEY_BITS, -def import_rsa_privatekey_from_file(filepath, password=None): +def import_rsa_privatekey_from_file(filepath, password=None, + scheme='rsassa-pss-sha256'): """ Import the encrypted PEM file in 'filepath', decrypt it, and return the key @@ -220,6 +221,9 @@ def import_rsa_privatekey_from_file(filepath, password=None): password: The passphrase to decrypt 'filepath'. + scheme: + The signature scheme used by the imported key. + securesystemslib.exceptions.FormatError, if the arguments are improperly formatted. @@ -240,6 +244,9 @@ def import_rsa_privatekey_from_file(filepath, password=None): # Raise 'securesystemslib.exceptions.FormatError' if there is a mismatch. securesystemslib.formats.PATH_SCHEMA.check_match(filepath) + # Is 'scheme' properly formatted? + securesystemslib.formats.RSA_SIG_SCHEMA.check_match(scheme) + # If the caller does not provide a password argument, prompt for one. # Password confirmation disabled here, which should ideally happen only # when creating encrypted key files (i.e., improve usability). @@ -257,11 +264,12 @@ def import_rsa_privatekey_from_file(filepath, password=None): # Convert 'pem_key' to 'securesystemslib.formats.RSAKEY_SCHEMA' format. # Raise 'securesystemslib.exceptions.CryptoError' if 'pem_key' is invalid. if len(password): - rsa_key = securesystemslib.keys.import_rsakey_from_private_pem(pem, password) + rsa_key = securesystemslib.keys.import_rsakey_from_private_pem(pem, scheme, password) else: logger.debug('An empty password was given. Attempting to import an unencrypted file.') - rsa_key = securesystemslib.keys.import_rsakey_from_private_pem(pem, password=None) + rsa_key = securesystemslib.keys.import_rsakey_from_private_pem(pem, + scheme, password=None) return rsa_key @@ -390,8 +398,11 @@ def generate_and_write_ed25519_keypair(filepath, password=None): # the keyid portion). keytype = ed25519_key['keytype'] keyval = ed25519_key['keyval'] + scheme = ed25519_key['scheme'] + ed25519key_metadata_format = \ - securesystemslib.keys.format_keyval_to_metadata(keytype, keyval, private=False) + securesystemslib.keys.format_keyval_to_metadata(keytype, scheme, keyval, + private=False) # Write the public key, conformant to 'securesystemslib.formats.KEY_SCHEMA', to # '.pub'. @@ -468,12 +479,12 @@ def import_ed25519_publickey_from_file(filepath): def import_ed25519_privatekey_from_file(filepath, password=None): """ - Import the encrypted ed25519 key file in 'filepath', decrypt it, and - return the key object in 'securesystemslib.formats.ED25519KEY_SCHEMA' format. + Import the encrypted ed25519 key file in 'filepath', decrypt it, and return + the key object in 'securesystemslib.formats.ED25519KEY_SCHEMA' format. Which cryptography library performs the cryptographic decryption is - determined by the string set in 'settings.ED25519_CRYPTO_LIBRARY'. PyCrypto - currently supported. + determined by the string set in 'settings.ED25519_CRYPTO_LIBRARY'. + PyCrypto and pyca/cryptography currently supported. The private key (may also contain the public part) is encrypted with AES 256 and CTR the mode of operation. The password is strengthened with @@ -495,15 +506,16 @@ def import_ed25519_privatekey_from_file(filepath, password=None): securesystemslib.exceptions.CryptoError, if 'filepath' cannot be decrypted. - securesystemslib.exceptions.UnsupportedLibraryError, if 'filepath' cannot be - decrypted due to an invalid configuration setting (i.e., invalid + securesystemslib.exceptions.UnsupportedLibraryError, if 'filepath' cannot + be decrypted due to an invalid configuration setting (i.e., invalid 'settings.py' setting). 'password' is used to decrypt the 'filepath' key file. - An ed25519 key object of the form: 'securesystemslib.formats.ED25519KEY_SCHEMA'. + An ed25519 key object of the form: + 'securesystemslib.formats.ED25519KEY_SCHEMA'. """ # Does 'filepath' have the correct format? @@ -619,8 +631,10 @@ def generate_and_write_ecdsa_keypair(filepath, password=None): # the keyid portion). keytype = ecdsa_key['keytype'] keyval = ecdsa_key['keyval'] + scheme = ecdsa_key['scheme'] + ecdsakey_metadata_format = \ - securesystemslib.keys.format_keyval_to_metadata(keytype, keyval, private=False) + securesystemslib.keys.format_keyval_to_metadata(keytype, scheme, keyval, private=False) # Write the public key, conformant to 'securesystemslib.formats.KEY_SCHEMA', to # '.pub'. diff --git a/securesystemslib/keys.py b/securesystemslib/keys.py index ba424ea9..48e49c91 100755 --- a/securesystemslib/keys.py +++ b/securesystemslib/keys.py @@ -163,7 +163,7 @@ logger = logging.getLogger('securesystemslib_keys') -def generate_rsa_key(bits=_DEFAULT_RSA_KEY_BITS): +def generate_rsa_key(bits=_DEFAULT_RSA_KEY_BITS, scheme='rsassa-pss-sha256'): """ Generate public and private RSA keys, with modulus length 'bits'. In @@ -172,6 +172,7 @@ def generate_rsa_key(bits=_DEFAULT_RSA_KEY_BITS): form: {'keytype': 'rsa', + 'scheme': 'rsassa-pss-sha256', 'keyid': keyid, 'keyval': {'public': '-----BEGIN RSA PUBLIC KEY----- ...', 'private': '-----BEGIN RSA PRIVATE KEY----- ...'}} @@ -189,6 +190,7 @@ def generate_rsa_key(bits=_DEFAULT_RSA_KEY_BITS): >>> rsa_key = generate_rsa_key(bits=2048) >>> securesystemslib.formats.RSAKEY_SCHEMA.matches(rsa_key) True + >>> public = rsa_key['keyval']['public'] >>> private = rsa_key['keyval']['private'] >>> securesystemslib.formats.PEMRSA_SCHEMA.matches(public) @@ -201,6 +203,10 @@ def generate_rsa_key(bits=_DEFAULT_RSA_KEY_BITS): The key size, or key length, of the RSA key. 'bits' must be 2048, or greater, and a multiple of 256. + scheme: + The signature scheme used by the key. It must be one of + ['rsassa-pss-sha256']. + securesystemslib.exceptions.FormatError, if 'bits' is improperly or invalid (i.e., not an integer and not at least 2048). @@ -228,6 +234,7 @@ def generate_rsa_key(bits=_DEFAULT_RSA_KEY_BITS): # with a minimum value of 2048. Raise 'securesystemslib.exceptions.FormatError' # if the check fails. securesystemslib.formats.RSAKEYBITS_SCHEMA.check_match(bits) + securesystemslib.formats.RSA_SIG_SCHEMA.check_match(scheme) # Raise 'securesystemslib.exceptions.UnsupportedLibraryError' if the following # libraries, specified in 'settings', are unsupported or unavailable: @@ -260,13 +267,14 @@ def generate_rsa_key(bits=_DEFAULT_RSA_KEY_BITS): # not included in the generation of the 'keyid' identifier. key_value = {'public': public, 'private': ''} - keyid = _get_keyid(keytype, key_value) + keyid = _get_keyid(keytype, scheme, key_value) # Build the 'rsakey_dict' dictionary. Update 'key_value' with the RSA # private key prior to adding 'key_value' to 'rsakey_dict'. key_value['private'] = private rsakey_dict['keytype'] = keytype + rsakey_dict['scheme'] = scheme rsakey_dict['keyid'] = keyid rsakey_dict['keyid_hash_algorithms'] = securesystemslib.settings.HASH_ALGORITHMS rsakey_dict['keyval'] = key_value @@ -277,7 +285,7 @@ def generate_rsa_key(bits=_DEFAULT_RSA_KEY_BITS): -def generate_ecdsa_key(algorithm='ecdsa-sha2-nistp256'): +def generate_ecdsa_key(scheme='ecdsa-sha2-nistp256'): """ Generate public and private ECDSA keys, with NIST P-256 + SHA256 (for @@ -286,27 +294,29 @@ def generate_ecdsa_key(algorithm='ecdsa-sha2-nistp256'): 'securesystemslib.formats.ECDSAKEY_SCHEMA' and has the form: {'keytype': 'ecdsa-sha2-nistp256', + 'scheme', 'ecdsa-sha2-nistp256', 'keyid': keyid, 'keyval': {'public': '', 'private': ''}} The public and private keys are strings in TODO format. - >>> ecdsa_key = generate_ecdsa_key(algorithm='ecdsa-sha2-nistp256') + >>> ecdsa_key = generate_ecdsa_key(scheme='ecdsa-sha2-nistp256') >>> securesystemslib.formats.ECDSAKEY_SCHEMA.matches(ecdsa_key) True - algorithm: + scheme: The ECDSA algorithm. By default, ECDSA NIST P-256 is used, with SHA256 for hashing. - securesystemslib.exceptions.FormatError, if 'algorithm' is improperly or invalid - (i.e., not one of the supported ECDSA algorithms). + securesystemslib.exceptions.FormatError, if 'algorithm' is improperly or + invalid (i.e., not one of the supported ECDSA algorithms). - securesystemslib.exceptions.UnsupportedLibraryError, if any of the cryptography - libraries specified in 'settings.py' are unsupported or unavailable. + securesystemslib.exceptions.UnsupportedLibraryError, if any of the + cryptography libraries specified in 'settings.py' are unsupported or + unavailable. None. @@ -319,7 +329,7 @@ def generate_ecdsa_key(algorithm='ecdsa-sha2-nistp256'): # Does 'algorithm' have the correct format? This check will ensure # 'algorithm is properly formatted and is a supported ECDSA algorithm. Raise # 'securesystemslib.exceptions.FormatError' if the check fails. - securesystemslib.formats.ECDSAALGORITHMS_SCHEMA.check_match(algorithm) + securesystemslib.formats.ECDSA_SIG_SCHEMA.check_match(scheme) # Raise 'securesystemslib.exceptions.UnsupportedLibraryError' if the following # libraries, specified in 'settings', are unsupported or unavailable: @@ -328,7 +338,7 @@ def generate_ecdsa_key(algorithm='ecdsa-sha2-nistp256'): # Begin building the ECDSA key dictionary. ecdsa_key = {} - keytype = algorithm + keytype = 'ecdsa-sha2-nistp256' public = None private = None @@ -336,7 +346,7 @@ def generate_ecdsa_key(algorithm='ecdsa-sha2-nistp256'): # libraries. if 'pyca-cryptography' in _available_crypto_libraries: public, private = \ - securesystemslib.ecdsa_keys.generate_public_and_private(algorithm) + securesystemslib.ecdsa_keys.generate_public_and_private(scheme) else: # pragma: no cover raise securesystemslib.exceptions.UnsupportedLibraryError('One of the' @@ -347,7 +357,7 @@ def generate_ecdsa_key(algorithm='ecdsa-sha2-nistp256'): # information is not included in the generation of the 'keyid' identifier. key_value = {'public': public, 'private': ''} - keyid = _get_keyid(keytype, key_value) + keyid = _get_keyid(keytype, scheme, key_value) # Build the 'ed25519_key' dictionary. Update 'key_value' with the Ed25519 # private key prior to adding 'key_value' to 'ed25519_key'. @@ -355,6 +365,7 @@ def generate_ecdsa_key(algorithm='ecdsa-sha2-nistp256'): key_value['private'] = private ecdsa_key['keytype'] = keytype + ecdsa_key['scheme'] = scheme ecdsa_key['keyid'] = keyid ecdsa_key['keyval'] = key_value @@ -369,7 +380,7 @@ def generate_ecdsa_key(algorithm='ecdsa-sha2-nistp256'): -def generate_ed25519_key(): +def generate_ed25519_key(scheme='ed25519'): """ Generate public and private ED25519 keys, both of length 32-bytes, although @@ -378,6 +389,7 @@ def generate_ed25519_key(): 'securesystemslib.formats.ED25519KEY_SCHEMA' and has the form: {'keytype': 'ed25519', + 'scheme': 'ed25519', 'keyid': 'f30a0870d026980100c0573bd557394f8c1bbd6...', 'keyval': {'public': '9ccf3f02b17f82febf5dd3bab878b767d8408...', 'private': 'ab310eae0e229a0eceee3947b6e0205dfab3...'}} @@ -391,7 +403,8 @@ def generate_ed25519_key(): 64 - None. + scheme: + The signature scheme used by the generated Ed25519 key. securesystemslib.exceptions.UnsupportedLibraryError, if an unsupported or @@ -406,9 +419,13 @@ def generate_ed25519_key(): Conforms to 'securesystemslib.formats.ED25519KEY_SCHEMA'. """ - # Raise 'securesystemslib.exceptions.UnsupportedLibraryError' if the following - # libraries, specified in 'settings', are unsupported or unavailable: - # 'securesystemslib.settings.ED25519_CRYPTO_LIBRARY'. + # Are the arguments properly formatted? If not, raise an + # 'securesystemslib.exceptions.FormatError' exceptions. + securesystemslib.formats.ED25519_SIG_SCHEMA.check_match(scheme) + + # Raise 'securesystemslib.exceptions.UnsupportedLibraryError' if the + # following libraries, specified in 'settings', are unsupported or + # unavailable: 'securesystemslib.settings.ED25519_CRYPTO_LIBRARY'. check_crypto_libraries(['ed25519']) # Begin building the Ed25519 key dictionary. @@ -435,13 +452,14 @@ def generate_ed25519_key(): # information is not included in the generation of the 'keyid' identifier. key_value = {'public': binascii.hexlify(public).decode(), 'private': ''} - keyid = _get_keyid(keytype, key_value) + keyid = _get_keyid(keytype, scheme, key_value) # Build the 'ed25519_key' dictionary. Update 'key_value' with the Ed25519 # private key prior to adding 'key_value' to 'ed25519_key'. key_value['private'] = binascii.hexlify(private).decode() ed25519_key['keytype'] = keytype + ed25519_key['scheme'] = scheme ed25519_key['keyid'] = keyid ed25519_key['keyid_hash_algorithms'] = securesystemslib.settings.HASH_ALGORITHMS ed25519_key['keyval'] = key_value @@ -452,7 +470,7 @@ def generate_ed25519_key(): -def format_keyval_to_metadata(keytype, key_value, private=False): +def format_keyval_to_metadata(keytype, scheme, key_value, private=False): """ Return a dictionary conformant to 'securesystemslib.formats.KEY_SCHEMA'. @@ -460,20 +478,23 @@ def format_keyval_to_metadata(keytype, key_value, private=False): returned has the form: {'keytype': keytype, + 'scheme' : scheme, 'keyval': {'public': '...', 'private': '...'}} or if 'private' is False: {'keytype': keytype, + 'scheme': scheme, 'keyval': {'public': '...', 'private': ''}} >>> ed25519_key = generate_ed25519_key() >>> key_val = ed25519_key['keyval'] >>> keytype = ed25519_key['keytype'] + >>> scheme = ed25519_key['scheme'] >>> ed25519_metadata = \ - format_keyval_to_metadata(keytype, key_val, private=True) + format_keyval_to_metadata(keytype, scheme, key_val, private=True) >>> securesystemslib.formats.KEY_SCHEMA.matches(ed25519_metadata) True @@ -481,6 +502,9 @@ def format_keyval_to_metadata(keytype, key_value, private=False): key_type: The 'rsa' or 'ed25519' strings. + scheme: + The signature scheme used by the key. + key_value: A dictionary containing a private and public keys. 'key_value' is of the form: @@ -496,8 +520,8 @@ def format_keyval_to_metadata(keytype, key_value, private=False): securesystemslib.exceptions.FormatError, if 'key_value' does not conform to - 'securesystemslib.formats.KEYVAL_SCHEMA', or if the private key is not present in - 'key_value' if requested by the caller via 'private'. + 'securesystemslib.formats.KEYVAL_SCHEMA', or if the private key is not + present in 'key_value' if requested by the caller via 'private'. None. @@ -512,6 +536,9 @@ def format_keyval_to_metadata(keytype, key_value, private=False): # Raise 'securesystemslib.exceptions.FormatError' if the check fails. securesystemslib.formats.KEYTYPE_SCHEMA.check_match(keytype) + # Does 'scheme' have the correct format? + securesystemslib.formats.SIG_SCHEME_SCHEMA.check_match(scheme) + # Does 'key_value' have the correct format? securesystemslib.formats.KEYVAL_SCHEMA.check_match(key_value) @@ -525,12 +552,13 @@ def format_keyval_to_metadata(keytype, key_value, private=False): ' is missing from: ' + repr(key_value)) else: - return {'keytype': keytype, 'keyval': key_value} + return {'keytype': keytype, 'scheme': scheme, 'keyval': key_value} else: public_key_value = {'public': key_value['public']} return {'keytype': keytype, + 'scheme': scheme, 'keyid_hash_algorithms': securesystemslib.settings.HASH_ALGORITHMS, 'keyval': public_key_value} @@ -548,6 +576,7 @@ def format_metadata_to_key(key_metadata): has the form: {'keytype': keytype, + 'scheme': scheme, 'keyid': 'f30a0870d026980100c0573bd557394f8c1bbd6...', 'keyval': {'public': '...', 'private': '...'}} @@ -562,8 +591,9 @@ def format_metadata_to_key(key_metadata): >>> ed25519_key = generate_ed25519_key() >>> key_val = ed25519_key['keyval'] >>> keytype = ed25519_key['keytype'] + >>> scheme = ed25519_key['scheme'] >>> ed25519_metadata = \ - format_keyval_to_metadata(keytype, key_val, private=True) + format_keyval_to_metadata(keytype, scheme, key_val, private=True) >>> ed25519_key_2, junk = format_metadata_to_key(ed25519_metadata) >>> securesystemslib.formats.ED25519KEY_SCHEMA.matches(ed25519_key_2) True @@ -576,6 +606,7 @@ def format_metadata_to_key(key_metadata): 'securesystemslib.formats.KEY_SCHEMA'. It has the form: {'keytype': '...', + 'scheme': scheme, 'keyval': {'public': '...', 'private': '...'}} @@ -600,21 +631,23 @@ def format_metadata_to_key(key_metadata): # Construct the dictionary to be returned. key_dict = {} keytype = key_metadata['keytype'] + scheme = key_metadata['scheme'] key_value = key_metadata['keyval'] # Convert 'key_value' to 'securesystemslib.formats.KEY_SCHEMA' and generate # its hash The hash is in hexdigest form. - default_keyid = _get_keyid(keytype, key_value) + default_keyid = _get_keyid(keytype, scheme, key_value) keyids = set() keyids.add(default_keyid) for hash_algorithm in securesystemslib.settings.HASH_ALGORITHMS: - keyid = _get_keyid(keytype, key_value, hash_algorithm) + keyid = _get_keyid(keytype, scheme, key_value, hash_algorithm) keyids.add(keyid) # All the required key values gathered. Build 'key_dict'. # 'keyid_hash_algorithms' key_dict['keytype'] = keytype + key_dict['scheme'] = scheme key_dict['keyid'] = default_keyid key_dict['keyid_hash_algorithms'] = securesystemslib.settings.HASH_ALGORITHMS key_dict['keyval'] = key_value @@ -623,13 +656,13 @@ def format_metadata_to_key(key_metadata): -def _get_keyid(keytype, key_value, hash_algorithm = 'sha256'): +def _get_keyid(keytype, scheme, key_value, hash_algorithm = 'sha256'): """Return the keyid of 'key_value'.""" # 'keyid' will be generated from an object conformant to KEY_SCHEMA, # which is the format Metadata files (e.g., root.json) store keys. # 'format_keyval_to_metadata()' returns the object needed by _get_keyid(). - key_meta = format_keyval_to_metadata(keytype, key_value, private=False) + key_meta = format_keyval_to_metadata(keytype, scheme, key_value, private=False) # Convert the key to JSON Canonical format, suitable for adding # to digest objects. @@ -749,13 +782,12 @@ def create_signature(key_dict, data): Return a signature dictionary of the form: {'keyid': 'f30a0870d026980100c0573bd557394f8c1bbd6...', - 'method': '...', 'sig': '...'}. The signing process will use the private key in key_dict['keyval']['private'] and 'data' to generate the signature. - The following signature methods are supported: + The following signature schemes are supported: 'RSASSA-PSS' RFC3447 - RSASSA-PSS @@ -790,6 +822,7 @@ def create_signature(key_dict, data): form: {'keytype': 'rsa', + 'scheme': 'rsassa-pss-sha256', 'keyid': 'f30a0870d026980100c0573bd557394f8c1bbd6...', 'keyval': {'public': '-----BEGIN RSA PUBLIC KEY----- ...', 'private': '-----BEGIN RSA PRIVATE KEY----- ...'}} @@ -800,7 +833,8 @@ def create_signature(key_dict, data): Data object used by create_signature() to generate the signature. - securesystemslib.exceptions.FormatError, if 'key_dict' is improperly formatted. + securesystemslib.exceptions.FormatError, if 'key_dict' is improperly + formatted. securesystemslib.exceptions.UnsupportedLibraryError, if an unsupported or unavailable library is detected. @@ -808,11 +842,12 @@ def create_signature(key_dict, data): TypeError, if 'key_dict' contains an invalid keytype. - The cryptography library specified in 'settings' called to perform the + The cryptography library specified in 'settings' is called to perform the actual signing routine. - A signature dictionary conformant to 'securesystemslib_format.SIGNATURE_SCHEMA'. + A signature dictionary conformant to + 'securesystemslib_format.SIGNATURE_SCHEMA'. """ # Does 'key_dict' have the correct format? @@ -828,16 +863,17 @@ def create_signature(key_dict, data): check_crypto_libraries([key_dict['keytype']]) # Signing the 'data' object requires a private key. - # 'RSASSA-PSS' and 'ed25519' are the only signing methods currently - # supported. RSASSA-PSS keys and signatures can be generated and verified by - # the PyCrypto and 'cryptography' modules, and Ed25519's by PyNaCl and PyCA's - # optimized, pure python implementation of Ed25519. + # 'rsassa-pss-sha256', 'ed25519', and 'ecdsa-sha2-nistp256' are the only + # signing schemess currently supported. RSASSA-PSS keys and signatures can be + # generated and verified by the PyCrypto and 'cryptography' modules, and + # Ed25519's by PyNaCl and PyCA's optimized, pure python implementation of + # Ed25519. signature = {} keytype = key_dict['keytype'] + scheme = key_dict['scheme'] public = key_dict['keyval']['public'] private = key_dict['keyval']['private'] keyid = key_dict['keyid'] - method = None sig = None # Convert 'data' to canonical JSON format so that repeatable signatures are @@ -849,24 +885,29 @@ def create_signature(key_dict, data): # Call the appropriate cryptography libraries for the supported key types, # otherwise raise an exception. if keytype == 'rsa': - if _RSA_CRYPTO_LIBRARY == 'pycrypto': - sig, method = securesystemslib.pycrypto_keys.create_rsa_signature(private, - data.encode('utf-8')) + if scheme == 'rsassa-pss-sha256': + if _RSA_CRYPTO_LIBRARY == 'pycrypto': + sig, scheme = securesystemslib.pycrypto_keys.create_rsa_signature(private, + data.encode('utf-8'), scheme) - elif _RSA_CRYPTO_LIBRARY == 'pyca-cryptography': - sig, method = securesystemslib.pyca_crypto_keys.create_rsa_signature(private, - data.encode('utf-8')) + elif _RSA_CRYPTO_LIBRARY == 'pyca-cryptography': + sig, scheme = securesystemslib.pyca_crypto_keys.create_rsa_signature(private, + data.encode('utf-8'), scheme) - else: # pragma: no cover - raise securesystemslib.exceptions.UnsupportedLibraryError('Unsupported' - ' "settings.RSA_CRYPTO_LIBRARY": ' + repr(_RSA_CRYPTO_LIBRARY) + '.') + else: # pragma: no cover + raise securesystemslib.exceptions.UnsupportedLibraryError('Unsupported' + ' "settings.RSA_CRYPTO_LIBRARY": ' + repr(_RSA_CRYPTO_LIBRARY) + '.') + + else: + raise securesystemslib.exceptions.UnsupportedAlgorithmError('Unsupported' + ' RSA signature algorithm specified: ' + repr(scheme)) elif keytype == 'ed25519': public = binascii.unhexlify(public.encode('utf-8')) private = binascii.unhexlify(private.encode('utf-8')) if 'pynacl' in _available_crypto_libraries: - sig, method = securesystemslib.ed25519_keys.create_signature(public, private, - data.encode('utf-8')) + sig, scheme = securesystemslib.ed25519_keys.create_signature(public, private, + data.encode('utf-8'), scheme) else: # pragma: no cover raise securesystemslib.exceptions.UnsupportedLibraryError('The required' @@ -874,8 +915,8 @@ def create_signature(key_dict, data): elif keytype == 'ecdsa-sha2-nistp256': if _ECDSA_CRYPTO_LIBRARY == 'pyca-cryptography': - sig, method = securesystemslib.ecdsa_keys.create_signature(public, private, - data.encode('utf-8')) + sig, scheme = securesystemslib.ecdsa_keys.create_signature(public, private, + data.encode('utf-8'), scheme) else: # pragma: no cover raise securesystemslib.exceptions.UnsupportedLibraryError('Unsupported' @@ -888,7 +929,6 @@ def create_signature(key_dict, data): # Build the signature dictionary to be returned. # The hexadecimal representation of 'sig' is stored in the signature. signature['keyid'] = keyid - signature['method'] = method signature['sig'] = binascii.hexlify(sig).decode() return signature @@ -902,8 +942,8 @@ def verify_signature(key_dict, signature, data): Determine whether the private key belonging to 'key_dict' produced 'signature'. verify_signature() will use the public key found in - 'key_dict', the 'method' and 'sig' objects contained in 'signature', - and 'data' to complete the verification. + 'key_dict', the 'sig' objects contained in 'signature', and 'data' to + complete the verification. >>> ed25519_key = generate_ed25519_key() >>> data = 'The quick brown fox jumps over the lazy dog' @@ -931,6 +971,7 @@ def verify_signature(key_dict, signature, data): If 'key_dict' is an RSA key, it has the form: {'keytype': 'rsa', + 'scheme': 'rsassa-pss-sha256', 'keyid': 'f30a0870d026980100c0573bd557394f8c1bbd6...', 'keyval': {'public': '-----BEGIN RSA PUBLIC KEY----- ...', 'private': '-----BEGIN RSA PRIVATE KEY----- ...'}} @@ -942,7 +983,6 @@ def verify_signature(key_dict, signature, data): 'signature' has the form: {'keyid': 'f30a0870d026980100c0573bd557394f8c1bbd6...', - 'method': 'method', 'sig': sig}. Conformant to 'securesystemslib.formats.SIGNATURE_SCHEMA'. @@ -958,8 +998,8 @@ def verify_signature(key_dict, signature, data): securesystemslib.exceptions.UnsupportedLibraryError, if an unsupported or unavailable library is detected. - securesystemslib.exceptions.UnknownMethodError. Raised if the signing method - used by 'signature' is not one supported. + securesystemslib.exceptions.UnsupportedAlgorithmError. Raised if the + signature scheme specified in 'key_dict' is not supported. The cryptography library specified in 'settings' called to do the actual @@ -982,11 +1022,11 @@ def verify_signature(key_dict, signature, data): # (i.e., rsakey_dict['keyval']['public']), verify whether 'signature' # was produced by key_dict's corresponding private key # key_dict['keyval']['private']. - method = signature['method'] sig = signature['sig'] sig = binascii.unhexlify(sig.encode('utf-8')) public = key_dict['keyval']['public'] keytype = key_dict['keytype'] + scheme = key_dict['scheme'] valid_signature = False # Convert 'data' to canonical JSON format so that repeatable signatures are @@ -998,59 +1038,74 @@ def verify_signature(key_dict, signature, data): # Call the appropriate cryptography libraries for the supported key types, # otherwise raise an exception. if keytype == 'rsa': - if _RSA_CRYPTO_LIBRARY == 'pycrypto': - if 'pycrypto' not in _available_crypto_libraries: # pragma: no cover - raise securesystemslib.exceptions.UnsupportedLibraryError('Metadata' - ' downloaded from the remote' - ' repository listed an RSA signature. "pycrypto" was set' - ' (in settings.py) to generate RSA signatures, but the PyCrypto' - ' library is not installed. \n$ pip install PyCrypto, or you can' - ' try switching your configuration (settings.py) to use' - ' pyca-cryptography if that is available instead.') - - else: - valid_signature = securesystemslib.pycrypto_keys.verify_rsa_signature(sig, method, - public, data) - elif _RSA_CRYPTO_LIBRARY == 'pyca-cryptography': - if 'pyca-cryptography' not in _available_crypto_libraries: # pragma: no cover - raise securesystemslib.exceptions.UnsupportedLibraryError('Metadata' - ' downloaded from the remote' - ' repository listed an RSA signature. "pyca-cryptography" was set' - ' (in settings.py) to generate RSA signatures, but the "cryptography"' - ' library is not installed. \n$ pip install cryptography,' - ' or you can try switching your configuration' - ' (securesystemslib/settings.py) to use PyCrypto if that is' - ' available instead.') - - else: - valid_signature = securesystemslib.pyca_crypto_keys.verify_rsa_signature(sig, - method, public, data) + if scheme == 'rsassa-pss-sha256': + if _RSA_CRYPTO_LIBRARY == 'pycrypto': + if 'pycrypto' not in _available_crypto_libraries: # pragma: no cover + raise securesystemslib.exceptions.UnsupportedLibraryError('Metadata' + ' downloaded from the remote' + ' repository listed an RSA signature. "pycrypto" was set' + ' (in settings.py) to generate RSA signatures, but the PyCrypto' + ' library is not installed. \n$ pip install PyCrypto, or you can' + ' try switching your configuration (settings.py) to use' + ' pyca-cryptography if that is available instead.') + + else: + valid_signature = securesystemslib.pycrypto_keys.verify_rsa_signature(sig, scheme, + public, data) + elif _RSA_CRYPTO_LIBRARY == 'pyca-cryptography': + if 'pyca-cryptography' not in _available_crypto_libraries: # pragma: no cover + raise securesystemslib.exceptions.UnsupportedLibraryError('Metadata' + ' downloaded from the remote' + ' repository listed an RSA signature. "pyca-cryptography" was set' + ' (in settings.py) to generate RSA signatures, but the "cryptography"' + ' library is not installed. \n$ pip install cryptography,' + ' or you can try switching your configuration' + ' (securesystemslib/settings.py) to use PyCrypto if that is' + ' available instead.') + + else: + valid_signature = securesystemslib.pyca_crypto_keys.verify_rsa_signature(sig, + scheme, public, data) - else: # pragma: no cover - raise securesystemslib.exceptions.UnsupportedLibraryError('Unsupported' - ' "settings.RSA_CRYPTO_LIBRARY": ' + repr(_RSA_CRYPTO_LIBRARY) + '.') + else: # pragma: no cover + raise securesystemslib.exceptions.UnsupportedLibraryError('Unsupported' + ' "settings.RSA_CRYPTO_LIBRARY": ' + repr(_RSA_CRYPTO_LIBRARY) + '.') + + else: + raise securesystemslib.exceptions.UnsupportedAlgorithmError('Unsupported' + ' signature scheme is specified: ' + repr(scheme)) elif keytype == 'ed25519': - public = binascii.unhexlify(public.encode('utf-8')) - if _ED25519_CRYPTO_LIBRARY == 'pynacl' or \ - 'pynacl' in _available_crypto_libraries: - valid_signature = securesystemslib.ed25519_keys.verify_signature(public, - method, sig, data, - use_pynacl=True) + if scheme == 'ed25519': + public = binascii.unhexlify(public.encode('utf-8')) + if _ED25519_CRYPTO_LIBRARY == 'pynacl' or \ + 'pynacl' in _available_crypto_libraries: + valid_signature = securesystemslib.ed25519_keys.verify_signature(public, + scheme, sig, data, + use_pynacl=True) + + # Fall back to the optimized pure python implementation of ed25519. + else: # pragma: no cover + valid_signature = securesystemslib.ed25519_keys.verify_signature(public, + scheme, sig, data, + use_pynacl=False) + else: + raise securesystemslib.exceptions.UnsupportedAlgorithmError('Unsupported' + ' signature scheme is specified: ' + repr(scheme)) - # Fall back to the optimized pure python implementation of ed25519. - else: # pragma: no cover - valid_signature = securesystemslib.ed25519_keys.verify_signature(public, - method, sig, data, - use_pynacl=False) elif keytype == 'ecdsa-sha2-nistp256': - if _ECDSA_CRYPTO_LIBRARY in _available_crypto_libraries: - valid_signature = securesystemslib.ecdsa_keys.verify_signature(public, - method, sig, data) + if scheme == 'ecdsa-sha2-nistp256': + if _ECDSA_CRYPTO_LIBRARY in _available_crypto_libraries: + valid_signature = securesystemslib.ecdsa_keys.verify_signature(public, + scheme, sig, data) - else: # pragma: no cover - raise securesystemslib.exceptions.UnsupportedLibraryError('Unsupported' - ' "settings.ECDSA_CRYPTO_LIBRARY": ' + repr(_ECDSA_CRYPTO_LIBRARY) + '.') + else: # pragma: no cover + raise securesystemslib.exceptions.UnsupportedLibraryError('Unsupported' + ' "settings.ECDSA_CRYPTO_LIBRARY": ' + repr(_ECDSA_CRYPTO_LIBRARY) + '.') + + else: + raise securesystemslib.exceptions.UnsupportedAlgorithmError('Unsupported' + ' signature scheme is specified: ' + repr(scheme)) # 'securesystemslib.formats.ANYKEY_SCHEMA' should detect invalid key types. else: # pragma: no cover @@ -1062,7 +1117,7 @@ def verify_signature(key_dict, signature, data): -def import_rsakey_from_private_pem(pem, password=None): +def import_rsakey_from_private_pem(pem, scheme='rsassa-pss-sha256', password=None): """ Import the private RSA key stored in 'pem', and generate its public key @@ -1071,6 +1126,7 @@ def import_rsakey_from_private_pem(pem, password=None): conforms to 'securesystemslib.formats.RSAKEY_SCHEMA' and has the form: {'keytype': 'rsa', + 'scheme': 'rsassa-pss-sha256', 'keyid': keyid, 'keyval': {'public': '-----BEGIN RSA PUBLIC KEY----- ...', 'private': '-----BEGIN RSA PRIVATE KEY----- ...'}} @@ -1078,10 +1134,11 @@ def import_rsakey_from_private_pem(pem, password=None): The private key is a string in PEM format. >>> rsa_key = generate_rsa_key() + >>> scheme = rsa_key['scheme'] >>> private = rsa_key['keyval']['private'] >>> passphrase = 'secret' >>> encrypted_pem = create_rsa_encrypted_pem(private, passphrase) - >>> rsa_key2 = import_rsakey_from_private_pem(encrypted_pem, passphrase) + >>> rsa_key2 = import_rsakey_from_private_pem(encrypted_pem, scheme, passphrase) >>> securesystemslib.formats.RSAKEY_SCHEMA.matches(rsa_key) True >>> securesystemslib.formats.RSAKEY_SCHEMA.matches(rsa_key2) @@ -1092,6 +1149,9 @@ def import_rsakey_from_private_pem(pem, password=None): A string in PEM format. The private key is extracted and returned in an rsakey object. + scheme: + The signature scheme used by the imported key. + password: (optional) The password, or passphrase, to decrypt the private part of the RSA key if it is encrypted. 'password' is not used directly as the encryption @@ -1117,6 +1177,9 @@ def import_rsakey_from_private_pem(pem, password=None): # 'securesystemslib.formats.PEMRSA_SCHEMA'. securesystemslib.formats.PEMRSA_SCHEMA.check_match(pem) + # Is 'scheme' properly formatted? + securesystemslib.formats.RSA_SIG_SCHEMA.check_match(scheme) + if password is not None: securesystemslib.formats.PASSWORD_SCHEMA.check_match(password) @@ -1161,13 +1224,14 @@ def import_rsakey_from_private_pem(pem, password=None): # information is not included in the generation of the 'keyid' identifier. key_value = {'public': public, 'private': ''} - keyid = _get_keyid(keytype, key_value) + keyid = _get_keyid(keytype, scheme, key_value) # Build the 'rsakey_dict' dictionary. Update 'key_value' with the RSA # private key prior to adding 'key_value' to 'rsakey_dict'. key_value['private'] = private rsakey_dict['keytype'] = keytype + rsakey_dict['scheme'] = scheme rsakey_dict['keyid'] = keyid rsakey_dict['keyval'] = key_value @@ -1177,7 +1241,7 @@ def import_rsakey_from_private_pem(pem, password=None): -def import_rsakey_from_public_pem(pem): +def import_rsakey_from_public_pem(pem, scheme='rsassa-pss-sha256'): """ Generate an RSA key object from 'pem'. In addition, a keyid identifier for @@ -1223,6 +1287,9 @@ def import_rsakey_from_public_pem(pem): # Raise 'securesystemslib.exceptions.FormatError' if the check fails. securesystemslib.formats.PEMRSA_SCHEMA.check_match(pem) + # Does 'scheme' have the correct format? + securesystemslib.formats.RSA_SIG_SCHEMA.check_match(scheme) + # Ensure the PEM string has a public header and footer. Although a simple # validation of 'pem' is performed here, a fully valid PEM string is needed # later to successfully verify signatures. Performing stricter validation of @@ -1243,9 +1310,10 @@ def import_rsakey_from_public_pem(pem): # information is not included in the generation of the 'keyid' identifier. key_value = {'public': public_pem, 'private': ''} - keyid = _get_keyid(keytype, key_value) + keyid = _get_keyid(keytype, scheme, key_value) rsakey_dict['keytype'] = keytype + rsakey_dict['scheme'] = scheme rsakey_dict['keyid'] = keyid rsakey_dict['keyval'] = key_value @@ -1260,7 +1328,7 @@ def import_rsakey_from_public_pem(pem): -def import_rsakey_from_pem(pem): +def import_rsakey_from_pem(pem, scheme='rsassa-pss-sha256'): """ Import either a public or private PEM. In contrast to the other explicit @@ -1272,6 +1340,9 @@ def import_rsakey_from_pem(pem): pem: A string in PEM format. + scheme: + The signature scheme used by the imported key. + securesystemslib.exceptions.FormatError, if 'pem' is improperly formatted. @@ -1289,6 +1360,9 @@ def import_rsakey_from_pem(pem): # Raise 'securesystemslib.exceptions.FormatError' if the check fails. securesystemslib.formats.PEMRSA_SCHEMA.check_match(pem) + # Is 'scheme' properly formatted? + securesystemslib.formats.RSA_SIG_SCHEMA.check_match(scheme) + public_pem = '' private_pem = '' @@ -1319,9 +1393,10 @@ def import_rsakey_from_pem(pem): # PEM with only a public key. key_value = {'public': public_pem, 'private': ''} - keyid = _get_keyid(keytype, key_value) + keyid = _get_keyid(keytype, scheme, key_value) rsakey_dict['keytype'] = keytype + rsakey_dict['scheme'] = scheme rsakey_dict['keyid'] = keyid rsakey_dict['keyval'] = key_value @@ -1553,10 +1628,10 @@ def decrypt_key(encrypted_key, passphrase): encrypted_key: - An encrypted key (additional data is also included, such as salt, - number of password iterations used for the derived encryption key, etc) - of the form 'securesystemslib.formats.ENCRYPTEDKEY_SCHEMA'. 'encrypted_key' - should have been generated with encrypted_key(). + An encrypted key (additional data is also included, such as salt, number + of password iterations used for the derived encryption key, etc) of the + form 'securesystemslib.formats.ENCRYPTEDKEY_SCHEMA'. 'encrypted_key' + should have been generated with encrypt_key(). password: The password, or passphrase, to decrypt 'encrypted_key'. 'password' is @@ -1826,7 +1901,7 @@ def is_pem_private(pem, keytype='rsa'): -def import_ecdsakey_from_private_pem(pem, password=None): +def import_ecdsakey_from_private_pem(pem, scheme='ecdsa-sha2-nistp256', password=None): """ Import the private ECDSA key stored in 'pem', and generate its public key @@ -1835,6 +1910,7 @@ def import_ecdsakey_from_private_pem(pem, password=None): conforms to: {'keytype': 'ecdsa-sha2-nistp256', + 'scheme': 'ecdsa-sha2-nistp256', 'keyid': keyid, 'keyval': {'public': '-----BEGIN PUBLIC KEY----- ... -----END PUBLIC KEY-----', 'private': '-----BEGIN EC PRIVATE KEY----- ... -----END EC PRIVATE KEY-----'}} @@ -1852,6 +1928,9 @@ def import_ecdsakey_from_private_pem(pem, password=None): A string in PEM format. The private key is extracted and returned in an ecdsakey object. + scheme: + The signature scheme used by the imported key. + password: (optional) The password, or passphrase, to decrypt the private part of the ECDSA key if it is encrypted. 'password' is not used directly as the encryption @@ -1877,6 +1956,9 @@ def import_ecdsakey_from_private_pem(pem, password=None): # 'securesystemslib.formats.ECDSARSA_SCHEMA'. securesystemslib.formats.PEMECDSA_SCHEMA.check_match(pem) + # Is 'scheme' properly formatted? + securesystemslib.formats.ECDSA_SIG_SCHEMA.check_match(scheme) + if password is not None: securesystemslib.formats.PASSWORD_SCHEMA.check_match(password) @@ -1898,13 +1980,14 @@ def import_ecdsakey_from_private_pem(pem, password=None): # information is not included in the generation of the 'keyid' identifier. key_value = {'public': public, 'private': ''} - keyid = _get_keyid(keytype, key_value) + keyid = _get_keyid(keytype, scheme, key_value) # Build the 'ecdsakey_dict' dictionary. Update 'key_value' with the ECDSA # private key prior to adding 'key_value' to 'ecdsakey_dict'. key_value['private'] = private ecdsakey_dict['keytype'] = keytype + ecdsakey_dict['scheme'] = scheme ecdsakey_dict['keyid'] = keyid ecdsakey_dict['keyval'] = key_value @@ -1919,7 +2002,7 @@ def import_ecdsakey_from_private_pem(pem, password=None): -def import_ecdsakey_from_public_pem(pem): +def import_ecdsakey_from_public_pem(pem, scheme='ecdsa-sha2-nistp256'): """ Generate an ECDSA key object from 'pem'. In addition, a keyid identifier @@ -1927,6 +2010,7 @@ def import_ecdsakey_from_public_pem(pem): 'securesystemslib.formats.ECDSAKEY_SCHEMA' and has the form: {'keytype': 'ecdsa-sha2-nistp256', + 'scheme': 'ecdsa-sha2-nistp256', 'keyid': keyid, 'keyval': {'public': '-----BEGIN PUBLIC KEY----- ...', 'private': ''}} @@ -1936,7 +2020,8 @@ def import_ecdsakey_from_public_pem(pem): >>> ecdsa_key = generate_ecdsa_key() >>> public = ecdsa_key['keyval']['public'] >>> ecdsa_key['keyval']['private'] = '' - >>> ecdsa_key2 = import_ecdsakey_from_public_pem(public) + >>> scheme = ecdsa_key['scheme'] + >>> ecdsa_key2 = import_ecdsakey_from_public_pem(public, scheme) >>> securesystemslib.formats.ECDSAKEY_SCHEMA.matches(ecdsa_key) True >>> securesystemslib.formats.ECDSAKEY_SCHEMA.matches(ecdsa_key2) @@ -1946,6 +2031,9 @@ def import_ecdsakey_from_public_pem(pem): pem: A string in PEM format (it should contain a public ECDSA key). + scheme: + The signature scheme used by the imported key. + securesystemslib.exceptions.FormatError, if 'pem' is improperly formatted. @@ -1965,6 +2053,9 @@ def import_ecdsakey_from_public_pem(pem): # Raise 'securesystemslib.exceptions.FormatError' if the check fails. securesystemslib.formats.PEMECDSA_SCHEMA.check_match(pem) + # Is 'scheme' properly formatted? + securesystemslib.formats.ECDSA_SIG_SCHEMA.check_match(scheme) + # Ensure the PEM string has a public header and footer. Although a simple # validation of 'pem' is performed here, a fully valid PEM string is needed # later to successfully verify signatures. Performing stricter validation of @@ -1985,9 +2076,10 @@ def import_ecdsakey_from_public_pem(pem): # information is not included in the generation of the 'keyid' identifier. key_value = {'public': public_pem, 'private': ''} - keyid = _get_keyid(keytype, key_value) + keyid = _get_keyid(keytype, scheme, key_value) ecdsakey_dict['keytype'] = keytype + ecdsakey_dict['scheme'] = scheme ecdsakey_dict['keyid'] = keyid ecdsakey_dict['keyval'] = key_value @@ -2002,7 +2094,7 @@ def import_ecdsakey_from_public_pem(pem): -def import_ecdsakey_from_pem(pem): +def import_ecdsakey_from_pem(pem, scheme='ecdsa-sha2-nistp256'): """ Import either a public or private ECDSA PEM. In contrast to the other @@ -2014,6 +2106,8 @@ def import_ecdsakey_from_pem(pem): pem: A string in PEM format. + scheme: + The signature scheme used by the imported key. securesystemslib.exceptions.FormatError, if 'pem' is improperly formatted. @@ -2031,6 +2125,9 @@ def import_ecdsakey_from_pem(pem): # Raise 'securesystemslib.exceptions.FormatError' if the check fails. securesystemslib.formats.PEMECDSA_SCHEMA.check_match(pem) + # Is 'scheme' properly formatted? + securesystemslib.formats.ECDSA_SIG_SCHEMA.check_match(scheme) + public_pem = '' private_pem = '' @@ -2061,9 +2158,10 @@ def import_ecdsakey_from_pem(pem): # PEM with only a public key. key_value = {'public': public_pem, 'private': ''} - keyid = _get_keyid(keytype, key_value) + keyid = _get_keyid(keytype, scheme, key_value) ecdsakey_dict['keytype'] = keytype + ecdsakey_dict['scheme'] = scheme ecdsakey_dict['keyid'] = keyid ecdsakey_dict['keyval'] = key_value diff --git a/securesystemslib/pyca_crypto_keys.py b/securesystemslib/pyca_crypto_keys.py index b292ae3d..ceef7c05 100755 --- a/securesystemslib/pyca_crypto_keys.py +++ b/securesystemslib/pyca_crypto_keys.py @@ -46,8 +46,8 @@ iterations. PEM-encrypted RSA key files use the Triple Data Encryption Algorithm (3DES), - and Cipher-block chaining (CBC) for the mode of operation. Password-Based Key - Derivation Function 1 (PBKF1) + MD5. + and Cipher-block chaining (CBC) for the mode of operation. Password-Based + Key Derivation Function 1 (PBKF1) + MD5. """ # Help with Python 3 compatibility, where the print statement is a function, an @@ -101,9 +101,9 @@ from cryptography.hazmat.primitives.asymmetric import padding # Import pyca/cryptography's Key Derivation Function (KDF) module. -# 'securesystemslib.keys.py' needs this module to derive a secret key according to the -# Password-Based Key Derivation Function 2 specification. The derived key is -# used as the symmetric key to encrypt TUF key information. +# 'securesystemslib.keys.py' needs this module to derive a secret key according +# to the Password-Based Key Derivation Function 2 specification. The derived +# key is used as the symmetric key to encrypt TUF key information. # PKCS#5 v2.0 PBKDF2 specification: http://tools.ietf.org/html/rfc2898#section-5.2 from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC @@ -235,11 +235,11 @@ def generate_rsa_public_and_private(bits=_DEFAULT_RSA_KEY_BITS): -def create_rsa_signature(private_key, data): +def create_rsa_signature(private_key, data, scheme='rsassa-pss-sha256'): """ - Generate an RSASSA-PSS signature. The signature, and the method (signature - algorithm) used, is returned as a (signature, method) tuple. + Generate a 'scheme' signature. The signature, and the signature scheme + used, is returned as a (signature, scheme) tuple. The signing process will use 'private_key' to generate the signature of 'data'. @@ -249,10 +249,11 @@ def create_rsa_signature(private_key, data): >>> public, private = generate_rsa_public_and_private(2048) >>> data = 'The quick brown fox jumps over the lazy dog'.encode('utf-8') - >>> signature, method = create_rsa_signature(private, data) - >>> securesystemslib.formats.NAME_SCHEMA.matches(method) + >>> scheme = 'rsassa-pss-sha256' + >>> signature, scheme = create_rsa_signature(private, data, scheme) + >>> securesystemslib.formats.NAME_SCHEMA.matches(scheme) True - >>> method == 'RSASSA-PSS' + >>> scheme == 'rsassa-pss-sha256' True >>> securesystemslib.formats.PYCACRYPTOSIGNATURE_SCHEMA.matches(signature) True @@ -264,113 +265,130 @@ def create_rsa_signature(private_key, data): data: Data (string) used by create_rsa_signature() to generate the signature. + scheme: + The signature scheme used to generate the signature. + - securesystemslib.exceptions.FormatError, if 'private_key' is improperly formatted. + securesystemslib.exceptions.FormatError, if 'private_key' is improperly + formatted. ValueError, if 'private_key' is unset. - securesystemslib.exceptions.CryptoError, if the signature cannot be generated. + securesystemslib.exceptions.CryptoError, if the signature cannot be + generated. pyca/cryptography's 'RSAPrivateKey.signer()' called to generate the signature. - A (signature, method) tuple, where the signature is a string and the method - is 'RSASSA-PSS'. + A (signature, scheme) tuple, where the signature is a string and the scheme + is one of the supported RSA signature schemes. For example: + 'rsassa-pss-sha256'. """ # Does the arguments have the correct format? - # This check will ensure the arguments conform to 'securesystemslib.formats.PEMRSA_SCHEMA'. - # and 'securesystemslib.formats.DATA_SCHEMA' - # Raise 'securesystemslib.exceptions.FormatError' if the checks fail. + # This check will ensure the arguments conform to + # 'securesystemslib.formats.PEMRSA_SCHEMA'. and + # 'securesystemslib.formats.DATA_SCHEMA' Raise + # 'securesystemslib.exceptions.FormatError' if the checks fail. securesystemslib.formats.PEMRSA_SCHEMA.check_match(private_key) securesystemslib.formats.DATA_SCHEMA.check_match(data) + securesystemslib.formats.RSA_SIG_SCHEMA.check_match(scheme) - # Signing 'data' requires a private key. The 'RSASSA-PSS' signing method is - # the only method currently supported. - method = 'RSASSA-PSS' + # Signing 'data' requires a private key. 'rsassa-pss-sha256' is the only + # signature scheme currently supported. signature = None - # Verify the signature, but only if the private key has been set. The private - # key is a NULL string if unset. Although it may be clearer to explicitly - # check that 'private_key' is not '', we can/should check for a value and not - # compare identities with the 'is' keyword. Up to this point 'private_key' - # has variable size and can be an empty string. + # Verify the signature, but only if the private key has been set. The + # private key is a NULL string if unset. Although it may be clearer to + # explicitly check that 'private_key' is not '', we can/should check for a + # value and not compare identities with the 'is' keyword. Up to this point + # 'private_key' has variable size and can be an empty string. if len(private_key): - # Generate an RSSA-PSS signature. Raise 'securesystemslib.exceptions.CryptoError' for any of the - # expected exceptions raised by pyca/cryptography. - try: - # 'private_key' (in PEM format) must first be converted to a - # pyca/cryptography private key object before a signature can be - # generated. - private_key_object = load_pem_private_key(private_key.encode('utf-8'), - password=None, - backend=default_backend()) - - # Calculate the SHA256 hash of 'data' and generate the hash's PKCS1-PSS - # signature. - rsa_signer = \ - private_key_object.signer(padding.PSS(mgf=padding.MGF1(hashes.SHA256()), - salt_length=hashes.SHA256().digest_size), hashes.SHA256()) - - # If the PEM data could not be decrypted, or if its structure could not be - # decoded successfully. - except ValueError: #pragma: no cover - raise securesystemslib.exceptions.CryptoError('The private key (in PEM format) could not be' - ' deserialized.') - - # 'TypeError' raised if a password was given and the private key was not - # encrypted, or if the key was encrypted but no password was supplied. - # Note: A passphrase or password is not used when generating 'private_key', - # since it should not be encrypted. - except TypeError: #pragma: no cover - raise securesystemslib.exceptions.CryptoError('The private key was unexpectedly encrypted.') - - # 'cryptography.exceptions.UnsupportedAlgorithm' raised if the serialized - # key is of a type that is not supported by the backend, or if the key is - # encrypted with a symmetric cipher that is not supported by the backend. - except cryptography.exceptions.UnsupportedAlgorithm: #pragma: no cover - raise securesystemslib.exceptions.CryptoError('The private key is encrypted with an' - ' unsupported algorithm.') - - # Generate an RSSA-PSS signature. - rsa_signer.update(data) - signature = rsa_signer.finalize() + # The check_match() above should have validated 'scheme'. This is an + # extra check... + if scheme == 'rsassa-pss-sha256': #pragma: no cover + # Generate an RSSA-PSS signature. Raise + # 'securesystemslib.exceptions.CryptoError' for any of the expected + # exceptions raised by pyca/cryptography. + try: + # 'private_key' (in PEM format) must first be converted to a + # pyca/cryptography private key object before a signature can be + # generated. + private_key_object = load_pem_private_key(private_key.encode('utf-8'), + password=None, + backend=default_backend()) + + # Calculate the SHA256 hash of 'data' and generate the hash's PKCS1-PSS + # signature. + rsa_signer = \ + private_key_object.signer(padding.PSS(mgf=padding.MGF1(hashes.SHA256()), + salt_length=hashes.SHA256().digest_size), hashes.SHA256()) + + # If the PEM data could not be decrypted, or if its structure could not be + # decoded successfully. + except ValueError: #pragma: no cover + raise securesystemslib.exceptions.CryptoError('The private key' + ' (in PEM format) could not be deserialized.') + + # 'TypeError' raised if a password was given and the private key was not + # encrypted, or if the key was encrypted but no password was supplied. + # Note: A passphrase or password is not used when generating + # 'private_key', since it should not be encrypted. + except TypeError: #pragma: no cover + raise securesystemslib.exceptions.CryptoError('The private key was' + ' unexpectedly encrypted.') + + # 'cryptography.exceptions.UnsupportedAlgorithm' raised if the serialized + # key is of a type that is not supported by the backend, or if the key is + # encrypted with a symmetric cipher that is not supported by the backend. + except cryptography.exceptions.UnsupportedAlgorithm: #pragma: no cover + raise securesystemslib.exceptions.CryptoError('The private key is' + ' encrypted with a unsupported algorithm.') + + # Generate an RSSA-PSS signature. + rsa_signer.update(data) + signature = rsa_signer.finalize() + + else: #pragma: no cover + raise securesystemslib.exceptions.UnsupportedAlgorithmError('Unsupported' + ' signature scheme is specified: ' + repr(scheme)) else: raise ValueError('The required private key is unset.') - return signature, method + return signature, scheme -def verify_rsa_signature(signature, signature_method, public_key, data): +def verify_rsa_signature(signature, signature_scheme, public_key, data): """ Determine whether the corresponding private key of 'public_key' produced - 'signature'. verify_signature() will use the public key, signature method, + 'signature'. verify_signature() will use the public key, signature scheme, and 'data' to complete the verification. >>> public, private = generate_rsa_public_and_private(2048) >>> data = b'The quick brown fox jumps over the lazy dog' - >>> signature, method = create_rsa_signature(private, data) - >>> verify_rsa_signature(signature, method, public, data) + >>> scheme = 'rsassa-pss-sha256' + >>> signature, scheme = create_rsa_signature(private, data, scheme) + >>> verify_rsa_signature(signature, scheme, public, data) True - >>> verify_rsa_signature(signature, method, public, b'bad_data') + >>> verify_rsa_signature(signature, scheme, public, b'bad_data') False signature: - An RSASSA PSS signature, as a string. This is the signature returned + A signature, as a string. This is the signature returned by create_rsa_signature(). - signature_method: - A string that indicates the signature algorithm used to generate - 'signature'. 'RSASSA-PSS' is currently supported. + signature_scheme: + A string that indicates the signature scheme used to generate + 'signature'. 'rsassa-pss-sha256' is currently supported. public_key: The RSA public key, a string in PEM format. @@ -380,14 +398,15 @@ def verify_rsa_signature(signature, signature_method, public_key, data): 'signature'. 'data' (a string) is needed here to verify 'signature'. - securesystemslib.exceptions.FormatError, if 'signature', 'signature_method', 'public_key', or - 'data' are improperly formatted. + securesystemslib.exceptions.FormatError, if 'signature', + 'signature_scheme', 'public_key', or 'data' are improperly formatted. - securesystemslib.exceptions.UnknownMethodError, if the signing method used by - 'signature' is not one supported by securesystemslib.keys.create_signature(). + securesystemslib.exceptions.UnsupportedAlgorithmError, if the signature + scheme used by 'signature' is not one supported by + securesystemslib.keys.create_signature(). - securesystemslib.exceptions.CryptoError, if the private key cannot be decoded or its key type - is unsupported. + securesystemslib.exceptions.CryptoError, if the private key cannot be + decoded or its key type is unsupported. pyca/cryptography's RSAPublicKey.verifier() called to do the actual @@ -398,12 +417,13 @@ def verify_rsa_signature(signature, signature_method, public_key, data): """ # Does 'public_key' have the correct format? - # This check will ensure 'public_key' conforms to 'securesystemslib.formats.PEMRSA_SCHEMA'. - # Raise 'securesystemslib.exceptions.FormatError' if the check fails. + # This check will ensure 'public_key' conforms to + # 'securesystemslib.formats.PEMRSA_SCHEMA'. Raise + # 'securesystemslib.exceptions.FormatError' if the check fails. securesystemslib.formats.PEMRSA_SCHEMA.check_match(public_key) - # Does 'signature_method' have the correct format? - securesystemslib.formats.NAME_SCHEMA.check_match(signature_method) + # Does 'signature_scheme' have the correct format? + securesystemslib.formats.RSA_SIG_SCHEMA.check_match(signature_scheme) # Does 'signature' have the correct format? securesystemslib.formats.PYCACRYPTOSIGNATURE_SCHEMA.check_match(signature) @@ -413,12 +433,13 @@ def verify_rsa_signature(signature, signature_method, public_key, data): # Verify whether the private key of 'public_key' produced 'signature'. # Before returning the 'valid_signature' Boolean result, ensure 'RSASSA-PSS' - # was used as the signing method. + # was used as the signature scheme. valid_signature = False - # Verify the expected 'signature_method' value. - if signature_method != 'RSASSA-PSS': - raise securesystemslib.exceptions.UnknownMethodError(signature_method) + # Verify the expected 'signature_scheme' value. This is an extra check, + # since the check_match() should have validated 'signature_scheme'. + if signature_scheme != 'rsassa-pss-sha256': #pragma: no cover + raise securesystemslib.exceptions.UnsupportedAlgorithmError(signature_scheme) # Verify the RSASSA-PSS signature with pyca/cryptography. try: @@ -671,6 +692,7 @@ def encrypt_key(key_object, password): https://en.wikipedia.org/wiki/PBKDF2 >>> ed25519_key = {'keytype': 'ed25519', \ + 'scheme': 'ed25519', \ 'keyid': \ 'd62247f817883f593cf6c66a5a55292488d457bcf638ae03207dbbba9dbe457d', \ 'keyval': {'public': \ @@ -761,6 +783,7 @@ def decrypt_key(encrypted_key, password): https://en.wikipedia.org/wiki/PBKDF2 >>> ed25519_key = {'keytype': 'ed25519', \ + 'scheme': 'ed25519', \ 'keyid': \ 'd62247f817883f593cf6c66a5a55292488d457bcf638ae03207dbbba9dbe457d', \ 'keyval': {'public': \ diff --git a/securesystemslib/pycrypto_keys.py b/securesystemslib/pycrypto_keys.py index c00072ae..ff3850cd 100755 --- a/securesystemslib/pycrypto_keys.py +++ b/securesystemslib/pycrypto_keys.py @@ -217,11 +217,11 @@ def generate_rsa_public_and_private(bits=_DEFAULT_RSA_KEY_BITS): -def create_rsa_signature(private_key, data): +def create_rsa_signature(private_key, data, scheme='rsassa-pss-sha256'): """ - Generate an RSASSA-PSS signature. The signature, and the method (signature - algorithm) used, is returned as a (signature, method) tuple. + Generate a 'scheme' signature. The signature, and the signature scheme + used, is returned as a (signature, scheme) tuple. The signing process will use 'private_key' and 'data' to generate the signature. @@ -231,10 +231,11 @@ def create_rsa_signature(private_key, data): >>> public, private = generate_rsa_public_and_private(2048) >>> data = 'The quick brown fox jumps over the lazy dog'.encode('utf-8') - >>> signature, method = create_rsa_signature(private, data) - >>> securesystemslib.formats.NAME_SCHEMA.matches(method) + >>> scheme = 'rsassa-pss-sha256' + >>> signature, scheme = create_rsa_signature(private, data, scheme) + >>> securesystemslib.formats.NAME_SCHEMA.matches(scheme) True - >>> method == 'RSASSA-PSS' + >>> scheme == 'rsassa-pss-sha256' True >>> securesystemslib.formats.PYCRYPTOSIGNATURE_SCHEMA.matches(signature) True @@ -246,19 +247,25 @@ def create_rsa_signature(private_key, data): data: Data (string) used by create_rsa_signature() to generate the signature. + scheme: + The signature scheme used by the provided 'private_key' to create + the signature. For example: 'rsassa-pss-sha256'. + - securesystemslib.exceptions.FormatError, if 'private_key' is improperly formatted. + securesystemslib.exceptions.FormatError, if 'private_key' is improperly + formatted. TypeError, if 'private_key' is unset. - securesystemslib.exceptions.CryptoError, if the signature cannot be generated. + securesystemslib.exceptions.CryptoError, if the signature cannot be + generated. - PyCrypto's 'Crypto.Signature.PKCS1_PSS' called to generate the signature. + PyCrypto's 'Crypto.Signature.PKCS1_PSS' is called to generate the signature. - A (signature, method) tuple, where the signature is a string and the method - is 'RSASSA-PSS'. + A (signature, scheme) tuple, where the signature is a string and the scheme + is one of the supported signature schemes (e.g., 'rsassa-pss-sha256'). """ # Does 'private_key' have the correct format? @@ -267,13 +274,14 @@ def create_rsa_signature(private_key, data): # 'securesystemslib.exceptions.FormatError' if the check fails. securesystemslib.formats.PEMRSA_SCHEMA.check_match(private_key) + # Is 'scheme' properly formatted? + securesystemslib.formats.RSA_SIG_SCHEMA.check_match(scheme) + # Does 'data' have the correct format? securesystemslib.formats.DATA_SCHEMA.check_match(data) - # Signing the 'data' object requires a private key. - # The 'RSASSA-PSS' (i.e., PyCrypto module) signing method is the - # only method currently supported. - method = 'RSASSA-PSS' + # Signing the 'data' object requires a private key. 'rssa-pss-sha256' is the + # only signature scheme currently supported. signature = None # Verify the signature, but only if the private key has been set. The @@ -282,72 +290,79 @@ def create_rsa_signature(private_key, data): # value and not compare identities with the 'is' keyword. Up to this point # 'private_key' has variable size and can be an empty string. if len(private_key): - # Calculate the SHA256 hash of 'data' and generate the hash's PKCS1-PSS - # signature. - - # PyCrypto's expected exceptions when generating RSA key object: - # "ValueError/IndexError/TypeError: When the given key cannot be parsed - # (possibly because the passphrase is wrong)." - # If the passphrase is incorrect, PyCrypto returns: "RSA key format is not - # supported". - try: - sha256_object = Crypto.Hash.SHA256.new(data) - rsa_key_object = Crypto.PublicKey.RSA.importKey(private_key) - - except (ValueError, IndexError, TypeError) as e: - raise securesystemslib.exceptions.CryptoError('Invalid private key or' - ' hash data: ' + str(e)) - - # Generate RSSA-PSS signature. Raise 'securesystemslib.exceptions.CryptoError' - # for the expected PyCrypto exceptions. - try: - pkcs1_pss_signer = Crypto.Signature.PKCS1_PSS.new(rsa_key_object) - signature = pkcs1_pss_signer.sign(sha256_object) - - except ValueError: #pragma: no cover - raise securesystemslib.exceptions.CryptoError('The RSA key too small for' - ' given hash algorithm.') - - except TypeError: - raise securesystemslib.exceptions.CryptoError('Missing required RSA' - ' private key.') - - except IndexError: # pragma: no cover - raise securesystemslib.exceptions.CryptoError('An RSA signature cannot' - ' be generated: ' + str(e)) + # The check_match() above should have validated 'scheme'. This is an extra + # check... + if scheme == 'rsassa-pss-sha256': #pragma: no cover + # Calculate the SHA256 hash of 'data' and generate the hash's PKCS1-PSS + # signature. + + # PyCrypto's expected exceptions when generating RSA key object: + # "ValueError/IndexError/TypeError: When the given key cannot be parsed + # (possibly because the passphrase is wrong)." If the passphrase is + # incorrect, PyCrypto returns: "RSA key format is not supported". + try: + sha256_object = Crypto.Hash.SHA256.new(data) + rsa_key_object = Crypto.PublicKey.RSA.importKey(private_key) + + except (ValueError, IndexError, TypeError) as e: + raise securesystemslib.exceptions.CryptoError('Invalid private key or' + ' hash data: ' + str(e)) + + # Generate RSSA-PSS signature. Raise + # 'securesystemslib.exceptions.CryptoError' for the expected PyCrypto + # exceptions. + try: + pkcs1_pss_signer = Crypto.Signature.PKCS1_PSS.new(rsa_key_object) + signature = pkcs1_pss_signer.sign(sha256_object) + + except ValueError: #pragma: no cover + raise securesystemslib.exceptions.CryptoError('The RSA key too small for' + ' given hash algorithm.') + + except TypeError: + raise securesystemslib.exceptions.CryptoError('Missing required RSA' + ' private key.') + + except IndexError: # pragma: no cover + raise securesystemslib.exceptions.CryptoError('An RSA signature cannot' + ' be generated: ' + str(e)) + else: #pragma: no cover + raise securesystemslib.exceptions.UnsupportedAlgorithmError('Unsupported' + ' signature scheme is specified: ' + repr(scheme)) else: raise ValueError('The required private key is unset.') - return signature, method + return signature, scheme -def verify_rsa_signature(signature, signature_method, public_key, data): +def verify_rsa_signature(signature, signature_scheme, public_key, data): """ Determine whether the corresponding private key of 'public_key' produced - 'signature'. verify_signature() will use the public key, signature method, - and 'data' to complete the verification. + 'signature'. verify_signature() will use the public key, + 'signature_scheme', and 'data' to complete the verification. >>> public, private = generate_rsa_public_and_private(2048) >>> data = b'The quick brown fox jumps over the lazy dog' - >>> signature, method = create_rsa_signature(private, data) - >>> verify_rsa_signature(signature, method, public, data) + >>> scheme = 'rsassa-pss-sha256' + >>> signature, scheme = create_rsa_signature(private, data, scheme) + >>> verify_rsa_signature(signature, scheme, public, data) True - >>> verify_rsa_signature(signature, method, public, b'bad_data') + >>> verify_rsa_signature(signature, scheme, public, b'bad_data') False signature: - An RSASSA PSS signature as a string. This is the signature returned - by create_rsa_signature(). + A 'signature_scheme' signature as a string. This is the signature + returned by create_rsa_signature(). - signature_method: + signature_scheme: A string that indicates the signature algorithm used to generate - 'signature'. 'RSASSA-PSS' is currently supported. + 'signature'. 'rsassa-pss-sha256' is currently supported. public_key: The RSA public key, a string in PEM format. @@ -357,12 +372,12 @@ def verify_rsa_signature(signature, signature_method, public_key, data): 'signature'. 'data' is needed here to verify the signature. - securesystemslib.exceptions.UnknownMethodError. Raised if the signing method - used by 'signature' is not one supported by + securesystemslib.exceptions.UnsupportedAlgorithmError. Raised if the + signature scheme used by 'signature' is not one supported by securesystemslib.keys.create_signature(). securesystemslib.exceptions.FormatError. Raised if 'signature', - 'signature_method', or 'public_key' is improperly formatted. + 'signature_scheme', or 'public_key' is improperly formatted. Crypto.Signature.PKCS1_PSS.verify() called to do the actual verification. @@ -377,8 +392,8 @@ def verify_rsa_signature(signature, signature_method, public_key, data): # 'securesystemslib.exceptions.FormatError' if the check fails. securesystemslib.formats.PEMRSA_SCHEMA.check_match(public_key) - # Does 'signature_method' have the correct format? - securesystemslib.formats.NAME_SCHEMA.check_match(signature_method) + # Does 'signature_scheme' have the correct format? + securesystemslib.formats.RSA_SIG_SCHEMA.check_match(signature_scheme) # Does 'signature' have the correct format? securesystemslib.formats.PYCRYPTOSIGNATURE_SCHEMA.check_match(signature) @@ -387,13 +402,15 @@ def verify_rsa_signature(signature, signature_method, public_key, data): securesystemslib.formats.DATA_SCHEMA.check_match(data) # Verify whether the private key of 'public_key' produced 'signature'. - # Before returning the 'valid_signature' Boolean result, ensure 'RSASSA-PSS' - # was used as the signing method. + # Before returning the 'valid_signature' Boolean result, ensure + # 'signature_scheme' is one of the supported signature schemes. valid_signature = False - # Verify the signature with PyCrypto if the signature method is valid, - # otherwise raise 'securesystemslib.exceptions.UnknownMethodError'. - if signature_method == 'RSASSA-PSS': + # Verify the signature with PyCrypto if the signature scheme is valid, + # otherwise raise 'securesystemslib.exceptions.UnsupportedAlgorithmError'. + # The check_match above should have validated 'signature_scheme'. This is + # an extra check... + if signature_scheme == 'rsassa-pss-sha256': #pragma: no cover try: rsa_key_object = Crypto.PublicKey.RSA.importKey(public_key) pkcs1_pss_verifier = Crypto.Signature.PKCS1_PSS.new(rsa_key_object) @@ -404,8 +421,8 @@ def verify_rsa_signature(signature, signature_method, public_key, data): raise securesystemslib.exceptions.CryptoError('The RSA signature could not' ' be verified.') - else: - raise securesystemslib.exceptions.UnknownMethodError(signature_method) + else: #pragma: no cover + raise securesystemslib.exceptions.UnsupportedAlgorithmError(signature_scheme) return valid_signature @@ -627,6 +644,7 @@ def encrypt_key(key_object, password): https://en.wikipedia.org/wiki/PBKDF2 >>> ed25519_key = {'keytype': 'ed25519', \ + 'scheme': 'ed25519', \ 'keyid': \ 'd62247f817883f593cf6c66a5a55292488d457bcf638ae03207dbbba9dbe457d', \ 'keyval': {'public': \ @@ -716,6 +734,7 @@ def decrypt_key(encrypted_key, password): https://en.wikipedia.org/wiki/PBKDF2 >>> ed25519_key = {'keytype': 'ed25519', \ + 'scheme': 'ed25519', \ 'keyid': \ 'd62247f817883f593cf6c66a5a55292488d457bcf638ae03207dbbba9dbe457d', \ 'keyval': {'public': \ diff --git a/tests/test_ecdsa_keys.py b/tests/test_ecdsa_keys.py index e936cd44..5e96fee5 100755 --- a/tests/test_ecdsa_keys.py +++ b/tests/test_ecdsa_keys.py @@ -109,9 +109,12 @@ def test_verify_signature(self): global public global private data = b'The quick brown fox jumps over the lazy dog' - signature, method = securesystemslib.ecdsa_keys.create_signature(public, private, data) + scheme = 'ecdsa-sha2-nistp256' + signature, scheme = securesystemslib.ecdsa_keys.create_signature(public, + private, data, scheme) - valid_signature = securesystemslib.ecdsa_keys.verify_signature(public, method, signature, data) + valid_signature = securesystemslib.ecdsa_keys.verify_signature(public, + scheme, signature, data) self.assertEqual(True, valid_signature) # Generate an RSA key so that we can verify that non-ECDSA keys are @@ -120,48 +123,54 @@ def test_verify_signature(self): # Verify that a non-ECDSA key (via the PEM argument) is rejected. self.assertRaises(securesystemslib.exceptions.FormatError, - securesystemslib.ecdsa_keys.verify_signature, rsa_pem, method, signature, + securesystemslib.ecdsa_keys.verify_signature, rsa_pem, scheme, signature, data) # Check for improperly formatted arguments. - self.assertRaises(securesystemslib.exceptions.FormatError, securesystemslib.ecdsa_keys.verify_signature, 123, method, - signature, data) + self.assertRaises(securesystemslib.exceptions.FormatError, + securesystemslib.ecdsa_keys.verify_signature, 123, scheme, + signature, data) # Signature method improperly formatted. - self.assertRaises(securesystemslib.exceptions.FormatError, securesystemslib.ecdsa_keys.verify_signature, public, 123, - signature, data) + self.assertRaises(securesystemslib.exceptions.FormatError, + securesystemslib.ecdsa_keys.verify_signature, public, 123, + signature, data) # Invalid signature method. - self.assertRaises(securesystemslib.exceptions.UnknownMethodError, securesystemslib.ecdsa_keys.verify_signature, public, - 'unsupported_method', signature, data) + self.assertRaises(securesystemslib.exceptions.FormatError, + securesystemslib.ecdsa_keys.verify_signature, public, + 'unsupported_scheme', signature, data) # Signature not a string. - self.assertRaises(securesystemslib.exceptions.FormatError, securesystemslib.ecdsa_keys.verify_signature, public, method, - 123, data) + self.assertRaises(securesystemslib.exceptions.FormatError, + securesystemslib.ecdsa_keys.verify_signature, public, scheme, + 123, data) # Invalid signature.. - self.assertRaises(securesystemslib.exceptions.FormatError, securesystemslib.ecdsa_keys.verify_signature, public, method, - 'bad_signature', data) + self.assertRaises(securesystemslib.exceptions.FormatError, + securesystemslib.ecdsa_keys.verify_signature, public, scheme, + 'bad_signature', data) # Check for invalid signature and data. # Mismatched data. self.assertRaises(securesystemslib.exceptions.FormatError, - securesystemslib.ecdsa_keys.verify_signature, public, method, + securesystemslib.ecdsa_keys.verify_signature, public, scheme, signature, '123') - self.assertEqual(False, securesystemslib.ecdsa_keys.verify_signature(public, method, - signature, b'123')) + self.assertEqual(False, securesystemslib.ecdsa_keys.verify_signature(public, + scheme, signature, b'123')) + # Mismatched signature. bad_signature = b'a'*64 - self.assertEqual(False, securesystemslib.ecdsa_keys.verify_signature(public, method, - bad_signature, data)) + self.assertEqual(False, securesystemslib.ecdsa_keys.verify_signature(public, + scheme, bad_signature, data)) # Generated signature created with different data. - new_signature, method = securesystemslib.ecdsa_keys.create_signature(public, private, - b'mismatched data') + new_signature, scheme = securesystemslib.ecdsa_keys.create_signature(public, + private, b'mismatched data') - self.assertEqual(False, securesystemslib.ecdsa_keys.verify_signature(public, method, - new_signature, data)) + self.assertEqual(False, securesystemslib.ecdsa_keys.verify_signature(public, + scheme, new_signature, data)) diff --git a/tests/test_ed25519_keys.py b/tests/test_ed25519_keys.py index 3beff170..7d37e075 100755 --- a/tests/test_ed25519_keys.py +++ b/tests/test_ed25519_keys.py @@ -57,85 +57,98 @@ def test_create_signature(self): global public global private data = b'The quick brown fox jumps over the lazy dog' - signature, method = securesystemslib.ed25519_keys.create_signature(public, private, data) + scheme = 'ed25519' + signature, scheme = securesystemslib.ed25519_keys.create_signature(public, + private, data, scheme) # Verify format of returned values. self.assertEqual(True, - securesystemslib.formats.ED25519SIGNATURE_SCHEMA.matches(signature)) + securesystemslib.formats.ED25519SIGNATURE_SCHEMA.matches(signature)) - self.assertEqual(True, securesystemslib.formats.NAME_SCHEMA.matches(method)) - self.assertEqual('ed25519', method) + self.assertEqual(True, securesystemslib.formats.ED25519_SIG_SCHEMA.matches(scheme)) + self.assertEqual('ed25519', scheme) # Check for improperly formatted argument. self.assertRaises(securesystemslib.exceptions.FormatError, - securesystemslib.ed25519_keys.create_signature, 123, private, data) + securesystemslib.ed25519_keys.create_signature, 123, private, data, + scheme) self.assertRaises(securesystemslib.exceptions.FormatError, - securesystemslib.ed25519_keys.create_signature, public, 123, data) + securesystemslib.ed25519_keys.create_signature, public, 123, data, + scheme) # Check for invalid 'data'. self.assertRaises(securesystemslib.exceptions.CryptoError, - securesystemslib.ed25519_keys.create_signature, public, private, 123) + securesystemslib.ed25519_keys.create_signature, public, private, 123, + scheme) def test_verify_signature(self): global public global private data = b'The quick brown fox jumps over the lazy dog' - signature, method = securesystemslib.ed25519_keys.create_signature(public, private, data) + scheme = 'ed25519' + signature, scheme = securesystemslib.ed25519_keys.create_signature(public, + private, data, scheme) - valid_signature = securesystemslib.ed25519_keys.verify_signature(public, method, signature, data) + valid_signature = securesystemslib.ed25519_keys.verify_signature(public, + scheme, signature, data) self.assertEqual(True, valid_signature) # Test with 'pynacl'. - valid_signature = securesystemslib.ed25519_keys.verify_signature(public, method, signature, data, - use_pynacl=True) + valid_signature = securesystemslib.ed25519_keys.verify_signature(public, + scheme, signature, data, use_pynacl=True) self.assertEqual(True, valid_signature) # Test with 'pynacl', but a bad signature is provided. bad_signature = os.urandom(64) - valid_signature = securesystemslib.ed25519_keys.verify_signature(public, method, bad_signature, - data, use_pynacl=True) + valid_signature = securesystemslib.ed25519_keys.verify_signature(public, + scheme, bad_signature, data, use_pynacl=True) self.assertEqual(False, valid_signature) # Check for improperly formatted arguments. - self.assertRaises(securesystemslib.exceptions.FormatError, securesystemslib.ed25519_keys.verify_signature, 123, method, - signature, data) + self.assertRaises(securesystemslib.exceptions.FormatError, + securesystemslib.ed25519_keys.verify_signature, 123, scheme, + signature, data) # Signature method improperly formatted. - self.assertRaises(securesystemslib.exceptions.FormatError, securesystemslib.ed25519_keys.verify_signature, public, 123, - signature, data) + self.assertRaises(securesystemslib.exceptions.FormatError, + securesystemslib.ed25519_keys.verify_signature, public, 123, + signature, data) # Invalid signature method. - self.assertRaises(securesystemslib.exceptions.UnknownMethodError, securesystemslib.ed25519_keys.verify_signature, public, - 'unsupported_method', signature, data) + self.assertRaises(securesystemslib.exceptions.FormatError, + securesystemslib.ed25519_keys.verify_signature, public, + 'unsupported_scheme', signature, data) # Signature not a string. - self.assertRaises(securesystemslib.exceptions.FormatError, securesystemslib.ed25519_keys.verify_signature, public, method, - 123, data) + self.assertRaises(securesystemslib.exceptions.FormatError, + securesystemslib.ed25519_keys.verify_signature, public, scheme, + 123, data) # Invalid signature length, which must be exactly 64 bytes.. - self.assertRaises(securesystemslib.exceptions.FormatError, securesystemslib.ed25519_keys.verify_signature, public, method, - 'bad_signature', data) + self.assertRaises(securesystemslib.exceptions.FormatError, + securesystemslib.ed25519_keys.verify_signature, public, scheme, + 'bad_signature', data) # Check for invalid signature and data. # Mismatched data. - self.assertEqual(False, securesystemslib.ed25519_keys.verify_signature(public, method, - signature, '123')) + self.assertEqual(False, securesystemslib.ed25519_keys.verify_signature(public, + scheme, signature, '123')) # Mismatched signature. bad_signature = b'a'*64 - self.assertEqual(False, securesystemslib.ed25519_keys.verify_signature(public, method, - bad_signature, data)) + self.assertEqual(False, securesystemslib.ed25519_keys.verify_signature(public, + scheme, bad_signature, data)) # Generated signature created with different data. - new_signature, method = securesystemslib.ed25519_keys.create_signature(public, private, - b'mismatched data') + new_signature, scheme = securesystemslib.ed25519_keys.create_signature(public, private, + b'mismatched data', scheme) - self.assertEqual(False, securesystemslib.ed25519_keys.verify_signature(public, method, - new_signature, data)) + self.assertEqual(False, securesystemslib.ed25519_keys.verify_signature(public, + scheme, new_signature, data)) diff --git a/tests/test_formats.py b/tests/test_formats.py index ff974136..47916447 100755 --- a/tests/test_formats.py +++ b/tests/test_formats.py @@ -65,7 +65,7 @@ def test_schemas(self): 'KEYIDS_SCHEMA': (securesystemslib.formats.KEYIDS_SCHEMA, ['123456789abcdef', '123456789abcdef']), - 'SIG_METHOD_SCHEMA': (securesystemslib.formats.SIG_METHOD_SCHEMA, 'ed25519'), + 'SIG_SCHEME_SCHEMA': (securesystemslib.formats.SIG_SCHEME_SCHEMA, 'ecdsa-sha2-nistp256'), 'RELPATH_SCHEMA': (securesystemslib.formats.RELPATH_SCHEMA, 'metadata/root/'), @@ -110,20 +110,24 @@ def test_schemas(self): 'KEY_SCHEMA': (securesystemslib.formats.KEY_SCHEMA, {'keytype': 'rsa', + 'scheme': 'rsassa-pss-sha256', 'keyval': {'public': 'pubkey', 'private': 'privkey'}}), 'PUBLIC_KEY_SCHEMA': (securesystemslib.formats.KEY_SCHEMA, {'keytype': 'rsa', + 'scheme': 'rsassa-pss-sha256', 'keyval': {'public': 'pubkey'}}), 'PUBLIC_KEY_SCHEMA2': (securesystemslib.formats.KEY_SCHEMA, {'keytype': 'rsa', + 'scheme': 'rsassa-pss-sha256', 'keyval': {'public': 'pubkey', 'private': ''}}), 'RSAKEY_SCHEMA': (securesystemslib.formats.RSAKEY_SCHEMA, {'keytype': 'rsa', + 'scheme': 'rsassa-pss-sha256', 'keyid': '123456789abcdef', 'keyval': {'public': 'pubkey', 'private': 'privkey'}}), @@ -159,6 +163,7 @@ def test_schemas(self): 'KEYDICT_SCHEMA': (securesystemslib.formats.KEYDICT_SCHEMA, {'123abc': {'keytype': 'rsa', + 'scheme': 'rsassa-pss-sha256', 'keyval': {'public': 'pubkey', 'private': 'privkey'}}}), @@ -185,6 +190,7 @@ def test_schemas(self): 'compression_algorithms': ['gz'], 'expires': '1985-10-21T13:20:00Z', 'keys': {'123abc': {'keytype': 'rsa', + 'scheme': 'rsassa-pss-sha256', 'keyval': {'public': 'pubkey', 'private': 'privkey'}}}, 'roles': {'root': {'keyids': ['123abc'], @@ -199,6 +205,7 @@ def test_schemas(self): 'hashes': {'sha256': 'ABCD123'}, 'custom': {'type': 'metadata'}}}, 'delegations': {'keys': {'123abc': {'keytype':'rsa', + 'scheme': 'rsassa-pss-sha256', 'keyval': {'public': 'pubkey', 'private': 'privkey'}}}, 'roles': [{'name': 'root', 'keyids': ['123abc'], @@ -245,6 +252,8 @@ def test_schemas(self): # Iterate 'valid_schemas', ensuring each 'valid_schema' correctly matches # its respective 'schema_type'. for schema_name, (schema_type, valid_schema) in six.iteritems(valid_schemas): + if not schema_type.matches(valid_schema): + print('bad schema: ' + repr(valid_schema)) self.assertEqual(True, schema_type.matches(valid_schema)) # Test conditions for invalid schemas. diff --git a/tests/test_interface.py b/tests/test_interface.py index 9c789998..7c418f23 100755 --- a/tests/test_interface.py +++ b/tests/test_interface.py @@ -103,6 +103,7 @@ def test_generate_and_write_rsa_keypair(self): self.assertTrue(os.path.exists(test_keypath_unencrypted + '.pub')) # Ensure the generated key files are importable. + scheme = 'rsassa-pss-sha256' imported_pubkey = \ interface.import_rsa_publickey_from_file(test_keypath + '.pub') self.assertTrue(securesystemslib.formats.RSAKEY_SCHEMA.matches(imported_pubkey)) @@ -277,8 +278,11 @@ def test_import_ed25519_publickey_from_file(self): # Invalid public key imported (contains unexpected keytype.) keytype = imported_ed25519_key['keytype'] keyval = imported_ed25519_key['keyval'] + scheme = imported_ed25519_key['scheme'] + ed25519key_metadata_format = \ - securesystemslib.keys.format_keyval_to_metadata(keytype, keyval, private=False) + securesystemslib.keys.format_keyval_to_metadata(keytype, scheme, + keyval, private=False) ed25519key_metadata_format['keytype'] = 'invalid_keytype' with open(ed25519_keypath + '.pub', 'wb') as file_object: @@ -293,6 +297,7 @@ def test_import_ed25519_publickey_from_file(self): 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='pw') @@ -414,8 +419,10 @@ def test_import_ecdsa_publickey_from_file(self): # Invalid public key imported (contains unexpected keytype.) keytype = imported_ecdsa_key['keytype'] keyval = imported_ecdsa_key['keyval'] + scheme = imported_ecdsa_key['scheme'] + ecdsakey_metadata_format = \ - securesystemslib.keys.format_keyval_to_metadata(keytype, keyval, private=False) + securesystemslib.keys.format_keyval_to_metadata(keytype, scheme, keyval, private=False) ecdsakey_metadata_format['keytype'] = 'invalid_keytype' with open(ecdsa_keypath + '.pub', 'wb') as file_object: diff --git a/tests/test_keys.py b/tests/test_keys.py index 2df3f1f1..1359594d 100755 --- a/tests/test_keys.py +++ b/tests/test_keys.py @@ -105,14 +105,16 @@ def test_generate_ecdsa_key(self): def test_format_keyval_to_metadata(self): keyvalue = self.rsakey_dict['keyval'] keytype = self.rsakey_dict['keytype'] - key_meta = KEYS.format_keyval_to_metadata(keytype, keyvalue) + scheme = self.rsakey_dict['scheme'] + + key_meta = KEYS.format_keyval_to_metadata(keytype, scheme, keyvalue) # Check if the format of the object returned by this function corresponds # to KEY_SCHEMA format. self.assertEqual(None, securesystemslib.formats.KEY_SCHEMA.check_match(key_meta), FORMAT_ERROR_MSG) - key_meta = KEYS.format_keyval_to_metadata(keytype, keyvalue, private=True) + key_meta = KEYS.format_keyval_to_metadata(keytype, scheme, keyvalue, private=True) # Check if the format of the object returned by this function corresponds # to KEY_SCHEMA format. @@ -122,14 +124,14 @@ def test_format_keyval_to_metadata(self): # Supplying a 'bad' keyvalue. self.assertRaises(securesystemslib.exceptions.FormatError, KEYS.format_keyval_to_metadata, - 'bad_keytype', keyvalue) + 'bad_keytype', scheme, keyvalue, private=True) # Test for missing 'public' entry. public = keyvalue['public'] del keyvalue['public'] self.assertRaises(securesystemslib.exceptions.FormatError, KEYS.format_keyval_to_metadata, - keytype, keyvalue) + keytype, scheme, keyvalue) keyvalue['public'] = public # Test for missing 'private' entry. @@ -137,7 +139,7 @@ def test_format_keyval_to_metadata(self): del keyvalue['private'] self.assertRaises(securesystemslib.exceptions.FormatError, KEYS.format_keyval_to_metadata, - keytype, keyvalue, private=True) + keytype, scheme, keyvalue, private=True) keyvalue['private'] = private @@ -209,6 +211,7 @@ def test_format_metadata_to_key(self): def test_helper_get_keyid(self): keytype = self.rsakey_dict['keytype'] keyvalue = self.rsakey_dict['keyval'] + scheme = self.rsakey_dict['scheme'] # Check format of 'keytype'. self.assertEqual(None, @@ -220,7 +223,12 @@ def test_helper_get_keyid(self): securesystemslib.formats.KEYVAL_SCHEMA.check_match(keyvalue), FORMAT_ERROR_MSG) - keyid = KEYS._get_keyid(keytype, keyvalue) + # Check format of 'scheme'. + self.assertEqual(None, + securesystemslib.formats.RSA_SIG_SCHEMA.check_match(scheme), + FORMAT_ERROR_MSG) + + keyid = KEYS._get_keyid(keytype, scheme, keyvalue) # Check format of 'keyid' - the output of '_get_keyid()' function. self.assertEqual(None, @@ -245,11 +253,19 @@ def test_create_signature(self): securesystemslib.formats.SIGNATURE_SCHEMA.check_match(ed25519_signature), FORMAT_ERROR_MSG) + # Test for invalid signature scheme. + args = (self.rsakey_dict, DATA) + + valid_scheme = self.rsakey_dict['scheme'] + self.rsakey_dict['scheme'] = 'invalid_scheme' + self.assertRaises(securesystemslib.exceptions.UnsupportedAlgorithmError, + KEYS.create_signature, *args) + self.rsakey_dict['scheme'] = valid_scheme + # Removing private key from 'rsakey_dict' - should raise a TypeError. private = self.rsakey_dict['keyval']['private'] self.rsakey_dict['keyval']['private'] = '' - args = (self.rsakey_dict, DATA) self.assertRaises(ValueError, KEYS.create_signature, *args) # Supplying an incorrect number of arguments. @@ -317,16 +333,30 @@ def test_verify_signature(self): DATA) self.assertTrue(verified, "Incorrect signature.") + # Verify that an invalid ed25519 signature scheme is rejected. + valid_scheme = self.ed25519key_dict['scheme'] + self.ed25519key_dict['scheme'] = 'invalid_scheme' + self.assertRaises(securesystemslib.exceptions.UnsupportedAlgorithmError, + KEYS.verify_signature, self.ed25519key_dict, ed25519_signature, DATA) + self.ed25519key_dict['scheme'] = valid_scheme + # Verifying the 'ecdsa_sigature' of 'DATA'. if ecdsa_signature: verified = KEYS.verify_signature(self.ecdsakey_dict, ecdsa_signature, DATA) self.assertTrue(verified, "Incorrect signature.") - # Verifying the 'ed25519_signature' of 'DATA'. + # Verifying the 'ecdsa_signature' of 'DATA'. verified = KEYS.verify_signature(self.ecdsakey_dict, ecdsa_signature, DATA) self.assertTrue(verified, "Incorrect signature.") + # Test for an invalid ecdsa signature scheme. + valid_scheme = self.ecdsakey_dict['scheme'] + self.ecdsakey_dict['scheme'] = 'invalid_scheme' + self.assertRaises(securesystemslib.exceptions.UnsupportedAlgorithmError, + KEYS.verify_signature, self.ecdsakey_dict, ecdsa_signature, DATA) + self.ecdsakey_dict['scheme'] = valid_scheme + # Testing invalid signatures. Same signature is passed, with 'DATA' being # different than the original 'DATA' that was used in creating the # 'rsa_signature'. Function should return 'False'. @@ -348,13 +378,17 @@ def test_verify_signature(self): self.assertFalse(verified, 'Returned \'True\' on an incorrect signature.') - # Modifying 'signature' to pass an incorrect method since only + # Modifying 'rsakey_dict' to pass an incorrect scheme since only # 'PyCrypto-PKCS#1 PSS' is accepted. - rsa_signature['method'] = 'Biff' + valid_scheme = self.rsakey_dict['scheme'] + self.rsakey_dict['scheme'] = 'Biff' args = (self.rsakey_dict, rsa_signature, DATA) - self.assertRaises(securesystemslib.exceptions.UnknownMethodError, - KEYS.verify_signature, *args) + self.assertRaises(securesystemslib.exceptions.UnsupportedAlgorithmError, + KEYS.verify_signature, *args) + + # Restore + self.rsakey_dict['scheme'] = valid_scheme # Passing incorrect number of arguments. self.assertRaises(TypeError, KEYS.verify_signature) @@ -382,11 +416,12 @@ def test_create_rsa_encrypted_pem(self): # Test valid arguments. private = self.rsakey_dict['keyval']['private'] passphrase = 'secret' + scheme = 'rsassa-pss-sha256' encrypted_pem = KEYS.create_rsa_encrypted_pem(private, passphrase) self.assertTrue(securesystemslib.formats.PEMRSA_SCHEMA.matches(encrypted_pem)) - # Try to import the encryped PEM file. - rsakey = KEYS.import_rsakey_from_private_pem(encrypted_pem, passphrase) + # Try to import the encrypted PEM file. + rsakey = KEYS.import_rsakey_from_private_pem(encrypted_pem, scheme, passphrase) self.assertTrue(securesystemslib.formats.RSAKEY_SCHEMA.matches(rsakey)) # Test improperly formatted arguments. @@ -511,9 +546,11 @@ def test_import_ecdsakey_from_private_pem(self): ecdsakey = KEYS.import_ecdsakey_from_private_pem(private_pem) # Test for an encrypted PEM. + scheme = 'ecdsa-sha2-nistp256' encrypted_pem = \ securesystemslib.ecdsa_keys.create_ecdsa_encrypted_pem(private_pem, 'password') - private_ecdsakey = KEYS.import_ecdsakey_from_private_pem(encrypted_pem.decode('utf-8'), 'password') + private_ecdsakey = KEYS.import_ecdsakey_from_private_pem(encrypted_pem.decode('utf-8'), + scheme, 'password') # Test for invalid arguments. diff --git a/tests/test_pyca_crypto_keys.py b/tests/test_pyca_crypto_keys.py index 51679785..9ec69bc9 100755 --- a/tests/test_pyca_crypto_keys.py +++ b/tests/test_pyca_crypto_keys.py @@ -65,13 +65,13 @@ def test_create_rsa_signature(self): global private_rsa global public_rsa data = 'The quick brown fox jumps over the lazy dog'.encode('utf-8') - signature, method = securesystemslib.pyca_crypto_keys.create_rsa_signature(private_rsa, data) + signature, scheme = securesystemslib.pyca_crypto_keys.create_rsa_signature(private_rsa, data) # Verify format of returned values. self.assertNotEqual(None, signature) - self.assertEqual(None, securesystemslib.formats.NAME_SCHEMA.check_match(method), + self.assertEqual(None, securesystemslib.formats.RSA_SIG_SCHEMA.check_match(scheme), FORMAT_ERROR_MSG) - self.assertEqual('RSASSA-PSS', method) + self.assertEqual('rsassa-pss-sha256', scheme) # Check for improperly formatted arguments. self.assertRaises(securesystemslib.exceptions.FormatError, @@ -97,15 +97,15 @@ def test_verify_rsa_signature(self): global public_rsa global private_rsa data = 'The quick brown fox jumps over the lazy dog'.encode('utf-8') - signature, method = securesystemslib.pyca_crypto_keys.create_rsa_signature(private_rsa, data) + signature, scheme = securesystemslib.pyca_crypto_keys.create_rsa_signature(private_rsa, data) - valid_signature = securesystemslib.pyca_crypto_keys.verify_rsa_signature(signature, method, public_rsa, + valid_signature = securesystemslib.pyca_crypto_keys.verify_rsa_signature(signature, scheme, public_rsa, data) self.assertEqual(True, valid_signature) # Check for an invalid public key. self.assertRaises(securesystemslib.exceptions.CryptoError, - securesystemslib.pyca_crypto_keys.verify_rsa_signature, signature, method, + securesystemslib.pyca_crypto_keys.verify_rsa_signature, signature, scheme, private_rsa, data) # Check for improperly formatted arguments. @@ -113,29 +113,29 @@ def test_verify_rsa_signature(self): 123, public_rsa, data) self.assertRaises(securesystemslib.exceptions.FormatError, securesystemslib.pyca_crypto_keys.verify_rsa_signature, signature, - method, 123, data) + scheme, 123, data) - self.assertRaises(securesystemslib.exceptions.FormatError, securesystemslib.pyca_crypto_keys.verify_rsa_signature, 123, method, + self.assertRaises(securesystemslib.exceptions.FormatError, securesystemslib.pyca_crypto_keys.verify_rsa_signature, 123, scheme, public_rsa, data) - self.assertRaises(securesystemslib.exceptions.UnknownMethodError, + self.assertRaises(securesystemslib.exceptions.FormatError, securesystemslib.pyca_crypto_keys.verify_rsa_signature, signature, - 'invalid_method', + 'invalid_scheme', public_rsa, data) # Check for invalid 'signature' and 'data' arguments. self.assertRaises(securesystemslib.exceptions.FormatError, securesystemslib.pyca_crypto_keys.verify_rsa_signature, - signature, method, public_rsa, 123) + signature, scheme, public_rsa, 123) - self.assertEqual(False, securesystemslib.pyca_crypto_keys.verify_rsa_signature(signature, method, + self.assertEqual(False, securesystemslib.pyca_crypto_keys.verify_rsa_signature(signature, scheme, public_rsa, b'mismatched data')) - mismatched_signature, method = securesystemslib.pyca_crypto_keys.create_rsa_signature(private_rsa, + mismatched_signature, scheme = securesystemslib.pyca_crypto_keys.create_rsa_signature(private_rsa, b'mismatched data') self.assertEqual(False, securesystemslib.pyca_crypto_keys.verify_rsa_signature(mismatched_signature, - method, public_rsa, data)) + scheme, public_rsa, data)) def test_create_rsa_encrypted_pem(self): @@ -184,6 +184,7 @@ def test_encrypt_key(self): global private_rsa key_object = {'keytype': 'rsa', + 'scheme': 'rsassa-pss-sha256', 'keyid': '1223', 'keyval': {'public': public_rsa, 'private': private_rsa}} @@ -204,6 +205,7 @@ def test_decrypt_key(self): passphrase = 'pw' rsa_key = {'keytype': 'rsa', + 'scheme': 'rsassa-pss-sha256', 'keyid': 'd62247f817883f593cf6c66a5a55292488d457bcf638ae03207dbbba9dbe457d', 'keyval': {'public': public_rsa, 'private': private_rsa}} diff --git a/tests/test_pycrypto_keys.py b/tests/test_pycrypto_keys.py index 6f40dcad..5fcd74a7 100755 --- a/tests/test_pycrypto_keys.py +++ b/tests/test_pycrypto_keys.py @@ -67,13 +67,13 @@ def test_create_rsa_signature(self): global private_rsa global public_rsa data = 'The quick brown fox jumps over the lazy dog'.encode('utf-8') - signature, method = securesystemslib.pycrypto_keys.create_rsa_signature(private_rsa, data) + signature, scheme = securesystemslib.pycrypto_keys.create_rsa_signature(private_rsa, data) # Verify format of returned values. self.assertNotEqual(None, signature) - self.assertEqual(None, securesystemslib.formats.NAME_SCHEMA.check_match(method), + self.assertEqual(None, securesystemslib.formats.RSA_SIG_SCHEMA.check_match(scheme), FORMAT_ERROR_MSG) - self.assertEqual('RSASSA-PSS', method) + self.assertEqual('rsassa-pss-sha256', scheme) # Check for improperly formatted arguments. self.assertRaises(securesystemslib.exceptions.FormatError, @@ -103,16 +103,16 @@ def test_verify_rsa_signature(self): global public_rsa global private_rsa data = 'The quick brown fox jumps over the lazy dog'.encode('utf-8') - signature, method = securesystemslib.pycrypto_keys.create_rsa_signature(private_rsa, data) + signature, scheme = securesystemslib.pycrypto_keys.create_rsa_signature(private_rsa, data) - valid_signature = securesystemslib.pycrypto_keys.verify_rsa_signature(signature, method, public_rsa, + valid_signature = securesystemslib.pycrypto_keys.verify_rsa_signature(signature, scheme, public_rsa, data) self.assertEqual(True, valid_signature) # Check for invalid arguments that result in a failed signature # verification. self.assertRaises(securesystemslib.exceptions.CryptoError, - securesystemslib.pycrypto_keys.verify_rsa_signature, signature, method, + securesystemslib.pycrypto_keys.verify_rsa_signature, signature, scheme, 'bad_key', data) # Check for improperly formatted arguments. @@ -120,12 +120,12 @@ def test_verify_rsa_signature(self): 123, public_rsa, data) self.assertRaises(securesystemslib.exceptions.FormatError, securesystemslib.pycrypto_keys.verify_rsa_signature, signature, - method, 123, data) + scheme, 123, data) - self.assertRaises(securesystemslib.exceptions.FormatError, securesystemslib.pycrypto_keys.verify_rsa_signature, 123, method, + self.assertRaises(securesystemslib.exceptions.FormatError, securesystemslib.pycrypto_keys.verify_rsa_signature, 123, scheme, public_rsa, data) - self.assertRaises(securesystemslib.exceptions.UnknownMethodError, securesystemslib.pycrypto_keys.verify_rsa_signature, + self.assertRaises(securesystemslib.exceptions.FormatError, securesystemslib.pycrypto_keys.verify_rsa_signature, signature, 'invalid_method', public_rsa, data) @@ -133,16 +133,16 @@ def test_verify_rsa_signature(self): # Check for invalid signature and data. # Verify_rsa_signature should reject non-string data. self.assertRaises(securesystemslib.exceptions.FormatError, securesystemslib.pycrypto_keys.verify_rsa_signature, signature, - method, public_rsa, 123) + scheme, public_rsa, 123) - self.assertEqual(False, securesystemslib.pycrypto_keys.verify_rsa_signature(signature, method, + self.assertEqual(False, securesystemslib.pycrypto_keys.verify_rsa_signature(signature, scheme, public_rsa, b'mismatched data')) mismatched_signature, method = securesystemslib.pycrypto_keys.create_rsa_signature(private_rsa, b'mismatched data') self.assertEqual(False, securesystemslib.pycrypto_keys.verify_rsa_signature(mismatched_signature, - method, public_rsa, data)) + scheme, public_rsa, data)) def test_create_rsa_encrypted_pem(self): @@ -242,8 +242,9 @@ def test_encrypt_key(self): passphrase = 'pw' rsa_key = {'keytype': 'rsa', - 'keyid': 'd62247f817883f593cf6c66a5a55292488d457bcf638ae03207dbbba9dbe457d', - 'keyval': {'public': public_rsa, 'private': private_rsa}} + 'scheme': 'rsassa-pss-sha256', + 'keyid': 'd62247f817883f593cf6c66a5a55292488d457bcf638ae03207dbbba9dbe457d', + 'keyval': {'public': public_rsa, 'private': private_rsa}} encrypted_rsa_key = securesystemslib.pycrypto_keys.encrypt_key(rsa_key, passphrase) @@ -260,8 +261,9 @@ def test_decrypt_key(self): passphrase = 'pw' rsa_key = {'keytype': 'rsa', - 'keyid': 'd62247f817883f593cf6c66a5a55292488d457bcf638ae03207dbbba9dbe457d', - 'keyval': {'public': public_rsa, 'private': private_rsa}} + 'scheme': 'rsassa-pss-sha256', + 'keyid': 'd62247f817883f593cf6c66a5a55292488d457bcf638ae03207dbbba9dbe457d', + 'keyval': {'public': public_rsa, 'private': private_rsa}} encrypted_rsa_key = securesystemslib.pycrypto_keys.encrypt_key(rsa_key, passphrase) diff --git a/tests/test_util.py b/tests/test_util.py index 76d3dbfc..634614b0 100755 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -497,6 +497,7 @@ def test_C4_ensure_all_targets_allowed(self): parent_delegations = {"keys": { "a394c28384648328b16731f81440d72243c77bb44c07c040be99347f0df7d7bf": { "keytype": "ed25519", + "scheme": "ed25519", "keyval": { "public": "3eb81026ded5af2c61fb3d4b272ac53cd1049a810ee88f4df1fc35cdaf918157" }