Skip to content

Commit 4749640

Browse files
tniessenMylesBorins
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 <[email protected]> Reviewed-By: Refael Ackermann <[email protected]>
1 parent ffb4087 commit 4749640

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
@@ -3530,46 +3531,51 @@ void Sign::SignUpdate(const FunctionCallbackInfo<Value>& args) {
35303531
sign->CheckThrow(err);
35313532
}
35323533

3533-
static int Node_SignFinal(EVPMDPointer&& mdctx, unsigned char* md,
3534-
unsigned int* sig_len,
3535-
const EVPKeyPointer& pkey, int padding,
3536-
int pss_salt_len) {
3534+
static MallocedBuffer<unsigned char> Node_SignFinal(EVPMDPointer&& mdctx,
3535+
const EVPKeyPointer& pkey,
3536+
int padding,
3537+
int pss_salt_len) {
35373538
unsigned char m[EVP_MAX_MD_SIZE];
35383539
unsigned int m_len;
35393540

3540-
*sig_len = 0;
35413541
if (!EVP_DigestFinal_ex(mdctx.get(), m, &m_len))
3542-
return 0;
3542+
return MallocedBuffer<unsigned char>();
3543+
3544+
int signed_sig_len = EVP_PKEY_size(pkey.get());
3545+
CHECK_GE(signed_sig_len, 0);
3546+
size_t sig_len = static_cast<size_t>(signed_sig_len);
3547+
MallocedBuffer<unsigned char> sig(sig_len);
35433548

3544-
size_t sltmp = static_cast<size_t>(EVP_PKEY_size(pkey.get()));
35453549
EVPKeyCtxPointer pkctx(EVP_PKEY_CTX_new(pkey.get(), nullptr));
35463550
if (pkctx &&
35473551
EVP_PKEY_sign_init(pkctx.get()) > 0 &&
35483552
ApplyRSAOptions(pkey, pkctx.get(), padding, pss_salt_len) &&
35493553
EVP_PKEY_CTX_set_signature_md(pkctx.get(),
35503554
EVP_MD_CTX_md(mdctx.get())) > 0 &&
3551-
EVP_PKEY_sign(pkctx.get(), md, &sltmp, m, m_len) > 0) {
3552-
*sig_len = sltmp;
3553-
return 1;
3555+
EVP_PKEY_sign(pkctx.get(), sig.data, &sig_len, m, m_len) > 0) {
3556+
sig.Truncate(sig_len);
3557+
return sig;
35543558
}
3555-
return 0;
3559+
3560+
return MallocedBuffer<unsigned char>();
35563561
}
35573562

3558-
SignBase::Error Sign::SignFinal(const char* key_pem,
3559-
int key_pem_len,
3560-
const char* passphrase,
3561-
unsigned char* sig,
3562-
unsigned int* sig_len,
3563-
int padding,
3564-
int salt_len) {
3563+
std::pair<SignBase::Error, MallocedBuffer<unsigned char>> Sign::SignFinal(
3564+
const char* key_pem,
3565+
int key_pem_len,
3566+
const char* passphrase,
3567+
int padding,
3568+
int salt_len) {
3569+
MallocedBuffer<unsigned char> buffer;
3570+
35653571
if (!mdctx_)
3566-
return kSignNotInitialised;
3572+
return std::make_pair(kSignNotInitialised, std::move(buffer));
35673573

35683574
EVPMDPointer mdctx = std::move(mdctx_);
35693575

35703576
BIOPointer bp(BIO_new_mem_buf(const_cast<char*>(key_pem), key_pem_len));
35713577
if (!bp)
3572-
return kSignPrivateKey;
3578+
return std::make_pair(kSignPrivateKey, std::move(buffer));
35733579

35743580
EVPKeyPointer pkey(PEM_read_bio_PrivateKey(bp.get(),
35753581
nullptr,
@@ -3580,7 +3586,7 @@ SignBase::Error Sign::SignFinal(const char* key_pem,
35803586
// without `pkey` being set to nullptr;
35813587
// cf. the test of `test_bad_rsa_privkey.pem` for an example.
35823588
if (!pkey || 0 != ERR_peek_error())
3583-
return kSignPrivateKey;
3589+
return std::make_pair(kSignPrivateKey, std::move(buffer));
35843590

35853591
#ifdef NODE_FIPS_MODE
35863592
/* Validate DSA2 parameters from FIPS 186-4 */
@@ -3604,10 +3610,9 @@ SignBase::Error Sign::SignFinal(const char* key_pem,
36043610
}
36053611
#endif // NODE_FIPS_MODE
36063612

3607-
if (Node_SignFinal(std::move(mdctx), sig, sig_len, pkey, padding, salt_len))
3608-
return kSignOk;
3609-
else
3610-
return kSignPrivateKey;
3613+
buffer = Node_SignFinal(std::move(mdctx), pkey, padding, salt_len);
3614+
Error error = buffer.is_empty() ? kSignPrivateKey : kSignOk;
3615+
return std::make_pair(error, std::move(buffer));
36113616
}
36123617

36133618

@@ -3631,22 +3636,22 @@ void Sign::SignFinal(const FunctionCallbackInfo<Value>& args) {
36313636
int salt_len = args[3].As<Int32>()->Value();
36323637

36333638
ClearErrorOnReturn clear_error_on_return;
3634-
unsigned char md_value[8192];
3635-
unsigned int md_len = sizeof(md_value);
36363639

3637-
Error err = sign->SignFinal(
3640+
std::pair<Error, MallocedBuffer<unsigned char>> ret = sign->SignFinal(
36383641
buf,
36393642
buf_len,
36403643
len >= 2 && !args[1]->IsNull() ? *passphrase : nullptr,
3641-
md_value,
3642-
&md_len,
36433644
padding,
36443645
salt_len);
3645-
if (err != kSignOk)
3646-
return sign->CheckThrow(err);
3646+
3647+
if (std::get<Error>(ret) != kSignOk)
3648+
return sign->CheckThrow(std::get<Error>(ret));
3649+
3650+
MallocedBuffer<unsigned char> sig =
3651+
std::move(std::get<MallocedBuffer<unsigned char>>(ret));
36473652

36483653
Local<Object> rc =
3649-
Buffer::Copy(env, reinterpret_cast<char*>(md_value), md_len)
3654+
Buffer::New(env, reinterpret_cast<char*>(sig.release()), sig.size)
36503655
.ToLocalChecked();
36513656
args.GetReturnValue().Set(rc);
36523657
}

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)