Contacts: Add possibility to retrieve number of infected contacts for a period key

This commit is contained in:
H1ghBre4k3r 2021-07-21 14:05:08 +02:00 committed by Patrick Rathje
parent 0b9378716d
commit 4399810691
11 changed files with 318 additions and 79 deletions

17
src/bloom.c Normal file
View File

@ -0,0 +1,17 @@
#include "bloom.h"
#include "ens/storage.h"
int bloom_init() {
// TODO lome: implement
return 0;
}
int bloom_add_record(record_t* record) {
// TODO lome: implement
return 0;
}
int bloom_probably_has_record(record_t* record) {
// TODO lome: implement
return 0;
}

17
src/bloom.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef BLOOM_H
#define BLOOM_H
#include "ens/storage.h"
/**
* Initialize the bloom filter on basis of the already registerred records.
*/
int bloom_init();
// TODO lome: maybe only use RPI (should be sufficient)
int bloom_add_record(record_t* record);
// TODO lome: maybe only use RPI (should be sufficient)
int bloom_probably_has_record(record_t* record);
#endif

View File

@ -4,40 +4,183 @@
* 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 <timing/timing.h>
#include <zephyr.h>
#include <zephyr/types.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 "bloom.h"
#include "contacts.h"
#include "exposure-notification.h"
#include "covid.h"
#include "covid_types.h"
#include "ens/records.h"
#include "ens/storage.h"
#include "exposure-notification.h"
void print_key(_ENBaseKey* key){
for( int i = 0; i < sizeof(key->b); i++){
void print_key(_ENBaseKey* key) {
for (int i = 0; i < sizeof(key->b); i++) {
printk("%02x", key->b[i]);
}
}
void print_rpi(rolling_proximity_identifier_t* rpi){
for( int i = 0; i < sizeof(rolling_proximity_identifier_t); i++){
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++){
void print_aem(associated_encrypted_metadata_t* aem) {
for (int i = 0; i < sizeof(associated_encrypted_metadata_t); i++) {
printk("%02x", aem->data[i]);
}
}
int register_record(record_t* record) {
int rc = add_record(record);
if (rc) {
return rc;
}
rc = bloom_add_record(record);
return rc;
}
int get_number_of_infected_for_period(ENPeriodKey* key, time_t timestamp) {
ENPeriodIdentifierKey periodIdentifier;
en_derive_period_identifier_key(&periodIdentifier, key);
ENIntervalNumber intervalStart = en_get_interval_number_at_period_start(timestamp);
// derive all interval identifiers
ENIntervalIdentifier intervals[EN_TEK_ROLLING_PERIOD];
for (int i = 0; i < EN_TEK_ROLLING_PERIOD; i++) {
en_derive_interval_identifier(&intervals[i], &periodIdentifier, intervalStart + i);
}
time_t end_ts = timestamp + 3600 * 24;
record_iterator_t iterator;
int rc = ens_records_iterator_init_timerange(&iterator, &timestamp, &end_ts);
if (rc) {
return rc;
}
printk("init iterator between %d and %d\n", iterator.current.sn, iterator.sn_end);
uint64_t infected = 0;
while (!iterator.finished) {
// printk("new contact\n");
for (int i = 0; i < EN_TEK_ROLLING_PERIOD; i++) {
// printk("new period");
if (memcmp(&iterator.current.rolling_proximity_identifier, &intervals[i],
sizeof(iterator.current.rolling_proximity_identifier)) == 0) {
// we found a match
infected++;
break;
}
}
ens_records_iterator_next(&iterator);
}
return infected;
}
void get_number_of_infected_for_multiple_periods(infected_for_period_key_ctx_t* ctx, time_t timestamp, int count) {
// for (int i = 0; i < count; i++) {
// ctx[i].infected = get_contacts_for_period_key(ctx[i].key, timestamp);
// }
ENIntervalIdentifier intervals[count][EN_TEK_ROLLING_PERIOD];
for (int i = 0; i < count; i++) {
ENIntervalNumber intervalStart = en_get_interval_number_at_period_start(timestamp);
for (int j = 0; j < EN_TEK_ROLLING_PERIOD; j++) {
en_derive_interval_identifier(&intervals[i][j], &ctx[i].key, intervalStart + j);
}
}
// init iterator
time_t end_ts = timestamp + 3600 * 24;
record_iterator_t iterator;
int rc = ens_records_iterator_init_timerange(&iterator, &timestamp, &end_ts);
if (rc) {
return rc;
}
printk("init iterator between %d and %d\n", iterator.current.sn, iterator.sn_end);
while (!iterator.finished) {
for (int i = 0; i < count; i++) {
for (int j = 0; j < EN_TEK_ROLLING_PERIOD; j++) {
if (memcmp(&iterator.current.rolling_proximity_identifier, &intervals[i][j],
sizeof(iterator.current.rolling_proximity_identifier)) == 0) {
ctx[i].infected++;
break;
}
}
}
ens_records_iterator_next(&iterator);
}
}
void setup_test_data() {
ENPeriodKey infectedPeriodKey = {
.b = {0x75, 0xc7, 0x34, 0xc6, 0xdd, 0x1a, 0x78, 0x2d, 0xe7, 0xa9, 0x65, 0xda, 0x5e, 0xb9, 0x31, 0x25}};
ENPeriodKey dummyPeriodKey = {
.b = {0x89, 0xa7, 0x34, 0xc6, 0xdd, 0x1a, 0x14, 0xda, 0xe7, 0x00, 0x65, 0xda, 0x6a, 0x9b, 0x13, 0x52}};
ENPeriodIdentifierKey infectedPik;
ENPeriodIdentifierKey dummyPik;
en_derive_period_identifier_key(&infectedPik, &infectedPeriodKey);
en_derive_period_identifier_key(&dummyPik, &dummyPeriodKey);
for (int i = 0; i < EN_TEK_ROLLING_PERIOD; i++) {
// create infected record which
record_t infectedRecord;
infectedRecord.timestamp = i * EN_INTERVAL_LENGTH;
en_derive_interval_identifier(&infectedRecord.rolling_proximity_identifier, &infectedPik, i);
record_t dummyRecord;
en_derive_interval_identifier(&dummyRecord.rolling_proximity_identifier, &dummyPik, i);
int rc;
if (i % 3 == 0) {
if (rc = add_record(&infectedRecord)) {
printk("err %d\n", rc);
}
}
int spread = 4;
for (int j = 0; j < EN_INTERVAL_LENGTH / spread; j++) {
dummyRecord.timestamp = i * EN_INTERVAL_LENGTH + j * spread + 1;
if (rc = add_record(&dummyRecord)) {
printk("err %d\n", rc);
}
}
printk("period %d\n", i);
}
printk("starting measurement\n");
timing_t start_time, end_time;
uint64_t total_cycles;
uint64_t total_ns;
timing_init();
timing_start();
start_time = timing_counter_get();
printk("found %d infected\n", get_number_of_infected_for_period(&infectedPeriodKey, 200));
end_time = timing_counter_get();
total_cycles = timing_cycles_get(&start_time, &end_time);
total_ns = timing_cycles_to_ns(total_cycles);
timing_stop();
printk("timing took %lld ns\n", total_ns);
}

View File

@ -12,10 +12,48 @@
#include "covid.h"
#include "covid_types.h"
#include "ens/storage.h"
#include "exposure-notification.h"
typedef struct infected_for_period_key_ctx {
ENPeriodKey* key;
int infected;
} infected_for_period_key_ctx_t;
void print_key(_ENBaseKey* key);
void print_rpi(rolling_proximity_identifier_t* rpi);
void print_aem(associated_encrypted_metadata_t* aem);
/**
* Register a new record in the system. This includes adding it to the storage and adding it to the bloom filter.
*
* @param record record to add
* @returns 0 in case of success, -ERRNO in case of an error
*/
int register_record(record_t* record);
/**
* Get the number of infected records for a given PeriodKey.
*
* @param key the PeriodKey
* @param timestamp the timestamp of the period
*
* @returns the number of infected records or -ERRNO in case of an error
*/
int get_number_of_infected_for_period(ENPeriodKey* key, time_t timestamp);
/**
* Get the number of infected records for multiple PeriodKeys.
*
* @param ctx array of period keys and timestamps with field "infected" for the number of infected records
* @param timestamp the timestamp of the period
* @param count the number of periods in the array
*/
void get_number_of_infected_for_multiple_periods(infected_for_period_key_ctx_t* ctx, time_t timestamp, int count);
/**
* Setup fixed test data for storage.
*/
void setup_test_data();
#endif

View File

@ -12,6 +12,7 @@
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <kernel.h>
#include "exposure-notification.h"
#include "covid_types.h"
@ -49,6 +50,8 @@ static struct bt_data ad[] = {
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 struct k_mutex key_change_lock;
static void scan_cb(const bt_addr_le_t *addr, int8_t rssi, uint8_t adv_type, struct net_buf_simple *buf)
{
if (adv_type == 3)
@ -82,7 +85,7 @@ static void scan_cb(const bt_addr_le_t *addr, int8_t rssi, uint8_t adv_type, str
memcpy(&contact.associated_encrypted_metadata, &rx_adv->associated_encrypted_metadata, sizeof(contact.associated_encrypted_metadata));
memcpy(&contact.rolling_proximity_identifier, &rx_adv->rolling_proximity_identifier, sizeof(contact.rolling_proximity_identifier));
memcpy(&contact.timestamp, &timestamp, sizeof(contact.timestamp));
int rc = add_record(&contact);
int rc = register_record(&contact);
printk("Contact stored (err %d)\n", rc);
}
}
@ -317,11 +320,12 @@ static void check_keys(struct k_work *work)
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
// lock, so we can be sure to only advertise correct packages
k_mutex_lock(&key_change_lock, K_FOREVER);
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));
k_mutex_unlock(&key_change_lock);
init = 0;
}
@ -371,6 +375,8 @@ int init_covid()
return err;
}
k_mutex_init(&key_change_lock);
k_timer_start(&my_timer, KEY_CHECK_INTERVAL, KEY_CHECK_INTERVAL);
return 0;
}
@ -381,6 +387,8 @@ int do_covid()
int err = 0;
#if CONFIG_BT
k_mutex_lock(&key_change_lock, K_FOREVER);
err = bt_le_adv_start(BT_LE_ADV_NCONN, ad, ARRAY_SIZE(ad), NULL, 0);
#endif
@ -395,6 +403,7 @@ int do_covid()
#if CONFIG_BT
err = bt_le_adv_stop();
#endif
k_mutex_unlock(&key_change_lock);
if (err)
{

View File

@ -1,5 +1,6 @@
#include <string.h>
#include <sys/types.h>
#include "../covid_types.h"
#include "ens_error.h"
#include "records.h"
@ -9,7 +10,6 @@
int ens_records_iterator_init_range(record_iterator_t* iterator,
record_sequence_number_t* opt_start,
record_sequence_number_t* opt_end) {
// prevent any changes during initialization
int rc = get_sequence_number_interval(&iterator->sn_next, &iterator->sn_end);
if (rc == 0) {
@ -29,22 +29,18 @@ int ens_records_iterator_init_range(record_iterator_t* iterator,
return 0;
}
int64_t get_timestamp_for_sn(record_sequence_number_t sn) {
record_t rec;
if(load_record(&rec, sn) == 0) {
if (load_record(&rec, sn) == 0) {
return rec.timestamp;
} else {
return -1;
}
}
enum record_timestamp_search_mode {
RECORD_TIMESTAMP_SEARCH_MODE_MIN,
RECORD_TIMESTAMP_SEARCH_MODE_MAX,
RECORD_TIMESTAMP_SEARCH_MODE_MAX,
};
/**
@ -54,8 +50,9 @@ enum record_timestamp_search_mode {
* @param target timestamp for which to find the nearest entry for
* @param greater flag for indicating, if the loaded sn shall correspond to a greater (1) or smaller (0) timestamp
*/
int find_sn_via_binary_search(record_sequence_number_t* sn_dest, uint32_t target, enum record_timestamp_search_mode search_mode) {
int find_sn_via_binary_search(record_sequence_number_t* sn_dest,
uint32_t target,
enum record_timestamp_search_mode search_mode) {
record_sequence_number_t start_sn;
record_sequence_number_t end_sn;
@ -67,12 +64,13 @@ int find_sn_via_binary_search(record_sequence_number_t* sn_dest, uint32_t target
return rc;
}
record_sequence_number_t last_sn = start_sn; // used to check if ran into issues, e.g. could not load the entry or rounding errors
record_sequence_number_t last_sn =
start_sn; // used to check if ran into issues, e.g. could not load the entry or rounding errors
while(!sn_equal(start_sn, end_sn)) {
while (!sn_equal(start_sn, end_sn)) {
// calculate the sn in the middle between start and end
record_sequence_number_t cur_sn = sn_get_middle_sn(start_sn, end_sn);
if (sn_equal(cur_sn, last_sn)) {
// if we already checked this entry -> we reduce our boundaries and try again
// this also solves issues with rounding
@ -81,24 +79,23 @@ int find_sn_via_binary_search(record_sequence_number_t* sn_dest, uint32_t target
int64_t start_ts = get_timestamp_for_sn(start_sn);
if (start_ts == -1 || start_ts < target) {
// we could not load this entry or this entry is strictly smaller than our target
start_sn = sn_increment(start_sn); // we can safely increment as start_sn < end_sn
start_sn = sn_increment(start_sn); // we can safely increment as start_sn < end_sn
} else {
// we actually found the wanted entry!
end_sn = start_sn; // this will break our loop
end_sn = start_sn; // this will break our loop
}
} else {
// we search for the biggest value among them
int64_t end_ts = get_timestamp_for_sn(end_sn);
if (end_ts == -1 || end_ts > target) {
// we could not load this entry or this entry is strictly bigger than our target
end_sn = sn_decrement(end_sn); // we can safely decrement as start_sn < end_sn
end_sn = sn_decrement(end_sn); // we can safely decrement as start_sn < end_sn
} else {
// we actually found the wanted entry!
start_sn = end_sn; // this will break our loop
start_sn = end_sn; // this will break our loop
}
}
} else {
int64_t mid_ts = get_timestamp_for_sn(cur_sn);
if (mid_ts >= 0) {
@ -118,19 +115,19 @@ int find_sn_via_binary_search(record_sequence_number_t* sn_dest, uint32_t target
}
} else {
// some errors -> we keep the current sn and try to narrow our boundaries
}
}
}
last_sn = cur_sn;
}
*sn_dest = start_sn; // == end_sn
*sn_dest = start_sn; // == end_sn
return 0;
}
// TODO: This iterator does neither check if the sequence numbers wrapped around while iteration. As a result, first
// results could have later timestamps than following entries
int ens_records_iterator_init_timerange(record_iterator_t* iterator, uint32_t* ts_start, uint32_t* ts_end) {
int ens_records_iterator_init_timerange(record_iterator_t* iterator, time_t* ts_start, time_t* ts_end) {
record_sequence_number_t oldest_sn = 0;
record_sequence_number_t newest_sn = 0;

View File

@ -26,7 +26,7 @@ int ens_records_iterator_init_range(record_iterator_t* iterator,
// TODO: Do we guarantee that higher sequence numbers have at least our timestamp and lower sequence numbers up to our
// timestamp?
int ens_records_iterator_init_timerange(record_iterator_t* iterator, uint32_t* ts_start, uint32_t* ts_end);
int ens_records_iterator_init_timerange(record_iterator_t* iterator, time_t* ts_start, time_t* ts_end);
record_t* ens_records_iterator_next(record_iterator_t* iter);

View File

@ -61,7 +61,7 @@ int save_storage_information() {
return rc;
}
int init_record_storage(void) {
int init_record_storage(bool clean) {
int rc = 0;
struct flash_pages_info info;
// define the nvs file system
@ -86,10 +86,18 @@ int init_record_storage(void) {
}
// Load the current storage information
rc = load_storage_information();
if (rc) {
printk("Cannot load storage information (err %d)\n", rc);
return rc;
if (clean) {
rc = save_storage_information();
if (rc < 0) {
printk("Clean init of storage failed (err %d)\n", rc);
return rc;
}
} else {
rc = load_storage_information();
if (rc < 0) {
printk("Cannot load storage information (err %d)\n", rc);
return rc;
}
}
printk("Currently %d contacts stored!\n", record_information.count);
@ -125,15 +133,16 @@ int add_record(record_t* src) {
* state of our information about the stored contacts in combination with correct state of our flash.
*/
record_t rec;
memcpy(&rec, src, sizeof(rec));
k_mutex_lock(&info_fs_lock, K_FOREVER);
// Check, if next sn would be at start of page
record_sequence_number_t potential_next_sn =
rec.sn =
get_latest_sequence_number() == get_oldest_sequence_number() ? 0 : sn_increment(get_latest_sequence_number());
storage_id_t potential_next_id = convert_sn_to_storage_id(potential_next_sn);
storage_id_t potential_next_id = convert_sn_to_storage_id(rec.sn);
// write our entry to flash and check, if the current entry is already in use
int rc = ens_fs_write(&ens_fs, potential_next_id, src);
int rc = ens_fs_write(&ens_fs, potential_next_id, &rec);
// if our error does NOT indicate, that this address is already in use, we just goto end and do nothing
if (rc && rc != -ENS_ADDRINU) {
// TODO: maybe also increment, if there is an internal error?
@ -151,7 +160,7 @@ int add_record(record_t* src) {
record_information.oldest_contact = sn_increment_by(record_information.oldest_contact, deletedRecordsCount);
}
// after creating some space, try to write again
rc = ens_fs_write(&ens_fs, potential_next_id, src);
rc = ens_fs_write(&ens_fs, potential_next_id, &rec);
if (rc) {
goto inc;
}

View File

@ -23,9 +23,11 @@ typedef struct stored_records_information {
/**
* Initializes the contact storage component
* @param clean flag for indicating, if storage shall be init with clean state
*
* @return 0 for success
*/
int init_record_storage();
int init_record_storage(bool clean);
/**
* Loads the record with number sn into the destination struct

View File

@ -10,16 +10,16 @@
#include <random/rand32.h>
#include <sys/printk.h>
#include "bloom.h"
#include "contacts.h"
#include "covid.h"
#include "covid_types.h"
#include "display.h"
#include "ens/storage.h"
#include "exposure-notification.h"
#include "extract_keys.h"
#include "gatt_service.h"
#include "io.h"
#include "display.h"
void main(void) {
#if CONFIG_TEST_UNPACK_KEYS
@ -31,23 +31,30 @@ void main(void) {
int err = 0;
printk("Starting Covid Contact Tracer\n");
// first init everything
#ifndef NATIVE_POSIX
// first init everything
#ifndef NATIVE_POSIX
// 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;
}
#endif
#endif
#if CONFIG_FLASH
err = init_record_storage();
#if CONFIG_FLASH
err = init_record_storage(true);
if (err) {
printk("init storage failed (err %d)\n", err);
return;
}
#endif
// setup_test_data();
#endif
err = bloom_init();
if (err) {
printk("init bloom failed (err %d)\n", err);
return;
}
err = init_io();
if (err) {
@ -55,22 +62,22 @@ void main(void) {
return;
}
#if CONFIG_BT
/* Initialize the Bluetooth Subsystem */
err = bt_enable(NULL);
if (err) {
printk("Bluetooth init failed (err %d)\n", err);
return;
}
#if CONFIG_BT
/* 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;
}
#endif
err = init_gatt();
if (err) {
printk("init gatt failed(err %d)\n", err);
return;
}
#endif
err = init_covid();
if (err) {
@ -78,16 +85,14 @@ void main(void) {
return;
}
printk("init display\n");
err = init_display();
if (err) {
printk("init display failed (err %d)\n", err);
}
printk("init display\n");
err = init_display();
if (err) {
printk("init display failed (err %d)\n", err);
}
do{
do_covid();
do_gatt();
} while (1);
do {
do_covid();
do_gatt();
} while (1);
}

View File

@ -40,6 +40,8 @@ CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096
# max contacts, that can be stored
CONFIG_ENS_MAX_CONTACTS=65536
CONFIG_TIMING_FUNCTIONS=y
CONFIG_LOG=y
# # Run protobuf unpack tests at startup