<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <title>SFrame test vector</title>
        <script src="../resources/testharness.js"></script>
        <script src="../resources/testharnessreport.js"></script>
    </head>
    <body>
<script>
// Tests taken from https://github.com/eomara/sframe/blob/master/test-vectors.json and updated to cope with key/counter length difference.
// Test suite 1 is AES_CM_128_HMAC_SHA256_4, test suite 2 is AES_CM_128_HMAC_SHA256_8.
// Test suites 3 and 4 are GCM and not yet added here since not supported.
const tests = [
  {
    "cipher_suite": 1,
    "base_key": "101112131415161718191a1b1c1d1e1f",
    "key": "343d3290f5c0b936415bea9a43c6f5a2",
    "salt": "42d662fbad5cd81eb3aad79a",
    "plaintext": "46726f6d2068656176656e6c79206861726d6f6e79202f2f205468697320756e6976657273616c206672616d6520626567616e",
    "encryptions": [
      {
        "kid": "7",
        "ctr": "0",
        "header": "0700",
        "nonce": "42d662fbad5cd81eb3aad79a",
        "ciphertext": "070065c67c6fb784631a7db1b589ffb62d75b78e28b0899e632fbbee3b944747a6382d75b6bd3788dc7b71b9295c7fb90b5098f7ad92befabb"
      },
      {
        "kid": "7",
        "ctr": "1",
        "header": "0701",
        "nonce": "42d662fbad5cd81eb3aad79b",
        "ciphertext": "0701ec742e98d667be810f153ff0d4dad7969f69b310aa7c6b9cb911e83af09b0f0a6d74772d8195c8c9dae3878fd1cb10edb4176dd4a56026"
      },
      {
        "kid": "7",
        "ctr": "2",
        "header": "0702",
        "nonce": "42d662fbad5cd81eb3aad798",
        "ciphertext": "0702ac9b495d37a1e48c712ade5cba72df0bf90f24aa022a454cfb92d8b87cd54335fb6b9eeded6a5aa4e2643d7a09946646001d0afd6a2d5f"
      },
      {
        "kid": "15",
        "ctr": "170",
        "header": "080faa",
        "nonce": "42d662fbad5cd81eb3aad730",
        "ciphertext": "080faaeaa5adc70cae0d6ebd36805fa87d2351dd02c55c751cd351a7fdb7f0927b474eae3e800033e08100a440002da17579678b36dcafb808b3"
      },
      {
        "kid": "511",
        "ctr": "170",
        "header": "0901ffaa",
        "nonce": "42d662fbad5cd81eb3aad730",
        "ciphertext": "0901ffaaeaa5adc70cae0d6ebd36805fa87d2351dd02c55c751cd351a7fdb7f0927b474eae3e800033e08100a440002da17579678b36dcc0a409c5"
      },
      {
        "kid": "511",
        "ctr": "543690",
        "header": "2901ff084bca",
        "nonce": "42d662fbad5cd81eb3a29c50",
        "ciphertext": "2901ff084bca99362198463c4739ebe78d4c1158fc7b7e118e8d035130a583744af343a370ab964a76d31aba68939336187bd251a7f63e9ec25a2bed97"
      },
      {
        "kid": "72057594037927935",
        "ctr": "72057594037927935",
        "header": "6effffffffffffffffffffffffffff",
        "nonce": "42d662fbada327e14c552865",
        "ciphertext": "6effffffffffffffffffffffffffffdca3655d5117bc838d6f4382ca468a4f992ff77bfd1d2f4391be6b33e8fb638dc48aa82f57fd91430c714def0b2089c8bfb2ac6a36e584"
      },
      {
        "kid": "92057594037927935",
        "ctr": "8",
        "header": "0f01470de4df81ffff08",
        "nonce": "42d662fbad5cd81eb3aad792",
        "ciphertext": "0f01470de4df81ffff084db66f48555956af80a014f06670d7771071afc72ad7452fbb835d353d07d16e771b2ca3593a6a87b643e39370b404a2eb898de8114fb9"
      }
    ]
  },
  {
    "cipher_suite": 2,
    "base_key": "202122232425262728292a2b2c2d2e2f",
    "key": "3fce747d505e46ec9b92d9f58ee7a5d4",
    "salt": "77fbf5f1d82c73f6d2b353c9",
    "plaintext": "46726f6d2068656176656e6c79206861726d6f6e79202f2f205468697320756e6976657273616c206672616d6520626567616e",
    "encryptions": [
      {
        "kid": "7",
        "ctr": "0",
        "header": "0700",
        "nonce": "77fbf5f1d82c73f6d2b353c900000000",
        "ciphertext": "0700647513fce71aab7fed1e904fd9240343d77092c831f0d58fde0985a0f3e5ba4020e87a7b9c870b5f8f7f628d27690cc1e571e4e8b90ec99c582cfa"
      },
      {
        "kid": "7",
        "ctr": "1",
        "header": "0701",
        "nonce": "77fbf5f1d82c73f6d2b353c8",
        "ciphertext": "07019e1bdf713b0d4c02f3dbf50a72ea773286e7da38f3872cc734f3e1b1448aab5009b424e05495214f96d02e4e8f8da975cc808fe931237a6ab700e8"
      },
      {
        "kid": "7",
        "ctr": "2",
        "header": "0702",
        "nonce": "77fbf5f1d82c73f6d2b353cb",
        "ciphertext": "070220ad36fd9191453ace2d36a175ad8a69c1f16b8613d14b4f7ef30c68bc5609e349df38155cc1544d7dbfa079e3faae3c7883b479fccb144b0c78a5"
      },
      {
        "kid": "15",
        "ctr": "170",
        "header": "080faa",
        "nonce": "77fbf5f1d82c73f6d2b35363",
        "ciphertext": "080faadab9b284a4b9e3aea36b9cdcae4a58e141d3f0f52f240ef80a93dbb8d809ede01b05b2cace18a22fb39c032724481c5baa181d081e260067ccd683"
      },
      {
        "kid": "511",
        "ctr": "170",
        "header": "0901ffaa",
        "nonce": "77fbf5f1d82c73f6d2b35363",
        "ciphertext": "0901ffaadab9b284a4b9e3aea36b9cdcae4a58e141d3f0f52f240ef80a93dbb8d809ede01b05b2cace18a22fb39c032724481c5baa181dc24facba5e9f7ea5"
      },
      {
        "kid": "511",
        "ctr": "43690",
        "header": "1901ffaaaa",
        "nonce": "77fbf5f1d82c73f6d2b3f963",
        "ciphertext": "1901ffaaaae0f2384e4dc472cb92238b5b722159205c4481665484de66985f155071655ca4e9d1c998781f8c7d439f8d1eb6f6071cd80fd2af2cd093aa260618"
      },
      {
        "kid": "72057594037927935",
        "ctr": "72057594037927935",
        "header": "6effffffffffffffffffffffffffff",
        "nonce": "77fbf5f1d8d38c092d4cac36",
        "ciphertext": "6effffffffffffffffffffffffffff4b8c7429d7ee83eec5e53808b80555b1f80b1df9d97877575fa1c7fa35b6119c68ed6543020075959dcc4ca6900a7f9cf1d9366dfcb086029e8966"
      },
      {
        "kid": "92057594037927935",
        "ctr": "8",
        "header": "0f01470de4df81ffff08",
        "nonce": "77fbf5f1d82c73f6d2b353c1",
        "ciphertext": "0f01470de4df81ffff08aaf4ea538ad99858a76f87b673dd548459dfbf98906fe82ce159ec738363b2af84d39c34418e2c56609d185ef66a3d7ce8d30547c1c9725439ac12"
      }
    ]
  }
];

function hexaDecodeToUint8Array(encoded)
{
    return new Uint8Array(encoded.match(/[\da-f]{2}/gi).map(function (h) {
        return parseInt(h, 16)
    }));
}

function hexaEncodeUint8Array(buffer)
{
    return Array.prototype.map.call(new Uint8Array(buffer), x => {
        return ('00' + x.toString(16)).slice(-2)
    }).join('');
}

function numberOrBigIntFromString(string)
{
    const value = parseInt(string);
    if ((''+ value) === string)
        return string;
    return BigInt(string);
}

async function doAESCMTest(testSuite, authenticationSize)
{
    let testCounter = 0;
    const key = await crypto.subtle.importKey("raw", hexaDecodeToUint8Array(testSuite.base_key), "HKDF", false, ["deriveBits", "deriveKey"]);

    for (const encryption of testSuite.encryptions) {
        testCounter++;

        promise_test(async () => {
            const transform = new SFrameTransform({ role : 'decrypt', authenticationSize : authenticationSize });
            transform.setEncryptionKey(key, numberOrBigIntFromString(encryption.kid));
            const reader = transform.readable.getReader();
            const writer = transform.writable.getWriter();

            const encryptedData = hexaDecodeToUint8Array(encryption.ciphertext);
            writer.write(encryptedData.buffer);
            const decrypted = await reader.read();

            const text = hexaEncodeUint8Array(new Uint8Array(decrypted.value));
            assert_equals(text, testSuite.plaintext);
        }, "Decryption test " + testCounter + " for ciphersuite " + testSuite.cipher_suite);

        const plaintextData = hexaDecodeToUint8Array(testSuite.plaintext);
        promise_test(async () => {
            const transform1 = new SFrameTransform({ role : 'encrypt', authenticationSize : authenticationSize });
            const transform2 = new SFrameTransform({ role : 'decrypt', authenticationSize : authenticationSize });
            transform1.setEncryptionKey(key, numberOrBigIntFromString(encryption.kid));
            transform2.setEncryptionKey(key, numberOrBigIntFromString(encryption.kid));
            const reader = transform2.readable.getReader();
            const writer = transform1.writable.getWriter();
            transform1.readable.pipeTo(transform2.writable);

            if (window.internals)
                internals.setSFrameCounter(transform1, encryption.ctr);
            else {
                const counter = numberOrBigIntFromString(encryption.ctr);
                if (typeof counter === "bigint") 
                    return;
                for (let i = 0; i < counter; ++i) {
                    await writer.write(new ArrayBuffer());
                    await reader.read();
               }
            }

            writer.write(plaintextData.buffer);
            const decrypted = await reader.read();

            const text = hexaEncodeUint8Array(new Uint8Array(decrypted.value));
            assert_equals(text, testSuite.plaintext);
        }, "Encryption/Decryption test " + testCounter + " for ciphersuite " + testSuite.cipher_suite);
    }
}
doAESCMTest(tests[0], 4);
doAESCMTest(tests[1], 8);
</script>
</body>
</html>
