1/*
2 *  Simple DTLS client demonstration program
3 *
4 *  Copyright The Mbed TLS Contributors
5 *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
6 */
7
8#include "mbedtls/build_info.h"
9
10#include "mbedtls/platform.h"
11
12#if !defined(MBEDTLS_SSL_CLI_C) || !defined(MBEDTLS_SSL_PROTO_DTLS) ||    \
13    !defined(MBEDTLS_NET_C)  || !defined(MBEDTLS_TIMING_C) ||             \
14    !defined(MBEDTLS_ENTROPY_C) || !defined(MBEDTLS_CTR_DRBG_C) ||        \
15    !defined(MBEDTLS_X509_CRT_PARSE_C) || !defined(MBEDTLS_RSA_C) ||      \
16    !defined(MBEDTLS_PEM_PARSE_C)
17int main(void)
18{
19    mbedtls_printf("MBEDTLS_SSL_CLI_C and/or MBEDTLS_SSL_PROTO_DTLS and/or "
20                   "MBEDTLS_NET_C and/or MBEDTLS_TIMING_C and/or "
21                   "MBEDTLS_ENTROPY_C and/or MBEDTLS_CTR_DRBG_C and/or "
22                   "MBEDTLS_X509_CRT_PARSE_C and/or MBEDTLS_RSA_C and/or "
23                   "MBEDTLS_PEM_PARSE_C not defined.\n");
24    mbedtls_exit(0);
25}
26#else
27
28#include <string.h>
29
30#include "mbedtls/net_sockets.h"
31#include "mbedtls/debug.h"
32#include "mbedtls/ssl.h"
33#include "mbedtls/entropy.h"
34#include "mbedtls/ctr_drbg.h"
35#include "mbedtls/error.h"
36#include "mbedtls/timing.h"
37#include "test/certs.h"
38
39/* Uncomment out the following line to default to IPv4 and disable IPv6 */
40//#define FORCE_IPV4
41
42#define SERVER_PORT "4433"
43#define SERVER_NAME "localhost"
44
45#ifdef FORCE_IPV4
46#define SERVER_ADDR "127.0.0.1"     /* Forces IPv4 */
47#else
48#define SERVER_ADDR "::1"
49#endif
50
51#define MESSAGE     "Echo this"
52
53#define READ_TIMEOUT_MS 1000
54#define MAX_RETRY       5
55
56#define DEBUG_LEVEL 0
57
58
59static void my_debug(void *ctx, int level,
60                     const char *file, int line,
61                     const char *str)
62{
63    ((void) level);
64
65    mbedtls_fprintf((FILE *) ctx, "%s:%04d: %s", file, line, str);
66    fflush((FILE *) ctx);
67}
68
69int main(int argc, char *argv[])
70{
71    int ret, len;
72    mbedtls_net_context server_fd;
73    uint32_t flags;
74    unsigned char buf[1024];
75    const char *pers = "dtls_client";
76    int retry_left = MAX_RETRY;
77
78    mbedtls_entropy_context entropy;
79    mbedtls_ctr_drbg_context ctr_drbg;
80    mbedtls_ssl_context ssl;
81    mbedtls_ssl_config conf;
82    mbedtls_x509_crt cacert;
83    mbedtls_timing_delay_context timer;
84
85    ((void) argc);
86    ((void) argv);
87
88#if defined(MBEDTLS_DEBUG_C)
89    mbedtls_debug_set_threshold(DEBUG_LEVEL);
90#endif
91
92    /*
93     * 0. Initialize the RNG and the session data
94     */
95    mbedtls_net_init(&server_fd);
96    mbedtls_ssl_init(&ssl);
97    mbedtls_ssl_config_init(&conf);
98    mbedtls_x509_crt_init(&cacert);
99    mbedtls_ctr_drbg_init(&ctr_drbg);
100    mbedtls_entropy_init(&entropy);
101
102#if defined(MBEDTLS_USE_PSA_CRYPTO)
103    psa_status_t status = psa_crypto_init();
104    if (status != PSA_SUCCESS) {
105        mbedtls_fprintf(stderr, "Failed to initialize PSA Crypto implementation: %d\n",
106                        (int) status);
107        ret = MBEDTLS_ERR_SSL_HW_ACCEL_FAILED;
108        goto exit;
109    }
110#endif /* MBEDTLS_USE_PSA_CRYPTO */
111
112    mbedtls_printf("\n  . Seeding the random number generator...");
113    fflush(stdout);
114
115    if ((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
116                                     (const unsigned char *) pers,
117                                     strlen(pers))) != 0) {
118        mbedtls_printf(" failed\n  ! mbedtls_ctr_drbg_seed returned %d\n", ret);
119        goto exit;
120    }
121
122    mbedtls_printf(" ok\n");
123
124    /*
125     * 0. Load certificates
126     */
127    mbedtls_printf("  . Loading the CA root certificate ...");
128    fflush(stdout);
129
130    ret = mbedtls_x509_crt_parse(&cacert, (const unsigned char *) mbedtls_test_cas_pem,
131                                 mbedtls_test_cas_pem_len);
132    if (ret < 0) {
133        mbedtls_printf(" failed\n  !  mbedtls_x509_crt_parse returned -0x%x\n\n",
134                       (unsigned int) -ret);
135        goto exit;
136    }
137
138    mbedtls_printf(" ok (%d skipped)\n", ret);
139
140    /*
141     * 1. Start the connection
142     */
143    mbedtls_printf("  . Connecting to udp/%s/%s...", SERVER_NAME, SERVER_PORT);
144    fflush(stdout);
145
146    if ((ret = mbedtls_net_connect(&server_fd, SERVER_ADDR,
147                                   SERVER_PORT, MBEDTLS_NET_PROTO_UDP)) != 0) {
148        mbedtls_printf(" failed\n  ! mbedtls_net_connect returned %d\n\n", ret);
149        goto exit;
150    }
151
152    mbedtls_printf(" ok\n");
153
154    /*
155     * 2. Setup stuff
156     */
157    mbedtls_printf("  . Setting up the DTLS structure...");
158    fflush(stdout);
159
160    if ((ret = mbedtls_ssl_config_defaults(&conf,
161                                           MBEDTLS_SSL_IS_CLIENT,
162                                           MBEDTLS_SSL_TRANSPORT_DATAGRAM,
163                                           MBEDTLS_SSL_PRESET_DEFAULT)) != 0) {
164        mbedtls_printf(" failed\n  ! mbedtls_ssl_config_defaults returned %d\n\n", ret);
165        goto exit;
166    }
167
168    /* OPTIONAL is usually a bad choice for security, but makes interop easier
169     * in this simplified example, in which the ca chain is hardcoded.
170     * Production code should set a proper ca chain and use REQUIRED. */
171    mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_OPTIONAL);
172    mbedtls_ssl_conf_ca_chain(&conf, &cacert, NULL);
173    mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg);
174    mbedtls_ssl_conf_dbg(&conf, my_debug, stdout);
175    mbedtls_ssl_conf_read_timeout(&conf, READ_TIMEOUT_MS);
176
177    if ((ret = mbedtls_ssl_setup(&ssl, &conf)) != 0) {
178        mbedtls_printf(" failed\n  ! mbedtls_ssl_setup returned %d\n\n", ret);
179        goto exit;
180    }
181
182    if ((ret = mbedtls_ssl_set_hostname(&ssl, SERVER_NAME)) != 0) {
183        mbedtls_printf(" failed\n  ! mbedtls_ssl_set_hostname returned %d\n\n", ret);
184        goto exit;
185    }
186
187    mbedtls_ssl_set_bio(&ssl, &server_fd,
188                        mbedtls_net_send, mbedtls_net_recv, mbedtls_net_recv_timeout);
189
190    mbedtls_ssl_set_timer_cb(&ssl, &timer, mbedtls_timing_set_delay,
191                             mbedtls_timing_get_delay);
192
193    mbedtls_printf(" ok\n");
194
195    /*
196     * 4. Handshake
197     */
198    mbedtls_printf("  . Performing the DTLS handshake...");
199    fflush(stdout);
200
201    do {
202        ret = mbedtls_ssl_handshake(&ssl);
203    } while (ret == MBEDTLS_ERR_SSL_WANT_READ ||
204             ret == MBEDTLS_ERR_SSL_WANT_WRITE);
205
206    if (ret != 0) {
207        mbedtls_printf(" failed\n  ! mbedtls_ssl_handshake returned -0x%x\n\n",
208                       (unsigned int) -ret);
209        goto exit;
210    }
211
212    mbedtls_printf(" ok\n");
213
214    /*
215     * 5. Verify the server certificate
216     */
217    mbedtls_printf("  . Verifying peer X.509 certificate...");
218
219    /* In real life, we would have used MBEDTLS_SSL_VERIFY_REQUIRED so that the
220     * handshake would not succeed if the peer's cert is bad.  Even if we used
221     * MBEDTLS_SSL_VERIFY_OPTIONAL, we would bail out here if ret != 0 */
222    if ((flags = mbedtls_ssl_get_verify_result(&ssl)) != 0) {
223#if !defined(MBEDTLS_X509_REMOVE_INFO)
224        char vrfy_buf[512];
225#endif
226
227        mbedtls_printf(" failed\n");
228
229#if !defined(MBEDTLS_X509_REMOVE_INFO)
230        mbedtls_x509_crt_verify_info(vrfy_buf, sizeof(vrfy_buf), "  ! ", flags);
231
232        mbedtls_printf("%s\n", vrfy_buf);
233#endif
234    } else {
235        mbedtls_printf(" ok\n");
236    }
237
238    /*
239     * 6. Write the echo request
240     */
241send_request:
242    mbedtls_printf("  > Write to server:");
243    fflush(stdout);
244
245    len = sizeof(MESSAGE) - 1;
246
247    do {
248        ret = mbedtls_ssl_write(&ssl, (unsigned char *) MESSAGE, len);
249    } while (ret == MBEDTLS_ERR_SSL_WANT_READ ||
250             ret == MBEDTLS_ERR_SSL_WANT_WRITE);
251
252    if (ret < 0) {
253        mbedtls_printf(" failed\n  ! mbedtls_ssl_write returned %d\n\n", ret);
254        goto exit;
255    }
256
257    len = ret;
258    mbedtls_printf(" %d bytes written\n\n%s\n\n", len, MESSAGE);
259
260    /*
261     * 7. Read the echo response
262     */
263    mbedtls_printf("  < Read from server:");
264    fflush(stdout);
265
266    len = sizeof(buf) - 1;
267    memset(buf, 0, sizeof(buf));
268
269    do {
270        ret = mbedtls_ssl_read(&ssl, buf, len);
271    } while (ret == MBEDTLS_ERR_SSL_WANT_READ ||
272             ret == MBEDTLS_ERR_SSL_WANT_WRITE);
273
274    if (ret <= 0) {
275        switch (ret) {
276            case MBEDTLS_ERR_SSL_TIMEOUT:
277                mbedtls_printf(" timeout\n\n");
278                if (retry_left-- > 0) {
279                    goto send_request;
280                }
281                goto exit;
282
283            case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY:
284                mbedtls_printf(" connection was closed gracefully\n");
285                goto close_notify;
286
287            default:
288                mbedtls_printf(" mbedtls_ssl_read returned -0x%x\n\n", (unsigned int) -ret);
289                goto exit;
290        }
291    }
292
293    len = ret;
294    mbedtls_printf(" %d bytes read\n\n%s\n\n", len, buf);
295
296    /*
297     * 8. Done, cleanly close the connection
298     */
299close_notify:
300    mbedtls_printf("  . Closing the connection...");
301
302    /* No error checking, the connection might be closed already */
303    do {
304        ret = mbedtls_ssl_close_notify(&ssl);
305    } while (ret == MBEDTLS_ERR_SSL_WANT_WRITE);
306    ret = 0;
307
308    mbedtls_printf(" done\n");
309
310    /*
311     * 9. Final clean-ups and exit
312     */
313exit:
314
315#ifdef MBEDTLS_ERROR_C
316    if (ret != 0) {
317        char error_buf[100];
318        mbedtls_strerror(ret, error_buf, 100);
319        mbedtls_printf("Last error was: %d - %s\n\n", ret, error_buf);
320    }
321#endif
322
323    mbedtls_net_free(&server_fd);
324    mbedtls_x509_crt_free(&cacert);
325    mbedtls_ssl_free(&ssl);
326    mbedtls_ssl_config_free(&conf);
327    mbedtls_ctr_drbg_free(&ctr_drbg);
328    mbedtls_entropy_free(&entropy);
329#if defined(MBEDTLS_USE_PSA_CRYPTO)
330    mbedtls_psa_crypto_free();
331#endif /* MBEDTLS_USE_PSA_CRYPTO */
332
333    /* Shell can not handle large exit numbers -> 1 for errors */
334    if (ret < 0) {
335        ret = 1;
336    }
337
338    mbedtls_exit(ret);
339}
340#endif /* MBEDTLS_SSL_CLI_C && MBEDTLS_SSL_PROTO_DTLS && MBEDTLS_NET_C &&
341          MBEDTLS_TIMING_C && MBEDTLS_ENTROPY_C && MBEDTLS_CTR_DRBG_C &&
342          MBEDTLS_X509_CRT_PARSE_C && MBEDTLS_RSA_C && MBEDTLS_PEM_PARSE_C */
343