/* * * Copyright (c) 2021-2022 Project CHIP Authors * Copyright (c) 2013-2017 Nest Labs, Inc. * 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 validates a CHIP certificate. * */ #include "chip-cert.h" #include #include "vector" namespace { using namespace chip; using namespace chip::ArgParser; using namespace chip::Credentials; using namespace chip::ASN1; #define CMD_NAME "chip-cert validate-cert" bool HandleOption(const char * progName, OptionSet * optSet, int id, const char * name, const char * arg); bool HandleNonOptionArgs(const char * progName, int argc, char * const argv[]); // clang-format off OptionDef gCmdOptionDefs[] = { { "cert", kArgumentRequired, 'c' }, { "trusted-cert", kArgumentRequired, 't' }, { } }; const char * const gCmdOptionHelp = " -c, --cert \n" "\n" " File or string containing an untrusted CHIP certificate to be used during\n" " validation. Usually, it is Intermediate CA certificate (ICAC).\n" "\n" " -t, --trusted-cert \n" "\n" " File or string containing a trusted CHIP certificate to be used during\n" " validation. Usually, it is trust anchor root certificate (RCAC).\n" "\n" ; OptionSet gCmdOptions = { HandleOption, gCmdOptionDefs, "COMMAND OPTIONS", gCmdOptionHelp }; HelpOptions gHelpOptions( CMD_NAME, "Usage: " CMD_NAME " [ ] \n", CHIP_VERSION_STRING "\n" COPYRIGHT_STRING, "Validate a chain of CHIP certificates.\n" "\n" "ARGUMENTS\n" "\n" " \n" "\n" " File or string containing the certificate to be validated.\n" " The formats of all input certificates are auto-detected and can be any of:\n" " X.509 PEM, X.509 DER, X.509 HEX, CHIP base-64, CHIP raw TLV or CHIP HEX.\n" "\n" ); OptionSet * gCmdOptionSets[] = { &gCmdOptions, &gHelpOptions, nullptr }; // clang-format on enum { kMaxCerts = 16, }; const char * gTargetCertFileName = nullptr; const char * gCACertFileNames[kMaxCerts] = { nullptr }; bool gCACertIsTrusted[kMaxCerts] = { false }; size_t gNumCertFileNames = 0; bool HandleOption(const char * progName, OptionSet * optSet, int id, const char * name, const char * arg) { switch (id) { case 'c': case 't': if (gNumCertFileNames >= kMaxCerts) { PrintArgError("%s: Too many certificate files\n", progName); return false; } gCACertFileNames[gNumCertFileNames] = arg; gCACertIsTrusted[gNumCertFileNames++] = (id == 't'); break; default: PrintArgError("%s: Unhandled option: %s\n", progName, name); return false; } return true; } bool HandleNonOptionArgs(const char * progName, int argc, char * const argv[]) { if (argc == 0) { PrintArgError("%s: Please specify the name of the certificate to be validated.\n", progName); return false; } if (argc > 1) { PrintArgError("%s: Unexpected argument: %s\n", progName, argv[1]); return false; } gTargetCertFileName = argv[0]; return true; } } // namespace bool Cmd_ValidateCert(int argc, char * argv[]) { bool res = true; CHIP_ERROR err = CHIP_NO_ERROR; ChipCertificateSet certSet; const ChipCertificateData * certToBeValidated = nullptr; const ChipCertificateData * validatedCert = nullptr; ValidationContext context; uint8_t certsBuf[kMaxCerts * kMaxCHIPCertLength]; MutableByteSpan chipCert[kMaxCerts]; context.Reset(); if (argc == 1) { gHelpOptions.PrintBriefUsage(stderr); ExitNow(res = true); } res = ParseArgs(CMD_NAME, argc, argv, gCmdOptionSets, HandleNonOptionArgs); VerifyTrueOrExit(res); err = certSet.Init(kMaxCerts); if (err != CHIP_NO_ERROR) { fprintf(stderr, "Failed to initialize certificate set: %s\n", chip::ErrorStr(err)); ExitNow(res = false); } for (size_t i = 0; i < gNumCertFileNames; i++) { chipCert[i] = MutableByteSpan(&certsBuf[i * kMaxCHIPCertLength], kMaxCHIPCertLength); res = LoadChipCert(gCACertFileNames[i], gCACertIsTrusted[i], certSet, chipCert[i]); VerifyTrueOrExit(res); } chipCert[gNumCertFileNames] = MutableByteSpan(&certsBuf[gNumCertFileNames * kMaxCHIPCertLength], kMaxCHIPCertLength); res = LoadChipCert(gTargetCertFileName, false, certSet, chipCert[gNumCertFileNames]); VerifyTrueOrExit(res); certToBeValidated = certSet.GetLastCert(); context.Reset(); uint32_t currentTime; res = chip::UnixEpochToChipEpochTime(static_cast(time(nullptr)), currentTime); context.mEffectiveTime.Set(currentTime); context.mRequiredKeyUsages.Set(KeyUsageFlags::kDigitalSignature); VerifyTrueOrExit(res); err = certSet.FindValidCert(certToBeValidated->mSubjectDN, certToBeValidated->mSubjectKeyId, context, &validatedCert); if (err != CHIP_NO_ERROR) { fprintf(stderr, "Failed certificate chain validation: %s\n", chip::ErrorStr(err)); ExitNow(res = false); } exit: certSet.Release(); return res; }