Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 54197ea

Browse filesBrowse files
tniessentargos
authored andcommitted
crypto: extend RSA-OAEP support with oaepHash
This adds an oaepHash option to asymmetric encryption which allows users to specify a hash function when using OAEP padding. This feature is required for interoperability with WebCrypto applications. PR-URL: #28335 Fixes: #25756 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Sam Roberts <vieuxtech@gmail.com>
1 parent 000999c commit 54197ea
Copy full SHA for 54197ea

File tree

Expand file treeCollapse file tree

5 files changed

+117
-5
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

5 files changed

+117
-5
lines changed
Open diff view settings
Collapse file

‎doc/api/crypto.md‎

Copy file name to clipboardExpand all lines: doc/api/crypto.md
+10Lines changed: 10 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -2307,11 +2307,16 @@ An array of supported digest functions can be retrieved using
23072307
<!-- YAML
23082308
added: v0.11.14
23092309
changes:
2310+
- version: REPLACEME
2311+
pr-url: https://github.com/nodejs/node/pull/28335
2312+
description: The `oaepHash` option was added.
23102313
- version: v11.6.0
23112314
pr-url: https://github.com/nodejs/node/pull/24234
23122315
description: This function now supports key objects.
23132316
-->
23142317
* `privateKey` {Object | string | Buffer | KeyObject}
2318+
- `oaepHash` {string} The hash function to use for OAEP padding.
2319+
**Default:** `'sha1'`
23152320
- `padding` {crypto.constants} An optional padding value defined in
23162321
`crypto.constants`, which may be: `crypto.constants.RSA_NO_PADDING`,
23172322
`crypto.constants.RSA_PKCS1_PADDING`, or
@@ -2383,12 +2388,17 @@ be passed instead of a public key.
23832388
<!-- YAML
23842389
added: v0.11.14
23852390
changes:
2391+
- version: REPLACEME
2392+
pr-url: https://github.com/nodejs/node/pull/28335
2393+
description: The `oaepHash` option was added.
23862394
- version: v11.6.0
23872395
pr-url: https://github.com/nodejs/node/pull/24234
23882396
description: This function now supports key objects.
23892397
-->
23902398
* `key` {Object | string | Buffer | KeyObject}
23912399
- `key` {string | Buffer | KeyObject} A PEM encoded public or private key.
2400+
- `oaepHash` {string} The hash function to use for OAEP padding.
2401+
**Default:** `'sha1'`
23922402
- `passphrase` {string | Buffer} An optional passphrase for the private key.
23932403
- `padding` {crypto.constants} An optional padding value defined in
23942404
`crypto.constants`, which may be: `crypto.constants.RSA_NO_PADDING`,
Collapse file

‎lib/internal/crypto/cipher.js‎

Copy file name to clipboardExpand all lines: lib/internal/crypto/cipher.js
+4-1Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,10 @@ function rsaFunctionFor(method, defaultPadding, keyType) {
5050
preparePrivateKey(options) :
5151
preparePublicOrPrivateKey(options);
5252
const padding = options.padding || defaultPadding;
53-
return method(data, format, type, passphrase, buffer, padding);
53+
const { oaepHash } = options;
54+
if (oaepHash !== undefined && typeof oaepHash !== 'string')
55+
throw new ERR_INVALID_ARG_TYPE('options.oaepHash', 'string', oaepHash);
56+
return method(data, format, type, passphrase, buffer, padding, oaepHash);
5457
};
5558
}
5659

Collapse file

‎src/node_crypto.cc‎

Copy file name to clipboardExpand all lines: src/node_crypto.cc
+11Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5225,6 +5225,7 @@ template <PublicKeyCipher::Operation operation,
52255225
bool PublicKeyCipher::Cipher(Environment* env,
52265226
const ManagedEVPPKey& pkey,
52275227
int padding,
5228+
const char* oaep_hash,
52285229
const unsigned char* data,
52295230
int len,
52305231
AllocatedBuffer* out) {
@@ -5236,6 +5237,12 @@ bool PublicKeyCipher::Cipher(Environment* env,
52365237
if (EVP_PKEY_CTX_set_rsa_padding(ctx.get(), padding) <= 0)
52375238
return false;
52385239

5240+
if (oaep_hash != nullptr) {
5241+
if (!EVP_PKEY_CTX_md(ctx.get(), EVP_PKEY_OP_TYPE_CRYPT,
5242+
EVP_PKEY_CTRL_RSA_OAEP_MD, oaep_hash))
5243+
return false;
5244+
}
5245+
52395246
size_t out_len = 0;
52405247
if (EVP_PKEY_cipher(ctx.get(), nullptr, &out_len, data, len) <= 0)
52415248
return false;
@@ -5272,6 +5279,9 @@ void PublicKeyCipher::Cipher(const FunctionCallbackInfo<Value>& args) {
52725279
uint32_t padding;
52735280
if (!args[offset + 1]->Uint32Value(env->context()).To(&padding)) return;
52745281

5282+
const node::Utf8Value oaep_str(env->isolate(), args[offset + 2]);
5283+
const char* oaep_hash = args[offset + 2]->IsString() ? *oaep_str : nullptr;
5284+
52755285
AllocatedBuffer out;
52765286

52775287
ClearErrorOnReturn clear_error_on_return;
@@ -5280,6 +5290,7 @@ void PublicKeyCipher::Cipher(const FunctionCallbackInfo<Value>& args) {
52805290
env,
52815291
pkey,
52825292
padding,
5293+
oaep_hash,
52835294
buf.data(),
52845295
buf.length(),
52855296
&out);
Collapse file

‎src/node_crypto.h‎

Copy file name to clipboardExpand all lines: src/node_crypto.h
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,7 @@ class PublicKeyCipher {
713713
static bool Cipher(Environment* env,
714714
const ManagedEVPPKey& pkey,
715715
int padding,
716+
const char* oaep_hash,
716717
const unsigned char* data,
717718
int len,
718719
AllocatedBuffer* out);
Collapse file

‎test/parallel/test-crypto-rsa-dsa.js‎

Copy file name to clipboardExpand all lines: test/parallel/test-crypto-rsa-dsa.js
+91-4Lines changed: 91 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ const decryptError = {
154154
}, decryptError);
155155
}
156156

157-
function test_rsa(padding) {
157+
function test_rsa(padding, encryptOaepHash, decryptOaepHash) {
158158
const size = (padding === 'RSA_NO_PADDING') ? rsaKeySize / 8 : 32;
159159
const input = Buffer.allocUnsafe(size);
160160
for (let i = 0; i < input.length; i++)
@@ -165,18 +165,21 @@ function test_rsa(padding) {
165165

166166
const encryptedBuffer = crypto.publicEncrypt({
167167
key: rsaPubPem,
168-
padding: padding
168+
padding: padding,
169+
oaepHash: encryptOaepHash
169170
}, bufferToEncrypt);
170171

171172
let decryptedBuffer = crypto.privateDecrypt({
172173
key: rsaKeyPem,
173-
padding: padding
174+
padding: padding,
175+
oaepHash: decryptOaepHash
174176
}, encryptedBuffer);
175177
assert.deepStrictEqual(decryptedBuffer, input);
176178

177179
decryptedBuffer = crypto.privateDecrypt({
178180
key: rsaPkcs8KeyPem,
179-
padding: padding
181+
padding: padding,
182+
oaepHash: decryptOaepHash
180183
}, encryptedBuffer);
181184
assert.deepStrictEqual(decryptedBuffer, input);
182185
}
@@ -185,6 +188,90 @@ test_rsa('RSA_NO_PADDING');
185188
test_rsa('RSA_PKCS1_PADDING');
186189
test_rsa('RSA_PKCS1_OAEP_PADDING');
187190

191+
// Test OAEP with different hash functions.
192+
test_rsa('RSA_PKCS1_OAEP_PADDING', undefined, 'sha1');
193+
test_rsa('RSA_PKCS1_OAEP_PADDING', 'sha1', undefined);
194+
test_rsa('RSA_PKCS1_OAEP_PADDING', 'sha256', 'sha256');
195+
test_rsa('RSA_PKCS1_OAEP_PADDING', 'sha512', 'sha512');
196+
197+
// The following RSA-OAEP test cases were created using the WebCrypto API to
198+
// ensure compatibility when using non-SHA1 hash functions.
199+
{
200+
function testDecrypt(oaepHash, ciphertext) {
201+
const decrypted = crypto.privateDecrypt({
202+
key: rsaPkcs8KeyPem,
203+
oaepHash
204+
}, Buffer.from(ciphertext, 'hex'));
205+
206+
assert.strictEqual(decrypted.toString('utf8'), 'Hello Node.js');
207+
}
208+
209+
testDecrypt(undefined, '16ece59cf985a8cf1a3434e4b9707c922c20638fdf9abf7e5dc' +
210+
'7943f4136899348c54116d15b2c17563b9c7143f9d5b85b4561' +
211+
'5ad0598ea6d21c900f3957b65400612306a9bebae441f005646' +
212+
'f7a7c97129a103ab54e777168ef966514adb17786b968ea0ff4' +
213+
'30a524904c4a11c683764b7c8dbb60df0952768381cdba4d665' +
214+
'e5006034393a10d56d33e75b2714db824a18da46441ef7f94a3' +
215+
'4a7058c0bbad0394083a038558bcc6dd370f8e518e1bd8d73b2' +
216+
'96fc51d77da44799e4ee774926ded7910e8768f92db76f63107' +
217+
'338d33354b735d3ad094240dbd7ffdfda27ef0255306dcf4a64' +
218+
'62849492abd1a97fdd37743ff87c4d2ec89866c5cdbb696bd2b' +
219+
'30');
220+
testDecrypt('sha1', '16ece59cf985a8cf1a3434e4b9707c922c20638fdf9abf7e5dc794' +
221+
'3f4136899348c54116d15b2c17563b9c7143f9d5b85b45615ad059' +
222+
'8ea6d21c900f3957b65400612306a9bebae441f005646f7a7c9712' +
223+
'9a103ab54e777168ef966514adb17786b968ea0ff430a524904c4a' +
224+
'11c683764b7c8dbb60df0952768381cdba4d665e5006034393a10d' +
225+
'56d33e75b2714db824a18da46441ef7f94a34a7058c0bbad039408' +
226+
'3a038558bcc6dd370f8e518e1bd8d73b296fc51d77da44799e4ee7' +
227+
'74926ded7910e8768f92db76f63107338d33354b735d3ad094240d' +
228+
'bd7ffdfda27ef0255306dcf4a6462849492abd1a97fdd37743ff87' +
229+
'c4d2ec89866c5cdbb696bd2b30');
230+
testDecrypt('sha256', '16ccf09afe5eb0130182b9fc1ca4af61a38e772047cac42146bf' +
231+
'a0fa5879aa9639203e4d01442d212ff95bddfbe4661222215a2e' +
232+
'91908c37ab926edea7cfc53f83357bc27f86af0f5f2818ae141f' +
233+
'4e9e934d4e66189aff30f062c9c3f6eb9bc495a59082cb978f99' +
234+
'b56ce5fa530a8469e46129258e5c42897cb194b6805e936e5cbb' +
235+
'eaa535bad6b1d3cdfc92119b7dd325a2e6d2979e316bdacc9f80' +
236+
'e29c7bbdf6846d738e380deadcb48df8c1e8aabf7a9dd2f8c71d' +
237+
'6681dbec7dcadc01887c51288674268796bc77fdf8f1c94c9ca5' +
238+
'0b1cc7cddbaf4e56cb151d23e2c699d2844c0104ee2e7e9dcdb9' +
239+
'07cfab43339120a40c59ca54f32b8d21b48a29656c77');
240+
testDecrypt('sha512', '831b72e8dd91841729ecbddf2647d6f19dc0094734f8803d8c65' +
241+
'1b5655a12ae6156b74d9b594bcc0eacd002728380b94f46e8657' +
242+
'f130f354e03b6e7815ee257eda78dba296d67d24410c31c48e58' +
243+
'75cc79e4bde594b412be5f357f57a7ac1f1d18b718e408df162d' +
244+
'1795508e6a0616192b647ad942ea068a44fb2b323d35a3a61b92' +
245+
'6feb105d6c0b2a8fc8050222d1cf4a9e44da1f95bbc677fd6437' +
246+
'49c6c89ac551d072f04cd9320c97a8d94755c8a804954c082bed' +
247+
'7fa59199a00aca154c14a7b584b63c538daf9b9c7c90abfca193' +
248+
'87d2131f9d9b9ecfc8672249c33144d1be3bfc41558a13f99466' +
249+
'3661a3af24fd0a97619d508db36f5fc131af86fc68cf');
250+
}
251+
252+
// Test invalid oaepHash options.
253+
for (const fn of [crypto.publicEncrypt, crypto.privateDecrypt]) {
254+
assert.throws(() => {
255+
fn({
256+
key: rsaPubPem,
257+
oaepHash: 'Hello world'
258+
}, Buffer.alloc(10));
259+
}, {
260+
code: 'ERR_OSSL_EVP_INVALID_DIGEST'
261+
});
262+
263+
for (const oaepHash of [0, false, null, Symbol(), () => {}]) {
264+
common.expectsError(() => {
265+
fn({
266+
key: rsaPubPem,
267+
oaepHash
268+
}, Buffer.alloc(10));
269+
}, {
270+
code: 'ERR_INVALID_ARG_TYPE'
271+
});
272+
}
273+
}
274+
188275
// Test RSA key signing/verification
189276
let rsaSign = crypto.createSign('SHA1');
190277
let rsaVerify = crypto.createVerify('SHA1');

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.