<!DOCTYPE html>
<html>

<head>
<title>WebCrypto Test Suite</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<link rel="stylesheet" href="./test_WebCrypto.css"/>
<script src="/tests/SimpleTest/SimpleTest.js"></script>

<!-- Utilities for manipulating ABVs -->
<script src="util.js"></script>

<!-- A simple wrapper around IndexedDB -->
<script src="simpledb.js"></script>

<!-- Test vectors drawn from the literature -->
<script src="./test-vectors.js"></script>

<!-- General testing framework -->
<script src="./test-array.js"></script>

<script>/* <![CDATA[*/
"use strict";

// -----------------------------------------------------------------------------
TestArray.addTest(
  "Key wrap known answer, using AES-GCM",
  function() {
    var that = this;
    var alg = {
      name: "AES-GCM",
      iv: tv.key_wrap_known_answer.wrapping_iv,
      tagLength: 128,
    };
    var key, wrappingKey;

    function doImport(k) {
      wrappingKey = k;
      return crypto.subtle.importKey("raw", tv.key_wrap_known_answer.key,
                                     alg, true, ["encrypt", "decrypt"]);
    }
    function doWrap(k) {
      key = k;
      return crypto.subtle.wrapKey("raw", key, wrappingKey, alg);
    }

    crypto.subtle.importKey("raw", tv.key_wrap_known_answer.wrapping_key,
                            alg, false, ["wrapKey"])
      .then(doImport, error(that))
      .then(doWrap, error(that))
      .then(
        memcmp_complete(that, tv.key_wrap_known_answer.wrapped_key),
        error(that)
      );
  }
);

// -----------------------------------------------------------------------------
TestArray.addTest(
  "Key wrap failing on non-extractable key",
  function() {
    var that = this;
    var alg = {
      name: "AES-GCM",
      iv: tv.key_wrap_known_answer.wrapping_iv,
      tagLength: 128,
    };
    var key, wrappingKey;

    function doImport(k) {
      wrappingKey = k;
      return crypto.subtle.importKey("raw", tv.key_wrap_known_answer.key,
                                     alg, false, ["encrypt", "decrypt"]);
    }
    function doWrap(k) {
      key = k;
      return crypto.subtle.wrapKey("raw", key, wrappingKey, alg);
    }

    crypto.subtle.importKey("raw", tv.key_wrap_known_answer.wrapping_key,
                            alg, false, ["wrapKey"])
      .then(doImport, error(that))
      .then(doWrap, error(that))
      .then(
        error(that),
        complete(that)
      );
  }
);

// -----------------------------------------------------------------------------
TestArray.addTest(
  "Key unwrap known answer, using AES-GCM",
  function() {
    var that = this;
    var alg = {
      name: "AES-GCM",
      iv: tv.key_wrap_known_answer.wrapping_iv,
      tagLength: 128,
    };
    var wrappingKey;

    function doUnwrap(k) {
      wrappingKey = k;
      return crypto.subtle.unwrapKey(
                "raw", tv.key_wrap_known_answer.wrapped_key,
                wrappingKey, alg,
                "AES-GCM", true, ["encrypt", "decrypt"]
             );
    }
    function doExport(k) {
      return crypto.subtle.exportKey("raw", k);
    }

    crypto.subtle.importKey("raw", tv.key_wrap_known_answer.wrapping_key,
                            alg, false, ["unwrapKey"])
      .then(doUnwrap, error(that))
      .then(doExport, error(that))
      .then(
        memcmp_complete(that, tv.key_wrap_known_answer.key),
        error(that)
      );
  }
);

// -----------------------------------------------------------------------------
TestArray.addTest(
  "Key wrap/unwrap round-trip, using RSA-OAEP",
  function() {
    var that = this;
    var oaep = {
      name: "RSA-OAEP",
      hash: "SHA-256",
    };
    var gcm = {
      name: "AES-GCM",
      iv: tv.aes_gcm_enc.iv,
      additionalData: tv.aes_gcm_enc.adata,
      tagLength: 128,
    };
    var unwrapKey;

    function doWrap(keys) {
      var originalKey = keys[0];
      var wrapKey = keys[1];
      unwrapKey = keys[2];
      return crypto.subtle.wrapKey("raw", originalKey, wrapKey, oaep);
    }
    function doUnwrap(wrappedKey) {
      return crypto.subtle.unwrapKey("raw", wrappedKey, unwrapKey, oaep,
                                     gcm, false, ["encrypt"]);
    }
    function doEncrypt(aesKey) {
      return crypto.subtle.encrypt(gcm, aesKey, tv.aes_gcm_enc.data);
    }

    // 1.Import:
    //  -> HMAC key
    //  -> OAEP wrap key (public)
    //  -> OAEP unwrap key (private)
    // 2. Wrap the HMAC key
    // 3. Unwrap it
    // 4. Compute HMAC
    // 5. Check HMAC value
    Promise.all([
      crypto.subtle.importKey("raw", tv.aes_gcm_enc.key, gcm, true, ["encrypt"]),
      crypto.subtle.importKey("spki", tv.rsaoaep.spki, oaep, true, ["wrapKey"]),
      crypto.subtle.importKey("pkcs8", tv.rsaoaep.pkcs8, oaep, false, ["unwrapKey"]),
    ])
      .then(doWrap, error(that))
      .then(doUnwrap, error(that))
      .then(doEncrypt, error(that))
      .then(
        memcmp_complete(that, tv.aes_gcm_enc.result),
        error(that)
      );
  }
);

// -----------------------------------------------------------------------------
TestArray.addTest(
  "HMAC JWK wrap/unwrap round-trip, with AES-GCM",
  function() {
    var that = this;
    var genAlg = { name: "HMAC", hash: "SHA-384", length: 512 };
    var wrapAlg = { name: "AES-GCM", iv: tv.aes_gcm_enc.iv };
    var wrapKey, originalKey, originalKeyJwk;

    function doExport(k) {
      return crypto.subtle.exportKey("jwk", k);
    }
    function doWrap() {
      return crypto.subtle.wrapKey("jwk", originalKey, wrapKey, wrapAlg);
    }
    function doUnwrap(wrappedKey) {
      return crypto.subtle.unwrapKey("jwk", wrappedKey, wrapKey, wrapAlg,
                                     { name: "HMAC", hash: "SHA-384"},
                                     true, ["sign", "verify"]);
    }

    Promise.all([
      crypto.subtle.importKey("jwk", tv.aes_gcm_enc.key_jwk,
                              "AES-GCM", false, ["wrapKey", "unwrapKey"])
        .then(function(x) { wrapKey = x; }),
      crypto.subtle.generateKey(genAlg, true, ["sign", "verify"])
        .then(function(x) { originalKey = x; return x; })
        .then(doExport)
        .then(function(x) { originalKeyJwk = x; }),
    ])
      .then(doWrap)
      .then(doUnwrap)
      .then(doExport)
      .then(
        complete(that, function(x) {
          return exists(x.k) && x.k == originalKeyJwk.k;
        }),
        error(that)
      );
  }
);

// -----------------------------------------------------------------------------
TestArray.addTest(
  "ECDSA private JWK wrap/unwrap round-trip, with AES-GCM",
  function() {
    var that = this;
    var genAlg = { name: "ECDSA", namedCurve: "P-256" };
    var wrapAlg = { name: "AES-GCM", iv: tv.aes_gcm_enc.iv };
    var wrapKey, originalKey, originalKeyJwk;

    function doExport(k) {
      return crypto.subtle.exportKey("jwk", k);
    }
    function doWrap() {
      return crypto.subtle.wrapKey("jwk", originalKey, wrapKey, wrapAlg);
    }
    function doUnwrap(wrappedKey) {
      return crypto.subtle.unwrapKey("jwk", wrappedKey, wrapKey, wrapAlg,
                                     genAlg, true, ["sign"]);
    }

    Promise.all([
      crypto.subtle.importKey("jwk", tv.aes_gcm_enc.key_jwk,
                              "AES-GCM", false, ["wrapKey", "unwrapKey"])
        .then(function(x) { wrapKey = x; }),
      crypto.subtle.generateKey(genAlg, true, ["sign", "verify"])
        .then(function(x) { originalKey = x.privateKey; return x.privateKey; })
        .then(doExport)
        .then(function(x) { originalKeyJwk = x; }),
    ])
      .then(doWrap)
      .then(doUnwrap)
      .then(doExport)
      .then(
        complete(that, function(x) {
     return (x.kty == originalKeyJwk.kty) &&
            (x.crv == originalKeyJwk.crv) &&
            (x.d == originalKeyJwk.d) &&
            (x.x == originalKeyJwk.x) &&
            (x.y == originalKeyJwk.y);
        }),
        error(that)
      );
  }
);

// -----------------------------------------------------------------------------
TestArray.addTest(
  "AES-KW known answer",
  function() {
    var that = this;

    function doWrap(keys) {
      var wrapKey = keys[0];
      var originalKey = keys[1];
      return crypto.subtle.wrapKey("raw", originalKey, wrapKey, "AES-KW");
    }

    Promise.all([
      crypto.subtle.importKey("jwk", tv.aes_kw.wrapping_key,
                              "AES-KW", false, ["wrapKey"]),
      crypto.subtle.importKey("jwk", tv.aes_kw.key,
                              "AES-GCM", true, ["encrypt"]),
    ])
      .then(doWrap)
      .then(
        memcmp_complete(that, tv.aes_kw.wrapped_key),
        error(that)
      );
  }
);

// -----------------------------------------------------------------------------
TestArray.addTest(
  "AES-KW unwrap failure on tampered key data",
  function() {
    var that = this;
    var tamperedWrappedKey = new Uint8Array(tv.aes_kw.wrapped_key);
    tamperedWrappedKey[5] ^= 0xFF;

    function doUnwrap(wrapKey) {
      return crypto.subtle.unwrapKey("raw", tamperedWrappedKey, wrapKey,
                                     "AES-KW", "AES-GCM",
                                     true, ["encrypt", "decrypt"]);
    }

    crypto.subtle.importKey("jwk", tv.aes_kw.wrapping_key,
                              "AES-KW", false, ["unwrapKey"])
      .then(doUnwrap)
      .then(error(that), complete(that));
  }
);

// -----------------------------------------------------------------------------
TestArray.addTest(
  "AES-KW unwrap AES-KW key data",
  function() {
    var that = this;
    var wrappedKey = new Uint8Array(tv.aes_kw.wrapped_key);

    function doUnwrap(wrapKey) {
      return crypto.subtle.unwrapKey("raw", wrappedKey, wrapKey,
                                     "AES-KW", "AES-KW",
                                     true, ["wrapKey", "unwrapKey"]);
    }

    crypto.subtle.importKey("jwk", tv.aes_kw.wrapping_key,
                              "AES-KW", false, ["unwrapKey"])
      .then(doUnwrap)
      .then(
        memcmp_complete(that, tv.aes_kw.key),
        error(that)
      );
  }
);

// -----------------------------------------------------------------------------
TestArray.addTest(
  "AES-KW wrap/unwrap round-trip",
  function() {
    var that = this;
    var genAlg = { name: "HMAC", hash: "SHA-384", length: 512 };
    var wrapKey, originalKey, originalKeyJwk;

    function doExport(k) {
      return crypto.subtle.exportKey("jwk", k);
    }
    function doWrap() {
      return crypto.subtle.wrapKey("raw", originalKey, wrapKey, "AES-KW");
    }
    function doUnwrap(wrappedKey) {
      return crypto.subtle.unwrapKey("raw", wrappedKey, wrapKey,
                                     "AES-KW", { name: "HMAC", hash: "SHA-384"},
                                     true, ["sign", "verify"]);
    }

    Promise.all([
      crypto.subtle.importKey("jwk", tv.aes_kw.wrapping_key,
                              "AES-KW", false, ["wrapKey", "unwrapKey"])
        .then(function(x) { wrapKey = x; }),
      crypto.subtle.generateKey(genAlg, true, ["sign"])
        .then(function(x) { originalKey = x; return x; })
        .then(doExport)
        .then(function(x) { originalKeyJwk = x; }),
    ])
      .then(doWrap)
      .then(doUnwrap)
      .then(doExport)
      .then(
        complete(that, function(x) {
          return exists(x.k) && x.k == originalKeyJwk.k;
        }),
        error(that)
      );
  }
);

// -----------------------------------------------------------------------------
TestArray.addTest(
  "JWK unwrap attempt on bogus data should error out",
  function() {
    // Largely cribbed from the "JWK wrap/unwrap round-trip, with AES-GCM" test
    var that = this;
    var wrapAlg = { name: "AES-GCM", iv: tv.aes_gcm_enc.iv };
    var wrapKey;

    function doBogusWrap() {
      var abv = new TextEncoder().encode("I am so not JSON");
      return crypto.subtle.encrypt(wrapAlg, wrapKey, abv);
    }
    function doUnwrap(wrappedKey) {
      return crypto.subtle.unwrapKey("jwk", wrappedKey, wrapKey, wrapAlg,
                                     {name: "HMAC", hash: "SHA-384"},
                                     true, ["sign", "verify"]);
    }

    crypto.subtle.importKey("jwk", tv.aes_gcm_enc.key_jwk,
                            "AES-GCM", false, ["encrypt", "unwrapKey"])
      .then(function(x) { wrapKey = x; })
      .then(doBogusWrap, error(that))
      .then(doUnwrap, error(that))
      .then(
        error(that),
        complete(that)
      );
  }
);

/* ]]>*/</script>
</head>

<body>

<div id="content">
	<div id="head">
		<b>Web</b>Crypto<br>
	</div>

    <div id="start" onclick="start();">RUN ALL</div>

    <div id="resultDiv" class="content">
    Summary:
    <span class="pass"><span id="passN">0</span> passed, </span>
    <span class="fail"><span id="failN">0</span> failed, </span>
    <span class="pending"><span id="pendingN">0</span> pending.</span>
    <br/>
    <br/>

    <table id="results">
        <tr>
            <th>Test</th>
            <th>Result</th>
            <th>Time</th>
        </tr>
    </table>

    </div>

    <div id="foot"></div>
</div>

</body>
</html>
