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