Skip to content

Commit aaba1d8

Browse files
felixdoerredpgeorge
authored andcommitted
extmod/modtls_mbedtls: Implement cert verification callback for mbedtls.
This is a useful alternative to .getpeercert() when the certificate is not stored to reduce RAM usage. Signed-off-by: Felix Dörre <felix@dogcraft.de>
1 parent b802f0f commit aaba1d8

5 files changed

Lines changed: 133 additions & 0 deletions

File tree

extmod/modtls_mbedtls.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ typedef struct _mp_obj_ssl_context_t {
6767
mbedtls_pk_context pkey;
6868
int authmode;
6969
int *ciphersuites;
70+
mp_obj_t handler;
7071
} mp_obj_ssl_context_t;
7172

7273
// This corresponds to an SSLSocket object.
@@ -188,6 +189,16 @@ STATIC void ssl_check_async_handshake_failure(mp_obj_ssl_socket_t *sslsock, int
188189
}
189190
}
190191

192+
STATIC int ssl_sock_cert_verify(void *ptr, mbedtls_x509_crt *crt, int depth, uint32_t *flags) {
193+
mp_obj_ssl_context_t *o = ptr;
194+
if (o->handler == mp_const_none) {
195+
return 0;
196+
}
197+
mp_obj_array_t cert;
198+
mp_obj_memoryview_init(&cert, 'B', 0, crt->raw.len, crt->raw.p);
199+
return mp_obj_get_int(mp_call_function_2(o->handler, MP_OBJ_FROM_PTR(&cert), MP_OBJ_NEW_SMALL_INT(depth)));
200+
}
201+
191202
/******************************************************************************/
192203
// SSLContext type.
193204

@@ -213,6 +224,7 @@ STATIC mp_obj_t ssl_context_make_new(const mp_obj_type_t *type_in, size_t n_args
213224
mbedtls_x509_crt_init(&self->cert);
214225
mbedtls_pk_init(&self->pkey);
215226
self->ciphersuites = NULL;
227+
self->handler = mp_const_none;
216228

217229
#ifdef MBEDTLS_DEBUG_C
218230
// Debug level (0-4) 1=warning, 2=info, 3=debug, 4=verbose
@@ -243,6 +255,7 @@ STATIC mp_obj_t ssl_context_make_new(const mp_obj_type_t *type_in, size_t n_args
243255
self->authmode = MBEDTLS_SSL_VERIFY_NONE;
244256
}
245257
mbedtls_ssl_conf_authmode(&self->conf, self->authmode);
258+
mbedtls_ssl_conf_verify(&self->conf, &ssl_sock_cert_verify, self);
246259
mbedtls_ssl_conf_rng(&self->conf, mbedtls_ctr_drbg_random, &self->ctr_drbg);
247260
#ifdef MBEDTLS_DEBUG_C
248261
mbedtls_ssl_conf_dbg(&self->conf, mbedtls_debug, NULL);
@@ -257,6 +270,8 @@ STATIC void ssl_context_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
257270
// Load attribute.
258271
if (attr == MP_QSTR_verify_mode) {
259272
dest[0] = MP_OBJ_NEW_SMALL_INT(self->authmode);
273+
} else if (attr == MP_QSTR_verify_callback) {
274+
dest[0] = self->handler;
260275
} else {
261276
// Continue lookup in locals_dict.
262277
dest[1] = MP_OBJ_SENTINEL;
@@ -267,6 +282,9 @@ STATIC void ssl_context_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
267282
self->authmode = mp_obj_get_int(dest[1]);
268283
dest[0] = MP_OBJ_NULL;
269284
mbedtls_ssl_conf_authmode(&self->conf, self->authmode);
285+
} else if (attr == MP_QSTR_verify_callback) {
286+
dest[0] = MP_OBJ_NULL;
287+
self->handler = dest[1];
270288
}
271289
}
272290
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Test creating an SSL connection and getting the peer certificate.
2+
3+
try:
4+
import io
5+
import os
6+
import socket
7+
import tls
8+
except ImportError:
9+
print("SKIP")
10+
raise SystemExit
11+
12+
PORT = 8000
13+
14+
# These are test certificates. See tests/README.md for details.
15+
cert = cafile = "ec_cert.der"
16+
key = "ec_key.der"
17+
18+
try:
19+
with open(cafile, "rb") as f:
20+
cadata = f.read()
21+
with open(key, "rb") as f:
22+
key = f.read()
23+
except OSError:
24+
print("SKIP")
25+
raise SystemExit
26+
27+
28+
def verify_callback(cert, depth):
29+
print(cert.hex())
30+
return 0
31+
32+
33+
# Server
34+
def instance0():
35+
multitest.globals(IP=multitest.get_network_ip())
36+
s = socket.socket()
37+
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
38+
s.bind(socket.getaddrinfo("0.0.0.0", PORT)[0][-1])
39+
s.listen(1)
40+
multitest.next()
41+
s2, _ = s.accept()
42+
server_ctx = tls.SSLContext(tls.PROTOCOL_TLS_SERVER)
43+
server_ctx.load_cert_chain(cadata, key)
44+
s2 = server_ctx.wrap_socket(s2, server_side=True)
45+
print(s2.read(16))
46+
s2.write(b"server to client")
47+
s2.close()
48+
s.close()
49+
50+
51+
# Client
52+
def instance1():
53+
s_test = tls.SSLContext(tls.PROTOCOL_TLS_CLIENT)
54+
if not hasattr(s_test, "verify_callback"):
55+
print("SKIP")
56+
raise SystemExit
57+
58+
multitest.next()
59+
s = socket.socket()
60+
s.connect(socket.getaddrinfo(IP, PORT)[0][-1])
61+
client_ctx = tls.SSLContext(tls.PROTOCOL_TLS_CLIENT)
62+
client_ctx.verify_mode = tls.CERT_REQUIRED
63+
client_ctx.verify_callback = verify_callback
64+
client_ctx.load_verify_locations(cadata)
65+
s = client_ctx.wrap_socket(s, server_hostname="micropython.local")
66+
s.write(b"client to server")
67+
print(s.read(16))
68+
s.close()
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
--- instance0 ---
2+
b'client to server'
3+
--- instance1 ---
4+
308201d330820179a00302010202144315a7cd8f69febe2640314e7c97d60a2523ad15300a06082a8648ce3d040302303f311a301806035504030c116d6963726f707974686f6e2e6c6f63616c31143012060355040a0c0b4d6963726f507974686f6e310b3009060355040613024155301e170d3234303131343034353335335a170d3235303131333034353335335a303f311a301806035504030c116d6963726f707974686f6e2e6c6f63616c31143012060355040a0c0b4d6963726f507974686f6e310b30090603550406130241553059301306072a8648ce3d020106082a8648ce3d0301070342000449b7f5fa687cb25a9464c397508149992f445c860bcf7002958eb4337636c6af840cd4c8cf3b96f2384860d8ae3ee3fa135dba051e8605e62bd871689c6af43ca3533051301d0603551d0e0416041441b3ae171d91e330411d8543ba45e0f2d5b2951b301f0603551d2304183016801441b3ae171d91e330411d8543ba45e0f2d5b2951b300f0603551d130101ff040530030101ff300a06082a8648ce3d04030203480030450220587f61c34739d6fab5802a674dcc54443ae9c87da374078c4ee1cd83f4ad1694022100cfc45dcf264888c6ba2c36e78bd27bb67856d7879a052dd7aa7ecf7215f7b992
5+
b'server to client'
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# test ssl verify_callback
2+
3+
import io
4+
import socket
5+
import tls
6+
7+
8+
def verify_callback(cert, depth):
9+
print("verify_callback:", type(cert), len(cert) > 100, depth)
10+
return 0
11+
12+
13+
def verify_callback_fail(cert, depth):
14+
print("verify_callback_fail:", type(cert), len(cert) > 100, depth)
15+
return 1
16+
17+
18+
def test(peer_addr):
19+
context = tls.SSLContext(tls.PROTOCOL_TLS_CLIENT)
20+
context.verify_mode = tls.CERT_OPTIONAL
21+
context.verify_callback = verify_callback
22+
s = socket.socket()
23+
s.connect(peer_addr)
24+
s = context.wrap_socket(s)
25+
s.close()
26+
27+
context.verify_callback = verify_callback_fail
28+
s = socket.socket()
29+
s.connect(peer_addr)
30+
try:
31+
s = context.wrap_socket(s)
32+
except OSError as e:
33+
print(e.args[1])
34+
35+
36+
if __name__ == "__main__":
37+
test(socket.getaddrinfo("micropython.org", 443)[0][-1])
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
verify_callback: <class 'bytes'> True 2
2+
verify_callback: <class 'bytes'> True 1
3+
verify_callback: <class 'bytes'> True 0
4+
verify_callback_fail: <class 'bytes'> True 2
5+
MBEDTLS_ERR_ERROR_GENERIC_ERROR

0 commit comments

Comments
 (0)