blob: 7ded05dc80ce8364a1b3841da9c6a98206fecb52 [file] [log] [blame]
Mate Toth-Pal51b61982022-03-17 14:19:30 +01001# -----------------------------------------------------------------------------
Mate Toth-Pal6e1d7742025-04-17 11:00:09 +02002# Copyright (c) 2019-2025, Arm Limited. All rights reserved.
Mate Toth-Pal51b61982022-03-17 14:19:30 +01003#
4# SPDX-License-Identifier: BSD-3-Clause
5#
6# -----------------------------------------------------------------------------
7
Mate Toth-Palb9057ff2022-04-29 16:03:21 +02008"""Unittests for iat-verifier using PSAIoTProfile1TokenVerifier"""
9
Mate Toth-Pal51b61982022-03-17 14:19:30 +010010import os
Mate Toth-Pal51b61982022-03-17 14:19:30 +010011import unittest
12
Thomas Fossatif4e1ca32024-08-16 16:01:31 +000013from pycose.algorithms import Es256, Es384
14
Mate Toth-Pal1cb66cd2022-04-26 15:40:07 +020015from iatverifier.psa_iot_profile1_token_verifier import PSAIoTProfile1TokenVerifier
Mate Toth-Pal5ebca512022-03-24 16:45:51 +010016from iatverifier.cca_token_verifier import CCATokenVerifier, CCAPlatformTokenVerifier
Mate Toth-Palb2508d52022-04-30 14:10:06 +020017from iatverifier.util import read_keyfile
Mate Toth-Pal5ebca512022-03-24 16:45:51 +010018from iatverifier.attest_token_verifier import AttestationClaim, VerifierConfiguration
19from iatverifier.attest_token_verifier import AttestationTokenVerifier
Mate Toth-Palb2508d52022-04-30 14:10:06 +020020from test_utils import create_and_read_iat, read_iat, create_token_tmp_file
Mate Toth-Pal51b61982022-03-17 14:19:30 +010021
22
23THIS_DIR = os.path.dirname(__file__)
24
25DATA_DIR = os.path.join(THIS_DIR, 'data')
26KEYFILE = os.path.join(DATA_DIR, 'key.pem')
Mate Toth-Pal5ebca512022-03-24 16:45:51 +010027KEYFILE_CCA_PLAT = os.path.join(DATA_DIR, 'cca_platform.pem')
28KEYFILE_CCA_REALM = os.path.join(DATA_DIR, 'cca_realm.pem')
Mate Toth-Palb21ae522022-09-01 12:02:21 +020029KEYFILE_CCA_REALM2= os.path.join(DATA_DIR, 'cca_realm2.pem')
Mate Toth-Pal51b61982022-03-17 14:19:30 +010030KEYFILE_ALT = os.path.join(DATA_DIR, 'key-alt.pem')
31
Mate Toth-Pal51b61982022-03-17 14:19:30 +010032class TestIatVerifier(unittest.TestCase):
Mate Toth-Palb9057ff2022-04-29 16:03:21 +020033 """A class used for testing iat-verifier.
34
35 This class uses the claim and token definitions for PSA Attestation Token"""
Mate Toth-Pal51b61982022-03-17 14:19:30 +010036
37 def setUp(self):
38 self.config = VerifierConfiguration()
39
40 def test_validate_signature(self):
Mate Toth-Palb9057ff2022-04-29 16:03:21 +020041 """Testing Signature validation"""
42 method=AttestationTokenVerifier.SIGN_METHOD_SIGN1
Thomas Fossatif4e1ca32024-08-16 16:01:31 +000043 cose_alg=Es256
Mate Toth-Pal51b61982022-03-17 14:19:30 +010044
Mate Toth-Palb9057ff2022-04-29 16:03:21 +020045 signing_key = read_keyfile(KEYFILE, method)
46 verifier_good_sig = PSAIoTProfile1TokenVerifier(
47 method=method,
48 cose_alg=cose_alg,
49 signing_key=signing_key,
50 configuration=self.config)
Mate Toth-Palb2508d52022-04-30 14:10:06 +020051 good_sig = create_token_tmp_file(DATA_DIR, 'valid-iat.yaml', verifier_good_sig)
Mate Toth-Pal51b61982022-03-17 14:19:30 +010052
Mate Toth-Palb9057ff2022-04-29 16:03:21 +020053 signing_key = read_keyfile(KEYFILE_ALT, method)
54 verifier_bad_sig = PSAIoTProfile1TokenVerifier(
55 method=method,
56 cose_alg=cose_alg,
57 signing_key=signing_key,
58 configuration=self.config)
Mate Toth-Palb2508d52022-04-30 14:10:06 +020059 bad_sig = create_token_tmp_file(DATA_DIR, 'valid-iat.yaml', verifier_bad_sig)
Mate Toth-Pal51b61982022-03-17 14:19:30 +010060
Mate Toth-Palb9057ff2022-04-29 16:03:21 +020061 #dump_file_binary(good_sig)
62
63 with open(good_sig, 'rb') as wfh:
Mate Toth-Palc7404e92022-07-15 11:11:13 +020064 token_item = verifier_good_sig.parse_token(
65 token=wfh.read(),
Mate Toth-Palc7404e92022-07-15 11:11:13 +020066 lower_case_key=False)
67 token_item.verify()
Mate Toth-Palb9057ff2022-04-29 16:03:21 +020068
69 with self.assertRaises(ValueError) as test_ctx:
70 with open(bad_sig, 'rb') as wfh:
Mate Toth-Palc7404e92022-07-15 11:11:13 +020071 token_item = verifier_good_sig.parse_token(
Mate Toth-Palb9057ff2022-04-29 16:03:21 +020072 token=wfh.read(),
Mate Toth-Palb9057ff2022-04-29 16:03:21 +020073 lower_case_key=False)
Mate Toth-Palc7404e92022-07-15 11:11:13 +020074 token_item.verify()
Mate Toth-Palb9057ff2022-04-29 16:03:21 +020075
76 self.assertIn('Bad signature', test_ctx.exception.args[0])
Mate Toth-Pal51b61982022-03-17 14:19:30 +010077
78 def test_validate_iat_structure(self):
Mate Toth-Palb9057ff2022-04-29 16:03:21 +020079 """Testing IAT structure validation"""
Mate Toth-Pal51b61982022-03-17 14:19:30 +010080 keep_going_conf = VerifierConfiguration(keep_going=True)
Mate Toth-Palb9057ff2022-04-29 16:03:21 +020081 method=AttestationTokenVerifier.SIGN_METHOD_SIGN1
Thomas Fossatif4e1ca32024-08-16 16:01:31 +000082 cose_alg=Es256
Mate Toth-Palb9057ff2022-04-29 16:03:21 +020083 signing_key = read_keyfile(KEYFILE, method)
Mate Toth-Pal5ebca512022-03-24 16:45:51 +010084 realm_token_key = read_keyfile(KEYFILE_CCA_REALM, method)
Mate Toth-Palb21ae522022-09-01 12:02:21 +020085 realm_token_key2 = read_keyfile(KEYFILE_CCA_REALM2, method)
Mate Toth-Pal5ebca512022-03-24 16:45:51 +010086 platform_token_key = read_keyfile(KEYFILE_CCA_PLAT, method)
Mate Toth-Pal51b61982022-03-17 14:19:30 +010087
Mate Toth-Palb9057ff2022-04-29 16:03:21 +020088 create_and_read_iat(
Mate Toth-Palb2508d52022-04-30 14:10:06 +020089 DATA_DIR,
Mate Toth-Palb9057ff2022-04-29 16:03:21 +020090 'valid-iat.yaml',
Mate Toth-Pal5ebca512022-03-24 16:45:51 +010091 PSAIoTProfile1TokenVerifier(
92 method=method,
93 cose_alg=cose_alg,
94 signing_key=signing_key,
95 configuration=self.config))
Mate Toth-Palb21ae522022-09-01 12:02:21 +020096
Mate Toth-Pal5ebca512022-03-24 16:45:51 +010097 create_and_read_iat(
98 DATA_DIR,
99 'valid-cca-token.yaml',
100 CCATokenVerifier(
101 realm_token_method=method,
Thomas Fossatif4e1ca32024-08-16 16:01:31 +0000102 realm_token_cose_alg=Es384,
Mate Toth-Pal5ebca512022-03-24 16:45:51 +0100103 realm_token_key=realm_token_key,
104 platform_token_method=method,
Thomas Fossatif4e1ca32024-08-16 16:01:31 +0000105 platform_token_cose_alg=Es384,
Mate Toth-Pal5ebca512022-03-24 16:45:51 +0100106 platform_token_key=platform_token_key,
107 configuration=self.config))
108
109 create_and_read_iat(
110 DATA_DIR,
111 'cca_platform_token.yaml',
112 CCAPlatformTokenVerifier(
113 method=method,
Thomas Fossatif4e1ca32024-08-16 16:01:31 +0000114 cose_alg=Es384,
Mate Toth-Pal5ebca512022-03-24 16:45:51 +0100115 signing_key=platform_token_key,
116 configuration=self.config,
Mate Toth-Pal6e1d7742025-04-17 11:00:09 +0200117 necessity=AttestationClaim.MANDATORY,
118 has_type_indicator=False))
119
120 create_and_read_iat(
121 DATA_DIR,
122 'cca_platform_token.yaml',
123 CCAPlatformTokenVerifier(
124 method=method,
125 cose_alg=Es384,
126 signing_key=platform_token_key,
127 configuration=self.config,
128 necessity=AttestationClaim.MANDATORY,
129 has_type_indicator=True))
Mate Toth-Pal51b61982022-03-17 14:19:30 +0100130
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200131 with self.assertRaises(ValueError) as test_ctx:
132 create_and_read_iat(
Mate Toth-Palb21ae522022-09-01 12:02:21 +0200133 DATA_DIR,
Mate Toth-Palc5dbad02022-09-01 22:17:26 +0200134 'cca-invalid-plat-challenge.yaml',
135 CCATokenVerifier(
136 realm_token_method=method,
Thomas Fossatif4e1ca32024-08-16 16:01:31 +0000137 realm_token_cose_alg=Es384,
Mate Toth-Palc5dbad02022-09-01 22:17:26 +0200138 realm_token_key=realm_token_key,
139 platform_token_method=method,
Thomas Fossatif4e1ca32024-08-16 16:01:31 +0000140 platform_token_cose_alg=Es384,
Mate Toth-Palc5dbad02022-09-01 22:17:26 +0200141 platform_token_key=platform_token_key,
142 configuration=self.config))
143 self.assertIn("Invalid CCA_PLATFORM_CHALLENGE byte at 16: 0x00 instead of 0xe4", test_ctx.exception.args[0])
144
145 with self.assertRaises(ValueError) as test_ctx:
146 create_and_read_iat(
147 DATA_DIR,
Mate Toth-Palb21ae522022-09-01 12:02:21 +0200148 'valid-cca-token.yaml',
149 CCATokenVerifier(
150 realm_token_method=method,
Thomas Fossatif4e1ca32024-08-16 16:01:31 +0000151 realm_token_cose_alg=Es384,
Mate Toth-Palb21ae522022-09-01 12:02:21 +0200152 realm_token_key=realm_token_key2,
153 platform_token_method=method,
Thomas Fossatif4e1ca32024-08-16 16:01:31 +0000154 platform_token_cose_alg=Es384,
Mate Toth-Palb21ae522022-09-01 12:02:21 +0200155 platform_token_key=platform_token_key,
156 configuration=self.config))
157 self.assertIn("Realm signature doesn't match Realm Public Key claim in Realm token", test_ctx.exception.args[0])
158
159 with self.assertRaises(ValueError) as test_ctx:
160 create_and_read_iat(
Mate Toth-Palb2508d52022-04-30 14:10:06 +0200161 DATA_DIR,
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200162 'invalid-profile-id.yaml',
163 PSAIoTProfile1TokenVerifier(method=method,
164 cose_alg=cose_alg,
165 signing_key=signing_key,
166 configuration=self.config))
167 self.assertIn('Invalid PROFILE_ID', test_ctx.exception.args[0])
Mate Toth-Pal51b61982022-03-17 14:19:30 +0100168
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200169 with self.assertRaises(ValueError) as test_ctx:
170 read_iat(
Mate Toth-Palb2508d52022-04-30 14:10:06 +0200171 DATA_DIR,
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200172 'malformed.cbor',
173 PSAIoTProfile1TokenVerifier(method=method,
174 cose_alg=cose_alg,
175 signing_key=signing_key,
176 configuration=self.config))
177 self.assertIn('Bad COSE', test_ctx.exception.args[0])
Mate Toth-Pal51b61982022-03-17 14:19:30 +0100178
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200179 with self.assertRaises(ValueError) as test_ctx:
180 create_and_read_iat(
Mate Toth-Palb2508d52022-04-30 14:10:06 +0200181 DATA_DIR,
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200182 'missing-claim.yaml',
183 PSAIoTProfile1TokenVerifier(method=method,
184 cose_alg=cose_alg,
185 signing_key=signing_key,
186 configuration=self.config))
187 self.assertIn('missing MANDATORY claim', test_ctx.exception.args[0])
Mate Toth-Pal51b61982022-03-17 14:19:30 +0100188
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200189 with self.assertRaises(ValueError) as test_ctx:
190 create_and_read_iat(
Mate Toth-Palb2508d52022-04-30 14:10:06 +0200191 DATA_DIR,
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200192 'submod-missing-claim.yaml',
193 PSAIoTProfile1TokenVerifier(method=method,
194 cose_alg=cose_alg,
195 signing_key=signing_key,
196 configuration=self.config))
197 self.assertIn('missing MANDATORY claim', test_ctx.exception.args[0])
Mate Toth-Pal51b61982022-03-17 14:19:30 +0100198
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200199 with self.assertRaises(ValueError) as test_ctx:
200 create_and_read_iat(
Mate Toth-Palb2508d52022-04-30 14:10:06 +0200201 DATA_DIR,
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200202 'missing-sw-comps.yaml',
203 PSAIoTProfile1TokenVerifier(method=method,
204 cose_alg=cose_alg,
205 signing_key=signing_key,
206 configuration=self.config))
Mate Toth-Pal51b61982022-03-17 14:19:30 +0100207 self.assertIn('NO_MEASUREMENTS claim is not present',
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200208 test_ctx.exception.args[0])
Mate Toth-Pal51b61982022-03-17 14:19:30 +0100209
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200210 with self.assertLogs() as test_ctx:
211 create_and_read_iat(
Mate Toth-Palb2508d52022-04-30 14:10:06 +0200212 DATA_DIR,
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200213 'missing-signer-id.yaml',
214 PSAIoTProfile1TokenVerifier(method=method,
215 cose_alg=cose_alg,
216 signing_key=signing_key,
217 configuration=self.config))
Mate Toth-Pald10a9142022-04-28 15:34:13 +0200218 self.assertIn('Missing RECOMMENDED claim "SIGNER_ID" from SW_COMPONENTS',
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200219 test_ctx.records[0].getMessage())
Mate Toth-Pal51b61982022-03-17 14:19:30 +0100220
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200221 with self.assertLogs() as test_ctx:
222 create_and_read_iat(
Mate Toth-Palb2508d52022-04-30 14:10:06 +0200223 DATA_DIR,
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200224 'invalid-type-length.yaml',
225 PSAIoTProfile1TokenVerifier(method=method,
226 cose_alg=cose_alg,
227 signing_key=signing_key,
228 configuration=keep_going_conf))
Mate Toth-Pal51b61982022-03-17 14:19:30 +0100229 self.assertIn("Invalid PROFILE_ID: must be a(n) <class 'str'>: found <class 'int'>",
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200230 test_ctx.records[0].getMessage())
Mate Toth-Pal51b61982022-03-17 14:19:30 +0100231 self.assertIn("Invalid SIGNER_ID: must be a(n) <class 'bytes'>: found <class 'str'>",
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200232 test_ctx.records[1].getMessage())
Mate Toth-Pal51b61982022-03-17 14:19:30 +0100233 self.assertIn("Invalid SIGNER_ID length: must be at least 32 bytes, found 12 bytes",
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200234 test_ctx.records[2].getMessage())
Mate Toth-Pal51b61982022-03-17 14:19:30 +0100235 self.assertIn("Invalid MEASUREMENT length: must be at least 32 bytes, found 28 bytes",
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200236 test_ctx.records[3].getMessage())
Mate Toth-Pal51b61982022-03-17 14:19:30 +0100237
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200238 with self.assertLogs() as test_ctx:
239 create_and_read_iat(
Mate Toth-Palb2508d52022-04-30 14:10:06 +0200240 DATA_DIR,
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200241 'invalid-hw-version.yaml',
Tamas Ban8ac8d172022-07-04 13:01:08 +0200242 PSAIoTProfile1TokenVerifier(
243 method=method,
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200244 cose_alg=cose_alg,
245 signing_key=signing_key,
246 configuration=keep_going_conf))
Tamas Ban8ac8d172022-07-04 13:01:08 +0200247 self.assertIn("Invalid HARDWARE_VERSION length; "
248 "must be 19 characters, found 10 characters",
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200249 test_ctx.records[0].getMessage())
Tamas Ban8ac8d172022-07-04 13:01:08 +0200250 self.assertIn("Invalid character at position 1",
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200251 test_ctx.records[1].getMessage())
Tamas Ban8ac8d172022-07-04 13:01:08 +0200252 self.assertIn("Invalid character - at position 4",
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200253 test_ctx.records[2].getMessage())
Tamas Ban8ac8d172022-07-04 13:01:08 +0200254 self.assertIn("Invalid character a at position 10",
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200255 test_ctx.records[3].getMessage())
Mate Toth-Pal51b61982022-03-17 14:19:30 +0100256
257 def test_binary_string_decoding(self):
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200258 """Test binary_string decoding"""
259 method=AttestationTokenVerifier.SIGN_METHOD_SIGN1
Thomas Fossatif4e1ca32024-08-16 16:01:31 +0000260 cose_alg=Es256
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200261 signing_key = read_keyfile(KEYFILE, method)
262 iat = create_and_read_iat(
Mate Toth-Palb2508d52022-04-30 14:10:06 +0200263 DATA_DIR,
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200264 'valid-iat.yaml',
265 PSAIoTProfile1TokenVerifier(method=method,
266 cose_alg=cose_alg,
267 signing_key=signing_key,
Mate Toth-Palc7404e92022-07-15 11:11:13 +0200268 configuration=self.config)).get_token_map()
Mate Toth-Pal916a3de2024-05-03 09:34:41 +0200269 self.assertEqual(iat['SECURITY_LIFECYCLE'], 'sl_secured_3000')
Mate Toth-Pal51b61982022-03-17 14:19:30 +0100270
271 def test_security_lifecycle_decoding(self):
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200272 """Test security lifecycle decoding"""
273 method=AttestationTokenVerifier.SIGN_METHOD_SIGN1
Thomas Fossatif4e1ca32024-08-16 16:01:31 +0000274 cose_alg=Es256
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200275 signing_key = read_keyfile(KEYFILE, method)
276 iat = create_and_read_iat(
Mate Toth-Palb2508d52022-04-30 14:10:06 +0200277 DATA_DIR,
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200278 'valid-iat.yaml',
279 PSAIoTProfile1TokenVerifier(method=method,
280 cose_alg=cose_alg,
281 signing_key=signing_key,
Mate Toth-Palc7404e92022-07-15 11:11:13 +0200282 configuration=self.config)).get_token_map()
Mate Toth-Pal916a3de2024-05-03 09:34:41 +0200283 self.assertEqual(iat['SECURITY_LIFECYCLE'], 'sl_secured_3000')
Thomas Fossati5ebf4832024-08-26 09:30:05 +0000284
285 def test_profiles(self):
286 """
287 Test that both legacy and new profiles are handled correctly.
288 In particular, ensure that the different RAK encodings are accommodated,
289 and that use of legacy profiles triggers a warning.
290 """
291 method=AttestationTokenVerifier.SIGN_METHOD_SIGN1
292 realm_token_key = read_keyfile(KEYFILE_CCA_REALM, method)
293 platform_token_key = read_keyfile(KEYFILE_CCA_PLAT, method)
294
295 # change directory here to make !inc work
296 os.chdir(DATA_DIR)
297
298 create_and_read_iat(
299 '.',
300 'cca_example_token.yaml',
301 CCATokenVerifier(
302 realm_token_method=method,
303 realm_token_cose_alg=Es384,
304 realm_token_key=realm_token_key,
305 platform_token_method=method,
306 platform_token_cose_alg=Es384,
307 platform_token_key=platform_token_key,
308 configuration=self.config
309 )
310 )
311
312 with self.assertLogs() as test_ctx:
313 create_and_read_iat(
314 '.',
315 'cca_example_token_legacy.yaml',
316 CCATokenVerifier(
317 realm_token_method=method,
318 realm_token_cose_alg=Es384,
319 realm_token_key=realm_token_key,
320 platform_token_method=method,
321 platform_token_cose_alg=Es384,
322 platform_token_key=platform_token_key,
323 configuration=self.config
324 )
325 )
326 self.assertIn('legacy profile http://arm.com/CCA-SSD/1.0.0 is deprecated',
327 test_ctx.records[0].getMessage())