/* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.ss3t.javacard.gpg; import net.ss3t.javacard.CardInterfaceBuilder; import net.ss3t.javacard.TLVDer; import org.junit.Test; import java.util.Arrays; import java.util.Random; import javax.smartcardio.CardException; import javax.smartcardio.ResponseAPDU; import static net.ss3t.javacard.CardUtils.assertSW; import static net.ss3t.javacard.CardUtils.assertSWData; import static net.ss3t.javacard.CardUtils.assertSWOnly; import static net.ss3t.javacard.HexString.bytesToHex; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; public abstract class GpgDataTest extends GpgTest { public GpgDataTest(CardInterfaceBuilder builder) { super(builder); } @Test public void getAID() throws CardException { assertSWData(0x9000, appletAID, card.sendAPDU(0, Gpg.CMD_GET_DATA, 0, 0x4F)); } @Test public void dataWriteAndRead() throws CardException { assertSWOnly(0x9000, card.sendAPDU(0, Gpg.CMD_VERIFY, 0, 0x82, "31 32 33 34 35 36")); assertSWOnly(0x9000, card.sendAPDU(0, Gpg.CMD_VERIFY, 0, 0x83, "31 32 33 34 35 36 37 38")); Random random = new Random(); for (DataObject dataObject : DataObject.values()) { if (dataObject.writeAccess != Access.NEVER) { byte[] data = new byte[dataObject.maxLength]; random.setSeed(dataObject.tag); random.nextBytes(data); assertSWOnly(0x9000, card.sendAPDU(0, Gpg.CMD_PUT_DATA, (byte) (dataObject.tag / 256), (byte) (dataObject.tag & 0xFF), data)); } } // Re-read all the objects that are individually readable. for (DataObject dataObject : DataObject.values()) { if (dataObject.writeAccess != Access.NEVER && !dataObject.groupedRead) { byte[] data = new byte[dataObject.maxLength]; random.setSeed(dataObject.tag); random.nextBytes(data); assertSWData(0x9000, data, card.sendAPDU(0, Gpg.CMD_GET_DATA, (byte) (dataObject.tag / 256), (byte) (dataObject.tag & 0xFF))); } } // Check the fingerprints. byte[] rand = new byte[DataObject.FINGERPRINT_1.maxLength]; byte[] expected = new byte[DataObject.FINGERPRINT_1.maxLength * 3]; random.setSeed(DataObject.FINGERPRINT_1.tag); random.nextBytes(rand); System.arraycopy(rand, 0, expected, 0, rand.length); random.setSeed(DataObject.FINGERPRINT_2.tag); random.nextBytes(rand); System.arraycopy(rand, 0, expected, rand.length, rand.length); random.setSeed(DataObject.FINGERPRINT_3.tag); random.nextBytes(rand); System.arraycopy(rand, 0, expected, 2 * rand.length, rand.length); assertSWData(0x9000, expected, card.sendAPDU(0, Gpg.CMD_GET_DATA, 0, 0xC5, 0)); // CA Fingerprints random.setSeed(DataObject.CA_FINGERPRINT_1.tag); random.nextBytes(rand); System.arraycopy(rand, 0, expected, 0, rand.length); random.setSeed(DataObject.CA_FINGERPRINT_2.tag); random.nextBytes(rand); System.arraycopy(rand, 0, expected, rand.length, rand.length); random.setSeed(DataObject.CA_FINGERPRINT_3.tag); random.nextBytes(rand); System.arraycopy(rand, 0, expected, 2 * rand.length, rand.length); assertSWData(0x9000, expected, card.sendAPDU(0, Gpg.CMD_GET_DATA, 0, 0xC6, 0)); // Generation dates rand = new byte[DataObject.GENERATION_TIME_1.maxLength]; expected = new byte[DataObject.GENERATION_TIME_1.maxLength * 3]; random.setSeed(DataObject.GENERATION_TIME_1.tag); random.nextBytes(rand); System.arraycopy(rand, 0, expected, 0, rand.length); random.setSeed(DataObject.GENERATION_TIME_2.tag); random.nextBytes(rand); System.arraycopy(rand, 0, expected, rand.length, rand.length); random.setSeed(DataObject.GENERATION_TIME_3.tag); random.nextBytes(rand); System.arraycopy(rand, 0, expected, 2 * rand.length, rand.length); assertSWData(0x9000, expected, card.sendAPDU(0, Gpg.CMD_GET_DATA, 0, 0xCD, 0)); // Make sure that the composite DOs Cardholder Related Data and Application related data // make their individual components. ResponseAPDU r = assertSW(0x9000, card.sendAPDU(0, Gpg.CMD_GET_DATA, 0, DataObject.APPLICATION_RELATED_DATA.tag)); checkCompositeData(r.getData()); r = assertSW(0x9000, card.sendAPDU(0, Gpg.CMD_GET_DATA, 0, DataObject.CARDHOLDER_RELATED_DATA.tag)); checkCompositeData(r.getData()); } @Test public void writePWStatus() throws CardException { assertSWOnly(0x9000, card.sendAPDU(0, Gpg.CMD_VERIFY, 0, 0x82, "31 32 33 34 35 36")); assertSWOnly(0x9000, card.sendAPDU(0, Gpg.CMD_VERIFY, 0, 0x83, "31 32 33 34 35 36 37 38")); assertSWOnly(0x9000, card.sendAPDU(0, Gpg.CMD_PUT_DATA, 0, 0xC4, "00")); ResponseAPDU response = assertSW(0x9000, card.sendAPDU(0, Gpg.CMD_GET_DATA, 0, 0xC4, 7)); assertEquals(0, response.getData()[0]); assertSWOnly(0x9000, card.sendAPDU(0, Gpg.CMD_PUT_DATA, 0, 0xC4, "01")); ResponseAPDU response2 = assertSW(0x9000, card.sendAPDU(0, Gpg.CMD_GET_DATA, 0, 0xC4, 7)); assertEquals(1, response2.getData()[0]); assertSWOnly(0x9000, card.sendAPDU(0, Gpg.CMD_PUT_DATA, 0, 0xC4, "00 FF FF FF FF FF FF FF")); response2 = assertSW(0x9000, card.sendAPDU(0, Gpg.CMD_GET_DATA, 0, 0xC4, 7)); assertArrayEquals(response.getData(), response2.getData()); // Too long. assertSWOnly(0x6700, card.sendAPDU(0, Gpg.CMD_PUT_DATA, 0, 0xC4, "00 FF FF FF FF FF FF FF FF")); // Bad PIN3 assertSWOnly(0x63C0 + Gpg.MAX_TRIES_PIN3 - 1, card.sendAPDU(0, Gpg.CMD_VERIFY, 0, 0x83, "31 32 33 34 35 36 37 37")); assertSWOnly(0x6982, card.sendAPDU(0, Gpg.CMD_PUT_DATA, 0, 0xC4, "00")); // Good PIN3 assertSWOnly(0x9000, card.sendAPDU(0, Gpg.CMD_VERIFY, 0, 0x83, "31 32 33 34 35 36 37 38")); } @Test public void readPWStatus() throws CardException { ResponseAPDU response = assertSW(0x9000, card.sendAPDU(0, Gpg.CMD_GET_DATA, 0, 0xC4, 7)); assertEquals(Gpg.MAX_PIN_LENGTH, response.getData()[1]); assertEquals(Gpg.MAX_PIN_LENGTH, response.getData()[2]); assertEquals(Gpg.MAX_PIN_LENGTH, response.getData()[3]); // No bad presentation so far but RC not set. assertEquals(Gpg.MAX_TRIES_PIN1, response.getData()[4]); assertEquals(0, response.getData()[5]); assertEquals(Gpg.MAX_TRIES_PIN3, response.getData()[6]); // Bad PIN. assertSWOnly(0x63C0 + Gpg.MAX_TRIES_PIN1 - 1, card.sendAPDU(0, Gpg.CMD_VERIFY, 0, 0x82, "31 32 33 34 35 36 37")); response = assertSW(0x9000, card.sendAPDU(0, Gpg.CMD_GET_DATA, 0, 0xC4, 7)); assertEquals(Gpg.MAX_PIN_LENGTH, response.getData()[1]); assertEquals(Gpg.MAX_PIN_LENGTH, response.getData()[2]); assertEquals(Gpg.MAX_PIN_LENGTH, response.getData()[3]); // No bad presentation so far but RC not set. assertEquals(Gpg.MAX_TRIES_PIN1 - 1, response.getData()[4]); assertEquals(0, response.getData()[5]); assertEquals(Gpg.MAX_TRIES_PIN3, response.getData()[6]); // Bad PW3 assertSWOnly(0x63C0 + Gpg.MAX_TRIES_PIN3 - 1, card.sendAPDU(0, Gpg.CMD_VERIFY, 0, 0x83, "31 32 33 34 35 36 37 37")); response = assertSW(0x9000, card.sendAPDU(0, Gpg.CMD_GET_DATA, 0, 0xC4, 7)); assertEquals(Gpg.MAX_PIN_LENGTH, response.getData()[1]); assertEquals(Gpg.MAX_PIN_LENGTH, response.getData()[2]); assertEquals(Gpg.MAX_PIN_LENGTH, response.getData()[3]); // No bad presentation so far but RC not set. assertEquals(Gpg.MAX_TRIES_PIN1 - 1, response.getData()[4]); assertEquals(0, response.getData()[5]); assertEquals(Gpg.MAX_TRIES_PIN3 - 1, response.getData()[6]); // Good PW1, PW3 submissions. assertSWOnly(0x9000, card.sendAPDU(0, Gpg.CMD_VERIFY, 0, 0x82, "31 32 33 34 35 36")); assertSWOnly(0x9000, card.sendAPDU(0, Gpg.CMD_VERIFY, 0, 0x83, "31 32 33 34 35 36 37 38")); response = assertSW(0x9000, card.sendAPDU(0, Gpg.CMD_GET_DATA, 0, 0xC4, 7)); assertEquals(Gpg.MAX_TRIES_PIN1, response.getData()[4]); assertEquals(0, response.getData()[5]); assertEquals(Gpg.MAX_TRIES_PIN3, response.getData()[6]); } @Test public void variableLength() throws CardException { assertSWOnly(0x9000, card.sendAPDU(0, Gpg.CMD_VERIFY, 0, 0x83, "31 32 33 34 35 36 37 38")); assertSWOnly(0x9000, card.sendAPDU(0, Gpg.CMD_PUT_DATA, 0, DataObject.LOGIN.tag, "00")); assertSWData(0x9000, "00", card.sendAPDU(0, Gpg.CMD_GET_DATA, 0, DataObject.LOGIN.tag)); assertSWOnly(0x9000, card.sendAPDU(0, Gpg.CMD_PUT_DATA, 0, DataObject.LOGIN.tag, "01 02")); assertSWData(0x9000, "01 02", card.sendAPDU(0, Gpg.CMD_GET_DATA, 0, DataObject.LOGIN.tag)); byte[] tooLong = new byte[DataObject.LOGIN.maxLength + 1]; Arrays.fill(tooLong, (byte) 0xFF); assertSWOnly(0x6700, card.sendAPDU(0, Gpg.CMD_PUT_DATA, 0, DataObject.LOGIN.tag, tooLong)); assertSWData(0x9000, "01 02", card.sendAPDU(0, Gpg.CMD_GET_DATA, 0, DataObject.LOGIN.tag)); } @Test public void cardWipe() throws CardException { assertSWOnly(0x9000, card.sendAPDU(0, Gpg.CMD_VERIFY, 0, 0x82, "31 32 33 34 35 36")); assertSWOnly(0x9000, card.sendAPDU(0, Gpg.CMD_VERIFY, 0, 0x83, "31 32 33 34 35 36 37 38")); Random random = new Random(); for (DataObject dataObject : DataObject.values()) { if (dataObject.writeAccess != Access.NEVER) { byte[] data = new byte[dataObject.maxLength]; random.setSeed(dataObject.tag); random.nextBytes(data); assertSWOnly(0x9000, card.sendAPDU(0, Gpg.CMD_PUT_DATA, (byte) (dataObject.tag / 256), (byte) (dataObject.tag & 0xFF), data)); } } clearCard(); assertSWOnly(0x9000, card.sendAPDU(0, Gpg.CMD_VERIFY, 0, 0x82, "31 32 33 34 35 36")); assertSWOnly(0x9000, card.sendAPDU(0, Gpg.CMD_VERIFY, 0, 0x83, "31 32 33 34 35 36 37 38")); // Now check that all the readable data is zero for (DataObject dataObject : DataObject.values()) { if (dataObject.readAccess != Access.NEVER) { if (dataObject == DataObject.AID || dataObject == DataObject.HISTORICAL_BYTES || dataObject == DataObject.DISCRETIONARY_DOS || dataObject == DataObject.EXTENDED_CAPABILITIES || dataObject == DataObject.ALGORITHM_ATTRIBUTES_1 || dataObject == DataObject.ALGORITHM_ATTRIBUTES_2 || dataObject == DataObject.ALGORITHM_ATTRIBUTES_3 || dataObject == DataObject.CARDHOLDER_RELATED_DATA || dataObject == DataObject.APPLICATION_RELATED_DATA || dataObject == DataObject.PW_STATUS) { // The system data isn't cleared. For the compound object (cardholder data, application // relate data) we check their individual components. continue; } ResponseAPDU r = assertSW(0x9000, card.sendAPDU(0, Gpg.CMD_GET_DATA, (byte) (dataObject.tag / 256), (byte) (dataObject.tag & 0xFF))); if (r.getData().length != 0) { byte[] zero = new byte[r.getData().length]; Arrays.fill(zero, (byte) 0); assertArrayEquals(zero, r.getData()); } } } } private void checkCompositeData(byte[] compositeData) throws CardException { int pos = 0; while (pos < compositeData.length) { TLVDer tlv = TLVDer.GetNext(compositeData, pos); if (tlv.status == TLVDer.Status.END) { break; } assertEquals(TLVDer.Status.OK, tlv.status); DataObject cardObject = DataObject.getByTag(tlv.tag); assertNotNull("Unknown object returned:" + tlv.tag, cardObject); ResponseAPDU singleObject = assertSW(0x9000, card.sendAPDU(0, Gpg.CMD_GET_DATA, tlv.tag >> 8, tlv.tag & 0xFF, 0)); assertArrayEquals("Expecting: " + bytesToHex(tlv.data), tlv.data, singleObject.getData()); // We should never get currentOffset == pos since we need at least a byte for the tag and // another for the length. assertNotEquals(pos, tlv.currentOffset); pos = tlv.currentOffset; } } }