blob: a52359c0f5706cab3db2cd1149ff7abe7928653c [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 reading, parsing,
* encoding, and decoding CHIP key material.
*
*/
#include "chip-cert.h"
#include <lib/core/CHIPEncoding.h>
#include <lib/support/BufferWriter.h>
#include <lib/support/BytesToHex.h>
#include <protocols/Protocols.h>
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<EC_GROUP, void (*)(EC_GROUP *)> group(EC_GROUP_new_by_curve_name(gNIDChipCurveP256), &EC_GROUP_free);
std::unique_ptr<EC_POINT, void (*)(EC_POINT *)> ecPoint(EC_POINT_new(group.get()), &EC_POINT_free);
std::unique_ptr<EC_KEY, void (*)(EC_KEY *)> 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<size_t>(kP256_PublicKey_Length));
return true;
}
bool DeserializeKeyPair(const uint8_t * keyPair, uint32_t keyPairLen, EVP_PKEY * key)
{
std::unique_ptr<BIGNUM, void (*)(BIGNUM *)> 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<EVP_PKEY, void (*)(EVP_PKEY *)> & key, bool ignorErrorIfUnsupportedCurve)
{
bool res = true;
uint32_t keyDataLen = 0;
KeyFormat keyFormat = kKeyFormat_Unknown;
std::unique_ptr<uint8_t[]> keyData;
// If fileNameOrStr is a file name
if (access(fileNameOrStr, R_OK) == 0)
{
res = ReadFileIntoMem(fileNameOrStr, nullptr, keyDataLen);
VerifyTrueOrExit(res);
keyData = std::unique_ptr<uint8_t[]>(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<uint32_t>(strlen(fileNameOrStr));
keyFormat = DetectKeyFormat(reinterpret_cast<const uint8_t *>(fileNameOrStr), keyDataLen);
if (keyFormat == kKeyFormat_Unknown)
{
fprintf(stderr, "Unrecognized Key Format in Input Argument: %s\n", fileNameOrStr);
return false;
}
keyData = std::unique_ptr<uint8_t[]>(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<uint32_t>(2 * len), false);
VerifyOrReturnError(2 * len == keyDataLen, false);
keyDataLen = static_cast<uint32_t>(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<const char *>(keyData.get());
keyDataLen = static_cast<uint32_t>(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<BIO, void (*)(BIO *)> keyBIO(
BIO_new_mem_buf(static_cast<const void *>(keyData.get()), static_cast<int>(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<EC_KEY, void (*)(EC_KEY *)> 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<EC_KEY, void (*)(EC_KEY *)> 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<size_t>(derKeyLen), res = false);
VerifyOrExit(WriteDataIntoFile(fileName, derKey, static_cast<size_t>(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;
}