Correctly restore gnutls_record_recv() in DTLS mode if interrupted during the retrasm...
[gnutls.git] / lib / auth / psk.c
blob34d95deccea60b7360a27417d74862f0fd4e08ad
1 /*
2 * Copyright (C) 2005-2012 Free Software Foundation, Inc.
4 * Author: Nikos Mavrogiannopoulos
6 * This file is part of GnuTLS.
8 * The GnuTLS is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * as published by the Free Software Foundation; either version 3 of
11 * the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>
23 #include <gnutls_int.h>
25 #ifdef ENABLE_PSK
27 #include "gnutls_errors.h"
28 #include "gnutls_auth.h"
29 #include "gnutls_auth.h"
30 #include "debug.h"
31 #include "gnutls_num.h"
32 #include <auth/psk.h>
33 #include <auth/psk_passwd.h>
34 #include <gnutls_str.h>
35 #include <gnutls_datum.h>
37 int _gnutls_gen_psk_server_kx (gnutls_session_t session, gnutls_buffer_st* data);
38 int _gnutls_gen_psk_client_kx (gnutls_session_t, gnutls_buffer_st*);
40 int _gnutls_proc_psk_client_kx (gnutls_session_t, uint8_t *, size_t);
42 int _gnutls_proc_psk_server_kx (gnutls_session_t session, uint8_t * data,
43 size_t _data_size);
45 const mod_auth_st psk_auth_struct = {
46 "PSK",
47 NULL,
48 NULL,
49 _gnutls_gen_psk_server_kx,
50 _gnutls_gen_psk_client_kx,
51 NULL,
52 NULL,
54 NULL,
55 NULL, /* certificate */
56 _gnutls_proc_psk_server_kx,
57 _gnutls_proc_psk_client_kx,
58 NULL,
59 NULL
62 /* Set the PSK premaster secret.
64 int
65 _gnutls_set_psk_session_key (gnutls_session_t session,
66 gnutls_datum_t * ppsk /* key */,
67 gnutls_datum_t * dh_secret)
69 gnutls_datum_t pwd_psk = { NULL, 0 };
70 size_t dh_secret_size;
71 int ret;
73 if (dh_secret == NULL)
74 dh_secret_size = ppsk->size;
75 else
76 dh_secret_size = dh_secret->size;
78 /* set the session key
80 session->key->key.size = 4 + dh_secret_size + ppsk->size;
81 session->key->key.data = gnutls_malloc (session->key->key.size);
82 if (session->key->key.data == NULL)
84 gnutls_assert ();
85 ret = GNUTLS_E_MEMORY_ERROR;
86 goto error;
89 /* format of the premaster secret:
90 * (uint16_t) psk_size
91 * psk_size bytes of (0)s
92 * (uint16_t) psk_size
93 * the psk
95 _gnutls_write_uint16 (dh_secret_size, session->key->key.data);
96 if (dh_secret == NULL)
97 memset (&session->key->key.data[2], 0, dh_secret_size);
98 else
99 memcpy (&session->key->key.data[2], dh_secret->data, dh_secret->size);
100 _gnutls_write_datum16 (&session->key->key.data[dh_secret_size + 2], *ppsk);
102 ret = 0;
104 error:
105 _gnutls_free_datum (&pwd_psk);
106 return ret;
109 /* returns the username and they key for the PSK session.
110 * Free is non (0) if they have to be freed.
112 int _gnutls_find_psk_key( gnutls_session_t session, gnutls_psk_client_credentials_t cred,
113 gnutls_datum_t * username, gnutls_datum_t* key, int* free)
115 char* user_p;
116 int ret;
118 *free = 0;
120 if (cred->username.data != NULL && cred->key.data != NULL)
122 username->data = cred->username.data;
123 username->size = cred->username.size;
124 key->data = cred->key.data;
125 key->size = cred->key.size;
127 else if (cred->get_function != NULL)
129 ret = cred->get_function (session, &user_p, key);
130 if (ret)
131 return gnutls_assert_val(ret);
133 username->data = (uint8_t*)user_p;
134 username->size = strlen(user_p);
136 *free = 1;
138 else
139 return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);
141 return 0;
145 /* Generates the PSK client key exchange
148 * struct {
149 * select (KeyExchangeAlgorithm) {
150 * uint8_t psk_identity<0..2^16-1>;
151 * } exchange_keys;
152 * } ClientKeyExchange;
156 _gnutls_gen_psk_client_kx (gnutls_session_t session, gnutls_buffer_st* data)
158 int ret, free;
159 gnutls_datum_t username;
160 gnutls_datum_t key;
161 gnutls_psk_client_credentials_t cred;
163 cred = (gnutls_psk_client_credentials_t)
164 _gnutls_get_cred (session->key, GNUTLS_CRD_PSK, NULL);
166 if (cred == NULL)
168 gnutls_assert ();
169 return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
172 ret = _gnutls_find_psk_key( session, cred, &username, &key, &free);
173 if (ret < 0)
174 return gnutls_assert_val(ret);
176 ret = _gnutls_set_psk_session_key (session, &key, NULL);
177 if (ret < 0)
179 gnutls_assert();
180 goto cleanup;
183 ret = _gnutls_buffer_append_data_prefix(data, 16, username.data, username.size);
184 if (ret < 0)
186 gnutls_assert();
189 cleanup:
190 if (free)
192 gnutls_free(username.data);
193 gnutls_free(key.data);
196 return ret;
200 /* just read the username from the client key exchange.
203 _gnutls_proc_psk_client_kx (gnutls_session_t session, uint8_t * data,
204 size_t _data_size)
206 ssize_t data_size = _data_size;
207 int ret;
208 gnutls_datum_t username, psk_key;
209 gnutls_psk_server_credentials_t cred;
210 psk_auth_info_t info;
212 cred = (gnutls_psk_server_credentials_t)
213 _gnutls_get_cred (session->key, GNUTLS_CRD_PSK, NULL);
215 if (cred == NULL)
217 gnutls_assert ();
218 return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
221 if ((ret =
222 _gnutls_auth_info_set (session, GNUTLS_CRD_PSK,
223 sizeof (psk_auth_info_st), 1)) < 0)
225 gnutls_assert ();
226 return ret;
229 DECR_LEN (data_size, 2);
230 username.size = _gnutls_read_uint16 (&data[0]);
232 DECR_LEN (data_size, username.size);
234 username.data = &data[2];
237 /* copy the username to the auth info structures
239 info = _gnutls_get_auth_info (session);
241 if (username.size > MAX_USERNAME_SIZE)
243 gnutls_assert ();
244 return GNUTLS_E_ILLEGAL_SRP_USERNAME;
247 memcpy (info->username, username.data, username.size);
248 info->username[username.size] = 0;
250 ret = _gnutls_psk_pwd_find_entry(session, info->username, &psk_key);
251 if (ret < 0)
252 return gnutls_assert_val(ret);
254 ret = _gnutls_set_psk_session_key (session, &psk_key, NULL);
255 if (ret < 0)
257 gnutls_assert ();
258 goto error;
261 ret = 0;
263 error:
264 _gnutls_free_datum(&psk_key);
266 return ret;
270 /* Generates the PSK server key exchange
272 * struct {
273 * select (KeyExchangeAlgorithm) {
274 * // other cases for rsa, diffie_hellman, etc.
275 * case psk: // NEW
276 * uint8_t psk_identity_hint<0..2^16-1>;
277 * };
278 * } ServerKeyExchange;
282 _gnutls_gen_psk_server_kx (gnutls_session_t session, gnutls_buffer_st* data)
284 gnutls_psk_server_credentials_t cred;
285 gnutls_datum_t hint;
287 cred = (gnutls_psk_server_credentials_t)
288 _gnutls_get_cred (session->key, GNUTLS_CRD_PSK, NULL);
290 if (cred == NULL)
292 gnutls_assert ();
293 return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
296 /* Abort sending this message if there is no PSK identity hint. */
297 if (cred->hint == NULL)
299 gnutls_assert ();
300 return GNUTLS_E_INT_RET_0;
303 hint.data = (uint8_t*)cred->hint;
304 hint.size = strlen (cred->hint);
306 return _gnutls_buffer_append_data_prefix(data, 16, hint.data, hint.size);
310 /* just read the hint from the server key exchange.
313 _gnutls_proc_psk_server_kx (gnutls_session_t session, uint8_t * data,
314 size_t _data_size)
316 ssize_t data_size = _data_size;
317 int ret;
318 gnutls_datum_t hint;
319 gnutls_psk_client_credentials_t cred;
320 psk_auth_info_t info;
322 cred = (gnutls_psk_client_credentials_t)
323 _gnutls_get_cred (session->key, GNUTLS_CRD_PSK, NULL);
325 if (cred == NULL)
327 gnutls_assert ();
328 return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
331 if ((ret =
332 _gnutls_auth_info_set (session, GNUTLS_CRD_PSK,
333 sizeof (psk_auth_info_st), 1)) < 0)
335 gnutls_assert ();
336 return ret;
339 DECR_LENGTH_RET (data_size, 2, 0);
340 hint.size = _gnutls_read_uint16 (&data[0]);
342 DECR_LEN (data_size, hint.size);
344 hint.data = &data[2];
346 /* copy the hint to the auth info structures
348 info = _gnutls_get_auth_info (session);
350 if (hint.size > MAX_USERNAME_SIZE)
352 gnutls_assert ();
353 return GNUTLS_E_ILLEGAL_SRP_USERNAME;
356 memcpy (info->hint, hint.data, hint.size);
357 info->hint[hint.size] = 0;
359 ret = _gnutls_set_psk_session_key (session, &cred->key, NULL);
360 if (ret < 0)
362 gnutls_assert ();
363 goto error;
366 ret = 0;
368 error:
369 return ret;
372 #endif /* ENABLE_PSK */