commandinterpreter.js
	
	
Summary
	
		Implementation of an ISO 7816-4 command interpreter
	
    
    
    | 
    
        Class Summary
    
     | 
    
    
    
    | CommandInterpreter | 
    Class implementing a command interpreter that handles ISO 7816-4 command APDUs
  | 
    
    
    
    
 
        
function CommandInterpreter(fileSelector) {
	this.fileSelector = fileSelector;
}
CommandInterpreter.prototype.setSecureChannel = function(secureChannel) {
	this.secureChannel = secureChannel;
}
CommandInterpreter.prototype.hasSecureChannel = function() {
	return (typeof(this.secureChannel) != "undefined") && (this.secureChannel != null);
}
CommandInterpreter.prototype.readBinary = function(apdu) {
	if (!apdu.hasLe()) {
		throw new GPError("CommandInterpreter", GPError.INVALID_DATA, APDU.SW_WRONGLENGTH, "Wrong length - missing Le field");
	}
	if (apdu.isChained()) {
		throw new GPError("CommandInterpreter", GPError.INVALID_DATA, APDU.SW_CHAINNOTSUPPORTED, "Chaining not supported in READ BINARY");
	}
	
	var dua = new DataUnitAPDU(apdu);
	
	if (dua.hasCData()) {
		throw new GPError("CommandInterpreter", GPError.INVALID_DATA, APDU.SW_INVDATA, "Command data not expected in  READ BINARY");
	}
	var ef;
	var sfi = dua.getSFI();
	var fid = dua.getFID();
	
	if (sfi >= 0) {
		ef = this.fileSelector.selectSFI(sfi);
	} else if (fid != null) {
		ef = this.fileSelector.selectFID(fid, false, false);
	} else {
		ef = this.fileSelector.getCurrentEF();
	
		if (ef == null) {
			throw new GPError("CommandInterpreter", GPError.INVALID_DATA, APDU.SW_COMNOTALLOWNOEF, "No current EF in READ BINARY");
		}
	}
	
	if (!(ef instanceof TransparentEF)) {
		throw new GPError("CommandInterpreter", GPError.INVALID_DATA, APDU.SW_COMINCOMPATIBLE, "EF is not a transparent file in READ BINARY");
	}
	var ac = this.fileSelector.getMeta("accessController");
	if (ac && !ac.checkFileReadAccess(this, apdu, ef)) {
		throw new GPError("CommandInterpreter", GPError.INVALID_DATA, APDU.SW_SECSTATNOTSAT, "Read access not allowed as determined by " + ac);
	}
	var offset = dua.getOffset();
	var length = apdu.getNe();
	var data = ef.readBinary(apdu, offset, length);
	apdu.setRData(data);
}
CommandInterpreter.prototype.updateBinary = function(apdu) {
	if (apdu.isChained()) {
		throw new GPError("CommandInterpreter", GPError.INVALID_DATA, APDU.SW_CHAINNOTSUPPORTED, "Chaining not supported in READ BINARY");
	}
	
	if (!apdu.hasCData()) {
		throw new GPError("CommandInterpreter", GPError.INVALID_DATA, APDU.SW_WRONGLENGTH, "No data found in UPDATE BINARY");
	}
	var dua = new DataUnitAPDU(apdu);
	
	if (!dua.hasCData()) {
		throw new GPError("CommandInterpreter", GPError.INVALID_DATA, APDU.SW_INVDATA, "No data found in UPDATE BINARY");
	}
	var ef;
	var sfi = dua.getSFI();
	var fid = dua.getFID();
	
	if (sfi >= 0) {
		ef = this.fileSelector.selectSFI(sfi);
	} else if (fid != null) {
		ef = this.fileSelector.selectFID(fid, false, false);
	} else {
		ef = this.fileSelector.getCurrentEF();
	
		if (ef == null) {
			throw new GPError("CommandInterpreter", GPError.INVALID_DATA, APDU.SW_COMNOTALLOWNOEF, "No current EF in UPDATE BINARY");
		}
	}
	
	if (!(ef instanceof TransparentEF)) {
		throw new GPError("CommandInterpreter", GPError.INVALID_DATA, APDU.SW_COMINCOMPATIBLE, "EF is not a transparent file in UPDATE BINARY");
	}
	var ac = this.fileSelector.getMeta("accessController");
	if (ac && !ac.checkFileWriteAccess(this, apdu, ef)) {
		throw new GPError("CommandInterpreter", GPError.INVALID_DATA, APDU.SW_SECSTATNOTSAT, "Write access not allowed as determined by " + ac);
	}
	var offset = dua.getOffset();
	var data = dua.getCData();
	ef.updateBinary(apdu, offset, data);
}
CommandInterpreter.prototype.readRecord = function(apdu) {
	if (!apdu.hasLe()) {
		throw new GPError("CommandInterpreter", GPError.INVALID_DATA, APDU.SW_WRONGLENGTH, "Wrong length - missing Le field");
	}
	if (apdu.isChained()) {
		throw new GPError("CommandInterpreter", GPError.INVALID_DATA, APDU.SW_CHAINNOTSUPPORTED, "Chaining not supported in READ RECORD");
	}
	var recno = apdu.getP1();
	var sfi = apdu.getP2() >> 3;
	var qualifier = apdu.getP2() & 0x7;
	if (sfi > 0) {
		ef = this.fileSelector.selectSFI(sfi);
	} else {
		ef = this.fileSelector.getCurrentEF();
		if (ef == null) {
			throw new GPError("CommandInterpreter", GPError.INVALID_DATA, APDU.SW_COMNOTALLOWNOEF, "No current EF in READ RECORD");
		}
	}
	if (!(ef instanceof LinearEF)) {
		throw new GPError("CommandInterpreter", GPError.INVALID_DATA, APDU.SW_COMINCOMPATIBLE, "EF is not a linear file in READ RECORD");
	}
	var ac = this.fileSelector.getMeta("accessController");
	if (ac && !ac.checkFileReadAccess(this, apdu, ef)) {
		throw new GPError("CommandInterpreter", GPError.INVALID_DATA, APDU.SW_SECSTATNOTSAT, "Read access not allowed as determined by " + ac);
	}
	var data = ef.readRecord(apdu, recno, qualifier, apdu.getNe());
	apdu.setRData(data);
}
CommandInterpreter.prototype.verify = function(apdu) {
	if (apdu.isChained()) {
		throw new GPError("CommandInterpreter", GPError.INVALID_DATA, APDU.SW_CHAINNOTSUPPORTED, "Chaining not supported in command");
	}
	
	var pinao = this.fileSelector.getObject(AuthenticationObject.TYPE_PIN, apdu.getP2());
	if (!pinao) {
		throw new GPError("CommandInterpreter", GPError.INVALID_DATA, APDU.SW_RDNOTFOUND, "PIN with reference " + apdu.getP2() + " not found");
	}
	if (apdu.hasCData()) {
		pinao.verify(apdu.getCData());
	} else {
		if (!this.fileSelector.isAuthenticated(ao)) {
			pinao.determineStatus();
		}
	}
	apdu.setSW(APDU.SW_OK);
}
CommandInterpreter.prototype.changeReferenceData = function(apdu) {
	if (apdu.isChained()) {
		throw new GPError("CommandInterpreter", GPError.INVALID_DATA, APDU.SW_CHAINNOTSUPPORTED, "Chaining not supported in command");
	}
	if (!apdu.hasCData()) {
		throw new GPError("CommandInterpreter", GPError.INVALID_DATA, APDU.SW_WRONGLENGTH, "Command must have C-data");
	}
	var pinao = this.fileSelector.getObject(AuthenticationObject.TYPE_PIN, apdu.getP2());
	if (!pinao) {
		throw new GPError("CommandInterpreter", GPError.INVALID_DATA, APDU.SW_RDNOTFOUND, "PIN with reference " + apdu.getP2() + " not found");
	}
	pinao.changeReferenceData(apdu.getP1(), apdu.getCData());
	apdu.setSW(APDU.SW_OK);
}
CommandInterpreter.prototype.resetRetryCounter = function(apdu) {
	if (apdu.isChained()) {
		throw new GPError("CommandInterpreter", GPError.INVALID_DATA, APDU.SW_CHAINNOTSUPPORTED, "Chaining not supported in command");
	}
	var p1 = apdu.getP1();
	if (p1 == 0x02) {
		if (!apdu.hasCData()) {
			throw new GPError("CommandInterpreter", GPError.INVALID_DATA, APDU.SW_WRONGLENGTH, "Command must have C-data");
		}
	} else if (p1 == 0x03) {
		if (apdu.hasCData()) {
			throw new GPError("CommandInterpreter", GPError.INVALID_DATA, APDU.SW_WRONGLENGTH, "Command must not have C-data");
		}
	} else {
		throw new GPError("CommandInterpreter", GPError.INVALID_DATA, APDU.SW_INCP1P2, "Invalid P1 or P2");
	}
	var pinao = this.fileSelector.getObject(AuthenticationObject.TYPE_PIN, apdu.getP2());
	if (!pinao) {
		throw new GPError("CommandInterpreter", GPError.INVALID_DATA, APDU.SW_RDNOTFOUND, "PIN with reference " + apdu.getP2() + " not found");
	}
	pinao.resetRetryCounter(apdu.getCData());
	apdu.setSW(APDU.SW_OK);
}
CommandInterpreter.prototype.manageSecurityEnvironment = function(apdu) {
	var p1 = apdu.getP1();
	var p2 = apdu.getP2();
	
	var se = this.fileSelector.getSecurityEnvironment();
	if ((p1 & 0x0F) != 1) { 	
		throw new GPError("CommandInterpreter", GPError.INVALID_TYPE, APDU.APDU.SW_FUNCNOTSUPPORTED, "Only MANAGE SE set variant supported");
	}
	
	var tlv = new ASN1(p2, apdu.getCData());
	if (p1 & 0x80) {					
		var t = new ASN1(tlv.getBytes());		
		se.VEXK.add(t);
	}
	if (p1 & 0x40) {					
		var t = new ASN1(tlv.getBytes());		
		se.CDIK.add(t);
	}
	if (p1 & 0x20) {					
		var t = new ASN1(tlv.getBytes());		
		se.SMRES.add(t);
	}
	if (p1 & 0x10) {					
		var t = new ASN1(tlv.getBytes());		
		se.SMCOM.add(t);
	}
	apdu.setSW(APDU.SW_OK);
}
CommandInterpreter.prototype.handleSecMsgCommandAPDU = function(apdu) {
	if (apdu.isSecureMessaging()) {
		if (this.hasSecureChannel()) {
			try	{
				apdu.setSecureChannel(this.secureChannel);
				apdu.unwrap();
			}
			catch(e) {
				this.setSecureChannel();	
				throw e;
			}
		} else {
			throw new GPError("CommandInterpreter", GPError.INVALID_DATA, APDU.SW_SMNOTSUPPORTED, "No secure messaging channel");
		}
	} else {
		if (this.hasSecureChannel()) {
			this.setSecureChannel();	
		}
	}
}
CommandInterpreter.prototype.handleSecMsgResponseAPDU = function(apdu) {
	if (apdu.isSecureMessaging() && this.hasSecureChannel()) {
		apdu.wrap();
	}
}
CommandInterpreter.prototype.dispatch = function(apdu, ins) {
	if (!apdu.isISO()) {
		apdu.setSW(APDU.SW_INVCLA);
		return;
	}
	if (apdu.isChained()) {
		throw new GPError("CommandInterpreter", GPError.INVALID_DATA, APDU.SW_CHAINNOTSUPPORTED, "Chaining not supported");
	}
	switch(ins) {
		case APDU.INS_SELECT:
			this.fileSelector.processSelectAPDU(apdu);
			break;
		case APDU.INS_READ_BINARY:
			this.readBinary(apdu);
			break;
		case APDU.INS_UPDATE_BINARY:
			this.updateBinary(apdu);
			break;
		case APDU.INS_READ_RECORD:
			this.readRecord(apdu);
			break;
		case APDU.INS_VERIFY:
			this.verify(apdu);
			break;
		case APDU.INS_RESET_RETRY_COUNTER:
			this.resetRetryCounter(apdu);
			break;
		case APDU.INS_CHANGE_REFERENCE_DATA:
			this.changeReferenceData(apdu);
			break;
		case APDU.INS_MANAGE_SE:
			this.manageSecurityEnvironment(apdu);
			break;
		default:
			apdu.setSW(APDU.SW_INVINS);
	}
}
CommandInterpreter.prototype.processAPDU = function(apdu) {
	try	{
		this.handleSecMsgCommandAPDU(apdu);
		
		var cla = apdu.getCLA();
		var ins = apdu.getINS();
		var tlv = (ins & 1) == 1;
		ins &= 0xFE;
		
		var ac = this.fileSelector.getMeta("accessController");
		if (ac && !ac.checkCommandAccess(this, apdu)) {
			throw new GPError("CommandInterpreter", GPError.INVALID_DATA, APDU.SW_SECSTATNOTSAT, "Command not allowed as determined by " + ac);
		}
		this.dispatch(apdu, ins);
	}
	catch(e) {
		GPSystem.trace(e.fileName + "#" + e.lineNumber + ": " + e);
		var sw = APDU.SW_GENERALERROR;
		if ((e instanceof GPError) && (e.reason >= 0x6200)) {
			apdu.setSW(e.reason);
		} else {
			apdu.setSW(APDU.SW_GENERALERROR);
		}
	}
	this.handleSecMsgResponseAPDU(apdu);
}
	
Documentation generated by 
JSDoc on Tue Sep  3 22:29:41 2013