@@ -105,6 +105,8 @@ public class GPSession {
105105 private APDUBIBO channel ;
106106 private GPRegistry registry = null ;
107107 private DMTokenizer tokenizer = DMTokenizer .none ();
108+ private ReceiptVerifier verifier = new ReceiptVerifier .NullVerifier ();
109+
108110 private boolean dirty = true ; // True if registry is dirty.
109111
110112 /*
@@ -220,6 +222,10 @@ public void setTokenizer(DMTokenizer tokenizer) {
220222 this .tokenizer = tokenizer ;
221223 }
222224
225+ public void setVerifier (ReceiptVerifier verifier ) {
226+ this .verifier = verifier ;
227+ }
228+
223229 public DMTokenizer getTokenizer () {
224230 return tokenizer ;
225231 }
@@ -433,24 +439,12 @@ public void openSecureChannel(GPCardKeys keys, GPSecureChannelVersion scp, byte[
433439 } else if (this .scpVersion .scp == SCP03 && update_response .length == 32 ) {
434440 seq = Arrays .copyOfRange (update_response , offset , 32 );
435441 offset += seq .length ;
436-
437- if ((scpVersion .i & 0x10 ) == 0x10 ) {
438- byte [] ctx = GPUtils .concatenate (seq , this .sdAID .getBytes ());
439- logger .trace ("Challenge calculation context: {}" , HexUtils .bin2hex (ctx ));
440- byte [] my_card_challenge = keys .scp3_kdf (KeyPurpose .ENC , GPCrypto .scp03_kdf_blocka ((byte ) 0x02 , 64 ), ctx , 8 );
441- if (!Arrays .equals (my_card_challenge , card_challenge )) {
442- logger .warn ("Pseudorandom card challenge does not match expected: {} vs {}" , HexUtils .bin2hex (my_card_challenge ), HexUtils .bin2hex (card_challenge ));
443- } else {
444- logger .debug ("Pseudorandom card challenge matches expected value: {}" , HexUtils .bin2hex (my_card_challenge ));
445- }
446- }
447442 } else {
448443 seq = null ;
449444 }
450445
451446 if (offset != update_response .length ) {
452447 logger .error ("Unhandled data in INITIALIZE UPDATE response: {}" , HexUtils .bin2hex (Arrays .copyOfRange (update_response , offset , update_response .length )));
453- //throw new GPDataException("Unhandled data in INITIALIZE UPDATE response", Arrays.copyOfRange(update_response, offset, update_response.length));
454448 }
455449
456450 logger .debug ("KDD: {}" , HexUtils .bin2hex (diversification_data ));
@@ -477,6 +471,19 @@ public void openSecureChannel(GPCardKeys keys, GPSecureChannelVersion scp, byte[
477471
478472 logger .info ("Diversified card keys: {}" , cardKeys );
479473
474+ // Check pseudorandom card challenge. This must happen _after_ key diversification.
475+ if (scpVersion .scp == SCP03 && (scpVersion .i & 0x10 ) == 0x10 ) {
476+ byte [] ctx = GPUtils .concatenate (seq , this .sdAID .getBytes ());
477+ logger .trace ("Challenge calculation context: {}" , HexUtils .bin2hex (ctx ));
478+ byte [] my_card_challenge = keys .scp3_kdf (KeyPurpose .ENC , GPCrypto .scp03_kdf_blocka ((byte ) 0x02 , 64 ), ctx , 8 );
479+ if (!Arrays .equals (my_card_challenge , card_challenge )) {
480+ logger .warn ("Pseudorandom card challenge does not match expected: {} vs {}" , HexUtils .bin2hex (my_card_challenge ), HexUtils .bin2hex (card_challenge ));
481+ } else {
482+ logger .debug ("Pseudorandom card challenge matches expected value: {}" , HexUtils .bin2hex (my_card_challenge ));
483+ }
484+ }
485+
486+
480487 // Derive session keys
481488 if (this .scpVersion .scp == GPSecureChannelVersion .SCP .SCP02 ) {
482489 sessionContext = seq .clone ();
@@ -540,11 +547,23 @@ public void openSecureChannel(GPCardKeys keys, GPSecureChannelVersion scp, byte[
540547 // Pipe through secure channel
541548 public ResponseAPDU transmit (CommandAPDU command ) throws IOException {
542549 try {
543- // TODO: BIBO pretty printer
544- //logger.trace("PT> {}", HexUtils.bin2hex(command.getBytes()));
545- ResponseAPDU unwrapped = wrapper .unwrap (channel .transmit (wrapper .wrap (command )));
546- //logger.trace("PT < {}", HexUtils.bin2hex(unwrapped.getBytes()));
547- return unwrapped ;
550+ CommandAPDU wrapped = wrapper .wrap (command );
551+ ResponseAPDU resp = null ;
552+
553+ // GPC 2.3.1 11.1.5.1
554+ List <byte []> chunks = GPUtils .splitArray (wrapped .getData (), blockSize );
555+ if (chunks .size () > 1 )
556+ logger .debug ("Chaining in {} chunks" , chunks .size ());
557+
558+ for (int i = 0 ; i < chunks .size (); i ++) {
559+ boolean last = i == chunks .size () - 1 ;
560+ int p1 = last ? command .getP1 () : command .getP1 () | 0x80 ; // XXX: should check if instruction is eligible for this treatment
561+ resp = channel .transmit (new CommandAPDU (wrapped .getCLA (), wrapped .getINS (), p1 , wrapped .getP2 (), chunks .get (i ), 256 ));
562+ if (!last ) {
563+ GPException .check (resp );
564+ }
565+ }
566+ return wrapper .unwrap (resp );
548567 } catch (GPException e ) {
549568 throw new IOException ("Secure channel failure: " + e .getMessage (), e );
550569 }
@@ -584,13 +603,13 @@ public void loadCapFile(CAPFile cap, AID targetDomain, AID dapDomain, byte[] dap
584603 byte [] hash = hashFunction == null ? new byte [0 ] : cap .getLoadFileDataHash (hashFunction .algo );
585604 byte [] code = cap .getCode ();
586605 byte [] loadParams = new byte [0 ]; // FIXME
587-
606+ AID pkg = cap . getPackageAID ();
588607
589608 ByteArrayOutputStream bo = new ByteArrayOutputStream ();
590609
591610 try {
592- bo .write (cap . getPackageAID () .getLength ());
593- bo .write (cap . getPackageAID () .getBytes ());
611+ bo .write (pkg .getLength ());
612+ bo .write (pkg .getBytes ());
594613
595614 bo .write (targetDomain .getLength ());
596615 bo .write (targetDomain .getBytes ());
@@ -609,6 +628,7 @@ public void loadCapFile(CAPFile cap, AID targetDomain, AID dapDomain, byte[] dap
609628 command = tokenizer .tokenize (command );
610629 ResponseAPDU response = transmitLV (command );
611630 GPException .check (response , "INSTALL [for load] failed" );
631+ verifier .check (response , ReceiptVerifier .load (pkg , targetDomain ));
612632
613633 // Construct load block
614634 ByteArrayOutputStream loadBlock = new ByteArrayOutputStream ();
@@ -637,7 +657,7 @@ public void loadCapFile(CAPFile cap, AID targetDomain, AID dapDomain, byte[] dap
637657
638658 for (int i = 0 ; i < blocks .size (); i ++) {
639659 byte p1 = (i == (blocks .size () - 1 )) ? P1_LAST_BLOCK : P1_MORE_BLOCKS ;
640- CommandAPDU load = new CommandAPDU (CLA_GP , INS_LOAD , p1 , (byte ) i , blocks .get (i ));
660+ CommandAPDU load = new CommandAPDU (CLA_GP , INS_LOAD , p1 , (byte ) i , blocks .get (i ), 256 );
641661 response = transmit (load );
642662 GPException .check (response , "LOAD failed" );
643663 }
@@ -654,6 +674,8 @@ public void installAndMakeSelectable(AID packageAID, AID appletAID, AID instance
654674 command = tokenizer .tokenize (command );
655675 ResponseAPDU response = transmitLV (command );
656676 GPException .check (response , "INSTALL [for install and make selectable] failed" );
677+
678+ verifier .check (response , ReceiptVerifier .install_make_selectable (packageAID , instanceAID ));
657679 dirty = true ;
658680 }
659681
@@ -664,6 +686,7 @@ private byte[] buildInstallData(AID packageAID, AID appletAID, AID instanceAID,
664686 if (installParams == null || installParams .length == 0 ) {
665687 installParams = new byte []{(byte ) 0xC9 , 0x00 };
666688 }
689+ // FIXME: if the parameters parse as tlv, check for presence of 0xC9 before prepending
667690 // Simple use: only application parameters without tag, prepend 0xC9
668691 if (installParams [0 ] != (byte ) 0xC9 ) {
669692 byte [] newparams = new byte [installParams .length + 2 ];
@@ -719,6 +742,8 @@ public void extradite(AID what, AID to) throws GPException, IOException {
719742 command = tokenizer .tokenize (command );
720743 ResponseAPDU response = transmitLV (command );
721744 GPException .check (response , "INSTALL [for extradition] failed" );
745+
746+ verifier .check (response , ReceiptVerifier .extradite (sdAID , what , to ));
722747 dirty = true ;
723748 }
724749
@@ -848,6 +873,7 @@ public void deleteAID(AID aid, boolean deleteDeps) throws GPException, IOExcepti
848873 command = tokenizer .tokenize (command );
849874 ResponseAPDU response = transmitTLV (command );
850875 GPException .check (response , "DELETE failed" );
876+ verifier .check (response , ReceiptVerifier .delete (aid ));
851877 dirty = true ;
852878 }
853879
@@ -858,7 +884,7 @@ public void deleteKey(Integer keyver, Integer keyid) throws GPException, IOExcep
858884 throw new IllegalArgumentException ("Must specify either key version or key ID" );
859885
860886 ByteArrayOutputStream bo = new ByteArrayOutputStream ();
861- if (keyid != null ) {
887+ if (keyid != null ) {
862888 bo .write (0xd0 ); // Key Identifier
863889 bo .write (1 );
864890 bo .write (0x01 );
@@ -981,10 +1007,10 @@ byte[] encodeRSAKey(RSAPublicKey key) {
9811007 byte [] exponent = GPUtils .positive (key .getPublicExponent ());
9821008
9831009 bo .write (0xA1 ); // Modulus
984- bo .write (modulus .length );
1010+ bo .write (GPUtils . encodeLength ( modulus .length ) );
9851011 bo .write (modulus );
9861012 bo .write (0xA0 );
987- bo .write (exponent .length );
1013+ bo .write (GPUtils . encodeLength ( exponent .length ) );
9881014 bo .write (exponent );
9891015 bo .write (0x00 ); // No KCV
9901016 } catch (IOException e ) {
0 commit comments