mirror of
https://github.com/Lurkars/esp-ena.git
synced 2024-11-21 17:46:11 +01:00
re-added ena-components, update interface
This commit is contained in:
parent
eff246a080
commit
4029099d2a
@ -1,13 +1,5 @@
|
|||||||
# The following lines of boilerplate have to be in your project's
|
# The following lines of boilerplate have to be in your project's
|
||||||
# CMakeLists in this exact order for cmake to work correctly
|
# CMakeLists in this exact order for cmake to work correctly
|
||||||
cmake_minimum_required(VERSION 3.5)
|
cmake_minimum_required(VERSION 3.5)
|
||||||
include(FetchContent)
|
|
||||||
FetchContent_Declare(
|
|
||||||
esp-ena
|
|
||||||
GIT_REPOSITORY https://github.com/Lurkars/esp-ena.git
|
|
||||||
GIT_TAG origin/component
|
|
||||||
)
|
|
||||||
FetchContent_MakeAvailable(esp-ena)
|
|
||||||
set(EXTRA_COMPONENT_DIRS ${esp-ena_SOURCE_DIR}/components)
|
|
||||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
project(esp-ena-device)
|
project(esp-ena-device)
|
@ -3,7 +3,7 @@
|
|||||||
Implementation of contact tracing with the Covid-19 Exposure Notification API by Apple and Google on an ESP32 (with [ESP-IDF](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/index.html)).
|
Implementation of contact tracing with the Covid-19 Exposure Notification API by Apple and Google on an ESP32 (with [ESP-IDF](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/index.html)).
|
||||||
More information about the Covid-19 Exposure Notification at [Apple](https://www.apple.com/covid19/contacttracing/) and [Google](https://www.google.com/covid19/exposurenotifications/). This is fully compatible with the official API and is meant for people without smartphone or without access to Apples/Googles implementation.
|
More information about the Covid-19 Exposure Notification at [Apple](https://www.apple.com/covid19/contacttracing/) and [Google](https://www.google.com/covid19/exposurenotifications/). This is fully compatible with the official API and is meant for people without smartphone or without access to Apples/Googles implementation.
|
||||||
|
|
||||||
The main source (the Exposure Notification API) is a separate branch in [**component**](https://github.com/Lurkars/esp-ena/tree/component).
|
The main source (the Exposure Notification API) is also available in a separate branch in [**component**](https://github.com/Lurkars/esp-ena/tree/component).
|
||||||
|
|
||||||
This implementation fully covers the BLE part including the cryptography specifications needed and the exposure check.
|
This implementation fully covers the BLE part including the cryptography specifications needed and the exposure check.
|
||||||
|
|
||||||
|
10
components/ena-binary-export/CMakeLists.txt
Normal file
10
components/ena-binary-export/CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
idf_component_register(
|
||||||
|
SRCS
|
||||||
|
"ena-binary-export.c"
|
||||||
|
INCLUDE_DIRS "."
|
||||||
|
PRIV_REQUIRES
|
||||||
|
ena
|
||||||
|
nanopb
|
||||||
|
EMBED_FILES
|
||||||
|
"test/export.bin"
|
||||||
|
)
|
90
components/ena-binary-export/ena-binary-export.c
Normal file
90
components/ena-binary-export/ena-binary-export.c
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
|
||||||
|
#include "ena-exposure.h"
|
||||||
|
|
||||||
|
#include "pb_decode.h"
|
||||||
|
#include "TemporaryExposureKeyExport.pb.h"
|
||||||
|
|
||||||
|
static const char kFileHeader[] = "EK Export v1 ";
|
||||||
|
static size_t kFileHeaderSize = sizeof(kFileHeader) - 1;
|
||||||
|
|
||||||
|
bool ena_binary_export_decode_key_data(pb_istream_t *stream, const pb_field_t *field, void **arg)
|
||||||
|
{
|
||||||
|
uint8_t *key_data = (uint8_t *)*arg;
|
||||||
|
|
||||||
|
if (!pb_read(stream, key_data, stream->bytes_left))
|
||||||
|
{
|
||||||
|
ESP_LOGW(ENA_EXPOSURE_LOG, "Decoding failed: %s\n", PB_GET_ERROR(stream));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ena_binary_export_decode_key(pb_istream_t *stream, const pb_field_t *field, void **arg)
|
||||||
|
{
|
||||||
|
uint8_t key_data[ENA_KEY_LENGTH] = {0};
|
||||||
|
TemporaryExposureKey tek = TemporaryExposureKey_init_zero;
|
||||||
|
|
||||||
|
tek.key_data = (pb_callback_t){
|
||||||
|
.funcs.decode = ena_binary_export_decode_key_data,
|
||||||
|
.arg = &key_data,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!pb_decode(stream, TemporaryExposureKey_fields, &tek))
|
||||||
|
{
|
||||||
|
ESP_LOGW(ENA_EXPOSURE_LOG, "Decoding failed: %s\n", PB_GET_ERROR(stream));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGD(ENA_EXPOSURE_LOG,
|
||||||
|
"check reported tek: rolling_start_interval_number %d, rolling_period %d, days_since_last_exposure %d, report_type %d, transmission_risk_values %d",
|
||||||
|
tek.rolling_start_interval_number, tek.rolling_period, tek.days_since_onset_of_symptoms, tek.report_type, tek.transmission_risk_level);
|
||||||
|
|
||||||
|
ESP_LOG_BUFFER_HEXDUMP(ENA_EXPOSURE_LOG, &key_data, ENA_KEY_LENGTH, ESP_LOG_DEBUG);
|
||||||
|
|
||||||
|
ena_temporary_exposure_key_t temporary_exposure_key = {
|
||||||
|
.transmission_risk_level = tek.transmission_risk_level,
|
||||||
|
.rolling_start_interval_number = tek.rolling_start_interval_number,
|
||||||
|
.rolling_period = tek.rolling_period,
|
||||||
|
.report_type = tek.report_type,
|
||||||
|
.days_since_onset_of_symptoms = tek.days_since_onset_of_symptoms,
|
||||||
|
};
|
||||||
|
|
||||||
|
memcpy(temporary_exposure_key.key_data, key_data, ENA_KEY_LENGTH);
|
||||||
|
|
||||||
|
ena_exposure_check_temporary_exposure_key(temporary_exposure_key);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t ena_binary_export_check_export(uint8_t *buf, size_t size)
|
||||||
|
{
|
||||||
|
// validate header
|
||||||
|
if (memcmp(kFileHeader, buf, kFileHeaderSize) != 0)
|
||||||
|
{
|
||||||
|
ESP_LOGW(ENA_EXPOSURE_LOG, "Wrong or missing header!");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
TemporaryExposureKeyExport tek_export = TemporaryExposureKeyExport_init_zero;
|
||||||
|
|
||||||
|
tek_export.keys = (pb_callback_t){
|
||||||
|
.funcs.decode = ena_binary_export_decode_key,
|
||||||
|
};
|
||||||
|
|
||||||
|
pb_istream_t stream = pb_istream_from_buffer(&buf[kFileHeaderSize], (size - kFileHeaderSize));
|
||||||
|
|
||||||
|
if (!pb_decode(&stream, TemporaryExposureKeyExport_fields, &tek_export))
|
||||||
|
{
|
||||||
|
ESP_LOGW(ENA_EXPOSURE_LOG, "Decoding failed: %s\n", PB_GET_ERROR(&stream));
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
37
components/ena-binary-export/ena-binary-export.h
Normal file
37
components/ena-binary-export/ena-binary-export.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// Copyright 2020 Lukas Haubaum
|
||||||
|
//
|
||||||
|
// Licensed under the GNU Affero General Public License, Version 3;
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// https://www.gnu.org/licenses/agpl-3.0.html
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
*
|
||||||
|
* @brief decode Exposure Key export and run exposure check
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef _ena_BINARY_H_
|
||||||
|
#define _ena_BINARY_H_
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "esp_err.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief reads a Temporary Exposue Key Export binary and check for exposures
|
||||||
|
*
|
||||||
|
* @param[in] buf the buffer containing the binary data
|
||||||
|
* @param[in] size the size of the buffer
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* esp_err_t status of reading binary
|
||||||
|
*/
|
||||||
|
esp_err_t ena_binary_export_check_export(uint8_t *buf, size_t size);
|
||||||
|
|
||||||
|
#endif
|
BIN
components/ena-binary-export/test/export.bin
Normal file
BIN
components/ena-binary-export/test/export.bin
Normal file
Binary file not shown.
BIN
components/ena-binary-export/test/export_g.bin
Normal file
BIN
components/ena-binary-export/test/export_g.bin
Normal file
Binary file not shown.
BIN
components/ena-binary-export/test/export_g.sig
Normal file
BIN
components/ena-binary-export/test/export_g.sig
Normal file
Binary file not shown.
BIN
components/ena-binary-export/test/export_tel.bin
Normal file
BIN
components/ena-binary-export/test/export_tel.bin
Normal file
Binary file not shown.
BIN
components/ena-binary-export/test/export_tel.sig
Normal file
BIN
components/ena-binary-export/test/export_tel.sig
Normal file
Binary file not shown.
14
components/ena/CMakeLists.txt
Normal file
14
components/ena/CMakeLists.txt
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
idf_component_register(
|
||||||
|
SRCS
|
||||||
|
"ena.c"
|
||||||
|
"ena-beacons.c"
|
||||||
|
"ena-bluetooth-advertise.c"
|
||||||
|
"ena-bluetooth-scan.c"
|
||||||
|
"ena-crypto.c"
|
||||||
|
"ena-exposure.c"
|
||||||
|
"ena-storage.c"
|
||||||
|
INCLUDE_DIRS "include"
|
||||||
|
PRIV_REQUIRES
|
||||||
|
spi_flash
|
||||||
|
mbedtls
|
||||||
|
bt)
|
94
components/ena/Kconfig.projbuild
Normal file
94
components/ena/Kconfig.projbuild
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
menu "Exposure Notification API"
|
||||||
|
|
||||||
|
menu "Storage"
|
||||||
|
config ENA_STORAGE_DUMP
|
||||||
|
bool "Dump storage"
|
||||||
|
default false
|
||||||
|
help
|
||||||
|
Dump storage (stored TEKs, temp. beacons and perm. beacons) to serial output after scan.
|
||||||
|
|
||||||
|
config ENA_STORAGE_TEK_MAX
|
||||||
|
int "Max. TEKs"
|
||||||
|
default 14
|
||||||
|
help
|
||||||
|
Defines the maximum number of TEKs to be stored. (Default 14 [14 * 144 => 14 days])
|
||||||
|
|
||||||
|
config ENA_STORAGE_EXPOSURE_INFORMATION_MAX
|
||||||
|
int "Max. exporure information"
|
||||||
|
default 500
|
||||||
|
help
|
||||||
|
Defines the maximum number of exposure information to be stored. (Default 500)
|
||||||
|
|
||||||
|
config ENA_STORAGE_TEMP_BEACONS_MAX
|
||||||
|
int "Max. temporary beacons"
|
||||||
|
default 1000
|
||||||
|
help
|
||||||
|
Defines the maximum number of temporary beacons to be stored. (Default 1000)
|
||||||
|
|
||||||
|
config ENA_STORAGE_START_ADDRESS
|
||||||
|
int "Storage start address"
|
||||||
|
default 0
|
||||||
|
help
|
||||||
|
Defines the start address on partition. (Default 0)
|
||||||
|
|
||||||
|
config ENA_STORAGE_PARTITION_NAME
|
||||||
|
string "Partition name"
|
||||||
|
default "ena"
|
||||||
|
help
|
||||||
|
Name of the partition used for storage. (Default "ena", see partitions.csv)
|
||||||
|
|
||||||
|
config ENA_STORAGE_ERASE
|
||||||
|
bool "Erase storage (!)"
|
||||||
|
default false
|
||||||
|
help
|
||||||
|
Erases the complete(!) partition on startup and reset counters.
|
||||||
|
|
||||||
|
endmenu
|
||||||
|
|
||||||
|
menu "Scanning"
|
||||||
|
config ENA_BEACON_TRESHOLD
|
||||||
|
int "Contact threshold"
|
||||||
|
default 300
|
||||||
|
help
|
||||||
|
Threshold in seconds after a received beacon is stored permanently. (Default 5 minutes)
|
||||||
|
|
||||||
|
config ENA_BEACON_CLEANUP_TRESHOLD
|
||||||
|
int "Clean-Up threshold"
|
||||||
|
default 14
|
||||||
|
help
|
||||||
|
Threshold in days after stored beacons to be removed.
|
||||||
|
|
||||||
|
config ENA_SCANNING_TIME
|
||||||
|
int "Scanning time"
|
||||||
|
default 30
|
||||||
|
help
|
||||||
|
Time in seconds how long a scan should run. (Default 30 seconds)
|
||||||
|
|
||||||
|
config ENA_SCANNING_INTERVAL
|
||||||
|
int "Scanning interval"
|
||||||
|
default 300
|
||||||
|
help
|
||||||
|
Interval in seconds for the next scan to happen. (Default 5 minutes)
|
||||||
|
endmenu
|
||||||
|
|
||||||
|
menu "Advertising"
|
||||||
|
config ENA_BT_ROTATION_TIMEOUT_INTERVAL
|
||||||
|
int "Rotation timeout interval"
|
||||||
|
default 900
|
||||||
|
help
|
||||||
|
Base rotation timeout interval in seconds for BT address change & therefore the advertised beacon.(Default 5 minutes)
|
||||||
|
|
||||||
|
config ENA_BT_RANDOMIZE_ROTATION_TIMEOUT_INTERVAL
|
||||||
|
int "Randomize rotation timeout interval"
|
||||||
|
default 150
|
||||||
|
help
|
||||||
|
Range in seconds for randomize the rotation timeout interval. (Default +/- ~2.5 minutes)
|
||||||
|
|
||||||
|
config ENA_TEK_ROLLING_PERIOD
|
||||||
|
int "TEK rolling period"
|
||||||
|
default 144
|
||||||
|
help
|
||||||
|
Defines the TEK rolling period in 10 minute steps. (Default 144 => 24 hours)
|
||||||
|
endmenu
|
||||||
|
|
||||||
|
endmenu
|
123
components/ena/ena-beacons.c
Normal file
123
components/ena/ena-beacons.c
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
// Copyright 2020 Lukas Haubaum
|
||||||
|
//
|
||||||
|
// Licensed under the GNU Affero General Public License, Version 3;
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// https://www.gnu.org/licenses/agpl-3.0.html
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "esp_log.h"
|
||||||
|
|
||||||
|
#include "ena-crypto.h"
|
||||||
|
#include "ena-storage.h"
|
||||||
|
|
||||||
|
#include "ena-beacons.h"
|
||||||
|
|
||||||
|
static uint32_t temp_beacons_count = 0;
|
||||||
|
static ena_beacon_t temp_beacons[ENA_STORAGE_TEMP_BEACONS_MAX];
|
||||||
|
|
||||||
|
int ena_get_temp_beacon_index(uint8_t *rpi, uint8_t *aem)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < temp_beacons_count; i++)
|
||||||
|
{
|
||||||
|
if (memcmp(temp_beacons[i].rpi, rpi, sizeof(ENA_KEY_LENGTH)) == 0 &&
|
||||||
|
memcmp(temp_beacons[i].aem, aem, sizeof(ENA_AEM_METADATA_LENGTH)) == 0)
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_beacons_temp_refresh(uint32_t unix_timestamp)
|
||||||
|
{
|
||||||
|
for (int i = temp_beacons_count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
// check for treshold and add permanent beacon
|
||||||
|
if (temp_beacons[i].timestamp_last - temp_beacons[i].timestamp_first >= ENA_BEACON_TRESHOLD)
|
||||||
|
{
|
||||||
|
ESP_LOGD(ENA_BEACON_LOG, "create beacon after treshold");
|
||||||
|
ESP_LOG_BUFFER_HEXDUMP(ENA_BEACON_LOG, temp_beacons[i].rpi, ENA_KEY_LENGTH, ESP_LOG_DEBUG);
|
||||||
|
ena_storage_add_beacon(&temp_beacons[i]);
|
||||||
|
ena_storage_remove_temp_beacon(i);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
// delete temp beacons older than two times time window (two times to be safe, one times time window enough?!)
|
||||||
|
if (unix_timestamp - temp_beacons[i].timestamp_last > (ENA_TIME_WINDOW * 2))
|
||||||
|
{
|
||||||
|
ESP_LOGD(ENA_BEACON_LOG, "remove old temporary beacon %u", i);
|
||||||
|
ena_storage_remove_temp_beacon(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// update beacons
|
||||||
|
temp_beacons_count = ena_storage_temp_beacons_count();
|
||||||
|
for (int i = 0; i < temp_beacons_count; i++)
|
||||||
|
{
|
||||||
|
ena_storage_get_temp_beacon(i, &temp_beacons[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if (CONFIG_ENA_STORAGE_DUMP)
|
||||||
|
// DEBUG dump
|
||||||
|
ena_storage_dump_teks();
|
||||||
|
ena_storage_dump_exposure_information();
|
||||||
|
ena_storage_dump_temp_beacons();
|
||||||
|
ena_storage_dump_beacons();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_beacons_cleanup(uint32_t unix_timestamp)
|
||||||
|
{
|
||||||
|
uint32_t count = ena_storage_beacons_count();
|
||||||
|
ena_beacon_t beacon;
|
||||||
|
for (int i = count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
ena_storage_get_beacon(i, &beacon);
|
||||||
|
if (((unix_timestamp - beacon.timestamp_last) / (60 * 60 * 24)) > ENA_BEACON_CLEANUP_TRESHOLD)
|
||||||
|
{
|
||||||
|
ena_storage_remove_beacon(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_beacon(uint32_t unix_timestamp, uint8_t *rpi, uint8_t *aem, int rssi)
|
||||||
|
{
|
||||||
|
uint32_t beacon_index = ena_get_temp_beacon_index(rpi, aem);
|
||||||
|
if (beacon_index == -1)
|
||||||
|
{
|
||||||
|
temp_beacons[temp_beacons_count].timestamp_first = unix_timestamp;
|
||||||
|
memcpy(temp_beacons[temp_beacons_count].rpi, rpi, ENA_KEY_LENGTH);
|
||||||
|
memcpy(temp_beacons[temp_beacons_count].aem, aem, ENA_AEM_METADATA_LENGTH);
|
||||||
|
temp_beacons[temp_beacons_count].rssi = rssi;
|
||||||
|
temp_beacons[temp_beacons_count].timestamp_last = unix_timestamp;
|
||||||
|
beacon_index = ena_storage_add_temp_beacon(&temp_beacons[temp_beacons_count]);
|
||||||
|
ESP_LOGD(ENA_BEACON_LOG, "new temporary beacon %d at %u", temp_beacons_count, unix_timestamp);
|
||||||
|
ESP_LOG_BUFFER_HEX_LEVEL(ENA_BEACON_LOG, rpi, ENA_KEY_LENGTH, ESP_LOG_DEBUG);
|
||||||
|
ESP_LOG_BUFFER_HEX_LEVEL(ENA_BEACON_LOG, aem, ENA_AEM_METADATA_LENGTH, ESP_LOG_DEBUG);
|
||||||
|
ESP_LOGD(ENA_BEACON_LOG, "RSSI %d", rssi);
|
||||||
|
|
||||||
|
if (beacon_index != temp_beacons_count)
|
||||||
|
{
|
||||||
|
ESP_LOGW(ENA_BEACON_LOG, "last temporary beacon index does not match array index!");
|
||||||
|
}
|
||||||
|
temp_beacons_count++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
temp_beacons[beacon_index].rssi = (temp_beacons[beacon_index].rssi + rssi) / 2;
|
||||||
|
temp_beacons[beacon_index].timestamp_last = unix_timestamp;
|
||||||
|
ESP_LOGD(ENA_BEACON_LOG, "update temporary beacon %d at %u", beacon_index, unix_timestamp);
|
||||||
|
ESP_LOG_BUFFER_HEX_LEVEL(ENA_BEACON_LOG, temp_beacons[beacon_index].rpi, ENA_KEY_LENGTH, ESP_LOG_DEBUG);
|
||||||
|
ESP_LOG_BUFFER_HEX_LEVEL(ENA_BEACON_LOG, temp_beacons[beacon_index].aem, ENA_AEM_METADATA_LENGTH, ESP_LOG_DEBUG);
|
||||||
|
ESP_LOGD(ENA_BEACON_LOG, "RSSI %d", temp_beacons[beacon_index].rssi);
|
||||||
|
ena_storage_set_temp_beacon(beacon_index, &temp_beacons[beacon_index]);
|
||||||
|
}
|
||||||
|
}
|
88
components/ena/ena-bluetooth-advertise.c
Normal file
88
components/ena/ena-bluetooth-advertise.c
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
// Copyright 2020 Lukas Haubaum
|
||||||
|
//
|
||||||
|
// Licensed under the GNU Affero General Public License, Version 3;
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// https://www.gnu.org/licenses/agpl-3.0.html
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_bt.h"
|
||||||
|
#include "esp_gap_ble_api.h"
|
||||||
|
|
||||||
|
#include "ena-crypto.h"
|
||||||
|
|
||||||
|
#include "ena-bluetooth-advertise.h"
|
||||||
|
|
||||||
|
static esp_ble_adv_params_t ena_adv_params = {
|
||||||
|
.adv_int_min = 0x140, // 200 ms
|
||||||
|
.adv_int_max = 0x190, // 250 ms
|
||||||
|
.adv_type = ADV_TYPE_NONCONN_IND,
|
||||||
|
.own_addr_type = BLE_ADDR_TYPE_RANDOM,
|
||||||
|
.channel_map = ADV_CHNL_ALL,
|
||||||
|
.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
|
||||||
|
};
|
||||||
|
|
||||||
|
void ena_bluetooth_advertise_start(void)
|
||||||
|
{
|
||||||
|
ESP_ERROR_CHECK(esp_ble_gap_start_advertising(&ena_adv_params));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_bluetooth_advertise_set_payload(uint32_t enin, uint8_t *tek)
|
||||||
|
{
|
||||||
|
uint8_t rpik[ENA_KEY_LENGTH] = {0};
|
||||||
|
uint8_t rpi[ENA_KEY_LENGTH] = {0};
|
||||||
|
uint8_t aemk[ENA_KEY_LENGTH] = {0};
|
||||||
|
uint8_t aem[ENA_AEM_METADATA_LENGTH] = {0};
|
||||||
|
|
||||||
|
ena_crypto_rpik(rpik, tek);
|
||||||
|
|
||||||
|
ena_crypto_rpi(rpi, rpik, enin);
|
||||||
|
|
||||||
|
ena_crypto_aemk(aemk, tek);
|
||||||
|
|
||||||
|
ena_crypto_aem(aem, aemk, rpi, esp_ble_tx_power_get(ESP_BLE_PWR_TYPE_ADV));
|
||||||
|
|
||||||
|
uint8_t adv_raw_data[31];
|
||||||
|
// FLAG??? skipped on sniffed android packages!?
|
||||||
|
adv_raw_data[0] = 0x02;
|
||||||
|
adv_raw_data[1] = 0x01;
|
||||||
|
adv_raw_data[2] = ENA_BLUETOOTH_TAG_DATA;
|
||||||
|
|
||||||
|
// SERVICE UUID
|
||||||
|
adv_raw_data[3] = 0x03;
|
||||||
|
adv_raw_data[4] = 0x03;
|
||||||
|
adv_raw_data[5] = 0x6F;
|
||||||
|
adv_raw_data[6] = 0xFD;
|
||||||
|
|
||||||
|
// SERVICE DATA
|
||||||
|
adv_raw_data[7] = 0x17;
|
||||||
|
adv_raw_data[8] = 0x16;
|
||||||
|
|
||||||
|
adv_raw_data[9] = 0x6F;
|
||||||
|
adv_raw_data[10] = 0xFD;
|
||||||
|
|
||||||
|
for (int i = 0; i < ENA_KEY_LENGTH; i++)
|
||||||
|
{
|
||||||
|
adv_raw_data[i + 11] = rpi[i];
|
||||||
|
}
|
||||||
|
for (int i = 0; i < ENA_AEM_METADATA_LENGTH; i++)
|
||||||
|
{
|
||||||
|
adv_raw_data[i + ENA_KEY_LENGTH + 11] = aem[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_ble_gap_config_adv_data_raw(adv_raw_data, sizeof(adv_raw_data));
|
||||||
|
|
||||||
|
ESP_LOGD(ENA_ADVERTISE_LOG, "payload for ENIN %u", enin);
|
||||||
|
ESP_LOG_BUFFER_HEXDUMP(ENA_ADVERTISE_LOG, adv_raw_data, sizeof(adv_raw_data), ESP_LOG_DEBUG);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_bluetooth_advertise_stop(void)
|
||||||
|
{
|
||||||
|
ESP_ERROR_CHECK(esp_ble_gap_stop_advertising());
|
||||||
|
}
|
113
components/ena/ena-bluetooth-scan.c
Normal file
113
components/ena/ena-bluetooth-scan.c
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
// Copyright 2020 Lukas Haubaum
|
||||||
|
//
|
||||||
|
// Licensed under the GNU Affero General Public License, Version 3;
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// https://www.gnu.org/licenses/agpl-3.0.html
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_gap_ble_api.h"
|
||||||
|
|
||||||
|
#include "ena-crypto.h"
|
||||||
|
#include "ena-beacons.h"
|
||||||
|
|
||||||
|
#include "ena-bluetooth-scan.h"
|
||||||
|
|
||||||
|
static int scan_status = ENA_SCAN_STATUS_NOT_SCANNING;
|
||||||
|
|
||||||
|
static const uint16_t ENA_SERVICE_UUID = 0xFD6F;
|
||||||
|
|
||||||
|
static esp_ble_scan_params_t ena_scan_params = {
|
||||||
|
.scan_type = BLE_SCAN_TYPE_ACTIVE,
|
||||||
|
.own_addr_type = BLE_ADDR_TYPE_RANDOM,
|
||||||
|
.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL,
|
||||||
|
.scan_interval = 0x50, // don't know good parameters, just copied
|
||||||
|
.scan_window = 0x30, // don't know good parameters, just copied
|
||||||
|
.scan_duplicate = BLE_SCAN_DUPLICATE_ENABLE,
|
||||||
|
};
|
||||||
|
|
||||||
|
void ena_bluetooth_scan_event_callback(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
|
||||||
|
{
|
||||||
|
|
||||||
|
uint32_t unix_timestamp = (uint32_t)time(NULL);
|
||||||
|
esp_ble_gap_cb_param_t *p = (esp_ble_gap_cb_param_t *)param;
|
||||||
|
switch (event)
|
||||||
|
{
|
||||||
|
case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT:
|
||||||
|
ESP_LOGD(ENA_SCAN_LOG, "start scanning...");
|
||||||
|
break;
|
||||||
|
case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT:
|
||||||
|
ESP_LOGD(ENA_SCAN_LOG, "stopped scanning...");
|
||||||
|
ena_beacons_temp_refresh(unix_timestamp);
|
||||||
|
break;
|
||||||
|
case ESP_GAP_BLE_SCAN_RESULT_EVT:
|
||||||
|
if (p->scan_rst.search_evt == ESP_GAP_SEARCH_INQ_RES_EVT)
|
||||||
|
{
|
||||||
|
uint8_t service_uuid_length = 0;
|
||||||
|
uint8_t *service_uuid_data = esp_ble_resolve_adv_data(p->scan_rst.ble_adv, 0x03, &service_uuid_length);
|
||||||
|
// check for ENA Service UUID
|
||||||
|
if (service_uuid_length == sizeof(ENA_SERVICE_UUID) && memcmp(service_uuid_data, &ENA_SERVICE_UUID, service_uuid_length) == 0)
|
||||||
|
{
|
||||||
|
uint8_t service_data_length = 0;
|
||||||
|
uint8_t *service_data = esp_ble_resolve_adv_data(p->scan_rst.ble_adv, 0x16, &service_data_length);
|
||||||
|
if (service_data_length != (sizeof(ENA_SERVICE_UUID) + ENA_KEY_LENGTH + ENA_AEM_METADATA_LENGTH))
|
||||||
|
{
|
||||||
|
ESP_LOGW(ENA_SCAN_LOG, "received ENA Service with invalid payload");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *rpi = malloc(ENA_KEY_LENGTH);
|
||||||
|
memcpy(rpi, &service_data[sizeof(ENA_SERVICE_UUID)], ENA_KEY_LENGTH);
|
||||||
|
uint8_t *aem = malloc(ENA_AEM_METADATA_LENGTH);
|
||||||
|
memcpy(aem, &service_data[sizeof(ENA_SERVICE_UUID) + ENA_KEY_LENGTH], ENA_AEM_METADATA_LENGTH);
|
||||||
|
ena_beacon(unix_timestamp, rpi, aem, p->scan_rst.rssi);
|
||||||
|
free(rpi);
|
||||||
|
free(aem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (p->scan_rst.search_evt == ESP_GAP_SEARCH_INQ_CMPL_EVT)
|
||||||
|
{
|
||||||
|
scan_status = ENA_SCAN_STATUS_NOT_SCANNING;
|
||||||
|
ena_beacons_temp_refresh(unix_timestamp);
|
||||||
|
ESP_LOGD(ENA_SCAN_LOG, "finished scanning...");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// nothing
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_bluetooth_scan_init(void)
|
||||||
|
{
|
||||||
|
ESP_ERROR_CHECK(esp_ble_gap_set_scan_params(&ena_scan_params));
|
||||||
|
ESP_ERROR_CHECK(esp_ble_gap_register_callback(ena_bluetooth_scan_event_callback));
|
||||||
|
// init temporary beacons
|
||||||
|
ena_beacons_temp_refresh((uint32_t)time(NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_bluetooth_scan_start(uint32_t duration)
|
||||||
|
{
|
||||||
|
scan_status = ENA_SCAN_STATUS_SCANNING;
|
||||||
|
ESP_ERROR_CHECK(esp_ble_gap_start_scanning(duration));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_bluetooth_scan_stop(void)
|
||||||
|
{
|
||||||
|
scan_status = ENA_SCAN_STATUS_WAITING;
|
||||||
|
ESP_ERROR_CHECK(esp_ble_gap_stop_scanning());
|
||||||
|
}
|
||||||
|
|
||||||
|
int ena_bluetooth_scan_get_status(void)
|
||||||
|
{
|
||||||
|
return scan_status;
|
||||||
|
}
|
96
components/ena/ena-crypto.c
Normal file
96
components/ena/ena-crypto.c
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
// Copyright 2020 Lukas Haubaum
|
||||||
|
//
|
||||||
|
// Licensed under the GNU Affero General Public License, Version 3;
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// https://www.gnu.org/licenses/agpl-3.0.html
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#include "mbedtls/md.h"
|
||||||
|
#include "mbedtls/aes.h"
|
||||||
|
#include "mbedtls/hkdf.h"
|
||||||
|
#include "mbedtls/entropy.h"
|
||||||
|
#include "mbedtls/ctr_drbg.h"
|
||||||
|
|
||||||
|
#include "esp_log.h"
|
||||||
|
|
||||||
|
#include "ena-crypto.h"
|
||||||
|
|
||||||
|
#define ESP_CRYPTO_LOG "ESP-CRYPTO"
|
||||||
|
|
||||||
|
static mbedtls_ctr_drbg_context ctr_drbg;
|
||||||
|
|
||||||
|
void ena_crypto_init(void)
|
||||||
|
{
|
||||||
|
mbedtls_entropy_context entropy;
|
||||||
|
uint8_t pers[] = "Exposure Notifcation API esp32";
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
mbedtls_entropy_init(&entropy);
|
||||||
|
mbedtls_ctr_drbg_init(&ctr_drbg);
|
||||||
|
|
||||||
|
if ((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, pers, sizeof(pers))) != 0)
|
||||||
|
{
|
||||||
|
ESP_LOGE(ESP_CRYPTO_LOG, " failed\n ! mbedtls_ctr_drbg_init returned -0x%04x\n", -ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t ena_crypto_enin(uint32_t unix_epoch_time)
|
||||||
|
{
|
||||||
|
return unix_epoch_time / ENA_TIME_WINDOW;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_crypto_tek(uint8_t *tek)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
if ((ret = mbedtls_ctr_drbg_random(&ctr_drbg, tek, ENA_KEY_LENGTH)) != 0)
|
||||||
|
{
|
||||||
|
ESP_LOGE(ESP_CRYPTO_LOG, " failed\n ! mbedtls_ctr_drbg_random returned -0x%04x\n", -ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_crypto_rpik(uint8_t *rpik, uint8_t *tek)
|
||||||
|
{
|
||||||
|
const uint8_t rpik_info[] = "EN-RPIK";
|
||||||
|
mbedtls_hkdf(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), NULL, 0, tek, ENA_KEY_LENGTH, rpik_info, sizeof(rpik_info), rpik, ENA_KEY_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_crypto_rpi(uint8_t *rpi, uint8_t *rpik, uint32_t enin)
|
||||||
|
{
|
||||||
|
uint8_t padded_data[] = "EN-RPI";
|
||||||
|
padded_data[12] = (enin & 0x000000ff);
|
||||||
|
padded_data[13] = (enin & 0x0000ff00) >> 8;
|
||||||
|
padded_data[14] = (enin & 0x00ff0000) >> 16;
|
||||||
|
padded_data[15] = (enin & 0xff000000) >> 24;
|
||||||
|
|
||||||
|
mbedtls_aes_context aes;
|
||||||
|
mbedtls_aes_init(&aes);
|
||||||
|
mbedtls_aes_setkey_enc(&aes, rpik, ENA_KEY_LENGTH * 8);
|
||||||
|
mbedtls_aes_crypt_ecb(&aes, MBEDTLS_AES_ENCRYPT, padded_data, rpi);
|
||||||
|
mbedtls_aes_free(&aes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_crypto_aemk(uint8_t *aemk, uint8_t *tek)
|
||||||
|
{
|
||||||
|
uint8_t aemkInfo[] = "EN-AEMK";
|
||||||
|
mbedtls_hkdf(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), NULL, 0, tek, ENA_KEY_LENGTH, aemkInfo, sizeof(aemkInfo), aemk, ENA_KEY_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_crypto_aem(uint8_t *aem, uint8_t *aemk, uint8_t *rpi, uint8_t power_level)
|
||||||
|
{
|
||||||
|
uint8_t metadata[ENA_AEM_METADATA_LENGTH];
|
||||||
|
metadata[0] = 0b01000000;
|
||||||
|
metadata[1] = power_level;
|
||||||
|
size_t count = 0;
|
||||||
|
uint8_t sb[16] = {0};
|
||||||
|
mbedtls_aes_context aes;
|
||||||
|
mbedtls_aes_init(&aes);
|
||||||
|
mbedtls_aes_setkey_enc(&aes, aemk, ENA_KEY_LENGTH * 8);
|
||||||
|
mbedtls_aes_crypt_ctr(&aes, ENA_AEM_METADATA_LENGTH, &count, rpi, sb, metadata, aem);
|
||||||
|
mbedtls_aes_free(&aes);
|
||||||
|
}
|
327
components/ena/ena-exposure.c
Normal file
327
components/ena/ena-exposure.c
Normal file
@ -0,0 +1,327 @@
|
|||||||
|
// Copyright 2020 Lukas Haubaum
|
||||||
|
//
|
||||||
|
// Licensed under the GNU Affero General Public License, Version 3;
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// https://www.gnu.org/licenses/agpl-3.0.html
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
|
||||||
|
#include "ena-crypto.h"
|
||||||
|
#include "ena-storage.h"
|
||||||
|
#include "ena-beacons.h"
|
||||||
|
|
||||||
|
#include "ena-exposure.h"
|
||||||
|
|
||||||
|
static ena_exposure_summary_t *current_summary;
|
||||||
|
|
||||||
|
static ena_exposure_config_t DEFAULT_ENA_EXPOSURE_CONFIG = {
|
||||||
|
// transmission_risk_values
|
||||||
|
{
|
||||||
|
MINIMAL, // UNKNOWN
|
||||||
|
LOW, // CONFIRMED_TEST_LOW
|
||||||
|
MEDIUM, // CONFIRMED_TEST_STANDARD
|
||||||
|
HIGH, // CONFIRMED_TEST_HIGH
|
||||||
|
VERY_HIGH, // CONFIRMED_CLINICAL_DIAGNOSIS
|
||||||
|
VERY_LOW, // SELF_REPORT
|
||||||
|
ZERO, // NEGATIVE
|
||||||
|
MINIMAL // RECURSIVE
|
||||||
|
},
|
||||||
|
// duration_risk_values
|
||||||
|
{
|
||||||
|
MINIMAL, // D = 0 min
|
||||||
|
MINIMAL, // D <= 5 min
|
||||||
|
MEDIUM, // D <= 10 min
|
||||||
|
VERY_HIGH, // D <= 15 min
|
||||||
|
VERY_HIGH, // D <= 20 min
|
||||||
|
MAXIMUM, // D <= 25 min
|
||||||
|
MAXIMUM, // D <= 30 min
|
||||||
|
MAXIMUM // D > 30 min
|
||||||
|
},
|
||||||
|
// days_risk_values
|
||||||
|
{
|
||||||
|
MINIMAL, // >= 14 days
|
||||||
|
VERY_LOW, // 12-13 days
|
||||||
|
VERY_LOW, // 10-11 days
|
||||||
|
MEDIUM, // 8-9 days
|
||||||
|
HIGH, // 6-7 days
|
||||||
|
MAXIMUM, // 4-5 days
|
||||||
|
MAXIMUM, // 2-3 days
|
||||||
|
MAXIMUM // 0-1 days
|
||||||
|
},
|
||||||
|
// attenuation_risk_values
|
||||||
|
{
|
||||||
|
MINIMAL, // A > 73 dB
|
||||||
|
MINIMAL, // 73 >= A > 63
|
||||||
|
MINIMAL, // 63 >= A > 61
|
||||||
|
MAXIMUM, // 51 >= A > 33
|
||||||
|
MAXIMUM, // 33 >= A > 27
|
||||||
|
MAXIMUM, // 27 >= A > 15
|
||||||
|
MAXIMUM, // 15 >= A > 10
|
||||||
|
MAXIMUM // A <= 10
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
int ena_exposure_transmission_risk_score(ena_exposure_config_t *config, ena_exposure_parameter_t params)
|
||||||
|
{
|
||||||
|
return config->transmission_risk_values[params.report_type];
|
||||||
|
}
|
||||||
|
|
||||||
|
int ena_exposure_duration_risk_score(ena_exposure_config_t *config, ena_exposure_parameter_t params)
|
||||||
|
{
|
||||||
|
// calc duration level
|
||||||
|
int duration_level = MINUTES_0;
|
||||||
|
if (params.duration > 0)
|
||||||
|
{
|
||||||
|
if (params.duration <= 5)
|
||||||
|
{
|
||||||
|
duration_level = MINUTES_5;
|
||||||
|
}
|
||||||
|
else if (params.duration <= 10)
|
||||||
|
{
|
||||||
|
duration_level = MINUTES_10;
|
||||||
|
}
|
||||||
|
if (params.duration <= 15)
|
||||||
|
{
|
||||||
|
duration_level = MINUTES_15;
|
||||||
|
}
|
||||||
|
if (params.duration <= 20)
|
||||||
|
{
|
||||||
|
duration_level = MINUTES_20;
|
||||||
|
}
|
||||||
|
if (params.duration <= 25)
|
||||||
|
{
|
||||||
|
duration_level = MINUTES_25;
|
||||||
|
}
|
||||||
|
if (params.duration <= 30)
|
||||||
|
{
|
||||||
|
duration_level = MINUTES_30;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
duration_level = MINUTES_LONGER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return config->duration_risk_values[duration_level];
|
||||||
|
}
|
||||||
|
|
||||||
|
int ena_exposure_days_risk_score(ena_exposure_config_t *config, ena_exposure_parameter_t params)
|
||||||
|
{
|
||||||
|
// calc days level
|
||||||
|
int days_level = DAYS_14;
|
||||||
|
|
||||||
|
if (params.days < 2)
|
||||||
|
{
|
||||||
|
days_level = DAYS_0;
|
||||||
|
}
|
||||||
|
else if (params.days < 4)
|
||||||
|
{
|
||||||
|
days_level = DAYS_3;
|
||||||
|
}
|
||||||
|
else if (params.days < 6)
|
||||||
|
{
|
||||||
|
days_level = DAYS_5;
|
||||||
|
}
|
||||||
|
else if (params.days < 8)
|
||||||
|
{
|
||||||
|
days_level = DAYS_7;
|
||||||
|
}
|
||||||
|
else if (params.days < 10)
|
||||||
|
{
|
||||||
|
days_level = DAYS_9;
|
||||||
|
}
|
||||||
|
else if (params.days < 12)
|
||||||
|
{
|
||||||
|
days_level = DAYS_11;
|
||||||
|
}
|
||||||
|
else if (params.days < 14)
|
||||||
|
{
|
||||||
|
days_level = DAYS_13;
|
||||||
|
}
|
||||||
|
|
||||||
|
return config->days_risk_values[days_level];
|
||||||
|
}
|
||||||
|
|
||||||
|
int ena_exposure_attenuation_risk_score(ena_exposure_config_t *config, ena_exposure_parameter_t params)
|
||||||
|
{
|
||||||
|
// calc attenuation level
|
||||||
|
int attenuation_level = ATTENUATION_73;
|
||||||
|
|
||||||
|
if (params.attenuation <= 10)
|
||||||
|
{
|
||||||
|
attenuation_level = ATTENUATION_LOWER;
|
||||||
|
}
|
||||||
|
else if (params.attenuation <= 15)
|
||||||
|
{
|
||||||
|
attenuation_level = ATTENUATION_10;
|
||||||
|
}
|
||||||
|
else if (params.attenuation <= 27)
|
||||||
|
{
|
||||||
|
attenuation_level = ATTENUATION_15;
|
||||||
|
}
|
||||||
|
else if (params.attenuation <= 33)
|
||||||
|
{
|
||||||
|
attenuation_level = ATTENUATION_27;
|
||||||
|
}
|
||||||
|
else if (params.attenuation <= 51)
|
||||||
|
{
|
||||||
|
attenuation_level = ATTENUATION_33;
|
||||||
|
}
|
||||||
|
else if (params.attenuation <= 63)
|
||||||
|
{
|
||||||
|
attenuation_level = ATTENUATION_51;
|
||||||
|
}
|
||||||
|
else if (params.attenuation <= 73)
|
||||||
|
{
|
||||||
|
attenuation_level = ATTENUATION_63;
|
||||||
|
}
|
||||||
|
|
||||||
|
return config->attenuation_risk_values[attenuation_level];
|
||||||
|
}
|
||||||
|
|
||||||
|
int ena_exposure_risk_score(ena_exposure_config_t *config, ena_exposure_parameter_t params)
|
||||||
|
{
|
||||||
|
int score = 1;
|
||||||
|
score *= ena_exposure_transmission_risk_score(config, params);
|
||||||
|
|
||||||
|
score *= ena_exposure_duration_risk_score(config, params);
|
||||||
|
|
||||||
|
score *= ena_exposure_days_risk_score(config, params);
|
||||||
|
|
||||||
|
score *= ena_exposure_attenuation_risk_score(config, params);
|
||||||
|
|
||||||
|
if (score > 255)
|
||||||
|
{
|
||||||
|
score = 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_exposure_summary(ena_exposure_config_t *config)
|
||||||
|
{
|
||||||
|
uint32_t count = ena_storage_exposure_information_count();
|
||||||
|
uint32_t current_time = (uint32_t)time(NULL);
|
||||||
|
|
||||||
|
if (current_summary == NULL)
|
||||||
|
{
|
||||||
|
current_summary = malloc(sizeof(ena_exposure_summary_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
current_summary->last_update = ena_storage_read_last_exposure_date();
|
||||||
|
current_summary->days_since_last_exposure = INT_MAX;
|
||||||
|
current_summary->max_risk_score = 0;
|
||||||
|
current_summary->risk_score_sum = 0;
|
||||||
|
current_summary->num_exposures = count;
|
||||||
|
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
current_summary->days_since_last_exposure = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ena_exposure_information_t exposure_info;
|
||||||
|
ena_exposure_parameter_t params;
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
ena_storage_get_exposure_information(i, &exposure_info);
|
||||||
|
params.days = (current_time - exposure_info.day) / (60 * 60 * 24); // difference in days
|
||||||
|
if (params.days < current_summary->days_since_last_exposure)
|
||||||
|
{
|
||||||
|
current_summary->days_since_last_exposure = params.days;
|
||||||
|
}
|
||||||
|
params.duration = exposure_info.duration_minutes;
|
||||||
|
params.attenuation = exposure_info.typical_attenuation;
|
||||||
|
int score = ena_exposure_risk_score(config, params);
|
||||||
|
if (score > current_summary->max_risk_score)
|
||||||
|
{
|
||||||
|
current_summary->max_risk_score = score;
|
||||||
|
}
|
||||||
|
current_summary->risk_score_sum += score;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ena_exposure_summary_t *ena_exposure_current_summary(void)
|
||||||
|
{
|
||||||
|
if (current_summary == NULL)
|
||||||
|
{
|
||||||
|
ena_exposure_summary(ena_exposure_default_config());
|
||||||
|
}
|
||||||
|
|
||||||
|
return current_summary;
|
||||||
|
}
|
||||||
|
|
||||||
|
ena_exposure_config_t *ena_exposure_default_config(void)
|
||||||
|
{
|
||||||
|
return &DEFAULT_ENA_EXPOSURE_CONFIG;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_exposure_check(ena_beacon_t beacon, ena_temporary_exposure_key_t temporary_exposure_key)
|
||||||
|
{
|
||||||
|
uint32_t timestamp_day_start = temporary_exposure_key.rolling_start_interval_number * ENA_TIME_WINDOW;
|
||||||
|
uint32_t timestamp_day_end = temporary_exposure_key.rolling_start_interval_number * ENA_TIME_WINDOW + temporary_exposure_key.rolling_period * ENA_TIME_WINDOW;
|
||||||
|
|
||||||
|
if (beacon.timestamp_first > timestamp_day_start && beacon.timestamp_last < timestamp_day_end)
|
||||||
|
{
|
||||||
|
ESP_LOGD(ENA_EXPOSURE_LOG, "matched timestamps!");
|
||||||
|
|
||||||
|
ESP_LOGD(ENA_EXPOSURE_LOG, "Beacon: %u,%u,%d", beacon.timestamp_first, beacon.timestamp_last, beacon.rssi);
|
||||||
|
ESP_LOG_BUFFER_HEXDUMP(ENA_EXPOSURE_LOG, beacon.rpi, ENA_KEY_LENGTH, ESP_LOG_DEBUG);
|
||||||
|
ESP_LOG_BUFFER_HEXDUMP(ENA_EXPOSURE_LOG, beacon.aem, ENA_AEM_METADATA_LENGTH, ESP_LOG_DEBUG);
|
||||||
|
|
||||||
|
ESP_LOGD(ENA_EXPOSURE_LOG, "Key: %u,%u,%d", timestamp_day_start, timestamp_day_end, temporary_exposure_key.rolling_period);
|
||||||
|
ESP_LOG_BUFFER_HEXDUMP(ENA_EXPOSURE_LOG, temporary_exposure_key.key_data, ENA_KEY_LENGTH, ESP_LOG_DEBUG);
|
||||||
|
bool match = false;
|
||||||
|
ena_exposure_information_t exposure_info;
|
||||||
|
exposure_info.duration_minutes = 0;
|
||||||
|
exposure_info.min_attenuation = INT_MAX;
|
||||||
|
exposure_info.typical_attenuation = 0;
|
||||||
|
exposure_info.report_type = temporary_exposure_key.report_type;
|
||||||
|
uint8_t rpi[ENA_KEY_LENGTH];
|
||||||
|
uint8_t rpik[ENA_KEY_LENGTH];
|
||||||
|
ena_crypto_rpik(rpik, temporary_exposure_key.key_data);
|
||||||
|
|
||||||
|
for (int i = 0; i < temporary_exposure_key.rolling_period; i++)
|
||||||
|
{
|
||||||
|
ena_crypto_rpi(rpi, rpik, temporary_exposure_key.rolling_start_interval_number + i);
|
||||||
|
if (memcmp(beacon.rpi, rpi, sizeof(ENA_KEY_LENGTH)) == 0)
|
||||||
|
{
|
||||||
|
match = true;
|
||||||
|
exposure_info.day = timestamp_day_start;
|
||||||
|
exposure_info.duration_minutes += (ENA_BEACON_TRESHOLD / 60);
|
||||||
|
exposure_info.typical_attenuation = (exposure_info.typical_attenuation + beacon.rssi) / 2;
|
||||||
|
if (beacon.rssi < exposure_info.min_attenuation)
|
||||||
|
{
|
||||||
|
exposure_info.min_attenuation = beacon.rssi;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (match)
|
||||||
|
{
|
||||||
|
ena_storage_add_exposure_information(&exposure_info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_exposure_check_temporary_exposure_key(ena_temporary_exposure_key_t temporary_exposure_key)
|
||||||
|
{
|
||||||
|
ena_beacon_t beacon;
|
||||||
|
uint32_t beacons_count = ena_storage_beacons_count();
|
||||||
|
for (int y = 0; y < beacons_count; y++)
|
||||||
|
{
|
||||||
|
ena_storage_get_beacon(y, &beacon);
|
||||||
|
ena_exposure_check(beacon, temporary_exposure_key);
|
||||||
|
}
|
||||||
|
}
|
504
components/ena/ena-storage.c
Normal file
504
components/ena/ena-storage.c
Normal file
@ -0,0 +1,504 @@
|
|||||||
|
// Copyright 2020 Lukas Haubaum
|
||||||
|
//
|
||||||
|
// Licensed under the GNU Affero General Public License, Version 3;
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// https://www.gnu.org/licenses/agpl-3.0.html
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#include <string.h>
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_partition.h"
|
||||||
|
|
||||||
|
#include "ena-storage.h"
|
||||||
|
#include "ena-crypto.h"
|
||||||
|
|
||||||
|
#define BLOCK_SIZE (4096)
|
||||||
|
|
||||||
|
const int ENA_STORAGE_LAST_EXPOSURE_DATE_ADDRESS = (ENA_STORAGE_START_ADDRESS);
|
||||||
|
const int ENA_STORAGE_TEK_COUNT_ADDRESS = (ENA_STORAGE_LAST_EXPOSURE_DATE_ADDRESS + sizeof(uint32_t));
|
||||||
|
const int ENA_STORAGE_TEK_START_ADDRESS = (ENA_STORAGE_TEK_COUNT_ADDRESS + sizeof(uint32_t));
|
||||||
|
const int ENA_STORAGE_EXPOSURE_INFORMATION_COUNT_ADDRESS = (ENA_STORAGE_TEK_START_ADDRESS + sizeof(ena_tek_t) * ENA_STORAGE_TEK_MAX);
|
||||||
|
const int ENA_STORAGE_EXPOSURE_INFORMATION_START_ADDRESS = (ENA_STORAGE_EXPOSURE_INFORMATION_COUNT_ADDRESS + sizeof(uint32_t));
|
||||||
|
const int ENA_STORAGE_TEMP_BEACONS_COUNT_ADDRESS = (ENA_STORAGE_EXPOSURE_INFORMATION_COUNT_ADDRESS + sizeof(ena_exposure_information_t) * ENA_STORAGE_EXPOSURE_INFORMATION_MAX);
|
||||||
|
const int ENA_STORAGE_TEMP_BEACONS_START_ADDRESS = (ENA_STORAGE_TEMP_BEACONS_COUNT_ADDRESS + sizeof(uint32_t));
|
||||||
|
const int ENA_STORAGE_BEACONS_COUNT_ADDRESS = (ENA_STORAGE_TEMP_BEACONS_START_ADDRESS + sizeof(ena_beacon_t) * ENA_STORAGE_TEMP_BEACONS_MAX);
|
||||||
|
const int ENA_STORAGE_BEACONS_START_ADDRESS = (ENA_STORAGE_BEACONS_COUNT_ADDRESS + sizeof(uint32_t));
|
||||||
|
|
||||||
|
void ena_storage_read(size_t address, void *data, size_t size)
|
||||||
|
{
|
||||||
|
const esp_partition_t *partition = esp_partition_find_first(
|
||||||
|
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, ENA_STORAGE_PARTITION_NAME);
|
||||||
|
assert(partition);
|
||||||
|
ESP_ERROR_CHECK(esp_partition_read(partition, address, data, size));
|
||||||
|
vTaskDelay(1);
|
||||||
|
ESP_LOGD(ENA_STORAGE_LOG, "read data at %u", address);
|
||||||
|
ESP_LOG_BUFFER_HEXDUMP(ENA_STORAGE_LOG, data, size, ESP_LOG_DEBUG);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_storage_write(size_t address, void *data, size_t size)
|
||||||
|
{
|
||||||
|
const int block_num = address / BLOCK_SIZE;
|
||||||
|
// check for overflow
|
||||||
|
if (address + size <= (block_num + 1) * BLOCK_SIZE)
|
||||||
|
{
|
||||||
|
const esp_partition_t *partition = esp_partition_find_first(
|
||||||
|
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, ENA_STORAGE_PARTITION_NAME);
|
||||||
|
assert(partition);
|
||||||
|
const int block_start = block_num * BLOCK_SIZE;
|
||||||
|
const int block_address = address - block_start;
|
||||||
|
void *buffer = malloc(BLOCK_SIZE);
|
||||||
|
if (buffer == NULL)
|
||||||
|
{
|
||||||
|
ESP_LOGE(ENA_STORAGE_LOG, "Warning %s malloc low memory", "buffer");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ESP_LOGD(ENA_STORAGE_LOG, "read block %d buffer: start %d size %u", block_num, block_start, BLOCK_SIZE);
|
||||||
|
ESP_ERROR_CHECK(esp_partition_read(partition, block_start, buffer, BLOCK_SIZE));
|
||||||
|
vTaskDelay(1);
|
||||||
|
ESP_ERROR_CHECK(esp_partition_erase_range(partition, block_start, BLOCK_SIZE));
|
||||||
|
|
||||||
|
memcpy((buffer + block_address), data, size);
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(esp_partition_write(partition, block_start, buffer, BLOCK_SIZE));
|
||||||
|
free(buffer);
|
||||||
|
ESP_LOGD(ENA_STORAGE_LOG, "write data at %u", address);
|
||||||
|
ESP_LOG_BUFFER_HEXDUMP(ENA_STORAGE_LOG, data, size, ESP_LOG_DEBUG);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ESP_LOGD(ENA_STORAGE_LOG, "overflow block at address %u with size %d (block %d)", address, size, block_num);
|
||||||
|
const size_t block2_address = (block_num + 1) * BLOCK_SIZE;
|
||||||
|
const size_t data2_size = address + size - block2_address;
|
||||||
|
const size_t data1_size = size - data2_size;
|
||||||
|
ESP_LOGD(ENA_STORAGE_LOG, "block1_address %d, block1_size %d (block %d)", address, data1_size, block_num);
|
||||||
|
ESP_LOGD(ENA_STORAGE_LOG, "block2_address %d, block2_size %d (block %d)", block2_address, data2_size, block_num + 1);
|
||||||
|
void *data1 = malloc(data1_size);
|
||||||
|
memcpy(data1, data, data1_size);
|
||||||
|
ena_storage_write(address, data1, data1_size);
|
||||||
|
free(data1);
|
||||||
|
|
||||||
|
void *data2 = malloc(data2_size);
|
||||||
|
memcpy(data2, (data + data1_size), data2_size);
|
||||||
|
ena_storage_write(block2_address, data2, data2_size);
|
||||||
|
free(data2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_storage_erase(size_t address, size_t size)
|
||||||
|
{
|
||||||
|
const int block_num = address / BLOCK_SIZE;
|
||||||
|
// check for overflow
|
||||||
|
if (address + size <= (block_num + 1) * BLOCK_SIZE)
|
||||||
|
{
|
||||||
|
uint8_t *zeros = calloc(size, sizeof(uint8_t));
|
||||||
|
ena_storage_write(address, zeros, size);
|
||||||
|
free(zeros);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const size_t block2_address = (block_num + 1) * BLOCK_SIZE;
|
||||||
|
const size_t data2_size = address + size - block2_address;
|
||||||
|
const size_t data1_size = size - data2_size;
|
||||||
|
uint8_t *zeros = calloc(data1_size, sizeof(uint8_t));
|
||||||
|
ena_storage_write(address, zeros, data1_size);
|
||||||
|
free(zeros);
|
||||||
|
ena_storage_erase(block2_address, data2_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_storage_shift_delete(size_t address, size_t end_address, size_t size)
|
||||||
|
{
|
||||||
|
int block_num_start = address / BLOCK_SIZE;
|
||||||
|
// check for overflow
|
||||||
|
if (address + size <= (block_num_start + 1) * BLOCK_SIZE)
|
||||||
|
{
|
||||||
|
const esp_partition_t *partition = esp_partition_find_first(
|
||||||
|
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, ENA_STORAGE_PARTITION_NAME);
|
||||||
|
assert(partition);
|
||||||
|
|
||||||
|
int block_num_end = end_address / BLOCK_SIZE;
|
||||||
|
size_t block_start = address - block_num_start * BLOCK_SIZE;
|
||||||
|
while (block_num_end >= block_num_start)
|
||||||
|
{
|
||||||
|
|
||||||
|
void *buffer = malloc(BLOCK_SIZE);
|
||||||
|
ESP_ERROR_CHECK(esp_partition_read(partition, block_num_start * BLOCK_SIZE, buffer, BLOCK_SIZE));
|
||||||
|
vTaskDelay(1);
|
||||||
|
// shift inside buffer
|
||||||
|
ESP_LOGD(ENA_STORAGE_LOG, "shift block %d from %u to %u with size %u", block_num_start, (block_start + size), block_start, (BLOCK_SIZE - block_start - size));
|
||||||
|
memcpy((buffer + block_start), (buffer + block_start + size), BLOCK_SIZE - block_start - size);
|
||||||
|
if (block_num_end > block_num_start)
|
||||||
|
{
|
||||||
|
void *buffer_next_block = malloc(BLOCK_SIZE);
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(esp_partition_read(partition, (block_num_start + 1) * BLOCK_SIZE, buffer_next_block, BLOCK_SIZE));
|
||||||
|
vTaskDelay(1);
|
||||||
|
// shift from next block
|
||||||
|
ESP_LOGD(ENA_STORAGE_LOG, "shift next block size %u", size);
|
||||||
|
memcpy((buffer + BLOCK_SIZE - size), buffer_next_block, size);
|
||||||
|
free(buffer_next_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(esp_partition_erase_range(partition, block_num_start * BLOCK_SIZE, BLOCK_SIZE));
|
||||||
|
ESP_ERROR_CHECK(esp_partition_write(partition, block_num_start * BLOCK_SIZE, buffer, BLOCK_SIZE));
|
||||||
|
free(buffer);
|
||||||
|
|
||||||
|
block_num_start++;
|
||||||
|
block_start = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ESP_LOGD(ENA_STORAGE_LOG, "overflow block at address %u with size %d (block %d)", address, size, block_num_start);
|
||||||
|
const size_t block1_address = address;
|
||||||
|
const size_t block2_address = (block_num_start + 1) * BLOCK_SIZE;
|
||||||
|
const size_t data2_size = address + size - block2_address;
|
||||||
|
const size_t data1_size = size - data2_size;
|
||||||
|
ena_storage_shift_delete(block1_address, block2_address, data1_size);
|
||||||
|
ena_storage_shift_delete(block2_address, end_address - data1_size, data2_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t ena_storage_read_last_exposure_date(void)
|
||||||
|
{
|
||||||
|
uint32_t timestamp = 0;
|
||||||
|
ena_storage_read(ENA_STORAGE_LAST_EXPOSURE_DATE_ADDRESS, ×tamp, sizeof(uint32_t));
|
||||||
|
return timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_storage_write_last_exposure_date(uint32_t timestamp)
|
||||||
|
{
|
||||||
|
ena_storage_write(ENA_STORAGE_LAST_EXPOSURE_DATE_ADDRESS, ×tamp, sizeof(uint32_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t ena_storage_read_last_tek(ena_tek_t *tek)
|
||||||
|
{
|
||||||
|
uint32_t tek_count = 0;
|
||||||
|
ena_storage_read(ENA_STORAGE_TEK_COUNT_ADDRESS, &tek_count, sizeof(uint32_t));
|
||||||
|
if (tek_count < 1)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
uint8_t index = (tek_count % ENA_STORAGE_TEK_MAX) - 1;
|
||||||
|
ena_storage_read(ENA_STORAGE_TEK_START_ADDRESS + index * sizeof(ena_tek_t), tek, sizeof(ena_tek_t));
|
||||||
|
|
||||||
|
ESP_LOGD(ENA_STORAGE_LOG, "read last tek %u:", tek->enin);
|
||||||
|
ESP_LOG_BUFFER_HEXDUMP(ENA_STORAGE_LOG, tek->key_data, ENA_KEY_LENGTH, ESP_LOG_DEBUG);
|
||||||
|
return tek_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_storage_write_tek(ena_tek_t *tek)
|
||||||
|
{
|
||||||
|
uint32_t tek_count = 0;
|
||||||
|
ena_storage_read(ENA_STORAGE_TEK_COUNT_ADDRESS, &tek_count, sizeof(uint32_t));
|
||||||
|
uint8_t index = (tek_count % ENA_STORAGE_TEK_MAX);
|
||||||
|
ena_storage_write(ENA_STORAGE_TEK_START_ADDRESS + index * sizeof(ena_tek_t), tek, sizeof(ena_tek_t));
|
||||||
|
|
||||||
|
tek_count++;
|
||||||
|
ena_storage_write(ENA_STORAGE_TEK_COUNT_ADDRESS, &tek_count, sizeof(uint32_t));
|
||||||
|
|
||||||
|
ESP_LOGD(ENA_STORAGE_LOG, "write tek: ENIN %u", tek->enin);
|
||||||
|
ESP_LOG_BUFFER_HEXDUMP(ENA_STORAGE_LOG, tek->key_data, ENA_KEY_LENGTH, ESP_LOG_DEBUG);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t ena_storage_exposure_information_count(void)
|
||||||
|
{
|
||||||
|
uint32_t count = 0;
|
||||||
|
ena_storage_read(ENA_STORAGE_EXPOSURE_INFORMATION_COUNT_ADDRESS, &count, sizeof(uint32_t));
|
||||||
|
ESP_LOGD(ENA_STORAGE_LOG, "read exposure information count: %u", count);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_storage_get_exposure_information(uint32_t index, ena_exposure_information_t *exposure_info)
|
||||||
|
{
|
||||||
|
ena_storage_read(ENA_STORAGE_EXPOSURE_INFORMATION_START_ADDRESS + index * sizeof(ena_exposure_information_t), exposure_info, sizeof(ena_exposure_information_t));
|
||||||
|
ESP_LOGD(ENA_STORAGE_LOG, "read exporuse information: day %u, duration %d", exposure_info->day, exposure_info->duration_minutes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_storage_add_exposure_information(ena_exposure_information_t *exposure_info)
|
||||||
|
{
|
||||||
|
uint32_t count = ena_storage_exposure_information_count();
|
||||||
|
ena_storage_write(ENA_STORAGE_EXPOSURE_INFORMATION_START_ADDRESS + count * sizeof(ena_exposure_information_t), exposure_info, sizeof(ena_exposure_information_t));
|
||||||
|
count++;
|
||||||
|
ena_storage_write(ENA_STORAGE_EXPOSURE_INFORMATION_COUNT_ADDRESS, &count, sizeof(uint32_t));
|
||||||
|
ESP_LOGD(ENA_STORAGE_LOG, "write exposure info: day %u, duration %d", exposure_info->day, exposure_info->duration_minutes);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t ena_storage_temp_beacons_count(void)
|
||||||
|
{
|
||||||
|
uint32_t count = 0;
|
||||||
|
ena_storage_read(ENA_STORAGE_TEMP_BEACONS_COUNT_ADDRESS, &count, sizeof(uint32_t));
|
||||||
|
ESP_LOGD(ENA_STORAGE_LOG, "read temp beacons count: %u", count);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_storage_get_temp_beacon(uint32_t index, ena_beacon_t *beacon)
|
||||||
|
{
|
||||||
|
ena_storage_read(ENA_STORAGE_TEMP_BEACONS_START_ADDRESS + index * sizeof(ena_beacon_t), beacon, sizeof(ena_beacon_t));
|
||||||
|
ESP_LOGD(ENA_STORAGE_LOG, "read temp beacon: first %u, last %u and rssi %d", beacon->timestamp_first, beacon->timestamp_last, beacon->rssi);
|
||||||
|
ESP_LOG_BUFFER_HEXDUMP(ENA_STORAGE_LOG, beacon->rpi, ENA_KEY_LENGTH, ESP_LOG_DEBUG);
|
||||||
|
ESP_LOG_BUFFER_HEXDUMP(ENA_STORAGE_LOG, beacon->aem, ENA_AEM_METADATA_LENGTH, ESP_LOG_DEBUG);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t ena_storage_add_temp_beacon(ena_beacon_t *beacon)
|
||||||
|
{
|
||||||
|
uint32_t count = ena_storage_temp_beacons_count();
|
||||||
|
// overwrite older temporary beacons?!
|
||||||
|
uint8_t index = count % ENA_STORAGE_TEMP_BEACONS_MAX;
|
||||||
|
ena_storage_set_temp_beacon(index, beacon);
|
||||||
|
ESP_LOGD(ENA_STORAGE_LOG, "add temp beacon at %u: first %u, last %u and rssi %d", index, beacon->timestamp_first, beacon->timestamp_last, beacon->rssi);
|
||||||
|
ESP_LOG_BUFFER_HEXDUMP(ENA_STORAGE_LOG, beacon->rpi, ENA_KEY_LENGTH, ESP_LOG_DEBUG);
|
||||||
|
ESP_LOG_BUFFER_HEXDUMP(ENA_STORAGE_LOG, beacon->aem, ENA_AEM_METADATA_LENGTH, ESP_LOG_DEBUG);
|
||||||
|
count++;
|
||||||
|
ena_storage_write(ENA_STORAGE_TEMP_BEACONS_COUNT_ADDRESS, &count, sizeof(uint32_t));
|
||||||
|
return count - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_storage_set_temp_beacon(uint32_t index, ena_beacon_t *beacon)
|
||||||
|
{
|
||||||
|
ena_storage_write(ENA_STORAGE_TEMP_BEACONS_START_ADDRESS + index * sizeof(ena_beacon_t), beacon, sizeof(ena_beacon_t));
|
||||||
|
ESP_LOGD(ENA_STORAGE_LOG, "set temp beacon at %u: first %u, last %u and rssi %d", index, beacon->timestamp_first, beacon->timestamp_last, beacon->rssi);
|
||||||
|
ESP_LOG_BUFFER_HEXDUMP(ENA_STORAGE_LOG, beacon->rpi, ENA_KEY_LENGTH, ESP_LOG_DEBUG);
|
||||||
|
ESP_LOG_BUFFER_HEXDUMP(ENA_STORAGE_LOG, beacon->aem, ENA_AEM_METADATA_LENGTH, ESP_LOG_DEBUG);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_storage_remove_temp_beacon(uint32_t index)
|
||||||
|
{
|
||||||
|
uint32_t count = ena_storage_temp_beacons_count();
|
||||||
|
size_t address_from = ENA_STORAGE_TEMP_BEACONS_START_ADDRESS + index * sizeof(ena_beacon_t);
|
||||||
|
size_t address_to = ENA_STORAGE_TEMP_BEACONS_START_ADDRESS + count * sizeof(ena_beacon_t);
|
||||||
|
|
||||||
|
ena_storage_shift_delete(address_from, address_to, sizeof(ena_beacon_t));
|
||||||
|
|
||||||
|
count--;
|
||||||
|
ena_storage_write(ENA_STORAGE_TEMP_BEACONS_COUNT_ADDRESS, &count, sizeof(uint32_t));
|
||||||
|
ESP_LOGD(ENA_STORAGE_LOG, "remove temp beacon: %u", index);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t ena_storage_beacons_count(void)
|
||||||
|
{
|
||||||
|
uint32_t count = 0;
|
||||||
|
ena_storage_read(ENA_STORAGE_BEACONS_COUNT_ADDRESS, &count, sizeof(uint32_t));
|
||||||
|
ESP_LOGD(ENA_STORAGE_LOG, "read contancts count: %u", count);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_storage_get_beacon(uint32_t index, ena_beacon_t *beacon)
|
||||||
|
{
|
||||||
|
ena_storage_read(ENA_STORAGE_BEACONS_START_ADDRESS + index * sizeof(ena_beacon_t), beacon, sizeof(ena_beacon_t));
|
||||||
|
ESP_LOGD(ENA_STORAGE_LOG, "read beacon: first %u, last %u and rssi %d", beacon->timestamp_first, beacon->timestamp_last, beacon->rssi);
|
||||||
|
ESP_LOG_BUFFER_HEXDUMP(ENA_STORAGE_LOG, beacon->rpi, ENA_KEY_LENGTH, ESP_LOG_DEBUG);
|
||||||
|
ESP_LOG_BUFFER_HEXDUMP(ENA_STORAGE_LOG, beacon->aem, ENA_AEM_METADATA_LENGTH, ESP_LOG_DEBUG);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_storage_add_beacon(ena_beacon_t *beacon)
|
||||||
|
{
|
||||||
|
ESP_LOG_BUFFER_HEXDUMP(ENA_STORAGE_LOG, beacon->rpi, ENA_KEY_LENGTH, ESP_LOG_DEBUG);
|
||||||
|
uint32_t count = ena_storage_beacons_count();
|
||||||
|
ena_storage_write(ENA_STORAGE_BEACONS_START_ADDRESS + count * sizeof(ena_beacon_t), beacon, sizeof(ena_beacon_t));
|
||||||
|
count++;
|
||||||
|
ena_storage_write(ENA_STORAGE_BEACONS_COUNT_ADDRESS, &count, sizeof(uint32_t));
|
||||||
|
ESP_LOGD(ENA_STORAGE_LOG, "write beacon: first %u, last %u and rssi %d", beacon->timestamp_first, beacon->timestamp_last, beacon->rssi);
|
||||||
|
ESP_LOG_BUFFER_HEXDUMP(ENA_STORAGE_LOG, beacon->rpi, ENA_KEY_LENGTH, ESP_LOG_DEBUG);
|
||||||
|
ESP_LOG_BUFFER_HEXDUMP(ENA_STORAGE_LOG, beacon->aem, ENA_AEM_METADATA_LENGTH, ESP_LOG_DEBUG);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_storage_remove_beacon(uint32_t index)
|
||||||
|
{
|
||||||
|
uint32_t count = ena_storage_beacons_count();
|
||||||
|
size_t address_from = ENA_STORAGE_BEACONS_START_ADDRESS + index * sizeof(ena_beacon_t);
|
||||||
|
size_t address_to = ENA_STORAGE_BEACONS_START_ADDRESS + count * sizeof(ena_beacon_t);
|
||||||
|
|
||||||
|
ena_storage_shift_delete(address_from, address_to, sizeof(ena_beacon_t));
|
||||||
|
|
||||||
|
count--;
|
||||||
|
ena_storage_write(ENA_STORAGE_BEACONS_COUNT_ADDRESS, &count, sizeof(uint32_t));
|
||||||
|
ESP_LOGD(ENA_STORAGE_LOG, "remove beacon: %u", index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_storage_erase_all(void)
|
||||||
|
{
|
||||||
|
const esp_partition_t *partition = esp_partition_find_first(
|
||||||
|
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, ENA_STORAGE_PARTITION_NAME);
|
||||||
|
assert(partition);
|
||||||
|
ESP_ERROR_CHECK(esp_partition_erase_range(partition, 0, partition->size));
|
||||||
|
ESP_LOGI(ENA_STORAGE_LOG, "erased partition %s!", ENA_STORAGE_PARTITION_NAME);
|
||||||
|
|
||||||
|
uint32_t count = 0;
|
||||||
|
ena_storage_write(ENA_STORAGE_LAST_EXPOSURE_DATE_ADDRESS, &count, sizeof(uint32_t));
|
||||||
|
ena_storage_write(ENA_STORAGE_TEK_COUNT_ADDRESS, &count, sizeof(uint32_t));
|
||||||
|
ena_storage_write(ENA_STORAGE_EXPOSURE_INFORMATION_COUNT_ADDRESS, &count, sizeof(uint32_t));
|
||||||
|
ena_storage_write(ENA_STORAGE_TEMP_BEACONS_COUNT_ADDRESS, &count, sizeof(uint32_t));
|
||||||
|
ena_storage_write(ENA_STORAGE_BEACONS_COUNT_ADDRESS, &count, sizeof(uint32_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_storage_erase_tek(void)
|
||||||
|
{
|
||||||
|
uint32_t count = 0;
|
||||||
|
ena_storage_read(ENA_STORAGE_TEK_COUNT_ADDRESS, &count, sizeof(uint32_t));
|
||||||
|
uint32_t stored = ENA_STORAGE_TEK_MAX;
|
||||||
|
|
||||||
|
if (count < ENA_STORAGE_TEK_MAX)
|
||||||
|
{
|
||||||
|
stored = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size = sizeof(uint32_t) + stored * sizeof(ena_tek_t);
|
||||||
|
ena_storage_erase(ENA_STORAGE_TEK_COUNT_ADDRESS, size);
|
||||||
|
ESP_LOGI(ENA_STORAGE_LOG, "erased %d teks (size %u at %u)", stored, size, ENA_STORAGE_TEK_COUNT_ADDRESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_storage_erase_exposure_information(void)
|
||||||
|
{
|
||||||
|
uint32_t count = ena_storage_exposure_information_count();
|
||||||
|
uint32_t stored = ENA_STORAGE_EXPOSURE_INFORMATION_MAX;
|
||||||
|
|
||||||
|
if (count < ENA_STORAGE_EXPOSURE_INFORMATION_MAX)
|
||||||
|
{
|
||||||
|
stored = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size = sizeof(uint32_t) + stored * sizeof(ena_exposure_information_t);
|
||||||
|
ena_storage_erase(ENA_STORAGE_EXPOSURE_INFORMATION_COUNT_ADDRESS, size);
|
||||||
|
ESP_LOGI(ENA_STORAGE_LOG, "erased %d exposure information (size %u at %u)", stored, size, ENA_STORAGE_EXPOSURE_INFORMATION_COUNT_ADDRESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_storage_erase_temporary_beacon(void)
|
||||||
|
{
|
||||||
|
uint32_t beacon_count = 0;
|
||||||
|
ena_storage_read(ENA_STORAGE_TEMP_BEACONS_COUNT_ADDRESS, &beacon_count, sizeof(uint32_t));
|
||||||
|
uint32_t stored = ENA_STORAGE_TEMP_BEACONS_MAX;
|
||||||
|
|
||||||
|
if (beacon_count < ENA_STORAGE_TEMP_BEACONS_MAX)
|
||||||
|
{
|
||||||
|
stored = beacon_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size = sizeof(uint32_t) + stored * sizeof(ena_beacon_t);
|
||||||
|
ena_storage_erase(ENA_STORAGE_TEMP_BEACONS_COUNT_ADDRESS, size);
|
||||||
|
|
||||||
|
ESP_LOGI(ENA_STORAGE_LOG, "erased %d temporary beacons (size %u at %u)", stored, size, ENA_STORAGE_TEMP_BEACONS_COUNT_ADDRESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_storage_erase_beacon(void)
|
||||||
|
{
|
||||||
|
uint32_t beacon_count = 0;
|
||||||
|
ena_storage_read(ENA_STORAGE_BEACONS_COUNT_ADDRESS, &beacon_count, sizeof(uint32_t));
|
||||||
|
|
||||||
|
size_t size = sizeof(uint32_t) + beacon_count * sizeof(ena_beacon_t);
|
||||||
|
ena_storage_erase(ENA_STORAGE_BEACONS_COUNT_ADDRESS, size);
|
||||||
|
ESP_LOGI(ENA_STORAGE_LOG, "erased %d beacons (size %u at %u)", beacon_count, size, ENA_STORAGE_BEACONS_COUNT_ADDRESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_storage_dump_hash_array(uint8_t *data, size_t size)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < size; i++)
|
||||||
|
{
|
||||||
|
if (i == 0)
|
||||||
|
{
|
||||||
|
printf("%02x", data[i]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf(" %02x", data[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_storage_dump_teks(void)
|
||||||
|
{
|
||||||
|
ena_tek_t tek;
|
||||||
|
uint32_t tek_count = 0;
|
||||||
|
ena_storage_read(ENA_STORAGE_TEK_COUNT_ADDRESS, &tek_count, sizeof(uint32_t));
|
||||||
|
uint32_t stored = ENA_STORAGE_TEK_MAX;
|
||||||
|
|
||||||
|
if (tek_count < ENA_STORAGE_TEK_MAX)
|
||||||
|
{
|
||||||
|
stored = tek_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGD(ENA_STORAGE_LOG, "%u TEKs (%u stored)\n", tek_count, stored);
|
||||||
|
printf("#,enin,tek,rolling_period\n");
|
||||||
|
for (int i = 0; i < stored; i++)
|
||||||
|
{
|
||||||
|
|
||||||
|
size_t address = ENA_STORAGE_TEK_START_ADDRESS + i * sizeof(ena_tek_t);
|
||||||
|
ena_storage_read(address, &tek, sizeof(ena_tek_t));
|
||||||
|
printf("%d,%u,", i, tek.enin);
|
||||||
|
ena_storage_dump_hash_array(tek.key_data, ENA_KEY_LENGTH);
|
||||||
|
printf(",%u\n", tek.rolling_period);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_storage_dump_exposure_information(void)
|
||||||
|
{
|
||||||
|
ena_exposure_information_t exposure_info;
|
||||||
|
uint32_t exposure_information_count = ena_storage_exposure_information_count();
|
||||||
|
uint32_t stored = ENA_STORAGE_EXPOSURE_INFORMATION_MAX;
|
||||||
|
|
||||||
|
if (exposure_information_count < ENA_STORAGE_EXPOSURE_INFORMATION_MAX)
|
||||||
|
{
|
||||||
|
stored = exposure_information_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGD(ENA_STORAGE_LOG, "%u exposure information (%u stored)\n", exposure_information_count, stored);
|
||||||
|
printf("#,day,typical_attenuation,min_attenuation,duration_minutes,report_type\n");
|
||||||
|
for (int i = 0; i < stored; i++)
|
||||||
|
{
|
||||||
|
|
||||||
|
size_t address = ENA_STORAGE_EXPOSURE_INFORMATION_START_ADDRESS + i * sizeof(ena_exposure_information_t);
|
||||||
|
ena_storage_read(address, &exposure_info, sizeof(ena_exposure_information_t));
|
||||||
|
printf("%d,%u,%d,%d,%d,%d\n", i, exposure_info.day, exposure_info.typical_attenuation, exposure_info.min_attenuation, exposure_info.duration_minutes, exposure_info.report_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_storage_dump_temp_beacons(void)
|
||||||
|
{
|
||||||
|
ena_beacon_t beacon;
|
||||||
|
uint32_t beacon_count = 0;
|
||||||
|
ena_storage_read(ENA_STORAGE_TEMP_BEACONS_COUNT_ADDRESS, &beacon_count, sizeof(uint32_t));
|
||||||
|
uint32_t stored = ENA_STORAGE_TEMP_BEACONS_MAX;
|
||||||
|
|
||||||
|
if (beacon_count < ENA_STORAGE_TEMP_BEACONS_MAX)
|
||||||
|
{
|
||||||
|
stored = beacon_count;
|
||||||
|
}
|
||||||
|
ESP_LOGD(ENA_STORAGE_LOG, "%u temporary beacons (%u stored)\n", beacon_count, stored);
|
||||||
|
printf("#,timestamp_first,timestamp_last,rpi,aem,rssi\n");
|
||||||
|
for (int i = 0; i < stored; i++)
|
||||||
|
{
|
||||||
|
ena_storage_get_temp_beacon(i, &beacon);
|
||||||
|
printf("%d,%u,%u,", i, beacon.timestamp_first, beacon.timestamp_last);
|
||||||
|
ena_storage_dump_hash_array(beacon.rpi, ENA_KEY_LENGTH);
|
||||||
|
printf(",");
|
||||||
|
ena_storage_dump_hash_array(beacon.aem, ENA_AEM_METADATA_LENGTH);
|
||||||
|
printf(",%d\n", beacon.rssi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_storage_dump_beacons(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
ena_beacon_t beacon;
|
||||||
|
uint32_t beacon_count = 0;
|
||||||
|
ena_storage_read(ENA_STORAGE_BEACONS_COUNT_ADDRESS, &beacon_count, sizeof(uint32_t));
|
||||||
|
ESP_LOGD(ENA_STORAGE_LOG, "%u beacons\n", beacon_count);
|
||||||
|
printf("#,timestamp_first,timestamp_last,rpi,aem,rssi\n");
|
||||||
|
for (int i = 0; i < beacon_count; i++)
|
||||||
|
{
|
||||||
|
ena_storage_get_beacon(i, &beacon);
|
||||||
|
printf("%d,%u,%u,", i, beacon.timestamp_first, beacon.timestamp_last);
|
||||||
|
ena_storage_dump_hash_array(beacon.rpi, ENA_KEY_LENGTH);
|
||||||
|
printf(",");
|
||||||
|
ena_storage_dump_hash_array(beacon.aem, ENA_AEM_METADATA_LENGTH);
|
||||||
|
printf(",%d\n", beacon.rssi);
|
||||||
|
}
|
||||||
|
}
|
185
components/ena/ena.c
Normal file
185
components/ena/ena.c
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
// Copyright 2020 Lukas Haubaum
|
||||||
|
//
|
||||||
|
// Licensed under the GNU Affero General Public License, Version 3;
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// https://www.gnu.org/licenses/agpl-3.0.html
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "esp_system.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_bt.h"
|
||||||
|
#include "esp_bt_main.h"
|
||||||
|
#include "esp_gap_ble_api.h"
|
||||||
|
|
||||||
|
#include "nvs_flash.h"
|
||||||
|
|
||||||
|
#include "ena-crypto.h"
|
||||||
|
#include "ena-storage.h"
|
||||||
|
#include "ena-bluetooth-scan.h"
|
||||||
|
#include "ena-bluetooth-advertise.h"
|
||||||
|
#include "ena-beacons.h"
|
||||||
|
|
||||||
|
#include "ena.h"
|
||||||
|
|
||||||
|
static ena_tek_t last_tek; // last ENIN
|
||||||
|
static uint32_t next_rpi_timestamp; // next rpi
|
||||||
|
|
||||||
|
void ena_next_rpi_timestamp(uint32_t timestamp)
|
||||||
|
{
|
||||||
|
int random_interval = esp_random() % (2 * ENA_BT_RANDOMIZE_ROTATION_TIMEOUT_INTERVAL);
|
||||||
|
if (random_interval > ENA_BT_RANDOMIZE_ROTATION_TIMEOUT_INTERVAL)
|
||||||
|
{
|
||||||
|
random_interval = ENA_BT_RANDOMIZE_ROTATION_TIMEOUT_INTERVAL - random_interval;
|
||||||
|
}
|
||||||
|
next_rpi_timestamp = timestamp + ENA_BT_ROTATION_TIMEOUT_INTERVAL + random_interval;
|
||||||
|
ESP_LOGD(ENA_LOG, "next rpi at %u (%u from %u)", next_rpi_timestamp, (ENA_BT_ROTATION_TIMEOUT_INTERVAL + random_interval), timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_run(void)
|
||||||
|
{
|
||||||
|
static uint32_t unix_timestamp = 0;
|
||||||
|
static uint32_t current_enin = 0;
|
||||||
|
unix_timestamp = (uint32_t)time(NULL);
|
||||||
|
current_enin = ena_crypto_enin(unix_timestamp);
|
||||||
|
if (current_enin - last_tek.enin >= last_tek.rolling_period)
|
||||||
|
{
|
||||||
|
ena_crypto_tek(last_tek.key_data);
|
||||||
|
last_tek.enin = current_enin;
|
||||||
|
// validity only to next day 00:00
|
||||||
|
last_tek.rolling_period = ENA_TEK_ROLLING_PERIOD - (last_tek.enin % ENA_TEK_ROLLING_PERIOD);
|
||||||
|
ena_storage_write_tek(&last_tek);
|
||||||
|
// clean up old beacons
|
||||||
|
ena_beacons_cleanup(unix_timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// change RPI
|
||||||
|
if (unix_timestamp >= next_rpi_timestamp)
|
||||||
|
{
|
||||||
|
if (ena_bluetooth_scan_get_status() == ENA_SCAN_STATUS_SCANNING)
|
||||||
|
{
|
||||||
|
ena_bluetooth_scan_stop();
|
||||||
|
}
|
||||||
|
ena_bluetooth_advertise_stop();
|
||||||
|
ena_bluetooth_advertise_set_payload(current_enin, last_tek.key_data);
|
||||||
|
ena_bluetooth_advertise_start();
|
||||||
|
if (ena_bluetooth_scan_get_status() == ENA_SCAN_STATUS_WAITING)
|
||||||
|
{
|
||||||
|
ena_bluetooth_scan_start(ENA_SCANNING_TIME);
|
||||||
|
}
|
||||||
|
ena_next_rpi_timestamp(unix_timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// scan
|
||||||
|
if (unix_timestamp % ENA_SCANNING_INTERVAL == 0 && ena_bluetooth_scan_get_status() == ENA_SCAN_STATUS_NOT_SCANNING)
|
||||||
|
{
|
||||||
|
ena_bluetooth_scan_start(ENA_SCANNING_TIME);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_start(void)
|
||||||
|
{
|
||||||
|
#if (CONFIG_ENA_STORAGE_ERASE)
|
||||||
|
ena_storage_erase_all();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (ena_storage_read_last_exposure_date() == 0xFFFFFFFF)
|
||||||
|
{
|
||||||
|
ena_storage_erase_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
// init NVS for BLE
|
||||||
|
esp_err_t ret;
|
||||||
|
ret = nvs_flash_init();
|
||||||
|
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
|
||||||
|
{
|
||||||
|
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||||
|
ESP_ERROR_CHECK(nvs_flash_init());
|
||||||
|
}
|
||||||
|
|
||||||
|
// init BLE
|
||||||
|
if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE)
|
||||||
|
{
|
||||||
|
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
|
||||||
|
ESP_ERROR_CHECK(esp_bt_controller_init(&bt_cfg));
|
||||||
|
while (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_INITED)
|
||||||
|
{
|
||||||
|
ESP_ERROR_CHECK(esp_bt_controller_enable(ESP_BT_MODE_BLE));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (esp_bluedroid_get_status() == ESP_BLUEDROID_STATUS_UNINITIALIZED)
|
||||||
|
{
|
||||||
|
ESP_ERROR_CHECK(esp_bluedroid_init());
|
||||||
|
}
|
||||||
|
if (esp_bluedroid_get_status() == ESP_BLUEDROID_STATUS_INITIALIZED)
|
||||||
|
{
|
||||||
|
ESP_ERROR_CHECK(esp_bluedroid_enable());
|
||||||
|
}
|
||||||
|
|
||||||
|
// new bluetooth address nesseccary?
|
||||||
|
uint8_t bt_address[ESP_BD_ADDR_LEN];
|
||||||
|
esp_fill_random(bt_address, ESP_BD_ADDR_LEN);
|
||||||
|
bt_address[0] |= 0xC0;
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(esp_ble_gap_set_rand_addr(bt_address));
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_DEFAULT, ESP_PWR_LVL_P9));
|
||||||
|
ESP_ERROR_CHECK(esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_ADV, ESP_PWR_LVL_P9));
|
||||||
|
ESP_ERROR_CHECK(esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_SCAN, ESP_PWR_LVL_P9));
|
||||||
|
ESP_ERROR_CHECK(esp_ble_gap_config_local_privacy(true));
|
||||||
|
|
||||||
|
// init ENA
|
||||||
|
ena_crypto_init();
|
||||||
|
|
||||||
|
uint32_t unix_timestamp = (uint32_t)time(NULL);
|
||||||
|
|
||||||
|
uint32_t current_enin = ena_crypto_enin(unix_timestamp);
|
||||||
|
uint32_t tek_count = ena_storage_read_last_tek(&last_tek);
|
||||||
|
|
||||||
|
ena_next_rpi_timestamp(unix_timestamp);
|
||||||
|
|
||||||
|
// read last TEK or create new
|
||||||
|
if (tek_count == 0 || (current_enin - last_tek.enin) >= last_tek.rolling_period)
|
||||||
|
{
|
||||||
|
ena_crypto_tek(last_tek.key_data);
|
||||||
|
last_tek.enin = ena_crypto_enin(unix_timestamp);
|
||||||
|
// validity only to next day 00:00
|
||||||
|
last_tek.rolling_period = ENA_TEK_ROLLING_PERIOD - (last_tek.enin % ENA_TEK_ROLLING_PERIOD);
|
||||||
|
ena_storage_write_tek(&last_tek);
|
||||||
|
}
|
||||||
|
|
||||||
|
// init scan
|
||||||
|
ena_bluetooth_scan_init();
|
||||||
|
|
||||||
|
// init and start advertising
|
||||||
|
ena_bluetooth_advertise_set_payload(current_enin, last_tek.key_data);
|
||||||
|
ena_bluetooth_advertise_start();
|
||||||
|
// initial scan on every start
|
||||||
|
ena_bluetooth_scan_start(ENA_SCANNING_TIME);
|
||||||
|
|
||||||
|
// what is a good stack size here?
|
||||||
|
// xTaskCreate(&ena_run, "ena_run", ENA_RAM, NULL, 5, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ena_stop(void)
|
||||||
|
{
|
||||||
|
ena_bluetooth_advertise_stop();
|
||||||
|
ena_bluetooth_scan_stop();
|
||||||
|
esp_bluedroid_disable();
|
||||||
|
esp_bluedroid_deinit();
|
||||||
|
esp_bt_controller_disable();
|
||||||
|
esp_bt_controller_deinit();
|
||||||
|
}
|
62
components/ena/include/ena-beacons.h
Normal file
62
components/ena/include/ena-beacons.h
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
// Copyright 2020 Lukas Haubaum
|
||||||
|
//
|
||||||
|
// Licensed under the GNU Affero General Public License, Version 3;
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// https://www.gnu.org/licenses/agpl-3.0.html
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
*
|
||||||
|
* @brief handles scanned data by storing temporary beacons, check for threshold and store beacons permanently
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef _ena_BEACON_H_
|
||||||
|
#define _ena_BEACON_H_
|
||||||
|
|
||||||
|
#define ENA_BEACON_LOG "ESP-ENA-beacon" // TAG for Logging
|
||||||
|
#define ENA_BEACON_TRESHOLD (CONFIG_ENA_BEACON_TRESHOLD) // meet for longer than 5 minutes
|
||||||
|
#define ENA_BEACON_CLEANUP_TRESHOLD (CONFIG_ENA_BEACON_CLEANUP_TRESHOLD) // threshold (in days) for stored beacons to be removed
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief check temporary beacon for threshold or expiring
|
||||||
|
*
|
||||||
|
* This function checks all current temporary beacons if the contact threshold is
|
||||||
|
* reached or if the temporary contact can be discarded.
|
||||||
|
*
|
||||||
|
* @param[in] unix_timestamp current time as UNIX timestamp to compare
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void ena_beacons_temp_refresh(uint32_t unix_timestamp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief check stored beacons to expire
|
||||||
|
*
|
||||||
|
* This function checks for all stored beacons if the last timestamp is over a threshold to remove the beacon.
|
||||||
|
*
|
||||||
|
* @param[in] unix_timestamp current time as UNIX timestamp to compate
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void ena_beacons_cleanup(uint32_t unix_timestamp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief handle new beacon received from a BLE scan
|
||||||
|
*
|
||||||
|
* This function gets called when a running BLE scan received a new ENA payload.
|
||||||
|
* On already detected RPI this will update just the timestamp and RSSI.
|
||||||
|
*
|
||||||
|
* @param[in] unix_timestamp UNIX timestamp when beacon was made
|
||||||
|
* @param[in] rpi received RPI from scanned payload
|
||||||
|
* @param[in] aem received AEM from scanned payload
|
||||||
|
* @param[in] rssi measured RSSI on scan
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void ena_beacon(uint32_t unix_timestamp, uint8_t *rpi, uint8_t *aem, int rssi);
|
||||||
|
|
||||||
|
#endif
|
52
components/ena/include/ena-bluetooth-advertise.h
Normal file
52
components/ena/include/ena-bluetooth-advertise.h
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// Copyright 2020 Lukas Haubaum
|
||||||
|
//
|
||||||
|
// Licensed under the GNU Affero General Public License, Version 3;
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// https://www.gnu.org/licenses/agpl-3.0.html
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
*
|
||||||
|
* @brief BLE advertising to send own beacons
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef _ena_BLUETOOTH_ADVERTISE_H_
|
||||||
|
#define _ena_BLUETOOTH_ADVERTISE_H_
|
||||||
|
|
||||||
|
#define ENA_ADVERTISE_LOG "ESP-ENA-advertise" // TAG for Logging
|
||||||
|
#define ENA_BLUETOOTH_TAG_DATA (0x1A) // Data for BLE payload TAG
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Start BLE advertising
|
||||||
|
*/
|
||||||
|
void ena_bluetooth_advertise_start(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set payload for BLE advertising
|
||||||
|
*
|
||||||
|
* This will set the payload for based on given ENIN and TEK.
|
||||||
|
*
|
||||||
|
* Source documents (Section: Advertising Payload)
|
||||||
|
*
|
||||||
|
* https://blog.google/documents/70/Exposure_Notification_-_Bluetooth_Specification_v1.2.2.pdf
|
||||||
|
*
|
||||||
|
* https://covid19-static.cdn-apple.com/applications/covid19/current/static/detection-tracing/pdf/ExposureNotification-BluetoothSpecificationv1.2.pdf
|
||||||
|
*
|
||||||
|
* @param[in] enin ENIN defining the start of the tek vadility. This should be the ENIN for the current timestamp
|
||||||
|
* @param[in] tek pointer to the TEK used to encrypt the payload.
|
||||||
|
*/
|
||||||
|
void ena_bluetooth_advertise_set_payload(uint32_t enin, uint8_t *tek);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Stop BLE advertising
|
||||||
|
*/
|
||||||
|
void ena_bluetooth_advertise_stop(void);
|
||||||
|
|
||||||
|
#endif
|
69
components/ena/include/ena-bluetooth-scan.h
Normal file
69
components/ena/include/ena-bluetooth-scan.h
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
// Copyright 2020 Lukas Haubaum
|
||||||
|
//
|
||||||
|
// Licensed under the GNU Affero General Public License, Version 3;
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// https://www.gnu.org/licenses/agpl-3.0.html
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
*
|
||||||
|
* @brief BLE scans for detecting other beacons
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef _ena_BLUETOOTH_SCAN_H_
|
||||||
|
#define _ena_BLUETOOTH_SCAN_H_
|
||||||
|
|
||||||
|
#define ENA_SCAN_LOG "ESP-ENA-scan" // TAG for Logging
|
||||||
|
#define ENA_SCANNING_TIME (CONFIG_ENA_SCANNING_TIME) // time how long a scan should run
|
||||||
|
#define ENA_SCANNING_INTERVAL (CONFIG_ENA_SCANNING_INTERVAL) // interval for next scan to happen
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief status of BLE scan
|
||||||
|
*/
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
ENA_SCAN_STATUS_SCANNING = 0, // scan is running
|
||||||
|
ENA_SCAN_STATUS_NOT_SCANNING, // scan is not running
|
||||||
|
ENA_SCAN_STATUS_WAITING, // scan is not running but stopped manually
|
||||||
|
} ena_bluetooth_scan_status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief initialize the BLE scanning
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void ena_bluetooth_scan_init(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief start BLE scanning for a given duration
|
||||||
|
*
|
||||||
|
* Source documents (Section: Scanning Behavior)
|
||||||
|
*
|
||||||
|
* https://blog.google/documents/70/Exposure_Notification_-_Bluetooth_Specification_v1.2.2.pdf
|
||||||
|
*
|
||||||
|
* https://covid19-static.cdn-apple.com/applications/covid19/current/static/detection-tracing/pdf/ExposureNotification-BluetoothSpecificationv1.2.pdf
|
||||||
|
*
|
||||||
|
* @param[in] duration duration of the scan in seconds
|
||||||
|
*/
|
||||||
|
void ena_bluetooth_scan_start(uint32_t duration);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief stop a running BLE scanning
|
||||||
|
*/
|
||||||
|
void ena_bluetooth_scan_stop(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief return the current scanning status
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* current scan status
|
||||||
|
*/
|
||||||
|
int ena_bluetooth_scan_get_status(void);
|
||||||
|
|
||||||
|
#endif
|
126
components/ena/include/ena-crypto.h
Normal file
126
components/ena/include/ena-crypto.h
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
// Copyright 2020 Lukas Haubaum
|
||||||
|
//
|
||||||
|
// Licensed under the GNU Affero General Public License, Version 3;
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// https://www.gnu.org/licenses/agpl-3.0.html
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
*
|
||||||
|
* @brief covers cryptography part (key creation, encryption etc.)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef _ena_CRYPTO_H_
|
||||||
|
#define _ena_CRYPTO_H_
|
||||||
|
|
||||||
|
#define ENA_TIME_WINDOW (600) // time window every 10 minutes
|
||||||
|
#define ENA_KEY_LENGTH (16) // key length
|
||||||
|
#define ENA_AEM_METADATA_LENGTH (4) // size of metadata
|
||||||
|
#define ENA_TEK_ROLLING_PERIOD (CONFIG_ENA_TEK_ROLLING_PERIOD) // TEKRollingPeriod
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief initialize cryptography
|
||||||
|
*
|
||||||
|
* This initialize the cryptography by setting up entropy.
|
||||||
|
*/
|
||||||
|
void ena_crypto_init(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief calculate ENIntervalNumber (ENIN) for given UNIX timestamp
|
||||||
|
*
|
||||||
|
* Source documents (Section: ENIntervalNumber)
|
||||||
|
*
|
||||||
|
* https://blog.google/documents/69/Exposure_Notification_-_Cryptography_Specification_v1.2.1.pdf
|
||||||
|
*
|
||||||
|
* https://covid19-static.cdn-apple.com/applications/covid19/current/static/detection-tracing/pdf/ExposureNotification-CryptographySpecificationv1.2.pdf
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param[in] unix_timestamp UNIX Timestamp to calculate ENIN for
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* ENIN for given timestamp
|
||||||
|
*/
|
||||||
|
uint32_t ena_crypto_enin(uint32_t unix_timestamp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief calculate a new random Temporary Exposure Key (TEK)
|
||||||
|
*
|
||||||
|
* Source documents (Section: Temporary Exposure Key)
|
||||||
|
*
|
||||||
|
* https://blog.google/documents/69/Exposure_Notification_-_Cryptography_Specification_v1.2.1.pdf
|
||||||
|
*
|
||||||
|
* https://covid19-static.cdn-apple.com/applications/covid19/current/static/detection-tracing/pdf/ExposureNotification-CryptographySpecificationv1.2.pdf
|
||||||
|
*
|
||||||
|
* @param[out] tek pointer to the new TEK
|
||||||
|
*/
|
||||||
|
void ena_crypto_tek(uint8_t *tek);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief calculate a new Rolling Proximity Identifier Key (RPIK) with given TEK
|
||||||
|
*
|
||||||
|
* Source documents (Section: Rolling Proximity Identifier Key)
|
||||||
|
*
|
||||||
|
* https://blog.google/documents/69/Exposure_Notification_-_Cryptography_Specification_v1.2.1.pdf
|
||||||
|
*
|
||||||
|
* https://covid19-static.cdn-apple.com/applications/covid19/current/static/detection-tracing/pdf/ExposureNotification-CryptographySpecificationv1.2.pdf
|
||||||
|
*
|
||||||
|
* @param[out] rpik pointer to the new RPIK
|
||||||
|
* @param[in] tek TEK for calculating RPIK
|
||||||
|
*/
|
||||||
|
void ena_crypto_rpik(uint8_t *rpik, uint8_t *tek);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief calculate a new Rolling Proximity Identifier with given RPIK and ENIN
|
||||||
|
*
|
||||||
|
* Source documents (Section: Rolling Proximity Identifier)
|
||||||
|
*
|
||||||
|
* https://blog.google/documents/69/Exposure_Notification_-_Cryptography_Specification_v1.2.1.pdf
|
||||||
|
*
|
||||||
|
* https://covid19-static.cdn-apple.com/applications/covid19/current/static/detection-tracing/pdf/ExposureNotification-CryptographySpecificationv1.2.pdf
|
||||||
|
*
|
||||||
|
* @param[out] rpi pointer to the new RPI
|
||||||
|
* @param[in] rpik RPIK for encrypting RPI
|
||||||
|
* @param[in] enin ENIN to encrypt in RPI
|
||||||
|
*/
|
||||||
|
void ena_crypto_rpi(uint8_t *rpi, uint8_t *rpik, uint32_t enin);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief calculate a new Associated Encrypted Metadata Key (AEMK) with given TEK
|
||||||
|
*
|
||||||
|
* Source documents (Section: Associated Encrypted Metadata Key)
|
||||||
|
*
|
||||||
|
* https://blog.google/documents/69/Exposure_Notification_-_Cryptography_Specification_v1.2.1.pdf
|
||||||
|
*
|
||||||
|
* https://covid19-static.cdn-apple.com/applications/covid19/current/static/detection-tracing/pdf/ExposureNotification-CryptographySpecificationv1.2.pdf
|
||||||
|
*
|
||||||
|
* @param[out] aemk pointer to the new AEMK
|
||||||
|
* @param[in] tek TEK for calculating AEMK
|
||||||
|
*/
|
||||||
|
void ena_crypto_aemk(uint8_t *aemk, uint8_t *tek);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief create Associated Encrypted Metadata (AEM) with given AEMK along the RPI
|
||||||
|
*
|
||||||
|
* Source documents (Section: Associated Encrypted Metadata)
|
||||||
|
*
|
||||||
|
* https://blog.google/documents/69/Exposure_Notification_-_Cryptography_Specification_v1.2.1.pdf
|
||||||
|
*
|
||||||
|
* https://covid19-static.cdn-apple.com/applications/covid19/current/static/detection-tracing/pdf/ExposureNotification-CryptographySpecificationv1.2.pdf
|
||||||
|
*
|
||||||
|
* @param[out] aem pointer to the new AEM
|
||||||
|
* @param[in] aemk AEMK for encrypting AEM
|
||||||
|
* @param[in] rpi RPI for encrypting AEM
|
||||||
|
* @param[in] power_level BLE power level to encrypt in AEM
|
||||||
|
*/
|
||||||
|
void ena_crypto_aem(uint8_t *aem, uint8_t *aemk, uint8_t *rpi, uint8_t power_level);
|
||||||
|
|
||||||
|
#endif
|
249
components/ena/include/ena-exposure.h
Normal file
249
components/ena/include/ena-exposure.h
Normal file
@ -0,0 +1,249 @@
|
|||||||
|
// Copyright 2020 Lukas Haubaum
|
||||||
|
//
|
||||||
|
// Licensed under the GNU Affero General Public License, Version 3;
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// https://www.gnu.org/licenses/agpl-3.0.html
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
*
|
||||||
|
* @brief compare temporary exposure keys with stored beacons, calculate score and risk
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef _ena_EXPOSURE_H_
|
||||||
|
#define _ena_EXPOSURE_H_
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "ena-storage.h"
|
||||||
|
#include "ena-crypto.h"
|
||||||
|
|
||||||
|
#define ENA_EXPOSURE_LOG "ESP-ENA-exposure" // TAG for Logging
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief report type
|
||||||
|
*/
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
UNKNOWN = 0,
|
||||||
|
CONFIRMED_TEST_LOW = 1,
|
||||||
|
CONFIRMED_TEST_STANDARD = 2,
|
||||||
|
CONFIRMED_TEST_HIGH = 3,
|
||||||
|
CONFIRMED_CLINICAL_DIAGNOSIS = 4,
|
||||||
|
SELF_REPORT = 5,
|
||||||
|
NEGATIVE = 6,
|
||||||
|
RECURSIVE = 7,
|
||||||
|
} ena_report_type_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief duration risk
|
||||||
|
*/
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
MINUTES_0 = 0, // D = 0 min
|
||||||
|
MINUTES_5 = 1, // D <= 5 min
|
||||||
|
MINUTES_10 = 2, // D <= 10 min
|
||||||
|
MINUTES_15 = 3, // D <= 15 min
|
||||||
|
MINUTES_20 = 4, // D <= 20 min
|
||||||
|
MINUTES_25 = 5, // D <= 25 min
|
||||||
|
MINUTES_30 = 6, // D <= 30 min
|
||||||
|
MINUTES_LONGER = 7, // D > 30 min
|
||||||
|
} ena_duration_risk_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief day risk
|
||||||
|
*/
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
DAYS_14 = 0, // >= 14 days
|
||||||
|
DAYS_13 = 1, // 12-13 days
|
||||||
|
DAYS_11 = 2, // 10-11 days
|
||||||
|
DAYS_9 = 3, // 8-9 days
|
||||||
|
DAYS_7 = 4, // 6-7 days
|
||||||
|
DAYS_5 = 5, // 4-5 days
|
||||||
|
DAYS_3 = 6, // 2-3 days
|
||||||
|
DAYS_0 = 7, // 0-1 days
|
||||||
|
} ena_day_risk_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief attenuation risk
|
||||||
|
*/
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
ATTENUATION_73 = 0, // A > 73 dB
|
||||||
|
ATTENUATION_63 = 1, // 73 >= A > 63
|
||||||
|
ATTENUATION_51 = 2, // 63 >= A > 61
|
||||||
|
ATTENUATION_33 = 3, // 51 >= A > 33
|
||||||
|
ATTENUATION_27 = 4, // 33 >= A > 27
|
||||||
|
ATTENUATION_15 = 5, // 27 >= A > 15
|
||||||
|
ATTENUATION_10 = 6, // 15 >= A > 10
|
||||||
|
ATTENUATION_LOWER = 7, // A <= 10
|
||||||
|
} ena_attenuation_risk_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief risk level from 0-8
|
||||||
|
*/
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
ZERO = 0,
|
||||||
|
MINIMAL = 1,
|
||||||
|
VERY_LOW = 2,
|
||||||
|
LOW = 3,
|
||||||
|
MEDIUM = 4,
|
||||||
|
INCREASED = 5,
|
||||||
|
HIGH = 6,
|
||||||
|
VERY_HIGH = 7,
|
||||||
|
MAXIMUM = 8,
|
||||||
|
} ena_risk_level_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief structure for exposure configuration
|
||||||
|
*
|
||||||
|
* The exposure configuration is used to calculate the risk score.
|
||||||
|
*/
|
||||||
|
typedef struct __attribute__((__packed__))
|
||||||
|
{
|
||||||
|
uint8_t transmission_risk_values[8];
|
||||||
|
uint8_t duration_risk_values[8];
|
||||||
|
uint8_t days_risk_values[8];
|
||||||
|
uint8_t attenuation_risk_values[8];
|
||||||
|
} ena_exposure_config_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief structure for exposure parameter
|
||||||
|
*
|
||||||
|
* These parameter are obtained from an exposure information to calculate the risk score.
|
||||||
|
*/
|
||||||
|
typedef struct __attribute__((__packed__))
|
||||||
|
{
|
||||||
|
ena_report_type_t report_type;
|
||||||
|
int days;
|
||||||
|
int duration;
|
||||||
|
int attenuation;
|
||||||
|
} ena_exposure_parameter_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief structure for exposure summary
|
||||||
|
*
|
||||||
|
* This represents the current state of all exposures.
|
||||||
|
*/
|
||||||
|
typedef struct __attribute__((__packed__))
|
||||||
|
{
|
||||||
|
uint32_t last_update; // timestamp of last update of exposure data
|
||||||
|
int days_since_last_exposure; // Number of days since the most recent exposure.
|
||||||
|
int num_exposures; // Number of all exposure information
|
||||||
|
int max_risk_score; // max. risk score of all exposure information
|
||||||
|
int risk_score_sum; // sum of all risk_scores
|
||||||
|
} ena_exposure_summary_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief structure for temporary exposure key
|
||||||
|
*
|
||||||
|
* The temporary exposure key is used to check for exposure.
|
||||||
|
*/
|
||||||
|
typedef struct __attribute__((__packed__))
|
||||||
|
{
|
||||||
|
uint8_t key_data[ENA_KEY_LENGTH];
|
||||||
|
uint8_t transmission_risk_level;
|
||||||
|
uint32_t rolling_start_interval_number;
|
||||||
|
uint32_t rolling_period;
|
||||||
|
ena_report_type_t report_type;
|
||||||
|
uint32_t days_since_onset_of_symptoms;
|
||||||
|
} ena_temporary_exposure_key_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief calculate transmission risk score
|
||||||
|
*
|
||||||
|
* @param[in] config the exposure configuration used for calculating score
|
||||||
|
* @param[in] params the exposure parameter to calculate with
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
int ena_exposure_transmission_risk_score(ena_exposure_config_t *config, ena_exposure_parameter_t params);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief calculate duration risk score
|
||||||
|
*
|
||||||
|
* @param[in] config the exposure configuration used for calculating score
|
||||||
|
* @param[in] params the exposure parameter to calculate with
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
int ena_exposure_duration_risk_score(ena_exposure_config_t *config, ena_exposure_parameter_t params);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief calculate days risk score
|
||||||
|
*
|
||||||
|
* @param[in] config the exposure configuration used for calculating score
|
||||||
|
* @param[in] params the exposure parameter to calculate with
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
int ena_exposure_days_risk_score(ena_exposure_config_t *config, ena_exposure_parameter_t params);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief calculate attenuation risk score
|
||||||
|
*
|
||||||
|
* @param[in] config the exposure configuration used for calculating score
|
||||||
|
* @param[in] params the exposure parameter to calculate with
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
int ena_exposure_attenuation_risk_score(ena_exposure_config_t *config, ena_exposure_parameter_t params);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief calculate overall risk score
|
||||||
|
*
|
||||||
|
* @param[in] config the exposure configuration used for calculating score
|
||||||
|
* @param[in] params the exposure parameter to calculate with
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
int ena_exposure_risk_score(ena_exposure_config_t *config, ena_exposure_parameter_t params);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief returns the current exposure summary
|
||||||
|
*
|
||||||
|
* @param[in] config the exposure configuration used for calculating scores
|
||||||
|
*/
|
||||||
|
void ena_exposure_summary(ena_exposure_config_t *config);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief return the current exposure summary
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* ena_exposure_summary_t pointer to the current exposure summary
|
||||||
|
*/
|
||||||
|
ena_exposure_summary_t *ena_exposure_current_summary(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief return a default exposure configuration
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* ena_exposure_config_t default exposure configuration
|
||||||
|
*/
|
||||||
|
ena_exposure_config_t *ena_exposure_default_config(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief reads Temporary Exposue Key check for exposures with certain beacon
|
||||||
|
*
|
||||||
|
* @param[in] ena_beacon_t the beacon to check against
|
||||||
|
* @param[in] temporary_exposure_key the temporary exposure keys to check
|
||||||
|
*/
|
||||||
|
void ena_exposure_check(ena_beacon_t beacon, ena_temporary_exposure_key_t temporary_exposure_key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief reads Temporary Exposue Key and check for exposures with all beacons
|
||||||
|
*
|
||||||
|
* @param[in] temporary_exposure_key the temporary exposure keys to check
|
||||||
|
*/
|
||||||
|
void ena_exposure_check_temporary_exposure_key(ena_temporary_exposure_key_t temporary_exposure_key);
|
||||||
|
|
||||||
|
#endif
|
302
components/ena/include/ena-storage.h
Normal file
302
components/ena/include/ena-storage.h
Normal file
@ -0,0 +1,302 @@
|
|||||||
|
// Copyright 2020 Lukas Haubaum
|
||||||
|
//
|
||||||
|
// Licensed under the GNU Affero General Public License, Version 3;
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// https://www.gnu.org/licenses/agpl-3.0.html
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
*
|
||||||
|
* @brief storage part to store own TEKs and beacons
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef _ena_STORAGE_H_
|
||||||
|
#define _ena_STORAGE_H_
|
||||||
|
|
||||||
|
#include "ena-crypto.h"
|
||||||
|
|
||||||
|
#define ENA_STORAGE_LOG "ESP-ENA-storage" // TAG for Logging
|
||||||
|
#define ENA_STORAGE_PARTITION_NAME (CONFIG_ENA_STORAGE_PARTITION_NAME) // name of partition to use for storing
|
||||||
|
#define ENA_STORAGE_START_ADDRESS (CONFIG_ENA_STORAGE_START_ADDRESS) // start address of storage
|
||||||
|
#define ENA_STORAGE_TEK_MAX (CONFIG_ENA_STORAGE_TEK_MAX) // Period of storing TEKs // length of a stored beacon -> RPI keysize + AEM size + 4 Bytes for ENIN + 4 Bytes for RSSI
|
||||||
|
#define ENA_STORAGE_TEMP_BEACONS_MAX (CONFIG_ENA_STORAGE_TEMP_BEACONS_MAX) // Maximum number of temporary stored beacons // length of a stored beacon -> RPI keysize + AEM size + 4 Bytes for ENIN + 4 Bytes for RSSI
|
||||||
|
#define ENA_STORAGE_EXPOSURE_INFORMATION_MAX (CONFIG_ENA_STORAGE_EXPOSURE_INFORMATION_MAX) // Maximum number of stored exposure information
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief structure for TEK
|
||||||
|
*/
|
||||||
|
typedef struct __attribute__((__packed__))
|
||||||
|
{
|
||||||
|
uint8_t key_data[ENA_KEY_LENGTH]; // key data for encryption
|
||||||
|
uint32_t enin; // ENIN marking start of validity
|
||||||
|
uint8_t rolling_period; // period after validity start to mark key as expired
|
||||||
|
} ena_tek_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief sturcture for storing a beacons
|
||||||
|
*/
|
||||||
|
typedef struct __attribute__((__packed__))
|
||||||
|
{
|
||||||
|
uint8_t rpi[ENA_KEY_LENGTH]; // received RPI of beacon
|
||||||
|
uint8_t aem[ENA_AEM_METADATA_LENGTH]; // received AEM of beacon
|
||||||
|
uint32_t timestamp_first; // timestamp of first recognition
|
||||||
|
uint32_t timestamp_last; // timestamp of last recognition
|
||||||
|
int rssi; // average measured RSSI
|
||||||
|
} ena_beacon_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief structure for storing a Exposure Information (combined ExposureInformation, ExposureWindow and ScanInstance from Google API >= 1.5)
|
||||||
|
*/
|
||||||
|
typedef struct __attribute__((__packed__))
|
||||||
|
{
|
||||||
|
uint32_t day; // Day of the exposure, using UTC, encapsulated as the time of the beginning of that day.
|
||||||
|
int typical_attenuation; // Aggregation of the attenuations of all of a given diagnosis key's beacons received during the scan, in dB.
|
||||||
|
int min_attenuation; // Minimum attenuation of all of a given diagnosis key's beacons received during the scan, in dB.
|
||||||
|
int duration_minutes; //The duration of the exposure in minutes.
|
||||||
|
int report_type; // Type of diagnosis associated with a key.
|
||||||
|
} ena_exposure_information_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief read bytes at given address
|
||||||
|
*
|
||||||
|
* @param[in] address the address to read bytes from
|
||||||
|
* @param[out] data pointer to write the read data
|
||||||
|
* @param[in] size how many bytes to read
|
||||||
|
*/
|
||||||
|
void ena_storage_read(size_t address, void *data, size_t size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief store bytes at given address
|
||||||
|
*
|
||||||
|
* @param[in] address the address to write bytes to
|
||||||
|
* @param[in] data pointer to the data to write
|
||||||
|
* @param[in] size how many bytes to write
|
||||||
|
*/
|
||||||
|
void ena_storage_write(size_t address, void *data, size_t size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief erase storage at given address
|
||||||
|
*
|
||||||
|
* @param[in] address the address to erase from
|
||||||
|
* @param[in] size how many bytes to erase
|
||||||
|
*/
|
||||||
|
void ena_storage_erase(size_t address, size_t size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief deletes bytes at given address and shift other data back
|
||||||
|
*
|
||||||
|
* @param[in] address the address to delete from
|
||||||
|
* @param[in] end_address the address to mark end of shift
|
||||||
|
* @param[in] size how many bytes to delete
|
||||||
|
*/
|
||||||
|
void ena_storage_shift_delete(size_t address, size_t end_address, size_t size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief get timestamp of most recent exposure data
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* unix timestamp
|
||||||
|
*/
|
||||||
|
uint32_t ena_storage_read_last_exposure_date(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief set timestamp of most recent exposure data
|
||||||
|
*
|
||||||
|
* @param[in] timestamp unix timestamp
|
||||||
|
*/
|
||||||
|
void ena_storage_write_last_exposure_date(uint32_t timestamp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief get last stored TEK
|
||||||
|
*
|
||||||
|
* @param[out] tek pointer to write last TEK to
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* total number of TEKs stored
|
||||||
|
*/
|
||||||
|
uint32_t ena_storage_read_last_tek(ena_tek_t *tek);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief store given TEK
|
||||||
|
*
|
||||||
|
* This will store the given TEK as new TEK.
|
||||||
|
*
|
||||||
|
* @param[in] tek the tek to store
|
||||||
|
*/
|
||||||
|
void ena_storage_write_tek(ena_tek_t *tek);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief get number of stored exposure information
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* total number of exposure information stored
|
||||||
|
*/
|
||||||
|
uint32_t ena_storage_exposure_information_count(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief get exposure information at given index
|
||||||
|
*
|
||||||
|
* @param[in] index the index of the exposure information to read
|
||||||
|
* @param[out] exposure_info pointer to exposure information to write to
|
||||||
|
*/
|
||||||
|
void ena_storage_get_exposure_information(uint32_t index, ena_exposure_information_t *exposure_info);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief store exposure information
|
||||||
|
*
|
||||||
|
* @param[in] exposure_info new exposure information to store
|
||||||
|
*/
|
||||||
|
void ena_storage_add_exposure_information(ena_exposure_information_t *exposure_info);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief get number of stored temporary beacons
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* total number of temporary beacons stored
|
||||||
|
*/
|
||||||
|
uint32_t ena_storage_temp_beacons_count(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief get temporary beacon at given index
|
||||||
|
*
|
||||||
|
* @param[in] index the index of the temporary beacon to read
|
||||||
|
* @param[out] beacon pointer to temporary beacon to write to
|
||||||
|
*/
|
||||||
|
void ena_storage_get_temp_beacon(uint32_t index, ena_beacon_t *beacon);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief store temporary beacon
|
||||||
|
*
|
||||||
|
* @param[in] beacon new temporary beacon to store
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* index of new stored beacon
|
||||||
|
*/
|
||||||
|
uint32_t ena_storage_add_temp_beacon(ena_beacon_t *beacon);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief store temporary beacon at given index
|
||||||
|
*
|
||||||
|
* @param[in] index the index of the temporary beacon to overwrite
|
||||||
|
* @param[in] beacon temporary beacon to store
|
||||||
|
*/
|
||||||
|
void ena_storage_set_temp_beacon(uint32_t index, ena_beacon_t *beacon);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief remove temporary beacon at given index
|
||||||
|
*
|
||||||
|
* @param[in] index the index of the temporary beacon to remove
|
||||||
|
*/
|
||||||
|
void ena_storage_remove_temp_beacon(uint32_t index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief get number of permanently stored beacons
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* total number of beacons stored
|
||||||
|
*/
|
||||||
|
uint32_t ena_storage_beacons_count(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief get permanently stored beacon at given index
|
||||||
|
*
|
||||||
|
* @param[in] index the index of the beacon to read
|
||||||
|
* @param[out] beacon pointer to to write to
|
||||||
|
*/
|
||||||
|
void ena_storage_get_beacon(uint32_t index, ena_beacon_t *beacon);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief permanently store beacon
|
||||||
|
*
|
||||||
|
* @param[in] beacon new beacon to permanently store
|
||||||
|
*/
|
||||||
|
void ena_storage_add_beacon(ena_beacon_t *beacon);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief remove beacon at given index
|
||||||
|
*
|
||||||
|
* @param[in] index the index of the beacon to remove
|
||||||
|
*/
|
||||||
|
void ena_storage_remove_beacon(uint32_t index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief erase the storage
|
||||||
|
*
|
||||||
|
* This function completely deletes all stored data and resets the counters
|
||||||
|
* of TEKs, temporary beacon and beacon to zero.
|
||||||
|
*/
|
||||||
|
void ena_storage_erase_all(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief erase all stored TEKs
|
||||||
|
*
|
||||||
|
* This function deletes all stored TEKs and resets counter to zero.
|
||||||
|
*/
|
||||||
|
void ena_storage_erase_tek(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief erase all stored exposure information
|
||||||
|
*
|
||||||
|
* This function deletes all stored exposure information and resets counter to zero.
|
||||||
|
*/
|
||||||
|
void ena_storage_erase_exposure_information(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief erase all stored temporary beacons
|
||||||
|
*
|
||||||
|
* This function deletes all stored temporary beacons and resets counter to zero.
|
||||||
|
*/
|
||||||
|
void ena_storage_erase_temporary_beacon(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief erase all permanently stored beacons
|
||||||
|
*
|
||||||
|
* This function deletes all stored beacons and resets counter to zero.
|
||||||
|
*/
|
||||||
|
void ena_storage_erase_beacon(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief helper to dump a byte array as hash
|
||||||
|
*/
|
||||||
|
void ena_storage_dump_hash_array(uint8_t *data, size_t size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief dump all stored TEKs to serial output
|
||||||
|
*
|
||||||
|
* This function prints all stored TEKs to serial output in
|
||||||
|
* the following CSV format: #,enin,tek
|
||||||
|
*/
|
||||||
|
void ena_storage_dump_teks(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief dump all stored exposure information to serial output
|
||||||
|
*
|
||||||
|
* This function prints all stored exposure information to serial output in
|
||||||
|
* the following CSV format: #,day,typical_attenuation,min_attenuation,duration_minutes,report_type
|
||||||
|
*/
|
||||||
|
void ena_storage_dump_exposure_information(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief dump all stored temporary beacons to serial output
|
||||||
|
*
|
||||||
|
* This function prints all stored temporary beacons to serial output in
|
||||||
|
* the following CSV format: #,timestamp_first,timestamp_last,rpi,aem,rssi
|
||||||
|
*/
|
||||||
|
void ena_storage_dump_temp_beacons(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief dump all stored beacons to serial output
|
||||||
|
*
|
||||||
|
* This function prints all stored beacons to serial output in
|
||||||
|
* the following CSV format: #,timestamp,rpi,aem,rssi
|
||||||
|
*/
|
||||||
|
void ena_storage_dump_beacons(void);
|
||||||
|
|
||||||
|
#endif
|
49
components/ena/include/ena.h
Normal file
49
components/ena/include/ena.h
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
// Copyright 2020 Lukas Haubaum
|
||||||
|
//
|
||||||
|
// Licensed under the GNU Affero General Public License, Version 3;
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// https://www.gnu.org/licenses/agpl-3.0.html
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
*
|
||||||
|
* @brief run all other ena parts together to time scanning, advertising and exposure checks
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef _ena_H_
|
||||||
|
#define _ena_H_
|
||||||
|
|
||||||
|
#define ENA_LOG "ESP-ENA" // TAG for Logging
|
||||||
|
#define ENA_BT_ROTATION_TIMEOUT_INTERVAL (CONFIG_ENA_BT_ROTATION_TIMEOUT_INTERVAL) // change advertising payload and therefore the BT address
|
||||||
|
#define ENA_BT_RANDOMIZE_ROTATION_TIMEOUT_INTERVAL (CONFIG_ENA_BT_RANDOMIZE_ROTATION_TIMEOUT_INTERVAL) // random intervall change for BT address change
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Run Exposure Notification API
|
||||||
|
*
|
||||||
|
* This runs the complete BLE logic
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void ena_run(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Start Exposure Notification API
|
||||||
|
*
|
||||||
|
* This initializes the complete stack of ESP_ENA. It will initialize BLE module and
|
||||||
|
* starting a task for managing advertising and scanning processes.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void ena_start(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief stop ena
|
||||||
|
*/
|
||||||
|
void ena_stop(void);
|
||||||
|
|
||||||
|
#endif
|
@ -78,7 +78,7 @@ void interface_data_rst(void)
|
|||||||
ena_storage_write_last_exposure_date(0);
|
ena_storage_write_last_exposure_date(0);
|
||||||
break;
|
break;
|
||||||
case INTERFACE_DATA_DEL_ALL:
|
case INTERFACE_DATA_DEL_ALL:
|
||||||
ena_storage_erase();
|
ena_storage_erase_all();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,6 +75,7 @@ void interface_wifi_mid(void)
|
|||||||
memset(¤t_wifi_config, 0, sizeof(wifi_config_t));
|
memset(¤t_wifi_config, 0, sizeof(wifi_config_t));
|
||||||
memcpy(current_wifi_config.sta.ssid, ap_info[ap_selected].ssid, strlen((char *)ap_info[ap_selected].ssid));
|
memcpy(current_wifi_config.sta.ssid, ap_info[ap_selected].ssid, strlen((char *)ap_info[ap_selected].ssid));
|
||||||
interface_input(&interface_wifi_input_rst, &interface_wifi_input_set, 64);
|
interface_input(&interface_wifi_input_rst, &interface_wifi_input_set, 64);
|
||||||
|
interface_input_set_text("muffimuffi");
|
||||||
}
|
}
|
||||||
void interface_wifi_up(void)
|
void interface_wifi_up(void)
|
||||||
{
|
{
|
||||||
|
8
components/nanopb/CMakeLists.txt
Normal file
8
components/nanopb/CMakeLists.txt
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
idf_component_register(
|
||||||
|
SRCS
|
||||||
|
"pb_common.c"
|
||||||
|
"pb_decode.c"
|
||||||
|
"pb_encode.c"
|
||||||
|
"TemporaryExposureKeyExport.pb.c"
|
||||||
|
INCLUDE_DIRS "."
|
||||||
|
)
|
20
components/nanopb/LICENSE.txt
Normal file
20
components/nanopb/LICENSE.txt
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
Copyright (c) 2011 Petteri Aimonen <jpa at nanopb.mail.kapsi.fi>
|
||||||
|
|
||||||
|
This software is provided 'as-is', without any express or
|
||||||
|
implied warranty. In no event will the authors be held liable
|
||||||
|
for any damages arising from the use of this software.
|
||||||
|
|
||||||
|
Permission is granted to anyone to use this software for any
|
||||||
|
purpose, including commercial applications, and to alter it and
|
||||||
|
redistribute it freely, subject to the following restrictions:
|
||||||
|
|
||||||
|
1. The origin of this software must not be misrepresented; you
|
||||||
|
must not claim that you wrote the original software. If you use
|
||||||
|
this software in a product, an acknowledgment in the product
|
||||||
|
documentation would be appreciated but is not required.
|
||||||
|
|
||||||
|
2. Altered source versions must be plainly marked as such, and
|
||||||
|
must not be misrepresented as being the original software.
|
||||||
|
|
||||||
|
3. This notice may not be removed or altered from any source
|
||||||
|
distribution.
|
19
components/nanopb/TemporaryExposureKeyExport.pb.c
Normal file
19
components/nanopb/TemporaryExposureKeyExport.pb.c
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/* Automatically generated nanopb constant definitions */
|
||||||
|
/* Generated by nanopb-0.4.2 */
|
||||||
|
|
||||||
|
#include "TemporaryExposureKeyExport.pb.h"
|
||||||
|
#if PB_PROTO_HEADER_VERSION != 40
|
||||||
|
#error Regenerate this file with the current version of nanopb generator.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
PB_BIND(TemporaryExposureKeyExport, TemporaryExposureKeyExport, AUTO)
|
||||||
|
|
||||||
|
|
||||||
|
PB_BIND(SignatureInfo, SignatureInfo, AUTO)
|
||||||
|
|
||||||
|
|
||||||
|
PB_BIND(TemporaryExposureKey, TemporaryExposureKey, AUTO)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
147
components/nanopb/TemporaryExposureKeyExport.pb.h
Normal file
147
components/nanopb/TemporaryExposureKeyExport.pb.h
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
/* Automatically generated nanopb header */
|
||||||
|
/* Generated by nanopb-0.4.2 */
|
||||||
|
|
||||||
|
#ifndef PB_TEMPORARYEXPOSUREKEYEXPORT_PB_H_INCLUDED
|
||||||
|
#define PB_TEMPORARYEXPOSUREKEYEXPORT_PB_H_INCLUDED
|
||||||
|
#include <pb.h>
|
||||||
|
|
||||||
|
#if PB_PROTO_HEADER_VERSION != 40
|
||||||
|
#error Regenerate this file with the current version of nanopb generator.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Enum definitions */
|
||||||
|
typedef enum _TemporaryExposureKey_ReportType {
|
||||||
|
TemporaryExposureKey_ReportType_UNKNOWN = 0,
|
||||||
|
TemporaryExposureKey_ReportType_CONFIRMED_TEST = 1,
|
||||||
|
TemporaryExposureKey_ReportType_CONFIRMED_CLINICAL_DIAGNOSIS = 2,
|
||||||
|
TemporaryExposureKey_ReportType_SELF_REPORT = 3,
|
||||||
|
TemporaryExposureKey_ReportType_RECURSIVE = 4,
|
||||||
|
TemporaryExposureKey_ReportType_REVOKED = 5
|
||||||
|
} TemporaryExposureKey_ReportType;
|
||||||
|
|
||||||
|
/* Struct definitions */
|
||||||
|
typedef struct _SignatureInfo {
|
||||||
|
pb_callback_t verification_key_version;
|
||||||
|
pb_callback_t verification_key_id;
|
||||||
|
pb_callback_t signature_algorithm;
|
||||||
|
} SignatureInfo;
|
||||||
|
|
||||||
|
typedef struct _TemporaryExposureKey {
|
||||||
|
pb_callback_t key_data;
|
||||||
|
bool has_transmission_risk_level;
|
||||||
|
int32_t transmission_risk_level;
|
||||||
|
bool has_rolling_start_interval_number;
|
||||||
|
int32_t rolling_start_interval_number;
|
||||||
|
bool has_rolling_period;
|
||||||
|
int32_t rolling_period;
|
||||||
|
bool has_report_type;
|
||||||
|
TemporaryExposureKey_ReportType report_type;
|
||||||
|
bool has_days_since_onset_of_symptoms;
|
||||||
|
int32_t days_since_onset_of_symptoms;
|
||||||
|
} TemporaryExposureKey;
|
||||||
|
|
||||||
|
typedef struct _TemporaryExposureKeyExport {
|
||||||
|
bool has_start_timestamp;
|
||||||
|
uint64_t start_timestamp;
|
||||||
|
bool has_end_timestamp;
|
||||||
|
uint64_t end_timestamp;
|
||||||
|
pb_callback_t region;
|
||||||
|
bool has_batch_num;
|
||||||
|
int32_t batch_num;
|
||||||
|
bool has_batch_size;
|
||||||
|
int32_t batch_size;
|
||||||
|
pb_callback_t signature_infos;
|
||||||
|
pb_callback_t keys;
|
||||||
|
pb_callback_t revised_keys;
|
||||||
|
} TemporaryExposureKeyExport;
|
||||||
|
|
||||||
|
|
||||||
|
/* Helper constants for enums */
|
||||||
|
#define _TemporaryExposureKey_ReportType_MIN TemporaryExposureKey_ReportType_UNKNOWN
|
||||||
|
#define _TemporaryExposureKey_ReportType_MAX TemporaryExposureKey_ReportType_REVOKED
|
||||||
|
#define _TemporaryExposureKey_ReportType_ARRAYSIZE ((TemporaryExposureKey_ReportType)(TemporaryExposureKey_ReportType_REVOKED+1))
|
||||||
|
|
||||||
|
|
||||||
|
/* Initializer values for message structs */
|
||||||
|
#define TemporaryExposureKeyExport_init_default {false, 0, false, 0, {{NULL}, NULL}, false, 0, false, 0, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}}
|
||||||
|
#define SignatureInfo_init_default {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}}
|
||||||
|
#define TemporaryExposureKey_init_default {{{NULL}, NULL}, false, 0, false, 0, false, 144, false, _TemporaryExposureKey_ReportType_MIN, false, 0}
|
||||||
|
#define TemporaryExposureKeyExport_init_zero {false, 0, false, 0, {{NULL}, NULL}, false, 0, false, 0, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}}
|
||||||
|
#define SignatureInfo_init_zero {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}}
|
||||||
|
#define TemporaryExposureKey_init_zero {{{NULL}, NULL}, false, 0, false, 0, false, 0, false, _TemporaryExposureKey_ReportType_MIN, false, 0}
|
||||||
|
|
||||||
|
/* Field tags (for use in manual encoding/decoding) */
|
||||||
|
#define SignatureInfo_verification_key_version_tag 3
|
||||||
|
#define SignatureInfo_verification_key_id_tag 4
|
||||||
|
#define SignatureInfo_signature_algorithm_tag 5
|
||||||
|
#define TemporaryExposureKey_key_data_tag 1
|
||||||
|
#define TemporaryExposureKey_transmission_risk_level_tag 2
|
||||||
|
#define TemporaryExposureKey_rolling_start_interval_number_tag 3
|
||||||
|
#define TemporaryExposureKey_rolling_period_tag 4
|
||||||
|
#define TemporaryExposureKey_report_type_tag 5
|
||||||
|
#define TemporaryExposureKey_days_since_onset_of_symptoms_tag 6
|
||||||
|
#define TemporaryExposureKeyExport_start_timestamp_tag 1
|
||||||
|
#define TemporaryExposureKeyExport_end_timestamp_tag 2
|
||||||
|
#define TemporaryExposureKeyExport_region_tag 3
|
||||||
|
#define TemporaryExposureKeyExport_batch_num_tag 4
|
||||||
|
#define TemporaryExposureKeyExport_batch_size_tag 5
|
||||||
|
#define TemporaryExposureKeyExport_signature_infos_tag 6
|
||||||
|
#define TemporaryExposureKeyExport_keys_tag 7
|
||||||
|
#define TemporaryExposureKeyExport_revised_keys_tag 8
|
||||||
|
|
||||||
|
/* Struct field encoding specification for nanopb */
|
||||||
|
#define TemporaryExposureKeyExport_FIELDLIST(X, a) \
|
||||||
|
X(a, STATIC, OPTIONAL, FIXED64, start_timestamp, 1) \
|
||||||
|
X(a, STATIC, OPTIONAL, FIXED64, end_timestamp, 2) \
|
||||||
|
X(a, CALLBACK, OPTIONAL, STRING, region, 3) \
|
||||||
|
X(a, STATIC, OPTIONAL, INT32, batch_num, 4) \
|
||||||
|
X(a, STATIC, OPTIONAL, INT32, batch_size, 5) \
|
||||||
|
X(a, CALLBACK, REPEATED, MESSAGE, signature_infos, 6) \
|
||||||
|
X(a, CALLBACK, REPEATED, MESSAGE, keys, 7) \
|
||||||
|
X(a, CALLBACK, REPEATED, MESSAGE, revised_keys, 8)
|
||||||
|
#define TemporaryExposureKeyExport_CALLBACK pb_default_field_callback
|
||||||
|
#define TemporaryExposureKeyExport_DEFAULT NULL
|
||||||
|
#define TemporaryExposureKeyExport_signature_infos_MSGTYPE SignatureInfo
|
||||||
|
#define TemporaryExposureKeyExport_keys_MSGTYPE TemporaryExposureKey
|
||||||
|
#define TemporaryExposureKeyExport_revised_keys_MSGTYPE TemporaryExposureKey
|
||||||
|
|
||||||
|
#define SignatureInfo_FIELDLIST(X, a) \
|
||||||
|
X(a, CALLBACK, OPTIONAL, STRING, verification_key_version, 3) \
|
||||||
|
X(a, CALLBACK, OPTIONAL, STRING, verification_key_id, 4) \
|
||||||
|
X(a, CALLBACK, OPTIONAL, STRING, signature_algorithm, 5)
|
||||||
|
#define SignatureInfo_CALLBACK pb_default_field_callback
|
||||||
|
#define SignatureInfo_DEFAULT NULL
|
||||||
|
|
||||||
|
#define TemporaryExposureKey_FIELDLIST(X, a) \
|
||||||
|
X(a, CALLBACK, OPTIONAL, BYTES, key_data, 1) \
|
||||||
|
X(a, STATIC, OPTIONAL, INT32, transmission_risk_level, 2) \
|
||||||
|
X(a, STATIC, OPTIONAL, INT32, rolling_start_interval_number, 3) \
|
||||||
|
X(a, STATIC, OPTIONAL, INT32, rolling_period, 4) \
|
||||||
|
X(a, STATIC, OPTIONAL, UENUM, report_type, 5) \
|
||||||
|
X(a, STATIC, OPTIONAL, SINT32, days_since_onset_of_symptoms, 6)
|
||||||
|
#define TemporaryExposureKey_CALLBACK pb_default_field_callback
|
||||||
|
#define TemporaryExposureKey_DEFAULT (const pb_byte_t*)"\x20\x90\x01\x00"
|
||||||
|
|
||||||
|
extern const pb_msgdesc_t TemporaryExposureKeyExport_msg;
|
||||||
|
extern const pb_msgdesc_t SignatureInfo_msg;
|
||||||
|
extern const pb_msgdesc_t TemporaryExposureKey_msg;
|
||||||
|
|
||||||
|
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
|
||||||
|
#define TemporaryExposureKeyExport_fields &TemporaryExposureKeyExport_msg
|
||||||
|
#define SignatureInfo_fields &SignatureInfo_msg
|
||||||
|
#define TemporaryExposureKey_fields &TemporaryExposureKey_msg
|
||||||
|
|
||||||
|
/* Maximum encoded size of messages (where known) */
|
||||||
|
/* TemporaryExposureKeyExport_size depends on runtime parameters */
|
||||||
|
/* SignatureInfo_size depends on runtime parameters */
|
||||||
|
/* TemporaryExposureKey_size depends on runtime parameters */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
65
components/nanopb/TemporaryExposureKeyExport.proto
Normal file
65
components/nanopb/TemporaryExposureKeyExport.proto
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
syntax = "proto2";
|
||||||
|
message TemporaryExposureKeyExport {
|
||||||
|
// Time window of keys in this batch based on arrival to server, in UTC seconds.
|
||||||
|
optional fixed64 start_timestamp = 1;
|
||||||
|
optional fixed64 end_timestamp = 2;
|
||||||
|
// Region for which these keys came from, such as country.
|
||||||
|
optional string region = 3;
|
||||||
|
// For example, file 2 in batch size of 10. Ordinal, 1-based numbering.
|
||||||
|
// Note: Not yet supported on iOS.
|
||||||
|
optional int32 batch_num = 4;
|
||||||
|
optional int32 batch_size = 5;
|
||||||
|
// Information about associated signatures
|
||||||
|
repeated SignatureInfo signature_infos = 6;
|
||||||
|
// 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.
|
||||||
|
repeated TemporaryExposureKey keys = 7;
|
||||||
|
// TemporaryExposureKeys that have changed status.
|
||||||
|
// Keys should be included in this list if they have changed status
|
||||||
|
// or have been revoked.
|
||||||
|
repeated TemporaryExposureKey revised_keys = 8;
|
||||||
|
}
|
||||||
|
message SignatureInfo {
|
||||||
|
// The first two fields have been deprecated
|
||||||
|
reserved 1, 2;
|
||||||
|
reserved "app_bundle_id", "android_package";
|
||||||
|
// Key version for rollovers
|
||||||
|
// Must be in character class [a-zA-Z0-9_]. For example, 'v1'
|
||||||
|
optional string verification_key_version = 3;
|
||||||
|
// 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, you can use your region's three-digit
|
||||||
|
// mobile country code (MCC). If your region has more than one MCC, choose the
|
||||||
|
// one that Apple has configured.
|
||||||
|
optional string verification_key_id = 4;
|
||||||
|
// ASN.1 OID for Algorithm Identifier. For example, `1.2.840.10045.4.3.2'
|
||||||
|
optional string signature_algorithm = 5;
|
||||||
|
}
|
||||||
|
message TemporaryExposureKey {
|
||||||
|
// Key of infected user
|
||||||
|
optional bytes key_data = 1;
|
||||||
|
// Varying risk associated with a key depending on diagnosis method
|
||||||
|
optional int32 transmission_risk_level = 2 [deprecated = true];
|
||||||
|
// The interval number since epoch for which a key starts
|
||||||
|
optional int32 rolling_start_interval_number = 3;
|
||||||
|
// Increments of 10 minutes describing how long a key is valid
|
||||||
|
optional int32 rolling_period = 4
|
||||||
|
[default = 144]; // defaults to 24 hours
|
||||||
|
// Data type representing why this key was published.
|
||||||
|
enum ReportType {
|
||||||
|
UNKNOWN = 0; // Never returned by the client API.
|
||||||
|
CONFIRMED_TEST = 1;
|
||||||
|
CONFIRMED_CLINICAL_DIAGNOSIS = 2;
|
||||||
|
SELF_REPORT = 3;
|
||||||
|
RECURSIVE = 4; // Reserved for future use.
|
||||||
|
REVOKED = 5; // Used to revoke a key, never returned by client API.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type of diagnosis associated with a key.
|
||||||
|
optional ReportType report_type = 5;
|
||||||
|
|
||||||
|
// Number of days elapsed between symptom onset and the TEK being used.
|
||||||
|
// E.g. 2 means TEK is 2 days after onset of symptoms.
|
||||||
|
optional sint32 days_since_onset_of_symptoms = 6;
|
||||||
|
}
|
868
components/nanopb/pb.h
Normal file
868
components/nanopb/pb.h
Normal file
@ -0,0 +1,868 @@
|
|||||||
|
/* Common parts of the nanopb library. Most of these are quite low-level
|
||||||
|
* stuff. For the high-level interface, see pb_encode.h and pb_decode.h.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PB_H_INCLUDED
|
||||||
|
#define PB_H_INCLUDED
|
||||||
|
|
||||||
|
/*****************************************************************
|
||||||
|
* Nanopb compilation time options. You can change these here by *
|
||||||
|
* uncommenting the lines, or on the compiler command line. *
|
||||||
|
*****************************************************************/
|
||||||
|
|
||||||
|
/* Enable support for dynamically allocated fields */
|
||||||
|
/* #define PB_ENABLE_MALLOC 1 */
|
||||||
|
|
||||||
|
/* Define this if your CPU / compiler combination does not support
|
||||||
|
* unaligned memory access to packed structures. */
|
||||||
|
/* #define PB_NO_PACKED_STRUCTS 1 */
|
||||||
|
|
||||||
|
/* Increase the number of required fields that are tracked.
|
||||||
|
* A compiler warning will tell if you need this. */
|
||||||
|
/* #define PB_MAX_REQUIRED_FIELDS 256 */
|
||||||
|
|
||||||
|
/* Add support for tag numbers > 65536 and fields larger than 65536 bytes. */
|
||||||
|
/* #define PB_FIELD_32BIT 1 */
|
||||||
|
|
||||||
|
/* Disable support for error messages in order to save some code space. */
|
||||||
|
/* #define PB_NO_ERRMSG 1 */
|
||||||
|
|
||||||
|
/* Disable support for custom streams (support only memory buffers). */
|
||||||
|
/* #define PB_BUFFER_ONLY 1 */
|
||||||
|
|
||||||
|
/* Disable support for 64-bit datatypes, for compilers without int64_t
|
||||||
|
or to save some code space. */
|
||||||
|
/* #define PB_WITHOUT_64BIT 1 */
|
||||||
|
|
||||||
|
/* Don't encode scalar arrays as packed. This is only to be used when
|
||||||
|
* the decoder on the receiving side cannot process packed scalar arrays.
|
||||||
|
* Such example is older protobuf.js. */
|
||||||
|
/* #define PB_ENCODE_ARRAYS_UNPACKED 1 */
|
||||||
|
|
||||||
|
/* Enable conversion of doubles to floats for platforms that do not
|
||||||
|
* support 64-bit doubles. Most commonly AVR. */
|
||||||
|
/* #define PB_CONVERT_DOUBLE_FLOAT 1 */
|
||||||
|
|
||||||
|
/* Check whether incoming strings are valid UTF-8 sequences. Slows down
|
||||||
|
* the string processing slightly and slightly increases code size. */
|
||||||
|
/* #define PB_VALIDATE_UTF8 1 */
|
||||||
|
|
||||||
|
/******************************************************************
|
||||||
|
* You usually don't need to change anything below this line. *
|
||||||
|
* Feel free to look around and use the defined macros, though. *
|
||||||
|
******************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
/* Version of the nanopb library. Just in case you want to check it in
|
||||||
|
* your own program. */
|
||||||
|
#define NANOPB_VERSION nanopb-0.4.2
|
||||||
|
|
||||||
|
/* Include all the system headers needed by nanopb. You will need the
|
||||||
|
* definitions of the following:
|
||||||
|
* - strlen, memcpy, memset functions
|
||||||
|
* - [u]int_least8_t, uint_fast8_t, [u]int_least16_t, [u]int32_t, [u]int64_t
|
||||||
|
* - size_t
|
||||||
|
* - bool
|
||||||
|
*
|
||||||
|
* If you don't have the standard header files, you can instead provide
|
||||||
|
* a custom header that defines or includes all this. In that case,
|
||||||
|
* define PB_SYSTEM_HEADER to the path of this file.
|
||||||
|
*/
|
||||||
|
#ifdef PB_SYSTEM_HEADER
|
||||||
|
#include PB_SYSTEM_HEADER
|
||||||
|
#else
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#ifdef PB_ENABLE_MALLOC
|
||||||
|
#include <stdlib.h>
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Macro for defining packed structures (compiler dependent).
|
||||||
|
* This just reduces memory requirements, but is not required.
|
||||||
|
*/
|
||||||
|
#if defined(PB_NO_PACKED_STRUCTS)
|
||||||
|
/* Disable struct packing */
|
||||||
|
# define PB_PACKED_STRUCT_START
|
||||||
|
# define PB_PACKED_STRUCT_END
|
||||||
|
# define pb_packed
|
||||||
|
#elif defined(__GNUC__) || defined(__clang__)
|
||||||
|
/* For GCC and clang */
|
||||||
|
# define PB_PACKED_STRUCT_START
|
||||||
|
# define PB_PACKED_STRUCT_END
|
||||||
|
# define pb_packed __attribute__((packed))
|
||||||
|
#elif defined(__ICCARM__) || defined(__CC_ARM)
|
||||||
|
/* For IAR ARM and Keil MDK-ARM compilers */
|
||||||
|
# define PB_PACKED_STRUCT_START _Pragma("pack(push, 1)")
|
||||||
|
# define PB_PACKED_STRUCT_END _Pragma("pack(pop)")
|
||||||
|
# define pb_packed
|
||||||
|
#elif defined(_MSC_VER) && (_MSC_VER >= 1500)
|
||||||
|
/* For Microsoft Visual C++ */
|
||||||
|
# define PB_PACKED_STRUCT_START __pragma(pack(push, 1))
|
||||||
|
# define PB_PACKED_STRUCT_END __pragma(pack(pop))
|
||||||
|
# define pb_packed
|
||||||
|
#else
|
||||||
|
/* Unknown compiler */
|
||||||
|
# define PB_PACKED_STRUCT_START
|
||||||
|
# define PB_PACKED_STRUCT_END
|
||||||
|
# define pb_packed
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Handly macro for suppressing unreferenced-parameter compiler warnings. */
|
||||||
|
#ifndef PB_UNUSED
|
||||||
|
#define PB_UNUSED(x) (void)(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Harvard-architecture processors may need special attributes for storing
|
||||||
|
* field information in program memory. */
|
||||||
|
#ifndef PB_PROGMEM
|
||||||
|
#ifdef __AVR__
|
||||||
|
#include <avr/pgmspace.h>
|
||||||
|
#define PB_PROGMEM PROGMEM
|
||||||
|
#define PB_PROGMEM_READU32(x) pgm_read_dword(&x)
|
||||||
|
#else
|
||||||
|
#define PB_PROGMEM
|
||||||
|
#define PB_PROGMEM_READU32(x) (x)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Compile-time assertion, used for checking compatible compilation options.
|
||||||
|
* If this does not work properly on your compiler, use
|
||||||
|
* #define PB_NO_STATIC_ASSERT to disable it.
|
||||||
|
*
|
||||||
|
* But before doing that, check carefully the error message / place where it
|
||||||
|
* comes from to see if the error has a real cause. Unfortunately the error
|
||||||
|
* message is not always very clear to read, but you can see the reason better
|
||||||
|
* in the place where the PB_STATIC_ASSERT macro was called.
|
||||||
|
*/
|
||||||
|
#ifndef PB_NO_STATIC_ASSERT
|
||||||
|
# ifndef PB_STATIC_ASSERT
|
||||||
|
# if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
|
||||||
|
/* C11 standard _Static_assert mechanism */
|
||||||
|
# define PB_STATIC_ASSERT(COND,MSG) _Static_assert(COND,#MSG);
|
||||||
|
# else
|
||||||
|
/* Classic negative-size-array static assert mechanism */
|
||||||
|
# define PB_STATIC_ASSERT(COND,MSG) typedef char PB_STATIC_ASSERT_MSG(MSG, __LINE__, __COUNTER__)[(COND)?1:-1];
|
||||||
|
# define PB_STATIC_ASSERT_MSG(MSG, LINE, COUNTER) PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER)
|
||||||
|
# define PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER) pb_static_assertion_##MSG##_##LINE##_##COUNTER
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
#else
|
||||||
|
/* Static asserts disabled by PB_NO_STATIC_ASSERT */
|
||||||
|
# define PB_STATIC_ASSERT(COND,MSG)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Number of required fields to keep track of. */
|
||||||
|
#ifndef PB_MAX_REQUIRED_FIELDS
|
||||||
|
#define PB_MAX_REQUIRED_FIELDS 64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if PB_MAX_REQUIRED_FIELDS < 64
|
||||||
|
#error You should not lower PB_MAX_REQUIRED_FIELDS from the default value (64).
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PB_WITHOUT_64BIT
|
||||||
|
#ifdef PB_CONVERT_DOUBLE_FLOAT
|
||||||
|
/* Cannot use doubles without 64-bit types */
|
||||||
|
#undef PB_CONVERT_DOUBLE_FLOAT
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* List of possible field types. These are used in the autogenerated code.
|
||||||
|
* Least-significant 4 bits tell the scalar type
|
||||||
|
* Most-significant 4 bits specify repeated/required/packed etc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef uint_least8_t pb_type_t;
|
||||||
|
|
||||||
|
/**** Field data types ****/
|
||||||
|
|
||||||
|
/* Numeric types */
|
||||||
|
#define PB_LTYPE_BOOL 0x00U /* bool */
|
||||||
|
#define PB_LTYPE_VARINT 0x01U /* int32, int64, enum, bool */
|
||||||
|
#define PB_LTYPE_UVARINT 0x02U /* uint32, uint64 */
|
||||||
|
#define PB_LTYPE_SVARINT 0x03U /* sint32, sint64 */
|
||||||
|
#define PB_LTYPE_FIXED32 0x04U /* fixed32, sfixed32, float */
|
||||||
|
#define PB_LTYPE_FIXED64 0x05U /* fixed64, sfixed64, double */
|
||||||
|
|
||||||
|
/* Marker for last packable field type. */
|
||||||
|
#define PB_LTYPE_LAST_PACKABLE 0x05U
|
||||||
|
|
||||||
|
/* Byte array with pre-allocated buffer.
|
||||||
|
* data_size is the length of the allocated PB_BYTES_ARRAY structure. */
|
||||||
|
#define PB_LTYPE_BYTES 0x06U
|
||||||
|
|
||||||
|
/* String with pre-allocated buffer.
|
||||||
|
* data_size is the maximum length. */
|
||||||
|
#define PB_LTYPE_STRING 0x07U
|
||||||
|
|
||||||
|
/* Submessage
|
||||||
|
* submsg_fields is pointer to field descriptions */
|
||||||
|
#define PB_LTYPE_SUBMESSAGE 0x08U
|
||||||
|
|
||||||
|
/* Submessage with pre-decoding callback
|
||||||
|
* The pre-decoding callback is stored as pb_callback_t right before pSize.
|
||||||
|
* submsg_fields is pointer to field descriptions */
|
||||||
|
#define PB_LTYPE_SUBMSG_W_CB 0x09U
|
||||||
|
|
||||||
|
/* Extension pseudo-field
|
||||||
|
* The field contains a pointer to pb_extension_t */
|
||||||
|
#define PB_LTYPE_EXTENSION 0x0AU
|
||||||
|
|
||||||
|
/* Byte array with inline, pre-allocated byffer.
|
||||||
|
* data_size is the length of the inline, allocated buffer.
|
||||||
|
* This differs from PB_LTYPE_BYTES by defining the element as
|
||||||
|
* pb_byte_t[data_size] rather than pb_bytes_array_t. */
|
||||||
|
#define PB_LTYPE_FIXED_LENGTH_BYTES 0x0BU
|
||||||
|
|
||||||
|
/* Number of declared LTYPES */
|
||||||
|
#define PB_LTYPES_COUNT 0x0CU
|
||||||
|
#define PB_LTYPE_MASK 0x0FU
|
||||||
|
|
||||||
|
/**** Field repetition rules ****/
|
||||||
|
|
||||||
|
#define PB_HTYPE_REQUIRED 0x00U
|
||||||
|
#define PB_HTYPE_OPTIONAL 0x10U
|
||||||
|
#define PB_HTYPE_SINGULAR 0x10U
|
||||||
|
#define PB_HTYPE_REPEATED 0x20U
|
||||||
|
#define PB_HTYPE_FIXARRAY 0x20U
|
||||||
|
#define PB_HTYPE_ONEOF 0x30U
|
||||||
|
#define PB_HTYPE_MASK 0x30U
|
||||||
|
|
||||||
|
/**** Field allocation types ****/
|
||||||
|
|
||||||
|
#define PB_ATYPE_STATIC 0x00U
|
||||||
|
#define PB_ATYPE_POINTER 0x80U
|
||||||
|
#define PB_ATYPE_CALLBACK 0x40U
|
||||||
|
#define PB_ATYPE_MASK 0xC0U
|
||||||
|
|
||||||
|
#define PB_ATYPE(x) ((x) & PB_ATYPE_MASK)
|
||||||
|
#define PB_HTYPE(x) ((x) & PB_HTYPE_MASK)
|
||||||
|
#define PB_LTYPE(x) ((x) & PB_LTYPE_MASK)
|
||||||
|
#define PB_LTYPE_IS_SUBMSG(x) (PB_LTYPE(x) == PB_LTYPE_SUBMESSAGE || \
|
||||||
|
PB_LTYPE(x) == PB_LTYPE_SUBMSG_W_CB)
|
||||||
|
|
||||||
|
/* Data type used for storing sizes of struct fields
|
||||||
|
* and array counts.
|
||||||
|
*/
|
||||||
|
#if defined(PB_FIELD_32BIT)
|
||||||
|
typedef uint32_t pb_size_t;
|
||||||
|
typedef int32_t pb_ssize_t;
|
||||||
|
#else
|
||||||
|
typedef uint_least16_t pb_size_t;
|
||||||
|
typedef int_least16_t pb_ssize_t;
|
||||||
|
#endif
|
||||||
|
#define PB_SIZE_MAX ((pb_size_t)-1)
|
||||||
|
|
||||||
|
/* Data type for storing encoded data and other byte streams.
|
||||||
|
* This typedef exists to support platforms where uint8_t does not exist.
|
||||||
|
* You can regard it as equivalent on uint8_t on other platforms.
|
||||||
|
*/
|
||||||
|
typedef uint_least8_t pb_byte_t;
|
||||||
|
|
||||||
|
/* Forward declaration of struct types */
|
||||||
|
typedef struct pb_istream_s pb_istream_t;
|
||||||
|
typedef struct pb_ostream_s pb_ostream_t;
|
||||||
|
typedef struct pb_field_iter_s pb_field_iter_t;
|
||||||
|
|
||||||
|
/* This structure is used in auto-generated constants
|
||||||
|
* to specify struct fields.
|
||||||
|
*/
|
||||||
|
PB_PACKED_STRUCT_START
|
||||||
|
typedef struct pb_msgdesc_s pb_msgdesc_t;
|
||||||
|
struct pb_msgdesc_s {
|
||||||
|
pb_size_t field_count;
|
||||||
|
const uint32_t *field_info;
|
||||||
|
const pb_msgdesc_t * const * submsg_info;
|
||||||
|
const pb_byte_t *default_value;
|
||||||
|
|
||||||
|
bool (*field_callback)(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_iter_t *field);
|
||||||
|
} pb_packed;
|
||||||
|
PB_PACKED_STRUCT_END
|
||||||
|
|
||||||
|
/* Iterator for message descriptor */
|
||||||
|
struct pb_field_iter_s {
|
||||||
|
const pb_msgdesc_t *descriptor; /* Pointer to message descriptor constant */
|
||||||
|
void *message; /* Pointer to start of the structure */
|
||||||
|
|
||||||
|
pb_size_t index; /* Index of the field */
|
||||||
|
pb_size_t field_info_index; /* Index to descriptor->field_info array */
|
||||||
|
pb_size_t required_field_index; /* Index that counts only the required fields */
|
||||||
|
pb_size_t submessage_index; /* Index that counts only submessages */
|
||||||
|
|
||||||
|
pb_size_t tag; /* Tag of current field */
|
||||||
|
pb_size_t data_size; /* sizeof() of a single item */
|
||||||
|
pb_size_t array_size; /* Number of array entries */
|
||||||
|
pb_type_t type; /* Type of current field */
|
||||||
|
|
||||||
|
void *pField; /* Pointer to current field in struct */
|
||||||
|
void *pData; /* Pointer to current data contents. Different than pField for arrays and pointers. */
|
||||||
|
void *pSize; /* Pointer to count/has field */
|
||||||
|
|
||||||
|
const pb_msgdesc_t *submsg_desc; /* For submessage fields, pointer to field descriptor for the submessage. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* For compatibility with legacy code */
|
||||||
|
typedef pb_field_iter_t pb_field_t;
|
||||||
|
|
||||||
|
/* Make sure that the standard integer types are of the expected sizes.
|
||||||
|
* Otherwise fixed32/fixed64 fields can break.
|
||||||
|
*
|
||||||
|
* If you get errors here, it probably means that your stdint.h is not
|
||||||
|
* correct for your platform.
|
||||||
|
*/
|
||||||
|
#ifndef PB_WITHOUT_64BIT
|
||||||
|
PB_STATIC_ASSERT(sizeof(int64_t) == 2 * sizeof(int32_t), INT64_T_WRONG_SIZE)
|
||||||
|
PB_STATIC_ASSERT(sizeof(uint64_t) == 2 * sizeof(uint32_t), UINT64_T_WRONG_SIZE)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* This structure is used for 'bytes' arrays.
|
||||||
|
* It has the number of bytes in the beginning, and after that an array.
|
||||||
|
* Note that actual structs used will have a different length of bytes array.
|
||||||
|
*/
|
||||||
|
#define PB_BYTES_ARRAY_T(n) struct { pb_size_t size; pb_byte_t bytes[n]; }
|
||||||
|
#define PB_BYTES_ARRAY_T_ALLOCSIZE(n) ((size_t)n + offsetof(pb_bytes_array_t, bytes))
|
||||||
|
|
||||||
|
struct pb_bytes_array_s {
|
||||||
|
pb_size_t size;
|
||||||
|
pb_byte_t bytes[1];
|
||||||
|
};
|
||||||
|
typedef struct pb_bytes_array_s pb_bytes_array_t;
|
||||||
|
|
||||||
|
/* This structure is used for giving the callback function.
|
||||||
|
* It is stored in the message structure and filled in by the method that
|
||||||
|
* calls pb_decode.
|
||||||
|
*
|
||||||
|
* The decoding callback will be given a limited-length stream
|
||||||
|
* If the wire type was string, the length is the length of the string.
|
||||||
|
* If the wire type was a varint/fixed32/fixed64, the length is the length
|
||||||
|
* of the actual value.
|
||||||
|
* The function may be called multiple times (especially for repeated types,
|
||||||
|
* but also otherwise if the message happens to contain the field multiple
|
||||||
|
* times.)
|
||||||
|
*
|
||||||
|
* The encoding callback will receive the actual output stream.
|
||||||
|
* It should write all the data in one call, including the field tag and
|
||||||
|
* wire type. It can write multiple fields.
|
||||||
|
*
|
||||||
|
* The callback can be null if you want to skip a field.
|
||||||
|
*/
|
||||||
|
typedef struct pb_callback_s pb_callback_t;
|
||||||
|
struct pb_callback_s {
|
||||||
|
/* Callback functions receive a pointer to the arg field.
|
||||||
|
* You can access the value of the field as *arg, and modify it if needed.
|
||||||
|
*/
|
||||||
|
union {
|
||||||
|
bool (*decode)(pb_istream_t *stream, const pb_field_t *field, void **arg);
|
||||||
|
bool (*encode)(pb_ostream_t *stream, const pb_field_t *field, void * const *arg);
|
||||||
|
} funcs;
|
||||||
|
|
||||||
|
/* Free arg for use by callback */
|
||||||
|
void *arg;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern bool pb_default_field_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field);
|
||||||
|
|
||||||
|
/* Wire types. Library user needs these only in encoder callbacks. */
|
||||||
|
typedef enum {
|
||||||
|
PB_WT_VARINT = 0,
|
||||||
|
PB_WT_64BIT = 1,
|
||||||
|
PB_WT_STRING = 2,
|
||||||
|
PB_WT_32BIT = 5
|
||||||
|
} pb_wire_type_t;
|
||||||
|
|
||||||
|
/* Structure for defining the handling of unknown/extension fields.
|
||||||
|
* Usually the pb_extension_type_t structure is automatically generated,
|
||||||
|
* while the pb_extension_t structure is created by the user. However,
|
||||||
|
* if you want to catch all unknown fields, you can also create a custom
|
||||||
|
* pb_extension_type_t with your own callback.
|
||||||
|
*/
|
||||||
|
typedef struct pb_extension_type_s pb_extension_type_t;
|
||||||
|
typedef struct pb_extension_s pb_extension_t;
|
||||||
|
struct pb_extension_type_s {
|
||||||
|
/* Called for each unknown field in the message.
|
||||||
|
* If you handle the field, read off all of its data and return true.
|
||||||
|
* If you do not handle the field, do not read anything and return true.
|
||||||
|
* If you run into an error, return false.
|
||||||
|
* Set to NULL for default handler.
|
||||||
|
*/
|
||||||
|
bool (*decode)(pb_istream_t *stream, pb_extension_t *extension,
|
||||||
|
uint32_t tag, pb_wire_type_t wire_type);
|
||||||
|
|
||||||
|
/* Called once after all regular fields have been encoded.
|
||||||
|
* If you have something to write, do so and return true.
|
||||||
|
* If you do not have anything to write, just return true.
|
||||||
|
* If you run into an error, return false.
|
||||||
|
* Set to NULL for default handler.
|
||||||
|
*/
|
||||||
|
bool (*encode)(pb_ostream_t *stream, const pb_extension_t *extension);
|
||||||
|
|
||||||
|
/* Free field for use by the callback. */
|
||||||
|
const void *arg;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pb_extension_s {
|
||||||
|
/* Type describing the extension field. Usually you'll initialize
|
||||||
|
* this to a pointer to the automatically generated structure. */
|
||||||
|
const pb_extension_type_t *type;
|
||||||
|
|
||||||
|
/* Destination for the decoded data. This must match the datatype
|
||||||
|
* of the extension field. */
|
||||||
|
void *dest;
|
||||||
|
|
||||||
|
/* Pointer to the next extension handler, or NULL.
|
||||||
|
* If this extension does not match a field, the next handler is
|
||||||
|
* automatically called. */
|
||||||
|
pb_extension_t *next;
|
||||||
|
|
||||||
|
/* The decoder sets this to true if the extension was found.
|
||||||
|
* Ignored for encoding. */
|
||||||
|
bool found;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define pb_extension_init_zero {NULL,NULL,NULL,false}
|
||||||
|
|
||||||
|
/* Memory allocation functions to use. You can define pb_realloc and
|
||||||
|
* pb_free to custom functions if you want. */
|
||||||
|
#ifdef PB_ENABLE_MALLOC
|
||||||
|
# ifndef pb_realloc
|
||||||
|
# define pb_realloc(ptr, size) realloc(ptr, size)
|
||||||
|
# endif
|
||||||
|
# ifndef pb_free
|
||||||
|
# define pb_free(ptr) free(ptr)
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* This is used to inform about need to regenerate .pb.h/.pb.c files. */
|
||||||
|
#define PB_PROTO_HEADER_VERSION 40
|
||||||
|
|
||||||
|
/* These macros are used to declare pb_field_t's in the constant array. */
|
||||||
|
/* Size of a structure member, in bytes. */
|
||||||
|
#define pb_membersize(st, m) (sizeof ((st*)0)->m)
|
||||||
|
/* Number of entries in an array. */
|
||||||
|
#define pb_arraysize(st, m) (pb_membersize(st, m) / pb_membersize(st, m[0]))
|
||||||
|
/* Delta from start of one member to the start of another member. */
|
||||||
|
#define pb_delta(st, m1, m2) ((int)offsetof(st, m1) - (int)offsetof(st, m2))
|
||||||
|
|
||||||
|
/* Force expansion of macro value */
|
||||||
|
#define PB_EXPAND(x) x
|
||||||
|
|
||||||
|
/* Binding of a message field set into a specific structure */
|
||||||
|
#define PB_BIND(msgname, structname, width) \
|
||||||
|
const uint32_t structname ## _field_info[] PB_PROGMEM = \
|
||||||
|
{ \
|
||||||
|
msgname ## _FIELDLIST(PB_GEN_FIELD_INFO_ ## width, structname) \
|
||||||
|
0 \
|
||||||
|
}; \
|
||||||
|
const pb_msgdesc_t* const structname ## _submsg_info[] = \
|
||||||
|
{ \
|
||||||
|
msgname ## _FIELDLIST(PB_GEN_SUBMSG_INFO, structname) \
|
||||||
|
NULL \
|
||||||
|
}; \
|
||||||
|
const pb_msgdesc_t structname ## _msg = \
|
||||||
|
{ \
|
||||||
|
0 msgname ## _FIELDLIST(PB_GEN_FIELD_COUNT, structname), \
|
||||||
|
structname ## _field_info, \
|
||||||
|
structname ## _submsg_info, \
|
||||||
|
msgname ## _DEFAULT, \
|
||||||
|
msgname ## _CALLBACK, \
|
||||||
|
}; \
|
||||||
|
msgname ## _FIELDLIST(PB_GEN_FIELD_INFO_ASSERT_ ## width, structname)
|
||||||
|
|
||||||
|
#define PB_GEN_FIELD_COUNT(structname, atype, htype, ltype, fieldname, tag) +1
|
||||||
|
|
||||||
|
/* X-macro for generating the entries in struct_field_info[] array. */
|
||||||
|
#define PB_GEN_FIELD_INFO_1(structname, atype, htype, ltype, fieldname, tag) \
|
||||||
|
PB_FIELDINFO_1(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
|
||||||
|
PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||||
|
PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||||
|
PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||||
|
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
|
||||||
|
|
||||||
|
#define PB_GEN_FIELD_INFO_2(structname, atype, htype, ltype, fieldname, tag) \
|
||||||
|
PB_FIELDINFO_2(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
|
||||||
|
PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||||
|
PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||||
|
PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||||
|
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
|
||||||
|
|
||||||
|
#define PB_GEN_FIELD_INFO_4(structname, atype, htype, ltype, fieldname, tag) \
|
||||||
|
PB_FIELDINFO_4(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
|
||||||
|
PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||||
|
PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||||
|
PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||||
|
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
|
||||||
|
|
||||||
|
#define PB_GEN_FIELD_INFO_8(structname, atype, htype, ltype, fieldname, tag) \
|
||||||
|
PB_FIELDINFO_8(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
|
||||||
|
PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||||
|
PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||||
|
PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||||
|
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
|
||||||
|
|
||||||
|
#define PB_GEN_FIELD_INFO_AUTO(structname, atype, htype, ltype, fieldname, tag) \
|
||||||
|
PB_FIELDINFO_AUTO2(PB_FIELDINFO_WIDTH_AUTO(_PB_ATYPE_ ## atype, _PB_HTYPE_ ## htype, _PB_LTYPE_ ## ltype), \
|
||||||
|
tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
|
||||||
|
PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||||
|
PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||||
|
PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||||
|
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
|
||||||
|
|
||||||
|
#define PB_FIELDINFO_AUTO2(width, tag, type, data_offset, data_size, size_offset, array_size) \
|
||||||
|
PB_FIELDINFO_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size)
|
||||||
|
|
||||||
|
#define PB_FIELDINFO_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) \
|
||||||
|
PB_FIELDINFO_ ## width(tag, type, data_offset, data_size, size_offset, array_size)
|
||||||
|
|
||||||
|
/* X-macro for generating asserts that entries fit in struct_field_info[] array.
|
||||||
|
* The structure of macros here must match the structure above in PB_GEN_FIELD_INFO_x(),
|
||||||
|
* but it is not easily reused because of how macro substitutions work. */
|
||||||
|
#define PB_GEN_FIELD_INFO_ASSERT_1(structname, atype, htype, ltype, fieldname, tag) \
|
||||||
|
PB_FIELDINFO_ASSERT_1(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
|
||||||
|
PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||||
|
PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||||
|
PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||||
|
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
|
||||||
|
|
||||||
|
#define PB_GEN_FIELD_INFO_ASSERT_2(structname, atype, htype, ltype, fieldname, tag) \
|
||||||
|
PB_FIELDINFO_ASSERT_2(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
|
||||||
|
PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||||
|
PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||||
|
PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||||
|
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
|
||||||
|
|
||||||
|
#define PB_GEN_FIELD_INFO_ASSERT_4(structname, atype, htype, ltype, fieldname, tag) \
|
||||||
|
PB_FIELDINFO_ASSERT_4(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
|
||||||
|
PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||||
|
PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||||
|
PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||||
|
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
|
||||||
|
|
||||||
|
#define PB_GEN_FIELD_INFO_ASSERT_8(structname, atype, htype, ltype, fieldname, tag) \
|
||||||
|
PB_FIELDINFO_ASSERT_8(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
|
||||||
|
PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||||
|
PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||||
|
PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||||
|
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
|
||||||
|
|
||||||
|
#define PB_GEN_FIELD_INFO_ASSERT_AUTO(structname, atype, htype, ltype, fieldname, tag) \
|
||||||
|
PB_FIELDINFO_ASSERT_AUTO2(PB_FIELDINFO_WIDTH_AUTO(_PB_ATYPE_ ## atype, _PB_HTYPE_ ## htype, _PB_LTYPE_ ## ltype), \
|
||||||
|
tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
|
||||||
|
PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||||
|
PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||||
|
PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||||
|
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
|
||||||
|
|
||||||
|
#define PB_FIELDINFO_ASSERT_AUTO2(width, tag, type, data_offset, data_size, size_offset, array_size) \
|
||||||
|
PB_FIELDINFO_ASSERT_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size)
|
||||||
|
|
||||||
|
#define PB_FIELDINFO_ASSERT_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) \
|
||||||
|
PB_FIELDINFO_ASSERT_ ## width(tag, type, data_offset, data_size, size_offset, array_size)
|
||||||
|
|
||||||
|
#define PB_DATA_OFFSET_STATIC(htype, structname, fieldname) PB_DO ## htype(structname, fieldname)
|
||||||
|
#define PB_DATA_OFFSET_POINTER(htype, structname, fieldname) PB_DO ## htype(structname, fieldname)
|
||||||
|
#define PB_DATA_OFFSET_CALLBACK(htype, structname, fieldname) PB_DO ## htype(structname, fieldname)
|
||||||
|
#define PB_DO_PB_HTYPE_REQUIRED(structname, fieldname) offsetof(structname, fieldname)
|
||||||
|
#define PB_DO_PB_HTYPE_SINGULAR(structname, fieldname) offsetof(structname, fieldname)
|
||||||
|
#define PB_DO_PB_HTYPE_ONEOF(structname, fieldname) offsetof(structname, PB_ONEOF_NAME(FULL, fieldname))
|
||||||
|
#define PB_DO_PB_HTYPE_OPTIONAL(structname, fieldname) offsetof(structname, fieldname)
|
||||||
|
#define PB_DO_PB_HTYPE_REPEATED(structname, fieldname) offsetof(structname, fieldname)
|
||||||
|
#define PB_DO_PB_HTYPE_FIXARRAY(structname, fieldname) offsetof(structname, fieldname)
|
||||||
|
|
||||||
|
#define PB_SIZE_OFFSET_STATIC(htype, structname, fieldname) PB_SO ## htype(structname, fieldname)
|
||||||
|
#define PB_SIZE_OFFSET_POINTER(htype, structname, fieldname) PB_SO_PTR ## htype(structname, fieldname)
|
||||||
|
#define PB_SIZE_OFFSET_CALLBACK(htype, structname, fieldname) PB_SO_CB ## htype(structname, fieldname)
|
||||||
|
#define PB_SO_PB_HTYPE_REQUIRED(structname, fieldname) 0
|
||||||
|
#define PB_SO_PB_HTYPE_SINGULAR(structname, fieldname) 0
|
||||||
|
#define PB_SO_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF2(structname, PB_ONEOF_NAME(FULL, fieldname), PB_ONEOF_NAME(UNION, fieldname))
|
||||||
|
#define PB_SO_PB_HTYPE_ONEOF2(structname, fullname, unionname) PB_SO_PB_HTYPE_ONEOF3(structname, fullname, unionname)
|
||||||
|
#define PB_SO_PB_HTYPE_ONEOF3(structname, fullname, unionname) pb_delta(structname, fullname, which_ ## unionname)
|
||||||
|
#define PB_SO_PB_HTYPE_OPTIONAL(structname, fieldname) pb_delta(structname, fieldname, has_ ## fieldname)
|
||||||
|
#define PB_SO_PB_HTYPE_REPEATED(structname, fieldname) pb_delta(structname, fieldname, fieldname ## _count)
|
||||||
|
#define PB_SO_PB_HTYPE_FIXARRAY(structname, fieldname) 0
|
||||||
|
#define PB_SO_PTR_PB_HTYPE_REQUIRED(structname, fieldname) 0
|
||||||
|
#define PB_SO_PTR_PB_HTYPE_SINGULAR(structname, fieldname) 0
|
||||||
|
#define PB_SO_PTR_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF(structname, fieldname)
|
||||||
|
#define PB_SO_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) 0
|
||||||
|
#define PB_SO_PTR_PB_HTYPE_REPEATED(structname, fieldname) PB_SO_PB_HTYPE_REPEATED(structname, fieldname)
|
||||||
|
#define PB_SO_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) 0
|
||||||
|
#define PB_SO_CB_PB_HTYPE_REQUIRED(structname, fieldname) 0
|
||||||
|
#define PB_SO_CB_PB_HTYPE_SINGULAR(structname, fieldname) 0
|
||||||
|
#define PB_SO_CB_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF(structname, fieldname)
|
||||||
|
#define PB_SO_CB_PB_HTYPE_OPTIONAL(structname, fieldname) 0
|
||||||
|
#define PB_SO_CB_PB_HTYPE_REPEATED(structname, fieldname) 0
|
||||||
|
#define PB_SO_CB_PB_HTYPE_FIXARRAY(structname, fieldname) 0
|
||||||
|
|
||||||
|
#define PB_ARRAY_SIZE_STATIC(htype, structname, fieldname) PB_AS ## htype(structname, fieldname)
|
||||||
|
#define PB_ARRAY_SIZE_POINTER(htype, structname, fieldname) PB_AS_PTR ## htype(structname, fieldname)
|
||||||
|
#define PB_ARRAY_SIZE_CALLBACK(htype, structname, fieldname) 1
|
||||||
|
#define PB_AS_PB_HTYPE_REQUIRED(structname, fieldname) 1
|
||||||
|
#define PB_AS_PB_HTYPE_SINGULAR(structname, fieldname) 1
|
||||||
|
#define PB_AS_PB_HTYPE_OPTIONAL(structname, fieldname) 1
|
||||||
|
#define PB_AS_PB_HTYPE_ONEOF(structname, fieldname) 1
|
||||||
|
#define PB_AS_PB_HTYPE_REPEATED(structname, fieldname) pb_arraysize(structname, fieldname)
|
||||||
|
#define PB_AS_PB_HTYPE_FIXARRAY(structname, fieldname) pb_arraysize(structname, fieldname)
|
||||||
|
#define PB_AS_PTR_PB_HTYPE_REQUIRED(structname, fieldname) 1
|
||||||
|
#define PB_AS_PTR_PB_HTYPE_SINGULAR(structname, fieldname) 1
|
||||||
|
#define PB_AS_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) 1
|
||||||
|
#define PB_AS_PTR_PB_HTYPE_ONEOF(structname, fieldname) 1
|
||||||
|
#define PB_AS_PTR_PB_HTYPE_REPEATED(structname, fieldname) 1
|
||||||
|
#define PB_AS_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) pb_arraysize(structname, fieldname[0])
|
||||||
|
|
||||||
|
#define PB_DATA_SIZE_STATIC(htype, structname, fieldname) PB_DS ## htype(structname, fieldname)
|
||||||
|
#define PB_DATA_SIZE_POINTER(htype, structname, fieldname) PB_DS_PTR ## htype(structname, fieldname)
|
||||||
|
#define PB_DATA_SIZE_CALLBACK(htype, structname, fieldname) PB_DS_CB ## htype(structname, fieldname)
|
||||||
|
#define PB_DS_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname)
|
||||||
|
#define PB_DS_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname)
|
||||||
|
#define PB_DS_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname)
|
||||||
|
#define PB_DS_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname))
|
||||||
|
#define PB_DS_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname[0])
|
||||||
|
#define PB_DS_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname[0])
|
||||||
|
#define PB_DS_PTR_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname[0])
|
||||||
|
#define PB_DS_PTR_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname[0])
|
||||||
|
#define PB_DS_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname[0])
|
||||||
|
#define PB_DS_PTR_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname)[0])
|
||||||
|
#define PB_DS_PTR_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname[0])
|
||||||
|
#define PB_DS_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname[0][0])
|
||||||
|
#define PB_DS_CB_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname)
|
||||||
|
#define PB_DS_CB_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname)
|
||||||
|
#define PB_DS_CB_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname)
|
||||||
|
#define PB_DS_CB_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname))
|
||||||
|
#define PB_DS_CB_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname)
|
||||||
|
#define PB_DS_CB_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname)
|
||||||
|
|
||||||
|
#define PB_ONEOF_NAME(type, tuple) PB_EXPAND(PB_ONEOF_NAME_ ## type tuple)
|
||||||
|
#define PB_ONEOF_NAME_UNION(unionname,membername,fullname) unionname
|
||||||
|
#define PB_ONEOF_NAME_MEMBER(unionname,membername,fullname) membername
|
||||||
|
#define PB_ONEOF_NAME_FULL(unionname,membername,fullname) fullname
|
||||||
|
|
||||||
|
#define PB_GEN_SUBMSG_INFO(structname, atype, htype, ltype, fieldname, tag) \
|
||||||
|
PB_SUBMSG_INFO_ ## htype(_PB_LTYPE_ ## ltype, structname, fieldname)
|
||||||
|
|
||||||
|
#define PB_SUBMSG_INFO_REQUIRED(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE)
|
||||||
|
#define PB_SUBMSG_INFO_SINGULAR(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE)
|
||||||
|
#define PB_SUBMSG_INFO_OPTIONAL(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE)
|
||||||
|
#define PB_SUBMSG_INFO_ONEOF(ltype, structname, fieldname) PB_SUBMSG_INFO_ONEOF2(ltype, structname, PB_ONEOF_NAME(UNION, fieldname), PB_ONEOF_NAME(MEMBER, fieldname))
|
||||||
|
#define PB_SUBMSG_INFO_ONEOF2(ltype, structname, unionname, membername) PB_SUBMSG_INFO_ONEOF3(ltype, structname, unionname, membername)
|
||||||
|
#define PB_SUBMSG_INFO_ONEOF3(ltype, structname, unionname, membername) PB_SI ## ltype(structname ## _ ## unionname ## _ ## membername ## _MSGTYPE)
|
||||||
|
#define PB_SUBMSG_INFO_REPEATED(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE)
|
||||||
|
#define PB_SUBMSG_INFO_FIXARRAY(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE)
|
||||||
|
#define PB_SI_PB_LTYPE_BOOL(t)
|
||||||
|
#define PB_SI_PB_LTYPE_BYTES(t)
|
||||||
|
#define PB_SI_PB_LTYPE_DOUBLE(t)
|
||||||
|
#define PB_SI_PB_LTYPE_ENUM(t)
|
||||||
|
#define PB_SI_PB_LTYPE_UENUM(t)
|
||||||
|
#define PB_SI_PB_LTYPE_FIXED32(t)
|
||||||
|
#define PB_SI_PB_LTYPE_FIXED64(t)
|
||||||
|
#define PB_SI_PB_LTYPE_FLOAT(t)
|
||||||
|
#define PB_SI_PB_LTYPE_INT32(t)
|
||||||
|
#define PB_SI_PB_LTYPE_INT64(t)
|
||||||
|
#define PB_SI_PB_LTYPE_MESSAGE(t) PB_SUBMSG_DESCRIPTOR(t)
|
||||||
|
#define PB_SI_PB_LTYPE_MSG_W_CB(t) PB_SUBMSG_DESCRIPTOR(t)
|
||||||
|
#define PB_SI_PB_LTYPE_SFIXED32(t)
|
||||||
|
#define PB_SI_PB_LTYPE_SFIXED64(t)
|
||||||
|
#define PB_SI_PB_LTYPE_SINT32(t)
|
||||||
|
#define PB_SI_PB_LTYPE_SINT64(t)
|
||||||
|
#define PB_SI_PB_LTYPE_STRING(t)
|
||||||
|
#define PB_SI_PB_LTYPE_UINT32(t)
|
||||||
|
#define PB_SI_PB_LTYPE_UINT64(t)
|
||||||
|
#define PB_SI_PB_LTYPE_EXTENSION(t)
|
||||||
|
#define PB_SI_PB_LTYPE_FIXED_LENGTH_BYTES(t)
|
||||||
|
#define PB_SUBMSG_DESCRIPTOR(t) &(t ## _msg),
|
||||||
|
|
||||||
|
/* The field descriptors use a variable width format, with width of either
|
||||||
|
* 1, 2, 4 or 8 of 32-bit words. The two lowest bytes of the first byte always
|
||||||
|
* encode the descriptor size, 6 lowest bits of field tag number, and 8 bits
|
||||||
|
* of the field type.
|
||||||
|
*
|
||||||
|
* Descriptor size is encoded as 0 = 1 word, 1 = 2 words, 2 = 4 words, 3 = 8 words.
|
||||||
|
*
|
||||||
|
* Formats, listed starting with the least significant bit of the first word.
|
||||||
|
* 1 word: [2-bit len] [6-bit tag] [8-bit type] [8-bit data_offset] [4-bit size_offset] [4-bit data_size]
|
||||||
|
*
|
||||||
|
* 2 words: [2-bit len] [6-bit tag] [8-bit type] [12-bit array_size] [4-bit size_offset]
|
||||||
|
* [16-bit data_offset] [12-bit data_size] [4-bit tag>>6]
|
||||||
|
*
|
||||||
|
* 4 words: [2-bit len] [6-bit tag] [8-bit type] [16-bit array_size]
|
||||||
|
* [8-bit size_offset] [24-bit tag>>6]
|
||||||
|
* [32-bit data_offset]
|
||||||
|
* [32-bit data_size]
|
||||||
|
*
|
||||||
|
* 8 words: [2-bit len] [6-bit tag] [8-bit type] [16-bit reserved]
|
||||||
|
* [8-bit size_offset] [24-bit tag>>6]
|
||||||
|
* [32-bit data_offset]
|
||||||
|
* [32-bit data_size]
|
||||||
|
* [32-bit array_size]
|
||||||
|
* [32-bit reserved]
|
||||||
|
* [32-bit reserved]
|
||||||
|
* [32-bit reserved]
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define PB_FIELDINFO_1(tag, type, data_offset, data_size, size_offset, array_size) \
|
||||||
|
(0 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(data_offset) & 0xFF) << 16) | \
|
||||||
|
(((uint32_t)(size_offset) & 0x0F) << 24) | (((uint32_t)(data_size) & 0x0F) << 28)),
|
||||||
|
|
||||||
|
#define PB_FIELDINFO_2(tag, type, data_offset, data_size, size_offset, array_size) \
|
||||||
|
(1 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(array_size) & 0xFFF) << 16) | (((uint32_t)(size_offset) & 0x0F) << 28)), \
|
||||||
|
(((uint32_t)(data_offset) & 0xFFFF) | (((uint32_t)(data_size) & 0xFFF) << 16) | (((uint32_t)(tag) & 0x3c0) << 22)),
|
||||||
|
|
||||||
|
#define PB_FIELDINFO_4(tag, type, data_offset, data_size, size_offset, array_size) \
|
||||||
|
(2 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(array_size) & 0xFFFF) << 16)), \
|
||||||
|
((uint32_t)(int_least8_t)(size_offset) | (((uint32_t)(tag) << 2) & 0xFFFFFF00)), \
|
||||||
|
(data_offset), (data_size),
|
||||||
|
|
||||||
|
#define PB_FIELDINFO_8(tag, type, data_offset, data_size, size_offset, array_size) \
|
||||||
|
(3 | (((tag) << 2) & 0xFF) | ((type) << 8)), \
|
||||||
|
((uint32_t)(int_least8_t)(size_offset) | (((uint32_t)(tag) << 2) & 0xFFFFFF00)), \
|
||||||
|
(data_offset), (data_size), (array_size), 0, 0, 0,
|
||||||
|
|
||||||
|
/* These assertions verify that the field information fits in the allocated space.
|
||||||
|
* The generator tries to automatically determine the correct width that can fit all
|
||||||
|
* data associated with a message. These asserts will fail only if there has been a
|
||||||
|
* problem in the automatic logic - this may be worth reporting as a bug. As a workaround,
|
||||||
|
* you can increase the descriptor width by defining PB_FIELDINFO_WIDTH or by setting
|
||||||
|
* descriptorsize option in .options file.
|
||||||
|
*/
|
||||||
|
#define PB_FITS(value,bits) ((uint32_t)(value) < ((uint32_t)1<<bits))
|
||||||
|
#define PB_FIELDINFO_ASSERT_1(tag, type, data_offset, data_size, size_offset, array_size) \
|
||||||
|
PB_STATIC_ASSERT(PB_FITS(tag,6) && PB_FITS(data_offset,8) && PB_FITS(size_offset,4) && PB_FITS(data_size,4) && PB_FITS(array_size,1), FIELDINFO_DOES_NOT_FIT_width1_field ## tag)
|
||||||
|
|
||||||
|
#define PB_FIELDINFO_ASSERT_2(tag, type, data_offset, data_size, size_offset, array_size) \
|
||||||
|
PB_STATIC_ASSERT(PB_FITS(tag,10) && PB_FITS(data_offset,16) && PB_FITS(size_offset,4) && PB_FITS(data_size,12) && PB_FITS(array_size,12), FIELDINFO_DOES_NOT_FIT_width2_field ## tag)
|
||||||
|
|
||||||
|
#ifndef PB_FIELD_32BIT
|
||||||
|
/* Maximum field sizes are still 16-bit if pb_size_t is 16-bit */
|
||||||
|
#define PB_FIELDINFO_ASSERT_4(tag, type, data_offset, data_size, size_offset, array_size) \
|
||||||
|
PB_STATIC_ASSERT(PB_FITS(tag,16) && PB_FITS(data_offset,16) && PB_FITS((int_least8_t)size_offset,8) && PB_FITS(data_size,16) && PB_FITS(array_size,16), FIELDINFO_DOES_NOT_FIT_width4_field ## tag)
|
||||||
|
|
||||||
|
#define PB_FIELDINFO_ASSERT_8(tag, type, data_offset, data_size, size_offset, array_size) \
|
||||||
|
PB_STATIC_ASSERT(PB_FITS(tag,16) && PB_FITS(data_offset,16) && PB_FITS((int_least8_t)size_offset,8) && PB_FITS(data_size,16) && PB_FITS(array_size,16), FIELDINFO_DOES_NOT_FIT_width8_field ## tag)
|
||||||
|
#else
|
||||||
|
/* Up to 32-bit fields supported.
|
||||||
|
* Note that the checks are against 31 bits to avoid compiler warnings about shift wider than type in the test.
|
||||||
|
* I expect that there is no reasonable use for >2GB messages with nanopb anyway.
|
||||||
|
*/
|
||||||
|
#define PB_FIELDINFO_ASSERT_4(tag, type, data_offset, data_size, size_offset, array_size) \
|
||||||
|
PB_STATIC_ASSERT(PB_FITS(tag,30) && PB_FITS(data_offset,31) && PB_FITS(size_offset,8) && PB_FITS(data_size,31) && PB_FITS(array_size,16), FIELDINFO_DOES_NOT_FIT_width4_field ## tag)
|
||||||
|
|
||||||
|
#define PB_FIELDINFO_ASSERT_8(tag, type, data_offset, data_size, size_offset, array_size) \
|
||||||
|
PB_STATIC_ASSERT(PB_FITS(tag,30) && PB_FITS(data_offset,31) && PB_FITS(size_offset,8) && PB_FITS(data_size,31) && PB_FITS(array_size,31), FIELDINFO_DOES_NOT_FIT_width8_field ## tag)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* Automatic picking of FIELDINFO width:
|
||||||
|
* Uses width 1 when possible, otherwise resorts to width 2.
|
||||||
|
* This is used when PB_BIND() is called with "AUTO" as the argument.
|
||||||
|
* The generator will give explicit size argument when it knows that a message
|
||||||
|
* structure grows beyond 1-word format limits.
|
||||||
|
*/
|
||||||
|
#define PB_FIELDINFO_WIDTH_AUTO(atype, htype, ltype) PB_FI_WIDTH ## atype(htype, ltype)
|
||||||
|
#define PB_FI_WIDTH_PB_ATYPE_STATIC(htype, ltype) PB_FI_WIDTH ## htype(ltype)
|
||||||
|
#define PB_FI_WIDTH_PB_ATYPE_POINTER(htype, ltype) PB_FI_WIDTH ## htype(ltype)
|
||||||
|
#define PB_FI_WIDTH_PB_ATYPE_CALLBACK(htype, ltype) 2
|
||||||
|
#define PB_FI_WIDTH_PB_HTYPE_REQUIRED(ltype) PB_FI_WIDTH ## ltype
|
||||||
|
#define PB_FI_WIDTH_PB_HTYPE_SINGULAR(ltype) PB_FI_WIDTH ## ltype
|
||||||
|
#define PB_FI_WIDTH_PB_HTYPE_OPTIONAL(ltype) PB_FI_WIDTH ## ltype
|
||||||
|
#define PB_FI_WIDTH_PB_HTYPE_ONEOF(ltype) PB_FI_WIDTH ## ltype
|
||||||
|
#define PB_FI_WIDTH_PB_HTYPE_REPEATED(ltype) 2
|
||||||
|
#define PB_FI_WIDTH_PB_HTYPE_FIXARRAY(ltype) 2
|
||||||
|
#define PB_FI_WIDTH_PB_LTYPE_BOOL 1
|
||||||
|
#define PB_FI_WIDTH_PB_LTYPE_BYTES 2
|
||||||
|
#define PB_FI_WIDTH_PB_LTYPE_DOUBLE 1
|
||||||
|
#define PB_FI_WIDTH_PB_LTYPE_ENUM 1
|
||||||
|
#define PB_FI_WIDTH_PB_LTYPE_UENUM 1
|
||||||
|
#define PB_FI_WIDTH_PB_LTYPE_FIXED32 1
|
||||||
|
#define PB_FI_WIDTH_PB_LTYPE_FIXED64 1
|
||||||
|
#define PB_FI_WIDTH_PB_LTYPE_FLOAT 1
|
||||||
|
#define PB_FI_WIDTH_PB_LTYPE_INT32 1
|
||||||
|
#define PB_FI_WIDTH_PB_LTYPE_INT64 1
|
||||||
|
#define PB_FI_WIDTH_PB_LTYPE_MESSAGE 2
|
||||||
|
#define PB_FI_WIDTH_PB_LTYPE_MSG_W_CB 2
|
||||||
|
#define PB_FI_WIDTH_PB_LTYPE_SFIXED32 1
|
||||||
|
#define PB_FI_WIDTH_PB_LTYPE_SFIXED64 1
|
||||||
|
#define PB_FI_WIDTH_PB_LTYPE_SINT32 1
|
||||||
|
#define PB_FI_WIDTH_PB_LTYPE_SINT64 1
|
||||||
|
#define PB_FI_WIDTH_PB_LTYPE_STRING 2
|
||||||
|
#define PB_FI_WIDTH_PB_LTYPE_UINT32 1
|
||||||
|
#define PB_FI_WIDTH_PB_LTYPE_UINT64 1
|
||||||
|
#define PB_FI_WIDTH_PB_LTYPE_EXTENSION 1
|
||||||
|
#define PB_FI_WIDTH_PB_LTYPE_FIXED_LENGTH_BYTES 2
|
||||||
|
|
||||||
|
/* The mapping from protobuf types to LTYPEs is done using these macros. */
|
||||||
|
#define PB_LTYPE_MAP_BOOL PB_LTYPE_BOOL
|
||||||
|
#define PB_LTYPE_MAP_BYTES PB_LTYPE_BYTES
|
||||||
|
#define PB_LTYPE_MAP_DOUBLE PB_LTYPE_FIXED64
|
||||||
|
#define PB_LTYPE_MAP_ENUM PB_LTYPE_VARINT
|
||||||
|
#define PB_LTYPE_MAP_UENUM PB_LTYPE_UVARINT
|
||||||
|
#define PB_LTYPE_MAP_FIXED32 PB_LTYPE_FIXED32
|
||||||
|
#define PB_LTYPE_MAP_FIXED64 PB_LTYPE_FIXED64
|
||||||
|
#define PB_LTYPE_MAP_FLOAT PB_LTYPE_FIXED32
|
||||||
|
#define PB_LTYPE_MAP_INT32 PB_LTYPE_VARINT
|
||||||
|
#define PB_LTYPE_MAP_INT64 PB_LTYPE_VARINT
|
||||||
|
#define PB_LTYPE_MAP_MESSAGE PB_LTYPE_SUBMESSAGE
|
||||||
|
#define PB_LTYPE_MAP_MSG_W_CB PB_LTYPE_SUBMSG_W_CB
|
||||||
|
#define PB_LTYPE_MAP_SFIXED32 PB_LTYPE_FIXED32
|
||||||
|
#define PB_LTYPE_MAP_SFIXED64 PB_LTYPE_FIXED64
|
||||||
|
#define PB_LTYPE_MAP_SINT32 PB_LTYPE_SVARINT
|
||||||
|
#define PB_LTYPE_MAP_SINT64 PB_LTYPE_SVARINT
|
||||||
|
#define PB_LTYPE_MAP_STRING PB_LTYPE_STRING
|
||||||
|
#define PB_LTYPE_MAP_UINT32 PB_LTYPE_UVARINT
|
||||||
|
#define PB_LTYPE_MAP_UINT64 PB_LTYPE_UVARINT
|
||||||
|
#define PB_LTYPE_MAP_EXTENSION PB_LTYPE_EXTENSION
|
||||||
|
#define PB_LTYPE_MAP_FIXED_LENGTH_BYTES PB_LTYPE_FIXED_LENGTH_BYTES
|
||||||
|
|
||||||
|
/* These macros are used for giving out error messages.
|
||||||
|
* They are mostly a debugging aid; the main error information
|
||||||
|
* is the true/false return value from functions.
|
||||||
|
* Some code space can be saved by disabling the error
|
||||||
|
* messages if not used.
|
||||||
|
*
|
||||||
|
* PB_SET_ERROR() sets the error message if none has been set yet.
|
||||||
|
* msg must be a constant string literal.
|
||||||
|
* PB_GET_ERROR() always returns a pointer to a string.
|
||||||
|
* PB_RETURN_ERROR() sets the error and returns false from current
|
||||||
|
* function.
|
||||||
|
*/
|
||||||
|
#ifdef PB_NO_ERRMSG
|
||||||
|
#define PB_SET_ERROR(stream, msg) PB_UNUSED(stream)
|
||||||
|
#define PB_GET_ERROR(stream) "(errmsg disabled)"
|
||||||
|
#else
|
||||||
|
#define PB_SET_ERROR(stream, msg) (stream->errmsg = (stream)->errmsg ? (stream)->errmsg : (msg))
|
||||||
|
#define PB_GET_ERROR(stream) ((stream)->errmsg ? (stream)->errmsg : "(none)")
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define PB_RETURN_ERROR(stream, msg) return PB_SET_ERROR(stream, msg), false
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#if __cplusplus >= 201103L
|
||||||
|
#define PB_CONSTEXPR constexpr
|
||||||
|
#else // __cplusplus >= 201103L
|
||||||
|
#define PB_CONSTEXPR
|
||||||
|
#endif // __cplusplus >= 201103L
|
||||||
|
|
||||||
|
#if __cplusplus >= 201703L
|
||||||
|
#define PB_INLINE_CONSTEXPR inline constexpr
|
||||||
|
#else // __cplusplus >= 201703L
|
||||||
|
#define PB_INLINE_CONSTEXPR PB_CONSTEXPR
|
||||||
|
#endif // __cplusplus >= 201703L
|
||||||
|
|
||||||
|
namespace nanopb {
|
||||||
|
// Each type will be partially specialized by the generator.
|
||||||
|
template <typename GenMessageT> struct MessageDescriptor;
|
||||||
|
} // namespace nanopb
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
345
components/nanopb/pb_common.c
Normal file
345
components/nanopb/pb_common.c
Normal file
@ -0,0 +1,345 @@
|
|||||||
|
/* pb_common.c: Common support functions for pb_encode.c and pb_decode.c.
|
||||||
|
*
|
||||||
|
* 2014 Petteri Aimonen <jpa@kapsi.fi>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "pb_common.h"
|
||||||
|
|
||||||
|
static bool load_descriptor_values(pb_field_iter_t *iter)
|
||||||
|
{
|
||||||
|
uint32_t word0;
|
||||||
|
uint32_t data_offset;
|
||||||
|
uint_least8_t format;
|
||||||
|
int_least8_t size_offset;
|
||||||
|
|
||||||
|
if (iter->index >= iter->descriptor->field_count)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
word0 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]);
|
||||||
|
format = word0 & 3;
|
||||||
|
iter->tag = (pb_size_t)((word0 >> 2) & 0x3F);
|
||||||
|
iter->type = (pb_type_t)((word0 >> 8) & 0xFF);
|
||||||
|
|
||||||
|
if (format == 0)
|
||||||
|
{
|
||||||
|
/* 1-word format */
|
||||||
|
iter->array_size = 1;
|
||||||
|
size_offset = (int_least8_t)((word0 >> 24) & 0x0F);
|
||||||
|
data_offset = (word0 >> 16) & 0xFF;
|
||||||
|
iter->data_size = (pb_size_t)((word0 >> 28) & 0x0F);
|
||||||
|
}
|
||||||
|
else if (format == 1)
|
||||||
|
{
|
||||||
|
/* 2-word format */
|
||||||
|
uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]);
|
||||||
|
|
||||||
|
iter->array_size = (pb_size_t)((word0 >> 16) & 0x0FFF);
|
||||||
|
iter->tag = (pb_size_t)(iter->tag | ((word1 >> 28) << 6));
|
||||||
|
size_offset = (int_least8_t)((word0 >> 28) & 0x0F);
|
||||||
|
data_offset = word1 & 0xFFFF;
|
||||||
|
iter->data_size = (pb_size_t)((word1 >> 16) & 0x0FFF);
|
||||||
|
}
|
||||||
|
else if (format == 2)
|
||||||
|
{
|
||||||
|
/* 4-word format */
|
||||||
|
uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]);
|
||||||
|
uint32_t word2 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 2]);
|
||||||
|
uint32_t word3 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 3]);
|
||||||
|
|
||||||
|
iter->array_size = (pb_size_t)(word0 >> 16);
|
||||||
|
iter->tag = (pb_size_t)(iter->tag | ((word1 >> 8) << 6));
|
||||||
|
size_offset = (int_least8_t)(word1 & 0xFF);
|
||||||
|
data_offset = word2;
|
||||||
|
iter->data_size = (pb_size_t)word3;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* 8-word format */
|
||||||
|
uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]);
|
||||||
|
uint32_t word2 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 2]);
|
||||||
|
uint32_t word3 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 3]);
|
||||||
|
uint32_t word4 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 4]);
|
||||||
|
|
||||||
|
iter->array_size = (pb_size_t)word4;
|
||||||
|
iter->tag = (pb_size_t)(iter->tag | ((word1 >> 8) << 6));
|
||||||
|
size_offset = (int_least8_t)(word1 & 0xFF);
|
||||||
|
data_offset = word2;
|
||||||
|
iter->data_size = (pb_size_t)word3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!iter->message)
|
||||||
|
{
|
||||||
|
/* Avoid doing arithmetic on null pointers, it is undefined */
|
||||||
|
iter->pField = NULL;
|
||||||
|
iter->pSize = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
iter->pField = (char*)iter->message + data_offset;
|
||||||
|
|
||||||
|
if (size_offset)
|
||||||
|
{
|
||||||
|
iter->pSize = (char*)iter->pField - size_offset;
|
||||||
|
}
|
||||||
|
else if (PB_HTYPE(iter->type) == PB_HTYPE_REPEATED &&
|
||||||
|
(PB_ATYPE(iter->type) == PB_ATYPE_STATIC ||
|
||||||
|
PB_ATYPE(iter->type) == PB_ATYPE_POINTER))
|
||||||
|
{
|
||||||
|
/* Fixed count array */
|
||||||
|
iter->pSize = &iter->array_size;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
iter->pSize = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PB_ATYPE(iter->type) == PB_ATYPE_POINTER && iter->pField != NULL)
|
||||||
|
{
|
||||||
|
iter->pData = *(void**)iter->pField;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
iter->pData = iter->pField;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PB_LTYPE_IS_SUBMSG(iter->type))
|
||||||
|
{
|
||||||
|
iter->submsg_desc = iter->descriptor->submsg_info[iter->submessage_index];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
iter->submsg_desc = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void advance_iterator(pb_field_iter_t *iter)
|
||||||
|
{
|
||||||
|
iter->index++;
|
||||||
|
|
||||||
|
if (iter->index >= iter->descriptor->field_count)
|
||||||
|
{
|
||||||
|
/* Restart */
|
||||||
|
iter->index = 0;
|
||||||
|
iter->field_info_index = 0;
|
||||||
|
iter->submessage_index = 0;
|
||||||
|
iter->required_field_index = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Increment indexes based on previous field type.
|
||||||
|
* All field info formats have the following fields:
|
||||||
|
* - lowest 2 bits tell the amount of words in the descriptor (2^n words)
|
||||||
|
* - bits 2..7 give the lowest bits of tag number.
|
||||||
|
* - bits 8..15 give the field type.
|
||||||
|
*/
|
||||||
|
uint32_t prev_descriptor = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]);
|
||||||
|
pb_type_t prev_type = (prev_descriptor >> 8) & 0xFF;
|
||||||
|
pb_size_t descriptor_len = (pb_size_t)(1 << (prev_descriptor & 3));
|
||||||
|
|
||||||
|
iter->field_info_index = (pb_size_t)(iter->field_info_index + descriptor_len);
|
||||||
|
|
||||||
|
if (PB_HTYPE(prev_type) == PB_HTYPE_REQUIRED)
|
||||||
|
{
|
||||||
|
iter->required_field_index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PB_LTYPE_IS_SUBMSG(prev_type))
|
||||||
|
{
|
||||||
|
iter->submessage_index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pb_field_iter_begin(pb_field_iter_t *iter, const pb_msgdesc_t *desc, void *message)
|
||||||
|
{
|
||||||
|
memset(iter, 0, sizeof(*iter));
|
||||||
|
|
||||||
|
iter->descriptor = desc;
|
||||||
|
iter->message = message;
|
||||||
|
|
||||||
|
return load_descriptor_values(iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pb_field_iter_begin_extension(pb_field_iter_t *iter, pb_extension_t *extension)
|
||||||
|
{
|
||||||
|
const pb_msgdesc_t *msg = (const pb_msgdesc_t*)extension->type->arg;
|
||||||
|
bool status;
|
||||||
|
|
||||||
|
uint32_t word0 = PB_PROGMEM_READU32(msg->field_info[0]);
|
||||||
|
if (PB_ATYPE(word0 >> 8) == PB_ATYPE_POINTER)
|
||||||
|
{
|
||||||
|
/* For pointer extensions, the pointer is stored directly
|
||||||
|
* in the extension structure. This avoids having an extra
|
||||||
|
* indirection. */
|
||||||
|
status = pb_field_iter_begin(iter, msg, &extension->dest);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
status = pb_field_iter_begin(iter, msg, extension->dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
iter->pSize = &extension->found;
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pb_field_iter_next(pb_field_iter_t *iter)
|
||||||
|
{
|
||||||
|
advance_iterator(iter);
|
||||||
|
(void)load_descriptor_values(iter);
|
||||||
|
return iter->index != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag)
|
||||||
|
{
|
||||||
|
if (iter->tag == tag)
|
||||||
|
{
|
||||||
|
return true; /* Nothing to do, correct field already. */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pb_size_t start = iter->index;
|
||||||
|
uint32_t fieldinfo;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
/* Advance iterator but don't load values yet */
|
||||||
|
advance_iterator(iter);
|
||||||
|
|
||||||
|
/* Do fast check for tag number match */
|
||||||
|
fieldinfo = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]);
|
||||||
|
|
||||||
|
if (((fieldinfo >> 2) & 0x3F) == (tag & 0x3F))
|
||||||
|
{
|
||||||
|
/* Good candidate, check further */
|
||||||
|
(void)load_descriptor_values(iter);
|
||||||
|
|
||||||
|
if (iter->tag == tag &&
|
||||||
|
PB_LTYPE(iter->type) != PB_LTYPE_EXTENSION)
|
||||||
|
{
|
||||||
|
/* Found it */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (iter->index != start);
|
||||||
|
|
||||||
|
/* Searched all the way back to start, and found nothing. */
|
||||||
|
(void)load_descriptor_values(iter);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *pb_const_cast(const void *p)
|
||||||
|
{
|
||||||
|
/* Note: this casts away const, in order to use the common field iterator
|
||||||
|
* logic for both encoding and decoding. The cast is done using union
|
||||||
|
* to avoid spurious compiler warnings. */
|
||||||
|
union {
|
||||||
|
void *p1;
|
||||||
|
const void *p2;
|
||||||
|
} t;
|
||||||
|
t.p2 = p;
|
||||||
|
return t.p1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pb_field_iter_begin_const(pb_field_iter_t *iter, const pb_msgdesc_t *desc, const void *message)
|
||||||
|
{
|
||||||
|
return pb_field_iter_begin(iter, desc, pb_const_cast(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pb_field_iter_begin_extension_const(pb_field_iter_t *iter, const pb_extension_t *extension)
|
||||||
|
{
|
||||||
|
return pb_field_iter_begin_extension(iter, (pb_extension_t*)pb_const_cast(extension));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pb_default_field_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field)
|
||||||
|
{
|
||||||
|
if (field->data_size == sizeof(pb_callback_t))
|
||||||
|
{
|
||||||
|
pb_callback_t *pCallback = (pb_callback_t*)field->pData;
|
||||||
|
|
||||||
|
if (pCallback != NULL)
|
||||||
|
{
|
||||||
|
if (istream != NULL && pCallback->funcs.decode != NULL)
|
||||||
|
{
|
||||||
|
return pCallback->funcs.decode(istream, field, &pCallback->arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ostream != NULL && pCallback->funcs.encode != NULL)
|
||||||
|
{
|
||||||
|
return pCallback->funcs.encode(ostream, field, &pCallback->arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true; /* Success, but didn't do anything */
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef PB_VALIDATE_UTF8
|
||||||
|
|
||||||
|
/* This function checks whether a string is valid UTF-8 text.
|
||||||
|
*
|
||||||
|
* Algorithm is adapted from https://www.cl.cam.ac.uk/~mgk25/ucs/utf8_check.c
|
||||||
|
* Original copyright: Markus Kuhn <http://www.cl.cam.ac.uk/~mgk25/> 2005-03-30
|
||||||
|
* Licensed under "Short code license", which allows use under MIT license or
|
||||||
|
* any compatible with it.
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool pb_validate_utf8(const char *str)
|
||||||
|
{
|
||||||
|
const pb_byte_t *s = (const pb_byte_t*)str;
|
||||||
|
while (*s)
|
||||||
|
{
|
||||||
|
if (*s < 0x80)
|
||||||
|
{
|
||||||
|
/* 0xxxxxxx */
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
else if ((s[0] & 0xe0) == 0xc0)
|
||||||
|
{
|
||||||
|
/* 110XXXXx 10xxxxxx */
|
||||||
|
if ((s[1] & 0xc0) != 0x80 ||
|
||||||
|
(s[0] & 0xfe) == 0xc0) /* overlong? */
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
s += 2;
|
||||||
|
}
|
||||||
|
else if ((s[0] & 0xf0) == 0xe0)
|
||||||
|
{
|
||||||
|
/* 1110XXXX 10Xxxxxx 10xxxxxx */
|
||||||
|
if ((s[1] & 0xc0) != 0x80 ||
|
||||||
|
(s[2] & 0xc0) != 0x80 ||
|
||||||
|
(s[0] == 0xe0 && (s[1] & 0xe0) == 0x80) || /* overlong? */
|
||||||
|
(s[0] == 0xed && (s[1] & 0xe0) == 0xa0) || /* surrogate? */
|
||||||
|
(s[0] == 0xef && s[1] == 0xbf &&
|
||||||
|
(s[2] & 0xfe) == 0xbe)) /* U+FFFE or U+FFFF? */
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
s += 3;
|
||||||
|
}
|
||||||
|
else if ((s[0] & 0xf8) == 0xf0)
|
||||||
|
{
|
||||||
|
/* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */
|
||||||
|
if ((s[1] & 0xc0) != 0x80 ||
|
||||||
|
(s[2] & 0xc0) != 0x80 ||
|
||||||
|
(s[3] & 0xc0) != 0x80 ||
|
||||||
|
(s[0] == 0xf0 && (s[1] & 0xf0) == 0x80) || /* overlong? */
|
||||||
|
(s[0] == 0xf4 && s[1] > 0x8f) || s[0] > 0xf4) /* > U+10FFFF? */
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
s += 4;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
45
components/nanopb/pb_common.h
Normal file
45
components/nanopb/pb_common.h
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/* pb_common.h: Common support functions for pb_encode.c and pb_decode.c.
|
||||||
|
* These functions are rarely needed by applications directly.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PB_COMMON_H_INCLUDED
|
||||||
|
#define PB_COMMON_H_INCLUDED
|
||||||
|
|
||||||
|
#include "pb.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Initialize the field iterator structure to beginning.
|
||||||
|
* Returns false if the message type is empty. */
|
||||||
|
bool pb_field_iter_begin(pb_field_iter_t *iter, const pb_msgdesc_t *desc, void *message);
|
||||||
|
|
||||||
|
/* Get a field iterator for extension field. */
|
||||||
|
bool pb_field_iter_begin_extension(pb_field_iter_t *iter, pb_extension_t *extension);
|
||||||
|
|
||||||
|
/* Same as pb_field_iter_begin(), but for const message pointer.
|
||||||
|
* Note that the pointers in pb_field_iter_t will be non-const but shouldn't
|
||||||
|
* be written to when using these functions. */
|
||||||
|
bool pb_field_iter_begin_const(pb_field_iter_t *iter, const pb_msgdesc_t *desc, const void *message);
|
||||||
|
bool pb_field_iter_begin_extension_const(pb_field_iter_t *iter, const pb_extension_t *extension);
|
||||||
|
|
||||||
|
/* Advance the iterator to the next field.
|
||||||
|
* Returns false when the iterator wraps back to the first field. */
|
||||||
|
bool pb_field_iter_next(pb_field_iter_t *iter);
|
||||||
|
|
||||||
|
/* Advance the iterator until it points at a field with the given tag.
|
||||||
|
* Returns false if no such field exists. */
|
||||||
|
bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag);
|
||||||
|
|
||||||
|
#ifdef PB_VALIDATE_UTF8
|
||||||
|
/* Validate UTF-8 text string */
|
||||||
|
bool pb_validate_utf8(const char *s);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
1745
components/nanopb/pb_decode.c
Normal file
1745
components/nanopb/pb_decode.c
Normal file
File diff suppressed because it is too large
Load Diff
196
components/nanopb/pb_decode.h
Normal file
196
components/nanopb/pb_decode.h
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
/* pb_decode.h: Functions to decode protocol buffers. Depends on pb_decode.c.
|
||||||
|
* The main function is pb_decode. You also need an input stream, and the
|
||||||
|
* field descriptions created by nanopb_generator.py.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PB_DECODE_H_INCLUDED
|
||||||
|
#define PB_DECODE_H_INCLUDED
|
||||||
|
|
||||||
|
#include "pb.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Structure for defining custom input streams. You will need to provide
|
||||||
|
* a callback function to read the bytes from your storage, which can be
|
||||||
|
* for example a file or a network socket.
|
||||||
|
*
|
||||||
|
* The callback must conform to these rules:
|
||||||
|
*
|
||||||
|
* 1) Return false on IO errors. This will cause decoding to abort.
|
||||||
|
* 2) You can use state to store your own data (e.g. buffer pointer),
|
||||||
|
* and rely on pb_read to verify that no-body reads past bytes_left.
|
||||||
|
* 3) Your callback may be used with substreams, in which case bytes_left
|
||||||
|
* is different than from the main stream. Don't use bytes_left to compute
|
||||||
|
* any pointers.
|
||||||
|
*/
|
||||||
|
struct pb_istream_s
|
||||||
|
{
|
||||||
|
#ifdef PB_BUFFER_ONLY
|
||||||
|
/* Callback pointer is not used in buffer-only configuration.
|
||||||
|
* Having an int pointer here allows binary compatibility but
|
||||||
|
* gives an error if someone tries to assign callback function.
|
||||||
|
*/
|
||||||
|
int *callback;
|
||||||
|
#else
|
||||||
|
bool (*callback)(pb_istream_t *stream, pb_byte_t *buf, size_t count);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void *state; /* Free field for use by callback implementation */
|
||||||
|
size_t bytes_left;
|
||||||
|
|
||||||
|
#ifndef PB_NO_ERRMSG
|
||||||
|
const char *errmsg;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifndef PB_NO_ERRMSG
|
||||||
|
#define PB_ISTREAM_EMPTY {0,0,0,0}
|
||||||
|
#else
|
||||||
|
#define PB_ISTREAM_EMPTY {0,0,0}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/***************************
|
||||||
|
* Main decoding functions *
|
||||||
|
***************************/
|
||||||
|
|
||||||
|
/* Decode a single protocol buffers message from input stream into a C structure.
|
||||||
|
* Returns true on success, false on any failure.
|
||||||
|
* The actual struct pointed to by dest must match the description in fields.
|
||||||
|
* Callback fields of the destination structure must be initialized by caller.
|
||||||
|
* All other fields will be initialized by this function.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
* MyMessage msg = {};
|
||||||
|
* uint8_t buffer[64];
|
||||||
|
* pb_istream_t stream;
|
||||||
|
*
|
||||||
|
* // ... read some data into buffer ...
|
||||||
|
*
|
||||||
|
* stream = pb_istream_from_buffer(buffer, count);
|
||||||
|
* pb_decode(&stream, MyMessage_fields, &msg);
|
||||||
|
*/
|
||||||
|
bool pb_decode(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct);
|
||||||
|
|
||||||
|
/* Extended version of pb_decode, with several options to control
|
||||||
|
* the decoding process:
|
||||||
|
*
|
||||||
|
* PB_DECODE_NOINIT: Do not initialize the fields to default values.
|
||||||
|
* This is slightly faster if you do not need the default
|
||||||
|
* values and instead initialize the structure to 0 using
|
||||||
|
* e.g. memset(). This can also be used for merging two
|
||||||
|
* messages, i.e. combine already existing data with new
|
||||||
|
* values.
|
||||||
|
*
|
||||||
|
* PB_DECODE_DELIMITED: Input message starts with the message size as varint.
|
||||||
|
* Corresponds to parseDelimitedFrom() in Google's
|
||||||
|
* protobuf API.
|
||||||
|
*
|
||||||
|
* PB_DECODE_NULLTERMINATED: Stop reading when field tag is read as 0. This allows
|
||||||
|
* reading null terminated messages.
|
||||||
|
* NOTE: Until nanopb-0.4.0, pb_decode() also allows
|
||||||
|
* null-termination. This behaviour is not supported in
|
||||||
|
* most other protobuf implementations, so PB_DECODE_DELIMITED
|
||||||
|
* is a better option for compatibility.
|
||||||
|
*
|
||||||
|
* Multiple flags can be combined with bitwise or (| operator)
|
||||||
|
*/
|
||||||
|
#define PB_DECODE_NOINIT 0x01U
|
||||||
|
#define PB_DECODE_DELIMITED 0x02U
|
||||||
|
#define PB_DECODE_NULLTERMINATED 0x04U
|
||||||
|
bool pb_decode_ex(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct, unsigned int flags);
|
||||||
|
|
||||||
|
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
|
||||||
|
#define pb_decode_noinit(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_NOINIT)
|
||||||
|
#define pb_decode_delimited(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_DELIMITED)
|
||||||
|
#define pb_decode_delimited_noinit(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_DELIMITED | PB_DECODE_NOINIT)
|
||||||
|
#define pb_decode_nullterminated(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_NULLTERMINATED)
|
||||||
|
|
||||||
|
#ifdef PB_ENABLE_MALLOC
|
||||||
|
/* Release any allocated pointer fields. If you use dynamic allocation, you should
|
||||||
|
* call this for any successfully decoded message when you are done with it. If
|
||||||
|
* pb_decode() returns with an error, the message is already released.
|
||||||
|
*/
|
||||||
|
void pb_release(const pb_msgdesc_t *fields, void *dest_struct);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
* Functions for manipulating streams *
|
||||||
|
**************************************/
|
||||||
|
|
||||||
|
/* Create an input stream for reading from a memory buffer.
|
||||||
|
*
|
||||||
|
* msglen should be the actual length of the message, not the full size of
|
||||||
|
* allocated buffer.
|
||||||
|
*
|
||||||
|
* Alternatively, you can use a custom stream that reads directly from e.g.
|
||||||
|
* a file or a network socket.
|
||||||
|
*/
|
||||||
|
pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t msglen);
|
||||||
|
|
||||||
|
/* Function to read from a pb_istream_t. You can use this if you need to
|
||||||
|
* read some custom header data, or to read data in field callbacks.
|
||||||
|
*/
|
||||||
|
bool pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count);
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
* Helper functions for writing field callbacks *
|
||||||
|
************************************************/
|
||||||
|
|
||||||
|
/* Decode the tag for the next field in the stream. Gives the wire type and
|
||||||
|
* field tag. At end of the message, returns false and sets eof to true. */
|
||||||
|
bool pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof);
|
||||||
|
|
||||||
|
/* Skip the field payload data, given the wire type. */
|
||||||
|
bool pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type);
|
||||||
|
|
||||||
|
/* Decode an integer in the varint format. This works for enum, int32,
|
||||||
|
* int64, uint32 and uint64 field types. */
|
||||||
|
#ifndef PB_WITHOUT_64BIT
|
||||||
|
bool pb_decode_varint(pb_istream_t *stream, uint64_t *dest);
|
||||||
|
#else
|
||||||
|
#define pb_decode_varint pb_decode_varint32
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Decode an integer in the varint format. This works for enum, int32,
|
||||||
|
* and uint32 field types. */
|
||||||
|
bool pb_decode_varint32(pb_istream_t *stream, uint32_t *dest);
|
||||||
|
|
||||||
|
/* Decode a bool value in varint format. */
|
||||||
|
bool pb_decode_bool(pb_istream_t *stream, bool *dest);
|
||||||
|
|
||||||
|
/* Decode an integer in the zig-zagged svarint format. This works for sint32
|
||||||
|
* and sint64. */
|
||||||
|
#ifndef PB_WITHOUT_64BIT
|
||||||
|
bool pb_decode_svarint(pb_istream_t *stream, int64_t *dest);
|
||||||
|
#else
|
||||||
|
bool pb_decode_svarint(pb_istream_t *stream, int32_t *dest);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Decode a fixed32, sfixed32 or float value. You need to pass a pointer to
|
||||||
|
* a 4-byte wide C variable. */
|
||||||
|
bool pb_decode_fixed32(pb_istream_t *stream, void *dest);
|
||||||
|
|
||||||
|
#ifndef PB_WITHOUT_64BIT
|
||||||
|
/* Decode a fixed64, sfixed64 or double value. You need to pass a pointer to
|
||||||
|
* a 8-byte wide C variable. */
|
||||||
|
bool pb_decode_fixed64(pb_istream_t *stream, void *dest);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PB_CONVERT_DOUBLE_FLOAT
|
||||||
|
/* Decode a double value into float variable. */
|
||||||
|
bool pb_decode_double_as_float(pb_istream_t *stream, float *dest);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Make a limited-length substream for reading a PB_WT_STRING field. */
|
||||||
|
bool pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream);
|
||||||
|
bool pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
978
components/nanopb/pb_encode.c
Normal file
978
components/nanopb/pb_encode.c
Normal file
@ -0,0 +1,978 @@
|
|||||||
|
/* pb_encode.c -- encode a protobuf using minimal resources
|
||||||
|
*
|
||||||
|
* 2011 Petteri Aimonen <jpa@kapsi.fi>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "pb.h"
|
||||||
|
#include "pb_encode.h"
|
||||||
|
#include "pb_common.h"
|
||||||
|
|
||||||
|
/* Use the GCC warn_unused_result attribute to check that all return values
|
||||||
|
* are propagated correctly. On other compilers and gcc before 3.4.0 just
|
||||||
|
* ignore the annotation.
|
||||||
|
*/
|
||||||
|
#if !defined(__GNUC__) || ( __GNUC__ < 3) || (__GNUC__ == 3 && __GNUC_MINOR__ < 4)
|
||||||
|
#define checkreturn
|
||||||
|
#else
|
||||||
|
#define checkreturn __attribute__((warn_unused_result))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
* Declarations internal to this file *
|
||||||
|
**************************************/
|
||||||
|
static bool checkreturn buf_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count);
|
||||||
|
static bool checkreturn encode_array(pb_ostream_t *stream, pb_field_iter_t *field);
|
||||||
|
static bool checkreturn pb_check_proto3_default_value(const pb_field_iter_t *field);
|
||||||
|
static bool checkreturn encode_basic_field(pb_ostream_t *stream, const pb_field_iter_t *field);
|
||||||
|
static bool checkreturn encode_callback_field(pb_ostream_t *stream, const pb_field_iter_t *field);
|
||||||
|
static bool checkreturn encode_field(pb_ostream_t *stream, pb_field_iter_t *field);
|
||||||
|
static bool checkreturn encode_extension_field(pb_ostream_t *stream, const pb_field_iter_t *field);
|
||||||
|
static bool checkreturn default_extension_encoder(pb_ostream_t *stream, const pb_extension_t *extension);
|
||||||
|
static bool checkreturn pb_encode_varint_32(pb_ostream_t *stream, uint32_t low, uint32_t high);
|
||||||
|
static bool checkreturn pb_enc_bool(pb_ostream_t *stream, const pb_field_iter_t *field);
|
||||||
|
static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_iter_t *field);
|
||||||
|
static bool checkreturn pb_enc_fixed(pb_ostream_t *stream, const pb_field_iter_t *field);
|
||||||
|
static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_iter_t *field);
|
||||||
|
static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_iter_t *field);
|
||||||
|
static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_iter_t *field);
|
||||||
|
static bool checkreturn pb_enc_fixed_length_bytes(pb_ostream_t *stream, const pb_field_iter_t *field);
|
||||||
|
|
||||||
|
#ifdef PB_WITHOUT_64BIT
|
||||||
|
#define pb_int64_t int32_t
|
||||||
|
#define pb_uint64_t uint32_t
|
||||||
|
#else
|
||||||
|
#define pb_int64_t int64_t
|
||||||
|
#define pb_uint64_t uint64_t
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*******************************
|
||||||
|
* pb_ostream_t implementation *
|
||||||
|
*******************************/
|
||||||
|
|
||||||
|
static bool checkreturn buf_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
pb_byte_t *dest = (pb_byte_t*)stream->state;
|
||||||
|
stream->state = dest + count;
|
||||||
|
|
||||||
|
for (i = 0; i < count; i++)
|
||||||
|
dest[i] = buf[i];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize)
|
||||||
|
{
|
||||||
|
pb_ostream_t stream;
|
||||||
|
#ifdef PB_BUFFER_ONLY
|
||||||
|
stream.callback = (void*)1; /* Just a marker value */
|
||||||
|
#else
|
||||||
|
stream.callback = &buf_write;
|
||||||
|
#endif
|
||||||
|
stream.state = buf;
|
||||||
|
stream.max_size = bufsize;
|
||||||
|
stream.bytes_written = 0;
|
||||||
|
#ifndef PB_NO_ERRMSG
|
||||||
|
stream.errmsg = NULL;
|
||||||
|
#endif
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkreturn pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count)
|
||||||
|
{
|
||||||
|
if (count > 0 && stream->callback != NULL)
|
||||||
|
{
|
||||||
|
if (stream->bytes_written + count < stream->bytes_written ||
|
||||||
|
stream->bytes_written + count > stream->max_size)
|
||||||
|
{
|
||||||
|
PB_RETURN_ERROR(stream, "stream full");
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef PB_BUFFER_ONLY
|
||||||
|
if (!buf_write(stream, buf, count))
|
||||||
|
PB_RETURN_ERROR(stream, "io error");
|
||||||
|
#else
|
||||||
|
if (!stream->callback(stream, buf, count))
|
||||||
|
PB_RETURN_ERROR(stream, "io error");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
stream->bytes_written += count;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************
|
||||||
|
* Encode a single field *
|
||||||
|
*************************/
|
||||||
|
|
||||||
|
/* Read a bool value without causing undefined behavior even if the value
|
||||||
|
* is invalid. See issue #434 and
|
||||||
|
* https://stackoverflow.com/questions/27661768/weird-results-for-conditional
|
||||||
|
*/
|
||||||
|
static bool safe_read_bool(const void *pSize)
|
||||||
|
{
|
||||||
|
const char *p = (const char *)pSize;
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < sizeof(bool); i++)
|
||||||
|
{
|
||||||
|
if (p[i] != 0)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Encode a static array. Handles the size calculations and possible packing. */
|
||||||
|
static bool checkreturn encode_array(pb_ostream_t *stream, pb_field_iter_t *field)
|
||||||
|
{
|
||||||
|
pb_size_t i;
|
||||||
|
pb_size_t count;
|
||||||
|
#ifndef PB_ENCODE_ARRAYS_UNPACKED
|
||||||
|
size_t size;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
count = *(pb_size_t*)field->pSize;
|
||||||
|
|
||||||
|
if (count == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (PB_ATYPE(field->type) != PB_ATYPE_POINTER && count > field->array_size)
|
||||||
|
PB_RETURN_ERROR(stream, "array max size exceeded");
|
||||||
|
|
||||||
|
#ifndef PB_ENCODE_ARRAYS_UNPACKED
|
||||||
|
/* We always pack arrays if the datatype allows it. */
|
||||||
|
if (PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE)
|
||||||
|
{
|
||||||
|
if (!pb_encode_tag(stream, PB_WT_STRING, field->tag))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Determine the total size of packed array. */
|
||||||
|
if (PB_LTYPE(field->type) == PB_LTYPE_FIXED32)
|
||||||
|
{
|
||||||
|
size = 4 * (size_t)count;
|
||||||
|
}
|
||||||
|
else if (PB_LTYPE(field->type) == PB_LTYPE_FIXED64)
|
||||||
|
{
|
||||||
|
size = 8 * (size_t)count;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pb_ostream_t sizestream = PB_OSTREAM_SIZING;
|
||||||
|
void *pData_orig = field->pData;
|
||||||
|
for (i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
if (!pb_enc_varint(&sizestream, field))
|
||||||
|
PB_RETURN_ERROR(stream, PB_GET_ERROR(&sizestream));
|
||||||
|
field->pData = (char*)field->pData + field->data_size;
|
||||||
|
}
|
||||||
|
field->pData = pData_orig;
|
||||||
|
size = sizestream.bytes_written;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pb_encode_varint(stream, (pb_uint64_t)size))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (stream->callback == NULL)
|
||||||
|
return pb_write(stream, NULL, size); /* Just sizing.. */
|
||||||
|
|
||||||
|
/* Write the data */
|
||||||
|
for (i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
if (PB_LTYPE(field->type) == PB_LTYPE_FIXED32 || PB_LTYPE(field->type) == PB_LTYPE_FIXED64)
|
||||||
|
{
|
||||||
|
if (!pb_enc_fixed(stream, field))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!pb_enc_varint(stream, field))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
field->pData = (char*)field->pData + field->data_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else /* Unpacked fields */
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
for (i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
/* Normally the data is stored directly in the array entries, but
|
||||||
|
* for pointer-type string and bytes fields, the array entries are
|
||||||
|
* actually pointers themselves also. So we have to dereference once
|
||||||
|
* more to get to the actual data. */
|
||||||
|
if (PB_ATYPE(field->type) == PB_ATYPE_POINTER &&
|
||||||
|
(PB_LTYPE(field->type) == PB_LTYPE_STRING ||
|
||||||
|
PB_LTYPE(field->type) == PB_LTYPE_BYTES))
|
||||||
|
{
|
||||||
|
bool status;
|
||||||
|
void *pData_orig = field->pData;
|
||||||
|
field->pData = *(void* const*)field->pData;
|
||||||
|
|
||||||
|
if (!field->pData)
|
||||||
|
{
|
||||||
|
/* Null pointer in array is treated as empty string / bytes */
|
||||||
|
status = pb_encode_tag_for_field(stream, field) &&
|
||||||
|
pb_encode_varint(stream, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
status = encode_basic_field(stream, field);
|
||||||
|
}
|
||||||
|
|
||||||
|
field->pData = pData_orig;
|
||||||
|
|
||||||
|
if (!status)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!encode_basic_field(stream, field))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
field->pData = (char*)field->pData + field->data_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* In proto3, all fields are optional and are only encoded if their value is "non-zero".
|
||||||
|
* This function implements the check for the zero value. */
|
||||||
|
static bool checkreturn pb_check_proto3_default_value(const pb_field_iter_t *field)
|
||||||
|
{
|
||||||
|
pb_type_t type = field->type;
|
||||||
|
|
||||||
|
if (PB_ATYPE(type) == PB_ATYPE_STATIC)
|
||||||
|
{
|
||||||
|
if (PB_HTYPE(type) == PB_HTYPE_REQUIRED)
|
||||||
|
{
|
||||||
|
/* Required proto2 fields inside proto3 submessage, pretty rare case */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (PB_HTYPE(type) == PB_HTYPE_REPEATED)
|
||||||
|
{
|
||||||
|
/* Repeated fields inside proto3 submessage: present if count != 0 */
|
||||||
|
return *(const pb_size_t*)field->pSize == 0;
|
||||||
|
}
|
||||||
|
else if (PB_HTYPE(type) == PB_HTYPE_ONEOF)
|
||||||
|
{
|
||||||
|
/* Oneof fields */
|
||||||
|
return *(const pb_size_t*)field->pSize == 0;
|
||||||
|
}
|
||||||
|
else if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL && field->pSize != NULL)
|
||||||
|
{
|
||||||
|
/* Proto2 optional fields inside proto3 message, or proto3
|
||||||
|
* submessage fields. */
|
||||||
|
return safe_read_bool(field->pSize) == false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Rest is proto3 singular fields */
|
||||||
|
if (PB_LTYPE(type) <= PB_LTYPE_LAST_PACKABLE)
|
||||||
|
{
|
||||||
|
/* Simple integer / float fields */
|
||||||
|
pb_size_t i;
|
||||||
|
const char *p = (const char*)field->pData;
|
||||||
|
for (i = 0; i < field->data_size; i++)
|
||||||
|
{
|
||||||
|
if (p[i] != 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (PB_LTYPE(type) == PB_LTYPE_BYTES)
|
||||||
|
{
|
||||||
|
const pb_bytes_array_t *bytes = (const pb_bytes_array_t*)field->pData;
|
||||||
|
return bytes->size == 0;
|
||||||
|
}
|
||||||
|
else if (PB_LTYPE(type) == PB_LTYPE_STRING)
|
||||||
|
{
|
||||||
|
return *(const char*)field->pData == '\0';
|
||||||
|
}
|
||||||
|
else if (PB_LTYPE(type) == PB_LTYPE_FIXED_LENGTH_BYTES)
|
||||||
|
{
|
||||||
|
/* Fixed length bytes is only empty if its length is fixed
|
||||||
|
* as 0. Which would be pretty strange, but we can check
|
||||||
|
* it anyway. */
|
||||||
|
return field->data_size == 0;
|
||||||
|
}
|
||||||
|
else if (PB_LTYPE_IS_SUBMSG(type))
|
||||||
|
{
|
||||||
|
/* Check all fields in the submessage to find if any of them
|
||||||
|
* are non-zero. The comparison cannot be done byte-per-byte
|
||||||
|
* because the C struct may contain padding bytes that must
|
||||||
|
* be skipped. Note that usually proto3 submessages have
|
||||||
|
* a separate has_field that is checked earlier in this if.
|
||||||
|
*/
|
||||||
|
pb_field_iter_t iter;
|
||||||
|
if (pb_field_iter_begin(&iter, field->submsg_desc, field->pData))
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (!pb_check_proto3_default_value(&iter))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} while (pb_field_iter_next(&iter));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (PB_ATYPE(type) == PB_ATYPE_POINTER)
|
||||||
|
{
|
||||||
|
return field->pData == NULL;
|
||||||
|
}
|
||||||
|
else if (PB_ATYPE(type) == PB_ATYPE_CALLBACK)
|
||||||
|
{
|
||||||
|
if (PB_LTYPE(type) == PB_LTYPE_EXTENSION)
|
||||||
|
{
|
||||||
|
const pb_extension_t *extension = *(const pb_extension_t* const *)field->pData;
|
||||||
|
return extension == NULL;
|
||||||
|
}
|
||||||
|
else if (field->descriptor->field_callback == pb_default_field_callback)
|
||||||
|
{
|
||||||
|
pb_callback_t *pCallback = (pb_callback_t*)field->pData;
|
||||||
|
return pCallback->funcs.encode == NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return field->descriptor->field_callback == NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false; /* Not typically reached, safe default for weird special cases. */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Encode a field with static or pointer allocation, i.e. one whose data
|
||||||
|
* is available to the encoder directly. */
|
||||||
|
static bool checkreturn encode_basic_field(pb_ostream_t *stream, const pb_field_iter_t *field)
|
||||||
|
{
|
||||||
|
if (!field->pData)
|
||||||
|
{
|
||||||
|
/* Missing pointer field */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pb_encode_tag_for_field(stream, field))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (PB_LTYPE(field->type))
|
||||||
|
{
|
||||||
|
case PB_LTYPE_BOOL:
|
||||||
|
return pb_enc_bool(stream, field);
|
||||||
|
|
||||||
|
case PB_LTYPE_VARINT:
|
||||||
|
case PB_LTYPE_UVARINT:
|
||||||
|
case PB_LTYPE_SVARINT:
|
||||||
|
return pb_enc_varint(stream, field);
|
||||||
|
|
||||||
|
case PB_LTYPE_FIXED32:
|
||||||
|
case PB_LTYPE_FIXED64:
|
||||||
|
return pb_enc_fixed(stream, field);
|
||||||
|
|
||||||
|
case PB_LTYPE_BYTES:
|
||||||
|
return pb_enc_bytes(stream, field);
|
||||||
|
|
||||||
|
case PB_LTYPE_STRING:
|
||||||
|
return pb_enc_string(stream, field);
|
||||||
|
|
||||||
|
case PB_LTYPE_SUBMESSAGE:
|
||||||
|
case PB_LTYPE_SUBMSG_W_CB:
|
||||||
|
return pb_enc_submessage(stream, field);
|
||||||
|
|
||||||
|
case PB_LTYPE_FIXED_LENGTH_BYTES:
|
||||||
|
return pb_enc_fixed_length_bytes(stream, field);
|
||||||
|
|
||||||
|
default:
|
||||||
|
PB_RETURN_ERROR(stream, "invalid field type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Encode a field with callback semantics. This means that a user function is
|
||||||
|
* called to provide and encode the actual data. */
|
||||||
|
static bool checkreturn encode_callback_field(pb_ostream_t *stream, const pb_field_iter_t *field)
|
||||||
|
{
|
||||||
|
if (field->descriptor->field_callback != NULL)
|
||||||
|
{
|
||||||
|
if (!field->descriptor->field_callback(NULL, stream, field))
|
||||||
|
PB_RETURN_ERROR(stream, "callback error");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Encode a single field of any callback, pointer or static type. */
|
||||||
|
static bool checkreturn encode_field(pb_ostream_t *stream, pb_field_iter_t *field)
|
||||||
|
{
|
||||||
|
/* Check field presence */
|
||||||
|
if (PB_HTYPE(field->type) == PB_HTYPE_ONEOF)
|
||||||
|
{
|
||||||
|
if (*(const pb_size_t*)field->pSize != field->tag)
|
||||||
|
{
|
||||||
|
/* Different type oneof field */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (PB_HTYPE(field->type) == PB_HTYPE_OPTIONAL)
|
||||||
|
{
|
||||||
|
if (field->pSize)
|
||||||
|
{
|
||||||
|
if (safe_read_bool(field->pSize) == false)
|
||||||
|
{
|
||||||
|
/* Missing optional field */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (PB_ATYPE(field->type) == PB_ATYPE_STATIC)
|
||||||
|
{
|
||||||
|
/* Proto3 singular field */
|
||||||
|
if (pb_check_proto3_default_value(field))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!field->pData)
|
||||||
|
{
|
||||||
|
if (PB_HTYPE(field->type) == PB_HTYPE_REQUIRED)
|
||||||
|
PB_RETURN_ERROR(stream, "missing required field");
|
||||||
|
|
||||||
|
/* Pointer field set to NULL */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Then encode field contents */
|
||||||
|
if (PB_ATYPE(field->type) == PB_ATYPE_CALLBACK)
|
||||||
|
{
|
||||||
|
return encode_callback_field(stream, field);
|
||||||
|
}
|
||||||
|
else if (PB_HTYPE(field->type) == PB_HTYPE_REPEATED)
|
||||||
|
{
|
||||||
|
return encode_array(stream, field);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return encode_basic_field(stream, field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Default handler for extension fields. Expects to have a pb_msgdesc_t
|
||||||
|
* pointer in the extension->type->arg field, pointing to a message with
|
||||||
|
* only one field in it. */
|
||||||
|
static bool checkreturn default_extension_encoder(pb_ostream_t *stream, const pb_extension_t *extension)
|
||||||
|
{
|
||||||
|
pb_field_iter_t iter;
|
||||||
|
|
||||||
|
if (!pb_field_iter_begin_extension_const(&iter, extension))
|
||||||
|
PB_RETURN_ERROR(stream, "invalid extension");
|
||||||
|
|
||||||
|
return encode_field(stream, &iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Walk through all the registered extensions and give them a chance
|
||||||
|
* to encode themselves. */
|
||||||
|
static bool checkreturn encode_extension_field(pb_ostream_t *stream, const pb_field_iter_t *field)
|
||||||
|
{
|
||||||
|
const pb_extension_t *extension = *(const pb_extension_t* const *)field->pData;
|
||||||
|
|
||||||
|
while (extension)
|
||||||
|
{
|
||||||
|
bool status;
|
||||||
|
if (extension->type->encode)
|
||||||
|
status = extension->type->encode(stream, extension);
|
||||||
|
else
|
||||||
|
status = default_extension_encoder(stream, extension);
|
||||||
|
|
||||||
|
if (!status)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
extension = extension->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* Encode all fields *
|
||||||
|
*********************/
|
||||||
|
|
||||||
|
bool checkreturn pb_encode(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct)
|
||||||
|
{
|
||||||
|
pb_field_iter_t iter;
|
||||||
|
if (!pb_field_iter_begin_const(&iter, fields, src_struct))
|
||||||
|
return true; /* Empty message type */
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (PB_LTYPE(iter.type) == PB_LTYPE_EXTENSION)
|
||||||
|
{
|
||||||
|
/* Special case for the extension field placeholder */
|
||||||
|
if (!encode_extension_field(stream, &iter))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Regular field */
|
||||||
|
if (!encode_field(stream, &iter))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} while (pb_field_iter_next(&iter));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkreturn pb_encode_ex(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct, unsigned int flags)
|
||||||
|
{
|
||||||
|
if ((flags & PB_ENCODE_DELIMITED) != 0)
|
||||||
|
{
|
||||||
|
return pb_encode_submessage(stream, fields, src_struct);
|
||||||
|
}
|
||||||
|
else if ((flags & PB_ENCODE_NULLTERMINATED) != 0)
|
||||||
|
{
|
||||||
|
const pb_byte_t zero = 0;
|
||||||
|
|
||||||
|
if (!pb_encode(stream, fields, src_struct))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return pb_write(stream, &zero, 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return pb_encode(stream, fields, src_struct);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pb_get_encoded_size(size_t *size, const pb_msgdesc_t *fields, const void *src_struct)
|
||||||
|
{
|
||||||
|
pb_ostream_t stream = PB_OSTREAM_SIZING;
|
||||||
|
|
||||||
|
if (!pb_encode(&stream, fields, src_struct))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
*size = stream.bytes_written;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************
|
||||||
|
* Helper functions *
|
||||||
|
********************/
|
||||||
|
|
||||||
|
/* This function avoids 64-bit shifts as they are quite slow on many platforms. */
|
||||||
|
static bool checkreturn pb_encode_varint_32(pb_ostream_t *stream, uint32_t low, uint32_t high)
|
||||||
|
{
|
||||||
|
size_t i = 0;
|
||||||
|
pb_byte_t buffer[10];
|
||||||
|
pb_byte_t byte = (pb_byte_t)(low & 0x7F);
|
||||||
|
low >>= 7;
|
||||||
|
|
||||||
|
while (i < 4 && (low != 0 || high != 0))
|
||||||
|
{
|
||||||
|
byte |= 0x80;
|
||||||
|
buffer[i++] = byte;
|
||||||
|
byte = (pb_byte_t)(low & 0x7F);
|
||||||
|
low >>= 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (high)
|
||||||
|
{
|
||||||
|
byte = (pb_byte_t)(byte | ((high & 0x07) << 4));
|
||||||
|
high >>= 3;
|
||||||
|
|
||||||
|
while (high)
|
||||||
|
{
|
||||||
|
byte |= 0x80;
|
||||||
|
buffer[i++] = byte;
|
||||||
|
byte = (pb_byte_t)(high & 0x7F);
|
||||||
|
high >>= 7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer[i++] = byte;
|
||||||
|
|
||||||
|
return pb_write(stream, buffer, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkreturn pb_encode_varint(pb_ostream_t *stream, pb_uint64_t value)
|
||||||
|
{
|
||||||
|
if (value <= 0x7F)
|
||||||
|
{
|
||||||
|
/* Fast path: single byte */
|
||||||
|
pb_byte_t byte = (pb_byte_t)value;
|
||||||
|
return pb_write(stream, &byte, 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#ifdef PB_WITHOUT_64BIT
|
||||||
|
return pb_encode_varint_32(stream, value, 0);
|
||||||
|
#else
|
||||||
|
return pb_encode_varint_32(stream, (uint32_t)value, (uint32_t)(value >> 32));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkreturn pb_encode_svarint(pb_ostream_t *stream, pb_int64_t value)
|
||||||
|
{
|
||||||
|
pb_uint64_t zigzagged;
|
||||||
|
if (value < 0)
|
||||||
|
zigzagged = ~((pb_uint64_t)value << 1);
|
||||||
|
else
|
||||||
|
zigzagged = (pb_uint64_t)value << 1;
|
||||||
|
|
||||||
|
return pb_encode_varint(stream, zigzagged);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkreturn pb_encode_fixed32(pb_ostream_t *stream, const void *value)
|
||||||
|
{
|
||||||
|
uint32_t val = *(const uint32_t*)value;
|
||||||
|
pb_byte_t bytes[4];
|
||||||
|
bytes[0] = (pb_byte_t)(val & 0xFF);
|
||||||
|
bytes[1] = (pb_byte_t)((val >> 8) & 0xFF);
|
||||||
|
bytes[2] = (pb_byte_t)((val >> 16) & 0xFF);
|
||||||
|
bytes[3] = (pb_byte_t)((val >> 24) & 0xFF);
|
||||||
|
return pb_write(stream, bytes, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef PB_WITHOUT_64BIT
|
||||||
|
bool checkreturn pb_encode_fixed64(pb_ostream_t *stream, const void *value)
|
||||||
|
{
|
||||||
|
uint64_t val = *(const uint64_t*)value;
|
||||||
|
pb_byte_t bytes[8];
|
||||||
|
bytes[0] = (pb_byte_t)(val & 0xFF);
|
||||||
|
bytes[1] = (pb_byte_t)((val >> 8) & 0xFF);
|
||||||
|
bytes[2] = (pb_byte_t)((val >> 16) & 0xFF);
|
||||||
|
bytes[3] = (pb_byte_t)((val >> 24) & 0xFF);
|
||||||
|
bytes[4] = (pb_byte_t)((val >> 32) & 0xFF);
|
||||||
|
bytes[5] = (pb_byte_t)((val >> 40) & 0xFF);
|
||||||
|
bytes[6] = (pb_byte_t)((val >> 48) & 0xFF);
|
||||||
|
bytes[7] = (pb_byte_t)((val >> 56) & 0xFF);
|
||||||
|
return pb_write(stream, bytes, 8);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool checkreturn pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number)
|
||||||
|
{
|
||||||
|
pb_uint64_t tag = ((pb_uint64_t)field_number << 3) | wiretype;
|
||||||
|
return pb_encode_varint(stream, tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pb_encode_tag_for_field ( pb_ostream_t* stream, const pb_field_iter_t* field )
|
||||||
|
{
|
||||||
|
pb_wire_type_t wiretype;
|
||||||
|
switch (PB_LTYPE(field->type))
|
||||||
|
{
|
||||||
|
case PB_LTYPE_BOOL:
|
||||||
|
case PB_LTYPE_VARINT:
|
||||||
|
case PB_LTYPE_UVARINT:
|
||||||
|
case PB_LTYPE_SVARINT:
|
||||||
|
wiretype = PB_WT_VARINT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PB_LTYPE_FIXED32:
|
||||||
|
wiretype = PB_WT_32BIT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PB_LTYPE_FIXED64:
|
||||||
|
wiretype = PB_WT_64BIT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PB_LTYPE_BYTES:
|
||||||
|
case PB_LTYPE_STRING:
|
||||||
|
case PB_LTYPE_SUBMESSAGE:
|
||||||
|
case PB_LTYPE_SUBMSG_W_CB:
|
||||||
|
case PB_LTYPE_FIXED_LENGTH_BYTES:
|
||||||
|
wiretype = PB_WT_STRING;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
PB_RETURN_ERROR(stream, "invalid field type");
|
||||||
|
}
|
||||||
|
|
||||||
|
return pb_encode_tag(stream, wiretype, field->tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkreturn pb_encode_string(pb_ostream_t *stream, const pb_byte_t *buffer, size_t size)
|
||||||
|
{
|
||||||
|
if (!pb_encode_varint(stream, (pb_uint64_t)size))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return pb_write(stream, buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkreturn pb_encode_submessage(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct)
|
||||||
|
{
|
||||||
|
/* First calculate the message size using a non-writing substream. */
|
||||||
|
pb_ostream_t substream = PB_OSTREAM_SIZING;
|
||||||
|
size_t size;
|
||||||
|
bool status;
|
||||||
|
|
||||||
|
if (!pb_encode(&substream, fields, src_struct))
|
||||||
|
{
|
||||||
|
#ifndef PB_NO_ERRMSG
|
||||||
|
stream->errmsg = substream.errmsg;
|
||||||
|
#endif
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size = substream.bytes_written;
|
||||||
|
|
||||||
|
if (!pb_encode_varint(stream, (pb_uint64_t)size))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (stream->callback == NULL)
|
||||||
|
return pb_write(stream, NULL, size); /* Just sizing */
|
||||||
|
|
||||||
|
if (stream->bytes_written + size > stream->max_size)
|
||||||
|
PB_RETURN_ERROR(stream, "stream full");
|
||||||
|
|
||||||
|
/* Use a substream to verify that a callback doesn't write more than
|
||||||
|
* what it did the first time. */
|
||||||
|
substream.callback = stream->callback;
|
||||||
|
substream.state = stream->state;
|
||||||
|
substream.max_size = size;
|
||||||
|
substream.bytes_written = 0;
|
||||||
|
#ifndef PB_NO_ERRMSG
|
||||||
|
substream.errmsg = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
status = pb_encode(&substream, fields, src_struct);
|
||||||
|
|
||||||
|
stream->bytes_written += substream.bytes_written;
|
||||||
|
stream->state = substream.state;
|
||||||
|
#ifndef PB_NO_ERRMSG
|
||||||
|
stream->errmsg = substream.errmsg;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (substream.bytes_written != size)
|
||||||
|
PB_RETURN_ERROR(stream, "submsg size changed");
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Field encoders */
|
||||||
|
|
||||||
|
static bool checkreturn pb_enc_bool(pb_ostream_t *stream, const pb_field_iter_t *field)
|
||||||
|
{
|
||||||
|
uint32_t value = safe_read_bool(field->pData) ? 1 : 0;
|
||||||
|
PB_UNUSED(field);
|
||||||
|
return pb_encode_varint(stream, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_iter_t *field)
|
||||||
|
{
|
||||||
|
if (PB_LTYPE(field->type) == PB_LTYPE_UVARINT)
|
||||||
|
{
|
||||||
|
/* Perform unsigned integer extension */
|
||||||
|
pb_uint64_t value = 0;
|
||||||
|
|
||||||
|
if (field->data_size == sizeof(uint_least8_t))
|
||||||
|
value = *(const uint_least8_t*)field->pData;
|
||||||
|
else if (field->data_size == sizeof(uint_least16_t))
|
||||||
|
value = *(const uint_least16_t*)field->pData;
|
||||||
|
else if (field->data_size == sizeof(uint32_t))
|
||||||
|
value = *(const uint32_t*)field->pData;
|
||||||
|
else if (field->data_size == sizeof(pb_uint64_t))
|
||||||
|
value = *(const pb_uint64_t*)field->pData;
|
||||||
|
else
|
||||||
|
PB_RETURN_ERROR(stream, "invalid data_size");
|
||||||
|
|
||||||
|
return pb_encode_varint(stream, value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Perform signed integer extension */
|
||||||
|
pb_int64_t value = 0;
|
||||||
|
|
||||||
|
if (field->data_size == sizeof(int_least8_t))
|
||||||
|
value = *(const int_least8_t*)field->pData;
|
||||||
|
else if (field->data_size == sizeof(int_least16_t))
|
||||||
|
value = *(const int_least16_t*)field->pData;
|
||||||
|
else if (field->data_size == sizeof(int32_t))
|
||||||
|
value = *(const int32_t*)field->pData;
|
||||||
|
else if (field->data_size == sizeof(pb_int64_t))
|
||||||
|
value = *(const pb_int64_t*)field->pData;
|
||||||
|
else
|
||||||
|
PB_RETURN_ERROR(stream, "invalid data_size");
|
||||||
|
|
||||||
|
if (PB_LTYPE(field->type) == PB_LTYPE_SVARINT)
|
||||||
|
return pb_encode_svarint(stream, value);
|
||||||
|
#ifdef PB_WITHOUT_64BIT
|
||||||
|
else if (value < 0)
|
||||||
|
return pb_encode_varint_32(stream, (uint32_t)value, (uint32_t)-1);
|
||||||
|
#endif
|
||||||
|
else
|
||||||
|
return pb_encode_varint(stream, (pb_uint64_t)value);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool checkreturn pb_enc_fixed(pb_ostream_t *stream, const pb_field_iter_t *field)
|
||||||
|
{
|
||||||
|
#ifdef PB_CONVERT_DOUBLE_FLOAT
|
||||||
|
if (field->data_size == sizeof(float) && PB_LTYPE(field->type) == PB_LTYPE_FIXED64)
|
||||||
|
{
|
||||||
|
return pb_encode_float_as_double(stream, *(float*)field->pData);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (field->data_size == sizeof(uint32_t))
|
||||||
|
{
|
||||||
|
return pb_encode_fixed32(stream, field->pData);
|
||||||
|
}
|
||||||
|
#ifndef PB_WITHOUT_64BIT
|
||||||
|
else if (field->data_size == sizeof(uint64_t))
|
||||||
|
{
|
||||||
|
return pb_encode_fixed64(stream, field->pData);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PB_RETURN_ERROR(stream, "invalid data_size");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_iter_t *field)
|
||||||
|
{
|
||||||
|
const pb_bytes_array_t *bytes = NULL;
|
||||||
|
|
||||||
|
bytes = (const pb_bytes_array_t*)field->pData;
|
||||||
|
|
||||||
|
if (bytes == NULL)
|
||||||
|
{
|
||||||
|
/* Treat null pointer as an empty bytes field */
|
||||||
|
return pb_encode_string(stream, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PB_ATYPE(field->type) == PB_ATYPE_STATIC &&
|
||||||
|
bytes->size > field->data_size - offsetof(pb_bytes_array_t, bytes))
|
||||||
|
{
|
||||||
|
PB_RETURN_ERROR(stream, "bytes size exceeded");
|
||||||
|
}
|
||||||
|
|
||||||
|
return pb_encode_string(stream, bytes->bytes, (size_t)bytes->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_iter_t *field)
|
||||||
|
{
|
||||||
|
size_t size = 0;
|
||||||
|
size_t max_size = (size_t)field->data_size;
|
||||||
|
const char *str = (const char*)field->pData;
|
||||||
|
|
||||||
|
if (PB_ATYPE(field->type) == PB_ATYPE_POINTER)
|
||||||
|
{
|
||||||
|
max_size = (size_t)-1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* pb_dec_string() assumes string fields end with a null
|
||||||
|
* terminator when the type isn't PB_ATYPE_POINTER, so we
|
||||||
|
* shouldn't allow more than max-1 bytes to be written to
|
||||||
|
* allow space for the null terminator.
|
||||||
|
*/
|
||||||
|
if (max_size == 0)
|
||||||
|
PB_RETURN_ERROR(stream, "zero-length string");
|
||||||
|
|
||||||
|
max_size -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (str == NULL)
|
||||||
|
{
|
||||||
|
size = 0; /* Treat null pointer as an empty string */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const char *p = str;
|
||||||
|
|
||||||
|
/* strnlen() is not always available, so just use a loop */
|
||||||
|
while (size < max_size && *p != '\0')
|
||||||
|
{
|
||||||
|
size++;
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*p != '\0')
|
||||||
|
{
|
||||||
|
PB_RETURN_ERROR(stream, "unterminated string");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef PB_VALIDATE_UTF8
|
||||||
|
if (!pb_validate_utf8(str))
|
||||||
|
PB_RETURN_ERROR(stream, "invalid utf8");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return pb_encode_string(stream, (const pb_byte_t*)str, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_iter_t *field)
|
||||||
|
{
|
||||||
|
if (field->submsg_desc == NULL)
|
||||||
|
PB_RETURN_ERROR(stream, "invalid field descriptor");
|
||||||
|
|
||||||
|
if (PB_LTYPE(field->type) == PB_LTYPE_SUBMSG_W_CB && field->pSize != NULL)
|
||||||
|
{
|
||||||
|
/* Message callback is stored right before pSize. */
|
||||||
|
pb_callback_t *callback = (pb_callback_t*)field->pSize - 1;
|
||||||
|
if (callback->funcs.encode)
|
||||||
|
{
|
||||||
|
if (!callback->funcs.encode(stream, field, &callback->arg))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pb_encode_submessage(stream, field->submsg_desc, field->pData);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool checkreturn pb_enc_fixed_length_bytes(pb_ostream_t *stream, const pb_field_iter_t *field)
|
||||||
|
{
|
||||||
|
return pb_encode_string(stream, (const pb_byte_t*)field->pData, (size_t)field->data_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef PB_CONVERT_DOUBLE_FLOAT
|
||||||
|
bool pb_encode_float_as_double(pb_ostream_t *stream, float value)
|
||||||
|
{
|
||||||
|
union { float f; uint32_t i; } in;
|
||||||
|
uint_least8_t sign;
|
||||||
|
int exponent;
|
||||||
|
uint64_t mantissa;
|
||||||
|
|
||||||
|
in.f = value;
|
||||||
|
|
||||||
|
/* Decompose input value */
|
||||||
|
sign = (uint_least8_t)((in.i >> 31) & 1);
|
||||||
|
exponent = (int)((in.i >> 23) & 0xFF) - 127;
|
||||||
|
mantissa = in.i & 0x7FFFFF;
|
||||||
|
|
||||||
|
if (exponent == 128)
|
||||||
|
{
|
||||||
|
/* Special value (NaN etc.) */
|
||||||
|
exponent = 1024;
|
||||||
|
}
|
||||||
|
else if (exponent == -127)
|
||||||
|
{
|
||||||
|
if (!mantissa)
|
||||||
|
{
|
||||||
|
/* Zero */
|
||||||
|
exponent = -1023;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Denormalized */
|
||||||
|
mantissa <<= 1;
|
||||||
|
while (!(mantissa & 0x800000))
|
||||||
|
{
|
||||||
|
mantissa <<= 1;
|
||||||
|
exponent--;
|
||||||
|
}
|
||||||
|
mantissa &= 0x7FFFFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Combine fields */
|
||||||
|
mantissa <<= 29;
|
||||||
|
mantissa |= (uint64_t)(exponent + 1023) << 52;
|
||||||
|
mantissa |= (uint64_t)sign << 63;
|
||||||
|
|
||||||
|
return pb_encode_fixed64(stream, &mantissa);
|
||||||
|
}
|
||||||
|
#endif
|
185
components/nanopb/pb_encode.h
Normal file
185
components/nanopb/pb_encode.h
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
/* pb_encode.h: Functions to encode protocol buffers. Depends on pb_encode.c.
|
||||||
|
* The main function is pb_encode. You also need an output stream, and the
|
||||||
|
* field descriptions created by nanopb_generator.py.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PB_ENCODE_H_INCLUDED
|
||||||
|
#define PB_ENCODE_H_INCLUDED
|
||||||
|
|
||||||
|
#include "pb.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Structure for defining custom output streams. You will need to provide
|
||||||
|
* a callback function to write the bytes to your storage, which can be
|
||||||
|
* for example a file or a network socket.
|
||||||
|
*
|
||||||
|
* The callback must conform to these rules:
|
||||||
|
*
|
||||||
|
* 1) Return false on IO errors. This will cause encoding to abort.
|
||||||
|
* 2) You can use state to store your own data (e.g. buffer pointer).
|
||||||
|
* 3) pb_write will update bytes_written after your callback runs.
|
||||||
|
* 4) Substreams will modify max_size and bytes_written. Don't use them
|
||||||
|
* to calculate any pointers.
|
||||||
|
*/
|
||||||
|
struct pb_ostream_s
|
||||||
|
{
|
||||||
|
#ifdef PB_BUFFER_ONLY
|
||||||
|
/* Callback pointer is not used in buffer-only configuration.
|
||||||
|
* Having an int pointer here allows binary compatibility but
|
||||||
|
* gives an error if someone tries to assign callback function.
|
||||||
|
* Also, NULL pointer marks a 'sizing stream' that does not
|
||||||
|
* write anything.
|
||||||
|
*/
|
||||||
|
int *callback;
|
||||||
|
#else
|
||||||
|
bool (*callback)(pb_ostream_t *stream, const pb_byte_t *buf, size_t count);
|
||||||
|
#endif
|
||||||
|
void *state; /* Free field for use by callback implementation. */
|
||||||
|
size_t max_size; /* Limit number of output bytes written (or use SIZE_MAX). */
|
||||||
|
size_t bytes_written; /* Number of bytes written so far. */
|
||||||
|
|
||||||
|
#ifndef PB_NO_ERRMSG
|
||||||
|
const char *errmsg;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
/***************************
|
||||||
|
* Main encoding functions *
|
||||||
|
***************************/
|
||||||
|
|
||||||
|
/* Encode a single protocol buffers message from C structure into a stream.
|
||||||
|
* Returns true on success, false on any failure.
|
||||||
|
* The actual struct pointed to by src_struct must match the description in fields.
|
||||||
|
* All required fields in the struct are assumed to have been filled in.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
* MyMessage msg = {};
|
||||||
|
* uint8_t buffer[64];
|
||||||
|
* pb_ostream_t stream;
|
||||||
|
*
|
||||||
|
* msg.field1 = 42;
|
||||||
|
* stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
|
||||||
|
* pb_encode(&stream, MyMessage_fields, &msg);
|
||||||
|
*/
|
||||||
|
bool pb_encode(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct);
|
||||||
|
|
||||||
|
/* Extended version of pb_encode, with several options to control the
|
||||||
|
* encoding process:
|
||||||
|
*
|
||||||
|
* PB_ENCODE_DELIMITED: Prepend the length of message as a varint.
|
||||||
|
* Corresponds to writeDelimitedTo() in Google's
|
||||||
|
* protobuf API.
|
||||||
|
*
|
||||||
|
* PB_ENCODE_NULLTERMINATED: Append a null byte to the message for termination.
|
||||||
|
* NOTE: This behaviour is not supported in most other
|
||||||
|
* protobuf implementations, so PB_ENCODE_DELIMITED
|
||||||
|
* is a better option for compatibility.
|
||||||
|
*/
|
||||||
|
#define PB_ENCODE_DELIMITED 0x02U
|
||||||
|
#define PB_ENCODE_NULLTERMINATED 0x04U
|
||||||
|
bool pb_encode_ex(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct, unsigned int flags);
|
||||||
|
|
||||||
|
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
|
||||||
|
#define pb_encode_delimited(s,f,d) pb_encode_ex(s,f,d, PB_ENCODE_DELIMITED)
|
||||||
|
#define pb_encode_nullterminated(s,f,d) pb_encode_ex(s,f,d, PB_ENCODE_NULLTERMINATED)
|
||||||
|
|
||||||
|
/* Encode the message to get the size of the encoded data, but do not store
|
||||||
|
* the data. */
|
||||||
|
bool pb_get_encoded_size(size_t *size, const pb_msgdesc_t *fields, const void *src_struct);
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
* Functions for manipulating streams *
|
||||||
|
**************************************/
|
||||||
|
|
||||||
|
/* Create an output stream for writing into a memory buffer.
|
||||||
|
* The number of bytes written can be found in stream.bytes_written after
|
||||||
|
* encoding the message.
|
||||||
|
*
|
||||||
|
* Alternatively, you can use a custom stream that writes directly to e.g.
|
||||||
|
* a file or a network socket.
|
||||||
|
*/
|
||||||
|
pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize);
|
||||||
|
|
||||||
|
/* Pseudo-stream for measuring the size of a message without actually storing
|
||||||
|
* the encoded data.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
* MyMessage msg = {};
|
||||||
|
* pb_ostream_t stream = PB_OSTREAM_SIZING;
|
||||||
|
* pb_encode(&stream, MyMessage_fields, &msg);
|
||||||
|
* printf("Message size is %d\n", stream.bytes_written);
|
||||||
|
*/
|
||||||
|
#ifndef PB_NO_ERRMSG
|
||||||
|
#define PB_OSTREAM_SIZING {0,0,0,0,0}
|
||||||
|
#else
|
||||||
|
#define PB_OSTREAM_SIZING {0,0,0,0}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Function to write into a pb_ostream_t stream. You can use this if you need
|
||||||
|
* to append or prepend some custom headers to the message.
|
||||||
|
*/
|
||||||
|
bool pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count);
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
* Helper functions for writing field callbacks *
|
||||||
|
************************************************/
|
||||||
|
|
||||||
|
/* Encode field header based on type and field number defined in the field
|
||||||
|
* structure. Call this from the callback before writing out field contents. */
|
||||||
|
bool pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_iter_t *field);
|
||||||
|
|
||||||
|
/* Encode field header by manually specifing wire type. You need to use this
|
||||||
|
* if you want to write out packed arrays from a callback field. */
|
||||||
|
bool pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number);
|
||||||
|
|
||||||
|
/* Encode an integer in the varint format.
|
||||||
|
* This works for bool, enum, int32, int64, uint32 and uint64 field types. */
|
||||||
|
#ifndef PB_WITHOUT_64BIT
|
||||||
|
bool pb_encode_varint(pb_ostream_t *stream, uint64_t value);
|
||||||
|
#else
|
||||||
|
bool pb_encode_varint(pb_ostream_t *stream, uint32_t value);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Encode an integer in the zig-zagged svarint format.
|
||||||
|
* This works for sint32 and sint64. */
|
||||||
|
#ifndef PB_WITHOUT_64BIT
|
||||||
|
bool pb_encode_svarint(pb_ostream_t *stream, int64_t value);
|
||||||
|
#else
|
||||||
|
bool pb_encode_svarint(pb_ostream_t *stream, int32_t value);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Encode a string or bytes type field. For strings, pass strlen(s) as size. */
|
||||||
|
bool pb_encode_string(pb_ostream_t *stream, const pb_byte_t *buffer, size_t size);
|
||||||
|
|
||||||
|
/* Encode a fixed32, sfixed32 or float value.
|
||||||
|
* You need to pass a pointer to a 4-byte wide C variable. */
|
||||||
|
bool pb_encode_fixed32(pb_ostream_t *stream, const void *value);
|
||||||
|
|
||||||
|
#ifndef PB_WITHOUT_64BIT
|
||||||
|
/* Encode a fixed64, sfixed64 or double value.
|
||||||
|
* You need to pass a pointer to a 8-byte wide C variable. */
|
||||||
|
bool pb_encode_fixed64(pb_ostream_t *stream, const void *value);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PB_CONVERT_DOUBLE_FLOAT
|
||||||
|
/* Encode a float value so that it appears like a double in the encoded
|
||||||
|
* message. */
|
||||||
|
bool pb_encode_float_as_double(pb_ostream_t *stream, float value);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Encode a submessage field.
|
||||||
|
* You need to pass the pb_field_t array and pointer to struct, just like
|
||||||
|
* with pb_encode(). This internally encodes the submessage twice, first to
|
||||||
|
* calculate message size and then to actually write it out.
|
||||||
|
*/
|
||||||
|
bool pb_encode_submessage(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user