From d09f03aa0d68c9e8f4d3f54fb1ffd34ab6d1f3c4 Mon Sep 17 00:00:00 2001 From: Alejandro Revilla Date: Mon, 8 May 2023 23:00:17 -0300 Subject: [PATCH] KeySerialNumber overhaul The KeySerialNumber class in its current implementation has several issues that need to be addressed. The properties baseID, deviceID, and transactionCounter are currently stored as simple String variables with limited validation. However, the standard requires that the transaction counter is just the rightmost 21 bits of the 10-byte Key Serial Number (KSN). As part of this refactor, the implementation is updated to comply and enforce this standard. Instead of keeping three Strings, we now keep two longs (baseId, deviceId) and an int (transactionCounter). Also added handy methods to get byte representation of those as well as the whole image (getBytes()). --- .../org/jpos/security/KeySerialNumber.java | 221 ++++++++++++------ .../jceadapter/JCESecurityModule.java | 58 +++-- .../org/jpos/security/BaseSMAdapterTest.java | 34 +-- .../jpos/security/KeySerialNumberTest.java | 113 +-------- .../jpos/security/jceadapter/DUKPTTest.java | 145 +++++++----- 5 files changed, 270 insertions(+), 301 deletions(-) diff --git a/jpos/src/main/java/org/jpos/security/KeySerialNumber.java b/jpos/src/main/java/org/jpos/security/KeySerialNumber.java index 8497cc76b8..c08362f44e 100644 --- a/jpos/src/main/java/org/jpos/security/KeySerialNumber.java +++ b/jpos/src/main/java/org/jpos/security/KeySerialNumber.java @@ -23,6 +23,8 @@ import java.io.PrintStream; import java.io.Serializable; +import java.nio.ByteBuffer; +import java.util.Objects; /** @@ -31,31 +33,13 @@ * Transaction) method is used.
* Refer to ANSI X9.24 for more information about DUKPT * @author Hani S. Kirollos - * @version $Revision$ $Date$ * @see EncryptedPIN */ -public class KeySerialNumber - implements Serializable, Loggeable { - - private static final long serialVersionUID = -8388775376202253082L; - /** - * baseKeyID a HexString representing the BaseKeyID (also called KeySet ID) - */ - String baseKeyID; - /** - * deviceID a HexString representing the Device ID (also called TRSM ID) - */ - String deviceID; - /** - * transactionCounter a HexString representing the transaction counter - */ - String transactionCounter; - - /** - * Constructs a key serial number object - */ - public KeySerialNumber () { - } +public class KeySerialNumber implements Serializable, Loggeable { + private static final long serialVersionUID = 5588769944206835776L; + private long baseId; + private long deviceId; + private int transactionCounter; /** * Constructs a key serial number object @@ -64,87 +48,108 @@ public KeySerialNumber () { * @param transactionCounter a HexString representing the transaction counter */ public KeySerialNumber (String baseKeyID, String deviceID, String transactionCounter) { - setBaseKeyID(baseKeyID); - setDeviceID(deviceID); - setTransactionCounter(transactionCounter); + try { + baseKeyID = ISOUtil.padleft(baseKeyID, 10, 'F'); + } catch (Exception e) { + throw new IllegalArgumentException("Invalid baseKeyID."); + } + baseId = Long.parseLong(baseKeyID, 16); + deviceId = Long.parseLong (deviceID, 16); + this.transactionCounter = Integer.parseInt (transactionCounter, 16); } - + /** - * Constructs a key serial number object from its hexadecimal representation. - * @param hexKSN hexadecimal representation of the KSN. - * @param idLength length of the base key ID. - * @param deviceLength length of the device ID. - * @param counterLength length of the transaction counter. + * Constructs a key serial number object from its binary representation. + * @param ksn binary representation of the KSN. */ - public KeySerialNumber(String hexKSN, int idLength, int deviceLength, int counterLength) { - if (hexKSN == null || hexKSN.trim().length() == 0) - throw new IllegalArgumentException("KSN cannot be empty."); - if (idLength + deviceLength + counterLength > hexKSN.length()) - throw new IllegalArgumentException("Length spec doesn't match KSN."); - setBaseKeyID(hexKSN.substring(0, idLength)); - setDeviceID(hexKSN.substring(idLength, idLength + deviceLength)); - setTransactionCounter(hexKSN.substring(idLength + deviceLength, idLength + deviceLength + counterLength)); + public KeySerialNumber(byte[] ksn) { + Objects.requireNonNull (ksn, "KSN cannot be null"); + if (ksn.length < 8 || ksn.length > 10) { + throw new IllegalArgumentException("KSN must be 8 to 10 bytes long."); + } + parseKsn (ksn); } - /** - * Constructs a key serial number object from its binary representation. - * @param binKSN binary representation of the KSN. - * @param idLength length of the base key ID. - * @param deviceLength length of the device ID. - * @param counterLength length of the transaction counter. + * Returns the base key ID as a hexadecimal string padded with leading zeros to a length of 10 characters. + * + * @return a String representing the base key ID. */ - public KeySerialNumber(byte[] binKSN, int idLength, int deviceLength, int counterLength) { - this(ISOUtil.byte2hex(binKSN).toUpperCase(), idLength, deviceLength, counterLength); + public String getBaseKeyID () { + return String.format ("%010X", baseId); } /** - * - * @param baseKeyID a HexString representing the BaseKeyID (also called KeySet ID) + * Returns the base key ID as an array of bytes. + * @return a 5 bytes array representing the base key ID. */ - public void setBaseKeyID (String baseKeyID) { - this.baseKeyID = baseKeyID; + public byte[] getBaseKeyIDBytes () { + ByteBuffer buf = ByteBuffer.allocate(8); + buf.putLong(baseId); + buf.position(3); + byte[] lastFive = new byte[5]; + buf.get(lastFive); + return lastFive; } /** - * - * @return baseKeyID a HexString representing the BaseKeyID (also called KeySet ID) + * Returns the device ID as a hexadecimal string padded with leading zeros to a length of 6 characters. + * @return a String representing the device ID. */ - public String getBaseKeyID () { - return baseKeyID; + public String getDeviceID () { + return String.format ("%06X", deviceId); } /** + * Returns the deviceID as an array of bytes. * - * @param deviceID a HexString representing the Device ID (also called TRSM ID) + * @ return a 3 bytes array representing the deviceID */ - public void setDeviceID (String deviceID) { - this.deviceID = deviceID; + public byte[] getDeviceIDBytes () { + ByteBuffer buf = ByteBuffer.allocate(8); + buf.putLong(deviceId); + buf.position(5); + byte[] lastThree = new byte[3]; + buf.get (lastThree); + return lastThree; } /** + * Returns the transaction counter as a hexadecimal string padded with leading zeros to a length of 6 characters. * - * @return deviceID a HexString representing the Device ID (also called TRSM ID) + * @return a String representing the transaction counter. */ - public String getDeviceID () { - return deviceID; + public String getTransactionCounter () { + return String.format ("%06X", transactionCounter); } /** + * Returns the transaction counter as an array of bytes. * - * @param transactionCounter a HexString representing the transaction counter + * @ return a 3 byte array representing the transaction counter. */ - public void setTransactionCounter (String transactionCounter) { - this.transactionCounter = transactionCounter; + public byte[] getTransactionCounterBytes () { + ByteBuffer buf = ByteBuffer.allocate(4); + buf.putInt(transactionCounter); + buf.position(1); + byte[] lastThree = new byte[3]; + buf.get (lastThree); + return lastThree; } /** + * Constructs a 10-byte Key Serial Number (KSN) array using the base key ID, device ID, and transaction counter. + * The method first extracts the last 5 bytes from the base key ID and device ID (shifted and combined with the + * transaction counter), and then combines them into a single ByteBuffer of size 10. * - * @return transactionCounter a HexString representing the transaction counter + * @return A byte array containing the 10-byte Key Serial Number. */ - public String getTransactionCounter () { - return transactionCounter; + public byte[] getBytes() { + ByteBuffer buf = ByteBuffer.allocate(10); + buf.put (last5(baseId)); + buf.put (last5(deviceId >> 1 << 21 | transactionCounter)); + return buf.array(); } - + /** * dumps Key Serial Number * @param p a PrintStream usually supplied by Logger @@ -154,12 +159,88 @@ public String getTransactionCounter () { public void dump (PrintStream p, String indent) { String inner = indent + " "; p.println(indent + ""); + p.printf ("%s%s%n", inner, ISOUtil.hexString(getBytes())); p.println(inner + "" + getBaseKeyID() + ""); p.println(inner + "" + getDeviceID() + ""); p.println(inner + "" + getTransactionCounter() + ""); } -} + + @Override + public String toString() { + return String.format( + "KeySerialNumber{base=%X, device=%X, counter=%X}", baseId, deviceId, transactionCounter + ); + } + + /** + * Parses a Key Serial Number (KSN) into its base key ID, device ID, and transaction counter components. + * The KSN is first padded to a length of 10 bytes, and then the base key ID, device ID, and transaction counter + * are extracted. + * The base key id has a fixed length of 5 bytes. + * The sequence number has a fixed length of 19 bits. + * The transaction counter has a fixed length of 21 bits per ANS X9.24 spec. + * + * It is important to mention that the device ID is a 19-bit value, which has been shifted one bit to the right + * from its original hexadecimal representation. To facilitate readability and manipulation when reconstructing + * the KSN byte image, the device ID is maintained in a left-shifted position by one bit. + * + * @param ksn The input KSN byte array to be parsed. + * @throws IllegalArgumentException If the base key ID length is smaller than 0 or greater than 8. + */ + private void parseKsn(byte[] ksn) { + ByteBuffer buf = padleft (ksn, 10, (byte) 0xFF); + + byte[] baseKeyIdBytes = new byte[5]; + buf.get(baseKeyIdBytes); + baseId = padleft (baseKeyIdBytes, 8, (byte) 0x00).getLong(); + ByteBuffer sliceCopy = buf.slice().duplicate(); + ByteBuffer remaining = ByteBuffer.allocate(8); + remaining.position(8 - sliceCopy.remaining()); + remaining.put(sliceCopy); + remaining.flip(); + long l = remaining.getLong(); + int mask = (1 << 21) - 1; + transactionCounter = (int) l & mask; + deviceId = l >>> 21 << 1; + } + + /** + * Pads the input byte array with a specified padding byte on the left side to achieve a desired length. + * + * @param b The input byte array to be padded. + * @param len The desired length of the resulting padded byte array. + * @param padbyte The byte value used for padding the input byte array. + * @return A ByteBuffer containing the padded byte array with the specified length. + * @throws IllegalArgumentException If the desired length is smaller than the length of the input byte array. + */ + private ByteBuffer padleft (byte[] b, int len, byte padbyte) { + if (len < b.length) { + throw new IllegalArgumentException("Desired length must be greater than or equal to the length of the input byte array."); + } + ByteBuffer buf = ByteBuffer.allocate(len); + for (int i=0; i 16) sn = sn.substring(sn.length()-16); - byte[] smidr = ISOUtil.hex2byte(sn); - byte[] reg3 = ISOUtil.hex2byte(ksn.getTransactionCounter()); + byte[] smidr = new byte[8]; + System.arraycopy (ksn.getBytes(), 2, smidr, 0, smidr.length); + + byte[] ksnImage = ksn.getBytes(); + byte[] reg3; + if (dataEncryption && ksnImage[0] != (byte) 0xFF && ksnImage[1] != (byte) 0xFF) { + // jPOS 2.x compatibility mode - + reg3 = new byte[5]; + System.arraycopy (ksnImage, 5, reg3, 0, reg3.length); + } else { + reg3 = ksn.getTransactionCounterBytes(); + } reg3 = and(reg3, _1FFFFF); byte[] shiftr = _100000; byte[] temp; @@ -2322,6 +2318,7 @@ private byte[] calculateDerivedKeyTDES(KeySerialNumber ksn, SecureDESKey bdk, bo byte[] curkeyL = new byte[8]; byte[] curkeyR = new byte[8]; smidr = and(smidr, _E00000, 5); + do { temp = and(shiftr, reg3); @@ -2382,13 +2379,10 @@ public SecureDESKey importBDK(String clearComponent1HexString, clearComponent3HexString); } - private KeySerialNumber getKSN(String s) - { - return new KeySerialNumber( - s.substring(0, 6), - s.substring(6, 10), - s.substring(10, Math.min(s.length(), 20)) - ); + private KeySerialNumber getKSN(byte[] b) { + ByteBuffer buf = ByteBuffer.allocate(10); + buf.put(b, 0, 10); + return new KeySerialNumber (buf.array()); } protected EncryptedPIN translatePINImpl diff --git a/jpos/src/test/java/org/jpos/security/BaseSMAdapterTest.java b/jpos/src/test/java/org/jpos/security/BaseSMAdapterTest.java index 7bd4d075d1..826404a30c 100644 --- a/jpos/src/test/java/org/jpos/security/BaseSMAdapterTest.java +++ b/jpos/src/test/java/org/jpos/security/BaseSMAdapterTest.java @@ -215,23 +215,7 @@ public void testImportPINImplThrowsSMException() throws Throwable { assertNull(ex.getNested(), "ex.getNested()"); } } - - @Test - public void testImportPINImplThrowsSMException1() throws Throwable { - BaseSMAdapter baseSMAdapter = new BaseSMAdapter(new SubConfiguration(new SimpleConfiguration(new Properties(null)), - "testBaseSMAdapterPrefix"), new Logger(), "testBaseSMAdapterRealm"); - try { - baseSMAdapter.importPINImpl(new EncryptedPIN("testBaseSMAdapterPinBlockHexString", (byte) 0, - "testBaseSMAdapterAccountNumber"), new KeySerialNumber(), new SecureDESKey((short) 100, - "testBaseSMAdapterKeyType", "testBaseSMAdapterKeyHexString1", "testBaseSMAdapterKeyCheckValueHexString1")); - fail("Expected SMException to be thrown"); - } catch (SMException ex) { - assertEquals("Operation not supported in: org.jpos.security.BaseSMAdapter", ex.getMessage(), "ex.getMessage()"); - assertNull(ex.nested, "ex.nested"); - assertNull(ex.getNested(), "ex.getNested()"); - } - } - + @Test public void testSetConfiguration() throws Throwable { BaseSMAdapter baseSMAdapter = new BaseSMAdapter(); @@ -255,21 +239,7 @@ public void testSetName() throws Throwable { baseSMAdapter.setName("testBaseSMAdapterName"); assertEquals("testBaseSMAdapterName", baseSMAdapter.getName(), "baseSMAdapter.getName()"); } - - @Test - public void testTranslatePINImplThrowsSMException() throws Throwable { - BaseSMAdapter baseSMAdapter = new BaseSMAdapter(); - SecureDESKey bdk = new SecureDESKey(); - try { - baseSMAdapter.translatePINImpl(new EncryptedPIN(), new KeySerialNumber(), bdk, bdk, (byte) 0); - fail("Expected SMException to be thrown"); - } catch (SMException ex) { - assertEquals("Operation not supported in: org.jpos.security.BaseSMAdapter", ex.getMessage(), "ex.getMessage()"); - assertNull(ex.nested, "ex.nested"); - assertNull(ex.getNested(), "ex.getNested()"); - } - } - + @Test public void testTranslatePINImplThrowsSMException1() throws Throwable { BaseSMAdapter baseSMAdapter = new BaseSMAdapter(new SubConfiguration(new SimpleConfiguration(new Properties(null)), diff --git a/jpos/src/test/java/org/jpos/security/KeySerialNumberTest.java b/jpos/src/test/java/org/jpos/security/KeySerialNumberTest.java index 6d2683104b..5050bda712 100644 --- a/jpos/src/test/java/org/jpos/security/KeySerialNumberTest.java +++ b/jpos/src/test/java/org/jpos/security/KeySerialNumberTest.java @@ -34,134 +34,37 @@ public class KeySerialNumberTest { - @Test - public void testConstructor() throws Throwable { - KeySerialNumber keySerialNumber = new KeySerialNumber(); - assertNull(keySerialNumber.getBaseKeyID(), "keySerialNumber.getBaseKeyID()"); - } - - @Test - public void testConstructor1() throws Throwable { - KeySerialNumber keySerialNumber = new KeySerialNumber("testKeySerialNumberBaseKeyID", "testKeySerialNumberDeviceID", - "testKeySerialNumberTransactionCounter"); - assertEquals("testKeySerialNumberBaseKeyID", keySerialNumber.baseKeyID, "keySerialNumber.baseKeyID"); - assertEquals("testKeySerialNumberDeviceID", keySerialNumber.deviceID, "keySerialNumber.deviceID"); - assertEquals("testKeySerialNumberTransactionCounter", - keySerialNumber.transactionCounter, "keySerialNumber.transactionCounter"); - } @Test public void testDump() throws Throwable { PrintStream p = new PrintStream(new ByteArrayOutputStream(), true, "UTF-8"); Object[] objects = new Object[1]; p.format("testKeySerialNumberParam1", objects); - new KeySerialNumber("testKeySerialNumberBaseKeyID", "testKeySerialNumberDeviceID", "testKeySerialNumberTransactionCounter") + new KeySerialNumber("FFFF987654", "3210E", "000008") .dump(p, "testKeySerialNumberIndent"); assertTrue(true, "Test completed without Exception"); } - @Test - public void testDumpThrowsNullPointerException() throws Throwable { - try { - new KeySerialNumber().dump(null, "testKeySerialNumberIndent"); - fail("Expected NullPointerException to be thrown"); - } catch (NullPointerException ex) { - if (isJavaVersionAtMost(JAVA_14)) { - assertNull(ex.getMessage(), "ex.getMessage()"); - } else { - assertEquals("Cannot invoke \"java.io.PrintStream.println(String)\" because \"p\" is null", ex.getMessage(), "ex.getMessage()"); - } - } - } - - @Test - public void testGetBaseKeyID() throws Throwable { - String result = new KeySerialNumber("testKeySerialNumberBaseKeyID", "testKeySerialNumberDeviceID", - "testKeySerialNumberTransactionCounter").getBaseKeyID(); - assertEquals("testKeySerialNumberBaseKeyID", result, "result"); - } - - @Test - public void testGetDeviceID() throws Throwable { - String result = new KeySerialNumber("testKeySerialNumberBaseKeyID", "testKeySerialNumberDeviceID", - "testKeySerialNumberTransactionCounter").getDeviceID(); - assertEquals("testKeySerialNumberDeviceID", result, "result"); - } - - @Test - public void testGetTransactionCounter() throws Throwable { - String result = new KeySerialNumber("testKeySerialNumberBaseKeyID", "testKeySerialNumberDeviceID", - "testKeySerialNumberTransactionCounter").getTransactionCounter(); - assertEquals("testKeySerialNumberTransactionCounter", result, "result"); - } - - @Test - public void testGetTransactionCounter1() throws Throwable { - String result = new KeySerialNumber().getTransactionCounter(); - assertNull(result, "result"); - } - - @Test - public void testSetBaseKeyID() throws Throwable { - KeySerialNumber keySerialNumber = new KeySerialNumber("testKeySerialNumberBaseKeyID", "testKeySerialNumberDeviceID", - "testKeySerialNumberTransactionCounter"); - keySerialNumber.setBaseKeyID("testKeySerialNumberBaseKeyID1"); - assertEquals("testKeySerialNumberBaseKeyID1", keySerialNumber.baseKeyID, "keySerialNumber.baseKeyID"); - } - - @Test - public void testSetDeviceID() throws Throwable { - KeySerialNumber keySerialNumber = new KeySerialNumber("testKeySerialNumberBaseKeyID", "testKeySerialNumberDeviceID", - "testKeySerialNumberTransactionCounter"); - keySerialNumber.setDeviceID("testKeySerialNumberDeviceID1"); - assertEquals("testKeySerialNumberDeviceID1", keySerialNumber.deviceID, "keySerialNumber.deviceID"); - } - - @Test - public void testSetTransactionCounter() throws Throwable { - KeySerialNumber keySerialNumber = new KeySerialNumber("testKeySerialNumberBaseKeyID", "testKeySerialNumberDeviceID", - "testKeySerialNumberTransactionCounter"); - keySerialNumber.setTransactionCounter("testKeySerialNumberTransactionCounter1"); - assertEquals("testKeySerialNumberTransactionCounter1", - keySerialNumber.transactionCounter, "keySerialNumber.transactionCounter"); - } - @Test public void testBinaryConstructor() { byte[] ksnBin = ISOUtil.hex2byte("9876543210E00008"); - KeySerialNumber ksn = new KeySerialNumber(ksnBin, 6, 5, 5); - assertEquals("987654", ksn.getBaseKeyID()); - assertEquals("3210E", ksn.getDeviceID()); - assertEquals("00008", ksn.getTransactionCounter()); - } - - @Test - public void testHexConstructor() { - String ksnHex = "9876543210E00008"; - KeySerialNumber ksn = new KeySerialNumber(ksnHex, 6, 5, 5); - assertEquals("987654", ksn.getBaseKeyID()); - assertEquals("3210E", ksn.getDeviceID()); - assertEquals("00008", ksn.getTransactionCounter()); + KeySerialNumber ksn = new KeySerialNumber(ksnBin); + assertEquals("FFFF987654", ksn.getBaseKeyID()); + assertEquals("03210E", ksn.getDeviceID()); + assertEquals("000008", ksn.getTransactionCounter()); } @Test public void testHexConstructorWrongLength() { assertThrows(IllegalArgumentException.class, () -> { - new KeySerialNumber("9876543210E008", 6, 5, 5); + new KeySerialNumber(ISOUtil.hex2byte("9876543210E008")); }); } @Test public void testHexConstructorNullKSN() { - assertThrows(IllegalArgumentException.class, () -> { - new KeySerialNumber((String) null, 6, 5, 5); - }); - } - - @Test - public void testHexConstructorEmptyKSN() { - assertThrows(IllegalArgumentException.class, () -> { - new KeySerialNumber(" ", 6, 5, 5); + assertThrows(NullPointerException.class, () -> { + new KeySerialNumber(null); }); } } diff --git a/jpos/src/test/java/org/jpos/security/jceadapter/DUKPTTest.java b/jpos/src/test/java/org/jpos/security/jceadapter/DUKPTTest.java index 72dd265fd3..adb868fa09 100644 --- a/jpos/src/test/java/org/jpos/security/jceadapter/DUKPTTest.java +++ b/jpos/src/test/java/org/jpos/security/jceadapter/DUKPTTest.java @@ -72,59 +72,61 @@ public void setUp() throws Exception @Test public void test_DUKPT() throws Exception { - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "E00002"), ISOUtil.hex2byte ("B76997F83C1479DB"), PAN); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "E00003"), ISOUtil.hex2byte ("925BC2A39652CF75"), PAN); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "E00009"), ISOUtil.hex2byte ("8DC939C56D0FD13C"), PAN); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "E0000F"), ISOUtil.hex2byte ("C578B541B9A58A5B"), PAN); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "E00010"), ISOUtil.hex2byte ("6268FFC127118969"), PAN); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "EFF800"), ISOUtil.hex2byte ("A6552D24B01E71A0"), PAN); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "EFFC00"), ISOUtil.hex2byte ("6DEF7FD593810AC7"), PAN); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "F00000"), ISOUtil.hex2byte ("3FAC6F8763C0B60C"), PAN); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "EFF800"), ISOUtil.hex2byte ("A6552D24B01E71A0"), PAN); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "E00002"), ISOUtil.hex2byte ("E6F851D98E8DD722"), PAN2); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "E00003"), ISOUtil.hex2byte ("DE4FF9ABA523F853"), PAN2); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "E00006"), ISOUtil.hex2byte ("148F2CD3554F09F3"), PAN2); + test_DUKPT ("test-bdk", new KeySerialNumber (ISOUtil.hex2byte("FFFF9876543210E00002")), ISOUtil.hex2byte ("B76997F83C1479DB"), PAN); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "00002"), ISOUtil.hex2byte ("B76997F83C1479DB"), PAN); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "00003"), ISOUtil.hex2byte ("925BC2A39652CF75"), PAN); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "00009"), ISOUtil.hex2byte ("8DC939C56D0FD13C"), PAN); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "0000F"), ISOUtil.hex2byte ("C578B541B9A58A5B"), PAN); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "00010"), ISOUtil.hex2byte ("6268FFC127118969"), PAN); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "FF800"), ISOUtil.hex2byte ("A6552D24B01E71A0"), PAN); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "FFC00"), ISOUtil.hex2byte ("6DEF7FD593810AC7"), PAN); + test_DUKPT ("test-bdk", new KeySerialNumber (ISOUtil.hex2byte("FFFF9876543210F00000")), ISOUtil.hex2byte ("3FAC6F8763C0B60C"), PAN); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "100000"), ISOUtil.hex2byte ("3FAC6F8763C0B60C"), PAN); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210F", "FF800"), ISOUtil.hex2byte ("A6552D24B01E71A0"), PAN); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "00002"), ISOUtil.hex2byte ("E6F851D98E8DD722"), PAN2); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "00003"), ISOUtil.hex2byte ("DE4FF9ABA523F853"), PAN2); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "00006"), ISOUtil.hex2byte ("148F2CD3554F09F3"), PAN2); // Test 3DES - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "E00001"), ISOUtil.hex2byte ("1B9C1845EB993A7A"), PAN,true); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "E00002"), ISOUtil.hex2byte ("10A01C8D02C69107"), PAN,true); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "E00003"), ISOUtil.hex2byte ("18DC07B94797B466"), PAN,true); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "E00004"), ISOUtil.hex2byte ("0BC79509D5645DF7"), PAN,true); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "E00005"), ISOUtil.hex2byte ("5BC0AF22AD87B327"), PAN,true); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "E00006"), ISOUtil.hex2byte ("A16DF70AE36158D8"), PAN,true); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "E00007"), ISOUtil.hex2byte ("27711C16CB257F8E"), PAN,true); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "E00008"), ISOUtil.hex2byte ("50E55547A5027551"), PAN,true); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "E00009"), ISOUtil.hex2byte ("536CF7F678ACFC8D"), PAN,true); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "E0000A"), ISOUtil.hex2byte ("EDABBA23221833FE"), PAN,true); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "E0000B"), ISOUtil.hex2byte ("2328981C57B4BDBA"), PAN,true); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "E0000C"), ISOUtil.hex2byte ("038D03CC926CF286"), PAN,true); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "E0000D"), ISOUtil.hex2byte ("6C8AA97088B62C68"), PAN,true); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "E0000E"), ISOUtil.hex2byte ("F17C9E1D72CD4950"), PAN,true); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "E0000F"), ISOUtil.hex2byte ("B170F6E7F7F2F64A"), PAN,true); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "E00010"), ISOUtil.hex2byte ("D5D9638559EF53D6"), PAN,true); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "E00011"), ISOUtil.hex2byte ("D544F8CDD292C863"), PAN,true); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "E00012"), ISOUtil.hex2byte ("7A21BD10F36DC41D"), PAN,true); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "E00013"), ISOUtil.hex2byte ("78649BD17D0DFA60"), PAN,true); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "E00014"), ISOUtil.hex2byte ("7E7E16EA0C31AD56"), PAN,true); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "E00015"), ISOUtil.hex2byte ("72105C22EBC791E6"), PAN,true); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "EFF800"), ISOUtil.hex2byte ("33365F5CC6F23C35"), PAN,true); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "EFF801"), ISOUtil.hex2byte ("3A86BF003F835C9D"), PAN,true); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "EFF802"), ISOUtil.hex2byte ("3DB977D05C36DF3F"), PAN,true); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "EFF804"), ISOUtil.hex2byte ("BA83243305712099"), PAN,true); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "EFF808"), ISOUtil.hex2byte ("B0DA04AC90A36D85"), PAN,true); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "EFF810"), ISOUtil.hex2byte ("2CF02BD9C309EEDA"), PAN,true); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "EFF820"), ISOUtil.hex2byte ("9D1E2F77AEEE81C6"), PAN,true); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "EFF840"), ISOUtil.hex2byte ("40870B0F8BA2011C"), PAN,true); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "EFF880"), ISOUtil.hex2byte ("22E340D6ABB40981"), PAN,true); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "EFF900"), ISOUtil.hex2byte ("1A4C10AFBA03A430"), PAN,true); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "EFFA00"), ISOUtil.hex2byte ("849763B43E5F9CFF"), PAN,true); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "EFFC00"), ISOUtil.hex2byte ("DEFC6F09F8927B71"), PAN,true); - test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210", "F00000"), ISOUtil.hex2byte ("73EC88AD0AC5830E"), PAN,true); - test_DUKPT ("test-bdk", new KeySerialNumber ("9876543210", "0000", "400002"), ISOUtil.hex2byte ("AEF0F261B1222EB1"), PAN,true); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "00001"), ISOUtil.hex2byte ("1B9C1845EB993A7A"), PAN,true); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "00002"), ISOUtil.hex2byte ("10A01C8D02C69107"), PAN,true); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "00003"), ISOUtil.hex2byte ("18DC07B94797B466"), PAN,true); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "00004"), ISOUtil.hex2byte ("0BC79509D5645DF7"), PAN,true); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "00005"), ISOUtil.hex2byte ("5BC0AF22AD87B327"), PAN,true); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "00006"), ISOUtil.hex2byte ("A16DF70AE36158D8"), PAN,true); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "00007"), ISOUtil.hex2byte ("27711C16CB257F8E"), PAN,true); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "00008"), ISOUtil.hex2byte ("50E55547A5027551"), PAN,true); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "00009"), ISOUtil.hex2byte ("536CF7F678ACFC8D"), PAN,true); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "0000A"), ISOUtil.hex2byte ("EDABBA23221833FE"), PAN,true); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "0000B"), ISOUtil.hex2byte ("2328981C57B4BDBA"), PAN,true); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "0000C"), ISOUtil.hex2byte ("038D03CC926CF286"), PAN,true); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "0000D"), ISOUtil.hex2byte ("6C8AA97088B62C68"), PAN,true); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "0000E"), ISOUtil.hex2byte ("F17C9E1D72CD4950"), PAN,true); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "0000F"), ISOUtil.hex2byte ("B170F6E7F7F2F64A"), PAN,true); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "00010"), ISOUtil.hex2byte ("D5D9638559EF53D6"), PAN,true); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "00011"), ISOUtil.hex2byte ("D544F8CDD292C863"), PAN,true); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "00012"), ISOUtil.hex2byte ("7A21BD10F36DC41D"), PAN,true); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "00013"), ISOUtil.hex2byte ("78649BD17D0DFA60"), PAN,true); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "00014"), ISOUtil.hex2byte ("7E7E16EA0C31AD56"), PAN,true); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "00015"), ISOUtil.hex2byte ("72105C22EBC791E6"), PAN,true); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "FF800"), ISOUtil.hex2byte ("33365F5CC6F23C35"), PAN,true); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "FF801"), ISOUtil.hex2byte ("3A86BF003F835C9D"), PAN,true); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "FF802"), ISOUtil.hex2byte ("3DB977D05C36DF3F"), PAN,true); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "FF804"), ISOUtil.hex2byte ("BA83243305712099"), PAN,true); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "FF808"), ISOUtil.hex2byte ("B0DA04AC90A36D85"), PAN,true); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "FF810"), ISOUtil.hex2byte ("2CF02BD9C309EEDA"), PAN,true); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "FF820"), ISOUtil.hex2byte ("9D1E2F77AEEE81C6"), PAN,true); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "FF840"), ISOUtil.hex2byte ("40870B0F8BA2011C"), PAN,true); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "FF880"), ISOUtil.hex2byte ("22E340D6ABB40981"), PAN,true); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "FF900"), ISOUtil.hex2byte ("1A4C10AFBA03A430"), PAN,true); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "FFA00"), ISOUtil.hex2byte ("849763B43E5F9CFF"), PAN,true); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "FFC00"), ISOUtil.hex2byte ("DEFC6F09F8927B71"), PAN,true); + test_DUKPT ("test-bdk", new KeySerialNumber ("987654", "3210E", "100000"), ISOUtil.hex2byte ("73EC88AD0AC5830E"), PAN,true); + test_DUKPT ("test-bdk", new KeySerialNumber ("9876543210", "00004", "00002"), ISOUtil.hex2byte ("AEF0F261B1222EB1"), PAN,true); } @Test public void test_dataEncrypt() throws Exception { - SecureDESKey bdk = (SecureDESKey) ks.getKey("test-bdk"); + SecureDESKey bdk = ks.getKey("test-bdk"); byte[] original = "The quick brown fox jumps over the lazy dog".getBytes(); byte[] cryptogram = sm.dataEncrypt(bdk, original); byte[] cleartext = sm.dataDecrypt(bdk, cryptogram); @@ -136,6 +138,21 @@ public void test_dataEncrypt() throws Exception { } catch (Exception ignored) { } } + @Test + public void test_dataDecryptCompatibility() throws Exception { + SecureDESKey bdk = ks.getKey("test-bdk"); + byte[] original = "The quick brown fox jumps over the lazy dog".getBytes(); + // cryptogram was created with jPOS 2.1.9-SNAPSHOT master/afb3977 previous to KeySerialNumber class overhaul + byte[] cryptogram = ISOUtil.hex2byte("CE1CF4085E1AA8348F1316F8FD641CB3CE1CF4085E1AA8346FB168F8A730914FFCA386A289F185EF50696A13B994AD6CF373FEC56C1E8C25137D0A560FCF57630C5100F89E7BA6FCC3B7DDC86E87A495"); + byte[] cleartext = sm.dataDecrypt(bdk, cryptogram); + assertEqual(original, cleartext); + cryptogram[0] = (byte) (cryptogram[0] ^ 0xAA); + try { + sm.dataDecrypt(bdk, cryptogram); + fail("SMException not raised"); + } catch (Exception ignored) { } + } + private void test_DUKPT(String keyName, KeySerialNumber ksn, byte[] pinUnderDukpt, String pan) throws Exception { @@ -147,20 +164,24 @@ private void test_DUKPT(String keyName, KeySerialNumber ksn, byte[] pinUnderDukp { LogEvent evt = log.createInfo("test_DUKPT " + ksn); evt.addMessage(ksn); - EncryptedPIN pin = new EncryptedPIN( - pinUnderDukpt, SMAdapter.FORMAT01, pan - ); - SecureDESKey bdk = ks.getKey(keyName); - evt.addMessage(pin); - evt.addMessage(ksn); - evt.addMessage(bdk); - - EncryptedPIN pinUnderLMK = sm.importPIN(pin, ksn, bdk,tdes); - evt.addMessage(pinUnderLMK); - evt.addMessage( - "" + sm.decryptPIN(pinUnderLMK) + "" - ); - Logger.log(evt); + try { + EncryptedPIN pin = new EncryptedPIN( + pinUnderDukpt, SMAdapter.FORMAT01, pan + ); + SecureDESKey bdk = ks.getKey(keyName); + evt.addMessage(pin); + evt.addMessage(ksn); + evt.addMessage(bdk); + + EncryptedPIN pinUnderLMK = sm.importPIN(pin, ksn, bdk,tdes); + evt.addMessage(pinUnderLMK); + evt.addMessage( + "" + sm.decryptPIN(pinUnderLMK) + "" + ); + + } finally { + Logger.log (evt); + } } private void initKS() throws Exception