blob: 33f221ede57527b595654a65b90ff7936f0fb862 [file] [log] [blame]
David Brown23f91ad2017-05-16 11:38:17 -06001"""
2Cryptographic key management for imgtool.
3"""
4
5from Crypto.Hash import SHA256
6from Crypto.PublicKey import RSA
David Brown07916c32017-05-31 13:38:31 -06007from Crypto.Signature import PKCS1_v1_5, PKCS1_PSS
David Brown23f91ad2017-05-16 11:38:17 -06008from ecdsa import SigningKey, NIST256p, util
David Brown07916c32017-05-31 13:38:31 -06009import hashlib
David Brown23f91ad2017-05-16 11:38:17 -060010from pyasn1.type import namedtype, univ
11from pyasn1.codec.der.encoder import encode
12
David Brown07916c32017-05-31 13:38:31 -060013# By default, we use RSA-PSS (PKCS 2.1). That can be overridden on
14# the command line to support the older (less secure) PKCS1.5
15sign_rsa_pss = True
16
David Brown23f91ad2017-05-16 11:38:17 -060017AUTOGEN_MESSAGE = "/* Autogenerated by imgtool.py, do not edit. */"
18
19class RSAPublicKey(univ.Sequence):
20 componentType = namedtype.NamedTypes(
21 namedtype.NamedType('modulus', univ.Integer()),
22 namedtype.NamedType('publicExponent', univ.Integer()))
23
24class RSA2048():
25 def __init__(self, key):
26 """Construct an RSA2048 key with the given key data"""
27 self.key = key
28
29 @staticmethod
30 def generate():
31 return RSA2048(RSA.generate(2048))
32
33 def export_private(self, path):
34 with open(path, 'wb') as f:
35 f.write(self.key.exportKey('PEM'))
36
David Brownd36e91a2017-09-01 09:33:00 -060037 def get_public_bytes(self):
David Brown23f91ad2017-05-16 11:38:17 -060038 node = RSAPublicKey()
39 node['modulus'] = self.key.n
40 node['publicExponent'] = self.key.e
David Brownd36e91a2017-09-01 09:33:00 -060041 return bytearray(encode(node))
42
43 def emit_c(self):
David Brown23f91ad2017-05-16 11:38:17 -060044 print(AUTOGEN_MESSAGE)
45 print("const unsigned char rsa_pub_key[] = {", end='')
David Brownd36e91a2017-09-01 09:33:00 -060046 encoded = self.get_public_bytes()
David Brown23f91ad2017-05-16 11:38:17 -060047 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 rsa_pub_key_len = {};".format(len(encoded)))
55
David Brownd36e91a2017-09-01 09:33:00 -060056 def emit_rust(self):
57 print(AUTOGEN_MESSAGE)
58 print("static RSA_PUB_KEY: &'static [u8] = &[", end='')
59 encoded = self.get_public_bytes()
60 for count, b in enumerate(encoded):
61 if count % 8 == 0:
62 print("\n ", end='')
63 else:
64 print(" ", end='')
65 print("0x{:02x},".format(b), end='')
66 print("\n];")
67
David Brown23f91ad2017-05-16 11:38:17 -060068 def sig_type(self):
69 """Return the type of this signature (as a string)"""
David Brown07916c32017-05-31 13:38:31 -060070 if sign_rsa_pss:
71 return "PKCS1_PSS_RSA2048_SHA256"
72 else:
73 return "PKCS15_RSA2048_SHA256"
David Brown23f91ad2017-05-16 11:38:17 -060074
75 def sig_len(self):
76 return 256
77
78 def sig_tlv(self):
79 return "RSA2048"
80
81 def sign(self, payload):
82 sha = SHA256.new(payload)
David Brown07916c32017-05-31 13:38:31 -060083 if sign_rsa_pss:
84 signer = PKCS1_PSS.new(self.key)
85 else:
86 signer = PKCS1_v1_5.new(self.key)
David Brown23f91ad2017-05-16 11:38:17 -060087 signature = signer.sign(sha)
88 assert len(signature) == self.sig_len()
89 return signature
90
91class ECDSA256P1():
92 def __init__(self, key):
93 """Construct an ECDSA P-256 private key"""
94 self.key = key
95
96 @staticmethod
97 def generate():
98 return ECDSA256P1(SigningKey.generate(curve=NIST256p))
99
100 def export_private(self, path):
101 with open(path, 'wb') as f:
David Brown8ae61c02017-07-27 10:23:02 -0600102 f.write(self.key.to_pem())
David Brown23f91ad2017-05-16 11:38:17 -0600103
David Browncb1bb482017-09-06 10:31:11 -0600104 def get_public_bytes(self):
105 vk = self.key.get_verifying_key()
106 return bytes(vk.to_der())
107
David Brown23f91ad2017-05-16 11:38:17 -0600108 def emit_c(self):
109 vk = self.key.get_verifying_key()
110 print(AUTOGEN_MESSAGE)
111 print("const unsigned char ecdsa_pub_key[] = {", end='')
112 encoded = bytes(vk.to_der())
113 for count, b in enumerate(encoded):
114 if count % 8 == 0:
115 print("\n\t", end='')
116 else:
117 print(" ", end='')
118 print("0x{:02x},".format(b), end='')
119 print("\n};")
120 print("const unsigned int ecdsa_pub_key_len = {};".format(len(encoded)))
121
David Brown76528c42017-09-05 11:18:35 -0600122 def emit_rust(self):
123 vk = self.key.get_verifying_key()
124 print(AUTOGEN_MESSAGE)
125 print("static ECDSA_PUB_KEY: &'static [u8] = &[", end='')
126 encoded = bytes(vk.to_der())
127 for count, b in enumerate(encoded):
128 if count % 8 == 0:
129 print("\n ", end='')
130 else:
131 print(" ", end='')
132 print("0x{:02x},".format(b), end='')
133 print("\n];")
134
David Brown23f91ad2017-05-16 11:38:17 -0600135 def sign(self, payload):
136 # To make this fixed length, possibly pad with zeros.
137 sig = self.key.sign(payload, hashfunc=hashlib.sha256, sigencode=util.sigencode_der)
138 sig += b'\000' * (self.sig_len() - len(sig))
139 return sig
140
141 def sig_len(self):
142 # The DER encoding depends on the high bit, and can be
143 # anywhere from 70 to 72 bytes. Because we have to fill in
144 # the length field before computing the signature, however,
145 # we'll give the largest, and the sig checking code will allow
146 # for it to be up to two bytes larger than the actual
147 # signature.
148 return 72
149
150 def sig_type(self):
151 """Return the type of this signature (as a string)"""
152 return "ECDSA256_SHA256"
153
154 def sig_tlv(self):
155 return "ECDSA256"
156
157def load(path):
158 with open(path, 'rb') as f:
159 pem = f.read()
160 try:
161 key = RSA.importKey(pem)
162 if key.n.bit_length() != 2048:
163 raise Exception("Unsupported RSA bit length, only 2048 supported")
164 return RSA2048(key)
165 except ValueError:
166 key = SigningKey.from_pem(pem)
167 if key.curve.name != 'NIST256p':
168 raise Exception("Unsupported ECDSA curve")
169 return ECDSA256P1(key)