/*
* 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 javacard.framework.JCSystem;
import javacard.framework.Util;
import javacard.security.CryptoException;
import javacard.security.Key;
import javacardx.crypto.Cipher;
import org.bouncycastle.crypto.BufferedBlockCipher;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.paddings.ISO7816d4Padding;
import org.bouncycastle.crypto.paddings.PKCS7Padding;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.paddings.ZeroBytePadding;
import org.bouncycastle.crypto.params.ParametersWithIV;
/**
* Implementation <code>Cipher</code> with symmetric keys based
* on BouncyCastle CryptoAPI.
* @see Cipher
*/
public class SymmetricCipherImpl extends Cipher {
byte algorithm;
BufferedBlockCipher engine;
boolean isInitialized;
public SymmetricCipherImpl(byte algorithm) {
this.algorithm = algorithm;
}
public void init(Key theKey, byte theMode) throws CryptoException {
selectCipherEngine(theKey);
engine.init(theMode == MODE_ENCRYPT, ((SymmetricKeyImpl) theKey).getParameters());
isInitialized = true;
}
public void init(Key theKey, byte theMode, byte[] bArray, short bOff, short bLen) throws CryptoException {
switch (algorithm) {
case ALG_DES_ECB_NOPAD:
case ALG_DES_ECB_ISO9797_M1:
case ALG_DES_ECB_ISO9797_M2:
case ALG_DES_ECB_PKCS5:
CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
break;
case ALG_DES_CBC_NOPAD:
case ALG_DES_CBC_ISO9797_M1:
case ALG_DES_CBC_ISO9797_M2:
case ALG_DES_CBC_PKCS5:
if (bLen != (short) 8) {
CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
}
break;
}
selectCipherEngine(theKey);
byte[] iv = JCSystem.makeTransientByteArray(bLen, JCSystem.CLEAR_ON_RESET);
Util.arrayCopyNonAtomic(bArray, bOff, iv, (short) 0, bLen);
engine.init(theMode == MODE_ENCRYPT, new ParametersWithIV(((SymmetricKeyImpl) theKey).getParameters(), iv));
isInitialized = true;
}
public byte getAlgorithm() {
return algorithm;
}
public short doFinal(byte[] inBuff, short inOffset, short inLength, byte[] outBuff, short outOffset) throws CryptoException {
if (!isInitialized) {
CryptoException.throwIt(CryptoException.INVALID_INIT);
}
short processedBytes = (short) engine.processBytes(inBuff, inOffset, inLength, outBuff, outOffset);
try {
return (short) (engine.doFinal(outBuff, outOffset + processedBytes) + processedBytes);
} catch (Exception ex) {
CryptoException.throwIt(CryptoException.ILLEGAL_USE);
}
return -1;
}
public short update(byte[] inBuff, short inOffset, short inLength, byte[] outBuff, short outOffset) throws CryptoException {
if (!isInitialized) {
CryptoException.throwIt(CryptoException.INVALID_INIT);
}
return (short) engine.processBytes(inBuff, inOffset, inLength, outBuff, outOffset);
}
private void selectCipherEngine(Key theKey) {
if (theKey == null) {
CryptoException.throwIt(CryptoException.UNINITIALIZED_KEY);
}
if (!theKey.isInitialized()) {
CryptoException.throwIt(CryptoException.UNINITIALIZED_KEY);
}
if (!(theKey instanceof SymmetricKeyImpl)) {
CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
}
SymmetricKeyImpl key = (SymmetricKeyImpl) theKey;
switch (algorithm) {
case ALG_DES_CBC_NOPAD:
case ALG_AES_BLOCK_128_CBC_NOPAD:
engine = new BufferedBlockCipher(new CBCBlockCipher(key.getCipher()));
break;
case ALG_DES_CBC_ISO9797_M1:
engine = new PaddedBufferedBlockCipher(new CBCBlockCipher(key.getCipher()), new ZeroBytePadding());
break;
case ALG_DES_CBC_ISO9797_M2:
engine = new PaddedBufferedBlockCipher(new CBCBlockCipher(key.getCipher()), new ISO7816d4Padding());
break;
case ALG_DES_CBC_PKCS5:
engine = new PaddedBufferedBlockCipher(new CBCBlockCipher(key.getCipher()), new PKCS7Padding());
break;
case ALG_DES_ECB_NOPAD:
case ALG_AES_BLOCK_128_ECB_NOPAD:
engine = new BufferedBlockCipher(key.getCipher());
break;
case ALG_DES_ECB_ISO9797_M1:
engine = new PaddedBufferedBlockCipher(key.getCipher(), new ZeroBytePadding());
break;
case ALG_DES_ECB_ISO9797_M2:
engine = new PaddedBufferedBlockCipher(key.getCipher(), new ISO7816d4Padding());
break;
case ALG_DES_ECB_PKCS5:
engine = new PaddedBufferedBlockCipher(key.getCipher(), new PKCS7Padding());
break;
case ALG_AES_CBC_ISO9797_M2:
engine = new PaddedBufferedBlockCipher(new CBCBlockCipher(key.getCipher()), new ISO7816d4Padding());
break;
default:
CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM);
break;
}
}
public byte getPaddingAlgorithm() {
throw new UnsupportedOperationException("Not supported yet.");
}
public byte getCipherAlgorithm() {
throw new UnsupportedOperationException("Not supported yet.");
}
}