tor-browser

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

RTCPeerConnection-peerIdentity.https.html (12395B)


      1 <!doctype html>
      2 <meta charset=utf-8>
      3 <title>RTCPeerConnection.prototype.peerIdentity</title>
      4 <script src="/resources/testharness.js"></script>
      5 <script src="/resources/testharnessreport.js"></script>
      6 <script src="identity-helper.sub.js"></script>
      7 <script>
      8  'use strict';
      9 
     10  // Test is based on the following editor draft:
     11  //   https://w3c.github.io/webrtc-identity/identity.html
     12 
     13  // The tests here interacts with the mock identity provider located at
     14  //   /.well-known/idp-proxy/mock-idp.js
     15 
     16  // The following helper functions are called from identity-helper.sub.js
     17  //   parseAssertionResult
     18  //   getIdpDomains
     19  //   assert_rtcerror_rejection
     20  //   hostString
     21 
     22  /*
     23    9.6.  RTCPeerConnection Interface Extensions
     24      partial interface RTCPeerConnection {
     25        void               setIdentityProvider(DOMString provider,
     26                                               optional RTCIdentityProviderOptions options);
     27        Promise<DOMString> getIdentityAssertion();
     28        readonly attribute Promise<RTCIdentityAssertion> peerIdentity;
     29        readonly attribute DOMString?                    idpLoginUrl;
     30        readonly attribute DOMString?                    idpErrorInfo;
     31      };
     32 
     33      dictionary RTCIdentityProviderOptions {
     34        DOMString protocol = "default";
     35        DOMString usernameHint;
     36        DOMString peerIdentity;
     37      };
     38 
     39      [Constructor(DOMString idp, DOMString name)]
     40      interface RTCIdentityAssertion {
     41        attribute DOMString idp;
     42        attribute DOMString name;
     43      };
     44   */
     45 
     46  /*
     47    4.3.2. setRemoteDescription
     48      If an a=identity attribute is present in the session description, the browser
     49      validates the identity assertion..
     50 
     51      If the "peerIdentity" configuration is applied to the RTCPeerConnection, this
     52      establishes a target peer identity of the provided value. Alternatively, if the
     53      RTCPeerConnection has previously authenticated the identity of the peer (that
     54      is, there is a current value for peerIdentity ), then this also establishes a
     55      target peer identity.
     56   */
     57  promise_test(async t => {
     58    const pc1 = new RTCPeerConnection();
     59    t.add_cleanup(() => pc1.close());
     60    const pc2 = new RTCPeerConnection();
     61 
     62    t.add_cleanup(() => pc2.close());
     63 
     64    const port = window.location.port;
     65    const [idpDomain] = getIdpDomains();
     66    const idpHost = hostString(idpDomain, port);
     67 
     68    pc1.setIdentityProvider(idpHost, {
     69      protocol: 'mock-idp.js',
     70      usernameHint: `alice@${idpDomain}`
     71    });
     72 
     73    const peerIdentity = pc2.peerIdentity;
     74    await pc2.setRemoteDescription(await pc1.createOffer());
     75    const { idp, name } = await peerIdentity;
     76    assert_equals(idp, idpHost, `Expect IdP to be ${idpHost}`);
     77    assert_equals(name, `alice@${idpDomain}`,
     78      `Expect validated identity from mock-idp.js to be same as specified in usernameHint`);
     79  }, 'setRemoteDescription() on offer with a=identity should establish peerIdentity');
     80 
     81  promise_test(async t => {
     82    const port = window.location.port;
     83    const [idpDomain] = getIdpDomains();
     84    const idpHost = hostString(idpDomain, port);
     85 
     86    const pc1 = new RTCPeerConnection();
     87    t.add_cleanup(() => pc1.close());
     88    pc1.setIdentityProvider(idpHost, {
     89      protocol: 'mock-idp.js',
     90      usernameHint: `doesnt_matter@${idpDomain}`
     91    });
     92 
     93    const pc2 = new RTCPeerConnection({
     94      peerIdentity: `bob@${idpDomain}`
     95    });
     96 
     97    t.add_cleanup(() => pc2.close());
     98 
     99    pc2.setIdentityProvider(idpHost, {
    100      protocol: 'mock-idp.js',
    101      usernameHint: `alice@${idpDomain}`
    102    });
    103 
    104    const offer = await pc1.createOffer();
    105 
    106    await promise_rejects_dom(t, 'OperationError',
    107      pc2.setRemoteDescription(offer));
    108    await promise_rejects_dom(t, 'OperationError',
    109      pc2.peerIdentity);
    110  }, 'setRemoteDescription() on offer with a=identity that resolve to value different from target peer identity should reject with OperationError');
    111 
    112  /*
    113    9.4.  Verifying Identity Assertions
    114      8.  The RTCPeerConnection decodes the contents and validates that it contains a
    115          fingerprint value for every a=fingerprint attribute in the session description.
    116          This ensures that the certificate used by the remote peer for communications
    117          is covered by the identity assertion.
    118 
    119      If identity validation fails, the peerIdentity promise is rejected with a newly
    120      created OperationError.
    121 
    122      If identity validation fails and there is a target peer identity for the
    123      RTCPeerConnection, the promise returned by setRemoteDescription MUST be rejected
    124      with the same DOMException.
    125   */
    126  promise_test(t => {
    127    const port = window.location.port;
    128    const [idpDomain] = getIdpDomains();
    129    const idpHost = hostString(idpDomain, port);
    130 
    131    const pc1 = new RTCPeerConnection();
    132    t.add_cleanup(() => pc1.close());
    133    const pc2 = new RTCPeerConnection({
    134      peerIdentity: `alice@${idpDomain}`
    135    });
    136 
    137    t.add_cleanup(() => pc2.close());
    138 
    139    // Ask mockidp.js to return custom contents in validation result
    140    pc1.setIdentityProvider(idpHost, {
    141      protocol: 'mock-idp.js?validatorAction=return-custom-contents&contents=bogus',
    142      usernameHint: `alice@${idpDomain}`
    143    });
    144 
    145    const peerIdentityPromise = pc2.peerIdentity;
    146 
    147    return pc1.createOffer()
    148    .then(offer => Promise.all([
    149      promise_rejects_dom(t, 'IdpError',
    150        pc2.setRemoteDescription(offer)),
    151      promise_rejects_dom(t, 'OperationError',
    152        peerIdentityPromise)
    153    ]));
    154  }, 'setRemoteDescription() with peerIdentity set and with IdP proxy that return validationAssertion with mismatch contents should reject with OperationError');
    155 
    156  /*
    157    9.4.  Verifying Identity Assertions
    158      9.  The RTCPeerConnection validates that the domain portion of the identity matches
    159          the domain of the IdP as described in [RTCWEB-SECURITY-ARCH]. If this check
    160          fails then the identity validation fails.
    161   */
    162  promise_test(t => {
    163    const port = window.location.port;
    164    const [idpDomain1, idpDomain2] = getIdpDomains();
    165    assert_not_equals(idpDomain1, idpDomain2,
    166      'Sanity check two idpDomains are different');
    167 
    168    const idpHost1 = hostString(idpDomain1, port);
    169 
    170    const pc1 = new RTCPeerConnection();
    171    t.add_cleanup(() => pc1.close());
    172    const pc2 = new RTCPeerConnection({
    173      peerIdentity: `alice@${idpDomain2}`
    174    });
    175 
    176    t.add_cleanup(() => pc2.close());
    177 
    178    // mock-idp.js will return assertion of domain2 identity
    179    // with domain1 in the idp.domain field
    180    pc1.setIdentityProvider(idpHost1, {
    181      protocol: 'mock-idp.js',
    182      usernameHint: `alice@${idpDomain2}`
    183    });
    184 
    185    return pc1.getIdentityAssertion()
    186    .then(assertionResultStr => {
    187      const { idp, assertion } = parseAssertionResult(assertionResultStr);
    188 
    189      assert_equals(idp.domain, idpHost1,
    190        'Sanity check domain of assertion is host1');
    191 
    192      assert_equals(assertion.args.options.usernameHint, `alice@${idpDomain2}`,
    193        'Sanity check domain1 is going to validate a domain2 identity');
    194 
    195      return pc1.createOffer();
    196    })
    197    .then(offer => Promise.all([
    198      promise_rejects_dom(t, 'OperationError',
    199        pc2.setRemoteDescription(offer)),
    200      promise_rejects_dom(t, 'OperationError',
    201        pc2.peerIdentity)
    202    ]));
    203  }, 'setRemoteDescription() and peerIdentity should reject with OperationError if IdP return validated identity that is different from its own domain');
    204 
    205  /*
    206    9.4 Verifying Identity Assertions
    207      If identity validation fails and there is a target peer identity for the
    208      RTCPeerConnection, the promise returned by setRemoteDescription MUST be rejected
    209      with the same DOMException.
    210 
    211    9.5 IdP Error Handling
    212      - If an identity provider throws an exception or returns a promise that is ultimately
    213        rejected, then the procedure that depends on the IdP MUST also fail. These types of
    214        errors will cause an IdP failure with an RTCError with errorDetail set to
    215        "idp-execution-failure".
    216 
    217      Any error generated by the IdP MAY provide additional information in the
    218      idpErrorInfo attribute. The information in this string is defined by the
    219      IdP in use.
    220   */
    221  promise_test(t => {
    222    const port = window.location.port;
    223    const [idpDomain] = getIdpDomains();
    224    const idpHost = hostString(idpDomain, port);
    225 
    226    const pc1 = new RTCPeerConnection();
    227    t.add_cleanup(() => pc1.close());
    228    const pc2 = new RTCPeerConnection({
    229      peerIdentity: `alice@${idpDomain}`
    230    });
    231 
    232    t.add_cleanup(() => pc2.close());
    233 
    234    // Ask mock-idp.js to throw error during validation,
    235    // i.e. during pc2.setRemoteDescription()
    236    pc1.setIdentityProvider(idpHost, {
    237      protocol: 'mock-idp.js?validatorAction=throw-error&errorInfo=bar',
    238      usernameHint: `alice@${idpDomain}`
    239    });
    240 
    241    return pc1.createOffer()
    242    .then(offer => Promise.all([
    243      assert_rtcerror_rejection('idp-execution-failure',
    244        pc2.setRemoteDescription(offer)),
    245      assert_rtcerror_rejection('idp-execution-failure',
    246        pc2.peerIdentity)
    247    ]))
    248    .then(() => {
    249      assert_equals(pc2.idpErrorInfo, 'bar',
    250        'Expect pc2.idpErrorInfo to be set to the err.idpErrorInfo thrown by mock-idp.js');
    251    });
    252  }, `When IdP throws error and pc has target peer identity, setRemoteDescription() and peerIdentity rejected with RTCError('idp-execution-error')`);
    253 
    254  /*
    255    4.3.2. setRemoteDescription
    256      If there is no target peer identity, then setRemoteDescription does not await the
    257      completion of identity validation.
    258 
    259    9.5.  IdP Error Handling
    260      - If an identity provider throws an exception or returns a promise that is
    261        ultimately rejected, then the procedure that depends on the IdP MUST also fail.
    262        These types of errors will cause an IdP failure with an RTCError with errorDetail
    263        set to "idp-execution-failure".
    264 
    265    9.4.  Verifying Identity Assertions
    266      If identity validation fails and there is no a target peer identity, the value of
    267      the peerIdentity MUST be set to a new, unresolved promise instance. This permits
    268      the use of renegotiation (or a subsequent answer, if the session description was
    269      a provisional answer) to resolve or reject the identity.
    270   */
    271  promise_test(t => {
    272    const pc1 = new RTCPeerConnection();
    273    t.add_cleanup(() => pc1.close());
    274    const pc2 = new RTCPeerConnection();
    275 
    276    t.add_cleanup(() => pc2.close());
    277 
    278    const port = window.location.port;
    279    const [idpDomain] = getIdpDomains();
    280    const idpHost = hostString(idpDomain, port);
    281 
    282    // Ask mock-idp.js to throw error during validation,
    283    // i.e. during pc2.setRemoteDescription()
    284    pc1.setIdentityProvider(idpHost, {
    285      protocol: 'mock-idp.js?validatorAction=throw-error',
    286      usernameHint: `alice@${idpDomain}`
    287    });
    288 
    289    const peerIdentityPromise1 = pc2.peerIdentity;
    290 
    291    return pc1.createOffer()
    292    .then(offer =>
    293      // setRemoteDescription should succeed because there is no target peer identity set
    294      pc2.setRemoteDescription(offer))
    295    .then(() =>
    296      assert_rtcerror_rejection('idp-execution-failure',
    297        peerIdentityPromise1,
    298        `Expect first peerIdentity promise to be rejected with RTCError('idp-execution-failure')`))
    299    .then(() => {
    300      const peerIdentityPromise2 = pc2.peerIdentity;
    301      assert_not_equals(peerIdentityPromise2, peerIdentityPromise1,
    302        'Expect pc2.peerIdentity to be replaced with a fresh unresolved promise');
    303 
    304      // regenerate an identity assertion with no test option to throw error
    305      pc1.setIdentityProvider(idpHost, {
    306        protocol: 'idp-test.js',
    307        usernameHint: `alice@${idpDomain}`
    308      });
    309 
    310      return pc1.createOffer()
    311      .then(offer => pc2.setRemoteDescription(offer))
    312      .then(peerIdentityPromise2)
    313      .then(identityAssertion => {
    314        const { idp, name } = identityAssertion;
    315 
    316        assert_equals(idp, idpDomain,
    317          `Expect IdP domain to be ${idpDomain}`);
    318 
    319        assert_equals(name, `alice@${idpDomain}`,
    320          `Expect validated identity to be alice@${idpDomain}`);
    321 
    322        assert_equals(pc2.peeridentity, peerIdentityPromise2,
    323          'Expect pc2.peerIdentity to stay fixed after identity is validated');
    324      });
    325    });
    326  }, 'IdP failure with no target peer identity should have following setRemoteDescription() succeed and replace pc.peerIdentity with a new promise');
    327 
    328 </script>