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