hmac.c revision 1.1 1 /*
2 * Copyright (c) 2004, Juniper Networks, Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the copyright holders nor the names of its
14 * contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29 /*
30 * Implement HMAC as described in RFC 2104
31 *
32 * You need to define the following before including this file.
33 *
34 * HMAC_FUNC the name of the function (hmac_sha1 or hmac_md5 etc)
35 * HASH_LENGTH the size of the digest (20 for SHA1, 16 for MD5)
36 * HASH_CTX the name of the HASH CTX
37 * HASH_Init
38 * HASH_Update
39 * Hash_Final
40 */
41 #include <sys/cdefs.h>
42 #if !defined(lint)
43 __RCSID("$Id: hmac.c,v 1.1 2004/07/02 00:05:23 sjg Exp $");
44 #endif /* not lint */
45
46 #include <stdlib.h>
47 #include <string.h>
48
49 /* Don't change these */
50 #define HMAC_IPAD 0x36
51 #define HMAC_OPAD 0x5c
52
53 /* Nor this */
54 #ifndef HMAC_BLOCKSZ
55 # define HMAC_BLOCKSZ 64
56 #endif
57
58 /* Keep gcc quiet */
59 void
60 HMAC_FUNC (unsigned char *text, size_t text_len,
61 unsigned char *key, size_t key_len,
62 unsigned char *digest);
63
64 /*
65 * The logic here is lifted straight from RFC 2104 except that
66 * rather than filling the pads with 0, copying in the key and then
67 * XOR with the pad byte, we just fill with the pad byte and
68 * XOR with the key.
69 */
70 void
71 HMAC_FUNC (unsigned char *text, size_t text_len,
72 unsigned char *key, size_t key_len,
73 unsigned char *digest)
74 {
75 HASH_CTX context;
76 /* Inner padding key XOR'd with ipad */
77 unsigned char k_ipad[HMAC_BLOCKSZ + 1];
78 /* Outer padding key XOR'd with opad */
79 unsigned char k_opad[HMAC_BLOCKSZ + 1];
80 /* HASH(key) if needed */
81 unsigned char tk[HASH_LENGTH];
82 int i;
83
84 /*
85 * If key is longer than HMAC_BLOCKSZ bytes
86 * reset it to key=HASH(key)
87 */
88 if (key_len > HMAC_BLOCKSZ) {
89 HASH_CTX tctx;
90
91 HASH_Init(&tctx);
92 HASH_Update(&tctx, key, key_len);
93 HASH_Final(tk, &tctx);
94
95 key = tk;
96 key_len = HASH_LENGTH;
97 }
98
99 /*
100 * The HMAC_ transform looks like:
101 *
102 * HASH(K XOR opad, HASH(K XOR ipad, text))
103 *
104 * where K is an n byte key
105 * ipad is the byte HMAC_IPAD repeated HMAC_BLOCKSZ times
106 * opad is the byte HMAC_OPAD repeated HMAC_BLOCKSZ times
107 * and text is the data being protected
108 */
109
110 /*
111 * Fill the pads and XOR in the key
112 */
113 memset( k_ipad, HMAC_IPAD, sizeof k_ipad);
114 memset( k_opad, HMAC_OPAD, sizeof k_opad);
115 for (i = 0; i < key_len; i++) {
116 k_ipad[i] ^= key[i];
117 k_opad[i] ^= key[i];
118 }
119
120 /*
121 * Perform inner HASH.
122 * Start with inner pad,
123 * then the text.
124 */
125 HASH_Init(&context);
126 HASH_Update(&context, k_ipad, HMAC_BLOCKSZ);
127 HASH_Update(&context, text, text_len);
128 HASH_Final(digest, &context);
129
130 /*
131 * Perform outer HASH.
132 * Start with the outer pad,
133 * then the result of the inner hash.
134 */
135 HASH_Init(&context);
136 HASH_Update(&context, k_opad, HMAC_BLOCKSZ);
137 HASH_Update(&context, digest, HASH_LENGTH);
138 HASH_Final(digest, &context);
139 }
140
141 #if defined(MAIN) || defined(UNIT_TEST)
142 #include <stdio.h>
143
144
145 static char *
146 b2x(char *buf, int bufsz, unsigned char *data, int nbytes)
147 {
148 int i;
149
150 if (bufsz <= (nbytes * 2))
151 return NULL;
152 buf[0] = '\0';
153 for (i = 0; i < nbytes; i++) {
154 (void) sprintf(&buf[i*2], "%02x", data[i]);
155 }
156 return buf;
157 }
158
159 #if defined(UNIT_TEST)
160
161 static int
162 x2b(unsigned char *buf, int bufsz, char *data, int nbytes)
163 {
164 int i;
165 int c;
166
167 if (nbytes < 0)
168 nbytes = strlen(data);
169 nbytes /= 2;
170 if (bufsz <= nbytes)
171 return 0;
172 for (i = 0; i < nbytes; i++) {
173 if (sscanf(&data[i*2], "%02x", &c) < 1)
174 break;
175 buf[i] = c;
176 }
177 buf[i] = 0;
178 return i;
179 }
180
181 #ifndef HMAC_KAT
182 # define HMAC_KAT hmac_kat
183 #endif
184
185 /*
186 * If a test key or data starts with 0x we'll convert to binary.
187 */
188 #define X2B(v, b) do { \
189 if (strncmp(v, "0x", 2) == 0) { \
190 v += 2; \
191 x2b(b, sizeof(b), v, strlen(v)); \
192 v = b; \
193 } \
194 } while (0)
195
196 /*
197 * Run some of the known answer tests from RFC 2202
198 * We assume that HASH_LENGTH==20 means SHA1 else MD5.
199 */
200 static int
201 HMAC_KAT (FILE *fp)
202 {
203 struct test_s {
204 unsigned char *key;
205 unsigned char *data;
206 unsigned char *expect;
207 } tests[] = {
208 {
209 #if HASH_LENGTH == 20
210 "0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
211 "Hi There",
212 "0xb617318655057264e28bc0b6fb378c8ef146be00",
213 #else
214 "0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
215 "Hi There",
216 "0x9294727a3638bb1c13f48ef8158bfc9d",
217 #endif
218 },
219 {
220 "Jefe",
221 "what do ya want for nothing?",
222 #if HASH_LENGTH == 20
223 "0xeffcdf6ae5eb2fa2d27416d5f184df9c259a7c79",
224 #else
225 "0x750c783e6ab0b503eaa86e310a5db738",
226 #endif
227 },
228 {
229 #if HASH_LENGTH == 20
230 "0x0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c",
231 "Test With Truncation",
232 "0x4c1a03424b55e07fe7f27be1d58bb9324a9a5a04",
233 #else
234 "0x0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c",
235 "Test With Truncation",
236 "0x56461ef2342edc00f9bab995690efd4c",
237 #endif
238 },
239 {
240 "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
241 "Test Using Larger Than Block-Size Key - Hash Key First",
242 #if HASH_LENGTH == 20
243 "0xaa4ae5e15272d00e95705637ce8a3b55ed402112",
244 #else
245 "0x6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd",
246 #endif
247 },
248 {
249 0, 0, 0,
250 },
251 };
252 struct test_s *test = tests;
253 unsigned char digest[HASH_LENGTH];
254 unsigned char kbuf[BUFSIZ];
255 unsigned char dbuf[BUFSIZ];
256 unsigned char *key;
257 unsigned char *data;
258 char *result;
259 int n = 0;
260
261 for (test = tests; test->key; test++) {
262 key = test->key;
263 X2B(key, kbuf);
264 data = test->data;
265 X2B(data, dbuf);
266 HMAC_FUNC(data, strlen(data), key, strlen(key), digest);
267 strcpy(dbuf, "0x");
268 b2x(&dbuf[2], (sizeof dbuf) - 2, digest, HASH_LENGTH);
269
270 if (strcmp(dbuf, test->expect) == 0)
271 result = "Ok";
272 else {
273 n++;
274 result = test->expect;
275 }
276 if (fp)
277 fprintf(fp, "key=%s, data=%s, result=%s: %s\n",
278 test->key, test->data, dbuf, result);
279 }
280 return n;
281 }
282 #endif
283
284
285 int
286 main (int argc, char *argv[])
287 {
288 char buf[BUFSIZ];
289 unsigned char *key;
290 unsigned char *data;
291 int key_len;
292 int data_len;
293 int i;
294 unsigned char digest[HASH_LENGTH];
295
296 #ifdef UNIT_TEST
297 if (argc == 1)
298 exit(HMAC_KAT(stdout));
299 #endif
300
301 if (argc < 3) {
302 fprintf(stderr, "Usage:\n\t%s key data\n", argv[0]);
303 exit(1);
304 }
305 key = argv[1];
306 data = argv[2];
307 key_len = strlen(key);
308 data_len = strlen(data);
309 HMAC_FUNC(data, data_len, key, key_len, digest);
310 printf("0x%s\n", b2x(buf, sizeof buf, digest, HASH_LENGTH));
311 exit(0);
312 }
313 #endif
314
315
316