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