Remove Gonk build directories

This commit is contained in:
wolfbeast 2018-05-12 11:09:44 +02:00 committed by Roy Tam
commit 66ea8f0645
355 changed files with 15 additions and 139695 deletions

View file

@ -1,116 +0,0 @@
/* 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/. */
/* Copyright © 2015, Deutsche Telekom, Inc. */
"use strict";
this.SEUtils = {
byteArrayToHexString: function byteArrayToHexString(array) {
let hexStr = "";
let len = array ? array.length : 0;
for (let i = 0; i < len; i++) {
let hex = (array[i] & 0xff).toString(16);
hex = (hex.length === 1) ? "0" + hex : hex;
hexStr += hex;
}
return hexStr.toUpperCase();
},
hexStringToByteArray: function hexStringToByteArray(hexStr) {
if (typeof hexStr !== "string" || hexStr.length % 2 !== 0) {
return [];
}
let array = [];
for (let i = 0, len = hexStr.length; i < len; i += 2) {
array.push(parseInt(hexStr.substr(i, 2), 16));
}
return array;
},
arraysEqual: function arraysEqual(a1, a2) {
if (!a1 || !a2) {
return false;
}
if (a1.length !== a2.length) {
return false;
}
for (let i = 0, len = a1.length; i < len; i++) {
if (a1[i] !== a2[i]) {
return false;
}
}
return true;
},
ensureIsArray: function ensureIsArray(obj) {
return Array.isArray(obj) ? obj : [obj];
},
/**
* parseTLV is intended primarily to be used to parse Global Platform Device
* Technology secure element access control data.
*
* The parsed result value is an internal format only.
*
* All tags will be treated as simple Tag Length Values (TLV), (i.e. with a
* plain value, not subject to further unpacking), unless those tags are
* listed in the containerTags array.
*
* @param bytes - byte array
* @param containerTags - byte array of tags
*/
parseTLV: function parseTLV(bytes, containerTags) {
let result = {};
if (typeof bytes === "string") {
bytes = this.hexStringToByteArray(bytes);
}
if (!Array.isArray(bytes)) {
debug("Passed value is not an array nor a string.");
return null;
}
for (let pos = 0; pos < bytes.length; ) {
let tag = bytes[pos],
length = bytes[pos + 1],
value = bytes.slice(pos + 2, pos + 2 + length),
parsed = null;
// Support for 0xFF padded files (GPD 7.1.2)
if (tag === 0xFF) {
break;
}
if (containerTags.indexOf(tag) >= 0) {
parsed = this.parseTLV(value, containerTags);
} else {
parsed = value;
}
// Internal parsed format.
if (!result[tag]) {
result[tag] = parsed;
} else if (Array.isArray(result[tag])) {
result[tag].push(parsed);
} else {
result[tag] = [result[tag], parsed];
}
pos = pos + 2 + length;
}
return result;
}
};
this.EXPORTED_SYMBOLS = ["SEUtils"];

View file

@ -1,139 +0,0 @@
/* 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/. */
/* Copyright © 2015, Deutsche Telekom, Inc. */
"use strict";
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "SEUtils",
"resource://gre/modules/SEUtils.jsm");
XPCOMUtils.defineLazyGetter(this, "SE", function() {
let obj = {};
Cu.import("resource://gre/modules/se_consts.js", obj);
return obj;
});
var DEBUG = SE.DEBUG_ACE;
function debug(msg) {
if (DEBUG) {
dump("ACEservice: " + msg + "\n");
}
}
/**
* Implements decision making algorithm as described in GPD specification,
* mostly in 3.1, 3.2 and 4.2.3.
*
* TODO: Bug 1137533: Implement GPAccessRulesManager APDU filters
*/
function GPAccessDecision(rules, certHash, aid) {
this.rules = rules;
this.certHash = certHash;
this.aid = aid;
}
GPAccessDecision.prototype = {
isAccessAllowed: function isAccessAllowed() {
// GPD SE Access Control v1.1, 3.4.1, Table 3-2: (Conflict resolution)
// If a specific rule allows, all other non-specific access is denied.
// Conflicting specific rules will resolve to the first Allowed == "true"
// match. Given no specific rule, the global "All" rules will determine
// access. "Some", skips further processing if access Allowed == "true".
//
// Access must be decided before the SE connector openChannel, and the
// exchangeAPDU call.
//
// NOTE: This implementation may change with the introduction of APDU
// filters.
let decision = this.rules.some(this._decideAppAccess.bind(this));
return decision;
},
_decideAppAccess: function _decideAppAccess(rule) {
let appMatched, appletMatched;
// GPD SE AC 4.2.3: Algorithm for Applying Rules
// Specific rule overrides global rule.
//
// DeviceAppID is the application hash, and the AID is SE Applet ID:
//
// GPD SE AC 4.2.3 A:
// SearchRuleFor(DeviceAppID, AID)
// GPD SE AC 4.2.3 B: If no rule fits A:
// SearchRuleFor(<AllDeviceApplications>, AID)
// GPD SE AC 4.2.3 C: If no rule fits A or B:
// SearchRuleFor(DeviceAppID, <AllSEApplications>)
// GPD SE AC 4.2.3 D: If no rule fits A, B, or C:
// SearchRuleFor(<AllDeviceApplications>, <AllSEApplications>)
// Device App
appMatched = Array.isArray(rule.application) ?
// GPD SE AC 4.2.3 A and 4.2.3 C (DeviceAppID rule)
this._appCertHashMatches(rule.application) :
// GPD SE AC 4.2.3 B and 4.2.3 D (All Device Applications)
rule.application === Ci.nsIAccessRulesManager.ALLOW_ALL;
if (!appMatched) {
return false; // bail out early.
}
// SE Applet
appletMatched = Array.isArray(rule.applet) ?
// GPD SE AC 4.2.3 A and 4.2.3 B (AID rule)
SEUtils.arraysEqual(rule.applet, this.aid) :
// GPD SE AC 4.2.3 C and 4.2.3 D (All AID)
rule.applet === Ci.nsIAccessRulesManager.ALL_APPLET;
return appletMatched;
},
_appCertHashMatches: function _appCertHashMatches(hashArray) {
if (!Array.isArray(hashArray)) {
return false;
}
return !!(hashArray.find((hash) => {
return SEUtils.arraysEqual(hash, this.certHash);
}));
}
};
function ACEService() {
this._rulesManagers = new Map();
this._rulesManagers.set(
SE.TYPE_UICC,
Cc["@mozilla.org/secureelement/access-control/rules-manager;1"]
.createInstance(Ci.nsIAccessRulesManager));
}
ACEService.prototype = {
_rulesManagers: null,
isAccessAllowed: function isAccessAllowed(localId, seType, aid) {
if(!Services.prefs.getBoolPref("devtools.debugger.forbid-certified-apps")) {
debug("Certified apps debug enabled, allowing access");
return Promise.resolve(true);
}
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
},
_getDevCertHashForApp: function getDevCertHashForApp(manifestURL) {
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
},
classID: Components.ID("{882a7463-2ca7-4d61-a89a-10eb6fd70478}"),
contractID: "@mozilla.org/secureelement/access-control/ace;1",
QueryInterface: XPCOMUtils.generateQI([Ci.nsIAccessControlEnforcer])
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ACEService]);

View file

@ -1,2 +0,0 @@
component {882a7463-2ca7-4d61-a89a-10eb6fd70478} ACEService.js
contract @mozilla.org/secureelement/access-control/ace;1 {882a7463-2ca7-4d61-a89a-10eb6fd70478}

View file

@ -1,436 +0,0 @@
/* 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/. */
/* Copyright © 2015, Deutsche Telekom, Inc. */
"use strict";
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/systemlibs.js");
XPCOMUtils.defineLazyServiceGetter(this, "UiccConnector",
"@mozilla.org/secureelement/connector/uicc;1",
"nsISecureElementConnector");
XPCOMUtils.defineLazyModuleGetter(this, "SEUtils",
"resource://gre/modules/SEUtils.jsm");
XPCOMUtils.defineLazyGetter(this, "SE", function() {
let obj = {};
Cu.import("resource://gre/modules/se_consts.js", obj);
return obj;
});
XPCOMUtils.defineLazyGetter(this, "GP", function() {
let obj = {};
Cu.import("resource://gre/modules/gp_consts.js", obj);
return obj;
});
var DEBUG = SE.DEBUG_ACE;
function debug(msg) {
if (DEBUG) {
dump("-*- GPAccessRulesManager " + msg);
}
}
/**
* Based on [1] - "GlobalPlatform Device Technology
* Secure Element Access Control Version 1.0".
* GPAccessRulesManager reads and parses access rules from SE file system
* as defined in section #7 of [1]: "Structure of Access Rule Files (ARF)".
* Rules retrieval from ARA-M applet is not implmented due to lack of
* commercial implemenations of ARA-M.
* @todo Bug 1137537: Implement ARA-M support according to section #4 of [1]
*/
function GPAccessRulesManager() {}
GPAccessRulesManager.prototype = {
// source [1] section 7.1.3 PKCS#15 Selection
PKCS_AID: "a000000063504b43532d3135",
// APDUs (ISO 7816-4) for accessing rules on SE file system
// see for more details: http://www.cardwerk.com/smartcards/
// smartcard_standard_ISO7816-4_6_basic_interindustry_commands.aspx
READ_BINARY: [GP.CLA_SM, GP.INS_RB, GP.P1_RB, GP.P2_RB],
GET_RESPONSE: [GP.CLA_SM, GP.INS_GR, GP.P1_GR, GP.P2_GR],
SELECT_BY_DF: [GP.CLA_SM, GP.INS_SF, GP.P1_SF_DF, GP.P2_SF_FCP],
// Non-null if there is a channel open
channel: null,
// Refresh tag path in the acMain file as described in GPD spec,
// sections 7.1.5 and C.1.
REFRESH_TAG_PATH: [GP.TAG_SEQUENCE, GP.TAG_OCTETSTRING],
refreshTag: null,
// Contains rules as read from the SE
rules: [],
// Returns the latest rules. Results are cached.
getAccessRules: function getAccessRules() {
debug("getAccessRules");
return new Promise((resolve, reject) => {
this._readAccessRules(() => resolve(this.rules));
});
},
_readAccessRules: Task.async(function*(done) {
try {
yield this._openChannel(this.PKCS_AID);
let odf = yield this._readODF();
let dodf = yield this._readDODF(odf);
let acmf = yield this._readACMF(dodf);
let refreshTag = acmf[this.REFRESH_TAG_PATH[0]]
[this.REFRESH_TAG_PATH[1]];
// Update cached rules based on refreshTag.
if (SEUtils.arraysEqual(this.refreshTag, refreshTag)) {
debug("_readAccessRules: refresh tag equals to the one saved.");
yield this._closeChannel();
return done();
}
this.refreshTag = refreshTag;
debug("_readAccessRules: refresh tag saved: " + this.refreshTag);
let acrf = yield this._readACRules(acmf);
let accf = yield this._readACConditions(acrf);
this.rules = yield this._parseRules(acrf, accf);
DEBUG && debug("_readAccessRules: " + JSON.stringify(this.rules, 0, 2));
yield this._closeChannel();
done();
} catch (error) {
debug("_readAccessRules: " + error);
this.rules = [];
yield this._closeChannel();
done();
}
}),
_openChannel: function _openChannel(aid) {
if (this.channel !== null) {
debug("_openChannel: Channel already opened, rejecting.");
return Promise.reject();
}
return new Promise((resolve, reject) => {
UiccConnector.openChannel(aid, {
notifyOpenChannelSuccess: (channel, openResponse) => {
debug("_openChannel/notifyOpenChannelSuccess: Channel " + channel +
" opened, open response: " + openResponse);
this.channel = channel;
resolve();
},
notifyError: (error) => {
debug("_openChannel/notifyError: failed to open channel, error: " +
error);
reject(error);
}
});
});
},
_closeChannel: function _closeChannel() {
if (this.channel === null) {
debug("_closeChannel: Channel not opened, rejecting.");
return Promise.reject();
}
return new Promise((resolve, reject) => {
UiccConnector.closeChannel(this.channel, {
notifyCloseChannelSuccess: () => {
debug("_closeChannel/notifyCloseChannelSuccess: chanel " +
this.channel + " closed");
this.channel = null;
resolve();
},
notifyError: (error) => {
debug("_closeChannel/notifyError: error closing channel, error" +
error);
reject(error);
}
});
});
},
_exchangeAPDU: function _exchangeAPDU(bytes) {
DEBUG && debug("apdu " + JSON.stringify(bytes));
let apdu = this._bytesToAPDU(bytes);
return new Promise((resolve, reject) => {
UiccConnector.exchangeAPDU(this.channel, apdu.cla,
apdu.ins, apdu.p1, apdu.p2, apdu.data, apdu.le,
{
notifyExchangeAPDUResponse: (sw1, sw2, data) => {
debug("APDU response is " + sw1.toString(16) + sw2.toString(16) +
" data: " + data);
// 90 00 is "success"
if (sw1 !== 0x90 && sw2 !== 0x00) {
debug("rejecting APDU response");
reject(new Error("Response " + sw1 + "," + sw2));
return;
}
resolve(this._parseTLV(data));
},
notifyError: (error) => {
debug("_exchangeAPDU/notifyError " + error);
reject(error);
}
}
);
});
},
_readBinaryFile: function _readBinaryFile(selectResponse) {
DEBUG && debug("Select response: " + JSON.stringify(selectResponse));
// 0x80 tag parameter - get the elementary file (EF) length
// without structural information.
let fileLength = selectResponse[GP.TAG_FCP][0x80];
// If file is empty, no need to attempt to read it.
if (fileLength[0] === 0 && fileLength[1] === 0) {
return Promise.resolve(null);
}
// TODO READ BINARY with filelength not supported
// let readApdu = this.READ_BINARY.concat(fileLength);
return this._exchangeAPDU(this.READ_BINARY);
},
_selectAndRead: function _selectAndRead(df) {
return this._exchangeAPDU(this.SELECT_BY_DF.concat(df.length & 0xFF, df))
.then((resp) => this._readBinaryFile(resp));
},
_readODF: function _readODF() {
debug("_readODF");
return this._selectAndRead(GP.ODF_DF);
},
_readDODF: function _readDODF(odfFile) {
debug("_readDODF, ODF file: " + odfFile);
// Data Object Directory File (DODF) is used as an entry point to the
// Access Control data. It is specified in PKCS#15 section 6.7.6.
// DODF is referenced by the ODF file, which looks as follows:
// A7 06
// 30 04
// 04 02 XY WZ
// where [0xXY, 0xWZ] is a DF of DODF file.
let DODF_DF = odfFile[GP.TAG_EF_ODF][GP.TAG_SEQUENCE][GP.TAG_OCTETSTRING];
return this._selectAndRead(DODF_DF);
},
_readACMF: function _readACMF(dodfFile) {
debug("_readACMF, DODF file: " + dodfFile);
// ACMF file DF is referenced in DODF file, which looks like this:
//
// A1 29
// 30 00
// 30 0F
// 0C 0D 47 50 20 53 45 20 41 63 63 20 43 74 6C
// A1 14
// 30 12
// 06 0A 2A 86 48 86 FC 6B 81 48 01 01 <-- GPD registered OID
// 30 04
// 04 02 AB CD <-- ACMF DF
// A1 2B
// 30 00
// 30 0F
// 0C 0D 53 41 54 53 41 20 47 54 4F 20 31 2E 31
// A1 16
// 30 14
// 06 0C 2B 06 01 04 01 2A 02 6E 03 01 01 01 <-- some other OID
// 30 04
// 04 02 XY WZ <-- some other file's DF
//
// DODF file consists of DataTypes with oidDO entries. Entry with OID
// equal to "1.2.840.114283.200.1.1" ("2A 86 48 86 FC 6B 81 48 01 01")
// contains DF of the ACMF. In the file above, it means that ACMF DF
// equals to [0xAB, 0xCD], and not [0xXY, 0xWZ].
//
// Algorithm used to encode OID to an byte array:
// http://www.snmpsharpnet.com/?p=153
let gpdOid = [0x2A, // 1.2
0x86, 0x48, // 840
0x86, 0xFC, 0x6B, // 114283
0x81, 0x48, // 129
0x01, // 1
0x01]; // 1
let records = SEUtils.ensureIsArray(dodfFile[GP.TAG_EXTERNALDO]);
// Look for the OID registered for GPD SE.
let gpdRecords = records.filter((record) => {
let oid = record[GP.TAG_EXTERNALDO][GP.TAG_SEQUENCE][GP.TAG_OID];
return SEUtils.arraysEqual(oid, gpdOid);
});
// [1] 7.1.5: "There shall be only one ACMF file per Secure Element.
// If a Secure Element contains several ACMF files, then the security shall
// be considered compromised and the Access Control enforcer shall forbid
// access to all (...) apps."
if (gpdRecords.length !== 1) {
return Promise.reject(new Error(gpdRecords.length + " ACMF files found"));
}
let ACMain_DF = gpdRecords[0][GP.TAG_EXTERNALDO][GP.TAG_SEQUENCE]
[GP.TAG_SEQUENCE][GP.TAG_OCTETSTRING];
return this._selectAndRead(ACMain_DF);
},
_readACRules: function _readACRules(acMainFile) {
debug("_readACRules, ACMain file: " + acMainFile);
// ACMF looks like this:
//
// 30 10
// 04 08 XX XX XX XX XX XX XX XX
// 30 04
// 04 02 XY WZ
//
// where [XY, WZ] is a DF of ACRF, and XX XX XX XX XX XX XX XX is a refresh
// tag.
let ACRules_DF = acMainFile[GP.TAG_SEQUENCE][GP.TAG_SEQUENCE][GP.TAG_OCTETSTRING];
return this._selectAndRead(ACRules_DF);
},
_readACConditions: function _readACConditions(acRulesFile) {
debug("_readACCondition, ACRules file: " + acRulesFile);
let acRules = SEUtils.ensureIsArray(acRulesFile[GP.TAG_SEQUENCE]);
if (acRules.length === 0) {
debug("No rules found in ACRules file.");
return Promise.reject(new Error("No rules found in ACRules file"));
}
// We first read all the condition files referenced in the ACRules file,
// because ACRules file might reference one ACCondition file more than
// once. Since reading it isn't exactly fast, we optimize here.
let acReadQueue = Promise.resolve({});
acRules.forEach((ruleEntry) => {
let df = ruleEntry[GP.TAG_SEQUENCE][GP.TAG_OCTETSTRING];
// Promise chain read condition entries:
let readAcCondition = (acConditionFiles) => {
if (acConditionFiles[df] !== undefined) {
debug("Skipping previously read acCondition df: " + df);
return acConditionFiles;
}
return this._selectAndRead(df)
.then((acConditionFileContents) => {
acConditionFiles[df] = acConditionFileContents;
return acConditionFiles;
});
}
acReadQueue = acReadQueue.then(readAcCondition);
});
return acReadQueue;
},
_parseRules: function _parseRules(acRulesFile, acConditionFiles) {
DEBUG && debug("_parseRules: acConditionFiles " + JSON.stringify(acConditionFiles));
let rules = [];
let acRules = SEUtils.ensureIsArray(acRulesFile[GP.TAG_SEQUENCE]);
acRules.forEach((ruleEntry) => {
DEBUG && debug("Parsing one rule: " + JSON.stringify(ruleEntry));
let rule = {};
// 0xA0 and 0x82 tags as per GPD spec sections C.1 - C.3. 0xA0 means
// that rule describes access to one SE applet only (and its AID is
// given). 0x82 means that rule describes acccess to all SE applets.
let oneApplet = ruleEntry[GP.TAG_GPD_AID];
let allApplets = ruleEntry[GP.TAG_GPD_ALL];
if (oneApplet) {
rule.applet = oneApplet[GP.TAG_OCTETSTRING];
} else if (allApplets) {
rule.applet = Ci.nsIAccessRulesManager.ALL_APPLET;
} else {
throw Error("Unknown applet definition");
}
let df = ruleEntry[GP.TAG_SEQUENCE][GP.TAG_OCTETSTRING];
let condition = acConditionFiles[df];
if (condition === null) {
rule.application = Ci.nsIAccessRulesManager.DENY_ALL;
} else if (condition[GP.TAG_SEQUENCE]) {
if (!Array.isArray(condition[GP.TAG_SEQUENCE]) &&
!condition[GP.TAG_SEQUENCE][GP.TAG_OCTETSTRING]) {
rule.application = Ci.nsIAccessRulesManager.ALLOW_ALL;
} else {
rule.application = SEUtils.ensureIsArray(condition[GP.TAG_SEQUENCE])
.map((conditionEntry) => {
return conditionEntry[GP.TAG_OCTETSTRING];
});
}
} else {
throw Error("Unknown application definition");
}
DEBUG && debug("Rule parsed, adding to the list: " + JSON.stringify(rule));
rules.push(rule);
});
DEBUG && debug("All rules parsed, we have those in total: " + JSON.stringify(rules));
return rules;
},
_parseTLV: function _parseTLV(bytes) {
let containerTags = [
GP.TAG_SEQUENCE,
GP.TAG_FCP,
GP.TAG_GPD_AID,
GP.TAG_EXTERNALDO,
GP.TAG_INDIRECT,
GP.TAG_EF_ODF
];
return SEUtils.parseTLV(bytes, containerTags);
},
// TODO consider removing if better format for storing
// APDU consts will be introduced
_bytesToAPDU: function _bytesToAPDU(arr) {
let apdu = {
cla: arr[0] & 0xFF,
ins: arr[1] & 0xFF,
p1: arr[2] & 0xFF,
p2: arr[3] & 0xFF,
p3: arr[4] & 0xFF,
le: 0
};
let data = (apdu.p3 > 0) ? (arr.slice(5)) : [];
apdu.data = (data.length) ? SEUtils.byteArrayToHexString(data) : null;
return apdu;
},
classID: Components.ID("{3e046b4b-9e66-439a-97e0-98a69f39f55f}"),
contractID: "@mozilla.org/secureelement/access-control/rules-manager;1",
QueryInterface: XPCOMUtils.generateQI([Ci.nsIAccessRulesManager])
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([GPAccessRulesManager]);

View file

@ -1,2 +0,0 @@
component {3e046b4b-9e66-439a-97e0-98a69f39f55f} GPAccessRulesManager.js
contract @mozilla.org/secureelement/access-control/rules-manager;1 {3e046b4b-9e66-439a-97e0-98a69f39f55f}

View file

@ -1,514 +0,0 @@
/* 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 dump, Components, XPCOMUtils, SE, Services, UiccConnector,
SEUtils, ppmm, gMap, UUIDGenerator */
const { classes: Cc, 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", () => {
let obj = {};
Cu.import("resource://gre/modules/se_consts.js", obj);
return obj;
});
// set to true in se_consts.js to see debug messages
var DEBUG = SE.DEBUG_SE;
function debug(s) {
if (DEBUG) {
dump("-*- SecureElement: " + s + "\n");
}
}
const SE_IPC_SECUREELEMENT_MSG_NAMES = [
"SE:GetSEReaders",
"SE:OpenChannel",
"SE:CloseChannel",
"SE:TransmitAPDU"
];
const SECUREELEMENTMANAGER_CONTRACTID =
"@mozilla.org/secureelement/parent-manager;1";
const SECUREELEMENTMANAGER_CID =
Components.ID("{48f4e650-28d2-11e4-8c21-0800200c9a66}");
const NS_XPCOM_SHUTDOWN_OBSERVER_ID = "xpcom-shutdown";
XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
"@mozilla.org/parentprocessmessagemanager;1",
"nsIMessageBroadcaster");
XPCOMUtils.defineLazyServiceGetter(this, "UUIDGenerator",
"@mozilla.org/uuid-generator;1",
"nsIUUIDGenerator");
XPCOMUtils.defineLazyModuleGetter(this, "SEUtils",
"resource://gre/modules/SEUtils.jsm");
XPCOMUtils.defineLazyGetter(this, "UiccConnector", () => {
let uiccClass = Cc["@mozilla.org/secureelement/connector/uicc;1"];
return uiccClass ? uiccClass.getService(Ci.nsISecureElementConnector) : null;
});
function getConnector(type) {
switch (type) {
case SE.TYPE_UICC:
return UiccConnector;
case SE.TYPE_ESE:
default:
debug("Unsupported SEConnector : " + type);
return null;
}
}
/**
* 'gMap' is a nested dictionary object that manages all the information
* pertaining to channels for a given application (appId). It manages the
* relationship between given application and its opened channels.
*/
XPCOMUtils.defineLazyGetter(this, "gMap", function() {
return {
// example structure of AppInfoMap
// {
// "appId1": {
// target: target1,
// channels: {
// "channelToken1": {
// seType: "uicc",
// aid: "aid1",
// channelNumber: 1
// },
// "channelToken2": { ... }
// }
// },
// "appId2": { ... }
// }
appInfoMap: {},
registerSecureElementTarget: function(appId, target) {
if (this.isAppIdRegistered(appId)) {
debug("AppId: " + appId + "already registered");
return;
}
this.appInfoMap[appId] = {
target: target,
channels: {}
};
debug("Registered a new SE target " + appId);
},
unregisterSecureElementTarget: function(target) {
let appId = Object.keys(this.appInfoMap).find((id) => {
return this.appInfoMap[id].target === target;
});
if (!appId) {
return;
}
debug("Unregistered SE Target for AppId: " + appId);
delete this.appInfoMap[appId];
},
isAppIdRegistered: function(appId) {
return this.appInfoMap[appId] !== undefined;
},
getChannelCountByAppIdType: function(appId, type) {
return Object.keys(this.appInfoMap[appId].channels)
.reduce((cnt, ch) => ch.type === type ? ++cnt : cnt, 0);
},
// Add channel to the appId. Upon successfully adding the entry
// this function will return the 'token'
addChannel: function(appId, type, aid, channelNumber) {
let token = UUIDGenerator.generateUUID().toString();
this.appInfoMap[appId].channels[token] = {
seType: type,
aid: aid,
channelNumber: channelNumber
};
return token;
},
removeChannel: function(appId, channelToken) {
if (this.appInfoMap[appId].channels[channelToken]) {
debug("Deleting channel with token : " + channelToken);
delete this.appInfoMap[appId].channels[channelToken];
}
},
getChannel: function(appId, channelToken) {
if (!this.appInfoMap[appId].channels[channelToken]) {
return null;
}
return this.appInfoMap[appId].channels[channelToken];
},
getChannelsByTarget: function(target) {
let appId = Object.keys(this.appInfoMap).find((id) => {
return this.appInfoMap[id].target === target;
});
if (!appId) {
return [];
}
return Object.keys(this.appInfoMap[appId].channels)
.map(token => this.appInfoMap[appId].channels[token]);
},
getTargets: function() {
return Object.keys(this.appInfoMap)
.map(appId => this.appInfoMap[appId].target);
},
};
});
/**
* 'SecureElementManager' is the main object that handles IPC messages from
* child process. It interacts with other objects such as 'gMap' & 'Connector
* instances (UiccConnector, eSEConnector)' to perform various
* SE-related (open, close, transmit) operations.
* @TODO: Bug 1118097 Support slot based SE/reader names
* @TODO: Bug 1118101 Introduce SE type specific permissions
*/
function SecureElementManager() {
this._registerMessageListeners();
this._registerSEListeners();
Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
this._acEnforcer =
Cc["@mozilla.org/secureelement/access-control/ace;1"]
.getService(Ci.nsIAccessControlEnforcer);
}
SecureElementManager.prototype = {
QueryInterface: XPCOMUtils.generateQI([
Ci.nsIMessageListener,
Ci.nsISEListener,
Ci.nsIObserver]),
classID: SECUREELEMENTMANAGER_CID,
classInfo: XPCOMUtils.generateCI({
classID: SECUREELEMENTMANAGER_CID,
classDescription: "SecureElementManager",
interfaces: [Ci.nsIMessageListener,
Ci.nsISEListener,
Ci.nsIObserver]
}),
// Stores information about supported SE types and their presence.
// key: secure element type, value: (Boolean) is present/accessible
_sePresence: {},
_acEnforcer: null,
_shutdown: function() {
this._acEnforcer = null;
this.secureelement = null;
Services.obs.removeObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
this._unregisterMessageListeners();
this._unregisterSEListeners();
},
_registerMessageListeners: function() {
ppmm.addMessageListener("child-process-shutdown", this);
for (let msgname of SE_IPC_SECUREELEMENT_MSG_NAMES) {
ppmm.addMessageListener(msgname, this);
}
},
_unregisterMessageListeners: function() {
ppmm.removeMessageListener("child-process-shutdown", this);
for (let msgname of SE_IPC_SECUREELEMENT_MSG_NAMES) {
ppmm.removeMessageListener(msgname, this);
}
ppmm = null;
},
_registerSEListeners: function() {
let connector = getConnector(SE.TYPE_UICC);
if (!connector) {
return;
}
this._sePresence[SE.TYPE_UICC] = false;
connector.registerListener(this);
},
_unregisterSEListeners: function() {
Object.keys(this._sePresence).forEach((type) => {
let connector = getConnector(type);
if (connector) {
connector.unregisterListener(this);
}
});
this._sePresence = {};
},
notifySEPresenceChanged: function(type, isPresent) {
// we need to notify all targets, even those without open channels,
// app could've stored the reader without actually using it
debug("notifying DOM about SE state change");
this._sePresence[type] = isPresent;
gMap.getTargets().forEach(target => {
let result = { type: type, isPresent: isPresent };
target.sendAsyncMessage("SE:ReaderPresenceChanged", { result: result });
});
},
_canOpenChannel: function(appId, type) {
let opened = gMap.getChannelCountByAppIdType(appId, type);
let limit = SE.MAX_CHANNELS_ALLOWED_PER_SESSION;
// UICC basic channel is not accessible see comment in se_consts.js
limit = type === SE.TYPE_UICC ? limit - 1 : limit;
return opened < limit;
},
_handleOpenChannel: function(msg, callback) {
if (!this._canOpenChannel(msg.appId, msg.type)) {
debug("Max channels per session exceed");
callback({ error: SE.ERROR_GENERIC });
return;
}
let connector = getConnector(msg.type);
if (!connector) {
debug("No SE connector available");
callback({ error: SE.ERROR_NOTPRESENT });
return;
}
this._acEnforcer.isAccessAllowed(msg.appId, msg.type, msg.aid)
.then((allowed) => {
if (!allowed) {
callback({ error: SE.ERROR_SECURITY });
return;
}
connector.openChannel(SEUtils.byteArrayToHexString(msg.aid), {
notifyOpenChannelSuccess: (channelNumber, openResponse) => {
// Add the new 'channel' to the map upon success
let channelToken =
gMap.addChannel(msg.appId, msg.type, msg.aid, channelNumber);
if (channelToken) {
callback({
error: SE.ERROR_NONE,
channelToken: channelToken,
isBasicChannel: (channelNumber === SE.BASIC_CHANNEL),
openResponse: SEUtils.hexStringToByteArray(openResponse)
});
} else {
callback({ error: SE.ERROR_GENERIC });
}
},
notifyError: (reason) => {
debug("Failed to open the channel to AID : " +
SEUtils.byteArrayToHexString(msg.aid) +
", Rejected with Reason : " + reason);
callback({ error: SE.ERROR_GENERIC, reason: reason, response: [] });
}
});
})
.catch((error) => {
debug("Failed to get info from accessControlEnforcer " + error);
callback({ error: SE.ERROR_SECURITY });
});
},
_handleTransmit: function(msg, callback) {
let channel = gMap.getChannel(msg.appId, msg.channelToken);
if (!channel) {
debug("Invalid token:" + msg.channelToken + ", appId: " + msg.appId);
callback({ error: SE.ERROR_GENERIC });
return;
}
let connector = getConnector(channel.seType);
if (!connector) {
debug("No SE connector available");
callback({ error: SE.ERROR_NOTPRESENT });
return;
}
// Bug 1137533 - ACE GPAccessRulesManager APDU filters
connector.exchangeAPDU(channel.channelNumber, msg.apdu.cla, msg.apdu.ins,
msg.apdu.p1, msg.apdu.p2,
SEUtils.byteArrayToHexString(msg.apdu.data),
msg.apdu.le, {
notifyExchangeAPDUResponse: (sw1, sw2, response) => {
callback({
error: SE.ERROR_NONE,
sw1: sw1,
sw2: sw2,
response: SEUtils.hexStringToByteArray(response)
});
},
notifyError: (reason) => {
debug("Transmit failed, rejected with Reason : " + reason);
callback({ error: SE.ERROR_INVALIDAPPLICATION, reason: reason });
}
});
},
_handleCloseChannel: function(msg, callback) {
let channel = gMap.getChannel(msg.appId, msg.channelToken);
if (!channel) {
debug("Invalid token:" + msg.channelToken + ", appId:" + msg.appId);
callback({ error: SE.ERROR_GENERIC });
return;
}
let connector = getConnector(channel.seType);
if (!connector) {
debug("No SE connector available");
callback({ error: SE.ERROR_NOTPRESENT });
return;
}
connector.closeChannel(channel.channelNumber, {
notifyCloseChannelSuccess: () => {
gMap.removeChannel(msg.appId, msg.channelToken);
callback({ error: SE.ERROR_NONE });
},
notifyError: (reason) => {
debug("Failed to close channel with token: " + msg.channelToken +
", reason: "+ reason);
callback({ error: SE.ERROR_BADSTATE, reason: reason });
}
});
},
_handleGetSEReadersRequest: function(msg, target, callback) {
gMap.registerSecureElementTarget(msg.appId, target);
let readers = Object.keys(this._sePresence).map(type => {
return { type: type, isPresent: this._sePresence[type] };
});
callback({ readers: readers, error: SE.ERROR_NONE });
},
_handleChildProcessShutdown: function(target) {
let channels = gMap.getChannelsByTarget(target);
let createCb = (seType, channelNumber) => {
return {
notifyCloseChannelSuccess: () => {
debug("closed " + seType + ", channel " + channelNumber);
},
notifyError: (reason) => {
debug("Failed to close " + seType + " channel " +
channelNumber + ", reason: " + reason);
}
};
};
channels.forEach((channel) => {
let connector = getConnector(channel.seType);
if (!connector) {
return;
}
connector.closeChannel(channel.channelNumber,
createCb(channel.seType, channel.channelNumber));
});
gMap.unregisterSecureElementTarget(target);
},
_sendSEResponse: function(msg, result) {
let promiseStatus = (result.error === SE.ERROR_NONE) ? "Resolved" : "Rejected";
result.resolverId = msg.data.resolverId;
msg.target.sendAsyncMessage(msg.name + promiseStatus, {result: result});
},
_isValidMessage: function(msg) {
let appIdValid = gMap.isAppIdRegistered(msg.data.appId);
return msg.name === "SE:GetSEReaders" ? true : appIdValid;
},
/**
* nsIMessageListener interface methods.
*/
receiveMessage: function(msg) {
DEBUG && debug("Received '" + msg.name + "' message from content process" +
": " + JSON.stringify(msg.data));
if (msg.name === "child-process-shutdown") {
this._handleChildProcessShutdown(msg.target);
return null;
}
if (SE_IPC_SECUREELEMENT_MSG_NAMES.indexOf(msg.name) !== -1) {
if (!msg.target.assertPermission("secureelement-manage")) {
debug("SecureElement message " + msg.name + " from a content process " +
"with no 'secureelement-manage' privileges.");
return null;
}
} else {
debug("Ignoring unknown message type: " + msg.name);
return null;
}
let callback = (result) => this._sendSEResponse(msg, result);
if (!this._isValidMessage(msg)) {
debug("Message not valid");
callback({ error: SE.ERROR_GENERIC });
return null;
}
switch (msg.name) {
case "SE:GetSEReaders":
this._handleGetSEReadersRequest(msg.data, msg.target, callback);
break;
case "SE:OpenChannel":
this._handleOpenChannel(msg.data, callback);
break;
case "SE:CloseChannel":
this._handleCloseChannel(msg.data, callback);
break;
case "SE:TransmitAPDU":
this._handleTransmit(msg.data, callback);
break;
}
return null;
},
/**
* nsIObserver interface methods.
*/
observe: function(subject, topic, data) {
if (topic === NS_XPCOM_SHUTDOWN_OBSERVER_ID) {
this._shutdown();
}
}
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SecureElementManager]);

View file

@ -1,18 +0,0 @@
# 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.
# SecureElementManager
component {48f4e650-28d2-11e4-8c21-0800200c9a66} SecureElement.js
contract @mozilla.org/secureelement/parent-manager;1 {48f4e650-28d2-11e4-8c21-0800200c9a66}
category profile-after-change SecureElementManager @mozilla.org/secureelement/parent-manager;1

View file

@ -1,360 +0,0 @@
/* 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,
iccService, SEUtils */
const { interfaces: Ci, utils: Cu, results: Cr } = 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
var 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, "iccService",
"@mozilla.org/icc/iccservice;1",
"nsIIccService");
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 iccService's channel management
* related interfaces that implements nsISecureElementConnector interface.
*/
function UiccConnector() {
this._init();
}
UiccConnector.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsISecureElementConnector,
Ci.nsIIccListener]),
classID: UICCCONNECTOR_CID,
classInfo: XPCOMUtils.generateCI({
classID: UICCCONNECTOR_CID,
contractID: UICCCONNECTOR_CONTRACTID,
classDescription: "UiccConnector",
interfaces: [Ci.nsISecureElementConnector,
Ci.nsIIccListener,
Ci.nsIObserver]
}),
_SEListeners: [],
_isPresent: false,
_init: function() {
Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
let icc = iccService.getIccByServiceId(PREFERRED_UICC_CLIENTID);
icc.registerListener(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);
let icc = iccService.getIccByServiceId(PREFERRED_UICC_CLIENTID);
icc.unregisterListener(this);
},
_updatePresenceState: function() {
let uiccNotReadyStates = [
Ci.nsIIcc.CARD_STATE_UNKNOWN,
Ci.nsIIcc.CARD_STATE_ILLEGAL,
Ci.nsIIcc.CARD_STATE_PERSONALIZATION_IN_PROGRESS,
Ci.nsIIcc.CARD_STATE_PERMANENT_BLOCKED,
Ci.nsIIcc.CARD_STATE_UNDETECTED
];
let cardState = iccService.getIccByServiceId(PREFERRED_UICC_CLIENTID).cardState;
let uiccPresent = cardState !== null &&
uiccNotReadyStates.indexOf(cardState) == -1;
if (this._isPresent === uiccPresent) {
return;
}
debug("Uicc presence changed " + this._isPresent + " -> " + uiccPresent);
this._isPresent = uiccPresent;
this._SEListeners.forEach((listener) => {
listener.notifySEPresenceChanged(SE.TYPE_UICC, this._isPresent);
});
},
// 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) {
let icc = iccService.getIccByServiceId(PREFERRED_UICC_CLIENTID);
icc.iccExchangeAPDU(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.
let icc = iccService.getIccByServiceId(PREFERRED_UICC_CLIENTID);
icc.iccOpenChannel(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;
}
let icc = iccService.getIccByServiceId(PREFERRED_UICC_CLIENTID);
icc.iccCloseChannel(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);
}
}
});
},
registerListener: function(listener) {
if (this._SEListeners.indexOf(listener) !== -1) {
throw Cr.NS_ERROR_UNEXPECTED;
}
this._SEListeners.push(listener);
// immediately notify listener about the current state
listener.notifySEPresenceChanged(SE.TYPE_UICC, this._isPresent);
},
unregisterListener: function(listener) {
let idx = this._SEListeners.indexOf(listener);
if (idx !== -1) {
this._SEListeners.splice(idx, 1);
}
},
/**
* nsIIccListener interface methods.
*/
notifyStkCommand: function() {},
notifyStkSessionEnd: function() {},
notifyIccInfoChanged: function() {},
notifyCardStateChanged: function() {
debug("Card state changed, updating UICC presence.");
this._updatePresenceState();
},
/**
* nsIObserver interface methods.
*/
observe: function(subject, topic, data) {
if (topic === NS_XPCOM_SHUTDOWN_OBSERVER_ID) {
this._shutdown();
}
}
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([UiccConnector]);

View file

@ -1,17 +0,0 @@
# 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}

View file

@ -1,62 +0,0 @@
/* 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/. */
/* Copyright © 2015, Deutsche Telekom, Inc. */
/* Object Directory File (ODF) is an elementary file which contain
pointers to other EFs. It is specified in PKCS#15 section 6.7. */
this.ODF_DF = [0x50, 0x31];
/* ISO 7816-4: secure messaging */
this.CLA_SM = 0x00;
/* ISO 7816-4, 5.4.1 table 11 */
this.INS_SF = 0xA4; // select file
this.INS_GR = 0xC0; // get response
this.INS_RB = 0xB0; // read binary
/* ISO 7816-4: select file, see 6.11.3, table 58 & 59 */
this.P1_SF_DF = 0x00; // select DF
this.P2_SF_FCP = 0x04; // return FCP
/* ISO 7816-4: read binary, 6.1.3. P1 and P2 describe offset of the first byte
to be read. We always read the whole files at the moment. */
this.P1_RB = 0x00;
this.P2_RB = 0x00;
/* ISO 7816-4: get response, 7.1.3 table 74, P1-P2 '0000' (other values RFU) */
this.P1_GR = 0x00;
this.P2_GR = 0x00;
/* ISO 7816-4: 5.1.5 File Control Information, Table 1. For FCP and FMD. */
this.TAG_PROPRIETARY = 0x00;
this.TAG_NON_TLV = 0x53;
this.TAG_BER_TLV = 0x73;
/* ASN.1 tags */
this.TAG_SEQUENCE = 0x30;
this.TAG_OCTETSTRING = 0x04;
this.TAG_OID = 0x06; // Object Identifier
/* ISO 7816-4: 5.1.5 File Control Information, Templates. */
this.TAG_FCP = 0x62; // File control parameters template
this.TAG_FMD = 0x64; // File management data template
this.TAG_FCI = 0x6F; // File control information template
/* EF_DIR tags */
this.TAG_APPLTEMPLATE = 0x61;
this.TAG_APPLIDENTIFIER = 0x4F;
this.TAG_APPLLABEL = 0x50;
this.TAG_APPLPATH = 0x51;
this.TAG_GPD_ALL = 0x82; // EF-ACRules - GPD spec. "all applets"
/* Generic TLVs that are parsed */
this.TAG_GPD_AID = 0xA0; // AID in the EF-ACRules - GPD spec, "one applet"
this.TAG_EXTERNALDO = 0xA1; // External data objects - PKCS#15
this.TAG_INDIRECT = 0xA5; // Indirect value.
this.TAG_EF_ODF = 0xA7; // Elemenetary File Object Directory File
// Allow this file to be imported via Components.utils.import().
this.EXPORTED_SYMBOLS = Object.keys(this);

View file

@ -1,32 +0,0 @@
/* 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/. */
/* Copyright © 2015, Deutsche Telekom, Inc. */
#include "nsISupports.idl"
interface nsIVariant;
[scriptable, uuid(4994a960-26d9-4d71-82dd-4505bd97bf2a)]
interface nsIAccessControlEnforcer : nsISupports
{
/**
* Determines whether application identified by its ID should be allowed
* to access Secure Element's applet identified by its AID. Decision
* is made according to the GPD specification.
*
* @param localId
* ID of an application accessing SE
* @param seType
* Type of the SE.
* @param aid
* AID of a SE applet
* @return Promise which is resolved to true if access should be allowed,
* false otherwise, and rejected if the application contains
* no developer certificate.
*/
jsval isAccessAllowed(in unsigned long localId,
in DOMString seType,
in DOMString aid);
};

View file

@ -1,50 +0,0 @@
/* 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/. */
/* Copyright © 2015, Deutsche Telekom, Inc. */
#include "nsISupports.idl"
[scriptable, uuid(7baedd2a-3189-4b03-b2a3-34016043b5e2)]
interface nsIAccessRulesManager : nsISupports
{
/* Wildcard: rule allows all applications to access an SE applet */
const unsigned short ALLOW_ALL = 1;
/* Wildcard: rule denies all applications to access an SE applet */
const unsigned short DENY_ALL = 2;
/* Wildcard: rule allows application(s) access to all SE applets */
const unsigned short ALL_APPLET = 3;
/**
* Initiates Access Rules Manager, this should perform the initial
* reading of rules from access rule source
* @return Promise which is resolved if init is successful or rejected
* otherwise
*/
jsval init();
/**
* Retrieves all access rules.
*
* Rules are stored in an array. Each rule contains the following properties:
* - applet - describes an SE applet referenced by this rule. Might equal
* to an applet AID (as a byte array), or to a wildcard "all"
* meaning all applets.
* - application - describes an application referenced by this rule. Might
* be an array of developer certificate hashes (each as
* a byte array) in which case it lists all applications
* allowed access. Alternatively, might equal to wildcard
* "allowed-all" or "denied-all".
*
* Example rule format:
* [{ applet: ALL_APPLET,
* application: [[0x01, 0x02, ..., 0x20],
* [0x20, 0x19, ...., 0x01]],
* { applet: [0x00, 0x01, ..., 0x05],
* application: ALLOW_ALL}}]
*
* @return Promise which resolves with Array containing parsed access rules
*/
jsval getAccessRules();
};

View file

@ -1,124 +0,0 @@
/* 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(417f59ee-f582-45b9-9a4e-e9dcefecb4f7)]
interface nsISEListener : nsISupports
{
void notifySEPresenceChanged(in DOMString seType, in boolean isPresent);
};
[scriptable, uuid(3cef313a-1d01-432d-9cd2-6610a80911f3)]
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);
/**
* Register a Secure Element listener
*
* @param listener
*/
void registerListener(in nsISEListener listener);
/**
* Unregister a Secure Element listener
*
* @param listener
*/
void unregisterListener(in nsISEListener listener);
};

View file

@ -1,68 +0,0 @@
/* 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. */
// Set to true to debug SecureElement (SE) stack
this.DEBUG_ALL = false;
// Set individually to debug specific layers
this.DEBUG_CONNECTOR = DEBUG_ALL || false;
this.DEBUG_ACE = DEBUG_ALL || false ;
this.DEBUG_SE = DEBUG_ALL || false ;
// Maximun logical channels per session.
// For 'uicc' SE type this value is 3, as opening a basic channel' : 0
// is not allowed for security reasons. In such scenarios, possible
// supplementary logical channels available are : [1, 2, or 3].
// However,Other SE types may support upto max 4 (including '0').
this.MAX_CHANNELS_ALLOWED_PER_SESSION = 4;
this.BASIC_CHANNEL = 0;
// According GPCardSpec 2.2
this.MAX_APDU_LEN = 255; // including APDU header
// CLA (1 byte) + INS (1 byte) + P1 (1 byte) + P2 (1 byte)
this.APDU_HEADER_LEN = 4;
this.LOGICAL_CHANNEL_NUMBER_LIMIT = 4;
this.SUPPLEMENTARY_LOGICAL_CHANNEL_NUMBER_LIMIT = 20;
this.MIN_AID_LEN = 5;
this.MAX_AID_LEN = 16;
this.CLA_GET_RESPONSE = 0x00;
this.INS_SELECT = 0xA4;
this.INS_MANAGE_CHANNEL = 0x70;
this.INS_GET_RESPONSE = 0xC0;
// Match the following errors with SecureElement.webidl's SEError enum values
this.ERROR_NONE = "";
this.ERROR_SECURITY = "SESecurityError";
this.ERROR_IO = "SEIoError";
this.ERROR_BADSTATE = "SEBadStateError";
this.ERROR_INVALIDCHANNEL = "SEInvalidChannelError";
this.ERROR_INVALIDAPPLICATION = "SEInvalidApplicationError";
this.ERROR_GENERIC = "SEGenericError";
this.ERROR_NOTPRESENT = "SENotPresentError";
this.ERROR_ILLEGALPARAMETER = "SEIllegalParameterError";
this.TYPE_UICC = "uicc";
this.TYPE_ESE = "eSE";
// Allow this file to be imported via Components.utils.import().
this.EXPORTED_SYMBOLS = Object.keys(this);

View file

@ -11,30 +11,6 @@ if CONFIG['MOZ_SECUREELEMENT']:
'DOMSecureElement.manifest',
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk' and CONFIG['MOZ_SECUREELEMENT']:
EXTRA_COMPONENTS += [
'gonk/ACEService.js',
'gonk/ACEService.manifest',
'gonk/GPAccessRulesManager.js',
'gonk/GPAccessRulesManager.manifest',
'gonk/SecureElement.js',
'gonk/SecureElement.manifest',
]
XPIDL_MODULE = 'dom_secureelement'
XPIDL_SOURCES += [
'gonk/nsIAccessControlEnforcer.idl',
'gonk/nsIAccessRulesManager.idl',
'gonk/nsISecureElementConnector.idl',
]
EXTRA_JS_MODULES += [
'gonk/gp_consts.js',
'gonk/se_consts.js',
'SEUtils.jsm'
]
XPCSHELL_TESTS_MANIFESTS += [
'tests/unit/xpcshell.ini'
]
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'