tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test_webauthn_crossorigin_featurepolicy.html (13285B)


      1 <!DOCTYPE html>
      2 <meta charset=utf-8>
      3 <head>
      4  <title>Tests for Publickey-Credentials-Get Feature Policy for W3C Web Authentication</title>
      5  <script src="/tests/SimpleTest/SimpleTest.js"></script>
      6  <script type="text/javascript" src="u2futil.js"></script>
      7  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
      8 </head>
      9 <body>
     10 
     11  <h1>Tests for Publickey-Credentials-Get Feature Policy for W3C Web Authentication</h1>
     12  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1460986">Mozilla Bug 1460986</a>
     13 
     14  <script class="testbody" type="text/javascript">
     15    "use strict";
     16 
     17    var gAuthenticatorId;
     18    var gSameCredId;
     19    var gCrossCredId;
     20 
     21    const CROSS_DOMAIN = "example.org";
     22 
     23    function compare(a, b) {
     24      if (a.length != b.length) return false;
     25      for (let i = 0; i < a.length; i += 1) {
     26        if (a[i] !== b[i]) return false;
     27      }
     28      return true;
     29    }
     30 
     31    function arrivingHereIsGood(aResult) {
     32      ok(true, "Good result! Received a: " + aResult);
     33    }
     34 
     35    function arrivingHereIsBad(aResult) {
     36      ok(false, "Bad result! Received a: " + aResult);
     37    }
     38 
     39    function expectNotAllowedError(aResult) {
     40      ok(aResult == "NotAllowedError", "Expecting a NotAllowedError, got " + aResult);
     41    }
     42 
     43    function expectSameCredId(aResult) {
     44      ok(compare(aResult, gSameCredId), "Expecting credential for " + document.domain);
     45    }
     46 
     47    function expectCrossCredId(aResult) {
     48      ok(compare(aResult, gCrossCredId), "Expecting credential for " + CROSS_DOMAIN);
     49    }
     50 
     51    function getAssertion(id) {
     52        let chall = new Uint8Array(16);
     53        this.content.window.crypto.getRandomValues(chall);
     54 
     55        let options = {
     56          challenge: chall,
     57          allowCredentials: [ { type: "public-key", id } ],
     58        };
     59 
     60        return this.content.window.navigator.credentials.get({publicKey: options})
     61          .then(res => Promise.resolve(new Uint8Array(res.rawId)))
     62          .catch(e => Promise.reject(e.name));
     63    }
     64 
     65    function getAssertionAndReturnClientDataJSON(id) {
     66        let chall = new Uint8Array(16);
     67        this.content.window.crypto.getRandomValues(chall);
     68 
     69        let options = {
     70          challenge: chall,
     71          allowCredentials: [ { type: "public-key", id } ],
     72        };
     73 
     74        return this.content.window.navigator.credentials.get({publicKey: options})
     75          .then(res => Promise.resolve(new Uint8Array(res.response.clientDataJSON)))
     76          .catch(e => Promise.reject(e.name));
     77    }
     78 
     79    function createCredential() {
     80      this.content.document.notifyUserGestureActivation();
     81 
     82      const cose_alg_ECDSA_w_SHA256 = -7;
     83      let publicKey = {
     84        rp: {id: this.content.window.document.domain, name: "none"},
     85        user: {id: new Uint8Array(), name: "none", displayName: "none"},
     86        challenge: this.content.window.crypto.getRandomValues(new Uint8Array(16)),
     87        pubKeyCredParams: [{type: "public-key", alg: cose_alg_ECDSA_w_SHA256}],
     88      };
     89 
     90      return this.content.window.navigator.credentials.create({publicKey})
     91          .then(res => Promise.resolve(new Uint8Array(res.rawId)))
     92          .catch(e => Promise.reject(e.name));
     93    }
     94 
     95    async function setup(preloadSame, preloadCross) {
     96      if (!gAuthenticatorId) {
     97          gAuthenticatorId = await addVirtualAuthenticator();
     98      }
     99      if (gSameCredId) {
    100        removeCredential(gAuthenticatorId, bytesToBase64UrlSafe(gSameCredId));
    101        gSameCredId = undefined;
    102      }
    103      if (gCrossCredId) {
    104        removeCredential(gAuthenticatorId, bytesToBase64UrlSafe(gCrossCredId));
    105        gCrossCredId = undefined;
    106      }
    107      if (preloadSame) {
    108        gSameCredId = await addCredential(gAuthenticatorId, document.domain).then(id => base64ToBytesUrlSafe(id));
    109      }
    110      if (preloadCross) {
    111        gCrossCredId = await addCredential(gAuthenticatorId, CROSS_DOMAIN).then(id => base64ToBytesUrlSafe(id));
    112      }
    113    }
    114 
    115    add_task(async function test_same_origin_iframe_allow() {
    116      // Don't preload any credentials. We'll try to create one in content.
    117      await setup(false, false);
    118 
    119      let iframe = document.createElement("iframe");
    120      iframe.setAttribute("src", "https://" + document.domain + "/tests/dom/webauthn/tests/empty.html");
    121      document.body.appendChild(iframe);
    122      await new Promise(resolve => iframe.addEventListener("load", resolve, {once: true}));
    123 
    124      ok("featurePolicy" in iframe, "we have iframe.featurePolicy");
    125      ok(iframe.featurePolicy.allowsFeature("publickey-credentials-create"), "iframe allows publickey-credentials-create");
    126      ok(iframe.featurePolicy.allowsFeature("publickey-credentials-get"), "iframe allows publickey-credentials-get");
    127 
    128      // We should be able to create a credential in a same-origin iframe by default.
    129      is(gSameCredId, undefined);
    130      gSameCredId = new Uint8Array(await SpecialPowers.spawn(iframe, [], createCredential));
    131 
    132      // We should be able to assert a credential in a same-origin iframe by default.
    133      await SpecialPowers.spawn(iframe, [gSameCredId], getAssertion)
    134          .then(expectSameCredId)
    135          .catch(expectNotAllowedError);
    136 
    137      // Assert again, but get the client data this time
    138      let clientDataBuffer = await SpecialPowers.spawn(iframe, [gSameCredId], getAssertionAndReturnClientDataJSON)
    139          .catch(arrivingHereIsBad);
    140 
    141      let clientData = JSON.parse(buffer2string(clientDataBuffer));
    142      ok(!clientData.crossOrigin, "Client data shows response is same origin");
    143      is(clientData.topOrigin, undefined, "Client data does not include top origin");
    144    });
    145 
    146    add_task(async function test_same_origin_iframe_deny() {
    147      // Preload same-origin credential to ensure we cannot assert it.
    148      await setup(true, false);
    149 
    150      let iframe = document.createElement("iframe");
    151      iframe.setAttribute("src", "https://" + document.domain + "/tests/dom/webauthn/tests/empty.html");
    152      iframe.setAttribute("allow", "publickey-credentials-create 'none'; publickey-credentials-get 'none'");
    153      document.body.appendChild(iframe);
    154      await new Promise(resolve => iframe.addEventListener("load", resolve, {once: true}));
    155 
    156      ok("featurePolicy" in iframe, "we have iframe.featurePolicy");
    157      ok(!iframe.featurePolicy.allowsFeature("publickey-credentials-create"), "iframe does not allow publickey-credentials-create");
    158      ok(!iframe.featurePolicy.allowsFeature("publickey-credentials-get"), "iframe does not allow publickey-credentials-get");
    159 
    160      // We should not be able to create a credential in a same-origin iframe if
    161      // the iframe does not allow publickey-credentials-create.
    162      await SpecialPowers.spawn(iframe, [], createCredential)
    163          .then(arrivingHereIsBad)
    164          .catch(expectNotAllowedError);
    165 
    166      // We should not be able to assert a credential in a same-origin iframe if
    167      // the iframe does not allow publickey-credentials-get.
    168      await SpecialPowers.spawn(iframe, [gSameCredId], getAssertion)
    169          .then(arrivingHereIsBad)
    170          .catch(expectNotAllowedError);
    171    });
    172 
    173    add_task(async function test_cross_origin_iframe_allow() {
    174      // Don't preload any credentials. We'll try to create one in content.
    175      await setup(false, false);
    176 
    177      let iframe = document.createElement("iframe");
    178      iframe.setAttribute("src", "https://" + CROSS_DOMAIN + "/tests/dom/webauthn/tests/empty.html");
    179      iframe.setAttribute("allow", "publickey-credentials-create https://" + CROSS_DOMAIN + "; publickey-credentials-get https://" + CROSS_DOMAIN);
    180      document.body.appendChild(iframe);
    181      await new Promise(resolve => iframe.addEventListener("load", resolve, {once: true}));
    182 
    183      ok("featurePolicy" in iframe, "we have iframe.featurePolicy");
    184      ok(iframe.featurePolicy.allowsFeature("publickey-credentials-create"), "iframe allows publickey-credentials-create");
    185      ok(iframe.featurePolicy.allowsFeature("publickey-credentials-get"), "iframe allows publickey-credentials-get");
    186 
    187      // We should be able to create a credential in a same-origin iframe if
    188      // the iframe allows publickey-credentials-create.
    189      is(gCrossCredId, undefined);
    190      gCrossCredId = new Uint8Array(await SpecialPowers.spawn(iframe, [], createCredential));
    191 
    192      // We should be able to assert a credential in a cross-origin iframe if
    193      // the iframe allows publickey-credentials-get.
    194      await SpecialPowers.spawn(iframe, [gCrossCredId], getAssertion)
    195          .then(expectCrossCredId)
    196          .catch(arrivingHereIsBad);
    197 
    198      // Assert again, but get the client data this time
    199      let clientDataBuffer = await SpecialPowers.spawn(iframe, [gCrossCredId], getAssertionAndReturnClientDataJSON)
    200          .catch(arrivingHereIsBad);
    201 
    202      let clientData = JSON.parse(buffer2string(clientDataBuffer));
    203      ok(clientData.crossOrigin, "Client data shows response is cross origin");
    204      is(clientData.topOrigin, window.location.origin, "Top origin is correct");
    205    });
    206 
    207    add_task(async function test_cross_origin_iframe_deny() {
    208      // Preload cross-origin credential to ensure we cannot assert it.
    209      await setup(false, true);
    210 
    211      let iframe = document.createElement("iframe");
    212      iframe.setAttribute("src", "https://" + CROSS_DOMAIN + "/tests/dom/webauthn/tests/empty.html");
    213      document.body.appendChild(iframe);
    214      await new Promise(resolve => iframe.addEventListener("load", resolve, {once: true}));
    215 
    216      ok("featurePolicy" in iframe, "we have iframe.featurePolicy");
    217      ok(!iframe.featurePolicy.allowsFeature("publickey-credentials-create"), "iframe does not allow publickey-credentials-create");
    218      ok(!iframe.featurePolicy.allowsFeature("publickey-credentials-get"), "iframe does not allow publickey-credentials-get");
    219 
    220      // We should not be able to create a credential in a cross-origin iframe if
    221      // the iframe does not allow publickey-credentials-create.
    222      await SpecialPowers.spawn(iframe, [], createCredential)
    223          .then(arrivingHereIsBad)
    224          .catch(expectNotAllowedError);
    225 
    226      // We should not be able to assert a credential in a cross-origin iframe if
    227      // the iframe does not allow publickey-credentials-get.
    228      await SpecialPowers.spawn(iframe, [gCrossCredId], getAssertion)
    229          .then(arrivingHereIsBad)
    230          .catch(expectNotAllowedError);
    231    });
    232 
    233    add_task(async function test_cross_origin_iframe_create_but_not_get() {
    234      // Don't preload any credentials. We'll try to create one in content.
    235      await setup(false, false);
    236 
    237      let iframe = document.createElement("iframe");
    238      iframe.setAttribute("src", "https://" + CROSS_DOMAIN + "/tests/dom/webauthn/tests/empty.html");
    239      iframe.setAttribute("allow", "publickey-credentials-create https://" + CROSS_DOMAIN);
    240      document.body.appendChild(iframe);
    241      await new Promise(resolve => iframe.addEventListener("load", resolve, {once: true}));
    242 
    243      ok("featurePolicy" in iframe, "we have iframe.featurePolicy");
    244      ok(iframe.featurePolicy.allowsFeature("publickey-credentials-create"), "iframe allows publickey-credentials-create");
    245      ok(!iframe.featurePolicy.allowsFeature("publickey-credentials-get"), "iframe does not allow publickey-credentials-get");
    246 
    247      // We should be able to create a credential in a cross-origin iframe if
    248      // the iframe allows publickey-credentials-create.
    249      is(gCrossCredId, undefined);
    250      gCrossCredId = new Uint8Array(await SpecialPowers.spawn(iframe, [], createCredential));
    251 
    252      // We should not be able to assert a credential in a cross-origin iframe if
    253      // the iframe does not allow publickey-credentials-get.
    254      await SpecialPowers.spawn(iframe, [gCrossCredId], getAssertion)
    255          .then(arrivingHereIsBad)
    256          .catch(expectNotAllowedError);
    257    });
    258 
    259    add_task(async function test_cross_origin_iframe_get_but_not_create() {
    260      // Preload cross-origin credential so we can assert it.
    261      await setup(false, true);
    262 
    263      let iframe = document.createElement("iframe");
    264      iframe.setAttribute("src", "https://" + CROSS_DOMAIN + "/tests/dom/webauthn/tests/empty.html");
    265      iframe.setAttribute("allow", "publickey-credentials-get https://" + CROSS_DOMAIN);
    266      document.body.appendChild(iframe);
    267      await new Promise(resolve => iframe.addEventListener("load", resolve, {once: true}));
    268 
    269      ok("featurePolicy" in iframe, "we have iframe.featurePolicy");
    270      ok(!iframe.featurePolicy.allowsFeature("publickey-credentials-create"), "iframe does not publickey-credentials-create");
    271      ok(iframe.featurePolicy.allowsFeature("publickey-credentials-get"), "iframe allows publickey-credentials-get");
    272 
    273      // We should not be able to create a credential in a cross-origin iframe if
    274      // the iframe does not allow publickey-credentials-create.
    275      await SpecialPowers.spawn(iframe, [], createCredential)
    276          .then(arrivingHereIsBad)
    277          .catch(expectNotAllowedError);
    278 
    279      // We should not be able to assert a credential in a cross-origin iframe if
    280      // the iframe does not allow publickey-credentials-get.
    281      await SpecialPowers.spawn(iframe, [gCrossCredId], getAssertion)
    282          .then(arrivingHereIsGood)
    283          .catch(arrivingHereIsBad);
    284    });
    285 
    286  </script>
    287 
    288 </body>
    289 </html>