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:
James M Snell 2024-11-02 08:11:28 -07:00 committed by GitHub
parent 91bce94010
commit 5b9bf39b47
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 466 additions and 374 deletions

View File

@ -80,7 +80,7 @@ std::optional<std::string> CryptoErrorList::pop_front() {
// ============================================================================
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)
@ -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() {
return EVPKeyPointer(EVP_PKEY_new());
}
@ -1661,14 +1688,13 @@ EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePublicKeyPEM(
}
EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePublicKey(
PKFormatType format,
PKEncodingType encoding,
const PublicKeyEncodingConfig& config,
const Buffer<const unsigned char>& buffer) {
if (format == PKFormatType::PEM) {
if (config.format == PKFormatType::PEM) {
return TryParsePublicKeyPEM(buffer);
}
if (format != PKFormatType::DER) {
if (config.format != PKFormatType::DER) {
return ParseKeyResult(PKParseError::FAILED);
}
@ -1676,12 +1702,12 @@ EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePublicKey(
EVP_PKEY* key = nullptr;
if (encoding == PKEncodingType::PKCS1 &&
if (config.type == PKEncodingType::PKCS1 &&
(key = d2i_PublicKey(EVP_PKEY_RSA, nullptr, &start, buffer.len))) {
return EVPKeyPointer::ParseKeyResult(EVPKeyPointer(key));
}
if (encoding == PKEncodingType::SPKI &&
if (config.type == PKEncodingType::SPKI &&
(key = d2i_PUBKEY(nullptr, &start, buffer.len))) {
return EVPKeyPointer::ParseKeyResult(EVPKeyPointer(key));
}
@ -1689,13 +1715,34 @@ EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePublicKey(
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(
PKFormatType format,
PKEncodingType encoding,
std::optional<Buffer<char>> maybe_passphrase,
const PrivateKeyEncodingConfig& config,
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 (ERR_GET_LIB(err) == ERR_LIB_PEM &&
ERR_GET_REASON(err) == PEM_R_BAD_PASSWORD_READ &&
@ -1708,24 +1755,23 @@ EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePrivateKey(
return ParseKeyResult(std::move(pkey));
};
Buffer<char>* passphrase = nullptr;
if (maybe_passphrase.has_value()) {
passphrase = &maybe_passphrase.value();
}
auto bio = BIOPointer::New(buffer);
if (!bio) return ParseKeyResult(PKParseError::FAILED);
if (format == PKFormatType::PEM) {
auto key = PEM_read_bio_PrivateKey(bio.get(), nullptr, PasswordCallback, passphrase);
return keyOrError(EVPKeyPointer(key), maybe_passphrase.has_value());
auto passphrase = GetPassphrase(config);
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);
}
switch (encoding) {
switch (config.type) {
case PKEncodingType::PKCS1: {
auto key = d2i_PrivateKey_bio(bio.get(), nullptr);
return keyOrError(EVPKeyPointer(key));
@ -1735,8 +1781,8 @@ EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePrivateKey(
auto key = d2i_PKCS8PrivateKey_bio(bio.get(),
nullptr,
PasswordCallback,
passphrase);
return keyOrError(EVPKeyPointer(key), maybe_passphrase.has_value());
config.passphrase.has_value() ? &passphrase : nullptr);
return keyOrError(EVPKeyPointer(key), config.passphrase.has_value());
}
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

View File

@ -386,13 +386,13 @@ public:
// SubjectPublicKeyInfo according to X.509.
SPKI,
// ECPrivateKey according to SEC1.
SEC1
SEC1,
};
enum class PKFormatType {
DER,
PEM,
JWK
JWK,
};
enum class PKParseError {
@ -402,18 +402,36 @@ public:
};
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(
PKFormatType format,
PKEncodingType encoding,
const PublicKeyEncodingConfig& config,
const Buffer<const unsigned char>& buffer);
static ParseKeyResult TryParsePublicKeyPEM(
const Buffer<const unsigned char>& buffer);
static ParseKeyResult TryParsePrivateKey(
PKFormatType format,
PKEncodingType encoding,
std::optional<Buffer<char>> passphrase,
const PrivateKeyEncodingConfig& config,
const Buffer<const unsigned char>& buffer);
EVPKeyPointer() = default;
@ -441,9 +459,11 @@ public:
size_t rawPrivateKeySize() const;
DataPointer rawPublicKey() const;
DataPointer rawPrivateKey() const;
BIOPointer derPublicKey() const;
Result<BIOPointer, bool> writePrivateKey(const PrivateKeyEncodingConfig& config) const;
Result<BIOPointer, bool> writePublicKey(const PublicKeyEncodingConfig& config) const;
EVPKeyCtxPointer newCtx() const;
static bool IsRSAPrivateKey(const Buffer<const unsigned char>& buffer);

View File

@ -48,8 +48,7 @@ Maybe<void> NidKeyPairGenTraits::AdditionalConfig(
EVPKeyCtxPointer NidKeyPairGenTraits::Setup(NidKeyPairGenConfig* params) {
EVPKeyCtxPointer ctx =
EVPKeyCtxPointer(EVP_PKEY_CTX_new_id(params->params.id, nullptr));
if (!ctx || EVP_PKEY_keygen_init(ctx.get()) <= 0)
return EVPKeyCtxPointer();
if (!ctx || EVP_PKEY_keygen_init(ctx.get()) <= 0) return {};
return ctx;
}

View File

@ -147,19 +147,16 @@ struct KeyPairGenTraits final {
// process input parameters. This allows each job to have a variable
// number of input parameters specific to each job type.
if (KeyPairAlgorithmTraits::AdditionalConfig(mode, args, offset, params)
.IsNothing()) {
.IsNothing() ||
!KeyObjectData::GetPublicKeyEncodingFromJs(
args, offset, kKeyContextGenerate)
.To(&params->public_key_encoding) ||
!KeyObjectData::GetPrivateKeyEncodingFromJs(
args, offset, kKeyContextGenerate)
.To(&params->private_key_encoding)) {
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();
}
@ -230,8 +227,8 @@ struct SecretKeyGenTraits final {
template <typename AlgorithmParams>
struct KeyPairGenConfig final : public MemoryRetainer {
PublicKeyEncodingConfig public_key_encoding;
PrivateKeyEncodingConfig private_key_encoding;
ncrypto::EVPKeyPointer::PublicKeyEncodingConfig public_key_encoding;
ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig private_key_encoding;
KeyObjectData key;
AlgorithmParams params;
@ -245,7 +242,7 @@ struct KeyPairGenConfig final : public MemoryRetainer {
explicit KeyPairGenConfig(KeyPairGenConfig&& other) noexcept
: public_key_encoding(other.public_key_encoding),
private_key_encoding(
std::forward<PrivateKeyEncodingConfig>(
std::forward<ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig>(
other.private_key_encoding)),
key(std::move(other.key)),
params(std::move(other.params)) {}
@ -258,9 +255,10 @@ struct KeyPairGenConfig final : public MemoryRetainer {
void MemoryInfo(MemoryTracker* tracker) const override {
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",
private_key_encoding.passphrase_->size());
passphrase.size());
}
tracker->TrackField("params", params);
}

View File

@ -25,6 +25,7 @@ using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
using v8::Int32;
using v8::Isolate;
using v8::Just;
using v8::JustVoid;
using v8::Local;
using v8::Maybe;
@ -40,199 +41,86 @@ using v8::Value;
namespace crypto {
namespace {
void GetKeyFormatAndTypeFromJs(
AsymmetricKeyEncodingConfig* config,
const FunctionCallbackInfo<Value>& args,
unsigned int* offset,
KeyEncodingContext context) {
Maybe<ncrypto::EVPKeyPointer::AsymmetricKeyEncodingConfig>
GetKeyFormatAndTypeFromJs(const FunctionCallbackInfo<Value>& args,
unsigned int* offset,
KeyEncodingContext context) {
ncrypto::EVPKeyPointer::AsymmetricKeyEncodingConfig config;
// During key pair generation, it is possible not to specify a key encoding,
// which will lead to a key object being returned.
if (args[*offset]->IsUndefined()) {
CHECK_EQ(context, kKeyContextGenerate);
CHECK(args[*offset + 1]->IsUndefined());
config->output_key_object_ = true;
config.output_key_object = true;
} else {
config->output_key_object_ = false;
config.output_key_object = false;
CHECK(args[*offset]->IsInt32());
config->format_ = static_cast<PKFormatType>(
config.format = static_cast<ncrypto::EVPKeyPointer::PKFormatType>(
args[*offset].As<Int32>()->Value());
if (args[*offset + 1]->IsInt32()) {
config->type_ =
static_cast<PKEncodingType>(args[*offset + 1].As<Int32>()->Value());
config.type = static_cast<ncrypto::EVPKeyPointer::PKEncodingType>(
args[*offset + 1].As<Int32>()->Value());
} else {
CHECK(
(context == kKeyContextInput &&
config->format_ == kKeyFormatPEM) ||
(context == kKeyContextGenerate &&
config->format_ == kKeyFormatJWK));
CHECK((context == kKeyContextInput &&
config.format == ncrypto::EVPKeyPointer::PKFormatType::PEM) ||
(context == kKeyContextGenerate &&
config.format == ncrypto::EVPKeyPointer::PKFormatType::JWK));
CHECK(args[*offset + 1]->IsNullOrUndefined());
config->type_ = std::nullopt;
config.type = ncrypto::EVPKeyPointer::PKEncodingType::PKCS1;
}
}
*offset += 2;
return Just(config);
}
MaybeLocal<Value> BIOToStringOrBuffer(Environment* env,
const BIOPointer& bio,
PKFormatType format) {
MaybeLocal<Value> BIOToStringOrBuffer(
Environment* env,
const BIOPointer& bio,
const ncrypto::EVPKeyPointer::AsymmetricKeyEncodingConfig& config) {
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.
return String::NewFromUtf8(env->isolate(), bptr->data,
NewStringType::kNormal,
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)
return String::NewFromUtf8(
env->isolate(), bptr->data, NewStringType::kNormal, bptr->length)
.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,
OSSL3_CONST EVP_PKEY* pkey,
const PrivateKeyEncodingConfig& config) {
auto bio = BIOPointer::NewMem();
CHECK(bio);
// If an empty string was passed as the passphrase, the ByteSource might
// contain a null pointer, which OpenSSL will ignore, causing it to invoke its
// 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);
}
MaybeLocal<Value> WritePrivateKey(
Environment* env,
const EVPKeyPointer& pkey,
const ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig& config) {
CHECK(pkey);
auto res = pkey.writePrivateKey(config);
if (res) {
return BIOToStringOrBuffer(env, std::move(res.value), config);
}
MarkPopErrorOnReturn mark_pop_error_on_return;
bool err;
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_);
ThrowCryptoError(
env, res.openssl_error.value_or(0), "Failed to encode private key");
return MaybeLocal<Value>();
}
bool WritePublicKeyInner(OSSL3_CONST EVP_PKEY* pkey,
const BIOPointer& bio,
const PublicKeyEncodingConfig& config) {
if (config.type_.value() == kKeyEncodingPKCS1) {
// PKCS#1 is only valid 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.
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,
const EVPKeyPointer& pkey,
const ncrypto::EVPKeyPointer::PublicKeyEncodingConfig& config) {
CHECK(pkey);
auto res = pkey.writePublicKey(config);
if (res) {
return BIOToStringOrBuffer(env, res.value, config);
}
}
MaybeLocal<Value> WritePublicKey(Environment* env,
OSSL3_CONST EVP_PKEY* pkey,
const PublicKeyEncodingConfig& config) {
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_);
ThrowCryptoError(
env, res.openssl_error.value_or(0), "Failed to encode public key");
return MaybeLocal<Value>();
}
Maybe<void> ExportJWKSecretKey(Environment* env,
@ -247,12 +135,11 @@ Maybe<void> ExportJWKSecretKey(Environment* env,
key.GetSymmetricKeySize(),
BASE64URL,
&error);
if (key_data.IsEmpty()) {
if (!key_data.ToLocal(&raw)) {
CHECK(!error.IsEmpty());
env->isolate()->ThrowException(error);
return Nothing<void>();
}
if (!key_data.ToLocal(&raw)) return Nothing<void>();
if (target->Set(
env->context(),
@ -277,8 +164,8 @@ KeyObjectData ImportJWKSecretKey(Environment* env, Local<Object> jwk) {
}
static_assert(String::kMaxLength <= INT_MAX);
auto key_data = ByteSource::FromEncodedString(env, key.As<String>());
return KeyObjectData::CreateSecret(std::move(key_data));
return KeyObjectData::CreateSecret(
ByteSource::FromEncodedString(env, key.As<String>()));
}
Maybe<void> ExportJWKAsymmetricKey(Environment* env,
@ -290,7 +177,8 @@ Maybe<void> ExportJWKAsymmetricKey(Environment* env,
if (handleRsaPss) return ExportJWKRsaKey(env, key, target);
break;
}
case EVP_PKEY_RSA: return ExportJWKRsaKey(env, key, target);
case EVP_PKEY_RSA:
return ExportJWKRsaKey(env, key, target);
case EVP_PKEY_EC:
return ExportJWKEcKey(env, key, target);
case EVP_PKEY_ED25519:
@ -299,7 +187,8 @@ Maybe<void> ExportJWKAsymmetricKey(Environment* env,
// Fall through
case EVP_PKEY_X25519:
// 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);
return Nothing<void>();
@ -354,38 +243,22 @@ Maybe<void> GetAsymmetricKeyDetail(Environment* env,
KeyObjectData TryParsePrivateKey(
Environment* env,
const PrivateKeyEncodingConfig& config,
const ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig& config,
const ncrypto::Buffer<const unsigned char>& buffer) {
std::optional<ncrypto::Buffer<char>> maybePassphrase = std::nullopt;
if (config.passphrase_.get() != nullptr) {
maybePassphrase = ncrypto::Buffer<char>{
.data = const_cast<char*>(config.passphrase_->data<char>()),
.len = config.passphrase_->size(),
};
auto res = EVPKeyPointer::TryParsePrivateKey(config, buffer);
if (res) {
return KeyObjectData::CreateAsymmetric(KeyType::kKeyTypePrivate,
std::move(res.value));
}
auto res = EVPKeyPointer::TryParsePrivateKey(
static_cast<EVPKeyPointer::PKFormatType>(config.format_),
static_cast<EVPKeyPointer::PKEncodingType>(
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 {};
}
if (res.error.value() == EVPKeyPointer::PKParseError::NEED_PASSPHRASE) {
THROW_ERR_MISSING_PASSPHRASE(env, "Passphrase required for encrypted key");
} else {
ThrowCryptoError(
env, res.openssl_error.value_or(0), "Failed to read private key");
return {};
}
return KeyObjectData::CreateAsymmetric(KeyType::kKeyTypePrivate,
std::move(res.value));
return {};
}
} // namespace
// This maps true to JustVoid and false to Nothing<void>().
static inline Maybe<void> NothingIfFalse(bool b) {
@ -408,58 +281,61 @@ Maybe<void> ExportJWKInner(Environment* env,
UNREACHABLE();
}
}
} // namespace
Maybe<void> KeyObjectData::ToEncodedPublicKey(
Environment* env,
const PublicKeyEncodingConfig& config,
const ncrypto::EVPKeyPointer::PublicKeyEncodingConfig& config,
Local<Value>* out) {
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
// private key.
return NothingIfFalse(
KeyObjectHandle::Create(env, addRefWithType(KeyType::kKeyTypePublic))
.ToLocal(out));
} else if (config.format_ == kKeyFormatJWK) {
} else if (config.format == ncrypto::EVPKeyPointer::PKFormatType::JWK) {
*out = Object::New(env->isolate());
return ExportJWKInner(
env, addRefWithType(KeyType::kKeyTypePublic), *out, false);
}
return NothingIfFalse(
WritePublicKey(env, GetAsymmetricKey().get(), config).ToLocal(out));
WritePublicKey(env, GetAsymmetricKey(), config).ToLocal(out));
}
Maybe<void> KeyObjectData::ToEncodedPrivateKey(
Environment* env,
const PrivateKeyEncodingConfig& config,
const ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig& config,
Local<Value>* out) {
CHECK(key_type_ != KeyType::kKeyTypeSecret);
if (config.output_key_object_) {
if (config.output_key_object) {
return NothingIfFalse(
KeyObjectHandle::Create(env, addRefWithType(KeyType::kKeyTypePrivate))
.ToLocal(out));
} else if (config.format_ == kKeyFormatJWK) {
} else if (config.format == ncrypto::EVPKeyPointer::PKFormatType::JWK) {
*out = Object::New(env->isolate());
return ExportJWKInner(
env, addRefWithType(KeyType::kKeyTypePrivate), *out, false);
}
return NothingIfFalse(
WritePrivateKey(env, GetAsymmetricKey().get(), config).ToLocal(out));
WritePrivateKey(env, GetAsymmetricKey(), config).ToLocal(out));
}
NonCopyableMaybe<PrivateKeyEncodingConfig>
Maybe<ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig>
KeyObjectData::GetPrivateKeyEncodingFromJs(
const FunctionCallbackInfo<Value>& args,
unsigned int* offset,
KeyEncodingContext context) {
Environment* env = Environment::GetCurrent(args);
PrivateKeyEncodingConfig result;
GetKeyFormatAndTypeFromJs(&result, args, offset, context);
ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig config;
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)
(*offset)++;
} else {
@ -467,44 +343,43 @@ KeyObjectData::GetPrivateKeyEncodingFromJs(
if (context != kKeyContextInput) {
if (args[*offset]->IsString()) {
Utf8Value cipher_name(env->isolate(), args[*offset]);
result.cipher_ = EVP_get_cipherbyname(*cipher_name);
if (result.cipher_ == nullptr) {
config.cipher = EVP_get_cipherbyname(*cipher_name);
if (config.cipher == nullptr) {
THROW_ERR_CRYPTO_UNKNOWN_CIPHER(env);
return NonCopyableMaybe<PrivateKeyEncodingConfig>();
return Nothing<ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig>();
}
needs_passphrase = true;
} else {
CHECK(args[*offset]->IsNullOrUndefined());
result.cipher_ = nullptr;
config.cipher = nullptr;
}
(*offset)++;
}
if (IsAnyBufferSource(args[*offset])) {
CHECK_IMPLIES(context != kKeyContextInput, result.cipher_ != nullptr);
CHECK_IMPLIES(context != kKeyContextInput, config.cipher != nullptr);
ArrayBufferOrViewContents<char> passphrase(args[*offset]);
if (!passphrase.CheckSizeInt32()) [[unlikely]] {
THROW_ERR_OUT_OF_RANGE(env, "passphrase is too big");
return NonCopyableMaybe<PrivateKeyEncodingConfig>();
return Nothing<ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig>();
}
result.passphrase_ = NonCopyableMaybe<ByteSource>(
passphrase.ToNullTerminatedCopy());
config.passphrase = passphrase.ToDataPointer();
} else {
CHECK(args[*offset]->IsNullOrUndefined() && !needs_passphrase);
}
}
(*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,
unsigned int* offset,
KeyEncodingContext context) {
PublicKeyEncodingConfig result;
GetKeyFormatAndTypeFromJs(&result, args, offset, context);
return result;
return GetKeyFormatAndTypeFromJs(args, offset, context);
}
KeyObjectData KeyObjectData::GetPrivateKeyFromJs(
@ -513,14 +388,17 @@ KeyObjectData KeyObjectData::GetPrivateKeyFromJs(
bool allow_key_object) {
if (args[*offset]->IsString() || IsAnyBufferSource(args[*offset])) {
Environment* env = Environment::GetCurrent(args);
ByteSource key = ByteSource::FromStringOrBuffer(env, args[(*offset)++]);
NonCopyableMaybe<PrivateKeyEncodingConfig> config =
GetPrivateKeyEncodingFromJs(args, offset, kKeyContextInput);
auto key = ByteSource::FromStringOrBuffer(env, args[(*offset)++]);
ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig config;
if (!GetPrivateKeyEncodingFromJs(args, offset, kKeyContextInput)
.To(&config)) {
return {};
}
if (config.IsEmpty()) return {};
return TryParsePrivateKey(
env,
config.Release(),
config,
ncrypto::Buffer<const unsigned char>{
.data = reinterpret_cast<const unsigned char*>(key.data()),
.len = key.size(),
@ -544,70 +422,62 @@ KeyObjectData KeyObjectData::GetPublicOrPrivateKeyFromJs(
THROW_ERR_OUT_OF_RANGE(env, "keyData is too big");
return {};
}
NonCopyableMaybe<PrivateKeyEncodingConfig> config_ =
KeyObjectData::GetPrivateKeyEncodingFromJs(
args, offset, kKeyContextInput);
if (config_.IsEmpty()) return {};
PrivateKeyEncodingConfig config = config_.Release();
ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig config;
if (!KeyObjectData::GetPrivateKeyEncodingFromJs(
args, offset, kKeyContextInput)
.To(&config)) {
return {};
}
ncrypto::Buffer<const unsigned char> buffer = {
.data = reinterpret_cast<const unsigned char*>(data.data()),
.len = data.size(),
};
std::optional<ncrypto::Buffer<char>> maybePassphrase = std::nullopt;
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) {
if (config.format == ncrypto::EVPKeyPointer::PKFormatType::PEM) {
// For PEM, we can easily determine whether it is a public or private key
// by looking for the respective PEM tags.
auto res = EVPKeyPointer::TryParsePublicKeyPEM(buffer);
if (!res) {
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 {};
if (res) {
return CreateAsymmetric(kKeyTypePublic, std::move(res.value));
}
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
// easy, but PKCS#1 can be a public key or a private key.
bool is_public = ([&] {
switch (config.type_.value()) {
case kKeyEncodingPKCS1:
static const auto is_public = [](const auto& config,
const auto& buffer) -> bool {
switch (config.type) {
case ncrypto::EVPKeyPointer::PKEncodingType::PKCS1:
return !EVPKeyPointer::IsRSAPrivateKey(buffer);
case kKeyEncodingSPKI:
case ncrypto::EVPKeyPointer::PKEncodingType::SPKI:
return true;
case kKeyEncodingPKCS8:
case ncrypto::EVPKeyPointer::PKEncodingType::PKCS8:
return false;
case kKeyEncodingSEC1:
case ncrypto::EVPKeyPointer::PKEncodingType::SEC1:
return false;
default:
UNREACHABLE("Invalid key encoding type");
}
})();
};
if (is_public) {
auto res = EVPKeyPointer::TryParsePublicKey(
static_cast<EVPKeyPointer::PKFormatType>(config.format_),
static_cast<EVPKeyPointer::PKEncodingType>(config.type_.value()),
buffer);
if (!res) {
ThrowCryptoError(env,
res.openssl_error.value_or(0),
"Failed to read asymmetric key");
return {};
if (is_public(config, buffer)) {
auto res = EVPKeyPointer::TryParsePublicKey(config, buffer);
if (res) {
return CreateAsymmetric(KeyType::kKeyTypePublic, std::move(res.value));
}
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);
@ -1120,20 +990,25 @@ void KeyObjectHandle::Export(const FunctionCallbackInfo<Value>& args) {
result = key->ExportSecretKey();
} else if (type == kKeyTypePublic) {
unsigned int offset = 0;
PublicKeyEncodingConfig config = KeyObjectData::GetPublicKeyEncodingFromJs(
args, &offset, kKeyContextExport);
ncrypto::EVPKeyPointer::PublicKeyEncodingConfig config;
if (!KeyObjectData::GetPublicKeyEncodingFromJs(
args, &offset, kKeyContextExport)
.To(&config)) {
return;
}
CHECK_EQ(offset, static_cast<unsigned int>(args.Length()));
result = key->ExportPublicKey(config);
} else {
CHECK_EQ(type, kKeyTypePrivate);
unsigned int offset = 0;
NonCopyableMaybe<PrivateKeyEncodingConfig> config =
KeyObjectData::GetPrivateKeyEncodingFromJs(
args, &offset, kKeyContextExport);
if (config.IsEmpty())
ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig config;
if (!KeyObjectData::GetPrivateKeyEncodingFromJs(
args, &offset, kKeyContextExport)
.To(&config)) {
return;
}
CHECK_EQ(offset, static_cast<unsigned int>(args.Length()));
result = key->ExportPrivateKey(config.Release());
result = key->ExportPrivateKey(config);
}
if (!result.IsEmpty())
@ -1147,13 +1022,13 @@ MaybeLocal<Value> KeyObjectHandle::ExportSecretKey() const {
}
MaybeLocal<Value> KeyObjectHandle::ExportPublicKey(
const PublicKeyEncodingConfig& config) const {
return WritePublicKey(env(), data_.GetAsymmetricKey().get(), config);
const ncrypto::EVPKeyPointer::PublicKeyEncodingConfig& config) const {
return WritePublicKey(env(), data_.GetAsymmetricKey(), config);
}
MaybeLocal<Value> KeyObjectHandle::ExportPrivateKey(
const PrivateKeyEncodingConfig& config) const {
return WritePrivateKey(env(), data_.GetAsymmetricKey().get(), config);
const ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig& config) const {
return WritePrivateKey(env(), data_.GetAsymmetricKey(), config);
}
void KeyObjectHandle::ExportJWK(
@ -1308,11 +1183,25 @@ void Initialize(Environment* env, Local<Object> target) {
FIXED_ONE_BYTE_STRING(env->isolate(), "KeyObjectHandle"),
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, kWebCryptoKeyFormatPKCS8);
NODE_DEFINE_CONSTANT(target, kWebCryptoKeyFormatSPKI);
NODE_DEFINE_CONSTANT(target, kWebCryptoKeyFormatJWK);
NODE_DEFINE_CONSTANT(target, EVP_PKEY_ED25519);
NODE_DEFINE_CONSTANT(target, EVP_PKEY_ED448);
NODE_DEFINE_CONSTANT(target, EVP_PKEY_X25519);

View File

@ -18,24 +18,6 @@
namespace node {
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 {
kKeyTypeSecret,
@ -58,22 +40,6 @@ enum class ParseKeyResult {
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.
class KeyObjectData final : public MemoryRetainer {
public:
@ -99,10 +65,10 @@ class KeyObjectData final : public MemoryRetainer {
Mutex& mutex() const;
static PublicKeyEncodingConfig GetPublicKeyEncodingFromJs(
const v8::FunctionCallbackInfo<v8::Value>& args,
unsigned int* offset,
KeyEncodingContext context);
static v8::Maybe<ncrypto::EVPKeyPointer::PublicKeyEncodingConfig>
GetPublicKeyEncodingFromJs(const v8::FunctionCallbackInfo<v8::Value>& args,
unsigned int* offset,
KeyEncodingContext context);
static KeyObjectData GetPrivateKeyFromJs(
const v8::FunctionCallbackInfo<v8::Value>& args,
@ -112,18 +78,20 @@ class KeyObjectData final : public MemoryRetainer {
static KeyObjectData GetPublicOrPrivateKeyFromJs(
const v8::FunctionCallbackInfo<v8::Value>& args, unsigned int* offset);
static NonCopyableMaybe<PrivateKeyEncodingConfig> GetPrivateKeyEncodingFromJs(
const v8::FunctionCallbackInfo<v8::Value>& args,
unsigned int* offset,
KeyEncodingContext context);
static v8::Maybe<ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig>
GetPrivateKeyEncodingFromJs(const v8::FunctionCallbackInfo<v8::Value>& args,
unsigned int* offset,
KeyEncodingContext context);
v8::Maybe<void> ToEncodedPublicKey(Environment* env,
const PublicKeyEncodingConfig& config,
v8::Local<v8::Value>* out);
v8::Maybe<void> ToEncodedPublicKey(
Environment* env,
const ncrypto::EVPKeyPointer::PublicKeyEncodingConfig& config,
v8::Local<v8::Value>* out);
v8::Maybe<void> ToEncodedPrivateKey(Environment* env,
const PrivateKeyEncodingConfig& config,
v8::Local<v8::Value>* out);
v8::Maybe<void> ToEncodedPrivateKey(
Environment* env,
const ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig& config,
v8::Local<v8::Value>* out);
inline KeyObjectData addRef() const {
return KeyObjectData(key_type_, mutex_, data_);
@ -204,9 +172,9 @@ class KeyObjectHandle : public BaseObject {
v8::MaybeLocal<v8::Value> ExportSecretKey() const;
v8::MaybeLocal<v8::Value> ExportPublicKey(
const PublicKeyEncodingConfig& config) const;
const ncrypto::EVPKeyPointer::PublicKeyEncodingConfig& config) const;
v8::MaybeLocal<v8::Value> ExportPrivateKey(
const PrivateKeyEncodingConfig& config) const;
const ncrypto::EVPKeyPointer::PrivateKeyEncodingConfig& config) const;
KeyObjectHandle(Environment* env,
v8::Local<v8::Object> wrap);

View File

@ -689,12 +689,22 @@ class ArrayBufferOrViewContents {
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>
void CopyTo(M* dest, size_t len) const {
static_assert(sizeof(M) == 1, "sizeof(M) must equal 1");
len = std::min(len, size());
if (len > 0 && data() != nullptr)
if (len > 0 && data() != nullptr) {
memcpy(dest, data(), len);
}
}
private: