/* See LICENSE for licensing and NOTICE for copyright. */ package org.cryptacular.spec; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.bouncycastle.crypto.BlockCipher; import org.bouncycastle.crypto.BufferedBlockCipher; import org.bouncycastle.crypto.modes.CBCBlockCipher; import org.bouncycastle.crypto.modes.CFBBlockCipher; import org.bouncycastle.crypto.modes.OFBBlockCipher; import org.bouncycastle.crypto.paddings.BlockCipherPadding; import org.bouncycastle.crypto.paddings.ISO10126d2Padding; import org.bouncycastle.crypto.paddings.ISO7816d4Padding; import org.bouncycastle.crypto.paddings.PKCS7Padding; import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher; import org.bouncycastle.crypto.paddings.TBCPadding; import org.bouncycastle.crypto.paddings.X923Padding; import org.bouncycastle.crypto.paddings.ZeroBytePadding; /** * Describes a block cipher in terms of a (algorithm, mode, padding) tuple and provides a facility to create a new * instance of the cipher via the {@link #newInstance()} method. * * @author Middleware Services * @version $Revision: 2744 $ */ public class BufferedBlockCipherSpec implements Spec<BufferedBlockCipher> { /** String specification format, <code>algorithm/mode/padding</code>. */ public static final Pattern FORMAT = Pattern.compile("(?<alg>[A-Za-z0-9_-]+)/(?<mode>\\w+)/(?<padding>\\w+)"); /** Cipher algorithm algorithm. */ private final String algorithm; /** Cipher mode, e.g. CBC, OFB. */ private final String mode; /** Cipher padding scheme, e.g. PKCS5Padding. */ private final String padding; /** * Creates a new instance from an algorithm name. * * @param algName Cipher algorithm name. */ public BufferedBlockCipherSpec(final String algName) { this(algName, null, null); } /** * Creates a new instance from a cipher algorithm and mode. * * @param algName Cipher algorithm name. * @param cipherMode Cipher mode. */ public BufferedBlockCipherSpec(final String algName, final String cipherMode) { this(algName, cipherMode, null); } /** * Creates a new instance from the given cipher specifications. * * @param algName Cipher algorithm name. * @param cipherMode Cipher mode. * @param cipherPadding Cipher padding scheme algorithm. */ public BufferedBlockCipherSpec(final String algName, final String cipherMode, final String cipherPadding) { this.algorithm = algName; this.mode = cipherMode; this.padding = cipherPadding; } @Override public String getAlgorithm() { return algorithm; } /** * Gets the cipher mode. * * @return Cipher mode, e.g. CBC, OFB. */ public String getMode() { return mode; } /** * Gets the cipher padding scheme. * * @return Padding scheme algorithm, e.g. PKCS5Padding. The following names are equivalent for no padding: NULL, * Zero, None. */ public String getPadding() { return padding; } /** * Gets the simple block cipher specification corresponding to this instance. * * @return Simple block cipher specification. */ public BlockCipherSpec getBlockCipherSpec() { return new BlockCipherSpec(this.algorithm); } /** * Creates a new buffered block cipher from the specification in this instance. * * @return New buffered block cipher instance. */ @Override public BufferedBlockCipher newInstance() { BlockCipher cipher = getBlockCipherSpec().newInstance(); switch (mode) { case "CBC": cipher = new CBCBlockCipher(cipher); break; case "OFB": cipher = new OFBBlockCipher(cipher, cipher.getBlockSize()); break; case "CFB": cipher = new CFBBlockCipher(cipher, cipher.getBlockSize()); break; default: break; } if (padding != null) { return new PaddedBufferedBlockCipher(cipher, getPadding(padding)); } return new BufferedBlockCipher(cipher); } @Override public String toString() { return algorithm + '/' + mode + '/' + padding; } /** * Parses a string representation of a buffered block cipher specification into an instance of this class. * * @param specification Block cipher specification of the form <code>algorithm/mode/padding</code>. * * @return Buffered block cipher specification instance. */ public static BufferedBlockCipherSpec parse(final String specification) { final Matcher m = FORMAT.matcher(specification); if (!m.matches()) { throw new IllegalArgumentException("Invalid specification " + specification); } return new BufferedBlockCipherSpec(m.group("alg"), m.group("mode"), m.group("padding")); } /** * Gets a instance of block cipher padding from a padding name string. * * @param padding Name of padding algorithm. * * @return Block cipher padding instance. */ private static BlockCipherPadding getPadding(final String padding) { final String name; final int pIndex = padding.indexOf("Padding"); if (pIndex > -1) { name = padding.substring(0, pIndex); } else { name = padding; } BlockCipherPadding blockCipherPadding; if ("ISO7816d4".equalsIgnoreCase(name) | "ISO7816".equalsIgnoreCase(name)) { blockCipherPadding = new ISO7816d4Padding(); } else if ("ISO10126".equalsIgnoreCase(name) || "ISO10126-2".equalsIgnoreCase(name)) { blockCipherPadding = new ISO10126d2Padding(); } else if ("PKCS7".equalsIgnoreCase(name) || "PKCS5".equalsIgnoreCase(name)) { blockCipherPadding = new PKCS7Padding(); } else if ("TBC".equalsIgnoreCase(name)) { blockCipherPadding = new TBCPadding(); } else if ("X923".equalsIgnoreCase(name)) { blockCipherPadding = new X923Padding(); } else if ("NULL".equalsIgnoreCase(name) || "Zero".equalsIgnoreCase(name) || "None".equalsIgnoreCase(name)) { blockCipherPadding = new ZeroBytePadding(); } else { throw new IllegalArgumentException("Invalid padding " + padding); } return blockCipherPadding; } }