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