michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: "use strict"; michael@0: michael@0: const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; michael@0: michael@0: let WSP = {}; michael@0: Cu.import("resource://gre/modules/WspPduHelper.jsm", WSP); michael@0: let WBXML = {}; michael@0: Cu.import("resource://gre/modules/WbxmlPduHelper.jsm", WBXML); michael@0: michael@0: Cu.import("resource://services-crypto/utils.js"); michael@0: Cu.import("resource://services-common/utils.js"); michael@0: michael@0: // set to true to see debug messages michael@0: let DEBUG = WBXML.DEBUG_ALL | false; michael@0: michael@0: /** michael@0: * Public identifier for CP michael@0: * michael@0: * @see http://technical.openmobilealliance.org/tech/omna/omna-wbxml-public-docid.aspx michael@0: */ michael@0: const PUBLIC_IDENTIFIER_CP = "-//WAPFORUM//DTD PROV 1.0//EN"; michael@0: michael@0: this.PduHelper = { michael@0: michael@0: /** michael@0: * @param data michael@0: * A wrapped object containing raw PDU data. michael@0: * @param contentType michael@0: * Content type of incoming CP message, should be "text/vnd.wap.connectivity-xml" michael@0: * or "application/vnd.wap.connectivity-wbxml". michael@0: * michael@0: * @return A message object containing attribute content and contentType. michael@0: * |content| will contain string of decoded CP message if successfully michael@0: * decoded, or raw data if failed. michael@0: * |contentType| will be string representing corresponding type of michael@0: * content. michael@0: */ michael@0: parse: function parse_cp(data, contentType) { michael@0: // We only need content and contentType michael@0: let msg = { michael@0: contentType: contentType michael@0: }; michael@0: michael@0: /** michael@0: * Message is compressed by WBXML, decode into string. michael@0: * michael@0: * @see WAP-192-WBXML-20010725-A michael@0: */ michael@0: if (contentType === "application/vnd.wap.connectivity-wbxml") { michael@0: let appToken = { michael@0: publicId: PUBLIC_IDENTIFIER_CP, michael@0: tagTokenList: CP_TAG_FIELDS, michael@0: attrTokenList: CP_ATTRIBUTE_FIELDS, michael@0: valueTokenList: CP_VALUE_FIELDS, michael@0: globalTokenOverride: null michael@0: } michael@0: michael@0: try { michael@0: let parseResult = WBXML.PduHelper.parse(data, appToken, msg); michael@0: msg.content = parseResult.content; michael@0: msg.contentType = "text/vnd.wap.connectivity-xml"; michael@0: } catch (e) { michael@0: // Provide raw data if we failed to parse. michael@0: msg.content = data.array; michael@0: } michael@0: michael@0: return msg; michael@0: } michael@0: michael@0: /** michael@0: * Message is plain text, transform raw to string. michael@0: */ michael@0: try { michael@0: let stringData = WSP.Octet.decodeMultiple(data, data.array.length); michael@0: msg.content = WSP.PduHelper.decodeStringContent(stringData, "UTF-8"); michael@0: } catch (e) { michael@0: // Provide raw data if we failed to parse. michael@0: msg.content = data.array; michael@0: } michael@0: return msg; michael@0: michael@0: } michael@0: }; michael@0: michael@0: /** michael@0: * SEC type values michael@0: * michael@0: * @see WAP-183-ProvCont-20010724-A, clause 5.3 michael@0: */ michael@0: const AUTH_SEC_TYPE = (function () { michael@0: let names = {}; michael@0: function add(name, number) { michael@0: names[number] = name; michael@0: } michael@0: michael@0: add("NETWPIN", 0); michael@0: add("USERPIN", 1); michael@0: add("USERNETWPIN", 2); michael@0: add("USERPINMAC", 3); michael@0: michael@0: return names; michael@0: })(); michael@0: michael@0: this.Authenticator = { michael@0: /** michael@0: * Format IMSI string into GSM format michael@0: * michael@0: * @param imsi michael@0: * IMSI string michael@0: * michael@0: * @return IMSI in GSM format as string object michael@0: */ michael@0: formatImsi: function formatImsi(imsi) { michael@0: let parityByte = ((imsi.length & 1) ? 9 : 1); michael@0: michael@0: // Make sure length of IMSI is 15 digits. michael@0: // @see GSM 11.11, clause 10.2.2 michael@0: let i = 0; michael@0: for (i = 15 - imsi.length; i > 0; i--) { michael@0: imsi += "F"; michael@0: } michael@0: michael@0: // char-by-char atoi michael@0: let imsiValue = []; michael@0: imsiValue.push(parityByte); michael@0: for (i = 0; i < imsi.length; i++) { michael@0: imsiValue.push(parseInt(imsi.substr(i, 1), 10)); michael@0: } michael@0: michael@0: // encoded IMSI michael@0: let imsiEncoded = ""; michael@0: for (i = 0; i < imsiValue.length; i += 2) { michael@0: imsiEncoded += String.fromCharCode(imsiValue[i] | (imsiValue[i+1] << 4)); michael@0: } michael@0: michael@0: return imsiEncoded; michael@0: }, michael@0: michael@0: /** michael@0: * Perform HMAC check michael@0: * michael@0: * @param wbxml michael@0: * Uint8 typed array of raw WBXML data. michael@0: * @param key michael@0: * key string for HMAC check. michael@0: * @param mac michael@0: * Expected MAC value. michael@0: * michael@0: * @return true for valid, false for invalid. michael@0: */ michael@0: isValid: function isValid(wbxml, key, mac) { michael@0: let hasher = CryptoUtils.makeHMACHasher(Ci.nsICryptoHMAC.SHA1, michael@0: CryptoUtils.makeHMACKey(key)); michael@0: hasher.update(wbxml, wbxml.length); michael@0: let result = CommonUtils.bytesAsHex(hasher.finish(false)).toUpperCase(); michael@0: return mac == result; michael@0: }, michael@0: michael@0: /** michael@0: * Perform HMAC authentication. michael@0: * michael@0: * @param wbxml michael@0: * Uint8 typed array of raw WBXML data. michael@0: * @param sec michael@0: * Security method for HMAC check. michael@0: * @param mac michael@0: * Expected MAC value. michael@0: * @param getNetworkPin michael@0: * Callback function for getting network pin. michael@0: * michael@0: * @return true for valid, false for invalid. michael@0: */ michael@0: check: function check_hmac(wbxml, sec, mac, getNetworkPin) { michael@0: // No security set. michael@0: if (sec == null || !mac) { michael@0: return null; michael@0: } michael@0: michael@0: let authInfo = { michael@0: pass: false, michael@0: checked: false, michael@0: sec: AUTH_SEC_TYPE[sec], michael@0: mac: mac.toUpperCase(), michael@0: data: wbxml michael@0: }; michael@0: michael@0: switch (authInfo.sec) { michael@0: case "NETWPIN": michael@0: let key = getNetworkPin(); michael@0: authInfo.pass = this.isValid(wbxml, key, authInfo.mac); michael@0: authInfo.checked = true; michael@0: return authInfo; michael@0: michael@0: case "USERPIN": michael@0: case "USERPINMAC": michael@0: // We can't check without USER PIN michael@0: return authInfo; michael@0: michael@0: case "USERNETWPIN": michael@0: default: michael@0: return null; michael@0: } michael@0: } michael@0: }; michael@0: michael@0: /** michael@0: * Tag tokens michael@0: * michael@0: * @see OMA-WAP-TS-ProvCont-V1_1-20090421-C, clause 7.1 michael@0: */ michael@0: const CP_TAG_FIELDS = (function () { michael@0: let names = {}; michael@0: function add(name, codepage, number) { michael@0: let entry = { michael@0: name: name, michael@0: number: number, michael@0: }; michael@0: if (!names[codepage]) { michael@0: names[codepage] = {}; michael@0: } michael@0: names[codepage][number] = entry; michael@0: } michael@0: michael@0: // Code page 0 michael@0: add("wap-provisioningdoc", 0, 0x05); michael@0: add("characteristic", 0, 0x06); michael@0: add("parm", 0, 0x07); michael@0: // Code page 1 michael@0: add("characteristic", 1, 0x06); michael@0: add("parm", 1, 0x07); michael@0: michael@0: return names; michael@0: })(); michael@0: michael@0: /** michael@0: * Attribute Tokens michael@0: * michael@0: * @see OMA-WAP-TS-ProvCont-V1_1-20090421-C, clause 7.2 michael@0: */ michael@0: const CP_ATTRIBUTE_FIELDS = (function () { michael@0: let names = {}; michael@0: function add(name, value, codepage, number) { michael@0: let entry = { michael@0: name: name, michael@0: value: value, michael@0: number: number, michael@0: }; michael@0: if (!names[codepage]) { michael@0: names[codepage] = {}; michael@0: } michael@0: names[codepage][number] = entry; michael@0: } michael@0: michael@0: // Code page 0 michael@0: add("name", "", 0, 0x05); michael@0: add("value", "", 0, 0x06); michael@0: add("name", "NAME", 0, 0x07); michael@0: add("name", "NAP-ADDRESS", 0, 0x08); michael@0: add("name", "NAP-ADDRTYPE", 0, 0x09); michael@0: add("name", "CALLTYPE", 0, 0x0A); michael@0: add("name", "VALIDUNTIL", 0, 0x0B); michael@0: add("name", "AUTHTYPE", 0, 0x0C); michael@0: add("name", "AUTHNAME", 0, 0x0D); michael@0: add("name", "AUTHSECRET", 0, 0x0E); michael@0: add("name", "LINGER", 0, 0x0F); michael@0: add("name", "BEARER", 0, 0x10); michael@0: add("name", "NAPID", 0, 0x11); michael@0: add("name", "COUNTRY", 0, 0x12); michael@0: add("name", "NETWORK", 0, 0x13); michael@0: add("name", "INTERNET", 0, 0x14); michael@0: add("name", "PROXY-ID", 0, 0x15); michael@0: add("name", "PROXY-PROVIDER-ID", 0, 0x16); michael@0: add("name", "DOMAIN", 0, 0x17); michael@0: add("name", "PROVURL", 0, 0x18); michael@0: add("name", "PXAUTH-TYPE", 0, 0x19); michael@0: add("name", "PXAUTH-ID", 0, 0x1A); michael@0: add("name", "PXAUTH-PW", 0, 0x1B); michael@0: add("name", "STARTPAGE", 0, 0x1C); michael@0: add("name", "BASAUTH-ID", 0, 0x1D); michael@0: add("name", "BASAUTH-PW", 0, 0x1E); michael@0: add("name", "PUSHENABLED", 0, 0x1F); michael@0: add("name", "PXADDR", 0, 0x20); michael@0: add("name", "PXADDRTYPE", 0, 0x21); michael@0: add("name", "TO-NAPID", 0, 0x22); michael@0: add("name", "PORTNBR", 0, 0x23); michael@0: add("name", "SERVICE", 0, 0x24); michael@0: add("name", "LINKSPEED", 0, 0x25); michael@0: add("name", "DNLINKSPEED", 0, 0x26); michael@0: add("name", "LOCAL-ADDR", 0, 0x27); michael@0: add("name", "LOCAL-ADDRTYPE", 0, 0x28); michael@0: add("name", "CONTEXT-ALLOW", 0, 0x29); michael@0: add("name", "TRUST", 0, 0x2A); michael@0: add("name", "MASTER", 0, 0x2B); michael@0: add("name", "SID", 0, 0x2C); michael@0: add("name", "SOC", 0, 0x2D); michael@0: add("name", "WSP-VERSION", 0, 0x2E); michael@0: add("name", "PHYSICAL-PROXY-ID", 0, 0x2F); michael@0: add("name", "CLIENT-ID", 0, 0x30); michael@0: add("name", "DELIVERY-ERR-PDU", 0, 0x31); michael@0: add("name", "DELIVERY-ORDER", 0, 0x32); michael@0: add("name", "TRAFFIC-CLASS", 0, 0x33); michael@0: add("name", "MAX-SDU-SIZE", 0, 0x34); michael@0: add("name", "MAX-BITRATE-UPLINK", 0, 0x35); michael@0: add("name", "MAX-BITRATE-DNLINK", 0, 0x36); michael@0: add("name", "RESIDUAL-BER", 0, 0x37); michael@0: add("name", "SDU-ERROR-RATIO", 0, 0x38); michael@0: add("name", "TRAFFIC-HANDL-PRIO", 0, 0x39); michael@0: add("name", "TRANSFER-DELAY", 0, 0x3A); michael@0: add("name", "GUARANTEED-BITRATE-UPLINK", 0, 0x3B); michael@0: add("name", "GUARANTEED-BITRATE-DNLINK", 0, 0x3C); michael@0: add("name", "PXADDR-FQDN", 0, 0x3D); michael@0: add("name", "PROXY-PW", 0, 0x3E); michael@0: add("name", "PPGAUTH-TYPE", 0, 0x3F); michael@0: add("version", "", 0, 0x45); michael@0: add("version", "1.0", 0, 0x46); michael@0: add("name", "PULLENABLED", 0, 0x47); michael@0: add("name", "DNS-ADDR", 0, 0x48); michael@0: add("name", "MAX-NUM-RETRY", 0, 0x49); michael@0: add("name", "FIRST-RETRY-TIMEOUT", 0, 0x4A); michael@0: add("name", "REREG-THRESHOLD", 0, 0x4B); michael@0: add("name", "T-BIT", 0, 0x4C); michael@0: add("name", "AUTH-ENTITY", 0, 0x4E); michael@0: add("name", "SPI", 0, 0x4F); michael@0: add("type", "", 0, 0x50); michael@0: add("type", "PXLOGICAL", 0, 0x51); michael@0: add("type", "PXPHYSICAL", 0, 0x52); michael@0: add("type", "PORT", 0, 0x53); michael@0: add("type", "VALIDITY", 0, 0x54); michael@0: add("type", "NAPDEF", 0, 0x55); michael@0: add("type", "BOOTSTRAP", 0, 0x56); michael@0: /* michael@0: * Mark out VENDORCONFIG so if it is contained in message, parse michael@0: * will failed and raw data is returned. michael@0: */ michael@0: // add("type", "VENDORCONFIG", 0, 0x57); michael@0: add("type", "CLIENTIDENTITY", 0, 0x58); michael@0: add("type", "PXAUTHINFO", 0, 0x59); michael@0: add("type", "NAPAUTHINFO", 0, 0x5A); michael@0: add("type", "ACCESS", 0, 0x5B); michael@0: michael@0: // Code page 1 michael@0: add("name", "", 1, 0x05); michael@0: add("value", "", 1, 0x06); michael@0: add("name", "NAME", 1, 0x07); michael@0: add("name", "INTERNET", 1, 0x14); michael@0: add("name", "STARTPAGE", 1, 0x1C); michael@0: add("name", "TO-NAPID", 1, 0x22); michael@0: add("name", "PORTNBR", 1, 0x23); michael@0: add("name", "SERVICE", 1, 0x24); michael@0: add("name", "AACCEPT", 1, 0x2E); michael@0: add("name", "AAUTHDATA", 1, 0x2F); michael@0: add("name", "AAUTHLEVEL", 1, 0x30); michael@0: add("name", "AAUTHNAME", 1, 0x31); michael@0: add("name", "AAUTHSECRET", 1, 0x32); michael@0: add("name", "AAUTHTYPE", 1, 0x33); michael@0: add("name", "ADDR", 1, 0x34); michael@0: add("name", "ADDRTYPE", 1, 0x35); michael@0: add("name", "APPID", 1, 0x36); michael@0: add("name", "APROTOCOL", 1, 0x37); michael@0: add("name", "PROVIDER-ID", 1, 0x38); michael@0: add("name", "TO-PROXY", 1, 0x39); michael@0: add("name", "URI", 1, 0x3A); michael@0: add("name", "RULE", 1, 0x3B); michael@0: add("type", "", 1, 0x50); michael@0: add("type", "PORT", 1, 0x53); michael@0: add("type", "APPLICATION", 1, 0x55); michael@0: add("type", "APPADDR", 1, 0x56); michael@0: add("type", "APPAUTH", 1, 0x57); michael@0: add("type", "CLIENTIDENTITY", 1, 0x58); michael@0: add("type", "RESOURCE", 1, 0x59); michael@0: michael@0: return names; michael@0: })(); michael@0: michael@0: /** michael@0: * Value Tokens michael@0: * michael@0: * @see OMA-WAP-TS-ProvCont-V1_1-20090421-C, clause 7.3 michael@0: */ michael@0: const CP_VALUE_FIELDS = (function () { michael@0: let names = {}; michael@0: function add(value, codepage, number) { michael@0: let entry = { michael@0: value: value, michael@0: number: number, michael@0: }; michael@0: if (!names[codepage]) { michael@0: names[codepage] = {}; michael@0: } michael@0: names[codepage][number] = entry; michael@0: } michael@0: michael@0: // Code page 0 michael@0: add("IPV4", 0, 0x85); michael@0: add("IPV6", 0, 0x86); michael@0: add("E164", 0, 0x87); michael@0: add("ALPHA", 0, 0x88); michael@0: add("APN", 0, 0x89); michael@0: add("SCODE", 0, 0x8A); michael@0: add("TETRA-ITSI", 0, 0x8B); michael@0: add("MAN", 0, 0x8C); michael@0: add("ANALOG-MODEM", 0, 0x90); michael@0: add("V.120", 0, 0x91); michael@0: add("V.110", 0, 0x92); michael@0: add("X.31", 0, 0x93); michael@0: add("BIT-TRANSPARENT", 0, 0x94); michael@0: add("DIRECT-ASYNCHRONOUS-DATA-SERVICE", 0, 0x95); michael@0: add("PAP", 0, 0x9A); michael@0: add("CHAP", 0, 0x9B); michael@0: add("HTTP-BASIC", 0, 0x9C); michael@0: add("HTTP-DIGEST", 0, 0x9D); michael@0: add("WTLS-SS", 0, 0x9E); michael@0: add("MD5", 0, 0x9F); // Added in OMA, 7.3.3 michael@0: add("GSM-USSD", 0, 0xA2); michael@0: add("GSM-SMS", 0, 0xA3); michael@0: add("ANSI-136-GUTS", 0, 0xA4); michael@0: add("IS-95-CDMA-SMS", 0, 0xA5); michael@0: add("IS-95-CDMA-CSD", 0, 0xA6); michael@0: add("IS-95-CDMA-PAC", 0, 0xA7); michael@0: add("ANSI-136-CSD", 0, 0xA8); michael@0: add("ANSI-136-GPRS", 0, 0xA9); michael@0: add("GSM-CSD", 0, 0xAA); michael@0: add("GSM-GPRS", 0, 0xAB); michael@0: add("AMPS-CDPD", 0, 0xAC); michael@0: add("PDC-CSD", 0, 0xAD); michael@0: add("PDC-PACKET", 0, 0xAE); michael@0: add("IDEN-SMS", 0, 0xAF); michael@0: add("IDEN-CSD", 0, 0xB0); michael@0: add("IDEN-PACKET", 0, 0xB1); michael@0: add("FLEX/REFLEX", 0, 0xB2); michael@0: add("PHS-SMS", 0, 0xB3); michael@0: add("PHS-CSD", 0, 0xB4); michael@0: add("TETRA-SDS", 0, 0xB5); michael@0: add("TETRA-PACKET", 0, 0xB6); michael@0: add("ANSI-136-GHOST", 0, 0xB7); michael@0: add("MOBITEX-MPAK", 0, 0xB8); michael@0: add("CDMA2000-1X-SIMPLE-IP", 0, 0xB9); // Added in OMA, 7.3.4 michael@0: add("CDMA2000-1X-MOBILE-IP", 0, 0xBA); // Added in OMA, 7.3.4 michael@0: add("AUTOBOUDING", 0, 0xC5); michael@0: add("CL-WSP", 0, 0xCA); michael@0: add("CO-WSP", 0, 0xCB); michael@0: add("CL-SEC-WSP", 0, 0xCC); michael@0: add("CO-SEC-WSP", 0, 0xCD); michael@0: add("CL-SEC-WTA", 0, 0xCE); michael@0: add("CO-SEC-WTA", 0, 0xCF); michael@0: add("OTA-HTTP-TO", 0, 0xD0); // Added in OMA, 7.3.6 michael@0: add("OTA-HTTP-TLS-TO", 0, 0xD1); // Added in OMA, 7.3.6 michael@0: add("OTA-HTTP-PO", 0, 0xD2); // Added in OMA, 7.3.6 michael@0: add("OTA-HTTP-TLS-PO", 0, 0xD3); // Added in OMA, 7.3.6 michael@0: add("AAA", 0, 0xE0); // Added in OMA, 7.3.8 michael@0: add("HA", 0, 0xE1); // Added in OMA, 7.3.8 michael@0: michael@0: // Code page 1 michael@0: add("IPV6", 1, 0x86); michael@0: add("E164", 1, 0x87); michael@0: add("ALPHA", 1, 0x88); michael@0: add("APPSRV", 1, 0x8D); michael@0: add("OBEX", 1, 0x8E); michael@0: add(",", 1, 0x90); michael@0: add("HTTP-", 1, 0x91); michael@0: add("BASIC", 1, 0x92); michael@0: add("DIGEST", 1, 0x93); michael@0: michael@0: return names; michael@0: })(); michael@0: michael@0: let debug; michael@0: if (DEBUG) { michael@0: debug = function (s) { michael@0: dump("-$- CpPduHelper: " + s + "\n"); michael@0: }; michael@0: } else { michael@0: debug = function (s) {}; michael@0: } michael@0: michael@0: this.EXPORTED_SYMBOLS = [ michael@0: // Parser michael@0: "PduHelper", michael@0: // HMAC Authenticator michael@0: "Authenticator", michael@0: ];