diff --git a/lib.omw.android/pom.xml b/lib.omw.android/pom.xml
index 6c64ddb..6ba8e62 100644
--- a/lib.omw.android/pom.xml
+++ b/lib.omw.android/pom.xml
@@ -4,7 +4,7 @@
diff --git a/lib.omw.ivid/src/main/java/net/vx4/lib/ivid/C2Transport.java b/lib.omw.ivid/src/main/java/net/vx4/lib/ivid/C2Transport.java
new file mode 100644
index 0000000..969c439
--- /dev/null
+++ b/lib.omw.ivid/src/main/java/net/vx4/lib/ivid/C2Transport.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2017-2020 adesso SE
+ *
+ * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by the
+ * European Commission - subsequent versions of the EUPL (the "Licence"); You may
+ * not use this work except in compliance with the Licence.
+ *
+ * You may obtain a copy of the Licence at:
+ * https://joinup.ec.europa.eu/software/page/eupl
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed
+ * under the Licence is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, either express or implied. See the Licence for the
+ * specific language governing permissions and limitations under the Licence.
+ */
+package net.vx4.lib.ivid;
+
+/**
+ * C2Transport is an implementation of a transport provider stack element to handle extended length APDU mapping to
+ * ENVELOPE (C2) / GET RESPONSE (C0) APDUs. Hence its name as the response is handled by the underlying stack and this
+ * class "only" cuts long APDUs into shorter ENVELOPE ADPUS.
+ *
+ * It is currently expected that GET RESPONSE handling is done under the hood. There exist only rare fails, which
+ * are assumed to be implementation fails. Might be extended if there is a reasonable demand. (Don't support bugs.)
+ *
+ * @author kahlo, 2018
+ * @version $Id$
+ */
+public class C2Transport implements TransportProvider {
+
+ private final short APDULen = 261; // T=0 limit
+ private final TransportProvider parent;
+ private byte envCLA = 0x00; // stick to ETSI-style without command chaining as we're on T=0 anyway
+ private byte envCLAlast = 0x00;
+ private byte envINS = (byte) 0xC2;
+ private short C2Len = 255;
+
+ /**
+ * Create an ENVELOPE transport provider from a physical layer transport provider such as
+ * ChannelTransportProvider.
+ *
+ * @param parent
+ */
+ public C2Transport(final TransportProvider parent) {
+ if (parent == null) {
+ throw new NullPointerException("parent transport provider required");
+ }
+ this.parent = parent;
+ }
+
+
+ /**
+ * Transmit an extended length APDU over a physical link layer.
+ * (Usually and intended for T=0, but sometimes necessary for T=1 over SWP also.)
+ *
+ * NOTE: If not using secure messaging but plain-text extended length APDUs and the other end is a card that
+ * responds "early" with intended no data, the additional envelope might result in an erroneous 6700.
+ * Only real fix: don't do it.
+ *
+ * @param apdu - APDU to be transmitted
+ * @return
+ */
+ @Override
+ public byte[] transmit(byte[] apdu) {
+ final byte channelId = ((ChannelTransportProvider) this.getParent()).getChannelId();
+
+ if (channelId < 4) {
+ apdu[0] = (byte) (apdu[0] & 0xBC | channelId);
+ } else if (channelId < 20) {
+ final boolean isSM = (apdu[0] & 0x0C) != 0;
+ apdu[0] = (byte) (apdu[0] & 0xB0 | 0x40 | channelId - 4);
+ if (isSM) {
+ apdu[0] |= 0x20;
+ }
+ }
+
+ if (apdu.length > APDULen || apdu.length > 5 && apdu[4] == 0) {
+ // sanitize APDU encoding
+ final short lc = (short) (((apdu[5] & 0xFF) << 8) + (apdu[6] & 0xFF));
+ String a2 = Hex.toString(apdu, 0, 7 + lc); // shorten APDU, strip off Le
+
+ // if (apdu[5] == 0) { // shorten APDU if length < 0x0100
+ if (lc < 0x0100) { // shorten APDU if length < 0x0100
+ a2 = a2.substring(0, 8) + a2.substring(12);
+ }
+ apdu = Hex.fromString(a2);
+
+ if (apdu.length > APDULen) {
+ int sent = 0;
+ byte[] last = null;
+ while (apdu.length - sent > 0) {
+ final int len = apdu.length - sent > C2Len ? C2Len : apdu.length - sent;
+ if (apdu.length - (sent + len) > 0) {
+ last = parent.transmit(Hex.fromString(Hex.toString(new byte[]{envCLA, envINS, 0, 0, (byte) len})
+ + Hex.toString(apdu, sent, len & 0xFF)));
+ } else {
+ last = parent.transmit(Hex.fromString(Hex.toString(new byte[]{envCLAlast, envINS, 0, 0, (byte) len})
+ + Hex.toString(apdu, sent, len & 0xFF)));
+ }
+ sent += len;
+ }
+
+ // ETSI-style, long variant with no data but SW OK send another empty ENVELOPE for EOF.
+ if (envCLA == 0x00 && last != null && last.length == 0 && parent.lastSW() == 0x9000) {
+ last = parent.transmit(new byte[] { envCLAlast, envINS, 0, 0, 0 });
+ }
+
+ return last;
+ }
+ }
+
+ return parent.transmit(apdu);
+ }
+
+ /**
+ * change maximum length of ENVELOPE frame
+ * @param len
+ */
+ public void setLength(final byte len) {
+ if (len <= 255) {
+ C2Len = (short) (len & 0xFF);
+ }
+ }
+
+ /**
+ * Set class byte values for "first" / "ongoing" frames and "only" / "last" frame.
+ *
+ * ETSI as well as JavaCard reference implementations stick to "00" for both, the last frame is detected
+ * either by a length smaller than 0xFF or a single frame with length 00. Frames with length 00 are
+ * mandatory for some implementations. That's why some assumptions are made if an empty frame should be
+ * sent.
+ *
+ * ISO on the hand chooses command chaining mode, which is supported in hand crafted implementations, tolerated by
+ * some JCREs and denied by some others. setCLA((byte) 0x10, (byte) 0x00) enforces ISO mode.
+ *
+ * @param firstCLA
+ * @param lastCLA
+ */
+ public void setCLA(final byte firstCLA, final byte lastCLA) {
+ envCLA = firstCLA;
+ envCLAlast = lastCLA;
+ }
+
+ /**
+ * Set a custom instruction byte values for ENVELOPEs. Very unusual.
+ *
+ * @param INS
+ */
+ public void setINS(final byte INS) {
+ envINS = INS;
+ }
+
+ @Override
+ public void close() {
+ parent.close();
+ }
+
+
+ @Override
+ public Object getParent() {
+ return parent;
+ }
+
+
+ @Override
+ public int lastSW() {
+ return parent.lastSW();
+ }
+}
diff --git a/lib.omw.ivid/src/main/java/net/vx4/lib/omapi/CMac.java b/lib.omw.ivid/src/main/java/net/vx4/lib/ivid/CMac.java
similarity index 99%
rename from lib.omw.ivid/src/main/java/net/vx4/lib/omapi/CMac.java
rename to lib.omw.ivid/src/main/java/net/vx4/lib/ivid/CMac.java
index d676f7d..5a532d8 100644
--- a/lib.omw.ivid/src/main/java/net/vx4/lib/omapi/CMac.java
+++ b/lib.omw.ivid/src/main/java/net/vx4/lib/ivid/CMac.java
@@ -58,7 +58,7 @@
* zusammen mit diesem Programm erhalten haben. Wenn nicht, siehe
*
* Authors Christian Kahlo, Ralf Wondratschek
@@ -58,11 +58,12 @@
* zusammen mit diesem Programm erhalten haben. Wenn nicht, siehe
*
- * This file is a stub, proving a working prototype. Work in progress. As storage on the UICC is limited the OMAPITP
- * wraps away "default" data as EF DIRECTORY, EF CARD ACCESS and EF CARD SECURITY. This data is public and until
- * algorithms are added or changed quite constant. EF CARD SECURITY changes due its included public key for chip
- * authentication, so this will be the first part to be stored separately and in an updatable manner according to the
- * personalization of the applet(s) in use.
- *
- * The OMAPITP also implements the PACE-to-CCID vendor command mapping to trigger PACE' / PACElight on "EstablishPACE"
- * command using the interal unlock key mechanism. Afterwards data is tunneled transparently through the standard
- * ISO secure messaging transport provider.
- *
+ *
+ * This file is a stub, proving a working prototype. Work in progress. As storage on the UICC is limited the
+ * IVIDTP wraps away "default" data as EF DIRECTORY, EF CARD ACCESS and EF CARD SECURITY. This data is public
+ * and until algorithms are added or changed quite constant. EF CARD SECURITY changes due its included public
+ * key for chip authentication, so this will be the first part to be stored separately and in an updatable
+ * manner according to the personalization of the applet(s) in use. The IVIDTP also implements the PACE-to-CCID
+ * vendor command mapping to trigger PACE' / PACElight on "EstablishPACE" command using the interal unlock key
+ * mechanism. Afterwards data is tunneled transparently through the standard ISO secure messaging transport
+ * provider.
*/
-public final class OMAPITP implements TransportProvider {
+public final class IVIDTP implements TransportProvider {
public static final String AID_NPA = "E80704007F00070302";
public static final String AID_VX4ID = "D2760000930101";
private static final byte[] EF_DIR = Hex.x(
"61324F0FE828BD080FA000000167455349474E500F434941207A752044462E655369676E5100730C4F0AA000000167455349474E61094F07A0000002471001610B4F09E80704007F00070302610C4F0AA000000167455349474E");
- private static final byte[] EF_ATR = Hex.x("");
+ private static final byte[] EF_ATR = Hex.x(""); // intentionally left blank
private static final byte[] EF_CA = Hex.x(
"3181C13012060A04007F0007020204020202010202010D300D060804007F00070202020201023012060A04007F00070202030202020102020129301C060904007F000702020302300C060704007F0007010202010D020129303E060804007F000702020831323012060A04007F0007020203020202010202012D301C060904007F000702020302300C060704007F0007010202010D02012D302A060804007F0007020206161E687474703A2F2F6273692E62756E642E64652F6369662F6E70612E786D6C");
private static final byte[] EF_CS = Hex.x(
"308206B006092A864886F70D010702A08206A13082069D020103310F300D0609608648016503040204050030820188060804007F0007030201A082017A04820176318201723012060A04007F0007020204020202010202010D300D060804007F00070202020201023017060A04007F0007020205020330090201010201010101003019060904007F000702020502300C060704007F0007010202010D3017060A04007F0007020205020330090201010201020101FF3012060A04007F00070202030202020102020129301C060904007F000702020302300C060704007F0007010202010D0201293062060904007F0007020201023052300C060704007F0007010202010D0342000419D4B7447788B0E1993DB35500999627E739A4E5E35F02D8FB07D6122E76567F17758D7A3AA6943EF23E5E2909B3E8B31BFAA4544C2CBF1FB487F31FF239C8F8020129303E060804007F000702020831323012060A04007F0007020203020202010202012D301C060904007F000702020302300C060704007F0007010202010D02012D302A060804007F0007020206161E687474703A2F2F6273692E62756E642E64652F6369662F6E70612E786D6CA08203EE308203EA30820371A00302010202012D300A06082A8648CE3D0403033055310B3009060355040613024445310D300B060355040A0C0462756E64310C300A060355040B0C03627369310D300B0603550405130430303033311A301806035504030C115445535420637363612D6765726D616E79301E170D3134303732333036333034305A170D3235303232333233353935395A305C310B3009060355040613024445310C300A060355040A0C03425349310D300B06035504051304303035303130302E06035504030C275445535420446F63756D656E74205369676E6572204964656E7469747920446F63756D656E7473308201133081D406072A8648CE3D02013081C8020101302806072A8648CE3D0101021D00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001303C041CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE041CB4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4043904B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34021D00FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D020101033A00043A79C3CBFDB8A6E569C9226CD54E81DE14381BC92A61AD554EBF349BFAFD72F18DC85D78E49742F37A75411E28E894308D6880D1380FBEB4A382016D30820169301F0603551D23041830168014A38DB7C0DBECF5A91FCA6B3D5EB2F328B5A5DC17301D0603551D0E04160414CF0A2AC150F28ADE4329F662E3D21CE5C78BCDE9300E0603551D0F0101FF040403020780302B0603551D1004243022800F32303134303732333036333034305A810F32303135303232333233353935395A30160603551D20040F300D300B060904007F000703010101302D0603551D1104263024821262756E646573647275636B657265692E6465A40E300C310A300806035504070C014430510603551D12044A30488118637363612D6765726D616E79406273692E62756E642E6465861C68747470733A2F2F7777772E6273692E62756E642E64652F63736361A40E300C310A300806035504070C01443019060767810801010602040E300C02010031071301411302494430350603551D1F042E302C302AA028A0268624687474703A2F2F7777772E6273692E62756E642E64652F746573745F637363615F63726C300A06082A8648CE3D040303036700306402300D90B1C6E52B5E20D8ECE1520981E11EF1AF02906A930420F87E90315588B70C0C9642160E877E42B1CE311849E388B802303450209749C1368D965CE879460F729E68BAB9D5D3269724721D0C564FB2752EC4C0F8F5542990CFDB7C848AA7D0A2BB3182010730820103020101305A3055310B3009060355040613024445310D300B060355040A0C0462756E64310C300A060355040B0C03627369310D300B0603550405130430303033311A301806035504030C115445535420637363612D6765726D616E7902012D300D06096086480165030402040500A046301706092A864886F70D010903310A060804007F0007030201302B06092A864886F70D010904311E041CC57AFB616E6837B63B22666F48547E3AD71795E33326C0CE5FF27C3A300A06082A8648CE3D040301043F303D021C58AE1E82475BE9C9167810593FCF7CA791DE45910380D5CF4FEB84D7021D00FFD316D91D85664479596BAFBBB2532540047334668E0C47EE99B826");
+ //
+
private final TransportProvider plainTP;
//
- int lastSW = -1;
+
+ private int lastSW = -1;
private TransportProvider tp;
- private CallbackHandler cbh;
+ private AuthenticationCallback authCB;
private byte[] efData = null;
+ // take care of freaked out implementations
+ private static final byte HCI_T1 = (byte) 0xC1;
+
+
/**
+ * Create a wrapper transport provider on top of some physical transport layer.
*
+ * @param chTP
*/
- public OMAPITP(final Channel channel) {
- this(new ChannelTransportProvider(channel));
+ // ChannelTransportProvider
+ public IVIDTP(final ChannelTransportProvider chTP) {
+ System.out.println("IVIDTP: seTP = [" + chTP + "]");
+ System.out.println("IVIDTP: seTP.getParent = [" + chTP.getParent() + "]");
+
+ final byte protocol = chTP.getProtocol();
+ if ((protocol & 0x0F) == 0x00) { // use T=0 ENVELOPE framing for T=0 on any physical link
+ this.tp = new C2Transport(chTP);
+ } else if (protocol == HCI_T1) { // we got CLF HCI over SWP with T=1, doesn't behave
+ this.tp = new C2Transport(chTP);
+ ((C2Transport) this.tp).setCLA((byte) 0x10, (byte) 0x00); // no ext. length, enforce ISO ENVELOPE of applet
+ } else { // i.e. 0xD1 -> SCI2C or GP SPI/I2C, use plain link layer
+ this.tp = chTP;
+ }
+
+ System.out.println("IVIDTP: tp = " + this.tp);
+ this.plainTP = this.tp;
}
/**
- * @param seTP
+ * Set the callback handler for user authentication and secret retrieval.
+ *
+ * @param authCB
*/
- public OMAPITP(final TransportProvider seTP) {
- System.out.println("OMAPITP: seTP = [" + seTP + "]");
- System.out.println("OMAPITP: seTP.getParent = [" + seTP.getParent() + "]");
-
- // if ("T=0".equals(card.getProtocol())) {
- tp = new C2Transport(seTP);
- // tp = seTP;
- // }
-
- System.out.println(tp);
- plainTP = tp;
- }
-
- public final void setCallbackHandler(final CallbackHandler cbh) {
- this.cbh = cbh;
+ public final void setAuthenticationCallback(final AuthenticationCallback authCB) {
+ this.authCB = authCB;
}
+ /**
+ * @param apdu
+ * @return
+ */
public final byte[] process(final byte[] apdu) {
byte[] rpdu = new byte[0];
short sw = (short) 0x9000;
@@ -106,16 +118,16 @@ public final byte[] process(final byte[] apdu) {
if (cmd.startsWith("FF9A0101")) { // get vendor
rpdu = "VX4.NET".getBytes(StandardCharsets.ISO_8859_1);
} else if (cmd.startsWith("FF9A0103")) { // get product
- rpdu = "OMAPI-SE".getBytes(StandardCharsets.ISO_8859_1);
+ rpdu = "IVID-SE".getBytes(StandardCharsets.ISO_8859_1);
} else if (cmd.startsWith("FF9A010600")) { // get firmware
// NOP
} else if (cmd.startsWith("FF9A010700")) { // get driver
// NOP
- } else if (cmd.startsWith("FF9A0401")) { // GetReaderPACE Capabilities
- rpdu = new byte[]{ 0x03 };
- } else if (cmd.startsWith("FF9A0402")) { // EstablishPACEChannel
- if (tp != plainTP) { // reset transport provider if channel already exists
- tp = plainTP;
+ } else if (cmd.startsWith("FF9A0401")) { // GetReaderPACE Capabilities
+ rpdu = new byte[] { 0x03 };
+ } else if (cmd.startsWith("FF9A0402")) { // EstablishPACEChannel
+ if (this.tp != this.plainTP) { // reset transport provider if channel already exists
+ this.tp = this.plainTP;
}
final byte[] miniPACERes = miniPACE();
@@ -124,78 +136,78 @@ public final byte[] process(final byte[] apdu) {
rpdu = new byte[0];
sw = 0x6985;
} else {
- //rpdu = miniPACERes;
-// if(this.lastSW() == 0x9000) { // doesn't work here, because sw is not set, comes from HAL-SE
+ // rpdu = miniPACERes;
+ // if(this.lastSW() == 0x9000) { // doesn't work here, because sw is not set, comes from HAL-SE
- byte[] IDPICC = TLV.get(miniPACERes, (byte) 0x86);
- byte[] CAR = TLV.get(miniPACERes, (byte) 0x87);
+ final byte[] IDPICC = TLV.get(miniPACERes, (byte) 0x86);
+ final byte[] CAR = TLV.get(miniPACERes, (byte) 0x87);
- StringBuffer sb = new StringBuffer();
+ final StringBuffer sb = new StringBuffer();
sb.append("9000"); // SW
- sb.append(Hex.byteToString(EF_CA.length) + "00"); // len EF_CardAccess
- sb.append(Hex.toString(EF_CA));
+ sb.append(Hex.x((byte) EF_CA.length) + "00"); // len EF_CardAccess
+ sb.append(Hex.x(EF_CA));
sb.append("0E").append(Hex.x(CAR));
sb.append("00");
sb.append("2000");
sb.append(Hex.x(IDPICC));
- byte[] res = Hex.x(sb.toString());
- int dataLen = res.length;
- rpdu = ArrayTool.concat(new byte[]{0, 0, 0, 0, (byte) dataLen, (byte) (dataLen >> 8)}, res);
-// } else {
-// rpdu = new byte[]{0x01, 0x00, 0x20, (byte) 0xF0}; // status, little-endian, abort
-// }
+ final byte[] res = Hex.x(sb.toString());
+ final int dataLen = res.length;
+ rpdu = ArrayTool.concat(new byte[] { 0, 0, 0, 0, (byte) dataLen, (byte) (dataLen >> 8) }, res);
+ // } else {
+ // rpdu = new byte[]{0x01, 0x00, 0x20, (byte) 0xF0}; // status, little-endian, abort
+ // }
sw = (short) 0x9000;
}
- } else if (cmd.startsWith("FF9A0403")) { // DestroyPACEChannel
+ } else if (cmd.startsWith("FF9A0403")) { // DestroyPACEChannel
// reset transport provider
- tp = plainTP;
+ this.tp = this.plainTP;
rpdu = new byte[0];
- } else if (cmd.startsWith("FF9A0410")) { // VerifyPIN / ModifyPIN
+ } else if (cmd.startsWith("FF9A0410")) { // VerifyPIN / ModifyPIN
// NOP
rpdu = new byte[0];
}
- return ArrayTool.concat(rpdu, new byte[]{(byte) (sw >> 8 & 0xFF), (byte) (sw & 0xFF)});
+ return ArrayTool.concat(rpdu, new byte[] { (byte) (sw >> 8 & 0xFF), (byte) (sw & 0xFF) });
}
- if ("00A4040C09E80704007F00070302".equals(cmd)) { // select DF_EID
+ if ("00A4040C09E80704007F00070302".equals(cmd)) { // select DF_EID
// return Hex.x("9000");
- } else if ("00A4000000".equals(cmd)) { // select MF
+ } else if ("00A4000000".equals(cmd)) { // select MF
// return Hex.x("9000");
- } else if ("00A40000023F00".equals(cmd)) { // select MF
+ } else if ("00A40000023F00".equals(cmd)) { // select MF
// return Hex.x("9000");
- } else if ("00A4000C023F00".equals(cmd)) { // select MF
+ } else if ("00A4000C023F00".equals(cmd)) { // select MF
// return Hex.x("9000");
- } else if ("00A4020C022F00".equals(cmd)) { // select EF.DIR
- efData = EF_DIR;
+ } else if ("00A4020C022F00".equals(cmd)) { // select EF.DIR
+ this.efData = EF_DIR;
// return Hex.x("9000");
- } else if ("00A4020C022F01".equals(cmd)) { // select EF.ATR
- efData = EF_ATR;
+ } else if ("00A4020C022F01".equals(cmd)) { // select EF.ATR
+ this.efData = EF_ATR;
// return Hex.x("9000");
- } else if ("00A4020C02011C".equals(cmd)) { // select EF.CA
- efData = EF_CA;
+ } else if ("00A4020C02011C".equals(cmd)) { // select EF.CA
+ this.efData = EF_CA;
// return Hex.x("9000");
- } else if ("00A4020C02011D".equals(cmd)) { // select EF.CS
+ } else if ("00A4020C02011D".equals(cmd)) { // select EF.CS
- efData = EF_CS;
+ this.efData = EF_CS;
// return Hex.x("9000");
- } else if (cmd.startsWith("00B0")) { // read binary
+ } else if (cmd.startsWith("00B0")) { // read binary
int ofs = ((apdu[2] & 0xFF) << 8) + (apdu[3] & 0xFF);
int len = apdu[4] & 0xFF;
if (cmd.startsWith("00B09C00")) {
- efData = EF_CA;
+ this.efData = EF_CA;
ofs = 0;
}
len = len == 0 ? 255 : len;
- if (efData.length - ofs < len) {
- len = efData.length - ofs;
+ if (this.efData.length - ofs < len) {
+ len = this.efData.length - ofs;
sw = 0x6282;
}
- rpdu = ArrayTool.sub(efData, ofs, len);
+ rpdu = ArrayTool.sub(this.efData, ofs, len);
} else if (cmd.startsWith("0022C1A4")) { // MSE PACE, 0022C1A4 12 80 0A 04007F00070202040202830103 84010D
// NOP for now
} else {
@@ -209,76 +221,77 @@ public final byte[] process(final byte[] apdu) {
apdu[3] ^= (byte) 0xAA;
}
- rpdu = plainTP.transmit(apdu); // CA-SM
+ rpdu = this.plainTP.transmit(apdu); // CA-SM
} else {
- rpdu = tp.transmit(apdu); // transmit with PACE channel
+ rpdu = this.tp.transmit(apdu); // transmit with PACE channel
}
- sw = (short) tp.lastSW();
+ sw = (short) this.tp.lastSW();
}
} catch (final Exception e) {
e.printStackTrace();
- return new byte[]{0x6F, (byte) 0xFF};
+ return new byte[] { 0x6F, (byte) 0xFF };
}
- return ArrayTool.concat(rpdu, new byte[]{(byte) (sw >> 8 & 0xFF), (byte) (sw & 0xFF)});
+ return ArrayTool.concat(rpdu, new byte[] { (byte) (sw >> 8 & 0xFF), (byte) (sw & 0xFF) });
}
+ /**
+ * PoC implementation of "PACE-light" in case we lack PACE support
+ *
+ * @return
+ */
private byte[] miniPACE() {
- if (cbh == null) {
- System.err.println("OMAPI-TP: miniPACE: no callback handler for secret registered.");
- }
-
- System.out.println("OMAPI-TP: using callback handler: " + cbh.getClass() + " / " + cbh.toString());
- final byte[] ulk = cbh.getSecret();
- if (ulk == null) {
- System.out.println("OMAPI-TP: secret is null, aborting and returning with null.");
+ if (this.authCB == null) {
+ System.out.println("IVID-TP: authentication callback is null, aborting and returning with null.");
return null;
}
-
- System.out.println("OMAPI-TP: got secret: " + Hex.x(ulk));
+ System.out.println("IVID-TP: callback = " + this.authCB.getClass() + " / " + this.authCB.toString());
try {
- final SecretKeySpec pinKey = new SecretKeySpec(ulk, "AES");
-
- final Cipher c = Cipher.getInstance("AES/CBC/NoPadding");
- c.init(Cipher.ENCRYPT_MODE, pinKey, new IvParameterSpec(new byte[16]));
-
- final byte[] hsRandom = new byte[32];
- new SecureRandom().nextBytes(hsRandom);
- byte[] hsRandEnc = c.doFinal(hsRandom, 0, hsRandom.length);
- hsRandEnc = TLV.build(0x7C, TLV.build(0x81, hsRandEnc));
-
- byte[] plRes = tp.transmit(Hex.fromString(
- "80CE0000" + Hex.byteToString((byte) hsRandEnc.length) + Hex.toString(hsRandEnc) + "00"));
+ // byte[] buf = TLV.build(0x7C, TLV.build(0x81, this.authCB.init("TEST")));
+ // buf = this.tp.transmit(Hex.x("80CE0000" + Hex.x((byte) buf.length) + Hex.x(buf) + "00"));
+ // if (this.tp.lastSW() != 0x9000) {
+ // System.out.println("IVID-TP: error setting up secure channel: " + Hex.x((short) this.tp.lastSW()));
+ // return null;
+ // }
+ //
+ // final byte[] IDPICC = TLV.get(TLV.get(buf, (byte) 0x7C), (byte) 0x82); // IDPICC = encrypted card random
+ // buf = this.authCB.finish(IDPICC);
- // TODO: lastSW != 0x9000 -> error
+ final Cipher unlockCipher = this.authCB.getCipher("TEST");
- plRes = TLV.get(TLV.get(plRes, (byte) 0x7C), (byte) 0x82);
+ final byte[] hsRandPlain = new byte[32];
+ new SecureRandom().nextBytes(hsRandPlain);
+ byte[] buf = TLV.build(0x7C, TLV.build(0x81, hsRandPlain));
- final byte[] IDPICC = plRes.clone(); // encrypted card random is IDPICC
+ buf = this.tp.transmit(Hex.x("80CE0000" + Hex.x((byte) buf.length) + Hex.x(buf) + "00"));
+ if (this.tp.lastSW() != 0x9000) {
+ System.out.println("IVID-TP: error setting up secure channel: " + Hex.x((short) this.tp.lastSW()));
+ return null;
+ }
- System.out.println("PL-RES 1: " + Hex.toString(IDPICC));
+ final byte[] IDPICC = TLV.get(TLV.get(buf, (byte) 0x7C), (byte) 0x82); // IDPICC = encrypted card random
//
- final MessageDigest mdSHA256 = MessageDigest.getInstance("SHA-256");
- c.init(Cipher.DECRYPT_MODE, pinKey, new IvParameterSpec(new byte[16]));
- mdSHA256.update(c.doFinal(plRes));
- plRes = mdSHA256.digest(hsRandom);
-
- System.out.println("PL-RES 2: " + Hex.toString(IDPICC));
-
- System.out.println("PACE-light secret2: " + Hex.toString(plRes));
-
- System.out.println("SE-TP parent: " + tp + " / " + tp.getParent());
+ {
+ final MessageDigest mdSHA256 = MessageDigest.getInstance("SHA-256");
+ mdSHA256.update(unlockCipher.doFinal(IDPICC));
+ buf = mdSHA256.digest(unlockCipher.doFinal(hsRandPlain));
+ }
- final MessageDigest mdSHA1 = MessageDigest.getInstance("SHA-1");
- final ISOSMTransport sesmTP = new ISOSMTransport(tp);
- sesmTP.setupKeys(KDF(mdSHA1, plRes, 1, 16), KDF(mdSHA1, plRes, 2, 16));
- tp = sesmTP;
+ //
+ {
+ System.out.println("SE-TP parent: " + this.tp + " / " + this.tp.getParent());
+ final ISOSMTransport sesmTP = new ISOSMTransport(this.tp);
+ final MessageDigest mdSHA1 = MessageDigest.getInstance("SHA-1");
+ sesmTP.setupKeys(KDF(mdSHA1, buf, 1, 16), KDF(mdSHA1, buf, 2, 16));
+ this.tp = sesmTP;
+ }
- byte[] ceres = tp.transmit(Hex.fromString("80CE0000"));
+ byte[] ceres = this.tp.transmit(Hex.x("80CE0000"));
+ // tp.lastSW()
ceres = TLV.get(ceres, (byte) 0x7C);
final List
diff --git a/lib.omw.ivid/src/main/java/net/vx4/lib/omapi/C2Transport.java b/lib.omw.ivid/src/main/java/net/vx4/lib/omapi/C2Transport.java
deleted file mode 100644
index 04ca2e9..0000000
--- a/lib.omw.ivid/src/main/java/net/vx4/lib/omapi/C2Transport.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright 2017-2019 adesso AG
- *
- * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by the
- * European Commission - subsequent versions of the EUPL (the "Licence"); You may
- * not use this work except in compliance with the Licence.
- *
- * You may obtain a copy of the Licence at:
- * https://joinup.ec.europa.eu/software/page/eupl
- *
- * Unless required by applicable law or agreed to in writing, software distributed
- * under the Licence is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR
- * CONDITIONS OF ANY KIND, either express or implied. See the Licence for the
- * specific language governing permissions and limitations under the Licence.
- */
-package net.vx4.lib.omapi;
-
-/**
- * C2Transport is an implementation of a transport provider stack element to handle extended length APDU mapping to
- * ENVELOPE (C2) / GET RESPONSE (C0) APDUs. Hence its name as the response is handled by the underlying stack and this
- * class "only" cuts long APDUs into shorter ENVELOPE ADPUS.
- *
- * @author kahlo, 2018
- * @version $Id$
- */
-public class C2Transport implements TransportProvider {
-
- private final TransportProvider parent;
- private final short APDULen = 261; // T=0 limit
- private byte envCLA = 0x10;
- private byte envCLAlast = 0x00;
- private byte envINS = (byte) 0xC2;
- private short C2Len = 255;
-
-
- public C2Transport(final TransportProvider parent) {
- if (parent == null) {
- throw new NullPointerException("parent transport provider required");
- }
- this.parent = parent;
- }
-
-
- @Override
- public byte[] transmit(byte[] apdu) {
- final byte channelId = ((ChannelTransportProvider) this.getParent()).getChannelId();
-
- if (channelId < 4) {
- apdu[0] = (byte) (apdu[0] & 0xBC | channelId);
- } else if (channelId < 20) {
- final boolean isSM = (apdu[0] & 0x0C) != 0;
- apdu[0] = (byte) (apdu[0] & 0xB0 | 0x40 | channelId - 4);
- if (isSM) {
- apdu[0] |= 0x20;
- }
- }
-
- if (apdu.length > APDULen || apdu.length > 5 && apdu[4] == 0) {
- // sanitize APDU encoding
- String a2 = Hex.toString(apdu);
- if (apdu[5] == 0) {
- a2 = a2.substring(0, 8) + a2.substring(12);
- }
-
- apdu = Hex.fromString(a2.endsWith("0000") ? a2.substring(0, a2.length() - 4) : a2);
-
- if (apdu.length > APDULen) {
- int sent = 0;
- byte[] last = null;
- while (apdu.length - sent > 0) {
- final int len = apdu.length - sent > C2Len ? C2Len : apdu.length - sent;
- if (apdu.length - (sent + len) > 0) {
- last = parent.transmit(
- Hex.fromString(Hex.toString(new byte[]{envCLA, envINS, 0, 0, (byte) len})
- + Hex.toString(apdu, sent, len & 0xFF)));
- } else {
- last = parent.transmit(
- Hex.fromString(
- Hex.toString(new byte[]{envCLAlast, envINS, 0, 0, (byte) len})
- + Hex.toString(apdu, sent, len & 0xFF)));
- }
- sent += len;
- }
- return last;
- }
- }
-
- return parent.transmit(apdu);
- }
-
- @Override
- public void close() {
- parent.close();
- }
-
-
- @Override
- public Object getParent() {
- return parent;
- }
-
-
- @Override
- public int lastSW() {
- return parent.lastSW();
- }
-}
diff --git a/lib.omw.ivid/src/main/java/net/vx4/lib/omapi/ChannelTransportProvider.java b/lib.omw.ivid/src/main/java/net/vx4/lib/omapi/ChannelTransportProvider.java
deleted file mode 100644
index a51f88c..0000000
--- a/lib.omw.ivid/src/main/java/net/vx4/lib/omapi/ChannelTransportProvider.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright 2017-2019 adesso AG
- *
- * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by the
- * European Commission - subsequent versions of the EUPL (the "Licence"); You may
- * not use this work except in compliance with the Licence.
- *
- * You may obtain a copy of the Licence at:
- * https://joinup.ec.europa.eu/software/page/eupl
- *
- * Unless required by applicable law or agreed to in writing, software distributed
- * under the Licence is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR
- * CONDITIONS OF ANY KIND, either express or implied. See the Licence for the
- * specific language governing permissions and limitations under the Licence.
- */
-package net.vx4.lib.omapi;
-
-import org.simalliance.openmobileapi.Channel;
-
-/**
- * The ChannelTransportProvider deals with automatically negotiated channels on the underlying terminal interface.
- * If a channel has been opened successfully it is the first contact to the selected app, so the SELECT APDU
- * response is fetch belowed and used to adopt to protocol and implementation specifics of the secure element.
- *
- * @author kahlo, 2018
- * @version $Id$
- */
-public class ChannelTransportProvider implements TransportProvider {
-
- private final byte channelId;
- private Channel channel = null;
- private int lastSW = -1;
-
- /**
- *
- * @param channel
- */
- public ChannelTransportProvider(final Channel channel) {
- this.channel = channel;
- final byte[] selRes = this.channel.getSelectResponse();
- channelId = TLV.get(TLV.get(selRes, (byte) 0x6F), (byte) 0x85)[0];
- }
-
-
- @Override
- public void close() {
- // TODO: checking closing behaviour
- System.out.println("ChannelTransportProvider.close(): WARNING, not closed");
- // channel.close();
- }
-
-
- @Override
- public Object getParent() {
- return channel;
- }
-
-
- @Override
- public int lastSW() {
- return lastSW;
- }
-
-
- public byte getChannelId() {
- return channelId;
- }
-
-
- @Override
- public byte[] transmit(final byte[] apdu) {
- lastSW = -1;
-
- System.out.println("ChannelTrannsport: channel = " + channel + " open? " + (channel != null ? !channel.isClosed() : "