summaryrefslogtreecommitdiff
path: root/smcc/src/test/java/at/gv/egiz/smcc/starcos/STARCOSCardChannelEmul.java
diff options
context:
space:
mode:
Diffstat (limited to 'smcc/src/test/java/at/gv/egiz/smcc/starcos/STARCOSCardChannelEmul.java')
-rw-r--r--smcc/src/test/java/at/gv/egiz/smcc/starcos/STARCOSCardChannelEmul.java375
1 files changed, 375 insertions, 0 deletions
diff --git a/smcc/src/test/java/at/gv/egiz/smcc/starcos/STARCOSCardChannelEmul.java b/smcc/src/test/java/at/gv/egiz/smcc/starcos/STARCOSCardChannelEmul.java
new file mode 100644
index 00000000..89030894
--- /dev/null
+++ b/smcc/src/test/java/at/gv/egiz/smcc/starcos/STARCOSCardChannelEmul.java
@@ -0,0 +1,375 @@
+/*
+* Copyright 2008 Federal Chancellery Austria and
+* Graz University of Technology
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package at.gv.egiz.smcc.starcos;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
+import java.util.HashMap;
+
+import javax.smartcardio.Card;
+import javax.smartcardio.CardException;
+import javax.smartcardio.CommandAPDU;
+import javax.smartcardio.ResponseAPDU;
+
+import at.gv.egiz.smcc.AbstractAppl;
+import at.gv.egiz.smcc.CardChannelEmul;
+import at.gv.egiz.smcc.CardEmul;
+import at.gv.egiz.smcc.File;
+import at.gv.egiz.smcc.PIN;
+
+@SuppressWarnings("restriction")
+public class STARCOSCardChannelEmul extends CardChannelEmul {
+
+ public static final int KID_PIN_Glob = 0x01;
+
+ /**
+ *
+ */
+ protected CardEmul cardEmul;
+
+ public final HashMap<Integer, PIN> globalPins = new HashMap<Integer, PIN>();
+
+ public STARCOSCardChannelEmul(CardEmul cardEmul) {
+ this.cardEmul = cardEmul;
+ globalPins.put(KID_PIN_Glob, new PIN(new byte[] { (byte) 0x24, (byte) 0x00,
+ (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF }, KID_PIN_Glob, 10));
+ }
+
+ @Override
+ public Card getCard() {
+ return cardEmul;
+ }
+
+ protected ResponseAPDU cmdSELECT(CommandAPDU command) throws CardException {
+
+ byte[] fid = command.getData();
+
+ switch (command.getP1()) {
+ case 0x00: // MF
+ if (fid.length !=0) {
+ return new ResponseAPDU(new byte[] {(byte) 0x6A, (byte) 0x80});
+ } else {
+ currentFile = null;
+ currentAppl = null;
+ return new ResponseAPDU(new byte[] {(byte) 0x90, (byte) 0x00});
+ }
+
+ case 0x01: // Lower-level DF
+ throw new CardException("Not supported.");
+
+ case 0x02: // EF in current DF
+ if (currentAppl != null) {
+ if (command.getP2() != 0x04) {
+ throw new CardException("Not supported.");
+ }
+ for (File file : currentAppl.getFiles()) {
+ if (Arrays.equals(fid, file.fid)) {
+ currentFile = file;
+ byte[] response = new byte[file.fcx.length + 2];
+ System.arraycopy(file.fcx, 0, response, 0, file.fcx.length);
+ response[file.fcx.length] = (byte) 0x90;
+ response[file.fcx.length + 1] = (byte) 0x00;
+ return new ResponseAPDU(response);
+ }
+ }
+ return new ResponseAPDU(new byte[] {(byte) 0x6A, (byte) 0x82});
+ } else {
+ throw new CardException("Not supported.");
+ }
+
+ case 0x03: // Higher-level DF
+ throw new CardException("Not supported.");
+
+ case 0x04: // Selection by DF name
+ AbstractAppl appl = cardEmul.getApplication(fid);
+ if (appl != null) {
+ if (command.getP2() != 0x00) {
+ throw new CardException("Not supported.");
+ }
+ if (currentAppl != null && currentAppl != appl) {
+ currentAppl.leaveApplContext();
+ currentFile = null;
+ }
+ currentAppl = appl;
+
+ byte[] fci = currentAppl.getFCI();
+ byte[] response = new byte[fci.length + 2];
+ System.arraycopy(fci, 0, response, 0, fci.length);
+ response[fci.length] = (byte) 0x90;
+ response[fci.length + 1] = (byte) 0x00;
+ return new ResponseAPDU(response);
+ }
+
+ default:
+ return new ResponseAPDU(new byte[] {(byte) 0x6A, (byte) 0x86});
+ }
+
+ }
+
+ protected ResponseAPDU cmdREAD_BINARY(CommandAPDU command) throws CardException {
+
+ if (command.getINS() != 0xB0) {
+ throw new IllegalArgumentException("INS has to be 0xB0.");
+ }
+
+ if (currentFile == null) {
+ return new ResponseAPDU(new byte[] {(byte) 0x69, (byte) 0x86});
+ }
+
+ if ((command.getP1() & 0x80) > 0) {
+ throw new CardException("Not implemented.");
+ }
+
+ int offset = command.getP2() + (command.getP1() << 8);
+ if (offset > currentFile.file.length) {
+ // Wrong length
+ return new ResponseAPDU(new byte[] {(byte) 0x67, (byte) 0x00});
+ }
+
+ if (command.getNe() == 0) {
+ throw new CardException("Not implemented.");
+ }
+
+ if (currentFile.kid != -1) {
+ PIN pin;
+ if ((currentFile.kid & 0x80) > 0) {
+ if (currentAppl == null
+ || (pin = currentAppl.pins.get(currentFile.kid)) == null
+ || pin.state != PIN.STATE_PIN_VERIFIED) {
+ return new ResponseAPDU(new byte[] {(byte) 0x69, (byte) 0x82});
+ }
+ } else {
+ if ((pin = globalPins.get(currentFile.kid)) == null
+ || pin.state != PIN.STATE_PIN_VERIFIED) {
+ return new ResponseAPDU(new byte[] {(byte) 0x69, (byte) 0x82});
+ }
+ }
+ }
+
+ if (command.getNe() == 256 || command.getNe() <= currentFile.file.length - offset) {
+ int len = Math.min(command.getNe(), currentFile.file.length - offset);
+ byte[] response = new byte[len + 2];
+ System.arraycopy(currentFile.file, offset, response, 0, len);
+ response[len] = (byte) 0x90;
+ response[len + 1] = (byte) 0x00;
+ return new ResponseAPDU(response);
+ } else if (command.getNe() >= currentFile.file.length - offset) {
+ return new ResponseAPDU(new byte[] {(byte) 0x62, (byte) 0x82});
+ } else {
+ return new ResponseAPDU(new byte[] {(byte) 0x67, (byte) 0x00});
+ }
+
+ }
+
+
+ @Override
+ public ResponseAPDU transmit(CommandAPDU command) throws CardException {
+
+ if (command.getCLA() == 0x00) {
+
+ switch (command.getINS()) {
+
+ // SELECT
+ case 0xA4:
+ return cmdSELECT(command);
+
+ // READ BINARY
+ case 0xB0:
+ return cmdREAD_BINARY(command);
+
+ // VERIFY
+ case 0x20:
+ return cmdVERIFY(command);
+
+ // MANAGE SECURITY ENVIRONMENT
+ case 0x22: {
+ if (currentAppl != null) {
+ return currentAppl.cmdMANAGE_SECURITY_ENVIRONMENT(command, this);
+ } else {
+ return new ResponseAPDU(new byte[] {(byte) 0x6F, (byte) 0x05});
+ }
+ }
+
+ // CHANGE REFERENCE DATA
+ case 0x24: {
+ return cmdCHANGE_REFERENCE_DATA(command);
+ }
+
+ // PERFORM SECURITY OPERATION
+ case 0x2A: {
+ if (currentAppl != null) {
+ return currentAppl.cmdPERFORM_SECURITY_OPERATION(command, this);
+ } else {
+ return new ResponseAPDU(new byte[] {(byte) 0x6F, (byte) 0x05});
+ }
+ }
+
+ // INTERNAL AUTHENTICATE
+ case 0x88: {
+ if (currentAppl != null) {
+ return currentAppl.cmdINTERNAL_AUTHENTICATE(command, this);
+ } else {
+ return new ResponseAPDU(new byte[] {(byte) 0x6F, (byte) 0x05});
+ }
+ }
+
+ default:
+ return new ResponseAPDU(new byte[] { (byte) 0x6D, (byte) 0x00});
+ }
+
+ } else {
+ return new ResponseAPDU(new byte[] { (byte) 0x6E, (byte) 0x00});
+ }
+
+ }
+
+ protected ResponseAPDU verifyPin(int kid, byte[] reference) {
+
+ PIN pin;
+ if ((kid & 0x80) > 0 && currentAppl != null) {
+ pin = currentAppl.pins.get(kid);
+ } else {
+ pin = globalPins.get(kid);
+ }
+
+ if (pin != null) {
+
+ if (reference.length == 0) {
+ return new ResponseAPDU(new byte[] { (byte) 0x63, (byte) (pin.kfpc | 0xC0)});
+ }
+
+ if (reference.length != 8) {
+ return new ResponseAPDU(new byte[] {(byte) 0x67, (byte) 0x00});
+ }
+
+ if (Arrays.equals(reference, pin.pin)) {
+ switch (pin.state) {
+ case PIN.STATE_PIN_BLOCKED:
+ return new ResponseAPDU(new byte[] { (byte) 0x69, (byte) 0x83 });
+
+ case PIN.STATE_RESET:
+ pin.state = PIN.STATE_PIN_VERIFIED;
+
+ default:
+ pin.kfpc = 10;
+ return new ResponseAPDU(new byte[] { (byte) 0x90, (byte) 0x00 });
+ }
+ } else {
+ switch (pin.state) {
+ case PIN.STATE_PIN_BLOCKED:
+ return new ResponseAPDU(new byte[] { (byte) 0x69, (byte) 0x83 });
+
+ default:
+ if (--pin.kfpc > 0) {
+ return new ResponseAPDU(new byte[] { (byte) 0x63, (byte) (pin.kfpc | 0xC0)});
+ } else {
+ pin.state = PIN.STATE_PIN_BLOCKED;
+ return new ResponseAPDU(new byte[] { (byte) 0x69, (byte) 0x83 });
+ }
+ }
+
+ }
+
+ } else {
+ return new ResponseAPDU(new byte[] {(byte) 0x6A, (byte) 0x00});
+ }
+
+ }
+
+ protected ResponseAPDU cmdVERIFY(CommandAPDU command) throws CardException {
+
+ if (command.getINS() != 0x20) {
+ throw new IllegalArgumentException("INS has to be 0x20.");
+ }
+
+ if (command.getP1() != 00) {
+ return new ResponseAPDU(new byte[] {(byte) 0x6B, (byte) 0x00});
+ }
+
+ return verifyPin(command.getP2(), command.getData());
+
+ }
+
+ protected ResponseAPDU cmdCHANGE_REFERENCE_DATA(CommandAPDU command) {
+
+ if (command.getINS() != 0x24) {
+ throw new IllegalArgumentException("INS has to be 0x24.");
+ }
+
+ byte[] data = command.getData();
+
+ ResponseAPDU response;
+
+ if (command.getP1() == 0x01) {
+
+ if (data.length != 8) {
+ return new ResponseAPDU(new byte[] {(byte) 0x67, (byte) 0x00});
+ }
+
+ response = verifyPin(0xFF & command.getP2(), data);
+
+ } else if (command.getP1() == 0x00) {
+
+ if (data.length != 16) {
+ return new ResponseAPDU(new byte[] {(byte) 0x67, (byte) 0x00});
+ }
+
+ response = verifyPin(0xFF & command.getP2(), Arrays.copyOf(data, 8));
+
+ } else {
+ return new ResponseAPDU(new byte[] { (byte) 0x6A, (byte) 0x81 });
+ }
+
+ if (response.getSW() == 0x9000) {
+ PIN pin;
+ if (currentAppl != null) {
+ pin = currentAppl.pins.get(command.getP2());
+ } else {
+ pin = globalPins.get(command.getP2());
+ }
+ pin.pin = Arrays.copyOfRange(data, 8, 16);
+ }
+
+ return response;
+
+ }
+
+ public void setPin(int kid, char[] value) {
+ PIN pin = globalPins.get(kid);
+ if (pin != null) {
+ if (value == null) {
+ pin.pin = null;
+ } else {
+ byte[] b = new byte[8];
+ b[0] = (byte) (0x20 | value.length);
+ for(int i = 1, j = 0; i < b.length; i++) {
+ int h = ((j < value.length)
+ ? Character.digit(value[j++], 10)
+ : 0x0F);
+ int l = ((j < value.length)
+ ? Character.digit(value[j++], 10)
+ : 0x0F);
+ b[i] = (byte) ((h << 4) | l);
+ }
+ pin.pin = b;
+ }
+ }
+ }
+
+
+} \ No newline at end of file