Merge pull request #3 from CovidBraceletPrj/feature/platformio

Feature: PlatformIO Support
This commit is contained in:
Olaf Landsiedel 2020-08-19 07:55:21 +02:00 committed by GitHub
commit cfe5d56aae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 492 additions and 28 deletions

17
.github/workflows/build.yaml vendored Normal file
View File

@ -0,0 +1,17 @@
name: build
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Set up Python
uses: actions/setup-python@v1
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install platformio
- name: Run PlatformIO Build
run: platformio run --environment nrf52840_dk

17
.github/workflows/test.yaml vendored Normal file
View File

@ -0,0 +1,17 @@
name: test
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Set up Python
uses: actions/setup-python@v1
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install platformio
- name: Run PlatformIO Tests
run: platformio test --environment desktop

46
.gitignore vendored
View File

@ -1 +1,47 @@
build/
# Random seed file created by test scripts and sample programs
seedfile
# CMake build artifacts:
CMakeCache.txt
CMakeFiles
CTestTestfile.cmake
cmake_install.cmake
Testing
# CMake generates *.dir/ folders for in-tree builds (used by MSVC projects), ignore all of those:
*.dir/
# MSVC files generated by CMake:
/*.sln
/*.vcxproj
/*.filters
# Test coverage build artifacts:
Coverage
*.gcno
*.gcda
# generated by scripts/memory.sh
massif-*
# MSVC build artifacts:
*.exe
*.pdb
*.ilk
*.lib
# Python build artifacts:
*.pyc
# Generated documentation:
/apidoc
# Editor navigation files:
/GPATH
/GRTAGS
/GSYMS
/GTAGS
/TAGS
/tags
.pio
.vscode

View File

@ -1,13 +0,0 @@
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.13.1)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(bluecovid)
target_sources(app PRIVATE src/main.c src/contacts.c src/exposure-notification.c src/gatt-service.c src/covid.c)
zephyr_include_directories(${APPLICATION_SOURCE_DIR}/src/tls_config)
#use for testing only, will update keys every X seconds, default is 600
#add_definitions(-DEN_INTERVAL_LENGTH=11)
#add_definitions(-DEN_INTERVAL_LENGTH=1)

View File

@ -1,7 +1,10 @@
# Covid Bracelet, compatibile with Covid Apps on iOS and Android
# Covid Bracelet, compatibile with Covid Apps on iOS and Android [![Actions Status: test](https://github.com/CovidBraceletPrj/CovidBracelet/workflows/test/badge.svg)](https://github.com/CovidBraceletPrj/CovidBracelet/actions) [![Actions Status: build](https://github.com/CovidBraceletPrj/CovidBracelet/workflows/build/badge.svg)](https://github.com/CovidBraceletPrj/CovidBracelet/actions)
**Contributions Welcome!**
## Features
* Sends and receives exposure beacons as specified by Google and Apple for Covid Contact Tracing
* Rolling, encrypted, anonymous beacons
@ -17,8 +20,14 @@
Builds on on Zephyr OS and NRF52 BLE SOCs. Note: as we for now do not use the flash for key storage, this currently only works on nrf52480 or you can just store a very small number of keys. Moving the keys to flash is on the TODO list and will fix this.
## Get Started:
We use Zepyhr master as we need a newer mbed TLS as the ones that ships with Zephyr 2.2. We are waiting for the Zepyhr 2.3 release. To build, please install Zephyr and compile via west. Note that Platform.io does not support Zephyr 2.3 rc / Zepyhr master at the current time.
## Get Started:
This project is based on platformIO, see: [https://platformio.org/platformio-ide](https://platformio.org/platformio-ide)
## Testing
To run the tests for the desktop environment select the test task for desktop or run:
```
platformio test -e desktop
```
**Note: this is a proof of concept and not ready for production**

View File

@ -0,0 +1,3 @@
#define MBEDTLS_HKDF_C
#define MBEDTLS_CIPHER_MODE_CTR
#define MBEDTLS_ENTROPY_C

46
lib/README Normal file
View File

@ -0,0 +1,46 @@
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file.
The source code of each library should be placed in a an own separate directory
("lib/your_library_name/[here are source files]").
For example, see a structure of the following two libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
| |
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |
| |- README --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
and a contents of `src/main.c`:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
PlatformIO Library Dependency Finder will find automatically dependent
libraries scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html

View File

@ -0,0 +1,52 @@
#include "exposure-notification.h"
#include "time.h"
int main(int argc, char **argv) {
// first init everything
// TODO: More randomization, we init with NULL to let en init mbdet entropy for us
en_init(NULL);
// get the currentTime
time_t currentTime = time(NULL);
// The periodKey changes each EN_TEK_ROLLING_PERIOD intervals
ENIntervalNumber periodInterval = en_get_interval_number_at_period_start(currentTime);
ENPeriodKey periodKey;
ENPeriodIdentifierKey periodIdentifierKey;
ENPeriodMetadataEncryptionKey periodMetadaEncryptionKey;
// setup period keys at the beginning
// in theory you could let them generate automatically setting periodInterval to 0
en_generate_and_derive_period_keys(&periodKey, &periodIdentifierKey, &periodMetadaEncryptionKey);
char metadata[128] = "";
while(1) {
// we check the current time to know if we actually need to regenerate anything
currentTime = time(NULL);
ENIntervalNumber currentInterval = en_get_interval_number(currentTime);
// we check if we need to generate new keys
if (currentInterval - periodInterval >= EN_TEK_ROLLING_PERIOD) {
periodInterval = en_get_interval_number_at_period_start(currentTime);
en_generate_and_derive_period_keys(&periodKey, &periodIdentifierKey, &periodMetadaEncryptionKey);
// TODO: Store new periodKey with periodInterval
}
// we now generate the new interval identifier and re-encrypt the metadata
ENIntervalIdentifier intervalIdentifier;
en_derive_interval_identifier(&intervalIdentifier, &periodIdentifierKey, currentInterval);
char encryptedMetadata[sizeof(metadata)];
en_encrypt_interval_metadata(&periodMetadaEncryptionKey, &intervalIdentifier, metadata, encryptedMetadata, sizeof(metadata));
// broadcast intervalIdentifier plus encryptedMetada according to specs
// TODO: receive packets and store them
// repeat for 10-20 minutes
}
return 0;
}

View File

@ -0,0 +1,14 @@
{
"description": "exposure-notification",
"frameworks": "*",
"keywords": "exposure, notification, contact, tracking, tracing",
"name": "exposure-notification",
"platforms": "*",
"version": "0.1",
"dependencies": [
{
"name": "mbedtls",
"version": "^2.16.4"
}
]
}

View File

@ -4,22 +4,27 @@
* SPDX-License-Identifier: Apache-2.0
*/
#if EN_INCLUDE_ZEPHYR_DEPS
#include <zephyr.h>
#include <zephyr/types.h>
#include <stddef.h>
#include <sys/printk.h>
#include <sys/util.h>
#endif
#include <string.h>
#include <mbedtls/entropy.h>
#include <mbedtls/ctr_drbg.h>
#include <mbedtls/hkdf.h>
#include <mbedtls/sha256.h>
#include <mbedtls/aes.h>
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/hkdf.h"
#include "mbedtls/sha256.h"
#include "mbedtls/aes.h"
#include "exposure-notification.h"
static mbedtls_ctr_drbg_context ctr_drbg;
static ENRandomBytesCallback random_bytes_callback = NULL;

36
platformio.ini Normal file
View File

@ -0,0 +1,36 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:nrf52840_dk]
platform = nordicnrf52
board = nrf52840_dk
framework = zephyr
monitor_speed = 115200
test_ignore = test_desktop
build_flags =
-Iinclude/tls_config
# For testing: -DUNITY_EXCLUDE_SETJMP_H=1
-DEN_INCLUDE_ZEPHYR_DEPS=1
-DEN_INIT_MBEDTLS_ENTROPY=0
lib_deps = exposure-notification
[env:desktop]
platform = native
test_ignore = test_embedded
lib_compat_mode = off
lib_deps = mbedtls@~2
build_flags =
-Iinclude/desktop
-Iinclude/tls_config
-Wno-nullability-completeness
-DMBEDTLS_USER_CONFIG_FILE='"user-tls.conf"'
src_filter = --<.src/>

View File

@ -1,5 +0,0 @@
#define MBEDTLS_HKDF_C
#define MBEDTLS_CIPHER_MODE_CTR
#define MBEDTLS_ENTROPY_C
#define EN_INIT_MBEDTLS_ENTROPY 0

11
test/README Normal file
View File

@ -0,0 +1,11 @@
This directory is intended for PIO Unit Testing and project tests.
Unit Testing is a software testing method by which individual units of
source code, sets of one or more MCU program modules together with associated
control data, usage procedures, and operating procedures, are tested to
determine whether they are fit for use. Unit testing finds problems early
in the development cycle.
More information about PIO Unit Testing:
- https://docs.platformio.org/page/plus/unit-testing.html

View File

@ -0,0 +1,222 @@
#include <unity.h>
#include <string.h>
#include "exposure-notification.h"
#define TEST_ASSERT_EQUAL_KEY(k1, k2) TEST_ASSERT_EQUAL(0, memcmp(k1.b, k2.b, sizeof(k1.b)))
#define TEST_ASSERT_NOT_EQUAL_KEY(k1, k2) TEST_ASSERT_NOT_EQUAL(0, memcmp(k1.b, k2.b, sizeof(k1.b)))
void test_init(void) {
int ret = en_init(NULL);
TEST_ASSERT_EQUAL(0, ret);
}
void test_get_interval_number(void) {
TEST_ASSERT_EQUAL(0, en_get_interval_number(0));
TEST_ASSERT_EQUAL(0, en_get_interval_number(EN_INTERVAL_LENGTH-1));
TEST_ASSERT_EQUAL(1, en_get_interval_number(EN_INTERVAL_LENGTH));
TEST_ASSERT_EQUAL(42, en_get_interval_number(EN_INTERVAL_LENGTH*42));
}
void test_get_interval_number_at_period_start(void) {
TEST_ASSERT_EQUAL(0, en_get_interval_number_at_period_start(0));
TEST_ASSERT_EQUAL(0, en_get_interval_number_at_period_start(EN_INTERVAL_LENGTH-1));
TEST_ASSERT_EQUAL(0, en_get_interval_number_at_period_start(EN_INTERVAL_LENGTH));
TEST_ASSERT_EQUAL(0, en_get_interval_number_at_period_start(EN_INTERVAL_LENGTH*42));
TEST_ASSERT_EQUAL(EN_TEK_ROLLING_PERIOD, en_get_interval_number_at_period_start(EN_INTERVAL_LENGTH*EN_TEK_ROLLING_PERIOD));
TEST_ASSERT_EQUAL(EN_TEK_ROLLING_PERIOD, en_get_interval_number_at_period_start(EN_INTERVAL_LENGTH*EN_TEK_ROLLING_PERIOD+1));
}
void test_generate_period_key(void) {
ENPeriodKey p1, p2;
en_generate_period_key(&p1);
en_generate_period_key(&p2);
// yes there is a minimal chance that both are the same ;)
TEST_ASSERT_NOT_EQUAL_KEY(p1, p2);
}
void test_get_period_identifier_key(void) {
ENPeriodKey pk;
en_generate_period_key(&pk);
ENPeriodIdentifierKey k1, k2;
en_derive_period_identifier_key(&k1, &pk);
en_derive_period_identifier_key(&k2, &pk);
// This time both should be the same key as the depent on the period key
TEST_ASSERT_EQUAL_KEY(k1, k2);
// BUT: they should be different than the period key itself
TEST_ASSERT_NOT_EQUAL_KEY(k1, pk);
}
void test_get_interval_identifier(void) {
ENPeriodKey pk;
en_generate_period_key(&pk);
ENIntervalNumber iv = en_get_interval_number(0);
ENIntervalIdentifier id1, id2;
en_derive_interval_identifier(&id1, &pk, iv);
en_derive_interval_identifier(&id2, &pk, iv);
// identities should be the same for the same intervalnumber and period key
TEST_ASSERT_EQUAL_KEY(id1, id2);
iv = en_get_interval_number(EN_INTERVAL_LENGTH);
en_derive_interval_identifier(&id2, &pk, iv);
// BUT: they should be different when we are using another interval number
TEST_ASSERT_NOT_EQUAL_KEY(id1, id2);
// They should also be different for another period key but the the same interval number
en_generate_period_key(&pk);
en_derive_interval_identifier(&id1, &pk, iv);
en_generate_period_key(&pk);
en_derive_interval_identifier(&id2, &pk, iv);
TEST_ASSERT_NOT_EQUAL_KEY(id1, id2);
}
void test_get_period_metadata_encryption_key(void) {
ENPeriodKey pk;
en_generate_period_key(&pk);
ENPeriodMetadataEncryptionKey k1, k2;
en_derive_period_metadata_encryption_key(&k1, &pk);
en_derive_period_metadata_encryption_key(&k2, &pk);
// This time both should be the same key as the depent on the period key
TEST_ASSERT_EQUAL_KEY(k1, k2);
// BUT: they should be different than the period key itself
TEST_ASSERT_NOT_EQUAL_KEY(k1, pk);
// and they should also be different than the periodIdentifierKey
ENPeriodIdentifierKey pik;
en_derive_period_identifier_key(&pik, &pk);
TEST_ASSERT_NOT_EQUAL_KEY(k1, pik);
}
void test_en_encrypt_interval_metadata(void) {
ENPeriodKey pk;
en_generate_period_key(&pk);
ENPeriodMetadataEncryptionKey k1;
en_derive_period_metadata_encryption_key(&k1, &pk);
ENIntervalNumber iv = en_get_interval_number(0);
ENIntervalIdentifier id;
en_derive_interval_identifier(&id, &pk, iv);
unsigned char testData[42] = "This is some test data :)";
unsigned char encryptedData[42] = "";
en_encrypt_interval_metadata(&k1, &id, testData, encryptedData, sizeof(testData));
// the original data should not be changed
TEST_ASSERT_EQUAL_CHAR_ARRAY("This is some test data :)", testData, sizeof("This is some test data :)")-1);
// but the encrypted one should be changed
TEST_ASSERT_NOT_EQUAL(0, memcmp(testData, encryptedData, sizeof(testData)));
unsigned char decryptedData[42] = "";
en_decrypt_interval_metadata(&k1, &id, encryptedData, decryptedData, sizeof(testData));
// the decrypted metadata should be the same as the testData
TEST_ASSERT_EQUAL_CHAR_ARRAY(testData, decryptedData, sizeof(testData));
}
void test_identifier_generation_performance(void) {
int runs = 1000000;
for(int i = 0; i < runs; i++) {
ENPeriodKey pk;
en_generate_period_key(&pk);
ENPeriodIdentifierKey ik;
en_derive_period_identifier_key(&ik, &pk);
for(int iv = 0; iv < EN_TEK_ROLLING_PERIOD; iv++) {
ENIntervalNumber intervalNumber = en_get_interval_number(iv);
ENIntervalIdentifier id;
en_derive_interval_identifier(&id, &ik, intervalNumber);
}
}
}
void test_generate_and_derive_period_keys(void) {
// Test multiple generation
ENPeriodKey periodKey;
ENPeriodIdentifierKey periodIdentifierKey;
ENPeriodMetadataEncryptionKey periodMetadaEncryptionKey;
for(int i = 0; i < 10; i++) {
en_generate_and_derive_period_keys(&periodKey, &periodIdentifierKey, &periodMetadaEncryptionKey);
}
}
void test_against_fixtures(void) {
// First define base values
ENIntervalNumber intervalNumber = 2642976;
ENPeriodKey periodKey = {.b = {0x75, 0xc7, 0x34, 0xc6, 0xdd, 0x1a, 0x78, 0x2d, 0xe7, 0xa9, 0x65, 0xda, 0x5e, 0xb9, 0x31, 0x25}};
unsigned char metadata[4] = {0x40, 0x08, 0x00, 0x00};
// define the expected values
ENPeriodIdentifierKey expectedPIK = {.b = {0x18, 0x5a, 0xd9, 0x1d, 0xb6, 0x9e, 0xc7, 0xdd, 0x04, 0x89, 0x60, 0xf1, 0xf3, 0xba, 0x61, 0x75}};
ENPeriodMetadataEncryptionKey expectedPMEK = {.b = {0xd5, 0x7c, 0x46, 0xaf, 0x7a, 0x1d, 0x83, 0x96, 0x5b, 0x9b, 0xed, 0x8b, 0xd1, 0x52, 0x93, 0x6a}};
ENIntervalIdentifier expectedIntervalIdentifier = {.b = {0x8b, 0xe6, 0xcd, 0x37, 0x1c, 0x5c, 0x89, 0x16, 0x04, 0xbf, 0xbe, 0x49, 0xdf, 0x84, 0x50, 0x96}};
unsigned char expectedEncryptedMetadata[4] = {0x72, 0x03, 0x38, 0x74};
ENPeriodIdentifierKey pik;
en_derive_period_identifier_key(&pik, &periodKey);
TEST_ASSERT_EQUAL_KEY(expectedPIK, pik);
ENPeriodMetadataEncryptionKey pmek;
en_derive_period_metadata_encryption_key(&pmek, &periodKey);
TEST_ASSERT_EQUAL_KEY(expectedPMEK, pmek);
ENIntervalIdentifier intervalIdentifier;
en_derive_interval_identifier(&intervalIdentifier, &pik, intervalNumber);
TEST_ASSERT_EQUAL_KEY(expectedIntervalIdentifier, intervalIdentifier);
unsigned char encryptedMetadata[sizeof(metadata)] = {0};
en_encrypt_interval_metadata(&pmek, &intervalIdentifier, metadata, encryptedMetadata, sizeof(metadata));
TEST_ASSERT_EQUAL_CHAR_ARRAY(expectedEncryptedMetadata, encryptedMetadata, sizeof(expectedEncryptedMetadata));
}
void test_exposure_notification() {
UNITY_BEGIN();
RUN_TEST(test_init);
RUN_TEST(test_get_interval_number);
RUN_TEST(test_get_interval_number_at_period_start);
RUN_TEST(test_generate_period_key);
RUN_TEST(test_get_period_identifier_key);
RUN_TEST(test_get_interval_identifier);
RUN_TEST(test_get_period_metadata_encryption_key);
RUN_TEST(test_en_encrypt_interval_metadata);
RUN_TEST(test_generate_and_derive_period_keys);
RUN_TEST(test_against_fixtures);
//RUN_TEST(test_identifier_generation_performance);
UNITY_END();
}
int main(int argc, char **argv) {
test_exposure_notification();
return 0;
}

6
zephyr/CMakeLists.txt Normal file
View File

@ -0,0 +1,6 @@
cmake_minimum_required(VERSION 3.13.1)
include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE)
project(test)
FILE(GLOB app_sources ../src/*.c*)
target_sources(app PRIVATE ${app_sources})

View File

@ -8,8 +8,6 @@ CONFIG_BT_DEVICE_NAME="BlueCovid"
CONFIG_ENTROPY_GENERATOR=y
CONFIG_MBEDTLS=y
#CONFIG_MBEDTLS_USER_CONFIG_ENABLE=y
#CONFIG_MBEDTLS_CFG_FILE="mbedtls_config.h"
CONFIG_MBEDTLS_USER_CONFIG_ENABLE=y
CONFIG_MBEDTLS_USER_CONFIG_FILE="user-tls.conf"