/* * 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.security.CryptoException; import javacard.security.InitializedMessageDigest; import javacard.security.MessageDigest; import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.digests.MD5Digest; import org.bouncycastle.crypto.digests.RIPEMD160Digest; import org.bouncycastle.crypto.digests.SHA1Digest; import org.bouncycastle.crypto.digests.SHA256Digest; import org.bouncycastle.crypto.digests.SHA384Digest; import org.bouncycastle.crypto.digests.SHA512Digest; import org.bouncycastle.crypto.util.Pack; /** * Implementation * <code>MessageDigest</code> based * on BouncyCastle CryptoAPI. * @see MessageDigest * @see MD5Digest * @see RIPEMD160Digest * @see SHA1Digest */ public class MessageDigestImpl extends InitializedMessageDigest { private Digest engine; private byte algorithm; private short blockSize; // internals BouncyCastle private String byteCountFieldName = "byteCount"; private Class digestClass; private byte componentSize; private byte componentCount; private byte componentStartIdx; public MessageDigestImpl(byte algorithm) { this.algorithm = algorithm; blockSize = 64; componentStartIdx = 1; switch (algorithm) { case ALG_SHA: engine = new SHA1Digest(); digestClass = engine.getClass(); break; case ALG_MD5: engine = new MD5Digest(); digestClass = engine.getClass(); break; case ALG_RIPEMD160: engine = new RIPEMD160Digest(); digestClass = engine.getClass(); componentStartIdx = 0; break; case ALG_SHA_256: engine = new SHA256Digest(); digestClass = engine.getClass(); break; case ALG_SHA_384: engine = new SHA384Digest(); blockSize = 128; byteCountFieldName = "byteCount1"; digestClass = engine.getClass().getSuperclass(); break; case ALG_SHA_512: engine = new SHA512Digest(); blockSize = 128; byteCountFieldName = "byteCount1"; digestClass = engine.getClass().getSuperclass(); break; default: CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); break; } componentSize = (byte)(blockSize == 64 ? 4 : 8); componentCount = (byte) (engine.getDigestSize() / componentSize); } public byte getAlgorithm() { return algorithm; } public byte getLength() { return (byte) engine.getDigestSize(); } public short doFinal(byte inBuff[], short inOffset, short inLength, byte outBuff[], short outOffset) { engine.update(inBuff, inOffset, inLength); return (short) engine.doFinal(outBuff, outOffset); } public void update(byte inBuff[], short inOffset, short inLength) { engine.update(inBuff, inOffset, inLength); } public void reset() { engine.reset(); } public void setInitialDigest(byte[] initialDigestBuf, short initialDigestOffset, short initialDigestLength, byte[] digestedMsgLenBuf, short digestedMsgLenOffset, short digestedMsgLenLength) throws CryptoException { // initialDigestLength must be == DIGEST_SIZE if (engine.getDigestSize() != initialDigestLength) { CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); } // digestedMsgLenLength must be > 0 and < long value, more formal 2^64-1 bits // TODO support length more 2^128-1 bits (SHA-384, SHA-512) if (digestedMsgLenLength == 0 || digestedMsgLenLength > 8) { CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); } long byteCount = 0; for (short i = 0; i < digestedMsgLenLength; i++) { byteCount = (byteCount << 8) + (digestedMsgLenBuf[digestedMsgLenOffset + i] & 0xff); } // byte count % block size must be == 0 if (byteCount % blockSize != 0) { CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); } // set hash state - BouncyCastle specific try { for (byte i = 0; i < componentCount; i++) { // some reflection work Field h = digestClass.getDeclaredField("H" + (i + componentStartIdx)); h.setAccessible(true); if (componentSize == 4) { h.setInt(engine, Pack.bigEndianToInt(initialDigestBuf, initialDigestOffset + i * componentSize)); } else { h.setLong(engine, Pack.bigEndianToLong(initialDigestBuf, initialDigestOffset + i * componentSize)); } } // set byteCount Field h = digestClass.getSuperclass().getDeclaredField(byteCountFieldName); h.setAccessible(true); h.setLong(engine, byteCount); } catch (Exception e) { CryptoException.throwIt(CryptoException.ILLEGAL_USE); } } void getIntermediateDigest(byte[] intermediateDigest, int off) { // get hash state - BouncyCastle specific try { for (byte i = 0; i < componentCount; i++) { // some reflection work Field h = digestClass.getDeclaredField("H" + (i + componentStartIdx)); h.setAccessible(true); if (componentSize == 4) { Pack.intToBigEndian(h.getInt(engine), intermediateDigest, off + i * componentSize); } else { Pack.longToBigEndian(h.getLong(engine), intermediateDigest, off + i * componentSize); } } } catch (Exception e) { CryptoException.throwIt(CryptoException.ILLEGAL_USE); } } short getBlockSize(){ return blockSize; } }