blob: d529b599b2f9afdba6f8335a5df7e3284f407858 [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
7from Crypto.Signature import PKCS1_v1_5
8from ecdsa import SigningKey, NIST256p, util
9from pyasn1.type import namedtype, univ
10from pyasn1.codec.der.encoder import encode
11
12AUTOGEN_MESSAGE = "/* Autogenerated by imgtool.py, do not edit. */"
13
14class RSAPublicKey(univ.Sequence):
15 componentType = namedtype.NamedTypes(
16 namedtype.NamedType('modulus', univ.Integer()),
17 namedtype.NamedType('publicExponent', univ.Integer()))
18
19class RSA2048():
20 def __init__(self, key):
21 """Construct an RSA2048 key with the given key data"""
22 self.key = key
23
24 @staticmethod
25 def generate():
26 return RSA2048(RSA.generate(2048))
27
28 def export_private(self, path):
29 with open(path, 'wb') as f:
30 f.write(self.key.exportKey('PEM'))
31
32 def emit_c(self):
33 node = RSAPublicKey()
34 node['modulus'] = self.key.n
35 node['publicExponent'] = self.key.e
36 print(AUTOGEN_MESSAGE)
37 print("const unsigned char rsa_pub_key[] = {", end='')
38 encoded = bytearray(encode(node))
39 for count, b in enumerate(encoded):
40 if count % 8 == 0:
41 print("\n\t", end='')
42 else:
43 print(" ", end='')
44 print("0x{:02x},".format(b), end='')
45 print("\n};")
46 print("const unsigned int rsa_pub_key_len = {};".format(len(encoded)))
47
48 def sig_type(self):
49 """Return the type of this signature (as a string)"""
50 return "PKCS15_RSA2048_SHA256"
51
52 def sig_len(self):
53 return 256
54
55 def sig_tlv(self):
56 return "RSA2048"
57
58 def sign(self, payload):
59 sha = SHA256.new(payload)
60 signer = PKCS1_v1_5.new(self.key)
61 signature = signer.sign(sha)
62 assert len(signature) == self.sig_len()
63 return signature
64
65class ECDSA256P1():
66 def __init__(self, key):
67 """Construct an ECDSA P-256 private key"""
68 self.key = key
69
70 @staticmethod
71 def generate():
72 return ECDSA256P1(SigningKey.generate(curve=NIST256p))
73
74 def export_private(self, path):
75 with open(path, 'wb') as f:
76 f.write(key.to_pem())
77
78 def emit_c(self):
79 vk = self.key.get_verifying_key()
80 print(AUTOGEN_MESSAGE)
81 print("const unsigned char ecdsa_pub_key[] = {", end='')
82 encoded = bytes(vk.to_der())
83 for count, b in enumerate(encoded):
84 if count % 8 == 0:
85 print("\n\t", end='')
86 else:
87 print(" ", end='')
88 print("0x{:02x},".format(b), end='')
89 print("\n};")
90 print("const unsigned int ecdsa_pub_key_len = {};".format(len(encoded)))
91
92 def sign(self, payload):
93 # To make this fixed length, possibly pad with zeros.
94 sig = self.key.sign(payload, hashfunc=hashlib.sha256, sigencode=util.sigencode_der)
95 sig += b'\000' * (self.sig_len() - len(sig))
96 return sig
97
98 def sig_len(self):
99 # The DER encoding depends on the high bit, and can be
100 # anywhere from 70 to 72 bytes. Because we have to fill in
101 # the length field before computing the signature, however,
102 # we'll give the largest, and the sig checking code will allow
103 # for it to be up to two bytes larger than the actual
104 # signature.
105 return 72
106
107 def sig_type(self):
108 """Return the type of this signature (as a string)"""
109 return "ECDSA256_SHA256"
110
111 def sig_tlv(self):
112 return "ECDSA256"
113
114def load(path):
115 with open(path, 'rb') as f:
116 pem = f.read()
117 try:
118 key = RSA.importKey(pem)
119 if key.n.bit_length() != 2048:
120 raise Exception("Unsupported RSA bit length, only 2048 supported")
121 return RSA2048(key)
122 except ValueError:
123 key = SigningKey.from_pem(pem)
124 if key.curve.name != 'NIST256p':
125 raise Exception("Unsupported ECDSA curve")
126 return ECDSA256P1(key)