blob: 7610106bc8d4760ff5d27162a3ac428d7b0ffa29 [file] [log] [blame]
David Brown5e7c6dd2017-11-16 14:47:16 -07001"""
2Tests for RSA keys
3"""
4
David Brown79c4fcf2021-01-26 15:04:05 -07005# SPDX-License-Identifier: Apache-2.0
6
David Brown5e7c6dd2017-11-16 14:47:16 -07007import io
8import os
9import sys
10import tempfile
11import unittest
12
13from cryptography.exceptions import InvalidSignature
14from cryptography.hazmat.primitives.asymmetric.padding import PSS, MGF1
15from cryptography.hazmat.primitives.hashes import SHA256
16
17# Setup sys path so 'imgtool' is in it.
Fabio Utzig19fd79a2019-05-08 18:20:39 -030018sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__),
19 '../..')))
David Brown5e7c6dd2017-11-16 14:47:16 -070020
Fabio Utzig19fd79a2019-05-08 18:20:39 -030021from imgtool.keys import load, RSA, RSAUsageError
22from imgtool.keys.rsa import RSA_KEY_SIZES
23
David Brown5e7c6dd2017-11-16 14:47:16 -070024
25class KeyGeneration(unittest.TestCase):
26
27 def setUp(self):
28 self.test_dir = tempfile.TemporaryDirectory()
29
30 def tname(self, base):
31 return os.path.join(self.test_dir.name, base)
32
33 def tearDown(self):
34 self.test_dir.cleanup()
35
36 def test_keygen(self):
Fabio Utzig19fd79a2019-05-08 18:20:39 -030037 # Try generating a RSA key with non-supported size
38 with self.assertRaises(RSAUsageError):
39 RSA.generate(key_size=1024)
David Brown5e7c6dd2017-11-16 14:47:16 -070040
Fabio Utzig19fd79a2019-05-08 18:20:39 -030041 for key_size in RSA_KEY_SIZES:
42 name1 = self.tname("keygen.pem")
43 k = RSA.generate(key_size=key_size)
44 k.export_private(name1, b'secret')
David Brown5e7c6dd2017-11-16 14:47:16 -070045
Fabio Utzig19fd79a2019-05-08 18:20:39 -030046 # Try loading the key without a password.
47 self.assertIsNone(load(name1))
David Brown5e7c6dd2017-11-16 14:47:16 -070048
Fabio Utzig19fd79a2019-05-08 18:20:39 -030049 k2 = load(name1, b'secret')
David Brown5e7c6dd2017-11-16 14:47:16 -070050
Fabio Utzig19fd79a2019-05-08 18:20:39 -030051 pubname = self.tname('keygen-pub.pem')
52 k2.export_public(pubname)
53 pk2 = load(pubname)
54
55 # We should be able to export the public key from the loaded
56 # public key, but not the private key.
57 pk2.export_public(self.tname('keygen-pub2.pem'))
58 self.assertRaises(RSAUsageError, pk2.export_private,
59 self.tname('keygen-priv2.pem'))
David Brown5e7c6dd2017-11-16 14:47:16 -070060
61 def test_emit(self):
62 """Basic sanity check on the code emitters."""
Fabio Utzig19fd79a2019-05-08 18:20:39 -030063 for key_size in RSA_KEY_SIZES:
64 k = RSA.generate(key_size=key_size)
David Brown5e7c6dd2017-11-16 14:47:16 -070065
Denis Mingulovfaf2dd12023-09-26 09:22:44 +030066 pubpem = io.StringIO()
67 k.emit_public_pem(pubpem)
68 self.assertIn("BEGIN PUBLIC KEY", pubpem.getvalue())
69 self.assertIn("END PUBLIC KEY", pubpem.getvalue())
70
Fabio Utzig19fd79a2019-05-08 18:20:39 -030071 ccode = io.StringIO()
Fabio Utzig9560d772020-04-02 13:44:30 -030072 k.emit_c_public(ccode)
Fabio Utzig19fd79a2019-05-08 18:20:39 -030073 self.assertIn("rsa_pub_key", ccode.getvalue())
74 self.assertIn("rsa_pub_key_len", ccode.getvalue())
David Brown5e7c6dd2017-11-16 14:47:16 -070075
Denis Mingulovfaf2dd12023-09-26 09:22:44 +030076 hashccode = io.StringIO()
77 k.emit_c_public_hash(hashccode)
78 self.assertIn("rsa_pub_key_hash", hashccode.getvalue())
79 self.assertIn("rsa_pub_key_hash_len", hashccode.getvalue())
80
Fabio Utzig19fd79a2019-05-08 18:20:39 -030081 rustcode = io.StringIO()
Fabio Utzig9560d772020-04-02 13:44:30 -030082 k.emit_rust_public(rustcode)
Fabio Utzig19fd79a2019-05-08 18:20:39 -030083 self.assertIn("RSA_PUB_KEY", rustcode.getvalue())
David Brown5e7c6dd2017-11-16 14:47:16 -070084
Denis Mingulovfaf2dd12023-09-26 09:22:44 +030085 # raw data - bytes
86 pubraw = io.BytesIO()
87 k.emit_raw_public(pubraw)
88 self.assertTrue(len(pubraw.getvalue()) > 0)
89
90 hashraw = io.BytesIO()
91 k.emit_raw_public_hash(hashraw)
92 self.assertTrue(len(hashraw.getvalue()) > 0)
93
David Brown5e7c6dd2017-11-16 14:47:16 -070094 def test_emit_pub(self):
95 """Basic sanity check on the code emitters, from public key."""
96 pubname = self.tname("public.pem")
Fabio Utzig19fd79a2019-05-08 18:20:39 -030097 for key_size in RSA_KEY_SIZES:
98 k = RSA.generate(key_size=key_size)
99 k.export_public(pubname)
David Brown5e7c6dd2017-11-16 14:47:16 -0700100
Fabio Utzig19fd79a2019-05-08 18:20:39 -0300101 k2 = load(pubname)
David Brown5e7c6dd2017-11-16 14:47:16 -0700102
Fabio Utzig19fd79a2019-05-08 18:20:39 -0300103 ccode = io.StringIO()
Fabio Utzig9560d772020-04-02 13:44:30 -0300104 k2.emit_c_public(ccode)
Fabio Utzig19fd79a2019-05-08 18:20:39 -0300105 self.assertIn("rsa_pub_key", ccode.getvalue())
106 self.assertIn("rsa_pub_key_len", ccode.getvalue())
David Brown5e7c6dd2017-11-16 14:47:16 -0700107
Fabio Utzig19fd79a2019-05-08 18:20:39 -0300108 rustcode = io.StringIO()
Fabio Utzig9560d772020-04-02 13:44:30 -0300109 k2.emit_rust_public(rustcode)
Fabio Utzig19fd79a2019-05-08 18:20:39 -0300110 self.assertIn("RSA_PUB_KEY", rustcode.getvalue())
David Brown5e7c6dd2017-11-16 14:47:16 -0700111
112 def test_sig(self):
Fabio Utzig19fd79a2019-05-08 18:20:39 -0300113 for key_size in RSA_KEY_SIZES:
114 k = RSA.generate(key_size=key_size)
115 buf = b'This is the message'
116 sig = k.sign(buf)
David Brown5e7c6dd2017-11-16 14:47:16 -0700117
Fabio Utzig19fd79a2019-05-08 18:20:39 -0300118 # The code doesn't have any verification, so verify this
119 # manually.
120 k.key.public_key().verify(
David Brown5e7c6dd2017-11-16 14:47:16 -0700121 signature=sig,
122 data=buf,
David Brown20462a72017-11-21 14:28:51 -0700123 padding=PSS(mgf=MGF1(SHA256()), salt_length=32),
David Brown5e7c6dd2017-11-16 14:47:16 -0700124 algorithm=SHA256())
125
Fabio Utzig19fd79a2019-05-08 18:20:39 -0300126 # Modify the message to make sure the signature fails.
127 self.assertRaises(InvalidSignature,
128 k.key.public_key().verify,
129 signature=sig,
130 data=b'This is thE message',
131 padding=PSS(mgf=MGF1(SHA256()), salt_length=32),
132 algorithm=SHA256())
133
David Brown5e7c6dd2017-11-16 14:47:16 -0700134
135if __name__ == '__main__':
136 unittest.main()