/* * * 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 reading, parsing, * encoding, and decoding CHIP key material. * */ #include "chip-cert.h" #include #include #include #include using namespace chip; using namespace chip::Encoding; using namespace chip::Protocols; using namespace chip::ASN1; using namespace chip::TLV; using namespace chip::Crypto; using namespace chip::Encoding; namespace { KeyFormat DetectKeyFormat(const uint8_t * key, uint32_t keyLen) { static uint32_t p256SerializedKeypairLen = kP256_PublicKey_Length + kP256_PrivateKey_Length; static const uint8_t chipRawPrefix[] = { 0x04 }; static const char chipHexPrefix[] = "04"; static const char chipB64Prefix[] = "B"; static const uint8_t derRawPrefix[] = { 0x30, 0x77, 0x02, 0x01, 0x01, 0x04 }; static const char derHexPrefix[] = "307702010104"; static const char ecPEMMarker[] = "-----BEGIN EC PRIVATE KEY-----"; static const char pkcs8PEMMarker[] = "-----BEGIN PRIVATE KEY-----"; static const char ecPUBPEMMarker[] = "-----BEGIN PUBLIC KEY-----"; VerifyOrReturnError(key != nullptr, kKeyFormat_Unknown); if ((keyLen == p256SerializedKeypairLen) && (memcmp(key, chipRawPrefix, sizeof(chipRawPrefix)) == 0)) { return kKeyFormat_Chip_Raw; } if ((keyLen == HEX_ENCODED_LENGTH(p256SerializedKeypairLen)) && (memcmp(key, chipHexPrefix, strlen(chipHexPrefix)) == 0)) { return kKeyFormat_Chip_Hex; } if ((keyLen == BASE64_ENCODED_LEN(p256SerializedKeypairLen)) && (memcmp(key, chipB64Prefix, strlen(chipB64Prefix)) == 0)) { return kKeyFormat_Chip_Base64; } if ((keyLen == kP256_PublicKey_Length) && (memcmp(key, chipRawPrefix, sizeof(chipRawPrefix)) == 0)) { return kKeyFormat_Chip_Pubkey_Raw; } if ((keyLen == (2 * kP256_PublicKey_Length)) && (memcmp(key, chipHexPrefix, strlen(chipHexPrefix)) == 0)) { return kKeyFormat_Chip_Pubkey_Hex; } if ((keyLen == BASE64_ENCODED_LEN(kP256_PublicKey_Length)) && (memcmp(key, chipB64Prefix, strlen(chipB64Prefix)) == 0)) { return kKeyFormat_Chip_Pubkey_Base64; } if ((keyLen > sizeof(derRawPrefix)) && (memcmp(key, derRawPrefix, sizeof(derRawPrefix)) == 0)) { return kKeyFormat_X509_DER; } if ((keyLen > strlen(derHexPrefix)) && (memcmp(key, derHexPrefix, strlen(derHexPrefix)) == 0)) { return kKeyFormat_X509_Hex; } if (ContainsPEMMarker(ecPEMMarker, key, keyLen) || ContainsPEMMarker(pkcs8PEMMarker, key, keyLen)) { return kKeyFormat_X509_PEM; } if (ContainsPEMMarker(ecPUBPEMMarker, key, keyLen)) { return kKeyFormat_X509_Pubkey_PEM; } return kKeyFormat_Unknown; } bool SetPublicKey(const uint8_t * pubkey, uint32_t pubkeyLen, EVP_PKEY * key) { std::unique_ptr group(EC_GROUP_new_by_curve_name(gNIDChipCurveP256), &EC_GROUP_free); std::unique_ptr ecPoint(EC_POINT_new(group.get()), &EC_POINT_free); std::unique_ptr ecKey(EC_KEY_new_by_curve_name(gNIDChipCurveP256), &EC_KEY_free); VerifyOrReturnError(EC_POINT_oct2point(group.get(), ecPoint.get(), pubkey, pubkeyLen, nullptr) == 1, false); VerifyOrReturnError(EC_KEY_set_public_key(ecKey.get(), ecPoint.get()) == 1, false); VerifyOrReturnError(EVP_PKEY_set1_EC_KEY(key, ecKey.get()) == 1, false); return true; } bool ExtractPublicKey(EVP_PKEY * key, MutableByteSpan & pubKey) { const EC_KEY * ecKey = nullptr; const EC_GROUP * group = nullptr; const EC_POINT * ecPoint = nullptr; VerifyOrReturnError(pubKey.size() >= kP256_PublicKey_Length, false); ecKey = EVP_PKEY_get1_EC_KEY(key); VerifyOrReturnError(ecKey != nullptr, false); group = EC_KEY_get0_group(ecKey); VerifyOrReturnError(group != nullptr, false); ecPoint = EC_KEY_get0_public_key(ecKey); VerifyOrReturnError(ecPoint != nullptr, false); VerifyOrReturnError(EC_POINT_point2oct(group, ecPoint, POINT_CONVERSION_UNCOMPRESSED, Uint8::to_uchar(pubKey.data()), kP256_PublicKey_Length, nullptr) == kP256_PublicKey_Length, false); pubKey.reduce_size(static_cast(kP256_PublicKey_Length)); return true; } bool DeserializeKeyPair(const uint8_t * keyPair, uint32_t keyPairLen, EVP_PKEY * key) { std::unique_ptr privKeyBN(BN_new(), &BN_clear_free); ERR_clear_error(); VerifyOrReturnError(key != nullptr, false); VerifyOrReturnError(keyPair != nullptr, false); VerifyOrReturnError(keyPairLen == kP256_PublicKey_Length + kP256_PrivateKey_Length, false); VerifyOrReturnError(SetPublicKey(keyPair, kP256_PublicKey_Length, key), false); VerifyOrReturnError(BN_bin2bn(keyPair + kP256_PublicKey_Length, kP256_PrivateKey_Length, privKeyBN.get()) != nullptr, false); VerifyOrReturnError(EC_KEY_set_private_key(EVP_PKEY_get1_EC_KEY(key), privKeyBN.get()) == 1, false); return true; } } // namespace bool SerializeKeyPair(EVP_PKEY * key, P256SerializedKeypair & serializedKeypair) { const EC_KEY * ecKey = nullptr; const BIGNUM * privKeyBN = nullptr; MutableByteSpan pubKey(serializedKeypair.Bytes(), kP256_PublicKey_Length); VerifyOrReturnError(ExtractPublicKey(key, pubKey), false); ecKey = EVP_PKEY_get1_EC_KEY(key); VerifyOrReturnError(ecKey != nullptr, false); privKeyBN = EC_KEY_get0_private_key(ecKey); VerifyOrReturnError(privKeyBN != nullptr, false); VerifyOrReturnError(BN_bn2binpad(privKeyBN, serializedKeypair.Bytes() + kP256_PublicKey_Length, kP256_PrivateKey_Length) == kP256_PrivateKey_Length, false); serializedKeypair.SetLength(kP256_PublicKey_Length + kP256_PrivateKey_Length); return true; } bool ReadKey(const char * fileNameOrStr, std::unique_ptr & key, bool ignorErrorIfUnsupportedCurve) { bool res = true; uint32_t keyDataLen = 0; KeyFormat keyFormat = kKeyFormat_Unknown; std::unique_ptr keyData; // If fileNameOrStr is a file name if (access(fileNameOrStr, R_OK) == 0) { res = ReadFileIntoMem(fileNameOrStr, nullptr, keyDataLen); VerifyTrueOrExit(res); keyData = std::unique_ptr(new uint8_t[keyDataLen]); res = ReadFileIntoMem(fileNameOrStr, keyData.get(), keyDataLen); VerifyTrueOrExit(res); keyFormat = DetectKeyFormat(keyData.get(), keyDataLen); if (keyFormat == kKeyFormat_Unknown) { fprintf(stderr, "Unrecognized Key Format in File: %s\n", fileNameOrStr); return false; } } // Otherwise, treat fileNameOrStr as a pointer to the key string else { keyDataLen = static_cast(strlen(fileNameOrStr)); keyFormat = DetectKeyFormat(reinterpret_cast(fileNameOrStr), keyDataLen); if (keyFormat == kKeyFormat_Unknown) { fprintf(stderr, "Unrecognized Key Format in Input Argument: %s\n", fileNameOrStr); return false; } keyData = std::unique_ptr(new uint8_t[keyDataLen]); memcpy(keyData.get(), fileNameOrStr, keyDataLen); } if ((keyFormat == kKeyFormat_X509_Hex) || (keyFormat == kKeyFormat_Chip_Hex) || (keyFormat == kKeyFormat_Chip_Pubkey_Hex)) { size_t len = HexToBytes(Uint8::to_char(keyData.get()), keyDataLen, keyData.get(), keyDataLen); VerifyOrReturnError(CanCastTo(2 * len), false); VerifyOrReturnError(2 * len == keyDataLen, false); keyDataLen = static_cast(len); } else if ((keyFormat == kKeyFormat_Chip_Base64) || (keyFormat == kKeyFormat_Chip_Pubkey_Base64)) { res = Base64Decode(keyData.get(), keyDataLen, keyData.get(), keyDataLen, keyDataLen); VerifyTrueOrExit(res); } 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 (IsChipPrivateKeyFormat(keyFormat)) { res = DeserializeKeyPair(keyData.get(), keyDataLen, key.get()); VerifyTrueOrExit(res); } else if (IsChipPublicKeyFormat(keyFormat)) { res = SetPublicKey(keyData.get(), keyDataLen, key.get()); VerifyTrueOrExit(res); } else { std::unique_ptr keyBIO( BIO_new_mem_buf(static_cast(keyData.get()), static_cast(keyDataLen)), &BIO_free_all); if (keyFormat == kKeyFormat_X509_Pubkey_PEM) { EC_KEY * ecKey = PEM_read_bio_EC_PUBKEY(keyBIO.get(), nullptr, nullptr, nullptr); if (ecKey == nullptr) { ReportOpenSSLErrorAndExit("PEM_read_bio_EC_PUBKEY", res = false); } if (EVP_PKEY_set1_EC_KEY(key.get(), ecKey) != 1) { ReportOpenSSLErrorAndExit("EVP_PKEY_set1_EC_KEY", res = false); } } else if (keyFormat == kKeyFormat_X509_PEM) { key.reset(PEM_read_bio_PrivateKey(keyBIO.get(), nullptr, nullptr, nullptr)); if (key.get() == nullptr) { ReportOpenSSLErrorAndExit("PEM_read_bio_PrivateKey", res = false); } } else { key.reset(d2i_PrivateKey_bio(keyBIO.get(), nullptr)); if (key.get() == nullptr) { ReportOpenSSLErrorAndExit("d2i_PrivateKey_bio", res = false); } } } if ((EC_GROUP_get_curve_name(EC_KEY_get0_group(EVP_PKEY_get1_EC_KEY(key.get()))) != gNIDChipCurveP256) && !ignorErrorIfUnsupportedCurve) { fprintf(stderr, "Specified key uses unsupported Elliptic Curve\n"); ExitNow(res = false); } exit: return res; } bool GenerateKeyPair(EVP_PKEY * key) { bool res = true; std::unique_ptr ecKey(EC_KEY_new_by_curve_name(gNIDChipCurveP256), &EC_KEY_free); VerifyOrExit(key != nullptr, res = false); if (!EC_KEY_generate_key(ecKey.get())) { ReportOpenSSLErrorAndExit("EC_KEY_generate_key", res = false); } if (!EVP_PKEY_set1_EC_KEY(key, ecKey.get())) { ReportOpenSSLErrorAndExit("EVP_PKEY_set1_EC_KEY", res = false); } exit: return res; } bool GenerateKeyPair_Secp256k1(EVP_PKEY * key) { bool res = true; std::unique_ptr ecKey(EC_KEY_new_by_curve_name(NID_secp256k1), &EC_KEY_free); VerifyOrExit(key != nullptr, res = false); if (!EC_KEY_generate_key(ecKey.get())) { ReportOpenSSLErrorAndExit("EC_KEY_generate_key", res = false); } if (!EVP_PKEY_set1_EC_KEY(key, ecKey.get())) { ReportOpenSSLErrorAndExit("EVP_PKEY_set1_EC_KEY", res = false); } exit: return res; } bool WriteKey(const char * fileName, EVP_PKEY * key, KeyFormat keyFmt) { bool res = true; FILE * file = nullptr; uint8_t * derKey = nullptr; DataFormat dataFormat = kDataFormat_Unknown; VerifyOrExit(key != nullptr, res = false); if (keyFmt == kKeyFormat_X509_PEM || keyFmt == kKeyFormat_X509_Pubkey_PEM || keyFmt == kKeyFormat_X509_DER) { VerifyOrExit(OpenFile(fileName, file, true), res = false); } if (EVP_PKEY_type(EVP_PKEY_id(key)) != EVP_PKEY_EC) { fprintf(stderr, "Unsupported private key type\n"); ExitNow(res = false); } switch (keyFmt) { case kKeyFormat_X509_PEM: if (PEM_write_ECPrivateKey(file, EVP_PKEY_get1_EC_KEY(key), nullptr, nullptr, 0, nullptr, nullptr) == 0) { ReportOpenSSLErrorAndExit("PEM_write_ECPrivateKey", res = false); } break; case kKeyFormat_X509_Pubkey_PEM: if (PEM_write_EC_PUBKEY(file, EVP_PKEY_get1_EC_KEY(key)) == 0) { ReportOpenSSLErrorAndExit("PEM_write_EC_PUBKEY", res = false); } break; case kKeyFormat_X509_DER: if (i2d_ECPrivateKey_fp(file, EVP_PKEY_get1_EC_KEY(key)) == 0) { ReportOpenSSLErrorAndExit("i2d_PrivateKey_fp", res = false); } break; case kKeyFormat_X509_Hex: { int derKeyLen = i2d_ECPrivateKey(EVP_PKEY_get1_EC_KEY(key), &derKey); if (derKeyLen < 0) { ReportOpenSSLErrorAndExit("i2d_X509", res = false); } VerifyOrExit(CanCastTo(derKeyLen), res = false); VerifyOrExit(WriteDataIntoFile(fileName, derKey, static_cast(derKeyLen), kDataFormat_Hex), res = false); } break; case kKeyFormat_Chip_Raw: case kKeyFormat_Chip_Hex: case kKeyFormat_Chip_Base64: if (keyFmt == kKeyFormat_Chip_Raw) dataFormat = kDataFormat_Raw; else if (keyFmt == kKeyFormat_Chip_Base64) dataFormat = kDataFormat_Base64; else dataFormat = kDataFormat_Hex; { P256SerializedKeypair serializedKeypair; VerifyOrExit(SerializeKeyPair(key, serializedKeypair), res = false); VerifyOrExit(WriteDataIntoFile(fileName, serializedKeypair.Bytes(), serializedKeypair.Length(), dataFormat), res = false); } break; case kKeyFormat_Chip_Pubkey_Raw: case kKeyFormat_Chip_Pubkey_Base64: case kKeyFormat_Chip_Pubkey_Hex: if (keyFmt == kKeyFormat_Chip_Pubkey_Raw) dataFormat = kDataFormat_Raw; else if (keyFmt == kKeyFormat_Chip_Pubkey_Base64) dataFormat = kDataFormat_Base64; else dataFormat = kDataFormat_Hex; { uint8_t pubkey[kP256_PublicKey_Length] = { 0 }; MutableByteSpan pubkeySpan(pubkey); VerifyOrExit(ExtractPublicKey(key, pubkeySpan), res = false); VerifyOrExit(WriteDataIntoFile(fileName, pubkeySpan.data(), pubkeySpan.size(), dataFormat), res = false); } break; default: fprintf(stderr, "Unsupported private key format\n"); ExitNow(res = false); } exit: CloseFile(file); OPENSSL_free(derKey); return res; }