initial code release

initial code release
This commit is contained in:
olafland 2020-06-08 11:15:05 +02:00
parent 6917ce657f
commit c66f9d8c9b
15 changed files with 1654 additions and 0 deletions

13
CMakeLists.txt Normal file
View File

@ -0,0 +1,13 @@
# 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)

42
README.md Normal file
View File

@ -0,0 +1,42 @@
# Covid Bracelet
** Contributions Welcome! **
## 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 sadly does not support Zephyr 2.3 rc / Zepyhr master at the current time.
## Features
* Code sends out and receives exposure beacons
* Rolling, encrypted, anonymous beacons as specified by Google and Apple
* Compatible with Apple iOS and Android phones
* Upon infection upload keys to a public database
* Retrieve keys of infections from database
* to check for exposure
* Based on Zephyr OS and NRF52 BLE SOCs
** Note: this is a proof of concept and not ready for production **
## Open / Possible next steps
* extensive compatibility testing with Apple iOS and Android devices
* contininous integration testing
* set device name in Flash
* fix entropy: keys are always the same on boot up
* set scanning interval to the correct value, for now we just use the default
* set advertisement interval, correct value, for now we just use the default: should be 200-270 milliseconds
* store long-term contacts in flash
* Energy efficiency
* BLE advertisements sets
* Secure GAT services
* More platforms / OS?
* time sync
* firmware of the air updates (signed)
##TODOs Wristband
Possible platforms for real-world deployment many, as many of the cheap fitness trackers base on NRF52 or chips with similar capabilities.
However, many would need the firmware to be shipped to manufactures.
* Watch UI
* Pine Time could be good for testing
##TODOs App / Basestation
* extend this beyond the simple basestation
* read keys form national databases

181
basestation.py Normal file
View File

@ -0,0 +1,181 @@
#
# Copyright (c) 2020 Olaf Landsiedel, based on a code example from Tony DiCola
#
# SPDX-License-Identifier: Apache-2.0
#
#NOTE: seems not to work well on some iMACs
import logging
import time
import uuid
import struct
import binascii
import Adafruit_BluefruitLE
# Enable debug output.
#logging.basicConfig(level=logging.DEBUG)
# Define service and characteristic UUIDs used by the Covid service.
COVID_SERVICE_UUID = uuid.UUID('F2110D79-699F-6A98-EA42-A7AD9EC75106')
NEXT_KEY_UUID = uuid.UUID('F3110D79-699F-6A98-EA42-A7AD9EC75106')
NEW_KEY_UUID = uuid.UUID('F4110D79-699F-6A98-EA42-A7AD9EC75106')
INFECTED_KEY_CNT_UUID = uuid.UUID('F5110D79-699F-6A98-EA42-A7AD9EC75106')
PERIOD_KEY_0_UUID = uuid.UUID('00110D79-699F-6A98-EA42-A7AD9EC75106')
PERIOD_KEY_1_UUID = uuid.UUID('01110D79-699F-6A98-EA42-A7AD9EC75106')
PERIOD_KEY_2_UUID = uuid.UUID('02110D79-699F-6A98-EA42-A7AD9EC75106')
PERIOD_KEY_3_UUID = uuid.UUID('03110D79-699F-6A98-EA42-A7AD9EC75106')
PERIOD_KEY_4_UUID = uuid.UUID('04110D79-699F-6A98-EA42-A7AD9EC75106')
PERIOD_KEY_5_UUID = uuid.UUID('05110D79-699F-6A98-EA42-A7AD9EC75106')
PERIOD_KEY_6_UUID = uuid.UUID('06110D79-699F-6A98-EA42-A7AD9EC75106')
PERIOD_KEY_7_UUID = uuid.UUID('07110D79-699F-6A98-EA42-A7AD9EC75106')
PERIOD_KEY_8_UUID = uuid.UUID('08110D79-699F-6A98-EA42-A7AD9EC75106')
PERIOD_KEY_9_UUID = uuid.UUID('09110D79-699F-6A98-EA42-A7AD9EC75106')
PERIOD_KEY_10_UUID = uuid.UUID('0A110D79-699F-6A98-EA42-A7AD9EC75106')
PERIOD_KEY_11_UUID = uuid.UUID('0B110D79-699F-6A98-EA42-A7AD9EC75106')
PERIOD_KEY_12_UUID = uuid.UUID('0C110D79-699F-6A98-EA42-A7AD9EC75106')
PERIOD_KEY_13_UUID = uuid.UUID('0D110D79-699F-6A98-EA42-A7AD9EC75106')
infected_period_keys = []
# Get the BLE provider for the current platform.
ble = Adafruit_BluefruitLE.get_provider()
def check_infection(covid):
infected_key_cnt_reader = covid.find_characteristic(INFECTED_KEY_CNT_UUID)
period_key_readers = (
covid.find_characteristic(PERIOD_KEY_0_UUID),
covid.find_characteristic(PERIOD_KEY_1_UUID),
covid.find_characteristic(PERIOD_KEY_2_UUID),
covid.find_characteristic(PERIOD_KEY_3_UUID),
covid.find_characteristic(PERIOD_KEY_4_UUID),
covid.find_characteristic(PERIOD_KEY_5_UUID),
covid.find_characteristic(PERIOD_KEY_6_UUID),
covid.find_characteristic(PERIOD_KEY_7_UUID),
covid.find_characteristic(PERIOD_KEY_8_UUID),
covid.find_characteristic(PERIOD_KEY_9_UUID),
covid.find_characteristic(PERIOD_KEY_10_UUID),
covid.find_characteristic(PERIOD_KEY_11_UUID),
covid.find_characteristic(PERIOD_KEY_12_UUID),
covid.find_characteristic(PERIOD_KEY_13_UUID)
)
#check for infection
ret = bytearray(infected_key_cnt_reader.read_value())
infected_key_cnt = int.from_bytes(ret, byteorder='little', signed=False)
print('device has: infected key cnt ', infected_key_cnt)
for i in range(infected_key_cnt):
ret = bytearray(period_key_readers[i].read_value())
if ret not in infected_period_keys:
print('adding to DB new infected key ', i, ': ', binascii.hexlify(bytearray(ret)))
infected_period_keys.append(ret)
else:
print('infected key ', i , 'already known in DB: ', binascii.hexlify(bytearray(ret)))
def upload_keys(covid):
next_key = covid.find_characteristic(NEXT_KEY_UUID)
new_key = covid.find_characteristic(NEW_KEY_UUID)
go_on = True
while go_on:
print("read key")
ret = bytearray(next_key.read_value())
expected_index = int.from_bytes(ret, byteorder='little', signed=False)
print('expected key index of device is ', expected_index, ', we have upto id ', len(infected_period_keys))
#TODO: check for maximum storage on device
if( expected_index >= 0 and expected_index < len(infected_period_keys) ):
print('Sending key ', expected_index, ' from DB to device...')
new_key.write_value(infected_period_keys[expected_index])
#TODO do we need this?
time.sleep(1)
else:
go_on = False
# Main function implements the program logic so it can run in a background
# thread. Most platforms require the main thread to handle GUI events and other
# asyncronous events like BLE actions. All of the threading logic is taken care
# of automatically though and you just need to provide a main function that uses
# the BLE provider.
def main():
# Clear any cached data because both bluez and CoreBluetooth have issues with
# caching data and it going stale.
ble.clear_cached_data()
# Get the first available BLE network adapter and make sure it's powered on.
adapter = ble.get_default_adapter()
adapter.power_on()
print('Using adapter: {0}'.format(adapter.name))
# Disconnect any currently connected UART devices. Good for cleaning up and
# starting from a fresh state.
print('Disconnecting any connected Covid devices...')
ble.disconnect_devices([COVID_SERVICE_UUID])
while True:
# Scan for Covid devices.
print('Searching for Covid device...')
try:
adapter.start_scan()
# Search for the first Covid device found (will time out after 60 seconds
# but you can specify an optional timeout_sec parameter to change it).
device = ble.find_device(service_uuids=[COVID_SERVICE_UUID])
if device is None:
raise RuntimeError('Failed to find Covid device!')
finally:
# Make sure scanning is stopped before exiting.
adapter.stop_scan()
if device is not None:
print('Connecting to device...')
device.connect() # Will time out after 60 seconds, specify timeout_sec parameter
# to change the timeout.
# Once connected do everything else in a try/finally to make sure the device
# is disconnected when done.
try:
# Wait for service discovery to complete for at least the specified
# service and characteristic UUID lists. Will time out after 60 seconds
# (specify timeout_sec parameter to override).
print('Discovering services...')
device.discover([COVID_SERVICE_UUID], [NEXT_KEY_UUID, NEW_KEY_UUID,
INFECTED_KEY_CNT_UUID,
PERIOD_KEY_0_UUID,
PERIOD_KEY_1_UUID,
PERIOD_KEY_2_UUID,
PERIOD_KEY_3_UUID,
PERIOD_KEY_4_UUID,
PERIOD_KEY_5_UUID,
PERIOD_KEY_6_UUID,
PERIOD_KEY_7_UUID,
PERIOD_KEY_8_UUID,
PERIOD_KEY_9_UUID,
PERIOD_KEY_10_UUID,
PERIOD_KEY_11_UUID,
PERIOD_KEY_12_UUID,
PERIOD_KEY_13_UUID
])
# Find the Covid service and its characteristics.
covid = device.find_service(COVID_SERVICE_UUID)
check_infection(covid)
upload_keys(covid)
finally:
# Make sure device is disconnected on exit.
device.disconnect()
# Initialize the BLE system. MUST be called before other BLE calls!
ble.initialize()
# Start the mainloop to process BLE events, and run the provided function in
# a background thread. When the provided main function stops running, returns
# an integer status code, or throws an error the program will exit.
ble.run_mainloop_with(main)

17
prj.conf Normal file
View File

@ -0,0 +1,17 @@
CONFIG_BT=y
CONFIG_BT_BROADCASTER=y
CONFIG_BT_OBSERVER=y
CONFIG_BT_DEBUG_LOG=y
CONFIG_BT_SMP=y
CONFIG_BT_PERIPHERAL=y
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"
#CONFIG_BT_HCI_TX_STACK_SIZE=1024
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048

233
src/contacts.c Normal file
View File

@ -0,0 +1,233 @@
/*
* Copyright (c) 2020 Olaf Landsiedel
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <zephyr/types.h>
#include <stddef.h>
#include <string.h>
#include <sys/printk.h>
#include <sys/util.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <zephyr.h>
#include <device.h>
#include <devicetree.h>
#include <drivers/gpio.h>
#include "covid_types.h"
#include "contacts.h"
#include "exposure-notification.h"
#include "covid.h"
typedef struct contact {
u32_t most_recent_contact_time; //TODO: what is the correct type here?
u32_t first_contact_time; //TODO: what is the correct type here?
u16_t cnt;
s8_t max_rssi;
rolling_proximity_identifier_t rolling_proximity_identifier;
associated_encrypted_metadata_t associated_encrypted_metadata;
} contact_t;
#define MAX_CONTACTS 1000
static contact_t contacts[MAX_CONTACTS];
static u32_t contact_count = 0;
void print_rpi(rolling_proximity_identifier_t* rpi){
for( int i = 0; i < sizeof(rolling_proximity_identifier_t); i++){
printk("%02x", rpi->data[i]);
}
}
void print_aem(associated_encrypted_metadata_t* aem){
for( int i = 0; i < sizeof(associated_encrypted_metadata_t); i++){
printk("%02x", aem->data[i]);
}
}
contact_t* find_contact(rolling_proximity_identifier_t* rpi, associated_encrypted_metadata_t* aem){
for( int i = 0; i < contact_count; i++ ){
//print_rpi(&contacts[i].rolling_proximity_identifier);
//print_aem(&contacts[i].associated_encrypted_metadata);
if ( memcmp(&contacts[i].rolling_proximity_identifier, rpi, sizeof(rolling_proximity_identifier_t)) == 0 &&
memcmp(&contacts[i].associated_encrypted_metadata, aem, sizeof(associated_encrypted_metadata_t)) == 0 ) {
return &contacts[i];
}
}
return NULL;
}
int check_add_contact(u32_t contact_time, rolling_proximity_identifier_t* rpi, associated_encrypted_metadata_t* aem, s8_t rssi){
contact_t* contact = find_contact(rpi, aem);
if( contact == NULL ){
if( contact_count >= MAX_CONTACTS ){
printk("out of contact buffers\n");
return -1;
}
printk("adding contact: rpi ");
print_rpi(rpi);
printk(" aem ");
print_aem(aem);
printk(" rssi %i \n", rssi);
contacts[contact_count].most_recent_contact_time = contact_time;
contacts[contact_count].first_contact_time = contact_time;
contacts[contact_count].cnt = 1;
contacts[contact_count].max_rssi = rssi;
memcpy(&contacts[contact_count].rolling_proximity_identifier, rpi, sizeof(rolling_proximity_identifier_t));
memcpy(&contacts[contact_count].associated_encrypted_metadata, aem, sizeof(associated_encrypted_metadata_t));
contact_count++;
} else {
contact->most_recent_contact_time = contact_time;
if( contacts->cnt < 0xFFFF ){ //avoid overflows
contacts->cnt++;
}
if( rssi > contact->max_rssi ){
contact->max_rssi = rssi;
}
// printk("update contact: rpi ");
// print_rpi(rpi);
// printk(" aem ");
// print_aem(aem);
// printk(" rssi %i, cnt %i \n", rssi, contacts->cnt);
}
return 0;
}
typedef struct period_contact {
u32_t duration;
u16_t cnt;
s8_t max_rssi; //TODO also store avg rssi?
rolling_proximity_identifier_t rolling_proximity_identifier;
associated_encrypted_metadata_t associated_encrypted_metadata;
} period_contact_t;
#define MAX_PERIOD_CONTACTS 400
#define PERIODS 14
typedef struct period_contacts{
int cnt;
period_contact_t period_contacts[MAX_PERIOD_CONTACTS];
} period_contacts_t;
static period_contacts_t period_contacts[PERIODS];
static int period_index = 0;
static s32_t next_infected_key_id = 0;
//10 minutes are over and we got new keys. Time to also sort our short term contacts and move them to long-term log
//TODO: move long-term storage to flash, as we have limited space in RAM
void key_change(int current_period_index){
if(current_period_index >= PERIODS){
printk("error, current periods index %i too large", current_period_index);
return;
}
if(current_period_index != period_index){
//printk("new period index\n");
period_index = current_period_index;
period_contacts[period_index].cnt = 0;
}
//check all short-term contacts (as long as we have space in our long-term storage)
for( int i = 0; i < contact_count && period_contacts[period_index].cnt < MAX_PERIOD_CONTACTS; i++){
//printk("check contact %i, duration %i\n", i, contacts[i].most_recent_contact_time - contacts[i].first_contact_time);
int index = period_contacts[period_index].cnt;
if( (contacts[i].most_recent_contact_time - contacts[i].first_contact_time) > (EN_INTERVAL_LENGTH / 2)){
period_contacts[period_index].period_contacts[index].duration = contacts[i].most_recent_contact_time - contacts[i].first_contact_time;
period_contacts[period_index].period_contacts[index].cnt = contacts[i].cnt;
period_contacts[period_index].period_contacts[index].max_rssi = contacts[i].max_rssi;
memcpy(&period_contacts[period_index].period_contacts[index].rolling_proximity_identifier, &contacts[i].rolling_proximity_identifier, sizeof(rolling_proximity_identifier_t));
memcpy(&period_contacts[period_index].period_contacts[index].associated_encrypted_metadata, &contacts[i].associated_encrypted_metadata, sizeof(associated_encrypted_metadata_t));
printk("store contact %i as exposure %i: rpi ", i, period_contacts[period_index].cnt);
print_rpi(&period_contacts[period_index].period_contacts[index].rolling_proximity_identifier);
printk(" aem ");
print_aem(&period_contacts[period_index].period_contacts[index].associated_encrypted_metadata);
printk(" max rssi %i, cnt %u, duration %u\n", period_contacts[period_index].period_contacts[index].max_rssi, period_contacts[period_index].period_contacts[index].cnt, period_contacts[period_index].period_contacts[index].duration);
period_contacts[period_index].cnt++;
}
}
contact_count = 0;
}
/* The devicetree node identifier for the "led1" alias. */
#define LED1_NODE DT_ALIAS(led1)
#if DT_NODE_HAS_STATUS(LED1_NODE, okay)
#define LED1 DT_GPIO_LABEL(LED1_NODE, gpios)
#define PIN DT_GPIO_PIN(LED1_NODE, gpios)
#if DT_PHA_HAS_CELL(LED1_NODE, gpios, flags)
#define FLAGS DT_GPIO_FLAGS(LED1_NODE, gpios)
#endif
#else
/* A build error here means your board isn't set up to blink an LED. */
#error "Unsupported board: led0 devicetree alias is not defined"
#define LED1 ""
#define PIN 0
#endif
#ifndef FLAGS
#define FLAGS 0
#endif
struct device *dev;
void add_infected_key(period_t* period){
//printk("Interval: %u\n", period->periodInterval);
//printk("RPI: "); print_rpi((rolling_proximity_identifier_t*)&period->periodKey); printk("\n");
next_infected_key_id++;
//find correct "day", TODO: also check a bit before and after
int index = get_index_by_interval(period->periodInterval);
if( index < 0 ){
printk("Exposure check: period %i not found\n", period->periodInterval);
return;
}
for( int i = 0; i < EN_TEK_ROLLING_PERIOD; i++){
static ENIntervalIdentifier intervalIdentifier;
en_derive_interval_identifier(&intervalIdentifier, &period->periodKey, period->periodInterval + i);
//go through all long-term contacts for this day and check if I have seen the intervalIdentifier
for( int j = 0; j < period_contacts[index].cnt; j++){
int ret = memcmp(&period_contacts[index].period_contacts[j].rolling_proximity_identifier, &intervalIdentifier, sizeof(rolling_proximity_identifier_t));
if( ret == 0 ){
printk("Found exposure: rpi ");
print_rpi(&period_contacts[index].period_contacts[j].rolling_proximity_identifier);
printk(" aem ");
print_aem(&period_contacts[index].period_contacts[j].associated_encrypted_metadata);
printk(" max rssi %i, cnt %u, duration %u\n", period_contacts[index].period_contacts[j].max_rssi, period_contacts[index].period_contacts[j].cnt, period_contacts[index].period_contacts[j].duration);
gpio_pin_set(dev, PIN, (int)1);
}
}
}
}
u32_t get_next_infected_key_id(){
return next_infected_key_id;
}
void init_contacts(){
contact_count = 0;
period_index = 0;
dev = device_get_binding(LED1);
if (dev == NULL) {
return;
}
int ret = gpio_pin_configure(dev, PIN, GPIO_OUTPUT_ACTIVE | FLAGS);
if (ret < 0) {
return;
}
gpio_pin_set(dev, PIN, (int)0);
}

26
src/contacts.h Normal file
View File

@ -0,0 +1,26 @@
/*
* Copyright (c) 2020 Olaf Landsiedel
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef CONTACTS_H
#define CONTACTS_H
#include <zephyr.h>
#include <zephyr/types.h>
#include "covid_types.h"
#include "covid.h"
void init_contacts();
int check_add_contact(u32_t contact_time, rolling_proximity_identifier_t* rpi, associated_encrypted_metadata_t* aem, s8_t rssi);
void key_change(int current_period_index);
void add_infected_key(period_t* period);
u32_t get_next_infected_key_id();
void print_rpi(rolling_proximity_identifier_t* rpi);
void print_aem(associated_encrypted_metadata_t* aem);
#endif

230
src/covid.c Normal file
View File

@ -0,0 +1,230 @@
/*
* Copyright (c) 2020 Olaf Landsiedel
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/types.h>
#include <stddef.h>
#include <sys/printk.h>
#include <sys/util.h>
#include <string.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include "exposure-notification.h"
#include "covid_types.h"
#include "contacts.h"
#include "covid.h"
typedef struct covid_adv_svd {
uint16_t ens;
rolling_proximity_identifier_t rolling_proximity_identifier;
associated_encrypted_metadata_t associated_encrypted_metadata;
} __packed covid_adv_svd_t;
const static bt_metadata_t bt_metadata= {
.version = 0b00100000,
.tx_power = 0, //TODO set to actual transmit power
.rsv1 = 0,
.rsv2 = 0,
};
#define COVID_ENS (0xFD6F)
static covid_adv_svd_t covid_adv_svd= {
.ens = COVID_ENS,
//do not initialiuze the rest of the packet, will write this later
};
static struct bt_data ad[] = {
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
BT_DATA_BYTES(BT_DATA_UUID16_ALL, 0x6f, 0xfd), //0xFD6F Exposure Notification Service
BT_DATA(BT_DATA_SVC_DATA16, &covid_adv_svd, sizeof(covid_adv_svd_t))
};
static void scan_cb(const bt_addr_le_t *addr, s8_t rssi, u8_t adv_type, struct net_buf_simple *buf)
{
if( adv_type == 3 ){
u8_t len = 0;
while (buf->len > 1) {
u8_t type;
len = net_buf_simple_pull_u8(buf);
if (!len) {
break;
}
/* Check if field length is correct */
if (len > buf->len || buf->len < 1) {
break;
}
type = net_buf_simple_pull_u8(buf);
if (type == BT_DATA_SVC_DATA16 && len == sizeof(covid_adv_svd_t) + 1){
covid_adv_svd_t* rx_adv = (covid_adv_svd_t*) buf->data;
if (rx_adv->ens == COVID_ENS){
check_add_contact(k_uptime_get() / 1000, &rx_adv->rolling_proximity_identifier, &rx_adv->associated_encrypted_metadata, rssi);
}
}
net_buf_simple_pull(buf, len - 1); //consume the rest, note we already consumed one byte via net_buf_simple_pull_u8(buf)
}
}
}
#define NUM_PERIOD_KEYS (14)
static period_t periods[NUM_PERIOD_KEYS];
static int current_period_index = 0;
static ENIntervalNumber currentInterval;
static unsigned int period_cnt = 0;
static ENPeriodMetadataEncryptionKey periodMetadataEncryptionKey;
static ENIntervalIdentifier intervalIdentifier;
static associated_encrypted_metadata_t encryptedMetadata;
static bool init = 1;
static bool infected = 0;
static void new_period_key(time_t currentTime ){
printk("\n----------------------------------------\n\n");
printk("\n----------------------------------------\n\n");
printk("*** New Period\n");
current_period_index = period_cnt % NUM_PERIOD_KEYS;
periods[current_period_index].periodInterval = en_get_interval_number_at_period_start(currentTime);
printk("periodInterval %u\n", periods[current_period_index].periodInterval);
en_generate_period_key(&periods[current_period_index].periodKey);
period_cnt++;
}
//To be called when new keys are needed
static void check_keys(struct k_work *work){
// we check the current time to know if we actually need to regenerate anything
// TODO: Use real unix timestamp!: currentTime = time(NULL);
time_t currentTime = k_uptime_get() / 1000;
ENIntervalNumber newInterval = en_get_interval_number(currentTime);
if( currentInterval != newInterval || init){
currentInterval = newInterval;
bool newPeriod = ((currentInterval - periods[current_period_index].periodInterval) >= EN_TEK_ROLLING_PERIOD);
// we check if we need to generate new keys
if (newPeriod || init) {
new_period_key(currentTime);
}
// we now generate the new interval identifier and re-encrypt the metadata
en_derive_interval_identifier(&intervalIdentifier, &periods[current_period_index].periodKey, currentInterval);
en_encrypt_interval_metadata(&periodMetadataEncryptionKey, &intervalIdentifier, (unsigned char*)&bt_metadata, (unsigned char*)&encryptedMetadata, sizeof(associated_encrypted_metadata_t));
// broadcast intervalIdentifier plus encryptedMetada according to specs
//printk("\n----------------------------------------\n\n");
printk("Time: %llu, ", currentTime);
printk("Interval: %u, ", currentInterval);
printk("RPI: "); print_rpi((rolling_proximity_identifier_t*)&intervalIdentifier); printk(", ");
printk("AEM: "); print_aem(&encryptedMetadata); printk("\n");
//TODO do we have to worry about race conditions here?
//worst case: we would be advertising a wrong key for a while
memcpy(&covid_adv_svd.rolling_proximity_identifier, &intervalIdentifier, sizeof(rolling_proximity_identifier_t));
memcpy(&covid_adv_svd.associated_encrypted_metadata, &encryptedMetadata, sizeof(associated_encrypted_metadata_t));
if( !init ){
key_change(current_period_index);
}
init = 0;
}
}
K_WORK_DEFINE(my_work, check_keys);
static void my_timer_handler(struct k_timer *dummy){
k_work_submit(&my_work);
}
K_TIMER_DEFINE(my_timer, my_timer_handler, NULL);
static const struct bt_le_scan_param scan_param = {
.type = BT_HCI_LE_SCAN_PASSIVE,
.filter_dup = BT_HCI_LE_SCAN_FILTER_DUP_DISABLE,
.interval = 0x0010, //Scan Interval (N * 0.625 ms), TODO: set to correct interval
.window = 0x0010, //Scan Window (N * 0.625 ms), TODO: set to correct interval
};
#define KEY_CHECK_INTERVAL (K_MSEC(EN_INTERVAL_LENGTH * 1000 / 10))
int init_covid(){
// TODO: Use real unix timestamp!: currentTime = time(NULL);
init = 1;
period_cnt = 0;
infected = 0;
check_keys(NULL);
int err = 0;
err = bt_le_scan_start(&scan_param, scan_cb);
if (err) {
printk("Starting scanning failed (err %d)\n", err);
return err;
}
k_timer_start(&my_timer, KEY_CHECK_INTERVAL, KEY_CHECK_INTERVAL);
return 0;
}
int do_covid(){
//printk("covid start\n");
int err = 0;
err = bt_le_adv_start(BT_LE_ADV_NCONN, ad, ARRAY_SIZE(ad), NULL, 0);
if (err) {
printk("Advertising failed to start (err %d)\n", err);
return err;
}
k_sleep(K_SECONDS(10));
err = bt_le_adv_stop();
if (err) {
printk("Advertising failed to stop (err %d)\n", err);
return err;
}
//printk("covid end\n");
return 0;
}
bool get_infection(){
return infected;
}
void set_infection(bool _infected){
infected = _infected;
}
unsigned int get_period_cnt_if_infected(){
if( !infected ){
return 0;
}
return period_cnt;
}
period_t* get_period_if_infected(unsigned int id, size_t* size){
if( !infected || id >= NUM_PERIOD_KEYS || id >= period_cnt ){
*size = 0;
return NULL;
}
*size = sizeof(period_t);
return &periods[id];
}
int get_index_by_interval(ENIntervalNumber periodInterval){
int index = 0;
while( index < NUM_PERIOD_KEYS || index < period_cnt ){
if( periods[index].periodInterval == periodInterval){
return index;
}
index++;
}
return -1;
}

29
src/covid.h Normal file
View File

@ -0,0 +1,29 @@
/*
* Copyright (c) 2020 Olaf Landsiedel
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef COVID_H
#define COVID_H
#include "exposure-notification.h"
typedef struct period{
ENPeriodKey periodKey;
ENIntervalNumber periodInterval;
} __packed period_t;
int init_covid();
int do_covid();
bool get_infection();
void set_infection(bool _infected);
unsigned int get_period_cnt_if_infected();
period_t* get_period_if_infected(unsigned int id, size_t* size);
int get_index_by_interval(ENIntervalNumber periodInterval);
void print_periods();
#endif

31
src/covid_types.h Normal file
View File

@ -0,0 +1,31 @@
/*
* Copyright (c) 2020 Olaf Landsiedel
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef COVID_TYPES_H
#define COVID_TYPES_H
#include <zephyr/types.h>
#define COVID_ROLLING_PROXIMITY_IDENTIFIER_LEN 16
typedef struct rolling_proximity_identifier {
uint8_t data[COVID_ROLLING_PROXIMITY_IDENTIFIER_LEN];
} __packed rolling_proximity_identifier_t;
typedef struct bt_metadata {
uint8_t version;
uint8_t tx_power;
uint8_t rsv1;
uint8_t rsv2;
} __packed bt_metadata_t;
//typedef struct bt_metadata bt_metadata_t;
typedef struct associated_encrypted_metadata {
uint8_t data[sizeof(bt_metadata_t)];
} __packed associated_encrypted_metadata_t;
#endif

135
src/exposure-notification.c Normal file
View File

@ -0,0 +1,135 @@
/*
* Copyright (c) 2020 Patrick Rathje
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <zephyr/types.h>
#include <stddef.h>
#include <sys/printk.h>
#include <sys/util.h>
#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 "exposure-notification.h"
static mbedtls_ctr_drbg_context ctr_drbg;
static ENRandomBytesCallback random_bytes_callback = NULL;
int en_mbedtls_random_bytes_fallback(void *buf, size_t len) {
return mbedtls_ctr_drbg_random(&ctr_drbg, buf, len);
}
#if EN_INIT_MBEDTLS_ENTROPY
int init_mbedtls_entropy() {
mbedtls_entropy_context entropy;
mbedtls_entropy_init( &entropy );
static char *personalization = "exposure-notification";
mbedtls_ctr_drbg_init( &ctr_drbg );
int ret = mbedtls_ctr_drbg_seed( &ctr_drbg , mbedtls_entropy_func, &entropy,
(const unsigned char *) personalization,
strlen( personalization ) );
return ret;
}
#endif
int en_init(ENRandomBytesCallback user_random_bytes_callback) {
int ret = 0;
#if EN_INIT_MBEDTLS_ENTROPY
ret = init_mbedtls_entropy();
#endif
// Fallback to mbedtls random bytes implementation and init the entropy
if (user_random_bytes_callback == NULL) {
random_bytes_callback = en_mbedtls_random_bytes_fallback;
} else {
random_bytes_callback = user_random_bytes_callback;
}
return ret;
}
ENIntervalNumber en_get_interval_number(time_t timestamp) {
return timestamp / EN_INTERVAL_LENGTH;
}
ENIntervalNumber en_get_interval_number_at_period_start(time_t timestamp) {
ENIntervalNumber current = en_get_interval_number(timestamp);
// round to period start
return (current/EN_TEK_ROLLING_PERIOD)*EN_TEK_ROLLING_PERIOD;
}
void en_generate_period_key(ENPeriodKey* periodKey) {
// TODO: error handling
random_bytes_callback(periodKey->b, sizeof(periodKey->b));
}
void en_derive_period_identifier_key(ENPeriodIdentifierKey *periodIdentifierKey, const ENPeriodKey *periodKey) {
const mbedtls_md_info_t *sha256_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
// TODO: Include \0 in the string?
// TODO: Check return value
// TODO: Is this utf-8 info correct?
// TODO: Correct erros in en_get_period_metadata_encryption_key as well!
const unsigned char info[7] = "EN-RPIK";
mbedtls_hkdf(sha256_info, 0, 0, periodKey->b, sizeof(periodKey->b), info, sizeof(info), periodIdentifierKey->b, sizeof(periodIdentifierKey->b));
}
void en_derive_interval_identifier(ENIntervalIdentifier *intervalIdentifier, const ENPeriodIdentifierKey *periodIdentifierKey, const ENIntervalNumber intervalNumber) {
unsigned char paddedData[16] = "EN-RPI";
// copy intervalNumber in little endian format
paddedData[12] = (intervalNumber >> 0) &0xFF;
paddedData[13] = (intervalNumber >> 8) &0xFF;
paddedData[14] = (intervalNumber >> 16) &0xFF;
paddedData[15] = (intervalNumber >> 24) &0xFF;
mbedtls_aes_context aes;
mbedtls_aes_init( &aes );
// Encrypt the padded data to get the identifier
mbedtls_aes_setkey_enc( &aes, (const unsigned char*) periodIdentifierKey->b, sizeof(periodIdentifierKey->b) * 8 );
mbedtls_aes_crypt_ecb(&aes, MBEDTLS_AES_ENCRYPT, paddedData, intervalIdentifier->b);
mbedtls_aes_free( &aes );
}
void en_derive_period_metadata_encryption_key(ENPeriodMetadataEncryptionKey *periodMetadataEncryptionKey, const ENPeriodKey *periodKey) {
const mbedtls_md_info_t *sha256_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
const unsigned char info[7] = "EN-AEMK";
mbedtls_hkdf(sha256_info, 0, 0, periodKey->b, sizeof(periodKey->b), info, sizeof(info), periodMetadataEncryptionKey->b, sizeof(periodMetadataEncryptionKey->b));
}
void en_encrypt_interval_metadata(const ENPeriodMetadataEncryptionKey *periodMetadataEncryptionKey, const ENIntervalIdentifier *intervalIdentifier, unsigned char *input, unsigned char *output, size_t inputSize) {
mbedtls_aes_context aes;
mbedtls_aes_init( &aes );
// Encrypt the padded data to get the identifier
size_t nonceOffset = 0;
unsigned char nonceCounter[16];
unsigned char streamBlock[16];
// init nonce/IV to intervalIdentifer
memcpy(nonceCounter, intervalIdentifier->b, sizeof(nonceCounter));
memset(streamBlock, 0, sizeof(streamBlock));
mbedtls_aes_setkey_enc( &aes, (const unsigned char*) periodMetadataEncryptionKey->b, sizeof(periodMetadataEncryptionKey->b) * 8 );
mbedtls_aes_crypt_ctr(&aes, inputSize, &nonceOffset, nonceCounter, streamBlock, input, output);
mbedtls_aes_free( &aes );
}
void en_decrypt_interval_metadata(const ENPeriodMetadataEncryptionKey *periodMetadataEncryptionKey, const ENIntervalIdentifier *intervalIdentifier, unsigned char *input, unsigned char *output, size_t inputSize) {
en_encrypt_interval_metadata(periodMetadataEncryptionKey, intervalIdentifier, input, output, inputSize);
}

170
src/exposure-notification.h Normal file
View File

@ -0,0 +1,170 @@
/*
* Copyright (c) 2020 Patrick Rathje
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef EXPOSURE_NOTIFICATION_H
#define EXPOSURE_NOTIFICATION_H
#ifdef __cplusplus
extern "C" {
#endif
#include <time.h>
#include <stdint.h>
#include <stddef.h>
#ifndef EN_TEK_ROLLING_PERIOD
#define EN_TEK_ROLLING_PERIOD 144
#endif
#ifndef EN_INTERVAL_LENGTH
#define EN_INTERVAL_LENGTH 600
#endif
#ifndef EN_INIT_MBEDTLS_ENTROPY
#define EN_INIT_MBEDTLS_ENTROPY 1
#endif
/*
* Type: _ENBaseKey
* --------------------
* 16 Byte base key definition
*/
typedef struct _ENBaseKey { unsigned char b[16];} _ENBaseKey;
/*
* Type: ENIntervalNumber
* --------------------
* The number of the 10 minute interval
*/
typedef uint32_t ENIntervalNumber;
/*
* Type: ENPeriodKey
* --------------------
* 16 Byte temporary exposure key used throughout a rolling period (one day)
*/
typedef struct _ENBaseKey ENPeriodKey;
/*
* Type: ENPeriodIdentifierKey
* --------------------
* 16 Byte key used throughout each period to encrypt the identifier for each interval
*/
typedef struct _ENBaseKey ENPeriodIdentifierKey;
/*
* Type: ENIntervalIdentifier
* --------------------
* 16 Byte identifier used within the packet
*/
typedef struct _ENBaseKey ENIntervalIdentifier;
/*
* Type: ENPeriodMetadataEncryptionKey
* --------------------
* 16 Byte key used throughout each interval to encrypt the metadata within one period
*/
typedef struct _ENBaseKey ENPeriodMetadataEncryptionKey;
/*
* Type: ENPeriodMetadataEncryptionKey
* --------------------
* 16 Byte key used throughout each interval to encrypt the metadata within one period
*/
typedef struct _ENBaseKey ENPeriodMetadataEncryptionKey;
typedef int (*ENRandomBytesCallback)(void *buf, size_t len);
/*
* Function: en_init
* --------------------
* Initializes the random number generator. Use NULL to initialize with the fallback random_bytes_func
*/
int en_init(ENRandomBytesCallback user_random_bytes_callback);
/*
* Function: en_get_interval_number
* --------------------
* computes the interval number according to the given unix timestamp. Each interval lasts for EN_INTERVAL_LENGTH secs (defaults to 10 minutes).
*/
ENIntervalNumber en_get_interval_number(time_t timestamp);
/*
* Function: en_get_period_start_interval_number
* --------------------
* computes the first interval number for the period given by the unix timestamp.
*/
ENIntervalNumber en_get_interval_number_at_period_start(time_t timestamp);
/*
* Function: en_generate_period_key
* --------------------
* Generates a new period key using a cryptographic number generator. Used when the next period is dues (once a day).
*/
void en_generate_period_key(ENPeriodKey* periodKey);
/*
* Function: en_derive_period_identifier_key
* --------------------
* Derives the interval identifier key based on the current period key
*/
void en_derive_period_identifier_key(ENPeriodIdentifierKey *periodIdentifierKey, const ENPeriodKey *periodKey);
/*
* Function: en_derive_interval_identifier
* --------------------
* Get the identifier for a specific interval based on the identifierKey
*/
void en_derive_interval_identifier(ENIntervalIdentifier *intervalIdentifier, const ENPeriodIdentifierKey *periodIdentifierKey, const ENIntervalNumber intervalNumber);
/*
* Function: en_derive_period_metadata_encryption_key
* --------------------
* Derives the interval metadata encryption key based on the current period key
*/
void en_derive_period_metadata_encryption_key(ENPeriodMetadataEncryptionKey *periodMetadataEncryptionKey, const ENPeriodKey *periodKey);
/*
* Function: en_encrypt_interval_metadata
* --------------------
* Encrypts the metadata.
*/
void en_encrypt_interval_metadata(const ENPeriodMetadataEncryptionKey *periodMetadataEncryptionKey, const ENIntervalIdentifier *intervalIdentifier, unsigned char *input, unsigned char *output, size_t inputSize);
/*
* Function: en_decrypt_interval_metadata
* --------------------
* Decrypts the metadata.
*/
void en_decrypt_interval_metadata(const ENPeriodMetadataEncryptionKey *periodMetadataEncryptionKey, const ENIntervalIdentifier *intervalIdentifier, unsigned char *input, unsigned char *output, size_t inputSize);
/*
* Function: en_generate_period_relevant_keys()
* --------------------
* This function generates all period keys at once (convenient right?)
*/
static inline void en_generate_and_derive_period_keys(ENPeriodKey *periodKey, ENPeriodIdentifierKey *periodIdentifierKey, ENPeriodMetadataEncryptionKey *periodMetadataEncryptionKey) {
en_generate_period_key(periodKey);
en_derive_period_identifier_key(periodIdentifierKey, periodKey);
en_derive_period_metadata_encryption_key(periodMetadataEncryptionKey, periodKey);
}
#ifdef __cplusplus
}
#endif
#endif

303
src/gatt-service.c Normal file
View File

@ -0,0 +1,303 @@
/*
* Copyright (c) 2020 Olaf Landsiedel
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/types.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <sys/printk.h>
#include <sys/byteorder.h>
#include <zephyr.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/conn.h>
#include <bluetooth/uuid.h>
#include <bluetooth/gatt.h>
#include "covid.h"
#include "contacts.h"
//TODO: change device name in project conf, set id dynamicaally, so that not all devices have the same name...
// 0651C79E-ADA7-42EA-986A-9F69790D11F2
static struct bt_uuid_128 covid_service_uuid = BT_UUID_INIT_128(
0x06, 0x51, 0xC7, 0x9E, 0xAD, 0xA7, 0x42, 0xEA,
0x98, 0x6A, 0x9F, 0x69, 0x79, 0x0D, 0x11, 0xF2);
static struct bt_uuid_128 next_key_uuid = BT_UUID_INIT_128(
0x06, 0x51, 0xC7, 0x9E, 0xAD, 0xA7, 0x42, 0xEA,
0x98, 0x6A, 0x9F, 0x69, 0x79, 0x0D, 0x11, 0xF3);
static struct bt_uuid_128 new_key_uuid = BT_UUID_INIT_128(
0x06, 0x51, 0xC7, 0x9E, 0xAD, 0xA7, 0x42, 0xEA,
0x98, 0x6A, 0x9F, 0x69, 0x79, 0x0D, 0x11, 0xF4);
static struct bt_uuid_128 infected_key_cnt_uuid = BT_UUID_INIT_128(
0x06, 0x51, 0xC7, 0x9E, 0xAD, 0xA7, 0x42, 0xEA,
0x98, 0x6A, 0x9F, 0x69, 0x79, 0x0D, 0x11, 0xF5);
static struct bt_uuid_128 infected_key_0_uuid = BT_UUID_INIT_128(
0x06, 0x51, 0xC7, 0x9E, 0xAD, 0xA7, 0x42, 0xEA,
0x98, 0x6A, 0x9F, 0x69, 0x79, 0x0D, 0x11, 0x00);
static struct bt_uuid_128 infected_key_1_uuid = BT_UUID_INIT_128(
0x06, 0x51, 0xC7, 0x9E, 0xAD, 0xA7, 0x42, 0xEA,
0x98, 0x6A, 0x9F, 0x69, 0x79, 0x0D, 0x11, 0x01);
static struct bt_uuid_128 infected_key_2_uuid = BT_UUID_INIT_128(
0x06, 0x51, 0xC7, 0x9E, 0xAD, 0xA7, 0x42, 0xEA,
0x98, 0x6A, 0x9F, 0x69, 0x79, 0x0D, 0x11, 0x02);
static struct bt_uuid_128 infected_key_3_uuid = BT_UUID_INIT_128(
0x06, 0x51, 0xC7, 0x9E, 0xAD, 0xA7, 0x42, 0xEA,
0x98, 0x6A, 0x9F, 0x69, 0x79, 0x0D, 0x11, 0x03);
static struct bt_uuid_128 infected_key_4_uuid = BT_UUID_INIT_128(
0x06, 0x51, 0xC7, 0x9E, 0xAD, 0xA7, 0x42, 0xEA,
0x98, 0x6A, 0x9F, 0x69, 0x79, 0x0D, 0x11, 0x04);
static struct bt_uuid_128 infected_key_5_uuid = BT_UUID_INIT_128(
0x06, 0x51, 0xC7, 0x9E, 0xAD, 0xA7, 0x42, 0xEA,
0x98, 0x6A, 0x9F, 0x69, 0x79, 0x0D, 0x11, 0x05);
static struct bt_uuid_128 infected_key_6_uuid = BT_UUID_INIT_128(
0x06, 0x51, 0xC7, 0x9E, 0xAD, 0xA7, 0x42, 0xEA,
0x98, 0x6A, 0x9F, 0x69, 0x79, 0x0D, 0x11, 0x06);
static struct bt_uuid_128 infected_key_7_uuid = BT_UUID_INIT_128(
0x06, 0x51, 0xC7, 0x9E, 0xAD, 0xA7, 0x42, 0xEA,
0x98, 0x6A, 0x9F, 0x69, 0x79, 0x0D, 0x11, 0x07);
static struct bt_uuid_128 infected_key_8_uuid = BT_UUID_INIT_128(
0x06, 0x51, 0xC7, 0x9E, 0xAD, 0xA7, 0x42, 0xEA,
0x98, 0x6A, 0x9F, 0x69, 0x79, 0x0D, 0x11, 0x08);
static struct bt_uuid_128 infected_key_9_uuid = BT_UUID_INIT_128(
0x06, 0x51, 0xC7, 0x9E, 0xAD, 0xA7, 0x42, 0xEA,
0x98, 0x6A, 0x9F, 0x69, 0x79, 0x0D, 0x11, 0x09);
static struct bt_uuid_128 infected_key_10_uuid = BT_UUID_INIT_128(
0x06, 0x51, 0xC7, 0x9E, 0xAD, 0xA7, 0x42, 0xEA,
0x98, 0x6A, 0x9F, 0x69, 0x79, 0x0D, 0x11, 0x0A);
static struct bt_uuid_128 infected_key_11_uuid = BT_UUID_INIT_128(
0x06, 0x51, 0xC7, 0x9E, 0xAD, 0xA7, 0x42, 0xEA,
0x98, 0x6A, 0x9F, 0x69, 0x79, 0x0D, 0x11, 0x0B);
static struct bt_uuid_128 infected_key_12_uuid = BT_UUID_INIT_128(
0x06, 0x51, 0xC7, 0x9E, 0xAD, 0xA7, 0x42, 0xEA,
0x98, 0x6A, 0x9F, 0x69, 0x79, 0x0D, 0x11, 0x0C);
static struct bt_uuid_128 infected_key_13_uuid = BT_UUID_INIT_128(
0x06, 0x51, 0xC7, 0x9E, 0xAD, 0xA7, 0x42, 0xEA,
0x98, 0x6A, 0x9F, 0x69, 0x79, 0x0D, 0x11, 0x0D);
#define DEVICE_NAME CONFIG_BT_DEVICE_NAME
#define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1)
static const struct bt_data ad[] = {
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
/* My Service UUID (same as above) */
BT_DATA_BYTES(BT_DATA_UUID128_ALL,
0x06, 0x51, 0xC7, 0x9E, 0xAD, 0xA7, 0x42, 0xEA,
0x98, 0x6A, 0x9F, 0x69, 0x79, 0x0D, 0x11, 0xF2),
};
static ssize_t read_next_key(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, u16_t len, u16_t offset){
u32_t id = get_next_infected_key_id();
return bt_gatt_attr_read(conn, attr, buf, len, offset, &id, sizeof(id));
}
static ssize_t read_key_cnt(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, u16_t len, u16_t offset){
unsigned int cnt = get_period_cnt_if_infected();
return bt_gatt_attr_read(conn, attr, buf, len, offset, &cnt, sizeof(unsigned int));
}
static ssize_t read_key_0(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, u16_t len, u16_t offset){
size_t size = 0;
period_t* p = get_period_if_infected(0, &size);
// printk("read key 0, size %u, pointer %p\n", size, p);
// printk("Interval: %u\n", p->periodInterval);
// printk("RPI: "); print_rpi((rolling_proximity_identifier_t*)&p->periodKey); printk("\n");
return bt_gatt_attr_read(conn, attr, buf, len, offset, p, size);
}
static ssize_t read_key_1(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, u16_t len, u16_t offset){
size_t size = 0;
period_t* p = get_period_if_infected(1, &size);
return bt_gatt_attr_read(conn, attr, buf, len, offset, p, size);
}
static ssize_t read_key_2(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, u16_t len, u16_t offset){
size_t size = 0;
period_t* p = get_period_if_infected(2, &size);
return bt_gatt_attr_read(conn, attr, buf, len, offset, p, size);
}
static ssize_t read_key_3(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, u16_t len, u16_t offset){
size_t size = 0;
period_t* p = get_period_if_infected(3, &size);
return bt_gatt_attr_read(conn, attr, buf, len, offset, p, size);
}
static ssize_t read_key_4(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, u16_t len, u16_t offset){
size_t size = 0;
period_t* p = get_period_if_infected(4, &size);
return bt_gatt_attr_read(conn, attr, buf, len, offset, p, size);
}
static ssize_t read_key_5(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, u16_t len, u16_t offset){
size_t size = 0;
period_t* p = get_period_if_infected(5, &size);
return bt_gatt_attr_read(conn, attr, buf, len, offset, p, size);
}
static ssize_t read_key_6(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, u16_t len, u16_t offset){
size_t size = 0;
period_t* p = get_period_if_infected(6, &size);
return bt_gatt_attr_read(conn, attr, buf, len, offset, p, size);
}
static ssize_t read_key_7(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, u16_t len, u16_t offset){
size_t size = 0;
period_t* p = get_period_if_infected(7, &size);
return bt_gatt_attr_read(conn, attr, buf, len, offset, p, size);
}
static ssize_t read_key_8(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, u16_t len, u16_t offset){
size_t size = 0;
period_t* p = get_period_if_infected(8, &size);
return bt_gatt_attr_read(conn, attr, buf, len, offset, p, size);
}
static ssize_t read_key_9(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, u16_t len, u16_t offset){
size_t size = 0;
period_t* p = get_period_if_infected(9, &size);
return bt_gatt_attr_read(conn, attr, buf, len, offset, p, size);
}
static ssize_t read_key_10(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, u16_t len, u16_t offset){
size_t size = 0;
period_t* p = get_period_if_infected(10, &size);
return bt_gatt_attr_read(conn, attr, buf, len, offset, &p, size);
}
static ssize_t read_key_11(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, u16_t len, u16_t offset){
size_t size = 0;
period_t* p = get_period_if_infected(11, &size);
return bt_gatt_attr_read(conn, attr, buf, len, offset, &p, size);
}
static ssize_t read_key_12(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, u16_t len, u16_t offset){
size_t size = 0;
period_t* p = get_period_if_infected(12, &size);
return bt_gatt_attr_read(conn, attr, buf, len, offset, &p, size);
}
static ssize_t read_key_13(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, u16_t len, u16_t offset){
size_t size = 0;
period_t* p = get_period_if_infected(13, &size);
return bt_gatt_attr_read(conn, attr, buf, len, offset, &p, size);
}
//TODO: copy more than one key
static ssize_t write_new_key(struct bt_conn *conn,
const struct bt_gatt_attr *attr,
const void *buf, u16_t len, u16_t offset,
u8_t flags)
{
//printk("write key\n");
if( offset != 0 ){
printk("error: invalid offset %u\n", offset);
return len;
}
if( len != sizeof(period_t) ){
printk("error: invalid length %u (expected %u)\n", len, sizeof(period_t));
return len;
}
period_t* period = (period_t*)buf;
add_infected_key(period);
return len;
}
static void connected(struct bt_conn *conn, u8_t err)
{
if (err) {
printk("Connection failed (err 0x%02x)\n", err);
} else {
//printk("Connected\n");
}
}
static void disconnected(struct bt_conn *conn, u8_t reason)
{
//printk("Disconnected (reason 0x%02x)\n", reason);
}
static struct bt_conn_cb conn_callbacks = {
.connected = connected,
.disconnected = disconnected,
};
static void auth_cancel(struct bt_conn *conn)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
printk("Pairing cancelled: %s\n", addr);
}
static struct bt_conn_auth_cb auth_cb_display = {
.cancel = auth_cancel,
};
int init_gatt(void)
{
bt_conn_cb_register(&conn_callbacks);
bt_conn_auth_cb_register(&auth_cb_display);
return 0;
}
int do_gatt(void){
//printk("gatt start\n");
int err;
err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, ad, ARRAY_SIZE(ad), NULL, 0);
if (err) {
printk("Advertising failed to start (err %d)\n", err);
return err;
}
k_sleep(K_SECONDS(1));
err = bt_le_adv_stop();
if (err) {
printk("Advertising failed to stop (err %d)\n", err);
return err;
}
//printk("gatt end\n");
return 0;
}
BT_GATT_SERVICE_DEFINE(covid_svc,
BT_GATT_PRIMARY_SERVICE(&covid_service_uuid),
//TODO: switch on crypto and switch to signed keys
BT_GATT_CHARACTERISTIC(&next_key_uuid.uuid, BT_GATT_CHRC_READ,
BT_GATT_PERM_READ, read_next_key, NULL, NULL),
BT_GATT_CHARACTERISTIC(&new_key_uuid.uuid, BT_GATT_CHRC_WRITE,
BT_GATT_PERM_WRITE, NULL, write_new_key, NULL),
BT_GATT_CHARACTERISTIC(&infected_key_cnt_uuid.uuid, BT_GATT_CHRC_READ,
BT_GATT_PERM_READ, read_key_cnt, NULL, NULL),
BT_GATT_CHARACTERISTIC(&infected_key_0_uuid.uuid, BT_GATT_CHRC_READ,
BT_GATT_PERM_READ, read_key_0, NULL, NULL),
BT_GATT_CHARACTERISTIC(&infected_key_1_uuid.uuid, BT_GATT_CHRC_READ,
BT_GATT_PERM_READ, read_key_1, NULL, NULL),
BT_GATT_CHARACTERISTIC(&infected_key_2_uuid.uuid, BT_GATT_CHRC_READ,
BT_GATT_PERM_READ, read_key_2, NULL, NULL),
BT_GATT_CHARACTERISTIC(&infected_key_3_uuid.uuid, BT_GATT_CHRC_READ,
BT_GATT_PERM_READ, read_key_3, NULL, NULL),
BT_GATT_CHARACTERISTIC(&infected_key_4_uuid.uuid, BT_GATT_CHRC_READ,
BT_GATT_PERM_READ, read_key_4, NULL, NULL),
BT_GATT_CHARACTERISTIC(&infected_key_5_uuid.uuid, BT_GATT_CHRC_READ,
BT_GATT_PERM_READ, read_key_5, NULL, NULL),
BT_GATT_CHARACTERISTIC(&infected_key_6_uuid.uuid, BT_GATT_CHRC_READ,
BT_GATT_PERM_READ, read_key_6, NULL, NULL),
BT_GATT_CHARACTERISTIC(&infected_key_7_uuid.uuid, BT_GATT_CHRC_READ,
BT_GATT_PERM_READ, read_key_7, NULL, NULL),
BT_GATT_CHARACTERISTIC(&infected_key_8_uuid.uuid, BT_GATT_CHRC_READ,
BT_GATT_PERM_READ, read_key_8, NULL, NULL),
BT_GATT_CHARACTERISTIC(&infected_key_9_uuid.uuid, BT_GATT_CHRC_READ,
BT_GATT_PERM_READ, read_key_9, NULL, NULL),
BT_GATT_CHARACTERISTIC(&infected_key_10_uuid.uuid, BT_GATT_CHRC_READ,
BT_GATT_PERM_READ, read_key_10, NULL, NULL),
BT_GATT_CHARACTERISTIC(&infected_key_11_uuid.uuid, BT_GATT_CHRC_READ,
BT_GATT_PERM_READ, read_key_11, NULL, NULL),
BT_GATT_CHARACTERISTIC(&infected_key_12_uuid.uuid, BT_GATT_CHRC_READ,
BT_GATT_PERM_READ, read_key_12, NULL, NULL),
BT_GATT_CHARACTERISTIC(&infected_key_13_uuid.uuid, BT_GATT_CHRC_READ,
BT_GATT_PERM_READ, read_key_13, NULL, NULL),
);

13
src/gatt_service.h Normal file
View File

@ -0,0 +1,13 @@
/*
* Copyright (c) 2020 Olaf Landsiedel
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef GATT_SERVICE_H
#define GATT_SERVICE_H
int init_gatt(void);
int do_gatt(void);
#endif

226
src/main.c Normal file
View File

@ -0,0 +1,226 @@
/* main.c - Application main entry point */
/*
* Copyright (c) 2020 Olaf Landsiedel
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <device.h>
#include <drivers/gpio.h>
#include <sys/util.h>
#include <sys/printk.h>
#include <inttypes.h>
#include <bluetooth/hci.h>
#include <zephyr/types.h>
#include "exposure-notification.h"
#include "covid_types.h"
#include "contacts.h"
#include "gatt_service.h"
#include "covid.h"
/*
* Devicetree helper macro which gets the 'flags' cell from a 'gpios'
* property, or returns 0 if the property has no 'flags' cell.
*/
#define FLAGS_OR_ZERO(node) \
COND_CODE_1(DT_PHA_HAS_CELL(node, gpios, flags), \
(DT_GPIO_FLAGS(node, gpios)), \
(0))
/*
* Get button configuration from the devicetree sw0 alias.
*
* At least a GPIO device and pin number must be provided. The 'flags'
* cell is optional.
*/
//on my NRF board this is button 1
#define SW0_NODE DT_ALIAS(sw0)
#if DT_NODE_HAS_STATUS(SW0_NODE, okay)
#define SW0_GPIO_LABEL DT_GPIO_LABEL(SW0_NODE, gpios)
#define SW0_GPIO_PIN DT_GPIO_PIN(SW0_NODE, gpios)
#define SW0_GPIO_FLAGS (GPIO_INPUT | FLAGS_OR_ZERO(SW0_NODE))
#else
#error "Unsupported board: sw0 devicetree alias is not defined"
#define SW0_GPIO_LABEL ""
#define SW0_GPIO_PIN 0
#define SW0_GPIO_FLAGS 0
#endif
//on my NRF board this is button 2
#define SW1_NODE DT_ALIAS(sw1)
#if DT_NODE_HAS_STATUS(SW1_NODE, okay)
#define SW1_GPIO_LABEL DT_GPIO_LABEL(SW1_NODE, gpios)
#define SW1_GPIO_PIN DT_GPIO_PIN(SW1_NODE, gpios)
#define SW1_GPIO_FLAGS (GPIO_INPUT | FLAGS_OR_ZERO(SW1_NODE))
#else
#error "Unsupported board: sw1 devicetree alias is not defined"
#define SW1_GPIO_LABEL ""
#define SW1_GPIO_PIN 0
#define SW1_GPIO_FLAGS 0
#endif
/* The devicetree node identifier for the "led0" alias. */
#define LED0_NODE DT_ALIAS(led0)
#if DT_NODE_HAS_STATUS(LED0_NODE, okay)
#define LED0 DT_GPIO_LABEL(LED0_NODE, gpios)
#define PIN DT_GPIO_PIN(LED0_NODE, gpios)
#if DT_PHA_HAS_CELL(LED0_NODE, gpios, flags)
#define FLAGS DT_GPIO_FLAGS(LED0_NODE, gpios)
#endif
#else
/* A build error here means your board isn't set up to blink an LED. */
#error "Unsupported board: led0 devicetree alias is not defined"
#define LED0 ""
#define PIN 0
#endif
#ifndef FLAGS
#define FLAGS 0
#endif
static struct gpio_callback button_0_cb_data;
static struct gpio_callback button_1_cb_data;
void button_0_pressed(struct device *dev, struct gpio_callback *cb, u32_t pins){
set_infection(true);
gpio_pin_set(dev, PIN, (int)1);
printk("Button 0 (=infected) pressed at %" PRIu32 "\n", k_cycle_get_32());
}
void button_1_pressed(struct device *dev, struct gpio_callback *cb, u32_t pins){
set_infection(false);
gpio_pin_set(dev, PIN, (int)0);
printk("Button 1 (=healthy) pressed at %" PRIu32 "\n", k_cycle_get_32());
}
int init_io(){
struct device *button0, *button1;
int err = 0;
//struct device *led;
button0 = device_get_binding(SW0_GPIO_LABEL);
if (button0 == NULL) {
printk("Error: didn't find %s device\n", SW0_GPIO_LABEL);
return -1;
}
button1 = device_get_binding(SW1_GPIO_LABEL);
if (button1 == NULL) {
printk("Error: didn't find %s device\n", SW1_GPIO_LABEL);
return -1;
}
err = gpio_pin_configure(button0, SW0_GPIO_PIN, SW0_GPIO_FLAGS);
if (err != 0) {
printk("Error %d: failed to configure %s pin %d\n",
err, SW0_GPIO_LABEL, SW0_GPIO_PIN);
return err;
}
err = gpio_pin_configure(button1, SW1_GPIO_PIN, SW1_GPIO_FLAGS);
if (err != 0) {
printk("Error %d: failed to configure %s pin %d\n",
err, SW1_GPIO_LABEL, SW1_GPIO_PIN);
return err;
}
err = gpio_pin_interrupt_configure(button0,
SW0_GPIO_PIN,
GPIO_INT_EDGE_TO_ACTIVE);
if (err != 0) {
printk("Error %d: failed to configure interrupt on %s pin %d\n",
err, SW0_GPIO_LABEL, SW0_GPIO_PIN);
return err;
}
err = gpio_pin_interrupt_configure(button1,
SW1_GPIO_PIN,
GPIO_INT_EDGE_TO_ACTIVE);
if (err != 0) {
printk("Error %d: failed to configure interrupt on %s pin %d\n",
err, SW1_GPIO_LABEL, SW1_GPIO_PIN);
return err;
}
gpio_init_callback(&button_0_cb_data, button_0_pressed, BIT(SW0_GPIO_PIN));
gpio_add_callback(button0, &button_0_cb_data);
printk("Set up button at %s pin %d\n", SW0_GPIO_LABEL, SW0_GPIO_PIN);
gpio_init_callback(&button_1_cb_data, button_1_pressed, BIT(SW1_GPIO_PIN));
gpio_add_callback(button1, &button_1_cb_data);
printk("Set up button at %s pin %d\n", SW1_GPIO_LABEL, SW1_GPIO_PIN);
struct device *dev;
dev = device_get_binding(LED0);
if (dev == NULL) {
return -1;
}
err = gpio_pin_configure(dev, PIN, GPIO_OUTPUT_ACTIVE | FLAGS);
if (err < 0) {
printk("Error %d: failed to configure leds\n", err);
return err;
}
//Turn LED 0 off
gpio_pin_set(dev, PIN, (int)0);
return 0;
}
void main(void)
{
int err = 0;
printk("Starting Covid Contact Tracer\n");
// first init everything
// Use custom randomization as the mbdet_tls context initialization messes with the Zeyhr BLE stack.
err = en_init(sys_csrand_get);
if (err) {
printk("Cyrpto init failed (err %d)\n", err);
return;
}
printk("init contacts\n");
init_contacts();
err = init_io();
if(err){
printk("Button init failed (err %d)\n", err);
return;
}
/* Initialize the Bluetooth Subsystem */
err = bt_enable(NULL);
if (err) {
printk("Bluetooth init failed (err %d)\n", err);
return;
}
printk("Bluetooth initialized\n");
err = init_gatt();
if (err) {
printk("init gatt failed(err %d)\n", err);
return;
}
err = init_covid();
if (err) {
printk("init covid failed (err %d)\n", err);
return;
}
do{
do_covid();
do_gatt();
} while (1);
}

View File

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