From ed07530db41ccb0c0aa0f905bbd4d4ec2828aef2 Mon Sep 17 00:00:00 2001 From: Shuanglei Tao Date: Wed, 14 May 2025 13:03:41 +0800 Subject: [PATCH] add nrf51 DFU project --- Keil/DFU-nRF51.uvprojx | 708 +++++++ .../components/boards/pca10028.h | 2 +- SDK/12.3.0_d7731ad/dfu/dfu-cc.pb.c | 122 ++ SDK/12.3.0_d7731ad/dfu/dfu-cc.pb.h | 227 +++ SDK/12.3.0_d7731ad/dfu/dfu_public_key.c | 12 + SDK/12.3.0_d7731ad/dfu/dfu_req_handling.c | 1074 +++++++++++ SDK/12.3.0_d7731ad/dfu/dfu_req_handling.h | 49 + SDK/12.3.0_d7731ad/dfu/main.c | 122 ++ SDK/12.3.0_d7731ad/dfu/sdk_config.h | 509 +++++ .../external/micro-ecc/license.txt | 21 + .../external/micro-ecc/micro-ecc/LICENSE.txt | 21 + .../external/micro-ecc/micro-ecc/asm_arm.inc | 820 ++++++++ .../micro-ecc/micro-ecc/curve-specific.inc | 1248 ++++++++++++ .../micro-ecc/micro-ecc/platform-specific.inc | 94 + .../external/micro-ecc/micro-ecc/types.h | 108 ++ .../external/micro-ecc/micro-ecc/uECC.c | 1673 +++++++++++++++++ .../external/micro-ecc/micro-ecc/uECC.h | 367 ++++ .../external/micro-ecc/micro-ecc/uECC_vli.h | 172 ++ .../micro-ecc/nrf51_armgcc/armgcc/Makefile | 70 + .../armgcc/ext_micro_ecc_gcc_nRF5x.ld | 28 + .../nrf51_armgcc/armgcc/micro_ecc_lib_nrf51.a | Bin 0 -> 124882 bytes .../micro-ecc/nrf51_keil/armgcc/Makefile | 70 + .../armgcc/ext_micro_ecc_gcc_nRF5x.ld | 28 + .../nrf51_keil/armgcc/micro_ecc_lib_nrf51.lib | Bin 0 -> 124890 bytes .../external/nano-pb/LICENSE.txt | 20 + SDK/12.3.0_d7731ad/external/nano-pb/pb.h | 556 ++++++ .../external/nano-pb/pb_common.c | 97 + .../external/nano-pb/pb_common.h | 42 + .../external/nano-pb/pb_decode.c | 1340 +++++++++++++ .../external/nano-pb/pb_decode.h | 152 ++ .../external/nano-pb/pb_encode.c | 689 +++++++ .../external/nano-pb/pb_encode.h | 154 ++ 32 files changed, 10594 insertions(+), 1 deletion(-) create mode 100644 Keil/DFU-nRF51.uvprojx create mode 100644 SDK/12.3.0_d7731ad/dfu/dfu-cc.pb.c create mode 100644 SDK/12.3.0_d7731ad/dfu/dfu-cc.pb.h create mode 100644 SDK/12.3.0_d7731ad/dfu/dfu_public_key.c create mode 100644 SDK/12.3.0_d7731ad/dfu/dfu_req_handling.c create mode 100644 SDK/12.3.0_d7731ad/dfu/dfu_req_handling.h create mode 100644 SDK/12.3.0_d7731ad/dfu/main.c create mode 100644 SDK/12.3.0_d7731ad/dfu/sdk_config.h create mode 100644 SDK/12.3.0_d7731ad/external/micro-ecc/license.txt create mode 100644 SDK/12.3.0_d7731ad/external/micro-ecc/micro-ecc/LICENSE.txt create mode 100644 SDK/12.3.0_d7731ad/external/micro-ecc/micro-ecc/asm_arm.inc create mode 100644 SDK/12.3.0_d7731ad/external/micro-ecc/micro-ecc/curve-specific.inc create mode 100644 SDK/12.3.0_d7731ad/external/micro-ecc/micro-ecc/platform-specific.inc create mode 100644 SDK/12.3.0_d7731ad/external/micro-ecc/micro-ecc/types.h create mode 100644 SDK/12.3.0_d7731ad/external/micro-ecc/micro-ecc/uECC.c create mode 100644 SDK/12.3.0_d7731ad/external/micro-ecc/micro-ecc/uECC.h create mode 100644 SDK/12.3.0_d7731ad/external/micro-ecc/micro-ecc/uECC_vli.h create mode 100644 SDK/12.3.0_d7731ad/external/micro-ecc/nrf51_armgcc/armgcc/Makefile create mode 100644 SDK/12.3.0_d7731ad/external/micro-ecc/nrf51_armgcc/armgcc/ext_micro_ecc_gcc_nRF5x.ld create mode 100644 SDK/12.3.0_d7731ad/external/micro-ecc/nrf51_armgcc/armgcc/micro_ecc_lib_nrf51.a create mode 100644 SDK/12.3.0_d7731ad/external/micro-ecc/nrf51_keil/armgcc/Makefile create mode 100644 SDK/12.3.0_d7731ad/external/micro-ecc/nrf51_keil/armgcc/ext_micro_ecc_gcc_nRF5x.ld create mode 100644 SDK/12.3.0_d7731ad/external/micro-ecc/nrf51_keil/armgcc/micro_ecc_lib_nrf51.lib create mode 100644 SDK/12.3.0_d7731ad/external/nano-pb/LICENSE.txt create mode 100644 SDK/12.3.0_d7731ad/external/nano-pb/pb.h create mode 100644 SDK/12.3.0_d7731ad/external/nano-pb/pb_common.c create mode 100644 SDK/12.3.0_d7731ad/external/nano-pb/pb_common.h create mode 100644 SDK/12.3.0_d7731ad/external/nano-pb/pb_decode.c create mode 100644 SDK/12.3.0_d7731ad/external/nano-pb/pb_decode.h create mode 100644 SDK/12.3.0_d7731ad/external/nano-pb/pb_encode.c create mode 100644 SDK/12.3.0_d7731ad/external/nano-pb/pb_encode.h diff --git a/Keil/DFU-nRF51.uvprojx b/Keil/DFU-nRF51.uvprojx new file mode 100644 index 0000000..f9e059c --- /dev/null +++ b/Keil/DFU-nRF51.uvprojx @@ -0,0 +1,708 @@ + + + + 2.1 + +
### uVision Project, (C) Keil Software
+ + + + nrf51822_xxaa_s130 + 0x4 + ARM-ADS + 5060960::V5.06 update 7 (build 960)::.\ARMCC + 0 + + + nRF51822_xxAA + Nordic Semiconductor + NordicSemiconductor.nRF_DeviceFamilyPack.8.11.1 + http://developer.nordicsemi.com/nRF5_SDK/pieces/nRF_DeviceFamilyPack/ + IRAM(0x20000000,0x4000) IROM(0x00000000,0x40000) CPUTYPE("Cortex-M0") CLOCK(12000000) ELITTLE + + + UL2CM3(-S0 -C0 -P0 -FD20000000 -FC4000 -FN1 -FF0nrf51xxx -FS00 -FL0200000 -FP0($$Device:nRF51822_xxAA$Flash\nrf51xxx.flm)) + 0 + $$Device:nRF51822_xxAA$Device\Include\nrf.h + + + + + + + + + + $$Device:nRF51822_xxAA$SVD\nrf51.svd + 0 + 0 + + + + + + + 0 + 0 + 0 + 0 + 1 + + .\_build\ + bl_nrf51822_xxaa_s130 + 1 + 0 + 1 + 1 + 1 + .\_build\ + 1 + 0 + 0 + + 0 + 0 + + + 0 + 0 + 0 + 0 + + + 0 + 0 + + + 0 + 0 + 0 + 0 + + + 0 + 0 + + + 0 + 0 + 0 + 0 + + 0 + + + + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 3 + + + 1 + + + SARMCM3.DLL + + DARMCM1.DLL + -pCM0 + SARMCM3.DLL + + TARMCM1.DLL + -pCM0 + + + + 1 + 0 + 0 + 0 + 16 + + + + + 1 + 0 + 0 + 1 + 1 + 4099 + + 1 + BIN\UL2CM3.DLL + + + + + + 0 + + + + 0 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 0 + 1 + 1 + 0 + 1 + 1 + 0 + 0 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 0 + 0 + "Cortex-M0" + + 0 + 0 + 0 + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 8 + 1 + 0 + 0 + 0 + 3 + 3 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 1 + 0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x20000000 + 0x4000 + + + 1 + 0x0 + 0x40000 + + + 0 + 0x0 + 0x0 + + + 1 + 0x0 + 0x0 + + + 1 + 0x0 + 0x0 + + + 1 + 0x0 + 0x0 + + + 1 + 0x3ac00 + 0x5000 + + + 1 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x0 + 0x0 + + + 0 + 0x200025e0 + 0x1a20 + + + 0 + 0x0 + 0x0 + + + + + + 1 + 4 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + + + BLE_STACK_SUPPORT_REQD __HEAP_SIZE=0 __STACK_SIZE=1600 SVC_INTERFACE_CALL_AS_NORMAL_FUNCTION NRF_SD_BLE_API_VERSION=2 S130 BOARD_PCA10028 SWI_DISABLE0 SOFTDEVICE_PRESENT NRF51422 NRF51 NRF_DFU_SETTINGS_VERSION=1 + + ..\SDK\12.3.0_d7731ad\dfu;..\SDK\12.3.0_d7731ad\components;..\SDK\12.3.0_d7731ad\components\ble\common;..\SDK\12.3.0_d7731ad\components\boards;..\SDK\12.3.0_d7731ad\components\drivers_nrf\clock;..\SDK\12.3.0_d7731ad\components\drivers_nrf\common;..\SDK\12.3.0_d7731ad\components\drivers_nrf\delay;..\SDK\12.3.0_d7731ad\components\drivers_nrf\hal;..\SDK\12.3.0_d7731ad\components\drivers_nrf\rng;..\SDK\12.3.0_d7731ad\components\libraries\bootloader;..\SDK\12.3.0_d7731ad\components\libraries\bootloader\ble_dfu;..\SDK\12.3.0_d7731ad\components\libraries\bootloader\dfu;..\SDK\12.3.0_d7731ad\components\libraries\crc32;..\SDK\12.3.0_d7731ad\components\libraries\crypto;..\SDK\12.3.0_d7731ad\components\libraries\ecc;..\SDK\12.3.0_d7731ad\components\libraries\experimental_section_vars;..\SDK\12.3.0_d7731ad\components\libraries\fstorage;..\SDK\12.3.0_d7731ad\components\libraries\hci;..\SDK\12.3.0_d7731ad\components\libraries\log;..\SDK\12.3.0_d7731ad\components\libraries\log\src;..\SDK\12.3.0_d7731ad\components\libraries\queue;..\SDK\12.3.0_d7731ad\components\libraries\scheduler;..\SDK\12.3.0_d7731ad\components\libraries\sha256;..\SDK\12.3.0_d7731ad\components\libraries\svc;..\SDK\12.3.0_d7731ad\components\libraries\timer;..\SDK\12.3.0_d7731ad\components\libraries\util;..\SDK\12.3.0_d7731ad\components\softdevice\common\softdevice_handler;..\SDK\12.3.0_d7731ad\components\softdevice\s130\headers;..\SDK\12.3.0_d7731ad\components\softdevice\s130\headers\nrf51;..\SDK\12.3.0_d7731ad\components\toolchain;..\SDK\12.3.0_d7731ad\components\toolchain\cmsis\include;..\SDK\12.3.0_d7731ad\external\micro-ecc\micro-ecc;..\SDK\12.3.0_d7731ad\external\nano-pb + + + + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 1 + + --cpreproc_opts=-DBLE_STACK_SUPPORT_REQD,-D__HEAP_SIZE=0,-DSVC_INTERFACE_CALL_AS_NORMAL_FUNCTION,-DNRF_SD_BLE_API_VERSION=2,-DS130,-DBOARD_PCA10028,-DSWI_DISABLE0,-DSOFTDEVICE_PRESENT,-DNRF51422,-DNRF51,-DNRF_DFU_SETTINGS_VERSION=1 + BLE_STACK_SUPPORT_REQD __HEAP_SIZE=0 __STACK_SIZE=1600 SVC_INTERFACE_CALL_AS_NORMAL_FUNCTION NRF_SD_BLE_API_VERSION=2 S130 BOARD_PCA10028 SWI_DISABLE0 SOFTDEVICE_PRESENT NRF51422 NRF51 NRF_DFU_SETTINGS_VERSION=1 + + ..\SDK\12.3.0_d7731ad\dfu;..\SDK\12.3.0_d7731ad\components;..\SDK\12.3.0_d7731ad\components\ble\common;..\SDK\12.3.0_d7731ad\components\boards;..\SDK\12.3.0_d7731ad\components\drivers_nrf\clock;..\SDK\12.3.0_d7731ad\components\drivers_nrf\common;..\SDK\12.3.0_d7731ad\components\drivers_nrf\delay;..\SDK\12.3.0_d7731ad\components\drivers_nrf\hal;..\SDK\12.3.0_d7731ad\components\drivers_nrf\rng;..\SDK\12.3.0_d7731ad\components\libraries\bootloader;..\SDK\12.3.0_d7731ad\components\libraries\bootloader\ble_dfu;..\SDK\12.3.0_d7731ad\components\libraries\bootloader\dfu;..\SDK\12.3.0_d7731ad\components\libraries\crc32;..\SDK\12.3.0_d7731ad\components\libraries\crypto;..\SDK\12.3.0_d7731ad\components\libraries\ecc;..\SDK\12.3.0_d7731ad\components\libraries\experimental_section_vars;..\SDK\12.3.0_d7731ad\components\libraries\fstorage;..\SDK\12.3.0_d7731ad\components\libraries\hci;..\SDK\12.3.0_d7731ad\components\libraries\log;..\SDK\12.3.0_d7731ad\components\libraries\log\src;..\SDK\12.3.0_d7731ad\components\libraries\queue;..\SDK\12.3.0_d7731ad\components\libraries\scheduler;..\SDK\12.3.0_d7731ad\components\libraries\sha256;..\SDK\12.3.0_d7731ad\components\libraries\svc;..\SDK\12.3.0_d7731ad\components\libraries\timer;..\SDK\12.3.0_d7731ad\components\libraries\util;..\SDK\12.3.0_d7731ad\components\softdevice\common\softdevice_handler;..\SDK\12.3.0_d7731ad\components\softdevice\s130\headers;..\SDK\12.3.0_d7731ad\components\softdevice\s130\headers\nrf51;..\SDK\12.3.0_d7731ad\components\toolchain;..\SDK\12.3.0_d7731ad\external\micro-ecc\micro-ecc;..\SDK\12.3.0_d7731ad\external\nano-pb;..\config + + + + 1 + 0 + 0 + 0 + 1 + 0 + 0x00000000 + 0x20000000 + + + + + + + + + + + + + Application + + + dfu-cc.pb.c + 1 + ..\SDK\12.3.0_d7731ad\dfu\dfu-cc.pb.c + + + dfu_public_key.c + 1 + ..\SDK\12.3.0_d7731ad\dfu\dfu_public_key.c + + + dfu_req_handling.c + 1 + ..\SDK\12.3.0_d7731ad\dfu\dfu_req_handling.c + + + main.c + 1 + ..\SDK\12.3.0_d7731ad\dfu\main.c + + + sdk_config.h + 5 + ..\SDK\12.3.0_d7731ad\dfu\sdk_config.h + + + + + Board Definition + + + boards.c + 1 + ..\SDK\12.3.0_d7731ad\components\boards\boards.c + + + + + nRF_BLE + + + ble_advdata.c + 1 + ..\SDK\12.3.0_d7731ad\components\ble\common\ble_advdata.c + + + ble_conn_params.c + 1 + ..\SDK\12.3.0_d7731ad\components\ble\common\ble_conn_params.c + + + ble_srv_common.c + 1 + ..\SDK\12.3.0_d7731ad\components\ble\common\ble_srv_common.c + + + + + nRF_BLE_DFU + + + nrf_ble_dfu.c + 1 + ..\SDK\12.3.0_d7731ad\components\libraries\bootloader\ble_dfu\nrf_ble_dfu.c + + + + + nRF_Bootloader + + + nrf_bootloader.c + 1 + ..\SDK\12.3.0_d7731ad\components\libraries\bootloader\nrf_bootloader.c + + + nrf_bootloader_app_start.c + 1 + ..\SDK\12.3.0_d7731ad\components\libraries\bootloader\nrf_bootloader_app_start.c + + + nrf_bootloader_info.c + 1 + ..\SDK\12.3.0_d7731ad\components\libraries\bootloader\nrf_bootloader_info.c + + + nrf_dfu.c + 1 + ..\SDK\12.3.0_d7731ad\components\libraries\bootloader\dfu\nrf_dfu.c + + + nrf_dfu_flash.c + 1 + ..\SDK\12.3.0_d7731ad\components\libraries\bootloader\dfu\nrf_dfu_flash.c + + + nrf_dfu_mbr.c + 1 + ..\SDK\12.3.0_d7731ad\components\libraries\bootloader\dfu\nrf_dfu_mbr.c + + + nrf_dfu_settings.c + 1 + ..\SDK\12.3.0_d7731ad\components\libraries\bootloader\dfu\nrf_dfu_settings.c + + + nrf_dfu_transport.c + 1 + ..\SDK\12.3.0_d7731ad\components\libraries\bootloader\dfu\nrf_dfu_transport.c + + + nrf_dfu_utils.c + 1 + ..\SDK\12.3.0_d7731ad\components\libraries\bootloader\dfu\nrf_dfu_utils.c + + + + + nRF_Drivers + + + nrf_drv_common.c + 1 + ..\SDK\12.3.0_d7731ad\components\drivers_nrf\common\nrf_drv_common.c + + + nrf_drv_rng.c + 1 + ..\SDK\12.3.0_d7731ad\components\drivers_nrf\rng\nrf_drv_rng.c + + + nrf_nvmc.c + 1 + ..\SDK\12.3.0_d7731ad\components\drivers_nrf\hal\nrf_nvmc.c + + + + + nRF_Libraries + + + app_error_weak.c + 1 + ..\SDK\12.3.0_d7731ad\components\libraries\util\app_error_weak.c + + + app_scheduler.c + 1 + ..\SDK\12.3.0_d7731ad\components\libraries\scheduler\app_scheduler.c + + + app_timer.c + 1 + ..\SDK\12.3.0_d7731ad\components\libraries\timer\app_timer.c + + + app_timer_appsh.c + 1 + ..\SDK\12.3.0_d7731ad\components\libraries\timer\app_timer_appsh.c + + + app_util_platform.c + 1 + ..\SDK\12.3.0_d7731ad\components\libraries\util\app_util_platform.c + + + crc32.c + 1 + ..\SDK\12.3.0_d7731ad\components\libraries\crc32\crc32.c + + + ecc.c + 1 + ..\SDK\12.3.0_d7731ad\components\libraries\ecc\ecc.c + + + fstorage.c + 1 + ..\SDK\12.3.0_d7731ad\components\libraries\fstorage\fstorage.c + + + hci_mem_pool.c + 1 + ..\SDK\12.3.0_d7731ad\components\libraries\hci\hci_mem_pool.c + + + nrf_assert.c + 1 + ..\SDK\12.3.0_d7731ad\components\libraries\util\nrf_assert.c + + + nrf_crypto.c + 1 + ..\SDK\12.3.0_d7731ad\components\libraries\crypto\nrf_crypto.c + + + nrf_queue.c + 1 + ..\SDK\12.3.0_d7731ad\components\libraries\queue\nrf_queue.c + + + sdk_errors.c + 1 + ..\SDK\12.3.0_d7731ad\components\libraries\util\sdk_errors.c + + + sha256.c + 1 + ..\SDK\12.3.0_d7731ad\components\libraries\sha256\sha256.c + + + + + nRF_SoftDevice + + + softdevice_handler.c + 1 + ..\SDK\12.3.0_d7731ad\components\softdevice\common\softdevice_handler\softdevice_handler.c + + + softdevice_handler_appsh.c + 1 + ..\SDK\12.3.0_d7731ad\components\softdevice\common\softdevice_handler\softdevice_handler_appsh.c + + + + + nRF_micro_ecc + + + micro_ecc_lib_nrf51.lib + 4 + ..\SDK\12.3.0_d7731ad\external\micro-ecc\nrf51_keil\armgcc\micro_ecc_lib_nrf51.lib + + + + + nano-pb + + + pb_common.c + 1 + ..\SDK\12.3.0_d7731ad\external\nano-pb\pb_common.c + + + pb_decode.c + 1 + ..\SDK\12.3.0_d7731ad\external\nano-pb\pb_decode.c + + + + + Startup + + + arm_startup_nrf51.s + 2 + ..\SDK\12.3.0_d7731ad\components\toolchain\arm\arm_startup_nrf51.s + + + system_nrf51.c + 1 + ..\SDK\12.3.0_d7731ad\components\toolchain\system_nrf51.c + + + + + + + + + + + + + + + + + + + + + + + RTE\Device\nRF51422_xxAC\arm_startup_nrf51.s + + + + + + RTE\Device\nRF51422_xxAC\system_nrf51.c + + + + + + RTE\Device\nRF51822_xxAA\arm_startup_nrf51.s + + + + + + RTE\Device\nRF51822_xxAA\system_nrf51.c + + + + + + + + + + + secure_dfu_secure_dfu_ble_s130_pca10028 + 1 + + + + +
diff --git a/SDK/12.3.0_d7731ad/components/boards/pca10028.h b/SDK/12.3.0_d7731ad/components/boards/pca10028.h index 6d736ab..afa16de 100644 --- a/SDK/12.3.0_d7731ad/components/boards/pca10028.h +++ b/SDK/12.3.0_d7731ad/components/boards/pca10028.h @@ -167,7 +167,7 @@ extern "C" { #ifdef S210 #define NRF_CLOCK_LFCLKSRC NRF_CLOCK_LFCLKSRC_XTAL_20_PPM #else -#define NRF_CLOCK_LFCLKSRC {.source = NRF_CLOCK_LF_SRC_XTAL, \ +#define NRF_CLOCK_LFCLKSRC {.source = NRF_CLOCK_LF_SRC_SYNTH, \ .rc_ctiv = 0, \ .rc_temp_ctiv = 0, \ .xtal_accuracy = NRF_CLOCK_LF_XTAL_ACCURACY_20_PPM} diff --git a/SDK/12.3.0_d7731ad/dfu/dfu-cc.pb.c b/SDK/12.3.0_d7731ad/dfu/dfu-cc.pb.c new file mode 100644 index 0000000..af90425 --- /dev/null +++ b/SDK/12.3.0_d7731ad/dfu/dfu-cc.pb.c @@ -0,0 +1,122 @@ +/** + * Copyright (c) 2016 - 2017, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.3.6-dev at Thu Jul 28 13:34:59 2016. */ + +#include "dfu-cc.pb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + +const bool dfu_init_command_is_debug_default = false; + + +const pb_field_t dfu_hash_fields[3] = { + PB_FIELD( 1, UENUM , REQUIRED, STATIC , FIRST, dfu_hash_t, hash_type, hash_type, 0), + PB_FIELD( 2, BYTES , REQUIRED, STATIC , OTHER, dfu_hash_t, hash, hash_type, 0), + PB_LAST_FIELD +}; + +const pb_field_t dfu_init_command_fields[10] = { + PB_FIELD( 1, UINT32 , OPTIONAL, STATIC , FIRST, dfu_init_command_t, fw_version, fw_version, 0), + PB_FIELD( 2, UINT32 , OPTIONAL, STATIC , OTHER, dfu_init_command_t, hw_version, fw_version, 0), + PB_FIELD( 3, UINT32 , REPEATED, STATIC , OTHER, dfu_init_command_t, sd_req, hw_version, 0), + PB_FIELD( 4, UENUM , OPTIONAL, STATIC , OTHER, dfu_init_command_t, type, sd_req, 0), + PB_FIELD( 5, UINT32 , OPTIONAL, STATIC , OTHER, dfu_init_command_t, sd_size, type, 0), + PB_FIELD( 6, UINT32 , OPTIONAL, STATIC , OTHER, dfu_init_command_t, bl_size, sd_size, 0), + PB_FIELD( 7, UINT32 , OPTIONAL, STATIC , OTHER, dfu_init_command_t, app_size, bl_size, 0), + PB_FIELD( 8, MESSAGE , OPTIONAL, STATIC , OTHER, dfu_init_command_t, hash, app_size, &dfu_hash_fields), + PB_FIELD( 9, BOOL , OPTIONAL, STATIC , OTHER, dfu_init_command_t, is_debug, hash, &dfu_init_command_is_debug_default), + PB_LAST_FIELD +}; + +const pb_field_t dfu_reset_command_fields[2] = { + PB_FIELD( 1, UINT32 , REQUIRED, STATIC , FIRST, dfu_reset_command_t, timeout, timeout, 0), + PB_LAST_FIELD +}; + +const pb_field_t dfu_command_fields[4] = { + PB_FIELD( 1, UENUM , OPTIONAL, STATIC , FIRST, dfu_command_t, op_code, op_code, 0), + PB_FIELD( 2, MESSAGE , OPTIONAL, STATIC , OTHER, dfu_command_t, init, op_code, &dfu_init_command_fields), + PB_FIELD( 3, MESSAGE , OPTIONAL, STATIC , OTHER, dfu_command_t, reset, init, &dfu_reset_command_fields), + PB_LAST_FIELD +}; + +const pb_field_t dfu_signed_command_fields[4] = { + PB_FIELD( 1, MESSAGE , REQUIRED, STATIC , FIRST, dfu_signed_command_t, command, command, &dfu_command_fields), + PB_FIELD( 2, UENUM , REQUIRED, STATIC , OTHER, dfu_signed_command_t, signature_type, command, 0), + PB_FIELD( 3, BYTES , REQUIRED, STATIC , OTHER, dfu_signed_command_t, signature, signature_type, 0), + PB_LAST_FIELD +}; + +const pb_field_t dfu_packet_fields[3] = { + PB_FIELD( 1, MESSAGE , OPTIONAL, STATIC , FIRST, dfu_packet_t, command, command, &dfu_command_fields), + PB_FIELD( 2, MESSAGE , OPTIONAL, STATIC , OTHER, dfu_packet_t, signed_command, command, &dfu_signed_command_fields), + PB_LAST_FIELD +}; + + +/* Check that field information fits in pb_field_t */ +#if !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_32BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in 8 or 16 bit + * field descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(dfu_init_command_t, hash) < 65536 && pb_membersize(dfu_command_t, init) < 65536 && pb_membersize(dfu_command_t, reset) < 65536 && pb_membersize(dfu_signed_command_t, command) < 65536 && pb_membersize(dfu_packet_t, command) < 65536 && pb_membersize(dfu_packet_t, signed_command) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_dfu_hash_dfu_init_command_dfu_reset_command_dfu_command_dfu_signed_command_dfu_packet) +#endif + +#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_16BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in the default + * 8 bit descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(dfu_init_command_t, hash) < 256 && pb_membersize(dfu_command_t, init) < 256 && pb_membersize(dfu_command_t, reset) < 256 && pb_membersize(dfu_signed_command_t, command) < 256 && pb_membersize(dfu_packet_t, command) < 256 && pb_membersize(dfu_packet_t, signed_command) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_dfu_hash_dfu_init_command_dfu_reset_command_dfu_command_dfu_signed_command_dfu_packet) +#endif + + +/* @@protoc_insertion_point(eof) */ diff --git a/SDK/12.3.0_d7731ad/dfu/dfu-cc.pb.h b/SDK/12.3.0_d7731ad/dfu/dfu-cc.pb.h new file mode 100644 index 0000000..997d29a --- /dev/null +++ b/SDK/12.3.0_d7731ad/dfu/dfu-cc.pb.h @@ -0,0 +1,227 @@ +/** + * Copyright (c) 2016 - 2017, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.3.6-dev at Thu Jul 28 13:34:59 2016. */ + +#ifndef PB_DFU_CC_PB_H_INCLUDED +#define PB_DFU_CC_PB_H_INCLUDED +#include + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Enum definitions */ +typedef enum +{ + DFU_OP_CODE_RESET = 0, + DFU_OP_CODE_INIT = 1 +} dfu_op_code_t; +#define DFU_OP_CODE_MIN DFU_OP_CODE_RESET +#define DFU_OP_CODE_MAX DFU_OP_CODE_INIT +#define DFU_OP_CODE_ARRAYSIZE ((dfu_op_code_t)(DFU_OP_CODE_INIT+1)) + +typedef enum +{ + DFU_FW_TYPE_APPLICATION = 0, + DFU_FW_TYPE_SOFTDEVICE = 1, + DFU_FW_TYPE_BOOTLOADER = 2, + DFU_FW_TYPE_SOFTDEVICE_BOOTLOADER = 3 +} dfu_fw_type_t; +#define DFU_FW_TYPE_MIN DFU_FW_TYPE_APPLICATION +#define DFU_FW_TYPE_MAX DFU_FW_TYPE_SOFTDEVICE_BOOTLOADER +#define DFU_FW_TYPE_ARRAYSIZE ((dfu_fw_type_t)(DFU_FW_TYPE_SOFTDEVICE_BOOTLOADER+1)) + +typedef enum +{ + DFU_HASH_TYPE_NO_HASH = 0, + DFU_HASH_TYPE_CRC = 1, + DFU_HASH_TYPE_SHA128 = 2, + DFU_HASH_TYPE_SHA256 = 3, + DFU_HASH_TYPE_SHA512 = 4 +} dfu_hash_type_t; +#define DFU_HASH_TYPE_MIN DFU_HASH_TYPE_NO_HASH +#define DFU_HASH_TYPE_MAX DFU_HASH_TYPE_SHA512 +#define DFU_HASH_TYPE_ARRAYSIZE ((dfu_hash_type_t)(DFU_HASH_TYPE_SHA512+1)) + +typedef enum +{ + DFU_SIGNATURE_TYPE_ECDSA_P256_SHA256 = 0, + DFU_SIGNATURE_TYPE_ED25519 = 1 +} dfu_signature_type_t; +#define DFU_SIGNATURE_TYPE_MIN DFU_SIGNATURE_TYPE_ECDSA_P256_SHA256 +#define DFU_SIGNATURE_TYPE_MAX DFU_SIGNATURE_TYPE_ED25519 +#define DFU_SIGNATURE_TYPE_ARRAYSIZE ((dfu_signature_type_t)(DFU_SIGNATURE_TYPE_ED25519+1)) + +/* Struct definitions */ +typedef PB_BYTES_ARRAY_T(32) dfu_hash_hash_t; +typedef struct { + dfu_hash_type_t hash_type; + dfu_hash_hash_t hash; +/* @@protoc_insertion_point(struct:dfu_hash_t) */ +} dfu_hash_t; + +typedef struct { + uint32_t timeout; +/* @@protoc_insertion_point(struct:dfu_reset_command_t) */ +} dfu_reset_command_t; + +typedef struct { + bool has_fw_version; + uint32_t fw_version; + bool has_hw_version; + uint32_t hw_version; + pb_size_t sd_req_count; + uint32_t sd_req[4]; + bool has_type; + dfu_fw_type_t type; + bool has_sd_size; + uint32_t sd_size; + bool has_bl_size; + uint32_t bl_size; + bool has_app_size; + uint32_t app_size; + bool has_hash; + dfu_hash_t hash; + bool has_is_debug; + bool is_debug; +/* @@protoc_insertion_point(struct:dfu_init_command_t) */ +} dfu_init_command_t; + +typedef struct { + bool has_op_code; + dfu_op_code_t op_code; + bool has_init; + dfu_init_command_t init; + bool has_reset; + dfu_reset_command_t reset; +/* @@protoc_insertion_point(struct:dfu_command_t) */ +} dfu_command_t; + +typedef PB_BYTES_ARRAY_T(64) dfu_signed_command_signature_t; +typedef struct { + dfu_command_t command; + dfu_signature_type_t signature_type; + dfu_signed_command_signature_t signature; +/* @@protoc_insertion_point(struct:dfu_signed_command_t) */ +} dfu_signed_command_t; + +typedef struct { + bool has_command; + dfu_command_t command; + bool has_signed_command; + dfu_signed_command_t signed_command; +/* @@protoc_insertion_point(struct:dfu_packet_t) */ +} dfu_packet_t; + +/* Default values for struct fields */ +extern const bool dfu_init_command_is_debug_default; + +/* Initializer values for message structs */ +#define DFU_HASH_INIT_DEFAULT {(dfu_hash_type_t)0, {0, {0}}} +#define DFU_INIT_COMMAND_INIT_DEFAULT {false, 0, false, 0, 0, {0, 0, 0, 0}, false, (dfu_fw_type_t)0, false, 0, false, 0, false, 0, false, DFU_HASH_INIT_DEFAULT, false, false} +#define DFU_RESET_COMMAND_INIT_DEFAULT {0} +#define DFU_COMMAND_INIT_DEFAULT {false, (dfu_op_code_t)0, false, DFU_INIT_COMMAND_INIT_DEFAULT, false, DFU_RESET_COMMAND_INIT_DEFAULT} +#define DFU_SIGNED_COMMAND_INIT_DEFAULT {DFU_COMMAND_INIT_DEFAULT, (dfu_signature_type_t)0, {0, {0}}} +#define DFU_PACKET_INIT_DEFAULT {false, DFU_COMMAND_INIT_DEFAULT, false, DFU_SIGNED_COMMAND_INIT_DEFAULT} +#define DFU_HASH_INIT_ZERO {(dfu_hash_type_t)0, {0, {0}}} +#define DFU_INIT_COMMAND_INIT_ZERO {false, 0, false, 0, 0, {0, 0, 0, 0}, false, (dfu_fw_type_t)0, false, 0, false, 0, false, 0, false, DFU_HASH_INIT_ZERO, false, 0} +#define DFU_RESET_COMMAND_INIT_ZERO {0} +#define DFU_COMMAND_INIT_ZERO {false, (dfu_op_code_t)0, false, DFU_INIT_COMMAND_INIT_ZERO, false, DFU_RESET_COMMAND_INIT_ZERO} +#define DFU_SIGNED_COMMAND_INIT_ZERO {DFU_COMMAND_INIT_ZERO, (dfu_signature_type_t)0, {0, {0}}} +#define DFU_PACKET_INIT_ZERO {false, DFU_COMMAND_INIT_ZERO, false, DFU_SIGNED_COMMAND_INIT_ZERO} + +/* Field tags (for use in manual encoding/decoding) */ +#define DFU_HASH_HASH_TYPE_TAG 1 +#define DFU_HASH_HASH_TAG 2 +#define DFU_RESET_COMMAND_TIMEOUT_TAG 1 +#define DFU_INIT_COMMAND_FW_VERSION_TAG 1 +#define DFU_INIT_COMMAND_HW_VERSION_TAG 2 +#define DFU_INIT_COMMAND_SD_REQ_TAG 3 +#define DFU_INIT_COMMAND_TYPE_TAG 4 +#define DFU_INIT_COMMAND_SD_SIZE_TAG 5 +#define DFU_INIT_COMMAND_BL_SIZE_TAG 6 +#define DFU_INIT_COMMAND_APP_SIZE_TAG 7 +#define DFU_INIT_COMMAND_HASH_TAG 8 +#define DFU_INIT_COMMAND_IS_DEBUG_TAG 9 +#define DFU_COMMAND_OP_CODE_TAG 1 +#define DFU_COMMAND_INIT_TAG 2 +#define DFU_COMMAND_RESET_TAG 3 +#define DFU_SIGNED_COMMAND_COMMAND_TAG 1 +#define DFU_SIGNED_COMMAND_SIGNATURE_TYPE_TAG 2 +#define DFU_SIGNED_COMMAND_SIGNATURE_TAG 3 +#define DFU_PACKET_COMMAND_TAG 1 +#define DFU_PACKET_SIGNED_COMMAND_TAG 2 + +/* Struct field encoding specification for nanopb */ +extern const pb_field_t dfu_hash_fields[3]; +extern const pb_field_t dfu_init_command_fields[10]; +extern const pb_field_t dfu_reset_command_fields[2]; +extern const pb_field_t dfu_command_fields[4]; +extern const pb_field_t dfu_signed_command_fields[4]; +extern const pb_field_t dfu_packet_fields[3]; + +/* Maximum encoded size of messages (where known) */ +#define DFU_HASH_SIZE 36 +#define DFU_INIT_COMMAND_SIZE 96 +#define DFU_RESET_COMMAND_SIZE 6 +#define DFU_COMMAND_SIZE 108 +#define DFU_SIGNED_COMMAND_SIZE 178 +#define DFU_PACKET_SIZE 291 + +/* Message IDs (where set with "msgid" option) */ +#ifdef PB_MSGID + +#define DFU_CC_MESSAGES \ + + +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif +/* @@protoc_insertion_point(eof) */ + +#endif diff --git a/SDK/12.3.0_d7731ad/dfu/dfu_public_key.c b/SDK/12.3.0_d7731ad/dfu/dfu_public_key.c new file mode 100644 index 0000000..5316e73 --- /dev/null +++ b/SDK/12.3.0_d7731ad/dfu/dfu_public_key.c @@ -0,0 +1,12 @@ + +/* This file was automatically generated by nrfutil on 2025-05-12 (YY-MM-DD) at 17:25:15 */ + +#include "stdint.h" +#include "compiler_abstraction.h" + +/** @brief Public key used to verify DFU images */ +__ALIGN(4) const uint8_t pk[64] = +{ + 0xcf, 0x3f, 0x99, 0x8e, 0x73, 0xeb, 0xca, 0x98, 0xfe, 0x89, 0x7b, 0x51, 0xa5, 0xe5, 0x55, 0x65, 0x8c, 0xe6, 0x00, 0x77, 0x7a, 0x1e, 0x7c, 0xd2, 0x8d, 0xa4, 0xb8, 0x71, 0x0b, 0xae, 0x51, 0x18, + 0x08, 0x22, 0xe3, 0xc7, 0x19, 0x0c, 0x0e, 0x58, 0xa0, 0xdd, 0xe3, 0xbc, 0xb5, 0xf7, 0x19, 0x6f, 0xfe, 0xc0, 0xc1, 0xf8, 0x8d, 0x34, 0x7b, 0xb3, 0xf2, 0x06, 0xfe, 0x96, 0x6c, 0x21, 0x5e, 0x55 +}; diff --git a/SDK/12.3.0_d7731ad/dfu/dfu_req_handling.c b/SDK/12.3.0_d7731ad/dfu/dfu_req_handling.c new file mode 100644 index 0000000..8329113 --- /dev/null +++ b/SDK/12.3.0_d7731ad/dfu/dfu_req_handling.c @@ -0,0 +1,1074 @@ +/** + * Copyright (c) 2016 - 2017, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "nrf_dfu_req_handler.h" + +#include +#include +#include "dfu_req_handling.h" +#include "nrf_dfu.h" +#include "nrf_dfu_types.h" +#include "nrf_dfu_settings.h" +#include "nrf_dfu_transport.h" +#include "nrf_dfu_utils.h" +#include "nrf_dfu_flash.h" +#include "nrf_ble_dfu.h" +#include "nrf_bootloader_info.h" +#include "pb.h" +#include "pb_common.h" +#include "pb_decode.h" +#include "dfu-cc.pb.h" +#include "crc32.h" +#include "nrf_log.h" +#include "app_util.h" +#include "nrf_sdm.h" +#include "sdk_macros.h" +#include "nrf_crypto.h" + +STATIC_ASSERT(DFU_SIGNED_COMMAND_SIZE <= INIT_COMMAND_MAX_SIZE); + + +/** @brief Macro for the hardware version of the kit used for requirement-match + * + * @note If not set, this will default to 51 or 52 according to the architecture + */ +#if defined ( NRF51 ) && !defined(NRF_DFU_HW_VERSION) + #define NRF_DFU_HW_VERSION (51) +#elif defined ( NRF52 ) && !defined(NRF_DFU_HW_VERSION) + #define NRF_DFU_HW_VERSION (52) +#else + #error No target set for HW version. +#endif + +/** @brief Cyclic buffers for storing data that is to be written to flash. + * This is because the RAM copy must be kept alive until copying is + * done and the DFU process must be able to progress while waiting for flash. + * + */ +#define FLASH_BUFFER_CHUNK_LENGTH 256 //< Length of a flash buffer chunk. must be a power of 4. +#define FLASH_BUFFER_CHUNK_COUNT 4 //< Number of flash buffer chunks. Must be a power of 2. +#define FLASH_BUFFER_SWAP() do \ + {m_current_data_buffer = (m_current_data_buffer + 1) & 0x03; m_data_buf_pos = 0;} \ + while (0) + +__ALIGN(4) static uint8_t m_data_buf[FLASH_BUFFER_CHUNK_COUNT][FLASH_BUFFER_CHUNK_LENGTH]; + +static uint16_t m_data_buf_pos; /**< The number of bytes written in the current buffer. */ +static uint8_t m_current_data_buffer; /**< Index of the current data buffer. Must be between 0 and FLASH_BUFFER_CHUNK_COUNT - 1. */ +static uint32_t m_flash_operations_pending; /**< A counter holding the number of pending flash operations. This will prevent flooding of the buffers. */ + +static uint32_t m_firmware_start_addr; /**< Start address of the current firmware image. */ +static uint32_t m_firmware_size_req; /**< The size of the entire firmware image. Defined by the init command. */ + +static bool m_valid_init_packet_present; /**< Global variable holding the current flags indicating the state of the DFU process. */ + + + + +static const nrf_crypto_key_t crypto_key_pk = +{ + .p_le_data = (uint8_t *) pk, + .len = sizeof(pk) +}; + +static nrf_crypto_key_t crypto_sig; +__ALIGN(4) static uint8_t hash[32]; +static nrf_crypto_key_t hash_data; + +__ALIGN(4) static uint8_t sig[64]; + +dfu_hash_type_t m_image_hash_type; + +static dfu_packet_t packet = DFU_PACKET_INIT_DEFAULT; + +static pb_istream_t stream; + + +static void on_dfu_complete(fs_evt_t const * const evt, fs_ret_t result) +{ + NRF_LOG_INFO("Resetting device. \r\n"); + (void)nrf_dfu_transports_close(); + NVIC_SystemReset(); + return; +} + + +static void dfu_data_write_handler(fs_evt_t const * const evt, fs_ret_t result) +{ + --m_flash_operations_pending; +} + + +static void pb_decoding_callback(pb_istream_t *str, uint32_t tag, pb_wire_type_t wire_type, void *iter) +{ + pb_field_iter_t* p_iter = (pb_field_iter_t *) iter; + + // match the beginning of the init command + if(p_iter->pos->ptr == &dfu_init_command_fields[0]) + { + uint8_t *ptr = (uint8_t *) str->state; + uint32_t size = str->bytes_left; + + // remove tag byte + ptr++; + size--; + + // store the info in hash_data + hash_data.p_le_data = ptr; + hash_data.len = size; + + NRF_LOG_INFO("PB: Init data len: %d\r\n", hash_data.len); + } +} + + +static nrf_dfu_res_code_t dfu_handle_prevalidate(dfu_signed_command_t const * p_command, pb_istream_t * p_stream, uint8_t * p_init_cmd, uint32_t init_cmd_len) +{ + dfu_init_command_t const * p_init = &p_command->command.init; + uint32_t err_code; + uint32_t hw_version = NRF_DFU_HW_VERSION; + uint32_t fw_version = 0; + + // check for init command found during decoding + if(!p_init_cmd || !init_cmd_len) + { + return NRF_DFU_RES_CODE_OPERATION_FAILED; + } + +#ifndef NRF_DFU_DEBUG_VERSION + if(p_init->has_is_debug && p_init->is_debug == true) + { + return NRF_DFU_RES_CODE_OPERATION_FAILED; + } +#endif + +#ifdef NRF_DFU_DEBUG_VERSION + if (p_init->has_is_debug == false || p_init->is_debug == false) + { +#endif + if (p_init->has_hw_version == false) + { + return NRF_DFU_RES_CODE_OPERATION_FAILED; + } + + // Check of init command HW version + if(p_init->hw_version != hw_version) + { + return NRF_DFU_RES_CODE_OPERATION_FAILED; + } + + // Precheck the SoftDevice version + bool found_sd_ver = false; + for(int i = 0; i < p_init->sd_req_count; i++) + { + if (p_init->sd_req[i] == SD_FWID_GET(MBR_SIZE)) + { + found_sd_ver = true; + break; + } + } + if (!found_sd_ver) + { + return NRF_DFU_RES_CODE_OPERATION_FAILED; + } + + // Get the fw version + switch (p_init->type) + { + case DFU_FW_TYPE_APPLICATION: + if (p_init->has_fw_version == false) + { + return NRF_DFU_RES_CODE_OPERATION_FAILED; + } + // Get the application FW version + fw_version = s_dfu_settings.app_version; + break; + + case DFU_FW_TYPE_SOFTDEVICE: + // not loaded + break; + + case DFU_FW_TYPE_BOOTLOADER: // fall through + case DFU_FW_TYPE_SOFTDEVICE_BOOTLOADER: + if (p_init->has_fw_version == false) + { + return NRF_DFU_RES_CODE_OPERATION_FAILED; + } + fw_version = s_dfu_settings.bootloader_version; + break; + + default: + NRF_LOG_INFO("Unknown FW update type\r\n"); + return NRF_DFU_RES_CODE_OPERATION_FAILED; + } + + NRF_LOG_INFO("Req version: %d, Present: %d\r\n", p_init->fw_version, fw_version); + + // Check of init command FW version + switch (p_init->type) + { + case DFU_FW_TYPE_APPLICATION: + if (p_init->fw_version < fw_version) + { + return NRF_DFU_RES_CODE_OPERATION_FAILED; + } + break; + + case DFU_FW_TYPE_BOOTLOADER: // fall through + case DFU_FW_TYPE_SOFTDEVICE_BOOTLOADER: + // updating the bootloader is stricter. There must be an increase in version number + if (p_init->fw_version <= fw_version) + { + return NRF_DFU_RES_CODE_OPERATION_FAILED; + } + break; + + default: + // do not care about fw_version in the case of a softdevice transfer + break; + } + +#ifdef NRF_DFU_DEBUG_VERSION + } +#endif + + // Check the signature + switch (p_command->signature_type) + { + case DFU_SIGNATURE_TYPE_ECDSA_P256_SHA256: + { + // prepare the actual hash destination. + hash_data.p_le_data = &hash[0]; + hash_data.len = sizeof(hash); + + NRF_LOG_INFO("Init command:\r\n"); + NRF_LOG_HEXDUMP_INFO(&s_dfu_settings.init_command[0], s_dfu_settings.progress.command_size); + NRF_LOG_INFO("\r\n"); + + NRF_LOG_INFO("p_Init command:\r\n"); + NRF_LOG_HEXDUMP_INFO(&p_init_cmd[0], init_cmd_len); + NRF_LOG_INFO("\r\n"); + + err_code = nrf_crypto_hash_compute(NRF_CRYPTO_HASH_ALG_SHA256, p_init_cmd, init_cmd_len, &hash_data); + if (err_code != NRF_SUCCESS) + { + return NRF_DFU_RES_CODE_OPERATION_FAILED; + } + + // prepare the signature received over the air. + memcpy(&sig[0], p_command->signature.bytes, p_command->signature.size); + + NRF_LOG_INFO("Signature\r\n"); + NRF_LOG_HEXDUMP_INFO(&p_command->signature.bytes[0], p_command->signature.size); + NRF_LOG_INFO("\r\n"); + + crypto_sig.p_le_data = sig; + crypto_sig.len = p_command->signature.size; + + NRF_LOG_INFO("signature len: %d\r\n", p_command->signature.size); + + // calculate the signature + err_code = nrf_crypto_verify(NRF_CRYPTO_CURVE_SECP256R1, &crypto_key_pk, &hash_data, &crypto_sig); + if (err_code != NRF_SUCCESS) + { + return NRF_DFU_RES_CODE_INVALID_OBJECT; + } + + NRF_LOG_INFO("Image verified\r\n"); + } + break; + + default: + return NRF_DFU_RES_CODE_OPERATION_FAILED; + } + + // Get the update size + m_firmware_size_req = 0; + + switch (p_init->type) + { + case DFU_FW_TYPE_APPLICATION: + if (p_init->has_app_size == false) + { + return NRF_DFU_RES_CODE_OPERATION_FAILED; + } + m_firmware_size_req += p_init->app_size; + break; + + case DFU_FW_TYPE_BOOTLOADER: + if (p_init->has_bl_size == false) + { + return NRF_DFU_RES_CODE_OPERATION_FAILED; + } + m_firmware_size_req += p_init->bl_size; + // check that the size of the bootloader is not larger than the present one. +#if defined ( NRF51 ) + if (p_init->bl_size > BOOTLOADER_SETTINGS_ADDRESS - BOOTLOADER_START_ADDR) +#elif defined ( NRF52 ) + if (p_init->bl_size > NRF_MBR_PARAMS_PAGE_ADDRESS - BOOTLOADER_START_ADDR) +#endif + { + return NRF_DFU_RES_CODE_INSUFFICIENT_RESOURCES; + } + break; + + case DFU_FW_TYPE_SOFTDEVICE: + if (p_init->has_sd_size == false) + { + return NRF_DFU_RES_CODE_OPERATION_FAILED; + } + m_firmware_size_req += p_init->sd_size; + break; + + case DFU_FW_TYPE_SOFTDEVICE_BOOTLOADER: + if (p_init->has_bl_size == false || p_init->has_sd_size == false) + { + return NRF_DFU_RES_CODE_OPERATION_FAILED; + } + m_firmware_size_req += p_init->sd_size + p_init->bl_size; + if (p_init->sd_size == 0 || p_init->bl_size == 0) + { + return NRF_DFU_RES_CODE_INVALID_PARAMETER; + } + + // check that the size of the bootloader is not larger than the present one. +#if defined ( NRF51 ) + if (p_init->bl_size > BOOTLOADER_SETTINGS_ADDRESS - BOOTLOADER_START_ADDR) +#elif defined ( NRF52 ) + if (p_init->bl_size > NRF_MBR_PARAMS_PAGE_ADDRESS - BOOTLOADER_START_ADDR) +#endif + { + return NRF_DFU_RES_CODE_INSUFFICIENT_RESOURCES; + } + break; + + default: + NRF_LOG_INFO("Unknown FW update type\r\n"); + return NRF_DFU_RES_CODE_OPERATION_FAILED; + } + + // SHA256 is the only supported hash + memcpy(&hash[0], &p_init->hash.hash.bytes[0], 32); + + // Instead of checking each type with has-check, check the result of the size_req to + // Validate its content. + if (m_firmware_size_req == 0) + { + return NRF_DFU_RES_CODE_INVALID_PARAMETER; + } + + // Find the location to place the DFU updates + err_code = nrf_dfu_find_cache(m_firmware_size_req, false, &m_firmware_start_addr); + if (err_code != NRF_SUCCESS) + { + return NRF_DFU_RES_CODE_INSUFFICIENT_RESOURCES; + } + + NRF_LOG_INFO("Write address set to 0x%08x\r\n", m_firmware_start_addr); + + NRF_LOG_INFO("DFU prevalidate SUCCESSFUL!\r\n"); + + return NRF_DFU_RES_CODE_SUCCESS; +} + + +/** @brief Function for validating the received image after all objects have been received and executed. + * + */ +static nrf_dfu_res_code_t nrf_dfu_postvalidate(dfu_init_command_t * p_init) +{ + uint32_t err_code; + nrf_dfu_res_code_t res_code = NRF_DFU_RES_CODE_SUCCESS; + nrf_dfu_bank_t * p_bank; + + switch (p_init->hash.hash_type) + { + case DFU_HASH_TYPE_SHA256: + hash_data.p_le_data = &hash[0]; + hash_data.len = sizeof(hash); + err_code = nrf_crypto_hash_compute(NRF_CRYPTO_HASH_ALG_SHA256, (uint8_t*)m_firmware_start_addr, m_firmware_size_req, &hash_data); + if (err_code != NRF_SUCCESS) + { + res_code = NRF_DFU_RES_CODE_OPERATION_FAILED; + } + + if (memcmp(&hash_data.p_le_data[0], &p_init->hash.hash.bytes[0], 32) != 0) + { + NRF_LOG_INFO("Hash failure\r\n"); + + res_code = NRF_DFU_RES_CODE_INVALID_OBJECT; + } + break; + + default: + res_code = NRF_DFU_RES_CODE_OPERATION_FAILED; + break; + } + + if (s_dfu_settings.bank_current == NRF_DFU_CURRENT_BANK_0) + { + NRF_LOG_INFO("Current bank is bank 0\r\n"); + p_bank = &s_dfu_settings.bank_0; + } + else if (s_dfu_settings.bank_current == NRF_DFU_CURRENT_BANK_1) + { + NRF_LOG_INFO("Current bank is bank 1\r\n"); + p_bank = &s_dfu_settings.bank_1; + } + else + { + NRF_LOG_INFO("Internal error, invalid current bank\r\n"); + return NRF_DFU_RES_CODE_OPERATION_FAILED; + } + + if (res_code == NRF_DFU_RES_CODE_SUCCESS) + { + NRF_LOG_INFO("Successfully run the postvalidation check!\r\n"); + + switch (p_init->type) + { + case DFU_FW_TYPE_APPLICATION: + p_bank->bank_code = NRF_DFU_BANK_VALID_APP; + break; + case DFU_FW_TYPE_SOFTDEVICE: + p_bank->bank_code = NRF_DFU_BANK_VALID_SD; + s_dfu_settings.sd_size = p_init->sd_size; + break; + case DFU_FW_TYPE_BOOTLOADER: + p_bank->bank_code = NRF_DFU_BANK_VALID_BL; + break; + case DFU_FW_TYPE_SOFTDEVICE_BOOTLOADER: + p_bank->bank_code = NRF_DFU_BANK_VALID_SD_BL; + s_dfu_settings.sd_size = p_init->sd_size; + break; + default: + res_code = NRF_DFU_RES_CODE_OPERATION_FAILED; + break; + } + +#ifdef NRF_DFU_DEBUG_VERSION + if (p_init->has_is_debug == false || p_init->is_debug == false) + { +#endif + + switch (p_init->type) + { + case DFU_FW_TYPE_APPLICATION: + s_dfu_settings.app_version = p_init->fw_version; + break; + case DFU_FW_TYPE_BOOTLOADER: + case DFU_FW_TYPE_SOFTDEVICE_BOOTLOADER: + s_dfu_settings.bootloader_version = p_init->fw_version; + break; + default: + // no implementation + break; + } + +#ifdef NRF_DFU_DEBUG_VERSION + } +#endif + // Calculate CRC32 for image + p_bank->image_crc = s_dfu_settings.progress.firmware_image_crc; + p_bank->image_size = m_firmware_size_req; + } + else + { + p_bank->bank_code = NRF_DFU_BANK_INVALID; + + // Calculate CRC32 for image + p_bank->image_crc = 0; + p_bank->image_size = 0; + } + + // Set the progress to zero and remove the last command + memset(&s_dfu_settings.progress, 0, sizeof(dfu_progress_t)); + memset(s_dfu_settings.init_command, 0xFF, DFU_SIGNED_COMMAND_SIZE); + s_dfu_settings.write_offset = 0; + + // Store the settings to flash and reset after that + if( nrf_dfu_settings_write(on_dfu_complete) != NRF_SUCCESS) + { + res_code = NRF_DFU_RES_CODE_OPERATION_FAILED; + } + + return res_code; +} + + +/** @brief Function to handle signed command + * + * @param[in] p_command Signed + */ +static nrf_dfu_res_code_t dfu_handle_signed_command(dfu_signed_command_t const * p_command, pb_istream_t * p_stream) +{ + nrf_dfu_res_code_t ret_val = NRF_DFU_RES_CODE_SUCCESS; + + // Currently only init-packet is signed + if (p_command->command.has_init != true) + { + return NRF_DFU_RES_CODE_INVALID_OBJECT; + } + + ret_val = dfu_handle_prevalidate(p_command, p_stream, hash_data.p_le_data, hash_data.len); + if(ret_val == NRF_DFU_RES_CODE_SUCCESS) + { + NRF_LOG_INFO("Prevalidate OK.\r\n"); + + // This saves the init command to flash + NRF_LOG_INFO("Saving init command...\r\n"); + if (nrf_dfu_settings_write(NULL) != NRF_SUCCESS) + { + return NRF_DFU_RES_CODE_OPERATION_FAILED; + } + } + else + { + NRF_LOG_INFO("Prevalidate FAILED!\r\n"); + } + return ret_val; +} + + +static nrf_dfu_res_code_t dfu_handle_command(dfu_command_t const * p_command) +{ + return NRF_DFU_RES_CODE_OPERATION_FAILED; +} + + +static uint32_t dfu_decode_commmand(void) +{ + stream = pb_istream_from_buffer(s_dfu_settings.init_command, s_dfu_settings.progress.command_size); + + // Attach our callback to follow the field decoding + stream.decoding_callback = pb_decoding_callback; + // reset the variable where the init pointer and length will be stored. + hash_data.p_le_data = NULL; + hash_data.len = 0; + + if (!pb_decode(&stream, dfu_packet_fields, &packet)) + { + NRF_LOG_INFO("Handler: Invalid protocol buffer stream\r\n"); + return 0; + } + + return 1; +} + + +/** @brief Function handling command requests from the transport layer. + * + * @param p_context[in,out] Pointer to structure holding context-specific data + * @param p_req[in] Pointer to the structure holding the DFU request. + * @param p_res[out] Pointer to the structure holding the DFU response. + * + * @retval NRF_SUCCESS If the command request was executed successfully. + * Any other error code indicates that the data request + * could not be handled. + */ +static nrf_dfu_res_code_t nrf_dfu_command_req(void * p_context, nrf_dfu_req_t * p_req, nrf_dfu_res_t * p_res) +{ + nrf_dfu_res_code_t ret_val = NRF_DFU_RES_CODE_SUCCESS; + + switch (p_req->req_type) + { + case NRF_DFU_OBJECT_OP_CREATE: + NRF_LOG_INFO("Before OP create command\r\n"); + if(p_req->object_size == 0) + { + return NRF_DFU_RES_CODE_INVALID_PARAMETER; + } + + if (p_req->object_size > INIT_COMMAND_MAX_SIZE) + { + // It is impossible to handle the command because the size is too large + return NRF_DFU_RES_CODE_INSUFFICIENT_RESOURCES; + } + + NRF_LOG_INFO("Valid Command Create\r\n"); + + // Setting DFU to uninitialized. + m_valid_init_packet_present = false; + + // Reset all progress to zero. + memset(&s_dfu_settings.progress, 0, sizeof(dfu_progress_t)); + s_dfu_settings.write_offset = 0; + + // Set the init command size. + s_dfu_settings.progress.command_size = p_req->object_size; + break; + + case NRF_DFU_OBJECT_OP_CRC: + NRF_LOG_INFO("Valid Command CRC\r\n"); + p_res->offset = s_dfu_settings.progress.command_offset; + p_res->crc = s_dfu_settings.progress.command_crc; + break; + + case NRF_DFU_OBJECT_OP_WRITE: + NRF_LOG_INFO("Before OP write command\r\n"); + + if ((p_req->req_len + s_dfu_settings.progress.command_offset) > s_dfu_settings.progress.command_size) + + { + // Too large for the command that was requested + p_res->offset = s_dfu_settings.progress.command_offset; + p_res->crc = s_dfu_settings.progress.command_crc; + NRF_LOG_INFO("Error. Init command larger than expected. \r\n"); + return NRF_DFU_RES_CODE_INVALID_PARAMETER; + } + + // Copy the received data to RAM, updating offset and calculating CRC. + memcpy(&s_dfu_settings.init_command[s_dfu_settings.progress.command_offset], p_req->p_req, p_req->req_len); + s_dfu_settings.progress.command_offset += p_req->req_len; + s_dfu_settings.progress.command_crc = crc32_compute(p_req->p_req, p_req->req_len, &s_dfu_settings.progress.command_crc); + + // Set output values. + p_res->offset = s_dfu_settings.progress.command_offset; + p_res->crc = s_dfu_settings.progress.command_crc; + + break; + + case NRF_DFU_OBJECT_OP_EXECUTE: + NRF_LOG_INFO("Before OP execute command\r\n"); + if (s_dfu_settings.progress.command_offset != s_dfu_settings.progress.command_size) + { + // The object wasn't the right (requested) size + NRF_LOG_INFO("Execute with faulty offset\r\n"); + return NRF_DFU_RES_CODE_OPERATION_NOT_PERMITTED; + } + + NRF_LOG_INFO("Valid command execute\r\n"); + + if (m_valid_init_packet_present) + { + // Init command already executed + return NRF_DFU_RES_CODE_SUCCESS; + } + + NRF_LOG_HEXDUMP_INFO(&s_dfu_settings.init_command[0], s_dfu_settings.progress.command_size); + + NRF_LOG_INFO("\r\n"); + + if (dfu_decode_commmand() != true) + { + return NRF_DFU_RES_CODE_INVALID_OBJECT; + } + + // We have a valid DFU packet + if (packet.has_signed_command) + { + NRF_LOG_INFO("Handling signed command\r\n"); + ret_val = dfu_handle_signed_command(&packet.signed_command, &stream); + } + else if (packet.has_command) + { + NRF_LOG_INFO("Handling unsigned command\r\n"); + ret_val = dfu_handle_command(&packet.command); + } + else + { + // We had no regular or signed command. + NRF_LOG_INFO("Decoded command but it has no content!!\r\n"); + return NRF_DFU_RES_CODE_INVALID_OBJECT; + } + + if (ret_val == NRF_DFU_RES_CODE_SUCCESS) + { + // Setting DFU to initialized + NRF_LOG_INFO("Setting DFU flag to initialized\r\n"); + m_valid_init_packet_present = true; + } + break; + + case NRF_DFU_OBJECT_OP_SELECT: + NRF_LOG_INFO("Valid Command: NRF_DFU_OBJECT_OP_SELECT\r\n"); + p_res->crc = s_dfu_settings.progress.command_crc; + p_res->offset = s_dfu_settings.progress.command_offset; + p_res->max_size = INIT_COMMAND_MAX_SIZE; + break; + + default: + NRF_LOG_INFO("Invalid Command Operation\r\n"); + ret_val = NRF_DFU_RES_CODE_OP_CODE_NOT_SUPPORTED; + break; + } + + return ret_val; +} + + +static nrf_dfu_res_code_t nrf_dfu_data_req(void * p_context, nrf_dfu_req_t * p_req, nrf_dfu_res_t * p_res) +{ + uint32_t const * p_write_addr; + nrf_dfu_res_code_t ret_val = NRF_DFU_RES_CODE_SUCCESS; + +#ifndef NRF51 + if(p_req == NULL) + { + return NRF_DFU_RES_CODE_INVALID_PARAMETER; + } +#endif + + switch (p_req->req_type) + { + case NRF_DFU_OBJECT_OP_CREATE: + NRF_LOG_INFO("Before OP create\r\n"); + + if (p_req->object_size == 0) + { + // Empty object is not possible + //NRF_LOG_INFO("Trying to create data object of size 0\r\n"); + return NRF_DFU_RES_CODE_INVALID_PARAMETER; + } + + if ( (p_req->object_size & (CODE_PAGE_SIZE - 1)) != 0 && + (s_dfu_settings.progress.firmware_image_offset_last + p_req->object_size != m_firmware_size_req) ) + { + NRF_LOG_ERROR("Trying to create an object with a size that is not page aligned\r\n"); + return NRF_DFU_RES_CODE_INVALID_PARAMETER; + } + + if (p_req->object_size > DATA_OBJECT_MAX_SIZE) + { + // It is impossible to handle the command because the size is too large + NRF_LOG_INFO("Invalid size for object (too large)\r\n"); + return NRF_DFU_RES_CODE_INSUFFICIENT_RESOURCES; + } + + if (m_valid_init_packet_present == false) + { + // Can't accept data because DFU isn't initialized by init command. + NRF_LOG_INFO("Trying to create data object without valid init command\r\n"); + return NRF_DFU_RES_CODE_OPERATION_NOT_PERMITTED; + } + + if ((s_dfu_settings.progress.firmware_image_offset_last + p_req->object_size) > m_firmware_size_req) + { + NRF_LOG_INFO("Trying to create an object of size %d, when offset is 0x%08x and firmware size is 0x%08x\r\n", p_req->object_size, s_dfu_settings.progress.firmware_image_offset_last, m_firmware_size_req); + return NRF_DFU_RES_CODE_OPERATION_NOT_PERMITTED; + } + + NRF_LOG_INFO("Valid Data Create\r\n"); + + s_dfu_settings.progress.firmware_image_crc = s_dfu_settings.progress.firmware_image_crc_last; + s_dfu_settings.progress.data_object_size = p_req->object_size; + s_dfu_settings.progress.firmware_image_offset = s_dfu_settings.progress.firmware_image_offset_last; + s_dfu_settings.write_offset = s_dfu_settings.progress.firmware_image_offset_last; + + FLASH_BUFFER_SWAP(); + + // Erase the page we're at. + m_flash_operations_pending++; + if (nrf_dfu_flash_erase((uint32_t*)(m_firmware_start_addr + s_dfu_settings.progress.firmware_image_offset), CEIL_DIV(p_req->object_size, CODE_PAGE_SIZE), dfu_data_write_handler) != FS_SUCCESS) + { + m_flash_operations_pending--; + NRF_LOG_INFO("Erase operation failed\r\n"); + return NRF_DFU_RES_CODE_INVALID_OBJECT; + } + + NRF_LOG_INFO("Creating object with size: %d. Offset: 0x%08x, CRC: 0x%08x\r\n", s_dfu_settings.progress.data_object_size, s_dfu_settings.progress.firmware_image_offset, s_dfu_settings.progress.firmware_image_crc); + + break; + + case NRF_DFU_OBJECT_OP_WRITE: + + // Setting to ensure we are not sending faulty information in case of an early return. + p_res->offset = s_dfu_settings.progress.firmware_image_offset; + p_res->crc = s_dfu_settings.progress.firmware_image_crc; + + if (m_valid_init_packet_present == false) + { + // Can't accept data because DFU isn't initialized by init command. + return NRF_DFU_RES_CODE_OPERATION_NOT_PERMITTED; + } + if (p_req->req_len > FLASH_BUFFER_CHUNK_LENGTH) + { + return NRF_DFU_RES_CODE_INSUFFICIENT_RESOURCES; + } + + if ((p_req->req_len + s_dfu_settings.progress.firmware_image_offset - s_dfu_settings.progress.firmware_image_offset_last) > s_dfu_settings.progress.data_object_size) + { + // Can't accept data because too much data has been received. + NRF_LOG_INFO("Write request too long\r\n"); + return NRF_DFU_RES_CODE_INVALID_PARAMETER; + } + + // Update the CRC of the firmware image. + s_dfu_settings.progress.firmware_image_crc = crc32_compute(p_req->p_req, p_req->req_len, &s_dfu_settings.progress.firmware_image_crc); + s_dfu_settings.progress.firmware_image_offset += p_req->req_len; + + // Update the return values + p_res->offset = s_dfu_settings.progress.firmware_image_offset; + p_res->crc = s_dfu_settings.progress.firmware_image_crc; + + if (m_data_buf_pos + p_req->req_len < FLASH_BUFFER_CHUNK_LENGTH) + { + //If there is enough space in the current buffer, store the received data. + memcpy(&m_data_buf[m_current_data_buffer][m_data_buf_pos], + p_req->p_req, p_req->req_len); + m_data_buf_pos += p_req->req_len; + } + else + { + // If there is not enough space in the current buffer, utilize what is left in the buffer, write it to flash and start using a new buffer. + + // Fill the remaining part of the current buffer + uint16_t first_segment_length = FLASH_BUFFER_CHUNK_LENGTH - m_data_buf_pos; + memcpy(&m_data_buf[m_current_data_buffer][m_data_buf_pos], + p_req->p_req, + first_segment_length); + + m_data_buf_pos += first_segment_length; + + // Keep only the remaining part which should be put in the next buffer. + p_req->req_len -= first_segment_length; + p_req->p_req += first_segment_length; + + // Write to flash. + p_write_addr = (uint32_t const *)(m_firmware_start_addr + s_dfu_settings.write_offset); + ++m_flash_operations_pending; + if (nrf_dfu_flash_store(p_write_addr, (uint32_t*)&m_data_buf[m_current_data_buffer][0], CEIL_DIV(m_data_buf_pos,4), dfu_data_write_handler) == FS_SUCCESS) + { + NRF_LOG_INFO("Storing %d B at: 0x%08x\r\n", m_data_buf_pos, (uint32_t)p_write_addr); + // Pre-calculate Offset + CRC assuming flash operation went OK + s_dfu_settings.write_offset += m_data_buf_pos; + } + else + { + --m_flash_operations_pending; + NRF_LOG_INFO("!!! Failed storing %d B at address: 0x%08x\r\n", m_data_buf_pos, (uint32_t)p_write_addr); + // Previous flash operation failed. Revert CRC and offset. + s_dfu_settings.progress.firmware_image_crc = s_dfu_settings.progress.firmware_image_crc_last; + s_dfu_settings.progress.firmware_image_offset = s_dfu_settings.progress.firmware_image_offset_last; + + // Update the return values + p_res->offset = s_dfu_settings.progress.firmware_image_offset_last; + p_res->crc = s_dfu_settings.progress.firmware_image_crc_last; + } + + FLASH_BUFFER_SWAP(); + + //Copy the remaining segment of the request into the next buffer. + if (p_req->req_len) + { + memcpy(&m_data_buf[m_current_data_buffer][m_data_buf_pos], + p_req->p_req, p_req->req_len); + m_data_buf_pos += p_req->req_len; + } + } + + if ((m_data_buf_pos) && + ( s_dfu_settings.write_offset - + s_dfu_settings.progress.firmware_image_offset_last + + m_data_buf_pos >= + s_dfu_settings.progress.data_object_size) + ) + { + //End of an object and there is still data in the write buffer. Flush the write buffer. + p_write_addr = (uint32_t const *)(m_firmware_start_addr + s_dfu_settings.write_offset); + ++m_flash_operations_pending; + if (nrf_dfu_flash_store(p_write_addr, (uint32_t*)&m_data_buf[m_current_data_buffer][0], CEIL_DIV(m_data_buf_pos,4), dfu_data_write_handler) == FS_SUCCESS) + { + NRF_LOG_INFO("Storing %d B at: 0x%08x\r\n", m_data_buf_pos, (uint32_t)p_write_addr); + s_dfu_settings.write_offset += m_data_buf_pos; + } + else + { + --m_flash_operations_pending; + NRF_LOG_INFO("!!! Failed storing %d B at address: 0x%08x\r\n", m_data_buf_pos, (uint32_t)p_write_addr); + // Previous flash operation failed. Revert CRC and offset. + s_dfu_settings.progress.firmware_image_crc = s_dfu_settings.progress.firmware_image_crc_last; + s_dfu_settings.progress.firmware_image_offset = s_dfu_settings.progress.firmware_image_offset_last; + + // Update the return values + p_res->offset = s_dfu_settings.progress.firmware_image_offset_last; + p_res->crc = s_dfu_settings.progress.firmware_image_crc_last; + } + + // Swap buffers. + FLASH_BUFFER_SWAP(); + } + + break; + + case NRF_DFU_OBJECT_OP_CRC: + NRF_LOG_INFO("Before OP crc\r\n"); + p_res->offset = s_dfu_settings.progress.firmware_image_offset; + p_res->crc = s_dfu_settings.progress.firmware_image_crc; + break; + + case NRF_DFU_OBJECT_OP_EXECUTE: + NRF_LOG_INFO("Before OP execute\r\n"); + if (s_dfu_settings.progress.data_object_size != + s_dfu_settings.progress.firmware_image_offset - + s_dfu_settings.progress.firmware_image_offset_last) + { + // The size of the written object was not as expected. + NRF_LOG_INFO("Invalid data here: exp: %d, got: %d\r\n", s_dfu_settings.progress.data_object_size, s_dfu_settings.progress.firmware_image_offset - s_dfu_settings.progress.firmware_image_offset_last); + return NRF_DFU_RES_CODE_OPERATION_NOT_PERMITTED; + } + + NRF_LOG_INFO("Valid Data Execute\r\n"); + + // Update the offset and crc values for the last object written. + s_dfu_settings.progress.data_object_size = 0; + s_dfu_settings.progress.firmware_image_offset_last = s_dfu_settings.progress.firmware_image_offset; + s_dfu_settings.progress.firmware_image_crc_last = s_dfu_settings.progress.firmware_image_crc; + if (nrf_dfu_settings_write(NULL) != NRF_SUCCESS) + { + return NRF_DFU_RES_CODE_OPERATION_FAILED; + } + + if (s_dfu_settings.progress.firmware_image_offset == m_firmware_size_req) + { + NRF_LOG_INFO("Waiting for %d pending flash operations before doing postvalidate.\r\n", m_flash_operations_pending); + while(m_flash_operations_pending) + { + nrf_dfu_wait(); + } + // Received the whole image. Doing postvalidate. + NRF_LOG_INFO("Doing postvalidate\r\n"); + ret_val = nrf_dfu_postvalidate(&packet.signed_command.command.init); + } + break; + + case NRF_DFU_OBJECT_OP_SELECT: + NRF_LOG_INFO("Valid Data Read info\r\n"); + p_res->crc = s_dfu_settings.progress.firmware_image_crc; + p_res->offset = s_dfu_settings.progress.firmware_image_offset; + p_res->max_size = DATA_OBJECT_MAX_SIZE; + break; + + default: + NRF_LOG_INFO("Invalid Data Operation\r\n"); + ret_val = NRF_DFU_RES_CODE_OP_CODE_NOT_SUPPORTED; + break; + } + + return ret_val; +} + + +uint32_t nrf_dfu_req_handler_init(void) +{ +#ifdef SOFTDEVICE_PRESENT + uint32_t ret_val = nrf_dfu_flash_init(true); +#else + uint32_t ret_val = nrf_dfu_flash_init(false); +#endif + + VERIFY_SUCCESS(ret_val); + + m_flash_operations_pending = 0; + + // If the command is stored to flash, init command was valid. + if (s_dfu_settings.progress.command_size != 0 && dfu_decode_commmand()) + { + // Get the previously stored firmware size + if (s_dfu_settings.bank_0.bank_code == NRF_DFU_BANK_INVALID && s_dfu_settings.bank_0.image_size != 0) + { + m_firmware_size_req = s_dfu_settings.bank_0.image_size; + } + else if (s_dfu_settings.bank_1.bank_code == NRF_DFU_BANK_INVALID && s_dfu_settings.bank_0.image_size != 0) + { + m_firmware_size_req = s_dfu_settings.bank_1.image_size; + } + else + { + return NRF_SUCCESS; + } + + // Location should still be valid, expecting result of find-cache to be true + (void)nrf_dfu_find_cache(m_firmware_size_req, false, &m_firmware_start_addr); + + // Setting valid init command to true to + m_valid_init_packet_present = true; + } + + return NRF_SUCCESS; +} + + +nrf_dfu_res_code_t nrf_dfu_req_handler_on_req(void * p_context, nrf_dfu_req_t * p_req, nrf_dfu_res_t * p_res) +{ + nrf_dfu_res_code_t ret_val; + + static nrf_dfu_obj_type_t cur_obj_type = NRF_DFU_OBJ_TYPE_COMMAND; + switch (p_req->req_type) + { + case NRF_DFU_OBJECT_OP_CREATE: + case NRF_DFU_OBJECT_OP_SELECT: + if ((nrf_dfu_obj_type_t)p_req->obj_type == NRF_DFU_OBJ_TYPE_COMMAND) + { + cur_obj_type = NRF_DFU_OBJ_TYPE_COMMAND; + } + else if ((nrf_dfu_obj_type_t)p_req->obj_type == NRF_DFU_OBJ_TYPE_DATA) + { + cur_obj_type = NRF_DFU_OBJ_TYPE_DATA; + } + else + { + return NRF_DFU_RES_CODE_UNSUPPORTED_TYPE; + } + break; + default: + // no implementation + break; + } + + switch (cur_obj_type) + { + case NRF_DFU_OBJ_TYPE_COMMAND: + ret_val = nrf_dfu_command_req(p_context, p_req, p_res); + break; + + case NRF_DFU_OBJ_TYPE_DATA: + ret_val = nrf_dfu_data_req(p_context, p_req, p_res); + break; + + default: + NRF_LOG_INFO("Invalid request type\r\n"); + ret_val = NRF_DFU_RES_CODE_INVALID_OBJECT; + break; + } + + return ret_val; +} + diff --git a/SDK/12.3.0_d7731ad/dfu/dfu_req_handling.h b/SDK/12.3.0_d7731ad/dfu/dfu_req_handling.h new file mode 100644 index 0000000..dc1f0e9 --- /dev/null +++ b/SDK/12.3.0_d7731ad/dfu/dfu_req_handling.h @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2016 - 2017, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef DFU_REQ_HANDLING_H__ +#define DFU_REQ_HANDLING_H__ + +#include "compiler_abstraction.h" + +__ALIGN(4) extern const uint8_t pk[64]; + + +#endif // #ifndef DFU_REQ_HANDLING_H__ diff --git a/SDK/12.3.0_d7731ad/dfu/main.c b/SDK/12.3.0_d7731ad/dfu/main.c new file mode 100644 index 0000000..3170389 --- /dev/null +++ b/SDK/12.3.0_d7731ad/dfu/main.c @@ -0,0 +1,122 @@ +/** + * Copyright (c) 2016 - 2017, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/** @file + * + * @defgroup bootloader_secure main.c + * @{ + * @ingroup dfu_bootloader_api + * @brief Bootloader project main file for secure DFU. + * + */ + +#include +#include "boards.h" +#include "nrf_mbr.h" +#include "nrf_bootloader.h" +#include "nrf_bootloader_app_start.h" +#include "nrf_dfu.h" +#include "nrf_log.h" +#include "nrf_log_ctrl.h" +#include "app_error.h" +#include "app_error_weak.h" +#include "nrf_bootloader_info.h" + +void app_error_fault_handler(uint32_t id, uint32_t pc, uint32_t info) +{ + NRF_LOG_ERROR("received a fault! id: 0x%08x, pc: 0x&08x\r\n", id, pc); + NVIC_SystemReset(); +} + +void app_error_handler_bare(uint32_t error_code) +{ + (void)error_code; + NRF_LOG_ERROR("received an error: 0x%08x!\r\n", error_code); + NVIC_SystemReset(); +} + + +/**@brief Function for initialization of LEDs. + */ +static void leds_init(void) +{ + bsp_board_leds_init(); + bsp_board_led_on(BSP_BOARD_LED_2); +} + + +/**@brief Function for initializing the button module. + */ +static void buttons_init(void) +{ + nrf_gpio_cfg_sense_input(BOOTLOADER_BUTTON, + BUTTON_PULL, + NRF_GPIO_PIN_SENSE_LOW); +} + + +/**@brief Function for application main entry. + */ +int main(void) +{ + uint32_t ret_val; + + (void) NRF_LOG_INIT(NULL); + + NRF_LOG_INFO("Inside main\r\n"); + + leds_init(); + buttons_init(); + + ret_val = nrf_bootloader_init(); + APP_ERROR_CHECK(ret_val); + + // Either there was no DFU functionality enabled in this project or the DFU module detected + // no ongoing DFU operation and found a valid main application. + // Boot the main application. + nrf_bootloader_app_start(MAIN_APPLICATION_START_ADDR); + + // Should never be reached. + NRF_LOG_INFO("After main\r\n"); +} + +/** + * @} + */ diff --git a/SDK/12.3.0_d7731ad/dfu/sdk_config.h b/SDK/12.3.0_d7731ad/dfu/sdk_config.h new file mode 100644 index 0000000..8d00e45 --- /dev/null +++ b/SDK/12.3.0_d7731ad/dfu/sdk_config.h @@ -0,0 +1,509 @@ + + +#ifndef SDK_CONFIG_H +#define SDK_CONFIG_H +// <<< Use Configuration Wizard in Context Menu >>>\n +#ifdef USE_APP_CONFIG +#include "app_config.h" +#endif +// nRF_Drivers + +//========================================================== +// CLOCK_ENABLED - nrf_drv_clock - CLOCK peripheral driver +//========================================================== +#ifndef CLOCK_ENABLED +#define CLOCK_ENABLED 0 +#endif +#if CLOCK_ENABLED +// CLOCK_CONFIG_XTAL_FREQ - HF XTAL Frequency + +// <0=> Default (64 MHz) +// <255=> Default (16 MHz) +// <0=> 32 MHz + +#ifndef CLOCK_CONFIG_XTAL_FREQ +#define CLOCK_CONFIG_XTAL_FREQ 255 +#endif + +// CLOCK_CONFIG_LF_SRC - LF Clock Source + +// <0=> RC +// <1=> XTAL +// <2=> Synth + +#ifndef CLOCK_CONFIG_LF_SRC +#define CLOCK_CONFIG_LF_SRC 1 +#endif + +// CLOCK_CONFIG_IRQ_PRIORITY - Interrupt priority + + +// Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice +// <0=> 0 (highest) +// <1=> 1 +// <2=> 2 +// <3=> 3 + +#ifndef CLOCK_CONFIG_IRQ_PRIORITY +#define CLOCK_CONFIG_IRQ_PRIORITY 3 +#endif + +// CLOCK_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef CLOCK_CONFIG_LOG_ENABLED +#define CLOCK_CONFIG_LOG_ENABLED 0 +#endif +#if CLOCK_CONFIG_LOG_ENABLED +// CLOCK_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef CLOCK_CONFIG_LOG_LEVEL +#define CLOCK_CONFIG_LOG_LEVEL 3 +#endif + +// CLOCK_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef CLOCK_CONFIG_INFO_COLOR +#define CLOCK_CONFIG_INFO_COLOR 0 +#endif + +// CLOCK_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef CLOCK_CONFIG_DEBUG_COLOR +#define CLOCK_CONFIG_DEBUG_COLOR 0 +#endif + +#endif //CLOCK_CONFIG_LOG_ENABLED +// + +#endif //CLOCK_ENABLED +// + +// PERIPHERAL_RESOURCE_SHARING_ENABLED - nrf_drv_common - Peripheral drivers common module +//========================================================== +#ifndef PERIPHERAL_RESOURCE_SHARING_ENABLED +#define PERIPHERAL_RESOURCE_SHARING_ENABLED 0 +#endif +#if PERIPHERAL_RESOURCE_SHARING_ENABLED +// COMMON_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef COMMON_CONFIG_LOG_ENABLED +#define COMMON_CONFIG_LOG_ENABLED 0 +#endif +#if COMMON_CONFIG_LOG_ENABLED +// COMMON_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef COMMON_CONFIG_LOG_LEVEL +#define COMMON_CONFIG_LOG_LEVEL 3 +#endif + +// COMMON_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef COMMON_CONFIG_INFO_COLOR +#define COMMON_CONFIG_INFO_COLOR 0 +#endif + +// COMMON_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef COMMON_CONFIG_DEBUG_COLOR +#define COMMON_CONFIG_DEBUG_COLOR 0 +#endif + +#endif //COMMON_CONFIG_LOG_ENABLED +// + +#endif //PERIPHERAL_RESOURCE_SHARING_ENABLED +// + +// RNG_ENABLED - nrf_drv_rng - RNG peripheral driver +//========================================================== +#ifndef RNG_ENABLED +#define RNG_ENABLED 1 +#endif +#if RNG_ENABLED +// RNG_CONFIG_ERROR_CORRECTION - Error correction + + +#ifndef RNG_CONFIG_ERROR_CORRECTION +#define RNG_CONFIG_ERROR_CORRECTION 0 +#endif + +// RNG_CONFIG_POOL_SIZE - Pool size +#ifndef RNG_CONFIG_POOL_SIZE +#define RNG_CONFIG_POOL_SIZE 32 +#endif + +// RNG_CONFIG_IRQ_PRIORITY - Interrupt priority + + +// Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice +// <0=> 0 (highest) +// <1=> 1 +// <2=> 2 +// <3=> 3 + +#ifndef RNG_CONFIG_IRQ_PRIORITY +#define RNG_CONFIG_IRQ_PRIORITY 3 +#endif + +// RNG_CONFIG_LOG_ENABLED - Enables logging in the module. +//========================================================== +#ifndef RNG_CONFIG_LOG_ENABLED +#define RNG_CONFIG_LOG_ENABLED 0 +#endif +#if RNG_CONFIG_LOG_ENABLED +// RNG_CONFIG_LOG_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef RNG_CONFIG_LOG_LEVEL +#define RNG_CONFIG_LOG_LEVEL 3 +#endif + +// RNG_CONFIG_INFO_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef RNG_CONFIG_INFO_COLOR +#define RNG_CONFIG_INFO_COLOR 0 +#endif + +// RNG_CONFIG_DEBUG_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef RNG_CONFIG_DEBUG_COLOR +#define RNG_CONFIG_DEBUG_COLOR 0 +#endif + +#endif //RNG_CONFIG_LOG_ENABLED +// + +#endif //RNG_ENABLED +// + +// +//========================================================== + +// nRF_Libraries + +//========================================================== +// APP_SCHEDULER_ENABLED - app_scheduler - Events scheduler +//========================================================== +#ifndef APP_SCHEDULER_ENABLED +#define APP_SCHEDULER_ENABLED 1 +#endif +#if APP_SCHEDULER_ENABLED +// APP_SCHEDULER_WITH_PAUSE - Enabling pause feature + + +#ifndef APP_SCHEDULER_WITH_PAUSE +#define APP_SCHEDULER_WITH_PAUSE 0 +#endif + +// APP_SCHEDULER_WITH_PROFILER - Enabling scheduler profiling + + +#ifndef APP_SCHEDULER_WITH_PROFILER +#define APP_SCHEDULER_WITH_PROFILER 0 +#endif + +#endif //APP_SCHEDULER_ENABLED +// + +// APP_TIMER_ENABLED - app_timer - Application timer functionality +//========================================================== +#ifndef APP_TIMER_ENABLED +#define APP_TIMER_ENABLED 1 +#endif +#if APP_TIMER_ENABLED +// APP_TIMER_WITH_PROFILER - Enable app_timer profiling + + +#ifndef APP_TIMER_WITH_PROFILER +#define APP_TIMER_WITH_PROFILER 0 +#endif + +// APP_TIMER_KEEPS_RTC_ACTIVE - Enable RTC always on + + +// If option is enabled RTC is kept running even if there is no active timers. +// This option can be used when app_timer is used for timestamping. + +#ifndef APP_TIMER_KEEPS_RTC_ACTIVE +#define APP_TIMER_KEEPS_RTC_ACTIVE 0 +#endif + +#endif //APP_TIMER_ENABLED +// + +// CRC32_ENABLED - crc32 - CRC32 calculation routines + + +#ifndef CRC32_ENABLED +#define CRC32_ENABLED 1 +#endif + +// ECC_ENABLED - ecc - Elliptic Curve Cryptography Library + + +#ifndef ECC_ENABLED +#define ECC_ENABLED 0 +#endif + +// FSTORAGE_ENABLED - fstorage - Flash storage module +//========================================================== +#ifndef FSTORAGE_ENABLED +#define FSTORAGE_ENABLED 1 +#endif +#if FSTORAGE_ENABLED +// FS_QUEUE_SIZE - Configures the size of the internal queue. +// Increase this if there are many users, or if it is likely that many +// operation will be queued at once without waiting for the previous operations +// to complete. In general, increase the queue size if you frequently receive +// @ref FS_ERR_QUEUE_FULL errors when calling @ref fs_store or @ref fs_erase. + +#ifndef FS_QUEUE_SIZE +#define FS_QUEUE_SIZE 4 +#endif + +// FS_OP_MAX_RETRIES - Number attempts to execute an operation if the SoftDevice fails. +// Increase this value if events return the @ref FS_ERR_OPERATION_TIMEOUT +// error often. The SoftDevice may fail to schedule flash access due to high BLE activity. + +#ifndef FS_OP_MAX_RETRIES +#define FS_OP_MAX_RETRIES 3 +#endif + +// FS_MAX_WRITE_SIZE_WORDS - Maximum number of words to be written to flash in a single operation. +// Tweaking this value can increase the chances of the SoftDevice being +// able to fit flash operations in between radio activity. This value is bound by the +// maximum number of words which the SoftDevice can write to flash in a single call to +// @ref sd_flash_write, which is 256 words for nRF51 ICs and 1024 words for nRF52 ICs. + +#ifndef FS_MAX_WRITE_SIZE_WORDS +#define FS_MAX_WRITE_SIZE_WORDS 256 +#endif + +#endif //FSTORAGE_ENABLED +// + +// HCI_MEM_POOL_ENABLED - hci_mem_pool - memory pool implementation used by HCI +//========================================================== +#ifndef HCI_MEM_POOL_ENABLED +#define HCI_MEM_POOL_ENABLED 1 +#endif +#if HCI_MEM_POOL_ENABLED +// HCI_TX_BUF_SIZE - TX buffer size in bytes. +#ifndef HCI_TX_BUF_SIZE +#define HCI_TX_BUF_SIZE 600 +#endif + +// HCI_RX_BUF_SIZE - RX buffer size in bytes. +#ifndef HCI_RX_BUF_SIZE +#define HCI_RX_BUF_SIZE 600 +#endif + +// HCI_RX_BUF_QUEUE_SIZE - RX buffer queue size. +#ifndef HCI_RX_BUF_QUEUE_SIZE +#define HCI_RX_BUF_QUEUE_SIZE 4 +#endif + +#endif //HCI_MEM_POOL_ENABLED +// + +// NRF_QUEUE_ENABLED - nrf_queue - Queue module + + +#ifndef NRF_QUEUE_ENABLED +#define NRF_QUEUE_ENABLED 1 +#endif + +// +//========================================================== + +// nRF_Log + +//========================================================== +// NRF_LOG_ENABLED - nrf_log - Logging +//========================================================== +#ifndef NRF_LOG_ENABLED +#define NRF_LOG_ENABLED 0 +#endif +#if NRF_LOG_ENABLED +// NRF_LOG_USES_COLORS - If enabled then ANSI escape code for colors is prefixed to every string +//========================================================== +#ifndef NRF_LOG_USES_COLORS +#define NRF_LOG_USES_COLORS 0 +#endif +#if NRF_LOG_USES_COLORS +// NRF_LOG_COLOR_DEFAULT - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_LOG_COLOR_DEFAULT +#define NRF_LOG_COLOR_DEFAULT 0 +#endif + +// NRF_LOG_ERROR_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_LOG_ERROR_COLOR +#define NRF_LOG_ERROR_COLOR 0 +#endif + +// NRF_LOG_WARNING_COLOR - ANSI escape code prefix. + +// <0=> Default +// <1=> Black +// <2=> Red +// <3=> Green +// <4=> Yellow +// <5=> Blue +// <6=> Magenta +// <7=> Cyan +// <8=> White + +#ifndef NRF_LOG_WARNING_COLOR +#define NRF_LOG_WARNING_COLOR 0 +#endif + +#endif //NRF_LOG_USES_COLORS +// + +// NRF_LOG_DEFAULT_LEVEL - Default Severity level + +// <0=> Off +// <1=> Error +// <2=> Warning +// <3=> Info +// <4=> Debug + +#ifndef NRF_LOG_DEFAULT_LEVEL +#define NRF_LOG_DEFAULT_LEVEL 3 +#endif + +// NRF_LOG_DEFERRED - Enable deffered logger. + +// Log data is buffered and can be processed in idle. +//========================================================== +#ifndef NRF_LOG_DEFERRED +#define NRF_LOG_DEFERRED 1 +#endif +#if NRF_LOG_DEFERRED +// NRF_LOG_DEFERRED_BUFSIZE - Size of the buffer for logs in words. +// Must be power of 2 + +#ifndef NRF_LOG_DEFERRED_BUFSIZE +#define NRF_LOG_DEFERRED_BUFSIZE 256 +#endif + +#endif //NRF_LOG_DEFERRED +// + +// NRF_LOG_USES_TIMESTAMP - Enable timestamping + + +// Function for getting the timestamp is provided by the user + +#ifndef NRF_LOG_USES_TIMESTAMP +#define NRF_LOG_USES_TIMESTAMP 0 +#endif + +#endif //NRF_LOG_ENABLED +// + +// +//========================================================== + +// <<< end of configuration section >>> +#endif //SDK_CONFIG_H + diff --git a/SDK/12.3.0_d7731ad/external/micro-ecc/license.txt b/SDK/12.3.0_d7731ad/external/micro-ecc/license.txt new file mode 100644 index 0000000..b61b81e --- /dev/null +++ b/SDK/12.3.0_d7731ad/external/micro-ecc/license.txt @@ -0,0 +1,21 @@ +Copyright (c) 2014, Kenneth MacKay +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/SDK/12.3.0_d7731ad/external/micro-ecc/micro-ecc/LICENSE.txt b/SDK/12.3.0_d7731ad/external/micro-ecc/micro-ecc/LICENSE.txt new file mode 100644 index 0000000..ab099ae --- /dev/null +++ b/SDK/12.3.0_d7731ad/external/micro-ecc/micro-ecc/LICENSE.txt @@ -0,0 +1,21 @@ +Copyright (c) 2014, Kenneth MacKay +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/SDK/12.3.0_d7731ad/external/micro-ecc/micro-ecc/asm_arm.inc b/SDK/12.3.0_d7731ad/external/micro-ecc/micro-ecc/asm_arm.inc new file mode 100644 index 0000000..12e747f --- /dev/null +++ b/SDK/12.3.0_d7731ad/external/micro-ecc/micro-ecc/asm_arm.inc @@ -0,0 +1,820 @@ +/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ + +#ifndef _UECC_ASM_ARM_H_ +#define _UECC_ASM_ARM_H_ + +#if (uECC_SUPPORTS_secp256r1 || uECC_SUPPORTS_secp256k1) + #define uECC_MIN_WORDS 8 +#endif +#if uECC_SUPPORTS_secp224r1 + #undef uECC_MIN_WORDS + #define uECC_MIN_WORDS 7 +#endif +#if uECC_SUPPORTS_secp192r1 + #undef uECC_MIN_WORDS + #define uECC_MIN_WORDS 6 +#endif +#if uECC_SUPPORTS_secp160r1 + #undef uECC_MIN_WORDS + #define uECC_MIN_WORDS 5 +#endif + +#if (uECC_PLATFORM == uECC_arm_thumb) + #define REG_RW "+&l" + #define REG_WRITE "=&l" +#else + #define REG_RW "+&r" + #define REG_WRITE "=&r" +#endif + +#if (uECC_PLATFORM == uECC_arm_thumb || uECC_PLATFORM == uECC_arm_thumb2) + #define REG_RW_LO "+&l" + #define REG_WRITE_LO "=&l" +#else + #define REG_RW_LO "+&r" + #define REG_WRITE_LO "=&r" +#endif + +#if (uECC_PLATFORM == uECC_arm_thumb2) + #define RESUME_SYNTAX +#else + #define RESUME_SYNTAX ".syntax divided \n\t" +#endif + +#if (uECC_OPTIMIZATION_LEVEL >= 2) + +uECC_VLI_API uECC_word_t uECC_vli_add(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { +#if (uECC_MAX_WORDS != uECC_MIN_WORDS) + #if (uECC_PLATFORM == uECC_arm_thumb) || (uECC_PLATFORM == uECC_arm_thumb2) + uint32_t jump = (uECC_MAX_WORDS - num_words) * 4 * 2 + 1; + #else /* ARM */ + uint32_t jump = (uECC_MAX_WORDS - num_words) * 4 * 4; + #endif +#endif + uint32_t carry; + uint32_t left_word; + uint32_t right_word; + + __asm__ volatile ( + ".syntax unified \n\t" + "movs %[carry], #0 \n\t" + #if (uECC_MAX_WORDS != uECC_MIN_WORDS) + "adr %[left], 1f \n\t" + ".align 4 \n\t" + "adds %[jump], %[left] \n\t" + #endif + + "ldmia %[lptr]!, {%[left]} \n\t" + "ldmia %[rptr]!, {%[right]} \n\t" + "adds %[left], %[right] \n\t" + "stmia %[dptr]!, {%[left]} \n\t" + + #if (uECC_MAX_WORDS != uECC_MIN_WORDS) + "bx %[jump] \n\t" + #endif + "1: \n\t" + REPEAT(DEC(uECC_MAX_WORDS), + "ldmia %[lptr]!, {%[left]} \n\t" + "ldmia %[rptr]!, {%[right]} \n\t" + "adcs %[left], %[right] \n\t" + "stmia %[dptr]!, {%[left]} \n\t") + + "adcs %[carry], %[carry] \n\t" + RESUME_SYNTAX + : [dptr] REG_RW_LO (result), [lptr] REG_RW_LO (left), [rptr] REG_RW_LO (right), + #if (uECC_MAX_WORDS != uECC_MIN_WORDS) + [jump] REG_RW_LO (jump), + #endif + [carry] REG_WRITE_LO (carry), [left] REG_WRITE_LO (left_word), + [right] REG_WRITE_LO (right_word) + : + : "cc", "memory" + ); + return carry; +} +#define asm_add 1 + +uECC_VLI_API uECC_word_t uECC_vli_sub(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { +#if (uECC_MAX_WORDS != uECC_MIN_WORDS) + #if (uECC_PLATFORM == uECC_arm_thumb) || (uECC_PLATFORM == uECC_arm_thumb2) + uint32_t jump = (uECC_MAX_WORDS - num_words) * 4 * 2 + 1; + #else /* ARM */ + uint32_t jump = (uECC_MAX_WORDS - num_words) * 4 * 4; + #endif +#endif + uint32_t carry; + uint32_t left_word; + uint32_t right_word; + + __asm__ volatile ( + ".syntax unified \n\t" + "movs %[carry], #0 \n\t" + #if (uECC_MAX_WORDS != uECC_MIN_WORDS) + "adr %[left], 1f \n\t" + ".align 4 \n\t" + "adds %[jump], %[left] \n\t" + #endif + + "ldmia %[lptr]!, {%[left]} \n\t" + "ldmia %[rptr]!, {%[right]} \n\t" + "subs %[left], %[right] \n\t" + "stmia %[dptr]!, {%[left]} \n\t" + + #if (uECC_MAX_WORDS != uECC_MIN_WORDS) + "bx %[jump] \n\t" + #endif + "1: \n\t" + REPEAT(DEC(uECC_MAX_WORDS), + "ldmia %[lptr]!, {%[left]} \n\t" + "ldmia %[rptr]!, {%[right]} \n\t" + "sbcs %[left], %[right] \n\t" + "stmia %[dptr]!, {%[left]} \n\t") + + "adcs %[carry], %[carry] \n\t" + RESUME_SYNTAX + : [dptr] REG_RW_LO (result), [lptr] REG_RW_LO (left), [rptr] REG_RW_LO (right), + #if (uECC_MAX_WORDS != uECC_MIN_WORDS) + [jump] REG_RW_LO (jump), + #endif + [carry] REG_WRITE_LO (carry), [left] REG_WRITE_LO (left_word), + [right] REG_WRITE_LO (right_word) + : + : "cc", "memory" + ); + return !carry; /* Note that on ARM, carry flag set means "no borrow" when subtracting + (for some reason...) */ +} +#define asm_sub 1 + +#endif /* (uECC_OPTIMIZATION_LEVEL >= 2) */ + +#if (uECC_OPTIMIZATION_LEVEL >= 3) + +#if (uECC_PLATFORM != uECC_arm_thumb) + +#if uECC_ARM_USE_UMAAL + #include "asm_arm_mult_square_umaal.inc" +#else + #include "asm_arm_mult_square.inc" +#endif + +#if (uECC_OPTIMIZATION_LEVEL == 3) + +uECC_VLI_API void uECC_vli_mult(uint32_t *result, + const uint32_t *left, + const uint32_t *right, + wordcount_t num_words) { + register uint32_t *r0 __asm__("r0") = result; + register const uint32_t *r1 __asm__("r1") = left; + register const uint32_t *r2 __asm__("r2") = right; + register uint32_t r3 __asm__("r3") = num_words; + + __asm__ volatile ( + ".syntax unified \n\t" +#if (uECC_MIN_WORDS == 5) + FAST_MULT_ASM_5 + #if (uECC_MAX_WORDS > 5) + FAST_MULT_ASM_5_TO_6 + #endif + #if (uECC_MAX_WORDS > 6) + FAST_MULT_ASM_6_TO_7 + #endif + #if (uECC_MAX_WORDS > 7) + FAST_MULT_ASM_7_TO_8 + #endif +#elif (uECC_MIN_WORDS == 6) + FAST_MULT_ASM_6 + #if (uECC_MAX_WORDS > 6) + FAST_MULT_ASM_6_TO_7 + #endif + #if (uECC_MAX_WORDS > 7) + FAST_MULT_ASM_7_TO_8 + #endif +#elif (uECC_MIN_WORDS == 7) + FAST_MULT_ASM_7 + #if (uECC_MAX_WORDS > 7) + FAST_MULT_ASM_7_TO_8 + #endif +#elif (uECC_MIN_WORDS == 8) + FAST_MULT_ASM_8 +#endif + "1: \n\t" + RESUME_SYNTAX + : "+r" (r0), "+r" (r1), "+r" (r2) + : "r" (r3) + : "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory" + ); +} +#define asm_mult 1 + +#if uECC_SQUARE_FUNC +uECC_VLI_API void uECC_vli_square(uECC_word_t *result, + const uECC_word_t *left, + wordcount_t num_words) { + register uint32_t *r0 __asm__("r0") = result; + register const uint32_t *r1 __asm__("r1") = left; + register uint32_t r2 __asm__("r2") = num_words; + + __asm__ volatile ( + ".syntax unified \n\t" +#if (uECC_MIN_WORDS == 5) + FAST_SQUARE_ASM_5 + #if (uECC_MAX_WORDS > 5) + FAST_SQUARE_ASM_5_TO_6 + #endif + #if (uECC_MAX_WORDS > 6) + FAST_SQUARE_ASM_6_TO_7 + #endif + #if (uECC_MAX_WORDS > 7) + FAST_SQUARE_ASM_7_TO_8 + #endif +#elif (uECC_MIN_WORDS == 6) + FAST_SQUARE_ASM_6 + #if (uECC_MAX_WORDS > 6) + FAST_SQUARE_ASM_6_TO_7 + #endif + #if (uECC_MAX_WORDS > 7) + FAST_SQUARE_ASM_7_TO_8 + #endif +#elif (uECC_MIN_WORDS == 7) + FAST_SQUARE_ASM_7 + #if (uECC_MAX_WORDS > 7) + FAST_SQUARE_ASM_7_TO_8 + #endif +#elif (uECC_MIN_WORDS == 8) + FAST_SQUARE_ASM_8 +#endif + + "1: \n\t" + RESUME_SYNTAX + : "+r" (r0), "+r" (r1) + : "r" (r2) + : "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory" + ); +} +#define asm_square 1 +#endif /* uECC_SQUARE_FUNC */ + +#else /* (uECC_OPTIMIZATION_LEVEL > 3) */ + +uECC_VLI_API void uECC_vli_mult(uint32_t *result, + const uint32_t *left, + const uint32_t *right, + wordcount_t num_words) { + register uint32_t *r0 __asm__("r0") = result; + register const uint32_t *r1 __asm__("r1") = left; + register const uint32_t *r2 __asm__("r2") = right; + register uint32_t r3 __asm__("r3") = num_words; + +#if uECC_SUPPORTS_secp160r1 + if (num_words == 5) { + __asm__ volatile ( + ".syntax unified \n\t" + FAST_MULT_ASM_5 + RESUME_SYNTAX + : "+r" (r0), "+r" (r1), "+r" (r2) + : "r" (r3) + : "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory" + ); + return; + } +#endif +#if uECC_SUPPORTS_secp192r1 + if (num_words == 6) { + __asm__ volatile ( + ".syntax unified \n\t" + FAST_MULT_ASM_6 + RESUME_SYNTAX + : "+r" (r0), "+r" (r1), "+r" (r2) + : "r" (r3) + : "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory" + ); + return; + } +#endif +#if uECC_SUPPORTS_secp224r1 + if (num_words == 7) { + __asm__ volatile ( + ".syntax unified \n\t" + FAST_MULT_ASM_7 + RESUME_SYNTAX + : "+r" (r0), "+r" (r1), "+r" (r2) + : "r" (r3) + : "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory" + ); + return; + } +#endif +#if (uECC_SUPPORTS_secp256r1 || uECC_SUPPORTS_secp256k1) + if (num_words == 8) { + __asm__ volatile ( + ".syntax unified \n\t" + FAST_MULT_ASM_8 + RESUME_SYNTAX + : "+r" (r0), "+r" (r1), "+r" (r2) + : "r" (r3) + : "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory" + ); + return; + } +#endif +} +#define asm_mult 1 + +#if uECC_SQUARE_FUNC +uECC_VLI_API void uECC_vli_square(uECC_word_t *result, + const uECC_word_t *left, + wordcount_t num_words) { + register uint32_t *r0 __asm__("r0") = result; + register const uint32_t *r1 __asm__("r1") = left; + register uint32_t r2 __asm__("r2") = num_words; + +#if uECC_SUPPORTS_secp160r1 + if (num_words == 5) { + __asm__ volatile ( + ".syntax unified \n\t" + FAST_SQUARE_ASM_5 + RESUME_SYNTAX + : "+r" (r0), "+r" (r1) + : "r" (r2) + : "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory" + ); + return; + } +#endif +#if uECC_SUPPORTS_secp192r1 + if (num_words == 6) { + __asm__ volatile ( + ".syntax unified \n\t" + FAST_SQUARE_ASM_6 + RESUME_SYNTAX + : "+r" (r0), "+r" (r1) + : "r" (r2) + : "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory" + ); + return; + } +#endif +#if uECC_SUPPORTS_secp224r1 + if (num_words == 7) { + __asm__ volatile ( + ".syntax unified \n\t" + FAST_SQUARE_ASM_7 + RESUME_SYNTAX + : "+r" (r0), "+r" (r1) + : "r" (r2) + : "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory" + ); + return; + } +#endif +#if (uECC_SUPPORTS_secp256r1 || uECC_SUPPORTS_secp256k1) + if (num_words == 8) { + __asm__ volatile ( + ".syntax unified \n\t" + FAST_SQUARE_ASM_8 + RESUME_SYNTAX + : "+r" (r0), "+r" (r1) + : "r" (r2) + : "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory" + ); + return; + } +#endif +} +#define asm_square 1 +#endif /* uECC_SQUARE_FUNC */ + +#endif /* (uECC_OPTIMIZATION_LEVEL > 3) */ + +#endif /* uECC_PLATFORM != uECC_arm_thumb */ + +#endif /* (uECC_OPTIMIZATION_LEVEL >= 3) */ + +/* ---- "Small" implementations ---- */ + +#if !asm_add +uECC_VLI_API uECC_word_t uECC_vli_add(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { + uint32_t carry = 0; + uint32_t left_word; + uint32_t right_word; + + __asm__ volatile ( + ".syntax unified \n\t" + "1: \n\t" + "ldmia %[lptr]!, {%[left]} \n\t" /* Load left word. */ + "ldmia %[rptr]!, {%[right]} \n\t" /* Load right word. */ + "lsrs %[carry], #1 \n\t" /* Set up carry flag (carry = 0 after this). */ + "adcs %[left], %[left], %[right] \n\t" /* Add with carry. */ + "adcs %[carry], %[carry], %[carry] \n\t" /* Store carry bit. */ + "stmia %[dptr]!, {%[left]} \n\t" /* Store result word. */ + "subs %[ctr], #1 \n\t" /* Decrement counter. */ + "bne 1b \n\t" /* Loop until counter == 0. */ + RESUME_SYNTAX + : [dptr] REG_RW (result), [lptr] REG_RW (left), [rptr] REG_RW (right), + [ctr] REG_RW (num_words), [carry] REG_RW (carry), + [left] REG_WRITE (left_word), [right] REG_WRITE (right_word) + : + : "cc", "memory" + ); + return carry; +} +#define asm_add 1 +#endif + +#if !asm_sub +uECC_VLI_API uECC_word_t uECC_vli_sub(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { + uint32_t carry = 1; /* carry = 1 initially (means don't borrow) */ + uint32_t left_word; + uint32_t right_word; + + __asm__ volatile ( + ".syntax unified \n\t" + "1: \n\t" + "ldmia %[lptr]!, {%[left]} \n\t" /* Load left word. */ + "ldmia %[rptr]!, {%[right]} \n\t" /* Load right word. */ + "lsrs %[carry], #1 \n\t" /* Set up carry flag (carry = 0 after this). */ + "sbcs %[left], %[left], %[right] \n\t" /* Subtract with borrow. */ + "adcs %[carry], %[carry], %[carry] \n\t" /* Store carry bit. */ + "stmia %[dptr]!, {%[left]} \n\t" /* Store result word. */ + "subs %[ctr], #1 \n\t" /* Decrement counter. */ + "bne 1b \n\t" /* Loop until counter == 0. */ + RESUME_SYNTAX + : [dptr] REG_RW (result), [lptr] REG_RW (left), [rptr] REG_RW (right), + [ctr] REG_RW (num_words), [carry] REG_RW (carry), + [left] REG_WRITE (left_word), [right] REG_WRITE (right_word) + : + : "cc", "memory" + ); + return !carry; +} +#define asm_sub 1 +#endif + +#if !asm_mult +uECC_VLI_API void uECC_vli_mult(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { +#if (uECC_PLATFORM != uECC_arm_thumb) + uint32_t c0 = 0; + uint32_t c1 = 0; + uint32_t c2 = 0; + uint32_t k = 0; + uint32_t i; + uint32_t t0, t1; + + __asm__ volatile ( + ".syntax unified \n\t" + + "1: \n\t" /* outer loop (k < num_words) */ + "movs %[i], #0 \n\t" /* i = 0 */ + "b 3f \n\t" + + "2: \n\t" /* outer loop (k >= num_words) */ + "movs %[i], %[k] \n\t" /* i = k */ + "subs %[i], %[last_word] \n\t" /* i = k - (num_words - 1) (times 4) */ + + "3: \n\t" /* inner loop */ + "subs %[t0], %[k], %[i] \n\t" /* t0 = k-i */ + + "ldr %[t1], [%[right], %[t0]] \n\t" /* t1 = right[k - i] */ + "ldr %[t0], [%[left], %[i]] \n\t" /* t0 = left[i] */ + + "umull %[t0], %[t1], %[t0], %[t1] \n\t" /* (t0, t1) = left[i] * right[k - i] */ + + "adds %[c0], %[c0], %[t0] \n\t" /* add low word to c0 */ + "adcs %[c1], %[c1], %[t1] \n\t" /* add high word to c1, including carry */ + "adcs %[c2], %[c2], #0 \n\t" /* add carry to c2 */ + + "adds %[i], #4 \n\t" /* i += 4 */ + "cmp %[i], %[last_word] \n\t" /* i > (num_words - 1) (times 4)? */ + "bgt 4f \n\t" /* if so, exit the loop */ + "cmp %[i], %[k] \n\t" /* i <= k? */ + "ble 3b \n\t" /* if so, continue looping */ + + "4: \n\t" /* end inner loop */ + + "str %[c0], [%[result], %[k]] \n\t" /* result[k] = c0 */ + "mov %[c0], %[c1] \n\t" /* c0 = c1 */ + "mov %[c1], %[c2] \n\t" /* c1 = c2 */ + "movs %[c2], #0 \n\t" /* c2 = 0 */ + "adds %[k], #4 \n\t" /* k += 4 */ + "cmp %[k], %[last_word] \n\t" /* k <= (num_words - 1) (times 4) ? */ + "ble 1b \n\t" /* if so, loop back, start with i = 0 */ + "cmp %[k], %[last_word], lsl #1 \n\t" /* k <= (num_words * 2 - 2) (times 4) ? */ + "ble 2b \n\t" /* if so, loop back, start with i = (k + 1) - num_words */ + /* end outer loop */ + + "str %[c0], [%[result], %[k]] \n\t" /* result[num_words * 2 - 1] = c0 */ + RESUME_SYNTAX + : [c0] "+r" (c0), [c1] "+r" (c1), [c2] "+r" (c2), + [k] "+r" (k), [i] "=&r" (i), [t0] "=&r" (t0), [t1] "=&r" (t1) + : [result] "r" (result), [left] "r" (left), [right] "r" (right), + [last_word] "r" ((num_words - 1) * 4) + : "cc", "memory" + ); + +#else /* Thumb-1 */ + uint32_t r4, r5, r6, r7; + + __asm__ volatile ( + ".syntax unified \n\t" + "subs %[r3], #1 \n\t" /* r3 = num_words - 1 */ + "lsls %[r3], #2 \n\t" /* r3 = (num_words - 1) * 4 */ + "mov r8, %[r3] \n\t" /* r8 = (num_words - 1) * 4 */ + "lsls %[r3], #1 \n\t" /* r3 = (num_words - 1) * 8 */ + "mov r9, %[r3] \n\t" /* r9 = (num_words - 1) * 8 */ + "movs %[r3], #0 \n\t" /* c0 = 0 */ + "movs %[r4], #0 \n\t" /* c1 = 0 */ + "movs %[r5], #0 \n\t" /* c2 = 0 */ + "movs %[r6], #0 \n\t" /* k = 0 */ + + "push {%[r0]} \n\t" /* keep result on the stack */ + + "1: \n\t" /* outer loop (k < num_words) */ + "movs %[r7], #0 \n\t" /* r7 = i = 0 */ + "b 3f \n\t" + + "2: \n\t" /* outer loop (k >= num_words) */ + "movs %[r7], %[r6] \n\t" /* r7 = k */ + "mov %[r0], r8 \n\t" /* r0 = (num_words - 1) * 4 */ + "subs %[r7], %[r0] \n\t" /* r7 = i = k - (num_words - 1) (times 4) */ + + "3: \n\t" /* inner loop */ + "mov r10, %[r3] \n\t" + "mov r11, %[r4] \n\t" + "mov r12, %[r5] \n\t" + "mov r14, %[r6] \n\t" + "subs %[r0], %[r6], %[r7] \n\t" /* r0 = k - i */ + + "ldr %[r4], [%[r2], %[r0]] \n\t" /* r4 = right[k - i] */ + "ldr %[r0], [%[r1], %[r7]] \n\t" /* r0 = left[i] */ + + "lsrs %[r3], %[r0], #16 \n\t" /* r3 = a1 */ + "uxth %[r0], %[r0] \n\t" /* r0 = a0 */ + + "lsrs %[r5], %[r4], #16 \n\t" /* r5 = b1 */ + "uxth %[r4], %[r4] \n\t" /* r4 = b0 */ + + "movs %[r6], %[r3] \n\t" /* r6 = a1 */ + "muls %[r6], %[r5], %[r6] \n\t" /* r6 = a1 * b1 */ + "muls %[r3], %[r4], %[r3] \n\t" /* r3 = b0 * a1 */ + "muls %[r5], %[r0], %[r5] \n\t" /* r5 = a0 * b1 */ + "muls %[r0], %[r4], %[r0] \n\t" /* r0 = a0 * b0 */ + + /* Add middle terms */ + "lsls %[r4], %[r3], #16 \n\t" + "lsrs %[r3], %[r3], #16 \n\t" + "adds %[r0], %[r4] \n\t" + "adcs %[r6], %[r3] \n\t" + + "lsls %[r4], %[r5], #16 \n\t" + "lsrs %[r5], %[r5], #16 \n\t" + "adds %[r0], %[r4] \n\t" + "adcs %[r6], %[r5] \n\t" + + "mov %[r3], r10\n\t" + "mov %[r4], r11\n\t" + "mov %[r5], r12\n\t" + "adds %[r3], %[r0] \n\t" /* add low word to c0 */ + "adcs %[r4], %[r6] \n\t" /* add high word to c1, including carry */ + "movs %[r0], #0 \n\t" /* r0 = 0 (does not affect carry bit) */ + "adcs %[r5], %[r0] \n\t" /* add carry to c2 */ + + "mov %[r6], r14\n\t" /* r6 = k */ + + "adds %[r7], #4 \n\t" /* i += 4 */ + "cmp %[r7], r8 \n\t" /* i > (num_words - 1) (times 4)? */ + "bgt 4f \n\t" /* if so, exit the loop */ + "cmp %[r7], %[r6] \n\t" /* i <= k? */ + "ble 3b \n\t" /* if so, continue looping */ + + "4: \n\t" /* end inner loop */ + + "ldr %[r0], [sp, #0] \n\t" /* r0 = result */ + + "str %[r3], [%[r0], %[r6]] \n\t" /* result[k] = c0 */ + "mov %[r3], %[r4] \n\t" /* c0 = c1 */ + "mov %[r4], %[r5] \n\t" /* c1 = c2 */ + "movs %[r5], #0 \n\t" /* c2 = 0 */ + "adds %[r6], #4 \n\t" /* k += 4 */ + "cmp %[r6], r8 \n\t" /* k <= (num_words - 1) (times 4) ? */ + "ble 1b \n\t" /* if so, loop back, start with i = 0 */ + "cmp %[r6], r9 \n\t" /* k <= (num_words * 2 - 2) (times 4) ? */ + "ble 2b \n\t" /* if so, loop back, with i = (k + 1) - num_words */ + /* end outer loop */ + + "str %[r3], [%[r0], %[r6]] \n\t" /* result[num_words * 2 - 1] = c0 */ + "pop {%[r0]} \n\t" /* pop result off the stack */ + + RESUME_SYNTAX + : [r3] "+l" (num_words), [r4] "=&l" (r4), + [r5] "=&l" (r5), [r6] "=&l" (r6), [r7] "=&l" (r7) + : [r0] "l" (result), [r1] "l" (left), [r2] "l" (right) + : "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory" + ); +#endif +} +#define asm_mult 1 +#endif + +#if uECC_SQUARE_FUNC +#if !asm_square +uECC_VLI_API void uECC_vli_square(uECC_word_t *result, + const uECC_word_t *left, + wordcount_t num_words) { +#if (uECC_PLATFORM != uECC_arm_thumb) + uint32_t c0 = 0; + uint32_t c1 = 0; + uint32_t c2 = 0; + uint32_t k = 0; + uint32_t i, tt; + uint32_t t0, t1; + + __asm__ volatile ( + ".syntax unified \n\t" + + "1: \n\t" /* outer loop (k < num_words) */ + "movs %[i], #0 \n\t" /* i = 0 */ + "b 3f \n\t" + + "2: \n\t" /* outer loop (k >= num_words) */ + "movs %[i], %[k] \n\t" /* i = k */ + "subs %[i], %[last_word] \n\t" /* i = k - (num_words - 1) (times 4) */ + + "3: \n\t" /* inner loop */ + "subs %[tt], %[k], %[i] \n\t" /* tt = k-i */ + + "ldr %[t1], [%[left], %[tt]] \n\t" /* t1 = left[k - i] */ + "ldr %[t0], [%[left], %[i]] \n\t" /* t0 = left[i] */ + + "umull %[t0], %[t1], %[t0], %[t1] \n\t" /* (t0, t1) = left[i] * right[k - i] */ + + "cmp %[i], %[tt] \n\t" /* (i < k - i) ? */ + "bge 4f \n\t" /* if i >= k - i, skip */ + "adds %[c0], %[c0], %[t0] \n\t" /* add low word to c0 */ + "adcs %[c1], %[c1], %[t1] \n\t" /* add high word to c1, including carry */ + "adcs %[c2], %[c2], #0 \n\t" /* add carry to c2 */ + + "4: \n\t" + "adds %[c0], %[c0], %[t0] \n\t" /* add low word to c0 */ + "adcs %[c1], %[c1], %[t1] \n\t" /* add high word to c1, including carry */ + "adcs %[c2], %[c2], #0 \n\t" /* add carry to c2 */ + + "adds %[i], #4 \n\t" /* i += 4 */ + "cmp %[i], %[k] \n\t" /* i >= k? */ + "bge 5f \n\t" /* if so, exit the loop */ + "subs %[tt], %[k], %[i] \n\t" /* tt = k - i */ + "cmp %[i], %[tt] \n\t" /* i <= k - i? */ + "ble 3b \n\t" /* if so, continue looping */ + + "5: \n\t" /* end inner loop */ + + "str %[c0], [%[result], %[k]] \n\t" /* result[k] = c0 */ + "mov %[c0], %[c1] \n\t" /* c0 = c1 */ + "mov %[c1], %[c2] \n\t" /* c1 = c2 */ + "movs %[c2], #0 \n\t" /* c2 = 0 */ + "adds %[k], #4 \n\t" /* k += 4 */ + "cmp %[k], %[last_word] \n\t" /* k <= (num_words - 1) (times 4) ? */ + "ble 1b \n\t" /* if so, loop back, start with i = 0 */ + "cmp %[k], %[last_word], lsl #1 \n\t" /* k <= (num_words * 2 - 2) (times 4) ? */ + "ble 2b \n\t" /* if so, loop back, start with i = (k + 1) - num_words */ + /* end outer loop */ + + "str %[c0], [%[result], %[k]] \n\t" /* result[num_words * 2 - 1] = c0 */ + RESUME_SYNTAX + : [c0] "+r" (c0), [c1] "+r" (c1), [c2] "+r" (c2), + [k] "+r" (k), [i] "=&r" (i), [tt] "=&r" (tt), [t0] "=&r" (t0), [t1] "=&r" (t1) + : [result] "r" (result), [left] "r" (left), [last_word] "r" ((num_words - 1) * 4) + : "cc", "memory" + ); + +#else + uint32_t r3, r4, r5, r6, r7; + + __asm__ volatile ( + ".syntax unified \n\t" + "subs %[r2], #1 \n\t" /* r2 = num_words - 1 */ + "lsls %[r2], #2 \n\t" /* r2 = (num_words - 1) * 4 */ + "mov r8, %[r2] \n\t" /* r8 = (num_words - 1) * 4 */ + "lsls %[r2], #1 \n\t" /* r2 = (num_words - 1) * 8 */ + "mov r9, %[r2] \n\t" /* r9 = (num_words - 1) * 8 */ + "movs %[r2], #0 \n\t" /* c0 = 0 */ + "movs %[r3], #0 \n\t" /* c1 = 0 */ + "movs %[r4], #0 \n\t" /* c2 = 0 */ + "movs %[r5], #0 \n\t" /* k = 0 */ + + "push {%[r0]} \n\t" /* keep result on the stack */ + + "1: \n\t" /* outer loop (k < num_words) */ + "movs %[r6], #0 \n\t" /* r6 = i = 0 */ + "b 3f \n\t" + + "2: \n\t" /* outer loop (k >= num_words) */ + "movs %[r6], %[r5] \n\t" /* r6 = k */ + "mov %[r0], r8 \n\t" /* r0 = (num_words - 1) * 4 */ + "subs %[r6], %[r0] \n\t" /* r6 = i = k - (num_words - 1) (times 4) */ + + "3: \n\t" /* inner loop */ + "mov r10, %[r2] \n\t" + "mov r11, %[r3] \n\t" + "mov r12, %[r4] \n\t" + "mov r14, %[r5] \n\t" + "subs %[r7], %[r5], %[r6] \n\t" /* r7 = k - i */ + + "ldr %[r3], [%[r1], %[r7]] \n\t" /* r3 = left[k - i] */ + "ldr %[r0], [%[r1], %[r6]] \n\t" /* r0 = left[i] */ + + "lsrs %[r2], %[r0], #16 \n\t" /* r2 = a1 */ + "uxth %[r0], %[r0] \n\t" /* r0 = a0 */ + + "lsrs %[r4], %[r3], #16 \n\t" /* r4 = b1 */ + "uxth %[r3], %[r3] \n\t" /* r3 = b0 */ + + "movs %[r5], %[r2] \n\t" /* r5 = a1 */ + "muls %[r5], %[r4], %[r5] \n\t" /* r5 = a1 * b1 */ + "muls %[r2], %[r3], %[r2] \n\t" /* r2 = b0 * a1 */ + "muls %[r4], %[r0], %[r4] \n\t" /* r4 = a0 * b1 */ + "muls %[r0], %[r3], %[r0] \n\t" /* r0 = a0 * b0 */ + + /* Add middle terms */ + "lsls %[r3], %[r2], #16 \n\t" + "lsrs %[r2], %[r2], #16 \n\t" + "adds %[r0], %[r3] \n\t" + "adcs %[r5], %[r2] \n\t" + + "lsls %[r3], %[r4], #16 \n\t" + "lsrs %[r4], %[r4], #16 \n\t" + "adds %[r0], %[r3] \n\t" + "adcs %[r5], %[r4] \n\t" + + /* Add to acc, doubling if necessary */ + "mov %[r2], r10\n\t" + "mov %[r3], r11\n\t" + "mov %[r4], r12\n\t" + + "cmp %[r6], %[r7] \n\t" /* (i < k - i) ? */ + "bge 4f \n\t" /* if i >= k - i, skip */ + "movs %[r7], #0 \n\t" /* r7 = 0 */ + "adds %[r2], %[r0] \n\t" /* add low word to c0 */ + "adcs %[r3], %[r5] \n\t" /* add high word to c1, including carry */ + "adcs %[r4], %[r7] \n\t" /* add carry to c2 */ + "4: \n\t" + "movs %[r7], #0 \n\t" /* r7 = 0 */ + "adds %[r2], %[r0] \n\t" /* add low word to c0 */ + "adcs %[r3], %[r5] \n\t" /* add high word to c1, including carry */ + "adcs %[r4], %[r7] \n\t" /* add carry to c2 */ + + "mov %[r5], r14\n\t" /* r5 = k */ + + "adds %[r6], #4 \n\t" /* i += 4 */ + "cmp %[r6], %[r5] \n\t" /* i >= k? */ + "bge 5f \n\t" /* if so, exit the loop */ + "subs %[r7], %[r5], %[r6] \n\t" /* r7 = k - i */ + "cmp %[r6], %[r7] \n\t" /* i <= k - i? */ + "ble 3b \n\t" /* if so, continue looping */ + + "5: \n\t" /* end inner loop */ + + "ldr %[r0], [sp, #0] \n\t" /* r0 = result */ + + "str %[r2], [%[r0], %[r5]] \n\t" /* result[k] = c0 */ + "mov %[r2], %[r3] \n\t" /* c0 = c1 */ + "mov %[r3], %[r4] \n\t" /* c1 = c2 */ + "movs %[r4], #0 \n\t" /* c2 = 0 */ + "adds %[r5], #4 \n\t" /* k += 4 */ + "cmp %[r5], r8 \n\t" /* k <= (num_words - 1) (times 4) ? */ + "ble 1b \n\t" /* if so, loop back, start with i = 0 */ + "cmp %[r5], r9 \n\t" /* k <= (num_words * 2 - 2) (times 4) ? */ + "ble 2b \n\t" /* if so, loop back, with i = (k + 1) - num_words */ + /* end outer loop */ + + "str %[r2], [%[r0], %[r5]] \n\t" /* result[num_words * 2 - 1] = c0 */ + "pop {%[r0]} \n\t" /* pop result off the stack */ + + RESUME_SYNTAX + : [r2] "+l" (num_words), [r3] "=&l" (r3), [r4] "=&l" (r4), + [r5] "=&l" (r5), [r6] "=&l" (r6), [r7] "=&l" (r7) + : [r0] "l" (result), [r1] "l" (left) + : "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory" + ); +#endif +} +#define asm_square 1 +#endif +#endif /* uECC_SQUARE_FUNC */ + +#endif /* _UECC_ASM_ARM_H_ */ diff --git a/SDK/12.3.0_d7731ad/external/micro-ecc/micro-ecc/curve-specific.inc b/SDK/12.3.0_d7731ad/external/micro-ecc/micro-ecc/curve-specific.inc new file mode 100644 index 0000000..0453b21 --- /dev/null +++ b/SDK/12.3.0_d7731ad/external/micro-ecc/micro-ecc/curve-specific.inc @@ -0,0 +1,1248 @@ +/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ + +#ifndef _UECC_CURVE_SPECIFIC_H_ +#define _UECC_CURVE_SPECIFIC_H_ + +#define num_bytes_secp160r1 20 +#define num_bytes_secp192r1 24 +#define num_bytes_secp224r1 28 +#define num_bytes_secp256r1 32 +#define num_bytes_secp256k1 32 + +#if (uECC_WORD_SIZE == 1) + +#define num_words_secp160r1 20 +#define num_words_secp192r1 24 +#define num_words_secp224r1 28 +#define num_words_secp256r1 32 +#define num_words_secp256k1 32 + +#define BYTES_TO_WORDS_8(a, b, c, d, e, f, g, h) \ + 0x##a, 0x##b, 0x##c, 0x##d, 0x##e, 0x##f, 0x##g, 0x##h +#define BYTES_TO_WORDS_4(a, b, c, d) 0x##a, 0x##b, 0x##c, 0x##d + +#elif (uECC_WORD_SIZE == 4) + +#define num_words_secp160r1 5 +#define num_words_secp192r1 6 +#define num_words_secp224r1 7 +#define num_words_secp256r1 8 +#define num_words_secp256k1 8 + +#define BYTES_TO_WORDS_8(a, b, c, d, e, f, g, h) 0x##d##c##b##a, 0x##h##g##f##e +#define BYTES_TO_WORDS_4(a, b, c, d) 0x##d##c##b##a + +#elif (uECC_WORD_SIZE == 8) + +#define num_words_secp160r1 3 +#define num_words_secp192r1 3 +#define num_words_secp224r1 4 +#define num_words_secp256r1 4 +#define num_words_secp256k1 4 + +#define BYTES_TO_WORDS_8(a, b, c, d, e, f, g, h) 0x##h##g##f##e##d##c##b##a##ull +#define BYTES_TO_WORDS_4(a, b, c, d) 0x##d##c##b##a##ull + +#endif /* uECC_WORD_SIZE */ + +#if uECC_SUPPORTS_secp160r1 || uECC_SUPPORTS_secp192r1 || \ + uECC_SUPPORTS_secp224r1 || uECC_SUPPORTS_secp256r1 +static void double_jacobian_default(uECC_word_t * X1, + uECC_word_t * Y1, + uECC_word_t * Z1, + uECC_Curve curve) { + /* t1 = X, t2 = Y, t3 = Z */ + uECC_word_t t4[uECC_MAX_WORDS]; + uECC_word_t t5[uECC_MAX_WORDS]; + wordcount_t num_words = curve->num_words; + + if (uECC_vli_isZero(Z1, num_words)) { + return; + } + + uECC_vli_modSquare_fast(t4, Y1, curve); /* t4 = y1^2 */ + uECC_vli_modMult_fast(t5, X1, t4, curve); /* t5 = x1*y1^2 = A */ + uECC_vli_modSquare_fast(t4, t4, curve); /* t4 = y1^4 */ + uECC_vli_modMult_fast(Y1, Y1, Z1, curve); /* t2 = y1*z1 = z3 */ + uECC_vli_modSquare_fast(Z1, Z1, curve); /* t3 = z1^2 */ + + uECC_vli_modAdd(X1, X1, Z1, curve->p, num_words); /* t1 = x1 + z1^2 */ + uECC_vli_modAdd(Z1, Z1, Z1, curve->p, num_words); /* t3 = 2*z1^2 */ + uECC_vli_modSub(Z1, X1, Z1, curve->p, num_words); /* t3 = x1 - z1^2 */ + uECC_vli_modMult_fast(X1, X1, Z1, curve); /* t1 = x1^2 - z1^4 */ + + uECC_vli_modAdd(Z1, X1, X1, curve->p, num_words); /* t3 = 2*(x1^2 - z1^4) */ + uECC_vli_modAdd(X1, X1, Z1, curve->p, num_words); /* t1 = 3*(x1^2 - z1^4) */ + if (uECC_vli_testBit(X1, 0)) { + uECC_word_t l_carry = uECC_vli_add(X1, X1, curve->p, num_words); + uECC_vli_rshift1(X1, num_words); + X1[num_words - 1] |= l_carry << (uECC_WORD_BITS - 1); + } else { + uECC_vli_rshift1(X1, num_words); + } + /* t1 = 3/2*(x1^2 - z1^4) = B */ + + uECC_vli_modSquare_fast(Z1, X1, curve); /* t3 = B^2 */ + uECC_vli_modSub(Z1, Z1, t5, curve->p, num_words); /* t3 = B^2 - A */ + uECC_vli_modSub(Z1, Z1, t5, curve->p, num_words); /* t3 = B^2 - 2A = x3 */ + uECC_vli_modSub(t5, t5, Z1, curve->p, num_words); /* t5 = A - x3 */ + uECC_vli_modMult_fast(X1, X1, t5, curve); /* t1 = B * (A - x3) */ + uECC_vli_modSub(t4, X1, t4, curve->p, num_words); /* t4 = B * (A - x3) - y1^4 = y3 */ + + uECC_vli_set(X1, Z1, num_words); + uECC_vli_set(Z1, Y1, num_words); + uECC_vli_set(Y1, t4, num_words); +} + +/* Computes result = x^3 + ax + b. result must not overlap x. */ +static void x_side_default(uECC_word_t *result, const uECC_word_t *x, uECC_Curve curve) { + uECC_word_t _3[uECC_MAX_WORDS] = {3}; /* -a = 3 */ + wordcount_t num_words = curve->num_words; + + uECC_vli_modSquare_fast(result, x, curve); /* r = x^2 */ + uECC_vli_modSub(result, result, _3, curve->p, num_words); /* r = x^2 - 3 */ + uECC_vli_modMult_fast(result, result, x, curve); /* r = x^3 - 3x */ + uECC_vli_modAdd(result, result, curve->b, curve->p, num_words); /* r = x^3 - 3x + b */ +} +#endif /* uECC_SUPPORTS_secp... */ + +#if uECC_SUPPORT_COMPRESSED_POINT +#if uECC_SUPPORTS_secp160r1 || uECC_SUPPORTS_secp192r1 || \ + uECC_SUPPORTS_secp256r1 || uECC_SUPPORTS_secp256k1 +/* Compute a = sqrt(a) (mod curve_p). */ +static void mod_sqrt_default(uECC_word_t *a, uECC_Curve curve) { + bitcount_t i; + uECC_word_t p1[uECC_MAX_WORDS] = {1}; + uECC_word_t l_result[uECC_MAX_WORDS] = {1}; + wordcount_t num_words = curve->num_words; + + /* When curve->p == 3 (mod 4), we can compute + sqrt(a) = a^((curve->p + 1) / 4) (mod curve->p). */ + uECC_vli_add(p1, curve->p, p1, num_words); /* p1 = curve_p + 1 */ + for (i = uECC_vli_numBits(p1, num_words) - 1; i > 1; --i) { + uECC_vli_modSquare_fast(l_result, l_result, curve); + if (uECC_vli_testBit(p1, i)) { + uECC_vli_modMult_fast(l_result, l_result, a, curve); + } + } + uECC_vli_set(a, l_result, num_words); +} +#endif /* uECC_SUPPORTS_secp... */ +#endif /* uECC_SUPPORT_COMPRESSED_POINT */ + +#if uECC_SUPPORTS_secp160r1 + +#if (uECC_OPTIMIZATION_LEVEL > 0) +static void vli_mmod_fast_secp160r1(uECC_word_t *result, uECC_word_t *product); +#endif + +static const struct uECC_Curve_t curve_secp160r1 = { + num_words_secp160r1, + num_bytes_secp160r1, + 161, /* num_n_bits */ + { BYTES_TO_WORDS_8(FF, FF, FF, 7F, FF, FF, FF, FF), + BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), + BYTES_TO_WORDS_4(FF, FF, FF, FF) }, + { BYTES_TO_WORDS_8(57, 22, 75, CA, D3, AE, 27, F9), + BYTES_TO_WORDS_8(C8, F4, 01, 00, 00, 00, 00, 00), + BYTES_TO_WORDS_8(00, 00, 00, 00, 01, 00, 00, 00) }, + { BYTES_TO_WORDS_8(82, FC, CB, 13, B9, 8B, C3, 68), + BYTES_TO_WORDS_8(89, 69, 64, 46, 28, 73, F5, 8E), + BYTES_TO_WORDS_4(68, B5, 96, 4A), + + BYTES_TO_WORDS_8(32, FB, C5, 7A, 37, 51, 23, 04), + BYTES_TO_WORDS_8(12, C9, DC, 59, 7D, 94, 68, 31), + BYTES_TO_WORDS_4(55, 28, A6, 23) }, + { BYTES_TO_WORDS_8(45, FA, 65, C5, AD, D4, D4, 81), + BYTES_TO_WORDS_8(9F, F8, AC, 65, 8B, 7A, BD, 54), + BYTES_TO_WORDS_4(FC, BE, 97, 1C) }, + &double_jacobian_default, +#if uECC_SUPPORT_COMPRESSED_POINT + &mod_sqrt_default, +#endif + &x_side_default, +#if (uECC_OPTIMIZATION_LEVEL > 0) + &vli_mmod_fast_secp160r1 +#endif +}; + +uECC_Curve uECC_secp160r1(void) { return &curve_secp160r1; } + +#if (uECC_OPTIMIZATION_LEVEL > 0 && !asm_mmod_fast_secp160r1) +/* Computes result = product % curve_p + see http://www.isys.uni-klu.ac.at/PDF/2001-0126-MT.pdf page 354 + + Note that this only works if log2(omega) < log2(p) / 2 */ +static void omega_mult_secp160r1(uECC_word_t *result, const uECC_word_t *right); +#if uECC_WORD_SIZE == 8 +static void vli_mmod_fast_secp160r1(uECC_word_t *result, uECC_word_t *product) { + uECC_word_t tmp[2 * num_words_secp160r1]; + uECC_word_t copy; + + uECC_vli_clear(tmp, num_words_secp160r1); + uECC_vli_clear(tmp + num_words_secp160r1, num_words_secp160r1); + + omega_mult_secp160r1(tmp, product + num_words_secp160r1 - 1); /* (Rq, q) = q * c */ + + product[num_words_secp160r1 - 1] &= 0xffffffff; + copy = tmp[num_words_secp160r1 - 1]; + tmp[num_words_secp160r1 - 1] &= 0xffffffff; + uECC_vli_add(result, product, tmp, num_words_secp160r1); /* (C, r) = r + q */ + uECC_vli_clear(product, num_words_secp160r1); + tmp[num_words_secp160r1 - 1] = copy; + omega_mult_secp160r1(product, tmp + num_words_secp160r1 - 1); /* Rq*c */ + uECC_vli_add(result, result, product, num_words_secp160r1); /* (C1, r) = r + Rq*c */ + + while (uECC_vli_cmp_unsafe(result, curve_secp160r1.p, num_words_secp160r1) > 0) { + uECC_vli_sub(result, result, curve_secp160r1.p, num_words_secp160r1); + } +} + +static void omega_mult_secp160r1(uint64_t *result, const uint64_t *right) { + uint32_t carry; + unsigned i; + + /* Multiply by (2^31 + 1). */ + carry = 0; + for (i = 0; i < num_words_secp160r1; ++i) { + uint64_t tmp = (right[i] >> 32) | (right[i + 1] << 32); + result[i] = (tmp << 31) + tmp + carry; + carry = (tmp >> 33) + (result[i] < tmp || (carry && result[i] == tmp)); + } + result[i] = carry; +} +#else +static void vli_mmod_fast_secp160r1(uECC_word_t *result, uECC_word_t *product) { + uECC_word_t tmp[2 * num_words_secp160r1]; + uECC_word_t carry; + + uECC_vli_clear(tmp, num_words_secp160r1); + uECC_vli_clear(tmp + num_words_secp160r1, num_words_secp160r1); + + omega_mult_secp160r1(tmp, product + num_words_secp160r1); /* (Rq, q) = q * c */ + + carry = uECC_vli_add(result, product, tmp, num_words_secp160r1); /* (C, r) = r + q */ + uECC_vli_clear(product, num_words_secp160r1); + omega_mult_secp160r1(product, tmp + num_words_secp160r1); /* Rq*c */ + carry += uECC_vli_add(result, result, product, num_words_secp160r1); /* (C1, r) = r + Rq*c */ + + while (carry > 0) { + --carry; + uECC_vli_sub(result, result, curve_secp160r1.p, num_words_secp160r1); + } + if (uECC_vli_cmp_unsafe(result, curve_secp160r1.p, num_words_secp160r1) > 0) { + uECC_vli_sub(result, result, curve_secp160r1.p, num_words_secp160r1); + } +} +#endif + +#if uECC_WORD_SIZE == 1 +static void omega_mult_secp160r1(uint8_t *result, const uint8_t *right) { + uint8_t carry; + uint8_t i; + + /* Multiply by (2^31 + 1). */ + uECC_vli_set(result + 4, right, num_words_secp160r1); /* 2^32 */ + uECC_vli_rshift1(result + 4, num_words_secp160r1); /* 2^31 */ + result[3] = right[0] << 7; /* get last bit from shift */ + + carry = uECC_vli_add(result, result, right, num_words_secp160r1); /* 2^31 + 1 */ + for (i = num_words_secp160r1; carry; ++i) { + uint16_t sum = (uint16_t)result[i] + carry; + result[i] = (uint8_t)sum; + carry = sum >> 8; + } +} +#elif uECC_WORD_SIZE == 4 +static void omega_mult_secp160r1(uint32_t *result, const uint32_t *right) { + uint32_t carry; + unsigned i; + + /* Multiply by (2^31 + 1). */ + uECC_vli_set(result + 1, right, num_words_secp160r1); /* 2^32 */ + uECC_vli_rshift1(result + 1, num_words_secp160r1); /* 2^31 */ + result[0] = right[0] << 31; /* get last bit from shift */ + + carry = uECC_vli_add(result, result, right, num_words_secp160r1); /* 2^31 + 1 */ + for (i = num_words_secp160r1; carry; ++i) { + uint64_t sum = (uint64_t)result[i] + carry; + result[i] = (uint32_t)sum; + carry = sum >> 32; + } +} +#endif /* uECC_WORD_SIZE */ +#endif /* (uECC_OPTIMIZATION_LEVEL > 0 && !asm_mmod_fast_secp160r1) */ + +#endif /* uECC_SUPPORTS_secp160r1 */ + +#if uECC_SUPPORTS_secp192r1 + +#if (uECC_OPTIMIZATION_LEVEL > 0) +static void vli_mmod_fast_secp192r1(uECC_word_t *result, uECC_word_t *product); +#endif + +static const struct uECC_Curve_t curve_secp192r1 = { + num_words_secp192r1, + num_bytes_secp192r1, + 192, /* num_n_bits */ + { BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), + BYTES_TO_WORDS_8(FE, FF, FF, FF, FF, FF, FF, FF), + BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF) }, + { BYTES_TO_WORDS_8(31, 28, D2, B4, B1, C9, 6B, 14), + BYTES_TO_WORDS_8(36, F8, DE, 99, FF, FF, FF, FF), + BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF) }, + { BYTES_TO_WORDS_8(12, 10, FF, 82, FD, 0A, FF, F4), + BYTES_TO_WORDS_8(00, 88, A1, 43, EB, 20, BF, 7C), + BYTES_TO_WORDS_8(F6, 90, 30, B0, 0E, A8, 8D, 18), + + BYTES_TO_WORDS_8(11, 48, 79, 1E, A1, 77, F9, 73), + BYTES_TO_WORDS_8(D5, CD, 24, 6B, ED, 11, 10, 63), + BYTES_TO_WORDS_8(78, DA, C8, FF, 95, 2B, 19, 07) }, + { BYTES_TO_WORDS_8(B1, B9, 46, C1, EC, DE, B8, FE), + BYTES_TO_WORDS_8(49, 30, 24, 72, AB, E9, A7, 0F), + BYTES_TO_WORDS_8(E7, 80, 9C, E5, 19, 05, 21, 64) }, + &double_jacobian_default, +#if uECC_SUPPORT_COMPRESSED_POINT + &mod_sqrt_default, +#endif + &x_side_default, +#if (uECC_OPTIMIZATION_LEVEL > 0) + &vli_mmod_fast_secp192r1 +#endif +}; + +uECC_Curve uECC_secp192r1(void) { return &curve_secp192r1; } + +#if (uECC_OPTIMIZATION_LEVEL > 0) +/* Computes result = product % curve_p. + See algorithm 5 and 6 from http://www.isys.uni-klu.ac.at/PDF/2001-0126-MT.pdf */ +#if uECC_WORD_SIZE == 1 +static void vli_mmod_fast_secp192r1(uint8_t *result, uint8_t *product) { + uint8_t tmp[num_words_secp192r1]; + uint8_t carry; + + uECC_vli_set(result, product, num_words_secp192r1); + + uECC_vli_set(tmp, &product[24], num_words_secp192r1); + carry = uECC_vli_add(result, result, tmp, num_words_secp192r1); + + tmp[0] = tmp[1] = tmp[2] = tmp[3] = tmp[4] = tmp[5] = tmp[6] = tmp[7] = 0; + tmp[8] = product[24]; tmp[9] = product[25]; tmp[10] = product[26]; tmp[11] = product[27]; + tmp[12] = product[28]; tmp[13] = product[29]; tmp[14] = product[30]; tmp[15] = product[31]; + tmp[16] = product[32]; tmp[17] = product[33]; tmp[18] = product[34]; tmp[19] = product[35]; + tmp[20] = product[36]; tmp[21] = product[37]; tmp[22] = product[38]; tmp[23] = product[39]; + carry += uECC_vli_add(result, result, tmp, num_words_secp192r1); + + tmp[0] = tmp[8] = product[40]; + tmp[1] = tmp[9] = product[41]; + tmp[2] = tmp[10] = product[42]; + tmp[3] = tmp[11] = product[43]; + tmp[4] = tmp[12] = product[44]; + tmp[5] = tmp[13] = product[45]; + tmp[6] = tmp[14] = product[46]; + tmp[7] = tmp[15] = product[47]; + tmp[16] = tmp[17] = tmp[18] = tmp[19] = tmp[20] = tmp[21] = tmp[22] = tmp[23] = 0; + carry += uECC_vli_add(result, result, tmp, num_words_secp192r1); + + while (carry || uECC_vli_cmp_unsafe(curve_secp192r1.p, result, num_words_secp192r1) != 1) { + carry -= uECC_vli_sub(result, result, curve_secp192r1.p, num_words_secp192r1); + } +} +#elif uECC_WORD_SIZE == 4 +static void vli_mmod_fast_secp192r1(uint32_t *result, uint32_t *product) { + uint32_t tmp[num_words_secp192r1]; + int carry; + + uECC_vli_set(result, product, num_words_secp192r1); + + uECC_vli_set(tmp, &product[6], num_words_secp192r1); + carry = uECC_vli_add(result, result, tmp, num_words_secp192r1); + + tmp[0] = tmp[1] = 0; + tmp[2] = product[6]; + tmp[3] = product[7]; + tmp[4] = product[8]; + tmp[5] = product[9]; + carry += uECC_vli_add(result, result, tmp, num_words_secp192r1); + + tmp[0] = tmp[2] = product[10]; + tmp[1] = tmp[3] = product[11]; + tmp[4] = tmp[5] = 0; + carry += uECC_vli_add(result, result, tmp, num_words_secp192r1); + + while (carry || uECC_vli_cmp_unsafe(curve_secp192r1.p, result, num_words_secp192r1) != 1) { + carry -= uECC_vli_sub(result, result, curve_secp192r1.p, num_words_secp192r1); + } +} +#else +static void vli_mmod_fast_secp192r1(uint64_t *result, uint64_t *product) { + uint64_t tmp[num_words_secp192r1]; + int carry; + + uECC_vli_set(result, product, num_words_secp192r1); + + uECC_vli_set(tmp, &product[3], num_words_secp192r1); + carry = (int)uECC_vli_add(result, result, tmp, num_words_secp192r1); + + tmp[0] = 0; + tmp[1] = product[3]; + tmp[2] = product[4]; + carry += uECC_vli_add(result, result, tmp, num_words_secp192r1); + + tmp[0] = tmp[1] = product[5]; + tmp[2] = 0; + carry += uECC_vli_add(result, result, tmp, num_words_secp192r1); + + while (carry || uECC_vli_cmp_unsafe(curve_secp192r1.p, result, num_words_secp192r1) != 1) { + carry -= uECC_vli_sub(result, result, curve_secp192r1.p, num_words_secp192r1); + } +} +#endif /* uECC_WORD_SIZE */ +#endif /* (uECC_OPTIMIZATION_LEVEL > 0) */ + +#endif /* uECC_SUPPORTS_secp192r1 */ + +#if uECC_SUPPORTS_secp224r1 + +#if uECC_SUPPORT_COMPRESSED_POINT +static void mod_sqrt_secp224r1(uECC_word_t *a, uECC_Curve curve); +#endif +#if (uECC_OPTIMIZATION_LEVEL > 0) +static void vli_mmod_fast_secp224r1(uECC_word_t *result, uECC_word_t *product); +#endif + +static const struct uECC_Curve_t curve_secp224r1 = { + num_words_secp224r1, + num_bytes_secp224r1, + 224, /* num_n_bits */ + { BYTES_TO_WORDS_8(01, 00, 00, 00, 00, 00, 00, 00), + BYTES_TO_WORDS_8(00, 00, 00, 00, FF, FF, FF, FF), + BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), + BYTES_TO_WORDS_4(FF, FF, FF, FF) }, + { BYTES_TO_WORDS_8(3D, 2A, 5C, 5C, 45, 29, DD, 13), + BYTES_TO_WORDS_8(3E, F0, B8, E0, A2, 16, FF, FF), + BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), + BYTES_TO_WORDS_4(FF, FF, FF, FF) }, + { BYTES_TO_WORDS_8(21, 1D, 5C, 11, D6, 80, 32, 34), + BYTES_TO_WORDS_8(22, 11, C2, 56, D3, C1, 03, 4A), + BYTES_TO_WORDS_8(B9, 90, 13, 32, 7F, BF, B4, 6B), + BYTES_TO_WORDS_4(BD, 0C, 0E, B7), + + BYTES_TO_WORDS_8(34, 7E, 00, 85, 99, 81, D5, 44), + BYTES_TO_WORDS_8(64, 47, 07, 5A, A0, 75, 43, CD), + BYTES_TO_WORDS_8(E6, DF, 22, 4C, FB, 23, F7, B5), + BYTES_TO_WORDS_4(88, 63, 37, BD) }, + { BYTES_TO_WORDS_8(B4, FF, 55, 23, 43, 39, 0B, 27), + BYTES_TO_WORDS_8(BA, D8, BF, D7, B7, B0, 44, 50), + BYTES_TO_WORDS_8(56, 32, 41, F5, AB, B3, 04, 0C), + BYTES_TO_WORDS_4(85, 0A, 05, B4) }, + &double_jacobian_default, +#if uECC_SUPPORT_COMPRESSED_POINT + &mod_sqrt_secp224r1, +#endif + &x_side_default, +#if (uECC_OPTIMIZATION_LEVEL > 0) + &vli_mmod_fast_secp224r1 +#endif +}; + +uECC_Curve uECC_secp224r1(void) { return &curve_secp224r1; } + + +#if uECC_SUPPORT_COMPRESSED_POINT +/* Routine 3.2.4 RS; from http://www.nsa.gov/ia/_files/nist-routines.pdf */ +static void mod_sqrt_secp224r1_rs(uECC_word_t *d1, + uECC_word_t *e1, + uECC_word_t *f1, + const uECC_word_t *d0, + const uECC_word_t *e0, + const uECC_word_t *f0) { + uECC_word_t t[num_words_secp224r1]; + + uECC_vli_modSquare_fast(t, d0, &curve_secp224r1); /* t <-- d0 ^ 2 */ + uECC_vli_modMult_fast(e1, d0, e0, &curve_secp224r1); /* e1 <-- d0 * e0 */ + uECC_vli_modAdd(d1, t, f0, curve_secp224r1.p, num_words_secp224r1); /* d1 <-- t + f0 */ + uECC_vli_modAdd(e1, e1, e1, curve_secp224r1.p, num_words_secp224r1); /* e1 <-- e1 + e1 */ + uECC_vli_modMult_fast(f1, t, f0, &curve_secp224r1); /* f1 <-- t * f0 */ + uECC_vli_modAdd(f1, f1, f1, curve_secp224r1.p, num_words_secp224r1); /* f1 <-- f1 + f1 */ + uECC_vli_modAdd(f1, f1, f1, curve_secp224r1.p, num_words_secp224r1); /* f1 <-- f1 + f1 */ +} + +/* Routine 3.2.5 RSS; from http://www.nsa.gov/ia/_files/nist-routines.pdf */ +static void mod_sqrt_secp224r1_rss(uECC_word_t *d1, + uECC_word_t *e1, + uECC_word_t *f1, + const uECC_word_t *d0, + const uECC_word_t *e0, + const uECC_word_t *f0, + const bitcount_t j) { + bitcount_t i; + + uECC_vli_set(d1, d0, num_words_secp224r1); /* d1 <-- d0 */ + uECC_vli_set(e1, e0, num_words_secp224r1); /* e1 <-- e0 */ + uECC_vli_set(f1, f0, num_words_secp224r1); /* f1 <-- f0 */ + for (i = 1; i <= j; i++) { + mod_sqrt_secp224r1_rs(d1, e1, f1, d1, e1, f1); /* RS (d1,e1,f1,d1,e1,f1) */ + } +} + +/* Routine 3.2.6 RM; from http://www.nsa.gov/ia/_files/nist-routines.pdf */ +static void mod_sqrt_secp224r1_rm(uECC_word_t *d2, + uECC_word_t *e2, + uECC_word_t *f2, + const uECC_word_t *c, + const uECC_word_t *d0, + const uECC_word_t *e0, + const uECC_word_t *d1, + const uECC_word_t *e1) { + uECC_word_t t1[num_words_secp224r1]; + uECC_word_t t2[num_words_secp224r1]; + + uECC_vli_modMult_fast(t1, e0, e1, &curve_secp224r1); /* t1 <-- e0 * e1 */ + uECC_vli_modMult_fast(t1, t1, c, &curve_secp224r1); /* t1 <-- t1 * c */ + /* t1 <-- p - t1 */ + uECC_vli_modSub(t1, curve_secp224r1.p, t1, curve_secp224r1.p, num_words_secp224r1); + uECC_vli_modMult_fast(t2, d0, d1, &curve_secp224r1); /* t2 <-- d0 * d1 */ + uECC_vli_modAdd(t2, t2, t1, curve_secp224r1.p, num_words_secp224r1); /* t2 <-- t2 + t1 */ + uECC_vli_modMult_fast(t1, d0, e1, &curve_secp224r1); /* t1 <-- d0 * e1 */ + uECC_vli_modMult_fast(e2, d1, e0, &curve_secp224r1); /* e2 <-- d1 * e0 */ + uECC_vli_modAdd(e2, e2, t1, curve_secp224r1.p, num_words_secp224r1); /* e2 <-- e2 + t1 */ + uECC_vli_modSquare_fast(f2, e2, &curve_secp224r1); /* f2 <-- e2^2 */ + uECC_vli_modMult_fast(f2, f2, c, &curve_secp224r1); /* f2 <-- f2 * c */ + /* f2 <-- p - f2 */ + uECC_vli_modSub(f2, curve_secp224r1.p, f2, curve_secp224r1.p, num_words_secp224r1); + uECC_vli_set(d2, t2, num_words_secp224r1); /* d2 <-- t2 */ +} + +/* Routine 3.2.7 RP; from http://www.nsa.gov/ia/_files/nist-routines.pdf */ +static void mod_sqrt_secp224r1_rp(uECC_word_t *d1, + uECC_word_t *e1, + uECC_word_t *f1, + const uECC_word_t *c, + const uECC_word_t *r) { + wordcount_t i; + wordcount_t pow2i = 1; + uECC_word_t d0[num_words_secp224r1]; + uECC_word_t e0[num_words_secp224r1] = {1}; /* e0 <-- 1 */ + uECC_word_t f0[num_words_secp224r1]; + + uECC_vli_set(d0, r, num_words_secp224r1); /* d0 <-- r */ + /* f0 <-- p - c */ + uECC_vli_modSub(f0, curve_secp224r1.p, c, curve_secp224r1.p, num_words_secp224r1); + for (i = 0; i <= 6; i++) { + mod_sqrt_secp224r1_rss(d1, e1, f1, d0, e0, f0, pow2i); /* RSS (d1,e1,f1,d0,e0,f0,2^i) */ + mod_sqrt_secp224r1_rm(d1, e1, f1, c, d1, e1, d0, e0); /* RM (d1,e1,f1,c,d1,e1,d0,e0) */ + uECC_vli_set(d0, d1, num_words_secp224r1); /* d0 <-- d1 */ + uECC_vli_set(e0, e1, num_words_secp224r1); /* e0 <-- e1 */ + uECC_vli_set(f0, f1, num_words_secp224r1); /* f0 <-- f1 */ + pow2i *= 2; + } +} + +/* Compute a = sqrt(a) (mod curve_p). */ +/* Routine 3.2.8 mp_mod_sqrt_224; from http://www.nsa.gov/ia/_files/nist-routines.pdf */ +static void mod_sqrt_secp224r1(uECC_word_t *a, uECC_Curve curve) { + bitcount_t i; + uECC_word_t e1[num_words_secp224r1]; + uECC_word_t f1[num_words_secp224r1]; + uECC_word_t d0[num_words_secp224r1]; + uECC_word_t e0[num_words_secp224r1]; + uECC_word_t f0[num_words_secp224r1]; + uECC_word_t d1[num_words_secp224r1]; + + /* s = a; using constant instead of random value */ + mod_sqrt_secp224r1_rp(d0, e0, f0, a, a); /* RP (d0, e0, f0, c, s) */ + mod_sqrt_secp224r1_rs(d1, e1, f1, d0, e0, f0); /* RS (d1, e1, f1, d0, e0, f0) */ + for (i = 1; i <= 95; i++) { + uECC_vli_set(d0, d1, num_words_secp224r1); /* d0 <-- d1 */ + uECC_vli_set(e0, e1, num_words_secp224r1); /* e0 <-- e1 */ + uECC_vli_set(f0, f1, num_words_secp224r1); /* f0 <-- f1 */ + mod_sqrt_secp224r1_rs(d1, e1, f1, d0, e0, f0); /* RS (d1, e1, f1, d0, e0, f0) */ + if (uECC_vli_isZero(d1, num_words_secp224r1)) { /* if d1 == 0 */ + break; + } + } + uECC_vli_modInv(f1, e0, curve_secp224r1.p, num_words_secp224r1); /* f1 <-- 1 / e0 */ + uECC_vli_modMult_fast(a, d0, f1, &curve_secp224r1); /* a <-- d0 / e0 */ +} +#endif /* uECC_SUPPORT_COMPRESSED_POINT */ + +#if (uECC_OPTIMIZATION_LEVEL > 0) +/* Computes result = product % curve_p + from http://www.nsa.gov/ia/_files/nist-routines.pdf */ +#if uECC_WORD_SIZE == 1 +static void vli_mmod_fast_secp224r1(uint8_t *result, uint8_t *product) { + uint8_t tmp[num_words_secp224r1]; + int8_t carry; + + /* t */ + uECC_vli_set(result, product, num_words_secp224r1); + + /* s1 */ + tmp[0] = tmp[1] = tmp[2] = tmp[3] = 0; + tmp[4] = tmp[5] = tmp[6] = tmp[7] = 0; + tmp[8] = tmp[9] = tmp[10] = tmp[11] = 0; + tmp[12] = product[28]; tmp[13] = product[29]; tmp[14] = product[30]; tmp[15] = product[31]; + tmp[16] = product[32]; tmp[17] = product[33]; tmp[18] = product[34]; tmp[19] = product[35]; + tmp[20] = product[36]; tmp[21] = product[37]; tmp[22] = product[38]; tmp[23] = product[39]; + tmp[24] = product[40]; tmp[25] = product[41]; tmp[26] = product[42]; tmp[27] = product[43]; + carry = uECC_vli_add(result, result, tmp, num_words_secp224r1); + + /* s2 */ + tmp[12] = product[44]; tmp[13] = product[45]; tmp[14] = product[46]; tmp[15] = product[47]; + tmp[16] = product[48]; tmp[17] = product[49]; tmp[18] = product[50]; tmp[19] = product[51]; + tmp[20] = product[52]; tmp[21] = product[53]; tmp[22] = product[54]; tmp[23] = product[55]; + tmp[24] = tmp[25] = tmp[26] = tmp[27] = 0; + carry += uECC_vli_add(result, result, tmp, num_words_secp224r1); + + /* d1 */ + tmp[0] = product[28]; tmp[1] = product[29]; tmp[2] = product[30]; tmp[3] = product[31]; + tmp[4] = product[32]; tmp[5] = product[33]; tmp[6] = product[34]; tmp[7] = product[35]; + tmp[8] = product[36]; tmp[9] = product[37]; tmp[10] = product[38]; tmp[11] = product[39]; + tmp[12] = product[40]; tmp[13] = product[41]; tmp[14] = product[42]; tmp[15] = product[43]; + tmp[16] = product[44]; tmp[17] = product[45]; tmp[18] = product[46]; tmp[19] = product[47]; + tmp[20] = product[48]; tmp[21] = product[49]; tmp[22] = product[50]; tmp[23] = product[51]; + tmp[24] = product[52]; tmp[25] = product[53]; tmp[26] = product[54]; tmp[27] = product[55]; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp224r1); + + /* d2 */ + tmp[0] = product[44]; tmp[1] = product[45]; tmp[2] = product[46]; tmp[3] = product[47]; + tmp[4] = product[48]; tmp[5] = product[49]; tmp[6] = product[50]; tmp[7] = product[51]; + tmp[8] = product[52]; tmp[9] = product[53]; tmp[10] = product[54]; tmp[11] = product[55]; + tmp[12] = tmp[13] = tmp[14] = tmp[15] = 0; + tmp[16] = tmp[17] = tmp[18] = tmp[19] = 0; + tmp[20] = tmp[21] = tmp[22] = tmp[23] = 0; + tmp[24] = tmp[25] = tmp[26] = tmp[27] = 0; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp224r1); + + if (carry < 0) { + do { + carry += uECC_vli_add(result, result, curve_secp224r1.p, num_words_secp224r1); + } while (carry < 0); + } else { + while (carry || uECC_vli_cmp_unsafe(curve_secp224r1.p, result, num_words_secp224r1) != 1) { + carry -= uECC_vli_sub(result, result, curve_secp224r1.p, num_words_secp224r1); + } + } +} +#elif uECC_WORD_SIZE == 4 +static void vli_mmod_fast_secp224r1(uint32_t *result, uint32_t *product) +{ + uint32_t tmp[num_words_secp224r1]; + int carry; + + /* t */ + uECC_vli_set(result, product, num_words_secp224r1); + + /* s1 */ + tmp[0] = tmp[1] = tmp[2] = 0; + tmp[3] = product[7]; + tmp[4] = product[8]; + tmp[5] = product[9]; + tmp[6] = product[10]; + carry = uECC_vli_add(result, result, tmp, num_words_secp224r1); + + /* s2 */ + tmp[3] = product[11]; + tmp[4] = product[12]; + tmp[5] = product[13]; + tmp[6] = 0; + carry += uECC_vli_add(result, result, tmp, num_words_secp224r1); + + /* d1 */ + tmp[0] = product[7]; + tmp[1] = product[8]; + tmp[2] = product[9]; + tmp[3] = product[10]; + tmp[4] = product[11]; + tmp[5] = product[12]; + tmp[6] = product[13]; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp224r1); + + /* d2 */ + tmp[0] = product[11]; + tmp[1] = product[12]; + tmp[2] = product[13]; + tmp[3] = tmp[4] = tmp[5] = tmp[6] = 0; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp224r1); + + if (carry < 0) { + do { + carry += uECC_vli_add(result, result, curve_secp224r1.p, num_words_secp224r1); + } while (carry < 0); + } else { + while (carry || uECC_vli_cmp_unsafe(curve_secp224r1.p, result, num_words_secp224r1) != 1) { + carry -= uECC_vli_sub(result, result, curve_secp224r1.p, num_words_secp224r1); + } + } +} +#else +static void vli_mmod_fast_secp224r1(uint64_t *result, uint64_t *product) +{ + uint64_t tmp[num_words_secp224r1]; + int carry = 0; + + /* t */ + uECC_vli_set(result, product, num_words_secp224r1); + result[num_words_secp224r1 - 1] &= 0xffffffff; + + /* s1 */ + tmp[0] = 0; + tmp[1] = product[3] & 0xffffffff00000000ull; + tmp[2] = product[4]; + tmp[3] = product[5] & 0xffffffff; + uECC_vli_add(result, result, tmp, num_words_secp224r1); + + /* s2 */ + tmp[1] = product[5] & 0xffffffff00000000ull; + tmp[2] = product[6]; + tmp[3] = 0; + uECC_vli_add(result, result, tmp, num_words_secp224r1); + + /* d1 */ + tmp[0] = (product[3] >> 32) | (product[4] << 32); + tmp[1] = (product[4] >> 32) | (product[5] << 32); + tmp[2] = (product[5] >> 32) | (product[6] << 32); + tmp[3] = product[6] >> 32; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp224r1); + + /* d2 */ + tmp[0] = (product[5] >> 32) | (product[6] << 32); + tmp[1] = product[6] >> 32; + tmp[2] = tmp[3] = 0; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp224r1); + + if (carry < 0) { + do { + carry += uECC_vli_add(result, result, curve_secp224r1.p, num_words_secp224r1); + } while (carry < 0); + } else { + while (uECC_vli_cmp_unsafe(curve_secp224r1.p, result, num_words_secp224r1) != 1) { + uECC_vli_sub(result, result, curve_secp224r1.p, num_words_secp224r1); + } + } +} +#endif /* uECC_WORD_SIZE */ +#endif /* (uECC_OPTIMIZATION_LEVEL > 0) */ + +#endif /* uECC_SUPPORTS_secp224r1 */ + +#if uECC_SUPPORTS_secp256r1 + +#if (uECC_OPTIMIZATION_LEVEL > 0) +static void vli_mmod_fast_secp256r1(uECC_word_t *result, uECC_word_t *product); +#endif + +static const struct uECC_Curve_t curve_secp256r1 = { + num_words_secp256r1, + num_bytes_secp256r1, + 256, /* num_n_bits */ + { BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), + BYTES_TO_WORDS_8(FF, FF, FF, FF, 00, 00, 00, 00), + BYTES_TO_WORDS_8(00, 00, 00, 00, 00, 00, 00, 00), + BYTES_TO_WORDS_8(01, 00, 00, 00, FF, FF, FF, FF) }, + { BYTES_TO_WORDS_8(51, 25, 63, FC, C2, CA, B9, F3), + BYTES_TO_WORDS_8(84, 9E, 17, A7, AD, FA, E6, BC), + BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), + BYTES_TO_WORDS_8(00, 00, 00, 00, FF, FF, FF, FF) }, + { BYTES_TO_WORDS_8(96, C2, 98, D8, 45, 39, A1, F4), + BYTES_TO_WORDS_8(A0, 33, EB, 2D, 81, 7D, 03, 77), + BYTES_TO_WORDS_8(F2, 40, A4, 63, E5, E6, BC, F8), + BYTES_TO_WORDS_8(47, 42, 2C, E1, F2, D1, 17, 6B), + + BYTES_TO_WORDS_8(F5, 51, BF, 37, 68, 40, B6, CB), + BYTES_TO_WORDS_8(CE, 5E, 31, 6B, 57, 33, CE, 2B), + BYTES_TO_WORDS_8(16, 9E, 0F, 7C, 4A, EB, E7, 8E), + BYTES_TO_WORDS_8(9B, 7F, 1A, FE, E2, 42, E3, 4F) }, + { BYTES_TO_WORDS_8(4B, 60, D2, 27, 3E, 3C, CE, 3B), + BYTES_TO_WORDS_8(F6, B0, 53, CC, B0, 06, 1D, 65), + BYTES_TO_WORDS_8(BC, 86, 98, 76, 55, BD, EB, B3), + BYTES_TO_WORDS_8(E7, 93, 3A, AA, D8, 35, C6, 5A) }, + &double_jacobian_default, +#if uECC_SUPPORT_COMPRESSED_POINT + &mod_sqrt_default, +#endif + &x_side_default, +#if (uECC_OPTIMIZATION_LEVEL > 0) + &vli_mmod_fast_secp256r1 +#endif +}; + +uECC_Curve uECC_secp256r1(void) { return &curve_secp256r1; } + + +#if (uECC_OPTIMIZATION_LEVEL > 0 && !asm_mmod_fast_secp256r1) +/* Computes result = product % curve_p + from http://www.nsa.gov/ia/_files/nist-routines.pdf */ +#if uECC_WORD_SIZE == 1 +static void vli_mmod_fast_secp256r1(uint8_t *result, uint8_t *product) { + uint8_t tmp[num_words_secp256r1]; + int8_t carry; + + /* t */ + uECC_vli_set(result, product, num_words_secp256r1); + + /* s1 */ + tmp[0] = tmp[1] = tmp[2] = tmp[3] = 0; + tmp[4] = tmp[5] = tmp[6] = tmp[7] = 0; + tmp[8] = tmp[9] = tmp[10] = tmp[11] = 0; + tmp[12] = product[44]; tmp[13] = product[45]; tmp[14] = product[46]; tmp[15] = product[47]; + tmp[16] = product[48]; tmp[17] = product[49]; tmp[18] = product[50]; tmp[19] = product[51]; + tmp[20] = product[52]; tmp[21] = product[53]; tmp[22] = product[54]; tmp[23] = product[55]; + tmp[24] = product[56]; tmp[25] = product[57]; tmp[26] = product[58]; tmp[27] = product[59]; + tmp[28] = product[60]; tmp[29] = product[61]; tmp[30] = product[62]; tmp[31] = product[63]; + carry = uECC_vli_add(tmp, tmp, tmp, num_words_secp256r1); + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + + /* s2 */ + tmp[12] = product[48]; tmp[13] = product[49]; tmp[14] = product[50]; tmp[15] = product[51]; + tmp[16] = product[52]; tmp[17] = product[53]; tmp[18] = product[54]; tmp[19] = product[55]; + tmp[20] = product[56]; tmp[21] = product[57]; tmp[22] = product[58]; tmp[23] = product[59]; + tmp[24] = product[60]; tmp[25] = product[61]; tmp[26] = product[62]; tmp[27] = product[63]; + tmp[28] = tmp[29] = tmp[30] = tmp[31] = 0; + carry += uECC_vli_add(tmp, tmp, tmp, num_words_secp256r1); + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + + /* s3 */ + tmp[0] = product[32]; tmp[1] = product[33]; tmp[2] = product[34]; tmp[3] = product[35]; + tmp[4] = product[36]; tmp[5] = product[37]; tmp[6] = product[38]; tmp[7] = product[39]; + tmp[8] = product[40]; tmp[9] = product[41]; tmp[10] = product[42]; tmp[11] = product[43]; + tmp[12] = tmp[13] = tmp[14] = tmp[15] = 0; + tmp[16] = tmp[17] = tmp[18] = tmp[19] = 0; + tmp[20] = tmp[21] = tmp[22] = tmp[23] = 0; + tmp[24] = product[56]; tmp[25] = product[57]; tmp[26] = product[58]; tmp[27] = product[59]; + tmp[28] = product[60]; tmp[29] = product[61]; tmp[30] = product[62]; tmp[31] = product[63]; + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + + /* s4 */ + tmp[0] = product[36]; tmp[1] = product[37]; tmp[2] = product[38]; tmp[3] = product[39]; + tmp[4] = product[40]; tmp[5] = product[41]; tmp[6] = product[42]; tmp[7] = product[43]; + tmp[8] = product[44]; tmp[9] = product[45]; tmp[10] = product[46]; tmp[11] = product[47]; + tmp[12] = product[52]; tmp[13] = product[53]; tmp[14] = product[54]; tmp[15] = product[55]; + tmp[16] = product[56]; tmp[17] = product[57]; tmp[18] = product[58]; tmp[19] = product[59]; + tmp[20] = product[60]; tmp[21] = product[61]; tmp[22] = product[62]; tmp[23] = product[63]; + tmp[24] = product[52]; tmp[25] = product[53]; tmp[26] = product[54]; tmp[27] = product[55]; + tmp[28] = product[32]; tmp[29] = product[33]; tmp[30] = product[34]; tmp[31] = product[35]; + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + + /* d1 */ + tmp[0] = product[44]; tmp[1] = product[45]; tmp[2] = product[46]; tmp[3] = product[47]; + tmp[4] = product[48]; tmp[5] = product[49]; tmp[6] = product[50]; tmp[7] = product[51]; + tmp[8] = product[52]; tmp[9] = product[53]; tmp[10] = product[54]; tmp[11] = product[55]; + tmp[12] = tmp[13] = tmp[14] = tmp[15] = 0; + tmp[16] = tmp[17] = tmp[18] = tmp[19] = 0; + tmp[20] = tmp[21] = tmp[22] = tmp[23] = 0; + tmp[24] = product[32]; tmp[25] = product[33]; tmp[26] = product[34]; tmp[27] = product[35]; + tmp[28] = product[40]; tmp[29] = product[41]; tmp[30] = product[42]; tmp[31] = product[43]; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + + /* d2 */ + tmp[0] = product[48]; tmp[1] = product[49]; tmp[2] = product[50]; tmp[3] = product[51]; + tmp[4] = product[52]; tmp[5] = product[53]; tmp[6] = product[54]; tmp[7] = product[55]; + tmp[8] = product[56]; tmp[9] = product[57]; tmp[10] = product[58]; tmp[11] = product[59]; + tmp[12] = product[60]; tmp[13] = product[61]; tmp[14] = product[62]; tmp[15] = product[63]; + tmp[16] = tmp[17] = tmp[18] = tmp[19] = 0; + tmp[20] = tmp[21] = tmp[22] = tmp[23] = 0; + tmp[24] = product[36]; tmp[25] = product[37]; tmp[26] = product[38]; tmp[27] = product[39]; + tmp[28] = product[44]; tmp[29] = product[45]; tmp[30] = product[46]; tmp[31] = product[47]; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + + /* d3 */ + tmp[0] = product[52]; tmp[1] = product[53]; tmp[2] = product[54]; tmp[3] = product[55]; + tmp[4] = product[56]; tmp[5] = product[57]; tmp[6] = product[58]; tmp[7] = product[59]; + tmp[8] = product[60]; tmp[9] = product[61]; tmp[10] = product[62]; tmp[11] = product[63]; + tmp[12] = product[32]; tmp[13] = product[33]; tmp[14] = product[34]; tmp[15] = product[35]; + tmp[16] = product[36]; tmp[17] = product[37]; tmp[18] = product[38]; tmp[19] = product[39]; + tmp[20] = product[40]; tmp[21] = product[41]; tmp[22] = product[42]; tmp[23] = product[43]; + tmp[24] = tmp[25] = tmp[26] = tmp[27] = 0; + tmp[28] = product[48]; tmp[29] = product[49]; tmp[30] = product[50]; tmp[31] = product[51]; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + + /* d4 */ + tmp[0] = product[56]; tmp[1] = product[57]; tmp[2] = product[58]; tmp[3] = product[59]; + tmp[4] = product[60]; tmp[5] = product[61]; tmp[6] = product[62]; tmp[7] = product[63]; + tmp[8] = tmp[9] = tmp[10] = tmp[11] = 0; + tmp[12] = product[36]; tmp[13] = product[37]; tmp[14] = product[38]; tmp[15] = product[39]; + tmp[16] = product[40]; tmp[17] = product[41]; tmp[18] = product[42]; tmp[19] = product[43]; + tmp[20] = product[44]; tmp[21] = product[45]; tmp[22] = product[46]; tmp[23] = product[47]; + tmp[24] = tmp[25] = tmp[26] = tmp[27] = 0; + tmp[28] = product[52]; tmp[29] = product[53]; tmp[30] = product[54]; tmp[31] = product[55]; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + + if (carry < 0) { + do { + carry += uECC_vli_add(result, result, curve_secp256r1.p, num_words_secp256r1); + } while (carry < 0); + } else { + while (carry || uECC_vli_cmp_unsafe(curve_secp256r1.p, result, num_words_secp256r1) != 1) { + carry -= uECC_vli_sub(result, result, curve_secp256r1.p, num_words_secp256r1); + } + } +} +#elif uECC_WORD_SIZE == 4 +static void vli_mmod_fast_secp256r1(uint32_t *result, uint32_t *product) { + uint32_t tmp[num_words_secp256r1]; + int carry; + + /* t */ + uECC_vli_set(result, product, num_words_secp256r1); + + /* s1 */ + tmp[0] = tmp[1] = tmp[2] = 0; + tmp[3] = product[11]; + tmp[4] = product[12]; + tmp[5] = product[13]; + tmp[6] = product[14]; + tmp[7] = product[15]; + carry = uECC_vli_add(tmp, tmp, tmp, num_words_secp256r1); + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + + /* s2 */ + tmp[3] = product[12]; + tmp[4] = product[13]; + tmp[5] = product[14]; + tmp[6] = product[15]; + tmp[7] = 0; + carry += uECC_vli_add(tmp, tmp, tmp, num_words_secp256r1); + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + + /* s3 */ + tmp[0] = product[8]; + tmp[1] = product[9]; + tmp[2] = product[10]; + tmp[3] = tmp[4] = tmp[5] = 0; + tmp[6] = product[14]; + tmp[7] = product[15]; + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + + /* s4 */ + tmp[0] = product[9]; + tmp[1] = product[10]; + tmp[2] = product[11]; + tmp[3] = product[13]; + tmp[4] = product[14]; + tmp[5] = product[15]; + tmp[6] = product[13]; + tmp[7] = product[8]; + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + + /* d1 */ + tmp[0] = product[11]; + tmp[1] = product[12]; + tmp[2] = product[13]; + tmp[3] = tmp[4] = tmp[5] = 0; + tmp[6] = product[8]; + tmp[7] = product[10]; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + + /* d2 */ + tmp[0] = product[12]; + tmp[1] = product[13]; + tmp[2] = product[14]; + tmp[3] = product[15]; + tmp[4] = tmp[5] = 0; + tmp[6] = product[9]; + tmp[7] = product[11]; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + + /* d3 */ + tmp[0] = product[13]; + tmp[1] = product[14]; + tmp[2] = product[15]; + tmp[3] = product[8]; + tmp[4] = product[9]; + tmp[5] = product[10]; + tmp[6] = 0; + tmp[7] = product[12]; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + + /* d4 */ + tmp[0] = product[14]; + tmp[1] = product[15]; + tmp[2] = 0; + tmp[3] = product[9]; + tmp[4] = product[10]; + tmp[5] = product[11]; + tmp[6] = 0; + tmp[7] = product[13]; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + + if (carry < 0) { + do { + carry += uECC_vli_add(result, result, curve_secp256r1.p, num_words_secp256r1); + } while (carry < 0); + } else { + while (carry || uECC_vli_cmp_unsafe(curve_secp256r1.p, result, num_words_secp256r1) != 1) { + carry -= uECC_vli_sub(result, result, curve_secp256r1.p, num_words_secp256r1); + } + } +} +#else +static void vli_mmod_fast_secp256r1(uint64_t *result, uint64_t *product) { + uint64_t tmp[num_words_secp256r1]; + int carry; + + /* t */ + uECC_vli_set(result, product, num_words_secp256r1); + + /* s1 */ + tmp[0] = 0; + tmp[1] = product[5] & 0xffffffff00000000ull; + tmp[2] = product[6]; + tmp[3] = product[7]; + carry = (int)uECC_vli_add(tmp, tmp, tmp, num_words_secp256r1); + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + + /* s2 */ + tmp[1] = product[6] << 32; + tmp[2] = (product[6] >> 32) | (product[7] << 32); + tmp[3] = product[7] >> 32; + carry += uECC_vli_add(tmp, tmp, tmp, num_words_secp256r1); + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + + /* s3 */ + tmp[0] = product[4]; + tmp[1] = product[5] & 0xffffffff; + tmp[2] = 0; + tmp[3] = product[7]; + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + + /* s4 */ + tmp[0] = (product[4] >> 32) | (product[5] << 32); + tmp[1] = (product[5] >> 32) | (product[6] & 0xffffffff00000000ull); + tmp[2] = product[7]; + tmp[3] = (product[6] >> 32) | (product[4] << 32); + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + + /* d1 */ + tmp[0] = (product[5] >> 32) | (product[6] << 32); + tmp[1] = (product[6] >> 32); + tmp[2] = 0; + tmp[3] = (product[4] & 0xffffffff) | (product[5] << 32); + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + + /* d2 */ + tmp[0] = product[6]; + tmp[1] = product[7]; + tmp[2] = 0; + tmp[3] = (product[4] >> 32) | (product[5] & 0xffffffff00000000ull); + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + + /* d3 */ + tmp[0] = (product[6] >> 32) | (product[7] << 32); + tmp[1] = (product[7] >> 32) | (product[4] << 32); + tmp[2] = (product[4] >> 32) | (product[5] << 32); + tmp[3] = (product[6] << 32); + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + + /* d4 */ + tmp[0] = product[7]; + tmp[1] = product[4] & 0xffffffff00000000ull; + tmp[2] = product[5]; + tmp[3] = product[6] & 0xffffffff00000000ull; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + + if (carry < 0) { + do { + carry += uECC_vli_add(result, result, curve_secp256r1.p, num_words_secp256r1); + } while (carry < 0); + } else { + while (carry || uECC_vli_cmp_unsafe(curve_secp256r1.p, result, num_words_secp256r1) != 1) { + carry -= uECC_vli_sub(result, result, curve_secp256r1.p, num_words_secp256r1); + } + } +} +#endif /* uECC_WORD_SIZE */ +#endif /* (uECC_OPTIMIZATION_LEVEL > 0 && !asm_mmod_fast_secp256r1) */ + +#endif /* uECC_SUPPORTS_secp256r1 */ + +#if uECC_SUPPORTS_secp256k1 + +static void double_jacobian_secp256k1(uECC_word_t * X1, + uECC_word_t * Y1, + uECC_word_t * Z1, + uECC_Curve curve); +static void x_side_secp256k1(uECC_word_t *result, const uECC_word_t *x, uECC_Curve curve); +#if (uECC_OPTIMIZATION_LEVEL > 0) +static void vli_mmod_fast_secp256k1(uECC_word_t *result, uECC_word_t *product); +#endif + +static const struct uECC_Curve_t curve_secp256k1 = { + num_words_secp256k1, + num_bytes_secp256k1, + 256, /* num_n_bits */ + { BYTES_TO_WORDS_8(2F, FC, FF, FF, FE, FF, FF, FF), + BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), + BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), + BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF) }, + { BYTES_TO_WORDS_8(41, 41, 36, D0, 8C, 5E, D2, BF), + BYTES_TO_WORDS_8(3B, A0, 48, AF, E6, DC, AE, BA), + BYTES_TO_WORDS_8(FE, FF, FF, FF, FF, FF, FF, FF), + BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF) }, + { BYTES_TO_WORDS_8(98, 17, F8, 16, 5B, 81, F2, 59), + BYTES_TO_WORDS_8(D9, 28, CE, 2D, DB, FC, 9B, 02), + BYTES_TO_WORDS_8(07, 0B, 87, CE, 95, 62, A0, 55), + BYTES_TO_WORDS_8(AC, BB, DC, F9, 7E, 66, BE, 79), + + BYTES_TO_WORDS_8(B8, D4, 10, FB, 8F, D0, 47, 9C), + BYTES_TO_WORDS_8(19, 54, 85, A6, 48, B4, 17, FD), + BYTES_TO_WORDS_8(A8, 08, 11, 0E, FC, FB, A4, 5D), + BYTES_TO_WORDS_8(65, C4, A3, 26, 77, DA, 3A, 48) }, + { BYTES_TO_WORDS_8(07, 00, 00, 00, 00, 00, 00, 00), + BYTES_TO_WORDS_8(00, 00, 00, 00, 00, 00, 00, 00), + BYTES_TO_WORDS_8(00, 00, 00, 00, 00, 00, 00, 00), + BYTES_TO_WORDS_8(00, 00, 00, 00, 00, 00, 00, 00) }, + &double_jacobian_secp256k1, +#if uECC_SUPPORT_COMPRESSED_POINT + &mod_sqrt_default, +#endif + &x_side_secp256k1, +#if (uECC_OPTIMIZATION_LEVEL > 0) + &vli_mmod_fast_secp256k1 +#endif +}; + +uECC_Curve uECC_secp256k1(void) { return &curve_secp256k1; } + + +/* Double in place */ +static void double_jacobian_secp256k1(uECC_word_t * X1, + uECC_word_t * Y1, + uECC_word_t * Z1, + uECC_Curve curve) { + /* t1 = X, t2 = Y, t3 = Z */ + uECC_word_t t4[num_words_secp256k1]; + uECC_word_t t5[num_words_secp256k1]; + + if (uECC_vli_isZero(Z1, num_words_secp256k1)) { + return; + } + + uECC_vli_modSquare_fast(t5, Y1, curve); /* t5 = y1^2 */ + uECC_vli_modMult_fast(t4, X1, t5, curve); /* t4 = x1*y1^2 = A */ + uECC_vli_modSquare_fast(X1, X1, curve); /* t1 = x1^2 */ + uECC_vli_modSquare_fast(t5, t5, curve); /* t5 = y1^4 */ + uECC_vli_modMult_fast(Z1, Y1, Z1, curve); /* t3 = y1*z1 = z3 */ + + uECC_vli_modAdd(Y1, X1, X1, curve->p, num_words_secp256k1); /* t2 = 2*x1^2 */ + uECC_vli_modAdd(Y1, Y1, X1, curve->p, num_words_secp256k1); /* t2 = 3*x1^2 */ + if (uECC_vli_testBit(Y1, 0)) { + uECC_word_t carry = uECC_vli_add(Y1, Y1, curve->p, num_words_secp256k1); + uECC_vli_rshift1(Y1, num_words_secp256k1); + Y1[num_words_secp256k1 - 1] |= carry << (uECC_WORD_BITS - 1); + } else { + uECC_vli_rshift1(Y1, num_words_secp256k1); + } + /* t2 = 3/2*(x1^2) = B */ + + uECC_vli_modSquare_fast(X1, Y1, curve); /* t1 = B^2 */ + uECC_vli_modSub(X1, X1, t4, curve->p, num_words_secp256k1); /* t1 = B^2 - A */ + uECC_vli_modSub(X1, X1, t4, curve->p, num_words_secp256k1); /* t1 = B^2 - 2A = x3 */ + + uECC_vli_modSub(t4, t4, X1, curve->p, num_words_secp256k1); /* t4 = A - x3 */ + uECC_vli_modMult_fast(Y1, Y1, t4, curve); /* t2 = B * (A - x3) */ + uECC_vli_modSub(Y1, Y1, t5, curve->p, num_words_secp256k1); /* t2 = B * (A - x3) - y1^4 = y3 */ +} + +/* Computes result = x^3 + b. result must not overlap x. */ +static void x_side_secp256k1(uECC_word_t *result, const uECC_word_t *x, uECC_Curve curve) { + uECC_vli_modSquare_fast(result, x, curve); /* r = x^2 */ + uECC_vli_modMult_fast(result, result, x, curve); /* r = x^3 */ + uECC_vli_modAdd(result, result, curve->b, curve->p, num_words_secp256k1); /* r = x^3 + b */ +} + +#if (uECC_OPTIMIZATION_LEVEL > 0 && !asm_mmod_fast_secp256k1) +static void omega_mult_secp256k1(uECC_word_t *result, const uECC_word_t *right); +static void vli_mmod_fast_secp256k1(uECC_word_t *result, uECC_word_t *product) { + uECC_word_t tmp[2 * num_words_secp256k1]; + uECC_word_t carry; + + uECC_vli_clear(tmp, num_words_secp256k1); + uECC_vli_clear(tmp + num_words_secp256k1, num_words_secp256k1); + + omega_mult_secp256k1(tmp, product + num_words_secp256k1); /* (Rq, q) = q * c */ + + carry = uECC_vli_add(result, product, tmp, num_words_secp256k1); /* (C, r) = r + q */ + uECC_vli_clear(product, num_words_secp256k1); + omega_mult_secp256k1(product, tmp + num_words_secp256k1); /* Rq*c */ + carry += uECC_vli_add(result, result, product, num_words_secp256k1); /* (C1, r) = r + Rq*c */ + + while (carry > 0) { + --carry; + uECC_vli_sub(result, result, curve_secp256k1.p, num_words_secp256k1); + } + if (uECC_vli_cmp_unsafe(result, curve_secp256k1.p, num_words_secp256k1) > 0) { + uECC_vli_sub(result, result, curve_secp256k1.p, num_words_secp256k1); + } +} + +#if uECC_WORD_SIZE == 1 +static void omega_mult_secp256k1(uint8_t * result, const uint8_t * right) { + /* Multiply by (2^32 + 2^9 + 2^8 + 2^7 + 2^6 + 2^4 + 1). */ + uECC_word_t r0 = 0; + uECC_word_t r1 = 0; + uECC_word_t r2 = 0; + wordcount_t k; + + /* Multiply by (2^9 + 2^8 + 2^7 + 2^6 + 2^4 + 1). */ + muladd(0xD1, right[0], &r0, &r1, &r2); + result[0] = r0; + r0 = r1; + r1 = r2; + /* r2 is still 0 */ + + for (k = 1; k < num_words_secp256k1; ++k) { + muladd(0x03, right[k - 1], &r0, &r1, &r2); + muladd(0xD1, right[k], &r0, &r1, &r2); + result[k] = r0; + r0 = r1; + r1 = r2; + r2 = 0; + } + muladd(0x03, right[num_words_secp256k1 - 1], &r0, &r1, &r2); + result[num_words_secp256k1] = r0; + result[num_words_secp256k1 + 1] = r1; + /* add the 2^32 multiple */ + result[4 + num_words_secp256k1] = + uECC_vli_add(result + 4, result + 4, right, num_words_secp256k1); +} +#elif uECC_WORD_SIZE == 4 +static void omega_mult_secp256k1(uint32_t * result, const uint32_t * right) { + /* Multiply by (2^9 + 2^8 + 2^7 + 2^6 + 2^4 + 1). */ + uint32_t carry = 0; + wordcount_t k; + + for (k = 0; k < num_words_secp256k1; ++k) { + uint64_t p = (uint64_t)0x3D1 * right[k] + carry; + result[k] = (uint32_t) p; + carry = p >> 32; + } + result[num_words_secp256k1] = carry; + /* add the 2^32 multiple */ + result[1 + num_words_secp256k1] = + uECC_vli_add(result + 1, result + 1, right, num_words_secp256k1); +} +#else +static void omega_mult_secp256k1(uint64_t * result, const uint64_t * right) { + uECC_word_t r0 = 0; + uECC_word_t r1 = 0; + uECC_word_t r2 = 0; + wordcount_t k; + + /* Multiply by (2^32 + 2^9 + 2^8 + 2^7 + 2^6 + 2^4 + 1). */ + for (k = 0; k < num_words_secp256k1; ++k) { + muladd(0x1000003D1ull, right[k], &r0, &r1, &r2); + result[k] = r0; + r0 = r1; + r1 = r2; + r2 = 0; + } + result[num_words_secp256k1] = r0; +} +#endif /* uECC_WORD_SIZE */ +#endif /* (uECC_OPTIMIZATION_LEVEL > 0 && && !asm_mmod_fast_secp256k1) */ + +#endif /* uECC_SUPPORTS_secp256k1 */ + +#endif /* _UECC_CURVE_SPECIFIC_H_ */ diff --git a/SDK/12.3.0_d7731ad/external/micro-ecc/micro-ecc/platform-specific.inc b/SDK/12.3.0_d7731ad/external/micro-ecc/micro-ecc/platform-specific.inc new file mode 100644 index 0000000..7e0373f --- /dev/null +++ b/SDK/12.3.0_d7731ad/external/micro-ecc/micro-ecc/platform-specific.inc @@ -0,0 +1,94 @@ +/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ + +#ifndef _UECC_PLATFORM_SPECIFIC_H_ +#define _UECC_PLATFORM_SPECIFIC_H_ + +#include "types.h" + +#if (defined(_WIN32) || defined(_WIN64)) +/* Windows */ + +// use pragma syntax to prevent tweaking the linker script for getting CryptXYZ function +#pragma comment(lib, "crypt32.lib") +#pragma comment(lib, "advapi32.lib") + +#define WIN32_LEAN_AND_MEAN +#include +#include + +static int default_RNG(uint8_t *dest, unsigned size) { + HCRYPTPROV prov; + if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { + return 0; + } + + CryptGenRandom(prov, size, (BYTE *)dest); + CryptReleaseContext(prov, 0); + return 1; +} +#define default_RNG_defined 1 + +#elif defined(unix) || defined(__linux__) || defined(__unix__) || defined(__unix) || \ + (defined(__APPLE__) && defined(__MACH__)) || defined(uECC_POSIX) + +/* Some POSIX-like system with /dev/urandom or /dev/random. */ +#include +#include +#include + +#ifndef O_CLOEXEC + #define O_CLOEXEC 0 +#endif + +static int default_RNG(uint8_t *dest, unsigned size) { + int fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC); + if (fd == -1) { + fd = open("/dev/random", O_RDONLY | O_CLOEXEC); + if (fd == -1) { + return 0; + } + } + + char *ptr = (char *)dest; + size_t left = size; + while (left > 0) { + ssize_t bytes_read = read(fd, ptr, left); + if (bytes_read <= 0) { // read failed + close(fd); + return 0; + } + left -= bytes_read; + ptr += bytes_read; + } + + close(fd); + return 1; +} +#define default_RNG_defined 1 + +#elif defined(RIOT_VERSION) + +#include + +static int default_RNG(uint8_t *dest, unsigned size) { + random_bytes(dest, size); + return 1; +} +#define default_RNG_defined 1 + +#elif defined(NRF52_SERIES) + +#include "app_error.h" +#include "nrf_crypto_rng.h" + +static int default_RNG(uint8_t *dest, unsigned size) +{ + // make sure to call nrf_crypto_init and nrf_crypto_rng_init first + ret_code_t ret_code = nrf_crypto_rng_vector_generate(dest, size); + return (ret_code == NRF_SUCCESS) ? 1 : 0; +} +#define default_RNG_defined 1 + +#endif /* platform */ + +#endif /* _UECC_PLATFORM_SPECIFIC_H_ */ diff --git a/SDK/12.3.0_d7731ad/external/micro-ecc/micro-ecc/types.h b/SDK/12.3.0_d7731ad/external/micro-ecc/micro-ecc/types.h new file mode 100644 index 0000000..9ee8143 --- /dev/null +++ b/SDK/12.3.0_d7731ad/external/micro-ecc/micro-ecc/types.h @@ -0,0 +1,108 @@ +/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ + +#ifndef _UECC_TYPES_H_ +#define _UECC_TYPES_H_ + +#ifndef uECC_PLATFORM + #if __AVR__ + #define uECC_PLATFORM uECC_avr + #elif defined(__thumb2__) || defined(_M_ARMT) /* I think MSVC only supports Thumb-2 targets */ + #define uECC_PLATFORM uECC_arm_thumb2 + #elif defined(__thumb__) + #define uECC_PLATFORM uECC_arm_thumb + #elif defined(__arm__) || defined(_M_ARM) + #define uECC_PLATFORM uECC_arm + #elif defined(__aarch64__) + #define uECC_PLATFORM uECC_arm64 + #elif defined(__i386__) || defined(_M_IX86) || defined(_X86_) || defined(__I86__) + #define uECC_PLATFORM uECC_x86 + #elif defined(__amd64__) || defined(_M_X64) + #define uECC_PLATFORM uECC_x86_64 + #else + #define uECC_PLATFORM uECC_arch_other + #endif +#endif + +#ifndef uECC_ARM_USE_UMAAL + #if (uECC_PLATFORM == uECC_arm) && (__ARM_ARCH >= 6) + #define uECC_ARM_USE_UMAAL 1 + #elif (uECC_PLATFORM == uECC_arm_thumb2) && (__ARM_ARCH >= 6) && !__ARM_ARCH_7M__ + #define uECC_ARM_USE_UMAAL 1 + #else + #define uECC_ARM_USE_UMAAL 0 + #endif +#endif + +#ifndef uECC_WORD_SIZE + #if uECC_PLATFORM == uECC_avr + #define uECC_WORD_SIZE 1 + #elif (uECC_PLATFORM == uECC_x86_64 || uECC_PLATFORM == uECC_arm64) + #define uECC_WORD_SIZE 8 + #else + #define uECC_WORD_SIZE 4 + #endif +#endif + +#if (uECC_WORD_SIZE != 1) && (uECC_WORD_SIZE != 4) && (uECC_WORD_SIZE != 8) + #error "Unsupported value for uECC_WORD_SIZE" +#endif + +#if ((uECC_PLATFORM == uECC_avr) && (uECC_WORD_SIZE != 1)) + #pragma message ("uECC_WORD_SIZE must be 1 for AVR") + #undef uECC_WORD_SIZE + #define uECC_WORD_SIZE 1 +#endif + +#if ((uECC_PLATFORM == uECC_arm || uECC_PLATFORM == uECC_arm_thumb || \ + uECC_PLATFORM == uECC_arm_thumb2) && \ + (uECC_WORD_SIZE != 4)) + #pragma message ("uECC_WORD_SIZE must be 4 for ARM") + #undef uECC_WORD_SIZE + #define uECC_WORD_SIZE 4 +#endif + +#if defined(__SIZEOF_INT128__) || ((__clang_major__ * 100 + __clang_minor__) >= 302) + #define SUPPORTS_INT128 1 +#else + #define SUPPORTS_INT128 0 +#endif + +typedef int8_t wordcount_t; +typedef int16_t bitcount_t; +typedef int8_t cmpresult_t; + +#if (uECC_WORD_SIZE == 1) + +typedef uint8_t uECC_word_t; +typedef uint16_t uECC_dword_t; + +#define HIGH_BIT_SET 0x80 +#define uECC_WORD_BITS 8 +#define uECC_WORD_BITS_SHIFT 3 +#define uECC_WORD_BITS_MASK 0x07 + +#elif (uECC_WORD_SIZE == 4) + +typedef uint32_t uECC_word_t; +typedef uint64_t uECC_dword_t; + +#define HIGH_BIT_SET 0x80000000 +#define uECC_WORD_BITS 32 +#define uECC_WORD_BITS_SHIFT 5 +#define uECC_WORD_BITS_MASK 0x01F + +#elif (uECC_WORD_SIZE == 8) + +typedef uint64_t uECC_word_t; +#if SUPPORTS_INT128 +typedef unsigned __int128 uECC_dword_t; +#endif + +#define HIGH_BIT_SET 0x8000000000000000ull +#define uECC_WORD_BITS 64 +#define uECC_WORD_BITS_SHIFT 6 +#define uECC_WORD_BITS_MASK 0x03F + +#endif /* uECC_WORD_SIZE */ + +#endif /* _UECC_TYPES_H_ */ diff --git a/SDK/12.3.0_d7731ad/external/micro-ecc/micro-ecc/uECC.c b/SDK/12.3.0_d7731ad/external/micro-ecc/micro-ecc/uECC.c new file mode 100644 index 0000000..1be5ffd --- /dev/null +++ b/SDK/12.3.0_d7731ad/external/micro-ecc/micro-ecc/uECC.c @@ -0,0 +1,1673 @@ +/* Copyright 2014, Kenneth MacKay. Licensed under the BSD 2-clause license. */ + +#include "uECC.h" +#include "uECC_vli.h" + +#ifndef uECC_RNG_MAX_TRIES + #define uECC_RNG_MAX_TRIES 64 +#endif + +#if uECC_ENABLE_VLI_API + #define uECC_VLI_API +#else + #define uECC_VLI_API static +#endif + +#if (uECC_PLATFORM == uECC_avr) || \ + (uECC_PLATFORM == uECC_arm) || \ + (uECC_PLATFORM == uECC_arm_thumb) || \ + (uECC_PLATFORM == uECC_arm_thumb2) + #define CONCATX(a, ...) a ## __VA_ARGS__ + #define CONCAT(a, ...) CONCATX(a, __VA_ARGS__) + + #define STRX(a) #a + #define STR(a) STRX(a) + + #define EVAL(...) EVAL1(EVAL1(EVAL1(EVAL1(__VA_ARGS__)))) + #define EVAL1(...) EVAL2(EVAL2(EVAL2(EVAL2(__VA_ARGS__)))) + #define EVAL2(...) EVAL3(EVAL3(EVAL3(EVAL3(__VA_ARGS__)))) + #define EVAL3(...) EVAL4(EVAL4(EVAL4(EVAL4(__VA_ARGS__)))) + #define EVAL4(...) __VA_ARGS__ + + #define DEC_1 0 + #define DEC_2 1 + #define DEC_3 2 + #define DEC_4 3 + #define DEC_5 4 + #define DEC_6 5 + #define DEC_7 6 + #define DEC_8 7 + #define DEC_9 8 + #define DEC_10 9 + #define DEC_11 10 + #define DEC_12 11 + #define DEC_13 12 + #define DEC_14 13 + #define DEC_15 14 + #define DEC_16 15 + #define DEC_17 16 + #define DEC_18 17 + #define DEC_19 18 + #define DEC_20 19 + #define DEC_21 20 + #define DEC_22 21 + #define DEC_23 22 + #define DEC_24 23 + #define DEC_25 24 + #define DEC_26 25 + #define DEC_27 26 + #define DEC_28 27 + #define DEC_29 28 + #define DEC_30 29 + #define DEC_31 30 + #define DEC_32 31 + + #define DEC(N) CONCAT(DEC_, N) + + #define SECOND_ARG(_, val, ...) val + #define SOME_CHECK_0 ~, 0 + #define GET_SECOND_ARG(...) SECOND_ARG(__VA_ARGS__, SOME,) + #define SOME_OR_0(N) GET_SECOND_ARG(CONCAT(SOME_CHECK_, N)) + + #define EMPTY(...) + #define DEFER(...) __VA_ARGS__ EMPTY() + + #define REPEAT_NAME_0() REPEAT_0 + #define REPEAT_NAME_SOME() REPEAT_SOME + #define REPEAT_0(...) + #define REPEAT_SOME(N, stuff) DEFER(CONCAT(REPEAT_NAME_, SOME_OR_0(DEC(N))))()(DEC(N), stuff) stuff + #define REPEAT(N, stuff) EVAL(REPEAT_SOME(N, stuff)) + + #define REPEATM_NAME_0() REPEATM_0 + #define REPEATM_NAME_SOME() REPEATM_SOME + #define REPEATM_0(...) + #define REPEATM_SOME(N, macro) macro(N) \ + DEFER(CONCAT(REPEATM_NAME_, SOME_OR_0(DEC(N))))()(DEC(N), macro) + #define REPEATM(N, macro) EVAL(REPEATM_SOME(N, macro)) +#endif + +#include "platform-specific.inc" + +#if (uECC_WORD_SIZE == 1) + #if uECC_SUPPORTS_secp160r1 + #define uECC_MAX_WORDS 21 /* Due to the size of curve_n. */ + #endif + #if uECC_SUPPORTS_secp192r1 + #undef uECC_MAX_WORDS + #define uECC_MAX_WORDS 24 + #endif + #if uECC_SUPPORTS_secp224r1 + #undef uECC_MAX_WORDS + #define uECC_MAX_WORDS 28 + #endif + #if (uECC_SUPPORTS_secp256r1 || uECC_SUPPORTS_secp256k1) + #undef uECC_MAX_WORDS + #define uECC_MAX_WORDS 32 + #endif +#elif (uECC_WORD_SIZE == 4) + #if uECC_SUPPORTS_secp160r1 + #define uECC_MAX_WORDS 6 /* Due to the size of curve_n. */ + #endif + #if uECC_SUPPORTS_secp192r1 + #undef uECC_MAX_WORDS + #define uECC_MAX_WORDS 6 + #endif + #if uECC_SUPPORTS_secp224r1 + #undef uECC_MAX_WORDS + #define uECC_MAX_WORDS 7 + #endif + #if (uECC_SUPPORTS_secp256r1 || uECC_SUPPORTS_secp256k1) + #undef uECC_MAX_WORDS + #define uECC_MAX_WORDS 8 + #endif +#elif (uECC_WORD_SIZE == 8) + #if uECC_SUPPORTS_secp160r1 + #define uECC_MAX_WORDS 3 + #endif + #if uECC_SUPPORTS_secp192r1 + #undef uECC_MAX_WORDS + #define uECC_MAX_WORDS 3 + #endif + #if uECC_SUPPORTS_secp224r1 + #undef uECC_MAX_WORDS + #define uECC_MAX_WORDS 4 + #endif + #if (uECC_SUPPORTS_secp256r1 || uECC_SUPPORTS_secp256k1) + #undef uECC_MAX_WORDS + #define uECC_MAX_WORDS 4 + #endif +#endif /* uECC_WORD_SIZE */ + +#define BITS_TO_WORDS(num_bits) ((num_bits + ((uECC_WORD_SIZE * 8) - 1)) / (uECC_WORD_SIZE * 8)) +#define BITS_TO_BYTES(num_bits) ((num_bits + 7) / 8) + +struct uECC_Curve_t { + wordcount_t num_words; + wordcount_t num_bytes; + bitcount_t num_n_bits; + uECC_word_t p[uECC_MAX_WORDS]; + uECC_word_t n[uECC_MAX_WORDS]; + uECC_word_t G[uECC_MAX_WORDS * 2]; + uECC_word_t b[uECC_MAX_WORDS]; + void (*double_jacobian)(uECC_word_t * X1, + uECC_word_t * Y1, + uECC_word_t * Z1, + uECC_Curve curve); +#if uECC_SUPPORT_COMPRESSED_POINT + void (*mod_sqrt)(uECC_word_t *a, uECC_Curve curve); +#endif + void (*x_side)(uECC_word_t *result, const uECC_word_t *x, uECC_Curve curve); +#if (uECC_OPTIMIZATION_LEVEL > 0) + void (*mmod_fast)(uECC_word_t *result, uECC_word_t *product); +#endif +}; + +#if uECC_VLI_NATIVE_LITTLE_ENDIAN +static void bcopy(uint8_t *dst, + const uint8_t *src, + unsigned num_bytes) { + while (0 != num_bytes) { + num_bytes--; + dst[num_bytes] = src[num_bytes]; + } +} +#endif + +static cmpresult_t uECC_vli_cmp_unsafe(const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words); + +#if (uECC_PLATFORM == uECC_arm || uECC_PLATFORM == uECC_arm_thumb || \ + uECC_PLATFORM == uECC_arm_thumb2) + #include "asm_arm.inc" +#endif + +#if (uECC_PLATFORM == uECC_avr) + #include "asm_avr.inc" +#endif + +#if default_RNG_defined +static uECC_RNG_Function g_rng_function = &default_RNG; +#else +static uECC_RNG_Function g_rng_function = 0; +#endif + +void uECC_set_rng(uECC_RNG_Function rng_function) { + g_rng_function = rng_function; +} + +uECC_RNG_Function uECC_get_rng(void) { + return g_rng_function; +} + +int uECC_curve_private_key_size(uECC_Curve curve) { + return BITS_TO_BYTES(curve->num_n_bits); +} + +int uECC_curve_public_key_size(uECC_Curve curve) { + return 2 * curve->num_bytes; +} + +#if !asm_clear +uECC_VLI_API void uECC_vli_clear(uECC_word_t *vli, wordcount_t num_words) { + wordcount_t i; + for (i = 0; i < num_words; ++i) { + vli[i] = 0; + } +} +#endif /* !asm_clear */ + +/* Constant-time comparison to zero - secure way to compare long integers */ +/* Returns 1 if vli == 0, 0 otherwise. */ +uECC_VLI_API uECC_word_t uECC_vli_isZero(const uECC_word_t *vli, wordcount_t num_words) { + uECC_word_t bits = 0; + wordcount_t i; + for (i = 0; i < num_words; ++i) { + bits |= vli[i]; + } + return (bits == 0); +} + +/* Returns nonzero if bit 'bit' of vli is set. */ +uECC_VLI_API uECC_word_t uECC_vli_testBit(const uECC_word_t *vli, bitcount_t bit) { + return (vli[bit >> uECC_WORD_BITS_SHIFT] & ((uECC_word_t)1 << (bit & uECC_WORD_BITS_MASK))); +} + +/* Counts the number of words in vli. */ +static wordcount_t vli_numDigits(const uECC_word_t *vli, const wordcount_t max_words) { + wordcount_t i; + /* Search from the end until we find a non-zero digit. + We do it in reverse because we expect that most digits will be nonzero. */ + for (i = max_words - 1; i >= 0 && vli[i] == 0; --i) { + } + + return (i + 1); +} + +/* Counts the number of bits required to represent vli. */ +uECC_VLI_API bitcount_t uECC_vli_numBits(const uECC_word_t *vli, const wordcount_t max_words) { + uECC_word_t i; + uECC_word_t digit; + + wordcount_t num_digits = vli_numDigits(vli, max_words); + if (num_digits == 0) { + return 0; + } + + digit = vli[num_digits - 1]; + for (i = 0; digit; ++i) { + digit >>= 1; + } + + return (((bitcount_t)(num_digits - 1) << uECC_WORD_BITS_SHIFT) + i); +} + +/* Sets dest = src. */ +#if !asm_set +uECC_VLI_API void uECC_vli_set(uECC_word_t *dest, const uECC_word_t *src, wordcount_t num_words) { + wordcount_t i; + for (i = 0; i < num_words; ++i) { + dest[i] = src[i]; + } +} +#endif /* !asm_set */ + +/* Returns sign of left - right. */ +static cmpresult_t uECC_vli_cmp_unsafe(const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { + wordcount_t i; + for (i = num_words - 1; i >= 0; --i) { + if (left[i] > right[i]) { + return 1; + } else if (left[i] < right[i]) { + return -1; + } + } + return 0; +} + +/* Constant-time comparison function - secure way to compare long integers */ +/* Returns one if left == right, zero otherwise. */ +uECC_VLI_API uECC_word_t uECC_vli_equal(const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { + uECC_word_t diff = 0; + wordcount_t i; + for (i = num_words - 1; i >= 0; --i) { + diff |= (left[i] ^ right[i]); + } + return (diff == 0); +} + +uECC_VLI_API uECC_word_t uECC_vli_sub(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words); + +/* Returns sign of left - right, in constant time. */ +uECC_VLI_API cmpresult_t uECC_vli_cmp(const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { + uECC_word_t tmp[uECC_MAX_WORDS]; + uECC_word_t neg = !!uECC_vli_sub(tmp, left, right, num_words); + uECC_word_t equal = uECC_vli_isZero(tmp, num_words); + return (!equal - 2 * neg); +} + +/* Computes vli = vli >> 1. */ +#if !asm_rshift1 +uECC_VLI_API void uECC_vli_rshift1(uECC_word_t *vli, wordcount_t num_words) { + uECC_word_t *end = vli; + uECC_word_t carry = 0; + + vli += num_words; + while (vli-- > end) { + uECC_word_t temp = *vli; + *vli = (temp >> 1) | carry; + carry = temp << (uECC_WORD_BITS - 1); + } +} +#endif /* !asm_rshift1 */ + +/* Computes result = left + right, returning carry. Can modify in place. */ +#if !asm_add +uECC_VLI_API uECC_word_t uECC_vli_add(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { + uECC_word_t carry = 0; + wordcount_t i; + for (i = 0; i < num_words; ++i) { + uECC_word_t sum = left[i] + right[i] + carry; + if (sum != left[i]) { + carry = (sum < left[i]); + } + result[i] = sum; + } + return carry; +} +#endif /* !asm_add */ + +/* Computes result = left - right, returning borrow. Can modify in place. */ +#if !asm_sub +uECC_VLI_API uECC_word_t uECC_vli_sub(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { + uECC_word_t borrow = 0; + wordcount_t i; + for (i = 0; i < num_words; ++i) { + uECC_word_t diff = left[i] - right[i] - borrow; + if (diff != left[i]) { + borrow = (diff > left[i]); + } + result[i] = diff; + } + return borrow; +} +#endif /* !asm_sub */ + +#if !asm_mult || (uECC_SQUARE_FUNC && !asm_square) || \ + (uECC_SUPPORTS_secp256k1 && (uECC_OPTIMIZATION_LEVEL > 0) && \ + ((uECC_WORD_SIZE == 1) || (uECC_WORD_SIZE == 8))) +static void muladd(uECC_word_t a, + uECC_word_t b, + uECC_word_t *r0, + uECC_word_t *r1, + uECC_word_t *r2) { +#if uECC_WORD_SIZE == 8 && !SUPPORTS_INT128 + uint64_t a0 = a & 0xffffffffull; + uint64_t a1 = a >> 32; + uint64_t b0 = b & 0xffffffffull; + uint64_t b1 = b >> 32; + + uint64_t i0 = a0 * b0; + uint64_t i1 = a0 * b1; + uint64_t i2 = a1 * b0; + uint64_t i3 = a1 * b1; + + uint64_t p0, p1; + + i2 += (i0 >> 32); + i2 += i1; + if (i2 < i1) { /* overflow */ + i3 += 0x100000000ull; + } + + p0 = (i0 & 0xffffffffull) | (i2 << 32); + p1 = i3 + (i2 >> 32); + + *r0 += p0; + *r1 += (p1 + (*r0 < p0)); + *r2 += ((*r1 < p1) || (*r1 == p1 && *r0 < p0)); +#else + uECC_dword_t p = (uECC_dword_t)a * b; + uECC_dword_t r01 = ((uECC_dword_t)(*r1) << uECC_WORD_BITS) | *r0; + r01 += p; + *r2 += (r01 < p); + *r1 = r01 >> uECC_WORD_BITS; + *r0 = (uECC_word_t)r01; +#endif +} +#endif /* muladd needed */ + +#if !asm_mult +uECC_VLI_API void uECC_vli_mult(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { + uECC_word_t r0 = 0; + uECC_word_t r1 = 0; + uECC_word_t r2 = 0; + wordcount_t i, k; + + /* Compute each digit of result in sequence, maintaining the carries. */ + for (k = 0; k < num_words; ++k) { + for (i = 0; i <= k; ++i) { + muladd(left[i], right[k - i], &r0, &r1, &r2); + } + result[k] = r0; + r0 = r1; + r1 = r2; + r2 = 0; + } + for (k = num_words; k < num_words * 2 - 1; ++k) { + for (i = (k + 1) - num_words; i < num_words; ++i) { + muladd(left[i], right[k - i], &r0, &r1, &r2); + } + result[k] = r0; + r0 = r1; + r1 = r2; + r2 = 0; + } + result[num_words * 2 - 1] = r0; +} +#endif /* !asm_mult */ + +#if uECC_SQUARE_FUNC + +#if !asm_square +static void mul2add(uECC_word_t a, + uECC_word_t b, + uECC_word_t *r0, + uECC_word_t *r1, + uECC_word_t *r2) { +#if uECC_WORD_SIZE == 8 && !SUPPORTS_INT128 + uint64_t a0 = a & 0xffffffffull; + uint64_t a1 = a >> 32; + uint64_t b0 = b & 0xffffffffull; + uint64_t b1 = b >> 32; + + uint64_t i0 = a0 * b0; + uint64_t i1 = a0 * b1; + uint64_t i2 = a1 * b0; + uint64_t i3 = a1 * b1; + + uint64_t p0, p1; + + i2 += (i0 >> 32); + i2 += i1; + if (i2 < i1) + { /* overflow */ + i3 += 0x100000000ull; + } + + p0 = (i0 & 0xffffffffull) | (i2 << 32); + p1 = i3 + (i2 >> 32); + + *r2 += (p1 >> 63); + p1 = (p1 << 1) | (p0 >> 63); + p0 <<= 1; + + *r0 += p0; + *r1 += (p1 + (*r0 < p0)); + *r2 += ((*r1 < p1) || (*r1 == p1 && *r0 < p0)); +#else + uECC_dword_t p = (uECC_dword_t)a * b; + uECC_dword_t r01 = ((uECC_dword_t)(*r1) << uECC_WORD_BITS) | *r0; + *r2 += (p >> (uECC_WORD_BITS * 2 - 1)); + p *= 2; + r01 += p; + *r2 += (r01 < p); + *r1 = r01 >> uECC_WORD_BITS; + *r0 = (uECC_word_t)r01; +#endif +} + +uECC_VLI_API void uECC_vli_square(uECC_word_t *result, + const uECC_word_t *left, + wordcount_t num_words) { + uECC_word_t r0 = 0; + uECC_word_t r1 = 0; + uECC_word_t r2 = 0; + + wordcount_t i, k; + + for (k = 0; k < num_words * 2 - 1; ++k) { + uECC_word_t min = (k < num_words ? 0 : (k + 1) - num_words); + for (i = min; i <= k && i <= k - i; ++i) { + if (i < k-i) { + mul2add(left[i], left[k - i], &r0, &r1, &r2); + } else { + muladd(left[i], left[k - i], &r0, &r1, &r2); + } + } + result[k] = r0; + r0 = r1; + r1 = r2; + r2 = 0; + } + + result[num_words * 2 - 1] = r0; +} +#endif /* !asm_square */ + +#else /* uECC_SQUARE_FUNC */ + +#if uECC_ENABLE_VLI_API +uECC_VLI_API void uECC_vli_square(uECC_word_t *result, + const uECC_word_t *left, + wordcount_t num_words) { + uECC_vli_mult(result, left, left, num_words); +} +#endif /* uECC_ENABLE_VLI_API */ + +#endif /* uECC_SQUARE_FUNC */ + +/* Computes result = (left + right) % mod. + Assumes that left < mod and right < mod, and that result does not overlap mod. */ +uECC_VLI_API void uECC_vli_modAdd(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + const uECC_word_t *mod, + wordcount_t num_words) { + uECC_word_t carry = uECC_vli_add(result, left, right, num_words); + if (carry || uECC_vli_cmp_unsafe(mod, result, num_words) != 1) { + /* result > mod (result = mod + remainder), so subtract mod to get remainder. */ + uECC_vli_sub(result, result, mod, num_words); + } +} + +/* Computes result = (left - right) % mod. + Assumes that left < mod and right < mod, and that result does not overlap mod. */ +uECC_VLI_API void uECC_vli_modSub(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + const uECC_word_t *mod, + wordcount_t num_words) { + uECC_word_t l_borrow = uECC_vli_sub(result, left, right, num_words); + if (l_borrow) { + /* In this case, result == -diff == (max int) - diff. Since -x % d == d - x, + we can get the correct result from result + mod (with overflow). */ + uECC_vli_add(result, result, mod, num_words); + } +} + +/* Computes result = product % mod, where product is 2N words long. */ +/* Currently only designed to work for curve_p or curve_n. */ +uECC_VLI_API void uECC_vli_mmod(uECC_word_t *result, + uECC_word_t *product, + const uECC_word_t *mod, + wordcount_t num_words) { + uECC_word_t mod_multiple[2 * uECC_MAX_WORDS]; + uECC_word_t tmp[2 * uECC_MAX_WORDS]; + uECC_word_t *v[2] = {tmp, product}; + uECC_word_t index; + + /* Shift mod so its highest set bit is at the maximum position. */ + bitcount_t shift = (num_words * 2 * uECC_WORD_BITS) - uECC_vli_numBits(mod, num_words); + wordcount_t word_shift = shift / uECC_WORD_BITS; + wordcount_t bit_shift = shift % uECC_WORD_BITS; + uECC_word_t carry = 0; + uECC_vli_clear(mod_multiple, word_shift); + if (bit_shift > 0) { + for(index = 0; index < (uECC_word_t)num_words; ++index) { + mod_multiple[word_shift + index] = (mod[index] << bit_shift) | carry; + carry = mod[index] >> (uECC_WORD_BITS - bit_shift); + } + } else { + uECC_vli_set(mod_multiple + word_shift, mod, num_words); + } + + for (index = 1; shift >= 0; --shift) { + uECC_word_t borrow = 0; + wordcount_t i; + for (i = 0; i < num_words * 2; ++i) { + uECC_word_t diff = v[index][i] - mod_multiple[i] - borrow; + if (diff != v[index][i]) { + borrow = (diff > v[index][i]); + } + v[1 - index][i] = diff; + } + index = !(index ^ borrow); /* Swap the index if there was no borrow */ + uECC_vli_rshift1(mod_multiple, num_words); + mod_multiple[num_words - 1] |= mod_multiple[num_words] << (uECC_WORD_BITS - 1); + uECC_vli_rshift1(mod_multiple + num_words, num_words); + } + uECC_vli_set(result, v[index], num_words); +} + +/* Computes result = (left * right) % mod. */ +uECC_VLI_API void uECC_vli_modMult(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + const uECC_word_t *mod, + wordcount_t num_words) { + uECC_word_t product[2 * uECC_MAX_WORDS]; + uECC_vli_mult(product, left, right, num_words); + uECC_vli_mmod(result, product, mod, num_words); +} + +uECC_VLI_API void uECC_vli_modMult_fast(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + uECC_Curve curve) { + uECC_word_t product[2 * uECC_MAX_WORDS]; + uECC_vli_mult(product, left, right, curve->num_words); +#if (uECC_OPTIMIZATION_LEVEL > 0) + curve->mmod_fast(result, product); +#else + uECC_vli_mmod(result, product, curve->p, curve->num_words); +#endif +} + +#if uECC_SQUARE_FUNC + +#if uECC_ENABLE_VLI_API +/* Computes result = left^2 % mod. */ +uECC_VLI_API void uECC_vli_modSquare(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *mod, + wordcount_t num_words) { + uECC_word_t product[2 * uECC_MAX_WORDS]; + uECC_vli_square(product, left, num_words); + uECC_vli_mmod(result, product, mod, num_words); +} +#endif /* uECC_ENABLE_VLI_API */ + +uECC_VLI_API void uECC_vli_modSquare_fast(uECC_word_t *result, + const uECC_word_t *left, + uECC_Curve curve) { + uECC_word_t product[2 * uECC_MAX_WORDS]; + uECC_vli_square(product, left, curve->num_words); +#if (uECC_OPTIMIZATION_LEVEL > 0) + curve->mmod_fast(result, product); +#else + uECC_vli_mmod(result, product, curve->p, curve->num_words); +#endif +} + +#else /* uECC_SQUARE_FUNC */ + +#if uECC_ENABLE_VLI_API +uECC_VLI_API void uECC_vli_modSquare(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *mod, + wordcount_t num_words) { + uECC_vli_modMult(result, left, left, mod, num_words); +} +#endif /* uECC_ENABLE_VLI_API */ + +uECC_VLI_API void uECC_vli_modSquare_fast(uECC_word_t *result, + const uECC_word_t *left, + uECC_Curve curve) { + uECC_vli_modMult_fast(result, left, left, curve); +} + +#endif /* uECC_SQUARE_FUNC */ + +#define EVEN(vli) (!(vli[0] & 1)) +static void vli_modInv_update(uECC_word_t *uv, + const uECC_word_t *mod, + wordcount_t num_words) { + uECC_word_t carry = 0; + if (!EVEN(uv)) { + carry = uECC_vli_add(uv, uv, mod, num_words); + } + uECC_vli_rshift1(uv, num_words); + if (carry) { + uv[num_words - 1] |= HIGH_BIT_SET; + } +} + +/* Computes result = (1 / input) % mod. All VLIs are the same size. + See "From Euclid's GCD to Montgomery Multiplication to the Great Divide" */ +uECC_VLI_API void uECC_vli_modInv(uECC_word_t *result, + const uECC_word_t *input, + const uECC_word_t *mod, + wordcount_t num_words) { + uECC_word_t a[uECC_MAX_WORDS], b[uECC_MAX_WORDS], u[uECC_MAX_WORDS], v[uECC_MAX_WORDS]; + cmpresult_t cmpResult; + + if (uECC_vli_isZero(input, num_words)) { + uECC_vli_clear(result, num_words); + return; + } + + uECC_vli_set(a, input, num_words); + uECC_vli_set(b, mod, num_words); + uECC_vli_clear(u, num_words); + u[0] = 1; + uECC_vli_clear(v, num_words); + while ((cmpResult = uECC_vli_cmp_unsafe(a, b, num_words)) != 0) { + if (EVEN(a)) { + uECC_vli_rshift1(a, num_words); + vli_modInv_update(u, mod, num_words); + } else if (EVEN(b)) { + uECC_vli_rshift1(b, num_words); + vli_modInv_update(v, mod, num_words); + } else if (cmpResult > 0) { + uECC_vli_sub(a, a, b, num_words); + uECC_vli_rshift1(a, num_words); + if (uECC_vli_cmp_unsafe(u, v, num_words) < 0) { + uECC_vli_add(u, u, mod, num_words); + } + uECC_vli_sub(u, u, v, num_words); + vli_modInv_update(u, mod, num_words); + } else { + uECC_vli_sub(b, b, a, num_words); + uECC_vli_rshift1(b, num_words); + if (uECC_vli_cmp_unsafe(v, u, num_words) < 0) { + uECC_vli_add(v, v, mod, num_words); + } + uECC_vli_sub(v, v, u, num_words); + vli_modInv_update(v, mod, num_words); + } + } + uECC_vli_set(result, u, num_words); +} + +/* ------ Point operations ------ */ + +#include "curve-specific.inc" + +/* Returns 1 if 'point' is the point at infinity, 0 otherwise. */ +#define EccPoint_isZero(point, curve) uECC_vli_isZero((point), (curve)->num_words * 2) + +/* Point multiplication algorithm using Montgomery's ladder with co-Z coordinates. +From http://eprint.iacr.org/2011/338.pdf +*/ + +/* Modify (x1, y1) => (x1 * z^2, y1 * z^3) */ +static void apply_z(uECC_word_t * X1, + uECC_word_t * Y1, + const uECC_word_t * const Z, + uECC_Curve curve) { + uECC_word_t t1[uECC_MAX_WORDS]; + + uECC_vli_modSquare_fast(t1, Z, curve); /* z^2 */ + uECC_vli_modMult_fast(X1, X1, t1, curve); /* x1 * z^2 */ + uECC_vli_modMult_fast(t1, t1, Z, curve); /* z^3 */ + uECC_vli_modMult_fast(Y1, Y1, t1, curve); /* y1 * z^3 */ +} + +/* P = (x1, y1) => 2P, (x2, y2) => P' */ +static void XYcZ_initial_double(uECC_word_t * X1, + uECC_word_t * Y1, + uECC_word_t * X2, + uECC_word_t * Y2, + const uECC_word_t * const initial_Z, + uECC_Curve curve) { + uECC_word_t z[uECC_MAX_WORDS]; + wordcount_t num_words = curve->num_words; + if (initial_Z) { + uECC_vli_set(z, initial_Z, num_words); + } else { + uECC_vli_clear(z, num_words); + z[0] = 1; + } + + uECC_vli_set(X2, X1, num_words); + uECC_vli_set(Y2, Y1, num_words); + + apply_z(X1, Y1, z, curve); + curve->double_jacobian(X1, Y1, z, curve); + apply_z(X2, Y2, z, curve); +} + +/* Input P = (x1, y1, Z), Q = (x2, y2, Z) + Output P' = (x1', y1', Z3), P + Q = (x3, y3, Z3) + or P => P', Q => P + Q + sub = x1' - x3 (used for subsequent call to XYcZ_addC()). +*/ +static void XYcZ_add(uECC_word_t * X1, + uECC_word_t * Y1, + uECC_word_t * X2, + uECC_word_t * Y2, + uECC_word_t * sub, + uECC_Curve curve) { + /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */ + uECC_word_t t5[uECC_MAX_WORDS]; + wordcount_t num_words = curve->num_words; + + uECC_vli_modSub(t5, X2, X1, curve->p, num_words); /* t5 = x2 - x1 */ + uECC_vli_modSquare_fast(t5, t5, curve); /* t5 = (x2 - x1)^2 = A */ + uECC_vli_modMult_fast(X1, X1, t5, curve); /* x1' = x1*A = B */ + uECC_vli_modMult_fast(X2, X2, t5, curve); /* t3 = x2*A = C */ + uECC_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = y2 - y1 */ + uECC_vli_modSquare_fast(t5, Y2, curve); /* t5 = (y2 - y1)^2 = D */ + + uECC_vli_modSub(t5, t5, X1, curve->p, num_words); /* t5 = D - B */ + uECC_vli_modSub(t5, t5, X2, curve->p, num_words); /* t5 = D - B - C = x3 */ + uECC_vli_modSub(X2, X2, X1, curve->p, num_words); /* t3 = C - B */ + uECC_vli_modMult_fast(Y1, Y1, X2, curve); /* y1' = y1*(C - B) */ + uECC_vli_modSub(sub, X1, t5, curve->p, num_words); /* s = B - x3 */ + uECC_vli_modMult_fast(Y2, Y2, sub, curve); /* t4 = (y2 - y1)*(B - x3) */ + uECC_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = y3 */ + + uECC_vli_set(X2, t5, num_words); /* move x3 to output */ +} + +/* Input P = (x1, y1, Z), Q = (x2, y2, Z), sub = x1 - x2 + Output P - Q = (x3', y3', Z3), P + Q = (x3, y3, Z3) + or P => P - Q, Q => P + Q +*/ +static void XYcZ_addC(uECC_word_t * X1, + uECC_word_t * Y1, + uECC_word_t * X2, + uECC_word_t * Y2, + uECC_word_t * sub, + uECC_Curve curve) { + /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */ + uECC_word_t t5[uECC_MAX_WORDS]; + uECC_word_t t6[uECC_MAX_WORDS]; + uECC_word_t t7[uECC_MAX_WORDS]; + wordcount_t num_words = curve->num_words; + + uECC_vli_modSquare_fast(t5, sub, curve); /* t5 = (x2 - x1)^2 = A */ + uECC_vli_modMult_fast(X1, X1, t5, curve); /* t1 = x1*A = B */ + uECC_vli_modMult_fast(X2, X2, t5, curve); /* t3 = x2*A = C */ + uECC_vli_modAdd(t5, Y2, Y1, curve->p, num_words); /* t5 = y2 + y1 */ + uECC_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = y2 - y1 */ + + uECC_vli_modSub(t6, X2, X1, curve->p, num_words); /* t6 = C - B */ + uECC_vli_modMult_fast(Y1, Y1, t6, curve); /* t2 = y1 * (C - B) = E */ + uECC_vli_modAdd(t6, X1, X2, curve->p, num_words); /* t6 = B + C */ + uECC_vli_modSquare_fast(X2, Y2, curve); /* t3 = (y2 - y1)^2 = D */ + uECC_vli_modSub(X2, X2, t6, curve->p, num_words); /* t3 = D - (B + C) = x3 */ + + uECC_vli_modSub(t7, X1, X2, curve->p, num_words); /* t7 = B - x3 */ + uECC_vli_modMult_fast(Y2, Y2, t7, curve); /* t4 = (y2 - y1)*(B - x3) */ + uECC_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = (y2 - y1)*(B - x3) - E = y3 */ + + uECC_vli_modSquare_fast(t7, t5, curve); /* t7 = (y2 + y1)^2 = F */ + uECC_vli_modSub(t7, t7, t6, curve->p, num_words); /* t7 = F - (B + C) = x3' */ + uECC_vli_modSub(t6, t7, X1, curve->p, num_words); /* t6 = x3' - B */ + uECC_vli_modMult_fast(t6, t6, t5, curve); /* t6 = (y2+y1)*(x3' - B) */ + uECC_vli_modSub(Y1, t6, Y1, curve->p, num_words); /* t2 = (y2+y1)*(x3' - B) - E = y3' */ + + uECC_vli_set(X1, t7, num_words); /* move x3' to output */ +} + +/* result may overlap point. */ +static void EccPoint_mult(uECC_word_t * result, + const uECC_word_t * point, + const uECC_word_t * scalar, + const uECC_word_t * initial_Z, + bitcount_t num_bits, + uECC_Curve curve) { + /* R0 and R1 */ + uECC_word_t Rx[2][uECC_MAX_WORDS]; + uECC_word_t Ry[2][uECC_MAX_WORDS]; + uECC_word_t z[uECC_MAX_WORDS]; + uECC_word_t sub[uECC_MAX_WORDS]; + bitcount_t i; + uECC_word_t nb; + wordcount_t num_words = curve->num_words; + + uECC_vli_set(Rx[1], point, num_words); + uECC_vli_set(Ry[1], point + num_words, num_words); + + XYcZ_initial_double(Rx[1], Ry[1], Rx[0], Ry[0], initial_Z, curve); + uECC_vli_modSub(sub, Rx[0], Rx[1], curve->p, num_words); + + for (i = num_bits - 2; i > 0; --i) { + nb = !uECC_vli_testBit(scalar, i); + XYcZ_addC(Rx[1 - nb], Ry[1 - nb], Rx[nb], Ry[nb], sub, curve); + XYcZ_add(Rx[nb], Ry[nb], Rx[1 - nb], Ry[1 - nb], sub, curve); + } + + nb = !uECC_vli_testBit(scalar, 0); + XYcZ_addC(Rx[1 - nb], Ry[1 - nb], Rx[nb], Ry[nb], sub, curve); + + /* Find final 1/Z value. */ + uECC_vli_modSub(z, Rx[1], Rx[0], curve->p, num_words); /* X1 - X0 */ + uECC_vli_modMult_fast(z, z, Ry[1 - nb], curve); /* Yb * (X1 - X0) */ + uECC_vli_modMult_fast(z, z, point, curve); /* xP * Yb * (X1 - X0) */ + uECC_vli_modInv(z, z, curve->p, num_words); /* 1 / (xP * Yb * (X1 - X0)) */ + uECC_vli_modMult_fast(z, z, point + num_words, curve); /* yP / (xP * Yb * (X1 - X0)) */ + uECC_vli_modMult_fast(z, z, Rx[1 - nb], curve); /* Xb * yP / (xP * Yb * (X1 - X0)) */ + /* End 1/Z calculation */ + + XYcZ_add(Rx[nb], Ry[nb], Rx[1 - nb], Ry[1 - nb], sub, curve); + apply_z(Rx[0], Ry[0], z, curve); + + uECC_vli_set(result, Rx[0], num_words); + uECC_vli_set(result + num_words, Ry[0], num_words); +} + +static uECC_word_t regularize_k(const uECC_word_t * const k, + uECC_word_t *k0, + uECC_word_t *k1, + uECC_Curve curve) { + wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); + bitcount_t num_n_bits = curve->num_n_bits; + uECC_word_t carry = uECC_vli_add(k0, k, curve->n, num_n_words) || + (num_n_bits < ((bitcount_t)num_n_words * uECC_WORD_SIZE * 8) && + uECC_vli_testBit(k0, num_n_bits)); + uECC_vli_add(k1, k0, curve->n, num_n_words); + return carry; +} + +/* Generates a random integer in the range 0 < random < top. + Both random and top have num_words words. */ +uECC_VLI_API int uECC_generate_random_int(uECC_word_t *random, + const uECC_word_t *top, + wordcount_t num_words) { + uECC_word_t mask = (uECC_word_t)-1; + uECC_word_t tries; + bitcount_t num_bits = uECC_vli_numBits(top, num_words); + + if (!g_rng_function) { + return 0; + } + + for (tries = 0; tries < uECC_RNG_MAX_TRIES; ++tries) { + if (!g_rng_function((uint8_t *)random, num_words * uECC_WORD_SIZE)) { + return 0; + } + random[num_words - 1] &= mask >> ((bitcount_t)(num_words * uECC_WORD_SIZE * 8 - num_bits)); + if (!uECC_vli_isZero(random, num_words) && + uECC_vli_cmp(top, random, num_words) == 1) { + return 1; + } + } + return 0; +} + +static uECC_word_t EccPoint_compute_public_key(uECC_word_t *result, + uECC_word_t *private_key, + uECC_Curve curve) { + uECC_word_t tmp1[uECC_MAX_WORDS]; + uECC_word_t tmp2[uECC_MAX_WORDS]; + uECC_word_t *p2[2] = {tmp1, tmp2}; + uECC_word_t *initial_Z = 0; + uECC_word_t carry; + + /* Regularize the bitcount for the private key so that attackers cannot use a side channel + attack to learn the number of leading zeros. */ + carry = regularize_k(private_key, tmp1, tmp2, curve); + + /* If an RNG function was specified, try to get a random initial Z value to improve + protection against side-channel attacks. */ + if (g_rng_function) { + if (!uECC_generate_random_int(p2[carry], curve->p, curve->num_words)) { + return 0; + } + initial_Z = p2[carry]; + } + EccPoint_mult(result, curve->G, p2[!carry], initial_Z, curve->num_n_bits + 1, curve); + + if (EccPoint_isZero(result, curve)) { + return 0; + } + return 1; +} + +#if uECC_WORD_SIZE == 1 + +uECC_VLI_API void uECC_vli_nativeToBytes(uint8_t *bytes, + int num_bytes, + const uint8_t *native) { + wordcount_t i; + for (i = 0; i < num_bytes; ++i) { + bytes[i] = native[(num_bytes - 1) - i]; + } +} + +uECC_VLI_API void uECC_vli_bytesToNative(uint8_t *native, + const uint8_t *bytes, + int num_bytes) { + uECC_vli_nativeToBytes(native, num_bytes, bytes); +} + +#else + +uECC_VLI_API void uECC_vli_nativeToBytes(uint8_t *bytes, + int num_bytes, + const uECC_word_t *native) { + int i; + for (i = 0; i < num_bytes; ++i) { + unsigned b = num_bytes - 1 - i; + bytes[i] = native[b / uECC_WORD_SIZE] >> (8 * (b % uECC_WORD_SIZE)); + } +} + +uECC_VLI_API void uECC_vli_bytesToNative(uECC_word_t *native, + const uint8_t *bytes, + int num_bytes) { + int i; + uECC_vli_clear(native, (num_bytes + (uECC_WORD_SIZE - 1)) / uECC_WORD_SIZE); + for (i = 0; i < num_bytes; ++i) { + unsigned b = num_bytes - 1 - i; + native[b / uECC_WORD_SIZE] |= + (uECC_word_t)bytes[i] << (8 * (b % uECC_WORD_SIZE)); + } +} + +#endif /* uECC_WORD_SIZE */ + +int uECC_make_key(uint8_t *public_key, + uint8_t *private_key, + uECC_Curve curve) { +#if uECC_VLI_NATIVE_LITTLE_ENDIAN + uECC_word_t *_private = (uECC_word_t *)private_key; + uECC_word_t *_public = (uECC_word_t *)public_key; +#else + uECC_word_t _private[uECC_MAX_WORDS]; + uECC_word_t _public[uECC_MAX_WORDS * 2]; +#endif + uECC_word_t tries; + + for (tries = 0; tries < uECC_RNG_MAX_TRIES; ++tries) { + if (!uECC_generate_random_int(_private, curve->n, BITS_TO_WORDS(curve->num_n_bits))) { + return 0; + } + + if (EccPoint_compute_public_key(_public, _private, curve)) { +#if uECC_VLI_NATIVE_LITTLE_ENDIAN == 0 + uECC_vli_nativeToBytes(private_key, BITS_TO_BYTES(curve->num_n_bits), _private); + uECC_vli_nativeToBytes(public_key, curve->num_bytes, _public); + uECC_vli_nativeToBytes( + public_key + curve->num_bytes, curve->num_bytes, _public + curve->num_words); +#endif + return 1; + } + } + return 0; +} + +int uECC_shared_secret(const uint8_t *public_key, + const uint8_t *private_key, + uint8_t *secret, + uECC_Curve curve) { + uECC_word_t _public[uECC_MAX_WORDS * 2]; + uECC_word_t _private[uECC_MAX_WORDS]; + + uECC_word_t tmp[uECC_MAX_WORDS]; + uECC_word_t *p2[2] = {_private, tmp}; + uECC_word_t *initial_Z = 0; + uECC_word_t carry; + wordcount_t num_words = curve->num_words; + wordcount_t num_bytes = curve->num_bytes; + +#if uECC_VLI_NATIVE_LITTLE_ENDIAN + bcopy((uint8_t *) _private, private_key, num_bytes); + bcopy((uint8_t *) _public, public_key, num_bytes*2); +#else + uECC_vli_bytesToNative(_private, private_key, BITS_TO_BYTES(curve->num_n_bits)); + uECC_vli_bytesToNative(_public, public_key, num_bytes); + uECC_vli_bytesToNative(_public + num_words, public_key + num_bytes, num_bytes); +#endif + + /* Regularize the bitcount for the private key so that attackers cannot use a side channel + attack to learn the number of leading zeros. */ + carry = regularize_k(_private, _private, tmp, curve); + + /* If an RNG function was specified, try to get a random initial Z value to improve + protection against side-channel attacks. */ + if (g_rng_function) { + if (!uECC_generate_random_int(p2[carry], curve->p, num_words)) { + return 0; + } + initial_Z = p2[carry]; + } + + EccPoint_mult(_public, _public, p2[!carry], initial_Z, curve->num_n_bits + 1, curve); +#if uECC_VLI_NATIVE_LITTLE_ENDIAN + bcopy((uint8_t *) secret, (uint8_t *) _public, num_bytes); +#else + uECC_vli_nativeToBytes(secret, num_bytes, _public); +#endif + return !EccPoint_isZero(_public, curve); +} + +#if uECC_SUPPORT_COMPRESSED_POINT +void uECC_compress(const uint8_t *public_key, uint8_t *compressed, uECC_Curve curve) { + wordcount_t i; + for (i = 0; i < curve->num_bytes; ++i) { + compressed[i+1] = public_key[i]; + } +#if uECC_VLI_NATIVE_LITTLE_ENDIAN + compressed[0] = 2 + (public_key[curve->num_bytes] & 0x01); +#else + compressed[0] = 2 + (public_key[curve->num_bytes * 2 - 1] & 0x01); +#endif +} + +void uECC_decompress(const uint8_t *compressed, uint8_t *public_key, uECC_Curve curve) { +#if uECC_VLI_NATIVE_LITTLE_ENDIAN + uECC_word_t *point = (uECC_word_t *)public_key; +#else + uECC_word_t point[uECC_MAX_WORDS * 2]; +#endif + uECC_word_t *y = point + curve->num_words; +#if uECC_VLI_NATIVE_LITTLE_ENDIAN + bcopy(public_key, compressed+1, curve->num_bytes); +#else + uECC_vli_bytesToNative(point, compressed + 1, curve->num_bytes); +#endif + curve->x_side(y, point, curve); + curve->mod_sqrt(y, curve); + + if ((y[0] & 0x01) != (compressed[0] & 0x01)) { + uECC_vli_sub(y, curve->p, y, curve->num_words); + } + +#if uECC_VLI_NATIVE_LITTLE_ENDIAN == 0 + uECC_vli_nativeToBytes(public_key, curve->num_bytes, point); + uECC_vli_nativeToBytes(public_key + curve->num_bytes, curve->num_bytes, y); +#endif +} +#endif /* uECC_SUPPORT_COMPRESSED_POINT */ + +uECC_VLI_API int uECC_valid_point(const uECC_word_t *point, uECC_Curve curve) { + uECC_word_t tmp1[uECC_MAX_WORDS]; + uECC_word_t tmp2[uECC_MAX_WORDS]; + wordcount_t num_words = curve->num_words; + + /* The point at infinity is invalid. */ + if (EccPoint_isZero(point, curve)) { + return 0; + } + + /* x and y must be smaller than p. */ + if (uECC_vli_cmp_unsafe(curve->p, point, num_words) != 1 || + uECC_vli_cmp_unsafe(curve->p, point + num_words, num_words) != 1) { + return 0; + } + + uECC_vli_modSquare_fast(tmp1, point + num_words, curve); + curve->x_side(tmp2, point, curve); /* tmp2 = x^3 + ax + b */ + + /* Make sure that y^2 == x^3 + ax + b */ + return (int)(uECC_vli_equal(tmp1, tmp2, num_words)); +} + +int uECC_valid_public_key(const uint8_t *public_key, uECC_Curve curve) { +#if uECC_VLI_NATIVE_LITTLE_ENDIAN + uECC_word_t *_public = (uECC_word_t *)public_key; +#else + uECC_word_t _public[uECC_MAX_WORDS * 2]; +#endif + +#if uECC_VLI_NATIVE_LITTLE_ENDIAN == 0 + uECC_vli_bytesToNative(_public, public_key, curve->num_bytes); + uECC_vli_bytesToNative( + _public + curve->num_words, public_key + curve->num_bytes, curve->num_bytes); +#endif + return uECC_valid_point(_public, curve); +} + +int uECC_compute_public_key(const uint8_t *private_key, uint8_t *public_key, uECC_Curve curve) { +#if uECC_VLI_NATIVE_LITTLE_ENDIAN + uECC_word_t *_private = (uECC_word_t *)private_key; + uECC_word_t *_public = (uECC_word_t *)public_key; +#else + uECC_word_t _private[uECC_MAX_WORDS]; + uECC_word_t _public[uECC_MAX_WORDS * 2]; +#endif + +#if uECC_VLI_NATIVE_LITTLE_ENDIAN == 0 + uECC_vli_bytesToNative(_private, private_key, BITS_TO_BYTES(curve->num_n_bits)); +#endif + + /* Make sure the private key is in the range [1, n-1]. */ + if (uECC_vli_isZero(_private, BITS_TO_WORDS(curve->num_n_bits))) { + return 0; + } + + if (uECC_vli_cmp(curve->n, _private, BITS_TO_WORDS(curve->num_n_bits)) != 1) { + return 0; + } + + /* Compute public key. */ + if (!EccPoint_compute_public_key(_public, _private, curve)) { + return 0; + } + +#if uECC_VLI_NATIVE_LITTLE_ENDIAN == 0 + uECC_vli_nativeToBytes(public_key, curve->num_bytes, _public); + uECC_vli_nativeToBytes( + public_key + curve->num_bytes, curve->num_bytes, _public + curve->num_words); +#endif + return 1; +} + + +/* -------- ECDSA code -------- */ + +static void bits2int(uECC_word_t *native, + const uint8_t *bits, + unsigned bits_size, + uECC_Curve curve) { + unsigned num_n_bytes = BITS_TO_BYTES(curve->num_n_bits); + unsigned num_n_words = BITS_TO_WORDS(curve->num_n_bits); + int shift; + uECC_word_t carry; + uECC_word_t *ptr; + + if (bits_size > num_n_bytes) { + bits_size = num_n_bytes; + } + + uECC_vli_clear(native, num_n_words); +#if uECC_VLI_NATIVE_LITTLE_ENDIAN + bcopy((uint8_t *) native, bits, bits_size); +#else + uECC_vli_bytesToNative(native, bits, bits_size); +#endif + if (bits_size * 8 <= (unsigned)curve->num_n_bits) { + return; + } + shift = bits_size * 8 - curve->num_n_bits; + carry = 0; + ptr = native + num_n_words; + while (ptr-- > native) { + uECC_word_t temp = *ptr; + *ptr = (temp >> shift) | carry; + carry = temp << (uECC_WORD_BITS - shift); + } + + /* Reduce mod curve_n */ + if (uECC_vli_cmp_unsafe(curve->n, native, num_n_words) != 1) { + uECC_vli_sub(native, native, curve->n, num_n_words); + } +} + +static int uECC_sign_with_k_internal(const uint8_t *private_key, + const uint8_t *message_hash, + unsigned hash_size, + uECC_word_t *k, + uint8_t *signature, + uECC_Curve curve) { + + uECC_word_t tmp[uECC_MAX_WORDS]; + uECC_word_t s[uECC_MAX_WORDS]; + uECC_word_t *k2[2] = {tmp, s}; + uECC_word_t *initial_Z = 0; +#if uECC_VLI_NATIVE_LITTLE_ENDIAN + uECC_word_t *p = (uECC_word_t *)signature; +#else + uECC_word_t p[uECC_MAX_WORDS * 2]; +#endif + uECC_word_t carry; + wordcount_t num_words = curve->num_words; + wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); + bitcount_t num_n_bits = curve->num_n_bits; + + /* Make sure 0 < k < curve_n */ + if (uECC_vli_isZero(k, num_words) || uECC_vli_cmp(curve->n, k, num_n_words) != 1) { + return 0; + } + + carry = regularize_k(k, tmp, s, curve); + /* If an RNG function was specified, try to get a random initial Z value to improve + protection against side-channel attacks. */ + if (g_rng_function) { + if (!uECC_generate_random_int(k2[carry], curve->p, num_words)) { + return 0; + } + initial_Z = k2[carry]; + } + EccPoint_mult(p, curve->G, k2[!carry], initial_Z, num_n_bits + 1, curve); + if (uECC_vli_isZero(p, num_words)) { + return 0; + } + + /* If an RNG function was specified, get a random number + to prevent side channel analysis of k. */ + if (!g_rng_function) { + uECC_vli_clear(tmp, num_n_words); + tmp[0] = 1; + } else if (!uECC_generate_random_int(tmp, curve->n, num_n_words)) { + return 0; + } + + /* Prevent side channel analysis of uECC_vli_modInv() to determine + bits of k / the private key by premultiplying by a random number */ + uECC_vli_modMult(k, k, tmp, curve->n, num_n_words); /* k' = rand * k */ + uECC_vli_modInv(k, k, curve->n, num_n_words); /* k = 1 / k' */ + uECC_vli_modMult(k, k, tmp, curve->n, num_n_words); /* k = 1 / k */ + +#if uECC_VLI_NATIVE_LITTLE_ENDIAN == 0 + uECC_vli_nativeToBytes(signature, curve->num_bytes, p); /* store r */ +#endif + +#if uECC_VLI_NATIVE_LITTLE_ENDIAN + bcopy((uint8_t *) tmp, private_key, BITS_TO_BYTES(curve->num_n_bits)); +#else + uECC_vli_bytesToNative(tmp, private_key, BITS_TO_BYTES(curve->num_n_bits)); /* tmp = d */ +#endif + + s[num_n_words - 1] = 0; + uECC_vli_set(s, p, num_words); + uECC_vli_modMult(s, tmp, s, curve->n, num_n_words); /* s = r*d */ + + bits2int(tmp, message_hash, hash_size, curve); + uECC_vli_modAdd(s, tmp, s, curve->n, num_n_words); /* s = e + r*d */ + uECC_vli_modMult(s, s, k, curve->n, num_n_words); /* s = (e + r*d) / k */ + if (uECC_vli_numBits(s, num_n_words) > (bitcount_t)curve->num_bytes * 8) { + return 0; + } +#if uECC_VLI_NATIVE_LITTLE_ENDIAN + bcopy((uint8_t *) signature + curve->num_bytes, (uint8_t *) s, curve->num_bytes); +#else + uECC_vli_nativeToBytes(signature + curve->num_bytes, curve->num_bytes, s); +#endif + return 1; +} + +/* For testing - sign with an explicitly specified k value */ +int uECC_sign_with_k(const uint8_t *private_key, + const uint8_t *message_hash, + unsigned hash_size, + const uint8_t *k, + uint8_t *signature, + uECC_Curve curve) { + uECC_word_t k2[uECC_MAX_WORDS]; + bits2int(k2, k, BITS_TO_BYTES(curve->num_n_bits), curve); + return uECC_sign_with_k_internal(private_key, message_hash, hash_size, k2, signature, curve); +} + +int uECC_sign(const uint8_t *private_key, + const uint8_t *message_hash, + unsigned hash_size, + uint8_t *signature, + uECC_Curve curve) { + uECC_word_t k[uECC_MAX_WORDS]; + uECC_word_t tries; + + for (tries = 0; tries < uECC_RNG_MAX_TRIES; ++tries) { + if (!uECC_generate_random_int(k, curve->n, BITS_TO_WORDS(curve->num_n_bits))) { + return 0; + } + + if (uECC_sign_with_k_internal(private_key, message_hash, hash_size, k, signature, curve)) { + return 1; + } + } + return 0; +} + +/* Compute an HMAC using K as a key (as in RFC 6979). Note that K is always + the same size as the hash result size. */ +static void HMAC_init(const uECC_HashContext *hash_context, const uint8_t *K) { + uint8_t *pad = hash_context->tmp + 2 * hash_context->result_size; + unsigned i; + for (i = 0; i < hash_context->result_size; ++i) + pad[i] = K[i] ^ 0x36; + for (; i < hash_context->block_size; ++i) + pad[i] = 0x36; + + hash_context->init_hash(hash_context); + hash_context->update_hash(hash_context, pad, hash_context->block_size); +} + +static void HMAC_update(const uECC_HashContext *hash_context, + const uint8_t *message, + unsigned message_size) { + hash_context->update_hash(hash_context, message, message_size); +} + +static void HMAC_finish(const uECC_HashContext *hash_context, + const uint8_t *K, + uint8_t *result) { + uint8_t *pad = hash_context->tmp + 2 * hash_context->result_size; + unsigned i; + for (i = 0; i < hash_context->result_size; ++i) + pad[i] = K[i] ^ 0x5c; + for (; i < hash_context->block_size; ++i) + pad[i] = 0x5c; + + hash_context->finish_hash(hash_context, result); + + hash_context->init_hash(hash_context); + hash_context->update_hash(hash_context, pad, hash_context->block_size); + hash_context->update_hash(hash_context, result, hash_context->result_size); + hash_context->finish_hash(hash_context, result); +} + +/* V = HMAC_K(V) */ +static void update_V(const uECC_HashContext *hash_context, uint8_t *K, uint8_t *V) { + HMAC_init(hash_context, K); + HMAC_update(hash_context, V, hash_context->result_size); + HMAC_finish(hash_context, K, V); +} + +/* Deterministic signing, similar to RFC 6979. Differences are: + * We just use H(m) directly rather than bits2octets(H(m)) + (it is not reduced modulo curve_n). + * We generate a value for k (aka T) directly rather than converting endianness. + + Layout of hash_context->tmp: | | (1 byte overlapped 0x00 or 0x01) / */ +int uECC_sign_deterministic(const uint8_t *private_key, + const uint8_t *message_hash, + unsigned hash_size, + const uECC_HashContext *hash_context, + uint8_t *signature, + uECC_Curve curve) { + uint8_t *K = hash_context->tmp; + uint8_t *V = K + hash_context->result_size; + wordcount_t num_bytes = curve->num_bytes; + wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); + bitcount_t num_n_bits = curve->num_n_bits; + uECC_word_t tries; + unsigned i; + for (i = 0; i < hash_context->result_size; ++i) { + V[i] = 0x01; + K[i] = 0; + } + + /* K = HMAC_K(V || 0x00 || int2octets(x) || h(m)) */ + HMAC_init(hash_context, K); + V[hash_context->result_size] = 0x00; + HMAC_update(hash_context, V, hash_context->result_size + 1); + HMAC_update(hash_context, private_key, num_bytes); + HMAC_update(hash_context, message_hash, hash_size); + HMAC_finish(hash_context, K, K); + + update_V(hash_context, K, V); + + /* K = HMAC_K(V || 0x01 || int2octets(x) || h(m)) */ + HMAC_init(hash_context, K); + V[hash_context->result_size] = 0x01; + HMAC_update(hash_context, V, hash_context->result_size + 1); + HMAC_update(hash_context, private_key, num_bytes); + HMAC_update(hash_context, message_hash, hash_size); + HMAC_finish(hash_context, K, K); + + update_V(hash_context, K, V); + + for (tries = 0; tries < uECC_RNG_MAX_TRIES; ++tries) { + uECC_word_t T[uECC_MAX_WORDS]; + uint8_t *T_ptr = (uint8_t *)T; + wordcount_t T_bytes = 0; + for (;;) { + update_V(hash_context, K, V); + for (i = 0; i < hash_context->result_size; ++i) { + T_ptr[T_bytes++] = V[i]; + if (T_bytes >= num_n_words * uECC_WORD_SIZE) { + goto filled; + } + } + } + filled: + if ((bitcount_t)num_n_words * uECC_WORD_SIZE * 8 > num_n_bits) { + uECC_word_t mask = (uECC_word_t)-1; + T[num_n_words - 1] &= + mask >> ((bitcount_t)(num_n_words * uECC_WORD_SIZE * 8 - num_n_bits)); + } + + if (uECC_sign_with_k_internal(private_key, message_hash, hash_size, T, signature, curve)) { + return 1; + } + + /* K = HMAC_K(V || 0x00) */ + HMAC_init(hash_context, K); + V[hash_context->result_size] = 0x00; + HMAC_update(hash_context, V, hash_context->result_size + 1); + HMAC_finish(hash_context, K, K); + + update_V(hash_context, K, V); + } + return 0; +} + +static bitcount_t smax(bitcount_t a, bitcount_t b) { + return (a > b ? a : b); +} + +int uECC_verify(const uint8_t *public_key, + const uint8_t *message_hash, + unsigned hash_size, + const uint8_t *signature, + uECC_Curve curve) { + uECC_word_t u1[uECC_MAX_WORDS], u2[uECC_MAX_WORDS]; + uECC_word_t z[uECC_MAX_WORDS]; + uECC_word_t sum[uECC_MAX_WORDS * 2]; + uECC_word_t rx[uECC_MAX_WORDS]; + uECC_word_t ry[uECC_MAX_WORDS]; + uECC_word_t tx[uECC_MAX_WORDS]; + uECC_word_t ty[uECC_MAX_WORDS]; + uECC_word_t tz[uECC_MAX_WORDS]; + const uECC_word_t *points[4]; + const uECC_word_t *point; + bitcount_t num_bits; + bitcount_t i; +#if uECC_VLI_NATIVE_LITTLE_ENDIAN + uECC_word_t *_public = (uECC_word_t *)public_key; +#else + uECC_word_t _public[uECC_MAX_WORDS * 2]; +#endif + uECC_word_t r[uECC_MAX_WORDS], s[uECC_MAX_WORDS]; + wordcount_t num_words = curve->num_words; + wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); + + rx[num_n_words - 1] = 0; + r[num_n_words - 1] = 0; + s[num_n_words - 1] = 0; + +#if uECC_VLI_NATIVE_LITTLE_ENDIAN + bcopy((uint8_t *) r, signature, curve->num_bytes); + bcopy((uint8_t *) s, signature + curve->num_bytes, curve->num_bytes); +#else + uECC_vli_bytesToNative(_public, public_key, curve->num_bytes); + uECC_vli_bytesToNative( + _public + num_words, public_key + curve->num_bytes, curve->num_bytes); + uECC_vli_bytesToNative(r, signature, curve->num_bytes); + uECC_vli_bytesToNative(s, signature + curve->num_bytes, curve->num_bytes); +#endif + + /* r, s must not be 0. */ + if (uECC_vli_isZero(r, num_words) || uECC_vli_isZero(s, num_words)) { + return 0; + } + + /* r, s must be < n. */ + if (uECC_vli_cmp_unsafe(curve->n, r, num_n_words) != 1 || + uECC_vli_cmp_unsafe(curve->n, s, num_n_words) != 1) { + return 0; + } + + /* Calculate u1 and u2. */ + uECC_vli_modInv(z, s, curve->n, num_n_words); /* z = 1/s */ + u1[num_n_words - 1] = 0; + bits2int(u1, message_hash, hash_size, curve); + uECC_vli_modMult(u1, u1, z, curve->n, num_n_words); /* u1 = e/s */ + uECC_vli_modMult(u2, r, z, curve->n, num_n_words); /* u2 = r/s */ + + /* Calculate sum = G + Q. */ + uECC_vli_set(sum, _public, num_words); + uECC_vli_set(sum + num_words, _public + num_words, num_words); + uECC_vli_set(tx, curve->G, num_words); + uECC_vli_set(ty, curve->G + num_words, num_words); + uECC_vli_modSub(z, sum, tx, curve->p, num_words); /* z = x2 - x1 */ + /* Note: safe to use tx for 'sub' param, since tx is not used after XYcZ_add. */ + XYcZ_add(tx, ty, sum, sum + num_words, tx, curve); + uECC_vli_modInv(z, z, curve->p, num_words); /* z = 1/z */ + apply_z(sum, sum + num_words, z, curve); + + /* Use Shamir's trick to calculate u1*G + u2*Q */ + points[0] = 0; + points[1] = curve->G; + points[2] = _public; + points[3] = sum; + num_bits = smax(uECC_vli_numBits(u1, num_n_words), + uECC_vli_numBits(u2, num_n_words)); + + point = points[(!!uECC_vli_testBit(u1, num_bits - 1)) | + ((!!uECC_vli_testBit(u2, num_bits - 1)) << 1)]; + uECC_vli_set(rx, point, num_words); + uECC_vli_set(ry, point + num_words, num_words); + uECC_vli_clear(z, num_words); + z[0] = 1; + + for (i = num_bits - 2; i >= 0; --i) { + uECC_word_t index; + curve->double_jacobian(rx, ry, z, curve); + + index = (!!uECC_vli_testBit(u1, i)) | ((!!uECC_vli_testBit(u2, i)) << 1); + point = points[index]; + if (point) { + uECC_vli_set(tx, point, num_words); + uECC_vli_set(ty, point + num_words, num_words); + apply_z(tx, ty, z, curve); + uECC_vli_modSub(tz, rx, tx, curve->p, num_words); /* Z = x2 - x1 */ + XYcZ_add(tx, ty, rx, ry, tx, curve); + uECC_vli_modMult_fast(z, z, tz, curve); + } + } + + uECC_vli_modInv(z, z, curve->p, num_words); /* Z = 1/Z */ + apply_z(rx, ry, z, curve); + + /* v = x1 (mod n) */ + if (uECC_vli_cmp_unsafe(curve->n, rx, num_n_words) != 1) { + uECC_vli_sub(rx, rx, curve->n, num_n_words); + } + + /* Accept only if v == r. */ + return (int)(uECC_vli_equal(rx, r, num_words)); +} + +#if uECC_ENABLE_VLI_API + +unsigned uECC_curve_num_words(uECC_Curve curve) { + return curve->num_words; +} + +unsigned uECC_curve_num_bytes(uECC_Curve curve) { + return curve->num_bytes; +} + +unsigned uECC_curve_num_bits(uECC_Curve curve) { + return curve->num_bytes * 8; +} + +unsigned uECC_curve_num_n_words(uECC_Curve curve) { + return BITS_TO_WORDS(curve->num_n_bits); +} + +unsigned uECC_curve_num_n_bytes(uECC_Curve curve) { + return BITS_TO_BYTES(curve->num_n_bits); +} + +unsigned uECC_curve_num_n_bits(uECC_Curve curve) { + return curve->num_n_bits; +} + +const uECC_word_t *uECC_curve_p(uECC_Curve curve) { + return curve->p; +} + +const uECC_word_t *uECC_curve_n(uECC_Curve curve) { + return curve->n; +} + +const uECC_word_t *uECC_curve_G(uECC_Curve curve) { + return curve->G; +} + +const uECC_word_t *uECC_curve_b(uECC_Curve curve) { + return curve->b; +} + +#if uECC_SUPPORT_COMPRESSED_POINT +void uECC_vli_mod_sqrt(uECC_word_t *a, uECC_Curve curve) { + curve->mod_sqrt(a, curve); +} +#endif + +void uECC_vli_mmod_fast(uECC_word_t *result, uECC_word_t *product, uECC_Curve curve) { +#if (uECC_OPTIMIZATION_LEVEL > 0) + curve->mmod_fast(result, product); +#else + uECC_vli_mmod(result, product, curve->p, curve->num_words); +#endif +} + +void uECC_point_mult(uECC_word_t *result, + const uECC_word_t *point, + const uECC_word_t *scalar, + uECC_Curve curve) { + uECC_word_t tmp1[uECC_MAX_WORDS]; + uECC_word_t tmp2[uECC_MAX_WORDS]; + uECC_word_t *p2[2] = {tmp1, tmp2}; + uECC_word_t carry = regularize_k(scalar, tmp1, tmp2, curve); + + EccPoint_mult(result, point, p2[!carry], 0, curve->num_n_bits + 1, curve); +} + +#endif /* uECC_ENABLE_VLI_API */ diff --git a/SDK/12.3.0_d7731ad/external/micro-ecc/micro-ecc/uECC.h b/SDK/12.3.0_d7731ad/external/micro-ecc/micro-ecc/uECC.h new file mode 100644 index 0000000..dcbdbfa --- /dev/null +++ b/SDK/12.3.0_d7731ad/external/micro-ecc/micro-ecc/uECC.h @@ -0,0 +1,367 @@ +/* Copyright 2014, Kenneth MacKay. Licensed under the BSD 2-clause license. */ + +#ifndef _UECC_H_ +#define _UECC_H_ + +#include + +/* Platform selection options. +If uECC_PLATFORM is not defined, the code will try to guess it based on compiler macros. +Possible values for uECC_PLATFORM are defined below: */ +#define uECC_arch_other 0 +#define uECC_x86 1 +#define uECC_x86_64 2 +#define uECC_arm 3 +#define uECC_arm_thumb 4 +#define uECC_arm_thumb2 5 +#define uECC_arm64 6 +#define uECC_avr 7 + +/* If desired, you can define uECC_WORD_SIZE as appropriate for your platform (1, 4, or 8 bytes). +If uECC_WORD_SIZE is not explicitly defined then it will be automatically set based on your +platform. */ + +/* Optimization level; trade speed for code size. + Larger values produce code that is faster but larger. + Currently supported values are 0 - 4; 0 is unusably slow for most applications. + Optimization level 4 currently only has an effect ARM platforms where more than one + curve is enabled. */ +#ifndef uECC_OPTIMIZATION_LEVEL + #define uECC_OPTIMIZATION_LEVEL 2 +#endif + +/* uECC_SQUARE_FUNC - If enabled (defined as nonzero), this will cause a specific function to be +used for (scalar) squaring instead of the generic multiplication function. This can make things +faster somewhat faster, but increases the code size. */ +#ifndef uECC_SQUARE_FUNC + #define uECC_SQUARE_FUNC 0 +#endif + +/* uECC_VLI_NATIVE_LITTLE_ENDIAN - If enabled (defined as nonzero), this will switch to native +little-endian format for *all* arrays passed in and out of the public API. This includes public +and private keys, shared secrets, signatures and message hashes. +Using this switch reduces the amount of call stack memory used by uECC, since less intermediate +translations are required. +Note that this will *only* work on native little-endian processors and it will treat the uint8_t +arrays passed into the public API as word arrays, therefore requiring the provided byte arrays +to be word aligned on architectures that do not support unaligned accesses. +IMPORTANT: Keys and signatures generated with uECC_VLI_NATIVE_LITTLE_ENDIAN=1 are incompatible +with keys and signatures generated with uECC_VLI_NATIVE_LITTLE_ENDIAN=0; all parties must use +the same endianness. */ +#ifndef uECC_VLI_NATIVE_LITTLE_ENDIAN + #define uECC_VLI_NATIVE_LITTLE_ENDIAN 0 +#endif + +/* Curve support selection. Set to 0 to remove that curve. */ +#ifndef uECC_SUPPORTS_secp160r1 + #define uECC_SUPPORTS_secp160r1 1 +#endif +#ifndef uECC_SUPPORTS_secp192r1 + #define uECC_SUPPORTS_secp192r1 1 +#endif +#ifndef uECC_SUPPORTS_secp224r1 + #define uECC_SUPPORTS_secp224r1 1 +#endif +#ifndef uECC_SUPPORTS_secp256r1 + #define uECC_SUPPORTS_secp256r1 1 +#endif +#ifndef uECC_SUPPORTS_secp256k1 + #define uECC_SUPPORTS_secp256k1 1 +#endif + +/* Specifies whether compressed point format is supported. + Set to 0 to disable point compression/decompression functions. */ +#ifndef uECC_SUPPORT_COMPRESSED_POINT + #define uECC_SUPPORT_COMPRESSED_POINT 1 +#endif + +struct uECC_Curve_t; +typedef const struct uECC_Curve_t * uECC_Curve; + +#ifdef __cplusplus +extern "C" +{ +#endif + +#if uECC_SUPPORTS_secp160r1 +uECC_Curve uECC_secp160r1(void); +#endif +#if uECC_SUPPORTS_secp192r1 +uECC_Curve uECC_secp192r1(void); +#endif +#if uECC_SUPPORTS_secp224r1 +uECC_Curve uECC_secp224r1(void); +#endif +#if uECC_SUPPORTS_secp256r1 +uECC_Curve uECC_secp256r1(void); +#endif +#if uECC_SUPPORTS_secp256k1 +uECC_Curve uECC_secp256k1(void); +#endif + +/* uECC_RNG_Function type +The RNG function should fill 'size' random bytes into 'dest'. It should return 1 if +'dest' was filled with random data, or 0 if the random data could not be generated. +The filled-in values should be either truly random, or from a cryptographically-secure PRNG. + +A correctly functioning RNG function must be set (using uECC_set_rng()) before calling +uECC_make_key() or uECC_sign(). + +Setting a correctly functioning RNG function improves the resistance to side-channel attacks +for uECC_shared_secret() and uECC_sign_deterministic(). + +A correct RNG function is set by default when building for Windows, Linux, or OS X. +If you are building on another POSIX-compliant system that supports /dev/random or /dev/urandom, +you can define uECC_POSIX to use the predefined RNG. For embedded platforms there is no predefined +RNG function; you must provide your own. +*/ +typedef int (*uECC_RNG_Function)(uint8_t *dest, unsigned size); + +/* uECC_set_rng() function. +Set the function that will be used to generate random bytes. The RNG function should +return 1 if the random data was generated, or 0 if the random data could not be generated. + +On platforms where there is no predefined RNG function (eg embedded platforms), this must +be called before uECC_make_key() or uECC_sign() are used. + +Inputs: + rng_function - The function that will be used to generate random bytes. +*/ +void uECC_set_rng(uECC_RNG_Function rng_function); + +/* uECC_get_rng() function. + +Returns the function that will be used to generate random bytes. +*/ +uECC_RNG_Function uECC_get_rng(void); + +/* uECC_curve_private_key_size() function. + +Returns the size of a private key for the curve in bytes. +*/ +int uECC_curve_private_key_size(uECC_Curve curve); + +/* uECC_curve_public_key_size() function. + +Returns the size of a public key for the curve in bytes. +*/ +int uECC_curve_public_key_size(uECC_Curve curve); + +/* uECC_make_key() function. +Create a public/private key pair. + +Outputs: + public_key - Will be filled in with the public key. Must be at least 2 * the curve size + (in bytes) long. For example, if the curve is secp256r1, public_key must be 64 + bytes long. + private_key - Will be filled in with the private key. Must be as long as the curve order; this + is typically the same as the curve size, except for secp160r1. For example, if the + curve is secp256r1, private_key must be 32 bytes long. + + For secp160r1, private_key must be 21 bytes long! Note that the first byte will + almost always be 0 (there is about a 1 in 2^80 chance of it being non-zero). + +Returns 1 if the key pair was generated successfully, 0 if an error occurred. +*/ +int uECC_make_key(uint8_t *public_key, uint8_t *private_key, uECC_Curve curve); + +/* uECC_shared_secret() function. +Compute a shared secret given your secret key and someone else's public key. If the public key +is not from a trusted source and has not been previously verified, you should verify it first +using uECC_valid_public_key(). +Note: It is recommended that you hash the result of uECC_shared_secret() before using it for +symmetric encryption or HMAC. + +Inputs: + public_key - The public key of the remote party. + private_key - Your private key. + +Outputs: + secret - Will be filled in with the shared secret value. Must be the same size as the + curve size; for example, if the curve is secp256r1, secret must be 32 bytes long. + +Returns 1 if the shared secret was generated successfully, 0 if an error occurred. +*/ +int uECC_shared_secret(const uint8_t *public_key, + const uint8_t *private_key, + uint8_t *secret, + uECC_Curve curve); + +#if uECC_SUPPORT_COMPRESSED_POINT +/* uECC_compress() function. +Compress a public key. + +Inputs: + public_key - The public key to compress. + +Outputs: + compressed - Will be filled in with the compressed public key. Must be at least + (curve size + 1) bytes long; for example, if the curve is secp256r1, + compressed must be 33 bytes long. +*/ +void uECC_compress(const uint8_t *public_key, uint8_t *compressed, uECC_Curve curve); + +/* uECC_decompress() function. +Decompress a compressed public key. + +Inputs: + compressed - The compressed public key. + +Outputs: + public_key - Will be filled in with the decompressed public key. +*/ +void uECC_decompress(const uint8_t *compressed, uint8_t *public_key, uECC_Curve curve); +#endif /* uECC_SUPPORT_COMPRESSED_POINT */ + +/* uECC_valid_public_key() function. +Check to see if a public key is valid. + +Note that you are not required to check for a valid public key before using any other uECC +functions. However, you may wish to avoid spending CPU time computing a shared secret or +verifying a signature using an invalid public key. + +Inputs: + public_key - The public key to check. + +Returns 1 if the public key is valid, 0 if it is invalid. +*/ +int uECC_valid_public_key(const uint8_t *public_key, uECC_Curve curve); + +/* uECC_compute_public_key() function. +Compute the corresponding public key for a private key. + +Inputs: + private_key - The private key to compute the public key for + +Outputs: + public_key - Will be filled in with the corresponding public key + +Returns 1 if the key was computed successfully, 0 if an error occurred. +*/ +int uECC_compute_public_key(const uint8_t *private_key, uint8_t *public_key, uECC_Curve curve); + +/* uECC_sign() function. +Generate an ECDSA signature for a given hash value. + +Usage: Compute a hash of the data you wish to sign (SHA-2 is recommended) and pass it in to +this function along with your private key. + +Inputs: + private_key - Your private key. + message_hash - The hash of the message to sign. + hash_size - The size of message_hash in bytes. + +Outputs: + signature - Will be filled in with the signature value. Must be at least 2 * curve size long. + For example, if the curve is secp256r1, signature must be 64 bytes long. + +Returns 1 if the signature generated successfully, 0 if an error occurred. +*/ +int uECC_sign(const uint8_t *private_key, + const uint8_t *message_hash, + unsigned hash_size, + uint8_t *signature, + uECC_Curve curve); + +/* uECC_HashContext structure. +This is used to pass in an arbitrary hash function to uECC_sign_deterministic(). +The structure will be used for multiple hash computations; each time a new hash +is computed, init_hash() will be called, followed by one or more calls to +update_hash(), and finally a call to finish_hash() to produce the resulting hash. + +The intention is that you will create a structure that includes uECC_HashContext +followed by any hash-specific data. For example: + +typedef struct SHA256_HashContext { + uECC_HashContext uECC; + SHA256_CTX ctx; +} SHA256_HashContext; + +void init_SHA256(uECC_HashContext *base) { + SHA256_HashContext *context = (SHA256_HashContext *)base; + SHA256_Init(&context->ctx); +} + +void update_SHA256(uECC_HashContext *base, + const uint8_t *message, + unsigned message_size) { + SHA256_HashContext *context = (SHA256_HashContext *)base; + SHA256_Update(&context->ctx, message, message_size); +} + +void finish_SHA256(uECC_HashContext *base, uint8_t *hash_result) { + SHA256_HashContext *context = (SHA256_HashContext *)base; + SHA256_Final(hash_result, &context->ctx); +} + +... when signing ... +{ + uint8_t tmp[32 + 32 + 64]; + SHA256_HashContext ctx = {{&init_SHA256, &update_SHA256, &finish_SHA256, 64, 32, tmp}}; + uECC_sign_deterministic(key, message_hash, &ctx.uECC, signature); +} +*/ +typedef struct uECC_HashContext { + void (*init_hash)(const struct uECC_HashContext *context); + void (*update_hash)(const struct uECC_HashContext *context, + const uint8_t *message, + unsigned message_size); + void (*finish_hash)(const struct uECC_HashContext *context, uint8_t *hash_result); + unsigned block_size; /* Hash function block size in bytes, eg 64 for SHA-256. */ + unsigned result_size; /* Hash function result size in bytes, eg 32 for SHA-256. */ + uint8_t *tmp; /* Must point to a buffer of at least (2 * result_size + block_size) bytes. */ +} uECC_HashContext; + +/* uECC_sign_deterministic() function. +Generate an ECDSA signature for a given hash value, using a deterministic algorithm +(see RFC 6979). You do not need to set the RNG using uECC_set_rng() before calling +this function; however, if the RNG is defined it will improve resistance to side-channel +attacks. + +Usage: Compute a hash of the data you wish to sign (SHA-2 is recommended) and pass it to +this function along with your private key and a hash context. Note that the message_hash +does not need to be computed with the same hash function used by hash_context. + +Inputs: + private_key - Your private key. + message_hash - The hash of the message to sign. + hash_size - The size of message_hash in bytes. + hash_context - A hash context to use. + +Outputs: + signature - Will be filled in with the signature value. + +Returns 1 if the signature generated successfully, 0 if an error occurred. +*/ +int uECC_sign_deterministic(const uint8_t *private_key, + const uint8_t *message_hash, + unsigned hash_size, + const uECC_HashContext *hash_context, + uint8_t *signature, + uECC_Curve curve); + +/* uECC_verify() function. +Verify an ECDSA signature. + +Usage: Compute the hash of the signed data using the same hash as the signer and +pass it to this function along with the signer's public key and the signature values (r and s). + +Inputs: + public_key - The signer's public key. + message_hash - The hash of the signed data. + hash_size - The size of message_hash in bytes. + signature - The signature value. + +Returns 1 if the signature is valid, 0 if it is invalid. +*/ +int uECC_verify(const uint8_t *public_key, + const uint8_t *message_hash, + unsigned hash_size, + const uint8_t *signature, + uECC_Curve curve); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* _UECC_H_ */ diff --git a/SDK/12.3.0_d7731ad/external/micro-ecc/micro-ecc/uECC_vli.h b/SDK/12.3.0_d7731ad/external/micro-ecc/micro-ecc/uECC_vli.h new file mode 100644 index 0000000..864cc33 --- /dev/null +++ b/SDK/12.3.0_d7731ad/external/micro-ecc/micro-ecc/uECC_vli.h @@ -0,0 +1,172 @@ +/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ + +#ifndef _UECC_VLI_H_ +#define _UECC_VLI_H_ + +#include "uECC.h" +#include "types.h" + +/* Functions for raw large-integer manipulation. These are only available + if uECC.c is compiled with uECC_ENABLE_VLI_API defined to 1. */ +#ifndef uECC_ENABLE_VLI_API + #define uECC_ENABLE_VLI_API 0 +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +#if uECC_ENABLE_VLI_API + +void uECC_vli_clear(uECC_word_t *vli, wordcount_t num_words); + +/* Constant-time comparison to zero - secure way to compare long integers */ +/* Returns 1 if vli == 0, 0 otherwise. */ +uECC_word_t uECC_vli_isZero(const uECC_word_t *vli, wordcount_t num_words); + +/* Returns nonzero if bit 'bit' of vli is set. */ +uECC_word_t uECC_vli_testBit(const uECC_word_t *vli, bitcount_t bit); + +/* Counts the number of bits required to represent vli. */ +bitcount_t uECC_vli_numBits(const uECC_word_t *vli, const wordcount_t max_words); + +/* Sets dest = src. */ +void uECC_vli_set(uECC_word_t *dest, const uECC_word_t *src, wordcount_t num_words); + +/* Constant-time comparison function - secure way to compare long integers */ +/* Returns one if left == right, zero otherwise */ +uECC_word_t uECC_vli_equal(const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words); + +/* Constant-time comparison function - secure way to compare long integers */ +/* Returns sign of left - right, in constant time. */ +cmpresult_t uECC_vli_cmp(const uECC_word_t *left, const uECC_word_t *right, wordcount_t num_words); + +/* Computes vli = vli >> 1. */ +void uECC_vli_rshift1(uECC_word_t *vli, wordcount_t num_words); + +/* Computes result = left + right, returning carry. Can modify in place. */ +uECC_word_t uECC_vli_add(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words); + +/* Computes result = left - right, returning borrow. Can modify in place. */ +uECC_word_t uECC_vli_sub(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words); + +/* Computes result = left * right. Result must be 2 * num_words long. */ +void uECC_vli_mult(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words); + +/* Computes result = left^2. Result must be 2 * num_words long. */ +void uECC_vli_square(uECC_word_t *result, const uECC_word_t *left, wordcount_t num_words); + +/* Computes result = (left + right) % mod. + Assumes that left < mod and right < mod, and that result does not overlap mod. */ +void uECC_vli_modAdd(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + const uECC_word_t *mod, + wordcount_t num_words); + +/* Computes result = (left - right) % mod. + Assumes that left < mod and right < mod, and that result does not overlap mod. */ +void uECC_vli_modSub(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + const uECC_word_t *mod, + wordcount_t num_words); + +/* Computes result = product % mod, where product is 2N words long. + Currently only designed to work for mod == curve->p or curve_n. */ +void uECC_vli_mmod(uECC_word_t *result, + uECC_word_t *product, + const uECC_word_t *mod, + wordcount_t num_words); + +/* Calculates result = product (mod curve->p), where product is up to + 2 * curve->num_words long. */ +void uECC_vli_mmod_fast(uECC_word_t *result, uECC_word_t *product, uECC_Curve curve); + +/* Computes result = (left * right) % mod. + Currently only designed to work for mod == curve->p or curve_n. */ +void uECC_vli_modMult(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + const uECC_word_t *mod, + wordcount_t num_words); + +/* Computes result = (left * right) % curve->p. */ +void uECC_vli_modMult_fast(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + uECC_Curve curve); + +/* Computes result = left^2 % mod. + Currently only designed to work for mod == curve->p or curve_n. */ +void uECC_vli_modSquare(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *mod, + wordcount_t num_words); + +/* Computes result = left^2 % curve->p. */ +void uECC_vli_modSquare_fast(uECC_word_t *result, const uECC_word_t *left, uECC_Curve curve); + +/* Computes result = (1 / input) % mod.*/ +void uECC_vli_modInv(uECC_word_t *result, + const uECC_word_t *input, + const uECC_word_t *mod, + wordcount_t num_words); + +#if uECC_SUPPORT_COMPRESSED_POINT +/* Calculates a = sqrt(a) (mod curve->p) */ +void uECC_vli_mod_sqrt(uECC_word_t *a, uECC_Curve curve); +#endif + +/* Converts an integer in uECC native format to big-endian bytes. */ +void uECC_vli_nativeToBytes(uint8_t *bytes, int num_bytes, const uECC_word_t *native); +/* Converts big-endian bytes to an integer in uECC native format. */ +void uECC_vli_bytesToNative(uECC_word_t *native, const uint8_t *bytes, int num_bytes); + +unsigned uECC_curve_num_words(uECC_Curve curve); +unsigned uECC_curve_num_bytes(uECC_Curve curve); +unsigned uECC_curve_num_bits(uECC_Curve curve); +unsigned uECC_curve_num_n_words(uECC_Curve curve); +unsigned uECC_curve_num_n_bytes(uECC_Curve curve); +unsigned uECC_curve_num_n_bits(uECC_Curve curve); + +const uECC_word_t *uECC_curve_p(uECC_Curve curve); +const uECC_word_t *uECC_curve_n(uECC_Curve curve); +const uECC_word_t *uECC_curve_G(uECC_Curve curve); +const uECC_word_t *uECC_curve_b(uECC_Curve curve); + +int uECC_valid_point(const uECC_word_t *point, uECC_Curve curve); + +/* Multiplies a point by a scalar. Points are represented by the X coordinate followed by + the Y coordinate in the same array, both coordinates are curve->num_words long. Note + that scalar must be curve->num_n_words long (NOT curve->num_words). */ +void uECC_point_mult(uECC_word_t *result, + const uECC_word_t *point, + const uECC_word_t *scalar, + uECC_Curve curve); + +/* Generates a random integer in the range 0 < random < top. + Both random and top have num_words words. */ +int uECC_generate_random_int(uECC_word_t *random, + const uECC_word_t *top, + wordcount_t num_words); + +#endif /* uECC_ENABLE_VLI_API */ + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* _UECC_VLI_H_ */ diff --git a/SDK/12.3.0_d7731ad/external/micro-ecc/nrf51_armgcc/armgcc/Makefile b/SDK/12.3.0_d7731ad/external/micro-ecc/nrf51_armgcc/armgcc/Makefile new file mode 100644 index 0000000..6dba7a8 --- /dev/null +++ b/SDK/12.3.0_d7731ad/external/micro-ecc/nrf51_armgcc/armgcc/Makefile @@ -0,0 +1,70 @@ +PROJECT_NAME := ext_micro_ecc_nrf51_library_armgcc +TARGETS := micro_ecc_lib +OUTPUT_DIRECTORY := _build + +SDK_ROOT := ../../../.. +PROJ_DIR := ../.. + + +# Source files common to all targets +SRC_FILES += \ + $(PROJ_DIR)/micro-ecc/uECC.c \ + +# Include folders common to all targets +INC_FOLDERS += \ + +# Libraries common to all targets +LIB_FILES += \ + +# C flags common to all targets +CFLAGS += -DuECC_ENABLE_VLI_API +CFLAGS += -DuECC_VLI_NATIVE_LITTLE_ENDIAN=1 +CFLAGS += -DuECC_SQUARE_FUNC=1 +CFLAGS += -DuECC_SUPPORTS_secp256r1=1 +CFLAGS += -DuECC_SUPPORT_COMPRESSED_POINT=0 +CFLAGS += -DuECC_OPTIMIZATION_LEVEL=3 +CFLAGS += -mcpu=cortex-m0 +CFLAGS += -mthumb -mabi=aapcs +CFLAGS += -Wall -Werror -Os -g3 +CFLAGS += -mfloat-abi=soft +# keep every function in separate section, this allows linker to discard unused ones +CFLAGS += -ffunction-sections -fdata-sections -fno-strict-aliasing +CFLAGS += -fno-builtin --short-enums + +# C++ flags common to all targets +CXXFLAGS += \ + +# Assembler flags common to all targets +ASMFLAGS += -x assembler-with-cpp +ASMFLAGS += -DuECC_ENABLE_VLI_API +ASMFLAGS += -DuECC_VLI_NATIVE_LITTLE_ENDIAN=1 +ASMFLAGS += -DuECC_SQUARE_FUNC=1 +ASMFLAGS += -DuECC_SUPPORTS_secp256r1=1 +ASMFLAGS += -DuECC_SUPPORT_COMPRESSED_POINT=0 +ASMFLAGS += -DuECC_OPTIMIZATION_LEVEL=3 + + + +.PHONY: $(TARGETS) default all clean help flash + +# Default target - first one defined +default: micro_ecc_lib + +# Print all targets that can be built +help: + @echo following targets are available: + @echo micro_ecc_lib + +TEMPLATE_PATH := $(SDK_ROOT)/components/toolchain/gcc + +include $(TEMPLATE_PATH)/Makefile.common + +$(call define_library, $(TARGETS), $(PROJ_DIR)/nrf51_armgcc/armgcc/micro_ecc_lib_nrf51.a) + +define create_library +@echo Creating library: $($@) +$(NO_ECHO)$(AR) $($@) $^ +@echo Done +endef +micro_ecc_lib: + $(create_library) diff --git a/SDK/12.3.0_d7731ad/external/micro-ecc/nrf51_armgcc/armgcc/ext_micro_ecc_gcc_nRF5x.ld b/SDK/12.3.0_d7731ad/external/micro-ecc/nrf51_armgcc/armgcc/ext_micro_ecc_gcc_nRF5x.ld new file mode 100644 index 0000000..4aa3241 --- /dev/null +++ b/SDK/12.3.0_d7731ad/external/micro-ecc/nrf51_armgcc/armgcc/ext_micro_ecc_gcc_nRF5x.ld @@ -0,0 +1,28 @@ +/* Linker script to configure memory regions. */ + +SEARCH_DIR(.) +GROUP(-lgcc -lc -lnosys) + +MEMORY +{ + FLASH (rx) : ORIGIN = 0x0, LENGTH = 0x0 + RAM (rwx) : ORIGIN = 0x0, LENGTH = 0x0 +} + +SECTIONS +{ + .fs_data : + { + PROVIDE(__start_fs_data = .); + KEEP(*(.fs_data)) + PROVIDE(__stop_fs_data = .); + } > RAM + .pwr_mgmt_data : + { + PROVIDE(__start_pwr_mgmt_data = .); + KEEP(*(.pwr_mgmt_data)) + PROVIDE(__stop_pwr_mgmt_data = .); + } > RAM +} INSERT AFTER .data; + +INCLUDE "nrf5x_common.ld" diff --git a/SDK/12.3.0_d7731ad/external/micro-ecc/nrf51_armgcc/armgcc/micro_ecc_lib_nrf51.a b/SDK/12.3.0_d7731ad/external/micro-ecc/nrf51_armgcc/armgcc/micro_ecc_lib_nrf51.a new file mode 100644 index 0000000000000000000000000000000000000000..dc8d3359681357bde2b6bd45b2d36fc5c918a3ed GIT binary patch literal 124882 zcmeFad3aUT^*_ANof47|rT`gE!p#7=VN3#qQOp2gQcMDbQD!uNLWF?S%Algfp_UMo zKvKb>HVk5#id8F?s#9A9}MOS%6$(8^YVrO!)rl| zQfjDD{`mjDrM2TqO6IS-V%hwKOO_}HShIFf7`Srn6*XaK%~fj`u3oC)HA`#euU>h% z1}=94i`TARw{(8>>SgN|)-0XBV(Aa&uUU4D)yl%HU3A5=#cfa&WbqYC7p@NXw`|Sq zrK?wk;hLpuYKoV+1X{WFO2BKv6p$(mEQPpNgu%sER)>MrYpRxAR+AU5xN_B!BDWa; zPmQnyDk7*ajxY_dteAhRhNmduA-s~vkUykgms`PHk!{B?yh(z@m1RaaK8g*4S|>enp0eC7PB zm(^6wUtx8Dig0QElBG3ES6{ho<+3$3%NFbUbxT(-yX*%RTX8vf!~CmPtzKfOqJ52( zqEK7PMULZy2_eJz+OIr62UNG=VL6z^(B0#BH{hON@CTPChymxztQ*ueWa)79o%j49 zHfll4mCF{dUNwK|;>Gi?Shi?Br}n_b16K`-8Ki@pO&I*HD43Uur#*10!K`{&u}@tqGFBTuwUZBhNAUTIKI&-}y8#$w;gpPu+O=sBZ; z>WmRAZ0USt_+jrdpLwY&S$1iX%FI|+SL{Dg5PWWKap8N@jKQCueY2{r*n6TNgV2{} zgJxz`{rDVnaaGWK6Q$utq7RoKk3LfE@abWOIn8ayX|Ba7&kUOPqNUA|{1yXdea13f zx);6FkFN&r?xxR-H^2Sjn{RUj&r+V*7iE^Jk0@1rI6CXCBR9Ytt|>Z{om$=Mb$^XIIOb#=sVlYofS7pWvX=ch8G zT+zBx->~uHj8}hEeCo8Szo9&{w127U?JFo9_ovfSkZve99v}Vqw$fdt_mmzeJr^n0 z$n9!dX@#mSRT)|`$koSlW+3D$NZwxBP~u8oXy@wj zh3gAj3RU%ZmD&05?3P=K{ijscc6eMu*()udpY04kjW@S7`^@O{s#1=BbW^l=OP*O( z8q8zO^3v!`dbnWI{bsI|a?R*{tjPx^-SVB%q>dM>fl;k}Q+1s&h7_Nk^$iW0Ic9VQ zArK``PBV>KPv%GS{N}OLr+W6vo6{*J@mSBJ@kNOfyS&m_)h47h z7OVd;%@w68`}G+ibA-r|VTMd&7dd+}Kc4rt+EE<*pZk(AUJVWE{Y2lWq|_f5cQ}z$ z6>Xj=^pqqo2xfFmsV$5#KRTOLkPE3s3Kt{fZw&#;H%AHl#SxX6Y<508x8*ii>^_w> z(-@plLW`9ZmlbtB(kpXPm!!h9QMVRuSDj}j7FLh{;;^yZld%WVk2HJbpX~W|{5^>k zU9tvGYf*L4W-?}DCiqiMqqhph9A9ks%{|3|Q@NHNezPj=#-cpas8_o)g)U@%1IrjA zo^6?oco=g^Wg2>{LPtWG2rklDyBXsU~RP(tb+MmxIjx+tH z%D%NZN~C#fS&kW~%Utf=A62(_dB}{eyLY*NXNS6|Oy5rLPWJ!S@&GAT8wLA(fqCrninoB`4C|86?30_8>zYcwZPxQ{H zodMxCB)QX+$BTQM%1@=g=ytxOFHUwolbZ@yPqc-CHZX&@wvHBR#_gre}Y2W=Ps(uDB`5PwJ)s zs13Av3oWBTt+mcE3ld$_%K<_A9E{Gr%RT#jXhS=`wIWJ-2<-GrAB&gYv%&+4z;-X} z^X3ZLp&-#;7Y+MGZSPQ*T^bOgUM^VS+ z0g-_AM_-`4S16&4#gUZJJE5hF7wtjXdxe&q&^z?vsCxEuRDC{zF zh;aQdpC~E(&MK7@mlQ2X+fuYdC8l&}&OM?sIv>7S6(#yc#HQ9`4-;3lIJ#R&?1DF= zK0{>Y`kP>Ss{CfGsrsCV^1ql2kJGUsfT)))exL?MxB4c(owzykJ{3yxRHX#Em3S9a zAK|>`{PvmN)Ek=thjS)^UV4}qvY^ zi917)J!^$B>K;^q1Fs;WqQ{Jed8%K*E7%{J#_pn)TErgTp?lQ!SvRZ7nSuSc%xpawFPZ=ZcTc~SAAqQD`pb|+id$BMN5ssS8! z%{mbkQ{t)5F?W}^D{6#`ZL_%87J>caud5))jiN|QzB_8byS zh1gize9w6~oWs2MaWi+gz9y9(6kqXXX0H#Kd1i*Wsl_Lro+%O7w_ok_3ar<6Epvvc zc5caMjkkgFbrkz!u;O}<^;-K6$5#5W-t?`JvC zWSF|vSMO~w&_8SGeKu{#?1hyf13L@7Ruboi+&D^$v3NRead!3dOZ@%8P}C0~7DpDy);MHz$E9QMPC`Z*|a1ftVJrmEeNkC>K94c1^k z;y3STj)UE!(|ej}!dr!w9gl0Wdm4-p&oSe~@|qi4#zA7;>u~J8qO;Rs71j^NeuFkt zJ3Zn7LDSQqb~54-n<){n(~F~^hexb3!t`#_dmeAY>rrnl>s4}gq34X=XGUj-^<~e# z{p3YHk&q9v^p$>yqn&;GwKQrcp*XZ7raSC&?l86dX>(=+B5tj(DPvc%X|5;MQ}5ev zn3+O396LnceqGLNSkjW3>TAlwIQBEu4$|hrQ^QbP(+Fr|2!QtiJnU=g*#WuL(`QeY$-DG!D0qYf9J`?w=!L%MMxgc@U9x zdry7BKJCk?VhNSi9k%#!b2qVtIipwVD2#O%Q0uXwUAu|A+BV(9Hrn>OJSsvy+DOy0 zrDr#MU96?Y$A;KGjPus_HPMT3?pp8J<>L%-Y}|FX#%L?p80-}-Js3mIA@)U?Jj(L<=vsqGeU-veJAjTC#V$zJs(5nD;h!!n1giL`b$(Azv$3HkPkG@eNteOkX zs8HHzw4}!fN1L{5Q?kR&f2u3&X~%n(llN=tu1%?DVwyZoYnyL!Q&(5(WLImJyIQT2 z9ewtm%{A>7nPQbA&8cSbfu2|s8s?SU<*QSfvD*T*pHyZwC1dwT-MtMet3gH8MePnW z>?mD4c1TkaJT+QyLuMp2sR6!L+g`piw!ISSGEB26OZFxiX8)!kQp!S}V9nXV=6!?B zD!XRyc(bX07?+Btg9h-fGxzx$vLq(?8a$&Ku>Pzl?VLVqp32ESJzS4X@3F;Wiw2}z zX_yD*Bz36kf8f%jB`BvP#h`2^B^*$79k33^;MvcRXC~GdclI-5>eI~*;`hD=V^r(e zQB_9vXtS~8!W8qsCgXHc{{ydEoO})^J{$WSPTqz%$e6Xy-0v69=A7iZLA$XW*c~qW zvFo$zqh0lVEqY4jgG5}Hw48%s!T$SnM2@XD_v!T|YIm%RL!#N=s<{9osV3kYg4ch@ z&mF$8*;DJsJm_Mrz=@Mns#HJYxC&;UxmTt2GU^s%PiADQmN~^w6&7Y6!V_Mn$sseP z%GgyuUWMLk&9Cw{gv@S*gR#SPx=+DC%*UO8(~p#GC8J+7V#gq=Tl6b|BPps?q7xW%ZW-1 z*GY+?I@an~5wl+)3zOb}xB-3qyP^i4>RHk0MB<6gGG-Zz)4CpNP`k@>&7igS$~9xK z!?1bAGzE4=?M@e;jl^&bW~8>`waxA#S9Zzta$-M{c);lt|i%)b`?4yVZ42wpZTAjH+OnA@z&0>S`u(p zp{2d2xYH?b_QJHAlW&b4QTS+K^V|ltwa_r%PfV;b@{N+D1s|UM678eVzPnkNg|#Q+ zz`R_u7qmVReem)R{@@*IV6hOft-;VP|nEAZdr#%R_5?c*PxHG*HtN|UmFUo%cy zGCLm%Jh*YeTb0od__q1Chp_4d?$A3PPeWqUY~_^#(w<=Kcv$tr%J`W0vA;eMD?Bw$U!T|%_UM^KIgUTa zt?y_qRCUc0TLL)gAnhZ?y^r@xUDA>Wjj;O`Z#F_H$M032B&zx()LGA`p)OdXj9psm zerD6)l&To4<$ZCg9$wX=%;9D?^cH=XyrW^$?qbVMu%$gC=JHw0n){qJ@n`sakd6V@Y?474N z+_}b^Ju#*~#)x!n=(>ykoR#MuS)a7mBis25qmu$9PW@j}r_=gx{dp|L6 zS<13InmMwnTH6R~gAYl82m2I}MA@Yd&{j#}s% z{~LK`T9bFTe%|mm_;$x1VkBk6tdWRmMaa4|>-o;pFzm87ta~=l4RB z_YrmjPSXIE@pbVJ8G;-tN2SHSEc^T}^E z7pqf4%?DamrCr!E!i<;N0CqstX*U&V8qV%Q6)1^0@UZ%PW*~>eE!yFmPY<`0wZcu!=eG#Y{CQyR`tPE%r{`-!P`> zpH?rq&^T?s)~vLt4_2~(wFc4?jJh%V`s^QgK%W!PBBd6salzrUZVUBk8tCxx*7vdE zG2deJK`VRA`!r3lcHVxR7p`J-#@itMPDtO6;>{4>81=#%qoeUh6AQa&*!>2{lX-Mr z^>|-y_4q8Lu}E8xq7NU;?g()NIa@=P9WDecEQTIH{z8SI`Pmio?Vep@a1c}+$0*SRM*bmUjOfy zZ$DLA?=E>i_?v4#-5O-%QNST7BSeKksyQbK_r={OL<6F*DN|mzY;H8);S*Qh zd-2y#KKJM+Tkh)l^9R0q=W&~!v@GBGWc|zIM%?}B-oft=xNd{@>W>TWTm08|9{+ND zasM|yKHhW1XXU>cR#o`$AD*9=w_?WN=lk`ztJAfU-amVL!=~hK-YkCWlF18BWRDsB z{HV{Hr~a`ykh=8oo9fq1Kl1)B&(@9H|MJjhX1itbC)~|GH?d#$<))dDvG%za4rTxI z*r(J$U*VEc*MI$oZjavjRMpMPmXwCpe0F=);X5bg|Lb?x3@gv{b$#~LSsQj#F*vmdGfkFUmjd~>orHFef`8vvn`DcN110LzL{{#%rfaN=sS1rxSZ47 z#+-ZP%so9KDTq%`o!jN5&G|zzx;$C=!f(Bk9^KY0f75RsT5%*U{#QdbsvCD*_u|+k z}cV5r9=wF#%9NxNkm^?eM{X^eQ&ny`coBi0!zxm6rn#Yz^<`;c-;FrF* z8)N+Z)FR?KFkLC1)cB0fg?PXCU=O7}7^BqQ2r{KSmktDF3cvmE8i7O)YMP@_lgd}l zUL*RB&Zv1JA2m5MS>y5N3}+T~`a)zr|G-$_-^c*IXg%@1s4r8IMf*F!?w%`9_RXm! zB04H|5CET_9~yxStC{jVIRKyLb;|+XbD|@P38PqyAAw@h#Vr2p0u+N+vUm&n_4m1n z#c_R<3S9CoihkeUaSwcgThx)!N;MnXs^KHiWjM$-YUe;56&>3J-hw;j|^DdOec37m0ovhmu-M=r|)IX2%ebLY7qP*u*l$)Y& zgawSdW4uZoRGy;{Kd|nH=-cnR4)>UPbbSX3v2xo32$)~s9#;sR5(Bx$7r=JBY$V#{ z86Ck_XC@{z-u44}<7Z@f$=lB|Y92XdFu$KhY z$ph+G-q2YzvOojs{XTZpDP-efrDTaTkW`e*@9TzQC-x^CL~zuOKt*TD92jAcnO*#QC)Mc~%CT*3 zr$LDB$-(aS2-QC-)gZxMJM@mxiPl4oMl8?AoM17j?DO#l}SCV-1B zsg!YsdmWK*6h^Q=kSifz&+r=vQ-kRAbJ z&Vt9w;L+~!{9Jn1#J2 zlF@iE%Dvu($x+MlcGPB0CPcBG0F+g|5Mw;UPnlmQ42CoPl=EYx-VA*s(XfwE=BZIC z8xaEKa?e`31}-!e_`oILmob{bU=cvfN+hH4F_d-xi=_W9=%rV;BA^zF1o?!YLMO%& z>2oUJ?11!9;r+y@Kv&4h(L1s-^l)1`Xse`$xzfXRpw>N9Ne?yPKn<4(|GnfdPP1Gz z{4HRu@d|-IBn*wOklqz^=#BZS^1+l^uCul5O}&eyR_xGw0^pezm1B$E6{ua=wrw`o z_6Tk&BNhSC_@Stg0j$xOfc=bCnavi-m^(){Q{g=sd^-%u@r+9lo5Nupd3b7PzLOz6+a}JcwW>{g+vq2OAU_-7JsBVqofsX(1XNhKl~ zjXwgBi4WpYsm(uF0`^G`vr#@2^)Isc3pUQ62HqQ4R9lgZ#yH5SUHwK`Enfky*G~or z71>2$@}_f7)}!#KzEYi|^%bzU$Cop~d$)i*Ici`4V%H#rJ+U zUp?_^z+GZ&Xc34mgzf*}CgQv-$5LbyRfC92?|VX2L2X^DQ_g0KhcmkwtX=I@#-EB{ zc^z-If!JuAg4o>a=a`|WrP1fYsJ&w75RdypfVUDZfyWs~d|W4gPFNi13E9^C4)CDA z!<@!%1pf`;>}0@41@3~lJgAZjz;AIlhxkQUc2rNwAYKAEtGZ@2oLTzc0yuXb2osQ6 z;UR-I6r=G=Lc9}5<&404z*4=d#XM_)0j1&|h6@gZ8x2?Ptz8nJ{YbD81hij2i-1jS z17f6dZU>%&00RnJCqAM^IH~M%>1V-Eob_FsIUSq)94>9#jcumiQeqzwq z+2{|zablby<~`~&>?H(c<6R<+--FR`Ve~E;4aaX2fa2avjbJCd5RTF1hk!;l`e_uBGR3h|3raWe6h(d}NADnXdJB3Btd@p8nk7cfs5+XQ?RpdR2_ zFK7PCfc1cG_fqB_Fi#Kt?Bu0jdN6lLj~4+K>WfAc>P~b;J8}>zyc-k-!DVWhE>x*m z$m~(%AaGw{tx=JUWfF2Yu{6;;QE`;e&7yMjcq_5xV@@1#)y-urrr zf%#Hwin_5#Mq@qb^Z60;Kl8;xgy3_iiA9cwLT#j>l8 zwU;xAuCdkzm^-;RDStwS86Eo}L@p3^oM=X4Up!0b_`1O0Rw9I1tmCVOrvcXSb)i98 zPi>hFuZ!?WvQnFY4nc55hgQwMxwDi zqWV?Fp`I}5T8yevXON7>CQLqU(zPC%bf05V4ZrA^^lA@jJ+&8Uldkv3n8d&+&_HZ< zNTjSVhK0?+5S0%!ZT5nt1ke>=6Sg~vrdUK@ejm|#5YZf|5QOG9FQ-O}sQCMcnyv0G zcN1yD>c;`fbQDB1>`g*s>7~&yZceM!kt0c)_S*W7ps`p#pfYZSa^5vqnsD9)w>BDm z-EHfy@zVO^U{3AO8omm!cIX-}X+53?!b^%#IwF^**0?^U=<$|`h z0JRfnecsD(SD~>M(Yp3TVRJ08h#J}xg*mOZh>o@=3Uk_R5&iZ1h@P>CVz6$~#Lx?F zBZ7xH{T)Q~h(X^+ln7gsXxjG?jkSn=@O?zwkCM|}-$%3sM6~!*ZlX565!wMF`qrC5 z6tVB2Z{25gzz4omHxP*21R5ENN94gDOm&q&`#%TN@{YwrcvLxcZ0f*5C61}Ym ztPdpmNYwRUr5$u1CcKd?d@=zuCWi!D3`mvS3+;`e&bddfbd%r1?q;a4T>w&f#4`#6 z-bq+O{V-wm6ku^{W4OW79mMIZl}j(j4@IQkw}Bqi4Ua~~NP#q=J zm}d+)T(Dy=0(4Fg0&xczjxMNGd0${#V-&)u`cTGbyc=vXWnWS#D^n)=&>|L!h(&1{!=NVEvpu?&02t^Hi7Ag*JD8o_5=UWG{5ks|1hOhwfWU`K{fILg8h z6h1(qs1SvYkU2L5a)b-jnGHzSAGJbP%mP_VM`5f@H;ye~8!lS(xM65V+vp97WgBO* zS1IZqEh9U;c3EhW|}7O zUjb*zL3S^J1K7Lii5irHh6HG zUngFIv|7I9|KDhOx{5(S3=76mC-Uv&083g%>3hHz-wE3j(>So1vymIee!qcJ(v zR$zoEFbN&Xro34`cn;b@!U{p`0fMX9RB3huP;L{*I43V7sVscch4dXDRp?#Z7%n#h>jWsy zw;TmK*@0BTtFQLujR7>llvSNDe!pznM5ZVYZ=Sa>;z8cpcQ>lg7Bu1$$)|#{olqVP zqia=-%80HvRSq{Xq;i5@2aVfn@+`nt`zi(K!Wq>UR+Z^zsJvecfc{HlaA64y?)%Q* zE|J0fCsftmxl{(TH>QsE&dUTY1*`}Aa)G&F)`MLocB}(Dp7GRJW({uhCRwX_pn43Q z(ev!}$UXfwz>XOZiPOcQv5vJF5+$;!ye*@_dZj236a{X=O0MI_N>Si96iBAmDiQE~ z!ZN+81&+fWQ$PJ*0N{-suIa){BzJ=mlx4#w}M;2dQkAR;voQXrt4kAAv*)GbpBxF zU92~Y6Z+9;-X%Pph{yUO`-z`S#~cPv+9)~*8yUHDB0k;F^Eoz`SZFb!1VVj=RscMg zr453g>a87%Db@B_qYsRY6EuA!$60i*rq<4Fh1Kw0s{%Z^4#Kr9e%_ibMmUUs(qgDm zvY>}I9}Uzr3BaFWAt~US*nC<~gX0MdpUN<%#eu#|6QhtW{p z+NMpVd@(yb!fYdGwm*Y%!f-TcOvl*|muBz9-17Vt^`Xa6&(-rr;5aoIiYnyv&_3>3 z3%`2?RCW-QV1>zRg+Ok=AC|_hS5%=8&>V(W1XJOwZQR5YG+*sr=GfphZBn5jAfk1B z8Pc~!R6%6ecpOx?v~UXvp~FoRTFfR{%%(*$({sq~S8^4aX7O7W$xmqYdG=+X;E>*{ zxmm}l+L`QumqEo65vs9>ThBwxCS>UlUk_qB;J?p9tU0@gZwE0g)H^f6LRu*apxUb& zEeR?#oh2^W*`w%Uy5Az+6hTZuZDPw|soNP4bL8uUm_v+TE1-^pdsyn6;jrX^H9}v4 zf=a#_(S?4XXGakh2^gBV6Q%-PkjL8gjBH1oU=fdwAlAa$ClijXxyPaTmS|$z!P=cH z478ZmxtT_|p#Yr3)PFe0xjg?;$n8^hEh^?ksMx~4=*HVFh_O>S7g-ckRQB$cD{KG6 zm(cN;q#Jy&qUw)iH16w-AT5t+8{|cUA48If5W2y~7@7x2LO5W?hD!m;p~gu=M(xaD#~brR_yn-V`8cxiDZ3*@nj1#*27g|T*n ziR3{GP9z_U?u0V1xd}@_(Osx`5wxMrs9~I2g<8kStTD0A6gV=sfXZGL zdA~G|9CAjefgqwW|75Y(VbpbkMe+2@AF}x{f{h(e^m;>l$80@j;v3?wdUc~->}IIo zt!QoSU}S*#?L~zOEdU)yZHcC{^t88|5n<+PXt&ZLuRAY!#2h3!qYZ8Jb0O!;4;5-c z9VP$0)I}PIbK+s(84_Yo}o47-MPh0faa0ZN*t-*KFpRnj>M$(HX<0Tqb6j>z(o z?~TSinA4e9^HIr#{j;6`vFPKoeS+QL)|oT&l`&etp#*0Eyg+W30Ry`!P7n=@N&r zHb)!~zw+g>?XBheFW7G@d>F1PhC3o8*zu4AmfOlj+gH0;7I?|^>i`=0ujiwcKwl=_ zQwpB+*X{^6oSpPgtSeh&(;!15or|e4A7ufjH;w&ndbVP~Cix<;CwWaIxrl1LyAv0& z={X-L_Fhfzs3`jqODsHwK|B?@))Kwd&0AD+j1j^6CeYKu9|=9|CKlE@iY<0n!uIOt zvW4yUIwaw4i+YbmJ;hBO***Q$CO-snYPMFBTbfxOt=EQiFc^1*G}wJ^u94&6w(m0_ zr%ulZxvi6S2_3jjS`0sRGqjb8ShqP|Z(7t@m?$oHu*OTsBW(J2&{L}^n%)^NaY@~a zwM+ck;=DPMvvxF#QtN41EQh-j`&nEsq0+y0aFuZ-w!cQQOzvagZ8WYLWKHg5nV)-Q z`eJgY2>2UuL%}ls({@w1@z=j6WE#Dy&@)edgS!Ykm*~5w%i}fxXr;sIt?dV zDQ|5dC$Y7tyPN?wkYT%4sO@1BXV9ZI90auM&osLTZ_!*~)PU&qEmAOoaHL zM7R1XQoOMup6z{(ug3M%#8ywces$=cd-{gQK$3H)p9# z05@@@A+1d^{dGI2X{SGFYTHhXVu+no4|W<0JMFdDe(h$fXP;wFgj##H9<=yo!KW~s zj$iAq5p090&|?8@*Q#nfbE8ukeiBsb<`LGv?%5f1x#Xm&U$X)3M%Y$jk z?&y{t%qeHTb+?0Gc)K+!f{F&QPi?>}XCxFLueN;j{)g(0;D^w>ddR~3&|qvYA4OFc zIO#|pYhJ(t|Gr=i3#U=oi^90Sp)l?f-u}&76n5(4y5N?}33v|a267_#Z*2xASs=@^ zEU=TWP#9}Bm`Ij9Bb-RK44o6nnjvW-*~4~4?4cu$&_bNJ9KoS^GBd2Ly_iQIGZt6` zl@SD@w_e0KwmhP|N(ihCFgsJBNfzo>HwxYBRhK8|54gEnQC|0Z1=@0Fc+A~aqc&Qo zw<1y8QEUT>2i$QunsCNS^TqUeccV-no}SiQJhFw*k(#J`fTC;@1ZDe`vs9>13kdiC zQ0?wSPydJ&;t5Nb`UpBQa&IG5=y{9ow{AKuEeN^t$d*NgUIHO)@~RLzLR0ZGKvC#_ z3(7w4)067Po<&#w1mBFP7r9i5$B!W53@zLOQDnTjmI#DKzwd=zmZ^(z)kMAy2v$9 z$s(7)&8?x=zd~$<4Uc^1CBHr}yt451yha&5S@e38A$AykS@BecHw(Z5sHD_$GJ#i& zupX09%f0kPd_R+fd1fs1@%H0C7zlNyG?A`XpblIsdx8P$N`A%Yma2^d#Su3%Pze2t} zD+j1I<5!Aq*8!fya8wN4Dv^xU-YJlKZCN>U8$v-3ezO-3SHWz-C;Hyi;Cc~1Kh$3%z5s)$5S&UZQZHRr_-nUp+`-da zU+uPydMW4JEwrePw4b9!|Lcc{cd09Xf5QLeWr~&z+mCh8$p?-06lg zcQu=~4GYnbJ^m`P$E~`dUXsDAZL=~oqvjvzW?{{#ayURXMQA=3jksVZ40jEr>}D8y z^*Yc5WvVQ?F>X3*cp@olK*=?Jg{HK}5}M0ML=tbd*xnJzR>m`u@-~a|h?~+`Ou~ZR z1v*;&Q;Saa<5v8(Y)(VAHQs(Sqcf)>#<(1s7N8t@ZQ(3?2!x!xB|;c>RcqDs)!v6U zg*neyR2XsF05+Qrq^RAoF_2;X#_c+A(k~RPkBx@xLNNF4I}OLkmJg5ZOKp^MjAhr58hb29tp3HMj{FaFAxl)`lpzt znKi5Mv&Xw6Y6pYB%fhfng6AZ_Qy%5ouvdcXra@iN%D7vA6+jM>5Py#Z;(GzhD|Wui z-He_%kz{PVwF?5OccH=$|8zA!OW{OZix$&R%{B2C?iR%4EC+_)V!_(N$C{SHO{K-c z8;rvu3Fs^ZKNF;C(56d3E*EEeMI0sKP&mBHxbt$7-pacsg@LG*YmBw{Rm zVx|%JWb1xNVZ0%q+k>IvR5Kd?a-nUJH+&&jh`%lCwI~^=Hw8XO_^B>{-;xjaKLX5e z#^09H@-7)z2M0qeCu`Wj{YO(5EG1#J1AFVdFp z3+D^KIUOxO6bO8^VLy@&{;Po0i~7g%`=*-!kC$H;d?IkC+HIS5cnY%$`(*Zt4yNoG zFCV6V>f@&nUf|?obXq3~z7GrfYj4}cl_>0i^s`ZdjPNxd))zc&E`j+tri+BaeteXb z*8XChuDk_g9MP?sk=+Zr^93?xbk~7uKd3mmf7HzMdX4WAuEsYD3;11vrXLIOkRZk+{(ajB!muIrvqfla`Dbg0qyh)O-gxeg`pC za5yT4^0K(eyGHxmL)IvL9QZLBa8dZ7ZXmN1D}po&ljEQMX5sLL_A{5K?f<;wmq}63F_&cVdt&1Kr>kyn@OJD+NT}sh_ zKSjDoH?WQftT$bdpx!M3Upe!bb;JLb`yxx6gKZjc_h7&7bqTW^&1N1&qJS)*Z6~Gki=T=IHpYQmcQI8y9cEg$W`SX_JbjR!e@V<`Nu*83SspR35HRbfTi1WUIv!Igr(z; zg#CQL?CfKKZUr=<3e5I{4LkcxI@^zGe6@oc{TDiRtnpsbXG(8;&y(rR?_6{S8@$ z1>-FSm$OVO1mhzzS2Eryu_eC{-9Vi z_tmz_epY{Dtm7-jN?dlYCJB%v4`#!p!Utdy=J*@~3rUcDM#$^~D}5A2dunF0K}GwY z5h{DtWc0lT7{=9Zuqqc`wy6kQt=3tqroG+6uFqODC16wd;nD<=%MxCb>d(V+c&K8n ztX$AAnE&!%NLD$6$$g5^SUnblBTvYy#at$vpDLl##Q$6sF%3a zV%)hPcg_p`Eb^86Y836JKVJ=@R~~ts)3RGTB$}&?zp_%H(H22B2(sT4C zadEyy)-HXMSZNzzJqNdnPZFOKKvT{YxQyjj$ndybQox(_qgn8h2r%{67L~446bTE_@@tsdG63QyjjzUE2->1R7vYV=uzmXMISO6i^kh(6^fu;;K5q0B7rX;ENT^tTFVH_ zUb93LxdX6%Lp#>eM5ROPake5lRG0esMb<;0l}#T*MLasjnEA0g$du_rrJ$^eREoQ7 zdnv|5kZqTKg&?mSRLL+H$@r0@)ebB%M&l98Aw5_>5-p-}?60-jDsYIfXmzt_HG#1F zaf(~yZyr~_|&gE_x+Fg`#`hY=U||tNEc1S#(bJG4%v3@ zF#}W4cu-^*)HfaFX6f%%aG053ROl<*_;)Mz0Nd7wYJlfC2M6R8 z!CRi@!*9L0BlguEw6Pz2RMew*0#gpGjML5XqIlJ>uVm=mae|ym}!a<{Du5 zcOeD|#{SMi&s*Sg_=6qI%r|$ zT0GlJ{;)-{SyP->@}r=ko7IN&8em_uz^(YUAK2GzUI4-R zjNNwQ_#2|2;2VBw=&gPE+Cj3hc+=0L_p2av4>@;vOXfA_@Y_=Q2{3x)ddJVxkH-L~ zuf|5+76QlUVQA|nz2A6WX78WbR-(rT0>2G-694?r?U)DA7#gEjVD?2=qDG+3*B^yJ zDD*@@<$eH?+faD%V(^e(CG?d)ztk7l$zKDBJ;UCM=#O}G<|7);ZJRd0Z((9&c+5Sv z0e|is67#h7*Fx!hP4e!wI90MxFGZrZqE#+Xd=~s%Q1Y@EtmxXYui zR<%!G8_1Bpc@8=OR6HjfCsggEY~n#AH~FDT5b`v3ohJ0v9=n$M$fgMD9JADs!&sTG zRcOG2%R@G2YRuz#t-sCLz1*;)oEo&_39j2_$E;&Z-AU61$b-2gd+UD)dwXBPLnJP` z+}nDmkcpQ5GSqPwlPYy?YIG@>VG$T=%X$yUpilLq}UEZJdLXRtlsaP2%9`q`1XR z(V-R;wzG-+*i2f42i=6e+LM|U2akUuimYCAQ^eGC&dXa7zZVJp7cq*XHoWycC}Q5}L_vIr)+38L1yg2lUF^BSmlJh8;;!8+rn z@>?)#X{Or+<-AookD0cCl4g2DQ+`ie+zm=jz}K`;wwdf`V@otd1L3QCOGZK8{#f4Xu>`O%WA0Oap5HIX5u=pDrccE4c3w7j zVH<=sPiu_E-qUT?8-#UWAYj?6Y!q4Z3Cm{!n*^RmSi0IQUEKm$ABtQT;PWU#i1q&E zdV%r0DF;?J1e|vU@~g!yvcGs3t)2HR9*6?RwF*lC#slktFjEXd%n*xZvDGz=1jCj%;=wX)hGeWZAf7zf7;iGd zy|L+dm$?B>U`I6eMCl4OlPD0a4S{5WPS&naSDZ+?izuK8NiTLx8z}>J{oyXmjyMB~ zvF029YH694oKg=-8ZA?%oZ$r_ikGaIs)89}kTMijTD5eQ#RAa%MI`WN(^TMVMKon_0MHs;hf*tidX_qcxZpAhodu-2~QP7L?*N zW*JEBMb)(BVcJSJZKTwiwjxYB-Ax;o8Xf5`{!|%@_Em`j#@z;)>#7Lv)xJHI*-Ru# zJk{|I?yGd0h~by*mMUYWy>B|Ru~zdkr#UM0)=ZU$X)E2dkqVQDc4poe$xt(xxrSGF zHv^TE^s znc1}Aq2zdB_es9YH0^X|x(gb|x#Cd*A+y`1IGx!xr0*4g1Rhnp1etTuR}eLsO_1U= z2Ze%`ek!vX!B#S3XI`~#?jCoq;h_xz-5<#{Xc+*h7A(PR7?MY=kz5Of<0|W#f}DI2 zyxdjPpQFS4GC@~VBU>%Sa%R`)THNI#A-TGwSO9RPR7Fg1E<3O$voxgkCgeP4-^?{} zgzLG{T|t5#dmtk(SLcANg;2(ggy-)GHX=uxAnYRKpqa|@s7*Yb8Fg*XtPg6zj9HEp z%b3xshN@&{H;e3ovjZN%j09~R#+nbzvXH!L&D=dKkhfQ@p1X$yHjIM#1JPNg6WMy8 zGKd7`%(NiWm@&nxt9xD2XL4_R>6F=PXdkClsY5#5mKlSEO6St?t&G`Nq)0>1U_m5~ zADw(Ol513Kv^51Z))`$Nh~uR}IkV14UUk)`JwX)64A#Znxkw%vL8c|3ih9GFnVlz? zU|R)j88c1=5HhPric|y@O4F7zTaIL?>C9}}@G#ORXb$jYrfFv|(_J_VHEQ_RMoGx* zloY2kJA>3-08NW+9{yk-5`<-T28py`0SZvVfdgM=nzoWzv(!#w_7GA)RrBxh>|t5= z&OvrAmu!a&vYXC~-{XF-Y^#NK8Z(P2n3J8JhfNg@v{RgKQ{e`*a=Ij_)rTwOY5r^&JqPRO3;{&>9)?JF_%#x)z zjae>Id!bzJteaIZvuVRtLZ$v2;i@9eMG#hiT~M@Wj9~7-=x>u{`yP^-8mk6?TtVEm z3T3)N->D>2V3sDuh}opgC$W&&NC!iOEaSHMbbH4@vMa?xU}isZ^#9pD`WY(hva4XqnT z2Rhk<1dTbEQ;-4*Us4YV$6q$+hJ>ia>>?>nXGZGC{s*FFf)JE3ngO z?9iizIw5;3V3Gz@MV)VI?)4ynxXiX9wI6~QA^3lJ@6<(@mNBE;mc8;(Y%hSO zEoWvEM9vURP~j#B4?J3lk7BurMp5@x$;@UJ-ZR;>SZ$?EJSwc* zS25SJh+-K4p%^ne@+8A&H^O5Y~mwUKS41-bNM=4zMyxas_cu zMwJKq9_DNhSz8@h@IT`n8T z*g~o*B$#3yGE&28b|X@tMy|cMKZyj#d>YxSx`?}6dvR|?LgSOj&gdeuH>FsPyIhaq z{t{A~g^A;Uw#>4mSjnspQu_&}mmpcWY=THXbgfY~LF9nxcrull?rplexA52jFY6io zjl^A%IKh~cv)c~=y0_|S01oYL8KO%pn{oA5UE7~7>oupZN6#D*rQL? zoZPs_-FJJPU~Q@tM8eLV8RueqIbE~fCWw?%&$J4eX~Jx}n^~mDn%PulHnT{{T+{ad z+=ALbvX}ag28xtZJBS#FQxyZrjLUC(18I?Eudivp=P^l(A;)8yR-RaF`@5Eb*}3z|pq-_h*_}c=jah>f%a~~n)7%^) zC38)6Zk%u$$Ltc#ftlTLq>#GVRA#!1N_Q9GiA7)fK$y|f+7!u-tAu{JmMhPn^GYdV|!J(idS_QLoBv3MY zMvBv!u~B=O=~#&K%rtGKn>H*neU{G(Ft9Ix+_~aWg4E1LOK}FXF-Ywvlq)=F+UaiE zNVnAkQ^Cx&ChKOE?q*?M0;xqil}$A5G&gNnAJV$!VFCseD-|>TNs#uUYdgt2)H9=u znQk`2-7HcIO)KlHrk(DljTBwe%8avR2+PYAt*Z#>_)NIUnGxYCC$NXBocT!(mmy); zRw7%ki<^<%q>E1?ds-K54+bqNoXbQBRm}LA5~tlp2Z=(a;LDj=_-zq*?KQHlYprH5 zBWZgVjTT<=#L_Iq>C9}}u!&GVj*?g4HfyK&c~ecVDsf;ZqfQslFmJT4{ zk&M{{bgcVeNn5C#nWZl}2bElfxL<9LhE+x;S3Fw%$JpTt@zrRB?H)5myzdS1 z`cXnoE=Y?gu>hHKX|x=&*sQ{#yZ~e~kpe2Z@M>l5VR^QOiwLv4(uGG+uCBOqafgSe zAq(ju?#|U2C19DcTMS@$M@*swBn&*W6e*T5vn7mNm>B3VCd@dfh+WKV+ORf>s4Ya| z(>ilXj!h8mas_eEM}k<)#z;}#BNysgW@Dx3EK-1MMXB#?7)PkGBVyZ?B~8kiQ7+)? zH?hFr=|L6q&APmvxy7VDf{A{|RKd)`N7_rT>9Y5^2|~a*n30su5nfn&fWbf{+JIVa zwaU!-aWpm#D9KfY`?W~02eaFx=va%4Xiz@pGuLmZu%U8^cw2-DKB3mHfHP1=put@( z4qaCy3>`E2OceMAufod<0zssJx(Zd12d+I)5Am3_8LswIB3fw6nQ2;i?OzCxw2>aA z`O2Y|rj7WRCDNlf?zARza&sNnaL7YaRnBZUQWOR}$agXX$UA_5x4Mm5u9-d-B=AvY zrAY17(zJ4nt7$9Uw2^9Q+6rcxcDkE3tX2R7|69IcT6qZ6e7{3po0jGxe;S3Mr3U)< zM|q?RW_h%Ri->2L%x!uUoy$ZCZosS&2{Y{FjSdop-be^0%!qG4-f2Q04;&VQ$U{&H z4tHgCrxfMM)#eqRU3A`gNK}yd4k31;MKM55FUZPlkrY{yi;eY+sxg>rsi)dfL#yy< zCmZNJw(Q-j0Al7cTaMJ;OQMB9oL|#UchiQw#0R=1LOTt2=klWjp_!3Wd;Xf>d%mtp z)f|{<7m0Aw$n$e5kKtmbX(QYzEG+OM4rUys(zM^<(sZ}~hOdrBa(*-%O)&+T(J9+2 z&*%ktnQ7WeH*KW6ny)+xYTD^;+VB(vFKZ2y?_^xhAhrx)uOqFNK^|SThfQZT6X|>9 z)wGpv+DLgdUs+oMBod$=*xmeGM!F(W+J(KfMp5%gAzDUC|^I4`h z+Vm(oS1C$xOlCW z8Xf{h22DWo$Q6gXu5#vo4^;gf$+hXo1$Yt(l828MSxGunhEM#SvnpvqBDX+@XarCG zSpjnop<8rKk**qpD&6&-XqI}S4(}oB4DEfOFiO2uwc0H17 z1m6K>om5R{Myyw@HTJOZu&xJlx#%TyiwG?l5vp|M zXo7M#L8SkZFIF>VB=V`%cs=DNk91(V4mxBuT8jV8d1b`GW26`f!b}c|T@PPlP)nU6 zWzYjB5mL+W9fy=AS_g%%qgc$c;9M)W`4w95 z6f3v;&9`8?T@W*xmu-)rK>Dp9i#BY_HO-=D)ZleaXL8EV;xP;6VDjRDAFt=EoWsfM zKjrH+nLK5)$R|x+_P7)Zq~kk_jTXGz%5DCo7Ch3*XIeRzSYC{LyaGCNa=X1fj&^R- z`7HYH$~i%K*>+%=w5+F(kY5m~FOq$2L18=6UZfaHJ`~kKq(ew8NG~JVS2YT&k=7yI zX;&hr4-_B`M{@W7Jm6NO*O1O3*^4HM&h>xihc-o)Ws(0oKeQoizqj-MZ{~+gMy^L# zUh#LW1AbpLs=R?{Z-);4n2E(G$NBt$m;0d*9V3K3efM^ZwF>cmcfG&k1zyZN3+IdT z`+kPi#~T%;YkdAFZbtpFcO#(r6aE6V{M|poMD_Rl*C^Z*ZyAAm@=V@Ssu4u|=0@!P z{HfDIxTie~bNGWzn8g0{3;N)mF&GohpE+iHp2NOE{W1Cd zIV12)>+j3QYkxof2;ASl29o&)yf_s1fo5;q2N{EK&;77F?s<2!$NXn{;6B((ofIpkFT&CM>J1K-`jW$~^3!?mN5$3U7x2D= zhE>a@Z7&)}-OFKB?@9gdr6xqfs^v7Y`h*5oZ&M$&do=Hlikyo%&}xX3FTu*-S8;Gy zziQyntFML6aiMubV26tZ&PuL+vq)sqgab_?ezbbGZ%EU>d1 z%X6>-0V|_$m)N$IuB$c+b}5}leaLC09_z*XBb;37MofLbdRY4666RN9M3*@t=WyZq zGpV__1F;VX4SwH>axbx7cah{uP6Rbq#^MX8pl;MSUYWJ}Tu8 zMNZ5u)c;w$>k5(NlS0<45}w1v0>Kb#Mr8B;l$3uVTDMBg@5J}M75SsYKZl6@4@vzb z@t@6Nf#;=ux{Si-LRp+Zo-0Mdi@UPCS@@L7_%?Ies)t3AlVbC$#U`0j^SdCiQ$4)% z#LBM@$>=N=ng1rew8$(w#K5J#5SwgbSX1A4d4E;Lh=1ACudbFJtA(<=NPa@YxsB4{OWNT(`@PG z4$-=o%!n1D#c1&y{u2X44AJOIX*F6TnJwd3Eb{zBEb|Ak!?_~TpAub`2>+2H=cPi) zKac5GT}8)F#nXNv<6b0Ix=dzYGoFO}>LVGcKZ_PT5rp-`XHrGeQ!=9mh|Dvj9FksA zq~90BL*9|G`%{2zpDW@0Z+W~A7yo=qWIiXFuD^iwRnqV4GCJ>wZ$B^o{D@dPTWI=r zB;^GXAM(et%>QZaS3QJh7b(9el>Nl!-^w^15(^&^`z#dx--;F<(W{eaJw)twi}*vb z=#?Tie@I4wn|{Pk@%BZ+d7F%8cNyy&q<5YR`PHX#Uo1ZVobcg)ZSkw03Fjn<55E?x zzM@#4En3eJ`{#@QPnTX+2LXFC`A{fdm42hedORHR zs~1F`AIXS##6DjM{Z~Rct(Y{oiBuo-7S8Fs|4TGFCVVcF8T#`~*8fFnVnqHJk*ZcW zoEAy4#Y6fCpK8(f7U`={B5fy$Hg#h2OU2JKWgM>&Pbe2VbQU|@D0Zk4`}|m19TQ)= zSA6&yp&2jOfF5O$|6m3s{Da6jM(QiYC-;j_o)Mlu5MSLV{eB`g86b0Zh0L;AFF7ob znh%6qzRWUTA?q&{?Pkj;td~)kBX&rUNVrQZbJ|171o6@yGEbAle^SL)Pf5R*NZX%} zAmwbCgLjC<_--8$N$h`b7nVmtLfJ|5*h%So{erd&HqfVgKg(YGe-3GcqSF-eNPmX9 z4jRY#J#HRHd;S61Z(}keN4=56AvclFInR0S zxo1D;o_i->e&=CSK^98Ys6(cp76lLOSkrHfn*V~N%YsiOtrvb9#{a|WBu5KxEyn+% z6D5s`i{$&#+9LdK`0)t*KYAZY{bL@JQr@2{*K+(-IM>K6fr(Ny&I%MsI$RidMGXET zFk15Amw~G!0d5YYi~b#fYMgk~T>%xly8|kEe+a1f-4jp|yf>g?I2e#9s$?2dD266p zED1FEYB@#fBT^9#D#yu{#(sNCRHr^99ny^VrKru$$0d)oqM&1y3K`#7vf#v{kdtjPWsNV--QUdM^ zoFm2U{=g+-@`1o6DTsdx$ZMfTJ|4JMlIn>-P$KhUpiv6WOMz!4@-GKwOF{oz;0h^a z?+4xxmA?nZNy`5#aF3+ZH-Q={Dc=V6l@py2+#soTW^l}C;H+SpT=vg{6Al7?5u7WR za&d5-IQ}~LCn+Dl37#%~*9C7vUXQ#Zn3N{-&fxjdD&7@5T&l{W!4|pN$AbTmg7c@~ zqf!Q54$hS7{z~u>NwBwq)nfXC;A*KEJA)rfb@)f{BuRoVgFTWw{|Y7~!1uv}B(={7 zElvY|8d@jUw+*KOE)Q*yVzfQvUIP9Bw5D&PQH&XkXkfBevWHo!2&Tb zzd*|AVXGyp%5Ftws-iOD6{+WA|8OY&hkq#r>tOkP^^qleN^NbtPR_CN9?8ig50uMZ zbr)il_1bN|WW(sa#L(`AQcA|$ zD2aH$;qrg-X_9DDUX;jAeO4}I+EMD_&XK%NeFm3kjYph=RzA!G1K;A+JOLbV#wU=O zIS+qb9nHPn8Oi2mtND;sJ-yv+y@{@HYe#3YFPy$UKX>-3wlxFc{_gJ1zHp+qE1c|V zO188lTUKRf7KCGwXwI=&tKgoorh82?BbjLGSkb zcaxnRO{?0Pn|(3HrDSHAc>li}A2u2AYoKK%C7I2V;)%YlIFvGxLLxQ~^sY^2^z|g0 zJ6b!MjcdFm*_s&W?2q?v=t=fvw#!0vysy6nO(O)Xc3wG~iB?C+?pt;t0H zK(Dfcpm7-K8|ZQD#MgFqK!Smu&P0D}H!{*6ACx4-0F≪l)GqQWs*?10{%VKZ6t; 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 z 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 gOP5}LR&^~t50qOtSF3(;e@DAp RAM + .pwr_mgmt_data : + { + PROVIDE(__start_pwr_mgmt_data = .); + KEEP(*(.pwr_mgmt_data)) + PROVIDE(__stop_pwr_mgmt_data = .); + } > RAM +} INSERT AFTER .data; + +INCLUDE "nrf5x_common.ld" diff --git a/SDK/12.3.0_d7731ad/external/micro-ecc/nrf51_keil/armgcc/micro_ecc_lib_nrf51.lib b/SDK/12.3.0_d7731ad/external/micro-ecc/nrf51_keil/armgcc/micro_ecc_lib_nrf51.lib new file mode 100644 index 0000000000000000000000000000000000000000..357ac8e47218468bf611169f2a41c8ddd91e268c GIT binary patch literal 124890 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�dIADOPeD=1(p5&JSeDd)z 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 iMI$X;dihz^wfH + +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. diff --git a/SDK/12.3.0_d7731ad/external/nano-pb/pb.h b/SDK/12.3.0_d7731ad/external/nano-pb/pb.h new file mode 100644 index 0000000..a17280d --- /dev/null +++ b/SDK/12.3.0_d7731ad/external/nano-pb/pb.h @@ -0,0 +1,556 @@ +/* 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 > 255 and fields larger than 255 bytes. */ +/* #define PB_FIELD_16BIT 1 */ + +/* 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 */ + +/* Switch back to the old-style callback function signature. + * This was the default until nanopb-0.2.1. */ +/* #define PB_OLD_CALLBACK_STYLE */ + + +/****************************************************************** + * 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.3.6-dev + +/* 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 +#include +#include +#include + +#ifdef PB_ENABLE_MALLOC +#include +#endif +#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 + +/* 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 +#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 +#else +#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 + +/* 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_VARINT 0x00 /* int32, int64, enum, bool */ +#define PB_LTYPE_UVARINT 0x01 /* uint32, uint64 */ +#define PB_LTYPE_SVARINT 0x02 /* sint32, sint64 */ +#define PB_LTYPE_FIXED32 0x03 /* fixed32, sfixed32, float */ +#define PB_LTYPE_FIXED64 0x04 /* fixed64, sfixed64, double */ + +/* Marker for last packable field type. */ +#define PB_LTYPE_LAST_PACKABLE 0x04 + +/* Byte array with pre-allocated buffer. + * data_size is the length of the allocated PB_BYTES_ARRAY structure. */ +#define PB_LTYPE_BYTES 0x05 + +/* String with pre-allocated buffer. + * data_size is the maximum length. */ +#define PB_LTYPE_STRING 0x06 + +/* Submessage + * submsg_fields is pointer to field descriptions */ +#define PB_LTYPE_SUBMESSAGE 0x07 + +/* Extension pseudo-field + * The field contains a pointer to pb_extension_t */ +#define PB_LTYPE_EXTENSION 0x08 + +/* Number of declared LTYPES */ +#define PB_LTYPES_COUNT 9 +#define PB_LTYPE_MASK 0x0F + +/**** Field repetition rules ****/ + +#define PB_HTYPE_REQUIRED 0x00 +#define PB_HTYPE_OPTIONAL 0x10 +#define PB_HTYPE_REPEATED 0x20 +#define PB_HTYPE_ONEOF 0x30 +#define PB_HTYPE_MASK 0x30 + +/**** Field allocation types ****/ + +#define PB_ATYPE_STATIC 0x00 +#define PB_ATYPE_POINTER 0x80 +#define PB_ATYPE_CALLBACK 0x40 +#define PB_ATYPE_MASK 0xC0 + +#define PB_ATYPE(x) ((x) & PB_ATYPE_MASK) +#define PB_HTYPE(x) ((x) & PB_HTYPE_MASK) +#define PB_LTYPE(x) ((x) & PB_LTYPE_MASK) + +/* 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; +#elif defined(PB_FIELD_16BIT) + typedef uint_least16_t pb_size_t; + typedef int_least16_t pb_ssize_t; +#else + typedef uint_least8_t pb_size_t; + typedef int_least8_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; + +/* This structure is used in auto-generated constants + * to specify struct fields. + * You can change field sizes if you need structures + * larger than 256 bytes or field tags larger than 256. + * The compiler should complain if your .proto has such + * structures. Fix that by defining PB_FIELD_16BIT or + * PB_FIELD_32BIT. + */ +PB_PACKED_STRUCT_START +typedef struct pb_field_s pb_field_t; +struct pb_field_s { + pb_size_t tag; + pb_type_t type; + pb_size_t data_offset; /* Offset of field data, relative to previous field. */ + pb_ssize_t size_offset; /* Offset of array size or has-boolean, relative to data */ + pb_size_t data_size; /* Data size in bytes for a single item */ + pb_size_t array_size; /* Maximum number of entries in array */ + + /* Field definitions for submessage + * OR default value for all other non-array, non-callback types + * If null, then field will zeroed. */ + const void *ptr; +} pb_packed; +PB_PACKED_STRUCT_END + +/* 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. + */ +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) + +/* 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_istream_s pb_istream_t; +typedef struct pb_ostream_s pb_ostream_t; +typedef struct pb_callback_s pb_callback_t; +struct pb_callback_s { +#ifdef PB_OLD_CALLBACK_STYLE + /* Deprecated since nanopb-0.2.1 */ + 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, const void *arg); + } funcs; +#else + /* New function signature, which allows modifying arg contents in callback. */ + 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; +#endif + + /* Free arg for use by callback */ + void *arg; +}; + +/* 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; +}; + +/* 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 30 + +/* 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)) +/* Marks the end of the field list */ +#define PB_LAST_FIELD {0,(pb_type_t) 0,0,0,0,0,0} + +/* Macros for filling in the data_offset field */ +/* data_offset for first field in a message */ +#define PB_DATAOFFSET_FIRST(st, m1, m2) (offsetof(st, m1)) +/* data_offset for subsequent fields */ +#define PB_DATAOFFSET_OTHER(st, m1, m2) (offsetof(st, m1) - offsetof(st, m2) - pb_membersize(st, m2)) +/* Choose first/other based on m1 == m2 (deprecated, remains for backwards compatibility) */ +#define PB_DATAOFFSET_CHOOSE(st, m1, m2) (int)(offsetof(st, m1) == offsetof(st, m2) \ + ? PB_DATAOFFSET_FIRST(st, m1, m2) \ + : PB_DATAOFFSET_OTHER(st, m1, m2)) + +/* Required fields are the simplest. They just have delta (padding) from + * previous field end, and the size of the field. Pointer is used for + * submessages and default values. + */ +#define PB_REQUIRED_STATIC(tag, st, m, fd, ltype, ptr) \ + {tag, PB_ATYPE_STATIC | PB_HTYPE_REQUIRED | ltype, \ + fd, 0, pb_membersize(st, m), 0, ptr} + +/* Optional fields add the delta to the has_ variable. */ +#define PB_OPTIONAL_STATIC(tag, st, m, fd, ltype, ptr) \ + {tag, PB_ATYPE_STATIC | PB_HTYPE_OPTIONAL | ltype, \ + fd, \ + pb_delta(st, has_ ## m, m), \ + pb_membersize(st, m), 0, ptr} + +/* Repeated fields have a _count field and also the maximum number of entries. */ +#define PB_REPEATED_STATIC(tag, st, m, fd, ltype, ptr) \ + {tag, PB_ATYPE_STATIC | PB_HTYPE_REPEATED | ltype, \ + fd, \ + pb_delta(st, m ## _count, m), \ + pb_membersize(st, m[0]), \ + pb_arraysize(st, m), ptr} + +/* Allocated fields carry the size of the actual data, not the pointer */ +#define PB_REQUIRED_POINTER(tag, st, m, fd, ltype, ptr) \ + {tag, PB_ATYPE_POINTER | PB_HTYPE_REQUIRED | ltype, \ + fd, 0, pb_membersize(st, m[0]), 0, ptr} + +/* Optional fields don't need a has_ variable, as information would be redundant */ +#define PB_OPTIONAL_POINTER(tag, st, m, fd, ltype, ptr) \ + {tag, PB_ATYPE_POINTER | PB_HTYPE_OPTIONAL | ltype, \ + fd, 0, pb_membersize(st, m[0]), 0, ptr} + +/* Repeated fields have a _count field and a pointer to array of pointers */ +#define PB_REPEATED_POINTER(tag, st, m, fd, ltype, ptr) \ + {tag, PB_ATYPE_POINTER | PB_HTYPE_REPEATED | ltype, \ + fd, pb_delta(st, m ## _count, m), \ + pb_membersize(st, m[0]), 0, ptr} + +/* Callbacks are much like required fields except with special datatype. */ +#define PB_REQUIRED_CALLBACK(tag, st, m, fd, ltype, ptr) \ + {tag, PB_ATYPE_CALLBACK | PB_HTYPE_REQUIRED | ltype, \ + fd, 0, pb_membersize(st, m), 0, ptr} + +#define PB_OPTIONAL_CALLBACK(tag, st, m, fd, ltype, ptr) \ + {tag, PB_ATYPE_CALLBACK | PB_HTYPE_OPTIONAL | ltype, \ + fd, 0, pb_membersize(st, m), 0, ptr} + +#define PB_REPEATED_CALLBACK(tag, st, m, fd, ltype, ptr) \ + {tag, PB_ATYPE_CALLBACK | PB_HTYPE_REPEATED | ltype, \ + fd, 0, pb_membersize(st, m), 0, ptr} + +/* Optional extensions don't have the has_ field, as that would be redundant. */ +#define PB_OPTEXT_STATIC(tag, st, m, fd, ltype, ptr) \ + {tag, PB_ATYPE_STATIC | PB_HTYPE_OPTIONAL | ltype, \ + 0, \ + 0, \ + pb_membersize(st, m), 0, ptr} + +#define PB_OPTEXT_POINTER(tag, st, m, fd, ltype, ptr) \ + PB_OPTIONAL_POINTER(tag, st, m, fd, ltype, ptr) + +#define PB_OPTEXT_CALLBACK(tag, st, m, fd, ltype, ptr) \ + PB_OPTIONAL_CALLBACK(tag, st, m, fd, ltype, ptr) + +/* The mapping from protobuf types to LTYPEs is done using these macros. */ +#define PB_LTYPE_MAP_BOOL PB_LTYPE_VARINT +#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_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 + +/* This is the actual macro used in field descriptions. + * It takes these arguments: + * - Field tag number + * - Field type: BOOL, BYTES, DOUBLE, ENUM, UENUM, FIXED32, FIXED64, + * FLOAT, INT32, INT64, MESSAGE, SFIXED32, SFIXED64 + * SINT32, SINT64, STRING, UINT32, UINT64 or EXTENSION + * - Field rules: REQUIRED, OPTIONAL or REPEATED + * - Allocation: STATIC or CALLBACK + * - Placement: FIRST or OTHER, depending on if this is the first field in structure. + * - Message name + * - Field name + * - Previous field name (or field name again for first field) + * - Pointer to default value or submsg fields. + */ + +#define PB_FIELD(tag, type, rules, allocation, placement, message, field, prevfield, ptr) \ + PB_ ## rules ## _ ## allocation(tag, message, field, \ + PB_DATAOFFSET_ ## placement(message, field, prevfield), \ + PB_LTYPE_MAP_ ## type, ptr) + +/* Field description for oneof fields. This requires taking into account the + * union name also, that's why a separate set of macros is needed. + */ +#define PB_ONEOF_STATIC(u, tag, st, m, fd, ltype, ptr) \ + {tag, PB_ATYPE_STATIC | PB_HTYPE_ONEOF | ltype, \ + fd, pb_delta(st, which_ ## u, u.m), \ + pb_membersize(st, u.m), 0, ptr} + +#define PB_ONEOF_POINTER(u, tag, st, m, fd, ltype, ptr) \ + {tag, PB_ATYPE_POINTER | PB_HTYPE_ONEOF | ltype, \ + fd, pb_delta(st, which_ ## u, u.m), \ + pb_membersize(st, u.m[0]), 0, ptr} + +#define PB_ONEOF_FIELD(union_name, tag, type, rules, allocation, placement, message, field, prevfield, ptr) \ + PB_ONEOF_ ## allocation(union_name, tag, message, field, \ + PB_DATAOFFSET_ ## placement(message, union_name.field, prevfield), \ + PB_LTYPE_MAP_ ## type, ptr) + +#define PB_ANONYMOUS_ONEOF_STATIC(u, tag, st, m, fd, ltype, ptr) \ + {tag, PB_ATYPE_STATIC | PB_HTYPE_ONEOF | ltype, \ + fd, pb_delta(st, which_ ## u, m), \ + pb_membersize(st, m), 0, ptr} + +#define PB_ANONYMOUS_ONEOF_POINTER(u, tag, st, m, fd, ltype, ptr) \ + {tag, PB_ATYPE_POINTER | PB_HTYPE_ONEOF | ltype, \ + fd, pb_delta(st, which_ ## u, m), \ + pb_membersize(st, m[0]), 0, ptr} + +#define PB_ANONYMOUS_ONEOF_FIELD(union_name, tag, type, rules, allocation, placement, message, field, prevfield, ptr) \ + PB_ANONYMOUS_ONEOF_ ## allocation(union_name, tag, message, field, \ + PB_DATAOFFSET_ ## placement(message, field, prevfield), \ + PB_LTYPE_MAP_ ## type, ptr) + +/* 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 + +#endif diff --git a/SDK/12.3.0_d7731ad/external/nano-pb/pb_common.c b/SDK/12.3.0_d7731ad/external/nano-pb/pb_common.c new file mode 100644 index 0000000..385c019 --- /dev/null +++ b/SDK/12.3.0_d7731ad/external/nano-pb/pb_common.c @@ -0,0 +1,97 @@ +/* pb_common.c: Common support functions for pb_encode.c and pb_decode.c. + * + * 2014 Petteri Aimonen + */ + +#include "pb_common.h" + +bool pb_field_iter_begin(pb_field_iter_t *iter, const pb_field_t *fields, void *dest_struct) +{ + iter->start = fields; + iter->pos = fields; + iter->required_field_index = 0; + iter->dest_struct = dest_struct; + iter->pData = (char*)dest_struct + iter->pos->data_offset; + iter->pSize = (char*)iter->pData + iter->pos->size_offset; + + return (iter->pos->tag != 0); +} + +bool pb_field_iter_next(pb_field_iter_t *iter) +{ + const pb_field_t *prev_field = iter->pos; + + if (prev_field->tag == 0) + { + /* Handle empty message types, where the first field is already the terminator. + * In other cases, the iter->pos never points to the terminator. */ + return false; + } + + iter->pos++; + + if (iter->pos->tag == 0) + { + /* Wrapped back to beginning, reinitialize */ + (void)pb_field_iter_begin(iter, iter->start, iter->dest_struct); + return false; + } + else + { + /* Increment the pointers based on previous field size */ + size_t prev_size = prev_field->data_size; + + if (PB_HTYPE(prev_field->type) == PB_HTYPE_ONEOF && + PB_HTYPE(iter->pos->type) == PB_HTYPE_ONEOF) + { + /* Don't advance pointers inside unions */ + prev_size = 0; + iter->pData = (char*)iter->pData - prev_field->data_offset; + } + else if (PB_ATYPE(prev_field->type) == PB_ATYPE_STATIC && + PB_HTYPE(prev_field->type) == PB_HTYPE_REPEATED) + { + /* In static arrays, the data_size tells the size of a single entry and + * array_size is the number of entries */ + prev_size *= prev_field->array_size; + } + else if (PB_ATYPE(prev_field->type) == PB_ATYPE_POINTER) + { + /* Pointer fields always have a constant size in the main structure. + * The data_size only applies to the dynamically allocated area. */ + prev_size = sizeof(void*); + } + + if (PB_HTYPE(prev_field->type) == PB_HTYPE_REQUIRED) + { + /* Count the required fields, in order to check their presence in the + * decoder. */ + iter->required_field_index++; + } + + iter->pData = (char*)iter->pData + prev_size + iter->pos->data_offset; + iter->pSize = (char*)iter->pData + iter->pos->size_offset; + return true; + } +} + +bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag) +{ + const pb_field_t *start = iter->pos; + + do { + if (iter->pos->tag == tag && + PB_LTYPE(iter->pos->type) != PB_LTYPE_EXTENSION) + { + /* Found the wanted field */ + return true; + } + + (void)pb_field_iter_next(iter); + } while (iter->pos != start); + + /* Searched all the way back to start, and found nothing. */ + return false; +} + + diff --git a/SDK/12.3.0_d7731ad/external/nano-pb/pb_common.h b/SDK/12.3.0_d7731ad/external/nano-pb/pb_common.h new file mode 100644 index 0000000..60b3d37 --- /dev/null +++ b/SDK/12.3.0_d7731ad/external/nano-pb/pb_common.h @@ -0,0 +1,42 @@ +/* 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 + +/* Iterator for pb_field_t list */ +struct pb_field_iter_s { + const pb_field_t *start; /* Start of the pb_field_t array */ + const pb_field_t *pos; /* Current position of the iterator */ + unsigned required_field_index; /* Zero-based index that counts only the required fields */ + void *dest_struct; /* Pointer to start of the structure */ + void *pData; /* Pointer to current field value */ + void *pSize; /* Pointer to count/has field */ +}; +typedef struct pb_field_iter_s pb_field_iter_t; + +/* 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_field_t *fields, void *dest_struct); + +/* 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 __cplusplus +} /* extern "C" */ +#endif + +#endif + diff --git a/SDK/12.3.0_d7731ad/external/nano-pb/pb_decode.c b/SDK/12.3.0_d7731ad/external/nano-pb/pb_decode.c new file mode 100644 index 0000000..ef69efe --- /dev/null +++ b/SDK/12.3.0_d7731ad/external/nano-pb/pb_decode.c @@ -0,0 +1,1340 @@ +/* pb_decode.c -- decode a protobuf using minimal resources + * + * 2011 Petteri Aimonen + */ + +/* 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 + +#include "pb.h" +#include "pb_decode.h" +#include "pb_common.h" + +/************************************** + * Declarations internal to this file * + **************************************/ + +typedef bool (*pb_decoder_t)(pb_istream_t *stream, const pb_field_t *field, void *dest) checkreturn; + +static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count); +static bool checkreturn pb_decode_varint32(pb_istream_t *stream, uint32_t *dest); +static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, pb_byte_t *buf, size_t *size); +static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *iter); +static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *iter); +static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *iter); +static void iter_from_extension(pb_field_iter_t *iter, pb_extension_t *extension); +static bool checkreturn default_extension_decoder(pb_istream_t *stream, pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type); +static bool checkreturn decode_extension(pb_istream_t *stream, uint32_t tag, pb_wire_type_t wire_type, pb_field_iter_t *iter); +static bool checkreturn find_extension_field(pb_field_iter_t *iter); +static void pb_field_set_to_default(pb_field_iter_t *iter); +static void pb_message_set_to_defaults(const pb_field_t fields[], void *dest_struct); +static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_t *field, void *dest); +static bool checkreturn pb_dec_uvarint(pb_istream_t *stream, const pb_field_t *field, void *dest); +static bool checkreturn pb_dec_svarint(pb_istream_t *stream, const pb_field_t *field, void *dest); +static bool checkreturn pb_dec_fixed32(pb_istream_t *stream, const pb_field_t *field, void *dest); +static bool checkreturn pb_dec_fixed64(pb_istream_t *stream, const pb_field_t *field, void *dest); +static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_t *field, void *dest); +static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_t *field, void *dest); +static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_t *field, void *dest); +static bool checkreturn pb_skip_varint(pb_istream_t *stream); +static bool checkreturn pb_skip_string(pb_istream_t *stream); + +#ifdef PB_ENABLE_MALLOC +static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, size_t data_size, size_t array_size); +static bool checkreturn pb_release_union_field(pb_istream_t *stream, pb_field_iter_t *iter); +static void pb_release_single_field(const pb_field_iter_t *iter); +#endif + +/* --- Function pointers to field decoders --- + * Order in the array must match pb_action_t LTYPE numbering. + */ +static const pb_decoder_t PB_DECODERS[PB_LTYPES_COUNT] = { + &pb_dec_varint, + &pb_dec_uvarint, + &pb_dec_svarint, + &pb_dec_fixed32, + &pb_dec_fixed64, + + &pb_dec_bytes, + &pb_dec_string, + &pb_dec_submessage, + NULL /* extensions */ +}; + +/******************************* + * pb_istream_t implementation * + *******************************/ + +static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count) +{ + const pb_byte_t *source = (const pb_byte_t*)stream->state; + stream->state = (pb_byte_t*)stream->state + count; + + if (buf != NULL) + { + while (count--) + *buf++ = *source++; + } + + return true; +} + +bool checkreturn pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count) +{ +#ifndef PB_BUFFER_ONLY + if (buf == NULL && stream->callback != buf_read) + { + /* Skip input bytes */ + pb_byte_t tmp[16]; + while (count > 16) + { + if (!pb_read(stream, tmp, 16)) + return false; + + count -= 16; + } + + return pb_read(stream, tmp, count); + } +#endif + + if (stream->bytes_left < count) + PB_RETURN_ERROR(stream, "end-of-stream"); + +#ifndef PB_BUFFER_ONLY + if (!stream->callback(stream, buf, count)) + PB_RETURN_ERROR(stream, "io error"); +#else + if (!buf_read(stream, buf, count)) + return false; +#endif + + stream->bytes_left -= count; + return true; +} + +/* Read a single byte from input stream. buf may not be NULL. + * This is an optimization for the varint decoding. */ +static bool checkreturn pb_readbyte(pb_istream_t *stream, pb_byte_t *buf) +{ + if (stream->bytes_left == 0) + PB_RETURN_ERROR(stream, "end-of-stream"); + +#ifndef PB_BUFFER_ONLY + if (!stream->callback(stream, buf, 1)) + PB_RETURN_ERROR(stream, "io error"); +#else + *buf = *(const pb_byte_t*)stream->state; + stream->state = (pb_byte_t*)stream->state + 1; +#endif + + stream->bytes_left--; + + return true; +} + +pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t bufsize) +{ + pb_istream_t stream; + /* Cast away the const from buf without a compiler error. We are + * careful to use it only in a const manner in the callbacks. + */ + union { + void *state; + const void *c_state; + } state; +#ifdef PB_BUFFER_ONLY + stream.callback = NULL; +#else + stream.callback = &buf_read; +#endif + state.c_state = buf; + stream.state = state.state; + stream.bytes_left = bufsize; + stream.decoding_callback = NULL; +#ifndef PB_NO_ERRMSG + stream.errmsg = NULL; +#endif + return stream; +} + +/******************** + * Helper functions * + ********************/ + +static bool checkreturn pb_decode_varint32(pb_istream_t *stream, uint32_t *dest) +{ + pb_byte_t byte; + uint32_t result; + + if (!pb_readbyte(stream, &byte)) + return false; + + if ((byte & 0x80) == 0) + { + /* Quick case, 1 byte value */ + result = byte; + } + else + { + /* Multibyte case */ + uint_fast8_t bitpos = 7; + result = byte & 0x7F; + + do + { + if (bitpos >= 32) + PB_RETURN_ERROR(stream, "varint overflow"); + + if (!pb_readbyte(stream, &byte)) + return false; + + result |= (uint32_t)(byte & 0x7F) << bitpos; + bitpos = (uint_fast8_t)(bitpos + 7); + } while (byte & 0x80); + } + + *dest = result; + return true; +} + +bool checkreturn pb_decode_varint(pb_istream_t *stream, uint64_t *dest) +{ + pb_byte_t byte; + uint_fast8_t bitpos = 0; + uint64_t result = 0; + + do + { + if (bitpos >= 64) + PB_RETURN_ERROR(stream, "varint overflow"); + + if (!pb_readbyte(stream, &byte)) + return false; + + result |= (uint64_t)(byte & 0x7F) << bitpos; + bitpos = (uint_fast8_t)(bitpos + 7); + } while (byte & 0x80); + + *dest = result; + return true; +} + +bool checkreturn pb_skip_varint(pb_istream_t *stream) +{ + pb_byte_t byte; + do + { + if (!pb_read(stream, &byte, 1)) + return false; + } while (byte & 0x80); + return true; +} + +bool checkreturn pb_skip_string(pb_istream_t *stream) +{ + uint32_t length; + if (!pb_decode_varint32(stream, &length)) + return false; + + return pb_read(stream, NULL, length); +} + +bool checkreturn pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof) +{ + uint32_t temp; + *eof = false; + *wire_type = (pb_wire_type_t) 0; + *tag = 0; + + if (!pb_decode_varint32(stream, &temp)) + { + if (stream->bytes_left == 0) + *eof = true; + + return false; + } + + if (temp == 0) + { + *eof = true; /* Special feature: allow 0-terminated messages. */ + return false; + } + + *tag = temp >> 3; + *wire_type = (pb_wire_type_t)(temp & 7); + return true; +} + +bool checkreturn pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type) +{ + switch (wire_type) + { + case PB_WT_VARINT: return pb_skip_varint(stream); + case PB_WT_64BIT: return pb_read(stream, NULL, 8); + case PB_WT_STRING: return pb_skip_string(stream); + case PB_WT_32BIT: return pb_read(stream, NULL, 4); + default: PB_RETURN_ERROR(stream, "invalid wire_type"); + } +} + +/* Read a raw value to buffer, for the purpose of passing it to callback as + * a substream. Size is maximum size on call, and actual size on return. + */ +static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, pb_byte_t *buf, size_t *size) +{ + size_t max_size = *size; + switch (wire_type) + { + case PB_WT_VARINT: + *size = 0; + do + { + (*size)++; + if (*size > max_size) return false; + if (!pb_read(stream, buf, 1)) return false; + } while (*buf++ & 0x80); + return true; + + case PB_WT_64BIT: + *size = 8; + return pb_read(stream, buf, 8); + + case PB_WT_32BIT: + *size = 4; + return pb_read(stream, buf, 4); + + default: PB_RETURN_ERROR(stream, "invalid wire_type"); + } +} + +/* Decode string length from stream and return a substream with limited length. + * Remember to close the substream using pb_close_string_substream(). + */ +bool checkreturn pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream) +{ + uint32_t size; + if (!pb_decode_varint32(stream, &size)) + return false; + + *substream = *stream; + if (substream->bytes_left < size) + PB_RETURN_ERROR(stream, "parent stream too short"); + + substream->bytes_left = size; + stream->bytes_left -= size; + return true; +} + +void pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream) +{ + stream->state = substream->state; + +#ifndef PB_NO_ERRMSG + stream->errmsg = substream->errmsg; +#endif +} + +/************************* + * Decode a single field * + *************************/ + +static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *iter) +{ + pb_type_t type; + pb_decoder_t func; + + type = iter->pos->type; + func = PB_DECODERS[PB_LTYPE(type)]; + + switch (PB_HTYPE(type)) + { + case PB_HTYPE_REQUIRED: + return func(stream, iter->pos, iter->pData); + + case PB_HTYPE_OPTIONAL: + *(bool*)iter->pSize = true; + return func(stream, iter->pos, iter->pData); + + case PB_HTYPE_REPEATED: + if (wire_type == PB_WT_STRING + && PB_LTYPE(type) <= PB_LTYPE_LAST_PACKABLE) + { + /* Packed array */ + bool status = true; + pb_size_t *size = (pb_size_t*)iter->pSize; + pb_istream_t substream; + if (!pb_make_string_substream(stream, &substream)) + return false; + + while (substream.bytes_left > 0 && *size < iter->pos->array_size) + { + void *pItem = (char*)iter->pData + iter->pos->data_size * (*size); + if (!func(&substream, iter->pos, pItem)) + { + status = false; + break; + } + (*size)++; + } + pb_close_string_substream(stream, &substream); + + if (substream.bytes_left != 0) + PB_RETURN_ERROR(stream, "array overflow"); + + return status; + } + else + { + /* Repeated field */ + pb_size_t *size = (pb_size_t*)iter->pSize; + void *pItem = (char*)iter->pData + iter->pos->data_size * (*size); + if (*size >= iter->pos->array_size) + PB_RETURN_ERROR(stream, "array overflow"); + + (*size)++; + return func(stream, iter->pos, pItem); + } + + case PB_HTYPE_ONEOF: + *(pb_size_t*)iter->pSize = iter->pos->tag; + if (PB_LTYPE(type) == PB_LTYPE_SUBMESSAGE) + { + /* We memset to zero so that any callbacks are set to NULL. + * Then set any default values. */ + memset(iter->pData, 0, iter->pos->data_size); + pb_message_set_to_defaults((const pb_field_t*)iter->pos->ptr, iter->pData); + } + return func(stream, iter->pos, iter->pData); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +#ifdef PB_ENABLE_MALLOC +/* Allocate storage for the field and store the pointer at iter->pData. + * array_size is the number of entries to reserve in an array. + * Zero size is not allowed, use pb_free() for releasing. + */ +static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, size_t data_size, size_t array_size) +{ + void *ptr = *(void**)pData; + + if (data_size == 0 || array_size == 0) + PB_RETURN_ERROR(stream, "invalid size"); + + /* Check for multiplication overflows. + * This code avoids the costly division if the sizes are small enough. + * Multiplication is safe as long as only half of bits are set + * in either multiplicand. + */ + { + const size_t check_limit = (size_t)1 << (sizeof(size_t) * 4); + if (data_size >= check_limit || array_size >= check_limit) + { + const size_t size_max = (size_t)-1; + if (size_max / array_size < data_size) + { + PB_RETURN_ERROR(stream, "size too large"); + } + } + } + + /* Allocate new or expand previous allocation */ + /* Note: on failure the old pointer will remain in the structure, + * the message must be freed by caller also on error return. */ + ptr = pb_realloc(ptr, array_size * data_size); + if (ptr == NULL) + PB_RETURN_ERROR(stream, "realloc failed"); + + *(void**)pData = ptr; + return true; +} + +/* Clear a newly allocated item in case it contains a pointer, or is a submessage. */ +static void initialize_pointer_field(void *pItem, pb_field_iter_t *iter) +{ + if (PB_LTYPE(iter->pos->type) == PB_LTYPE_STRING || + PB_LTYPE(iter->pos->type) == PB_LTYPE_BYTES) + { + *(void**)pItem = NULL; + } + else if (PB_LTYPE(iter->pos->type) == PB_LTYPE_SUBMESSAGE) + { + pb_message_set_to_defaults((const pb_field_t *) iter->pos->ptr, pItem); + } +} +#endif + +static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *iter) +{ +#ifndef PB_ENABLE_MALLOC + PB_UNUSED(wire_type); + PB_UNUSED(iter); + PB_RETURN_ERROR(stream, "no malloc support"); +#else + pb_type_t type; + pb_decoder_t func; + + type = iter->pos->type; + func = PB_DECODERS[PB_LTYPE(type)]; + + switch (PB_HTYPE(type)) + { + case PB_HTYPE_REQUIRED: + case PB_HTYPE_OPTIONAL: + case PB_HTYPE_ONEOF: + if (PB_LTYPE(type) == PB_LTYPE_SUBMESSAGE && + *(void**)iter->pData != NULL) + { + /* Duplicate field, have to release the old allocation first. */ + pb_release_single_field(iter); + } + + if (PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + *(pb_size_t*)iter->pSize = iter->pos->tag; + } + + if (PB_LTYPE(type) == PB_LTYPE_STRING || + PB_LTYPE(type) == PB_LTYPE_BYTES) + { + return func(stream, iter->pos, iter->pData); + } + else + { + if (!allocate_field(stream, iter->pData, iter->pos->data_size, 1)) + return false; + + initialize_pointer_field(*(void**)iter->pData, iter); + return func(stream, iter->pos, *(void**)iter->pData); + } + + case PB_HTYPE_REPEATED: + if (wire_type == PB_WT_STRING + && PB_LTYPE(type) <= PB_LTYPE_LAST_PACKABLE) + { + /* Packed array, multiple items come in at once. */ + bool status = true; + pb_size_t *size = (pb_size_t*)iter->pSize; + size_t allocated_size = *size; + void *pItem; + pb_istream_t substream; + + if (!pb_make_string_substream(stream, &substream)) + return false; + + while (substream.bytes_left) + { + if ((size_t)*size + 1 > allocated_size) + { + /* Allocate more storage. This tries to guess the + * number of remaining entries. Round the division + * upwards. */ + allocated_size += (substream.bytes_left - 1) / iter->pos->data_size + 1; + + if (!allocate_field(&substream, iter->pData, iter->pos->data_size, allocated_size)) + { + status = false; + break; + } + } + + /* Decode the array entry */ + pItem = *(char**)iter->pData + iter->pos->data_size * (*size); + initialize_pointer_field(pItem, iter); + if (!func(&substream, iter->pos, pItem)) + { + status = false; + break; + } + + if (*size == PB_SIZE_MAX) + { +#ifndef PB_NO_ERRMSG + stream->errmsg = "too many array entries"; +#endif + status = false; + break; + } + + (*size)++; + } + pb_close_string_substream(stream, &substream); + + return status; + } + else + { + /* Normal repeated field, i.e. only one item at a time. */ + pb_size_t *size = (pb_size_t*)iter->pSize; + void *pItem; + + if (*size == PB_SIZE_MAX) + PB_RETURN_ERROR(stream, "too many array entries"); + + (*size)++; + if (!allocate_field(stream, iter->pData, iter->pos->data_size, *size)) + return false; + + pItem = *(char**)iter->pData + iter->pos->data_size * (*size - 1); + initialize_pointer_field(pItem, iter); + return func(stream, iter->pos, pItem); + } + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +#endif +} + +static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *iter) +{ + pb_callback_t *pCallback = (pb_callback_t*)iter->pData; + +#ifdef PB_OLD_CALLBACK_STYLE + void *arg = pCallback->arg; +#else + void **arg = &(pCallback->arg); +#endif + + if (pCallback->funcs.decode == NULL) + return pb_skip_field(stream, wire_type); + + if (wire_type == PB_WT_STRING) + { + pb_istream_t substream; + + if (!pb_make_string_substream(stream, &substream)) + return false; + + do + { + if (!pCallback->funcs.decode(&substream, iter->pos, arg)) + PB_RETURN_ERROR(stream, "callback failed"); + } while (substream.bytes_left); + + pb_close_string_substream(stream, &substream); + return true; + } + else + { + /* Copy the single scalar value to stack. + * This is required so that we can limit the stream length, + * which in turn allows to use same callback for packed and + * not-packed fields. */ + pb_istream_t substream; + pb_byte_t buffer[10]; + size_t size = sizeof(buffer); + + if (!read_raw_value(stream, wire_type, buffer, &size)) + return false; + substream = pb_istream_from_buffer(buffer, size); + + return pCallback->funcs.decode(&substream, iter->pos, arg); + } +} + +static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *iter) +{ +#ifdef PB_ENABLE_MALLOC + /* When decoding an oneof field, check if there is old data that must be + * released first. */ + if (PB_HTYPE(iter->pos->type) == PB_HTYPE_ONEOF) + { + if (!pb_release_union_field(stream, iter)) + return false; + } +#endif + + switch (PB_ATYPE(iter->pos->type)) + { + case PB_ATYPE_STATIC: + return decode_static_field(stream, wire_type, iter); + + case PB_ATYPE_POINTER: + return decode_pointer_field(stream, wire_type, iter); + + case PB_ATYPE_CALLBACK: + return decode_callback_field(stream, wire_type, iter); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +static void iter_from_extension(pb_field_iter_t *iter, pb_extension_t *extension) +{ + /* Fake a field iterator for the extension field. + * It is not actually safe to advance this iterator, but decode_field + * will not even try to. */ + const pb_field_t *field = (const pb_field_t*)extension->type->arg; + (void)pb_field_iter_begin(iter, field, extension->dest); + iter->pData = extension->dest; + iter->pSize = &extension->found; + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { + /* For pointer extensions, the pointer is stored directly + * in the extension structure. This avoids having an extra + * indirection. */ + iter->pData = &extension->dest; + } +} + +/* Default handler for extension fields. Expects a pb_field_t structure + * in extension->type->arg. */ +static bool checkreturn default_extension_decoder(pb_istream_t *stream, + pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type) +{ + const pb_field_t *field = (const pb_field_t*)extension->type->arg; + pb_field_iter_t iter; + + if (field->tag != tag) + return true; + + iter_from_extension(&iter, extension); + extension->found = true; + return decode_field(stream, wire_type, &iter); +} + +/* Try to decode an unknown field as an extension field. Tries each extension + * decoder in turn, until one of them handles the field or loop ends. */ +static bool checkreturn decode_extension(pb_istream_t *stream, + uint32_t tag, pb_wire_type_t wire_type, pb_field_iter_t *iter) +{ + pb_extension_t *extension = *(pb_extension_t* const *)iter->pData; + size_t pos = stream->bytes_left; + + while (extension != NULL && pos == stream->bytes_left) + { + bool status; + if (extension->type->decode) + status = extension->type->decode(stream, extension, tag, wire_type); + else + status = default_extension_decoder(stream, extension, tag, wire_type); + + if (!status) + return false; + + extension = extension->next; + } + + return true; +} + +/* Step through the iterator until an extension field is found or until all + * entries have been checked. There can be only one extension field per + * message. Returns false if no extension field is found. */ +static bool checkreturn find_extension_field(pb_field_iter_t *iter) +{ + const pb_field_t *start = iter->pos; + + do { + if (PB_LTYPE(iter->pos->type) == PB_LTYPE_EXTENSION) + return true; + (void)pb_field_iter_next(iter); + } while (iter->pos != start); + + return false; +} + +/* Initialize message fields to default values, recursively */ +static void pb_field_set_to_default(pb_field_iter_t *iter) +{ + pb_type_t type; + type = iter->pos->type; + + if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) + { + pb_extension_t *ext = *(pb_extension_t* const *)iter->pData; + while (ext != NULL) + { + pb_field_iter_t ext_iter; + ext->found = false; + iter_from_extension(&ext_iter, ext); + pb_field_set_to_default(&ext_iter); + ext = ext->next; + } + } + else if (PB_ATYPE(type) == PB_ATYPE_STATIC) + { + bool init_data = true; + if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL) + { + /* Set has_field to false. Still initialize the optional field + * itself also. */ + *(bool*)iter->pSize = false; + } + else if (PB_HTYPE(type) == PB_HTYPE_REPEATED || + PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + /* REPEATED: Set array count to 0, no need to initialize contents. + ONEOF: Set which_field to 0. */ + *(pb_size_t*)iter->pSize = 0; + init_data = false; + } + + if (init_data) + { + if (PB_LTYPE(iter->pos->type) == PB_LTYPE_SUBMESSAGE) + { + /* Initialize submessage to defaults */ + pb_message_set_to_defaults((const pb_field_t *) iter->pos->ptr, iter->pData); + } + else if (iter->pos->ptr != NULL) + { + /* Initialize to default value */ + memcpy(iter->pData, iter->pos->ptr, iter->pos->data_size); + } + else + { + /* Initialize to zeros */ + memset(iter->pData, 0, iter->pos->data_size); + } + } + } + else if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + /* Initialize the pointer to NULL. */ + *(void**)iter->pData = NULL; + + /* Initialize array count to 0. */ + if (PB_HTYPE(type) == PB_HTYPE_REPEATED || + PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + *(pb_size_t*)iter->pSize = 0; + } + } + else if (PB_ATYPE(type) == PB_ATYPE_CALLBACK) + { + /* Don't overwrite callback */ + } +} + +static void pb_message_set_to_defaults(const pb_field_t fields[], void *dest_struct) +{ + pb_field_iter_t iter; + + if (!pb_field_iter_begin(&iter, fields, dest_struct)) + return; /* Empty message type */ + + do + { + pb_field_set_to_default(&iter); + } while (pb_field_iter_next(&iter)); +} + +/********************* + * Decode all fields * + *********************/ + +bool checkreturn pb_decode_noinit(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct) +{ + uint32_t fields_seen[(PB_MAX_REQUIRED_FIELDS + 31) / 32] = {0, 0}; + const uint32_t allbits = ~(uint32_t)0; + uint32_t extension_range_start = 0; + pb_field_iter_t iter; + + /* Return value ignored, as empty message types will be correctly handled by + * pb_field_iter_find() anyway. */ + (void)pb_field_iter_begin(&iter, fields, dest_struct); + + while (stream->bytes_left) + { + uint32_t tag; + pb_wire_type_t wire_type; + bool eof; + + if (!pb_decode_tag(stream, &wire_type, &tag, &eof)) + { + if (eof) + break; + else + return false; + } + + if (!pb_field_iter_find(&iter, tag)) + { + /* No match found, check if it matches an extension. */ + if (tag >= extension_range_start) + { + if (!find_extension_field(&iter)) + extension_range_start = (uint32_t)-1; + else + extension_range_start = iter.pos->tag; + + if (tag >= extension_range_start) + { + size_t pos = stream->bytes_left; + + if (!decode_extension(stream, tag, wire_type, &iter)) + return false; + + if (pos != stream->bytes_left) + { + /* The field was handled */ + continue; + } + } + } + + /* No match found, skip data */ + if (!pb_skip_field(stream, wire_type)) + return false; + continue; + } + + if (PB_HTYPE(iter.pos->type) == PB_HTYPE_REQUIRED + && iter.required_field_index < PB_MAX_REQUIRED_FIELDS) + { + uint32_t tmp = ((uint32_t)1 << (iter.required_field_index & 31)); + fields_seen[iter.required_field_index >> 5] |= tmp; + } + + if(stream->decoding_callback) + { + stream->decoding_callback(stream, tag, wire_type, &iter); + } + + if (!decode_field(stream, wire_type, &iter)) + return false; + } + + /* Check that all required fields were present. */ + { + /* First figure out the number of required fields by + * seeking to the end of the field array. Usually we + * are already close to end after decoding. + */ + unsigned req_field_count; + pb_type_t last_type; + unsigned i; + do { + req_field_count = iter.required_field_index; + last_type = iter.pos->type; + } while (pb_field_iter_next(&iter)); + + /* Fixup if last field was also required. */ + if (PB_HTYPE(last_type) == PB_HTYPE_REQUIRED && iter.pos->tag != 0) + req_field_count++; + + if (req_field_count > 0) + { + /* Check the whole words */ + for (i = 0; i < (req_field_count >> 5); i++) + { + if (fields_seen[i] != allbits) + PB_RETURN_ERROR(stream, "missing required field"); + } + + /* Check the remaining bits */ + if (fields_seen[req_field_count >> 5] != (allbits >> (32 - (req_field_count & 31)))) + PB_RETURN_ERROR(stream, "missing required field"); + } + } + + return true; +} + +bool checkreturn pb_decode(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct) +{ + bool status; + pb_message_set_to_defaults(fields, dest_struct); + status = pb_decode_noinit(stream, fields, dest_struct); + +#ifdef PB_ENABLE_MALLOC + if (!status) + pb_release(fields, dest_struct); +#endif + + return status; +} + +bool pb_decode_delimited(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct) +{ + pb_istream_t substream; + bool status; + + if (!pb_make_string_substream(stream, &substream)) + return false; + + status = pb_decode(&substream, fields, dest_struct); + pb_close_string_substream(stream, &substream); + return status; +} + +#ifdef PB_ENABLE_MALLOC +/* Given an oneof field, if there has already been a field inside this oneof, + * release it before overwriting with a different one. */ +static bool pb_release_union_field(pb_istream_t *stream, pb_field_iter_t *iter) +{ + pb_size_t old_tag = *(pb_size_t*)iter->pSize; /* Previous which_ value */ + pb_size_t new_tag = iter->pos->tag; /* New which_ value */ + + if (old_tag == 0) + return true; /* Ok, no old data in union */ + + if (old_tag == new_tag) + return true; /* Ok, old data is of same type => merge */ + + /* Release old data. The find can fail if the message struct contains + * invalid data. */ + if (!pb_field_iter_find(iter, old_tag)) + PB_RETURN_ERROR(stream, "invalid union tag"); + + pb_release_single_field(iter); + + /* Restore iterator to where it should be. + * This shouldn't fail unless the pb_field_t structure is corrupted. */ + if (!pb_field_iter_find(iter, new_tag)) + PB_RETURN_ERROR(stream, "iterator error"); + + return true; +} + +static void pb_release_single_field(const pb_field_iter_t *iter) +{ + pb_type_t type; + type = iter->pos->type; + + if (PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + if (*(pb_size_t*)iter->pSize != iter->pos->tag) + return; /* This is not the current field in the union */ + } + + /* Release anything contained inside an extension or submsg. + * This has to be done even if the submsg itself is statically + * allocated. */ + if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) + { + /* Release fields from all extensions in the linked list */ + pb_extension_t *ext = *(pb_extension_t**)iter->pData; + while (ext != NULL) + { + pb_field_iter_t ext_iter; + iter_from_extension(&ext_iter, ext); + pb_release_single_field(&ext_iter); + ext = ext->next; + } + } + else if (PB_LTYPE(type) == PB_LTYPE_SUBMESSAGE) + { + /* Release fields in submessage or submsg array */ + void *pItem = iter->pData; + pb_size_t count = 1; + + if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + pItem = *(void**)iter->pData; + } + + if (PB_HTYPE(type) == PB_HTYPE_REPEATED) + { + count = *(pb_size_t*)iter->pSize; + } + + if (pItem) + { + while (count--) + { + pb_release((const pb_field_t*)iter->pos->ptr, pItem); + pItem = (char*)pItem + iter->pos->data_size; + } + } + } + + if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + if (PB_HTYPE(type) == PB_HTYPE_REPEATED && + (PB_LTYPE(type) == PB_LTYPE_STRING || + PB_LTYPE(type) == PB_LTYPE_BYTES)) + { + /* Release entries in repeated string or bytes array */ + void **pItem = *(void***)iter->pData; + pb_size_t count = *(pb_size_t*)iter->pSize; + while (count--) + { + pb_free(*pItem); + *pItem++ = NULL; + } + } + + if (PB_HTYPE(type) == PB_HTYPE_REPEATED) + { + /* We are going to release the array, so set the size to 0 */ + *(pb_size_t*)iter->pSize = 0; + } + + /* Release main item */ + pb_free(*(void**)iter->pData); + *(void**)iter->pData = NULL; + } +} + +void pb_release(const pb_field_t fields[], void *dest_struct) +{ + pb_field_iter_t iter; + + if (!dest_struct) + return; /* Ignore NULL pointers, similar to free() */ + + if (!pb_field_iter_begin(&iter, fields, dest_struct)) + return; /* Empty message type */ + + do + { + pb_release_single_field(&iter); + } while (pb_field_iter_next(&iter)); +} +#endif + +/* Field decoders */ + +bool pb_decode_svarint(pb_istream_t *stream, int64_t *dest) +{ + uint64_t value; + if (!pb_decode_varint(stream, &value)) + return false; + + if (value & 1) + *dest = (int64_t)(~(value >> 1)); + else + *dest = (int64_t)(value >> 1); + + return true; +} + +bool pb_decode_fixed32(pb_istream_t *stream, void *dest) +{ + pb_byte_t bytes[4]; + + if (!pb_read(stream, bytes, 4)) + return false; + + *(uint32_t*)dest = ((uint32_t)bytes[0] << 0) | + ((uint32_t)bytes[1] << 8) | + ((uint32_t)bytes[2] << 16) | + ((uint32_t)bytes[3] << 24); + return true; +} + +bool pb_decode_fixed64(pb_istream_t *stream, void *dest) +{ + pb_byte_t bytes[8]; + + if (!pb_read(stream, bytes, 8)) + return false; + + *(uint64_t*)dest = ((uint64_t)bytes[0] << 0) | + ((uint64_t)bytes[1] << 8) | + ((uint64_t)bytes[2] << 16) | + ((uint64_t)bytes[3] << 24) | + ((uint64_t)bytes[4] << 32) | + ((uint64_t)bytes[5] << 40) | + ((uint64_t)bytes[6] << 48) | + ((uint64_t)bytes[7] << 56); + + return true; +} + +static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_t *field, void *dest) +{ + uint64_t value; + int64_t svalue; + int64_t clamped; + if (!pb_decode_varint(stream, &value)) + return false; + + /* See issue 97: Google's C++ protobuf allows negative varint values to + * be cast as int32_t, instead of the int64_t that should be used when + * encoding. Previous nanopb versions had a bug in encoding. In order to + * not break decoding of such messages, we cast <=32 bit fields to + * int32_t first to get the sign correct. + */ + if (field->data_size == sizeof(int64_t)) + svalue = (int64_t)value; + else + svalue = (int32_t)value; + + /* Cast to the proper field size, while checking for overflows */ + if (field->data_size == sizeof(int64_t)) + clamped = *(int64_t*)dest = svalue; + else if (field->data_size == sizeof(int32_t)) + clamped = *(int32_t*)dest = (int32_t)svalue; + else if (field->data_size == sizeof(int_least16_t)) + clamped = *(int_least16_t*)dest = (int_least16_t)svalue; + else if (field->data_size == sizeof(int_least8_t)) + clamped = *(int_least8_t*)dest = (int_least8_t)svalue; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + if (clamped != svalue) + PB_RETURN_ERROR(stream, "integer too large"); + + return true; +} + +static bool checkreturn pb_dec_uvarint(pb_istream_t *stream, const pb_field_t *field, void *dest) +{ + uint64_t value, clamped; + if (!pb_decode_varint(stream, &value)) + return false; + + /* Cast to the proper field size, while checking for overflows */ + if (field->data_size == sizeof(uint64_t)) + clamped = *(uint64_t*)dest = value; + else if (field->data_size == sizeof(uint32_t)) + clamped = *(uint32_t*)dest = (uint32_t)value; + else if (field->data_size == sizeof(uint_least16_t)) + clamped = *(uint_least16_t*)dest = (uint_least16_t)value; + else if (field->data_size == sizeof(uint_least8_t)) + clamped = *(uint_least8_t*)dest = (uint_least8_t)value; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + if (clamped != value) + PB_RETURN_ERROR(stream, "integer too large"); + + return true; +} + +static bool checkreturn pb_dec_svarint(pb_istream_t *stream, const pb_field_t *field, void *dest) +{ + int64_t value, clamped; + if (!pb_decode_svarint(stream, &value)) + return false; + + /* Cast to the proper field size, while checking for overflows */ + if (field->data_size == sizeof(int64_t)) + clamped = *(int64_t*)dest = value; + else if (field->data_size == sizeof(int32_t)) + clamped = *(int32_t*)dest = (int32_t)value; + else if (field->data_size == sizeof(int_least16_t)) + clamped = *(int_least16_t*)dest = (int_least16_t)value; + else if (field->data_size == sizeof(int_least8_t)) + clamped = *(int_least8_t*)dest = (int_least8_t)value; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + if (clamped != value) + PB_RETURN_ERROR(stream, "integer too large"); + + return true; +} + +static bool checkreturn pb_dec_fixed32(pb_istream_t *stream, const pb_field_t *field, void *dest) +{ + PB_UNUSED(field); + return pb_decode_fixed32(stream, dest); +} + +static bool checkreturn pb_dec_fixed64(pb_istream_t *stream, const pb_field_t *field, void *dest) +{ + PB_UNUSED(field); + return pb_decode_fixed64(stream, dest); +} + +static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_t *field, void *dest) +{ + uint32_t size; + size_t alloc_size; + pb_bytes_array_t *bdest; + + if (!pb_decode_varint32(stream, &size)) + return false; + + if (size > PB_SIZE_MAX) + PB_RETURN_ERROR(stream, "bytes overflow"); + + alloc_size = PB_BYTES_ARRAY_T_ALLOCSIZE(size); + if (size > alloc_size) + PB_RETURN_ERROR(stream, "size too large"); + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { +#ifndef PB_ENABLE_MALLOC + PB_RETURN_ERROR(stream, "no malloc support"); +#else + if (!allocate_field(stream, dest, alloc_size, 1)) + return false; + bdest = *(pb_bytes_array_t**)dest; +#endif + } + else + { + if (alloc_size > field->data_size) + PB_RETURN_ERROR(stream, "bytes overflow"); + bdest = (pb_bytes_array_t*)dest; + } + + bdest->size = (pb_size_t)size; + return pb_read(stream, bdest->bytes, size); +} + +static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_t *field, void *dest) +{ + uint32_t size; + size_t alloc_size; + bool status; + if (!pb_decode_varint32(stream, &size)) + return false; + + /* Space for null terminator */ + alloc_size = size + 1; + + if (alloc_size < size) + PB_RETURN_ERROR(stream, "size too large"); + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { +#ifndef PB_ENABLE_MALLOC + PB_RETURN_ERROR(stream, "no malloc support"); +#else + if (!allocate_field(stream, dest, alloc_size, 1)) + return false; + dest = *(void**)dest; +#endif + } + else + { + if (alloc_size > field->data_size) + PB_RETURN_ERROR(stream, "string overflow"); + } + + status = pb_read(stream, (pb_byte_t*)dest, size); + *((pb_byte_t*)dest + size) = 0; + return status; +} + +static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_t *field, void *dest) +{ + bool status; + pb_istream_t substream; + const pb_field_t* submsg_fields = (const pb_field_t*)field->ptr; + + if (!pb_make_string_substream(stream, &substream)) + return false; + + if (field->ptr == NULL) + PB_RETURN_ERROR(stream, "invalid field descriptor"); + + /* New array entries need to be initialized, while required and optional + * submessages have already been initialized in the top-level pb_decode. */ + if (PB_HTYPE(field->type) == PB_HTYPE_REPEATED) + status = pb_decode(&substream, submsg_fields, dest); + else + status = pb_decode_noinit(&substream, submsg_fields, dest); + + pb_close_string_substream(stream, &substream); + return status; +} diff --git a/SDK/12.3.0_d7731ad/external/nano-pb/pb_decode.h b/SDK/12.3.0_d7731ad/external/nano-pb/pb_decode.h new file mode 100644 index 0000000..7ab8ce7 --- /dev/null +++ b/SDK/12.3.0_d7731ad/external/nano-pb/pb_decode.h @@ -0,0 +1,152 @@ +/* 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; + + /* Informative callback for field decoding */ + void (* decoding_callback)(pb_istream_t *strem, uint32_t tag, pb_wire_type_t wire_type, void *iter); + +#ifndef PB_NO_ERRMSG + const char *errmsg; +#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_field_t fields[], void *dest_struct); + +/* Same as pb_decode, except does not initialize the destination structure + * to default values. This is slightly faster if you need no default values + * and just do memset(struct, 0, sizeof(struct)) yourself. + * + * This can also be used for 'merging' two messages, i.e. update only the + * fields that exist in the new message. + * + * Note: If this function returns with an error, it will not release any + * dynamically allocated fields. You will need to call pb_release() yourself. + */ +bool pb_decode_noinit(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct); + +/* Same as pb_decode, except expects the stream to start with the message size + * encoded as varint. Corresponds to parseDelimitedFrom() in Google's + * protobuf API. + */ +bool pb_decode_delimited(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct); + +#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_field_t fields[], void *dest_struct); +#endif + + +/************************************** + * Functions for manipulating streams * + **************************************/ + +/* Create an input stream for reading from a memory 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 bufsize); + +/* 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 bool, enum, int32, + * int64, uint32 and uint64 field types. */ +bool pb_decode_varint(pb_istream_t *stream, uint64_t *dest); + +/* Decode an integer in the zig-zagged svarint format. This works for sint32 + * and sint64. */ +bool pb_decode_svarint(pb_istream_t *stream, int64_t *dest); + +/* 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); + +/* 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); + +/* 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); +void pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/SDK/12.3.0_d7731ad/external/nano-pb/pb_encode.c b/SDK/12.3.0_d7731ad/external/nano-pb/pb_encode.c new file mode 100644 index 0000000..9f91c9d --- /dev/null +++ b/SDK/12.3.0_d7731ad/external/nano-pb/pb_encode.c @@ -0,0 +1,689 @@ +/* pb_encode.c -- encode a protobuf using minimal resources + * + * 2011 Petteri Aimonen + */ + +#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 * + **************************************/ +typedef bool (*pb_encoder_t)(pb_ostream_t *stream, const pb_field_t *field, const void *src) checkreturn; + +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, const pb_field_t *field, const void *pData, size_t count, pb_encoder_t func); +static bool checkreturn encode_field(pb_ostream_t *stream, const pb_field_t *field, const void *pData); +static bool checkreturn default_extension_encoder(pb_ostream_t *stream, const pb_extension_t *extension); +static bool checkreturn encode_extension_field(pb_ostream_t *stream, const pb_field_t *field, const void *pData); +static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_t *field, const void *src); +static bool checkreturn pb_enc_uvarint(pb_ostream_t *stream, const pb_field_t *field, const void *src); +static bool checkreturn pb_enc_svarint(pb_ostream_t *stream, const pb_field_t *field, const void *src); +static bool checkreturn pb_enc_fixed32(pb_ostream_t *stream, const pb_field_t *field, const void *src); +static bool checkreturn pb_enc_fixed64(pb_ostream_t *stream, const pb_field_t *field, const void *src); +static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src); +static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_t *field, const void *src); +static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_t *field, const void *src); + +/* --- Function pointers to field encoders --- + * Order in the array must match pb_action_t LTYPE numbering. + */ +static const pb_encoder_t PB_ENCODERS[PB_LTYPES_COUNT] = { + &pb_enc_varint, + &pb_enc_uvarint, + &pb_enc_svarint, + &pb_enc_fixed32, + &pb_enc_fixed64, + + &pb_enc_bytes, + &pb_enc_string, + &pb_enc_submessage, + NULL /* extensions */ +}; + +/******************************* + * pb_ostream_t implementation * + *******************************/ + +static bool checkreturn buf_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count) +{ + pb_byte_t *dest = (pb_byte_t*)stream->state; + stream->state = dest + count; + + while (count--) + *dest++ = *buf++; + + 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 (stream->callback != NULL) + { + if (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 * + *************************/ + +/* Encode a static array. Handles the size calculations and possible packing. */ +static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *field, + const void *pData, size_t count, pb_encoder_t func) +{ + size_t i; + const void *p; + size_t size; + + 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"); + + /* 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 * count; + } + else if (PB_LTYPE(field->type) == PB_LTYPE_FIXED64) + { + size = 8 * count; + } + else + { + pb_ostream_t sizestream = PB_OSTREAM_SIZING; + p = pData; + for (i = 0; i < count; i++) + { + if (!func(&sizestream, field, p)) + return false; + p = (const char*)p + field->data_size; + } + size = sizestream.bytes_written; + } + + if (!pb_encode_varint(stream, (uint64_t)size)) + return false; + + if (stream->callback == NULL) + return pb_write(stream, NULL, size); /* Just sizing.. */ + + /* Write the data */ + p = pData; + for (i = 0; i < count; i++) + { + if (!func(stream, field, p)) + return false; + p = (const char*)p + field->data_size; + } + } + else + { + p = pData; + for (i = 0; i < count; i++) + { + if (!pb_encode_tag_for_field(stream, field)) + return false; + + /* 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)) + { + if (!func(stream, field, *(const void* const*)p)) + return false; + } + else + { + if (!func(stream, field, p)) + return false; + } + p = (const char*)p + field->data_size; + } + } + + return true; +} + +/* 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_t *field, const void *pData) +{ + pb_encoder_t func; + const void *pSize; + bool implicit_has = true; + + func = PB_ENCODERS[PB_LTYPE(field->type)]; + + if (field->size_offset) + pSize = (const char*)pData + field->size_offset; + else + pSize = &implicit_has; + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { + /* pData is a pointer to the field, which contains pointer to + * the data. If the 2nd pointer is NULL, it is interpreted as if + * the has_field was false. + */ + + pData = *(const void* const*)pData; + implicit_has = (pData != NULL); + } + + switch (PB_HTYPE(field->type)) + { + case PB_HTYPE_REQUIRED: + if (!pData) + PB_RETURN_ERROR(stream, "missing required field"); + if (!pb_encode_tag_for_field(stream, field)) + return false; + if (!func(stream, field, pData)) + return false; + break; + + case PB_HTYPE_OPTIONAL: + if (*(const bool*)pSize) + { + if (!pb_encode_tag_for_field(stream, field)) + return false; + + if (!func(stream, field, pData)) + return false; + } + break; + + case PB_HTYPE_REPEATED: + if (!encode_array(stream, field, pData, *(const pb_size_t*)pSize, func)) + return false; + break; + + case PB_HTYPE_ONEOF: + if (*(const pb_size_t*)pSize == field->tag) + { + if (!pb_encode_tag_for_field(stream, field)) + return false; + + if (!func(stream, field, pData)) + return false; + } + break; + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } + + return true; +} + +/* 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_t *field, const void *pData) +{ + const pb_callback_t *callback = (const pb_callback_t*)pData; + +#ifdef PB_OLD_CALLBACK_STYLE + const void *arg = callback->arg; +#else + void * const *arg = &(callback->arg); +#endif + + if (callback->funcs.encode != NULL) + { + if (!callback->funcs.encode(stream, field, arg)) + PB_RETURN_ERROR(stream, "callback error"); + } + return true; +} + +/* Encode a single field of any callback or static type. */ +static bool checkreturn encode_field(pb_ostream_t *stream, + const pb_field_t *field, const void *pData) +{ + switch (PB_ATYPE(field->type)) + { + case PB_ATYPE_STATIC: + case PB_ATYPE_POINTER: + return encode_basic_field(stream, field, pData); + + case PB_ATYPE_CALLBACK: + return encode_callback_field(stream, field, pData); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +/* Default handler for extension fields. Expects to have a pb_field_t + * pointer in the extension->type->arg field. */ +static bool checkreturn default_extension_encoder(pb_ostream_t *stream, + const pb_extension_t *extension) +{ + const pb_field_t *field = (const pb_field_t*)extension->type->arg; + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { + /* For pointer extensions, the pointer is stored directly + * in the extension structure. This avoids having an extra + * indirection. */ + return encode_field(stream, field, &extension->dest); + } + else + { + return encode_field(stream, field, extension->dest); + } +} + +/* 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_t *field, const void *pData) +{ + const pb_extension_t *extension = *(const pb_extension_t* const *)pData; + PB_UNUSED(field); + + 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 * + *********************/ + +static void *remove_const(const void *p) +{ + /* Note: this casts away const, in order to use the common field iterator + * logic for both encoding and decoding. */ + union { + void *p1; + const void *p2; + } t; + t.p2 = p; + return t.p1; +} + +bool checkreturn pb_encode(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct) +{ + pb_field_iter_t iter; + if (!pb_field_iter_begin(&iter, fields, remove_const(src_struct))) + return true; /* Empty message type */ + + do { + if (PB_LTYPE(iter.pos->type) == PB_LTYPE_EXTENSION) + { + /* Special case for the extension field placeholder */ + if (!encode_extension_field(stream, iter.pos, iter.pData)) + return false; + } + else + { + /* Regular field */ + if (!encode_field(stream, iter.pos, iter.pData)) + return false; + } + } while (pb_field_iter_next(&iter)); + + return true; +} + +bool pb_encode_delimited(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct) +{ + return pb_encode_submessage(stream, fields, src_struct); +} + +bool pb_get_encoded_size(size_t *size, const pb_field_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 * + ********************/ +bool checkreturn pb_encode_varint(pb_ostream_t *stream, uint64_t value) +{ + pb_byte_t buffer[10]; + size_t i = 0; + + if (value <= 0x7F) + { + pb_byte_t v = (pb_byte_t)value; + return pb_write(stream, &v, 1); + } + + while (value) + { + buffer[i] = (pb_byte_t)((value & 0x7F) | 0x80); + value >>= 7; + i++; + } + buffer[i-1] &= 0x7F; /* Unset top bit on last byte */ + + return pb_write(stream, buffer, i); +} + +bool checkreturn pb_encode_svarint(pb_ostream_t *stream, int64_t value) +{ + uint64_t zigzagged; + if (value < 0) + zigzagged = ~((uint64_t)value << 1); + else + zigzagged = (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); +} + +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); +} + +bool checkreturn pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number) +{ + uint64_t tag = ((uint64_t)field_number << 3) | wiretype; + return pb_encode_varint(stream, tag); +} + +bool checkreturn pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_t *field) +{ + pb_wire_type_t wiretype; + switch (PB_LTYPE(field->type)) + { + 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: + 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, (uint64_t)size)) + return false; + + return pb_write(stream, buffer, size); +} + +bool checkreturn pb_encode_submessage(pb_ostream_t *stream, const pb_field_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, (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_varint(pb_ostream_t *stream, const pb_field_t *field, const void *src) +{ + int64_t value = 0; + + if (field->data_size == sizeof(int_least8_t)) + value = *(const int_least8_t*)src; + else if (field->data_size == sizeof(int_least16_t)) + value = *(const int_least16_t*)src; + else if (field->data_size == sizeof(int32_t)) + value = *(const int32_t*)src; + else if (field->data_size == sizeof(int64_t)) + value = *(const int64_t*)src; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + return pb_encode_varint(stream, (uint64_t)value); +} + +static bool checkreturn pb_enc_uvarint(pb_ostream_t *stream, const pb_field_t *field, const void *src) +{ + uint64_t value = 0; + + if (field->data_size == sizeof(uint_least8_t)) + value = *(const uint_least8_t*)src; + else if (field->data_size == sizeof(uint_least16_t)) + value = *(const uint_least16_t*)src; + else if (field->data_size == sizeof(uint32_t)) + value = *(const uint32_t*)src; + else if (field->data_size == sizeof(uint64_t)) + value = *(const uint64_t*)src; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + return pb_encode_varint(stream, value); +} + +static bool checkreturn pb_enc_svarint(pb_ostream_t *stream, const pb_field_t *field, const void *src) +{ + int64_t value = 0; + + if (field->data_size == sizeof(int_least8_t)) + value = *(const int_least8_t*)src; + else if (field->data_size == sizeof(int_least16_t)) + value = *(const int_least16_t*)src; + else if (field->data_size == sizeof(int32_t)) + value = *(const int32_t*)src; + else if (field->data_size == sizeof(int64_t)) + value = *(const int64_t*)src; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + return pb_encode_svarint(stream, value); +} + +static bool checkreturn pb_enc_fixed64(pb_ostream_t *stream, const pb_field_t *field, const void *src) +{ + PB_UNUSED(field); + return pb_encode_fixed64(stream, src); +} + +static bool checkreturn pb_enc_fixed32(pb_ostream_t *stream, const pb_field_t *field, const void *src) +{ + PB_UNUSED(field); + return pb_encode_fixed32(stream, src); +} + +static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src) +{ + const pb_bytes_array_t *bytes = (const pb_bytes_array_t*)src; + + if (src == NULL) + { + /* Threat null pointer as an empty bytes field */ + return pb_encode_string(stream, NULL, 0); + } + + if (PB_ATYPE(field->type) == PB_ATYPE_STATIC && + PB_BYTES_ARRAY_T_ALLOCSIZE(bytes->size) > field->data_size) + { + PB_RETURN_ERROR(stream, "bytes size exceeded"); + } + + return pb_encode_string(stream, bytes->bytes, bytes->size); +} + +static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_t *field, const void *src) +{ + size_t size = 0; + size_t max_size = field->data_size; + const char *p = (const char*)src; + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + max_size = (size_t)-1; + + if (src == NULL) + { + size = 0; /* Threat null pointer as an empty string */ + } + else + { + /* strnlen() is not always available, so just use a loop */ + while (size < max_size && *p != '\0') + { + size++; + p++; + } + } + + return pb_encode_string(stream, (const pb_byte_t*)src, size); +} + +static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_t *field, const void *src) +{ + if (field->ptr == NULL) + PB_RETURN_ERROR(stream, "invalid field descriptor"); + + return pb_encode_submessage(stream, (const pb_field_t*)field->ptr, src); +} + diff --git a/SDK/12.3.0_d7731ad/external/nano-pb/pb_encode.h b/SDK/12.3.0_d7731ad/external/nano-pb/pb_encode.h new file mode 100644 index 0000000..d9909fb --- /dev/null +++ b/SDK/12.3.0_d7731ad/external/nano-pb/pb_encode.h @@ -0,0 +1,154 @@ +/* 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_field_t fields[], const void *src_struct); + +/* Same as pb_encode, but prepends the length of the message as a varint. + * Corresponds to writeDelimitedTo() in Google's protobuf API. + */ +bool pb_encode_delimited(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct); + +/* 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_field_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_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. */ +bool pb_encode_varint(pb_ostream_t *stream, uint64_t value); + +/* Encode an integer in the zig-zagged svarint format. + * This works for sint32 and sint64. */ +bool pb_encode_svarint(pb_ostream_t *stream, int64_t value); + +/* 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); + +/* 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); + +/* 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_field_t fields[], const void *src_struct); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif