Mercurial > pidgin
diff src/cipher.c @ 12382:cfc808463763
[gaim-migrate @ 14688]
Reimplement HTTP Digest Authentication (RFC 2617) as part of the Cipher API
committer: Tailor Script <tailor@pidgin.im>
author | Richard Laager <rlaager@wiktel.com> |
---|---|
date | Wed, 07 Dec 2005 04:50:36 +0000 |
parents | 8004885fabbe |
children | 4e045668b9d0 |
line wrap: on
line diff
--- a/src/cipher.c Wed Dec 07 01:47:31 2005 +0000 +++ b/src/cipher.c Wed Dec 07 04:50:36 2005 +0000 @@ -1780,3 +1780,165 @@ return context->data; } + +gchar *gaim_cipher_http_digest_calculate_session_key( + const gchar *algorithm, + const gchar *username, + const gchar *realm, + const gchar *password, + const gchar *nonce, + const gchar *client_nonce) +{ + GaimCipher *cipher; + GaimCipherContext *context; + gchar hash[32]; /* We only support MD5. */ + + g_return_val_if_fail(username != NULL, NULL); + g_return_val_if_fail(realm != NULL, NULL); + g_return_val_if_fail(password != NULL, NULL); + g_return_val_if_fail(nonce != NULL, NULL); + + /* Check for a supported algorithm. */ + g_return_val_if_fail(algorithm == NULL || + *algorithm == '\0' || + strcasecmp(algorithm, "MD5") || + strcasecmp(algorithm, "MD5-sess"), NULL); + + cipher = gaim_ciphers_find_cipher("md5"); + g_return_val_if_fail(cipher != NULL, NULL); + + context = gaim_cipher_context_new(cipher, NULL); + + gaim_cipher_context_append(context, (guchar *)username, strlen(username)); + gaim_cipher_context_append(context, (guchar *)":", 1); + gaim_cipher_context_append(context, (guchar *)realm, strlen(realm)); + gaim_cipher_context_append(context, (guchar *)":", 1); + gaim_cipher_context_append(context, (guchar *)password, strlen(password)); + + if (algorithm != NULL && !strcasecmp(algorithm, "MD5-sess")) + { + guchar digest[16]; + + if (client_nonce == NULL) + { + gaim_cipher_context_destroy(context); + gaim_debug_error("cipher", "Required client_nonce missing for MD5-sess digest calculation."); + return NULL; + } + + gaim_cipher_context_digest(context, sizeof(digest), digest, NULL); + gaim_cipher_context_destroy(context); + + context = gaim_cipher_context_new(cipher, NULL); + gaim_cipher_context_append(context, digest, sizeof(digest)); + gaim_cipher_context_append(context, (guchar *)":", 1); + gaim_cipher_context_append(context, (guchar *)nonce, strlen(nonce)); + gaim_cipher_context_append(context, (guchar *)":", 1); + gaim_cipher_context_append(context, (guchar *)client_nonce, strlen(client_nonce)); + } + + gaim_cipher_context_digest_to_str(context, sizeof(hash), hash, NULL); + gaim_cipher_context_destroy(context); + + return g_strdup(hash); +} + +gchar *gaim_cipher_http_digest_calculate_response( + const gchar *algorithm, + const gchar *method, + const gchar *digest_uri, + const gchar *qop, + const gchar *hashed_entity, + size_t hashed_entity_len, + const gchar *nonce, + const gchar *nonce_count, + const gchar *client_nonce, + const gchar *session_key) +{ + GaimCipher *cipher; + GaimCipherContext *context; + gchar hash2[32]; /* We only support MD5. */ + + g_return_val_if_fail(method != NULL, NULL); + g_return_val_if_fail(digest_uri != NULL, NULL); + g_return_val_if_fail(nonce != NULL, NULL); + g_return_val_if_fail(session_key != NULL, NULL); + + /* Check for a supported algorithm. */ + g_return_val_if_fail(algorithm == NULL || + *algorithm == '\0' || + strcasecmp(algorithm, "MD5") || + strcasecmp(algorithm, "MD5-sess"), NULL); + + /* Check for a supported "quality of protection". */ + g_return_val_if_fail(qop == NULL || + *qop == '\0' || + strcasecmp(qop, "auth") || + strcasecmp(qop, "auth-int"), NULL); + + cipher = gaim_ciphers_find_cipher("md5"); + g_return_val_if_fail(cipher != NULL, NULL); + + context = gaim_cipher_context_new(cipher, NULL); + + gaim_cipher_context_append(context, (guchar *)method, strlen(method)); + gaim_cipher_context_append(context, (guchar *)":", 1); + gaim_cipher_context_append(context, (guchar *)digest_uri, strlen(digest_uri)); + + if (qop != NULL && !strcasecmp(qop, "auth-int")) + { + if (hashed_entity == NULL) + { + gaim_cipher_context_destroy(context); + gaim_debug_error("cipher", "Required hashed_entity missing for auth-int digest calculation."); + return NULL; + } + + gaim_cipher_context_append(context, (guchar *)":", 1); + gaim_cipher_context_append(context, (guchar *)hashed_entity, hashed_entity_len); + } + + gaim_cipher_context_digest_to_str(context, sizeof(hash2), hash2, NULL); + gaim_cipher_context_destroy(context); + + context = gaim_cipher_context_new(cipher, NULL); + gaim_cipher_context_append(context, (guchar *)session_key, strlen(session_key)); + gaim_cipher_context_append(context, (guchar *)":", 1); + gaim_cipher_context_append(context, (guchar *)nonce, strlen(nonce)); + gaim_cipher_context_append(context, (guchar *)":", 1); + + if (qop != NULL && *qop != '\0') + { + if (nonce_count == NULL) + { + gaim_cipher_context_destroy(context); + gaim_debug_error("cipher", "Required nonce_count missing for digest calculation."); + return NULL; + } + + if (client_nonce == NULL) + { + gaim_cipher_context_destroy(context); + gaim_debug_error("cipher", "Required client_nonce missing for digest calculation."); + return NULL; + } + + gaim_cipher_context_append(context, (guchar *)nonce_count, strlen(nonce_count)); + gaim_cipher_context_append(context, (guchar *)":", 1); + gaim_cipher_context_append(context, (guchar *)client_nonce, strlen(client_nonce)); + gaim_cipher_context_append(context, (guchar *)":", 1); + + if (qop != NULL) + gaim_cipher_context_append(context, (guchar *)qop, strlen(qop)); + else + gaim_cipher_context_append(context, (guchar *)"", 0); + + gaim_cipher_context_append(context, (guchar *)":", 1); + } + + gaim_cipher_context_append(context, (guchar *)hash2, strlen(hash2)); + gaim_cipher_context_digest_to_str(context, sizeof(hash2), hash2, NULL); + gaim_cipher_context_destroy(context); + + return g_strdup(hash2); +}