//* Licensed Materials - Property of * //* IBM * //* Miracle A/S * //* Alexandra Instituttet A/S * //* * //* eu.abc4trust.pabce.1.34 * //* * //* (C) Copyright IBM Corp. 2014. All Rights Reserved. * //* (C) Copyright Miracle A/S, Denmark. 2014. All Rights Reserved. * //* (C) Copyright Alexandra Instituttet A/S, Denmark. 2014. All * //* Rights Reserved. * //* US Government Users Restricted Rights - Use, duplication or * //* disclosure restricted by GSA ADP Schedule Contract with IBM Corp. * //* * //* This file is 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 eu.abc4trust.smartcard; import java.io.ByteArrayOutputStream; import java.io.UnsupportedEncodingException; import java.math.BigInteger; import java.net.URI; import java.nio.ByteBuffer; import java.security.MessageDigest; import java.security.SecureRandom; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Random; import java.util.Set; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import javax.smartcardio.Card; import javax.smartcardio.CardChannel; import javax.smartcardio.CardException; import javax.smartcardio.CardTerminal; import javax.smartcardio.CommandAPDU; import javax.smartcardio.ResponseAPDU; import org.apache.commons.lang.NotImplementedException; import eu.abc4trust.cryptoEngine.user.CredentialSerializer; import eu.abc4trust.cryptoEngine.user.PseudonymSerializer; import eu.abc4trust.guice.ProductionModuleFactory.CryptoEngine; import eu.abc4trust.util.TimingsLogger; import eu.abc4trust.xml.Credential; import eu.abc4trust.xml.PseudonymWithMetadata; /** * A few things that are assumed to be true: * - IssuerID's are static and can be found by a lookup in a static map from ID's to URI's. * - Credential IDs are stored in blobs with the first byte * of the blob being the ID of the credential. * - A root-key with keyID=0 is always present when in working mode. * - IssuerID = CounterID = groupID (but probably only for the pilot) * @author Kasper damgaard */ public class HardwareSmartcard implements Smartcard { public static final String CREDENTIAL_PREFIX = "device-cred-"; static public final String UPROVE_RELOAD_URI_POSTFIX = ":S:UPROVERELOAD"; private Card card; private CardChannel channel; private CardTerminal terminal; @SuppressWarnings("unused") private final byte getMode = 0x02, //works in any mode. returns 1 byte data setRootMode = 0x04, //8 byte accesscode required setWorkingMode = 0x06, //only from root. nothing else req. setVirginMode = 0x08, //16byte mac req. pinTrialsLeft = 0x0A, //returns single byte pukTrialsLeft = 0x0C, //returns single byte changePin = 0x0E, //old-pin and new-pin - 8 bytes total resetPin = 0x10, //8 byte PUK and 4 byte new pin initializeDevice = 0x12, //2 byte id and 2 byte size - only root. Gives back a ciphertext getDeviceID = 0x14, //pin, gives back a 2 byte deviceID getVersion = 0x16, //gives back a 64 byte version number getMemorySpace = 0x18, //pin, 2 byte back putData = 0x1A, //variable length input > 0 getChallenge = 0x1C, //input challenge size (0 is 256), get back challenge of that size. authenticateData = 0x1E, //single keyID byte - prior: PUT DATA setAuthenticationKey = 0x20, //single keyID byte - prior to: GET CHALLENGE, PUT DATA, AUTHENTICATE DATA listAuthenticationKeys = 0x22, //pin, get back keyID||size(key) over all keys readAuthenticationKey = 0x24, //pin, keyID , get back Auth. key removeAuthenticationKey = 0x26, //single byte keyID - prior to: GET CHALLENGE, PUT DATA, AUTHENTICATE DATA setGroupComponent = 0x28, //groupID, compType [0,2] - prior to: GET CHALLENGE, PUT DATA, AUTHENTICATE DATA setGenerator = 0x2A, //groupID, genID - prior to: GET CHALLENGE, PUT DATA, AUTHENTICATE DATA listGroups = 0x2C, //pin, get back concat of all groupID's readGroup = 0x2E, //pin, groupID, get back description of the group readGroupComponent = 0x30, //pin, groupID, comptype [0:modulus, 1:group order, 2: cofactor, 3:# of generators] readGenerator = 0x32, //pin, groupID, genID, get back the group generator removeGroup = 0x34, //groupID - prior to: GET CHALLENGE, PUT DATA, AUTHENTICATE DATA setCounter = 0x36, //counterID, keyID, index, threshold, 4 byte cursor - prior to: GET CHALLENGE, PUT DATA, AUTHENTICATE DATA incrementCounter = 0x38, //keyID, byte[] sig listCounters = 0x3A, //pin, get back concat of all counterID's. readCounter = 0x3C, //pin, counterID, get back description of counter (see setCounter) removeCounter = 0x3E, //counterID - prior to: GET CHALLENGE, PUT DATA, AUTHENTICATE DATA setIssuer = 0x40, //issuerID, groupID, genID1, genID2, numpres, counterID - prior to: GET CHALLENGE, PUT DATA, AUTHENTICATE DATA listIssuers = 0x42, //pin, get back concat of issuerID's readIssuer = 0x44, //pin, issuerID, get back description of Issuer removeIssuer = 0x46, //issuerID - prior to: GET CHALLENGE, PUT DATA, AUTHENTICATE DATA setProver = 0x48, //proverID, 2 byte ksize, 2 byte csize, byte[] credIDs - prior to: GET CHALLENGE, PUT DATA, AUTHENTICATE DATA readProver = 0x4A, //pin, proverID, get back description of prover removeProver = 0x4C, //proverID - prior to: GET CHALLENGE, PUT DATA, AUTHENTICATE DATA startCommitments = 0x4E, //pin, proverID, get back 16 byte proofsession startResponses = 0x50, //pin, proverID, byte[] input (at most 2043 bytes) setCredential = 0x52, //pin, credentialID, issuerID listCredentials = 0x54, //pin, get back concat of all credentialID's readCredential = 0x56, //pin, credentialID, 7 byte description of credential removeCredential = 0x58, //pin, credentialID getCredentialPublicKey = 0x5A, //pin, credentialID, get back byte[] public key getIssuanceCommitment = 0x5C, //pin, credentialID, get back byte[] C (issuance commitment) getIssuanceResponse = 0x5E, //pin, credentialID, get back byte[] R getPresentationCommitment = 0x60, //pin, credentialID, get back byte[] C (presentiation commitment) getPresentationResponse = 0x62, //pin, credentialID, get back byte[] R (presentation response) getDevicePublicKey = 0x64, //pin, get back byte[] device Pk getDeviceCommitment = 0x66, //pin, get back byte[] C (device commitment) getDeviceResponse = 0x68, //pin, get back byte[] R (device response) getScopeExclusivePseudonym = 0x6A, //pin, byte[] scope (max 2044 bytes), get back byte[] h(scope)^deviceKey mod m getScopeExclusiveCommitment = 0x6C, //pin, byte[] scope, get back byte[] C (scope-exclusive commitment) getScopeExclusiveResponse = 0x6E, //pin, byte[] scope, get back byte[] R (scope-exclusive response) storeBlob = 0x70, //pin, byte[] uri (1-200 bytes) listBlobs = 0x72, //pin, nread (number of URI's already read), get back URI's in LV1 format + updated nread + nunread URI's readBlob = 0x74, //pin, byte[] uri (1-200 bytes), get back contents of the URI removeBlob = 0x76, //pin, byte[] uri backupDevice = 0x78, //pin, password(8 bytes) - (extended) restoreDevice = 0x7A, //pin, password (same as the one used in backupDevice) - NOT extended backupCounters = 0x7C, //pin, password (8 bytes) - NOT extended restoreCounters = 0x7E; // pin, password (same as the one used in // backupCounters) - NOT extended private final int backupCredential = 0x80, // pin, password (8 bytes), credentialID - Exteded restoreCredential = 0x82, //pin, password (8 bytes) - NOT Exteded. isAndroid = 0x8E; //only Android smartcard should answer this command with success private final int ABC4TRUSTCMD = 0xBC, STATUS_OK = 0x90, STATUS_FORBIDDEN = 0x9A, STATUS_TOO_LITTLE_DATA = 0x9B, STATUS_NOT_EXACT_DATA_SIZE = 0x9C, STATUS_TOO_SMALL_CHALLENGE = 0x9D, STATUS_RFU = 0x9E, STATUS_ERROR = 0x9F; private final int STATUS_BAD_PIN = 0x03, STATUS_CARD_LOCKED = 0x04, STATUS_BAD_PUK = 0x05, STATUS_CARD_DEAD = 0x06; private static final int MAX_CREDENTIALS = 3; public int max_blob_bytes = 512; private final Random rand; private static final StaticUriToIDMap staticMap = StaticUriToIDMap.getInstance(); public static boolean printInput = false; /** * * @param terminal * @param card * @param file A file describing the credential URI to ID mapping. If null, it is assumed that no mapping is done yet. */ public HardwareSmartcard(CardTerminal terminal, Card card, Random rand) { this.terminal = terminal; this.channel = card.getBasicChannel(); this.card = card; this.rand = rand; if (isAndroid()) { System.out.println("Running with an Android phone, so we limit the max blob size to 255 bytes"); max_blob_bytes = 253; //Android does not support extended apdu's } } private ResponseAPDU transmitCommand(CommandAPDU cmd) throws CardException{ int count = 0; while(count < 4){ try{ card.beginExclusive(); break; }catch(CardException e){ System.err.println("Could not obtain exclusive lock on the card!"); count++; if(count == 4){ throw e; } try { Thread.sleep(500); } catch (InterruptedException e1) { e1.printStackTrace(); } card.disconnect(false); card = terminal.connect("*"); this.channel = card.getBasicChannel(); } } ResponseAPDU response = channel.transmit(cmd); card.endExclusive(); return response; } @SuppressWarnings("unused") private void resetCard(){ try { this.channel = null; card.disconnect(true); //reset after disconnect card = null; this.card = this.terminal.connect("*"); this.channel = this.card.getBasicChannel(); } catch (CardException e) { throw new RuntimeException(e); } } /* private void detectMaxBlobSize(int pin){ if(MAX_BLOB_BYTES == 0){ if(this.readIssuer(pin, StaticUriToIDMap.credUnivUProveIssuer) == null){ MAX_BLOB_BYTES = 1900; //Idemix }else{ MAX_BLOB_BYTES = 2046; //UProve } } } */ private SmartcardStatusCode evaluateStatus(ResponseAPDU response){ switch(response.getSW1()){ case STATUS_OK: return SmartcardStatusCode.OK; case STATUS_FORBIDDEN: return SmartcardStatusCode.FORBIDDEN; case STATUS_TOO_LITTLE_DATA: case STATUS_NOT_EXACT_DATA_SIZE: case STATUS_TOO_SMALL_CHALLENGE: case STATUS_RFU: return SmartcardStatusCode.BAD_REQUEST; case STATUS_ERROR: switch(response.getSW2()){ case STATUS_BAD_PIN: case STATUS_BAD_PUK: return SmartcardStatusCode.UNAUTHORIZED; case STATUS_CARD_LOCKED: case STATUS_CARD_DEAD: return SmartcardStatusCode.FORBIDDEN; } return SmartcardStatusCode.BAD_REQUEST; default: return SmartcardStatusCode.BAD_REQUEST; } } /** * * @param length * @return a byte array of length 2 containing the length in bytes */ private byte[] intLengthToShortByteArr(int length){ return ByteBuffer.allocate(2).putShort((short)length).array(); } private byte[] pinToByteArr(int pin) { String s = String.valueOf(pin); if(s.length() != 4){ int l = s.length(); int diff = 4-l; String tmp = s; s = ""; for(int i =0; i < diff; i++){ s += "0"; } s+=tmp; } byte[] res = new byte[4]; for(int i = 0; i < 4; i++){ res[i] = (byte)s.charAt(i); } return res; } private byte[] pukToByteArr(int puk){ String s = String.valueOf(puk); if(s.length() != 8){ return null; } byte[] res = new byte[8]; for(int i = 0; i < 8; i++){ res[i] = (byte)(s.charAt(i) & 0xFF); } return res; } private byte[] uriToByteArr(URI uri){ String s = uri.toASCIIString(); byte[] res = new byte[s.length()]; for(int i = 0; i < s.length(); i++){ res[i] = (byte)s.charAt(i); } return res; } private URI byteArrToUri(byte[] b){ try { String s = new String(b, "US-ASCII"); System.out.println("s: " +s); return URI.create(s); } catch (UnsupportedEncodingException e) { e.printStackTrace(); return null; } } private byte getIssuerIDFromUri(int pin, URI uri){ return staticMap.getIssuerIDFromUri(uri); } private URI getIssuerUriFromID(int pin, byte ID, CryptoEngine engine){ return staticMap.getIssuerUriFromID(ID, engine); } public byte getCredentialIDFromUri(int pin, URI uri){ return this.getBlob(pin, uri).blob[0]; } private URI getCredentialUriFromID(int pin, byte ID){ Map<URI, SmartcardBlob> blobs = getBlobs(pin); for(URI uri : blobs.keySet()){ if(blobs.get(uri).blob[0] == ID && blobs.get(uri).blob.length == 1){ return uri; } } return null; } private byte getNewCredentialID(int pin){ int maxNoOfCredentials = 8; //hardcoded in the card as well. Map<URI, SmartcardBlob> blobs = getBlobs(pin); for(int credID = 1; credID <= maxNoOfCredentials ; credID++){ boolean foundCredID = false; for(URI uri : blobs.keySet()){ if(blobs.get(uri).blob[0] == credID && blobs.get(uri).blob.length == 1){ URI possibleCredURI = URI.create(uri.toString()+"_1"); if(blobs.containsKey(possibleCredURI)){ //there really is a credential with this ID foundCredID = true; continue; }else{ //at some point in an issuance, something went wrong, and we only have the "URI to ID blob". //Delete the URI to ID blob and return the credID this.deleteBlob(pin, uri); return (byte)credID; } } } if(foundCredID){ continue; } return (byte)credID; } throw new RuntimeException("No more than "+maxNoOfCredentials+" credentials can be stored. remove one and try again."); } private byte getNewIssuerID(URI issuerUri){ return staticMap.getIssuerIDFromUri(issuerUri); } /** * Stores the ID at position 2 in the blob in order to differentiate between stored credentials and stored issuers * @param pin * @param uri * @param ID */ public SmartcardStatusCode storeIssuerUriAndID(int pin, URI uri, byte ID){ SmartcardBlob blob = new SmartcardBlob(); blob.blob = new byte[2]; blob.blob[1] = ID; return this.storeBlob(pin, uri, blob); } private SmartcardStatusCode storeCredentialUriAndID(int pin, URI uri, byte ID){ SmartcardBlob blob = new SmartcardBlob(); blob.blob = new byte[1]; blob.blob[0] = ID; return this.storeBlob(pin, uri, blob); } @Override public void removeCredentialUri(int pin, URI uri){ int i = 1; while(true){ URI tmpUri = URI.create(uri.toString()+"_"+i++); if(this.deleteBlob(pin, tmpUri) != SmartcardStatusCode.OK){ if(i == 1){ //Actual error - we should be able to remove at least 1 blob throw new RuntimeException("Could not delete blob: " + tmpUri); } return; }else{ System.out.println("intermediate step, removed credential blob: "+ tmpUri); } } } private boolean isAndroid() { try { ByteBuffer buf = ByteBuffer.allocate(5); buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, (byte) this.isAndroid, 0, 0, 0}); buf.position(0); System.out.println("Input to isAndroid: " + Arrays.toString(buf.array())); ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); System.out.println("Reponse from isAndroid: " + response); return (this.evaluateStatus(response) == SmartcardStatusCode.OK); } catch (CardException e) { return false; } } public int getMode(){ try { ByteBuffer buf = ByteBuffer.allocate(5); buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, this.getMode, 0, 0, 1}); buf.position(0); System.out.println("Input to GetMode: " + Arrays.toString(buf.array())); ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); System.out.println("Reponse from getMode: " + response); if(this.evaluateStatus(response) == SmartcardStatusCode.OK){ return response.getData()[0]; } } catch (CardException e) { // TODO Auto-generated catch block e.printStackTrace(); } return -1; } public String getVersion(){ try { ResponseAPDU response = this.transmitCommand(new CommandAPDU(this.ABC4TRUSTCMD, this.getVersion, 0, 0, 64)); System.out.println("Response from getVersion: " + response); if(this.evaluateStatus(response) == SmartcardStatusCode.OK){ String res = ""; byte[] data = response.getData(); for(int i = 0; i < 64; i++){ res += (char)(data[i] & 0xFF); } return res; } } catch (CardException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } public SmartcardStatusCode setVirginMode(byte[] mac){ try { ResponseAPDU response = this.transmitCommand(new CommandAPDU(this.ABC4TRUSTCMD, this.setVirginMode, 0, 0, mac)); System.out.println("response from setVirginMode: " + response); return this.evaluateStatus(response); } catch (CardException e) { System.err.println("Failed to setVirginMode : " + e); return SmartcardStatusCode.NOT_FOUND; } } public SmartcardStatusCode setRootMode(byte[] accesscode){ try { ByteBuffer buf = ByteBuffer.allocate(13); buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, this.setRootMode, 0, 0, 8}); buf.put(accesscode); buf.position(0); System.out.println("Input to setRootMode: " + Arrays.toString(buf.array())); ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); System.out.println("response from setRootMode: " + response); return this.evaluateStatus(response); } catch (CardException e) { return SmartcardStatusCode.NOT_FOUND; } } public SmartcardStatusCode setWorkingMode(){ ByteBuffer buf = ByteBuffer.allocate(4); buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, this.setWorkingMode, 0, 0}); buf.position(0); try { System.out.println("Input for setWorkingMode: " + Arrays.toString(buf.array())); ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); System.out.println("response from setWorkingMode: " + response); return this.evaluateStatus(response); } catch (CardException e) { return SmartcardStatusCode.NOT_FOUND; } } private void putData(byte[] data){ try { ByteBuffer buf = ByteBuffer.allocate(7 + data.length); buf.put((byte) this.ABC4TRUSTCMD); buf.put(this.putData); buf.put(new byte[]{0,0,0}); buf.put(this.intLengthToShortByteArr(data.length)); buf.put(data); buf.position(0); System.out.println("Input to PutData: "+ Arrays.toString(buf.array())); ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); System.out.println("Response from putData: " + response); } catch (CardException e) { e.printStackTrace(); } } @Override public RSAVerificationKey readAuthenticationKey(int pin, int keyID){ byte[] data = new byte[5]; System.arraycopy(this.pinToByteArr(pin), 0, data, 0, 4); data[4] = (byte)keyID; ByteBuffer buffer = ByteBuffer.allocate(14); buffer.put(new byte[]{(byte) this.ABC4TRUSTCMD, this.readAuthenticationKey, 0, 0, 0, 0, 5}); buffer.put(data); buffer.put(new byte[]{0, 0}); buffer.position(0); try { ResponseAPDU response = this.transmitCommand(new CommandAPDU(buffer)); System.out.println("Response from readAuthenticationKey: "+ response); if(this.evaluateStatus(response) == SmartcardStatusCode.OK){ RSAVerificationKey vkey = new RSAVerificationKey(); vkey.n = new BigInteger(1, response.getData()); return vkey; } return null; } catch (CardException e) { e.printStackTrace(); return null; } } private byte[] removeSignBit(byte[] positiveNumber){ if(positiveNumber[0] == 0){ byte[] tmp = new byte[positiveNumber.length-1]; System.arraycopy(positiveNumber, 1, tmp, 0, tmp.length); return tmp; }else{ return positiveNumber; } } public SmartcardStatusCode setAuthenticationKey(BigInteger pk, int keyID, RSAKeyPair rootKey){ byte[] pk_bytes = pk.toByteArray(); pk_bytes = removeSignBit(pk_bytes); ResponseAPDU response; try { int mode = this.getMode(); if(mode == 1){ this.putData(pk_bytes); }else if(mode == 2){ System.out.println("Can only use setAuthenticationKey in root mode"); return SmartcardStatusCode.UNAUTHORIZED; } System.out.println("Input for setAuthKey: " + Arrays.toString(new byte[]{(byte)this.ABC4TRUSTCMD, this.setAuthenticationKey, 0, 0, 1, (byte)keyID})); response = this.transmitCommand(new CommandAPDU(this.ABC4TRUSTCMD, this.setAuthenticationKey, 0, 0, new byte[]{(byte)keyID})); System.out.println("response from setAuthKey: " + response); return this.evaluateStatus(response); } catch (CardException e) { return SmartcardStatusCode.NOT_FOUND; } } /** * * @param mode * @param component * @param groupID * @param compType (0: mod, 1: order, 2: co-factor) * @param rootKey * @return * @throws CardException */ private SmartcardStatusCode setGroupComponent(int mode, byte[] component, int groupID, int compType, RSAKeyPair rootKey) throws CardException{ component = removeSignBit(component); byte[] data = new byte[2]; data[0] = (byte)groupID; data[1] = (byte)compType; if(mode == 1){ this.putData(component); }else{ System.out.println("Can only use setGroupComponent in root mode"); return SmartcardStatusCode.UNAUTHORIZED; } ByteBuffer buf = ByteBuffer.allocate(7); buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, this.setGroupComponent, 0, 0, 2, (byte)groupID, (byte)compType}); buf.position(0); System.out.println("Input for set Group Component: " + Arrays.toString(buf.array())); ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); System.out.println("Response from setGroupComponent: " + response); return this.evaluateStatus(response); } private SmartcardStatusCode setGenerator(int mode, byte[] g, int groupID, int genID, RSAKeyPair rootKey) throws CardException{ g = this.removeSignBit(g); byte[] data = new byte[2]; data[0] = (byte)groupID; data[1] = (byte)genID; if(mode == 1){ this.putData(g); }else{ System.out.println("Can only use setGenerator in root mode"); return SmartcardStatusCode.UNAUTHORIZED; } ByteBuffer buf = ByteBuffer.allocate(7); buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, this.setGenerator, 0, 0, 2, (byte)groupID, (byte)genID}); buf.position(0); System.out.println("Input for set Generator: " + Arrays.toString(buf.array())); ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); System.out.println("Response from setGenerator: " + response); return this.evaluateStatus(response); } private SmartcardStatusCode setCounter(int counterID, int keyID, int index, int threshold, byte[] cursor, RSAKeyPair rootKey){ if(cursor.length != 4){ throw new RuntimeException("Cursor should be of length 4"); } byte[] data = new byte[8]; data[0] = (byte)counterID; data[1] = (byte)keyID; data[2] = (byte)index; data[3] = (byte)threshold; System.arraycopy(cursor, 0, data, 4, 4); try { int mode = this.getMode(); if(mode == 2){ System.out.println("Can only use setCounter in root mode"); return SmartcardStatusCode.UNAUTHORIZED; } ByteBuffer buf = ByteBuffer.allocate(13); buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, this.setCounter, 0, 0, 8}); buf.put(data); buf.position(0); System.out.println("Input for setCounter: " + Arrays.toString(buf.array())); ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); System.out.println("Response from setCounter: " + response); return this.evaluateStatus(response); } catch (CardException e) { return SmartcardStatusCode.NOT_FOUND; } } public byte[] getChallenge(int size){ //TODO: Make this work for challenge sizes of 256 (or 0) if((size > 256) || (size < 1)){ System.err.println("Argument 'size' for getChallenge should be in the range [1,256]"); return null; } try { int realSize = size; if(realSize == 256){ realSize = 0; } ByteBuffer buf = ByteBuffer.allocate(7); System.out.println("Le: " + (byte)size); buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, this.getChallenge, 0, 0, 1, (byte)realSize, 0}); buf.position(0); System.out.println("Input for getChallenge: " + Arrays.toString(buf.array())); ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); System.out.println("Response from getChallenge: " + response); if(this.evaluateStatus(response) == SmartcardStatusCode.OK){ return response.getData(); }else{ return null; } } catch (CardException e) { return null; } } @Override public boolean wasInit() { int mode = this.getMode(); if((mode == 0) || (mode == 1)){ return false; }else{ return true; } } @Override public URI getDeviceURI(int pin) { try { SmartcardBlob blob = this.getBlob(pin, Smartcard.device_name); if(blob == null){ return null; } return URI.create(new String(blob.blob, "US-ASCII")); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return null; } @Override public short getDeviceID(int pin){ try { ResponseAPDU response = this.transmitCommand(new CommandAPDU(this.ABC4TRUSTCMD, this.getDeviceID, 0, 0, this.pinToByteArr(pin), 2)); System.out.println("Response from getdeviceID: " + response); if(this.evaluateStatus(response) == SmartcardStatusCode.OK){ return ByteBuffer.wrap(response.getData()).getShort(); } } catch (CardException e) { e.printStackTrace(); } return -1; } @Override public TrustedIssuerParameters getIssuerParametersOfCredential(int pin, URI credentialId) { byte credID = this.getCredentialIDFromUri(pin, credentialId); byte[] cred_info = this.readCredential(pin, credID); byte issuerID = cred_info[0]; CryptoEngine engine = CryptoEngine.UPROVE; if(credentialId.toString().startsWith("IdmxCredential")){ engine = CryptoEngine.IDEMIX; } return this.getIssuerParameters(pin, this.getIssuerUriFromID(pin, issuerID, engine)); } SystemParameters cachedSystemParams = null; @Override public SystemParameters getSystemParameters(int pin) { if(cachedSystemParams!=null) { return cachedSystemParams; } //here we need to get the prime modulus p, the generator g and the subgroup order SystemParameters params = new SystemParameters(); params.p = this.getGroupComponent(pin, 0, 0); params.subgroupOrder = this.getGroupComponent(pin, 0, 1); params.g = this.getGenerator(pin, 0, 1); System.out.println("Fetched System Parameters p, q and g: "); cachedSystemParams = params; return params; } Map<String, BigInteger> cachedGroupComponent = new HashMap<String, BigInteger>(); /** * * @param pin * @param groupID * @param compType 0: modulus, 1: group order 2: cofactor * @return */ private BigInteger getGroupComponent(int pin, int groupID, int compType){ if(cachedGroupComponent.containsKey(groupID + ":" + compType)) { BigInteger cached = cachedGroupComponent.get(groupID + ":" + compType); System.out.println("Cached readGroupComponent: " + groupID + " : " + compType + " : " + cached); return cached; } ByteBuffer buf = ByteBuffer.allocate(15); buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, this.readGroupComponent, 0, 0, 0, 0, 6}); buf.put(this.pinToByteArr(pin)); buf.put(new byte[]{(byte)groupID, (byte)compType, 0, 0}); buf.position(0); try { if(printInput) System.out.println("Input for readGroupComponent: " + groupID + " : " + compType + " : " + Arrays.toString(buf.array())); TimingsLogger.logTiming("HardwareSmartcard.transmitCommand(readGroupComponent)", true); ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); TimingsLogger.logTiming("HardwareSmartcard.transmitCommand(readGroupComponent)", false); System.out.println("Response from readGroupComponent: " + response); if(this.evaluateStatus(response) == SmartcardStatusCode.OK){ BigInteger groupComponent = new BigInteger(1, response.getData()); System.out.println("GroupComponent - is : " + groupID + " : " + compType + " : " + groupComponent); cachedGroupComponent.put(groupID + ":" + compType, groupComponent); return groupComponent; } } catch (CardException e) { e.printStackTrace(); } return null; } Map<String, BigInteger> cachedGenerator = new HashMap<String, BigInteger>(); private BigInteger getGenerator(int pin, int groupID, int genID){ if(cachedGenerator.containsKey(groupID + ":" + genID)) { BigInteger cached = cachedGenerator.get(groupID + ":" + genID); System.out.println("Cached readGenerator: " + groupID + " : " + genID + " : " + cached); return cached; } ByteBuffer buf = ByteBuffer.allocate(15); buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, this.readGenerator, 0, 0, 0, 0, 6}); buf.put(this.pinToByteArr(pin)); buf.put(new byte[]{(byte)groupID, (byte)genID, 0, 0}); buf.position(0); try { if(printInput) System.out.println("Input for readGenerator: " + groupID + " : " + genID + " : " + Arrays.toString(buf.array())); TimingsLogger.logTiming("HardwareSmartcard.transmitCommand(readGenerator)", true); ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); TimingsLogger.logTiming("HardwareSmartcard.transmitCommand(readGenerator)", false); System.out.println("Response from readGenerator: " + response); if(this.evaluateStatus(response) == SmartcardStatusCode.OK){ BigInteger generator = new BigInteger(1, response.getData()); System.out.println("Generator - is : " + groupID + " : " + genID + " : " + generator); cachedGenerator.put(groupID + ":" + genID, generator); return generator; } } catch (CardException e) { e.printStackTrace(); } return null; } Map<URI,BigInteger> cachedScopeExclusivePseudonym = new HashMap<URI, BigInteger>(); @Override public BigInteger computeScopeExclusivePseudonym(int pin, URI scope) { if(cachedScopeExclusivePseudonym.containsKey(scope)) { BigInteger pv = cachedScopeExclusivePseudonym.get(scope); System.out.println("Cached from getScopeExclusivePseudonym: " + scope + " : " + pv); return pv; } try { byte[] scopeBytes = this.uriToByteArr(scope); if(scopeBytes.length > 2044){ throw new RuntimeException("The inputted scope is too large."); } byte[] begin = new byte[]{(byte)this.ABC4TRUSTCMD, this.getScopeExclusivePseudonym, 0, 0, 0}; ByteBuffer buf = ByteBuffer.allocate(9+4+scopeBytes.length); buf.put(begin); buf.put(this.intLengthToShortByteArr(4+scopeBytes.length)); buf.put(this.pinToByteArr(pin)); buf.put(scopeBytes); buf.put(new byte[]{0,0}); buf.position(0); if(printInput) System.out.println("Input for getScopeExclusivePseudonym: " + Arrays.toString(buf.array())); TimingsLogger.logTiming("HardwareSmartcard.transmitCommand(getScopeExclusivePseudonym)", true); ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); TimingsLogger.logTiming("HardwareSmartcard.transmitCommand(getScopeExclusivePseudonym)", false); System.out.println("Response from getScopeExclusivePseudonym: " + response); if(this.evaluateStatus(response) == SmartcardStatusCode.OK){ BigInteger pv = new BigInteger(1, response.getData()); cachedScopeExclusivePseudonym.put(scope, pv); return pv; } return null; } catch (CardException e) { e.printStackTrace(); return null; } } @Override public BigInteger computeDevicePublicKey(int pin) { ByteBuffer buf = ByteBuffer.allocate(13); buf.put(new byte[]{(byte) this.ABC4TRUSTCMD, this.getDevicePublicKey, 0, 0, 0, 0, 4}); buf.put(this.pinToByteArr(pin)); buf.put(new byte[]{0, 0}); buf.position(0); try { TimingsLogger.logTiming("HardwareSmartcard.transmitCommand(getDevicePublicKey)", true); ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); TimingsLogger.logTiming("HardwareSmartcard.transmitCommand(getDevicePublicKey)", false); System.out.println("Response from getDevicePublicKey: " + response); if(this.evaluateStatus(response) == SmartcardStatusCode.OK){ return new BigInteger(1, response.getData()); } } catch (CardException e) { e.printStackTrace(); return null; } return null; } @Override public ZkProofCommitment prepareZkProof(int pin, Set<URI> credentialIds, Set<URI> scopeExclusivePseudonyms, boolean includeDevicePublicKeyProof) { TimingsLogger.logTiming("HardwareSmartcard.prepareZkProof", true); ZkProofCommitment comm = new ZkProofCommitment(); SystemParameters params = this.getSystemParameters(pin); comm.spec = new ZkProofSpecification(params); comm.spec.parametersForPseudonyms = params; comm.spec.credentialBases = new HashMap<URI, SmartcardParameters>(); comm.spec.credFragment = new HashMap<URI, BigInteger>(); for(URI courseId: credentialIds) { byte credID = this.getCredentialIDFromUri(pin, courseId); byte[] cred = this.readCredential(pin, credID); byte issuerID = cred[0]; SmartcardParameters groupParams = this.getGroupParameters(pin, issuerID); comm.spec.credentialBases.put(courseId, groupParams); comm.spec.credFragment.put(courseId, this.computeCredentialFragment(pin, courseId)); } comm.spec.scopeExclusivePseudonymValues = new HashMap<URI, BigInteger>(); byte[] data = new byte[5]; System.arraycopy(this.pinToByteArr(pin), 0, data, 0, 4); data[4] = 1; //ProverID - TODO: hardcoded to 1 as of now. Assuming there can be only 1 for the pilot ByteBuffer buf = ByteBuffer.allocate(10); buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, this.startCommitments, 0, 0, 5}); buf.put(data); //buf.put((byte)16); buf.position(0); try { if(printInput) System.out.println("Input for startCommitments: " + Arrays.toString(buf.array())); TimingsLogger.logTiming("HardwareSmartcard.transmitCommand(startCommitments)", true); ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); TimingsLogger.logTiming("HardwareSmartcard.transmitCommand(startCommitments)", false); System.out.println("Response from startCommitments: "+response); if(this.evaluateStatus(response) != SmartcardStatusCode.OK){ return null; } } catch (CardException e) { throw new RuntimeException("PrepareZkProof crashed.", e); } //ProofStatus set to 1 if(includeDevicePublicKeyProof){ comm.spec.devicePublicKey = this.computeDevicePublicKey(pin); comm.commitmentForDevicePublicKey = this.computeDevicePublicKeyCommitment(pin); } boolean notEnoughAttendance = false; for(URI uri : credentialIds){ byte credID = this.getCredentialIDFromUri(pin, uri); byte[] credInfo = readCredential(pin, credID); //byte issuerID = credInfo[0]; //byte counterID = this.readIssuer(pin, issuerID)[4]; byte status = credInfo[5]; byte presentOrIssuance = this.getIssuanceCommitment; String command = "getIssuanceCommitment"; //System.out.println("\nStatus of credential before commitments are made: " + status); if(status == 2){ //credential has already been issued. So we assume we want to present it. command = "getPresentationCommitment"; presentOrIssuance = this.getPresentationCommitment; } /* if(counterID != 0){ //Counter active. We must know if the attendance is high enough. byte[] counterInfo = readCounter(pin, counterID); int index = counterInfo[1]; int threshold = counterInfo[2]; if(index < threshold && presentOrIssuance == this.getPresentationCommitment){ //Not enough attendance. aborting at the end; Done because of timing attacks. notEnoughAttendance = true; } } */ buf = ByteBuffer.allocate(14); buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, presentOrIssuance, 0, 0, 0, 0, 5}); buf.put(this.pinToByteArr(pin)); buf.put(credID); buf.put(new byte[]{0,0}); buf.position(0); try { if(printInput) System.out.println("Input for "+command+": " +Arrays.toString(buf.array())); TimingsLogger.logTiming("HardwareSmartcard.transmitCommand("+command+")", true); ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); TimingsLogger.logTiming("HardwareSmartcard.transmitCommand("+command+")", false); System.out.println("Response from "+command+": "+response); if(this.evaluateStatus(response) == SmartcardStatusCode.OK){ comm.commitmentForCreds.put(uri, new BigInteger(1, response.getData())); }else{ return null; } } catch (CardException e) { throw new RuntimeException("PrepareZkProof crashed.", e); } } for(URI scope : scopeExclusivePseudonyms){ BigInteger pseudonymCommitment = this.getScopeExclusiveCommitment(pin, scope); comm.commitmentForScopeExclusivePseudonyms.put(scope, pseudonymCommitment); comm.spec.scopeExclusivePseudonymValues.put(scope, this.computeScopeExclusivePseudonym(pin, scope)); } if(notEnoughAttendance){ System.out.println("Because of not enough attendance?"); TimingsLogger.logTiming("HardwareSmartcard.prepareZkProof", false); return null; }else{ TimingsLogger.logTiming("HardwareSmartcard.prepareZkProof", false); return comm; } } private SmartcardParameters getGroupParameters(int pin, byte groupID) { BigInteger g1 = this.getGenerator(pin, groupID, 1); BigInteger g2 = this.getGenerator(pin, groupID, 2); BigInteger n = this.getGroupComponent(pin, groupID, 0); BigInteger q = this.getGroupComponent(pin, groupID, 1); return new SmartcardParameters(n, q, g1, g2); } /** * * @param pin * @param credentialID * @return byte array containing: issuerID || size(v) [2 bytes] || size(kv) [2 bytes] || status || prescount */ private byte[] readCredential(int pin, int credentialID){ byte[] data = new byte[5]; System.arraycopy(this.pinToByteArr(pin), 0, data, 0, 4); data[4] = (byte)credentialID; ByteBuffer buf = ByteBuffer.allocate(11); buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, this.readCredential, 0, 0, 5}); buf.put(data); buf.put((byte)7); buf.position(0); try { if(printInput) System.out.println("Input for readCredential: " + Arrays.toString(buf.array())); System.out.println("Reading the on-board credential with ID="+credentialID); ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); System.out.println("Response from readCredential: " + response); System.out.println("With the data: " + Arrays.toString(response.getData())); if(this.evaluateStatus(response) == SmartcardStatusCode.OK){ return response.getData(); } } catch (CardException e) { e.printStackTrace(); } return null; } Map<Integer, byte[]> cachedIssuerByteArray = new HashMap<Integer, byte[]>(); /** * @param pin * @param issuerID * @return byte array containing: groupID || genID1 || genID2 || numpres || counterID */ private byte[] readIssuer(int pin, int issuerID){ if(cachedIssuerByteArray.containsKey(issuerID)) { byte[] cached = cachedIssuerByteArray.get(issuerID); System.out.println("ReadIssuer - use cached : " + (cached == null ? null : Arrays.toString(cached))); return cached; } byte[] data = new byte[5]; System.arraycopy(this.pinToByteArr(pin), 0, data, 0, 4); data[4] = (byte)issuerID; ByteBuffer buf = ByteBuffer.allocate(11); buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, this.readIssuer, 0, 0, 5}); buf.put(data); buf.put((byte)5); buf.position(0); try { if(printInput) System.out.println("Input for readIssuer: " + Arrays.toString(buf.array())); ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); System.out.println("Response from readIssuer: " + response); System.out.println("With the data: "+Arrays.toString(response.getData())); if(this.evaluateStatus(response) == SmartcardStatusCode.OK){ cachedIssuerByteArray.put(issuerID, response.getData()); return response.getData(); } } catch (CardException e) { e.printStackTrace(); } cachedIssuerByteArray.put(issuerID, null); return null; } private BigInteger computeDevicePublicKeyCommitment(int pin) { ByteBuffer buf = ByteBuffer.allocate(13); buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, this.getDeviceCommitment, 0, 0, 0, 0, 4}); buf.put(this.pinToByteArr(pin)); buf.put(new byte[]{0, 0}); buf.position(0); try { if(printInput) System.out.println("Input for getDeviceCommitment: " + Arrays.toString(buf.array())); TimingsLogger.logTiming("HardwareSmartcard.transmitCommand(getDeviceCommitment)", true); ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); TimingsLogger.logTiming("HardwareSmartcard.transmitCommand(getDeviceCommitment)", false); System.out.println("Response from getDeviceCommitment: " + response); if(this.evaluateStatus(response) == SmartcardStatusCode.OK){ System.out.println("And this is the output: " + Arrays.toString(response.getData())); System.out.println("Or this bigInt: " + new BigInteger(1, response.getData())); return new BigInteger(1, response.getData()); } } catch (CardException e) { e.printStackTrace(); } return null; } private BigInteger getScopeExclusiveCommitment(int pin, URI scope){ byte[] uri = this.uriToByteArr(scope); ByteBuffer buf = ByteBuffer.allocate(13+uri.length); buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, this.getScopeExclusiveCommitment, 0, 0, 0}); buf.put(this.intLengthToShortByteArr(4+uri.length)); buf.put(this.pinToByteArr(pin)); buf.put(uri); buf.put(new byte[]{0,0}); buf.position(0); try { if(printInput) System.out.println("Input for getScopeExclusiveCommitment: " + Arrays.toString(buf.array())); TimingsLogger.logTiming("HardwareSmartcard.transmitCommand(getScopeExclusiveCommitment)", true); ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); TimingsLogger.logTiming("HardwareSmartcard.transmitCommand(getScopeExclusiveCommitment)", false); System.out.println("Response from getScopeExclusiveCommitment: "+response); if(this.evaluateStatus(response) == SmartcardStatusCode.OK){ return new BigInteger(1, response.getData()); }else{ throw new RuntimeException("Failed scope exclusive Commitment. Card answered: " + response); } } catch (CardException e) { throw new RuntimeException("getScopeExclusiveCommitment crashed.", e); } } private byte[] getChallengeBytesFromChallenge(BigInteger c){ byte[] c_bytes = c.toByteArray(); byte[] res = new byte[32]; if(c_bytes.length > 32){ res = this.removeSignBit(c_bytes); }else if(c_bytes.length == 32){ res = c_bytes; }else{ System.arraycopy(c_bytes, 0, res, 32-c_bytes.length, c_bytes.length); } System.out.println("Challenge comming in: "+c); System.out.println("Challenge going out: "+new BigInteger(1, res)); System.out.println("The same? "+c.equals(new BigInteger(1, res))); return res; } @Override public ZkProofResponse finalizeZkProof(int pin, BigInteger challenge, Set<URI> credentialIDs, Set<URI> scopeExclusivePseudonyms) { byte[] challenge_bytes = this.getChallengeBytesFromChallenge(challenge); System.out.println("Challenge bytes length after removing sign bit: "+challenge_bytes.length); byte[] data = new byte[4+1+challenge_bytes.length]; //pin, prooverID, challenge System.arraycopy(this.pinToByteArr(pin), 0, data, 0, 4); data[4] = 1; //TODO: ProoverID - Hardcoded for now System.out.println("data length: " + data.length); System.arraycopy(challenge_bytes, 0, data, 4+1, challenge_bytes.length); ByteBuffer buf = ByteBuffer.allocate(7 + data.length); buf.put(new byte[]{(byte) this.ABC4TRUSTCMD, this.startResponses, 0, 0, 0}); buf.put(this.intLengthToShortByteArr(data.length)); buf.put(data); buf.position(0); if(printInput) System.out.println("Input for startResponses: " + Arrays.toString(buf.array())); try { ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); System.out.println("Response from startResponses: "+response ); System.out.println("And this is the output: " + Arrays.toString(response.getData())); if(this.evaluateStatus(response) != SmartcardStatusCode.OK){ return null; } } catch (CardException e) { e.printStackTrace(); return null; } ZkProofResponse zkpr = new ZkProofResponse(); zkpr.responseForDeviceSecret = this.computeDevicePublicKeyResponse(pin); //For Get issuance response for(URI uri : credentialIDs){ byte credID = this.getCredentialIDFromUri(pin, uri); byte[] credInfo = readCredential(pin, credID); byte status = credInfo[5]; String command = "getIssuanceResponse"; byte issueOrPresent = this.getIssuanceResponse; if(status >= 2){ System.out.println("Presentation. Status: " + status); //credential has already been issued, so we want to present response. command = "getPresentationResponse"; issueOrPresent = this.getPresentationResponse; } buf = ByteBuffer.allocate(14); buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, issueOrPresent, 0, 0, 0 ,0, 5}); buf.put(this.pinToByteArr(pin)); buf.put(credID); buf.put(new byte[]{0, 0}); buf.position(0); try { if(printInput) System.out.println("Input for "+command+": " + Arrays.toString(buf.array())); ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); System.out.println("Response from "+command+": " + response); if(this.evaluateStatus(response) != SmartcardStatusCode.OK){ return null; } System.out.println("data returned: size: "+response.getData().length+" value: " + Arrays.toString(response.getData())); byte[] zx = new byte[response.getNr()/2]; byte[] zv = new byte[response.getNr()/2]; System.arraycopy(response.getData(), 0, zx, 0, zx.length); System.arraycopy(response.getData(), zx.length, zv, 0, zv.length); System.out.println("zx: " + Arrays.toString(zx)); System.out.println("zv: " + Arrays.toString(zv)); boolean idemix = true; byte issuerID = credInfo[0]; byte[] issuerInfo = this.readIssuer(pin, issuerID); byte genID2 = issuerInfo[2]; if(genID2 == 0){ idemix = false; } if(idemix){ System.out.println("Info: this is an IDEMIX ISSUER. We thus set zv: " + new BigInteger(1, zv)); zkpr.responseForCourses.put(uri, new BigInteger(1, zv)); zkpr.responseForDeviceSecret = new BigInteger(1, zx); }else{ System.out.println("Info: this is an UPROVE ISSUER. We thus DO NOT set zv to anything. Instead we use the device response as zx"); //Do nothing } } catch (CardException e) { e.printStackTrace(); return null; } } return zkpr; } private BigInteger computeDevicePublicKeyResponse(int pin) { ByteBuffer buf = ByteBuffer.allocate(13); buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, this.getDeviceResponse, 0, 0, 0, 0, 4}); buf.put(this.pinToByteArr(pin)); buf.put(new byte[]{0, 0}); buf.position(0); try { if(printInput) System.out.println("Input for getDeviceResponse: " + Arrays.toString(buf.array())); ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); System.out.println("Response from getDeviceResponse: " + response); System.out.println("And this is the output: " + Arrays.toString(response.getData())); System.out.println("which gives this BigInteger: " + new BigInteger(1, response.getData())); if(this.evaluateStatus(response) == SmartcardStatusCode.OK){ return new BigInteger(1, response.getData()); } } catch (CardException e) { e.printStackTrace(); } return null; } Map<URI, BigInteger> cachedCredentialFragment = new HashMap<URI, BigInteger>(); @Override public BigInteger computeCredentialFragment(int pin, URI credentialId) { //fragment is equal to the public key of a credential if(cachedCredentialFragment.containsKey(credentialId)) { BigInteger cached = cachedCredentialFragment.get(credentialId); System.out.println("Cached getCredentialPublicKey: " + credentialId + " - " + cached); return cached; } int credID = this.getCredentialIDFromUri(pin, credentialId); ByteBuffer buf = ByteBuffer.allocate(14); buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, this.getCredentialPublicKey, 0, 0, 0, 0, 5}); buf.put(this.pinToByteArr(pin)); buf.put((byte)credID); buf.put(new byte[]{0, 0}); buf.position(0); try { if(printInput) System.out.println("Input for getCredentialPublicKey: " + credentialId + " : " + Arrays.toString(buf.array())); TimingsLogger.logTiming("HardwareSmartcard.transmitCommand(getCredentialPublicKey)", true); ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); TimingsLogger.logTiming("HardwareSmartcard.transmitCommand(getCredentialPublicKey)", false); System.out.println("Response from getCredentialPublicKey (fragment): " + response); if(this.evaluateStatus(response) == SmartcardStatusCode.OK){ System.out.println("And this is the output: " + Arrays.toString(response.getData())); BigInteger credentialFragment = new BigInteger(1, response.getData()); System.out.println("which gives this BigInteger: " + credentialFragment); cachedCredentialFragment.put(credentialId, credentialFragment); return credentialFragment; } } catch (CardException e) { e.printStackTrace(); } return null; } @Override public boolean credentialExists(int pin, URI credentialUri) { byte credentialID; try{ credentialID = this.getCredentialIDFromUri(pin, credentialUri); }catch(Exception e){ return false; } byte[] credInfo = this.readCredential(pin, credentialID); return credInfo != null; } @Override public SmartcardStatusCode storeCredential(int pin, URI credentialId, Credential cred, CredentialSerializer serializer){ //this.detectMaxBlobSize(pin); byte[] credBytes = serializer.serializeCredential(cred); System.out.println("CredBytes length: " + credBytes.length); int nextCredBlobUri = 1; SmartcardStatusCode returnCode = SmartcardStatusCode.OK; int bytesLeft = credBytes.length; int i = 0; boolean done = false; while(!done){ SmartcardBlob blob = new SmartcardBlob(); if(bytesLeft > max_blob_bytes){ blob.blob = new byte[max_blob_bytes]; bytesLeft -= max_blob_bytes; System.arraycopy(credBytes, i*max_blob_bytes, blob.blob, 0, max_blob_bytes); }else{ blob.blob = new byte[bytesLeft]; System.arraycopy(credBytes, i*max_blob_bytes, blob.blob, 0, bytesLeft); done = true; //We know we are done as we put the last bytes in the blob. } URI credUri = URI.create(credentialId.toASCIIString()+"_"+nextCredBlobUri++); System.out.println("storing a blob of size: " + blob.blob.length + " with uri: " + credUri.toASCIIString()); returnCode = storeBlob(pin, credUri, blob); if(returnCode != SmartcardStatusCode.OK){ return returnCode; } i++; } return returnCode; } @Override public SmartcardStatusCode storePseudonym(int pin, URI pseudonymId, PseudonymWithMetadata pseudo, PseudonymSerializer serializer){ //this.detectMaxBlobSize(pin); byte[] pseudoBytes = serializer.serializePseudonym(pseudo); System.out.println("PseudoBytes length: " + pseudoBytes.length); int nextPseudoBlobUri = 1; SmartcardStatusCode returnCode = SmartcardStatusCode.OK; int bytesLeft = pseudoBytes.length; int i = 0; boolean done = false; while(!done){ SmartcardBlob blob = new SmartcardBlob(); if(bytesLeft > max_blob_bytes){ blob.blob = new byte[max_blob_bytes]; bytesLeft -= max_blob_bytes; System.arraycopy(pseudoBytes, i*max_blob_bytes, blob.blob, 0, max_blob_bytes); }else{ blob.blob = new byte[bytesLeft]; System.arraycopy(pseudoBytes, i*max_blob_bytes, blob.blob, 0, bytesLeft); done = true; //We know we are done as we put the last bytes in the blob. } URI credUri = URI.create(pseudonymId.toASCIIString()+"_"+nextPseudoBlobUri++); System.out.println("storing a blob of size: " + blob.blob.length + " with uri: " + credUri.toASCIIString()); returnCode = storeBlob(pin, credUri, blob); if(returnCode != SmartcardStatusCode.OK){ return returnCode; } i++; } return returnCode; } @Override public PseudonymWithMetadata getPseudonym(int pin, URI pseudonymUID, PseudonymSerializer serializer){ ByteArrayOutputStream accumulatedPseuBytes = new ByteArrayOutputStream(); return getPseudonym(pin, pseudonymUID, 1, accumulatedPseuBytes, serializer); } private PseudonymWithMetadata getPseudonym(int pin, URI pseudonymUID, int nextPseuBlobUriId, ByteArrayOutputStream accumulatedPseuBytes, PseudonymSerializer serializer){ System.out.println("Accumulated this many bytes: " + accumulatedPseuBytes.size()); URI nextPseuBlobUri = URI.create(pseudonymUID.toASCIIString()+"_"+nextPseuBlobUriId); System.out.println("getting this uri: " + nextPseuBlobUri.toASCIIString()); SmartcardBlob scBlob = this.getBlob(pin, nextPseuBlobUri); if(scBlob == null){ return serializer.unserializePseudonym(accumulatedPseuBytes.toByteArray(), pseudonymUID); } byte[] blob = scBlob.blob; accumulatedPseuBytes.write(blob, 0, blob.length); if(blob.length < max_blob_bytes){ return serializer.unserializePseudonym(accumulatedPseuBytes.toByteArray(), pseudonymUID); }else{ //next round return getPseudonym(pin, pseudonymUID, nextPseuBlobUriId+1, accumulatedPseuBytes, serializer); } } @Override public SmartcardStatusCode deletePseudonym(int pin, URI pseudonymUri){ int i = 1; while(true){ pseudonymUri = URI.create(pseudonymUri.toString()+"_"+i++); SmartcardStatusCode code = this.deleteBlob(pin, pseudonymUri); if(code != SmartcardStatusCode.OK){ return code; } } } @Override public Credential getCredential(int pin, URI credentialId, CredentialSerializer serializer){ //this.detectMaxBlobSize(pin); ByteArrayOutputStream accumulatedCredBytes = new ByteArrayOutputStream(); return getCredential(pin, credentialId, 1, accumulatedCredBytes, serializer); } private Credential getCredential(int pin, URI credentialId, int nextCredBlobUriId, ByteArrayOutputStream accumulatedCredBytes, CredentialSerializer serializer){ System.out.println("Accumulated this many bytes: " + accumulatedCredBytes.size()); URI nextCredBlobUri = URI.create(credentialId.toASCIIString()+"_"+nextCredBlobUriId); System.out.println("getting this uri: " + nextCredBlobUri.toASCIIString()); SmartcardBlob scBlob = this.getBlob(pin, nextCredBlobUri); if(scBlob == null){ return serializer.unserializeCredential(accumulatedCredBytes.toByteArray(), credentialId, this.getDeviceURI(pin)); } byte[] blob = scBlob.blob; accumulatedCredBytes.write(blob, 0, blob.length); if(blob.length < max_blob_bytes){ //return new CredentialSerializerGzipXml().unserializeCredential(accumulatedCredBytes.toByteArray()); return serializer.unserializeCredential(accumulatedCredBytes.toByteArray(), credentialId, this.getDeviceURI(pin)); }else{ //next round return getCredential(pin, credentialId, nextCredBlobUriId+1, accumulatedCredBytes, serializer); } } @Override public SmartcardStatusCode allocateCredential(int pin, URI credentialId, URI issuerParameters) { byte[] credIdBytes = null; credIdBytes = this.uriToByteArr(credentialId); if(credIdBytes.length > 199){ return SmartcardStatusCode.REQUEST_URI_TOO_LONG; } byte issuerID = this.getIssuerIDFromUri(pin, issuerParameters); byte newCredentialID = this.getNewCredentialID(pin); if(newCredentialID == (byte)-1){ return SmartcardStatusCode.INSUFFICIENT_STORAGE; } ByteBuffer buf = ByteBuffer.allocate(11); buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, this.setCredential, 0, 0, 6}); buf.put(this.pinToByteArr(pin)); buf.put(newCredentialID); buf.put(issuerID); buf.position(0); try { if(printInput) System.out.println("Input for setCredential: " + Arrays.toString(buf.array())); ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); System.out.println("Response from setCredential: " + response); if(this.evaluateStatus(response) != SmartcardStatusCode.OK){ return this.evaluateStatus(response); } } catch (CardException e) { e.printStackTrace(); return SmartcardStatusCode.NOT_FOUND; } //Then store the mapping from credentialURI to credentialID: TimingsLogger.logTiming("HardwareSmartcard.storeCredentialUriAndID", true); SmartcardStatusCode code = this.storeCredentialUriAndID(pin, credentialId, newCredentialID); TimingsLogger.logTiming("HardwareSmartcard.storeCredentialUriAndID", false); if(code != SmartcardStatusCode.OK){ System.err.println("Credential stored correctly on card, but storing the Uri/ID failed with code: " + code); return code; } return SmartcardStatusCode.OK; } @Override public SmartcardStatusCode deleteCredential(int pin, URI credentialId) { byte credID = this.getCredentialIDFromUri(pin, credentialId); ByteBuffer buf = ByteBuffer.allocate(10); buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, this.removeCredential, 0, 0, 5}); buf.put(this.pinToByteArr(pin)); buf.put(credID); buf.position(0); try { System.out.println("Removing credential with uri: " + credentialId); this.deleteBlob(pin, credentialId); if(credentialId.toString().startsWith(CREDENTIAL_PREFIX)){ URI reloadURI = URI.create(credentialId.toString()+UPROVE_RELOAD_URI_POSTFIX); if(reloadURI.toString().contains(":") && !reloadURI.toString().contains("_")){ reloadURI = URI.create(reloadURI.toString().replaceAll(":", "_")); //change all ':' to '_' } this.deleteBlob(pin, reloadURI); System.out.println("deleted the reload blob of the credential: " + reloadURI); } this.removeCredentialUri(pin, credentialId); if(printInput) System.out.println("Input for removeCredential: " + Arrays.toString(buf.array())); System.out.println("Trying to remove on-board credential with ID="+credID); ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); System.out.println("response from RemoveCredential: " + response); return this.evaluateStatus(response); } catch (CardException e) { return SmartcardStatusCode.NOT_FOUND; } } @Override public boolean smartcardPresent() { try { return this.terminal.isCardPresent(); } catch (CardException ex) { return false; } } @Override public int init(int newPin, SystemParameters pseuParams, RSAKeyPair rootKey, short deviceId) { if(this.wasInit()){ return -1; } try { byte[] deviceID = ByteBuffer.allocate(2).putShort(deviceId).array(); this.setAuthenticationKey(rootKey.getN(), 0, null); byte[] deviceKeySize = this.intLengthToShortByteArr(pseuParams.deviceSecretSizeBytes); byte[] idAndDeviceKeySize = new byte[]{deviceID[0], deviceID[1], deviceKeySize[0], deviceKeySize[1]}; ByteBuffer buf = ByteBuffer.allocate(13); buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, this.initializeDevice, 0, 0, 0, 0, 4}); buf.put(idAndDeviceKeySize); buf.put(new byte[]{0,0}); buf.position(0); if(printInput) System.out.println("Input to initialize device: " + Arrays.toString(buf.array())); ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); if(this.evaluateStatus(response) != SmartcardStatusCode.OK){ return -1; } byte[] pinAndPuk = SmartcardCrypto.decrypt(response.getData(), rootKey); byte[] pin = new byte[4]; byte[] puk = new byte[8]; System.arraycopy(pinAndPuk, 0, pin, 0, 4); System.arraycopy(pinAndPuk, 4, puk, 0, 8); String ipin = "", ipuk = ""; for(int i = 0; i < 4; i++){ ipin += (char)(pin[i] & 0xFF); } for(int i = 0; i < 8; i++){ ipuk += (char)(puk[i] & 0xFF); } if(this.changePin(Integer.parseInt(ipin), newPin) != SmartcardStatusCode.OK){ System.out.println("Could not change pin."); return -1; } System.out.println("Now initializing group stuff"); int mode = this.getMode(); if(this.setGroupComponent(mode, pseuParams.p.toByteArray(), 0, 0, null) != SmartcardStatusCode.OK){ return -1; } if(this.setGroupComponent(mode, pseuParams.subgroupOrder.toByteArray(), 0, 1, null) != SmartcardStatusCode.OK){ return -1; } BigInteger f = pseuParams.p.subtract(BigInteger.ONE).divide(pseuParams.subgroupOrder); //cofactor this.setGroupComponent(mode, f.toByteArray(), 0, 2, null); //then add a generator of the subgroup q if(this.setGenerator(mode, pseuParams.g.toByteArray(), 0, 1, null) != SmartcardStatusCode.OK){ return -1; } //set prover byte[] data = new byte[5+MAX_CREDENTIALS+1]; data[0] = 1; //id 1 int ksize = pseuParams.zkChallengeSizeBytes*2+pseuParams.zkStatisticalHidingSizeBytes; byte[] ksize_bytes = this.intLengthToShortByteArr(ksize); data[1] = ksize_bytes[0]; data[2] = ksize_bytes[1]; // as large as the subgroup order is -1 to prevent overflow. int csize = pseuParams.zkChallengeSizeBytes; byte[] csize_bytes = this.intLengthToShortByteArr(csize); data[3] = csize_bytes[0]; data[4] = csize_bytes[1]; // challenge size: 256 bit = 32 bytes (as per default in SystemParameters) for(int i = 0; i <= MAX_CREDENTIALS; i++){ //0 means it accepts both credentials and scope-exclusive stuff. //1,2,3,... means it accepts credentials with id 1,2,3,... data[i+5] = (byte)i; } buf = ByteBuffer.allocate(5+data.length); buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, this.setProver, 0, 0, (byte)data.length}); buf.put(data); buf.position(0); System.out.println("Input to prover: " + Arrays.toString(buf.array())); response = this.transmitCommand(new CommandAPDU(buf)); System.out.println("Response from setProver: " + response); if(this.evaluateStatus(response) != SmartcardStatusCode.OK){ return -1; } //After init, one should call setIssuer which creates a group and counter. return Integer.parseInt(ipuk); } catch (CardException e) { e.printStackTrace(); return -1; } } @Override public int pinTrialsLeft() { try { ResponseAPDU response = this.transmitCommand(new CommandAPDU(this.ABC4TRUSTCMD, this.pinTrialsLeft, 0, 0, 1)); if(this.evaluateStatus(response) == SmartcardStatusCode.OK){ return response.getData()[0]; } } catch (CardException e) { e.printStackTrace(); } return -1; } @Override public int pukTrialsLeft() { try { ResponseAPDU response = this.transmitCommand(new CommandAPDU(this.ABC4TRUSTCMD, this.pukTrialsLeft, 0, 0, 1)); if(this.evaluateStatus(response) == SmartcardStatusCode.OK){ return response.getData()[0]; } } catch (CardException e) { e.printStackTrace(); } return -1; } @Override public SmartcardStatusCode resetPinWithPuk(int puk, int newPin) { byte[] data = new byte[8+4]; System.arraycopy(this.pukToByteArr(puk), 0, data, 0, 8); System.arraycopy(this.pinToByteArr(newPin), 0, data, 8, 4); try { ResponseAPDU response = this.transmitCommand(new CommandAPDU(this.ABC4TRUSTCMD, this.resetPin, 0, 0, data)); System.out.println("response from resetPinWithPuk: " + response); return this.evaluateStatus(response); } catch (CardException e) { return SmartcardStatusCode.NOT_FOUND; } } Map<URI, SmartcardBlob> blobCache = new HashMap<URI, SmartcardBlob>(); @Override public SmartcardStatusCode storeBlob(int pin, URI uri, SmartcardBlob blob) { //this.resetCard(); String[] forbiddenChars = new String[]{"\u0167", ":", "*", "?", "<", ">", " ", "|"}; if(uri.toString().contains(":") && !uri.toString().contains("_")){ uri = URI.create(uri.toString().replaceAll(":", "_")); //change all ':' to '_' }else{ for(int i = 0; i < forbiddenChars.length; i++){ if(uri.toString().contains(forbiddenChars[i])){ throw new RuntimeException("Cannot store a blob under a URI containing the following char: " +forbiddenChars[i]); } } } byte[] uriBytes = null; uriBytes = this.uriToByteArr(uri); if(uriBytes.length > 199){ return SmartcardStatusCode.REQUEST_URI_TOO_LONG; } // BLOB CACHE! blobCache.put(uri, blob); blobUrisCache.add(uri); //first put data from blob followed by the STORE BLOB command this.putData(blob.blob); byte[] data = new byte[4+uriBytes.length]; System.arraycopy(this.pinToByteArr(pin), 0, data, 0, 4); System.arraycopy(uriBytes, 0, data, 4, uriBytes.length); ByteBuffer buf = ByteBuffer.allocate(9+uriBytes.length); buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, this.storeBlob, 0, 0, (byte)data.length}); buf.put(data); buf.position(0); try { if(printInput) System.out.println("Input for storeBlob: " + Arrays.toString(buf.array())); ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); System.out.println("Response from storeBlob: " + response); if((response.getSW1() != STATUS_OK) && (response.getSW1() != STATUS_BAD_PIN)){ throw new InsufficientStorageException("Could not store blob. Response from card: " + response); } return this.evaluateStatus(response); } catch (CardException e) { e.printStackTrace(); return null; } } @Override public SmartcardStatusCode deleteBlob(int pin, URI uri) { byte[] uriBytes = null; uriBytes = this.uriToByteArr(uri); if(uriBytes.length > 199){ return SmartcardStatusCode.REQUEST_URI_TOO_LONG; } // BLOB CACHE! blobCache.remove(uri); blobUrisCache.remove(uri); byte[] data = new byte[4+uriBytes.length]; System.arraycopy(this.pinToByteArr(pin), 0, data, 0, 4); System.arraycopy(uriBytes, 0, data, 4, uriBytes.length); ByteBuffer buf = ByteBuffer.allocate(9+uriBytes.length); buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, this.removeBlob, 0, 0, (byte)data.length}); buf.put(data); buf.position(0); try { if(printInput) System.out.println("Input for removeBlob: "+Arrays.toString(buf.array())); ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); System.out.println("Response from removeBlob: " + response); return this.evaluateStatus(response); } catch (CardException e) { e.printStackTrace(); return null; } } @Override public Map<URI, SmartcardBlob> getBlobs(int pin) { TimingsLogger.logTiming("HardwareSmartcard.getBlobs", true); Set<URI> uris = this.getBlobUris(pin); Map<URI, SmartcardBlob> result = new HashMap<URI, SmartcardBlob>(); for(URI uri : uris){ result.put(uri, this.getBlob(pin, uri)); } TimingsLogger.logTiming("HardwareSmartcard.getBlobs", false); return result; } private boolean loadedBlobUris = false; private Set<URI> blobUrisCache = new HashSet<URI>(); @Override public Set<URI> getBlobUris(int pin) { //TODO: Works only if the total length of URIs is less than 2048-#URIs-2 Set<URI> uris = new HashSet<URI>(); if(loadedBlobUris){ System.out.println("Returning the cached blob uris: "+blobUrisCache); Set<URI> cached = new HashSet<URI>(); cached.addAll(blobUrisCache); return cached; } byte nread = 0; int eternalLoopPreventer = 0; while(true){ byte[] readInfo = this.getBlobUrisHelper(pin, uris, nread); nread = readInfo[0]; if(readInfo[1] == 0){ loadedBlobUris = true; blobUrisCache.clear(); blobUrisCache.addAll(uris); return uris; } eternalLoopPreventer++; if(eternalLoopPreventer > 1000){ //meaning if the card can store more than 1MB, we're fucked, but it cant... return null; } } } /** * Returns the number of uris read, no of uris remaining to be read. */ private byte[] getBlobUrisHelper(int pin, Set<URI> uris, byte nread){ ByteBuffer buf = ByteBuffer.allocate(14); buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, this.listBlobs, 0, 0, 0, 0, 5}); buf.put(this.pinToByteArr(pin)); buf.put(new byte[]{nread, 0, 0}); //first arg is how many URIs we read so far. buf.position(0); try { if(printInput) System.out.println("Input for listBlobs: " + Arrays.toString(buf.array())); ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); System.out.println("Response from listBlobs: " + response); if(this.evaluateStatus(response) != SmartcardStatusCode.OK){ return null; } byte[] data = response.getData(); System.out.println("data: " + Arrays.toString(data)); int index = 0; while(true){ if((index+2) == data.length){ //at the end, so the last two bytes is the updated number of read URIs and the number of unread URIs // System.out.println("data.length: " + data.length); // System.out.println("index: " + index); nread = data[index]; byte unread = data[index+1]; System.out.println("nread: " + nread); System.out.println("unread: " + unread); return new byte[]{nread, unread}; }else{ byte uriSize = data[index]; byte[] uri = new byte[uriSize]; System.arraycopy(data, index+1, uri, 0, uriSize); uris.add(this.byteArrToUri(uri)); index += uriSize+1; } } } catch (CardException e) { e.printStackTrace(); return null; } } @Override public SmartcardBlob getBlob(int pin, URI uri) { //this.resetCard(); uri = URI.create(uri.toString().replaceAll(":", "_")); byte[] uriBytes = this.uriToByteArr(uri); if(uriBytes.length > 199){ throw new RuntimeException("URI is too long. Cannot have been stored on smartcard."); } // BLOB CACHE! if(blobCache.containsKey(uri)) { SmartcardBlob cached = blobCache.get(uri); System.out.println("Cached readBlob: " + uri + " : " + cached.blob.length); // Arrays.toString(cached.blob)); return cached; } ByteBuffer buf = ByteBuffer.allocate(9+4+uriBytes.length); buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, this.readBlob, 0, 0, 0}); buf.put(this.intLengthToShortByteArr(uriBytes.length+4)); buf.put(this.pinToByteArr(pin)); buf.put(uriBytes); buf.put(new byte[]{0, 0}); buf.position(0); try { if(printInput) System.out.println("Input for readBlob: " + Arrays.toString(buf.array())); ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); System.out.println("Response from readBlob: " + response); if(this.evaluateStatus(response) == SmartcardStatusCode.OK){ SmartcardBlob blob = new SmartcardBlob(); blob.blob = response.getData(); // BLOB CACHE! blobCache.put(uri, blob); return blob; }else{ return null; } } catch (CardException e) { e.printStackTrace(); return null; } } @Override public SmartcardStatusCode changePin(int pin, int newPin) { byte[] data = new byte[8]; System.arraycopy(this.pinToByteArr(pin), 0, data, 0, 4); System.arraycopy(this.pinToByteArr(newPin), 0, data, 4, 4); try { ByteBuffer buf = ByteBuffer.allocate(13); buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, this.changePin, 0, 0, 8}); buf.put(data); buf.position(0); if(printInput) System.out.println("Input for changePin: " + Arrays.toString(buf.array())); ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); System.out.println("Response from changePin: " + response); return this.evaluateStatus(response); } catch (CardException e) { e.printStackTrace(); return SmartcardStatusCode.NOT_FOUND; } } @Override public Set<URI> listCredentialsUris(int pin) { Set<URI> credUris = new HashSet<URI>(); Set<URI> blobs = this.getBlobUris(pin); for(URI uri: blobs){ String uriString = uri.toString(); System.out.println("Comparing: "+uriString+" to "+CREDENTIAL_PREFIX); if((uriString.startsWith(CREDENTIAL_PREFIX)) && uriString.endsWith("_1")){ URI credURI = URI.create(uri.toString().substring(0, uri.toString().length()-2)); System.out.println("listCredentialUris - added a cred uri: " + credURI); credUris.add(credURI); } } return credUris; } @SuppressWarnings("unused") private List<Byte> listCounters(int pin) { ByteBuffer buf = ByteBuffer.allocate(10); buf.put(new byte[]{(byte) this.ABC4TRUSTCMD, this.listCounters, 0, 0, 4}); buf.put(this.pinToByteArr(pin)); buf.put(new byte[]{0}); buf.position(0); try { if(printInput) System.out.println("Input for listCounters: " + Arrays.toString(buf.array())); ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); System.out.println("Response from listCounters: " + response); if(this.evaluateStatus(response) == SmartcardStatusCode.OK){ List<Byte> counters = new ArrayList<Byte>(); byte[] counterIDs = response.getData(); for (byte counterID : counterIDs) { counters.add(counterID); } return counters; } } catch (CardException e) { e.printStackTrace(); } return null; } @Override public Set<Course> listCourses(int pin) { Set<TrustedIssuerParameters> issuers = this.getIssuerParametersList(pin); Set<Course> courses = new HashSet<Course>(); for(TrustedIssuerParameters params: issuers){ courses.add(params.course); } return courses; } @Override public Course getCourse(int pin, URI issuerUri) { byte issuerID = this.getIssuerIDFromUri(pin, issuerUri); byte[] issuerInfo = this.readIssuer(pin, issuerID); byte counterID = issuerInfo[4]; if (counterID == 0) { return null; } byte[] counterInfo = this.readCounter(pin, counterID); if(counterInfo == null){ return null; } byte threshold = counterInfo[2]; byte keyID = counterInfo[0]; return new Course(counterID, issuerUri, threshold, keyID); } @Override public TrustedIssuerParameters getIssuerParameters(int pin, URI paramsUri) { int issuerID = this.getIssuerIDFromUri(pin, paramsUri); byte[] issuerData = this.readIssuer(pin, issuerID); byte groupID = issuerData[0]; byte genID1 = issuerData[1]; byte genID2 = issuerData[2]; byte counterID = issuerData[4]; BigInteger p = this.getGroupComponent(pin, groupID, 0); BigInteger q = this.getGroupComponent(pin, groupID, 1); BigInteger base1 = this.getGenerator(pin, groupID, genID1); BigInteger base2 = this.getGenerator(pin, groupID, genID2); SmartcardParameters groupParams = new SmartcardParameters(p, q, base1, base2); if (counterID == 0) { return new TrustedIssuerParameters(paramsUri, groupParams); } byte[] counterInfo = this.readCounter(pin, counterID); if(counterInfo == null){ return new TrustedIssuerParameters(paramsUri, groupParams); }else{ int keyID = counterInfo[0]; int index = counterInfo[1]; int threshold = counterInfo[2]; byte[] cursor = new byte[4]; System.arraycopy(counterInfo, 3, cursor, 0, 4); int lectureID = ByteBuffer.wrap(cursor).getInt(); TrustedIssuerParameters tip = new TrustedIssuerParameters(counterID, paramsUri, groupParams, threshold, keyID); if(index > 0) tip.course.activate(); for(int i = 1; i < index; i++){ tip.course.updateLectureId(i); //update courses lecture count by all but one } tip.course.updateLectureId(lectureID); //update to the real lecture ID return tip; } } /** * * @param pin * @param counterID * @return the 7-byte stream keyID || index || threshold || cursor(4 bytes). */ private byte[] readCounter(int pin, int counterID){ byte[] data = new byte[5]; System.arraycopy(this.pinToByteArr(pin), 0, data, 0, 4); data[4] = (byte)counterID; ByteBuffer buf = ByteBuffer.allocate(11); buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, this.readCounter, 0, 0, 5}); buf.put(data); buf.put((byte)7); buf.position(0); try { if(printInput) System.out.println("Input for readCounter: " + Arrays.toString(buf.array())); ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); System.out.println("Response from readCounter: " + response); System.out.println("With data: " + Arrays.toString(response.getData())); if(this.evaluateStatus(response) == SmartcardStatusCode.OK){ return response.getData(); }else{ return null; } } catch (CardException e) { e.printStackTrace(); return null; } } @Override public int getCounterValue(int pin, URI issuerId){ TrustedIssuerParameters tip = this.getIssuerParameters(pin, issuerId); return tip.course.getLectureCount(); } @Override public Set<TrustedIssuerParameters> getIssuerParametersList(int pin) { // ByteBuffer buf = ByteBuffer.allocate(10); // buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, this.listIssuers, 0, 0, 4}); // buf.put(this.pinToByteArr(pin)); // buf.put((byte)0); // buf.position(0); // try { // ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); // System.out.println("Response from listIssuers: " + response); // if(this.evaluateStatus(response) != SmartcardStatusCode.OK){ // return null; // } // byte[] issuerIDs = response.getData(); // Set<TrustedIssuerParameters> issuers = new HashSet<TrustedIssuerParameters>(); // for(int i = 0; i < response.getNr(); i++){ // issuers.add(this.getIssuerParameters(pin, this.getIssuerUriFromID(pin, issuerIDs[i]))); // } // return issuers; // } catch (CardException e) { // e.printStackTrace(); // return null; // } throw new NotImplementedException("This method is unused so far, and needs a few changes to the interface before it works. If needed, it can be fixed"); } protected static final String salt = "very salty!"; @Override public SmartcardBackup backupAttendanceData(int pin, String password) { SmartcardBackup backup = new SmartcardBackup(); byte[] password_bytes = Utils.passwordToByteArr(password); if(password_bytes == null){ return null; } try { //We backup the counters. counterID, index and cursor is encrypted, but //the threshold and keyID is hidden. Thus we need to save those along with the counterID //in cleartext. We assume that the key is put on the card in the initialization phase. ByteBuffer buf = ByteBuffer.allocate(18); buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, this.backupCounters, 0, 0, 0x0C}); buf.put(this.pinToByteArr(pin)); buf.put(password_bytes); buf.put((byte)0); buf.position(0); ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); System.out.println("Response from backupCounters: " + response); if(this.evaluateStatus(response) == SmartcardStatusCode.OK){ backup.macCounters = response.getData(); }else{ backup.macCounters = null; } } catch (CardException e) { e.printStackTrace(); return null; } return backup; } private static SecretKeySpec getSecretKeySpec(String salt, String password) throws Exception{ //generate key byte[] key = (salt + password).getBytes(); // Need to pad key for AES MessageDigest dig = MessageDigest.getInstance("SHA-256"); byte[] paddedKey = dig.digest(key); byte[] cutKey = new byte[16]; System.arraycopy(paddedKey, 0, cutKey, 0, 16); // Generate the secret key specs. SecretKeySpec secretKeySpec = new SecretKeySpec(cutKey, "AES"); return secretKeySpec; } public static Cipher getAESKey(String salt, String password, byte[] IV, boolean encrypt){ try{ // Get the SecretKeySpec SecretKeySpec secretKeySpec = getSecretKeySpec(salt, password); // Instantiate the cipher Cipher cipher = Cipher.getInstance("AES/CBC/pkcs5padding"); if(encrypt){ cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, new IvParameterSpec(IV)); }else{ cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(IV)); } return cipher; }catch(Exception e){ e.printStackTrace(); return null; } } @Override public SmartcardStatusCode restoreAttendanceData(int pin, String password, SmartcardBackup backup) { try { ByteBuffer buf; if(backup.macCounters != null && backup.macCounters.length != 0){ buf = ByteBuffer.allocate(17); buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, this.restoreCounters, 0, 0, 0x0C}); buf.put(this.pinToByteArr(pin)); buf.put(Utils.passwordToByteArr(password)); buf.position(0); this.putData(backup.macCounters); //put the encrypted data in the buffer ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); System.out.println("Response from restoreCounters: " + response); if(this.evaluateStatus(response) != SmartcardStatusCode.OK){ return this.evaluateStatus(response); } } } catch (CardException e) { e.printStackTrace(); return SmartcardStatusCode.NOT_FOUND; } return SmartcardStatusCode.OK; } public List<Byte> listCredentialIDs(int pin) { ByteBuffer buf = ByteBuffer.allocate(10); buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, this.listCredentials, 0, 0, 4}); buf.put(this.pinToByteArr(pin)); buf.put((byte)0); buf.position(0); try { if(printInput) System.out.println("Input for listCredentials: " + Arrays.toString(buf.array())); ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); System.out.println("Response from listCredentials: " + response); if(this.evaluateStatus(response) == SmartcardStatusCode.OK){ List<Byte> credentialIDs = new ArrayList<Byte>(); byte[] creds = response.getData(); for (byte cred : creds) { credentialIDs.add(cred); } return credentialIDs; } } catch (CardException e) { e.printStackTrace(); return null; } return null; } @Override public SmartcardStatusCode deleteIssuer(int pin, URI issuerParameters, RSAKeyPair rootKey) { if(this.getMode() != 1){ System.out.println("Can only use deleteIssuer in root mode"); return SmartcardStatusCode.UNAUTHORIZED; } byte issuerID = this.getIssuerIDFromUri(pin, issuerParameters); try { ResponseAPDU response = this.transmitCommand(new CommandAPDU(this.ABC4TRUSTCMD, this.removeIssuer, 0, 0, new byte[]{issuerID})); return this.evaluateStatus(response); } catch (CardException e) { return SmartcardStatusCode.NOT_FOUND; } } @Override public byte[] getNewNonceForSignature() { //Not used - unless we rename getChallenge to this method name, but that would be wrong I think. return this.getChallenge(16); } @Override public SmartcardStatusCode addIssuerParametersWithAttendanceCheck(RSAKeyPair rootKey, URI parametersUri, int keyIDForCounter, SmartcardParameters credBases, RSAVerificationKey courseKey, int minimumAttendance) { byte issuerID = this.getNewIssuerID(parametersUri); byte groupID = issuerID; byte genID1 = 1; byte genID2 = 0; if(parametersUri.toString().contains("idemix")){ System.out.println("Adding Idemix issuer, so adding a secondary generator"); genID2 = 2; if(credBases.getBaseForCredentialSecretOrNull() == null){ System.err.println("No second generator is found within the credBases."); return SmartcardStatusCode.NOT_FOUND; } } byte numPres = 0; //unlimited presentations - limit not used in the pilot byte counterID = issuerID; ByteBuffer buf = ByteBuffer.allocate(11); //SET ISSUER(BYTE issuerID, groupID, genID1, genID2, numpres, counterID) byte[] data = new byte[]{issuerID, groupID, genID1, genID2, numPres, counterID}; buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, this.setIssuer, 0, 0, 6}); buf.put(data); buf.position(0); try { // Before setting the issuer, we must create a group, generators as well as a counter int mode = this.getMode(); SmartcardStatusCode status; status = setupCredentialBases(rootKey, credBases, groupID, genID1, genID2, mode); if(status != SmartcardStatusCode.OK) { return status; } byte[] cursor = this.getNewCursor(0); //Create a new key with keyID that counter can use. this.setAuthenticationKey(courseKey.n, keyIDForCounter, rootKey); this.setCounter(counterID, keyIDForCounter, 0, minimumAttendance, cursor, rootKey); //prior to the actual command,if we are in working mode, //we have to authenticate the input data first. if(mode == 2){ System.out.println("Can only use addIssuerParameters in root mode"); return SmartcardStatusCode.UNAUTHORIZED; } System.out.println("Input to setIssuer: " + Arrays.toString(buf.array())); ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); System.out.println("Response from setIssuer: " + response); if(evaluateStatus(response) == SmartcardStatusCode.OK){ // SmartcardStatusCode code = this.storeIssuerUriAndID(pin, parametersUri, issuerID); // if(code != SmartcardStatusCode.OK){ // System.err.println("Could not store the issuerURI and ID on the card, but the issuer itself is still stored on the card. Returned code: " + code); // return code; // } } return this.evaluateStatus(response); } catch (CardException e) { //TODO: Error handling. Remove stuff again if something fails. e.printStackTrace(); return SmartcardStatusCode.NOT_FOUND; } } private SmartcardStatusCode setupCredentialBases(RSAKeyPair rootKey, SmartcardParameters credBases, byte groupID, byte genID1, byte genID2, int mode) throws CardException { SmartcardStatusCode status; status = this.setGroupComponent(mode, credBases.getModulus().toByteArray(), groupID, 0, rootKey); if (status != SmartcardStatusCode.OK) { return status; } if (credBases.getOrderOrNull() != null) { status = this.setGroupComponent(mode, credBases.getOrderOrNull().toByteArray(), groupID, 1, rootKey); if (status != SmartcardStatusCode.OK) { return status; } status = this.setGroupComponent(mode, credBases.getCofactorOrNull().toByteArray(), groupID, 2, rootKey); if (status != SmartcardStatusCode.OK) { return status; } } status = this.setGenerator(mode, credBases.getBaseForDeviceSecret().toByteArray(), groupID, genID1, rootKey); if (status != SmartcardStatusCode.OK) { return status; } if (credBases.getBaseForCredentialSecretOrNull() != null) { status = this.setGenerator(mode, credBases.getBaseForCredentialSecretOrNull().toByteArray(), groupID, genID2, rootKey); if (status != SmartcardStatusCode.OK) { return status; } } return status; } /* public SmartcardStatusCode addUProveIssuerParametersWithAttendanceCheck(RSAKeyPair rootKey, URI parametersUri, int keyIDForCounter, UProveParams uProveParams, RSAVerificationKey courseKey, int minimumAttendance) { byte issuerID = this.getNewIssuerID(parametersUri); byte groupID = issuerID; byte genID1 = 1; byte genID2 = 0; //Not used in UProve, thus set to 0. byte numPres = 0; //unlimited presentations - limit not used in the pilot byte counterID = issuerID; ByteBuffer buf = ByteBuffer.allocate(11); //SET ISSUER(BYTE issuerID, groupID, genID1, genID2, numpres, counterID) byte[] data = new byte[]{issuerID, groupID, genID1, genID2, numPres, counterID}; buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, this.setIssuer, 0, 0, 6}); buf.put(data); buf.position(0); try { //Before setting the issuer, we must create a group, generators as well as a counter int mode = this.getMode(); this.setGroupComponent(mode, uProveParams.p.toByteArray(), groupID, 0, rootKey); this.setGroupComponent(mode, uProveParams.q.toByteArray(), groupID, 1, rootKey); this.setGroupComponent(mode, uProveParams.f.toByteArray(), groupID, 2, rootKey); System.out.println("p: " + uProveParams.p); System.out.println("q: " + uProveParams.q); System.out.println("g: " + uProveParams.g); System.out.println("f: " + uProveParams.f); this.setGenerator(mode, uProveParams.g.toByteArray(), groupID, genID1, rootKey); byte[] cursor = this.getNewCursor(0); //Create a new key with keyID that counter can use. this.setAuthenticationKey(courseKey.n, keyIDForCounter, rootKey); this.setCounter(counterID, keyIDForCounter, 0, minimumAttendance, cursor, rootKey); //prior to the actual command,if we are in working mode, //we have to authenticate the input data first. if(mode == 2){ System.out.println("Can only use addIssuerParameters in root mode"); return SmartcardStatusCode.UNAUTHORIZED; } System.out.println("Input for setIssuer: " +Arrays.toString(buf.array())); ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); System.out.println("Response from setIssuer: " + response); if(evaluateStatus(response) == SmartcardStatusCode.OK){ // SmartcardStatusCode code = this.storeIssuerUriAndID(pin, parametersUri, issuerID); // if(code != SmartcardStatusCode.OK){ // System.err.println("Could not store the issuerURI and ID on the card, but the issuer itself is still stored on the card. Returned code: " + code); // return code; // } } return this.evaluateStatus(response); } catch (CardException e) { //TODO: Error handling. Remove stuff again if something fails. e.printStackTrace(); return SmartcardStatusCode.NOT_FOUND; } } */ @Override public SmartcardStatusCode addIssuerParameters(RSAKeyPair rootKey, URI parametersUri, SmartcardParameters credBases) { byte issuerID = this.getNewIssuerID(parametersUri); byte groupID = issuerID; byte genID1 = 1;//R0 byte genID2 = 2;//S byte numPres = 0; //unlimited presentations - limit not used in the pilot byte counterID = 0; //no counter present ByteBuffer buf = ByteBuffer.allocate(11); //SET ISSUER(BYTE issuerID, groupID, genID1, genID2, numpres, counterID) byte[] data = new byte[]{issuerID, groupID, genID1, genID2, numPres, counterID}; buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, this.setIssuer, 0, 0, 6}); buf.put(data); buf.position(0); try { //Before setting the issuer, we must create a group with generators. Idemix uses unknown order. int mode = this.getMode(); SmartcardStatusCode status; status = setupCredentialBases(rootKey, credBases, groupID, genID1, genID2, mode); if(status != SmartcardStatusCode.OK) { return status; } //prior to the actual command, if we are in working mode, //we have to authenticate the input data first. if(mode == 2){ System.out.println("Can only use addIssuerParameters in root mode"); return SmartcardStatusCode.UNAUTHORIZED; } System.out.println("Input for set Issuer: " +Arrays.toString(buf.array())); ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); System.out.println("Response from setIssuer: " + response); return evaluateStatus(response); } catch (CardException e) { //TODO: Error handling. Remove stuff again if something fails. e.printStackTrace(); return SmartcardStatusCode.NOT_FOUND; } } /* @Override public SmartcardStatusCode addUProveIssuerParameters(RSAKeyPair rootKey, URI parametersUri, UProveParams uProveParams){ byte issuerID = this.getNewIssuerID(parametersUri); byte groupID = issuerID; byte genID1 = 1; byte genID2 = 0; byte numPres = 0; //unlimited presentations - limit not used in the pilot byte counterID = 0; //no counter present ByteBuffer buf = ByteBuffer.allocate(11); //SET ISSUER(BYTE issuerID, groupID, genID1, genID2, numpres, counterID) byte[] data = new byte[]{issuerID, groupID, genID1, genID2, numPres, counterID}; buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, this.setIssuer, 0, 0, 6}); buf.put(data); buf.position(0); try { //Before setting the issuer, we must create a group, generators as well as a counter int mode = this.getMode(); this.setGroupComponent(mode, uProveParams.p.toByteArray(), groupID, 0, rootKey); this.setGroupComponent(mode, uProveParams.q.toByteArray(), groupID, 1, rootKey); this.setGroupComponent(mode, uProveParams.f.toByteArray(), groupID, 2, rootKey); this.setGenerator(mode, uProveParams.g.toByteArray(), groupID, genID1, rootKey); //prior to the actual command, if we are in working mode, //we have to authenticate the input data first. if(mode == 2){ System.out.println("Can only use addIssuerParameters in root mode"); return SmartcardStatusCode.UNAUTHORIZED; } System.out.println("Input for setIssuer: " +Arrays.toString(buf.array())); ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); System.out.println("Response from setIssuer: " + response); return this.evaluateStatus(response); } catch (CardException e) { //TODO: Error handling. Remove stuff again if something fails. e.printStackTrace(); return SmartcardStatusCode.NOT_FOUND; } } */ @Override public SmartcardStatusCode incrementCourseCounter(int pin, RSAKeyPair key, URI issuerId, int lectureId) { //First check if the counter is enabled. //TODO! // TrustedIssuerParameters tip = this.getIssuerParameters(pin, issuerId); // if(!tip.course.isActivated()){ // if(!tip.course.updateLectureId(lectureId)){ // //Course not yet issued! // return SmartcardStatusCode.NOT_MODIFIED; // } // } //auth data should be counterID||cursor , with cursor having the updated value. byte counterID = this.getIssuerIDFromUri(pin, issuerId); //IssuerID is the same as CounterID if the counter exists. byte keyID = this.readCounter(pin, counterID)[0]; byte[] data = new byte[5]; data[0] = counterID; System.arraycopy(this.getNewCursor(lectureId), 0, data, 1, 4); byte[] challenge = this.getNewNonceForSignature(); byte[] sig = SmartcardCrypto.generateSignature(data, challenge, key, this.rand).sig; //sig = this.removeSignBit(sig); ByteBuffer buf = ByteBuffer.allocate(7+1+sig.length); byte[] bufferLength = ByteBuffer.allocate(2).putShort((short)(sig.length+1)).array(); buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, this.incrementCounter, 0, 0, 0}); buf.put(bufferLength); buf.put(keyID); buf.put(sig); buf.position(0); try { byte[] counterInfo = this.readCounter(pin, counterID); byte index = counterInfo[1]; if(printInput) System.out.println("Input for incrementCounter: " + Arrays.toString(buf.array())); ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); System.out.println("Response from incrementCounter: " + response); if(this.evaluateStatus(response) == SmartcardStatusCode.OK){ //ensure that counter was increased or return not modified byte[] newCounterInfo = this.readCounter(pin, counterID); int newIndex = newCounterInfo[1]; if(index == newIndex){ return SmartcardStatusCode.NOT_MODIFIED; }else{ return SmartcardStatusCode.OK; } } return this.evaluateStatus(response); } catch (CardException e) { e.printStackTrace(); return SmartcardStatusCode.NOT_FOUND; } } /** * @param lectureID an ID for the lecture that is supposed to increase for each lecture. * @return 4 bytes describing the lectureID */ private byte[] getNewCursor(int lectureID){ return ByteBuffer.allocate(4).putInt(lectureID).array(); } public SmartcardStatusCode issueCredentialOnSmartcard(int pin, byte credID){ byte[] credInfo = this.readCredential(pin, credID); byte status = credInfo[5]; if(status == 0){ try { //Start commitments byte[] data = new byte[5]; System.arraycopy(this.pinToByteArr(pin), 0, data, 0, 4); data[4] = 1; //ProverID - TODO: hardcoded to 1 as of now. Assuming there can be only 1 for the pilot ByteBuffer buf = ByteBuffer.allocate(11); buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, this.startCommitments, 0, 0, 5}); buf.put(data); buf.put((byte)16); buf.position(0); if(printInput) System.out.println("Input for startCommitments: " + Arrays.toString(buf.array())); ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); System.out.println("Response from startCommitments: "+response); System.out.println("And this is the output: " + Arrays.toString(response.getData())); byte[] proofSession = response.getData(); if(this.evaluateStatus(response) != SmartcardStatusCode.OK){ return this.evaluateStatus(response); } //Issue the credential buf = ByteBuffer.allocate(14); buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, getIssuanceCommitment, 0, 0, 0, 0, 5}); buf.put(this.pinToByteArr(pin)); buf.put(credID); buf.put(new byte[]{0,0}); buf.position(0); if(printInput) System.out.println("Input for getIssuanceCommitment: " +Arrays.toString(buf.array())); response = this.transmitCommand(new CommandAPDU(buf)); System.out.println("Response from getIssuanceCommitment: "+response); if(this.evaluateStatus(response) != SmartcardStatusCode.OK){ return this.evaluateStatus(response); } //Start responses data = new byte[4+1+1+16+1]; //pin, prooverID, d which is the number of proofs, proofsession and h System.arraycopy(this.pinToByteArr(pin), 0, data, 0, 4); data[4] = 1; //TODO: ProoverID - Hardcoded for now data[5] = 1; //number of proofs - hardcoded to 1 for pilot. System.arraycopy(proofSession, 0, data, 6, 16); buf = ByteBuffer.allocate(7+data.length); buf.put(new byte[]{(byte) this.ABC4TRUSTCMD, this.startResponses, 0, 0, 0}); buf.put(this.intLengthToShortByteArr(data.length)); buf.put(data); buf.position(0); if(printInput) System.out.println("Input for startResponses: " + Arrays.toString(buf.array())); response = this.transmitCommand(new CommandAPDU(buf)); System.out.println("Response from startResponses: "+response ); if(this.evaluateStatus(response) != SmartcardStatusCode.OK){ return this.evaluateStatus(response); } //Set status of cred to 2 - issued finalized buf = ByteBuffer.allocate(14); buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, getIssuanceResponse, 0, 0, 0 ,0, 5}); buf.put(this.pinToByteArr(pin)); buf.put(credID); buf.put(new byte[]{0, 0}); buf.position(0); if(printInput) System.out.println("Input for getIssuanceResponse: " + Arrays.toString(buf.array())); response = this.transmitCommand(new CommandAPDU(buf)); System.out.println("Response from getIssuanceResponse: " + response); if(this.evaluateStatus(response) != SmartcardStatusCode.OK){ return this.evaluateStatus(response); } credInfo = this.readCredential(pin, credID); status = credInfo[5]; System.out.println("After issuing the credential with ID "+credID+", it now has status: " + status); } catch (CardException e) { throw new RuntimeException("issueCred on smartcard failed.", e); } }else{ System.out.println("Warn: Credential on sc attempted issued, but was already issued"); } return SmartcardStatusCode.OK; } }