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 SubtleCrypto.supports feature detection in Web Cryptography · nodejs/node@f4fbcca

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
  • doc/api
    • webcrypto.md
  • lib/internal/crypto
    • hkdf.js
    • pbkdf2.js
    • webcrypto.js
  • test
    • fixtures/webcrypto
      • supports-level-2.mjs
      • supports-modern-algorithms.mjs
      • supports-secure-curves.mjs
    • parallel
      • test-webcrypto-constructors.js
      • test-webcrypto-supports.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
+96Lines changed: 96 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,10 @@ Key Formats:
101101
* `'raw-secret'`
102102
* `'raw-seed'`
103103

104+
Methods:
105+
106+
* [`SubtleCrypto.supports()`][]
107+
104108
## Secure Curves in the Web Cryptography API
105109

106110
> Stability: 1.1 - Active development
@@ -387,6 +391,76 @@ async function digest(data, algorithm = 'SHA-512') {
387391
}
388392
```
389393

394+
### Checking for runtime algorithm support
395+
396+
[`SubtleCrypto.supports()`][] allows feature detection in Web Crypto API,
397+
which can be used to detect whether a given algorithm identifier
398+
(including its parameters) is supported for the given operation.
399+
400+
This example derives a key from a password using Argon2, if available,
401+
or PBKDF2, otherwise; and then encrypts and decrypts some text with it
402+
using AES-OCB, if available, and AES-GCM, otherwise.
403+
404+
```mjs
405+
const { SubtleCrypto, crypto } = globalThis;
406+
407+
const password = 'correct horse battery staple';
408+
const derivationAlg =
409+
SubtleCrypto.supports?.('importKey', 'Argon2id') ?
410+
'Argon2id' :
411+
'PBKDF2';
412+
const encryptionAlg =
413+
SubtleCrypto.supports?.('importKey', 'AES-OCB') ?
414+
'AES-OCB' :
415+
'AES-GCM';
416+
const passwordKey = await crypto.subtle.importKey(
417+
derivationAlg === 'Argon2id' ? 'raw-secret' : 'raw',
418+
new TextEncoder().encode(password),
419+
derivationAlg,
420+
false,
421+
['deriveKey'],
422+
);
423+
const nonce = crypto.getRandomValues(new Uint8Array(16));
424+
const derivationParams =
425+
derivationAlg === 'Argon2id' ?
426+
{
427+
nonce,
428+
parallelism: 4,
429+
memory: 2 ** 21,
430+
passes: 1,
431+
} :
432+
{
433+
salt: nonce,
434+
iterations: 100_000,
435+
hash: 'SHA-256',
436+
};
437+
const key = await crypto.subtle.deriveKey(
438+
{
439+
name: derivationAlg,
440+
...derivationParams,
441+
},
442+
passwordKey,
443+
{
444+
name: encryptionAlg,
445+
length: 256,
446+
},
447+
false,
448+
['encrypt', 'decrypt'],
449+
);
450+
const plaintext = 'Hello, world!';
451+
const iv = crypto.getRandomValues(new Uint8Array(16));
452+
const encrypted = await crypto.subtle.encrypt(
453+
{ name: encryptionAlg, iv },
454+
key,
455+
new TextEncoder().encode(plaintext),
456+
);
457+
const decrypted = new TextDecoder().decode(await crypto.subtle.decrypt(
458+
{ name: encryptionAlg, iv },
459+
key,
460+
encrypted,
461+
));
462+
```
463+
390464
## Algorithm matrix
391465
392466
The table details the algorithms supported by the Node.js Web Crypto API
@@ -591,6 +665,27 @@ added: v15.0.0
591665
added: v15.0.0
592666
-->
593667
668+
### Static method: `SubtleCrypto.supports(operation, algorithm[, lengthOrAdditionalAlgorithm])`
669+
670+
<!-- YAML
671+
added: REPLACEME
672+
-->
673+
674+
> Stability: 1.1 - Active development
675+
676+
<!--lint disable maximum-line-length remark-lint-->
677+
678+
* `operation` {string} "encrypt", "decrypt", "sign", "verify", "digest", "generateKey", "deriveKey", "deriveBits", "importKey", "exportKey", "wrapKey", or "unwrapKey"
679+
* `algorithm` {string|Algorithm}
680+
* `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.
681+
* Returns: {boolean} Indicating whether the implementation supports the given operation
682+
683+
<!--lint enable maximum-line-length remark-lint-->
684+
685+
Allows feature detection in Web Crypto API,
686+
which can be used to detect whether a given algorithm identifier
687+
(including its parameters) is supported for the given operation.
688+
594689
### `subtle.decrypt(algorithm, key, data)`
595690
596691
<!-- YAML
@@ -1922,3 +2017,4 @@ The length (in bytes) of the random salt to use.
19222017
[RFC 4122]: https://www.rfc-editor.org/rfc/rfc4122.txt
19232018
[Secure Curves in the Web Cryptography API]: #secure-curves-in-the-web-cryptography-api
19242019
[Web Crypto API]: https://www.w3.org/TR/WebCryptoAPI/
2020+
[`SubtleCrypto.supports()`]: #static-method-subtlecryptosupportsoperation-algorithm-lengthoradditionalalgorithm
Collapse file

‎lib/internal/crypto/hkdf.js‎

Copy file name to clipboardExpand all lines: lib/internal/crypto/hkdf.js
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,4 +170,5 @@ module.exports = {
170170
hkdf,
171171
hkdfSync,
172172
hkdfDeriveBits,
173+
validateHkdfDeriveBitsLength,
173174
};
Collapse file

‎lib/internal/crypto/pbkdf2.js‎

Copy file name to clipboardExpand all lines: lib/internal/crypto/pbkdf2.js
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,4 +128,5 @@ module.exports = {
128128
pbkdf2,
129129
pbkdf2Sync,
130130
pbkdf2DeriveBits,
131+
validatePbkdf2DeriveBitsLength,
131132
};
Collapse file

‎lib/internal/crypto/webcrypto.js‎

Copy file name to clipboardExpand all lines: lib/internal/crypto/webcrypto.js
+141Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ const {
4747
} = require('internal/crypto/util');
4848

4949
const {
50+
emitExperimentalWarning,
5051
kEnumerableProperty,
5152
lazyDOMException,
5253
} = require('internal/util');
@@ -1026,7 +1027,147 @@ class SubtleCrypto {
10261027
constructor() {
10271028
throw new ERR_ILLEGAL_CONSTRUCTOR();
10281029
}
1030+
1031+
// Implements https://wicg.github.io/webcrypto-modern-algos/#SubtleCrypto-method-supports
1032+
static supports(operation, algorithm, lengthOrAdditionalAlgorithm = null) {
1033+
emitExperimentalWarning('The supports Web Crypto API method');
1034+
if (this !== SubtleCrypto) throw new ERR_INVALID_THIS('SubtleCrypto constructor');
1035+
webidl ??= require('internal/crypto/webidl');
1036+
const prefix = "Failed to execute 'supports' on 'SubtleCrypto'";
1037+
webidl.requiredArguments(arguments.length, 2, { prefix });
1038+
1039+
operation = webidl.converters.DOMString(operation, {
1040+
prefix,
1041+
context: '1st argument',
1042+
});
1043+
algorithm = webidl.converters.AlgorithmIdentifier(algorithm, {
1044+
prefix,
1045+
context: '2nd argument',
1046+
});
1047+
1048+
switch (operation) {
1049+
case 'encrypt':
1050+
case 'decrypt':
1051+
case 'sign':
1052+
case 'verify':
1053+
case 'digest':
1054+
case 'generateKey':
1055+
case 'deriveKey':
1056+
case 'deriveBits':
1057+
case 'importKey':
1058+
case 'exportKey':
1059+
case 'wrapKey':
1060+
case 'unwrapKey':
1061+
break;
1062+
default:
1063+
return false;
1064+
}
1065+
1066+
let length;
1067+
let additionalAlgorithm;
1068+
if (operation === 'deriveKey') {
1069+
additionalAlgorithm = webidl.converters.AlgorithmIdentifier(lengthOrAdditionalAlgorithm, {
1070+
prefix,
1071+
context: '3rd argument',
1072+
});
1073+
1074+
if (!check('importKey', additionalAlgorithm)) {
1075+
return false;
1076+
}
1077+
1078+
try {
1079+
length = getKeyLength(normalizeAlgorithm(additionalAlgorithm, 'get key length'));
1080+
} catch {
1081+
return false;
1082+
}
1083+
1084+
operation = 'deriveBits';
1085+
} else if (operation === 'wrapKey') {
1086+
additionalAlgorithm = webidl.converters.AlgorithmIdentifier(lengthOrAdditionalAlgorithm, {
1087+
prefix,
1088+
context: '3rd argument',
1089+
});
1090+
1091+
if (!check('exportKey', additionalAlgorithm)) {
1092+
return false;
1093+
}
1094+
} else if (operation === 'unwrapKey') {
1095+
additionalAlgorithm = webidl.converters.AlgorithmIdentifier(lengthOrAdditionalAlgorithm, {
1096+
prefix,
1097+
context: '3rd argument',
1098+
});
1099+
1100+
if (!check('importKey', additionalAlgorithm)) {
1101+
return false;
1102+
}
1103+
} else if (operation === 'deriveBits') {
1104+
length = lengthOrAdditionalAlgorithm;
1105+
if (length !== null) {
1106+
length = webidl.converters['unsigned long'](length, {
1107+
prefix,
1108+
context: '3rd argument',
1109+
});
1110+
}
1111+
}
1112+
1113+
return check(operation, algorithm, length);
1114+
}
10291115
}
1116+
1117+
function check(op, alg, length) {
1118+
let normalizedAlgorithm;
1119+
try {
1120+
normalizedAlgorithm = normalizeAlgorithm(alg, op);
1121+
} catch {
1122+
if (op === 'wrapKey') {
1123+
return check('encrypt', alg);
1124+
}
1125+
1126+
if (op === 'unwrapKey') {
1127+
return check('decrypt', alg);
1128+
}
1129+
1130+
return false;
1131+
}
1132+
1133+
switch (op) {
1134+
case 'encrypt':
1135+
case 'decrypt':
1136+
case 'sign':
1137+
case 'verify':
1138+
case 'digest':
1139+
case 'generateKey':
1140+
case 'importKey':
1141+
case 'exportKey':
1142+
case 'wrapKey':
1143+
case 'unwrapKey':
1144+
return true;
1145+
case 'deriveBits': {
1146+
if (normalizedAlgorithm.name === 'HKDF') {
1147+
try {
1148+
require('internal/crypto/hkdf').validateHkdfDeriveBitsLength(length);
1149+
} catch {
1150+
return false;
1151+
}
1152+
}
1153+
1154+
if (normalizedAlgorithm.name === 'PBKDF2') {
1155+
try {
1156+
require('internal/crypto/pbkdf2').validatePbkdf2DeriveBitsLength(length);
1157+
} catch {
1158+
return false;
1159+
}
1160+
}
1161+
1162+
return true;
1163+
}
1164+
default: {
1165+
const assert = require('internal/assert');
1166+
assert.fail('Unreachable code');
1167+
}
1168+
}
1169+
}
1170+
10301171
const subtle = ReflectConstruct(function() {}, [], SubtleCrypto);
10311172

10321173
class Crypto {

0 commit comments

Comments
0 (0)
You are visitor 01402520 — 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.