Skip to content

Commit e7a6370

Browse files
authored
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 045ff95 commit e7a6370

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
@@ -530,45 +530,51 @@ function exportKeySpki(key) {
530530
}
531531

532532
function exportKeyPkcs8(key) {
533+
let exporter;
533534
switch (getCryptoKeyAlgorithm(key).name) {
534535
case 'RSASSA-PKCS1-v1_5':
535536
// Fall through
536537
case 'RSA-PSS':
537538
// Fall through
538539
case 'RSA-OAEP':
539-
return require('internal/crypto/rsa')
540-
.rsaExportKey(key, kWebCryptoKeyFormatPKCS8);
540+
exporter = require('internal/crypto/rsa').rsaExportKey;
541+
break;
541542
case 'ECDSA':
542543
// Fall through
543544
case 'ECDH':
544-
return require('internal/crypto/ec')
545-
.ecExportKey(key, kWebCryptoKeyFormatPKCS8);
545+
exporter = require('internal/crypto/ec').ecExportKey;
546+
break;
546547
case 'Ed25519':
547548
// Fall through
548549
case 'Ed448':
549550
// Fall through
550551
case 'X25519':
551552
// Fall through
552553
case 'X448':
553-
return require('internal/crypto/cfrg')
554-
.cfrgExportKey(key, kWebCryptoKeyFormatPKCS8);
554+
exporter = require('internal/crypto/cfrg').cfrgExportKey;
555+
break;
555556
case 'ML-DSA-44':
556557
// Fall through
557558
case 'ML-DSA-65':
558559
// Fall through
559560
case 'ML-DSA-87':
560-
return require('internal/crypto/ml_dsa')
561-
.mlDsaExportKey(key, kWebCryptoKeyFormatPKCS8);
561+
exporter = require('internal/crypto/ml_dsa').mlDsaExportKey;
562+
break;
562563
case 'ML-KEM-512':
563564
// Fall through
564565
case 'ML-KEM-768':
565566
// Fall through
566567
case 'ML-KEM-1024':
567-
return require('internal/crypto/ml_kem')
568-
.mlKemExportKey(key, kWebCryptoKeyFormatPKCS8);
568+
exporter = require('internal/crypto/ml_kem').mlKemExportKey;
569+
break;
569570
default:
570571
return undefined;
571572
}
573+
574+
if (getCryptoKeyType(key) !== 'private')
575+
throw lazyDOMException('Key must be a private key', 'InvalidAccessError');
576+
577+
return exporter(key, kWebCryptoKeyFormatPKCS8);
572578
}
573579

574580
function exportKeyRawPublic(key, format) {
@@ -777,9 +783,7 @@ function exportKeySync(format, key) {
777783
break;
778784
}
779785
case 'pkcs8': {
780-
if (type === 'private') {
781-
result = exportKeyPkcs8(key);
782-
}
786+
result = exportKeyPkcs8(key);
783787
break;
784788
}
785789
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
@@ -567,6 +567,17 @@ async function testNonByteLengthWrapUnwrap({
567567
name: 'InvalidAccessError',
568568
});
569569

570+
// Symmetric case: exporting a public key as 'pkcs8' must also fail with
571+
// InvalidAccessError, not the generic NotSupportedError.
572+
await assert.rejects(
573+
subtle.wrapKey('pkcs8', ecKey.publicKey, wrapKey, {
574+
name: 'AES-GCM',
575+
iv: new Uint8Array(12),
576+
}), {
577+
message: 'Key must be a private key',
578+
name: 'InvalidAccessError',
579+
});
580+
570581
// --- unwrapKey validation tests ---
571582

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

0 commit comments

Comments
 (0)