mirror of
https://github.com/nodejs/node.git
synced 2026-10-14 10:26:26 -04:00
crypto: decorate async crypto job errors with OpenSSL error details
PR-URL: https://github.com/nodejs/node/pull/62348 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
1 parent
e78ccd8017
commit
74509b166a
32 files changed
+443
-80
No files matched your search
@@ -124,7 +124,8 @@ Maybe<void> Argon2Traits::AdditionalConfig(
|
||||
bool Argon2Traits::DeriveBits(Environment* env,
|
||||
const Argon2Config& config,
|
||||
ByteSource* out,
|
||||
CryptoJobMode mode) {
|
||||
CryptoJobMode mode,
|
||||
CryptoErrorStore* errors) {
|
||||
// If the config.length is zero-length, just return an empty buffer.
|
||||
// It's useless, yes, but allowed via the API.
|
||||
if (config.keylen == 0) {
|
||||
@@ -144,7 +145,10 @@ bool Argon2Traits::DeriveBits(Environment* env,
|
||||
config.ad,
|
||||
config.type);
|
||||
|
||||
if (!dp) return false;
|
||||
if (!dp) {
|
||||
errors->Insert(NodeCryptoError::ARGON2_FAILED);
|
||||
return false;
|
||||
}
|
||||
DCHECK(!dp.isSecure());
|
||||
*out = ByteSource::Allocated(dp.release());
|
||||
return true;
|
||||
|
||||
@@ -59,7 +59,8 @@ struct Argon2Traits final {
|
||||
static bool DeriveBits(Environment* env,
|
||||
const Argon2Config& config,
|
||||
ByteSource* out,
|
||||
CryptoJobMode mode);
|
||||
CryptoJobMode mode,
|
||||
CryptoErrorStore* errors);
|
||||
|
||||
static v8::MaybeLocal<v8::Value> EncodeOutput(Environment* env,
|
||||
const Argon2Config& config,
|
||||
|
||||
@@ -504,16 +504,11 @@ MaybeLocal<Value> DHBitsTraits::EncodeOutput(Environment* env,
|
||||
bool DHBitsTraits::DeriveBits(Environment* env,
|
||||
const DHBitsConfig& params,
|
||||
ByteSource* out,
|
||||
CryptoJobMode mode) {
|
||||
CryptoJobMode mode,
|
||||
CryptoErrorStore* errors) {
|
||||
auto dp = DHPointer::stateless(params.private_key.GetAsymmetricKey(),
|
||||
params.public_key.GetAsymmetricKey());
|
||||
if (!dp) {
|
||||
bool can_throw = mode == CryptoJobMode::kCryptoJobSync;
|
||||
|
||||
if (can_throw) {
|
||||
unsigned long err = ERR_get_error(); // NOLINT(runtime/int)
|
||||
if (err) ThrowCryptoError(env, err, "diffieHellman failed");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -83,7 +83,8 @@ struct DHBitsTraits final {
|
||||
static bool DeriveBits(Environment* env,
|
||||
const DHBitsConfig& params,
|
||||
ByteSource* out_,
|
||||
CryptoJobMode mode);
|
||||
CryptoJobMode mode,
|
||||
CryptoErrorStore* errors);
|
||||
|
||||
static v8::MaybeLocal<v8::Value> EncodeOutput(Environment* env,
|
||||
const DHBitsConfig& params,
|
||||
|
||||
@@ -433,7 +433,8 @@ Maybe<void> ECDHBitsTraits::AdditionalConfig(
|
||||
bool ECDHBitsTraits::DeriveBits(Environment* env,
|
||||
const ECDHBitsConfig& params,
|
||||
ByteSource* out,
|
||||
CryptoJobMode mode) {
|
||||
CryptoJobMode mode,
|
||||
CryptoErrorStore* errors) {
|
||||
size_t len = 0;
|
||||
const auto& m_privkey = params.private_.GetAsymmetricKey();
|
||||
const auto& m_pubkey = params.public_.GetAsymmetricKey();
|
||||
@@ -464,8 +465,10 @@ bool ECDHBitsTraits::DeriveBits(Environment* env,
|
||||
const EC_KEY* public_key = m_pubkey;
|
||||
|
||||
const auto group = ECKeyPointer::GetGroup(private_key);
|
||||
if (group == nullptr)
|
||||
if (group == nullptr) {
|
||||
errors->Insert(NodeCryptoError::ECDH_FAILED);
|
||||
return false;
|
||||
}
|
||||
|
||||
CHECK(ECKeyPointer::Check(private_key));
|
||||
CHECK(ECKeyPointer::Check(public_key));
|
||||
|
||||
@@ -80,7 +80,8 @@ struct ECDHBitsTraits final {
|
||||
static bool DeriveBits(Environment* env,
|
||||
const ECDHBitsConfig& params,
|
||||
ByteSource* out_,
|
||||
CryptoJobMode mode);
|
||||
CryptoJobMode mode,
|
||||
CryptoErrorStore* errors);
|
||||
|
||||
static v8::MaybeLocal<v8::Value> EncodeOutput(Environment* env,
|
||||
const ECDHBitsConfig& params,
|
||||
|
||||
@@ -540,7 +540,8 @@ Maybe<void> HashTraits::AdditionalConfig(
|
||||
bool HashTraits::DeriveBits(Environment* env,
|
||||
const HashConfig& params,
|
||||
ByteSource* out,
|
||||
CryptoJobMode mode) {
|
||||
CryptoJobMode mode,
|
||||
CryptoErrorStore* errors) {
|
||||
auto ctx = EVPMDCtxPointer::New();
|
||||
|
||||
if (!ctx.digestInit(params.digest) || !ctx.digestUpdate(params.in))
|
||||
|
||||
@@ -73,7 +73,8 @@ struct HashTraits final {
|
||||
static bool DeriveBits(Environment* env,
|
||||
const HashConfig& params,
|
||||
ByteSource* out,
|
||||
CryptoJobMode mode);
|
||||
CryptoJobMode mode,
|
||||
CryptoErrorStore* errors);
|
||||
|
||||
static v8::MaybeLocal<v8::Value> EncodeOutput(Environment* env,
|
||||
const HashConfig& params,
|
||||
|
||||
@@ -100,7 +100,8 @@ Maybe<void> HKDFTraits::AdditionalConfig(
|
||||
bool HKDFTraits::DeriveBits(Environment* env,
|
||||
const HKDFConfig& params,
|
||||
ByteSource* out,
|
||||
CryptoJobMode mode) {
|
||||
CryptoJobMode mode,
|
||||
CryptoErrorStore* errors) {
|
||||
auto dp = ncrypto::hkdf(params.digest,
|
||||
ncrypto::Buffer<const unsigned char>{
|
||||
.data = reinterpret_cast<const unsigned char*>(
|
||||
@@ -116,7 +117,10 @@ bool HKDFTraits::DeriveBits(Environment* env,
|
||||
.len = params.salt.size(),
|
||||
},
|
||||
params.length);
|
||||
if (!dp) return false;
|
||||
if (!dp) {
|
||||
errors->Insert(NodeCryptoError::HKDF_FAILED);
|
||||
return false;
|
||||
}
|
||||
|
||||
DCHECK(!dp.isSecure());
|
||||
*out = ByteSource::Allocated(dp.release());
|
||||
|
||||
@@ -45,7 +45,8 @@ struct HKDFTraits final {
|
||||
static bool DeriveBits(Environment* env,
|
||||
const HKDFConfig& params,
|
||||
ByteSource* out,
|
||||
CryptoJobMode mode);
|
||||
CryptoJobMode mode,
|
||||
CryptoErrorStore* errors);
|
||||
|
||||
static v8::MaybeLocal<v8::Value> EncodeOutput(Environment* env,
|
||||
const HKDFConfig& params,
|
||||
|
||||
@@ -231,7 +231,8 @@ Maybe<void> HmacTraits::AdditionalConfig(
|
||||
bool HmacTraits::DeriveBits(Environment* env,
|
||||
const HmacConfig& params,
|
||||
ByteSource* out,
|
||||
CryptoJobMode mode) {
|
||||
CryptoJobMode mode,
|
||||
CryptoErrorStore* errors) {
|
||||
auto ctx = HMACCtxPointer::New();
|
||||
|
||||
ncrypto::Buffer<const void> key_buf{
|
||||
|
||||
@@ -76,7 +76,8 @@ struct HmacTraits final {
|
||||
static bool DeriveBits(Environment* env,
|
||||
const HmacConfig& params,
|
||||
ByteSource* out,
|
||||
CryptoJobMode mode);
|
||||
CryptoJobMode mode,
|
||||
CryptoErrorStore* errors);
|
||||
|
||||
static v8::MaybeLocal<v8::Value> EncodeOutput(Environment* env,
|
||||
const HmacConfig& params,
|
||||
|
||||
+17
-16
@@ -51,12 +51,12 @@ namespace {
|
||||
bool DoKEMEncapsulate(Environment* env,
|
||||
const EVPKeyPointer& public_key,
|
||||
ByteSource* out,
|
||||
CryptoJobMode mode) {
|
||||
CryptoJobMode mode,
|
||||
CryptoErrorStore* errors) {
|
||||
auto result = ncrypto::KEM::Encapsulate(public_key);
|
||||
if (!result) {
|
||||
if (mode == kCryptoJobSync) {
|
||||
THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to perform encapsulation");
|
||||
}
|
||||
errors->Insert(NodeCryptoError::ENCAPSULATION_FAILED);
|
||||
errors->SetNodeErrorCode("ERR_CRYPTO_OPERATION_FAILED");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -68,10 +68,8 @@ bool DoKEMEncapsulate(Environment* env,
|
||||
|
||||
auto data = ncrypto::DataPointer::Alloc(total_len);
|
||||
if (!data) {
|
||||
if (mode == kCryptoJobSync) {
|
||||
THROW_ERR_CRYPTO_OPERATION_FAILED(env,
|
||||
"Failed to allocate output buffer");
|
||||
}
|
||||
errors->Insert(NodeCryptoError::ALLOCATION_FAILED);
|
||||
errors->SetNodeErrorCode("ERR_CRYPTO_OPERATION_FAILED");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -97,14 +95,14 @@ bool DoKEMDecapsulate(Environment* env,
|
||||
const EVPKeyPointer& private_key,
|
||||
const ByteSource& ciphertext,
|
||||
ByteSource* out,
|
||||
CryptoJobMode mode) {
|
||||
CryptoJobMode mode,
|
||||
CryptoErrorStore* errors) {
|
||||
ncrypto::Buffer<const void> ciphertext_buf{ciphertext.data(),
|
||||
ciphertext.size()};
|
||||
auto shared_key = ncrypto::KEM::Decapsulate(private_key, ciphertext_buf);
|
||||
if (!shared_key) {
|
||||
if (mode == kCryptoJobSync) {
|
||||
THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to perform decapsulation");
|
||||
}
|
||||
errors->Insert(NodeCryptoError::DECAPSULATION_FAILED);
|
||||
errors->SetNodeErrorCode("ERR_CRYPTO_OPERATION_FAILED");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -137,11 +135,12 @@ Maybe<void> KEMEncapsulateTraits::AdditionalConfig(
|
||||
bool KEMEncapsulateTraits::DeriveBits(Environment* env,
|
||||
const KEMConfiguration& params,
|
||||
ByteSource* out,
|
||||
CryptoJobMode mode) {
|
||||
CryptoJobMode mode,
|
||||
CryptoErrorStore* errors) {
|
||||
Mutex::ScopedLock lock(params.key.mutex());
|
||||
const auto& public_key = params.key.GetAsymmetricKey();
|
||||
|
||||
return DoKEMEncapsulate(env, public_key, out, mode);
|
||||
return DoKEMEncapsulate(env, public_key, out, mode, errors);
|
||||
}
|
||||
|
||||
MaybeLocal<Value> KEMEncapsulateTraits::EncodeOutput(
|
||||
@@ -218,11 +217,13 @@ Maybe<void> KEMDecapsulateTraits::AdditionalConfig(
|
||||
bool KEMDecapsulateTraits::DeriveBits(Environment* env,
|
||||
const KEMConfiguration& params,
|
||||
ByteSource* out,
|
||||
CryptoJobMode mode) {
|
||||
CryptoJobMode mode,
|
||||
CryptoErrorStore* errors) {
|
||||
Mutex::ScopedLock lock(params.key.mutex());
|
||||
const auto& private_key = params.key.GetAsymmetricKey();
|
||||
|
||||
return DoKEMDecapsulate(env, private_key, params.ciphertext, out, mode);
|
||||
return DoKEMDecapsulate(
|
||||
env, private_key, params.ciphertext, out, mode, errors);
|
||||
}
|
||||
|
||||
MaybeLocal<Value> KEMDecapsulateTraits::EncodeOutput(
|
||||
|
||||
@@ -48,7 +48,8 @@ struct KEMEncapsulateTraits final {
|
||||
static bool DeriveBits(Environment* env,
|
||||
const KEMConfiguration& params,
|
||||
ByteSource* out,
|
||||
CryptoJobMode mode);
|
||||
CryptoJobMode mode,
|
||||
CryptoErrorStore* errors);
|
||||
|
||||
static v8::MaybeLocal<v8::Value> EncodeOutput(Environment* env,
|
||||
const KEMConfiguration& params,
|
||||
@@ -71,7 +72,8 @@ struct KEMDecapsulateTraits final {
|
||||
static bool DeriveBits(Environment* env,
|
||||
const KEMConfiguration& params,
|
||||
ByteSource* out,
|
||||
CryptoJobMode mode);
|
||||
CryptoJobMode mode,
|
||||
CryptoErrorStore* errors);
|
||||
|
||||
static v8::MaybeLocal<v8::Value> EncodeOutput(Environment* env,
|
||||
const KEMConfiguration& params,
|
||||
|
||||
@@ -122,7 +122,8 @@ Maybe<void> KmacTraits::AdditionalConfig(
|
||||
bool KmacTraits::DeriveBits(Environment* env,
|
||||
const KmacConfig& params,
|
||||
ByteSource* out,
|
||||
CryptoJobMode mode) {
|
||||
CryptoJobMode mode,
|
||||
CryptoErrorStore* errors) {
|
||||
if (params.length == 0) {
|
||||
*out = ByteSource();
|
||||
return true;
|
||||
@@ -133,6 +134,7 @@ bool KmacTraits::DeriveBits(Environment* env,
|
||||
size_t key_size = params.key.GetSymmetricKeySize();
|
||||
|
||||
if (key_size == 0) {
|
||||
errors->Insert(NodeCryptoError::KMAC_FAILED);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,8 @@ struct KmacTraits final {
|
||||
static bool DeriveBits(Environment* env,
|
||||
const KmacConfig& params,
|
||||
ByteSource* out,
|
||||
CryptoJobMode mode);
|
||||
CryptoJobMode mode,
|
||||
CryptoErrorStore* errors);
|
||||
|
||||
static v8::MaybeLocal<v8::Value> EncodeOutput(Environment* env,
|
||||
const KmacConfig& params,
|
||||
|
||||
@@ -113,7 +113,8 @@ Maybe<void> PBKDF2Traits::AdditionalConfig(
|
||||
bool PBKDF2Traits::DeriveBits(Environment* env,
|
||||
const PBKDF2Config& params,
|
||||
ByteSource* out,
|
||||
CryptoJobMode mode) {
|
||||
CryptoJobMode mode,
|
||||
CryptoErrorStore* errors) {
|
||||
// Both pass and salt may be zero length here.
|
||||
auto dp = ncrypto::pbkdf2(params.digest,
|
||||
ncrypto::Buffer<const char>{
|
||||
@@ -127,7 +128,10 @@ bool PBKDF2Traits::DeriveBits(Environment* env,
|
||||
params.iterations,
|
||||
params.length);
|
||||
|
||||
if (!dp) return false;
|
||||
if (!dp) {
|
||||
errors->Insert(NodeCryptoError::PBKDF2_FAILED);
|
||||
return false;
|
||||
}
|
||||
DCHECK(!dp.isSecure());
|
||||
*out = ByteSource::Allocated(dp.release());
|
||||
return true;
|
||||
|
||||
@@ -58,7 +58,8 @@ struct PBKDF2Traits final {
|
||||
static bool DeriveBits(Environment* env,
|
||||
const PBKDF2Config& params,
|
||||
ByteSource* out,
|
||||
CryptoJobMode mode);
|
||||
CryptoJobMode mode,
|
||||
CryptoErrorStore* errors);
|
||||
|
||||
static v8::MaybeLocal<v8::Value> EncodeOutput(Environment* env,
|
||||
const PBKDF2Config& params,
|
||||
|
||||
@@ -68,7 +68,8 @@ Maybe<void> RandomBytesTraits::AdditionalConfig(
|
||||
bool RandomBytesTraits::DeriveBits(Environment* env,
|
||||
const RandomBytesConfig& params,
|
||||
ByteSource* unused,
|
||||
CryptoJobMode mode) {
|
||||
CryptoJobMode mode,
|
||||
CryptoErrorStore* errors) {
|
||||
return ncrypto::CSPRNG(params.buffer, params.size);
|
||||
}
|
||||
|
||||
@@ -155,7 +156,8 @@ Maybe<void> RandomPrimeTraits::AdditionalConfig(
|
||||
bool RandomPrimeTraits::DeriveBits(Environment* env,
|
||||
const RandomPrimeConfig& params,
|
||||
ByteSource* unused,
|
||||
CryptoJobMode mode) {
|
||||
CryptoJobMode mode,
|
||||
CryptoErrorStore* errors) {
|
||||
return params.prime.generate(
|
||||
BignumPointer::PrimeConfig{
|
||||
.bits = params.bits,
|
||||
@@ -194,7 +196,8 @@ Maybe<void> CheckPrimeTraits::AdditionalConfig(
|
||||
bool CheckPrimeTraits::DeriveBits(Environment* env,
|
||||
const CheckPrimeConfig& params,
|
||||
ByteSource* out,
|
||||
CryptoJobMode mode) {
|
||||
CryptoJobMode mode,
|
||||
CryptoErrorStore* errors) {
|
||||
int ret = params.candidate.isPrime(params.checks, getPrimeCheckCallback(env));
|
||||
if (ret < 0) [[unlikely]]
|
||||
return false;
|
||||
|
||||
@@ -35,7 +35,8 @@ struct RandomBytesTraits final {
|
||||
static bool DeriveBits(Environment* env,
|
||||
const RandomBytesConfig& params,
|
||||
ByteSource* out_,
|
||||
CryptoJobMode mode);
|
||||
CryptoJobMode mode,
|
||||
CryptoErrorStore* errors);
|
||||
|
||||
static v8::MaybeLocal<v8::Value> EncodeOutput(Environment* env,
|
||||
const RandomBytesConfig& params,
|
||||
@@ -70,7 +71,8 @@ struct RandomPrimeTraits final {
|
||||
static bool DeriveBits(Environment* env,
|
||||
const RandomPrimeConfig& params,
|
||||
ByteSource* out_,
|
||||
CryptoJobMode mode);
|
||||
CryptoJobMode mode,
|
||||
CryptoErrorStore* errors);
|
||||
|
||||
static v8::MaybeLocal<v8::Value> EncodeOutput(Environment* env,
|
||||
const RandomPrimeConfig& params,
|
||||
@@ -104,7 +106,8 @@ struct CheckPrimeTraits final {
|
||||
static bool DeriveBits(Environment* env,
|
||||
const CheckPrimeConfig& params,
|
||||
ByteSource* out,
|
||||
CryptoJobMode mode);
|
||||
CryptoJobMode mode,
|
||||
CryptoErrorStore* errors);
|
||||
|
||||
static v8::MaybeLocal<v8::Value> EncodeOutput(Environment* env,
|
||||
const CheckPrimeConfig& params,
|
||||
|
||||
@@ -116,7 +116,8 @@ Maybe<void> ScryptTraits::AdditionalConfig(
|
||||
bool ScryptTraits::DeriveBits(Environment* env,
|
||||
const ScryptConfig& params,
|
||||
ByteSource* out,
|
||||
CryptoJobMode mode) {
|
||||
CryptoJobMode mode,
|
||||
CryptoErrorStore* errors) {
|
||||
// If the params.length is zero-length, just return an empty buffer.
|
||||
// It's useless, yes, but allowed via the API.
|
||||
if (params.length == 0) {
|
||||
@@ -139,7 +140,10 @@ bool ScryptTraits::DeriveBits(Environment* env,
|
||||
params.maxmem,
|
||||
params.length);
|
||||
|
||||
if (!dp) return false;
|
||||
if (!dp) {
|
||||
errors->Insert(NodeCryptoError::SCRYPT_FAILED);
|
||||
return false;
|
||||
}
|
||||
DCHECK(!dp.isSecure());
|
||||
*out = ByteSource::Allocated(dp.release());
|
||||
return true;
|
||||
|
||||
@@ -60,7 +60,8 @@ struct ScryptTraits final {
|
||||
static bool DeriveBits(Environment* env,
|
||||
const ScryptConfig& params,
|
||||
ByteSource* out,
|
||||
CryptoJobMode mode);
|
||||
CryptoJobMode mode,
|
||||
CryptoErrorStore* errors);
|
||||
|
||||
static v8::MaybeLocal<v8::Value> EncodeOutput(Environment* env,
|
||||
const ScryptConfig& params,
|
||||
|
||||
@@ -688,8 +688,8 @@ Maybe<void> SignTraits::AdditionalConfig(
|
||||
bool SignTraits::DeriveBits(Environment* env,
|
||||
const SignConfiguration& params,
|
||||
ByteSource* out,
|
||||
CryptoJobMode mode) {
|
||||
bool can_throw = mode == CryptoJobMode::kCryptoJobSync;
|
||||
CryptoJobMode mode,
|
||||
CryptoErrorStore* errors) {
|
||||
auto context = EVPMDCtxPointer::New();
|
||||
if (!context) [[unlikely]]
|
||||
return false;
|
||||
@@ -699,7 +699,8 @@ bool SignTraits::DeriveBits(Environment* env,
|
||||
params.context_string.size() > 0);
|
||||
|
||||
if (has_context && !SupportsContextString(key)) {
|
||||
if (can_throw) crypto::CheckThrow(env, SignBase::Error::ContextUnsupported);
|
||||
errors->Insert(NodeCryptoError::CONTEXT_UNSUPPORTED);
|
||||
errors->SetNodeErrorCode("ERR_CRYPTO_OPERATION_FAILED");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -728,7 +729,6 @@ bool SignTraits::DeriveBits(Environment* env,
|
||||
})();
|
||||
|
||||
if (!ctx.has_value()) [[unlikely]] {
|
||||
if (can_throw) crypto::CheckThrow(env, SignBase::Error::Init);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -742,7 +742,6 @@ bool SignTraits::DeriveBits(Environment* env,
|
||||
: std::nullopt;
|
||||
|
||||
if (!ApplyRSAOptions(key, *ctx, padding, salt_length)) {
|
||||
if (can_throw) crypto::CheckThrow(env, SignBase::Error::PrivateKey);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -751,7 +750,6 @@ bool SignTraits::DeriveBits(Environment* env,
|
||||
if (key.isOneShotVariant()) {
|
||||
auto data = context.signOneShot(params.data);
|
||||
if (!data) [[unlikely]] {
|
||||
if (can_throw) crypto::CheckThrow(env, SignBase::Error::PrivateKey);
|
||||
return false;
|
||||
}
|
||||
DCHECK(!data.isSecure());
|
||||
@@ -759,7 +757,6 @@ bool SignTraits::DeriveBits(Environment* env,
|
||||
} else {
|
||||
auto data = context.sign(params.data);
|
||||
if (!data) [[unlikely]] {
|
||||
if (can_throw) crypto::CheckThrow(env, SignBase::Error::PrivateKey);
|
||||
return false;
|
||||
}
|
||||
DCHECK(!data.isSecure());
|
||||
|
||||
@@ -143,7 +143,8 @@ struct SignTraits final {
|
||||
static bool DeriveBits(Environment* env,
|
||||
const SignConfiguration& params,
|
||||
ByteSource* out,
|
||||
CryptoJobMode mode);
|
||||
CryptoJobMode mode,
|
||||
CryptoErrorStore* errors);
|
||||
|
||||
static v8::MaybeLocal<v8::Value> EncodeOutput(Environment* env,
|
||||
const SignConfiguration& params,
|
||||
|
||||
@@ -472,7 +472,8 @@ Maybe<void> TurboShakeTraits::AdditionalConfig(
|
||||
bool TurboShakeTraits::DeriveBits(Environment* env,
|
||||
const TurboShakeConfig& params,
|
||||
ByteSource* out,
|
||||
CryptoJobMode mode) {
|
||||
CryptoJobMode mode,
|
||||
CryptoErrorStore* errors) {
|
||||
CHECK_GT(params.output_length, 0);
|
||||
char* buf = MallocOpenSSL<char>(params.output_length);
|
||||
|
||||
@@ -585,7 +586,8 @@ Maybe<void> KangarooTwelveTraits::AdditionalConfig(
|
||||
bool KangarooTwelveTraits::DeriveBits(Environment* env,
|
||||
const KangarooTwelveConfig& params,
|
||||
ByteSource* out,
|
||||
CryptoJobMode mode) {
|
||||
CryptoJobMode mode,
|
||||
CryptoErrorStore* errors) {
|
||||
CHECK_GT(params.output_length, 0);
|
||||
char* buf = MallocOpenSSL<char>(params.output_length);
|
||||
|
||||
|
||||
@@ -42,7 +42,8 @@ struct TurboShakeTraits final {
|
||||
static bool DeriveBits(Environment* env,
|
||||
const TurboShakeConfig& params,
|
||||
ByteSource* out,
|
||||
CryptoJobMode mode);
|
||||
CryptoJobMode mode,
|
||||
CryptoErrorStore* errors);
|
||||
|
||||
static v8::MaybeLocal<v8::Value> EncodeOutput(Environment* env,
|
||||
const TurboShakeConfig& params,
|
||||
@@ -86,7 +87,8 @@ struct KangarooTwelveTraits final {
|
||||
static bool DeriveBits(Environment* env,
|
||||
const KangarooTwelveConfig& params,
|
||||
ByteSource* out,
|
||||
CryptoJobMode mode);
|
||||
CryptoJobMode mode,
|
||||
CryptoErrorStore* errors);
|
||||
|
||||
static v8::MaybeLocal<v8::Value> EncodeOutput(
|
||||
Environment* env, const KangarooTwelveConfig& params, ByteSource* out);
|
||||
|
||||
@@ -230,7 +230,9 @@ void GetOpenSSLSecLevelCrypto(const FunctionCallbackInfo<Value>& args) {
|
||||
|
||||
void CryptoErrorStore::Capture() {
|
||||
errors_.clear();
|
||||
primary_openssl_error_ = 0;
|
||||
while (const uint32_t err = ERR_get_error()) {
|
||||
if (primary_openssl_error_ == 0) primary_openssl_error_ = err;
|
||||
char buf[256];
|
||||
ERR_error_string_n(err, buf, sizeof(buf));
|
||||
errors_.emplace_back(buf);
|
||||
@@ -297,6 +299,12 @@ MaybeLocal<Value> cryptoErrorListToException(Environment* env,
|
||||
return exception;
|
||||
}
|
||||
|
||||
namespace error {
|
||||
v8::Maybe<void> Decorate(Environment* env,
|
||||
v8::Local<v8::Object> obj,
|
||||
unsigned long err); // NOLINT(runtime/int)
|
||||
} // namespace error
|
||||
|
||||
MaybeLocal<Value> CryptoErrorStore::ToException(
|
||||
Environment* env,
|
||||
Local<String> exception_string) const {
|
||||
@@ -321,10 +329,10 @@ MaybeLocal<Value> CryptoErrorStore::ToException(
|
||||
|
||||
Local<Value> exception_v = Exception::Error(exception_string);
|
||||
CHECK(!exception_v.IsEmpty());
|
||||
CHECK(exception_v->IsObject());
|
||||
Local<Object> exception = exception_v.As<Object>();
|
||||
|
||||
if (!Empty()) {
|
||||
CHECK(exception_v->IsObject());
|
||||
Local<Object> exception = exception_v.As<Object>();
|
||||
Local<Value> stack;
|
||||
if (!ToV8Value(env->context(), errors_).ToLocal(&stack) ||
|
||||
exception->Set(env->context(), env->openssl_error_stack(), stack)
|
||||
@@ -333,6 +341,20 @@ MaybeLocal<Value> CryptoErrorStore::ToException(
|
||||
}
|
||||
}
|
||||
|
||||
if (primary_openssl_error_ != 0) {
|
||||
if (error::Decorate(env, exception, primary_openssl_error_).IsNothing()) {
|
||||
return MaybeLocal<Value>();
|
||||
}
|
||||
} else if (node_error_code_ != nullptr) {
|
||||
if (exception
|
||||
->Set(env->context(),
|
||||
env->code_string(),
|
||||
OneByteString(env->isolate(), node_error_code_))
|
||||
.IsNothing()) {
|
||||
return MaybeLocal<Value>();
|
||||
}
|
||||
}
|
||||
|
||||
return exception_v;
|
||||
}
|
||||
|
||||
|
||||
+28
-11
@@ -72,13 +72,23 @@ void Decode(const v8::FunctionCallbackInfo<v8::Value>& args,
|
||||
}
|
||||
}
|
||||
|
||||
#define NODE_CRYPTO_ERROR_CODES_MAP(V) \
|
||||
V(CIPHER_JOB_FAILED, "Cipher job failed") \
|
||||
V(DERIVING_BITS_FAILED, "Deriving bits failed") \
|
||||
V(ENGINE_NOT_FOUND, "Engine \"%s\" was not found") \
|
||||
V(INVALID_KEY_TYPE, "Invalid key type") \
|
||||
V(KEY_GENERATION_JOB_FAILED, "Key generation job failed") \
|
||||
V(OK, "Ok") \
|
||||
#define NODE_CRYPTO_ERROR_CODES_MAP(V) \
|
||||
V(ALLOCATION_FAILED, "Failed to allocate output buffer") \
|
||||
V(ARGON2_FAILED, "Argon2 derivation failed") \
|
||||
V(CIPHER_JOB_FAILED, "Cipher job failed") \
|
||||
V(CONTEXT_UNSUPPORTED, "Context parameter is unsupported") \
|
||||
V(DECAPSULATION_FAILED, "Decapsulation failed") \
|
||||
V(DERIVING_BITS_FAILED, "Deriving bits failed") \
|
||||
V(ECDH_FAILED, "ECDH key agreement failed") \
|
||||
V(ENCAPSULATION_FAILED, "Encapsulation failed") \
|
||||
V(ENGINE_NOT_FOUND, "Engine \"%s\" was not found") \
|
||||
V(HKDF_FAILED, "HKDF derivation failed") \
|
||||
V(INVALID_KEY_TYPE, "Invalid key type") \
|
||||
V(KEY_GENERATION_JOB_FAILED, "Key generation job failed") \
|
||||
V(KMAC_FAILED, "KMAC derivation failed") \
|
||||
V(OK, "Ok") \
|
||||
V(PBKDF2_FAILED, "PBKDF2 derivation failed") \
|
||||
V(SCRYPT_FAILED, "scrypt derivation failed")
|
||||
|
||||
enum class NodeCryptoError {
|
||||
#define V(CODE, DESCRIPTION) CODE,
|
||||
@@ -115,12 +125,16 @@ struct CryptoErrorStore final : public MemoryRetainer {
|
||||
Environment* env,
|
||||
v8::Local<v8::String> exception_string = v8::Local<v8::String>()) const;
|
||||
|
||||
void SetNodeErrorCode(const char* code) { node_error_code_ = code; }
|
||||
|
||||
SET_NO_MEMORY_INFO()
|
||||
SET_MEMORY_INFO_NAME(CryptoErrorStore)
|
||||
SET_SELF_SIZE(CryptoErrorStore)
|
||||
|
||||
private:
|
||||
std::vector<std::string> errors_;
|
||||
unsigned long primary_openssl_error_ = 0; // NOLINT(runtime/int)
|
||||
const char* node_error_code_ = nullptr;
|
||||
};
|
||||
|
||||
template <typename... Args>
|
||||
@@ -411,14 +425,17 @@ class DeriveBitsJob final : public CryptoJob<DeriveBitsTraits> {
|
||||
|
||||
void DoThreadPoolWork() override {
|
||||
ncrypto::ClearErrorOnReturn clear_error_on_return;
|
||||
CryptoErrorStore* errors = CryptoJob<DeriveBitsTraits>::errors();
|
||||
if (!DeriveBitsTraits::DeriveBits(AsyncWrap::env(),
|
||||
*CryptoJob<DeriveBitsTraits>::params(),
|
||||
&out_,
|
||||
this->mode())) {
|
||||
CryptoErrorStore* errors = CryptoJob<DeriveBitsTraits>::errors();
|
||||
errors->Capture();
|
||||
if (errors->Empty())
|
||||
this->mode(),
|
||||
errors)) {
|
||||
if (errors->Empty()) errors->Capture();
|
||||
if (errors->Empty()) {
|
||||
errors->Insert(NodeCryptoError::DERIVING_BITS_FAILED);
|
||||
errors->SetNodeErrorCode("ERR_CRYPTO_OPERATION_FAILED");
|
||||
}
|
||||
return;
|
||||
}
|
||||
success_ = true;
|
||||
|
||||
@@ -154,13 +154,16 @@ MCowBQYDK2VuAyEA6pwGRbadNQAI/tYN8+/p/0/hbsdHfOEGr1ADiLVk/Gc=
|
||||
const signature = crypto.randomBytes(16);
|
||||
|
||||
let expected = /no default digest/;
|
||||
let expectedCode = 'ERR_OSSL_EVP_NO_DEFAULT_DIGEST';
|
||||
if (hasOpenSSL3 || process.features.openssl_is_boringssl) {
|
||||
expected = /operation[\s_]not[\s_]supported[\s_]for[\s_]this[\s_]keytype/i;
|
||||
expectedCode = 'ERR_OSSL_EVP_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE';
|
||||
}
|
||||
|
||||
crypto.verify(undefined, data, untrustedKey, signature, common.mustCall((err) => {
|
||||
assert.ok(err);
|
||||
assert.match(err.message, expected);
|
||||
assert.strictEqual(err.code, expectedCode);
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -171,5 +174,6 @@ MCowBQYDK2VuAyEA6pwGRbadNQAI/tYN8+/p/0/hbsdHfOEGr1ADiLVk/Gc=
|
||||
crypto.sign('sha512', 'message', privateKey, common.mustCall((err) => {
|
||||
assert.ok(err);
|
||||
assert.match(err.message, /digest[\s_]too[\s_]big[\s_]for[\s_]rsa[\s_]key/i);
|
||||
assert.match(err.code, /^ERR_OSSL_.*DIGEST_TOO_BIG_FOR_RSA_KEY$/);
|
||||
}));
|
||||
}
|
||||
@@ -141,6 +141,7 @@ for (const [params1, params2] of list) {
|
||||
assert.ok(err);
|
||||
assert.strictEqual(err.name, 'Error');
|
||||
assert.match(err.message, hasOpenSSL3 ? /mismatching domain parameters/ : /different parameters/);
|
||||
assert.match(err.code, /^ERR_OSSL_.*(?:MISMATCHING_DOMAIN_PARAMETERS|DIFFERENT_PARAMETERS)$/);
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -199,6 +200,7 @@ crypto.diffieHellman({
|
||||
assert.ok(err);
|
||||
assert.strictEqual(err.name, 'Error');
|
||||
assert.match(err.message, hasOpenSSL3 ? /mismatching domain parameters/ : /different parameters/);
|
||||
assert.match(err.code, /^ERR_OSSL_.*(?:MISMATCHING_DOMAIN_PARAMETERS|DIFFERENT_PARAMETERS)$/);
|
||||
}));
|
||||
|
||||
test(crypto.generateKeyPairSync('x448'),
|
||||
@@ -216,5 +218,6 @@ test(crypto.generateKeyPairSync('x25519'),
|
||||
assert.ok(err);
|
||||
assert.strictEqual(err.name, 'Error');
|
||||
assert.match(err.message, hasOpenSSL3 ? /failed during derivation/ : /Deriving bits failed/);
|
||||
assert.strictEqual(err.code, hasOpenSSL3 ? 'ERR_OSSL_FAILED_DURING_DERIVATION' : 'ERR_CRYPTO_OPERATION_FAILED');
|
||||
}));
|
||||
}
|
||||
@@ -224,7 +224,7 @@ for (const [name, {
|
||||
{
|
||||
const { ciphertext } = crypto.encapsulate(publicKey);
|
||||
assert.throws(() => crypto.decapsulate(wrongPrivateKey, ciphertext), {
|
||||
message: /Failed to (initialize|perform) decapsulation/,
|
||||
message: /Decapsulation failed/,
|
||||
code: 'ERR_CRYPTO_OPERATION_FAILED',
|
||||
});
|
||||
}
|
||||
@@ -234,7 +234,8 @@ for (const [name, {
|
||||
crypto.encapsulate(publicKey, common.mustSucceed(({ ciphertext }) => {
|
||||
crypto.decapsulate(wrongPrivateKey, ciphertext, common.mustCall((err) => {
|
||||
assert(err);
|
||||
assert.strictEqual(err.message, 'Deriving bits failed');
|
||||
assert.match(err.message, /Decapsulation failed/);
|
||||
assert.strictEqual(err.code, 'ERR_CRYPTO_OPERATION_FAILED');
|
||||
}));
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,278 @@
|
||||
'use strict';
|
||||
|
||||
// Tests that sync and async crypto operations produce errors with matching
|
||||
// properties. This validates that both code paths go through the same
|
||||
// CryptoErrorStore-based error creation rather than diverging.
|
||||
|
||||
const common = require('../common');
|
||||
if (!common.hasCrypto)
|
||||
common.skip('missing crypto');
|
||||
|
||||
const { hasOpenSSL } = require('../common/crypto');
|
||||
const assert = require('assert');
|
||||
const crypto = require('crypto');
|
||||
const fixtures = require('../common/fixtures');
|
||||
|
||||
function getError(fn) {
|
||||
let err;
|
||||
assert.throws(fn, (e) => { err = e; return true; });
|
||||
return err;
|
||||
}
|
||||
|
||||
// Compare error properties between sync and async errors.
|
||||
function assertErrorMatch(syncErr, asyncErr, label) {
|
||||
assert(asyncErr instanceof Error, `${label}: expected async Error`);
|
||||
assert.strictEqual(asyncErr.message, syncErr.message,
|
||||
`${label}: message mismatch`);
|
||||
assert.strictEqual(asyncErr.code, syncErr.code,
|
||||
`${label}: code mismatch`);
|
||||
// OpenSSL error decoration properties
|
||||
for (const prop of ['library', 'reason']) {
|
||||
assert.strictEqual(asyncErr[prop], syncErr[prop],
|
||||
`${label}: ${prop} mismatch`);
|
||||
}
|
||||
}
|
||||
|
||||
const data = Buffer.from('test data');
|
||||
|
||||
// === crypto.sign / crypto.verify ===
|
||||
|
||||
// Sign: ed25519 with explicit digest (OpenSSL error at signInit)
|
||||
{
|
||||
const privateKey = crypto.createPrivateKey(
|
||||
fixtures.readKey('ed25519_private.pem'));
|
||||
|
||||
const syncErr = getError(() => crypto.sign('sha256', data, privateKey));
|
||||
assert(syncErr.code);
|
||||
assert(syncErr.message);
|
||||
|
||||
crypto.sign('sha256', data, privateKey, common.mustCall((asyncErr) => {
|
||||
assertErrorMatch(syncErr, asyncErr, 'sign: ed25519 + sha256');
|
||||
}));
|
||||
}
|
||||
|
||||
// Sign: context on key that does not support it (CONTEXT_UNSUPPORTED)
|
||||
{
|
||||
const privateKey = crypto.createPrivateKey(
|
||||
fixtures.readKey('rsa_private.pem'));
|
||||
const keyOpts = { key: privateKey, context: Buffer.alloc(1) };
|
||||
|
||||
const syncErr = getError(() => crypto.sign('sha256', data, keyOpts));
|
||||
assert.strictEqual(syncErr.code, 'ERR_CRYPTO_OPERATION_FAILED');
|
||||
assert.match(syncErr.message, /Context parameter is unsupported/);
|
||||
|
||||
crypto.sign('sha256', data, keyOpts, common.mustCall((asyncErr) => {
|
||||
assertErrorMatch(syncErr, asyncErr, 'sign: RSA + context');
|
||||
}));
|
||||
}
|
||||
|
||||
// Sign: RSA key too small for digest (OpenSSL error)
|
||||
{
|
||||
const { privateKey } = crypto.generateKeyPairSync('rsa', {
|
||||
modulusLength: 512,
|
||||
});
|
||||
|
||||
const syncErr = getError(() => crypto.sign('sha512', data, privateKey));
|
||||
assert.match(syncErr.message, /digest[\s_]too[\s_]big[\s_]for[\s_]rsa[\s_]key/i);
|
||||
|
||||
crypto.sign('sha512', data, privateKey, common.mustCall((asyncErr) => {
|
||||
assertErrorMatch(syncErr, asyncErr, 'sign: RSA 512 + sha512');
|
||||
}));
|
||||
}
|
||||
|
||||
// Sign: illegal RSA padding mode (OpenSSL error)
|
||||
{
|
||||
const privateKey = crypto.createPrivateKey(
|
||||
fixtures.readKey('rsa_private.pem'));
|
||||
|
||||
const keyOpts = {
|
||||
key: privateKey,
|
||||
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
|
||||
};
|
||||
|
||||
const syncErr = getError(() => crypto.sign('sha256', data, keyOpts));
|
||||
assert.match(syncErr.message,
|
||||
/illegal[\s_]or[\s_]unsupported[\s_]padding[\s_]mode/i);
|
||||
|
||||
crypto.sign('sha256', data, keyOpts, common.mustCall((asyncErr) => {
|
||||
assertErrorMatch(syncErr, asyncErr, 'sign: RSA + OAEP padding');
|
||||
}));
|
||||
}
|
||||
|
||||
// Verify: X25519 key (not a signing key - OpenSSL error)
|
||||
{
|
||||
const publicKey = crypto.createPublicKey(
|
||||
fixtures.readKey('x25519_public.pem'));
|
||||
const sig = Buffer.alloc(64);
|
||||
|
||||
const syncErr = getError(() =>
|
||||
crypto.verify(null, data, publicKey, sig));
|
||||
assert(syncErr.message);
|
||||
|
||||
crypto.verify(null, data, publicKey, sig, common.mustCall((asyncErr) => {
|
||||
assertErrorMatch(syncErr, asyncErr, 'verify: X25519 key');
|
||||
}));
|
||||
}
|
||||
|
||||
// === crypto.diffieHellman ===
|
||||
|
||||
// DH: Mismatched EC curves (OpenSSL error)
|
||||
{
|
||||
const alicePrivate = crypto.createPrivateKey(
|
||||
fixtures.readKey('ec_p256_private.pem'));
|
||||
const bobPublic = crypto.createPublicKey(
|
||||
fixtures.readKey('ec_p384_public.pem'));
|
||||
|
||||
const syncErr = getError(() =>
|
||||
crypto.diffieHellman({
|
||||
privateKey: alicePrivate,
|
||||
publicKey: bobPublic,
|
||||
}));
|
||||
assert(syncErr.message);
|
||||
|
||||
crypto.diffieHellman({
|
||||
privateKey: alicePrivate,
|
||||
publicKey: bobPublic,
|
||||
}, common.mustCall((asyncErr) => {
|
||||
assertErrorMatch(syncErr, asyncErr, 'DH: mismatched EC curves');
|
||||
}));
|
||||
}
|
||||
|
||||
// DH: X25519 with all-zero public key (OpenSSL error)
|
||||
{
|
||||
const privateKey = crypto.createPrivateKey(
|
||||
fixtures.readKey('x25519_private.pem'));
|
||||
const publicKey = crypto.createPublicKey(
|
||||
'-----BEGIN PUBLIC KEY-----\n' +
|
||||
'MCowBQYDK2VuAyEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\n' +
|
||||
'-----END PUBLIC KEY-----');
|
||||
|
||||
const syncErr = getError(() =>
|
||||
crypto.diffieHellman({ publicKey, privateKey }));
|
||||
assert(syncErr.message);
|
||||
|
||||
crypto.diffieHellman({ publicKey, privateKey },
|
||||
common.mustCall((asyncErr) => {
|
||||
assertErrorMatch(
|
||||
syncErr, asyncErr, 'DH: X25519 zero key');
|
||||
}));
|
||||
}
|
||||
|
||||
// DH: Mismatched DH group params (OpenSSL error)
|
||||
{
|
||||
const alice = crypto.generateKeyPairSync('dh', { group: 'modp5' });
|
||||
const bob = crypto.generateKeyPairSync('dh', { group: 'modp18' });
|
||||
|
||||
const syncErr = getError(() =>
|
||||
crypto.diffieHellman({
|
||||
privateKey: alice.privateKey,
|
||||
publicKey: bob.publicKey,
|
||||
}));
|
||||
assert(syncErr.message);
|
||||
|
||||
crypto.diffieHellman({
|
||||
privateKey: alice.privateKey,
|
||||
publicKey: bob.publicKey,
|
||||
}, common.mustCall((asyncErr) => {
|
||||
assertErrorMatch(syncErr, asyncErr, 'DH: mismatched DH groups');
|
||||
}));
|
||||
}
|
||||
|
||||
// === crypto.encapsulate / crypto.decapsulate ===
|
||||
if (hasOpenSSL(3)) {
|
||||
// KEM: Decapsulate with wrong private key type
|
||||
{
|
||||
const rsaPublicKey = crypto.createPublicKey(
|
||||
fixtures.readKey('rsa_public.pem'));
|
||||
const x25519Private = crypto.createPrivateKey(
|
||||
fixtures.readKey('x25519_private.pem'));
|
||||
|
||||
const { ciphertext } = crypto.encapsulate(rsaPublicKey);
|
||||
|
||||
const syncErr = getError(() =>
|
||||
crypto.decapsulate(x25519Private, ciphertext));
|
||||
assert.strictEqual(syncErr.code, 'ERR_CRYPTO_OPERATION_FAILED');
|
||||
assert.match(syncErr.message, /Decapsulation failed/);
|
||||
|
||||
crypto.decapsulate(x25519Private, ciphertext,
|
||||
common.mustCall((asyncErr) => {
|
||||
assertErrorMatch(
|
||||
syncErr, asyncErr, 'KEM: wrong key decapsulate');
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
// === crypto.hkdf / crypto.hkdfSync ===
|
||||
|
||||
// HKDF: Invalid digest (AdditionalConfig error - should throw in both paths)
|
||||
{
|
||||
const key = crypto.randomBytes(32);
|
||||
const salt = Buffer.alloc(0);
|
||||
const info = Buffer.alloc(0);
|
||||
|
||||
const syncErr = getError(() =>
|
||||
crypto.hkdfSync('nonexistent', key, salt, info, 64));
|
||||
assert.strictEqual(syncErr.code, 'ERR_CRYPTO_INVALID_DIGEST');
|
||||
|
||||
// Async hkdf also throws synchronously for setup errors
|
||||
const asyncErr = getError(() =>
|
||||
crypto.hkdf('nonexistent', key, salt, info, 64, common.mustNotCall()));
|
||||
assertErrorMatch(syncErr, asyncErr, 'HKDF: invalid digest');
|
||||
}
|
||||
|
||||
// HKDF: keylen too large (AdditionalConfig error)
|
||||
{
|
||||
const key = crypto.randomBytes(32);
|
||||
const salt = Buffer.alloc(0);
|
||||
const info = Buffer.alloc(0);
|
||||
// sha256 output is 32 bytes, max HKDF output is 255 * 32 = 8160
|
||||
const tooLarge = 255 * 32 + 1;
|
||||
|
||||
const syncErr = getError(() =>
|
||||
crypto.hkdfSync('sha256', key, salt, info, tooLarge));
|
||||
assert.strictEqual(syncErr.code, 'ERR_CRYPTO_INVALID_KEYLEN');
|
||||
|
||||
const asyncErr = getError(() =>
|
||||
crypto.hkdf('sha256', key, salt, info, tooLarge, common.mustNotCall()));
|
||||
assertErrorMatch(syncErr, asyncErr, 'HKDF: keylen too large');
|
||||
}
|
||||
|
||||
// === crypto.pbkdf2 / crypto.pbkdf2Sync ===
|
||||
|
||||
// PBKDF2: Invalid digest (AdditionalConfig error)
|
||||
{
|
||||
const syncErr = getError(() =>
|
||||
crypto.pbkdf2Sync('password', 'salt', 1, 32, 'nonexistent'));
|
||||
assert.strictEqual(syncErr.code, 'ERR_CRYPTO_INVALID_DIGEST');
|
||||
|
||||
// pbkdf2 also throws synchronously for setup errors
|
||||
const asyncErr = getError(() =>
|
||||
crypto.pbkdf2('password', 'salt', 1, 32, 'nonexistent',
|
||||
common.mustNotCall()));
|
||||
assertErrorMatch(syncErr, asyncErr, 'PBKDF2: invalid digest');
|
||||
}
|
||||
|
||||
// === crypto.scrypt / crypto.scryptSync ===
|
||||
|
||||
// Scrypt: Invalid params (AdditionalConfig error sent to OpenSSL)
|
||||
{
|
||||
const syncErr = getError(() =>
|
||||
crypto.scryptSync('password', 'salt', 64, { N: 3 }));
|
||||
assert.strictEqual(syncErr.code, 'ERR_CRYPTO_INVALID_SCRYPT_PARAMS');
|
||||
|
||||
const asyncErr = getError(() =>
|
||||
crypto.scrypt('password', 'salt', 64, { N: 3 },
|
||||
common.mustNotCall()));
|
||||
assertErrorMatch(syncErr, asyncErr, 'Scrypt: N not power of 2');
|
||||
}
|
||||
|
||||
{
|
||||
const syncErr = getError(() =>
|
||||
crypto.scryptSync('password', 'salt', 64, { N: 1 }));
|
||||
assert.strictEqual(syncErr.code, 'ERR_CRYPTO_INVALID_SCRYPT_PARAMS');
|
||||
|
||||
const asyncErr = getError(() =>
|
||||
crypto.scrypt('password', 'salt', 64, { N: 1 },
|
||||
common.mustNotCall()));
|
||||
assertErrorMatch(syncErr, asyncErr, 'Scrypt: N=1');
|
||||
}
|
||||
Reference in new issue
Block a user