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
main(int argc,char ** argv)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
server_log_func(int level,const char * str)55 static void server_log_func(int level, const char *str)
56 {
57 fprintf(stderr, "server|<%d>| %s", level, str);
58 }
59
client_log_func(int level,const char * str)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
dump(const char * name,uint8_t * data,unsigned data_size)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
terminate(void)81 static void terminate(void)
82 {
83 int status = 0;
84
85 kill(child, SIGTERM);
86 wait(&status);
87 exit(1);
88 }
89
client(int fd)90 static void client(int fd)
91 {
92 gnutls_session_t session;
93 int ret;
94 gnutls_anon_client_credentials_t anoncred;
95 gnutls_datum_t mac_key, iv, cipher_key;
96 gnutls_datum_t read_mac_key, read_iv, read_cipher_key;
97 unsigned char rseq_number[8];
98 unsigned char wseq_number[8];
99 unsigned char key_material[512], *p;
100 unsigned i;
101 unsigned block_size, hash_size, key_size, iv_size;
102 const char *err;
103 /* Need to enable anonymous KX specifically. */
104
105 global_init();
106
107 if (debug) {
108 gnutls_global_set_log_function(client_log_func);
109 gnutls_global_set_log_level(4711);
110 }
111
112 gnutls_anon_allocate_client_credentials(&anoncred);
113
114 /* Initialize TLS session
115 */
116 gnutls_init(&session, GNUTLS_CLIENT);
117
118 /* Use default priorities */
119 ret = gnutls_priority_set_direct(session,
120 "NONE:+VERS-TLS1.0:+AES-128-CBC:+SHA1:+SIGN-ALL:+COMP-NULL:+ANON-DH:+ANON-ECDH:+CURVE-ALL",
121 &err);
122 if (ret < 0) {
123 fail("client: priority set failed (%s): %s\n",
124 gnutls_strerror(ret), err);
125 exit(1);
126 }
127
128 /* put the anonymous credentials to the current session
129 */
130 gnutls_credentials_set(session, GNUTLS_CRD_ANON, anoncred);
131
132 gnutls_transport_set_int(session, fd);
133
134 /* Perform the TLS handshake
135 */
136 do {
137 ret = gnutls_handshake(session);
138 }
139 while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
140
141 if (ret < 0) {
142 fail("client: Handshake failed: %s\n", strerror(ret));
143 terminate();
144 } else {
145 if (debug)
146 success("client: Handshake was completed\n");
147 }
148
149 if (debug)
150 success("client: TLS version is: %s\n",
151 gnutls_protocol_get_name
152 (gnutls_protocol_get_version(session)));
153
154 ret = gnutls_cipher_get(session);
155 if (ret != GNUTLS_CIPHER_AES_128_CBC) {
156 fprintf(stderr, "negotiated unexpected cipher: %s\n", gnutls_cipher_get_name(ret));
157 terminate();
158 }
159
160 ret = gnutls_mac_get(session);
161 if (ret != GNUTLS_MAC_SHA1) {
162 fprintf(stderr, "negotiated unexpected mac: %s\n", gnutls_mac_get_name(ret));
163 terminate();
164 }
165
166 iv_size = 16;
167 hash_size = 20;
168 key_size = 16;
169 block_size = 2*hash_size + 2*key_size + 2 *iv_size;
170
171 ret = gnutls_prf(session, 13, "key expansion", 1, 0, NULL, block_size,
172 (void*)key_material);
173 if (ret < 0) {
174 fprintf(stderr, "error in %d\n", __LINE__);
175 gnutls_perror(ret);
176 terminate();
177 }
178 p = key_material;
179
180 /* check whether the key material matches our calculations */
181 ret = gnutls_record_get_state(session, 0, &mac_key, &iv, &cipher_key, wseq_number);
182 if (ret < 0) {
183 fprintf(stderr, "error in %d\n", __LINE__);
184 gnutls_perror(ret);
185 terminate();
186 }
187
188 if (memcmp(wseq_number, "\x00\x00\x00\x00\x00\x00\x00\x01", 8) != 0) {
189 dump("wseq:", wseq_number, 8);
190 fprintf(stderr, "error in %d\n", __LINE__);
191 terminate();
192 }
193
194 ret = gnutls_record_get_state(session, 1, &read_mac_key, &read_iv, &read_cipher_key, rseq_number);
195 if (ret < 0) {
196 fprintf(stderr, "error in %d\n", __LINE__);
197 gnutls_perror(ret);
198 terminate();
199 }
200
201 if (memcmp(rseq_number, "\x00\x00\x00\x00\x00\x00\x00\x01", 8) != 0) {
202 dump("rseq:", rseq_number, 8);
203 fprintf(stderr, "error in %d\n", __LINE__);
204 terminate();
205 }
206
207 if (hash_size != mac_key.size || memcmp(p, mac_key.data, hash_size) != 0) {
208 dump("MAC:", mac_key.data, mac_key.size);
209 dump("Block:", key_material, block_size);
210 fprintf(stderr, "error in %d\n", __LINE__);
211 terminate();
212 }
213 p+= hash_size;
214
215 if (hash_size != read_mac_key.size || memcmp(p, read_mac_key.data, hash_size) != 0) {
216 dump("MAC:", read_mac_key.data, read_mac_key.size);
217 dump("Block:", key_material, block_size);
218 fprintf(stderr, "error in %d\n", __LINE__);
219 terminate();
220 }
221 p+= hash_size;
222
223 if (key_size != cipher_key.size || memcmp(p, cipher_key.data, key_size) != 0) {
224 fprintf(stderr, "error in %d\n", __LINE__);
225 terminate();
226 }
227 p+= key_size;
228
229 if (key_size != read_cipher_key.size || memcmp(p, read_cipher_key.data, key_size) != 0) {
230 fprintf(stderr, "error in %d\n", __LINE__);
231 terminate();
232 }
233 p+= key_size;
234
235 if (iv_size != iv.size || memcmp(p, iv.data, iv_size) != 0) {
236 fprintf(stderr, "error in %d\n", __LINE__);
237 terminate();
238 }
239 p+=iv_size;
240
241 if (iv_size != read_iv.size || memcmp(p, read_iv.data, iv_size) != 0) {
242 fprintf(stderr, "error in %d\n", __LINE__);
243 terminate();
244 }
245
246 /* check sequence numbers */
247 for (i=0;i<5;i++) {
248 ret = gnutls_record_send(session, "hello", 5);
249 if (ret < 0) {
250 fail("gnutls_record_send: %s\n", gnutls_strerror(ret));
251 }
252 }
253
254 ret = gnutls_record_get_state(session, 0, NULL, NULL, NULL, wseq_number);
255 if (ret < 0) {
256 fprintf(stderr, "error in %d\n", __LINE__);
257 gnutls_perror(ret);
258 terminate();
259 }
260
261 if (memcmp(wseq_number, "\x00\x00\x00\x00\x00\x00\x00\x06", 8) != 0) {
262 dump("wseq:", wseq_number, 8);
263 fprintf(stderr, "error in %d\n", __LINE__);
264 terminate();
265 }
266
267 ret = gnutls_record_get_state(session, 1, NULL, NULL, NULL, rseq_number);
268 if (ret < 0) {
269 fprintf(stderr, "error in %d\n", __LINE__);
270 gnutls_perror(ret);
271 terminate();
272 }
273
274 if (memcmp(rseq_number, "\x00\x00\x00\x00\x00\x00\x00\x01", 8) != 0) {
275 dump("wseq:", wseq_number, 8);
276 fprintf(stderr, "error in %d\n", __LINE__);
277 terminate();
278 }
279 gnutls_bye(session, GNUTLS_SHUT_WR);
280
281 close(fd);
282
283 gnutls_deinit(session);
284
285 gnutls_anon_free_client_credentials(anoncred);
286
287 gnutls_global_deinit();
288 }
289
server(int fd)290 static void server(int fd)
291 {
292 int ret;
293 gnutls_session_t session;
294 gnutls_anon_server_credentials_t anoncred;
295 gnutls_dh_params_t dh_params;
296 char buf[128];
297 const gnutls_datum_t p3 =
298 { (unsigned char *) pkcs3, strlen(pkcs3) };
299
300 /* this must be called once in the program
301 */
302 global_init();
303
304 if (debug) {
305 gnutls_global_set_log_function(server_log_func);
306 gnutls_global_set_log_level(4711);
307 }
308
309 gnutls_anon_allocate_server_credentials(&anoncred);
310 gnutls_dh_params_init(&dh_params);
311 gnutls_dh_params_import_pkcs3(dh_params, &p3, GNUTLS_X509_FMT_PEM);
312 gnutls_anon_set_server_dh_params(anoncred, dh_params);
313
314 gnutls_init(&session, GNUTLS_SERVER);
315
316 /* avoid calling all the priority functions, since the defaults
317 * are adequate.
318 */
319 ret = gnutls_priority_set_direct(session,
320 "NORMAL:+ANON-DH:+ANON-ECDH:-VERS-ALL:+VERS-TLS1.0", NULL);
321 if (ret < 0) {
322 fail("server: priority set failed (%s)\n\n",
323 gnutls_strerror(ret));
324 terminate();
325 }
326
327 gnutls_credentials_set(session, GNUTLS_CRD_ANON, anoncred);
328
329 gnutls_transport_set_int(session, fd);
330
331 do {
332 ret = gnutls_handshake(session);
333 }
334 while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
335 if (ret < 0) {
336 close(fd);
337 gnutls_deinit(session);
338 fail("server: Handshake has failed (%s)\n\n",
339 gnutls_strerror(ret));
340 terminate();
341 }
342 if (debug)
343 success("server: Handshake was completed\n");
344
345 if (debug)
346 success("server: TLS version is: %s\n",
347 gnutls_protocol_get_name
348 (gnutls_protocol_get_version(session)));
349
350 do {
351 ret = gnutls_record_recv(session, buf, sizeof(buf));
352 } while(ret > 0);
353
354 if (ret < 0) {
355 fail("error: %s\n", gnutls_strerror(ret));
356 }
357
358 /* do not wait for the peer to close the connection.
359 */
360 gnutls_bye(session, GNUTLS_SHUT_WR);
361
362 close(fd);
363 gnutls_deinit(session);
364
365 gnutls_anon_free_server_credentials(anoncred);
366 gnutls_dh_params_deinit(dh_params);
367
368 gnutls_global_deinit();
369
370 if (debug)
371 success("server: finished\n");
372 }
373
start(void)374 static void start(void)
375 {
376 int fd[2];
377 int ret;
378
379 ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
380 if (ret < 0) {
381 perror("socketpair");
382 exit(1);
383 }
384
385 child = fork();
386 if (child < 0) {
387 perror("fork");
388 fail("fork");
389 exit(1);
390 }
391
392 if (child) {
393 int status;
394 /* parent */
395
396 server(fd[0]);
397 wait(&status);
398 check_wait_status(status);
399 } else {
400 close(fd[0]);
401 client(fd[1]);
402 exit(0);
403 }
404 }
405
doit(void)406 void doit(void)
407 {
408 start();
409 }
410
411 #endif /* _WIN32 */
412