diff options
Diffstat (limited to 'bdb/hmac/hmac.c')
-rw-r--r-- | bdb/hmac/hmac.c | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/bdb/hmac/hmac.c b/bdb/hmac/hmac.c new file mode 100644 index 00000000000..d39a154ec63 --- /dev/null +++ b/bdb/hmac/hmac.c @@ -0,0 +1,207 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2001-2002 + * Sleepycat Software. All rights reserved. + * + * Some parts of this code originally written by Adam Stubblefield, + * astubble@rice.edu. + */ + +#include "db_config.h" + +#ifndef lint +static const char revid[] = "$Id: hmac.c,v 1.25 2002/09/10 02:40:40 bostic Exp $"; +#endif /* not lint */ + +#ifndef NO_SYSTEM_INCLUDES +#include <string.h> +#endif + +#include "db_int.h" +#include "dbinc/crypto.h" +#include "dbinc/db_page.h" /* for hash.h only */ +#include "dbinc/hash.h" +#include "dbinc/hmac.h" + +#define HMAC_OUTPUT_SIZE 20 +#define HMAC_BLOCK_SIZE 64 + +static void __db_hmac __P((u_int8_t *, u_int8_t *, size_t, u_int8_t *)); + +/* + * !!! + * All of these functions use a ctx structure on the stack. The __db_SHA1Init + * call does not initialize the 64-byte buffer portion of it. The + * underlying SHA1 functions will properly pad the buffer if the data length + * is less than 64-bytes, so there isn't a chance of reading uninitialized + * memory. Although it would be cleaner to do a memset(ctx.buffer, 0, 64) + * we do not want to incur that penalty if we don't have to for performance. + */ + +/* + * __db_hmac -- + * Do a hashed MAC. + */ +static void +__db_hmac(k, data, data_len, mac) + u_int8_t *k, *data, *mac; + size_t data_len; +{ + SHA1_CTX ctx; + u_int8_t key[HMAC_BLOCK_SIZE]; + u_int8_t ipad[HMAC_BLOCK_SIZE]; + u_int8_t opad[HMAC_BLOCK_SIZE]; + u_int8_t tmp[HMAC_OUTPUT_SIZE]; + int i; + + memset(key, 0x00, HMAC_BLOCK_SIZE); + memset(ipad, 0x36, HMAC_BLOCK_SIZE); + memset(opad, 0x5C, HMAC_BLOCK_SIZE); + + memcpy(key, k, HMAC_OUTPUT_SIZE); + + for (i = 0; i < HMAC_BLOCK_SIZE; i++) { + ipad[i] ^= key[i]; + opad[i] ^= key[i]; + } + + __db_SHA1Init(&ctx); + __db_SHA1Update(&ctx, ipad, HMAC_BLOCK_SIZE); + __db_SHA1Update(&ctx, data, data_len); + __db_SHA1Final(tmp, &ctx); + __db_SHA1Init(&ctx); + __db_SHA1Update(&ctx, opad, HMAC_BLOCK_SIZE); + __db_SHA1Update(&ctx, tmp, HMAC_OUTPUT_SIZE); + __db_SHA1Final(mac, &ctx); + return; +} + +/* + * __db_chksum -- + * Create a MAC/SHA1 checksum. + * + * PUBLIC: void __db_chksum __P((u_int8_t *, size_t, u_int8_t *, u_int8_t *)); + */ +void +__db_chksum(data, data_len, mac_key, store) + u_int8_t *data; + size_t data_len; + u_int8_t *mac_key; + u_int8_t *store; +{ + int sumlen; + u_int32_t hash4; + u_int8_t tmp[DB_MAC_KEY]; + + /* + * Since the checksum might be on a page of data we are checksumming + * we might be overwriting after checksumming, we zero-out the + * checksum value so that we can have a known value there when + * we verify the checksum. + */ + if (mac_key == NULL) + sumlen = sizeof(u_int32_t); + else + sumlen = DB_MAC_KEY; + memset(store, 0, sumlen); + if (mac_key == NULL) { + /* Just a hash, no MAC */ + hash4 = __ham_func4(NULL, data, (u_int32_t)data_len); + memcpy(store, &hash4, sumlen); + } else { + memset(tmp, 0, DB_MAC_KEY); + __db_hmac(mac_key, data, data_len, tmp); + memcpy(store, tmp, sumlen); + } + return; +} +/* + * __db_derive_mac -- + * Create a MAC/SHA1 key. + * + * PUBLIC: void __db_derive_mac __P((u_int8_t *, size_t, u_int8_t *)); + */ +void +__db_derive_mac(passwd, plen, mac_key) + u_int8_t *passwd; + size_t plen; + u_int8_t *mac_key; +{ + SHA1_CTX ctx; + + /* Compute the MAC key. mac_key must be 20 bytes. */ + __db_SHA1Init(&ctx); + __db_SHA1Update(&ctx, passwd, plen); + __db_SHA1Update(&ctx, (u_int8_t *)DB_MAC_MAGIC, strlen(DB_MAC_MAGIC)); + __db_SHA1Update(&ctx, passwd, plen); + __db_SHA1Final(mac_key, &ctx); + + return; +} + +/* + * __db_check_chksum -- + * Verify a checksum. + * + * Return 0 on success, >0 (errno) on error, -1 on checksum mismatch. + * + * PUBLIC: int __db_check_chksum __P((DB_ENV *, + * PUBLIC: DB_CIPHER *, u_int8_t *, void *, size_t, int)); + */ +int +__db_check_chksum(dbenv, db_cipher, chksum, data, data_len, is_hmac) + DB_ENV *dbenv; + DB_CIPHER *db_cipher; + u_int8_t *chksum; + void *data; + size_t data_len; + int is_hmac; +{ + int ret; + size_t sum_len; + u_int32_t hash4; + u_int8_t *mac_key, old[DB_MAC_KEY], new[DB_MAC_KEY]; + + /* + * If we are just doing checksumming and not encryption, then checksum + * is 4 bytes. Otherwise, it is DB_MAC_KEY size. Check for illegal + * combinations of crypto/non-crypto checksums. + */ + if (is_hmac == 0) { + if (db_cipher != NULL) { + __db_err(dbenv, + "Unencrypted checksum with a supplied encryption key"); + return (EINVAL); + } + sum_len = sizeof(u_int32_t); + mac_key = NULL; + } else { + if (db_cipher == NULL) { + __db_err(dbenv, + "Encrypted checksum: no encryption key specified"); + return (EINVAL); + } + sum_len = DB_MAC_KEY; + mac_key = db_cipher->mac_key; + } + + /* + * !!! + * Since the checksum might be on the page, we need to have known data + * there so that we can generate the same original checksum. We zero + * it out, just like we do in __db_chksum above. + */ + memcpy(old, chksum, sum_len); + memset(chksum, 0, sum_len); + if (mac_key == NULL) { + /* Just a hash, no MAC */ + hash4 = __ham_func4(NULL, data, (u_int32_t)data_len); + ret = memcmp((u_int32_t *)old, &hash4, sum_len) ? -1 : 0; + } else { + __db_hmac(mac_key, data, data_len, new); + ret = memcmp(old, new, sum_len) ? -1 : 0; + } + + return (ret); +} |