src: move more key related stuff to ncrypto
PR-URL: https://github.com/nodejs/node/pull/55368 Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
This commit is contained in:
parent
91bce94010
commit
5b9bf39b47
252
deps/ncrypto/ncrypto.cc
vendored
252
deps/ncrypto/ncrypto.cc
vendored
@ -80,7 +80,7 @@ std::optional<std::string> CryptoErrorList::pop_front() {
|
|||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
DataPointer DataPointer::Alloc(size_t len) {
|
DataPointer DataPointer::Alloc(size_t len) {
|
||||||
return DataPointer(OPENSSL_malloc(len), len);
|
return DataPointer(OPENSSL_zalloc(len), len);
|
||||||
}
|
}
|
||||||
|
|
||||||
DataPointer::DataPointer(void* data, size_t length)
|
DataPointer::DataPointer(void* data, size_t length)
|
||||||
@ -1428,6 +1428,33 @@ DataPointer pbkdf2(const EVP_MD* md,
|
|||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
|
EVPKeyPointer::PrivateKeyEncodingConfig::PrivateKeyEncodingConfig(
|
||||||
|
const PrivateKeyEncodingConfig& other)
|
||||||
|
: PrivateKeyEncodingConfig(other.output_key_object, other.format, other.type) {
|
||||||
|
cipher = other.cipher;
|
||||||
|
if (other.passphrase.has_value()) {
|
||||||
|
auto& otherPassphrase = other.passphrase.value();
|
||||||
|
auto newPassphrase = DataPointer::Alloc(otherPassphrase.size());
|
||||||
|
memcpy(newPassphrase.get(), otherPassphrase.get(), otherPassphrase.size());
|
||||||
|
passphrase = std::move(newPassphrase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EVPKeyPointer::AsymmetricKeyEncodingConfig::AsymmetricKeyEncodingConfig(
|
||||||
|
bool output_key_object,
|
||||||
|
PKFormatType format,
|
||||||
|
PKEncodingType type)
|
||||||
|
: output_key_object(output_key_object),
|
||||||
|
format(format),
|
||||||
|
type(type) {}
|
||||||
|
|
||||||
|
EVPKeyPointer::PrivateKeyEncodingConfig& EVPKeyPointer::PrivateKeyEncodingConfig::operator=(
|
||||||
|
const PrivateKeyEncodingConfig& other) {
|
||||||
|
if (this == &other) return *this;
|
||||||
|
this->~PrivateKeyEncodingConfig();
|
||||||
|
return *new (this) PrivateKeyEncodingConfig(other);
|
||||||
|
}
|
||||||
|
|
||||||
EVPKeyPointer EVPKeyPointer::New() {
|
EVPKeyPointer EVPKeyPointer::New() {
|
||||||
return EVPKeyPointer(EVP_PKEY_new());
|
return EVPKeyPointer(EVP_PKEY_new());
|
||||||
}
|
}
|
||||||
@ -1661,14 +1688,13 @@ EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePublicKeyPEM(
|
|||||||
}
|
}
|
||||||
|
|
||||||
EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePublicKey(
|
EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePublicKey(
|
||||||
PKFormatType format,
|
const PublicKeyEncodingConfig& config,
|
||||||
PKEncodingType encoding,
|
|
||||||
const Buffer<const unsigned char>& buffer) {
|
const Buffer<const unsigned char>& buffer) {
|
||||||
if (format == PKFormatType::PEM) {
|
if (config.format == PKFormatType::PEM) {
|
||||||
return TryParsePublicKeyPEM(buffer);
|
return TryParsePublicKeyPEM(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (format != PKFormatType::DER) {
|
if (config.format != PKFormatType::DER) {
|
||||||
return ParseKeyResult(PKParseError::FAILED);
|
return ParseKeyResult(PKParseError::FAILED);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1676,12 +1702,12 @@ EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePublicKey(
|
|||||||
|
|
||||||
EVP_PKEY* key = nullptr;
|
EVP_PKEY* key = nullptr;
|
||||||
|
|
||||||
if (encoding == PKEncodingType::PKCS1 &&
|
if (config.type == PKEncodingType::PKCS1 &&
|
||||||
(key = d2i_PublicKey(EVP_PKEY_RSA, nullptr, &start, buffer.len))) {
|
(key = d2i_PublicKey(EVP_PKEY_RSA, nullptr, &start, buffer.len))) {
|
||||||
return EVPKeyPointer::ParseKeyResult(EVPKeyPointer(key));
|
return EVPKeyPointer::ParseKeyResult(EVPKeyPointer(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (encoding == PKEncodingType::SPKI &&
|
if (config.type == PKEncodingType::SPKI &&
|
||||||
(key = d2i_PUBKEY(nullptr, &start, buffer.len))) {
|
(key = d2i_PUBKEY(nullptr, &start, buffer.len))) {
|
||||||
return EVPKeyPointer::ParseKeyResult(EVPKeyPointer(key));
|
return EVPKeyPointer::ParseKeyResult(EVPKeyPointer(key));
|
||||||
}
|
}
|
||||||
@ -1689,13 +1715,34 @@ EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePublicKey(
|
|||||||
return ParseKeyResult(PKParseError::FAILED);
|
return ParseKeyResult(PKParseError::FAILED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
Buffer<char> GetPassphrase(const EVPKeyPointer::PrivateKeyEncodingConfig& config) {
|
||||||
|
Buffer<char> pass {
|
||||||
|
// OpenSSL will not actually dereference this pointer, so it can be any
|
||||||
|
// non-null pointer. We cannot assert that directly, which is why we
|
||||||
|
// intentionally use a pointer that will likely cause a segmentation fault
|
||||||
|
// when dereferenced.
|
||||||
|
.data = reinterpret_cast<char*>(-1),
|
||||||
|
.len = 0,
|
||||||
|
};
|
||||||
|
if (config.passphrase.has_value()) {
|
||||||
|
auto& passphrase = config.passphrase.value();
|
||||||
|
// The pass.data can't be a nullptr, even if the len is zero or else
|
||||||
|
// openssl will prompt for a password and we really don't want that.
|
||||||
|
if (passphrase.get() != nullptr) {
|
||||||
|
pass.data = static_cast<char*>(passphrase.get());
|
||||||
|
}
|
||||||
|
pass.len = passphrase.size();
|
||||||
|
}
|
||||||
|
return pass;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePrivateKey(
|
EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePrivateKey(
|
||||||
PKFormatType format,
|
const PrivateKeyEncodingConfig& config,
|
||||||
PKEncodingType encoding,
|
|
||||||
std::optional<Buffer<char>> maybe_passphrase,
|
|
||||||
const Buffer<const unsigned char>& buffer) {
|
const Buffer<const unsigned char>& buffer) {
|
||||||
|
|
||||||
static auto keyOrError = [&](EVPKeyPointer pkey, bool had_passphrase = false) {
|
static constexpr auto keyOrError = [](EVPKeyPointer pkey, bool had_passphrase = false) {
|
||||||
if (int err = ERR_peek_error()) {
|
if (int err = ERR_peek_error()) {
|
||||||
if (ERR_GET_LIB(err) == ERR_LIB_PEM &&
|
if (ERR_GET_LIB(err) == ERR_LIB_PEM &&
|
||||||
ERR_GET_REASON(err) == PEM_R_BAD_PASSWORD_READ &&
|
ERR_GET_REASON(err) == PEM_R_BAD_PASSWORD_READ &&
|
||||||
@ -1708,24 +1755,23 @@ EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePrivateKey(
|
|||||||
return ParseKeyResult(std::move(pkey));
|
return ParseKeyResult(std::move(pkey));
|
||||||
};
|
};
|
||||||
|
|
||||||
Buffer<char>* passphrase = nullptr;
|
|
||||||
if (maybe_passphrase.has_value()) {
|
|
||||||
passphrase = &maybe_passphrase.value();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto bio = BIOPointer::New(buffer);
|
auto bio = BIOPointer::New(buffer);
|
||||||
if (!bio) return ParseKeyResult(PKParseError::FAILED);
|
if (!bio) return ParseKeyResult(PKParseError::FAILED);
|
||||||
|
|
||||||
if (format == PKFormatType::PEM) {
|
auto passphrase = GetPassphrase(config);
|
||||||
auto key = PEM_read_bio_PrivateKey(bio.get(), nullptr, PasswordCallback, passphrase);
|
|
||||||
return keyOrError(EVPKeyPointer(key), maybe_passphrase.has_value());
|
if (config.format == PKFormatType::PEM) {
|
||||||
|
auto key = PEM_read_bio_PrivateKey(bio.get(), nullptr, PasswordCallback,
|
||||||
|
config.passphrase.has_value() ? &passphrase : nullptr);
|
||||||
|
return keyOrError(EVPKeyPointer(key), config.passphrase.has_value());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (format != PKFormatType::DER) {
|
if (config.format != PKFormatType::DER) {
|
||||||
return ParseKeyResult(PKParseError::FAILED);
|
return ParseKeyResult(PKParseError::FAILED);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (encoding) {
|
switch (config.type) {
|
||||||
case PKEncodingType::PKCS1: {
|
case PKEncodingType::PKCS1: {
|
||||||
auto key = d2i_PrivateKey_bio(bio.get(), nullptr);
|
auto key = d2i_PrivateKey_bio(bio.get(), nullptr);
|
||||||
return keyOrError(EVPKeyPointer(key));
|
return keyOrError(EVPKeyPointer(key));
|
||||||
@ -1735,8 +1781,8 @@ EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePrivateKey(
|
|||||||
auto key = d2i_PKCS8PrivateKey_bio(bio.get(),
|
auto key = d2i_PKCS8PrivateKey_bio(bio.get(),
|
||||||
nullptr,
|
nullptr,
|
||||||
PasswordCallback,
|
PasswordCallback,
|
||||||
passphrase);
|
config.passphrase.has_value() ? &passphrase : nullptr);
|
||||||
return keyOrError(EVPKeyPointer(key), maybe_passphrase.has_value());
|
return keyOrError(EVPKeyPointer(key), config.passphrase.has_value());
|
||||||
}
|
}
|
||||||
|
|
||||||
PKCS8Pointer p8inf(d2i_PKCS8_PRIV_KEY_INFO_bio(bio.get(), nullptr));
|
PKCS8Pointer p8inf(d2i_PKCS8_PRIV_KEY_INFO_bio(bio.get(), nullptr));
|
||||||
@ -1755,4 +1801,166 @@ EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePrivateKey(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result<BIOPointer, bool> EVPKeyPointer::writePrivateKey(
|
||||||
|
const PrivateKeyEncodingConfig& config) const {
|
||||||
|
if (config.format == PKFormatType::JWK) {
|
||||||
|
return Result<BIOPointer, bool>(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto bio = BIOPointer::NewMem();
|
||||||
|
if (!bio) {
|
||||||
|
return Result<BIOPointer, bool>(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto passphrase = GetPassphrase(config);
|
||||||
|
MarkPopErrorOnReturn mark_pop_error_on_return;
|
||||||
|
bool err;
|
||||||
|
|
||||||
|
switch (config.type) {
|
||||||
|
case PKEncodingType::PKCS1: {
|
||||||
|
// PKCS1 is only permitted for RSA keys.
|
||||||
|
if (id() != EVP_PKEY_RSA) return Result<BIOPointer, bool>(false);
|
||||||
|
|
||||||
|
#if OPENSSL_VERSION_MAJOR >= 3
|
||||||
|
const RSA* rsa = EVP_PKEY_get0_RSA(get());
|
||||||
|
#else
|
||||||
|
RSA* rsa = EVP_PKEY_get0_RSA(get());
|
||||||
|
#endif
|
||||||
|
switch (config.format) {
|
||||||
|
case PKFormatType::PEM: {
|
||||||
|
err = PEM_write_bio_RSAPrivateKey(bio.get(), rsa, config.cipher,
|
||||||
|
reinterpret_cast<unsigned char*>(passphrase.data),
|
||||||
|
passphrase.len, nullptr, nullptr) != 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PKFormatType::DER: {
|
||||||
|
// Encoding PKCS1 as DER. This variation does not permit encryption.
|
||||||
|
err = i2d_RSAPrivateKey_bio(bio.get(), rsa) != 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
// Should never get here.
|
||||||
|
return Result<BIOPointer, bool>(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PKEncodingType::PKCS8: {
|
||||||
|
switch (config.format) {
|
||||||
|
case PKFormatType::PEM: {
|
||||||
|
// Encode PKCS#8 as PEM.
|
||||||
|
err = PEM_write_bio_PKCS8PrivateKey(
|
||||||
|
bio.get(), get(),
|
||||||
|
config.cipher,
|
||||||
|
passphrase.data,
|
||||||
|
passphrase.len,
|
||||||
|
nullptr, nullptr) != 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PKFormatType::DER: {
|
||||||
|
err = i2d_PKCS8PrivateKey_bio(
|
||||||
|
bio.get(), get(),
|
||||||
|
config.cipher,
|
||||||
|
passphrase.data,
|
||||||
|
passphrase.len,
|
||||||
|
nullptr, nullptr) != 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
// Should never get here.
|
||||||
|
return Result<BIOPointer, bool>(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PKEncodingType::SEC1: {
|
||||||
|
// SEC1 is only permitted for EC keys
|
||||||
|
if (id() != EVP_PKEY_EC) return Result<BIOPointer, bool>(false);
|
||||||
|
|
||||||
|
#if OPENSSL_VERSION_MAJOR >= 3
|
||||||
|
const EC_KEY* ec = EVP_PKEY_get0_EC_KEY(get());
|
||||||
|
#else
|
||||||
|
EC_KEY* ec = EVP_PKEY_get0_EC_KEY(get());
|
||||||
|
#endif
|
||||||
|
switch (config.format) {
|
||||||
|
case PKFormatType::PEM: {
|
||||||
|
err = PEM_write_bio_ECPrivateKey(bio.get(),
|
||||||
|
ec,
|
||||||
|
config.cipher,
|
||||||
|
reinterpret_cast<unsigned char*>(passphrase.data),
|
||||||
|
passphrase.len,
|
||||||
|
nullptr,
|
||||||
|
nullptr) != 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PKFormatType::DER: {
|
||||||
|
// Encoding SEC1 as DER. This variation does not permit encryption.
|
||||||
|
err = i2d_ECPrivateKey_bio(bio.get(), ec) != 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
// Should never get here.
|
||||||
|
return Result<BIOPointer, bool>(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
// Not a valid private key encoding
|
||||||
|
return Result<BIOPointer, bool>(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
// Failed to encode the private key.
|
||||||
|
return Result<BIOPointer, bool>(false, mark_pop_error_on_return.peekError());
|
||||||
|
}
|
||||||
|
|
||||||
|
return bio;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<BIOPointer, bool> EVPKeyPointer::writePublicKey(
|
||||||
|
const ncrypto::EVPKeyPointer::PublicKeyEncodingConfig& config) const {
|
||||||
|
auto bio = BIOPointer::NewMem();
|
||||||
|
if (!bio) return Result<BIOPointer, bool>(false);
|
||||||
|
|
||||||
|
MarkPopErrorOnReturn mark_pop_error_on_return;
|
||||||
|
|
||||||
|
if (config.type == ncrypto::EVPKeyPointer::PKEncodingType::PKCS1) {
|
||||||
|
// PKCS#1 is only valid for RSA keys.
|
||||||
|
#if OPENSSL_VERSION_MAJOR >= 3
|
||||||
|
const RSA* rsa = EVP_PKEY_get0_RSA(get());
|
||||||
|
#else
|
||||||
|
RSA* rsa = EVP_PKEY_get0_RSA(get());
|
||||||
|
#endif
|
||||||
|
if (config.format == ncrypto::EVPKeyPointer::PKFormatType::PEM) {
|
||||||
|
// Encode PKCS#1 as PEM.
|
||||||
|
if (PEM_write_bio_RSAPublicKey(bio.get(), rsa) != 1) {
|
||||||
|
return Result<BIOPointer, bool>(false, mark_pop_error_on_return.peekError());
|
||||||
|
}
|
||||||
|
return bio;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode PKCS#1 as DER.
|
||||||
|
if (i2d_RSAPublicKey_bio(bio.get(), rsa) != 1) {
|
||||||
|
return Result<BIOPointer, bool>(false, mark_pop_error_on_return.peekError());
|
||||||
|
}
|
||||||
|
return bio;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.format == ncrypto::EVPKeyPointer::PKFormatType::PEM) {
|
||||||
|
// Encode SPKI as PEM.
|
||||||
|
if (PEM_write_bio_PUBKEY(bio.get(), get()) != 1) {
|
||||||
|
return Result<BIOPointer, bool>(false, mark_pop_error_on_return.peekError());
|
||||||
|
}
|
||||||
|
return bio;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode SPKI as DER.
|
||||||
|
if (i2d_PUBKEY_bio(bio.get(), get()) != 1) {
|
||||||
|
return Result<BIOPointer, bool>(false, mark_pop_error_on_return.peekError());
|
||||||
|
}
|
||||||
|
return bio;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ncrypto
|
} // namespace ncrypto
|
||||||
|
36
deps/ncrypto/ncrypto.h
vendored
36
deps/ncrypto/ncrypto.h
vendored
@ -386,13 +386,13 @@ public:
|
|||||||
// SubjectPublicKeyInfo according to X.509.
|
// SubjectPublicKeyInfo according to X.509.
|
||||||
SPKI,
|
SPKI,
|
||||||
// ECPrivateKey according to SEC1.
|
// ECPrivateKey according to SEC1.
|
||||||
SEC1
|
SEC1,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class PKFormatType {
|
enum class PKFormatType {
|
||||||
DER,
|
DER,
|
||||||
PEM,
|
PEM,
|
||||||
JWK
|
JWK,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class PKParseError {
|
enum class PKParseError {
|
||||||
@ -402,18 +402,36 @@ public:
|
|||||||
};
|
};
|
||||||
using ParseKeyResult = Result<EVPKeyPointer, PKParseError>;
|
using ParseKeyResult = Result<EVPKeyPointer, PKParseError>;
|
||||||
|
|
||||||
|
struct AsymmetricKeyEncodingConfig {
|
||||||
|
bool output_key_object = false;
|
||||||
|
PKFormatType format = PKFormatType::DER;
|
||||||
|
PKEncodingType type = PKEncodingType::PKCS8;
|
||||||
|
AsymmetricKeyEncodingConfig() = default;
|
||||||
|
AsymmetricKeyEncodingConfig(bool output_key_object, PKFormatType format, PKEncodingType type);
|
||||||
|
AsymmetricKeyEncodingConfig(const AsymmetricKeyEncodingConfig&) = default;
|
||||||
|
AsymmetricKeyEncodingConfig& operator=(const AsymmetricKeyEncodingConfig&) = default;
|
||||||
|
};
|
||||||
|
using PublicKeyEncodingConfig = AsymmetricKeyEncodingConfig;
|
||||||
|
|
||||||
|
struct PrivateKeyEncodingConfig: public AsymmetricKeyEncodingConfig {
|
||||||
|
const EVP_CIPHER* cipher = nullptr;
|
||||||
|
std::optional<DataPointer> passphrase = std::nullopt;
|
||||||
|
PrivateKeyEncodingConfig() = default;
|
||||||
|
PrivateKeyEncodingConfig(bool output_key_object, PKFormatType format, PKEncodingType type)
|
||||||
|
: AsymmetricKeyEncodingConfig(output_key_object, format, type) {}
|
||||||
|
PrivateKeyEncodingConfig(const PrivateKeyEncodingConfig&);
|
||||||
|
PrivateKeyEncodingConfig& operator=(const PrivateKeyEncodingConfig&);
|
||||||
|
};
|
||||||
|
|
||||||
static ParseKeyResult TryParsePublicKey(
|
static ParseKeyResult TryParsePublicKey(
|
||||||
PKFormatType format,
|
const PublicKeyEncodingConfig& config,
|
||||||
PKEncodingType encoding,
|
|
||||||
const Buffer<const unsigned char>& buffer);
|
const Buffer<const unsigned char>& buffer);
|
||||||
|
|
||||||
static ParseKeyResult TryParsePublicKeyPEM(
|
static ParseKeyResult TryParsePublicKeyPEM(
|
||||||
const Buffer<const unsigned char>& buffer);
|
const Buffer<const unsigned char>& buffer);
|
||||||
|
|
||||||
static ParseKeyResult TryParsePrivateKey(
|
static ParseKeyResult TryParsePrivateKey(
|
||||||
PKFormatType format,
|
const PrivateKeyEncodingConfig& config,
|
||||||
PKEncodingType encoding,
|
|
||||||
std::optional<Buffer<char>> passphrase,
|
|
||||||
const Buffer<const unsigned char>& buffer);
|
const Buffer<const unsigned char>& buffer);
|
||||||
|
|
||||||
EVPKeyPointer() = default;
|
EVPKeyPointer() = default;
|
||||||
@ -441,9 +459,11 @@ public:
|
|||||||
size_t rawPrivateKeySize() const;
|
size_t rawPrivateKeySize() const;
|
||||||
DataPointer rawPublicKey() const;
|
DataPointer rawPublicKey() const;
|
||||||
DataPointer rawPrivateKey() const;
|
DataPointer rawPrivateKey() const;
|
||||||
|
|
||||||
BIOPointer derPublicKey() const;
|
BIOPointer derPublicKey() const;
|
||||||
|
|
||||||
|
Result<BIOPointer, bool> writePrivateKey(const PrivateKeyEncodingConfig& config) const;
|
||||||
|
Result<BIOPointer, bool> writePublicKey(const PublicKeyEncodingConfig& config) const;
|
||||||
|
|
||||||
EVPKeyCtxPointer newCtx() const;
|
EVPKeyCtxPointer newCtx() const;
|
||||||
|
|
||||||
static bool IsRSAPrivateKey(const Buffer<const unsigned char>& buffer);
|
static bool IsRSAPrivateKey(const Buffer<const unsigned char>& buffer);
|
||||||
|
@ -48,8 +48,7 @@ Maybe<void> NidKeyPairGenTraits::AdditionalConfig(
|
|||||||
EVPKeyCtxPointer NidKeyPairGenTraits::Setup(NidKeyPairGenConfig* params) {
|
EVPKeyCtxPointer NidKeyPairGenTraits::Setup(NidKeyPairGenConfig* params) {
|
||||||
EVPKeyCtxPointer ctx =
|
EVPKeyCtxPointer ctx =
|
||||||
EVPKeyCtxPointer(EVP_PKEY_CTX_new_id(params->params.id, nullptr));
|
EVPKeyCtxPointer(EVP_PKEY_CTX_new_id(params->params.id, nullptr));
|
||||||
if (!ctx || EVP_PKEY_keygen_init(ctx.get()) <= 0)
|
if (!ctx || EVP_PKEY_keygen_init(ctx.get()) <= 0) return {};
|
||||||
return EVPKeyCtxPointer();
|
|
||||||
|
|
||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
@ -147,19 +147,16 @@ struct KeyPairGenTraits final {
|
|||||||
// process input parameters. This allows each job to have a variable
|
// process input parameters. This allows each job to have a variable
|
||||||
// number of input parameters specific to each job type.
|
// number of input parameters specific to each job type.
|
||||||
if (KeyPairAlgorithmTraits::AdditionalConfig(mode, args, offset, params)
|
if (KeyPairAlgorithmTraits::AdditionalConfig(mode, args, offset, params)
|
||||||
.IsNothing()) {
|
.IsNothing() ||
|
||||||
|
!KeyObjectData::GetPublicKeyEncodingFromJs(
|
||||||
|
args, offset, kKeyContextGenerate)
|
||||||
|
.To(¶ms->public_key_encoding) ||
|
||||||
|
!KeyObjectData::GetPrivateKeyEncodingFromJs(
|
||||||
|
args, offset, kKeyContextGenerate)
|
||||||
|
.To(¶ms->private_key_encoding)) {
|
||||||
return v8::Nothing<void>();
|
return v8::Nothing<void>();
|
||||||
}
|
}
|
||||||
|
|
||||||
params->public_key_encoding = KeyObjectData::GetPublicKeyEncodingFromJs(
|
|
||||||
args, offset, kKeyContextGenerate);
|
|
||||||
|
|
||||||
auto private_key_encoding = KeyObjectData::GetPrivateKeyEncodingFromJs(
|
|
||||||
args, offset, kKeyContextGenerate);
|
|
||||||
|
|
||||||
if (!private_key_encoding.IsEmpty())
|
|
||||||
params->private_key_encoding = private_key_encoding.Release();
|
|
||||||
|
|
||||||
return v8::JustVoid();
|
return v8::JustVoid();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,8 +227,8 @@ struct SecretKeyGenTraits final {
|
|||||||
|
|
||||||
template <typename AlgorithmParams>
|
template <typename AlgorithmParams>
|
||||||
struct KeyPairGenConfig final : public MemoryRetainer {
|
struct KeyPairGenConfig final : public MemoryRetainer {
|
||||||
PublicKeyEncodingConfig public_key_encoding;
|
ncrypto::EVPKeyPointer::PublicKeyEncodingConfig public_key_encoding;
|
||||||
PrivateKeyEncodingConfig private_key_encoding;
|
ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig private_key_encoding;
|
||||||
KeyObjectData key;
|
KeyObjectData key;
|
||||||
AlgorithmParams params;
|
AlgorithmParams params;
|
||||||
|
|
||||||
@ -245,7 +242,7 @@ struct KeyPairGenConfig final : public MemoryRetainer {
|
|||||||
explicit KeyPairGenConfig(KeyPairGenConfig&& other) noexcept
|
explicit KeyPairGenConfig(KeyPairGenConfig&& other) noexcept
|
||||||
: public_key_encoding(other.public_key_encoding),
|
: public_key_encoding(other.public_key_encoding),
|
||||||
private_key_encoding(
|
private_key_encoding(
|
||||||
std::forward<PrivateKeyEncodingConfig>(
|
std::forward<ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig>(
|
||||||
other.private_key_encoding)),
|
other.private_key_encoding)),
|
||||||
key(std::move(other.key)),
|
key(std::move(other.key)),
|
||||||
params(std::move(other.params)) {}
|
params(std::move(other.params)) {}
|
||||||
@ -258,9 +255,10 @@ struct KeyPairGenConfig final : public MemoryRetainer {
|
|||||||
|
|
||||||
void MemoryInfo(MemoryTracker* tracker) const override {
|
void MemoryInfo(MemoryTracker* tracker) const override {
|
||||||
tracker->TrackField("key", key);
|
tracker->TrackField("key", key);
|
||||||
if (!private_key_encoding.passphrase_.IsEmpty()) {
|
if (private_key_encoding.passphrase.has_value()) {
|
||||||
|
auto& passphrase = private_key_encoding.passphrase.value();
|
||||||
tracker->TrackFieldWithSize("private_key_encoding.passphrase",
|
tracker->TrackFieldWithSize("private_key_encoding.passphrase",
|
||||||
private_key_encoding.passphrase_->size());
|
passphrase.size());
|
||||||
}
|
}
|
||||||
tracker->TrackField("params", params);
|
tracker->TrackField("params", params);
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ using v8::FunctionCallbackInfo;
|
|||||||
using v8::FunctionTemplate;
|
using v8::FunctionTemplate;
|
||||||
using v8::Int32;
|
using v8::Int32;
|
||||||
using v8::Isolate;
|
using v8::Isolate;
|
||||||
|
using v8::Just;
|
||||||
using v8::JustVoid;
|
using v8::JustVoid;
|
||||||
using v8::Local;
|
using v8::Local;
|
||||||
using v8::Maybe;
|
using v8::Maybe;
|
||||||
@ -40,199 +41,86 @@ using v8::Value;
|
|||||||
|
|
||||||
namespace crypto {
|
namespace crypto {
|
||||||
namespace {
|
namespace {
|
||||||
void GetKeyFormatAndTypeFromJs(
|
Maybe<ncrypto::EVPKeyPointer::AsymmetricKeyEncodingConfig>
|
||||||
AsymmetricKeyEncodingConfig* config,
|
GetKeyFormatAndTypeFromJs(const FunctionCallbackInfo<Value>& args,
|
||||||
const FunctionCallbackInfo<Value>& args,
|
unsigned int* offset,
|
||||||
unsigned int* offset,
|
KeyEncodingContext context) {
|
||||||
KeyEncodingContext context) {
|
ncrypto::EVPKeyPointer::AsymmetricKeyEncodingConfig config;
|
||||||
// During key pair generation, it is possible not to specify a key encoding,
|
// During key pair generation, it is possible not to specify a key encoding,
|
||||||
// which will lead to a key object being returned.
|
// which will lead to a key object being returned.
|
||||||
if (args[*offset]->IsUndefined()) {
|
if (args[*offset]->IsUndefined()) {
|
||||||
CHECK_EQ(context, kKeyContextGenerate);
|
CHECK_EQ(context, kKeyContextGenerate);
|
||||||
CHECK(args[*offset + 1]->IsUndefined());
|
CHECK(args[*offset + 1]->IsUndefined());
|
||||||
config->output_key_object_ = true;
|
config.output_key_object = true;
|
||||||
} else {
|
} else {
|
||||||
config->output_key_object_ = false;
|
config.output_key_object = false;
|
||||||
|
|
||||||
CHECK(args[*offset]->IsInt32());
|
CHECK(args[*offset]->IsInt32());
|
||||||
config->format_ = static_cast<PKFormatType>(
|
config.format = static_cast<ncrypto::EVPKeyPointer::PKFormatType>(
|
||||||
args[*offset].As<Int32>()->Value());
|
args[*offset].As<Int32>()->Value());
|
||||||
|
|
||||||
if (args[*offset + 1]->IsInt32()) {
|
if (args[*offset + 1]->IsInt32()) {
|
||||||
config->type_ =
|
config.type = static_cast<ncrypto::EVPKeyPointer::PKEncodingType>(
|
||||||
static_cast<PKEncodingType>(args[*offset + 1].As<Int32>()->Value());
|
args[*offset + 1].As<Int32>()->Value());
|
||||||
} else {
|
} else {
|
||||||
CHECK(
|
CHECK((context == kKeyContextInput &&
|
||||||
(context == kKeyContextInput &&
|
config.format == ncrypto::EVPKeyPointer::PKFormatType::PEM) ||
|
||||||
config->format_ == kKeyFormatPEM) ||
|
(context == kKeyContextGenerate &&
|
||||||
(context == kKeyContextGenerate &&
|
config.format == ncrypto::EVPKeyPointer::PKFormatType::JWK));
|
||||||
config->format_ == kKeyFormatJWK));
|
|
||||||
CHECK(args[*offset + 1]->IsNullOrUndefined());
|
CHECK(args[*offset + 1]->IsNullOrUndefined());
|
||||||
config->type_ = std::nullopt;
|
config.type = ncrypto::EVPKeyPointer::PKEncodingType::PKCS1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*offset += 2;
|
*offset += 2;
|
||||||
|
return Just(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
MaybeLocal<Value> BIOToStringOrBuffer(Environment* env,
|
MaybeLocal<Value> BIOToStringOrBuffer(
|
||||||
const BIOPointer& bio,
|
Environment* env,
|
||||||
PKFormatType format) {
|
const BIOPointer& bio,
|
||||||
|
const ncrypto::EVPKeyPointer::AsymmetricKeyEncodingConfig& config) {
|
||||||
BUF_MEM* bptr = bio;
|
BUF_MEM* bptr = bio;
|
||||||
if (format == kKeyFormatPEM) {
|
if (config.format == ncrypto::EVPKeyPointer::PKFormatType::PEM) {
|
||||||
// PEM is an ASCII format, so we will return it as a string.
|
// PEM is an ASCII format, so we will return it as a string.
|
||||||
return String::NewFromUtf8(env->isolate(), bptr->data,
|
return String::NewFromUtf8(
|
||||||
NewStringType::kNormal,
|
env->isolate(), bptr->data, NewStringType::kNormal, bptr->length)
|
||||||
bptr->length).FromMaybe(Local<Value>());
|
|
||||||
} else {
|
|
||||||
CHECK_EQ(format, kKeyFormatDER);
|
|
||||||
// DER is binary, return it as a buffer.
|
|
||||||
return Buffer::Copy(env, bptr->data, bptr->length)
|
|
||||||
.FromMaybe(Local<Value>());
|
.FromMaybe(Local<Value>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CHECK_EQ(config.format, ncrypto::EVPKeyPointer::PKFormatType::DER);
|
||||||
|
// DER is binary, return it as a buffer.
|
||||||
|
return Buffer::Copy(env, bptr->data, bptr->length).FromMaybe(Local<Value>());
|
||||||
}
|
}
|
||||||
|
|
||||||
MaybeLocal<Value> WritePrivateKey(Environment* env,
|
MaybeLocal<Value> WritePrivateKey(
|
||||||
OSSL3_CONST EVP_PKEY* pkey,
|
Environment* env,
|
||||||
const PrivateKeyEncodingConfig& config) {
|
const EVPKeyPointer& pkey,
|
||||||
auto bio = BIOPointer::NewMem();
|
const ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig& config) {
|
||||||
CHECK(bio);
|
CHECK(pkey);
|
||||||
|
auto res = pkey.writePrivateKey(config);
|
||||||
// If an empty string was passed as the passphrase, the ByteSource might
|
if (res) {
|
||||||
// contain a null pointer, which OpenSSL will ignore, causing it to invoke its
|
return BIOToStringOrBuffer(env, std::move(res.value), config);
|
||||||
// default passphrase callback, which would block the thread until the user
|
|
||||||
// manually enters a passphrase. We could supply our own passphrase callback
|
|
||||||
// to handle this special case, but it is easier to avoid passing a null
|
|
||||||
// pointer to OpenSSL.
|
|
||||||
char* pass = nullptr;
|
|
||||||
size_t pass_len = 0;
|
|
||||||
if (!config.passphrase_.IsEmpty()) {
|
|
||||||
pass = const_cast<char*>(config.passphrase_->data<char>());
|
|
||||||
pass_len = config.passphrase_->size();
|
|
||||||
if (pass == nullptr) {
|
|
||||||
// OpenSSL will not actually dereference this pointer, so it can be any
|
|
||||||
// non-null pointer. We cannot assert that directly, which is why we
|
|
||||||
// intentionally use a pointer that will likely cause a segmentation fault
|
|
||||||
// when dereferenced.
|
|
||||||
CHECK_EQ(pass_len, 0);
|
|
||||||
pass = reinterpret_cast<char*>(-1);
|
|
||||||
CHECK_NE(pass, nullptr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MarkPopErrorOnReturn mark_pop_error_on_return;
|
ThrowCryptoError(
|
||||||
bool err;
|
env, res.openssl_error.value_or(0), "Failed to encode private key");
|
||||||
|
return MaybeLocal<Value>();
|
||||||
PKEncodingType encoding_type = config.type_.value();
|
|
||||||
if (encoding_type == kKeyEncodingPKCS1) {
|
|
||||||
// PKCS#1 is only permitted for RSA keys.
|
|
||||||
CHECK_EQ(EVPKeyPointer::id(pkey), EVP_PKEY_RSA);
|
|
||||||
|
|
||||||
OSSL3_CONST RSA* rsa = EVP_PKEY_get0_RSA(pkey);
|
|
||||||
if (config.format_ == kKeyFormatPEM) {
|
|
||||||
// Encode PKCS#1 as PEM.
|
|
||||||
err = PEM_write_bio_RSAPrivateKey(bio.get(),
|
|
||||||
rsa,
|
|
||||||
config.cipher_,
|
|
||||||
reinterpret_cast<unsigned char*>(pass),
|
|
||||||
pass_len,
|
|
||||||
nullptr,
|
|
||||||
nullptr) != 1;
|
|
||||||
} else {
|
|
||||||
// Encode PKCS#1 as DER. This does not permit encryption.
|
|
||||||
CHECK_EQ(config.format_, kKeyFormatDER);
|
|
||||||
CHECK_NULL(config.cipher_);
|
|
||||||
err = i2d_RSAPrivateKey_bio(bio.get(), rsa) != 1;
|
|
||||||
}
|
|
||||||
} else if (encoding_type == kKeyEncodingPKCS8) {
|
|
||||||
if (config.format_ == kKeyFormatPEM) {
|
|
||||||
// Encode PKCS#8 as PEM.
|
|
||||||
err = PEM_write_bio_PKCS8PrivateKey(
|
|
||||||
bio.get(), pkey,
|
|
||||||
config.cipher_,
|
|
||||||
pass,
|
|
||||||
pass_len,
|
|
||||||
nullptr, nullptr) != 1;
|
|
||||||
} else {
|
|
||||||
// Encode PKCS#8 as DER.
|
|
||||||
CHECK_EQ(config.format_, kKeyFormatDER);
|
|
||||||
err = i2d_PKCS8PrivateKey_bio(
|
|
||||||
bio.get(), pkey,
|
|
||||||
config.cipher_,
|
|
||||||
pass,
|
|
||||||
pass_len,
|
|
||||||
nullptr, nullptr) != 1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
CHECK_EQ(encoding_type, kKeyEncodingSEC1);
|
|
||||||
|
|
||||||
// SEC1 is only permitted for EC keys.
|
|
||||||
CHECK_EQ(EVPKeyPointer::id(pkey), EVP_PKEY_EC);
|
|
||||||
|
|
||||||
OSSL3_CONST EC_KEY* ec_key = EVP_PKEY_get0_EC_KEY(pkey);
|
|
||||||
if (config.format_ == kKeyFormatPEM) {
|
|
||||||
// Encode SEC1 as PEM.
|
|
||||||
err = PEM_write_bio_ECPrivateKey(bio.get(),
|
|
||||||
ec_key,
|
|
||||||
config.cipher_,
|
|
||||||
reinterpret_cast<unsigned char*>(pass),
|
|
||||||
pass_len,
|
|
||||||
nullptr,
|
|
||||||
nullptr) != 1;
|
|
||||||
} else {
|
|
||||||
// Encode SEC1 as DER. This does not permit encryption.
|
|
||||||
CHECK_EQ(config.format_, kKeyFormatDER);
|
|
||||||
CHECK_NULL(config.cipher_);
|
|
||||||
err = i2d_ECPrivateKey_bio(bio.get(), ec_key) != 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
ThrowCryptoError(env, ERR_get_error(), "Failed to encode private key");
|
|
||||||
return MaybeLocal<Value>();
|
|
||||||
}
|
|
||||||
return BIOToStringOrBuffer(env, bio, config.format_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WritePublicKeyInner(OSSL3_CONST EVP_PKEY* pkey,
|
MaybeLocal<Value> WritePublicKey(
|
||||||
const BIOPointer& bio,
|
Environment* env,
|
||||||
const PublicKeyEncodingConfig& config) {
|
const EVPKeyPointer& pkey,
|
||||||
if (config.type_.value() == kKeyEncodingPKCS1) {
|
const ncrypto::EVPKeyPointer::PublicKeyEncodingConfig& config) {
|
||||||
// PKCS#1 is only valid for RSA keys.
|
CHECK(pkey);
|
||||||
CHECK_EQ(EVPKeyPointer::id(pkey), EVP_PKEY_RSA);
|
auto res = pkey.writePublicKey(config);
|
||||||
OSSL3_CONST RSA* rsa = EVP_PKEY_get0_RSA(pkey);
|
if (res) {
|
||||||
if (config.format_ == kKeyFormatPEM) {
|
return BIOToStringOrBuffer(env, res.value, config);
|
||||||
// Encode PKCS#1 as PEM.
|
|
||||||
return PEM_write_bio_RSAPublicKey(bio.get(), rsa) == 1;
|
|
||||||
} else {
|
|
||||||
// Encode PKCS#1 as DER.
|
|
||||||
CHECK_EQ(config.format_, kKeyFormatDER);
|
|
||||||
return i2d_RSAPublicKey_bio(bio.get(), rsa) == 1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
CHECK_EQ(config.type_.value(), kKeyEncodingSPKI);
|
|
||||||
if (config.format_ == kKeyFormatPEM) {
|
|
||||||
// Encode SPKI as PEM.
|
|
||||||
return PEM_write_bio_PUBKEY(bio.get(), pkey) == 1;
|
|
||||||
} else {
|
|
||||||
// Encode SPKI as DER.
|
|
||||||
CHECK_EQ(config.format_, kKeyFormatDER);
|
|
||||||
return i2d_PUBKEY_bio(bio.get(), pkey) == 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
MaybeLocal<Value> WritePublicKey(Environment* env,
|
ThrowCryptoError(
|
||||||
OSSL3_CONST EVP_PKEY* pkey,
|
env, res.openssl_error.value_or(0), "Failed to encode public key");
|
||||||
const PublicKeyEncodingConfig& config) {
|
return MaybeLocal<Value>();
|
||||||
auto bio = BIOPointer::NewMem();
|
|
||||||
CHECK(bio);
|
|
||||||
|
|
||||||
if (!WritePublicKeyInner(pkey, bio, config)) {
|
|
||||||
ThrowCryptoError(env, ERR_get_error(), "Failed to encode public key");
|
|
||||||
return MaybeLocal<Value>();
|
|
||||||
}
|
|
||||||
return BIOToStringOrBuffer(env, bio, config.format_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Maybe<void> ExportJWKSecretKey(Environment* env,
|
Maybe<void> ExportJWKSecretKey(Environment* env,
|
||||||
@ -247,12 +135,11 @@ Maybe<void> ExportJWKSecretKey(Environment* env,
|
|||||||
key.GetSymmetricKeySize(),
|
key.GetSymmetricKeySize(),
|
||||||
BASE64URL,
|
BASE64URL,
|
||||||
&error);
|
&error);
|
||||||
if (key_data.IsEmpty()) {
|
if (!key_data.ToLocal(&raw)) {
|
||||||
CHECK(!error.IsEmpty());
|
CHECK(!error.IsEmpty());
|
||||||
env->isolate()->ThrowException(error);
|
env->isolate()->ThrowException(error);
|
||||||
return Nothing<void>();
|
return Nothing<void>();
|
||||||
}
|
}
|
||||||
if (!key_data.ToLocal(&raw)) return Nothing<void>();
|
|
||||||
|
|
||||||
if (target->Set(
|
if (target->Set(
|
||||||
env->context(),
|
env->context(),
|
||||||
@ -277,8 +164,8 @@ KeyObjectData ImportJWKSecretKey(Environment* env, Local<Object> jwk) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static_assert(String::kMaxLength <= INT_MAX);
|
static_assert(String::kMaxLength <= INT_MAX);
|
||||||
auto key_data = ByteSource::FromEncodedString(env, key.As<String>());
|
return KeyObjectData::CreateSecret(
|
||||||
return KeyObjectData::CreateSecret(std::move(key_data));
|
ByteSource::FromEncodedString(env, key.As<String>()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Maybe<void> ExportJWKAsymmetricKey(Environment* env,
|
Maybe<void> ExportJWKAsymmetricKey(Environment* env,
|
||||||
@ -290,7 +177,8 @@ Maybe<void> ExportJWKAsymmetricKey(Environment* env,
|
|||||||
if (handleRsaPss) return ExportJWKRsaKey(env, key, target);
|
if (handleRsaPss) return ExportJWKRsaKey(env, key, target);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EVP_PKEY_RSA: return ExportJWKRsaKey(env, key, target);
|
case EVP_PKEY_RSA:
|
||||||
|
return ExportJWKRsaKey(env, key, target);
|
||||||
case EVP_PKEY_EC:
|
case EVP_PKEY_EC:
|
||||||
return ExportJWKEcKey(env, key, target);
|
return ExportJWKEcKey(env, key, target);
|
||||||
case EVP_PKEY_ED25519:
|
case EVP_PKEY_ED25519:
|
||||||
@ -299,7 +187,8 @@ Maybe<void> ExportJWKAsymmetricKey(Environment* env,
|
|||||||
// Fall through
|
// Fall through
|
||||||
case EVP_PKEY_X25519:
|
case EVP_PKEY_X25519:
|
||||||
// Fall through
|
// Fall through
|
||||||
case EVP_PKEY_X448: return ExportJWKEdKey(env, key, target);
|
case EVP_PKEY_X448:
|
||||||
|
return ExportJWKEdKey(env, key, target);
|
||||||
}
|
}
|
||||||
THROW_ERR_CRYPTO_JWK_UNSUPPORTED_KEY_TYPE(env);
|
THROW_ERR_CRYPTO_JWK_UNSUPPORTED_KEY_TYPE(env);
|
||||||
return Nothing<void>();
|
return Nothing<void>();
|
||||||
@ -354,38 +243,22 @@ Maybe<void> GetAsymmetricKeyDetail(Environment* env,
|
|||||||
|
|
||||||
KeyObjectData TryParsePrivateKey(
|
KeyObjectData TryParsePrivateKey(
|
||||||
Environment* env,
|
Environment* env,
|
||||||
const PrivateKeyEncodingConfig& config,
|
const ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig& config,
|
||||||
const ncrypto::Buffer<const unsigned char>& buffer) {
|
const ncrypto::Buffer<const unsigned char>& buffer) {
|
||||||
std::optional<ncrypto::Buffer<char>> maybePassphrase = std::nullopt;
|
auto res = EVPKeyPointer::TryParsePrivateKey(config, buffer);
|
||||||
if (config.passphrase_.get() != nullptr) {
|
if (res) {
|
||||||
maybePassphrase = ncrypto::Buffer<char>{
|
return KeyObjectData::CreateAsymmetric(KeyType::kKeyTypePrivate,
|
||||||
.data = const_cast<char*>(config.passphrase_->data<char>()),
|
std::move(res.value));
|
||||||
.len = config.passphrase_->size(),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto res = EVPKeyPointer::TryParsePrivateKey(
|
if (res.error.value() == EVPKeyPointer::PKParseError::NEED_PASSPHRASE) {
|
||||||
static_cast<EVPKeyPointer::PKFormatType>(config.format_),
|
THROW_ERR_MISSING_PASSPHRASE(env, "Passphrase required for encrypted key");
|
||||||
static_cast<EVPKeyPointer::PKEncodingType>(
|
} else {
|
||||||
config.type_.value_or(kKeyEncodingPKCS8)),
|
|
||||||
std::move(maybePassphrase),
|
|
||||||
buffer);
|
|
||||||
|
|
||||||
if (!res) {
|
|
||||||
if (res.error.value() == EVPKeyPointer::PKParseError::NEED_PASSPHRASE) {
|
|
||||||
THROW_ERR_MISSING_PASSPHRASE(env,
|
|
||||||
"Passphrase required for encrypted key");
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
ThrowCryptoError(
|
ThrowCryptoError(
|
||||||
env, res.openssl_error.value_or(0), "Failed to read private key");
|
env, res.openssl_error.value_or(0), "Failed to read private key");
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
return {};
|
||||||
return KeyObjectData::CreateAsymmetric(KeyType::kKeyTypePrivate,
|
|
||||||
std::move(res.value));
|
|
||||||
}
|
}
|
||||||
} // namespace
|
|
||||||
|
|
||||||
// This maps true to JustVoid and false to Nothing<void>().
|
// This maps true to JustVoid and false to Nothing<void>().
|
||||||
static inline Maybe<void> NothingIfFalse(bool b) {
|
static inline Maybe<void> NothingIfFalse(bool b) {
|
||||||
@ -408,58 +281,61 @@ Maybe<void> ExportJWKInner(Environment* env,
|
|||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
Maybe<void> KeyObjectData::ToEncodedPublicKey(
|
Maybe<void> KeyObjectData::ToEncodedPublicKey(
|
||||||
Environment* env,
|
Environment* env,
|
||||||
const PublicKeyEncodingConfig& config,
|
const ncrypto::EVPKeyPointer::PublicKeyEncodingConfig& config,
|
||||||
Local<Value>* out) {
|
Local<Value>* out) {
|
||||||
CHECK(key_type_ != KeyType::kKeyTypeSecret);
|
CHECK(key_type_ != KeyType::kKeyTypeSecret);
|
||||||
if (config.output_key_object_) {
|
if (config.output_key_object) {
|
||||||
// Note that this has the downside of containing sensitive data of the
|
// Note that this has the downside of containing sensitive data of the
|
||||||
// private key.
|
// private key.
|
||||||
return NothingIfFalse(
|
return NothingIfFalse(
|
||||||
KeyObjectHandle::Create(env, addRefWithType(KeyType::kKeyTypePublic))
|
KeyObjectHandle::Create(env, addRefWithType(KeyType::kKeyTypePublic))
|
||||||
.ToLocal(out));
|
.ToLocal(out));
|
||||||
} else if (config.format_ == kKeyFormatJWK) {
|
} else if (config.format == ncrypto::EVPKeyPointer::PKFormatType::JWK) {
|
||||||
*out = Object::New(env->isolate());
|
*out = Object::New(env->isolate());
|
||||||
return ExportJWKInner(
|
return ExportJWKInner(
|
||||||
env, addRefWithType(KeyType::kKeyTypePublic), *out, false);
|
env, addRefWithType(KeyType::kKeyTypePublic), *out, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return NothingIfFalse(
|
return NothingIfFalse(
|
||||||
WritePublicKey(env, GetAsymmetricKey().get(), config).ToLocal(out));
|
WritePublicKey(env, GetAsymmetricKey(), config).ToLocal(out));
|
||||||
}
|
}
|
||||||
|
|
||||||
Maybe<void> KeyObjectData::ToEncodedPrivateKey(
|
Maybe<void> KeyObjectData::ToEncodedPrivateKey(
|
||||||
Environment* env,
|
Environment* env,
|
||||||
const PrivateKeyEncodingConfig& config,
|
const ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig& config,
|
||||||
Local<Value>* out) {
|
Local<Value>* out) {
|
||||||
CHECK(key_type_ != KeyType::kKeyTypeSecret);
|
CHECK(key_type_ != KeyType::kKeyTypeSecret);
|
||||||
if (config.output_key_object_) {
|
if (config.output_key_object) {
|
||||||
return NothingIfFalse(
|
return NothingIfFalse(
|
||||||
KeyObjectHandle::Create(env, addRefWithType(KeyType::kKeyTypePrivate))
|
KeyObjectHandle::Create(env, addRefWithType(KeyType::kKeyTypePrivate))
|
||||||
.ToLocal(out));
|
.ToLocal(out));
|
||||||
} else if (config.format_ == kKeyFormatJWK) {
|
} else if (config.format == ncrypto::EVPKeyPointer::PKFormatType::JWK) {
|
||||||
*out = Object::New(env->isolate());
|
*out = Object::New(env->isolate());
|
||||||
return ExportJWKInner(
|
return ExportJWKInner(
|
||||||
env, addRefWithType(KeyType::kKeyTypePrivate), *out, false);
|
env, addRefWithType(KeyType::kKeyTypePrivate), *out, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return NothingIfFalse(
|
return NothingIfFalse(
|
||||||
WritePrivateKey(env, GetAsymmetricKey().get(), config).ToLocal(out));
|
WritePrivateKey(env, GetAsymmetricKey(), config).ToLocal(out));
|
||||||
}
|
}
|
||||||
|
|
||||||
NonCopyableMaybe<PrivateKeyEncodingConfig>
|
Maybe<ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig>
|
||||||
KeyObjectData::GetPrivateKeyEncodingFromJs(
|
KeyObjectData::GetPrivateKeyEncodingFromJs(
|
||||||
const FunctionCallbackInfo<Value>& args,
|
const FunctionCallbackInfo<Value>& args,
|
||||||
unsigned int* offset,
|
unsigned int* offset,
|
||||||
KeyEncodingContext context) {
|
KeyEncodingContext context) {
|
||||||
Environment* env = Environment::GetCurrent(args);
|
Environment* env = Environment::GetCurrent(args);
|
||||||
|
|
||||||
PrivateKeyEncodingConfig result;
|
ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig config;
|
||||||
GetKeyFormatAndTypeFromJs(&result, args, offset, context);
|
if (!GetKeyFormatAndTypeFromJs(args, offset, context).To(&config)) {
|
||||||
|
return Nothing<ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig>();
|
||||||
|
}
|
||||||
|
|
||||||
if (result.output_key_object_) {
|
if (config.output_key_object) {
|
||||||
if (context != kKeyContextInput)
|
if (context != kKeyContextInput)
|
||||||
(*offset)++;
|
(*offset)++;
|
||||||
} else {
|
} else {
|
||||||
@ -467,44 +343,43 @@ KeyObjectData::GetPrivateKeyEncodingFromJs(
|
|||||||
if (context != kKeyContextInput) {
|
if (context != kKeyContextInput) {
|
||||||
if (args[*offset]->IsString()) {
|
if (args[*offset]->IsString()) {
|
||||||
Utf8Value cipher_name(env->isolate(), args[*offset]);
|
Utf8Value cipher_name(env->isolate(), args[*offset]);
|
||||||
result.cipher_ = EVP_get_cipherbyname(*cipher_name);
|
config.cipher = EVP_get_cipherbyname(*cipher_name);
|
||||||
if (result.cipher_ == nullptr) {
|
if (config.cipher == nullptr) {
|
||||||
THROW_ERR_CRYPTO_UNKNOWN_CIPHER(env);
|
THROW_ERR_CRYPTO_UNKNOWN_CIPHER(env);
|
||||||
return NonCopyableMaybe<PrivateKeyEncodingConfig>();
|
return Nothing<ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig>();
|
||||||
}
|
}
|
||||||
needs_passphrase = true;
|
needs_passphrase = true;
|
||||||
} else {
|
} else {
|
||||||
CHECK(args[*offset]->IsNullOrUndefined());
|
CHECK(args[*offset]->IsNullOrUndefined());
|
||||||
result.cipher_ = nullptr;
|
config.cipher = nullptr;
|
||||||
}
|
}
|
||||||
(*offset)++;
|
(*offset)++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsAnyBufferSource(args[*offset])) {
|
if (IsAnyBufferSource(args[*offset])) {
|
||||||
CHECK_IMPLIES(context != kKeyContextInput, result.cipher_ != nullptr);
|
CHECK_IMPLIES(context != kKeyContextInput, config.cipher != nullptr);
|
||||||
ArrayBufferOrViewContents<char> passphrase(args[*offset]);
|
ArrayBufferOrViewContents<char> passphrase(args[*offset]);
|
||||||
if (!passphrase.CheckSizeInt32()) [[unlikely]] {
|
if (!passphrase.CheckSizeInt32()) [[unlikely]] {
|
||||||
THROW_ERR_OUT_OF_RANGE(env, "passphrase is too big");
|
THROW_ERR_OUT_OF_RANGE(env, "passphrase is too big");
|
||||||
return NonCopyableMaybe<PrivateKeyEncodingConfig>();
|
return Nothing<ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig>();
|
||||||
}
|
}
|
||||||
result.passphrase_ = NonCopyableMaybe<ByteSource>(
|
config.passphrase = passphrase.ToDataPointer();
|
||||||
passphrase.ToNullTerminatedCopy());
|
|
||||||
} else {
|
} else {
|
||||||
CHECK(args[*offset]->IsNullOrUndefined() && !needs_passphrase);
|
CHECK(args[*offset]->IsNullOrUndefined() && !needs_passphrase);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(*offset)++;
|
(*offset)++;
|
||||||
return NonCopyableMaybe<PrivateKeyEncodingConfig>(std::move(result));
|
return Just<ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig>(
|
||||||
|
std::move(config));
|
||||||
}
|
}
|
||||||
|
|
||||||
PublicKeyEncodingConfig KeyObjectData::GetPublicKeyEncodingFromJs(
|
Maybe<ncrypto::EVPKeyPointer::PublicKeyEncodingConfig>
|
||||||
|
KeyObjectData::GetPublicKeyEncodingFromJs(
|
||||||
const FunctionCallbackInfo<Value>& args,
|
const FunctionCallbackInfo<Value>& args,
|
||||||
unsigned int* offset,
|
unsigned int* offset,
|
||||||
KeyEncodingContext context) {
|
KeyEncodingContext context) {
|
||||||
PublicKeyEncodingConfig result;
|
return GetKeyFormatAndTypeFromJs(args, offset, context);
|
||||||
GetKeyFormatAndTypeFromJs(&result, args, offset, context);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyObjectData KeyObjectData::GetPrivateKeyFromJs(
|
KeyObjectData KeyObjectData::GetPrivateKeyFromJs(
|
||||||
@ -513,14 +388,17 @@ KeyObjectData KeyObjectData::GetPrivateKeyFromJs(
|
|||||||
bool allow_key_object) {
|
bool allow_key_object) {
|
||||||
if (args[*offset]->IsString() || IsAnyBufferSource(args[*offset])) {
|
if (args[*offset]->IsString() || IsAnyBufferSource(args[*offset])) {
|
||||||
Environment* env = Environment::GetCurrent(args);
|
Environment* env = Environment::GetCurrent(args);
|
||||||
ByteSource key = ByteSource::FromStringOrBuffer(env, args[(*offset)++]);
|
auto key = ByteSource::FromStringOrBuffer(env, args[(*offset)++]);
|
||||||
NonCopyableMaybe<PrivateKeyEncodingConfig> config =
|
|
||||||
GetPrivateKeyEncodingFromJs(args, offset, kKeyContextInput);
|
ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig config;
|
||||||
|
if (!GetPrivateKeyEncodingFromJs(args, offset, kKeyContextInput)
|
||||||
|
.To(&config)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
if (config.IsEmpty()) return {};
|
|
||||||
return TryParsePrivateKey(
|
return TryParsePrivateKey(
|
||||||
env,
|
env,
|
||||||
config.Release(),
|
config,
|
||||||
ncrypto::Buffer<const unsigned char>{
|
ncrypto::Buffer<const unsigned char>{
|
||||||
.data = reinterpret_cast<const unsigned char*>(key.data()),
|
.data = reinterpret_cast<const unsigned char*>(key.data()),
|
||||||
.len = key.size(),
|
.len = key.size(),
|
||||||
@ -544,70 +422,62 @@ KeyObjectData KeyObjectData::GetPublicOrPrivateKeyFromJs(
|
|||||||
THROW_ERR_OUT_OF_RANGE(env, "keyData is too big");
|
THROW_ERR_OUT_OF_RANGE(env, "keyData is too big");
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
NonCopyableMaybe<PrivateKeyEncodingConfig> config_ =
|
|
||||||
KeyObjectData::GetPrivateKeyEncodingFromJs(
|
ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig config;
|
||||||
args, offset, kKeyContextInput);
|
if (!KeyObjectData::GetPrivateKeyEncodingFromJs(
|
||||||
if (config_.IsEmpty()) return {};
|
args, offset, kKeyContextInput)
|
||||||
PrivateKeyEncodingConfig config = config_.Release();
|
.To(&config)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
ncrypto::Buffer<const unsigned char> buffer = {
|
ncrypto::Buffer<const unsigned char> buffer = {
|
||||||
.data = reinterpret_cast<const unsigned char*>(data.data()),
|
.data = reinterpret_cast<const unsigned char*>(data.data()),
|
||||||
.len = data.size(),
|
.len = data.size(),
|
||||||
};
|
};
|
||||||
|
|
||||||
std::optional<ncrypto::Buffer<char>> maybePassphrase = std::nullopt;
|
if (config.format == ncrypto::EVPKeyPointer::PKFormatType::PEM) {
|
||||||
if (config.passphrase_.get() != nullptr) {
|
|
||||||
maybePassphrase = ncrypto::Buffer<char>{
|
|
||||||
.data = const_cast<char*>(config.passphrase_->data<char>()),
|
|
||||||
.len = config.passphrase_->size(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.format_ == kKeyFormatPEM) {
|
|
||||||
// For PEM, we can easily determine whether it is a public or private key
|
// For PEM, we can easily determine whether it is a public or private key
|
||||||
// by looking for the respective PEM tags.
|
// by looking for the respective PEM tags.
|
||||||
auto res = EVPKeyPointer::TryParsePublicKeyPEM(buffer);
|
auto res = EVPKeyPointer::TryParsePublicKeyPEM(buffer);
|
||||||
if (!res) {
|
if (res) {
|
||||||
if (res.error.value() == EVPKeyPointer::PKParseError::NOT_RECOGNIZED) {
|
return CreateAsymmetric(kKeyTypePublic, std::move(res.value));
|
||||||
return TryParsePrivateKey(env, config, buffer);
|
|
||||||
}
|
|
||||||
ThrowCryptoError(env,
|
|
||||||
res.openssl_error.value_or(0),
|
|
||||||
"Failed to read asymmetric key");
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
return CreateAsymmetric(kKeyTypePublic, std::move(res.value));
|
|
||||||
|
if (res.error.value() == EVPKeyPointer::PKParseError::NOT_RECOGNIZED) {
|
||||||
|
return TryParsePrivateKey(env, config, buffer);
|
||||||
|
}
|
||||||
|
ThrowCryptoError(
|
||||||
|
env, res.openssl_error.value_or(0), "Failed to read asymmetric key");
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// For DER, the type determines how to parse it. SPKI, PKCS#8 and SEC1 are
|
// For DER, the type determines how to parse it. SPKI, PKCS#8 and SEC1 are
|
||||||
// easy, but PKCS#1 can be a public key or a private key.
|
// easy, but PKCS#1 can be a public key or a private key.
|
||||||
bool is_public = ([&] {
|
static const auto is_public = [](const auto& config,
|
||||||
switch (config.type_.value()) {
|
const auto& buffer) -> bool {
|
||||||
case kKeyEncodingPKCS1:
|
switch (config.type) {
|
||||||
|
case ncrypto::EVPKeyPointer::PKEncodingType::PKCS1:
|
||||||
return !EVPKeyPointer::IsRSAPrivateKey(buffer);
|
return !EVPKeyPointer::IsRSAPrivateKey(buffer);
|
||||||
case kKeyEncodingSPKI:
|
case ncrypto::EVPKeyPointer::PKEncodingType::SPKI:
|
||||||
return true;
|
return true;
|
||||||
case kKeyEncodingPKCS8:
|
case ncrypto::EVPKeyPointer::PKEncodingType::PKCS8:
|
||||||
return false;
|
return false;
|
||||||
case kKeyEncodingSEC1:
|
case ncrypto::EVPKeyPointer::PKEncodingType::SEC1:
|
||||||
return false;
|
return false;
|
||||||
default:
|
default:
|
||||||
UNREACHABLE("Invalid key encoding type");
|
UNREACHABLE("Invalid key encoding type");
|
||||||
}
|
}
|
||||||
})();
|
};
|
||||||
|
|
||||||
if (is_public) {
|
if (is_public(config, buffer)) {
|
||||||
auto res = EVPKeyPointer::TryParsePublicKey(
|
auto res = EVPKeyPointer::TryParsePublicKey(config, buffer);
|
||||||
static_cast<EVPKeyPointer::PKFormatType>(config.format_),
|
if (res) {
|
||||||
static_cast<EVPKeyPointer::PKEncodingType>(config.type_.value()),
|
return CreateAsymmetric(KeyType::kKeyTypePublic, std::move(res.value));
|
||||||
buffer);
|
|
||||||
if (!res) {
|
|
||||||
ThrowCryptoError(env,
|
|
||||||
res.openssl_error.value_or(0),
|
|
||||||
"Failed to read asymmetric key");
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
return CreateAsymmetric(KeyType::kKeyTypePublic, std::move(res.value));
|
|
||||||
|
ThrowCryptoError(
|
||||||
|
env, res.openssl_error.value_or(0), "Failed to read asymmetric key");
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
return TryParsePrivateKey(env, config, buffer);
|
return TryParsePrivateKey(env, config, buffer);
|
||||||
@ -1120,20 +990,25 @@ void KeyObjectHandle::Export(const FunctionCallbackInfo<Value>& args) {
|
|||||||
result = key->ExportSecretKey();
|
result = key->ExportSecretKey();
|
||||||
} else if (type == kKeyTypePublic) {
|
} else if (type == kKeyTypePublic) {
|
||||||
unsigned int offset = 0;
|
unsigned int offset = 0;
|
||||||
PublicKeyEncodingConfig config = KeyObjectData::GetPublicKeyEncodingFromJs(
|
ncrypto::EVPKeyPointer::PublicKeyEncodingConfig config;
|
||||||
args, &offset, kKeyContextExport);
|
if (!KeyObjectData::GetPublicKeyEncodingFromJs(
|
||||||
|
args, &offset, kKeyContextExport)
|
||||||
|
.To(&config)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
CHECK_EQ(offset, static_cast<unsigned int>(args.Length()));
|
CHECK_EQ(offset, static_cast<unsigned int>(args.Length()));
|
||||||
result = key->ExportPublicKey(config);
|
result = key->ExportPublicKey(config);
|
||||||
} else {
|
} else {
|
||||||
CHECK_EQ(type, kKeyTypePrivate);
|
CHECK_EQ(type, kKeyTypePrivate);
|
||||||
unsigned int offset = 0;
|
unsigned int offset = 0;
|
||||||
NonCopyableMaybe<PrivateKeyEncodingConfig> config =
|
ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig config;
|
||||||
KeyObjectData::GetPrivateKeyEncodingFromJs(
|
if (!KeyObjectData::GetPrivateKeyEncodingFromJs(
|
||||||
args, &offset, kKeyContextExport);
|
args, &offset, kKeyContextExport)
|
||||||
if (config.IsEmpty())
|
.To(&config)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
CHECK_EQ(offset, static_cast<unsigned int>(args.Length()));
|
CHECK_EQ(offset, static_cast<unsigned int>(args.Length()));
|
||||||
result = key->ExportPrivateKey(config.Release());
|
result = key->ExportPrivateKey(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!result.IsEmpty())
|
if (!result.IsEmpty())
|
||||||
@ -1147,13 +1022,13 @@ MaybeLocal<Value> KeyObjectHandle::ExportSecretKey() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MaybeLocal<Value> KeyObjectHandle::ExportPublicKey(
|
MaybeLocal<Value> KeyObjectHandle::ExportPublicKey(
|
||||||
const PublicKeyEncodingConfig& config) const {
|
const ncrypto::EVPKeyPointer::PublicKeyEncodingConfig& config) const {
|
||||||
return WritePublicKey(env(), data_.GetAsymmetricKey().get(), config);
|
return WritePublicKey(env(), data_.GetAsymmetricKey(), config);
|
||||||
}
|
}
|
||||||
|
|
||||||
MaybeLocal<Value> KeyObjectHandle::ExportPrivateKey(
|
MaybeLocal<Value> KeyObjectHandle::ExportPrivateKey(
|
||||||
const PrivateKeyEncodingConfig& config) const {
|
const ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig& config) const {
|
||||||
return WritePrivateKey(env(), data_.GetAsymmetricKey().get(), config);
|
return WritePrivateKey(env(), data_.GetAsymmetricKey(), config);
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeyObjectHandle::ExportJWK(
|
void KeyObjectHandle::ExportJWK(
|
||||||
@ -1308,11 +1183,25 @@ void Initialize(Environment* env, Local<Object> target) {
|
|||||||
FIXED_ONE_BYTE_STRING(env->isolate(), "KeyObjectHandle"),
|
FIXED_ONE_BYTE_STRING(env->isolate(), "KeyObjectHandle"),
|
||||||
KeyObjectHandle::Initialize(env)).Check();
|
KeyObjectHandle::Initialize(env)).Check();
|
||||||
|
|
||||||
|
constexpr int kKeyEncodingPKCS1 =
|
||||||
|
static_cast<int>(ncrypto::EVPKeyPointer::PKEncodingType::PKCS1);
|
||||||
|
constexpr int kKeyEncodingPKCS8 =
|
||||||
|
static_cast<int>(ncrypto::EVPKeyPointer::PKEncodingType::PKCS8);
|
||||||
|
constexpr int kKeyEncodingSPKI =
|
||||||
|
static_cast<int>(ncrypto::EVPKeyPointer::PKEncodingType::SPKI);
|
||||||
|
constexpr int kKeyEncodingSEC1 =
|
||||||
|
static_cast<int>(ncrypto::EVPKeyPointer::PKEncodingType::SEC1);
|
||||||
|
constexpr int kKeyFormatDER =
|
||||||
|
static_cast<int>(ncrypto::EVPKeyPointer::PKFormatType::DER);
|
||||||
|
constexpr int kKeyFormatPEM =
|
||||||
|
static_cast<int>(ncrypto::EVPKeyPointer::PKFormatType::PEM);
|
||||||
|
constexpr int kKeyFormatJWK =
|
||||||
|
static_cast<int>(ncrypto::EVPKeyPointer::PKFormatType::JWK);
|
||||||
|
|
||||||
NODE_DEFINE_CONSTANT(target, kWebCryptoKeyFormatRaw);
|
NODE_DEFINE_CONSTANT(target, kWebCryptoKeyFormatRaw);
|
||||||
NODE_DEFINE_CONSTANT(target, kWebCryptoKeyFormatPKCS8);
|
NODE_DEFINE_CONSTANT(target, kWebCryptoKeyFormatPKCS8);
|
||||||
NODE_DEFINE_CONSTANT(target, kWebCryptoKeyFormatSPKI);
|
NODE_DEFINE_CONSTANT(target, kWebCryptoKeyFormatSPKI);
|
||||||
NODE_DEFINE_CONSTANT(target, kWebCryptoKeyFormatJWK);
|
NODE_DEFINE_CONSTANT(target, kWebCryptoKeyFormatJWK);
|
||||||
|
|
||||||
NODE_DEFINE_CONSTANT(target, EVP_PKEY_ED25519);
|
NODE_DEFINE_CONSTANT(target, EVP_PKEY_ED25519);
|
||||||
NODE_DEFINE_CONSTANT(target, EVP_PKEY_ED448);
|
NODE_DEFINE_CONSTANT(target, EVP_PKEY_ED448);
|
||||||
NODE_DEFINE_CONSTANT(target, EVP_PKEY_X25519);
|
NODE_DEFINE_CONSTANT(target, EVP_PKEY_X25519);
|
||||||
|
@ -18,24 +18,6 @@
|
|||||||
|
|
||||||
namespace node {
|
namespace node {
|
||||||
namespace crypto {
|
namespace crypto {
|
||||||
// TODO(@jasnell): These static casts are temporarily while this code
|
|
||||||
// is being shifted over into ncrypto
|
|
||||||
enum PKEncodingType {
|
|
||||||
// RSAPublicKey / RSAPrivateKey according to PKCS#1.
|
|
||||||
kKeyEncodingPKCS1 = static_cast<int>(EVPKeyPointer::PKEncodingType::PKCS1),
|
|
||||||
// PrivateKeyInfo or EncryptedPrivateKeyInfo according to PKCS#8.
|
|
||||||
kKeyEncodingPKCS8 = static_cast<int>(EVPKeyPointer::PKEncodingType::PKCS8),
|
|
||||||
// SubjectPublicKeyInfo according to X.509.
|
|
||||||
kKeyEncodingSPKI = static_cast<int>(EVPKeyPointer::PKEncodingType::SPKI),
|
|
||||||
// ECPrivateKey according to SEC1.
|
|
||||||
kKeyEncodingSEC1 = static_cast<int>(EVPKeyPointer::PKEncodingType::SEC1),
|
|
||||||
};
|
|
||||||
|
|
||||||
enum PKFormatType {
|
|
||||||
kKeyFormatDER = static_cast<int>(EVPKeyPointer::PKFormatType::DER),
|
|
||||||
kKeyFormatPEM = static_cast<int>(EVPKeyPointer::PKFormatType::PEM),
|
|
||||||
kKeyFormatJWK = static_cast<int>(EVPKeyPointer::PKFormatType::JWK),
|
|
||||||
};
|
|
||||||
|
|
||||||
enum KeyType {
|
enum KeyType {
|
||||||
kKeyTypeSecret,
|
kKeyTypeSecret,
|
||||||
@ -58,22 +40,6 @@ enum class ParseKeyResult {
|
|||||||
kParseKeyOk,
|
kParseKeyOk,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AsymmetricKeyEncodingConfig {
|
|
||||||
bool output_key_object_ = false;
|
|
||||||
PKFormatType format_ = kKeyFormatDER;
|
|
||||||
std::optional<PKEncodingType> type_ = std::nullopt;
|
|
||||||
};
|
|
||||||
|
|
||||||
using PublicKeyEncodingConfig = AsymmetricKeyEncodingConfig;
|
|
||||||
|
|
||||||
struct PrivateKeyEncodingConfig : public AsymmetricKeyEncodingConfig {
|
|
||||||
const EVP_CIPHER* cipher_;
|
|
||||||
// The ByteSource alone is not enough to distinguish between "no passphrase"
|
|
||||||
// and a zero-length passphrase (which can be a null pointer), therefore, we
|
|
||||||
// use a NonCopyableMaybe.
|
|
||||||
NonCopyableMaybe<ByteSource> passphrase_;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Objects of this class can safely be shared among threads.
|
// Objects of this class can safely be shared among threads.
|
||||||
class KeyObjectData final : public MemoryRetainer {
|
class KeyObjectData final : public MemoryRetainer {
|
||||||
public:
|
public:
|
||||||
@ -99,10 +65,10 @@ class KeyObjectData final : public MemoryRetainer {
|
|||||||
|
|
||||||
Mutex& mutex() const;
|
Mutex& mutex() const;
|
||||||
|
|
||||||
static PublicKeyEncodingConfig GetPublicKeyEncodingFromJs(
|
static v8::Maybe<ncrypto::EVPKeyPointer::PublicKeyEncodingConfig>
|
||||||
const v8::FunctionCallbackInfo<v8::Value>& args,
|
GetPublicKeyEncodingFromJs(const v8::FunctionCallbackInfo<v8::Value>& args,
|
||||||
unsigned int* offset,
|
unsigned int* offset,
|
||||||
KeyEncodingContext context);
|
KeyEncodingContext context);
|
||||||
|
|
||||||
static KeyObjectData GetPrivateKeyFromJs(
|
static KeyObjectData GetPrivateKeyFromJs(
|
||||||
const v8::FunctionCallbackInfo<v8::Value>& args,
|
const v8::FunctionCallbackInfo<v8::Value>& args,
|
||||||
@ -112,18 +78,20 @@ class KeyObjectData final : public MemoryRetainer {
|
|||||||
static KeyObjectData GetPublicOrPrivateKeyFromJs(
|
static KeyObjectData GetPublicOrPrivateKeyFromJs(
|
||||||
const v8::FunctionCallbackInfo<v8::Value>& args, unsigned int* offset);
|
const v8::FunctionCallbackInfo<v8::Value>& args, unsigned int* offset);
|
||||||
|
|
||||||
static NonCopyableMaybe<PrivateKeyEncodingConfig> GetPrivateKeyEncodingFromJs(
|
static v8::Maybe<ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig>
|
||||||
const v8::FunctionCallbackInfo<v8::Value>& args,
|
GetPrivateKeyEncodingFromJs(const v8::FunctionCallbackInfo<v8::Value>& args,
|
||||||
unsigned int* offset,
|
unsigned int* offset,
|
||||||
KeyEncodingContext context);
|
KeyEncodingContext context);
|
||||||
|
|
||||||
v8::Maybe<void> ToEncodedPublicKey(Environment* env,
|
v8::Maybe<void> ToEncodedPublicKey(
|
||||||
const PublicKeyEncodingConfig& config,
|
Environment* env,
|
||||||
v8::Local<v8::Value>* out);
|
const ncrypto::EVPKeyPointer::PublicKeyEncodingConfig& config,
|
||||||
|
v8::Local<v8::Value>* out);
|
||||||
|
|
||||||
v8::Maybe<void> ToEncodedPrivateKey(Environment* env,
|
v8::Maybe<void> ToEncodedPrivateKey(
|
||||||
const PrivateKeyEncodingConfig& config,
|
Environment* env,
|
||||||
v8::Local<v8::Value>* out);
|
const ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig& config,
|
||||||
|
v8::Local<v8::Value>* out);
|
||||||
|
|
||||||
inline KeyObjectData addRef() const {
|
inline KeyObjectData addRef() const {
|
||||||
return KeyObjectData(key_type_, mutex_, data_);
|
return KeyObjectData(key_type_, mutex_, data_);
|
||||||
@ -204,9 +172,9 @@ class KeyObjectHandle : public BaseObject {
|
|||||||
|
|
||||||
v8::MaybeLocal<v8::Value> ExportSecretKey() const;
|
v8::MaybeLocal<v8::Value> ExportSecretKey() const;
|
||||||
v8::MaybeLocal<v8::Value> ExportPublicKey(
|
v8::MaybeLocal<v8::Value> ExportPublicKey(
|
||||||
const PublicKeyEncodingConfig& config) const;
|
const ncrypto::EVPKeyPointer::PublicKeyEncodingConfig& config) const;
|
||||||
v8::MaybeLocal<v8::Value> ExportPrivateKey(
|
v8::MaybeLocal<v8::Value> ExportPrivateKey(
|
||||||
const PrivateKeyEncodingConfig& config) const;
|
const ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig& config) const;
|
||||||
|
|
||||||
KeyObjectHandle(Environment* env,
|
KeyObjectHandle(Environment* env,
|
||||||
v8::Local<v8::Object> wrap);
|
v8::Local<v8::Object> wrap);
|
||||||
|
@ -689,12 +689,22 @@ class ArrayBufferOrViewContents {
|
|||||||
return std::move(buf).release(size());
|
return std::move(buf).release(size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline ncrypto::DataPointer ToDataPointer() const {
|
||||||
|
if (empty()) return {};
|
||||||
|
if (auto dp = ncrypto::DataPointer::Alloc(size())) {
|
||||||
|
memcpy(dp.get(), data(), size());
|
||||||
|
return dp;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
template <typename M>
|
template <typename M>
|
||||||
void CopyTo(M* dest, size_t len) const {
|
void CopyTo(M* dest, size_t len) const {
|
||||||
static_assert(sizeof(M) == 1, "sizeof(M) must equal 1");
|
static_assert(sizeof(M) == 1, "sizeof(M) must equal 1");
|
||||||
len = std::min(len, size());
|
len = std::min(len, size());
|
||||||
if (len > 0 && data() != nullptr)
|
if (len > 0 && data() != nullptr) {
|
||||||
memcpy(dest, data(), len);
|
memcpy(dest, data(), len);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user