Bug 914685 - 0001. Support GSM network pin authentication. r=vicamo, a=koi+
authorChuck Lee <chulee@mozilla.com>
Mon, 14 Oct 2013 14:47:50 +0800
changeset 160743 ccbb7ab73787f0b6dd9dcb7d234b554aa73d035e
parent 160742 ce3af266f47b037ea02ce0b387c90a63fca1183e
child 160744 7bbdb999c0be283ea827de4af12dbac7641bf8f3
push id2961
push userlsblakk@mozilla.com
push dateMon, 28 Oct 2013 21:59:28 +0000
treeherdermozilla-beta@73ef4f13486f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersvicamo, koi
bugs914685
milestone26.0a2
Bug 914685 - 0001. Support GSM network pin authentication. r=vicamo, a=koi+
dom/wappush/src/gonk/CpPduHelper.jsm
dom/wappush/src/gonk/WapPushManager.js
--- a/dom/wappush/src/gonk/CpPduHelper.jsm
+++ b/dom/wappush/src/gonk/CpPduHelper.jsm
@@ -6,16 +6,19 @@
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 let WSP = {};
 Cu.import("resource://gre/modules/WspPduHelper.jsm", WSP);
 let WBXML = {};
 Cu.import("resource://gre/modules/WbxmlPduHelper.jsm", WBXML);
 
+Cu.import("resource://services-crypto/utils.js");
+Cu.import("resource://services-common/utils.js");
+
 // set to true to see debug messages
 let DEBUG = WBXML.DEBUG_ALL | false;
 
 /**
  * Public identifier for CP
  *
  * @see http://technical.openmobilealliance.org/tech/omna/omna-wbxml-public-docid.aspx
  */
@@ -79,16 +82,138 @@ this.PduHelper = {
       msg.content = data.array;
     }
     return msg;
 
   }
 };
 
 /**
+ * SEC type values
+ *
+ * @see WAP-183-ProvCont-20010724-A, clause 5.3
+ */
+const AUTH_SEC_TYPE = (function () {
+  let names = {};
+  function add(name, number) {
+    names[number] = name;
+  }
+
+  add("NETWPIN",      0);
+  add("USERPIN",      1);
+  add("USERNETWPIN",  2);
+  add("USERPINMAC",   3);
+
+  return names;
+})();
+
+this.Authenticator = {
+  /**
+   * Format IMSI string into GSM format
+   *
+   * @param imsi
+   *        IMSI string
+   *
+   * @return IMSI in GSM format as string object
+   */
+  formatImsi: function formatImsi(imsi) {
+    let parityByte = ((imsi.length & 1) ? 9 : 1);
+
+    // Make sure length of IMSI is 15 digits.
+    // @see GSM 11.11, clause 10.2.2
+    let i = 0;
+    for (i = 15 - imsi.length; i > 0; i--) {
+      imsi += "F";
+    }
+
+    // char-by-char atoi
+    let imsiValue = [];
+    imsiValue.push(parityByte);
+    for (i = 0; i < imsi.length; i++) {
+      imsiValue.push(parseInt(imsi.substr(i, 1), 10));
+    }
+
+    // encoded IMSI
+    let imsiEncoded = "";
+    for (i = 0; i < imsiValue.length; i += 2) {
+      imsiEncoded += String.fromCharCode(imsiValue[i] | (imsiValue[i+1] << 4));
+    }
+
+    return imsiEncoded;
+  },
+
+  /**
+   * Perform HMAC check
+   *
+   * @param wbxml
+   *        Uint8 typed array of raw WBXML data.
+   * @param key
+   *        key string for HMAC check.
+   * @param mac
+   *        Expected MAC value.
+   *
+   * @return true for valid, false for invalid.
+   */
+  isValid: function isValid(wbxml, key, mac) {
+    let hasher = CryptoUtils.makeHMACHasher(Ci.nsICryptoHMAC.SHA1,
+                                            CryptoUtils.makeHMACKey(key));
+    hasher.update(wbxml, wbxml.length);
+    let result = CommonUtils.bytesAsHex(hasher.finish(false)).toUpperCase();
+    return mac == result;
+  },
+
+  /**
+   * Perform HMAC authentication.
+   *
+   * @param wbxml
+   *        Uint8 typed array of raw WBXML data.
+   * @param sec
+   *        Security method for HMAC check.
+   * @param mac
+   *        Expected MAC value.
+   * @param getNetworkPin
+   *        Callback function for getting network pin.
+   *
+   * @return true for valid, false for invalid.
+   */
+  check: function check_hmac(wbxml, sec, mac, getNetworkPin) {
+    // No security set.
+    if (sec == null || !mac) {
+      return null;
+    }
+
+    let authInfo = {
+      pass: false,
+      checked: false,
+      sec: AUTH_SEC_TYPE[sec],
+      mac: mac.toUpperCase(),
+      dataLength: wbxml.length,
+      data: wbxml
+    };
+
+    switch (authInfo.sec) {
+      case "NETWPIN":
+        let key = getNetworkPin();
+        authInfo.pass = this.isValid(wbxml, key, authInfo.mac);
+        authInfo.checked = true;
+        return authInfo;
+
+      case "USERPIN":
+      case "USERPINMAC":
+        // We can't check without USER PIN
+        return authInfo;
+
+      case "USERNETWPIN":
+      default:
+        return null;
+    }
+  }
+};
+
+/**
   * Tag tokens
   *
   * @see WAP-183-ProvCont-20010724-A, clause 8.1 for code page 0
   * @see OMA-WAP-TS-ProvCont-V1_1-20090421-C, clause 7.1 for code page 1
   */
 const CP_TAG_FIELDS = (function () {
   let names = {};
   function add(name, codepage, number) {
@@ -342,9 +467,11 @@ if (DEBUG) {
   };
 } else {
   debug = function (s) {};
 }
 
 this.EXPORTED_SYMBOLS = [
   // Parser
   "PduHelper",
+  // HMAC Authenticator
+  "Authenticator",
 ];
--- a/dom/wappush/src/gonk/WapPushManager.js
+++ b/dom/wappush/src/gonk/WapPushManager.js
@@ -32,16 +32,19 @@ XPCOMUtils.defineLazyGetter(this, "CP", 
   let CP = {};
   Cu.import("resource://gre/modules/CpPduHelper.jsm", CP);
   return CP;
 });
 
 XPCOMUtils.defineLazyServiceGetter(this, "gSystemMessenger",
                                    "@mozilla.org/system-message-internal;1",
                                    "nsISystemMessagesInternal");
+XPCOMUtils.defineLazyServiceGetter(this, "gRIL",
+                                   "@mozilla.org/ril;1",
+                                   "nsIRadioInterfaceLayer");
 
 /**
  * Helpers for WAP PDU processing.
  */
 this.WapPushManager = {
 
   /**
    * Parse raw PDU data and deliver to a proper target.
@@ -86,25 +89,38 @@ this.WapPushManager = {
     * SL(WBXML)           "application/vnd.wap.slc"                 "x-wap-application:wml.ua"
     * Provisioning        "text/vnd.wap.connectivity-xml"           "x-wap-application:wml.ua"
     * Provisioning(WBXML) "application/vnd.wap.connectivity-wbxml"  "x-wap-application:wml.ua"
     *
     * @see http://technical.openmobilealliance.org/tech/omna/omna-wsp-content-type.aspx
     */
     let contentType = options.headers["content-type"].media;
     let msg;
+    let authInfo = null;
 
     if (contentType === "text/vnd.wap.si" ||
         contentType === "application/vnd.wap.sic") {
       msg = SI.PduHelper.parse(data, contentType);
     } else if (contentType === "text/vnd.wap.sl" ||
                contentType === "application/vnd.wap.slc") {
       msg = SL.PduHelper.parse(data, contentType);
     } else if (contentType === "text/vnd.wap.connectivity-xml" ||
                contentType === "application/vnd.wap.connectivity-wbxml") {
+      // Apply HMAC authentication on WBXML encoded CP message.
+      if (contentType === "application/vnd.wap.connectivity-wbxml") {
+        let params = options.headers["content-type"].params;
+        let sec = params && params.sec;
+        let mac = params && params.mac;
+        authInfo = CP.Authenticator.check(data.array.subarray(data.offset),
+                                          sec, mac, function getNetworkPin() {
+          let imsi = gRIL.getRadioInterface(0).rilContext.imsi;
+          return CP.Authenticator.formatImsi(imsi);
+        });
+      }
+
       msg = CP.PduHelper.parse(data, contentType);
     } else {
       // Unsupported type, provide raw data.
       msg = {
         contentType: contentType,
         content: data.array
       };
     }
@@ -113,17 +129,18 @@ this.WapPushManager = {
     let parsedSender = PhoneNumberUtils.parse(sender);
     if (parsedSender && parsedSender.internationalNumber) {
       sender = parsedSender.internationalNumber;
     }
 
     gSystemMessenger.broadcastMessage("wappush-received", {
       sender:         sender,
       contentType:    msg.contentType,
-      content:        msg.content
+      content:        msg.content,
+      authInfo:       authInfo
     });
   },
 
   /**
    * @param array
    *        A Uint8Array or an octet array representing raw PDU data.
    * @param length
    *        Length of the array.