blob: a812ba2f19481c18f8098acf5d8a9d1c46673ca4 [file] [log] [blame]
Denis Mingulov3113df82023-09-26 21:20:39 +03001# SPDX-License-Identifier: Apache-2.0
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
15import pytest
16
17import subprocess
18from click.testing import CliRunner
19from imgtool import main as imgtool_main
20from imgtool.main import imgtool
21
22# all supported key types for 'keygen'
23KEY_TYPES = [*imgtool_main.keygens]
24KEY_ENCODINGS = [*imgtool_main.valid_encodings]
25PUB_HASH_ENCODINGS = [*imgtool_main.valid_hash_encodings]
26PVT_KEY_FORMATS = [*imgtool_main.valid_formats]
27
28OPENSSL_KEY_TYPES = {
29 "rsa-2048": "Private-Key: (2048 bit, 2 primes)",
30 "rsa-3072": "Private-Key: (3072 bit, 2 primes)",
31 "ecdsa-p256": "Private-Key: (256 bit)",
32 "ecdsa-p384": "Private-Key: (384 bit)",
33 "ed25519": "ED25519 Private-Key:",
34 "x25519": "X25519 Private-Key:",
35}
36
37GEN_KEY_EXT = ".key"
38GEN_ANOTHER_KEY_EXT = ".another.key"
39PUB_KEY_EXT = ".pub"
40PUB_KEY_HASH_EXT = ".pubhash"
41
42
43def tmp_name(tmp_path, key_type, suffix=""):
44 return tmp_path / (key_type + suffix)
45
46
47@pytest.fixture(scope="session")
48def tmp_path_persistent(tmp_path_factory):
49 return tmp_path_factory.mktemp("keys")
50
51
52@pytest.mark.parametrize("key_type", KEY_TYPES)
53def test_keygen(key_type, tmp_path_persistent):
54 """Generate keys by imgtool"""
55
56 runner = CliRunner()
57
58 gen_key = tmp_name(tmp_path_persistent, key_type, GEN_KEY_EXT)
59
60 assert not gen_key.exists()
61 result = runner.invoke(
62 imgtool, ["keygen", "--key", str(gen_key), "--type", key_type]
63 )
64 assert result.exit_code == 0
65 assert gen_key.exists()
66 assert gen_key.stat().st_size > 0
67
68 # another key
69 gen_key2 = tmp_name(tmp_path_persistent, key_type, GEN_ANOTHER_KEY_EXT)
70
71 assert str(gen_key2) != str(gen_key)
72
73 assert not gen_key2.exists()
74 result = runner.invoke(
75 imgtool, ["keygen", "--key", str(gen_key2), "--type", key_type]
76 )
77 assert result.exit_code == 0
78 assert gen_key2.exists()
79 assert gen_key2.stat().st_size > 0
80
81 # content must be different
82 assert gen_key.read_bytes() != gen_key2.read_bytes()
83
84
85@pytest.mark.parametrize("key_type", KEY_TYPES)
86def test_keygen_type(key_type, tmp_path_persistent):
87 """Check generated keys"""
88 assert key_type in OPENSSL_KEY_TYPES
89
90 gen_key = tmp_name(tmp_path_persistent, key_type, GEN_KEY_EXT)
91
92 result = subprocess.run(
93 ["openssl", "pkey", "-in", str(gen_key), "-check", "-noout", "-text"],
94 capture_output=True,
95 text=True,
96 )
97 assert result.returncode == 0
98 assert "Key is valid" in result.stdout
99 assert OPENSSL_KEY_TYPES[key_type] in result.stdout
100
101
102@pytest.mark.parametrize("key_type", KEY_TYPES)
103@pytest.mark.parametrize("format", PVT_KEY_FORMATS)
104def test_getpriv(key_type, format, tmp_path_persistent):
105 """Get private key"""
106 runner = CliRunner()
107
108 gen_key = tmp_name(tmp_path_persistent, key_type, GEN_KEY_EXT)
109
110 result = runner.invoke(
111 imgtool,
112 [
113 "getpriv",
114 "--key",
115 str(gen_key),
116 "--format",
117 format,
118 ],
119 )
120 assert result.exit_code == 0
121
122
123@pytest.mark.parametrize("key_type", KEY_TYPES)
124@pytest.mark.parametrize("encoding", KEY_ENCODINGS)
125def test_getpub(key_type, encoding, tmp_path_persistent):
126 """Get public key"""
127 runner = CliRunner()
128
129 gen_key = tmp_name(tmp_path_persistent, key_type, GEN_KEY_EXT)
130 pub_key = tmp_name(tmp_path_persistent, key_type, PUB_KEY_EXT
131 + "." + encoding)
132
133 assert not pub_key.exists()
134 result = runner.invoke(
135 imgtool,
136 [
137 "getpub",
138 "--key",
139 str(gen_key),
140 "--output",
141 str(pub_key),
142 "--encoding",
143 encoding,
144 ],
145 )
146 assert result.exit_code == 0
147 assert pub_key.exists()
148 assert pub_key.stat().st_size > 0
149
150
151@pytest.mark.parametrize("key_type", KEY_TYPES)
152@pytest.mark.parametrize("encoding", PUB_HASH_ENCODINGS)
153def test_getpubhash(key_type, encoding, tmp_path_persistent):
154 """Get the hash of the public key"""
155 runner = CliRunner()
156
157 gen_key = tmp_name(tmp_path_persistent, key_type, GEN_KEY_EXT)
158 pub_key_hash = tmp_name(
159 tmp_path_persistent, key_type, PUB_KEY_HASH_EXT + "." + encoding
160 )
161
162 assert not pub_key_hash.exists()
163 result = runner.invoke(
164 imgtool,
165 [
166 "getpubhash",
167 "--key",
168 str(gen_key),
169 "--output",
170 str(pub_key_hash),
171 "--encoding",
172 encoding,
173 ],
174 )
175 assert result.exit_code == 0
176 assert pub_key_hash.exists()
177 assert pub_key_hash.stat().st_size > 0
178
179
180@pytest.mark.parametrize("key_type", KEY_TYPES)
181def test_sign_verify(key_type, tmp_path_persistent):
182 """Test basic sign and verify"""
183 runner = CliRunner()
184
185 gen_key = tmp_name(tmp_path_persistent, key_type, GEN_KEY_EXT)
186 wrong_key = tmp_name(tmp_path_persistent, key_type, GEN_ANOTHER_KEY_EXT)
187 image = tmp_name(tmp_path_persistent, "image", "bin")
188 image_signed = tmp_name(tmp_path_persistent, "image", "signed")
189
190 with image.open("wb") as f:
191 f.write(b"\x00" * 1024)
192
193 # not all required arguments are provided
194 result = runner.invoke(
195 imgtool,
196 [
197 "sign",
198 "--key",
199 str(gen_key),
200 str(image),
201 str(image_signed),
202 ],
203 )
204 assert result.exit_code != 0
205 assert not image_signed.exists()
206
207 result = runner.invoke(
208 imgtool,
209 [
210 "sign",
211 "--key",
212 str(gen_key),
213 "--align",
214 "16",
215 "--version",
216 "1.0.0",
217 "--header-size",
218 "0x400",
219 "--slot-size",
220 "0x10000",
221 "--pad-header",
222 str(image),
223 str(image_signed),
224 ],
225 )
226 assert result.exit_code == 0
227 assert image_signed.exists()
228 assert image_signed.stat().st_size > 0
229
230 # original key can be used to verify a signed image
231 result = runner.invoke(
232 imgtool,
233 [
234 "verify",
235 "--key",
236 str(gen_key),
237 str(image_signed),
238 ],
239 )
240 assert result.exit_code == 0
241
242 # 'another' key is not valid to verify a signed image
243 result = runner.invoke(
244 imgtool,
245 [
246 "verify",
247 "--key",
248 str(wrong_key),
249 str(image_signed),
250 ],
251 )
252 assert result.exit_code != 0
253 image_signed.unlink()