Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[chip-cert] Updated support for HEX encoding to certificate tool. #20561

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
191 changes: 79 additions & 112 deletions src/tools/chip-cert/CertUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,36 +169,48 @@ void ToolChipDN::PrintDN(FILE * file, const char * name) const

namespace {

CertFormat DetectCertFormat(uint8_t * cert, uint32_t certLen)
CertFormat DetectCertFormat(const 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 * chipB64Prefix = "FTAB";
static const uint8_t derRawPrefix[] = { 0x30, 0x82 };
static const char * derHexPrefix = "30820";
static const char * pemMarker = "-----BEGIN CERTIFICATE-----";

if (certLen > sizeof(chipRawPrefix) && memcmp(cert, chipRawPrefix, sizeof(chipRawPrefix)) == 0)
VerifyOrReturnError(cert != nullptr, kCertFormat_Unknown);

if ((certLen > sizeof(chipRawPrefix)) && (memcmp(cert, chipRawPrefix, sizeof(chipRawPrefix)) == 0))
{
return kCertFormat_Chip_Raw;
}

if (certLen > chipB64PrefixLen && memcmp(cert, chipB64Prefix, chipB64PrefixLen) == 0)
if ((certLen > strlen(chipHexPrefix)) && (memcmp(cert, chipHexPrefix, strlen(chipHexPrefix)) == 0))
{
return kCertFormat_Chip_Hex;
}

if ((certLen > strlen(chipB64Prefix)) && (memcmp(cert, chipB64Prefix, strlen(chipB64Prefix)) == 0))
{
return kCertFormat_Chip_Base64;
}

if (certLen > chipHexPrefixLen && memcmp(cert, chipHexPrefix, chipHexPrefixLen) == 0)
if ((certLen > sizeof(derRawPrefix)) && (memcmp(cert, derRawPrefix, sizeof(derRawPrefix)) == 0))
{
return kCertFormat_Chip_Hex;
return kCertFormat_X509_DER;
}

if ((certLen > strlen(derHexPrefix)) && (memcmp(cert, derHexPrefix, strlen(derHexPrefix)) == 0))
{
return kCertFormat_X509_Hex;
}

if (ContainsPEMMarker(pemMarker, cert, certLen))
{
return kCertFormat_X509_PEM;
}

return kCertFormat_X509_DER;
return kCertFormat_Unknown;
}

bool SetCertSerialNumber(X509 * cert)
Expand Down Expand Up @@ -538,18 +550,31 @@ bool ReadCert(const char * fileName, X509 * cert, CertFormat & certFmt)
VerifyTrueOrExit(res);

certFmt = DetectCertFormat(certBuf.get(), certLen);
if (certFmt == kCertFormat_Unknown)
{
fprintf(stderr, "Unrecognized Cert Format in File: %s\n", fileName);
return false;
}

if ((certFmt == kCertFormat_X509_Hex) || (certFmt == kCertFormat_Chip_Hex))
{
size_t len = chip::Encoding::HexToBytes(Uint8::to_char(certBuf.get()), certLen, certBuf.get(), certLen);
VerifyOrReturnError(CanCastTo<uint32_t>(2 * len), false);
VerifyOrReturnError(2 * len == certLen, false);
certLen = static_cast<uint32_t>(len);
}

if (certFmt == kCertFormat_X509_PEM)
{
res = ReadCertPEM(fileName, cert);
VerifyTrueOrExit(res);
}
else if (certFmt == kCertFormat_X509_DER)
else if ((certFmt == kCertFormat_X509_DER) || (certFmt == kCertFormat_X509_Hex))
{
const uint8_t * outCert = certBuf.get();

VerifyOrReturnError(chip::CanCastTo<int>(certLen), false);

const uint8_t * outCert = certBuf.get();

if (d2i_X509(&cert, &outCert, static_cast<int>(certLen)) == nullptr)
{
ReportOpenSSLErrorAndExit("d2i_X509", res = false);
Expand All @@ -563,14 +588,6 @@ 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<const char *>(certBuf.get());

certLen = static_cast<uint32_t>(Encoding::HexToBytes(certChars, certLen, certBuf.get(), certLen));
res = (certLen > 0);
VerifyTrueOrExit(res);
}

std::unique_ptr<uint8_t[]> x509CertBuf(new uint8_t[kMaxDERCertLength]);
MutableByteSpan x509Cert(x509CertBuf.get(), kMaxDERCertLength);
Expand Down Expand Up @@ -640,6 +657,7 @@ bool X509ToChipCert(X509 * cert, MutableByteSpan & chipCert)
}

exit:
OPENSSL_free(derCert);
return res;
}

Expand Down Expand Up @@ -678,13 +696,37 @@ bool LoadChipCert(const char * fileName, bool isTrused, ChipCertificateSet & cer

bool WriteCert(const char * fileName, X509 * cert, CertFormat certFmt)
{
bool res = true;
FILE * file = nullptr;
bool res = true;
FILE * file = nullptr;
uint8_t * derCert = nullptr;

VerifyOrExit(cert != nullptr, res = false);
VerifyOrReturnError(cert != nullptr, false);
VerifyOrReturnError(certFmt != kCertFormat_Unknown, false);

res = OpenFile(fileName, file, true);
VerifyTrueOrExit(res);
if (IsChipCertFormat(certFmt))
{
uint8_t chipCertBuf[kMaxCHIPCertLength];
MutableByteSpan chipCert(chipCertBuf);

VerifyOrReturnError(X509ToChipCert(cert, chipCert), false);

return WriteChipCert(fileName, chipCert, certFmt);
}

if (certFmt == kCertFormat_X509_Hex)
{
int derCertLen = i2d_X509(cert, &derCert);
if (derCertLen < 0)
{
ReportOpenSSLErrorAndExit("i2d_X509", res = false);
}

VerifyOrExit(CanCastTo<uint32_t>(derCertLen), res = false);
VerifyOrExit(WriteDataIntoFile(fileName, derCert, static_cast<uint32_t>(derCertLen), kDataFormat_Hex), res = false);
ExitNow(res = true);
}

VerifyOrExit(OpenFile(fileName, file, true), res = false);

if (certFmt == kCertFormat_X509_PEM)
{
Expand All @@ -700,109 +742,34 @@ 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 || certFmt == kCertFormat_Chip_Hex)
else
{
uint8_t * certToWrite = nullptr;
size_t certToWriteLen = 0;
uint32_t certLen;
uint32_t chipCertDecodedLen = HEX_ENCODED_LENGTH(kMaxCHIPCertLength);
std::unique_ptr<uint8_t[]> chipCertDecoded(new uint8_t[chipCertDecodedLen]);
uint8_t chipCertBuf[kMaxCHIPCertLength];
MutableByteSpan chipCert(chipCertBuf);

res = X509ToChipCert(cert, chipCert);
VerifyTrueOrExit(res);
certLen = static_cast<uint32_t>(chipCert.size());

if (certFmt == kCertFormat_Chip_Base64)
{
chipCertDecodedLen = BASE64_ENCODED_LEN(certLen);
res = Base64Encode(chipCert.data(), certLen, chipCertDecoded.get(), chipCertDecodedLen, chipCertDecodedLen);
VerifyTrueOrExit(res);

certToWrite = chipCertDecoded.get();
certToWriteLen = chipCertDecodedLen;
}
else if (certFmt == kCertFormat_Chip_Hex)
{
char * certHex = reinterpret_cast<char *>(chipCertDecoded.get());

chipCertDecodedLen = HEX_ENCODED_LENGTH(certLen);
SuccessOrExit(Encoding::BytesToLowercaseHexBuffer(chipCert.data(), certLen, certHex, chipCertDecodedLen));

certToWrite = chipCertDecoded.get();
certToWriteLen = chipCertDecodedLen;
}
else
{
certToWrite = chipCert.data();
certToWriteLen = chipCert.size();
}

if (fwrite(certToWrite, 1, certToWriteLen, file) != certToWriteLen)
{
fprintf(stderr, "Unable to write to %s: %s\n", fileName, strerror(ferror(file) ? errno : ENOSPC));
ExitNow(res = false);
}
fprintf(stderr, "Unsupported certificate format\n");
ExitNow(res = false);
}

printf("\r\n");

exit:
OPENSSL_free(derCert);
CloseFile(file);
return res;
}

bool WriteChipCert(const char * fileName, const ByteSpan & chipCert, CertFormat certFmt)
{
bool res = true;
FILE * file = nullptr;
const uint8_t * certToWrite = nullptr;
uint32_t certLen = static_cast<uint32_t>(chipCert.size());
size_t certToWriteLen = 0;
uint32_t chipCertDecodedLen = HEX_ENCODED_LENGTH(kMaxCHIPCertLength); // Maximum possible encoding size
std::unique_ptr<uint8_t[]> chipCertDecoded(new uint8_t[chipCertDecodedLen]);

VerifyOrReturnError(certFmt == kCertFormat_Chip_Raw || certFmt == kCertFormat_Chip_Base64 || certFmt == kCertFormat_Chip_Hex,
false);
DataFormat dataFormat = kDataFormat_Unknown;

if (certFmt == kCertFormat_Chip_Base64)
{
chipCertDecodedLen = BASE64_ENCODED_LEN(certLen);
res = Base64Encode(chipCert.data(), certLen, chipCertDecoded.get(), chipCertDecodedLen, chipCertDecodedLen);
VerifyTrueOrExit(res);
VerifyOrReturnError(IsChipCertFormat(certFmt), false);

certToWrite = chipCertDecoded.get();
certToWriteLen = chipCertDecodedLen;
}
else if (certFmt == kCertFormat_Chip_Hex)
{
char * certHex = reinterpret_cast<char *>(chipCertDecoded.get());
chipCertDecodedLen = HEX_ENCODED_LENGTH(certLen);

SuccessOrExit(Encoding::BytesToLowercaseHexBuffer(chipCert.data(), certLen, certHex, chipCertDecodedLen));

certToWrite = chipCertDecoded.get();
certToWriteLen = chipCertDecodedLen;
}
if (certFmt == kCertFormat_Chip_Raw)
dataFormat = kDataFormat_Raw;
else if (certFmt == kCertFormat_Chip_Base64)
dataFormat = kDataFormat_Base64;
else
{
certToWrite = chipCert.data();
certToWriteLen = chipCert.size();
}

res = OpenFile(fileName, file, true);
VerifyTrueOrExit(res);

if (fwrite(certToWrite, 1, certToWriteLen, file) != certToWriteLen)
{
fprintf(stderr, "Unable to write to %s: %s\n", fileName, strerror(ferror(file) ? errno : ENOSPC));
ExitNow(res = false);
}
dataFormat = kDataFormat_Hex;

exit:
CloseFile(file);
return res;
return WriteDataIntoFile(fileName, chipCert.data(), static_cast<uint32_t>(chipCert.size()), dataFormat);
}

bool MakeCert(uint8_t certType, const ToolChipDN * subjectDN, X509 * caCert, EVP_PKEY * caKey, const struct tm & validFrom,
Expand Down
12 changes: 10 additions & 2 deletions src/tools/chip-cert/Cmd_ConvertCert.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
*
* Copyright (c) 2021 Project CHIP Authors
* Copyright (c) 2021-2022 Project CHIP Authors
* Copyright (c) 2013-2017 Nest Labs, Inc.
* All rights reserved.
*
Expand Down Expand Up @@ -42,6 +42,7 @@ OptionDef gCmdOptionDefs[] =
{
{ "x509-pem", kNoArgument, 'p' },
{ "x509-der", kNoArgument, 'd' },
{ "x509-hex", kNoArgument, 'X' },
{ "chip", kNoArgument, 'c' },
{ "chip-hex", kNoArgument, 'x' },
{ "chip-b64", kNoArgument, 'b' },
Expand All @@ -57,6 +58,10 @@ const char * const gCmdOptionHelp =
"\n"
" Output certificate in X.509 DER format.\n"
"\n"
" -X, --x509-hex\n"
"\n"
" Output certificate in X.509 DER hex encoded format.\n"
"\n"
" -c, --chip\n"
"\n"
" Output certificate in raw CHIP TLV format.\n"
Expand Down Expand Up @@ -110,7 +115,7 @@ OptionSet * gCmdOptionSets[] =

const char * gInFileName = nullptr;
const char * gOutFileName = nullptr;
CertFormat gOutCertFormat = kCertFormat_Chip_Base64;
CertFormat gOutCertFormat = kCertFormat_Default;

bool HandleOption(const char * progName, OptionSet * optSet, int id, const char * name, const char * arg)
{
Expand All @@ -122,6 +127,9 @@ bool HandleOption(const char * progName, OptionSet * optSet, int id, const char
case 'd':
gOutCertFormat = kCertFormat_X509_DER;
break;
case 'X':
gOutCertFormat = kCertFormat_X509_Hex;
break;
case 'x':
gOutCertFormat = kCertFormat_Chip_Hex;
break;
Expand Down
Loading