Skip to content

Commit eaaaa0d

Browse files
tniessenaddaleax
authored andcommitted
crypto: always accept private keys as public keys
Some APIs already accept private keys instead of public keys. This changes all relevant crypto APIs to do so. PR-URL: #25217 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Sam Roberts <vieuxtech@gmail.com>
1 parent 1e20c5e commit eaaaa0d

5 files changed

Lines changed: 34 additions & 42 deletions

File tree

‎doc/api/crypto.md‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1379,6 +1379,9 @@ This can be called many times with new data as it is streamed.
13791379
<!-- YAML
13801380
added: v0.1.92
13811381
changes:
1382+
- version: REPLACEME
1383+
pr-url: https://github.com/nodejs/node/pull/25217
1384+
description: The key can now be a private key.
13821385
- version: v8.0.0
13831386
pr-url: https://github.com/nodejs/node/pull/11705
13841387
description: Support for RSASSA-PSS and additional options was added.
@@ -1419,6 +1422,9 @@ The `verify` object can not be used again after `verify.verify()` has been
14191422
called. Multiple calls to `verify.verify()` will result in an error being
14201423
thrown.
14211424

1425+
Because public keys can be derived from private keys, a private key may
1426+
be passed instead of a public key.
1427+
14221428
## `crypto` module methods and properties
14231429

14241430
### crypto.constants
@@ -1829,6 +1835,10 @@ must be an object with the properties described above.
18291835
### crypto.createPublicKey(key)
18301836
<!-- YAML
18311837
added: v11.6.0
1838+
changes:
1839+
- version: REPLACEME
1840+
pr-url: https://github.com/nodejs/node/pull/25217
1841+
description: The `key` argument can now be a private key.
18321842
-->
18331843
*`key` {Object | string | Buffer}
18341844
-`key`: {string | Buffer}
@@ -1843,6 +1853,12 @@ must be an object with the properties described above.
18431853

18441854
If the format is `'pem'`, the `'key'` may also be an X.509 certificate.
18451855

1856+
Because public keys can be derived from private keys, a private key may be
1857+
passed instead of a public key. In that case, this function behaves as if
1858+
[`crypto.createPrivateKey()`][] had been called, except that the type of the
1859+
returned `KeyObject` will be `public` and that the private key cannot be
1860+
extracted from the returned `KeyObject`.
1861+
18461862
### crypto.createSecretKey(key)
18471863
<!-- YAML
18481864
added: v11.6.0

‎lib/internal/crypto/keys.js‎

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -261,10 +261,6 @@ function prepareAsymmetricKey(key, isPublic, allowKeyObject = true) {
261261
}
262262
}
263263

264-
functionpreparePublicKey(key,allowKeyObject){
265-
returnprepareAsymmetricKey(key,true,allowKeyObject);
266-
}
267-
268264
functionpreparePrivateKey(key,allowKeyObject){
269265
returnprepareAsymmetricKey(key,false,allowKeyObject);
270266
}
@@ -300,7 +296,7 @@ function createSecretKey(key) {
300296
}
301297

302298
functioncreatePublicKey(key){
303-
const{ format, type, data }=preparePublicKey(key,false);
299+
const{ format, type, data }=preparePublicOrPrivateKey(key,false);
304300
consthandle=newKeyObjectHandle(kKeyTypePublic);
305301
handle.init(data,format,type);
306302
returnnewPublicKeyObject(handle);
@@ -326,7 +322,6 @@ module.exports = {
326322
// These are designed for internal use only and should not be exposed.
327323
parsePublicKeyEncoding,
328324
parsePrivateKeyEncoding,
329-
preparePublicKey,
330325
preparePrivateKey,
331326
preparePublicOrPrivateKey,
332327
prepareSecretKey,

‎lib/internal/crypto/sig.js‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const {
1919
}=require('internal/crypto/util');
2020
const{
2121
preparePrivateKey,
22-
preparePublicKey
22+
preparePublicOrPrivateKey
2323
}=require('internal/crypto/keys');
2424
const{ Writable }=require('stream');
2525
const{ inherits }=require('util');
@@ -111,8 +111,9 @@ Verify.prototype.verify = function verify(options, signature, sigEncoding) {
111111
const{
112112
data,
113113
format,
114-
type
115-
}=preparePublicKey(options,true);
114+
type,
115+
passphrase
116+
}=preparePublicOrPrivateKey(options,true);
116117

117118
sigEncoding=sigEncoding||getDefaultEncoding();
118119

@@ -124,7 +125,7 @@ Verify.prototype.verify = function verify(options, signature, sigEncoding) {
124125
signature=validateArrayBufferView(toBuf(signature,sigEncoding),
125126
'signature');
126127

127-
returnthis[kHandle].verify(data,format,type,signature,
128+
returnthis[kHandle].verify(data,format,type,passphrase,signature,
128129
rsaPadding,pssSaltLength);
129130
};
130131

‎src/node_crypto.cc‎

Lines changed: 2 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2995,30 +2995,6 @@ static PublicKeyEncodingConfig GetPublicKeyEncodingFromJs(
29952995
return result;
29962996
}
29972997

2998-
static ManagedEVPPKey GetPublicKeyFromJs(
2999-
const FunctionCallbackInfo<Value>& args,
3000-
unsignedint* offset,
3001-
bool allow_key_object) {
3002-
if (args[*offset]->IsString() || Buffer::HasInstance(args[*offset])) {
3003-
Environment* env = Environment::GetCurrent(args);
3004-
ByteSource key = ByteSource::FromStringOrBuffer(env, args[(*offset)++]);
3005-
PublicKeyEncodingConfig config =
3006-
GetPublicKeyEncodingFromJs(args, offset, kKeyContextInput);
3007-
EVPKeyPointer pkey;
3008-
ParsePublicKey(&pkey, config, key.get(), key.size());
3009-
if (!pkey)
3010-
ThrowCryptoError(env, ERR_get_error(), "Failed to read public key");
3011-
returnManagedEVPPKey(pkey.release());
3012-
} else {
3013-
CHECK(args[*offset]->IsObject() && allow_key_object);
3014-
KeyObject* key;
3015-
ASSIGN_OR_RETURN_UNWRAP(&key, args[*offset].As<Object>(), ManagedEVPPKey());
3016-
CHECK_EQ(key->GetKeyType(), kKeyTypePublic);
3017-
(*offset) += 3;
3018-
return key->GetAsymmetricKey();
3019-
}
3020-
}
3021-
30222998
static NonCopyableMaybe<PrivateKeyEncodingConfig> GetPrivateKeyEncodingFromJs(
30232999
const FunctionCallbackInfo<Value>& args,
30243000
unsignedint* offset,
@@ -3380,7 +3356,7 @@ void KeyObject::Init(const FunctionCallbackInfo<Value>& args) {
33803356
CHECK_EQ(args.Length(), 3);
33813357

33823358
offset = 0;
3383-
pkey = GetPublicKeyFromJs(args, &offset, false);
3359+
pkey = GetPublicOrPrivateKeyFromJs(args, &offset, false);
33843360
if (!pkey)
33853361
return;
33863362
key->InitPublic(pkey);
@@ -4662,7 +4638,7 @@ void Verify::VerifyFinal(const FunctionCallbackInfo<Value>& args) {
46624638
ASSIGN_OR_RETURN_UNWRAP(&verify, args.Holder());
46634639

46644640
unsignedint offset = 0;
4665-
ManagedEVPPKey pkey = GetPublicKeyFromJs(args, &offset, true);
4641+
ManagedEVPPKey pkey = GetPublicOrPrivateKeyFromJs(args, &offset, true);
46664642

46674643
char* hbuf = Buffer::Data(args[offset]);
46684644
ssize_t hlen = Buffer::Length(args[offset]);

‎test/parallel/test-crypto-keygen.js‎

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,19 +31,23 @@ function assertApproximateSize(key, expectedSize) {
3131
functiontestEncryptDecrypt(publicKey,privateKey){
3232
constmessage='Hello Node.js world!';
3333
constplaintext=Buffer.from(message,'utf8');
34-
constciphertext=publicEncrypt(publicKey,plaintext);
35-
constreceived=privateDecrypt(privateKey,ciphertext);
36-
assert.strictEqual(received.toString('utf8'),message);
34+
for(constkeyof[publicKey,privateKey]){
35+
constciphertext=publicEncrypt(key,plaintext);
36+
constreceived=privateDecrypt(privateKey,ciphertext);
37+
assert.strictEqual(received.toString('utf8'),message);
38+
}
3739
}
3840

3941
// Tests that a key pair can be used for signing / verification.
4042
functiontestSignVerify(publicKey,privateKey){
4143
constmessage='Hello Node.js world!';
4244
constsignature=createSign('SHA256').update(message)
4345
.sign(privateKey,'hex');
44-
constokay=createVerify('SHA256').update(message)
45-
.verify(publicKey,signature,'hex');
46-
assert(okay);
46+
for(constkeyof[publicKey,privateKey]){
47+
constokay=createVerify('SHA256').update(message)
48+
.verify(key,signature,'hex');
49+
assert(okay);
50+
}
4751
}
4852

4953
// Constructs a regular expression for a PEM-encoded key with the given label.

0 commit comments

Comments
 (0)