Skip to content

Commit 1ad660b

Browse files
tniessenjasnell
authored andcommitted
crypto: reduce memory usage of SignFinal
The fixed-size buffer on the stack is unnecessary and way too large for most applications. This change removes it and allocates the required memory directly instead of copying into heap later. PR-URL: #23427 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Refael Ackermann <refack@gmail.com>
1 parent 996b3c5 commit 1ad660b

File tree

3 files changed

+50
-41
lines changed

3 files changed

+50
-41
lines changed

src/node_crypto.cc

+38-33
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343

4444
#include <algorithm>
4545
#include <memory>
46+
#include <utility>
4647
#include <vector>
4748

4849
static const int X509_NAME_FLAGS = ASN1_STRFLGS_ESC_CTRL
@@ -3523,46 +3524,51 @@ void Sign::SignUpdate(const FunctionCallbackInfo<Value>& args) {
35233524
sign->CheckThrow(err);
35243525
}
35253526

3526-
static int Node_SignFinal(EVPMDPointer&& mdctx, unsigned char* md,
3527-
unsigned int* sig_len,
3528-
const EVPKeyPointer& pkey, int padding,
3529-
int pss_salt_len) {
3527+
static MallocedBuffer<unsigned char> Node_SignFinal(EVPMDPointer&& mdctx,
3528+
const EVPKeyPointer& pkey,
3529+
int padding,
3530+
int pss_salt_len) {
35303531
unsigned char m[EVP_MAX_MD_SIZE];
35313532
unsigned int m_len;
35323533

3533-
*sig_len = 0;
35343534
if (!EVP_DigestFinal_ex(mdctx.get(), m, &m_len))
3535-
return 0;
3535+
return MallocedBuffer<unsigned char>();
3536+
3537+
int signed_sig_len = EVP_PKEY_size(pkey.get());
3538+
CHECK_GE(signed_sig_len, 0);
3539+
size_t sig_len = static_cast<size_t>(signed_sig_len);
3540+
MallocedBuffer<unsigned char> sig(sig_len);
35363541

3537-
size_t sltmp = static_cast<size_t>(EVP_PKEY_size(pkey.get()));
35383542
EVPKeyCtxPointer pkctx(EVP_PKEY_CTX_new(pkey.get(), nullptr));
35393543
if (pkctx &&
35403544
EVP_PKEY_sign_init(pkctx.get()) > 0 &&
35413545
ApplyRSAOptions(pkey, pkctx.get(), padding, pss_salt_len) &&
35423546
EVP_PKEY_CTX_set_signature_md(pkctx.get(),
35433547
EVP_MD_CTX_md(mdctx.get())) > 0 &&
3544-
EVP_PKEY_sign(pkctx.get(), md, &sltmp, m, m_len) > 0) {
3545-
*sig_len = sltmp;
3546-
return 1;
3548+
EVP_PKEY_sign(pkctx.get(), sig.data, &sig_len, m, m_len) > 0) {
3549+
sig.Truncate(sig_len);
3550+
return sig;
35473551
}
3548-
return 0;
3552+
3553+
return MallocedBuffer<unsigned char>();
35493554
}
35503555

3551-
SignBase::Error Sign::SignFinal(const char* key_pem,
3552-
int key_pem_len,
3553-
const char* passphrase,
3554-
unsigned char* sig,
3555-
unsigned int* sig_len,
3556-
int padding,
3557-
int salt_len) {
3556+
std::pair<SignBase::Error, MallocedBuffer<unsigned char>> Sign::SignFinal(
3557+
const char* key_pem,
3558+
int key_pem_len,
3559+
const char* passphrase,
3560+
int padding,
3561+
int salt_len) {
3562+
MallocedBuffer<unsigned char> buffer;
3563+
35583564
if (!mdctx_)
3559-
return kSignNotInitialised;
3565+
return std::make_pair(kSignNotInitialised, std::move(buffer));
35603566

35613567
EVPMDPointer mdctx = std::move(mdctx_);
35623568

35633569
BIOPointer bp(BIO_new_mem_buf(const_cast<char*>(key_pem), key_pem_len));
35643570
if (!bp)
3565-
return kSignPrivateKey;
3571+
return std::make_pair(kSignPrivateKey, std::move(buffer));
35663572

35673573
EVPKeyPointer pkey(PEM_read_bio_PrivateKey(bp.get(),
35683574
nullptr,
@@ -3573,7 +3579,7 @@ SignBase::Error Sign::SignFinal(const char* key_pem,
35733579
// without `pkey` being set to nullptr;
35743580
// cf. the test of `test_bad_rsa_privkey.pem` for an example.
35753581
if (!pkey || 0 != ERR_peek_error())
3576-
return kSignPrivateKey;
3582+
return std::make_pair(kSignPrivateKey, std::move(buffer));
35773583

35783584
#ifdef NODE_FIPS_MODE
35793585
/* Validate DSA2 parameters from FIPS 186-4 */
@@ -3597,10 +3603,9 @@ SignBase::Error Sign::SignFinal(const char* key_pem,
35973603
}
35983604
#endif // NODE_FIPS_MODE
35993605

3600-
if (Node_SignFinal(std::move(mdctx), sig, sig_len, pkey, padding, salt_len))
3601-
return kSignOk;
3602-
else
3603-
return kSignPrivateKey;
3606+
buffer = Node_SignFinal(std::move(mdctx), pkey, padding, salt_len);
3607+
Error error = buffer.is_empty() ? kSignPrivateKey : kSignOk;
3608+
return std::make_pair(error, std::move(buffer));
36043609
}
36053610

36063611

@@ -3624,22 +3629,22 @@ void Sign::SignFinal(const FunctionCallbackInfo<Value>& args) {
36243629
int salt_len = args[3].As<Int32>()->Value();
36253630

36263631
ClearErrorOnReturn clear_error_on_return;
3627-
unsigned char md_value[8192];
3628-
unsigned int md_len = sizeof(md_value);
36293632

3630-
Error err = sign->SignFinal(
3633+
std::pair<Error, MallocedBuffer<unsigned char>> ret = sign->SignFinal(
36313634
buf,
36323635
buf_len,
36333636
len >= 2 && !args[1]->IsNull() ? *passphrase : nullptr,
3634-
md_value,
3635-
&md_len,
36363637
padding,
36373638
salt_len);
3638-
if (err != kSignOk)
3639-
return sign->CheckThrow(err);
3639+
3640+
if (std::get<Error>(ret) != kSignOk)
3641+
return sign->CheckThrow(std::get<Error>(ret));
3642+
3643+
MallocedBuffer<unsigned char> sig =
3644+
std::move(std::get<MallocedBuffer<unsigned char>>(ret));
36403645

36413646
Local<Object> rc =
3642-
Buffer::Copy(env, reinterpret_cast<char*>(md_value), md_len)
3647+
Buffer::New(env, reinterpret_cast<char*>(sig.release()), sig.size)
36433648
.ToLocalChecked();
36443649
args.GetReturnValue().Set(rc);
36453650
}

src/node_crypto.h

+6-7
Original file line numberDiff line numberDiff line change
@@ -518,13 +518,12 @@ class Sign : public SignBase {
518518
public:
519519
static void Initialize(Environment* env, v8::Local<v8::Object> target);
520520

521-
Error SignFinal(const char* key_pem,
522-
int key_pem_len,
523-
const char* passphrase,
524-
unsigned char* sig,
525-
unsigned int* sig_len,
526-
int padding,
527-
int saltlen);
521+
std::pair<Error, MallocedBuffer<unsigned char>> SignFinal(
522+
const char* key_pem,
523+
int key_pem_len,
524+
const char* passphrase,
525+
int padding,
526+
int saltlen);
528527

529528
protected:
530529
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);

src/util.h

+6-1
Original file line numberDiff line numberDiff line change
@@ -439,9 +439,14 @@ struct MallocedBuffer {
439439
return ret;
440440
}
441441

442+
void Truncate(size_t new_size) {
443+
CHECK(new_size <= size);
444+
size = new_size;
445+
}
446+
442447
inline bool is_empty() const { return data == nullptr; }
443448

444-
MallocedBuffer() : data(nullptr) {}
449+
MallocedBuffer() : data(nullptr), size(0) {}
445450
explicit MallocedBuffer(size_t size) : data(Malloc<T>(size)), size(size) {}
446451
MallocedBuffer(T* data, size_t size) : data(data), size(size) {}
447452
MallocedBuffer(MallocedBuffer&& other) : data(other.data), size(other.size) {

0 commit comments

Comments
 (0)