1 /* $NetBSD: hmac.c,v 1.3 2011/05/16 10:39:12 drochner 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.3 2011/05/16 10:39:12 drochner 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 void 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 * 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 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 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 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 311 312