1 /* $NetBSD: hmac.c,v 1.3 2011/05/16 10:39:12 drochner Exp $ */
4 * Copyright (c) 2004, Juniper Networks, Inc.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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.
32 * Implement HMAC as described in RFC 2104
34 * You need to define the following before including this file.
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
43 #include <sys/cdefs.h>
45 __RCSID("$NetBSD: hmac.c,v 1.3 2011/05/16 10:39:12 drochner Exp $");
51 /* Don't change these */
52 #define HMAC_IPAD 0x36
53 #define HMAC_OPAD 0x5c
57 # define HMAC_BLOCKSZ 64
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
67 HMAC_FUNC (const unsigned char *text
, size_t text_len
,
68 const unsigned char *key
, size_t key_len
,
69 unsigned char *digest
)
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
];
81 * If key is longer than HMAC_BLOCKSZ bytes
82 * reset it to key=HASH(key)
84 if (key_len
> HMAC_BLOCKSZ
) {
88 HASH_Update(&tctx
, key
, key_len
);
89 HASH_Final(tk
, &tctx
);
92 key_len
= HASH_LENGTH
;
96 * The HMAC_ transform looks like:
98 * HASH(K XOR opad, HASH(K XOR ipad, text))
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
107 * Fill the pads and XOR in the key
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
++) {
117 * Perform inner HASH.
118 * Start with inner pad,
122 HASH_Update(&context
, k_ipad
, HMAC_BLOCKSZ
);
123 HASH_Update(&context
, text
, text_len
);
124 HASH_Final(digest
, &context
);
127 * Perform outer HASH.
128 * Start with the outer pad,
129 * then the result of the inner hash.
132 HASH_Update(&context
, k_opad
, HMAC_BLOCKSZ
);
133 HASH_Update(&context
, digest
, HASH_LENGTH
);
134 HASH_Final(digest
, &context
);
137 #if defined(MAIN) || defined(UNIT_TEST)
142 b2x(char *buf
, int bufsz
, unsigned char *data
, int nbytes
)
146 if (bufsz
<= (nbytes
* 2))
149 for (i
= 0; i
< nbytes
; i
++) {
150 (void) sprintf(&buf
[i
*2], "%02x", data
[i
]);
155 #if defined(UNIT_TEST)
158 x2b(unsigned char *buf
, int bufsz
, char *data
, int nbytes
)
164 nbytes
= strlen(data
);
168 for (i
= 0; i
< nbytes
; i
++) {
169 if (sscanf(&data
[i
*2], "%02x", &c
) < 1)
178 # define HMAC_KAT hmac_kat
182 * If a test key or data starts with 0x we'll convert to binary.
184 #define X2B(v, b) do { \
185 if (strncmp(v, "0x", 2) == 0) { \
187 x2b(b, sizeof(b), v, strlen(v)); \
193 * Run some of the known answer tests from RFC 2202
194 * We assume that HASH_LENGTH==20 means SHA1 else MD5.
202 unsigned char *expect
;
205 #if HASH_LENGTH == 20
206 "0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
208 "0xb617318655057264e28bc0b6fb378c8ef146be00",
210 "0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
212 "0x9294727a3638bb1c13f48ef8158bfc9d",
217 "what do ya want for nothing?",
218 #if HASH_LENGTH == 20
219 "0xeffcdf6ae5eb2fa2d27416d5f184df9c259a7c79",
221 "0x750c783e6ab0b503eaa86e310a5db738",
225 #if HASH_LENGTH == 20
226 "0x0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c",
227 "Test With Truncation",
228 "0x4c1a03424b55e07fe7f27be1d58bb9324a9a5a04",
230 "0x0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c",
231 "Test With Truncation",
232 "0x56461ef2342edc00f9bab995690efd4c",
236 "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
237 "Test Using Larger Than Block-Size Key - Hash Key First",
238 #if HASH_LENGTH == 20
239 "0xaa4ae5e15272d00e95705637ce8a3b55ed402112",
241 "0x6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd",
248 struct test_s
*test
= tests
;
249 unsigned char digest
[HASH_LENGTH
];
250 unsigned char kbuf
[BUFSIZ
];
251 unsigned char dbuf
[BUFSIZ
];
257 for (test
= tests
; test
->key
; test
++) {
262 HMAC_FUNC(data
, strlen(data
), key
, strlen(key
), digest
);
264 b2x(&dbuf
[2], (sizeof dbuf
) - 2, digest
, HASH_LENGTH
);
266 if (strcmp(dbuf
, test
->expect
) == 0)
270 result
= test
->expect
;
273 fprintf(fp
, "key=%s, data=%s, result=%s: %s\n",
274 test
->key
, test
->data
, dbuf
, result
);
282 main (int argc
, char *argv
[])
290 unsigned char digest
[HASH_LENGTH
];
294 exit(HMAC_KAT(stdout
));
298 fprintf(stderr
, "Usage:\n\t%s key data\n", argv
[0]);
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
));