1 /**
  2  *  ---------
  3  * |.##> <##.|  Open Smart Card Development Platform (www.openscdp.org)
  4  * |#       #|
  5  * |#       #|  Copyright (c) 1999-2018 CardContact Software & System Consulting
  6  * |'##> <##'|  Andreas Schwier, 32429 Minden, Germany (www.cardcontact.de)
  7  *  --------- 
  8  *
  9  *  This file is part of OpenSCDP.
 10  *
 11  *  OpenSCDP is free software; you can redistribute it and/or modify
 12  *  it under the terms of the GNU General Public License version 2 as
 13  *  published by the Free Software Foundation.
 14  *
 15  *  OpenSCDP is distributed in the hope that it will be useful,
 16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 18  *  GNU General Public License for more details.
 19  *
 20  *  You should have received a copy of the GNU General Public License
 21  *  along with OpenSCDP; if not, write to the Free Software
 22  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 23  *
 24  * @fileoverview Implementation of a ISO 7816-4 file system simulation
 25  */
 26 
 27 
 28 
 29 /**
 30  * Create a File Control Parameter containing information about a file system node
 31  *
 32  * @class Class storing File Control Parameter for a file system node
 33  * @constructor
 34  */
 35 function FCP() {
 36 }
 37 
 38 exports.FCP = FCP;
 39 
 40 
 41 /** File type for DF */
 42 FCP.DEDICATEDFILE = 0x38;
 43 
 44 /** File type for transparent EF */
 45 FCP.TRANSPARENT   = 0x01;
 46 
 47 /** File type for record oriented EF with fixed record size */
 48 FCP.LINEARFIXED   = 0x02;
 49 
 50 /** File type for record oriented EF with variable record size */
 51 FCP.LINEARVARIABLE   = 0x04;
 52 
 53 
 54 /**
 55  * Convert an integer value into an two byte ByteString
 56  *
 57  * @param {Number} val the value
 58  * @type ByteString
 59  * @return the 2 byte encoded value MSB||LSB
 60  */
 61 FCP.short2bytestring = function(val) {
 62 	var bb = new ByteBuffer();
 63 	bb.append(val >> 8);
 64 	bb.append(val & 0xFF);
 65 	return(bb.toByteString());
 66 }
 67 
 68 
 69 
 70 /**
 71  * Construct a new FCP object from parameters.
 72  *
 73  * <p>This function should never be called directly. Use newTransparentDF(), newDF() or newLinearEF() instead.</p>
 74  *
 75  * @param {String|ByteString} fid the file identifier (2 Bytes)
 76  * @param {Number} sfi the short file identifier or -1 or 0 if not defined
 77  * @param {Number} type the file type, one of FCP.DEDICATEDFILE, FCP.TRANSPARENT or FCP.LINEAR*
 78  * @param {Boolean} shareable true, if file may be shared between logical channels
 79  * @param {Boolean} internal true, if file is internal only and not externally selectable
 80  * @param {ByteString} supl supplemental information
 81  * @type FCP
 82  * @return the newly constructed FCP object
 83  */
 84 FCP.newFCP = function(fid, sfi, type, shareable, internal, supl) {
 85 	var fcp = new FCP();
 86 	
 87 	if (fid != null) {
 88 		if (typeof(fid) == "string") {
 89 			if (fid.length != 4) {
 90 				throw new GPError("FCP", GPError.INVALID_DATA, 0, "File Identifier must be 2 bytes");
 91 			}
 92 			fcp.fid = new ByteString(fid, HEX);
 93 		} else if (fid instanceof ByteString) {
 94 			if (fid.length != 2) {
 95 				throw new GPError("FCP", GPError.INVALID_DATA, 0, "File Identifier must be 2 bytes");
 96 			}
 97 			fcp.fid = fid;
 98 		} else {
 99 			throw new GPError("FCP", GPError.INVALID_TYPE, 0, "Argument must be of type String or ByteString");
100 		}
101 	}
102 	
103 	if (typeof(sfi) != "number") {
104 		throw new GPError("FCP", GPError.INVALID_TYPE, 1, "Argument must be of type Number");
105 	}
106 	if ((sfi >= -1) && (sfi <= 30)) {
107 		if (sfi > 0) {
108 			fcp.sfi = sfi;
109 		}
110 	} else {
111 		throw new GPError("FCP", GPError.INVALID_DATA, 1, "SFI must be in the range 1 to 30 or 0 if not defined");
112 	}
113 
114 	if (typeof(type) != "number") {
115 		throw new GPError("FCP", GPError.INVALID_TYPE, 2, "Argument must be of type Number");
116 	}
117 	fcp.type = type;
118 
119 	if (typeof(shareable) != "boolean") {
120 		throw new GPError("FCP", GPError.INVALID_TYPE, 3, "Argument must be of type Boolean");
121 	}
122 	fcp.shareable = shareable;
123 
124 	if (typeof(internal) != "boolean") {
125 		throw new GPError("FCP", GPError.INVALID_TYPE, 4, "Argument must be of type Boolean");
126 	}
127 	fcp.internal = internal;
128 
129 	fcp.supl = supl;
130 	return fcp;
131 }
132 
133 
134 
135 /**
136  * Construct a new FCP object for an EF of type transparent.
137  *
138  * @param {String|ByteString} fid the file identifier (2 Bytes)
139  * @param {Number} sfi the short file identifier or -1 or 0 if not defined
140  * @param {Number} size the file size
141  * @param {ByteString} supl supplemental information
142  * @type FCP
143  * @return the newly constructed FCP object
144  */
145 FCP.newTransparentEF = function(fid, sfi, size, supl) {
146 	if (typeof(size) != "number") {
147 		throw new GPError("FCP", GPError.INVALID_TYPE, 2, "Argument size must be of type Number");
148 	}
149 
150 	var fcp = FCP.newFCP(fid, sfi, FCP.TRANSPARENT, false, false, supl);
151 
152 	fcp.size = size;
153 	return fcp;
154 }
155 
156 
157 
158 /**
159  * Construct a new FCP object for a DF.
160  *
161  * @param {String|ByteString} fid the file identifier (2 Bytes)
162  * @param {ByteString} aid the DF's application identifier (DFName)
163  * @param {ByteString} supl supplemental information
164  * @type FCP
165  * @return the newly constructed FCP object
166  */
167 FCP.newDF = function(fid, aid, supl) {
168 	var fcp = FCP.newFCP(fid, -1, FCP.DEDICATEDFILE, false, false, supl);
169 
170 	if (aid != null) {
171 		if ((typeof(aid) != "object") && !(aid instanceof(ByteString))) {
172 			throw new GPError("FCP", GPError.INVALID_TYPE, 2, "Argument size must be of type Number");
173 		}
174 		fcp.aid = aid;
175 	}
176 
177 	return fcp;
178 }
179 
180 
181 
182 /**
183  * Construct a new FCP object for an EF of type linear.
184  *
185  * @param {String|ByteString} fid the file identifier (2 Bytes)
186  * @param {Number} sfi the short file identifier or -1 or 0 if not defined
187  * @param {Number} type the file type, one of FCP.LINEARFIXED or FCP.LINEARVARIABLE
188  * @param {Number} recno the maximum number of records
189  * @param {Number} recsize the maximum or fixed record size
190  * @param {ByteString} supl supplemental information
191  * @type FCP
192  * @return the newly constructed FCP object
193  */
194 FCP.newLinearEF = function(fid, sfi, type, recno, recsize, supl) {
195 	if (typeof(recsize) != "number") {
196 		throw new GPError("FCP", GPError.INVALID_TYPE, 3, "Argument recsize must be of type Number");
197 	}
198 	if (typeof(recno) != "number") {
199 		throw new GPError("FCP", GPError.INVALID_TYPE, 4, "Argument recno must be of type Number");
200 	}
201 
202 	var fcp = FCP.newFCP(fid, sfi, type, false, false, supl);
203 	return fcp;
204 }
205 
206 
207 
208 /**
209  * Returns the File Identifier (FID)
210  *
211  * @type ByteString
212  * @return the FID
213  */
214 FCP.prototype.getFID = function() {
215 	return this.fid;
216 }
217 
218 
219 
220 /**
221  * Returns the Application Identifier (AID)
222  *
223  * @type ByteString
224  * @return the AID
225  */
226 FCP.prototype.getAID = function() {
227 	return this.aid;
228 }
229 
230 
231 
232 /**
233  * Returns the Short File Identifier (SFI)
234  *
235  * @type Number
236  * @return the SFI
237  */
238 FCP.prototype.getSFI = function() {
239 	return this.sfi;
240 }
241 
242 
243 
244 /**
245  * Returns the encoded FCP
246  *
247  * @type ByteString
248  * @return the encoded FCP
249  */
250 FCP.prototype.getBytes = function() {
251 	var fcp = new ASN1("fcp", 0x62);
252 
253 	if (typeof(this.size) != "undefined") {
254 		fcp.add(new ASN1("fileSizeTransparent", 0x80, FCP.short2bytestring(this.size)));
255 	}
256 
257 	var bb = new ByteBuffer();
258 	bb.append(this.type);
259 	
260 	// ToDo: extra type bytes
261 	
262 	fcp.add(new ASN1("fileDescriptor", 0x82, bb.toByteString()));
263 	
264 	if (typeof(this.fid) != "undefined") {
265 		fcp.add(new ASN1("fileIdentifier", 0x83, this.fid));
266 	}
267 	
268 	if (typeof(this.aid) != "undefined") {
269 		fcp.add(new ASN1("dFName", 0x84, this.aid));
270 	}
271 	
272 	if (typeof(this.sfi) != "undefined") {
273 		var bb = new ByteBuffer();
274 		bb.append(this.sfi << 3);
275 		fcp.add(new ASN1("shortFileIdentifier", 0x88, bb.toByteString()));
276 	}
277 	
278 	return(fcp.getBytes());
279 }
280 
281 
282 
283 /**
284  * Returns the FCI
285  *
286  * @type ASN1
287  * @return the FCI
288  */
289 FCP.prototype.getFCI = function() {
290 	var fci = new ASN1("fci", 0x6F);
291 
292 	if (typeof(this.aid) != "undefined") {
293 		fci.add(new ASN1("dFName", 0x84, this.aid));
294 	}
295 
296 	if (this.supl) {
297 		fci.add(new ASN1(this.supl));
298 	}
299 
300 	return(fci);
301 }
302 
303 
304 
305 /**
306  * Return a human readible string for this object
307  *
308  * @type String
309  * @return the string
310  */
311 FCP.prototype.toString = function() {
312 	var str = "FCP(";
313 	
314 	for (var i in this) {
315 		if (typeof(this[i]) != "function") {
316 			str += i + "=" + this[i] + ",";
317 		}
318 	}
319 	str += ")";
320 	return str;
321 }
322