FileDocCategorySizeDatePackage
ISO9797Alg3Mac.javaAPI DocAndroid 1.5 API8495Wed May 06 22:41:06 BST 2009org.bouncycastle.crypto.macs

ISO9797Alg3Mac

public class ISO9797Alg3Mac extends Object implements org.bouncycastle.crypto.Mac
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

Fields Summary
private byte[]
mac
private byte[]
buf
private int
bufOff
private org.bouncycastle.crypto.BlockCipher
cipher
private org.bouncycastle.crypto.paddings.BlockCipherPadding
padding
private int
macSize
private org.bouncycastle.crypto.params.KeyParameter
lastKey2
private org.bouncycastle.crypto.params.KeyParameter
lastKey3
Constructors Summary
public ISO9797Alg3Mac(org.bouncycastle.crypto.BlockCipher cipher)
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.

        this(cipher, cipher.getBlockSize() * 8, null);
    
public ISO9797Alg3Mac(org.bouncycastle.crypto.BlockCipher cipher, org.bouncycastle.crypto.paddings.BlockCipherPadding padding)
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.

        this(cipher, cipher.getBlockSize() * 8, padding);
    
public ISO9797Alg3Mac(org.bouncycastle.crypto.BlockCipher cipher, int macSizeInBits)
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.

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.

        this(cipher, macSizeInBits, null);
    
public ISO9797Alg3Mac(org.bouncycastle.crypto.BlockCipher cipher, int macSizeInBits, org.bouncycastle.crypto.paddings.BlockCipherPadding padding)
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.

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.

        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;
    
Methods Summary
public intdoFinal(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;
    
public java.lang.StringgetAlgorithmName()

        return "ISO9797Alg3";
    
public intgetMacSize()

        return macSize;
    
public voidinit(org.bouncycastle.crypto.CipherParameters params)

        reset();

        if (!(params instanceof KeyParameter))
        {
            throw new IllegalArgumentException(
                    "params must be an instance of KeyParameter");
        }

        // 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 = (KeyParameter)params;
        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");
        }

        cipher.init(true, key1);
    
public voidreset()
Reset the mac generator.

        /*
         * clean the buffer.
         */
        for (int i = 0; i < buf.length; i++)
        {
            buf[i] = 0;
        }
        
        bufOff = 0;
        
        /*
         * reset the underlying cipher.
         */
        cipher.reset();
    
public voidupdate(byte in)

        int         resultLen = 0;
        
        if (bufOff == buf.length)
        {
            resultLen = cipher.processBlock(buf, 0, mac, 0);
            bufOff = 0;
        }
        
        buf[bufOff++] = in;
    
public voidupdate(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;