/* * * 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 #include #include #include 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(const_cast(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(fileLen), res = false); dataLen = static_cast(fileLen); if (data != nullptr) { readRes = fread(data, 1, static_cast(dataLen), file); if (readRes < static_cast(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 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(BASE64_ENCODED_LEN(dataLen)), res = false); dataToWriteLen = static_cast(BASE64_ENCODED_LEN(dataLen)); dataBuf = std::unique_ptr(new uint8_t[dataToWriteLen]); dataToWrite = dataBuf.get(); VerifyOrExit(Base64Encode(data, static_cast(dataLen), dataBuf.get(), dataToWriteLen, dataToWriteLen), res = false); } else if (dataFmt == kDataFormat_Hex) { VerifyOrExit(CanCastTo(HEX_ENCODED_LENGTH(dataLen)), res = false); dataToWriteLen = static_cast(HEX_ENCODED_LENGTH(dataLen)); dataBuf = std::unique_ptr(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(dataLen), res = false); dataToWriteLen = static_cast(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 to stdout in a text format if ((strcmp(fileName, "-") == 0) && (dataFmt != kDataFormat_Raw) && (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; }