diff options
author | David Hook <dgh@cryptoworkshop.com> | 2013-05-31 11:07:45 +0400 |
---|---|---|
committer | David Hook <dgh@cryptoworkshop.com> | 2013-05-31 11:07:45 +0400 |
commit | 2b976f5364cfdbc37d3086019d93483c983eb80b (patch) | |
tree | cb846af3fd1d43f9c2562a1fb2d06b997ad8f229 /core/src/main/java/org/bouncycastle/crypto/macs/ISO9797Alg3Mac.java | |
parent | 5f714bd92fbd780d22406f4bc3681be005f6f04a (diff) |
initial reshuffle
Diffstat (limited to 'core/src/main/java/org/bouncycastle/crypto/macs/ISO9797Alg3Mac.java')
-rw-r--r-- | core/src/main/java/org/bouncycastle/crypto/macs/ISO9797Alg3Mac.java | 305 |
1 files changed, 305 insertions, 0 deletions
diff --git a/core/src/main/java/org/bouncycastle/crypto/macs/ISO9797Alg3Mac.java b/core/src/main/java/org/bouncycastle/crypto/macs/ISO9797Alg3Mac.java new file mode 100644 index 00000000..330b39e7 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/macs/ISO9797Alg3Mac.java @@ -0,0 +1,305 @@ +package org.bouncycastle.crypto.macs; + +import org.bouncycastle.crypto.BlockCipher; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.Mac; +import org.bouncycastle.crypto.engines.DESEngine; +import org.bouncycastle.crypto.modes.CBCBlockCipher; +import org.bouncycastle.crypto.paddings.BlockCipherPadding; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; + +/** + * DES based CBC Block Cipher MAC according to ISO9797, algorithm 3 (ANSI X9.19 Retail MAC) + * + * This could as well be derived from CBCBlockCipherMac, but then the property mac in the base + * class must be changed to protected + */ + +public class ISO9797Alg3Mac + implements Mac +{ + private byte[] mac; + + private byte[] buf; + private int bufOff; + private BlockCipher cipher; + private BlockCipherPadding padding; + + private int macSize; + private KeyParameter lastKey2; + private KeyParameter lastKey3; + + /** + * create a Retail-MAC based on a CBC block cipher. This will produce an + * authentication code of the length of the block size of the cipher. + * + * @param cipher the cipher to be used as the basis of the MAC generation. This must + * be DESEngine. + */ + public ISO9797Alg3Mac( + BlockCipher cipher) + { + this(cipher, cipher.getBlockSize() * 8, null); + } + + /** + * create a Retail-MAC based on a CBC block cipher. This will produce an + * authentication code of the length of the block size of the cipher. + * + * @param cipher the cipher to be used as the basis of the MAC generation. + * @param padding the padding to be used to complete the last block. + */ + public ISO9797Alg3Mac( + BlockCipher cipher, + BlockCipherPadding padding) + { + this(cipher, cipher.getBlockSize() * 8, padding); + } + + /** + * create a Retail-MAC based on a block cipher with the size of the + * MAC been given in bits. This class uses single DES CBC mode as the basis for the + * MAC generation. + * <p> + * Note: the size of the MAC must be at least 24 bits (FIPS Publication 81), + * or 16 bits if being used as a data authenticator (FIPS Publication 113), + * and in general should be less than the size of the block cipher as it reduces + * the chance of an exhaustive attack (see Handbook of Applied Cryptography). + * + * @param cipher the cipher to be used as the basis of the MAC generation. + * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8. + */ + public ISO9797Alg3Mac( + BlockCipher cipher, + int macSizeInBits) + { + this(cipher, macSizeInBits, null); + } + + /** + * create a standard MAC based on a block cipher with the size of the + * MAC been given in bits. This class uses single DES CBC mode as the basis for the + * MAC generation. The final block is decrypted and then encrypted using the + * middle and right part of the key. + * <p> + * Note: the size of the MAC must be at least 24 bits (FIPS Publication 81), + * or 16 bits if being used as a data authenticator (FIPS Publication 113), + * and in general should be less than the size of the block cipher as it reduces + * the chance of an exhaustive attack (see Handbook of Applied Cryptography). + * + * @param cipher the cipher to be used as the basis of the MAC generation. + * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8. + * @param padding the padding to be used to complete the last block. + */ + public ISO9797Alg3Mac( + BlockCipher cipher, + int macSizeInBits, + BlockCipherPadding padding) + { + if ((macSizeInBits % 8) != 0) + { + throw new IllegalArgumentException("MAC size must be multiple of 8"); + } + + if (!(cipher instanceof DESEngine)) + { + throw new IllegalArgumentException("cipher must be instance of DESEngine"); + } + + this.cipher = new CBCBlockCipher(cipher); + this.padding = padding; + this.macSize = macSizeInBits / 8; + + mac = new byte[cipher.getBlockSize()]; + + buf = new byte[cipher.getBlockSize()]; + bufOff = 0; + } + + public String getAlgorithmName() + { + return "ISO9797Alg3"; + } + + public void init(CipherParameters params) + { + reset(); + + if (!(params instanceof KeyParameter || params instanceof ParametersWithIV)) + { + throw new IllegalArgumentException( + "params must be an instance of KeyParameter or ParametersWithIV"); + } + + // KeyParameter must contain a double or triple length DES key, + // however the underlying cipher is a single DES. The middle and + // right key are used only in the final step. + + KeyParameter kp; + + if (params instanceof KeyParameter) + { + kp = (KeyParameter)params; + } + else + { + kp = (KeyParameter)((ParametersWithIV)params).getParameters(); + } + + KeyParameter key1; + byte[] keyvalue = kp.getKey(); + + if (keyvalue.length == 16) + { // Double length DES key + key1 = new KeyParameter(keyvalue, 0, 8); + this.lastKey2 = new KeyParameter(keyvalue, 8, 8); + this.lastKey3 = key1; + } + else if (keyvalue.length == 24) + { // Triple length DES key + key1 = new KeyParameter(keyvalue, 0, 8); + this.lastKey2 = new KeyParameter(keyvalue, 8, 8); + this.lastKey3 = new KeyParameter(keyvalue, 16, 8); + } + else + { + throw new IllegalArgumentException( + "Key must be either 112 or 168 bit long"); + } + + if (params instanceof ParametersWithIV) + { + cipher.init(true, new ParametersWithIV(key1, ((ParametersWithIV)params).getIV())); + } + else + { + cipher.init(true, key1); + } + } + + public int getMacSize() + { + return macSize; + } + + public void update( + byte in) + { + if (bufOff == buf.length) + { + cipher.processBlock(buf, 0, mac, 0); + bufOff = 0; + } + + buf[bufOff++] = in; + } + + + public void update( + byte[] in, + int inOff, + int len) + { + if (len < 0) + { + throw new IllegalArgumentException("Can't have a negative input length!"); + } + + int blockSize = cipher.getBlockSize(); + int resultLen = 0; + int gapLen = blockSize - bufOff; + + if (len > gapLen) + { + System.arraycopy(in, inOff, buf, bufOff, gapLen); + + resultLen += cipher.processBlock(buf, 0, mac, 0); + + bufOff = 0; + len -= gapLen; + inOff += gapLen; + + while (len > blockSize) + { + resultLen += cipher.processBlock(in, inOff, mac, 0); + + len -= blockSize; + inOff += blockSize; + } + } + + System.arraycopy(in, inOff, buf, bufOff, len); + + bufOff += len; + } + + public int doFinal( + byte[] out, + int outOff) + { + int blockSize = cipher.getBlockSize(); + + if (padding == null) + { + // + // pad with zeroes + // + while (bufOff < blockSize) + { + buf[bufOff] = 0; + bufOff++; + } + } + else + { + if (bufOff == blockSize) + { + cipher.processBlock(buf, 0, mac, 0); + bufOff = 0; + } + + padding.addPadding(buf, bufOff); + } + + cipher.processBlock(buf, 0, mac, 0); + + // Added to code from base class + DESEngine deseng = new DESEngine(); + + deseng.init(false, this.lastKey2); + deseng.processBlock(mac, 0, mac, 0); + + deseng.init(true, this.lastKey3); + deseng.processBlock(mac, 0, mac, 0); + // **** + + System.arraycopy(mac, 0, out, outOff, macSize); + + reset(); + + return macSize; + } + + + /** + * Reset the mac generator. + */ + public void reset() + { + /* + * clean the buffer. + */ + for (int i = 0; i < buf.length; i++) + { + buf[i] = 0; + } + + bufOff = 0; + + /* + * reset the underlying cipher. + */ + cipher.reset(); + } +} |