diff --git a/src/auth.c b/src/auth.c index c45fc08..5954f65 100644 --- a/src/auth.c +++ b/src/auth.c @@ -85,7 +85,7 @@ static int _handle_digestmd5_rspauth(xmpp_conn_t *const conn, static int _handle_scram_sha1_challenge(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza, void *const userdata); -static char *_make_scram_sha1_init_msg(xmpp_conn_t *const conn); +static char *_make_scram_init_msg(xmpp_conn_t *const conn); static int _handle_missing_features_sasl(xmpp_conn_t *const conn, void *const userdata); @@ -430,6 +430,11 @@ static int _handle_digestmd5_rspauth(xmpp_conn_t *const conn, return 1; } +struct scram_user_data { + char *scram_init; + const struct hash_alg *alg; +}; + /* handle the challenge phase of SCRAM-SHA-1 auth */ static int _handle_scram_sha1_challenge(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza, @@ -437,14 +442,16 @@ static int _handle_scram_sha1_challenge(xmpp_conn_t *const conn, { char *text; char *response; - xmpp_stanza_t *auth, *authdata; + xmpp_stanza_t *auth; + xmpp_stanza_t *authdata; const char *name; + const char *scram_name; char *challenge; - char *scram_init = (char *)userdata; + struct scram_user_data *scram_ctx = (struct scram_user_data *)userdata; name = xmpp_stanza_get_name(stanza); - xmpp_debug(conn->ctx, "xmpp", - "handle SCRAM-SHA-1 (challenge) called for %s", name); + xmpp_debug(conn->ctx, "xmpp", "handle %s (challenge) called for %s", + scram_ctx->alg->scram_name, name); if (strcmp(name, "challenge") == 0) { text = xmpp_stanza_get_text(stanza); @@ -456,8 +463,8 @@ static int _handle_scram_sha1_challenge(xmpp_conn_t *const conn, if (!challenge) goto err; - response = sasl_scram_sha1(conn->ctx, challenge, scram_init, conn->jid, - conn->pass); + response = sasl_scram(conn->ctx, scram_ctx->alg, challenge, + scram_ctx->scram_init, conn->jid, conn->pass); xmpp_free(conn->ctx, challenge); if (!response) goto err; @@ -481,8 +488,10 @@ static int _handle_scram_sha1_challenge(xmpp_conn_t *const conn, xmpp_stanza_release(auth); } else { - xmpp_free(conn->ctx, scram_init); - return _handle_sasl_result(conn, stanza, "SCRAM-SHA-1"); + scram_name = scram_ctx->alg->scram_name; + xmpp_free(conn->ctx, scram_ctx->scram_init); + xmpp_free(conn->ctx, scram_ctx); + return _handle_sasl_result(conn, stanza, (void *)scram_name); } return 1; @@ -492,12 +501,13 @@ err_release_auth: err_free_response: xmpp_free(conn->ctx, response); err: - xmpp_free(conn->ctx, scram_init); + xmpp_free(conn->ctx, scram_ctx->scram_init); + xmpp_free(conn->ctx, scram_ctx); disconnect_mem_error(conn); return 0; } -static char *_make_scram_sha1_init_msg(xmpp_conn_t *const conn) +static char *_make_scram_init_msg(xmpp_conn_t *const conn) { xmpp_ctx_t *ctx = conn->ctx; size_t message_len; @@ -559,8 +569,8 @@ static void _auth(xmpp_conn_t *const conn) { xmpp_stanza_t *auth; xmpp_stanza_t *authdata; + struct scram_user_data *scram_ctx; char *authid; - char *scram_init; char *str; int anonjid; @@ -632,25 +642,30 @@ static void _auth(xmpp_conn_t *const conn) xmpp_error(conn->ctx, "auth", "No node in JID, and SASL ANONYMOUS unsupported."); xmpp_disconnect(conn); - } else if (conn->sasl_support & SASL_MASK_SCRAMSHA1) { - auth = _make_sasl_auth(conn, "SCRAM-SHA-1"); + } else if (conn->sasl_support & SASL_MASK_SCRAM) { + scram_ctx = xmpp_alloc(conn->ctx, sizeof(*scram_ctx)); + scram_ctx->alg = &scram_sha1; + auth = _make_sasl_auth(conn, scram_ctx->alg->scram_name); if (!auth) { disconnect_mem_error(conn); return; } /* don't free scram_init on success */ - scram_init = _make_scram_sha1_init_msg(conn); - if (!scram_init) { + scram_ctx->scram_init = _make_scram_init_msg(conn); + if (!scram_ctx->scram_init) { + xmpp_free(conn->ctx, scram_ctx); xmpp_stanza_release(auth); disconnect_mem_error(conn); return; } - str = xmpp_base64_encode(conn->ctx, (unsigned char *)scram_init, - strlen(scram_init)); + str = xmpp_base64_encode(conn->ctx, + (unsigned char *)scram_ctx->scram_init, + strlen(scram_ctx->scram_init)); if (!str) { - xmpp_free(conn->ctx, scram_init); + xmpp_free(conn->ctx, scram_ctx->scram_init); + xmpp_free(conn->ctx, scram_ctx); xmpp_stanza_release(auth); disconnect_mem_error(conn); return; @@ -659,7 +674,8 @@ static void _auth(xmpp_conn_t *const conn) authdata = xmpp_stanza_new(conn->ctx); if (!authdata) { xmpp_free(conn->ctx, str); - xmpp_free(conn->ctx, scram_init); + xmpp_free(conn->ctx, scram_ctx->scram_init); + xmpp_free(conn->ctx, scram_ctx); xmpp_stanza_release(auth); disconnect_mem_error(conn); return; @@ -670,13 +686,13 @@ static void _auth(xmpp_conn_t *const conn) xmpp_stanza_release(authdata); handler_add(conn, _handle_scram_sha1_challenge, XMPP_NS_SASL, NULL, - NULL, (void *)scram_init); + NULL, (void *)scram_ctx); xmpp_send(conn, auth); xmpp_stanza_release(auth); /* SASL SCRAM-SHA-1 was tried, unset flag */ - conn->sasl_support &= ~SASL_MASK_SCRAMSHA1; + conn->sasl_support &= ~scram_ctx->alg->mask; } else if (conn->sasl_support & SASL_MASK_DIGESTMD5) { auth = _make_sasl_auth(conn, "DIGEST-MD5"); if (!auth) { diff --git a/src/common.h b/src/common.h index 1ce200c..b9ae58c 100644 --- a/src/common.h +++ b/src/common.h @@ -137,6 +137,8 @@ struct _xmpp_handlist_t { #define SASL_MASK_ANONYMOUS (1 << 2) #define SASL_MASK_SCRAMSHA1 (1 << 3) +#define SASL_MASK_SCRAM (SASL_MASK_SCRAMSHA1) + enum { XMPP_PORT_CLIENT = 5222, XMPP_PORT_CLIENT_LEGACY_SSL = 5223, diff --git a/src/sasl.c b/src/sasl.c index 6499ef8..a41353a 100644 --- a/src/sasl.c +++ b/src/sasl.c @@ -380,11 +380,12 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx, } /** generate auth response string for the SASL SCRAM-SHA-1 mechanism */ -char *sasl_scram_sha1(xmpp_ctx_t *ctx, - const char *challenge, - const char *first_bare, - const char *jid, - const char *password) +char *sasl_scram(xmpp_ctx_t *ctx, + const struct hash_alg *alg, + const char *challenge, + const char *first_bare, + const char *jid, + const char *password) { uint8_t key[SHA1_DIGEST_SIZE]; uint8_t sign[SHA1_DIGEST_SIZE]; @@ -404,7 +405,6 @@ char *sasl_scram_sha1(xmpp_ctx_t *ctx, char *result = NULL; size_t response_len; size_t auth_len; - int j; tmp = xmpp_strdup(ctx, challenge); if (!tmp) { @@ -449,12 +449,10 @@ char *sasl_scram_sha1(xmpp_ctx_t *ctx, xmpp_snprintf(auth, auth_len, "%s,%s,%s", first_bare + 3, challenge, response); - SCRAM_SHA1_ClientKey((uint8_t *)password, strlen(password), (uint8_t *)sval, - sval_len, (uint32_t)ival, key); - SCRAM_SHA1_ClientSignature(key, (uint8_t *)auth, strlen(auth), sign); - for (j = 0; j < SHA1_DIGEST_SIZE; j++) { - sign[j] ^= key[j]; - } + SCRAM_ClientKey(alg, (uint8_t *)password, strlen(password), (uint8_t *)sval, + sval_len, (uint32_t)ival, key); + SCRAM_ClientSignature(alg, key, (uint8_t *)auth, strlen(auth), sign); + SCRAM_ClientProof(alg, sign, key, sign); sign_b64 = xmpp_base64_encode(ctx, sign, sizeof(sign)); if (!sign_b64) { diff --git a/src/sasl.h b/src/sasl.h index 6310cf0..76ee799 100644 --- a/src/sasl.h +++ b/src/sasl.h @@ -17,6 +17,7 @@ #define __LIBSTROPHE_SASL_H__ #include "strophe.h" +#include "scram.h" /** low-level sasl routines */ @@ -25,10 +26,11 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge, const char *jid, const char *password); -char *sasl_scram_sha1(xmpp_ctx_t *ctx, - const char *challenge, - const char *first_bare, - const char *jid, - const char *password); +char *sasl_scram(xmpp_ctx_t *ctx, + const struct hash_alg *alg, + const char *challenge, + const char *first_bare, + const char *jid, + const char *password); #endif /* _LIBXMPP_SASL_H__ */ diff --git a/src/scram.c b/src/scram.c index 2530dd4..541d8ee 100644 --- a/src/scram.c +++ b/src/scram.c @@ -19,6 +19,7 @@ #include #include +#include "common.h" #include "sha1.h" #include "ostypes.h" @@ -29,25 +30,39 @@ static const uint8_t ipad = 0x36; static const uint8_t opad = 0x5C; -static void crypto_HMAC_SHA1(const uint8_t *key, - size_t key_len, - const uint8_t *text, - size_t len, - uint8_t *digest) +const struct hash_alg scram_sha1 = { + "SCRAM-SHA-1", + SASL_MASK_SCRAMSHA1, + SHA1_DIGEST_SIZE, + (void (*)(const uint8_t *, size_t, uint8_t *))crypto_SHA1, + (void (*)(void *))crypto_SHA1_Init, + (void (*)(void *, const uint8_t *, size_t))crypto_SHA1_Update, + (void (*)(void *, uint8_t *))crypto_SHA1_Final}; + +union common_hash_ctx { + SHA1_CTX sha1; +}; + +static void crypto_HMAC(const struct hash_alg *alg, + const uint8_t *key, + size_t key_len, + const uint8_t *text, + size_t len, + uint8_t *digest) { uint8_t key_pad[HMAC_BLOCK_SIZE]; uint8_t key_ipad[HMAC_BLOCK_SIZE]; uint8_t key_opad[HMAC_BLOCK_SIZE]; uint8_t sha_digest[SHA1_DIGEST_SIZE]; int i; - SHA1_CTX ctx; + union common_hash_ctx ctx; memset(key_pad, 0, sizeof(key_pad)); if (key_len <= HMAC_BLOCK_SIZE) { memcpy(key_pad, key, key_len); } else { /* according to RFC2104 */ - crypto_SHA1(key, key_len, key_pad); + alg->hash(key, key_len, key_pad); } for (i = 0; i < HMAC_BLOCK_SIZE; i++) { @@ -55,25 +70,26 @@ static void crypto_HMAC_SHA1(const uint8_t *key, key_opad[i] = key_pad[i] ^ opad; } - crypto_SHA1_Init(&ctx); - crypto_SHA1_Update(&ctx, key_ipad, HMAC_BLOCK_SIZE); - crypto_SHA1_Update(&ctx, text, len); - crypto_SHA1_Final(&ctx, sha_digest); + alg->init((void *)&ctx); + alg->update((void *)&ctx, key_ipad, HMAC_BLOCK_SIZE); + alg->update((void *)&ctx, text, len); + alg->final((void *)&ctx, sha_digest); - crypto_SHA1_Init(&ctx); - crypto_SHA1_Update(&ctx, key_opad, HMAC_BLOCK_SIZE); - crypto_SHA1_Update(&ctx, sha_digest, SHA1_DIGEST_SIZE); - crypto_SHA1_Final(&ctx, digest); + alg->init((void *)&ctx); + alg->update((void *)&ctx, key_opad, HMAC_BLOCK_SIZE); + alg->update((void *)&ctx, sha_digest, alg->digest_size); + alg->final((void *)&ctx, digest); } -static void SCRAM_SHA1_Hi(const uint8_t *text, - size_t len, - const uint8_t *salt, - size_t salt_len, - uint32_t i, - uint8_t *digest) +static void SCRAM_Hi(const struct hash_alg *alg, + const uint8_t *text, + size_t len, + const uint8_t *salt, + size_t salt_len, + uint32_t i, + uint8_t *digest) { - int k; + size_t k; uint32_t j; uint8_t tmp[128]; @@ -82,7 +98,7 @@ static void SCRAM_SHA1_Hi(const uint8_t *text, /* assume salt + INT(1) isn't longer than sizeof(tmp) */ assert(salt_len <= sizeof(tmp) - sizeof(int1)); - memset(digest, 0, SHA1_DIGEST_SIZE); + memset(digest, 0, alg->digest_size); if (i == 0) { return; } @@ -91,50 +107,53 @@ static void SCRAM_SHA1_Hi(const uint8_t *text, memcpy(&tmp[salt_len], int1, sizeof(int1)); /* 'text' for Hi is a 'key' for HMAC */ - crypto_HMAC_SHA1(text, len, tmp, salt_len + sizeof(int1), digest); - memcpy(tmp, digest, SHA1_DIGEST_SIZE); + crypto_HMAC(alg, text, len, tmp, salt_len + sizeof(int1), digest); + memcpy(tmp, digest, alg->digest_size); for (j = 1; j < i; j++) { - crypto_HMAC_SHA1(text, len, tmp, SHA1_DIGEST_SIZE, tmp); - for (k = 0; k < SHA1_DIGEST_SIZE; k++) { + crypto_HMAC(alg, text, len, tmp, alg->digest_size, tmp); + for (k = 0; k < alg->digest_size; k++) { digest[k] ^= tmp[k]; } } } -void SCRAM_SHA1_ClientKey(const uint8_t *password, - size_t len, - const uint8_t *salt, - size_t salt_len, - uint32_t i, - uint8_t *key) +void SCRAM_ClientKey(const struct hash_alg *alg, + const uint8_t *password, + size_t len, + const uint8_t *salt, + size_t salt_len, + uint32_t i, + uint8_t *key) { uint8_t salted[SHA1_DIGEST_SIZE]; /* XXX: Normalize(password) is omitted */ - SCRAM_SHA1_Hi(password, len, salt, salt_len, i, salted); - crypto_HMAC_SHA1(salted, SHA1_DIGEST_SIZE, (uint8_t *)"Client Key", - strlen("Client Key"), key); + SCRAM_Hi(alg, password, len, salt, salt_len, i, salted); + crypto_HMAC(alg, salted, alg->digest_size, (uint8_t *)"Client Key", + strlen("Client Key"), key); } -void SCRAM_SHA1_ClientSignature(const uint8_t *ClientKey, - const uint8_t *AuthMessage, - size_t len, - uint8_t *sign) +void SCRAM_ClientSignature(const struct hash_alg *alg, + const uint8_t *ClientKey, + const uint8_t *AuthMessage, + size_t len, + uint8_t *sign) { uint8_t stored[SHA1_DIGEST_SIZE]; - crypto_SHA1(ClientKey, SHA1_DIGEST_SIZE, stored); - crypto_HMAC_SHA1(stored, SHA1_DIGEST_SIZE, AuthMessage, len, sign); + alg->hash(ClientKey, alg->digest_size, stored); + crypto_HMAC(alg, stored, alg->digest_size, AuthMessage, len, sign); } -void SCRAM_SHA1_ClientProof(const uint8_t *ClientKey, - const uint8_t *ClientSignature, - uint8_t *proof) +void SCRAM_ClientProof(const struct hash_alg *alg, + const uint8_t *ClientKey, + const uint8_t *ClientSignature, + uint8_t *proof) { - int i; - for (i = 0; i < SHA1_DIGEST_SIZE; i++) { + size_t i; + for (i = 0; i < alg->digest_size; i++) { proof[i] = ClientKey[i] ^ ClientSignature[i]; } } diff --git a/src/scram.h b/src/scram.h index b99a8d6..f744ef8 100644 --- a/src/scram.h +++ b/src/scram.h @@ -19,22 +19,35 @@ /* make sure the stdint.h types are available */ #include "ostypes.h" -#include "sha1.h" +struct hash_alg { + const char *scram_name; + int mask; + size_t digest_size; + void (*hash)(const uint8_t *, size_t, uint8_t *); + void (*init)(void *); + void (*update)(void *, const uint8_t *, size_t); + void (*final)(void *, uint8_t *); +}; -void SCRAM_SHA1_ClientKey(const uint8_t *password, - size_t len, - const uint8_t *salt, - size_t salt_len, - uint32_t i, - uint8_t *key); +extern const struct hash_alg scram_sha1; -void SCRAM_SHA1_ClientSignature(const uint8_t *ClientKey, - const uint8_t *AuthMessage, - size_t len, - uint8_t *sign); +void SCRAM_ClientKey(const struct hash_alg *alg, + const uint8_t *password, + size_t len, + const uint8_t *salt, + size_t salt_len, + uint32_t i, + uint8_t *key); -void SCRAM_SHA1_ClientProof(const uint8_t *ClientKey, - const uint8_t *ClientSignature, - uint8_t *proof); +void SCRAM_ClientSignature(const struct hash_alg *alg, + const uint8_t *ClientKey, + const uint8_t *AuthMessage, + size_t len, + uint8_t *sign); + +void SCRAM_ClientProof(const struct hash_alg *alg, + const uint8_t *ClientKey, + const uint8_t *ClientSignature, + uint8_t *proof); #endif /* __LIBSTROPHE_SCRAM_H__ */ diff --git a/tests/test_scram.c b/tests/test_scram.c index abb1159..3826aee 100644 --- a/tests/test_scram.c +++ b/tests/test_scram.c @@ -67,9 +67,9 @@ static void test_df(void) printf("Derivation function tests (SCRAM_SHA1_Hi).\n"); for (i = 0; i < ARRAY_SIZE(df_vectors); ++i) { printf("Test #%d: ", (int)i + 1); - SCRAM_SHA1_Hi((uint8_t *)df_vectors[i].P, df_vectors[i].P_len, - (uint8_t *)df_vectors[i].S, df_vectors[i].S_len, - df_vectors[i].c, dk); + SCRAM_Hi(&scram_sha1, (uint8_t *)df_vectors[i].P, df_vectors[i].P_len, + (uint8_t *)df_vectors[i].S, df_vectors[i].S_len, + df_vectors[i].c, dk); s = test_bin_to_hex(dk, sizeof(dk)); COMPARE(df_vectors[i].DK, s); printf("ok\n"); @@ -119,10 +119,11 @@ static void test_scram(void) scram_vectors[i].challenge, scram_vectors[i].response); test_hex_to_bin(scram_vectors[i].salt, salt, &salt_len); - SCRAM_SHA1_ClientKey((uint8_t *)scram_vectors[i].password, - strlen(scram_vectors[i].password), salt, salt_len, - scram_vectors[i].i, key); - SCRAM_SHA1_ClientSignature(key, (uint8_t *)auth, strlen(auth), sign); + SCRAM_ClientKey(&scram_sha1, (uint8_t *)scram_vectors[i].password, + strlen(scram_vectors[i].password), salt, salt_len, + scram_vectors[i].i, key); + SCRAM_ClientSignature(&scram_sha1, key, (uint8_t *)auth, strlen(auth), + sign); for (j = 0; j < SHA1_DIGEST_SIZE; j++) { sign[j] ^= key[j]; }