GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — crypto: add subtle.getPublicKey() utility function in Web Cryptography · nodejs/node@6fcce90

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
  • doc/api
    • webcrypto.md
  • lib/internal/crypto
    • webcrypto.js
  • test
    • fixtures/webcrypto
      • supports-modern-algorithms.mjs
    • parallel
      • test-webcrypto-constructors.js
      • test-webcrypto-get-public-key.mjs
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎doc/api/webcrypto.md‎

Copy file name to clipboardExpand all lines: doc/api/webcrypto.md
+47-31Lines changed: 47 additions & 31 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ Key Formats:
114114

115115
Methods:
116116

117+
* [`subtle.getPublicKey()`][]
117118
* [`SubtleCrypto.supports()`][]
118119

119120
## Secure Curves in the Web Cryptography API
@@ -477,36 +478,36 @@ const decrypted = new TextDecoder().decode(await crypto.subtle.decrypt(
477478
The table details the algorithms supported by the Node.js Web Crypto API
478479
implementation and the APIs supported for each:
479480
480-
| Algorithm | `generateKey` | `exportKey` | `importKey` | `encrypt` | `decrypt` | `wrapKey` | `unwrapKey` | `deriveBits` | `deriveKey` | `sign` | `verify` | `digest` |
481-
| ---------------------------- | ------------- | ----------- | ----------- | --------- | --------- | --------- | ----------- | ------------ | ----------- | ------ | -------- | -------- |
482-
| `'AES-CBC'` | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | | | | |
483-
| `'AES-CTR'` | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | | | | |
484-
| `'AES-GCM'` | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | | | | |
485-
| `'AES-KW'` | ✔ | ✔ | ✔ | | | ✔ | ✔ | | | | | |
486-
| `'cSHAKE128'`[^modern-algos] | | | | | | | | | | | | ✔ |
487-
| `'cSHAKE256'`[^modern-algos] | | | | | | | | | | | | ✔ |
488-
| `'ECDH'` | ✔ | ✔ | ✔ | | | | | ✔ | ✔ | | | |
489-
| `'ECDSA'` | ✔ | ✔ | ✔ | | | | | | | ✔ | ✔ | |
490-
| `'Ed25519'` | ✔ | ✔ | ✔ | | | | | | | ✔ | ✔ | |
491-
| `'Ed448'`[^secure-curves] | ✔ | ✔ | ✔ | | | | | | | ✔ | ✔ | |
492-
| `'HKDF'` | | ✔ | ✔ | | | | | ✔ | ✔ | | | |
493-
| `'HMAC'` | ✔ | ✔ | ✔ | | | | | | | ✔ | ✔ | |
494-
| `'ML-DSA-44'`[^modern-algos] | ✔ | ✔ | ✔ | | | | | | | ✔ | ✔ | |
495-
| `'ML-DSA-65'`[^modern-algos] | ✔ | ✔ | ✔ | | | | | | | ✔ | ✔ | |
496-
| `'ML-DSA-87'`[^modern-algos] | ✔ | ✔ | ✔ | | | | | | | ✔ | ✔ | |
497-
| `'PBKDF2'` | | ✔ | ✔ | | | | | ✔ | ✔ | | | |
498-
| `'RSA-OAEP'` | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | | | | |
499-
| `'RSA-PSS'` | ✔ | ✔ | ✔ | | | | | | | ✔ | ✔ | |
500-
| `'RSASSA-PKCS1-v1_5'` | ✔ | ✔ | ✔ | | | | | | | ✔ | ✔ | |
501-
| `'SHA-1'` | | | | | | | | | | | | ✔ |
502-
| `'SHA-256'` | | | | | | | | | | | | ✔ |
503-
| `'SHA-384'` | | | | | | | | | | | | ✔ |
504-
| `'SHA-512'` | | | | | | | | | | | | ✔ |
505-
| `'SHA3-256'`[^modern-algos] | | | | | | | | | | | | ✔ |
506-
| `'SHA3-384'`[^modern-algos] | | | | | | | | | | | | ✔ |
507-
| `'SHA3-512'`[^modern-algos] | | | | | | | | | | | | ✔ |
508-
| `'X25519'` | ✔ | ✔ | ✔ | | | | | ✔ | ✔ | | | |
509-
| `'X448'`[^secure-curves] | ✔ | ✔ | ✔ | | | | | ✔ | ✔ | | | |
481+
| Algorithm | `generateKey` | `exportKey` | `importKey` | `encrypt` | `decrypt` | `wrapKey` | `unwrapKey` | `deriveBits` | `deriveKey` | `sign` | `verify` | `digest` | `getPublicKey` |
482+
| ---------------------------- | ------------- | ----------- | ----------- | --------- | --------- | --------- | ----------- | ------------ | ----------- | ------ | -------- | -------- | -------------- |
483+
| `'AES-CBC'` | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | | | | | |
484+
| `'AES-CTR'` | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | | | | | |
485+
| `'AES-GCM'` | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | | | | | |
486+
| `'AES-KW'` | ✔ | ✔ | ✔ | | | ✔ | ✔ | | | | | | |
487+
| `'cSHAKE128'`[^modern-algos] | | | | | | | | | | | | ✔ | |
488+
| `'cSHAKE256'`[^modern-algos] | | | | | | | | | | | | ✔ | |
489+
| `'ECDH'` | ✔ | ✔ | ✔ | | | | | ✔ | ✔ | | | | ✔ |
490+
| `'ECDSA'` | ✔ | ✔ | ✔ | | | | | | | ✔ | ✔ | | ✔ |
491+
| `'Ed25519'` | ✔ | ✔ | ✔ | | | | | | | ✔ | ✔ | | ✔ |
492+
| `'Ed448'`[^secure-curves] | ✔ | ✔ | ✔ | | | | | | | ✔ | ✔ | | ✔ |
493+
| `'HKDF'` | | ✔ | ✔ | | | | | ✔ | ✔ | | | | |
494+
| `'HMAC'` | ✔ | ✔ | ✔ | | | | | | | ✔ | ✔ | | |
495+
| `'ML-DSA-44'`[^modern-algos] | ✔ | ✔ | ✔ | | | | | | | ✔ | ✔ | | ✔ |
496+
| `'ML-DSA-65'`[^modern-algos] | ✔ | ✔ | ✔ | | | | | | | ✔ | ✔ | | ✔ |
497+
| `'ML-DSA-87'`[^modern-algos] | ✔ | ✔ | ✔ | | | | | | | ✔ | ✔ | | ✔ |
498+
| `'PBKDF2'` | | ✔ | ✔ | | | | | ✔ | ✔ | | | | |
499+
| `'RSA-OAEP'` | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | | | | | ✔ |
500+
| `'RSA-PSS'` | ✔ | ✔ | ✔ | | | | | | | ✔ | ✔ | | ✔ |
501+
| `'RSASSA-PKCS1-v1_5'` | ✔ | ✔ | ✔ | | | | | | | ✔ | ✔ | | ✔ |
502+
| `'SHA-1'` | | | | | | | | | | | | ✔ | |
503+
| `'SHA-256'` | | | | | | | | | | | | ✔ | |
504+
| `'SHA-384'` | | | | | | | | | | | | ✔ | |
505+
| `'SHA-512'` | | | | | | | | | | | | ✔ | |
506+
| `'SHA3-256'`[^modern-algos] | | | | | | | | | | | | ✔ | |
507+
| `'SHA3-384'`[^modern-algos] | | | | | | | | | | | | ✔ | |
508+
| `'SHA3-512'`[^modern-algos] | | | | | | | | | | | | ✔ | |
509+
| `'X25519'` | ✔ | ✔ | ✔ | | | | | ✔ | ✔ | | | | ✔ |
510+
| `'X448'`[^secure-curves] | ✔ | ✔ | ✔ | | | | | ✔ | ✔ | | | | ✔ |
510511
511512
## Class: `Crypto`
512513
@@ -691,7 +692,7 @@ added: REPLACEME
691692
692693
<!--lint disable maximum-line-length remark-lint-->
693694
694-
* `operation` {string} "encrypt", "decrypt", "sign", "verify", "digest", "generateKey", "deriveKey", "deriveBits", "importKey", "exportKey", "wrapKey", or "unwrapKey"
695+
* `operation` {string} "encrypt", "decrypt", "sign", "verify", "digest", "generateKey", "deriveKey", "deriveBits", "importKey", "exportKey", "getPublicKey", "wrapKey", or "unwrapKey"
695696
* `algorithm` {string|Algorithm}
696697
* `lengthOrAdditionalAlgorithm` {null|number|string|Algorithm|undefined} Depending on the operation this is either ignored, the value of the length argument when operation is "deriveBits", the algorithm of key to be derived when operation is "deriveKey", the algorithm of key to be exported before wrapping when operation is "wrapKey", or the algorithm of key to be imported after unwrapping when operation is "unwrapKey". **Default:** `null` when operation is "deriveBits", `undefined` otherwise.
697698
* Returns: {boolean} Indicating whether the implementation supports the given operation
@@ -925,6 +926,20 @@ specification.
925926
| `'RSA-PSS'` | ✔ | ✔ | ✔ | | | | |
926927
| `'RSASSA-PKCS1-v1_5'` | ✔ | ✔ | ✔ | | | | |
927928
929+
### `subtle.getPublicKey(key, keyUsages)`
930+
931+
<!-- YAML
932+
added: REPLACEME
933+
-->
934+
935+
> Stability: 1.1 - Active development
936+
937+
* `key` {CryptoKey} A private key from which to derive the corresponding public key.
938+
* `keyUsages` {string\[]} See [Key usages][].
939+
* Returns: {Promise} Fulfills with a {CryptoKey} upon success.
940+
941+
Derives the public key from a given private key.
942+
928943
### `subtle.generateKey(algorithm, extractable, keyUsages)`
929944
930945
<!-- YAML
@@ -2142,3 +2157,4 @@ The length (in bytes) of the random salt to use.
21422157
[Secure Curves in the Web Cryptography API]: #secure-curves-in-the-web-cryptography-api
21432158
[Web Crypto API]: https://www.w3.org/TR/WebCryptoAPI/
21442159
[`SubtleCrypto.supports()`]: #static-method-subtlecryptosupportsoperation-algorithm-lengthoradditionalalgorithm
2160+
[`subtle.getPublicKey()`]: #subtlegetpublickeykey-keyusages
Collapse file

‎lib/internal/crypto/webcrypto.js‎

Copy file name to clipboardExpand all lines: lib/internal/crypto/webcrypto.js
+55Lines 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 = {
Collapse file

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

Copy file name to clipboardExpand all lines: test/fixtures/webcrypto/supports-modern-algorithms.mjs
+16Lines 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
};
Collapse file

‎test/parallel/test-webcrypto-constructors.js‎

Copy file name to clipboardExpand all lines: test/parallel/test-webcrypto-constructors.js
+7Lines 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',
Collapse file

‎test/parallel/test-webcrypto-get-public-key.mjs‎

Copy file name to clipboard
+51Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import * as common from '../common/index.mjs';
2+
3+
if (!common.hasCrypto) common.skip('missing crypto');
4+
5+
import * as assert from 'node:assert';
6+
const { subtle } = globalThis.crypto;
7+
8+
const RSA_KEY_GEN = {
9+
modulusLength: 2048,
10+
publicExponent: new Uint8Array([1, 0, 1]),
11+
hash: 'SHA-256',
12+
};
13+
14+
const publicUsages = {
15+
'ECDH': [],
16+
'ECDSA': ['verify'],
17+
'Ed25519': ['verify'],
18+
'RSA-OAEP': ['encrypt', 'wrapKey'],
19+
'RSA-PSS': ['verify'],
20+
'RSASSA-PKCS1-v1_5': ['verify'],
21+
'X25519': [],
22+
};
23+
24+
for await (const { privateKey } of [
25+
subtle.generateKey({ name: 'ECDH', namedCurve: 'P-256' }, false, ['deriveBits']),
26+
subtle.generateKey({ name: 'ECDSA', namedCurve: 'P-256' }, false, ['sign']),
27+
subtle.generateKey('Ed25519', false, ['sign']),
28+
subtle.generateKey({ name: 'RSA-OAEP', ...RSA_KEY_GEN }, false, ['decrypt', 'unwrapKey']),
29+
subtle.generateKey({ name: 'RSA-PSS', ...RSA_KEY_GEN }, false, ['sign']),
30+
subtle.generateKey({ name: 'RSASSA-PKCS1-v1_5', ...RSA_KEY_GEN }, false, ['sign']),
31+
subtle.generateKey('X25519', false, ['deriveBits']),
32+
]) {
33+
const { name } = privateKey.algorithm;
34+
const usages = publicUsages[name];
35+
const publicKey = await subtle.getPublicKey(privateKey, usages);
36+
assert.deepStrictEqual(publicKey.algorithm, privateKey.algorithm);
37+
assert.strictEqual(publicKey.type, 'public');
38+
assert.strictEqual(publicKey.extractable, true);
39+
40+
await assert.rejects(() => subtle.getPublicKey(privateKey, ['deriveBits']), {
41+
name: 'SyntaxError',
42+
message: /Unsupported key usage/
43+
});
44+
}
45+
46+
const secretKey = await subtle.generateKey(
47+
{ name: 'AES-CBC', length: 128 }, true, ['encrypt', 'decrypt']);
48+
await assert.rejects(() => subtle.getPublicKey(secretKey, ['encrypt', 'decrypt']), {
49+
name: 'InvalidAccessError',
50+
message: 'key must be a private key'
51+
});

0 commit comments

Comments
0 (0)
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.