diff options
| -rw-r--r-- | meta-oe/recipes-devtools/nodejs/nodejs/CVE-2023-46809.patch | 625 | ||||
| -rw-r--r-- | meta-oe/recipes-devtools/nodejs/nodejs_16.20.2.bb | 1 |
2 files changed, 626 insertions, 0 deletions
diff --git a/meta-oe/recipes-devtools/nodejs/nodejs/CVE-2023-46809.patch b/meta-oe/recipes-devtools/nodejs/nodejs/CVE-2023-46809.patch new file mode 100644 index 0000000000..991d39fcf9 --- /dev/null +++ b/meta-oe/recipes-devtools/nodejs/nodejs/CVE-2023-46809.patch | |||
| @@ -0,0 +1,625 @@ | |||
| 1 | From d3d357ab096884f10f5d2f164149727eea875635 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Michael Dawson <midawson@redhat.com> | ||
| 3 | Date: Thu, 4 Jan 2024 21:32:51 +0000 | ||
| 4 | Subject: [PATCH] crypto: disable PKCS#1 padding for privateDecrypt | ||
| 5 | |||
| 6 | Refs: https://hackerone.com/bugs?subject=nodejs&report_id=2269177 | ||
| 7 | |||
| 8 | Disable RSA_PKCS1_PADDING for crypto.privateDecrypt() in order | ||
| 9 | to protect against the Marvin attack. | ||
| 10 | |||
| 11 | Includes a security revert flag that can be used to restore | ||
| 12 | support. | ||
| 13 | |||
| 14 | Signed-off-by: Michael Dawson <midawson@redhat.com> | ||
| 15 | PR-URL: https://github.com/nodejs-private/node-private/pull/525 | ||
| 16 | Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com> | ||
| 17 | Reviewed-By: Matteo Collina <matteo.collina@gmail.com> | ||
| 18 | |||
| 19 | CVE-ID: CVE-2023-46809 | ||
| 20 | |||
| 21 | Upstream-Status: Backport [https://github.com/nodejs/node/commit/d3d357ab096884f1] | ||
| 22 | Signed-off-by: Archana Polampalli <archana.polampalli@windriver.com> | ||
| 23 | --- | ||
| 24 | src/crypto/crypto_cipher.cc | 28 ++ | ||
| 25 | src/node_revert.h | 1 + | ||
| 26 | test/parallel/test-crypto-rsa-dsa-revert.js | 475 ++++++++++++++++++++ | ||
| 27 | test/parallel/test-crypto-rsa-dsa.js | 42 +- | ||
| 28 | 4 files changed, 533 insertions(+), 13 deletions(-) | ||
| 29 | create mode 100644 test/parallel/test-crypto-rsa-dsa-revert.js | ||
| 30 | |||
| 31 | diff --git a/src/crypto/crypto_cipher.cc b/src/crypto/crypto_cipher.cc | ||
| 32 | index 10579ce..0311c68 100644 | ||
| 33 | --- a/src/crypto/crypto_cipher.cc | ||
| 34 | +++ b/src/crypto/crypto_cipher.cc | ||
| 35 | @@ -6,6 +6,7 @@ | ||
| 36 | #include "node_buffer.h" | ||
| 37 | #include "node_internals.h" | ||
| 38 | #include "node_process-inl.h" | ||
| 39 | +#include "node_revert.h" | ||
| 40 | #include "v8.h" | ||
| 41 | |||
| 42 | namespace node { | ||
| 43 | @@ -1061,6 +1062,33 @@ void PublicKeyCipher::Cipher(const FunctionCallbackInfo<Value>& args) { | ||
| 44 | uint32_t padding; | ||
| 45 | if (!args[offset + 1]->Uint32Value(env->context()).To(&padding)) return; | ||
| 46 | |||
| 47 | + if (EVP_PKEY_cipher == EVP_PKEY_decrypt && | ||
| 48 | + operation == PublicKeyCipher::kPrivate && padding == RSA_PKCS1_PADDING && | ||
| 49 | + !IsReverted(SECURITY_REVERT_CVE_2023_46809)) { | ||
| 50 | + EVPKeyCtxPointer ctx(EVP_PKEY_CTX_new(pkey.get(), nullptr)); | ||
| 51 | + CHECK(ctx); | ||
| 52 | + | ||
| 53 | + if (EVP_PKEY_decrypt_init(ctx.get()) <= 0) { | ||
| 54 | + return ThrowCryptoError(env, ERR_get_error()); | ||
| 55 | + } | ||
| 56 | + | ||
| 57 | + int rsa_pkcs1_implicit_rejection = | ||
| 58 | + EVP_PKEY_CTX_ctrl_str(ctx.get(), "rsa_pkcs1_implicit_rejection", "1"); | ||
| 59 | + // From the doc -2 means that the option is not supported. | ||
| 60 | + // The default for the option is enabled and if it has been | ||
| 61 | + // specifically disabled we want to respect that so we will | ||
| 62 | + // not throw an error if the option is supported regardless | ||
| 63 | + // of how it is set. The call to set the value | ||
| 64 | + // will not affect what is used since a different context is | ||
| 65 | + // used in the call if the option is supported | ||
| 66 | + if (rsa_pkcs1_implicit_rejection <= 0) { | ||
| 67 | + return THROW_ERR_INVALID_ARG_VALUE( | ||
| 68 | + env, | ||
| 69 | + "RSA_PKCS1_PADDING is no longer supported for private decryption," | ||
| 70 | + " this can be reverted with --security-revert=CVE-2023-46809"); | ||
| 71 | + } | ||
| 72 | + } | ||
| 73 | + | ||
| 74 | const EVP_MD* digest = nullptr; | ||
| 75 | if (args[offset + 2]->IsString()) { | ||
| 76 | const Utf8Value oaep_str(env->isolate(), args[offset + 2]); | ||
| 77 | diff --git a/src/node_revert.h b/src/node_revert.h | ||
| 78 | index 83dcb62..bc2a288 100644 | ||
| 79 | --- a/src/node_revert.h | ||
| 80 | +++ b/src/node_revert.h | ||
| 81 | @@ -18,6 +18,7 @@ namespace node { | ||
| 82 | #define SECURITY_REVERSIONS(XX) \ | ||
| 83 | XX(CVE_2021_44531, "CVE-2021-44531", "Cert Verif Bypass via URI SAN") \ | ||
| 84 | XX(CVE_2021_44532, "CVE-2021-44532", "Cert Verif Bypass via Str Inject") \ | ||
| 85 | + XX(CVE_2023_46809, "CVE-2023-46809", "Marvin attack on PKCS#1 padding") \ | ||
| 86 | // XX(CVE_2016_PEND, "CVE-2016-PEND", "Vulnerability Title") | ||
| 87 | |||
| 88 | enum reversion { | ||
| 89 | diff --git a/test/parallel/test-crypto-rsa-dsa-revert.js b/test/parallel/test-crypto-rsa-dsa-revert.js | ||
| 90 | new file mode 100644 | ||
| 91 | index 0000000..84ec8f6 | ||
| 92 | --- /dev/null | ||
| 93 | +++ b/test/parallel/test-crypto-rsa-dsa-revert.js | ||
| 94 | @@ -0,0 +1,475 @@ | ||
| 95 | +'use strict'; | ||
| 96 | +// Flags: --security-revert=CVE-2023-46809 | ||
| 97 | +const common = require('../common'); | ||
| 98 | +if (!common.hasCrypto) | ||
| 99 | + common.skip('missing crypto'); | ||
| 100 | + | ||
| 101 | +const assert = require('assert'); | ||
| 102 | +const crypto = require('crypto'); | ||
| 103 | + | ||
| 104 | +const constants = crypto.constants; | ||
| 105 | + | ||
| 106 | +const fixtures = require('../common/fixtures'); | ||
| 107 | + | ||
| 108 | +// Test certificates | ||
| 109 | +const certPem = fixtures.readKey('rsa_cert.crt'); | ||
| 110 | +const keyPem = fixtures.readKey('rsa_private.pem'); | ||
| 111 | +const rsaKeySize = 2048; | ||
| 112 | +const rsaPubPem = fixtures.readKey('rsa_public.pem', 'ascii'); | ||
| 113 | +const rsaKeyPem = fixtures.readKey('rsa_private.pem', 'ascii'); | ||
| 114 | +const rsaKeyPemEncrypted = fixtures.readKey('rsa_private_encrypted.pem', | ||
| 115 | + 'ascii'); | ||
| 116 | +const dsaPubPem = fixtures.readKey('dsa_public.pem', 'ascii'); | ||
| 117 | +const dsaKeyPem = fixtures.readKey('dsa_private.pem', 'ascii'); | ||
| 118 | +const dsaKeyPemEncrypted = fixtures.readKey('dsa_private_encrypted.pem', | ||
| 119 | + 'ascii'); | ||
| 120 | +const rsaPkcs8KeyPem = fixtures.readKey('rsa_private_pkcs8.pem'); | ||
| 121 | +const dsaPkcs8KeyPem = fixtures.readKey('dsa_private_pkcs8.pem'); | ||
| 122 | + | ||
| 123 | +const ec = new TextEncoder(); | ||
| 124 | + | ||
| 125 | +const openssl1DecryptError = { | ||
| 126 | + message: 'error:06065064:digital envelope routines:EVP_DecryptFinal_ex:' + | ||
| 127 | + 'bad decrypt', | ||
| 128 | + code: 'ERR_OSSL_EVP_BAD_DECRYPT', | ||
| 129 | + reason: 'bad decrypt', | ||
| 130 | + function: 'EVP_DecryptFinal_ex', | ||
| 131 | + library: 'digital envelope routines', | ||
| 132 | +}; | ||
| 133 | + | ||
| 134 | +const decryptError = common.hasOpenSSL3 ? | ||
| 135 | + { message: 'error:1C800064:Provider routines::bad decrypt' } : | ||
| 136 | + openssl1DecryptError; | ||
| 137 | + | ||
| 138 | +const decryptPrivateKeyError = common.hasOpenSSL3 ? { | ||
| 139 | + message: 'error:1C800064:Provider routines::bad decrypt', | ||
| 140 | +} : openssl1DecryptError; | ||
| 141 | + | ||
| 142 | +function getBufferCopy(buf) { | ||
| 143 | + return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength); | ||
| 144 | +} | ||
| 145 | + | ||
| 146 | +// Test RSA encryption/decryption | ||
| 147 | +{ | ||
| 148 | + const input = 'I AM THE WALRUS'; | ||
| 149 | + const bufferToEncrypt = Buffer.from(input); | ||
| 150 | + const bufferPassword = Buffer.from('password'); | ||
| 151 | + | ||
| 152 | + let encryptedBuffer = crypto.publicEncrypt(rsaPubPem, bufferToEncrypt); | ||
| 153 | + | ||
| 154 | + // Test other input types | ||
| 155 | + let otherEncrypted; | ||
| 156 | + { | ||
| 157 | + const ab = getBufferCopy(ec.encode(rsaPubPem)); | ||
| 158 | + const ab2enc = getBufferCopy(bufferToEncrypt); | ||
| 159 | + | ||
| 160 | + crypto.publicEncrypt(ab, ab2enc); | ||
| 161 | + crypto.publicEncrypt(new Uint8Array(ab), new Uint8Array(ab2enc)); | ||
| 162 | + crypto.publicEncrypt(new DataView(ab), new DataView(ab2enc)); | ||
| 163 | + otherEncrypted = crypto.publicEncrypt({ | ||
| 164 | + key: Buffer.from(ab).toString('hex'), | ||
| 165 | + encoding: 'hex' | ||
| 166 | + }, Buffer.from(ab2enc).toString('hex')); | ||
| 167 | + } | ||
| 168 | + | ||
| 169 | + let decryptedBuffer = crypto.privateDecrypt(rsaKeyPem, encryptedBuffer); | ||
| 170 | + const otherDecrypted = crypto.privateDecrypt(rsaKeyPem, otherEncrypted); | ||
| 171 | + assert.strictEqual(decryptedBuffer.toString(), input); | ||
| 172 | + assert.strictEqual(otherDecrypted.toString(), input); | ||
| 173 | + | ||
| 174 | + decryptedBuffer = crypto.privateDecrypt(rsaPkcs8KeyPem, encryptedBuffer); | ||
| 175 | + assert.strictEqual(decryptedBuffer.toString(), input); | ||
| 176 | + | ||
| 177 | + let decryptedBufferWithPassword = crypto.privateDecrypt({ | ||
| 178 | + key: rsaKeyPemEncrypted, | ||
| 179 | + passphrase: 'password' | ||
| 180 | + }, encryptedBuffer); | ||
| 181 | + | ||
| 182 | + const otherDecryptedBufferWithPassword = crypto.privateDecrypt({ | ||
| 183 | + key: rsaKeyPemEncrypted, | ||
| 184 | + passphrase: ec.encode('password') | ||
| 185 | + }, encryptedBuffer); | ||
| 186 | + | ||
| 187 | + assert.strictEqual( | ||
| 188 | + otherDecryptedBufferWithPassword.toString(), | ||
| 189 | + decryptedBufferWithPassword.toString()); | ||
| 190 | + | ||
| 191 | + decryptedBufferWithPassword = crypto.privateDecrypt({ | ||
| 192 | + key: rsaKeyPemEncrypted, | ||
| 193 | + passphrase: 'password' | ||
| 194 | + }, encryptedBuffer); | ||
| 195 | + | ||
| 196 | + assert.strictEqual(decryptedBufferWithPassword.toString(), input); | ||
| 197 | + | ||
| 198 | + encryptedBuffer = crypto.publicEncrypt({ | ||
| 199 | + key: rsaKeyPemEncrypted, | ||
| 200 | + passphrase: 'password' | ||
| 201 | + }, bufferToEncrypt); | ||
| 202 | + | ||
| 203 | + decryptedBufferWithPassword = crypto.privateDecrypt({ | ||
| 204 | + key: rsaKeyPemEncrypted, | ||
| 205 | + passphrase: 'password' | ||
| 206 | + }, encryptedBuffer); | ||
| 207 | + assert.strictEqual(decryptedBufferWithPassword.toString(), input); | ||
| 208 | + | ||
| 209 | + encryptedBuffer = crypto.privateEncrypt({ | ||
| 210 | + key: rsaKeyPemEncrypted, | ||
| 211 | + passphrase: bufferPassword | ||
| 212 | + }, bufferToEncrypt); | ||
| 213 | + | ||
| 214 | + decryptedBufferWithPassword = crypto.publicDecrypt({ | ||
| 215 | + key: rsaKeyPemEncrypted, | ||
| 216 | + passphrase: bufferPassword | ||
| 217 | + }, encryptedBuffer); | ||
| 218 | + assert.strictEqual(decryptedBufferWithPassword.toString(), input); | ||
| 219 | + | ||
| 220 | + // Now with explicit RSA_PKCS1_PADDING. | ||
| 221 | + encryptedBuffer = crypto.privateEncrypt({ | ||
| 222 | + padding: crypto.constants.RSA_PKCS1_PADDING, | ||
| 223 | + key: rsaKeyPemEncrypted, | ||
| 224 | + passphrase: bufferPassword | ||
| 225 | + }, bufferToEncrypt); | ||
| 226 | + | ||
| 227 | + decryptedBufferWithPassword = crypto.publicDecrypt({ | ||
| 228 | + padding: crypto.constants.RSA_PKCS1_PADDING, | ||
| 229 | + key: rsaKeyPemEncrypted, | ||
| 230 | + passphrase: bufferPassword | ||
| 231 | + }, encryptedBuffer); | ||
| 232 | + assert.strictEqual(decryptedBufferWithPassword.toString(), input); | ||
| 233 | + | ||
| 234 | + // Omitting padding should be okay because RSA_PKCS1_PADDING is the default. | ||
| 235 | + decryptedBufferWithPassword = crypto.publicDecrypt({ | ||
| 236 | + key: rsaKeyPemEncrypted, | ||
| 237 | + passphrase: bufferPassword | ||
| 238 | + }, encryptedBuffer); | ||
| 239 | + assert.strictEqual(decryptedBufferWithPassword.toString(), input); | ||
| 240 | + | ||
| 241 | + // Now with RSA_NO_PADDING. Plaintext needs to match key size. | ||
| 242 | + // OpenSSL 3.x has a rsa_check_padding that will cause an error if | ||
| 243 | + // RSA_NO_PADDING is used. | ||
| 244 | + if (!common.hasOpenSSL3) { | ||
| 245 | + { | ||
| 246 | + const plaintext = 'x'.repeat(rsaKeySize / 8); | ||
| 247 | + encryptedBuffer = crypto.privateEncrypt({ | ||
| 248 | + padding: crypto.constants.RSA_NO_PADDING, | ||
| 249 | + key: rsaKeyPemEncrypted, | ||
| 250 | + passphrase: bufferPassword | ||
| 251 | + }, Buffer.from(plaintext)); | ||
| 252 | + | ||
| 253 | + decryptedBufferWithPassword = crypto.publicDecrypt({ | ||
| 254 | + padding: crypto.constants.RSA_NO_PADDING, | ||
| 255 | + key: rsaKeyPemEncrypted, | ||
| 256 | + passphrase: bufferPassword | ||
| 257 | + }, encryptedBuffer); | ||
| 258 | + assert.strictEqual(decryptedBufferWithPassword.toString(), plaintext); | ||
| 259 | + } | ||
| 260 | + } | ||
| 261 | + | ||
| 262 | + encryptedBuffer = crypto.publicEncrypt(certPem, bufferToEncrypt); | ||
| 263 | + | ||
| 264 | + decryptedBuffer = crypto.privateDecrypt(keyPem, encryptedBuffer); | ||
| 265 | + assert.strictEqual(decryptedBuffer.toString(), input); | ||
| 266 | + | ||
| 267 | + encryptedBuffer = crypto.publicEncrypt(keyPem, bufferToEncrypt); | ||
| 268 | + | ||
| 269 | + decryptedBuffer = crypto.privateDecrypt(keyPem, encryptedBuffer); | ||
| 270 | + assert.strictEqual(decryptedBuffer.toString(), input); | ||
| 271 | + | ||
| 272 | + encryptedBuffer = crypto.privateEncrypt(keyPem, bufferToEncrypt); | ||
| 273 | + | ||
| 274 | + decryptedBuffer = crypto.publicDecrypt(keyPem, encryptedBuffer); | ||
| 275 | + assert.strictEqual(decryptedBuffer.toString(), input); | ||
| 276 | + | ||
| 277 | + assert.throws(() => { | ||
| 278 | + crypto.privateDecrypt({ | ||
| 279 | + key: rsaKeyPemEncrypted, | ||
| 280 | + passphrase: 'wrong' | ||
| 281 | + }, bufferToEncrypt); | ||
| 282 | + }, decryptError); | ||
| 283 | + | ||
| 284 | + assert.throws(() => { | ||
| 285 | + crypto.publicEncrypt({ | ||
| 286 | + key: rsaKeyPemEncrypted, | ||
| 287 | + passphrase: 'wrong' | ||
| 288 | + }, encryptedBuffer); | ||
| 289 | + }, decryptError); | ||
| 290 | + | ||
| 291 | + encryptedBuffer = crypto.privateEncrypt({ | ||
| 292 | + key: rsaKeyPemEncrypted, | ||
| 293 | + passphrase: Buffer.from('password') | ||
| 294 | + }, bufferToEncrypt); | ||
| 295 | + | ||
| 296 | + assert.throws(() => { | ||
| 297 | + crypto.publicDecrypt({ | ||
| 298 | + key: rsaKeyPemEncrypted, | ||
| 299 | + passphrase: Buffer.from('wrong') | ||
| 300 | + }, encryptedBuffer); | ||
| 301 | + }, decryptError); | ||
| 302 | +} | ||
| 303 | + | ||
| 304 | +function test_rsa(padding, encryptOaepHash, decryptOaepHash) { | ||
| 305 | + const size = (padding === 'RSA_NO_PADDING') ? rsaKeySize / 8 : 32; | ||
| 306 | + const input = Buffer.allocUnsafe(size); | ||
| 307 | + for (let i = 0; i < input.length; i++) | ||
| 308 | + input[i] = (i * 7 + 11) & 0xff; | ||
| 309 | + const bufferToEncrypt = Buffer.from(input); | ||
| 310 | + | ||
| 311 | + padding = constants[padding]; | ||
| 312 | + | ||
| 313 | + const encryptedBuffer = crypto.publicEncrypt({ | ||
| 314 | + key: rsaPubPem, | ||
| 315 | + padding: padding, | ||
| 316 | + oaepHash: encryptOaepHash | ||
| 317 | + }, bufferToEncrypt); | ||
| 318 | + | ||
| 319 | + let decryptedBuffer = crypto.privateDecrypt({ | ||
| 320 | + key: rsaKeyPem, | ||
| 321 | + padding: padding, | ||
| 322 | + oaepHash: decryptOaepHash | ||
| 323 | + }, encryptedBuffer); | ||
| 324 | + assert.deepStrictEqual(decryptedBuffer, input); | ||
| 325 | + | ||
| 326 | + decryptedBuffer = crypto.privateDecrypt({ | ||
| 327 | + key: rsaPkcs8KeyPem, | ||
| 328 | + padding: padding, | ||
| 329 | + oaepHash: decryptOaepHash | ||
| 330 | + }, encryptedBuffer); | ||
| 331 | + assert.deepStrictEqual(decryptedBuffer, input); | ||
| 332 | +} | ||
| 333 | + | ||
| 334 | +test_rsa('RSA_NO_PADDING'); | ||
| 335 | +test_rsa('RSA_PKCS1_PADDING'); | ||
| 336 | +test_rsa('RSA_PKCS1_OAEP_PADDING'); | ||
| 337 | + | ||
| 338 | +// Test OAEP with different hash functions. | ||
| 339 | +test_rsa('RSA_PKCS1_OAEP_PADDING', undefined, 'sha1'); | ||
| 340 | +test_rsa('RSA_PKCS1_OAEP_PADDING', 'sha1', undefined); | ||
| 341 | +test_rsa('RSA_PKCS1_OAEP_PADDING', 'sha256', 'sha256'); | ||
| 342 | +test_rsa('RSA_PKCS1_OAEP_PADDING', 'sha512', 'sha512'); | ||
| 343 | +assert.throws(() => { | ||
| 344 | + test_rsa('RSA_PKCS1_OAEP_PADDING', 'sha256', 'sha512'); | ||
| 345 | +}, { | ||
| 346 | + code: 'ERR_OSSL_RSA_OAEP_DECODING_ERROR' | ||
| 347 | +}); | ||
| 348 | + | ||
| 349 | +// The following RSA-OAEP test cases were created using the WebCrypto API to | ||
| 350 | +// ensure compatibility when using non-SHA1 hash functions. | ||
| 351 | +{ | ||
| 352 | + const { decryptionTests } = | ||
| 353 | + JSON.parse(fixtures.readSync('rsa-oaep-test-vectors.js', 'utf8')); | ||
| 354 | + | ||
| 355 | + for (const { ct, oaepHash, oaepLabel } of decryptionTests) { | ||
| 356 | + const label = oaepLabel ? Buffer.from(oaepLabel, 'hex') : undefined; | ||
| 357 | + const copiedLabel = oaepLabel ? getBufferCopy(label) : undefined; | ||
| 358 | + | ||
| 359 | + const decrypted = crypto.privateDecrypt({ | ||
| 360 | + key: rsaPkcs8KeyPem, | ||
| 361 | + oaepHash, | ||
| 362 | + oaepLabel: oaepLabel ? label : undefined | ||
| 363 | + }, Buffer.from(ct, 'hex')); | ||
| 364 | + | ||
| 365 | + assert.strictEqual(decrypted.toString('utf8'), 'Hello Node.js'); | ||
| 366 | + | ||
| 367 | + const otherDecrypted = crypto.privateDecrypt({ | ||
| 368 | + key: rsaPkcs8KeyPem, | ||
| 369 | + oaepHash, | ||
| 370 | + oaepLabel: copiedLabel | ||
| 371 | + }, Buffer.from(ct, 'hex')); | ||
| 372 | + | ||
| 373 | + assert.strictEqual(otherDecrypted.toString('utf8'), 'Hello Node.js'); | ||
| 374 | + } | ||
| 375 | +} | ||
| 376 | + | ||
| 377 | +// Test invalid oaepHash and oaepLabel options. | ||
| 378 | +for (const fn of [crypto.publicEncrypt, crypto.privateDecrypt]) { | ||
| 379 | + assert.throws(() => { | ||
| 380 | + fn({ | ||
| 381 | + key: rsaPubPem, | ||
| 382 | + oaepHash: 'Hello world' | ||
| 383 | + }, Buffer.alloc(10)); | ||
| 384 | + }, { | ||
| 385 | + code: 'ERR_OSSL_EVP_INVALID_DIGEST' | ||
| 386 | + }); | ||
| 387 | + | ||
| 388 | + for (const oaepHash of [0, false, null, Symbol(), () => {}]) { | ||
| 389 | + assert.throws(() => { | ||
| 390 | + fn({ | ||
| 391 | + key: rsaPubPem, | ||
| 392 | + oaepHash | ||
| 393 | + }, Buffer.alloc(10)); | ||
| 394 | + }, { | ||
| 395 | + code: 'ERR_INVALID_ARG_TYPE' | ||
| 396 | + }); | ||
| 397 | + } | ||
| 398 | + | ||
| 399 | + for (const oaepLabel of [0, false, null, Symbol(), () => {}, {}]) { | ||
| 400 | + assert.throws(() => { | ||
| 401 | + fn({ | ||
| 402 | + key: rsaPubPem, | ||
| 403 | + oaepLabel | ||
| 404 | + }, Buffer.alloc(10)); | ||
| 405 | + }, { | ||
| 406 | + code: 'ERR_INVALID_ARG_TYPE' | ||
| 407 | + }); | ||
| 408 | + } | ||
| 409 | +} | ||
| 410 | + | ||
| 411 | +// Test RSA key signing/verification | ||
| 412 | +let rsaSign = crypto.createSign('SHA1'); | ||
| 413 | +let rsaVerify = crypto.createVerify('SHA1'); | ||
| 414 | +assert.ok(rsaSign); | ||
| 415 | +assert.ok(rsaVerify); | ||
| 416 | + | ||
| 417 | +const expectedSignature = fixtures.readKey( | ||
| 418 | + 'rsa_public_sha1_signature_signedby_rsa_private_pkcs8.sha1', | ||
| 419 | + 'hex' | ||
| 420 | +); | ||
| 421 | + | ||
| 422 | +rsaSign.update(rsaPubPem); | ||
| 423 | +let rsaSignature = rsaSign.sign(rsaKeyPem, 'hex'); | ||
| 424 | +assert.strictEqual(rsaSignature, expectedSignature); | ||
| 425 | + | ||
| 426 | +rsaVerify.update(rsaPubPem); | ||
| 427 | +assert.strictEqual(rsaVerify.verify(rsaPubPem, rsaSignature, 'hex'), true); | ||
| 428 | + | ||
| 429 | +// Test RSA PKCS#8 key signing/verification | ||
| 430 | +rsaSign = crypto.createSign('SHA1'); | ||
| 431 | +rsaSign.update(rsaPubPem); | ||
| 432 | +rsaSignature = rsaSign.sign(rsaPkcs8KeyPem, 'hex'); | ||
| 433 | +assert.strictEqual(rsaSignature, expectedSignature); | ||
| 434 | + | ||
| 435 | +rsaVerify = crypto.createVerify('SHA1'); | ||
| 436 | +rsaVerify.update(rsaPubPem); | ||
| 437 | +assert.strictEqual(rsaVerify.verify(rsaPubPem, rsaSignature, 'hex'), true); | ||
| 438 | + | ||
| 439 | +// Test RSA key signing/verification with encrypted key | ||
| 440 | +rsaSign = crypto.createSign('SHA1'); | ||
| 441 | +rsaSign.update(rsaPubPem); | ||
| 442 | +const signOptions = { key: rsaKeyPemEncrypted, passphrase: 'password' }; | ||
| 443 | +rsaSignature = rsaSign.sign(signOptions, 'hex'); | ||
| 444 | +assert.strictEqual(rsaSignature, expectedSignature); | ||
| 445 | + | ||
| 446 | +rsaVerify = crypto.createVerify('SHA1'); | ||
| 447 | +rsaVerify.update(rsaPubPem); | ||
| 448 | +assert.strictEqual(rsaVerify.verify(rsaPubPem, rsaSignature, 'hex'), true); | ||
| 449 | + | ||
| 450 | +rsaSign = crypto.createSign('SHA1'); | ||
| 451 | +rsaSign.update(rsaPubPem); | ||
| 452 | +assert.throws(() => { | ||
| 453 | + const signOptions = { key: rsaKeyPemEncrypted, passphrase: 'wrong' }; | ||
| 454 | + rsaSign.sign(signOptions, 'hex'); | ||
| 455 | +}, decryptPrivateKeyError); | ||
| 456 | + | ||
| 457 | +// | ||
| 458 | +// Test RSA signing and verification | ||
| 459 | +// | ||
| 460 | +{ | ||
| 461 | + const privateKey = fixtures.readKey('rsa_private_b.pem'); | ||
| 462 | + const publicKey = fixtures.readKey('rsa_public_b.pem'); | ||
| 463 | + | ||
| 464 | + const input = 'I AM THE WALRUS'; | ||
| 465 | + | ||
| 466 | + const signature = fixtures.readKey( | ||
| 467 | + 'I_AM_THE_WALRUS_sha256_signature_signedby_rsa_private_b.sha256', | ||
| 468 | + 'hex' | ||
| 469 | + ); | ||
| 470 | + | ||
| 471 | + const sign = crypto.createSign('SHA256'); | ||
| 472 | + sign.update(input); | ||
| 473 | + | ||
| 474 | + const output = sign.sign(privateKey, 'hex'); | ||
| 475 | + assert.strictEqual(output, signature); | ||
| 476 | + | ||
| 477 | + const verify = crypto.createVerify('SHA256'); | ||
| 478 | + verify.update(input); | ||
| 479 | + | ||
| 480 | + assert.strictEqual(verify.verify(publicKey, signature, 'hex'), true); | ||
| 481 | + | ||
| 482 | + // Test the legacy signature algorithm name. | ||
| 483 | + const sign2 = crypto.createSign('RSA-SHA256'); | ||
| 484 | + sign2.update(input); | ||
| 485 | + | ||
| 486 | + const output2 = sign2.sign(privateKey, 'hex'); | ||
| 487 | + assert.strictEqual(output2, signature); | ||
| 488 | + | ||
| 489 | + const verify2 = crypto.createVerify('SHA256'); | ||
| 490 | + verify2.update(input); | ||
| 491 | + | ||
| 492 | + assert.strictEqual(verify2.verify(publicKey, signature, 'hex'), true); | ||
| 493 | +} | ||
| 494 | + | ||
| 495 | + | ||
| 496 | +// | ||
| 497 | +// Test DSA signing and verification | ||
| 498 | +// | ||
| 499 | +{ | ||
| 500 | + const input = 'I AM THE WALRUS'; | ||
| 501 | + | ||
| 502 | + // DSA signatures vary across runs so there is no static string to verify | ||
| 503 | + // against. | ||
| 504 | + const sign = crypto.createSign('SHA1'); | ||
| 505 | + sign.update(input); | ||
| 506 | + const signature = sign.sign(dsaKeyPem, 'hex'); | ||
| 507 | + | ||
| 508 | + const verify = crypto.createVerify('SHA1'); | ||
| 509 | + verify.update(input); | ||
| 510 | + | ||
| 511 | + assert.strictEqual(verify.verify(dsaPubPem, signature, 'hex'), true); | ||
| 512 | + | ||
| 513 | + // Test the legacy 'DSS1' name. | ||
| 514 | + const sign2 = crypto.createSign('DSS1'); | ||
| 515 | + sign2.update(input); | ||
| 516 | + const signature2 = sign2.sign(dsaKeyPem, 'hex'); | ||
| 517 | + | ||
| 518 | + const verify2 = crypto.createVerify('DSS1'); | ||
| 519 | + verify2.update(input); | ||
| 520 | + | ||
| 521 | + assert.strictEqual(verify2.verify(dsaPubPem, signature2, 'hex'), true); | ||
| 522 | +} | ||
| 523 | + | ||
| 524 | + | ||
| 525 | +// | ||
| 526 | +// Test DSA signing and verification with PKCS#8 private key | ||
| 527 | +// | ||
| 528 | +{ | ||
| 529 | + const input = 'I AM THE WALRUS'; | ||
| 530 | + | ||
| 531 | + // DSA signatures vary across runs so there is no static string to verify | ||
| 532 | + // against. | ||
| 533 | + const sign = crypto.createSign('SHA1'); | ||
| 534 | + sign.update(input); | ||
| 535 | + const signature = sign.sign(dsaPkcs8KeyPem, 'hex'); | ||
| 536 | + | ||
| 537 | + const verify = crypto.createVerify('SHA1'); | ||
| 538 | + verify.update(input); | ||
| 539 | + | ||
| 540 | + assert.strictEqual(verify.verify(dsaPubPem, signature, 'hex'), true); | ||
| 541 | +} | ||
| 542 | + | ||
| 543 | + | ||
| 544 | +// | ||
| 545 | +// Test DSA signing and verification with encrypted key | ||
| 546 | +// | ||
| 547 | +const input = 'I AM THE WALRUS'; | ||
| 548 | + | ||
| 549 | +{ | ||
| 550 | + const sign = crypto.createSign('SHA1'); | ||
| 551 | + sign.update(input); | ||
| 552 | + assert.throws(() => { | ||
| 553 | + sign.sign({ key: dsaKeyPemEncrypted, passphrase: 'wrong' }, 'hex'); | ||
| 554 | + }, decryptPrivateKeyError); | ||
| 555 | +} | ||
| 556 | + | ||
| 557 | +{ | ||
| 558 | + // DSA signatures vary across runs so there is no static string to verify | ||
| 559 | + // against. | ||
| 560 | + const sign = crypto.createSign('SHA1'); | ||
| 561 | + sign.update(input); | ||
| 562 | + const signOptions = { key: dsaKeyPemEncrypted, passphrase: 'password' }; | ||
| 563 | + const signature = sign.sign(signOptions, 'hex'); | ||
| 564 | + | ||
| 565 | + const verify = crypto.createVerify('SHA1'); | ||
| 566 | + verify.update(input); | ||
| 567 | + | ||
| 568 | + assert.strictEqual(verify.verify(dsaPubPem, signature, 'hex'), true); | ||
| 569 | +} | ||
| 570 | diff --git a/test/parallel/test-crypto-rsa-dsa.js b/test/parallel/test-crypto-rsa-dsa.js | ||
| 571 | index 9afcb38..fd27827 100644 | ||
| 572 | --- a/test/parallel/test-crypto-rsa-dsa.js | ||
| 573 | +++ b/test/parallel/test-crypto-rsa-dsa.js | ||
| 574 | @@ -220,20 +220,36 @@ function test_rsa(padding, encryptOaepHash, decryptOaepHash) { | ||
| 575 | padding: padding, | ||
| 576 | oaepHash: encryptOaepHash | ||
| 577 | }, bufferToEncrypt); | ||
| 578 | + if (padding === constants.RSA_PKCS1_PADDING) { | ||
| 579 | + assert.throws(() => { | ||
| 580 | + crypto.privateDecrypt({ | ||
| 581 | + key: rsaKeyPem, | ||
| 582 | + padding: padding, | ||
| 583 | + oaepHash: decryptOaepHash | ||
| 584 | + }, encryptedBuffer); | ||
| 585 | + }, { code: 'ERR_INVALID_ARG_VALUE' }); | ||
| 586 | + assert.throws(() => { | ||
| 587 | + crypto.privateDecrypt({ | ||
| 588 | + key: rsaPkcs8KeyPem, | ||
| 589 | + padding: padding, | ||
| 590 | + oaepHash: decryptOaepHash | ||
| 591 | + }, encryptedBuffer); | ||
| 592 | + }, { code: 'ERR_INVALID_ARG_VALUE' }); | ||
| 593 | + } else { | ||
| 594 | + let decryptedBuffer = crypto.privateDecrypt({ | ||
| 595 | + key: rsaKeyPem, | ||
| 596 | + padding: padding, | ||
| 597 | + oaepHash: decryptOaepHash | ||
| 598 | + }, encryptedBuffer); | ||
| 599 | + assert.deepStrictEqual(decryptedBuffer, input); | ||
| 600 | |||
| 601 | - let decryptedBuffer = crypto.privateDecrypt({ | ||
| 602 | - key: rsaKeyPem, | ||
| 603 | - padding: padding, | ||
| 604 | - oaepHash: decryptOaepHash | ||
| 605 | - }, encryptedBuffer); | ||
| 606 | - assert.deepStrictEqual(decryptedBuffer, input); | ||
| 607 | - | ||
| 608 | - decryptedBuffer = crypto.privateDecrypt({ | ||
| 609 | - key: rsaPkcs8KeyPem, | ||
| 610 | - padding: padding, | ||
| 611 | - oaepHash: decryptOaepHash | ||
| 612 | - }, encryptedBuffer); | ||
| 613 | - assert.deepStrictEqual(decryptedBuffer, input); | ||
| 614 | + decryptedBuffer = crypto.privateDecrypt({ | ||
| 615 | + key: rsaPkcs8KeyPem, | ||
| 616 | + padding: padding, | ||
| 617 | + oaepHash: decryptOaepHash | ||
| 618 | + }, encryptedBuffer); | ||
| 619 | + assert.deepStrictEqual(decryptedBuffer, input); | ||
| 620 | + } | ||
| 621 | } | ||
| 622 | |||
| 623 | test_rsa('RSA_NO_PADDING'); | ||
| 624 | -- | ||
| 625 | 2.40.0 | ||
diff --git a/meta-oe/recipes-devtools/nodejs/nodejs_16.20.2.bb b/meta-oe/recipes-devtools/nodejs/nodejs_16.20.2.bb index 79e37214d4..95b36c926d 100644 --- a/meta-oe/recipes-devtools/nodejs/nodejs_16.20.2.bb +++ b/meta-oe/recipes-devtools/nodejs/nodejs_16.20.2.bb | |||
| @@ -29,6 +29,7 @@ SRC_URI = "http://nodejs.org/dist/v${PV}/node-v${PV}.tar.xz \ | |||
| 29 | file://CVE-2022-25883.patch \ | 29 | file://CVE-2022-25883.patch \ |
| 30 | file://CVE-2024-22019.patch \ | 30 | file://CVE-2024-22019.patch \ |
| 31 | file://CVE-2024-22025.patch \ | 31 | file://CVE-2024-22025.patch \ |
| 32 | file://CVE-2023-46809.patch \ | ||
| 32 | " | 33 | " |
| 33 | SRC_URI:append:class-target = " \ | 34 | SRC_URI:append:class-target = " \ |
| 34 | file://0001-Using-native-binaries.patch \ | 35 | file://0001-Using-native-binaries.patch \ |
