/* * * 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 converts a CHIP private key between CHIP serialized and * PEM/DER formats. * */ #include "chip-cert.h" namespace { using namespace chip::ArgParser; using namespace chip::Credentials; #define CMD_NAME "chip-cert convert-key" 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[] = { { "x509-pem", kNoArgument, 'p' }, { "x509-der", kNoArgument, 'd' }, { "x509-hex", kNoArgument, 'x' }, { "x509-pubkey-pem", kNoArgument, 'P' }, { "chip", kNoArgument, 'c' }, { "chip-b64", kNoArgument, 'b' }, { "chip-hex", kNoArgument, 'e' }, { "chip-pubkey", kNoArgument, 'C' }, { "chip-pubkey-b64", kNoArgument, 'B' }, { "chip-pubkey-hex", kNoArgument, 'E' }, { } }; const char * const gCmdOptionHelp = " -p, --x509-pem\n" "\n" " Output the private key in SEC1/RFC-5915 PEM format.\n" "\n" " -d, --x509-der\n" "\n" " Output the private key in SEC1/RFC-5915 DER format. \n" "\n" " -x, --x509-hex\n" "\n" " Output the private key in SEC1/RFC-5915 DER hex encoded format.\n" "\n" " -P, --x509-pubkey-pem\n" "\n" " Output the public key in SEC1/RFC-5915 PEM format.\n" "\n" " -c, --chip\n" "\n" " Output the private key in raw CHIP serialized format.\n" " -x, --chip-hex\n" "\n" " Output the private key in hex encoded CHIP serialized format.\n" "\n" " -b, --chip-b64\n" "\n" " Output the private key in base-64 encoded CHIP serialized format.\n" " This is the default.\n" "\n" " -e, --chip-hex\n" "\n" " Output the private key in hex encoded CHIP serialized format.\n" "\n" " -C, --chip-pubkey\n" "\n" " Output the raw public key.\n" "\n" " -B, --chip-pubkey-b64\n" "\n" " Output the public key in base-64 encoded format.\n" "\n" " -E, --chip-pubkey-hex\n" "\n" " Output the public key in hex encoded format.\n" "\n" ; OptionSet gCmdOptions = { HandleOption, gCmdOptionDefs, "COMMAND OPTIONS", gCmdOptionHelp }; HelpOptions gHelpOptions( CMD_NAME, "Usage: " CMD_NAME " [ ] \n", CHIP_VERSION_STRING "\n" COPYRIGHT_STRING, "Convert private/public key between CHIP and X.509 formats.\n" "\n" "ARGUMENTS\n" "\n" " \n" "\n" " File or string containing private/public key to be converted.\n" " The format of the input key is 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" " Note: the private key formats include both private and public keys, while\n" " the public key formats include only public keys. Therefore, conversion from any\n" " private key format to public key is supported but conversion from public key\n" " to private CANNOT be done.\n" "\n" " \n" "\n" " The output private key file name, or '-' to write to stdout.\n" "\n" ); OptionSet *gCmdOptionSets[] = { &gCmdOptions, &gHelpOptions, nullptr }; // clang-ormat on const char * gInFileNameOrStr = nullptr; const char * gOutFileName = nullptr; KeyFormat gOutFormat = kKeyFormat_Chip_Base64; bool HandleOption(const char * progName, OptionSet * optSet, int id, const char * name, const char * arg) { switch (id) { case 'p': gOutFormat = kKeyFormat_X509_PEM; break; case 'd': gOutFormat = kKeyFormat_X509_DER; break; case 'x': gOutFormat = kKeyFormat_X509_Hex; break; case 'P': gOutFormat = kKeyFormat_X509_Pubkey_PEM; break; case 'c': gOutFormat = kKeyFormat_Chip_Raw; break; case 'b': gOutFormat = kKeyFormat_Chip_Base64; break; case 'e': gOutFormat = kKeyFormat_Chip_Hex; break; case 'C': gOutFormat = kKeyFormat_Chip_Pubkey_Raw; break; case 'B': gOutFormat = kKeyFormat_Chip_Pubkey_Base64; break; case 'E': gOutFormat = kKeyFormat_Chip_Pubkey_Hex; 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 input key file, or - for stdin.\n", progName); return false; } if (argc == 1) { PrintArgError("%s: Please specify the name of the output key file, or - for stdout\n", progName); return false; } if (argc > 2) { PrintArgError("%s: Unexpected argument: %s\n", progName, argv[2]); return false; } gInFileNameOrStr = argv[0]; gOutFileName = argv[1]; return true; } } // namespace bool Cmd_ConvertKey(int argc, char * argv[]) { bool res = true; std::unique_ptr key(EVP_PKEY_new(), &EVP_PKEY_free); if (argc == 1) { gHelpOptions.PrintBriefUsage(stderr); ExitNow(res = true); } res = ParseArgs(CMD_NAME, argc, argv, gCmdOptionSets, HandleNonOptionArgs); VerifyTrueOrExit(res); res = InitOpenSSL(); VerifyTrueOrExit(res); res = ReadKey(gInFileNameOrStr, key); VerifyTrueOrExit(res); if (IsPrivateKeyFormat(gOutFormat) && EC_KEY_get0_private_key(EVP_PKEY_get1_EC_KEY(key.get())) == nullptr) { fprintf(stderr, "Cannot convert to the private key format as the original key doesn't include private key.\n"); return false; } res = WriteKey(gOutFileName, key.get(), gOutFormat); VerifyTrueOrExit(res); exit: return res; }