/****************************************************************************
* Copyright (C) 2012 ecsec GmbH.
* All rights reserved.
* Contact: ecsec GmbH (info@ecsec.de)
*
* This file is part of the Open eCard App.
*
* GNU General Public License Usage
* This file may be used under the terms of the GNU General Public
* License version 3.0 as published by the Free Software Foundation
* and appearing in the file LICENSE.GPL included in the packaging of
* this file. Please review the following information to ensure the
* GNU General Public License version 3.0 requirements will be met:
* http://www.gnu.org/copyleft/gpl.html.
*
* Other Usage
* Alternatively, this file may be used in accordance with the terms
* and conditions contained in a signed written agreement between
* you and ecsec GmbH.
*
***************************************************************************/
package org.openecard.ifd.scio;
import iso.std.iso_iec._24727.tech.schema.Connect;
import iso.std.iso_iec._24727.tech.schema.ControlIFD;
import iso.std.iso_iec._24727.tech.schema.ControlIFDResponse;
import iso.std.iso_iec._24727.tech.schema.EstablishChannel;
import iso.std.iso_iec._24727.tech.schema.EstablishChannelResponse;
import iso.std.iso_iec._24727.tech.schema.EstablishContext;
import iso.std.iso_iec._24727.tech.schema.InputUnitType;
import iso.std.iso_iec._24727.tech.schema.ListIFDs;
import iso.std.iso_iec._24727.tech.schema.PasswordAttributesType;
import iso.std.iso_iec._24727.tech.schema.PasswordTypeType;
import iso.std.iso_iec._24727.tech.schema.PinInputType;
import iso.std.iso_iec._24727.tech.schema.VerifyUser;
import iso.std.iso_iec._24727.tech.schema.VerifyUserResponse;
import java.math.BigInteger;
import javax.activation.UnsupportedDataTypeException;
import javax.xml.bind.JAXBException;
import org.openecard.common.ECardConstants;
import org.openecard.common.util.ByteUtils;
import org.openecard.common.util.PINUtils;
import org.openecard.common.util.StringUtils;
import org.openecard.common.util.UtilException;
import org.openecard.gui.swing.SwingDialogWrapper;
import org.openecard.gui.swing.SwingUserConsent;
import org.openecard.ifd.scio.reader.PCSCFeatures;
import org.openecard.ifd.scio.reader.PCSCPinModify;
import org.openecard.ifd.scio.reader.PCSCPinVerify;
import org.openecard.ws.marshal.WSMarshaller;
import org.openecard.ws.marshal.WSMarshallerException;
import org.openecard.ws.marshal.WSMarshallerFactory;
import org.testng.annotations.Test;
import org.xml.sax.SAXException;
import static iso.std.iso_iec._24727.tech.schema.PasswordTypeType.*;
import static org.testng.Assert.*;
/**
*
* @author Tobias Wich <tobias.wich@ecsec.de>
*/
public class PINTest {
private static PasswordAttributesType create(boolean needsPadding, PasswordTypeType pwdType, int minLen, int storedLen, int maxLen) {
PasswordAttributesType r = create(needsPadding, pwdType, minLen, storedLen);
r.setMaxLength(BigInteger.valueOf(maxLen));
return r;
}
private static PasswordAttributesType create(boolean needsPadding, PasswordTypeType pwdType, int minLen, int storedLen) {
PasswordAttributesType r = new PasswordAttributesType();
r.setMinLength(BigInteger.valueOf(minLen));
r.setStoredLength(BigInteger.valueOf(storedLen));
r.setPwdType(pwdType);
if (needsPadding) {
r.getPwdFlags().add("needs-padding");
}
return r;
}
@Test
public void testISO() throws UtilException {
PasswordAttributesType pwdAttr = create(true, ISO_9564_1, 4, 8, 12);
byte[] pinMask = PINUtils.createPinMask(pwdAttr);
assertEquals(new byte[] {0x20,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF}, pinMask);
byte[] pinResult = PINUtils.encodePin("123456789", pwdAttr);
assertEquals(new byte[] {0x29,0x12,0x34,0x56,0x78,(byte)0x9F,(byte)0xFF,(byte)0xFF}, pinResult);
}
@Test
public void testBCD() throws UtilException {
PasswordAttributesType pwdAttr = create(true, BCD, 4, 3, 6);
pwdAttr.setPadChar(new byte[]{(byte)0xFF});
byte[] pinMask = PINUtils.createPinMask(pwdAttr);
assertEquals(new byte[] {(byte)0xFF,(byte)0xFF,(byte)0xFF}, pinMask);
byte[] pinResult = PINUtils.encodePin("12345", pwdAttr);
assertEquals(new byte[] {(byte)0x12,(byte)0x34,(byte)0x5F}, pinResult);
}
@Test
public void testASCII() throws UtilException {
PasswordAttributesType pwdAttr = create(false, ASCII_NUMERIC, 6, 6);
byte[] pinResult = PINUtils.encodePin("123456", pwdAttr);
assertEquals(new byte[] {0x31,0x32,0x33,0x34,0x35,0x36}, pinResult);
try {
pwdAttr = create(true, ASCII_NUMERIC, 6, 6);
PINUtils.encodePin("123456", pwdAttr);
fail(); // padding needed, but no char given
} catch (UtilException ex) {
}
// try {
// pwdAttr = create(false, ASCII_NUMERIC, 6, 7);
// PINUtils.encodePin("123456", pwdAttr);
// fail(); // padding inferred, but no char given
// } catch (UtilException ex) {
// }
}
@Test
public void testHalfNibble() throws UtilException {
PasswordAttributesType pwdAttr = create(false, HALF_NIBBLE_BCD, 6, 6);
byte[] pinResult = PINUtils.encodePin("123456", pwdAttr);
assertEquals(new byte[] {(byte)0xF1,(byte)0xF2,(byte)0xF3,(byte)0xF4,(byte)0xF5,(byte)0xF6}, pinResult);
pwdAttr = create(true, HALF_NIBBLE_BCD, 6, 7);
pwdAttr.setPadChar(new byte[]{(byte)0xFF});
pinResult = PINUtils.encodePin("123456", pwdAttr);
assertEquals(new byte[] {(byte)0xF1,(byte)0xF2,(byte)0xF3,(byte)0xF4,(byte)0xF5,(byte)0xF6,(byte)0xFF}, pinResult);
}
@Test
public void verifyISO() throws IFDException {
PasswordAttributesType pwdAttr = create(true, ISO_9564_1, 4, 8);
PCSCPinVerify ctrlStruct = new PCSCPinVerify(pwdAttr, StringUtils.toByteArray("00200001"));
byte[] structData = ctrlStruct.toBytes();
String pinStr = "00 20 00 01 08 20 FF FF FF FF FF FF FF"; // length=13
String ctrlStr = "00 0F 89 47 04 0E04 02 FF 0407 00 000000 0D000000";
byte[] referenceData = StringUtils.toByteArray(ctrlStr + pinStr, true);
assertEquals(referenceData, structData);
}
@Test
public void verifyASCII() throws IFDException {
PasswordAttributesType pwdAttr = create(false, ASCII_NUMERIC, 4, 4);
PCSCPinVerify ctrlStruct = new PCSCPinVerify(pwdAttr, StringUtils.toByteArray("00200001"));
byte[] structData = ctrlStruct.toBytes();
String pinStr = "00 20 00 01"; // length=5
String ctrlStr = "00 0F 82 04 00 0404 02 FF 0407 00 000000 04000000";
byte[] referenceData = StringUtils.toByteArray(ctrlStr + pinStr, true);
assertEquals(referenceData, structData);
}
@Test(enabled=false)
public void testModifyPin() throws IFDException, WSMarshallerException, SAXException {
IFD ifd = new IFD();
ifd.setGUI(new SwingUserConsent(new SwingDialogWrapper()));
EstablishContext eCtx = new EstablishContext();
byte[] ctxHandle = ifd.establishContext(eCtx).getContextHandle();
ListIFDs listIFDs = new ListIFDs();
listIFDs.setContextHandle(ctxHandle);
String ifdName = ifd.listIFDs(listIFDs).getIFDName().get(0);
Connect connect = new Connect();
connect.setContextHandle(ctxHandle);
connect.setIFDName(ifdName);
connect.setSlot(BigInteger.ZERO);
byte[] slotHandle = ifd.connect(connect).getSlotHandle();
// prepare pace call
String xmlCall = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<iso:EstablishChannel xmlns:iso=\"urn:iso:std:iso-iec:24727:tech:schema\">\n" +
" <iso:SlotHandle>" + ByteUtils.toHexString(slotHandle) + "</iso:SlotHandle>\n" +
" <iso:AuthenticationProtocolData Protocol=\"urn:oid:0.4.0.127.0.7.2.2.4\">\n" +
" <iso:PinID>03</iso:PinID>\n" +
" </iso:AuthenticationProtocolData>\n" +
"</iso:EstablishChannel>";
WSMarshaller m = WSMarshallerFactory.createInstance();
EstablishChannel eCh = (EstablishChannel) m.unmarshal(m.str2doc(xmlCall));
// send pace call
EstablishChannelResponse eChR = ifd.establishChannel(eCh);
assertEquals(eChR.getResult().getResultMajor(), ECardConstants.Major.OK);
PasswordAttributesType pwdAttr = create(true, ASCII_NUMERIC, 6, 6, 6);
pwdAttr.setPadChar(new byte[]{(byte) 0x3F});
PCSCPinModify ctrlStruct = new PCSCPinModify(pwdAttr, StringUtils.toByteArray("002C0203"));
byte[] structData = ctrlStruct.toBytes();
String pinStr = "00 2C 02 03 06 3F3F3F3F3F3F";
String ctrlStr = "15 05 82 06 00 00 00 0606 01 02 02 0407 00 01 02 000000 0B000000";
// This is the command the 'AusweisApp' sends
//String ausweisApp = "150582080000000606010202090400010200000005000000002C020300";
byte[] referenceData = StringUtils.toByteArray(ctrlStr + pinStr, true);
assertEquals(referenceData, structData);
ControlIFD controlIFD = new ControlIFD();
controlIFD.setCommand(ByteUtils.concatenate((byte) PCSCFeatures.MODIFY_PIN_DIRECT, structData));
controlIFD.setContextHandle(ctxHandle);
controlIFD.setIFDName(ifdName);
ControlIFDResponse response = ifd.controlIFD(controlIFD);
}
@Test(enabled=false)
public void verifyeGK() {
IFD ifd = new IFD();
ifd.setGUI(new SwingUserConsent(new SwingDialogWrapper()));
EstablishContext eCtx = new EstablishContext();
byte[] ctxHandle = ifd.establishContext(eCtx).getContextHandle();
ListIFDs listIFDs = new ListIFDs();
listIFDs.setContextHandle(ctxHandle);
String ifdName = ifd.listIFDs(listIFDs).getIFDName().get(0);
Connect connect = new Connect();
connect.setContextHandle(ctxHandle);
connect.setIFDName(ifdName);
connect.setSlot(BigInteger.ZERO);
byte[] slotHandle = ifd.connect(connect).getSlotHandle();
VerifyUser verify = new VerifyUser();
verify.setSlotHandle(slotHandle);
InputUnitType inputUnit = new InputUnitType();
verify.setInputUnit(inputUnit);
PinInputType pinInput = new PinInputType();
inputUnit.setPinInput(pinInput);
pinInput.setIndex(BigInteger.ZERO);
pinInput.setPasswordAttributes(create(true, ISO_9564_1, 6, 8, 8));
verify.setTemplate(StringUtils.toByteArray("00 20 00 01", true));
VerifyUserResponse verifyR = ifd.verifyUser(verify);
byte[] responseCode = verifyR.getResponse();
}
@Test(enabled=false)
public void executePACE_PIN() throws UnsupportedDataTypeException, JAXBException, SAXException, WSMarshallerException {
IFD ifd = new IFD();
ifd.setGUI(new SwingUserConsent(new SwingDialogWrapper()));
EstablishContext eCtx = new EstablishContext();
byte[] ctxHandle = ifd.establishContext(eCtx).getContextHandle();
ListIFDs listIFDs = new ListIFDs();
listIFDs.setContextHandle(ctxHandle);
String ifdName = ifd.listIFDs(listIFDs).getIFDName().get(0);
Connect connect = new Connect();
connect.setContextHandle(ctxHandle);
connect.setIFDName(ifdName);
connect.setSlot(BigInteger.ZERO);
byte[] slotHandle = ifd.connect(connect).getSlotHandle();
// prepare pace call
String xmlCall = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<iso:EstablishChannel xmlns:iso=\"urn:iso:std:iso-iec:24727:tech:schema\">\n" +
" <iso:SlotHandle>" + ByteUtils.toHexString(slotHandle) + "</iso:SlotHandle>\n" +
" <iso:AuthenticationProtocolData Protocol=\"urn:oid:0.4.0.127.0.7.2.2.4\">\n" +
" <iso:PinID>03</iso:PinID>\n" +
" </iso:AuthenticationProtocolData>\n" +
"</iso:EstablishChannel>";
WSMarshaller m = WSMarshallerFactory.createInstance();
EstablishChannel eCh = (EstablishChannel) m.unmarshal(m.str2doc(xmlCall));
// send pace call
EstablishChannelResponse eChR = ifd.establishChannel(eCh);
}
}