blob: fddfec9b1a147b98cb70ef296da9efa7d9c81221 [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
104 def emit_c(self):
105 vk = self.key.get_verifying_key()
106 print(AUTOGEN_MESSAGE)
107 print("const unsigned char ecdsa_pub_key[] = {", end='')
108 encoded = bytes(vk.to_der())
109 for count, b in enumerate(encoded):
110 if count % 8 == 0:
111 print("\n\t", end='')
112 else:
113 print(" ", end='')
114 print("0x{:02x},".format(b), end='')
115 print("\n};")
116 print("const unsigned int ecdsa_pub_key_len = {};".format(len(encoded)))
117
118 def sign(self, payload):
119 # To make this fixed length, possibly pad with zeros.
120 sig = self.key.sign(payload, hashfunc=hashlib.sha256, sigencode=util.sigencode_der)
121 sig += b'\000' * (self.sig_len() - len(sig))
122 return sig
123
124 def sig_len(self):
125 # The DER encoding depends on the high bit, and can be
126 # anywhere from 70 to 72 bytes. Because we have to fill in
127 # the length field before computing the signature, however,
128 # we'll give the largest, and the sig checking code will allow
129 # for it to be up to two bytes larger than the actual
130 # signature.
131 return 72
132
133 def sig_type(self):
134 """Return the type of this signature (as a string)"""
135 return "ECDSA256_SHA256"
136
137 def sig_tlv(self):
138 return "ECDSA256"
139
140def load(path):
141 with open(path, 'rb') as f:
142 pem = f.read()
143 try:
144 key = RSA.importKey(pem)
145 if key.n.bit_length() != 2048:
146 raise Exception("Unsupported RSA bit length, only 2048 supported")
147 return RSA2048(key)
148 except ValueError:
149 key = SigningKey.from_pem(pem)
150 if key.curve.name != 'NIST256p':
151 raise Exception("Unsupported ECDSA curve")
152 return ECDSA256P1(key)