summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/tests/unit/webcrypto_test.ts47
-rw-r--r--ext/crypto/00_crypto.js240
-rw-r--r--ext/crypto/01_webidl.js118
-rw-r--r--ext/crypto/lib.deno_crypto.d.ts35
-rw-r--r--tools/wpt/expectation.json36
5 files changed, 384 insertions, 92 deletions
diff --git a/cli/tests/unit/webcrypto_test.ts b/cli/tests/unit/webcrypto_test.ts
index 0e4b17b9a..a37e32eba 100644
--- a/cli/tests/unit/webcrypto_test.ts
+++ b/cli/tests/unit/webcrypto_test.ts
@@ -243,22 +243,43 @@ unitTest(async function testSignRSASSAKey() {
assert(signature);
});
+// deno-fmt-ignore
+const rawKey = new Uint8Array([
+ 1, 2, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16
+]);
+
+const jwk: JsonWebKey = {
+ kty: "oct",
+ // unpadded base64 for rawKey.
+ k: "AQIDBAUGBwgJCgsMDQ4PEA",
+ alg: "HS256",
+};
+
unitTest(async function subtleCryptoHmacImportExport() {
- // deno-fmt-ignore
- const rawKey = new Uint8Array([
- 1, 2, 3, 4, 5, 6, 7, 8,
- 9, 10, 11, 12, 13, 14, 15, 16
- ]);
- const key = await crypto.subtle.importKey(
+ const key1 = await crypto.subtle.importKey(
"raw",
rawKey,
{ name: "HMAC", hash: "SHA-256" },
true,
["sign"],
);
- const actual = await crypto.subtle.sign(
+ const key2 = await crypto.subtle.importKey(
+ "jwk",
+ jwk,
+ { name: "HMAC", hash: "SHA-256" },
+ true,
+ ["sign"],
+ );
+ const actual1 = await crypto.subtle.sign(
{ name: "HMAC" },
- key,
+ key1,
+ new Uint8Array([1, 2, 3, 4]),
+ );
+
+ const actual2 = await crypto.subtle.sign(
+ { name: "HMAC" },
+ key2,
new Uint8Array([1, 2, 3, 4]),
);
// deno-fmt-ignore
@@ -269,10 +290,14 @@ unitTest(async function subtleCryptoHmacImportExport() {
23, 122, 222, 1, 146, 46, 182, 87,
]);
assertEquals(
- new Uint8Array(actual),
+ new Uint8Array(actual1),
expected,
);
-
- const exportedKey = await crypto.subtle.exportKey("raw", key);
+ assertEquals(
+ new Uint8Array(actual2),
+ expected,
+ );
+ // TODO(@littledivy): Add a test for exporting JWK key when supported.
+ const exportedKey = await crypto.subtle.exportKey("raw", key1);
assertEquals(new Uint8Array(exportedKey), rawKey);
});
diff --git a/ext/crypto/00_crypto.js b/ext/crypto/00_crypto.js
index 5c80ac0ca..375170ad9 100644
--- a/ext/crypto/00_crypto.js
+++ b/ext/crypto/00_crypto.js
@@ -12,13 +12,18 @@
const core = window.Deno.core;
const webidl = window.__bootstrap.webidl;
const { DOMException } = window.__bootstrap.domException;
+ const { atob } = window.__bootstrap.base64;
const {
ArrayPrototypeFind,
- ArrayBufferIsView,
+ ArrayPrototypeEvery,
ArrayPrototypeIncludes,
+ ArrayBuffer,
+ ArrayBufferIsView,
BigInt64Array,
StringPrototypeToUpperCase,
+ StringPrototypeReplace,
+ StringPrototypeCharCodeAt,
Symbol,
SymbolFor,
SymbolToStringTag,
@@ -100,6 +105,23 @@
},
};
+ // Decodes the unpadded base64 to the octet sequence containing key value `k` defined in RFC7518 Section 6.4
+ function decodeSymmetricKey(key) {
+ // Decode from base64url without `=` padding.
+ const base64 = StringPrototypeReplace(
+ StringPrototypeReplace(key, /\-/g, "+"),
+ /\_/g,
+ "/",
+ );
+ const decodedKey = atob(base64);
+ const keyLength = decodedKey.length;
+ const keyBytes = new Uint8Array(keyLength);
+ for (let i = 0; i < keyLength; i++) {
+ keyBytes[i] = StringPrototypeCharCodeAt(decodedKey, i);
+ }
+ return keyBytes;
+ }
+
// See https://www.w3.org/TR/WebCryptoAPI/#dfn-normalize-an-algorithm
// 18.4.4
function normalizeAlgorithm(algorithm, op) {
@@ -631,7 +653,7 @@
prefix,
context: "Argument 1",
});
- keyData = webidl.converters.BufferSource(keyData, {
+ keyData = webidl.converters["BufferSource or JsonWebKey"](keyData, {
prefix,
context: "Argument 2",
});
@@ -649,22 +671,32 @@
});
// 2.
- if (ArrayBufferIsView(keyData)) {
- keyData = new Uint8Array(
- keyData.buffer,
- keyData.byteOffset,
- keyData.byteLength,
- );
+ if (format !== "jwk") {
+ if (ArrayBufferIsView(keyData) || keyData instanceof ArrayBuffer) {
+ if (ArrayBufferIsView(keyData)) {
+ keyData = new Uint8Array(
+ keyData.buffer,
+ keyData.byteOffset,
+ keyData.byteLength,
+ );
+ } else {
+ keyData = new Uint8Array(keyData);
+ }
+ keyData = TypedArrayPrototypeSlice(keyData);
+ } else {
+ throw new TypeError("keyData is a JsonWebKey");
+ }
} else {
- keyData = new Uint8Array(keyData);
+ if (ArrayBufferIsView(keyData) || keyData instanceof ArrayBuffer) {
+ throw new TypeError("keyData is not a JsonWebKey");
+ }
}
- keyData = TypedArrayPrototypeSlice(keyData);
const normalizedAlgorithm = normalizeAlgorithm(algorithm, "importKey");
switch (normalizedAlgorithm.name) {
- // https://w3c.github.io/webcrypto/#hmac-operations
case "HMAC": {
+ // 2.
if (
ArrayPrototypeFind(
keyUsages,
@@ -674,59 +706,177 @@
throw new DOMException("Invalid key usages", "SyntaxError");
}
+ // 3.
+ let hash;
+ let data;
+
+ // 4. https://w3c.github.io/webcrypto/#hmac-operations
switch (format) {
case "raw": {
- const hash = normalizedAlgorithm.hash;
+ data = keyData;
+ hash = normalizedAlgorithm.hash;
+ break;
+ }
+ case "jwk": {
+ // TODO(@littledivy): Why does the spec validate JWK twice?
+ const jwk = keyData;
+ // 2.
+ if (jwk.kty !== "oct") {
+ throw new DOMException(
+ "`kty` member of JsonWebKey must be `oct`",
+ "DataError",
+ );
+ }
+
+ // Section 6.4.1 of RFC7518
+ if (!jwk.k) {
+ throw new DOMException(
+ "`k` member of JsonWebKey must be present",
+ "DataError",
+ );
+ }
+
+ // 4.
+ data = decodeSymmetricKey(jwk.k);
// 5.
- let length = keyData.byteLength * 8;
+ hash = normalizedAlgorithm.hash;
// 6.
- if (length === 0) {
- throw new DOMException("Key length is zero", "DataError");
+ switch (hash.name) {
+ case "SHA-1": {
+ if (jwk.alg !== undefined && jwk.alg !== "HS1") {
+ throw new DOMException(
+ "`alg` member of JsonWebKey must be `HS1`",
+ "DataError",
+ );
+ }
+ break;
+ }
+ case "SHA-256": {
+ if (jwk.alg !== undefined && jwk.alg !== "HS256") {
+ throw new DOMException(
+ "`alg` member of JsonWebKey must be `HS256`",
+ "DataError",
+ );
+ }
+ break;
+ }
+ case "SHA-384": {
+ if (jwk.alg !== undefined && jwk.alg !== "HS384") {
+ throw new DOMException(
+ "`alg` member of JsonWebKey must be `HS384`",
+ "DataError",
+ );
+ }
+ break;
+ }
+ case "SHA-512": {
+ if (jwk.alg !== undefined && jwk.alg !== "HS512") {
+ throw new DOMException(
+ "`alg` member of JsonWebKey must be `HS512`",
+ "DataError",
+ );
+ }
+ break;
+ }
+ default:
+ throw new TypeError("unreachable");
}
- if (normalizeAlgorithm.length) {
- // 7.
+
+ // 7.
+ if (keyUsages.length > 0 && jwk.use && jwk.use !== "sign") {
+ throw new DOMException(
+ "`use` member of JsonWebKey must be `sign`",
+ "DataError",
+ );
+ }
+
+ // 8.
+ // Section 4.3 of RFC7517
+ if (jwk.key_ops) {
if (
- normalizedAlgorithm.length > length ||
- normalizedAlgorithm.length <= (length - 8)
+ ArrayPrototypeFind(
+ jwk.key_ops,
+ (u) => !ArrayPrototypeIncludes(recognisedUsages, u),
+ ) !== undefined
) {
throw new DOMException(
- "Key length is invalid",
+ "`key_ops` member of JsonWebKey is invalid",
"DataError",
);
}
- length = normalizeAlgorithm.length;
- }
- if (keyUsages.length == 0) {
- throw new DOMException("Key usage is empty", "SyntaxError");
+ if (
+ !ArrayPrototypeEvery(
+ jwk.key_ops,
+ (u) => ArrayPrototypeIncludes(keyUsages, u),
+ )
+ ) {
+ throw new DOMException(
+ "`key_ops` member of JsonWebKey is invalid",
+ "DataError",
+ );
+ }
}
- const handle = {};
- WeakMapPrototypeSet(KEY_STORE, handle, {
- type: "raw",
- data: keyData,
- });
-
- const algorithm = {
- name: "HMAC",
- length,
- hash,
- };
-
- const key = constructKey(
- "secret",
- extractable,
- usageIntersection(keyUsages, recognisedUsages),
- algorithm,
- handle,
- );
+ // 9.
+ if (jwk.ext === false && extractable == true) {
+ throw new DOMException(
+ "`ext` member of JsonWebKey is invalid",
+ "DataError",
+ );
+ }
- return key;
+ break;
}
- // TODO(@littledivy): jwk
default:
throw new DOMException("Not implemented", "NotSupportedError");
}
+
+ // 5.
+ let length = data.byteLength * 8;
+ // 6.
+ if (length === 0) {
+ throw new DOMException("Key length is zero", "DataError");
+ }
+ // 7.
+ if (normalizedAlgorithm.length !== undefined) {
+ if (
+ normalizedAlgorithm.length > length ||
+ normalizedAlgorithm.length <= (length - 8)
+ ) {
+ throw new DOMException(
+ "Key length is invalid",
+ "DataError",
+ );
+ }
+ length = normalizedAlgorithm.length;
+ }
+
+ if (keyUsages.length == 0) {
+ throw new DOMException("Key usage is empty", "SyntaxError");
+ }
+
+ const handle = {};
+ WeakMapPrototypeSet(KEY_STORE, handle, {
+ type: "raw",
+ data,
+ });
+
+ const algorithm = {
+ name: "HMAC",
+ length,
+ hash,
+ };
+
+ const key = constructKey(
+ "secret",
+ extractable,
+ usageIntersection(keyUsages, recognisedUsages),
+ algorithm,
+ handle,
+ );
+
+ return key;
}
// TODO(@littledivy): RSASSA-PKCS1-v1_5
// TODO(@littledivy): RSA-PSS
diff --git a/ext/crypto/01_webidl.js b/ext/crypto/01_webidl.js
index 43bc5e822..e781e4334 100644
--- a/ext/crypto/01_webidl.js
+++ b/ext/crypto/01_webidl.js
@@ -9,6 +9,7 @@
((window) => {
const webidl = window.__bootstrap.webidl;
const { CryptoKey } = window.__bootstrap.crypto;
+ const { ArrayBufferIsView, ArrayBuffer } = window.__bootstrap.primordials;
webidl.converters.AlgorithmIdentifier = (V, opts) => {
// Union for (object or DOMString)
@@ -18,6 +19,14 @@
return webidl.converters.DOMString(V, opts);
};
+ webidl.converters["BufferSource or JsonWebKey"] = (V, opts) => {
+ // Union for (BufferSource or JsonWebKey)
+ if (ArrayBufferIsView(V) || V instanceof ArrayBuffer) {
+ return webidl.converters.BufferSource(V, opts);
+ }
+ return webidl.converters.JsonWebKey(V, opts);
+ };
+
webidl.converters.KeyType = webidl.createEnumConverter("KeyType", [
"public",
"private",
@@ -178,6 +187,115 @@
webidl.converters.HmacImportParams = webidl
.createDictionaryConverter("HmacImportParams", dictHmacImportParams);
+ const dictRsaOtherPrimesInfo = [
+ {
+ key: "r",
+ converter: webidl.converters["DOMString"],
+ },
+ {
+ key: "d",
+ converter: webidl.converters["DOMString"],
+ },
+ {
+ key: "t",
+ converter: webidl.converters["DOMString"],
+ },
+ ];
+
+ webidl.converters.RsaOtherPrimesInfo = webidl.createDictionaryConverter(
+ "RsaOtherPrimesInfo",
+ dictRsaOtherPrimesInfo,
+ );
+ webidl.converters["sequence<RsaOtherPrimesInfo>"] = webidl
+ .createSequenceConverter(
+ webidl.converters.RsaOtherPrimesInfo,
+ );
+
+ const dictJsonWebKey = [
+ // Sections 4.2 and 4.3 of RFC7517.
+ // https://datatracker.ietf.org/doc/html/rfc7517#section-4
+ {
+ key: "kty",
+ converter: webidl.converters["DOMString"],
+ },
+ {
+ key: "use",
+ converter: webidl.converters["DOMString"],
+ },
+ {
+ key: "key_ops",
+ converter: webidl.converters["sequence<DOMString>"],
+ },
+ {
+ key: "alg",
+ converter: webidl.converters["DOMString"],
+ },
+ // JSON Web Key Parameters Registration
+ {
+ key: "ext",
+ converter: webidl.converters["boolean"],
+ },
+ // Section 6 of RFC7518 JSON Web Algorithms
+ // https://datatracker.ietf.org/doc/html/rfc7518#section-6
+ {
+ key: "crv",
+ converter: webidl.converters["DOMString"],
+ },
+ {
+ key: "x",
+ converter: webidl.converters["DOMString"],
+ },
+ {
+ key: "y",
+ converter: webidl.converters["DOMString"],
+ },
+ {
+ key: "d",
+ converter: webidl.converters["DOMString"],
+ },
+ {
+ key: "n",
+ converter: webidl.converters["DOMString"],
+ },
+ {
+ key: "e",
+ converter: webidl.converters["DOMString"],
+ },
+ {
+ key: "p",
+ converter: webidl.converters["DOMString"],
+ },
+ {
+ key: "q",
+ converter: webidl.converters["DOMString"],
+ },
+ {
+ key: "dp",
+ converter: webidl.converters["DOMString"],
+ },
+ {
+ key: "dq",
+ converter: webidl.converters["DOMString"],
+ },
+ {
+ key: "qi",
+ converter: webidl.converters["DOMString"],
+ },
+ {
+ key: "oth",
+ converter: webidl.converters["sequence<RsaOtherPrimesInfo>"],
+ },
+ {
+ key: "k",
+ converter: webidl.converters["DOMString"],
+ },
+ ];
+
+ webidl.converters.JsonWebKey = webidl.createDictionaryConverter(
+ "JsonWebKey",
+ dictJsonWebKey,
+ );
+
const dictPbkdf2Params = [
...dictAlgorithm,
{
diff --git a/ext/crypto/lib.deno_crypto.d.ts b/ext/crypto/lib.deno_crypto.d.ts
index 6b1901611..2e8d2f8b2 100644
--- a/ext/crypto/lib.deno_crypto.d.ts
+++ b/ext/crypto/lib.deno_crypto.d.ts
@@ -28,6 +28,34 @@ type KeyUsage =
type NamedCurve = string;
+interface RsaOtherPrimesInfo {
+ d?: string;
+ r?: string;
+ t?: string;
+}
+
+interface JsonWebKey {
+ alg?: string;
+ crv?: string;
+ d?: string;
+ dp?: string;
+ dq?: string;
+ e?: string;
+ ext?: boolean;
+ k?: string;
+ // deno-lint-ignore camelcase
+ key_ops?: string[];
+ kty?: string;
+ n?: string;
+ oth?: RsaOtherPrimesInfo[];
+ p?: string;
+ q?: string;
+ qi?: string;
+ use?: string;
+ x?: string;
+ y?: string;
+}
+
interface HmacKeyGenParams extends Algorithm {
hash: HashAlgorithmIdentifier;
length?: number;
@@ -123,6 +151,13 @@ interface SubtleCrypto {
keyUsages: KeyUsage[],
): Promise<CryptoKeyPair | CryptoKey>;
importKey(
+ format: "jwk",
+ keyData: JsonWebKey,
+ algorithm: AlgorithmIdentifier | HmacImportParams,
+ extractable: boolean,
+ keyUsages: KeyUsage[],
+ ): Promise<CryptoKey>;
+ importKey(
format: "raw",
keyData: BufferSource,
algorithm: AlgorithmIdentifier | HmacImportParams,
diff --git a/tools/wpt/expectation.json b/tools/wpt/expectation.json
index e71d06c10..39786ff8e 100644
--- a/tools/wpt/expectation.json
+++ b/tools/wpt/expectation.json
@@ -14041,42 +14041,6 @@
"Good parameters: 256 bits (jwk, {alg: A256KW, k: AQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyA, kty: oct}, {name: AES-KW}, true, [unwrapKey])",
"Good parameters: 256 bits (raw, {0: 1, 1: 2, 10: 11, 11: 12, 12: 13, 13: 14, 14: 15, 15: 16, 16: 17, 17: 18, 18: 19, 19: 20, 2: 3, 20: 21, 21: 22, 22: 23, 23: 24, 24: 25, 25: 26, 26: 27, 27: 28, 28: 29, 29: 30, 3: 4, 30: 31, 31: 32, 4: 5, 5: 6, 6: 7, 7: 8, 8: 9, 9: 10}, {name: AES-KW}, false, [unwrapKey])",
"Good parameters: 256 bits (jwk, {alg: A256KW, k: AQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyA, kty: oct}, {name: AES-KW}, false, [unwrapKey])",
- "Good parameters: 128 bits (jwk, {alg: HS1, k: AQIDBAUGBwgJCgsMDQ4PEA, kty: oct}, {hash: SHA-1, name: HMAC}, false, [sign])",
- "Good parameters: 128 bits (jwk, {alg: HS1, k: AQIDBAUGBwgJCgsMDQ4PEA, kty: oct}, {hash: SHA-1, name: HMAC}, false, [verify, sign])",
- "Good parameters: 128 bits (jwk, {alg: HS1, k: AQIDBAUGBwgJCgsMDQ4PEA, kty: oct}, {hash: SHA-1, name: HMAC}, false, [verify])",
- "Good parameters: 192 bits (jwk, {alg: HS1, k: AQIDBAUGBwgJCgsMDQ4PEBESExQVFhcY, kty: oct}, {hash: SHA-1, name: HMAC}, false, [sign])",
- "Good parameters: 192 bits (jwk, {alg: HS1, k: AQIDBAUGBwgJCgsMDQ4PEBESExQVFhcY, kty: oct}, {hash: SHA-1, name: HMAC}, false, [verify, sign])",
- "Good parameters: 192 bits (jwk, {alg: HS1, k: AQIDBAUGBwgJCgsMDQ4PEBESExQVFhcY, kty: oct}, {hash: SHA-1, name: HMAC}, false, [verify])",
- "Good parameters: 256 bits (jwk, {alg: HS1, k: AQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyA, kty: oct}, {hash: SHA-1, name: HMAC}, false, [sign])",
- "Good parameters: 256 bits (jwk, {alg: HS1, k: AQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyA, kty: oct}, {hash: SHA-1, name: HMAC}, false, [verify, sign])",
- "Good parameters: 256 bits (jwk, {alg: HS1, k: AQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyA, kty: oct}, {hash: SHA-1, name: HMAC}, false, [verify])",
- "Good parameters: 128 bits (jwk, {alg: HS256, k: AQIDBAUGBwgJCgsMDQ4PEA, kty: oct}, {hash: SHA-256, name: HMAC}, false, [sign])",
- "Good parameters: 128 bits (jwk, {alg: HS256, k: AQIDBAUGBwgJCgsMDQ4PEA, kty: oct}, {hash: SHA-256, name: HMAC}, false, [verify, sign])",
- "Good parameters: 128 bits (jwk, {alg: HS256, k: AQIDBAUGBwgJCgsMDQ4PEA, kty: oct}, {hash: SHA-256, name: HMAC}, false, [verify])",
- "Good parameters: 192 bits (jwk, {alg: HS256, k: AQIDBAUGBwgJCgsMDQ4PEBESExQVFhcY, kty: oct}, {hash: SHA-256, name: HMAC}, false, [sign])",
- "Good parameters: 192 bits (jwk, {alg: HS256, k: AQIDBAUGBwgJCgsMDQ4PEBESExQVFhcY, kty: oct}, {hash: SHA-256, name: HMAC}, false, [verify, sign])",
- "Good parameters: 192 bits (jwk, {alg: HS256, k: AQIDBAUGBwgJCgsMDQ4PEBESExQVFhcY, kty: oct}, {hash: SHA-256, name: HMAC}, false, [verify])",
- "Good parameters: 256 bits (jwk, {alg: HS256, k: AQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyA, kty: oct}, {hash: SHA-256, name: HMAC}, false, [sign])",
- "Good parameters: 256 bits (jwk, {alg: HS256, k: AQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyA, kty: oct}, {hash: SHA-256, name: HMAC}, false, [verify, sign])",
- "Good parameters: 256 bits (jwk, {alg: HS256, k: AQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyA, kty: oct}, {hash: SHA-256, name: HMAC}, false, [verify])",
- "Good parameters: 128 bits (jwk, {alg: HS384, k: AQIDBAUGBwgJCgsMDQ4PEA, kty: oct}, {hash: SHA-384, name: HMAC}, false, [sign])",
- "Good parameters: 128 bits (jwk, {alg: HS384, k: AQIDBAUGBwgJCgsMDQ4PEA, kty: oct}, {hash: SHA-384, name: HMAC}, false, [verify, sign])",
- "Good parameters: 128 bits (jwk, {alg: HS384, k: AQIDBAUGBwgJCgsMDQ4PEA, kty: oct}, {hash: SHA-384, name: HMAC}, false, [verify])",
- "Good parameters: 192 bits (jwk, {alg: HS384, k: AQIDBAUGBwgJCgsMDQ4PEBESExQVFhcY, kty: oct}, {hash: SHA-384, name: HMAC}, false, [sign])",
- "Good parameters: 192 bits (jwk, {alg: HS384, k: AQIDBAUGBwgJCgsMDQ4PEBESExQVFhcY, kty: oct}, {hash: SHA-384, name: HMAC}, false, [verify, sign])",
- "Good parameters: 192 bits (jwk, {alg: HS384, k: AQIDBAUGBwgJCgsMDQ4PEBESExQVFhcY, kty: oct}, {hash: SHA-384, name: HMAC}, false, [verify])",
- "Good parameters: 256 bits (jwk, {alg: HS384, k: AQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyA, kty: oct}, {hash: SHA-384, name: HMAC}, false, [sign])",
- "Good parameters: 256 bits (jwk, {alg: HS384, k: AQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyA, kty: oct}, {hash: SHA-384, name: HMAC}, false, [verify, sign])",
- "Good parameters: 256 bits (jwk, {alg: HS384, k: AQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyA, kty: oct}, {hash: SHA-384, name: HMAC}, false, [verify])",
- "Good parameters: 128 bits (jwk, {alg: HS512, k: AQIDBAUGBwgJCgsMDQ4PEA, kty: oct}, {hash: SHA-512, name: HMAC}, false, [sign])",
- "Good parameters: 128 bits (jwk, {alg: HS512, k: AQIDBAUGBwgJCgsMDQ4PEA, kty: oct}, {hash: SHA-512, name: HMAC}, false, [verify, sign])",
- "Good parameters: 128 bits (jwk, {alg: HS512, k: AQIDBAUGBwgJCgsMDQ4PEA, kty: oct}, {hash: SHA-512, name: HMAC}, false, [verify])",
- "Good parameters: 192 bits (jwk, {alg: HS512, k: AQIDBAUGBwgJCgsMDQ4PEBESExQVFhcY, kty: oct}, {hash: SHA-512, name: HMAC}, false, [sign])",
- "Good parameters: 192 bits (jwk, {alg: HS512, k: AQIDBAUGBwgJCgsMDQ4PEBESExQVFhcY, kty: oct}, {hash: SHA-512, name: HMAC}, false, [verify, sign])",
- "Good parameters: 192 bits (jwk, {alg: HS512, k: AQIDBAUGBwgJCgsMDQ4PEBESExQVFhcY, kty: oct}, {hash: SHA-512, name: HMAC}, false, [verify])",
- "Good parameters: 256 bits (jwk, {alg: HS512, k: AQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyA, kty: oct}, {hash: SHA-512, name: HMAC}, false, [sign])",
- "Good parameters: 256 bits (jwk, {alg: HS512, k: AQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyA, kty: oct}, {hash: SHA-512, name: HMAC}, false, [verify, sign])",
- "Good parameters: 256 bits (jwk, {alg: HS512, k: AQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyA, kty: oct}, {hash: SHA-512, name: HMAC}, false, [verify])",
"Good parameters: 128 bits (raw, {0: 1, 1: 2, 10: 11, 11: 12, 12: 13, 13: 14, 14: 15, 15: 16, 2: 3, 3: 4, 4: 5, 5: 6, 6: 7, 7: 8, 8: 9, 9: 10}, {name: HKDF}, false, [deriveBits])",
"Good parameters: 128 bits (raw, {0: 1, 1: 2, 10: 11, 11: 12, 12: 13, 13: 14, 14: 15, 15: 16, 2: 3, 3: 4, 4: 5, 5: 6, 6: 7, 7: 8, 8: 9, 9: 10}, {name: HKDF}, false, [deriveKey, deriveBits])",
"Good parameters: 128 bits (raw, {0: 1, 1: 2, 10: 11, 11: 12, 12: 13, 13: 14, 14: 15, 15: 16, 2: 3, 3: 4, 4: 5, 5: 6, 6: 7, 7: 8, 8: 9, 9: 10}, {name: HKDF}, false, [deriveKey])",