mech-scram-sha1.c revision 0a3769a4ef3afbbbd05df38f43ec7382fd65a2b6
/*
* SCRAM-SHA-1 SASL authentication, see RFC-5802
*
* Copyright (c) 2011 Florian Zeitz <florob@babelmonkeys.de>
*
* This software is released under the MIT license.
*/
#include "auth-common.h"
#include "base64.h"
#include "buffer.h"
#include "hmac.h"
#include "sha1.h"
#include "randgen.h"
#include "safe-memset.h"
#include "str.h"
#include "strfuncs.h"
#include "mech.h"
/* SCRAM hash iteration count. RFC says it SHOULD be at least 4096 */
#define SCRAM_ITERATE_COUNT 4096
/* s-nonce length */
#define SCRAM_SERVER_NONCE_LEN 64
struct scram_auth_request {
struct auth_request auth_request;
/* sent: */
const char *server_first_message;
unsigned char salt[16];
unsigned char salted_password[SHA1_RESULTLEN];
/* received: */
const char *gs2_cbind_flag;
const char *cnonce;
const char *snonce;
const char *client_first_message_bare;
const char *client_final_message_without_proof;
};
unsigned char result[SHA1_RESULTLEN])
{
struct hmac_context ctx;
unsigned char U[SHA1_RESULTLEN];
unsigned int j, k;
/* Calculate U1 */
hmac_final(&ctx, U);
/* Calculate U2 to Ui and Hi */
for (j = 2; j <= i; j++) {
hmac_update(&ctx, U, sizeof(U));
hmac_final(&ctx, U);
for (k = 0; k < SHA1_RESULTLEN; k++)
result[k] ^= U[k];
}
}
{
size_t i;
/* make sure snonce is printable and does not contain ',' */
for (i = 0; i < sizeof(snonce)-1; i++) {
if (snonce[i] == ',')
snonce[i] = '~';
}
}
{
struct hmac_context ctx;
const char *auth_message;
unsigned char server_key[SHA1_RESULTLEN];
unsigned char server_signature[SHA1_RESULTLEN];
sizeof(request->salted_password));
}
static const char *scram_unescape_username(const char *in)
{
if (in[0] == '=') {
else
return NULL;
in += 2;
} else {
}
}
}
const char **error_r)
{
const char *const *fields;
*error_r = "Invalid initial client message";
return FALSE;
}
switch (fields[0][0]) {
case 'p':
*error_r = "Channel binding not supported";
return FALSE;
case 'y':
case 'n':
break;
default:
*error_r = "Invalid GS2 header";
return FALSE;
}
*error_r = "authzid not supported";
return FALSE;
}
*error_r = "Mandatory extension(s) not supported";
return FALSE;
}
/* Unescape username */
const char *username =
*error_r = "Username escaping is invalid";
return FALSE;
}
return FALSE;
} else {
*error_r = "Invalid username field";
return FALSE;
}
else {
*error_r = "Invalid client nonce";
return FALSE;
}
/* This works only without channel binding support,
otherwise the GS2 header doesn't have a fixed length */
return TRUE;
}
{
struct hmac_context ctx;
const char *auth_message;
unsigned char client_key[SHA1_RESULTLEN];
unsigned char client_signature[SHA1_RESULTLEN];
unsigned char stored_key[SHA1_RESULTLEN];
size_t i;
/* FIXME: credentials should be SASLprepped UTF8 data here */
for (i = 0; i < sizeof(client_signature); i++)
client_signature[i] ^= client_key[i];
}
struct auth_request *auth_request)
{
struct scram_auth_request *request =
(struct scram_auth_request *)auth_request;
const char *server_final_message;
switch (result) {
case PASSDB_RESULT_OK:
"password mismatch");
} else {
}
break;
break;
default:
break;
}
}
const char **error_r)
{
unsigned int field_count;
if (field_count < 3) {
*error_r = "Invalid final client message";
return FALSE;
}
*error_r = "Invalid channel binding data";
return FALSE;
}
*error_r = "Wrong nonce";
return FALSE;
}
*error_r = "Invalid base64 encoding";
return FALSE;
}
*error_r = "Invalid ClientProof length";
return FALSE;
}
} else {
*error_r = "Invalid ClientProof";
return FALSE;
}
return TRUE;
}
const unsigned char *data,
{
struct scram_auth_request *request =
(struct scram_auth_request *)auth_request;
if (!request->client_first_message_bare) {
/* Received client-first-message */
return;
}
} else {
/* Received client-final-message */
return;
}
}
static struct auth_request *mech_scram_sha1_auth_new(void)
{
struct scram_auth_request *request;
return &request->auth_request;
}
const struct mech_module mech_scram_sha1 = {
"SCRAM-SHA-1",
};