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