1
0
mirror of https://github.com/CovidBraceletPrj/CovidBracelet.git synced 2024-07-25 11:42:18 +02:00

Refactor and Clean Project for essential functionality

This commit is contained in:
Patrick Rathje 2022-05-27 21:52:29 +02:00
parent ac320271a0
commit ee4612104d
39 changed files with 579 additions and 2959 deletions

3
.gitignore vendored
View File

@ -46,4 +46,5 @@ massif-*
.pio
.vscode
.DS_Store
.DS_Store
/.idea

View File

@ -1,25 +0,0 @@
#ifndef BLOOM_H
#define BLOOM_H
#include "ens/storage.h"
#include "exposure-notification.h"
typedef struct {
uint8_t* data;
size_t size;
} bloom_filter_t;
/**
* Initialize the bloom filter on basis of the already registerred records.
*/
bloom_filter_t* bloom_init(size_t size);
void bloom_destroy(bloom_filter_t* bloom);
// TODO lome: maybe only use RPI (should be sufficient)
void bloom_add_record(bloom_filter_t* bloom, ENIntervalIdentifier* rpi);
// TODO lome: maybe only use RPI (should be sufficient)
bool bloom_probably_has_record(bloom_filter_t* bloom, ENIntervalIdentifier* rpi);
#endif

View File

@ -1,66 +0,0 @@
/*
* 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.h"
#include "covid_types.h"
#include "ens/storage.h"
#include "exposure-notification.h"
typedef struct {
ENIntervalIdentifier interval_identifier;
int met;
time_t search_start;
time_t search_end;
} __packed infected_for_interval_ident_ctx_t;
typedef struct {
ENPeriodKey periodKey;
time_t start;
int met;
} __packed period_key_information_t;
typedef int (*test_func_t)(infected_for_interval_ident_ctx_t* infectedIntervals, int count);
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);
/**
* Initialize the contacts module.
*/
int init_contacts();
/**
* Check for a list of specified interval identifiers, whether they were probably met or not.
* @param ctx list of interval identifiers to check
* @param count amount of identifiers to check
* @return the amount of met intervals, -ERRNO on error
*/
int check_possible_contacts_for_intervals(infected_for_interval_ident_ctx_t* ctx, int count);
/**
* Check for a list of specified period keys, whether they were probably met or not.
* @param ctx list of period keys and their meta information to check
* @param count amount of period keys to check
* @return -ERRNO on error, 0 otherwise
*/
int check_possible_contacts_for_periods(period_key_information_t periodKeyInformation[], int count);
#endif

View File

@ -1,31 +0,0 @@
/*
* Copyright (c) 2020 Olaf Landsiedel
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef COVID_H
#define COVID_H
#include <zephyr.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

View File

@ -1,33 +0,0 @@
/*
* Copyright (c) 2020 Olaf Landsiedel
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef COVID_TYPES_H
#define COVID_TYPES_H
#include <exposure-notification.h>
#include <zephyr.h>
#include <zephyr/types.h>
#define COVID_ROLLING_PROXIMITY_IDENTIFIER_LEN 16
typedef ENIntervalIdentifier 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;
typedef uint8_t rssi_t;
#endif

View File

@ -1,73 +0,0 @@
#ifndef DISPLAY_H
#define DISPLAY_H
/**
* @brief Initializes all display elements and adds them to the canvas.
*
* @return int
*/
int init_display();
/**
* @brief Updates all information on the display using the get functions, defined by `display.c`. Alternatively the
* values of the displayed elements can be set explicitly via set `display_set_<property>`-functions.
*
* @return int
*/
int update_display();
/**
* @brief This is the entry point for the display thread. It peridocially updates the display and handles input events.
*
*/
void display_thread(void*, void*, void*);
/**
* @brief Displays a message at the bottom of the display. The message will persist until it is removed expicitly, by calling `display_set_message("")`.
*
* @param msg A pointer to the string that should be displayed
* @return int
*/
int display_set_message(char* msg);
/**
* @brief Sets the on display clock. The time should have the format `h * 100 + m`. So `display_set_time(1145)` sets the clock to 11:45.
*
* @param time A number representation of the time
* @return int
*/
int display_set_time(int time);
/**
* @brief Set the battery level percentage.
*
* @param bat The battery level in percent
* @return int
*/
int display_set_bat(int bat);
/**
* @brief Set the non-volatile memory state.
*
* @param mem The occupied memory in percent
* @return int
*/
int display_set_mem(int mem);
/**
* @brief Set the number of total registered contacts in the last 14 days.
*
* @param contacts The number of contacts
* @return int
*/
int display_set_contacts(int contacts);
/**
* @brief Set the number of contacts that were tested positively on Covid and registered in the last 14 days.
*
* @param risk_contacts The number of risk contacts
* @return int
*/
int display_set_risk_contacts(int risk_contacts);
#endif

View File

@ -1,10 +0,0 @@
#ifndef ENS_ERROR_H
#define ENS_ERROR_H
#define ENS_INTERR 1 // internal error
#define ENS_NOENT 2 // entry not found or invalid
#define ENS_DELENT 3 // entry got deleted
#define ENS_ADDRINU 4 // address alread in use or corrupt
#define ENS_INVARG 5 // invalid argument
#endif

View File

@ -1,61 +0,0 @@
/*
* Copyright (c) 2021 Louis Meyer and Patrick Rathje
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ENS_RECORDS_H
#define ENS_RECORDS_H
#include "sequencenumber.h"
#include "storage.h"
typedef struct record_iterator {
/**
* @internal
*/
record_t current;
/**
* @internal
*/
record_sequence_number_t sn_next;
/**
* @internal
*/
record_sequence_number_t sn_end; // the last sn to include
/**
* @internal
*/
uint8_t finished;
} record_iterator_t;
// Also uses start and end from storage if NULL pointers given
// iterate over a sequence number range of records (Null will select the latest and newest for start / end)
// automatically
int ens_records_iterator_init_range(record_iterator_t* iterator,
record_sequence_number_t* opt_start,
record_sequence_number_t* opt_end);
// 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, time_t* ts_start, time_t* ts_end);
record_t* ens_records_iterator_next(record_iterator_t* iter);
int ens_record_iterator_clear(record_iterator_t* iter);
// TODO: Is a callback the easiest thing to do? -> now it is the iterator :)
// TODO: should we really indicated the last record by sending a NULL pointer?
typedef uint8_t (*ens_record_iterator_cb_t)(const record_t* record, void* userdata);
enum {
ENS_RECORD_ITER_STOP, // STOP the iteration, this would not result in a NULL callback after the end is reached
ENS_RECORD_ITER_CONTINUE, // CONTINUE the iteration, this results in a NULL callback after the end is reached
};
// TODO: this function could be made asynchronous to handle delays in contact_storage reads?!
// TODO: How can we handle iteration while records are being added or deleted? (should be safe as long as the
// load_record function is thread safe?!)
uint8_t ens_records_iterate_with_callback(record_iterator_t* iter, ens_record_iterator_cb_t cb, void* userdata);
#endif

View File

@ -1,80 +0,0 @@
#ifndef CONTACT_STORAGE_H
#define CONTACT_STORAGE_H
#include <zephyr/types.h>
#include "covid_types.h"
#include "sequencenumber.h"
typedef uint64_t storage_id_t;
typedef struct record {
record_sequence_number_t sn; // TODO: Convert Sequence Number
uint32_t timestamp; // TODO: Seconds from january first 2000 (UTC+0)
rssi_t rssi; // TODO: Check correct
rolling_proximity_identifier_t rolling_proximity_identifier;
associated_encrypted_metadata_t associated_encrypted_metadata;
} __packed record_t;
typedef struct stored_records_information {
record_sequence_number_t oldest_contact;
uint32_t count;
} stored_records_information_t;
/**
* 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(bool clean);
/**
* Reset state of record storage.
*/
void reset_record_storage();
/**
* Loads the record with number sn into the destination struct
* @param dest
* @param sn
* @return 0 in case of success
*/
int load_record(record_t* dest, record_sequence_number_t sn);
/**
* Stores the record from src with number sn, increases latest sequence number
* @param sn
* @param src
* @return 0 in case of success
*/
int add_record(record_t* src);
/**
* Deletes the record from storage with number sn
* @param sn the sequence number to delete
* @return 0 in case of success
*/
int delete_record(record_sequence_number_t sn);
/**
* TODO: How to handle if none is available?
* @return The latest available sequence number (Caution: can actually be lower than the oldes in case of a
* wrap-around!)
*/
record_sequence_number_t get_latest_sequence_number();
/**
* TODO: How to handle if none is available?
* @return The oldest available sequence number
*/
record_sequence_number_t get_oldest_sequence_number();
/**
* @return The amount of contacts, usually get_latest_sequence_number() - get_oldest_sequence_number()
*/
uint32_t get_num_records();
int get_sequence_number_interval(record_sequence_number_t* oldest, record_sequence_number_t* latest);
#endif

View File

@ -1,355 +0,0 @@
/* Generated by the protocol buffer compiler. DO NOT EDIT! */
/* Generated from: export.proto */
#ifndef PROTOBUF_C_export_2eproto__INCLUDED
#define PROTOBUF_C_export_2eproto__INCLUDED
#include <protobuf-c.h>
PROTOBUF_C__BEGIN_DECLS
#if PROTOBUF_C_VERSION_NUMBER < 1000000
# error This file was generated by a newer version of protoc-c which is incompatible with your libprotobuf-c headers. Please update your headers.
#elif 1004000 < PROTOBUF_C_MIN_COMPILER_VERSION
# error This file was generated by an older version of protoc-c which is incompatible with your libprotobuf-c headers. Please regenerate this file with a newer version of protoc-c.
#endif
typedef struct TemporaryExposureKeyExport TemporaryExposureKeyExport;
typedef struct SignatureInfo SignatureInfo;
typedef struct TemporaryExposureKey TemporaryExposureKey;
typedef struct TEKSignatureList TEKSignatureList;
typedef struct TEKSignature TEKSignature;
/* --- enums --- */
/*
* Data type representing why this key was published.
*/
typedef enum _TemporaryExposureKey__ReportType {
/*
* Never returned by the client API.
*/
TEMPORARY_EXPOSURE_KEY__REPORT_TYPE__UNKNOWN = 0,
TEMPORARY_EXPOSURE_KEY__REPORT_TYPE__CONFIRMED_TEST = 1,
TEMPORARY_EXPOSURE_KEY__REPORT_TYPE__CONFIRMED_CLINICAL_DIAGNOSIS = 2,
TEMPORARY_EXPOSURE_KEY__REPORT_TYPE__SELF_REPORT = 3,
TEMPORARY_EXPOSURE_KEY__REPORT_TYPE__RECURSIVE = 4,
/*
* Used to revoke a key, never returned by client API.
*/
TEMPORARY_EXPOSURE_KEY__REPORT_TYPE__REVOKED = 5
PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(TEMPORARY_EXPOSURE_KEY__REPORT_TYPE)
} TemporaryExposureKey__ReportType;
/* --- messages --- */
/*
* Protobuf definition for exports of confirmed temporary exposure keys.
* The full file format is documented under "Exposure Key Export File Format
* and Verification" at https://www.google.com/covid19/exposurenotifications/
* These files have a 16-byte, space-padded header before the protobuf data
* starts. They will be contained in a zip archive, alongside a signature
* file verifying the contents.
*/
struct TemporaryExposureKeyExport
{
ProtobufCMessage base;
/*
* Time window of keys in this file based on arrival to server, in UTC
* seconds. start_timestamp, end_timestamp, and batch_num must be unique
* at any given snapshot of the index for a server. If multiple
* files are used for a specific time period, and batch_num/batch_size
* are both 1 (See below), then offsetting the end_timestamp is the
* suggested method for forcing uniqueness.
*/
protobuf_c_boolean has_start_timestamp;
uint64_t start_timestamp;
protobuf_c_boolean has_end_timestamp;
uint64_t end_timestamp;
/*
* Region for which these keys came from (e.g., country)
*/
char *region;
/*
* E.g., Batch 2 of 10. Ordinal, 1-based numbering.
* Note: Not yet supported on iOS. Use values of 1 for both.
*/
protobuf_c_boolean has_batch_num;
int32_t batch_num;
protobuf_c_boolean has_batch_size;
int32_t batch_size;
/*
* Information about signatures
* If there are multiple entries, they must be ordered in descending
* time order by signing key effective time (most recent one first).
* There is a limit of 10 signature infos per export file (mobile OS may
* not check anything after that).
*/
size_t n_signature_infos;
SignatureInfo **signature_infos;
/*
* The TemporaryExposureKeys for initial release of keys.
* Keys should be included in this list for initial release,
* whereas revised or revoked keys should go in revised_keys.
*/
size_t n_keys;
TemporaryExposureKey **keys;
/*
* TemporaryExposureKeys that have changed status.
* Keys should be included in this list if they have changed status
* or have been revoked.
*/
size_t n_revised_keys;
TemporaryExposureKey **revised_keys;
};
#define TEMPORARY_EXPOSURE_KEY_EXPORT__INIT \
{ PROTOBUF_C_MESSAGE_INIT (&temporary_exposure_key_export__descriptor) \
, 0, 0, 0, 0, NULL, 0, 0, 0, 0, 0,NULL, 0,NULL, 0,NULL }
struct SignatureInfo
{
ProtobufCMessage base;
/*
* Key version for rollovers
* Must be in character class [a-zA-Z0-9_]. E.g., 'v1'
*/
char *verification_key_version;
/*
* Alias with which to identify public key to be used for verification
* Must be in character class [a-zA-Z0-9_]
* For cross-compatibility with Apple, use MCC
* (https://en.wikipedia.org/wiki/Mobile_country_code).
*/
char *verification_key_id;
/*
* ASN.1 OID for Algorithm Identifier. Supported algorithms are
* either 1.2.840.10045.4.3.2 or 1.2.840.10045.4.3.4
*/
char *signature_algorithm;
};
#define SIGNATURE_INFO__INIT \
{ PROTOBUF_C_MESSAGE_INIT (&signature_info__descriptor) \
, NULL, NULL, NULL }
struct TemporaryExposureKey
{
ProtobufCMessage base;
/*
* Key of infected user
*/
protobuf_c_boolean has_key_data;
ProtobufCBinaryData key_data;
/*
* Varying risks associated with exposure depending on type of verification
* Ignored by the v1.5 client API when report_type is set.
*/
protobuf_c_boolean has_transmission_risk_level PROTOBUF_C__DEPRECATED;
int32_t transmission_risk_level PROTOBUF_C__DEPRECATED;
/*
* The interval number since epoch for which a key starts
*/
protobuf_c_boolean has_rolling_start_interval_number;
int32_t rolling_start_interval_number;
/*
* Increments of 10 minutes describing how long a key is valid
*/
/*
* defaults to 24 hours
*/
protobuf_c_boolean has_rolling_period;
int32_t rolling_period;
/*
* Type of diagnosis associated with a key.
*/
protobuf_c_boolean has_report_type;
TemporaryExposureKey__ReportType report_type;
/*
* Number of days elapsed between symptom onset and the TEK being used.
* E.g. 2 means TEK is 2 days after onset of symptoms.
*/
protobuf_c_boolean has_days_since_onset_of_symptoms;
int32_t days_since_onset_of_symptoms;
};
#define TEMPORARY_EXPOSURE_KEY__INIT \
{ PROTOBUF_C_MESSAGE_INIT (&temporary_exposure_key__descriptor) \
, 0, {0,NULL}, 0, 0, 0, 0, 0, 144, 0, TEMPORARY_EXPOSURE_KEY__REPORT_TYPE__UNKNOWN, 0, 0 }
struct TEKSignatureList
{
ProtobufCMessage base;
/*
* When there are multiple signatures, they must be sorted in time order
* by first effective date for the signing key in descending order.
* The most recent effective signing key must appear first.
* There is a limit of 10 signature infos per export file (mobile OS may
* not check anything after that).
*/
size_t n_signatures;
TEKSignature **signatures;
};
#define TEKSIGNATURE_LIST__INIT \
{ PROTOBUF_C_MESSAGE_INIT (&teksignature_list__descriptor) \
, 0,NULL }
struct TEKSignature
{
ProtobufCMessage base;
/*
* Info about the signing key, version, algorithm, etc.
*/
SignatureInfo *signature_info;
/*
* E.g., Batch 2 of 10
* Must match fields from TemporaryExposureKeyExport, see
* documentation on that message.
*/
protobuf_c_boolean has_batch_num;
int32_t batch_num;
protobuf_c_boolean has_batch_size;
int32_t batch_size;
/*
* Signature in X9.62 format (ASN.1 SEQUENCE of two INTEGER fields)
*/
protobuf_c_boolean has_signature;
ProtobufCBinaryData signature;
};
#define TEKSIGNATURE__INIT \
{ PROTOBUF_C_MESSAGE_INIT (&teksignature__descriptor) \
, NULL, 0, 0, 0, 0, 0, {0,NULL} }
/* TemporaryExposureKeyExport methods */
void temporary_exposure_key_export__init
(TemporaryExposureKeyExport *message);
size_t temporary_exposure_key_export__get_packed_size
(const TemporaryExposureKeyExport *message);
size_t temporary_exposure_key_export__pack
(const TemporaryExposureKeyExport *message,
uint8_t *out);
size_t temporary_exposure_key_export__pack_to_buffer
(const TemporaryExposureKeyExport *message,
ProtobufCBuffer *buffer);
TemporaryExposureKeyExport *
temporary_exposure_key_export__unpack
(ProtobufCAllocator *allocator,
size_t len,
const uint8_t *data);
void temporary_exposure_key_export__free_unpacked
(TemporaryExposureKeyExport *message,
ProtobufCAllocator *allocator);
/* SignatureInfo methods */
void signature_info__init
(SignatureInfo *message);
size_t signature_info__get_packed_size
(const SignatureInfo *message);
size_t signature_info__pack
(const SignatureInfo *message,
uint8_t *out);
size_t signature_info__pack_to_buffer
(const SignatureInfo *message,
ProtobufCBuffer *buffer);
SignatureInfo *
signature_info__unpack
(ProtobufCAllocator *allocator,
size_t len,
const uint8_t *data);
void signature_info__free_unpacked
(SignatureInfo *message,
ProtobufCAllocator *allocator);
/* TemporaryExposureKey methods */
void temporary_exposure_key__init
(TemporaryExposureKey *message);
size_t temporary_exposure_key__get_packed_size
(const TemporaryExposureKey *message);
size_t temporary_exposure_key__pack
(const TemporaryExposureKey *message,
uint8_t *out);
size_t temporary_exposure_key__pack_to_buffer
(const TemporaryExposureKey *message,
ProtobufCBuffer *buffer);
TemporaryExposureKey *
temporary_exposure_key__unpack
(ProtobufCAllocator *allocator,
size_t len,
const uint8_t *data);
void temporary_exposure_key__free_unpacked
(TemporaryExposureKey *message,
ProtobufCAllocator *allocator);
/* TEKSignatureList methods */
void teksignature_list__init
(TEKSignatureList *message);
size_t teksignature_list__get_packed_size
(const TEKSignatureList *message);
size_t teksignature_list__pack
(const TEKSignatureList *message,
uint8_t *out);
size_t teksignature_list__pack_to_buffer
(const TEKSignatureList *message,
ProtobufCBuffer *buffer);
TEKSignatureList *
teksignature_list__unpack
(ProtobufCAllocator *allocator,
size_t len,
const uint8_t *data);
void teksignature_list__free_unpacked
(TEKSignatureList *message,
ProtobufCAllocator *allocator);
/* TEKSignature methods */
void teksignature__init
(TEKSignature *message);
size_t teksignature__get_packed_size
(const TEKSignature *message);
size_t teksignature__pack
(const TEKSignature *message,
uint8_t *out);
size_t teksignature__pack_to_buffer
(const TEKSignature *message,
ProtobufCBuffer *buffer);
TEKSignature *
teksignature__unpack
(ProtobufCAllocator *allocator,
size_t len,
const uint8_t *data);
void teksignature__free_unpacked
(TEKSignature *message,
ProtobufCAllocator *allocator);
/* --- per-message closures --- */
typedef void (*TemporaryExposureKeyExport_Closure)
(const TemporaryExposureKeyExport *message,
void *closure_data);
typedef void (*SignatureInfo_Closure)
(const SignatureInfo *message,
void *closure_data);
typedef void (*TemporaryExposureKey_Closure)
(const TemporaryExposureKey *message,
void *closure_data);
typedef void (*TEKSignatureList_Closure)
(const TEKSignatureList *message,
void *closure_data);
typedef void (*TEKSignature_Closure)
(const TEKSignature *message,
void *closure_data);
/* --- services --- */
/* --- descriptors --- */
extern const ProtobufCMessageDescriptor temporary_exposure_key_export__descriptor;
extern const ProtobufCMessageDescriptor signature_info__descriptor;
extern const ProtobufCMessageDescriptor temporary_exposure_key__descriptor;
extern const ProtobufCEnumDescriptor temporary_exposure_key__report_type__descriptor;
extern const ProtobufCMessageDescriptor teksignature_list__descriptor;
extern const ProtobufCMessageDescriptor teksignature__descriptor;
PROTOBUF_C__END_DECLS
#endif /* PROTOBUF_C_export_2eproto__INCLUDED */

View File

@ -1,41 +0,0 @@
#ifndef EXTRACT_KEYS_H
#define EXTRACT_KEYS_H
#include "export.pb-c.h"
/**
* @brief Process a key. This function could trigger the comparision between the key and those registered by the ENS.
*
* @param key A pointer to the Exposure key data structure
*/
void process_key(TemporaryExposureKey* key);
/**
* @brief Generates a protocol buffer containing dummy keys.
*
* @param buffer_pointer A pointer to the pointer which will be used to reference the buffer externally. This will be
* set to the memory area allocated to store the protocol buffer.
* @param num_keys The number of keys that will be generated
* @return size_t The size of the protocol buffer
*/
size_t generate_keys(uint8_t** buffer_pointer, int num_keys);
/**
* @brief Unpacks the protocol buffer and iterates the `process_key` function over all keys.
*
* @param buf a pointer to the buffer
* @param buf_size the size of the buffer in bytes
* @return int
*/
int unpack_keys(uint8_t* buf, size_t buf_size);
/**
* @brief Generates an protocol buffer with a specified number of keys and measures the time to execute `unpack_keys`,
* which unpacks the protocol buffer and iterates `process_key` over the keys.
*
* @param num_keys the number of keys that will be created
* @return int
*/
int test_unpacking(int num_keys);
#endif

View File

@ -1,13 +0,0 @@
/*
* 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

151
include/record_storage.h Normal file
View File

@ -0,0 +1,151 @@
/*
* Copyright (c) 2020 Olaf Landsiedel
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef RECORD_STORAGE_H
#define RECORD_STORAGE_H
#include <zephyr/types.h>
#include "utility/sequencenumber.h"
#include <exposure-notification.h>
/**
* RECORD STORAGE
*/
typedef uint64_t storage_id_t;
typedef struct bt_metadata {
uint8_t version;
uint8_t tx_power;
uint8_t rsv1;
uint8_t rsv2;
} __packed bt_metadata_t;
typedef struct associated_encrypted_metadata {
uint8_t data[sizeof(bt_metadata_t)];
} __packed associated_encrypted_metadata_t;
typedef struct record {
record_sequence_number_t sn; // TODO: Convert Sequence Number
uint32_t timestamp; // TODO: Seconds from january first 2000 (UTC+0)
uint8_t rssi; // TODO: Check correct
ENIntervalIdentifier rolling_proximity_identifier;
associated_encrypted_metadata_t associated_encrypted_metadata;
} __packed record_t;
typedef struct stored_records_information {
record_sequence_number_t oldest_contact;
uint32_t count;
} stored_records_information_t;
/**
* Initializes the contact storage component
* @param clean flag for indicating, if storage shall be init with clean state
*
* @return 0 for success
*/
int record_storage_init(bool clean);
/**
* Reset state of record storage.
*/
void reset_record_storage();
/**
* Loads the record with number sn into the destination struct
* @param dest
* @param sn
* @return 0 in case of success
*/
int load_record(record_t* dest, record_sequence_number_t sn);
/**
* Stores the record from src with number sn, increases latest sequence number
* @param sn
* @param src
* @return 0 in case of success
*/
int add_record(record_t* src);
/**
* Deletes the record from storage with number sn
* @param sn the sequence number to delete
* @return 0 in case of success
*/
int delete_record(record_sequence_number_t sn);
/**
* TODO: How to handle if none is available?
* @return The latest available sequence number (Caution: can actually be lower than the oldes in case of a
* wrap-around!)
*/
record_sequence_number_t get_latest_sequence_number();
/**
* TODO: How to handle if none is available?
* @return The oldest available sequence number
*/
record_sequence_number_t get_oldest_sequence_number();
/**
* @return The amount of contacts, usually get_latest_sequence_number() - get_oldest_sequence_number()
*/
uint32_t get_num_records();
int get_sequence_number_interval(record_sequence_number_t* oldest, record_sequence_number_t* latest);
/**
* RECORD ITERATOR
*/
typedef struct record_iterator {
/**
* @internal
*/
record_t current;
/**
* @internal
*/
record_sequence_number_t sn_next;
/**
* @internal
*/
record_sequence_number_t sn_end; // the last sn to include
/**
* @internal
*/
uint8_t finished;
} record_iterator_t;
// Also uses start and end from storage if NULL pointers given
// iterate over a sequence number range of records (Null will select the latest and newest for start / end)
// automatically
int ens_records_iterator_init_range(record_iterator_t* iterator,
record_sequence_number_t* opt_start,
record_sequence_number_t* opt_end);
// 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, time_t* ts_start, time_t* ts_end);
record_t* ens_records_iterator_next(record_iterator_t* iter);
int ens_record_iterator_clear(record_iterator_t* iter);
// TODO: Is a callback the easiest thing to do? -> now it is the iterator :)
// TODO: should we really indicated the last record by sending a NULL pointer?
typedef uint8_t (*ens_record_iterator_cb_t)(const record_t* record, void* userdata);
enum {
ENS_RECORD_ITER_STOP, // STOP the iteration, this would not result in a NULL callback after the end is reached
ENS_RECORD_ITER_CONTINUE, // CONTINUE the iteration, this results in a NULL callback after the end is reached
};
// TODO: this function could be made asynchronous to handle delays in contact_storage reads?!
// TODO: How can we handle iteration while records are being added or deleted? (should be safe as long as the
// load_record function is thread safe?!)
uint8_t ens_records_iterate_with_callback(record_iterator_t* iter, ens_record_iterator_cb_t cb, void* userdata);
#endif

13
include/sync_service.h Normal file
View File

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

35
include/tek_storage.h Normal file
View File

@ -0,0 +1,35 @@
/*
* Copyright (c) 2020 Olaf Landsiedel
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef tek_storage_H
#define tek_storage_H
#include <exposure-notification.h>
#include <zephyr/types.h>
#include "utility/sequencenumber.h"
#include "record_storage.h"
typedef struct tek {
uint32_t timestamp; // Seconds from january first 2000 (UTC+0)
ENPeriodKey tek; // the temporary exposure key
} __packed tek_t;
/**
* Initializes the contact storage component
* @param clean flag for indicating, if storage shall be init with clean state
*
* @return 0 for success
*/
int tek_storage_init(bool clean);
int tek_storage_add(tek_t* src);
int tek_storage_delete(tek_t* src);
/**
* Returns 0 on success, else non zero (if none available)
*/
int tek_storage_get_latest_at_ts(tek_t* dest, uint32_t timestamp);
#endif

View File

@ -1,14 +1,13 @@
/* io.h - Application main entry point */
/*
* Copyright (c) 2020 Olaf Landsiedel
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef IO_H
#define IO_H
#ifndef TRACING_H
#define TRACING_H
int init_io();
int tracing_init(void);
int tracing_run(void);
#endif

View File

@ -1,9 +0,0 @@
#ifndef UTIL_H
#define UTIL_H
// TODO: We should use timeutil_sync
// return the current unix timestamp in seconds
uint32_t time_get_unix_seconds();
#endif // UTIL_H

View File

@ -7,6 +7,12 @@
#include <stdint.h>
#include <storage/flash_map.h>
#define ENS_INTERR 1 // internal error
#define ENS_NOENT 2 // entry not found or invalid
#define ENS_DELENT 3 // entry got deleted
#define ENS_ADDRINU 4 // address alread in use or corrupt
#define ENS_INVARG 5 // invalid argument
typedef struct ens_fs {
/**
* Flash area for this file system.

16
include/utility/util.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef UTIL_H
#define UTIL_H
#include <exposure-notification.h>
#include "../record_storage.h" // TODO: this dependency is not great...
// TODO: We should use timeutil_sync
// return the current unix timestamp in seconds
uint32_t time_get_unix_seconds();
void print_key(_ENBaseKey* key);
void print_rpi(ENIntervalIdentifier* rpi);
void print_aem(associated_encrypted_metadata_t* aem);
#endif // UTIL_H

View File

@ -16,7 +16,7 @@ platform = nordicnrf52@7.0.0
board = nrf52840_dk
framework = zephyr
monitor_speed = 115200
upload_protocol = jlink
;upload_protocol = jlink
test_ignore = test_desktop
build_flags =
-Iinclude/tls_config
@ -28,12 +28,12 @@ lib_deps =
prathje/exposure-notification @ ^0.1
lib_ignore =
mbedtls
src_filter = -<../src/main_test.c>
;src_filter = -<../src/main_test.c>
[env:nrf52840_dk_test]
src_filter = -<../src/main.c>
extends = env:nrf52840_dk
board = nrf52840_mdk
;[env:nrf52840_dk_test]
;src_filter = -<../src/main.c>
;extends = env:nrf52840_dk
;board = nrf52840_mdk
[env:nrf52840_mdk]

View File

@ -1,46 +0,0 @@
#include "bloom.h"
#include "ens/storage.h"
#include "exposure-notification.h"
bloom_filter_t* bloom_init(size_t size) {
bloom_filter_t* bloom = k_calloc(1, sizeof(bloom_filter_t));
if (!bloom) {
return NULL;
}
bloom->size = size;
bloom->data = k_calloc(size, sizeof(uint8_t));
if (!bloom->data) {
bloom->size = 0;
k_free(bloom);
return NULL;
}
return bloom;
}
void bloom_destroy(bloom_filter_t* bloom) {
if (bloom) {
k_free(bloom->data);
k_free(bloom);
}
}
void bloom_add_record(bloom_filter_t* bloom, ENIntervalIdentifier* rpi) {
uint8_t* data = bloom->data;
for (int i = 0; i < sizeof(*rpi); i += 2) {
uint32_t hash = (rpi->b[i] << 8) | rpi->b[i + 1];
hash %= bloom->size * 8;
data[hash / 8] |= 1 << (hash % 8);
}
}
bool bloom_probably_has_record(bloom_filter_t* bloom, ENIntervalIdentifier* rpi) {
uint8_t* data = bloom->data;
for (int i = 0; i < sizeof(*rpi); i += 2) {
uint32_t hash = (rpi->b[i] << 8) | rpi->b[i + 1];
hash %= bloom->size * 8;
if (!(data[hash / 8] & (1 << (hash % 8)))) {
return false;
}
}
return true;
}

View File

@ -1,328 +0,0 @@
/*
* Copyright (c) 2020 Olaf Landsiedel
*
* SPDX-License-Identifier: Apache-2.0
*/
#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 <device.h>
#include <devicetree.h>
#include <drivers/gpio.h>
#include "bloom.h"
#include "contacts.h"
#include "covid.h"
#include "covid_types.h"
#include "ens/records.h"
#include "ens/storage.h"
#include "exposure-notification.h"
// #define CONFIG_INTERVAL_SPREAD 100
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++) {
printk("%02x", rpi->b[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) {
return add_record(record);
}
/**
* Simple implementation, where an iterator is created for every element in the passed arrray.
*/
int get_number_of_infected_for_multiple_intervals_simple(infected_for_interval_ident_ctx_t* ctx, int count) {
record_iterator_t iterator;
for (int i = 0; i < count; i++) {
if (!ctx[i].met) {
continue;
}
ctx[i].met = 0;
int rc = ens_records_iterator_init_timerange(&iterator, &ctx[i].search_start, &ctx[i].search_end);
if (rc) {
// on error, skip this rpi
continue;
}
record_t* current;
while ((current = ens_records_iterator_next(&iterator))) {
if (memcmp(&(current->rolling_proximity_identifier), &ctx[i].interval_identifier,
sizeof(rolling_proximity_identifier_t)) == 0) {
ctx[i].met++;
}
}
}
return 0;
}
/**
* Fill the bloom filter with all stored records.
*/
void fill_bloom_with_stored_records(bloom_filter_t* bloom) {
// init iterator for filling bloom filter
record_iterator_t iterator;
int rc = ens_records_iterator_init_timerange(&iterator, NULL, NULL);
if (rc) {
printk("init iterator failed0 (err %d)\n", rc);
return;
}
// fill bloom filter with records
record_t* current;
while ((current = ens_records_iterator_next(&iterator))) {
bloom_add_record(bloom, &(current->rolling_proximity_identifier));
}
}
/**
* Fill the bloom filter with flash records and test passed RPIs against it.
*/
int64_t bloom_filter(infected_for_interval_ident_ctx_t* ctx, int count) {
bloom_filter_t* bloom = bloom_init(get_num_records() * 2);
if (!bloom) {
printk("bloom init failed\n");
return -1;
}
fill_bloom_with_stored_records(bloom);
// test bloom performance
for (int i = 0; i < count; i++) {
if (bloom_probably_has_record(bloom, &ctx[i].interval_identifier)) {
ctx[i].met++;
}
}
bloom_destroy(bloom);
return get_number_of_infected_for_multiple_intervals_simple(ctx, count);
}
////////////////////
// FILL TEST DATA //
////////////////////
static ENPeriodKey infectedPeriodKey = {
.b = {0x75, 0xc7, 0x34, 0xc6, 0xdd, 0x1a, 0x78, 0x2d, 0xe7, 0xa9, 0x65, 0xda, 0x5e, 0xb9, 0x31, 0x25}};
static ENPeriodKey dummyPeriodKey = {
.b = {0x89, 0xa7, 0x34, 0xc6, 0xdd, 0x1a, 0x14, 0xda, 0xe7, 0x00, 0x65, 0xda, 0x6a, 0x9b, 0x13, 0x52}};
static ENPeriodKey testKey = {
.b = {0x89, 0xa7, 0x72, 0xc6, 0xdd, 0x10, 0x14, 0xda, 0xe7, 0x00, 0x65, 0xda, 0x8a, 0x9b, 0x13, 0x52}};
static ENPeriodIdentifierKey infectedPik;
static ENPeriodIdentifierKey dummyPik;
static ENPeriodIdentifierKey testPik;
void fill_test_rki_data(infected_for_interval_ident_ctx_t* infectedIntervals, int count) {
int totalTime = EN_TEK_ROLLING_PERIOD * EN_INTERVAL_LENGTH;
int stepSize = totalTime / count;
for (int i = 0; i < count; i++) {
int intervalNumber = (i * stepSize) / EN_INTERVAL_LENGTH;
en_derive_interval_identifier(&infectedIntervals[i].interval_identifier, &infectedPik, intervalNumber);
infectedIntervals[i].met = 0;
infectedIntervals[i].search_start = i < 3 ? 0 : (i - 2) * stepSize;
infectedIntervals[i].search_end = (i + 2) * stepSize;
}
}
////////////////////
// MEASURING FUNC //
////////////////////
void measure_perf(infected_for_interval_ident_ctx_t testIntervals[], int count) {
const char* label = "bloom filter";
printk("---------------------------\n'%s': starting measurement\n", label);
// fill_test_rki_data(infectedIntervals, count);
// setup our ordered array with met RPIs
printk("Starting measurements with %d RPIs to seach and an infection rate of %d\n", count,
CONFIG_TEST_INFECTED_RATE);
timing_t start_time, end_time;
uint64_t total_cycles;
uint64_t total_ns;
timing_init();
timing_start();
start_time = timing_counter_get();
check_possible_contacts_for_intervals(testIntervals, count);
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("\n'%s' took %lld ms\n---------------------------\n", label, total_ns / 1000000);
}
void check_results(infected_for_interval_ident_ctx_t testIntervals[], int count) {
int counter = 0;
for (int i = 0; i < count / 2; i++) {
counter += CONFIG_TEST_RECORDS_PER_INTERVAL * CONFIG_TEST_INFECTED_RATE;
int met = counter / 100;
counter %= 100;
if (testIntervals[i].met != met) {
printk("interval %d should have been met %d times (met %d)\n", i, met, testIntervals[i].met);
return;
}
}
for (int j = count / 2; j < count; j++) {
if (testIntervals[j].met) {
printk("infected interval should not have been met (interval %d)\n", j);
return;
}
}
printk("all results are as expected!\n");
}
int reverse_bloom_filter(infected_for_interval_ident_ctx_t* ctx, int count) {
bloom_filter_t* bloom = bloom_init(count * 2);
if (!bloom) {
printk("bloom init failed\n");
return -1;
}
// init bloom filter with passed records
for (int i = 0; i < count; i++) {
bloom_add_record(bloom, &ctx[i].interval_identifier);
}
int rc = 0;
// init iterator over the entire storage
record_iterator_t iterator;
rc = ens_records_iterator_init_timerange(&iterator, NULL, NULL);
if (rc) {
printk("init iterator failed (err %d)\n", rc);
goto cleanup;
}
record_t* current;
while ((current = ens_records_iterator_next(&iterator))) {
if (bloom_probably_has_record(bloom, &(current->rolling_proximity_identifier))) {
for (int i = 0; i < count; i++) {
if (memcmp(&(current->rolling_proximity_identifier), &ctx[i].interval_identifier,
sizeof(current->rolling_proximity_identifier)) == 0) {
ctx[i].met++;
}
}
}
}
cleanup:
// destroy bloom filter after things are finished
bloom_destroy(bloom);
return rc;
}
////////////////////
// SETUP DATA //
////////////////////
void setup_test_data(infected_for_interval_ident_ctx_t testIntervals[], int count) {
en_derive_period_identifier_key(&infectedPik, &infectedPeriodKey);
en_derive_period_identifier_key(&dummyPik, &dummyPeriodKey);
en_derive_period_identifier_key(&testPik, &testKey);
int counter = 0;
for (int i = 0; i < EN_TEK_ROLLING_PERIOD; i++) {
record_t infectedRecord;
record_t dummyRecord;
en_derive_interval_identifier((ENIntervalIdentifier*)&infectedRecord.rolling_proximity_identifier, &infectedPik,
i);
en_derive_interval_identifier((ENIntervalIdentifier*)&dummyRecord.rolling_proximity_identifier, &dummyPik, i);
for (int j = 0; j < CONFIG_TEST_RECORDS_PER_INTERVAL; j++) {
counter += CONFIG_TEST_INFECTED_RATE;
record_t* curRecord;
if (counter >= 100) {
counter -= 100;
curRecord = &infectedRecord;
} else {
curRecord = &dummyRecord;
}
curRecord->timestamp = i * EN_INTERVAL_LENGTH + j * (EN_INTERVAL_LENGTH / CONFIG_TEST_RECORDS_PER_INTERVAL);
int rc = add_record(curRecord);
if (rc) {
printk("err %d\n", rc);
}
}
}
for (int i = 0; i < count / 2; i++) {
en_derive_interval_identifier(&testIntervals[i].interval_identifier, &infectedPik, i);
testIntervals[i].met = 0;
testIntervals[i].search_start = MAX(0, (i * EN_INTERVAL_LENGTH - 2 * 60 * 60));
testIntervals[i].search_end = i * EN_INTERVAL_LENGTH + 2 * 60 * 60;
int j = i + count / 2;
en_derive_interval_identifier(&testIntervals[j].interval_identifier, &testPik, i);
testIntervals[j].met = 0;
testIntervals[j].search_start = MAX(0, (i * EN_INTERVAL_LENGTH - 2 * 60 * 60));
testIntervals[j].search_end = i * EN_INTERVAL_LENGTH + 2 * 60 * 60;
}
}
int init_contacts() {
#if CONFIG_CONTACTS_PERFORM_RISC_CHECK_TEST
static infected_for_interval_ident_ctx_t testIntervals[EN_TEK_ROLLING_PERIOD * 2];
reset_record_storage();
setup_test_data(testIntervals, EN_TEK_ROLLING_PERIOD * 2);
measure_perf(testIntervals, EN_TEK_ROLLING_PERIOD * 2);
check_results(testIntervals, EN_TEK_ROLLING_PERIOD * 2);
#endif
return 0;
}
int check_possible_contacts_for_intervals(infected_for_interval_ident_ctx_t* ctx, int count) {
#if CONFIG_CONTACTS_BLOOM_REVERSE