/* * Copyright (c) 1997-2003 The Stanford SRP Authentication Project * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. * * IN NO EVENT SHALL STANFORD BE LIABLE FOR ANY SPECIAL, INCIDENTAL, * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * In addition, the following conditions apply: * * 1. Any software that incorporates the SRP authentication technology * is requested to display the following acknowlegment: * "This product uses the 'Secure Remote Password' cryptographic * authentication system developed by Tom Wu (tjw@CS.Stanford.EDU)." * * 2. Any software that incorporates all or part of the SRP distribution * itself must display the following acknowledgment: * "This product includes software developed by Tom Wu and Eugene * Jhong for the SRP Distribution (http://srp.stanford.edu/)." * * 3. Redistributions in source or binary form must retain an intact copy * of this copyright notice and list of conditions. */ #include "t_defines.h" #include "srp.h" #include "t_sha.h" /* * The RFC2945 server keeps track of the running hash state via * SHA1_CTX structures pointed to by the meth_data pointer. * The "hash" member is the hash value that will be sent to the * other side; the "ckhash" member is the hash value expected * from the other side. The server also keeps two more "old" * hash states, for backwards-compatibility. */ struct server_meth_st { SHA1_CTX hash; SHA1_CTX ckhash; SHA1_CTX oldhash; SHA1_CTX oldckhash; unsigned char k[RFC2945_KEY_LEN]; unsigned char r[RFC2945_RESP_LEN]; }; #define SERVER_CTXP(srp) ((struct server_meth_st *)(srp)->meth_data) static SRP_RESULT srp2945_server_init(SRP * srp) { srp->magic = SRP_MAGIC_SERVER; srp->meth_data = malloc(sizeof(struct server_meth_st)); SHA1Init(&SERVER_CTXP(srp)->hash); SHA1Init(&SERVER_CTXP(srp)->ckhash); SHA1Init(&SERVER_CTXP(srp)->oldhash); SHA1Init(&SERVER_CTXP(srp)->oldckhash); return SRP_SUCCESS; } static SRP_RESULT srp2945_server_finish(SRP * srp) { if(srp->meth_data) { memset(srp->meth_data, 0, sizeof(struct server_meth_st)); free(srp->meth_data); } return SRP_SUCCESS; } static SRP_RESULT srp2945_server_params(SRP * srp, const unsigned char * modulus, int modlen, const unsigned char * generator, int genlen, const unsigned char * salt, int saltlen) { unsigned char buf1[SHA_DIGESTSIZE], buf2[SHA_DIGESTSIZE]; SHA1_CTX ctxt; int i; /* Fields set by SRP_set_params */ /* Update hash state */ SHA1Init(&ctxt); SHA1Update(&ctxt, modulus, modlen); SHA1Final(buf1, &ctxt); /* buf1 = H(modulus) */ SHA1Init(&ctxt); SHA1Update(&ctxt, generator, genlen); SHA1Final(buf2, &ctxt); /* buf2 = H(generator) */ for(i = 0; i < sizeof(buf1); ++i) buf1[i] ^= buf2[i]; /* buf1 = H(modulus) XOR H(generator) */ /* ckhash: H(N) xor H(g) */ SHA1Update(&SERVER_CTXP(srp)->ckhash, buf1, sizeof(buf1)); SHA1Init(&ctxt); SHA1Update(&ctxt, srp->username->data, srp->username->length); SHA1Final(buf1, &ctxt); /* buf1 = H(user) */ /* ckhash: (H(N) xor H(g)) | H(U) */ SHA1Update(&SERVER_CTXP(srp)->ckhash, buf1, sizeof(buf1)); /* ckhash: (H(N) xor H(g)) | H(U) | s */ SHA1Update(&SERVER_CTXP(srp)->ckhash, salt, saltlen); return SRP_SUCCESS; } static SRP_RESULT srp2945_server_auth(SRP * srp, const unsigned char * a, int alen) { /* On the server, the authenticator is the verifier */ srp->verifier = BigIntegerFromBytes(a, alen); return SRP_SUCCESS; } /* Normally this method isn't called, except maybe by test programs */ static SRP_RESULT srp2945_server_passwd(SRP * srp, const unsigned char * p, int plen) { SHA1_CTX ctxt; unsigned char dig[SHA_DIGESTSIZE]; SHA1Init(&ctxt); SHA1Update(&ctxt, srp->username->data, srp->username->length); SHA1Update(&ctxt, ":", 1); SHA1Update(&ctxt, p, plen); SHA1Final(dig, &ctxt); /* dig = H(U | ":" | P) */ SHA1Init(&ctxt); SHA1Update(&ctxt, srp->salt->data, srp->salt->length); SHA1Update(&ctxt, dig, sizeof(dig)); SHA1Final(dig, &ctxt); /* dig = H(s | H(U | ":" | P)) */ memset(&ctxt, 0, sizeof(ctxt)); srp->password = BigIntegerFromBytes(dig, sizeof(dig)); memset(dig, 0, sizeof(dig)); /* verifier = g^x mod N */ srp->verifier = BigIntegerFromInt(0); BigIntegerModExp(srp->verifier, srp->generator, srp->password, srp->modulus, srp->bctx, srp->accel); return SRP_SUCCESS; } static SRP_RESULT srp2945_server_genpub(SRP * srp, cstr ** result) { cstr * bstr; SHA1_CTX ctxt; unsigned char dig[SHA_DIGESTSIZE]; unsigned long u; int slen = (SRP_get_secret_bits(BigIntegerBitLen(srp->modulus)) + 7) / 8; if(result == NULL) bstr = cstr_new(); else { if(*result == NULL) *result = cstr_new(); bstr = *result; } cstr_set_length(bstr, BigIntegerByteLen(srp->modulus)); t_random(bstr->data, slen); srp->secret = BigIntegerFromBytes(bstr->data, slen); srp->pubkey = BigIntegerFromInt(0); u = 0; /* Screen for u != 0 */ while(u == 0) { /* B = v + g^b mod n (blinding) */ BigIntegerModExp(srp->pubkey, srp->generator, srp->secret, srp->modulus, srp->bctx, srp->accel); BigIntegerAdd(srp->pubkey, srp->pubkey, srp->verifier); if(BigIntegerCmp(srp->pubkey, srp->modulus) > 0) BigIntegerSub(srp->pubkey, srp->pubkey, srp->modulus); /* Compute u = first-32-bits of H(pubkey) */ BigIntegerToCstr(srp->pubkey, bstr); SHA1Init(&ctxt); SHA1Update(&ctxt, bstr->data, bstr->length); SHA1Final(dig, &ctxt); /* dig = H(pubkey) */ u = (dig[0] << 24) | (dig[1] << 16) | (dig[2] << 8) | dig[3]; } srp->u = BigIntegerFromBytes(dig, 4); /* oldckhash: B */ SHA1Update(&SERVER_CTXP(srp)->oldckhash, bstr->data, bstr->length); if(result == NULL) /* bstr was a temporary */ cstr_clear_free(bstr); return SRP_SUCCESS; } static SRP_RESULT srp2945_server_key(SRP * srp, cstr ** result, const unsigned char * pubkey, int pubkeylen) { cstr * s; BigInteger t1, t2, t3; /* ckhash: (H(N) xor H(g)) | H(U) | s | A */ SHA1Update(&SERVER_CTXP(srp)->ckhash, pubkey, pubkeylen); s = cstr_new(); BigIntegerToCstr(srp->pubkey, s); /* get encoding of B */ /* ckhash: (H(N) xor H(g)) | H(U) | s | A | B */ SHA1Update(&SERVER_CTXP(srp)->ckhash, s->data, s->length); /* hash: A */ SHA1Update(&SERVER_CTXP(srp)->hash, pubkey, pubkeylen); /* oldhash: A */ SHA1Update(&SERVER_CTXP(srp)->oldhash, pubkey, pubkeylen); /* compute A*v^u */ t1 = BigIntegerFromInt(0); BigIntegerModExp(t1, srp->verifier, srp->u, srp->modulus, srp->bctx, srp->accel); /* t1 = v^u */ t2 = BigIntegerFromBytes(pubkey, pubkeylen); /* t2 = A */ t3 = BigIntegerFromInt(0); BigIntegerModMul(t3, t2, t1, srp->modulus, srp->bctx); /* t3 = A*v^u (mod N) */ if(BigIntegerCmpInt(t3, 1) <= 0) { /* Reject A*v^u == 0,1 (mod N) */ BigIntegerFree(t1); BigIntegerFree(t2); BigIntegerFree(t3); cstr_free(s); return SRP_ERROR; } BigIntegerAddInt(t1, t3, 1); if(BigIntegerCmp(t1, srp->modulus) == 0) { /* Reject A*v^u == -1 (mod N) */ BigIntegerFree(t1); BigIntegerFree(t2); BigIntegerFree(t3); cstr_free(s); return SRP_ERROR; } srp->key = BigIntegerFromInt(0); BigIntegerModExp(srp->key, t3, srp->secret, srp->modulus, srp->bctx, srp->accel); /* (Av^u)^b */ BigIntegerFree(t1); BigIntegerFree(t2); BigIntegerFree(t3); /* convert srp->key into session key, update hashes */ BigIntegerToCstr(srp->key, s); t_sessionkey(SERVER_CTXP(srp)->k, s->data, s->length); /* Interleaved hash */ cstr_clear_free(s); /* ckhash: (H(N) xor H(g)) | H(U) | s | A | B | K */ SHA1Update(&SERVER_CTXP(srp)->ckhash, SERVER_CTXP(srp)->k, RFC2945_KEY_LEN); /* ckhash: (H(N) xor H(g)) | H(U) | s | A | B | K | ex_data */ if(srp->ex_data->length > 0) SHA1Update(&SERVER_CTXP(srp)->ckhash, srp->ex_data->data, srp->ex_data->length); /* oldhash: A | K */ SHA1Update(&SERVER_CTXP(srp)->oldhash, SERVER_CTXP(srp)->k, RFC2945_KEY_LEN); /* oldckhash: B | K */ SHA1Update(&SERVER_CTXP(srp)->oldckhash, SERVER_CTXP(srp)->k, RFC2945_KEY_LEN); if(result) { if(*result == NULL) *result = cstr_new(); cstr_setn(*result, SERVER_CTXP(srp)->k, RFC2945_KEY_LEN); } return SRP_SUCCESS; } static SRP_RESULT srp2945_server_verify(SRP * srp, const unsigned char * proof, int prooflen) { unsigned char expected[SHA_DIGESTSIZE]; SHA1Final(expected, &SERVER_CTXP(srp)->oldckhash); if(prooflen == RFC2945_RESP_LEN && memcmp(expected, proof, prooflen) == 0) { SHA1Final(SERVER_CTXP(srp)->r, &SERVER_CTXP(srp)->oldhash); return SRP_SUCCESS; } SHA1Final(expected, &SERVER_CTXP(srp)->ckhash); if(prooflen == RFC2945_RESP_LEN && memcmp(expected, proof, prooflen) == 0) { /* hash: A | M | K */ SHA1Update(&SERVER_CTXP(srp)->hash, expected, sizeof(expected)); SHA1Update(&SERVER_CTXP(srp)->hash, SERVER_CTXP(srp)->k, RFC2945_KEY_LEN); SHA1Final(SERVER_CTXP(srp)->r, &SERVER_CTXP(srp)->hash); return SRP_SUCCESS; } return SRP_ERROR; } static SRP_RESULT srp2945_server_respond(SRP * srp, cstr ** proof) { if(proof == NULL) return SRP_ERROR; if(*proof == NULL) *proof = cstr_new(); cstr_set_length(*proof, RFC2945_RESP_LEN); memcpy((*proof)->data, SERVER_CTXP(srp)->r, RFC2945_RESP_LEN); return SRP_SUCCESS; } static SRP_METHOD srp_rfc2945_server_meth = { "RFC2945 SRP server (tjw)", srp2945_server_init, srp2945_server_finish, srp2945_server_params, srp2945_server_auth, srp2945_server_passwd, srp2945_server_genpub, srp2945_server_key, srp2945_server_verify, srp2945_server_respond, NULL }; _TYPE( SRP_METHOD * ) SRP_RFC2945_server_method() { return &srp_rfc2945_server_meth; }