Overview | File | Class | Tree | Index | Help
PREV NEXT | FRAMES NO FRAMES

## CardOutlineFactory2.0.js

#### Summary

No overview generated for 'CardOutlineFactory2.0.js'

Class Summary
CardOutlineFactory |
OutlineDataObject |
OutlinePKCS15 |

    /*
     *  ---------
     * |.##> <##.|  Open Smart Card Development Platform (www.openscdp.org)
     * |#       #|
     * |#       #|  Copyright (c) 1999-2006 CardContact Software & System Consulting
     * |'##> <##'|  Andreas Schwier, 32429 Minden, Germany (www.cardcontact.de)
     *  ---------
     *
     *  This file is part of OpenSCDP.
     *
     *  OpenSCDP is free software; you can redistribute it and/or modify
     *  it under the terms of the GNU General Public License version 2 as
     *  published by the Free Software Foundation.
     *
     *  OpenSCDP is distributed in the hope that it will be useful,
     *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     *  GNU General Public License for more details.
     *
     *  You should have received a copy of the GNU General Public License
     *  along with OpenSCDP; if not, write to the Free Software
     *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     *
     *  Core classes to construct card outlines
     *
     * Changes to Version 1.0
     *
     *  - Added support for nested DF structures (requires updated application profiles)
     *  - Added support for PKCS#15 / ISO 7816-15 formatted cards
     *
     */

    load("tools/FCPDecorator.js");
    load("tools/CRTDecorator.js");
    load("tools/ARRDecorator.js");
    load("tools/p15classes.js");

    //
    // CTOR - OutlineCard default
    //
    function OutlineCard(outlineFactory, card, applicationFactory, aidlist) {

    	this.factory = outlineFactory;
    	this.card = card;
    	this.af = applicationFactory;
    	this.presetaidlist = aidlist;

    	this.atr = card.reset(Card.RESET_COLD);

    	this.pkcs15 = new PKCS15(card);

    	// Create OutlineNode object and register in card object

    	var name = card.profile.CardManufacturerProduct.Name;

    	if (!name) {
    		name = "Card";
    	}

    	var view = new OutlineNode(name);
    	view.setUserObject(this);
    	this.view = view;
    }

    //
    // Expand clicked on node
    //
    // Read application list from EF_DIR or from card profile
    //
    OutlineCard.prototype.expandListener = function() {
    	if (this.expanded)
    		return;

    	var view = this.view;

    	//
    	// Display ATR
    	//
    	var atrnode = this.factory.newOutlineATR(this.atr);

    	view.insert(atrnode.view);

    	//
    	// Get list of application instances from card profile
    	//
    	var applicationInstances = this.card.profile.ApplicationInstances.ApplicationInstance;

    	//
    	// Make sure that we got a list
    	//
    	assert(applicationInstances instanceof Array);

    	//
    	// Try reading the EF_DIR
    	//
    	var hasEFDIR = true;

    	var aidlist = new Array();

    	try	{
    		this.pkcs15.readApplicationDirectory();
    		aidlist = this.pkcs15.getAidList();
    		aidlist["3F00"] = null;
    	}
    	catch(e) {
    		print("Exception selecting EF_DIR. Assuming no EF_DIR...\n" + e);
    		hasEFDIR = false;

    		//
    		// Create application list from card profile
    		//
    		for (var aid in  applicationInstances) {
    			if (aid == "arrayIndex")
    				continue;
    			aidlist[aid] = null;
    		}
    	}

    	var sortedaidlist = new Array();
    	for (var aid in aidlist) {
    		sortedaidlist.push(aid);
    	}

    	if (this.presetaidlist) {
    		for (var i = 0; i < this.presetaidlist.length; i++) {
    			sortedaidlist.push(this.presetaidlist[i]);
    		}
    	}

    	sortedaidlist.sort();

    	for (var i = 0; i < sortedaidlist.length; i++) {
    		var aid = sortedaidlist[i];
    		var at = aidlist[aid];
    		var applinstance = applicationInstances[aid.toString(HEX)];
    		if (applinstance) {
    /*
    			print("from Card Profile: " + applinstance);
    			print("  Label     : " + applinstance.Label);
    			print("  AID       : " + applinstance.AID);
    			print("  ProfileID : " + applinstance.ProfileID);
    */
    			var uniqueId = new ByteString(applinstance.ProfileID, HEX);

    			var instance = this.af.getApplicationInstance(new Object(), new ByteString(aid, HEX), this.card, uniqueId);

    			var struct = null;

    			if (instance.profile.DataStructure) {
    				struct = instance.profile.DataStructure;
    			}

    			var path;
    			if (aid.length <= 4) {
    				path = ":" + aid.toString(HEX);
    			} else {
    				path = "#" + aid.toString(HEX);
    			}

    			var applentry;
    			if (struct) {
    				applentry = this.factory.newOutlineDF(this.card, null, path, applinstance.Label, struct);
    			} else {
    				applentry = this.factory.newOutlineApplet(instance);
    			}
    			view.insert(applentry.view);
    	  	} else {
    	  		print("No profile found for aid " + aid);
    	  	}
    	  	if (at) {
    			if ((!at.ddo) && ((aid == "A000000063504B43532D3135") || (aid.substr(0, 10) == "E828BD080F"))) {
    				var t = new ASN1(new ByteString("7300", HEX));
    				at.ddo = new PKCS15_CIODDO(t);
    		  	}
    		  	if (at.ddo) {
    				var applentry;
    				applentry = this.factory.newOutlinePKCS15(this.pkcs15, aid, at);
    				view.insert(applentry.view);
    	  		}
    	  	}
    	}

    	this.expanded = true;
    }

    //
    // CTOR - ATR Outline
    //
    function OutlineATR(factory, atr) {
    	if (arguments.length == 0)
    		return;

    	this.factory = factory;
    	this.atr = atr;

    	// Create OutlineNode object and register in ATR object
    	var view = new OutlineNode("ATR=" + this.atr.toByteString().toString(HEX));
    	view.setIcon("atr");
    	view.setUserObject(this);
    	view.setToolTip("Click to decode ATR");
    	this.view = view;
    }

    OutlineATR.prototype.selectedListener = function() {
    	print(this.atr);
    }

    //
    // CTOR - FCP Outline
    //
    function OutlineFCP(factory, fcp) {
    	if (arguments.length == 0)
    		return;

    	this.factory = factory;
    	this.fcp = fcp;

    	// Create OutlineNode object and register in FCP object
    	var view = new OutlineNode("FCP=" + this.fcp.toString(HEX));
    	view.setIcon("header");
    	view.setUserObject(this);
    	this.view = view;

    	if ((fcp.byteAt(0) == 0x62) ||
    		(fcp.byteAt(0) == 0x64) ||
    		(fcp.byteAt(0) == 0x6F)) {
    		try	{
    			var asn = new ASN1(this.fcp);
    			FCPDecorator(asn);
    			view.insert(asn);
    		}
    		catch(e) {
    			print(e);
    			print("Unable to decode FCP structure");
    		}
    	} else {
    	}
    }

    //
    // CTOR - Outline node for DFs
    //
    function OutlineDF(factory, card, parentdf, id, name, profile) {
    	if (arguments.length == 0)
    		return;

    	this.factory = factory;
    	this.card = card;
    	this.parentdf = parentdf;
    	this.id = id;

    	// Create OutlineNode object and register in OutlineDF object
    	var view = new OutlineNode(name, true);
    	view.setUserObject(this);
    	this.view = view;

    	this.profile = profile;
    }

    //
    // Event handler for expand notifications
    //
    OutlineDF.prototype.expandListener = function() {

    	if (this.expanded)
    		return;

    	var view = this.view;

    	var eflist = null;
    	if (this.profile && this.profile.FileStructure) {
    		eflist = this.profile.FileStructure.EF;
    	}

    	var dflist = null;
    	if (this.profile && this.profile.DirectoryStructure) {
    		dflist = this.profile.DirectoryStructure.DF;
    	}

    	var dolist = null;
    	if (this.profile && this.profile.DataObjectStructure) {
    		dolist = this.profile.DataObjectStructure.DF;
    	}

    	try	{
    		var df;

    		if (this.parentdf) {
    			df = new CardFile(this.parentdf.df, this.id);
    			} else {
    			df = new CardFile(this.card, this.id);
    			}
    		this.df = df;

    		var fcp = df.getFCPBytes();
    		if (fcp && (fcp.length > 1)) {
    			var fcpmodel = this.factory.newOutlineFCP(fcp);
    			view.insert(fcpmodel.view);
    		}

    		if (dflist) {
    			for (var i = 0; i < dflist.length; i++) {
    				var dfp = dflist[i];

    				var sdf = this.factory.newOutlineDF(this.card, this, ":" + dfp.fid, dfp.name, dfp);
    				view.insert(sdf.view);
    			}
    		}

    		if (eflist) {
    			for (var i = 0; i < eflist.length; i++) {
    				var ef = this.factory.newOutlineEF(this.df, eflist[i].name, eflist[i]);
    				view.insert(ef.view);
    			}
    		}

    		if (dolist) {
    			for (var i = 0; i < dolist.length; i++) {
    				var doo = this.factory.newOutlineDataObject(this.df, dolist[i].id, dolist[i].name, dolist.format);
    				view.insert(doo.view);
    			}
    		}
    	}
    	catch(e) {
    		print(e);
    	}

    	this.expanded = true;
    }

    //
    // CTOR - Outline node for EFs
    //
    function OutlineEF(factory, df, name, profile) {
    	if (arguments.length == 0)
    		return;

    	this.factory = factory;
    	this.df = df;

    	// Create OutlineNode object and register in OutlineEF object
    	var view = new OutlineNode(name, true);
    	view.setIcon("document");
    	view.setUserObject(this);
    	this.view = view;

    	this.profile = profile;
    }

    //
    // Event handler for expand notification
    //
    OutlineEF.prototype.expandListener = function() {
    	if (this.expanded)
    		return;

    	var view = this.view;
    	var efdesc = this.profile;

    	try	{
    		var ef = new CardFile(this.df, ":" + efdesc.fid);
    	}
    	catch(e) {
    		print(e);
    		return;
    	}

    	var isTransparent = true;
    	var fcp = ef.getFCPBytes();
    	var filelength = -1;
    	if (fcp && (fcp.length > 1)) {
    		var fcpmodel = this.factory.newOutlineFCP(fcp);
    		view.insert(fcpmodel.view);
    		isTransparent = ef.isTransparent();
    		filelength = ef.getLength();
    	} else {
    		isTransparent = (efdesc.type == "T");
    	}

    	if (isTransparent) {
    		try	{
    			if (filelength > 0) {
    				var bs = ef.readBinary(0, filelength);
    			} else {
    				var bs = ef.readBinary();
    			}
    		}
    		catch(e) {
    			print(e);
    			this.expanded = true;
    			return;
    		}

    		var bindata = this.factory.newDataOutline(bs, efdesc.format);
    		view.insert(bindata.view);

    	} else {
    		for (var rec = 1; rec < 255; rec++) {
    			try {
    				ef.readRecord(rec);
    				var record = this.factory.newOutlineRecord(ef, rec, efdesc);
    				view.insert(record.view);
    			}
    			catch(e) {
    				if ((e instanceof GPError) && (e.reason == 0x6A83))
    					break;
    				print(e);
    				break;
    			}
    		}
    	}

    	this.expanded = true;
    }

    //
    // CTOR - Outline node for data object retrievable with GET_DATA
    //
    function OutlineDataObject(factory, df, id, name, format) {
    	if (arguments.length == 0)
    		return;

    	this.factory = factory;
    	this.df = df;
    	this.id = id;
    	this.format = format;

    	// Create OutlineNode object and register in OutlineEF object
    	print(name + "(" + id.toString(16) + ")");
    	var view = new OutlineNode(name + "(" + id.toString(16) + ")", true);
    	view.setIcon("document");
    	view.setUserObject(this);
    	this.view = view;
    }

    //
    // Event handler for expand notification
    //
    OutlineDataObject.prototype.expandListener = function() {
    	if (this.expanded)
    		return;

    	var view = this.view;

    	var card = this.df.card;

    	var bs = card.sendApdu(0x00, 0xCA, this.id >> 8, this.id & 0xFF, 0);

    	if (card.SW != 0x9000) {
    		var bs = card.sendApdu(0x80, 0xCA, this.id >> 8, this.id & 0xFF, 0);
    	}

    	if (card.SW != 0x9000) {
    		print("Error getting object: " + card.SWMSG);
    	} else {
    		var bindata = this.factory.newDataOutline(bs, this.format);
    		view.insert(bindata.view);
    	}

    	this.expanded = true;
    }

    //
    // CTOR - Outline for records in an EF
    //
    function OutlineRecord(factory, ef, no, efdesc) {
    	if (arguments.length == 0)
    		return;

    	this.factory = factory;
    	this.ef = ef;
    	this.no = no;
    	this.efdesc = efdesc;

    	// Create OutlineNode object and register in OutlineRecord object
    	var view = new OutlineNode("Record#" + no, true);
    	view.setIcon("record");
    	view.setUserObject(this);
    	this.view = view;
    }

    //
    // Expand event handler for records
    //
    OutlineRecord.prototype.expandListener = function() {
    	if (this.expanded)
    		return;

    	var view = this.view;
    	var ef = this.ef;

    	var bs = ef.readRecord(this.no);

    	var bindata = this.factory.newDataOutline(bs, this.efdesc.format);
    	view.insert(bindata.view);

    	this.expanded = true;
    }

    //
    // CTOR - Binary data outline
    //
    function DataOutline(factory, data, format) {
    	if (arguments.length == 0)
    		return;

    	this.factory = factory;
    	this.data = data;
    	this.format = format;

    	var ddata = data;
    	if (ddata.length > 256) {
    		ddata = data.bytes(0, 256);
    	}

    	var view = new OutlineNode(ddata.toString(HEX));
    	view.setIcon("binary");
    	view.setUserObject(this);
    	view.setToolTip("Select format from context menu");
    	view.setContextMenu(DataOutline.ctxMenuList);
    	this.view = view;
    	this.decorate(format);
    }

    DataOutline.ctxMenuList = ["as Hexdump", "as ASN1", "as X509"];

    DataOutline.prototype.decorate = function(format) {

    	var view = this.view;
    	var data = this.data;

    	if (format && ((format.substr(0, 4) == "asn1") || (format.substr(0, 7) == "tlvlist")) && (data.length >= 2)) {
    		var i = format.indexOf(".");

    		if ((i > 0) && (format.substr(i + 1) == "striprecordheader")) {
    			data = data.bytes(2);
    		}

    		var total = data.length;

    		while(total >= 2) {
    			try	{
    				var asn = new ASN1(data);
    			}
    			catch(e) {
    				print("Error in TLV structure: " + e);
    				return;
    			}

    			this.asn = asn;
    			this.asn1DecoratorHook(format);
    			view.insert(asn);
    			total -= asn.size;

    			if (format.substr(0, 3) == "asn") {
    				break;
    			}

    			data = data.bytes(asn.size);
    			if ((data.length == 0) || (data.byteAt(0) == 0x00) || (data.byteAt(0) == 0xFF)) {
    				break;
    			}
    		}

    		if (total > 0) {
    			var sparecontent = new OutlineNode(total + " spare bytes");
    			view.insert(sparecontent);
    		}
    	}
    }

    DataOutline.prototype.asn1DecoratorHook = function(format) {
    	var i = format.indexOf(".");

    	if (i > 0) {
    		switch(format.substr(i + 1)) {
    		case "arr" :
    			ARRDecorator(this.asn);
    			break;
    		}
    	}
    }

    DataOutline.prototype.actionListener = function(node, action) {
    	print("--------------------------------------------------------------------------------");

    	switch(action) {
    		case DataOutline.ctxMenuList[0]:
    			print(this.data);
    			break;
    		case DataOutline.ctxMenuList[1]:
    			var asn;
    			try	{
    				asn = new ASN1(this.data);
    				print(asn);
    			}
    			catch(e) {
    				print("Error in TLV structure: " + e);
    				return;
    			}
    			break;
    		case DataOutline.ctxMenuList[2]:
    			try 	{
    				var x509 = new X509(this.data);
    				print(x509.toString());
    			}
    			catch(e) {
    				print(e);
    			}
    			break;
    	}
    }

    //
    // CTOR - Outline node for PKCS15 structures
    //
    function OutlinePKCS15(factory, pkcs15, aid, at) {
    	if (arguments.length == 0)
    		return;

    	this.factory = factory;
    	this.pkcs15 = pkcs15;
    	this.aid = aid;
    	this.at = at;

    	var name = this.at.label;
    	if (!name) {
    		name = aid;
    	}

    	name = "PKCS#15 \"" + name + "\"";

    	// Create OutlineNode object and register in OutlineDF object
    	var view = new OutlineNode(name, true);
    	view.setUserObject(this);
    	this.view = view;
    }

    //
    // Event handler for expand notifications
    //
    OutlinePKCS15.prototype.expandListener = function() {

    	if (this.expanded)
    		return;

    	var view = this.view;
    	var at = this.at;

    	if (!at.ddo.ciaInfoPath) {
    		// Set a default path 5032 to EF.CIAInfo
    		var tlv = new ASN1(new ByteString("300404025032", HEX));
    		at.ddo.ciaInfoPath = new PKCS15_Path(tlv);
    		at.ddo.ciaInfoPath.aid = "#" + this.aid;
    	}

    	var cia = this.pkcs15.getCIAInfo(at.ddo.ciaInfoPath);

    	var label = cia.label;
    	if (!label) {
    		label = "CIAInfo";
    	} else {
    		label = "CIAInfo \"" + label + "\"";
    	}

    	cia.tlv.setName(label);
    	view.insert(cia.tlv);

    	if (!at.ddo.odfPath) {
    		// Set a default path 5031 to EF.OD
    		var tlv = new ASN1(new ByteString("300404025031", HEX));
    		at.ddo.odfPath = new PKCS15_Path(tlv);
    		at.ddo.odfPath.aid = "#" + this.aid;
    	}

    	this.pkcs15.readObjectListForApplication(at);

    	for (var i = 0; i < at.objlist.length; i++) {
    		var o = at.objlist[i];

    		var label = o.label;
    		if (!label) {
    			label = o.type;
    		} else {
    			label = o.type + " \"" + label + "\"";
    		}

    		if ((o.type == "X509Certificate") || (o.type == "TrustedX509Certificate") || (o.type == "UsefulX509Certificate")) {
    			var onode = new OutlineNode(label, true);
    			view.insert(onode);
    			o.tlv.setName("CIO");
    			onode.insert(o.tlv);

    			if (o.value && (o.value instanceof PKCS15_Path)) {
    				try	{
    					var bs = this.pkcs15.readCardObject(this.pkcs15.df, o.value);
    					var bindata = this.factory.newDataOutline(bs, "asn1.x509");
    					onode.insert(bindata.view);
    				}
    				catch(e) {
    					print("Unable to read certificate: " + e);
    				}
    			}
    		} else {
    			o.tlv.setName(label);
    			view.insert(o.tlv);
    		}
    	}

    	this.expanded = true;
    }

    //
    // CTOR - OutlineFactory
    // Derive from this class to include custom made outline elements
    //
    function CardOutlineFactory() {

    }

    //
    // Method to create new OutlineATR object
    //
    // Overwrite if you want to tailor this behaviour
    //
    CardOutlineFactory.prototype.newOutlineATR = function(atr) {
    	return new OutlineATR(this, atr);
    }

    //
    // Method to create new OutlineDF object
    //
    // Overwrite if you want to tailor this behaviour
    //
    CardOutlineFactory.prototype.newOutlineDF = function(card, parentdf, id, name, profile) {
    	return new OutlineDF(this, card, parentdf, id, name, profile);
    }

    //
    // Method to create new OutlineApplet object
    //
    // Overwrite if you want to tailor this behaviour
    //
    CardOutlineFactory.prototype.newOutlineApplet = function(instance) {
    	throw new GPError("CardOutlineFactory", GPError.INVALID_USAGE, 0, "Must overwrite newOutlineApplet to use explorer functionality");
    }

    //
    // Method to create new OutlinePKCS15 object
    //
    // Overwrite if you want to tailor this behaviour
    //
    CardOutlineFactory.prototype.newOutlinePKCS15 = function(pkcs15, aid, at) {
    	return new OutlinePKCS15(this, pkcs15, aid, at);
    }

    //
    // Method to create new OutlineEF object
    //
    // Overwrite if you want to tailor this behaviour
    //
    CardOutlineFactory.prototype.newOutlineEF = function(df, name, profile) {
    	return new OutlineEF(this, df, name, profile);
    }

    //
    // Method to create new OutlineDataObject object
    //
    // Overwrite if you want to tailor this behaviour
    //
    CardOutlineFactory.prototype.newOutlineDataObject = function(df, id, name, format) {
    	return new OutlineDataObject(this, df, id, name, format);
    }

    //
    // Method to create new OutlineFCP object
    //
    // Overwrite if you want to tailor this behaviour
    //
    CardOutlineFactory.prototype.newOutlineFCP = function(fcp) {
    	return new OutlineFCP(this, fcp);
    }

    //
    // Method to create new OutlineRecord object
    //
    // Overwrite if you want to tailor this behaviour
    //
    CardOutlineFactory.prototype.newOutlineRecord = function(ef, no, efdesc) {
    	return new OutlineRecord(this, ef, no, efdesc);
    }

    //
    // Method to create new DataOutline object
    //
    // Overwrite if you want to tailor this behaviour
    //
    CardOutlineFactory.prototype.newDataOutline = function(data, format) {
    	return new DataOutline(this, data, format);
    }

Overview | File | Class | Tree | Index | Help
PREV NEXT | FRAMES NO FRAMES

Documentation generated by JSDoc on Wed Jan 14 16:42:14 2026