Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
parent
93e3a48004
commit
b38dea0597
25901 changed files with 9982925 additions and 369 deletions
116
gaia/dev_apps/mobile-wallet/js/secure-element/apdu_helpers.js
Executable file
116
gaia/dev_apps/mobile-wallet/js/secure-element/apdu_helpers.js
Executable file
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
CRS AID, TAGS and APDUs based on:
|
||||
GlobalPlatform Card Technology Contactless Services Card Specification v2.2
|
||||
– Amendment C Version 1.1.1
|
||||
|
||||
UICC APDUs based on:
|
||||
ETSI TS 102 221 V8.2.0 (2009-06)
|
||||
Smart Cards; UICC-Terminal interface; Physical and logical characteristics
|
||||
(Release 8)
|
||||
*/
|
||||
|
||||
/* globals SEUtils */
|
||||
/* exported APDU, AID, TAG */
|
||||
|
||||
'use strict';
|
||||
|
||||
(function(exports) {
|
||||
|
||||
const AID = {
|
||||
CRS: 'A00000015143525300',
|
||||
UICC: 'A0000000871001FF33FFFF8901010100'
|
||||
};
|
||||
|
||||
const APDU = {
|
||||
// gets the response from SIM, can be used to retireve channel select
|
||||
getResponse: { cla: 0x00, ins: 0xC0, p1: 0x00, p2: 0x00 },
|
||||
readBinary: { cla: 0x00, ins: 0xB0, p1: 0x00, p2: 0x00 },
|
||||
CRS: {
|
||||
// Get applet version number and global update counter
|
||||
getData: { cla: 0x80, ins: 0xCA, p1: 0x00, p2: 0xA5 },
|
||||
// Get status of applets and Security Domains only.
|
||||
// The ISD is ignored. Get first or only occurrence
|
||||
getStatusAll1st: {
|
||||
cla: 0x80, ins: 0xF2, p1: 0x40, p2: 0x00,
|
||||
data: new Uint8Array([
|
||||
0x4F, 0x00, // applet AID, empty here
|
||||
0x5C, 0x03, // tag list to be returned
|
||||
0x4F, // AID
|
||||
0x9F, 0x70 // applet life cycle state - first byte;
|
||||
// contactless activation state - second byte
|
||||
])
|
||||
},
|
||||
// Get status of applets and Security Domains only.
|
||||
// The ISD is ignored. Get next occurrence(s), if SW is 63h10h
|
||||
getStatusAllNext: { cla: 0x80, ins: 0xF2, p1: 0x40, p2: 0x01,
|
||||
data: new Uint8Array([0x4F, 0x00, 0x5C, 0x03, 0x4F, 0x9F, 0x70]) },
|
||||
// Get status of applet identified with AID
|
||||
getStatusAID: (aid) => {
|
||||
return { cla: 0x80, ins: 0xF2, p1: 0x40, p2: 0x00,
|
||||
data: SEUtils.joinUint8Arrays(
|
||||
[0x4F, 0x0F], // applet AID
|
||||
SEUtils.hexStringToByte(aid),
|
||||
[0x5C, 0x04, // tag list
|
||||
0x4F, // AID
|
||||
0x9F, // applet lifecycle state
|
||||
0x70, // contactless activation state
|
||||
0x81 // selection priority
|
||||
]
|
||||
)
|
||||
};
|
||||
},
|
||||
activateCLF: (aid) => {
|
||||
return { cla: 0x80, ins: 0xF0, p1: 0x01, p2: 0x01,
|
||||
data: SEUtils.joinUint8Arrays([0x4F, 0x0F],
|
||||
SEUtils.hexStringToByte(aid))
|
||||
};
|
||||
},
|
||||
deactivateCLF: (aid) => {
|
||||
return { cla: 0x80, ins: 0xF0, p1: 0x01, p2: 0x00,
|
||||
data: SEUtils.joinUint8Arrays([0x4F, 0x0F],
|
||||
SEUtils.hexStringToByte(aid))
|
||||
};
|
||||
},
|
||||
nfcActivate: { cla: 0x80, ins: 0xF0, p1: 0x04, p2: 0x80,
|
||||
data: new Uint8Array([0x80, 0x01, 0x40]) },
|
||||
nfcDeactivate: { cla: 0x80, ins: 0xF0, p1: 0x04, p2: 0x00,
|
||||
data: new Uint8Array([0x80, 0x01, 0x40]) }
|
||||
},
|
||||
// PIN commands
|
||||
UICC: {
|
||||
getAttempts: (p2) => {
|
||||
return { cla: 0x00, ins: 0x20, p1: 0x00, p2: p2 };
|
||||
},
|
||||
verify: (p2, pinBytes) => {
|
||||
return { cla: 0x00, ins: 0x20, p1: 0x00, p2: p2, data: pinBytes };
|
||||
},
|
||||
enable: (p2, defPinBytes) => {
|
||||
return { cla: 0x00, ins: 0x28, p1: 0x00, p2: p2, data: defPinBytes };
|
||||
},
|
||||
disable: (p2, defPinBytes) => {
|
||||
return { cla: 0x00, ins: 0x26, p1: 0x00, p2: p2, data: defPinBytes };
|
||||
},
|
||||
change: (p2, curPinBytes, newPinBytes) => {
|
||||
var data = SEUtils.joinUint8Arrays(curPinBytes, newPinBytes);
|
||||
return { cla: 0x00, ins: 0x24, p1: 0x00, p2: p2, data: data };
|
||||
},
|
||||
unblock: (p2, pukBytes, defPinBytes) => {
|
||||
var data = SEUtils.joinUint8Arrays(pukBytes, defPinBytes);
|
||||
return { cla: 0x00, ins: 0x2C, p1: 0x00, p2: p2, data: data };
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const TAG = {
|
||||
CRS: {
|
||||
APP_TEMPLATE: 0x61,
|
||||
AID: 0x4F,
|
||||
STATE: 0x9F70
|
||||
},
|
||||
};
|
||||
|
||||
exports.AID = AID;
|
||||
exports.APDU = APDU;
|
||||
exports.TAG = TAG;
|
||||
|
||||
}((typeof exports === 'undefined') ? window : exports));
|
||||
90
gaia/dev_apps/mobile-wallet/js/secure-element/se_utils.js
Executable file
90
gaia/dev_apps/mobile-wallet/js/secure-element/se_utils.js
Executable file
|
|
@ -0,0 +1,90 @@
|
|||
'use strict';
|
||||
|
||||
/* exported SEUtils */
|
||||
|
||||
(function(exports) {
|
||||
|
||||
var SEUtils = {
|
||||
byteToHexString: function byteToHexString(uint8arr) {
|
||||
if (!uint8arr) {
|
||||
return '';
|
||||
}
|
||||
|
||||
var hexStr = '';
|
||||
for (var i = 0; i < uint8arr.length; i++) {
|
||||
var hex = (uint8arr[i] & 0xff).toString(16);
|
||||
hex = (hex.length === 1) ? '0' + hex : hex;
|
||||
hexStr += hex;
|
||||
}
|
||||
return hexStr.toUpperCase();
|
||||
},
|
||||
|
||||
hexStringToByte: function hexStringToByte(str) {
|
||||
var a = [];
|
||||
for(var i = 0, len = str.length; i < len; i+=2) {
|
||||
a.push(parseInt(str.substr(i,2),16));
|
||||
}
|
||||
return new Uint8Array(a);
|
||||
},
|
||||
|
||||
byteToASCIIString: function byteToASCIIString(arr) {
|
||||
var str = '';
|
||||
|
||||
var len = arr && arr.length ? arr.length : 0;
|
||||
for(var i = 0; i < len; i++) {
|
||||
str += arr[i] < 0x20 || arr > 0x7E ? '' : String.fromCharCode(arr[i]);
|
||||
}
|
||||
|
||||
return str;
|
||||
},
|
||||
|
||||
joinUint8Arrays: function joinUint8Arrays() {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
var length = args.reduce(function(a, b) { return a + b.length; }, 0);
|
||||
var out = new Uint8Array(length);
|
||||
|
||||
args.reduce(function(previousLen, buffer) {
|
||||
out.set(buffer, previousLen);
|
||||
return previousLen + buffer.length;
|
||||
}, 0);
|
||||
|
||||
return out;
|
||||
},
|
||||
|
||||
// no support for multibyte tag and constructed data object
|
||||
parseSimpleTLV: function parseSimpleTLV(data) {
|
||||
if(!data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var result = {};
|
||||
for(var i = 0, len = data.length; i < len;) {
|
||||
var tag = data[i];
|
||||
var length = data[i+1];
|
||||
var value = data.slice(i+2, i+2 + length);
|
||||
result[tag] = value;
|
||||
i += 2 + length;
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
promises: {
|
||||
whilePromise: function(condition, action, init) {
|
||||
return new Promise((resolve, reject) => {
|
||||
var loop = (value) => {
|
||||
if (condition(value)) {
|
||||
return resolve(value);
|
||||
}
|
||||
|
||||
return action(value).then(loop).catch(reject);
|
||||
};
|
||||
loop(init);
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
exports.SEUtils = SEUtils;
|
||||
}((typeof exports === 'undefined') ? window : exports));
|
||||
|
||||
187
gaia/dev_apps/mobile-wallet/js/secure-element/sim_access_manager.js
Executable file
187
gaia/dev_apps/mobile-wallet/js/secure-element/sim_access_manager.js
Executable file
|
|
@ -0,0 +1,187 @@
|
|||
'use strict';
|
||||
|
||||
/* globals SEUtils, APDU, ERRORS, DEFAULTS */
|
||||
/* exported simAccessManager */
|
||||
|
||||
(function(exports) {
|
||||
|
||||
const SIM_ACCESS_DISABLED = 'disabled';
|
||||
const SIM_ACCESS_IDLE = 'idle';
|
||||
const SIM_ACCESS_BUSY = 'busy';
|
||||
|
||||
var _simState = SIM_ACCESS_DISABLED;
|
||||
var _seSession = null;
|
||||
|
||||
var _crsAid = DEFAULTS.CRS_AID;
|
||||
var _uiccAid = DEFAULTS.UICC_AID;
|
||||
var _pinP2 = DEFAULTS.PIN_P2;
|
||||
|
||||
var SIMAccessManager = {
|
||||
set crsAid(value) {
|
||||
_crsAid = value;
|
||||
},
|
||||
|
||||
get crsAidBytes() {
|
||||
return SEUtils.hexStringToByte(_crsAid);
|
||||
},
|
||||
|
||||
set uiccAid(value) {
|
||||
_uiccAid = value;
|
||||
},
|
||||
|
||||
get uiccAidBytes() {
|
||||
return SEUtils.hexStringToByte(_uiccAid);
|
||||
},
|
||||
|
||||
set pinP2(value) {
|
||||
_pinP2 = value;
|
||||
},
|
||||
|
||||
get pinP2Bytes() {
|
||||
return SEUtils.hexStringToByte(_pinP2)[0];
|
||||
},
|
||||
|
||||
start: function start() {
|
||||
if(!navigator.seManager) {
|
||||
return Promise.reject(ERRORS.GLOBAL.NO_API);
|
||||
}
|
||||
|
||||
return navigator.seManager.getSEReaders()
|
||||
.then((readers) => {
|
||||
var reader = readers.find(r => r.type.toLowerCase() === 'uicc' && r.isSEPresent);
|
||||
if(!reader) {
|
||||
return Promise.reject(ERRORS.SIM.NO_READER);
|
||||
}
|
||||
return reader.openSession();
|
||||
})
|
||||
.then((session) => {
|
||||
_seSession = session;
|
||||
_simState = SIM_ACCESS_IDLE;
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
_simState = SIM_ACCESS_DISABLED;
|
||||
|
||||
if(e !== ERRORS.SIM.NO_READER) {
|
||||
e = ERRORS.SIM.ACCESS_FAILED;
|
||||
}
|
||||
return Promise.reject(e);
|
||||
});
|
||||
},
|
||||
|
||||
stop: function stop() {
|
||||
return _seSession.closeAll()
|
||||
.then(() => {
|
||||
_seSession = null;
|
||||
_simState = SIM_ACCESS_DISABLED;
|
||||
})
|
||||
.catch(e => this._cleanup(e, ERRORS.SIM.SIM_FAILURE));
|
||||
},
|
||||
|
||||
_initSIMAccess: function _initSIMAccess() {
|
||||
if(_simState !== SIM_ACCESS_IDLE) {
|
||||
return Promise.reject(ERRORS.SIM.NOT_IDLE);
|
||||
}
|
||||
|
||||
if(!_seSession.reader.isSEPresent) {
|
||||
return Promise.reject(ERRORS.SIM.NO_READER);
|
||||
}
|
||||
|
||||
_simState = SIM_ACCESS_BUSY;
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
_cleanup: function _cleanup(error, message) {
|
||||
console.error(error);
|
||||
_simState = SIM_ACCESS_IDLE;
|
||||
if(error === ERRORS.SIM.NOT_IDLE || error === ERRORS.SIM.NO_READER) {
|
||||
message = error;
|
||||
}
|
||||
return Promise.reject(message);
|
||||
},
|
||||
|
||||
getAppletsData: function getAppletsData() {
|
||||
return this._initSIMAccess()
|
||||
.then(() => _seSession.openLogicalChannel(this.crsAidBytes))
|
||||
.then((channel) => {
|
||||
return channel.transmit(APDU.CRS.getStatusAll1st);
|
||||
})
|
||||
.then((resp) => {
|
||||
var receivedData = new Uint8Array();
|
||||
|
||||
// we can get different responses, also propriatary
|
||||
// using counter to not allow to try to query the SIM indifinately
|
||||
var count = 0;
|
||||
var condition = (resp) => (count++ > 10 || resp.sw1 === 0x90 && resp.sw2 === 0x00);
|
||||
var action = (resp) => {
|
||||
receivedData = SEUtils.joinUint8Arrays(receivedData, resp.data);
|
||||
return resp.channel.transmit(APDU.CRS.getStatusAllNext);
|
||||
};
|
||||
|
||||
return SEUtils.promises.whilePromise(condition, action, resp)
|
||||
.then((lastResp) => {
|
||||
lastResp.channel.close();
|
||||
_simState = SIM_ACCESS_IDLE;
|
||||
return SEUtils.joinUint8Arrays(receivedData, lastResp.data);
|
||||
});
|
||||
})
|
||||
.catch(e => this._cleanup(e, ERRORS.SIM.APPLETS_FAILURE));
|
||||
},
|
||||
|
||||
toggleCLF: function toggleCLF(on, aid) {
|
||||
var apdu = on ? APDU.CRS.activateCLF(aid) : APDU.CRS.deactivateCLF(aid);
|
||||
return this._initSIMAccess()
|
||||
.then(() => _seSession.openLogicalChannel(this.crsAidBytes))
|
||||
.then((channel) => channel.transmit(apdu))
|
||||
.then((resp) => {
|
||||
return resp.channel.close()
|
||||
.then(() => {
|
||||
_simState = SIM_ACCESS_IDLE;
|
||||
return resp.sw1 === 0x90 && resp.sw2 === 0x00;
|
||||
});
|
||||
})
|
||||
.catch(e => this._cleanup(e, ERRORS.SIM.SIM_FAILURE));
|
||||
},
|
||||
|
||||
_execPinCommand: function _execPinCommand(type, pin1, pin2) {
|
||||
return this._initSIMAccess()
|
||||
.then(() => _seSession.openLogicalChannel(this.uiccAidBytes))
|
||||
.then((channel) => {
|
||||
var apdu = APDU.UICC[type](this.pinP2Bytes, pin1, pin2);
|
||||
if(!apdu) {
|
||||
return Promise.reject('PIN APDU type ' + type + ' not found');
|
||||
}
|
||||
return channel.transmit(apdu);
|
||||
})
|
||||
.then((resp) => {
|
||||
return resp.channel.close().then(() => {
|
||||
_simState = SIM_ACCESS_IDLE;
|
||||
return SEUtils.byteToHexString([resp.sw1, resp.sw2]);
|
||||
});
|
||||
})
|
||||
.catch(e => this._cleanup(e, ERRORS.SIM.PIN_FAILURE));
|
||||
},
|
||||
|
||||
verifyPIN: function verifyPIN(pin) {
|
||||
return this._execPinCommand('verify', pin);
|
||||
},
|
||||
|
||||
enablePIN: function enablePIN(pin) {
|
||||
return this._execPinCommand('enable', pin);
|
||||
},
|
||||
|
||||
unblockPIN: function unblockPIN(puk, pin) {
|
||||
return this._execPinCommand('unblock', puk, pin);
|
||||
},
|
||||
|
||||
disablePIN: function disablePIN(pin) {
|
||||
return this._execPinCommand('disable', pin);
|
||||
},
|
||||
|
||||
changePIN: function changePIN(currentPin, newPin) {
|
||||
return this._execPinCommand('change', currentPin, newPin);
|
||||
},
|
||||
};
|
||||
|
||||
exports.SIMAccess = SIMAccessManager;
|
||||
}((typeof exports === 'undefined') ? window : exports));
|
||||
Loading…
Reference in a new issue