1 /**
  2  *  ---------
  3  * |.##> <##.|  Open Smart Card Development Platform (www.openscdp.org)
  4  * |#       #|  
  5  * |#       #|  Copyright (c) 1999-2009 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 Access controller for eID card
 25  */
 26 
 27 load("../cardsim/accesscontroller.js");
 28 
 29 
 30  
 31 /**
 32  * Create an access controller for the Master File
 33  * @Class Class implementing an access controller for the Master File
 34  * @constructor
 35  */
 36 function MFAccessController() {
 37 	AccessController.call(this);
 38 	this.name = "MFAccessController";
 39 }
 40 MFAccessController.prototype = new AccessController();
 41 MFAccessController.constructor = MFAccessController;
 42 
 43 
 44 /**
 45  * Check if read access to file system node is allowed
 46  *
 47  * @param {eIDCommandInterpreter} ci the command interpreter
 48  * @param {APDU} apdu the APDU used to access the object
 49  * @param {FSNode} node the file system object
 50  * @type boolean
 51  * @return true if access is allowed
 52  */
 53 MFAccessController.prototype.checkFileReadAccess = function(ci, apdu, node) {
 54 	var fid = node.getFCP().fid.toUnsigned();
 55 	if ((fid == 0x011C) || (fid == 0x2F01)) {
 56 		return true;
 57 	}
 58 
 59 	if (!apdu.isSecureMessaging()) {
 60 		GPSystem.trace("Read access not allowed without secure messaging");
 61 		return false;
 62 	}
 63 
 64 	if (!ci.isAuthenticatedTerminal()) {
 65 		GPSystem.trace("Must have passed terminal authentication");
 66 		return false;
 67 	}
 68 
 69 	if (fid == 0x011b) {				// EF.ChipSecurity only readable if priviledged terminal right is granted
 70 		return this.checkRight(ci, apdu, 3);
 71 	}
 72 	return true;
 73 }
 74 
 75 
 76 
 77 /**
 78  * Check if access to special function is allowed
 79  *
 80  * @param {eIDCommandInterpreter} ci the command interpreter
 81  * @param {APDU} apdu the APDU used to access the object
 82  * @type boolean
 83  * @return true if access is allowed
 84  */
 85 MFAccessController.prototype.checkRight = function(ci, apdu, bit) {
 86 	if (!apdu.isSecureMessaging()) {
 87 		GPSystem.trace("Special functions can only be performed with secure messaging");
 88 		return false;
 89 	}
 90 
 91 	if (!ci.isAuthenticatedTerminal()) {
 92 		GPSystem.trace("Must have passed terminal authentication");
 93 		return false;
 94 	}
 95 
 96 	// Other roles than id-AT have no access
 97 	if (!ci.getTerminalRole().equals(new ByteString("id-AT", OID))) {
 98 		GPSystem.trace("No access to roles other than id-AT");
 99 		return false;
100 	}
101 
102 	// The integer value must not excced 2^32, so we only take the first 4 byte of the CHAT
103 	var mask = ByteString.valueOf(1 << bit, 5);
104 	print("EffRights:" + ci.effectiveRights);
105 	print("ReqRights:" + mask);
106 	print(mask.and(ci.effectiveRights));
107 	return ci.effectiveRights.and(mask).right(4).toUnsigned() > 0;
108 }
109 
110 
111 
112 /**
113  * Create an access controller for the ePass application
114  * @Class Class implementing an access controller for the ePass application
115  * @constructor
116  */
117 function ePassAccessController() {
118 	AccessController.call(this);
119 	this.name = "ePassAccessController";
120 }
121 ePassAccessController.prototype = new AccessController();
122 ePassAccessController.constructor = ePassAccessController;
123 
124 
125 
126 /**
127  * Check if read access to file system node is allowed
128  *
129  * @param {eIDCommandInterpreter} ci the command interpreter
130  * @param {APDU} apdu the APDU used to access the object
131  * @param {FSNode} node the file system object
132  * @type boolean
133  * @return true if access is allowed
134  */
135 ePassAccessController.prototype.checkFileReadAccess = function(ci, apdu, node) {
136 	if (!apdu.isSecureMessaging()) {
137 		GPSystem.trace("Read access not allowed without secure messaging");
138 		return false;
139 	}
140 
141 	// A secure channel with id-AT or id-ST does not qualify to read DF.ePass
142 	if (ci.isAuthenticatedTerminal()) {
143 		if (!ci.getTerminalRole().equals(new ByteString("id-IS", OID))) {
144 			GPSystem.trace("No access to roles other than id-IS");
145 			return false;
146 		}
147 	}
148 
149 	var fid = node.getFCP().fid.toUnsigned();
150 	if ((fid != 0x0103) && (fid != 0x0104)) {
151 		return true;
152 	}
153 
154 	if (!ci.isAuthenticatedTerminal()) {
155 		GPSystem.trace("Must have passed terminal authentication");
156 		return false;
157 	}
158 
159 	var mask = ByteString.valueOf(0x01 << ((fid & 0xFF) - 3), 1);
160 	print("EffRights:" + ci.effectiveRights);
161 	print("ReqRights:" + mask);
162 	print(mask.and(ci.effectiveRights));
163 	return ci.effectiveRights.and(mask).toUnsigned() > 0;
164 }
165 
166 
167 
168 /**
169  * Check if access to special function is allowed
170  *
171  * @param {eIDCommandInterpreter} ci the command interpreter
172  * @param {APDU} apdu the APDU used to access the object
173  * @type boolean
174  * @return true if access is allowed
175  */
176 ePassAccessController.prototype.checkRight = function(ci, apdu, bit) {
177 	return false;
178 }
179 
180 
181 
182 /**
183  * Check if command is allowed
184  *
185  * @param {APDU} apdu the APDU to check
186  * @type boolean
187  * @return true if access is allowed
188  */
189 ePassAccessController.prototype.checkCommandAccess = function(ci, apdu) {
190 	if ((apdu.getINS() == 0xA4) && (apdu.getP1() != 0x04)) {
191 		return apdu.isSecureMessaging();
192 	}
193 	return true;
194 }
195 
196 
197 
198 /**
199  * Create an access controller for the eID application
200  * @Class Class implementing an access controller for the eID application
201  * @constructor
202  */
203 function eIDAccessController() {
204 	AccessController.call(this);
205 	this.name = "eIDAccessController";
206 }
207 eIDAccessController.prototype = new AccessController();
208 eIDAccessController.constructor = eIDAccessController;
209 
210 
211 
212 /**
213  * Check basic access conditions for eID application
214  *
215  * @param {eIDCommandInterpreter} ci the command interpreter
216  * @param {APDU} apdu the APDU used to access the object
217  * @type boolean
218  * @return true if access is allowed based on basic checks
219  */
220 eIDAccessController.prototype.checkBasicAccess = function(ci, apdu) {
221 	if (!apdu.isSecureMessaging()) {
222 		GPSystem.trace("Read access not allowed without secure messaging");
223 		return false;
224 	}
225 
226 	if (!ci.isAuthenticatedTerminal()) {
227 		GPSystem.trace("No access to unauthenticated terminal");
228 		false;
229 	}
230 
231 	// Access to eID functions only allowed with eID PIN or if CAN allowed is granted and PACE(CAN) was performed
232 	if (ci.paceao.id == 3) {
233 		return true;
234 	}
235 
236 	if (!ci.getTerminalRole().equals(new ByteString("id-AT", OID))) {
237 		return true;
238 	}
239 
240 	// CAN allowed checking only if id-AT terminal
241 	if (!this.checkBit(ci, apdu, 4)) {
242 		print("CAN allowed right not granted");
243 		return false;
244 	}
245 
246 	if (ci.paceao.id != 2) {
247 		print("CAN allowed only effective for PACE with CAN");
248 		return false;
249 	}
250 	return true;
251 }
252 
253 
254 /**
255  * Check if read access to file system node is allowed
256  *
257  * @param {eIDCommandInterpreter} ci the command interpreter
258  * @param {APDU} apdu the APDU used to access the object
259  * @param {FSNode} node the file system object
260  * @type boolean
261  * @return true if access is allowed
262  */
263 eIDAccessController.prototype.checkFileReadAccess = function(ci, apdu, node) {
264 	if (!this.checkBasicAccess(ci, apdu)) {
265 		return false;
266 	}
267 
268 	// Check valid range 0x0101 - 0x0115
269 	var fid = node.getFCP().fid;
270 	if ((fid.byteAt(0) != 0x01) || (fid.byteAt(1) < 0x01) || (fid.byteAt(1) > 0x15)) {
271 		GPSystem.trace("FID " + fid + " out of defined range");
272 		return false;
273 	}
274 
275 	// IS have generell access if GAP with CHAT was used
276 	if ((ci.getTerminalRole().equals(new ByteString("id-IS", OID)) && ci.chat)) {
277 		return true;
278 	}
279 	
280 	// Other roles than id-AT have no access
281 	if (!ci.getTerminalRole().equals(new ByteString("id-AT", OID))) {
282 		GPSystem.trace("No access to roles other than id-AT");
283 		return false;
284 	}
285 
286 	var mask = ByteString.valueOf(0x0100 << (fid.byteAt(1) - 1), 5);
287 	print("EffRights:" + ci.effectiveRights);
288 	print("ReqRights:" + mask);
289 	print(mask.and(ci.effectiveRights));
290 	return ci.effectiveRights.and(mask).toUnsigned() > 0;
291 }
292 
293 
294 
295 /**
296  * Check if write access to file system node is allowed
297  *
298  * @param {eIDCommandInterpreter} ci the command interpreter
299  * @param {APDU} apdu the APDU used to access the object
300  * @param {FSNode} node the file system object
301  * @type boolean
302  * @return true if access is allowed
303  */
304 eIDAccessController.prototype.checkFileWriteAccess = function(ci, apdu, node) {
305 	if (!this.checkBasicAccess(ci, apdu)) {
306 		return false;
307 	}
308 
309 	// Check valid range 0x0101 - 0x0115
310 	var fid = node.getFCP().fid;
311 	if ((fid.byteAt(0) != 0x01) || (fid.byteAt(1) < 0x11) || (fid.byteAt(1) > 0x15)) {
312 		GPSystem.trace("FID out of defined range");
313 		return false;
314 	}
315 
316 	// Other roles than id-AT have no access
317 	if (!ci.getTerminalRole().equals(new ByteString("id-AT", OID))) {
318 		GPSystem.trace("No access to roles other than id-AT");
319 		return false;
320 	}
321 
322 	// The integer value must not excced 2^32, so we only take the first 4 byte of the CHAT
323 	var mask = ByteString.valueOf(0x20000000 >> (fid.byteAt(1) - 17), 4);
324 	mask = mask.concat(new ByteString.valueOf(0, 1));
325 	print("EffRights:" + ci.effectiveRights);
326 	print("ReqRights:" + mask);
327 	print(mask.and(ci.effectiveRights));
328 	return ci.effectiveRights.and(mask).left(4).toUnsigned() > 0;
329 }
330 
331 
332 
333 /**
334  * Check if access to special function is allowed
335  *
336  * @param {eIDCommandInterpreter} ci the command interpreter
337  * @param {APDU} apdu the APDU used to access the object
338  * @type boolean
339  * @return true if access is allowed
340  */
341 eIDAccessController.prototype.checkBit = function(ci, apdu, bit) {
342 	// The integer value must not excced 2^32, so we only take the first 4 byte of the CHAT
343 	var mask = ByteString.valueOf(1 << bit, 5);
344 	print("EffRights:" + ci.effectiveRights);
345 	print("ReqRights:" + mask);
346 	print(mask.and(ci.effectiveRights));
347 	return ci.effectiveRights.and(mask).right(4).toUnsigned() > 0;
348 }
349 
350 
351 
352 /**
353  * Check if access to special function is allowed
354  *
355  * @param {eIDCommandInterpreter} ci the command interpreter
356  * @param {APDU} apdu the APDU used to access the object
357  * @type boolean
358  * @return true if access is allowed
359  */
360 eIDAccessController.prototype.checkRight = function(ci, apdu, bit) {
361 	if (!this.checkBasicAccess(ci, apdu)) {
362 		return false;
363 	}
364 	
365 	// Other roles than id-AT have no access
366 	if (!ci.getTerminalRole().equals(new ByteString("id-AT", OID))) {
367 		GPSystem.trace("No access to roles other than id-AT");
368 		return false;
369 	}
370 
371 	return this.checkBit(ci, apdu, bit);
372 }
373 
374 
375 
376 /**
377  * Create an access controller for the eSign application
378  * @Class Class implementing an access controller for the eSign application
379  * @constructor
380  */
381 function eSignAccessController() {
382 	AccessController.call(this);
383 	this.name = "eSignAccessController";
384 }
385 eSignAccessController.prototype = new AccessController();
386 eSignAccessController.constructor = eSignAccessController;
387 
388 
389 
390 
391 /**
392  * Check if read access to file system node is allowed
393  *
394  * @param {eIDCommandInterpreter} ci the command interpreter
395  * @param {APDU} apdu the APDU used to access the object
396  * @param {FSNode} node the file system object
397  * @type boolean
398  * @return true if access is allowed
399  */
400 eSignAccessController.prototype.checkFileReadAccess = function(ci, apdu, node) {
401 	if (!apdu.isSecureMessaging()) {
402 		GPSystem.trace("Read access not allowed without secure messaging");
403 		return false;
404 	}
405 
406 	if (!ci.isAuthenticatedTerminal()) {
407 		GPSystem.trace("No access to unauthenticated terminal");
408 		return false;
409 	}
410 
411 	// ST have generell access
412 	if (ci.getTerminalRole().equals(new ByteString("id-ST", OID))) {
413 		return true;
414 	}
415 
416 	// AT have access may have right to install certificate
417 	if (ci.getTerminalRole().equals(new ByteString("id-AT", OID))) {
418 		if (ci.effectiveRights.right(1).toUnsigned() & 0xC0) {
419 			return true;
420 		}
421 		GPSystem.trace("AT terminal has no right to install certificate");
422 	}
423 
424 	return false;
425 }
426 
427 
428 
429 /**
430  * Check if write access to file system node is allowed
431  *
432  * @param {eIDCommandInterpreter} ci the command interpreter
433  * @param {APDU} apdu the APDU used to access the object
434  * @param {FSNode} node the file system object
435  * @type boolean
436  * @return true if access is allowed
437  */
438 eSignAccessController.prototype.checkFileWriteAccess = function(ci, apdu, node) {
439 	if (!apdu.isSecureMessaging()) {
440 		GPSystem.trace("Write access not allowed without secure messaging");
441 		return false;
442 	}
443 
444 	if (!ci.isAuthenticatedTerminal()) {
445 		GPSystem.trace("No access to unauthenticated terminal");
446 		false;
447 	}
448 
449 	// AT have access may have right to install certificate
450 	if (ci.getTerminalRole().equals(new ByteString("id-AT", OID))) {
451 		if (ci.effectiveRights.right(1).toUnsigned() & 0xC0) {
452 			return true;
453 		}
454 		GPSystem.trace("AT terminal has no right to install certificate");
455 	}
456 
457 	return false;
458 }
459 
460 
461 
462 /**
463  * Check if access to special function is allowed
464  *
465  * @param {eIDCommandInterpreter} ci the command interpreter
466  * @param {APDU} apdu the APDU used to access the object
467  * @type boolean
468  * @return true if access is allowed
469  */
470 eSignAccessController.prototype.checkRight = function(ci, apdu, bit) {
471 	if (!apdu.isSecureMessaging()) {
472 		GPSystem.trace("Special functions can only be performed with secure messaging");
473 		return false;
474 	}
475 
476 	if (!ci.isAuthenticatedTerminal()) {
477 		GPSystem.trace("No access to unauthenticated terminal");
478 		false;
479 	}
480 	
481 	return true;
482 }
483