Home | History | Annotate | Download | only in macs
      1 package org.bouncycastle.crypto.macs;
      2 
      3 import org.bouncycastle.crypto.BlockCipher;
      4 import org.bouncycastle.crypto.CipherParameters;
      5 import org.bouncycastle.crypto.Mac;
      6 import org.bouncycastle.crypto.modes.CBCBlockCipher;
      7 import org.bouncycastle.crypto.paddings.BlockCipherPadding;
      8 
      9 /**
     10  * standard CBC Block Cipher MAC - if no padding is specified the default of
     11  * pad of zeroes is used.
     12  */
     13 public class CBCBlockCipherMac
     14     implements Mac
     15 {
     16     private byte[]              mac;
     17 
     18     private byte[]              buf;
     19     private int                 bufOff;
     20     private BlockCipher         cipher;
     21     private BlockCipherPadding  padding;
     22 
     23     private int                 macSize;
     24 
     25     /**
     26      * create a standard MAC based on a CBC block cipher. This will produce an
     27      * authentication code half the length of the block size of the cipher.
     28      *
     29      * @param cipher the cipher to be used as the basis of the MAC generation.
     30      */
     31     public CBCBlockCipherMac(
     32         BlockCipher     cipher)
     33     {
     34         this(cipher, (cipher.getBlockSize() * 8) / 2, null);
     35     }
     36 
     37     /**
     38      * create a standard MAC based on a CBC block cipher. This will produce an
     39      * authentication code half the length of the block size of the cipher.
     40      *
     41      * @param cipher the cipher to be used as the basis of the MAC generation.
     42      * @param padding the padding to be used to complete the last block.
     43      */
     44     public CBCBlockCipherMac(
     45         BlockCipher         cipher,
     46         BlockCipherPadding  padding)
     47     {
     48         this(cipher, (cipher.getBlockSize() * 8) / 2, padding);
     49     }
     50 
     51     /**
     52      * create a standard MAC based on a block cipher with the size of the
     53      * MAC been given in bits. This class uses CBC mode as the basis for the
     54      * MAC generation.
     55      * <p>
     56      * Note: the size of the MAC must be at least 24 bits (FIPS Publication 81),
     57      * or 16 bits if being used as a data authenticator (FIPS Publication 113),
     58      * and in general should be less than the size of the block cipher as it reduces
     59      * the chance of an exhaustive attack (see Handbook of Applied Cryptography).
     60      *
     61      * @param cipher the cipher to be used as the basis of the MAC generation.
     62      * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8.
     63      */
     64     public CBCBlockCipherMac(
     65         BlockCipher     cipher,
     66         int             macSizeInBits)
     67     {
     68         this(cipher, macSizeInBits, null);
     69     }
     70 
     71     /**
     72      * create a standard MAC based on a block cipher with the size of the
     73      * MAC been given in bits. This class uses CBC mode as the basis for the
     74      * MAC generation.
     75      * <p>
     76      * Note: the size of the MAC must be at least 24 bits (FIPS Publication 81),
     77      * or 16 bits if being used as a data authenticator (FIPS Publication 113),
     78      * and in general should be less than the size of the block cipher as it reduces
     79      * the chance of an exhaustive attack (see Handbook of Applied Cryptography).
     80      *
     81      * @param cipher the cipher to be used as the basis of the MAC generation.
     82      * @param macSizeInBits the size of the MAC in bits, must be a multiple of 8.
     83      * @param padding the padding to be used to complete the last block.
     84      */
     85     public CBCBlockCipherMac(
     86         BlockCipher         cipher,
     87         int                 macSizeInBits,
     88         BlockCipherPadding  padding)
     89     {
     90         if ((macSizeInBits % 8) != 0)
     91         {
     92             throw new IllegalArgumentException("MAC size must be multiple of 8");
     93         }
     94 
     95         this.cipher = new CBCBlockCipher(cipher);
     96         this.padding = padding;
     97         this.macSize = macSizeInBits / 8;
     98 
     99         mac = new byte[cipher.getBlockSize()];
    100 
    101         buf = new byte[cipher.getBlockSize()];
    102         bufOff = 0;
    103     }
    104 
    105     public String getAlgorithmName()
    106     {
    107         return cipher.getAlgorithmName();
    108     }
    109 
    110     public void init(
    111         CipherParameters    params)
    112     {
    113         reset();
    114 
    115         cipher.init(true, params);
    116     }
    117 
    118     public int getMacSize()
    119     {
    120         return macSize;
    121     }
    122 
    123     public void update(
    124         byte        in)
    125     {
    126         if (bufOff == buf.length)
    127         {
    128             cipher.processBlock(buf, 0, mac, 0);
    129             bufOff = 0;
    130         }
    131 
    132         buf[bufOff++] = in;
    133     }
    134 
    135     public void update(
    136         byte[]      in,
    137         int         inOff,
    138         int         len)
    139     {
    140         if (len < 0)
    141         {
    142             throw new IllegalArgumentException("Can't have a negative input length!");
    143         }
    144 
    145         int blockSize = cipher.getBlockSize();
    146         int gapLen = blockSize - bufOff;
    147 
    148         if (len > gapLen)
    149         {
    150             System.arraycopy(in, inOff, buf, bufOff, gapLen);
    151 
    152             cipher.processBlock(buf, 0, mac, 0);
    153 
    154             bufOff = 0;
    155             len -= gapLen;
    156             inOff += gapLen;
    157 
    158             while (len > blockSize)
    159             {
    160                 cipher.processBlock(in, inOff, mac, 0);
    161 
    162                 len -= blockSize;
    163                 inOff += blockSize;
    164             }
    165         }
    166 
    167         System.arraycopy(in, inOff, buf, bufOff, len);
    168 
    169         bufOff += len;
    170     }
    171 
    172     public int doFinal(
    173         byte[]  out,
    174         int     outOff)
    175     {
    176         int blockSize = cipher.getBlockSize();
    177 
    178         if (padding == null)
    179         {
    180             //
    181             // pad with zeroes
    182             //
    183             while (bufOff < blockSize)
    184             {
    185                 buf[bufOff] = 0;
    186                 bufOff++;
    187             }
    188         }
    189         else
    190         {
    191             if (bufOff == blockSize)
    192             {
    193                 cipher.processBlock(buf, 0, mac, 0);
    194                 bufOff = 0;
    195             }
    196 
    197             padding.addPadding(buf, bufOff);
    198         }
    199 
    200         cipher.processBlock(buf, 0, mac, 0);
    201 
    202         System.arraycopy(mac, 0, out, outOff, macSize);
    203 
    204         reset();
    205 
    206         return macSize;
    207     }
    208 
    209     /**
    210      * Reset the mac generator.
    211      */
    212     public void reset()
    213     {
    214         /*
    215          * clean the buffer.
    216          */
    217         for (int i = 0; i < buf.length; i++)
    218         {
    219             buf[i] = 0;
    220         }
    221 
    222         bufOff = 0;
    223 
    224         /*
    225          * reset the underlying cipher.
    226          */
    227         cipher.reset();
    228     }
    229 }
    230