1 /* $NetBSD: hmac.c,v 1.5 2024/07/23 22:37:11 riastradh 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.5 2024/07/23 22:37:11 riastradh 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  */
66 crypt_private void
HMAC_FUNC(const unsigned char * text,size_t text_len,const unsigned char * key,size_t key_len,unsigned char * digest)67 HMAC_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 
141 static char *
b2x(char * buf,int bufsz,unsigned char * data,int nbytes)142 b2x(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 
157 static int
x2b(unsigned char * buf,int bufsz,char * data,int nbytes)158 x2b(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  */
196 static int
HMAC_KAT(FILE * fp)197 HMAC_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 
281 int
main(int argc,char * argv[])282 main (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