/**************************************************************************** * Copyright (C) 2012 ecsec GmbH. * All rights reserved. * Contact: ecsec GmbH (info@ecsec.de) * * This file is part of the Open eCard App. * * GNU General Public License Usage * This file may be used under the terms of the GNU General Public * License version 3.0 as published by the Free Software Foundation * and appearing in the file LICENSE.GPL included in the packaging of * this file. Please review the following information to ensure the * GNU General Public License version 3.0 requirements will be met: * http://www.gnu.org/copyleft/gpl.html. * * Other Usage * Alternatively, this file may be used in accordance with the terms * and conditions contained in a signed written agreement between * you and ecsec GmbH. * ***************************************************************************/ package org.openecard.pkcs11; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.EnumMap; import javax.annotation.Nonnull; import org.json.JSONException; import org.json.JSONObject; import org.openecard.addon.Context; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Dispatcher implementation for PKCS11 functions. * The dispatcher takes PKCS11 function parameters and maps them to a previously loaded implementation. * <p>The implementation must contain public functions with the {@link PKCS11Dispatchable} annotation. The functions * must be named as in the pkcs11_f.h header. The available names are also defined in the {@link PKCS11Functions} enum. * <br/>The parameter type of the function must be <tt>{@link JSONObject} -> {@link PKCS11Result}</tt>.</p> * * @author Tobias Wich <tobias.wich@ecsec.de> */ public class PKCS11Dispatcher { private static final Logger logger = LoggerFactory.getLogger(PKCS11Dispatcher.class); private final Object instance; private EnumMap<PKCS11Functions, Method> functionIndex; /** * Creates a dispatcher instance using {@link PKCS11Impl} as the PKCS11 implementation. * * @param ctx */ public PKCS11Dispatcher(Context ctx) { this(new PKCS11Impl(ctx)); } /** * Creates a dispatcher instance and uses the given instance as PKCS11 implementation. * * @param instance Instance of a class implementing some or all of the PKCS11 functions. */ public PKCS11Dispatcher(Object instance) { this.instance = instance; this.functionIndex = buildIndex(instance); } /** * Dispatches the request data to the matching PKCS11 function in the underlying PKCS11 implementation. * The request data is converted to a JSON object and passed to the matching function. If no implementation of the * function exists, a result with an appropriate error and empty response data is returned. * * @param fName Name of the PKCS11 function as defined in the {@code pkcs11_f.h}. * @param request Parameter values for the PKCS11 function in the form of a JSON string. * @return Result containing a response code and optionally a JSON string with values to update reference * parameters. * @throws JSONException Thrown in case the input data is not a valid JSON string. */ @Nonnull public PKCS11Result dispatch(@Nonnull String fName, @Nonnull String request) throws JSONException { JSONObject reqData = new JSONObject(request); PKCS11Functions fType = PKCS11Functions.valueOf(fName); Method m = functionIndex.get(fType); if (m != null) { try { return (PKCS11Result) m.invoke(instance, reqData); } catch (IllegalAccessException ex) { String msg = String.format("Calling the function %s was permitted by JVM security mechanisms.", fName); logger.error(msg, ex); return new PKCS11Result(PKCS11ReturnCode.CKR_GENERAL_ERROR, msg); } catch (InvocationTargetException ex) { String msg = String.format("The function %s threw an exception of type %s.", fName, ex.getClass()); logger.error(msg, ex); return new PKCS11Result(PKCS11ReturnCode.CKR_GENERAL_ERROR, msg); } } else { String msg = String.format("Function %s is not implemented.", fName); logger.error(msg); return new PKCS11Result(PKCS11ReturnCode.CKR_GENERAL_ERROR, msg); } } private static EnumMap<PKCS11Functions, Method> buildIndex(Object impl) { EnumMap<PKCS11Functions, Method> functionIndex = new EnumMap<PKCS11Functions, Method>(PKCS11Functions.class); for (Method m : impl.getClass().getMethods()) { if (m.isAnnotationPresent(PKCS11Dispatchable.class)) { String fName = m.getName(); try { PKCS11Functions func = PKCS11Functions.valueOf(fName); if (functionIndex.containsKey(func)) { String msg = "Function overloading is not possible in the dispatcher. {} is defined twice."; logger.warn(msg, fName); } else { boolean resultOk = PKCS11Result.class.isAssignableFrom(m.getReturnType()); if (! resultOk) { logger.warn("Skipping function {} because of invalid return type.", fName); continue; } Class<?>[] params = m.getParameterTypes(); boolean paramOk = params.length == 1 ? JSONObject.class.isAssignableFrom(params[0]) : false; if (! paramOk) { logger.warn("Skipping function {} because of invalid parameter types.", fName); continue; } functionIndex.put(func, m); } } catch (IllegalArgumentException ex) { logger.warn("No PKCS11 method defined for name {}.", fName); } } } return functionIndex; } }