1:
37:
38:
39: package ;
40:
41: import ;
42: import ;
43: import ;
44: import ;
45:
46: import ;
47: import ;
48: import ;
49:
50:
79: public class HMac
80: extends BaseMac
81: implements Cloneable
82: {
83: public static final String USE_WITH_PKCS5_V2 = "gnu.crypto.hmac.pkcs5";
84: private static final byte IPAD_BYTE = 0x36;
85: private static final byte OPAD_BYTE = 0x5C;
86:
87: private static Boolean valid;
88: protected int macSize;
89: protected int blockSize;
90: protected IMessageDigest ipadHash;
91: protected IMessageDigest opadHash;
92: protected byte[] ipad;
93:
94:
99: protected HMac(IMessageDigest underlyingHash)
100: {
101: super(Registry.HMAC_NAME_PREFIX + underlyingHash.name(), underlyingHash);
102:
103: this.blockSize = underlyingHash.blockSize();
104: this.macSize = underlyingHash.hashSize();
105: ipadHash = opadHash = null;
106: }
107:
108: public Object clone() throws CloneNotSupportedException
109: {
110: HMac result = (HMac) super.clone();
111: if (this.ipadHash != null)
112: result.ipadHash = (IMessageDigest) this.ipadHash.clone();
113: if (this.opadHash != null)
114: result.opadHash = (IMessageDigest) this.opadHash.clone();
115: if (this.ipad != null)
116: result.ipad = (byte[]) this.ipad.clone();
117:
118: return result;
119: }
120:
121: public void init(Map attributes) throws InvalidKeyException,
122: IllegalStateException
123: {
124: Integer ts = (Integer) attributes.get(TRUNCATED_SIZE);
125: truncatedSize = (ts == null ? macSize : ts.intValue());
126: if (truncatedSize < (macSize / 2))
127: throw new IllegalArgumentException("Truncated size too small");
128: else if (truncatedSize < 10)
129: throw new IllegalArgumentException("Truncated size less than 80 bits");
130:
131:
132: byte[] K = (byte[]) attributes.get(MAC_KEY_MATERIAL);
133: if (K == null)
134: {
135: if (ipadHash == null)
136: throw new InvalidKeyException("Null key");
137:
138: underlyingHash = (IMessageDigest) ipadHash.clone();
139: return;
140: }
141:
142:
143:
144: Boolean pkcs5 = (Boolean) attributes.get(USE_WITH_PKCS5_V2);
145: if (pkcs5 == null)
146: pkcs5 = Boolean.FALSE;
147: if (K.length < macSize && ! pkcs5.booleanValue())
148: throw new InvalidKeyException("Key too short");
149:
150: if (K.length > blockSize)
151: {
152:
153:
154: underlyingHash.update(K, 0, K.length);
155: K = underlyingHash.digest();
156: }
157: if (K.length < blockSize)
158: {
159:
160:
161:
162: int limit = (K.length > blockSize) ? blockSize : K.length;
163: byte[] newK = new byte[blockSize];
164: System.arraycopy(K, 0, newK, 0, limit);
165: K = newK;
166: }
167: underlyingHash.reset();
168: opadHash = (IMessageDigest) underlyingHash.clone();
169: if (ipad == null)
170: ipad = new byte[blockSize];
171:
172:
173:
174:
175:
176: for (int i = 0; i < blockSize; i++)
177: ipad[i] = (byte)(K[i] ^ IPAD_BYTE);
178: for (int i = 0; i < blockSize; i++)
179: opadHash.update((byte)(K[i] ^ OPAD_BYTE));
180: underlyingHash.update(ipad, 0, blockSize);
181: ipadHash = (IMessageDigest) underlyingHash.clone();
182: K = null;
183: }
184:
185: public void reset()
186: {
187: super.reset();
188: if (ipad != null)
189: {
190: underlyingHash.update(ipad, 0, blockSize);
191: ipadHash = (IMessageDigest) underlyingHash.clone();
192: }
193: }
194:
195: public byte[] digest()
196: {
197: if (ipadHash == null)
198: throw new IllegalStateException("HMAC not initialised");
199: byte[] out = underlyingHash.digest();
200:
201:
202: underlyingHash = (IMessageDigest) opadHash.clone();
203:
204:
205: underlyingHash.update(out, 0, macSize);
206:
207: out = underlyingHash.digest();
208:
209: if (truncatedSize == macSize)
210: return out;
211: byte[] result = new byte[truncatedSize];
212: System.arraycopy(out, 0, result, 0, truncatedSize);
213: return result;
214: }
215:
216: public boolean selfTest()
217: {
218: if (valid == null)
219: {
220: try
221: {
222: IMac mac = new HMac(new MD5());
223: String tv1 = "9294727A3638BB1C13F48EF8158BFC9D";
224: String tv3 = "56BE34521D144C88DBB8C733F0E8B3F6";
225: byte[] k1 = new byte[] {
226: 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
227: 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B };
228: byte[] k3 = new byte[] {
229: (byte) 0xAA, (byte) 0xAA, (byte) 0xAA, (byte) 0xAA,
230: (byte) 0xAA, (byte) 0xAA, (byte) 0xAA, (byte) 0xAA,
231: (byte) 0xAA, (byte) 0xAA, (byte) 0xAA, (byte) 0xAA,
232: (byte) 0xAA, (byte) 0xAA, (byte) 0xAA, (byte) 0xAA };
233: byte[] data = new byte[50];
234: for (int i = 0; i < 50;)
235: data[i++] = (byte) 0xDD;
236:
237: HashMap map = new HashMap();
238:
239: map.put(MAC_KEY_MATERIAL, k1);
240: mac.init(map);
241: mac.update("Hi There".getBytes("ASCII"), 0, 8);
242: if (! tv1.equals(Util.toString(mac.digest())))
243: valid = Boolean.FALSE;
244:
245:
246:
247:
248: map.put(MAC_KEY_MATERIAL, k3);
249: mac.init(map);
250: mac.update(data, 0, 50);
251: if (! tv3.equals(Util.toString(mac.digest())))
252: valid = Boolean.FALSE;
253: valid = Boolean.TRUE;
254: }
255: catch (Exception x)
256: {
257: x.printStackTrace(System.err);
258: valid = Boolean.FALSE;
259: }
260: }
261: return valid.booleanValue();
262: }
263: }