1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601
|
/**************************************************************************/
/* */
/* The Why platform for program certification */
/* Copyright (C) 2002-2008 */
/* Romain BARDOU */
/* Jean-Franois COUCHOT */
/* Mehdi DOGGUY */
/* Jean-Christophe FILLITRE */
/* Thierry HUBERT */
/* Claude MARCH */
/* Yannick MOY */
/* Christine PAULIN */
/* Yann RGIS-GIANAS */
/* Nicolas ROUSSET */
/* Xavier URBAIN */
/* */
/* This software 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. */
/* */
/* This software 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 version 2 for more details */
/* (enclosed in the file GPL). */
/* */
/**************************************************************************/
/* $Id: SCID.java,v 1.15 2008/04/22 06:53:45 nrousset Exp $ */
//@+ CheckArithOverflow = no
//@+ InvariantPolicy = Arguments
// @+ AnnotationPolicy = Invariants
// @+ AbstractDomain = Box
// @+ AbstractDomain = Oct
// @+ AbstractDomain = Pol
// Smart Card ID - Example for a Smart Card based Identification and Authentication Application
// File: SCID.java
//
// This applet realizes a identification and authentication application which is typically used
// in online environment. The advantage of this solution is that nearly no adinistrative overhead
// on the smart card side is neccessary because the card is only used for identification of the
// user by PIN verification and authentication of the smart card by a challenge response algorithm.
//
// Smart Card ID is a multi purpose applet. Nearly all applications can be realised with it
// (e.g. physical access for room, logical access computers or information, ....).
// The application functionality is in the background system. Offline Terminals can use precomputed
// triplets for secure authentication of the smart card. For a no security authentication only the
// smart card ID can be used.
//
// In the administrative phase it is possible to write after a successful verification of the
// administrative PIN all data objects into the memory.
// In the operative phase it is possible to read the unique smart card ID, to authenticate the
// smart card with a callenge response algorithm based on DES and to identifiay the user with
// his user PIN. A successful PIN verification can be checked in a secured way.
//
// Memory consumption: EEPROM: 1344 byte, RAM 44 byte
//
// Package AID: 'D2 76 00 00 60 50 05'
// Applet AID: 'D2 76 00 00 60 41 05'
//
// Source code based on java card specification version 2.1.
// Compiled and tested with JDK 1.3.1 Java Card Application Studio (JCAST) V 2.3,
// JLoad 2.0, Java Card Class File Converter 2.1.2, IFDSIM 4.0 and UniverSIM Java Card 64 kB from G&D.
//
//---------------------------------------------------------------------------------------
// Specification of ISO/IEC 7816-4 case 3 command SELECT FILE
// command APDU CLA = '00' || INS = 'A4' ||
// P1 (= '04' = select by DF Name) || P2 (='00') ||
// Lc (= length of DF Name) || DATA (= DF Name)
// response APDU (all case) SW1 || SW2
//
// Specification of ISO/IEC 7816-4 case 3 command VERIFY
// command APDU CLA = '00' || INS = '20' || P1 (= '00') ||
// P2 ('01' = user PIN, '02' = admin PIN)
// Lc (= length of PIN) || DATA (= PIN)
// response APDU (all case) SW1 || SW2
//
// Specification of ISO/IEC 7816-4 case 3 command PUT DATA
// command APDU CLA = '00' || INS = 'DA' ||
// P1 (= '00') || P2 (= TAG ['40' ... 'FE']) ||
// Lc (= length of DATA) ||
// DATA (value field of the DO, with length Lc)
// response APDU (all case) SW1 || SW2
//
// Specification of ISO/IEC 7816-4 case 2 command GET DATA
// command APDU CLA = '00' || INS = 'CA' ||
// P1 (= '00') || P2 (= TAG ['40' ... 'FE'])
// Le (= length of expected data)
// response APDU (good case) DATA (value field of the DO, with length Le) ||
// SW1 || SW2
// response APDU (bad case) SW1 || SW2
//
// Specification of the proprietary case 4 command AUTH SC (authenticate smart card)
// command APDU CLA = '80' || INS = '08' || P1 (= '00') || P2 (= '00')
// Lc (= 8 byte) || C-DATA (with length Lc) || Le (= 21)
// response APDU (good case) R-DATA (with length Le) || SW1 || SW2
// response APDU (bad case) SW1 || SW2
//
// C-DATA: RND [8 byte]
// R-DATA: authentication data || MAC
// authentication data = RND [8 byte] || smart card ID [4 byte] ||
// authentication counter [2 byte] ||
// admin PIN error counter [1 byte] ||
// user PIN error counter [1 byte] || PIN status [1 byte] ||
// padding (with '00', length must be a multiple of 8)
// PIN status = Bit 8 (RFU) || ... Bit 3 (RFU) ||
// Bit 2 (1 = admin PIN succesful verified) ||
// Bit 1 (1 = user PIN succesful verified)
// MAC = last 4 byte of enc (AuthKey) (authentication data)
// authentication counter: no of further possible authentications
// '7FFF' = 32767 max. value,
// '0000' = no further authentication possible
// padding according to ISO/IEC 9797 Method 1 (= 00 00 ...)
//
//---------------------------------------------------------------------------------------
// Implementation Notes
// * no support of different DFs
// * no secure messaging support (= class is alway '00')
// * the authentication counter is used to limit the number of possible attacks to
// the used cryptoalgorithm, it can be set to any value with the PUT DATA command
// * instead of the encryption of the whole response data in the AUTH SC command only
// a 4 byte MAC is used for authentication, because this gives more transperency about
// the exchanged data (data protection laws)
// * the adminstration phase (e.g. the personalisation of the smart card) is done with the
// PUT DATA command. All external seeable data elements of the smart card can be written
// with this ISO/IEC 7816-4 compatible command.
// * The access condition for the administration phase is a VERIFY command with the
// administration PIN.
// * the authentication key could be smart card individual or common for all smart cards
// * if neccessary it is possible to use smart card individual authentication keys. This
// keys should be written into the smart card during the personalisation. As a reference
// to smart card individual keys the smart card ID can be used.
// * abbreviations: RND - random number
// RFU - reserved for future use (content of such a data element is not defined!)
// admin - administration
//
//---------------------------------------------------------------------------------------
// Use Cases
// note: all shown use cases are good cases
// format: --command to smart card-->
// <--response from smart card--
//
// -----Administrative Phase
// Personalisation
// --VERIFY (with default Admin PIN)-->
// <--ok--
// --PUT DATA (Smart Card ID, admin PIN, user PIN, authentication key, authentication counter)-->
// <--ok--
// the following reset is important because of the reset of the validation status of the admin PIN
// --Reset-->
// <--ATR--
//
// change user PIN (e.g. new user PIN, forgotten user PIN, error counter of the user PIN reached maximum value)
// the following command is only neccessary if a card individual admin PIN is used
// --GET DATA (smart card ID)-->
// <--Smart Card ID--
// --VERIFY (with admin PIN)-->
// <--ok--
// --PUT DATA (new user PIN)-->
// <--ok--
// the following reset is important because of the reset of the validation status of the admin PIN
// --Reset-->
// <--ATR--
//
// forgotten admin PIN, error counter of the admin PIN reached maximum value
// new smart card neccessary
//
// -----Operative Phase
// get smart card ID
// --GET DATA (smart card ID)-->
// <--smart card ID--
//
// authenticate smart card without security (advantage: no key is neccessary)
// --GET DATA (Smart Card ID)-->
// <--smart card ID--
//
// authenticate smart card with security
// the following command is only neccessary if a card individual authentication key is used
// --GET DATA (smart card ID)-->
// <--smart card ID--
// --AUTH SC (RND)-->
// <--auth data || MAC--
//
// check user identity
// the following command is only neccessary if a card individual authentication key is used
// --GET DATA (smart card ID)-->
// <--smart card ID--
// --VERIFY (with user PIN)-->
// <--ok--
// --AUTH SC (RND)-->
// <--auth data || MAC--
//
//---------------------------------------------------------------------------------------
// Test Strategy
// good case tests:
// 1.1 test all use cases in good case
// 2.1 write all data objects and verify if they are stored correct
// 3.1 Test all marked "Test!" testpoints
//
// bad case tests:
// 1.1 test correct and wrong admin PIN incl. error counter behaviour
// 1.2 is PUT DATA only possible after successful admin PIN verification?
// 1.3 does AUTH SC give the correct admin PIN validation state back?
// 1.4 does AUTH SC give the correct admin PIN error counter back?
// 2.1 test case 1.1 with user PIN
// 2.2 test case 1.3 with user PIN
// 2.2 test case 1.4 with user PIN
// 4.1 correct authetication behaviour if error counter = 0?
//
//---------------------------------------------------------------------------------------
// This source code is under GNU general public license (see www.opensource.org for details).
// Please send corrections and ideas for extensions to Wolfgang Rankl (www.wrankl.de)
// Copyright 2004 by Wolfgang Rankl, Munich
//---------------------------------------------------------------------------------------
// 29. April 2004 - V 1: initial runnable version
// 10. May 2004 - V 2: improved documentation, 1st published version
//---------------------------------------------------------------------------------------
import javacard.framework.*; // import all neccessary packages for the java card framework
import javacard.security.*; // import all neccessary packages for the java card security
// Bug with lemma ? : gwhy blocked ?
// @ lemma a1: \forall integer b; 0 <= b <= 127 ==> (b & 0x00FF) == b;
// @ axiom a1: \forall integer b; 0 <= b <= 127 ==> (b & 0x00FF) == b;
public class SCID extends Applet {
// definitions for the classes and commands
final static byte PROP_CLASS = (byte) 0x80; // Class of the proprietary APDU commands
final static byte INS_SELECT = (byte) 0xA4; // instruction for the ISO/IEC 7816-4 SELECT FILE command
final static byte INS_VERIFY = (byte) 0x20; // instruction for the ISO/IEC 7816-4 VERIFY command
final static byte INS_PUTDATA = (byte) 0xDA; // instruction for the ISO/IEC 7816-4 PUT DATA command
final static byte INS_GETDATA = (byte) 0xCA; // instruction for the ISO/IEC 7816-4 GET DATA command
final static byte INS_AUTHSC = (byte) 0x08; // instruction for the proprietary AUTH SC command
// definitions for specific returncodes
final static short SW_PIN_FAILED = (short) 0x63C0; // returncode for PIN verification failed
// the last nibble shows the number of remaining tries
final static short SW_DATA_NOT_FOUND = (short) 0x6A88; // referenced data not found
// definitions for the AUTH SC command
final static byte LEN_CAPDU_AUTHSC = (byte) 8; // length of the data in the command APDU of the AUTH SC command
final static byte LEN_RAPDU_AUTHSC = (byte) 21; // length of the data in the response APDU of the AUTH SC command
final static short INDEX_RND = (short) 0; // start position of the RND in the AUTH SC response
final static short INDEX_SCID = (short) 8; // start position of the authentication counter in the AUTH SC response
final static short INDEX_AuthCntr = (short) 12; // start position of the authentication counter in the AUTH SC response
final static short INDEX_APINTries = (short) 14; // position of the admin PIN remaining tries in the AUTH SC response
final static short INDEX_UPINTries = (short) 15; // position of the user PIN remaining tries in the AUTH SC response
final static short INDEX_APINValid = (short) 16; // position of the admin PIN validation status in the AUTH SC response
final static short INDEX_UPINValid = (short) 16; // position of the user PIN validation status in the AUTH SC response
final static short INDEX_MAC = (short) 17; // start position of the MAC in the AUTH SC response
// definitions for some data elements
static short authcntr; // authentication counter, it is a 2 byte signed short value
final static short SIZE_AUTHCNTR = (short) 2; // size of the authentication counter in byte
static byte[] scid; // the smart card ID
final static short SIZE_SCID = (short) 4; // size of the unique smart card identifier in byte
//@ static invariant scid_inv: scid != null && scid.length == SIZE_SCID;
static byte[] workarray; // workarray
final static short SIZE_WORKARRAY = (short) 30; // size of the workarray
//@ static invariant workarray_inv: workarray != null && workarray.length == SIZE_WORKARRAY;
static DESKey authkey; // authentication key
//@ static invariant authkey_inv: authkey != null;
final static short SIZE_AUTHKEY = (short) 8; // authentication key is a 8 byte long DES key
static Signature mac; // MAC (message authentication code)
//@ static invariant mac_inv: mac != null;
// definitions for the storage of the data elements
final static short TAG_USERPIN = (short) 0x51; // tag for the user PIN
final static short TAG_ADMINPIN = (short) 0x52; // tag for the administrative PIN
final static short TAG_AUTHKEY = (short) 0x53; // tag for the authentication key
final static short TAG_AUTHCNTR = (short) 0x54; // tag for the athentication counter
final static short TAG_SCID = (short) 0x55; // tag for the smart card ID
// constants and variables for the admin PIN management
final static byte[] DEFAULT_ADMINPIN // default admin PIN value
= {(byte) 0x22, (byte) 0x22, (byte) 0x22};
final static byte ADMINPIN_SIZE = (byte) 3; // size of the PIN in byte
final static byte DEFAULT_ADMINPIN_MAXEC = (byte) 2; // default value of the PIN error counter
final static byte ADMINPIN_ID = (byte) 2; // PIN identifier, PIN 2 = admin PIN
static OwnerPIN adminpin; // the PIN object
//@ static invariant adminpin_inv: adminpin != null;
// constants and variables for the user PIN management
final static byte[] DEFAULT_USERPIN // default user PIN value
= {(byte) 0x00, (byte) 0x00};
final static byte USERPIN_SIZE = (byte) 2; // size of the PIN in byte
final static byte DEFAULT_USERPIN_MAXEC = (byte) 3; // default value of the PIN error counter
final static byte USERPIN_ID = (byte) 1; // PIN identifier, PIN 1 = user PIN
static OwnerPIN userpin; // the PIN object
//@ static invariant userpin_inv: userpin != null;
// constants and variables for the key management
final static byte[] DEFAULT_AUTHKEY // default authentication key
= {(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00};
//---------------------------------------------------------------------------------------
//----- installation and registration of the applet
public static void install(byte[] buffer, short offset, byte length) {
// create the object for the smart card ID
scid = new byte[SIZE_SCID];
// create a multiple useable object in RAM with an array of bytes
workarray = JCSystem.makeTransientByteArray(SIZE_WORKARRAY, JCSystem.CLEAR_ON_DESELECT); // Test!
// create the object for admin PIN and set it to the default value
adminpin = new OwnerPIN(DEFAULT_ADMINPIN_MAXEC, ADMINPIN_SIZE);
adminpin.update(DEFAULT_ADMINPIN, (short)(0), ADMINPIN_SIZE);
// create the object for user PIN and set it to the default value
userpin = new OwnerPIN(DEFAULT_USERPIN_MAXEC, USERPIN_SIZE);
userpin.update(DEFAULT_USERPIN, (short)(0), USERPIN_SIZE);
// create the key object for authentication and set the key to the default value
authkey = (DESKey)KeyBuilder.buildKey(KeyBuilder.TYPE_DES, KeyBuilder.LENGTH_DES, false);
authkey.setKey(DEFAULT_AUTHKEY, (short)0);
// create a signature object for MAC calculating
// padding in line with ISO/IEC 9797 Method 1 (= 000 ...)
mac = Signature.getInstance(Signature.ALG_DES_MAC4_ISO9797_M1, false);
// register the application within the JCRE
new SCID().register();
} // install
//---------------------------------------------------------------------------------------
//----- this is the command dispatcher
//@ requires apdu != null;
public void process(APDU apdu) {
byte[] cmd_apdu = apdu.getBuffer();
// delete global used variables, for better robustness
Util.arrayFillNonAtomic(workarray, (short) 0, SIZE_WORKARRAY, (byte) 0);
if (cmd_apdu[ISO7816.OFFSET_CLA] == ISO7816.CLA_ISO7816) {
//----- it is the ISO/IEC 7816 class
switch(cmd_apdu[ISO7816.OFFSET_INS]) {
case INS_SELECT:
// it is a SELECT FILE instruction
cmdSELECT(apdu);
break;
case INS_VERIFY:
// it is a VERIFY instruction
cmdVERIFY(apdu);
break;
case INS_PUTDATA:
// it is a PUT DATA instruction
cmdPUTDATA(apdu);
break;
case INS_GETDATA:
// it is a GET DATA instruction
cmdGETDATA(apdu);
break;
default :
// the instruction in the command apdu is not supported
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
} // switch
} // if
else if (cmd_apdu[ISO7816.OFFSET_CLA] == PROP_CLASS) {
//----- it is the proprietary class
switch(cmd_apdu[ISO7816.OFFSET_INS]) {
case INS_AUTHSC:
// it is a AUTH SC instruction
cmdAUTHSC(apdu);
break;
default :
// the instruction in the command apdu is not supported
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
} // switch
} // else if
else {
// the class in the command apdu is not supported
ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
} // else
} // process
//---------------------------------------------------------------------------------------
//----- program code for the APDU command SELECT FILE
private void cmdSELECT(APDU apdu) {
byte[] cmd_apdu = apdu.getBuffer();
//----- check preconditions in the APDU header
// check if P1='04'
if (cmd_apdu[ISO7816.OFFSET_P1] != 0x04) {
ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
} // if
// check if P2='00'
if (cmd_apdu[ISO7816.OFFSET_P2] != 0x00) {
ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
} // if
short lc = (short)(cmd_apdu[ISO7816.OFFSET_LC] & 0x00FF); // get Lc (command length)
receiveAPDUBody(apdu); // get the command body
//----- command functionality
if (JCSystem.getAID().equals(cmd_apdu, ISO7816.OFFSET_CDATA, (byte) lc) == false) {
ISOException.throwIt(ISO7816.SW_APPLET_SELECT_FAILED);
} // if
//----- prepare response APDU
ISOException.throwIt(ISO7816.SW_NO_ERROR); // command proper executed
} // cmdSELECT
//---------------------------------------------------------------------------------------
//----- program code for the APDU command VERIFY
private void cmdVERIFY(APDU apdu) {
byte[] cmd_apdu = apdu.getBuffer();
//----- check preconditions in the APDU header
// check if P1='00'
if (cmd_apdu[ISO7816.OFFSET_P1] != 0) {
ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
} // if
short lc = (short)(cmd_apdu[ISO7816.OFFSET_LC] & 0x00FF); // get Lc (command length)
receiveAPDUBody(apdu); // get the command body
if (cmd_apdu[ISO7816.OFFSET_P2] == USERPIN_ID) { // it is the user PIN
if (lc != USERPIN_SIZE) { // Lc = length of PIN ?
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
} //if
if (userpin.check(cmd_apdu, ISO7816.OFFSET_CDATA, USERPIN_SIZE) == false) {
// PIN verification not successful
short tries = userpin.getTriesRemaining();
ISOException.throwIt( (short) (SW_PIN_FAILED + tries)); // send error counter in APDU back
} // if
} // if
else if (cmd_apdu[ISO7816.OFFSET_P2] == ADMINPIN_ID) { // it is the administrative PIN
if (lc != ADMINPIN_SIZE) { // Lc = length of PIN ?
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
} //if
if (adminpin.check(cmd_apdu, ISO7816.OFFSET_CDATA, ADMINPIN_SIZE) == false) {
// PIN verification not successful
short tries = adminpin.getTriesRemaining();
ISOException.throwIt( (short) (SW_PIN_FAILED + tries)); // send error counter in APDU back
} // if
} // if
else { // it is not a valid PIN identifier
ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
} // else
// PIN verification successful
ISOException.throwIt(ISO7816.SW_NO_ERROR); // command proper executed
} // cmdVERIFY
//---------------------------------------------------------------------------------------
//----- program code for the APDU command PUT DATA
private void cmdPUTDATA(APDU apdu) {
byte[] cmd_apdu = apdu.getBuffer();
//----- check preconditions in the APDU header
// check if P1=0
if (cmd_apdu[ISO7816.OFFSET_P1] != 0) {
ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
} // if
// check if P2 contents an allowed tag value
short tag = (short) (cmd_apdu[ISO7816.OFFSET_P2] & (short) 0x00FF);
if ((tag < (short) 0x0040) || (tag > (short) 0x00FE)) {
ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
} // if
short lc = (short)(cmd_apdu[ISO7816.OFFSET_LC] & (short) 0x00FF); // calculate Lc (command length)
receiveAPDUBody(apdu); // get the command body
//----- check precoditions of security status
if (adminpin.isValidated() == false) {
ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
} // if
//----- command functionality
switch(tag) {
case TAG_USERPIN:
// the user PIN must be changed
if (lc != USERPIN_SIZE) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
Util.arrayCopy(cmd_apdu, (short)((ISO7816.OFFSET_CDATA) & 0x00FF), workarray, (short) 0, lc);
userpin.update(workarray, (short) 0, USERPIN_SIZE);
break;
case TAG_ADMINPIN:
// the admin PIN must be changed
if (lc != ADMINPIN_SIZE) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
Util.arrayCopy(cmd_apdu, (short)((ISO7816.OFFSET_CDATA) & 0x00FF), workarray, (short) 0, lc);
adminpin.update(workarray, (short) 0, ADMINPIN_SIZE);
break;
case TAG_AUTHKEY:
// the authentication key must be changed
if (lc != SIZE_AUTHKEY) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
Util.arrayCopy(cmd_apdu, (short)((ISO7816.OFFSET_CDATA) & 0x00FF), workarray, (short) 0, lc);
authkey.setKey(workarray, (short)0);
break;
case TAG_AUTHCNTR:
// the authentication counter must be changed
if (lc != SIZE_AUTHCNTR) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
Util.arrayCopy(cmd_apdu, (short)((ISO7816.OFFSET_CDATA) & 0x00FF), workarray, (short) 0, lc);
if (Util.getShort(workarray, (short)0) > 0x7FFF) ISOException.throwIt(ISO7816.SW_WRONG_DATA);
authcntr = Util.getShort(workarray, (short)0);
break;
case TAG_SCID:
// the smart card ID must be changed
if (lc != SIZE_SCID) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
Util.arrayCopy(cmd_apdu, (short)((ISO7816.OFFSET_CDATA) & 0x00FF), scid, (short) 0, lc);
break;
default :
// the tag in the command apdu is not supported
ISOException.throwIt(SW_DATA_NOT_FOUND);
} // switch
//----- prepare response APDU
ISOException.throwIt(ISO7816.SW_NO_ERROR); // command proper executed
} // cmdPUTDATA
//---------------------------------------------------------------------------------------
//----- program code for the APDU command GET DATA
private void cmdGETDATA(APDU apdu) {
byte[] cmd_apdu = apdu.getBuffer();
//----- check preconditions in the APDU header
// check if P1=0
if (cmd_apdu[ISO7816.OFFSET_P1] != 0) {
ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
} // if
// check if P2 contents an allowed tag value
short tag = (short) (cmd_apdu[ISO7816.OFFSET_P2] & (short) 0x00FF);
if ((tag < 0x40) || (tag > 0xFE)) {
ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
} // if
short le = (short)(cmd_apdu[ISO7816.OFFSET_LC] & 0x00FF); // calculate Le (expected length)
//----- command functionality
switch(tag) { // a switch construct is used because of the easy extensibility for additional tags
case TAG_SCID:
// get the smart card ID
if (le != SIZE_SCID) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
Util.arrayCopy(scid, (short) (0), workarray, (short) 0, le);
break;
default :
// the tag in the command apdu could not found
ISOException.throwIt(SW_DATA_NOT_FOUND);
} // switch
//----- now all preparations are done, the data object can be send to the IFD -----
apdu.setOutgoing(); // set transmission to outgoing data
apdu.setOutgoingLength((short)le); // set the number of bytes to send to the IFD
apdu.sendBytesLong(workarray, (short) 0, (short)le); // send the requested the number of bytes to the IFD, data field of the DO
} // cmdGETDATA
//---------------------------------------------------------------------------------------
//----- program code for the APDU command AUTH SC
private void cmdAUTHSC(APDU apdu) {
byte[] cmd_apdu = apdu.getBuffer();
//----- check preconditions in the APDU header
// check if P1=0
if (cmd_apdu[ISO7816.OFFSET_P1] != 0)
ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
// check if P2=0
if (cmd_apdu[ISO7816.OFFSET_P2] != 0)
ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
short lc = (short)(cmd_apdu[ISO7816.OFFSET_LC] & 0x00FF); // get Lc (command length)
if (lc != LEN_CAPDU_AUTHSC)
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
receiveAPDUBody(apdu); // get the command body
//----- check security preconditions
if (authcntr < 0)
ISOException.throwIt(SW_PIN_FAILED); // Test!
//----- collect all data for the signature
Util.arrayCopy(cmd_apdu, (short) ISO7816.OFFSET_CDATA, workarray, INDEX_RND, lc); // RND from IFD
Util.arrayCopy(scid, (short) (0), workarray, INDEX_SCID, SIZE_SCID); // smart card ID
Util.setShort(workarray, INDEX_AuthCntr, authcntr); // authentication counter
workarray[INDEX_APINTries] = adminpin.getTriesRemaining();
workarray[INDEX_UPINTries] = userpin.getTriesRemaining();
if (adminpin.isValidated() == true) {
workarray[INDEX_APINValid] = (byte) (workarray[INDEX_APINValid] | (byte) 0x01); // Test!
} // if
if (userpin.isValidated() == true) {
workarray[INDEX_UPINValid] = (byte) (workarray[INDEX_UPINValid] | (byte) 0x02); // Test!
} // if
mac.init(authkey, Signature.MODE_SIGN);
short len = mac.sign(workarray, (short) 0, LEN_RAPDU_AUTHSC, workarray, INDEX_MAC); // calculate the MAC
authcntr = (short) (authcntr - (short) 1); // update number of allowed authentication
//----- now all calculations are done, the data can be send to the IFD
apdu.setOutgoing(); // set transmission to outgoing data
apdu.setOutgoingLength(LEN_RAPDU_AUTHSC); // set the number of bytes to send to the IFD
apdu.sendBytesLong(workarray, (short) 0, LEN_RAPDU_AUTHSC); // send the requested the number of bytes to the IFD, data field of the DO
} // cmdAUTHSC
//---------------------------------------------------------------------------------------
//----- receive the body of the command APDU
public void receiveAPDUBody(APDU apdu) {
byte[] buffer = apdu.getBuffer();
short lc = (short)(buffer[ISO7816.OFFSET_LC] & 0x00FF); // calculate Lc (command length)
// check if Lc != number of received bytes of the command APDU body
if (lc != apdu.setIncomingAndReceive()) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
} // if
} // receiveAPDUBody
} // class
|