Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
179 lines
5.1 KiB
JavaScript
Executable file
179 lines
5.1 KiB
JavaScript
Executable file
'use strict';
|
|
|
|
/* globals Utils, AID, APDU */
|
|
|
|
var SIMAccessManager = {
|
|
ERROR_MSG: {
|
|
NOT_IDLE: 'WRONG SIM ACCESS STATE',
|
|
ACCESS_FAILED: 'FAILED TO ENABLE SIM ACCESS',
|
|
APPLETS_FAILED: 'FAILED TO GET APPLETS DATA'
|
|
},
|
|
|
|
SIM_ACCESS_STATES: {
|
|
DISABLED: 'disabled',
|
|
IDLE: 'idle',
|
|
BUSY: 'busy'
|
|
},
|
|
|
|
_simState: null,
|
|
_seSession: null,
|
|
|
|
start: function start() {
|
|
if(!navigator.seManager) {
|
|
return Promise.reject('Securel Element API not present');
|
|
}
|
|
|
|
return navigator.seManager.getSEReaders()
|
|
.then((readers) => {
|
|
return readers[0].openSession();
|
|
})
|
|
.then((session) => {
|
|
this._seSession = session;
|
|
this._simState = this.SIM_ACCESS_STATES.IDLE;
|
|
})
|
|
.catch(() => {
|
|
this._simState = this.SIM_ACCESS_STATES.DISABLED;
|
|
return Promise.reject(this.ERROR_MSG.ACCESS_FAILED);
|
|
});
|
|
},
|
|
|
|
stop: function stop() {
|
|
return this._seSession.closeAll()
|
|
.then(() => {
|
|
this._seSession = null;
|
|
this._simState = this.SIM_ACCESS_STATES.DISABLED;
|
|
});
|
|
},
|
|
|
|
getAppletsData: function getAppletsData() {
|
|
if(this._simState !== this.SIM_ACCESS_STATES.IDLE) {
|
|
return Promise.reject(this.ERROR_MSG.NOT_IDLE);
|
|
}
|
|
|
|
this._simState = this.SIM_ACCESS_STATES.BUSY;
|
|
return this._seSession.openLogicalChannel(AID.CRS)
|
|
.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 = Utils.joinUint8Arrays(receivedData, resp.data);
|
|
return resp.channel.transmit(APDU.CRS.getStatusAllNext)
|
|
};
|
|
|
|
return Utils.whilePromise(condition, action, resp)
|
|
.then((lastResp) => {
|
|
lastResp.channel.close();
|
|
this._simState = this.SIM_ACCESS_STATES.IDLE;
|
|
|
|
return Utils.joinUint8Arrays(receivedData, lastResp.data);
|
|
});
|
|
})
|
|
.catch((e) => {
|
|
return this._cleanup(this.ERROR_MSG.APPLETS_FAILED);
|
|
});
|
|
},
|
|
|
|
toggleApplet: function toggleApplet(on, aid) {
|
|
if(this._simState !== this.SIM_ACCESS_STATES.IDLE) {
|
|
return Promise.resolve(false);
|
|
}
|
|
|
|
this._simState = this.SIM_ACCESS_STATES.BUSY;
|
|
var apdu = on ? APDU.CRS.activateCLF(aid) : APDU.CRS.deactivateCLF(aid);
|
|
return this._seSession.openLogicalChannel(AID.CRS)
|
|
.then((channel) => channel.transmit(apdu))
|
|
.then((resp) => {
|
|
return resp.channel.close()
|
|
.then(() => {
|
|
this._simState = this.SIM_ACCESS_STATES.IDLE;
|
|
return resp.sw1 === 0x90 && resp.sw2 === 0x00;
|
|
});
|
|
});
|
|
},
|
|
|
|
_cleanup: function cleanup(message) {
|
|
this._simState = this.SIM_ACCESS_STATES.IDLE;
|
|
return Promise.reject(message);
|
|
}
|
|
};
|
|
|
|
var WalletDemo = {
|
|
init: function init() {
|
|
if(!navigator.seManager) {
|
|
this._updateStatus('Secure Element API not available.');
|
|
return;
|
|
}
|
|
|
|
SIMAccessManager.start()
|
|
.then(() => {
|
|
this._updateStatus('Init SIM access, getting applets from CRS');
|
|
return this._readDataRefreshUI();
|
|
})
|
|
.catch((error) => {
|
|
this._updateStatus('Init failed.', error);
|
|
SIMAccessManager.stop();
|
|
});
|
|
},
|
|
|
|
_readDataRefreshUI: function readDataRefreshUI() {
|
|
return SIMAccessManager.getAppletsData()
|
|
.then((appletsData) => {
|
|
// parsing and filtering to get payment enabled applets
|
|
var list = Utils.parseAppletsData(appletsData).filter((applet) => {
|
|
return applet.state === '1F01' || applet.state === '1F00';
|
|
});
|
|
this._updateStatus(list.length + ' applets read from SIM');
|
|
this._updateUI(list);
|
|
});
|
|
},
|
|
|
|
_updateUI: function updateUI(parsedApplets) {
|
|
var div = document.querySelector('#applets-view div');
|
|
div.innerHTML = '';
|
|
parsedApplets.forEach((applet) => {
|
|
var button = document.createElement('button');
|
|
var state = (applet.state === '1F01') ? 'active' : 'not active' ;
|
|
button.innerHTML = '<p>' + applet.aid + '</p>' +
|
|
'<p> contactless state: ' + state + '</p>';
|
|
|
|
button.addEventListener('click',
|
|
() => this._toggleApplet(applet.state === '1F00', applet.aid));
|
|
div.appendChild(button);
|
|
});
|
|
},
|
|
|
|
_toggleApplet: function toggleApplet(activate, aid) {
|
|
SIMAccessManager.toggleApplet(activate, aid)
|
|
.then((success) => {
|
|
if(success) {
|
|
this._updateStatus(aid + ' ' + ((activate) ? 'active' : 'not active'));
|
|
return this._readDataRefreshUI();
|
|
}
|
|
})
|
|
.catch((error) => {
|
|
this._updateStatus('Failed to toggle applets', error);
|
|
});
|
|
},
|
|
|
|
_updateStatus: function showStatus(status, error) {
|
|
console.log(status);
|
|
var selector = '.success';
|
|
if(error) {
|
|
console.log(error);
|
|
selector = '.error';
|
|
status += ', error: ' + error;
|
|
}
|
|
document.querySelector(selector).textContent = status;
|
|
},
|
|
|
|
};
|
|
|
|
window.addEventListener('load', () => WalletDemo.init());
|