blob: 3fb099d81c0052f93e04c8e6932a2aa868fe6fa4 [file] [log] [blame]
#!/usr/bin/env python
import argparse
import json
import os
import subprocess
import sys
import typing
import cryptography.x509
import os.path
import glob
from binascii import hexlify, unhexlify
from enum import Enum
VID_NOT_PRESENT = 0xFFFF
PID_NOT_PRESENT = 0x0000
VALID_IN_PAST = "2020-06-28 14:23:43"
VALID_NOW = "2022-09-28 14:23:43"
VALID_IN_FUTURE = "2031-06-28 14:23:43"
class CertType(Enum):
PAA = 1
PAI = 2
DAC = 3
CERT_STRUCT_TEST_CASES = [
{
"description": 'Valid certificate version field set to v3(2)',
"test_folder": 'cert_version_v3',
"error_flag": 'no-error',
"is_success_case": 'true',
},
{
"description": 'Invalid certificate version field set to v2(1)',
"test_folder": 'cert_version_v2',
"error_flag": 'cert-version',
"is_success_case": 'false',
},
{
"description": 'Valid certificate signature algorithm ECDSA_WITH_SHA256',
"test_folder": 'sig_algo_ecdsa_with_sha256',
"error_flag": 'no-error',
"is_success_case": 'true',
},
{
"description": 'Invalid certificate signature algorithm ECDSA_WITH_SHA1',
"test_folder": 'sig_algo_ecdsa_with_sha1',
"error_flag": 'sig-algo',
"is_success_case": 'false',
},
{
"description": "VID in Subject field doesn't match VID in Issuer field",
"test_folder": 'subject_vid_mismatch',
"error_flag": 'subject-vid-mismatch',
"is_success_case": 'false',
},
{
"description": "PID in Subject field doesn't match PID in Issuer field",
"test_folder": 'subject_pid_mismatch',
"error_flag": 'subject-pid-mismatch',
"is_success_case": 'false',
},
{
"description": "Valid certificate public key curve prime256v1",
"test_folder": 'sig_curve_prime256v1',
"error_flag": 'no-error',
"is_success_case": 'true',
},
{
"description": "Invalid certificate public key curve secp256k1",
"test_folder": 'sig_curve_secp256k1',
"error_flag": 'sig-curve',
"is_success_case": 'false',
},
{
"description": "Certificate validity period starts in the past",
"test_folder": 'valid_in_past',
"error_flag": 'no-error',
"is_success_case": 'false',
},
{
"description": "Certificate validity period starts in the future",
"test_folder": 'valid_in_future',
"error_flag": 'no-error',
"is_success_case": 'false',
},
# TODO Cases:
# 'issuer-vid'
# 'issuer-pid'
# 'subject-vid'
# 'subject-pid'
{
"description": "Certificate doesn't include Basic Constraint extension",
"test_folder": 'ext_basic_missing',
"error_flag": 'ext-basic-missing',
"is_success_case": 'false',
},
{
"description": "Certificate Basic Constraint extension critical field is missing",
"test_folder": 'ext_basic_critical_missing',
"error_flag": 'ext-basic-critical-missing',
"is_success_case": 'false',
},
{
"description": "Certificate Basic Constraint extension critical field is set as 'non-critical'",
"test_folder": 'ext_basic_critical_wrong',
"error_flag": 'ext-basic-critical-wrong',
"is_success_case": 'false',
},
{
"description": "Certificate Basic Constraint extension CA field is missing",
"test_folder": 'ext_basic_ca_missing',
"error_flag": 'ext-basic-ca-missing',
"is_success_case": 'false',
},
{
"description": "Certificate Basic Constraint extension CA field is wrong (TRUE for DAC and FALSE for PAI)",
"test_folder": 'ext_basic_ca_wrong',
"error_flag": 'ext-basic-ca-wrong',
"is_success_case": 'false',
},
{
"description": "Certificate Basic Constraint extension PathLen field presence is wrong (present for DAC not present for PAI)",
"test_folder": 'ext_basic_pathlen_presence_wrong',
"error_flag": 'ext-basic-pathlen-presence-wrong',
"is_success_case": 'false',
},
{
"description": "Certificate Basic Constraint extension PathLen field set to 0",
"test_folder": 'ext_basic_pathlen0',
"error_flag": 'ext-basic-pathlen0',
"is_success_case": 'false',
},
{
"description": "Certificate Basic Constraint extension PathLen field set to 1",
"test_folder": 'ext_basic_pathlen1',
"error_flag": 'ext-basic-pathlen1',
"is_success_case": 'false',
},
{
"description": "Certificate Basic Constraint extension PathLen field set to 2",
"test_folder": 'ext_basic_pathlen2',
"error_flag": 'ext-basic-pathlen2',
"is_success_case": 'false',
},
{
"description": "Certificate doesn't include Key Usage extension",
"test_folder": 'ext_key_usage_missing',
"error_flag": 'ext-key-usage-missing',
"is_success_case": 'false',
},
{
"description": "Certificate Key Usage extension critical field is missing",
"test_folder": 'ext_key_usage_critical_missing',
"error_flag": 'ext-key-usage-critical-missing',
"is_success_case": 'false',
},
{
"description": "Certificate Key Usage extension critical field is set as 'non-critical'",
"test_folder": 'ext_key_usage_critical_wrong',
"error_flag": 'ext-key-usage-critical-wrong',
"is_success_case": 'false',
},
{
"description": "Certificate Key Usage extension diginalSignature field is wrong (not present for DAC and present for PAI, which is OK as optional)",
"test_folder": 'ext_key_usage_dig_sig_wrong',
"error_flag": 'ext-key-usage-dig-sig',
"is_success_case": 'false',
},
{
"description": "Certificate Key Usage extension keyCertSign field is wrong (present for DAC and not present for PAI)",
"test_folder": 'ext_key_usage_key_cert_sign_wrong',
"error_flag": 'ext-key-usage-key-cert-sign',
"is_success_case": 'false',
},
{
"description": "Certificate Key Usage extension cRLSign field is wrong (present for DAC and not present for PAI)",
"test_folder": 'ext_key_usage_crl_sign_wrong',
"error_flag": 'ext-key-usage-crl-sign',
"is_success_case": 'false',
},
{
"description": "Certificate doesn't include Authority Key ID (AKID) extension",
"test_folder": 'ext_akid_missing',
"error_flag": 'ext-akid-missing',
"is_success_case": 'false',
},
{
"description": "Certificate doesn't include Subject Key ID (SKID) extension",
"test_folder": 'ext_skid_missing',
"error_flag": 'ext-skid-missing',
"is_success_case": 'false',
},
{
"description": "Certificate includes optional Extended Key Usage extension",
"test_folder": 'ext_extended_key_usage_present',
"error_flag": 'ext-extended-key-usage',
"is_success_case": 'true',
},
{
"description": "Certificate includes optional Authority Information Access extension",
"test_folder": 'ext_authority_info_access_present',
"error_flag": 'ext-authority-info-access',
"is_success_case": 'true',
},
{
"description": "Certificate includes optional Subject Alternative Name extension",
"test_folder": 'ext_subject_alt_name_present',
"error_flag": 'ext-subject-alt-name',
"is_success_case": 'true',
},
]
VIDPID_FALLBACK_ENCODING_TEST_CASES = [
# Valid/Invalid encoding examples from the spec:
{
"description": 'Fallback VID and PID encoding example from spec: valid and recommended since easily human-readable',
"common_name": 'ACME Matter Devel DAC 5CDA9899 Mvid:FFF1 Mpid:00B1',
"test_folder": 'vidpid_fallback_encoding_01',
"is_success_case": 'true',
},
{
"description": 'Fallback VID and PID encoding example from spec: valid and recommended since easily human-readable',
"common_name": 'ACME Matter Devel DAC 5CDA9899 Mpid:00B1 Mvid:FFF1',
"test_folder": 'vidpid_fallback_encoding_02',
"is_success_case": 'true',
},
{
"description": 'Fallback VID and PID encoding example from spec: valid example showing that order or separators are not considered at all for the overall validity of the embedded fields',
"common_name": 'Mpid:00B1,ACME Matter Devel DAC 5CDA9899,Mvid:FFF1',
"test_folder": 'vidpid_fallback_encoding_03',
"is_success_case": 'true',
},
{
"description": 'Fallback VID and PID encoding example from spec: valid, but less readable',
"common_name": 'ACME Matter Devel DAC 5CDA9899 Mvid:FFF1Mpid:00B1',
"test_folder": 'vidpid_fallback_encoding_04',
"is_success_case": 'true',
},
{
"description": 'Fallback VID and PID encoding example from spec: valid, but highly discouraged, since embedding of substrings within other substrings may be confusing to human readers',
"common_name": 'Mvid:FFF1ACME Matter Devel DAC 5CDAMpid:00B19899',
"test_folder": 'vidpid_fallback_encoding_05',
"is_success_case": 'true',
},
{
"description": 'Fallback VID and PID encoding example from spec: invalid, since substring following Mvid: is not exactly 4 uppercase hexadecimal digits',
"common_name": 'ACME Matter Devel DAC 5CDA9899 Mvid:FF1 Mpid:00B1',
"test_folder": 'vidpid_fallback_encoding_06',
"is_success_case": 'false',
},
{
"description": 'Fallback VID and PID encoding example from spec: invalid, since substring following Mvid: is not exactly 4 uppercase hexadecimal digits',
"common_name": 'ACME Matter Devel DAC 5CDA9899 Mvid:fff1 Mpid:00B1',
"test_folder": 'vidpid_fallback_encoding_07',
"is_success_case": 'false',
},
{
"description": 'Fallback VID and PID encoding example from spec: invalid, since substring following Mpid: is not exactly 4 uppercase hexadecimal digits',
"common_name": 'ACME Matter Devel DAC 5CDA9899 Mvid:FFF1 Mpid:B1',
"test_folder": 'vidpid_fallback_encoding_08',
"is_success_case": 'false',
},
{
"description": 'Fallback VID and PID encoding example from spec: invalid, since substring following Mpid: is not exactly 4 uppercase hexadecimal digits',
"common_name": 'ACME Matter Devel DAC 5CDA9899 Mpid: Mvid:FFF1',
"test_folder": 'vidpid_fallback_encoding_09',
"is_success_case": 'false',
},
# More valid/invalid fallback encoding examples:
{
"description": 'Fallback VID and PID encoding example: invalid VID encoding',
"common_name": 'Mvid:FFF Mpid:00B10x',
"test_folder": 'vidpid_fallback_encoding_10',
"is_success_case": 'false',
},
{
"description": 'Fallback VID and PID encoding example: valid, but less human-readable',
"common_name": 'MpidMvid:FFF10 Matter Test Mpid:00B1',
"test_folder": 'vidpid_fallback_encoding_11',
"is_success_case": 'true',
},
{
"description": 'Fallback VID and PID encoding example: invalid, PID not present and VID not upper case',
"common_name": 'Matter Devel DAC Mpid:Mvid:Fff1',
"test_folder": 'vidpid_fallback_encoding_12',
"is_success_case": 'false',
},
{
"description": 'Fallback VID and PID encoding example: invalid VID prefix',
"common_name": 'Matter Devel DAC Mpid:00B1 MVID:FFF1',
"test_folder": 'vidpid_fallback_encoding_13',
"is_success_case": 'false',
},
{
"description": 'Fallback VID and PID encoding example: invalid PID and VID prefixes',
"common_name": 'Matter Devel DAC Mpid_00B1 Mvid_FFF1',
"test_folder": 'vidpid_fallback_encoding_14',
"is_success_case": 'false',
},
# Examples with both fallback encoding in the common name and using Matter specific OIDs
{
"description": 'Mix of Fallback and Matter OID encoding for VID and PID: valid, Matter OIDs are used and wrong values in the common-name are ignored',
"common_name": 'ACME Matter Devel DAC 5CDA9899 Mvid:FFF2 Mpid:00B2',
"vid": 0xFFF1,
"pid": 0x00B1,
"test_folder": 'vidpid_fallback_encoding_15',
"is_success_case": 'true',
},
{
"description": 'Mix of Fallback and Matter OID encoding for VID and PID: wrong, Correct values encoded in the common-name are ignored',
"common_name": 'ACME Matter Devel DAC 5CDA9899 Mvid:FFF1 Mpid:00B1',
"vid": 0xFFF2,
"pid": 0x00B2,
"test_folder": 'vidpid_fallback_encoding_16',
"is_success_case": 'false',
},
{
"description": 'Mix of Fallback and Matter OID encoding for VID and PID: invalid, PID is using Matter OID then VID must also use Matter OID',
"common_name": 'Mvid:FFF1',
"pid": 0x00B1,
"test_folder": 'vidpid_fallback_encoding_17',
"is_success_case": 'false',
},
]
CD_STRUCT_TEST_CASES = [
{
"description": 'Valid format_version field set to 1.',
"test_folder": 'format_version_1',
"error_flag": 'no-error',
"is_success_case": 'true',
},
{
"description": 'The format_version field is missing.',
"test_folder": 'format_version_missing',
"error_flag": 'format-version-missing',
"is_success_case": 'false',
},
{
"description": 'Invalid format_version field set to 2.',
"test_folder": 'format_version_2',
"error_flag": 'format-version-wrong',
"is_success_case": 'false',
},
{
"description": 'The vendor_id field is missing.',
"test_folder": 'vid_missing',
"error_flag": 'vid-missing',
"is_success_case": 'false',
},
{
"description": "The vendor_id field doesn't match the VID in DAC.",
"test_folder": 'vid_mismatch',
"error_flag": 'vid-mismatch',
"is_success_case": 'false',
},
{
"description": 'The product_id_array field is missing.',
"test_folder": 'pid_array_missing',
"error_flag": 'pid-array-missing',
"is_success_case": 'false',
},
{
"description": "The product_id_array field is empty TLV array.",
"test_folder": 'pid_array_count0',
"error_flag": 'pid-array-count0',
"is_success_case": 'false',
},
{
"description": "The product_id_array field has one PID value which matches the PID value in DAC.",
"test_folder": 'pid_array_count01_valid',
"error_flag": 'pid-array-count01-valid',
"is_success_case": 'true',
},
{
"description": "The product_id_array field has one PID value that doesn't match the PID value in DAC.",
"test_folder": 'pid_array_count01_mismatch',
"error_flag": 'pid-array-count01-mismatch',
"is_success_case": 'false',
},
{
"description": "The product_id_array field has 10 PID values one of which matches the PID value in DAC.",
"test_folder": 'pid_array_count10_valid',
"error_flag": 'pid-array-count10-valid',
"is_success_case": 'true',
},
{
"description": "The product_id_array field has 10 PID values none of which matches the PID value in DAC.",
"test_folder": 'pid_array_count10_mismatch',
"error_flag": 'pid-array-count10-mismatch',
"is_success_case": 'false',
},
{
"description": "The product_id_array field has 100 PID values one of which matches the PID value in DAC.",
"test_folder": 'pid_array_count100_valid',
"error_flag": 'pid-array-count100-valid',
"is_success_case": 'true',
},
{
"description": "The product_id_array field has 100 PID values none of which matches the PID value in DAC.",
"test_folder": 'pid_array_count100_mismatch',
"error_flag": 'pid-array-count100-mismatch',
"is_success_case": 'false',
},
{
"description": "The device_type_id field is missing.",
"test_folder": 'device_type_id_missing',
"error_flag": 'device-type-id-missing',
"is_success_case": 'false',
},
{
"description": "The device_type_id field doesn't match the device_type_id value in the DCL entries associated with the VID and PID.",
"test_folder": 'device_type_id_mismatch',
"error_flag": 'device-type-id-mismatch',
"is_success_case": 'false',
},
{
"description": "The certificate_id field is missing.",
"test_folder": 'cert_id_missing',
"error_flag": 'cert-id-missing',
"is_success_case": 'false',
},
{
"description": "The certificate_id field doesn't contain a globally unique serial number allocated by the CSA for this CD.",
"test_folder": 'cert_id_mismatch',
"error_flag": 'cert-id-mismatch',
"is_success_case": 'false',
},
{
"description": 'The certificate_id field has wrong length.',
"test_folder": 'cert_id_len_wrong',
"error_flag": 'cert-id-len-wrong',
"is_success_case": 'false',
},
{
"description": 'The security_level field is missing.',
"test_folder": 'security_level_missing',
"error_flag": 'security-level-missing',
"is_success_case": 'false',
},
{
"description": 'The security_level field is set to invalid value (different from 0).',
"test_folder": 'security_level_wrong',
"error_flag": 'security-level-wrong',
"is_success_case": 'false',
},
{
"description": 'The security_information field is missing.',
"test_folder": 'security_info_missing',
"error_flag": 'security-info-missing',
"is_success_case": 'false',
},
{
"description": 'The security_information field is set to invalid value (different from 0).',
"test_folder": 'security_info_wrong',
"error_flag": 'security-info-wrong',
"is_success_case": 'false',
},
{
"description": 'The version_number field is missing.',
"test_folder": 'version_number_missing',
"error_flag": 'version-number-missing',
"is_success_case": 'false',
},
{
"description": 'The version_number field matches the VID and PID used in a DeviceSoftwareVersionModel entry in the DCL matching the certification record associated with the product presenting this CD.',
"test_folder": 'version_number_match',
"error_flag": 'no-error',
"is_success_case": 'true',
},
{
"description": "The version_number field doesn't match the VID and PID used in a DeviceSoftwareVersionModel entry in the DCL matching the certification record associated with the product presenting this CD.",
"test_folder": 'version_number_wrong',
"error_flag": 'version-number-wrong',
"is_success_case": 'false',
},
{
"description": 'The certification_type field is missing.',
"test_folder": 'cert_type_missing',
"error_flag": 'cert-type-missing',
"is_success_case": 'false',
},
{
"description": 'The certification_type field is set to invalid value.',
"test_folder": 'cert_type_wrong',
"error_flag": 'cert-type-wrong',
"is_success_case": 'false',
},
{
"description": 'The dac_origin_vendor_id and dac_origin_product_id fields are not present.',
"test_folder": 'dac_origin_vid_pid_missing',
"error_flag": 'no-error',
"is_success_case": 'true',
},
{
"description": 'The dac_origin_vendor_id field is present and dac_origin_product_id fields is not present.',
"test_folder": 'dac_origin_vid_present_pid_missing',
"error_flag": 'dac-origin-vid-present',
"is_success_case": 'false',
},
{
"description": 'The dac_origin_vendor_id field is not present and dac_origin_product_id is present.',
"test_folder": 'dac_origin_vid_missing_pid_present',
"error_flag": 'dac-origin-pid-present',
"is_success_case": 'false',
},
{
"description": 'The dac_origin_vendor_id and dac_origin_product_id fields present and contain the VID and PID values that match the VID and PID found in the DAC Subject DN.',
"test_folder": 'dac_origin_vid_pid_present_match',
"error_flag": 'dac-origin-vid-pid-present',
"is_success_case": 'true',
},
{
"description": "The dac_origin_vendor_id and dac_origin_product_id fields present and the VID value doesn't match the VID found in the DAC Subject DN.",
"test_folder": 'dac_origin_vid_pid_present_vid_mismatch',
"error_flag": 'dac-origin-vid-mismatch',
"is_success_case": 'false',
},
{
"description": "The dac_origin_vendor_id and dac_origin_product_id fields present and the PID value doesn't match the PID found in the DAC Subject DN.",
"test_folder": 'dac_origin_vid_pid_present_pid_mismatch',
"error_flag": 'dac-origin-pid-mismatch',
"is_success_case": 'false',
},
{
"description": 'The optional authorized_paa_list field is not present.',
"test_folder": 'authorized_paa_list_missing',
"error_flag": 'no-error',
"is_success_case": 'true',
},
{
"description": 'The authorized_paa_list contains one valid PAA which is authorized to sign the PAI.',
"test_folder": 'authorized_paa_list_count0',
"error_flag": 'authorized-paa-list-count0',
"is_success_case": 'false',
},
{
"description": 'The authorized_paa_list contains one valid PAA which is authorized to sign the PAI.',
"test_folder": 'authorized_paa_list_count1_valid',
"error_flag": 'authorized-paa-list-count1-valid',
"is_success_case": 'true',
},
{
"description": 'The authorized_paa_list contains two PAAs one of which is valid PAA authorized to sign the PAI.',
"test_folder": 'authorized_paa_list_count2_valid',
"error_flag": 'authorized-paa-list-count2-valid',
"is_success_case": 'true',
},
{
"description": 'The authorized_paa_list contains three PAAs none of which is a valid PAA authorized to sign the PAI.',
"test_folder": 'authorized_paa_list_count3_invalid',
"error_flag": 'authorized-paa-list-count3-invalid',
"is_success_case": 'false',
},
{
"description": 'The authorized_paa_list contains ten PAAs one of which is valid PAA authorized to sign the PAI.',
"test_folder": 'authorized_paa_list_count10_valid',
"error_flag": 'authorized-paa-list-count10-valid',
"is_success_case": 'true',
},
{
"description": 'The authorized_paa_list contains ten PAAs none of which is a valid PAA authorized to sign the PAI.',
"test_folder": 'authorized_paa_list_count10_invalid',
"error_flag": 'authorized-paa-list-count10-invalid',
"is_success_case": 'false',
},
{
"description": 'Invalid Signer Info version set to v2.',
"test_folder": 'signer_info_v2',
"error_flag": 'signer-info-v2',
"is_success_case": 'false',
},
{
"description": 'Invalid Signer Info digest algorithm SHA1.',
"test_folder": 'signer_info_digest_algo_sha1',
"error_flag": 'signer-info-digest-algo',
"is_success_case": 'false',
},
{
"description": 'The subjectKeyIdentifier contains SKID of a well-known Zigbee Alliance certificate.',
"test_folder": 'signer_info_skid_valid',
"error_flag": 'no-error',
"is_success_case": 'true',
},
{
"description": 'The subjectKeyIdentifier contains invalid SKID of a certificate unknown by Zigbee Alliance.',
"test_folder": 'signer_info_skid_invalid',
"error_flag": 'signer-info-skid',
"is_success_case": 'false',
},
{
"description": 'Valid CMS version set to v3.',
"test_folder": 'cms_v3',
"error_flag": 'no-error',
"is_success_case": 'true',
},
{
"description": 'Invalid CMS version set to v2.',
"test_folder": 'cms_v2',
"error_flag": 'cms-v2',
"is_success_case": 'false',
},
{
"description": 'Valid CMS digest algorithm SHA256.',
"test_folder": 'cms_digest_algo_sha256',
"error_flag": 'no-error',
"is_success_case": 'true',
},
{
"description": 'Invalid CMS digest algorithm SHA1.',
"test_folder": 'cms_digest_algo_sha1',
"error_flag": 'cms-digest-algo',
"is_success_case": 'false',
},
{
"description": 'Valid CMS signature algorithm ECDSA_WITH_SHA256.',
"test_folder": 'cms_sig_algo_ecdsa_with_sha256',
"error_flag": 'no-error',
"is_success_case": 'true',
},
{
"description": 'Invalid CMS signature algorithm ECDSA_WITH_SHA1.',
"test_folder": 'cms_sig_algo_ecdsa_with_sha1',
"error_flag": 'cms-sig-algo',
"is_success_case": 'false',
},
{
"description": 'Valid CMS eContentType pkcs7-data.',
"test_folder": 'cms_econtent_type_pkcs7_data',
"error_flag": 'no-error',
"is_success_case": 'true',
},
{
"description": 'Invalid CMS eContentType is set to Microsoft Authenticode [MSAC] OID = { 1.3.6.1.4.1.311.2.1.4 }.',
"test_folder": 'cms_econtent_type_msac',
"error_flag": 'cms-econtent-type',
"is_success_case": 'false',
},
{
"description": 'Invalid CMS Signature.',
"test_folder": 'cms_signature',
"error_flag": 'cms-sig',
"is_success_case": 'false',
},
]
class Names:
def __init__(self, cert_type: CertType, paa_path, test_case_out_dir):
prefixes = {CertType.PAA: paa_path,
CertType.PAI: test_case_out_dir + '/pai-',
CertType.DAC: test_case_out_dir + '/dac-'}
prefix = prefixes[cert_type]
self.cert_pem = prefix + 'Cert.pem'
self.cert_der = prefix + 'Cert.der'
self.key_pem = prefix + 'Key.pem'
self.key_der = prefix + 'Key.der'
class DevCertBuilder:
def __init__(self, cert_type: CertType, error_type: str, paa_path: str, test_case_out_dir: str, chip_cert: str, vid: int, pid: int, custom_cn_attribute: str, valid_from: str):
self.vid = vid
self.pid = pid
self.cert_type = cert_type
self.error_type = error_type
self.chipcert = chip_cert
self.custom_cn_attribute = custom_cn_attribute
self.valid_from = valid_from
if not os.path.exists(self.chipcert):
raise Exception('Path not found: %s' % self.chipcert)
if not os.path.exists(test_case_out_dir):
os.mkdir(test_case_out_dir)
paa = Names(CertType.PAA, paa_path, test_case_out_dir)
pai = Names(CertType.PAI, paa_path, test_case_out_dir)
dac = Names(CertType.DAC, paa_path, test_case_out_dir)
if cert_type == CertType.PAI:
self.signer = paa
self.own = pai
if cert_type == CertType.DAC:
self.signer = pai
self.own = dac
def make_certs_and_keys(self) -> None:
"""Creates the PEM and DER certs and keyfiles"""
error_type_flag = ' -I -E' + self.error_type
subject_name = self.custom_cn_attribute
vid_flag = ' -V 0x{:X}'.format(self.vid)
pid_flag = ' -P 0x{:X}'.format(self.pid)
if (len(self.valid_from) == 0):
validity_flags = ' -l 4294967295 '
else:
validity_flags = ' -f "' + self.valid_from + '" -l 730 '
if self.cert_type == CertType.PAI:
if (len(subject_name) == 0):
subject_name = 'Matter Test PAI'
type_flag = '-t i'
elif self.cert_type == CertType.DAC:
if (len(subject_name) == 0):
subject_name = 'Matter Test DAC'
type_flag = '-t d'
else:
return
cmd = self.chipcert + ' gen-att-cert ' + type_flag + error_type_flag + ' -c "' + subject_name + '" -C ' + self.signer.cert_pem + ' -K ' + \
self.signer.key_pem + vid_flag + pid_flag + validity_flags + ' -o ' + self.own.cert_pem + ' -O ' + self.own.key_pem
subprocess.run(cmd, shell=True)
cmd = 'openssl x509 -inform pem -in ' + self.own.cert_pem + \
' -out ' + self.own.cert_der + ' -outform DER'
subprocess.run(cmd, shell=True)
cmd = 'openssl ec -inform pem -in ' + self.own.key_pem + \
' -out ' + self.own.key_der + ' -outform DER'
subprocess.run(cmd, shell=True)
def add_raw_ec_keypair_to_dict_from_der(der_key_filename: str, json_dict: dict):
with open(der_key_filename, 'rb') as infile:
key_data_der = infile.read()
key_der = cryptography.hazmat.primitives.serialization.load_der_private_key(key_data_der, None)
json_dict["dac_private_key"] = hexlify(key_der.private_numbers().private_value.to_bytes(32, byteorder='big')).decode('utf-8')
pk_x = key_der.public_key().public_numbers().x
pk_y = key_der.public_key().public_numbers().y
public_key_raw_bytes = bytearray([0x04])
public_key_raw_bytes.extend(bytearray(pk_x.to_bytes(32, byteorder='big')))
public_key_raw_bytes.extend(bytearray(pk_y.to_bytes(32, byteorder='big')))
json_dict["dac_public_key"] = hexlify(bytes(public_key_raw_bytes)).decode('utf-8')
def add_files_to_json_config(files_mapping: dict, json_dict: dict):
for output_key_name, filename in files_mapping.items():
with open(filename, "rb") as infile:
file_bytes = infile.read()
json_dict[output_key_name] = hexlify(file_bytes).decode('utf-8')
def generate_test_case_vector_json(test_case_out_dir: str, test_cert: str, test_case):
json_dict = {}
files_in_path = glob.glob(os.path.join(test_case_out_dir, "*"))
output_json_filename = test_case_out_dir + "/test_case_vector.json"
files_to_add = {
"dac_cert": "dac-Cert.der",
"pai_cert": "pai-Cert.der",
"firmware_information": "firmware-info.bin",
"certification_declaration": "cd.der",
}
# Add description fields to JSON Config
if "description" in test_case:
json_dict["description"] = test_cert.upper() + " Test Vector: " + test_case["description"]
if "is_success_case" in test_case:
# These test cases are expected to fail when error injected in DAC but expected to pass when error injected in PAI
if (test_cert == 'pai') and (test_case["test_folder"] in ['ext_basic_pathlen0', 'vidpid_fallback_encoding_08', 'vidpid_fallback_encoding_09', 'ext_key_usage_dig_sig_wrong']):
json_dict["is_success_case"] = "true"
else:
json_dict["is_success_case"] = test_case["is_success_case"]
# Out of all files we could add, find the ones that were present in test case, and embed them in hex
files_available = {os.path.basename(path) for path in files_in_path}
files_to_add = {key: os.path.join(test_case_out_dir, filename)
for key, filename in files_to_add.items() if filename in files_available}
add_files_to_json_config(files_to_add, json_dict)
# Embed the DAC key if present
if "dac-Key.der" in files_available:
der_key_filename = os.path.join(test_case_out_dir, "dac-Key.der")
add_raw_ec_keypair_to_dict_from_der(der_key_filename, json_dict)
with open(output_json_filename, "wt+") as outfile:
json.dump(json_dict, outfile, indent=2)
def main():
argparser = argparse.ArgumentParser()
argparser.add_argument('-o', '--out_dir', dest='outdir',
default='credentials/development/commissioner_dut',
help='output directory for all generated test vectors')
argparser.add_argument('-p', '--paa', dest='paapath',
default='credentials/test/attestation/Chip-Test-PAA-FFF1-', help='PAA to use')
argparser.add_argument('-d', '--cd', dest='cdpath',
default='credentials/test/certification-declaration/Chip-Test-CD-Signing-',
help='CD Signing Key/Cert to use')
argparser.add_argument('-c', '--chip-cert_dir', dest='chipcertdir',
default='out/debug/linux_x64_clang/', help='Directory where chip-cert tool is located')
args = argparser.parse_args()
if not os.path.exists(args.outdir):
os.mkdir(args.outdir)
chipcert = args.chipcertdir + 'chip-cert'
if not os.path.exists(chipcert):
raise Exception('Path not found: %s' % chipcert)
cd_cert = args.cdpath + 'Cert.pem'
cd_key = args.cdpath + 'Key.pem'
for test_cert in ['dac', 'pai']:
for test_case in CERT_STRUCT_TEST_CASES:
test_case_out_dir = args.outdir + '/struct_' + test_cert + '_' + test_case["test_folder"]
if test_case["test_folder"] == 'valid_in_past':
if test_cert == 'dac':
dac_valid_from = VALID_IN_PAST
pai_valid_from = VALID_NOW
else:
dac_valid_from = VALID_NOW
pai_valid_from = VALID_IN_PAST
elif test_case["test_folder"] == 'valid_in_future':
if test_cert == 'dac':
dac_valid_from = VALID_IN_FUTURE
pai_valid_from = VALID_NOW
else:
dac_valid_from = VALID_NOW
pai_valid_from = VALID_IN_FUTURE
else:
dac_valid_from = ''
pai_valid_from = ''
if test_cert == 'dac':
error_type_dac = test_case["error_flag"]
error_type_pai = 'no-error'
else:
if test_case["error_flag"] == 'ext-skid-missing':
error_type_dac = 'ext-akid-missing'
else:
error_type_dac = 'no-error'
error_type_pai = test_case["error_flag"]
vid = 0xFFF1
pid = 0x8000
# Generate PAI Cert/Key
builder = DevCertBuilder(CertType.PAI, error_type_pai, args.paapath, test_case_out_dir,
chipcert, vid, PID_NOT_PRESENT, '', pai_valid_from)
builder.make_certs_and_keys()
if test_cert == 'pai':
if test_case["error_flag"] == 'subject-vid-mismatch':
vid += 1
if test_case["error_flag"] == 'subject-pid-mismatch':
pid += 1
# Generate DAC Cert/Key
builder = DevCertBuilder(CertType.DAC, error_type_dac, args.paapath, test_case_out_dir,
chipcert, vid, pid, '', dac_valid_from)
builder.make_certs_and_keys()
# Generate Certification Declaration (CD)
vid_flag = ' -V 0x{:X}'.format(vid)
pid_flag = ' -p 0x{:X}'.format(pid)
cmd = chipcert + ' gen-cd -K ' + cd_key + ' -C ' + cd_cert + ' -O ' + test_case_out_dir + '/cd.der' + \
' -f 1 ' + vid_flag + pid_flag + ' -d 0x1234 -c "ZIG20141ZB330001-24" -l 0 -i 0 -n 9876 -t 0'
subprocess.run(cmd, shell=True)
# Generate Test Case Data Container in JSON Format
generate_test_case_vector_json(test_case_out_dir, test_cert, test_case)
for test_cert in ['dac', 'pai']:
for test_case in VIDPID_FALLBACK_ENCODING_TEST_CASES:
test_case_out_dir = args.outdir + '/struct_' + test_cert + '_' + test_case["test_folder"]
if test_cert == 'dac':
common_name_dac = test_case["common_name"]
common_name_pai = ''
if "vid" in test_case:
vid_dac = test_case["vid"]
else:
vid_dac = VID_NOT_PRESENT
if "pid" in test_case:
pid_dac = test_case["pid"]
else:
pid_dac = PID_NOT_PRESENT
vid_pai = 0xFFF1
pid_pai = 0x00B1
else:
common_name_dac = ''
common_name_pai = test_case["common_name"]
common_name_pai = common_name_pai.replace('DAC', 'PAI')
vid_dac = 0xFFF1
pid_dac = 0x00B1
if "vid" in test_case:
vid_pai = test_case["vid"]
else:
vid_pai = VID_NOT_PRESENT
if "pid" in test_case:
pid_pai = test_case["pid"]
else:
pid_pai = PID_NOT_PRESENT
# Generate PAI Cert/Key
builder = DevCertBuilder(CertType.PAI, 'no-error', args.paapath, test_case_out_dir,
chipcert, vid_pai, pid_pai, common_name_pai, '')
builder.make_certs_and_keys()
# Generate DAC Cert/Key
builder = DevCertBuilder(CertType.DAC, 'no-error', args.paapath, test_case_out_dir,
chipcert, vid_dac, pid_dac, common_name_dac, '')
builder.make_certs_and_keys()
# Generate Certification Declaration (CD)
cmd = chipcert + ' gen-cd -K ' + cd_key + ' -C ' + cd_cert + ' -O ' + test_case_out_dir + '/cd.der' + \
' -f 1 -V 0xFFF1 -p 0x00B1 -d 0x1234 -c "ZIG20141ZB330001-24" -l 0 -i 0 -n 9876 -t 0'
subprocess.run(cmd, shell=True)
# Generate Test Case Data Container in JSON Format
generate_test_case_vector_json(test_case_out_dir, test_cert, test_case)
for test_case in CD_STRUCT_TEST_CASES:
test_case_out_dir = args.outdir + '/struct_cd_' + test_case["test_folder"]
vid = 0xFFF1
pid = 0x8000
# Generate PAI Cert/Key
builder = DevCertBuilder(CertType.PAI, 'no-error', args.paapath, test_case_out_dir,
chipcert, vid, pid, '', '')
builder.make_certs_and_keys()
# Generate DAC Cert/Key
builder = DevCertBuilder(CertType.DAC, 'no-error', args.paapath, test_case_out_dir,
chipcert, vid, pid, '', '')
builder.make_certs_and_keys()
# Generate Certification Declaration (CD)
vid_flag = ' -V 0x{:X}'.format(vid)
pid_flag = ' -p 0x{:X}'.format(pid)
dac_origin_flag = ' '
if test_case["error_flag"] == 'dac-origin-vid-present' or test_case["error_flag"] == 'dac-origin-vid-pid-present':
dac_origin_flag += ' -o 0x{:X}'.format(vid)
if test_case["error_flag"] == 'dac-origin-pid-present' or test_case["error_flag"] == 'dac-origin-vid-pid-present':
dac_origin_flag += ' -r 0x{:X}'.format(pid)
if test_case["error_flag"] == 'authorized-paa-list-count0' or test_case["error_flag"] == 'authorized-paa-list-count1-valid' or test_case["error_flag"] == 'authorized-paa-list-count2-valid' or test_case["error_flag"] == 'authorized-paa-list-count3-invalid' or test_case["error_flag"] == 'authorized-paa-list-count10-valid' or test_case["error_flag"] == 'authorized-paa-list-count10-invalid':
authorized_paa_flag = ' -a ' + args.paapath + 'Cert.pem'
else:
authorized_paa_flag = ''
cmd = chipcert + ' gen-cd -I -E ' + test_case["error_flag"] + ' -K ' + cd_key + ' -C ' + cd_cert + ' -O ' + test_case_out_dir + '/cd.der' + \
' -f 1 ' + vid_flag + pid_flag + dac_origin_flag + authorized_paa_flag + ' -d 0x1234 -c "ZIG20141ZB330001-24" -l 0 -i 0 -n 9876 -t 0'
subprocess.run(cmd, shell=True)
# Generate Test Case Data Container in JSON Format
generate_test_case_vector_json(test_case_out_dir, 'cd', test_case)
# Test case: Generate {DAC, PAI, PAA} chain with random (invalid) PAA
test_case = {
"description": 'Use Invalid PAA (Not Registered in the DCL).',
"test_folder": 'invalid_paa',
"error_flag": 'no-error',
"is_success_case": 'false',
}
test_case_out_dir = args.outdir + '/' + test_case["test_folder"]
paapath = test_case_out_dir + '/paa-'
if not os.path.exists(test_case_out_dir):
os.mkdir(test_case_out_dir)
# Generate PAA Cert/Key
cmd = chipcert + ' gen-att-cert -t a -c "Invalid (Not Registered in the DCL) Matter PAA" -f "' + VALID_IN_PAST + \
'" -l 4294967295 -o ' + paapath + 'Cert.pem -O ' + paapath + 'Key.pem'
subprocess.run(cmd, shell=True)
vid = 0xFFF1
pid = 0x8000
# Generate PAI Cert/Key
builder = DevCertBuilder(CertType.PAI, test_case["error_flag"], paapath, test_case_out_dir,
chipcert, vid, PID_NOT_PRESENT, '', VALID_IN_PAST)
builder.make_certs_and_keys()
# Generate DAC Cert/Key
builder = DevCertBuilder(CertType.DAC, test_case["error_flag"], paapath, test_case_out_dir,
chipcert, vid, pid, '', VALID_IN_PAST)
builder.make_certs_and_keys()
# Generate Certification Declaration (CD)
vid_flag = ' -V 0x{:X}'.format(vid)
pid_flag = ' -p 0x{:X}'.format(pid)
cmd = chipcert + ' gen-cd -K ' + cd_key + ' -C ' + cd_cert + ' -O ' + test_case_out_dir + '/cd.der' + \
' -f 1 ' + vid_flag + pid_flag + ' -d 0x1234 -c "ZIG20141ZB330001-24" -l 0 -i 0 -n 9876 -t 0'
subprocess.run(cmd, shell=True)
# Generate Test Case Data Container in JSON Format
generate_test_case_vector_json(test_case_out_dir, 'paa', test_case)
if __name__ == '__main__':
sys.exit(main())