Mercurial > pidgin
diff libpurple/cipher.c @ 21908:f786e478e08b
HMAC digest support from Elliott Sales de Andrade
committer: Gary Kramlich <grim@reaperworld.com>
author | Elliott Sales de Andrade <qulogic@pidgin.im> |
---|---|
date | Thu, 20 Dec 2007 03:44:01 +0000 |
parents | 03463c52b9d7 |
children | 76e0463db3aa |
line wrap: on
line diff
--- a/libpurple/cipher.c Thu Dec 20 03:40:56 2007 +0000 +++ b/libpurple/cipher.c Thu Dec 20 03:44:01 2007 +0000 @@ -64,6 +64,8 @@ /******************************************************************************* * MD5 ******************************************************************************/ +#define MD5_HMAC_BLOCK_SIZE 64 + struct MD5Context { guint32 total[2]; guint32 state[4]; @@ -325,6 +327,13 @@ return TRUE; } +static size_t +md5_get_block_size(PurpleCipherContext *context) +{ + /* This does not change (in this case) */ + return MD5_HMAC_BLOCK_SIZE; +} + static PurpleCipherOps MD5Ops = { NULL, /* Set option */ NULL, /* Get option */ @@ -342,10 +351,8 @@ NULL, /* get key size */ NULL, /* set batch mode */ NULL, /* get batch mode */ - - /* padding */ - NULL, - NULL + md5_get_block_size, /* get block size */ + NULL /* set key with len */ }; /******************************************************************************* @@ -580,6 +587,13 @@ md4_context = NULL; } +static size_t +md4_get_block_size(PurpleCipherContext *context) +{ + /* This does not change (in this case) */ + return MD4_HMAC_BLOCK_SIZE; +} + static PurpleCipherOps MD4Ops = { NULL, /* Set option */ NULL, /* Get option */ @@ -597,10 +611,200 @@ NULL, /* get key size */ NULL, /* set batch mode */ NULL, /* get batch mode */ - - /* padding */ - NULL, - NULL + md4_get_block_size, /* get block size */ + NULL /* set key with len */ +}; + +/******************************************************************************* + * HMAC + ******************************************************************************/ + +struct HMAC_Context { + PurpleCipherContext *hash; + char *name; + int blocksize; + guchar *opad; +}; + +static void +hmac_init(PurpleCipherContext *context, gpointer extra) +{ + struct HMAC_Context *hctx; + hctx = g_new0(struct HMAC_Context, 1); + purple_cipher_context_set_data(context, hctx); + purple_cipher_context_reset(context, extra); +} + +static void +hmac_reset(PurpleCipherContext *context, gpointer extra) +{ + struct HMAC_Context *hctx; + + hctx = purple_cipher_context_get_data(context); + + g_free(hctx->name); + hctx->name = NULL; + if (hctx->hash) + purple_cipher_context_destroy(hctx->hash); + hctx->hash = NULL; + hctx->blocksize = 0; + g_free(hctx->opad); + hctx->opad = NULL; +} + +static void +hmac_set_opt(PurpleCipherContext *context, const gchar *name, void *value) +{ + struct HMAC_Context *hctx; + + hctx = purple_cipher_context_get_data(context); + + if (!strcmp(name, "hash")) { + g_free(hctx->name); + if (hctx->hash) + purple_cipher_context_destroy(hctx->hash); + hctx->name = g_strdup((char*)value); + hctx->hash = purple_cipher_context_new_by_name((char *)value, NULL); + hctx->blocksize = purple_cipher_context_get_block_size(hctx->hash); + } +} + +static void * +hmac_get_opt(PurpleCipherContext *context, const gchar *name) +{ + struct HMAC_Context *hctx; + + hctx = purple_cipher_context_get_data(context); + + if (!strcmp(name, "hash")) { + return hctx->name; + } + + return NULL; +} + +static void +hmac_append(PurpleCipherContext *context, const guchar *data, size_t len) +{ + struct HMAC_Context *hctx = purple_cipher_context_get_data(context); + + g_return_if_fail(hctx->hash != NULL); + + purple_cipher_context_append(hctx->hash, data, len); +} + +static gboolean +hmac_digest(PurpleCipherContext *context, size_t in_len, guchar *out, size_t *out_len) +{ + struct HMAC_Context *hctx = purple_cipher_context_get_data(context); + PurpleCipherContext *hash = hctx->hash; + guchar *inner_hash; + size_t hash_len; + gboolean result; + + g_return_val_if_fail(hash != NULL, FALSE); + + inner_hash = g_malloc(100); /* TODO: Should be enough for now... */ + result = purple_cipher_context_digest(hash, 100, inner_hash, &hash_len); + + purple_cipher_context_reset(hash, NULL); + + purple_cipher_context_append(hash, hctx->opad, hctx->blocksize); + purple_cipher_context_append(hash, inner_hash, hash_len); + + g_free(inner_hash); + + result = result && purple_cipher_context_digest(hash, in_len, out, out_len); + + return result; +} + +static void +hmac_uninit(PurpleCipherContext *context) +{ + struct HMAC_Context *hctx; + + purple_cipher_context_reset(context, NULL); + + hctx = purple_cipher_context_get_data(context); + + g_free(hctx); +} + +static void +hmac_set_key_with_len(PurpleCipherContext *context, const guchar * key, size_t key_len) +{ + struct HMAC_Context *hctx = purple_cipher_context_get_data(context); + int blocksize, i; + guchar *ipad; + guchar *full_key; + + g_return_if_fail(hctx->hash != NULL); + + g_free(hctx->opad); + + blocksize = hctx->blocksize; + ipad = g_malloc(blocksize); + hctx->opad = g_malloc(blocksize); + + if (key_len > blocksize) { + purple_cipher_context_reset(hctx->hash, NULL); + purple_cipher_context_append(hctx->hash, key, key_len); + full_key = g_malloc(100); /* TODO: Should be enough for now... */ + purple_cipher_context_digest(hctx->hash, 100, full_key, &key_len); + } else + full_key = g_memdup(key, key_len); + + if (key_len < blocksize) { + full_key = g_realloc(full_key, blocksize); + memset(full_key + key_len, 0, blocksize - key_len); + } + + for(i = 0; i < blocksize; i++) { + ipad[i] = 0x36 ^ full_key[i]; + hctx->opad[i] = 0x5c ^ full_key[i]; + } + + g_free(full_key); + + purple_cipher_context_reset(hctx->hash, NULL); + purple_cipher_context_append(hctx->hash, ipad, blocksize); + g_free(ipad); +} + +static void +hmac_set_key(PurpleCipherContext *context, const guchar * key) +{ + hmac_set_key_with_len(context, key, strlen(key)); +} + +static size_t +hmac_get_block_size(PurpleCipherContext *context) +{ + struct HMAC_Context *hctx = purple_cipher_context_get_data(context); + + return hctx->blocksize; +} + +static PurpleCipherOps HMACOps = { + hmac_set_opt, /* Set option */ + hmac_get_opt, /* Get option */ + hmac_init, /* init */ + hmac_reset, /* reset */ + hmac_uninit, /* uninit */ + NULL, /* set iv */ + hmac_append, /* append */ + hmac_digest, /* digest */ + NULL, /* encrypt */ + NULL, /* decrypt */ + NULL, /* set salt */ + NULL, /* get salt size */ + hmac_set_key, /* set key */ + NULL, /* get key size */ + NULL, /* set batch mode */ + NULL, /* get batch mode */ + hmac_get_block_size, /* get block size */ + hmac_set_key_with_len /* set key with len */ }; /****************************************************************************** @@ -1051,10 +1255,8 @@ NULL, /* get key size */ NULL, /* set batch mode */ NULL, /* get batch mode */ - - /* padding */ - NULL, - NULL + NULL, /* get block size */ + NULL /* set key with len */ }; /****************************************************************************** @@ -1403,15 +1605,14 @@ NULL, /* get key size */ des3_set_batch, /* set batch mode */ des3_get_batch, /* get batch mode */ - - /* padding */ - NULL, - NULL + NULL, /* get block size */ + NULL /* set key with len */ }; /******************************************************************************* * SHA-1 ******************************************************************************/ +#define SHA1_HMAC_BLOCK_SIZE 64 #define SHA1_ROTL(X,n) ((((X) << (n)) | ((X) >> (32-(n)))) & 0xFFFFFFFF) struct SHA1Context { @@ -1632,6 +1833,13 @@ return TRUE; } +static size_t +sha1_get_block_size(PurpleCipherContext *context) +{ + /* This does not change (in this case) */ + return SHA1_HMAC_BLOCK_SIZE; +} + static PurpleCipherOps SHA1Ops = { sha1_set_opt, /* Set Option */ sha1_get_opt, /* Get Option */ @@ -1649,10 +1857,8 @@ NULL, /* get key size */ NULL, /* set batch mode */ NULL, /* get batch mode */ - - /* padding */ - NULL, - NULL + sha1_get_block_size, /* get block size */ + NULL /* set key with len */ }; /******************************************************************************* @@ -1818,10 +2024,8 @@ rc4_get_key_size, /* get key size */ NULL, /* set batch mode */ NULL, /* get batch mode */ - - /* padding */ - NULL, - NULL + NULL, /* get block size */ + NULL /* set key with len */ }; /******************************************************************************* @@ -1895,6 +2099,10 @@ caps |= PURPLE_CIPHER_CAPS_SET_BATCH_MODE; if(ops->get_batch_mode) caps |= PURPLE_CIPHER_CAPS_GET_BATCH_MODE; + if(ops->get_block_size) + caps |= PURPLE_CIPHER_CAPS_GET_BLOCK_SIZE; + if(ops->set_key_with_len) + caps |= PURPLE_CIPHER_CAPS_SET_KEY_WITH_LEN; return caps; } @@ -2021,6 +2229,7 @@ purple_ciphers_register_cipher("md5", &MD5Ops); purple_ciphers_register_cipher("sha1", &SHA1Ops); purple_ciphers_register_cipher("md4", &MD4Ops); + purple_ciphers_register_cipher("hmac", &HMACOps); purple_ciphers_register_cipher("des", &DESOps); purple_ciphers_register_cipher("des3", &DES3Ops); purple_ciphers_register_cipher("rc4", &RC4Ops); @@ -2389,6 +2598,43 @@ } } +size_t +purple_cipher_context_get_block_size(PurpleCipherContext *context) +{ + PurpleCipher *cipher = NULL; + + g_return_val_if_fail(context, -1); + + cipher = context->cipher; + g_return_val_if_fail(cipher, -1); + + if(cipher->ops && cipher->ops->get_block_size) + return cipher->ops->get_block_size(context); + else { + purple_debug_info("cipher", "The %s cipher does not support the " + "get_block_size operation\n", cipher->name); + return -1; + } +} + +void +purple_cipher_context_set_key_with_len(PurpleCipherContext *context, + const guchar *key, size_t len) +{ + PurpleCipher *cipher = NULL; + + g_return_if_fail(context); + + cipher = context->cipher; + g_return_if_fail(cipher); + + if(cipher->ops && cipher->ops->set_key_with_len) + cipher->ops->set_key_with_len(context, key, len); + else + purple_debug_info("cipher", "The %s cipher does not support the " + "set_key_with_len operation\n", cipher->name); +} + void purple_cipher_context_set_data(PurpleCipherContext *context, gpointer data) { g_return_if_fail(context);