/****************************************************************************
* Copyright (C) 2012-2015 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.sal.protocol.eac;
import iso.std.iso_iec._24727.tech.schema.DIDAuthenticate;
import iso.std.iso_iec._24727.tech.schema.DIDAuthenticateResponse;
import java.util.Map;
import org.openecard.addon.sal.FunctionType;
import org.openecard.addon.sal.ProtocolStep;
import org.openecard.binding.tctoken.TR03112Keys;
import org.openecard.common.DynamicContext;
import org.openecard.common.ECardConstants;
import org.openecard.common.WSHelper;
import org.openecard.common.interfaces.Dispatcher;
import org.openecard.common.interfaces.ObjectSchemaValidator;
import org.openecard.common.interfaces.ObjectValidatorException;
import org.openecard.common.util.Promise;
import org.openecard.crypto.common.asn1.cvc.CardVerifiableCertificate;
import org.openecard.crypto.common.asn1.cvc.CardVerifiableCertificateChain;
import org.openecard.sal.protocol.eac.anytype.EAC2InputType;
import org.openecard.sal.protocol.eac.anytype.EAC2OutputType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Implements TerminalAuthentication protocol step according to BSI-TR-03112-7.
* See BSI-TR-03112, version 1.1.2, part 7, section 4.6.6.
*
* @author Moritz Horsch
* @author Dirk Petrautzki
* @author Tobias Wich
* @author Hans-Martin Haase
*/
public class TerminalAuthenticationStep implements ProtocolStep<DIDAuthenticate, DIDAuthenticateResponse> {
private static final Logger logger = LoggerFactory.getLogger(TerminalAuthenticationStep.class.getName());
private final Dispatcher dispatcher;
/**
* Creates a new Terminal Authentication protocol step.
*
* @param dispatcher Dispatcher
*/
public TerminalAuthenticationStep(Dispatcher dispatcher) {
this.dispatcher = dispatcher;
}
@Override
public FunctionType getFunctionType() {
return FunctionType.DIDAuthenticate;
}
@Override
public DIDAuthenticateResponse perform(DIDAuthenticate didAuthenticate, Map<String, Object> internalData) {
DIDAuthenticateResponse response = new DIDAuthenticateResponse();
DynamicContext dynCtx = DynamicContext.getInstance(TR03112Keys.INSTANCE_KEY);
try {
ObjectSchemaValidator valid = (ObjectSchemaValidator) dynCtx.getPromise(EACProtocol.SCHEMA_VALIDATOR).deref();
boolean messageValid = valid.validateObject(didAuthenticate);
if (! messageValid) {
String msg = "Validation of the EAC2InputType message failed.";
logger.error(msg);
dynCtx.put(EACProtocol.AUTHENTICATION_FAILED, true);
response.setResult(WSHelper.makeResultError(ECardConstants.Minor.App.INCORRECT_PARM, msg));
return response;
}
} catch (ObjectValidatorException ex) {
String msg = "Validation of the EAC2InputType message failed due to invalid input data.";
logger.error(msg, ex);
dynCtx.put(EACProtocol.AUTHENTICATION_FAILED, true);
response.setResult(WSHelper.makeResultError(ECardConstants.Minor.App.INT_ERROR, msg));
return response;
} catch (InterruptedException ex) {
String msg = "Thread interrupted while waiting for schema validator instance.";
logger.error(msg, ex);
dynCtx.put(EACProtocol.AUTHENTICATION_FAILED, true);
response.setResult(WSHelper.makeResultError(ECardConstants.Minor.App.INT_ERROR, msg));
return response;
}
byte[] slotHandle = didAuthenticate.getConnectionHandle().getSlotHandle();
try {
EAC2InputType eac2Input = new EAC2InputType(didAuthenticate.getAuthenticationProtocolData());
EAC2OutputType eac2Output = eac2Input.getOutputType();
TerminalAuthentication ta = new TerminalAuthentication(dispatcher, slotHandle);
// Build certificate chain
CardVerifiableCertificateChain certificateChain;
certificateChain = (CardVerifiableCertificateChain) internalData.get(EACConstants.IDATA_CERTIFICATES);
certificateChain.addCertificates(eac2Input.getCertificates());
byte[] currentCAR = (byte[]) internalData.get(EACConstants.IDATA_CURRENT_CAR);
byte[] previousCAR = (byte[]) internalData.get(EACConstants.IDATA_PREVIOUS_CAR);
CardVerifiableCertificateChain tmpChain = certificateChain.getCertificateChainFromCAR(currentCAR);
// try again with previous car if it didn't work
if (tmpChain.getCertificates().isEmpty() && previousCAR != null) {
tmpChain = certificateChain.getCertificateChainFromCAR(previousCAR);
}
certificateChain = tmpChain;
if (certificateChain.getCertificates().isEmpty()) {
String msg = "Failed to create a valid certificate chain from the transmitted certificates.";
logger.error(msg);
response.setResult(WSHelper.makeResultError(ECardConstants.Minor.App.INCORRECT_PARM, msg));
return response;
}
// TA: Step 1 - Verify certificates
ta.verifyCertificates(certificateChain);
// save values for later use
CardVerifiableCertificate terminalCertificate = certificateChain.getTerminalCertificate();
byte[] key = eac2Input.getEphemeralPublicKey();
byte[] signature = eac2Input.getSignature();
internalData.put(EACConstants.IDATA_PK_PCD, key);
internalData.put(EACConstants.IDATA_SIGNATURE, signature);
internalData.put(EACConstants.IDATA_TERMINAL_CERTIFICATE, terminalCertificate);
if (signature != null) {
logger.trace("Signature has been provided in EAC2InputType.");
// perform TA and CA authentication
ChipAuthentication ca = new ChipAuthentication(dispatcher, slotHandle);
AuthenticationHelper auth = new AuthenticationHelper(ta, ca);
eac2Output = auth.performAuth(eac2Output, internalData);
// no third step needed, notify GUI
DynamicContext ctx = DynamicContext.getInstance(TR03112Keys.INSTANCE_KEY);
ctx.put(EACProtocol.AUTHENTICATION_DONE, true);
} else {
logger.trace("Signature has not been provided in EAC2InputType.");
// send challenge again
byte[] rPICC = (byte[]) internalData.get(EACConstants.IDATA_CHALLENGE);
eac2Output.setChallenge(rPICC);
}
response.setResult(WSHelper.makeResultOK());
response.setAuthenticationProtocolData(eac2Output.getAuthDataType());
} catch (Exception e) {
logger.error(e.getMessage(), e);
response.setResult(WSHelper.makeResultUnknownError(e.getMessage()));
dynCtx.put(EACProtocol.AUTHENTICATION_FAILED, true);
}
Promise<Object> p = (Promise<Object>) dynCtx.getPromise(TR03112Keys.PROCESSING_CANCELLATION);
if (p.derefNonblocking() == null) {
return response;
} else {
response = new DIDAuthenticateResponse();
String msg = "Authentication Canceled by the user.";
response.setResult(WSHelper.makeResultError(ECardConstants.Minor.SAL.CANCELLATION_BY_USER, msg));
return response;
}
}
}