Skip to content

Commit 7dd847a

Browse files
committed
extmod/modssl_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 0b0b4b9 commit 7dd847a

File tree

5 files changed

+131
-0
lines changed

5 files changed

+131
-0
lines changed

extmod/modtls_mbedtls.c

Lines changed: 16 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,14 @@ 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+
return mp_obj_get_int(mp_call_function_2(o->handler, mp_obj_new_bytes(crt->raw.p, crt->raw.len), MP_OBJ_NEW_SMALL_INT(depth)));
198+
}
199+
191200
/******************************************************************************/
192201
// SSLContext type.
193202

@@ -213,6 +222,7 @@ STATIC mp_obj_t ssl_context_make_new(const mp_obj_type_t *type_in, size_t n_args
213222
mbedtls_x509_crt_init(&self->cert);
214223
mbedtls_pk_init(&self->pkey);
215224
self->ciphersuites = NULL;
225+
self->handler = mp_const_none;
216226

217227
#ifdef MBEDTLS_DEBUG_C
218228
// Debug level (0-4) 1=warning, 2=info, 3=debug, 4=verbose
@@ -243,6 +253,7 @@ STATIC mp_obj_t ssl_context_make_new(const mp_obj_type_t *type_in, size_t n_args
243253
self->authmode = MBEDTLS_SSL_VERIFY_NONE;
244254
}
245255
mbedtls_ssl_conf_authmode(&self->conf, self->authmode);
256+
mbedtls_ssl_conf_verify(&self->conf, &ssl_sock_cert_verify, self);
246257
mbedtls_ssl_conf_rng(&self->conf, mbedtls_ctr_drbg_random, &self->ctr_drbg);
247258
#ifdef MBEDTLS_DEBUG_C
248259
mbedtls_ssl_conf_dbg(&self->conf, mbedtls_debug, NULL);
@@ -257,6 +268,8 @@ STATIC void ssl_context_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
257268
// Load attribute.
258269
if (attr == MP_QSTR_verify_mode) {
259270
dest[0] = MP_OBJ_NEW_SMALL_INT(self->authmode);
271+
} else if (attr == MP_QSTR_verify_callback) {
272+
dest[0] = self->handler;
260273
} else {
261274
// Continue lookup in locals_dict.
262275
dest[1] = MP_OBJ_SENTINEL;
@@ -267,6 +280,9 @@ STATIC void ssl_context_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
267280
self->authmode = mp_obj_get_int(dest[1]);
268281
dest[0] = MP_OBJ_NULL;
269282
mbedtls_ssl_conf_authmode(&self->conf, self->authmode);
283+
} else if (attr == MP_QSTR_verify_callback) {
284+
dest[0] = MP_OBJ_NULL;
285+
self->handler = dest[1];
270286
}
271287
}
272288
}
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy