re-factor SCRAM to be independent of the hash

This commit is contained in:
Steffen Jaeckel
2019-10-08 10:22:34 +02:00
committed by Dmitry Podgorny
parent 2ca2fcfedd
commit fc064bc883
7 changed files with 158 additions and 107 deletions

View File

@@ -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) {

View File

@@ -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,

View File

@@ -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) {

View File

@@ -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__ */

View File

@@ -19,6 +19,7 @@
#include <assert.h>
#include <string.h>
#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];
}
}

View File

@@ -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__ */

View File

@@ -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];
}