blob: 48604b1f08404076cfb2a677f99cb000cab41044 [file] [log] [blame]
# -----------------------------------------------------------------------------
# Copyright (c) 2019-2024, Arm Limited. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
# -----------------------------------------------------------------------------
"""Unittests for iat-verifier using PSAIoTProfile1TokenVerifier"""
import os
import unittest
from pycose.algorithms import Es256, Es384
from iatverifier.psa_iot_profile1_token_verifier import PSAIoTProfile1TokenVerifier
from iatverifier.cca_token_verifier import CCATokenVerifier, CCAPlatformTokenVerifier
from iatverifier.util import read_keyfile
from iatverifier.attest_token_verifier import AttestationClaim, VerifierConfiguration
from iatverifier.attest_token_verifier import AttestationTokenVerifier
from test_utils import create_and_read_iat, read_iat, create_token_tmp_file
THIS_DIR = os.path.dirname(__file__)
DATA_DIR = os.path.join(THIS_DIR, 'data')
KEYFILE = os.path.join(DATA_DIR, 'key.pem')
KEYFILE_CCA_PLAT = os.path.join(DATA_DIR, 'cca_platform.pem')
KEYFILE_CCA_REALM = os.path.join(DATA_DIR, 'cca_realm.pem')
KEYFILE_CCA_REALM2= os.path.join(DATA_DIR, 'cca_realm2.pem')
KEYFILE_ALT = os.path.join(DATA_DIR, 'key-alt.pem')
class TestIatVerifier(unittest.TestCase):
"""A class used for testing iat-verifier.
This class uses the claim and token definitions for PSA Attestation Token"""
def setUp(self):
self.config = VerifierConfiguration()
def test_validate_signature(self):
"""Testing Signature validation"""
method=AttestationTokenVerifier.SIGN_METHOD_SIGN1
cose_alg=Es256
signing_key = read_keyfile(KEYFILE, method)
verifier_good_sig = PSAIoTProfile1TokenVerifier(
method=method,
cose_alg=cose_alg,
signing_key=signing_key,
configuration=self.config)
good_sig = create_token_tmp_file(DATA_DIR, 'valid-iat.yaml', verifier_good_sig)
signing_key = read_keyfile(KEYFILE_ALT, method)
verifier_bad_sig = PSAIoTProfile1TokenVerifier(
method=method,
cose_alg=cose_alg,
signing_key=signing_key,
configuration=self.config)
bad_sig = create_token_tmp_file(DATA_DIR, 'valid-iat.yaml', verifier_bad_sig)
#dump_file_binary(good_sig)
with open(good_sig, 'rb') as wfh:
token_item = verifier_good_sig.parse_token(
token=wfh.read(),
lower_case_key=False)
token_item.verify()
with self.assertRaises(ValueError) as test_ctx:
with open(bad_sig, 'rb') as wfh:
token_item = verifier_good_sig.parse_token(
token=wfh.read(),
lower_case_key=False)
token_item.verify()
self.assertIn('Bad signature', test_ctx.exception.args[0])
def test_validate_iat_structure(self):
"""Testing IAT structure validation"""
keep_going_conf = VerifierConfiguration(keep_going=True)
method=AttestationTokenVerifier.SIGN_METHOD_SIGN1
cose_alg=Es256
signing_key = read_keyfile(KEYFILE, method)
realm_token_key = read_keyfile(KEYFILE_CCA_REALM, method)
realm_token_key2 = read_keyfile(KEYFILE_CCA_REALM2, method)
platform_token_key = read_keyfile(KEYFILE_CCA_PLAT, method)
create_and_read_iat(
DATA_DIR,
'valid-iat.yaml',
PSAIoTProfile1TokenVerifier(
method=method,
cose_alg=cose_alg,
signing_key=signing_key,
configuration=self.config))
create_and_read_iat(
DATA_DIR,
'valid-cca-token.yaml',
CCATokenVerifier(
realm_token_method=method,
realm_token_cose_alg=Es384,
realm_token_key=realm_token_key,
platform_token_method=method,
platform_token_cose_alg=Es384,
platform_token_key=platform_token_key,
configuration=self.config))
create_and_read_iat(
DATA_DIR,
'cca_platform_token.yaml',
CCAPlatformTokenVerifier(
method=method,
cose_alg=Es384,
signing_key=platform_token_key,
configuration=self.config,
necessity=AttestationClaim.MANDATORY))
with self.assertRaises(ValueError) as test_ctx:
create_and_read_iat(
DATA_DIR,
'cca-invalid-plat-challenge.yaml',
CCATokenVerifier(
realm_token_method=method,
realm_token_cose_alg=Es384,
realm_token_key=realm_token_key,
platform_token_method=method,
platform_token_cose_alg=Es384,
platform_token_key=platform_token_key,
configuration=self.config))
self.assertIn("Invalid CCA_PLATFORM_CHALLENGE byte at 16: 0x00 instead of 0xe4", test_ctx.exception.args[0])
with self.assertRaises(ValueError) as test_ctx:
create_and_read_iat(
DATA_DIR,
'valid-cca-token.yaml',
CCATokenVerifier(
realm_token_method=method,
realm_token_cose_alg=Es384,
realm_token_key=realm_token_key2,
platform_token_method=method,
platform_token_cose_alg=Es384,
platform_token_key=platform_token_key,
configuration=self.config))
self.assertIn("Realm signature doesn't match Realm Public Key claim in Realm token", test_ctx.exception.args[0])
with self.assertRaises(ValueError) as test_ctx:
create_and_read_iat(
DATA_DIR,
'invalid-profile-id.yaml',
PSAIoTProfile1TokenVerifier(method=method,
cose_alg=cose_alg,
signing_key=signing_key,
configuration=self.config))
self.assertIn('Invalid PROFILE_ID', test_ctx.exception.args[0])
with self.assertRaises(ValueError) as test_ctx:
read_iat(
DATA_DIR,
'malformed.cbor',
PSAIoTProfile1TokenVerifier(method=method,
cose_alg=cose_alg,
signing_key=signing_key,
configuration=self.config))
self.assertIn('Bad COSE', test_ctx.exception.args[0])
with self.assertRaises(ValueError) as test_ctx:
create_and_read_iat(
DATA_DIR,
'missing-claim.yaml',
PSAIoTProfile1TokenVerifier(method=method,
cose_alg=cose_alg,
signing_key=signing_key,
configuration=self.config))
self.assertIn('missing MANDATORY claim', test_ctx.exception.args[0])
with self.assertRaises(ValueError) as test_ctx:
create_and_read_iat(
DATA_DIR,
'submod-missing-claim.yaml',
PSAIoTProfile1TokenVerifier(method=method,
cose_alg=cose_alg,
signing_key=signing_key,
configuration=self.config))
self.assertIn('missing MANDATORY claim', test_ctx.exception.args[0])
with self.assertRaises(ValueError) as test_ctx:
create_and_read_iat(
DATA_DIR,
'missing-sw-comps.yaml',
PSAIoTProfile1TokenVerifier(method=method,
cose_alg=cose_alg,
signing_key=signing_key,
configuration=self.config))
self.assertIn('NO_MEASUREMENTS claim is not present',
test_ctx.exception.args[0])
with self.assertLogs() as test_ctx:
create_and_read_iat(
DATA_DIR,
'missing-signer-id.yaml',
PSAIoTProfile1TokenVerifier(method=method,
cose_alg=cose_alg,
signing_key=signing_key,
configuration=self.config))
self.assertIn('Missing RECOMMENDED claim "SIGNER_ID" from SW_COMPONENTS',
test_ctx.records[0].getMessage())
with self.assertLogs() as test_ctx:
create_and_read_iat(
DATA_DIR,
'invalid-type-length.yaml',
PSAIoTProfile1TokenVerifier(method=method,
cose_alg=cose_alg,
signing_key=signing_key,
configuration=keep_going_conf))
self.assertIn("Invalid PROFILE_ID: must be a(n) <class 'str'>: found <class 'int'>",
test_ctx.records[0].getMessage())
self.assertIn("Invalid SIGNER_ID: must be a(n) <class 'bytes'>: found <class 'str'>",
test_ctx.records[1].getMessage())
self.assertIn("Invalid SIGNER_ID length: must be at least 32 bytes, found 12 bytes",
test_ctx.records[2].getMessage())
self.assertIn("Invalid MEASUREMENT length: must be at least 32 bytes, found 28 bytes",
test_ctx.records[3].getMessage())
with self.assertLogs() as test_ctx:
create_and_read_iat(
DATA_DIR,
'invalid-hw-version.yaml',
PSAIoTProfile1TokenVerifier(
method=method,
cose_alg=cose_alg,
signing_key=signing_key,
configuration=keep_going_conf))
self.assertIn("Invalid HARDWARE_VERSION length; "
"must be 19 characters, found 10 characters",
test_ctx.records[0].getMessage())
self.assertIn("Invalid character at position 1",
test_ctx.records[1].getMessage())
self.assertIn("Invalid character - at position 4",
test_ctx.records[2].getMessage())
self.assertIn("Invalid character a at position 10",
test_ctx.records[3].getMessage())
def test_binary_string_decoding(self):
"""Test binary_string decoding"""
method=AttestationTokenVerifier.SIGN_METHOD_SIGN1
cose_alg=Es256
signing_key = read_keyfile(KEYFILE, method)
iat = create_and_read_iat(
DATA_DIR,
'valid-iat.yaml',
PSAIoTProfile1TokenVerifier(method=method,
cose_alg=cose_alg,
signing_key=signing_key,
configuration=self.config)).get_token_map()
self.assertEqual(iat['SECURITY_LIFECYCLE'], 'sl_secured_3000')
def test_security_lifecycle_decoding(self):
"""Test security lifecycle decoding"""
method=AttestationTokenVerifier.SIGN_METHOD_SIGN1
cose_alg=Es256
signing_key = read_keyfile(KEYFILE, method)
iat = create_and_read_iat(
DATA_DIR,
'valid-iat.yaml',
PSAIoTProfile1TokenVerifier(method=method,
cose_alg=cose_alg,
signing_key=signing_key,
configuration=self.config)).get_token_map()
self.assertEqual(iat['SECURITY_LIFECYCLE'], 'sl_secured_3000')