From 81e18a9ddc366be31dd69d3fbca91fafb4c0d517 Mon Sep 17 00:00:00 2001 From: Martin Turon Date: Fri, 1 Jul 2022 12:01:01 -0700 Subject: [PATCH] [chip-cert] Add support for 'chip-hex' encoding to certificate tool. (#20147) * [chip-cert] Add handling for hex encoding to chip-cert tool. * [chip-tool] Expand chip-hex support to convert-key and generate-cert. * [spell] [restyle] [pretty] --- src/lib/support/BytesToHex.h | 12 ++++ src/tools/chip-cert/CertUtils.cpp | 76 +++++++++++++++++++------ src/tools/chip-cert/Cmd_ConvertCert.cpp | 14 ++++- src/tools/chip-cert/Cmd_ConvertKey.cpp | 13 ++++- src/tools/chip-cert/Cmd_GenCert.cpp | 6 ++ src/tools/chip-cert/KeyUtils.cpp | 49 +++++++++++++--- src/tools/chip-cert/README.md | 12 +++- src/tools/chip-cert/chip-cert.h | 6 +- 8 files changed, 152 insertions(+), 36 deletions(-) diff --git a/src/lib/support/BytesToHex.h b/src/lib/support/BytesToHex.h index e21a972f4725c6..8ade696f17db05 100644 --- a/src/lib/support/BytesToHex.h +++ b/src/lib/support/BytesToHex.h @@ -178,5 +178,17 @@ size_t UppercaseHexToUint32(const char * src_hex, const size_t src_size, uint32_ /** Same as UppercaseHexToUint64() but for uint16_t. */ size_t UppercaseHexToUint16(const char * src_hex, const size_t src_size, uint16_t & dest); +/** + * Computes the hex encoded length for a given input length. + * Left shift to generate optimized equivalent of LEN*2. + */ +#define HEX_ENCODED_LENGTH(LEN) ((LEN) << 1) + +/** + * Computes the maximum possible decoded length for a given hex string input length. + * Right shift to generate optimized equivalent of LEN/2. + */ +#define HEX_MAX_DECODED_LENGTH(LEN) ((LEN) >> 1) + } // namespace Encoding } // namespace chip diff --git a/src/tools/chip-cert/CertUtils.cpp b/src/tools/chip-cert/CertUtils.cpp index 8c0991a7c1a0ca..26ffb18053c613 100644 --- a/src/tools/chip-cert/CertUtils.cpp +++ b/src/tools/chip-cert/CertUtils.cpp @@ -36,6 +36,7 @@ using namespace chip; using namespace chip::Credentials; using namespace chip::ASN1; using namespace chip::TLV; +using namespace chip::Encoding; bool ToolChipDN::SetCertName(X509_NAME * name) const { @@ -173,6 +174,8 @@ CertFormat DetectCertFormat(uint8_t * cert, uint32_t certLen) static const uint8_t chipRawPrefix[] = { 0x15, 0x30, 0x01 }; static const char * chipB64Prefix = "FTAB"; static const size_t chipB64PrefixLen = strlen(chipB64Prefix); + static const char * chipHexPrefix = "153001"; + static const size_t chipHexPrefixLen = strlen(chipHexPrefix); static const char * pemMarker = "-----BEGIN CERTIFICATE-----"; if (certLen > sizeof(chipRawPrefix) && memcmp(cert, chipRawPrefix, sizeof(chipRawPrefix)) == 0) @@ -185,6 +188,11 @@ CertFormat DetectCertFormat(uint8_t * cert, uint32_t certLen) return kCertFormat_Chip_Base64; } + if (certLen > chipHexPrefixLen && memcmp(cert, chipHexPrefix, chipHexPrefixLen) == 0) + { + return kCertFormat_Chip_Hex; + } + if (ContainsPEMMarker(pemMarker, cert, certLen)) { return kCertFormat_X509_PEM; @@ -547,7 +555,7 @@ bool ReadCert(const char * fileName, X509 * cert, CertFormat & certFmt) ReportOpenSSLErrorAndExit("d2i_X509", res = false); } } - // Otherwise, it is either CHIP TLV or CHIP TLV Base64 encoded. + // Otherwise, it is either CHIP TLV in raw, Base64, or hex encoded format. else { if (certFmt == kCertFormat_Chip_Base64) @@ -555,6 +563,14 @@ bool ReadCert(const char * fileName, X509 * cert, CertFormat & certFmt) res = Base64Decode(certBuf.get(), certLen, certBuf.get(), certLen, certLen); VerifyTrueOrExit(res); } + else if (certFmt == kCertFormat_Chip_Hex) + { + const char * certChars = reinterpret_cast(certBuf.get()); + + certLen = static_cast(Encoding::HexToBytes(certChars, certLen, certBuf.get(), certLen)); + res = (certLen > 0); + VerifyTrueOrExit(res); + } std::unique_ptr x509CertBuf(new uint8_t[kMaxDERCertLength]); MutableByteSpan x509Cert(x509CertBuf.get(), kMaxDERCertLength); @@ -684,26 +700,38 @@ bool WriteCert(const char * fileName, X509 * cert, CertFormat certFmt) ReportOpenSSLErrorAndExit("i2d_X509_fp", res = false); } } - else if (certFmt == kCertFormat_Chip_Raw || certFmt == kCertFormat_Chip_Base64) + else if (certFmt == kCertFormat_Chip_Raw || certFmt == kCertFormat_Chip_Base64 || certFmt == kCertFormat_Chip_Hex) { - uint8_t * certToWrite = nullptr; - size_t certToWriteLen = 0; - uint32_t chipCertBase64Len = BASE64_ENCODED_LEN(kMaxCHIPCertLength); - std::unique_ptr chipCertBase64(new uint8_t[chipCertBase64Len]); + uint8_t * certToWrite = nullptr; + size_t certToWriteLen = 0; + uint32_t certLen; + uint32_t chipCertDecodedLen = HEX_ENCODED_LENGTH(kMaxCHIPCertLength); + std::unique_ptr chipCertDecoded(new uint8_t[chipCertDecodedLen]); uint8_t chipCertBuf[kMaxCHIPCertLength]; MutableByteSpan chipCert(chipCertBuf); res = X509ToChipCert(cert, chipCert); VerifyTrueOrExit(res); + certLen = static_cast(chipCert.size()); if (certFmt == kCertFormat_Chip_Base64) { - res = Base64Encode(chipCert.data(), static_cast(chipCert.size()), chipCertBase64.get(), chipCertBase64Len, - chipCertBase64Len); + chipCertDecodedLen = BASE64_ENCODED_LEN(certLen); + res = Base64Encode(chipCert.data(), certLen, chipCertDecoded.get(), chipCertDecodedLen, chipCertDecodedLen); VerifyTrueOrExit(res); - certToWrite = chipCertBase64.get(); - certToWriteLen = chipCertBase64Len; + certToWrite = chipCertDecoded.get(); + certToWriteLen = chipCertDecodedLen; + } + else if (certFmt == kCertFormat_Chip_Hex) + { + char * certHex = reinterpret_cast(chipCertDecoded.get()); + + chipCertDecodedLen = HEX_ENCODED_LENGTH(certLen); + SuccessOrExit(Encoding::BytesToLowercaseHexBuffer(chipCert.data(), certLen, certHex, chipCertDecodedLen)); + + certToWrite = chipCertDecoded.get(); + certToWriteLen = chipCertDecodedLen; } else { @@ -718,6 +746,8 @@ bool WriteCert(const char * fileName, X509 * cert, CertFormat certFmt) } } + printf("\r\n"); + exit: CloseFile(file); return res; @@ -728,20 +758,32 @@ bool WriteChipCert(const char * fileName, const ByteSpan & chipCert, CertFormat bool res = true; FILE * file = nullptr; const uint8_t * certToWrite = nullptr; + uint32_t certLen = static_cast(chipCert.size()); size_t certToWriteLen = 0; - uint32_t chipCertBase64Len = BASE64_ENCODED_LEN(static_cast(chipCert.size())); - std::unique_ptr chipCertBase64(new uint8_t[chipCertBase64Len]); + uint32_t chipCertDecodedLen = HEX_ENCODED_LENGTH(kMaxCHIPCertLength); // Maximum possible encoding size + std::unique_ptr chipCertDecoded(new uint8_t[chipCertDecodedLen]); - VerifyOrReturnError(certFmt == kCertFormat_Chip_Raw || certFmt == kCertFormat_Chip_Base64, false); + VerifyOrReturnError(certFmt == kCertFormat_Chip_Raw || certFmt == kCertFormat_Chip_Base64 || certFmt == kCertFormat_Chip_Hex, + false); if (certFmt == kCertFormat_Chip_Base64) { - res = Base64Encode(chipCert.data(), static_cast(chipCert.size()), chipCertBase64.get(), chipCertBase64Len, - chipCertBase64Len); + chipCertDecodedLen = BASE64_ENCODED_LEN(certLen); + res = Base64Encode(chipCert.data(), certLen, chipCertDecoded.get(), chipCertDecodedLen, chipCertDecodedLen); VerifyTrueOrExit(res); - certToWrite = chipCertBase64.get(); - certToWriteLen = chipCertBase64Len; + certToWrite = chipCertDecoded.get(); + certToWriteLen = chipCertDecodedLen; + } + else if (certFmt == kCertFormat_Chip_Hex) + { + char * certHex = reinterpret_cast(chipCertDecoded.get()); + chipCertDecodedLen = HEX_ENCODED_LENGTH(certLen); + + SuccessOrExit(Encoding::BytesToLowercaseHexBuffer(chipCert.data(), certLen, certHex, chipCertDecodedLen)); + + certToWrite = chipCertDecoded.get(); + certToWriteLen = chipCertDecodedLen; } else { diff --git a/src/tools/chip-cert/Cmd_ConvertCert.cpp b/src/tools/chip-cert/Cmd_ConvertCert.cpp index ddbe30ee91fccd..2f8ca4988e4af0 100644 --- a/src/tools/chip-cert/Cmd_ConvertCert.cpp +++ b/src/tools/chip-cert/Cmd_ConvertCert.cpp @@ -41,8 +41,9 @@ bool HandleNonOptionArgs(const char * progName, int argc, char * const argv[]); OptionDef gCmdOptionDefs[] = { { "x509-pem", kNoArgument, 'p' }, - { "x509-der", kNoArgument, 'x' }, + { "x509-der", kNoArgument, 'd' }, { "chip", kNoArgument, 'c' }, + { "chip-hex", kNoArgument, 'x' }, { "chip-b64", kNoArgument, 'b' }, { } }; @@ -52,7 +53,7 @@ const char * const gCmdOptionHelp = "\n" " Output certificate in X.509 PEM format.\n" "\n" - " -x, --x509-der\n" + " -d, --x509-der\n" "\n" " Output certificate in X.509 DER format.\n" "\n" @@ -60,6 +61,10 @@ const char * const gCmdOptionHelp = "\n" " Output certificate in raw CHIP TLV format.\n" "\n" + " -x, --chip-hex\n" + "\n" + " Output certificate in CHIP TLV hexadecimal format.\n" + "\n" " -b --chip-b64\n" "\n" " Output certificate in CHIP TLV base-64 encoded format.\n" @@ -114,9 +119,12 @@ bool HandleOption(const char * progName, OptionSet * optSet, int id, const char case 'p': gOutCertFormat = kCertFormat_X509_PEM; break; - case 'x': + case 'd': gOutCertFormat = kCertFormat_X509_DER; break; + case 'x': + gOutCertFormat = kCertFormat_Chip_Hex; + break; case 'b': gOutCertFormat = kCertFormat_Chip_Base64; break; diff --git a/src/tools/chip-cert/Cmd_ConvertKey.cpp b/src/tools/chip-cert/Cmd_ConvertKey.cpp index 88f117bccdf47a..f84db75cc7f3ba 100644 --- a/src/tools/chip-cert/Cmd_ConvertKey.cpp +++ b/src/tools/chip-cert/Cmd_ConvertKey.cpp @@ -41,8 +41,9 @@ bool HandleNonOptionArgs(const char * progName, int argc, char * const argv[]); OptionDef gCmdOptionDefs[] = { { "x509-pem", kNoArgument, 'p' }, - { "x509-der", kNoArgument, 'x' }, + { "x509-der", kNoArgument, 'd' }, { "chip", kNoArgument, 'c' }, + { "chip-hex", kNoArgument, 'x' }, { "chip-b64", kNoArgument, 'b' }, { } }; @@ -52,13 +53,16 @@ const char * const gCmdOptionHelp = "\n" " Output the private key in SEC1/RFC-5915 PEM format.\n" "\n" - " -x, --x509-der\n" + " -d, --x509-der\n" "\n" " Output the private key in SEC1/RFC-5915 DER 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" @@ -115,12 +119,15 @@ bool HandleOption(const char * progName, OptionSet * optSet, int id, const char case 'p': gOutFormat = kKeyFormat_X509_PEM; break; - case 'x': + case 'd': gOutFormat = kKeyFormat_X509_DER; break; case 'b': gOutFormat = kKeyFormat_Chip_Base64; break; + case 'x': + gOutFormat = kKeyFormat_Chip_Hex; + break; case 'c': gOutFormat = kKeyFormat_Chip_Raw; break; diff --git a/src/tools/chip-cert/Cmd_GenCert.cpp b/src/tools/chip-cert/Cmd_GenCert.cpp index 9de1492200b1dc..c8d5f6a8af63d8 100644 --- a/src/tools/chip-cert/Cmd_GenCert.cpp +++ b/src/tools/chip-cert/Cmd_GenCert.cpp @@ -140,6 +140,7 @@ const char * const gCmdOptionHelp = " x509-pem - X.509 PEM format\n" " x509-der - X.509 DER format\n" " chip - raw CHIP TLV format\n" + " chip-hex - hex encoded CHIP TLV format\n" " chip-b64 - base-64 encoded CHIP TLV format (default)\n" "\n" " -V, --valid-from --
[ :: ]\n" @@ -459,6 +460,11 @@ bool HandleOption(const char * progName, OptionSet * optSet, int id, const char gOutCertFormat = kCertFormat_Chip_Base64; gOutKeyFormat = kKeyFormat_Chip_Base64; } + else if (strcmp(arg, "chip-hex") == 0) + { + gOutCertFormat = kCertFormat_Chip_Hex; + gOutKeyFormat = kKeyFormat_Chip_Hex; + } else { PrintArgError("%s: Invalid value specified for the output format: %s\n", progName, arg); diff --git a/src/tools/chip-cert/KeyUtils.cpp b/src/tools/chip-cert/KeyUtils.cpp index f72fce4f255943..68b5ee7a5b4850 100644 --- a/src/tools/chip-cert/KeyUtils.cpp +++ b/src/tools/chip-cert/KeyUtils.cpp @@ -26,6 +26,7 @@ #include "chip-cert.h" #include +#include #include using namespace chip; @@ -33,6 +34,7 @@ using namespace chip::Protocols; using namespace chip::ASN1; using namespace chip::TLV; using namespace chip::Crypto; +using namespace chip::Encoding; namespace { @@ -42,6 +44,9 @@ KeyFormat DetectKeyFormat(const uint8_t * key, uint32_t keyLen) static const char * ecPEMMarker = "-----BEGIN EC PRIVATE KEY-----"; static const char * pkcs8PEMMarker = "-----BEGIN PRIVATE KEY-----"; static const char * ecPUBPEMMarker = "-----BEGIN PUBLIC KEY-----"; + static const char * chipHexPrefix = "04c2"; + static const size_t chipHexPrefixLen = strlen(chipHexPrefix); + static const size_t chipHexMinLen = HEX_ENCODED_LENGTH(p256SerializedKeypairLen); if (keyLen == p256SerializedKeypairLen) { @@ -63,6 +68,11 @@ KeyFormat DetectKeyFormat(const uint8_t * key, uint32_t keyLen) return kKeyFormat_X509_PUBKEY_PEM; } + if (keyLen >= chipHexMinLen && memcmp(key, chipHexPrefix, chipHexPrefixLen) == 0) + { + return kKeyFormat_Chip_Hex; + } + return kKeyFormat_X509_DER; } @@ -166,6 +176,16 @@ bool ReadKey(const char * fileName, EVP_PKEY * key, bool ignorErrorIfUnsupported keyFormat = kKeyFormat_Chip_Raw; } + else if (keyFormat == kKeyFormat_Chip_Hex) + { + const char * keyChars = reinterpret_cast(keyData.get()); + + keyDataLen = static_cast(Encoding::HexToBytes(keyChars, keyDataLen, keyData.get(), keyDataLen)); + res = (keyDataLen > 0); + VerifyTrueOrExit(res); + + keyFormat = kKeyFormat_Chip_Raw; + } if (keyFormat == kKeyFormat_Chip_Raw) { @@ -267,10 +287,10 @@ bool WritePrivateKey(const char * fileName, EVP_PKEY * key, KeyFormat keyFmt) uint8_t * keyToWrite = nullptr; uint32_t keyToWriteLen = 0; P256SerializedKeypair serializedKeypair; - uint32_t chipKeySize = serializedKeypair.Capacity(); - uint32_t chipKeyBase64Size = BASE64_ENCODED_LEN(chipKeySize); + uint32_t chipKeySize = serializedKeypair.Capacity(); + uint32_t chipKeyDecodedSize = HEX_ENCODED_LENGTH(chipKeySize); std::unique_ptr chipKey(new uint8_t[chipKeySize]); - std::unique_ptr chipKeyBase64(new uint8_t[chipKeyBase64Size]); + std::unique_ptr chipKeyDecoded(new uint8_t[chipKeyDecodedSize]); VerifyOrExit(key != nullptr, res = false); @@ -298,20 +318,29 @@ bool WritePrivateKey(const char * fileName, EVP_PKEY * key, KeyFormat keyFmt) } break; case kKeyFormat_Chip_Raw: + case kKeyFormat_Chip_Hex: case kKeyFormat_Chip_Base64: res = SerializeKeyPair(key, serializedKeypair); VerifyTrueOrExit(res); if (keyFmt == kKeyFormat_Chip_Base64) { - uint32_t chipKeyBase64Len; - - res = Base64Encode(serializedKeypair.Bytes(), static_cast(serializedKeypair.Length()), chipKeyBase64.get(), - chipKeyBase64Size, chipKeyBase64Len); + res = Base64Encode(serializedKeypair.Bytes(), static_cast(serializedKeypair.Length()), chipKeyDecoded.get(), + chipKeyDecodedSize, chipKeyDecodedSize); VerifyTrueOrExit(res); - keyToWrite = chipKeyBase64.get(); - keyToWriteLen = chipKeyBase64Len; + keyToWrite = chipKeyDecoded.get(); + keyToWriteLen = chipKeyDecodedSize; + } + else if (keyFmt == kKeyFormat_Chip_Hex) + { + char * keyHex = reinterpret_cast(chipKeyDecoded.get()); + + SuccessOrExit(Encoding::BytesToLowercaseHexBuffer( + serializedKeypair.Bytes(), static_cast(serializedKeypair.Length()), keyHex, chipKeyDecodedSize)); + + keyToWrite = chipKeyDecoded.get(); + keyToWriteLen = chipKeyDecodedSize; } else { @@ -330,6 +359,8 @@ bool WritePrivateKey(const char * fileName, EVP_PKEY * key, KeyFormat keyFmt) ExitNow(res = false); } + printf("\r\n"); + exit: CloseFile(file); diff --git a/src/tools/chip-cert/README.md b/src/tools/chip-cert/README.md index e9beeb35bd9f5c..6d7227b593baba 100644 --- a/src/tools/chip-cert/README.md +++ b/src/tools/chip-cert/README.md @@ -258,6 +258,7 @@ COMMAND OPTIONS x509-pem - X.509 PEM format x509-der - X.509 DER format chip - raw CHIP TLV format + chip-hex - hex encoded CHIP TLV format chip-b64 - base-64 encoded CHIP TLV format (default) -V, --valid-from --
[ :: ] @@ -306,7 +307,7 @@ COMMAND OPTIONS Output certificate in X.509 PEM format. - -x, --x509-der + -d, --x509-der Output certificate in X.509 DER format. @@ -314,6 +315,10 @@ COMMAND OPTIONS Output certificate in raw CHIP TLV format. + -x, --chip-hex + + Output certificate in CHIP TLV hexadecimal format. + -b --chip-b64 Output certificate in CHIP TLV base-64 encoded format. @@ -353,13 +358,16 @@ COMMAND OPTIONS Output the private key in SEC1/RFC-5915 PEM format. - -x, --x509-der + -d, --x509-der Output the private key in SEC1/RFC-5915 DER format. -c, --chip Output the private key in raw CHIP serialized format. + -x, --chip-hex + + Output the private key in hex encoded CHIP serialized format. -b, --chip-b64 diff --git a/src/tools/chip-cert/chip-cert.h b/src/tools/chip-cert/chip-cert.h index e9889cc0210f2c..7c9851764a0bdd 100644 --- a/src/tools/chip-cert/chip-cert.h +++ b/src/tools/chip-cert/chip-cert.h @@ -94,7 +94,8 @@ enum CertFormat kCertFormat_X509_DER, kCertFormat_X509_PEM, kCertFormat_Chip_Raw, - kCertFormat_Chip_Base64 + kCertFormat_Chip_Base64, + kCertFormat_Chip_Hex, }; enum KeyFormat @@ -104,7 +105,8 @@ enum KeyFormat kKeyFormat_X509_PEM, kKeyFormat_X509_PUBKEY_PEM, kKeyFormat_Chip_Raw, - kKeyFormat_Chip_Base64 + kKeyFormat_Chip_Base64, + kKeyFormat_Chip_Hex }; enum AttCertType