Home | History | Annotate | Download | only in macs
      1 package org.bouncycastle.crypto.macs;
      2 
      3 import java.util.Hashtable;
      4 
      5 import org.bouncycastle.crypto.CipherParameters;
      6 import org.bouncycastle.crypto.Digest;
      7 import org.bouncycastle.crypto.ExtendedDigest;
      8 import org.bouncycastle.crypto.Mac;
      9 import org.bouncycastle.crypto.params.KeyParameter;
     10 
     11 /**
     12  * HMAC implementation based on RFC2104
     13  *
     14  * H(K XOR opad, H(K XOR ipad, text))
     15  */
     16 public class HMac
     17     implements Mac
     18 {
     19     private final static byte IPAD = (byte)0x36;
     20     private final static byte OPAD = (byte)0x5C;
     21 
     22     private Digest digest;
     23     private int digestSize;
     24     private int blockLength;
     25 
     26     private byte[] inputPad;
     27     private byte[] outputPad;
     28 
     29     private static Hashtable blockLengths;
     30 
     31     static
     32     {
     33         blockLengths = new Hashtable();
     34 
     35         blockLengths.put("GOST3411", Integer.valueOf(32));
     36 
     37         blockLengths.put("MD2", Integer.valueOf(16));
     38         blockLengths.put("MD4", Integer.valueOf(64));
     39         blockLengths.put("MD5", Integer.valueOf(64));
     40 
     41         blockLengths.put("RIPEMD128", Integer.valueOf(64));
     42         blockLengths.put("RIPEMD160", Integer.valueOf(64));
     43 
     44         blockLengths.put("SHA-1", Integer.valueOf(64));
     45         blockLengths.put("SHA-224", Integer.valueOf(64));
     46         blockLengths.put("SHA-256", Integer.valueOf(64));
     47         blockLengths.put("SHA-384", Integer.valueOf(128));
     48         blockLengths.put("SHA-512", Integer.valueOf(128));
     49 
     50         blockLengths.put("Tiger", Integer.valueOf(64));
     51         blockLengths.put("Whirlpool", Integer.valueOf(64));
     52     }
     53 
     54     private static int getByteLength(
     55         Digest digest)
     56     {
     57         if (digest instanceof ExtendedDigest)
     58         {
     59             return ((ExtendedDigest)digest).getByteLength();
     60         }
     61 
     62         Integer  b = (Integer)blockLengths.get(digest.getAlgorithmName());
     63 
     64         if (b == null)
     65         {
     66             throw new IllegalArgumentException("unknown digest passed: " + digest.getAlgorithmName());
     67         }
     68 
     69         return b.intValue();
     70     }
     71 
     72     /**
     73      * Base constructor for one of the standard digest algorithms that the
     74      * byteLength of the algorithm is know for.
     75      *
     76      * @param digest the digest.
     77      */
     78     public HMac(
     79         Digest digest)
     80     {
     81         this(digest, getByteLength(digest));
     82     }
     83 
     84     private HMac(
     85         Digest digest,
     86         int    byteLength)
     87     {
     88         this.digest = digest;
     89         digestSize = digest.getDigestSize();
     90 
     91         this.blockLength = byteLength;
     92 
     93         inputPad = new byte[blockLength];
     94         outputPad = new byte[blockLength];
     95     }
     96 
     97     public String getAlgorithmName()
     98     {
     99         return digest.getAlgorithmName() + "/HMAC";
    100     }
    101 
    102     public Digest getUnderlyingDigest()
    103     {
    104         return digest;
    105     }
    106 
    107     public void init(
    108         CipherParameters params)
    109     {
    110         digest.reset();
    111 
    112         byte[] key = ((KeyParameter)params).getKey();
    113 
    114         if (key.length > blockLength)
    115         {
    116             digest.update(key, 0, key.length);
    117             digest.doFinal(inputPad, 0);
    118             for (int i = digestSize; i < inputPad.length; i++)
    119             {
    120                 inputPad[i] = 0;
    121             }
    122         }
    123         else
    124         {
    125             System.arraycopy(key, 0, inputPad, 0, key.length);
    126             for (int i = key.length; i < inputPad.length; i++)
    127             {
    128                 inputPad[i] = 0;
    129             }
    130         }
    131 
    132         outputPad = new byte[inputPad.length];
    133         System.arraycopy(inputPad, 0, outputPad, 0, inputPad.length);
    134 
    135         for (int i = 0; i < inputPad.length; i++)
    136         {
    137             inputPad[i] ^= IPAD;
    138         }
    139 
    140         for (int i = 0; i < outputPad.length; i++)
    141         {
    142             outputPad[i] ^= OPAD;
    143         }
    144 
    145         digest.update(inputPad, 0, inputPad.length);
    146     }
    147 
    148     public int getMacSize()
    149     {
    150         return digestSize;
    151     }
    152 
    153     public void update(
    154         byte in)
    155     {
    156         digest.update(in);
    157     }
    158 
    159     public void update(
    160         byte[] in,
    161         int inOff,
    162         int len)
    163     {
    164         digest.update(in, inOff, len);
    165     }
    166 
    167     public int doFinal(
    168         byte[] out,
    169         int outOff)
    170     {
    171         byte[] tmp = new byte[digestSize];
    172         digest.doFinal(tmp, 0);
    173 
    174         digest.update(outputPad, 0, outputPad.length);
    175         digest.update(tmp, 0, tmp.length);
    176 
    177         int     len = digest.doFinal(out, outOff);
    178 
    179         reset();
    180 
    181         return len;
    182     }
    183 
    184     /**
    185      * Reset the mac generator.
    186      */
    187     public void reset()
    188     {
    189         /*
    190          * reset the underlying digest.
    191          */
    192         digest.reset();
    193 
    194         /*
    195          * reinitialize the digest.
    196          */
    197         digest.update(inputPad, 0, inputPad.length);
    198     }
    199 }
    200