From fc596470b93c1adac55c191bbd0766ca6145dc6a Mon Sep 17 00:00:00 2001 From: H1ghBre4k3r Date: Wed, 21 Jul 2021 14:05:08 +0200 Subject: [PATCH] Contacts: Add possibility to retrieve number of infected contacts for a period key --- src/bloom.c | 17 +++++ src/bloom.h | 17 +++++ src/contacts.c | 165 ++++++++++++++++++++++++++++++++++++++++++---- src/contacts.h | 38 +++++++++++ src/covid.c | 17 +++-- src/ens/records.c | 37 +++++------ src/ens/records.h | 2 +- src/ens/storage.c | 29 +++++--- src/ens/storage.h | 4 +- src/main.c | 69 ++++++++++--------- zephyr/prj.conf | 2 + 11 files changed, 318 insertions(+), 79 deletions(-) create mode 100644 src/bloom.c create mode 100644 src/bloom.h diff --git a/src/bloom.c b/src/bloom.c new file mode 100644 index 0000000..e7e147c --- /dev/null +++ b/src/bloom.c @@ -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; +} \ No newline at end of file diff --git a/src/bloom.h b/src/bloom.h new file mode 100644 index 0000000..3a5828e --- /dev/null +++ b/src/bloom.h @@ -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 \ No newline at end of file diff --git a/src/contacts.c b/src/contacts.c index 21e8902..9e5e336 100644 --- a/src/contacts.c +++ b/src/contacts.c @@ -4,40 +4,183 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include -#include #include #include #include #include +#include +#include +#include #include #include -#include #include #include #include -#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, ×tamp, &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, ×tamp, &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); +} diff --git a/src/contacts.h b/src/contacts.h index c39ed4a..0e0f556 100644 --- a/src/contacts.h +++ b/src/contacts.h @@ -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 \ No newline at end of file diff --git a/src/covid.c b/src/covid.c index a28e82d..d194076 100644 --- a/src/covid.c +++ b/src/covid.c @@ -12,6 +12,7 @@ #include #include +#include #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, ×tamp, 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) { diff --git a/src/ens/records.c b/src/ens/records.c index a3aa92b..4f9ddca 100644 --- a/src/ens/records.c +++ b/src/ens/records.c @@ -1,5 +1,6 @@ #include +#include #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; diff --git a/src/ens/records.h b/src/ens/records.h index d5f1392..a46a895 100644 --- a/src/ens/records.h +++ b/src/ens/records.h @@ -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); diff --git a/src/ens/storage.c b/src/ens/storage.c index 689efee..7a2a0c2 100644 --- a/src/ens/storage.c +++ b/src/ens/storage.c @@ -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; } diff --git a/src/ens/storage.h b/src/ens/storage.h index 97bad24..fadaf03 100644 --- a/src/ens/storage.h +++ b/src/ens/storage.h @@ -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 diff --git a/src/main.c b/src/main.c index 30679cd..1f3250e 100644 --- a/src/main.c +++ b/src/main.c @@ -10,16 +10,16 @@ #include #include +#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); } diff --git a/zephyr/prj.conf b/zephyr/prj.conf index c03ddb2..e92373e 100644 --- a/zephyr/prj.conf +++ b/zephyr/prj.conf @@ -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