Fixed demo with wolfSSH (SCP server)

pull/7/head
Daniele Lacamera 2021-02-16 13:54:54 +01:00
parent 3424d6475d
commit aceee4cfc2
6 changed files with 162 additions and 89 deletions

View File

@ -35,7 +35,10 @@ endif
CFLAGS+=-I$(KINETIS_DRIVERS)/drivers -I$(KINETIS_DRIVERS) -DCPU_MK64FN1M0VLL12 -I$(KINETIS_CMSIS)/Include -I$(PHY) -DDEBUG_CONSOLE_ASSERT_DISABLE=1
LDFLAGS=$(CFLAGS) -Wl,-gc-sections -ffreestanding -nostartfiles -lc -lnosys -specs=nano.specs -Wl,-Map=image.map
LDFLAGS=$(CFLAGS) -Wl,-gc-sections -ffreestanding -nostartfiles -lc -lnosys -Wl,-Map=image.map -specs=nano.specs
OBJS:= \
$(KINETIS_DRIVERS)/drivers/fsl_clock.o \
@ -46,13 +49,12 @@ OBJS:= \
$(KINETIS_DRIVERS)/drivers/fsl_sysmpu.o \
$(WOLFBOOT)/src/libwolfboot.o \
$(WOLFBOOT)/hal/kinetis.o \
$(WOLFSSH_ROOT)/src/wolfscp.o \
$(WOLFSSH_ROOT)/src/internal.o \
$(WOLFSSH_ROOT)/src/ssh.o \
$(WOLFSSH_ROOT)/src/log.o \
$(WOLFSSH_ROOT)/src/internal.o \
$(WOLFSSH_ROOT)/src/misc.o \
$(WOLFSSH_ROOT)/src/io.o \
$(WOLFSSH_ROOT)/src/port.o \
$(WOLFSSH_ROOT)/src/wolfscp.o \
$(WOLFSSL_BUILD)/wolfcrypt/signature.o \
src/clock_config.o \
src/main.o \
@ -132,11 +134,12 @@ wolfboot-align.bin:CFLAGS=-mthumb -Wall -Wextra -Wno-main -Wstack-usage=1024 -ff
-DWOLFSSL_USER_SETTINGS -I$(WOLFSSL_ROOT) -DPICO_PORT_CUSTOM \
-mthumb -mlittle-endian -mthumb-interwork -ffreestanding -fno-exceptions
wolfboot-align.bin:CFLAGS+=-I$(KINETIS_DRIVERS)/drivers -I$(KINETIS_DRIVERS) -DCPU_MK64FN1M0VLL12 -I$(KINETIS_CMSIS)/Include -I$(PHY) -DDEBUG_CONSOLE_ASSERT_DISABLE=1 -mcpu=cortex-m3 -DNVM_FLASH_WRITEONCE=1
wolfboot-align.bin:LDFLAGS=$(CFLAGS) -Wl,-gc-sections -ffreestanding -nostartfiles -lc -lnosys -specs=nano.specs -Wl,-Map=image.map
wolfboot-align.bin:LDFLAGS=$(CFLAGS) -Wl,-gc-sections -ffreestanding -nostartfiles -lc -lnosys -Wl,-Map=image.map -specs=nano.specs
wolfboot-align.bin: wolfboot_target
rm -f ../wolfBoot/hal/kinetis.o
rm -f ../wolfBoot/src/*.o
rm -f $(KINETIS_DRIVERS)/drivers/*.o
cp src/wolfboot.config ../wolfBoot/.config
make -C ../wolfBoot wolfboot-align.bin
cp ../wolfBoot/wolfboot-align.bin .

View File

@ -1,20 +1,21 @@
# Freescale-K64F-FreeRTOS-TLS1.3-Update
Firmware update example on FreeRTOS, using a simple HTTPS server with TLS 1.3
Firmware update example on FreeRTOS, using wolfSSH
This project is meant to demonstrate a firmware upgrade mechanism based on [wolfBoot secure bootloader](https://github.com/wolfssl/wolfBoot), powered by wolfSSL.
The bootloader expect the application to transfer the new firmware, store it in the update partition on the flash memory and trigger an upgrade on the next reboot.
The application in this example uses a HTTPS web form to consent to upload a new firmware image from the host machine. Once the transfer is complete, the target is rebooted into
The application in this example uses wolfSSH to create a simple SCP server.
A SCP client can upload a new firmware image from the host machine. Once the transfer is complete, the target is rebooted into
the bootloader, which validates the new image and copies it to the active boot partition.
## Components
- Bootloader: [wolfBoot](https://github.com/wolfssl/wolfBoot) by wolfSSL
- SSH library: [wolfSSH](https://github.com/wolfssl/wolfSSH) by wolfSSL
- OS: [freeRTOS](https://www.freertos.org/)
- TCP/IP stack: PicoTCP
- Application: Custom HTTPS server over TLS1.3, providing a web form to transfer a new image
## Preparing the initial firmware
@ -45,32 +46,35 @@ More information about wolfBoot upgrade mechanism can be found in the [wolfBoot]
## Firmware update
Connect to the target through the ethernet port using a web browser (default static IP address: https://192.168.178.211).
Once the factory image is installed on the board and running, the board can be reached at the IP address 192.168.178.211.
A file submission form is shown. Select the version two of the compiled image `image.bin.v2.signed`, created at compile time, and click `Update`.
The public key allowed by the board is the ECDSA key provided in the wolfSSH example with username 'hansel'.
![Update submission form](png/kinetis-freertos-before.png)
First of all, change the permission of this example private key distributed with wolfSSH. A SCP client may refuse to use SSH keys that are publicly readable. This can be done via:
When the transfer is complete, a confirmation page is shown. A flag is activated at the end of the flash area to notify wolfBoot of a pending upgrade (using `wolfBoot_update_trigger()` from the application)
```
chmod 0700 wolfssh/keys/hansel-key-ecc.pem
```
![Update submission form](png/kinetis-freertos-transfer.png)
To initiate a firmware update, transfer the file using scp. Use the '-i' option to force public-key based authentication using hansel's private key as follows:
After reboot, wolfBoot will copy the image from the secondary partition to the primary partition, to allow the new firmware to run, but only if the new firmware can be authenticated using the public Ed25519 key stored in the bootloader image. In all other cases, the upgrade is canceled and the old firmware can be started again.
```
scp -i wolfssh/keys/hansel-key-ecc.pem image_v2_signed.bin hansel@192.168.178.211:/update.bin
```
After 30 seconds, the page is automatically refreshed, and the target should now show a new webpage, with the updated version number.
After the update, wait 30-40 seconds. The board should now reboot into version 2 of the firmware.
![Update submission form](png/kinetis-freertos-after.png)
## Copyright notice
This example is Copyright (c) 2019 wolfSSL Inc., and distributed under the term of GNU GPL2.
This example is Copyright (c) 2021 wolfSSL Inc., and distributed under the term of GNU GPL2.
FreeRTOS Kernel is Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. Distributed freely as specified by the MIT Open source license.
FreeRTOS Kernel is Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. Distributed freely as specified by the MIT Open source license.
Some NXP/Freescale specific drivers used in this example are Copyright (c) 2015, Freescale Semiconductor, Inc., Copyright 2016-2017 NXP, All rights reserved. Distributed freely as specified by BSD-3-Clause License.
PicoTCP is Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved. Distribute freely as specified by the GPL license.
wolfBoot, wolfSSL (formerly known as CyaSSL) and wolfCrypt are Copyright (c) 2006-2018 wolfSSL Inc., and licensed for use under GPLv2.
wolfBoot, wolfSSH, wolfSSL (formerly known as CyaSSL) and wolfCrypt are Copyright (c) 2021 wolfSSL Inc., and licensed for use under GPLv2.
See the documentation within each component subdirectory for more information about using and distributing this software.

View File

@ -1,9 +1,9 @@
/* main.c
*
* wolfSSL firmware update demo application
* wolfSSL firmware update demo application
* running on FRDM-K64F with freeRTOS, picoTCP, wolfSSL and wolfBoot
*
* This application demonstrates how to transfer a firmware update on
* This application demonstrates how to transfer a firmware update on
* Kinetis K64F, using HTTPS/TLS1.3.
*
* Firmware update is performed in wolfBoot after the image is transfered
@ -27,7 +27,8 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
#include "user_settings.h"
#include "pico_stack.h"
#include "pico_ipv4.h"
#include "pico_socket.h"
@ -46,8 +47,12 @@
#include "certs.h"
#include "semphr.h"
#include "wolfboot/wolfboot.h"
#include "target.h"
static const char ServerBanner[] = "wolfSSH Firmware update server\n";
const char client_pubkey[] = "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNkI5JTP6D0lF42tbxX19cE87hztUS6FSDoGvPfiU0CgeNSbI+aFdKIzTP5CQEJSvm25qUzgDtH7oyaQROUnNvk= hansel\n";
static char ServerBanner[] = "wolfSSH Firmware update server\n";
extern unsigned int _stored_data;
extern unsigned int _start_data;
extern unsigned int _end_data;
@ -109,7 +114,7 @@ static HeapRegion_t xHeapRegions[] =
{ &heap_sram_upper[0], sizeof(heap_sram_upper)},
{ NULL, 0 } // << Terminates the array.
};
static void socket_cb(uint16_t ev, struct pico_socket *s)
{
@ -162,7 +167,7 @@ static PwMap* PwMapNew(PwMapList* list, byte type, const byte* username,
map->type = type;
if (usernameSz >= sizeof(map->username))
usernameSz = sizeof(map->username) - 1;
memcpy(map->username, username, usernameSz + 1);
XMEMCPY(map->username, username, usernameSz + 1);
map->username[usernameSz] = 0;
map->usernameSz = usernameSz;
@ -210,7 +215,7 @@ static int UserAuth(byte authType,
return WOLFSSH_USERAUTH_FAILURE;
if (authType != WOLFSSH_USERAUTH_PUBLICKEY)
return WOLFSSH_USERAUTH_FAILURE;
return WOLFSSH_USERAUTH_INVALID_AUTHTYPE;
if (authType == WOLFSSH_USERAUTH_PUBLICKEY) {
c32toa(authData->sf.publicKey.publicKeySz, flatSz);
@ -243,48 +248,80 @@ static int UserAuth(byte authType,
#ifndef SSH_BUFFER_SIZE
#define SSH_BUFFER_SIZE 4096
#endif
#define SCP_BUFFER_SIZE 4096
#define SCRATCH_BUFFER_SIZE 1200
static uint8_t scratch[SCRATCH_BUFFER_SIZE];
static uint8_t fileBuffer[SSH_BUFFER_SIZE];
static uint8_t fileBuffer[SCP_BUFFER_SIZE];
static uint32_t fileBuffer_off = 0;
static int update_send_data(WOLFSSH *ssh, char *inbuf, size_t size)
/*
typedef int (*WS_CallbackScpRecv)(WOLFSSH*, int, const char*, const char*,
int, word64, word64, word32, byte*, word32,
word32, void*);
typedef int (*WS_CallbackScpSend)(WOLFSSH*, int, const char*, char*, word32,
word64*, word64*, int*, word32, word32*,
byte*, word32, void*);
*/
static int update_send_data(WOLFSSH* ssh, int state, const char* basePath,
const char* fileName, int fileMode, word64 mTime, word64 aTime,
word32 fw_sz, byte* buf, word32 bufSz, word32 fileOffset,
void* ctx)
{
return -1;
}
static int update_recv_data(WOLFSSH *ssh, char *inbuf, size_t size)
static int update_recv_data(WOLFSSH* ssh, int state, const char* basePath,
const char* fileName, int fileMode, word64 mTime, word64 aTime,
word32 fw_sz, byte* buf, word32 bufSz, word32 file_off,
void* ctx)
{
uint32_t fw_off = 0;
uint8_t *fw_dst = (uint8_t*)WOLFBOOT_PARTITION_UPDATE_ADDRESS;
uint32_t sz;
uint32_t buf_off = 0;
uint32_t fw_siz = 0;
unsigned int i = 0;
int res;
uint8_t fw_buffer = (uint8_t*)WOLFBOOT_PARTITION_UPDATE_ADDRESS;
if (strncmp(&inbuf[i], "WOLF", 4) == 0) {
i+=4;
fw_siz = inbuf[i + 4] + ((inbuf[i + 5]) << 8) + ((inbuf[i + 6]) << 16) + ((inbuf[i + 7]) << 24);
if (fileName) {
if ((file_off == 0) && (bufSz > 0)) {
hal_flash_erase(fw_dst, WOLFBOOT_PARTITION_SIZE);
XMEMSET(fileBuffer, 0xFF, SCP_BUFFER_SIZE);
fileBuffer_off = 0;
}
if (fileBuffer_off > 0) {
file_off -= fileBuffer_off;
sz = SCP_BUFFER_SIZE - fileBuffer_off;
if (sz > bufSz)
sz = bufSz;
XMEMCPY(fileBuffer + fileBuffer_off, buf, sz);
buf_off += sz;
fileBuffer_off += sz;
}
if (fileBuffer_off == SCP_BUFFER_SIZE) {
hal_flash_write(fw_dst + file_off, fileBuffer, SCP_BUFFER_SIZE);
fileBuffer_off = 0;
XMEMSET(fileBuffer, 0xFF, SCP_BUFFER_SIZE);
file_off += SCP_BUFFER_SIZE;
}
while (buf_off < bufSz) {
sz = SCP_BUFFER_SIZE;
if ((bufSz - buf_off) < sz) {
sz = bufSz - buf_off;
XMEMCPY(fileBuffer + fileBuffer_off, buf + buf_off, sz);
fileBuffer_off += sz;
} else {
hal_flash_write(fw_dst + file_off, buf + buf_off, sz);
}
if ((fileBuffer_off > 0) && (file_off + fileBuffer_off >= fw_sz)) {
hal_flash_write(fw_dst + file_off, fileBuffer, SCP_BUFFER_SIZE);
fileBuffer_off = 0;
XMEMSET(fileBuffer, 0xFF, SCP_BUFFER_SIZE);
}
buf_off += sz;
file_off += sz;
}
}
if ((fw_siz > WOLFBOOT_PARTITION_SIZE) || (fw_siz < WOLFBOOT_SECTOR_SIZE))
goto internal_error;
fw_siz += 0x100;
if (i < size) {
printf("Write at %d sz: %d\n", buf_off, i);
buf_off = size - i;
}
hal_flash_unlock();
while(fw_off < fw_siz) {
/* TODO: stream_recv */
}
if (fw_off == fw_siz) {
return -1;
}
internal_error:
return -1;
return WS_SCP_CONTINUE;
}
static int load_key(byte isEcc, byte* buf, word32 bufSz)
@ -369,16 +406,37 @@ static int LoadPublicKeyBuffer(byte* buf, word32 bufSz, PwMapList* list)
return 0;
}
int wolfssh_socket_send(WOLFSSH *ssh, void *_data, word32 len, void *_ctx)
{
struct pico_socket *cli = (struct pico_socket *)_ctx;
uint8_t *data = _data;
return pico_socket_write(cli, data, len);
}
int wolfssh_socket_recv(WOLFSSH *ssh, void *_data, word32 len, void *_ctx)
{
struct pico_socket *cli = (struct pico_socket *)_ctx;
uint8_t *data = _data;
return pico_socket_read(cli, data, len);
}
/* Custom file header interface for SCP */
static char file_header[] = "C0700 0 UPDATE.BIN";
char *scp_get_file_hdr(WOLFSSH *ssh)
{
return file_header;
}
void MainTask(void *pv)
{
int res;
struct pico_socket *s;
uint16_t port = short_be(443);
uint16_t port = short_be(22);
uint32_t bufSz;
WOLFSSH *ssh;
struct pico_ip4 any;
wolfSSH_Init();
PwMapList pwMapList;
PwMapList pubkeyMapList;
xSemaphoreTake(picotcp_started, portMAX_DELAY);
any.addr = 0;
@ -389,21 +447,27 @@ void MainTask(void *pv)
cli = pico_socket_accept(s, NULL, NULL);
WOLFSSH_CTX *ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_SERVER, NULL);
memset(&pwMapList, 0, sizeof(pwMapList));
memset(&pubkeyMapList, 0, sizeof(pubkeyMapList));
wolfSSH_SetUserAuth(ctx, UserAuth);
wolfSSH_CTX_SetBanner(ctx, ServerBanner);
/* Load key */
bufSz = load_key(1, scratch, SCRATCH_BUFFER_SIZE);
if (bufSz == 0)
if (bufSz == 0)
while(1)
;
if (wolfSSH_CTX_UsePrivateKey_buffer(ctx, scratch, bufSz,
WOLFSSH_FORMAT_ASN1) < 0) {
fprintf(stderr, "Couldn't use key buffer.\n");
while(1)
;
}
LoadPublicKeyBuffer(scratch, bufSz, &pwMapList);
/* Load client public keys */
strcpy(scratch, client_pubkey);
LoadPublicKeyBuffer(scratch, strlen(scratch), &pubkeyMapList);
/* Install ctx send/recv I/O */
wolfSSH_SetIORecv(ctx, wolfssh_socket_recv);
wolfSSH_SetIOSend(ctx, wolfssh_socket_send);
while(!cli) {
vTaskDelay(pdMS_TO_TICKS(5));
@ -416,20 +480,23 @@ void MainTask(void *pv)
wolfSSH_SetScpRecv(ctx, update_recv_data);
wolfSSH_SetScpSend(ctx, update_send_data);
/* Set auth CTX based on the MapList */
wolfSSH_SetUserAuthCtx(ssh, &pubkeyMapList);
wolfBoot_success();
while (1) {
wolfSSH_SetUserAuthCtx(ssh, &pwMapList);
wolfSSH_SetUserAuthCtx(ssh, &pubkeyMapList);
wolfSSH_SetIOReadCtx(ssh, cli);
wolfSSH_SetIOWriteCtx(ssh, cli);
res = wolfSSH_accept(ssh);
if (res == WS_SCP_COMPLETE) {
printf("scp file transfer completed\n");
//printf("scp file transfer completed\n");
wolfBoot_update_trigger();
/* Wait one second, reboot */
vTaskDelay(pdMS_TO_TICKS(1000));
reboot();
} else if (res == WS_SFTP_COMPLETE) {
printf("SFTP is not supported.\n");
//printf("SFTP is not supported.\n");
}
wolfSSH_stream_exit(ssh, 0);
pico_socket_close(cli);
@ -450,7 +517,7 @@ void PicoTask(void *pv) {
pico_stack_init();
dev = pico_enet_create("en0");
if (dev) {
pico_ipv4_link_add(dev, addr, mask);
pico_ipv4_link_add(dev, addr, mask);
pico_ipv4_route_add(any, any, gw, 1, NULL);
LED_GREEN_ON();
}
@ -458,7 +525,7 @@ void PicoTask(void *pv) {
pico_stack_tick();
xLastWakeTime = xTaskGetTickCount();
while(1) {
while(1) {
vTaskDelayUntil(&xLastWakeTime, xFrequency);
pico_stack_tick();
}
@ -474,7 +541,7 @@ int main(void) {
LED_GREEN_INIT(1);
vPortDefineHeapRegions(xHeapRegions); // Pass the array into vPortDefineHeapRegions(). Must be called first!
picotcp_started = xSemaphoreCreateBinary();
picotcp_rx_data = xSemaphoreCreateBinary();
@ -508,3 +575,4 @@ int main(void) {
void SystemInit(void)
{
}

View File

@ -40,6 +40,7 @@ static inline int pico_recv(void *ssl, char *buf, int len, void *ctx)
#define NO_64BIT
#define WOLFSSL_GENERAL_ALIGNMENT 4
//#define DEBUG_WOLFSSL
@ -53,9 +54,14 @@ static inline int pico_recv(void *ssl, char *buf, int len, void *ctx)
#define XMALLOC(s, h, type) pvPortMalloc((s))
#define XREALLOC(p, n, h, t) pvPortRealloc((p), (n))
#define XFREE(p, h, type) vPortFree((p))
#define NO_SCP_TIMESTAMP
#define NO_SCP_ENTER_DIRECTORY
#define NO_WOLFSSH_CLIENT
#define WSCPFILEHDR scp_get_file_hdr
#define TIME_OVERRIDES
static inline long XTIME(long *x) { return xTaskGetTickCount() / configTICK_RATE_HZ;}
#define WTIME XTIME
//#define WTIME XTIME
#define WOLFSSH_NO_TIMESTAMP
#define NO_ASN_TIME
#define WOLFSSL_USER_CURRTIME
@ -85,20 +91,6 @@ static inline long XTIME(long *x) { return xTaskGetTickCount() / configTICK_RATE
#define WOLFSSL_SP_SMALL
#define SP_WORD_SIZE 32
/* Edwards */
#if 0
# define HAVE_ED25519
# define ED25519_SMALL
# define USE_FAST_MATH
# define WOLFSSL_SHA512
# define HAVE_CURVE25519
# define WOLFSSL_SHA512
/* Chacha20-poly1305 */
# define HAVE_CHACHA
# define HAVE_POLY1305
#endif
/* RSA */
#define HAVE_RSA
#define USE_CERT_BUFFERS_2048
@ -111,13 +103,19 @@ static inline long XTIME(long *x) { return xTaskGetTickCount() / configTICK_RATE
#define USE_SLOW_SHA2
//#define USE_SLOW_SHA512
/* AES */
#define HAVE_AESGCM
#define HAVE_AESCCM
#define HAVE_AES_COUNTER
#define HAVE_AES_DIRECT
/* Disabled ciphers */
#define NO_DES3
#define NO_MD4
#define NO_RABBIT
#define NO_RC4
#define NO_HC128
#define NO_HASH_DBRG
#define NO_WRITEV

View File

@ -2,10 +2,10 @@ ARCH?=ARM
TARGET?=kinetis
SIGN?=ECC256
HASH?=SHA256
KINETIS?=$(HOME)/src/FRDM-K64F
KINETIS_CPU?=MK64FN1M0VLL12
KINETIS_DRIVERS?=$(KINETIS)/devices/MK64F12
KINETIS_CMSIS?=$(HOME)/src/FRDM-K64F/CMSIS
MCUXPRESSO?=$(HOME)/src/FRDM-K64F
MCUXPRESSO_CPU?=MK64FN1M0VLL12
MCUXPRESSO_DRIVERS?=$(MCUXPRESSO)/devices/MK64F12
MCUXPRESSO_CMSIS?=$(HOME)/src/FRDM-K64F/CMSIS
FREEDOM_E_SDK?=$(HOME)/src/freedom-e-sdk
DEBUG?=0
VTOR?=1

@ -1 +1 @@
Subproject commit 43d653867fd33f67253013b7c3c07d4be9dc89fe
Subproject commit 52a1e13470c7619fa3bc86af1f0b59836d609fc9