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 getByteLength( Digest digest)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 */ HMac( Digest digest)88 public HMac( 89 Digest digest) 90 { 91 this(digest, getByteLength(digest)); 92 } 93 HMac( Digest digest, int byteLength)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 getAlgorithmName()105 public String getAlgorithmName() 106 { 107 return digest.getAlgorithmName() + "/HMAC"; 108 } 109 getUnderlyingDigest()110 public Digest getUnderlyingDigest() 111 { 112 return digest; 113 } 114 init( CipherParameters params)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 getMacSize()160 public int getMacSize() 161 { 162 return digestSize; 163 } 164 update( byte in)165 public void update( 166 byte in) 167 { 168 digest.update(in); 169 } 170 update( byte[] in, int inOff, int len)171 public void update( 172 byte[] in, 173 int inOff, 174 int len) 175 { 176 digest.update(in, inOff, len); 177 } 178 doFinal( byte[] out, int outOff)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 */ reset()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 xorPad(byte[] pad, int len, byte n)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