xref: /netbsd-src/lib/libc/hash/hmac.c (revision aaf4ece63a859a04e37cf3a7229b5fab0157cc06)
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