| <script> | |
| function log(msg) | |
| { | |
| // alert(msg); | |
| window.webkit.messageHandlers.testHandler.postMessage(msg); | |
| } | |
| function shouldBeType(_a, _type) | |
| { | |
| let exception; | |
| let _av; | |
| try { | |
| _av = eval(_a); | |
| } catch (e) { | |
| exception = e; | |
| } | |
| const _typev = eval(_type); | |
| if (_av instanceof _typev) { | |
| return true; | |
| } else { | |
| return false; | |
| } | |
| } | |
| function shouldBeCryptoKey(_a, _key) | |
| { | |
| if (!(JSON.stringify(_key.key_ops) == JSON.stringify(_a.key_ops)) || !(_key.ext == _a.ext)) | |
| return false; | |
| if (_key.kty == "oct" && _a.kty == "oct") | |
| return (_key.k == _a.k) && (_key.alg == _a.alg); | |
| if (_key.kty == "EC" && _a.kty == "EC") | |
| return (_key.k == _a.k) && (_key.crv == _a.crv) && (_key.x == _a.x) && (_key.y == _a.y) && (_key.d == _a.d); | |
| if (_key.kty == "RSA" && _a.kty == "RSA") | |
| return (_key.alg == _a.alg) && (_key.n == _a.n) && (_key.e == _a.e) && (_key.d == _a.d) && (_key.p == _a.p) && (_key.q == _a.q) && (_key.dp == _a.dp) && (_key.dq == _a.dq) && (_key.qi == _a.qi); | |
| return false; | |
| } | |
| // Values that are used to infer the type of a given primitive type. | |
| const testValues = [ | |
| undefined, | |
| null, | |
| 5, // Int | |
| 0, | |
| 1, | |
| false, | |
| true, | |
| 5.5, // Double | |
| "Hello,World!", | |
| "" | |
| ]; | |
| // Values that are used to infer the type of a given Boolean/String type. | |
| const testTypeValues = [ | |
| { item: new Boolean(true), type: "Boolean", value: true }, | |
| { item: new Boolean(false), type: "Boolean", value: false }, | |
| { item: new String(), type: "String", value: "" } | |
| ]; | |
| // Values are in JWK formats. | |
| const testCryptoKeyValues = [ | |
| { kty: "oct", k: "YWJjZGVmZ2gxMjM0NTY3OA", alg: "A128CBC", use: "enc", key_ops: ['decrypt', 'encrypt', 'unwrapKey', 'wrapKey'], ext: true }, | |
| { kty: "EC", use: "sig", ext: true, crv: "P-256", x: "1FSVWieTvikFkG1NOyhkUCaMbdQhxwH6aCu4Ez-sRtA", y: "9jmNTLqM4cjBhdAnHcNI9YQV3O8LFmo-EdZWk8ntAaI", key_ops: ['verify'] }, | |
| { kty: "EC", use: "sig", ext: true, key_ops: ["sign"], crv: "P-256", x: "1FSVWieTvikFkG1NOyhkUCaMbdQhxwH6aCu4Ez-sRtA", y: "9jmNTLqM4cjBhdAnHcNI9YQV3O8LFmo-EdZWk8ntAaI", d: "ppxBSov3N8_AUcisAuvmLV4yE8e_L_BLE8bZb9Z1Xjg" }, | |
| { kty: "oct", k: "YWJjZGVmZ2gxMjM0NTY3OA", alg: "HS256", use: "sig", key_ops: ["sign", "verify"], ext: true, }, | |
| { kty: "RSA", alg: "PS256", use: "sig", key_ops: ["verify"], ext: true, n: "rcCUCv7Oc1HVam1DIhCzqknThWawOp8QLk8Ziy2p10ByjQFCajoFiyuAWl-R1WXZaf4xitLRracT9agpzIzc-MbLSHIGgWQGO21lGiImy5ftZ-D8bHAqRz2y15pzD4c4CEou7XSSLDoRnR0QG5MsDhD6s2gV9mwHkrtkCxtMWdBi-77as8wGmlNRldcOSgZDLK8UnCSgA1OguZ989bFyc8tOOEIb0xUSfPSz3LPSCnyYz68aDjmKVeNH-ig857OScyWbGyEy3Biw64qun3juUlNWsJ3zngkOdteYWytx5Qr4XKNs6R-Myyq72KUp02mJDZiiyiglxML_i3-_CeecCw", e: "AQAB" }, | |
| { kty: "RSA", alg: "PS1", use: "sig", key_ops: ["sign"], ext: true, n: "rcCUCv7Oc1HVam1DIhCzqknThWawOp8QLk8Ziy2p10ByjQFCajoFiyuAWl-R1WXZaf4xitLRracT9agpzIzc-MbLSHIGgWQGO21lGiImy5ftZ-D8bHAqRz2y15pzD4c4CEou7XSSLDoRnR0QG5MsDhD6s2gV9mwHkrtkCxtMWdBi-77as8wGmlNRldcOSgZDLK8UnCSgA1OguZ989bFyc8tOOEIb0xUSfPSz3LPSCnyYz68aDjmKVeNH-ig857OScyWbGyEy3Biw64qun3juUlNWsJ3zngkOdteYWytx5Qr4XKNs6R-Myyq72KUp02mJDZiiyiglxML_i3-_CeecCw", e: "AQAB", d: "IfKI2jcblgtsk50iazLxL3m6IovELcfrLOkWeAtlbiFw4BzZYZDRiXXn34BhqVx7ZIqeGl4DSpbkvu_qrsTs3YxVpmNk4v3se8rtYHRAuTGHWG7sUrF9iSrEWPybs3J7X59C2PEYKBz2WQiXH5QbHOpzWP8in2FO_3uuWb8tJTTPqyi1z3n04uNWBGjnYfUAeMOXyq4GSzAcCO55FEZeFQpuNIQBAaC1NCqPps4ps2AEoMBC6Kdg0AI8xwPGysq_qnFbNlkCqopRKiu3bs5pnOzOHpQCb8fYNeIih4qPsKaPQ0lHwFAAo-AsE_7niiPSJQwzB-dcoVfSVXss6TZ49Q", p: "4miTuAjKMeH5uJ5KB397QUwhbkYEgSbcA2mifmSkvE2018gb55qkBHK1eVryf1_m43LNlc6O_ak6gfzdZIZvS5NCGjPl0q09plUpu8qFOSspBwA67qGH76lFlZLn_d4yglS7wfLru4_5Ys8qLLs-DqVLviwposOnyyWqwM5AXp0", q: "xHYrzkivtmnz_sGchnWGc0q-pDOkKicptRpv2pMFIIXxnFX5aMeEXIZjVujXtwUy1UlFIN2GZJSvy5KJ79mu_XyNnFHMzedH-A3ee3u8h1UUrZF-vUu1_e4U_x67NN1dedzUSKynN7pFl3OkuShMBWGV-cwzOPdcVAfVuZlxUMc", dp: "fBzDzYDUBmBQGop7Hn0dvf_T27V6RqpctWo074CQZcFbP2atFVtKSj3viWT3xid2VHzcgiDHdfpM3nEVlEO1wwIonGCSvdjGEOZiiFVOjrZAOVxA8guOjyyFvqbXke06VwPIIVvfKeSU2zuhbP__1tt6F_fxow4Kb2xonGT0GGk", dq: "jmE2DiIPdhwDgLXAQpIaBqQ81bO3XfVT_LRULAwwwwlPuQV148H04zlh9TJ6Y2GZHYokV1U0eOBpJxfkb7dLYtpJpuiBjRf4yIUEoGlkkI_QlJnFSFr-YjGRdfNHqWBkxlSMZL770R9mIATndGkH7z5x-r9KwBZFC4FCG2hg_zE", qi: "YCX_pLwbMBA1ThVH0WcwmnytqNcrMCEwTm7ByA2eU6nWbQrULvf7m9_kzfLUcjsnpAVlBQG5JMXMy0Sq4ptwbywsa5-G8KAOOOR2L3v4hC-Eys9ftgFM_3i0o40eeQH4b3haPbntrIeMg8IzlOuVYKf9-2QuKDoWeRdd7NsdxTk" } | |
| ]; | |
| indexedDB.deleteDatabase("backward_compatibility"); | |
| const openRequest = indexedDB.open("backward_compatibility"); | |
| openRequest.onupgradeneeded = function(event) { | |
| // This objectStore stores elements that we only need to check its type to determine its serialization tag. | |
| event.target.result.createObjectStore("type"); | |
| // This objectStore stores elements that we need to check its value to determine its serialization tag. | |
| event.target.result.createObjectStore("value", { autoIncrement : true }); | |
| // This objectStore stores elements that we need to check both its type and its value to determine its serialization tag. | |
| event.target.result.createObjectStore("typeValue", { autoIncrement : true }); | |
| // This objectStore stores crypto keys. | |
| event.target.result.createObjectStore("cryptoKey", { autoIncrement : true }); | |
| }; | |
| openRequest.onerror = function(event) { | |
| log("Error: " + event.target.error.name); | |
| }; | |
| openRequest.onsuccess = function(event) { | |
| db = event.target.result; | |
| storeType(); | |
| }; | |
| function storeType() | |
| { | |
| const transaction = db.transaction("type", "readwrite"); | |
| transaction.onerror = function(event) { | |
| log("Error: " + event.target.error.name); | |
| }; | |
| transaction.oncomplete = function(event) { storeValue(); }; | |
| const objectStore = transaction.objectStore("type"); | |
| // The key of an item is the type of its value, hence we could determine type mismatch, i.e. serialization tag reorder | |
| // simply by using an item's key and value. | |
| objectStore.add([1, 2], "Array"); | |
| objectStore.add({ property: null }, "Object"); | |
| objectStore.add(new Date(), "Date"); | |
| objectStore.add(new File(["foo"], "foo.txt"), "File"); | |
| objectStore.add(new ImageData(100, 100), "ImageData"); | |
| objectStore.add(new Blob(["foo"]), "Blob"); | |
| objectStore.add(new RegExp(''), "RegExp"); | |
| objectStore.add(new ArrayBuffer(1), "ArrayBuffer"); | |
| objectStore.add(new String(''), "String"); | |
| objectStore.add(new Number(12), "Number"); | |
| objectStore.add(new Set([1, 2]), "Set"); | |
| objectStore.add(new Map(), "Map"); | |
| // Typed arrays | |
| objectStore.add(new Int8Array(), "Int8Array"); | |
| objectStore.add(new Uint8Array(), "Uint8Array"); | |
| objectStore.add(new Uint8ClampedArray(), "Uint8ClampedArray"); | |
| objectStore.add(new Int16Array(), "Int16Array"); | |
| objectStore.add(new Uint16Array(), "Uint16Array"); | |
| objectStore.add(new Int32Array(), "Int32Array"); | |
| objectStore.add(new Uint32Array(), "Uint32Array"); | |
| objectStore.add(new Float32Array(), "Float32Array"); | |
| objectStore.add(new Float64Array(), "Float64Array"); | |
| } | |
| function storeValue() | |
| { | |
| const transaction = db.transaction("value", "readwrite"); | |
| transaction.onerror = function(event) { | |
| log("Error: " + event.target.error.name); | |
| }; | |
| transaction.oncomplete = function(event) { storeTypeValue(); }; | |
| const objectStore = transaction.objectStore("value"); | |
| // Here we store values in the test matrix in order and read them in order to see if any mismatch happens. | |
| for (let i = 0; i < testValues.length; i++) | |
| objectStore.add(testValues[i]); | |
| } | |
| function storeTypeValue() | |
| { | |
| const transaction = db.transaction("typeValue", "readwrite"); | |
| transaction.onerror = function(event) { | |
| log("Error: " + event.target.error.name); | |
| }; | |
| transaction.oncomplete = function(event) { storeCryptoKey(); }; | |
| const objectStore = transaction.objectStore("typeValue"); | |
| // Here we store items in the test matrix in order and read them in order to see if any mismatches its type and value. | |
| for (let i = 0; i < testTypeValues.length; i++) | |
| objectStore.add(testTypeValues[i].item); | |
| } | |
| async function storeCryptoKey() | |
| { | |
| const transaction = db.transaction("cryptoKey", "readwrite"); | |
| transaction.onerror = function(event) { | |
| log("Error: " + event.target.error.name); | |
| }; | |
| transaction.oncomplete = function(event) { readType(); }; | |
| const objectStore = transaction.objectStore("cryptoKey"); | |
| // Here we store items in the test matrix in order and read them in order to see if any mismatches its type and value. | |
| let cryptoKey = await crypto.subtle.importKey("jwk", testCryptoKeyValues[0], "aes-cbc", true, ["encrypt", "decrypt", "wrapKey", "unwrapKey"]); | |
| objectStore.add(cryptoKey); | |
| cryptoKey = await crypto.subtle.importKey("jwk", testCryptoKeyValues[1], { name: "ECDSA", namedCurve: "P-256" }, true, ['verify']); | |
| objectStore.add(cryptoKey); | |
| cryptoKey = await crypto.subtle.importKey("jwk", testCryptoKeyValues[2], { name: "ECDSA", namedCurve: "P-256" }, true, ["sign"]); | |
| objectStore.add(cryptoKey); | |
| cryptoKey = await crypto.subtle.importKey("jwk", testCryptoKeyValues[3], {name: "hmac", hash: "sha-256"}, true, ["sign", "verify"]); | |
| objectStore.add(cryptoKey); | |
| cryptoKey = await crypto.subtle.importKey("jwk", testCryptoKeyValues[4], {name: "RSA-PSS", hash: "SHA-256"}, true, ["verify"]); | |
| objectStore.add(cryptoKey); | |
| cryptoKey = await crypto.subtle.importKey("jwk", testCryptoKeyValues[5], {name: "RSA-PSS", hash: "SHA-1"}, true, ["sign"]); | |
| objectStore.add(cryptoKey); | |
| } | |
| let result = true; | |
| function readType() | |
| { | |
| const objectStore = db.transaction("type").objectStore("type"); | |
| objectStore.openCursor().onsuccess = function(event) { | |
| cursor = event.target.result; | |
| if (cursor) { | |
| result = result && shouldBeType("cursor.value", cursor.key); | |
| cursor.continue(); | |
| } else | |
| readValue(); | |
| }; | |
| } | |
| function readValue() | |
| { | |
| const objectStore = db.transaction("value").objectStore("value"); | |
| objectStore.openCursor().onsuccess = function(event) { | |
| cursor = event.target.result; | |
| if (cursor) { | |
| result = result && (cursor.value === testValues[cursor.key - 1]); | |
| cursor.continue(); | |
| } else | |
| readTypeValue(); | |
| }; | |
| } | |
| function readTypeValue() | |
| { | |
| const objectStore = db.transaction("typeValue").objectStore("typeValue"); | |
| objectStore.openCursor().onsuccess = function(event) { | |
| cursor = event.target.result; | |
| if (cursor) { | |
| result = result && shouldBeType("cursor.value", testTypeValues[cursor.key - 1].type) && (cursor.value == testTypeValues[cursor.key - 1].value); | |
| cursor.continue(); | |
| } else { | |
| readCryptoKey(); | |
| } | |
| }; | |
| } | |
| async function readCryptoKey() | |
| { | |
| const objectStore = db.transaction("cryptoKey").objectStore("cryptoKey"); | |
| objectStore.openCursor().onsuccess = async function(event) { | |
| cursor = event.target.result; | |
| if (cursor) { | |
| const jwkKey = await crypto.subtle.exportKey("jwk", cursor.value); | |
| result = result && shouldBeCryptoKey(jwkKey, testCryptoKeyValues[cursor.key - 1]); | |
| cursor.continue(); | |
| } else { | |
| if (result) | |
| log("Pass"); | |
| else | |
| log("Fail"); | |
| } | |
| }; | |
| } | |
| </script> |