foundationdb/flow/PKey.cpp

176 lines
5.3 KiB
C++

/*
* PKey.h
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
*
* 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.
*/
#include "flow/Error.h"
#include "flow/PKey.h"
#include "flow/ScopeExit.h"
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
namespace {
void traceAndThrowDecode(const char* type) {
auto te = TraceEvent(SevWarnAlways, type);
te.suppressFor(10);
if (auto err = ::ERR_get_error()) {
char buf[256]{
0,
};
::ERR_error_string_n(err, buf, sizeof(buf));
te.detail("OpenSSLError", static_cast<const char*>(buf));
}
throw pkey_decode_error();
}
void traceAndThrowEncode(const char* type) {
auto te = TraceEvent(SevWarnAlways, type);
te.suppressFor(10);
if (auto err = ::ERR_get_error()) {
char buf[256]{
0,
};
::ERR_error_string_n(err, buf, sizeof(buf));
te.detail("OpenSSLError", static_cast<const char*>(buf));
}
throw pkey_encode_error();
}
} // anonymous namespace
StringRef doWritePublicKeyPem(Arena& arena, EVP_PKEY* key) {
ASSERT(key);
auto mem = ::BIO_new(::BIO_s_mem());
if (!mem)
traceAndThrowEncode("PublicKeyPemWriteInitError");
auto memGuard = ScopeExit([mem]() { ::BIO_free(mem); });
if (1 != ::PEM_write_bio_PUBKEY(mem, key))
traceAndThrowEncode("PublicKeyPemWrite");
auto bioBuf = std::add_pointer_t<char>{};
auto const len = ::BIO_get_mem_data(mem, &bioBuf);
ASSERT_GT(len, 0);
auto buf = new (arena) uint8_t[len];
::memcpy(buf, bioBuf, len);
return StringRef(buf, static_cast<int>(len));
}
StringRef doWritePublicKeyDer(Arena& arena, EVP_PKEY* key) {
auto len = 0;
len = ::i2d_PUBKEY(key, nullptr);
ASSERT_LT(0, len);
auto buf = new (arena) uint8_t[len];
auto out = std::add_pointer_t<uint8_t>(buf);
len = ::i2d_PUBKEY(key, &out);
return StringRef(buf, len);
}
PublicKey::PublicKey(PemEncoded, StringRef pem) {
ASSERT(!pem.empty());
auto mem = ::BIO_new_mem_buf(pem.begin(), pem.size());
if (!mem)
traceAndThrowDecode("PemMemBioInitError");
auto bioGuard = ScopeExit([mem]() { ::BIO_free(mem); });
auto key = ::PEM_read_bio_PUBKEY(mem, nullptr, nullptr, nullptr);
if (!key)
traceAndThrowDecode("PemReadPublicKeyError");
ptr = std::shared_ptr<EVP_PKEY>(key, &::EVP_PKEY_free);
}
PublicKey::PublicKey(DerEncoded, StringRef der) {
ASSERT(!der.empty());
auto data = der.begin();
auto key = ::d2i_PUBKEY(nullptr, &data, der.size());
if (!key)
traceAndThrowDecode("DerReadPublicKeyError");
ptr = std::shared_ptr<EVP_PKEY>(key, &::EVP_PKEY_free);
}
StringRef PublicKey::writePem(Arena& arena) const {
return doWritePublicKeyPem(arena, nativeHandle());
}
StringRef PublicKey::writeDer(Arena& arena) const {
return doWritePublicKeyDer(arena, nativeHandle());
}
PrivateKey::PrivateKey(PemEncoded, StringRef pem) {
ASSERT(!pem.empty());
auto mem = ::BIO_new_mem_buf(pem.begin(), pem.size());
if (!mem)
traceAndThrowDecode("PrivateKeyDecodeInitError");
auto bioGuard = ScopeExit([mem]() { ::BIO_free(mem); });
auto key = ::PEM_read_bio_PrivateKey(mem, nullptr, nullptr, nullptr);
if (!key)
traceAndThrowDecode("PemReadPrivateKeyError");
ptr = std::shared_ptr<EVP_PKEY>(key, &::EVP_PKEY_free);
}
PrivateKey::PrivateKey(DerEncoded, StringRef der) {
ASSERT(!der.empty());
auto data = der.begin();
auto key = ::d2i_AutoPrivateKey(nullptr, &data, der.size());
if (!key)
traceAndThrowDecode("DerReadPrivateKeyError");
ptr = std::shared_ptr<EVP_PKEY>(key, &::EVP_PKEY_free);
}
PrivateKey::PrivateKey(std::shared_ptr<EVP_PKEY> key) : ptr(std::move(key)) {}
StringRef PrivateKey::writePem(Arena& arena) const {
ASSERT(ptr);
auto mem = ::BIO_new(::BIO_s_mem());
if (!mem)
traceAndThrowEncode("PrivateKeyPemWriteInitError");
auto memGuard = ScopeExit([mem]() { ::BIO_free(mem); });
if (1 != ::PEM_write_bio_PrivateKey(mem, nativeHandle(), nullptr, nullptr, 0, 0, nullptr))
traceAndThrowEncode("PrivateKeyDerPemWrite");
auto bioBuf = std::add_pointer_t<char>{};
auto const len = ::BIO_get_mem_data(mem, &bioBuf);
ASSERT_GT(len, 0);
auto buf = new (arena) uint8_t[len];
::memcpy(buf, bioBuf, len);
return StringRef(buf, static_cast<int>(len));
}
StringRef PrivateKey::writeDer(Arena& arena) const {
ASSERT(ptr);
auto len = 0;
len = ::i2d_PrivateKey(nativeHandle(), nullptr);
ASSERT_LT(0, len);
auto buf = new (arena) uint8_t[len];
auto out = std::add_pointer_t<uint8_t>(buf);
len = ::i2d_PrivateKey(nativeHandle(), &out);
return StringRef(buf, len);
}
StringRef PrivateKey::writePublicKeyPem(Arena& arena) const {
return doWritePublicKeyPem(arena, nativeHandle());
}
StringRef PrivateKey::writePublicKeyDer(Arena& arena) const {
return doWritePublicKeyDer(arena, nativeHandle());
}
PublicKey PrivateKey::toPublicKey() const {
auto arena = Arena();
return PublicKey(DerEncoded{}, writePublicKeyDer(arena));
}