• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2021 The Pigweed Authors
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may not
4# use this file except in compliance with the License. You may obtain a copy of
5# the License at
6#
7#     https://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, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations under
13# the License.
14"""Unit tests for pw_software_update/keys.py."""
15
16from pathlib import Path
17import tempfile
18import unittest
19
20from pw_software_update import keys
21from pw_software_update.tuf_pb2 import Key, KeyType, KeyScheme
22
23
24class KeyGenTest(unittest.TestCase):
25    """Test the generation of keys."""
26    def test_ecdsa_keygen(self):
27        """Test ECDSA key generation."""
28        with tempfile.TemporaryDirectory() as tempdir_name:
29            temp_root = Path(tempdir_name)
30            private_key_filename = (temp_root / 'test_key')
31            public_key_filename = (temp_root / 'test_key.pub')
32
33            keys.gen_ecdsa_keypair(private_key_filename)
34
35            self.assertTrue(private_key_filename.exists())
36            self.assertTrue(public_key_filename.exists())
37            public_key = keys.import_ecdsa_public_key(
38                public_key_filename.read_bytes())
39            self.assertEqual(public_key.key.key_type,
40                             KeyType.ECDSA_SHA2_NISTP256)
41            self.assertEqual(public_key.key.scheme,
42                             KeyScheme.ECDSA_SHA2_NISTP256_SCHEME)
43
44
45class KeyIdTest(unittest.TestCase):
46    """Test Key ID generations """
47    def test_256bit_length(self):
48        key_id = keys.gen_key_id(
49            Key(key_type=KeyType.ECDSA_SHA2_NISTP256,
50                scheme=KeyScheme.ECDSA_SHA2_NISTP256_SCHEME,
51                keyval=b'public_key bytes'))
52        self.assertEqual(len(key_id), 32)
53
54    def test_different_keyval(self):
55        key1 = Key(key_type=KeyType.ECDSA_SHA2_NISTP256,
56                   scheme=KeyScheme.ECDSA_SHA2_NISTP256_SCHEME,
57                   keyval=b'key 1 bytes')
58        key2 = Key(key_type=KeyType.ECDSA_SHA2_NISTP256,
59                   scheme=KeyScheme.ECDSA_SHA2_NISTP256_SCHEME,
60                   keyval=b'key 2 bytes')
61
62        key1_id, key2_id = keys.gen_key_id(key1), keys.gen_key_id(key2)
63        self.assertNotEqual(key1_id, key2_id)
64
65    def test_different_key_type(self):
66        key1 = Key(key_type=KeyType.RSA,
67                   scheme=KeyScheme.ECDSA_SHA2_NISTP256_SCHEME,
68                   keyval=b'key bytes')
69        key2 = Key(key_type=KeyType.ECDSA_SHA2_NISTP256,
70                   scheme=KeyScheme.ECDSA_SHA2_NISTP256_SCHEME,
71                   keyval=b'key bytes')
72
73        key1_id, key2_id = keys.gen_key_id(key1), keys.gen_key_id(key2)
74        self.assertNotEqual(key1_id, key2_id)
75
76    def test_different_scheme(self):
77        key1 = Key(key_type=KeyType.ECDSA_SHA2_NISTP256,
78                   scheme=KeyScheme.ECDSA_SHA2_NISTP256_SCHEME,
79                   keyval=b'key bytes')
80        key2 = Key(key_type=KeyType.ECDSA_SHA2_NISTP256,
81                   scheme=KeyScheme.ED25519_SCHEME,
82                   keyval=b'key bytes')
83
84        key1_id, key2_id = keys.gen_key_id(key1), keys.gen_key_id(key2)
85        self.assertNotEqual(key1_id, key2_id)
86
87
88class KeyImportTest(unittest.TestCase):
89    """Test key importing"""
90    def setUp(self):
91        # Generated with:
92        # $> openssl ecparam -name prime256v1 -genkey -noout -out priv.pem
93        # $> openssl ec -in priv.pem -pubout -out pub.pem
94        # $> cat pub.pem
95        self.valid_nistp256_pem_bytes = (
96            b'-----BEGIN PUBLIC KEY-----\n'
97            b'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEKmK5mJwMV7eimA6MfFQL2q6KbZDr'
98            b'SnWwoeHvXB/aZBnwF422OLifuOuMjEUEHrNMmoekcua+ulHW41X3AgbvIw==\n'
99            b'-----END PUBLIC KEY-----\n')
100
101        # Generated with:
102        # $> openssl ecparam -name secp384r1 -genkey -noout -out priv.pem
103        # $> openssl ec -in priv.pem -pubout -out pub.pem
104        # $> cat pub.pem
105        self.valid_secp384r1_pem_bytes = (
106            b'-----BEGIN PUBLIC KEY-----\n'
107            b'MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE6xs+TEjb2/vIzs4AzSm2CSUWpJMCPAts'
108            b'e+gwvGwFrr2bXKHVLNCxr5/Va6rD0nDmB2NOiJwAXX1Z8CB5wqLLB31emCBFRb5i'
109            b'1LjZu8Bp3hrWOL7uvXer8uExnSfTKAoT\n'
110            b'-----END PUBLIC KEY-----\n')
111
112        # Replaces "MF" with "MM"
113        self.tampered_nistp256_pem_bytes = (
114            b'-----BEGIN PUBLIC KEY-----\n'
115            b'MMkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEKmK5mJwMV7eimA6MfFQL2q6KbZDr'
116            b'SnWwoeHvXB/aZBnwF422OLifuOuMjEUEHrNMmoekcua+ulHW41X3AgbvIw==\n'
117            b'-----END PUBLIC KEY-----\n')
118
119        self.rsa_2048_pem_bytes = (
120            b'-----BEGIN PUBLIC KEY-----\n'
121            b'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsu0+ol90Ri2BQ5TE9ife'
122            b'6aAmAUMzvAD2b3cnWaTBGXKpi7O9PKnfKbMVf/nJcWsyw2Bj8uStx3oV98U6owLO'
123            b'vsQwyFKVgLZdrXo2qv0L6ljBfCLJxnDhjesEV/oG04dwdN7qyPwAZtpVBCrC7Qi8'
124            b'2rkTnzTQi/1slUxRjliDDhgEdqP7dHbCr7QXNIAA0HFRiOqYmHGD7HNKl67iYmAX'
125            b'd/Jv8GfZL/ykZstP6Ow1/ByP1ZKvrZvg2iXjC686hZXiMJLqmp0sIqLire82oW+8'
126            b'XFc1uyr1j20m+NI5Siy0G3RbfPXrVKyXIgAYPW12+a/BXR9SrqYJYcWwuOGbHZCM'
127            b'pwIDAQAB\n'
128            b'-----END PUBLIC KEY-----\n')
129
130    def test_valid_nistp256_key(self):
131        keys.import_ecdsa_public_key(self.valid_nistp256_pem_bytes)
132
133    def test_tampered_nistp256_key(self):
134        with self.assertRaises(ValueError):
135            keys.import_ecdsa_public_key(self.tampered_nistp256_pem_bytes)
136
137    def test_non_ec_key(self):
138        with self.assertRaises(TypeError):
139            keys.import_ecdsa_public_key(self.rsa_2048_pem_bytes)
140
141    def test_wrong_curve(self):
142        with self.assertRaises(TypeError):
143            keys.import_ecdsa_public_key(self.valid_secp384r1_pem_bytes)
144
145
146class SignatureVerificationTest(unittest.TestCase):
147    """ECDSA signing and verification test."""
148    def setUp(self):
149        # Generated with:
150        # $> openssl ecparam -name prime256v1 -genkey -noout -out priv.pem
151        # $> openssl ec -in priv.pem -pubout -out pub.pem
152        # $> cat priv.pem pub.pem
153        self.private_key_pem = (
154            b'-----BEGIN EC PRIVATE KEY-----\n'
155            b'MHcCAQEEIH9u1n4qAT59f7KRRl/ZB0Y/BUfS4blba+LONlF4s3ltoAoGCCqGSM49'
156            b'AwEHoUQDQgAEgKf3kY9Hi3hxIyqm2EkfqQvJkCijjlJSmEAJ1oAp0Godi5x2af+m'
157            b'cSNuBjpRcC8iW8x1/gizqyWlfAVrZV0XdA==\n'
158            b'-----END EC PRIVATE KEY-----\n')
159        self.public_key_pem = (
160            b'-----BEGIN PUBLIC KEY-----\n'
161            b'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgKf3kY9Hi3hxIyqm2EkfqQvJkCij'
162            b'jlJSmEAJ1oAp0Godi5x2af+mcSNuBjpRcC8iW8x1/gizqyWlfAVrZV0XdA==\n'
163            b'-----END PUBLIC KEY-----\n')
164
165        self.message = b'Hello Pigweed!'
166        self.tampered_message = b'Hell0 Pigweed!'
167
168    def test_good_signature(self):
169        sig = keys.create_ecdsa_signature(self.message, self.private_key_pem)
170        self.assertTrue(
171            keys.verify_ecdsa_signature(
172                sig.sig, self.message,
173                keys.import_ecdsa_public_key(self.public_key_pem).key))
174
175    def test_tampered_message(self):
176        sig = keys.create_ecdsa_signature(self.message, self.private_key_pem)
177        self.assertFalse(
178            keys.verify_ecdsa_signature(
179                sig.sig, self.tampered_message,
180                keys.import_ecdsa_public_key(self.public_key_pem).key))
181
182    def test_tampered_signature(self):
183        sig = keys.create_ecdsa_signature(self.message, self.private_key_pem)
184        tampered_sig = bytearray(sig.sig)
185        tampered_sig[0] ^= 1
186        self.assertFalse(
187            keys.verify_ecdsa_signature(
188                tampered_sig, self.message,
189                keys.import_ecdsa_public_key(self.public_key_pem).key))
190
191
192if __name__ == '__main__':
193    unittest.main()
194