Skip to content

Commit 128168c

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 6be807d commit 128168c

File tree

5 files changed

+138
-0
lines changed

5 files changed

+138
-0
lines changed

extmod/modtls_mbedtls.c

Lines changed: 23 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.
@@ -213,6 +214,7 @@ STATIC mp_obj_t ssl_context_make_new(const mp_obj_type_t *type_in, size_t n_args
213214
mbedtls_x509_crt_init(&self->cert);
214215
mbedtls_pk_init(&self->pkey);
215216
self->ciphersuites = NULL;
217+
self->handler = mp_const_none;
216218

217219
#ifdef MBEDTLS_DEBUG_C
218220
// Debug level (0-4) 1=warning, 2=info, 3=debug, 4=verbose
@@ -250,13 +252,30 @@ STATIC mp_obj_t ssl_context_make_new(const mp_obj_type_t *type_in, size_t n_args
250252

251253
return MP_OBJ_FROM_PTR(self);
252254
}
255+
STATIC int ssl_sock_cert_verify(void *ptr, mbedtls_x509_crt *crt, int depth, uint32_t *flags) {
256+
mp_obj_ssl_socket_t *o = ptr;
257+
if (o->ssl_context->handler == mp_const_none) {
258+
return 0;
259+
}
260+
return mp_obj_get_int(mp_call_function_2(o->ssl_context->handler, mp_obj_new_bytes(crt->raw.p, crt->raw.len), MP_OBJ_NEW_SMALL_INT(depth)));
261+
}
262+
263+
STATIC void ssl_context_set_verify_callback(mp_obj_ssl_context_t *self, mp_obj_t handler_in) {
264+
if (handler_in != mp_const_none && !mp_obj_is_callable(handler_in)) {
265+
mp_raise_ValueError(MP_ERROR_TEXT("invalid handler"));
266+
}
267+
self->handler = handler_in;
268+
}
269+
253270

254271
STATIC void ssl_context_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
255272
mp_obj_ssl_context_t *self = MP_OBJ_TO_PTR(self_in);
256273
if (dest[0] == MP_OBJ_NULL) {
257274
// Load attribute.
258275
if (attr == MP_QSTR_verify_mode) {
259276
dest[0] = MP_OBJ_NEW_SMALL_INT(self->authmode);
277+
} else if (attr == MP_QSTR_verify_callback) {
278+
dest[0] = self->handler;
260279
} else {
261280
// Continue lookup in locals_dict.
262281
dest[1] = MP_OBJ_SENTINEL;
@@ -267,6 +286,9 @@ STATIC void ssl_context_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
267286
self->authmode = mp_obj_get_int(dest[1]);
268287
dest[0] = MP_OBJ_NULL;
269288
mbedtls_ssl_conf_authmode(&self->conf, self->authmode);
289+
} else if (attr == MP_QSTR_verify_callback) {
290+
dest[0] = MP_OBJ_NULL;
291+
ssl_context_set_verify_callback(self, dest[1]);
270292
}
271293
}
272294
}
@@ -502,6 +524,7 @@ STATIC mp_obj_t ssl_socket_make_new(mp_obj_ssl_context_t *ssl_context, mp_obj_t
502524
mbedtls_ssl_free(&o->ssl);
503525
mp_raise_ValueError(MP_ERROR_TEXT("CERT_REQUIRED requires server_hostname"));
504526
}
527+
mbedtls_ssl_set_verify(&o->ssl, &ssl_sock_cert_verify, o);
505528
mbedtls_ssl_set_bio(&o->ssl, &o->sock, _mbedtls_ssl_send, _mbedtls_ssl_recv, NULL);
506529

507530
if (do_handshake_on_connect) {
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=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(type(cert), len(cert) > 100, depth)
10+
return 0
11+
12+
13+
def verify_callback_fail(cert, depth):
14+
print(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+
<class 'bytes'> True 2
2+
<class 'bytes'> True 1
3+
<class 'bytes'> True 0
4+
<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