/****************************************************************************
* Copyright (C) 2012 HS Coburg.
* 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.genericcryptography;
import iso.std.iso_iec._24727.tech.schema.ConnectionHandleType;
import iso.std.iso_iec._24727.tech.schema.CryptographicServiceActionName;
import iso.std.iso_iec._24727.tech.schema.DIDScopeType;
import iso.std.iso_iec._24727.tech.schema.DIDStructureType;
import iso.std.iso_iec._24727.tech.schema.Sign;
import iso.std.iso_iec._24727.tech.schema.SignResponse;
import iso.std.iso_iec._24727.tech.schema.TransmitResponse;
import java.util.Collections;
import java.util.Map;
import org.openecard.addon.sal.FunctionType;
import org.openecard.addon.sal.ProtocolStep;
import org.openecard.bouncycastle.util.Arrays;
import org.openecard.common.ECardException;
import org.openecard.common.WSHelper;
import org.openecard.common.apdu.InternalAuthenticate;
import org.openecard.common.apdu.ManageSecurityEnvironment;
import org.openecard.common.apdu.common.CardCommandAPDU;
import org.openecard.common.apdu.common.CardResponseAPDU;
import org.openecard.common.interfaces.Dispatcher;
import org.openecard.common.sal.Assert;
import org.openecard.common.sal.anytype.CryptoMarkerType;
import org.openecard.common.sal.exception.IncorrectParameterException;
import org.openecard.common.sal.state.CardStateEntry;
import org.openecard.common.sal.util.SALUtils;
import org.openecard.common.tlv.TLV;
import org.openecard.common.util.ByteUtils;
import org.openecard.sal.protocol.genericcryptography.apdu.PSOComputeDigitalSignature;
import org.openecard.sal.protocol.genericcryptography.apdu.PSOHash;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Implements the Sign step of the Generic cryptography protocol.
* See TR-03112, version 1.1.2, part 7, section 4.9.9.
*
* @author Dirk Petrautzki <petrautzki@hs-coburg.de>
*/
public class SignStep implements ProtocolStep<Sign, SignResponse> {
private static final Logger logger = LoggerFactory.getLogger(SignStep.class);
//TODO extract the blocksize from somewhere
private static final byte BLOCKSIZE = (byte) 256;
private static final byte SET_COMPUTATION = (byte) 0x41;
private static final byte KEY_REFERENCE_PRIVATE_KEY = (byte) 0x84;
private final Dispatcher dispatcher;
/**
* Creates a new SignStep.
*
* @param dispatcher Dispatcher
*/
public SignStep(Dispatcher dispatcher) {
this.dispatcher = dispatcher;
}
@Override
public FunctionType getFunctionType() {
return FunctionType.Sign;
}
@Override
public SignResponse perform(Sign sign, Map<String, Object> internalData) {
SignResponse response = WSHelper.makeResponse(SignResponse.class, WSHelper.makeResultOK());
try {
ConnectionHandleType connectionHandle = SALUtils.getConnectionHandle(sign);
String didName = SALUtils.getDIDName(sign);
CardStateEntry cardStateEntry = SALUtils.getCardStateEntry(internalData, connectionHandle);
DIDStructureType didStructure = SALUtils.getDIDStructure(sign, didName, cardStateEntry, connectionHandle);
CryptoMarkerType cryptoMarker = new CryptoMarkerType(didStructure.getDIDMarker());
byte[] slotHandle = connectionHandle.getSlotHandle();
byte[] applicationID = connectionHandle.getCardApplication();
Assert.securityConditionDID(cardStateEntry, applicationID, didName, CryptographicServiceActionName.SIGN);
byte[] keyReference = cryptoMarker.getCryptoKeyInfo().getKeyRef().getKeyRef();
byte[] algorithmIdentifier = cryptoMarker.getAlgorithmInfo().getCardAlgRef();
if (didStructure.getDIDScope().equals(DIDScopeType.LOCAL)) {
keyReference[0] = (byte) (0x80 | keyReference[0]);
}
byte[] message = sign.getMessage();
byte[] signature = new byte[0];
TLV tagAlgorithmIdentifier = new TLV();
tagAlgorithmIdentifier.setTagNumWithClass(0x80);
tagAlgorithmIdentifier.setValue(algorithmIdentifier);
CardCommandAPDU cmdAPDU = null;
CardResponseAPDU responseAPDU = null;
String[] signatureGenerationInfo = cryptoMarker.getSignatureGenerationInfo();
for (int i = 0; i < signatureGenerationInfo.length; i++) {
String command = signatureGenerationInfo[i];
String nextCmd = "";
if (i < signatureGenerationInfo.length - 1) {
nextCmd = signatureGenerationInfo[i + 1];
}
if (command.equals("MSE_KEY")) {
TLV tagKeyReference = new TLV();
tagKeyReference.setTagNumWithClass(KEY_REFERENCE_PRIVATE_KEY);
tagKeyReference.setValue(keyReference);
if (nextCmd.equals("PSO_CDS")) {
byte[] mseData = ByteUtils.concatenate(tagKeyReference.toBER(), tagAlgorithmIdentifier.toBER());
cmdAPDU = new ManageSecurityEnvironment(SET_COMPUTATION, ManageSecurityEnvironment.DST, mseData);
} else if (nextCmd.equals("INT_AUTH")) {
byte[] mseData = ByteUtils.concatenate(tagKeyReference.toBER(), tagAlgorithmIdentifier.toBER());
cmdAPDU = new ManageSecurityEnvironment(SET_COMPUTATION, ManageSecurityEnvironment.AT, mseData);
} else {
String msg = "The command 'MSE_KEY' followed by '" + nextCmd + "' is currently not supported.";
logger.error(msg);
throw new IncorrectParameterException(msg);
}
} else if (command.equals("PSO_CDS")) {
cmdAPDU = new PSOComputeDigitalSignature(message, BLOCKSIZE);
} else if (command.equals("INT_AUTH")) {
cmdAPDU = new InternalAuthenticate(message, BLOCKSIZE);
} else if (command.equals("MSE_RESTORE")) {
cmdAPDU = new ManageSecurityEnvironment.Restore(ManageSecurityEnvironment.DST);
} else if (command.equals("MSE_HASH")) {
cmdAPDU = new ManageSecurityEnvironment.Set(SET_COMPUTATION, ManageSecurityEnvironment.HT);
} else if (command.equals("PSO_HASH")) {
cmdAPDU = new PSOHash(signature);
} else if (command.equals("MSE_DS")) {
cmdAPDU = new ManageSecurityEnvironment.Set(SET_COMPUTATION, ManageSecurityEnvironment.DST);
} else if (command.equals("MSE_KEY_DS")) {
cmdAPDU = new ManageSecurityEnvironment.Set(SET_COMPUTATION, ManageSecurityEnvironment.DST);
} else {
String msg = "The signature generation command '" + command + "' is unknown.";
throw new IncorrectParameterException(msg);
}
responseAPDU = cmdAPDU.transmit(dispatcher, slotHandle, Collections.<byte[]>emptyList());
}
byte[] signedMessage = responseAPDU.getData();
// check if further response data is available
while (responseAPDU.getTrailer()[0] == (byte) 0x61) {
CardCommandAPDU getResponseData = new CardCommandAPDU((byte) 0x00, (byte) 0xC0, (byte) 0x00, (byte) 0x00,
responseAPDU.getTrailer()[1]);
responseAPDU = getResponseData.transmit(dispatcher, slotHandle, Collections.<byte[]>emptyList());
signedMessage = Arrays.concatenate(signedMessage, responseAPDU.getData());
}
if (! Arrays.areEqual(responseAPDU.getTrailer(), new byte[] {(byte) 0x90, (byte) 0x00})) {
TransmitResponse tr = new TransmitResponse();
tr.getOutputAPDU().add(responseAPDU.toByteArray());
WSHelper.checkResult(response);
}
response.setSignature(signedMessage);
} catch (ECardException e) {
response.setResult(e.getResult());
} catch (Exception e) {
logger.warn(e.getMessage(), e);
response.setResult(WSHelper.makeResult(e));
}
return response;
}
}