tests: mini-key-material: avoid memory leak
[gnutls:gnutls.git] / tests / mini-key-material.c
1 /*
2  * Copyright (C) 2013 Nikos Mavrogiannopoulos
3  *
4  * This file is part of GnuTLS.
5  *
6  * GnuTLS is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuTLS is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with GnuTLS; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <stdio.h>
26 #include <stdlib.h>
27
28 #if defined(_WIN32) || !defined(ENABLE_ALPN)
29
30 int main(int argc, char **argv)
31 {
32         exit(77);
33 }
34
35 #else
36
37 #include <string.h>
38 #include <sys/types.h>
39 #include <netinet/in.h>
40 #include <sys/socket.h>
41 #include <sys/wait.h>
42 #include <arpa/inet.h>
43 #include <unistd.h>
44 #include <gnutls/gnutls.h>
45 #include <gnutls/dtls.h>
46
47 #include "utils.h"
48
49 static void terminate(void);
50
51 /* This program tests whether the gnutls_record_get_state() works as
52  * expected.
53  */
54
55 static void server_log_func(int level, const char *str)
56 {
57         fprintf(stderr, "server|<%d>| %s", level, str);
58 }
59
60 static void client_log_func(int level, const char *str)
61 {
62         fprintf(stderr, "client|<%d>| %s", level, str);
63 }
64
65 /* These are global */
66 static pid_t child;
67
68 /* A very basic DTLS client, with anonymous authentication, that negotiates SRTP
69  */
70
71 static void dump(const char *name, uint8_t *data, unsigned data_size)
72 {
73         unsigned i;
74
75         fprintf(stderr, "%s", name);
76         for (i=0;i<data_size;i++)
77                 fprintf(stderr, "%.2x", (unsigned)data[i]);
78         fprintf(stderr, "\n");
79 }
80
81 static void client(int fd)
82 {
83         gnutls_session_t session;
84         int ret;
85         gnutls_datum_t proto;
86         gnutls_anon_client_credentials_t anoncred;
87         gnutls_datum_t mac_key, iv, cipher_key;
88         gnutls_datum_t read_mac_key, read_iv, read_cipher_key;
89         unsigned char seq_number[8];
90         unsigned char key_material[512], *p;
91         unsigned block_size, hash_size, key_size, iv_size;
92         const char *err;
93         /* Need to enable anonymous KX specifically. */
94
95         global_init();
96
97         if (debug) {
98                 gnutls_global_set_log_function(client_log_func);
99                 gnutls_global_set_log_level(4711);
100         }
101
102         gnutls_anon_allocate_client_credentials(&anoncred);
103
104         /* Initialize TLS session
105          */
106         gnutls_init(&session, GNUTLS_CLIENT);
107
108         /* Use default priorities */
109         ret = gnutls_priority_set_direct(session,
110                                    "NONE:+VERS-TLS1.0:+AES-128-CBC:+SHA1:+SIGN-ALL:+COMP-NULL:+ANON-DH:+ANON-ECDH:+CURVE-ALL",
111                                    &err);
112         if (ret < 0) {
113                 fail("client: priority set failed (%s): %s\n",
114                      gnutls_strerror(ret), err);
115                 exit(1);
116         }
117
118         /* put the anonymous credentials to the current session
119          */
120         gnutls_credentials_set(session, GNUTLS_CRD_ANON, anoncred);
121
122         gnutls_transport_set_int(session, fd);
123
124         /* Perform the TLS handshake
125          */
126         do {
127                 ret = gnutls_handshake(session);
128         }
129         while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
130
131         if (ret < 0) {
132                 fail("client: Handshake failed: %s\n", strerror(ret));
133                 exit(1);
134         } else {
135                 if (debug)
136                         success("client: Handshake was completed\n");
137         }
138
139         if (debug)
140                 success("client: TLS version is: %s\n",
141                         gnutls_protocol_get_name
142                         (gnutls_protocol_get_version(session)));
143
144         ret = gnutls_cipher_get(session);
145         if (ret != GNUTLS_CIPHER_AES_128_CBC) {
146                 fprintf(stderr, "negotiated unexpected cipher: %s\n", gnutls_cipher_get_name(ret));
147                 exit(1);
148         }
149
150         ret = gnutls_mac_get(session);
151         if (ret != GNUTLS_MAC_SHA1) {
152                 fprintf(stderr, "negotiated unexpected mac: %s\n", gnutls_mac_get_name(ret));
153                 exit(1);
154         }
155
156         iv_size = 16;
157         hash_size = 20;
158         key_size = 16;
159         block_size = 2*hash_size + 2*key_size + 2 *iv_size;
160
161         ret = gnutls_prf(session, 13, "key expansion", 1, 0, NULL, block_size,
162                          (void*)key_material);
163         if (ret < 0) {
164                 fprintf(stderr, "error in %d\n", __LINE__);
165                 gnutls_perror(ret);
166                 exit(1);
167         }
168         p = key_material;
169
170         ret = gnutls_record_get_state(session, 0, &mac_key, &iv, &cipher_key, seq_number);
171         if (ret < 0) {
172                 fprintf(stderr, "error in %d\n", __LINE__);
173                 gnutls_perror(ret);
174                 exit(1);
175         }
176
177         ret = gnutls_record_get_state(session, 1, &read_mac_key, &read_iv, &read_cipher_key, seq_number);
178         if (ret < 0) {
179                 fprintf(stderr, "error in %d\n", __LINE__);
180                 gnutls_perror(ret);
181                 exit(1);
182         }
183
184         if (hash_size != mac_key.size || memcmp(p, mac_key.data, hash_size) != 0) {
185                 dump("MAC:", mac_key.data, mac_key.size);
186                 dump("Block:", key_material, block_size);
187                 fprintf(stderr, "error in %d\n", __LINE__);
188                 exit(1);
189         }
190         p+= hash_size;
191
192         if (hash_size != read_mac_key.size || memcmp(p, read_mac_key.data, hash_size) != 0) {
193                 dump("MAC:", read_mac_key.data, read_mac_key.size);
194                 dump("Block:", key_material, block_size);
195                 fprintf(stderr, "error in %d\n", __LINE__);
196                 exit(1);
197         }
198         p+= hash_size;
199
200         if (key_size != cipher_key.size || memcmp(p, cipher_key.data, key_size) != 0) {
201                 fprintf(stderr, "error in %d\n", __LINE__);
202                 exit(1);
203         }
204         p+= key_size;
205
206         if (key_size != read_cipher_key.size || memcmp(p, read_cipher_key.data, key_size) != 0) {
207                 fprintf(stderr, "error in %d\n", __LINE__);
208                 exit(1);
209         }
210         p+= key_size;
211
212         if (iv_size != iv.size || memcmp(p, iv.data, iv_size) != 0) {
213                 fprintf(stderr, "error in %d\n", __LINE__);
214                 exit(1);
215         }
216         p+=iv_size;
217
218         if (iv_size != read_iv.size || memcmp(p, read_iv.data, iv_size) != 0) {
219                 fprintf(stderr, "error in %d\n", __LINE__);
220                 exit(1);
221         }
222
223         /* check whether the key material matches our calculations */
224         
225
226         if (debug) {
227                 fprintf(stderr, "selected protocol: %.*s\n",
228                         (int) proto.size, proto.data);
229         }
230
231
232         gnutls_bye(session, GNUTLS_SHUT_WR);
233
234         close(fd);
235
236         gnutls_deinit(session);
237
238         gnutls_anon_free_client_credentials(anoncred);
239
240         gnutls_global_deinit();
241 }
242
243 static void terminate(void)
244 {
245         int status;
246
247         kill(child, SIGTERM);
248         wait(&status);
249         exit(1);
250 }
251
252 static void server(int fd)
253 {
254         int ret;
255         gnutls_session_t session;
256         gnutls_anon_server_credentials_t anoncred;
257         gnutls_dh_params_t dh_params;
258         const gnutls_datum_t p3 =
259             { (unsigned char *) pkcs3, strlen(pkcs3) };
260
261         /* this must be called once in the program
262          */
263         global_init();
264
265         if (debug) {
266                 gnutls_global_set_log_function(server_log_func);
267                 gnutls_global_set_log_level(4711);
268         }
269
270         gnutls_anon_allocate_server_credentials(&anoncred);
271         gnutls_dh_params_init(&dh_params);
272         gnutls_dh_params_import_pkcs3(dh_params, &p3, GNUTLS_X509_FMT_PEM);
273         gnutls_anon_set_server_dh_params(anoncred, dh_params);
274
275         gnutls_init(&session, GNUTLS_SERVER);
276
277         /* avoid calling all the priority functions, since the defaults
278          * are adequate.
279          */
280         ret = gnutls_priority_set_direct(session,
281                                    "NORMAL:+ANON-DH:+ANON-ECDH", NULL);
282         if (ret < 0) {
283                 fail("server: priority set failed (%s)\n\n",
284                      gnutls_strerror(ret));
285                 terminate();
286         }
287
288         gnutls_credentials_set(session, GNUTLS_CRD_ANON, anoncred);
289
290         gnutls_transport_set_int(session, fd);
291
292         do {
293                 ret = gnutls_handshake(session);
294         }
295         while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
296         if (ret < 0) {
297                 close(fd);
298                 gnutls_deinit(session);
299                 fail("server: Handshake has failed (%s)\n\n",
300                      gnutls_strerror(ret));
301                 terminate();
302         }
303         if (debug)
304                 success("server: Handshake was completed\n");
305
306         if (debug)
307                 success("server: TLS version is: %s\n",
308                         gnutls_protocol_get_name
309                         (gnutls_protocol_get_version(session)));
310
311         /* do not wait for the peer to close the connection.
312          */
313         gnutls_bye(session, GNUTLS_SHUT_WR);
314
315         close(fd);
316         gnutls_deinit(session);
317
318         gnutls_anon_free_server_credentials(anoncred);
319         gnutls_dh_params_deinit(dh_params);
320
321         gnutls_global_deinit();
322
323         if (debug)
324                 success("server: finished\n");
325 }
326
327 static void start(void)
328 {
329         int fd[2];
330         int ret;
331
332         ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
333         if (ret < 0) {
334                 perror("socketpair");
335                 exit(1);
336         }
337
338         child = fork();
339         if (child < 0) {
340                 perror("fork");
341                 fail("fork");
342                 exit(1);
343         }
344
345         if (child) {
346                 int status;
347                 /* parent */
348
349                 server(fd[0]);
350                 wait(&status);
351                 if (WEXITSTATUS(status) != 0)
352                         fail("Child died with status %d\n",
353                              WEXITSTATUS(status));
354         } else {
355                 close(fd[0]);
356                 client(fd[1]);
357                 exit(0);
358         }
359 }
360
361 void doit(void)
362 {
363         start();
364 }
365
366 #endif                          /* _WIN32 */