Skip to content

Commit

Permalink
[chip-cert] Updated support for HEX encoding to certificate tool. (#2…
Browse files Browse the repository at this point in the history
…0561)

- Added HEX format support for the DER encoded data.
  - Added HEX foramt support for the Certification Declaration data.
  - Added support for only Public Key (without Private Key) including public key in HEX format.
  - Code refactored.
  • Loading branch information
emargolis authored Jul 16, 2022
1 parent ce92c97 commit aa7c0ba
Show file tree
Hide file tree
Showing 9 changed files with 468 additions and 261 deletions.
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

0 comments on commit aa7c0ba

Please sign in to comment.