1 // Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
2
3 package org.xbill.DNS.utils;
4
5 import java.util.Arrays;
6 import java.security.*;
7
8 /**
9 * An implementation of the HMAC message authentication code.
10 *
11 * @author Brian Wellington
12 */
13
14 public class HMAC {
15
16 private MessageDigest digest;
17 private int blockLength;
18
19 private byte [] ipad, opad;
20
21 private static final byte IPAD = 0x36;
22 private static final byte OPAD = 0x5c;
23
24 private void
init(byte [] key)25 init(byte [] key) {
26 int i;
27
28 if (key.length > blockLength) {
29 key = digest.digest(key);
30 digest.reset();
31 }
32 ipad = new byte[blockLength];
33 opad = new byte[blockLength];
34 for (i = 0; i < key.length; i++) {
35 ipad[i] = (byte) (key[i] ^ IPAD);
36 opad[i] = (byte) (key[i] ^ OPAD);
37 }
38 for (; i < blockLength; i++) {
39 ipad[i] = IPAD;
40 opad[i] = OPAD;
41 }
42 digest.update(ipad);
43 }
44
45 /**
46 * Creates a new HMAC instance
47 * @param digest The message digest object.
48 * @param blockLength The block length of the message digest.
49 * @param key The secret key
50 */
51 public
HMAC(MessageDigest digest, int blockLength, byte [] key)52 HMAC(MessageDigest digest, int blockLength, byte [] key) {
53 digest.reset();
54 this.digest = digest;
55 this.blockLength = blockLength;
56 init(key);
57 }
58
59 /**
60 * Creates a new HMAC instance
61 * @param digestName The name of the message digest function.
62 * @param blockLength The block length of the message digest.
63 * @param key The secret key.
64 */
65 public
HMAC(String digestName, int blockLength, byte [] key)66 HMAC(String digestName, int blockLength, byte [] key) {
67 try {
68 digest = MessageDigest.getInstance(digestName);
69 } catch (NoSuchAlgorithmException e) {
70 throw new IllegalArgumentException("unknown digest algorithm "
71 + digestName);
72 }
73 this.blockLength = blockLength;
74 init(key);
75 }
76
77 /**
78 * Creates a new HMAC instance
79 * @param digest The message digest object.
80 * @param key The secret key
81 * @deprecated won't work with digests using a padding length other than 64;
82 * use {@code HMAC(MessageDigest digest, int blockLength,
83 * byte [] key)} instead.
84 * @see HMAC#HMAC(MessageDigest digest, int blockLength, byte [] key)
85 */
86 public
HMAC(MessageDigest digest, byte [] key)87 HMAC(MessageDigest digest, byte [] key) {
88 this(digest, 64, key);
89 }
90
91 /**
92 * Creates a new HMAC instance
93 * @param digestName The name of the message digest function.
94 * @param key The secret key.
95 * @deprecated won't work with digests using a padding length other than 64;
96 * use {@code HMAC(String digestName, int blockLength, byte [] key)}
97 * instead
98 * @see HMAC#HMAC(String digestName, int blockLength, byte [] key)
99 */
100 public
HMAC(String digestName, byte [] key)101 HMAC(String digestName, byte [] key) {
102 this(digestName, 64, key);
103 }
104
105 /**
106 * Adds data to the current hash
107 * @param b The data
108 * @param offset The index at which to start adding to the hash
109 * @param length The number of bytes to hash
110 */
111 public void
update(byte [] b, int offset, int length)112 update(byte [] b, int offset, int length) {
113 digest.update(b, offset, length);
114 }
115
116 /**
117 * Adds data to the current hash
118 * @param b The data
119 */
120 public void
update(byte [] b)121 update(byte [] b) {
122 digest.update(b);
123 }
124
125 /**
126 * Signs the data (computes the secure hash)
127 * @return An array with the signature
128 */
129 public byte []
sign()130 sign() {
131 byte [] output = digest.digest();
132 digest.reset();
133 digest.update(opad);
134 return digest.digest(output);
135 }
136
137 /**
138 * Verifies the data (computes the secure hash and compares it to the input)
139 * @param signature The signature to compare against
140 * @return true if the signature matches, false otherwise
141 */
142 public boolean
verify(byte [] signature)143 verify(byte [] signature) {
144 return verify(signature, false);
145 }
146
147 /**
148 * Verifies the data (computes the secure hash and compares it to the input)
149 * @param signature The signature to compare against
150 * @param truncation_ok If true, the signature may be truncated; only the
151 * number of bytes in the provided signature are compared.
152 * @return true if the signature matches, false otherwise
153 */
154 public boolean
verify(byte [] signature, boolean truncation_ok)155 verify(byte [] signature, boolean truncation_ok) {
156 byte [] expected = sign();
157 if (truncation_ok && signature.length < expected.length) {
158 byte [] truncated = new byte[signature.length];
159 System.arraycopy(expected, 0, truncated, 0, truncated.length);
160 expected = truncated;
161 }
162 return Arrays.equals(signature, expected);
163 }
164
165 /**
166 * Resets the HMAC object for further use
167 */
168 public void
clear()169 clear() {
170 digest.reset();
171 digest.update(ipad);
172 }
173
174 /**
175 * Returns the length of the digest.
176 */
177 public int
digestLength()178 digestLength() {
179 return digest.getDigestLength();
180 }
181
182 }
183