# Copyright 2017 Linaro Limited # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ Cryptographic key management for imgtool. """ from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric.rsa import ( RSAPrivateKey, RSAPublicKey) from cryptography.hazmat.primitives.asymmetric.ec import ( EllipticCurvePrivateKey, EllipticCurvePublicKey) from cryptography.hazmat.primitives.asymmetric.ed25519 import ( Ed25519PrivateKey, Ed25519PublicKey) from cryptography.hazmat.primitives.asymmetric.x25519 import ( X25519PrivateKey, X25519PublicKey) from .rsa import RSA, RSAPublic, RSAUsageError, RSA_KEY_SIZES from .ecdsa import ECDSA256P1, ECDSA256P1Public, ECDSAUsageError from .ed25519 import Ed25519, Ed25519Public, Ed25519UsageError from .x25519 import X25519, X25519Public, X25519UsageError class PasswordRequired(Exception): """Raised to indicate that the key is password protected, but a password was not specified.""" pass def load(path, passwd=None): """Try loading a key from the given path. Returns None if the password wasn't specified.""" with open(path, 'rb') as f: raw_pem = f.read() try: pk = serialization.load_pem_private_key( raw_pem, password=passwd, backend=default_backend()) # Unfortunately, the crypto library raises unhelpful exceptions, # so we have to look at the text. except TypeError as e: msg = str(e) if "private key is encrypted" in msg: return None raise e except ValueError: # This seems to happen if the key is a public key, let's try # loading it as a public key. pk = serialization.load_pem_public_key( raw_pem, backend=default_backend()) if isinstance(pk, RSAPrivateKey): if pk.key_size not in RSA_KEY_SIZES: raise Exception("Unsupported RSA key size: " + pk.key_size) return RSA(pk) elif isinstance(pk, RSAPublicKey): if pk.key_size not in RSA_KEY_SIZES: raise Exception("Unsupported RSA key size: " + pk.key_size) return RSAPublic(pk) elif isinstance(pk, EllipticCurvePrivateKey): if pk.curve.name != 'secp256r1': raise Exception("Unsupported EC curve: " + pk.curve.name) if pk.key_size != 256: raise Exception("Unsupported EC size: " + pk.key_size) return ECDSA256P1(pk) elif isinstance(pk, EllipticCurvePublicKey): if pk.curve.name != 'secp256r1': raise Exception("Unsupported EC curve: " + pk.curve.name) if pk.key_size != 256: raise Exception("Unsupported EC size: " + pk.key_size) return ECDSA256P1Public(pk) elif isinstance(pk, Ed25519PrivateKey): return Ed25519(pk) elif isinstance(pk, Ed25519PublicKey): return Ed25519Public(pk) elif isinstance(pk, X25519PrivateKey): return X25519(pk) elif isinstance(pk, X25519PublicKey): return X25519Public(pk) else: raise Exception("Unknown key type: " + str(type(pk)))