crypto: add subtle.getPublicKey() utility function in Web Cryptography · nodejs/node@6fcce90 · GitHub
Skip to content

Commit 6fcce90

Browse files
panvatargos
authored andcommitted
crypto: add subtle.getPublicKey() utility function in Web Cryptography
PR-URL: #59365 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Ethan Arrowood <ethan@arrowood.dev> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
1 parent 76cde76 commit 6fcce90

5 files changed

Lines changed: 176 additions & 31 deletions

File tree

doc/api/webcrypto.md

Lines changed: 47 additions & 31 deletions

lib/internal/crypto/webcrypto.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const {
88
ReflectApply,
99
ReflectConstruct,
1010
StringPrototypeRepeat,
11+
StringPrototypeSlice,
1112
SymbolToStringTag,
1213
} = primordials;
1314

@@ -29,6 +30,7 @@ const {
2930
} = require('internal/errors');
3031

3132
const {
33+
createPublicKey,
3234
CryptoKey,
3335
importGenericSecretKey,
3436
} = require('internal/crypto/keys');
@@ -1028,6 +1030,31 @@ async function decrypt(algorithm, key, data) {
10281030
return cipherOrWrap(kWebCryptoCipherDecrypt, algorithm, key, data, 'decrypt');
10291031
}
10301032

1033+
// Implements https://wicg.github.io/webcrypto-modern-algos/#SubtleCrypto-method-getPublicKey
1034+
async function getPublicKey(key, keyUsages) {
1035+
emitExperimentalWarning('The getPublicKey Web Crypto API method');
1036+
if (this !== subtle) throw new ERR_INVALID_THIS('SubtleCrypto');
1037+
1038+
webidl ??= require('internal/crypto/webidl');
1039+
const prefix = "Failed to execute 'getPublicKey' on 'SubtleCrypto'";
1040+
webidl.requiredArguments(arguments.length, 2, { prefix });
1041+
key = webidl.converters.CryptoKey(key, {
1042+
prefix,
1043+
context: '1st argument',
1044+
});
1045+
keyUsages = webidl.converters['sequence<KeyUsage>'](keyUsages, {
1046+
prefix,
1047+
context: '2nd argument',
1048+
});
1049+
1050+
if (key.type !== 'private')
1051+
throw lazyDOMException('key must be a private key', 'InvalidAccessError');
1052+
1053+
const keyObject = createPublicKey(key[kKeyObject]);
1054+
1055+
return keyObject.toCryptoKey(key.algorithm, true, keyUsages);
1056+
}
1057+
10311058
// The SubtleCrypto and Crypto classes are defined as part of the
10321059
// Web Crypto API standard: https://www.w3.org/TR/WebCryptoAPI/
10331060

@@ -1066,6 +1093,7 @@ class SubtleCrypto {
10661093
case 'exportKey':
10671094
case 'wrapKey':
10681095
case 'unwrapKey':
1096+
case 'getPublicKey':
10691097
break;
10701098
default:
10711099
return false;
@@ -1116,6 +1144,26 @@ class SubtleCrypto {
11161144
context: '3rd argument',
11171145
});
11181146
}
1147+
} else if (operation === 'getPublicKey') {
1148+
let normalizedAlgorithm;
1149+
try {
1150+
normalizedAlgorithm = normalizeAlgorithm(algorithm, 'exportKey');
1151+
} catch {
1152+
return false;
1153+
}
1154+
1155+
switch (StringPrototypeSlice(normalizedAlgorithm.name, 0, 2)) {
1156+
case 'ML': // ML-DSA-*, ML-KEM-*
1157+
case 'SL': // SLH-DSA-*
1158+
case 'RS': // RSA-OAEP, RSA-PSS, RSASSA-PKCS1-v1_5
1159+
case 'EC': // ECDSA, ECDH
1160+
case 'Ed': // Ed*
1161+
case 'X2': // X25519
1162+
case 'X4': // X448
1163+
return true;
1164+
default:
1165+
return false;
1166+
}
11191167
}
11201168

11211169
return check(operation, algorithm, length);
@@ -1319,6 +1367,13 @@ ObjectDefineProperties(
13191367
writable: true,
13201368
value: unwrapKey,
13211369
},
1370+
getPublicKey: {
1371+
__proto__: null,
1372+
enumerable: true,
1373+
configurable: true,
1374+
writable: true,
1375+
value: getPublicKey,
1376+
},
13221377
});
13231378

13241379
module.exports = {

test/fixtures/webcrypto/supports-modern-algorithms.mjs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,20 @@ export const vectors = {
2323
[pqc, 'ML-DSA-65'],
2424
[pqc, 'ML-DSA-87'],
2525
],
26+
'getPublicKey': [
27+
[true, 'RSA-OAEP'],
28+
[true, 'RSA-PSS'],
29+
[true, 'RSASSA-PKCS1-v1_5'],
30+
[true, 'X25519'],
31+
[true, 'X448'],
32+
[true, 'Ed25519'],
33+
[true, 'Ed448'],
34+
[true, 'ECDH'],
35+
[true, 'ECDSA'],
36+
[pqc, 'ML-DSA-44'],
37+
[false, 'AES-CTR'],
38+
[false, 'AES-CBC'],
39+
[false, 'AES-GCM'],
40+
[false, 'AES-KW'],
41+
],
2642
};

test/parallel/test-webcrypto-constructors.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,13 @@ const notSubtle = Reflect.construct(function() {}, [], SubtleCrypto);
144144
});
145145
}
146146

147+
// Test SubtleCrypto.prototype.getPublicKey
148+
{
149+
assert.rejects(() => notSubtle.getPublicKey(), {
150+
name: 'TypeError', code: 'ERR_INVALID_THIS',
151+
}).then(common.mustCall());
152+
}
153+
147154
{
148155
subtle.importKey(
149156
'raw',
Lines changed: 51 additions & 0 deletions

0 commit comments

Comments
 (0)