This repository has been archived by the owner on Nov 11, 2017. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 11
/
CardEdge.java
2147 lines (2010 loc) · 77.6 KB
/
CardEdge.java
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
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* Copyright (c) 2001
* BSD ?
*/
//
// $Workfile: CardEdge.java $
// $Revision$
// $Date$
// $Author$
// $Archive: CardEdge $
// $Modtime: 5/02/00 8:48p $
//
package com.musclecard.CardEdge;
import javacard.framework.APDU;
import javacard.framework.ISO7816;
import javacard.framework.ISOException;
import javacard.framework.JCSystem;
import javacard.framework.OwnerPIN;
import javacard.framework.SystemException;
import javacard.framework.Util;
import javacard.security.DESKey;
import javacard.security.DSAKey;
import javacard.security.DSAPrivateKey;
import javacard.security.DSAPublicKey;
import javacard.security.Key;
import javacard.security.KeyBuilder;
import javacard.security.KeyPair;
import javacard.security.PrivateKey;
import javacard.security.RSAPrivateCrtKey;
import javacard.security.RSAPrivateKey;
import javacard.security.RSAPublicKey;
import javacard.security.RandomData;
import javacard.security.Signature;
import javacardx.apdu.ExtendedLength;
import javacardx.crypto.Cipher;
/**
* Implements MUSCLE's Card Edge Specification.
* <p>
*
* TODO:
* <ul>
* <li>Allows maximum number of keys and PINs and total mem to be specified at
* the instantiation moment.
* <p>
* <li>How do transactions fit in the methods ?
* <li>Where should we issue begin/end transaction ?
* <li>Should we ever abort transaction ? Where ?
* <li>Every time there is an "if (avail < )" check, call ThrowDeleteObjects().
* </ul>
*/
public class CardEdge extends javacard.framework.Applet implements ExtendedLength {
/* constants declaration */
// Maximum number of keys handled by the Cardlet
private final static byte MAX_NUM_KEYS = (byte) 8;
// Maximum number of PIN codes
private final static byte MAX_NUM_PINS = (byte) 8;
// Maximum number of keys allowed for ExtAuth
private final static byte MAX_NUM_AUTH_KEYS = (byte) 6;
// Maximum size for the extended APDU buffer for a 2048 bit key:
// CLA [1 byte] + INS [1 byte] + P1 [1 byte] + P2 [1 byte] +
// LC [3 bytes] + cipher_mode[1 byte] + cipher_direction [1 byte] +
// data_location [1 byte] + data_size [2 bytes] + data [256 bytes]
// = 268 bytes
private final static short EXT_APDU_BUFFER_SIZE = (short) 268;
// Minimum PIN size
private final static byte PIN_MIN_SIZE = (byte) 4;
// Maximum PIN size
private final static byte PIN_MAX_SIZE = (byte) 16;
// Maximum external authentication tries per key
private final static byte MAX_KEY_TRIES = (byte) 5;
// Import/Export Object ID
private final static short IN_OBJECT_CLA = (short) 0xFFFF;
private final static short IN_OBJECT_ID = (short) 0xFFFE;
private final static short OUT_OBJECT_CLA = (short) 0xFFFF;
private final static short OUT_OBJECT_ID = (short) 0xFFFF;
private final static byte KEY_ACL_SIZE = (byte) 6;
// Standard public ACL
private static byte[] STD_PUBLIC_ACL;/*
* = { 0x0000, // Read always allowed
* 0x0000, // Write always allowed
* 0x0000 // Delete always allowed };
*/
private static byte[] acl; // Temporary ACL
// code of CLA byte in the command APDU header
private final static byte CardEdge_CLA = (byte) 0xB0;
/****************************************
* Instruction codes *
****************************************/
// Applet initialization
private final static byte INS_SETUP = (byte) 0x2A;
// Keys' use and management
private final static byte INS_GEN_KEYPAIR = (byte) 0x30;
private final static byte INS_IMPORT_KEY = (byte) 0x32;
private final static byte INS_EXPORT_KEY = (byte) 0x34;
private final static byte INS_COMPUTE_CRYPT = (byte) 0x36;
// External authentication
private final static byte INS_CREATE_PIN = (byte) 0x40;
private final static byte INS_VERIFY_PIN = (byte) 0x42;
private final static byte INS_CHANGE_PIN = (byte) 0x44;
private final static byte INS_UNBLOCK_PIN = (byte) 0x46;
private final static byte INS_LOGOUT_ALL = (byte) 0x60;
private final static byte INS_GET_CHALLENGE = (byte) 0x62;
private final static byte INS_EXT_AUTH = (byte) 0x38;
// Objects' use and management
private final static byte INS_CREATE_OBJ = (byte) 0x5A;
private final static byte INS_DELETE_OBJ = (byte) 0x52;
private final static byte INS_READ_OBJ = (byte) 0x56;
private final static byte INS_WRITE_OBJ = (byte) 0x54;
// Status information
private final static byte INS_LIST_OBJECTS = (byte) 0x58;
private final static byte INS_LIST_PINS = (byte) 0x48;
private final static byte INS_LIST_KEYS = (byte) 0x3A;
private final static byte INS_GET_STATUS = (byte) 0x3C;
/** There have been memory problems on the card */
private final static short SW_NO_MEMORY_LEFT = ObjectManager.SW_NO_MEMORY_LEFT;
/** Entered PIN is not correct */
private final static short SW_AUTH_FAILED = (short) 0x9C02;
/** Required operation is not allowed in actual circumstances */
private final static short SW_OPERATION_NOT_ALLOWED = (short) 0x9C03;
/** Required feature is not (yet) supported */
private final static short SW_UNSUPPORTED_FEATURE = (short) 0x9C05;
/** Required operation was not authorized because of a lack of privileges */
private final static short SW_UNAUTHORIZED = (short) 0x9C06;
/** Required object is missing */
private final static short SW_OBJECT_NOT_FOUND = (short) 0x9C07;
/** New object ID already in use */
private final static short SW_OBJECT_EXISTS = (short) 0x9C08;
/** Algorithm specified is not correct */
private final static short SW_INCORRECT_ALG = (short) 0x9C09;
/** Incorrect P1 parameter */
private final static short SW_INCORRECT_P1 = (short) 0x9C10;
/** Incorrect P2 parameter */
private final static short SW_INCORRECT_P2 = (short) 0x9C11;
/** No more data available */
private final static short SW_SEQUENCE_END = (short) 0x9C12;
/** Invalid input parameter to command */
private final static short SW_INVALID_PARAMETER = (short) 0x9C0F;
/** Verify operation detected an invalid signature */
private final static short SW_SIGNATURE_INVALID = (short) 0x9C0B;
/** Operation has been blocked for security reason */
private final static short SW_IDENTITY_BLOCKED = (short) 0x9C0C;
/** Unspecified error */
private final static short SW_UNSPECIFIED_ERROR = (short) 0x9C0D;
/** For debugging purposes */
private final static short SW_INTERNAL_ERROR = (short) 0x9CFF;
// Algorithm Type in APDUs
private final static byte ALG_RSA = (byte) 0x00;
private final static byte ALG_RSA_CRT = (byte) 0x01;
private final static byte ALG_DSA = (byte) 0x02;
private final static byte ALG_DES = (byte) 0x03;
private final static byte ALG_3DES = (byte) 0x04;
private final static byte ALG_3DES3 = (byte) 0x05;
// Key Type in Key Blobs
private final static byte KEY_RSA_PUBLIC = (byte) 0x01;
private final static byte KEY_RSA_PRIVATE = (byte) 0x02;
private final static byte KEY_RSA_PRIVATE_CRT = (byte) 0x03;
private final static byte KEY_DSA_PUBLIC = (byte) 0x04;
private final static byte KEY_DSA_PRIVATE = (byte) 0x05;
private final static byte KEY_DES = (byte) 0x06;
private final static byte KEY_3DES = (byte) 0x07;
private final static byte KEY_3DES3 = (byte) 0x08;
// KeyBlob Encoding in Key Blobs
private final static byte BLOB_ENC_PLAIN = (byte) 0x00;
// Cipher Operations admitted in ComputeCrypt()
private final static byte OP_INIT = (byte) 0x01;
private final static byte OP_PROCESS = (byte) 0x02;
private final static byte OP_FINALIZE = (byte) 0x03;
// Cipher Directions admitted in ComputeCrypt()
private final static byte CD_SIGN = (byte) 0x01;
private final static byte CD_VERIFY = (byte) 0x02;
private final static byte CD_ENCRYPT = (byte) 0x03;
private final static byte CD_DECRYPT = (byte) 0x04;
// Cipher Modes admitted in ComputeCrypt()
private final static byte CM_RSA_NOPAD = (byte) 0x00;
private final static byte CM_RSA_PAD_PKCS1 = (byte) 0x01;
private final static byte CM_DSA_SHA = (byte) 0x10;
private final static byte CM_DES_CBC_NOPAD = (byte) 0x20;
private final static byte CM_DES_ECB_NOPAD = (byte) 0x21;
private final static byte DL_APDU = (byte) 0x01;
private final static byte DL_OBJECT = (byte) 0x02;
private final static byte LIST_OPT_RESET = (byte) 0x00;
private final static byte LIST_OPT_NEXT = (byte) 0x01;
private final static byte OPT_DEFAULT = (byte) 0x00; // Use JC defaults
private final static byte OPT_RSA_PUB_EXP = (byte) 0x01; // RSA: provide public exponent
private final static byte OPT_DSA_GPQ = (byte) 0x02; // DSA: provide p,q,g public key parameters
// Offsets in buffer[] for key generation
private final static short OFFSET_GENKEY_ALG = (short) (ISO7816.OFFSET_CDATA);
private final static short OFFSET_GENKEY_SIZE = (short) (ISO7816.OFFSET_CDATA + 1);
private final static short OFFSET_GENKEY_PRV_ACL = (short) (ISO7816.OFFSET_CDATA + 3);
private final static short OFFSET_GENKEY_PUB_ACL = (short) (OFFSET_GENKEY_PRV_ACL + KEY_ACL_SIZE);
private final static short OFFSET_GENKEY_OPTIONS = (short) (OFFSET_GENKEY_PUB_ACL + KEY_ACL_SIZE);
private final static short OFFSET_GENKEY_RSA_PUB_EXP_LENGTH = (short) (OFFSET_GENKEY_OPTIONS + 1);
private final static short OFFSET_GENKEY_RSA_PUB_EXP_VALUE = (short) (OFFSET_GENKEY_RSA_PUB_EXP_LENGTH + 2);
private final static short OFFSET_GENKEY_DSA_GPQ = (short) (OFFSET_GENKEY_OPTIONS + 1);
/****************************************
* Instance variables declaration *
****************************************/
// Memory Manager
private MemoryManager mem;
// Object Manager
private ObjectManager om;
// Key objects (allocated on demand)
private Key[] keys;
// Key ACLs
private byte[] keyACLs;
// Key Tries Left
private byte[] keyTries;
// Key iterator for ListKeys: it's an offset in the keys[] array.
private byte key_it;
// True if a GetChallenge() has been issued
private boolean getChallengeDone;
/*
* KeyPair, Cipher and Signature objects * These are allocated on demand *
* TODO: Here we could have just 1 Object[] and * make proper casts when
* needed
*/
private Cipher[] ciphers;
private Signature[] signatures;
// Says if we are using a signature or a cipher object
private byte[] ciph_dirs;
private KeyPair[] keyPairs;
private RandomData randomData; // RandomData class instance
// PIN and PUK objects, allocated on demand
private OwnerPIN[] pins, ublk_pins;
// Buffer for storing extended APDUs
private byte[] recvBuffer;
/*
* Logged identities: this is used for faster access control, so we don't
* have to ping each PIN object
*/
private short logged_ids;
/* For the setup function - should only be called once */
private boolean setupDone = false;
private byte create_object_ACL;
private byte create_key_ACL;
private byte create_pin_ACL;
/****************************************
* Methods *
****************************************/
private CardEdge(byte[] bArray, short bOffset, byte bLength) {
// FIXME: something should be done already here, not only with setup APDU
}
public static void install(byte[] bArray, short bOffset, byte bLength) {
CardEdge wal = new CardEdge(bArray, bOffset, bLength);
/* Register the Applet (copied code) */
if (bArray[bOffset] == 0)
wal.register();
else
wal.register(bArray, (short) (bOffset + 1), (byte) (bArray[bOffset]));
}
public boolean select() {
/*
* Application has been selected: Do session cleanup operation
*/
// Destroy the IO objects (if they exist)
if (setupDone) {
om.destroyObject(IN_OBJECT_CLA, IN_OBJECT_ID, true);
om.destroyObject(OUT_OBJECT_CLA, OUT_OBJECT_ID, true);
}
LogOutAll();
return true;
}
public void deselect() {
// Destroy the IO objects (if they exist)
if (setupDone) {
om.destroyObject(IN_OBJECT_CLA, IN_OBJECT_ID, true);
om.destroyObject(OUT_OBJECT_CLA, OUT_OBJECT_ID, true);
}
LogOutAll();
}
public void process(APDU apdu) {
// APDU object carries a byte array (buffer) to
// transfer incoming and outgoing APDU header
// and data bytes between card and CAD
// At this point, only the first header bytes
// [CLA, INS, P1, P2, P3] are available in
// the APDU buffer.
// The interface javacard.framework.ISO7816
// declares constants to denote the offset of
// these bytes in the APDU buffer
if (selectingApplet())
ISOException.throwIt(ISO7816.SW_NO_ERROR);
byte[] buffer = apdu.getBuffer();
// check SELECT APDU command
if ((buffer[ISO7816.OFFSET_CLA] == 0) && (buffer[ISO7816.OFFSET_INS] == (byte) 0xA4))
return;
// verify the rest of commands have the
// correct CLA byte, which specifies the
// command structure
if (buffer[ISO7816.OFFSET_CLA] != CardEdge_CLA)
ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
byte ins = buffer[ISO7816.OFFSET_INS];
if (!setupDone && (ins != (byte) INS_SETUP))
ISOException.throwIt(SW_UNSUPPORTED_FEATURE);
if (setupDone && (ins == (byte) INS_SETUP))
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
switch (ins) {
case INS_SETUP:
setup(apdu, buffer);
break;
case INS_GEN_KEYPAIR:
GenerateKeyPair(apdu, buffer);
break;
case INS_IMPORT_KEY:
ImportKey(apdu, buffer);
break;
case INS_EXPORT_KEY:
ExportKey(apdu, buffer);
break;
case INS_COMPUTE_CRYPT:
ComputeCrypt(apdu, buffer);
break;
case INS_VERIFY_PIN:
VerifyPIN(apdu, buffer);
break;
case INS_CREATE_PIN:
CreatePIN(apdu, buffer);
break;
case INS_CHANGE_PIN:
ChangePIN(apdu, buffer);
break;
case INS_UNBLOCK_PIN:
UnblockPIN(apdu, buffer);
break;
case INS_LOGOUT_ALL:
LogOutAll();
break;
case INS_GET_CHALLENGE:
GetChallenge(apdu, buffer);
break;
case INS_EXT_AUTH:
ExternalAuthenticate(apdu, buffer);
break;
case INS_CREATE_OBJ:
CreateObject(apdu, buffer);
break;
case INS_DELETE_OBJ:
DeleteObject(apdu, buffer);
break;
case INS_READ_OBJ:
ReadObject(apdu, buffer);
break;
case INS_WRITE_OBJ:
WriteObject(apdu, buffer);
break;
case INS_LIST_PINS:
ListPINs(apdu, buffer);
break;
case INS_LIST_OBJECTS:
ListObjects(apdu, buffer);
break;
case INS_LIST_KEYS:
ListKeys(apdu, buffer);
break;
case INS_GET_STATUS:
GetStatus(apdu, buffer);
break;
default:
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
}
;
} // end of process method
/** Setup APDU - initialize the applet
*
* Incoming data:
* PIN0 len + PIN0 + PUK0 len + PUK0 +
*/
private void setup(APDU apdu, byte[] buffer) {
short bytesLeft = Util.makeShort((byte) 0x00, buffer[ISO7816.OFFSET_LC]);
if (bytesLeft != apdu.setIncomingAndReceive())
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
short base = (short) (ISO7816.OFFSET_CDATA);
byte numBytes = buffer[base++];
OwnerPIN pin = pins[0];
if (!CheckPINPolicy(buffer, base, numBytes))
ISOException.throwIt(SW_INVALID_PARAMETER);
if (pin.getTriesRemaining() == (byte) 0x00)
ISOException.throwIt(SW_IDENTITY_BLOCKED);
if (!pin.check(buffer, base, numBytes))
ISOException.throwIt(SW_AUTH_FAILED);
base += numBytes;
byte pin_tries = buffer[base++];
byte ublk_tries = buffer[base++];
numBytes = buffer[base++];
if (!CheckPINPolicy(buffer, base, numBytes))
ISOException.throwIt(SW_INVALID_PARAMETER);
pins[0] = new OwnerPIN(pin_tries, PIN_MAX_SIZE);
pins[0].update(buffer, base, numBytes);
base += numBytes;
numBytes = buffer[base++];
if (!CheckPINPolicy(buffer, base, numBytes))
ISOException.throwIt(SW_INVALID_PARAMETER);
ublk_pins[0] = new OwnerPIN(ublk_tries, PIN_MAX_SIZE);
ublk_pins[0].update(buffer, base, numBytes);
base += numBytes;
pin_tries = buffer[base++];
ublk_tries = buffer[base++];
numBytes = buffer[base++];
if (!CheckPINPolicy(buffer, base, numBytes))
ISOException.throwIt(SW_INVALID_PARAMETER);
pins[1] = new OwnerPIN(pin_tries, PIN_MAX_SIZE);
pins[1].update(buffer, base, numBytes);
base += numBytes;
numBytes = buffer[base++];
if (!CheckPINPolicy(buffer, base, numBytes))
ISOException.throwIt(SW_INVALID_PARAMETER);
ublk_pins[1] = new OwnerPIN(ublk_tries, PIN_MAX_SIZE);
ublk_pins[1].update(buffer, base, numBytes);
base += numBytes;
base += (short) 2;
short mem_size = Util.getShort(buffer, base);
base += (short) 2;
create_object_ACL = buffer[base++];
create_key_ACL = buffer[base++];
create_pin_ACL = buffer[base++];
mem = new MemoryManager((short) mem_size);
om = new ObjectManager(mem);
keys = new Key[MAX_NUM_KEYS];
keyACLs = new byte[(short) (MAX_NUM_KEYS * KEY_ACL_SIZE)];
keyTries = new byte[MAX_NUM_KEYS];
for (byte i = (byte) 0; i < (byte) MAX_NUM_KEYS; i++)
keyTries[i] = MAX_KEY_TRIES;
keyPairs = new KeyPair[MAX_NUM_KEYS];
ciphers = new Cipher[MAX_NUM_KEYS];
signatures = new Signature[MAX_NUM_KEYS];
ciph_dirs = new byte[MAX_NUM_KEYS];
for (byte i = (byte) 0; i < (byte) MAX_NUM_KEYS; i++)
ciph_dirs[i] = (byte) 0xFF;
logged_ids = 0x00; // No identities logged in
getChallengeDone = false; // No GetChallenge() issued so far
randomData = null; // Will be created on demand when needed
STD_PUBLIC_ACL = new byte[KEY_ACL_SIZE];
for (byte i = (byte) 0; i < (byte) KEY_ACL_SIZE; i += (short) 2)
Util.setShort(STD_PUBLIC_ACL, i, (short) 0x0000);
// Initialize the extended APDU buffer
try {
// Try to allocate the extended APDU buffer on RAM memory
recvBuffer = JCSystem.makeTransientByteArray((short) EXT_APDU_BUFFER_SIZE, JCSystem.CLEAR_ON_DESELECT);
} catch (SystemException e) {
// Allocate the extended APDU buffer on EEPROM memory
// This is the fallback method, but its usage is really not
// recommended
// as after ~ 100000 writes it will kill the EEPROM cells...
recvBuffer = new byte[EXT_APDU_BUFFER_SIZE];
}
setupDone = true;
}
/********** UTILITY FUNCTIONS **********/
/*
* SendData() wraps the setOutgoing(), setLength(), .. stuff * that could be
* necessary to be fully JavaCard compliant.
*/
private void sendData(APDU apdu, byte[] data, short offset, short size) {
if (size > EXT_APDU_BUFFER_SIZE)
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
apdu.setOutgoing();
apdu.setOutgoingLength(size);
apdu.sendBytesLong(data, offset, size);
}
/* Retrieves the full contents from the apdu object in case of */
/* an extended APDU. */
private void getData(APDU apdu, byte[] src, short bytesRead, byte[] dst) {
short recvLen = 0;
short apduOffset = bytesRead;
Util.arrayCopyNonAtomic(src, (short) 0, dst, (short) 0, apduOffset);
do {
recvLen = apdu.receiveBytes((short) 0);
Util.arrayCopyNonAtomic(src, (short) 0, dst, apduOffset, recvLen);
apduOffset += recvLen;
} while (recvLen > 0);
}
/*
* Retrieves the Cipher object to be used w/ the specified key * and
* algorithm id (Cipher.ALG_XX). * If exists, check it has the proper
* algorithm and throws * SW_OP_NOT_ALLOWED if not * If not, creates it
*/
private Cipher getCipher(byte key_nb, byte alg_id) {
if (ciphers[key_nb] == null) {
ciphers[key_nb] = Cipher.getInstance(alg_id, false);
} else if (ciphers[key_nb].getAlgorithm() != alg_id)
ISOException.throwIt(SW_OPERATION_NOT_ALLOWED);
return ciphers[key_nb];
}
/*
* Retrieves the Signature object to be used w/ the specified key * and
* algorithm id (Signature.ALG_XX). * If exists, check it has the proper
* algorithm and throws * SW_OPERATION_NOT_ALLOWED if not * If does not
* exist, creates it
*/
private Signature getSignature(byte key_nb, byte alg_id) {
if (signatures[key_nb] == null) {
signatures[key_nb] = Signature.getInstance(alg_id, false);
} else if (signatures[key_nb].getAlgorithm() != alg_id)
ISOException.throwIt(SW_OPERATION_NOT_ALLOWED);
return signatures[key_nb];
}
/**
* Retrieves the Key object to be used w/ the specified key number, key type
* (KEY_XX) and size. If exists, check it has the proper key type If not,
* creates it.
*
* @return Retrieved Key object or throws SW_UNATUTHORIZED,
* SW_OPERATION_NOT_ALLOWED
*/
private Key getKey(byte key_nb, byte key_type, short key_size) {
byte jc_key_type = keyType2JCType(key_type);
if (keys[key_nb] == null) {
// We have to create the Key
/* Check that Identity n.0 is logged */
if ((create_key_ACL == (byte) 0xFF)
|| (((logged_ids & create_key_ACL) == (short) 0x0000) && (create_key_ACL != (byte) 0x00)))
ISOException.throwIt(SW_UNAUTHORIZED);
keys[key_nb] = KeyBuilder.buildKey(jc_key_type, key_size, false);
} else {
// Key already exists: check size & type
/*
* TODO: As an option, we could just discard and recreate if not of
* the correct type, but creates trash objects
*/
if ((keys[key_nb].getSize() != key_size) || (keys[key_nb].getType() != jc_key_type))
ISOException.throwIt(SW_OPERATION_NOT_ALLOWED);
}
return keys[key_nb];
}
// Converts a Applet's Key Type to the JavaCard one.
private byte keyType2JCType(byte key_type) {
switch (key_type) {
case KEY_RSA_PUBLIC:
return KeyBuilder.TYPE_RSA_PUBLIC;
case KEY_RSA_PRIVATE:
return KeyBuilder.TYPE_RSA_PRIVATE;
case KEY_RSA_PRIVATE_CRT:
return KeyBuilder.TYPE_RSA_CRT_PRIVATE;
case KEY_DSA_PUBLIC:
return KeyBuilder.TYPE_DSA_PUBLIC;
case KEY_DSA_PRIVATE:
return KeyBuilder.TYPE_DSA_PUBLIC;
case KEY_DES:
return KeyBuilder.TYPE_DES;
case KEY_3DES:
case KEY_3DES3:
return KeyBuilder.TYPE_DES;
default:
ISOException.throwIt(SW_INVALID_PARAMETER);
}
return (byte) 0; // Avoid compiler warning
}
// Converts a JavaCard's Key Type to the Applet one.
private byte getKeyType(Key key) {
switch (key.getType()) {
case KeyBuilder.TYPE_RSA_PUBLIC:
return KEY_RSA_PUBLIC;
case KeyBuilder.TYPE_RSA_PRIVATE:
return KEY_RSA_PRIVATE;
case KeyBuilder.TYPE_RSA_CRT_PRIVATE:
return KEY_RSA_PRIVATE_CRT;
case KeyBuilder.TYPE_DSA_PUBLIC:
return KEY_DSA_PUBLIC;
case KeyBuilder.TYPE_DSA_PRIVATE:
return KEY_DSA_PRIVATE;
case KeyBuilder.TYPE_DES:
if (key.getSize() == (short) 64)
return KEY_DES;
if (key.getSize() == (short) 128)
return KEY_3DES;
if (key.getSize() == (short) 192)
return KEY_3DES3;
default:
ISOException.throwIt(SW_INTERNAL_ERROR);
}
return (byte) 0; // Avoid compiler warning
}
/** Check from ACL if a key can be read */
boolean authorizeKeyRead(byte key_nb) {
short acl_offset = (short) (key_nb * KEY_ACL_SIZE);
short required_ids = Util.getShort(keyACLs, acl_offset);
return ((required_ids != (short) 0xFFFF) && ((short) (required_ids & logged_ids) == required_ids));
}
/** Check from ACL if a key can be overwritten */
boolean authorizeKeyWrite(byte key_nb) {
short acl_offset = (short) (key_nb * KEY_ACL_SIZE + 2);
short required_ids = Util.getShort(keyACLs, acl_offset);
return ((required_ids != (short) 0xFFFF) && ((short) (required_ids & logged_ids) == required_ids));
}
/** Check from ACL if a key can be used */
boolean authorizeKeyUse(byte key_nb) {
short acl_offset = (short) (key_nb * KEY_ACL_SIZE + 4);
short required_ids = Util.getShort(keyACLs, acl_offset);
return ((required_ids != (short) 0xFFFF) && ((short) (required_ids & logged_ids) == required_ids));
}
/** Returns an ACL that requires current logged in identities. */
byte[] getCurrentACL() {
if (acl == null)
acl = new byte[KEY_ACL_SIZE];
byte i;
for (i = (byte) 0; i < KEY_ACL_SIZE; i += (byte) 2)
Util.setShort(acl, i, logged_ids);
return acl;
}
/** Returns an ACL that disables all operations for the application. */
byte[] getRestrictedACL() {
if (acl == null)
acl = new byte[KEY_ACL_SIZE];
byte i;
for (i = (byte) 0; i < KEY_ACL_SIZE; i += (byte) 2)
Util.setShort(acl, i, (short) 0xFFFF);
return acl;
}
/** Registers login of strong identity associated with a key number */
private void LoginStrongIdentity(byte key_nb) {
logged_ids |= (short) (((short) 0x01) << (key_nb + 8));
}
/**
* Registers logout of an identity. This must be called anycase when a PIN
* verification or external authentication fail
*/
private void LogoutIdentity(byte id_nb) {
logged_ids &= (short) ~(0x0001 << id_nb);
}
/** Deletes and zeros the IO objects and throws the passed in exception */
private void ThrowDeleteObjects(short exception) {
om.destroyObject(IN_OBJECT_CLA, IN_OBJECT_ID, true);
om.destroyObject(OUT_OBJECT_CLA, OUT_OBJECT_ID, true);
ISOException.throwIt(exception);
}
/** Checks if PIN policies are satisfied for a PIN code */
private boolean CheckPINPolicy(byte[] pin_buffer, short pin_offset, byte pin_size) {
if ((pin_size < PIN_MIN_SIZE) || (pin_size > PIN_MAX_SIZE))
return false;
return true;
}
/****************************************
* APDU handlers *
****************************************/
private void ComputeCrypt(APDU apdu, byte[] apduBuffer) {
/* Buffer pointer */
byte[] buffer = apduBuffer;
short bytesLeft = apdu.setIncomingAndReceive();
short LC = apdu.getIncomingLength();
short dataOffset = apdu.getOffsetCdata();
if ((short) (LC + dataOffset) > EXT_APDU_BUFFER_SIZE)
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
/* Is this an extended APDU? */
if (bytesLeft != LC) {
getData(apdu, apduBuffer, (short) (dataOffset + bytesLeft), recvBuffer);
buffer = recvBuffer;
bytesLeft = LC;
}
byte key_nb = buffer[ISO7816.OFFSET_P1];
if ((key_nb < 0) || (key_nb >= MAX_NUM_KEYS) || (keys[key_nb] == null))
ISOException.throwIt(SW_INCORRECT_P1);
/* Enforce Access Control */
if (!authorizeKeyUse(key_nb))
ISOException.throwIt(SW_UNAUTHORIZED);
byte op = buffer[ISO7816.OFFSET_P2];
Key key = keys[key_nb];
byte ciph_dir;
byte data_location;
byte[] src_buff;
short src_base;
short src_avail;
short size;
switch (op) {
case OP_INIT:
if (bytesLeft < 3)
ISOException.throwIt(SW_INVALID_PARAMETER);
byte ciph_mode = buffer[dataOffset];
ciph_dir = buffer[(short) (dataOffset + 1)];
byte ciph_alg_id;
data_location = buffer[(short) (dataOffset + 2)];
switch (data_location) {
case DL_APDU:
src_buff = buffer;
src_base = (short) (dataOffset + 3);
src_avail = (short) (bytesLeft - 3);
break;
case DL_OBJECT:
src_buff = mem.getBuffer();
src_base = om.getBaseAddress(IN_OBJECT_CLA, IN_OBJECT_ID);
if (src_base == MemoryManager.NULL_OFFSET)
ISOException.throwIt(SW_OBJECT_NOT_FOUND);
src_avail = om.getSizeFromAddress(src_base);
break;
default:
ISOException.throwIt(SW_INVALID_PARAMETER);
return; // Compiler warning
}
if (src_avail < 2)
ISOException.throwIt(SW_INVALID_PARAMETER);
size = Util.getShort(src_buff, src_base);
if (src_avail < (short) (2 + size))
ISOException.throwIt(SW_INVALID_PARAMETER);
switch (ciph_dir) {
case CD_SIGN:
case CD_VERIFY:
switch (key.getType()) {
case KeyBuilder.TYPE_RSA_PUBLIC:
case KeyBuilder.TYPE_RSA_PRIVATE:
ciph_alg_id = Signature.ALG_RSA_MD5_PKCS1;
ISOException.throwIt(SW_UNSUPPORTED_FEATURE);
break;
case KeyBuilder.TYPE_DSA_PUBLIC:
case KeyBuilder.TYPE_DSA_PRIVATE:
if (ciph_mode == CM_DSA_SHA)
ciph_alg_id = Signature.ALG_DSA_SHA;
else {
ISOException.throwIt(SW_INVALID_PARAMETER);
return; // Compiler warning (ciph_alg_id)
}
break;
default:
// DSA Encryption/Decryption is not supported by JavaCard !!
ISOException.throwIt(SW_INCORRECT_ALG);
return; // Compiler warning (ciph_alg_id)
}
Signature sign = getSignature(key_nb, ciph_alg_id);
if (size == (short) 0)
sign.init(key, (ciph_dir == CD_SIGN) ? Signature.MODE_SIGN : Signature.MODE_VERIFY);
else
sign.init(key, (ciph_dir == CD_SIGN) ? Signature.MODE_SIGN : Signature.MODE_VERIFY, src_buff,
(short) (src_base + 2), size);
ciph_dirs[key_nb] = ciph_dir;
break;
case CD_ENCRYPT:
case CD_DECRYPT:
switch (key.getType()) {
case KeyBuilder.TYPE_RSA_PUBLIC:
case KeyBuilder.TYPE_RSA_PRIVATE:
case KeyBuilder.TYPE_RSA_CRT_PRIVATE:
if (ciph_mode == CM_RSA_NOPAD)
ciph_alg_id = Cipher.ALG_RSA_NOPAD;
else if (ciph_mode == CM_RSA_PAD_PKCS1)
ciph_alg_id = Cipher.ALG_RSA_PKCS1;
else {
ISOException.throwIt(SW_INVALID_PARAMETER);
return;
}
break;
case KeyBuilder.TYPE_DES:
if (ciph_mode == CM_DES_CBC_NOPAD)
ciph_alg_id = Cipher.ALG_DES_CBC_NOPAD;
else if (ciph_mode == CM_DES_ECB_NOPAD)
ciph_alg_id = Cipher.ALG_DES_ECB_NOPAD;
else {
ISOException.throwIt(SW_INVALID_PARAMETER);
return;
}
break;
case KeyBuilder.TYPE_DSA_PUBLIC:
case KeyBuilder.TYPE_DSA_PRIVATE:
// DSA Encryption/Decryption is not supported by JavaCard !!
ISOException.throwIt(SW_INVALID_PARAMETER);
return;
default:
ISOException.throwIt(SW_INTERNAL_ERROR);
return; // Compiler warning (ciph_alg_id unset)
}
Cipher ciph = getCipher(key_nb, ciph_alg_id);
if (size == (short) 0)
ciph.init(key, (ciph_dir == CD_ENCRYPT) ? Cipher.MODE_ENCRYPT : Cipher.MODE_DECRYPT);
else
ciph.init(key, (ciph_dir == CD_ENCRYPT) ? Cipher.MODE_ENCRYPT : Cipher.MODE_DECRYPT, src_buff,
(short) (src_base + 2), size);
ciph_dirs[key_nb] = ciph_dir;
break;
default:
ISOException.throwIt(SW_INVALID_PARAMETER);
}
break;
case OP_PROCESS:
case OP_FINALIZE:
ciph_dir = ciph_dirs[key_nb];
switch (ciph_dir) {
case CD_SIGN:
case CD_VERIFY:
Signature sign = signatures[key_nb];
if (sign == null)
/*
* Don't know what is incorrect: just say incorrect
* parameters we guess it was specified a wrong key number
*/
ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
data_location = buffer[dataOffset];
switch (data_location) {
case DL_APDU:
src_buff = mem.getBuffer();
// Skip Data Location byte.
src_base = (short) (dataOffset + 1);
src_avail = (short) (bytesLeft - 1);
break;
case DL_OBJECT:
src_buff = mem.getBuffer();
src_base = om.getBaseAddress(IN_OBJECT_CLA, IN_OBJECT_ID);
if (src_base == MemoryManager.NULL_OFFSET)
ISOException.throwIt(SW_OBJECT_NOT_FOUND);
src_avail = om.getSizeFromAddress(src_base);
break;
default:
ISOException.throwIt(SW_INVALID_PARAMETER);
return;
}
if (src_avail < 2)
ISOException.throwIt(SW_INVALID_PARAMETER);
size = Util.getShort(src_buff, src_base);
// IO objects are allowed to be larger than size of contained
// data
if (src_avail < (short) (2 + size))
ISOException.throwIt(SW_INVALID_PARAMETER);
if (op == OP_PROCESS)
sign.update(src_buff, (short) (src_base + 2), size);
else {
// OP_FINALIZE
if (ciph_dir == CD_SIGN) {
om.destroyObject(OUT_OBJECT_CLA, OUT_OBJECT_ID, true);
short dst_base = om.createObject(OUT_OBJECT_CLA, OUT_OBJECT_ID, (short) (sign.getLength() + 2),
getCurrentACL(), (short) 0);
if (dst_base == MemoryManager.NULL_OFFSET)
ISOException.throwIt(SW_NO_MEMORY_LEFT);
short sign_size = sign.sign(src_buff, (short) (src_base + 2), size, mem.getBuffer(),
(short) (dst_base + 2));
if (sign_size > sign.getLength())
// We got a buffer overflow (unless we were in
// memory end and got an exception...)
ISOException.throwIt(SW_INTERNAL_ERROR);
mem.setShort(dst_base, sign_size);
// Actually send data back (and clear output buffer)
// only if location is APDU
if (data_location == DL_APDU) {
sendData(apdu, mem.getBuffer(), dst_base, (short) (sign_size + 2));
om.destroyObject(OUT_OBJECT_CLA, OUT_OBJECT_ID, true);
}
} else { // ciph_dir == CD_VERIFY
if (src_avail < (short) (2 + size + 2))
ISOException.throwIt(SW_INVALID_PARAMETER);
short sign_size = Util.getShort(src_buff, (short) (src_base + 2 + size));
if (src_avail < (short) (2 + size + 2 + sign_size))
ISOException.throwIt(SW_INVALID_PARAMETER);
if (sign_size != sign.getLength())
ISOException.throwIt(SW_INVALID_PARAMETER);
if (!sign.verify(src_buff, (short) (src_base + 2), size, src_buff,
(short) (src_base + 2 + size + 2), sign_size))
ISOException.throwIt(SW_SIGNATURE_INVALID);
}
}
break;
case CD_ENCRYPT:
case CD_DECRYPT:
Cipher ciph = ciphers[key_nb];
if (ciph == null)
/*
* Don't know what is incorrect: just say incorrect
* parameters we guess it was specified a wrong key number
*/
ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
data_location = buffer[dataOffset];
switch (data_location) {
case DL_APDU:
src_buff = buffer;
src_base = (short) (dataOffset + 1);
src_avail = (short) (bytesLeft - 1);
break;
case DL_OBJECT:
src_buff = mem.getBuffer();
src_base = om.getBaseAddress(IN_OBJECT_CLA, IN_OBJECT_ID);
if (src_base == MemoryManager.NULL_OFFSET)
ISOException.throwIt(SW_OBJECT_NOT_FOUND);
src_avail = om.getSizeFromAddress(src_base);
break;
default:
ISOException.throwIt(SW_INVALID_PARAMETER);
return;
}
if (src_avail < 2)
ISOException.throwIt(SW_INVALID_PARAMETER);
size = Util.getShort(src_buff, src_base);
if (src_avail < (short) (2 + size))
ISOException.throwIt(SW_INVALID_PARAMETER);
// TODO: Don't destroy the out obj every time, but keep it
om.destroyObject(OUT_OBJECT_CLA, OUT_OBJECT_ID, true);
// Create object with 2 more bytes for DataChunk Size field
short dst_base = om.createObject(OUT_OBJECT_CLA, OUT_OBJECT_ID, (short) (size + 2), getCurrentACL(),
(short) 0);
if (dst_base == MemoryManager.NULL_OFFSET)
ISOException.throwIt(SW_NO_MEMORY_LEFT);