From cefc73cf32ba492c64a81da4a9d3da44427afd3d Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Sun, 2 Mar 2025 09:45:13 +0100 Subject: [PATCH] crypto: add SubtleCrypto.supports feature detection in Web Crypto API --- doc/api/webcrypto.md | 90 ++++++++ lib/internal/crypto/hkdf.js | 1 + lib/internal/crypto/pbkdf2.js | 1 + lib/internal/crypto/util.js | 16 ++ lib/internal/crypto/webcrypto.js | 147 ++++++++++++ test/fixtures/webcrypto/supports-level-2.mjs | 228 +++++++++++++++++++ test/parallel/test-webcrypto-supports.mjs | 39 ++++ 7 files changed, 522 insertions(+) create mode 100644 test/fixtures/webcrypto/supports-level-2.mjs create mode 100644 test/parallel/test-webcrypto-supports.mjs diff --git a/doc/api/webcrypto.md b/doc/api/webcrypto.md index f93b7e98de4f38..34abbd99d2ce54 100644 --- a/doc/api/webcrypto.md +++ b/doc/api/webcrypto.md @@ -352,6 +352,73 @@ async function digest(data, algorithm = 'SHA-512') { } ``` +### Checking for runtime algorithm support + +> Stability: 1.0 - Early development. SubleCrypto.supports is an experimental +> implementation based on [Modern Algorithms in the Web Cryptography API][] + +This example derives a key from a password using Argon2, if available, +or PBKDF2, otherwise; and then encrypts and decrypts some text with it +using AES-OCB, if available, and AES-GCM, otherwise. + +```mjs +const password = 'correct horse battery staple'; +const derivationAlg = + SubtleCrypto.supports?.('importKey', 'Argon2id') ? + 'Argon2id' : + 'PBKDF2'; +const encryptionAlg = + SubtleCrypto.supports?.('importKey', 'AES-OCB') ? + 'AES-OCB' : + 'AES-GCM'; +const passwordKey = await crypto.subtle.importKey( + 'raw', + new TextEncoder().encode(password), + derivationAlg, + false, + ['deriveKey'], +); +const nonce = crypto.getRandomValues(new Uint8Array(16)); +const derivationParams = + derivationAlg === 'Argon2id' ? + { + nonce, + parallelism: 4, + memory: 2 ** 21, + passes: 1, + } : + { + salt: nonce, + iterations: 100_000, + hash: 'SHA-256', + }; +const key = await crypto.subtle.deriveKey( + { + name: derivationAlg, + ...derivationParams, + }, + passwordKey, + { + name: encryptionAlg, + length: 256, + }, + false, + ['encrypt', 'decrypt'], +); +const plaintext = 'Hello, world!'; +const iv = crypto.getRandomValues(new Uint8Array(16)); +const encrypted = await crypto.subtle.encrypt( + { name: encryptionAlg, iv }, + key, + new TextEncoder().encode(plaintext), +); +const decrypted = new TextDecoder().decode(await crypto.subtle.decrypt( + { name: encryptionAlg, iv }, + key, + encrypted, +)); +``` + ## Algorithm matrix The table details the algorithms supported by the Node.js Web Crypto API @@ -550,6 +617,28 @@ added: v15.0.0 added: v15.0.0 --> +### Static method: `SubtleCrypto.supports(operation, algorithm[, lengthOrAdditionalAlgorithm])` + +> Stability: 1.0 - Early development. SubleCrypto.supports is an experimental +> implementation based on [Modern Algorithms in the Web Cryptography API][] + + + + + +* `operation`: {string} "encrypt", "decrypt", "sign", "verify", "digest", "generateKey", "deriveKey", "deriveBits", "importKey", "exportKey", "wrapKey", or "unwrapKey" +* `algorithm`: {string|Algorithm|AesCbcParams|AesCtrParams|AesGcmParams|AesKeyGenParams|EcdhKeyDeriveParams|EcdsaParams|EcKeyGenParams|EcKeyImportParams|Ed448Params|HkdfParams|HmacImportParams|HmacKeyGenParams|Pbkdf2Params|RsaHashedImportParams|RsaHashedKeyGenParams|RsaOaepParams|RsaPssParams} +* `lengthOrAdditionalAlgorithm`: {null|number|string|Algorithm|AesCbcParams|AesCtrParams|AesDerivedKeyParams|AesGcmParams|AesKeyGenParams|EcdhKeyDeriveParams|EcdsaParams|EcKeyGenParams|EcKeyImportParams|Ed448Params|HkdfParams|HmacImportParams|HmacKeyGenParams|Pbkdf2Params|RsaHashedImportParams|RsaHashedKeyGenParams|RsaOaepParams|RsaPssParams} 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. +* Returns: {boolean} Indicating whether the implementation supports the given operation + + + +Allows feature detection in Web Crypto API, which can be used to detect whether +a given algorithm identifier (including any of its parameters) is supported for +the given operation. + ### `subtle.decrypt(algorithm, key, data)`