Skip to content

Commit

Permalink
added padding option to CryptogramDataBuilder + tests
Browse files Browse the repository at this point in the history
  • Loading branch information
T-eli committed Dec 21, 2023
1 parent cbc2668 commit 0a9f0a4
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
* @author Rainer Reyes
*/
public class CVN10DataBuilder implements CryptogramDataBuilder {

@Override
public String getDefaultARPCRequest(boolean approved) {
return approved ? "0000" : "9900";
Expand All @@ -44,4 +44,9 @@ public String buildARQCRequest(TLVList data, IssuerApplicationData iad) {
sb.append(iad.getCardVerificationResults());
return sb.toString();
}

@Override
public String buildARQCRequest_padded(TLVList data, IssuerApplicationData iad, PaddingMethod paddingMethod){
return paddingMethod.apply(buildARQCRequest(data, iad));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
* @author Rainer Reyes
*/
public class CVN18DataBuilder implements CryptogramDataBuilder {

@Override
public String getDefaultARPCRequest(boolean approved) {
/* for success:
Expand All @@ -47,4 +47,9 @@ public String buildARQCRequest(TLVList data, IssuerApplicationData iad) {
sb.append(iad.toString());
return sb.toString();
}

@Override
public String buildARQCRequest_padded(TLVList data, IssuerApplicationData iad, PaddingMethod paddingMethod) {
return paddingMethod.apply(buildARQCRequest(data, iad));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,14 @@ public String buildARQCRequest(TLVList data, IssuerApplicationData iad) {
return sb.toString();
}

@Override
public String buildARQCRequest_padded(TLVList data, IssuerApplicationData iad, PaddingMethod paddingMethod) {
return paddingMethod.apply(buildARQCRequest(data, iad));
}

@Override
public String getDefaultARPCRequest(boolean approved) {
return approved ? "0012" : "9900";
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,10 @@

import org.jpos.emv.EMVStandardTagType;
import org.jpos.emv.IssuerApplicationData;
import org.jpos.iso.ISOUtil;
import org.jpos.tlv.TLVList;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.*;


/**
Expand All @@ -34,6 +33,22 @@
*/
public interface CryptogramDataBuilder {

final PaddingMethod ZERO_PADDING = data -> data;

/**
* ISO/IEC 9797-1 padding method 1
* for Block size 8, n = 64
*/
final PaddingMethod ISO9797Method1 = data -> data.isEmpty() ?
"0000000000000000" :
ISOUtil.zeropadRight(data, data.length() % 16 == 0 ? data.length() : data.length() + 16 - data.length() % 16);

/**
* ISO/IEC 9797-1 padding method 2
* for Block size 8, n = 64
*/
final PaddingMethod ISO9797Method2 = data -> ISO9797Method1.apply(data + "80");

/**
* Method that selects the minimum set of data elements recommended for
* the generation of application cryptograms described in EMV Book 2 sec 8.1.1
Expand Down Expand Up @@ -67,7 +82,7 @@ static List<String> minimumSetOfDataElement(TLVList data) {
String getDefaultARPCRequest(boolean approved);

/**
* Select necessary data elements and create the string used to generate the ARQC
* Select necessary data elements and create the string used to generate the ARQC with no padding
* <p>
*
* @param data ICC data received
Expand All @@ -76,4 +91,22 @@ static List<String> minimumSetOfDataElement(TLVList data) {
*/
String buildARQCRequest(TLVList data, IssuerApplicationData iad);


/**
* Select necessary data elements and create the string used to generate the ARQC with padding
* <p>
*
* @param data ICC data received
* @param iad Issuer application Data
* @param paddingMethod Padding method to use
* @return String used to generate the ARQC
*/
String buildARQCRequest_padded(TLVList data, IssuerApplicationData iad, PaddingMethod paddingMethod);

/**
* Padding Method Interface
*/
interface PaddingMethod {
String apply(String data);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ void testBuildARQCRequest() {
builder.buildARQCRequest(data, iad)
);

assertEquals(
"00000000010000000000000008400000000000084018123101ABCDEF101800000203000000000000",
builder.buildARQCRequest_padded(data, iad, builder.ISO9797Method1)
);


}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ void testBuildARQCRequest() {
builder.buildARQCRequest(data, iad)
);

assertEquals(
"00000000010000000000000008400000000000084018123101ABCDEF101800000106011203000000",
builder.buildARQCRequest_padded(data, iad, builder.ISO9797Method1)
);


}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package org.jpos.emv.cryptogram;

import org.jpos.iso.ISOUtil;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.util.Arrays;
import java.util.Random;

import static org.jpos.emv.cryptogram.CryptogramDataBuilder.ISO9797Method2;
import static org.jpos.emv.cryptogram.CryptogramDataBuilder.ISO9797Method1;

import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;

/**
* @author Elias Terranti
*/
public class CryptogramDataBuilderTest {

Random r;
byte[] r0;
byte[] r1;
byte[] r2;

@BeforeEach
public void setUp() {
Random r = new Random();

r0 = new byte[0];
r1 = new byte[8];
r2 = new byte[r.nextInt(256)];

r.nextBytes(r0);
r.nextBytes(r1);
r.nextBytes(r2);
}

@Test
void testPaddingMethod2() {
int n = 8; // block size
Arrays.asList(r0, r1, r2).forEach(b -> {

byte[] b_padded = ISOUtil.hex2byte(ISO9797Method2.apply(ISOUtil.hexString(b)));

assertEquals(0, b_padded.length % n);

// if data is empty, padded data should be a block of 1 bit followed by 0s
if (b.length == 0) {
assertArrayEquals(ISOUtil.hex2byte("8000000000000000"), b_padded);
}
// if data length is multiple of n, data is padded with a new block of 1 bit followed by 0s
if (b.length % n == 0 && b.length > 0) {
assertArrayEquals(ISOUtil.concat(b, ISOUtil.hex2byte("8000000000000000")), b_padded);
}

// if data length is not multiple of n, data is padded with 1 bit followed by 0s
if (b.length % n > 0) {
byte[] padding = Arrays.copyOfRange(b_padded, b.length, b_padded.length);
assertEquals(((byte) 0x80), padding[0]);
if (padding.length > 1) {
assertArrayEquals(ISOUtil.concat(new byte[]{(byte) 0x80}, new byte[n - b.length % n - 1]), padding);
}
}
});
}

@Test
void testPaddingMethod1() {
int n = 8; // block size

Arrays.asList(r0, r1, r2).forEach(b -> {
byte[] b_padded = ISOUtil.hex2byte(ISO9797Method1.apply(ISOUtil.hexString(b)));

assertEquals(0, b_padded.length % n);

// if data is empty, padded data should be a block of 0s
if (b.length == 0) {
assertArrayEquals(new byte[n], b_padded);
}

// if data length is multiple of n, no padding is added
if (b.length % n == 0 && b.length > 0) {
assertArrayEquals(b, b_padded);
}

// if data length is not multiple of n, data is padded with 0s
if (b.length % n > 0) {
assertArrayEquals(ISOUtil.concat(b, new byte[n - b.length % n]), b_padded);
}
});
}

}

0 comments on commit 0a9f0a4

Please sign in to comment.