Loop/Architecture/Fingerprint Validation

From MozillaWiki
< Loop‎ | Architecture
Revision as of 22:05, 23 September 2015 by AdamRoach (talk | contribs)
Jump to navigation Jump to search

WebRTC's communications security model works by way of a three-pronged model:

  1. All media is encrypted with SRTP-DLTS, which uses a DH exchange to establish media security. The negotiated DH keys are not exposed to content.
  2. To prevent active on-path attacks, DTLS fingerprint validation is performed by using a third-party signature, rooted in the web PKI, to authenticate the fingerprint. See the WebRTC 1.0 Identity mechanism for details.
  3. To prevent in-content attacks, any streams for which fingerprint validation has been performed are "isolated" such that the content cannot be extracted by the webpage.

The first mechanism prevents passive interception; the second, active on-path interception; and the third, attacks by the content itself.

Currently, Hello makes use of only the first of these mechanisms, and relies on the security of the underlying network infrastructure (specifically, the OpenTok servers) to prevent on-path attacks. Because the nature of Hello does not require the use of any identity, making use of the second and third levels of protection can't be employed as specified.

It is worth noting that additional trust gained by adding the Identity mechanism is only as good as the trust provided by the party that is vouching for a user's identity. For example, if the corresponding service cannot be trusted to provide such certificates only to authenticated users (or cannot be trusted to properly validate certificates when asked to), then its role is largely defeated.

This basically sets up a system in which the Identity provider is serving as a third-party check against attacks on the infrastructure used to exchange SDP (and therefore DTLS fingerprints).

The design below provides additional protection by using Mozilla's servers to provide a third-party check against attacks on the OpenTok infrastructure, without requiring the use of verifiable user identities. Note that this isn't as strong as the protection provided by the WebRTC Identity mechanism; however, it is a significant improvement over the existing situation, as it would require attacks on both the OpenTok infrastructure and Mozilla's infrastructure to launch an active interception of content.

API Changes

Joining a Room

Loop/Architecture/Rooms#Joining a Room

POST /rooms/QzBbvGmIZWU HTTP/1.1
Accept: application/json
Accept-Encoding: gzip, deflate
Content-Type: application/json; charset=utf-8
Authorization: <stripped>
Host: localhost:5000

{
    "action": "join",
    "displayName": "Adam",
    "clientMaxSize": 2,
    "features": ["fingerprint"]
}


User Identification in a Room

Loop/Architecture/Rooms#User Identification in a Room

{
    "displayName": "Alexis",
    "account": "alexis@example.com",
    "roomConnectionId": "2a1787a6-4a73-43b5-ae3e-906ec1e763cb",
    "fingerprints": []
}
  • fingerprints: A list of "fingerprint" values associated with all the PeerConnections the client currently has in use. Only included if client included "fingerprint" in features array in "join"

Joining a Room

Loop/Architecture/Rooms#Joining a Room

POST /rooms/QzBbvGmIZWU HTTP/1.1
Accept: application/json
Accept-Encoding: gzip, deflate
Content-Type: application/json; charset=utf-8
Authorization: <stripped>
Host: localhost:5000

{
    "action": "newpc",
    "fingerprint": "sha-256 15:E2:AF:50:91:87:FD:54:4C:82:F5:65:46:7A:84:D8:6C:53:00:99:C6:97:4E:64:2A:32:AA:A5:3C:91:E9:51"
}


Retrieving Room Information

Loop/Architecture/Rooms#GET_.2Frooms.2F.7Btoken.7D

HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 30
Content-Type: application/json; charset=utf-8
Date: Wed, 16 Jul 2014 13:23:04 GMT
ETag: W/"1e-2896316483"
Timestamp: 1405516984

{
    "roomToken": "3jKS_Els9IU",
    "roomName": "UX Discussion",
    "roomUrl": "http://localhost:3000/rooms/3jKS_Els9IU",
    "roomOwner": "Alexis",
    "maxSize": 2,
    "clientMaxSize": 2,
    "creationTime": 1405517546,
    "ctime": 1405517824,
    "expiresAt": 1405534180,
    "participants": [
       {
         "displayName": "Alexis",
         "account": "alexis@example.com",
         "roomConnectionId": "2a1787a6-4a73-43b5-ae3e-906ec1e763cb",
         "fingerprints": [
           "sha-256 15:E2:AF:50:91:87:FD:54:4C:82:F5:65:46:7A:84:D8:6C:53:00:99:C6:97:4E:64:2A:32:AA:A5:3C:91:E9:51",
           "sha-256 92:4B:E6:3C:DE:41:D6:F6:4A:F8:37:EC:44:3E:71:76:F3:4D:AC:7D:9C:21:6F:A9:37:5B:33:E5:9D:E2:7F:C0"
         ]
       },
       {
         "displayName": "Adam",
         "roomConnectionId": "781f012b-f1ea-4ce1-9105-7cfc36fb4ec7",
         "fingerprints": [
           "sha-256 87:C1:3C:5C:CB:D0:B6:86:3C:6E:A9:BF:CF:12:CD:F9:3F:37:95:B0:8C:3E:03:A1:6B:85:D7:B4:A4:22:1F:30",
           "sha-256 23:5E:B5:28:CF:2D:9F:D3:09:EE:E2:2F:D8:EF:DD:05:FA:FF:41:AB:1F:81:1F:73:21:E7:24:40:45:F1:8E:D4"
         ]
       }
     ]
}

Client Behavior

Roughly:

  • Monkeypatch setLocalDescription. When called:
    • POST new fingerprint to the room using "newpc" action
  • Monkeypatch setRemoteDescription. When called:
    1. Check list of fingerprints published by other person in the room
    2. If no match, perform a room GET to refresh information, and compare again
    3. If still no match, set a timer for 1 second; on expiry, try one more fetch and compare
    4. If still no match, error out the session and log an error to the server


Proof-of-Concept Monkeypatch Shim

(I presume this can be adapted to work with Chrome)

 window._originalRTCPeerConnection = window.mozRTCPeerConnection;
 
 window.mozRTCPeerConnection = function() {
   var setDescriptionShim = function(sdp, success, failure, pc, localRemote) {
     var fingerprint = /a=fingerprint:([^\r\n]*)/.exec(sdp.sdp)[1];
     console.log(localRemote + " fingerprint = " + fingerprint);
     pc["_originalSet" + localRemote + "Description"](sdp, success, failure);
   }
   var pc = new window._originalRTCPeerConnection();
   pc._originalSetLocalDescription = pc.setLocalDescription;
   pc._originalSetRemoteDescription = pc.setRemoteDescription;
   pc.setLocalDescription = function(sdp, success, failure) {
     setDescriptionShim(sdp, success, failure, pc, "Local");
   }
   pc.setRemoteDescription = function(sdp, success, failure) {
     setDescriptionShim(sdp, success, failure, pc, "Remote");
   }
   return pc;
 }