summaryrefslogtreecommitdiff
path: root/lib/gnutls_srp.c
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@gnutls.org>2004-07-28 10:28:48 +0000
committerNikos Mavrogiannopoulos <nmav@gnutls.org>2004-07-28 10:28:48 +0000
commit131a588a720c2f0d3193bd70e8495eda21f58ad0 (patch)
tree42557fd4736b59c456fbf946ec29ddee38b55727 /lib/gnutls_srp.c
parent3875ce124736753d5eaaac4a42b22b51700f8347 (diff)
downloadgnutls-131a588a720c2f0d3193bd70e8495eda21f58ad0.tar.gz
SRP ciphersuites were moved to the gnutls (lgpl) library.
Diffstat (limited to 'lib/gnutls_srp.c')
-rw-r--r--lib/gnutls_srp.c702
1 files changed, 702 insertions, 0 deletions
diff --git a/lib/gnutls_srp.c b/lib/gnutls_srp.c
new file mode 100644
index 0000000000..e1374f4bac
--- /dev/null
+++ b/lib/gnutls_srp.c
@@ -0,0 +1,702 @@
+/*
+ * Copyright (C) 2001,2002,2003 Nikos Mavroyanopoulos
+ * Copyright (C) 2004 Free Software Foundation
+ *
+ * This file is part of GNUTLS.
+ *
+ * The GNUTLS library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <gnutls_int.h>
+#include <gnutls_errors.h>
+#include <auth_srp.h>
+#include <gnutls_state.h>
+
+#ifdef ENABLE_SRP
+
+#include <gnutls_srp.h>
+#include <auth_srp_passwd.h>
+#include <gnutls_mpi.h>
+#include <gnutls_num.h>
+#include "debug.h"
+
+
+/* Here functions for SRP (like g^x mod n) are defined
+ */
+
+int _gnutls_srp_gx(opaque * text, size_t textsize, opaque ** result,
+ mpi_t g, mpi_t prime,
+ gnutls_alloc_function galloc_func)
+{
+ mpi_t x, e;
+ size_t result_size;
+
+ if (_gnutls_mpi_scan(&x, text, &textsize)) {
+ gnutls_assert();
+ return GNUTLS_E_MPI_SCAN_FAILED;
+ }
+
+ e = _gnutls_mpi_alloc_like(prime);
+ if (e == NULL) {
+ gnutls_assert();
+ _gnutls_mpi_release(&x);
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ /* e = g^x mod prime (n) */
+ _gnutls_mpi_powm(e, g, x, prime);
+ _gnutls_mpi_release(&x);
+
+ _gnutls_mpi_print(NULL, &result_size, e);
+ if (result != NULL) {
+ *result = galloc_func(result_size);
+ if ((*result) == NULL)
+ return GNUTLS_E_MEMORY_ERROR;
+
+ _gnutls_mpi_print(*result, &result_size, e);
+ }
+
+ _gnutls_mpi_release(&e);
+
+ return result_size;
+
+}
+
+
+/****************
+ * Choose a random value b and calculate B = (k* v + g^b) % N.
+ * where k == SHA1(N|g)
+ * Return: B and if ret_b is not NULL b.
+ */
+mpi_t _gnutls_calc_srp_B(mpi_t * ret_b, mpi_t g, mpi_t n, mpi_t v)
+{
+ mpi_t tmpB = NULL, tmpV = NULL;
+ mpi_t b = NULL, B = NULL, k = NULL;
+ int bits;
+ size_t n_size = 0;
+
+ /* get the size of n in bytes */
+ _gnutls_mpi_print( NULL, &n_size, n);
+
+ /* calculate: B = (k*v + g^b) % N
+ */
+ bits = _gnutls_mpi_get_nbits(n);
+ b = _gnutls_mpi_snew(bits);
+ if (b == NULL) {
+ gnutls_assert();
+ return NULL;
+ }
+
+ tmpV = _gnutls_mpi_alloc_like(n);
+
+ if (tmpV == NULL) {
+ gnutls_assert();
+ goto error;
+ }
+
+ _gnutls_mpi_randomize(b, bits, GCRY_STRONG_RANDOM);
+
+ tmpB = _gnutls_mpi_snew(bits);
+ if (tmpB == NULL) {
+ gnutls_assert();
+ goto error;
+ }
+
+ B = _gnutls_mpi_snew(bits);
+ if (B == NULL) {
+ gnutls_assert();
+ goto error;
+ }
+
+ k = _gnutls_calc_srp_u(n, g, n_size);
+ if (k == NULL) {
+ gnutls_assert();
+ goto error;
+ }
+
+ _gnutls_mpi_mulm(tmpV, k, v, n);
+ _gnutls_mpi_powm(tmpB, g, b, n);
+
+ _gnutls_mpi_addm(B, tmpV, tmpB, n);
+
+ _gnutls_mpi_release(&k);
+ _gnutls_mpi_release(&tmpB);
+ _gnutls_mpi_release(&tmpV);
+
+ if (ret_b)
+ *ret_b = b;
+ else
+ _gnutls_mpi_release(&b);
+
+ return B;
+
+ error:
+ _gnutls_mpi_release(&b);
+ _gnutls_mpi_release(&B);
+ _gnutls_mpi_release(&k);
+ _gnutls_mpi_release(&tmpB);
+ _gnutls_mpi_release(&tmpV);
+ return NULL;
+
+}
+
+/* This calculates the SHA1(A | B)
+ * A and B will be left-padded with zeros to fill n_size.
+ */
+mpi_t _gnutls_calc_srp_u(mpi_t A, mpi_t B, size_t n_size)
+{
+ size_t b_size, a_size;
+ opaque *holder, hd[MAX_HASH_SIZE];
+ size_t holder_size, hash_size;
+ GNUTLS_HASH_HANDLE td;
+ int ret;
+ mpi_t res;
+
+ _gnutls_mpi_print(NULL, &a_size, A);
+ _gnutls_mpi_print(NULL, &b_size, B);
+
+ if (a_size > n_size || b_size > n_size) {
+ gnutls_assert();
+ return NULL; /* internal error */
+ }
+
+ holder_size = n_size + n_size;
+
+ holder = gnutls_calloc(1, holder_size);
+ if (holder == NULL)
+ return NULL;
+
+ _gnutls_mpi_print(&holder[n_size - a_size], &a_size, A);
+ _gnutls_mpi_print(&holder[n_size + n_size - b_size], &b_size, B);
+
+ td = _gnutls_hash_init(GNUTLS_MAC_SHA);
+ if (td == NULL) {
+ gnutls_free(holder);
+ gnutls_assert();
+ return NULL;
+ }
+ _gnutls_hash(td, holder, holder_size);
+ _gnutls_hash_deinit(td, hd);
+
+ /* convert the bytes of hd to integer
+ */
+ hash_size = 20; /* SHA */
+ ret = _gnutls_mpi_scan(&res, hd, &hash_size);
+ gnutls_free(holder);
+
+ if (ret < 0) {
+ gnutls_assert();
+ return NULL;
+ }
+
+ return res;
+}
+
+/* S = (A * v^u) ^ b % N
+ * this is our shared key (server premaster secret)
+ */
+mpi_t _gnutls_calc_srp_S1(mpi_t A, mpi_t b, mpi_t u,
+ mpi_t v, mpi_t n)
+{
+ mpi_t tmp1 = NULL, tmp2 = NULL;
+ mpi_t S = NULL;
+
+ S = _gnutls_mpi_alloc_like(n);
+ if (S == NULL)
+ return NULL;
+
+ tmp1 = _gnutls_mpi_alloc_like(n);
+ tmp2 = _gnutls_mpi_alloc_like(n);
+
+ if (tmp1 == NULL || tmp2 == NULL)
+ goto freeall;
+
+ _gnutls_mpi_powm(tmp1, v, u, n);
+ _gnutls_mpi_mulm(tmp2, A, tmp1, n);
+ _gnutls_mpi_powm(S, tmp2, b, n);
+
+ _gnutls_mpi_release(&tmp1);
+ _gnutls_mpi_release(&tmp2);
+
+ return S;
+
+ freeall:
+ _gnutls_mpi_release(&tmp1);
+ _gnutls_mpi_release(&tmp2);
+ return NULL;
+}
+
+/* A = g^a % N
+ * returns A and a (which is random)
+ */
+mpi_t _gnutls_calc_srp_A(mpi_t * a, mpi_t g, mpi_t n)
+{
+ mpi_t tmpa;
+ mpi_t A;
+ int bits;
+
+ bits = _gnutls_mpi_get_nbits(n);
+ tmpa = _gnutls_mpi_snew(bits);
+ if (tmpa == NULL) {
+ gnutls_assert();
+ return NULL;
+ }
+
+ _gnutls_mpi_randomize(tmpa, bits, GCRY_STRONG_RANDOM);
+
+ A = _gnutls_mpi_snew(bits);
+ if (A == NULL) {
+ gnutls_assert();
+ _gnutls_mpi_release(&tmpa);
+ return NULL;
+ }
+ _gnutls_mpi_powm(A, g, tmpa, n);
+
+ if (a != NULL)
+ *a = tmpa;
+ else
+ _gnutls_mpi_release(&tmpa);
+
+ return A;
+}
+
+/* generate x = SHA(s | SHA(U | ":" | p))
+ * The output is exactly 20 bytes
+ */
+int _gnutls_calc_srp_sha(const char *username, const char *password,
+ opaque * salt, int salt_size, size_t * size,
+ void *digest)
+{
+ GNUTLS_HASH_HANDLE td;
+ opaque res[MAX_HASH_SIZE];
+
+ *size = 20;
+
+ td = _gnutls_hash_init(GNUTLS_MAC_SHA);
+ if (td == NULL) {
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+ _gnutls_hash(td, username, strlen(username));
+ _gnutls_hash(td, ":", 1);
+ _gnutls_hash(td, password, strlen(password));
+
+ _gnutls_hash_deinit(td, res);
+
+ td = _gnutls_hash_init(GNUTLS_MAC_SHA);
+ if (td == NULL) {
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ _gnutls_hash(td, salt, salt_size);
+ _gnutls_hash(td, res, 20); /* 20 bytes is the output of sha1 */
+
+ _gnutls_hash_deinit(td, digest);
+
+ return 0;
+}
+
+int _gnutls_calc_srp_x(char *username, char *password, opaque * salt,
+ size_t salt_size, size_t * size, void *digest)
+{
+
+ return _gnutls_calc_srp_sha(username, password, salt,
+ salt_size, size, digest);
+}
+
+
+/* S = (B - k*g^x) ^ (a + u * x) % N
+ * this is our shared key (client premaster secret)
+ */
+mpi_t _gnutls_calc_srp_S2(mpi_t B, mpi_t g, mpi_t x,
+ mpi_t a, mpi_t u, mpi_t n)
+{
+ mpi_t S = NULL, tmp1 = NULL, tmp2 = NULL;
+ mpi_t tmp4 = NULL, tmp3 = NULL, k = NULL;
+ size_t n_size = 0;
+
+ /* get the size of n in bytes */
+ _gnutls_mpi_print( NULL, &n_size, n);
+
+ S = _gnutls_mpi_alloc_like(n);
+ if (S == NULL)
+ return NULL;
+
+ tmp1 = _gnutls_mpi_alloc_like(n);
+ tmp2 = _gnutls_mpi_alloc_like(n);
+ tmp3 = _gnutls_mpi_alloc_like(n);
+ if (tmp1 == NULL || tmp2 == NULL || tmp3 == NULL) {
+ goto freeall;
+ }
+
+ k = _gnutls_calc_srp_u(n, g, n_size);
+ if (k == NULL) {
+ gnutls_assert();
+ goto freeall;
+ }
+
+ _gnutls_mpi_powm(tmp1, g, x, n); /* g^x */
+ _gnutls_mpi_mulm(tmp3, tmp1, k, n); /* k*g^x mod n */
+ _gnutls_mpi_subm(tmp2, B, tmp3, n);
+
+ tmp4 = _gnutls_mpi_alloc_like(n);
+ if (tmp4 == NULL)
+ goto freeall;
+
+ _gnutls_mpi_mul(tmp1, u, x);
+ _gnutls_mpi_add(tmp4, a, tmp1);
+ _gnutls_mpi_powm(S, tmp2, tmp4, n);
+
+ _gnutls_mpi_release(&tmp1);
+ _gnutls_mpi_release(&tmp2);
+ _gnutls_mpi_release(&tmp3);
+ _gnutls_mpi_release(&tmp4);
+ _gnutls_mpi_release(&k);
+
+ return S;
+
+ freeall:
+ _gnutls_mpi_release(&k);
+ _gnutls_mpi_release(&tmp1);
+ _gnutls_mpi_release(&tmp2);
+ _gnutls_mpi_release(&tmp3);
+ _gnutls_mpi_release(&tmp4);
+ _gnutls_mpi_release(&S);
+ return NULL;
+}
+
+/**
+ * gnutls_srp_free_server_credentials - Used to free an allocated gnutls_srp_client_credentials_t structure
+ * @sc: is an &gnutls_srp_client_credentials_t structure.
+ *
+ * This structure is complex enough to manipulate directly thus
+ * this helper function is provided in order to free (deallocate) it.
+ *
+ **/
+void gnutls_srp_free_client_credentials(gnutls_srp_client_credentials_t sc)
+{
+ gnutls_free(sc->username);
+ gnutls_free(sc->password);
+ gnutls_free(sc);
+}
+
+/**
+ * gnutls_srp_allocate_server_credentials - Used to allocate an gnutls_srp_server_credentials_t structure
+ * @sc: is a pointer to an &gnutls_srp_server_credentials_t structure.
+ *
+ * This structure is complex enough to manipulate directly thus
+ * this helper function is provided in order to allocate it.
+ *
+ * Returns 0 on success.
+ **/
+int gnutls_srp_allocate_client_credentials(gnutls_srp_client_credentials_t *
+ sc)
+{
+ *sc = gnutls_calloc(1, sizeof(srp_client_credentials_st));
+
+ if (*sc == NULL)
+ return GNUTLS_E_MEMORY_ERROR;
+
+ return 0;
+}
+
+/**
+ * gnutls_srp_set_client_credentials - Used to set the username/password, in a gnutls_srp_client_credentials_t structure
+ * @res: is an &gnutls_srp_client_credentials_t structure.
+ * @username: is the user's userid
+ * @password: is the user's password
+ *
+ * This function sets the username and password, in a gnutls_srp_client_credentials_t structure.
+ * Those will be used in SRP authentication. @username and @password should be ASCII
+ * strings or UTF-8 strings prepared using the "SASLprep" profile of "stringprep".
+ *
+ * Returns 0 on success.
+ **/
+int gnutls_srp_set_client_credentials(gnutls_srp_client_credentials_t res,
+ char *username, char *password)
+{
+
+ if (username == NULL || password == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ res->username = gnutls_strdup(username);
+ if (res->username == NULL)
+ return GNUTLS_E_MEMORY_ERROR;
+
+ res->password = gnutls_strdup(password);
+ if (res->password == NULL) {
+ gnutls_free(res->username);
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_srp_free_server_credentials - Used to free an allocated gnutls_srp_server_credentials_t structure
+ * @sc: is an &gnutls_srp_server_credentials_t structure.
+ *
+ * This structure is complex enough to manipulate directly thus
+ * this helper function is provided in order to free (deallocate) it.
+ *
+ **/
+void gnutls_srp_free_server_credentials(gnutls_srp_server_credentials_t sc)
+{
+ gnutls_free(sc->password_file);
+ gnutls_free(sc->password_conf_file);
+
+ gnutls_free(sc);
+}
+
+/**
+ * gnutls_srp_allocate_server_credentials - Used to allocate an gnutls_srp_server_credentials_t structure
+ * @sc: is a pointer to an &gnutls_srp_server_credentials_t structure.
+ *
+ * This structure is complex enough to manipulate directly thus
+ * this helper function is provided in order to allocate it.
+ *
+ * Returns 0 on success.
+ **/
+int gnutls_srp_allocate_server_credentials(gnutls_srp_server_credentials_t *
+ sc)
+{
+ *sc = gnutls_calloc(1, sizeof(srp_server_cred_st));
+
+ if (*sc == NULL)
+ return GNUTLS_E_MEMORY_ERROR;
+
+ return 0;
+}
+
+inline static int file_exists(const char *file)
+{
+ FILE *fd;
+
+ fd = fopen(file, "r");
+ if (fd == NULL)
+ return -1;
+
+ fclose(fd);
+ return 0;
+}
+
+/**
+ * gnutls_srp_set_server_credentials_file - Used to set the password files, in a gnutls_srp_server_credentials_t structure
+ * @res: is an &gnutls_srp_server_credentials_t structure.
+ * @password_file: is the SRP password file (tpasswd)
+ * @password_conf_file: is the SRP password conf file (tpasswd.conf)
+ *
+ * This function sets the password files, in a gnutls_srp_server_credentials_t structure.
+ * Those password files hold usernames and verifiers and will be used for SRP authentication.
+ *
+ * Returns 0 on success.
+ *
+ **/
+int gnutls_srp_set_server_credentials_file(gnutls_srp_server_credentials_t
+ res, const char *password_file,
+ const char *password_conf_file)
+{
+
+ if (password_file == NULL || password_conf_file == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* Check if the files can be opened */
+ if (file_exists(password_file) != 0) {
+ gnutls_assert();
+ return GNUTLS_E_FILE_ERROR;
+ }
+
+ if (file_exists(password_conf_file) != 0) {
+ gnutls_assert();
+ return GNUTLS_E_FILE_ERROR;
+ }
+
+ res->password_file = gnutls_strdup(password_file);
+ if (res->password_file == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ res->password_conf_file = gnutls_strdup(password_conf_file);
+ if (res->password_conf_file == NULL) {
+ gnutls_assert();
+ gnutls_free(res->password_file);
+ res->password_file = NULL;
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ return 0;
+}
+
+
+/**
+ * gnutls_srp_set_server_credentials_function - Used to set a callback to retrieve the user's SRP credentials
+ * @cred: is a &gnutls_srp_server_credentials_t structure.
+ * @func: is the callback function
+ *
+ * This function can be used to set a callback to retrieve the user's SRP credentials.
+ * The callback's function form is:
+ * int (*callback)(gnutls_session_t, const char* username,
+ * gnutls_datum_t* salt, gnutls_datum_t *verifier, gnutls_datum_t* g,
+ * gnutls_datum_t* n);
+ *
+ * @username contains the actual username.
+ * The @salt, @verifier, @generator and @prime must be filled
+ * in using the gnutls_malloc(). For convenience @prime and @generator
+ * may also be one of the static parameters defined in extra.h.
+ *
+ * In case the callback returned a negative number then gnutls will
+ * assume that the username does not exist.
+ *
+ * In order to prevent attackers from guessing valid usernames,
+ * if a user does not exist, g and n values should be filled in
+ * using a random user's parameters. In that case the callback must
+ * return the special value (1).
+ *
+ * The callback function will only be called once per handshake.
+ * The callback function should return 0 on success, while
+ * -1 indicates an error.
+ *
+ **/
+void
+gnutls_srp_set_server_credentials_function(gnutls_srp_server_credentials_t
+ cred, gnutls_srp_server_credentials_function * func)
+{
+ cred->pwd_callback = func;
+}
+
+/**
+ * gnutls_srp_set_client_credentials_function - Used to set a callback to retrieve the username and password
+ * @cred: is a &gnutls_srp_server_credentials_t structure.
+ * @func: is the callback function
+ *
+ * This function can be used to set a callback to retrieve the username and
+ * password for client SRP authentication.
+ * The callback's function form is:
+ * int (*callback)(gnutls_session_t, unsigned int times, char** username,
+ * char** password);
+ *
+ * The @username and @password must be allocated using gnutls_malloc().
+ * @times will be 0 the first time called, and 1 the second.
+ * @username and @password should be ASCII strings or UTF-8 strings
+ * prepared using the "SASLprep" profile of "stringprep".
+ *
+ * The callback function will be called once or twice per handshake.
+ * The first time called, is before the ciphersuite is negotiated.
+ * At that time if the callback returns a negative error code,
+ * the callback will be called again if SRP has been
+ * negotiated. This uses a special TLS-SRP idiom in order to avoid
+ * asking the user for SRP password and username if the server does
+ * not support SRP.
+ *
+ * The callback should not return a negative error code the second
+ * time called, since the handshake procedure will be aborted.
+ *
+ * The callback function should return 0 on success.
+ * -1 indicates an error.
+ *
+ **/
+void
+gnutls_srp_set_client_credentials_function(gnutls_srp_client_credentials_t
+ cred,
+ gnutls_srp_client_credentials_function
+ * func)
+{
+ cred->get_function = func;
+}
+
+
+/**
+ * gnutls_srp_server_get_username - This function returns the username of the peer
+ * @session: is a gnutls session
+ *
+ * This function will return the username of the peer. This should only be
+ * called in case of SRP authentication and in case of a server.
+ * Returns NULL in case of an error.
+ *
+ **/
+const char *gnutls_srp_server_get_username(gnutls_session_t session)
+{
+ srp_server_auth_info_t info;
+
+ CHECK_AUTH(GNUTLS_CRD_SRP, NULL);
+
+ info = _gnutls_get_auth_info(session);
+ if (info == NULL)
+ return NULL;
+ return info->username;
+}
+
+/**
+ * gnutls_srp_verifier - Used to calculate an SRP verifier
+ * @username: is the user's name
+ * @password: is the user's password
+ * @salt: should be some randomly generated bytes
+ * @generator: is the generator of the group
+ * @prime: is the group's prime
+ * @res: where the verifier will be stored.
+ *
+ * This function will create an SRP verifier, as specified in RFC2945.
+ * The @prime and @generator should be one of the static parameters defined
+ * in gnutls/extra.h or may be generated using the GCRYPT functions
+ * gcry_prime_generate() and gcry_prime_group_generator().
+ * The verifier will be allocated with @malloc and will be stored in @res using
+ * binary format.
+ *
+ **/
+int gnutls_srp_verifier(const char *username, const char *password,
+ const gnutls_datum_t * salt,
+ const gnutls_datum_t * generator,
+ const gnutls_datum_t * prime, gnutls_datum_t * res)
+{
+ mpi_t _n, _g;
+ int ret;
+ size_t digest_size = 20, size;
+ opaque digest[20];
+
+ ret = _gnutls_calc_srp_sha(username, password, salt->data,
+ salt->size, &digest_size, digest);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ size = prime->size;
+ if (_gnutls_mpi_scan(&_n, prime->data, &size)) {
+ gnutls_assert();
+ return GNUTLS_E_MPI_SCAN_FAILED;
+ }
+
+ size = generator->size;
+ if (_gnutls_mpi_scan(&_g, generator->data, &size)) {
+ gnutls_assert();
+ return GNUTLS_E_MPI_SCAN_FAILED;
+ }
+
+ ret = _gnutls_srp_gx(digest, 20, &res->data, _g, _n, malloc);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+ res->size = ret;
+
+ return 0;
+}
+
+#endif /* ENABLE_SRP */