| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -5712,6 +5712,9 @@ Throws an error if FIPS mode is not available. | |||
| 5712 | 5712 | <!-- YAML | |
| 5713 | 5713 | added: v12.0.0 | |
| 5714 | 5714 | changes: | |
| 5715 | + - version: REPLACEME | ||
| 5716 | + pr-url: https://github.com/nodejs/node/pull/59570 | ||
| 5717 | + description: Add support for ML-DSA, Ed448, and SLH-DSA context parameter. | ||
| 5715 | 5718 | - version: REPLACEME | |
| 5716 | 5719 | pr-url: https://github.com/nodejs/node/pull/59537 | |
| 5717 | 5720 | description: Add support for SLH-DSA signing. | |
@@ -5772,6 +5775,9 @@ additional properties can be passed: | |||
| 5772 | 5775 | `crypto.constants.RSA_PSS_SALTLEN_DIGEST` sets the salt length to the digest | |
| 5773 | 5776 | size, `crypto.constants.RSA_PSS_SALTLEN_MAX_SIGN` (default) sets it to the | |
| 5774 | 5777 | maximum permissible value. | |
| 5778 | + * `context` {ArrayBuffer|Buffer|TypedArray|DataView} For Ed448, ML-DSA, and SLH-DSA, | ||
| 5779 | + this option specifies the optional context to differentiate signatures generated | ||
| 5780 | + for different purposes with the same key. | ||
| 5775 | 5781 | ||
| 5776 | 5782 | If the `callback` function is provided this function uses libuv's threadpool. | |
| 5777 | 5783 | ||
@@ -5831,6 +5837,9 @@ not introduce timing vulnerabilities. | |||
| 5831 | 5837 | <!-- YAML | |
| 5832 | 5838 | added: v12.0.0 | |
| 5833 | 5839 | changes: | |
| 5840 | + - version: REPLACEME | ||
| 5841 | + pr-url: https://github.com/nodejs/node/pull/59570 | ||
| 5842 | + description: Add support for ML-DSA, Ed448, and SLH-DSA context parameter. | ||
| 5834 | 5843 | - version: REPLACEME | |
| 5835 | 5844 | pr-url: https://github.com/nodejs/node/pull/59537 | |
| 5836 | 5845 | description: Add support for SLH-DSA signature verification. | |
@@ -5897,6 +5906,9 @@ additional properties can be passed: | |||
| 5897 | 5906 | `crypto.constants.RSA_PSS_SALTLEN_DIGEST` sets the salt length to the digest | |
| 5898 | 5907 | size, `crypto.constants.RSA_PSS_SALTLEN_MAX_SIGN` (default) sets it to the | |
| 5899 | 5908 | maximum permissible value. | |
| 5909 | + * `context` {ArrayBuffer|Buffer|TypedArray|DataView} For Ed448, ML-DSA, and SLH-DSA, | ||
| 5910 | + this option specifies the optional context to differentiate signatures generated | ||
| 5911 | + for different purposes with the same key. | ||
| 5900 | 5912 | ||
| 5901 | 5913 | The `signature` argument is the previously calculated signature for the `data`. | |
| 5902 | 5914 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -101,6 +101,19 @@ function getDSASignatureEncoding(options) { | |||
| 101 | 101 | return kSigEncDER; | |
| 102 | 102 | } | |
| 103 | 103 | ||
| 104 | + function getContext(options) { | ||
| 105 | + if (options?.context === undefined) { | ||
| 106 | + return undefined; | ||
| 107 | + } | ||
| 108 | + | ||
| 109 | + if (!isArrayBufferView(options.context)) { | ||
| 110 | + throw new ERR_INVALID_ARG_TYPE( | ||
| 111 | + 'options.context', ['Buffer', 'TypedArray', 'DataView'], options.context); | ||
| 112 | + } | ||
| 113 | + | ||
| 114 | + return options.context; | ||
| 115 | + } | ||
| 116 | + | ||
| 104 | 117 | function getIntOption(name, options) { | |
| 105 | 118 | const value = options[name]; | |
| 106 | 119 | if (value !== undefined) { | |
@@ -153,6 +166,9 @@ function signOneShot(algorithm, data, key, callback) { | |||
| 153 | 166 | // Options specific to (EC)DSA | |
| 154 | 167 | const dsaSigEnc = getDSASignatureEncoding(key); | |
| 155 | 168 | ||
| 169 | + // Options specific to Ed448 and ML-DSA | ||
| 170 | + const context = getContext(key); | ||
| 171 | + | ||
| 156 | 172 | const { | |
| 157 | 173 | data: keyData, | |
| 158 | 174 | format: keyFormat, | |
@@ -172,7 +188,7 @@ function signOneShot(algorithm, data, key, callback) { | |||
| 172 | 188 | pssSaltLength, | |
| 173 | 189 | rsaPadding, | |
| 174 | 190 | dsaSigEnc, | |
| 175 | - undefined, | ||
| 191 | + context, | ||
| 176 | 192 | undefined); | |
| 177 | 193 | ||
| 178 | 194 | if (!callback) { | |
@@ -251,6 +267,9 @@ function verifyOneShot(algorithm, data, key, signature, callback) { | |||
| 251 | 267 | // Options specific to (EC)DSA | |
| 252 | 268 | const dsaSigEnc = getDSASignatureEncoding(key); | |
| 253 | 269 | ||
| 270 | + // Options specific to Ed448 and ML-DSA | ||
| 271 | + const context = getContext(key); | ||
| 272 | + | ||
| 254 | 273 | if (!isArrayBufferView(signature)) { | |
| 255 | 274 | throw new ERR_INVALID_ARG_TYPE( | |
| 256 | 275 | 'signature', | |
@@ -278,7 +297,7 @@ function verifyOneShot(algorithm, data, key, signature, callback) { | |||
| 278 | 297 | pssSaltLength, | |
| 279 | 298 | rsaPadding, | |
| 280 | 299 | dsaSigEnc, | |
| 281 | - undefined, | ||
| 300 | + context, | ||
| 282 | 301 | signature); | |
| 283 | 302 | ||
| 284 | 303 | if (!callback) { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -246,6 +246,18 @@ bool SupportsContextString(const EVPKeyPointer& key) { | |||
| 246 | 246 | case EVP_PKEY_ML_DSA_44: | |
| 247 | 247 | case EVP_PKEY_ML_DSA_65: | |
| 248 | 248 | case EVP_PKEY_ML_DSA_87: | |
| 249 | + case EVP_PKEY_SLH_DSA_SHA2_128F: | ||
| 250 | + case EVP_PKEY_SLH_DSA_SHA2_128S: | ||
| 251 | + case EVP_PKEY_SLH_DSA_SHA2_192F: | ||
| 252 | + case EVP_PKEY_SLH_DSA_SHA2_192S: | ||
| 253 | + case EVP_PKEY_SLH_DSA_SHA2_256F: | ||
| 254 | + case EVP_PKEY_SLH_DSA_SHA2_256S: | ||
| 255 | + case EVP_PKEY_SLH_DSA_SHAKE_128F: | ||
| 256 | + case EVP_PKEY_SLH_DSA_SHAKE_128S: | ||
| 257 | + case EVP_PKEY_SLH_DSA_SHAKE_192F: | ||
| 258 | + case EVP_PKEY_SLH_DSA_SHAKE_192S: | ||
| 259 | + case EVP_PKEY_SLH_DSA_SHAKE_256F: | ||
| 260 | + case EVP_PKEY_SLH_DSA_SHAKE_256S: | ||
| 249 | 261 | #endif | |
| 250 | 262 | return true; | |
| 251 | 263 | default: | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -9,7 +9,7 @@ const exec = require('child_process').exec; | |||
| 9 | 9 | const crypto = require('crypto'); | |
| 10 | 10 | const fixtures = require('../common/fixtures'); | |
| 11 | 11 | const { | |
| 12 | - hasOpenSSL3, | ||
| 12 | + hasOpenSSL, | ||
| 13 | 13 | opensslCli, | |
| 14 | 14 | } = require('../common/crypto'); | |
| 15 | 15 | ||
@@ -66,7 +66,7 @@ const keySize = 2048; | |||
| 66 | 66 | key: keyPem, | |
| 67 | 67 | padding: crypto.constants.RSA_PKCS1_OAEP_PADDING | |
| 68 | 68 | }); | |
| 69 | - }, { message: hasOpenSSL3 ? | ||
| 69 | + }, { message: hasOpenSSL(3) ? | ||
| 70 | 70 | 'error:1C8000A5:Provider routines::illegal or unsupported padding mode' : | |
| 71 | 71 | 'bye, bye, error stack' }); | |
| 72 | 72 | ||
@@ -344,7 +344,7 @@ assert.throws( | |||
| 344 | 344 | key: keyPem, | |
| 345 | 345 | padding: crypto.constants.RSA_PKCS1_OAEP_PADDING | |
| 346 | 346 | }); | |
| 347 | - }, hasOpenSSL3 ? { | ||
| 347 | + }, hasOpenSSL(3) ? { | ||
| 348 | 348 | code: 'ERR_OSSL_ILLEGAL_OR_UNSUPPORTED_PADDING_MODE', | |
| 349 | 349 | message: /illegal or unsupported padding mode/, | |
| 350 | 350 | } : { | |
@@ -426,6 +426,7 @@ assert.throws( | |||
| 426 | 426 | { private: fixtures.readKey('ed448_private.pem', 'ascii'), | |
| 427 | 427 | public: fixtures.readKey('ed448_public.pem', 'ascii'), | |
| 428 | 428 | algo: null, | |
| 429 | + supportsContext: true, | ||
| 429 | 430 | sigLen: 114 }, | |
| 430 | 431 | { private: fixtures.readKey('rsa_private_2048.pem', 'ascii'), | |
| 431 | 432 | public: fixtures.readKey('rsa_public_2048.pem', 'ascii'), | |
@@ -473,6 +474,55 @@ assert.throws( | |||
| 473 | 474 | assert.strictEqual(crypto.verify(algo, data, pair.private, sig), | |
| 474 | 475 | true); | |
| 475 | 476 | }); | |
| 477 | + | ||
| 478 | + if (pair.supportsContext && hasOpenSSL(3, 2)) { | ||
| 479 | + const data = Buffer.from('Hello world'); | ||
| 480 | + { | ||
| 481 | + const context = new Uint8Array(); | ||
| 482 | + const sig = crypto.sign(algo, data, { key: pair.private, context }); | ||
| 483 | + assert.strictEqual(crypto.verify(algo, data, { key: pair.public }, sig), true); | ||
| 484 | + assert.strictEqual(crypto.verify(algo, data, { key: pair.public, context }, sig), true); | ||
| 485 | + assert.strictEqual(crypto.verify(algo, data, { key: pair.public, context: crypto.randomBytes(30) }, sig), false); | ||
| 486 | + } | ||
| 487 | + | ||
| 488 | + { | ||
| 489 | + const context = new Uint8Array(32); | ||
| 490 | + const sig = crypto.sign(algo, data, { key: pair.private, context }); | ||
| 491 | + assert.strictEqual(crypto.verify(algo, data, { key: pair.public }, sig), false); | ||
| 492 | + assert.strictEqual(crypto.verify(algo, data, { key: pair.public, context }, sig), true); | ||
| 493 | + assert.strictEqual(crypto.verify(algo, data, { key: pair.public, context: crypto.randomBytes(30) }, sig), false); | ||
| 494 | + } | ||
| 495 | + | ||
| 496 | + assert.throws(() => crypto.sign(algo, data, { key: pair.private, context: new Uint8Array(256) }), { | ||
| 497 | + code: 'ERR_OUT_OF_RANGE', | ||
| 498 | + message: 'context string must be at most 255 bytes', | ||
| 499 | + }); | ||
| 500 | + | ||
| 501 | + assert.throws(() => { | ||
| 502 | + crypto.verify(algo, data, { key: pair.public, context: new Uint8Array(256) }, new Uint8Array()); | ||
| 503 | + }, { | ||
| 504 | + code: 'ERR_OUT_OF_RANGE', | ||
| 505 | + message: 'context string must be at most 255 bytes', | ||
| 506 | + }); | ||
| 507 | + } else if (pair.supportsContext) { | ||
| 508 | + const data = Buffer.from('Hello world'); | ||
| 509 | + { | ||
| 510 | + const context = new Uint8Array(); | ||
| 511 | + const sig = crypto.sign(algo, data, { key: pair.private, context }); | ||
| 512 | + assert.strictEqual(crypto.verify(algo, data, { key: pair.public }, sig), true); | ||
| 513 | + assert.strictEqual(crypto.verify(algo, data, { key: pair.public, context }, sig), true); | ||
| 514 | + } | ||
| 515 | + | ||
| 516 | + { | ||
| 517 | + const context = new Uint8Array(32); | ||
| 518 | + assert.throws(() => { | ||
| 519 | + crypto.sign(algo, data, { key: pair.private, context }); | ||
| 520 | + }, { message: 'Context parameter is unsupported' }); | ||
| 521 | + assert.throws(() => { | ||
| 522 | + crypto.verify(algo, data, { key: pair.public, context: crypto.randomBytes(30) }, crypto.randomBytes(32)); | ||
| 523 | + }, { message: 'Context parameter is unsupported' }); | ||
| 524 | + } | ||
| 525 | + } | ||
| 476 | 526 | }); | |
| 477 | 527 | ||
| 478 | 528 | [1, {}, [], true, Infinity].forEach((input) => { | |
| Back | FazBrowse Home | New Git URL |
0 commit comments