blob: 8a2c50fcdd0b8de8510a88fdaf5f0acb973b794d [file] [log] [blame]
David Brown1314bf32017-12-20 11:10:55 -07001# Copyright 2017 Linaro Limited
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
David Brown23f91ad2017-05-16 11:38:17 -060015"""
16Cryptographic key management for imgtool.
17"""
18
David Brown5e7c6dd2017-11-16 14:47:16 -070019from cryptography.hazmat.backends import default_backend
20from cryptography.hazmat.primitives import serialization
21from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey, RSAPublicKey
David Brown23f91ad2017-05-16 11:38:17 -060022
David Brown5e7c6dd2017-11-16 14:47:16 -070023from .rsa import RSA2048, RSA2048Public, RSAUsageError
David Brown23f91ad2017-05-16 11:38:17 -060024
25class ECDSA256P1():
26 def __init__(self, key):
27 """Construct an ECDSA P-256 private key"""
28 self.key = key
29
30 @staticmethod
31 def generate():
32 return ECDSA256P1(SigningKey.generate(curve=NIST256p))
33
34 def export_private(self, path):
35 with open(path, 'wb') as f:
David Brown8ae61c02017-07-27 10:23:02 -060036 f.write(self.key.to_pem())
David Brown23f91ad2017-05-16 11:38:17 -060037
David Browncb1bb482017-09-06 10:31:11 -060038 def get_public_bytes(self):
39 vk = self.key.get_verifying_key()
40 return bytes(vk.to_der())
41
David Brown23f91ad2017-05-16 11:38:17 -060042 def emit_c(self):
43 vk = self.key.get_verifying_key()
44 print(AUTOGEN_MESSAGE)
45 print("const unsigned char ecdsa_pub_key[] = {", end='')
46 encoded = bytes(vk.to_der())
47 for count, b in enumerate(encoded):
48 if count % 8 == 0:
49 print("\n\t", end='')
50 else:
51 print(" ", end='')
52 print("0x{:02x},".format(b), end='')
53 print("\n};")
54 print("const unsigned int ecdsa_pub_key_len = {};".format(len(encoded)))
55
David Brown76528c42017-09-05 11:18:35 -060056 def emit_rust(self):
57 vk = self.key.get_verifying_key()
58 print(AUTOGEN_MESSAGE)
59 print("static ECDSA_PUB_KEY: &'static [u8] = &[", end='')
60 encoded = bytes(vk.to_der())
61 for count, b in enumerate(encoded):
62 if count % 8 == 0:
63 print("\n ", end='')
64 else:
65 print(" ", end='')
66 print("0x{:02x},".format(b), end='')
67 print("\n];")
68
David Brown23f91ad2017-05-16 11:38:17 -060069 def sign(self, payload):
70 # To make this fixed length, possibly pad with zeros.
71 sig = self.key.sign(payload, hashfunc=hashlib.sha256, sigencode=util.sigencode_der)
72 sig += b'\000' * (self.sig_len() - len(sig))
73 return sig
74
75 def sig_len(self):
76 # The DER encoding depends on the high bit, and can be
77 # anywhere from 70 to 72 bytes. Because we have to fill in
78 # the length field before computing the signature, however,
79 # we'll give the largest, and the sig checking code will allow
80 # for it to be up to two bytes larger than the actual
81 # signature.
82 return 72
83
84 def sig_type(self):
85 """Return the type of this signature (as a string)"""
86 return "ECDSA256_SHA256"
87
88 def sig_tlv(self):
89 return "ECDSA256"
90
David Brown5e7c6dd2017-11-16 14:47:16 -070091class PasswordRequired(Exception):
92 """Raised to indicate that the key is password protected, but a
93 password was not specified."""
94 pass
95
96def load(path, passwd=None):
97 """Try loading a key from the given path. Returns None if the password wasn't specified."""
David Brown23f91ad2017-05-16 11:38:17 -060098 with open(path, 'rb') as f:
David Brown5e7c6dd2017-11-16 14:47:16 -070099 raw_pem = f.read()
David Brown23f91ad2017-05-16 11:38:17 -0600100 try:
David Brown5e7c6dd2017-11-16 14:47:16 -0700101 pk = serialization.load_pem_private_key(
102 raw_pem,
103 password=passwd,
104 backend=default_backend())
105 # This is a bit nonsensical of an exception, but it is what
106 # cryptography seems to currently raise if the password is needed.
107 except TypeError:
108 return None
David Brown23f91ad2017-05-16 11:38:17 -0600109 except ValueError:
David Brown5e7c6dd2017-11-16 14:47:16 -0700110 # This seems to happen if the key is a public key, let's try
111 # loading it as a public key.
112 pk = serialization.load_pem_public_key(
113 raw_pem,
114 backend=default_backend())
115
116 if isinstance(pk, RSAPrivateKey):
117 if pk.key_size != 2048:
118 raise Exception("Unsupported RSA key size: " + pk.key_size)
119 return RSA2048(pk)
120 elif isinstance(pk, RSAPublicKey):
121 if pk.key_size != 2048:
122 raise Exception("Unsupported RSA key size: " + pk.key_size)
123 return RSA2048Public(pk)
124 else:
125 raise Exception("Unknown key type: " + str(type(pk)))