Skip to content

Commit 191ebfb

Browse files
koreahghgaduh95
authored andcommitted
crypto: fix public PKCS8 export error
exportKeySpki() already rejects exporting a private key as 'spki' with an InvalidAccessError, per the Web Crypto export key algorithm steps for each of RSA, EC, CFRG, ML-DSA, and ML-KEM. exportKeyPkcs8() was missing the symmetric check: exporting a public key as 'pkcs8' fell through to the generic "Unable to export ... key using pkcs8 format" NotSupportedError instead of the spec-mandated InvalidAccessError. Add the same key-type check to exportKeyPkcs8(), mirroring exportKeySpki(), and drop the now-redundant type guard around its call site in exportKeySync(). This also fixes wrapKey(), which delegates to the same export path. Add test coverage for both subtle.exportKey('pkcs8', publicKey) and subtle.wrapKey('pkcs8', publicKey, ...). Signed-off-by: koreahghg <koreahghg@gmail.com> PR-URL: #65609 Reviewed-By: Filip Skokan <panva.ip@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent 10b1a0f commit 191ebfb

3 files changed

Lines changed: 34 additions & 13 deletions

File tree

lib/internal/crypto/webcrypto.js

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -483,45 +483,51 @@ function exportKeySpki(key) {
483483
}
484484

485485
function exportKeyPkcs8(key) {
486+
let exporter;
486487
switch (getCryptoKeyAlgorithm(key).name) {
487488
case 'RSASSA-PKCS1-v1_5':
488489
// Fall through
489490
case 'RSA-PSS':
490491
// Fall through
491492
case 'RSA-OAEP':
492-
return require('internal/crypto/rsa')
493-
.rsaExportKey(key, kWebCryptoKeyFormatPKCS8);
493+
exporter = require('internal/crypto/rsa').rsaExportKey;
494+
break;
494495
case 'ECDSA':
495496
// Fall through
496497
case 'ECDH':
497-
return require('internal/crypto/ec')
498-
.ecExportKey(key, kWebCryptoKeyFormatPKCS8);
498+
exporter = require('internal/crypto/ec').ecExportKey;
499+
break;
499500
case 'Ed25519':
500501
// Fall through
501502
case 'Ed448':
502503
// Fall through
503504
case 'X25519':
504505
// Fall through
505506
case 'X448':
506-
return require('internal/crypto/cfrg')
507-
.cfrgExportKey(key, kWebCryptoKeyFormatPKCS8);
507+
exporter = require('internal/crypto/cfrg').cfrgExportKey;
508+
break;
508509
case 'ML-DSA-44':
509510
// Fall through
510511
case 'ML-DSA-65':
511512
// Fall through
512513
case 'ML-DSA-87':
513-
return require('internal/crypto/ml_dsa')
514-
.mlDsaExportKey(key, kWebCryptoKeyFormatPKCS8);
514+
exporter = require('internal/crypto/ml_dsa').mlDsaExportKey;
515+
break;
515516
case 'ML-KEM-512':
516517
// Fall through
517518
case 'ML-KEM-768':
518519
// Fall through
519520
case 'ML-KEM-1024':
520-
return require('internal/crypto/ml_kem')
521-
.mlKemExportKey(key, kWebCryptoKeyFormatPKCS8);
521+
exporter = require('internal/crypto/ml_kem').mlKemExportKey;
522+
break;
522523
default:
523524
return undefined;
524525
}
526+
527+
if (getCryptoKeyType(key) !== 'private')
528+
throw lazyDOMException('Key must be a private key', 'InvalidAccessError');
529+
530+
return exporter(key, kWebCryptoKeyFormatPKCS8);
525531
}
526532

527533
function exportKeyRawPublic(key, format) {
@@ -730,9 +736,7 @@ function exportKeySync(format, key) {
730736
break;
731737
}
732738
case 'pkcs8': {
733-
if (type === 'private') {
734-
result = exportKeyPkcs8(key);
735-
}
739+
result = exportKeyPkcs8(key);
736740
break;
737741
}
738742
case 'jwk': {

test/parallel/test-webcrypto-export-import-ec.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,12 @@ async function testImportSpki({ name, publicUsages }, namedCurve, extractable) {
120120
assert.strictEqual(
121121
Buffer.from(spki).toString('hex'),
122122
keyData[namedCurve].spki.toString('hex'));
123+
124+
await assert.rejects(
125+
subtle.exportKey('pkcs8', key), {
126+
message: 'Key must be a private key',
127+
name: 'InvalidAccessError',
128+
});
123129
} else {
124130
await assert.rejects(
125131
subtle.exportKey('spki', key), {

test/parallel/test-webcrypto-wrap-unwrap.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,17 @@ async function testNonByteLengthWrapUnwrap({
516516
name: 'InvalidAccessError',
517517
});
518518

519+
// Symmetric case: exporting a public key as 'pkcs8' must also fail with
520+
// InvalidAccessError, not the generic NotSupportedError.
521+
await assert.rejects(
522+
subtle.wrapKey('pkcs8', ecKey.publicKey, wrapKey, {
523+
name: 'AES-GCM',
524+
iv: new Uint8Array(12),
525+
}), {
526+
message: 'Key must be a private key',
527+
name: 'InvalidAccessError',
528+
});
529+
519530
// --- unwrapKey validation tests ---
520531

521532
const ciphertext = new Uint8Array(32); // Dummy ciphertext

0 commit comments

Comments
 (0)