# HG changeset patch # User Paul Aurich # Date 1246749203 0 # Node ID fc5508709e74eb2acf355544492d728efeb70dc0 # Parent 6e250a050cedec3d1c2da29076c42f7397a6709f Use glib's SHA1, SHA256, and MD5 implementations when available (glib 2.16) These SHA1 and SHA256 implementations do not expose any options, which the built-in ones do (for getting/setting sizeHi, sizeLo, and lenW). Nothing in Pidgin uses those (and I can't think of a decent use case), so I think this is OK. Feel free to disagree. As I mentioned on the mailing list, glib's SHA1 implementation was just under 3x as fast on my system. diff -r 6e250a050ced -r fc5508709e74 libpurple/cipher.c --- a/libpurple/cipher.c Sat Jul 04 22:27:37 2009 +0000 +++ b/libpurple/cipher.c Sat Jul 04 23:13:23 2009 +0000 @@ -61,11 +61,152 @@ #include "signals.h" #include "value.h" +#if GLIB_CHECK_VERSION(2,16,0) +static void +purple_g_checksum_init(PurpleCipherContext *context, GChecksumType type) +{ + GChecksum *checksum; + + checksum = g_checksum_new(type); + purple_cipher_context_set_data(context, checksum); +} + +static void +purple_g_checksum_reset(PurpleCipherContext *context, GChecksumType type) +{ + GChecksum *checksum; + + checksum = purple_cipher_context_get_data(context); + g_return_if_fail(checksum != NULL); + +#if GLIB_CHECK_VERSION(2,18,0) + g_checksum_reset(checksum); +#else + g_checksum_free(checksum); + checksum = g_checksum_new(type); + purple_cipher_context_set_data(context, checksum); +#endif +} + +static void +purple_g_checksum_uninit(PurpleCipherContext *context) +{ + GChecksum *checksum; + + checksum = purple_cipher_context_get_data(context); + g_return_if_fail(checksum != NULL); + + g_checksum_free(checksum); +} + +static void +purple_g_checksum_append(PurpleCipherContext *context, const guchar *data, + gsize len) +{ + GChecksum *checksum; + + checksum = purple_cipher_context_get_data(context); + g_return_if_fail(checksum != NULL); + + if (len > (gsize)G_MAXSSIZE) { + /* + * g_checksum_update takes a gssize, whereas we handle gsizes. To + * be pedantically correct, we need to handle this case. In real life, + * I think this couldn't actually occur. + */ + const guchar *buf = data; + gssize chunk_size; + while (len > 0) { + chunk_size = MIN(G_MAXSSIZE, len); + g_checksum_update(checksum, buf, chunk_size); + len -= chunk_size; + buf += chunk_size; + } + } else + g_checksum_update(checksum, data, len); +} + +static gboolean +purple_g_checksum_digest(PurpleCipherContext *context, GChecksumType type, + gsize len, guchar *digest, gsize *out_len) +{ + GChecksum *checksum; + const gssize required_length = g_checksum_type_get_length(type); + + checksum = purple_cipher_context_get_data(context); + + g_return_val_if_fail(len >= required_length, FALSE); + g_return_val_if_fail(checksum != NULL, FALSE); + + g_checksum_get_digest(checksum, digest, &len); + + purple_cipher_context_reset(context, NULL); + + if (out_len) + *out_len = len; + + return TRUE; +} +#endif + + /******************************************************************************* * MD5 ******************************************************************************/ #define MD5_HMAC_BLOCK_SIZE 64 +static size_t +md5_get_block_size(PurpleCipherContext *context) +{ + /* This does not change (in this case) */ + return MD5_HMAC_BLOCK_SIZE; +} + +#if GLIB_CHECK_VERSION(2,16,0) + +static void +md5_init(PurpleCipherContext *context, void *extra) +{ + purple_g_checksum_init(context, G_CHECKSUM_MD5); +} + +static void +md5_reset(PurpleCipherContext *context, void *extra) +{ + purple_g_checksum_reset(context, G_CHECKSUM_MD5); +} + +static gboolean +md5_digest(PurpleCipherContext *context, gsize in_len, guchar digest[16], + size_t *out_len) +{ + return purple_g_checksum_digest(context, G_CHECKSUM_MD5, in_len, + digest, out_len); +} + +static PurpleCipherOps MD5Ops = { + NULL, /* Set Option */ + NULL, /* Get Option */ + md5_init, /* init */ + md5_reset, /* reset */ + purple_g_checksum_uninit, /* uninit */ + NULL, /* set iv */ + purple_g_checksum_append, /* append */ + md5_digest, /* digest */ + NULL, /* encrypt */ + NULL, /* decrypt */ + NULL, /* set salt */ + NULL, /* get salt size */ + NULL, /* set key */ + NULL, /* get key size */ + NULL, /* set batch mode */ + NULL, /* get batch mode */ + md5_get_block_size, /* get block size */ + NULL /* set key with len */ +}; + +#else /* GLIB_CHECK_VERSION(2,16,0) */ + struct MD5Context { guint32 total[2]; guint32 state[4]; @@ -327,13 +468,6 @@ 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 */ @@ -355,6 +489,8 @@ NULL /* set key with len */ }; +#endif /* GLIB_CHECK_VERSION(2,16,0) */ + /******************************************************************************* * MD4 ******************************************************************************/ @@ -1613,6 +1749,61 @@ * SHA-1 ******************************************************************************/ #define SHA1_HMAC_BLOCK_SIZE 64 + +static size_t +sha1_get_block_size(PurpleCipherContext *context) +{ + /* This does not change (in this case) */ + return SHA1_HMAC_BLOCK_SIZE; +} + + +#if GLIB_CHECK_VERSION(2,16,0) + +static void +sha1_init(PurpleCipherContext *context, void *extra) +{ + purple_g_checksum_init(context, G_CHECKSUM_SHA1); +} + +static void +sha1_reset(PurpleCipherContext *context, void *extra) +{ + purple_g_checksum_reset(context, G_CHECKSUM_SHA1); +} + +static gboolean +sha1_digest(PurpleCipherContext *context, gsize in_len, guchar digest[20], + gsize *out_len) +{ + return purple_g_checksum_digest(context, G_CHECKSUM_SHA1, in_len, + digest, out_len); +} + +static PurpleCipherOps SHA1Ops = { + NULL, /* Set Option */ + NULL, /* Get Option */ + sha1_init, /* init */ + sha1_reset, /* reset */ + purple_g_checksum_uninit, /* uninit */ + NULL, /* set iv */ + purple_g_checksum_append, /* append */ + sha1_digest, /* digest */ + NULL, /* encrypt */ + NULL, /* decrypt */ + NULL, /* set salt */ + NULL, /* get salt size */ + NULL, /* set key */ + NULL, /* get key size */ + NULL, /* set batch mode */ + NULL, /* get batch mode */ + sha1_get_block_size, /* get block size */ + NULL /* set key with len */ +}; + +#else /* GLIB_CHECK_VERSION(2,16,0) */ + +#define SHA1_HMAC_BLOCK_SIZE 64 #define SHA1_ROTL(X,n) ((((X) << (n)) | ((X) >> (32-(n)))) & 0xFFFFFFFF) struct SHA1Context { @@ -1833,13 +2024,6 @@ 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 */ @@ -1861,10 +2045,65 @@ NULL /* set key with len */ }; +#endif /* GLIB_CHECK_VERSION(2,16,0) */ + /******************************************************************************* * SHA-256 ******************************************************************************/ #define SHA256_HMAC_BLOCK_SIZE 64 + +static size_t +sha256_get_block_size(PurpleCipherContext *context) +{ + /* This does not change (in this case) */ + return SHA256_HMAC_BLOCK_SIZE; +} + +#if GLIB_CHECK_VERSION(2,16,0) + +static void +sha256_init(PurpleCipherContext *context, void *extra) +{ + purple_g_checksum_init(context, G_CHECKSUM_SHA256); +} + +static void +sha256_reset(PurpleCipherContext *context, void *extra) +{ + purple_g_checksum_reset(context, G_CHECKSUM_SHA256); +} + +static gboolean +sha256_digest(PurpleCipherContext *context, gsize in_len, guchar digest[20], + gsize *out_len) +{ + return purple_g_checksum_digest(context, G_CHECKSUM_SHA256, in_len, + digest, out_len); +} + +static PurpleCipherOps SHA256Ops = { + NULL, /* Set Option */ + NULL, /* Get Option */ + sha256_init, /* init */ + sha256_reset, /* reset */ + purple_g_checksum_uninit, /* uninit */ + NULL, /* set iv */ + purple_g_checksum_append, /* append */ + sha256_digest, /* digest */ + NULL, /* encrypt */ + NULL, /* decrypt */ + NULL, /* set salt */ + NULL, /* get salt size */ + NULL, /* set key */ + NULL, /* get key size */ + NULL, /* set batch mode */ + NULL, /* get batch mode */ + sha256_get_block_size, /* get block size */ + NULL /* set key with len */ +}; + +#else /* GLIB_CHECK_VERSION(2,16,0) */ + #define SHA256_ROTR(X,n) ((((X) >> (n)) | ((X) << (32-(n)))) & 0xFFFFFFFF) static const guint32 sha256_K[64] = @@ -2088,13 +2327,6 @@ return TRUE; } -static size_t -sha256_get_block_size(PurpleCipherContext *context) -{ - /* This does not change (in this case) */ - return SHA256_HMAC_BLOCK_SIZE; -} - static PurpleCipherOps SHA256Ops = { sha256_set_opt, /* Set Option */ sha256_get_opt, /* Get Option */ @@ -2116,6 +2348,8 @@ NULL /* set key with len */ }; +#endif /* GLIB_CHECK_VERSION(2,16,0) */ + /******************************************************************************* * RC4 ******************************************************************************/ diff -r 6e250a050ced -r fc5508709e74 libpurple/internal.h --- a/libpurple/internal.h Sat Jul 04 22:27:37 2009 +0000 +++ b/libpurple/internal.h Sat Jul 04 23:13:23 2009 +0000 @@ -148,6 +148,14 @@ # endif #endif +#ifndef G_MAXSSIZE +# if GLIB_SIZEOF_LONG == 8 +# define G_MAXSSIZE ((gssize) 0x7fffffffffffffff) +# else +# define G_MAXSSIZE ((gssize) 0x7fffffff) +# endif +#endif + #if GLIB_CHECK_VERSION(2,6,0) # include #endif diff -r 6e250a050ced -r fc5508709e74 libpurple/tests/test_cipher.c --- a/libpurple/tests/test_cipher.c Sat Jul 04 22:27:37 2009 +0000 +++ b/libpurple/tests/test_cipher.c Sat Jul 04 23:13:23 2009 +0000 @@ -168,6 +168,11 @@ purple_cipher_context_destroy(context); \ } +START_TEST(test_sha1_empty_string) { + SHA1_TEST("", "da39a3ee5e6b4b0d3255bfef95601890afd80709"); +} +END_TEST + START_TEST(test_sha1_a) { SHA1_TEST("a", "86f7e437faa5a7fce15d1ddcb9eaeaea377667b8"); } @@ -190,6 +195,66 @@ END_TEST /****************************************************************************** + * SHA-256 Tests + *****************************************************************************/ +#define SHA256_TEST(data, digest) { \ + PurpleCipher *cipher = NULL; \ + PurpleCipherContext *context = NULL; \ + gchar cdigest[65]; \ + gboolean ret = FALSE; \ + \ + cipher = purple_ciphers_find_cipher("sha256"); \ + context = purple_cipher_context_new(cipher, NULL); \ + \ + if((data)) { \ + purple_cipher_context_append(context, (guchar *)(data), strlen((data))); \ + } else { \ + gint j; \ + guchar buff[1000]; \ + \ + memset(buff, 'a', 1000); \ + \ + for(j = 0; j < 1000; j++) \ + purple_cipher_context_append(context, buff, 1000); \ + } \ + \ + ret = purple_cipher_context_digest_to_str(context, sizeof(cdigest), cdigest, \ + NULL); \ + \ + fail_unless(ret == TRUE, NULL); \ + \ + fail_unless(strcmp((digest), cdigest) == 0, NULL); \ + \ + purple_cipher_context_destroy(context); \ +} + +START_TEST(test_sha256_empty_string) { + SHA256_TEST("", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); +} +END_TEST + +START_TEST(test_sha256_a) { + SHA256_TEST("a", "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb"); +} +END_TEST + +START_TEST(test_sha256_abc) { + SHA256_TEST("abc", "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"); +} +END_TEST + +START_TEST(test_sha256_abcd_gibberish) { + SHA256_TEST("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"); +} +END_TEST + +START_TEST(test_sha256_1000_as_1000_times) { + SHA256_TEST(NULL, "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0"); +} +END_TEST + +/****************************************************************************** * DES Tests *****************************************************************************/ #define DES_TEST(in, keyz, out, len) { \ @@ -726,12 +791,22 @@ /* sha1 tests */ tc = tcase_create("SHA1"); + tcase_add_test(tc, test_sha1_empty_string); tcase_add_test(tc, test_sha1_a); tcase_add_test(tc, test_sha1_abc); tcase_add_test(tc, test_sha1_abcd_gibberish); tcase_add_test(tc, test_sha1_1000_as_1000_times); suite_add_tcase(s, tc); + /* sha256 tests */ + tc = tcase_create("SHA256"); + tcase_add_test(tc, test_sha256_empty_string); + tcase_add_test(tc, test_sha256_a); + tcase_add_test(tc, test_sha256_abc); + tcase_add_test(tc, test_sha256_abcd_gibberish); + tcase_add_test(tc, test_sha256_1000_as_1000_times); + suite_add_tcase(s, tc); + /* des tests */ tc = tcase_create("DES"); tcase_add_test(tc, test_des_12345678);