//* 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. * //*/**/**************************************************************** package eu.abc4trust.ui.idselectservice; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.net.URLDecoder; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.concurrent.Semaphore; import java.util.logging.Level; import java.util.logging.Logger; import javax.servlet.ServletContext; import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import javax.xml.bind.JAXBElement; import eu.abc4trust.abce.external.user.UserAbcEngine; import eu.abc4trust.abce.internal.user.credentialManager.SecretNotInStorageException; import eu.abc4trust.abce.internal.user.policyCredentialMatcher.PolicyCredentialMatcherImpl; import eu.abc4trust.returnTypes.IssuMsgOrCredDesc; import eu.abc4trust.returnTypes.IssuanceReturn; import eu.abc4trust.returnTypes.ObjectFactoryReturnTypes; import eu.abc4trust.returnTypes.UiIssuanceReturn; import eu.abc4trust.returnTypes.UiManageCredentialData; import eu.abc4trust.returnTypes.UiPresentationArguments; import eu.abc4trust.returnTypes.UiPresentationReturn; import eu.abc4trust.returnTypes.ui.CredentialInUi; import eu.abc4trust.returnTypes.ui.CredentialSpecInUi; import eu.abc4trust.returnTypes.ui.IssuerInUi; import eu.abc4trust.returnTypes.ui.UiCommonArguments; import eu.abc4trust.ri.servicehelper.FileSystem; import eu.abc4trust.ri.servicehelper.user.UserHelper; import eu.abc4trust.smartcard.BasicSmartcard; import eu.abc4trust.smartcard.CardStorage; //import eu.abc4trust.smartcard.HardwareSmartcard; import eu.abc4trust.smartcard.InsufficientStorageException; import eu.abc4trust.smartcard.SecretBasedSmartcard; import eu.abc4trust.smartcard.Smartcard; import eu.abc4trust.smartcard.SmartcardBackup; import eu.abc4trust.smartcard.SmartcardStatusCode; import eu.abc4trust.smartcard.SoftwareSmartcard; import eu.abc4trust.smartcard.StaticUriToIDMap; import eu.abc4trust.smartcard.Utils; import eu.abc4trust.util.TimingsLogger; import eu.abc4trust.xml.Attribute; import eu.abc4trust.xml.AttributeDescription; import eu.abc4trust.xml.Credential; import eu.abc4trust.xml.CredentialDescription; import eu.abc4trust.xml.CredentialDescriptions; import eu.abc4trust.xml.CredentialDescriptionsEntry; import eu.abc4trust.xml.CredentialSpecification; import eu.abc4trust.xml.InspectorPublicKey; import eu.abc4trust.xml.IssuanceMessage; import eu.abc4trust.xml.IssuerParameters; import eu.abc4trust.xml.ObjectFactory; import eu.abc4trust.xml.PresentationPolicyAlternatives; import eu.abc4trust.xml.PresentationToken; import eu.abc4trust.xml.RevocationAuthorityParameters; import eu.abc4trust.xml.Secret; import eu.abc4trust.xml.SystemParameters; import eu.abc4trust.xml.util.XmlUtils; @Path("/") public class UserService { public static final String USER_NAME = "standalone_demo"; private final static Logger logger = Logger.getLogger(UserService.class.getName()); public static boolean touchThisBooleanToForceStaticInit = true; private static final boolean useSemaphore = false; private static boolean DEBUG = false; private static String deploymentVersionId = "N/A"; public static String userServiceVersionId = "NOT RESOLVED YET"; private final ObjectFactory of = new ObjectFactory(); private UserAbcEngine engine; private final CardStorage cardStorage; private static final URI SECRET_BASED_SMARTCARD_URI = URI.create("secret://secretbased-smartcard-1234"); // Leftovers static HashMap<String, PresentationToken> presentationTokens; static HashMap<String, IssuMsgOrCredDesc> issuanceMessages; static HashMap<String, IdentitySelectionUIWrapper> identitySelections; private static final Map<String, URI> contextMap = new HashMap<String, URI>(); // private static final Map<String, CryptoEngine> cryptoEngineMap = new HashMap<String, CryptoEngine>(); private static boolean userServiceBusy = false; static IdentitySelectionUIWrapper currentIdentitySelections; @Context ServletContext context; String[] pilotRun_issuerParamsResourceList = null; String[] pilotRun_credSpecResourceList = null; // String pilotRun_filePrefix = ""; private static String fileStoragePrefix = ""; private static String softwareSmartcardResource = null; // private static int softwareSmartcardPin = -1; private static BasicSmartcard softwareSmartcard = null; private long credentialDeleterSleepTime; private static Thread revokedCredentialDeleter; private static boolean deploymentSpecificPropertiesInitialized; private static StringBuilder debugInfo = new StringBuilder(); private static void addDebugInfo(String msg) { debugInfo.append(msg); debugInfo.append("\n"); } private static void addDebugInfo(String msg, Throwable t) { debugInfo.append(msg); debugInfo.append(" - exception : " + t); debugInfo.append("\n"); for(StackTraceElement ste : t.getStackTrace()) { debugInfo.append(" "); debugInfo.append(ste.toString()); debugInfo.append("\n"); } debugInfo.append("\n"); } static { logger.fine("UserService static init ! - file.encoding : " + System.getProperty("file.encoding", null)); addDebugInfo("UserService static init ! - file.encoding : " + System.getProperty("file.encoding", null)); // specify behaviour in PolicyCredentialMatcher PolicyCredentialMatcherImpl.GENERATE_SECRET_IF_NONE_EXIST = true; String resourceFolder; try { addDebugInfo("Init User ABCE"); resourceFolder = initUserABCE(); addDebugInfo("Init User ABCE DONE!"); } catch (Exception e) { addDebugInfo("Could not init User ABCE", e); throw new IllegalStateException("Could not init User ABCE", e); } // // check for software smartcards // softwareSmartcardResource = System.getProperty("UserSoftwareSmartcard", null); // logger.warning("LOG W Try to use SoftwareSmartcard : " + softwareSmartcardResource); // System.out.println("Sys O Try to use SoftwareSmartcard : " + softwareSmartcardResource); // System.err.println("Sys E Try to use SoftwareSmartcard : " + softwareSmartcardResource); // // addDebugInfo("softwareSmartcardResource : " + softwareSmartcardResource); // // // if((softwareSmartcardResource!=null) && (softwareSmartcard == null)) { // logger.fine("Try to use SoftwareSmartcard : " + softwareSmartcardResource); // try { // softwareSmartcard = FileSystem.loadObjectFromResource(softwareSmartcardResource); // addDebugInfo("Try to use SoftwareSmartcard : " + softwareSmartcardResource); // } catch(Exception e) { // addDebugInfo("UserSoftwareSmartcard could not be loaded from : " + softwareSmartcardResource, e); // throw new IllegalStateException("UserSoftwareSmartcard could not be loaded from : " + softwareSmartcardResource, e); // } // // } else { // addDebugInfo("UserSerivce Initialize ABCE could not initialize Software Smartcards" + softwareSmartcard); // logger.warning("UserSerivce Initialize ABCE could not initialize Software Smartcards"); // throw new IllegalStateException("UserSerivce Initialize ABCE could not initialize Software Smartcards"); // } if(softwareSmartcard != null) { System.out.println("INFO : SoftwareSmartcard - initialized as SecretBased Smartcard : " + softwareSmartcard); } else { System.err.println("ERROR : SoftwareSmartcard - not initialized as SecretBased Smartcard ??"); } logMemoryUsage(); addDebugInfo("UserSerivce Version Numbers - deployment Id / userservice Id : " + deploymentVersionId + " / " + userServiceVersionId); logger.fine("UserSerivce Version Numbers - deployment Id / userservice Id : " + deploymentVersionId + " / " + userServiceVersionId); } private static String logMemoryUsage() { long kb = 1024; long mb = 1024 *1204; long total = Runtime.getRuntime().totalMemory(); // / mb; long free = Runtime.getRuntime().freeMemory(); // / mb; long max = Runtime.getRuntime().maxMemory(); // / mb; long used = (total - free); // / mb; // String memUsageString.format("Memory Usage : Used + Free = Total : Max : %,2d + %,2d = %,2d : %,2d",used, free, total, max); String memUsage = String.format("Memory Usage : Used + Free = Total : Max (Kilobytes) : %,2d + %,2d = %,2d : %,2d",used/kb, free/kb, total/kb, max/kb); // String memUsageString.format("Memory Usage : Used + Free = Total : Max (Megabytes) : %,2d + %,2d = %,2d : %,2d",used/mb, free/mb, total/mb, max/mb); logger.fine(memUsage); return memUsage; } private void saveSoftwareSmartcard() { if(softwareSmartcard != null) { logger.fine("saveSoftwareSmartcard to resource " + softwareSmartcardResource); try { if(softwareSmartcard instanceof SoftwareSmartcard) { // FileSystem.storeObjectInFile(softwareSmartcard, softwareSmartcardResource); System.out.println(" - skipped storing SoftwareSmartcard..." + softwareSmartcard); } else { System.out.println(" - do not store BasicSmartcard of type : " + softwareSmartcard); } } catch(Exception e) { System.err.println("WARN : Failed to store software smartcard to resource " + softwareSmartcardResource + " - error : " + e); } } } public UserService() throws Exception { // are these per session ? this.engine = UserHelper.getInstance().getEngine(); this.cardStorage = UserHelper.getInstance().cardStorage; // if (UserService.revokedCredentialDeleter == null) { System.out .println("Launching thread for revoked credential deleter"); this.engine = UserHelper.getInstance().getEngine(); // Check every five minutes. this.credentialDeleterSleepTime = 60 * 60 * 1000; RevokedCredentialDeleter rcd = new RevokedCredentialDeleter( this.engine, this.credentialDeleterSleepTime); UserService.revokedCredentialDeleter = new Thread(rcd); UserService.revokedCredentialDeleter.start(); //Also start the detector for smart cards. // SmartcardServletContext.cardStorageReference // .getAndSet(this.cardStorage); // SmartcardServletContext.startDetector(); try{ Thread.sleep(500); }catch(Exception e){ //Do nothing other than note it happened, since we want to run anyways. logger.fine("Could not sleep - was interrupted!: "+e.getMessage()); } } } private static String initUserABCE() throws Exception { String userServiceRunDir = System.getProperty("UserServiceRunDir"); if(userServiceRunDir == null) { throw new IllegalStateException("UserServiceRunDir should have been set from executeable jar..."); } String abc4Trust_LOCALAPPDATA = System.getProperty("ABC4TRUST_LOCALAPPDATA"); if(abc4Trust_LOCALAPPDATA == null) { throw new IllegalStateException("ABC4TRUST_LOCALAPPDATA should have been set from executeable jar..."); } addDebugInfo("userServiceRunDir == " + userServiceRunDir + " - abc4Trust_LOCALAPPDATA : " + abc4Trust_LOCALAPPDATA); String resourceFolder = userServiceRunDir + (userServiceRunDir.endsWith("/") || userServiceRunDir.endsWith("\\") ? "" : "/") + "resources"; // TODO : FIND USERs HOME !!! fileStoragePrefix = abc4Trust_LOCALAPPDATA + (abc4Trust_LOCALAPPDATA.endsWith("/") || abc4Trust_LOCALAPPDATA.endsWith("\\")? "" : "/") + "user_storage/"; addDebugInfo("fileStoragePrefix == " + fileStoragePrefix); logger.fine("fileStoragePrefix : " + fileStoragePrefix); if(UserHelper.isInit()) { logger.fine("UserService already init !"); } else { logger.fine("UserService initiating !"); // load all - should only be 1 String systemParamsResource = resourceFolder + "/system_params.xml"; SystemParameters systemParams = FileSystem.loadXmlFromResource(systemParamsResource); try { List<IssuerParameters> issuerParamsList = new ArrayList<IssuerParameters>(); // load all credspecs in folder List<CredentialSpecification> credSpecList = new ArrayList<CredentialSpecification>(); // load all inspector public keys in folder List<InspectorPublicKey> inspectorPublicKeyList = new ArrayList<InspectorPublicKey>(); List<RevocationAuthorityParameters> revocationAuthorityParametersList = new ArrayList<RevocationAuthorityParameters>(); UserHelper.initInstance(systemParams, issuerParamsList, fileStoragePrefix, credSpecList, inspectorPublicKeyList, revocationAuthorityParametersList); SystemParameters check = UserHelper.getInstance().keyManager.getSystemParameters(); System.out.println("RESOURCE SP : " + systemParams.getSystemParametersUID()); System.out.println("STORAGE SP : " + check.getSystemParametersUID()); } catch(Exception e) { System.err.println("Failed to perform SystemParameter test ??"); e.printStackTrace(); } finally { UserHelper.resetInstance(); } // load all issuer params in folder List<IssuerParameters> issuerParamsList = FileSystem.findAndLoadXmlResourcesInDir(resourceFolder, "issuer_params"); // load all credspecs in folder List<CredentialSpecification> credSpecList = FileSystem.findAndLoadXmlResourcesInDir(resourceFolder, "cred_spec"); // load all inspector public keys in folder List<InspectorPublicKey> inspectorPublicKeyList = FileSystem.findAndLoadXmlResourcesInDir(resourceFolder, "inspector_publickey"); List<RevocationAuthorityParameters> revocationAuthorityParametersList = FileSystem.findAndLoadXmlResourcesInDir(resourceFolder, "revocation_authority"); if (issuerParamsList.size() == 0) { throw new IllegalStateException( "Did not find any issuer resources. Please look in: " + resourceFolder); } for(IssuerParameters ip : issuerParamsList){ logger.fine(" - ip : " + ip.getAlgorithmID() + " : " + ip.getVersion() + " - " + ip.getParametersUID()); addDebugInfo(" - ip : " + ip.getAlgorithmID() + " : " + ip.getVersion() + " - " + ip.getParametersUID()); } if((issuerParamsList.size() != credSpecList.size()) && ((issuerParamsList.size() / 2) != credSpecList.size())) { logger.fine("Warning : Mismatch between number of IssuerParmameter and number of credspecs - " + issuerParamsList.size() + " (and / 2 ) != " + credSpecList.size()); addDebugInfo("Warning : Mismatch between number of IssuerParmameter and number of credspecs - " + issuerParamsList.size() + " (and / 2 ) != " + credSpecList.size()); } readDeploymentSpecificProperties(resourceFolder); UserHelper.initInstance(systemParams, issuerParamsList, fileStoragePrefix, credSpecList, inspectorPublicKeyList, revocationAuthorityParametersList); // always overwrite Rev Aut info... for(RevocationAuthorityParameters rap : revocationAuthorityParametersList) { UserHelper.getInstance().keyManager.storeRevocationAuthorityParameters(rap.getParametersUID(), rap); } SecretBasedSmartcard secretBasedSmartcard = new SecretBasedSmartcard(UserService.USER_NAME, UserHelper.getInstance().credentialManager, UserHelper.getInstance().keyManager); softwareSmartcard = secretBasedSmartcard; try { Secret secret = UserHelper.getInstance().credentialManager.getSecret(UserService.USER_NAME, SECRET_BASED_SMARTCARD_URI); System.out.println(" - Secret already generated!"); secretBasedSmartcard.initFromSecret(secret); } catch(SecretNotInStorageException e) { System.out.println(" - Secret NOT in CredentialManager - try to create!"); secretBasedSmartcard.initNew(systemParams, SECRET_BASED_SMARTCARD_URI); Secret secret = secretBasedSmartcard.getSecret(); UserHelper.getInstance().credentialManager.storeSecret(UserService.USER_NAME, secret); System.out.println(" - Secret Generated! and stored in CredentialManager!"); } presentationTokens = new HashMap<String, PresentationToken>(); issuanceMessages = new HashMap<String, IssuMsgOrCredDesc>(); identitySelections = new HashMap<String, IdentitySelectionUIWrapper>(); } logger.fine("UserService init ! DONE"); return resourceFolder; } private static String getResourceNames(String[] resourceList) { StringBuilder s = new StringBuilder(); if(resourceList.length==0) { s.append("Length == 0"); } else { boolean first = true; for(String r : resourceList) { if(first) { first = false; } else { s.append(", "); } s.append(r); } } return s.toString(); } private static void readDeploymentSpecificProperties(String resourceFolder) { if(deploymentSpecificPropertiesInitialized) { return; } InputStream is = null; try { try { is = FileSystem.getInputStream("/deploymentspecific.properties"); logger.fine("Reading deploymentspecific.properties from '/deploymentspecific.properties'"); addDebugInfo("Reading deploymentspecific.properties from '/deploymentspecific.properties'"); } catch(IOException ignore) { } if(is==null) { try { is = FileSystem.getInputStream(resourceFolder + "/deploymentspecific.properties"); logger.fine("Reading deploymentspecific.properties from '" + resourceFolder + "/deploymentspecific.properties'"); addDebugInfo("Reading deploymentspecific.properties from '" + resourceFolder + "/deploymentspecific.properties'"); } catch(IOException ignore) { } } if(is != null) { try { Properties props = new Properties(); props.load(is); DEBUG = Boolean.parseBoolean(props.getProperty("printDebugInfo", "false")); logger.fine("Allow debug printing: "+DEBUG); deploymentVersionId = props.getProperty("deploymentVersionId", "N/A"); } catch(Exception e) { logger.log(Level.WARNING, "Failed to load properties.", e); } } else { logger.warning("No deployment specific properties."); } } finally { deploymentSpecificPropertiesInitialized = true; if(is!=null) { try { is.close(); } catch(Exception ignore){}; } } } @GET() @Path("/user/getUiPresentationArguments/{SessionID}") @Consumes({MediaType.APPLICATION_XML, MediaType.TEXT_XML}) @Produces(MediaType.APPLICATION_XML) public Response getUiPresentationArguments(@PathParam ("SessionID") final String sessionId) throws Exception{ IdentitySelectionUIWrapper isw = identitySelections.get(sessionId); logger.fine("--- getUiPresentationArguments - session ID : " + sessionId + " - wrapper : " + isw); if(isw==null) { String msg = "Internal Error ! - IdentitySelectionUIWrapper should be defined for session : " + sessionId; System.err.println(msg); throw new Exception(msg); } if(isw.getUiPresentationArguments()==null) { String msg = "Internal Error ! - uiPresentationArguments should have been defined set on IdentitySelectionUIWrapper"; System.err.println(msg); throw new Exception(msg); } logger.fine("- uiPresentationArguments : " + isw.getUiPresentationArguments()); logger.fine("- uiPresentationArguments : " + XmlUtils.toXml(ObjectFactoryReturnTypes.wrap(isw.getUiPresentationArguments()), false)); // System.out.println("- uiPresentationArguments : " + isw.getUiPresentationArguments()); // System.out.println("- uiPresentationArguments : " + XmlUtils.toXml(ObjectFactoryReturnTypes.wrap(isw.getUiPresentationArguments()), false)); // logger.fine("- uiPresentationArguments : " + isw.getUiPresentationArguments().data.inspectors); // logger.fine("- uiPresentationArguments : " + isw.getUiPresentationArguments().data.issuers); return Response.ok(isw.getUiPresentationArguments()).build(); } @POST() @Path("/user/setUiPresentationReturn/{SessionID}") @Consumes({MediaType.APPLICATION_XML, MediaType.TEXT_XML}) @Produces(MediaType.APPLICATION_XML) public Response setUiPresentationReturn(@PathParam ("SessionID") final String sessionId, UiPresentationReturn uiPresentationReturn) throws Exception{ IdentitySelectionUIWrapper isw = identitySelections.get(sessionId); logger.fine("--- setUiPresentationReturn - session ID : " + sessionId + " - wrapper : " + isw); logger.fine("- UiPresentationReturn : " + uiPresentationReturn); // logger.fine("- UiPresentationReturn XML : " + XmlUtils.toXml(ObjectFactoryReturnTypes.wrap(uiPresentationReturn))); if(isw==null) { String msg = "Internal Error ! - IdentitySelectionUIWrapper should be defined for session : " + sessionId; System.err.println(msg); throw new Exception(msg); } // TODO : Should this be fixed in 'abce-components' ?? if(uiPresentationReturn.chosenPseudonymList == -1) { uiPresentationReturn.chosenPseudonymList = 0; } isw.setUiPresentationReturn(uiPresentationReturn); return Response.noContent().build(); } @GET() @Path("/user/getUiIssuanceArguments/{SessionID}") @Consumes({MediaType.APPLICATION_XML, MediaType.TEXT_XML}) @Produces(MediaType.APPLICATION_XML) public Response getUiIssuanceArguments(@PathParam ("SessionID") final String sessionId) throws Exception{ IdentitySelectionUIWrapper isw = identitySelections.get(sessionId); logger.fine("--- getUiIssuanceArguments - session ID : " + sessionId + " - wrapper : " + isw); if(isw==null) { String msg = "Internal Error ! - IdentitySelectionUIWrapper should be defined for session : " + sessionId; System.err.println(msg); throw new Exception(msg); } if(isw.getUiIssuanceArguments()==null) { String msg = "Internal Error ! - uiIssuanceArguments should have been defined set on IdentitySelectionUIWrapper"; System.err.println(msg); throw new Exception(msg); } logger.fine("- uiIssuanceArguments : " + isw.getUiIssuanceArguments()); // logger.fine("- uiIssuanceArguments : " + XmlUtils.toXml(ObjectFactoryReturnTypes.wrap(isw.getUiIssuanceArguments()), false)); return Response.ok(isw.getUiIssuanceArguments()).build(); } @POST() @Path("/user/setUiIssuanceReturn/{SessionID}") @Consumes({MediaType.APPLICATION_XML, MediaType.TEXT_XML}) @Produces(MediaType.APPLICATION_XML) public Response setUiIssuanceReturn(@PathParam ("SessionID") final String sessionId, UiIssuanceReturn uiIssuanceReturn) throws Exception{ IdentitySelectionUIWrapper isw = identitySelections.get(sessionId); logger.fine("--- setUiIssuanceReturn - session ID : " + sessionId + " - wrapper : " + isw); logger.fine("- uiIssuanceReturn - session ID : " + uiIssuanceReturn); // logger.fine("- uiIssuanceReturn XML : " + XmlUtils.toXml(ObjectFactoryReturnTypes.wrap(uiIssuanceReturn))); if(isw==null) { String msg = "Internal Error ! - IdentitySelectionUIWrapper should be defined for session : " + sessionId; System.err.println(msg); throw new Exception(msg); } // TODO : Should this be fixed in 'abce-components' ?? if(uiIssuanceReturn.chosenPseudonymList == -1) { uiIssuanceReturn.chosenPseudonymList = 0; } isw.setUiIssuanceReturn(uiIssuanceReturn); return Response.noContent().build(); } private final Semaphore createPresentationTokenSemaphore = new Semaphore(1, true); private int presentationCallsCounter = 0; /** * First call to create presentation token. Takes a presentation policy as input * If the policy cannot be satisfied, return 422. * If the policy requires user involvement, returns 203 + JSON * If the policy can be fulfilled without user involvement, return 200 + XML * @param sessionId * @param presentationPolicy * @return 422 if policy can not be satisfied, 203+JSON if user choice is required and 200+xml when done */ @POST() @Path("/user/createPresentationToken/{SessionID}") @Consumes({MediaType.APPLICATION_XML, MediaType.TEXT_XML}) @Produces({MediaType.APPLICATION_JSON,MediaType.TEXT_XML, MediaType.TEXT_PLAIN}) public Response createPresentationToken(@PathParam ("SessionID") final String sessionId, final JAXBElement<PresentationPolicyAlternatives> presentationPolicy_jaxb) { final PresentationPolicyAlternatives presentationPolicy = presentationPolicy_jaxb.getValue(); if(presentationCallsCounter > 0){ logger.warning("WARNING: CreatePresentationToken called again before the last one finished. presentationCallsCounter="+presentationCallsCounter); } presentationCallsCounter++; if(useSemaphore){ try { createPresentationTokenSemaphore.acquire(); } catch (InterruptedException e) { throw new RuntimeException("Could not aquire semaphore lock - was interrupted", e); } } logger.fine("--- createPresentationToken - session ID : " + sessionId); logMemoryUsage(); try{ logger.fine("-- -- " + XmlUtils.toXml(this.of.createPresentationPolicyAlternatives(presentationPolicy))); if(!this.engine.canBeSatisfied(UserService.USER_NAME, presentationPolicy)){ logger.fine("cannot satisfy policy, halting!"); finishPresentationCount(); return Response.status(422).build(); } logger.fine("-- -- policy can be satisfied!"); }catch(Exception e){ logger.log(Level.WARNING, "engine.canBeSatisfied threw an exception:", e); finishPresentationCount(); return Response.status(422).build(); }catch(Throwable t) { logger.log(Level.SEVERE, "internal error calling : engine.canBeSatisfied", t); finishPresentationCount(); return Response.status(422).build(); } final IdentitySelectionUIWrapper isw = new IdentitySelectionUIWrapper(); identitySelections.put(sessionId, isw); // TODO: REMOVE AFTER TEST! currentIdentitySelections = isw; Thread thread = new Thread(new Runnable(){ @SuppressWarnings("deprecation") public void run(){ try{ UiPresentationArguments uiPresentationArguments = UserService.this.engine.createPresentationToken(UserService.USER_NAME, presentationPolicy); // this will sleep thread until selectiont is done... isw.selectPresentationTokenDescription(uiPresentationArguments); // - here we wait for return... PresentationToken pt = UserService.this.engine.createPresentationToken(UserService.USER_NAME, isw.getUiPresentationReturn()); presentationTokens.put(sessionId, pt); } catch(Exception e){ logger.log(Level.WARNING, "internal err! :", e); //TODO something to store the exception in isw to allow for error handling } finally { // set done! isw.done = true; } } }); logger.fine("--- createpresentationToken starting thread and going to sleep"); userServiceBusy = true; thread.start(); try { while((presentationTokens.get(sessionId)==null) &&!isw.hasPresentationChoices() && !isw.done) {Thread.sleep(200);} }catch(InterruptedException e){ logger.fine("Interrupted while waiting for idSelectionWrapper to get choices or finish"); if((presentationTokens.get(sessionId)==null) &&!isw.hasPresentationChoices() && !isw.done) { finishPresentationCount(); userServiceBusy = false; return Response.status(500).build(); } } logger.fine("### --- createpresentationToken woke up : " + sessionId + " : "+presentationTokens.get(sessionId)+" "+isw.hasPresentationChoices()+" "+isw.done); if(isw.done || (presentationTokens.get(sessionId)!=null)) { logger.fine("### --- createPresentationToken finished without need for user interaction " + isw.done + " : " + presentationTokens.get(sessionId)); identitySelections.remove(sessionId); PresentationToken pt = presentationTokens.remove(sessionId); if((isw.getException() == null) && (pt != null)) { // !! saveSoftwareSmartcard this.saveSoftwareSmartcard(); finishPresentationCount(); userServiceBusy = false; return Response.ok(this.of.createPresentationToken(pt)).type(MediaType.TEXT_XML).build(); } } else{ logger.fine("### --- createPresentationToken has choices for ui selection"); finishPresentationCount(); userServiceBusy = false; return Response.status(203).entity("GO AHEAD CALL NEW UI FOR PRESENTATION").type(MediaType.TEXT_PLAIN).build(); } logger.fine("### --- createpresentaitontoken - this will never be reached"); finishPresentationCount(); userServiceBusy = false; return Response.notAcceptable(null).build(); } private void finishPresentationCount(){ presentationCallsCounter--; if(useSemaphore){ createPresentationTokenSemaphore.release(); } } // new method for createPresentationTokenIdentitySelection // takes JSON as input, delivers it to ISWrapper and waits until there is something in presentationTokens @POST() @Path("/user/createPresentationTokenIdentitySelection/{SessionID}") @Consumes(MediaType.TEXT_PLAIN) @Produces(MediaType.APPLICATION_XML) public Response createPresentationTokenIdentitySelection(@PathParam ("SessionID") final String sessionId, final String choice) throws Exception { logger.fine("-- createPresentationToken - got IdentitySelection - for Session ID : " + sessionId + " - JSon choice : [" + choice + "]"); IdentitySelectionUIWrapper isw = identitySelections.get(sessionId); if(isw==null){ //Invalid sessionID logger.warning("Unknown IdentitySelectionWrapper for sessionID: "+sessionId+", ABORTING"); return Response.status(422).build(); } logger.fine("- isw " + isw); logger.fine("- isw " + isw.hasPresentationChoices()); logger.fine("- isw " + isw.getUiPresentationReturn()); logger.fine("- isw done ? " + isw.done); // if null - user cancelled / closed window. This was not detected by UI - so we notify by setting 'null' UIIssuanceReturn! if(isw.getUiPresentationReturn()==null) { logger.fine("CreatePresentationToken called but ID Selection not set ? User has cancelled/closed windows... sessionID: "+sessionId+", ABORTING"); isw.setUiPresentationReturn(null); // identitySelections.remove(sessionId); // return Response.status(422).build(); } try{ userServiceBusy = true; while(!isw.done) { Thread.sleep(200); } } catch(InterruptedException e){ if(!isw.done) { logger.fine("idSelectionWrapper waiting for ABC engine (after choice has been made) interrupted without being done"); } logger.warning("- interrupted ERROR 500"); userServiceBusy = false; return Response.status(500).build(); } userServiceBusy = false; // PresentationToken pt = presentationTokens.remove(sessionId); identitySelections.remove(sessionId); if(pt == null){ logger.fine("- No PresentationToken : ERROR 422"); logger.fine("Unknown PresentationToken for sessionID: "+sessionId+", ABORTING"); return Response.status(422).build(); } // !! saveSoftwareSmartcard this.saveSoftwareSmartcard(); JAXBElement<PresentationToken> ptJaxB = this.of.createPresentationToken(pt); // String ptXml = XmlUtils.toXml(ptJaxB); // logger.fine("- PresentationToken XML : " + ptXml); logger.fine("- PresentationToken - A OK - return http : 200"); return Response.ok(ptJaxB).build(); } /** * Takes an IssuanceMessage and passes it on to the ABC engine. * If the ABC engine requires user interaction via the UI, a * JSON message is returned with status 203 otherwise an * IssuanceMessage (encoded as XML) is returned with status 200 * or an empty message and status 204 is returned if the issuer * does not expect a reply. * * All exceptions results in an empty message and status 500. * * @param sessionId Current sessionId * @param mess IssuanceMessage as XML * @return XML with status 200, JSON with status 203 or empty message with status 204 or 500 or: * 501 which means that there is not space on the card. */ @POST() @Path("/user/issuanceProtocolStep/{SessionID}") @Consumes({MediaType.APPLICATION_XML, MediaType.TEXT_XML}) @Produces({MediaType.APPLICATION_JSON,MediaType.TEXT_XML, MediaType.TEXT_PLAIN}) public Response issuanceProtocolStep(@PathParam ("SessionID") final String sessionId, @QueryParam("startRequest") String startIssuanceUrl, @QueryParam("stepRequest") String stepIssuanceUrl, final JAXBElement<IssuanceMessage> mess_jaxb) throws Exception { final IssuanceMessage mess = mess_jaxb.getValue(); logger.fine("-- issuanceProtocolStep: "+sessionId); if(DEBUG){ logger.fine("-- issuanceMessage - incoming : "+XmlUtils.toXml(this.of.createIssuanceMessage(mess), false)); } logMemoryUsage(); if((startIssuanceUrl != null) && (stepIssuanceUrl != null)){ startIssuanceUrl = URLDecoder.decode(startIssuanceUrl, "UTF-8"); stepIssuanceUrl = URLDecoder.decode(stepIssuanceUrl, "UTF-8"); } if(DEBUG){ logger.fine("-- issuanceProtocolStep - startRequest: "+startIssuanceUrl); logger.fine("-- issuanceProtocolStep - stepRequest: "+stepIssuanceUrl); } // CryptoEngine cryptoEngine = cryptoEngineMap.get(sessionId); // if(cryptoEngine == null){ // IssuancePolicy ip = null; // try{ip = (IssuancePolicy) XmlUtils.unwrap(mess.getAny(), IssuancePolicy.class);} // catch(Exception e){ // try{ip = (IssuancePolicy) mess.getAny().get(1);} // catch(Exception ex){ // System.err.println("WARNING: Neither unwrapping worked!"); // } // } // if(ip != null){ //// if(this.engine.listCredentials().size() > 6){ //// logger.fine("Cannot issue the 8'th credential - technical problems. Tell user to check revoked status"); //// return Response.status(501).build(); //// } // // contextMap.put(sessionId, mess.getContext()); //for maybe calling some reload token code depending on cryptoEngine. // logger.fine("Mapping sessionId to this context: " + mess.getContext()); // if(ip.getCredentialTemplate().getIssuerParametersUID().toString().endsWith("uprove")){ // logger.fine("From issuance message, it is assumed that we are working with UProve."); // cryptoEngineMap.put(sessionId, CryptoEngine.UPROVE); // }else if(ip.getCredentialTemplate().getIssuerParametersUID().toString().endsWith("idemix")){ // logger.fine("From issuance message, it is assumed that we are working with Idemix."); // cryptoEngineMap.put(sessionId, CryptoEngine.IDEMIX); // }else{ // System.err.println("Warning: issuer parameter contains no known cryptoEngine!"); // } // }else{ // System.err.println("Warning: Issuance Policy was null - this should not be the case!"); // } // } if(identitySelections.get(sessionId)!= null) { logger.fine("-- Session identifier is already used"); return Response.status(900).build(); } final IdentitySelectionUIWrapper isw = new IdentitySelectionUIWrapper(); identitySelections.put(sessionId, isw); Thread thread = new Thread(new Runnable(){ public void run(){ try { logger.fine("Starting Thread for IssanceProtocol Selection"); // @SuppressWarnings("deprecation") IssuMsgOrCredDesc imOrDesc = new IssuMsgOrCredDesc();; IssuanceReturn issuanceReturn = UserService.this.engine.issuanceProtocolStep(UserService.USER_NAME, mess); if(issuanceReturn.uia!=null) { isw.selectIssuanceTokenDescription(issuanceReturn.uia); // here we wait for return... imOrDesc.im = UserService.this.engine.issuanceProtocolStep(UserService.USER_NAME, isw.getUiIssuanceReturn()); } else { imOrDesc.im = issuanceReturn.im; imOrDesc.cd = issuanceReturn.cd; } // IssuMsgOrCredDesc imOrDesc = UserService.this.engine.issuanceProtocolStep(UserService.USER_NAME, mess, isw); logger.fine("UserABCE Creaded IssuanceMessage : " + imOrDesc); issuanceMessages.put(sessionId,imOrDesc); //add to include IdentitySelectionWrapper logger.fine("Stored IssuanceMessage for session : " + sessionId + " : " + imOrDesc); } catch(Exception e) { logger.log(Level.WARNING, "internal err (Exception)", e); isw.setException(e); //put e in isw to allow for error handling } catch(Throwable e) { logger.log(Level.WARNING, "internal err (Throwable)"); //put e in isw to allow for error handling } finally { // set done! isw.done = true; } } }); logger.fine("-- issuanceProtooclStep: starting thread and going to sleep"); userServiceBusy = true; thread.start(); try { while((issuanceMessages.get(sessionId)==null) && !isw.hasIssuanceChoices() && !isw.done) {Thread.sleep(200);} }catch(InterruptedException e){ logger.fine("Interrupted while waiting for idSelectionWrapper to get choices or finish"); if((issuanceMessages.get(sessionId)==null) &&!isw.hasIssuanceChoices() && !isw.done) { userServiceBusy = false; return Response.status(500).build(); } } logger.fine("-- issuanceProtooclStep: waking up: "+issuanceMessages.get(sessionId)+ "- "+isw.hasIssuanceChoices()+" - "+isw.done); logger.fine("-- done waiting for wrapper!"); if(isw.done || (issuanceMessages.get(sessionId)!=null)) { logger.fine("-- wrapper is done! "); identitySelections.remove(sessionId); IssuMsgOrCredDesc userIm = issuanceMessages.remove(sessionId); if (userIm == null){ // this is an error case! logger.fine("-- Error running Issuance Protocol!"); if(isw.getException() != null){ if(isw.getException() instanceof InsufficientStorageException){ userServiceBusy = false; return Response.status(501).build(); } } userServiceBusy = false; return Response.status(500).build(); } else if (userIm.cd != null){ // TODO : NOT IMPLEMENTED FOR PATRAS // //Save information for reloading tokens if we are running UProve // if(cryptoEngineMap.get(sessionId) == CryptoEngine.UPROVE){ // if(contextMap.get(sessionId) != null){ // logger.fine("=====================\n\n Adding reload token info - ProtocolStep! \n\n======================="); // UserHelper.getInstance().reloadTokens.addCredentialIssuer(contextMap.get(sessionId), userIm.cd, startIssuanceUrl, stepIssuanceUrl); // }else{ // System.err.println("======== \n Reload token info should have been added, but no context was found under the session id "+sessionId); // } // } // !! saveSoftwareSmartcard this.saveSoftwareSmartcard(); userServiceBusy = false; return Response.status(204).build(); }else{ if(DEBUG){ logger.fine("-- issuanceMessage - send back to issuer (no select) : "+XmlUtils.toXml(this.of.createIssuanceMessage(mess), false)); }else{ logger.fine("-- issuanceMessage - send back to issuer (no select) : "+mess); } return Response.ok(this.of.createIssuanceMessage(userIm.im)).type(MediaType.TEXT_XML).build(); } }else { logger.fine("-- wrapper has choices! "); userServiceBusy = false; return Response.status(203).entity("GO AHEAD CALL NEW UI FOR ISSUANCE").type(MediaType.TEXT_PLAIN).build(); } } //fun stuff part! @POST() @Path("/user/issuanceProtocolStepSelect/{SessionID}") @Consumes({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN}) @Produces({MediaType.TEXT_XML}) public Response issuanceProtocolStepSelect(@PathParam ("SessionID") final String sessionId, @QueryParam("startRequest") String startIssuanceUrl, @QueryParam("stepRequest") String stepIssuanceUrl, final String choice) throws Exception { logger.fine("issuanceProtocolStepSelect : "+sessionId + " - JSon choice : [" + choice + "]"); if((startIssuanceUrl != null) && (stepIssuanceUrl != null)){ startIssuanceUrl = URLDecoder.decode(startIssuanceUrl, "UTF-8"); stepIssuanceUrl = URLDecoder.decode(stepIssuanceUrl, "UTF-8"); } if(DEBUG){ logger.fine("-- issuanceProtocolStepSelect - startRequest: "+startIssuanceUrl); logger.fine("-- issuanceProtocolStepSelect - stepRequest: "+stepIssuanceUrl); } IdentitySelectionUIWrapper isw = identitySelections.get(sessionId); if(isw==null){ //Invalid sessionID logger.fine("Unknown IdentitySelectionWrapper(1) for sessionID: "+sessionId+", ABORTING"); return Response.status(422).build(); } // if null - user cancelled / closed window. This was not detected by UI - so we notify by setting 'null' UIIssuanceReturn! if(isw.getUiIssuanceReturn()==null) { isw.setUiIssuanceReturn(null); // } try{ userServiceBusy = true; while(!isw.done) { logger.fine("Waiting for ISW to finish! " + isw.done); Thread.sleep(200); } } catch(InterruptedException e){ if(!isw.done) { logger.fine("idSelectionWrapper waiting for ABC engine (after choice has been made) interrupted without being done"); } userServiceBusy = false; return Response.status(500).build(); } userServiceBusy = false; IssuMsgOrCredDesc userIm = issuanceMessages.remove(sessionId); identitySelections.remove(sessionId); if(userIm == null){ logger.warning("Unknown IdentitySelectionWrapper(2) for sessionID: "+sessionId+", ABORTING"); return Response.status(422).build(); } if (userIm.cd != null){ //The ABC Engine returned a credential description, so the protocol is done // TODO : NOT IMPLEMENTED FOR PATRAS // //Save information for reloading tokens if we are running UProve // if(cryptoEngineMap.get(sessionId) == CryptoEngine.UPROVE){ // if(contextMap.get(sessionId) != null){ // logger.fine("=====================\n Adding reload token info - ProtocolStepSelect! \n======================="); // UserHelper.getInstance().reloadTokens.addCredentialIssuer(contextMap.get(sessionId), userIm.cd, startIssuanceUrl, stepIssuanceUrl); // }else{ // System.err.println("======== \n Reload token info should have been added, but no context was found under the session id "+sessionId); // } // } // !! saveSoftwareSmartcard this.saveSoftwareSmartcard(); return Response.status(204).build(); }else{ //The ABC engine returned a issuancemessage that has to be sent to the issuer if(DEBUG){ logger.fine("-- issuanceMessage - send back to issuer (After select) : "+XmlUtils.toXml(this.of.createIssuanceMessage(userIm.im), false)); }else{ logger.fine("-- issuanceMessage - send back to issuer (After select) : "+userIm.im); } return Response.ok(this.of.createIssuanceMessage(userIm.im)).build(); } } @POST() @Path("/user/updateNonRevocationEvidence") @Consumes({MediaType.APPLICATION_XML, MediaType.TEXT_XML}) @Produces(MediaType.TEXT_XML) public Response updateNonRevocationEvidence() { @SuppressWarnings("unused") ObjectFactory of = new ObjectFactory(); logger.fine("updateNonRevocationEvidence"); try { userServiceBusy = true; this.engine.updateNonRevocationEvidence(UserService.USER_NAME); userServiceBusy = false; logger.fine(" - updateNonRevocationEvidence Done"); return Response.ok().build(); } catch (Exception e) { logger.log(Level.SEVERE, " - updateNonRevocationEvidence Failed", e); return Response.serverError().build(); } } @POST @Path("/user/checkRevocationStatus") @Consumes({MediaType.APPLICATION_XML, MediaType.TEXT_XML}) @Produces(MediaType.TEXT_XML) public Response checkRevocationStatus(){ logger.fine("checkRevocationStatus"); try{ userServiceBusy = true; for (URI credUri : this.engine.listCredentials(UserService.USER_NAME)) { if (this.engine.isRevoked(UserService.USER_NAME, credUri)) { logger.fine("Deleting revoked credential: " + credUri); this.engine.deleteCredential(UserService.USER_NAME, credUri); logger.fine("Deleted revoked credential: " + credUri); } else { logger.fine("Credential OK: " + credUri); } } userServiceBusy = false; logger.fine("checkRevocationStatus - done"); return Response.ok().build(); }catch(Exception e){ logger.log(Level.SEVERE, "checkRevocationStatus failed : ", e); return Response.serverError().build(); } } @POST() @Path("/user/listCredentials") @Consumes({MediaType.APPLICATION_XML, MediaType.TEXT_XML}) @Produces(MediaType.TEXT_PLAIN) public Response listCredentials() { @SuppressWarnings("unused") ObjectFactory of = new ObjectFactory(); logger.fine("listCredentials"); try { // List<URI> resp = engine.listCredentials(); List<URI> resp = new ArrayList<URI>(); resp.add(new URI("http://asdf.gh/jkl")); logger.fine(" - resp " + resp); StringBuilder sb = new StringBuilder(); if (resp != null) { for (URI uri : resp) { sb.append(uri); sb.append("\n"); } } return Response.ok(sb.toString()).build(); } catch (Exception e) { logger.fine(" - failed"); e.printStackTrace(); return Response.serverError().build(); } } @POST() @Path("/user/getCredentialDescription") @Consumes({MediaType.TEXT_PLAIN}) @Produces(MediaType.TEXT_XML) public Response getCredentialDescription(final String creduid) { ObjectFactory of = new ObjectFactory(); logger.fine("getCredentialDescription : " + creduid); URI uri; try { uri = new URI(creduid); } catch (Exception e) { return Response.status(Status.BAD_REQUEST).build(); } try { CredentialDescription resp = this.engine.getCredentialDescription(UserService.USER_NAME, uri); return Response.ok(of.createCredentialDescription(resp)).build(); } catch (Exception e) { logger.fine(" - failed"); e.printStackTrace(); return Response.serverError().build(); } } @POST() @Path("/user/deleteCredential") @Consumes({MediaType.TEXT_PLAIN}) @Produces(MediaType.TEXT_XML) public Response deleteCredential(final String creduid) { @SuppressWarnings("unused") ObjectFactory of = new ObjectFactory(); logger.fine("getCredentialDescription : " + creduid); URI uri; try { uri = new URI(creduid); } catch (Exception e) { return Response.status(Status.BAD_REQUEST).build(); } try { boolean result = this.engine.deleteCredential(UserService.USER_NAME, uri); logger.fine(" - call ok - deleted : " + result); if (result) { return Response.ok().build(); } else { return Response.status(Status.NO_CONTENT).build(); } } catch (Exception e) { logger.log(Level.SEVERE, " - failed", e); return Response.serverError().build(); } } /** * returns whether or not the card is the same as last time we checked. * Returns: * 204 if it is the same card, or * 410 if no card is currently present, or * 406 if it is not the same card, or if it is the first time it's called (thus we still need PIN). */ //static boolean smartcardAvailable = false; static String lastCardReference = null; @GET() @Path("/user/checkSmartcard/{SessionID}") public Response checkSmartcard(@PathParam ("SessionID") final String sessionId) throws Exception { boolean sameCard = this.isSameCardStorageReference(); logger.fine("checkSmartcard - available and same card ? : " + sameCard); // smartcardAvailable); if(sameCard) { // smartcardAvailable) { return Response.noContent().build(); } else { // if software - and not 'sameCard' - card not unlocked yet... if(softwareSmartcard!=null) { return Response.status(Status.NOT_ACCEPTABLE).build(); } // real card - check if card in reader... if(this.cardStorage.getClosedSmartcards().size() == 0 && this.cardStorage.getSmartcards().size() == 0) { //No card in card-reader return Response.status(Status.GONE).build(); }else{ return Response.status(Status.NOT_ACCEPTABLE).build(); } } } @GET() @Path("/user/isSameSmartcard/{SessionID}") public Response isSameSmartcard(@PathParam ("SessionID") final String sessionId) throws Exception { boolean sameCard = this.isSameCardStorageReference(); logger.fine("isSameSmartcard - available and same card ? : " + sameCard); // smartcardAvailable); if(sameCard) { return Response.noContent().build(); } else { return Response.status(Status.GONE).build(); } } private String getCurrentCardReference() { System.out.println("getCardStorageReference : " + this.cardStorage.getSmartcards() + " : " + this.cardStorage.getClosedSmartcards()); if(this.cardStorage.getClosedSmartcards().size()>0) { // we hav closed cards ?? logger.fine("we have closed cards ?? : " + this.cardStorage.getClosedSmartcards()); return null; } else if(this.cardStorage.getSmartcards().size()>0) { System.out.println("xx : " + this.cardStorage.getSmartcards()); String currentCardReference = "currentCard" + this.cardStorage.getSmartcards(); System.out.println("currentCardReference : "+ currentCardReference); return currentCardReference; } else { return null; } } private void storeCardStorageReference() { lastCardReference = this.getCurrentCardReference(); System.out.println("storeCardStorageReference - after authenticate! " + lastCardReference); } private boolean isSameCardStorageReference() { String current = this.getCurrentCardReference(); System.out.println("isSameCardStoarageReference - last : " + lastCardReference + " - cur : " + current); if((lastCardReference!=null) && lastCardReference.equals(current)) { logger.fine("- still same card! " + lastCardReference); return true; } else { logger.fine("- card updated!"); return false; } } /** * The pins in pinsStr must match in order and number with the list of * smartcards returned by {@code this.cardStorage.getClosedSmartcards();} * * @param pinsStr * @return * @throws Exception */ @POST() @Path("/user/unlockSmartcards/{SessionID}") @Consumes({ MediaType.TEXT_PLAIN }) public Response unlockSmartcards(@PathParam ("SessionID") final String sessionId, final String pinsStr) throws Exception { System.out.println("unlockSmartcards - called with: " + sessionId +" and pinstr: " + "xxxx - softwareSmart : " + softwareSmartcard ); // pinsStr); // if(softwareSmartcard!=null && lastCardReference==null) { // thismight be first we ust smartcard! System.out.println("is this first time we use smartcard ?? : " + softwareSmartcard + " : " + lastCardReference + " - storage size : " + this.cardStorage.getSmartcards().size()); if(this.cardStorage.getSmartcard(softwareSmartcard.getDeviceURI(Integer.parseInt(pinsStr))) == null) { // not added! System.out.println("unlockSmartcards - add software smartcard : " + softwareSmartcard + " : " + this.cardStorage.getSmartcards()); boolean status = this.cardStorage.addSmartcard(softwareSmartcard, Integer.parseInt(pinsStr)); System.out.println("Unlock - add card status : " + status + " - current smartcars : " + this.cardStorage.getSmartcards() + " - closed cards : " + this.cardStorage.getClosedSmartcards() ); if(!status) { System.err.println("Unlock - software smartcard could not be unlocked..."); return Response.status(Status.UNAUTHORIZED).build(); } } else { System.out.println("unlockSmartcards - card already added! - but register it as last used!"); } // save card this.storeCardStorageReference(); return Response.noContent().build(); } else { System.err.println("NO softwareSmartcard FOUND ?? : " + softwareSmartcard + " : " + this.cardStorage.getSmartcards().size()); } if(this.cardStorage.getSmartcards().size() == 1){ for(URI uri : this.cardStorage.getSmartcards().keySet()){ try { this.cardStorage.getSmartcards().get(uri).getDeviceID(Integer.parseInt(pinsStr)); } catch(IllegalStateException e) { // card has been removed! logger.fine("Card has been removed ?? " + e); break; } // - go ahead! if(this.cardStorage.getSmartcards().get(uri).getDeviceURI(Integer.parseInt(pinsStr)) == null){ return Response.status(Status.CONFLICT).build(); }else{ return Response.noContent().build(); } } } int pin = Integer.parseInt(pinsStr); int smartcards = this.cardStorage.getClosedSmartcards().size(); logger.fine("Smartcardpins. Size of closed smartcards: "+smartcards); if(smartcards == 1){ return this.checkPinAndAddSmartcardHelper(pin); }else if(smartcards > 1){ logger.fine("More than one smartcard was found. Aborting!"); return Response.status(Status.CONFLICT).build(); }else{ int waitSeconds = 0; int maxWaitSeconds = 60; //wait for a smartcard to appear while((smartcards == 0) && (waitSeconds <= maxWaitSeconds)){ try{ Thread.sleep(2000); waitSeconds += 2; smartcards = this.cardStorage.getClosedSmartcards().size(); logger.fine("no. of smartcards: "+ smartcards + " - waited for : " + waitSeconds + " <= " + maxWaitSeconds); }catch(InterruptedException e){ logger.fine("Waiting for smartcard Thread got interrupted."); if(smartcards == 1){ return this.checkPinAndAddSmartcardHelper(pin); }else{ logger.fine("SmartcardPins: No cards after interrupt. Sending conflict "); return Response.status(Status.CONFLICT).build(); } } } if(smartcards == 1){ return this.checkPinAndAddSmartcardHelper(pin); }else{ if(waitSeconds >= maxWaitSeconds) { logger.fine("Timeout waiting for smartcard after # seconds : " + maxWaitSeconds); } else { logger.fine("SmartcardPins: Strangely, the number of smartcards seem to now be: " + smartcards); } return Response.status(Status.CONFLICT).build(); } } // // ORIGINAL CODE ! // // boolean res = new SmartcardUnlocker().unlock(pinsStr, this.cardStorage); // if (res) { // return Response.ok().build(); // } // return Response.status(Status.FORBIDDEN).build(); } private Response checkPinAndAddSmartcardHelper(int pin){ Smartcard sc = (Smartcard) this.cardStorage.getClosedSmartcards().get(0); SmartcardStatusCode code = sc.changePin(pin, pin); if(code == SmartcardStatusCode.UNAUTHORIZED){ return Response.status(Status.UNAUTHORIZED).build(); }else if(code == SmartcardStatusCode.FORBIDDEN){ return Response.status(Status.FORBIDDEN).build(); } System.out.println("checkPinAndAddSmartcardHelper : " + this.cardStorage.getSmartcards().size() ); boolean added = this.cardStorage.addSmartcard(this.cardStorage.getClosedSmartcards().get(0), pin); if(added){ this.cardStorage.getClosedSmartcards().remove(0); } this.storeCardStorageReference(); // smartcardAvailable = true; return Response.noContent().build(); } @GET() @Path("/user/backupExists/{SessionID}") public Response backupExists(@PathParam ("SessionID") final String sessionID) throws Exception{ for(URI uri : this.cardStorage.getSmartcards().keySet()){ //We know there is only one card in this set Smartcard s = (Smartcard)this.cardStorage.getSmartcards().get(uri); int pin = this.cardStorage.getPin(uri); File f = new File(UserService.fileStoragePrefix+"smartcard_backup_"+s.getDeviceID(pin)+".bac"); logger.fine("BackupExists? : " + f.exists()); if(f.exists()){ return Response.status(204).build(); }else{ return Response.ok().build(); } } return Response.status(Status.NOT_FOUND).build(); } @POST() @Path("/user/backupSmartcard/{SessionID}") @Consumes({MediaType.TEXT_PLAIN}) public Response backupSmartcard(@PathParam ("SessionID") final String sessionId, final String password) throws Exception{ logger.fine("backupSmartcard"); if(Utils.passwordToByteArr(password) == null){ //Password not valid System.err.println("Password not valid!"); return Response.status(Status.NOT_ACCEPTABLE).build(); } if(!this.isSameCardStorageReference()) { // smartcardAvailable){ System.err.println("Smartcard not found."); return Response.status(Status.NOT_FOUND).build(); } if(this.cardStorage.getSmartcards().size() != 1){ System.err.println("Too many smartcards found."); return Response.status(Status.CONFLICT).build(); } try{ for(URI uri : this.cardStorage.getSmartcards().keySet()){ //We know there is only one card in this set Smartcard s = (Smartcard)this.cardStorage.getSmartcards().get(uri); int pin = this.cardStorage.getPin(uri); SmartcardBackup backup = s.backupAttendanceData(pin, password); if(backup == null){ return Response.status(Status.BAD_REQUEST).build(); } File f = new File(UserService.fileStoragePrefix+"smartcard_backup_"+s.getDeviceID(pin)+".bac"); backup.serialize(f); } } catch(Exception e){ e.printStackTrace(); return Response.status(Status.BAD_REQUEST).build(); } return Response.ok().build(); } @POST() @Path("/user/restoreSmartcard/{SessionID}") @Consumes({MediaType.TEXT_PLAIN}) public Response restoreSmartcard(@PathParam ("SessionID") final String sessionId, final String password) throws Exception{ logger.fine("restoreSmartcard"); if(Utils.passwordToByteArr(password) == null){ //Password not valid return Response.status(Status.NOT_ACCEPTABLE).build(); } if(!this.isSameCardStorageReference()) { // smartcardAvailable){ return Response.status(Status.NOT_FOUND).build(); } if(this.cardStorage.getSmartcards().size() != 1){ return Response.status(Status.CONFLICT).build(); } URI scURI = null; try{ for(URI uri : this.cardStorage.getSmartcards().keySet()){ //We know there is only one card in this set Smartcard s = (Smartcard)this.cardStorage.getSmartcards().get(uri); int pin = this.cardStorage.getPin(uri); File f = new File(UserService.fileStoragePrefix+"smartcard_backup_"+s.getDeviceID(pin)+".bac"); if(!f.exists()){ logger.fine("Backup file not found.. "); return Response.status(Status.NOT_FOUND).build(); } SmartcardBackup backup = SmartcardBackup.deserialize(f); SmartcardStatusCode code = s.restoreAttendanceData(pin, password, backup); if(code != SmartcardStatusCode.OK){ System.err.println("Restoration failed: " + code); return Response.status(Status.BAD_REQUEST).build(); } scURI = uri; } } catch(Exception e){ e.printStackTrace(); return Response.status(Status.BAD_REQUEST).build(); } boolean removed = this.cardStorage.removeSmartcard(this.cardStorage.getSmartcards().get(scURI)); if(removed) { this.storeCardStorageReference(); // smartcardAvailable = false; } return Response.ok().build(); } @POST() @Path("/user/changePin") @Consumes(MediaType.TEXT_PLAIN) public Response changePin(String pins) throws Exception{ boolean sameCard = this.isSameCardStorageReference(); Smartcard sc = null; boolean closedCard = false; if(sameCard){ for(URI uri : this.cardStorage.getSmartcards().keySet()){ sc = (Smartcard) this.cardStorage.getSmartcard(uri); break; } }else{ sc = (Smartcard) this.cardStorage.getClosedSmartcards().get(0); closedCard = true; } String[] pinSplit = pins.split(" "); int oldPin_ = Integer.parseInt(pinSplit[0]); int newPin_ = Integer.parseInt(pinSplit[1]); SmartcardStatusCode code = sc.changePin(oldPin_, newPin_); // - save smartcard saveSoftwareSmartcard(); if(code == SmartcardStatusCode.UNAUTHORIZED){ return Response.status(401).build(); }else if(code == SmartcardStatusCode.FORBIDDEN){ return Response.status(402).build(); }else{ if(closedCard){ System.out.println("changePin - 1: " + this.cardStorage.getSmartcards().size() ); boolean added = this.cardStorage.addSmartcard(sc, newPin_); if(added){ this.cardStorage.getClosedSmartcards().remove(0); } this.storeCardStorageReference(); // smartcardAvailable = true; }else{ System.out.println("changePin 2 : " + this.cardStorage.getSmartcards().size() ); this.cardStorage.removeSmartcard(sc); this.cardStorage.addSmartcard(sc, newPin_); } return Response.ok().build(); } } @POST() @Path("/user/unlockCard") @Consumes(MediaType.TEXT_PLAIN) public Response unlockCard(String pukAndPin) throws Exception{ boolean sameCard = this.isSameCardStorageReference(); Smartcard sc = null; if(sameCard){ for(URI uri : this.cardStorage.getSmartcards().keySet()){ sc = (Smartcard) this.cardStorage.getSmartcard(uri); } }else{ sc = (Smartcard) this.cardStorage.getClosedSmartcards().get(0); } String[] pinSplit = pukAndPin.split(" "); int puk = Integer.parseInt(pinSplit[0]); int pin = Integer.parseInt(pinSplit[1]); SmartcardStatusCode code = sc.resetPinWithPuk(puk, pin); // - save smartcard saveSoftwareSmartcard(); if(code == SmartcardStatusCode.UNAUTHORIZED){ //Wrong Puk return Response.status(401).build(); }else if(code == SmartcardStatusCode.FORBIDDEN){ //Wrong puk 10 times in a row.. card is dead. return Response.status(402).build(); }else{ return Response.ok().build(); } } // private Response handleUserDataBlob(String storeValue) { // // if(!this.isSameCardStorageReference()) { // smartcardAvailable){ // System.err.println("Smartcard not found."); // return Response.status(Status.NOT_FOUND).build(); // } // if(this.cardStorage.getSmartcards().size() != 1){ // System.err.println("Too many smartcards found."); // return Response.status(Status.CONFLICT).build(); // } // // try{ // Smartcard s = null; // int pin = -1; // for(URI uri : this.cardStorage.getSmartcards().keySet()){ // //We know there is only one card in this set // s = (Smartcard)this.cardStorage.getSmartcards().get(uri); // pin = this.cardStorage.getPin(uri); // break; // } // if(s==null) { // System.err.println("Smartcards disappeared."); // return Response.status(Status.CONFLICT).build(); // } // // // hvordan vælger man uri // URI soderhamnDataStoreBlobURI = URI.create("urn:datablob"); // // //Set<URI> blobUris = s.getBlobUris(pin); // //logger.fine("blobUris " + blobUris); // // we now have card! // if(storeValue!=null) { // // we store // logger.fine("storeData - new value : [" + storeValue + "]"); // // // skal gammel blob slettes først ? // SmartcardBlob exits = s.getBlob(pin, soderhamnDataStoreBlobURI); // logger.fine("- blob exits " + exits); // SmartcardBlob replace = new SmartcardBlob(); // replace.blob = storeValue.getBytes("UTF-8"); // int maxAmountOfBytes = HardwareSmartcard.MAX_BLOB_BYTES; // if(replace.blob.length > maxAmountOfBytes){ // //We need to split the blob - rounding up // int amountOfBlobs = (replace.blob.length+maxAmountOfBytes-1) / maxAmountOfBytes; // for(int i = 0; i < amountOfBlobs; i++){ // byte[] toStore = new byte[maxAmountOfBytes]; // int bytesLeft = replace.blob.length-(i*maxAmountOfBytes); // int amountToCopy = (bytesLeft > maxAmountOfBytes) ? maxAmountOfBytes : bytesLeft; // System.arraycopy(replace.blob, i*maxAmountOfBytes, toStore, 0, amountToCopy); // SmartcardBlob blobToStore = new SmartcardBlob(); // blobToStore.blob = toStore; // URI uriToStoreUnder; // if(i == 0){ // uriToStoreUnder = soderhamnDataStoreBlobURI; // }else{ // uriToStoreUnder = URI.create(soderhamnDataStoreBlobURI.toString()+":"+i); // } // s.storeBlob(pin, uriToStoreUnder, blobToStore); // } // }else{ // SmartcardStatusCode status = s.storeBlob(pin, soderhamnDataStoreBlobURI, replace); // logger.fine("Status of storing!" + status + " : " + new String(replace.blob) + " : " + replace.getLength()); // int i = 1; // while(status == SmartcardStatusCode.OK){ // status = s.deleteBlob(pin, URI.create(soderhamnDataStoreBlobURI.toString()+":"+i)); // logger.fine("(tried to) remove the intermediate blob: "+soderhamnDataStoreBlobURI.toString()+":"+i); // i++; // } // } // // // // this.saveSoftwareSmartcard(); // // //logger.fine("control : "+ s.getBlob(pin, soderhamnDataStoreBlobURI)); // logger.fine("Done storing data"); // // } else { // // we load // logger.fine("loadData..."); // // ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); // SmartcardBlob blob = s.getBlob(pin, soderhamnDataStoreBlobURI); // logger.fine("- blob : " + blob); // if(blob!=null) { // logger.fine("- blob length : " + blob.getLength()); // logger.fine("- blob : " + new String(blob.blob)); // byteStream.write(blob.blob); // int i = 1; // SmartcardBlob tmpBlob = new SmartcardBlob(); // tmpBlob.blob = blob.blob; // while(true){ // if(tmpBlob.getLength() == HardwareSmartcard.MAX_BLOB_BYTES){ // URI tmpBlobURI = URI.create(soderhamnDataStoreBlobURI.toString()+":"+i); // tmpBlob = s.getBlob(pin, tmpBlobURI); // if(tmpBlob == null){ // break; // } // byteStream.write(tmpBlob.blob); // i++; // }else{ // break; // } // } // } // // if((blob == null) || (blob.getLength()==0)) { // logger.fine("Done loading data. - no data in blob"); // return Response.ok("").build(); // } else { // logger.fine("Done loading data. Result: "+new String(byteStream.toByteArray(), "UTF-8").trim()); // String dataStorageValue = new String(byteStream.toByteArray(), "UTF-8").trim(); // return Response.ok(dataStorageValue).build(); // } // // } // // // // } catch(Exception e){ // e.printStackTrace(); // return Response.status(Status.BAD_REQUEST).build(); // } // return Response.ok().build(); // // } /** * */ @POST() @Path("/user/storeData/{SessionID}") @Consumes(MediaType.TEXT_PLAIN) public Response storeData(@PathParam ("SessionID") final String sessionId, final String value) throws Exception { userServiceBusy = true; // NO DATA SOTRING IN DEMO! Response resp = Response.ok().build(); // this.handleUserDataBlob(value); userServiceBusy = false; return resp; } /** * */ @GET() @Path("/user/loadData/{SessionID}") @Produces(MediaType.TEXT_PLAIN + ";charset=UTF-8") public Response loadData(@PathParam ("SessionID") final String sessionId) throws Exception { userServiceBusy = true; // NO DATA SOTRING IN DEMO! Response resp = Response.ok().build(); // this.handleUserDataBlob(null); userServiceBusy = false; return resp; } @GET() @Path("/user/getCredentialDescriptionList/{SessionID}") @Consumes({MediaType.APPLICATION_XML, MediaType.TEXT_XML}) @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN}) public Response getCredentialDescriptionList(@PathParam ("SessionID") final String sessionId) throws Exception { try { ObjectFactory of = new ObjectFactory(); logger.fine("getCredentialDescriptionList - " + sessionId); CredentialDescriptions credentialDescriptions = new CredentialDescriptions(); List<URI> uriList = this.engine.listCredentials(UserService.USER_NAME); logger.fine(" # of credentials : " + (uriList == null ? " 'null' " : uriList.size())); for(URI uri: uriList) { logger.fine("- credential URI : " + uri); CredentialDescription cd = this.engine.getCredentialDescription(UserService.USER_NAME, uri); CredentialSpecification credSpec = UserHelper.getInstance().keyManager.getCredentialSpecification(cd.getCredentialSpecificationUID()); // Add revocation status as attribute boolean isRevoked = credSpec.isRevocable() ? engine.isRevoked(UserService.USER_NAME, uri) : false; Attribute isRevokedAtt = of.createAttribute(); AttributeDescription ad = of.createAttributeDescription(); ad.setType(new URI("urn:abc4trust:gui:isRevoked")); ad.setEncoding(new URI("urn:abc4trust:1.0:encoding:boolean:unsigned")); ad.setDataType(new URI("xs:boolean")); isRevokedAtt.setAttributeDescription(ad); isRevokedAtt.setAttributeUID(new URI(cd.getCredentialUID().toString()+":gui:isRevoked")); isRevokedAtt.setAttributeValue(isRevoked); cd.getAttribute().add(isRevokedAtt); CredentialDescriptionsEntry entry = new CredentialDescriptionsEntry(); entry.setKey(uri); entry.setValue(cd); credentialDescriptions.getEntry().add(entry); } // TODO : check if this is patras - and try to add counter - as a credential... // try { // logger.fine("JSON/XML for CredentialList : " + XmlUtils.toXml(this.of.createCredentialDescriptions(credentialDescriptions))); // } catch(Exception e) { // logger.fine("Failed logging JSON/XML for CredentialList : " + e); // } return Response.ok(this.of.createCredentialDescriptions(credentialDescriptions)).build(); } catch(Exception e) { logger.log(Level.SEVERE, " - failed", e); return Response.serverError().build(); } } @GET() @Path("/user/getUiManageCredentialData/{SessionID}") @Consumes({MediaType.APPLICATION_XML, MediaType.TEXT_XML}) @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN}) public Response getUiManageCredentialData(@PathParam ("SessionID") final String sessionId) throws Exception { try { logger.fine("getUiCredentialDescriptionList - " + sessionId); List<URI> uriList = this.engine.listCredentials(UserService.USER_NAME); logger.fine(" # of credentials : " + (uriList == null ? " 'null' " : uriList.size())); UiManageCredentialData uiManageCredentialData = new UiManageCredentialData(); UiCommonArguments uiCredListInfo = uiManageCredentialData.data; for(URI uri: uriList) { logger.fine("- credential URI : " + uri); CredentialDescription cd = this.engine.getCredentialDescription(UserService.USER_NAME, uri); if(cd.getSecretReference()==null) { System.out.println("- NO Secret Reference in CredentialDescription! - try to fix" + uri); Credential c = UserHelper.getInstance().credentialManager.getCredential(UserService.USER_NAME, uri); // c.getCredentialDescription().setSecretReference(SECRET_BASED_SMARTCARD_URI); UserHelper.getInstance().credentialManager.updateCredential(UserService.USER_NAME, c); cd = c.getCredentialDescription(); Credential c_constrol = UserHelper.getInstance().credentialManager.getCredential(UserService.USER_NAME, uri); System.out.println(" : " + c_constrol.getCredentialDescription().getSecretReference()); } else { System.out.println("- CredentialDescription has Secret Reference !! " + cd.getSecretReference()); } CredentialSpecification credSpec = UserHelper.getInstance().keyManager.getCredentialSpecification(cd.getCredentialSpecificationUID()); IssuerParameters ip = UserHelper.getInstance().keyManager.getIssuerParameters(cd.getIssuerParametersUID()); IssuerInUi issInUi = new IssuerInUi(ip); uiCredListInfo.addIssuer(issInUi ); boolean isRevoked = false; RevocationAuthorityParameters rap = null; if( credSpec.isRevocable() ) { isRevoked = engine.isRevoked(UserService.USER_NAME, uri); URI revocationParametersUID = ip.getRevocationParametersUID(); rap = UserHelper.getInstance().keyManager.getRevocationAuthorityParameters(revocationParametersUID); uiManageCredentialData.revokedCredentials.put(uri, isRevoked); } // set revoked - for UI... cd.setRevokedByIssuer(isRevoked); CredentialSpecInUi csInUi = new CredentialSpecInUi(credSpec); uiCredListInfo.addCredentialSpec(csInUi ); CredentialInUi credInUi = new CredentialInUi(cd, ip , credSpec, rap); uiCredListInfo.addCredential(credInUi); logger.fine(" -- Added Credential : " + cd.getCredentialSpecificationUID() + " : " + ip.getVersion()); } JAXBElement<UiManageCredentialData> jaxb = ObjectFactoryReturnTypes.wrap(uiManageCredentialData); return Response.ok(jaxb).build(); } catch(Exception e) { System.err.println("FAILED : getUiCredentialDescriptionList - " + sessionId + " : " + e.getMessage()); e.printStackTrace(); return Response.serverError().build(); } } @GET() @Path("/user/getDebugInfo/{SessionID}") @Consumes({MediaType.TEXT_PLAIN,MediaType.APPLICATION_XML, MediaType.TEXT_XML}) @Produces(MediaType.TEXT_PLAIN) public Response getDebugInfo(@PathParam ("SessionID") final String sessionId) throws Exception { logger.fine("getDebugInfo - " + sessionId); try { StringBuilder sb = new StringBuilder(debugInfo); sb.append("\n"); sb.append(logMemoryUsage()); return Response.ok(sb.toString()).build(); } catch(Exception e) { logger.log(Level.SEVERE, " - failed", e); return Response.serverError().build(); } } @GET() @Path("/alive/isAlive") @Produces(MediaType.TEXT_PLAIN) public Response isAlive() throws Exception { if(userServiceBusy){ return Response.status(201).build(); }else{ return Response.ok().build(); } } @GET() @Path("/info/deploymentVersionId") @Produces(MediaType.TEXT_PLAIN) public Response deploymentVersionId() throws Exception { logger.fine("deploymentVersionId : " + deploymentVersionId); logger.fine("softwareCard used?: "+ softwareSmartcard); String toReturn = ""; if(softwareSmartcard != null){ toReturn += "software "; }else{ toReturn += "hardware "; } toReturn+=deploymentVersionId; return Response.ok(toReturn).build(); } @GET() @Path("/info/userServiceVersionId") @Produces(MediaType.TEXT_PLAIN) public Response userServiceVersionId() throws Exception { logger.fine("userServiceVersionId : " + userServiceVersionId); logger.fine("softwareCard used?: "+ softwareSmartcard); String toReturn = ""; if(softwareSmartcard != null){ toReturn += "software "; }else{ toReturn += "hardware "; } toReturn+=userServiceVersionId; return Response.ok(toReturn).build(); } @GET() @Path("/debug/timingsOnOff") @Produces(MediaType.TEXT_PLAIN) public Response timingsOnOff() throws Exception { //TODO: Make this actually change some static variable in abce-components. boolean on = TimingsLogger.toogleLogger(); logger.fine("timings are now on?: " + on); if(on){ return Response.ok("true").build(); }else{ return Response.ok("false").build(); } } @GET() @Path("/info/getAttendanceData") @Produces(MediaType.TEXT_PLAIN) public Response attendanceData() throws Exception { if(!this.isSameCardStorageReference()) { // smartcardAvailable){ System.err.println("Smartcard not found."); return Response.status(Status.NOT_FOUND).build(); } if(this.cardStorage.getSmartcards().size() != 1){ System.err.println("Too many smartcards found."); return Response.status(Status.CONFLICT).build(); } try{ Smartcard s = null; int pin = -1; for(URI uri : this.cardStorage.getSmartcards().keySet()){ //We know there is only one card in this set s = (Smartcard)this.cardStorage.getSmartcards().get(uri); pin = this.cardStorage.getPin(uri); break; } if(s==null) { System.err.println("Smartcards disappeared."); return Response.status(Status.CONFLICT).build(); } //Ready to do work with the card int counterValue = -1; try{ counterValue = s.getCounterValue(pin, StaticUriToIDMap.courseIssuerUID); }catch(Exception e){ System.err.println("exception when getting counterValue. Sending -1 back. Exception was: "); e.printStackTrace(); } return Response.ok(""+counterValue).build(); }catch(Exception e){ logger.fine("getAttendanceData - failed"); e.printStackTrace(); return Response.serverError().build(); } } }