diff options
Diffstat (limited to 'prov/src/main/java/org/spongycastle/jce/provider/BrokenJCEBlockCipher.java')
-rw-r--r-- | prov/src/main/java/org/spongycastle/jce/provider/BrokenJCEBlockCipher.java | 621 |
1 files changed, 621 insertions, 0 deletions
diff --git a/prov/src/main/java/org/spongycastle/jce/provider/BrokenJCEBlockCipher.java b/prov/src/main/java/org/spongycastle/jce/provider/BrokenJCEBlockCipher.java new file mode 100644 index 00000000..95304581 --- /dev/null +++ b/prov/src/main/java/org/spongycastle/jce/provider/BrokenJCEBlockCipher.java @@ -0,0 +1,621 @@ +package org.spongycastle.jce.provider; + +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEParameterSpec; +import javax.crypto.spec.RC2ParameterSpec; +import javax.crypto.spec.RC5ParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import org.spongycastle.crypto.BlockCipher; +import org.spongycastle.crypto.BufferedBlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.DataLengthException; +import org.spongycastle.crypto.InvalidCipherTextException; +import org.spongycastle.crypto.engines.DESEngine; +import org.spongycastle.crypto.engines.DESedeEngine; +import org.spongycastle.crypto.engines.TwofishEngine; +import org.spongycastle.crypto.modes.CBCBlockCipher; +import org.spongycastle.crypto.modes.CFBBlockCipher; +import org.spongycastle.crypto.modes.CTSBlockCipher; +import org.spongycastle.crypto.modes.OFBBlockCipher; +import org.spongycastle.crypto.paddings.PaddedBufferedBlockCipher; +import org.spongycastle.crypto.params.KeyParameter; +import org.spongycastle.crypto.params.ParametersWithIV; +import org.spongycastle.crypto.params.RC2Parameters; +import org.spongycastle.crypto.params.RC5Parameters; +import org.spongycastle.jcajce.provider.symmetric.util.BCPBEKey; +import org.spongycastle.util.Strings; + +public class BrokenJCEBlockCipher + implements BrokenPBE +{ + // + // specs we can handle. + // + private Class[] availableSpecs = + { + IvParameterSpec.class, + PBEParameterSpec.class, + RC2ParameterSpec.class, + RC5ParameterSpec.class + }; + + private BufferedBlockCipher cipher; + private ParametersWithIV ivParam; + + private int pbeType = PKCS12; + private int pbeHash = SHA1; + private int pbeKeySize; + private int pbeIvSize; + + private int ivLength = 0; + + private AlgorithmParameters engineParams = null; + + protected BrokenJCEBlockCipher( + BlockCipher engine) + { + cipher = new PaddedBufferedBlockCipher(engine); + } + + protected BrokenJCEBlockCipher( + BlockCipher engine, + int pbeType, + int pbeHash, + int pbeKeySize, + int pbeIvSize) + { + cipher = new PaddedBufferedBlockCipher(engine); + + this.pbeType = pbeType; + this.pbeHash = pbeHash; + this.pbeKeySize = pbeKeySize; + this.pbeIvSize = pbeIvSize; + } + + protected int engineGetBlockSize() + { + return cipher.getBlockSize(); + } + + protected byte[] engineGetIV() + { + return (ivParam != null) ? ivParam.getIV() : null; + } + + protected int engineGetKeySize( + Key key) + { + return key.getEncoded().length; + } + + protected int engineGetOutputSize( + int inputLen) + { + return cipher.getOutputSize(inputLen); + } + + protected AlgorithmParameters engineGetParameters() + { + if (engineParams == null) + { + if (ivParam != null) + { + String name = cipher.getUnderlyingCipher().getAlgorithmName(); + + if (name.indexOf('/') >= 0) + { + name = name.substring(0, name.indexOf('/')); + } + + try + { + engineParams = AlgorithmParameters.getInstance(name, BouncyCastleProvider.PROVIDER_NAME); + engineParams.init(ivParam.getIV()); + } + catch (Exception e) + { + throw new RuntimeException(e.toString()); + } + } + } + + return engineParams; + } + + protected void engineSetMode( + String mode) + { + String modeName = Strings.toUpperCase(mode); + + if (modeName.equals("ECB")) + { + ivLength = 0; + cipher = new PaddedBufferedBlockCipher(cipher.getUnderlyingCipher()); + } + else if (modeName.equals("CBC")) + { + ivLength = cipher.getUnderlyingCipher().getBlockSize(); + cipher = new PaddedBufferedBlockCipher( + new CBCBlockCipher(cipher.getUnderlyingCipher())); + } + else if (modeName.startsWith("OFB")) + { + ivLength = cipher.getUnderlyingCipher().getBlockSize(); + if (modeName.length() != 3) + { + int wordSize = Integer.parseInt(modeName.substring(3)); + + cipher = new PaddedBufferedBlockCipher( + new OFBBlockCipher(cipher.getUnderlyingCipher(), wordSize)); + } + else + { + cipher = new PaddedBufferedBlockCipher( + new OFBBlockCipher(cipher.getUnderlyingCipher(), 8 * cipher.getBlockSize())); + } + } + else if (modeName.startsWith("CFB")) + { + ivLength = cipher.getUnderlyingCipher().getBlockSize(); + if (modeName.length() != 3) + { + int wordSize = Integer.parseInt(modeName.substring(3)); + + cipher = new PaddedBufferedBlockCipher( + new CFBBlockCipher(cipher.getUnderlyingCipher(), wordSize)); + } + else + { + cipher = new PaddedBufferedBlockCipher( + new CFBBlockCipher(cipher.getUnderlyingCipher(), 8 * cipher.getBlockSize())); + } + } + else + { + throw new IllegalArgumentException("can't support mode " + mode); + } + } + + protected void engineSetPadding( + String padding) + throws NoSuchPaddingException + { + String paddingName = Strings.toUpperCase(padding); + + if (paddingName.equals("NOPADDING")) + { + cipher = new BufferedBlockCipher(cipher.getUnderlyingCipher()); + } + else if (paddingName.equals("PKCS5PADDING") || paddingName.equals("PKCS7PADDING") || paddingName.equals("ISO10126PADDING")) + { + cipher = new PaddedBufferedBlockCipher(cipher.getUnderlyingCipher()); + } + else if (paddingName.equals("WITHCTS")) + { + cipher = new CTSBlockCipher(cipher.getUnderlyingCipher()); + } + else + { + throw new NoSuchPaddingException("Padding " + padding + " unknown."); + } + } + + protected void engineInit( + int opmode, + Key key, + AlgorithmParameterSpec params, + SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException + { + CipherParameters param; + + // + // a note on iv's - if ivLength is zero the IV gets ignored (we don't use it). + // + if (key instanceof BCPBEKey) + { + param = BrokenPBE.Util.makePBEParameters((BCPBEKey)key, params, pbeType, pbeHash, + cipher.getUnderlyingCipher().getAlgorithmName(), pbeKeySize, pbeIvSize); + + if (pbeIvSize != 0) + { + ivParam = (ParametersWithIV)param; + } + } + else if (params == null) + { + param = new KeyParameter(key.getEncoded()); + } + else if (params instanceof IvParameterSpec) + { + if (ivLength != 0) + { + param = new ParametersWithIV(new KeyParameter(key.getEncoded()), ((IvParameterSpec)params).getIV()); + ivParam = (ParametersWithIV)param; + } + else + { + param = new KeyParameter(key.getEncoded()); + } + } + else if (params instanceof RC2ParameterSpec) + { + RC2ParameterSpec rc2Param = (RC2ParameterSpec)params; + + param = new RC2Parameters(key.getEncoded(), ((RC2ParameterSpec)params).getEffectiveKeyBits()); + + if (rc2Param.getIV() != null && ivLength != 0) + { + param = new ParametersWithIV(param, rc2Param.getIV()); + ivParam = (ParametersWithIV)param; + } + } + else if (params instanceof RC5ParameterSpec) + { + RC5ParameterSpec rc5Param = (RC5ParameterSpec)params; + + param = new RC5Parameters(key.getEncoded(), ((RC5ParameterSpec)params).getRounds()); + if (rc5Param.getWordSize() != 32) + { + throw new IllegalArgumentException("can only accept RC5 word size 32 (at the moment...)"); + } + if ((rc5Param.getIV() != null) && (ivLength != 0)) + { + param = new ParametersWithIV(param, rc5Param.getIV()); + ivParam = (ParametersWithIV)param; + } + } + else + { + throw new InvalidAlgorithmParameterException("unknown parameter type."); + } + + if ((ivLength != 0) && !(param instanceof ParametersWithIV)) + { + if (random == null) + { + random = new SecureRandom(); + } + + if ((opmode == Cipher.ENCRYPT_MODE) || (opmode == Cipher.WRAP_MODE)) + { + byte[] iv = new byte[ivLength]; + + random.nextBytes(iv); + param = new ParametersWithIV(param, iv); + ivParam = (ParametersWithIV)param; + } + else + { + throw new InvalidAlgorithmParameterException("no IV set when one expected"); + } + } + + switch (opmode) + { + case Cipher.ENCRYPT_MODE: + case Cipher.WRAP_MODE: + cipher.init(true, param); + break; + case Cipher.DECRYPT_MODE: + case Cipher.UNWRAP_MODE: + cipher.init(false, param); + break; + default: + System.out.println("eeek!"); + } + } + + protected void engineInit( + int opmode, + Key key, + AlgorithmParameters params, + SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException + { + AlgorithmParameterSpec paramSpec = null; + + if (params != null) + { + for (int i = 0; i != availableSpecs.length; i++) + { + try + { + paramSpec = params.getParameterSpec(availableSpecs[i]); + break; + } + catch (Exception e) + { + continue; + } + } + + if (paramSpec == null) + { + throw new InvalidAlgorithmParameterException("can't handle parameter " + params.toString()); + } + } + + engineParams = params; + engineInit(opmode, key, paramSpec, random); + } + + protected void engineInit( + int opmode, + Key key, + SecureRandom random) + throws InvalidKeyException + { + try + { + engineInit(opmode, key, (AlgorithmParameterSpec)null, random); + } + catch (InvalidAlgorithmParameterException e) + { + throw new IllegalArgumentException(e.getMessage()); + } + } + + protected byte[] engineUpdate( + byte[] input, + int inputOffset, + int inputLen) + { + int length = cipher.getUpdateOutputSize(inputLen); + + if (length > 0) + { + byte[] out = new byte[length]; + + cipher.processBytes(input, inputOffset, inputLen, out, 0); + return out; + } + + cipher.processBytes(input, inputOffset, inputLen, null, 0); + + return null; + } + + protected int engineUpdate( + byte[] input, + int inputOffset, + int inputLen, + byte[] output, + int outputOffset) + { + return cipher.processBytes(input, inputOffset, inputLen, output, outputOffset); + } + + protected byte[] engineDoFinal( + byte[] input, + int inputOffset, + int inputLen) + throws IllegalBlockSizeException, BadPaddingException + { + int len = 0; + byte[] tmp = new byte[engineGetOutputSize(inputLen)]; + + if (inputLen != 0) + { + len = cipher.processBytes(input, inputOffset, inputLen, tmp, 0); + } + + try + { + len += cipher.doFinal(tmp, len); + } + catch (DataLengthException e) + { + throw new IllegalBlockSizeException(e.getMessage()); + } + catch (InvalidCipherTextException e) + { + throw new BadPaddingException(e.getMessage()); + } + + byte[] out = new byte[len]; + + System.arraycopy(tmp, 0, out, 0, len); + + return out; + } + + protected int engineDoFinal( + byte[] input, + int inputOffset, + int inputLen, + byte[] output, + int outputOffset) + throws IllegalBlockSizeException, BadPaddingException + { + int len = 0; + + if (inputLen != 0) + { + len = cipher.processBytes(input, inputOffset, inputLen, output, outputOffset); + } + + try + { + return len + cipher.doFinal(output, outputOffset + len); + } + catch (DataLengthException e) + { + throw new IllegalBlockSizeException(e.getMessage()); + } + catch (InvalidCipherTextException e) + { + throw new BadPaddingException(e.getMessage()); + } + } + + protected byte[] engineWrap( + Key key) + throws IllegalBlockSizeException, java.security.InvalidKeyException + { + byte[] encoded = key.getEncoded(); + if (encoded == null) + { + throw new InvalidKeyException("Cannot wrap key, null encoding."); + } + + try + { + return engineDoFinal(encoded, 0, encoded.length); + } + catch (BadPaddingException e) + { + throw new IllegalBlockSizeException(e.getMessage()); + } + } + + protected Key engineUnwrap( + byte[] wrappedKey, + String wrappedKeyAlgorithm, + int wrappedKeyType) + throws InvalidKeyException + { + byte[] encoded = null; + try + { + encoded = engineDoFinal(wrappedKey, 0, wrappedKey.length); + } + catch (BadPaddingException e) + { + throw new InvalidKeyException(e.getMessage()); + } + catch (IllegalBlockSizeException e2) + { + throw new InvalidKeyException(e2.getMessage()); + } + + if (wrappedKeyType == Cipher.SECRET_KEY) + { + return new SecretKeySpec(encoded, wrappedKeyAlgorithm); + } + else + { + try + { + KeyFactory kf = KeyFactory.getInstance(wrappedKeyAlgorithm, BouncyCastleProvider.PROVIDER_NAME); + + if (wrappedKeyType == Cipher.PUBLIC_KEY) + { + return kf.generatePublic(new X509EncodedKeySpec(encoded)); + } + else if (wrappedKeyType == Cipher.PRIVATE_KEY) + { + return kf.generatePrivate(new PKCS8EncodedKeySpec(encoded)); + } + } + catch (NoSuchProviderException e) + { + throw new InvalidKeyException("Unknown key type " + e.getMessage()); + } + catch (NoSuchAlgorithmException e) + { + throw new InvalidKeyException("Unknown key type " + e.getMessage()); + } + catch (InvalidKeySpecException e2) + { + throw new InvalidKeyException("Unknown key type " + e2.getMessage()); + } + + throw new InvalidKeyException("Unknown key type " + wrappedKeyType); + } + } + + /* + * The ciphers that inherit from us. + */ + + /** + * PBEWithMD5AndDES + */ + static public class BrokePBEWithMD5AndDES + extends BrokenJCEBlockCipher + { + public BrokePBEWithMD5AndDES() + { + super(new CBCBlockCipher(new DESEngine()), PKCS5S1, MD5, 64, 64); + } + } + + /** + * PBEWithSHA1AndDES + */ + static public class BrokePBEWithSHA1AndDES + extends BrokenJCEBlockCipher + { + public BrokePBEWithSHA1AndDES() + { + super(new CBCBlockCipher(new DESEngine()), PKCS5S1, SHA1, 64, 64); + } + } + + /** + * PBEWithSHAAnd3-KeyTripleDES-CBC + */ + static public class BrokePBEWithSHAAndDES3Key + extends BrokenJCEBlockCipher + { + public BrokePBEWithSHAAndDES3Key() + { + super(new CBCBlockCipher(new DESedeEngine()), PKCS12, SHA1, 192, 64); + } + } + + /** + * OldPBEWithSHAAnd3-KeyTripleDES-CBC + */ + static public class OldPBEWithSHAAndDES3Key + extends BrokenJCEBlockCipher + { + public OldPBEWithSHAAndDES3Key() + { + super(new CBCBlockCipher(new DESedeEngine()), OLD_PKCS12, SHA1, 192, 64); + } + } + + /** + * PBEWithSHAAnd2-KeyTripleDES-CBC + */ + static public class BrokePBEWithSHAAndDES2Key + extends BrokenJCEBlockCipher + { + public BrokePBEWithSHAAndDES2Key() + { + super(new CBCBlockCipher(new DESedeEngine()), PKCS12, SHA1, 128, 64); + } + } + + /** + * OldPBEWithSHAAndTwofish-CBC + */ + static public class OldPBEWithSHAAndTwofish + extends BrokenJCEBlockCipher + { + public OldPBEWithSHAAndTwofish() + { + super(new CBCBlockCipher(new TwofishEngine()), OLD_PKCS12, SHA1, 256, 128); + } + } +} |