-
Notifications
You must be signed in to change notification settings - Fork 42
/
CardEdge.java
3245 lines (2875 loc) · 146 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
/*
* SatoChip Bitcoin Hardware Wallet based on javacard
* (c) 2015-2019 by Toporin - 16DMCk4WUaHofchAhpMaQS4UPm4urcy2dN
* Sources available on https://github.com/Toporin
* Changes include: -Bip32 support
* -simple Bitcoin transaction signatures
* -Bitcoin message signatures
*
*
* Based on the M.US.C.L.E framework:
* see http://pcsclite.alioth.debian.org/musclecard.com/musclecard/
* see https://github.com/martinpaljak/MuscleApplet/blob/d005f36209bdd7020bac0d783b228243126fd2f8/src/com/musclecard/CardEdge/CardEdge.java
*
* MUSCLE SmartCard Development
* Authors: Tommaso Cucinotta <cucinotta@sssup.it>
* David Corcoran <corcoran@linuxnet.com>
* Description: CardEdge implementation with JavaCard
* Protocol Authors: Tommaso Cucinotta <cucinotta@sssup.it>
* David Corcoran <corcoran@linuxnet.com>
*
* BEGIN LICENSE BLOCK
* Copyright (C) 1999-2002 David Corcoran <corcoran@linuxnet.com>
* Copyright (C) 2015-2019 Toporin
* All rights reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
* END LICENSE_BLOCK
*/
package org.satochip.applet;
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.AESKey;
import javacard.security.ECPrivateKey;
import javacard.security.ECPublicKey;
import javacard.security.CryptoException;
import javacard.security.Key;
import javacard.security.KeyAgreement;
import javacard.security.KeyBuilder;
//import javacard.security.KeyPair;
import javacard.security.Signature;
import javacard.security.MessageDigest;
import javacard.security.RandomData;
import javacardx.crypto.Cipher;
/**
* Implements MUSCLE's Card Edge Specification.
*/
public class CardEdge extends javacard.framework.Applet {
/* constants declaration */
/**
* VERSION HISTORY
* PROTOCOL VERSION: changes that impact compatibility with the client side
* APPLET VERSION: changes with no impact on compatibility of the client
*/
// 0.1-0.1: initial version
// 0.2-0.1: support for hmac-sha1 authorisation + improved sha256 + bip32 full support
// 0.3-0.1: change parseTransaction response for coherence: add separate short flags for second factor authentication
// 0.3-0.2: fast Sha512 computation
// 0.4-0.1: getBIP32ExtendedKey also returns chaincode
// 0.5-0.1: Support for Segwit transaction
// 0.5-0.2: bip32 cached memory optimisation: fixed array instead of list
// 0.6-0.1: bip32 optimisation: speed up computation during derivation of non-hardened child
// 0.6-0.2: get_status returns number of pin/puk tries remaining
// 0.6-0.3: Patch in function SignTransaction(): add optional 2FA flag in data
// 0.7-0.1: add CryptTransaction2FA() to encrypt/decrypt tx messages sent to 2FA device for privacy
// 0.7-0.2: 2FA patch to mitigate replay attack when importing a new seed
// 0.8-0.1: add APDUs to reset the seed/eckey/2FA. 2FA required to sign tx/msg and reset seed/eckey/2FA. 2FA can only be disabled when all privkeys are cleared.
// 0.9-0.1: Message signing for altcoin.
// 0.10-0.1: add method SignTransactionHash()
// 0.10-0.2: support for native sha512
// 0.10-0.3: ECkey recovery optimisation: support ALG_EC_SVDP_DH_PLAIN_XY
// 0.10-0.4: Cleanup: optimised code only, removed legacy sha512 implementation and slow pubkey recovery
// 0.11-0.1: support (mandatory) secure channel
// 0.12-0.1: Card label & Support for encrypted seed import from a SeedKeeper
// 0.12-0.2: 2FA can be disabled using reset2FAKey() without reseting the seed
// 0.12-0.3: SeedKeeper support: label & labelsize no longer required
// 0.12-0.4: add reset to factory support
// 0.12-0.5: add support for personalisation PKI
// 0.12-0.6: patch spurious select issue
private final static byte PROTOCOL_MAJOR_VERSION = (byte) 0;
private final static byte PROTOCOL_MINOR_VERSION = (byte) 12;
private final static byte APPLET_MAJOR_VERSION = (byte) 0;
private final static byte APPLET_MINOR_VERSION = (byte) 6;
// Maximum number of keys handled by the Cardlet
private final static byte MAX_NUM_KEYS = (byte) 16;
// Maximum number of PIN codes
private final static byte MAX_NUM_PINS = (byte) 8; // TODO: set to 2?
// Maximum size for the extended APDU buffer
private final static short EXT_APDU_BUFFER_SIZE = (short) 268;
private final static short TMP_BUFFER_SIZE = (short) 256;
// Minimum PIN size
private final static byte PIN_MIN_SIZE = (byte) 4;
// Maximum PIN size
private final static byte PIN_MAX_SIZE = (byte) 16;// TODO: increase size?
// PIN[0] initial value...
private final static byte[] PIN_INIT_VALUE={(byte)'M',(byte)'u',(byte)'s',(byte)'c',(byte)'l',(byte)'e',(byte)'0',(byte)'0'};
// 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_IMPORT_KEY = (byte) 0x32;
private final static byte INS_RESET_KEY = (byte) 0x33;
private final static byte INS_GET_PUBLIC_FROM_PRIVATE= (byte)0x35;
// External authentication
private final static byte INS_CREATE_PIN = (byte) 0x40; //TODO: remove?
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;
// Status information
private final static byte INS_LIST_PINS = (byte) 0x48;
private final static byte INS_GET_STATUS = (byte) 0x3C;
private final static byte INS_CARD_LABEL = (byte) 0x3D;
// HD wallet
private final static byte INS_BIP32_IMPORT_SEED= (byte) 0x6C;
private final static byte INS_BIP32_RESET_SEED= (byte) 0x77;
private final static byte INS_BIP32_GET_AUTHENTIKEY= (byte) 0x73;
private final static byte INS_BIP32_SET_AUTHENTIKEY_PUBKEY= (byte)0x75;
private final static byte INS_BIP32_GET_EXTENDED_KEY= (byte) 0x6D;
private final static byte INS_BIP32_SET_EXTENDED_PUBKEY= (byte) 0x74;
private final static byte INS_SIGN_MESSAGE= (byte) 0x6E;
private final static byte INS_SIGN_SHORT_MESSAGE= (byte) 0x72;
private final static byte INS_SIGN_TRANSACTION= (byte) 0x6F;
private final static byte INS_PARSE_TRANSACTION = (byte) 0x71;
private final static byte INS_CRYPT_TRANSACTION_2FA = (byte) 0x76;
private final static byte INS_SET_2FA_KEY = (byte) 0x79;
private final static byte INS_RESET_2FA_KEY = (byte) 0x78;
private final static byte INS_SIGN_TRANSACTION_HASH= (byte) 0x7A;
// secure channel
private final static byte INS_INIT_SECURE_CHANNEL = (byte) 0x81;
private final static byte INS_PROCESS_SECURE_CHANNEL = (byte) 0x82;
// secure import from SeedKeeper
//private final static byte INS_BIP32_IMPORT_ENCRYPTED_SEED = (byte) 0xAC; //deprecated
private final static byte INS_IMPORT_ENCRYPTED_SECRET = (byte) 0xAC;
private final static byte INS_IMPORT_TRUSTED_PUBKEY = (byte) 0xAA;
private final static byte INS_EXPORT_TRUSTED_PUBKEY = (byte) 0xAB;
private final static byte INS_EXPORT_AUTHENTIKEY= (byte) 0xAD;
// Personalization PKI support
private final static byte INS_IMPORT_PKI_CERTIFICATE = (byte) 0x92;
private final static byte INS_EXPORT_PKI_CERTIFICATE = (byte) 0x93;
private final static byte INS_SIGN_PKI_CSR = (byte) 0x94;
private final static byte INS_EXPORT_PKI_PUBKEY = (byte) 0x98;
private final static byte INS_LOCK_PKI = (byte) 0x99;
private final static byte INS_CHALLENGE_RESPONSE_PKI= (byte) 0x9A;
// reset to factory settings
private final static byte INS_RESET_TO_FACTORY = (byte) 0xFF;
/****************************************
* Error codes *
****************************************/
/** Entered PIN is not correct */
private final static short SW_PIN_FAILED = (short)0x63C0;// includes number of tries remaining
///** DEPRECATED - 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 setup is not not done */
private final static short SW_SETUP_NOT_DONE = (short) 0x9C04;
/** Required setup is already done */
private final static short SW_SETUP_ALREADY_DONE = (short) 0x9C07;
/** Required feature is not (yet) supported */
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;
/** Algorithm specified is not correct */
private final static short SW_INCORRECT_ALG = (short) 0x9C09;
/** There have been memory problems on the card */
private final static short SW_NO_MEMORY_LEFT = Bip32ObjectManager.SW_NO_MEMORY_LEFT;
///** DEPRECATED - Required object is missing */
//private final static short SW_OBJECT_NOT_FOUND= (short) 0x9C07;
/** Incorrect P1 parameter */
private final static short SW_INCORRECT_P1 = (short) 0x9C10;
/** Incorrect P2 parameter */
private final static short SW_INCORRECT_P2 = (short) 0x9C11;
/** Invalid input parameter to command */
private final static short SW_INVALID_PARAMETER = (short) 0x9C0F;
/** Eckeys initialized */
private final static short SW_ECKEYS_INITIALIZED_KEY = (short) 0x9C1A;
/** 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;
/** For debugging purposes */
private final static short SW_INTERNAL_ERROR = (short) 0x9CFF;
/** Very low probability error */
private final static short SW_BIP32_DERIVATION_ERROR = (short) 0x9C0E;
/** Incorrect initialization of method */
private final static short SW_INCORRECT_INITIALIZATION = (short) 0x9C13;
/** Bip32 seed is not initialized*/
private final static short SW_BIP32_UNINITIALIZED_SEED = (short) 0x9C14;
/** Bip32 seed is already initialized (must be reset before change)*/
private final static short SW_BIP32_INITIALIZED_SEED = (short) 0x9C17;
//** DEPRECATED - Bip32 authentikey pubkey is not initialized*/
//private final static short SW_BIP32_UNINITIALIZED_AUTHENTIKEY_PUBKEY= (short) 0x9C16;
/** Incorrect transaction hash */
private final static short SW_INCORRECT_TXHASH = (short) 0x9C15;
/** 2FA already initialized*/
private final static short SW_2FA_INITIALIZED_KEY = (short) 0x9C18;
/** 2FA uninitialized*/
private final static short SW_2FA_UNINITIALIZED_KEY = (short) 0x9C19;
/** HMAC errors */
static final short SW_HMAC_UNSUPPORTED_KEYSIZE = (short) 0x9c1E;
static final short SW_HMAC_UNSUPPORTED_MSGSIZE = (short) 0x9c1F;
/** Secure channel */
private final static short SW_SECURE_CHANNEL_REQUIRED = (short) 0x9C20;
private final static short SW_SECURE_CHANNEL_UNINITIALIZED = (short) 0x9C21;
private final static short SW_SECURE_CHANNEL_WRONG_IV= (short) 0x9C22;
private final static short SW_SECURE_CHANNEL_WRONG_MAC= (short) 0x9C23;
/** Secret data is too long for import **/
private final static short SW_IMPORTED_DATA_TOO_LONG = (short) 0x9C32;
/** Wrong HMAC when importing Secret through Secure import **/
private final static short SW_SECURE_IMPORT_WRONG_MAC = (short) 0x9C33;
/** Wrong Fingerprint when importing Secret through Secure import **/
private final static short SW_SECURE_IMPORT_WRONG_FINGERPRINT = (short) 0x9C34;
/** No Trusted Pubkey when importing Secret through Secure import **/
private final static short SW_SECURE_IMPORT_NO_TRUSTEDPUBKEY = (short) 0x9C35;
/** PKI perso error */
private final static short SW_PKI_ALREADY_LOCKED = (short) 0x9C40;
/** CARD HAS BEEN RESET TO FACTORY */
private final static short SW_RESET_TO_FACTORY = (short) 0xFF00;
/** For instructions that have been deprecated*/
private final static short SW_INS_DEPRECATED = (short) 0x9C26;
/** For debugging purposes 2 */
private final static short SW_DEBUG_FLAG = (short) 0x9FFF;
// 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;
// JC API 2.2.2 does not define these constants:
private final static byte ALG_ECDSA_SHA_256= (byte) 33;
private final static byte ALG_EC_SVDP_DH_PLAIN= (byte) 3; //https://javacard.kenai.com/javadocs/connected/javacard/security/KeyAgreement.html#ALG_EC_SVDP_DH_PLAIN
private final static byte ALG_EC_SVDP_DH_PLAIN_XY= (byte) 6; //https://docs.oracle.com/javacard/3.0.5/api/javacard/security/KeyAgreement.html#ALG_EC_SVDP_DH_PLAIN_XY
private final static short LENGTH_EC_FP_256= (short) 256;
/****************************************
* Instance variables declaration *
****************************************/
// Key objects (allocated on demand)
private Key[] eckeys;
private ECPrivateKey tmpkey;
short eckeys_flag=0x0000; //flag bit set to 1 when corresponding key is initialised
// PIN and PUK objects, allocated on demand
private OwnerPIN[] pins, ublk_pins;
// card label
private final static byte MAX_CARD_LABEL_SIZE = (byte) 64;
private byte card_label_size = (byte) 0x00;
private byte[] card_label;
// Buffer for storing extended APDUs
private byte[] recvBuffer;
private byte[] tmpBuffer;
/*
* 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;
// shared cryptographic objects
private RandomData randomData;
private KeyAgreement keyAgreement;
private Signature sigECDSA;
private Cipher aes128;
/*********************************************
* BIP32 Hierarchical Deterministic Wallet *
*********************************************/
// Secure Memory & Object Manager with no access from outside (used internally for storing BIP32 objects)
private Bip32ObjectManager bip32_om;
// seed derivation
private static final byte[] BITCOIN_SEED = {'B','i','t','c','o','i','n',' ','s','e','e','d'};
private static final byte MAX_BIP32_DEPTH = 10; // max depth in extended key from master (m/i is depth 1)
// BIP32_object= [ hash(address) (4b) | extended_key (32b) | chain_code (32b) | compression_byte(1b)]
// recvBuffer=[ parent_chain_code (32b) | 0x00 | parent_key (32b) | hash(address) (32b) | current_extended_key(32b) | current_chain_code(32b) ]
// hash(address)= [ index(4b) | unused (28-4b) | ANTICOLLISIONHASH(4b)]
private static final short BIP32_KEY_SIZE= 32; // size of extended key and chain code is 256 bits
private static final short BIP32_ANTICOLLISION_LENGTH=4; // max 12 bytes so that index+crc + two hashes fit in 32 bits
private static final short BIP32_OBJECT_SIZE= (short)(2*BIP32_KEY_SIZE+BIP32_ANTICOLLISION_LENGTH+1);
// offset in working buffer
private static final short BIP32_OFFSET_PARENT_CHAINCODE=0;
private static final short BIP32_OFFSET_PARENT_SEPARATOR=BIP32_KEY_SIZE;
private static final short BIP32_OFFSET_PARENT_KEY=BIP32_KEY_SIZE+1;
private static final short BIP32_OFFSET_INDEX= (short)(2*BIP32_KEY_SIZE+1);
private static final short BIP32_OFFSET_COLLISIONHASH= (short)(BIP32_OFFSET_INDEX+BIP32_KEY_SIZE-BIP32_ANTICOLLISION_LENGTH);
private static final short BIP32_OFFSET_CHILD_KEY= (short)(BIP32_OFFSET_INDEX+BIP32_KEY_SIZE);
private static final short BIP32_OFFSET_CHILD_CHAINCODE= (short)(BIP32_OFFSET_CHILD_KEY+BIP32_KEY_SIZE);
private static final short BIP32_OFFSET_PUB= (short)(BIP32_OFFSET_CHILD_CHAINCODE+BIP32_KEY_SIZE);
private static final short BIP32_OFFSET_PUBX= (short)(BIP32_OFFSET_PUB+1);
private static final short BIP32_OFFSET_PUBY= (short)(BIP32_OFFSET_PUBX+BIP32_KEY_SIZE);
private static final short BIP32_OFFSET_PATH= (short)(BIP32_OFFSET_PUBY+BIP32_KEY_SIZE);
private static final short BIP32_OFFSET_END= (short)(BIP32_OFFSET_PATH+4*MAX_BIP32_DEPTH);
private static final short BIP32_OFFSET_SIG= (short)(ISO7816.OFFSET_CDATA+4*MAX_BIP32_DEPTH);//temporary location
// bip32 keys
private boolean bip32_seeded= false;
private byte bip32_master_compbyte; // compression byte for master key
private AESKey bip32_masterkey;
private AESKey bip32_masterchaincode;
private AESKey bip32_encryptkey; // used to encrypt sensitive data in object
private ECPrivateKey bip32_extendedkey; // object storing last extended key used
/*********************************************
* PKI objects *
*********************************************/
private static final byte[] PKI_CHALLENGE_MSG = {'C','h','a','l','l','e','n','g','e',':'};
private boolean personalizationDone=false;
private ECPrivateKey authentikey_private;
private ECPublicKey authentikey_public;
//private KeyPair authentikey_pair;
private short authentikey_certificate_size=0;
private byte[] authentikey_certificate;
/*********************************************
* Other data instances *
*********************************************/
// Message signing
private static final byte[] BITCOIN_SIGNED_MESSAGE_HEADER = {0x18,'B','i','t','c','o','i','n',' ','S','i','g','n','e','d',' ','M','e','s','s','a','g','e',':','\n'}; //"Bitcoin Signed Message:\n";
private MessageDigest sha256;
private boolean sign_flag= false;
// transaction signing
private byte[] transactionData;
private static final byte OFFSET_TRANSACTION_HASH=0;
private static final byte OFFSET_TRANSACTION_AMOUNT=OFFSET_TRANSACTION_HASH+32;
private static final byte OFFSET_TRANSACTION_TOTAL=OFFSET_TRANSACTION_AMOUNT+8;
private static final byte OFFSET_TRANSACTION_SIZE=OFFSET_TRANSACTION_TOTAL+8;
// tx parsing
private static final byte PARSE_STD=0x00;
private static final byte PARSE_SEGWIT=0x01;
//2FA data
private boolean needs_2FA= false;
//private boolean done_once_2FA= false;
private byte[] data2FA;
private static final byte OFFSET_2FA_HMACKEY=0;
private static final byte OFFSET_2FA_ID=OFFSET_2FA_HMACKEY+20;
private static final byte OFFSET_2FA_LIMIT=OFFSET_2FA_ID+20;
private static final byte OFFSET_2FA_SIZE=OFFSET_2FA_LIMIT+8;
private static final short HMAC_CHALRESP_2FA=(short)0x8000;
/**
* 2FA codes:
* - Tx approval: [ 32b TX hash | 32-bit 0x00-padding ]
* - ECkey import:[ 32b coordx | 32-bit (0x10^key_nb)-padding ]
* - ECkey reset: [ 32b coordx | 32-bit (0x20^key_nb)-padding ]
* - Seed reset: [ 32b authentikey-coordx | 32-bit 0xFF-padding ]
* - 2FA reset: [ 20b 2FA_ID | 44-bit 0xAA-padding ]
* - Msg signing: [ 32b SHA26(btcHeader+msg) | 32-bit 0xBB-padding ]
* - Hash signing:[ 32b Hash | 32-bit 0xCC-padding ]
* */
// 2FA msg encryption
private static final byte[] CST_2FA = {'i','d','_','2','F','A',
'k','e','y','_','2','F','A'};
private Cipher aes128_cbc;
private AESKey key_2FA;
// secure channel
private static final byte[] CST_SC = {'s','c','_','k','e','y', 's','c','_','m','a','c'};
private boolean needs_secure_channel= true;
private boolean initialized_secure_channel= false;
private ECPrivateKey sc_ephemeralkey;
private AESKey sc_sessionkey;
private Cipher sc_aes128_cbc;
private byte[] sc_buffer;
private static final byte OFFSET_SC_IV=0;
private static final byte OFFSET_SC_IV_RANDOM=OFFSET_SC_IV;
private static final byte OFFSET_SC_IV_COUNTER=12;
private static final byte OFFSET_SC_MACKEY=16;
private static final byte SIZE_SC_MACKEY=20;
private static final byte SIZE_SC_IV= 16;
private static final byte SIZE_SC_IV_RANDOM=12;
private static final byte SIZE_SC_IV_COUNTER=SIZE_SC_IV-SIZE_SC_IV_RANDOM;
private static final byte SIZE_SC_BUFFER=SIZE_SC_MACKEY+SIZE_SC_IV;
// import from seedkeeper
private boolean is_trusted_pubkey = false;
private byte[] trusted_pubkey;
private AESKey secret_sc_sessionkey;
private final static byte[] SECRET_CST_SC = {'s','e','c','k','e','y','s','e','c','m','a','c'};
private final static byte SECRET_HEADER_SIZE = (byte) 12;
private final static byte SECRET_FINGERPRINT_SIZE = (byte) 4;
private final static byte MAX_LABEL_SIZE = (byte) 127;
private final static byte SECRET_OFFSET_FINGERPRINT = (byte) 6;
private final static byte PUBKEY_SIZE = (byte) 65;
private final static byte SECRET_TYPE_MASTER_SEED = (byte) 0x10;
private final static byte SECRET_TYPE_2FA= (byte) 0xB0;
// additional options
private short option_flags;
// reset factory
private byte[] reset_array;
private final static byte MAX_RESET_COUNTER= (byte)5;
private byte reset_counter=MAX_RESET_COUNTER;
/****************************************
* Methods *
****************************************/
private CardEdge(byte[] bArray, short bOffset, byte bLength) {
// FIXED: something should be done already here, not only with setup APDU
/* If init pin code does not satisfy policies, internal error */
if (!CheckPINPolicy(PIN_INIT_VALUE, (short) 0, (byte) PIN_INIT_VALUE.length))
ISOException.throwIt(SW_INTERNAL_ERROR);
ublk_pins = new OwnerPIN[MAX_NUM_PINS];
pins = new OwnerPIN[MAX_NUM_PINS];
// DONE: pass in starting PIN setting with instantiation
/* Setting initial PIN n.0 value */
pins[0] = new OwnerPIN((byte) 3, (byte) PIN_INIT_VALUE.length);
pins[0].update(PIN_INIT_VALUE, (short) 0, (byte) PIN_INIT_VALUE.length);
// Temporary working arrays
try {
tmpBuffer = JCSystem.makeTransientByteArray((short) TMP_BUFFER_SIZE, JCSystem.CLEAR_ON_DESELECT);
} catch (SystemException e) {
tmpBuffer = new byte[TMP_BUFFER_SIZE];
}
// 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];
}
// common cryptographic objects
randomData = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM);
sigECDSA= Signature.getInstance(ALG_ECDSA_SHA_256, false);
sha256= MessageDigest.getInstance(MessageDigest.ALG_SHA_256, false);
aes128= Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_ECB_NOPAD, false);
HmacSha160.init(tmpBuffer);
HmacSha512.init(tmpBuffer);
try {
keyAgreement = KeyAgreement.getInstance(ALG_EC_SVDP_DH_PLAIN_XY, false);
} catch (CryptoException e) {
ISOException.throwIt(SW_UNSUPPORTED_FEATURE);// unsupported feature => use a more recent card!
}
//secure channel objects
try {
sc_buffer = JCSystem.makeTransientByteArray((short) SIZE_SC_BUFFER, JCSystem.CLEAR_ON_DESELECT);
} catch (SystemException e) {
sc_buffer = new byte[SIZE_SC_BUFFER];
}
// reset to factory test...
try {
reset_array = JCSystem.makeTransientByteArray((short) 1, JCSystem.CLEAR_ON_RESET);
} catch (SystemException e) {
ISOException.throwIt(SW_UNSUPPORTED_FEATURE);// unsupported feature => use a more recent card!
}
// secure channel
sc_sessionkey= (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false); // todo: make transient?
sc_ephemeralkey= (ECPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE, LENGTH_EC_FP_256, false);
sc_aes128_cbc= Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, false);
// perso PKI: generate public/private keypair
authentikey_private= (ECPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE, LENGTH_EC_FP_256, false);
Secp256k1.setCommonCurveParameters(authentikey_private);
authentikey_public= (ECPublicKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PUBLIC, LENGTH_EC_FP_256, false);
Secp256k1.setCommonCurveParameters(authentikey_public);
//authentikey_pair= new KeyPair(authentikey_public, authentikey_private);
//authentikey_pair.genKeyPair(); //=> cap file fails to load!
randomData.generateData(recvBuffer, (short)0, BIP32_KEY_SIZE);
authentikey_private.setS(recvBuffer, (short)0, BIP32_KEY_SIZE); //random value first
keyAgreement.init(authentikey_private);
keyAgreement.generateSecret(Secp256k1.SECP256K1, Secp256k1.OFFSET_SECP256K1_G, (short) 65, recvBuffer, (short)0); //pubkey in uncompressed form => silently fail after cap loaded
authentikey_public.setW(recvBuffer, (short)0, (short)65);
// BIP32 material
bip32_masterkey= (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_256, false);
bip32_masterchaincode= (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_256, false);
bip32_extendedkey= (ECPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE, LENGTH_EC_FP_256, false);
bip32_encryptkey= (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false);
randomData.generateData(recvBuffer, (short) 0, (short)16);
bip32_encryptkey.setKey(recvBuffer, (short)0);
// private key array
eckeys = new Key[MAX_NUM_KEYS];
// Transaction signing
Transaction.init();
transactionData= new byte[OFFSET_TRANSACTION_SIZE];
// 2FA
data2FA= new byte[OFFSET_2FA_SIZE];
aes128_cbc= Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, false);
key_2FA= (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false);
// card label
card_label = new byte[MAX_CARD_LABEL_SIZE];
// import from SeedKeeper
trusted_pubkey = new byte[PUBKEY_SIZE];
secret_sc_sessionkey = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false);
// debug
register();
} // end of constructor
public static void install(byte[] bArray, short bOffset, byte bLength) {
CardEdge wal = new CardEdge(bArray, bOffset, bLength);
}
public boolean select() {
/*
* Application has been selected: Do session cleanup operation
*/
LogOutAll();
//todo: clear secure channel values?
initialized_secure_channel=false;
return true;
}
public void deselect() {
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))
ISOException.throwIt(ISO7816.SW_FILE_NOT_FOUND); // spurious select (see https://github.com/Toporin/SatochipApplet/issues/11)
// 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];
// Reset to factory
// To trigger reset to factory, user must insert and remove card a fixed number of time,
// without sending any other command than 1 reset in between
if (ins == (byte) INS_RESET_TO_FACTORY){
if (reset_array[0]==0){
reset_counter--;
reset_array[0]=(byte)1;
}else{
// if INS_RESET_TO_FACTORY is sent after any instruction, the reset process is aborted.
reset_counter=MAX_RESET_COUNTER;
ISOException.throwIt((short)(SW_RESET_TO_FACTORY + 0xFF));
}
if (reset_counter== 0) {
reset_counter=MAX_RESET_COUNTER;
resetToFactory();
ISOException.throwIt(SW_RESET_TO_FACTORY);
}
ISOException.throwIt((short)(SW_RESET_TO_FACTORY + reset_counter));
}
else{
reset_counter=MAX_RESET_COUNTER;
reset_array[0]=(byte)1;
}
// prepare APDU buffer
if (ins != (byte) INS_GET_STATUS){
short bytesLeft = Util.makeShort((byte) 0x00, buffer[ISO7816.OFFSET_LC]);
if (bytesLeft != apdu.setIncomingAndReceive())
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
}
// only 3 commands are allowed, the others must be wrapped in a secure channel command
// the 3 commands are: get_status, initialize_secure_channel & process_secure_channel
short sizeout=(short)0;
if (ins == (byte) INS_GET_STATUS){
sizeout= GetStatus(apdu, buffer);
apdu.setOutgoingAndSend((short) 0, sizeout);
return;
}
else if (ins == (byte) INS_INIT_SECURE_CHANNEL){
sizeout= InitiateSecureChannel(apdu, buffer);
apdu.setOutgoingAndSend((short) 0, sizeout);
return;
}
else if (ins == (byte) INS_PROCESS_SECURE_CHANNEL){
sizeout= ProcessSecureChannel(apdu, buffer);
//todo: check if sizeout and buffer[ISO7816.OFFSET_LC] matches...
//if sizeout>4, buffer[ISO7816.OFFSET_LC] should be equal to (sizeout-5)
//todo: remove padding ? (it is actually not used)
}
else if (needs_secure_channel){
ISOException.throwIt(SW_SECURE_CHANNEL_REQUIRED);
}
// at this point, the encrypted content has been deciphered in the buffer
ins = buffer[ISO7816.OFFSET_INS];
// check setup status
//if (!setupDone && (ins != (byte) INS_SETUP))
// ISOException.throwIt(SW_SETUP_NOT_DONE);
if (!setupDone && (ins != INS_SETUP)){
//before setup, only personalization is allowed
if (personalizationDone ||
((ins != INS_VERIFY_PIN)
&& (ins != INS_EXPORT_PKI_PUBKEY)
&& (ins != INS_IMPORT_PKI_CERTIFICATE)
&& (ins != INS_SIGN_PKI_CSR)
&& (ins != INS_LOCK_PKI)) ){
ISOException.throwIt(SW_SETUP_NOT_DONE);
}
}
if (setupDone && (ins == (byte) INS_SETUP))
ISOException.throwIt(SW_SETUP_ALREADY_DONE);
switch (ins) {
case INS_SETUP:
sizeout= setup(apdu, buffer);
break;
case INS_IMPORT_KEY:
sizeout= ImportKey(apdu, buffer);
break;
case INS_RESET_KEY:
sizeout= ResetKey(apdu, buffer);
break;
case INS_GET_PUBLIC_FROM_PRIVATE:
sizeout= getPublicKeyFromPrivate(apdu, buffer);
break;
case INS_VERIFY_PIN:
sizeout= VerifyPIN(apdu, buffer);
break;
case INS_CREATE_PIN:
sizeout= CreatePIN(apdu, buffer);
break;
case INS_CHANGE_PIN:
sizeout= ChangePIN(apdu, buffer);
break;
case INS_UNBLOCK_PIN:
sizeout= UnblockPIN(apdu, buffer);
break;
case INS_LOGOUT_ALL:
sizeout= LogOutAll();
break;
case INS_LIST_PINS:
sizeout= ListPINs(apdu, buffer);
break;
case INS_GET_STATUS:
sizeout= GetStatus(apdu, buffer);
break;
case INS_CARD_LABEL:
sizeout = cardLabel(apdu, buffer);
break;
// BIP32
case INS_BIP32_IMPORT_SEED:
sizeout= importBIP32Seed(apdu, buffer);
break;
case INS_BIP32_RESET_SEED:
sizeout= resetBIP32Seed(apdu, buffer);
break;
case INS_BIP32_GET_AUTHENTIKEY:
sizeout= getBIP32AuthentiKey(apdu, buffer);
break;
case INS_BIP32_SET_AUTHENTIKEY_PUBKEY:
sizeout= setBIP32AuthentikeyPubkey(apdu, buffer);
break;
case INS_BIP32_GET_EXTENDED_KEY:
sizeout= getBIP32ExtendedKey(apdu, buffer);
break;
case INS_BIP32_SET_EXTENDED_PUBKEY:
sizeout= setBIP32ExtendedPubkey(apdu, buffer);
break;
case INS_SIGN_MESSAGE:
sizeout= signMessage(apdu, buffer);
break;
case INS_SIGN_SHORT_MESSAGE:
//sizeout= signShortMessage(apdu, buffer);
ISOException.throwIt(SW_INS_DEPRECATED);
break;
case INS_SIGN_TRANSACTION:
sizeout= SignTransaction(apdu, buffer);
break;
case INS_SIGN_TRANSACTION_HASH:
sizeout= SignTransactionHash(apdu, buffer);
break;
case INS_PARSE_TRANSACTION:
sizeout= ParseTransaction(apdu, buffer);
break;
// 2FA
case INS_SET_2FA_KEY:
sizeout= set2FAKey(apdu, buffer);
break;
case INS_RESET_2FA_KEY:
sizeout= reset2FAKey(apdu, buffer);
break;
case INS_CRYPT_TRANSACTION_2FA:
sizeout= CryptTransaction2FA(apdu, buffer);
break;
// SEEDKEEPER support
case INS_IMPORT_ENCRYPTED_SECRET:
sizeout= importEncryptedSecret(apdu, buffer);
break;
case INS_IMPORT_TRUSTED_PUBKEY:
sizeout= importTrustedPubkey(apdu, buffer);
break;
case INS_EXPORT_TRUSTED_PUBKEY:
sizeout= exportTrustedPubkey(apdu, buffer);
break;
case INS_EXPORT_AUTHENTIKEY:
sizeout= getAuthentikey(apdu, buffer);
break;
//PKI
case INS_EXPORT_PKI_PUBKEY:
sizeout= export_PKI_pubkey(apdu, buffer);
break;
case INS_SIGN_PKI_CSR:
sizeout= sign_PKI_CSR(apdu, buffer);
break;
case INS_IMPORT_PKI_CERTIFICATE:
sizeout= import_PKI_certificate(apdu, buffer);
break;
case INS_EXPORT_PKI_CERTIFICATE:
sizeout= export_PKI_certificate(apdu, buffer);
break;
case INS_LOCK_PKI:
sizeout= lock_PKI(apdu, buffer);
break;
case INS_CHALLENGE_RESPONSE_PKI:
sizeout= challenge_response_pki(apdu, buffer);
break;
default:
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
}//end of switch
// Prepare buffer for return
if (sizeout==0){
return;
}
else if ((ins == (byte) INS_GET_STATUS) || (ins == (byte) INS_INIT_SECURE_CHANNEL)) {
apdu.setOutgoingAndSend((short) 0, sizeout);
}
else if (needs_secure_channel) { // encrypt response
// buffer contains the data (sizeout)
// for encryption, data is padded with PKCS#7
short blocksize=(short)16;
short padsize= (short) (blocksize - (sizeout%blocksize));
Util.arrayCopy(buffer, (short)0, tmpBuffer, (short)0, sizeout);
Util.arrayFillNonAtomic(tmpBuffer, sizeout, padsize, (byte)padsize);//padding
Util.arrayCopy(sc_buffer, OFFSET_SC_IV, buffer, (short)0, SIZE_SC_IV);
sc_aes128_cbc.init(sc_sessionkey, Cipher.MODE_ENCRYPT, sc_buffer, OFFSET_SC_IV, SIZE_SC_IV);
short sizeoutCrypt=sc_aes128_cbc.doFinal(tmpBuffer, (short)0, (short)(sizeout+padsize), buffer, (short) (18));
Util.setShort(buffer, (short)16, sizeoutCrypt);
sizeout= (short)(18+sizeoutCrypt);
//send back
apdu.setOutgoingAndSend((short) 0, sizeout);
}
else {
apdu.setOutgoingAndSend((short) 0, sizeout);
}
} // end of process method
/**
* Setup APDU - initialize the applet and reserve memory
* This is done only once during the lifetime of the applet
*
* ins: INS_SETUP (0x2A)
* p1: 0x00
* p2: 0x00
* data: [default_pin_length(1b) | default_pin |
* pin_tries0(1b) | ublk_tries0(1b) | pin0_length(1b) | pin0 | ublk0_length(1b) | ublk0 |
* pin_tries1(1b) | ublk_tries1(1b) | pin1_length(1b) | pin1 | ublk1_length(1b) | ublk1 |
* secmemsize(2b) | RFU(2b) | RFU(3b) |
* option_flags(2b) |
* (option): hmacsha1_key(20b) | amount_limit(8b)
* ]
* where:
* default_pin: {0x4D, 0x75, 0x73, 0x63, 0x6C, 0x65, 0x30, 0x30};
* pin_tries: max number of PIN try allowed before the corresponding PIN is blocked
* ublk_tries: max number of UBLK(unblock) try allowed before the PUK is blocked
* secmemsize: number of bytes reserved for internal memory (storage of Bip32 objects)
* memsize: number of bytes reserved for memory with external access
* ACL: creation rights for objects - Key - PIN
* option_flags: flags to define up to 16 additional options:
* bit15 set: second factor authentication using hmac-sha1 challenge-response (v0.2-0.1)
* hmacsha1_key: 20-byte hmac key used for transaction authorization
* amount_limit: max amount (in satoshis) allowed without confirmation (this includes change value)
*
* return: none
*/
private short setup(APDU apdu, byte[] buffer) {
personalizationDone=true;// perso PKI should be locked once setup is done
short bytesLeft = Util.makeShort((byte) 0x00, buffer[ISO7816.OFFSET_LC]);
short base = (short) (ISO7816.OFFSET_CDATA);
byte numBytes = buffer[base++];
bytesLeft--;
OwnerPIN pin = pins[0];
if (!CheckPINPolicy(buffer, base, numBytes))
ISOException.throwIt(SW_INVALID_PARAMETER);
byte triesRemaining = pin.getTriesRemaining();
if (triesRemaining == (byte) 0x00)
ISOException.throwIt(SW_IDENTITY_BLOCKED);
if (!pin.check(buffer, base, numBytes))
ISOException.throwIt((short)(SW_PIN_FAILED + triesRemaining - 1));
base += numBytes;
bytesLeft-=numBytes;
byte pin_tries = buffer[base++];
byte ublk_tries = buffer[base++];
numBytes = buffer[base++];
bytesLeft-=3;
if (!CheckPINPolicy(buffer, base, numBytes))
ISOException.throwIt(SW_INVALID_PARAMETER);
pins[0] = new OwnerPIN(pin_tries, PIN_MAX_SIZE);//TODO: new pin or update pin?
pins[0].update(buffer, base, numBytes);
base += numBytes;
bytesLeft-=numBytes;
numBytes = buffer[base++];
bytesLeft--;
if (!CheckPINPolicy(buffer, base, numBytes))
ISOException.throwIt(SW_INVALID_PARAMETER);
if (ublk_pins[0]==null)
ublk_pins[0] = new OwnerPIN(ublk_tries, PIN_MAX_SIZE);
ublk_pins[0].update(buffer, base, numBytes);
base += numBytes;
bytesLeft-=numBytes;
pin_tries = buffer[base++];
ublk_tries = buffer[base++];
numBytes = buffer[base++];
bytesLeft-=3;
if (!CheckPINPolicy(buffer, base, numBytes))
ISOException.throwIt(SW_INVALID_PARAMETER);
if (pins[1]==null)
pins[1] = new OwnerPIN(pin_tries, PIN_MAX_SIZE);
pins[1].update(buffer, base, numBytes);
base += numBytes;
bytesLeft-=numBytes;
numBytes = buffer[base++];
bytesLeft--;
if (!CheckPINPolicy(buffer, base, numBytes))
ISOException.throwIt(SW_INVALID_PARAMETER);
if (ublk_pins[1]==null)
ublk_pins[1] = new OwnerPIN(ublk_tries, PIN_MAX_SIZE);
ublk_pins[1].update(buffer, base, numBytes);
base += numBytes;
bytesLeft-=numBytes;
short secmem_size= Util.getShort(buffer, base);
base += (short) 2;
short RFU = Util.getShort(buffer, base); //mem_size deprecated => RFU...
base += (short) 2;
bytesLeft-=4;
RFU = buffer[base++]; //create_object_ACL deprecated => RFU
RFU = buffer[base++]; //create_key_ACL deprecated => RFU
RFU = buffer[base++]; //create_pin_ACL deprecated => RFU
bytesLeft-=3;
if (bip32_om==null)
bip32_om= new Bip32ObjectManager(secmem_size,BIP32_OBJECT_SIZE, BIP32_ANTICOLLISION_LENGTH);
logged_ids = 0x0000; // No identities logged in
// bip32 material
bip32_seeded= false;
bip32_master_compbyte=0x04;
// bip32_masterkey= (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_256, false);
// bip32_masterchaincode= (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_256, false);
// bip32_encryptkey= (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false);
// // object containing the current extended key
// bip32_extendedkey= (ECPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE, LENGTH_EC_FP_256, false);
Secp256k1.setCommonCurveParameters(bip32_extendedkey);
// key used to recover public key from private TODO: remove?
//bip32_pubkey= (ECPublicKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PUBLIC, LENGTH_EC_FP_256, false);
//Secp256k1.setCommonCurveParameters(bip32_pubkey);
// parse options
option_flags=0;
if (bytesLeft>=2){
option_flags = Util.getShort(buffer, base);
base+=(short)2;
bytesLeft-=(short)2;
// 2FA: transaction confirmation based on hmacsha160
if ((option_flags & HMAC_CHALRESP_2FA)==HMAC_CHALRESP_2FA){
Util.arrayCopyNonAtomic(buffer, base, data2FA, OFFSET_2FA_HMACKEY, (short)20);
base+=(short)20;
bytesLeft-=(short)20;
Util.arrayCopyNonAtomic(buffer, base, data2FA, OFFSET_2FA_LIMIT, (short)8);
base+=(short)8;
bytesLeft-=(short)8;
// hmac derivation for id_2FA & key_2FA
HmacSha160.computeHmacSha160(data2FA, OFFSET_2FA_HMACKEY, (short)20, CST_2FA, (short)0, (short)6, data2FA, OFFSET_2FA_ID);
HmacSha160.computeHmacSha160(data2FA, OFFSET_2FA_HMACKEY, (short)20, CST_2FA, (short)6, (short)7, recvBuffer, (short)0);
key_2FA.setKey(recvBuffer,(short)0); // AES-128: 16-bytes key!!
needs_2FA= true;
}
}
setupDone = true;