| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent e254f65 commit f5725ca
5 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1260,6 +1260,10 @@ The {CryptoKey} (secret key) generating algorithms supported include: | |||
| 1260 | 1260 | <!-- YAML | |
| 1261 | 1261 | added: v15.0.0 | |
| 1262 | 1262 | changes: | |
| 1263 | + - version: REPLACEME | ||
| 1264 | + pr-url: https://github.com/nodejs/node/pull/62218 | ||
| 1265 | + description: Importing ML-DSA and ML-KEM PKCS#8 keys | ||
| 1266 | + without a seed is no longer supported. | ||
| 1263 | 1267 | - version: v24.8.0 | |
| 1264 | 1268 | pr-url: https://github.com/nodejs/node/pull/59647 | |
| 1265 | 1269 | description: KMAC algorithms are now supported. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -192,6 +192,19 @@ function mlDsaImportKey( | |||
| 192 | 192 | } | |
| 193 | 193 | case 'pkcs8': { | |
| 194 | 194 | verifyAcceptableMlDsaKeyUse(name, false, usagesSet); | |
| 195 | + | ||
| 196 | + const privOnlyLengths = { | ||
| 197 | + '__proto__': null, | ||
| 198 | + 'ML-DSA-44': 2588, | ||
| 199 | + 'ML-DSA-65': 4060, | ||
| 200 | + 'ML-DSA-87': 4924, | ||
| 201 | + }; | ||
| 202 | + if (keyData.byteLength === privOnlyLengths[name]) { | ||
| 203 | + throw lazyDOMException( | ||
| 204 | + 'Importing an ML-DSA PKCS#8 key without a seed is not supported', | ||
| 205 | + 'NotSupportedError'); | ||
| 206 | + } | ||
| 207 | + | ||
| 195 | 208 | try { | |
| 196 | 209 | keyObject = createPrivateKey({ | |
| 197 | 210 | key: keyData, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -182,6 +182,19 @@ function mlKemImportKey( | |||
| 182 | 182 | } | |
| 183 | 183 | case 'pkcs8': { | |
| 184 | 184 | verifyAcceptableMlKemKeyUse(name, false, usagesSet); | |
| 185 | + | ||
| 186 | + const privOnlyLengths = { | ||
| 187 | + '__proto__': null, | ||
| 188 | + 'ML-KEM-512': 1660, | ||
| 189 | + 'ML-KEM-768': 2428, | ||
| 190 | + 'ML-KEM-1024': 3196, | ||
| 191 | + }; | ||
| 192 | + if (keyData.byteLength === privOnlyLengths[name]) { | ||
| 193 | + throw lazyDOMException( | ||
| 194 | + 'Importing an ML-KEM PKCS#8 key without a seed is not supported', | ||
| 195 | + 'NotSupportedError'); | ||
| 196 | + } | ||
| 197 | + | ||
| 185 | 198 | try { | |
| 186 | 199 | keyObject = createPrivateKey({ | |
| 187 | 200 | key: keyData, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -12,6 +12,7 @@ if (!hasOpenSSL(3, 5)) | |||
| 12 | 12 | ||
| 13 | 13 | const assert = require('assert'); | |
| 14 | 14 | const { subtle } = globalThis.crypto; | |
| 15 | + const { createPrivateKey } = require('crypto'); | ||
| 15 | 16 | ||
| 16 | 17 | const fixtures = require('../common/fixtures'); | |
| 17 | 18 | ||
@@ -196,41 +197,32 @@ async function testImportPkcs8SeedOnly({ name, privateUsages }, extractable) { | |||
| 196 | 197 | } | |
| 197 | 198 | ||
| 198 | 199 | async function testImportPkcs8PrivOnly({ name, privateUsages }, extractable) { | |
| 199 | - const key = await subtle.importKey( | ||
| 200 | - 'pkcs8', | ||
| 201 | - keyData[name].pkcs8_priv_only, | ||
| 202 | - { name }, | ||
| 203 | - extractable, | ||
| 204 | - privateUsages); | ||
| 205 | - assert.strictEqual(key.type, 'private'); | ||
| 206 | - assert.strictEqual(key.extractable, extractable); | ||
| 207 | - assert.deepStrictEqual(key.usages, privateUsages); | ||
| 208 | - assert.deepStrictEqual(key.algorithm.name, name); | ||
| 209 | - assert.strictEqual(key.algorithm, key.algorithm); | ||
| 210 | - assert.strictEqual(key.usages, key.usages); | ||
| 211 | - | ||
| 212 | - if (extractable) { | ||
| 213 | - await assert.rejects(subtle.exportKey('pkcs8', key), (err) => { | ||
| 214 | - assert.strictEqual(err.name, 'OperationError'); | ||
| 215 | - assert.strictEqual(err.cause.code, 'ERR_CRYPTO_OPERATION_FAILED'); | ||
| 216 | - assert.strictEqual(err.cause.message, 'Failed to get raw seed'); | ||
| 217 | - return true; | ||
| 200 | + await assert.rejects( | ||
| 201 | + subtle.importKey( | ||
| 202 | + 'pkcs8', | ||
| 203 | + keyData[name].pkcs8_priv_only, | ||
| 204 | + { name }, | ||
| 205 | + extractable, | ||
| 206 | + privateUsages), | ||
| 207 | + { | ||
| 208 | + name: 'NotSupportedError', | ||
| 209 | + message: 'Importing an ML-DSA PKCS#8 key without a seed is not supported', | ||
| 218 | 210 | }); | |
| 219 | - } else { | ||
| 220 | - await assert.rejects( | ||
| 221 | - subtle.exportKey('pkcs8', key), { | ||
| 222 | - message: /key is not extractable/ | ||
| 223 | - }); | ||
| 224 | - } | ||
| 211 | + } | ||
| 225 | 212 | ||
| 213 | + async function testImportPkcs8MismatchedSeed({ name, privateUsages }, extractable) { | ||
| 214 | + const modified = Buffer.from(keyData[name].pkcs8); | ||
| 215 | + modified[30] ^= 0xff; | ||
| 226 | 216 | await assert.rejects( | |
| 227 | 217 | subtle.importKey( | |
| 228 | 218 | 'pkcs8', | |
| 229 | - keyData[name].pkcs8_seed_only, | ||
| 219 | + modified, | ||
| 230 | 220 | { name }, | |
| 231 | 221 | extractable, | |
| 232 | - [/* empty usages */]), | ||
| 233 | - { name: 'SyntaxError', message: 'Usages cannot be empty when importing a private key.' }); | ||
| 222 | + privateUsages), | ||
| 223 | + { | ||
| 224 | + name: 'DataError', | ||
| 225 | + }); | ||
| 234 | 226 | } | |
| 235 | 227 | ||
| 236 | 228 | async function testImportJwk({ name, publicUsages, privateUsages }, extractable) { | |
@@ -493,6 +485,7 @@ async function testImportRawSeed({ name, privateUsages }, extractable) { | |||
| 493 | 485 | tests.push(testImportPkcs8(vector, extractable)); | |
| 494 | 486 | tests.push(testImportPkcs8SeedOnly(vector, extractable)); | |
| 495 | 487 | tests.push(testImportPkcs8PrivOnly(vector, extractable)); | |
| 488 | + tests.push(testImportPkcs8MismatchedSeed(vector, extractable)); | ||
| 496 | 489 | tests.push(testImportJwk(vector, extractable)); | |
| 497 | 490 | tests.push(testImportRawSeed(vector, extractable)); | |
| 498 | 491 | tests.push(testImportRawPublic(vector, extractable)); | |
@@ -509,3 +502,17 @@ async function testImportRawSeed({ name, privateUsages }, extractable) { | |||
| 509 | 502 | message: 'Unable to import ML-DSA-44 using raw format', | |
| 510 | 503 | }); | |
| 511 | 504 | })().then(common.mustCall()); | |
| 505 | + | ||
| 506 | + (async function() { | ||
| 507 | + for (const { name, privateUsages } of testVectors) { | ||
| 508 | + const pem = fixtures.readKey(getKeyFileName(name.toLowerCase(), 'private_priv_only'), 'ascii'); | ||
| 509 | + const keyObject = createPrivateKey(pem); | ||
| 510 | + const key = keyObject.toCryptoKey({ name }, true, privateUsages); | ||
| 511 | + await assert.rejects(subtle.exportKey('pkcs8', key), (err) => { | ||
| 512 | + assert.strictEqual(err.name, 'OperationError'); | ||
| 513 | + assert.strictEqual(err.cause.code, 'ERR_CRYPTO_OPERATION_FAILED'); | ||
| 514 | + assert.strictEqual(err.cause.message, 'Failed to get raw seed'); | ||
| 515 | + return true; | ||
| 516 | + }); | ||
| 517 | + } | ||
| 518 | + })().then(common.mustCall()); | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -12,6 +12,7 @@ if (!hasOpenSSL(3, 5)) | |||
| 12 | 12 | ||
| 13 | 13 | const assert = require('assert'); | |
| 14 | 14 | const { subtle } = globalThis.crypto; | |
| 15 | + const { createPrivateKey } = require('crypto'); | ||
| 15 | 16 | ||
| 16 | 17 | const fixtures = require('../common/fixtures'); | |
| 17 | 18 | ||
@@ -179,41 +180,32 @@ async function testImportPkcs8SeedOnly({ name, privateUsages }, extractable) { | |||
| 179 | 180 | } | |
| 180 | 181 | ||
| 181 | 182 | async function testImportPkcs8PrivOnly({ name, privateUsages }, extractable) { | |
| 182 | - const key = await subtle.importKey( | ||
| 183 | - 'pkcs8', | ||
| 184 | - keyData[name].pkcs8_priv_only, | ||
| 185 | - { name }, | ||
| 186 | - extractable, | ||
| 187 | - privateUsages); | ||
| 188 | - assert.strictEqual(key.type, 'private'); | ||
| 189 | - assert.strictEqual(key.extractable, extractable); | ||
| 190 | - assert.deepStrictEqual(key.usages, privateUsages); | ||
| 191 | - assert.deepStrictEqual(key.algorithm.name, name); | ||
| 192 | - assert.strictEqual(key.algorithm, key.algorithm); | ||
| 193 | - assert.strictEqual(key.usages, key.usages); | ||
| 194 | - | ||
| 195 | - if (extractable) { | ||
| 196 | - await assert.rejects(subtle.exportKey('pkcs8', key), (err) => { | ||
| 197 | - assert.strictEqual(err.name, 'OperationError'); | ||
| 198 | - assert.strictEqual(err.cause.code, 'ERR_CRYPTO_OPERATION_FAILED'); | ||
| 199 | - assert.strictEqual(err.cause.message, 'Failed to get raw seed'); | ||
| 200 | - return true; | ||
| 183 | + await assert.rejects( | ||
| 184 | + subtle.importKey( | ||
| 185 | + 'pkcs8', | ||
| 186 | + keyData[name].pkcs8_priv_only, | ||
| 187 | + { name }, | ||
| 188 | + extractable, | ||
| 189 | + privateUsages), | ||
| 190 | + { | ||
| 191 | + name: 'NotSupportedError', | ||
| 192 | + message: 'Importing an ML-KEM PKCS#8 key without a seed is not supported', | ||
| 201 | 193 | }); | |
| 202 | - } else { | ||
| 203 | - await assert.rejects( | ||
| 204 | - subtle.exportKey('pkcs8', key), { | ||
| 205 | - message: /key is not extractable/ | ||
| 206 | - }); | ||
| 207 | - } | ||
| 194 | + } | ||
| 208 | 195 | ||
| 196 | + async function testImportPkcs8MismatchedSeed({ name, privateUsages }, extractable) { | ||
| 197 | + const modified = Buffer.from(keyData[name].pkcs8); | ||
| 198 | + modified[30] ^= 0xff; | ||
| 209 | 199 | await assert.rejects( | |
| 210 | 200 | subtle.importKey( | |
| 211 | 201 | 'pkcs8', | |
| 212 | - keyData[name].pkcs8_seed_only, | ||
| 202 | + modified, | ||
| 213 | 203 | { name }, | |
| 214 | 204 | extractable, | |
| 215 | - [/* empty usages */]), | ||
| 216 | - { name: 'SyntaxError', message: 'Usages cannot be empty when importing a private key.' }); | ||
| 205 | + privateUsages), | ||
| 206 | + { | ||
| 207 | + name: 'DataError', | ||
| 208 | + }); | ||
| 217 | 209 | } | |
| 218 | 210 | ||
| 219 | 211 | async function testImportRawPublic({ name, publicUsages }, extractable) { | |
@@ -298,6 +290,7 @@ async function testImportRawSeed({ name, privateUsages }, extractable) { | |||
| 298 | 290 | tests.push(testImportPkcs8(vector, extractable)); | |
| 299 | 291 | tests.push(testImportPkcs8SeedOnly(vector, extractable)); | |
| 300 | 292 | tests.push(testImportPkcs8PrivOnly(vector, extractable)); | |
| 293 | + tests.push(testImportPkcs8MismatchedSeed(vector, extractable)); | ||
| 301 | 294 | tests.push(testImportRawSeed(vector, extractable)); | |
| 302 | 295 | tests.push(testImportRawPublic(vector, extractable)); | |
| 303 | 296 | } | |
@@ -313,3 +306,17 @@ async function testImportRawSeed({ name, privateUsages }, extractable) { | |||
| 313 | 306 | message: 'Unable to import ML-KEM-512 using raw format', | |
| 314 | 307 | }); | |
| 315 | 308 | })().then(common.mustCall()); | |
| 309 | + | ||
| 310 | + (async function() { | ||
| 311 | + for (const { name, privateUsages } of testVectors) { | ||
| 312 | + const pem = fixtures.readKey(getKeyFileName(name.toLowerCase(), 'private_priv_only'), 'ascii'); | ||
| 313 | + const keyObject = createPrivateKey(pem); | ||
| 314 | + const key = keyObject.toCryptoKey({ name }, true, privateUsages); | ||
| 315 | + await assert.rejects(subtle.exportKey('pkcs8', key), (err) => { | ||
| 316 | + assert.strictEqual(err.name, 'OperationError'); | ||
| 317 | + assert.strictEqual(err.cause.code, 'ERR_CRYPTO_OPERATION_FAILED'); | ||
| 318 | + assert.strictEqual(err.cause.message, 'Failed to get raw seed'); | ||
| 319 | + return true; | ||
| 320 | + }); | ||
| 321 | + } | ||
| 322 | + })().then(common.mustCall()); | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments