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