3 * Copyright (c) 2019 Apple Computer, Inc. All rights reserved.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 * DNS SIG(0) signature verification for DNSSD SRP using mbedtls.
19 * Provides functions for generating a public key validating context based on SIG(0) KEY RR data, and
20 * validating a signature using a context generated with that public key. Currently only ECDSASHA256 is
25 #include <arpa/inet.h>
32 #define SRP_CRYPTO_MBEDTLS_INTERNAL
34 #include "srp-crypto.h"
38 // Context that is shared amongs all TLS connections, regardless of which server cert/key is in use.
39 mbedtls_entropy_context entropy
;
40 mbedtls_ctr_drbg_context ctr_drbg
;
42 // For now, assume that we are using just one key and one server cert, plus the ca cert. Consequently, we
43 // can treat this as global state. If wanted later, we could make this its own structure.
44 mbedtls_x509_crt cacert_struct
, *cacert
= NULL
;
45 mbedtls_x509_crt srvcert_struct
, *srvcert
= NULL
;
46 mbedtls_pk_context srvkey
;
47 mbedtls_ssl_config tls_server_config
;
48 mbedtls_ssl_config tls_client_config
;
55 // Initialize the shared data structures.
56 mbedtls_x509_crt_init(&srvcert_struct
);
57 mbedtls_pk_init(&srvkey
);
58 mbedtls_entropy_init(&entropy
);
59 mbedtls_ctr_drbg_init(&ctr_drbg
);
61 status
= mbedtls_ctr_drbg_seed(&ctr_drbg
, mbedtls_entropy_func
, &entropy
, NULL
, 0);
63 ERROR("Unable to seed RNG: %x", -status
);
70 mbedtls_config_init(mbedtls_ssl_config
*config
, int flags
)
72 int status
= mbedtls_ssl_config_defaults(config
, flags
,
73 MBEDTLS_SSL_TRANSPORT_STREAM
, MBEDTLS_SSL_PRESET_DEFAULT
);
75 ERROR("Unable to set up default TLS config state: %x", -status
);
79 mbedtls_ssl_conf_rng(config
, mbedtls_ctr_drbg_random
, &ctr_drbg
);
84 srp_tls_client_init(void)
86 if (!mbedtls_config_init(&tls_client_config
, MBEDTLS_SSL_IS_CLIENT
)) {
93 srp_tls_server_init(const char *cacert_file
, const char *srvcert_file
, const char *server_key_file
)
97 // Load the public key and cert
98 if (cacert_file
!= NULL
) {
99 status
= mbedtls_x509_crt_parse_file(&cacert_struct
, cacert_file
);
101 ERROR("Unable to parse ca cert file: %x", -status
);
104 cacert
= &cacert_struct
;
107 if (srvcert_file
!= NULL
) {
108 status
= mbedtls_x509_crt_parse_file(&srvcert_struct
, srvcert_file
);
110 ERROR("Unable to parse server cert file: %x", -status
);
113 srvcert
= &srvcert_struct
;
114 if (srvcert_struct
.next
&& cacert
!= NULL
) {
115 cacert
= srvcert_struct
.next
;
119 if (server_key_file
!= NULL
) {
120 status
= mbedtls_pk_parse_keyfile(&srvkey
, server_key_file
, NULL
);
122 ERROR("Unable to parse server cert file: %x", -status
);
127 if (!mbedtls_config_init(&tls_server_config
, MBEDTLS_SSL_IS_SERVER
)) {
131 if (cacert
!= NULL
) {
132 mbedtls_ssl_conf_ca_chain(&tls_server_config
, cacert
, NULL
);
135 status
= mbedtls_ssl_conf_own_cert(&tls_server_config
, srvcert
, &srvkey
);
137 ERROR("Unable to configure own cert: %x", -status
);
144 srp_tls_io_send(void *ctx
, const unsigned char *buf
, size_t len
)
148 ret
= write(comm
->io
.sock
, buf
, len
);
150 if (errno
== EAGAIN
) {
151 return MBEDTLS_ERR_SSL_WANT_WRITE
;
153 return MBEDTLS_ERR_SSL_INTERNAL_ERROR
;
161 srp_tls_io_recv(void *ctx
, unsigned char *buf
, size_t max
)
165 ret
= read(comm
->io
.sock
, buf
, max
);
167 if (errno
== EWOULDBLOCK
|| errno
== EAGAIN
) {
168 return MBEDTLS_ERR_SSL_WANT_READ
;
170 return MBEDTLS_ERR_SSL_INTERNAL_ERROR
;
172 } else if (ret
== 0) {
173 return MBEDTLS_ERR_SSL_CONN_EOF
;
180 srp_tls_listen_callback(comm_t
*comm
)
184 // Allocate the TLS config and state structures.
185 comm
->tls_context
= calloc(1, sizeof *comm
->tls_context
);
186 if (comm
->tls_context
== NULL
) {
189 status
= mbedtls_ssl_setup(&comm
->tls_context
->context
, &tls_server_config
);
191 ERROR("Unable to set up TLS listener state: %x", -status
);
195 // Set up the I/O functions.
196 mbedtls_ssl_set_bio(&comm
->tls_context
->context
, comm
, srp_tls_io_send
, srp_tls_io_recv
, NULL
);
198 // Start the TLS handshake.
199 status
= mbedtls_ssl_handshake(&comm
->tls_context
->context
);
200 if (status
!= 0 && status
!= MBEDTLS_ERR_SSL_WANT_READ
&& status
!= MBEDTLS_ERR_SSL_WANT_WRITE
) {
201 ERROR("TLS handshake failed: %x", -status
);
202 srp_tls_context_free(comm
);
203 ioloop_close(&comm
->io
);
209 srp_tls_connect_callback(comm_t
*comm
)
213 // Allocate the TLS config and state structures.
214 comm
->tls_context
= calloc(1, sizeof *comm
->tls_context
);
215 if (comm
->tls_context
== NULL
) {
218 status
= mbedtls_ssl_setup(&comm
->tls_context
->context
, &tls_client_config
);
220 ERROR("Unable to set up TLS connect state: %x", -status
);
224 // Set up the I/O functions.
225 mbedtls_ssl_set_bio(&comm
->tls_context
->context
, comm
, srp_tls_io_send
, srp_tls_io_recv
, NULL
);
227 // Start the TLS handshake.
228 status
= mbedtls_ssl_handshake(&comm
->tls_context
->context
);
229 if (status
!= 0 && status
!= MBEDTLS_ERR_SSL_WANT_READ
&& status
!= MBEDTLS_ERR_SSL_WANT_WRITE
) {
230 ERROR("TLS handshake failed: %x", -status
);
231 srp_tls_context_free(comm
);
232 ioloop_close(&comm
->io
);
238 srp_tls_read(comm_t
*comm
, unsigned char *buf
, size_t max
)
240 int ret
= mbedtls_ssl_read(&comm
->tls_context
->context
, buf
, max
);
243 case MBEDTLS_ERR_SSL_WANT_READ
:
245 case MBEDTLS_ERR_SSL_WANT_WRITE
:
246 ERROR("Got SSL want write in TLS read!");
247 // This means we got EWOULDBLOCK on a write operation.
248 // Not implemented yet, but the right way to handle this is probably to
249 // deselect read events until the socket is ready to write, then write,
250 // and then re-enable read events. What we don't want is to keep calling
251 // read, because that will spin.
253 case MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS
:
254 ERROR("Got async in progress in TLS read!");
255 // No idea how to handle this yet.
257 #ifdef MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS
258 case MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS
:
259 ERROR("Got crypto in progress in TLS read!");
260 // No idea how to handle this.
264 ERROR("Unexpected response from SSL read: %x", -ret
);
268 // mbedtls returns 0 for EOF, just like read(), but we need a different signal,
269 // so we treat 0 as an error (for now). In principle, we should get a notification
270 // when the remote end is done writing, so a clean close should be different than
280 srp_tls_context_free(comm_t
*comm
)
282 // Free any state that the TLS library allocated
283 mbedtls_ssl_free(&comm
->tls_context
->context
);
284 // Free and forget the context data structure
285 free(comm
->tls_context
);
286 comm
->tls_context
= 0;
290 srp_tls_write(comm_t
*comm
, struct iovec
*iov
, int iov_len
)
294 int bytes_written
= 0;
295 for (i
= 0; i
< iov_len
; i
++) {
296 ret
= mbedtls_ssl_write(&comm
->tls_context
->context
, iov
[i
].iov_base
, iov
[i
].iov_len
);
299 case MBEDTLS_ERR_SSL_WANT_READ
:
300 return bytes_written
;
301 case MBEDTLS_ERR_SSL_WANT_WRITE
:
302 ERROR("Got SSL want write in TLS read!");
303 return bytes_written
;
304 case MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS
:
305 ERROR("Got async in progress in TLS read!");
306 return bytes_written
;
307 #ifdef MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS
308 case MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS
:
309 ERROR("Got crypto in progress in TLS read!");
310 return bytes_written
;
313 ERROR("Unexpected response from SSL read: %x", -ret
);
316 } else if (ret
!= iov
[i
].iov_len
) {
317 return bytes_written
+ ret
;
319 bytes_written
+= ret
;
322 return bytes_written
;
328 // c-file-style: "bsd"
331 // indent-tabs-mode: nil