/*
* The MIT License (MIT)
*
* Copyright (c) 2017 Bertrand Martel
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package fr.bmartel.aram;
import fr.bmartel.aram.util.TestUtils;
import javacard.framework.ISO7816;
import org.junit.Before;
import org.junit.Test;
import pro.javacard.gp.GPDataException;
import pro.javacard.gp.SEAccessControl;
import javax.smartcardio.CardException;
import javax.smartcardio.CommandAPDU;
import javax.smartcardio.ResponseAPDU;
import java.util.List;
import static org.junit.Assert.*;
/**
* Testing ARAM applet.
*
* @author Bertrand Martel
*/
public class AramTest extends JavaCardTest {
private void sendGetAll(byte[] data, int expectedSw, byte[] expectedResponse) throws CardException {
TestUtils.sendCmdBatch(this, AramConstTest.CMD_GET_ALL, data, expectedSw, expectedResponse);
}
private void sendGetNext(byte[] data, int expectedSw, byte[] expectedResponse) throws CardException {
TestUtils.sendCmdBatch(this, AramConstTest.CMD_GET_NEXT, data, expectedSw, expectedResponse);
}
private ResponseAPDU sendGetRefreshTag(byte[] data, int expectedSw) throws CardException {
return TestUtils.sendCmdBatch(this, AramConstTest.CMD_GET_REFRESH_TAG, data, expectedSw);
}
private void deleteAllRules() throws CardException, GPDataException {
CommandAPDU commandAPDU = new CommandAPDU(TestUtils.buildApdu(AramConstTest.CMD_GET_ALL, new byte[]{}));
ResponseAPDU response = this.transmitCommand(commandAPDU);
SEAccessControl.BerTlvData temp = SEAccessControl.AcrListResponse.getAcrListData(null, response.getData());
while (temp.getCurrentIndex() < temp.getLength()) {
commandAPDU = new CommandAPDU(TestUtils.buildApdu(AramConstTest.CMD_GET_NEXT, new byte[]{}));
response = this.transmitCommand(commandAPDU);
temp = SEAccessControl.AcrListResponse.getAcrListData(temp, response.getData());
}
List resp = SEAccessControl.AcrListResponse.fromBytes(temp.getLength(), temp.getData()).acrList;
for (int i = 0; i < resp.size(); i++) {
deleteData(resp.get(i).getBytes());
}
sendGetAll(new byte[]{}, 0x9000, AramConstTest.GET_DATA_EMPTY_RESPONSE);
}
@Before
public void initTest() throws NoSuchFieldException, IllegalAccessException, CardException, GPDataException {
TestSuite.setup();
deleteAllRules();
}
@Test
public void wrongClass() throws CardException {
TestUtils.sendCmdBatch(this, AramConstTest.CMD_WRONG_CLA, new byte[]{}, ISO7816.SW_CLA_NOT_SUPPORTED, new byte[]{});
}
@Test
public void wrongInstruction() throws CardException {
TestUtils.sendCmdBatch(this, AramConstTest.CMD_WRONG_INS, new byte[]{}, ISO7816.SW_INS_NOT_SUPPORTED, new byte[]{});
}
@Test
public void wrongP1P2GetData() throws CardException {
TestUtils.sendCmdBatch(this, AramConstTest.CMD_WRONG_P1P2_GET_DATA, new byte[]{}, ISO7816.SW_INCORRECT_P1P2, new byte[]{});
}
@Test
public void getAllEmptyTest() throws CardException {
sendGetAll(new byte[]{}, 0x9000, AramConstTest.GET_DATA_EMPTY_RESPONSE);
}
@Test
public void wrongP1P2StoreData() throws CardException {
TestUtils.sendCmdBatch(this, AramConstTest.CMD_WRONG_P1P2_STORE_DATA, new byte[]{}, ISO7816.SW_INCORRECT_P1P2, new byte[]{});
}
@Test
public void storeDataWrongStore() throws CardException {
TestUtils.sendCmdBatch(this,
TestUtils.concatByteArray(AramConstTest.CMD_STORE_HEADER,
new byte[]{(byte) AramConstTest.CMD_STORE_DATA_INVALID_CMD_STORE.length},
AramConstTest.CMD_STORE_DATA_INVALID_CMD_STORE),
new byte[]{},
ISO7816.SW_DATA_INVALID,
new byte[]{});
}
@Test
public void storeDataWrongRefArDo() throws CardException {
TestUtils.sendCmdBatch(this,
TestUtils.concatByteArray(AramConstTest.CMD_STORE_HEADER,
new byte[]{(byte) AramConstTest.CMD_STORE_DATA_INVALID_REFARDO.length},
AramConstTest.CMD_STORE_DATA_INVALID_REFARDO),
new byte[]{},
ISO7816.SW_DATA_INVALID,
new byte[]{});
}
@Test
public void storeDataWrongRefDo() throws CardException {
TestUtils.sendCmdBatch(this,
TestUtils.concatByteArray(AramConstTest.CMD_STORE_HEADER,
new byte[]{(byte) AramConstTest.CMD_STORE_DATA_INVALID_REFDO.length},
AramConstTest.CMD_STORE_DATA_INVALID_REFDO),
new byte[]{},
ISO7816.SW_DATA_INVALID,
new byte[]{});
}
@Test
public void storeDataWrongAidRefDo() throws CardException {
TestUtils.sendCmdBatch(this,
TestUtils.concatByteArray(AramConstTest.CMD_STORE_HEADER,
new byte[]{(byte) AramConstTest.CMD_STORE_DATA_INVALID_AIDREFDO.length},
AramConstTest.CMD_STORE_DATA_INVALID_AIDREFDO),
new byte[]{},
ISO7816.SW_DATA_INVALID,
new byte[]{});
}
@Test
public void storeDataWrongHashRefDo() throws CardException {
TestUtils.sendCmdBatch(this,
TestUtils.concatByteArray(AramConstTest.CMD_STORE_HEADER,
new byte[]{(byte) AramConstTest.CMD_STORE_DATA_INVALID_HASHREFDO.length},
AramConstTest.CMD_STORE_DATA_INVALID_HASHREFDO),
new byte[]{},
ISO7816.SW_DATA_INVALID,
new byte[]{});
}
@Test
public void storeDataWrongArDo() throws CardException {
TestUtils.sendCmdBatch(this,
TestUtils.concatByteArray(AramConstTest.CMD_STORE_HEADER,
new byte[]{(byte) AramConstTest.CMD_STORE_DATA_INVALID_ARDO.length},
AramConstTest.CMD_STORE_DATA_INVALID_ARDO),
new byte[]{},
ISO7816.SW_DATA_INVALID,
new byte[]{});
}
private void storeData(byte[] refArDo) throws CardException {
byte[] request = TestUtils.concatByteArray(new byte[]{
//F0 = Command-Store-AR-DO
(byte) 0xF0, (byte) refArDo.length
}, refArDo);
TestUtils.sendCmdBatch(this,
TestUtils.concatByteArray(AramConstTest.CMD_STORE_HEADER,
new byte[]{(byte) request.length},
request),
new byte[]{},
0x9000,
new byte[]{});
}
private void updateRefreshTag() throws CardException {
byte[] request = new byte[]{
//F0 = Command-Store-AR-DO
(byte) 0xF2, (byte) 0
};
TestUtils.sendCmdBatch(this,
TestUtils.concatByteArray(AramConstTest.CMD_STORE_HEADER,
new byte[]{(byte) request.length},
request),
new byte[]{},
0x9000,
new byte[]{});
}
private ResponseAPDU getSpecific(byte[] refDo, byte[] expected) throws CardException {
byte[] request = TestUtils.concatByteArray(new byte[]{(byte) refDo.length}, refDo);
return TestUtils.sendCmdBatch(this,
TestUtils.concatByteArray(AramConstTest.CMD_GET_SPECIFIC,
new byte[]{(byte) request.length},
request),
new byte[]{}, 0x9000, expected);
}
private void deleteData(byte[] refArDo) throws CardException {
byte[] request = TestUtils.concatByteArray(new byte[]{
//F1 = Command-Delete-AR-DO
(byte) 0xF1, (byte) refArDo.length
}, refArDo);
TestUtils.sendCmdBatch(this,
TestUtils.concatByteArray(AramConstTest.CMD_STORE_HEADER,
new byte[]{(byte) request.length},
request),
new byte[]{},
0x9000,
new byte[]{});
}
@Test
public void storeDataValid() throws CardException {
storeData(AramConstTest.VALID_REF_AR_DO);
sendGetAll(new byte[]{}, 0x9000, TestUtils.checkList(0, AramConstTest.VALID_REF_AR_DO));
}
@Test
public void storeDataMultiple() throws CardException {
storeData(AramConstTest.VALID_REF_AR_DO);
storeData(AramConstTest.VALID_REF_AR_DO1);
sendGetAll(new byte[]{}, 0x9000, TestUtils.checkList(0, TestUtils.concatByteArray(AramConstTest.VALID_REF_AR_DO1, AramConstTest.VALID_REF_AR_DO)));
}
@Test
public void storeDataMultipleNextLength2() throws CardException {
storeData(AramConstTest.VALID_REF_AR_DO);
storeData(AramConstTest.VALID_REF_AR_DO1);
storeData(AramConstTest.VALID_REF_AR_DO2);
storeData(AramConstTest.VALID_REF_AR_DO3);
storeData(AramConstTest.VALID_REF_AR_DO4);
sendGetAll(new byte[]{}, 0x9000, TestUtils.checkList(0, TestUtils.concatByteArray(AramConstTest.VALID_REF_AR_DO4, AramConstTest.VALID_REF_AR_DO3, AramConstTest.VALID_REF_AR_DO2, AramConstTest.VALID_REF_AR_DO1, AramConstTest.VALID_REF_AR_DO)));
}
@Test
public void storeDataMultipleNextLength3() throws CardException {
storeData(AramConstTest.VALID_REF_AR_DO);
storeData(AramConstTest.VALID_REF_AR_DO1);
storeData(AramConstTest.VALID_REF_AR_DO2);
storeData(AramConstTest.VALID_REF_AR_DO3);
storeData(AramConstTest.VALID_REF_AR_DO4);
storeData(AramConstTest.VALID_REF_AR_DO5);
sendGetAll(new byte[]{}, 0x9000, TestUtils.checkList(0, TestUtils.concatByteArray(AramConstTest.VALID_REF_AR_DO5, AramConstTest.VALID_REF_AR_DO4, AramConstTest.VALID_REF_AR_DO3, AramConstTest.VALID_REF_AR_DO2, AramConstTest.VALID_REF_AR_DO1, AramConstTest.VALID_REF_AR_DO)));
}
@Test
public void storeDataMultipleNextLength1next() throws CardException {
storeData(AramConstTest.VALID_REF_AR_DO);
storeData(AramConstTest.VALID_REF_AR_DO1);
storeData(AramConstTest.VALID_REF_AR_DO2);
storeData(AramConstTest.VALID_REF_AR_DO3);
storeData(AramConstTest.VALID_REF_AR_DO4);
storeData(AramConstTest.VALID_REF_AR_DO5);
storeData(AramConstTest.VALID_REF_AR_DO6);
storeData(AramConstTest.VALID_REF_AR_DO7);
byte[] expectedData = TestUtils.concatByteArray(AramConstTest.VALID_REF_AR_DO7, AramConstTest.VALID_REF_AR_DO6, AramConstTest.VALID_REF_AR_DO5, AramConstTest.VALID_REF_AR_DO4, AramConstTest.VALID_REF_AR_DO3, AramConstTest.VALID_REF_AR_DO2, AramConstTest.VALID_REF_AR_DO1, AramConstTest.VALID_REF_AR_DO);
sendGetAll(new byte[]{}, 0x9000, TestUtils.checkList(0, expectedData));
sendGetNext(new byte[]{}, 0x9000, TestUtils.checkList(1, expectedData));
sendGetNext(new byte[]{}, 0x6A88, new byte[]{});
}
@Test
public void storeDataMultipleNextLength2next() throws CardException {
storeData(AramConstTest.VALID_REF_AR_DO);
storeData(AramConstTest.VALID_REF_AR_DO1);
storeData(AramConstTest.VALID_REF_AR_DO2);
storeData(AramConstTest.VALID_REF_AR_DO3);
storeData(AramConstTest.VALID_REF_AR_DO4);
storeData(AramConstTest.VALID_REF_AR_DO5);
storeData(AramConstTest.VALID_REF_AR_DO6);
storeData(AramConstTest.VALID_REF_AR_DO7);
storeData(AramConstTest.VALID_REF_AR_DO8);
storeData(AramConstTest.VALID_REF_AR_DO9);
storeData(AramConstTest.VALID_REF_AR_DO10);
byte[] expectedData = TestUtils.concatByteArray(AramConstTest.VALID_REF_AR_DO10, AramConstTest.VALID_REF_AR_DO9, AramConstTest.VALID_REF_AR_DO8, AramConstTest.VALID_REF_AR_DO7, AramConstTest.VALID_REF_AR_DO6, AramConstTest.VALID_REF_AR_DO5, AramConstTest.VALID_REF_AR_DO4, AramConstTest.VALID_REF_AR_DO3, AramConstTest.VALID_REF_AR_DO2, AramConstTest.VALID_REF_AR_DO1, AramConstTest.VALID_REF_AR_DO);
sendGetAll(new byte[]{}, 0x9000, TestUtils.checkList(0, expectedData));
sendGetNext(new byte[]{}, 0x9000, TestUtils.checkList(1, expectedData));
sendGetNext(new byte[]{}, 0x9000, TestUtils.checkList(2, expectedData));
sendGetNext(new byte[]{}, 0x6A88, new byte[]{});
}
@Test
public void nextDataNotFound() throws CardException {
sendGetNext(new byte[]{}, 0x6A88, new byte[]{});
}
@Test
public void nextDataSingleNextLengthRequest() throws CardException, IllegalAccessException, NoSuchFieldException, GPDataException {
storeData(AramConstTest.VALID_REF_AR_DO);
storeData(AramConstTest.VALID_REF_AR_DO1);
storeData(AramConstTest.VALID_REF_AR_DO2);
storeData(AramConstTest.VALID_REF_AR_DO3);
storeData(AramConstTest.VALID_REF_AR_DO4);
storeData(AramConstTest.VALID_REF_AR_DO5);
byte[] expectedData = TestUtils.concatByteArray(AramConstTest.VALID_REF_AR_DO5, AramConstTest.VALID_REF_AR_DO4, AramConstTest.VALID_REF_AR_DO3, AramConstTest.VALID_REF_AR_DO2, AramConstTest.VALID_REF_AR_DO1, AramConstTest.VALID_REF_AR_DO);
sendGetAll(new byte[]{}, 0x9000, TestUtils.checkList(0, expectedData));
sendGetNext(new byte[]{}, 0x9000, TestUtils.checkList(1, expectedData));
sendGetNext(new byte[]{}, 0x6A88, new byte[]{});
}
@Test
public void deleteAll() throws CardException {
storeData(AramConstTest.VALID_REF_AR_DO);
storeData(AramConstTest.VALID_REF_AR_DO_CLONE);
deleteData(new byte[]{});
sendGetAll(new byte[]{}, 0x9000, AramConstTest.GET_DATA_EMPTY_RESPONSE);
}
@Test
public void deleteByAid() throws CardException {
storeData(AramConstTest.VALID_REF_AR_DO);
sendGetAll(new byte[]{}, 0x9000, TestUtils.checkList(0, AramConstTest.VALID_REF_AR_DO));
deleteData(AramConstTest.AID_REF_DO);
sendGetAll(new byte[]{}, 0x9000, AramConstTest.GET_DATA_EMPTY_RESPONSE);
}
@Test
public void deleteMultipleSameAid() throws CardException {
storeData(AramConstTest.VALID_REF_AR_DO);
storeData(AramConstTest.VALID_REF_AR_DO_CLONE);
sendGetAll(new byte[]{}, 0x9000, TestUtils.checkList(0, TestUtils.concatByteArray(AramConstTest.VALID_REF_AR_DO_CLONE, AramConstTest.VALID_REF_AR_DO)));
deleteData(AramConstTest.AID_REF_DO);
sendGetAll(new byte[]{}, 0x9000, AramConstTest.GET_DATA_EMPTY_RESPONSE);
}
@Test
public void deleteByAidHash() throws CardException {
storeData(AramConstTest.VALID_REF_AR_DO);
sendGetAll(new byte[]{}, 0x9000, TestUtils.checkList(0, AramConstTest.VALID_REF_AR_DO));
deleteData(AramConstTest.REF_DO);
sendGetAll(new byte[]{}, 0x9000, AramConstTest.GET_DATA_EMPTY_RESPONSE);
}
@Test
public void deleteByAidHashRule() throws CardException {
storeData(AramConstTest.VALID_REF_AR_DO);
sendGetAll(new byte[]{}, 0x9000, TestUtils.checkList(0, AramConstTest.VALID_REF_AR_DO));
deleteData(AramConstTest.VALID_REF_AR_DO);
sendGetAll(new byte[]{}, 0x9000, AramConstTest.GET_DATA_EMPTY_RESPONSE);
}
@Test
public void deleteByAidHashRuleEmpty() throws CardException {
storeData(AramConstTest.VALID_REF_AR_DO);
sendGetAll(new byte[]{}, 0x9000, TestUtils.checkList(0, AramConstTest.VALID_REF_AR_DO));
deleteData(AramConstTest.VALID_REF_AR_DO_EMPTY);
sendGetAll(new byte[]{}, 0x9000, AramConstTest.GET_DATA_EMPTY_RESPONSE);
}
@Test
public void refreshTag() throws CardException {
byte[] req1 = sendGetRefreshTag(new byte[]{}, 0x9000).getData();
assertEquals(11, req1.length);
assertArrayEquals(new byte[]{(byte) 0xDF,0x20}, new byte[]{req1[0],req1[1]});
assertEquals(8, req1[2]);
byte[] req2 = sendGetRefreshTag(new byte[]{}, 0x9000).getData();
assertArrayEquals(req1, req2);
updateRefreshTag();
byte[] req3 = sendGetRefreshTag(new byte[]{}, 0x9000).getData();
assertNotEquals(req3, req2);
}
@Test
public void getSpecificArDo() throws CardException {
storeData(AramConstTest.VALID_REF_AR_DO);
sendGetAll(new byte[]{}, 0x9000, TestUtils.checkList(0, AramConstTest.VALID_REF_AR_DO));
getSpecific(AramConstTest.REF_DO, TestUtils.concatByteArray(new byte[]{(byte) 0xFF, (byte) 0x50, (byte) AramConstTest.VALID_REF_AR_DO.length}, AramConstTest.VALID_REF_AR_DO));
}
}