blob: b652d902ce389f3cdf25c4e00e1e5f7a2a6bb1bf [file] [log] [blame]
Tamas Banf70ef8c2017-12-19 15:35:09 +00001# Copyright 2017 Linaro Limited
Tamas Ban861835c2019-05-13 08:59:38 +01002# Copyright (c) 2017-2019, Arm Limited.
Tamas Banf70ef8c2017-12-19 15:35:09 +00003#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16"""
17Cryptographic key management for imgtool.
18"""
19
Gabor Kertesz33e9b232018-09-12 15:38:41 +020020from __future__ import print_function
Tamas Banf70ef8c2017-12-19 15:35:09 +000021from Crypto.Hash import SHA256
22from Crypto.PublicKey import RSA
23from Crypto.Signature import PKCS1_v1_5, PKCS1_PSS
Tamas Banf70ef8c2017-12-19 15:35:09 +000024import hashlib
25from pyasn1.type import namedtype, univ
26from pyasn1.codec.der.encoder import encode
27
Tamas Ban861835c2019-05-13 08:59:38 +010028# Sizes that bootutil will recognize
29RSA_KEY_SIZES = [2048, 3072]
30
Tamas Banf70ef8c2017-12-19 15:35:09 +000031# By default, we use RSA-PSS (PKCS 2.1). That can be overridden on
32# the command line to support the older (less secure) PKCS1.5
33sign_rsa_pss = True
34
35AUTOGEN_MESSAGE = "/* Autogenerated by imgtool.py, do not edit. */"
36
Tamas Ban861835c2019-05-13 08:59:38 +010037class RSAUsageError(Exception):
38 pass
39
Tamas Banf70ef8c2017-12-19 15:35:09 +000040class RSAPublicKey(univ.Sequence):
41 componentType = namedtype.NamedTypes(
42 namedtype.NamedType('modulus', univ.Integer()),
43 namedtype.NamedType('publicExponent', univ.Integer()))
44
Tamas Ban861835c2019-05-13 08:59:38 +010045class RSAutil():
Tamas Ban32d84642019-07-11 08:25:11 +010046 def __init__(self, key, public_key_format='hash'):
Tamas Ban861835c2019-05-13 08:59:38 +010047 """Construct an RSA key with the given key data"""
Tamas Banf70ef8c2017-12-19 15:35:09 +000048 self.key = key
Tamas Ban32d84642019-07-11 08:25:11 +010049 self.public_key_format = public_key_format
Tamas Banf70ef8c2017-12-19 15:35:09 +000050
Tamas Ban861835c2019-05-13 08:59:38 +010051 def key_size(self):
52 return self.key.n.bit_length()
53
Tamas Ban32d84642019-07-11 08:25:11 +010054 def get_public_key_format(self):
55 return self.public_key_format
56
Tamas Banf70ef8c2017-12-19 15:35:09 +000057 @staticmethod
Tamas Ban861835c2019-05-13 08:59:38 +010058 def generate(key_size=2048):
59 if key_size not in RSA_KEY_SIZES:
60 raise RSAUsageError("Key size {} is not supported by MCUboot"
61 .format(key_size))
62 return RSAutil(RSA.generate(key_size))
Tamas Banf70ef8c2017-12-19 15:35:09 +000063
64 def export_private(self, path):
65 with open(path, 'wb') as f:
66 f.write(self.key.exportKey('PEM'))
67
68 def get_public_bytes(self):
69 node = RSAPublicKey()
70 node['modulus'] = self.key.n
71 node['publicExponent'] = self.key.e
72 return bytearray(encode(node))
73
74 def emit_c(self):
75 print(AUTOGEN_MESSAGE)
76 print("const unsigned char rsa_pub_key[] = {", end='')
77 encoded = self.get_public_bytes()
78 for count, b in enumerate(encoded):
79 if count % 8 == 0:
80 print("\n\t", end='')
81 else:
82 print(" ", end='')
83 print("0x{:02x},".format(b), end='')
84 print("\n};")
85 print("const unsigned int rsa_pub_key_len = {};".format(len(encoded)))
86
Tamas Banf70ef8c2017-12-19 15:35:09 +000087 def sig_type(self):
88 """Return the type of this signature (as a string)"""
89 if sign_rsa_pss:
Tamas Ban861835c2019-05-13 08:59:38 +010090 return "PKCS1_PSS_RSA{}_SHA256".format(self.key_size())
Tamas Banf70ef8c2017-12-19 15:35:09 +000091 else:
Tamas Ban861835c2019-05-13 08:59:38 +010092 return "PKCS15_RSA{}_SHA256".format(self.key_size())
Tamas Banf70ef8c2017-12-19 15:35:09 +000093
94 def sig_len(self):
Tamas Ban861835c2019-05-13 08:59:38 +010095 return 256 if self.key_size() == 2048 else 384
Tamas Banf70ef8c2017-12-19 15:35:09 +000096
97 def sig_tlv(self):
Tamas Ban861835c2019-05-13 08:59:38 +010098 return "RSA2048" if self.key_size() == 2048 else "RSA3072"
Tamas Banf70ef8c2017-12-19 15:35:09 +000099
100 def sign(self, payload):
Tamas Ban581034a2017-12-19 19:54:37 +0000101 converted_payload = bytes(payload)
102 sha = SHA256.new(converted_payload)
Tamas Banf70ef8c2017-12-19 15:35:09 +0000103 if sign_rsa_pss:
104 signer = PKCS1_PSS.new(self.key)
105 else:
106 signer = PKCS1_v1_5.new(self.key)
107 signature = signer.sign(sha)
108 assert len(signature) == self.sig_len()
109 return signature
110
Tamas Ban32d84642019-07-11 08:25:11 +0100111def load(path, public_key_format='hash'):
Tamas Banf70ef8c2017-12-19 15:35:09 +0000112 with open(path, 'rb') as f:
113 pem = f.read()
114 try:
115 key = RSA.importKey(pem)
Tamas Ban32d84642019-07-11 08:25:11 +0100116 return RSAutil(key, public_key_format)
Tamas Banf70ef8c2017-12-19 15:35:09 +0000117 except ValueError:
Tamas Ban581034a2017-12-19 19:54:37 +0000118 raise Exception("Unsupported RSA key file")