diff --git a/deps/ncrypto/ncrypto.cc b/deps/ncrypto/ncrypto.cc index 8a5bb5bb5389d0..3e5d6a8924e00d 100644 --- a/deps/ncrypto/ncrypto.cc +++ b/deps/ncrypto/ncrypto.cc @@ -1254,7 +1254,7 @@ DataPointer DHPointer::stateless(const EVPKeyPointer& ourKey, } // ============================================================================ -// HKDF +// KDF const EVP_MD* getDigestByName(const std::string_view name) { return EVP_get_digestbyname(name.data()); @@ -1275,6 +1275,8 @@ DataPointer hkdf(const EVP_MD* md, const Buffer& info, const Buffer& salt, size_t length) { + ClearErrorOnReturn clearErrorOnReturn; + if (!checkHkdfLength(md, length) || info.len > INT_MAX || salt.len > INT_MAX) { @@ -1331,4 +1333,32 @@ DataPointer hkdf(const EVP_MD* md, return buf; } +bool checkScryptParams(uint64_t N, uint64_t r, uint64_t p, uint64_t maxmem) { + return EVP_PBE_scrypt(nullptr, 0, nullptr, 0, N, r, p, maxmem, nullptr, 0) == 1; +} + +DataPointer scrypt(const Buffer& pass, + const Buffer& salt, + uint64_t N, + uint64_t r, + uint64_t p, + uint64_t maxmem, + size_t length) { + ClearErrorOnReturn clearErrorOnReturn; + + if (pass.len > INT_MAX || + salt.len > INT_MAX) { + return {}; + } + + auto dp = DataPointer::Alloc(length); + if (dp && EVP_PBE_scrypt( + pass.data, pass.len, salt.data, salt.len, N, r, p, maxmem, + reinterpret_cast(dp.get()), length)) { + return dp; + } + + return {}; +} + } // namespace ncrypto diff --git a/deps/ncrypto/ncrypto.h b/deps/ncrypto/ncrypto.h index 9808503c62cb3f..a002831b4b01b3 100644 --- a/deps/ncrypto/ncrypto.h +++ b/deps/ncrypto/ncrypto.h @@ -589,7 +589,7 @@ BIOPointer ExportPublicKey(const char* input, size_t length); Buffer ExportChallenge(const char* input, size_t length); // ============================================================================ -// HKDF +// KDF const EVP_MD* getDigestByName(const std::string_view name); @@ -604,6 +604,16 @@ DataPointer hkdf(const EVP_MD* md, const Buffer& salt, size_t length); +bool checkScryptParams(uint64_t N, uint64_t r, uint64_t p, uint64_t maxmem); + +DataPointer scrypt(const Buffer& pass, + const Buffer& salt, + uint64_t N, + uint64_t r, + uint64_t p, + uint64_t maxmem, + size_t length); + // ============================================================================ // Version metadata #define NCRYPTO_VERSION "0.0.1" diff --git a/src/crypto/crypto_scrypt.cc b/src/crypto/crypto_scrypt.cc index a262a2be96d7c3..fb6d3d73a98b78 100644 --- a/src/crypto/crypto_scrypt.cc +++ b/src/crypto/crypto_scrypt.cc @@ -93,17 +93,10 @@ Maybe ScryptTraits::AdditionalConfig( params->p = args[offset + 4].As()->Value(); params->maxmem = args[offset + 5]->IntegerValue(env->context()).ToChecked(); - if (EVP_PBE_scrypt( - nullptr, - 0, - nullptr, - 0, - params->N, - params->r, - params->p, - params->maxmem, - nullptr, - 0) != 1) { + params->length = args[offset + 6].As()->Value(); + CHECK_GE(params->length, 0); + + if (!ncrypto::checkScryptParams(params->N, params->r, params->p, params->maxmem)) { // Do not use CryptoErrorStore or ThrowCryptoError here in order to maintain // backward compatibility with ERR_CRYPTO_INVALID_SCRYPT_PARAMS. uint32_t err = ERR_peek_last_error(); @@ -118,9 +111,6 @@ Maybe ScryptTraits::AdditionalConfig( return Nothing(); } - params->length = args[offset + 6].As()->Value(); - CHECK_GE(params->length, 0); - return Just(true); } @@ -128,23 +118,31 @@ bool ScryptTraits::DeriveBits( Environment* env, const ScryptConfig& params, ByteSource* out) { - ByteSource::Builder buf(params.length); - - // Both the pass and salt may be zero-length at this point - - if (!EVP_PBE_scrypt(params.pass.data(), - params.pass.size(), - params.salt.data(), - params.salt.size(), - params.N, - params.r, - params.p, - params.maxmem, - buf.data(), - params.length)) { - return false; + + // 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) { + *out = ByteSource(); + return true; } - *out = std::move(buf).release(); + + auto dp = ncrypto::scrypt( + ncrypto::Buffer { + .data = params.pass.data(), + .len = params.pass.size(), + }, + ncrypto::Buffer { + .data = params.salt.data(), + .len = params.salt.size(), + }, + params.N, + params.r, + params.p, + params.maxmem, + params.length); + + if (!dp) return false; + *out = ByteSource::Allocated(dp.release()); return true; } diff --git a/test/parallel/test-crypto-scrypt.js b/test/parallel/test-crypto-scrypt.js index 0cf6efb8eed997..76f933f4df3a6c 100644 --- a/test/parallel/test-crypto-scrypt.js +++ b/test/parallel/test-crypto-scrypt.js @@ -1,4 +1,4 @@ -// Flags: --expose-internals +// Flags: --expose-internals --no-warnings 'use strict'; const common = require('../common'); if (!common.hasCrypto)