Bug 1103120 - Part 16: Server: Receive OOB data from client. r=past
authorJ. Ryan Stinnett <jryans@gmail.com>
Mon, 26 Jan 2015 12:47:13 -0600
changeset 239178 e815a6d601eb0f6ea2b2b2c76dfec44a39ac86f8
parent 239177 77d19984fec7f0bce8d13dc07f65f6249e32e523
child 239179 b36599b3df09683921e220c71c5885b18bd87dc9
push id487
push userbcampen@mozilla.com
push dateMon, 26 Jan 2015 23:32:56 +0000
reviewerspast
bugs1103120
milestone38.0a1
Bug 1103120 - Part 16: Server: Receive OOB data from client. r=past
toolkit/devtools/security/auth.js
toolkit/devtools/security/socket.js
--- a/toolkit/devtools/security/auth.js
+++ b/toolkit/devtools/security/auth.js
@@ -10,16 +10,18 @@ let { Ci, Cc } = require("chrome");
 let Services = require("Services");
 let promise = require("promise");
 let DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 let { dumpn, dumpv } = DevToolsUtils;
 loader.lazyRequireGetter(this, "prompt",
   "devtools/toolkit/security/prompt");
 loader.lazyRequireGetter(this, "cert",
   "devtools/toolkit/security/cert");
+DevToolsUtils.defineLazyModuleGetter(this, "Task",
+  "resource://gre/modules/Task.jsm");
 
 /**
  * A simple enum-like object with keys mirrored to values.
  * This makes comparison to a specfic value simpler without having to repeat and
  * mis-type the value.
  */
 function createEnum(obj) {
   for (let key in obj) {
@@ -464,36 +466,80 @@ OOBCert.Server.prototype = {
    *              sha256
    *            }
    *          },
    *          transport
    *        }
    * @return An AuthenticationResult value.
    *         A promise that will be resolved to the above is also allowed.
    */
-  authenticate({ client, server, transport }) {
+  authenticate: Task.async(function*({ client, server, transport }) {
     // Step B.3 / C.3
     // TLS connection established, authentication begins
     // TODO: Bug 1032128: Consult a list of persisted, approved clients
     // Step B.4
     // Server sees that ClientCert is from a unknown client
     // Tell client they are unknown and should display OOB client UX
     transport.send({
       authResult: AuthenticationResult.PENDING
     });
 
     // Step B.5
     // User is shown a Allow / Deny / Always Allow prompt on the Server
     // with Client name and hash(ClientCert)
-    return this.allowConnection({
+    let result = yield this.allowConnection({
       authentication: this.mode,
       client,
       server
     });
-  },
+
+    switch (result) {
+      case AuthenticationResult.ALLOW_PERSIST:
+        // TODO: Bug 1032128: Persist the client
+      case AuthenticationResult.ALLOW:
+        break; // Further processing
+      default:
+        return result; // Abort for any negative results
+    }
+
+    // Examine additional data for authentication
+    let oob = yield this.receiveOOB();
+    if (!oob) {
+      dumpn("Invalid OOB data received");
+      return AuthenticationResult.DENY;
+    }
+
+    let { sha256, k } = oob;
+    // The OOB auth prompt should have transferred:
+    // hash(ClientCert) + K(random 128-bit number)
+    // from the client.
+    if (!sha256 || !k) {
+      dumpn("Invalid OOB data received");
+      return AuthenticationResult.DENY;
+    }
+
+    // Step B.10
+    // Server verifies that Client's cert matches hash(ClientCert) from
+    // out-of-band channel
+    if (client.cert.sha256 != sha256) {
+      dumpn("Client cert hash doesn't match OOB data");
+      return AuthenticationResult.DENY;
+    }
+
+    // Step B.11
+    // Server sends K to Client over TLS connection
+    transport.send({ authResult: result, k });
+
+    // Client may decide to abort if K does not match.
+    // Server's portion of authentication is now complete.
+
+    // Step B.13
+    // Debugging begins
+    return result;
+  }),
 
   /**
    * Prompt the user to accept or decline the incoming connection. The default
    * implementation is used unless this is overridden on a particular
    * authenticator instance.
    *
    * It is expected that the implementation of |allowConnection| will show a
    * prompt to the user so that they can allow or deny the connection.
@@ -516,16 +562,27 @@ OOBCert.Server.prototype = {
    *            }
    *          }
    *        }
    * @return An AuthenticationResult value.
    *         A promise that will be resolved to the above is also allowed.
    */
   allowConnection: prompt.Server.defaultAllowConnection,
 
+  /**
+   * The user must transfer some data through some out of band mechanism from
+   * the client to the server to authenticate the devices.
+   *
+   * @return An object containing:
+   *         * sha256: hash(ClientCert)
+   *         * k     : K(random 128-bit number)
+   *         A promise that will be resolved to the above is also allowed.
+   */
+  receiveOOB: null, // TODO: Added later in patch series
+
 };
 
 exports.Authenticators = {
   get(mode) {
     if (!mode) {
       mode = Prompt.mode;
     }
     for (let key in Authenticators) {
--- a/toolkit/devtools/security/socket.js
+++ b/toolkit/devtools/security/socket.js
@@ -683,16 +683,17 @@ ServerSocketConnection.prototype = {
     switch (result) {
       case AuthenticationResult.DISABLE_ALL:
         DebuggerServer.closeAllListeners();
         Services.prefs.setBoolPref("devtools.debugger.remote-enabled", false);
         return promise.reject(Cr.NS_ERROR_CONNECTION_REFUSED);
       case AuthenticationResult.DENY:
         return promise.reject(Cr.NS_ERROR_CONNECTION_REFUSED);
       case AuthenticationResult.ALLOW:
+      case AuthenticationResult.ALLOW_PERSIST:
         return promise.resolve();
       default:
         return promise.reject(Cr.NS_ERROR_CONNECTION_REFUSED);
     }
   }),
 
   deny(result) {
     let errorName = result;