/*
* Copyright 2011 Licel LLC.
*
* 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 com.licel.jcardsim.crypto;
import java.lang.reflect.Field;
import javacard.framework.JCSystem;
import javacard.framework.Util;
import javacard.security.CryptoException;
import javacard.security.Key;
import javacard.security.Signature;
import javacard.security.SignatureMessageRecovery;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.Signer;
import org.bouncycastle.crypto.SignerWithRecovery;
import org.bouncycastle.crypto.digests.MD5Digest;
import org.bouncycastle.crypto.digests.RIPEMD160Digest;
import org.bouncycastle.crypto.digests.SHA1Digest;
import org.bouncycastle.crypto.engines.RSAEngine;
import org.bouncycastle.crypto.signers.DSADigestSigner;
import org.bouncycastle.crypto.signers.ECDSASigner;
import org.bouncycastle.crypto.signers.ISO9796d2Signer;
import org.bouncycastle.crypto.signers.RSADigestSigner;
/*
* Implementation <code>Signature</code> with asymmetric keys based
* on BouncyCastle CryptoAPI.
* @see Signature
*/
public class AsymmetricSignatureImpl extends Signature implements SignatureMessageRecovery{
Signer engine;
Key key;
byte algorithm;
boolean isInitialized;
boolean isRecovery;
byte[] preSig;
public AsymmetricSignatureImpl(byte algorithm) {
this.algorithm = algorithm;
isRecovery = false;
switch (algorithm) {
case ALG_RSA_SHA_ISO9796:
engine = new ISO9796d2Signer(new RSAEngine(), new SHA1Digest());
break;
case ALG_RSA_SHA_ISO9796_MR:
engine = new ISO9796d2Signer(new RSAEngine(), new SHA1Digest());
isRecovery = true;
break;
case ALG_RSA_SHA_PKCS1:
engine = new RSADigestSigner(new SHA1Digest());
break;
case ALG_RSA_MD5_PKCS1:
engine = new RSADigestSigner(new MD5Digest());
break;
case ALG_RSA_RIPEMD160_ISO9796:
engine = new ISO9796d2Signer(new RSAEngine(), new RIPEMD160Digest());
break;
case ALG_RSA_RIPEMD160_PKCS1:
engine = new RSADigestSigner(new RIPEMD160Digest());
break;
case ALG_ECDSA_SHA:
engine = new DSADigestSigner(new ECDSASigner(), new SHA1Digest());
break;
}
}
public void init(Key theKey, byte theMode) throws CryptoException {
if (theKey == null) {
CryptoException.throwIt(CryptoException.UNINITIALIZED_KEY);
}
if (!theKey.isInitialized()) {
CryptoException.throwIt(CryptoException.UNINITIALIZED_KEY);
}
if (!(theKey instanceof KeyWithParameters)) {
CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
}
engine.init(theMode == MODE_SIGN, ((KeyWithParameters) theKey).getParameters());
this.key = theKey;
isInitialized = true;
}
public void init(Key theKey, byte theMode, byte[] bArray, short bOff, short bLen) throws CryptoException {
CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
}
public short getLength() throws CryptoException {
if (!isInitialized) {
CryptoException.throwIt(CryptoException.INVALID_INIT);
}
if (!key.isInitialized()) {
CryptoException.throwIt(CryptoException.UNINITIALIZED_KEY);
}
switch (algorithm) {
case ALG_RSA_SHA_ISO9796:
case ALG_RSA_SHA_PKCS1:
case ALG_RSA_MD5_PKCS1:
case ALG_RSA_RIPEMD160_ISO9796:
case ALG_RSA_RIPEMD160_PKCS1:
return (short)(key.getSize()>>3);
case ALG_ECDSA_SHA:
// x,y + der payload
return (short)(((key.getSize()*2)>>3) + 8);
}
return 0;
}
public byte getAlgorithm() {
return algorithm;
}
public void update(byte[] inBuff, short inOffset, short inLength) throws CryptoException {
if (!isInitialized) {
CryptoException.throwIt(CryptoException.INVALID_INIT);
}
engine.update(inBuff, inOffset, inLength);
}
public short sign(byte[] inBuff, short inOffset, short inLength, byte[] sigBuff, short sigOffset) throws CryptoException {
if (isRecovery) {
CryptoException.throwIt(CryptoException.ILLEGAL_USE);
}
if (!isInitialized) {
CryptoException.throwIt(CryptoException.INVALID_INIT);
}
engine.update(inBuff, inOffset, inLength);
byte[] sig;
try {
sig = engine.generateSignature();
Util.arrayCopyNonAtomic(sig, (short) 0, sigBuff, sigOffset, (short) sig.length);
return (short) sig.length;
} catch (org.bouncycastle.crypto.CryptoException ex) {
CryptoException.throwIt(CryptoException.ILLEGAL_USE);
} catch (DataLengthException ex) {
CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
} finally {
engine.reset();
}
return -1;
}
public boolean verify(byte[] inBuff, short inOffset, short inLength, byte[] sigBuff, short sigOffset, short sigLength) throws CryptoException {
if (isRecovery) {
CryptoException.throwIt(CryptoException.ILLEGAL_USE);
}
if (!isInitialized) {
CryptoException.throwIt(CryptoException.INVALID_INIT);
}
engine.update(inBuff, inOffset, inLength);
byte[] sig = new byte[sigLength];
Util.arrayCopyNonAtomic(sigBuff, sigOffset, sig, (short) 0, sigLength);
boolean b = engine.verifySignature(sig);
engine.reset();
return b;
}
public short beginVerify(byte[] sigAndRecDataBuff, short buffOffset, short sigLength) throws CryptoException {
if (!isRecovery) {
CryptoException.throwIt(CryptoException.ILLEGAL_USE);
}
if (!isInitialized) {
CryptoException.throwIt(CryptoException.INVALID_INIT);
}
preSig = JCSystem.makeTransientByteArray(sigLength, JCSystem.CLEAR_ON_RESET);
Util.arrayCopyNonAtomic(sigAndRecDataBuff, buffOffset, preSig, (short) 0, sigLength);
try {
((SignerWithRecovery) engine).updateWithRecoveredMessage(preSig);
return (short) ((SignerWithRecovery) engine).getRecoveredMessage().length;
} catch (Exception ex) {
CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
}
return -1;
}
public short sign(byte[] inBuff, short inOffset, short inLength, byte[] sigBuff, short sigOffset, short[] recMsgLen,
short recMsgLenOffset) throws CryptoException {
if (!isRecovery) {
CryptoException.throwIt(CryptoException.ILLEGAL_USE);
}
if (!isInitialized) {
CryptoException.throwIt(CryptoException.INVALID_INIT);
}
engine.update(inBuff, inOffset, inLength);
byte[] sig;
try {
sig = engine.generateSignature();
Util.arrayCopyNonAtomic(sig, (short) 0, sigBuff, sigOffset, (short) sig.length);
// there is no direct way to obtain encoded message length
int keyBits = key.getSize();
Field messageLengthField = engine.getClass().getDeclaredField("messageLength");
messageLengthField.setAccessible(true);
int messageLength = messageLengthField.getInt(engine);
int digSize = 20;
int x = (digSize + messageLength) * 8 + 16 + 4 - keyBits;
int mR = messageLength;
if (x > 0) {
mR = messageLength - ((x + 7) / 8);
}
recMsgLen[recMsgLenOffset] = (short) mR;
return (short) sig.length;
} catch (org.bouncycastle.crypto.CryptoException ex) {
CryptoException.throwIt(CryptoException.ILLEGAL_USE);
} catch (DataLengthException ex) {
CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
} catch (Exception ex) {
CryptoException.throwIt(CryptoException.ILLEGAL_USE);
} finally {
engine.reset();
}
return -1;
}
public boolean verify(byte[] inBuff, short inOffset, short inLength) throws CryptoException {
if(!isRecovery){
CryptoException.throwIt(CryptoException.ILLEGAL_USE);
}
if(preSig == null){
CryptoException.throwIt(CryptoException.ILLEGAL_USE);
}
if (!isInitialized) {
CryptoException.throwIt(CryptoException.INVALID_INIT);
}
engine.update(inBuff, inOffset, inLength);
boolean b = engine.verifySignature(preSig);
engine.reset();
return b;
}
public void setInitialDigest(byte[] bytes, short s, short s1, byte[] bytes1, short s2, short s3) throws CryptoException {
throw new UnsupportedOperationException("Not supported yet.");
}
public short signPreComputedHash(byte[] bytes, short s, short s1, byte[] bytes1, short s2) throws CryptoException {
throw new UnsupportedOperationException("Not supported yet.");
}
public boolean verifyPreComputedHash(byte[] bytes, short s, short s1, byte[] bytes1, short s2, short s3) throws CryptoException {
throw new UnsupportedOperationException("Not supported yet.");
}
public byte getPaddingAlgorithm() {
throw new UnsupportedOperationException("Not supported yet.");
}
public byte getCipherAlgorithm() {
throw new UnsupportedOperationException("Not supported yet.");
}
public byte getMessageDigestAlgorithm() {
throw new UnsupportedOperationException("Not supported yet.");
}
}