One of my goal of the past months was to use an STM32 Nucleo board to get a webpage through HTTPS, using an Ethernet shield for TCP/IP stack. I was close last time, but I discovered that my Nucleo-F103RB did not have enough RAM. Now I have my hands on a Nucleo-F411RE that has 128KiB of RAM and 512KiB of Flash, and I was finally able to do it.
The hardware is composed of:
- Nucleo-F411RE
- STM32F411RE microcontroller
- SPI master to communicate with other chips
- USART serial port with STLink USB connection for standard I/O
- JTAG with STLink USB connection to flash program
- Ethernet shield
- Wiznet W5100 that implements the TCP/IP and UDP/IP stacks.
- SPI connection to the board below.
The software that I uploaded on board is composed of:
- Low-level drivers provided by libopencm3
- W5100 SPI driver to expose the chip registers and memories.
- Socket API: POSIX socket library developed by me on top of W5100 functionality.
- USART minimal driver to write/read from serial port through the STLink USB connection on the Nucleo.
- DNS: I developed a minimal implementation of getaddrinfo() to retrieve the IP address of a website from its name.
- TLS: I used mbedTLS 2.2.0 for the Transport Layer Security 1.2
- The certificate of the CA, that is embedded statically as a binary blob into the program (I used Chrome to download it beforehand).
- The application is simply an HTTP GET request.
The tools I used to build the software are:
- GCC ARM Embedded toolchain (version 4.9 2015q1) to compile and link the code,
- OpenOCD (version 0.9.0) to write the software image into the STM32 embedded flash,
- Chrome to download the CA certificate,
- minicom to connect with the serial port
- All on my Debian (Stretch) Linux box
To build and run:
git
checkout my nucleo_tests repo (now at commit eb2cc1942618f745286028c3cc591012b92d56bc).- Go in
libopencm3
directory and run “make
” to compile the low-level drivers. - Download mbedTLS 2.2.0 and unpack it in nucleo_tests directory.
- Open “
nucleo_tests/tests/test.mk
“, comment the stm32f103rb line and uncomment the stm32f411re line. - Go in “
nucleo_tests/tests/mbedtls
” directory and run “make
” to build both the mbedTLS library and the application. - Connect the Nucleo board + Ethernet shield to the PC with USB, and connect the Ethernet cable.
- In another terminal run “
minicom -b 57600 -D /dev/ttyACM0
” to connect to the serial port. - Run “
make flash
” to write the program into the STM32 embedded flash (usesopenocd
program) - Wait for the Link LED on the Ethernet shield to turn on.
- Go in the minicom terminal, and you should see a prompt; press a key to continue.
- The terminal should print the page that has been retrieved through HTTPS.
This is the output I get from the serial port:
Press any key to continue... Certificate: cert. version : 3 serial number : 02:0B:70:F4:68:67:1E:47:13:96:63:3D:99:2C:E5:CD issuer name : C=US, O=DigiCert Inc, CN=DigiCert SHA2 Secure Server CA subject name : C=US, ST=Cali HTTP/1.1 200 OK Server: nginx Date: Thu, 04 Feb 2016 20:41:12 GMT Content-Type: text/html; charset=UTF-8 Transfer-Encoding: chunked Connection: keep-alive Last-Modified: Mon, 09 Nov 2015 23:59:43 GMT ETag: "687f8a-bd-52424631079c0" Cache-Control: max-age=1209600 Expires: Wed, 10 Feb 2016 15:34:38 GMT Vary: Accept-Encoding X-Varnish: 324309826 304737767 Age: 709594 Via: 1.1 varnish Strict-Transport-Security: max-age=31536000; includeSubdomains X-Content-Type-Options: nosniff X-Frame-Options: SAMEORIGIN bd <html> <head> <title>Site Under Maintenance</title> </head> <body> <h1>This site is currently under maintenance. Please try back later.</h1> </body> </html> 0
To conclude, this proof of concept shows a very small platform that can be used to build IoT applications with a layer of security. Instead of having an application processor running Linux, we have a microcontroller running at a frequency as low as 16MHz, the base software is around 90KiB of code running in flash, and needs around 50KiB of RAM, between static and dynamic memory. We are using a security layer that doesn’t invent anything new: it’s an implementation of the widely used TLS.
Stefan Kanev
2016/08/18
Hello,i want to reproduce Your work – to learn about TSL/SSL.
Which Ethernet shield i should use from these:
https://www.amazon.co.uk/s/ref=nb_sb_ss_c_1_18?url=search-alias%3Daps&field-keywords=arduino+ethernet+shield&sprefix=arduino+ethernet+s%2Caps%2C337
Balau
2016/08/23
My work should apply to the shields that are compatible with Arduino UNO and have a W5100 Ethernet chip on board. The shield that I have is revision R2 but a revision R3 should work too because the difference is just in some more pins that are unused by the Ethernet shield itself.
Marcel
2017/01/02
I would like to port this project to an STM32F407.
How difficult is this?
I found a little bug in this project, I reported the bug on Github.
Balau
2017/01/07
Thank you! I changed the Makefile to remove user-dependent directories, sorry it slipped.
To build for STM32F407 you would probably just need to create a new
scripts/stm32f407xx.ld
memory map file, ascripts/libopencm3.stm32f407xx.mk
sub-makefile and include it intests/test.mk
instead of the one that is currently used. Your device has more RAM then mine, and for the rest it needs the SPI to communicate with the Wiznet chip and the UART for standard I/O, so it should not be a problem.Omar Grati
2017/09/20
Hello Balau,
in step 5 as I run make I got this error:
.
.
.
CC x509write_csr.c
AR libmbedx509.a
RL libmbedx509.a
CC debug.c
CC net.c
net.c: In function ‘mbedtls_net_accept’:
net.c:305:37: warning: passing argument 5 of ‘getsockopt’ from incompatible pointer type
(void *) &type, &type_len ) != 0 ||
^
In file included from net.c:64:0:
/home/grati/nucleo_tests/include/sys/socket.h:102:9: note: expected ‘socklen_t *’ but argument is of type ‘int *’
int getsockopt(int, int, int, void *__restrict, socklen_t *__restrict);
^
net.c:315:76: warning: passing argument 3 of ‘accept’ from incompatible pointer type
(struct sockaddr *) &client_addr, &n );
^
In file included from net.c:64:0:
/home/grati/nucleo_tests/include/sys/socket.h:87:9: note: expected ‘socklen_t *’ but argument is of type ‘int *’
int accept(int, struct sockaddr *__restrict, socklen_t *__restrict);
^
net.c:323:59: warning: passing argument 6 of ‘recvfrom’ from incompatible pointer type
(struct sockaddr *) &client_addr, &n );
^
In file included from net.c:64:0:
/home/grati/nucleo_tests/include/sys/socket.h:111:9: note: expected ‘socklen_t *’ but argument is of type ‘int *’
ssize_t recvfrom(int, void *__restrict, size_t, int,
^
net.c:358:59: warning: passing argument 3 of ‘getsockname’ from incompatible pointer type
(struct sockaddr *) &local_addr, &n ) != 0 ||
^
In file included from net.c:64:0:
/home/grati/nucleo_tests/include/sys/socket.h:99:9: note: expected ‘socklen_t *’ but argument is of type ‘int *’
int getsockname(int, struct sockaddr *__restrict, socklen_t *__restrict);
^
net.c: In function ‘mbedtls_net_usleep’:
net.c:441:5: warning: implicit declaration of function ‘select’ [-Wimplicit-function-declaration]
select( 0, NULL, NULL, NULL, &tv );
^
net.c: In function ‘mbedtls_net_recv_timeout’:
net.c:489:5: error: unknown type name ‘fd_set’
fd_set read_fds;
^
net.c:495:5: warning: implicit declaration of function ‘FD_ZERO’ [-Wimplicit-function-declaration]
FD_ZERO( &read_fds );
^
net.c:496:5: warning: implicit declaration of function ‘FD_SET’ [-Wimplicit-function-declaration]
FD_SET( fd, &read_fds );
^
Makefile:160: recipe for target ‘net.o’ failed
make[2]: *** [net.o] Error 1
make[2]: Leaving directory ‘/home/grati/nucleo_tests/mbedtls-2.2.0/library’
Makefile:17: recipe for target ‘lib’ failed
make[1]: *** [lib] Error 2
make[1]: Leaving directory ‘/home/grati/nucleo_tests/mbedtls-2.2.0’
Makefile:56: recipe for target ‘../..//mbedtls-2.2.0/library/libmbedtls.a’ failed
make: *** [../..//mbedtls-2.2.0/library/libmbedtls.a] Error 2
The problem is with code of the mbedTLS. As I used the new version 2.6.0 of mbedTLS, I got similar problem.
I am using the version 4.9.3 20150529 of arm-none-eabi-gcc.
Can the problem be the usage of the another version of compiler? Or where do you see the problem?
Regards,
Grati.
Balau
2017/09/21
My code contains the header sys/select.h that contains the missing declarations. Try modifying the net.c to include it.
My code tries to conform to POSIX standard, but maybe mbedtls expects something else where fd_set is in another header.
Praveen
2020/10/17
Hello Balau,
I’m working on a embedded client (stm417 controller).
My doubt, which server certificate should be kept at client! Only root certificate of server or the tail one, or all the certificates (root, intermediate and tail) at client. I’m downloading the certificate from browser certificate path link. For example, for http://www.google.com, I can see two certificates in certification path from my browser.
Best Regards,
Praveen
Gogo
2022/02/19
Why is libopencm3 directory empty after cloning your repo?