diff options
Diffstat (limited to 'smccTest')
5 files changed, 460 insertions, 201 deletions
| diff --git a/smccTest/src/main/java/at/gv/egiz/smcc/activation/Activation.java b/smccTest/src/main/java/at/gv/egiz/smcc/activation/Activation.java index 516e9487..8e618ac7 100644 --- a/smccTest/src/main/java/at/gv/egiz/smcc/activation/Activation.java +++ b/smccTest/src/main/java/at/gv/egiz/smcc/activation/Activation.java @@ -33,7 +33,6 @@ import java.security.InvalidKeyException;  import java.security.MessageDigest;  import java.security.NoSuchAlgorithmException;  import java.security.NoSuchProviderException; -import java.security.PublicKey;  import java.util.Arrays;  import java.util.List;  import java.util.Random; @@ -270,7 +269,7 @@ public class Activation {          resp = channel.transmit(cmdAPDU);          System.out.println(" -> " + toString(resp.getBytes()) + "\n"); -//        openSecureChannel(k_qsig, cin); +        openSecureChannel(k_qsig, cin);          System.out.println("READ BINARY");          cmdAPDU = new CommandAPDU(0x00, 0xb0, 0x00, 0x00, 256); @@ -296,8 +295,11 @@ public class Activation {  //            System.out.println("Qx: " + toString(Qx));  //            System.out.println("Qy: " + toString(Qy)); -//            ECPublicKey ecPuK = decodeECPublicKey(Q, new String(oid)); -//            System.out.println("PuK: " + ecPuK); +            byte[] Q_ = new byte[Q.length + 1]; +            Q_[0] = (byte) 0x04; +            System.arraycopy(Q, 0, Q_, 1, Q.length); +            ECPublicKey ecPuK = decodeECPublicKey(Q_, new String(oid)); +            System.out.println("PuK: " + ecPuK);          } catch (CodingException ex) {              throw new SignatureCardException("failed to read EF.PuK", ex); @@ -507,7 +509,7 @@ public class Activation {      //	print("MAC          : " + mac); -        byte[] mac = RetailCBCMac.retailMac(cryptogram, "DES", "DESede", k_mac, 8, 8); +        byte[] mac = RetailCBCMac.retailMac(cryptogram, RetailCBCMac.PADDING.ISO9797_2, "DES", "DESede", k_mac, 8, 8);          System.out.println("mac (" + mac.length +"byte): " + toString(mac));  //  // @@ -539,7 +541,7 @@ public class Activation {          System.arraycopy(resp.getData(), 0, cryptogram, 0, 96);          System.arraycopy(resp.getData(), 96, mac, 0, 8); -        byte[] mac_ = RetailCBCMac.retailMac(cryptogram, "DES", "DESede", k_mac, 8, 8); +        byte[] mac_ = RetailCBCMac.retailMac(cryptogram, RetailCBCMac.PADDING.ISO9797_2, "DES", "DESede", k_mac, 8, 8);          if (!Arrays.equals(mac, mac_)) {            throw new SignatureCardException("Failed to authenticate card, invalid MAC " + toString(mac)); @@ -595,7 +597,8 @@ public class Activation {          for (int i = 0; i < kd_ifd.length; i++) {              kinp[i] = (byte) (kd_icc[i] ^ kd_ifd[i]);          } -        System.out.println("session key negotiation key (key seed): " + toString(kinp)); + +        System.out.println("session key negotiation key (key seed) (" + kinp.length + "B): " + toString(kinp));          //	var hashin = keyinp.concat(new ByteString("00000001", HEX));          //	var hashres = crypto.digest(Crypto.SHA_256, hashin);          //	var kencval = hashres.bytes(0, 24); @@ -623,6 +626,7 @@ public class Activation {          //	GPSystem.trace("Kmac SSC     : " + kmacssc);          //	var kmac = new Key();          //	kmac.setComponent(Key.DES, kmacval); +          sha256.update(kinp);          sha256.update(new byte[]{(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x02});          enc_ = sha256.digest(); @@ -640,7 +644,7 @@ public class Activation {          //	return sc;          //} -        return new SecureChannel(channel, kenc, kmac, kencssc, kmacssc); +        return new SecureChannel(channel, kenc, kmac, 8, kencssc, kmacssc);      }       /** @@ -675,7 +679,6 @@ public class Activation {      // ECGroupFactory. We assume that the curve is non-singular.      EllipticCurve ec = gfac.getCurve(aElement, bElement, r, CoordinateTypes.AFFINE_COORDINATES); -      AffineCoordinate coord = PointFormatter.getInstance().getPointCodec().decodePoint(ecPoint, ec);      // With these coordinates, we can construct a point on the curve. @@ -687,14 +690,92 @@ public class Activation {    } -    public static void main(String[] args) { +  public static void main(String[] args) { -        try { -            Activation test = new Activation(); -            test.setUp(); -            test.activate(); -        } catch (Exception e) { -            e.printStackTrace(); +    try { +      Activation test = new Activation(); +      test.setUp(); +      test.activate(); +//      test.testMACProtection(); +    } catch (Exception e) { +        e.printStackTrace(); +    } +  } + + +/* +  static final byte[] kicc = new byte[] { (byte)0xB3, (byte)0xB1, (byte)0x0A, (byte)0x87, (byte)0x84, (byte)0xEC, (byte)0x26, (byte)0xE7, (byte)0x90, (byte)0xA1, (byte)0x14, (byte)0xFC, (byte)0x1A, (byte)0xA9, (byte)0x40, (byte)0xF9, (byte)0x7E, (byte)0xE9, (byte)0xAC, (byte)0x69, (byte)0x6D, (byte)0x64, (byte)0xAF, (byte)0xD8, (byte)0x24, (byte)0xCA, (byte)0x6A, (byte)0xB7, (byte)0x30, (byte)0x49, (byte)0x68, (byte)0xFE, (byte)0xFD, (byte)0x5A, (byte)0xC6, (byte)0x58, (byte)0xAD, (byte)0x33, (byte)0xC1, (byte)0xE4, (byte)0xDE, (byte)0x16, (byte)0xC0, (byte)0x06, (byte)0x66, (byte)0xA3, (byte)0x1E, (byte)0x86, (byte)0xEF, (byte)0xBF, (byte)0xAA, (byte)0x3B, (byte)0x4B, (byte)0xCA, (byte)0x47, (byte)0x81, (byte)0x33, (byte)0xE1, (byte)0x67, (byte)0x6A, (byte)0x69, (byte)0xA5, (byte)0x4E, (byte)0xB4 }; +  static final byte[] kifd = new byte[] { (byte)0xB9, (byte)0xFD, (byte)0x50, (byte)0xF3, (byte)0x25, (byte)0x92, (byte)0xE9, (byte)0xF9, (byte)0x7C, (byte)0xF4, (byte)0x8F, (byte)0xEF, (byte)0xC4, (byte)0x8E, (byte)0x93, (byte)0x8F, (byte)0xC6, (byte)0x86, (byte)0x40, (byte)0xAF, (byte)0x4F, (byte)0x3E, (byte)0xFF, (byte)0x76, (byte)0xDE, (byte)0x1B, (byte)0xCE, (byte)0x87, (byte)0x8A, (byte)0x19, (byte)0xF9, (byte)0x2F, (byte)0xF9, (byte)0x94, (byte)0x9A, (byte)0x63, (byte)0x4E, (byte)0xED, (byte)0x84, (byte)0xDE, (byte)0x5B, (byte)0x47, (byte)0x53, (byte)0x12, (byte)0xBC, (byte)0xD6, (byte)0x24, (byte)0x1C, (byte)0xE0, (byte)0xC4, (byte)0xB1, (byte)0x83, (byte)0x24, (byte)0x33, (byte)0x11, (byte)0x49, (byte)0x51, (byte)0x3E, (byte)0xDC, (byte)0xB4, (byte)0x3F, (byte)0x7B, (byte)0xF2, (byte)0x71 }; +  static final byte[] keyinp = new byte[] { (byte)0x0A, (byte)0x4C, (byte)0x5A, (byte)0x74, (byte)0xA1, (byte)0x7E, (byte)0xCF, (byte)0x1E, (byte)0xEC, (byte)0x55, (byte)0x9B, (byte)0x13, (byte)0xDE, (byte)0x27, (byte)0xD3, (byte)0x76, (byte)0xB8, (byte)0x6F, (byte)0xEC, (byte)0xC6, (byte)0x22, (byte)0x5A, (byte)0x50, (byte)0xAE, (byte)0xFA, (byte)0xD1, (byte)0xA4, (byte)0x30, (byte)0xBA, (byte)0x50, (byte)0x91, (byte)0xD1, (byte)0x04, (byte)0xCE, (byte)0x5C, (byte)0x3B, (byte)0xE3, (byte)0xDE, (byte)0x45, (byte)0x3A, (byte)0x85, (byte)0x51, (byte)0x93, (byte)0x14, (byte)0xDA, (byte)0x75, (byte)0x3A, (byte)0x9A, (byte)0x0F, (byte)0x7B, (byte)0x1B, (byte)0xB8, (byte)0x6F, (byte)0xF9, (byte)0x56, (byte)0xC8, (byte)0x62, (byte)0xDF, (byte)0xBB, (byte)0xDE, (byte)0x56, (byte)0xDE, (byte)0xBC, (byte)0xC5 }; +  static final byte[] hashinp = new byte[] { (byte)0x0A, (byte)0x4C, (byte)0x5A, (byte)0x74, (byte)0xA1, (byte)0x7E, (byte)0xCF, (byte)0x1E, (byte)0xEC, (byte)0x55, (byte)0x9B, (byte)0x13, (byte)0xDE, (byte)0x27, (byte)0xD3, (byte)0x76, (byte)0xB8, (byte)0x6F, (byte)0xEC, (byte)0xC6, (byte)0x22, (byte)0x5A, (byte)0x50, (byte)0xAE, (byte)0xFA, (byte)0xD1, (byte)0xA4, (byte)0x30, (byte)0xBA, (byte)0x50, (byte)0x91, (byte)0xD1, (byte)0x04, (byte)0xCE, (byte)0x5C, (byte)0x3B, (byte)0xE3, (byte)0xDE, (byte)0x45, (byte)0x3A, (byte)0x85, (byte)0x51, (byte)0x93, (byte)0x14, (byte)0xDA, (byte)0x75, (byte)0x3A, (byte)0x9A, (byte)0x0F, (byte)0x7B, (byte)0x1B, (byte)0xB8, (byte)0x6F, (byte)0xF9, (byte)0x56, (byte)0xC8, (byte)0x62, (byte)0xDF, (byte)0xBB, (byte)0xDE, (byte)0x56, (byte)0xDE, (byte)0xBC, (byte)0xC5, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01 }; +//hashres      : A6 85 3E A7 A2 9E 2D 5C 00 CC 8D 0A E5 89 9E DF 6A 0A AD 65 47 C6 12 A2 19 D8 67 49 D9 F3 A0 6E +* + */ + + +  /* +   * +   Plain Block  : 3E 43 8A 71 9E 25 1E 87 33 1F D2 4A A7 79 12 18 5B 3D B2 93 3F E8 CE 7C 00 00 00 23 00 79 05 03 4A B5 FB 44 33 01 64 57 80 21 B5 B1 B6 FF D9 C3 5C 10 FB 4A A8 88 B7 F8 79 19 D6 3A 3E 5D F4 D7 1B CA 48 82 E9 9B 6E C7 4F AB 34 CC 0D AA 43 25 21 29 D0 78 31 A3 F2 26 F7 EF 52 70 F9 2E 84 2B +K_enc        : C7 61 DA 43 FD 89 89 9E 92 54 AD 08 5D 67 4F 8C 38 DC 32 7C A4 FB 67 07 +Cryptogram   : B1 0C 9A F3 6D FA B0 70 D8 4D 6A 0B 3C E9 8E 5E 50 D0 2A 82 1E CD 70 77 6F CC D4 E8 4D 0E A3 E6 B8 87 2E 31 0F B1 0B 42 5F EB C6 36 B0 EC 18 86 94 0D 5B 67 6C 1A 96 8F C7 2B 8E 4B 85 2A 91 63 C9 E4 66 43 42 D9 55 FF 44 5C C9 DE A6 44 D3 46 37 DA 47 02 A3 63 BA E7 4D CB 52 64 5D F6 B4 94 +K_mac        : 02 E5 8A D6 1A DA 98 EA E9 0B C4 68 0B C8 29 A4 31 26 F1 16 91 46 7F 97 +MAC          : 6B 51 02 0A DD 8F 10 55 +kicc         : 56 B8 10 10 B9 BE 09 34 AA DA 1A 0A 64 24 B7 35 5A 6C A0 5C 75 9F A8 B1 B6 1F 4E 5E 16 15 37 51 EA 9F 0B DE CC FC 31 0B 21 40 28 B5 75 63 86 79 6E 64 C8 14 4A C6 05 1E C1 FB 7E 86 EF 7F B9 9C +kifd         : 4A B5 FB 44 33 01 64 57 80 21 B5 B1 B6 FF D9 C3 5C 10 FB 4A A8 88 B7 F8 79 19 D6 3A 3E 5D F4 D7 1B CA 48 82 E9 9B 6E C7 4F AB 34 CC 0D AA 43 25 21 29 D0 78 31 A3 F2 26 F7 EF 52 70 F9 2E 84 2B +keyinp         : 1C 0D EB 54 8A BF 6D 63 2A FB AF BB D2 DB 6E F6 06 7C 5B 16 DD 17 1F 49 CF 06 98 64 28 48 C3 86 F1 55 43 5C 25 67 5F CC 6E EB 1C 79 78 C9 C5 5C 4F 4D 18 6C 7B 65 F7 38 36 14 2C F6 16 51 3D B7 +hashinp      : 1C 0D EB 54 8A BF 6D 63 2A FB AF BB D2 DB 6E F6 06 7C 5B 16 DD 17 1F 49 CF 06 98 64 28 48 C3 86 F1 55 43 5C 25 67 5F CC 6E EB 1C 79 78 C9 C5 5C 4F 4D 18 6C 7B 65 F7 38 36 14 2C F6 16 51 3D B7 00 00 00 01 +hashres      : 78 8B 53 B9 E0 3B 9B 87 11 42 4A 13 2D 03 7B AA 0E 9E 97 43 4E 4E FC 5A F1 BE 47 51 F3 EC C8 04 +hashinp      : 1C 0D EB 54 8A BF 6D 63 2A FB AF BB D2 DB 6E F6 06 7C 5B 16 DD 17 1F 49 CF 06 98 64 28 48 C3 86 F1 55 43 5C 25 67 5F CC 6E EB 1C 79 78 C9 C5 5C 4F 4D 18 6C 7B 65 F7 38 36 14 2C F6 16 51 3D B7 00 00 00 02 +hashres      : 4D BF FC AD 67 94 55 F9 7F DD 47 30 C7 74 B1 7D CF B8 B3 74 93 87 0F 21 ED 72 96 5D D6 04 66 58 +   */ + + +    /** +     * sniffed values (cf. activate-800400...790503.log lines 95-101, hashres = sk2 | ssc2 +     * and apdu_ from activate activate-800400...790503-apdu.log lines 79-83, corresponding challenge) +     */ +     static final byte[] kicc = new byte[] { (byte)0x56, (byte)0xB8, (byte)0x10, (byte)0x10, (byte)0xB9, (byte)0xBE, (byte)0x09, (byte)0x34, (byte)0xAA, (byte)0xDA, (byte)0x1A, (byte)0x0A, (byte)0x64, (byte)0x24, (byte)0xB7, (byte)0x35, (byte)0x5A, (byte)0x6C, (byte)0xA0, (byte)0x5C, (byte)0x75, (byte)0x9F, (byte)0xA8, (byte)0xB1, (byte)0xB6, (byte)0x1F, (byte)0x4E, (byte)0x5E, (byte)0x16, (byte)0x15, (byte)0x37, (byte)0x51, (byte)0xEA, (byte)0x9F, (byte)0x0B, (byte)0xDE, (byte)0xCC, (byte)0xFC, (byte)0x31, (byte)0x0B, (byte)0x21, (byte)0x40, (byte)0x28, (byte)0xB5, (byte)0x75, (byte)0x63, (byte)0x86, (byte)0x79, (byte)0x6E, (byte)0x64, (byte)0xC8, (byte)0x14, (byte)0x4A, (byte)0xC6, (byte)0x05, (byte)0x1E, (byte)0xC1, (byte)0xFB, (byte)0x7E, (byte)0x86, (byte)0xEF, (byte)0x7F, (byte)0xB9, (byte)0x9C }; +     static final byte[] kifd = new byte[] { (byte)0x4A, (byte)0xB5, (byte)0xFB, (byte)0x44, (byte)0x33, (byte)0x01, (byte)0x64, (byte)0x57, (byte)0x80, (byte)0x21, (byte)0xB5, (byte)0xB1, (byte)0xB6, (byte)0xFF, (byte)0xD9, (byte)0xC3, (byte)0x5C, (byte)0x10, (byte)0xFB, (byte)0x4A, (byte)0xA8, (byte)0x88, (byte)0xB7, (byte)0xF8, (byte)0x79, (byte)0x19, (byte)0xD6, (byte)0x3A, (byte)0x3E, (byte)0x5D, (byte)0xF4, (byte)0xD7, (byte)0x1B, (byte)0xCA, (byte)0x48, (byte)0x82, (byte)0xE9, (byte)0x9B, (byte)0x6E, (byte)0xC7, (byte)0x4F, (byte)0xAB, (byte)0x34, (byte)0xCC, (byte)0x0D, (byte)0xAA, (byte)0x43, (byte)0x25, (byte)0x21, (byte)0x29, (byte)0xD0, (byte)0x78, (byte)0x31, (byte)0xA3, (byte)0xF2, (byte)0x26, (byte)0xF7, (byte)0xEF, (byte)0x52, (byte)0x70, (byte)0xF9, (byte)0x2E, (byte)0x84, (byte)0x2B }; +     static final byte[] sk2 = new byte[] { (byte)0x4D, (byte)0xBF, (byte)0xFC, (byte)0xAD, (byte)0x67, (byte)0x94, (byte)0x55, (byte)0xF9, (byte)0x7F, (byte)0xDD, (byte)0x47, (byte)0x30, (byte)0xC7, (byte)0x74, (byte)0xB1, (byte)0x7D, (byte)0xCF, (byte)0xB8, (byte)0xB3, (byte)0x74, (byte)0x93, (byte)0x87, (byte)0x0F, (byte)0x21 }; +     static final byte[] ssc2 = new byte[] { (byte)0xED, (byte)0x72, (byte)0x96, (byte)0x5D, (byte)0xD6, (byte)0x04, (byte)0x66, (byte)0x58 }; +     static final byte[] apdu_ = new byte[] { (byte)0x0C, (byte)0xB0, (byte)0x00, (byte)0x00, (byte)0x0D, (byte)0x97, (byte)0x01, (byte)0xDF, (byte)0x8E, (byte)0x08, (byte)0xCE, (byte)0xBD, (byte)0xFA, (byte)0xEC, (byte)0xFA, (byte)0xB2, (byte)0xC5, (byte)0xD7, (byte)0x00 }; + + +    void testMACProtection() throws IllegalArgumentException, InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException, GeneralSecurityException { + +        SecureChannel channel = secureChannel(kicc, kifd); + +        if (!Arrays.equals(channel.kmac.getEncoded(), sk2)) { +            System.out.println("derived session key kmac does not match"); +            System.out.println("kmac orig:    " + toString(sk2)); +            System.out.println("kmac test:    " + toString(channel.kmac.getEncoded())); +        } +        if (!Arrays.equals(channel.kmacssc, ssc2)) { +            System.out.println("derived send sequence counter kmacssc does not match"); +            System.out.println("kmacssc orig: " + toString(ssc2)); +            System.out.println("kmacssc test: " + toString(channel.kmacssc)); +        } +         +         +        for (int i = 0; i < 3; i++) { +            byte[] case2apdu = channel.protectCase2(new byte[] { (byte)0x00, (byte)0x0b, (byte)0x00, (byte)0x00, (byte)0x00 }); + +            System.out.println("apdu orig:   " + toString(apdu_)); +            System.out.println("apdu test:   " + toString(case2apdu)); + +            if (Arrays.equals(apdu_, case2apdu)) { +                System.out.println(" ******************************* "); +            }          }      } + +		public static final byte[] SM_RESP_APDU = new byte[] { +			(byte)0x81, (byte)0x81, (byte)0xac, (byte)0xa8, (byte)0x82, (byte)0x00, (byte)0xa8, (byte)0xb6, (byte)0x16, (byte)0x83, (byte)0x14, (byte)0x80, (byte)0x04, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x23, (byte)0x00, (byte)0x79, (byte)0x05, (byte)0x03, (byte)0xd0, (byte)0x40, (byte)0x00, (byte)0x00, (byte)0x17, (byte)0x00, (byte)0x12, (byte)0x01, (byte)0x02, (byte)0x00, (byte)0x7f, +			(byte)0x49, (byte)0x82, (byte)0x00, (byte)0x44, (byte)0x86, (byte)0x40, (byte)0xd4, (byte)0x7c, (byte)0x12, (byte)0x55, (byte)0xe4, (byte)0x7b, (byte)0x0c, (byte)0x7d, (byte)0x4e, (byte)0xbb, (byte)0x17, (byte)0xe4, (byte)0x83, (byte)0xe5, (byte)0x3d, (byte)0x56, (byte)0xdf, (byte)0x45, (byte)0x7e, (byte)0x99, (byte)0xcb, (byte)0xcc, (byte)0x93, (byte)0xd2, (byte)0xc2, (byte)0x5e, +			(byte)0x4d, (byte)0x91, (byte)0x27, (byte)0x6e, (byte)0x8b, (byte)0xe7, (byte)0x6d, (byte)0x23, (byte)0x53, (byte)0xf6, (byte)0xab, (byte)0x2e, (byte)0xa6, (byte)0xdd, (byte)0xb7, (byte)0x1c, (byte)0x68, (byte)0xfb, (byte)0x59, (byte)0xcd, (byte)0xd0, (byte)0x45, (byte)0x2b, (byte)0x10, (byte)0x0e, (byte)0x27, (byte)0x00, (byte)0x6e, (byte)0xaa, (byte)0x1c, (byte)0x49, (byte)0x90, +			(byte)0x67, (byte)0xa9, (byte)0x9f, (byte)0x59, (byte)0xd1, (byte)0x97, (byte)0xc1, (byte)0x00, (byte)0xc0, (byte)0x01, (byte)0x80, (byte)0x9e, (byte)0x82, (byte)0x00, (byte)0x40, (byte)0xde, (byte)0x22, (byte)0x37, (byte)0x4c, (byte)0x41, (byte)0xe0, (byte)0xf7, (byte)0x94, (byte)0x9a, (byte)0x5a, (byte)0xe4, (byte)0x76, (byte)0xb8, (byte)0x9b, (byte)0x00, (byte)0xb8, (byte)0x23, +			(byte)0x7c, (byte)0xe9, (byte)0x4a, (byte)0x92, (byte)0xfd, (byte)0xb0, (byte)0xfb, (byte)0x25, (byte)0x4a, (byte)0xa7, (byte)0x0e, (byte)0x4d, (byte)0x5f, (byte)0x6f, (byte)0x3d, (byte)0x3a, (byte)0x54, (byte)0x28, (byte)0xf8, (byte)0x90, (byte)0xa1, (byte)0x7d, (byte)0x60, (byte)0x28, (byte)0xf8, (byte)0x72, (byte)0xb7, (byte)0x0f, (byte)0x9f, (byte)0xa6, (byte)0xa8, (byte)0x53, +			(byte)0x15, (byte)0xf2, (byte)0x9f, (byte)0x88, (byte)0x37, (byte)0xd4, (byte)0x6b, (byte)0x77, (byte)0xf7, (byte)0x69, (byte)0xc1, (byte)0xb9, (byte)0xe7, (byte)0x2a, (byte)0x43, (byte)0x99, (byte)0x02, (byte)0x90, (byte)0x00, (byte)0x8e, (byte)0x08, (byte)0xbc, (byte)0xdc, (byte)0x25, (byte)0x67, (byte)0x5e, (byte)0xfd, (byte)0x6c, (byte)0xba }; //, (byte)0x90, (byte)0x00}; + +		  } diff --git a/smccTest/src/main/java/at/gv/egiz/smcc/activation/RetailCBCMac.java b/smccTest/src/main/java/at/gv/egiz/smcc/activation/RetailCBCMac.java index f8cedc0e..c614e57f 100644 --- a/smccTest/src/main/java/at/gv/egiz/smcc/activation/RetailCBCMac.java +++ b/smccTest/src/main/java/at/gv/egiz/smcc/activation/RetailCBCMac.java @@ -13,6 +13,10 @@ import javax.crypto.spec.IvParameterSpec;  import javax.crypto.spec.SecretKeySpec;  public class RetailCBCMac { + +    public enum PADDING { +      NoPadding, ISO9797_2 +    };    /**     * Calculates a Retail CBC Mac from the given message. @@ -53,7 +57,8 @@ public class RetailCBCMac {     * @throws InvalidKeyException if the key cannot be used with the Ciphers      * @throws GeneralSecurityException if the Cipher operation(s) fails     */ -  static byte[] retailMac(byte[] msg,  +  static byte[] retailMac(byte[] msg, +                          PADDING msgPadding,                            String cbcCipherAlg,                            String cipherAlg,                            SecretKey csk, @@ -84,17 +89,20 @@ public class RetailCBCMac {        System.arraycopy(rawCsk, 0, rawCbcCipherKey, 0, blockSize);        cbcCipherKey = new SecretKeySpec(rawCbcCipherKey, cbcCipherAlg);      } -    // if necessary pad message with zeros -    byte[] paddedMsg = pad(msg, blockSize); -     + +    if (msgPadding == PADDING.ISO9797_2) { +        // if necessary pad message with zeros +        msg = pad(msg, blockSize); +    } +      // calculate CBC Mac for the first n-1 blocks -    int n = paddedMsg.length; +    int n = msg.length;      int n_1 = n - blockSize; -    byte[] cbcMac = cbcMac(paddedMsg, 0, n_1, cbcCipherKey, cbcCipherAlg, blockSize); +    byte[] cbcMac = cbcMac(msg, 0, n_1, cbcCipherKey, cbcCipherAlg, blockSize);      // calculate retail mac      byte[] xor = new byte[blockSize]; -    CryptoUtils.xorBlock(paddedMsg, n_1, cbcMac, 0, xor, 0, blockSize); +    CryptoUtils.xorBlock(msg, n_1, cbcMac, 0, xor, 0, blockSize);      Cipher cipher = Cipher.getInstance(cipherAlg+"/ECB/NoPadding", "IAIK");      cipher.init(Cipher.ENCRYPT_MODE, csk);      byte[] retailMac = cipher.doFinal(xor); diff --git a/smccTest/src/main/java/at/gv/egiz/smcc/activation/SecureChannel.java b/smccTest/src/main/java/at/gv/egiz/smcc/activation/SecureChannel.java index 9bbd39fb..0590dac2 100644 --- a/smccTest/src/main/java/at/gv/egiz/smcc/activation/SecureChannel.java +++ b/smccTest/src/main/java/at/gv/egiz/smcc/activation/SecureChannel.java @@ -2,199 +2,352 @@   * To change this template, choose Tools | Templates   * and open the template in the editor.   */ -  package at.gv.egiz.smcc.activation; +import at.gv.egiz.smcc.DNIeCryptoUtil; +import at.gv.egiz.smcc.util.TLVSequence;  import java.nio.ByteBuffer;  import java.security.GeneralSecurityException;  import java.security.InvalidKeyException; +import java.security.MessageDigest;  import java.security.NoSuchAlgorithmException;  import java.security.NoSuchProviderException;  import java.util.Arrays; -import java.util.logging.Level; -import java.util.logging.Logger;  import javax.crypto.spec.SecretKeySpec;  import javax.smartcardio.Card;  import javax.smartcardio.CardChannel;  import javax.smartcardio.CardException;  import javax.smartcardio.CommandAPDU;  import javax.smartcardio.ResponseAPDU; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory;  /**   * + * +00002393 APDU: 00 A4 02 04 02 0E 01 00 +00015613 SW: 62 19 80 02 01 2C C5 02 00 AC 82 01 41 83 02 0E 01 88 00 8A 01 05 A1 03 8B 01 05 90 00 + * 80: num data bytes: 01 2c + * c5: ? + * 82: fd=41 shareable, transparent structure + * 83: fid=0e 01 + * 88: no short fid + * 8a: activated + * a1: security attribute template (proprietary format): 8b 01 05 +00001770 APDU: 00 22 81 A4 06 83 01 81 80 01 54 +00013461 SW: 90 00 +00000592 APDU: 00 84 00 00 08 +00024160 SW: 5B 3D B2 93 3F E8 CE 7C 90 00 +00002153 APDU: 00 82 00 00 68 B1 0C 9A F3 6D FA B0 70 D8 4D 6A 0B 3C E9 8E 5E 50 D0 2A 82 1E CD 70 77 6F CC D4 E8 4D 0E A3 E6 B8 87 2E 31 0F B1 0B 42 5F EB C6 36 B0 EC 18 86 94 0D 5B 67 6C 1A 96 8F C7 2B 8E 4B 85 2A 91 63 C9 E4 66 43 42 D9 55 FF 44 5C C9 DE A6 44 D3 46 37 DA 47 02 A3 63 BA E7 4D CB 52 64 5D F6 B4 94 6B 51 02 0A DD 8F 10 55 00 +00186875 SW: B2 72 5B 23 D2 F6 CB 09 9B 76 1D 62 2E 58 87 24 2A 51 8A 7B F3 79 D8 E9 A3 0B B9 B1 CB 72 77 DD B4 A1 C1 8E 22 5B 0F FA 28 F4 DC 00 B0 D0 96 8F 48 58 85 44 65 7F 11 46 A1 BB DA 17 F2 05 82 23 EC 5E B9 55 92 63 EF 41 A7 BE C8 7E C1 6F 83 82 7A A5 6E AD 94 15 35 37 43 A9 88 89 60 41 A8 87 76 41 EE DB 97 E3 70 CC 90 00 +00003139 APDU: 0C B0 00 00 0D 97 01 DF 8E 08 CE BD FA EC FA B2 C5 D7 00 +00041878 SW: 81 81 AC A8 82 00 A8 B6 16 83 14 80 04 00 00 00 23 00 79 05 03 D0 40 00 00 17 00 12 01 02 00 7F 49 82 00 44 86 40 D4 7C 12 55 E4 7B 0C 7D 4E BB 17 E4 83 E5 3D 56 DF 45 7E 99 CB CC 93 D2 C2 5E 4D 91 27 6E 8B E7 6D 23 53 F6 AB 2E A6 DD B7 1C 68 FB 59 CD D0 45 2B 10 0E 27 00 6E AA 1C 49 90 67 A9 9F 59 D1 97 C1 00 C0 01 80 9E 82 00 40 DE 22 37 4C 41 E0 F7 94 9A 5A E4 76 B8 9B 00 B8 23 7C E9 4A 92 FD B0 FB 25 4A A7 0E 4D 5F 6F 3D 3A 54 28 F8 90 A1 7D 60 28 F8 72 B7 0F 9F A6 A8 53 15 F2 9F 88 37 D4 6B 77 F7 69 C1 B9 E7 2A 43 99 02 62 82 8E 08 25 1C C9 6E 87 22 DD DB 62 82 +   * @author clemens   */  public class SecureChannel extends CardChannel { -    CardChannel basicChannel; - -    SecretKeySpec kenc; -    SecretKeySpec kmac; -    byte[] kencssc; -    byte[] kmacssc; - -    public SecureChannel(CardChannel basicChannel,  -            SecretKeySpec kenc, SecretKeySpec kmac, -            byte[] kencssc, byte[] kmacssc) { -        this.basicChannel = basicChannel; -        this.kenc = kenc; -        this.kmac = kmac; -        this.kencssc = kencssc; -        this.kmacssc = kmacssc; -    } - -     -    @Override -    public Card getCard() { -        return basicChannel.getCard(); -    } - -    @Override -    public int getChannelNumber() { -        return basicChannel.getChannelNumber(); -    } - -    @Override -    public ResponseAPDU transmit(CommandAPDU capdu) throws CardException { -        try { -            kmacssc[7] += 1; -            System.out.println("incrementing kmac_ssc: " + toString(kmacssc)); -            System.out.println("kmac: " + toString(kmac.getEncoded())); -            CommandAPDU capdu_ = new CommandAPDU(protectAPDU(capdu.getBytes())); -            System.out.println(" cmd apdu* " + toString(capdu_.getBytes())); -            return basicChannel.transmit(capdu_); -        } catch (Exception ex) { -            System.out.println("failed to transmit protected APDU: " + ex.getMessage()); -            throw new CardException(ex); -        } -    } - -    @Override -    public int transmit(ByteBuffer bb, ByteBuffer bb1) throws CardException { -        throw new UnsupportedOperationException("Not supported yet."); -    } - -    @Override -    public void close() throws CardException { -        throw new UnsupportedOperationException("Not supported yet."); -    } - -    /** -     * -     * @param apdu -     * @return -     * @throws IllegalArgumentException -     */ -    protected byte[] protectAPDU(byte[] apdu) throws IllegalArgumentException, InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException, GeneralSecurityException { - -        byte[] apdu_ = null; -        byte[] mac_in = null; -        int i_mac = 0; - -        if (apdu.length < 4) { -            throw new IllegalArgumentException("invalid Command APDU " + toString(apdu)); -        } else if (apdu.length == 4) { -            // no command data, no response data, status not protected -            // CLA|INS|P1|P2            -> CLA'|INS|P1|P2|Lc'|TLmac -            apdu_ = new byte[15]; - -            // authenticate header: CLA* b3=1 -            apdu_[0] = (byte) (apdu[0] | (byte) 0x0c); // CLA*: b8-6=000 b4-3=11 -            apdu_[1] = apdu[1]; -            apdu_[2] = apdu[2]; -            apdu_[3] = apdu[3]; -             -            // Lc': TLmac (blocksize=8) -            apdu_[4] = (byte) 0x0a; -            // cryptographic checksum -            apdu_[5] = (byte) 0x8e; -            apdu_[6] = (byte) 0x08; -            i_mac = 7; - -            // 2 data objects: SSC, header -            mac_in = new byte[2*8]; - -            //TODO increment SSC -            System.arraycopy(kmacssc, 0, mac_in, 0, 8); -             -            // CLA** INS P1 P2 padding -            byte[] header_ = RetailCBCMac.pad(Arrays.copyOf(apdu_, 4), 8); -            System.arraycopy(header_, 0, mac_in, 8, 8); - -            System.out.println("data covered by cryptographic checksum: " + toString(mac_in)); -             -        } else if (apdu.length == 5) { -            // CLA|INS|P1|P2|Le         -> CLA'|INS|P1|P2|Lc'|TLle|TLmac|00 -            // no command data, response data -            apdu_ = new byte[19]; - -            // authenticate header: CLA* b3=1 -            apdu_[0] = (byte) (apdu[0] | (byte) 0x0c); // CLA*: b8-6=000 b4-3=11 -            apdu_[1] = apdu[1]; -            apdu_[2] = apdu[2]; -            apdu_[3] = apdu[3]; - -            // Lc': TLle TLmac -            apdu_[4] = (byte) 0x0d; - -            // Ne -            apdu_[5] = (byte) 0x97; -            apdu_[6] = (byte) 0x01; -            apdu_[7] = apdu[4]; - -            // cryptographic checksum -            apdu_[8] = (byte) 0x8e; -            apdu_[9] = (byte) 0x08; -            i_mac = 10; -            apdu_[18] = 0x00; - -            // 3 data objects: SSC, header, Le -            mac_in = new byte[3*8]; - -            //TODO increment SSC -            System.arraycopy(kmacssc, 0, mac_in, 0, 8); - -            // CLA** INS P1 P2 padding -            byte[] header_ = RetailCBCMac.pad(Arrays.copyOf(apdu_, 4), 8); -            System.arraycopy(header_, 0, mac_in, 8, 8); - -            System.arraycopy(RetailCBCMac.pad(Arrays.copyOfRange(apdu_, 5, 8), 8), 0, mac_in, 16, 8); -            System.out.println("data covered by cryptographic checksum: " + toString(mac_in)); - - -        } else if (apdu.length == 5 + apdu[4]){ -            // CLA|INS|P1|P2|Lc|Data    -> CLA'|INS|P1|P2|Lc'|TLplain|TLmac -            // 81 (plain value) 8e (cryptographic checksum) - -        } else { -            // CLA|INS|P1|P2|Lc|Data|Le -> CLA'|INS|P1|P2|Lc'|TLplain|TLle|TLmac|00 -            // 81 (plain value) 97 (Ne) 8e (cryptographic checksum) -             -            // TODO extended APDUs - -        } - - -        byte[] mac = RetailCBCMac.retailMac(mac_in, "DES", "DESede", kmac, 8, 8); -        System.out.println("mac: " + toString(mac)); -        System.arraycopy(mac, 0, apdu_, i_mac, 8); -        return apdu_; - -    } - -    public static String toString(byte[] b) { -        StringBuilder sb = new StringBuilder(); -        sb.append('['); -        if (b != null && b.length > 0) { -            sb.append(Integer.toHexString((b[0] & 240) >> 4)); -            sb.append(Integer.toHexString(b[0] & 15)); -            for (int i = 1; i < b.length; i++) { -                sb.append((i % 32 == 0) ? '\n' : ':'); -                sb.append(Integer.toHexString((b[i] & 240) >> 4)); -                sb.append(Integer.toHexString(b[i] & 15)); -            } -        } -        sb.append(']'); -        return sb.toString(); -    } +	protected static final Logger log = LoggerFactory.getLogger(SecureChannel.class); + +	//TODO access  +	CardChannel channel; +	SecretKeySpec kenc; +	SecretKeySpec kmac; +	byte[] kencssc; +	byte[] kmacssc; +	int blocksize; + +	public SecureChannel(CardChannel basicChannel, +					SecretKeySpec kenc, SecretKeySpec kmac, int blocksize, +					byte[] kencssc, byte[] kmacssc) { +		this.channel = basicChannel; +		this.kenc = kenc; +		this.kmac = kmac; + +		if (kencssc.length != kmacssc.length || kmacssc.length < blocksize) { +			throw new IllegalArgumentException("invalid ssc or blocksize"); +		} +		this.kencssc = kencssc; +		this.kmacssc = kmacssc; +		this.blocksize = blocksize; + +	} + +	@Override +	public Card getCard() { +		return channel.getCard(); +	} + +	@Override +	public int getChannelNumber() { +		return channel.getChannelNumber(); +	} + +	@Override +	public ResponseAPDU transmit(CommandAPDU capdu) throws CardException { + +		byte[] apdu = capdu.getBytes(); + +		try { +			if (apdu.length < 4) { +				throw new IllegalArgumentException("invalid Command APDU " + toString(apdu)); +			} else if (apdu.length == 4) { +				CommandAPDU capdu_ = new CommandAPDU(protectCase1b(apdu)); +				log.info("cmd apdu*: {}", toString(capdu_.getBytes())); +				ResponseAPDU resp_ = channel.transmit(capdu_); +				log.info(" -> resp*: {}", toString(resp_.getBytes())); +				return unProtectCase1b(resp_); +			} else if (apdu.length == 5) { +				CommandAPDU capdu_ = new CommandAPDU(protectCase2(apdu)); +				log.info("cmd apdu*: {}", toString(capdu_.getBytes())); +				ResponseAPDU resp_ = channel.transmit(capdu_); +				log.info(" -> resp*: {}", toString(resp_.getBytes())); +				return unProtectCase2(resp_); + +			} else if (apdu.length == 5 + apdu[4]) { +				// CLA|INS|P1|P2|Lc|Data    -> CLA'|INS|P1|P2|Lc'|TLplain|TLmac +				// 81 (plain value) 8e (cryptographic checksum) +			} else { +				// CLA|INS|P1|P2|Lc|Data|Le -> CLA'|INS|P1|P2|Lc'|TLplain|TLle|TLmac|00 +				// 81 (plain value) 97 (Ne) 8e (cryptographic checksum) +				// TODO extended APDUs +			} + +			throw new UnsupportedOperationException("not implemented"); +			 +		} catch (Exception ex) { +			System.out.println("failed to transmit protected APDU: " + ex.getMessage()); +			throw new CardException(ex); +		} +	} + +	@Override +	public int transmit(ByteBuffer bb, ByteBuffer bb1) throws CardException { +		throw new UnsupportedOperationException("Not supported yet."); +	} + +	@Override +	public void close() throws CardException { +		throw new UnsupportedOperationException("Not supported yet."); +	} + +	/** +	 * Case 1 of command-response pair defined in ISO 7816-3 +	 * no command data, no response data, status not protected +	 * +	 * CLA|INS|P1|P2 -> CLA'|INS|P1|P2|Lc'|TLmac +	 * @param apdu +	 * @return +	 * @throws GeneralSecurityException mac-calculation error +	 */ +	byte[] protectCase1b(byte[] apdu) throws GeneralSecurityException { + +		byte[] apdu_ = new byte[16]; + +		// authenticate header: CLA* b3=1 +		apdu_[0] = (byte) (apdu[0] | (byte) 0x0c); // CLA*: b8-6=000 b4-3=11 +		apdu_[1] = apdu[1]; +		apdu_[2] = apdu[2]; +		apdu_[3] = apdu[3]; + +		// Lc': TLmac +		apdu_[4] = (byte) (blocksize + 2); //0x0a; + +		// cryptographic checksum +		apdu_[5] = (byte) 0x8e; +		apdu_[6] = (byte) blocksize; //0x08; + +		// Le' (case 1b) +		apdu_[7 + blocksize] = 0x00; + +		// 2 data objects: SSC, header +		byte[] mac_in = new byte[2 * blocksize]; + +		// SSC (rightmost blocksize bytes) +		incrementSSC(); +		System.arraycopy(kmacssc, kmacssc.length-blocksize, mac_in, 0, blocksize); + +		// CLA** INS P1 P2 padding +		byte[] paddedHeader = RetailCBCMac.pad(Arrays.copyOf(apdu_, 4), blocksize); +		System.arraycopy(paddedHeader, 0, mac_in, blocksize, blocksize); + +		byte[] mac = RetailCBCMac.retailMac(mac_in, RetailCBCMac.PADDING.NoPadding, +						"DES", "DESede", kmac, blocksize, blocksize); + +		log.debug("cryptographic checksum ({}): {}", toString(mac_in), toString(mac)); + +		// insert mac in 8E object +		System.arraycopy(mac, 0, apdu_, 7, blocksize); +		return apdu_; +	} + +	ResponseAPDU unProtectCase1b(ResponseAPDU resp_) throws GeneralSecurityException, CardException { + +		if (resp_.getSW() == 0x9000) { + +			byte[] respData = resp_.getData(); + +			// T*Lsw1sw2 | TLcc (CC size == blocksize) + +			byte[] mac_in = new byte[blocksize + 4]; + +			// SSC +			incrementSSC(); +			System.arraycopy(kmacssc, kmacssc.length-blocksize, mac_in, 0, blocksize); + +			// data +			System.arraycopy(respData, 0, resp_, blocksize, 4); +			 +			byte[] mac_ = RetailCBCMac.retailMac(mac_in, RetailCBCMac.PADDING.ISO9797_2, +							"DES", "DESede", kmac, blocksize, blocksize); + +			log.debug("cryptographic checksum ({}): {}", toString(mac_in), toString(mac_)); + +			TLVSequence respSeq = new TLVSequence(respData); + +			byte[] cc = respSeq.getValue(0x8e); +			if (!Arrays.equals(mac_, cc)) { +				throw new CardException("invalid cryptographic checksum " + toString(cc)); +			} + +			byte[] sw = respSeq.getValue(0x99); +			if (sw[0] != (byte) 0x90 || sw[1] != (byte) 0x00) { +				throw new CardException("invalid status-word object " + toString(sw)); +			} + +			return new ResponseAPDU(sw); +		} + +		log.info("unexpected response " + toString(resp_.getBytes())); +		return resp_; +	} + +	/** +	 * Case 2 of command-response pair defined in ISO 7816-3 +	 * no command data, response data +	 * +	 * CLA|INS|P1|P2|Le -> CLA'|INS|P1|P2|Lc'|TLle|TLmac|00 +	 * +	 * @param apdu +	 * @return +	 * @throws GeneralSecurityException mac-calculation error +	 */ +	byte[] protectCase2(byte[] apdu) throws GeneralSecurityException { + +		byte[] apdu_ = new byte[19]; +		// authenticate header: CLA** b3=1 +		apdu_[0] = (byte) (apdu[0] | (byte) 0x0c); //CLA**: b8-6=000 b4-3=11 +		apdu_[1] = apdu[1]; +		apdu_[2] = apdu[2]; +		apdu_[3] = apdu[3]; + +		// Lc': TLle TLmac +		apdu_[4] = (byte) (blocksize + 2 + 3); //0x0d; + +		// Ne +		apdu_[5] = (byte) 0x97; +		apdu_[6] = (byte) 0x01; +		apdu_[7] = apdu[4]; + +		// cryptographic checksum +		apdu_[8] = (byte) 0x8e; +		apdu_[9] = (byte) blocksize; //0x08; + +		// Le' +		apdu_[10 + blocksize] = 0x00; + +		// 3 data objects: SSC, header, Le +		byte[] mac_in = new byte[3 * blocksize]; + +		// SSC (rightmost blocksize bytes) +		incrementSSC(); +		System.arraycopy(kmacssc, kmacssc.length-blocksize, mac_in, 0, blocksize); + +		// CLA** INS P1 P2 padding +		byte[] paddedHeader = RetailCBCMac.pad(Arrays.copyOf(apdu_, 4), blocksize); +		System.arraycopy(paddedHeader, 0, mac_in, blocksize, blocksize); + +		// TL Le +		byte[] paddedTLLe = RetailCBCMac.pad(Arrays.copyOfRange(apdu_, 5, 8), blocksize); +		System.arraycopy(paddedTLLe, 0, mac_in, 2*blocksize, blocksize); + +		byte[] mac = RetailCBCMac.retailMac(mac_in, RetailCBCMac.PADDING.NoPadding,  +						"DES", "DESede", kmac, blocksize, blocksize); + +		log.debug("cryptographic checksum ({}): {}", toString(mac_in), toString(mac)); + +		// insert mac in 8E object +		System.arraycopy(mac, 0, apdu_, 10, blocksize); +		return apdu_; +	} + +	ResponseAPDU unProtectCase2(ResponseAPDU resp_) throws GeneralSecurityException, CardException { + +		if (resp_.getSW() == 0x9000) { + +			byte[] respData = resp_.getData(); + +			// T*Lplain | T*Lsw1sw2 | TLcc (CC size == blocksize) + +			int TLccInd = respData.length - 2 - blocksize; + +			byte[] mac_in = new byte[respData.length - 2]; //+blocksize-blocksize + +			// SSC (rightmost blocksize bytes) +			incrementSSC(); +			System.arraycopy(kmacssc, kmacssc.length-blocksize, mac_in, 0, blocksize); + +			// data +			System.arraycopy(respData, 0, mac_in, blocksize, TLccInd); +			 +			byte[] mac_ = RetailCBCMac.retailMac(mac_in, RetailCBCMac.PADDING.ISO9797_2, +							"DES", "DESede", kmac, blocksize, blocksize); + +			log.debug("cryptographic checksum ({}): {}", toString(mac_in), toString(mac_)); + +			TLVSequence respSeq = new TLVSequence(respData); + +			byte[] cc = respSeq.getValue(0x8e); +			if (!Arrays.equals(mac_, cc)) { +				throw new CardException("invalid cryptographic checksum " + toString(cc)); +			} + +			byte[] sw = respSeq.getValue(0x99); +			if (sw[0] != (byte) 0x90 || sw[1] != (byte) 0x00) { +				throw new CardException("invalid status-word object " + toString(sw)); +			} + +			byte[] plain = respSeq.getValue(0x81); +			byte[] resp = new byte[plain.length + 2]; +			System.arraycopy(plain, 0, resp, 0, plain.length); +			resp[resp.length - 2] = (byte) 0x90; +			resp[resp.length - 1] = (byte) 0x00; +			return new ResponseAPDU(resp); +		} + +		log.info("unexpected response " + toString(resp_.getBytes())); +		return resp_; +	} + +	void incrementSSC() { +		//TODO +		kmacssc[7] += 1; +		System.out.println(" [SC] incrementing kmac_ssc: " + toString(kmacssc)); +	} + +	public static String toString(byte[] b) { +		StringBuilder sb = new StringBuilder(); +		sb.append('['); +		if (b != null && b.length > 0) { +			sb.append(Integer.toHexString((b[0] & 240) >> 4)); +			sb.append(Integer.toHexString(b[0] & 15)); +			for (int i = 1; i < b.length; i++) { +				sb.append((i % 32 == 0) ? '\n' : ':'); +				sb.append(Integer.toHexString((b[i] & 240) >> 4)); +				sb.append(Integer.toHexString(b[i] & 15)); +			} +		} +		sb.append(']'); +		return sb.toString(); +	}  } diff --git a/smccTest/src/main/java/at/gv/egiz/smcc/util/TLV.java b/smccTest/src/main/java/at/gv/egiz/smcc/util/TLV.java index b67fe3fa..a724b4b5 100644 --- a/smccTest/src/main/java/at/gv/egiz/smcc/util/TLV.java +++ b/smccTest/src/main/java/at/gv/egiz/smcc/util/TLV.java @@ -39,11 +39,28 @@ public class TLV {      return 0xFF & bytes[start];    } +	public int getLengthFieldLength() { +		if ((bytes[start + 1] & 0x80) > 0) { +			// ISO 7816 allows length fields of up to 5 bytes +			return 1 + (bytes[start + 1] & 0x07); +		} +		return 1; +	} +    /**     * @return the length     */    public int getLength() { -    return 0xFF & bytes[start + 1]; + +		if ((bytes[start + 1] & 0x80) > 0) { +			int length = 0; +			for (int i = 0; i < (bytes[start + 1] & 0x07); i++) { +				length <<= 8; +				length += bytes[start + 2 + i] & 0xff; +			} +			return length; +		} +		return bytes[start + 1] & 0x7f;    }    /** @@ -51,7 +68,7 @@ public class TLV {     */    public byte[] getValue() {      byte[] value = new byte[getLength()]; -    System.arraycopy(bytes, start + 2, value, 0, value.length); +    System.arraycopy(bytes, start + 1 + getLengthFieldLength(), value, 0, value.length);      return value;    } @@ -78,7 +95,7 @@ public class TLV {      sb.append(']');      return sb.toString();    } +	 -    } diff --git a/smccTest/src/main/java/at/gv/egiz/smcc/util/TLVSequence.java b/smccTest/src/main/java/at/gv/egiz/smcc/util/TLVSequence.java index fc29a7e7..b017ec71 100644 --- a/smccTest/src/main/java/at/gv/egiz/smcc/util/TLVSequence.java +++ b/smccTest/src/main/java/at/gv/egiz/smcc/util/TLVSequence.java @@ -66,7 +66,7 @@ public class TLVSequence implements Iterable<TLV> {      public TLV next() {        if (hasNext()) {          TLV tlv = new TLV(bytes, pos); -        pos += tlv.getLength() + 2; +        pos += 1 + tlv.getLengthFieldLength() + tlv.getLength();          return tlv;        } else {          throw new NoSuchElementException(); | 
