Add a CAN bus TLS example using ISO-TP transport

This provides a simple echo server / client that uses ISO-TP over CAN
bus and wolfSSL for TLS.
pull/279/head
Andrew Hutchings 2021-12-08 11:52:16 +00:00
parent b96386c95e
commit 191b5db535
10 changed files with 715 additions and 0 deletions

3
.gitmodules vendored
View File

@ -16,3 +16,6 @@
[submodule "android/wolfcryptjni-ndk-gradle/wolfcrypt-jni"]
path = android/wolfcryptjni-ndk-gradle/wolfcrypt-jni
url = https://github.com/wolfssl/wolfcrypt-jni
[submodule "can-bus/isotp-c"]
path = can-bus/isotp-c
url = https://github.com/lishen2/isotp-c

View File

@ -28,6 +28,13 @@ way to exchange data securely without BTLE pairing.
Please see the [btle/README.md](btle/README.md) for further usage and details.
#### can-bus
This directory contains an example echo server/client using wolfSSL over
CAN bus with an ISO-TP transport layer. It can be used with a virtual CAN bus
in Linux.
Please see the [can-bus/README.md](can-bus/README.md) for further usage and details.
#### certfields (X509 field extraction)

24
can-bus/Makefile 100644
View File

@ -0,0 +1,24 @@
CC=gcc
LIBS=-lwolfssl
CFLAGS=-Iisotp-c -Wno-cpp
COMMON_OBJS=isotp-c/isotp.o common.o
CLIENT_OBJS=client.o
SERVER_OBJS=server.o
all: client server
%.o: %.c
@$(CC) -c $< -o $@ $(CFLAGS)
client: $(COMMON_OBJS) $(CLIENT_OBJS)
@$(CC) -o $@ $(COMMON_OBJS) $(CLIENT_OBJS) $(CFLAGS) $(LIBS)
server: $(COMMON_OBJS) $(SERVER_OBJS)
@$(CC) -o $@ $(COMMON_OBJS) $(SERVER_OBJS) $(CFLAGS) $(LIBS)
clean:
@rm -f *.o
@rm -f client
@rm -f server
@rm -f isotp-c/isotp.o

97
can-bus/README.md 100644
View File

@ -0,0 +1,97 @@
# wolfSSL CAN Bus Example
This example implements a simple echo client and server that uses TLS over a CAN bus using [ISO-TP](https://en.wikipedia.org/wiki/ISO_15765-2) as a transport protocol. This is because the raw CAN bus protocol can only support payloads of up to 8 bytes. The example requires Linux to run but can modified to work on any setup that uses CAN bus.
## Building
You need to have wolfSSL installed on your computer prior to building. If you haven't already you will also need to get the git submodules for this tree, you can do this by using `git submodule update --init`. A simple `make` will then build the source files.
To generate the required SSL certificates use `./generate_ssl.sh`.
## Setting Up
If you do not have a physical CAN bus between too machines you can use the virtual CAN bus which is a Linux kernel module. This behaves just like a real CAN bus with a similar bandwidth. To enable this run the following commands:
```sh
modprobe vcan
sudo ip link add dev vcan0 type vcan
sudo ip link set vcan0 up
```
## Running
On one console run the server, this should be executed first or the handshake will fail. This is executed using:
```sh
server vcan0
```
Then in another terminal run the client:
```sh
server vcan0
```
On the client you will see (byte numbers will vary):
```
Sending 164 bytes
Receiving 95 bytes
Receiving 954 bytes
Receiving 338 bytes
Receiving 9 bytes
Sending 75 bytes
Sending 6 bytes
Sending 45 bytes
Receiving 6 bytes
Receiving 45 bytes
SSL handshake done!
```
And on the server:
```
Receiving 164 bytes
Sending 95 bytes
Sending 954 bytes
Sending 338 bytes
Sending 9 bytes
Receiving 75 bytes
Receiving 6 bytes
Receiving 45 bytes
Sending 6 bytes
Sending 45 bytes
SSL handshake done!
```
Once you see the message "SSL handshake done!" on both consoles you can enter text into the client console. When you hit "enter" this will be sent to the server via the TLS encrypted CAN bus and will echo there.
For example, on the client if we type "Hello world, this is a TLS test!":
```
Hello world, this is a TLS test!
Sending: Hello world, this is a TLS test!
Sending 62 bytes
Message sent
```
The server will echo:
```
Receiving 62 bytes
Got message: Hello world, this is a TLS test!
```
## Cleaning Up
If you wish to disable the virtual CAN bus you can turn it off by doing:
```sh
sudo ip link set vcan0 down
```

66
can-bus/client.c 100644
View File

@ -0,0 +1,66 @@
/* client.c
*
* Copyright (C) 2021 wolfSSL Inc.
*
* This file is part of wolfSSL.
*
* wolfSSL is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* wolfSSL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "common.h"
extern volatile int keep_running;
int main(int argc, char *argv[])
{
WOLFSSL_CTX *ctx = NULL;
WOLFSSL_METHOD* method = NULL;
WOLFSSL* ssl = NULL;
uint8_t data[CAN_MSG_LEN];
int length;
int ret;
if (argc != 2) {
printf("Usage: ./transmit <CAN interface>\n");
return -1;
}
ret = setup_connection(argv[1], CLIENT_ID, SERVER_ID);
if (ret) {
return ret;
}
ret = setup_ssl(SERVICE_TYPE_CLIENT, &ctx, &method, &ssl);
if (ret) {
return ret;
}
while(keep_running) {
char *line = NULL;
size_t len = 0;
ssize_t line_size = 0;
line_size = getline(&line, &len, stdin);
if (line_size > 0) {
printf("\nSending: %.*s\n\n", (int)line_size, line);
wolfSSL_send(ssl, line, line_size, 0);
printf("Message sent\n");
free(line);
}
}
close_ssl(ctx, ssl);
return 0;
}

368
can-bus/common.c 100644
View File

@ -0,0 +1,368 @@
/* common.c
*
* Copyright (C) 2021 wolfSSL Inc.
*
* This file is part of wolfSSL.
*
* wolfSSL is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* wolfSSL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "common.h"
int sock;
volatile int keep_running = 1;
static uint8_t *copy_buf = NULL;
static uint8_t *copy_buf_ptr;
static size_t copy_buf_len;
static IsoTpLink g_link;
/* Alloc send and receive buffer statically in RAM */
static uint8_t g_isotpRecvBuf[ISOTP_BUFSIZE];
static uint8_t g_isotpSendBuf[ISOTP_BUFSIZE];
void sig_handle(int dummy);
/* Debug function required by ISO-TP to compile, can be blank */
void isotp_user_debug(const char* format, ...) {
va_list args;
va_start(args, format);
vprintf(format, args);
printf("\r\n");
va_end(args);
}
/* Timing function required by ISO-TP to compile */
uint32_t isotp_user_get_ms(void) {
struct timeval t;
gettimeofday(&t, NULL);
return (t.tv_sec * 1000) + (t.tv_usec / 1000);
}
/* CAN bus send function require by ISO-TP to compile */
int isotp_user_send_can(const uint32_t arbitration_id, const uint8_t* data,
const uint8_t size) {
/* Copy the message into a CAN bus frame, the message will always be
* 8 bytes or less */
struct can_frame frame;
frame.can_id = arbitration_id;
frame.can_dlc = size;
memcpy(frame.data, data, size);
/* Write the frame to the CAN bus */
if (write(sock, &frame, sizeof(struct can_frame))
!= sizeof(struct can_frame)) {
perror("Write error\n");
return -1;
}
return 0;
}
/* Our CAN bus receive function */
int can_receive(uint8_t data[CAN_MSG_LEN], int *length) {
int nbytes;
struct can_frame frame;
struct pollfd p[1];
p[0].fd = sock;
p[0].events = POLLIN;
/* Poll for new data */
int retval = poll(p, 1, 10);
if (retval < 0) {
perror("Poll error\n");
return 1;
}
else if (retval == 0) {
/* No data */
*length = 0;
return EAGAIN;
}
/* Read in the frame data */
nbytes = read(sock, &frame, sizeof(struct can_frame));
if (nbytes < 0) {
perror("Read error\n");
return 1;
}
memcpy(data, frame.data, frame.can_dlc);
*length = frame.can_dlc;
return 0;
}
/* Connect to the CAN bus */
int can_connect(const char *address, uint16_t filter)
{
struct sockaddr_can addr;
struct ifreq ifr;
/* Setup a CAN bus ID filter so that the kernel space filters out messages
* we don't need. This is faster than userspace doing it */
struct can_filter rfilter[1];
rfilter[0].can_id = filter;
rfilter[0].can_mask = 0xFFF;
if ((sock = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
perror("Socket open error\n");
return 1;
}
/* Set the filter */
setsockopt(sock, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));
strcpy(ifr.ifr_name, address);
ioctl(sock, SIOCGIFINDEX, &ifr);
memset(&addr, 0, sizeof(addr));
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
perror("Bind error\n");
return 1;
}
/* Setup the receive copy buffer */
copy_buf = malloc(ISOTP_BUFSIZE);
copy_buf_ptr = copy_buf;
copy_buf_len = 0;
if (!copy_buf) {
fprintf(stderr, "Copy buf malloc fail\n");
return -99;
}
return 0;
}
void can_close()
{
free(copy_buf);
close(sock);
}
/* The wolfSSL send packet callback. This function sends the data via ISO-TP
* and then listens and processes the ISO-TP control packets until the message
* is completely sent */
int send_ssl(WOLFSSL *ssl, char *buf, int sz, void *ctx)
{
uint8_t data[CAN_MSG_LEN];
int length;
IsoTpLink *g_link = (struct IsoTpLink*)ctx;
int ret = isotp_send(g_link, buf, sz);
printf("Sending %d bytes\n", sz);
if (ret) {
fprintf(stderr, "Error from isotp: %d\n", ret);
return -64;
}
while(g_link->send_status != ISOTP_SEND_STATUS_IDLE) {
ret = can_receive(data, &length);
if (!ret && (length > 0)) {
/* Got a control frame */
isotp_on_can_message(g_link, data, length);
} else if (ret != EAGAIN) {
fprintf(stderr, "Receive error\n");
}
isotp_poll(g_link);
}
return sz;
}
/* Receive callback for wolfSSL. ISO-TP will only return a buffer once and it
* can be more data than wolfSSL wants, so we copy this into an intermediate
* buffer and just return what wolfSSL is asking for */
int recv_ssl(WOLFSSL* ssl, char* buf, int sz, void* ctx)
{
uint8_t data[CAN_MSG_LEN];
int data_len;
uint16_t msg_len = 0;
int ret;
IsoTpLink *g_link = (struct IsoTpLink*)ctx;
if (!copy_buf_len) {
while (isotp_receive(g_link, copy_buf, ISOTP_BUFSIZE, &msg_len)
!= ISOTP_RET_OK) {
int ret = can_receive(data, &data_len);
if (ret == EAGAIN) {
return 0;
}
if (!ret && (data_len > 0)) {
isotp_on_can_message(g_link, data, data_len);
} else if (ret) {
fprintf(stderr, "Recieve error\n");
return -99;
}
}
if (msg_len) {
copy_buf_ptr = copy_buf;
printf("Receiving %d bytes\n", msg_len);
copy_buf_len = msg_len;
}
}
if (copy_buf_len >= sz) {
memcpy(buf, copy_buf_ptr, sz);
copy_buf_ptr+= sz;
copy_buf_len-= sz;
return sz;
} else {
memcpy(buf, copy_buf_ptr, copy_buf_len);
sz = copy_buf_len;
copy_buf_len = 0;
return sz;
}
return msg_len;
}
void close_ssl(WOLFSSL_CTX *ctx, WOLFSSL *ssl)
{
if (ssl) {
int ret = SSL_SHUTDOWN_NOT_DONE;
while (ret == SSL_SHUTDOWN_NOT_DONE) {
ret = wolfSSL_shutdown(ssl);
}
if (ret != SSL_SUCCESS) {
char buffer[ERR_MSG_LEN];
int err = wolfSSL_get_error(ssl, ret);
fprintf(stderr, "Error shutting down TLS connection: %d, %s",
err, wolfSSL_ERR_error_string(err, buffer));
return;
}
}
can_close();
wolfSSL_free(ssl);
wolfSSL_CTX_free(ctx);
}
void sig_handle(int dummy)
{
keep_running = 0;
}
int setup_connection(const char *interface, int local_id, int remote_id)
{
struct sigaction sa = { .sa_handler = sig_handle, /* .sa_flags = 0 */ };
sigaction(SIGINT, &sa, 0);
wolfSSL_Init();
/* Connect to CAN bus provided on command line, filter out everything
* except for the local CAN ID */
if (can_connect(interface, local_id)) {
return -1;
}
/* Setup ISO-TP and set the buffers */
isotp_init_link(&g_link, remote_id, g_isotpSendBuf,
sizeof(g_isotpSendBuf), g_isotpRecvBuf, sizeof(g_isotpRecvBuf));
return 0;
}
int setup_ssl(enum service_type type, WOLFSSL_CTX **new_ctx,
WOLFSSL_METHOD **new_method, WOLFSSL **new_ssl)
{
int ret;
WOLFSSL_CTX *ctx = NULL;
WOLFSSL_METHOD* method = NULL;
WOLFSSL* ssl = NULL;
if (type == SERVICE_TYPE_CLIENT) {
method = wolfTLSv1_2_client_method();
} else {
method = wolfTLSv1_2_server_method();
}
if (!method) {
fprintf(stderr, "Could not init wolfSSL method\n");
return -1;
}
ctx = wolfSSL_CTX_new(method);
if (!ctx) {
fprintf(stderr, "Could not init wolfSSL context\n");
close_ssl(NULL, NULL);
return -1;
}
/* Set the send and receive callback functions which will use the
* CAN bus */
wolfSSL_CTX_SetIOSend(ctx, send_ssl);
wolfSSL_CTX_SetIORecv(ctx, recv_ssl);
wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_NONE, NULL);
if (type == SERVICE_TYPE_CLIENT) {
ret = wolfSSL_CTX_load_verify_locations(ctx, "client.pem", NULL);
} else {
ret = wolfSSL_CTX_use_certificate_file(ctx, "server.pem",
SSL_FILETYPE_PEM);
}
if (ret != SSL_SUCCESS) {
fprintf(stderr, "ERROR: failed to load cert, "
"please check the file.\n");
close_ssl(ctx, NULL);
return -1;
}
if (type == SERVICE_TYPE_SERVER) {
if ((ret = wolfSSL_CTX_use_PrivateKey_file(ctx, "server.key",
SSL_FILETYPE_PEM)) != WOLFSSL_SUCCESS) {
fprintf(stderr, "ERROR: failed to load key file, "
"please check the file.\n");
close_ssl(ctx, NULL);
return -1;
}
}
ssl = wolfSSL_new(ctx);
if (!ssl) {
fprintf(stderr, "Could not init wolfSSL\n");
close_ssl(ctx, NULL);
return -1;
}
/* Set the read and write context to both use the ISO-TP link */
wolfSSL_SetIOWriteCtx(ssl, &g_link);
wolfSSL_SetIOReadCtx(ssl, &g_link);
if (type == SERVICE_TYPE_CLIENT) {
ret = wolfSSL_connect(ssl);
} else {
ret = wolfSSL_accept(ssl);
}
wolfSSL_set_using_nonblock(ssl, 1);
if (ret != SSL_SUCCESS) {
char buffer[ERR_MSG_LEN];
int err = wolfSSL_get_error(ssl, ret);
fprintf(stderr, "ERROR: failed to connect using wolfSSL: %d, %s\n",
err, wolfSSL_ERR_error_string(err, buffer));
close_ssl(ctx, ssl);
return -1;
}
*new_ctx = ctx;
*new_method = method;
*new_ssl = ssl;
printf("SSL handshake done!\n");
return 0;
}

66
can-bus/common.h 100644
View File

@ -0,0 +1,66 @@
/* common.h
*
* Copyright (C) 2021 wolfSSL Inc.
*
* This file is part of wolfSSL.
*
* wolfSSL is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* wolfSSL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef __CANCOMMON_H__
#define __CANCOMMON_H__
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <stdio.h>
#include <stdarg.h>
#include <inttypes.h>
#include <unistd.h>
#include <poll.h>
#include <signal.h>
#include <isotp.h>
#include <wolfssl/ssl.h>
#define ISOTP_BUFSIZE 16384
#define CAN_MSG_LEN 8
#define ERR_MSG_LEN 80
#define SERVER_ID 0x7
#define CLIENT_ID 0x8
enum service_type {
SERVICE_TYPE_CLIENT,
SERVICE_TYPE_SERVER
};
int can_receive(uint8_t data[CAN_MSG_LEN], int *length);
int can_connect(const char *address, uint16_t filter);
void can_close(void);
int send_ssl(WOLFSSL *ssl, char *buf, int sz, void *ctx);
int recv_ssl(WOLFSSL* ssl, char* buf, int sz, void* ctx);
void close_ssl(WOLFSSL_CTX *ctx, WOLFSSL *ssl);
int setup_connection(const char *interface, int local_id, int remote_id);
int setup_ssl(enum service_type type, WOLFSSL_CTX **new_ctx,
WOLFSSL_METHOD **new_method, WOLFSSL **new_ssl);
#endif /* __CANCOMMON_H__ */

View File

@ -0,0 +1,24 @@
#!/bin/sh
# Generate self signed root CA cert
openssl req -nodes -x509 -newkey rsa:2048 -keyout ca.key -out ca.crt -subj "/C=GB/ST=London/L=Lon1/O=wolfSSL/OU=root/CN=`hostname -f`/emailAddress=info@wolfssl.com"
# Generate server cert to be signed
openssl req -nodes -newkey rsa:2048 -keyout server.key -out server.csr -subj "/C=GB/ST=London/L=Lon1/O=wolfSSL/OU=server/CN=`hostname -f`/emailAddress=info@wolfssl.com"
# Sign the server cert
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt
# Create server PEM file
cat server.key server.crt > server.pem
# Generate client cert to be signed
openssl req -nodes -newkey rsa:2048 -keyout client.key -out client.csr -subj "/C=GB/ST=London/L=Lon1/O=wolfSSL/OU=client/CN=`hostname -f`/emailAddress=info@wolfssl.com"
# Sign the client cert
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAserial ca.srl -out client.crt
# Create client PEM file
cat client.key client.crt > client.pem

1
can-bus/isotp-c 160000

@ -0,0 +1 @@
Subproject commit 9755e0390921b18f188a01978ac7f03233af68fd

59
can-bus/server.c 100644
View File

@ -0,0 +1,59 @@
/* server.c
*
* Copyright (C) 2021 wolfSSL Inc.
*
* This file is part of wolfSSL.
*
* wolfSSL is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* wolfSSL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "common.h"
extern volatile int keep_running;
int main(int argc, char *argv[])
{
WOLFSSL_CTX *ctx = NULL;
WOLFSSL_METHOD* method = NULL;
WOLFSSL* ssl = NULL;
int ret;
if (argc != 2) {
printf("Usage: ./receive <CAN interface>\n");
return -1;
}
ret = setup_connection(argv[1], SERVER_ID, CLIENT_ID);
if (ret) {
return ret;
}
ret = setup_ssl(SERVICE_TYPE_SERVER, &ctx, &method, &ssl);
if (ret) {
return ret;
}
while(keep_running) {
char reply[64];
int input = wolfSSL_read(ssl, reply, sizeof(reply));
if (input > 0) {
printf("\n\nGot message: %.*s\n", input, reply);
}
}
close_ssl(ctx, ssl);
return 0;
}