diff options
author | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2004-07-28 10:28:48 +0000 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2004-07-28 10:28:48 +0000 |
commit | 131a588a720c2f0d3193bd70e8495eda21f58ad0 (patch) | |
tree | 42557fd4736b59c456fbf946ec29ddee38b55727 /lib | |
parent | 3875ce124736753d5eaaac4a42b22b51700f8347 (diff) | |
download | gnutls-131a588a720c2f0d3193bd70e8495eda21f58ad0.tar.gz |
SRP ciphersuites were moved to the gnutls (lgpl) library.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Makefile.am | 8 | ||||
-rw-r--r-- | lib/auth_srp.c | 782 | ||||
-rw-r--r-- | lib/auth_srp.h | 64 | ||||
-rw-r--r-- | lib/auth_srp_passwd.c | 409 | ||||
-rw-r--r-- | lib/auth_srp_passwd.h | 18 | ||||
-rw-r--r-- | lib/auth_srp_rsa.c | 200 | ||||
-rw-r--r-- | lib/auth_srp_sb64.c | 426 | ||||
-rw-r--r-- | lib/ext_srp.c | 152 | ||||
-rw-r--r-- | lib/ext_srp.h | 9 | ||||
-rw-r--r-- | lib/gnutls.h.in.in | 49 | ||||
-rw-r--r-- | lib/gnutls_algorithms.c | 7 | ||||
-rw-r--r-- | lib/gnutls_extensions.c | 6 | ||||
-rw-r--r-- | lib/gnutls_srp.c | 702 | ||||
-rw-r--r-- | lib/gnutls_srp.h | 15 |
14 files changed, 2844 insertions, 3 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am index f4dbe60d71..9882ceb832 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -35,7 +35,8 @@ EXTRA_DIST = debug.h gnutls_compress.h defines.h gnutls.asn pkix.asn \ gnutls_sig.h gnutls_mem.h gnutls_ui.h \ io_debug.h ext_max_record.h gnutls_session_pack.h \ gnutls_alert.h gnutls_str.h gnutls_state.h gnutls_x509.h \ - ext_cert_type.h gnutls_rsa_export.h ext_server_name.h auth_dh_common.h + ext_cert_type.h gnutls_rsa_export.h ext_server_name.h auth_dh_common.h \ + ext_srp.h gnutls_srp.h auth_srp.h auth_srp_passwd.h lib_LTLIBRARIES = libgnutls.la X509_COBJECTS = x509/crl.c x509/dn.c x509/common.c x509/x509.c x509/extensions.c \ @@ -44,6 +45,9 @@ X509_COBJECTS = x509/crl.c x509/dn.c x509/common.c x509/x509.c x509/extensions.c x509/privkey_pkcs8.c x509/pkcs12.c x509/pkcs12_bag.c x509/pkcs12_encr.c \ x509/x509_write.c x509/crl_write.c x509/compat.c +SRP_COBJECTS = ext_srp.c gnutls_srp.c auth_srp.c auth_srp_passwd.c \ + auth_srp_sb64.c auth_srp_rsa.c + COBJECTS = gnutls_record.c gnutls_compress.c debug.c \ gnutls_cipher.c gnutls_buffers.c gnutls_handshake.c gnutls_num.c \ gnutls_errors.c gnutls_algorithms.c gnutls_dh.c gnutls_kx.c \ @@ -63,7 +67,7 @@ COBJECTS = gnutls_record.c gnutls_compress.c debug.c \ # Separate so we can create the documentation -libgnutls_la_SOURCES = $(COBJECTS) $(X509_COBJECTS) $(MINITASN1_COBJECTS) +libgnutls_la_SOURCES = $(COBJECTS) $(X509_COBJECTS) $(MINITASN1_COBJECTS) $(SRP_COBJECTS) libgnutls_la_LDFLAGS = $(LIBTASN1_LIBS) $(LIBGCRYPT_LIBS) \ $(libgnutls_version_script_cmd) \ diff --git a/lib/auth_srp.c b/lib/auth_srp.c new file mode 100644 index 0000000000..671ec20fa0 --- /dev/null +++ b/lib/auth_srp.c @@ -0,0 +1,782 @@ +/* + * 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> + +#ifdef ENABLE_SRP + +#include "gnutls_errors.h" +#include "auth_srp_passwd.h" +#include "gnutls_auth.h" +#include "gnutls_auth_int.h" +#include "gnutls_srp.h" +#include "debug.h" +#include "gnutls_num.h" +#include "auth_srp.h" +#include <gnutls_str.h> +#include <gnutls_datum.h> +#include <gnutls_alert.h> + +int _gnutls_gen_srp_server_kx(gnutls_session_t, opaque **); +int _gnutls_gen_srp_client_kx(gnutls_session_t, opaque **); + +int _gnutls_proc_srp_server_kx(gnutls_session_t, opaque *, size_t); +int _gnutls_proc_srp_client_kx(gnutls_session_t, opaque *, size_t); + +const mod_auth_st srp_auth_struct = { + "SRP", + NULL, + NULL, + _gnutls_gen_srp_server_kx, + _gnutls_gen_srp_client_kx, + NULL, + NULL, + + NULL, + NULL, /* certificate */ + _gnutls_proc_srp_server_kx, + _gnutls_proc_srp_client_kx, + NULL, + NULL +}; + + +#define _b session->key->b +#define B session->key->B +#define _a session->key->a +#define A session->key->A +#define N session->key->client_p +#define G session->key->client_g +#define V session->key->x +#define S session->key->KEY + +/* Checks if b%n==0 which is a fatal srp error. + * Returns a proper error code in that case, and 0 when + * all are ok. + */ +inline static int check_b_mod_n(mpi_t b, mpi_t n) +{ + int ret; + mpi_t r = _gnutls_mpi_alloc_like(b); + + if (r == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + _gnutls_mpi_mod(r, b, n); + ret = _gnutls_mpi_cmp_ui(r, 0); + + _gnutls_mpi_release(&r); + + if (ret == 0) { + gnutls_assert(); + return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; + } + + return 0; +} + +/* Checks if a%n==0,+1,-1%n which is a fatal srp error. + * Returns a proper error code in that case, and 0 when + * all are ok. + */ +inline static int check_a_mod_n(mpi_t a, mpi_t n) +{ + int ret; + mpi_t r = _gnutls_mpi_alloc_like(a); + + if (r == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + _gnutls_mpi_mod(r, a, n); + ret = _gnutls_mpi_cmp_ui(r, 0); + + _gnutls_mpi_release(&r); + + if (ret == 0) { + gnutls_assert(); + return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; + } + + return 0; +} + + +/* Send the first key exchange message ( g, n, s) and append the verifier algorithm number + * Data is allocated by the caller, and should have data_size size. + */ +int _gnutls_gen_srp_server_kx(gnutls_session_t session, opaque ** data) +{ + int ret; + uint8 *data_n, *data_s; + uint8 *data_g; + char *username; + SRP_PWD_ENTRY *pwd_entry; + srp_server_auth_info_t info; + ssize_t data_size; + size_t n_b, tmp_size; + char buf[64]; + uint8 *data_b; + + if ((ret = + _gnutls_auth_info_set(session, GNUTLS_CRD_SRP, + sizeof(srp_server_auth_info_st), 1)) < 0) { + gnutls_assert(); + return ret; + } + + info = _gnutls_get_auth_info(session); + username = info->username; + + _gnutls_str_cpy(username, MAX_SRP_USERNAME, + session->security_parameters.extensions.srp_username); + + ret = _gnutls_srp_pwd_read_entry(session, username, &pwd_entry); + + if (ret < 0) { + gnutls_assert(); + return ret; + } + + /* copy from pwd_entry to local variables (actually in session) */ + if (_gnutls_mpi_scan(&G, pwd_entry->g.data, &pwd_entry->g.size) < 0) { + gnutls_assert(); + return GNUTLS_E_MPI_SCAN_FAILED; + } + + tmp_size = pwd_entry->n.size; + if (_gnutls_mpi_scan(&N, pwd_entry->n.data, &tmp_size) < 0) { + gnutls_assert(); + return GNUTLS_E_MPI_SCAN_FAILED; + } + + tmp_size = pwd_entry->v.size; + if (_gnutls_mpi_scan(&V, pwd_entry->v.data, &tmp_size) < 0) { + gnutls_assert(); + return GNUTLS_E_MPI_SCAN_FAILED; + } + + /* Calculate: B = (k*v + g^b) % N + */ + B = _gnutls_calc_srp_B(&_b, G, N, V); + if (B == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + if (_gnutls_mpi_print(NULL, &n_b, B) != 0) { + gnutls_assert(); + return GNUTLS_E_MPI_PRINT_FAILED; + } + + + /* Allocate size to hold the N, g, s, B + */ + + data_size = (pwd_entry->n.size + 2 + pwd_entry->g.size + 2 + + pwd_entry->salt.size + 1) + (n_b + 2); + + (*data) = gnutls_malloc(data_size); + if ((*data) == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + /* copy N (mod n) + */ + data_n = *data; + _gnutls_write_datum16(data_n, pwd_entry->n); + + + /* copy G (generator) to data + */ + data_g = &data_n[2 + pwd_entry->n.size]; + _gnutls_write_datum16(data_g, pwd_entry->g); + + + /* copy the salt + */ + data_s = &data_g[2 + pwd_entry->g.size]; + _gnutls_write_datum8(data_s, pwd_entry->salt); + + + /* Copy the B value + */ + + data_b = &data_s[1 + pwd_entry->salt.size]; + if (_gnutls_mpi_print(&data_b[2], &n_b, B) != 0) + return GNUTLS_E_MPI_PRINT_FAILED; + _gnutls_write_uint16(n_b, data_b); + + _gnutls_hard_log("INT: SRP B[%d]: %s\n", n_b, + _gnutls_bin2hex(&data_b[2], n_b, buf, sizeof(buf))); + + _gnutls_srp_entry_free(pwd_entry); + + return data_size; +} + +/* return A = g^a % N */ +int _gnutls_gen_srp_client_kx(gnutls_session_t session, opaque ** data) +{ + size_t n_a; + int ret; + uint8 *data_a; + char *username, *password; + size_t n_size = 0; + char buf[64]; + const gnutls_srp_client_credentials_t cred = + _gnutls_get_cred(session->key, GNUTLS_CRD_SRP, NULL); + + if (cred == NULL) { + gnutls_assert(); + return GNUTLS_E_INSUFFICIENT_CREDENTIALS; + } + + if (session->internals.srp_username == NULL) { + username = cred->username; + password = cred->password; + } else { + username = session->internals.srp_username; + password = session->internals.srp_password; + } + + if (username == NULL || password == NULL) { + gnutls_assert(); + return GNUTLS_E_INSUFFICIENT_CREDENTIALS; + } + + /* calc A = g^a % N + */ + if (G == NULL || N == NULL) { + gnutls_assert(); + return GNUTLS_E_INSUFFICIENT_CREDENTIALS; + } + + /* get the size of n in bytes */ + _gnutls_mpi_print( NULL, &n_size, N); + + A = _gnutls_calc_srp_A(&_a, G, N); + if (A == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + /* Rest of SRP calculations + */ + + /* calculate u */ + session->key->u = _gnutls_calc_srp_u(A, B, n_size); + if (session->key->u == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + _gnutls_dump_mpi("SRP U: ", session->key->u); + + /* S = (B - g^x) ^ (a + u * x) % N */ + S = _gnutls_calc_srp_S2(B, G, session->key->x, _a, session->key->u, N); + if (S == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + _gnutls_dump_mpi("SRP B: ", B); + + _gnutls_mpi_release(&_b); + _gnutls_mpi_release(&V); + _gnutls_mpi_release(&session->key->u); + _gnutls_mpi_release(&B); + + ret = _gnutls_generate_session_key(session->key); + _gnutls_mpi_release(&S); + + if (ret < 0) + return ret; + + if (_gnutls_mpi_print(NULL, &n_a, A) != 0) { + gnutls_assert(); + return GNUTLS_E_MPI_PRINT_FAILED; + } + + (*data) = gnutls_malloc(n_a + 2); + if ((*data) == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + /* copy A */ + data_a = (*data); + if (_gnutls_mpi_print(&data_a[2], &n_a, A) != 0) { + gnutls_free(*data); + return GNUTLS_E_MPI_PRINT_FAILED; + } + _gnutls_hard_log("INT: SRP A[%d]: %s\n", n_a, + _gnutls_bin2hex(&data_a[2], n_a, buf, sizeof(buf))); + + _gnutls_mpi_release(&A); + + _gnutls_write_uint16(n_a, data_a); + + return n_a + 2; +} + + +/* just read A and put it to session */ +int _gnutls_proc_srp_client_kx(gnutls_session_t session, opaque * data, + size_t _data_size) +{ + size_t _n_A; + ssize_t data_size = _data_size; + int ret; + size_t n_size = 0; + + DECR_LEN(data_size, 2); + _n_A = _gnutls_read_uint16(&data[0]); + + DECR_LEN(data_size, _n_A); + if (_gnutls_mpi_scan(&A, &data[2], &_n_A) || A == NULL) { + gnutls_assert(); + return GNUTLS_E_MPI_SCAN_FAILED; + } + + _gnutls_dump_mpi("SRP A: ", A); + _gnutls_dump_mpi("SRP B: ", B); + + /* Checks if A % n == 0. + */ + if ((ret = check_a_mod_n(A, N)) < 0) { + gnutls_assert(); + return ret; + } + + /* Start the SRP calculations. + * - Calculate u + */ + /* get the size of n in bytes */ + _gnutls_mpi_print( NULL, &n_size, N); + + session->key->u = _gnutls_calc_srp_u(A, B, n_size); + if (session->key->u == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + _gnutls_dump_mpi("SRP U: ", session->key->u); + + /* S = (A * v^u) ^ b % N + */ + S = _gnutls_calc_srp_S1(A, _b, session->key->u, V, N); + if (S == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + _gnutls_dump_mpi("SRP S: ", S); + + _gnutls_mpi_release(&A); + _gnutls_mpi_release(&_b); + _gnutls_mpi_release(&V); + _gnutls_mpi_release(&session->key->u); + _gnutls_mpi_release(&B); + + ret = _gnutls_generate_session_key(session->key); + _gnutls_mpi_release(&S); + + if (ret < 0) { + gnutls_assert(); + return ret; + } + + return 0; +} + + + +/* Static parameters according to draft-ietf-tls-srp-07 + * Note that if more parameters are added check_g_n() + * and _gnutls_srp_entry_free() should be changed. + */ +static const unsigned char srp_params_1024[] = { + 0xEE, 0xAF, 0x0A, 0xB9, 0xAD, 0xB3, 0x8D, 0xD6, + 0x9C, 0x33, 0xF8, 0x0A, 0xFA, 0x8F, 0xC5, 0xE8, + 0x60, 0x72, 0x61, 0x87, 0x75, 0xFF, 0x3C, 0x0B, + 0x9E, 0xA2, 0x31, 0x4C, 0x9C, 0x25, 0x65, 0x76, + 0xD6, 0x74, 0xDF, 0x74, 0x96, 0xEA, 0x81, 0xD3, + 0x38, 0x3B, 0x48, 0x13, 0xD6, 0x92, 0xC6, 0xE0, + 0xE0, 0xD5, 0xD8, 0xE2, 0x50, 0xB9, 0x8B, 0xE4, + 0x8E, 0x49, 0x5C, 0x1D, 0x60, 0x89, 0xDA, 0xD1, + 0x5D, 0xC7, 0xD7, 0xB4, 0x61, 0x54, 0xD6, 0xB6, + 0xCE, 0x8E, 0xF4, 0xAD, 0x69, 0xB1, 0x5D, 0x49, + 0x82, 0x55, 0x9B, 0x29, 0x7B, 0xCF, 0x18, 0x85, + 0xC5, 0x29, 0xF5, 0x66, 0x66, 0x0E, 0x57, 0xEC, + 0x68, 0xED, 0xBC, 0x3C, 0x05, 0x72, 0x6C, 0xC0, + 0x2F, 0xD4, 0xCB, 0xF4, 0x97, 0x6E, 0xAA, 0x9A, + 0xFD, 0x51, 0x38, 0xFE, 0x83, 0x76, 0x43, 0x5B, + 0x9F, 0xC6, 0x1D, 0x2F, 0xC0, 0xEB, 0x06, 0xE3 +}; + +static const unsigned char srp_generator = 0x02; + +const gnutls_datum_t gnutls_srp_1024_group_prime = { + (void *) srp_params_1024, sizeof(srp_params_1024) +}; +const gnutls_datum_t gnutls_srp_1024_group_generator = { + (void *) &srp_generator, sizeof(srp_generator) +}; + +static const unsigned char srp_params_1536[] = { + 0x9D, 0xEF, 0x3C, 0xAF, 0xB9, 0x39, 0x27, 0x7A, 0xB1, + 0xF1, 0x2A, 0x86, 0x17, 0xA4, 0x7B, 0xBB, 0xDB, 0xA5, + 0x1D, 0xF4, 0x99, 0xAC, 0x4C, 0x80, 0xBE, 0xEE, 0xA9, + 0x61, 0x4B, 0x19, 0xCC, 0x4D, 0x5F, 0x4F, 0x5F, 0x55, + 0x6E, 0x27, 0xCB, 0xDE, 0x51, 0xC6, 0xA9, 0x4B, 0xE4, + 0x60, 0x7A, 0x29, 0x15, 0x58, 0x90, 0x3B, 0xA0, 0xD0, + 0xF8, 0x43, 0x80, 0xB6, 0x55, 0xBB, 0x9A, 0x22, 0xE8, + 0xDC, 0xDF, 0x02, 0x8A, 0x7C, 0xEC, 0x67, 0xF0, 0xD0, + 0x81, 0x34, 0xB1, 0xC8, 0xB9, 0x79, 0x89, 0x14, 0x9B, + 0x60, 0x9E, 0x0B, 0xE3, 0xBA, 0xB6, 0x3D, 0x47, 0x54, + 0x83, 0x81, 0xDB, 0xC5, 0xB1, 0xFC, 0x76, 0x4E, 0x3F, + 0x4B, 0x53, 0xDD, 0x9D, 0xA1, 0x15, 0x8B, 0xFD, 0x3E, + 0x2B, 0x9C, 0x8C, 0xF5, 0x6E, 0xDF, 0x01, 0x95, 0x39, + 0x34, 0x96, 0x27, 0xDB, 0x2F, 0xD5, 0x3D, 0x24, 0xB7, + 0xC4, 0x86, 0x65, 0x77, 0x2E, 0x43, 0x7D, 0x6C, 0x7F, + 0x8C, 0xE4, 0x42, 0x73, 0x4A, 0xF7, 0xCC, 0xB7, 0xAE, + 0x83, 0x7C, 0x26, 0x4A, 0xE3, 0xA9, 0xBE, 0xB8, 0x7F, + 0x8A, 0x2F, 0xE9, 0xB8, 0xB5, 0x29, 0x2E, 0x5A, 0x02, + 0x1F, 0xFF, 0x5E, 0x91, 0x47, 0x9E, 0x8C, 0xE7, 0xA2, + 0x8C, 0x24, 0x42, 0xC6, 0xF3, 0x15, 0x18, 0x0F, 0x93, + 0x49, 0x9A, 0x23, 0x4D, 0xCF, 0x76, 0xE3, 0xFE, 0xD1, + 0x35, 0xF9, 0xBB +}; + +const gnutls_datum_t gnutls_srp_1536_group_prime = { + (void *) srp_params_1536, sizeof(srp_params_1536) +}; +const gnutls_datum_t gnutls_srp_1536_group_generator = { + (void *) &srp_generator, sizeof(srp_generator) +}; + +static const unsigned char srp_params_2048[] = { + 0xAC, 0x6B, 0xDB, 0x41, 0x32, 0x4A, 0x9A, 0x9B, 0xF1, + 0x66, 0xDE, 0x5E, 0x13, 0x89, 0x58, 0x2F, 0xAF, 0x72, + 0xB6, 0x65, 0x19, 0x87, 0xEE, 0x07, 0xFC, 0x31, 0x92, + 0x94, 0x3D, 0xB5, 0x60, 0x50, 0xA3, 0x73, 0x29, 0xCB, + 0xB4, 0xA0, 0x99, 0xED, 0x81, 0x93, 0xE0, 0x75, 0x77, + 0x67, 0xA1, 0x3D, 0xD5, 0x23, 0x12, 0xAB, 0x4B, 0x03, + 0x31, 0x0D, 0xCD, 0x7F, 0x48, 0xA9, 0xDA, 0x04, 0xFD, + 0x50, 0xE8, 0x08, 0x39, 0x69, 0xED, 0xB7, 0x67, 0xB0, + 0xCF, 0x60, 0x95, 0x17, 0x9A, 0x16, 0x3A, 0xB3, 0x66, + 0x1A, 0x05, 0xFB, 0xD5, 0xFA, 0xAA, 0xE8, 0x29, 0x18, + 0xA9, 0x96, 0x2F, 0x0B, 0x93, 0xB8, 0x55, 0xF9, 0x79, + 0x93, 0xEC, 0x97, 0x5E, 0xEA, 0xA8, 0x0D, 0x74, 0x0A, + 0xDB, 0xF4, 0xFF, 0x74, 0x73, 0x59, 0xD0, 0x41, 0xD5, + 0xC3, 0x3E, 0xA7, 0x1D, 0x28, 0x1E, 0x44, 0x6B, 0x14, + 0x77, 0x3B, 0xCA, 0x97, 0xB4, 0x3A, 0x23, 0xFB, 0x80, + 0x16, 0x76, 0xBD, 0x20, 0x7A, 0x43, 0x6C, 0x64, 0x81, + 0xF1, 0xD2, 0xB9, 0x07, 0x87, 0x17, 0x46, 0x1A, 0x5B, + 0x9D, 0x32, 0xE6, 0x88, 0xF8, 0x77, 0x48, 0x54, 0x45, + 0x23, 0xB5, 0x24, 0xB0, 0xD5, 0x7D, 0x5E, 0xA7, 0x7A, + 0x27, 0x75, 0xD2, 0xEC, 0xFA, 0x03, 0x2C, 0xFB, 0xDB, + 0xF5, 0x2F, 0xB3, 0x78, 0x61, 0x60, 0x27, 0x90, 0x04, + 0xE5, 0x7A, 0xE6, 0xAF, 0x87, 0x4E, 0x73, 0x03, 0xCE, + 0x53, 0x29, 0x9C, 0xCC, 0x04, 0x1C, 0x7B, 0xC3, 0x08, + 0xD8, 0x2A, 0x56, 0x98, 0xF3, 0xA8, 0xD0, 0xC3, 0x82, + 0x71, 0xAE, 0x35, 0xF8, 0xE9, 0xDB, 0xFB, 0xB6, 0x94, + 0xB5, 0xC8, 0x03, 0xD8, 0x9F, 0x7A, 0xE4, 0x35, 0xDE, + 0x23, 0x6D, 0x52, 0x5F, 0x54, 0x75, 0x9B, 0x65, 0xE3, + 0x72, 0xFC, 0xD6, 0x8E, 0xF2, 0x0F, 0xA7, 0x11, 0x1F, + 0x9E, 0x4A, 0xFF, 0x73 +}; + +const gnutls_datum_t gnutls_srp_2048_group_prime = { + (void *) srp_params_2048, sizeof(srp_params_2048) +}; +const gnutls_datum_t gnutls_srp_2048_group_generator = { + (void *) &srp_generator, sizeof(srp_generator) +}; + + +/* Check if G and N are parameters from the SRP draft. + */ +static int check_g_n(const opaque * g, size_t n_g, + const opaque * n, size_t n_n) +{ + + if (n_g != 1 || g[0] != srp_generator) + return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; + + if (n_n == sizeof(srp_params_1024) && + memcmp(srp_params_1024, n, n_n) == 0) { + return 0; + } + + if (n_n == sizeof(srp_params_1536) && + memcmp(srp_params_1536, n, n_n) == 0) { + return 0; + } + + if (n_n == sizeof(srp_params_2048) && + memcmp(srp_params_2048, n, n_n) == 0) { + return 0; + } + + return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; +} + +/* Check if N is a prime and G a generator of the + * group. + */ +static int group_check_g_n(mpi_t g, mpi_t n) +{ + mpi_t q = NULL, two = NULL, w = NULL; + int ret; + + /* N must be of the form N=2q+1 + * where q is also a prime. + */ + if (_gnutls_prime_check(n, 0) != 0) { + _gnutls_dump_mpi("no prime N: ", n); + gnutls_assert(); + return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; + } + + two = _gnutls_mpi_new(4); + if (two == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + q = _gnutls_mpi_alloc_like(n); + if (q == NULL) { + gnutls_assert(); + ret = GNUTLS_E_MEMORY_ERROR; + goto error; + } + + /* q = n-1 + */ + _gnutls_mpi_sub_ui(q, n, 1); + + /* q = q/2, remember that q is divisible by 2 (prime - 1) + */ + _gnutls_mpi_set_ui(two, 2); + _gnutls_mpi_div(q, NULL, q, two, 0); + + if (_gnutls_prime_check(q, 0) != 0) { + /* N was not on the form N=2q+1, where q = prime + */ + _gnutls_dump_mpi("no prime Q: ", q); + gnutls_assert(); + return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; + } + + /* We also check whether g is a generator, + */ + + /* check if g < q < N + */ + if (_gnutls_mpi_cmp(g, q) >= 0) { + gnutls_assert(); + ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; + goto error; + } + + w = _gnutls_mpi_alloc_like(q); + if (w == NULL) { + gnutls_assert(); + ret = GNUTLS_E_MEMORY_ERROR; + goto error; + } + + /* check if g^q mod N == N-1 + * w = g^q mod N + */ + _gnutls_mpi_powm(w, g, q, n); + + /* w++ + */ + _gnutls_mpi_add_ui(w, w, 1); + + if (_gnutls_mpi_cmp(w, n) != 0) { + gnutls_assert(); + ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; + goto error; + } + + ret = 0; + + error: + _gnutls_mpi_release(&q); + _gnutls_mpi_release(&two); + _gnutls_mpi_release(&w); + + return ret; + +} + +/* receive the key exchange message ( n, g, s, B) + */ +int _gnutls_proc_srp_server_kx(gnutls_session_t session, opaque * data, + size_t _data_size) +{ + uint8 n_s; + uint16 n_g, n_n, n_b; + size_t _n_s, _n_g, _n_n, _n_b; + const uint8 *data_n; + const uint8 *data_g; + const uint8 *data_s; + const uint8 *data_b; + int i, ret; + opaque hd[SRP_MAX_HASH_SIZE]; + char *username, *password; + ssize_t data_size = _data_size; + + const gnutls_srp_client_credentials_t cred = + _gnutls_get_cred(session->key, GNUTLS_CRD_SRP, NULL); + + if (cred == NULL) { + gnutls_assert(); + return GNUTLS_E_INSUFFICIENT_CREDENTIALS; + } + + if (session->internals.srp_username == NULL) { + username = cred->username; + password = cred->password; + } else { + username = session->internals.srp_username; + password = session->internals.srp_password; + } + + if (username == NULL || password == NULL) { + gnutls_assert(); + return GNUTLS_E_INSUFFICIENT_CREDENTIALS; + } + + i = 0; + + /* Read N + */ + DECR_LEN(data_size, 2); + n_n = _gnutls_read_uint16(&data[i]); + i += 2; + + DECR_LEN(data_size, n_n); + data_n = &data[i]; + i += n_n; + + /* Read G + */ + DECR_LEN(data_size, 2); + n_g = _gnutls_read_uint16(&data[i]); + i += 2; + + DECR_LEN(data_size, n_g); + data_g = &data[i]; + i += n_g; + + /* Read salt + */ + DECR_LEN(data_size, 1); + n_s = data[i]; + i += 1; + + DECR_LEN(data_size, n_s); + data_s = &data[i]; + i += n_s; + + /* Read B + */ + DECR_LEN(data_size, 2); + n_b = _gnutls_read_uint16(&data[i]); + i += 2; + + DECR_LEN(data_size, n_b); + data_b = &data[i]; + i += n_b; + + _n_s = n_s; + _n_g = n_g; + _n_n = n_n; + _n_b = n_b; + + if (_gnutls_mpi_scan(&N, data_n, &_n_n) != 0) { + gnutls_assert(); + return GNUTLS_E_MPI_SCAN_FAILED; + } + + if (_gnutls_mpi_scan(&G, data_g, &_n_g) != 0) { + gnutls_assert(); + return GNUTLS_E_MPI_SCAN_FAILED; + } + + if (_gnutls_mpi_scan(&B, data_b, &_n_b) != 0) { + gnutls_assert(); + return GNUTLS_E_MPI_SCAN_FAILED; + } + + + /* Check if the g and n are from the SRP + * draft. Otherwise check if N is a prime and G + * a generator. + */ + if ((ret = check_g_n(data_g, _n_g, data_n, _n_n)) < 0) { + _gnutls_x509_log("Checking the SRP group parameters.\n"); + if ((ret = group_check_g_n(G, N)) < 0) { + gnutls_assert(); + return ret; + } + } + + /* Checks if b % n == 0 + */ + if ((ret = check_b_mod_n(B, N)) < 0) { + gnutls_assert(); + return ret; + } + + + /* generate x = SHA(s | SHA(U | ":" | p)) + * (or the equivalent using bcrypt) + */ + if ((ret = + _gnutls_calc_srp_x(username, password, (opaque *) data_s, n_s, + &_n_g, hd)) < 0) { + gnutls_assert(); + return ret; + } + + if (_gnutls_mpi_scan(&session->key->x, hd, &_n_g) != 0) { + gnutls_assert(); + return GNUTLS_E_MPI_SCAN_FAILED; + } + + + return i; /* return the processed data + * needed in auth_srp_rsa. + */ +} + +#endif /* ENABLE_SRP */ diff --git a/lib/auth_srp.h b/lib/auth_srp.h new file mode 100644 index 0000000000..992cf9247c --- /dev/null +++ b/lib/auth_srp.h @@ -0,0 +1,64 @@ +#ifndef AUTH_SRP_H +# define AUTH_SRP_H + +#include <gnutls_auth.h> + + +typedef int gnutls_srp_server_credentials_function(gnutls_session_t, + const char *username, gnutls_datum_t * salt, gnutls_datum_t * verifier, + gnutls_datum_t * generator, gnutls_datum_t * prime); + +typedef int gnutls_srp_client_credentials_function(gnutls_session_t, + unsigned int times, char **username, char** password); + + +typedef struct { + char *username; + char *password; + gnutls_srp_client_credentials_function *get_function; +} srp_client_credentials_st; + +#define gnutls_srp_client_credentials_t srp_client_credentials_st* + +typedef struct { + char *password_file; + char *password_conf_file; + /* callback function, instead of reading the + * password files. + */ + gnutls_srp_server_credentials_function *pwd_callback; +} srp_server_cred_st; + +#define gnutls_srp_server_credentials_t srp_server_cred_st* + +/* these structures should not use allocated data */ +typedef struct srp_server_auth_info_st { + char username[MAX_SRP_USERNAME]; +} *srp_server_auth_info_t; + +extern const gnutls_datum_t gnutls_srp_1024_group_prime; +extern const gnutls_datum_t gnutls_srp_1024_group_generator; +extern const gnutls_datum_t gnutls_srp_1536_group_prime; +extern const gnutls_datum_t gnutls_srp_1536_group_generator; +extern const gnutls_datum_t gnutls_srp_2048_group_prime; +extern const gnutls_datum_t gnutls_srp_2048_group_generator; + + +#ifdef ENABLE_SRP + +int _gnutls_proc_srp_server_hello(gnutls_session_t state, + const opaque * data, size_t data_size); +int _gnutls_gen_srp_server_hello(gnutls_session_t state, opaque * data, + size_t data_size); + +int _gnutls_gen_srp_server_kx(gnutls_session_t, opaque **); +int _gnutls_gen_srp_client_kx(gnutls_session_t, opaque **); + +int _gnutls_proc_srp_server_kx(gnutls_session_t, opaque *, size_t); +int _gnutls_proc_srp_client_kx(gnutls_session_t, opaque *, size_t); + +typedef struct srp_server_auth_info_st srp_server_auth_info_st; + +#endif /* ENABLE_SRP */ + +#endif diff --git a/lib/auth_srp_passwd.c b/lib/auth_srp_passwd.c new file mode 100644 index 0000000000..56af4f0d85 --- /dev/null +++ b/lib/auth_srp_passwd.c @@ -0,0 +1,409 @@ +/* + * 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 + * + */ + +/* Functions for operating in an SRP passwd file are included here */ + +#include <gnutls_int.h> + +#ifdef ENABLE_SRP + +#include "x509_b64.h" +#include "gnutls_errors.h" +#include <auth_srp_passwd.h> +#include "auth_srp.h" +#include "gnutls_auth_int.h" +#include "gnutls_srp.h" +#include "gnutls_random.h" +#include "gnutls_dh.h" +#include "debug.h" +#include <gnutls_str.h> +#include <gnutls_datum.h> +#include <gnutls_num.h> + +static int _randomize_pwd_entry(SRP_PWD_ENTRY * entry); + +/* this function parses tpasswd.conf file. Format is: + * string(username):base64(v):base64(salt):int(index) + */ +static int pwd_put_values(SRP_PWD_ENTRY * entry, char *str) +{ + char *p; + int len, ret; + opaque *verifier; + size_t verifier_size; + int indx; + + p = strrchr(str, ':'); /* we have index */ + if (p == NULL) { + gnutls_assert(); + return GNUTLS_E_SRP_PWD_PARSING_ERROR; + } + + *p = '\0'; + p++; + + len = strlen(p); + indx = atoi(p); + if (indx == 0) { + gnutls_assert(); + return GNUTLS_E_SRP_PWD_PARSING_ERROR; + } + + /* now go for salt */ + p = strrchr(str, ':'); /* we have salt */ + if (p == NULL) { + gnutls_assert(); + return GNUTLS_E_SRP_PWD_PARSING_ERROR; + } + + *p = '\0'; + p++; + + len = strlen(p); + + entry->salt.size = _gnutls_sbase64_decode(p, len, &entry->salt.data); + + if (entry->salt.size <= 0) { + gnutls_assert(); + return GNUTLS_E_SRP_PWD_PARSING_ERROR; + } + + /* now go for verifier */ + p = strrchr(str, ':'); /* we have verifier */ + if (p == NULL) { + _gnutls_free_datum(&entry->salt); + return GNUTLS_E_SRP_PWD_PARSING_ERROR; + } + + *p = '\0'; + p++; + + len = strlen(p); + ret = _gnutls_sbase64_decode(p, len, &verifier); + if (ret <= 0) { + gnutls_assert(); + _gnutls_free_datum(&entry->salt); + return GNUTLS_E_SRP_PWD_PARSING_ERROR; + } + + verifier_size = ret; + entry->v.data = verifier; + entry->v.size = verifier_size; + + /* now go for username */ + *p = '\0'; + + entry->username = gnutls_strdup(str); + if (entry->username == NULL) { + _gnutls_free_datum(&entry->salt); + _gnutls_free_datum(&entry->v); + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + return indx; +} + + +/* this function parses tpasswd.conf file. Format is: + * int(index):base64(n):int(g) + */ +static int pwd_put_values2(SRP_PWD_ENTRY * entry, char *str) +{ + char *p; + int len; + opaque *tmp; + int ret; + + p = strrchr(str, ':'); /* we have g */ + if (p == NULL) { + gnutls_assert(); + return GNUTLS_E_SRP_PWD_PARSING_ERROR; + } + + *p = '\0'; + p++; + + /* read the generator */ + len = strlen(p); + if (p[len - 1] == '\n' || p[len - 1] == ' ') + len--; + ret = _gnutls_sbase64_decode(p, len, &tmp); + + if (ret < 0) { + gnutls_assert(); + return GNUTLS_E_SRP_PWD_PARSING_ERROR; + } + + entry->g.data = tmp; + entry->g.size = ret; + + /* now go for n - modulo */ + p = strrchr(str, ':'); /* we have n */ + if (p == NULL) { + _gnutls_free_datum(&entry->g); + gnutls_assert(); + return GNUTLS_E_SRP_PWD_PARSING_ERROR; + } + + *p = '\0'; + p++; + + len = strlen(p); + ret = _gnutls_sbase64_decode(p, len, &tmp); + + if (ret < 0) { + gnutls_assert(); + _gnutls_free_datum(&entry->g); + return GNUTLS_E_SRP_PWD_PARSING_ERROR; + } + + entry->n.data = tmp; + entry->n.size = ret; + + return 0; +} + + +/* this function opens the tpasswd.conf file and reads the g and n + * values. They are put in the entry. + */ +static int pwd_read_conf(const char *pconf_file, SRP_PWD_ENTRY * entry, + int index) +{ + FILE *fd; + char line[2 * 1024]; + uint i, len; + char indexstr[10]; + + sprintf(indexstr, "%d", index); /* Flawfinder: ignore */ + + fd = fopen(pconf_file, "r"); + if (fd == NULL) { + gnutls_assert(); + return GNUTLS_E_FILE_ERROR; + } + + len = strlen(indexstr); + while (fgets(line, sizeof(line), fd) != NULL) { + /* move to first ':' */ + i = 0; + while ((line[i] != ':') && (line[i] != '\0') && (i < sizeof(line))) { + i++; + } + if (strncmp(indexstr, line, GMAX(i, len)) == 0) { + if ((index = pwd_put_values2(entry, line)) >= 0) + return 0; + else { + return GNUTLS_E_SRP_PWD_ERROR; + } + } + } + return GNUTLS_E_SRP_PWD_ERROR; + +} + +int _gnutls_srp_pwd_read_entry(gnutls_session_t state, char *username, + SRP_PWD_ENTRY ** _entry) +{ + const gnutls_srp_server_credentials_t cred; + FILE *fd; + char line[2 * 1024]; + uint i, len; + int ret; + int index, last_index; + SRP_PWD_ENTRY *entry; + + *_entry = gnutls_calloc(1, sizeof(SRP_PWD_ENTRY)); + if (*_entry == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + entry = *_entry; + + cred = _gnutls_get_cred(state->key, GNUTLS_CRD_SRP, NULL); + if (cred == NULL) { + gnutls_assert(); + _gnutls_srp_entry_free(entry); + return GNUTLS_E_INSUFFICIENT_CREDENTIALS; + } + + /* if the callback which sends the parameters is + * set, use it. + */ + if (cred->pwd_callback != NULL) { + ret = cred->pwd_callback(state, username, &entry->salt, + &entry->v, &entry->g, &entry->n); + + if (ret == 1) { /* the user does not exist */ + if (entry->g.size != 0 && entry->n.size != 0) { + ret = _randomize_pwd_entry(entry); + if (ret < 0) { + _gnutls_srp_entry_free(entry); + return ret; + } + return 0; + } else { + gnutls_assert(); + ret = -1; /* error in the callback */ + } + } + + if (ret < 0) { + gnutls_assert(); + _gnutls_srp_entry_free(entry); + return GNUTLS_E_SRP_PWD_ERROR; + } + + return 0; + } + + /* The callback was not set. Proceed. + */ + + if (cred->password_file == NULL) { + gnutls_assert(); + return GNUTLS_E_SRP_PWD_ERROR; + } + + /* Open the selected password file. + */ + fd = fopen(cred->password_file, "r"); + if (fd == NULL) { + gnutls_assert(); + _gnutls_srp_entry_free(entry); + return GNUTLS_E_SRP_PWD_ERROR; + } + + last_index = 1; /* a default value */ + + len = strlen(username); + while (fgets(line, sizeof(line), fd) != NULL) { + /* move to first ':' */ + i = 0; + while ((line[i] != ':') && (line[i] != '\0') && (i < sizeof(line))) { + i++; + } + + if (strncmp(username, line, GMAX(i, len)) == 0) { + if ((index = pwd_put_values(entry, line)) >= 0) { + /* Keep the last index in memory, so we can retrieve fake parameters (g,n) + * when the user does not exist. + */ + last_index = index; + if (pwd_read_conf(cred->password_conf_file, entry, index) + == 0) { + return 0; + } else { + gnutls_assert(); + _gnutls_srp_entry_free(entry); + return GNUTLS_E_SRP_PWD_ERROR; + } + } else { + gnutls_assert(); + _gnutls_srp_entry_free(entry); + return GNUTLS_E_SRP_PWD_ERROR; + } + } + } + + /* user was not found. Fake him. Actually read the g,n values from + * the last index found and randomize the entry. + */ + if (pwd_read_conf(cred->password_conf_file, entry, last_index) == 0) { + ret = _randomize_pwd_entry(entry); + if (ret < 0) { + gnutls_assert(); + _gnutls_srp_entry_free(entry); + return ret; + } + + return 0; + } + + gnutls_assert(); + _gnutls_srp_entry_free(entry); + return GNUTLS_E_SRP_PWD_ERROR; + +} + +/* Randomizes the given password entry. It actually sets the verifier + * and the salt. Returns 0 on success. + */ +static int _randomize_pwd_entry(SRP_PWD_ENTRY * entry) +{ + unsigned char rnd; + + if (entry->g.size == 0 || entry->n.size == 0) { + gnutls_assert(); + return GNUTLS_E_INTERNAL_ERROR; + } + + _gnutls_get_random(&rnd, 1, GNUTLS_WEAK_RANDOM); + entry->salt.size = (rnd % 10) + 9; + + entry->v.data = gnutls_malloc(20); + entry->v.size = 20; + if (entry->v.data == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + _gnutls_get_random(entry->v.data, 20, GNUTLS_WEAK_RANDOM); + + entry->salt.data = gnutls_malloc(entry->salt.size); + if (entry->salt.data == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + if (_gnutls_get_random + (entry->salt.data, entry->salt.size, GNUTLS_WEAK_RANDOM) < 0) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + return 0; +} + +/* Free all the entry parameters, except if g and n are + * the static ones defined in extra.h + */ +void _gnutls_srp_entry_free(SRP_PWD_ENTRY * entry) +{ + _gnutls_free_datum(&entry->v); + _gnutls_free_datum(&entry->salt); + + if (entry->g.data != gnutls_srp_1024_group_generator.data) + _gnutls_free_datum(&entry->g); + + if (entry->n.data != gnutls_srp_1024_group_prime.data && + entry->n.data != gnutls_srp_1536_group_prime.data && + entry->n.data != gnutls_srp_2048_group_prime.data) + _gnutls_free_datum(&entry->n); + + gnutls_free(entry->username); + gnutls_free(entry); +} + + +#endif /* ENABLE SRP */ diff --git a/lib/auth_srp_passwd.h b/lib/auth_srp_passwd.h new file mode 100644 index 0000000000..a9ff502536 --- /dev/null +++ b/lib/auth_srp_passwd.h @@ -0,0 +1,18 @@ +#ifdef ENABLE_SRP + +typedef struct { + char* username; + + gnutls_datum_t salt; + gnutls_datum_t v; + gnutls_datum_t g; + gnutls_datum_t n; +} SRP_PWD_ENTRY; + +/* this is locally allocated. It should be freed using the provided function */ +int _gnutls_srp_pwd_read_entry( gnutls_session_t state, char* username, SRP_PWD_ENTRY**); +void _gnutls_srp_entry_free( SRP_PWD_ENTRY * entry); +int _gnutls_sbase64_encode(uint8 * data, size_t data_size, uint8 ** result); +int _gnutls_sbase64_decode(uint8 * data, size_t data_size, uint8 ** result); + +#endif /* ENABLE_SRP */ diff --git a/lib/auth_srp_rsa.c b/lib/auth_srp_rsa.c new file mode 100644 index 0000000000..00f853060c --- /dev/null +++ b/lib/auth_srp_rsa.c @@ -0,0 +1,200 @@ +/* + * 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> + +#ifdef ENABLE_SRP + +#include "gnutls_errors.h" +#include "auth_srp_passwd.h" +#include "gnutls_auth.h" +#include "gnutls_auth_int.h" +#include "gnutls_srp.h" +#include "debug.h" +#include "gnutls_num.h" +#include "auth_srp.h" +#include <gnutls_str.h> +#include <auth_cert.h> +#include <gnutls_datum.h> +#include <gnutls_sig.h> +#include <auth_srp.h> +#include <gnutls_x509.h> +#include <gnutls_extra.h> + +static int gen_srp_cert_server_kx(gnutls_session_t, opaque **); +static int proc_srp_cert_server_kx(gnutls_session_t, opaque *, size_t); + +const mod_auth_st srp_rsa_auth_struct = { + "SRP", + _gnutls_gen_cert_server_certificate, + NULL, + gen_srp_cert_server_kx, + _gnutls_gen_srp_client_kx, + NULL, + NULL, + + _gnutls_proc_cert_server_certificate, + NULL, /* certificate */ + proc_srp_cert_server_kx, + _gnutls_proc_srp_client_kx, + NULL, + NULL +}; + +const mod_auth_st srp_dss_auth_struct = { + "SRP", + _gnutls_gen_cert_server_certificate, + NULL, + gen_srp_cert_server_kx, + _gnutls_gen_srp_client_kx, + NULL, + NULL, + + _gnutls_proc_cert_server_certificate, + NULL, /* certificate */ + proc_srp_cert_server_kx, + _gnutls_proc_srp_client_kx, + NULL, + NULL +}; + +static int gen_srp_cert_server_kx(gnutls_session_t session, opaque ** data) +{ + ssize_t ret, data_size; + gnutls_datum_t signature, ddata; + const gnutls_certificate_credentials_t cred; + gnutls_cert *apr_cert_list; + gnutls_privkey *apr_pkey; + int apr_cert_list_length; + + ret = _gnutls_gen_srp_server_kx(session, data); + + if (ret < 0) + return ret; + + data_size = ret; + ddata.data = *data; + ddata.size = data_size; + + cred = _gnutls_get_cred(session->key, GNUTLS_CRD_CERTIFICATE, NULL); + if (cred == NULL) { + gnutls_assert(); + return GNUTLS_E_INSUFFICIENT_CREDENTIALS; + } + + /* find the appropriate certificate */ + if ((ret = + _gnutls_get_selected_cert(session, &apr_cert_list, + &apr_cert_list_length, + &apr_pkey)) < 0) { + gnutls_assert(); + return ret; + } + + if ((ret = + _gnutls_tls_sign_params(session, &apr_cert_list[0], + apr_pkey, &ddata, &signature)) < 0) { + gnutls_assert(); + gnutls_free(*data); + return ret; + } + + *data = gnutls_realloc_fast(*data, data_size + signature.size + 2); + if (*data == NULL) { + _gnutls_free_datum(&signature); + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + _gnutls_write_datum16(&(*data)[data_size], signature); + data_size += signature.size + 2; + + _gnutls_free_datum(&signature); + + return data_size; + +} + +static int proc_srp_cert_server_kx(gnutls_session_t session, opaque * data, + size_t _data_size) +{ + ssize_t ret; + int sigsize; + gnutls_datum_t vparams, signature; + ssize_t data_size; + cert_auth_info_t info; + gnutls_cert peer_cert; + opaque *p; + + ret = _gnutls_proc_srp_server_kx(session, data, _data_size); + if (ret < 0) + return ret; + + data_size = _data_size - ret; + + info = _gnutls_get_auth_info(session); + if (info == NULL || info->ncerts == 0) { + gnutls_assert(); + /* we need this in order to get peer's certificate */ + return GNUTLS_E_INTERNAL_ERROR; + } + + /* VERIFY SIGNATURE */ + + vparams.size = ret; /* all the data minus the signature */ + vparams.data = data; + + p = &data[vparams.size]; + + DECR_LEN(data_size, 2); + sigsize = _gnutls_read_uint16(p); + + DECR_LEN(data_size, sigsize); + signature.data = &p[2]; + signature.size = sigsize; + + ret = + _gnutls_raw_cert_to_gcert(&peer_cert, + session->security_parameters.cert_type, + &info->raw_certificate_list[0], + CERT_NO_COPY); + + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = + _gnutls_verify_sig_params(session, + &peer_cert, &vparams, &signature); + + _gnutls_gcert_deinit(&peer_cert); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + return 0; +} + + +#endif /* ENABLE_SRP */ diff --git a/lib/auth_srp_sb64.c b/lib/auth_srp_sb64.c new file mode 100644 index 0000000000..c446508fe6 --- /dev/null +++ b/lib/auth_srp_sb64.c @@ -0,0 +1,426 @@ +/* + * 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 <gnutls_datum.h> + +#ifdef ENABLE_SRP + +/* this a modified base64 for srp !!! + * It seems that everybody makes an own base64 conversion. + */ +static const uint8 b64table[] = + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz./"; + +static const uint8 asciitable[128] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x3e, 0x3f, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x0a, + 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, + 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, + 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, + 0x23, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x24, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, + 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, + 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, + 0x3b, 0x3c, 0x3d, 0xff, 0xff, 0xff, + 0xff, 0xff +}; + +inline static int encode(uint8 * result, const uint8 * rdata, int left) +{ + + int data_len; + int c, ret = 4; + uint8 data[3]; + + if (left > 3) + data_len = 3; + else + data_len = left; + + data[0] = data[1] = data[2] = 0; + memcpy(data, rdata, data_len); + + switch (data_len) { + case 3: + result[0] = b64table[((data[0] & 0xfc) >> 2)]; + result[1] = + b64table[(((((data[0] & 0x03) & 0xff) << 4) & 0xff) | + ((data[1] & 0xf0) >> 4))]; + result[2] = + b64table[((((data[1] & 0x0f) << 2) & 0xff) | + ((data[2] & 0xc0) >> 6))]; + result[3] = b64table[(data[2] & 0x3f) & 0xff]; + break; + case 2: + if ((c = ((data[0] & 0xf0) >> 4)) != 0) { + result[0] = b64table[c]; + result[1] = + b64table[((((data[0] & 0x0f) << 2) & 0xff) | + ((data[1] & 0xc0) >> 6))]; + result[2] = b64table[(data[1] & 0x3f) & 0xff]; + result[3] = '\0'; + ret -= 1; + } else { + if ((c = + ((data[0] & 0x0f) << 2) | ((data[1] & 0xc0) >> 6)) != 0) { + result[0] = b64table[c]; + result[1] = b64table[data[1] & 0x3f]; + result[2] = '\0'; + result[3] = '\0'; + ret -= 2; + } else { + result[0] = b64table[data[0] & 0x3f]; + result[1] = '\0'; + result[2] = '\0'; + result[3] = '\0'; + ret -= 3; + } + } + break; + case 1: + if ((c = ((data[0] & 0xc0) >> 6)) != 0) { + result[0] = b64table[c]; + result[1] = b64table[(data[0] & 0x3f) & 0xff]; + result[2] = '\0'; + result[3] = '\0'; + ret -= 2; + } else { + result[0] = b64table[(data[0] & 0x3f) & 0xff]; + result[1] = '\0'; + result[2] = '\0'; + result[3] = '\0'; + ret -= 3; + } + break; + default: + return -1; + } + + return ret; + +} + +/* encodes data and puts the result into result (locally allocated) + * The result_size is the return value + */ +int _gnutls_sbase64_encode(uint8 * data, size_t data_size, uint8 ** result) +{ + uint i, j; + int ret, tmp; + opaque tmpres[4]; + int mod = data_size % 3; + + ret = mod; + if (ret != 0) + ret = 4; + else + ret = 0; + + ret += (data_size * 4) / 3; + + (*result) = gnutls_calloc(1, ret + 1); + if ((*result) == NULL) + return -1; + + i = j = 0; +/* encode the bytes that are not a multiple of 3 + */ + if (mod > 0) { + tmp = encode(tmpres, &data[0], mod); + if (tmp < 0) { + gnutls_free((*result)); + return tmp; + } + + memcpy(&(*result)[0], tmpres, tmp); + i = mod; + j = tmp; + + } +/* encode the rest + */ + for (; i < data_size; i += 3, j += 4) { + tmp = encode(tmpres, &data[i], data_size - i); + if (tmp < 0) { + gnutls_free((*result)); + return tmp; + } + memcpy(&(*result)[j], tmpres, tmp); + } + + return strlen(*result); +} + + +/* data must be 4 bytes + * result should be 3 bytes + */ +#define TOASCII(c) (c < 127 ? asciitable[c] : 0xff) +inline static int decode(uint8 * result, const uint8 * data) +{ + uint8 a1, a2; + int ret = 3; + + memset(result, 0, 3); + + a1 = TOASCII(data[3]); + a2 = TOASCII(data[2]); + if (a1 != 0xff) + result[2] = a1 & 0xff; + else + return -1; + if (a2 != 0xff) + result[2] |= ((a2 & 0x03) << 6) & 0xff; + + a1 = a2; + a2 = TOASCII(data[1]); + if (a1 != 0xff) + result[1] = ((a1 & 0x3c) >> 2); + if (a2 != 0xff) + result[1] |= ((a2 & 0x0f) << 4); + else if (a1 == 0xff || result[1] == 0) + ret--; + + a1 = a2; + a2 = TOASCII(data[0]); + if (a1 != 0xff) + result[0] = (((a1 & 0x30) >> 4) & 0xff); + if (a2 != 0xff) + result[0] |= ((a2 << 2) & 0xff); + else if (a1 == 0xff || result[0] == 0) + ret--; + + return ret; +} + +/* decodes data and puts the result into result (locally allocated) + * The result_size is the return value. + * That function does not ignore newlines tabs etc. You should remove them + * before calling it. + */ +int _gnutls_sbase64_decode(uint8 * data, size_t idata_size, + uint8 ** result) +{ + uint i, j; + int ret, left; + int data_size, tmp; + uint8 datrev[4]; + uint8 tmpres[3]; + + data_size = (idata_size / 4) * 4; + left = idata_size % 4; + + ret = (data_size / 4) * 3; + + if (left > 0) + ret += 3; + + (*result) = gnutls_malloc(ret + 1); + if ((*result) == NULL) + return -1; + + /* the first "block" is treated with special care */ + tmp = 0; + if (left > 0) { + memset(datrev, 0, 4); + memcpy(&datrev[4 - left], data, left); + + tmp = decode(tmpres, datrev); + if (tmp < 0) { + gnutls_free((*result)); + *result = NULL; + return tmp; + } + + memcpy(*result, &tmpres[3 - tmp], tmp); + if (tmp < 3) + ret -= (3 - tmp); + } + + /* rest data */ + for (i = left, j = tmp; i < idata_size; i += 4) { + tmp = decode(tmpres, &data[i]); + if (tmp < 0) { + gnutls_free((*result)); + *result = NULL; + return tmp; + } + memcpy(&(*result)[j], tmpres, tmp); + if (tmp < 3) + ret -= (3 - tmp); + j += 3; + } + + return ret; +} + +/** + * gnutls_srp_base64_encode - This function will convert raw data to base64 encoded + * @data: contain the raw data + * @result: the place where base64 data will be copied + * @result_size: holds the size of the result + * + * This function will convert the given data to printable data, using the base64 + * encoding, as used in the libsrp. This is the encoding used in SRP password files. + * If the provided buffer is not long enough GNUTLS_E_SHORT_MEMORY_BUFFER is returned. + * + **/ +int gnutls_srp_base64_encode(const gnutls_datum_t * data, char *result, + int *result_size) +{ + opaque *ret; + int size; + + size = _gnutls_sbase64_encode(data->data, data->size, &ret); + if (size < 0) + return size; + + if (result == NULL || *result_size < size) { + gnutls_free(ret); + *result_size = size; + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } else { + memcpy(result, ret, size); + gnutls_free(ret); + *result_size = size; + } + + return 0; +} + +/** + * gnutls_srp_base64_encode_alloc - This function will convert raw data to Base64 encoded + * @data: contains the raw data + * @result: will hold the newly allocated encoded data + * + * This function will convert the given data to printable data, using the base64 + * encoding. This is the encoding used in SRP password files. This function will + * allocate the required memory to hold the encoded data. + * + * You should use gnutls_free() to free the returned data. + * + **/ +int gnutls_srp_base64_encode_alloc(const gnutls_datum_t * data, + gnutls_datum_t * result) +{ + opaque *ret; + int size; + + size = _gnutls_sbase64_encode(data->data, data->size, &ret); + if (size < 0) + return size; + + if (result == NULL) { + gnutls_free(ret); + return GNUTLS_E_INVALID_REQUEST; + } else { + result->data = ret; + result->size = size; + } + + return 0; +} + +/** + * gnutls_srp_base64_decode - This function will decode base64 encoded data + * @b64_data: contain the encoded data + * @result: the place where decoded data will be copied + * @result_size: holds the size of the result + * + * This function will decode the given encoded data, using the base64 encoding + * found in libsrp. + * + * Note that b64_data should be null terminated. + * + * Returns GNUTLS_E_SHORT_MEMORY_BUFFER if the buffer given is not long enough, + * or 0 on success. + **/ +int gnutls_srp_base64_decode(const gnutls_datum_t * b64_data, char *result, + int *result_size) +{ + opaque *ret; + int size; + + size = _gnutls_sbase64_decode(b64_data->data, b64_data->size, &ret); + if (size < 0) + return size; + + if (result == NULL || *result_size < size) { + gnutls_free(ret); + *result_size = size; + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } else { + memcpy(result, ret, size); + gnutls_free(ret); + *result_size = size; + } + + return 0; +} + +/** + * gnutls_srp_base64_decode_alloc - This function will decode base64 encoded data + * @b64_data: contains the encoded data + * @result: the place where decoded data lie + * + * This function will decode the given encoded data. The decoded data + * will be allocated, and stored into result. + * It will decode using the base64 algorithm found in libsrp. + * + * You should use gnutls_free() to free the returned data. + * + **/ +int gnutls_srp_base64_decode_alloc(const gnutls_datum_t * b64_data, + gnutls_datum_t * result) +{ + opaque *ret; + int size; + + size = _gnutls_sbase64_decode(b64_data->data, b64_data->size, &ret); + if (size < 0) + return size; + + if (result == NULL) { + gnutls_free(ret); + return GNUTLS_E_INVALID_REQUEST; + } else { + result->data = ret; + result->size = size; + } + + return 0; +} + +#endif /* ENABLE_SRP */ diff --git a/lib/ext_srp.c b/lib/ext_srp.c new file mode 100644 index 0000000000..8bb1cbc7de --- /dev/null +++ b/lib/ext_srp.c @@ -0,0 +1,152 @@ +/* + * 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 <ext_srp.h> + +#ifdef ENABLE_SRP + +#include "gnutls_auth_int.h" +#include "auth_srp.h" +#include "gnutls_errors.h" +#include "gnutls_algorithms.h" +#include <gnutls_num.h> + +int _gnutls_srp_recv_params(gnutls_session_t session, const opaque * data, + size_t _data_size) +{ + uint8 len; + ssize_t data_size = _data_size; + + if (_gnutls_kx_priority(session, GNUTLS_KX_SRP) < 0 && + _gnutls_kx_priority(session, GNUTLS_KX_SRP_DSS) < 0 && + _gnutls_kx_priority(session, GNUTLS_KX_SRP_RSA) < 0) { + /* algorithm was not allowed in this session + */ + return 0; + } + + if (session->security_parameters.entity == GNUTLS_SERVER) { + if (data_size > 0) { + len = data[0]; + DECR_LEN(data_size, len); + + if (sizeof + (session->security_parameters.extensions.srp_username) <= + len) { + gnutls_assert(); + return GNUTLS_E_ILLEGAL_SRP_USERNAME; + } + memcpy(session->security_parameters.extensions.srp_username, + &data[1], len); + session->security_parameters.extensions.srp_username[len] = 0; /* null terminated */ + } + } + return 0; +} + +/* Checks if the given cipher suite is an SRP one + */ +inline static int is_srp(cipher_suite_st suite) +{ + int kx = _gnutls_cipher_suite_get_kx_algo(&suite); + + if (IS_SRP_KX(kx)) + return 1; + return 0; +} + +/* returns data_size or a negative number on failure + * data is allocated locally + */ +int _gnutls_srp_send_params(gnutls_session_t session, opaque * data, + size_t data_size) +{ + uint len; + + if (_gnutls_kx_priority(session, GNUTLS_KX_SRP) < 0 && + _gnutls_kx_priority(session, GNUTLS_KX_SRP_DSS) < 0 && + _gnutls_kx_priority(session, GNUTLS_KX_SRP_RSA) < 0) { + /* algorithm was not allowed in this session + */ + return 0; + } + + /* this function sends the client extension data (username) */ + if (session->security_parameters.entity == GNUTLS_CLIENT) { + const gnutls_srp_client_credentials_t cred = + _gnutls_get_cred(session->key, GNUTLS_CRD_SRP, NULL); + + if (cred == NULL) + return 0; + + if (cred->username != NULL) { /* send username */ + len = GMIN(strlen(cred->username), 255); + + if (data_size < len + 1) { + gnutls_assert(); + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + data[0] = (uint8) len; + memcpy(&data[1], cred->username, len); + return len + 1; + } else if (cred->get_function != NULL) { + /* Try the callback + */ + char *username = NULL, *password = NULL; + + if (cred-> + get_function(session, + session->internals.handshake_restarted, + &username, &password) < 0 || username == NULL + || password == NULL) { + + if (session->internals.handshake_restarted) { + gnutls_assert(); + return GNUTLS_E_ILLEGAL_SRP_USERNAME; + } + + return 0; + } + + len = GMIN(strlen(username), 255); + + if (data_size < len + 1) { + gnutls_free(username); + gnutls_free(password); + gnutls_assert(); + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + session->internals.srp_username = username; + session->internals.srp_password = password; + + data[0] = (uint8) len; + memcpy(&data[1], username, len); + return len + 1; + } + } + return 0; +} + +#endif /* ENABLE_SRP */ diff --git a/lib/ext_srp.h b/lib/ext_srp.h new file mode 100644 index 0000000000..8f69518be9 --- /dev/null +++ b/lib/ext_srp.h @@ -0,0 +1,9 @@ +#ifdef ENABLE_SRP + +#define IS_SRP_KX(kx) ((kx == GNUTLS_KX_SRP || (kx == GNUTLS_KX_SRP_RSA) || \ + kx == GNUTLS_KX_SRP_DSS)?1:0) + +int _gnutls_srp_recv_params( gnutls_session_t state, const opaque* data, size_t data_size); +int _gnutls_srp_send_params( gnutls_session_t state, opaque* data, size_t); + +#endif diff --git a/lib/gnutls.h.in.in b/lib/gnutls.h.in.in index 26b6b7bb01..a391345413 100644 --- a/lib/gnutls.h.in.in +++ b/lib/gnutls.h.in.in @@ -497,3 +497,52 @@ void gnutls_openpgp_send_key(gnutls_session_t session, gnutls_openpgp_key_status int gnutls_fingerprint(gnutls_digest_algorithm_t algo, const gnutls_datum_t* data, void* result, size_t* result_size); + +/* SRP + */ + +typedef struct DSTRUCT* gnutls_srp_server_credentials_t; +typedef struct DSTRUCT* gnutls_srp_client_credentials_t; + +void gnutls_srp_free_client_credentials( gnutls_srp_client_credentials_t sc); +int gnutls_srp_allocate_client_credentials( gnutls_srp_client_credentials_t *sc); +int gnutls_srp_set_client_credentials( gnutls_srp_client_credentials_t res, char *username, char* password); + +void gnutls_srp_free_server_credentials( gnutls_srp_server_credentials_t sc); +int gnutls_srp_allocate_server_credentials( gnutls_srp_server_credentials_t *sc); +int gnutls_srp_set_server_credentials_file( gnutls_srp_server_credentials_t res, + const char *password_file, const char* password_conf_file); + +const char* gnutls_srp_server_get_username( gnutls_session_t state); + +int gnutls_srp_verifier( const char* username, const char* password, const gnutls_datum_t *salt, + const gnutls_datum_t* g, const gnutls_datum_t* n, + gnutls_datum_t * res); + +/* The static parameters defined in draft-ietf-tls-srp-05 + * Those should be used as input to gnutls_srp_verifier(). + */ +extern const gnutls_datum_t gnutls_srp_2048_group_prime; +extern const gnutls_datum_t gnutls_srp_2048_group_generator; + +extern const gnutls_datum_t gnutls_srp_1536_group_prime; +extern const gnutls_datum_t gnutls_srp_1536_group_generator; + +extern const gnutls_datum_t gnutls_srp_1024_group_prime; +extern const gnutls_datum_t gnutls_srp_1024_group_generator; + +typedef int gnutls_srp_server_credentials_function( + gnutls_session_t, + const char* username, gnutls_datum_t* salt, + gnutls_datum_t* verifier, gnutls_datum_t* generator, + gnutls_datum_t* prime +); +void gnutls_srp_set_server_credentials_function( + gnutls_srp_server_credentials_t, + gnutls_srp_server_credentials_function *); + +typedef int gnutls_srp_client_credentials_function(gnutls_session_t, unsigned int, + char **, char**); +void gnutls_srp_set_client_credentials_function( gnutls_srp_client_credentials_t, + gnutls_srp_client_credentials_function *); + diff --git a/lib/gnutls_algorithms.c b/lib/gnutls_algorithms.c index e0ee6000db..2c8d181ce7 100644 --- a/lib/gnutls_algorithms.c +++ b/lib/gnutls_algorithms.c @@ -217,6 +217,9 @@ extern mod_auth_st rsa_export_auth_struct; extern mod_auth_st dhe_rsa_auth_struct; extern mod_auth_st dhe_dss_auth_struct; extern mod_auth_st anon_auth_struct; +extern mod_auth_st srp_auth_struct; +extern mod_auth_st srp_rsa_auth_struct; +extern mod_auth_st srp_dss_auth_struct; #define MAX_KX_ALGOS 10 @@ -230,6 +233,10 @@ gnutls_kx_algo_entry _gnutls_kx_algorithms[MAX_KX_ALGOS] = { {"RSA EXPORT", GNUTLS_KX_RSA_EXPORT, &rsa_export_auth_struct, 0, 1}, {"DHE RSA", GNUTLS_KX_DHE_RSA, &dhe_rsa_auth_struct, 1, 0}, {"DHE DSS", GNUTLS_KX_DHE_DSS, &dhe_dss_auth_struct, 1, 0}, + + {"SRP DSS", GNUTLS_KX_SRP_DSS, &srp_dss_auth_struct, 0, 0}, + {"SRP RSA", GNUTLS_KX_SRP_RSA, &srp_rsa_auth_struct, 0, 0}, + {"SRP", GNUTLS_KX_SRP, &srp_auth_struct, 0, 0}, /* other algorithms are appended here by gnutls-extra * initialization function. */ diff --git a/lib/gnutls_extensions.c b/lib/gnutls_extensions.c index adfd109552..ee1cf1e86c 100644 --- a/lib/gnutls_extensions.c +++ b/lib/gnutls_extensions.c @@ -31,7 +31,8 @@ #include "ext_max_record.h" #include <ext_cert_type.h> #include <ext_server_name.h> -#include "gnutls_num.h" +#include <ext_srp.h> +#include <gnutls_num.h> /* Key Exchange Section */ #define GNUTLS_EXTENSION_ENTRY(type, ext_func_recv, ext_func_send) \ @@ -51,6 +52,9 @@ gnutls_extension_entry _gnutls_extensions[MAX_EXT_SIZE] = { GNUTLS_EXTENSION_ENTRY(GNUTLS_EXTENSION_SERVER_NAME, _gnutls_server_name_recv_params, _gnutls_server_name_send_params), + GNUTLS_EXTENSION_ENTRY(GNUTLS_EXTENSION_SRP, + _gnutls_srp_recv_params, + _gnutls_srp_send_params), {0, 0, 0, 0} }; 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 */ diff --git a/lib/gnutls_srp.h b/lib/gnutls_srp.h new file mode 100644 index 0000000000..1aeb6046a8 --- /dev/null +++ b/lib/gnutls_srp.h @@ -0,0 +1,15 @@ +#ifdef ENABLE_SRP + +int _gnutls_srp_gx(opaque *text, size_t textsize, opaque** result, mpi_t g, mpi_t prime, gnutls_alloc_function); +mpi_t _gnutls_calc_srp_B(mpi_t * ret_b, mpi_t g, mpi_t n, mpi_t v); +mpi_t _gnutls_calc_srp_u( mpi_t A, mpi_t B, size_t n_size); +mpi_t _gnutls_calc_srp_S1(mpi_t A, mpi_t b, mpi_t u, mpi_t v, mpi_t n); +mpi_t _gnutls_calc_srp_A(mpi_t *a, mpi_t g, mpi_t n); +mpi_t _gnutls_calc_srp_S2(mpi_t B, mpi_t g, mpi_t x, mpi_t a, mpi_t u, mpi_t n); +int _gnutls_calc_srp_x( char* username, char* password, opaque* salt, size_t salt_size, size_t* size, void* digest); +int _gnutls_srp_gn( opaque** ret_g, opaque** ret_n, int bits); + +/* g is defined to be 2 */ +#define SRP_MAX_HASH_SIZE 24 + +#endif |