Skip to content

Commit fcb4bf3

Browse files
committed
fixup! crypto: support OpenSSL STORE private keys
1 parent dffa62d commit fcb4bf3

4 files changed

Lines changed: 76 additions & 36 deletions

File tree

deps/ncrypto/ncrypto.cc

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4784,9 +4784,14 @@ DataPointer EVPMDCtxPointer::sign(
47844784

47854785
bool EVPMDCtxPointer::verify(const Buffer<const unsigned char>& buf,
47864786
const Buffer<const unsigned char>& sig) const {
4787-
if (!ctx_) return false;
4788-
int ret = EVP_DigestVerify(ctx_.get(), sig.data, sig.len, buf.data, buf.len);
4789-
return ret == 1;
4787+
return verifyOneShot(buf, sig) == 1;
4788+
}
4789+
4790+
int EVPMDCtxPointer::verifyOneShot(
4791+
const Buffer<const unsigned char>& buf,
4792+
const Buffer<const unsigned char>& sig) const {
4793+
if (!ctx_) return -1;
4794+
return EVP_DigestVerify(ctx_.get(), sig.data, sig.len, buf.data, buf.len);
47904795
}
47914796

47924797
EVPMDCtxPointer EVPMDCtxPointer::New() {

deps/ncrypto/ncrypto.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1531,6 +1531,8 @@ class EVPMDCtxPointer final {
15311531
DataPointer sign(const Buffer<const unsigned char>& buf) const;
15321532
bool verify(const Buffer<const unsigned char>& buf,
15331533
const Buffer<const unsigned char>& sig) const;
1534+
int verifyOneShot(const Buffer<const unsigned char>& buf,
1535+
const Buffer<const unsigned char>& sig) const;
15341536

15351537
const EVP_MD* getDigest() const;
15361538
size_t getDigestSize() const;

src/crypto/crypto_sig.cc

Lines changed: 28 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -391,12 +391,12 @@ bool SupportsContextString(const EVPKeyPointer& key) {
391391
return false;
392392
}
393393

394-
bool UsePrehashedSignVerify(const EVPKeyPointer& key,
395-
const Digest& digest,
396-
bool has_context) {
397-
// Match streaming sign/verify for digest-based keys: hash locally, then ask
398-
// the key implementation to sign or verify the digest. Some providers only
399-
// expose token-backed keys through that lower-level operation.
394+
bool CanFallbackToPrehashedSignVerify(const EVPKeyPointer& key,
395+
const Digest& digest,
396+
bool has_context) {
397+
// The primary path is OpenSSL's digest-sign operation. Fall back to hashing
398+
// locally only when that path fails; some providers expose token-backed keys
399+
// through the lower-level prehashed sign/verify operation only.
400400
return digest && !has_context && !key.isOneShotVariant();
401401
}
402402

@@ -901,32 +901,8 @@ bool SignTraits::DeriveBits(Environment* env,
901901
params.flags & SignConfiguration::kHasSaltLength
902902
? std::optional<int>(params.salt_length)
903903
: std::nullopt;
904-
905-
if (UsePrehashedSignVerify(key, params.digest, has_context)) {
906-
switch (params.mode) {
907-
case SignConfiguration::Mode::Sign: {
908-
*out = SignPrehashed(env,
909-
key,
910-
params.digest,
911-
params.data,
912-
padding,
913-
salt_length,
914-
params.dsa_encoding);
915-
return static_cast<bool>(*out);
916-
}
917-
case SignConfiguration::Mode::Verify: {
918-
auto buf = DataPointer::Alloc(1);
919-
static_cast<char*>(buf.get())[0] = VerifyPrehashed(key,
920-
params.digest,
921-
params.data,
922-
params.signature,
923-
padding,
924-
salt_length);
925-
*out = ByteSource::Allocated(buf.release());
926-
return true;
927-
}
928-
}
929-
}
904+
bool can_fallback_to_prehash =
905+
CanFallbackToPrehashedSignVerify(key, params.digest, has_context);
930906

931907
auto context = EVPMDCtxPointer::New();
932908
if (!context) [[unlikely]]
@@ -975,6 +951,16 @@ bool SignTraits::DeriveBits(Environment* env,
975951
*out = ByteSource::Allocated(data.release());
976952
} else {
977953
auto data = context.sign(params.data);
954+
if (!data && can_fallback_to_prehash) {
955+
*out = SignPrehashed(env,
956+
key,
957+
params.digest,
958+
params.data,
959+
padding,
960+
salt_length,
961+
params.dsa_encoding);
962+
return static_cast<bool>(*out);
963+
}
978964
if (!data) [[unlikely]] {
979965
return false;
980966
}
@@ -992,9 +978,18 @@ bool SignTraits::DeriveBits(Environment* env,
992978
case SignConfiguration::Mode::Verify: {
993979
auto buf = DataPointer::Alloc(1);
994980
static_cast<char*>(buf.get())[0] = 0;
995-
if (context.verify(params.data, params.signature) &&
981+
int verify_result = context.verifyOneShot(params.data, params.signature);
982+
if (verify_result == 1 &&
996983
!HasSmallOrderEdDsaPoint(key, params.signature)) {
997984
static_cast<char*>(buf.get())[0] = 1;
985+
} else if (verify_result < 0 && can_fallback_to_prehash &&
986+
VerifyPrehashed(key,
987+
params.digest,
988+
params.data,
989+
params.signature,
990+
padding,
991+
salt_length)) {
992+
static_cast<char*>(buf.get())[0] = 1;
998993
}
999994
*out = ByteSource::Allocated(buf.release());
1000995
}

test/parallel/test-crypto-sign-verify.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,44 @@ if (hasOpenSSL(3, 2)) {
625625
assert.throws(() => crypto.verify(null, data, 'test', input), errObj);
626626
});
627627

628+
// Preserve the current behavior from https://github.com/nodejs/node/issues/53761:
629+
// one-shot verify does not accept SM2 signatures produced by the streaming path.
630+
if (hasOpenSSL(3) && crypto.getHashes().includes('sm3')) {
631+
const data = Buffer.from('AABB');
632+
const privateKey = crypto.createPrivateKey(`-----BEGIN PRIVATE KEY-----
633+
MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgbjCNHopgvyGVfLaP
634+
PamI9E9lf6jXT+xm1Pns1t/xQTihRANCAATV+I7HUGF2gC+miVl3JfjpoZaU2hrZ
635+
QqHwKUNtIDE/uxxWNLBbYKaiLOWrbYA8skrWQWl3RkbXW4ZI28afRw9g
636+
-----END PRIVATE KEY-----
637+
`);
638+
const publicKey = crypto.createPublicKey(`-----BEGIN PUBLIC KEY-----
639+
MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE1fiOx1BhdoAvpolZdyX46aGWlNoa
640+
2UKh8ClDbSAxP7scVjSwW2Cmoizlq22APLJK1kFpd0ZG11uGSNvGn0cPYA==
641+
-----END PUBLIC KEY-----`);
642+
const validOneShotSignature = Buffer.from(
643+
'3045022100cf800e13a0ead14be0ec9d614257fb6f409187642404dccb3a43' +
644+
'74ace9fc162602206bf6820b8902ae9ce4cd10708f188fddd5bc2a41db2c47' +
645+
'c003ca8265a00396ae',
646+
'hex');
647+
const streamingOnlySignature = Buffer.from(
648+
'3044022056cc3567aa4842c4b1c5f9de75059dbaf63efab5aa34097e5a85' +
649+
'47419aa3175e02206deb865046f12b25f111922da6858c7e97e475b656604' +
650+
'679bcd4a0b05d8c76f1',
651+
'hex');
652+
653+
assert.strictEqual(
654+
crypto.verify('sm3', data, publicKey, validOneShotSignature),
655+
true);
656+
assert.strictEqual(
657+
crypto.verify('sm3', data, publicKey, streamingOnlySignature),
658+
false);
659+
660+
const generatedSignature = crypto.sign('sm3', data, privateKey);
661+
assert.strictEqual(
662+
crypto.verify('sm3', data, publicKey, generatedSignature),
663+
true);
664+
}
665+
628666
{
629667
const data = Buffer.from('Hello world');
630668
const keys = [['ec-key.pem', 64], ['dsa_private_1025.pem', 40]];

0 commit comments

Comments
 (0)