/* * * Copyright (c) 2021-2022 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @file * This file implements the command handler for the 'chip-cert' tool * that generates a CHIP Certification Declaration. * */ #include "chip-cert.h" #include #include namespace { using namespace chip; using namespace chip::ASN1; using namespace chip::ArgParser; using namespace chip::Credentials; using namespace chip::Crypto; using namespace chip::TLV; #define CMD_NAME "chip-cert gen-cd" bool HandleOption(const char * progName, OptionSet * optSet, int id, const char * name, const char * arg); // clang-format off OptionDef gCmdOptionDefs[] = { { "key", kArgumentRequired, 'K' }, { "cert", kArgumentRequired, 'C' }, { "out", kArgumentRequired, 'O' }, { "format-version", kArgumentRequired, 'f' }, { "vendor-id", kArgumentRequired, 'V' }, { "product-id", kArgumentRequired, 'p' }, { "device-type-id", kArgumentRequired, 'd' }, { "certificate-id", kArgumentRequired, 'c' }, { "security-level", kArgumentRequired, 'l' }, { "security-info", kArgumentRequired, 'i' }, { "version-number", kArgumentRequired, 'n' }, { "certification-type", kArgumentRequired, 't' }, { "dac-origin-vendor-id", kArgumentRequired, 'o' }, { "dac-origin-product-id", kArgumentRequired, 'r' }, { "authorized-paa-cert", kArgumentRequired, 'a' }, #if CHIP_CONFIG_INTERNAL_FLAG_GENERATE_DA_TEST_CASES { "ignore-error", kNoArgument, 'I' }, { "error-type", kArgumentRequired, 'E' }, #endif { } }; const char * const gCmdOptionHelp = " -K, --key \n" "\n" " File or string containing private key to be used to sign the Certification Declaration.\n" "\n" " -C, --cert \n" "\n" " File or string containing certificate associated with the private key that is used\n" " to sign the Certification Declaration. The Subject Key Identifier in the\n" " certificate will be included in the signed Certification Declaration message.\n" "\n" " -O, --out \n" "\n" " File to contain the signed Certification Declaration message.\n" " If specified '-' then output is written to stdout.\n" "\n" " -f, --format-version \n" "\n" " Format Version.\n" "\n" " -V, --vendor-id \n" "\n" " Vendor Id (VID) in hex.\n" "\n" " -p, --product-id \n" "\n" " Product Id (PID) in hex. Maximum 100 PID values can be specified.\n" " Each PID value should have it's own -p or --product-id option selector.\n" "\n" " -d, --device-type-id \n" "\n" " Device Type Id in hex.\n" "\n" " -c, --certificate-id \n" "\n" " Certificate Id encoded as UTF8 string.\n" "\n" " -l, --security-level \n" "\n" " Security Level in hex.\n" "\n" " -i, --security-info \n" "\n" " Security Information in hex.\n" "\n" " -n, --version-number \n" "\n" " Version Number in hex.\n" "\n" " -t, --certification-type \n" "\n" " Certification Type. Valid values are:\n" " 0 - Development and Test (default)\n" " 1 - Provisional\n" " 2 - Official\n" "\n" " -o, --dac-origin-vendor-id \n" "\n" " DAC Origin Vendor Id in hex.\n" "\n" " -r, --dac-origin-product-id \n" "\n" " DAC Origin Product Id in hex.\n" "\n" " -a, --authorized-paa-cert \n" "\n" " File or string containing PAA certificate authorized to sign PAI which signs the DAC\n" " for a product carrying this CD. This field is optional and if present, only specified\n" " PAAs will be authorized to sign device's PAI for the lifetime of the generated CD.\n" " Maximum 10 authorized PAA certificates can be specified.\n" " Each PAA should have its own -a (--authorized-paa-cert) option selector.\n" " The certificate can be in DER or PEM Form.\n" " Note that only the Subject Key Identifier (SKID) value will be extracted\n" " from the PAA certificate and put into CD Structure.\n" "\n" #if CHIP_CONFIG_INTERNAL_FLAG_GENERATE_DA_TEST_CASES " -I, --ignore-error\n" "\n" " Ignore some input parameters error.\n" " WARNING: This option makes it possible to circumvent Certification Declaration\n" " structure/parameter requirement. This is required for negative testing of the attestation flow.\n" " Because of this it SHOULD NEVER BE ENABLED IN PRODUCTION BUILDS.\n" "\n" " -E, --error-type \n" "\n" " When specified injects specific error into the structure of generated Certification Declaration.\n" " Note that 'ignore-error' option MUST be specified for this error injection to take effect.\n" " Supported error types that can be injected are:\n" " no-error - No error to inject.\n" " format-version-missing - The CD TLV structure won't have format version field.\n" " format-version-wrong - Format version will be set to 2 instead of required 1.\n" " vid-missing - The CD TLV structure won't have vedor_id field.\n" " vid-mismatch - The vendor_id field will have value that doesn't match the VID in DAC.\n" " pid-array-missing - The CD TLV structure won't have product_id_array field.\n" " pid-array-count0 - The product_id_array will be empty.\n" " pid-array-count01-valid - The product_id_array field will have one valid PID value.\n" " pid-array-count01-mismatch - The product_id_array field will have one PID value that doesn't match PID in DAC.\n" " pid-array-count10-valid - The product_id_array field will have 10 PID values one of which is valid matches PID in DAC.\n" " pid-array-count10-mismatch - The product_id_array field will have 10 PID values none of which matches the PID in DAC.\n" " pid-array-count100-valid - The product_id_array field will have 100 PID values one of which is valid matches PID in DAC.\n" " pid-array-count100-mismatch - The product_id_array field will have 100 PID values none of which matches the PID in DAC.\n" " device-type-id-missing - The CD TLV structure won't have device_type_id field.\n" " device-type-id-mismatch - device_type_id field won't match the value in the DCL entries associated with the VID and PID.\n" " cert-id-missing - The CD TLV structure won't have certificate_id field.\n" " cert-id-mismatch - The certificate_id field will contain value NOT allocated by the CSA.\n" " cert-id-len-wrong - The certificate_id field will be truncated to have invalid length.\n" " security-level-missing - The CD TLV structure won't have security_level field.\n" " security-level-wrong - The security_level field will be set to invalid value (different from 0).\n" " security-info-missing - The CD TLV structure won't have security_information field.\n" " security-info-wrong - The security_information field will be set to invalid value (different from 0).\n" " version-number-missing - The CD TLV structure won't have version_number field.\n" " version-number-wrong - The version_number field will contain value NOT assigned by the CSA.\n" " cert-type-missing - The CD TLV structure won't have certification_type field.\n" " cert-type-wrong - The certification_type field will contain invalue value.\n" " dac-origin-vid-present - The CD TLV structure will include optional dac_origin_vid field.\n" " dac-origin-pid-present - The CD TLV structure will include optional dac_origin_pid field.\n" " dac-origin-vid-pid-present - The CD TLV structure will include optional dac_origin_vid and dac_origin_pid fields.\n" " dac-origin-vid-mismatch - The optional dac_origin_vid field will be present and won't match the VID in DAC.\n" " dac-origin-pid-mismatch - The optional dac_origin_pid field will be present and won't match the PID in DAC.\n" " authorized-paa-list-count0 - The authorized_paa_list will be empty TLV list.\n" " authorized-paa-list-count1-valid - The authorized_paa_list will have one valid value.\n" " authorized-paa-list-count2-valid - The authorized_paa_list will have two elements one of which is valid.\n" " authorized-paa-list-count3-invalid - The authorized_paa_list will have three elements none of which is valid.\n" " authorized-paa-list-count10-valid - The authorized_paa_list will have ten elements one of which is valid.\n" " authorized-paa-list-count10-invalid- The authorized_paa_list will have ten elements none of which is valid.\n" " signer-info-v2 - Signer Info version will be set to v2 instead of required v3.\n" " signer-info-digest-algo - Use Signer Info SHA1 digest algorithm instead of required SHA256.\n" " signer-info-skid - Inject error into SKID of a Signer Info signing certificate.\n" " cms-v2 - CMS version will be set to v2 instead of required v3.\n" " cms-digest-algo - Use SHA1 digest algorithm instead of required SHA256.\n" " cms-sig-algo - Use ecdsa-with-SHA1 signature algorithm instead of required ecdsa-with-SHA256.\n" " required secp256r1 (aka prime256v1).\n" " cms-econtent-type - CMS eContentType is set to Microsoft Authenticode [MSAC] ( OID = { 1.3.6.1.4.1.311.2.1.4 } )\n" " instead of required pkcs7_data.\n" " cms-sig - Inject error into CMS signature.\n" "\n" #endif ; OptionSet gCmdOptions = { HandleOption, gCmdOptionDefs, "COMMAND OPTIONS", gCmdOptionHelp }; HelpOptions gHelpOptions( CMD_NAME, "Usage: " CMD_NAME " [ ]\n", CHIP_VERSION_STRING "\n" COPYRIGHT_STRING, "Generate CD CMS Signed Message" ); OptionSet *gCmdOptionSets[] = { &gCmdOptions, &gHelpOptions, nullptr }; // clang-format on /** Certification Declaration Error and Configuration Flags * * By default all methods (if none of the class setters were used) return valid * certification declaration configuration parameter as described in the spec. * These parameters can be modified to inject errors into cd structure. */ class CDStructConfig { public: void EnableErrorTestCase() { mEnabled = true; } void SetFormatVersionMissing() { mFlags.Set(CDConfigFlags::kFormatVersionMissing); } void SetFormatVersionWrong() { mFlags.Set(CDConfigFlags::kFormatVersionWrong); } void SetVIDMissing() { mFlags.Set(CDConfigFlags::kVIDMissing); } void SetVIDWrong() { mFlags.Set(CDConfigFlags::kVIDWrong); } void SetPIDArrayMissing() { mFlags.Set(CDConfigFlags::kPIDArrayMissing); } void SetPIDArrayWrong() { mFlags.Set(CDConfigFlags::kPIDArrayWrong); } void SetPIDArrayCount(uint8_t pidArrayCount) { mPIDArrayCount = pidArrayCount; } void SetDeviceTypeIdMissing() { mFlags.Set(CDConfigFlags::kDeviceTypeIdMissing); } void SetDeviceTypeIdWrong() { mFlags.Set(CDConfigFlags::kDeviceTypeIdWrong); } void SetCertIdMissing() { mFlags.Set(CDConfigFlags::kCertIdMissing); } void SetCertIdWrong() { mFlags.Set(CDConfigFlags::kCertIdWrong); } void SetCertIdLenWrong() { mFlags.Set(CDConfigFlags::kCertIdLenWrong); } void SetSecurityLevelMissing() { mFlags.Set(CDConfigFlags::kSecurityLevelMissing); } void SetSecurityLevelWrong() { mFlags.Set(CDConfigFlags::kSecurityLevelWrong); } void SetSecurityInfoMissing() { mFlags.Set(CDConfigFlags::kSecurityInfoMissing); } void SetSecurityInfoWrong() { mFlags.Set(CDConfigFlags::kSecurityInfoWrong); } void SetVersionNumberMissing() { mFlags.Set(CDConfigFlags::kVersionNumberMissing); } void SetVersionNumberWrong() { mFlags.Set(CDConfigFlags::kVersionNumberWrong); } void SetCertTypeMissing() { mFlags.Set(CDConfigFlags::kCertTypeMissing); } void SetCertTypeWrong() { mFlags.Set(CDConfigFlags::kCertTypeWrong); } void SetDACOriginVIDWrong() { mFlags.Set(CDConfigFlags::kDACOriginVID); } void SetDACOriginPIDWrong() { mFlags.Set(CDConfigFlags::kDACOriginPID); } void SetDACOriginVIDPresent() { mFlags.Set(CDConfigFlags::kDACOriginVIDPresent); } void SetDACOriginPIDPresent() { mFlags.Set(CDConfigFlags::kDACOriginPIDPresent); } void SetAuthPAAListPresent() { mFlags.Set(CDConfigFlags::kAuthPAAListPresent); } void SetAuthPAAListWrong() { mFlags.Set(CDConfigFlags::kAuthPAAListWrong); } void SetAuthPAAListCount(uint8_t authPAAListCount) { mAuthPAAListCount = authPAAListCount; } void SetSignerInfoVersionWrong() { mFlags.Set(CDConfigFlags::kSignerInfoVersion); } void SetSignerInfoDigestAlgoWrong() { mFlags.Set(CDConfigFlags::kSignerInfoDigestAlgo); } void SetSignerInfoSKIDWrong() { mFlags.Set(CDConfigFlags::kSignerInfoSKID); } void SetCMSVersionWrong() { mFlags.Set(CDConfigFlags::kCMSVersion); } void SetCMSDigestAlgoWrong() { mFlags.Set(CDConfigFlags::kCMSDigestAlgo); } void SetCMSSigAlgoWrong() { mFlags.Set(CDConfigFlags::kCMSSigAlgo); } void SetCMSEContentTypeWrong() { mFlags.Set(CDConfigFlags::kCMSEContentType); } void SetCMSSignatureWrong() { mFlags.Set(CDConfigFlags::kCMSSignature); } bool IsErrorTestCaseEnabled() { return mEnabled; } bool IsFormatVersionPresent() { return (!mEnabled || !mFlags.Has(CDConfigFlags::kFormatVersionMissing)); } uint8_t GetFormatVersion() { return (mEnabled && mFlags.Has(CDConfigFlags::kFormatVersionWrong)) ? 2 : 1; } bool IsVIDPresent() { return (!mEnabled || !mFlags.Has(CDConfigFlags::kVIDMissing)); } bool IsVIDCorrect() { return (!mEnabled || !mFlags.Has(CDConfigFlags::kVIDWrong)); } bool IsPIDArrayPresent() { return (!mEnabled || !mFlags.Has(CDConfigFlags::kPIDArrayMissing)); } bool IsPIDArrayCorrect() { return (!mEnabled || !mFlags.Has(CDConfigFlags::kPIDArrayWrong)); } uint8_t GetPIDArrayCount() const { return mPIDArrayCount; } bool IsDeviceTypeIdPresent() { return (!mEnabled || !mFlags.Has(CDConfigFlags::kDeviceTypeIdMissing)); } bool IsDeviceTypeIdCorrect() { return (!mEnabled || !mFlags.Has(CDConfigFlags::kDeviceTypeIdWrong)); } bool IsCertIdPresent() { return (!mEnabled || !mFlags.Has(CDConfigFlags::kCertIdMissing)); } bool IsCertIdCorrect() { return (!mEnabled || !mFlags.Has(CDConfigFlags::kCertIdWrong)); } bool IsCertIdLenCorrect() { return (!mEnabled || !mFlags.Has(CDConfigFlags::kCertIdLenWrong)); } bool IsSecurityLevelPresent() { return (!mEnabled || !mFlags.Has(CDConfigFlags::kSecurityLevelMissing)); } bool IsSecurityLevelCorrect() { return (!mEnabled || !mFlags.Has(CDConfigFlags::kSecurityLevelWrong)); } bool IsSecurityInfoPresent() { return (!mEnabled || !mFlags.Has(CDConfigFlags::kSecurityInfoMissing)); } bool IsSecurityInfoCorrect() { return (!mEnabled || !mFlags.Has(CDConfigFlags::kSecurityInfoWrong)); } bool IsVersionNumberPresent() { return (!mEnabled || !mFlags.Has(CDConfigFlags::kVersionNumberMissing)); } bool IsVersionNumberCorrect() { return (!mEnabled || !mFlags.Has(CDConfigFlags::kVersionNumberWrong)); } bool IsCertTypePresent() { return (!mEnabled || !mFlags.Has(CDConfigFlags::kCertTypeMissing)); } bool IsCertTypeCorrect() { return (!mEnabled || !mFlags.Has(CDConfigFlags::kCertTypeWrong)); } bool IsDACOriginVIDCorrect() { return (!mEnabled || !mFlags.Has(CDConfigFlags::kDACOriginVID)); } bool IsDACOriginPIDCorrect() { return (!mEnabled || !mFlags.Has(CDConfigFlags::kDACOriginPID)); } bool IsDACOriginVIDPresent() { return (mEnabled && mFlags.Has(CDConfigFlags::kDACOriginVIDPresent)); } bool IsDACOriginPIDPresent() { return (mEnabled && mFlags.Has(CDConfigFlags::kDACOriginPIDPresent)); } bool IsAuthPAAListPresent() { return (mFlags.Has(CDConfigFlags::kAuthPAAListPresent)); } bool IsAuthPAAListCorrect() { return (!mEnabled || !mFlags.Has(CDConfigFlags::kAuthPAAListWrong)); } uint8_t GetAuthPAAListCount() const { return mAuthPAAListCount; } bool IsSignerInfoVersionCorrect() { return (!mEnabled || !mFlags.Has(CDConfigFlags::kSignerInfoVersion)); } bool IsSignerInfoDigestAlgoCorrect() { return (!mEnabled || !mFlags.Has(CDConfigFlags::kSignerInfoDigestAlgo)); } bool IsSignerInfoSKIDCorrect() { return (!mEnabled || !mFlags.Has(CDConfigFlags::kSignerInfoSKID)); } bool IsCMSVersionCorrect() { return (!mEnabled || !mFlags.Has(CDConfigFlags::kCMSVersion)); } bool IsCMSDigestAlgoCorrect() { return (!mEnabled || !mFlags.Has(CDConfigFlags::kCMSDigestAlgo)); } bool IsCMSSigAlgoCorrect() { return (!mEnabled || !mFlags.Has(CDConfigFlags::kCMSSigAlgo)); } bool IsCMSEContentTypeCorrect() { return (!mEnabled || !mFlags.Has(CDConfigFlags::kCMSEContentType)); } bool IsCMSSignatureCorrect() { return (!mEnabled || !mFlags.Has(CDConfigFlags::kCMSSignature)); } private: enum class CDConfigFlags : uint64_t { kFormatVersionMissing = 0x0000000000000001, kFormatVersionWrong = 0x0000000000000002, kVIDMissing = 0x0000000000000004, kVIDWrong = 0x0000000000000008, kPIDArrayMissing = 0x0000000000000010, kPIDArrayWrong = 0x0000000000000020, kDeviceTypeIdMissing = 0x0000000000000040, kDeviceTypeIdWrong = 0x0000000000000080, kCertIdMissing = 0x0000000000000100, kCertIdWrong = 0x0000000000000200, kCertIdLenWrong = 0x0000000000000400, kSecurityLevelMissing = 0x0000000000000800, kSecurityLevelWrong = 0x0000000000001000, kSecurityInfoMissing = 0x0000000000002000, kSecurityInfoWrong = 0x0000000000004000, kVersionNumberMissing = 0x0000000000008000, kVersionNumberWrong = 0x0000000000010000, kCertTypeMissing = 0x0000000000020000, kCertTypeWrong = 0x0000000000040000, kDACOriginVID = 0x0000000000080000, kDACOriginPID = 0x0000000000100000, kDACOriginVIDPresent = 0x0000000000200000, kDACOriginPIDPresent = 0x0000000000400000, kAuthPAAListPresent = 0x0000000000800000, kAuthPAAListWrong = 0x0000000001000000, kSignerInfoVersion = 0x0000000002000000, kSignerInfoDigestAlgo = 0x0000000004000000, kSignerInfoSKID = 0x0000000008000000, kCMSVersion = 0x0000000010000000, kCMSDigestAlgo = 0x0000000020000000, kCMSSigAlgo = 0x0000000040000000, kCMSEContentType = 0x0000000080000000, kCMSSignature = 0x0000000100000000, }; bool mEnabled = false; chip::BitFlags mFlags; uint8_t mPIDArrayCount = 1; uint8_t mAuthPAAListCount = 1; }; CertificationElements gCertElements; const char * gCertFileNameOrStr = nullptr; const char * gKeyFileNameOrStr = nullptr; const char * gSignedCDFileName = nullptr; CDStructConfig gCDConfig; bool ExtractSKIDFromX509Cert(X509 * cert, ByteSpan & skid) { const ASN1_OCTET_STRING * skidString = X509_get0_subject_key_id(cert); VerifyOrReturnError(skidString != nullptr, false); VerifyOrReturnError(skidString->length == kKeyIdentifierLength, false); VerifyOrReturnError(CanCastTo(skidString->length), false); skid = ByteSpan(skidString->data, static_cast(skidString->length)); return true; }; bool HandleOption(const char * progName, OptionSet * optSet, int id, const char * name, const char * arg) { switch (id) { case 'C': gCertFileNameOrStr = arg; break; case 'K': gKeyFileNameOrStr = arg; break; case 'O': gSignedCDFileName = arg; break; case 'f': if (!ParseInt(arg, gCertElements.FormatVersion, 16)) { PrintArgError("%s: Invalid value specified for Format Version: %s\n", progName, arg); return false; } break; case 'V': if (!ParseInt(arg, gCertElements.VendorId, 16) || gCertElements.VendorId == 0) { PrintArgError("%s: Invalid value specified for Vendor Id: %s\n", progName, arg); return false; } break; case 'p': if (gCertElements.ProductIdsCount == ArraySize(gCertElements.ProductIds)) { PrintArgError("%s: Too many Product Ids are specified: %s\n", progName, arg); return false; } if (!ParseInt(arg, gCertElements.ProductIds[gCertElements.ProductIdsCount], 16) || gCertElements.ProductIds[gCertElements.ProductIdsCount] == 0) { PrintArgError("%s: Invalid value specified for Product Id: %s\n", progName, arg); return false; } gCertElements.ProductIdsCount++; break; case 'd': if (!ParseInt(arg, gCertElements.DeviceTypeId, 16)) { PrintArgError("%s: Invalid value specified for Device Type Id: %s\n", progName, arg); return false; } break; case 'c': if (strlen(arg) != kCertificateIdLength) { PrintArgError("%s: Invalid value specified for Certificate Id: %s\n", progName, arg); return false; } memcpy(gCertElements.CertificateId, arg, strlen(arg)); gCertElements.CertificateId[kCertificateIdLength] = '\0'; break; case 'l': if (!ParseInt(arg, gCertElements.SecurityLevel, 16)) { PrintArgError("%s: Invalid value specified for Security Level: %s\n", progName, arg); return false; } break; case 'i': if (!ParseInt(arg, gCertElements.SecurityInformation, 16)) { PrintArgError("%s: Invalid value specified for Security Information: %s\n", progName, arg); return false; } break; case 'n': if (!ParseInt(arg, gCertElements.VersionNumber, 16)) { PrintArgError("%s: Invalid value specified for Version Number: %s\n", progName, arg); return false; } break; case 't': if (!ParseInt(arg, gCertElements.CertificationType) || gCertElements.CertificationType > 2) { PrintArgError("%s: Invalid value specified for Certification Type: %s\n", progName, arg); return false; } break; case 'o': if (!ParseInt(arg, gCertElements.DACOriginVendorId, 16) || gCertElements.DACOriginVendorId == 0) { PrintArgError("%s: Invalid value specified for DAC Origin Vendor Id: %s\n", progName, arg); return false; } gCertElements.DACOriginVIDandPIDPresent = true; break; case 'r': if (!ParseInt(arg, gCertElements.DACOriginProductId, 16) || gCertElements.DACOriginProductId == 0) { PrintArgError("%s: Invalid value specified for DAC Origin Product Id: %s\n", progName, arg); return false; } gCertElements.DACOriginVIDandPIDPresent = true; break; case 'a': if (gCertElements.AuthorizedPAAListCount >= ArraySize(gCertElements.AuthorizedPAAList)) { PrintArgError("%s: Too many Authorized PAA Certificates are specified: %s\n", progName, arg); return false; } { const char * fileNameOrStr = arg; std::unique_ptr cert(nullptr, &X509_free); VerifyOrReturnError(ReadCert(fileNameOrStr, cert), false); ByteSpan skid; VerifyOrReturnError(ExtractSKIDFromX509Cert(cert.get(), skid), false); memcpy(gCertElements.AuthorizedPAAList[gCertElements.AuthorizedPAAListCount++], skid.data(), skid.size()); } break; #if CHIP_CONFIG_INTERNAL_FLAG_GENERATE_DA_TEST_CASES case 'I': gCDConfig.EnableErrorTestCase(); break; case 'E': if (strcmp(arg, "format-version-missing") == 0) { gCDConfig.SetFormatVersionMissing(); } else if (strcmp(arg, "format-version-wrong") == 0) { gCDConfig.SetFormatVersionWrong(); } else if (strcmp(arg, "vid-missing") == 0) { gCDConfig.SetVIDMissing(); } else if (strcmp(arg, "vid-mismatch") == 0) { gCDConfig.SetVIDWrong(); } else if (strcmp(arg, "pid-array-missing") == 0) { gCDConfig.SetPIDArrayMissing(); } else if (strcmp(arg, "pid-array-count0") == 0) { gCDConfig.SetPIDArrayCount(0); } else if (strcmp(arg, "pid-array-count01-valid") == 0) { gCDConfig.SetPIDArrayCount(1); } else if (strcmp(arg, "pid-array-count01-mismatch") == 0) { gCDConfig.SetPIDArrayCount(1); gCDConfig.SetPIDArrayWrong(); } else if (strcmp(arg, "pid-array-count10-valid") == 0) { gCDConfig.SetPIDArrayCount(10); } else if (strcmp(arg, "pid-array-count10-mismatch") == 0) { gCDConfig.SetPIDArrayCount(10); gCDConfig.SetPIDArrayWrong(); } else if (strcmp(arg, "pid-array-count100-valid") == 0) { gCDConfig.SetPIDArrayCount(100); } else if (strcmp(arg, "pid-array-count100-mismatch") == 0) { gCDConfig.SetPIDArrayCount(100); gCDConfig.SetPIDArrayWrong(); } else if (strcmp(arg, "device-type-id-missing") == 0) { gCDConfig.SetDeviceTypeIdMissing(); } else if (strcmp(arg, "device-type-id-mismatch") == 0) { gCDConfig.SetDeviceTypeIdWrong(); } else if (strcmp(arg, "cert-id-missing") == 0) { gCDConfig.SetCertIdMissing(); } else if (strcmp(arg, "cert-id-mismatch") == 0) { gCDConfig.SetCertIdWrong(); } else if (strcmp(arg, "cert-id-len-wrong") == 0) { gCDConfig.SetCertIdLenWrong(); } else if (strcmp(arg, "security-level-missing") == 0) { gCDConfig.SetSecurityLevelMissing(); } else if (strcmp(arg, "security-level-wrong") == 0) { gCDConfig.SetSecurityLevelWrong(); } else if (strcmp(arg, "security-info-missing") == 0) { gCDConfig.SetSecurityInfoMissing(); } else if (strcmp(arg, "security-info-wrong") == 0) { gCDConfig.SetSecurityInfoWrong(); } else if (strcmp(arg, "version-number-missing") == 0) { gCDConfig.SetVersionNumberMissing(); } else if (strcmp(arg, "version-number-wrong") == 0) { gCDConfig.SetVersionNumberWrong(); } else if (strcmp(arg, "cert-type-missing") == 0) { gCDConfig.SetCertTypeMissing(); } else if (strcmp(arg, "cert-type-wrong") == 0) { gCDConfig.SetCertTypeWrong(); } else if (strcmp(arg, "dac-origin-vid-present") == 0) { gCDConfig.SetDACOriginVIDPresent(); } else if (strcmp(arg, "dac-origin-pid-present") == 0) { gCDConfig.SetDACOriginPIDPresent(); } else if (strcmp(arg, "dac-origin-vid-pid-present") == 0) { gCDConfig.SetDACOriginVIDPresent(); gCDConfig.SetDACOriginPIDPresent(); } else if (strcmp(arg, "dac-origin-vid-mismatch") == 0) { gCDConfig.SetDACOriginVIDPresent(); gCDConfig.SetDACOriginPIDPresent(); gCDConfig.SetDACOriginVIDWrong(); } else if (strcmp(arg, "dac-origin-pid-mismatch") == 0) { gCDConfig.SetDACOriginVIDPresent(); gCDConfig.SetDACOriginPIDPresent(); gCDConfig.SetDACOriginPIDWrong(); } else if (strcmp(arg, "different-origin") == 0) { gCDConfig.SetDACOriginVIDPresent(); gCDConfig.SetDACOriginPIDPresent(); } else if (strcmp(arg, "authorized-paa-list-count0") == 0) { gCDConfig.SetAuthPAAListPresent(); gCDConfig.SetAuthPAAListCount(0); } else if (strcmp(arg, "authorized-paa-list-count1-valid") == 0) { gCDConfig.SetAuthPAAListPresent(); gCDConfig.SetAuthPAAListCount(1); } else if (strcmp(arg, "authorized-paa-list-count2-valid") == 0) { gCDConfig.SetAuthPAAListPresent(); gCDConfig.SetAuthPAAListCount(2); } else if (strcmp(arg, "authorized-paa-list-count3-invalid") == 0) { gCDConfig.SetAuthPAAListCount(3); gCDConfig.SetAuthPAAListPresent(); gCDConfig.SetAuthPAAListWrong(); } else if (strcmp(arg, "authorized-paa-list-count10-valid") == 0) { gCDConfig.SetAuthPAAListPresent(); gCDConfig.SetAuthPAAListCount(10); } else if (strcmp(arg, "authorized-paa-list-count10-invalid") == 0) { gCDConfig.SetAuthPAAListCount(10); gCDConfig.SetAuthPAAListPresent(); gCDConfig.SetAuthPAAListWrong(); } else if (strcmp(arg, "signer-info-v2") == 0) { gCDConfig.SetSignerInfoVersionWrong(); } else if (strcmp(arg, "signer-info-digest-algo") == 0) { gCDConfig.SetSignerInfoDigestAlgoWrong(); } else if (strcmp(arg, "signer-info-skid") == 0) { gCDConfig.SetSignerInfoSKIDWrong(); } else if (strcmp(arg, "cms-v2") == 0) { gCDConfig.SetCMSVersionWrong(); } else if (strcmp(arg, "cms-digest-algo") == 0) { gCDConfig.SetCMSDigestAlgoWrong(); } else if (strcmp(arg, "cms-sig-algo") == 0) { gCDConfig.SetCMSSigAlgoWrong(); } else if (strcmp(arg, "cms-econtent-type") == 0) { gCDConfig.SetCMSEContentTypeWrong(); } else if (strcmp(arg, "cms-sig") == 0) { gCDConfig.SetCMSSignatureWrong(); } else if (strcmp(arg, "no-error") != 0) { PrintArgError("%s: Invalid value specified for the error type: %s\n", progName, arg); return false; } break; #endif default: PrintArgError("%s: Unhandled option: %s\n", progName, name); return false; } return true; } static constexpr uint8_t sOID_ContentType_PKCS7Data[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x01 }; static constexpr uint8_t sOID_ContentType_MSAC[] = { 0x06, 0x0A, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, 0x01, 0x04 }; static constexpr uint8_t sOID_ContentType_PKCS7SignedData[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02 }; static constexpr uint8_t sOID_DigestAlgo_SHA256[] = { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01 }; static constexpr uint8_t sOID_DigestAlgo_SHA1[] = { 0x2B, 0x0E, 0x03, 0x02, 0x1A }; static constexpr uint8_t sOID_SigAlgo_ECDSAWithSHA1[] = { 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x01 }; /** Certification Declaration Element TLV Tags */ enum { kTag_FormatVersion = 0, /**< [ unsigned int ] Format version. */ kTag_VendorId = 1, /**< [ unsigned int ] Vedor identifier. */ kTag_ProductIdArray = 2, /**< [ array ] Product identifiers (each is unsigned int). */ kTag_DeviceTypeId = 3, /**< [ unsigned int ] Device Type identifier. */ kTag_CertificateId = 4, /**< [ UTF-8 string, length 19 ] Certificate identifier. */ kTag_SecurityLevel = 5, /**< [ unsigned int ] Security level. */ kTag_SecurityInformation = 6, /**< [ unsigned int ] Security information. */ kTag_VersionNumber = 7, /**< [ unsigned int ] Version number. */ kTag_CertificationType = 8, /**< [ unsigned int ] Certification Type. */ kTag_DACOriginVendorId = 9, /**< [ unsigned int, optional ] DAC origin vendor identifier. */ kTag_DACOriginProductId = 10, /**< [ unsigned int, optional ] DAC origin product identifier. */ kTag_AuthorizedPAAList = 11, /**< [ array, optional ] Authorized PAA List. */ }; CHIP_ERROR EncodeCertificationElements_Ignore_Error(const CertificationElements & certElements, MutableByteSpan & encodedCertElements, CDStructConfig & cdConfig) { TLVWriter writer; TLVType outerContainer1, outerContainer2; writer.Init(encodedCertElements); ReturnErrorOnFailure(writer.StartContainer(AnonymousTag(), kTLVType_Structure, outerContainer1)); if (cdConfig.IsFormatVersionPresent()) { ReturnErrorOnFailure(writer.Put(ContextTag(kTag_FormatVersion), cdConfig.GetFormatVersion())); } if (cdConfig.IsVIDPresent()) { if (cdConfig.IsVIDCorrect()) { ReturnErrorOnFailure(writer.Put(ContextTag(kTag_VendorId), certElements.VendorId)); } else { ReturnErrorOnFailure(writer.Put(ContextTag(kTag_VendorId), static_cast(certElements.VendorId ^ UINT16_MAX))); } } if (cdConfig.IsPIDArrayPresent()) { ReturnErrorOnFailure(writer.StartContainer(ContextTag(kTag_ProductIdArray), kTLVType_Array, outerContainer2)); uint16_t pid = cdConfig.IsPIDArrayCorrect() ? certElements.ProductIds[0] : static_cast(certElements.ProductIds[0] + 1); for (uint8_t i = 0; i < cdConfig.GetPIDArrayCount(); i++) { ReturnErrorOnFailure(writer.Put(AnonymousTag(), static_cast(pid++))); } ReturnErrorOnFailure(writer.EndContainer(outerContainer2)); } if (cdConfig.IsDeviceTypeIdPresent()) { if (cdConfig.IsDeviceTypeIdCorrect()) { ReturnErrorOnFailure(writer.Put(ContextTag(kTag_DeviceTypeId), certElements.DeviceTypeId)); } else { ReturnErrorOnFailure( writer.Put(ContextTag(kTag_DeviceTypeId), static_cast(certElements.DeviceTypeId ^ UINT32_MAX))); } } if (cdConfig.IsCertIdPresent()) { if (cdConfig.IsCertIdCorrect() && cdConfig.IsCertIdLenCorrect()) { ReturnErrorOnFailure(writer.PutString(ContextTag(kTag_CertificateId), certElements.CertificateId)); } else if (!cdConfig.IsCertIdCorrect()) { ReturnErrorOnFailure(writer.PutString(ContextTag(kTag_CertificateId), "INV20141ZB330001-24")); } else { std::string cert_id(certElements.CertificateId); cert_id += "1234"; ReturnErrorOnFailure(writer.PutString(ContextTag(kTag_CertificateId), cert_id.c_str())); } } if (cdConfig.IsSecurityLevelPresent()) { if (cdConfig.IsSecurityLevelCorrect()) { ReturnErrorOnFailure(writer.Put(ContextTag(kTag_SecurityLevel), certElements.SecurityLevel)); } else { ReturnErrorOnFailure( writer.Put(ContextTag(kTag_SecurityLevel), static_cast(certElements.SecurityLevel ^ UINT8_MAX))); } } if (cdConfig.IsSecurityInfoPresent()) { if (cdConfig.IsSecurityInfoCorrect()) { ReturnErrorOnFailure(writer.Put(ContextTag(kTag_SecurityInformation), certElements.SecurityInformation)); } else { ReturnErrorOnFailure(writer.Put(ContextTag(kTag_SecurityInformation), static_cast(certElements.SecurityInformation ^ UINT16_MAX))); } } if (cdConfig.IsVersionNumberPresent()) { if (cdConfig.IsVersionNumberCorrect()) { ReturnErrorOnFailure(writer.Put(ContextTag(kTag_VersionNumber), certElements.VersionNumber)); } else { ReturnErrorOnFailure( writer.Put(ContextTag(kTag_VersionNumber), static_cast(certElements.VersionNumber ^ UINT16_MAX))); } } if (cdConfig.IsCertTypePresent()) { if (cdConfig.IsCertTypeCorrect()) { ReturnErrorOnFailure(writer.Put(ContextTag(kTag_CertificationType), certElements.CertificationType)); } else { ReturnErrorOnFailure( writer.Put(ContextTag(kTag_CertificationType), static_cast(certElements.CertificationType ^ UINT8_MAX))); } } if (cdConfig.IsDACOriginVIDPresent()) { if (cdConfig.IsDACOriginVIDCorrect() && certElements.DACOriginVIDandPIDPresent) { ReturnErrorOnFailure(writer.Put(ContextTag(kTag_DACOriginVendorId), certElements.DACOriginVendorId)); } else { uint16_t wrong_dac_origin_vid = 0x8008; ReturnErrorOnFailure(writer.Put(ContextTag(kTag_DACOriginVendorId), wrong_dac_origin_vid)); } } if (cdConfig.IsDACOriginPIDPresent()) { if (cdConfig.IsDACOriginPIDCorrect() && certElements.DACOriginVIDandPIDPresent) { ReturnErrorOnFailure(writer.Put(ContextTag(kTag_DACOriginProductId), certElements.DACOriginProductId)); } else { uint16_t wrong_dac_origin_pid = 0xFF00; ReturnErrorOnFailure(writer.Put(ContextTag(kTag_DACOriginProductId), wrong_dac_origin_pid)); } } if (cdConfig.IsAuthPAAListPresent()) { ReturnErrorOnFailure(writer.StartContainer(ContextTag(kTag_AuthorizedPAAList), kTLVType_Array, outerContainer2)); uint8_t wrong_kid[kKeyIdentifierLength] = { 0xF4, 0x44, 0xCA, 0xBB, 0xC5, 0x01, 0x65, 0x77, 0xAA, 0x8B, 0x44, 0xFF, 0xB9, 0x0F, 0xCC, 0xA1, 0x40, 0xFE, 0x66, 0x20 }; for (uint8_t i = 0; i < cdConfig.GetAuthPAAListCount(); i++) { if (cdConfig.IsAuthPAAListCorrect() && (i < certElements.AuthorizedPAAListCount)) { ReturnErrorOnFailure(writer.Put(AnonymousTag(), ByteSpan(certElements.AuthorizedPAAList[i]))); } else { wrong_kid[(i % kKeyIdentifierLength)] ^= 0xFF; ReturnErrorOnFailure(writer.Put(AnonymousTag(), ByteSpan(wrong_kid))); } } ReturnErrorOnFailure(writer.EndContainer(outerContainer2)); } ReturnErrorOnFailure(writer.EndContainer(outerContainer1)); ReturnErrorOnFailure(writer.Finalize()); encodedCertElements.reduce_size(writer.GetLengthWritten()); return CHIP_NO_ERROR; } CHIP_ERROR EncodeEncapsulatedContent_Ignor_Error(const ByteSpan & cdContent, ASN1Writer & writer, CDStructConfig & cdConfig) { /** * EncapsulatedContentInfo ::= SEQUENCE { * eContentType OBJECT IDENTIFIER pkcs7-data (1.2.840.113549.1.7.1), * eContent [0] EXPLICIT OCTET STRING cd_content } */ CHIP_ERROR err = CHIP_NO_ERROR; ASN1_START_SEQUENCE { // eContentType OBJECT IDENTIFIER pkcs7-data (1.2.840.113549.1.7.1) if (cdConfig.IsCMSEContentTypeCorrect()) { ReturnErrorOnFailure(writer.PutObjectId(sOID_ContentType_PKCS7Data, sizeof(sOID_ContentType_PKCS7Data))); } else { ReturnErrorOnFailure(writer.PutObjectId(sOID_ContentType_MSAC, sizeof(sOID_ContentType_MSAC))); } // eContent [0] EXPLICIT OCTET STRING cd_content ASN1_START_CONSTRUCTED(kASN1TagClass_ContextSpecific, 0) { // OCTET STRING cd_content ReturnErrorOnFailure(writer.PutOctetString(cdContent.data(), static_cast(cdContent.size()))); } ASN1_END_CONSTRUCTED; } ASN1_END_SEQUENCE; exit: return err; } CHIP_ERROR EncodeSignerInfo_Ignor_Error(const ByteSpan & signerKeyId, const P256ECDSASignature & signature, ASN1Writer & writer, CDStructConfig & cdConfig) { /** * SignerInfo ::= SEQUENCE { * version INTEGER ( v3(3) ), * subjectKeyIdentifier OCTET STRING, * digestAlgorithm OBJECT IDENTIFIER sha256 (2.16.840.1.101.3.4.2.1), * signatureAlgorithm OBJECT IDENTIFIER ecdsa-with-SHA256 (1.2.840.10045.4.3.2), * signature OCTET STRING } */ CHIP_ERROR err = CHIP_NO_ERROR; ASN1_START_SET { ASN1_START_SEQUENCE { // version INTEGER ( v3(3) ) ASN1_ENCODE_INTEGER(cdConfig.IsSignerInfoVersionCorrect() ? 3 : 2); // subjectKeyIdentifier OCTET STRING if (cdConfig.IsSignerInfoSKIDCorrect()) { ReturnErrorOnFailure(writer.PutOctetString(kASN1TagClass_ContextSpecific, 0, signerKeyId.data(), static_cast(signerKeyId.size()))); } else { uint8_t wrong_skid[kKeyIdentifierLength]; memcpy(wrong_skid, signerKeyId.data(), signerKeyId.size()); wrong_skid[7] ^= 0xFF; ReturnErrorOnFailure(writer.PutOctetString(kASN1TagClass_ContextSpecific, 0, wrong_skid, sizeof(wrong_skid))); } // digestAlgorithm OBJECT IDENTIFIER sha256 (2.16.840.1.101.3.4.2.1) ASN1_START_SEQUENCE { if (cdConfig.IsSignerInfoDigestAlgoCorrect()) { ReturnErrorOnFailure(writer.PutObjectId(sOID_DigestAlgo_SHA256, sizeof(sOID_DigestAlgo_SHA256))); } else { ReturnErrorOnFailure(writer.PutObjectId(sOID_DigestAlgo_SHA1, sizeof(sOID_DigestAlgo_SHA1))); } } ASN1_END_SEQUENCE; // signatureAlgorithm OBJECT IDENTIFIER ecdsa-with-SHA256 (1.2.840.10045.4.3.2) ASN1_START_SEQUENCE { if (cdConfig.IsCMSSigAlgoCorrect()) { ASN1_ENCODE_OBJECT_ID(kOID_SigAlgo_ECDSAWithSHA256); } else { ReturnErrorOnFailure(writer.PutObjectId(sOID_SigAlgo_ECDSAWithSHA1, sizeof(sOID_SigAlgo_ECDSAWithSHA1))); } } ASN1_END_SEQUENCE; uint8_t asn1SignatureBuf[kMax_ECDSA_Signature_Length_Der]; MutableByteSpan asn1Signature(asn1SignatureBuf); ReturnErrorOnFailure(ConvertECDSASignatureRawToDER(P256ECDSASignatureSpan(signature.ConstBytes()), asn1Signature)); if (!cdConfig.IsCMSSignatureCorrect()) { asn1SignatureBuf[10] ^= 0xFF; } // signature OCTET STRING ReturnErrorOnFailure(writer.PutOctetString(asn1Signature.data(), static_cast(asn1Signature.size()))); } ASN1_END_SEQUENCE; } ASN1_END_SET; exit: return err; } CHIP_ERROR CMS_Sign_Ignore_Error(const ByteSpan & cdContent, const ByteSpan & signerKeyId, Crypto::P256Keypair & signerKeypair, MutableByteSpan & signedMessage, CDStructConfig & cdConfig) { /** * CertificationDeclaration ::= SEQUENCE { * version INTEGER ( v3(3) ), * digestAlgorithm OBJECT IDENTIFIER sha256 (2.16.840.1.101.3.4.2.1), * encapContentInfo EncapsulatedContentInfo, * signerInfo SignerInfo } */ CHIP_ERROR err = CHIP_NO_ERROR; ASN1Writer writer; uint32_t size = static_cast(std::min(static_cast(UINT32_MAX), signedMessage.size())); writer.Init(signedMessage.data(), size); ASN1_START_SEQUENCE { // OID identifies the CMS signed-data content type ReturnErrorOnFailure(writer.PutObjectId(sOID_ContentType_PKCS7SignedData, sizeof(sOID_ContentType_PKCS7SignedData))); ASN1_START_CONSTRUCTED(kASN1TagClass_ContextSpecific, 0) { ASN1_START_SEQUENCE { // version INTEGER ( v3(3) ) ASN1_ENCODE_INTEGER(cdConfig.IsCMSVersionCorrect() ? 3 : 2); // digestAlgorithm OBJECT IDENTIFIER sha256 (2.16.840.1.101.3.4.2.1) ASN1_START_SET { ASN1_START_SEQUENCE { if (cdConfig.IsCMSDigestAlgoCorrect()) { ReturnErrorOnFailure(writer.PutObjectId(sOID_DigestAlgo_SHA256, sizeof(sOID_DigestAlgo_SHA256))); } else { ReturnErrorOnFailure(writer.PutObjectId(sOID_DigestAlgo_SHA1, sizeof(sOID_DigestAlgo_SHA1))); } } ASN1_END_SEQUENCE; } ASN1_END_SET; // encapContentInfo EncapsulatedContentInfo ReturnErrorOnFailure(EncodeEncapsulatedContent_Ignor_Error(cdContent, writer, cdConfig)); Crypto::P256ECDSASignature signature; ReturnErrorOnFailure(signerKeypair.ECDSA_sign_msg(cdContent.data(), cdContent.size(), signature)); // signerInfo SignerInfo ReturnErrorOnFailure(EncodeSignerInfo_Ignor_Error(signerKeyId, signature, writer, cdConfig)); } ASN1_END_SEQUENCE; } ASN1_END_CONSTRUCTED; } ASN1_END_SEQUENCE; signedMessage.reduce_size(writer.GetLengthWritten()); exit: return err; } } // namespace bool Cmd_GenCD(int argc, char * argv[]) { if (argc == 1) { gHelpOptions.PrintBriefUsage(stderr); return true; } VerifyOrReturnError(ParseArgs(CMD_NAME, argc, argv, gCmdOptionSets), false); if (gCDConfig.IsErrorTestCaseEnabled()) { fprintf(stderr, "WARNING gen-cd: The ignor-error option is set. This option makes it possible to generate invalid certification " "declaration.\n"); } if (gKeyFileNameOrStr == nullptr) { fprintf(stderr, "Please specify the signing private key using the --key option.\n"); return false; } if (gCertFileNameOrStr == nullptr) { fprintf(stderr, "Please specify the signing certificate using the --cert option.\n"); return false; } if (gSignedCDFileName == nullptr) { fprintf(stderr, "Please specify the file name for the signed Certification Declaration using the --out option.\n"); return false; } if (strcmp(gSignedCDFileName, "-") != 0 && access(gSignedCDFileName, R_OK) == 0) { fprintf(stderr, "Output signed CD file already exists (%s)\n" "To replace the file, please remove it and re-run the command.\n", gSignedCDFileName); return false; } if (!gCDConfig.IsErrorTestCaseEnabled()) { if (gCertElements.VendorId == 0 || gCertElements.ProductIdsCount == 0 || strlen(gCertElements.CertificateId) == 0 || gCertElements.VersionNumber == 0) { fprintf(stderr, "Please specify all mandatory CD elements.\n"); return false; } if (gCertElements.DACOriginVIDandPIDPresent && (gCertElements.DACOriginVendorId == 0 || gCertElements.DACOriginProductId == 0)) { fprintf(stderr, "The DAC Origin Vendor Id and Product Id SHALL be specified together.\n"); return false; } } { std::unique_ptr cert(nullptr, &X509_free); std::unique_ptr key(EVP_PKEY_new(), &EVP_PKEY_free); VerifyOrReturnError(ReadCert(gCertFileNameOrStr, cert), false); VerifyOrReturnError(ReadKey(gKeyFileNameOrStr, key), false); // Extract the subject key id from the X509 certificate. ByteSpan signerKeyId; VerifyOrReturnError(ExtractSKIDFromX509Cert(cert.get(), signerKeyId), false); // Initialize P256Keypair from EVP_PKEY. P256Keypair keypair; { P256SerializedKeypair serializedKeypair; VerifyOrReturnError(SerializeKeyPair(key.get(), serializedKeypair), false); VerifyOrReturnError(keypair.Deserialize(serializedKeypair) == CHIP_NO_ERROR, false); } // Encode CD TLV content. uint8_t encodedCDBuf[kCertificationElements_TLVEncodedMaxLength]; MutableByteSpan encodedCD(encodedCDBuf); if (gCDConfig.IsErrorTestCaseEnabled()) { VerifyOrReturnError(EncodeCertificationElements_Ignore_Error(gCertElements, encodedCD, gCDConfig) == CHIP_NO_ERROR, false); } else { VerifyOrReturnError(EncodeCertificationElements(gCertElements, encodedCD) == CHIP_NO_ERROR, false); } // Sign CD. uint8_t signedMessageBuf[kMaxCMSSignedCDMessage]; MutableByteSpan signedMessage(signedMessageBuf); if (gCDConfig.IsErrorTestCaseEnabled()) { VerifyOrReturnError(CMS_Sign_Ignore_Error(encodedCD, signerKeyId, keypair, signedMessage, gCDConfig) == CHIP_NO_ERROR, false); } else { VerifyOrReturnError(CMS_Sign(encodedCD, signerKeyId, keypair, signedMessage) == CHIP_NO_ERROR, false); } // Write to file. VerifyOrReturnError(WriteDataIntoFile(gSignedCDFileName, signedMessage.data(), static_cast(signedMessage.size()), kDataFormat_Raw), false); } return true; }