Java tutorial
//* Licensed Materials - Property of IBM, Miracle A/S, and * //* Alexandra Instituttet A/S * //* eu.abc4trust.pabce.1.0 * //* (C) Copyright IBM Corp. 2012. All Rights Reserved. * //* (C) Copyright Miracle A/S, Denmark. 2012. All Rights Reserved. * //* (C) Copyright Alexandra Instituttet A/S, Denmark. 2012. 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.idemix.user.IdemixCryptoEngineUserImpl; import eu.abc4trust.cryptoEngine.uprove.user.ReloadStorageManager; import eu.abc4trust.cryptoEngine.uprove.user.UProveCryptoEngineUserImpl; 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 { 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. 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 = 8; public static final 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; } 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); } } } 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, GroupParameters>(); 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]; GroupParameters 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 byte[] proofSession = null; 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); 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); System.out.println("And this is the output: " + Arrays.toString(response.getData())); if (this.evaluateStatus(response) != SmartcardStatusCode.OK) { return null; } proofSession = response.getData(); } catch (CardException e) { throw new RuntimeException("PrepareZkProof crashed.", e); } //ProofStatus set to 1 comm.nonceCommitment = proofSession; 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 GroupParameters 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); GroupParameters gp; if (g2 == null) { //UPROVE BigInteger q = this.getGroupComponent(pin, groupID, 1); gp = new UProveParams(g1, n, q); } else { //IDEMIX gp = new CredentialBases(g1, g2, n); } return gp; } /** * * @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); } } @Override public ZkProofResponse finalizeZkProof(int pin, byte[] challengeHashPreimage, Set<URI> credentialIDs, Set<URI> scopeExclusivePseudonyms, byte[] nonceCommitment) { byte[] data = new byte[4 + 1 + 1 + 16 + challengeHashPreimage.length]; //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.out.println("nonce length: " + nonceCommitment.length); System.out.println("data length: " + data.length); System.arraycopy(nonceCommitment, 0, data, 6, 16); System.arraycopy(challengeHashPreimage, 0, data, 4 + 1 + 1 + 16, challengeHashPreimage.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)); zkpr.responseForCourses.put(uri, new BigInteger(1, zv)); zkpr.responseForDeviceSecret = new BigInteger(1, zx); } 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(UProveCryptoEngineUserImpl.UProveCredential)) { URI reloadURI = URI.create(credentialId.toString() + ReloadStorageManager.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(); if ((uriString.startsWith(IdemixCryptoEngineUserImpl.IdmxCredential) || uriString.startsWith(UProveCryptoEngineUserImpl.UProveCredential)) && 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) { CryptoEngine engine; if (paramsUri.toString().endsWith("uprove")) { engine = CryptoEngine.UPROVE; } else { engine = CryptoEngine.IDEMIX; } 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]; GroupParameters groupParams; BigInteger p = this.getGroupComponent(pin, groupID, 0); if (engine == CryptoEngine.IDEMIX) { BigInteger R0 = this.getGenerator(pin, groupID, genID1); BigInteger S = this.getGenerator(pin, groupID, genID2); groupParams = new CredentialBases(R0, S, p); } else { BigInteger g = this.getGenerator(pin, groupID, genID1); BigInteger q = this.getGroupComponent(pin, groupID, 1); groupParams = new UProveParams(g, p, q); } 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(); ByteBuffer buf = ByteBuffer.allocate(21); buf.put(new byte[] { (byte) this.ABC4TRUSTCMD, this.backupDevice, 0, 0, 0, 0, 0x0C }); buf.put(this.pinToByteArr(pin)); byte[] password_bytes = Utils.passwordToByteArr(password); if (password_bytes == null) { return null; } buf.put(password_bytes); buf.put(new byte[] { 0, 0 }); buf.position(0); try { //First we backup the device-specific stuff. pin, puk and deviceSecret is encrypted and //deviceID and deviceURI is stored in plain text ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); System.out.println("Response from backupDevice: " + response); if (this.evaluateStatus(response) != SmartcardStatusCode.OK) { return null; } backup.macDevice = response.getData(); backup.deviceID = this.getDeviceID(pin); backup.deviceUri = this.getDeviceURI(pin); //Then 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. // 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); // 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; // } // // List<Byte> credentials = this.listCredentialIDs(pin); // for(Byte credID : credentials){ // byte[] credInfo = this.readCredential(pin, credID); // byte status = credInfo[5]; // System.out.println("backing up credential: "+this.getCredentialUriFromID(pin, credID)+" with status: " + status); // if(status != 2){ // //Credential is either just created, and thus not backed up, // //OR done presenting (limited amount of presentations), thus not backupable. // continue; // } // buf = ByteBuffer.allocate(22); // buf.put(new byte[]{(byte)this.ABC4TRUSTCMD, (byte) this.backupCredential, 0, 0, 0, 0, 0x0D}); // buf.put(this.pinToByteArr(pin)); // buf.put(password_bytes); // buf.put(credID); // buf.put(new byte[]{0, 0}); // buf.position(0); // response = this.transmitCommand(new CommandAPDU(buf)); // System.out.println("Response from backupCredentials: " + response); // if(this.evaluateStatus(response) != SmartcardStatusCode.OK){ // return null; // } // backup.macCredentials.put(credID, response.getData()); // } //Create AES key using the PIN and Password of the user final byte[] IV = new byte[16]; new SecureRandom().nextBytes(IV); Cipher cipher = getAESKey(salt, password, IV, true); backup.IV = IV; //finally we backup the blobstore Map<URI, SmartcardBlob> blobs = this.getBlobs(pin); Map<URI, byte[]> encBlobs = new HashMap<URI, byte[]>(); try { for (URI uri : blobs.keySet()) { String uriString = uri.toString(); if (uriString.startsWith(IdemixCryptoEngineUserImpl.IdmxCredential) || uriString.startsWith(UProveCryptoEngineUserImpl.UProveCredential)) { continue; } byte[] blob = blobs.get(uri).blob; encBlobs.put(uri, cipher.doFinal(blob)); } } catch (Exception e) { throw new RuntimeException("Could not encrypt blobstore", e); } backup.blobstore = encBlobs; } 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) { //restore device URI SmartcardBlob deviceUriBlob = new SmartcardBlob(); try { deviceUriBlob.blob = backup.deviceUri.toASCIIString().getBytes("US-ASCII"); } catch (UnsupportedEncodingException e1) { return SmartcardStatusCode.BAD_REQUEST; } this.storeBlob(pin, Smartcard.device_name, deviceUriBlob); 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); } } for (byte credID : backup.macCredentials.keySet()) { buf = ByteBuffer.allocate(17); buf.put(new byte[] { (byte) this.ABC4TRUSTCMD, (byte) this.restoreCredential, 0, 0, 0x0C }); buf.put(this.pinToByteArr(pin)); buf.put(Utils.passwordToByteArr(password)); buf.position(0); if (printInput) System.out.println("Input for for restoreCredential: " + Arrays.toString(buf.array())); this.putData(backup.macCredentials.get(credID));//put the encrypted data in the buffer ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); System.out.println("Response from restoreCredential: " + response); if (this.evaluateStatus(response) != SmartcardStatusCode.OK) { return this.evaluateStatus(response); } } //restoring the blob-store, but remove the current blob-store first (in particular remove the pseudonym that no longer works.) Set<URI> blobs = this.getBlobUris(pin); for (URI uri : blobs) { this.deleteBlob(pin, uri); } try { for (URI uri : backup.blobstore.keySet()) { //decrypt blobs before putting them in. byte[] encedBlob = backup.blobstore.get(uri); Cipher cipher = getAESKey(salt, password, backup.IV, false); byte[] blob = cipher.doFinal(encedBlob); SmartcardBlob SCBlob = new SmartcardBlob(); SCBlob.blob = blob; this.storeBlob(pin, uri, SCBlob); } } catch (Exception e) { throw new RuntimeException("Could not decrypt blobs", e); } //finally restore the device - this means also restoring the pin, so using the provided pin no longer works. buf = ByteBuffer.allocate(17); buf.put(new byte[] { (byte) this.ABC4TRUSTCMD, this.restoreDevice, 0, 0, 0x0C }); buf.put(this.pinToByteArr(pin)); buf.put(Utils.passwordToByteArr(password)); buf.position(0); this.putData(backup.macDevice); //put the encrypted data in the buffer ResponseAPDU response = this.transmitCommand(new CommandAPDU(buf)); System.out.println("Response from restoreDevice: " + 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, CredentialBases credBases, RSAVerificationKey courseKey, int minimumAttendance) { byte issuerID = this.getNewIssuerID(parametersUri); byte groupID = issuerID; byte genID1 = 1; byte genID2 = 2; 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, credBases.n.toByteArray(), groupID, 0, rootKey); this.setGenerator(mode, credBases.R0.toByteArray(), groupID, genID1, rootKey); this.setGenerator(mode, credBases.S.toByteArray(), groupID, genID2, 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 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; } } 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, CredentialBases 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 = this.setGroupComponent(mode, credBases.n.toByteArray(), groupID, 0, rootKey); if (status != SmartcardStatusCode.OK) { return status; } status = this.setGenerator(mode, credBases.R0.toByteArray(), groupID, genID1, rootKey); if (status != SmartcardStatusCode.OK) { return status; } status = this.setGenerator(mode, credBases.S.toByteArray(), groupID, genID2, rootKey); 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; } }