Files
bun.sh/test/js/deno/crypto/webcrypto.test.ts
2024-07-10 17:18:39 -07:00

2651 lines
67 KiB
TypeScript

// GENERATED - DO NOT EDIT
// Copyright 2018+ the Deno authors. All rights reserved. MIT license.
// https://raw.githubusercontent.com/denoland/deno/main/cli/tests/unit/webcrypto_test.ts
import { createDenoTest } from "deno:harness";
const { test, assert, assertEquals, assertNotEquals, assertRejects } = createDenoTest(import.meta.path, 10_000);
test(async function testImportArrayBufferKey() {
const subtle = window.crypto.subtle;
assert(subtle);
const key = new Uint8Array([
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16
]);
const cryptoKey = await subtle.importKey("raw", key.buffer, {
name: "HMAC",
hash: "SHA-1"
}, true, [
"sign"
]);
assert(cryptoKey);
await subtle.sign({
name: "HMAC"
}, cryptoKey, new Uint8Array(8));
});
test(async function testSignVerify() {
const subtle = window.crypto.subtle;
assert(subtle);
for (const algorithm of [
"RSA-PSS",
"RSASSA-PKCS1-v1_5"
]){
for (const hash of [
"SHA-1",
"SHA-256",
"SHA-384",
"SHA-512"
]){
const keyPair = await subtle.generateKey({
name: algorithm,
modulusLength: 2048,
publicExponent: new Uint8Array([
1,
0,
1
]),
hash
}, true, [
"sign",
"verify"
]);
const data = new Uint8Array([
1,
2,
3
]);
const signAlgorithm = {
name: algorithm,
saltLength: 32
};
const signature = await subtle.sign(signAlgorithm, keyPair.privateKey, data);
assert(signature);
assert(signature.byteLength > 0);
assert(signature.byteLength % 8 == 0);
assert(signature instanceof ArrayBuffer);
const verified = await subtle.verify(signAlgorithm, keyPair.publicKey, signature, data);
assert(verified);
}
}
});
const plainText = new Uint8Array([
95,
77,
186,
79,
50,
12,
12,
232,
118,
114,
90,
252,
229,
251,
210,
91,
248,
62,
90,
113,
37,
160,
140,
175,
231,
60,
62,
186,
196,
33,
119,
157,
249,
213,
93,
24,
12,
58,
233,
148,
38,
69,
225,
216,
47,
238,
140,
157,
41,
75,
60,
177,
160,
138,
153,
49,
32,
27,
60,
14,
129,
252,
71,
202,
207,
131,
21,
162,
175,
102,
50,
65,
19,
195,
182,
98,
48,
195,
70,
8,
196,
244,
89,
54,
52,
206,
2,
178,
103,
54,
34,
119,
240,
168,
64,
202,
116,
188,
61,
26,
98,
54,
149,
44,
94,
215,
170,
248,
168,
254,
203,
221,
250,
117,
132,
230,
151,
140,
234,
93,
42,
91,
159,
183,
241,
180,
140,
139,
11,
229,
138,
48,
82,
2,
117,
77,
131,
118,
16,
115,
116,
121,
60,
240,
38,
170,
238,
83,
0,
114,
125,
131,
108,
215,
30,
113,
179,
69,
221,
178,
228,
68,
70,
255,
197,
185,
1,
99,
84,
19,
137,
13,
145,
14,
163,
128,
152,
74,
144,
25,
16,
49,
50,
63,
22,
219,
204,
157,
107,
225,
104,
184,
72,
133,
56,
76,
160,
62,
18,
96,
10,
193,
194,
72,
2,
138,
243,
114,
108,
201,
52,
99,
136,
46,
168,
192,
42,
171
]);
const hashPlainTextVector = [
{
hash: "SHA-1",
plainText: plainText.slice(0, 214)
},
{
hash: "SHA-256",
plainText: plainText.slice(0, 190)
},
{
hash: "SHA-384",
plainText: plainText.slice(0, 158)
},
{
hash: "SHA-512",
plainText: plainText.slice(0, 126)
}
];
test(async function testEncryptDecrypt() {
const subtle = window.crypto.subtle;
assert(subtle);
for (const { hash , plainText } of hashPlainTextVector){
const keyPair = await subtle.generateKey({
name: "RSA-OAEP",
modulusLength: 2048,
publicExponent: new Uint8Array([
1,
0,
1
]),
hash
}, true, [
"encrypt",
"decrypt"
]);
const encryptAlgorithm = {
name: "RSA-OAEP"
};
const cipherText = await subtle.encrypt(encryptAlgorithm, keyPair.publicKey, plainText);
assert(cipherText);
assert(cipherText.byteLength > 0);
assertEquals(cipherText.byteLength * 8, 2048);
assert(cipherText instanceof ArrayBuffer);
const decrypted = await subtle.decrypt(encryptAlgorithm, keyPair.privateKey, cipherText);
assert(decrypted);
assert(decrypted instanceof ArrayBuffer);
assertEquals(new Uint8Array(decrypted), plainText);
const badPlainText = new Uint8Array(plainText.byteLength + 1);
badPlainText.set(plainText, 0);
badPlainText.set(new Uint8Array([
32
]), plainText.byteLength);
await assertRejects(async ()=>{
await subtle.encrypt(encryptAlgorithm, keyPair.publicKey, badPlainText);
throw new TypeError("unreachable");
}, DOMException);
}
});
test(async function testGenerateRSAKey() {
const subtle = window.crypto.subtle;
assert(subtle);
const keyPair = await subtle.generateKey({
name: "RSA-PSS",
modulusLength: 2048,
publicExponent: new Uint8Array([
1,
0,
1
]),
hash: "SHA-256"
}, true, [
"sign",
"verify"
]);
assert(keyPair.privateKey);
assert(keyPair.publicKey);
assertEquals(keyPair.privateKey.extractable, true);
assert(keyPair.privateKey.usages.includes("sign"));
});
test(async function testGenerateHMACKey() {
const key = await window.crypto.subtle.generateKey({
name: "HMAC",
hash: "SHA-512"
}, true, [
"sign",
"verify"
]);
assert(key);
assertEquals(key.extractable, true);
assert(key.usages.includes("sign"));
});
test(async function testECDSASignVerify() {
const key = await window.crypto.subtle.generateKey({
name: "ECDSA",
namedCurve: "P-384"
}, true, [
"sign",
"verify"
]);
const encoder = new TextEncoder();
const encoded = encoder.encode("Hello, World!");
const signature = await window.crypto.subtle.sign({
name: "ECDSA",
hash: "SHA-384"
}, key.privateKey, encoded);
assert(signature);
assert(signature instanceof ArrayBuffer);
const verified = await window.crypto.subtle.verify({
hash: {
name: "SHA-384"
},
name: "ECDSA"
}, key.publicKey, signature, encoded);
assert(verified);
});
test(async function testECDSASignVerifyFail() {
const key = await window.crypto.subtle.generateKey({
name: "ECDSA",
namedCurve: "P-384"
}, true, [
"sign",
"verify"
]);
const encoded = new Uint8Array([
1
]);
await assertRejects(async ()=>{
await window.crypto.subtle.sign({
name: "ECDSA",
hash: "SHA-384"
}, key.publicKey, new Uint8Array([
1
]));
throw new TypeError("unreachable");
}, DOMException);
const signature = await window.crypto.subtle.sign({
name: "ECDSA",
hash: "SHA-384"
}, key.privateKey, encoded);
await assertRejects(async ()=>{
await window.crypto.subtle.verify({
hash: {
name: "SHA-384"
},
name: "ECDSA"
}, key.privateKey, signature, encoded);
throw new TypeError("unreachable");
}, DOMException);
});
test(async function testSignRSASSAKey() {
const subtle = window.crypto.subtle;
assert(subtle);
const keyPair = await subtle.generateKey({
name: "RSASSA-PKCS1-v1_5",
modulusLength: 2048,
publicExponent: new Uint8Array([
1,
0,
1
]),
hash: "SHA-256"
}, true, [
"sign",
"verify"
]);
assert(keyPair.privateKey);
assert(keyPair.publicKey);
assertEquals(keyPair.privateKey.extractable, true);
assert(keyPair.privateKey.usages.includes("sign"));
const encoder = new TextEncoder();
const encoded = encoder.encode("Hello, World!");
const signature = await window.crypto.subtle.sign({
name: "RSASSA-PKCS1-v1_5"
}, keyPair.privateKey, encoded);
assert(signature);
});
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",
k: "AQIDBAUGBwgJCgsMDQ4PEA",
alg: "HS256",
ext: true,
"key_ops": [
"sign"
]
};
test(async function subtleCryptoHmacImportExport() {
const key1 = await crypto.subtle.importKey("raw", rawKey, {
name: "HMAC",
hash: "SHA-256"
}, true, [
"sign"
]);
const key2 = await crypto.subtle.importKey("jwk", jwk, {
name: "HMAC",
hash: "SHA-256"
}, true, [
"sign"
]);
const actual1 = await crypto.subtle.sign({
name: "HMAC"
}, key1, new Uint8Array([
1,
2,
3,
4
]));
const actual2 = await crypto.subtle.sign({
name: "HMAC"
}, key2, new Uint8Array([
1,
2,
3,
4
]));
const expected = new Uint8Array([
59,
170,
255,
216,
51,
141,
51,
194,
213,
48,
41,
191,
184,
40,
216,
47,
130,
165,
203,
26,
163,
43,
38,
71,
23,
122,
222,
1,
146,
46,
182,
87
]);
assertEquals(new Uint8Array(actual1), expected);
assertEquals(new Uint8Array(actual2), expected);
const exportedKey1 = await crypto.subtle.exportKey("raw", key1);
assertEquals(new Uint8Array(exportedKey1), rawKey);
const exportedKey2 = await crypto.subtle.exportKey("jwk", key2);
assertEquals(exportedKey2, jwk);
});
test(async function generateImportHmacJwk() {
const key = await crypto.subtle.generateKey({
name: "HMAC",
hash: "SHA-512"
}, true, [
"sign"
]);
assert(key);
assertEquals(key.type, "secret");
assertEquals(key.extractable, true);
assertEquals(key.usages, [
"sign"
]);
const exportedKey = await crypto.subtle.exportKey("jwk", key);
assertEquals(exportedKey.kty, "oct");
assertEquals(exportedKey.alg, "HS512");
assertEquals(exportedKey.key_ops, [
"sign"
]);
assertEquals(exportedKey.ext, true);
assert(typeof exportedKey.k == "string");
assertEquals(exportedKey.k.length, 171);
});
const pkcs8TestVectors = [
{
pem: "id_rsaEncryption.pem",
hash: "SHA-256"
}
];
test({
permissions: {
read: true
}
}, async function importRsaPkcs8() {
const pemHeader = "-----BEGIN PRIVATE KEY-----";
const pemFooter = "-----END PRIVATE KEY-----";
for (const { pem , hash } of pkcs8TestVectors){
const keyFile = await Deno.readTextFile(pem);
const pemContents = keyFile.substring(pemHeader.length, keyFile.length - pemFooter.length);
const binaryDerString = atob(pemContents);
const binaryDer = new Uint8Array(binaryDerString.length);
for(let i = 0; i < binaryDerString.length; i++){
binaryDer[i] = binaryDerString.charCodeAt(i);
}
const key = await crypto.subtle.importKey("pkcs8", binaryDer, {
name: "RSA-PSS",
hash
}, true, [
"sign"
]);
assert(key);
assertEquals(key.type, "private");
assertEquals(key.extractable, true);
assertEquals(key.usages, [
"sign"
]);
const algorithm = key.algorithm as RsaHashedKeyAlgorithm;
assertEquals(algorithm.name, "RSA-PSS");
assertEquals(algorithm.hash.name, hash);
assertEquals(algorithm.modulusLength, 2048);
assertEquals(algorithm.publicExponent, new Uint8Array([
1,
0,
1
]));
}
});
const nonInteroperableVectors = [
{
pem: "id_rsassaPss.pem",
hash: "SHA-256"
},
{
pem: "id_rsassaPss_default.pem",
hash: "SHA-1"
},
{
pem: "id_rsassaPss_saltLen_30.pem",
hash: "SHA-1"
}
];
test({
permissions: {
read: true
}
}, async function importNonInteroperableRsaPkcs8() {
const pemHeader = "-----BEGIN PRIVATE KEY-----";
const pemFooter = "-----END PRIVATE KEY-----";
for (const { pem , hash } of nonInteroperableVectors){
const keyFile = await Deno.readTextFile(pem);
const pemContents = keyFile.substring(pemHeader.length, keyFile.length - pemFooter.length);
const binaryDerString = atob(pemContents);
const binaryDer = new Uint8Array(binaryDerString.length);
for(let i = 0; i < binaryDerString.length; i++){
binaryDer[i] = binaryDerString.charCodeAt(i);
}
await assertRejects(()=>crypto.subtle.importKey("pkcs8", binaryDer, {
name: "RSA-PSS",
hash
}, true, [
"sign"
]), DOMException, "unsupported algorithm");
}
});
const asn1AlgorithmIdentifier = new Uint8Array([
0x02,
0x01,
0x00,
0x30,
0x0d,
0x06,
0x09,
0x2a,
0x86,
0x48,
0x86,
0xf7,
0x0d,
0x01,
0x01,
0x01,
0x05,
0x00
]);
test(async function rsaExport() {
for (const algorithm of [
"RSASSA-PKCS1-v1_5",
"RSA-PSS",
"RSA-OAEP"
]){
const keyPair = await crypto.subtle.generateKey({
name: algorithm,
modulusLength: 2048,
publicExponent: new Uint8Array([
1,
0,
1
]),
hash: "SHA-256"
}, true, algorithm !== "RSA-OAEP" ? [
"sign",
"verify"
] : [
"encrypt",
"decrypt"
]);
assert(keyPair.privateKey);
assert(keyPair.publicKey);
assertEquals(keyPair.privateKey.extractable, true);
const exportedPrivateKey = await crypto.subtle.exportKey("pkcs8", keyPair.privateKey);
assert(exportedPrivateKey);
assert(exportedPrivateKey instanceof ArrayBuffer);
const pkcs8 = new Uint8Array(exportedPrivateKey);
assert(pkcs8.length > 0);
assertEquals(pkcs8.slice(4, asn1AlgorithmIdentifier.byteLength + 4), asn1AlgorithmIdentifier);
const exportedPublicKey = await crypto.subtle.exportKey("spki", keyPair.publicKey);
const spki = new Uint8Array(exportedPublicKey);
assert(spki.length > 0);
assertEquals(spki.slice(4, asn1AlgorithmIdentifier.byteLength + 1), asn1AlgorithmIdentifier.slice(3));
}
});
test(async function testHkdfDeriveBits() {
const rawKey = crypto.getRandomValues(new Uint8Array(16));
const key = await crypto.subtle.importKey("raw", rawKey, {
name: "HKDF",
hash: "SHA-256"
}, false, [
"deriveBits"
]);
const salt = crypto.getRandomValues(new Uint8Array(16));
const info = crypto.getRandomValues(new Uint8Array(16));
const result = await crypto.subtle.deriveBits({
name: "HKDF",
hash: "SHA-256",
salt: salt,
info: info
}, key, 128);
assertEquals(result.byteLength, 128 / 8);
});
test(async function testHkdfDeriveBitsWithLargeKeySize() {
const key = await crypto.subtle.importKey("raw", new Uint8Array([
0x00
]), "HKDF", false, [
"deriveBits"
]);
await assertRejects(()=>crypto.subtle.deriveBits({
name: "HKDF",
hash: "SHA-1",
salt: new Uint8Array(),
info: new Uint8Array()
}, key, ((20 * 255) << 3) + 8), DOMException, "The length provided for HKDF is too large");
});
test(async function testEcdhDeriveBitsWithShorterLength() {
const keypair = await crypto.subtle.generateKey({
name: "ECDH",
namedCurve: "P-384"
}, true, [
"deriveBits",
"deriveKey"
]);
const result = await crypto.subtle.deriveBits({
name: "ECDH",
public: keypair.publicKey
}, keypair.privateKey, 256);
assertEquals(result.byteLength * 8, 256);
});
test(async function testEcdhDeriveBitsWithLongerLength() {
const keypair = await crypto.subtle.generateKey({
name: "ECDH",
namedCurve: "P-384"
}, true, [
"deriveBits",
"deriveKey"
]);
await assertRejects(()=>crypto.subtle.deriveBits({
name: "ECDH",
public: keypair.publicKey
}, keypair.privateKey, 512), DOMException, "Invalid length");
});
test(async function testEcdhDeriveBitsWithNullLength() {
const keypair = await crypto.subtle.generateKey({
name: "ECDH",
namedCurve: "P-384"
}, true, [
"deriveBits",
"deriveKey"
]);
const result = await crypto.subtle.deriveBits({
name: "ECDH",
public: keypair.publicKey
}, keypair.privateKey, null);
assertEquals(result.byteLength * 8, 384);
});
test(async function testDeriveKey() {
const rawKey = crypto.getRandomValues(new Uint8Array(16));
const key = await crypto.subtle.importKey("raw", rawKey, "PBKDF2", false, [
"deriveKey",
"deriveBits"
]);
const salt = crypto.getRandomValues(new Uint8Array(16));
const derivedKey = await crypto.subtle.deriveKey({
name: "PBKDF2",
salt,
iterations: 1000,
hash: "SHA-256"
}, key, {
name: "HMAC",
hash: "SHA-256"
}, true, [
"sign"
]);
assert(derivedKey instanceof CryptoKey);
assertEquals(derivedKey.type, "secret");
assertEquals(derivedKey.extractable, true);
assertEquals(derivedKey.usages, [
"sign"
]);
const algorithm = derivedKey.algorithm as HmacKeyAlgorithm;
assertEquals(algorithm.name, "HMAC");
assertEquals(algorithm.hash.name, "SHA-256");
assertEquals(algorithm.length, 512);
});
test(async function testAesCbcEncryptDecrypt() {
const key = await crypto.subtle.generateKey({
name: "AES-CBC",
length: 128
}, true, [
"encrypt",
"decrypt"
]);
const iv = crypto.getRandomValues(new Uint8Array(16));
const encrypted = await crypto.subtle.encrypt({
name: "AES-CBC",
iv
}, key as CryptoKey, new Uint8Array([
1,
2,
3,
4,
5,
6
]));
assert(encrypted instanceof ArrayBuffer);
assertEquals(encrypted.byteLength, 16);
const decrypted = await crypto.subtle.decrypt({
name: "AES-CBC",
iv
}, key as CryptoKey, encrypted);
assert(decrypted instanceof ArrayBuffer);
assertEquals(decrypted.byteLength, 6);
assertEquals(new Uint8Array(decrypted), new Uint8Array([
1,
2,
3,
4,
5,
6
]));
});
test(async function testAesCtrEncryptDecrypt() {
async function aesCtrRoundTrip(key: CryptoKey, counter: Uint8Array, length: number, plainText: Uint8Array) {
const cipherText = await crypto.subtle.encrypt({
name: "AES-CTR",
counter,
length
}, key, plainText);
assert(cipherText instanceof ArrayBuffer);
assertEquals(cipherText.byteLength, plainText.byteLength);
assertNotEquals(new Uint8Array(cipherText), plainText);
const decryptedText = await crypto.subtle.decrypt({
name: "AES-CTR",
counter,
length
}, key, cipherText);
assert(decryptedText instanceof ArrayBuffer);
assertEquals(decryptedText.byteLength, plainText.byteLength);
assertEquals(new Uint8Array(decryptedText), plainText);
}
for (const keySize of [
128,
192,
256
]){
const key = await crypto.subtle.generateKey({
name: "AES-CTR",
length: keySize
}, true, [
"encrypt",
"decrypt"
]) as CryptoKey;
for (const length of [
128
]){
const counter = crypto.getRandomValues(new Uint8Array(16));
await aesCtrRoundTrip(key, counter, length, new Uint8Array([
1,
2,
3,
4,
5,
6
]));
}
for (const length of [
32,
64,
128
]){
const plaintext1 = crypto.getRandomValues(new Uint8Array(32));
const counter = new Uint8Array(16);
for(let off = 0; off < 16 - (length / 8); ++off){
counter[off] = off;
}
const ciphertext1 = await crypto.subtle.encrypt({
name: "AES-CTR",
counter,
length
}, key, plaintext1);
for(let off = 16 - (length / 8); off < 16; ++off){
counter[off] = 0xff;
}
const plaintext2 = new Uint8Array(48);
plaintext2.set(plaintext1, 16);
const ciphertext2 = await crypto.subtle.encrypt({
name: "AES-CTR",
counter,
length
}, key, plaintext2);
assertEquals(new Uint8Array(ciphertext1), new Uint8Array(ciphertext2).slice(16));
}
}
});
test(async function testECDH() {
for (const keySize of [
256,
384
]){
const keyPair = await crypto.subtle.generateKey({
name: "ECDH",
namedCurve: "P-" + keySize
}, true, [
"deriveBits"
]);
const derivedKey = await crypto.subtle.deriveBits({
name: "ECDH",
public: keyPair.publicKey
}, keyPair.privateKey, keySize);
assert(derivedKey instanceof ArrayBuffer);
assertEquals(derivedKey.byteLength, keySize / 8);
}
});
test(async function testWrapKey() {
const key = await crypto.subtle.generateKey({
name: "RSA-OAEP",
modulusLength: 4096,
publicExponent: new Uint8Array([
1,
0,
1
]),
hash: "SHA-256"
}, true, [
"wrapKey",
"unwrapKey"
]);
const hmacKey = await crypto.subtle.generateKey({
name: "HMAC",
hash: "SHA-256",
length: 128
}, true, [
"sign"
]);
const wrappedKey = await crypto.subtle.wrapKey("raw", hmacKey, key.publicKey, {
name: "RSA-OAEP",
label: new Uint8Array(8)
});
assert(wrappedKey instanceof ArrayBuffer);
assertEquals(wrappedKey.byteLength, 512);
},);
test.ignore(async function testAesKeyGen() {
const key = await crypto.subtle.generateKey({
name: "AES-GCM",
length: 256
}, true, [
"encrypt",
"decrypt"
]);
assert(key);
assertEquals(key.type, "secret");
assertEquals(key.extractable, true);
assertEquals(key.usages, [
"encrypt",
"decrypt"
]);
const algorithm = key.algorithm as AesKeyAlgorithm;
assertEquals(algorithm.name, "AES-GCM");
assertEquals(algorithm.length, 256);
});
test.ignore(async function testUnwrapKey() {
const subtle = crypto.subtle;
const AES_KEY: AesKeyAlgorithm & AesCbcParams = {
name: "AES-CBC",
length: 128,
iv: new Uint8Array(16)
};
const RSA_KEY: RsaHashedKeyGenParams & RsaOaepParams = {
name: "RSA-OAEP",
modulusLength: 2048,
publicExponent: new Uint8Array([
1,
0,
1
]),
hash: "SHA-1"
};
const aesKey = await subtle.generateKey(AES_KEY, true, [
"encrypt",
"decrypt"
]);
const rsaKeyPair = await subtle.generateKey({
name: "RSA-OAEP",
hash: "SHA-1",
publicExponent: new Uint8Array([
1,
0,
1
]),
modulusLength: 2048
}, false, [
"wrapKey",
"encrypt",
"unwrapKey",
"decrypt"
]);
const enc = await subtle.wrapKey("raw", aesKey, rsaKeyPair.publicKey, RSA_KEY);
const unwrappedKey = await subtle.unwrapKey("raw", enc, rsaKeyPair.privateKey, RSA_KEY, AES_KEY, false, [
"encrypt",
"decrypt"
]);
assert(unwrappedKey instanceof CryptoKey);
assertEquals(unwrappedKey.type, "secret");
assertEquals(unwrappedKey.extractable, false);
assertEquals(unwrappedKey.usages, [
"encrypt",
"decrypt"
]);
});
test(async function testDecryptWithInvalidIntializationVector() {
const data = new Uint8Array([
42,
42,
42,
42,
42,
42,
42,
42,
42,
42,
42,
42,
42,
42,
42
]);
const key = await crypto.subtle.importKey("raw", new Uint8Array(16), {
name: "AES-CBC",
length: 256
}, true, [
"encrypt",
"decrypt"
]);
const initVector = new Uint8Array([
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15
]);
const encrypted = await crypto.subtle.encrypt({
name: "AES-CBC",
iv: initVector
}, key, data);
const initVector2 = new Uint8Array([
15,
14,
13,
12,
11,
10,
9,
8,
7,
6,
5,
4,
3,
2,
1,
0
]);
await assertRejects(async ()=>{
await crypto.subtle.decrypt({
name: "AES-CBC",
iv: initVector2
}, key, encrypted);
}, DOMException);
});
const jwtRSAKeys = {
"1024": {
size: 1024,
publicJWK: {
kty: "RSA",
n: "zZn4sRGfjQos56yL_Qy1R9NI-THMnFynn94g5RxA6wGrJh4BJT3x6I9x0IbpS3q-d4ORA6R2vuDMh8dDFRr9RDH6XY-gUScc9U5Jz3UA2KmVfsCbnUPvcAmMV_ENA7_TF0ivVjuIFodyDTx7EKHNVTrHHSlrbt7spbmcivs23Zc",
e: "AQAB"
},
privateJWK: {
kty: "RSA",
n: "zZn4sRGfjQos56yL_Qy1R9NI-THMnFynn94g5RxA6wGrJh4BJT3x6I9x0IbpS3q-d4ORA6R2vuDMh8dDFRr9RDH6XY-gUScc9U5Jz3UA2KmVfsCbnUPvcAmMV_ENA7_TF0ivVjuIFodyDTx7EKHNVTrHHSlrbt7spbmcivs23Zc",
e: "AQAB",
d: "YqIK_GdH85F-GWZdgfgmv15NE78gOaL5h2g4v7DeM9-JC7A5PHSLKNYn87HFGcC4vv0PBIBRtyCA_mJJfEaGWORVCOXSBpWNepMYpio52n3w5uj5UZEsBnbtZc0EtWhVF2Auqa7VbiKrWcQUEgEI8V0gE5D4tyBg8GXv9975dQE",
p: "9BrAg5L1zfqGPuWJDuDCBX-TmtZdrOI3Ys4ZaN-yMPlTjwWSEPO0qnfjEZcw2VgXHgJJmbVco6TxckJCmEYqeQ",
q: "157jDJ1Ya5nmQvTPbhKAPAeMWogxCyaQTkBrp30pEKd6mGSB385hqr4BIk8s3f7MdXpM-USpaZgUoT4o_2VEjw",
dp: "qdd_QUzcaB-6jkKo1Ug-1xKIAgDLFsIjJUUfWt_iHL8ti2Kl2dOnTcCypgebPm5TT1bqHN-agGYAdK5zpX2UiQ",
dq: "hNRfwOSplNfhLvxLUN7a2qA3yYm-1MSz_1DWQP7srlLORlUcYPht2FZmsnEeDcAqynBGPQUcbG2Av_hgHz2OZw",
qi: "zbpJQAhinrxSbVKxBQ2EZGFUD2e3WCXbAJRYpk8HVQ5AA52OhKTicOye2hEHnrgpFKzC8iznTsCG3FMkvwcj4Q"
}
},
"2048": {
size: 2048,
publicJWK: {
kty: "RSA",
n: "09eVwAhT9SPBxdEN-74BBeEANGaVGwqH-YglIc4VV7jfhR2by5ivzVq8NCeQ1_ACDIlTDY8CTMQ5E1c1SEXmo_T7q84XUGXf8U9mx6uRg46sV7fF-hkwJR80BFVsvWxp4ahPlVJYj__94ft7rIVvchb5tyalOjrYFCJoFnSgq-i3ZjU06csI9XnO5klINucD_Qq0vUhO23_Add2HSYoRjab8YiJJR_Eths7Pq6HHd2RSXmwYp5foRnwe0_U75XmesHWDJlJUHYbwCZo0kP9G8g4QbucwU-MSNBkZOO2x2ZtZNexpHd0ThkATbnNlpVG_z2AGNORp_Ve3rlXwrGIXXw",
e: "AQAB"
},
privateJWK: {
kty: "RSA",
n: "09eVwAhT9SPBxdEN-74BBeEANGaVGwqH-YglIc4VV7jfhR2by5ivzVq8NCeQ1_ACDIlTDY8CTMQ5E1c1SEXmo_T7q84XUGXf8U9mx6uRg46sV7fF-hkwJR80BFVsvWxp4ahPlVJYj__94ft7rIVvchb5tyalOjrYFCJoFnSgq-i3ZjU06csI9XnO5klINucD_Qq0vUhO23_Add2HSYoRjab8YiJJR_Eths7Pq6HHd2RSXmwYp5foRnwe0_U75XmesHWDJlJUHYbwCZo0kP9G8g4QbucwU-MSNBkZOO2x2ZtZNexpHd0ThkATbnNlpVG_z2AGNORp_Ve3rlXwrGIXXw",
e: "AQAB",
d: "H4xboN2co0VP9kXL71G8lUOM5EDis8Q9u8uqu_4U75t4rjpamVeD1vFMVfgOehokM_m_hKVnkkcmuNqj9L90ObaiRFPM5QxG7YkFpXbHlPAKeoXD1hsqMF0VQg_2wb8DhberInHA_rEA_kaVhHvavQLu7Xez45gf1d_J4I4931vjlCB6cupbLL0H5hHsxbMsX_5nnmAJdL_U3gD-U7ZdQheUPhDBJR2KeGzvnTm3KVKpOnwn-1Cd45MU4-KDdP0FcBVEuBsSrsQHliTaciBgkbyj__BangPj3edDxTkb-fKkEvhkXRjAoJs1ixt8nfSGDce9cM_GqAX9XGb4s2QkAQ",
dp: "mM82RBwzGzi9LAqjGbi-badLtHRRBoH9sfMrJuOtzxRnmwBFccg_lwy-qAhUTqnN9kvD0H1FzXWzoFPFJbyi-AOmumYGpWm_PvzQGldne5CPJ02pYaeg-t1BePsT3OpIq0Am8E2Kjf9polpRJwIjO7Kx8UJKkhg5bISnsy0V8wE",
dq: "ZlM4AvrWIpXwqsH_5Q-6BsLJdbnN_GypFCXoT9VXniXncSBZIWCkgDndBdWkSzyzIN65NiMRBfZaf9yduTFj4kvOPwb3ch3J0OxGJk0Ary4OGSlS1zNwMl93ALGal1FzpWUuiia9L9RraGqXAUr13L7TIIMRobRjpAV-z7M-ruM",
p: "7VwGt_tJcAFQHrmDw5dM1EBru6fidM45NDv6VVOEbxKuD5Sh2EfAHfm5c6oouA1gZqwvKH0sn_XpB1NsyYyHEQd3sBVdK0zRjTo-E9mRP-1s-LMd5YDXVq6HE339nxpXsmO25slQEF6zBrj1bSNNXBFc7fgDnlq-HIeleMvsY_E",
q: "5HqMHLzb4IgXhUl4pLz7E4kjY8PH2YGzaQfK805zJMbOXzmlZK0hizKo34Qqd2nB9xos7QgzOYQrNfSWheARwVsSQzAE0vGvw3zHIPP_lTtChBlCTPctQcURjw4dXcnK1oQ-IT321FNOW3EO-YTsyGcypJqJujlZrLbxYjOjQE8",
qi: "OQXzi9gypDnpdHatIi0FaUGP8LSzfVH0AUugURJXs4BTJpvA9y4hcpBQLrcl7H_vq6kbGmvC49V-9I5HNVX_AuxGIXKuLZr5WOxPq8gLTqHV7X5ZJDtWIP_nq2NNgCQQyNNRrxebiWlwGK9GnX_unewT6jopI_oFhwp0Q13rBR0"
}
},
"4096": {
size: 4096,
publicJWK: {
kty: "RSA",
n: "2qr2TL2c2JmbsN0OLIRnaAB_ZKb1-Gh9H0qb4lrBuDaqkW_eFPwT-JIsvnNJvDT7BLJ57tTMIj56ZMtv6efSSTWSk9MOoW2J1K_iEretZ2cegB_aRX7qQVjnoFsz9U02BKfAIUT0o_K7b9G08d1rrAUohi_SVQhwObodg7BddMbKUmz70QNIS487LN44WUVnn9OgE9atTYUARNukT0DuQb3J-K20ksTuVujXbSelohDmLobqlGoi5sY_548Qs9BtFmQ2nGuEHNB2zdlZ5EvEqbUFVZ2QboG6jXdoos6qcwdgUvAhj1Hz10Ngic_RFqL7bNDoIOzNp66hdA35uxbwuaygZ16ikxoPj7eTYud1hrkyQCgeGw2YhCiKIE6eos_U5dL7WHRD5aSkkzsgXtnF8pVmStsuf0QcdAoC-eeCex0tSTgRw9AtGTz8Yr1tGQD9l_580zAXnE6jmrwRRQ68EEA7vohGov3tnG8pGyg_zcxeADLtPlfTc1tEwmh3SGrioDClioYCipm1JvkweEgP9eMPpEC8SgRU1VNDSVe1SF4uNsH8vA7PHFKfg6juqJEc5ht-l10FYER-Qq6bZXsU2oNcfE5SLDeLTWmxiHmxK00M8ABMFIV5gUkPoMiWcl87O6XwzA2chsIERp7Vb-Vn2O-EELiXzv7lPhc6fTGQ0Nc",
e: "AQAB"
},
privateJWK: {
kty: "RSA",
n: "2qr2TL2c2JmbsN0OLIRnaAB_ZKb1-Gh9H0qb4lrBuDaqkW_eFPwT-JIsvnNJvDT7BLJ57tTMIj56ZMtv6efSSTWSk9MOoW2J1K_iEretZ2cegB_aRX7qQVjnoFsz9U02BKfAIUT0o_K7b9G08d1rrAUohi_SVQhwObodg7BddMbKUmz70QNIS487LN44WUVnn9OgE9atTYUARNukT0DuQb3J-K20ksTuVujXbSelohDmLobqlGoi5sY_548Qs9BtFmQ2nGuEHNB2zdlZ5EvEqbUFVZ2QboG6jXdoos6qcwdgUvAhj1Hz10Ngic_RFqL7bNDoIOzNp66hdA35uxbwuaygZ16ikxoPj7eTYud1hrkyQCgeGw2YhCiKIE6eos_U5dL7WHRD5aSkkzsgXtnF8pVmStsuf0QcdAoC-eeCex0tSTgRw9AtGTz8Yr1tGQD9l_580zAXnE6jmrwRRQ68EEA7vohGov3tnG8pGyg_zcxeADLtPlfTc1tEwmh3SGrioDClioYCipm1JvkweEgP9eMPpEC8SgRU1VNDSVe1SF4uNsH8vA7PHFKfg6juqJEc5ht-l10FYER-Qq6bZXsU2oNcfE5SLDeLTWmxiHmxK00M8ABMFIV5gUkPoMiWcl87O6XwzA2chsIERp7Vb-Vn2O-EELiXzv7lPhc6fTGQ0Nc",
e: "AQAB",
d: "uXPRXBhcE5-DWabBRKQuhxgU8ype5gTISWefeYP7U96ZHqu_sBByZ5ihdgyU9pgAZGVx4Ep9rnVKnH2lNr2zrP9Qhyqy99nM0aMxmypIWLAuP__DwLj4t99M4sU29c48CAq1egHfccSFjzpNuetOTCA71EJuokt70pm0OmGzgTyvjuR7VTLxd5PMXitBowSn8_cphmnFpT8tkTiuy8CH0R3DU7MOuINomDD1s8-yPBcVAVTPUnwJiauNuzestLQKMLlhT5wn-cAbYk36XRKdgkjSc2AkhHRl4WDqT1nzWYdh_DVIYSLiKSktkPO9ovMrRYiPtozfhl0m9SR9Ll0wXtcnnDlWXc_MSGpw18vmUBSJ4PIhkiFsvLn-db3wUkA8uve-iqqfk0sxlGWughWx03kGmZDmprWbXugCBHfsI4X93w4exznXH_tapxPnmjbhVUQR6p41MvO2lcHWPLwGJgLIoejBHpnn3TmMN0UjFZki7q9B_dJ3fXh0mX9DzAlC0sil1NgCPhMPq02393_giinQquMknrBvgKxGSfGUrDKuflCx611ZZlRM3R7YMX2OIy1g4DyhPzBVjxRMtm8PnIs3m3Hi-O-C_PHF93w9J8Wqd0yIw7SpavDqZXLPC6Cqi8K7MBZyVECXHtRj1bBqT-h_xZmFCDjSU0NqfOdgApE",
p: "9NrXwq4kY9kBBOwLoFZVQc4kJI_NbKa_W9FLdQdRIbMsZZHXJ3XDUR9vJAcaaR75WwIC7X6N55nVtWTq28Bys9flJ9RrCTfciOntHEphBhYaL5ZTUl-6khYmsOf_psff2VaOOCvHGff5ejuOmBQxkw2E-cv7knRgWFHoLWpku2NJIMuGHt9ks7OAUfIZVYl9YJnw4FYUzhgaxemknjLeZ8XTkGW2zckzF-d95YI9i8zD80Umubsw-YxriSfqFQ0rGHBsbQ8ZOTd_KJju42BWnXIjNDYmjFUqdzVjI4XQ8EGrCEf_8_iwphGyXD7LOJ4fqd97B3bYpoRTPnCgY_SEHQ",
q: "5J758_NeKr1XPZiLxXohYQQnh0Lb4QtGZ1xzCgjhBQLcIBeTOG_tYjCues9tmLt93LpJfypSJ-SjDLwkR2s069_IByYGpxyeGtV-ulqYhSw1nD2CXKMDGyO5jXDs9tJrS_UhfobXKQH03CRdFugyPkSNmXY-AafFynG7xLr7oYBC05FnhUXPm3VBTPt9K-BpqwYd_h9vkAWeprSPo83UlwcLMupSJY9LaHxhRdz2yi0ZKNwXXHRwcszGjDBvvzUcCYbqWqjzbEvFY6KtH8Jh4LhM46rHaoEOTernJsDF6a6W8Df88RthqTExcwnaQf0O_dlbjSxEIPfbxx8t1EQugw",
dp: "4Y7Hu5tYAnLhMXuQqj9dgqU3PkcKYdCp7xc6f7Ah2P2JJHfYz4z4RD7Ez1eLyNKzulZ8A_PVHUjlSZiRkaYTBAEaJDrV70P6cFWuC6WpA0ZREQ1V7EgrQnANbGILa8QsPbYyhSQu4YlB1IwQq5_OmzyVBtgWA7AZIMMzMsMT0FuB_if-gWohBjmRN-vh0p45VUf6UW568-_YmgDFmMYbg1UFs7s_TwrNenPR0h7MO4CB8hP9vJLoZrooRczzIjljPbwy5bRG9CJfjTJ0vhj9MUT3kR1hHV1HJVGU5iBbfTfBKnvJGSI6-IDM4ZUm-B0R5hbs6s9cfOjhFmACIJIbMQ",
dq: "gT4iPbfyHyVEwWyQb4X4grjvg7bXSKSwG1SXMDAOzV9tg7LwJjKYNy8gJAtJgNNVdsfVLs-E_Epzpoph1AIWO9YZZXkov6Yc9zyEVONMX9S7ReU74hTBd8E9b2lMfMg9ogYk9jtSPTt-6kigW4fOh4cHqZ6_tP3cgfLD3JZ8FDPHE4WaySvLDq49yUBO5dQKyIU_xV6OGhQjOUjP_yEoMmzn9tOittsIHTxbXTxqQ6c1FvU9O6YTv8Jl5_Cl66khfX1I1RG38xvurcHULyUbYgeuZ_Iuo9XreT73h9_owo9RguGT29XH4vcNZmRGf5GIvRb4e5lvtleIZkwJA3u78w",
qi: "JHmVKb1zwW5iRR6RCeexYnh2fmY-3DrPSdM8Dxhr0F8dayi-tlRqEdnG0hvp45n8gLUskWWcB9EXlUJObZGKDfGuxgMa3g_xeLA2vmFQ12MxPsyH4iCNZvsgmGxx7TuOHrnDh5EBVnM4_de63crEJON2sYI8Ozi-xp2OEmAr2seWKq4sxkFni6exLhqb-NE4m9HMKlng1EtQh2rLBFG1VYD3SYYpMLc5fxzqGvSxn3Fa-Xgg-IZPY3ubrcm52KYgmLUGmnYStfVqGSWSdhDXHlNgI5pdAA0FzpyBk3ZX-JsxhwcnneKrYBBweq06kRMGWgvdbdAQ-7wSeGqqj5VPwA"
}
}
};
test(async function testImportRsaJwk() {
const subtle = window.crypto.subtle;
assert(subtle);
for (const [_key, jwkData] of Object.entries(jwtRSAKeys)){
const { size , publicJWK , privateJWK } = jwkData;
if (size < 2048) {
continue;
}
for (const hash of [
"SHA-1",
"SHA-256",
"SHA-384",
"SHA-512"
]){
const hashMapPSS: Record<string, string> = {
"SHA-1": "PS1",
"SHA-256": "PS256",
"SHA-384": "PS384",
"SHA-512": "PS512"
};
if (size == 1024 && hash == "SHA-512") {
continue;
}
const privateKeyPSS = await crypto.subtle.importKey("jwk", {
alg: hashMapPSS[hash],
...privateJWK,
ext: true,
"key_ops": [
"sign"
]
}, {
name: "RSA-PSS",
hash
}, true, [
"sign"
]);
const publicKeyPSS = await crypto.subtle.importKey("jwk", {
alg: hashMapPSS[hash],
...publicJWK,
ext: true,
"key_ops": [
"verify"
]
}, {
name: "RSA-PSS",
hash
}, true, [
"verify"
]);
const signaturePSS = await crypto.subtle.sign({
name: "RSA-PSS",
saltLength: 32
}, privateKeyPSS, new Uint8Array([
1,
2,
3,
4
]));
const verifyPSS = await crypto.subtle.verify({
name: "RSA-PSS",
saltLength: 32
}, publicKeyPSS, signaturePSS, new Uint8Array([
1,
2,
3,
4
]));
assert(verifyPSS);
}
for (const hash of [
"SHA-1",
"SHA-256",
"SHA-384",
"SHA-512"
]){
const hashMapPKCS1: Record<string, string> = {
"SHA-1": "RS1",
"SHA-256": "RS256",
"SHA-384": "RS384",
"SHA-512": "RS512"
};
if (size == 1024 && hash == "SHA-512") {
continue;
}
const privateKeyPKCS1 = await crypto.subtle.importKey("jwk", {
alg: hashMapPKCS1[hash],
...privateJWK,
ext: true,
"key_ops": [
"sign"
]
}, {
name: "RSASSA-PKCS1-v1_5",
hash
}, true, [
"sign"
]);
const publicKeyPKCS1 = await crypto.subtle.importKey("jwk", {
alg: hashMapPKCS1[hash],
...publicJWK,
ext: true,
"key_ops": [
"verify"
]
}, {
name: "RSASSA-PKCS1-v1_5",
hash
}, true, [
"verify"
]);
const signaturePKCS1 = await crypto.subtle.sign({
name: "RSASSA-PKCS1-v1_5",
saltLength: 32
}, privateKeyPKCS1, new Uint8Array([
1,
2,
3,
4
]));
const verifyPKCS1 = await crypto.subtle.verify({
name: "RSASSA-PKCS1-v1_5",
saltLength: 32
}, publicKeyPKCS1, signaturePKCS1, new Uint8Array([
1,
2,
3,
4
]));
assert(verifyPKCS1);
}
for (const { hash , plainText } of hashPlainTextVector){
const hashMapOAEP: Record<string, string> = {
"SHA-1": "RSA-OAEP",
"SHA-256": "RSA-OAEP-256",
"SHA-384": "RSA-OAEP-384",
"SHA-512": "RSA-OAEP-512"
};
if (size == 1024 && hash == "SHA-512") {
continue;
}
const encryptAlgorithm = {
name: "RSA-OAEP"
};
const privateKeyOAEP = await crypto.subtle.importKey("jwk", {
alg: hashMapOAEP[hash],
...privateJWK,
ext: true,
"key_ops": [
"decrypt"
]
}, {
...encryptAlgorithm,
hash
}, true, [
"decrypt"
]);
const publicKeyOAEP = await crypto.subtle.importKey("jwk", {
alg: hashMapOAEP[hash],
...publicJWK,
ext: true,
"key_ops": [
"encrypt"
]
}, {
...encryptAlgorithm,
hash
}, true, [
"encrypt"
]);
const cipherText = await subtle.encrypt(encryptAlgorithm, publicKeyOAEP, plainText);
assert(cipherText);
assert(cipherText.byteLength > 0);
assertEquals(cipherText.byteLength * 8, size);
assert(cipherText instanceof ArrayBuffer);
const decrypted = await subtle.decrypt(encryptAlgorithm, privateKeyOAEP, cipherText);
assert(decrypted);
assert(decrypted instanceof ArrayBuffer);
assertEquals(new Uint8Array(decrypted), plainText);
}
}
});
const jwtECKeys = {
"256": {
size: 256,
algo: "ES256",
publicJWK: {
kty: "EC",
crv: "P-256",
x: "0hCwpvnZ8BKGgFi0P6T0cQGFQ7ugDJJQ35JXwqyuXdE",
y: "zgN1UtSBRQzjm00QlXAbF1v6s0uObAmeGPHBmDWDYeg"
},
privateJWK: {
kty: "EC",
crv: "P-256",
x: "0hCwpvnZ8BKGgFi0P6T0cQGFQ7ugDJJQ35JXwqyuXdE",
y: "zgN1UtSBRQzjm00QlXAbF1v6s0uObAmeGPHBmDWDYeg",
d: "E9M6LVq_nPnrsh_4YNSu_m5W53eQ9N7ptAiE69M1ROo"
}
},
"384": {
size: 384,
algo: "ES384",
publicJWK: {
kty: "EC",
crv: "P-384",
x: "IZwU1mYXs27G2IVrOFtzp000T9iude8EZDXdpU47RL1fvevR0I3Wni19wdwhjLQ1",
y: "vSgTjMd4M3qEL2vWGyQOdCSfJGZ8KlgQp2v8KOAzX4imUB3sAZdtqFr7AIactqzo"
},
privateJWK: {
kty: "EC",
crv: "P-384",
x: "IZwU1mYXs27G2IVrOFtzp000T9iude8EZDXdpU47RL1fvevR0I3Wni19wdwhjLQ1",
y: "vSgTjMd4M3qEL2vWGyQOdCSfJGZ8KlgQp2v8KOAzX4imUB3sAZdtqFr7AIactqzo",
d: "RTe1mQeE08LSLpao-S-hqkku6HPldqQVguFEGDyYiNEOa560ztSyzEAS5KxeqEBz"
}
}
};
type JWK = Record<string, string>;
function equalJwk(expected: JWK, got: JWK): boolean {
const fields = Object.keys(expected);
for(let i = 0; i < fields.length; i++){
const fieldName = fields[i];
if (!(fieldName in got)) {
return false;
}
if (expected[fieldName] !== got[fieldName]) {
return false;
}
}
return true;
}
test(async function testImportExportEcDsaJwk() {
const subtle = crypto.subtle;
assert(subtle);
for (const [_key, keyData] of Object.entries(jwtECKeys)){
const { publicJWK , privateJWK , algo } = keyData;
const privateKeyECDSA = await subtle.importKey("jwk", {
alg: algo,
...privateJWK,
ext: true,
"key_ops": [
"sign"
]
}, {
name: "ECDSA",
namedCurve: privateJWK.crv
}, true, [
"sign"
]);
const expPrivateKeyJWK = await subtle.exportKey("jwk", privateKeyECDSA);
assert(equalJwk(privateJWK, expPrivateKeyJWK as JWK));
const publicKeyECDSA = await subtle.importKey("jwk", {
alg: algo,
...publicJWK,
ext: true,
"key_ops": [
"verify"
]
}, {
name: "ECDSA",
namedCurve: publicJWK.crv
}, true, [
"verify"
]);
const expPublicKeyJWK = await subtle.exportKey("jwk", publicKeyECDSA);
assert(equalJwk(publicJWK, expPublicKeyJWK as JWK));
const signatureECDSA = await subtle.sign({
name: "ECDSA",
hash: `SHA-${keyData.size}`
}, privateKeyECDSA, new Uint8Array([
1,
2,
3,
4
]));
const verifyECDSA = await subtle.verify({
name: "ECDSA",
hash: `SHA-${keyData.size}`
}, publicKeyECDSA, signatureECDSA, new Uint8Array([
1,
2,
3,
4
]));
assert(verifyECDSA);
}
});
test(async function testImportEcDhJwk() {
const subtle = crypto.subtle;
assert(subtle);
for (const [_key, jwkData] of Object.entries(jwtECKeys)){
const { size , publicJWK , privateJWK } = jwkData;
const privateKeyECDH = await subtle.importKey("jwk", {
...privateJWK,
ext: true,
"key_ops": [
"deriveBits"
]
}, {
name: "ECDH",
namedCurve: privateJWK.crv
}, true, [
"deriveBits"
]);
const expPrivateKeyJWK = await subtle.exportKey("jwk", privateKeyECDH);
assert(equalJwk(privateJWK, expPrivateKeyJWK as JWK));
const publicKeyECDH = await subtle.importKey("jwk", {
...publicJWK,
ext: true,
"key_ops": []
}, {
name: "ECDH",
namedCurve: publicJWK.crv
}, true, []);
const expPublicKeyJWK = await subtle.exportKey("jwk", publicKeyECDH);
assert(equalJwk(publicJWK, expPublicKeyJWK as JWK));
const derivedKey = await subtle.deriveBits({
name: "ECDH",
public: publicKeyECDH
}, privateKeyECDH, size);
assert(derivedKey instanceof ArrayBuffer);
assertEquals(derivedKey.byteLength, size / 8);
}
});
const ecTestKeys = [
{
size: 256,
namedCurve: "P-256",
signatureLength: 64,
raw: new Uint8Array([
4,
210,
16,
176,
166,
249,
217,
240,
18,
134,
128,
88,
180,
63,
164,
244,
113,
1,
133,
67,
187,
160,
12,
146,
80,
223,
146,
87,
194,
172,
174,
93,
209,
206,
3,
117,
82,
212,
129,
69,
12,
227,
155,
77,
16,
149,
112,
27,
23,
91,
250,
179,
75,
142,
108,
9,
158,
24,
241,
193,
152,
53,
131,
97,
232
]),
spki: new Uint8Array([
48,
89,
48,
19,
6,
7,
42,
134,
72,
206,
61,
2,
1,
6,
8,
42,
134,
72,
206,
61,
3,
1,
7,
3,
66,
0,
4,
210,
16,
176,
166,
249,
217,
240,
18,
134,
128,
88,
180,
63,
164,
244,
113,
1,
133,
67,
187,
160,
12,
146,
80,
223,
146,
87,
194,
172,
174,
93,
209,
206,
3,
117,
82,
212,
129,
69,
12,
227,
155,
77,
16,
149,
112,
27,
23,
91,
250,
179,
75,
142,
108,
9,
158,
24,
241,
193,
152,
53,
131,
97,
232
]),
pkcs8: new Uint8Array([
48,
129,
135,
2,
1,
0,
48,
19,
6,
7,
42,
134,
72,
206,
61,
2,
1,
6,
8,
42,
134,
72,
206,
61,
3,
1,
7,
4,
109,
48,
107,
2,
1,
1,
4,
32,
19,
211,
58,
45,
90,
191,
156,
249,
235,
178,
31,
248,
96,
212,
174,
254,
110,
86,
231,
119,
144,
244,
222,
233,
180,
8,
132,
235,
211,
53,
68,
234,
161,
68,
3,
66,
0,
4,
210,
16,
176,
166,
249,
217,
240,
18,
134,
128,
88,
180,
63,
164,
244,
113,
1,
133,
67,
187,
160,
12,
146,
80,
223,
146,
87,
194,
172,
174,
93,
209,
206,
3,
117,
82,
212,
129,
69,
12,
227,
155,
77,
16,
149,
112,
27,
23,
91,
250,
179,
75,
142,
108,
9,
158,
24,
241,
193,
152,
53,
131,
97,
232
])
},
{
size: 384,
namedCurve: "P-384",
signatureLength: 96,
raw: new Uint8Array([
4,
118,
64,
176,
165,
100,
177,
112,
49,
254,
58,
53,
158,
63,
73,
200,
148,
248,
242,
216,
186,
80,
92,
160,
53,
64,
232,
157,
19,
1,
12,
226,
115,
51,
42,
143,
98,
206,
55,
220,
108,
78,
24,
71,
157,
21,
120,
126,
104,
157,
86,
48,
226,
110,
96,
52,
48,
77,
170,
9,
231,
159,
26,
165,
200,
26,
164,
99,
46,
227,
169,
105,
172,
225,
60,
102,
141,
145,
139,
165,
47,
72,
53,
17,
17,
246,
161,
220,
26,
21,
23,
219,
1,
107,
185,
163,
215
]),
spki: new Uint8Array([
48,
118,
48,
16,
6,
7,
42,
134,
72,
206,
61,
2,
1,
6,
5,
43,
129,
4,
0,
34,
3,
98,
0,
4,
118,
64,
176,
165,
100,
177,
112,
49,
254,
58,
53,
158,
63,
73,
200,
148,
248,
242,
216,
186,
80,
92,
160,
53,
64,
232,
157,
19,
1,
12,
226,
115,
51,
42,
143,
98,
206,
55,
220,
108,
78,
24,
71,
157,
21,
120,
126,
104,
157,
86,
48,
226,
110,
96,
52,
48,
77,
170,
9,
231,
159,
26,
165,
200,
26,
164,
99,
46,
227,
169,
105,
172,
225,
60,
102,
141,
145,
139,
165,
47,
72,
53,
17,
17,
246,
161,
220,
26,
21,
23,
219,
1,
107,
185,
163,
215
]),
pkcs8: new Uint8Array([
48,
129,
182,
2,
1,
0,
48,
16,
6,
7,
42,
134,
72,
206,
61,
2,
1,
6,
5,
43,
129,
4,
0,
34,
4,
129,
158,
48,
129,
155,
2,
1,
1,
4,
48,
202,
7,
195,
169,
124,
170,
81,
169,
253,
127,
56,
28,
98,
90,
255,
165,
72,
142,
133,
138,
237,
200,
176,
92,
179,
192,
83,
28,
47,
118,
157,
152,
47,
65,
133,
140,
50,
83,
182,
191,
224,
96,
216,
179,
59,
150,
15,
233,
161,
100,
3,
98,
0,
4,
118,
64,
176,
165,
100,
177,
112,
49,
254,
58,
53,
158,
63,
73,
200,
148,
248,
242,
216,
186,
80,
92,
160,
53,
64,
232,
157,
19,
1,
12,
226,
115,
51,
42,
143,
98,
206,
55,
220,
108,
78,
24,
71,
157,
21,
120,
126,
104,
157,
86,
48,
226,
110,
96,
52,
48,
77,
170,
9,
231,
159,
26,
165,
200,
26,
164,
99,
46,
227,
169,
105,
172,
225,
60,
102,
141,
145,
139,
165,
47,
72,
53,
17,
17,
246,
161,
220,
26,
21,
23,
219,
1,
107,
185,
163,
215
])
}
];
test(async function testImportEcSpkiPkcs8() {
const subtle = window.crypto.subtle;
assert(subtle);
for (const { namedCurve , raw , spki , pkcs8 , signatureLength } of ecTestKeys){
const rawPublicKeyECDSA = await subtle.importKey("raw", raw, {
name: "ECDSA",
namedCurve
}, true, [
"verify"
]);
const expPublicKeyRaw = await subtle.exportKey("raw", rawPublicKeyECDSA);
assertEquals(new Uint8Array(expPublicKeyRaw), raw);
const privateKeyECDSA = await subtle.importKey("pkcs8", pkcs8, {
name: "ECDSA",
namedCurve
}, true, [
"sign"
]);
const expPrivateKeyPKCS8 = await subtle.exportKey("pkcs8", privateKeyECDSA);
assertEquals(new Uint8Array(expPrivateKeyPKCS8), pkcs8);
const expPrivateKeyJWK = await subtle.exportKey("jwk", privateKeyECDSA);
assertEquals(expPrivateKeyJWK.crv, namedCurve);
const publicKeyECDSA = await subtle.importKey("spki", spki, {
name: "ECDSA",
namedCurve
}, true, [
"verify"
]);
const expPublicKeySPKI = await subtle.exportKey("spki", publicKeyECDSA);
assertEquals(new Uint8Array(expPublicKeySPKI), spki);
const expPublicKeyJWK = await subtle.exportKey("jwk", publicKeyECDSA);
assertEquals(expPublicKeyJWK.crv, namedCurve);
for (const hash of [
"SHA-1",
"SHA-256",
"SHA-384",
"SHA-512"
]){
const signatureECDSA = await subtle.sign({
name: "ECDSA",
hash
}, privateKeyECDSA, new Uint8Array([
1,
2,
3,
4
]));
const verifyECDSA = await subtle.verify({
name: "ECDSA",
hash
}, publicKeyECDSA, signatureECDSA, new Uint8Array([
1,
2,
3,
4
]));
assert(verifyECDSA);
}
}
});
test(async function testAesGcmEncrypt() {
const key = await crypto.subtle.importKey("raw", new Uint8Array(16), {
name: "AES-GCM",
length: 256
}, true, [
"encrypt",
"decrypt"
]);
const nonces = [
{
iv: new Uint8Array([
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11
]),
ciphertext: new Uint8Array([
50,
223,
112,
178,
166,
156,
255,
110,
125,
138,
95,
141,
82,
47,
14,
164,
134,
247,
22
])
},
{
iv: new Uint8Array([
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15
]),
ciphertext: new Uint8Array([
210,
101,
81,
216,
151,
9,
192,
197,
62,
254,
28,
132,
89,
106,
40,
29,
175,
232,
201
])
}
];
for (const { iv , ciphertext: fixture } of nonces){
const data = new Uint8Array([
1,
2,
3
]);
const cipherText = await crypto.subtle.encrypt({
name: "AES-GCM",
iv
}, key, data);
assert(cipherText instanceof ArrayBuffer);
assertEquals(cipherText.byteLength, 19);
assertEquals(new Uint8Array(cipherText), fixture);
const plainText = await crypto.subtle.decrypt({
name: "AES-GCM",
iv
}, key, cipherText);
assert(plainText instanceof ArrayBuffer);
assertEquals(plainText.byteLength, 3);
assertEquals(new Uint8Array(plainText), data);
}
});
async function roundTripSecretJwk(jwk: JsonWebKey, algId: AlgorithmIdentifier | HmacImportParams, ops: KeyUsage[], validateKeys: (key: CryptoKey, originalJwk: JsonWebKey, exportedJwk: JsonWebKey) => void) {
const key = await crypto.subtle.importKey("jwk", jwk, algId, true, ops);
assert(key instanceof CryptoKey);
assertEquals(key.type, "secret");
const exportedKey = await crypto.subtle.exportKey("jwk", key);
validateKeys(key, jwk, exportedKey);
}
test(async function testSecretJwkBase64Url() {
const keyData = `{
"kty": "oct",
"k": "xxx",
"alg": "HS512",
"key_ops": ["sign", "verify"],
"ext": true
}`;
await roundTripSecretJwk(JSON.parse(keyData), {
name: "HMAC",
hash: "SHA-512"
}, [
"sign",
"verify"
], (key, _orig, exp)=>{
assertEquals((key.algorithm as HmacKeyAlgorithm).length, 16);
assertEquals(exp.k, "xxw");
});
await roundTripSecretJwk({
kty: "oct",
k: "HnZXRyDKn-_G5Fx4JWR1YA",
alg: "HS256",
"key_ops": [
"sign",
"verify"
],
ext: true
}, {
name: "HMAC",
hash: "SHA-256"
}, [
"sign",
"verify"
], (key, orig, exp)=>{
assertEquals((key.algorithm as HmacKeyAlgorithm).length, 128);
assertEquals(orig.k, exp.k);
});
await roundTripSecretJwk({
kty: "oct",
k: "a-_AlFa-2-OmEGa_-z==",
alg: "HS384",
"key_ops": [
"sign",
"verify"
],
ext: true
}, {
name: "HMAC",
hash: "SHA-384"
}, [
"sign",
"verify"
], (key, _orig, exp)=>{
assertEquals((key.algorithm as HmacKeyAlgorithm).length, 104);
assertEquals("a-_AlFa-2-OmEGa_-w", exp.k);
});
await roundTripSecretJwk({
kty: "oct",
k: "_u3K_gEjRWf-7cr-ASNFZw",
alg: "A128CBC",
"key_ops": [
"encrypt",
"decrypt"
],
ext: true
}, {
name: "AES-CBC"
}, [
"encrypt",
"decrypt"
], (_key, orig, exp)=>{
assertEquals(orig.k, exp.k);
});
await roundTripSecretJwk({
kty: "oct",
k: "_____________________w==",
alg: "A128CBC",
"key_ops": [
"encrypt",
"decrypt"
],
ext: true
}, {
name: "AES-CBC"
}, [
"encrypt",
"decrypt"
], (_key, _orig, exp)=>{
assertEquals(exp.k, "_____________________w");
});
});
test(async function testAESWrapKey() {
const key = await crypto.subtle.generateKey({
name: "AES-KW",
length: 128
}, true, [
"wrapKey",
"unwrapKey"
]);
const hmacKey = await crypto.subtle.generateKey({
name: "HMAC",
hash: "SHA-256",
length: 128
}, true, [
"sign"
]);
const wrappedKey = await crypto.subtle.wrapKey("raw", hmacKey, key, {
name: "AES-KW"
});
assert(wrappedKey instanceof ArrayBuffer);
assertEquals(wrappedKey.byteLength, 16 + 8);
const unwrappedKey = await crypto.subtle.unwrapKey("raw", wrappedKey, key, {
name: "AES-KW"
}, {
name: "HMAC",
hash: "SHA-256"
}, true, [
"sign"
]);
assert(unwrappedKey instanceof CryptoKey);
assertEquals((unwrappedKey.algorithm as HmacKeyAlgorithm).length, 128);
const hmacKeyBytes = await crypto.subtle.exportKey("raw", hmacKey);
const unwrappedKeyBytes = await crypto.subtle.exportKey("raw", unwrappedKey);
assertEquals(new Uint8Array(hmacKeyBytes), new Uint8Array(unwrappedKeyBytes));
});
test.ignore(async function testAesGcmTagLength() {
const key = await crypto.subtle.importKey("raw", new Uint8Array(32), "AES-GCM", false, [
"encrypt",
"decrypt"
]);
const iv = crypto.getRandomValues(new Uint8Array(12));
const encrypted = await crypto.subtle.encrypt({
name: "AES-GCM",
iv,
tagLength: 96
}, key, new Uint8Array(32));
await assertRejects(async ()=>{
await crypto.subtle.decrypt({
name: "AES-GCM",
iv,
tagLength: 96
}, key, encrypted);
});
});
test(async function ecPrivateKeyMaterialExportSpki() {
const keys = await crypto.subtle.generateKey({
name: "ECDSA",
namedCurve: "P-256"
}, true, [
"sign",
"verify"
]);
assert(keys.privateKey instanceof CryptoKey);
assert(keys.publicKey instanceof CryptoKey);
const spki = await crypto.subtle.exportKey("spki", keys.publicKey);
assert(spki instanceof ArrayBuffer);
});
test(async function importJwkWithUse() {
const jwk = {
"kty": "EC",
"use": "sig",
"crv": "P-256",
"x": "FWZ9rSkLt6Dx9E3pxLybhdM6xgR5obGsj5_pqmnz5J4",
"y": "_n8G69C-A2Xl4xUW2lF0i8ZGZnk_KPYrhv4GbTGu5G4"
};
const algorithm = {
name: "ECDSA",
namedCurve: "P-256"
};
const key = await crypto.subtle.importKey("jwk", jwk, algorithm, true, [
"verify"
]);
assert(key instanceof CryptoKey);
});
test(async function exportKeyNotExtractable() {
const key = await crypto.subtle.generateKey({
name: "HMAC",
hash: "SHA-512"
}, false, [
"sign",
"verify"
]);
assert(key);
assertEquals(key.extractable, false);
await assertRejects(async ()=>{
await crypto.subtle.exportKey("raw", key);
}, DOMException);
});
test(async function testImportLeadingZeroesKey() {
const alg = {
name: "ECDSA",
namedCurve: "P-256"
};
const jwk = {
kty: "EC",
crv: "P-256",
alg: "ES256",
x: "EvidcdFB1xC6tgfakqZsU9aIURxAJkcX62zHe1Nt6xU",
y: "AHsk6BioGM7MZWeXOE_49AGmtuaXFT3Ill3DYtz9uYg",
d: "WDeYo4o1heCF9l_2VIaClRyIeO16zsMlN8UG6Le9dU8",
"key_ops": [
"sign"
],
ext: true
};
const key = await crypto.subtle.importKey("jwk", jwk, alg, true, [
"sign"
]);
assert(key instanceof CryptoKey);
assertEquals(key.type, "private");
});
test(async function testECspkiRoundTrip() {
const alg = {
name: "ECDH",
namedCurve: "P-256"
};
const { publicKey } = await crypto.subtle.generateKey(alg, true, [
"deriveBits"
]);
const spki = await crypto.subtle.exportKey("spki", publicKey);
await crypto.subtle.importKey("spki", spki, alg, true, []);
});
test(async function testHmacJwkImport() {
await crypto.subtle.importKey("jwk", {
kty: "oct",
use: "sig",
alg: "HS256",
k: "hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg"
}, {
name: "HMAC",
hash: "SHA-256"
}, false, [
"sign",
"verify"
]);
});