blob: ee54a0f3545d9427ae26120cbdcb2d8b618524b3 [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
19from Crypto.Hash import SHA256
20from Crypto.PublicKey import RSA
David Brown07916c32017-05-31 13:38:31 -060021from Crypto.Signature import PKCS1_v1_5, PKCS1_PSS
David Brown23f91ad2017-05-16 11:38:17 -060022from ecdsa import SigningKey, NIST256p, util
David Brown07916c32017-05-31 13:38:31 -060023import hashlib
David Brown23f91ad2017-05-16 11:38:17 -060024from pyasn1.type import namedtype, univ
25from pyasn1.codec.der.encoder import encode
26
David Brown07916c32017-05-31 13:38:31 -060027# By default, we use RSA-PSS (PKCS 2.1). That can be overridden on
28# the command line to support the older (less secure) PKCS1.5
29sign_rsa_pss = True
30
David Brown23f91ad2017-05-16 11:38:17 -060031AUTOGEN_MESSAGE = "/* Autogenerated by imgtool.py, do not edit. */"
32
33class RSAPublicKey(univ.Sequence):
34 componentType = namedtype.NamedTypes(
35 namedtype.NamedType('modulus', univ.Integer()),
36 namedtype.NamedType('publicExponent', univ.Integer()))
37
38class RSA2048():
39 def __init__(self, key):
40 """Construct an RSA2048 key with the given key data"""
41 self.key = key
42
43 @staticmethod
44 def generate():
45 return RSA2048(RSA.generate(2048))
46
47 def export_private(self, path):
48 with open(path, 'wb') as f:
49 f.write(self.key.exportKey('PEM'))
50
David Brownd36e91a2017-09-01 09:33:00 -060051 def get_public_bytes(self):
David Brown23f91ad2017-05-16 11:38:17 -060052 node = RSAPublicKey()
53 node['modulus'] = self.key.n
54 node['publicExponent'] = self.key.e
David Brownd36e91a2017-09-01 09:33:00 -060055 return bytearray(encode(node))
56
57 def emit_c(self):
David Brown23f91ad2017-05-16 11:38:17 -060058 print(AUTOGEN_MESSAGE)
59 print("const unsigned char rsa_pub_key[] = {", end='')
David Brownd36e91a2017-09-01 09:33:00 -060060 encoded = self.get_public_bytes()
David Brown23f91ad2017-05-16 11:38:17 -060061 for count, b in enumerate(encoded):
62 if count % 8 == 0:
63 print("\n\t", end='')
64 else:
65 print(" ", end='')
66 print("0x{:02x},".format(b), end='')
67 print("\n};")
68 print("const unsigned int rsa_pub_key_len = {};".format(len(encoded)))
69
David Brownd36e91a2017-09-01 09:33:00 -060070 def emit_rust(self):
71 print(AUTOGEN_MESSAGE)
72 print("static RSA_PUB_KEY: &'static [u8] = &[", end='')
73 encoded = self.get_public_bytes()
74 for count, b in enumerate(encoded):
75 if count % 8 == 0:
76 print("\n ", end='')
77 else:
78 print(" ", end='')
79 print("0x{:02x},".format(b), end='')
80 print("\n];")
81
David Brown23f91ad2017-05-16 11:38:17 -060082 def sig_type(self):
83 """Return the type of this signature (as a string)"""
David Brown07916c32017-05-31 13:38:31 -060084 if sign_rsa_pss:
85 return "PKCS1_PSS_RSA2048_SHA256"
86 else:
87 return "PKCS15_RSA2048_SHA256"
David Brown23f91ad2017-05-16 11:38:17 -060088
89 def sig_len(self):
90 return 256
91
92 def sig_tlv(self):
93 return "RSA2048"
94
95 def sign(self, payload):
96 sha = SHA256.new(payload)
David Brown07916c32017-05-31 13:38:31 -060097 if sign_rsa_pss:
98 signer = PKCS1_PSS.new(self.key)
99 else:
100 signer = PKCS1_v1_5.new(self.key)
David Brown23f91ad2017-05-16 11:38:17 -0600101 signature = signer.sign(sha)
102 assert len(signature) == self.sig_len()
103 return signature
104
105class ECDSA256P1():
106 def __init__(self, key):
107 """Construct an ECDSA P-256 private key"""
108 self.key = key
109
110 @staticmethod
111 def generate():
112 return ECDSA256P1(SigningKey.generate(curve=NIST256p))
113
114 def export_private(self, path):
115 with open(path, 'wb') as f:
David Brown8ae61c02017-07-27 10:23:02 -0600116 f.write(self.key.to_pem())
David Brown23f91ad2017-05-16 11:38:17 -0600117
David Browncb1bb482017-09-06 10:31:11 -0600118 def get_public_bytes(self):
119 vk = self.key.get_verifying_key()
120 return bytes(vk.to_der())
121
David Brown23f91ad2017-05-16 11:38:17 -0600122 def emit_c(self):
123 vk = self.key.get_verifying_key()
124 print(AUTOGEN_MESSAGE)
125 print("const unsigned char ecdsa_pub_key[] = {", end='')
126 encoded = bytes(vk.to_der())
127 for count, b in enumerate(encoded):
128 if count % 8 == 0:
129 print("\n\t", end='')
130 else:
131 print(" ", end='')
132 print("0x{:02x},".format(b), end='')
133 print("\n};")
134 print("const unsigned int ecdsa_pub_key_len = {};".format(len(encoded)))
135
David Brown76528c42017-09-05 11:18:35 -0600136 def emit_rust(self):
137 vk = self.key.get_verifying_key()
138 print(AUTOGEN_MESSAGE)
139 print("static ECDSA_PUB_KEY: &'static [u8] = &[", end='')
140 encoded = bytes(vk.to_der())
141 for count, b in enumerate(encoded):
142 if count % 8 == 0:
143 print("\n ", end='')
144 else:
145 print(" ", end='')
146 print("0x{:02x},".format(b), end='')
147 print("\n];")
148
David Brown23f91ad2017-05-16 11:38:17 -0600149 def sign(self, payload):
150 # To make this fixed length, possibly pad with zeros.
151 sig = self.key.sign(payload, hashfunc=hashlib.sha256, sigencode=util.sigencode_der)
152 sig += b'\000' * (self.sig_len() - len(sig))
153 return sig
154
155 def sig_len(self):
156 # The DER encoding depends on the high bit, and can be
157 # anywhere from 70 to 72 bytes. Because we have to fill in
158 # the length field before computing the signature, however,
159 # we'll give the largest, and the sig checking code will allow
160 # for it to be up to two bytes larger than the actual
161 # signature.
162 return 72
163
164 def sig_type(self):
165 """Return the type of this signature (as a string)"""
166 return "ECDSA256_SHA256"
167
168 def sig_tlv(self):
169 return "ECDSA256"
170
171def load(path):
172 with open(path, 'rb') as f:
173 pem = f.read()
174 try:
175 key = RSA.importKey(pem)
176 if key.n.bit_length() != 2048:
177 raise Exception("Unsupported RSA bit length, only 2048 supported")
178 return RSA2048(key)
179 except ValueError:
180 key = SigningKey.from_pem(pem)
181 if key.curve.name != 'NIST256p':
182 raise Exception("Unsupported ECDSA curve")
183 return ECDSA256P1(key)