diff --git a/CLOBBER b/CLOBBER index bb1befee796..2cb15c63b78 100644 --- a/CLOBBER +++ b/CLOBBER @@ -22,4 +22,4 @@ # changes to stick? As of bug 928195, this shouldn't be necessary! Please # don't change CLOBBER for WebIDL changes any more. -bug 832837 removes nsISecurityWarningDialogs.idl, which requires a clobber according to bug 1114669 +Bug 879861 - Touch CLOBBER because adding a new IDL is a crapshoot these days. diff --git a/dom/secureelement/gonk/UiccConnector.js b/dom/secureelement/gonk/UiccConnector.js new file mode 100644 index 00000000000..059e63da02e --- /dev/null +++ b/dom/secureelement/gonk/UiccConnector.js @@ -0,0 +1,326 @@ +/* Copyright 2012 Mozilla Foundation and Mozilla contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Copyright © 2014, Deutsche Telekom, Inc. */ + +"use strict"; + +/* globals Components, XPCOMUtils, SE, dump, libcutils, Services, + iccProvider, SEUtils */ + +const { interfaces: Ci, utils: Cu } = Components; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/systemlibs.js"); + +XPCOMUtils.defineLazyGetter(this, "SE", function() { + let obj = {}; + Cu.import("resource://gre/modules/se_consts.js", obj); + return obj; +}); + +// set to true in se_consts.js to see debug messages +let DEBUG = SE.DEBUG_CONNECTOR; +function debug(s) { + if (DEBUG) { + dump("-*- UiccConnector: " + s + "\n"); + } +} + +XPCOMUtils.defineLazyModuleGetter(this, "SEUtils", + "resource://gre/modules/SEUtils.jsm"); + +XPCOMUtils.defineLazyServiceGetter(this, "iccProvider", + "@mozilla.org/ril/content-helper;1", + "nsIIccProvider"); + +const UICCCONNECTOR_CONTRACTID = + "@mozilla.org/secureelement/connector/uicc;1"; +const UICCCONNECTOR_CID = + Components.ID("{8e040e5d-c8c3-4c1b-ac82-c00d25d8c4a4}"); +const NS_XPCOM_SHUTDOWN_OBSERVER_ID = "xpcom-shutdown"; + +// TODO: Bug 1118099 - Add multi-sim support. +// In the Multi-sim, there is more than one client. +// For now, use default clientID as 0. Ideally, SE parent process would like to +// know which clients (uicc slot) are connected to CLF over SWP interface. +const PREFERRED_UICC_CLIENTID = + libcutils.property_get("ro.moz.se.def_client_id", "0"); + +/** + * 'UiccConnector' object is a wrapper over iccProvider's channel management + * related interfaces that implements nsISecureElementConnector interface. + */ +function UiccConnector() { + this._init(); +} + +UiccConnector.prototype = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsISecureElementConnector]), + classID: UICCCONNECTOR_CID, + classInfo: XPCOMUtils.generateCI({ + classID: UICCCONNECTOR_CID, + contractID: UICCCONNECTOR_CONTRACTID, + classDescription: "UiccConnector", + interfaces: [Ci.nsISecureElementConnector, + Ci.nsIIccListener, + Ci.nsIObserver] + }), + + _isPresent: false, + + _init: function() { + Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false); + iccProvider.registerIccMsg(PREFERRED_UICC_CLIENTID, this); + + // Update the state in order to avoid race condition. + // By this time, 'notifyCardStateChanged (with proper card state)' + // may have occurred already before this module initialization. + this._updatePresenceState(); + }, + + _shutdown: function() { + Services.obs.removeObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID); + iccProvider.unregisterIccMsg(PREFERRED_UICC_CLIENTID, this); + }, + + _updatePresenceState: function() { + // Consider following Card states as not quite ready for performing + // IccChannel* related commands + let notReadyStates = [ + "unknown", + "illegal", + "personalizationInProgress", + "permanentBlocked", + ]; + let cardState = iccProvider.getCardState(PREFERRED_UICC_CLIENTID); + this._isPresent = cardState !== null && + notReadyStates.indexOf(cardState) == -1; + }, + + // See GP Spec, 11.1.4 Class Byte Coding + _setChannelToCLAByte: function(cla, channel) { + if (channel < SE.LOGICAL_CHANNEL_NUMBER_LIMIT) { + // b7 = 0 indicates the first interindustry class byte coding + cla = (cla & 0x9C) & 0xFF | channel; + } else if (channel < SE.SUPPLEMENTARY_LOGICAL_CHANNEL_NUMBER_LIMIT) { + // b7 = 1 indicates the further interindustry class byte coding + cla = (cla & 0xB0) & 0xFF | 0x40 | (channel - SE.LOGICAL_CHANNEL_NUMBER_LIMIT); + } else { + debug("Channel number must be within [0..19]"); + return SE.ERROR_GENERIC; + } + return cla; + }, + + _doGetOpenResponse: function(channel, length, callback) { + // Le value is set. It means that this is a request for all available + // response bytes. + let cla = this._setChannelToCLAByte(SE.CLA_GET_RESPONSE, channel); + this.exchangeAPDU(channel, cla, SE.INS_GET_RESPONSE, 0x00, 0x00, + null, length, { + notifyExchangeAPDUResponse: function(sw1, sw2, response) { + debug("GET Response : " + response); + if (callback) { + callback({ + error: SE.ERROR_NONE, + sw1: sw1, + sw2: sw2, + response: response + }); + } + }, + + notifyError: function(reason) { + debug("Failed to get open response: " + + ", Rejected with Reason : " + reason); + if (callback) { + callback({ error: SE.ERROR_INVALIDAPPLICATION, reason: reason }); + } + } + }); + }, + + _doIccExchangeAPDU: function(channel, cla, ins, p1, p2, p3, + data, appendResp, callback) { + iccProvider.iccExchangeAPDU(PREFERRED_UICC_CLIENTID, channel, cla & 0xFC, + ins, p1, p2, p3, data, { + notifyExchangeAPDUResponse: (sw1, sw2, response) => { + debug("sw1 : " + sw1 + ", sw2 : " + sw2 + ", response : " + response); + + // According to ETSI TS 102 221 , Section 7.2.2.3.1, + // Enforce 'Procedure bytes' checks before notifying the callback. + // Note that 'Procedure bytes'are special cases. + // There is no need to handle '0x60' procedure byte as it implies + // no-action from SE stack perspective. This procedure byte is not + // notified to application layer. + if (sw1 === 0x6C) { + // Use the previous command header with length as second procedure + // byte (SW2) as received and repeat the procedure. + + // Recursive! and Pass empty response '' as args, since '0x6C' + // procedure does not have to deal with appended responses. + this._doIccExchangeAPDU(channel, cla, ins, p1, p2, + sw2, data, "", callback); + } else if (sw1 === 0x61) { + // Since the terminal waited for a second procedure byte and + // received it (sw2), send a GET RESPONSE command header to the UICC + // with a maximum length of 'XX', where 'XX' is the value of the + // second procedure byte (SW2). + + let claWithChannel = this._setChannelToCLAByte(SE.CLA_GET_RESPONSE, + channel); + + // Recursive, with GET RESPONSE bytes and '0x61' procedure IS interested + // in appended responses. Pass appended response and note that p3=sw2. + this._doIccExchangeAPDU(channel, claWithChannel, SE.INS_GET_RESPONSE, + 0x00, 0x00, sw2, null, + (response ? response + appendResp : appendResp), + callback); + } else if (callback) { + callback.notifyExchangeAPDUResponse(sw1, sw2, response); + } + }, + + notifyError: (reason) => { + debug("Failed to trasmit C-APDU over the channel # : " + channel + + ", Rejected with Reason : " + reason); + if (callback) { + callback.notifyError(reason); + } + } + }); + }, + + /** + * nsISecureElementConnector interface methods. + */ + + /** + * Opens a channel on a default clientId + */ + openChannel: function(aid, callback) { + if (!this._isPresent) { + callback.notifyError(SE.ERROR_NOTPRESENT); + return; + } + + // TODO: Bug 1118106: Handle Resource management / leaks by persisting + // the newly opened channel in some persistent storage so that when this + // module gets restarted (say after opening a channel) in the event of + // some erroneous conditions such as gecko restart /, crash it can read + // the persistent storage to check if there are any held resources + // (opened channels) and close them. + iccProvider.iccOpenChannel(PREFERRED_UICC_CLIENTID, aid, { + notifyOpenChannelSuccess: (channel) => { + this._doGetOpenResponse(channel, 0x00, function(result) { + if (callback) { + callback.notifyOpenChannelSuccess(channel, result.response); + } + }); + }, + + notifyError: (reason) => { + debug("Failed to open the channel to AID : " + aid + + ", Rejected with Reason : " + reason); + if (callback) { + callback.notifyError(reason); + } + } + }); + }, + + /** + * Transmit the C-APDU (command) on default clientId. + */ + exchangeAPDU: function(channel, cla, ins, p1, p2, data, le, callback) { + if (!this._isPresent) { + callback.notifyError(SE.ERROR_NOTPRESENT); + return; + } + + if (data && data.length % 2 !== 0) { + callback.notifyError("Data should be a hex string with length % 2 === 0"); + return; + } + + cla = this._setChannelToCLAByte(cla, channel); + let lc = data ? data.length / 2 : 0; + let p3 = lc || le; + + if (lc && (le !== -1)) { + data += SEUtils.byteArrayToHexString([le]); + } + + // Pass empty response '' as args as we are not interested in appended + // responses yet! + debug("exchangeAPDU on Channel # " + channel); + this._doIccExchangeAPDU(channel, cla, ins, p1, p2, p3, data, "", + callback); + }, + + /** + * Closes the channel on default clientId. + */ + closeChannel: function(channel, callback) { + if (!this._isPresent) { + callback.notifyError(SE.ERROR_NOTPRESENT); + return; + } + + iccProvider.iccCloseChannel(PREFERRED_UICC_CLIENTID, channel, { + notifyCloseChannelSuccess: function() { + debug("closeChannel successfully closed the channel # : " + channel); + if (callback) { + callback.notifyCloseChannelSuccess(); + } + }, + + notifyError: function(reason) { + debug("Failed to close the channel # : " + channel + + ", Rejected with Reason : " + reason); + if (callback) { + callback.notifyError(reason); + } + } + }); + }, + + /** + * nsIIccListener interface methods. + */ + notifyStkCommand: function() {}, + + notifyStkSessionEnd: function() {}, + + notifyIccInfoChanged: function() {}, + + notifyCardStateChanged: function() { + this._updatePresenceState(); + }, + + /** + * nsIObserver interface methods. + */ + + observe: function(subject, topic, data) { + if (topic === NS_XPCOM_SHUTDOWN_OBSERVER_ID) { + this._shutdown(); + } + } +}; + +this.NSGetFactory = XPCOMUtils.generateNSGetFactory([UiccConnector]); diff --git a/dom/secureelement/gonk/UiccConnector.manifest b/dom/secureelement/gonk/UiccConnector.manifest new file mode 100644 index 00000000000..5ac8b3b7bbc --- /dev/null +++ b/dom/secureelement/gonk/UiccConnector.manifest @@ -0,0 +1,17 @@ +# Copyright 2012 Mozilla Foundation and Mozilla contributors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# UiccConnector +component {8e040e5d-c8c3-4c1b-ac82-c00d25d8c4a4} UiccConnector.js +contract @mozilla.org/secureelement/connector/uicc;1 {8e040e5d-c8c3-4c1b-ac82-c00d25d8c4a4} diff --git a/dom/secureelement/gonk/nsISecureElementConnector.idl b/dom/secureelement/gonk/nsISecureElementConnector.idl new file mode 100644 index 00000000000..ee9bc501b8f --- /dev/null +++ b/dom/secureelement/gonk/nsISecureElementConnector.idl @@ -0,0 +1,104 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +[scriptable, uuid(1ff3f35a-1b6f-4e65-a89e-a363b8604cd7)] +interface nsISEChannelCallback : nsISupports +{ + /** + * Callback function to notify on successfully opening a logical channel. + * + * @param channel + * The Channel Number/Handle that is successfully opened. + * @param openResponse + * Response from SE for OpenChannel operation. + */ + void notifyOpenChannelSuccess(in long channel, in DOMString openResponse); + + /** + * Callback function to notify on successfully closing the logical channel. + * + */ + void notifyCloseChannelSuccess(); + + /** + * Callback function to notify the status of 'seExchangeAPDU' command. + * + * @param sw1 + * Response's First Status Byte + * @param sw2 + * Response's Second Status Byte + * @param data + * Response's data + */ + void notifyExchangeAPDUResponse(in octet sw1, + in octet sw2, + in DOMString data); + + /** + * Callback function to notify error + * + * @param error + * Error describing the reason for failure. + */ + void notifyError(in DOMString error); +}; + +[scriptable, uuid(88e23684-0de3-4792-83a0-0eb67a6ca448)] +interface nsISecureElementConnector : nsISupports +{ + /** + * Open a logical communication channel with the specific secure element type + * + * @param aid + * Application Identifier of the Card Applet on the secure element. + * @param callback + * callback to notify the result of the operation. + */ + void openChannel(in DOMString aid, + in nsISEChannelCallback callback); + + /** + * Exchanges APDU channel with the specific secure element type + * + * @param channel + * Channel on which C-APDU to be transmitted. + * @param cla + Class Byte. + * @param ins + Instruction Byte + * @param p1 + Reference parameter first byte + * @param p2 + Reference parameter second byte + * Refer to 3G TS 31.101 , 10.2 'Command APDU Structure' for all the cases. + * @param data + Sequence of C-APDU data octets + * @param le [optional] + * le is the length of expected response. If the response is not expected, + it should be explicitly set to -1. + * @param callback + * callback to notify the result of the operation. + */ + void exchangeAPDU(in long channel, + in octet cla, + in octet ins, + in octet p1, + in octet p2, + in DOMString data, + in short le, + in nsISEChannelCallback callback); + + /** + * Closes the logical communication channel to the specific secure element type + * + * @param channel + * Channel to be closed. + * @param callback + * callback to notify the result of the operation. + */ + void closeChannel(in long channel, + in nsISEChannelCallback callback); +};