blob: 32479c45c7b8fcea4b0e23653dcd3e579e62e9fc [file] [log] [blame]
/*
*
* 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 utility functions for OpenSSL, base-64
* encoding and decoding, date and time parsing, integer parsing,
* OID translation, and file reading.
*
*/
#include "chip-cert.h"
#include <lib/core/CHIPEncoding.h>
#include <lib/support/BytesToHex.h>
#include <lib/support/SafeInt.h>
using namespace chip;
using namespace chip::Credentials;
using namespace chip::Encoding;
using namespace chip::ASN1;
int gNIDChipNodeId;
int gNIDChipFirmwareSigningId;
int gNIDChipICAId;
int gNIDChipRootId;
int gNIDChipFabricId;
int gNIDChipCASEAuthenticatedTag;
int gNIDChipAttAttrVID;
int gNIDChipAttAttrPID;
int gNIDChipCurveP256 = EC_curve_nist2nid("P-256");
bool InitOpenSSL()
{
bool res = true;
OPENSSL_malloc_init();
ERR_load_crypto_strings();
OpenSSL_add_all_algorithms();
gNIDChipNodeId = OBJ_create("1.3.6.1.4.1.37244.1.1", "ChipNodeId", "ChipNodeId");
if (gNIDChipNodeId == 0)
{
ReportOpenSSLErrorAndExit("OBJ_create", res = false);
}
gNIDChipFirmwareSigningId = OBJ_create("1.3.6.1.4.1.37244.1.2", "ChipFirmwareSigningId", "ChipFirmwareSigningId");
if (gNIDChipFirmwareSigningId == 0)
{
ReportOpenSSLErrorAndExit("OBJ_create", res = false);
}
gNIDChipICAId = OBJ_create("1.3.6.1.4.1.37244.1.3", "ChipICAId", "ChipICAId");
if (gNIDChipICAId == 0)
{
ReportOpenSSLErrorAndExit("OBJ_create", res = false);
}
gNIDChipRootId = OBJ_create("1.3.6.1.4.1.37244.1.4", "ChipRootId", "ChipRootId");
if (gNIDChipICAId == 0)
{
ReportOpenSSLErrorAndExit("OBJ_create", res = false);
}
gNIDChipFabricId = OBJ_create("1.3.6.1.4.1.37244.1.5", "ChipFabricId", "ChipFabricId");
if (gNIDChipFabricId == 0)
{
ReportOpenSSLErrorAndExit("OBJ_create", res = false);
}
gNIDChipCASEAuthenticatedTag = OBJ_create("1.3.6.1.4.1.37244.1.6", "ChipCASEAuthenticatedTag", "ChipCASEAuthenticatedTag");
if (gNIDChipCASEAuthenticatedTag == 0)
{
ReportOpenSSLErrorAndExit("OBJ_create", res = false);
}
gNIDChipAttAttrVID = OBJ_create("1.3.6.1.4.1.37244.2.1", "ChipAttestationAttrVID", "ChipAttestationAttrVID");
if (gNIDChipAttAttrVID == 0)
{
ReportOpenSSLErrorAndExit("OBJ_create", res = false);
}
gNIDChipAttAttrPID = OBJ_create("1.3.6.1.4.1.37244.2.2", "ChipAttestationAttrPID", "ChipAttestationAttrPID");
if (gNIDChipAttAttrPID == 0)
{
ReportOpenSSLErrorAndExit("OBJ_create", res = false);
}
ASN1_STRING_TABLE_add(gNIDChipNodeId, 16, 16, B_ASN1_UTF8STRING, 0);
ASN1_STRING_TABLE_add(gNIDChipFirmwareSigningId, 16, 16, B_ASN1_UTF8STRING, 0);
ASN1_STRING_TABLE_add(gNIDChipICAId, 16, 16, B_ASN1_UTF8STRING, 0);
ASN1_STRING_TABLE_add(gNIDChipRootId, 16, 16, B_ASN1_UTF8STRING, 0);
ASN1_STRING_TABLE_add(gNIDChipFabricId, 16, 16, B_ASN1_UTF8STRING, 0);
ASN1_STRING_TABLE_add(gNIDChipCASEAuthenticatedTag, 8, 8, B_ASN1_UTF8STRING, 0);
ASN1_STRING_TABLE_add(gNIDChipAttAttrVID, 4, 4, B_ASN1_UTF8STRING, 0);
ASN1_STRING_TABLE_add(gNIDChipAttAttrPID, 4, 4, B_ASN1_UTF8STRING, 0);
exit:
return res;
}
bool IsChipCertFormat(CertFormat certFormat)
{
return ((certFormat == kCertFormat_Chip_Raw) || (certFormat == kCertFormat_Chip_Base64) ||
(certFormat == kCertFormat_Chip_Hex));
}
bool IsX509PrivateKeyFormat(KeyFormat keyFormat)
{
return ((keyFormat == kKeyFormat_X509_PEM) || (keyFormat == kKeyFormat_X509_DER) || (keyFormat == kKeyFormat_X509_Hex));
}
bool IsChipPrivateKeyFormat(KeyFormat keyFormat)
{
return ((keyFormat == kKeyFormat_Chip_Raw) || (keyFormat == kKeyFormat_Chip_Base64) || (keyFormat == kKeyFormat_Chip_Hex));
}
bool IsPrivateKeyFormat(KeyFormat keyFormat)
{
return (IsX509PrivateKeyFormat(keyFormat) || IsChipPrivateKeyFormat(keyFormat));
}
bool IsChipPublicKeyFormat(KeyFormat keyFormat)
{
return ((keyFormat == kKeyFormat_Chip_Pubkey_Raw) || (keyFormat == kKeyFormat_Chip_Pubkey_Base64) ||
(keyFormat == kKeyFormat_Chip_Pubkey_Hex));
}
bool IsPublicKeyFormat(KeyFormat keyFormat)
{
return (IsChipPublicKeyFormat(keyFormat) || (keyFormat == kKeyFormat_X509_Pubkey_PEM));
}
bool Base64Encode(const uint8_t * inData, uint32_t inDataLen, uint8_t * outBuf, uint32_t outBufSize, uint32_t & outDataLen)
{
bool res = true;
VerifyOrExit(outBuf != nullptr, res = false);
VerifyOrExit(outBufSize >= BASE64_ENCODED_LEN(inDataLen), res = false);
outDataLen = chip::Base64Encode32(inData, inDataLen, Uint8::to_char(outBuf));
exit:
return res;
}
bool Base64Decode(const uint8_t * inData, uint32_t inDataLen, uint8_t * outBuf, uint32_t outBufSize, uint32_t & outDataLen)
{
bool res = true;
VerifyOrExit(outBuf != nullptr, res = false);
VerifyOrExit(outBufSize >= BASE64_MAX_DECODED_LEN(inDataLen), res = false);
outDataLen = chip::Base64Decode32(Uint8::to_const_char(inData), inDataLen, outBuf);
VerifyOrExit(outDataLen != UINT32_MAX, res = false);
exit:
return res;
}
bool IsBase64String(const char * str, uint32_t strLen)
{
for (; strLen > 0; strLen--, str++)
{
if (!isalnum(*str) && *str != '+' && *str != '/' && *str != '=' && !isspace(*str))
{
return false;
}
}
return true;
}
bool ContainsPEMMarker(const char * marker, const uint8_t * data, uint32_t dataLen)
{
size_t markerLen = strlen(marker);
if (dataLen > markerLen)
{
for (uint32_t i = 0; i <= dataLen - markerLen; i++)
{
if (strncmp(reinterpret_cast<char *>(const_cast<uint8_t *>(data + i)), marker, markerLen) == 0)
{
return true;
}
}
}
return false;
}
bool ParseDateTime(const char * str, struct tm & date)
{
const char * p;
memset(&date, 0, sizeof(date));
if ((p = strptime(str, "%Y-%m-%d %H:%M:%S", &date)) == nullptr && (p = strptime(str, "%Y/%m/%d %H:%M:%S", &date)) == nullptr &&
(p = strptime(str, "%Y%m%d%H%M%SZ", &date)) == nullptr && (p = strptime(str, "%Y-%m-%d", &date)) == nullptr &&
(p = strptime(str, "%Y/%m/%d", &date)) == nullptr && (p = strptime(str, "%Y%m%d", &date)) == nullptr)
{
return false;
}
if (*p != 0)
{
return false;
}
return true;
}
bool OpenFile(const char * fileName, FILE *& file, bool toWrite)
{
VerifyOrReturnError(fileName != nullptr, false);
if (strcmp(fileName, "-") != 0)
{
file = fopen(fileName, toWrite ? "w+" : "r");
if (file == nullptr)
{
fprintf(stderr, "Unable to open %s: %s\n", fileName, strerror(errno));
return false;
}
}
else
{
file = toWrite ? stdout : stdin;
}
return true;
}
void CloseFile(FILE *& file)
{
if (file != nullptr)
{
fclose(file);
file = nullptr;
}
}
bool ReadFileIntoMem(const char * fileName, uint8_t * data, uint32_t & dataLen)
{
bool res = true;
FILE * file = nullptr;
long int fileLen;
size_t readRes;
res = OpenFile(fileName, file, false);
VerifyTrueOrExit(res);
fseek(file, 0, SEEK_END);
fileLen = ftell(file);
fseek(file, 0, SEEK_SET);
if (fileLen < 0 || ferror(file))
{
fprintf(stderr, "Error reading %s: %s\n", fileName, strerror(errno));
ExitNow(res = false);
}
VerifyOrExit(chip::CanCastTo<uint32_t>(fileLen), res = false);
dataLen = static_cast<uint32_t>(fileLen);
if (data != nullptr)
{
readRes = fread(data, 1, static_cast<size_t>(dataLen), file);
if (readRes < static_cast<size_t>(dataLen) || ferror(file))
{
fprintf(stderr, "Error reading %s: %s\n", fileName, strerror(errno));
ExitNow(res = false);
}
}
exit:
CloseFile(file);
return res;
}
bool WriteDataIntoFile(const char * fileName, const uint8_t * data, size_t dataLen, DataFormat dataFmt)
{
bool res = true;
FILE * file = nullptr;
const uint8_t * dataToWrite = nullptr;
uint32_t dataToWriteLen = 0;
std::unique_ptr<uint8_t[]> dataBuf;
VerifyOrExit(OpenFile(fileName, file, true) == true, res = false);
VerifyOrExit(data != nullptr, res = false);
VerifyOrExit(dataFmt != kDataFormat_Unknown, res = false);
if (dataFmt == kDataFormat_Base64)
{
VerifyOrExit(CanCastTo<uint32_t>(BASE64_ENCODED_LEN(dataLen)), res = false);
dataToWriteLen = static_cast<uint32_t>(BASE64_ENCODED_LEN(dataLen));
dataBuf = std::unique_ptr<uint8_t[]>(new uint8_t[dataToWriteLen]);
dataToWrite = dataBuf.get();
VerifyOrExit(Base64Encode(data, static_cast<uint32_t>(dataLen), dataBuf.get(), dataToWriteLen, dataToWriteLen),
res = false);
}
else if (dataFmt == kDataFormat_Hex)
{
VerifyOrExit(CanCastTo<uint32_t>(HEX_ENCODED_LENGTH(dataLen)), res = false);
dataToWriteLen = static_cast<uint32_t>(HEX_ENCODED_LENGTH(dataLen));
dataBuf = std::unique_ptr<uint8_t[]>(new uint8_t[dataToWriteLen]);
dataToWrite = dataBuf.get();
VerifyOrExit(BytesToHex(data, dataLen, Uint8::to_char(dataBuf.get()), dataToWriteLen, HexFlags::kUppercase) ==
CHIP_NO_ERROR,
res = false);
}
else
{
VerifyOrExit(CanCastTo<uint32_t>(dataLen), res = false);
dataToWriteLen = static_cast<uint32_t>(dataLen);
dataToWrite = data;
}
if (fwrite(dataToWrite, 1, dataToWriteLen, file) != dataToWriteLen)
{
fprintf(stderr, "Unable to write to %s: %s\n", fileName, strerror(ferror(file) ? errno : ENOSPC));
ExitNow(res = false);
}
// Add new line if the output is stdout
if ((strcmp(fileName, "-") == 0) && (fwrite("\n", 1, 1, file) != 1))
{
fprintf(stderr, "Unable to write to %s: %s\n", fileName, strerror(ferror(file) ? errno : ENOSPC));
ExitNow(res = false);
}
exit:
CloseFile(file);
return res;
}