static void calculate_M( SRP_HashAlgorithm alg, NGConstant *ng, unsigned char * dest, const char * I, const BIGNUM * s, const BIGNUM * A, const BIGNUM * B, const unsigned char * K ) { unsigned char H_N[ SHA256_DIGEST_LENGTH ]; unsigned char H_g[ SHA256_DIGEST_LENGTH ]; unsigned char H_I[ SHA256_DIGEST_LENGTH ]; unsigned char H_xor[ SHA256_DIGEST_LENGTH ]; HashCTX ctx; int i = 0; int hash_len = hash_length(alg); hash_num( alg, ng->N, H_N ); hash_num( alg, ng->g, H_g ); hash(alg, (const unsigned char *)I, strlen(I), H_I); for (i=0; i < hash_len; i++ ) H_xor[i] = H_N[i] ^ H_g[i]; hash_init( alg, &ctx ); hash_update( alg, &ctx, H_xor, hash_len ); hash_update( alg, &ctx, H_I, hash_len ); update_hash_n( alg, &ctx, s ); update_hash_n( alg, &ctx, A ); update_hash_n( alg, &ctx, B ); hash_update( alg, &ctx, K, hash_len ); hash_final( alg, &ctx, dest ); }
static SRP_Result calculate_M(SRP_HashAlgorithm alg, NGConstant *ng, unsigned char *dest, const char *I, const unsigned char *s_bytes, size_t s_len, const mpz_t A, const mpz_t B, const unsigned char *K) { unsigned char H_N[SHA512_DIGEST_LENGTH]; unsigned char H_g[SHA512_DIGEST_LENGTH]; unsigned char H_I[SHA512_DIGEST_LENGTH]; unsigned char H_xor[SHA512_DIGEST_LENGTH]; HashCTX ctx; size_t i = 0; size_t hash_len = hash_length(alg); if (!hash_num(alg, ng->N, H_N)) return SRP_ERR; if (!hash_num(alg, ng->g, H_g)) return SRP_ERR; hash(alg, (const unsigned char *)I, strlen(I), H_I); for (i = 0; i < hash_len; i++) H_xor[i] = H_N[i] ^ H_g[i]; hash_init(alg, &ctx); hash_update(alg, &ctx, H_xor, hash_len); hash_update(alg, &ctx, H_I, hash_len); hash_update(alg, &ctx, s_bytes, s_len); if (!update_hash_n(alg, &ctx, A)) return SRP_ERR; if (!update_hash_n(alg, &ctx, B)) return SRP_ERR; hash_update(alg, &ctx, K, hash_len); hash_final(alg, &ctx, dest); return SRP_OK; }
/* * Hash a term. */ extern hash_t hash_term(term_t t) { switch (type(t)) { case VAR: return hash_var(var(t)); case NIL: return hash_nil(); case BOOL: return hash_bool(boolean(t)); case NUM: return hash_num(num(t)); case ATOM: return hash_atom(atom(t)); case STR: return hash_string(string(t)); case FOREIGN: return hash_foreign(foreign(t)); case FUNC: return hash_func(func(t)); default: { hash_t dummy = HASH(0, 0); return dummy; } } }
/* Out: bytes_B, len_B. * * On failure, bytes_B will be set to NULL and len_B will be set to 0 */ struct SRPVerifier * srp_verifier_new( SRP_HashAlgorithm alg, SRP_NGType ng_type, const char * username, const unsigned char * bytes_s, int len_s, const unsigned char * bytes_v, int len_v, const unsigned char * bytes_A, int len_A, const unsigned char ** bytes_B, int * len_B, const char * n_hex, const char * g_hex ) { BIGNUM *s = BN_bin2bn(bytes_s, len_s, NULL); BIGNUM *v = BN_bin2bn(bytes_v, len_v, NULL); BIGNUM *A = BN_bin2bn(bytes_A, len_A, NULL); BIGNUM *u = 0; BIGNUM *B = BN_new(); BIGNUM *S = BN_new(); BIGNUM *b = BN_new(); BIGNUM *k = 0; BIGNUM *tmp1 = BN_new(); BIGNUM *tmp2 = BN_new(); BN_CTX *ctx = BN_CTX_new(); int ulen = strlen(username) + 1; NGConstant *ng = new_ng( ng_type, n_hex, g_hex ); struct SRPVerifier *ver = 0; *len_B = 0; *bytes_B = 0; if( !s || !v || !A || !B || !S || !b || !tmp1 || !tmp2 || !ctx || !ng ) goto cleanup_and_exit; ver = (struct SRPVerifier *) malloc( sizeof(struct SRPVerifier) ); if (!ver) goto cleanup_and_exit; init_random(); /* Only happens once */ ver->username = (char *) malloc( ulen ); ver->hash_alg = alg; ver->ng = ng; if (!ver->username) { free(ver); ver = 0; goto cleanup_and_exit; } memcpy( (char*)ver->username, username, ulen ); ver->authenticated = 0; /* SRP-6a safety check */ BN_mod(tmp1, A, ng->N, ctx); if ( !BN_is_zero(tmp1) ) { BN_rand(b, 256, -1, 0); k = H_nn(alg, ng->N, ng->g); /* B = kv + g^b */ BN_mul(tmp1, k, v, ctx); BN_mod_exp(tmp2, ng->g, b, ng->N, ctx); BN_add(B, tmp1, tmp2); u = H_nn(alg, A, B); /* S = (A *(v^u)) ^ b */ BN_mod_exp(tmp1, v, u, ng->N, ctx); BN_mul(tmp2, A, tmp1, ctx); BN_mod_exp(S, tmp2, b, ng->N, ctx); hash_num(alg, S, ver->session_key); calculate_M( alg, ng, ver->M, username, s, A, B, ver->session_key ); calculate_H_AMK( alg, ver->H_AMK, A, ver->M, ver->session_key ); *len_B = BN_num_bytes(B); *bytes_B = malloc( *len_B ); if( !*bytes_B ) { free( (void*) ver->username ); free( ver ); ver = 0; *len_B = 0; goto cleanup_and_exit; } BN_bn2bin( B, (unsigned char *) *bytes_B ); ver->bytes_B = *bytes_B; } cleanup_and_exit: BN_free(s); BN_free(v); BN_free(A); if (u) BN_free(u); if (k) BN_free(k); BN_free(B); BN_free(S); BN_free(b); BN_free(tmp1); BN_free(tmp2); BN_CTX_free(ctx); return ver; }
/* Output: bytes_M. Buffer length is SHA512_DIGEST_LENGTH */ void srp_user_process_challenge( struct SRPUser * usr, const unsigned char * bytes_s, int len_s, const unsigned char * bytes_B, int len_B, const unsigned char ** bytes_M, int * len_M ) { BIGNUM *s = BN_bin2bn(bytes_s, len_s, NULL); BIGNUM *B = BN_bin2bn(bytes_B, len_B, NULL); BIGNUM *u = 0; BIGNUM *x = 0; BIGNUM *k = 0; BIGNUM *v = BN_new(); BIGNUM *tmp1 = BN_new(); BIGNUM *tmp2 = BN_new(); BIGNUM *tmp3 = BN_new(); BN_CTX *ctx = BN_CTX_new(); *len_M = 0; *bytes_M = 0; if( !s || !B || !v || !tmp1 || !tmp2 || !tmp3 || !ctx ) goto cleanup_and_exit; u = H_nn(usr->hash_alg, usr->A, B); if (!u) goto cleanup_and_exit; x = calculate_x( usr->hash_alg, s, usr->username, usr->password, usr->password_len ); if (!x) goto cleanup_and_exit; k = H_nn(usr->hash_alg, usr->ng->N, usr->ng->g); if (!k) goto cleanup_and_exit; /* SRP-6a safety check */ if ( !BN_is_zero(B) && !BN_is_zero(u) ) { BN_mod_exp(v, usr->ng->g, x, usr->ng->N, ctx); /* S = (B - k*(g^x)) ^ (a + ux) */ BN_mul(tmp1, u, x, ctx); BN_add(tmp2, usr->a, tmp1); /* tmp2 = (a + ux) */ BN_mod_exp(tmp1, usr->ng->g, x, usr->ng->N, ctx); BN_mul(tmp3, k, tmp1, ctx); /* tmp3 = k*(g^x) */ BN_sub(tmp1, B, tmp3); /* tmp1 = (B - K*(g^x)) */ BN_mod_exp(usr->S, tmp1, tmp2, usr->ng->N, ctx); hash_num(usr->hash_alg, usr->S, usr->session_key); calculate_M( usr->hash_alg, usr->ng, usr->M, usr->username, s, usr->A, B, usr->session_key ); calculate_H_AMK( usr->hash_alg, usr->H_AMK, usr->A, usr->M, usr->session_key ); *bytes_M = usr->M; if (len_M) *len_M = hash_length( usr->hash_alg ); } else { *bytes_M = NULL; if (len_M) *len_M = 0; } cleanup_and_exit: BN_free(s); BN_free(B); BN_free(u); BN_free(x); BN_free(k); BN_free(v); BN_free(tmp1); BN_free(tmp2); BN_free(tmp3); BN_CTX_free(ctx); }
/* Output: bytes_M. Buffer length is SHA512_DIGEST_LENGTH */ void srp_user_process_challenge(struct SRPUser *usr, const unsigned char *bytes_s, size_t len_s, const unsigned char *bytes_B, size_t len_B, unsigned char **bytes_M, size_t *len_M) { mpz_t B; mpz_init(B); mpz_from_bin(bytes_B, len_B, B); mpz_t u; mpz_init(u); mpz_t x; mpz_init(x); mpz_t k; mpz_init(k); mpz_t v; mpz_init(v); mpz_t tmp1; mpz_init(tmp1); mpz_t tmp2; mpz_init(tmp2); mpz_t tmp3; mpz_init(tmp3); mpz_t tmp4; mpz_init(tmp4); // clang-format on if(len_M) *len_M = 0; *bytes_M = 0; if (!H_nn(u, usr->hash_alg, usr->ng->N, usr->A, B)) goto cleanup_and_exit; srp_dbg_num(u, "Client calculated u: "); if (!calculate_x(x, usr->hash_alg, bytes_s, len_s, usr->username_verifier, usr->password, usr->password_len)) goto cleanup_and_exit; srp_dbg_num(x, "Client calculated x: "); if (!H_nn(k, usr->hash_alg, usr->ng->N, usr->ng->N, usr->ng->g)) goto cleanup_and_exit; /* SRP-6a safety check */ if (mpz_sgn(B) != 0 && mpz_sgn(u) != 0) { mpz_powm(v, usr->ng->g, x, usr->ng->N); srp_dbg_num(v, "Client calculated v: "); // clang-format off /* S = (B - k*(g^x)) ^ (a + ux) */ mpz_mul(tmp1, u, x); mpz_add(tmp2, usr->a, tmp1); /* tmp2 = (a + ux) */ mpz_powm(tmp1, usr->ng->g, x, usr->ng->N); /* tmp1 = g^x */ mpz_mulm(tmp3, k, tmp1, usr->ng->N, tmp4); /* tmp3 = k*(g^x) */ mpz_subm(tmp1, B, tmp3, usr->ng->N, tmp4); /* tmp1 = (B - K*(g^x)) */ mpz_powm(usr->S, tmp1, tmp2, usr->ng->N); // clang-format on if (!hash_num(usr->hash_alg, usr->S, usr->session_key)) goto cleanup_and_exit; if (!calculate_M(usr->hash_alg, usr->ng, usr->M, usr->username, bytes_s, len_s, usr->A, B, usr->session_key)) goto cleanup_and_exit; if (!calculate_H_AMK(usr->hash_alg, usr->H_AMK, usr->A, usr->M, usr->session_key)) goto cleanup_and_exit; *bytes_M = usr->M; if (len_M) *len_M = hash_length(usr->hash_alg); } else { *bytes_M = NULL; if (len_M) *len_M = 0; } cleanup_and_exit: mpz_clear(B); mpz_clear(u); mpz_clear(x); mpz_clear(k); mpz_clear(v); mpz_clear(tmp1); mpz_clear(tmp2); mpz_clear(tmp3); mpz_clear(tmp4); }
/* Out: bytes_B, len_B. * * On failure, bytes_B will be set to NULL and len_B will be set to 0 */ struct SRPVerifier *srp_verifier_new(SRP_HashAlgorithm alg, SRP_NGType ng_type, const char *username, const unsigned char *bytes_s, size_t len_s, const unsigned char *bytes_v, size_t len_v, const unsigned char *bytes_A, size_t len_A, const unsigned char *bytes_b, size_t len_b, unsigned char **bytes_B, size_t *len_B, const char *n_hex, const char *g_hex ) { mpz_t v; mpz_init(v); mpz_from_bin(bytes_v, len_v, v); mpz_t A; mpz_init(A); mpz_from_bin(bytes_A, len_A, A); mpz_t u; mpz_init(u); mpz_t B; mpz_init(B); mpz_t S; mpz_init(S); mpz_t b; mpz_init(b); mpz_t k; mpz_init(k); mpz_t tmp1; mpz_init(tmp1); mpz_t tmp2; mpz_init(tmp2); mpz_t tmp3; mpz_init(tmp3); // clang-format on size_t ulen = strlen(username) + 1; NGConstant *ng = new_ng(ng_type, n_hex, g_hex); struct SRPVerifier *ver = 0; *len_B = 0; *bytes_B = 0; if (!ng) goto cleanup_and_exit; ver = (struct SRPVerifier *)srp_alloc(sizeof(struct SRPVerifier)); if (!ver) goto cleanup_and_exit; if (init_random() != SRP_OK) { /* Only happens once */ srp_free(ver); ver = 0; goto cleanup_and_exit; } ver->username = (char *)srp_alloc(ulen); ver->hash_alg = alg; ver->ng = ng; if (!ver->username) { srp_free(ver); ver = 0; goto cleanup_and_exit; } memcpy((char *)ver->username, username, ulen); ver->authenticated = 0; /* SRP-6a safety check */ mpz_mod(tmp1, A, ng->N); if (mpz_sgn(tmp1) != 0) { if (bytes_b) { mpz_from_bin(bytes_b, len_b, b); } else { if (!mpz_fill_random(b)) goto ver_cleanup_and_exit; } if (!H_nn(k, alg, ng->N, ng->N, ng->g)) goto ver_cleanup_and_exit; /* B = kv + g^b */ mpz_mulm(tmp1, k, v, ng->N, tmp3); mpz_powm(tmp2, ng->g, b, ng->N); mpz_addm(B, tmp1, tmp2, ng->N, tmp3); if (!H_nn(u, alg, ng->N, A, B)) goto ver_cleanup_and_exit; srp_dbg_num(u, "Server calculated u: "); /* S = (A *(v^u)) ^ b */ mpz_powm(tmp1, v, u, ng->N); mpz_mulm(tmp2, A, tmp1, ng->N, tmp3); mpz_powm(S, tmp2, b, ng->N); if (!hash_num(alg, S, ver->session_key)) goto ver_cleanup_and_exit; if (!calculate_M( alg, ng, ver->M, username, bytes_s, len_s, A, B, ver->session_key)) { goto ver_cleanup_and_exit; } if (!calculate_H_AMK(alg, ver->H_AMK, A, ver->M, ver->session_key)) { goto ver_cleanup_and_exit; } *len_B = mpz_num_bytes(B); *bytes_B = (unsigned char *)srp_alloc(*len_B); if (!*bytes_B) { *len_B = 0; goto ver_cleanup_and_exit; } mpz_to_bin(B, *bytes_B); ver->bytes_B = *bytes_B; } else { srp_free(ver); ver = 0; } cleanup_and_exit: mpz_clear(v); mpz_clear(A); mpz_clear(u); mpz_clear(k); mpz_clear(B); mpz_clear(S); mpz_clear(b); mpz_clear(tmp1); mpz_clear(tmp2); mpz_clear(tmp3); return ver; ver_cleanup_and_exit: srp_free(ver->username); srp_free(ver); ver = 0; goto cleanup_and_exit; }