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