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