From 58ebbe700d922af33b79886f715ee302fb62c523 Mon Sep 17 00:00:00 2001 From: clemenso Date: Fri, 28 Jan 2011 17:22:04 +0000 Subject: Read PuK_QS (secure messaging case1b/2) git-svn-id: https://joinup.ec.europa.eu/svn/mocca/trunk@896 8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4 --- .../at/gv/egiz/smcc/activation/Activation.java | 113 ++++- .../at/gv/egiz/smcc/activation/RetailCBCMac.java | 22 +- .../at/gv/egiz/smcc/activation/SecureChannel.java | 501 ++++++++++++++------- .../src/main/java/at/gv/egiz/smcc/util/TLV.java | 23 +- .../java/at/gv/egiz/smcc/util/TLVSequence.java | 2 +- 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 { 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(); -- cgit v1.2.3